Skip to content

Commit 574a568

Browse files
committed
fix(agents): preserve successful cancellation outcomes
1 parent 4285f5a commit 574a568

4 files changed

Lines changed: 56 additions & 3 deletions

File tree

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,6 +1081,42 @@ describe("createCodexDynamicToolBridge", () => {
10811081
});
10821082
});
10831083

1084+
it("preserves explicitly successful cancellation outcomes", async () => {
1085+
const onAgentToolResult = vi.fn();
1086+
const cancelledResult = textToolResult("Approval rejected.", {
1087+
ok: true,
1088+
status: "cancelled",
1089+
});
1090+
const bridge = createCodexDynamicToolBridge({
1091+
tools: [
1092+
createTool({
1093+
name: "lobster",
1094+
execute: vi.fn(async () => cancelledResult),
1095+
}),
1096+
],
1097+
signal: new AbortController().signal,
1098+
});
1099+
1100+
const result = await bridge.handleToolCall(
1101+
{
1102+
threadId: "thread-1",
1103+
turnId: "turn-1",
1104+
callId: "call-1",
1105+
namespace: null,
1106+
tool: "lobster",
1107+
arguments: {},
1108+
},
1109+
{ onAgentToolResult },
1110+
);
1111+
1112+
expect(result).toMatchObject({ success: true });
1113+
expect(onAgentToolResult).toHaveBeenCalledWith({
1114+
toolName: "lobster",
1115+
result: cancelledResult,
1116+
isError: false,
1117+
});
1118+
});
1119+
10841120
it("reports sanitized dynamic tool results to the private result observer", async () => {
10851121
const onAgentToolResult = vi.fn();
10861122
const bridge = createCodexDynamicToolBridge({

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
embeddedAgentLog,
1313
type EmbeddedRunAttemptParams,
1414
isToolWrappedWithBeforeToolCallHook,
15+
isToolResultError,
1516
isMessagingTool,
1617
isMessagingToolSendAction,
1718
normalizeHeartbeatToolResponse,
@@ -735,10 +736,16 @@ function readPositiveInteger(value: unknown): number | undefined {
735736
}
736737

737738
function isCodexToolResultError(result: AgentToolResult<unknown>): boolean {
739+
if (isToolResultError(result)) {
740+
return true;
741+
}
738742
const details = result.details;
739743
if (!isRecord(details)) {
740744
return false;
741745
}
746+
if (details.ok === true || details.success === true) {
747+
return false;
748+
}
742749
if (details.timedOut === true) {
743750
return true;
744751
}

src/agents/embedded-agent-subscribe.tools.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,12 @@ describe("isToolResultError", () => {
134134
expect(isToolResultError({ details: { status: "completed", timedOut: true } })).toBe(true);
135135
expect(isToolResultError({ details: { status: "completed", exitCode: 1 } })).toBe(true);
136136
expect(isToolResultError({ details: { status: "completed", exitCode: 0 } })).toBe(false);
137+
expect(isToolResultError({ details: { ok: true, status: "cancelled" } })).toBe(false);
138+
expect(isToolResultError({ details: { success: true, status: "canceled" } })).toBe(false);
139+
expect(isToolResultError({ details: { ok: false, status: "completed" } })).toBe(true);
140+
expect(isToolResultError({ details: { ok: true, status: "cancelled", timedOut: true } })).toBe(
141+
true,
142+
);
137143
});
138144
});
139145

src/agents/embedded-agent-subscribe.tools.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,11 @@ export function extractToolResultMediaPaths(result: unknown): string[] {
543543
export function isToolResultError(result: unknown): boolean {
544544
const details = readToolResultDetails(result);
545545
const normalized = readToolResultStatus(result);
546-
if (
546+
const explicitlySuccessful = details?.ok === true || details?.success === true;
547+
if (details?.ok === false || details?.success === false) {
548+
return true;
549+
}
550+
const hasFailureStatus =
547551
normalized === "error" ||
548552
normalized === "failed" ||
549553
normalized === "failure" ||
@@ -559,8 +563,8 @@ export function isToolResultError(result: unknown): boolean {
559563
normalized === "cancelled" ||
560564
normalized === "canceled" ||
561565
normalized === "killed" ||
562-
normalized === "invalid"
563-
) {
566+
normalized === "invalid";
567+
if (hasFailureStatus && !explicitlySuccessful) {
564568
return true;
565569
}
566570
if (details?.timedOut === true || Boolean(details?.error)) {

0 commit comments

Comments
 (0)