

























11// Codex tests cover attempt turn watches plugin behavior.
22import { MAX_TIMER_TIMEOUT_MS } from "openclaw/plugin-sdk/number-runtime";
33import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
4+import { updateActiveCompletionBlockerItemIds } from "./attempt-notifications.js";
45import { createCodexAttemptTurnWatchController } from "./attempt-turn-watches.js";
5667describe("Codex app-server attempt turn watches", () => {
@@ -23,6 +24,7 @@ describe("Codex app-server attempt turn watches", () => {
2324let terminalQueued = false;
2425let activeRequests = 0;
2526let activeItems = 0;
27+let activeCompletionBlockers = 0;
2628const interrupts: Array<Record<string, unknown>> = [];
2729const timeouts: Array<Record<string, unknown>> = [];
2830const events: Array<{ name: string; fields: Record<string, unknown> }> = [];
@@ -36,6 +38,7 @@ describe("Codex app-server attempt turn watches", () => {
3638isTerminalTurnNotificationQueued: () => terminalQueued,
3739getActiveAppServerTurnRequests: () => activeRequests,
3840getActiveTurnItemCount: () => activeItems,
41+getActiveCompletionBlockerItemCount: () => activeCompletionBlockers,
3942turnCompletionIdleTimeoutMs: 10,
4043turnAssistantCompletionIdleTimeoutMs: 10,
4144turnAttemptIdleTimeoutMs: 10,
@@ -69,6 +72,9 @@ describe("Codex app-server attempt turn watches", () => {
6972set activeItems(value: number) {
7073activeItems = value;
7174},
75+set activeCompletionBlockers(value: number) {
76+activeCompletionBlockers = value;
77+},
7278 interrupts,
7379 timeouts,
7480 events,
@@ -155,6 +161,32 @@ describe("Codex app-server attempt turn watches", () => {
155161expect(harness.abortController.signal.aborted).toBe(false);
156162});
157163164+it("waits for active completion blocker items before firing completion idle timeout", () => {
165+const harness = createController();
166+harness.activeCompletionBlockers = 1;
167+168+harness.controller.touchActivity("request:mcpServer/elicitation/request:response", {
169+arm: true,
170+});
171+vi.advanceTimersByTime(10);
172+173+expect(harness.timeouts).toEqual([]);
174+expect(harness.abortController.signal.aborted).toBe(false);
175+176+harness.activeCompletionBlockers = 0;
177+harness.controller.touchActivity("notification:item/completed");
178+vi.advanceTimersByTime(10);
179+180+expect(harness.timeouts).toMatchObject([
181+{
182+kind: "completion",
183+idleMs: 10,
184+timeoutMs: 10,
185+lastActivityReason: "notification:item/completed",
186+},
187+]);
188+});
189+158190it("releases a completed assistant item after the assistant idle guard expires", () => {
159191const harness = createController();
160192@@ -214,3 +246,41 @@ describe("Codex app-server attempt turn watches", () => {
214246expect(harness.abortController.signal.reason).toBe("turn_progress_idle_timeout");
215247});
216248});
249+250+describe("Codex completion blocker item tracking", () => {
251+it.each([
252+"collabAgentToolCall",
253+"commandExecution",
254+"dynamicToolCall",
255+"fileChange",
256+"imageGeneration",
257+"imageView",
258+"mcpToolCall",
259+"webSearch",
260+])("tracks the %s lifecycle", (type) => {
261+const activeItemIds = new Set<string>();
262+updateActiveCompletionBlockerItemIds(
263+{ method: "item/started", params: { item: { id: "item-1", type } } },
264+activeItemIds,
265+);
266+expect(activeItemIds).toEqual(new Set(["item-1"]));
267+268+updateActiveCompletionBlockerItemIds(
269+{ method: "item/completed", params: { item: { id: "item-1", type } } },
270+activeItemIds,
271+);
272+expect(activeItemIds).toEqual(new Set());
273+});
274+275+it.each(["agentMessage", "contextCompaction", "plan", "reasoning", "subAgentActivity"])(
276+"does not track the %s lifecycle",
277+(type) => {
278+const activeItemIds = new Set<string>();
279+updateActiveCompletionBlockerItemIds(
280+{ method: "item/started", params: { item: { id: "item-1", type } } },
281+activeItemIds,
282+);
283+expect(activeItemIds).toEqual(new Set());
284+},
285+);
286+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。