authorgravatar for me@t3.ggTheo Browne <me@t3.gg> 2026-09-25 19:12:20-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-25 19:12:20-07:00
log5660ab5cb55633a1657e715b0af9bd1e449c8a63
treefc59ed15577ca907f928f3e4e9a900d1a147e48f
parentc216ba4dade683bf3a7c96bf63b33a85afc1a16c
signature Signed by PGP key B5690EEEBB952194

Remove unused items tracking from Claude adapter state (#13718)

Co-authored-by: Claude <noreply@anthropic.com>

2 files changed, 57 insertions(+), 20 deletions(-)

apps/server/src/provider/Layers/ClaudeAdapter.test.ts+50
...@@ -6691,6 +6691,56 @@ describe("ClaudeAdapterLive", () => {...@@ -6691,6 +6691,56 @@ describe("ClaudeAdapterLive", () => {
6691 );6691 );
6692 });6692 });
66936693
6694 it.effect("completed turns keep their ids but not the SDK messages", () => {
6695 const harness = makeHarness();
6696 return Effect.gen(function* () {
6697 const adapter = yield* ClaudeAdapter;
6698 const session = yield* adapter.startSession({
6699 threadId: THREAD_ID,
6700 provider: ProviderDriverKind.make("claudeAgent"),
6701 runtimeMode: "full-access",
6702 });
6703 const turn = yield* adapter.sendTurn({
6704 threadId: session.threadId,
6705 input: "hello",
6706 attachments: [],
6707 });
6708 const completedFiber = yield* Stream.filter(
6709 adapter.streamEvents,
6710 (event) => event.type === "turn.completed",
6711 ).pipe(Stream.runHead, Effect.forkChild);
6712
6713 harness.query.emit({
6714 type: "assistant",
6715 session_id: "sdk-session-1",
6716 uuid: "assistant-1",
6717 parent_tool_use_id: null,
6718 message: {
6719 id: "assistant-message-1",
6720 content: [{ type: "text", text: "Hi" }],
6721 },
6722 } as unknown as SDKMessage);
6723 harness.query.emit({
6724 type: "result",
6725 subtype: "success",
6726 is_error: false,
6727 errors: [],
6728 session_id: "sdk-session-1",
6729 uuid: "result-1",
6730 } as unknown as SDKMessage);
6731 yield* Fiber.join(completedFiber);
6732
6733 const snapshot = yield* adapter.readThread(session.threadId);
6734 assert.deepEqual(
6735 snapshot.turns.map((entry) => ({ id: String(entry.id), items: entry.items })),
6736 [{ id: String(turn.turnId), items: [] }],
6737 );
6738 }).pipe(
6739 Effect.provideService(Random.Random, makeDeterministicRandomService()),
6740 Effect.provide(harness.layer),
6741 );
6742 });
6743
6694 it.effect("rewinds Claude history when the fork omits retained system messages", () => {6744 it.effect("rewinds Claude history when the fork omits retained system messages", () => {
6695 const forkCalls: Array<Parameters<NonNullable<ClaudeAdapterLiveOptions["forkSession"]>>> = [];6745 const forkCalls: Array<Parameters<NonNullable<ClaudeAdapterLiveOptions["forkSession"]>>> = [];
6696 let firstTurnId = "";6746 let firstTurnId = "";
apps/server/src/provider/Layers/ClaudeAdapter.ts+7-20
...@@ -267,7 +267,6 @@ interface ClaudeTurnState {...@@ -267,7 +267,6 @@ interface ClaudeTurnState {
267 * steered instead (the queued message continues the same turn).267 * steered instead (the queued message continues the same turn).
268 */268 */
269 readonly synthetic?: boolean;269 readonly synthetic?: boolean;
270 readonly items: Array<unknown>;
271 readonly assistantTextBlocks: Map<number, AssistantTextBlockState>;270 readonly assistantTextBlocks: Map<number, AssistantTextBlockState>;
272 readonly assistantTextBlockOrder: Array<AssistantTextBlockState>;271 readonly assistantTextBlockOrder: Array<AssistantTextBlockState>;
273 readonly capturedProposedPlanKeys: Set<string>;272 readonly capturedProposedPlanKeys: Set<string>;
...@@ -422,10 +421,11 @@ interface ClaudeSessionContext {...@@ -422,10 +421,11 @@ interface ClaudeSessionContext {
422 resumeSessionId: string | undefined;421 resumeSessionId: string | undefined;
423 readonly pendingApprovals: Map<ApprovalRequestId, PendingApproval>;422 readonly pendingApprovals: Map<ApprovalRequestId, PendingApproval>;
424 readonly pendingUserInputs: Map<ApprovalRequestId, PendingUserInput>;423 readonly pendingUserInputs: Map<ApprovalRequestId, PendingUserInput>;
425 readonly turns: Array<{424 /** Completed turn ids, reported by readThread and trimmed on rollback.
426 id: TurnId;425 * SDK messages are not kept: rollback reads Claude's own history through
427 items: Array<unknown>;426 * turnStartMessageIds, and a long-lived session would otherwise hold every
428 }>;427 * message it ever produced. */
428 readonly turns: Array<{ readonly id: TurnId }>;
429 readonly inFlightTools: Map<number, ToolInFlight>;429 readonly inFlightTools: Map<number, ToolInFlight>;
430 readonly claudeTasks: Map<string, ClaudeTaskState>;430 readonly claudeTasks: Map<string, ClaudeTaskState>;
431 readonly taskAgents: Map<string, ClaudeTaskAgentState>;431 readonly taskAgents: Map<string, ClaudeTaskAgentState>;
...@@ -2176,10 +2176,7 @@ export const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (...@@ -2176,10 +2176,7 @@ export const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (
2176 }2176 }
2177 return {2177 return {
2178 threadId,2178 threadId,
2179 turns: context.turns.map((turn) => ({2179 turns: context.turns.map((turn) => ({ id: turn.id, items: [] })),
2180 id: turn.id,
2181 items: [...turn.items],
2182 })),
2183 };2180 };
2184 });2181 });
21852182
...@@ -2808,10 +2805,7 @@ export const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (...@@ -2808,10 +2805,7 @@ export const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (
2808 });2805 });
2809 }2806 }
28102807
2811 context.turns.push({2808 context.turns.push({ id: turnState.turnId });
2812 id: turnState.turnId,
2813 items: [...turnState.items],
2814 });
28152809
2816 yield* emitThreadTokenUsage(context, usageSnapshot, {2810 yield* emitThreadTokenUsage(context, usageSnapshot, {
2817 rawMethod: "claude/result",2811 rawMethod: "claude/result",
...@@ -3175,10 +3169,6 @@ export const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (...@@ -3175,10 +3169,6 @@ export const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (
3175 return;3169 return;
3176 }3170 }
31773171
3178 if (context.turnState) {
3179 context.turnState.items.push(message.message);
3180 }
3181
3182 for (const toolResult of toolResultBlocksFromUserMessage(message)) {3172 for (const toolResult of toolResultBlocksFromUserMessage(message)) {
3183 const toolEntry = Array.from(context.inFlightTools.entries()).find(3173 const toolEntry = Array.from(context.inFlightTools.entries()).find(
3184 ([, tool]) => tool.itemId === toolResult.toolUseId,3174 ([, tool]) => tool.itemId === toolResult.toolUseId,
...@@ -3378,7 +3368,6 @@ export const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (...@@ -3378,7 +3368,6 @@ export const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (
3378 turnId,3368 turnId,
3379 startedAt,3369 startedAt,
3380 synthetic: true,3370 synthetic: true,
3381 items: [],
3382 assistantTextBlocks: new Map(),3371 assistantTextBlocks: new Map(),
3383 assistantTextBlockOrder: [],3372 assistantTextBlockOrder: [],
3384 capturedProposedPlanKeys: new Set(),3373 capturedProposedPlanKeys: new Set(),
...@@ -3461,7 +3450,6 @@ export const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (...@@ -3461,7 +3450,6 @@ export const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (
3461 cwd: path.resolve(context.session.cwd ?? "."),3450 cwd: path.resolve(context.session.cwd ?? "."),
3462 });3451 });
3463 }3452 }
3464 context.turnState.items.push(message.message);
3465 if (3453 if (
3466 normalizeClaudeActiveTokenUsage(3454 normalizeClaudeActiveTokenUsage(
3467 message.message.usage,3455 message.message.usage,
...@@ -5205,7 +5193,6 @@ export const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (...@@ -5205,7 +5193,6 @@ export const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (
5205 const turnState: ClaudeTurnState = {5193 const turnState: ClaudeTurnState = {
5206 turnId,5194 turnId,
5207 startedAt: yield* nowIso,5195 startedAt: yield* nowIso,
5208 items: [],
5209 assistantTextBlocks: new Map(),5196 assistantTextBlocks: new Map(),
5210 assistantTextBlockOrder: [],5197 assistantTextBlockOrder: [],
5211 capturedProposedPlanKeys: new Set(),5198 capturedProposedPlanKeys: new Set(),