






















@@ -1521,6 +1521,98 @@ describe("runCodexAppServerAttempt", () => {
15211521expect(warnData?.lastActivityReason).toBe("request:item/tool/call:response");
15221522});
152315231524+it("keeps the post-tool completion watchdog armed across dynamic tool completion bookkeeping", async () => {
1525+let notify: (notification: CodexServerNotification) => Promise<void> = async () => undefined;
1526+let handleRequest:
1527+| ((request: { id: string; method: string; params?: unknown }) => Promise<unknown>)
1528+| undefined;
1529+const warn = vi.spyOn(embeddedAgentLog, "warn").mockImplementation(() => undefined);
1530+const request = vi.fn(async (method: string) => {
1531+if (method === "thread/start") {
1532+return threadStartResult("thread-1");
1533+}
1534+if (method === "turn/start") {
1535+return turnStartResult("turn-1", "inProgress");
1536+}
1537+return {};
1538+});
1539+__testing.setCodexAppServerClientFactoryForTests(
1540+async () =>
1541+({
1542+ request,
1543+addNotificationHandler: (handler: typeof notify) => {
1544+notify = handler;
1545+return () => undefined;
1546+},
1547+addRequestHandler: (
1548+handler: (request: {
1549+id: string;
1550+method: string;
1551+params?: unknown;
1552+}) => Promise<unknown>,
1553+) => {
1554+handleRequest = handler;
1555+return () => undefined;
1556+},
1557+}) as never,
1558+);
1559+const params = createParams(
1560+path.join(tempDir, "session.jsonl"),
1561+path.join(tempDir, "workspace"),
1562+);
1563+params.timeoutMs = 60_000;
1564+1565+const run = runCodexAppServerAttempt(params, {
1566+turnCompletionIdleTimeoutMs: 5,
1567+turnTerminalIdleTimeoutMs: 200,
1568+});
1569+await vi.waitFor(() => expect(handleRequest).toBeTypeOf("function"), { interval: 1 });
1570+1571+const toolResult = (await handleRequest?.({
1572+id: "request-tool-1",
1573+method: "item/tool/call",
1574+params: {
1575+threadId: "thread-1",
1576+turnId: "turn-1",
1577+callId: "call-1",
1578+namespace: null,
1579+tool: "message",
1580+arguments: { action: "send", text: "already sent" },
1581+},
1582+})) as { success?: boolean };
1583+expect(toolResult.success).toBe(false);
1584+await notify({
1585+method: "item/completed",
1586+params: {
1587+threadId: "thread-1",
1588+turnId: "turn-1",
1589+item: {
1590+type: "dynamicToolCall",
1591+id: "call-1",
1592+tool: "message",
1593+},
1594+},
1595+});
1596+1597+const result = await run;
1598+expect(result.aborted).toBe(true);
1599+expect(result.timedOut).toBe(true);
1600+expect(result.promptError).toBe(
1601+"codex app-server turn idle timed out waiting for turn/completed",
1602+);
1603+expect(
1604+warn.mock.calls.some(
1605+([message]) => message === "codex app-server turn idle timed out waiting for completion",
1606+),
1607+).toBe(true);
1608+expect(
1609+warn.mock.calls.some(
1610+([message]) =>
1611+message === "codex app-server turn idle timed out waiting for terminal event",
1612+),
1613+).toBe(false);
1614+});
1615+15241616it("keeps waiting when Codex emits a raw assistant item after a dynamic tool response", async () => {
15251617let notify: (notification: CodexServerNotification) => Promise<void> = async () => undefined;
15261618let handleRequest:
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。