@@ -156,7 +156,9 @@ export function summarizeAgentRunControllerSnapshot(snapshot: AgentRunController
156156 } ;
157157}
158158
159- function resolveControllerContinuation ( input : AgentRunControllerProviderStepInput ) : AgentRunControllerContinuation | undefined {
159+ function resolveControllerContinuation (
160+ input : AgentRunControllerProviderStepInput
161+ ) : AgentRunControllerContinuation | undefined {
160162 if ( input . forceContinuation ) {
161163 return input . forceContinuation ;
162164 }
@@ -171,12 +173,10 @@ function resolveControllerContinuation(input: AgentRunControllerProviderStepInpu
171173 const incompleteCount = continuation ?. incompleteTodo ?. incompleteCount ?? 0 ;
172174 if (
173175 incompleteCount > 0 &&
174- (
175- ! assistantMessage . trim ( ) ||
176+ ( ! assistantMessage . trim ( ) ||
176177 Boolean ( continuation ?. incompleteTodo ?. hasInProgress ) ||
177178 looksLikeUnfinishedAgentWriteReply ( assistantMessage ) ||
178- looksLikeAgentTodoContinuationReply ( assistantMessage )
179- )
179+ looksLikeAgentTodoContinuationReply ( assistantMessage ) )
180180 ) {
181181 return {
182182 reason : 'incomplete_todo' ,
@@ -236,6 +236,31 @@ function createPartBase(options: {
236236 } ;
237237}
238238
239+ function assistantTextPrefixCandidates ( parts : AgentCoreMessagePart [ ] ) : string [ ] {
240+ const texts = parts
241+ . filter ( ( part ) : part is Extract < AgentCoreMessagePart , { kind : 'assistant_text' } > => part . kind === 'assistant_text' )
242+ . map ( ( part ) => part . text )
243+ . filter ( ( text ) => text . trim ( ) . length > 0 ) ;
244+ if ( texts . length === 0 ) {
245+ return [ ] ;
246+ }
247+ return [ ...new Set ( [ texts . join ( '' ) , texts . join ( '\n\n' ) ] ) ] . sort ( ( left , right ) => right . length - left . length ) ;
248+ }
249+
250+ function stripPreviouslyRecordedAssistantTextSnapshot ( text : string , parts : AgentCoreMessagePart [ ] ) : string | undefined {
251+ const trimmed = text . trim ( ) ;
252+ if ( ! trimmed ) {
253+ return undefined ;
254+ }
255+ for ( const prefix of assistantTextPrefixCandidates ( parts ) ) {
256+ if ( prefix && trimmed . startsWith ( prefix ) ) {
257+ const suffix = trimmed . slice ( prefix . length ) . trimStart ( ) ;
258+ return suffix . trim ( ) ? suffix : undefined ;
259+ }
260+ }
261+ return text ;
262+ }
263+
239264export function createAgentRunController ( options : AgentRunControllerOptions = { } ) {
240265 const createdAt = options . createdAt ?? ( ( ) => new Date ( ) . toISOString ( ) ) ;
241266 const machine = createAgentCoreStateMachine ( options . initialState ) ;
@@ -257,7 +282,10 @@ export function createAgentRunController(options: AgentRunControllerOptions = {}
257282 nextSequence = Math . max ( nextSequence , part . sequence + 1 ) ;
258283 }
259284
260- function markToolCallStatus ( toolUseId : string , status : Extract < AgentCoreMessagePart , { kind : 'tool_call' } > [ 'status' ] ) : void {
285+ function markToolCallStatus (
286+ toolUseId : string ,
287+ status : Extract < AgentCoreMessagePart , { kind : 'tool_call' } > [ 'status' ]
288+ ) : void {
261289 const index = parts . findIndex ( ( part ) => part . kind === 'tool_call' && part . toolUseId === toolUseId ) ;
262290 if ( index >= 0 ) {
263291 const part = parts [ index ] ;
@@ -355,7 +383,10 @@ export function createAgentRunController(options: AgentRunControllerOptions = {}
355383 usage : input . providerStep . usage
356384 } ) ;
357385 }
358- if ( input . providerStep . text ) {
386+ const providerStepText = input . providerStep . text
387+ ? stripPreviouslyRecordedAssistantTextSnapshot ( input . providerStep . text , parts )
388+ : undefined ;
389+ if ( providerStepText ) {
359390 pushPart ( {
360391 ...createPartBase ( {
361392 id : `provider_step:${ providerStepCount } :text` ,
@@ -366,7 +397,7 @@ export function createAgentRunController(options: AgentRunControllerOptions = {}
366397 turnId : options . turnId
367398 } ) ,
368399 kind : 'assistant_text' ,
369- text : input . providerStep . text ,
400+ text : providerStepText ,
370401 final : ! continuation && input . providerStep . toolCalls . length === 0 && input . providerStep . finishReason === 'stop'
371402 } ) ;
372403 }
@@ -552,7 +583,11 @@ export function createAgentRunController(options: AgentRunControllerOptions = {}
552583
553584 function recordPermissionDenied ( input : AgentRunControllerPermissionDeniedInput ) : AgentRunControllerSnapshot {
554585 if ( machine . getSnapshot ( ) . state === 'awaiting_permission' ) {
555- machine . transition ( 'executing_tools' , 'Permission denied; record a structured tool error for provider replay.' , now ( ) ) ;
586+ machine . transition (
587+ 'executing_tools' ,
588+ 'Permission denied; record a structured tool error for provider replay.' ,
589+ now ( )
590+ ) ;
556591 }
557592 return recordToolResult ( {
558593 toolUseId : input . toolUseId ,
@@ -567,7 +602,11 @@ export function createAgentRunController(options: AgentRunControllerOptions = {}
567602
568603 function recordPermissionApproved ( input : AgentRunControllerPermissionApprovedInput ) : AgentRunControllerSnapshot {
569604 if ( machine . getSnapshot ( ) . state === 'awaiting_permission' ) {
570- machine . transition ( 'executing_tools' , 'Permission approved; record a structured permission result for provider replay.' , now ( ) ) ;
605+ machine . transition (
606+ 'executing_tools' ,
607+ 'Permission approved; record a structured permission result for provider replay.' ,
608+ now ( )
609+ ) ;
571610 }
572611 return recordToolResult ( {
573612 toolUseId : input . toolUseId ,
@@ -578,9 +617,7 @@ export function createAgentRunController(options: AgentRunControllerOptions = {}
578617 }
579618
580619 function findPendingToolName ( toolUseId : string ) : string | undefined {
581- const toolPart = [ ...parts ]
582- . reverse ( )
583- . find ( ( part ) => part . kind === 'tool_call' && part . toolUseId === toolUseId ) ;
620+ const toolPart = [ ...parts ] . reverse ( ) . find ( ( part ) => part . kind === 'tool_call' && part . toolUseId === toolUseId ) ;
584621 return toolPart ?. kind === 'tool_call' ? toolPart . name : undefined ;
585622 }
586623
@@ -622,7 +659,9 @@ export function createAgentRunController(options: AgentRunControllerOptions = {}
622659 return getSnapshot ( ) ;
623660 }
624661
625- function requestContextCompression ( reason = 'Context budget requires compression before the next provider input.' ) : AgentRunControllerSnapshot {
662+ function requestContextCompression (
663+ reason = 'Context budget requires compression before the next provider input.'
664+ ) : AgentRunControllerSnapshot {
626665 const currentState = machine . getSnapshot ( ) . state ;
627666 if ( canTransitionAgentCoreState ( currentState , 'compacting_context' ) ) {
628667 machine . transition ( 'compacting_context' , reason , now ( ) ) ;
0 commit comments