@rynfar/meridian 1.62.5 → 1.62.7

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
@@ -104,13 +104,43 @@ The Claude Agent SDK provides programmatic access to Claude. But your favorite c
104
104
  | [Aider](https://github.com/paul-gauthier/aider) | ✅ Verified | Env vars — file editing, streaming; `--no-stream` broken (litellm bug) |
105
105
  | [Open WebUI](https://github.com/open-webui/open-webui) | ✅ Verified | OpenAI-compatible endpoints — set base URL to `http://127.0.0.1:3456` |
106
106
  | [Pi](https://github.com/mariozechner/pi-coding-agent) | ✅ Verified | models.json config (see [Agent Setup](docs/agents.md)) — full tool support via passthrough; detected via `x-meridian-agent: pi` header |
107
- | [Prime Agent](https://www.npmjs.com/package/prime-agent) | Verified | Extension config (see [Agent Setup](docs/agents.md)) — a Pi fork with its own `prime` adapter; single `ipython` tool via passthrough, RLM subagents get distinct session keys, sessions survive long idle gaps. The extension's `metadata.user_id` stamp is **required**, not optional. Cron/scheduled ticks are [not yet verified](docs/agents.md#prime-agent) |
107
+ | [Prime Agent](https://www.npmjs.com/package/prime-agent) | ⚠️ Single-agent verified | Extension config (see [Agent Setup](docs/agents.md)) — reliable with one active agent. Concurrent RLM subagents receive distinct session keys, but are not yet production-safe; see [Prime Agent subagents](#prime-agent-subagents). The extension's `metadata.user_id` stamp is **required**, not optional. |
108
108
  | [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | ✅ Verified | `ANTHROPIC_BASE_URL` — remote clients share a Max subscription over the network; client CWD preserved in system prompt |
109
109
  | [Cherry Studio](https://github.com/CherryHQ/cherry-studio) | ✅ Verified | `cherry` adapter (see [Agent Setup](docs/agents.md)) — chat client with Claude's built-in web search via internal mode |
110
110
  | Jcode | ✅ Verified | `/v1/chat/completions` + `x-jcode-session` header — dedicated `jcode` adapter keeps append-only history intact, so retained sessions resume on one SDK session (90.9% cache hit on turn 2 of a two-turn Opus session) |
111
111
  | [Codex CLI](https://github.com/openai/codex) | ✅ Verified | `/v1/responses` (see [Agent Setup](docs/agents.md)) — Responses-API provider, passthrough tool execution; verified on 0.144 (plain + tool-driving turns) |
112
112
  | [Continue](https://github.com/continuedev/continue) | 🔲 Untested | OpenAI-compatible endpoints should work — set `apiBase` to `http://127.0.0.1:3456` |
113
113
 
114
+ ### Prime Agent subagents
115
+
116
+ Prime Agent is reliable through Meridian with one active agent. RLM children have
117
+ separate session identities and can execute successfully, but concurrent subagent
118
+ orchestration is not yet production-safe. Observed failure modes include overload
119
+ amplification, expensive cache churn after fresh-session replay, loss of child-task
120
+ context during recovery, undelivered tool envelopes, and incomplete parent-to-child
121
+ cancellation. Use a single active Prime Agent for unattended or usage-sensitive work
122
+ until coordinated fixes land in Prime Agent and Meridian.
123
+
124
+ Prime Agent can keep Opus on the root session while selecting Sol for an individual
125
+ child. A child inherits its parent's model unless the `rlm` call supplies an exact
126
+ `provider/model` selector returned by `rlm.find_models()`:
127
+
128
+ ```python
129
+ sol_models = await rlm.find_models("sol")
130
+ print(sol_models) # choose an available exact selector for your authenticated providers
131
+
132
+ child = await rlm(
133
+ "Review this change and report your findings to the parent.",
134
+ name="sol-reviewer",
135
+ model="openai-codex/gpt-5.6-sol",
136
+ )
137
+ ```
138
+
139
+ The selector above requires an authenticated OpenAI Codex provider in Prime Agent;
140
+ Prime Inference may expose a different Sol selector. Explicit child model selection
141
+ reduces Claude Max pressure, but does not by itself fix the orchestration and
142
+ cancellation limitations above.
143
+
114
144
  Tested an agent or built a plugin? [Open an issue](https://github.com/rynfar/meridian/issues) and we'll add it.
115
145
 
116
146
  ## FAQ
@@ -44,20 +44,23 @@ import {
44
44
  isClosedControllerError,
45
45
  mapModelToClaudeModel,
46
46
  parseAuthorizationCodeInput,
47
+ recordExtendedContextRateLimited,
47
48
  recordExtendedContextUnavailable,
48
49
  resolveClaudeExecutableAsync,
49
50
  resolvePassthrough,
50
51
  resolveSdkModelDefaults,
51
52
  stripExtendedContext,
52
53
  subscriptionIncludesExtendedContext
53
- } from "./cli-d45dq9gf.js";
54
+ } from "./cli-xfbhn15a.js";
54
55
  import {
55
56
  getSetting,
56
57
  setSetting
57
58
  } from "./cli-vj9cv18n.js";
58
59
  import {
59
- checkPluginConfigured
60
- } from "./cli-je60fevk.js";
60
+ LRUMap,
61
+ checkPluginConfigured,
62
+ notePluginlessOpenCodeRequest
63
+ } from "./cli-pc0mtjjv.js";
61
64
  import {
62
65
  claudeLog,
63
66
  createPlatformCredentialStore,
@@ -2282,7 +2285,13 @@ var init_opencode2 = __esm(() => {
2282
2285
  openCodeAdapter = {
2283
2286
  name: "opencode",
2284
2287
  getSessionId(c) {
2285
- return c.req.header("x-opencode-session") ?? c.req.header("x-session-affinity");
2288
+ const base = c.req.header("x-opencode-session") ?? c.req.header("x-session-affinity");
2289
+ if (!base)
2290
+ return;
2291
+ if (c.req.header("x-opencode-agent-mode") !== "subagent")
2292
+ return base;
2293
+ const agent = c.req.header("x-opencode-agent-name")?.trim();
2294
+ return agent ? `${base}#${agent}` : base;
2286
2295
  },
2287
2296
  getAgentMode(c) {
2288
2297
  return c.req.header("x-opencode-agent-mode");
@@ -11309,71 +11318,6 @@ function shouldAttemptRecovery(input) {
11309
11318
  // src/proxy/server.ts
11310
11319
  init_agentMatch();
11311
11320
 
11312
- // src/utils/lruMap.ts
11313
- class LRUMap {
11314
- maxSize;
11315
- onEvict;
11316
- map = new Map;
11317
- constructor(maxSize, onEvict) {
11318
- this.maxSize = maxSize;
11319
- this.onEvict = onEvict;
11320
- }
11321
- get size() {
11322
- return this.map.size;
11323
- }
11324
- get(key) {
11325
- const value = this.map.get(key);
11326
- if (value === undefined)
11327
- return;
11328
- this.map.delete(key);
11329
- this.map.set(key, value);
11330
- return value;
11331
- }
11332
- set(key, value) {
11333
- if (this.map.has(key)) {
11334
- this.map.delete(key);
11335
- } else if (this.map.size >= this.maxSize) {
11336
- this.evictOldest();
11337
- }
11338
- this.map.set(key, value);
11339
- return this;
11340
- }
11341
- has(key) {
11342
- return this.map.has(key);
11343
- }
11344
- delete(key) {
11345
- return this.map.delete(key);
11346
- }
11347
- clear() {
11348
- this.map.clear();
11349
- }
11350
- entries() {
11351
- return this.map.entries();
11352
- }
11353
- keys() {
11354
- return this.map.keys();
11355
- }
11356
- values() {
11357
- return this.map.values();
11358
- }
11359
- forEach(callbackfn) {
11360
- this.map.forEach((value, key) => callbackfn(value, key, this));
11361
- }
11362
- [Symbol.iterator]() {
11363
- return this.map[Symbol.iterator]();
11364
- }
11365
- evictOldest() {
11366
- const oldestKey = this.map.keys().next().value;
11367
- if (oldestKey === undefined)
11368
- return;
11369
- const oldestValue = this.map.get(oldestKey);
11370
- if (oldestValue === undefined)
11371
- return;
11372
- this.map.delete(oldestKey);
11373
- this.onEvict?.(oldestKey, oldestValue);
11374
- }
11375
- }
11376
-
11377
11321
  // src/telemetry/index.ts
11378
11322
  init_env();
11379
11323
  import { join as join2 } from "node:path";
@@ -19415,12 +19359,15 @@ function stripConfigDir(env2) {
19415
19359
  delete out.CLAUDE_CONFIG_DIR;
19416
19360
  return out;
19417
19361
  }
19418
- function computePassthroughMaxTurns(hasDeferredTools, advisorModel) {
19362
+ function computePassthroughMaxTurns(hasDeferredTools, advisorModel, singleTurnHandoff) {
19419
19363
  const deferredBump = hasDeferredTools ? 1 : 0;
19420
19364
  const defaultBase = 3 + deferredBump;
19421
19365
  const configured = envInt("PASSTHROUGH_MAX_TURNS", defaultBase);
19422
- const base = configured > 0 ? configured : defaultBase;
19366
+ const operatorPinned = env("PASSTHROUGH_MAX_TURNS") !== undefined && configured > 0;
19423
19367
  const advisorBump = advisorModel ? 3 : 0;
19368
+ if (singleTurnHandoff && !operatorPinned)
19369
+ return 1;
19370
+ const base = configured > 0 ? configured : defaultBase;
19424
19371
  return base + advisorBump;
19425
19372
  }
19426
19373
  function buildCwdNote(sdkCwd, clientCwd) {
@@ -19502,7 +19449,7 @@ function buildQueryOptions(ctx, abortController) {
19502
19449
  prompt,
19503
19450
  options: {
19504
19451
  executable: "node",
19505
- maxTurns: passthrough ? computePassthroughMaxTurns(hasDeferredTools, ctx.advisorModel) : 200,
19452
+ maxTurns: passthrough ? computePassthroughMaxTurns(hasDeferredTools, ctx.advisorModel, ctx.earlyStop !== false && !hasDeferredTools && !ctx.advisorModel && !outputFormat) : 200,
19506
19453
  cwd: workingDirectory,
19507
19454
  model,
19508
19455
  pathToClaudeCodeExecutable: claudeExecutable,
@@ -21056,8 +21003,8 @@ function createProxyServer(config = {}) {
21056
21003
  proxyLogSilent = finalConfig.silent;
21057
21004
  const serverVersion = finalConfig.version ?? "unknown";
21058
21005
  restoreActiveProfile(finalConfig.profiles);
21059
- const sessionDiscoveredTools = new Map;
21060
- const sessionToolCache = new Map;
21006
+ const sessionDiscoveredTools = new LRUMap(getMaxSessionsLimit());
21007
+ const sessionToolCache = new LRUMap(getMaxSessionsLimit());
21061
21008
  const sessionMcpCache = new LRUMap(getMaxSessionsLimit());
21062
21009
  const RESUME_REFUSAL_MAX_RETRIES = 3;
21063
21010
  const RESUME_REFUSAL_RETRY_DELAY_MS = parseInt(process.env.MERIDIAN_BUSY_RETRY_DELAY_MS ?? "500", 10);
@@ -21362,7 +21309,7 @@ data: ${JSON.stringify(lastError)}
21362
21309
  const isSubagentRequest = declaredAgentMode === "subagent" || requestSource?.startsWith("subagent-") === true;
21363
21310
  const agentMode = isSubagentRequest ? "subagent" : declaredAgentMode;
21364
21311
  const requestedModel = typeof body.model === "string" ? body.model : "sonnet";
21365
- let model = mapModelToClaudeModel(requestedModel, authStatus?.subscriptionType, agentMode);
21312
+ let model = mapModelToClaudeModel(requestedModel, authStatus?.subscriptionType, agentMode, profile.id);
21366
21313
  const envOverrides = explicitModelPin(requestedModel);
21367
21314
  const cwdResolution = resolveSdkWorkingDirectory({
21368
21315
  envOverride: process.env.MERIDIAN_WORKDIR ?? process.env.CLAUDE_PROXY_WORKDIR,
@@ -21445,6 +21392,20 @@ data: ${JSON.stringify(lastError)}
21445
21392
  const taskBudget = Number.isFinite(parsedBudget) ? { total: parsedBudget } : body.task_budget ? { total: body.task_budget.total ?? body.task_budget } : undefined;
21446
21393
  const betas = betaFilter.forwarded;
21447
21394
  const agentSessionId = adapter.getSessionId(c, body);
21395
+ const pluginlessWarning = notePluginlessOpenCodeRequest({
21396
+ userAgent: c.req.header("user-agent"),
21397
+ agentModeHeader: c.req.header("x-opencode-agent-mode"),
21398
+ sessionId: agentSessionId
21399
+ });
21400
+ if (pluginlessWarning) {
21401
+ plog(`[PROXY] ${requestMeta.requestId} ${pluginlessWarning}`);
21402
+ diagnosticLog2.log({
21403
+ level: "warn",
21404
+ category: "session",
21405
+ message: `${requestMeta.requestId} ${pluginlessWarning}`,
21406
+ requestId: requestMeta.requestId
21407
+ });
21408
+ }
21448
21409
  const profileSessionId = profile.id !== "default" && agentSessionId ? `${profile.id}:${agentSessionId}` : agentSessionId;
21449
21410
  const commitSessionTurn = () => {
21450
21411
  if (profileSessionId)
@@ -21871,6 +21832,7 @@ data: ${JSON.stringify(lastError)}
21871
21832
  cleanEnv: profileEnv,
21872
21833
  envOverrides,
21873
21834
  hasDeferredTools,
21835
+ earlyStop: earlyStopEnabled,
21874
21836
  resumeSessionId,
21875
21837
  isUndo,
21876
21838
  resumeSessionAtUuid: undoRollbackUuid ?? passthroughToolCallAssistantUuid,
@@ -21959,6 +21921,7 @@ data: ${JSON.stringify(lastError)}
21959
21921
  cleanEnv: profileEnv,
21960
21922
  envOverrides,
21961
21923
  hasDeferredTools,
21924
+ earlyStop: earlyStopEnabled,
21962
21925
  resumeSessionId: undefined,
21963
21926
  isUndo: false,
21964
21927
  resumeSessionAtUuid: undefined,
@@ -21992,7 +21955,7 @@ data: ${JSON.stringify(lastError)}
21992
21955
  if (isExtraUsageRequiredError(errMsg) && hasExtendedContext(model)) {
21993
21956
  const from = model;
21994
21957
  model = stripExtendedContext(model);
21995
- recordExtendedContextUnavailable();
21958
+ recordExtendedContextUnavailable(profile.id);
21996
21959
  claudeLog("upstream.context_fallback", {
21997
21960
  mode: "non_stream",
21998
21961
  from,
@@ -22028,6 +21991,7 @@ data: ${JSON.stringify(lastError)}
22028
21991
  cleanEnv: profileEnv,
22029
21992
  envOverrides,
22030
21993
  hasDeferredTools,
21994
+ earlyStop: earlyStopEnabled,
22031
21995
  resumeSessionId: undefined,
22032
21996
  isUndo: false,
22033
21997
  resumeSessionAtUuid: undefined,
@@ -22071,6 +22035,7 @@ data: ${JSON.stringify(lastError)}
22071
22035
  if (hasExtendedContext(model)) {
22072
22036
  const from = model;
22073
22037
  model = stripExtendedContext(model);
22038
+ recordExtendedContextRateLimited(profile.id, priorityCooldownUntil(profile.id, Date.now()));
22074
22039
  claudeLog("upstream.context_fallback", {
22075
22040
  mode: "non_stream",
22076
22041
  from,
@@ -22244,6 +22209,17 @@ Subprocess stderr: ${stderrOutput}`;
22244
22209
  reason: sdkTerm.reason,
22245
22210
  captured: capturedToolUses.length
22246
22211
  });
22212
+ if (lastUsage)
22213
+ logUsage(requestMeta.requestId, lastUsage);
22214
+ } else if (passthrough && sdkTerm.reason === "max_turns" && contentBlocks.length > 0) {
22215
+ lastStopReason = "max_tokens";
22216
+ claudeLog("passthrough.capped_turn_truncated", {
22217
+ mode: "non_stream",
22218
+ blocks: contentBlocks.length
22219
+ });
22220
+ plog(`[PROXY] ${requestMeta.requestId} capped turn produced no forwardable tool call — reporting as truncated`);
22221
+ if (lastUsage)
22222
+ logUsage(requestMeta.requestId, lastUsage);
22247
22223
  } else {
22248
22224
  claudeLog("upstream.failed", {
22249
22225
  mode: "non_stream",
@@ -22524,6 +22500,7 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
22524
22500
  cleanEnv: profileEnv,
22525
22501
  envOverrides,
22526
22502
  hasDeferredTools,
22503
+ earlyStop: earlyStopEnabled,
22527
22504
  resumeSessionId,
22528
22505
  isUndo,
22529
22506
  resumeSessionAtUuid: undoRollbackUuid ?? passthroughToolCallAssistantUuid,
@@ -22611,6 +22588,7 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
22611
22588
  cleanEnv: profileEnv,
22612
22589
  envOverrides,
22613
22590
  hasDeferredTools,
22591
+ earlyStop: earlyStopEnabled,
22614
22592
  resumeSessionId: undefined,
22615
22593
  isUndo: false,
22616
22594
  resumeSessionAtUuid: undefined,
@@ -22644,7 +22622,7 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
22644
22622
  if (isExtraUsageRequiredError(errMsg) && hasExtendedContext(model)) {
22645
22623
  const from = model;
22646
22624
  model = stripExtendedContext(model);
22647
- recordExtendedContextUnavailable();
22625
+ recordExtendedContextUnavailable(profile.id);
22648
22626
  claudeLog("upstream.context_fallback", {
22649
22627
  mode: "stream",
22650
22628
  from,
@@ -22680,6 +22658,7 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
22680
22658
  cleanEnv: profileEnv,
22681
22659
  envOverrides,
22682
22660
  hasDeferredTools,
22661
+ earlyStop: earlyStopEnabled,
22683
22662
  resumeSessionId: undefined,
22684
22663
  isUndo: false,
22685
22664
  resumeSessionAtUuid: undefined,
@@ -22723,6 +22702,7 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
22723
22702
  if (hasExtendedContext(model)) {
22724
22703
  const from = model;
22725
22704
  model = stripExtendedContext(model);
22705
+ recordExtendedContextRateLimited(profile.id, priorityCooldownUntil(profile.id, Date.now()));
22726
22706
  claudeLog("upstream.context_fallback", {
22727
22707
  mode: "stream",
22728
22708
  from,
@@ -23168,6 +23148,7 @@ data: ${JSON.stringify({
23168
23148
  cleanEnv: profileEnv,
23169
23149
  envOverrides,
23170
23150
  hasDeferredTools,
23151
+ earlyStop: earlyStopEnabled,
23171
23152
  resumeSessionId: currentSessionId || resumeSessionId,
23172
23153
  isUndo: false,
23173
23154
  resumeSessionAtUuid: nextPassthroughToolCallAssistantUuid,
@@ -23426,10 +23407,6 @@ data: {"type":"message_stop"}
23426
23407
  claudeLog("passthrough.client_abort_settled", { action: disposition.action });
23427
23408
  return;
23428
23409
  }
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
- }
23433
23410
  const stderrOutput = stderrLines.join(`
23434
23411
  `).trim();
23435
23412
  if (stderrOutput && error instanceof Error && !error.message.includes(stderrOutput)) {
@@ -23459,6 +23436,11 @@ Subprocess stderr: ${stderrOutput}`;
23459
23436
  capturedToolUses: capturedToolUses.length,
23460
23437
  abortIsOurs: sawDuplicateToolUse
23461
23438
  }) && messageStartEmitted;
23439
+ const recoverableCheckpoint = canRecoverAsToolUse && sdkTerm.reason === "max_turns" && Boolean(currentSessionId) && Boolean(nextPassthroughToolCallAssistantUuid) && Boolean(nextPassthroughToolCallIds?.length) && earlyStopFired && !isIndependentSession && !sawDuplicateToolUse;
23440
+ if (passthrough && streamedToolUseIds.size > 0 && !sawCanonicalResult && !recoverableCheckpoint) {
23441
+ evictSession(profileSessionId, profileScopedCwd, body.messages || []);
23442
+ claudeLog("passthrough.noncanonical_session_evicted", { mode: "stream", reason: "drain_error" });
23443
+ }
23462
23444
  if (canRecoverAsToolUse) {
23463
23445
  diagnosticLog2.session(`${requestMeta.requestId} sdk_termination_recovered ${formatSdkTermination(sdkTerm, {
23464
23446
  model,
@@ -23510,6 +23492,17 @@ data: {"type":"message_stop"}
23510
23492
 
23511
23493
  `), "recover_message_stop");
23512
23494
  recordEnvelopeViolations(checkUndeliveredToolUses(capturedToolUses, streamedToolUseIds));
23495
+ if (recoverableCheckpoint) {
23496
+ storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage, nextPassthroughToolCallAssistantUuid, nextPassthroughToolCallIds);
23497
+ commitSessionTurn();
23498
+ claudeLog("passthrough.checkpoint_persisted", {
23499
+ mode: "stream",
23500
+ reason: "single_turn_boundary",
23501
+ toolCalls: nextPassthroughToolCallIds.length
23502
+ });
23503
+ }
23504
+ if (lastUsage)
23505
+ logUsage(requestMeta.requestId, lastUsage);
23513
23506
  const recoverTotalMs = Date.now() - requestStartAt;
23514
23507
  const recoverQueueWaitMs = totalQueueWaitMs(requestMeta);
23515
23508
  telemetryStore2.record({
@@ -23540,6 +23533,11 @@ data: {"type":"message_stop"}
23540
23533
  contentBlocks: eventsForwarded + unseenToolUses.length,
23541
23534
  textEvents: textEventsForwarded,
23542
23535
  error: null,
23536
+ inputTokens: lastUsage?.input_tokens,
23537
+ outputTokens: lastUsage?.output_tokens,
23538
+ cacheReadInputTokens: lastUsage?.cache_read_input_tokens,
23539
+ cacheCreationInputTokens: lastUsage?.cache_creation_input_tokens,
23540
+ cacheHitRate: computeCacheHitRate(lastUsage),
23543
23541
  ...envelopeViolations.length > 0 ? { envelopeViolations: [...envelopeViolations] } : {}
23544
23542
  });
23545
23543
  if (!streamClosed) {
@@ -5,6 +5,72 @@ import { dirname, join } from "path";
5
5
  import { fileURLToPath } from "url";
6
6
  import { applyEdits, modify, parse as parseJsonc } from "jsonc-parser";
7
7
 
8
+ // src/utils/lruMap.ts
9
+ class LRUMap {
10
+ maxSize;
11
+ onEvict;
12
+ map = new Map;
13
+ constructor(maxSize, onEvict) {
14
+ this.maxSize = maxSize;
15
+ this.onEvict = onEvict;
16
+ }
17
+ get size() {
18
+ return this.map.size;
19
+ }
20
+ get(key) {
21
+ const value = this.map.get(key);
22
+ if (value === undefined)
23
+ return;
24
+ this.map.delete(key);
25
+ this.map.set(key, value);
26
+ return value;
27
+ }
28
+ set(key, value) {
29
+ if (this.map.has(key)) {
30
+ this.map.delete(key);
31
+ } else if (this.map.size >= this.maxSize) {
32
+ this.evictOldest();
33
+ }
34
+ this.map.set(key, value);
35
+ return this;
36
+ }
37
+ has(key) {
38
+ return this.map.has(key);
39
+ }
40
+ delete(key) {
41
+ return this.map.delete(key);
42
+ }
43
+ clear() {
44
+ this.map.clear();
45
+ }
46
+ entries() {
47
+ return this.map.entries();
48
+ }
49
+ keys() {
50
+ return this.map.keys();
51
+ }
52
+ values() {
53
+ return this.map.values();
54
+ }
55
+ forEach(callbackfn) {
56
+ this.map.forEach((value, key) => callbackfn(value, key, this));
57
+ }
58
+ [Symbol.iterator]() {
59
+ return this.map[Symbol.iterator]();
60
+ }
61
+ evictOldest() {
62
+ const oldestKey = this.map.keys().next().value;
63
+ if (oldestKey === undefined)
64
+ return;
65
+ const oldestValue = this.map.get(oldestKey);
66
+ if (oldestValue === undefined)
67
+ return;
68
+ this.map.delete(oldestKey);
69
+ this.onEvict?.(oldestKey, oldestValue);
70
+ }
71
+ }
72
+
73
+ // src/proxy/setup.ts
8
74
  class UnparseableConfigError extends Error {
9
75
  configPath;
10
76
  constructor(configPath) {
@@ -55,6 +121,22 @@ function checkPluginConfigured(configPath) {
55
121
  const plugins = Array.isArray(config.plugin) ? config.plugin : [];
56
122
  return plugins.some((p) => typeof p === "string" && isMeridianEntry(p));
57
123
  }
124
+ var pluginlessWarned = new LRUMap(256);
125
+ function clearPluginlessWarnings() {
126
+ pluginlessWarned.clear();
127
+ }
128
+ function notePluginlessOpenCodeRequest(input) {
129
+ if (!input.userAgent?.toLowerCase().startsWith("opencode/"))
130
+ return;
131
+ if (input.agentModeHeader)
132
+ return;
133
+ const key = input.sessionId || "(keyless)";
134
+ if (pluginlessWarned.get(key))
135
+ return;
136
+ pluginlessWarned.set(key, true);
137
+ const shortId = input.sessionId ? `${input.sessionId.slice(0, 12)}…` : "(no session header)";
138
+ return `OpenCode request without the Meridian plugin's agent headers (session ${shortId}). ` + `OpenCode runs its internal title/summary agents under your session id, so Meridian ` + `cannot tell them apart from your conversation: the first turn of each session can fail ` + `with a 400 or replay against a cold cache. Fix: meridian setup (or update the plugin).`;
139
+ }
58
140
  function runSetup(pluginPath, configPath) {
59
141
  const path = configPath ?? findOpencodeConfigPath();
60
142
  const dir = dirname(path);
@@ -82,4 +164,4 @@ function runSetup(pluginPath, configPath) {
82
164
  return { configPath: path, pluginPath, alreadyConfigured, removedStale, created: false };
83
165
  }
84
166
 
85
- export { UnparseableConfigError, findOpencodeConfigPath, findPluginPath, checkPluginConfigured, runSetup };
167
+ export { LRUMap, UnparseableConfigError, findOpencodeConfigPath, findPluginPath, checkPluginConfigured, clearPluginlessWarnings, notePluginlessOpenCodeRequest, runSetup };
@@ -92,7 +92,7 @@ function supports1mContext(model) {
92
92
  return false;
93
93
  return true;
94
94
  }
95
- function mapModelToClaudeModel(model, subscriptionType, agentMode) {
95
+ function mapModelToClaudeModel(model, subscriptionType, agentMode, profileId) {
96
96
  if (model.includes("haiku"))
97
97
  return "haiku";
98
98
  const use1m = supports1mContext(model);
@@ -105,7 +105,7 @@ function mapModelToClaudeModel(model, subscriptionType, agentMode) {
105
105
  if (fableOverrideRaw && fableOverride !== "fable[1m]") {
106
106
  warnUnrecognizedTierOverride("FABLE_MODEL", fableOverrideRaw, "fable");
107
107
  }
108
- if (use1m && !isSubagent && !isExtendedContextKnownUnavailable())
108
+ if (use1m && !isSubagent && !isExtendedContextKnownUnavailable(profileId))
109
109
  return "fable[1m]";
110
110
  return "fable";
111
111
  }
@@ -117,25 +117,46 @@ function mapModelToClaudeModel(model, subscriptionType, agentMode) {
117
117
  if (opusOverrideRaw && opusOverride !== "opus[1m]") {
118
118
  warnUnrecognizedTierOverride("OPUS_MODEL", opusOverrideRaw, "opus");
119
119
  }
120
- if (use1m && !isSubagent && !isExtendedContextKnownUnavailable())
120
+ if (use1m && !isSubagent && !isExtendedContextKnownUnavailable(profileId))
121
121
  return "opus[1m]";
122
122
  return "opus";
123
123
  }
124
124
  const sonnetOverride = process.env.MERIDIAN_SONNET_MODEL ?? process.env.CLAUDE_PROXY_SONNET_MODEL;
125
125
  if (sonnetOverride === "sonnet[1m]") {
126
- if (!use1m || isSubagent || isExtendedContextKnownUnavailable())
126
+ if (!use1m || isSubagent || isExtendedContextKnownUnavailable(profileId))
127
127
  return "sonnet";
128
128
  return "sonnet[1m]";
129
129
  }
130
130
  return "sonnet";
131
131
  }
132
132
  var EXTRA_USAGE_RETRY_MS = 60 * 60 * 1000;
133
- var extraUsageUnavailableAt = 0;
134
- function recordExtendedContextUnavailable() {
135
- extraUsageUnavailableAt = Date.now();
133
+ var DEFAULT_BENCH_KEY = "__default__";
134
+ var extendedContextBenchedUntil = new Map;
135
+ function benchExtendedContext(profileId, until) {
136
+ if (until <= Date.now())
137
+ return;
138
+ const key = profileId || DEFAULT_BENCH_KEY;
139
+ const existing = extendedContextBenchedUntil.get(key);
140
+ if (existing !== undefined && existing >= until)
141
+ return;
142
+ extendedContextBenchedUntil.set(key, until);
143
+ }
144
+ function recordExtendedContextUnavailable(profileId) {
145
+ benchExtendedContext(profileId, Date.now() + EXTRA_USAGE_RETRY_MS);
146
+ }
147
+ function recordExtendedContextRateLimited(profileId, until) {
148
+ benchExtendedContext(profileId, until);
136
149
  }
137
- function isExtendedContextKnownUnavailable() {
138
- return extraUsageUnavailableAt > 0 && Date.now() - extraUsageUnavailableAt < EXTRA_USAGE_RETRY_MS;
150
+ function isExtendedContextKnownUnavailable(profileId) {
151
+ const key = profileId || DEFAULT_BENCH_KEY;
152
+ const until = extendedContextBenchedUntil.get(key);
153
+ if (until === undefined)
154
+ return false;
155
+ if (until <= Date.now()) {
156
+ extendedContextBenchedUntil.delete(key);
157
+ return false;
158
+ }
159
+ return true;
139
160
  }
140
161
  function stripExtendedContext(model) {
141
162
  if (model === "opus[1m]")
@@ -855,4 +876,4 @@ Examples:
855
876
  meridian profile list # Show all profiles`);
856
877
  }
857
878
 
858
- export { env, envBool, resolvePassthrough, envInt, init_env, CANONICAL_SONNET_MODEL, resolveSdkModelDefaults, explicitModelPin, mapModelToClaudeModel, recordExtendedContextUnavailable, stripExtendedContext, hasExtendedContext, subscriptionIncludesExtendedContext, getAuthCacheInfo, getClaudeAuthStatusAsync, getResolvedClaudeExecutableInfo, resolveClaudeExecutableAsync, isClosedControllerError, OAUTH_TOKEN_URL, OAUTH_CLIENT_ID, OAUTH_REDIRECT_URI, buildAuthLoginEnv, createManualOAuthSession, parseAuthorizationCodeInput, profileAdd, profileAddOauthToken, profileList, dirsToRemoveOnProfileRemove, profileRemove, profileSwitch, profileLogin, profileHelp };
879
+ export { env, envBool, resolvePassthrough, envInt, init_env, CANONICAL_SONNET_MODEL, resolveSdkModelDefaults, explicitModelPin, mapModelToClaudeModel, recordExtendedContextUnavailable, recordExtendedContextRateLimited, stripExtendedContext, hasExtendedContext, subscriptionIncludesExtendedContext, getAuthCacheInfo, getClaudeAuthStatusAsync, getResolvedClaudeExecutableInfo, resolveClaudeExecutableAsync, isClosedControllerError, OAUTH_TOKEN_URL, OAUTH_CLIENT_ID, OAUTH_REDIRECT_URI, buildAuthLoginEnv, createManualOAuthSession, parseAuthorizationCodeInput, profileAdd, profileAddOauthToken, profileList, dirsToRemoveOnProfileRemove, profileRemove, profileSwitch, profileLogin, profileHelp };
package/dist/cli.js CHANGED
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startProxyServer
4
- } from "./cli-dya07jbg.js";
4
+ } from "./cli-21w6xm0d.js";
5
5
  import"./cli-m0p2bc8v.js";
6
6
  import"./cli-sry5aqdj.js";
7
7
  import"./cli-xmweegb1.js";
8
8
  import {
9
9
  resolveClaudeExecutableAsync
10
- } from "./cli-d45dq9gf.js";
10
+ } from "./cli-xfbhn15a.js";
11
11
  import"./cli-vj9cv18n.js";
12
- import"./cli-je60fevk.js";
12
+ import"./cli-pc0mtjjv.js";
13
13
  import"./cli-khhjyk04.js";
14
14
  import {
15
15
  __require
@@ -55,7 +55,7 @@ See https://github.com/rynfar/meridian for full documentation.`);
55
55
  process.exit(0);
56
56
  }
57
57
  if (args[0] === "profile") {
58
- const { profileAdd, profileAddOauthToken, profileList, profileRemove, profileSwitch, profileLogin, profileHelp } = await import("./profileCli-7f6yhakj.js");
58
+ const { profileAdd, profileAddOauthToken, profileList, profileRemove, profileSwitch, profileLogin, profileHelp } = await import("./profileCli-39vshwdn.js");
59
59
  const subcommand = args[1];
60
60
  const profileId = args[2];
61
61
  const headless = args.includes("--headless");
@@ -80,7 +80,7 @@ if (args[0] === "profile") {
80
80
  process.exit(0);
81
81
  }
82
82
  if (args[0] === "setup") {
83
- const { findPluginPath, runSetup, UnparseableConfigError } = await import("./setup-6c11e8d6.js");
83
+ const { findPluginPath, runSetup, UnparseableConfigError } = await import("./setup-0x573t61.js");
84
84
  const pluginPath = findPluginPath(import.meta.url);
85
85
  let result;
86
86
  try {
@@ -146,7 +146,7 @@ async function runCli(start = startProxyServer, runAuthCheck = async () => {
146
146
  return execFile(claudePath, ["auth", "status"], { timeout: 5000 });
147
147
  }) {
148
148
  try {
149
- const { findOpencodeConfigPath, checkPluginConfigured, findPluginPath } = await import("./setup-6c11e8d6.js");
149
+ const { findOpencodeConfigPath, checkPluginConfigured, findPluginPath } = await import("./setup-0x573t61.js");
150
150
  const configPath = findOpencodeConfigPath();
151
151
  const { existsSync } = await import("fs");
152
152
  if (existsSync(configPath) && !checkPluginConfigured(configPath)) {
@@ -13,7 +13,7 @@ import {
13
13
  profileLogin,
14
14
  profileRemove,
15
15
  profileSwitch
16
- } from "./cli-d45dq9gf.js";
16
+ } from "./cli-xfbhn15a.js";
17
17
  import"./cli-vj9cv18n.js";
18
18
  import"./cli-khhjyk04.js";
19
19
  import"./cli-p9swy5t3.js";
@@ -1 +1 @@
1
- {"version":3,"file":"opencode.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/opencode.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAS9C,eAAO,MAAM,eAAe,EAAE,YA+H7B,CAAA;AAED,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAC3D,OAAO,EAAE,kBAAkB,EAAE,CAAA"}
1
+ {"version":3,"file":"opencode.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/opencode.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAS9C,eAAO,MAAM,eAAe,EAAE,YAoK7B,CAAA;AAED,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAC3D,OAAO,EAAE,kBAAkB,EAAE,CAAA"}
@@ -54,7 +54,7 @@ export interface ClaudeAuthStatus {
54
54
  }
55
55
  /** Clear the per-variable warn-once tracking — for testing only. */
56
56
  export declare function resetWarnedTierOverrides(): void;
57
- export declare function mapModelToClaudeModel(model: string, subscriptionType?: string | null, agentMode?: string | null): ClaudeModel;
57
+ export declare function mapModelToClaudeModel(model: string, subscriptionType?: string | null, agentMode?: string | null, profileId?: string): ClaudeModel;
58
58
  /**
59
59
  * Record that Extra Usage is not enabled on this subscription.
60
60
  * For the next hour, mapModelToClaudeModel will return the base model
@@ -62,15 +62,26 @@ export declare function mapModelToClaudeModel(model: string, subscriptionType?:
62
62
  * the next request probes [1m] once; if Extra Usage was enabled in the
63
63
  * meantime it succeeds and the flag is never set again.
64
64
  */
65
- export declare function recordExtendedContextUnavailable(): void;
65
+ export declare function recordExtendedContextUnavailable(profileId?: string): void;
66
66
  /**
67
- * Returns true while within the cooldown window after a confirmed
68
- * Extra Usage failure. After the window expires this returns false,
69
- * allowing one probe to check whether Extra Usage has been enabled.
70
- */
71
- export declare function isExtendedContextKnownUnavailable(): boolean;
72
- /** Reset the Extended Context unavailability timer for testing only. */
73
- export declare function resetExtendedContextUnavailable(): void;
67
+ * Record that a [1m] request was rate-limited, benching it until `until`.
68
+ *
69
+ * Callers derive `until` from the account's own observed reset rather than a
70
+ * constant. Stripping [1m] on a rate limit while recording nothing is what
71
+ * makes the next request map straight back to [1m]: the conversation then
72
+ * flaps between two models and pays a cold prompt cache in BOTH directions,
73
+ * which routinely costs more than the rate limit it was routing around (#862).
74
+ */
75
+ export declare function recordExtendedContextRateLimited(profileId: string | undefined, until: number): void;
76
+ /**
77
+ * Returns true while this profile's [1m] access is benched. Expired marks are
78
+ * dropped on read, so the next request probes [1m] once — and if the window
79
+ * has genuinely reset, it simply succeeds.
80
+ */
81
+ export declare function isExtendedContextKnownUnavailable(profileId?: string): boolean;
82
+ /** Clear extended-context benches — for testing only. Clears every profile
83
+ * when no id is given. */
84
+ export declare function resetExtendedContextUnavailable(profileId?: string): void;
74
85
  /**
75
86
  * Strip the [1m] suffix from a model, returning the base variant.
76
87
  * Used for fallback when the 1M context window is rate-limited.
@@ -1 +1 @@
1
- {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/proxy/models.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/proxy/models.ts"],"names":[],"mappings":"AAAA;;GAEG;AAsBH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,YAAY,GAAG,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,WAAW,CAAA;AAEzG;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,qBAAqB,mBAAmB,CAAA;AACrD,eAAO,MAAM,oBAAoB,kBAAkB,CAAA;AACnD,eAAO,MAAM,sBAAsB,oBAAoB,CAAA;AACvD,eAAO,MAAM,qBAAqB,qBAAqB,CAAA;AAEvD;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOxB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAM3F;AACD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAuCD,oEAAoE;AACpE,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C;AAkBD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,WAAW,CA0EjJ;AAwCD;;;;;;GAMG;AACH,wBAAgB,gCAAgC,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAEzE;AAED;;;;;;;;GAQG;AACH,wBAAgB,gCAAgC,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAEnG;AAED;;;;GAIG;AACH,wBAAgB,iCAAiC,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAS7E;AAED;2BAC2B;AAC3B,wBAAgB,+BAA+B,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAGxE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,WAAW,GAAG,WAAW,CAKpE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAE9D;AAaD;;;;;;;;;;;GAWG;AACH,wBAAgB,mCAAmC,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAK7F;AAaD;gFACgF;AAChF,wBAAgB,gBAAgB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAOzH;AAWD;;;;GAIG;AACH,wBAAsB,wBAAwB,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAiE1I;AAID;;;;;;GAMG;AACH,MAAM,MAAM,sBAAsB,GAC9B,KAAK,GACL,SAAS,GACT,kBAAkB,GAClB,aAAa,GACb,eAAe,CAAA;AAEnB,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,sBAAsB,CAAA;CAC/B;AAKD;;;;;;;;;;GAUG;AACH;;;;GAIG;AACH,KAAK,YAAY,GAAG;IAClB,UAAU,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAA;IAClC,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IACzC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAClD,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAA;IAC7C,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAA;IAC5C,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAA;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,OAAO,CAAA;CACf,CAAA;AA4HD;;;;;;;;;GASG;AACH,wBAAsB,iCAAiC,CACrD,IAAI,GAAE,YAA2B,GAChC,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAYtC;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAAC,IAAI,GAAE,YAA2B,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAGvG;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,2BAA2B,CACzC,IAAI,GAAE,YAA2B,GAChC,oBAAoB,GAAG,IAAI,CAQ7B;AAED;;;;;GAKG;AACH,wBAAgB,+BAA+B,IAAI,oBAAoB,GAAG,IAAI,CAE7E;AAED,wBAAsB,4BAA4B,IAAI,OAAO,CAAC,MAAM,CAAC,CAqBpE;AAED,2CAA2C;AAC3C,wBAAgB,qBAAqB,IAAI,IAAI,CAG5C;AAED,kDAAkD;AAClD,wBAAgB,2BAA2B,IAAI,IAAI,CAOlD;AAED;;6DAE6D;AAC7D,wBAAgB,qBAAqB,IAAI,IAAI,CAO5C;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAG/D"}
@@ -13,9 +13,18 @@
13
13
  * checkpoint, but they are NOT a durability acknowledgement: a live PTY E2E
14
14
  * observed the assistant and deny in the iterator while neither existed in the
15
15
  * session JSONL after an immediate abort. The proxy therefore freezes the
16
- * assistant UUID/tool IDs at deny settlement, drains the hidden digest without
17
- * forwarding it, and stores the checkpoint only after the SDK's canonical
18
- * terminal result commits the transcript.
16
+ * assistant UUID/tool IDs at deny settlement and stores the checkpoint only
17
+ * after the SDK's canonical terminal result commits the transcript.
18
+ *
19
+ * What stops the digest turn is the maxTurns cap in query.ts, not this module:
20
+ * capped at 1, the SDK reaches the tool-use boundary and then declines to start
21
+ * another turn, so the digest never generates AND the terminal result still
22
+ * arrives (as `error_max_turns`) to commit the transcript. That is the
23
+ * combination an immediate abort could not give — it skipped the commit.
24
+ *
25
+ * This module still drains rather than aborts, because the cap is lifted for
26
+ * deferred tools, advisors, structured output, and the kill switch. In those
27
+ * configurations the digest turn does generate and is discarded here.
19
28
  *
20
29
  * Pure module — no I/O, no imports from server.ts or session/.
21
30
  */
@@ -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,gFAAgF;IAChF,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACrB,6EAA6E;IAC7E,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACrB;yEACqE;IACrE,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,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;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAatF;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAQjF;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAM5E;AAED;;;;GAIG;AACH,wBAAgB,gCAAgC,CAC9C,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,EACtD,WAAW,EAAE,SAAS,MAAM,EAAE,GAC7B,OAAO,CAuCT;AAED,8EAA8E;AAC9E,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,GAAG,SAAS,CAE1F;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAMlE;AAED,qEAAqE;AACrE,MAAM,MAAM,sBAAsB,GAC9B;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,GACnB;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAEtB;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE;IAC5C,oBAAoB,EAAE,OAAO,CAAA;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,mBAAmB,EAAE,OAAO,CAAA;IAC5B,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,oEAAoE;IACpE,WAAW,EAAE,OAAO,CAAA;CACrB,GAAG,sBAAsB,CAMzB"}
1
+ {"version":3,"file":"passthroughEarlyStop.d.ts","sourceRoot":"","sources":["../../src/proxy/passthroughEarlyStop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAUH,MAAM,WAAW,gBAAgB;IAC/B,gFAAgF;IAChF,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACrB,6EAA6E;IAC7E,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACrB;yEACqE;IACrE,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,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;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAatF;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAQjF;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAM5E;AAED;;;;GAIG;AACH,wBAAgB,gCAAgC,CAC9C,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,EACtD,WAAW,EAAE,SAAS,MAAM,EAAE,GAC7B,OAAO,CAuCT;AAED,8EAA8E;AAC9E,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,GAAG,SAAS,CAE1F;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAMlE;AAED,qEAAqE;AACrE,MAAM,MAAM,sBAAsB,GAC9B;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,GACnB;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAEtB;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE;IAC5C,oBAAoB,EAAE,OAAO,CAAA;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,mBAAmB,EAAE,OAAO,CAAA;IAC5B,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,oEAAoE;IACpE,WAAW,EAAE,OAAO,CAAA;CACrB,GAAG,sBAAsB,CAMzB"}
@@ -39,6 +39,14 @@ export interface QueryContext {
39
39
  envOverrides?: Record<string, string | undefined>;
40
40
  /** Whether any passthrough tools use deferred loading */
41
41
  hasDeferredTools: boolean;
42
+ /**
43
+ * Whether passthrough early stop is active (MERIDIAN_PASSTHROUGH_EARLY_STOP
44
+ * != "0"). Gates the single-turn maxTurns cap: the cap is only safe when the
45
+ * checkpoint machinery is running to capture and store the tool boundary.
46
+ * Defaults to on — omitting it must not silently reintroduce the billed
47
+ * digest turn.
48
+ */
49
+ earlyStop?: boolean;
42
50
  /** SDK session ID for resume (if continuing a session) */
43
51
  resumeSessionId?: string;
44
52
  /** Whether this is an undo operation */
@@ -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":"AAkBA,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;AAoDnG,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EAGpB,KAAK,aAAa,EAGnB,MAAM,mBAAmB,CAAA;AAI1B,OAAO,EAA+B,iBAAiB,EAAE,mBAAmB,EAAsC,MAAM,iBAAiB,CAAA;AAIzI,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,oBAAoB,EAAE,CAAA;AAChE,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,CAAA;AACjD,YAAY,EAAE,aAAa,EAAE,CAAA;AA0W7B,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,WAAW,CAu9JhF;AAWD,wBAAgB,gCAAgC,IAAI,IAAI,CAavD;AAED,wBAAsB,gBAAgB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAqHhG"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/proxy/server.ts"],"names":[],"mappings":"AAkBA,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;AAoDnG,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EAGpB,KAAK,aAAa,EAGnB,MAAM,mBAAmB,CAAA;AAI1B,OAAO,EAA+B,iBAAiB,EAAE,mBAAmB,EAAsC,MAAM,iBAAiB,CAAA;AAIzI,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,oBAAoB,EAAE,CAAA;AAChE,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,CAAA;AACjD,YAAY,EAAE,aAAa,EAAE,CAAA;AA0W7B,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,WAAW,CAylKhF;AAWD,wBAAgB,gCAAgC,IAAI,IAAI,CAavD;AAED,wBAAsB,gBAAgB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAqHhG"}
@@ -6,6 +6,8 @@
6
6
  * - `meridian setup` — writes the plugin entry
7
7
  * - `meridian` startup — warns if plugin is missing
8
8
  * - `GET /health` — reports plugin status
9
+ * - every request — warns when an OpenCode client sends no plugin
10
+ * headers (see notePluginlessOpenCodeRequest)
9
11
  */
10
12
  /**
11
13
  * Thrown when an existing OpenCode config can't be parsed (even tolerantly).
@@ -32,6 +34,47 @@ export declare function findPluginPath(fromUrl: string): string;
32
34
  * plugin is missing.
33
35
  */
34
36
  export declare function checkPluginConfigured(configPath?: string): boolean;
37
+ /** Reset the warned-session memory. Used by tests. */
38
+ export declare function clearPluginlessWarnings(): void;
39
+ /**
40
+ * NOTE: OpenCode-specific. Warn when an OpenCode client reaches the proxy
41
+ * without the plugin's agent headers, once per session.
42
+ *
43
+ * This exists because the exposure it reports cannot be fixed from inside
44
+ * Meridian. OpenCode 1.18.11 sends `x-session-affinity` natively, so a
45
+ * plugin-less client is fully keyed and never reaches the fingerprint fallback
46
+ * — and its internal `title` / `summary` / `compaction` agents run under the
47
+ * SAME session id as the user's chat. One real key, two unrelated
48
+ * conversations. Live, both attempts of a plugin-less run returned HTTP 400
49
+ * `session_turn_conflict` on the user's first turn after an ~8s wait.
50
+ *
51
+ * The fix for that collision scopes the session key by agent, which it reads
52
+ * from the plugin's `x-opencode-agent-mode`. A client that sends none cannot be
53
+ * scoped, and inferring the agent from request shape was tried and reverted:
54
+ * "tool-less, one message" is equally the first turn of an ordinary tool-less
55
+ * chat, and keying that apart broke resume for it.
56
+ *
57
+ * So the remaining job is to stop the exposure being silent. The startup
58
+ * warning in `bin/cli.ts` does not cover it — that one is gated on an OpenCode
59
+ * config FILE existing, deliberately, so Meridian stays quiet for the many
60
+ * clients that are not OpenCode. Run the documented
61
+ * `ANTHROPIC_BASE_URL=… opencode` with no config file and nothing warns.
62
+ *
63
+ * Keyed on the `opencode/` User-Agent rather than the resolved adapter:
64
+ * `MERIDIAN_DEFAULT_AGENT` defaults to opencode, so unrelated clients land on
65
+ * that adapter, and telling a Pi user to configure an OpenCode plugin is worse
66
+ * than saying nothing. The User-Agent has no such ambiguity.
67
+ *
68
+ * Returns the message to log, or undefined when there is nothing to say.
69
+ * Stateful but I/O-free — the caller owns the logging.
70
+ */
71
+ export declare function notePluginlessOpenCodeRequest(input: {
72
+ userAgent: string | undefined;
73
+ /** The plugin's `x-opencode-agent-mode` header, if it sent one. */
74
+ agentModeHeader: string | undefined;
75
+ /** Client session id — used only to warn once per conversation. */
76
+ sessionId: string | undefined;
77
+ }): string | undefined;
35
78
  export interface SetupResult {
36
79
  configPath: string;
37
80
  pluginPath: string;
@@ -1 +1 @@
1
- {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/proxy/setup.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH;;;;GAIG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;aACnB,UAAU,EAAE,MAAM;gBAAlB,UAAU,EAAE,MAAM;CAI/C;AAoBD;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAW/C;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGtD;AAkBD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAOlE;AAMD,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,iBAAiB,EAAE,OAAO,CAAA;IAC1B,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,OAAO,EAAE,OAAO,CAAA;CACjB;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,WAAW,CAsC7E"}
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/proxy/setup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AASH;;;;GAIG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;aACnB,UAAU,EAAE,MAAM;gBAAlB,UAAU,EAAE,MAAM;CAI/C;AAoBD;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAW/C;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGtD;AAkBD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAOlE;AAaD,sDAAsD;AACtD,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,6BAA6B,CAAC,KAAK,EAAE;IACnD,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;IAC7B,mEAAmE;IACnE,eAAe,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC,mEAAmE;IACnE,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;CAC9B,GAAG,MAAM,GAAG,SAAS,CAkBrB;AAMD,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,iBAAiB,EAAE,OAAO,CAAA;IAC1B,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,OAAO,EAAE,OAAO,CAAA;CACjB;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,WAAW,CAsC7E"}
package/dist/server.js CHANGED
@@ -11,13 +11,13 @@ import {
11
11
  runObserveHook,
12
12
  runTransformHook,
13
13
  startProxyServer
14
- } from "./cli-dya07jbg.js";
14
+ } from "./cli-21w6xm0d.js";
15
15
  import"./cli-m0p2bc8v.js";
16
16
  import"./cli-sry5aqdj.js";
17
17
  import"./cli-xmweegb1.js";
18
- import"./cli-d45dq9gf.js";
18
+ import"./cli-xfbhn15a.js";
19
19
  import"./cli-vj9cv18n.js";
20
- import"./cli-je60fevk.js";
20
+ import"./cli-pc0mtjjv.js";
21
21
  import"./cli-khhjyk04.js";
22
22
  import"./cli-p9swy5t3.js";
23
23
  export {
@@ -1,15 +1,19 @@
1
1
  import {
2
2
  UnparseableConfigError,
3
3
  checkPluginConfigured,
4
+ clearPluginlessWarnings,
4
5
  findOpencodeConfigPath,
5
6
  findPluginPath,
7
+ notePluginlessOpenCodeRequest,
6
8
  runSetup
7
- } from "./cli-je60fevk.js";
9
+ } from "./cli-pc0mtjjv.js";
8
10
  import"./cli-p9swy5t3.js";
9
11
  export {
10
12
  runSetup,
13
+ notePluginlessOpenCodeRequest,
11
14
  findPluginPath,
12
15
  findOpencodeConfigPath,
16
+ clearPluginlessWarnings,
13
17
  checkPluginConfigured,
14
18
  UnparseableConfigError
15
19
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rynfar/meridian",
3
- "version": "1.62.5",
3
+ "version": "1.62.7",
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",