From 020067a670242f9844e677974d1845270ce0731f Mon Sep 17 00:00:00 2001 From: Gustavo Neiva Date: Wed, 23 Sep 2026 12:55:56 -0300 Subject: [PATCH] fix(pi): restore Pi tool names on Claude responses --- packages/pi/src/stream.ts | 8 ++++- packages/pi/src/tests/stream.test.ts | 49 ++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/packages/pi/src/stream.ts b/packages/pi/src/stream.ts index fa4cd30e..1118a805 100644 --- a/packages/pi/src/stream.ts +++ b/packages/pi/src/stream.ts @@ -74,8 +74,11 @@ import { type AssistantMessageEventStream, type Context, calculateCost, + collapseSystemMessages, createAssistantMessageEventStream, + getCurrentTools, type Model, + normalizeContext, type SimpleStreamOptions, type StopReason, type TextContent, @@ -1520,6 +1523,9 @@ export function streamCortexKitAnthropic( getClaustrumMode(await loadAccounts(storagePath)) !== 'claustrum' ) throw new Error('Missing Anthropic OAuth access token') + const tools = getCurrentTools( + collapseSystemMessages(normalizeContext(context)).messages, + ) const response = await executeWithFallback({ model, context, @@ -1581,7 +1587,7 @@ export function streamCortexKitAnthropic( output.content.push({ type: 'toolCall', id: String(block.id), - name: fromClaudeCodeToolName(String(block.name), context.tools), + name: fromClaudeCodeToolName(String(block.name), tools), arguments: {}, partialJson: '', index: event.index, diff --git a/packages/pi/src/tests/stream.test.ts b/packages/pi/src/tests/stream.test.ts index a718c074..c479b2d2 100644 --- a/packages/pi/src/tests/stream.test.ts +++ b/packages/pi/src/tests/stream.test.ts @@ -928,6 +928,55 @@ describe('Pi API fallback routing helpers', () => { }) describe('Pi Anthropic stream content blocks', () => { + test('maps Claude Code tool names back to Pi transcript tool names', async () => { + tempDir = await mkdtemp(join(tmpdir(), 'pi-transcript-tools-')) + process.env.PI_ANTHROPIC_AUTH_FILE = join(tempDir, 'anthropic-auth.json') + + let sentTools: Array<{ name: string }> = [] + globalThis.fetch = mock( + async (input: string | URL | Request, init?: RequestInit) => { + if (!input.toString().includes('/v1/messages')) { + return new Response('{}', { status: 200 }) + } + sentTools = JSON.parse(String(init?.body)).tools + return new Response( + [ + 'data: {"type":"message_start","message":{"usage":{"input_tokens":1,"output_tokens":0}}}\n\n', + 'data: {"type":"content_block_start","index":0,"content_block":{"type":"tool_use","id":"tool_1","name":"Bash","input":{}}}\n\n', + 'data: {"type":"content_block_stop","index":0}\n\n', + 'data: {"type":"message_delta","delta":{"stop_reason":"tool_use"},"usage":{"output_tokens":1}}\n\n', + ].join(''), + { status: 200 }, + ) + }, + ) as unknown as typeof fetch + + const context = { + messages: [ + { + role: 'system', + content: 'test', + toolsAdded: [ + { + name: 'bash', + description: 'Shell', + parameters: { type: 'object', properties: {} }, + }, + ], + timestamp: 0, + }, + { role: 'user', content: 'run status', timestamp: 1 }, + ], + } as any + const result = await streamCortexKitAnthropic(anthropicModel, context, { + apiKey: 'main-access', + }).result() + + expect(sentTools.map((tool) => tool.name)).toEqual(['Bash']) + expect(result.stopReason).toBe('toolUse') + expect(result.content[0]).toMatchObject({ type: 'toolCall', name: 'bash' }) + }) + test('preserves redacted thinking for same-model replay', async () => { tempDir = await mkdtemp(join(tmpdir(), 'pi-redacted-thinking-')) process.env.PI_ANTHROPIC_AUTH_FILE = join(tempDir, 'anthropic-auth.json')