@@ -5,12 +5,29 @@ import type {
55 AgentOutput ,
66} from '@codebuff/common/types/session-state'
77
8+ /** Messages tagged with these tags are stripped from agent output. */
9+ const EXCLUDED_OUTPUT_TAGS = [ 'TOOL_CALL_ERROR' ] as const
10+
11+ function isExcludedFromOutput ( message : Message ) : boolean {
12+ return ! ! message . tags ?. some ( ( t ) =>
13+ ( EXCLUDED_OUTPUT_TAGS as readonly string [ ] ) . includes ( t ) ,
14+ )
15+ }
16+
817/**
9- * Get the last assistant turn messages, which includes the last assistant message
10- * and any subsequent tool messages that are responses to its tool calls.
18+ * Get the last assistant turn messages, which includes the last assistant
19+ * message and any subsequent tool messages that are responses to its tool
20+ * calls.
21+ *
22+ * Turn selection walks the raw `messageHistory` so that user-role messages
23+ * (including synthesized TOOL_CALL_ERROR ones) correctly bound the turn —
24+ * otherwise a failed attempt + its retry would get conflated into a single
25+ * "turn". Exclusion filtering is applied *after* selection: TOOL_CALL_ERROR
26+ * messages are user-role so they never enter `result` anyway (the role check
27+ * below stops at user messages), but keeping the filter explicit documents
28+ * the contract that no excluded tags leak into agent output.
1129 */
1230function getLastAssistantTurnMessages ( messageHistory : Message [ ] ) : Message [ ] {
13- // Find the index of the last assistant message
1431 let lastAssistantIndex = - 1
1532 for ( let i = messageHistory . length - 1 ; i >= 0 ; i -- ) {
1633 if ( messageHistory [ i ] . role === 'assistant' ) {
@@ -29,19 +46,18 @@ function getLastAssistantTurnMessages(messageHistory: Message[]): Message[] {
2946 return [ ]
3047 }
3148
32- // Collect the assistant message and all subsequent tool messages
3349 const result : Message [ ] = [ ]
3450 for ( let i = lastAssistantIndex ; i < messageHistory . length ; i ++ ) {
3551 const message = messageHistory [ i ]
3652 if ( message . role === 'assistant' || message . role === 'tool' ) {
3753 result . push ( message )
3854 } else {
39- // Stop if we hit a user or system message
55+ // Stop if we hit a user or system message.
4056 break
4157 }
4258 }
4359
44- return result
60+ return result . filter ( ( m ) => ! isExcludedFromOutput ( m ) )
4561}
4662
4763export function getAgentOutput (
@@ -71,7 +87,9 @@ export function getAgentOutput(
7187 }
7288 if ( agentTemplate . outputMode === 'all_messages' ) {
7389 // Remove the first message, which includes the previous conversation history.
74- const agentMessages = agentState . messageHistory . slice ( 1 )
90+ const agentMessages = agentState . messageHistory
91+ . slice ( 1 )
92+ . filter ( ( m ) => ! isExcludedFromOutput ( m ) )
7593 return {
7694 type : 'allMessages' ,
7795 value : agentMessages ,
0 commit comments