





















@@ -265,6 +265,158 @@ describe("CodexNativeSubagentMonitor", () => {
265265);
266266});
267267268+it("reconciles transcript final text before delivering empty Codex completion notifications", async () => {
269+const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-codex-subagent-"));
270+const codexHome = path.join(tempDir, "codex-home");
271+const transcriptDir = path.join(codexHome, "sessions", "2026", "06", "07");
272+await fs.mkdir(transcriptDir, { recursive: true });
273+await fs.writeFile(
274+path.join(transcriptDir, "rollout-2026-06-07T08-21-40-child-thread.jsonl"),
275+[
276+JSON.stringify({
277+type: "session_meta",
278+payload: {
279+source: {
280+subagent: {
281+thread_spawn: {
282+parent_thread_id: "parent-thread",
283+depth: 1,
284+},
285+},
286+},
287+},
288+}),
289+JSON.stringify({
290+timestamp: "2026-06-07T08:22:40.000Z",
291+type: "event_msg",
292+payload: {
293+type: "task_complete",
294+last_agent_message: "child transcript final result",
295+completed_at: 1780816960,
296+},
297+}),
298+"",
299+].join("\n"),
300+);
301+const client = createClient();
302+const runtime = createRuntime();
303+const monitor = new CodexNativeSubagentMonitor(client, runtime, {
304+ codexHome,
305+transcriptPollDelaysMs: [60_000],
306+});
307+monitor.registerParent({
308+parentThreadId: "parent-thread",
309+requesterSessionKey: "agent:main:discord:channel:C123",
310+taskRuntimeScope: createTaskScope(),
311+agentId: "main",
312+});
313+314+await notifyChildStarted(client);
315+await client.notify(
316+nativeCompletionNotification({
317+agentPath: "child-thread",
318+statusLabel: "completed",
319+result: null,
320+}),
321+);
322+323+expect(runtime.finalizeTaskRunByRunId).toHaveBeenCalledWith(
324+expect.objectContaining({
325+runId: "codex-thread:child-thread",
326+status: "succeeded",
327+terminalSummary: "child transcript final result",
328+}),
329+);
330+expect(runtime.deliverAgentHarnessTaskCompletion).toHaveBeenCalledTimes(1);
331+expect(runtime.deliverAgentHarnessTaskCompletion).toHaveBeenCalledWith(
332+expect.objectContaining({
333+childSessionKey: "codex-thread:child-thread",
334+childSessionId: "child-thread",
335+status: "succeeded",
336+statusLabel: "task_complete",
337+result: "child transcript final result",
338+}),
339+);
340+341+client.close();
342+});
343+344+it("delivers a typed no-final reason when no transcript source is configured", async () => {
345+const client = createClient();
346+const runtime = createRuntime();
347+const monitor = new CodexNativeSubagentMonitor(client, runtime);
348+monitor.registerParent({
349+parentThreadId: "parent-thread",
350+requesterSessionKey: "agent:main:discord:channel:C123",
351+taskRuntimeScope: createTaskScope(),
352+agentId: "main",
353+});
354+355+await notifyChildStarted(client);
356+await client.notify(
357+nativeCompletionNotification({
358+agentPath: "child-thread",
359+statusLabel: "completed",
360+result: null,
361+}),
362+);
363+364+expect(runtime.deliverAgentHarnessTaskCompletion).toHaveBeenCalledWith(
365+expect.objectContaining({
366+childSessionId: "child-thread",
367+status: "succeeded",
368+statusLabel: "completed_without_final_message",
369+result: "Codex native subagent completed without a final assistant message.",
370+}),
371+);
372+});
373+374+it("falls back to typed no-final delivery when transcript reconciliation is unavailable", async () => {
375+vi.useFakeTimers();
376+try {
377+const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-codex-subagent-"));
378+const codexHome = path.join(tempDir, "codex-home");
379+const client = createClient();
380+const runtime = createRuntime();
381+const monitor = new CodexNativeSubagentMonitor(client, runtime, {
382+ codexHome,
383+transcriptPollDelaysMs: [10],
384+});
385+monitor.registerParent({
386+parentThreadId: "parent-thread",
387+requesterSessionKey: "agent:main:discord:channel:C123",
388+taskRuntimeScope: createTaskScope(),
389+agentId: "main",
390+});
391+392+await notifyChildStarted(client);
393+await client.notify(
394+nativeCompletionNotification({
395+agentPath: "child-thread",
396+statusLabel: "completed",
397+result: null,
398+}),
399+);
400+401+expect(runtime.deliverAgentHarnessTaskCompletion).not.toHaveBeenCalled();
402+403+await vi.advanceTimersByTimeAsync(20);
404+405+expect(runtime.deliverAgentHarnessTaskCompletion).toHaveBeenCalledWith(
406+expect.objectContaining({
407+childSessionId: "child-thread",
408+status: "succeeded",
409+statusLabel: "completed_without_final_message",
410+result: "Codex native subagent completed without a final assistant message.",
411+}),
412+);
413+414+client.close();
415+} finally {
416+vi.useRealTimers();
417+}
418+});
419+268420it("delivers failed parent wakeups from Codex errored subagent notifications", async () => {
269421const client = createClient();
270422const runtime = createRuntime();
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。