@@ -58,6 +58,7 @@ import { getModelConfigAlias } from './registry.js';
5858import { getVersion } from '../utils/version.js' ;
5959import { getToolCallContext } from '../utils/toolCallContext.js' ;
6060import { scheduleAgentTools } from './agent-scheduler.js' ;
61+ import { DeadlineTimer } from '../utils/deadlineTimer.js' ;
6162
6263/** A callback function to report on agent activity. */
6364export type ActivityCallback = ( activity : SubagentActivityEvent ) => void ;
@@ -231,6 +232,7 @@ export class LocalAgentExecutor<TOutput extends z.ZodTypeAny> {
231232 turnCounter : number ,
232233 combinedSignal : AbortSignal ,
233234 timeoutSignal : AbortSignal , // Pass the timeout controller's signal
235+ onWaitingForConfirmation ?: ( waiting : boolean ) => void ,
234236 ) : Promise < AgentTurnResult > {
235237 const promptId = `${ this . agentId } #${ turnCounter } ` ;
236238
@@ -265,7 +267,12 @@ export class LocalAgentExecutor<TOutput extends z.ZodTypeAny> {
265267 }
266268
267269 const { nextMessage, submittedOutput, taskCompleted } =
268- await this . processFunctionCalls ( functionCalls , combinedSignal , promptId ) ;
270+ await this . processFunctionCalls (
271+ functionCalls ,
272+ combinedSignal ,
273+ promptId ,
274+ onWaitingForConfirmation ,
275+ ) ;
269276 if ( taskCompleted ) {
270277 const finalResult = submittedOutput ?? 'Task completed successfully.' ;
271278 return {
@@ -322,6 +329,7 @@ export class LocalAgentExecutor<TOutput extends z.ZodTypeAny> {
322329 | AgentTerminateMode . MAX_TURNS
323330 | AgentTerminateMode . ERROR_NO_COMPLETE_TASK_CALL ,
324331 externalSignal : AbortSignal , // The original signal passed to run()
332+ onWaitingForConfirmation ?: ( waiting : boolean ) => void ,
325333 ) : Promise < string | null > {
326334 this . emitActivity ( 'THOUGHT_CHUNK' , {
327335 text : `Execution limit reached (${ reason } ). Attempting one final recovery turn with a grace period.` ,
@@ -355,6 +363,7 @@ export class LocalAgentExecutor<TOutput extends z.ZodTypeAny> {
355363 turnCounter , // This will be the "last" turn number
356364 combinedSignal ,
357365 graceTimeoutController . signal , // Pass grace signal to identify a *grace* timeout
366+ onWaitingForConfirmation ,
358367 ) ;
359368
360369 if (
@@ -415,14 +424,22 @@ export class LocalAgentExecutor<TOutput extends z.ZodTypeAny> {
415424 this . definition . runConfig . maxTimeMinutes ?? DEFAULT_MAX_TIME_MINUTES ;
416425 const maxTurns = this . definition . runConfig . maxTurns ?? DEFAULT_MAX_TURNS ;
417426
418- const timeoutController = new AbortController ( ) ;
419- const timeoutId = setTimeout (
420- ( ) => timeoutController . abort ( new Error ( 'Agent timed out.' ) ) ,
427+ const deadlineTimer = new DeadlineTimer (
421428 maxTimeMinutes * 60 * 1000 ,
429+ 'Agent timed out.' ,
422430 ) ;
423431
432+ // Track time spent waiting for user confirmation to credit it back to the agent.
433+ const onWaitingForConfirmation = ( waiting : boolean ) => {
434+ if ( waiting ) {
435+ deadlineTimer . pause ( ) ;
436+ } else {
437+ deadlineTimer . resume ( ) ;
438+ }
439+ } ;
440+
424441 // Combine the external signal with the internal timeout signal.
425- const combinedSignal = AbortSignal . any ( [ signal , timeoutController . signal ] ) ;
442+ const combinedSignal = AbortSignal . any ( [ signal , deadlineTimer . signal ] ) ;
426443
427444 logAgentStart (
428445 this . runtimeContext ,
@@ -458,7 +475,7 @@ export class LocalAgentExecutor<TOutput extends z.ZodTypeAny> {
458475 // Check for timeout or external abort.
459476 if ( combinedSignal . aborted ) {
460477 // Determine which signal caused the abort.
461- terminateReason = timeoutController . signal . aborted
478+ terminateReason = deadlineTimer . signal . aborted
462479 ? AgentTerminateMode . TIMEOUT
463480 : AgentTerminateMode . ABORTED ;
464481 break ;
@@ -469,7 +486,8 @@ export class LocalAgentExecutor<TOutput extends z.ZodTypeAny> {
469486 currentMessage ,
470487 turnCounter ++ ,
471488 combinedSignal ,
472- timeoutController . signal ,
489+ deadlineTimer . signal ,
490+ onWaitingForConfirmation ,
473491 ) ;
474492
475493 if ( turnResult . status === 'stop' ) {
@@ -498,6 +516,7 @@ export class LocalAgentExecutor<TOutput extends z.ZodTypeAny> {
498516 turnCounter , // Use current turnCounter for the recovery attempt
499517 terminateReason ,
500518 signal , // Pass the external signal
519+ onWaitingForConfirmation ,
501520 ) ;
502521
503522 if ( recoveryResult !== null ) {
@@ -551,7 +570,7 @@ export class LocalAgentExecutor<TOutput extends z.ZodTypeAny> {
551570 if (
552571 error instanceof Error &&
553572 error . name === 'AbortError' &&
554- timeoutController . signal . aborted &&
573+ deadlineTimer . signal . aborted &&
555574 ! signal . aborted // Ensure the external signal was not the cause
556575 ) {
557576 terminateReason = AgentTerminateMode . TIMEOUT ;
@@ -563,6 +582,7 @@ export class LocalAgentExecutor<TOutput extends z.ZodTypeAny> {
563582 turnCounter , // Use current turnCounter
564583 AgentTerminateMode . TIMEOUT ,
565584 signal ,
585+ onWaitingForConfirmation ,
566586 ) ;
567587
568588 if ( recoveryResult !== null ) {
@@ -591,7 +611,7 @@ export class LocalAgentExecutor<TOutput extends z.ZodTypeAny> {
591611 this . emitActivity ( 'ERROR' , { error : String ( error ) } ) ;
592612 throw error ; // Re-throw other errors or external aborts.
593613 } finally {
594- clearTimeout ( timeoutId ) ;
614+ deadlineTimer . abort ( ) ;
595615 logAgentFinish (
596616 this . runtimeContext ,
597617 new AgentFinishEvent (
@@ -779,6 +799,7 @@ export class LocalAgentExecutor<TOutput extends z.ZodTypeAny> {
779799 functionCalls : FunctionCall [ ] ,
780800 signal : AbortSignal ,
781801 promptId : string ,
802+ onWaitingForConfirmation ?: ( waiting : boolean ) => void ,
782803 ) : Promise < {
783804 nextMessage : Content ;
784805 submittedOutput : string | null ;
@@ -979,6 +1000,7 @@ export class LocalAgentExecutor<TOutput extends z.ZodTypeAny> {
9791000 parentCallId : this . parentCallId ,
9801001 toolRegistry : this . toolRegistry ,
9811002 signal,
1003+ onWaitingForConfirmation,
9821004 } ,
9831005 ) ;
9841006
0 commit comments