@rynfar/meridian 1.49.1 → 1.50.0

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.
package/README.md CHANGED
@@ -232,6 +232,7 @@ For large tool sets (>15 tools), non-core tools are automatically deferred via t
232
232
  - **Single tool round-trip per request** — in passthrough mode, the SDK is configured with `maxTurns=2` (or 3 for deferred tools). Multi-step agentic loops where Claude needs several consecutive tool calls require the client to re-send after each round.
233
233
  - **Blocked tools** — 13 built-in SDK tools (Read, Write, Bash, etc.) are blocked to prevent conflicts with the client's own tools. 15 additional Claude Code-only tools (CronCreate, EnterWorktree, Agent, etc.) are blocked because they require capabilities that external clients don't support.
234
234
  - **Subagent extraction** — Meridian parses the client's Task tool description to extract subagent names and build SDK AgentDefinitions. If the client's agent framework uses a non-standard format, subagent routing may not work automatically.
235
+ - **Scratchpad suppression (passthrough)** — the Claude CLI advertises a proxy-host scratchpad directory that clients can't use; OpenCode 1.18+ permission-blocks writes to it. Meridian suppresses it in passthrough mode (`CLAUDE_CODE_SESSION_KIND=bg` on the subprocess). Kill switch: `MERIDIAN_SUPPRESS_SCRATCHPAD=0`.
235
236
  - **Anthropic server tools not supported** — native server-side tools (`web_search_*`, `web_fetch_*`) are a raw Anthropic API feature (billed to an API key) that emits `server_tool_use` / `web_search_tool_result` blocks the Claude Max / Agent SDK path cannot produce. A request carrying one is rejected with a `400` explaining the fix. If a plugin needs server-side web search (e.g. [`opencode-websearch`](https://github.com/emilsvennesson/opencode-websearch)), give it its **own** provider pointed at `https://api.anthropic.com` with your `ANTHROPIC_API_KEY` — don't route that call through Meridian.
236
237
 
237
238
  ## Multi-Profile Support
@@ -83,6 +83,7 @@ function computeSummary(metrics, windowMs) {
83
83
  windowMs,
84
84
  totalRequests: 0,
85
85
  errorCount: 0,
86
+ envelopeViolationCount: 0,
86
87
  requestsPerMinute: 0,
87
88
  queueWait: emptyPhase,
88
89
  proxyOverhead: emptyPhase,
@@ -102,6 +103,7 @@ function computeSummary(metrics, windowMs) {
102
103
  };
103
104
  }
104
105
  const errorCount = metrics.filter((m) => m.error !== null).length;
106
+ const envelopeViolationCount = metrics.reduce((sum, m) => sum + (m.envelopeViolations?.length ?? 0), 0);
105
107
  const oldest = metrics[metrics.length - 1].timestamp;
106
108
  const newest = metrics[0].timestamp;
107
109
  const spanMs = Math.max(newest - oldest, 1);
@@ -148,6 +150,7 @@ function computeSummary(metrics, windowMs) {
148
150
  windowMs,
149
151
  totalRequests: metrics.length,
150
152
  errorCount,
153
+ envelopeViolationCount,
151
154
  requestsPerMinute: Math.round(requestsPerMinute * 100) / 100,
152
155
  queueWait: computePercentiles(queueWaits),
153
156
  proxyOverhead: computePercentiles(overheads),
@@ -8283,7 +8286,7 @@ function noteUserContent(tracker, content) {
8283
8286
  return;
8284
8287
  for (const block of content) {
8285
8288
  const b = block;
8286
- if (b?.type === "tool_result" && typeof b.tool_use_id === "string" && tracker.expected.has(b.tool_use_id)) {
8289
+ if (b?.type === "tool_result" && typeof b.tool_use_id === "string") {
8287
8290
  tracker.resolved.add(b.tool_use_id);
8288
8291
  }
8289
8292
  }
@@ -8301,6 +8304,45 @@ function shouldEarlyStop(tracker) {
8301
8304
  return true;
8302
8305
  }
8303
8306
 
8307
+ // src/proxy/envelopeIntegrity.ts
8308
+ function checkEmptyToolInputs(contentBlocks, tools) {
8309
+ if (!tools || tools.length === 0 || contentBlocks.length === 0)
8310
+ return [];
8311
+ const requiredByName = new Map;
8312
+ for (const t of tools) {
8313
+ const req = t.input_schema?.required;
8314
+ requiredByName.set(t.name, Array.isArray(req) && req.length > 0);
8315
+ }
8316
+ const violations = [];
8317
+ for (const b of contentBlocks) {
8318
+ if (b.type !== "tool_use" || typeof b.name !== "string")
8319
+ continue;
8320
+ if (!requiredByName.get(b.name))
8321
+ continue;
8322
+ const input = b.input;
8323
+ const empty = input == null || typeof input === "object" && Object.keys(input).length === 0;
8324
+ if (empty) {
8325
+ violations.push({
8326
+ type: "empty_tool_input",
8327
+ detail: `tool_use ${b.name} (${String(b.id ?? "?")}) delivered with empty input but schema requires arguments`
8328
+ });
8329
+ }
8330
+ }
8331
+ return violations;
8332
+ }
8333
+ function checkUndeliveredToolUses(captured, deliveredIds) {
8334
+ const violations = [];
8335
+ for (const c of captured) {
8336
+ if (!deliveredIds.has(c.id)) {
8337
+ violations.push({
8338
+ type: "undelivered_tool_use",
8339
+ detail: `captured tool_use ${c.name} (${c.id}) was never delivered to the client`
8340
+ });
8341
+ }
8342
+ }
8343
+ return violations;
8344
+ }
8345
+
8304
8346
  // src/proxy/agentDefs.ts
8305
8347
  var FALLBACK_AGENT_NAME = "general";
8306
8348
  var DEFAULT_AGENT_TYPES = {
@@ -8897,6 +8939,7 @@ function render(s, reqs, logs, quota) {
8897
8939
  html += '<div class="cards">'
8898
8940
  + card('Requests', s.totalRequests, s.requestsPerMinute.toFixed(1) + ' req/min')
8899
8941
  + card('Errors', s.errorCount, s.totalRequests > 0 ? ((s.errorCount/s.totalRequests)*100).toFixed(1) + '% error rate' : '')
8942
+ + '<div class="card"><div class="card-label">Envelope</div><div class="card-value" style="color:' + ((s.envelopeViolationCount || 0) > 0 ? 'var(--red)' : 'var(--green)') + '">' + (s.envelopeViolationCount || 0) + '</div><div class="card-detail">' + ((s.envelopeViolationCount || 0) > 0 ? 'wire-contract violations — check logs' : 'wire contract clean') + '</div></div>'
8900
8943
  + card('Median Total', ms(s.totalDuration.p50), 'p95: ' + ms(s.totalDuration.p95))
8901
8944
  + card('Median TTFB', ms(s.ttfb.p50), 'p95: ' + ms(s.ttfb.p95))
8902
8945
  + card('Proxy Overhead', ms(s.proxyOverhead.p50), 'p95: ' + ms(s.proxyOverhead.p95))
@@ -8974,6 +9017,7 @@ function render(s, reqs, logs, quota) {
8974
9017
  const respW = Math.max((r.upstreamDurationMs - (r.ttfbMs || 0)) * scale, 2);
8975
9018
 
8976
9019
  const lineageBadge = r.lineageType ? '<span style="font-size:10px;padding:1px 5px;border-radius:3px;background:' + ({continuation:'var(--green)',compaction:'var(--yellow)',undo:'var(--purple)',diverged:'var(--red)',new:'var(--muted)'}[r.lineageType] || 'var(--muted)') + ';color:var(--bg)">' + r.lineageType + '</span>' : '';
9020
+ const envBadge = (r.envelopeViolations && r.envelopeViolations.length > 0) ? ' <span style="font-size:10px;padding:1px 5px;border-radius:3px;background:var(--red);color:var(--bg)" title="' + r.envelopeViolations.join(', ') + '">envelope×' + r.envelopeViolations.length + '</span>' : '';
8977
9021
  const sessionShort = r.sdkSessionId ? r.sdkSessionId.slice(0, 8) : '—';
8978
9022
  const msgCount = r.messageCount != null ? r.messageCount : '?';
8979
9023
 
@@ -8984,7 +9028,7 @@ function render(s, reqs, logs, quota) {
8984
9028
  + '<td>' + (r.adapter || '—') + sourceBadge + '</td>'
8985
9029
  + '<td>' + (r.requestModel || r.model) + '<br><span style="font-size:10px;color:var(--muted)">' + r.model + '</span></td>'
8986
9030
  + '<td>' + r.mode + (r.hasDeferredTools ? (function() { var sessDisc = r.sessionDiscoveredCount || 0; var loaded = ((r.toolCount || 0) - (r.deferredToolCount || 0)) + sessDisc; var deferred = Math.max(0, (r.deferredToolCount || 0) - sessDisc); var newDisc = r.discoveredTools || []; return '<br><span style="font-size:10px;color:var(--purple)">loaded=' + loaded + ' deferred=' + deferred + '</span>' + (newDisc.length > 0 ? '<br><span style="font-size:10px;color:var(--green)">+' + newDisc.join(', +') + '</span>' : ''); })() : '') + '</td>'
8987
- + '<td class="mono">' + sessionShort + ' ' + lineageBadge + '<br><span style="font-size:10px;color:var(--muted)">' + msgCount + ' msgs</span></td>'
9031
+ + '<td class="mono">' + sessionShort + ' ' + lineageBadge + envBadge + '<br><span style="font-size:10px;color:var(--muted)">' + msgCount + ' msgs</span></td>'
8988
9032
  + '<td class="' + statusClass + '">' + statusText + '</td>'
8989
9033
  + '<td class="mono">' + ms(r.queueWaitMs) + '</td>'
8990
9034
  + '<td class="mono">' + ms(r.proxyOverheadMs) + '</td>'
@@ -17236,6 +17280,7 @@ function buildQueryOptions(ctx, abortController) {
17236
17280
  ...sharedMemory ? stripConfigDir(cleanEnv) : cleanEnv,
17237
17281
  ENABLE_TOOL_SEARCH: hasDeferredTools ? "true" : "false",
17238
17282
  ...passthrough ? { ENABLE_CLAUDEAI_MCP_SERVERS: "false" } : {},
17283
+ ...passthrough && process.env.MERIDIAN_SUPPRESS_SCRATCHPAD !== "0" ? { CLAUDE_CODE_SESSION_KIND: "bg" } : {},
17239
17284
  ...process.getuid?.() === 0 ? { IS_SANDBOX: "1" } : {},
17240
17285
  ...ctx.envOverrides
17241
17286
  },
@@ -18408,6 +18453,26 @@ function createProxyServer(config = {}) {
18408
18453
  const sessionDiscoveredTools = new Map;
18409
18454
  const sessionToolCache = new Map;
18410
18455
  const sessionMcpCache = new LRUMap(getMaxSessionsLimit());
18456
+ const PENDING_STORE_WAIT_MS = 3000;
18457
+ const PENDING_STORE_AUTO_RESOLVE_MS = 1e4;
18458
+ const pendingSessionStores = new Map;
18459
+ const registerPendingStore = (key) => {
18460
+ let resolveFn = () => {};
18461
+ const promise = new Promise((resolve3) => {
18462
+ const timer = setTimeout(resolve3, PENDING_STORE_AUTO_RESOLVE_MS);
18463
+ resolveFn = () => {
18464
+ clearTimeout(timer);
18465
+ resolve3();
18466
+ };
18467
+ });
18468
+ const entry = { promise, resolve: resolveFn };
18469
+ pendingSessionStores.set(key, entry);
18470
+ return () => {
18471
+ entry.resolve();
18472
+ if (pendingSessionStores.get(key) === entry)
18473
+ pendingSessionStores.delete(key);
18474
+ };
18475
+ };
18411
18476
  const pluginDir = finalConfig.pluginDir ?? join6(homedir5(), ".config", "meridian", "plugins");
18412
18477
  const pluginConfigPath = finalConfig.pluginConfigPath ?? join6(homedir5(), ".config", "meridian", "plugins.json");
18413
18478
  let loadedPlugins = [];
@@ -18594,6 +18659,17 @@ function createProxyServer(config = {}) {
18594
18659
  const lastIsToolResult = Array.isArray(lastMessage?.content) && lastMessage.content.some((b) => b?.type === "tool_result");
18595
18660
  const isClientDrivenLoop = adapterBase !== "claude-code" && !agentSessionId && lastIsToolResult;
18596
18661
  const isIndependentSession = requestSource?.startsWith("fork-") || requestSource?.startsWith("subagent-") || isClientDrivenLoop || false;
18662
+ if (!isIndependentSession && profileSessionId) {
18663
+ const pendingStore = pendingSessionStores.get(profileSessionId);
18664
+ if (pendingStore) {
18665
+ const waitStart = Date.now();
18666
+ await Promise.race([
18667
+ pendingStore.promise,
18668
+ new Promise((resolve3) => setTimeout(resolve3, PENDING_STORE_WAIT_MS))
18669
+ ]);
18670
+ claudeLog("session.pending_store_awaited", { waitedMs: Date.now() - waitStart });
18671
+ }
18672
+ }
18597
18673
  let lineageResult = isIndependentSession ? { type: "diverged" } : lookupSession(profileSessionId, body.messages || [], profileScopedCwd);
18598
18674
  if (lineageResult.type === "undo" && adapterBase === "opencode" && !agentSessionId) {
18599
18675
  lineageResult = { type: "diverged" };
@@ -18718,6 +18794,35 @@ function createProxyServer(config = {}) {
18718
18794
  const earlyStopEnabled = passthrough && process.env.MERIDIAN_PASSTHROUGH_EARLY_STOP !== "0";
18719
18795
  const earlyStop = createEarlyStopTracker();
18720
18796
  let earlyStopFired = false;
18797
+ const envelopeViolations = [];
18798
+ const recordEnvelopeViolations = (violations) => {
18799
+ for (const v of violations) {
18800
+ envelopeViolations.push(v.type);
18801
+ claudeLog("envelope.violation", { type: v.type, detail: v.detail });
18802
+ diagnosticLog2.error(`${requestMeta.requestId} ENVELOPE VIOLATION [${v.type}] ${v.detail}`, requestMeta.requestId);
18803
+ }
18804
+ };
18805
+ const DENY_HOLD_TIMEOUT_MS = 8000;
18806
+ const pendingDenyReleases = [];
18807
+ let turnGenerating = false;
18808
+ const releaseHeldDenies = (reason) => {
18809
+ turnGenerating = false;
18810
+ if (pendingDenyReleases.length === 0)
18811
+ return;
18812
+ claudeLog("passthrough.deny_hold_released", { reason, count: pendingDenyReleases.length });
18813
+ for (const release of pendingDenyReleases.splice(0))
18814
+ release();
18815
+ };
18816
+ const holdDenyUntilTurnEnd = () => new Promise((resolve3) => {
18817
+ const timer = setTimeout(() => {
18818
+ claudeLog("passthrough.deny_hold_timeout", { afterMs: DENY_HOLD_TIMEOUT_MS });
18819
+ resolve3();
18820
+ }, DENY_HOLD_TIMEOUT_MS);
18821
+ pendingDenyReleases.push(() => {
18822
+ clearTimeout(timer);
18823
+ resolve3();
18824
+ });
18825
+ });
18721
18826
  const toolChoice = body.tool_choice;
18722
18827
  const forceSingleToolUse = !!toolChoice && (toolChoice.type === "tool" || toolChoice.disable_parallel_tool_use === true);
18723
18828
  const fileChanges = [];
@@ -18803,6 +18908,9 @@ function createProxyServer(config = {}) {
18803
18908
  input: toolInput
18804
18909
  });
18805
18910
  }
18911
+ if (stream2 && earlyStopEnabled && turnGenerating && !requestAbort.controller.signal.aborted) {
18912
+ await holdDenyUntilTurnEnd();
18913
+ }
18806
18914
  if (isExactDuplicate) {
18807
18915
  return {
18808
18916
  decision: "block",
@@ -19310,8 +19418,16 @@ Subprocess stderr: ${stderrOutput}`;
19310
19418
  outputTokens: lastUsage?.output_tokens,
19311
19419
  cacheReadInputTokens: lastUsage?.cache_read_input_tokens,
19312
19420
  cacheCreationInputTokens: lastUsage?.cache_creation_input_tokens,
19313
- cacheHitRate: computeCacheHitRate(lastUsage)
19421
+ cacheHitRate: computeCacheHitRate(lastUsage),
19422
+ ...envelopeViolations.length > 0 ? { envelopeViolations: [...envelopeViolations] } : {}
19314
19423
  });
19424
+ if (passthrough) {
19425
+ const deliveredIds = new Set(contentBlocks.filter((b) => b.type === "tool_use" && typeof b.id === "string").map((b) => b.id));
19426
+ recordEnvelopeViolations([
19427
+ ...checkEmptyToolInputs(contentBlocks, requestTools),
19428
+ ...checkUndeliveredToolUses(capturedToolUses, deliveredIds)
19429
+ ]);
19430
+ }
19315
19431
  if (currentSessionId && !isIndependentSession && !sawDuplicateToolUse) {
19316
19432
  storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage);
19317
19433
  }
@@ -19378,6 +19494,23 @@ Subprocess stderr: ${stderrOutput}`;
19378
19494
  let structuredOutput;
19379
19495
  const streamedToolUseIds = new Set;
19380
19496
  const openClientBlocks = new Set;
19497
+ const resolvePendingStore = passthrough && earlyStopEnabled && !isIndependentSession && profileSessionId ? registerPendingStore(profileSessionId) : () => {};
19498
+ const flushOpenClientBlocks = (source) => {
19499
+ if (openClientBlocks.size === 0)
19500
+ return;
19501
+ recordEnvelopeViolations([...openClientBlocks].map((idx) => ({
19502
+ type: "dangling_block",
19503
+ detail: `content block ${idx} still open at ${source} close`
19504
+ })));
19505
+ claudeLog("stream.dangling_blocks_closed", { source, count: openClientBlocks.size });
19506
+ for (const idx of openClientBlocks) {
19507
+ safeEnqueue(encoder.encode(`event: content_block_stop
19508
+ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
19509
+
19510
+ `), `${source}_close_dangling`);
19511
+ }
19512
+ openClientBlocks.clear();
19513
+ };
19381
19514
  try {
19382
19515
  let currentSessionId;
19383
19516
  const MAX_RATE_LIMIT_RETRIES = 2;
@@ -19658,6 +19791,7 @@ Subprocess stderr: ${stderrOutput}`;
19658
19791
  if (shouldEarlyStop(earlyStop) && streamedToolUseIds.size > 0) {
19659
19792
  earlyStopFired = true;
19660
19793
  claudeLog("passthrough.early_stop", { mode: "stream", captured: capturedToolUses.length, drained: awaitingEarlyStopDrain });
19794
+ flushOpenClientBlocks("early_stop");
19661
19795
  safeEnqueue(encoder.encode(`event: message_delta
19662
19796
  data: ${JSON.stringify({ type: "message_delta", delta: { stop_reason: "tool_use", stop_sequence: null }, usage: { output_tokens: lastUsage?.output_tokens ?? 0 } })}
19663
19797
 
@@ -19700,6 +19834,12 @@ data: ${JSON.stringify({ type: "message_stop" })}
19700
19834
  const event = message.event;
19701
19835
  const eventType = event.type;
19702
19836
  const eventIndex = event.index;
19837
+ if (eventType === "message_delta" || eventType === "message_stop" || eventType === "message_start" && messageStartEmitted) {
19838
+ releaseHeldDenies(eventType);
19839
+ }
19840
+ if (eventType === "message_start") {
19841
+ turnGenerating = true;
19842
+ }
19703
19843
  if (outputFormat) {
19704
19844
  if (eventType === "message_start") {
19705
19845
  const startUsage = event.message?.usage;
@@ -19720,6 +19860,7 @@ data: ${JSON.stringify({ type: "message_stop" })}
19720
19860
  lastUsage = { ...lastUsage, ...startUsage };
19721
19861
  if (messageStartEmitted) {
19722
19862
  if (passthrough && streamedToolUseIds.size > 0) {
19863
+ flushOpenClientBlocks("turn2_suppression");
19723
19864
  safeEnqueue(encoder.encode(`event: message_delta
19724
19865
  data: ${JSON.stringify({ type: "message_delta", delta: { stop_reason: "tool_use", stop_sequence: null }, usage: { output_tokens: lastUsage?.output_tokens ?? 0 } })}
19725
19866
 
@@ -19846,6 +19987,7 @@ data: ${JSON.stringify(event)}
19846
19987
  openClientBlocks.delete(idx);
19847
19988
  }
19848
19989
  if (passthrough && eventType === "message_delta" && event.delta?.stop_reason === "tool_use" && streamedToolUseIds.size > 0) {
19990
+ flushOpenClientBlocks("drain_close");
19849
19991
  safeEnqueue(encoder.encode(`event: message_stop
19850
19992
  data: ${JSON.stringify({ type: "message_stop" })}
19851
19993
 
@@ -19868,6 +20010,7 @@ data: ${JSON.stringify({ type: "message_stop" })}
19868
20010
  }
19869
20011
  } finally {
19870
20012
  clearInterval(heartbeat);
20013
+ releaseHeldDenies("stream_loop_exit");
19871
20014
  }
19872
20015
  if (outputFormat) {
19873
20016
  if (!hasStructuredOutput) {
@@ -19923,6 +20066,9 @@ data: ${JSON.stringify({
19923
20066
  eventsForwarded += 5;
19924
20067
  textEventsForwarded += 1;
19925
20068
  }
20069
+ if (passthrough) {
20070
+ recordEnvelopeViolations(checkUndeliveredToolUses(capturedToolUses, streamedToolUseIds));
20071
+ }
19926
20072
  claudeLog("upstream.completed", {
19927
20073
  mode: "stream",
19928
20074
  model,
@@ -19946,12 +20092,14 @@ data: ${JSON.stringify({
19946
20092
  if (currentSessionId && !isIndependentSession && !sawDuplicateToolUse) {
19947
20093
  storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage);
19948
20094
  }
20095
+ resolvePendingStore();
19949
20096
  if (!streamClosed) {
19950
20097
  const unseenToolUses = capturedToolUses.filter((tu) => !streamedToolUseIds.has(tu.id));
19951
20098
  if (passthrough && unseenToolUses.length > 0 && messageStartEmitted) {
19952
20099
  for (let i = 0;i < unseenToolUses.length; i++) {
19953
20100
  const tu = unseenToolUses[i];
19954
20101
  const blockIndex = eventsForwarded + i;
20102
+ streamedToolUseIds.add(tu.id);
19955
20103
  safeEnqueue(encoder.encode(`event: content_block_start
19956
20104
  data: ${JSON.stringify({
19957
20105
  type: "content_block_start",
@@ -20081,7 +20229,8 @@ data: {"type":"message_stop"}
20081
20229
  outputTokens: lastUsage?.output_tokens,
20082
20230
  cacheReadInputTokens: lastUsage?.cache_read_input_tokens,
20083
20231
  cacheCreationInputTokens: lastUsage?.cache_creation_input_tokens,
20084
- cacheHitRate: computeCacheHitRate(lastUsage)
20232
+ cacheHitRate: computeCacheHitRate(lastUsage),
20233
+ ...envelopeViolations.length > 0 ? { envelopeViolations: [...envelopeViolations] } : {}
20085
20234
  });
20086
20235
  if (textEventsForwarded === 0) {
20087
20236
  claudeLog("response.empty_stream", {
@@ -20104,6 +20253,7 @@ data: {"type":"message_stop"}
20104
20253
  });
20105
20254
  return;
20106
20255
  }
20256
+ resolvePendingStore();
20107
20257
  const stderrOutput = stderrLines.join(`
20108
20258
  `).trim();
20109
20259
  if (stderrOutput && error instanceof Error && !error.message.includes(stderrOutput)) {
@@ -20136,17 +20286,12 @@ Subprocess stderr: ${stderrOutput}`;
20136
20286
  hasDeferredTools,
20137
20287
  sdkSessionId: resumeSessionId
20138
20288
  })} captured=${capturedToolUses.length}`, requestMeta.requestId);
20139
- for (const idx of openClientBlocks) {
20140
- safeEnqueue(encoder.encode(`event: content_block_stop
20141
- data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
20142
-
20143
- `), "recover_close_dangling_block");
20144
- }
20145
- openClientBlocks.clear();
20289
+ flushOpenClientBlocks("recovery");
20146
20290
  const unseenToolUses = capturedToolUses.filter((tu) => !streamedToolUseIds.has(tu.id));
20147
20291
  for (let i = 0;i < unseenToolUses.length; i++) {
20148
20292
  const tu = unseenToolUses[i];
20149
20293
  const blockIndex = eventsForwarded + i;
20294
+ streamedToolUseIds.add(tu.id);
20150
20295
  safeEnqueue(encoder.encode(`event: content_block_start
20151
20296
  data: ${JSON.stringify({
20152
20297
  type: "content_block_start",
@@ -20183,6 +20328,7 @@ data: ${JSON.stringify({
20183
20328
  data: {"type":"message_stop"}
20184
20329
 
20185
20330
  `), "recover_message_stop");
20331
+ recordEnvelopeViolations(checkUndeliveredToolUses(capturedToolUses, streamedToolUseIds));
20186
20332
  const recoverTotalMs = Date.now() - requestStartAt;
20187
20333
  const recoverQueueWaitMs = requestMeta.queueStartedAt - requestMeta.queueEnteredAt;
20188
20334
  telemetryStore2.record({
@@ -20209,7 +20355,8 @@ data: {"type":"message_stop"}
20209
20355
  totalDurationMs: recoverTotalMs,
20210
20356
  contentBlocks: eventsForwarded + unseenToolUses.length,
20211
20357
  textEvents: textEventsForwarded,
20212
- error: null
20358
+ error: null,
20359
+ ...envelopeViolations.length > 0 ? { envelopeViolations: [...envelopeViolations] } : {}
20213
20360
  });
20214
20361
  if (!streamClosed) {
20215
20362
  try {
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startProxyServer
4
- } from "./cli-mjrt119g.js";
4
+ } from "./cli-yw8cx02m.js";
5
5
  import"./cli-f0yqy2d2.js";
6
6
  import"./cli-sry5aqdj.js";
7
7
  import"./cli-4rqtm83g.js";
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Envelope integrity checks.
3
+ *
4
+ * Every #552-family bug (red reads, beheaded parallel calls, lost tool
5
+ * inputs) manifested as a wire-contract violation that was visible at
6
+ * response time — but only to the CLIENT, so we learned about each one from
7
+ * user transcripts weeks later. These checks make the proxy assert its own
8
+ * output contract on every response:
9
+ *
10
+ * - dangling_block: a content block left open at stream close
11
+ * (recorded at the flush site in server.ts)
12
+ * - undelivered_tool_use: a hook-captured call that never reached the client
13
+ * - empty_tool_input: a delivered tool_use with no arguments where the
14
+ * tool's schema requires them (the client-visible
15
+ * shape of a beheaded call)
16
+ *
17
+ * Violations are logged (envelope.violation) and counted on /telemetry so
18
+ * the next regression in this family fires a tripwire in OUR logs instead
19
+ * of waiting for a user report.
20
+ *
21
+ * Pure module — no I/O, no imports from server.ts or session/.
22
+ */
23
+ export interface EnvelopeViolation {
24
+ type: "dangling_block" | "undelivered_tool_use" | "empty_tool_input";
25
+ detail: string;
26
+ }
27
+ interface ToolSchema {
28
+ name: string;
29
+ input_schema?: {
30
+ required?: unknown;
31
+ [key: string]: unknown;
32
+ };
33
+ }
34
+ /**
35
+ * Flag delivered tool_use blocks whose input is empty/missing even though
36
+ * the tool's schema declares required fields. A legit no-arg tool (empty
37
+ * `required`) never flags; unknown tools are skipped (nothing to judge by).
38
+ */
39
+ export declare function checkEmptyToolInputs(contentBlocks: Array<Record<string, unknown>>, tools: ToolSchema[] | undefined): EnvelopeViolation[];
40
+ /**
41
+ * Flag hook-captured tool calls that never reached the client — the
42
+ * proxy promised the model "forwarded to the client" for these, so a
43
+ * missing delivery diverges the model's view from reality (#552 family).
44
+ */
45
+ export declare function checkUndeliveredToolUses(captured: Array<{
46
+ id: string;
47
+ name: string;
48
+ }>, deliveredIds: ReadonlySet<string>): EnvelopeViolation[];
49
+ export {};
50
+ //# sourceMappingURL=envelopeIntegrity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envelopeIntegrity.d.ts","sourceRoot":"","sources":["../../src/proxy/envelopeIntegrity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,gBAAgB,GAAG,sBAAsB,GAAG,kBAAkB,CAAA;IACpE,MAAM,EAAE,MAAM,CAAA;CACf;AAED,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,YAAY,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAA;CAC9D;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAC7C,KAAK,EAAE,UAAU,EAAE,GAAG,SAAS,GAC9B,iBAAiB,EAAE,CAqBrB;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,EAC7C,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,GAChC,iBAAiB,EAAE,CAWrB"}
@@ -42,8 +42,14 @@ export declare function isClientForwardedToolUse(block: unknown): boolean;
42
42
  */
43
43
  export declare function noteAssistantContent(tracker: EarlyStopTracker, content: unknown): void;
44
44
  /**
45
- * Record persisted tool_results from a user message's content. Only results
46
- * matching an expected id count — unrelated results are ignored.
45
+ * Record persisted tool_results from a user message's content.
46
+ *
47
+ * Records EVERY tool_result id, not just already-expected ones: the CLI
48
+ * dispatches hooks per-block while later blocks are still streaming, and it
49
+ * emits per-block assistant messages — so a deny's tool_result can reach the
50
+ * iterator BEFORE the assistant message that arms its id in `expected`
51
+ * (observed live, MERIDIAN_TRACE_STREAM). Unmatched ids are harmless:
52
+ * shouldEarlyStop only ever checks ids that are in `expected`.
47
53
  */
48
54
  export declare function noteUserContent(tracker: EarlyStopTracker, content: unknown): void;
49
55
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"passthroughEarlyStop.d.ts","sourceRoot":"","sources":["../../src/proxy/passthroughEarlyStop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAUH,MAAM,WAAW,gBAAgB;IAC/B,8EAA8E;IAC9E,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACrB,6EAA6E;IAC7E,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACrB,0EAA0E;IAC1E,KAAK,EAAE,OAAO,CAAA;CACf;AAED,wBAAgB,sBAAsB,IAAI,gBAAgB,CAEzD;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAQhE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAOtF;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAQjF;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAQlE"}
1
+ {"version":3,"file":"passthroughEarlyStop.d.ts","sourceRoot":"","sources":["../../src/proxy/passthroughEarlyStop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAUH,MAAM,WAAW,gBAAgB;IAC/B,8EAA8E;IAC9E,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACrB,6EAA6E;IAC7E,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACrB,0EAA0E;IAC1E,KAAK,EAAE,OAAO,CAAA;CACf;AAED,wBAAgB,sBAAsB,IAAI,gBAAgB,CAEzD;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAQhE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAOtF;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAQjF;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAQlE"}
@@ -1 +1 @@
1
- {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/proxy/query.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/proxy/query.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/proxy/server.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACtE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,CAAA;AAGvD,YAAY,EACV,SAAS,EACT,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,WAAW,GACZ,MAAM,aAAa,CAAA;AAKpB,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAsCnG,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EAEpB,KAAK,aAAa,EAGnB,MAAM,mBAAmB,CAAA;AAG1B,OAAO,EAA+B,iBAAiB,EAAE,mBAAmB,EAAsC,MAAM,iBAAiB,CAAA;AAGzI,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,oBAAoB,EAAE,CAAA;AAChE,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,CAAA;AACjD,YAAY,EAAE,aAAa,EAAE,CAAA;AA+Q7B,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,WAAW,CA2tGhF;AAWD,wBAAgB,gCAAgC,IAAI,IAAI,CAavD;AAED,wBAAsB,gBAAgB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAmGhG"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/proxy/server.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACtE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,CAAA;AAGvD,YAAY,EACV,SAAS,EACT,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,WAAW,GACZ,MAAM,aAAa,CAAA;AAKpB,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAuCnG,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EAEpB,KAAK,aAAa,EAGnB,MAAM,mBAAmB,CAAA;AAG1B,OAAO,EAA+B,iBAAiB,EAAE,mBAAmB,EAAsC,MAAM,iBAAiB,CAAA;AAGzI,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,oBAAoB,EAAE,CAAA;AAChE,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,CAAA;AACjD,YAAY,EAAE,aAAa,EAAE,CAAA;AA+Q7B,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,WAAW,CAm4GhF;AAWD,wBAAgB,gCAAgC,IAAI,IAAI,CAavD;AAED,wBAAsB,gBAAgB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAmGhG"}
package/dist/server.js CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  runObserveHook,
12
12
  runTransformHook,
13
13
  startProxyServer
14
- } from "./cli-mjrt119g.js";
14
+ } from "./cli-yw8cx02m.js";
15
15
  import"./cli-f0yqy2d2.js";
16
16
  import"./cli-sry5aqdj.js";
17
17
  import"./cli-4rqtm83g.js";
@@ -1 +1 @@
1
- {"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../../src/telemetry/dashboard.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,eAAO,MAAM,aAAa,QAsdlB,CAAA"}
1
+ {"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../../src/telemetry/dashboard.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,eAAO,MAAM,aAAa,QAwdlB,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"percentiles.d.ts","sourceRoot":"","sources":["../../src/telemetry/percentiles.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE3E,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,WAAW,CAchE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,aAAa,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAuG3F"}
1
+ {"version":3,"file":"percentiles.d.ts","sourceRoot":"","sources":["../../src/telemetry/percentiles.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE3E,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,WAAW,CAchE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,aAAa,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CA0G3F"}
@@ -26,6 +26,10 @@ export interface RequestMetric {
26
26
  requestModel?: string;
27
27
  /** Streaming or non-streaming */
28
28
  mode: "stream" | "non-stream";
29
+ /** Envelope-integrity violations detected on this response (dangling_block,
30
+ * undelivered_tool_use, empty_tool_input). Absent when the envelope was
31
+ * clean — the overwhelmingly common case. See proxy/envelopeIntegrity.ts. */
32
+ envelopeViolations?: string[];
29
33
  /** Whether the request used session resume */
30
34
  isResume: boolean;
31
35
  /** Whether passthrough mode was active */
@@ -98,6 +102,9 @@ export interface TelemetrySummary {
98
102
  totalRequests: number;
99
103
  /** Requests that returned an error */
100
104
  errorCount: number;
105
+ /** Total envelope-integrity violations across the window (dangling blocks,
106
+ * undelivered tool calls, empty required inputs). 0 = wire contract clean. */
107
+ envelopeViolationCount: number;
101
108
  /** Requests per minute */
102
109
  requestsPerMinute: number;
103
110
  /** Timing breakdowns by phase */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/telemetry/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/telemetry/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rynfar/meridian",
3
- "version": "1.49.1",
3
+ "version": "1.50.0",
4
4
  "description": "Local Anthropic API powered by your Claude Max subscription. One subscription, every agent.",
5
5
  "type": "module",
6
6
  "main": "./dist/server.js",