diff --git a/package-lock.json b/package-lock.json index 76e924484..ed1ad2394 100644 --- a/package-lock.json +++ b/package-lock.json @@ -109,134 +109,6 @@ "zod": "^3.25.76 || ^4.1.8" } }, - "node_modules/@anthropic-ai/claude-agent-sdk": { - "version": "0.3.181", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.3.181.tgz", - "integrity": "sha512-xT2pXqqvCSmTwO5zctIxL4dO9vX7+rKeybvLBuEvUxub8sDyScK8s1dVstxL4ZtHQzOIDUng43qEq/kefNp+aA==", - "license": "SEE LICENSE IN README.md", - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.181", - "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.181", - "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.181", - "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.181", - "@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.181", - "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.181", - "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.181", - "@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.181" - }, - "peerDependencies": { - "@anthropic-ai/sdk": ">=0.93.0", - "@modelcontextprotocol/sdk": "^1.29.0", - "zod": "^4.0.0" - } - }, - "node_modules/@anthropic-ai/claude-agent-sdk-darwin-arm64": { - "version": "0.3.181", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.181.tgz", - "integrity": "sha512-krU5aV651tfv0IeLXo2m+YYduOdirQ+/ptv4IVXivsrjtBXHrtTTLrOaBEFuRQSLqCAMaTH3cITR+mj6ij4kMA==", - "cpu": [ - "arm64" - ], - "license": "SEE LICENSE IN LICENSE.md", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@anthropic-ai/claude-agent-sdk-darwin-x64": { - "version": "0.3.181", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.3.181.tgz", - "integrity": "sha512-kD+eMhGu2GA1/itW1LDYYELTNnQCre5C1RpekdEtQRP1iXWt6qQ+E42RJmMutBGVLGAl1cqRXIxTHXbuOWsecQ==", - "cpu": [ - "x64" - ], - "license": "SEE LICENSE IN LICENSE.md", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64": { - "version": "0.3.181", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.3.181.tgz", - "integrity": "sha512-DgrafZ4bfZN7mPOLraEzraU2A263K9Kg01i3PBSH4Mx5CfTUbJ6IVR1cYgwRCk2c4BwQmS0onnvxfgwAxP0dKg==", - "cpu": [ - "arm64" - ], - "license": "SEE LICENSE IN LICENSE.md", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64-musl": { - "version": "0.3.181", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.3.181.tgz", - "integrity": "sha512-lI39OgTCqKAqTtNF3a6+z4ToS2QKLWQw0oZGSGCMb2k6MrPB2OqyN/JSH6nvKfxLpaQmVTowwpyR4JInHMzobw==", - "cpu": [ - "arm64" - ], - "license": "SEE LICENSE IN LICENSE.md", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@anthropic-ai/claude-agent-sdk-linux-x64": { - "version": "0.3.181", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.3.181.tgz", - "integrity": "sha512-KKYcN2PKHPUOPKKWsPY1bEEkeVMiOmTRpzIleGlCPsXJLS/QnN6RgNOHIrapRmh0z9uFl24KCP5X1OUMWZqu4Q==", - "cpu": [ - "x64" - ], - "license": "SEE LICENSE IN LICENSE.md", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@anthropic-ai/claude-agent-sdk-linux-x64-musl": { - "version": "0.3.181", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.3.181.tgz", - "integrity": "sha512-FNf6QhAZ8/7q9MfFTCEfc0nA6oOSrEyKq3Xv2Jt4iSEViXYuIC25shk4T4EvU8qoLtbS51NCxCdpD7b0zCHvFQ==", - "cpu": [ - "x64" - ], - "license": "SEE LICENSE IN LICENSE.md", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@anthropic-ai/claude-agent-sdk-win32-arm64": { - "version": "0.3.181", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.3.181.tgz", - "integrity": "sha512-7plhWEB9/ExlzTMY9SzUy6zSoCrMP4KFBJwfmkt0plxwv8lFJqX/tEH/r4QDQxrMQnR+d6RcbgPH6g3hnu3NMQ==", - "cpu": [ - "arm64" - ], - "license": "SEE LICENSE IN LICENSE.md", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@anthropic-ai/claude-agent-sdk-win32-x64": { - "version": "0.3.181", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.3.181.tgz", - "integrity": "sha512-CQKaMv5tYBUxtFe1XG2qhnNJrmChXf+nhi5FhbE3GElWeNaXKMOTYqflii3zOT3yhqnrbh7fz1gzhOFwALtCeA==", - "cpu": [ - "x64" - ], - "license": "SEE LICENSE IN LICENSE.md", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@anthropic-ai/sdk": { "version": "0.104.2", "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.104.2.tgz", @@ -38147,7 +38019,7 @@ "version": "0.1.102-beta.1", "dependencies": { "@agentclientprotocol/sdk": "^0.17.1", - "@anthropic-ai/claude-agent-sdk": "^0.3.181", + "@anthropic-ai/claude-agent-sdk": "^0.3.195", "@anthropic-ai/sdk": "^0.104.2", "@getpaseo/client": "0.1.102-beta.1", "@getpaseo/highlight": "0.1.102-beta.1", @@ -38195,6 +38067,134 @@ "vitest": "^4.1.6" } }, + "packages/server/node_modules/@anthropic-ai/claude-agent-sdk": { + "version": "0.3.195", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.3.195.tgz", + "integrity": "sha512-FVmXu9pvOMbuBKWrF8YsYQdQ/upOpv5rS8lFAnFO5jbyXT/2hN7kEPd2vd2GJpaMvNcO/KptyQUK5AxjjTz3+w==", + "license": "SEE LICENSE IN README.md", + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.195", + "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.195", + "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.195", + "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.195", + "@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.195", + "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.195", + "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.195", + "@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.195" + }, + "peerDependencies": { + "@anthropic-ai/sdk": ">=0.93.0", + "@modelcontextprotocol/sdk": "^1.29.0", + "zod": "^4.0.0" + } + }, + "packages/server/node_modules/@anthropic-ai/claude-agent-sdk-darwin-arm64": { + "version": "0.3.195", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.195.tgz", + "integrity": "sha512-WIMM/8HRCLsTDHFTIwQvvE8WCA/oaMJtdQxsP7iNyfzIGwXbuOyU95V8vYIhZfaO2yaSpbBRncunq4CtR5H4ng==", + "cpu": [ + "arm64" + ], + "license": "SEE LICENSE IN LICENSE.md", + "optional": true, + "os": [ + "darwin" + ] + }, + "packages/server/node_modules/@anthropic-ai/claude-agent-sdk-darwin-x64": { + "version": "0.3.195", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.3.195.tgz", + "integrity": "sha512-RY7DB+4LXosE0MJ+XELmakfPrDN1YX4lkk9CTDm28jGCVcESRz9kAEqbyaiC48dZcmN9V1NCLutzINGdcr1TBg==", + "cpu": [ + "x64" + ], + "license": "SEE LICENSE IN LICENSE.md", + "optional": true, + "os": [ + "darwin" + ] + }, + "packages/server/node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64": { + "version": "0.3.195", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.3.195.tgz", + "integrity": "sha512-JuIq5Fnz/F1snl0aqi1gcuRZqPWoPNrL9dJ0DuievCxKkO8hnEz/Mmn5Zos7x1X8HE//ZnEvmQXoEQEZXonJew==", + "cpu": [ + "arm64" + ], + "license": "SEE LICENSE IN LICENSE.md", + "optional": true, + "os": [ + "linux" + ] + }, + "packages/server/node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64-musl": { + "version": "0.3.195", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.3.195.tgz", + "integrity": "sha512-ZmyBA/AFzhgutcxb7dbhCm6GTjJytwNYXTxJoKE2B3A409WCYccjMqeji6vCMNxyyfylglGo5D8dVMIxW9aoug==", + "cpu": [ + "arm64" + ], + "license": "SEE LICENSE IN LICENSE.md", + "optional": true, + "os": [ + "linux" + ] + }, + "packages/server/node_modules/@anthropic-ai/claude-agent-sdk-linux-x64": { + "version": "0.3.195", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.3.195.tgz", + "integrity": "sha512-s1lNi1cL93luoqsItH+fNO4KpIhdkvnVhWGGQUQ/8ftwa2gfmcIQnOg1hG8Ks+KzeD3UUQ8L9YEVHVADnFI/9A==", + "cpu": [ + "x64" + ], + "license": "SEE LICENSE IN LICENSE.md", + "optional": true, + "os": [ + "linux" + ] + }, + "packages/server/node_modules/@anthropic-ai/claude-agent-sdk-linux-x64-musl": { + "version": "0.3.195", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.3.195.tgz", + "integrity": "sha512-nf8Q/LauB+ZOC6QDjxNhbsvwUtYjKYnaWJLTYFwhkmsLujePnety1AtT/1ubaUoq5AM1j297DhMlYTasa79OUA==", + "cpu": [ + "x64" + ], + "license": "SEE LICENSE IN LICENSE.md", + "optional": true, + "os": [ + "linux" + ] + }, + "packages/server/node_modules/@anthropic-ai/claude-agent-sdk-win32-arm64": { + "version": "0.3.195", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.3.195.tgz", + "integrity": "sha512-hbkDE+xPIZzRWm+D+BKrH9uJH6USIZdDIlsyrIlGi3JFHoieYoA1vdUNyldSS9+F3ZqQtfPjr2Qy08IVB6akYA==", + "cpu": [ + "arm64" + ], + "license": "SEE LICENSE IN LICENSE.md", + "optional": true, + "os": [ + "win32" + ] + }, + "packages/server/node_modules/@anthropic-ai/claude-agent-sdk-win32-x64": { + "version": "0.3.195", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.3.195.tgz", + "integrity": "sha512-av0piEB3X1Dzhpr8A+DqHVZ9y8s1jpn8enzwX0TKKUPBn5IqLTWC7wD6v66aoUgu4f+g4ThZirmDZA6shyPEZQ==", + "cpu": [ + "x64" + ], + "license": "SEE LICENSE IN LICENSE.md", + "optional": true, + "os": [ + "win32" + ] + }, "packages/server/node_modules/@isaacs/ttlcache": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@isaacs/ttlcache/-/ttlcache-2.1.4.tgz", diff --git a/packages/server/package.json b/packages/server/package.json index 61fcb7bcc..748a94446 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -63,7 +63,7 @@ }, "dependencies": { "@agentclientprotocol/sdk": "^0.17.1", - "@anthropic-ai/claude-agent-sdk": "^0.3.181", + "@anthropic-ai/claude-agent-sdk": "^0.3.195", "@anthropic-ai/sdk": "^0.104.2", "@getpaseo/client": "0.1.102-beta.1", "@getpaseo/highlight": "0.1.102-beta.1", diff --git a/packages/server/src/server/agent/providers/claude/agent.sub-agent-sidechain.test.ts b/packages/server/src/server/agent/providers/claude/agent.sub-agent-sidechain.test.ts index ca87fee3e..a7b8e0fe7 100644 --- a/packages/server/src/server/agent/providers/claude/agent.sub-agent-sidechain.test.ts +++ b/packages/server/src/server/agent/providers/claude/agent.sub-agent-sidechain.test.ts @@ -211,6 +211,20 @@ describe("ClaudeAgentSession sub-agent sidechain updates", () => { parent_tool_use_id: "task-call-1", elapsed_time_seconds: 1, }, + { + type: "assistant", + parent_tool_use_id: "task-call-1", + message: { + id: "subagent-message-1", + role: "assistant", + content: [ + { + type: "text", + text: "Sub-agent narration belongs inside the Task row, not the parent transcript.", + }, + ], + }, + }, { type: "assistant", parent_tool_use_id: null, @@ -295,6 +309,46 @@ describe("ClaudeAgentSession sub-agent sidechain updates", () => { expect(projectedTaskCalls).toHaveLength(1); }); + test("keeps sidechain assistant text out of the parent transcript", async () => { + const session = await new ClaudeAgentClient({ + logger, + queryFactory, + resolveBinary: async () => "/test/claude/bin", + }).createSession({ + provider: "claude", + cwd: process.cwd(), + }); + + const events = await collectUntilTerminal(streamSession(session, "delegate work")); + await session.close(); + + const visibleAssistantText = events + .flatMap((event) => + event.type === "timeline" && event.item.type === "assistant_message" + ? [event.item.text] + : [], + ) + .join(""); + + expect(visibleAssistantText).not.toContain("Sub-agent narration"); + + const latestSubAgentUpdate = events + .filter( + (event): event is Extract => + event.type === "timeline" && + event.item.type === "tool_call" && + event.item.callId === "task-call-1" && + event.item.detail.type === "sub_agent", + ) + .map((event) => event.item) + .at(-1); + + expect(latestSubAgentUpdate?.detail).toMatchObject({ + type: "sub_agent", + log: expect.stringContaining("[Read] README.md"), + }); + }); + test("tails sub-agent actions instead of dropping latest entries at cap", async () => { queryFactory.mockImplementation(() => buildQueryMock(buildTailScenarioEvents(205))); diff --git a/packages/server/src/server/agent/providers/claude/agent.ts b/packages/server/src/server/agent/providers/claude/agent.ts index 7761d8ea0..62af2b0fa 100644 --- a/packages/server/src/server/agent/providers/claude/agent.ts +++ b/packages/server/src/server/agent/providers/claude/agent.ts @@ -1731,6 +1731,14 @@ function isClaudeSubagentToolName(name: string | undefined): boolean { return name === "Task" || name === "Agent"; } +function readClaudeParentToolUseId(message: SDKMessage): string | null { + if (!("parent_tool_use_id" in message)) { + return null; + } + const parentToolUseId = (message as { parent_tool_use_id?: unknown }).parent_tool_use_id; + return typeof parentToolUseId === "string" && parentToolUseId.length > 0 ? parentToolUseId : null; +} + class ClaudeContextUsageState { private contextWindowMaxTokens: number | undefined; private streamRequestInputTokens: number | undefined; @@ -3495,20 +3503,22 @@ class ClaudeAgentSession implements AgentSession { suppressAssistantText: true, suppressReasoning: true, }); - const assistantTimelineEvents = this.timelineAssembler - .consume({ - message, - runId: turnId, - messageIdHint, - }) - .map( - (item) => - ({ - type: "timeline", - item, - provider: "claude", - }) satisfies AgentStreamEvent, - ); + const assistantTimelineEvents = readClaudeParentToolUseId(message) + ? [] + : this.timelineAssembler + .consume({ + message, + runId: turnId, + messageIdHint, + }) + .map( + (item) => + ({ + type: "timeline", + item, + provider: "claude", + }) satisfies AgentStreamEvent, + ); return [...messageEvents, ...assistantTimelineEvents]; } @@ -3582,10 +3592,7 @@ class ClaudeAgentSession implements AgentSession { suppressReasoning?: boolean; }, ): AgentStreamEvent[] { - const parentToolUseId = - "parent_tool_use_id" in message - ? (message as { parent_tool_use_id: string | null }).parent_tool_use_id - : null; + const parentToolUseId = readClaudeParentToolUseId(message); if (parentToolUseId) { return this.sidechainTracker.handleMessage(message, parentToolUseId); }