support pi

This commit is contained in:
Franklin 2026-02-05 17:06:53 -05:00
parent cc5a9e0d73
commit 843498e9db
41 changed files with 2654 additions and 102 deletions

View file

@ -0,0 +1,124 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://sandbox-agent/schemas/pi.json",
"title": "Pi RPC Schema",
"definitions": {
"RpcEvent": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"sessionId": {
"type": "string"
},
"messageId": {
"type": "string"
},
"message": {
"$ref": "#/definitions/RpcMessage"
},
"assistantMessageEvent": {
"$ref": "#/definitions/AssistantMessageEvent"
},
"toolCallId": {
"type": "string"
},
"toolName": {
"type": "string"
},
"args": {},
"partialResult": {},
"result": {
"$ref": "#/definitions/ToolResult"
},
"isError": {
"type": "boolean"
},
"error": {}
},
"required": [
"type"
]
},
"RpcMessage": {
"type": "object",
"properties": {
"role": {
"type": "string"
},
"content": {}
}
},
"AssistantMessageEvent": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"delta": {
"type": "string"
},
"content": {},
"partial": {},
"messageId": {
"type": "string"
}
}
},
"ToolResult": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"content": {
"type": "string"
},
"text": {
"type": "string"
}
}
},
"RpcResponse": {
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "response"
},
"id": {
"type": "integer"
},
"success": {
"type": "boolean"
},
"data": {},
"error": {}
},
"required": [
"type"
]
},
"RpcCommand": {
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "command"
},
"id": {
"type": "integer"
},
"command": {
"type": "string"
},
"params": {}
},
"required": [
"type",
"command"
]
}
}
}

View file

@ -9,6 +9,7 @@
"extract:claude": "tsx src/index.ts --agent=claude",
"extract:codex": "tsx src/index.ts --agent=codex",
"extract:amp": "tsx src/index.ts --agent=amp",
"extract:pi": "tsx src/index.ts --agent=pi",
"extract:claude-events": "tsx src/claude-event-types.ts",
"extract:claude-events:sdk": "tsx src/claude-event-types-sdk.ts",
"extract:claude-events:cli": "tsx src/claude-event-types-cli.ts",

View file

@ -4,18 +4,20 @@ import { extractOpenCodeSchema } from "./opencode.js";
import { extractClaudeSchema } from "./claude.js";
import { extractCodexSchema } from "./codex.js";
import { extractAmpSchema } from "./amp.js";
import { extractPiSchema } from "./pi.js";
import { validateSchema, type NormalizedSchema } from "./normalize.js";
const RESOURCE_DIR = join(import.meta.dirname, "..");
const DIST_DIR = join(RESOURCE_DIR, "artifacts", "json-schema");
type AgentName = "opencode" | "claude" | "codex" | "amp";
type AgentName = "opencode" | "claude" | "codex" | "amp" | "pi";
const EXTRACTORS: Record<AgentName, () => Promise<NormalizedSchema>> = {
opencode: extractOpenCodeSchema,
claude: extractClaudeSchema,
codex: extractCodexSchema,
amp: extractAmpSchema,
pi: extractPiSchema,
};
function parseArgs(): { agents: AgentName[] } {

View file

@ -0,0 +1,191 @@
import { execSync } from "child_process";
import { existsSync, mkdtempSync, readdirSync, rmSync, writeFileSync } from "fs";
import { join } from "path";
import { tmpdir } from "os";
import { createGenerator, type Config } from "ts-json-schema-generator";
import { createNormalizedSchema, type NormalizedSchema } from "./normalize.js";
import type { JSONSchema7 } from "json-schema";
const PI_SOURCE_URL = "https://codeload.github.com/badlogic/pi-mono/tar.gz/refs/heads/main";
const RPC_TYPES_PATH = "packages/coding-agent/src/modes/rpc/rpc-types.ts";
const TARGET_TYPES = ["RpcEvent", "RpcResponse", "RpcCommand"] as const;
export async function extractPiSchema(): Promise<NormalizedSchema> {
console.log("Extracting Pi schema from pi-mono sources...");
const tempDir = mkdtempSync(join(tmpdir(), "pi-schema-"));
try {
const archivePath = join(tempDir, "pi-mono.tar.gz");
await downloadToFile(PI_SOURCE_URL, archivePath);
execSync(`tar -xzf "${archivePath}" -C "${tempDir}"`, {
stdio: ["ignore", "ignore", "ignore"],
});
const repoRoot = findRepoRoot(tempDir);
const rpcTypesPath = join(repoRoot, RPC_TYPES_PATH);
if (!existsSync(rpcTypesPath)) {
throw new Error(`rpc-types.ts not found at ${rpcTypesPath}`);
}
const tsconfig = resolveTsconfig(repoRoot);
const definitions = generateDefinitions(rpcTypesPath, tsconfig);
if (Object.keys(definitions).length === 0) {
console.log(" [warn] No schemas extracted from source, using fallback");
return createFallbackSchema();
}
console.log(` [ok] Extracted ${Object.keys(definitions).length} types from source`);
return createNormalizedSchema("pi", "Pi RPC Schema", definitions);
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
console.log(` [warn] Pi schema extraction failed: ${errorMessage}`);
console.log(" [fallback] Using embedded schema definitions");
return createFallbackSchema();
} finally {
rmSync(tempDir, { recursive: true, force: true });
}
}
async function downloadToFile(url: string, filePath: string): Promise<void> {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const buffer = Buffer.from(await response.arrayBuffer());
writeFileSync(filePath, buffer);
}
function findRepoRoot(root: string): string {
const entries = readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory());
const repoDir = entries.find((entry) => entry.name.startsWith("pi-mono"));
if (!repoDir) {
throw new Error("pi-mono source directory not found after extraction");
}
return join(root, repoDir.name);
}
function resolveTsconfig(root: string): string | undefined {
const candidates = [
join(root, "tsconfig.json"),
join(root, "tsconfig.base.json"),
join(root, "packages", "coding-agent", "tsconfig.json"),
];
return candidates.find((path) => existsSync(path));
}
function generateDefinitions(
rpcTypesPath: string,
tsconfigPath?: string
): Record<string, JSONSchema7> {
const definitions: Record<string, JSONSchema7> = {};
for (const typeName of TARGET_TYPES) {
const config: Config = {
path: rpcTypesPath,
type: typeName,
expose: "all",
skipTypeCheck: false,
topRef: false,
...(tsconfigPath ? { tsconfig: tsconfigPath } : {}),
};
const schema = createGenerator(config).createSchema(typeName) as JSONSchema7;
mergeDefinitions(definitions, schema, typeName);
}
return definitions;
}
function mergeDefinitions(
target: Record<string, JSONSchema7>,
schema: JSONSchema7,
typeName: string
): void {
if (schema.definitions) {
for (const [name, def] of Object.entries(schema.definitions)) {
target[name] = def as JSONSchema7;
}
} else if (schema.$defs) {
for (const [name, def] of Object.entries(schema.$defs)) {
target[name] = def as JSONSchema7;
}
} else {
target[typeName] = schema;
}
if (!target[typeName]) {
target[typeName] = schema;
}
}
function createFallbackSchema(): NormalizedSchema {
const definitions: Record<string, JSONSchema7> = {
RpcEvent: {
type: "object",
properties: {
type: { type: "string" },
sessionId: { type: "string" },
messageId: { type: "string" },
message: { $ref: "#/definitions/RpcMessage" },
assistantMessageEvent: { $ref: "#/definitions/AssistantMessageEvent" },
toolCallId: { type: "string" },
toolName: { type: "string" },
args: {},
partialResult: {},
result: { $ref: "#/definitions/ToolResult" },
isError: { type: "boolean" },
error: {},
},
required: ["type"],
},
RpcMessage: {
type: "object",
properties: {
role: { type: "string" },
content: {},
},
},
AssistantMessageEvent: {
type: "object",
properties: {
type: { type: "string" },
delta: { type: "string" },
content: {},
partial: {},
messageId: { type: "string" },
},
},
ToolResult: {
type: "object",
properties: {
type: { type: "string" },
content: { type: "string" },
text: { type: "string" },
},
},
RpcResponse: {
type: "object",
properties: {
type: { type: "string", const: "response" },
id: { type: "integer" },
success: { type: "boolean" },
data: {},
error: {},
},
required: ["type"],
},
RpcCommand: {
type: "object",
properties: {
type: { type: "string", const: "command" },
id: { type: "integer" },
command: { type: "string" },
params: {},
},
required: ["type", "command"],
},
};
console.log(` [ok] Using fallback schema with ${Object.keys(definitions).length} definitions`);
return createNormalizedSchema("pi", "Pi RPC Schema", definitions);
}