@@ -75,6 +75,53 @@ async function loadCleanupBrowserSessionsForLifecycleEnd(): Promise<
7575 return ( await browserCleanupLoader . load ( ) ) . cleanupBrowserSessionsForLifecycleEnd ;
7676}
7777
78+ function resolveSubagentRunDeadlineMs (
79+ entry : SubagentRunRecord ,
80+ observedStartedAt ?: number ,
81+ ) : number | undefined {
82+ const timeoutSeconds = entry . runTimeoutSeconds ;
83+ if (
84+ typeof timeoutSeconds !== "number" ||
85+ ! Number . isFinite ( timeoutSeconds ) ||
86+ timeoutSeconds <= 0
87+ ) {
88+ return undefined ;
89+ }
90+ const startedAt =
91+ typeof observedStartedAt === "number" && Number . isFinite ( observedStartedAt )
92+ ? observedStartedAt
93+ : typeof entry . startedAt === "number" && Number . isFinite ( entry . startedAt )
94+ ? entry . startedAt
95+ : entry . createdAt ;
96+ return Number . isFinite ( startedAt ) ? startedAt + Math . floor ( timeoutSeconds * 1000 ) : undefined ;
97+ }
98+
99+ function shouldPreserveExplicitRunTimeout ( params : { entry : SubagentRunRecord } ) : boolean {
100+ if ( params . entry . outcome ?. status !== "timeout" || typeof params . entry . endedAt !== "number" ) {
101+ return false ;
102+ }
103+ if (
104+ params . entry . cleanupHandled ||
105+ typeof params . entry . cleanupCompletedAt === "number" ||
106+ typeof params . entry . endedHookEmittedAt === "number" ||
107+ params . entry . delivery ?. status === "delivered" ||
108+ typeof params . entry . delivery ?. announcedAt === "number"
109+ ) {
110+ return true ;
111+ }
112+ return false ;
113+ }
114+
115+ function resolveExpiredExplicitRunDeadlineMs ( params : {
116+ entry : SubagentRunRecord ;
117+ nextOutcome : SubagentRunOutcome ;
118+ nextEndedAt : number ;
119+ observedStartedAt ?: number ;
120+ } ) : number | undefined {
121+ const deadlineMs = resolveSubagentRunDeadlineMs ( params . entry , params . observedStartedAt ) ;
122+ return deadlineMs !== undefined && params . nextEndedAt > deadlineMs ? deadlineMs : undefined ;
123+ }
124+
78125export function createSubagentRegistryLifecycleController ( params : {
79126 runs : Map < string , SubagentRunRecord > ;
80127 resumedRuns : Set < string > ;
@@ -1016,6 +1063,7 @@ export function createSubagentRegistryLifecycleController(params: {
10161063 sendFarewell ?: boolean ;
10171064 accountId ?: string ;
10181065 triggerCleanup : boolean ;
1066+ startedAt ?: number ;
10191067 } ) => {
10201068 params . clearPendingLifecycleError ( completeParams . runId ) ;
10211069 const entry = params . runs . get ( completeParams . runId ) ;
@@ -1036,8 +1084,40 @@ export function createSubagentRegistryLifecycleController(params: {
10361084 mutated = true ;
10371085 }
10381086
1039- const endedAt =
1040- typeof completeParams . endedAt === "number" ? completeParams . endedAt : Date . now ( ) ;
1087+ let endedAt = typeof completeParams . endedAt === "number" ? completeParams . endedAt : Date . now ( ) ;
1088+ let completionOutcome = completeParams . outcome ;
1089+ let completionReason = completeParams . reason ;
1090+ if (
1091+ shouldPreserveExplicitRunTimeout ( {
1092+ entry,
1093+ } )
1094+ ) {
1095+ return ;
1096+ }
1097+
1098+ const observedStartedAt =
1099+ typeof completeParams . startedAt === "number" && Number . isFinite ( completeParams . startedAt )
1100+ ? completeParams . startedAt
1101+ : undefined ;
1102+ if ( observedStartedAt !== undefined && entry . startedAt !== observedStartedAt ) {
1103+ entry . startedAt = observedStartedAt ;
1104+ if ( typeof entry . sessionStartedAt !== "number" ) {
1105+ entry . sessionStartedAt = observedStartedAt ;
1106+ }
1107+ mutated = true ;
1108+ }
1109+
1110+ const expiredDeadlineMs = resolveExpiredExplicitRunDeadlineMs ( {
1111+ entry,
1112+ nextOutcome : completionOutcome ,
1113+ nextEndedAt : endedAt ,
1114+ observedStartedAt,
1115+ } ) ;
1116+ if ( expiredDeadlineMs !== undefined ) {
1117+ endedAt = expiredDeadlineMs ;
1118+ completionOutcome = { status : "timeout" } ;
1119+ completionReason = SUBAGENT_ENDED_REASON_COMPLETE ;
1120+ }
10411121 if ( entry . endedAt !== endedAt ) {
10421122 entry . endedAt = endedAt ;
10431123 entry . execution = {
@@ -1048,7 +1128,7 @@ export function createSubagentRegistryLifecycleController(params: {
10481128 } ;
10491129 mutated = true ;
10501130 }
1051- const outcome = withSubagentOutcomeTiming ( completeParams . outcome , {
1131+ const outcome = withSubagentOutcomeTiming ( completionOutcome , {
10521132 startedAt : entry . startedAt ,
10531133 endedAt,
10541134 } ) ;
@@ -1070,8 +1150,8 @@ export function createSubagentRegistryLifecycleController(params: {
10701150 } ;
10711151 mutated = true ;
10721152 }
1073- if ( entry . endedReason !== completeParams . reason ) {
1074- entry . endedReason = completeParams . reason ;
1153+ if ( entry . endedReason !== completionReason ) {
1154+ entry . endedReason = completionReason ;
10751155 mutated = true ;
10761156 }
10771157 if ( entry . pauseReason !== undefined ) {
@@ -1114,7 +1194,7 @@ export function createSubagentRegistryLifecycleController(params: {
11141194 ! suppressedForSteerRestart &&
11151195 params . shouldEmitEndedHookForRun ( {
11161196 entry,
1117- reason : completeParams . reason ,
1197+ reason : completionReason ,
11181198 } ) ;
11191199 const shouldDeferEndedHook =
11201200 shouldEmitEndedHook &&
@@ -1124,7 +1204,7 @@ export function createSubagentRegistryLifecycleController(params: {
11241204 if ( ! shouldDeferEndedHook && shouldEmitEndedHook ) {
11251205 await params . emitSubagentEndedHookForRun ( {
11261206 entry,
1127- reason : completeParams . reason ,
1207+ reason : completionReason ,
11281208 sendFarewell : completeParams . sendFarewell ,
11291209 accountId : completeParams . accountId ,
11301210 } ) ;
0 commit comments