fix(agent-codex): add jsonrpc 2.0 field and fix resume flag ordering
- Add "jsonrpc": "2.0" to all JSON-RPC requests, notifications, and approval responses for spec compliance - Place flags before positional threadId in resume command for CLI parser compatibility (codex resume --flags <threadId>) - Add test assertions for jsonrpc field and flag ordering Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e7033048f2
commit
3f76338bb4
|
|
@ -137,8 +137,10 @@ describe("CodexAppServerClient", () => {
|
|||
// Should have sent initialize request and initialized notification
|
||||
const initReq = findRequest(proc, "initialize");
|
||||
expect(initReq).toBeDefined();
|
||||
expect(initReq!["jsonrpc"]).toBe("2.0");
|
||||
const initializedNotif = findRequest(proc, "initialized");
|
||||
expect(initializedNotif).toBeDefined();
|
||||
expect(initializedNotif!["jsonrpc"]).toBe("2.0");
|
||||
|
||||
await closeClient(client, proc);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import { EventEmitter } from "node:events";
|
|||
|
||||
/** JSON-RPC request sent from client to server */
|
||||
export interface JsonRpcRequest {
|
||||
jsonrpc: "2.0";
|
||||
id: string;
|
||||
method: string;
|
||||
params: Record<string, unknown>;
|
||||
|
|
@ -339,7 +340,7 @@ export class CodexAppServerClient extends EventEmitter {
|
|||
}
|
||||
|
||||
const id = randomUUID();
|
||||
const request: JsonRpcRequest = { id, method, params };
|
||||
const request: JsonRpcRequest = { jsonrpc: "2.0", id, method, params };
|
||||
|
||||
return new Promise<Record<string, unknown>>((resolve, reject) => {
|
||||
const timer = setTimeout(() => {
|
||||
|
|
@ -357,7 +358,7 @@ export class CodexAppServerClient extends EventEmitter {
|
|||
if (this.closed) return;
|
||||
if (!this.process?.stdin?.writable) return;
|
||||
|
||||
this.writeLine(JSON.stringify({ method, params }));
|
||||
this.writeLine(JSON.stringify({ jsonrpc: "2.0", method, params }));
|
||||
}
|
||||
|
||||
/** Respond to an approval request from the server */
|
||||
|
|
@ -365,7 +366,7 @@ export class CodexAppServerClient extends EventEmitter {
|
|||
if (this.closed) return;
|
||||
if (!this.process?.stdin?.writable) return;
|
||||
|
||||
this.writeLine(JSON.stringify({ id, result: { decision } }));
|
||||
this.writeLine(JSON.stringify({ jsonrpc: "2.0", id, result: { decision } }));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -942,6 +942,30 @@ describe("getRestoreCommand", () => {
|
|||
expect(cmd).toContain("--ask-for-approval untrusted");
|
||||
});
|
||||
|
||||
it("places flags before positional threadId in resume command", async () => {
|
||||
const content = jsonl(
|
||||
{ type: "session_meta", cwd: "/workspace/test", model: "o3-mini" },
|
||||
{ threadId: "thread-order-test" },
|
||||
);
|
||||
mockReaddir.mockResolvedValue(["sess.jsonl"]);
|
||||
setupMockOpen(content);
|
||||
mockReadFile.mockResolvedValue(content);
|
||||
mockStat.mockResolvedValue({ mtimeMs: 1000 });
|
||||
|
||||
const session = makeSession({ workspacePath: "/workspace/test" });
|
||||
const cmd = await agent.getRestoreCommand!(session, makeProjectConfig({
|
||||
agentConfig: { permissions: "auto-edit", model: "o3-mini" },
|
||||
}));
|
||||
|
||||
expect(cmd).not.toBeNull();
|
||||
// threadId should come after all flags
|
||||
const threadIdIdx = cmd!.indexOf("thread-order-test");
|
||||
const flagIdx = cmd!.indexOf("--ask-for-approval");
|
||||
const modelIdx = cmd!.indexOf("--model");
|
||||
expect(flagIdx).toBeLessThan(threadIdIdx);
|
||||
expect(modelIdx).toBeLessThan(threadIdIdx);
|
||||
});
|
||||
|
||||
it("includes model from project config (overrides session model)", async () => {
|
||||
const content = jsonl(
|
||||
{ type: "session_meta", cwd: "/workspace/test", model: "gpt-4o" },
|
||||
|
|
|
|||
|
|
@ -732,8 +732,9 @@ function createCodexAgent(): Agent {
|
|||
|
||||
// Use Codex's native `resume` subcommand for proper conversation resume.
|
||||
// This restores the full thread state, not just a text prompt re-injection.
|
||||
// Flags are placed before the positional threadId for CLI parser compatibility.
|
||||
const binary = resolvedBinary ?? "codex";
|
||||
const parts: string[] = [binary, "resume", shellEscape(threadId)];
|
||||
const parts: string[] = [binary, "resume"];
|
||||
|
||||
// Add approval policy flags from project config
|
||||
const perms = project.agentConfig?.permissions as string | undefined;
|
||||
|
|
@ -754,6 +755,9 @@ function createCodexAgent(): Agent {
|
|||
}
|
||||
}
|
||||
|
||||
// Positional threadId goes last, after all flags
|
||||
parts.push(shellEscape(threadId));
|
||||
|
||||
return parts.join(" ");
|
||||
},
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue