@@ -34,7 +34,7 @@ import type { SubagentAnnounceDeliveryResult } from "./subagent-announce-dispatc
3434import { resolveAnnounceOrigin } from "./subagent-announce-origin.js" ;
3535import {
3636 applySubagentWaitOutcome ,
37- buildChildCompletionFindings ,
37+ buildChildCompletionFindingsWithRuns ,
3838 buildCompactAnnounceStatsLine ,
3939 dedupeLatestChildCompletionRows ,
4040 filterCurrentDirectChildCompletionRows ,
@@ -128,6 +128,97 @@ function buildDescendantWakeMessage(params: { findings: string; taskLabel: strin
128128
129129const WAKE_RUN_SUFFIX = ":wake" ;
130130
131+ type MarkRequesterConsumedCompletion = ( params : {
132+ requesterSessionKey : string ;
133+ runStartedAt : number ;
134+ runIds : readonly string [ ] ;
135+ kind : "subagent_descendant_result" ;
136+ consumerRunId ?: string ;
137+ } ) => unknown ;
138+
139+ type RequesterConsumedRegistryRuntime = {
140+ markDescendantCompletionConsumedByRequester ?: MarkRequesterConsumedCompletion ;
141+ } ;
142+
143+ function buildRequesterConsumedChildCompletion (
144+ directChildren : Parameters < typeof filterCurrentDirectChildCompletionRows > [ 0 ] ,
145+ options : Parameters < typeof filterCurrentDirectChildCompletionRows > [ 1 ] ,
146+ ) : { text ?: string ; consumedRunIds : string [ ] } {
147+ const currentDirectChildren = filterCurrentDirectChildCompletionRows ( directChildren , options ) ;
148+ const childCompletion = buildChildCompletionFindingsWithRuns (
149+ dedupeLatestChildCompletionRows ( currentDirectChildren ) ,
150+ ) ;
151+ return { text : childCompletion ?. text , consumedRunIds : childCompletion ?. consumedRunIds ?? [ ] } ;
152+ }
153+
154+ function normalizeRunIdList ( runIds : readonly string [ ] | undefined ) : string [ ] {
155+ const seen = new Set < string > ( ) ;
156+ const result : string [ ] = [ ] ;
157+ for ( const runId of runIds ?? [ ] ) {
158+ const trimmed = runId . trim ( ) ;
159+ if ( ! trimmed || seen . has ( trimmed ) ) {
160+ continue ;
161+ }
162+ seen . add ( trimmed ) ;
163+ result . push ( trimmed ) ;
164+ }
165+ return result ;
166+ }
167+
168+ function deliveryHasRequesterConsumptionCredit ( delivery : SubagentAnnounceDeliveryResult ) : boolean {
169+ if ( ! delivery . delivered ) {
170+ return false ;
171+ }
172+ const phases = delivery . phases ?? [ ] ;
173+ if ( phases . some ( ( phase ) => phase . phase === "direct-primary" && phase . delivered ) ) {
174+ return true ;
175+ }
176+ // Older callers/tests may construct a bare direct result without dispatch phase evidence.
177+ // Fallback steering is never represented as path="direct", so keep legacy direct-delivery
178+ // credit while denying transcript-only steer fallback.
179+ return phases . length === 0 && delivery . path === "direct" ;
180+ }
181+
182+ function recordRequesterConsumedDescendantCompletions ( params : {
183+ delivery : SubagentAnnounceDeliveryResult ;
184+ subagentRegistryRuntime ?: RequesterConsumedRegistryRuntime ;
185+ childSessionKey : string ;
186+ childRunId : string ;
187+ startedAt ?: number ;
188+ childCompletionFindings ?: string ;
189+ consumedChildRunIds ?: readonly string [ ] ;
190+ pendingRequesterConsumedDescendantRunIds ?: readonly string [ ] ;
191+ pendingRequesterConsumedRunStartedAt ?: number ;
192+ } ) : void {
193+ if ( ! deliveryHasRequesterConsumptionCredit ( params . delivery ) ) {
194+ return ;
195+ }
196+ const mark = params . subagentRegistryRuntime ?. markDescendantCompletionConsumedByRequester ;
197+ const startedAt = params . startedAt ?? 0 ;
198+ const directRunIds = params . childCompletionFindings ?. trim ( )
199+ ? normalizeRunIdList ( params . consumedChildRunIds )
200+ : [ ] ;
201+ if ( directRunIds . length > 0 ) {
202+ mark ?.( {
203+ requesterSessionKey : params . childSessionKey ,
204+ runStartedAt : startedAt ,
205+ runIds : directRunIds ,
206+ kind : "subagent_descendant_result" ,
207+ consumerRunId : params . childRunId ,
208+ } ) ;
209+ }
210+ const pendingRunIds = normalizeRunIdList ( params . pendingRequesterConsumedDescendantRunIds ) ;
211+ if ( pendingRunIds . length > 0 ) {
212+ mark ?.( {
213+ requesterSessionKey : params . childSessionKey ,
214+ runStartedAt : params . pendingRequesterConsumedRunStartedAt ?? startedAt ,
215+ runIds : pendingRunIds ,
216+ kind : "subagent_descendant_result" ,
217+ consumerRunId : params . childRunId ,
218+ } ) ;
219+ }
220+ }
221+
131222function stripWakeRunSuffixes ( runId : string ) : string {
132223 let next = runId . trim ( ) ;
133224 while ( next . endsWith ( WAKE_RUN_SUFFIX ) ) {
@@ -172,6 +263,8 @@ async function wakeSubagentRunAfterDescendants(params: {
172263 findings : string ;
173264 announceId : string ;
174265 signal ?: AbortSignal ;
266+ pendingRequesterConsumedDescendantRunIds ?: string [ ] ;
267+ pendingRequesterConsumedRunStartedAt ?: number ;
175268} ) : Promise < boolean > {
176269 if ( params . signal ?. aborted ) {
177270 return false ;
@@ -231,6 +324,8 @@ async function wakeSubagentRunAfterDescendants(params: {
231324 // Persist the wake message as the replacement run's task so that any
232325 // post-restart redispatch reconstructs the correct prompt.
233326 task : wakeMessage ,
327+ pendingRequesterConsumedDescendantRunIds : params . pendingRequesterConsumedDescendantRunIds ,
328+ pendingRequesterConsumedRunStartedAt : params . pendingRequesterConsumedRunStartedAt ,
234329 } ) ;
235330}
236331
@@ -258,6 +353,8 @@ export async function runSubagentAnnounceFlow(params: {
258353 expectsCompletionMessage ?: boolean ;
259354 spawnMode ?: SpawnSubagentMode ;
260355 wakeOnDescendantSettle ?: boolean ;
356+ pendingRequesterConsumedDescendantRunIds ?: string [ ] ;
357+ pendingRequesterConsumedRunStartedAt ?: number ;
261358 signal ?: AbortSignal ;
262359 bestEffortDeliver ?: boolean ;
263360 onDeliveryResult ?: ( delivery : SubagentAnnounceDeliveryResult ) => void ;
@@ -317,6 +414,7 @@ export async function runSubagentAnnounceFlow(params: {
317414 requesterDepth >= 1 || isCronSessionKey ( targetRequesterSessionKey ) ;
318415
319416 let childCompletionFindings : string | undefined ;
417+ let consumedChildRunIds : string [ ] = [ ] ;
320418 let subagentRegistryRuntime :
321419 | Awaited < ReturnType < typeof loadSubagentRegistryRuntime > >
322420 | undefined ;
@@ -348,15 +446,13 @@ export async function runSubagentAnnounceFlow(params: {
348446 } ,
349447 ) ;
350448 if ( Array . isArray ( directChildren ) && directChildren . length > 0 ) {
351- childCompletionFindings = buildChildCompletionFindings (
352- dedupeLatestChildCompletionRows (
353- filterCurrentDirectChildCompletionRows ( directChildren , {
354- requesterSessionKey : params . childSessionKey ,
355- getLatestSubagentRunByChildSessionKey :
356- subagentRegistryRuntime . getLatestSubagentRunByChildSessionKey ,
357- } ) ,
358- ) ,
359- ) ;
449+ const childCompletion = buildRequesterConsumedChildCompletion ( directChildren , {
450+ requesterSessionKey : params . childSessionKey ,
451+ getLatestSubagentRunByChildSessionKey :
452+ subagentRegistryRuntime . getLatestSubagentRunByChildSessionKey ,
453+ } ) ;
454+ consumedChildRunIds = childCompletion . consumedRunIds ;
455+ childCompletionFindings = childCompletion . text ;
360456 }
361457 }
362458 } catch {
@@ -385,6 +481,8 @@ export async function runSubagentAnnounceFlow(params: {
385481 findings : childCompletionFindings ,
386482 announceId : wakeAnnounceId ,
387483 signal : params . signal ,
484+ pendingRequesterConsumedDescendantRunIds : consumedChildRunIds ,
485+ pendingRequesterConsumedRunStartedAt : params . startedAt ,
388486 } ) ;
389487 if ( woke ) {
390488 shouldDeleteChildSession = false ;
@@ -599,6 +697,17 @@ export async function runSubagentAnnounceFlow(params: {
599697 } ) ;
600698 params . onDeliveryResult ?.( delivery ) ;
601699 didAnnounce = delivery . delivered || delivery . terminal === true ;
700+ recordRequesterConsumedDescendantCompletions ( {
701+ delivery,
702+ subagentRegistryRuntime,
703+ childSessionKey : params . childSessionKey ,
704+ childRunId : params . childRunId ,
705+ startedAt : params . startedAt ,
706+ childCompletionFindings,
707+ consumedChildRunIds,
708+ pendingRequesterConsumedDescendantRunIds : params . pendingRequesterConsumedDescendantRunIds ,
709+ pendingRequesterConsumedRunStartedAt : params . pendingRequesterConsumedRunStartedAt ,
710+ } ) ;
602711 if ( ! delivery . delivered && delivery . path === "direct" && delivery . error ) {
603712 defaultRuntime . log (
604713 `[warn] Subagent completion direct announce failed for run ${ params . childRunId } : ${ delivery . error } ` ,
0 commit comments