@@ -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