mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
refactor(agents): remove retired Codex CLI parser (#116017)
This commit is contained in:
committed by
GitHub
parent
777cfed85a
commit
97d374b357
@@ -156,11 +156,6 @@ function isGeminiCliProvider(providerId: string): boolean {
|
||||
return normalizeLowercaseStringOrEmpty(providerId) === "google-gemini-cli";
|
||||
}
|
||||
|
||||
function isCodexExecJsonlProvider(providerId: string): boolean {
|
||||
const normalized = normalizeLowercaseStringOrEmpty(providerId);
|
||||
return normalized === "codex" || normalized === "codex-cli";
|
||||
}
|
||||
|
||||
function isGeminiStreamJsonDialect(params: {
|
||||
backend: CliBackendConfig;
|
||||
providerId: string;
|
||||
@@ -1174,128 +1169,6 @@ function dispatchGeminiCliStreamingToolEvent(params: {
|
||||
}
|
||||
}
|
||||
|
||||
type CodexToolEvent = {
|
||||
toolCallId: string;
|
||||
name: string;
|
||||
kind: CliToolUseStartDelta["kind"];
|
||||
args: Record<string, unknown>;
|
||||
result?: unknown;
|
||||
isError: boolean;
|
||||
};
|
||||
|
||||
function readCodexToolEvent(item: Record<string, unknown>): CodexToolEvent | null {
|
||||
const toolCallId = typeof item.id === "string" ? item.id.trim() : "";
|
||||
if (!toolCallId) {
|
||||
return null;
|
||||
}
|
||||
const type = normalizeLowercaseStringOrEmpty(item.type);
|
||||
if (type === "command_execution") {
|
||||
return {
|
||||
toolCallId,
|
||||
name: "bash",
|
||||
kind: "tool_use",
|
||||
args: typeof item.command === "string" ? { command: item.command } : {},
|
||||
result: item.aggregated_output,
|
||||
isError: item.status === "failed" || item.status === "declined",
|
||||
};
|
||||
}
|
||||
if (type === "file_change") {
|
||||
return {
|
||||
toolCallId,
|
||||
name: "apply_patch",
|
||||
kind: "tool_use",
|
||||
args: Array.isArray(item.changes) ? { changes: item.changes } : {},
|
||||
result: item.changes,
|
||||
isError: item.status === "failed",
|
||||
};
|
||||
}
|
||||
if (type === "web_search") {
|
||||
return {
|
||||
toolCallId,
|
||||
name: "web_search",
|
||||
kind: "server_tool_use",
|
||||
args: typeof item.query === "string" ? { query: item.query } : {},
|
||||
result: item.query,
|
||||
isError: false,
|
||||
};
|
||||
}
|
||||
if (type !== "mcp_tool_call") {
|
||||
if (type !== "collab_tool_call") {
|
||||
return null;
|
||||
}
|
||||
const tool = typeof item.tool === "string" ? item.tool.trim() : "";
|
||||
if (!tool) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
toolCallId,
|
||||
name: `collab.${tool}`,
|
||||
kind: "server_tool_use",
|
||||
args: {
|
||||
...(typeof item.sender_thread_id === "string"
|
||||
? { sender_thread_id: item.sender_thread_id }
|
||||
: {}),
|
||||
...(Array.isArray(item.receiver_thread_ids)
|
||||
? { receiver_thread_ids: item.receiver_thread_ids }
|
||||
: {}),
|
||||
...(typeof item.prompt === "string" ? { prompt: item.prompt } : {}),
|
||||
},
|
||||
result: item.agents_states,
|
||||
isError: item.status === "failed",
|
||||
};
|
||||
}
|
||||
const server = typeof item.server === "string" ? item.server.trim() : "";
|
||||
const tool = typeof item.tool === "string" ? item.tool.trim() : "";
|
||||
if (!tool) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
toolCallId,
|
||||
name: server ? `${server}.${tool}` : tool,
|
||||
kind: "mcp_tool_use",
|
||||
args: isRecord(item.arguments) ? item.arguments : {},
|
||||
result: item.status === "failed" ? item.error : item.result,
|
||||
isError: item.status === "failed",
|
||||
};
|
||||
}
|
||||
|
||||
function dispatchCodexCliStreamingToolEvent(params: {
|
||||
providerId: string;
|
||||
parsed: Record<string, unknown>;
|
||||
tracker: ToolUseTracker;
|
||||
onToolUseStart?: (delta: CliToolUseStartDelta) => void;
|
||||
onToolResult?: (delta: CliToolResultDelta) => void;
|
||||
}): void {
|
||||
if (
|
||||
!isCodexExecJsonlProvider(params.providerId) ||
|
||||
(params.parsed.type !== "item.started" && params.parsed.type !== "item.completed") ||
|
||||
!isRecord(params.parsed.item)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const event = readCodexToolEvent(params.parsed.item);
|
||||
if (!event) {
|
||||
return;
|
||||
}
|
||||
emitToolStartOnce(
|
||||
params.tracker,
|
||||
event.toolCallId,
|
||||
event.name,
|
||||
event.kind,
|
||||
event.args,
|
||||
params.onToolUseStart,
|
||||
);
|
||||
if (params.parsed.type === "item.completed") {
|
||||
emitToolResultOnce(
|
||||
params.tracker,
|
||||
event.toolCallId,
|
||||
event.isError,
|
||||
event.result,
|
||||
params.onToolResult,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const GEMINI_CLI_ERROR_EVENT_FALLBACK = "Gemini CLI emitted an error event.";
|
||||
const GEMINI_CLI_RESULT_ERROR_FALLBACK = "Gemini CLI result status was error.";
|
||||
|
||||
@@ -1527,13 +1400,6 @@ export function createCliJsonlStreamingParser(params: {
|
||||
}
|
||||
|
||||
if (params.onToolUseStart || params.onToolResult) {
|
||||
dispatchCodexCliStreamingToolEvent({
|
||||
providerId: params.providerId,
|
||||
parsed,
|
||||
tracker: toolTracker,
|
||||
onToolUseStart: params.onToolUseStart,
|
||||
onToolResult: params.onToolResult,
|
||||
});
|
||||
dispatchGeminiCliStreamingToolEvent({
|
||||
backend: params.backend,
|
||||
providerId: params.providerId,
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
/** Tests Codex exec JSONL tool-summary projection through the CLI process boundary. */
|
||||
import { readFileSync } from "node:fs";
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildPreparedCliRunContext,
|
||||
type PreparedCliRunContextOverrides,
|
||||
} from "./cli-runner.test-helpers.js";
|
||||
import { createManagedRun, supervisorSpawnMock } from "./cli-runner.test-support.js";
|
||||
import { executePreparedCliRun } from "./cli-runner/execute.js";
|
||||
|
||||
const CODEX_BACKEND: PreparedCliRunContextOverrides["backend"] = {
|
||||
output: "jsonl",
|
||||
sessionIdFields: ["thread_id"],
|
||||
systemPromptFileConfigArg: undefined,
|
||||
};
|
||||
|
||||
function queueCodexFixture(name: string) {
|
||||
supervisorSpawnMock.mockResolvedValueOnce(
|
||||
createManagedRun({
|
||||
reason: "exit",
|
||||
exitCode: 0,
|
||||
exitSignal: null,
|
||||
durationMs: 10,
|
||||
stdout: readFileSync(`test/fixtures/cli/${name}.jsonl`, "utf8"),
|
||||
stderr: "",
|
||||
timedOut: false,
|
||||
noOutputTimedOut: false,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async function runCodexFixture(name: string) {
|
||||
queueCodexFixture(name);
|
||||
return await executePreparedCliRun(
|
||||
buildPreparedCliRunContext({
|
||||
provider: "codex-cli",
|
||||
model: "gpt-5.5",
|
||||
backend: CODEX_BACKEND,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
supervisorSpawnMock.mockReset();
|
||||
});
|
||||
|
||||
describe("Codex CLI tool summaries", () => {
|
||||
it("emits an explicit empty summary for a successful zero-tool turn", async () => {
|
||||
const output = await runCodexFixture("codex-tool-summary-zero");
|
||||
|
||||
expect(output.toolSummary).toEqual({ calls: 0, tools: [], failures: 0 });
|
||||
});
|
||||
|
||||
it("counts paired MCP lifecycle events once", async () => {
|
||||
const output = await runCodexFixture("codex-tool-summary-paired-mcp");
|
||||
|
||||
expect(output.toolSummary).toEqual({ calls: 1, tools: ["github.search"], failures: 0 });
|
||||
});
|
||||
|
||||
it("projects terminal-only MCP and native items in first-observed order", async () => {
|
||||
const output = await runCodexFixture("codex-tool-summary-terminal-only");
|
||||
|
||||
expect(output.toolSummary).toEqual({
|
||||
calls: 4,
|
||||
tools: ["lookup", "bash", "apply_patch", "web_search"],
|
||||
failures: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it("counts a typed failed terminal MCP item", async () => {
|
||||
const output = await runCodexFixture("codex-tool-summary-failed-mcp");
|
||||
|
||||
expect(output.toolSummary).toEqual({ calls: 1, tools: ["docs.read"], failures: 1 });
|
||||
});
|
||||
|
||||
it("counts a declined command terminal as a failure", async () => {
|
||||
const output = await runCodexFixture("codex-tool-summary-declined-command");
|
||||
|
||||
expect(output.toolSummary).toEqual({ calls: 1, tools: ["bash"], failures: 1 });
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
fixture: "codex-tool-summary-collab-paired",
|
||||
tool: "collab.spawn_agent",
|
||||
failures: 0,
|
||||
},
|
||||
{
|
||||
fixture: "codex-tool-summary-collab-terminal-only",
|
||||
tool: "collab.wait",
|
||||
failures: 0,
|
||||
},
|
||||
{
|
||||
fixture: "codex-tool-summary-collab-failed",
|
||||
tool: "collab.send_input",
|
||||
failures: 1,
|
||||
},
|
||||
{
|
||||
fixture: "codex-tool-summary-collab-close-agent",
|
||||
tool: "collab.close_agent",
|
||||
failures: 0,
|
||||
},
|
||||
])("projects $fixture lifecycle metadata", async ({ fixture, tool, failures }) => {
|
||||
const output = await runCodexFixture(fixture);
|
||||
|
||||
expect(output.toolSummary).toEqual({ calls: 1, tools: [tool], failures });
|
||||
});
|
||||
});
|
||||
@@ -690,6 +690,7 @@ describe("executePreparedCliRun supervisor output capture", () => {
|
||||
|
||||
expect(spawnInput.captureOutput).toBe(false);
|
||||
expect(result.text).toBe("Hello world");
|
||||
expect(result.toolSummary).toEqual({ calls: 0, tools: [], failures: 0 });
|
||||
expect(agentEvents).toEqual([
|
||||
{ text: "Hello", delta: "Hello" },
|
||||
{ text: "Hello world", delta: " world" },
|
||||
@@ -753,7 +754,12 @@ describe("executePreparedCliRun supervisor output capture", () => {
|
||||
context.params.agentId = "coder";
|
||||
|
||||
try {
|
||||
await executePreparedCliRun(context);
|
||||
const result = await executePreparedCliRun(context);
|
||||
expect(result.toolSummary).toEqual({
|
||||
calls: 1,
|
||||
tools: ["mcp__team__lookup"],
|
||||
failures: 0,
|
||||
});
|
||||
} finally {
|
||||
stop();
|
||||
}
|
||||
@@ -859,7 +865,12 @@ describe("executePreparedCliRun supervisor output capture", () => {
|
||||
context.mcpDeliveryCapture = true;
|
||||
|
||||
try {
|
||||
await executePreparedCliRun(context);
|
||||
const result = await executePreparedCliRun(context);
|
||||
expect(result.toolSummary).toEqual({
|
||||
calls: 1,
|
||||
tools: ["mcp__openclaw__message"],
|
||||
failures: 1,
|
||||
});
|
||||
} finally {
|
||||
stop();
|
||||
}
|
||||
@@ -938,7 +949,12 @@ describe("executePreparedCliRun supervisor output capture", () => {
|
||||
context.mcpDeliveryCapture = true;
|
||||
|
||||
try {
|
||||
await executePreparedCliRun(context);
|
||||
const result = await executePreparedCliRun(context);
|
||||
expect(result.toolSummary).toEqual({
|
||||
calls: 1,
|
||||
tools: ["mcp__openclaw__message"],
|
||||
failures: 1,
|
||||
});
|
||||
} finally {
|
||||
stop();
|
||||
}
|
||||
@@ -1021,7 +1037,12 @@ describe("executePreparedCliRun supervisor output capture", () => {
|
||||
context.mcpDeliveryCapture = true;
|
||||
|
||||
try {
|
||||
await executePreparedCliRun(context);
|
||||
const result = await executePreparedCliRun(context);
|
||||
expect(result.toolSummary).toEqual({
|
||||
calls: 2,
|
||||
tools: ["mcp__openclaw__message"],
|
||||
failures: 1,
|
||||
});
|
||||
} finally {
|
||||
stop();
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
{"type":"thread.started","thread_id":"thread-collab-close"}
|
||||
{"type":"turn.started"}
|
||||
{"type":"item.started","item":{"id":"collab-close","type":"collab_tool_call","tool":"close_agent","sender_thread_id":"thread-parent","receiver_thread_ids":["thread-child"],"prompt":null,"agents_states":{"thread-child":{"status":"running","message":null}},"status":"in_progress"}}
|
||||
{"type":"item.completed","item":{"id":"collab-close","type":"collab_tool_call","tool":"close_agent","sender_thread_id":"thread-parent","receiver_thread_ids":["thread-child"],"prompt":null,"agents_states":{"thread-child":{"status":"shutdown","message":null}},"status":"completed"}}
|
||||
{"type":"item.completed","item":{"id":"message-collab-close","type":"agent_message","text":"collab closed"}}
|
||||
{"type":"turn.completed","usage":{"input_tokens":4,"cached_input_tokens":0,"cache_write_input_tokens":0,"output_tokens":2,"reasoning_output_tokens":0}}
|
||||
@@ -1,5 +0,0 @@
|
||||
{"type":"thread.started","thread_id":"thread-collab-failed"}
|
||||
{"type":"turn.started"}
|
||||
{"type":"item.completed","item":{"id":"collab-failed","type":"collab_tool_call","tool":"send_input","sender_thread_id":"thread-parent","receiver_thread_ids":["missing-thread"],"prompt":"continue","agents_states":{"missing-thread":{"status":"not_found","message":"agent not found"}},"status":"failed"}}
|
||||
{"type":"item.completed","item":{"id":"message-collab-failed","type":"agent_message","text":"collab failed"}}
|
||||
{"type":"turn.completed","usage":{"input_tokens":4,"cached_input_tokens":0,"cache_write_input_tokens":0,"output_tokens":2,"reasoning_output_tokens":0}}
|
||||
@@ -1,6 +0,0 @@
|
||||
{"type":"thread.started","thread_id":"thread-collab-paired"}
|
||||
{"type":"turn.started"}
|
||||
{"type":"item.started","item":{"id":"collab-paired","type":"collab_tool_call","tool":"spawn_agent","sender_thread_id":"thread-parent","receiver_thread_ids":[],"prompt":"draft a plan","agents_states":{},"status":"in_progress"}}
|
||||
{"type":"item.completed","item":{"id":"collab-paired","type":"collab_tool_call","tool":"spawn_agent","sender_thread_id":"thread-parent","receiver_thread_ids":["thread-child"],"prompt":"draft a plan","agents_states":{"thread-child":{"status":"running","message":null}},"status":"completed"}}
|
||||
{"type":"item.completed","item":{"id":"message-collab-paired","type":"agent_message","text":"collab paired"}}
|
||||
{"type":"turn.completed","usage":{"input_tokens":5,"cached_input_tokens":0,"cache_write_input_tokens":0,"output_tokens":3,"reasoning_output_tokens":0}}
|
||||
@@ -1,5 +0,0 @@
|
||||
{"type":"thread.started","thread_id":"thread-collab-terminal"}
|
||||
{"type":"turn.started"}
|
||||
{"type":"item.completed","item":{"id":"collab-terminal","type":"collab_tool_call","tool":"wait","sender_thread_id":"thread-parent","receiver_thread_ids":["thread-child"],"prompt":null,"agents_states":{"thread-child":{"status":"completed","message":"done"}},"status":"completed"}}
|
||||
{"type":"item.completed","item":{"id":"message-collab-terminal","type":"agent_message","text":"collab terminal"}}
|
||||
{"type":"turn.completed","usage":{"input_tokens":4,"cached_input_tokens":0,"cache_write_input_tokens":0,"output_tokens":2,"reasoning_output_tokens":0}}
|
||||
@@ -1,6 +0,0 @@
|
||||
{"type":"thread.started","thread_id":"thread-declined"}
|
||||
{"type":"turn.started"}
|
||||
{"type":"item.started","item":{"id":"command-declined","type":"command_execution","command":"rm protected.txt","aggregated_output":"","status":"in_progress"}}
|
||||
{"type":"item.completed","item":{"id":"command-declined","type":"command_execution","command":"rm protected.txt","aggregated_output":"","status":"declined"}}
|
||||
{"type":"item.completed","item":{"id":"message-declined","type":"agent_message","text":"command declined"}}
|
||||
{"type":"turn.completed","usage":{"input_tokens":4,"cached_input_tokens":0,"cache_write_input_tokens":0,"output_tokens":2,"reasoning_output_tokens":0}}
|
||||
@@ -1,5 +0,0 @@
|
||||
{"type":"thread.started","thread_id":"thread-failed"}
|
||||
{"type":"turn.started"}
|
||||
{"type":"item.completed","item":{"id":"mcp-failed","type":"mcp_tool_call","server":"docs","tool":"read","arguments":{"path":"missing"},"error":{"message":"not found"},"status":"failed"}}
|
||||
{"type":"item.completed","item":{"id":"message-failed","type":"agent_message","text":"handled failure"}}
|
||||
{"type":"turn.completed","usage":{"input_tokens":6,"cached_input_tokens":0,"cache_write_input_tokens":0,"output_tokens":3,"reasoning_output_tokens":0}}
|
||||
@@ -1,6 +0,0 @@
|
||||
{"type":"thread.started","thread_id":"thread-paired"}
|
||||
{"type":"turn.started"}
|
||||
{"type":"item.started","item":{"id":"mcp-paired","type":"mcp_tool_call","server":"github","tool":"search","arguments":{"query":"openclaw"},"status":"in_progress"}}
|
||||
{"type":"item.completed","item":{"id":"mcp-paired","type":"mcp_tool_call","server":"github","tool":"search","arguments":{"query":"openclaw"},"result":{"content":[],"structured_content":{}},"status":"completed"}}
|
||||
{"type":"item.completed","item":{"id":"message-paired","type":"agent_message","text":"paired mcp"}}
|
||||
{"type":"turn.completed","usage":{"input_tokens":5,"cached_input_tokens":1,"cache_write_input_tokens":0,"output_tokens":3,"reasoning_output_tokens":0}}
|
||||
@@ -1,8 +0,0 @@
|
||||
{"type":"thread.started","thread_id":"thread-terminal-only"}
|
||||
{"type":"turn.started"}
|
||||
{"type":"item.completed","item":{"id":"mcp-terminal","type":"mcp_tool_call","server":"","tool":"lookup","arguments":{"id":"42"},"result":{"content":[],"structured_content":{}},"status":"completed"}}
|
||||
{"type":"item.completed","item":{"id":"command-terminal","type":"command_execution","command":"pwd","aggregated_output":"/workspace\n","exit_code":0,"status":"completed"}}
|
||||
{"type":"item.completed","item":{"id":"patch-terminal","type":"file_change","changes":[{"path":"README.md","kind":"update"}],"status":"completed"}}
|
||||
{"type":"item.completed","item":{"id":"search-terminal","type":"web_search","query":"OpenClaw"}}
|
||||
{"type":"item.completed","item":{"id":"message-terminal","type":"agent_message","text":"terminal only"}}
|
||||
{"type":"turn.completed","usage":{"input_tokens":8,"cached_input_tokens":0,"cache_write_input_tokens":0,"output_tokens":4,"reasoning_output_tokens":1}}
|
||||
@@ -1,4 +0,0 @@
|
||||
{"type":"thread.started","thread_id":"thread-zero"}
|
||||
{"type":"turn.started"}
|
||||
{"type":"item.completed","item":{"id":"message-zero","type":"agent_message","text":"zero tools"}}
|
||||
{"type":"turn.completed","usage":{"input_tokens":3,"cached_input_tokens":0,"cache_write_input_tokens":0,"output_tokens":2,"reasoning_output_tokens":0}}
|
||||
Reference in New Issue
Block a user