Fix Codex SDK shell command hydration status
This commit is contained in:
@@ -1591,17 +1591,9 @@ function handleRolloutFunctionCall(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (SHELL_FUNCTION_NAMES.has(name)) {
|
if (SHELL_FUNCTION_NAMES.has(name)) {
|
||||||
const args = parseJsonOrObject<Record<string, unknown>>(payload.arguments);
|
const { command, cwd } = buildCommandContext(payload.arguments);
|
||||||
const command = formatCommand(args);
|
|
||||||
const cwd =
|
|
||||||
args &&
|
|
||||||
typeof args === "object" &&
|
|
||||||
typeof (args as { workdir?: unknown }).workdir === "string"
|
|
||||||
? ((args as { workdir?: unknown }).workdir as string)
|
|
||||||
: undefined;
|
|
||||||
if (typeof payload.call_id === "string") {
|
if (typeof payload.call_id === "string") {
|
||||||
const commandValue = command ?? "Command";
|
commandCalls.set(payload.call_id, { command, cwd });
|
||||||
commandCalls.set(payload.call_id, { command: commandValue, cwd });
|
|
||||||
events.push({
|
events.push({
|
||||||
type: "timeline",
|
type: "timeline",
|
||||||
provider: "codex",
|
provider: "codex",
|
||||||
@@ -1612,7 +1604,7 @@ function handleRolloutFunctionCall(
|
|||||||
callId: payload.call_id,
|
callId: payload.call_id,
|
||||||
displayName: buildCommandDisplayName(command),
|
displayName: buildCommandDisplayName(command),
|
||||||
kind: "execute",
|
kind: "execute",
|
||||||
input: { command: commandValue, cwd },
|
input: { command, cwd },
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1659,40 +1651,7 @@ function finalizeRolloutFunctionCall(
|
|||||||
if (!command) {
|
if (!command) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const result = parseJsonOrObject<CommandExecutionResult>(payload.output);
|
const { status, output } = buildCommandOutput(command, payload.output);
|
||||||
const parsedOutput =
|
|
||||||
typeof payload.output === "string"
|
|
||||||
? parseCommandOutputText(payload.output)
|
|
||||||
: null;
|
|
||||||
const exitCode = result?.metadata?.exit_code ?? parsedOutput?.exitCode;
|
|
||||||
const status =
|
|
||||||
exitCode === undefined || exitCode === 0 ? "completed" : "failed";
|
|
||||||
const stdout =
|
|
||||||
result?.stdout ??
|
|
||||||
(typeof result?.output === "string" ? result.output : undefined) ??
|
|
||||||
parsedOutput?.stdout;
|
|
||||||
const metadata =
|
|
||||||
result?.metadata ??
|
|
||||||
(typeof exitCode === "number" ? { exit_code: exitCode } : undefined);
|
|
||||||
|
|
||||||
// Build structured command output
|
|
||||||
let output: unknown;
|
|
||||||
if (stdout !== undefined) {
|
|
||||||
output = {
|
|
||||||
type: "command" as const,
|
|
||||||
command: command.command,
|
|
||||||
output: stdout,
|
|
||||||
exitCode,
|
|
||||||
cwd: command.cwd,
|
|
||||||
metadata,
|
|
||||||
};
|
|
||||||
} else if (result && typeof result === "object") {
|
|
||||||
output = { ...result, metadata: metadata ?? result.metadata };
|
|
||||||
} else if (metadata) {
|
|
||||||
output = { metadata };
|
|
||||||
} else {
|
|
||||||
output = result;
|
|
||||||
}
|
|
||||||
|
|
||||||
events.push({
|
events.push({
|
||||||
type: "timeline",
|
type: "timeline",
|
||||||
@@ -1748,6 +1707,27 @@ function handleRolloutCustomToolCall(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (payload?.name && SHELL_FUNCTION_NAMES.has(payload.name)) {
|
||||||
|
const commandContext = buildCommandContext(payload.input);
|
||||||
|
const { status, output } = buildCommandOutput(commandContext, payload.output);
|
||||||
|
events.push({
|
||||||
|
type: "timeline",
|
||||||
|
provider: "codex",
|
||||||
|
item: createToolCallTimelineItem({
|
||||||
|
server: "command",
|
||||||
|
tool: "shell",
|
||||||
|
status: payload.status ?? status,
|
||||||
|
callId:
|
||||||
|
typeof payload.call_id === "string" ? payload.call_id : undefined,
|
||||||
|
displayName: buildCommandDisplayName(commandContext.command),
|
||||||
|
kind: "execute",
|
||||||
|
input: { command: commandContext.command, cwd: commandContext.cwd },
|
||||||
|
output,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof payload?.name === "string") {
|
if (typeof payload?.name === "string") {
|
||||||
events.push({
|
events.push({
|
||||||
type: "timeline",
|
type: "timeline",
|
||||||
@@ -1810,6 +1790,7 @@ type RolloutCustomToolCallPayload = {
|
|||||||
type: "custom_tool_call";
|
type: "custom_tool_call";
|
||||||
name?: string;
|
name?: string;
|
||||||
status?: string;
|
status?: string;
|
||||||
|
call_id?: string;
|
||||||
input?: string | Record<string, unknown>;
|
input?: string | Record<string, unknown>;
|
||||||
output?: unknown;
|
output?: unknown;
|
||||||
};
|
};
|
||||||
@@ -2158,6 +2139,64 @@ function parseCommandOutputText(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildCommandContext(input: unknown): { command: string; cwd?: string } {
|
||||||
|
const args = parseJsonOrObject<Record<string, unknown>>(input);
|
||||||
|
const commandFromArgs = formatCommand(args);
|
||||||
|
let command = commandFromArgs;
|
||||||
|
if (!command && typeof input === "string") {
|
||||||
|
const trimmed = input.trim();
|
||||||
|
if (trimmed && !trimmed.startsWith("{") && !trimmed.startsWith("[")) {
|
||||||
|
command = trimmed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const cwd =
|
||||||
|
args &&
|
||||||
|
typeof args === "object" &&
|
||||||
|
typeof (args as { workdir?: unknown }).workdir === "string"
|
||||||
|
? ((args as { workdir?: unknown }).workdir as string)
|
||||||
|
: undefined;
|
||||||
|
return { command: command ?? "Command", cwd };
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildCommandOutput(
|
||||||
|
command: { command: string; cwd?: string },
|
||||||
|
rawOutput: unknown
|
||||||
|
): { status: "completed" | "failed"; output: unknown } {
|
||||||
|
const result = parseJsonOrObject<CommandExecutionResult>(rawOutput);
|
||||||
|
const parsedOutput =
|
||||||
|
typeof rawOutput === "string" ? parseCommandOutputText(rawOutput) : null;
|
||||||
|
const exitCode = result?.metadata?.exit_code ?? parsedOutput?.exitCode;
|
||||||
|
const status =
|
||||||
|
exitCode === undefined || exitCode === 0 ? "completed" : "failed";
|
||||||
|
const stdout =
|
||||||
|
result?.stdout ??
|
||||||
|
(typeof result?.output === "string" ? result.output : undefined) ??
|
||||||
|
parsedOutput?.stdout;
|
||||||
|
const metadata =
|
||||||
|
result?.metadata ??
|
||||||
|
(typeof exitCode === "number" ? { exit_code: exitCode } : undefined);
|
||||||
|
|
||||||
|
let output: unknown;
|
||||||
|
if (stdout !== undefined) {
|
||||||
|
output = {
|
||||||
|
type: "command" as const,
|
||||||
|
command: command.command,
|
||||||
|
output: stdout,
|
||||||
|
exitCode,
|
||||||
|
cwd: command.cwd,
|
||||||
|
metadata,
|
||||||
|
};
|
||||||
|
} else if (result && typeof result === "object") {
|
||||||
|
output = { ...result, metadata: metadata ?? result.metadata };
|
||||||
|
} else if (metadata) {
|
||||||
|
output = { metadata };
|
||||||
|
} else {
|
||||||
|
output = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { status, output };
|
||||||
|
}
|
||||||
|
|
||||||
function formatCommand(args: unknown): string | null {
|
function formatCommand(args: unknown): string | null {
|
||||||
if (!args || typeof args !== "object") {
|
if (!args || typeof args !== "object") {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -259,7 +259,8 @@ Build a new Codex MCP provider side‑by‑side with the existing Codex SDK prov
|
|||||||
- If failures: add follow-up fix tasks immediately after this item.
|
- If failures: add follow-up fix tasks immediately after this item.
|
||||||
- **Done (2025-12-24 20:25)**: Ran `npm run test --workspace=@paseo/server`; failures in Codex SDK persisted shell_command hydration and multiple Codex MCP mapping/persistence/permission checks; `agent-mcp.e2e.test.ts` hung and was interrupted.
|
- **Done (2025-12-24 20:25)**: Ran `npm run test --workspace=@paseo/server`; failures in Codex SDK persisted shell_command hydration and multiple Codex MCP mapping/persistence/permission checks; `agent-mcp.e2e.test.ts` hung and was interrupted.
|
||||||
|
|
||||||
- [ ] **Fix**: Codex SDK persisted shell_command hydration still missing completed status.
|
- [x] **Fix**: Codex SDK persisted shell_command hydration still missing completed status.
|
||||||
|
- **Done (2025-12-24 20:31)**: Mapped shell_command custom_tool_call entries to command tool calls and normalized output/status during rollout hydration.
|
||||||
|
|
||||||
- [ ] **Fix**: Codex MCP command output should include exit codes for command tool calls (missing in timeline mapping).
|
- [ ] **Fix**: Codex MCP command output should include exit codes for command tool calls (missing in timeline mapping).
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user