@rynfar/meridian 1.62.3 → 1.62.5

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.
@@ -153,30 +153,44 @@ function parseAgentDescriptions(taskDescription) {
153
153
  }
154
154
  return agents;
155
155
  }
156
- function buildAgentDefinitions(taskDescription, mcpToolNames) {
156
+ function mapModelTier(model) {
157
+ if (!model)
158
+ return "inherit";
159
+ const lower = model.toLowerCase();
160
+ if (lower.includes("opus"))
161
+ return "opus";
162
+ if (lower.includes("fable") || lower.includes("mythos"))
163
+ return "fable";
164
+ if (lower.includes("haiku"))
165
+ return "haiku";
166
+ if (lower.includes("sonnet"))
167
+ return "sonnet";
168
+ return "inherit";
169
+ }
170
+ function buildAgentDefinitions(taskDescription, mcpToolNames, modelTier = "inherit") {
157
171
  const descriptions = parseAgentDescriptions(taskDescription);
158
172
  const agents = {};
159
173
  for (const [name, description] of descriptions) {
160
174
  agents[name] = {
161
175
  description,
162
176
  prompt: buildAgentPrompt(name, description),
163
- model: "inherit",
177
+ model: modelTier,
164
178
  ...mcpToolNames?.length ? { tools: [...mcpToolNames] } : {}
165
179
  };
166
180
  }
167
181
  if (descriptions.size > 0) {
168
- ensureDefaultAgents(agents, mcpToolNames);
182
+ ensureDefaultAgents(agents, mcpToolNames, modelTier);
169
183
  addCaseVariants(agents);
170
184
  }
171
185
  return agents;
172
186
  }
173
- function ensureDefaultAgents(agents, mcpToolNames) {
187
+ function ensureDefaultAgents(agents, mcpToolNames, modelTier) {
174
188
  for (const [name, description] of Object.entries(DEFAULT_AGENT_TYPES)) {
175
189
  if (!agents[name]) {
176
190
  agents[name] = {
177
191
  description,
178
192
  prompt: buildAgentPrompt(name, description),
179
- model: "inherit",
193
+ model: modelTier,
180
194
  ...mcpToolNames?.length ? { tools: [...mcpToolNames] } : {}
181
195
  };
182
196
  }
@@ -223,10 +237,10 @@ function parseAgentNamesFromSchema(taskTool) {
223
237
  return [];
224
238
  return enumNames.filter((n) => typeof n === "string");
225
239
  }
226
- function buildAgentDefinitionsFromTool(taskTool, mcpToolNames) {
240
+ function buildAgentDefinitionsFromTool(taskTool, mcpToolNames, modelTier = "inherit") {
227
241
  const rawDescription = getNested(taskTool, "description");
228
242
  const description = typeof rawDescription === "string" ? rawDescription : "";
229
- const fromDescription = buildAgentDefinitions(description, mcpToolNames);
243
+ const fromDescription = buildAgentDefinitions(description, mcpToolNames, modelTier);
230
244
  if (Object.keys(fromDescription).length > 0)
231
245
  return fromDescription;
232
246
  const names = parseAgentNamesFromSchema(taskTool);
@@ -240,11 +254,11 @@ function buildAgentDefinitionsFromTool(taskTool, mcpToolNames) {
240
254
  agents[name] = {
241
255
  description: desc,
242
256
  prompt: buildAgentPrompt(name, desc),
243
- model: "inherit",
257
+ model: modelTier,
244
258
  ...mcpToolNames?.length ? { tools: [...mcpToolNames] } : {}
245
259
  };
246
260
  }
247
- ensureDefaultAgents(agents, mcpToolNames);
261
+ ensureDefaultAgents(agents, mcpToolNames, modelTier);
248
262
  addCaseVariants(agents);
249
263
  return agents;
250
264
  }
@@ -1974,13 +1988,6 @@ ${history}
1974
1988
 
1975
1989
  ` + last.text;
1976
1990
  }
1977
- function framePassthroughContinuation(delta) {
1978
- if (!delta)
1979
- return delta;
1980
- return `${PASSTHROUGH_CONTINUATION_LEAD_IN}
1981
-
1982
- ${delta}`;
1983
- }
1984
1991
  function stripNonStandardStreamFields(event) {
1985
1992
  if (event && typeof event === "object") {
1986
1993
  const e = event;
@@ -2119,7 +2126,7 @@ function extractSystemText(system) {
2119
2126
  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
2127
  `);
2121
2128
  }
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;
2129
+ 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
2130
  var init_messages = __esm(() => {
2124
2131
  HASH_IGNORED_BLOCK_TYPES = new Set(["thinking", "redacted_thinking"]);
2125
2132
  HASH_HANDLED_BLOCK_TYPES = new Set(["text", "tool_use", "tool_result"]);
@@ -2136,7 +2143,6 @@ var init_messages = __esm(() => {
2136
2143
  "tool_search_tool_result",
2137
2144
  "container_upload"
2138
2145
  ]);
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
2146
  MULTIMODAL_TYPES = new Set(["image", "document", "file"]);
2141
2147
  TOOL_TARGET_KEYS = ["filePath", "file_path", "path", "command", "code", "pattern", "query", "url"];
2142
2148
  TRANSPORT_HEADER_BLOCK = /^\s*x-anthropic-[a-z0-9-]*header\s*:/i;
@@ -2199,7 +2205,7 @@ var init_opencode = __esm(() => {
2199
2205
  if (Array.isArray(body.tools)) {
2200
2206
  const taskTool = body.tools.find((t) => t.name === "task" || t.name === "Task");
2201
2207
  if (taskTool) {
2202
- sdkAgents = buildAgentDefinitionsFromTool(taskTool, [...allowedMcpTools]);
2208
+ sdkAgents = buildAgentDefinitionsFromTool(taskTool, [...allowedMcpTools], mapModelTier(body.model));
2203
2209
  }
2204
2210
  }
2205
2211
  let sdkHooks = undefined;
@@ -2278,6 +2284,9 @@ var init_opencode2 = __esm(() => {
2278
2284
  getSessionId(c) {
2279
2285
  return c.req.header("x-opencode-session") ?? c.req.header("x-session-affinity");
2280
2286
  },
2287
+ getAgentMode(c) {
2288
+ return c.req.header("x-opencode-agent-mode");
2289
+ },
2281
2290
  extractWorkingDirectory(body) {
2282
2291
  return extractClientCwd(body);
2283
2292
  },
@@ -2314,7 +2323,7 @@ var init_opencode2 = __esm(() => {
2314
2323
  const taskTool = body.tools.find((t) => t.name === "task" || t.name === "Task");
2315
2324
  if (!taskTool)
2316
2325
  return {};
2317
- return buildAgentDefinitionsFromTool(taskTool, [...mcpToolNames]);
2326
+ return buildAgentDefinitionsFromTool(taskTool, [...mcpToolNames], mapModelTier(body.model));
2318
2327
  },
2319
2328
  buildSdkHooks(body, sdkAgents) {
2320
2329
  const validAgentNames = Object.keys(sdkAgents);
@@ -11111,6 +11120,17 @@ function noteAssistantContent(tracker, content) {
11111
11120
  }
11112
11121
  }
11113
11122
  }
11123
+ function noteAssistantMessage(tracker, message) {
11124
+ const m = message;
11125
+ if (m?.type !== "assistant")
11126
+ return;
11127
+ const content = m.message?.content;
11128
+ const before = tracker.expected.size;
11129
+ noteAssistantContent(tracker, content);
11130
+ if (tracker.expected.size > before) {
11131
+ tracker.toolCallAssistantUuid = typeof m.uuid === "string" && m.uuid.length > 0 ? m.uuid : undefined;
11132
+ }
11133
+ }
11114
11134
  function noteUserContent(tracker, content) {
11115
11135
  if (!Array.isArray(content))
11116
11136
  return;
@@ -11121,43 +11141,69 @@ function noteUserContent(tracker, content) {
11121
11141
  }
11122
11142
  }
11123
11143
  }
11124
- function shouldEarlyStop(tracker) {
11125
- if (tracker.fired)
11126
- return false;
11144
+ function allForwardedCallsResolved(tracker) {
11127
11145
  if (tracker.expected.size === 0)
11128
11146
  return false;
11129
11147
  for (const id of tracker.expected) {
11130
11148
  if (!tracker.resolved.has(id))
11131
11149
  return false;
11132
11150
  }
11151
+ return true;
11152
+ }
11153
+ function isCompleteToolResultContinuation(messages, expectedIds) {
11154
+ if (expectedIds.length === 0 || messages.length === 0)
11155
+ return false;
11156
+ const expected = new Set(expectedIds);
11157
+ const actual = new Set;
11158
+ const echoedCalls = new Set;
11159
+ let sawUser = false;
11160
+ let sawNonToolResult = false;
11161
+ for (const message of messages) {
11162
+ if (message.role === "assistant" && !sawUser) {
11163
+ if (Array.isArray(message.content)) {
11164
+ for (const rawBlock of message.content) {
11165
+ const block = rawBlock;
11166
+ if (block?.type !== "tool_use")
11167
+ continue;
11168
+ if (typeof block.id !== "string" || !expected.has(block.id) || echoedCalls.has(block.id))
11169
+ return false;
11170
+ echoedCalls.add(block.id);
11171
+ }
11172
+ }
11173
+ continue;
11174
+ }
11175
+ if (message.role !== "user" || !Array.isArray(message.content) || sawUser)
11176
+ return false;
11177
+ sawUser = true;
11178
+ for (const rawBlock of message.content) {
11179
+ const block = rawBlock;
11180
+ if (block?.type === "tool_result") {
11181
+ if (sawNonToolResult || typeof block.tool_use_id !== "string")
11182
+ return false;
11183
+ if (!expected.has(block.tool_use_id) || actual.has(block.tool_use_id))
11184
+ return false;
11185
+ actual.add(block.tool_use_id);
11186
+ } else {
11187
+ sawNonToolResult = true;
11188
+ }
11189
+ }
11190
+ }
11191
+ return actual.size === expected.size && (echoedCalls.size === 0 || echoedCalls.size === expected.size);
11192
+ }
11193
+ function settledToolCallAssistantUuid(tracker) {
11194
+ return allForwardedCallsResolved(tracker) ? tracker.toolCallAssistantUuid : undefined;
11195
+ }
11196
+ function shouldEarlyStop(tracker) {
11197
+ if (tracker.fired || !settledToolCallAssistantUuid(tracker))
11198
+ return false;
11133
11199
  tracker.fired = true;
11134
11200
  return true;
11135
11201
  }
11136
11202
  function clientAbortDisposition(input) {
11137
11203
  if (input.isIndependentSession || !input.profileSessionId)
11138
11204
  return { action: "none" };
11139
- if (!input.passthrough)
11140
- return { action: "evict" };
11141
- if (input.currentSessionId && !input.sawDuplicateToolUse && input.resumeBoundaryUuid) {
11142
- return { action: "store", resumeUuid: input.resumeBoundaryUuid };
11143
- }
11144
11205
  return { action: "evict" };
11145
11206
  }
11146
- function resumeBoundaryUuid(message) {
11147
- const m = message;
11148
- if (m?.type !== "user")
11149
- return;
11150
- if (typeof m.uuid !== "string" || m.uuid.length === 0)
11151
- return;
11152
- const content = m.message?.content;
11153
- if (!Array.isArray(content))
11154
- return;
11155
- const hasResult = content.some((block) => {
11156
- const b = block;
11157
- return b?.type === "tool_result";
11158
- });
11159
- return hasResult ? m.uuid : undefined;
11160
- }
11161
11207
 
11162
11208
  // src/proxy/envelopeIntegrity.ts
11163
11209
  function checkEmptyToolInputs(contentBlocks, tools) {
@@ -20032,6 +20078,13 @@ function normalizeContextUsage(usage) {
20032
20078
  return lastIteration ?? usage;
20033
20079
  }
20034
20080
  var MIN_SUFFIX_FOR_COMPACTION = 2;
20081
+ function withClientAssistantUuid(existing, clientMessageCount, uuid) {
20082
+ const next = existing.slice(0, clientMessageCount + 1);
20083
+ while (next.length < clientMessageCount)
20084
+ next.push(null);
20085
+ next[clientMessageCount] = typeof uuid === "string" && uuid.length > 0 ? uuid : null;
20086
+ return next;
20087
+ }
20035
20088
  function computeLineageHash(messages) {
20036
20089
  if (!messages || messages.length === 0)
20037
20090
  return "";
@@ -20347,15 +20400,20 @@ function writeStore(store) {
20347
20400
  }
20348
20401
  }
20349
20402
  }
20403
+ function hasLegacyUserDenialBoundary(session) {
20404
+ const legacy = session.passthroughResumeUuid;
20405
+ return typeof legacy === "string" && legacy.length > 0 && !session.passthroughToolCallAssistantUuid;
20406
+ }
20350
20407
  function lookupSharedSession(key) {
20351
20408
  const store = readStore();
20352
- return store[key];
20409
+ const session = store[key];
20410
+ return session && !hasLegacyUserDenialBoundary(session) ? session : undefined;
20353
20411
  }
20354
20412
  function lookupSharedSessionByClaudeId(claudeSessionId) {
20355
20413
  const sessions = Object.values(readStore());
20356
20414
  let newest;
20357
20415
  for (const session of sessions) {
20358
- if (session.claudeSessionId !== claudeSessionId)
20416
+ if (session.claudeSessionId !== claudeSessionId || hasLegacyUserDenialBoundary(session))
20359
20417
  continue;
20360
20418
  if (!newest || session.lastUsedAt > newest.lastUsedAt) {
20361
20419
  newest = session;
@@ -20363,7 +20421,7 @@ function lookupSharedSessionByClaudeId(claudeSessionId) {
20363
20421
  }
20364
20422
  return newest;
20365
20423
  }
20366
- function storeSharedSession(key, claudeSessionId, messageCount, lineageHash, messageHashes, sdkMessageUuids, contextUsage, messageBlockHashes, passthroughResumeUuid) {
20424
+ function storeSharedSession(key, claudeSessionId, messageCount, lineageHash, messageHashes, sdkMessageUuids, contextUsage, messageBlockHashes, passthroughToolCallAssistantUuid, passthroughToolCallIds) {
20367
20425
  const path3 = getStorePath();
20368
20426
  const lockPath = `${path3}.lock`;
20369
20427
  const hasLock = skipLocking ? false : acquireLock(lockPath);
@@ -20383,7 +20441,8 @@ function storeSharedSession(key, claudeSessionId, messageCount, lineageHash, mes
20383
20441
  messageHashes: messageHashes ?? existing?.messageHashes,
20384
20442
  messageBlockHashes: messageBlockHashes ?? existing?.messageBlockHashes,
20385
20443
  sdkMessageUuids: sdkMessageUuids ?? existing?.sdkMessageUuids,
20386
- passthroughResumeUuid: passthroughResumeUuid === undefined ? existing?.passthroughResumeUuid : passthroughResumeUuid ?? undefined,
20444
+ passthroughToolCallAssistantUuid: passthroughToolCallAssistantUuid === undefined ? existing?.passthroughToolCallAssistantUuid : passthroughToolCallAssistantUuid ?? undefined,
20445
+ passthroughToolCallIds: passthroughToolCallIds === undefined ? existing?.passthroughToolCallIds : passthroughToolCallIds ?? undefined,
20387
20446
  contextUsage: contextUsage ?? existing?.contextUsage,
20388
20447
  ...previousClaudeSessionId ? { previousClaudeSessionId } : {}
20389
20448
  };
@@ -20581,7 +20640,8 @@ function lookupSession(sessionId, messages, workingDirectory) {
20581
20640
  messageHashes: shared.messageHashes,
20582
20641
  messageBlockHashes: shared.messageBlockHashes,
20583
20642
  sdkMessageUuids: shared.sdkMessageUuids,
20584
- passthroughResumeUuid: shared.passthroughResumeUuid,
20643
+ passthroughToolCallAssistantUuid: shared.passthroughToolCallAssistantUuid,
20644
+ passthroughToolCallIds: shared.passthroughToolCallIds,
20585
20645
  contextUsage: shared.contextUsage
20586
20646
  };
20587
20647
  const result = classifyLineage(state, messages, sessionId);
@@ -20611,7 +20671,8 @@ function lookupSession(sessionId, messages, workingDirectory) {
20611
20671
  messageHashes: shared.messageHashes,
20612
20672
  messageBlockHashes: shared.messageBlockHashes,
20613
20673
  sdkMessageUuids: shared.sdkMessageUuids,
20614
- passthroughResumeUuid: shared.passthroughResumeUuid,
20674
+ passthroughToolCallAssistantUuid: shared.passthroughToolCallAssistantUuid,
20675
+ passthroughToolCallIds: shared.passthroughToolCallIds,
20615
20676
  contextUsage: shared.contextUsage
20616
20677
  };
20617
20678
  const result = classifyLineage(state, messages, fp);
@@ -20646,13 +20707,14 @@ function getSessionByClaudeId(claudeSessionId) {
20646
20707
  messageHashes: shared.messageHashes,
20647
20708
  messageBlockHashes: shared.messageBlockHashes,
20648
20709
  sdkMessageUuids: shared.sdkMessageUuids,
20649
- passthroughResumeUuid: shared.passthroughResumeUuid,
20710
+ passthroughToolCallAssistantUuid: shared.passthroughToolCallAssistantUuid,
20711
+ passthroughToolCallIds: shared.passthroughToolCallIds,
20650
20712
  contextUsage: shared.contextUsage
20651
20713
  });
20652
20714
  }
20653
20715
  return newest;
20654
20716
  }
20655
- function storeSession(sessionId, messages, claudeSessionId, workingDirectory, sdkMessageUuids, contextUsage, passthroughResumeUuid) {
20717
+ function storeSession(sessionId, messages, claudeSessionId, workingDirectory, sdkMessageUuids, contextUsage, passthroughToolCallAssistantUuid, passthroughToolCallIds) {
20656
20718
  if (!claudeSessionId)
20657
20719
  return;
20658
20720
  const lineageHash = computeLineageHash(messages);
@@ -20666,7 +20728,8 @@ function storeSession(sessionId, messages, claudeSessionId, workingDirectory, sd
20666
20728
  messageHashes,
20667
20729
  messageBlockHashes,
20668
20730
  sdkMessageUuids,
20669
- ...passthroughResumeUuid ? { passthroughResumeUuid } : {},
20731
+ ...passthroughToolCallAssistantUuid ? { passthroughToolCallAssistantUuid } : {},
20732
+ ...passthroughToolCallIds ? { passthroughToolCallIds } : {},
20670
20733
  ...contextUsage ? { contextUsage } : {}
20671
20734
  };
20672
20735
  if (sessionId)
@@ -20676,7 +20739,7 @@ function storeSession(sessionId, messages, claudeSessionId, workingDirectory, sd
20676
20739
  fingerprintCache.set(fp, state);
20677
20740
  const key = sessionId || fp;
20678
20741
  if (key) {
20679
- storeSharedSession(key, claudeSessionId, state.messageCount, lineageHash, messageHashes, sdkMessageUuids, contextUsage, messageBlockHashes, passthroughResumeUuid ?? null);
20742
+ storeSharedSession(key, claudeSessionId, state.messageCount, lineageHash, messageHashes, sdkMessageUuids, contextUsage, messageBlockHashes, passthroughToolCallAssistantUuid ?? null, passthroughToolCallIds ?? null);
20680
20743
  }
20681
20744
  }
20682
20745
 
@@ -20836,21 +20899,21 @@ function stripCacheControlDeep(content) {
20836
20899
  return rest;
20837
20900
  });
20838
20901
  }
20839
- function normalizeStructuredUserContent(content) {
20902
+ function normalizeStructuredUserContent(content, preserveToolResultWrapper = false) {
20840
20903
  if (!Array.isArray(content))
20841
20904
  return content;
20842
20905
  const normalized = [];
20843
20906
  for (const block of content) {
20844
20907
  if (!block || typeof block !== "object")
20845
20908
  continue;
20846
- if (block.type === "tool_result" && Array.isArray(block.content) && hasMultimodalContent(block.content)) {
20909
+ if (!preserveToolResultWrapper && block.type === "tool_result" && Array.isArray(block.content) && hasMultimodalContent(block.content)) {
20847
20910
  normalized.push(...normalizeStructuredUserContent(block.content));
20848
20911
  continue;
20849
20912
  }
20850
20913
  if (block.type === "tool_result" && Array.isArray(block.content)) {
20851
20914
  normalized.push({
20852
20915
  ...block,
20853
- content: normalizeStructuredUserContent(block.content)
20916
+ content: normalizeStructuredUserContent(block.content, preserveToolResultWrapper)
20854
20917
  });
20855
20918
  continue;
20856
20919
  }
@@ -21294,8 +21357,10 @@ data: ${JSON.stringify(lastError)}
21294
21357
  }
21295
21358
  const profile = resolveProfile(finalConfig.profiles, finalConfig.defaultProfile, options.forcedProfileId || c.req.header("x-meridian-profile") || undefined, routingMode === "sticky" ? { routingMode, stickySessionKey: adapter.getSessionId(c, body) } : undefined);
21296
21359
  const authStatus = await getClaudeAuthStatusAsync(profile.id !== "default" ? profile.id : undefined, Object.keys(profile.env).length > 0 ? profile.env : undefined);
21297
- const agentMode = c.req.header("x-opencode-agent-mode") ?? null;
21298
21360
  const requestSource = c.req.header("x-meridian-source")?.slice(0, 64) || undefined;
21361
+ const declaredAgentMode = adapter.getAgentMode?.(c, body) ?? c.req.header("x-opencode-agent-mode") ?? null;
21362
+ const isSubagentRequest = declaredAgentMode === "subagent" || requestSource?.startsWith("subagent-") === true;
21363
+ const agentMode = isSubagentRequest ? "subagent" : declaredAgentMode;
21299
21364
  const requestedModel = typeof body.model === "string" ? body.model : "sonnet";
21300
21365
  let model = mapModelToClaudeModel(requestedModel, authStatus?.subscriptionType, agentMode);
21301
21366
  const envOverrides = explicitModelPin(requestedModel);
@@ -21389,12 +21454,12 @@ data: ${JSON.stringify(lastError)}
21389
21454
  const lastMessage = Array.isArray(body.messages) ? body.messages[body.messages.length - 1] : undefined;
21390
21455
  const lastIsToolResult = Array.isArray(lastMessage?.content) && lastMessage.content.some((b) => b?.type === "tool_result");
21391
21456
  const isClientDrivenLoop = adapterBase !== "claude-code" && !agentSessionId && lastIsToolResult;
21392
- const isIndependentSession = !agentSessionId && (requestSource?.startsWith("fork-") || requestSource?.startsWith("subagent-")) || isClientDrivenLoop || false;
21457
+ const isIndependentSession = !agentSessionId && (requestSource?.startsWith("fork-") || isSubagentRequest) || isClientDrivenLoop || false;
21393
21458
  let lineageResult = isIndependentSession ? { type: "diverged", reason: "independent-request" } : lookupSession(profileSessionId, body.messages || [], profileScopedCwd);
21394
21459
  if (lineageResult.type === "undo" && adapterBase === "opencode" && !agentSessionId) {
21395
21460
  lineageResult = { type: "diverged", reason: "missing-session-header" };
21396
21461
  }
21397
- const declaresConcurrentFlow = requestSource?.startsWith("fork-") === true || requestSource?.startsWith("subagent-") === true;
21462
+ const declaresConcurrentFlow = requestSource?.startsWith("fork-") === true || isSubagentRequest;
21398
21463
  if (profileSessionId && !declaresConcurrentFlow && requestMeta.sessionTurnLease?.advancedWhileWaiting(profileSessionId) && lineageResult.type !== "continuation" && lineageResult.type !== "compaction") {
21399
21464
  const reason = lineageResult.type === "diverged" ? lineageResult.reason : lineageResult.type;
21400
21465
  const message = "This session advanced while the request was waiting. Retry with the latest conversation history or use a distinct session ID.";
@@ -21457,15 +21522,16 @@ data: ${JSON.stringify(lastError)}
21457
21522
  } : undefined
21458
21523
  }, adapterBase);
21459
21524
  }
21460
- const isResume = lineageResult.type === "continuation" || lineageResult.type === "compaction";
21525
+ let isResume = lineageResult.type === "continuation" || lineageResult.type === "compaction";
21461
21526
  const isUndo = lineageResult.type === "undo";
21462
21527
  const cachedSession = lineageResult.type !== "diverged" ? lineageResult.session : undefined;
21463
- const resumeSessionId = cachedSession?.claudeSessionId;
21528
+ let resumeSessionId = cachedSession?.claudeSessionId;
21464
21529
  const passthrough = adapter.instancePassthrough !== undefined ? adapter.instancePassthrough : pipelineCtx.passthrough !== undefined ? pipelineCtx.passthrough : envBool("PASSTHROUGH");
21465
21530
  const resumeFrom = lineageResult.type === "continuation" || lineageResult.type === "compaction" ? lineageResult.resumeFrom : undefined;
21466
21531
  const resumeContentFrom = lineageResult.type === "continuation" ? lineageResult.resumeContentFrom : undefined;
21467
21532
  const undoRollbackUuid = isUndo && lineageResult.type === "undo" ? lineageResult.rollbackUuid : undefined;
21468
- const passthroughResumeUuid = passthrough && isResume ? cachedSession?.passthroughResumeUuid : undefined;
21533
+ let passthroughToolCallAssistantUuid = passthrough && isResume ? cachedSession?.passthroughToolCallAssistantUuid : undefined;
21534
+ const passthroughToolCallIds = passthrough && isResume ? cachedSession?.passthroughToolCallIds : undefined;
21469
21535
  const msgSummary = body.messages?.map((m) => {
21470
21536
  const contentTypes = Array.isArray(m.content) ? m.content.map((b) => b.type).join(",") : "string";
21471
21537
  return `${m.role}[${contentTypes}]`;
@@ -21531,17 +21597,28 @@ data: ${JSON.stringify(lastError)}
21531
21597
  } else {
21532
21598
  messagesToConvert = allMessages;
21533
21599
  }
21600
+ if (passthroughToolCallAssistantUuid && !isCompleteToolResultContinuation(messagesToConvert, passthroughToolCallIds ?? [])) {
21601
+ claudeLog("passthrough.checkpoint_replay", {
21602
+ expectedToolIds: passthroughToolCallIds?.length ?? 0,
21603
+ reason: "incomplete_or_mismatched_results"
21604
+ });
21605
+ isResume = false;
21606
+ resumeSessionId = undefined;
21607
+ passthroughToolCallAssistantUuid = undefined;
21608
+ messagesToConvert = allMessages;
21609
+ }
21534
21610
  const hasMultimodal = messagesToConvert?.some((m) => hasMultimodalContent(m.content));
21611
+ const hasPassthroughToolResults = Boolean(passthroughToolCallAssistantUuid) && messagesToConvert?.some((m) => m.role === "user" && Array.isArray(m.content) && m.content.some((block) => block?.type === "tool_result"));
21535
21612
  let structuredMessages;
21536
21613
  let textPrompt;
21537
- if (hasMultimodal) {
21614
+ if (hasMultimodal || hasPassthroughToolResults) {
21538
21615
  structuredMessages = [];
21539
21616
  if (isResume) {
21540
21617
  for (const m of messagesToConvert) {
21541
21618
  if (m.role === "user") {
21542
21619
  structuredMessages.push({
21543
21620
  type: "user",
21544
- message: { role: "user", content: normalizeStructuredUserContent(stripCacheControlDeep(m.content)) },
21621
+ message: { role: "user", content: normalizeStructuredUserContent(stripCacheControlDeep(m.content), Boolean(passthroughToolCallAssistantUuid)) },
21545
21622
  parent_tool_use_id: null
21546
21623
  });
21547
21624
  }
@@ -21551,7 +21628,7 @@ data: ${JSON.stringify(lastError)}
21551
21628
  if (m.role === "user") {
21552
21629
  structuredMessages.push({
21553
21630
  type: "user",
21554
- message: { role: "user", content: normalizeStructuredUserContent(stripCacheControlDeep(m.content)) },
21631
+ message: { role: "user", content: normalizeStructuredUserContent(stripCacheControlDeep(m.content), Boolean(passthroughToolCallAssistantUuid)) },
21555
21632
  parent_tool_use_id: null
21556
21633
  });
21557
21634
  } else {
@@ -21569,13 +21646,6 @@ data: ${JSON.stringify(lastError)}
21569
21646
  if (structuredMessages.length > 1) {
21570
21647
  structuredMessages = consolidateMultimodalOntoLastUser(structuredMessages);
21571
21648
  }
21572
- if (passthroughResumeUuid && structuredMessages.length > 0) {
21573
- structuredMessages.unshift({
21574
- type: "user",
21575
- message: { role: "user", content: PASSTHROUGH_CONTINUATION_LEAD_IN },
21576
- parent_tool_use_id: null
21577
- });
21578
- }
21579
21649
  } else {
21580
21650
  const toolIndex = buildToolUseIndex(allMessages ?? messagesToConvert ?? []);
21581
21651
  const promptTurns = (messagesToConvert ?? []).map((m) => {
@@ -21590,7 +21660,7 @@ data: ${JSON.stringify(lastError)}
21590
21660
  const resumeDelta = promptTurns.map((t) => t.text).filter(Boolean).join(`
21591
21661
 
21592
21662
  `) || "";
21593
- textPrompt = isResume ? passthroughResumeUuid ? framePassthroughContinuation(resumeDelta) : resumeDelta : frameReplayTurns(promptTurns);
21663
+ textPrompt = isResume ? resumeDelta : frameReplayTurns(promptTurns);
21594
21664
  }
21595
21665
  const settingSources = envBool("LOAD_CONTEXT") || sdkFeatures.claudeMd === "full" ? ["user", "project"] : sdkFeatures.claudeMd === "project" ? ["project"] : pipelineCtx.settingSources ?? [];
21596
21666
  const capturedToolUses = [];
@@ -21693,11 +21763,15 @@ data: ${JSON.stringify(lastError)}
21693
21763
  }
21694
21764
  const signature = toolUseSignature(toolName, toolInput);
21695
21765
  const isExactDuplicate = capturedSignatures.has(signature);
21766
+ const isPostCheckpointCall = earlyStopFired;
21696
21767
  const isSameToolRepeat = !earlyStopEnabled && !isExactDuplicate && capturedToolNames.has(toolName);
21697
21768
  const exceedsForcedSingle = forceSingleToolUse && capturedToolUses.length >= 1;
21698
- if (isExactDuplicate) {
21769
+ if (isExactDuplicate || isPostCheckpointCall) {
21699
21770
  droppedToolUseIds.add(input.tool_use_id);
21700
- claudeLog("passthrough.duplicate_tool_use_dropped", { name: toolName });
21771
+ claudeLog("passthrough.duplicate_tool_use_dropped", {
21772
+ name: toolName,
21773
+ reason: isPostCheckpointCall ? "hidden_digest" : "exact_duplicate"
21774
+ });
21701
21775
  } else if (isSameToolRepeat || exceedsForcedSingle) {
21702
21776
  droppedToolUseIds.add(input.tool_use_id);
21703
21777
  sawDuplicateToolUse = true;
@@ -21718,10 +21792,10 @@ data: ${JSON.stringify(lastError)}
21718
21792
  if (earlyStopEnabled && turnGenerating && !requestAbort.controller.signal.aborted) {
21719
21793
  await holdDenyUntilTurnEnd();
21720
21794
  }
21721
- if (isExactDuplicate) {
21795
+ if (isExactDuplicate || isPostCheckpointCall) {
21722
21796
  return {
21723
21797
  decision: "block",
21724
- 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."
21798
+ 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."
21725
21799
  };
21726
21800
  }
21727
21801
  if (isSameToolRepeat || exceedsForcedSingle) {
@@ -21753,13 +21827,15 @@ data: ${JSON.stringify(lastError)}
21753
21827
  const upstreamStartAt = Date.now();
21754
21828
  let firstChunkAt;
21755
21829
  let currentSessionId;
21756
- const sdkUuidMap = cachedSession?.sdkMessageUuids ? [...cachedSession.sdkMessageUuids] : [];
21830
+ let sdkUuidMap = (isResume || isUndo) && cachedSession?.sdkMessageUuids ? [...cachedSession.sdkMessageUuids] : [];
21757
21831
  while (sdkUuidMap.length < allMessages.length)
21758
21832
  sdkUuidMap.push(null);
21759
21833
  claudeLog("upstream.start", { mode: "non_stream", model });
21760
21834
  let lastUsage;
21761
21835
  let lastStopReason;
21762
- let nextPassthroughResumeUuid;
21836
+ let nextPassthroughToolCallAssistantUuid;
21837
+ let nextPassthroughToolCallIds;
21838
+ let sawCanonicalResult = false;
21763
21839
  try {
21764
21840
  if (!claudeExecutable) {
21765
21841
  claudeExecutable = await resolveClaudeExecutableAsync();
@@ -21797,8 +21873,8 @@ data: ${JSON.stringify(lastError)}
21797
21873
  hasDeferredTools,
21798
21874
  resumeSessionId,
21799
21875
  isUndo,
21800
- resumeSessionAtUuid: undoRollbackUuid ?? passthroughResumeUuid,
21801
- forkSession: busySessionFork || Boolean(passthroughResumeUuid) || undefined,
21876
+ resumeSessionAtUuid: undoRollbackUuid ?? passthroughToolCallAssistantUuid,
21877
+ forkSession: busySessionFork || undefined,
21802
21878
  sdkHooks,
21803
21879
  blockedTools: pipelineCtx.blockedTools,
21804
21880
  incompatibleTools: pipelineCtx.incompatibleTools,
@@ -22031,25 +22107,33 @@ data: ${JSON.stringify(lastError)}
22031
22107
  claudeLog("passthrough.loop_break", { mode: "non_stream", assistantMessages, captured: capturedToolUses.length });
22032
22108
  break;
22033
22109
  }
22034
- if (earlyStopEnabled) {
22035
- if (message.type === "assistant") {
22036
- noteAssistantContent(earlyStop, message.message?.content);
22037
- } else if (message.type === "user") {
22038
- nextPassthroughResumeUuid = resumeBoundaryUuid(message) ?? nextPassthroughResumeUuid;
22039
- noteUserContent(earlyStop, message.message?.content);
22040
- if (shouldEarlyStop(earlyStop)) {
22041
- earlyStopFired = true;
22042
- claudeLog("passthrough.early_stop", { mode: "non_stream", captured: capturedToolUses.length });
22043
- requestAbort.abort("passthrough turn complete");
22044
- break;
22110
+ let assistantAddedForwardedCall = false;
22111
+ if (passthrough && message.type === "assistant" && !earlyStopFired && earlyStop.resolved.size === 0) {
22112
+ const expectedBefore = earlyStop.expected.size;
22113
+ noteAssistantMessage(earlyStop, message);
22114
+ assistantAddedForwardedCall = earlyStop.expected.size > expectedBefore;
22115
+ } else if (passthrough && message.type === "user" && !earlyStopFired) {
22116
+ noteUserContent(earlyStop, message.message?.content);
22117
+ if (earlyStopEnabled && shouldEarlyStop(earlyStop)) {
22118
+ nextPassthroughToolCallAssistantUuid = settledToolCallAssistantUuid(earlyStop);
22119
+ nextPassthroughToolCallIds = [...earlyStop.expected];
22120
+ earlyStopFired = true;
22121
+ for (let i = capturedToolUses.length - 1;i >= 0; i--) {
22122
+ if (!earlyStop.expected.has(capturedToolUses[i].id))
22123
+ capturedToolUses.splice(i, 1);
22045
22124
  }
22125
+ claudeLog("passthrough.checkpoint_ready", {
22126
+ mode: "non_stream",
22127
+ captured: capturedToolUses.length,
22128
+ toolCallAssistantUuid: nextPassthroughToolCallAssistantUuid
22129
+ });
22046
22130
  }
22047
22131
  }
22048
22132
  if (message.type === "assistant") {
22049
22133
  releaseHeldDenies("assistant_message");
22050
22134
  assistantMessages += 1;
22051
- if (message.uuid) {
22052
- sdkUuidMap.push(message.uuid);
22135
+ if (!passthrough || earlyStop.expected.size === 0 || assistantAddedForwardedCall) {
22136
+ sdkUuidMap = withClientAssistantUuid(sdkUuidMap, allMessages.length, message.uuid);
22053
22137
  }
22054
22138
  if (!firstChunkAt) {
22055
22139
  firstChunkAt = Date.now();
@@ -22093,11 +22177,12 @@ data: ${JSON.stringify(lastError)}
22093
22177
  const msgUsage = message.message.usage;
22094
22178
  if (msgUsage)
22095
22179
  lastUsage = { ...lastUsage, ...msgUsage };
22096
- if (typeof message.message.stop_reason === "string") {
22180
+ if (!isPassthroughTurn2 && typeof message.message.stop_reason === "string") {
22097
22181
  lastStopReason = message.message.stop_reason;
22098
22182
  }
22099
22183
  }
22100
22184
  if (message.type === "result") {
22185
+ sawCanonicalResult = true;
22101
22186
  const resultUsage = message.usage;
22102
22187
  if (resultUsage) {
22103
22188
  lastUsage = { ...lastUsage, ...resultUsage };
@@ -22129,6 +22214,10 @@ data: ${JSON.stringify(lastError)}
22129
22214
  }
22130
22215
  } catch (error) {
22131
22216
  releaseHeldDenies("non_stream_error");
22217
+ if (passthrough && capturedToolUses.length > 0 && !sawCanonicalResult) {
22218
+ evictSession(profileSessionId, profileScopedCwd, body.messages || []);
22219
+ claudeLog("passthrough.noncanonical_session_evicted", { mode: "non_stream", reason: "drain_error" });
22220
+ }
22132
22221
  const stderrOutput = stderrLines.join(`
22133
22222
  `).trim();
22134
22223
  if (stderrOutput && error instanceof Error && !error.message.includes(stderrOutput)) {
@@ -22283,8 +22372,14 @@ Subprocess stderr: ${stderrOutput}`;
22283
22372
  ]);
22284
22373
  }
22285
22374
  if (currentSessionId && !isIndependentSession && !sawDuplicateToolUse) {
22286
- storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage, earlyStopFired ? nextPassthroughResumeUuid : null);
22287
- commitSessionTurn();
22375
+ const checkpointTurn = passthrough && contentBlocks.some((b) => b.type === "tool_use");
22376
+ if (checkpointTurn && (!earlyStopFired || !sawCanonicalResult)) {
22377
+ evictSession(profileSessionId, profileScopedCwd, body.messages || []);
22378
+ claudeLog("passthrough.noncanonical_session_evicted", { mode: "non_stream" });
22379
+ } else {
22380
+ storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage, earlyStopFired ? nextPassthroughToolCallAssistantUuid : null, earlyStopFired ? nextPassthroughToolCallIds : null);
22381
+ commitSessionTurn();
22382
+ }
22288
22383
  }
22289
22384
  const responseSessionId = currentSessionId || resumeSessionId || `session_${Date.now()}`;
22290
22385
  return new Response(JSON.stringify({
@@ -22325,6 +22420,7 @@ Subprocess stderr: ${stderrOutput}`;
22325
22420
  let bytesSent = 0;
22326
22421
  let streamClosed = false;
22327
22422
  let awaitingEarlyStopDrain = false;
22423
+ let exitedBeforeCanonicalTerminal = false;
22328
22424
  claudeLog("upstream.start", { mode: "stream", model });
22329
22425
  const safeEnqueue = (payload, source) => {
22330
22426
  if (streamClosed)
@@ -22346,14 +22442,16 @@ Subprocess stderr: ${stderrOutput}`;
22346
22442
  throw error;
22347
22443
  }
22348
22444
  };
22349
- const sdkUuidMap = cachedSession?.sdkMessageUuids ? [...cachedSession.sdkMessageUuids] : [];
22445
+ let sdkUuidMap = (isResume || isUndo) && cachedSession?.sdkMessageUuids ? [...cachedSession.sdkMessageUuids] : [];
22350
22446
  while (sdkUuidMap.length < allMessages.length)
22351
22447
  sdkUuidMap.push(null);
22352
22448
  let messageStartEmitted = false;
22353
22449
  let lastUsage;
22354
22450
  let hasStructuredOutput = false;
22355
22451
  let structuredOutput;
22356
- let nextPassthroughResumeUuid;
22452
+ let nextPassthroughToolCallAssistantUuid;
22453
+ let nextPassthroughToolCallIds;
22454
+ let sawCanonicalResult = false;
22357
22455
  const silentTurnRecoveryEnabled = env("SILENT_TURN_RECOVERY") !== "0";
22358
22456
  let silentTurnRecoveryAttempted = false;
22359
22457
  let silentTurnRecovered = false;
@@ -22378,33 +22476,6 @@ data: ${JSON.stringify({
22378
22476
  eventsForwarded += 1;
22379
22477
  };
22380
22478
  const openClientBlocks = new Set;
22381
- let pendingEarlyStop = false;
22382
- let pendingEarlyStopAt = 0;
22383
- const fireEarlyStop = (reason) => {
22384
- earlyStopFired = true;
22385
- claudeLog("passthrough.early_stop", {
22386
- mode: "stream",
22387
- captured: capturedToolUses.length,
22388
- drained: awaitingEarlyStopDrain,
22389
- reason,
22390
- deferredMs: pendingEarlyStopAt ? Date.now() - pendingEarlyStopAt : 0
22391
- });
22392
- pendingEarlyStop = false;
22393
- flushOpenClientBlocks("early_stop");
22394
- sendTerminalDelta("tool_use");
22395
- safeEnqueue(encoder.encode(`event: message_stop
22396
- data: ${JSON.stringify({ type: "message_stop" })}
22397
-
22398
- `), "early_stop");
22399
- requestAbort.abort("passthrough turn complete");
22400
- awaitingEarlyStopDrain = false;
22401
- if (!streamClosed) {
22402
- streamClosed = true;
22403
- try {
22404
- controller.close();
22405
- } catch {}
22406
- }
22407
- };
22408
22479
  const flushOpenClientBlocks = (source) => {
22409
22480
  if (openClientBlocks.size === 0)
22410
22481
  return;
@@ -22455,8 +22526,8 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
22455
22526
  hasDeferredTools,
22456
22527
  resumeSessionId,
22457
22528
  isUndo,
22458
- resumeSessionAtUuid: undoRollbackUuid ?? passthroughResumeUuid,
22459
- forkSession: busySessionFork || Boolean(passthroughResumeUuid) || undefined,
22529
+ resumeSessionAtUuid: undoRollbackUuid ?? passthroughToolCallAssistantUuid,
22530
+ forkSession: busySessionFork || undefined,
22460
22531
  sdkHooks,
22461
22532
  blockedTools: pipelineCtx.blockedTools,
22462
22533
  incompatibleTools: pipelineCtx.incompatibleTools,
@@ -22716,38 +22787,42 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
22716
22787
  try {
22717
22788
  for await (const message of guardedResponse) {
22718
22789
  if (streamClosed && !awaitingEarlyStopDrain) {
22790
+ exitedBeforeCanonicalTerminal = true;
22719
22791
  break;
22720
22792
  }
22721
22793
  if (message.session_id) {
22722
22794
  currentSessionId = message.session_id;
22723
22795
  }
22724
- if (message.type === "assistant" && message.uuid) {
22725
- sdkUuidMap.push(message.uuid);
22796
+ let assistantAddedForwardedCall = false;
22797
+ if (earlyStopEnabled && message.type === "assistant" && !earlyStopFired) {
22798
+ const expectedBefore = earlyStop.expected.size;
22799
+ noteAssistantMessage(earlyStop, message);
22800
+ assistantAddedForwardedCall = earlyStop.expected.size > expectedBefore;
22801
+ } else if (earlyStopEnabled && message.type === "user" && !earlyStopFired) {
22802
+ noteUserContent(earlyStop, message.message?.content);
22726
22803
  }
22727
- nextPassthroughResumeUuid = resumeBoundaryUuid(message) ?? nextPassthroughResumeUuid;
22728
- if (earlyStopEnabled) {
22729
- if (message.type === "assistant") {
22730
- noteAssistantContent(earlyStop, message.message?.content);
22731
- } else if (message.type === "user") {
22732
- noteUserContent(earlyStop, message.message?.content);
22733
- if (shouldEarlyStop(earlyStop) && streamedToolUseIds.size > 0) {
22734
- if (openClientBlocks.size > 0) {
22735
- if (!pendingEarlyStop) {
22736
- pendingEarlyStop = true;
22737
- pendingEarlyStopAt = Date.now();
22738
- claudeLog("passthrough.early_stop_deferred", {
22739
- openBlocks: openClientBlocks.size,
22740
- captured: capturedToolUses.length
22741
- });
22742
- }
22743
- } else {
22744
- fireEarlyStop("immediate");
22745
- break;
22746
- }
22804
+ if (earlyStopEnabled && !earlyStopFired) {
22805
+ const hasCompleteStreamedSet = streamedToolUseIds.size > 0 && earlyStop.expected.size === streamedToolUseIds.size && [...streamedToolUseIds].every((id) => earlyStop.expected.has(id));
22806
+ if (!turnGenerating && openClientBlocks.size === 0 && hasCompleteStreamedSet && shouldEarlyStop(earlyStop)) {
22807
+ nextPassthroughToolCallAssistantUuid = settledToolCallAssistantUuid(earlyStop);
22808
+ nextPassthroughToolCallIds = [...earlyStop.expected];
22809
+ earlyStopFired = true;
22810
+ for (let i = capturedToolUses.length - 1;i >= 0; i--) {
22811
+ if (!earlyStop.expected.has(capturedToolUses[i].id))
22812
+ capturedToolUses.splice(i, 1);
22747
22813
  }
22814
+ claudeLog("passthrough.checkpoint_ready", {
22815
+ mode: "stream",
22816
+ captured: capturedToolUses.length,
22817
+ toolCallAssistantUuid: nextPassthroughToolCallAssistantUuid
22818
+ });
22748
22819
  }
22749
22820
  }
22821
+ if (message.type === "assistant" && (!passthrough || earlyStop.expected.size === 0 || assistantAddedForwardedCall)) {
22822
+ sdkUuidMap = withClientAssistantUuid(sdkUuidMap, allMessages.length, message.uuid);
22823
+ }
22750
22824
  if (message.type === "result") {
22825
+ sawCanonicalResult = true;
22751
22826
  const resultUsage = message.usage;
22752
22827
  if (resultUsage)
22753
22828
  lastUsage = { ...lastUsage, ...resultUsage };
@@ -22757,6 +22832,8 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
22757
22832
  }
22758
22833
  }
22759
22834
  if (message.type === "stream_event") {
22835
+ if (streamClosed && awaitingEarlyStopDrain)
22836
+ continue;
22760
22837
  streamEventsSeen += 1;
22761
22838
  if (!firstChunkAt) {
22762
22839
  firstChunkAt = Date.now();
@@ -22796,16 +22873,19 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
22796
22873
  lastUsage = { ...lastUsage, ...startUsage };
22797
22874
  if (messageStartEmitted) {
22798
22875
  if (passthrough && streamedToolUseIds.size > 0) {
22799
- flushOpenClientBlocks("turn2_suppression");
22800
- sendTerminalDelta("tool_use");
22801
- safeEnqueue(encoder.encode(`event: message_stop
22876
+ if (!streamClosed) {
22877
+ flushOpenClientBlocks("turn2_suppression");
22878
+ sendTerminalDelta("tool_use");
22879
+ safeEnqueue(encoder.encode(`event: message_stop
22802
22880
  data: ${JSON.stringify({ type: "message_stop" })}
22803
22881
 
22804
22882
  `), "passthrough_turn2_stop");
22883
+ streamClosed = true;
22884
+ controller.close();
22885
+ }
22886
+ awaitingEarlyStopDrain = true;
22805
22887
  claudeLog("passthrough.turn2_suppressed", { mode: "stream", toolUses: streamedToolUseIds.size });
22806
- streamClosed = true;
22807
- controller.close();
22808
- break;
22888
+ continue;
22809
22889
  }
22810
22890
  continue;
22811
22891
  }
@@ -22929,10 +23009,6 @@ data: ${JSON.stringify(event)}
22929
23009
  const idx = event.index;
22930
23010
  if (typeof idx === "number")
22931
23011
  openClientBlocks.delete(idx);
22932
- if (pendingEarlyStop && openClientBlocks.size === 0) {
22933
- fireEarlyStop("blocks_closed");
22934
- break;
22935
- }
22936
23012
  }
22937
23013
  if (passthrough && eventType === "message_delta" && event.delta?.stop_reason === "tool_use" && streamedToolUseIds.size > 0) {
22938
23014
  flushOpenClientBlocks("drain_close");
@@ -22947,6 +23023,7 @@ data: ${JSON.stringify({ type: "message_stop" })}
22947
23023
  awaitingEarlyStopDrain = true;
22948
23024
  continue;
22949
23025
  }
23026
+ exitedBeforeCanonicalTerminal = true;
22950
23027
  break;
22951
23028
  }
22952
23029
  if (eventType === "content_block_delta") {
@@ -23042,8 +23119,14 @@ data: ${JSON.stringify({
23042
23119
  plog(`[PROXY] ${requestMeta.requestId} discovered=${discoveredTools.size} (${newNames}) session_total=${allNames.length}`);
23043
23120
  }
23044
23121
  if (currentSessionId && !isIndependentSession && !sawDuplicateToolUse) {
23045
- storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage, earlyStopFired ? nextPassthroughResumeUuid : null);
23046
- commitSessionTurn();
23122
+ const checkpointTurn = passthrough && streamedToolUseIds.size > 0;
23123
+ if (exitedBeforeCanonicalTerminal || checkpointTurn && (!earlyStopFired || !sawCanonicalResult)) {
23124
+ evictSession(profileSessionId, profileScopedCwd, body.messages || []);
23125
+ claudeLog("passthrough.noncanonical_session_evicted", { mode: "stream" });
23126
+ } else {
23127
+ storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage, earlyStopFired ? nextPassthroughToolCallAssistantUuid : null, earlyStopFired ? nextPassthroughToolCallIds : null);
23128
+ commitSessionTurn();
23129
+ }
23047
23130
  }
23048
23131
  const classifyNow = () => classifyTurnOutcome({
23049
23132
  textEvents: textEventsForwarded,
@@ -23068,7 +23151,8 @@ data: ${JSON.stringify({
23068
23151
  });
23069
23152
  const recoveryLifter = createRecoveryLifter(() => nextClientBlockIndex++);
23070
23153
  let recoverySessionId;
23071
- let recoveryBoundaryUuid;
23154
+ let recoveryToolCallAssistantUuid;
23155
+ const recoveryEarlyStop = createEarlyStopTracker();
23072
23156
  try {
23073
23157
  for await (const event of runSdkQueryAttempt(buildQueryOptions({
23074
23158
  prompt: SILENT_TURN_NUDGE,
@@ -23086,7 +23170,7 @@ data: ${JSON.stringify({
23086
23170
  hasDeferredTools,
23087
23171
  resumeSessionId: currentSessionId || resumeSessionId,
23088
23172
  isUndo: false,
23089
- resumeSessionAtUuid: nextPassthroughResumeUuid,
23173
+ resumeSessionAtUuid: nextPassthroughToolCallAssistantUuid,
23090
23174
  forkSession: true,
23091
23175
  sdkHooks,
23092
23176
  blockedTools: pipelineCtx.blockedTools,
@@ -23116,7 +23200,12 @@ data: ${JSON.stringify({
23116
23200
  const recoveryMessage = event;
23117
23201
  if (recoveryMessage.session_id)
23118
23202
  recoverySessionId = recoveryMessage.session_id;
23119
- recoveryBoundaryUuid = resumeBoundaryUuid(recoveryMessage) ?? recoveryBoundaryUuid;
23203
+ if (recoveryMessage.type === "assistant") {
23204
+ noteAssistantMessage(recoveryEarlyStop, recoveryMessage);
23205
+ } else if (recoveryMessage.type === "user") {
23206
+ noteUserContent(recoveryEarlyStop, recoveryMessage.message?.content);
23207
+ recoveryToolCallAssistantUuid = settledToolCallAssistantUuid(recoveryEarlyStop);
23208
+ }
23120
23209
  if (recoveryMessage.type !== "stream_event")
23121
23210
  continue;
23122
23211
  const lifted = recoveryLifter.lift(event.event);
@@ -23145,11 +23234,12 @@ data: ${JSON.stringify(lifted.frame)}
23145
23234
  }
23146
23235
  if (silentTurnRecovered && recoverySessionId && !isIndependentSession && !sawDuplicateToolUse) {
23147
23236
  currentSessionId = recoverySessionId;
23148
- nextPassthroughResumeUuid = recoveryBoundaryUuid;
23237
+ nextPassthroughToolCallAssistantUuid = recoveryToolCallAssistantUuid;
23238
+ nextPassthroughToolCallIds = recoveryToolCallAssistantUuid ? [...recoveryEarlyStop.expected] : undefined;
23149
23239
  sdkUuidMap.length = 0;
23150
23240
  for (let i = 0;i < allMessages.length; i++)
23151
23241
  sdkUuidMap.push(null);
23152
- storeSession(profileSessionId, body.messages || [], recoverySessionId, profileScopedCwd, sdkUuidMap, lastUsage, recoveryBoundaryUuid ?? null);
23242
+ storeSession(profileSessionId, body.messages || [], recoverySessionId, profileScopedCwd, sdkUuidMap, lastUsage, recoveryToolCallAssistantUuid ?? null, recoveryToolCallAssistantUuid ? [...recoveryEarlyStop.expected] : null);
23153
23243
  commitSessionTurn();
23154
23244
  }
23155
23245
  claudeLog("response.silent_turn_recovery_result", {
@@ -23327,18 +23417,19 @@ data: {"type":"message_stop"}
23327
23417
  profileSessionId,
23328
23418
  currentSessionId,
23329
23419
  sawDuplicateToolUse,
23330
- resumeBoundaryUuid: nextPassthroughResumeUuid,
23420
+ toolCallAssistantUuid: nextPassthroughToolCallAssistantUuid,
23331
23421
  passthrough
23332
23422
  });
23333
- if (disposition.action === "store" && currentSessionId) {
23334
- storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage, disposition.resumeUuid);
23335
- commitSessionTurn();
23336
- } else if (disposition.action === "evict") {
23423
+ if (disposition.action === "evict") {
23337
23424
  evictSession(profileSessionId, profileScopedCwd, body.messages || []);
23338
23425
  }
23339
23426
  claudeLog("passthrough.client_abort_settled", { action: disposition.action });
23340
23427
  return;
23341
23428
  }
23429
+ if (passthrough && streamedToolUseIds.size > 0 && !sawCanonicalResult) {
23430
+ evictSession(profileSessionId, profileScopedCwd, body.messages || []);
23431
+ claudeLog("passthrough.noncanonical_session_evicted", { mode: "stream", reason: "drain_error" });
23432
+ }
23342
23433
  const stderrOutput = stderrLines.join(`
23343
23434
  `).trim();
23344
23435
  if (stderrOutput && error instanceof Error && !error.message.includes(stderrOutput)) {
@@ -23366,7 +23457,7 @@ Subprocess stderr: ${stderrOutput}`;
23366
23457
  reason: sdkTerm.reason,
23367
23458
  passthrough,
23368
23459
  capturedToolUses: capturedToolUses.length,
23369
- abortIsOurs: sawDuplicateToolUse || earlyStopFired
23460
+ abortIsOurs: sawDuplicateToolUse
23370
23461
  }) && messageStartEmitted;
23371
23462
  if (canRecoverAsToolUse) {
23372
23463
  diagnosticLog2.session(`${requestMeta.requestId} sdk_termination_recovered ${formatSdkTermination(sdkTerm, {
@@ -23549,6 +23640,10 @@ data: ${JSON.stringify({
23549
23640
  cancel(reason) {
23550
23641
  requestAbort.abort(reason);
23551
23642
  requestAbort.detach();
23643
+ if (!isIndependentSession) {
23644
+ evictSession(profileSessionId, profileScopedCwd, body.messages || []);
23645
+ claudeLog("passthrough.client_abort_settled", { action: "evict", source: "stream_cancel" });
23646
+ }
23552
23647
  }
23553
23648
  });
23554
23649
  const streamSessionId = resumeSessionId || `session_${Date.now()}`;