mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
Fix Codex SDK shell command hydration status
This commit is contained in:
@@ -1591,17 +1591,9 @@ function handleRolloutFunctionCall(
|
||||
}
|
||||
|
||||
if (SHELL_FUNCTION_NAMES.has(name)) {
|
||||
const args = parseJsonOrObject<Record<string, unknown>>(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;
|
||||
const { command, cwd } = buildCommandContext(payload.arguments);
|
||||
if (typeof payload.call_id === "string") {
|
||||
const commandValue = command ?? "Command";
|
||||
commandCalls.set(payload.call_id, { command: commandValue, cwd });
|
||||
commandCalls.set(payload.call_id, { command, cwd });
|
||||
events.push({
|
||||
type: "timeline",
|
||||
provider: "codex",
|
||||
@@ -1612,7 +1604,7 @@ function handleRolloutFunctionCall(
|
||||
callId: payload.call_id,
|
||||
displayName: buildCommandDisplayName(command),
|
||||
kind: "execute",
|
||||
input: { command: commandValue, cwd },
|
||||
input: { command, cwd },
|
||||
}),
|
||||
});
|
||||
}
|
||||
@@ -1659,40 +1651,7 @@ function finalizeRolloutFunctionCall(
|
||||
if (!command) {
|
||||
return;
|
||||
}
|
||||
const result = parseJsonOrObject<CommandExecutionResult>(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;
|
||||
}
|
||||
const { status, output } = buildCommandOutput(command, payload.output);
|
||||
|
||||
events.push({
|
||||
type: "timeline",
|
||||
@@ -1748,6 +1707,27 @@ function handleRolloutCustomToolCall(
|
||||
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") {
|
||||
events.push({
|
||||
type: "timeline",
|
||||
@@ -1810,6 +1790,7 @@ type RolloutCustomToolCallPayload = {
|
||||
type: "custom_tool_call";
|
||||
name?: string;
|
||||
status?: string;
|
||||
call_id?: string;
|
||||
input?: string | Record<string, 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 {
|
||||
if (!args || typeof args !== "object") {
|
||||
return null;
|
||||
|
||||
3
plan.md
3
plan.md
@@ -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.
|
||||
- **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).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user