@@ -2,7 +2,6 @@ import * as bigquery from '@codebuff/bigquery'
22import * as analytics from '@codebuff/common/analytics'
33import { TEST_USER_ID } from '@codebuff/common/old-constants'
44import { TEST_AGENT_RUNTIME_IMPL } from '@codebuff/common/testing/impl/agent-runtime'
5- import { getToolCallString } from '@codebuff/common/tools/utils'
65import { getInitialSessionState } from '@codebuff/common/types/session-state'
76import * as stringUtils from '@codebuff/common/util/string'
87import {
@@ -15,9 +14,11 @@ import {
1514 test ,
1615} from 'bun:test'
1716
18- import { mockFileContext } from './test-utils'
17+ import { createToolCallChunk , mockFileContext } from './test-utils'
1918import { processStream } from '../tools/stream-parser'
2019
20+ import type { StreamChunk } from '@codebuff/common/types/contracts/llm'
21+
2122import type { AgentTemplate } from '../templates/types'
2223import type {
2324 AgentRuntimeDeps ,
@@ -119,22 +120,26 @@ describe('malformed tool call error handling', () => {
119120 agentRuntimeImpl = { ...TEST_AGENT_RUNTIME_IMPL }
120121 } )
121122
122- function createMockStream ( chunks : string [ ] ) {
123+ function createMockStream ( chunks : StreamChunk [ ] ) {
123124 async function * generator ( ) {
124125 for ( const chunk of chunks ) {
125- yield { type : 'text' as const , text : chunk }
126+ yield chunk
126127 }
127128 return 'mock-message-id'
128129 }
129130 return generator ( )
130131 }
131132
133+ function textChunk ( text : string ) : StreamChunk {
134+ return { type : 'text' as const , text }
135+ }
136+
132137 test ( 'should add tool result errors to message history after stream completes' , async ( ) => {
133- const chunks = [
134- // Malformed JSON tool call
135- '<codebuff_tool_call>\n{\n "cb_tool_name": "read_files",\n "paths": ["test.ts"\n}\n</codebuff_tool_call>' ,
136- // Valid end turn
137- getToolCallString ( 'end_turn' , { } ) ,
138+ // With native tools, malformed tool calls are handled at the API level.
139+ // This test now verifies that an unknown tool is properly handled.
140+ const chunks : StreamChunk [ ] = [
141+ createToolCallChunk ( 'unknown_tool_xyz' , { paths : [ 'test.ts' ] } ) ,
142+ createToolCallChunk ( 'end_turn' , { } ) ,
138143 ]
139144
140145 const stream = createMockStream ( chunks )
@@ -152,7 +157,7 @@ describe('malformed tool call error handling', () => {
152157
153158 expect ( toolMessages . length ) . toBeGreaterThan ( 0 )
154159
155- // Find the error tool result
160+ // Find the error tool result for the unknown tool
156161 const errorToolResult = toolMessages . find (
157162 ( m ) =>
158163 m . content ?. [ 0 ] ?. type === 'json' &&
@@ -162,17 +167,15 @@ describe('malformed tool call error handling', () => {
162167 expect ( errorToolResult ) . toBeDefined ( )
163168 expect (
164169 ( errorToolResult ?. content ?. [ 0 ] as any ) ?. value ?. errorMessage ,
165- ) . toContain ( 'Invalid JSON ' )
170+ ) . toContain ( 'not found ' )
166171 } )
167172
168- test ( 'should handle multiple malformed tool calls' , async ( ) => {
169- const chunks = [
170- // First malformed call
171- '<codebuff_tool_call>\n{\n "cb_tool_name": "read_files",\n invalid\n}\n</codebuff_tool_call>' ,
172- 'Some text between calls' ,
173- // Second malformed call
174- '<codebuff_tool_call>\n{\n missing_quotes: value\n}\n</codebuff_tool_call>' ,
175- getToolCallString ( 'end_turn' , { } ) ,
173+ test ( 'should handle multiple unknown tool calls' , async ( ) => {
174+ const chunks : StreamChunk [ ] = [
175+ createToolCallChunk ( 'unknown_tool_1' , { param : 'value1' } ) ,
176+ textChunk ( 'Some text between calls' ) ,
177+ createToolCallChunk ( 'unknown_tool_2' , { param : 'value2' } ) ,
178+ createToolCallChunk ( 'end_turn' , { } ) ,
176179 ]
177180
178181 const stream = createMockStream ( chunks )
@@ -197,9 +200,9 @@ describe('malformed tool call error handling', () => {
197200 } )
198201
199202 test ( 'should preserve original toolResults array alongside message history' , async ( ) => {
200- const chunks = [
201- '<codebuff_tool_call>\n{\n "cb_tool_name": "read_files",\n malformed\n}\n</codebuff_tool_call>' ,
202- getToolCallString ( 'end_turn' , { } ) ,
203+ const chunks : StreamChunk [ ] = [
204+ createToolCallChunk ( 'unknown_tool_xyz' , { param : 'value' } ) ,
205+ createToolCallChunk ( 'end_turn' , { } ) ,
203206 ]
204207
205208 const stream = createMockStream ( chunks )
@@ -228,9 +231,9 @@ describe('malformed tool call error handling', () => {
228231 } )
229232
230233 test ( 'should handle unknown tool names and add error to message history' , async ( ) => {
231- const chunks = [
232- '<codebuff_tool_call>\n{\n "cb_tool_name": " unknown_tool",\n " param": " value"\n}\n</codebuff_tool_call>' ,
233- getToolCallString ( 'end_turn' , { } ) ,
234+ const chunks : StreamChunk [ ] = [
235+ createToolCallChunk ( ' unknown_tool' , { param : ' value' } ) ,
236+ createToolCallChunk ( 'end_turn' , { } ) ,
234237 ]
235238
236239 const stream = createMockStream ( chunks )
@@ -258,12 +261,12 @@ describe('malformed tool call error handling', () => {
258261 } )
259262
260263 test ( 'should not affect valid tool calls in message history' , async ( ) => {
261- const chunks = [
264+ const chunks : StreamChunk [ ] = [
262265 // Valid tool call
263- getToolCallString ( 'read_files' , { paths : [ 'test.ts' ] } ) ,
264- // Malformed tool call
265- '<codebuff_tool_call>\n{\n "cb_tool_name": "read_files",\n invalid\n}\n</codebuff_tool_call>' ,
266- getToolCallString ( 'end_turn' , { } ) ,
266+ createToolCallChunk ( 'read_files' , { paths : [ 'test.ts' ] } ) ,
267+ // Unknown tool call
268+ createToolCallChunk ( 'unknown_tool_xyz' , { param : 'value' } ) ,
269+ createToolCallChunk ( 'end_turn' , { } ) ,
267270 ]
268271
269272 const stream = createMockStream ( chunks )
@@ -299,10 +302,10 @@ describe('malformed tool call error handling', () => {
299302 expect ( errorResults . length ) . toBeGreaterThan ( 0 )
300303 } )
301304
302- test ( 'should handle stream with only malformed calls' , async ( ) => {
303- const chunks = [
304- '<codebuff_tool_call>\n{\n invalid1\n}\n</codebuff_tool_call>' ,
305- '<codebuff_tool_call>\n{\n invalid2\n}\n</codebuff_tool_call>' ,
305+ test ( 'should handle stream with only unknown tool calls' , async ( ) => {
306+ const chunks : StreamChunk [ ] = [
307+ createToolCallChunk ( 'unknown_tool_1' , { param : 'value1' } ) ,
308+ createToolCallChunk ( 'unknown_tool_2' , { param : 'value2' } ) ,
306309 ]
307310
308311 const stream = createMockStream ( chunks )
@@ -320,7 +323,7 @@ describe('malformed tool call error handling', () => {
320323 toolMessages . forEach ( ( msg ) => {
321324 expect ( msg . content ?. [ 0 ] ?. type ) . toBe ( 'json' )
322325 expect ( ( msg . content ?. [ 0 ] as any ) ?. value ?. errorMessage ) . toContain (
323- 'Invalid JSON ' ,
326+ 'not found ' ,
324327 )
325328 } )
326329 } )
0 commit comments