


























@@ -55,6 +55,10 @@ type ChildState = {
5555transcriptPollAttempt: number;
5656transcriptPollTimer?: ReturnType<typeof setTimeout>;
5757transcriptTerminal: boolean;
58+idle: boolean;
59+lastAgentMessage?: string;
60+lastAgentMessageAt?: number;
61+agentMessageCompletionDelivered: boolean;
5862pendingCompletion?: CodexNativeSubagentCompletion;
5963pendingCompletionEventAt?: number;
6064completionDeliveryAttempt: number;
@@ -211,7 +215,10 @@ export class CodexNativeSubagentMonitor {
211215});
212216}
213217}
218+const childThreadId = this.recordChildAgentMessage(notification);
219+const idleChildThreadId = this.recordChildIdle(notification);
214220await this.handleCompletionNotification(notification);
221+await this.processChildAgentMessageCompletion(childThreadId ?? idleChildThreadId);
215222}
216223217224private ensureParentTaskRuntime(state: ParentState): void {
@@ -552,6 +559,8 @@ export class CodexNativeSubagentMonitor {
552559parentThreadId: normalizedParentThreadId,
553560transcriptPollAttempt: 0,
554561transcriptTerminal: false,
562+idle: false,
563+agentMessageCompletionDelivered: false,
555564completionDeliveryAttempt: 0,
556565};
557566this.childStates.set(normalizedChildThreadId, childState);
@@ -561,6 +570,83 @@ export class CodexNativeSubagentMonitor {
561570}
562571}
563572573+private recordChildAgentMessage(notification: CodexServerNotification): string | undefined {
574+if (notification.method !== "item/completed") {
575+return undefined;
576+}
577+const params = isJsonObject(notification.params) ? notification.params : undefined;
578+const item = isJsonObject(params?.item) ? params.item : undefined;
579+if (!params || !item || readString(item, "type") !== "agentMessage") {
580+return undefined;
581+}
582+const childThreadId = readString(params, "threadId")?.trim();
583+const childState = childThreadId ? this.childStates.get(childThreadId) : undefined;
584+if (!childState || childState.transcriptTerminal) {
585+return undefined;
586+}
587+// Codex app-server can report the child final answer as the child thread's
588+// own agentMessage without also emitting a parent subagent notification.
589+// Pair it with idle below so commentary does not become a false terminal.
590+const phase = readString(item, "phase");
591+if (phase === "commentary") {
592+return undefined;
593+}
594+const text = readString(item, "text")?.trim();
595+if (!text) {
596+return undefined;
597+}
598+childState.lastAgentMessage = text;
599+childState.lastAgentMessageAt = Date.now();
600+return childState.childThreadId;
601+}
602+603+private recordChildIdle(notification: CodexServerNotification): string | undefined {
604+if (notification.method !== "thread/status/changed") {
605+return undefined;
606+}
607+const params = isJsonObject(notification.params) ? notification.params : undefined;
608+if (!params || !isJsonObject(params.status) || readString(params.status, "type") !== "idle") {
609+return undefined;
610+}
611+const childThreadId = readString(params, "threadId")?.trim();
612+const childState = childThreadId ? this.childStates.get(childThreadId) : undefined;
613+if (!childState || childState.transcriptTerminal) {
614+return undefined;
615+}
616+childState.idle = true;
617+return childState.childThreadId;
618+}
619+620+private async processChildAgentMessageCompletion(
621+childThreadId: string | undefined,
622+): Promise<void> {
623+const childState = childThreadId ? this.childStates.get(childThreadId) : undefined;
624+if (
625+!childState ||
626+!childState.idle ||
627+childState.transcriptTerminal ||
628+childState.agentMessageCompletionDelivered ||
629+!childState.lastAgentMessage
630+) {
631+return;
632+}
633+const state = this.parentStates.get(childState.parentThreadId);
634+if (!state) {
635+return;
636+}
637+childState.agentMessageCompletionDelivered = true;
638+await this.processCompletion(
639+state,
640+{
641+childThreadId: childState.childThreadId,
642+status: "succeeded",
643+statusLabel: "agent_message",
644+result: childState.lastAgentMessage,
645+},
646+childState.lastAgentMessageAt,
647+);
648+}
649+564650private ensureChildState(parentThreadId: string, childThreadId: string): ChildState {
565651this.registerChildThread(parentThreadId, childThreadId);
566652return this.childStates.get(childThreadId.trim())!;
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。