import { autoContinueCursorToolResponseStream } from '../dist/handler.js'; import { parseToolCalls } from '../dist/converter.js'; let passed = 0; let failed = 0; function assert(condition, message) { if (!condition) throw new Error(message || 'Assertion failed'); } function assertEqual(actual, expected, message) { const a = JSON.stringify(actual); const b = JSON.stringify(expected); if (a !== b) { throw new Error(message || `Expected ${b}, got ${a}`); } } function buildCursorReq() { return { model: 'claude-sonnet-4-5', id: 'req_test', trigger: 'user', messages: [ { id: 'msg_user', role: 'user', parts: [{ type: 'text', text: 'Write a long file.' }], }, ], }; } function createSseResponse(deltas) { const encoder = new TextEncoder(); const stream = new ReadableStream({ start(controller) { for (const delta of deltas) { controller.enqueue(encoder.encode(`data: ${JSON.stringify({ type: 'text-delta', delta })}\n\n`)); } controller.close(); }, }); return new Response(stream, { status: 200, headers: { 'Content-Type': 'text/event-stream' }, }); } async function runTest(name, fn) { try { await fn(); console.log(` OK ${name}`); passed++; } catch (error) { const message = error instanceof Error ? error.message : String(error); console.error(` FAIL ${name}`); console.error(` ${message}`); failed++; } } console.log('\nOpenAI stream truncation regression\n'); await runTest('long Write triggers continuation and restores multi-frame tool_calls', async () => { const originalFetch = global.fetch; const fetchCalls = []; try { global.fetch = async (url, init) => { fetchCalls.push({ url: String(url), body: init?.body ? JSON.parse(String(init.body)) : null }); return createSseResponse([ 'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB', 'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB', 'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB', 'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB', '"\n }\n}\n```', ]); }; const initialResponse = [ 'Preparing file write.', '', '```json action', '{', ' "tool": "Write",', ' "parameters": {', ' "file_path": "/tmp/long.txt",', ' "content": "AAAA' + 'A'.repeat(1800), ].join('\n'); const fullResponse = await autoContinueCursorToolResponseStream(buildCursorReq(), initialResponse, true); const parsed = parseToolCalls(fullResponse); assertEqual(fetchCalls.length, 1, 'long Write truncation should trigger one continuation request'); assertEqual(parsed.toolCalls.length, 1, 'continuation should restore one tool call'); assertEqual(parsed.toolCalls[0].name, 'Write'); assert(typeof fetchCalls[0].body?.messages?.at(-1)?.parts?.[0]?.text === 'string', 'continuation request should include a user guidance message'); assert(fetchCalls[0].body.messages.at(-1).parts[0].text.includes('Continue EXACTLY from where you stopped'), 'continuation prompt should be injected'); const content = String(parsed.toolCalls[0].arguments.content || ''); assert(content.startsWith('AAAA'), 'should preserve original prefix before truncation'); assert(content.includes('BBBB'), 'should append continuation content'); const argsStr = JSON.stringify(parsed.toolCalls[0].arguments); const CHUNK_SIZE = 128; const chunks = []; for (let j = 0; j < argsStr.length; j += CHUNK_SIZE) { chunks.push(argsStr.slice(j, j + CHUNK_SIZE)); } assert(chunks.length > 1, 'long Write arguments should split into multiple tool_call frames in OpenAI stream mode'); assertEqual(chunks.join(''), argsStr, 'rejoined chunks should equal original arguments'); } finally { global.fetch = originalFetch; } }); await runTest('short Read does not trigger continuation in OpenAI stream mode', async () => { const originalFetch = global.fetch; let fetchCount = 0; try { global.fetch = async () => { fetchCount++; throw new Error('short-argument tools should not trigger continuation'); }; const initialResponse = [ '```json action', '{', ' "tool": "Read",', ' "parameters": {', ' "file_path": "/tmp/config.yaml"', ' }', ].join('\n'); const fullResponse = await autoContinueCursorToolResponseStream(buildCursorReq(), initialResponse, true); const parsed = parseToolCalls(fullResponse); assertEqual(fetchCount, 0, 'short Read should not enter continuation'); assertEqual(parsed.toolCalls.length, 1, 'short-argument tools should still be recovered directly'); assertEqual(parsed.toolCalls[0].name, 'Read'); } finally { global.fetch = originalFetch; } }); console.log(`\nresult: ${passed} passed / ${failed} failed / ${passed + failed} total\n`); if (failed > 0) process.exit(1);