@rynfar/meridian 1.62.2 → 1.62.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1974,13 +1974,6 @@ ${history}
1974
1974
 
1975
1975
  ` + last.text;
1976
1976
  }
1977
- function framePassthroughContinuation(delta) {
1978
- if (!delta)
1979
- return delta;
1980
- return `${PASSTHROUGH_CONTINUATION_LEAD_IN}
1981
-
1982
- ${delta}`;
1983
- }
1984
1977
  function stripNonStandardStreamFields(event) {
1985
1978
  if (event && typeof event === "object") {
1986
1979
  const e = event;
@@ -2119,7 +2112,7 @@ function extractSystemText(system) {
2119
2112
  return system.filter((b) => b?.type === "text" && typeof b.text === "string" && b.text).map((b) => b.text).filter((text) => !TRANSPORT_HEADER_BLOCK.test(text)).join(`
2120
2113
  `);
2121
2114
  }
2122
- var HASH_IGNORED_BLOCK_TYPES, HASH_HANDLED_BLOCK_TYPES, HASH_SERIALIZED_BLOCK_TYPES, PASSTHROUGH_CONTINUATION_LEAD_IN, MULTIMODAL_TYPES, TOOL_TARGET_KEYS, TOOL_TARGET_MAX = 80, CONTENT_SUMMARY_MAX = 120, TRANSPORT_HEADER_BLOCK;
2115
+ var HASH_IGNORED_BLOCK_TYPES, HASH_HANDLED_BLOCK_TYPES, HASH_SERIALIZED_BLOCK_TYPES, MULTIMODAL_TYPES, TOOL_TARGET_KEYS, TOOL_TARGET_MAX = 80, CONTENT_SUMMARY_MAX = 120, TRANSPORT_HEADER_BLOCK;
2123
2116
  var init_messages = __esm(() => {
2124
2117
  HASH_IGNORED_BLOCK_TYPES = new Set(["thinking", "redacted_thinking"]);
2125
2118
  HASH_HANDLED_BLOCK_TYPES = new Set(["text", "tool_use", "tool_result"]);
@@ -2136,7 +2129,6 @@ var init_messages = __esm(() => {
2136
2129
  "tool_search_tool_result",
2137
2130
  "container_upload"
2138
2131
  ]);
2139
- PASSTHROUGH_CONTINUATION_LEAD_IN = "The tool calls from your previous turn were forwarded to the client, which has now executed them — " + "their results follow. The instruction to end that turn without further text applied to it alone and " + "is now discharged: continue the work and respond.";
2140
2132
  MULTIMODAL_TYPES = new Set(["image", "document", "file"]);
2141
2133
  TOOL_TARGET_KEYS = ["filePath", "file_path", "path", "command", "code", "pattern", "query", "url"];
2142
2134
  TRANSPORT_HEADER_BLOCK = /^\s*x-anthropic-[a-z0-9-]*header\s*:/i;
@@ -6516,19 +6508,34 @@ function resolveMaxConcurrent(source = process.env, warn = console.warn) {
6516
6508
  }
6517
6509
  return DEFAULT_MAX_CONCURRENT;
6518
6510
  }
6511
+ function assertPositiveLimit(limit) {
6512
+ if (!Number.isSafeInteger(limit) || limit <= 0) {
6513
+ throw new RangeError("Semaphore limit must be a positive integer");
6514
+ }
6515
+ return limit;
6516
+ }
6519
6517
 
6520
6518
  class AbortableSemaphore {
6521
- limit;
6522
6519
  activeCount = 0;
6520
+ currentLimit;
6523
6521
  waiters = [];
6524
6522
  constructor(limit) {
6525
- this.limit = limit;
6526
- if (!Number.isSafeInteger(limit) || limit <= 0) {
6527
- throw new RangeError("Semaphore limit must be a positive integer");
6528
- }
6523
+ this.currentLimit = assertPositiveLimit(limit);
6524
+ }
6525
+ get limit() {
6526
+ return this.currentLimit;
6527
+ }
6528
+ setLimit(next) {
6529
+ const limit = assertPositiveLimit(next);
6530
+ if (limit === this.currentLimit)
6531
+ return;
6532
+ const raised = limit > this.currentLimit;
6533
+ this.currentLimit = limit;
6534
+ if (raised)
6535
+ this.grantNext();
6529
6536
  }
6530
6537
  get snapshot() {
6531
- return { active: this.activeCount, queued: this.waiters.length, limit: this.limit };
6538
+ return { active: this.activeCount, queued: this.waiters.length, limit: this.currentLimit };
6532
6539
  }
6533
6540
  acquire(signal) {
6534
6541
  if (signal?.aborted)
@@ -6584,7 +6591,12 @@ class AbortableSemaphore {
6584
6591
  }
6585
6592
  }
6586
6593
  function getProcessSdkSemaphore() {
6587
- processSdkSemaphore ??= new AbortableSemaphore(resolveMaxConcurrent());
6594
+ const limit = resolveMaxConcurrent();
6595
+ if (!processSdkSemaphore) {
6596
+ processSdkSemaphore = new AbortableSemaphore(limit);
6597
+ return processSdkSemaphore;
6598
+ }
6599
+ processSdkSemaphore.setLimit(limit);
6588
6600
  return processSdkSemaphore;
6589
6601
  }
6590
6602
 
@@ -11091,6 +11103,17 @@ function noteAssistantContent(tracker, content) {
11091
11103
  }
11092
11104
  }
11093
11105
  }
11106
+ function noteAssistantMessage(tracker, message) {
11107
+ const m = message;
11108
+ if (m?.type !== "assistant")
11109
+ return;
11110
+ const content = m.message?.content;
11111
+ const before = tracker.expected.size;
11112
+ noteAssistantContent(tracker, content);
11113
+ if (tracker.expected.size > before) {
11114
+ tracker.toolCallAssistantUuid = typeof m.uuid === "string" && m.uuid.length > 0 ? m.uuid : undefined;
11115
+ }
11116
+ }
11094
11117
  function noteUserContent(tracker, content) {
11095
11118
  if (!Array.isArray(content))
11096
11119
  return;
@@ -11101,43 +11124,69 @@ function noteUserContent(tracker, content) {
11101
11124
  }
11102
11125
  }
11103
11126
  }
11104
- function shouldEarlyStop(tracker) {
11105
- if (tracker.fired)
11106
- return false;
11127
+ function allForwardedCallsResolved(tracker) {
11107
11128
  if (tracker.expected.size === 0)
11108
11129
  return false;
11109
11130
  for (const id of tracker.expected) {
11110
11131
  if (!tracker.resolved.has(id))
11111
11132
  return false;
11112
11133
  }
11134
+ return true;
11135
+ }
11136
+ function isCompleteToolResultContinuation(messages, expectedIds) {
11137
+ if (expectedIds.length === 0 || messages.length === 0)
11138
+ return false;
11139
+ const expected = new Set(expectedIds);
11140
+ const actual = new Set;
11141
+ const echoedCalls = new Set;
11142
+ let sawUser = false;
11143
+ let sawNonToolResult = false;
11144
+ for (const message of messages) {
11145
+ if (message.role === "assistant" && !sawUser) {
11146
+ if (Array.isArray(message.content)) {
11147
+ for (const rawBlock of message.content) {
11148
+ const block = rawBlock;
11149
+ if (block?.type !== "tool_use")
11150
+ continue;
11151
+ if (typeof block.id !== "string" || !expected.has(block.id) || echoedCalls.has(block.id))
11152
+ return false;
11153
+ echoedCalls.add(block.id);
11154
+ }
11155
+ }
11156
+ continue;
11157
+ }
11158
+ if (message.role !== "user" || !Array.isArray(message.content) || sawUser)
11159
+ return false;
11160
+ sawUser = true;
11161
+ for (const rawBlock of message.content) {
11162
+ const block = rawBlock;
11163
+ if (block?.type === "tool_result") {
11164
+ if (sawNonToolResult || typeof block.tool_use_id !== "string")
11165
+ return false;
11166
+ if (!expected.has(block.tool_use_id) || actual.has(block.tool_use_id))
11167
+ return false;
11168
+ actual.add(block.tool_use_id);
11169
+ } else {
11170
+ sawNonToolResult = true;
11171
+ }
11172
+ }
11173
+ }
11174
+ return actual.size === expected.size && (echoedCalls.size === 0 || echoedCalls.size === expected.size);
11175
+ }
11176
+ function settledToolCallAssistantUuid(tracker) {
11177
+ return allForwardedCallsResolved(tracker) ? tracker.toolCallAssistantUuid : undefined;
11178
+ }
11179
+ function shouldEarlyStop(tracker) {
11180
+ if (tracker.fired || !settledToolCallAssistantUuid(tracker))
11181
+ return false;
11113
11182
  tracker.fired = true;
11114
11183
  return true;
11115
11184
  }
11116
11185
  function clientAbortDisposition(input) {
11117
11186
  if (input.isIndependentSession || !input.profileSessionId)
11118
11187
  return { action: "none" };
11119
- if (!input.passthrough)
11120
- return { action: "evict" };
11121
- if (input.currentSessionId && !input.sawDuplicateToolUse && input.resumeBoundaryUuid) {
11122
- return { action: "store", resumeUuid: input.resumeBoundaryUuid };
11123
- }
11124
11188
  return { action: "evict" };
11125
11189
  }
11126
- function resumeBoundaryUuid(message) {
11127
- const m = message;
11128
- if (m?.type !== "user")
11129
- return;
11130
- if (typeof m.uuid !== "string" || m.uuid.length === 0)
11131
- return;
11132
- const content = m.message?.content;
11133
- if (!Array.isArray(content))
11134
- return;
11135
- const hasResult = content.some((block) => {
11136
- const b = block;
11137
- return b?.type === "tool_result";
11138
- });
11139
- return hasResult ? m.uuid : undefined;
11140
- }
11141
11190
 
11142
11191
  // src/proxy/envelopeIntegrity.ts
11143
11192
  function checkEmptyToolInputs(contentBlocks, tools) {
@@ -20012,6 +20061,13 @@ function normalizeContextUsage(usage) {
20012
20061
  return lastIteration ?? usage;
20013
20062
  }
20014
20063
  var MIN_SUFFIX_FOR_COMPACTION = 2;
20064
+ function withClientAssistantUuid(existing, clientMessageCount, uuid) {
20065
+ const next = existing.slice(0, clientMessageCount + 1);
20066
+ while (next.length < clientMessageCount)
20067
+ next.push(null);
20068
+ next[clientMessageCount] = typeof uuid === "string" && uuid.length > 0 ? uuid : null;
20069
+ return next;
20070
+ }
20015
20071
  function computeLineageHash(messages) {
20016
20072
  if (!messages || messages.length === 0)
20017
20073
  return "";
@@ -20327,15 +20383,20 @@ function writeStore(store) {
20327
20383
  }
20328
20384
  }
20329
20385
  }
20386
+ function hasLegacyUserDenialBoundary(session) {
20387
+ const legacy = session.passthroughResumeUuid;
20388
+ return typeof legacy === "string" && legacy.length > 0 && !session.passthroughToolCallAssistantUuid;
20389
+ }
20330
20390
  function lookupSharedSession(key) {
20331
20391
  const store = readStore();
20332
- return store[key];
20392
+ const session = store[key];
20393
+ return session && !hasLegacyUserDenialBoundary(session) ? session : undefined;
20333
20394
  }
20334
20395
  function lookupSharedSessionByClaudeId(claudeSessionId) {
20335
20396
  const sessions = Object.values(readStore());
20336
20397
  let newest;
20337
20398
  for (const session of sessions) {
20338
- if (session.claudeSessionId !== claudeSessionId)
20399
+ if (session.claudeSessionId !== claudeSessionId || hasLegacyUserDenialBoundary(session))
20339
20400
  continue;
20340
20401
  if (!newest || session.lastUsedAt > newest.lastUsedAt) {
20341
20402
  newest = session;
@@ -20343,7 +20404,7 @@ function lookupSharedSessionByClaudeId(claudeSessionId) {
20343
20404
  }
20344
20405
  return newest;
20345
20406
  }
20346
- function storeSharedSession(key, claudeSessionId, messageCount, lineageHash, messageHashes, sdkMessageUuids, contextUsage, messageBlockHashes, passthroughResumeUuid) {
20407
+ function storeSharedSession(key, claudeSessionId, messageCount, lineageHash, messageHashes, sdkMessageUuids, contextUsage, messageBlockHashes, passthroughToolCallAssistantUuid, passthroughToolCallIds) {
20347
20408
  const path3 = getStorePath();
20348
20409
  const lockPath = `${path3}.lock`;
20349
20410
  const hasLock = skipLocking ? false : acquireLock(lockPath);
@@ -20363,7 +20424,8 @@ function storeSharedSession(key, claudeSessionId, messageCount, lineageHash, mes
20363
20424
  messageHashes: messageHashes ?? existing?.messageHashes,
20364
20425
  messageBlockHashes: messageBlockHashes ?? existing?.messageBlockHashes,
20365
20426
  sdkMessageUuids: sdkMessageUuids ?? existing?.sdkMessageUuids,
20366
- passthroughResumeUuid: passthroughResumeUuid === undefined ? existing?.passthroughResumeUuid : passthroughResumeUuid ?? undefined,
20427
+ passthroughToolCallAssistantUuid: passthroughToolCallAssistantUuid === undefined ? existing?.passthroughToolCallAssistantUuid : passthroughToolCallAssistantUuid ?? undefined,
20428
+ passthroughToolCallIds: passthroughToolCallIds === undefined ? existing?.passthroughToolCallIds : passthroughToolCallIds ?? undefined,
20367
20429
  contextUsage: contextUsage ?? existing?.contextUsage,
20368
20430
  ...previousClaudeSessionId ? { previousClaudeSessionId } : {}
20369
20431
  };
@@ -20561,7 +20623,8 @@ function lookupSession(sessionId, messages, workingDirectory) {
20561
20623
  messageHashes: shared.messageHashes,
20562
20624
  messageBlockHashes: shared.messageBlockHashes,
20563
20625
  sdkMessageUuids: shared.sdkMessageUuids,
20564
- passthroughResumeUuid: shared.passthroughResumeUuid,
20626
+ passthroughToolCallAssistantUuid: shared.passthroughToolCallAssistantUuid,
20627
+ passthroughToolCallIds: shared.passthroughToolCallIds,
20565
20628
  contextUsage: shared.contextUsage
20566
20629
  };
20567
20630
  const result = classifyLineage(state, messages, sessionId);
@@ -20591,7 +20654,8 @@ function lookupSession(sessionId, messages, workingDirectory) {
20591
20654
  messageHashes: shared.messageHashes,
20592
20655
  messageBlockHashes: shared.messageBlockHashes,
20593
20656
  sdkMessageUuids: shared.sdkMessageUuids,
20594
- passthroughResumeUuid: shared.passthroughResumeUuid,
20657
+ passthroughToolCallAssistantUuid: shared.passthroughToolCallAssistantUuid,
20658
+ passthroughToolCallIds: shared.passthroughToolCallIds,
20595
20659
  contextUsage: shared.contextUsage
20596
20660
  };
20597
20661
  const result = classifyLineage(state, messages, fp);
@@ -20626,13 +20690,14 @@ function getSessionByClaudeId(claudeSessionId) {
20626
20690
  messageHashes: shared.messageHashes,
20627
20691
  messageBlockHashes: shared.messageBlockHashes,
20628
20692
  sdkMessageUuids: shared.sdkMessageUuids,
20629
- passthroughResumeUuid: shared.passthroughResumeUuid,
20693
+ passthroughToolCallAssistantUuid: shared.passthroughToolCallAssistantUuid,
20694
+ passthroughToolCallIds: shared.passthroughToolCallIds,
20630
20695
  contextUsage: shared.contextUsage
20631
20696
  });
20632
20697
  }
20633
20698
  return newest;
20634
20699
  }
20635
- function storeSession(sessionId, messages, claudeSessionId, workingDirectory, sdkMessageUuids, contextUsage, passthroughResumeUuid) {
20700
+ function storeSession(sessionId, messages, claudeSessionId, workingDirectory, sdkMessageUuids, contextUsage, passthroughToolCallAssistantUuid, passthroughToolCallIds) {
20636
20701
  if (!claudeSessionId)
20637
20702
  return;
20638
20703
  const lineageHash = computeLineageHash(messages);
@@ -20646,7 +20711,8 @@ function storeSession(sessionId, messages, claudeSessionId, workingDirectory, sd
20646
20711
  messageHashes,
20647
20712
  messageBlockHashes,
20648
20713
  sdkMessageUuids,
20649
- ...passthroughResumeUuid ? { passthroughResumeUuid } : {},
20714
+ ...passthroughToolCallAssistantUuid ? { passthroughToolCallAssistantUuid } : {},
20715
+ ...passthroughToolCallIds ? { passthroughToolCallIds } : {},
20650
20716
  ...contextUsage ? { contextUsage } : {}
20651
20717
  };
20652
20718
  if (sessionId)
@@ -20656,7 +20722,7 @@ function storeSession(sessionId, messages, claudeSessionId, workingDirectory, sd
20656
20722
  fingerprintCache.set(fp, state);
20657
20723
  const key = sessionId || fp;
20658
20724
  if (key) {
20659
- storeSharedSession(key, claudeSessionId, state.messageCount, lineageHash, messageHashes, sdkMessageUuids, contextUsage, messageBlockHashes, passthroughResumeUuid ?? null);
20725
+ storeSharedSession(key, claudeSessionId, state.messageCount, lineageHash, messageHashes, sdkMessageUuids, contextUsage, messageBlockHashes, passthroughToolCallAssistantUuid ?? null, passthroughToolCallIds ?? null);
20660
20726
  }
20661
20727
  }
20662
20728
 
@@ -20816,21 +20882,21 @@ function stripCacheControlDeep(content) {
20816
20882
  return rest;
20817
20883
  });
20818
20884
  }
20819
- function normalizeStructuredUserContent(content) {
20885
+ function normalizeStructuredUserContent(content, preserveToolResultWrapper = false) {
20820
20886
  if (!Array.isArray(content))
20821
20887
  return content;
20822
20888
  const normalized = [];
20823
20889
  for (const block of content) {
20824
20890
  if (!block || typeof block !== "object")
20825
20891
  continue;
20826
- if (block.type === "tool_result" && Array.isArray(block.content) && hasMultimodalContent(block.content)) {
20892
+ if (!preserveToolResultWrapper && block.type === "tool_result" && Array.isArray(block.content) && hasMultimodalContent(block.content)) {
20827
20893
  normalized.push(...normalizeStructuredUserContent(block.content));
20828
20894
  continue;
20829
20895
  }
20830
20896
  if (block.type === "tool_result" && Array.isArray(block.content)) {
20831
20897
  normalized.push({
20832
20898
  ...block,
20833
- content: normalizeStructuredUserContent(block.content)
20899
+ content: normalizeStructuredUserContent(block.content, preserveToolResultWrapper)
20834
20900
  });
20835
20901
  continue;
20836
20902
  }
@@ -21437,15 +21503,16 @@ data: ${JSON.stringify(lastError)}
21437
21503
  } : undefined
21438
21504
  }, adapterBase);
21439
21505
  }
21440
- const isResume = lineageResult.type === "continuation" || lineageResult.type === "compaction";
21506
+ let isResume = lineageResult.type === "continuation" || lineageResult.type === "compaction";
21441
21507
  const isUndo = lineageResult.type === "undo";
21442
21508
  const cachedSession = lineageResult.type !== "diverged" ? lineageResult.session : undefined;
21443
- const resumeSessionId = cachedSession?.claudeSessionId;
21509
+ let resumeSessionId = cachedSession?.claudeSessionId;
21444
21510
  const passthrough = adapter.instancePassthrough !== undefined ? adapter.instancePassthrough : pipelineCtx.passthrough !== undefined ? pipelineCtx.passthrough : envBool("PASSTHROUGH");
21445
21511
  const resumeFrom = lineageResult.type === "continuation" || lineageResult.type === "compaction" ? lineageResult.resumeFrom : undefined;
21446
21512
  const resumeContentFrom = lineageResult.type === "continuation" ? lineageResult.resumeContentFrom : undefined;
21447
21513
  const undoRollbackUuid = isUndo && lineageResult.type === "undo" ? lineageResult.rollbackUuid : undefined;
21448
- const passthroughResumeUuid = passthrough && isResume ? cachedSession?.passthroughResumeUuid : undefined;
21514
+ let passthroughToolCallAssistantUuid = passthrough && isResume ? cachedSession?.passthroughToolCallAssistantUuid : undefined;
21515
+ const passthroughToolCallIds = passthrough && isResume ? cachedSession?.passthroughToolCallIds : undefined;
21449
21516
  const msgSummary = body.messages?.map((m) => {
21450
21517
  const contentTypes = Array.isArray(m.content) ? m.content.map((b) => b.type).join(",") : "string";
21451
21518
  return `${m.role}[${contentTypes}]`;
@@ -21511,17 +21578,28 @@ data: ${JSON.stringify(lastError)}
21511
21578
  } else {
21512
21579
  messagesToConvert = allMessages;
21513
21580
  }
21581
+ if (passthroughToolCallAssistantUuid && !isCompleteToolResultContinuation(messagesToConvert, passthroughToolCallIds ?? [])) {
21582
+ claudeLog("passthrough.checkpoint_replay", {
21583
+ expectedToolIds: passthroughToolCallIds?.length ?? 0,
21584
+ reason: "incomplete_or_mismatched_results"
21585
+ });
21586
+ isResume = false;
21587
+ resumeSessionId = undefined;
21588
+ passthroughToolCallAssistantUuid = undefined;
21589
+ messagesToConvert = allMessages;
21590
+ }
21514
21591
  const hasMultimodal = messagesToConvert?.some((m) => hasMultimodalContent(m.content));
21592
+ const hasPassthroughToolResults = Boolean(passthroughToolCallAssistantUuid) && messagesToConvert?.some((m) => m.role === "user" && Array.isArray(m.content) && m.content.some((block) => block?.type === "tool_result"));
21515
21593
  let structuredMessages;
21516
21594
  let textPrompt;
21517
- if (hasMultimodal) {
21595
+ if (hasMultimodal || hasPassthroughToolResults) {
21518
21596
  structuredMessages = [];
21519
21597
  if (isResume) {
21520
21598
  for (const m of messagesToConvert) {
21521
21599
  if (m.role === "user") {
21522
21600
  structuredMessages.push({
21523
21601
  type: "user",
21524
- message: { role: "user", content: normalizeStructuredUserContent(stripCacheControlDeep(m.content)) },
21602
+ message: { role: "user", content: normalizeStructuredUserContent(stripCacheControlDeep(m.content), Boolean(passthroughToolCallAssistantUuid)) },
21525
21603
  parent_tool_use_id: null
21526
21604
  });
21527
21605
  }
@@ -21531,7 +21609,7 @@ data: ${JSON.stringify(lastError)}
21531
21609
  if (m.role === "user") {
21532
21610
  structuredMessages.push({
21533
21611
  type: "user",
21534
- message: { role: "user", content: normalizeStructuredUserContent(stripCacheControlDeep(m.content)) },
21612
+ message: { role: "user", content: normalizeStructuredUserContent(stripCacheControlDeep(m.content), Boolean(passthroughToolCallAssistantUuid)) },
21535
21613
  parent_tool_use_id: null
21536
21614
  });
21537
21615
  } else {
@@ -21549,13 +21627,6 @@ data: ${JSON.stringify(lastError)}
21549
21627
  if (structuredMessages.length > 1) {
21550
21628
  structuredMessages = consolidateMultimodalOntoLastUser(structuredMessages);
21551
21629
  }
21552
- if (passthroughResumeUuid && structuredMessages.length > 0) {
21553
- structuredMessages.unshift({
21554
- type: "user",
21555
- message: { role: "user", content: PASSTHROUGH_CONTINUATION_LEAD_IN },
21556
- parent_tool_use_id: null
21557
- });
21558
- }
21559
21630
  } else {
21560
21631
  const toolIndex = buildToolUseIndex(allMessages ?? messagesToConvert ?? []);
21561
21632
  const promptTurns = (messagesToConvert ?? []).map((m) => {
@@ -21570,7 +21641,7 @@ data: ${JSON.stringify(lastError)}
21570
21641
  const resumeDelta = promptTurns.map((t) => t.text).filter(Boolean).join(`
21571
21642
 
21572
21643
  `) || "";
21573
- textPrompt = isResume ? passthroughResumeUuid ? framePassthroughContinuation(resumeDelta) : resumeDelta : frameReplayTurns(promptTurns);
21644
+ textPrompt = isResume ? resumeDelta : frameReplayTurns(promptTurns);
21574
21645
  }
21575
21646
  const settingSources = envBool("LOAD_CONTEXT") || sdkFeatures.claudeMd === "full" ? ["user", "project"] : sdkFeatures.claudeMd === "project" ? ["project"] : pipelineCtx.settingSources ?? [];
21576
21647
  const capturedToolUses = [];
@@ -21673,11 +21744,15 @@ data: ${JSON.stringify(lastError)}
21673
21744
  }
21674
21745
  const signature = toolUseSignature(toolName, toolInput);
21675
21746
  const isExactDuplicate = capturedSignatures.has(signature);
21747
+ const isPostCheckpointCall = earlyStopFired;
21676
21748
  const isSameToolRepeat = !earlyStopEnabled && !isExactDuplicate && capturedToolNames.has(toolName);
21677
21749
  const exceedsForcedSingle = forceSingleToolUse && capturedToolUses.length >= 1;
21678
- if (isExactDuplicate) {
21750
+ if (isExactDuplicate || isPostCheckpointCall) {
21679
21751
  droppedToolUseIds.add(input.tool_use_id);
21680
- claudeLog("passthrough.duplicate_tool_use_dropped", { name: toolName });
21752
+ claudeLog("passthrough.duplicate_tool_use_dropped", {
21753
+ name: toolName,
21754
+ reason: isPostCheckpointCall ? "hidden_digest" : "exact_duplicate"
21755
+ });
21681
21756
  } else if (isSameToolRepeat || exceedsForcedSingle) {
21682
21757
  droppedToolUseIds.add(input.tool_use_id);
21683
21758
  sawDuplicateToolUse = true;
@@ -21698,10 +21773,10 @@ data: ${JSON.stringify(lastError)}
21698
21773
  if (earlyStopEnabled && turnGenerating && !requestAbort.controller.signal.aborted) {
21699
21774
  await holdDenyUntilTurnEnd();
21700
21775
  }
21701
- if (isExactDuplicate) {
21776
+ if (isExactDuplicate || isPostCheckpointCall) {
21702
21777
  return {
21703
21778
  decision: "block",
21704
- reason: "This exact tool call has already been forwarded to the client — do not repeat it. " + "Do not call additional tools and do not generate further text — end your turn now."
21779
+ reason: "This tool call has already been handled by the client-facing turn — do not repeat it. " + "Do not call additional tools and do not generate further text — end your turn now."
21705
21780
  };
21706
21781
  }
21707
21782
  if (isSameToolRepeat || exceedsForcedSingle) {
@@ -21733,13 +21808,15 @@ data: ${JSON.stringify(lastError)}
21733
21808
  const upstreamStartAt = Date.now();
21734
21809
  let firstChunkAt;
21735
21810
  let currentSessionId;
21736
- const sdkUuidMap = cachedSession?.sdkMessageUuids ? [...cachedSession.sdkMessageUuids] : [];
21811
+ let sdkUuidMap = (isResume || isUndo) && cachedSession?.sdkMessageUuids ? [...cachedSession.sdkMessageUuids] : [];
21737
21812
  while (sdkUuidMap.length < allMessages.length)
21738
21813
  sdkUuidMap.push(null);
21739
21814
  claudeLog("upstream.start", { mode: "non_stream", model });
21740
21815
  let lastUsage;
21741
21816
  let lastStopReason;
21742
- let nextPassthroughResumeUuid;
21817
+ let nextPassthroughToolCallAssistantUuid;
21818
+ let nextPassthroughToolCallIds;
21819
+ let sawCanonicalResult = false;
21743
21820
  try {
21744
21821
  if (!claudeExecutable) {
21745
21822
  claudeExecutable = await resolveClaudeExecutableAsync();
@@ -21777,8 +21854,8 @@ data: ${JSON.stringify(lastError)}
21777
21854
  hasDeferredTools,
21778
21855
  resumeSessionId,
21779
21856
  isUndo,
21780
- resumeSessionAtUuid: undoRollbackUuid ?? passthroughResumeUuid,
21781
- forkSession: busySessionFork || Boolean(passthroughResumeUuid) || undefined,
21857
+ resumeSessionAtUuid: undoRollbackUuid ?? passthroughToolCallAssistantUuid,
21858
+ forkSession: busySessionFork || undefined,
21782
21859
  sdkHooks,
21783
21860
  blockedTools: pipelineCtx.blockedTools,
21784
21861
  incompatibleTools: pipelineCtx.incompatibleTools,
@@ -22011,25 +22088,33 @@ data: ${JSON.stringify(lastError)}
22011
22088
  claudeLog("passthrough.loop_break", { mode: "non_stream", assistantMessages, captured: capturedToolUses.length });
22012
22089
  break;
22013
22090
  }
22014
- if (earlyStopEnabled) {
22015
- if (message.type === "assistant") {
22016
- noteAssistantContent(earlyStop, message.message?.content);
22017
- } else if (message.type === "user") {
22018
- nextPassthroughResumeUuid = resumeBoundaryUuid(message) ?? nextPassthroughResumeUuid;
22019
- noteUserContent(earlyStop, message.message?.content);
22020
- if (shouldEarlyStop(earlyStop)) {
22021
- earlyStopFired = true;
22022
- claudeLog("passthrough.early_stop", { mode: "non_stream", captured: capturedToolUses.length });
22023
- requestAbort.abort("passthrough turn complete");
22024
- break;
22091
+ let assistantAddedForwardedCall = false;
22092
+ if (passthrough && message.type === "assistant" && !earlyStopFired && earlyStop.resolved.size === 0) {
22093
+ const expectedBefore = earlyStop.expected.size;
22094
+ noteAssistantMessage(earlyStop, message);
22095
+ assistantAddedForwardedCall = earlyStop.expected.size > expectedBefore;
22096
+ } else if (passthrough && message.type === "user" && !earlyStopFired) {
22097
+ noteUserContent(earlyStop, message.message?.content);
22098
+ if (earlyStopEnabled && shouldEarlyStop(earlyStop)) {
22099
+ nextPassthroughToolCallAssistantUuid = settledToolCallAssistantUuid(earlyStop);
22100
+ nextPassthroughToolCallIds = [...earlyStop.expected];
22101
+ earlyStopFired = true;
22102
+ for (let i = capturedToolUses.length - 1;i >= 0; i--) {
22103
+ if (!earlyStop.expected.has(capturedToolUses[i].id))
22104
+ capturedToolUses.splice(i, 1);
22025
22105
  }
22106
+ claudeLog("passthrough.checkpoint_ready", {
22107
+ mode: "non_stream",
22108
+ captured: capturedToolUses.length,
22109
+ toolCallAssistantUuid: nextPassthroughToolCallAssistantUuid
22110
+ });
22026
22111
  }
22027
22112
  }
22028
22113
  if (message.type === "assistant") {
22029
22114
  releaseHeldDenies("assistant_message");
22030
22115
  assistantMessages += 1;
22031
- if (message.uuid) {
22032
- sdkUuidMap.push(message.uuid);
22116
+ if (!passthrough || earlyStop.expected.size === 0 || assistantAddedForwardedCall) {
22117
+ sdkUuidMap = withClientAssistantUuid(sdkUuidMap, allMessages.length, message.uuid);
22033
22118
  }
22034
22119
  if (!firstChunkAt) {
22035
22120
  firstChunkAt = Date.now();
@@ -22073,11 +22158,12 @@ data: ${JSON.stringify(lastError)}
22073
22158
  const msgUsage = message.message.usage;
22074
22159
  if (msgUsage)
22075
22160
  lastUsage = { ...lastUsage, ...msgUsage };
22076
- if (typeof message.message.stop_reason === "string") {
22161
+ if (!isPassthroughTurn2 && typeof message.message.stop_reason === "string") {
22077
22162
  lastStopReason = message.message.stop_reason;
22078
22163
  }
22079
22164
  }
22080
22165
  if (message.type === "result") {
22166
+ sawCanonicalResult = true;
22081
22167
  const resultUsage = message.usage;
22082
22168
  if (resultUsage) {
22083
22169
  lastUsage = { ...lastUsage, ...resultUsage };
@@ -22109,6 +22195,10 @@ data: ${JSON.stringify(lastError)}
22109
22195
  }
22110
22196
  } catch (error) {
22111
22197
  releaseHeldDenies("non_stream_error");
22198
+ if (passthrough && capturedToolUses.length > 0 && !sawCanonicalResult) {
22199
+ evictSession(profileSessionId, profileScopedCwd, body.messages || []);
22200
+ claudeLog("passthrough.noncanonical_session_evicted", { mode: "non_stream", reason: "drain_error" });
22201
+ }
22112
22202
  const stderrOutput = stderrLines.join(`
22113
22203
  `).trim();
22114
22204
  if (stderrOutput && error instanceof Error && !error.message.includes(stderrOutput)) {
@@ -22263,8 +22353,14 @@ Subprocess stderr: ${stderrOutput}`;
22263
22353
  ]);
22264
22354
  }
22265
22355
  if (currentSessionId && !isIndependentSession && !sawDuplicateToolUse) {
22266
- storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage, earlyStopFired ? nextPassthroughResumeUuid : null);
22267
- commitSessionTurn();
22356
+ const checkpointTurn = passthrough && contentBlocks.some((b) => b.type === "tool_use");
22357
+ if (checkpointTurn && (!earlyStopFired || !sawCanonicalResult)) {
22358
+ evictSession(profileSessionId, profileScopedCwd, body.messages || []);
22359
+ claudeLog("passthrough.noncanonical_session_evicted", { mode: "non_stream" });
22360
+ } else {
22361
+ storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage, earlyStopFired ? nextPassthroughToolCallAssistantUuid : null, earlyStopFired ? nextPassthroughToolCallIds : null);
22362
+ commitSessionTurn();
22363
+ }
22268
22364
  }
22269
22365
  const responseSessionId = currentSessionId || resumeSessionId || `session_${Date.now()}`;
22270
22366
  return new Response(JSON.stringify({
@@ -22305,6 +22401,7 @@ Subprocess stderr: ${stderrOutput}`;
22305
22401
  let bytesSent = 0;
22306
22402
  let streamClosed = false;
22307
22403
  let awaitingEarlyStopDrain = false;
22404
+ let exitedBeforeCanonicalTerminal = false;
22308
22405
  claudeLog("upstream.start", { mode: "stream", model });
22309
22406
  const safeEnqueue = (payload, source) => {
22310
22407
  if (streamClosed)
@@ -22326,14 +22423,16 @@ Subprocess stderr: ${stderrOutput}`;
22326
22423
  throw error;
22327
22424
  }
22328
22425
  };
22329
- const sdkUuidMap = cachedSession?.sdkMessageUuids ? [...cachedSession.sdkMessageUuids] : [];
22426
+ let sdkUuidMap = (isResume || isUndo) && cachedSession?.sdkMessageUuids ? [...cachedSession.sdkMessageUuids] : [];
22330
22427
  while (sdkUuidMap.length < allMessages.length)
22331
22428
  sdkUuidMap.push(null);
22332
22429
  let messageStartEmitted = false;
22333
22430
  let lastUsage;
22334
22431
  let hasStructuredOutput = false;
22335
22432
  let structuredOutput;
22336
- let nextPassthroughResumeUuid;
22433
+ let nextPassthroughToolCallAssistantUuid;
22434
+ let nextPassthroughToolCallIds;
22435
+ let sawCanonicalResult = false;
22337
22436
  const silentTurnRecoveryEnabled = env("SILENT_TURN_RECOVERY") !== "0";
22338
22437
  let silentTurnRecoveryAttempted = false;
22339
22438
  let silentTurnRecovered = false;
@@ -22358,33 +22457,6 @@ data: ${JSON.stringify({
22358
22457
  eventsForwarded += 1;
22359
22458
  };
22360
22459
  const openClientBlocks = new Set;
22361
- let pendingEarlyStop = false;
22362
- let pendingEarlyStopAt = 0;
22363
- const fireEarlyStop = (reason) => {
22364
- earlyStopFired = true;
22365
- claudeLog("passthrough.early_stop", {
22366
- mode: "stream",
22367
- captured: capturedToolUses.length,
22368
- drained: awaitingEarlyStopDrain,
22369
- reason,
22370
- deferredMs: pendingEarlyStopAt ? Date.now() - pendingEarlyStopAt : 0
22371
- });
22372
- pendingEarlyStop = false;
22373
- flushOpenClientBlocks("early_stop");
22374
- sendTerminalDelta("tool_use");
22375
- safeEnqueue(encoder.encode(`event: message_stop
22376
- data: ${JSON.stringify({ type: "message_stop" })}
22377
-
22378
- `), "early_stop");
22379
- requestAbort.abort("passthrough turn complete");
22380
- awaitingEarlyStopDrain = false;
22381
- if (!streamClosed) {
22382
- streamClosed = true;
22383
- try {
22384
- controller.close();
22385
- } catch {}
22386
- }
22387
- };
22388
22460
  const flushOpenClientBlocks = (source) => {
22389
22461
  if (openClientBlocks.size === 0)
22390
22462
  return;
@@ -22435,8 +22507,8 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
22435
22507
  hasDeferredTools,
22436
22508
  resumeSessionId,
22437
22509
  isUndo,
22438
- resumeSessionAtUuid: undoRollbackUuid ?? passthroughResumeUuid,
22439
- forkSession: busySessionFork || Boolean(passthroughResumeUuid) || undefined,
22510
+ resumeSessionAtUuid: undoRollbackUuid ?? passthroughToolCallAssistantUuid,
22511
+ forkSession: busySessionFork || undefined,
22440
22512
  sdkHooks,
22441
22513
  blockedTools: pipelineCtx.blockedTools,
22442
22514
  incompatibleTools: pipelineCtx.incompatibleTools,
@@ -22696,38 +22768,42 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
22696
22768
  try {
22697
22769
  for await (const message of guardedResponse) {
22698
22770
  if (streamClosed && !awaitingEarlyStopDrain) {
22771
+ exitedBeforeCanonicalTerminal = true;
22699
22772
  break;
22700
22773
  }
22701
22774
  if (message.session_id) {
22702
22775
  currentSessionId = message.session_id;
22703
22776
  }
22704
- if (message.type === "assistant" && message.uuid) {
22705
- sdkUuidMap.push(message.uuid);
22777
+ let assistantAddedForwardedCall = false;
22778
+ if (earlyStopEnabled && message.type === "assistant" && !earlyStopFired) {
22779
+ const expectedBefore = earlyStop.expected.size;
22780
+ noteAssistantMessage(earlyStop, message);
22781
+ assistantAddedForwardedCall = earlyStop.expected.size > expectedBefore;
22782
+ } else if (earlyStopEnabled && message.type === "user" && !earlyStopFired) {
22783
+ noteUserContent(earlyStop, message.message?.content);
22706
22784
  }
22707
- nextPassthroughResumeUuid = resumeBoundaryUuid(message) ?? nextPassthroughResumeUuid;
22708
- if (earlyStopEnabled) {
22709
- if (message.type === "assistant") {
22710
- noteAssistantContent(earlyStop, message.message?.content);
22711
- } else if (message.type === "user") {
22712
- noteUserContent(earlyStop, message.message?.content);
22713
- if (shouldEarlyStop(earlyStop) && streamedToolUseIds.size > 0) {
22714
- if (openClientBlocks.size > 0) {
22715
- if (!pendingEarlyStop) {
22716
- pendingEarlyStop = true;
22717
- pendingEarlyStopAt = Date.now();
22718
- claudeLog("passthrough.early_stop_deferred", {
22719
- openBlocks: openClientBlocks.size,
22720
- captured: capturedToolUses.length
22721
- });
22722
- }
22723
- } else {
22724
- fireEarlyStop("immediate");
22725
- break;
22726
- }
22785
+ if (earlyStopEnabled && !earlyStopFired) {
22786
+ const hasCompleteStreamedSet = streamedToolUseIds.size > 0 && earlyStop.expected.size === streamedToolUseIds.size && [...streamedToolUseIds].every((id) => earlyStop.expected.has(id));
22787
+ if (!turnGenerating && openClientBlocks.size === 0 && hasCompleteStreamedSet && shouldEarlyStop(earlyStop)) {
22788
+ nextPassthroughToolCallAssistantUuid = settledToolCallAssistantUuid(earlyStop);
22789
+ nextPassthroughToolCallIds = [...earlyStop.expected];
22790
+ earlyStopFired = true;
22791
+ for (let i = capturedToolUses.length - 1;i >= 0; i--) {
22792
+ if (!earlyStop.expected.has(capturedToolUses[i].id))
22793
+ capturedToolUses.splice(i, 1);
22727
22794
  }
22795
+ claudeLog("passthrough.checkpoint_ready", {
22796
+ mode: "stream",
22797
+ captured: capturedToolUses.length,
22798
+ toolCallAssistantUuid: nextPassthroughToolCallAssistantUuid
22799
+ });
22728
22800
  }
22729
22801
  }
22802
+ if (message.type === "assistant" && (!passthrough || earlyStop.expected.size === 0 || assistantAddedForwardedCall)) {
22803
+ sdkUuidMap = withClientAssistantUuid(sdkUuidMap, allMessages.length, message.uuid);
22804
+ }
22730
22805
  if (message.type === "result") {
22806
+ sawCanonicalResult = true;
22731
22807
  const resultUsage = message.usage;
22732
22808
  if (resultUsage)
22733
22809
  lastUsage = { ...lastUsage, ...resultUsage };
@@ -22737,6 +22813,8 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
22737
22813
  }
22738
22814
  }
22739
22815
  if (message.type === "stream_event") {
22816
+ if (streamClosed && awaitingEarlyStopDrain)
22817
+ continue;
22740
22818
  streamEventsSeen += 1;
22741
22819
  if (!firstChunkAt) {
22742
22820
  firstChunkAt = Date.now();
@@ -22776,16 +22854,19 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
22776
22854
  lastUsage = { ...lastUsage, ...startUsage };
22777
22855
  if (messageStartEmitted) {
22778
22856
  if (passthrough && streamedToolUseIds.size > 0) {
22779
- flushOpenClientBlocks("turn2_suppression");
22780
- sendTerminalDelta("tool_use");
22781
- safeEnqueue(encoder.encode(`event: message_stop
22857
+ if (!streamClosed) {
22858
+ flushOpenClientBlocks("turn2_suppression");
22859
+ sendTerminalDelta("tool_use");
22860
+ safeEnqueue(encoder.encode(`event: message_stop
22782
22861
  data: ${JSON.stringify({ type: "message_stop" })}
22783
22862
 
22784
22863
  `), "passthrough_turn2_stop");
22864
+ streamClosed = true;
22865
+ controller.close();
22866
+ }
22867
+ awaitingEarlyStopDrain = true;
22785
22868
  claudeLog("passthrough.turn2_suppressed", { mode: "stream", toolUses: streamedToolUseIds.size });
22786
- streamClosed = true;
22787
- controller.close();
22788
- break;
22869
+ continue;
22789
22870
  }
22790
22871
  continue;
22791
22872
  }
@@ -22909,10 +22990,6 @@ data: ${JSON.stringify(event)}
22909
22990
  const idx = event.index;
22910
22991
  if (typeof idx === "number")
22911
22992
  openClientBlocks.delete(idx);
22912
- if (pendingEarlyStop && openClientBlocks.size === 0) {
22913
- fireEarlyStop("blocks_closed");
22914
- break;
22915
- }
22916
22993
  }
22917
22994
  if (passthrough && eventType === "message_delta" && event.delta?.stop_reason === "tool_use" && streamedToolUseIds.size > 0) {
22918
22995
  flushOpenClientBlocks("drain_close");
@@ -22927,6 +23004,7 @@ data: ${JSON.stringify({ type: "message_stop" })}
22927
23004
  awaitingEarlyStopDrain = true;
22928
23005
  continue;
22929
23006
  }
23007
+ exitedBeforeCanonicalTerminal = true;
22930
23008
  break;
22931
23009
  }
22932
23010
  if (eventType === "content_block_delta") {
@@ -23022,8 +23100,14 @@ data: ${JSON.stringify({
23022
23100
  plog(`[PROXY] ${requestMeta.requestId} discovered=${discoveredTools.size} (${newNames}) session_total=${allNames.length}`);
23023
23101
  }
23024
23102
  if (currentSessionId && !isIndependentSession && !sawDuplicateToolUse) {
23025
- storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage, earlyStopFired ? nextPassthroughResumeUuid : null);
23026
- commitSessionTurn();
23103
+ const checkpointTurn = passthrough && streamedToolUseIds.size > 0;
23104
+ if (exitedBeforeCanonicalTerminal || checkpointTurn && (!earlyStopFired || !sawCanonicalResult)) {
23105
+ evictSession(profileSessionId, profileScopedCwd, body.messages || []);
23106
+ claudeLog("passthrough.noncanonical_session_evicted", { mode: "stream" });
23107
+ } else {
23108
+ storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage, earlyStopFired ? nextPassthroughToolCallAssistantUuid : null, earlyStopFired ? nextPassthroughToolCallIds : null);
23109
+ commitSessionTurn();
23110
+ }
23027
23111
  }
23028
23112
  const classifyNow = () => classifyTurnOutcome({
23029
23113
  textEvents: textEventsForwarded,
@@ -23048,7 +23132,8 @@ data: ${JSON.stringify({
23048
23132
  });
23049
23133
  const recoveryLifter = createRecoveryLifter(() => nextClientBlockIndex++);
23050
23134
  let recoverySessionId;
23051
- let recoveryBoundaryUuid;
23135
+ let recoveryToolCallAssistantUuid;
23136
+ const recoveryEarlyStop = createEarlyStopTracker();
23052
23137
  try {
23053
23138
  for await (const event of runSdkQueryAttempt(buildQueryOptions({
23054
23139
  prompt: SILENT_TURN_NUDGE,
@@ -23066,7 +23151,7 @@ data: ${JSON.stringify({
23066
23151
  hasDeferredTools,
23067
23152
  resumeSessionId: currentSessionId || resumeSessionId,
23068
23153
  isUndo: false,
23069
- resumeSessionAtUuid: nextPassthroughResumeUuid,
23154
+ resumeSessionAtUuid: nextPassthroughToolCallAssistantUuid,
23070
23155
  forkSession: true,
23071
23156
  sdkHooks,
23072
23157
  blockedTools: pipelineCtx.blockedTools,
@@ -23096,7 +23181,12 @@ data: ${JSON.stringify({
23096
23181
  const recoveryMessage = event;
23097
23182
  if (recoveryMessage.session_id)
23098
23183
  recoverySessionId = recoveryMessage.session_id;
23099
- recoveryBoundaryUuid = resumeBoundaryUuid(recoveryMessage) ?? recoveryBoundaryUuid;
23184
+ if (recoveryMessage.type === "assistant") {
23185
+ noteAssistantMessage(recoveryEarlyStop, recoveryMessage);
23186
+ } else if (recoveryMessage.type === "user") {
23187
+ noteUserContent(recoveryEarlyStop, recoveryMessage.message?.content);
23188
+ recoveryToolCallAssistantUuid = settledToolCallAssistantUuid(recoveryEarlyStop);
23189
+ }
23100
23190
  if (recoveryMessage.type !== "stream_event")
23101
23191
  continue;
23102
23192
  const lifted = recoveryLifter.lift(event.event);
@@ -23125,11 +23215,12 @@ data: ${JSON.stringify(lifted.frame)}
23125
23215
  }
23126
23216
  if (silentTurnRecovered && recoverySessionId && !isIndependentSession && !sawDuplicateToolUse) {
23127
23217
  currentSessionId = recoverySessionId;
23128
- nextPassthroughResumeUuid = recoveryBoundaryUuid;
23218
+ nextPassthroughToolCallAssistantUuid = recoveryToolCallAssistantUuid;
23219
+ nextPassthroughToolCallIds = recoveryToolCallAssistantUuid ? [...recoveryEarlyStop.expected] : undefined;
23129
23220
  sdkUuidMap.length = 0;
23130
23221
  for (let i = 0;i < allMessages.length; i++)
23131
23222
  sdkUuidMap.push(null);
23132
- storeSession(profileSessionId, body.messages || [], recoverySessionId, profileScopedCwd, sdkUuidMap, lastUsage, recoveryBoundaryUuid ?? null);
23223
+ storeSession(profileSessionId, body.messages || [], recoverySessionId, profileScopedCwd, sdkUuidMap, lastUsage, recoveryToolCallAssistantUuid ?? null, recoveryToolCallAssistantUuid ? [...recoveryEarlyStop.expected] : null);
23133
23224
  commitSessionTurn();
23134
23225
  }
23135
23226
  claudeLog("response.silent_turn_recovery_result", {
@@ -23307,18 +23398,19 @@ data: {"type":"message_stop"}
23307
23398
  profileSessionId,
23308
23399
  currentSessionId,
23309
23400
  sawDuplicateToolUse,
23310
- resumeBoundaryUuid: nextPassthroughResumeUuid,
23401
+ toolCallAssistantUuid: nextPassthroughToolCallAssistantUuid,
23311
23402
  passthrough
23312
23403
  });
23313
- if (disposition.action === "store" && currentSessionId) {
23314
- storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage, disposition.resumeUuid);
23315
- commitSessionTurn();
23316
- } else if (disposition.action === "evict") {
23404
+ if (disposition.action === "evict") {
23317
23405
  evictSession(profileSessionId, profileScopedCwd, body.messages || []);
23318
23406
  }
23319
23407
  claudeLog("passthrough.client_abort_settled", { action: disposition.action });
23320
23408
  return;
23321
23409
  }
23410
+ if (passthrough && streamedToolUseIds.size > 0 && !sawCanonicalResult) {
23411
+ evictSession(profileSessionId, profileScopedCwd, body.messages || []);
23412
+ claudeLog("passthrough.noncanonical_session_evicted", { mode: "stream", reason: "drain_error" });
23413
+ }
23322
23414
  const stderrOutput = stderrLines.join(`
23323
23415
  `).trim();
23324
23416
  if (stderrOutput && error instanceof Error && !error.message.includes(stderrOutput)) {
@@ -23346,7 +23438,7 @@ Subprocess stderr: ${stderrOutput}`;
23346
23438
  reason: sdkTerm.reason,
23347
23439
  passthrough,
23348
23440
  capturedToolUses: capturedToolUses.length,
23349
- abortIsOurs: sawDuplicateToolUse || earlyStopFired
23441
+ abortIsOurs: sawDuplicateToolUse
23350
23442
  }) && messageStartEmitted;
23351
23443
  if (canRecoverAsToolUse) {
23352
23444
  diagnosticLog2.session(`${requestMeta.requestId} sdk_termination_recovered ${formatSdkTermination(sdkTerm, {
@@ -23529,6 +23621,10 @@ data: ${JSON.stringify({
23529
23621
  cancel(reason) {
23530
23622
  requestAbort.abort(reason);
23531
23623
  requestAbort.detach();
23624
+ if (!isIndependentSession) {
23625
+ evictSession(profileSessionId, profileScopedCwd, body.messages || []);
23626
+ claudeLog("passthrough.client_abort_settled", { action: "evict", source: "stream_cancel" });
23627
+ }
23532
23628
  }
23533
23629
  });
23534
23630
  const streamSessionId = resumeSessionId || `session_${Date.now()}`;