@@ -428,25 +428,59 @@ function* handleStepsMax({
428428}
429429
430430function * handleStepsOpus ( {
431+ agentState,
431432 params,
432433 logger,
433434} : AgentStepContext ) : ReturnType <
434435 NonNullable < SecretAgentDefinition [ 'handleSteps' ] >
435436> {
436- const DEFAULT_N = 3
437+ const MAX_N = 3
437438 const selectorAgent = 'best-of-n-selector-opus'
438439 const n = Math . min (
439440 10 ,
440- Math . max ( 1 , ( params ?. n as number | undefined ) ?? DEFAULT_N ) ,
441+ Math . max ( 1 , ( params ?. n as number | undefined ) ?? MAX_N ) ,
441442 )
442443
443- // Spawn implementor agents
444- const implementorAgents = [ ]
445- for ( let i = 0 ; i < n ; i ++ ) {
446- implementorAgents . push ( {
447- agent_type : 'editor-implementor-opus' ,
448- } )
444+ // Model selection pattern for max mode, using opus and gpt-5
445+ const MAX_MODEL_PATTERN = [
446+ 'editor-implementor-opus' ,
447+ 'editor-implementor-opus' ,
448+ 'editor-implementor-opus' ,
449+ 'editor-implementor-opus' ,
450+ 'editor-implementor-opus' ,
451+ 'editor-implementor-opus' ,
452+ 'editor-implementor-opus' ,
453+ 'editor-implementor-opus' ,
454+ 'editor-implementor-opus' ,
455+ 'editor-implementor-opus' ,
456+ ] as const
457+
458+ // Only keep messages up to just before the last user role message (skips input prompt, instrucitons prompt).
459+ const { messageHistory : initialMessageHistory } = agentState
460+ let userMessageIndex = initialMessageHistory . length
461+
462+ while ( userMessageIndex > 0 ) {
463+ const message = initialMessageHistory [ userMessageIndex - 1 ]
464+ if ( message . role === 'user' ) {
465+ userMessageIndex --
466+ } else {
467+ break
468+ }
449469 }
470+ const updatedMessageHistory = initialMessageHistory . slice ( 0 , userMessageIndex )
471+ yield {
472+ toolName : 'set_messages' ,
473+ input : {
474+ messages : updatedMessageHistory ,
475+ } ,
476+ includeToolCall : false ,
477+ } satisfies ToolCall < 'set_messages' >
478+
479+ // Spawn implementor agents using the model pattern
480+ const implementorAgents = MAX_MODEL_PATTERN . slice ( 0 , n ) . map ( ( agent_type ) => ( {
481+ agent_type,
482+ } ) )
483+
450484 // Spawn all implementor agents
451485 const { toolResult : implementorResults } = yield {
452486 toolName : 'spawn_agents' ,
@@ -457,9 +491,14 @@ function* handleStepsOpus({
457491 } satisfies ToolCall < 'spawn_agents' >
458492
459493 // Extract spawn results
460- const spawnedImplementations =
461- extractSpawnResults < { text : string } [ ] > ( implementorResults )
494+ const spawnedImplementations = extractSpawnResults (
495+ implementorResults ,
496+ ) as any [ ]
462497
498+ logger . info (
499+ { implementorResults, spawnedImplementations } ,
500+ 'spawnedImplementations' ,
501+ )
463502 // Extract all the plans from the structured outputs
464503 const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
465504 // Parse implementations from spawn results
@@ -468,11 +507,11 @@ function* handleStepsOpus({
468507 content :
469508 'errorMessage' in result
470509 ? `Error: ${ result . errorMessage } `
471- : result [ 0 ] . text ,
510+ : extractLastMessageText ( result ) ?? '' ,
472511 } ) )
473512
474513 // Spawn selector with implementations as params
475- const { toolResult : selectorResult } = yield {
514+ const { toolResult : selectorResult , agentState : selectorAgentState } = yield {
476515 toolName : 'spawn_agents' ,
477516 input : {
478517 agents : [
@@ -486,8 +525,10 @@ function* handleStepsOpus({
486525 } satisfies ToolCall < 'spawn_agents' >
487526
488527 const selectorOutput = extractSpawnResults < {
489- implementationId : string
490- reasoning : string
528+ value : {
529+ implementationId : string
530+ reasoning : string
531+ }
491532 } > ( selectorResult ) [ 0 ]
492533
493534 if ( 'errorMessage' in selectorOutput ) {
@@ -497,7 +538,7 @@ function* handleStepsOpus({
497538 } satisfies ToolCall < 'set_output' >
498539 return
499540 }
500- const { implementationId } = selectorOutput
541+ const { implementationId } = selectorOutput . value
501542 const chosenImplementation = implementations . find (
502543 ( implementation ) => implementation . id === implementationId ,
503544 )
@@ -509,68 +550,77 @@ function* handleStepsOpus({
509550 return
510551 }
511552
512- // Apply the chosen implementation using STEP_TEXT (only tool calls, no commentary)
513- const toolCallsOnly = extractToolCallsOnly (
514- typeof chosenImplementation . content === 'string'
515- ? chosenImplementation . content
516- : '' ,
517- )
553+ const numMessagesBeforeStepText = selectorAgentState . messageHistory . length
554+
518555 const { agentState : postEditsAgentState } = yield {
519556 type : 'STEP_TEXT' ,
520- text : toolCallsOnly ,
557+ text : chosenImplementation . content ,
521558 } as StepText
522559 const { messageHistory } = postEditsAgentState
523- const lastAssistantMessageIndex = messageHistory . findLastIndex (
524- ( message ) => message . role === 'assistant' ,
525- )
526- const editToolResults = messageHistory
527- . slice ( lastAssistantMessageIndex )
528- . filter ( ( message ) => message . role === 'tool' )
529- . flatMap ( ( message ) => message . content )
530- . filter ( ( output ) => output . type === 'json' )
531- . map ( ( output ) => output . value )
532560
533- // Set output with the chosen implementation and reasoning
561+ // Set output with the messages from running the step text of the chosen implementation
534562 yield {
535563 toolName : 'set_output' ,
536564 input : {
537- response : chosenImplementation . content ,
538- toolResults : editToolResults ,
565+ messages : messageHistory . slice ( numMessagesBeforeStepText ) ,
539566 } ,
540567 includeToolCall : false ,
541568 } satisfies ToolCall < 'set_output' >
542569
543- function extractSpawnResults < T > (
544- results : any [ ] | undefined ,
545- ) : ( T | { errorMessage : string } ) [ ] {
546- if ( ! results ) return [ ]
547- const spawnedResults = results
548- . filter ( ( result ) => result . type === 'json' )
549- . map ( ( result ) => result . value )
550- . flat ( ) as {
551- agentType : string
552- value : { value ?: T ; errorMessage ?: string }
553- } [ ]
554- return spawnedResults . map (
555- ( result ) =>
556- result . value . value ?? {
557- errorMessage :
558- result . value . errorMessage ?? 'Error extracting spawn results' ,
559- } ,
560- )
570+ /**
571+ * Extracts the array of subagent results from spawn_agents tool output.
572+ *
573+ * The spawn_agents tool result structure is:
574+ * [{ type: 'json', value: [{ agentName, agentType, value: AgentOutput }] }]
575+ *
576+ * Returns an array of agent outputs, one per spawned agent.
577+ */
578+ function extractSpawnResults < T > ( results : any [ ] | undefined ) : T [ ] {
579+ if ( ! results || results . length === 0 ) return [ ]
580+
581+ // Find the json result containing spawn results
582+ const jsonResult = results . find ( ( r ) => r . type === 'json' )
583+ if ( ! jsonResult ?. value ) return [ ]
584+
585+ // Get the spawned agent results array
586+ const spawnedResults = Array . isArray ( jsonResult . value )
587+ ? jsonResult . value
588+ : [ jsonResult . value ]
589+
590+ // Extract the value (AgentOutput) from each result
591+ return spawnedResults . map ( ( result : any ) => result ?. value ) . filter ( Boolean )
561592 }
562593
563- // Extract only tool calls from text, removing any commentary
564- function extractToolCallsOnly ( text : string ) : string {
565- const toolExtractionPattern =
566- / < c o d e b u f f _ t o o l _ c a l l > \n ( .* ?) \n < \/ c o d e b u f f _ t o o l _ c a l l > / gs
567- const matches : string [ ] = [ ]
594+ /**
595+ * Extracts the text content from a 'lastMessage' AgentOutput.
596+ *
597+ * For agents with outputMode: 'last_message', the output structure is:
598+ * { type: 'lastMessage', value: [{ role: 'assistant', content: [{ type: 'text', text: '...' }] }] }
599+ *
600+ * Returns the text from the last assistant message, or null if not found.
601+ */
602+ function extractLastMessageText ( agentOutput : any ) : string | null {
603+ if ( ! agentOutput ) return null
568604
569- for ( const match of text . matchAll ( toolExtractionPattern ) ) {
570- matches . push ( match [ 0 ] ) // Include the full tool call with tags
605+ // Handle 'lastMessage' output mode - the value contains an array of messages
606+ if (
607+ agentOutput . type === 'lastMessage' &&
608+ Array . isArray ( agentOutput . value )
609+ ) {
610+ // Find the last assistant message with text content
611+ for ( let i = agentOutput . value . length - 1 ; i >= 0 ; i -- ) {
612+ const message = agentOutput . value [ i ]
613+ if ( message . role === 'assistant' && Array . isArray ( message . content ) ) {
614+ // Find text content in the message
615+ for ( const part of message . content ) {
616+ if ( part . type === 'text' && typeof part . text === 'string' ) {
617+ return part . text
618+ }
619+ }
620+ }
621+ }
571622 }
572-
573- return matches . join ( '\n' )
623+ return null
574624 }
575625}
576626
0 commit comments