

























@@ -203,10 +203,191 @@ describe("CodexNativeSubagentMonitor", () => {
203203task: "inspect the repo",
204204}),
205205);
206+expect(runtime.recordTaskRunProgressByRunId).toHaveBeenCalledWith(
207+expect.objectContaining({
208+runId: "codex-thread:child-thread",
209+progressSummary: "Codex native subagent is idle.",
210+}),
211+);
212+expect(runtime.finalizeTaskRunByRunId).not.toHaveBeenCalled();
213+});
214+215+it.each([
216+{ label: "remote V1", codexHome: undefined, finalizes: true },
217+{ label: "local transcript-backed V1", codexHome: "/tmp/codex-home", finalizes: false },
218+])(
219+"uses collab completion as a terminal fallback only for $label",
220+async ({ codexHome, finalizes }) => {
221+const client = createClient();
222+const runtime = createRuntime();
223+const monitor = new CodexNativeSubagentMonitor(client, runtime, { codexHome });
224+monitor.registerParent({
225+parentThreadId: "parent-thread",
226+requesterSessionKey: "agent:main:main",
227+taskRuntimeScope: createTaskScope("agent:main:main"),
228+agentId: "main",
229+});
230+231+await notifyChildStarted(client, "parent-thread", "child-thread", "");
232+await client.notify({
233+method: "item/completed",
234+params: {
235+threadId: "parent-thread",
236+item: {
237+type: "collabAgentToolCall",
238+tool: "wait",
239+senderThreadId: "parent-thread",
240+agentsStates: {
241+"child-thread": {
242+status: "completed",
243+message: "child final result",
244+},
245+},
246+},
247+},
248+});
249+250+if (finalizes) {
251+expect(runtime.finalizeTaskRunByRunId).toHaveBeenCalledWith(
252+expect.objectContaining({
253+runId: "codex-thread:child-thread",
254+status: "succeeded",
255+terminalSummary: "child final result",
256+}),
257+);
258+} else {
259+expect(runtime.recordTaskRunProgressByRunId).toHaveBeenCalledWith(
260+expect.objectContaining({
261+runId: "codex-thread:child-thread",
262+progressSummary: "child final result",
263+}),
264+);
265+expect(runtime.finalizeTaskRunByRunId).not.toHaveBeenCalled();
266+}
267+monitor.dispose();
268+},
269+);
270+271+it("does not complete mirrored task rows from idle status before native completion", async () => {
272+const client = createClient();
273+const runtime = createRuntime();
274+const monitor = new CodexNativeSubagentMonitor(client, runtime);
275+monitor.registerParent({
276+parentThreadId: "parent-thread",
277+requesterSessionKey: "agent:main:discord:channel:C123",
278+taskRuntimeScope: createTaskScope(),
279+agentId: "main",
280+});
281+282+await notifyChildStarted(client);
283+await client.notify({
284+method: "thread/status/changed",
285+params: {
286+threadId: "child-thread",
287+status: { type: "idle" },
288+},
289+});
290+291+expect(runtime.finalizeTaskRunByRunId).not.toHaveBeenCalled();
292+expect(runtime.deliverAgentHarnessTaskCompletion).not.toHaveBeenCalled();
293+294+await client.notify(
295+nativeCompletionNotification({
296+agentPath: "child-thread",
297+statusLabel: "completed",
298+result: "child final result",
299+}),
300+);
301+302+expect(runtime.finalizeTaskRunByRunId).toHaveBeenCalledWith(
303+expect.objectContaining({
304+runId: "codex-thread:child-thread",
305+status: "succeeded",
306+terminalSummary: "child final result",
307+}),
308+);
309+expect(runtime.deliverAgentHarnessTaskCompletion).toHaveBeenCalledWith(
310+expect.objectContaining({
311+childSessionId: "child-thread",
312+result: "child final result",
313+}),
314+);
315+});
316+317+it("keeps late idle lifecycle updates from overwriting native completion results", async () => {
318+const client = createClient();
319+const runtime = createRuntime();
320+const monitor = new CodexNativeSubagentMonitor(client, runtime);
321+monitor.registerParent({
322+parentThreadId: "parent-thread",
323+requesterSessionKey: "agent:main:discord:channel:C123",
324+taskRuntimeScope: createTaskScope(),
325+agentId: "main",
326+});
327+328+await notifyChildStarted(client);
329+await client.notify(
330+nativeCompletionNotification({
331+agentPath: "child-thread",
332+statusLabel: "completed",
333+result: "child final result",
334+}),
335+);
336+runtime.recordTaskRunProgressByRunId.mockClear();
337+338+await client.notify({
339+method: "thread/status/changed",
340+params: {
341+threadId: "child-thread",
342+status: { type: "idle" },
343+},
344+});
345+346+expect(runtime.recordTaskRunProgressByRunId).not.toHaveBeenCalled();
347+expect(runtime.finalizeTaskRunByRunId).toHaveBeenCalledTimes(1);
206348expect(runtime.finalizeTaskRunByRunId).toHaveBeenCalledWith(
207349expect.objectContaining({
208350runId: "codex-thread:child-thread",
209351status: "succeeded",
352+terminalSummary: "child final result",
353+}),
354+);
355+});
356+357+it("keeps later lifecycle errors from rewriting native completion results", async () => {
358+const client = createClient();
359+const runtime = createRuntime();
360+const monitor = new CodexNativeSubagentMonitor(client, runtime);
361+monitor.registerParent({
362+parentThreadId: "parent-thread",
363+requesterSessionKey: "agent:main:discord:channel:C123",
364+taskRuntimeScope: createTaskScope(),
365+agentId: "main",
366+});
367+368+await notifyChildStarted(client);
369+await client.notify(
370+nativeCompletionNotification({
371+agentPath: "child-thread",
372+statusLabel: "completed",
373+result: "child final result",
374+}),
375+);
376+377+await client.notify({
378+method: "thread/status/changed",
379+params: {
380+threadId: "child-thread",
381+status: { type: "systemError" },
382+},
383+});
384+385+expect(runtime.finalizeTaskRunByRunId).toHaveBeenCalledTimes(1);
386+expect(runtime.finalizeTaskRunByRunId).toHaveBeenCalledWith(
387+expect.objectContaining({
388+runId: "codex-thread:child-thread",
389+status: "succeeded",
390+terminalSummary: "child final result",
210391}),
211392);
212393});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。