Skip to content

Commit adcfebc

Browse files
authored
fix(codex): classify get_goal read statuses as successful dynamic tool calls (#98659)
isCodexToolResultError fail-closes any dynamic-tool result whose details.status is absent from its non-error allowlist. get_goal returns details.status "found" or "missing" (a successful read of the thread goal, or its absence), neither of which was in the allowlist, so every get_goal call was classified as an error: reported to codex as success: false and persisted on the transcript with isError: true. Sibling #96856 added the write-side goal statuses (created/updated) and the accepted spawn status but missed the read-side get_goal statuses. This adds found/missing alongside them. Genuinely failed statuses stay fail-closed.
1 parent 9541f4e commit adcfebc

2 files changed

Lines changed: 49 additions & 0 deletions

File tree

extensions/codex/src/app-server/dynamic-tools.test.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,53 @@ describe("createCodexDynamicToolBridge", () => {
402402
expect(updatedResult.success).toBe(true);
403403
});
404404

405+
it("treats get_goal read statuses (found / missing) as successful dynamic tool calls", async () => {
406+
const onFoundResult = vi.fn();
407+
const foundBridge = createBridgeWithToolResult(
408+
"get_goal",
409+
textToolResult('{\n "status": "found"\n}', {
410+
status: "found",
411+
goal: { objective: "ship the fix", status: "active" },
412+
}),
413+
);
414+
const foundResult = await foundBridge.handleToolCall(
415+
{
416+
threadId: "thread-1",
417+
turnId: "turn-1",
418+
callId: "call-found",
419+
namespace: null,
420+
tool: "get_goal",
421+
arguments: {},
422+
},
423+
{ onAgentToolResult: onFoundResult },
424+
);
425+
expect(foundResult.success).toBe(true);
426+
expect(onFoundResult).toHaveBeenCalledWith(
427+
expect.objectContaining({ toolName: "get_goal", isError: false }),
428+
);
429+
430+
const onMissingResult = vi.fn();
431+
const missingBridge = createBridgeWithToolResult(
432+
"get_goal",
433+
textToolResult('{\n "status": "missing"\n}', { status: "missing" }),
434+
);
435+
const missingResult = await missingBridge.handleToolCall(
436+
{
437+
threadId: "thread-1",
438+
turnId: "turn-1",
439+
callId: "call-missing",
440+
namespace: null,
441+
tool: "get_goal",
442+
arguments: {},
443+
},
444+
{ onAgentToolResult: onMissingResult },
445+
);
446+
expect(missingResult.success).toBe(true);
447+
expect(onMissingResult).toHaveBeenCalledWith(
448+
expect.objectContaining({ toolName: "get_goal", isError: false }),
449+
);
450+
});
451+
405452
it("keeps available and registered schemas paired with their tools", () => {
406453
const bridge = createCodexDynamicToolBridge({
407454
tools: [

extensions/codex/src/app-server/dynamic-tools.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1187,6 +1187,8 @@ function isCodexToolResultError(result: AgentToolResult<unknown>): boolean {
11871187
status !== "created" &&
11881188
status !== "updated" &&
11891189
status !== "accepted" &&
1190+
status !== "found" &&
1191+
status !== "missing" &&
11901192
status !== "pending" &&
11911193
status !== "started" &&
11921194
status !== "running" &&

0 commit comments

Comments
 (0)