@rynfar/meridian 1.57.0 → 1.57.1

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.
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  getSetting,
3
3
  setSetting
4
- } from "./cli-340h1chz.js";
4
+ } from "./cli-vj9cv18n.js";
5
5
 
6
6
  // src/proxy/profiles.ts
7
7
  import { existsSync, readFileSync } from "node:fs";
@@ -98,6 +98,34 @@ class ProfileExhaustion {
98
98
  }
99
99
  }
100
100
 
101
+ class AssignmentStore {
102
+ max;
103
+ entries = new Map;
104
+ constructor(max) {
105
+ this.max = max;
106
+ }
107
+ get(key) {
108
+ const value = this.entries.get(key);
109
+ if (value === undefined)
110
+ return;
111
+ this.entries.delete(key);
112
+ this.entries.set(key, value);
113
+ return value;
114
+ }
115
+ set(key, value) {
116
+ this.entries.delete(key);
117
+ this.entries.set(key, value);
118
+ if (this.entries.size > this.max) {
119
+ const oldest = this.entries.keys().next().value;
120
+ if (oldest !== undefined)
121
+ this.entries.delete(oldest);
122
+ }
123
+ }
124
+ get size() {
125
+ return this.entries.size;
126
+ }
127
+ }
128
+
101
129
  // src/proxy/profiles.ts
102
130
  var CONFIG_FILE = join(homedir(), ".config", "meridian", "profiles.json");
103
131
  var DISK_CACHE_TTL_MS = 5000;
@@ -213,4 +241,4 @@ function listProfiles(profiles, defaultProfile) {
213
241
  }));
214
242
  }
215
243
 
216
- export { getRoutingMode, resolvePriorityOrder, choosePriorityProfile, ProfileExhaustion, loadProfilesFromDisk, setActiveProfile, getActiveProfileId, resetActiveProfile, restoreActiveProfile, enableDiskProfileDiscovery, getEffectiveProfiles, hasProfiles, resolveProfile, listProfiles };
244
+ export { getRoutingMode, resolvePriorityOrder, choosePriorityProfile, ProfileExhaustion, AssignmentStore, loadProfilesFromDisk, setActiveProfile, getActiveProfileId, resetActiveProfile, restoreActiveProfile, enableDiskProfileDiscovery, getEffectiveProfiles, hasProfiles, resolveProfile, listProfiles };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  setSetting
3
- } from "./cli-340h1chz.js";
3
+ } from "./cli-vj9cv18n.js";
4
4
  import {
5
5
  createPlatformCredentialStore
6
6
  } from "./cli-aq5zz92m.js";
@@ -2,25 +2,30 @@
2
2
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
3
3
  import { join, dirname } from "node:path";
4
4
  import { homedir } from "node:os";
5
- var SETTINGS_FILE = join(homedir(), ".config", "meridian", "settings.json");
5
+ function settingsFile() {
6
+ const override = process.env.MERIDIAN_CONFIG_DIR;
7
+ return override ? join(override, "settings.json") : join(homedir(), ".config", "meridian", "settings.json");
8
+ }
6
9
  function loadSettings() {
10
+ const file = settingsFile();
7
11
  try {
8
- if (!existsSync(SETTINGS_FILE))
12
+ if (!existsSync(file))
9
13
  return {};
10
- return JSON.parse(readFileSync(SETTINGS_FILE, "utf-8"));
14
+ return JSON.parse(readFileSync(file, "utf-8"));
11
15
  } catch {
12
16
  return {};
13
17
  }
14
18
  }
15
19
  function saveSettings(updates) {
20
+ const file = settingsFile();
16
21
  const current = loadSettings();
17
22
  const merged = { ...current, ...updates };
18
23
  try {
19
- mkdirSync(dirname(SETTINGS_FILE), { recursive: true });
20
- writeFileSync(SETTINGS_FILE, JSON.stringify(merged, null, 2) + `
24
+ mkdirSync(dirname(file), { recursive: true });
25
+ writeFileSync(file, JSON.stringify(merged, null, 2) + `
21
26
  `, { mode: 384 });
22
27
  } catch (err) {
23
- console.warn(`[meridian] Failed to write ${SETTINGS_FILE}: ${err instanceof Error ? err.message : err}`);
28
+ console.warn(`[meridian] Failed to write ${file}: ${err instanceof Error ? err.message : err}`);
24
29
  }
25
30
  }
26
31
  function getSetting(key) {
@@ -1,4 +1,5 @@
1
1
  import {
2
+ AssignmentStore,
2
3
  ProfileExhaustion,
3
4
  choosePriorityProfile,
4
5
  getActiveProfileId,
@@ -9,7 +10,7 @@ import {
9
10
  resolveProfile,
10
11
  restoreActiveProfile,
11
12
  setActiveProfile
12
- } from "./cli-ngtexmne.js";
13
+ } from "./cli-h6hfkg3s.js";
13
14
  import {
14
15
  isTrackedPlugin,
15
16
  recordError,
@@ -47,11 +48,11 @@ import {
47
48
  resolvePassthrough,
48
49
  resolveSdkModelDefaults,
49
50
  stripExtendedContext
50
- } from "./cli-bsg2dd52.js";
51
+ } from "./cli-kjd4cwcq.js";
51
52
  import {
52
53
  getSetting,
53
54
  setSetting
54
- } from "./cli-340h1chz.js";
55
+ } from "./cli-vj9cv18n.js";
55
56
  import {
56
57
  checkPluginConfigured
57
58
  } from "./cli-je60fevk.js";
@@ -4364,24 +4365,40 @@ import { query } from "@anthropic-ai/claude-agent-sdk";
4364
4365
 
4365
4366
  // src/proxy/rateLimitStore.ts
4366
4367
  class RateLimitStore {
4367
- entries = new Map;
4368
- record(info, observedAt = Date.now()) {
4368
+ byProfile = new Map;
4369
+ record(profileId, info, observedAt = Date.now()) {
4369
4370
  if (!info || typeof info !== "object")
4370
4371
  return;
4371
4372
  const key = info.rateLimitType ?? "default";
4372
- this.entries.set(key, { ...info, observedAt });
4373
- }
4374
- getAll() {
4375
- return Array.from(this.entries.values()).sort((a, b) => b.observedAt - a.observedAt);
4376
- }
4377
- get(key) {
4378
- return this.entries.get(key);
4379
- }
4380
- get size() {
4381
- return this.entries.size;
4373
+ let buckets = this.byProfile.get(profileId);
4374
+ if (!buckets) {
4375
+ buckets = new Map;
4376
+ this.byProfile.set(profileId, buckets);
4377
+ }
4378
+ buckets.set(key, { ...info, observedAt });
4382
4379
  }
4383
- clear() {
4384
- this.entries.clear();
4380
+ getAll(profileId) {
4381
+ const buckets = this.byProfile.get(profileId);
4382
+ if (!buckets)
4383
+ return [];
4384
+ return Array.from(buckets.values()).sort((a, b) => b.observedAt - a.observedAt);
4385
+ }
4386
+ get(profileId, key) {
4387
+ return this.byProfile.get(profileId)?.get(key);
4388
+ }
4389
+ size(profileId) {
4390
+ if (profileId !== undefined)
4391
+ return this.byProfile.get(profileId)?.size ?? 0;
4392
+ let total = 0;
4393
+ for (const buckets of this.byProfile.values())
4394
+ total += buckets.size;
4395
+ return total;
4396
+ }
4397
+ clear(profileId) {
4398
+ if (profileId !== undefined)
4399
+ this.byProfile.delete(profileId);
4400
+ else
4401
+ this.byProfile.clear();
4385
4402
  }
4386
4403
  }
4387
4404
  var rateLimitStore = new RateLimitStore;
@@ -11086,13 +11103,25 @@ function buildModelList(isMaxSubscription, now = Math.floor(Date.now() / 1000))
11086
11103
  }
11087
11104
 
11088
11105
  // src/proxy/openaiResponses.ts
11106
+ function itemDiscriminator(item) {
11107
+ if (typeof item !== "object" || item === null)
11108
+ return;
11109
+ const record = item;
11110
+ if (typeof record.type === "string")
11111
+ return record.type;
11112
+ if (record.type === undefined && "role" in record)
11113
+ return "message";
11114
+ return;
11115
+ }
11089
11116
  function partsToText(content) {
11117
+ if (content === undefined)
11118
+ return "";
11090
11119
  if (typeof content === "string")
11091
11120
  return content;
11092
11121
  return content.filter((p) => typeof p.text === "string").map((p) => p.text).join("");
11093
11122
  }
11094
11123
  function partsToBlocks(content) {
11095
- if (typeof content === "string")
11124
+ if (content === undefined || typeof content === "string")
11096
11125
  return null;
11097
11126
  const hasImage = content.some((p) => p.type === "input_image");
11098
11127
  if (!hasImage)
@@ -11141,7 +11170,7 @@ function translateResponsesToAnthropic(body) {
11141
11170
  }
11142
11171
  };
11143
11172
  for (const item of items) {
11144
- switch (item.type) {
11173
+ switch (itemDiscriminator(item)) {
11145
11174
  case "message": {
11146
11175
  const msg = item;
11147
11176
  if (msg.role === "developer" || msg.role === "system") {
@@ -11833,6 +11862,12 @@ function getConversationFingerprint(messages, workingDirectory) {
11833
11862
  ${text.slice(0, 2000)}` : text.slice(0, 2000);
11834
11863
  return createHash("sha256").update(seed).digest("hex").slice(0, 16);
11835
11864
  }
11865
+ function getPriorityAssignmentKey(sessionId, messages, workingDirectory) {
11866
+ if (sessionId)
11867
+ return sessionId;
11868
+ const fingerprint = getConversationFingerprint(messages, workingDirectory);
11869
+ return fingerprint ? `fp:${fingerprint}` : null;
11870
+ }
11836
11871
 
11837
11872
  // src/proxy/adapters/opencode.ts
11838
11873
  init_env();
@@ -19189,22 +19224,20 @@ function findSuffixAnchorStart(storedHashes, incomingHashes, suffixOverlap) {
19189
19224
  return -1;
19190
19225
  return anchor - suffixOverlap + 1;
19191
19226
  }
19192
- function verifyLineage(cached, messages, cacheKey2, cache) {
19227
+ function verifyLineage(cached, messages) {
19193
19228
  if (!cached.lineageHash || cached.messageCount === 0) {
19194
- return { type: "continuation", session: cached };
19229
+ return { type: "diverged", reason: "unverifiable" };
19195
19230
  }
19196
19231
  const prefix = messages.slice(0, cached.messageCount);
19197
19232
  const prefixHash = computeLineageHash(prefix);
19198
19233
  if (prefixHash === cached.lineageHash) {
19199
19234
  if (messages.length <= cached.messageCount) {
19200
- cache.delete(cacheKey2);
19201
- return { type: "diverged" };
19235
+ return { type: "diverged", reason: "replayed-request" };
19202
19236
  }
19203
- return { type: "continuation", session: cached };
19237
+ return { type: "continuation", session: cached, resumeFrom: cached.messageCount };
19204
19238
  }
19205
19239
  if (!cached.messageHashes || cached.messageHashes.length === 0) {
19206
- cache.delete(cacheKey2);
19207
- return { type: "diverged" };
19240
+ return { type: "diverged", reason: "unverifiable" };
19208
19241
  }
19209
19242
  const incomingHashes = computeMessageHashes(messages);
19210
19243
  const prefixOverlap = measurePrefixOverlap(cached.messageHashes, incomingHashes);
@@ -19212,13 +19245,12 @@ function verifyLineage(cached, messages, cacheKey2, cache) {
19212
19245
  const MIN_STORED_FOR_COMPACTION = 6;
19213
19246
  const suffixStartInIncoming = incomingHashes.length - suffixOverlap >= 0 ? findSuffixAnchorStart(cached.messageHashes, incomingHashes, suffixOverlap) : -1;
19214
19247
  if (suffixOverlap >= MIN_SUFFIX_FOR_COMPACTION && cached.messageHashes.length >= MIN_STORED_FOR_COMPACTION && suffixStartInIncoming > 0) {
19215
- const compactionMsg = `Compaction detected (key=${cacheKey2.slice(0, 8)}…): suffix overlap ${suffixOverlap}/${cached.messageHashes.length}. Allowing resume.`;
19216
- console.error(`[PROXY] ${compactionMsg}`);
19217
- diagnosticLog2.lineage(compactionMsg);
19218
- cached.lineageHash = computeLineageHash(messages);
19219
- cached.messageHashes = incomingHashes;
19220
- cached.messageCount = messages.length;
19221
- return { type: "compaction", session: cached };
19248
+ return {
19249
+ type: "compaction",
19250
+ session: cached,
19251
+ resumeFrom: suffixStartInIncoming + suffixOverlap,
19252
+ suffixOverlap
19253
+ };
19222
19254
  }
19223
19255
  if (prefixOverlap > 0 && suffixOverlap === 0 && messages.length <= cached.messageCount) {
19224
19256
  let rollbackUuid;
@@ -19230,22 +19262,12 @@ function verifyLineage(cached, messages, cacheKey2, cache) {
19230
19262
  }
19231
19263
  }
19232
19264
  }
19233
- const undoMsg = `Undo detected (key=${cacheKey2.slice(0, 8)}…): prefix overlap ${prefixOverlap}/${cached.messageHashes.length}, rollback UUID: ${rollbackUuid || "none (legacy session)"}.`;
19234
- console.error(`[PROXY] ${undoMsg}`);
19235
- diagnosticLog2.lineage(undoMsg);
19236
19265
  return { type: "undo", session: cached, prefixOverlap, rollbackUuid };
19237
19266
  }
19238
19267
  if (prefixOverlap > 0 && messages.length > cached.messageCount) {
19239
- const modifiedMsg = `Modified continuation (key=${cacheKey2.slice(0, 8)}…): prefix overlap ${prefixOverlap}/${cached.messageHashes.length}, incoming ${messages.length} msgs. Allowing resume.`;
19240
- console.error(`[PROXY] ${modifiedMsg}`);
19241
- diagnosticLog2.lineage(modifiedMsg);
19242
- cached.lineageHash = computeLineageHash(messages.slice(0, messages.length));
19243
- cached.messageHashes = incomingHashes;
19244
- cached.messageCount = messages.length;
19245
- return { type: "continuation", session: cached };
19268
+ return { type: "diverged", reason: "modified-history", prefixOverlap };
19246
19269
  }
19247
- cache.delete(cacheKey2);
19248
- return { type: "diverged" };
19270
+ return { type: "diverged", reason: "unrelated-history", prefixOverlap };
19249
19271
  }
19250
19272
 
19251
19273
  // src/proxy/sessionStore.ts
@@ -19546,11 +19568,28 @@ function touchSession(state) {
19546
19568
  state.lastAccess = Date.now();
19547
19569
  return state;
19548
19570
  }
19571
+ function classifyLineage(state, messages, cacheKey2) {
19572
+ const result = verifyLineage(state, messages);
19573
+ if (result.type === "compaction") {
19574
+ const msg = `Compaction detected (key=${cacheKey2.slice(0, 8)}…): suffix overlap ${result.suffixOverlap}/${state.messageCount}, resume from incoming message ${result.resumeFrom}.`;
19575
+ console.error(`[PROXY] ${msg}`);
19576
+ diagnosticLog2.lineage(msg);
19577
+ } else if (result.type === "undo") {
19578
+ const msg = `Undo detected (key=${cacheKey2.slice(0, 8)}…): prefix overlap ${result.prefixOverlap}/${state.messageCount}, rollback UUID: ${result.rollbackUuid || "none (legacy session)"}.`;
19579
+ console.error(`[PROXY] ${msg}`);
19580
+ diagnosticLog2.lineage(msg);
19581
+ } else if (result.type === "diverged" && result.reason === "modified-history") {
19582
+ const msg = `Stale session detected (key=${cacheKey2.slice(0, 8)}…): prefix overlap ${result.prefixOverlap || 0}/${state.messageCount}, incoming ${messages.length} msgs. Starting fresh replay.`;
19583
+ console.error(`[PROXY] ${msg}`);
19584
+ diagnosticLog2.lineage(msg);
19585
+ }
19586
+ return result;
19587
+ }
19549
19588
  function lookupSession(sessionId, messages, workingDirectory) {
19550
19589
  if (sessionId) {
19551
19590
  const cached = sessionCache.get(sessionId);
19552
19591
  if (cached) {
19553
- const result = verifyLineage(cached, messages, sessionId, sessionCache);
19592
+ const result = classifyLineage(cached, messages, sessionId);
19554
19593
  if (result.type === "continuation" || result.type === "compaction")
19555
19594
  touchSession(result.session);
19556
19595
  return result;
@@ -19566,19 +19605,19 @@ function lookupSession(sessionId, messages, workingDirectory) {
19566
19605
  sdkMessageUuids: shared.sdkMessageUuids,
19567
19606
  contextUsage: shared.contextUsage
19568
19607
  };
19569
- const result = verifyLineage(state, messages, sessionId, sessionCache);
19608
+ const result = classifyLineage(state, messages, sessionId);
19570
19609
  if (result.type === "continuation" || result.type === "compaction") {
19571
19610
  sessionCache.set(sessionId, state);
19572
19611
  }
19573
19612
  return result;
19574
19613
  }
19575
- return { type: "diverged" };
19614
+ return { type: "diverged", reason: "not-found" };
19576
19615
  }
19577
19616
  const fp = getConversationFingerprint(messages, workingDirectory);
19578
19617
  if (fp) {
19579
19618
  const cached = fingerprintCache.get(fp);
19580
19619
  if (cached) {
19581
- const result = verifyLineage(cached, messages, fp, fingerprintCache);
19620
+ const result = classifyLineage(cached, messages, fp);
19582
19621
  if (result.type === "continuation" || result.type === "compaction")
19583
19622
  touchSession(result.session);
19584
19623
  return result;
@@ -19594,14 +19633,14 @@ function lookupSession(sessionId, messages, workingDirectory) {
19594
19633
  sdkMessageUuids: shared.sdkMessageUuids,
19595
19634
  contextUsage: shared.contextUsage
19596
19635
  };
19597
- const result = verifyLineage(state, messages, fp, fingerprintCache);
19636
+ const result = classifyLineage(state, messages, fp);
19598
19637
  if (result.type === "continuation" || result.type === "compaction") {
19599
19638
  fingerprintCache.set(fp, state);
19600
19639
  }
19601
19640
  return result;
19602
19641
  }
19603
19642
  }
19604
- return { type: "diverged" };
19643
+ return { type: "diverged", reason: "not-found" };
19605
19644
  }
19606
19645
  function getSessionByClaudeId(claudeSessionId) {
19607
19646
  let newest;
@@ -19905,8 +19944,8 @@ function createProxyServer(config = {}) {
19905
19944
  app.use("/settings", requireAuth);
19906
19945
  app.use("/design-login", requireAuth);
19907
19946
  const priorityExhaustion = new ProfileExhaustion;
19908
- const priorityAssignments = new Map;
19909
19947
  const PRIORITY_ASSIGNMENTS_MAX = 5000;
19948
+ const priorityAssignments = new AssignmentStore(PRIORITY_ASSIGNMENTS_MAX);
19910
19949
  const PRIORITY_DEFAULT_COOLDOWN_MS = 10 * 60000;
19911
19950
  const PRIORITY_COOLDOWN_CAP_MS = 6 * 60 * 60000;
19912
19951
  function priorityProfileOrderSetting() {
@@ -19916,11 +19955,33 @@ function createProxyServer(config = {}) {
19916
19955
  const setting = getSetting("profileOrder");
19917
19956
  return Array.isArray(setting) && setting.length > 0 ? setting : undefined;
19918
19957
  }
19919
- function priorityCooldownUntil(now) {
19920
- const fiveHour = rateLimitStore.getAll().find((e) => e.rateLimitType === "five_hour" && (e.resetsAt ?? 0) > now);
19958
+ function priorityCooldownUntil(profileId, now) {
19959
+ const fiveHour = rateLimitStore.getAll(profileId).find((e) => e.rateLimitType === "five_hour" && (e.resetsAt ?? 0) > now && (e.status === "rejected" || (e.utilization ?? 0) >= 1));
19921
19960
  const until = fiveHour?.resetsAt ?? now + PRIORITY_DEFAULT_COOLDOWN_MS;
19922
19961
  return Math.min(until, now + PRIORITY_COOLDOWN_CAP_MS);
19923
19962
  }
19963
+ function refinePriorityCooldown(profileId) {
19964
+ const target = getEffectiveProfiles(finalConfig.profiles).find((p) => p.id === profileId);
19965
+ fetchOAuthUsage({ profileId, claudeConfigDir: target?.claudeConfigDir, force: true }).then((usage) => {
19966
+ if (!usage || usage.stale)
19967
+ return;
19968
+ const fiveHour = usage.windows.find((w) => w.type === "five_hour");
19969
+ if (!fiveHour || (fiveHour.utilization ?? 0) < 1)
19970
+ return;
19971
+ const now = Date.now();
19972
+ const resetsAt = fiveHour.resetsAt;
19973
+ if (!resetsAt || resetsAt <= now)
19974
+ return;
19975
+ const until = Math.min(resetsAt, now + PRIORITY_COOLDOWN_CAP_MS);
19976
+ priorityExhaustion.mark(profileId, until, "rate_limit_error");
19977
+ claudeLog("priority.cooldown_refined", { profile: profileId, until, source: "oauth_usage" });
19978
+ }).catch((err) => {
19979
+ claudeLog("priority.cooldown_refine_failed", {
19980
+ profile: profileId,
19981
+ error: err instanceof Error ? err.message : String(err)
19982
+ });
19983
+ });
19984
+ }
19924
19985
  async function sniffQuotaFailure(res) {
19925
19986
  const contentType = res.headers.get("content-type") ?? "";
19926
19987
  if (!contentType.includes("text/event-stream")) {
@@ -19995,22 +20056,18 @@ function createProxyServer(config = {}) {
19995
20056
  const inner = await app.fetch(new Request(c.req.url, { method: "POST", headers, body: bodyBuf }));
19996
20057
  const { failed, errorPayload, response } = await sniffQuotaFailure(inner);
19997
20058
  if (!failed) {
19998
- if (sessionKey) {
20059
+ if (sessionKey)
19999
20060
  priorityAssignments.set(sessionKey, candidate);
20000
- if (priorityAssignments.size > PRIORITY_ASSIGNMENTS_MAX) {
20001
- const oldest = priorityAssignments.keys().next().value;
20002
- if (oldest !== undefined)
20003
- priorityAssignments.delete(oldest);
20004
- }
20005
- }
20006
20061
  if (previous) {
20007
20062
  claudeLog("profile.failover", { from: previous, to: candidate, reason: "rate_limit_error", sessionKey });
20008
20063
  plog(`[PROXY] PRIORITY failover ${previous} -> ${candidate}`);
20009
20064
  }
20010
20065
  return response;
20011
20066
  }
20012
- priorityExhaustion.mark(candidate, priorityCooldownUntil(Date.now()), "rate_limit_error");
20013
- claudeLog("priority.exhausted", { profile: candidate, until: priorityCooldownUntil(Date.now()) });
20067
+ const cooldownUntil = priorityCooldownUntil(candidate, Date.now());
20068
+ priorityExhaustion.mark(candidate, cooldownUntil, "rate_limit_error");
20069
+ claudeLog("priority.exhausted", { profile: candidate, until: cooldownUntil });
20070
+ refinePriorityCooldown(candidate);
20014
20071
  lastError = errorPayload;
20015
20072
  previous = candidate;
20016
20073
  }
@@ -20098,7 +20155,8 @@ data: ${JSON.stringify(lastError)}
20098
20155
  const { order, unknown } = resolvePriorityOrder(effectivePool.map((p) => p.id), priorityProfileOrderSetting());
20099
20156
  if (unknown.length > 0)
20100
20157
  claudeLog("priority.unknown_order_ids", { unknown });
20101
- const sessionKey = adapter.getSessionId(c, body) || null;
20158
+ const assignmentCwd = adapter.extractClientWorkingDirectory?.(body) ?? adapter.extractWorkingDirectory(body);
20159
+ const sessionKey = getPriorityAssignmentKey(adapter.getSessionId(c, body), body.messages, assignmentCwd);
20102
20160
  const assigned = sessionKey ? priorityAssignments.get(sessionKey) : undefined;
20103
20161
  let first;
20104
20162
  if (assigned && order.includes(assigned) && !priorityExhaustion.isExhausted(assigned)) {
@@ -20224,14 +20282,15 @@ data: ${JSON.stringify(lastError)}
20224
20282
  claudeLog("session.pending_store_awaited", { waitedMs: Date.now() - waitStart });
20225
20283
  }
20226
20284
  }
20227
- let lineageResult = isIndependentSession ? { type: "diverged" } : lookupSession(profileSessionId, body.messages || [], profileScopedCwd);
20285
+ let lineageResult = isIndependentSession ? { type: "diverged", reason: "independent-request" } : lookupSession(profileSessionId, body.messages || [], profileScopedCwd);
20228
20286
  if (lineageResult.type === "undo" && adapterBase === "opencode" && !agentSessionId) {
20229
- lineageResult = { type: "diverged" };
20287
+ lineageResult = { type: "diverged", reason: "missing-session-header" };
20230
20288
  }
20231
20289
  const isResume = lineageResult.type === "continuation" || lineageResult.type === "compaction";
20232
20290
  const isUndo = lineageResult.type === "undo";
20233
20291
  const cachedSession = lineageResult.type !== "diverged" ? lineageResult.session : undefined;
20234
20292
  const resumeSessionId = cachedSession?.claudeSessionId;
20293
+ const resumeFrom = lineageResult.type === "continuation" || lineageResult.type === "compaction" ? lineageResult.resumeFrom : undefined;
20235
20294
  const undoRollbackUuid = isUndo && lineageResult.type === "undo" ? lineageResult.rollbackUuid : undefined;
20236
20295
  const msgSummary = body.messages?.map((m) => {
20237
20296
  const contentTypes = Array.isArray(m.content) ? m.content.map((b) => b.type).join(",") : "string";
@@ -20274,9 +20333,8 @@ data: ${JSON.stringify(lastError)}
20274
20333
  if (isUndo && undoRollbackUuid) {
20275
20334
  messagesToConvert = getLastUserMessage(allMessages);
20276
20335
  } else if (isResume) {
20277
- const knownCount = cachedSession.messageCount || 0;
20278
- if (knownCount > 0 && knownCount < allMessages.length) {
20279
- messagesToConvert = allMessages.slice(knownCount);
20336
+ if (resumeFrom !== undefined && resumeFrom < allMessages.length) {
20337
+ messagesToConvert = allMessages.slice(resumeFrom);
20280
20338
  } else {
20281
20339
  messagesToConvert = getLastUserMessage(allMessages);
20282
20340
  }
@@ -20569,7 +20627,7 @@ data: ${JSON.stringify(lastError)}
20569
20627
  advisorModel
20570
20628
  }, requestAbort.controller))) {
20571
20629
  if (event.type === "rate_limit_event") {
20572
- rateLimitStore.record(event.rate_limit_info);
20630
+ rateLimitStore.record(profile.id, event.rate_limit_info);
20573
20631
  }
20574
20632
  if (event.type === "assistant" && !event.error) {
20575
20633
  didYieldContent = true;
@@ -21151,7 +21209,7 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
21151
21209
  advisorModel
21152
21210
  }, requestAbort.controller))) {
21153
21211
  if (event.type === "rate_limit_event") {
21154
- rateLimitStore.record(event.rate_limit_info);
21212
+ rateLimitStore.record(profile.id, event.rate_limit_info);
21155
21213
  }
21156
21214
  if (event.type === "stream_event") {
21157
21215
  didYieldClientEvent = true;
@@ -22302,7 +22360,6 @@ data: ${JSON.stringify({
22302
22360
  const previousProfile = getActiveProfileId() ?? null;
22303
22361
  setActiveProfile(body.profile);
22304
22362
  clearSessionCache();
22305
- rateLimitStore.clear();
22306
22363
  claudeLog("profile.switched", {
22307
22364
  from: previousProfile,
22308
22365
  to: body.profile,
@@ -22356,7 +22413,7 @@ data: ${JSON.stringify({
22356
22413
  const store = credentialStoreForProfile(profile);
22357
22414
  const success = store ? await refreshOAuthToken(store) : false;
22358
22415
  if (success) {
22359
- rateLimitStore.clear();
22416
+ rateLimitStore.clear(profile.id);
22360
22417
  return c.json({ success: true, message: "OAuth token refreshed successfully", profile: profile.id });
22361
22418
  }
22362
22419
  return c.json({ success: false, message: "Token refresh failed. If the problem persists, run 'claude login'." }, 500);
@@ -22579,11 +22636,11 @@ data: ${JSON.stringify({ response: { id: responseId, status: "failed", error: {
22579
22636
  return c.json({ object: "list", data: buildModelList(isMax) });
22580
22637
  });
22581
22638
  app.get("/v1/usage/quota", async (c) => {
22582
- const sdkEntries = rateLimitStore.getAll().filter((entry) => entry.rateLimitType !== undefined);
22583
22639
  const requestedProfile = c.req.query("profile");
22584
22640
  const profilesList = getEffectiveProfiles(finalConfig.profiles);
22585
22641
  const targetProfileId = requestedProfile || getActiveProfileId() || finalConfig.defaultProfile || profilesList[0]?.id || null;
22586
22642
  const targetProfile = targetProfileId ? profilesList.find((p) => p.id === targetProfileId) : undefined;
22643
+ const sdkEntries = rateLimitStore.getAll(targetProfileId ?? "default").filter((entry) => entry.rateLimitType !== undefined);
22587
22644
  const oauth = await fetchOAuthUsage({
22588
22645
  profileId: targetProfileId ?? undefined,
22589
22646
  claudeConfigDir: targetProfile?.claudeConfigDir
package/dist/cli.js CHANGED
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startProxyServer
4
- } from "./cli-sz964j04.js";
5
- import"./cli-ngtexmne.js";
4
+ } from "./cli-wszp24mg.js";
5
+ import"./cli-h6hfkg3s.js";
6
6
  import"./cli-sry5aqdj.js";
7
7
  import"./cli-xmweegb1.js";
8
8
  import {
9
9
  resolveClaudeExecutableAsync
10
- } from "./cli-bsg2dd52.js";
11
- import"./cli-340h1chz.js";
10
+ } from "./cli-kjd4cwcq.js";
11
+ import"./cli-vj9cv18n.js";
12
12
  import"./cli-je60fevk.js";
13
13
  import"./cli-aq5zz92m.js";
14
14
  import {
@@ -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-00z04stw.js");
58
+ const { profileAdd, profileAddOauthToken, profileList, profileRemove, profileSwitch, profileLogin, profileHelp } = await import("./profileCli-b1zmg2ad.js");
59
59
  const subcommand = args[1];
60
60
  const profileId = args[2];
61
61
  const headless = args.includes("--headless");
@@ -171,7 +171,7 @@ async function runCli(start = startProxyServer, runAuthCheck = async () => {
171
171
  console.error("\x1B[33m⚠ Could not verify Claude auth status. If requests fail, run: claude login\x1B[0m");
172
172
  }
173
173
  if (!profiles) {
174
- const { enableDiskProfileDiscovery } = await import("./profiles-sq9t3fh9.js");
174
+ const { enableDiskProfileDiscovery } = await import("./profiles-3rg4x7cz.js");
175
175
  enableDiskProfileDiscovery();
176
176
  }
177
177
  const proxy = await start({ port, host, idleTimeoutSeconds, pluginDir, pluginConfigPath, profiles, defaultProfile, version, installProcessErrorHandlers: true });
@@ -13,8 +13,8 @@ import {
13
13
  profileLogin,
14
14
  profileRemove,
15
15
  profileSwitch
16
- } from "./cli-bsg2dd52.js";
17
- import"./cli-340h1chz.js";
16
+ } from "./cli-kjd4cwcq.js";
17
+ import"./cli-vj9cv18n.js";
18
18
  import"./cli-aq5zz92m.js";
19
19
  import"./cli-p9swy5t3.js";
20
20
  export {
@@ -9,8 +9,8 @@ import {
9
9
  resolveProfile,
10
10
  restoreActiveProfile,
11
11
  setActiveProfile
12
- } from "./cli-ngtexmne.js";
13
- import"./cli-340h1chz.js";
12
+ } from "./cli-h6hfkg3s.js";
13
+ import"./cli-vj9cv18n.js";
14
14
  import"./cli-p9swy5t3.js";
15
15
  export {
16
16
  setActiveProfile,
@@ -24,9 +24,11 @@ interface ResponsesContentPart {
24
24
  image_url?: string;
25
25
  }
26
26
  interface ResponsesMessageItem {
27
- type: "message";
27
+ /** Optional per the spec — `EasyInputMessage` requires only `role`. */
28
+ type?: "message";
28
29
  role: "user" | "assistant" | "developer" | "system";
29
- content: ResponsesContentPart[] | string;
30
+ /** Also optional per the spec: `EasyInputMessage` requires only `role`. */
31
+ content?: ResponsesContentPart[] | string;
30
32
  }
31
33
  interface ResponsesFunctionCallItem {
32
34
  type: "function_call";
@@ -1 +1 @@
1
- {"version":3,"file":"openaiResponses.d.ts","sourceRoot":"","sources":["../../src/proxy/openaiResponses.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"openaiResponses.d.ts","sourceRoot":"","sources":["../../src/proxy/openaiResponses.ts"],"names":[],"mappings":"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"}
@@ -27,13 +27,16 @@
27
27
  * in memory. State resets on proxy restart — that's fine because the SDK will
28
28
  * push a fresh event on the next request.
29
29
  *
30
- * Singleton one Meridian process holds one snapshot at a time. With
31
- * multi-profile setups (`x-meridian-profile` / `POST /profiles/active`)
32
- * each profile is a separate Claude Max subscription with separate quotas,
33
- * so the store is **cleared on profile switch and on `/auth/refresh`** —
34
- * the next SDK call repopulates it for the active profile. Consumers of
35
- * `/v1/usage/quota` should treat the snapshot as "the active profile's
36
- * latest known state" and re-fetch after switching profiles.
30
+ * Entries are scoped per profile. Each profile is a separate Claude Max
31
+ * subscription with separate quotas, so a flat store would let one account's
32
+ * reset time be read as another's which is exactly how priority routing
33
+ * (`routing: "priority"`) mis-derived exhaustion cooldowns before this was
34
+ * scoped. Every read names its profile explicitly; single-profile setups
35
+ * simply use the literal `"default"` key.
36
+ *
37
+ * `clear(profileId)` drops one account (wired into `POST /auth/refresh`,
38
+ * which re-authenticates one credential); `clear()` drops everything and is
39
+ * used by tests.
37
40
  */
38
41
  import type { SDKRateLimitInfo } from "@anthropic-ai/claude-agent-sdk";
39
42
  export interface RateLimitEntry extends SDKRateLimitInfo {
@@ -43,29 +46,30 @@ export interface RateLimitEntry extends SDKRateLimitInfo {
43
46
  /** Type discriminator for the entry's bucket key. */
44
47
  export type RateLimitBucketKey = NonNullable<SDKRateLimitInfo["rateLimitType"]> | "default";
45
48
  declare class RateLimitStore {
46
- private entries;
49
+ /** profileId -> (bucket key -> entry). One inner map per configured profile. */
50
+ private byProfile;
47
51
  /**
48
- * Record a rate-limit info snapshot.
49
- * Last-write-wins per bucket key (rateLimitType). Older entries for the
50
- * same key are overwritten — clients should treat the latest as canonical.
52
+ * Record a rate-limit info snapshot for a specific profile.
53
+ * Last-write-wins per (profileId, rateLimitType). Older entries for the
54
+ * same pair are overwritten — clients should treat the latest as canonical.
51
55
  *
52
56
  * `observedAt` defaults to the current wall clock but may be supplied
53
57
  * explicitly so callers (and tests) can control the capture timestamp used
54
58
  * for newest-first ordering in {@link getAll}.
55
59
  */
56
- record(info: SDKRateLimitInfo | undefined | null, observedAt?: number): void;
57
- /** Snapshot all current entries, newest-first by observedAt. */
58
- getAll(): RateLimitEntry[];
59
- /** Snapshot a single bucket, or undefined if not yet seen. */
60
- get(key: RateLimitBucketKey): RateLimitEntry | undefined;
61
- /** Number of distinct buckets observed. */
62
- get size(): number;
60
+ record(profileId: string, info: SDKRateLimitInfo | undefined | null, observedAt?: number): void;
61
+ /** Snapshot one profile's entries, newest-first by observedAt. */
62
+ getAll(profileId: string): RateLimitEntry[];
63
+ /** Snapshot a single bucket for one profile, or undefined if not yet seen. */
64
+ get(profileId: string, key: RateLimitBucketKey): RateLimitEntry | undefined;
65
+ /** Bucket count for one profile, or across all profiles when omitted. */
66
+ size(profileId?: string): number;
63
67
  /**
64
- * Drop all stored entries. Wired into the `POST /profiles/active` and
65
- * `POST /auth/refresh` handlers so quotas can't leak across profiles or
66
- * stale credential boundaries. Also used by tests for isolation.
68
+ * Drop stored entries for one profile, or every profile when `profileId`
69
+ * is omitted. Wired into the `POST /auth/refresh` handler so a refreshed
70
+ * credential's stale quotas can't linger. Also used by tests for isolation.
67
71
  */
68
- clear(): void;
72
+ clear(profileId?: string): void;
69
73
  }
70
74
  /**
71
75
  * Process-wide singleton. Importers should always use this instance — do
@@ -1 +1 @@
1
- {"version":3,"file":"rateLimitStore.d.ts","sourceRoot":"","sources":["../../src/proxy/rateLimitStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAA;AAEtE,MAAM,WAAW,cAAe,SAAQ,gBAAgB;IACtD,+CAA+C;IAC/C,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,qDAAqD;AACrD,MAAM,MAAM,kBAAkB,GAAG,WAAW,CAAC,gBAAgB,CAAC,eAAe,CAAC,CAAC,GAAG,SAAS,CAAA;AAE3F,cAAM,cAAc;IAClB,OAAO,CAAC,OAAO,CAAgD;IAE/D;;;;;;;;OAQG;IACH,MAAM,CAAC,IAAI,EAAE,gBAAgB,GAAG,SAAS,GAAG,IAAI,EAAE,UAAU,GAAE,MAAmB,GAAG,IAAI;IAMxF,gEAAgE;IAChE,MAAM,IAAI,cAAc,EAAE;IAI1B,8DAA8D;IAC9D,GAAG,CAAC,GAAG,EAAE,kBAAkB,GAAG,cAAc,GAAG,SAAS;IAIxD,2CAA2C;IAC3C,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;;;OAIG;IACH,KAAK,IAAI,IAAI;CAGd;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,gBAAuB,CAAA;AAElD,wCAAwC;AACxC,OAAO,EAAE,cAAc,IAAI,uBAAuB,EAAE,CAAA"}
1
+ {"version":3,"file":"rateLimitStore.d.ts","sourceRoot":"","sources":["../../src/proxy/rateLimitStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAA;AAEtE,MAAM,WAAW,cAAe,SAAQ,gBAAgB;IACtD,+CAA+C;IAC/C,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,qDAAqD;AACrD,MAAM,MAAM,kBAAkB,GAAG,WAAW,CAAC,gBAAgB,CAAC,eAAe,CAAC,CAAC,GAAG,SAAS,CAAA;AAE3F,cAAM,cAAc;IAClB,gFAAgF;IAChF,OAAO,CAAC,SAAS,CAA6D;IAE9E;;;;;;;;OAQG;IACH,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,GAAG,SAAS,GAAG,IAAI,EAAE,UAAU,GAAE,MAAmB,GAAG,IAAI;IAW3G,kEAAkE;IAClE,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,EAAE;IAM3C,8EAA8E;IAC9E,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,kBAAkB,GAAG,cAAc,GAAG,SAAS;IAI3E,yEAAyE;IACzE,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM;IAOhC;;;;OAIG;IACH,KAAK,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;CAIhC;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,gBAAuB,CAAA;AAElD,wCAAwC;AACxC,OAAO,EAAE,cAAc,IAAI,uBAAuB,EAAE,CAAA"}
@@ -17,7 +17,9 @@
17
17
  * Default mode is "active" (the pre-#383 chain) — existing setups are
18
18
  * byte-identical unless routing is explicitly enabled.
19
19
  *
20
- * This is a leaf module — pure functions, no I/O.
20
+ * This is a leaf module — no I/O. Most functions here are pure; the
21
+ * exhaustion/assignment trackers below hold mutable in-memory state (by
22
+ * design — see their own doc comments) but still perform no I/O.
21
23
  */
22
24
  export type RoutingMode = "active" | "sticky" | "priority";
23
25
  /**
@@ -82,4 +84,25 @@ export declare class ProfileExhaustion {
82
84
  /** Live entries only — expired marks are dropped on read. */
83
85
  snapshot(): ExhaustionEntry[];
84
86
  }
87
+ /**
88
+ * Session-to-profile assignments with LRU eviction.
89
+ *
90
+ * A JS Map preserves insertion order and a bare `set()` on an EXISTING key
91
+ * does not reorder it — so a plain map evicts first-inserted, which drops a
92
+ * long-lived active conversation ahead of a newer idle one. Both read and
93
+ * write therefore delete-then-set to refresh recency.
94
+ *
95
+ * Deliberately not persisted: this is routing hygiene, not durable truth.
96
+ * After a restart the next request re-establishes the assignment.
97
+ */
98
+ export declare class AssignmentStore {
99
+ private readonly max;
100
+ private readonly entries;
101
+ constructor(max: number);
102
+ /** Read an assignment, marking it most-recently-used. */
103
+ get(key: string): string | undefined;
104
+ /** Write an assignment, marking it most-recently-used and evicting if over capacity. */
105
+ set(key: string, value: string): void;
106
+ get size(): number;
107
+ }
85
108
  //# sourceMappingURL=routing.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/proxy/routing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,CAAA;AAE1D;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,WAAW,CAKnE;AAaD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAYvG;AAED;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,EAAE,aAAa,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,EAAE,MAAM,CAAC,CAM/F,CAAA;AAOD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,aAAa,EAAE,SAAS,MAAM,EAAE,EAChC,YAAY,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GAC1C;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAUxC;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,GACnC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,CAMnD;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;;;;GAMG;AACH,qBAAa,iBAAiB;IAEhB,OAAO,CAAC,QAAQ,CAAC,GAAG;IADhC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuD;gBAChD,GAAG,GAAE,MAAM,MAAiB;IAEzD,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAMrD,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAUhC,6DAA6D;IAC7D,QAAQ,IAAI,eAAe,EAAE;CAQ9B"}
1
+ {"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/proxy/routing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,CAAA;AAE1D;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,WAAW,CAKnE;AAaD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAYvG;AAED;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,EAAE,aAAa,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,EAAE,MAAM,CAAC,CAM/F,CAAA;AAOD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,aAAa,EAAE,SAAS,MAAM,EAAE,EAChC,YAAY,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GAC1C;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAUxC;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,GACnC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,CAMnD;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;;;;GAMG;AACH,qBAAa,iBAAiB;IAEhB,OAAO,CAAC,QAAQ,CAAC,GAAG;IADhC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuD;gBAChD,GAAG,GAAE,MAAM,MAAiB;IAEzD,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAMrD,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAUhC,6DAA6D;IAC7D,QAAQ,IAAI,eAAe,EAAE;CAQ9B;AAED;;;;;;;;;;GAUG;AACH,qBAAa,eAAe;IAGd,OAAO,CAAC,QAAQ,CAAC,GAAG;IAFhC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;gBAEvB,GAAG,EAAE,MAAM;IAExC,yDAAyD;IACzD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAQpC,wFAAwF;IACxF,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IASrC,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/proxy/server.ts"],"names":[],"mappings":"AAgBA,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;AAkDnG,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;AAgR7B,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,WAAW,CAs5HhF;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":"AAgBA,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;AAkDnG,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EAEpB,KAAK,aAAa,EAGnB,MAAM,mBAAmB,CAAA;AAI1B,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;AAgR7B,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,WAAW,CA0+HhF;AAWD,wBAAgB,gCAAgC,IAAI,IAAI,CAavD;AAED,wBAAsB,gBAAgB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAmGhG"}
@@ -1 +1 @@
1
- {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../src/proxy/session/cache.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,OAAO,EAIL,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,aAAa,EACnB,MAAM,WAAW,CAAA;AAMlB,wBAAgB,mBAAmB,IAAI,MAAM,CAW5C;AAqCD;kGACkG;AAClG,wBAAgB,iBAAiB,SAYhC;AAED;iFACiF;AACjF,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,gBAAgB,CAAC,EAAE,MAAM,EACzB,QAAQ,CAAC,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,GAC/C,IAAI,CAoBN;AAUD;;uDAEuD;AACvD,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,EAC/C,gBAAgB,CAAC,EAAE,MAAM,GACxB,aAAa,CAuDf;AAED;;uFAEuF;AACvF,wBAAgB,oBAAoB,CAAC,eAAe,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CA2BtF;AAED;;;yFAGyF;AACzF,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,EACnD,eAAe,EAAE,MAAM,EACvB,gBAAgB,CAAC,EAAE,MAAM,EACzB,eAAe,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,EACtC,YAAY,CAAC,EAAE,UAAU,QAoC1B"}
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../src/proxy/session/cache.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,OAAO,EAIL,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,aAAa,EACnB,MAAM,WAAW,CAAA;AAMlB,wBAAgB,mBAAmB,IAAI,MAAM,CAW5C;AAqCD;kGACkG;AAClG,wBAAgB,iBAAiB,SAYhC;AAED;iFACiF;AACjF,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,gBAAgB,CAAC,EAAE,MAAM,EACzB,QAAQ,CAAC,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,GAC/C,IAAI,CAoBN;AAkCD;;uDAEuD;AACvD,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,EAC/C,gBAAgB,CAAC,EAAE,MAAM,GACxB,aAAa,CAuDf;AAED;;uFAEuF;AACvF,wBAAgB,oBAAoB,CAAC,eAAe,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CA2BtF;AAED;;;yFAGyF;AACzF,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,EACnD,eAAe,EAAE,MAAM,EACvB,gBAAgB,CAAC,EAAE,MAAM,EACzB,eAAe,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,EACtC,YAAY,CAAC,EAAE,UAAU,QAoC1B"}
@@ -28,4 +28,30 @@ export declare function getConversationFingerprint(messages: Array<{
28
28
  role: string;
29
29
  content: any;
30
30
  }>, workingDirectory?: string): string;
31
+ /**
32
+ * Key a conversation for priority-pool assignment.
33
+ *
34
+ * An explicit session id always wins — keyed clients behave exactly as before.
35
+ * Without one, the conversation fingerprint stands in, which is what gives
36
+ * keyless clients pool affinity: Pylon's main process deliberately sends no
37
+ * session key (its provider headers are per-process, so one key would merge
38
+ * every open chat into a single meridian session), and without a fallback
39
+ * such a conversation re-picks its account every turn — bouncing back to the
40
+ * preferred profile the moment its cooldown expires and replaying its whole
41
+ * history against a cold cache.
42
+ *
43
+ * The `fp:` prefix namespaces fingerprint-derived keys so they can never
44
+ * collide with a real session id. An empty fingerprint returns null rather
45
+ * than inventing a key, preserving today's no-affinity behavior for requests
46
+ * we cannot identify.
47
+ *
48
+ * NOTE: this is only ever an ACCOUNT key, never a session key. Two unrelated
49
+ * conversations that share a first message and working directory will share
50
+ * an assignment — that costs nothing, because it selects a profile and never
51
+ * a resumable SDK session.
52
+ */
53
+ export declare function getPriorityAssignmentKey(sessionId: string | undefined, messages: Array<{
54
+ role: string;
55
+ content: any;
56
+ }>, workingDirectory?: string): string | null;
31
57
  //# sourceMappingURL=fingerprint.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fingerprint.d.ts","sourceRoot":"","sources":["../../../src/proxy/session/fingerprint.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,GAAG,GAAG,MAAM,GAAG,SAAS,CAc9D;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM,CAW7H"}
1
+ {"version":3,"file":"fingerprint.d.ts","sourceRoot":"","sources":["../../../src/proxy/session/fingerprint.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,GAAG,GAAG,MAAM,GAAG,SAAS,CAc9D;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM,CAW7H;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,EAC/C,gBAAgB,CAAC,EAAE,MAAM,GACxB,MAAM,GAAG,IAAI,CAIf"}
@@ -49,9 +49,12 @@ export interface SessionState {
49
49
  export type LineageResult = {
50
50
  type: "continuation";
51
51
  session: SessionState;
52
+ resumeFrom: number;
52
53
  } | {
53
54
  type: "compaction";
54
55
  session: SessionState;
56
+ resumeFrom: number;
57
+ suffixOverlap: number;
55
58
  } | {
56
59
  type: "undo";
57
60
  session: SessionState;
@@ -59,7 +62,10 @@ export type LineageResult = {
59
62
  rollbackUuid: string | undefined;
60
63
  } | {
61
64
  type: "diverged";
65
+ reason: LineageDivergenceReason;
66
+ prefixOverlap?: number;
62
67
  };
68
+ export type LineageDivergenceReason = "unverifiable" | "replayed-request" | "modified-history" | "unrelated-history" | "not-found" | "independent-request" | "missing-session-header";
63
69
  /**
64
70
  * Compute a lineage hash of an ordered message array.
65
71
  * Used as a fast-path check: if the aggregate hash matches, the messages
@@ -117,22 +123,22 @@ export declare function measurePrefixOverlap(storedHashes: string[], incomingHas
117
123
  * contiguity.
118
124
  */
119
125
  export declare function measureSuffixOverlap(storedHashes: string[], incomingHashes: string[]): number;
120
- /** Cache-like interface for verifyLineage — only needs get/set/delete */
121
- export interface SessionCacheLike {
122
- delete(key: string): boolean;
123
- }
124
126
  /**
125
127
  * Verify that incoming messages are a valid continuation of a cached session.
126
128
  * Uses per-message hash comparison to deterministically classify mutations.
129
+ * This function is deliberately side-effect free: it never mutates the cached
130
+ * state. The caller commits new hashes/counts only after the upstream request
131
+ * succeeds.
127
132
  *
128
133
  * Decision matrix:
129
- * Full prefix match (fast-path) → continuation (resume normally)
130
- * Suffix overlap >= MIN_SUFFIX → compaction (resume normally)
131
- * Prefix overlap > 0, no suffix → undo (fork at rollback point)
132
- * No overlap → diverged (start fresh)
134
+ * Full prefix match (fast-path) → continuation (resume from stored count)
135
+ * Suffix overlap >= MIN_SUFFIX → compaction (resume after matched suffix)
136
+ * Prefix overlap > 0, no suffix, shrank → undo (fork at rollback point)
137
+ * Cached prefix changed while growing → diverged (fresh full-history replay)
138
+ * No overlap → diverged (fresh full-history replay)
133
139
  */
134
140
  export declare function verifyLineage(cached: SessionState, messages: Array<{
135
141
  role: string;
136
142
  content: any;
137
- }>, cacheKey: string, cache: SessionCacheLike): LineageResult;
143
+ }>): LineageResult;
138
144
  //# sourceMappingURL=lineage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"lineage.d.ts","sourceRoot":"","sources":["../../../src/proxy/session/lineage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,4EAA4E;AAC5E,MAAM,WAAW,mBAAmB;IAClC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,uBAAuB,CAAC,EAAE,MAAM,CAAA;IAChC,2BAA2B,CAAC,EAAE,MAAM,CAAA;IACpC,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,kFAAkF;AAClF,MAAM,WAAW,UAAW,SAAQ,mBAAmB;IACrD,UAAU,CAAC,EAAE,mBAAmB,EAAE,CAAA;CACnC;AAED;;6DAE6D;AAC7D,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,UAAU,GAAG,mBAAmB,CAG5E;AAED;0EAC0E;AAC1E,eAAO,MAAM,yBAAyB,IAAI,CAAA;AAE1C,MAAM,WAAW,YAAY;IAC3B,eAAe,EAAE,MAAM,CAAA;IACvB,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB;;qDAEiD;IACjD,WAAW,EAAE,MAAM,CAAA;IACnB;;kCAE8B;IAC9B,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB;;oDAEgD;IAChD,eAAe,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IACtC,iGAAiG;IACjG,YAAY,CAAC,EAAE,UAAU,CAAA;CAC1B;AAED;;;GAGG;AACH,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,YAAY,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,YAAY,CAAC;IAAG,OAAO,EAAE,YAAY,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,MAAM,CAAC;IAAS,OAAO,EAAE,YAAY,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GACxG;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAAA;AAIxB;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,GAAG,MAAM,CAI1F;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,GAAG,MAAM,CAK3E;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,GAAG,MAAM,EAAE,CAG9F;AAID;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,MAAM,CAQ7F;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,MAAM,CA6B7F;AAyBD,yEAAyE;AACzE,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAC7B;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,EAC/C,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,gBAAgB,GACtB,aAAa,CA6Ff"}
1
+ {"version":3,"file":"lineage.d.ts","sourceRoot":"","sources":["../../../src/proxy/session/lineage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,4EAA4E;AAC5E,MAAM,WAAW,mBAAmB;IAClC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,uBAAuB,CAAC,EAAE,MAAM,CAAA;IAChC,2BAA2B,CAAC,EAAE,MAAM,CAAA;IACpC,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,kFAAkF;AAClF,MAAM,WAAW,UAAW,SAAQ,mBAAmB;IACrD,UAAU,CAAC,EAAE,mBAAmB,EAAE,CAAA;CACnC;AAED;;6DAE6D;AAC7D,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,UAAU,GAAG,mBAAmB,CAG5E;AAED;0EAC0E;AAC1E,eAAO,MAAM,yBAAyB,IAAI,CAAA;AAE1C,MAAM,WAAW,YAAY;IAC3B,eAAe,EAAE,MAAM,CAAA;IACvB,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB;;qDAEiD;IACjD,WAAW,EAAE,MAAM,CAAA;IACnB;;kCAE8B;IAC9B,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB;;oDAEgD;IAChD,eAAe,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IACtC,iGAAiG;IACjG,YAAY,CAAC,EAAE,UAAU,CAAA;CAC1B;AAED;;;GAGG;AACH,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,YAAY,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GACnE;IAAE,IAAI,EAAE,YAAY,CAAC;IAAG,OAAO,EAAE,YAAY,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GAC1F;IAAE,IAAI,EAAE,MAAM,CAAC;IAAS,OAAO,EAAE,YAAY,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GACxG;IAAE,IAAI,EAAE,UAAU,CAAC;IAAK,MAAM,EAAE,uBAAuB,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAErF,MAAM,MAAM,uBAAuB,GAC/B,cAAc,GACd,kBAAkB,GAClB,kBAAkB,GAClB,mBAAmB,GACnB,WAAW,GACX,qBAAqB,GACrB,wBAAwB,CAAA;AAI5B;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,GAAG,MAAM,CAI1F;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,GAAG,MAAM,CAK3E;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,GAAG,MAAM,EAAE,CAG9F;AAID;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,MAAM,CAQ7F;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,MAAM,CA6B7F;AAyBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,GAC9C,aAAa,CAyFf"}
@@ -1 +1 @@
1
- {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/proxy/settings.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH,MAAM,WAAW,gBAAgB;IAC/B,yDAAyD;IACzD,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;8EAC0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;gFAC4E;IAC5E,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;CACxB;AAED,yFAAyF;AACzF,wBAAgB,YAAY,IAAI,gBAAgB,CAO/C;AAED,4FAA4F;AAC5F,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,IAAI,CASrE;AAED,iCAAiC;AACjC,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,gBAAgB,EAAE,GAAG,EAAE,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAExF;AAED,6CAA6C;AAC7C,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,gBAAgB,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,IAAI,CAErG"}
1
+ {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/proxy/settings.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAyBH,MAAM,WAAW,gBAAgB;IAC/B,yDAAyD;IACzD,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;8EAC0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;gFAC4E;IAC5E,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;CACxB;AAED,yFAAyF;AACzF,wBAAgB,YAAY,IAAI,gBAAgB,CAQ/C;AAED,4FAA4F;AAC5F,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,IAAI,CAUrE;AAED,iCAAiC;AACjC,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,gBAAgB,EAAE,GAAG,EAAE,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAExF;AAED,6CAA6C;AAC7C,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,gBAAgB,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,IAAI,CAErG"}
package/dist/server.js CHANGED
@@ -11,12 +11,12 @@ import {
11
11
  runObserveHook,
12
12
  runTransformHook,
13
13
  startProxyServer
14
- } from "./cli-sz964j04.js";
15
- import"./cli-ngtexmne.js";
14
+ } from "./cli-wszp24mg.js";
15
+ import"./cli-h6hfkg3s.js";
16
16
  import"./cli-sry5aqdj.js";
17
17
  import"./cli-xmweegb1.js";
18
- import"./cli-bsg2dd52.js";
19
- import"./cli-340h1chz.js";
18
+ import"./cli-kjd4cwcq.js";
19
+ import"./cli-vj9cv18n.js";
20
20
  import"./cli-je60fevk.js";
21
21
  import"./cli-aq5zz92m.js";
22
22
  import"./cli-p9swy5t3.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rynfar/meridian",
3
- "version": "1.57.0",
3
+ "version": "1.57.1",
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",
@@ -23,7 +23,7 @@
23
23
  "proxy": "./bin/claude-proxy-supervisor.sh",
24
24
  "build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && bun build bin/cli.ts src/proxy/server.ts --outdir dist --target node --splitting --external @anthropic-ai/claude-agent-sdk --external jsonc-parser --entry-naming \"[name].js\" && tsc -p tsconfig.build.json",
25
25
  "postbuild": "node scripts/fix-bun-exports.mjs && node --check dist/cli.js && node --check dist/server.js && node -e \"if(!require('fs').existsSync('dist/proxy/server.d.ts'))process.exit(1)\"",
26
- "postinstall": "node ./node_modules/@anthropic-ai/claude-code/install.cjs 2>/dev/null || true",
26
+ "postinstall": "node -e \"try{require('child_process').spawnSync(process.execPath,[require.resolve('@anthropic-ai/claude-code/install.cjs')],{stdio:'inherit'})}catch(e){console.error('[meridian] claude-code postinstall skipped:',e.message)}\"",
27
27
  "prepublishOnly": "bun run build",
28
28
  "test": "bun test --path-ignore-patterns '**/*session-store*' --path-ignore-patterns '**/*proxy-async-ops*' --path-ignore-patterns '**/*models-auth-status*' --path-ignore-patterns '**/*proxy-context-usage-store*' --path-ignore-patterns '**/*proxy-passthrough-thinking*' --path-ignore-patterns '**/*proxy-thinking-setting*' --path-ignore-patterns '**/*session-recovery*' --path-ignore-patterns '**/*models.test*' && bun test src/__tests__/proxy-async-ops.test.ts && bun test src/__tests__/proxy-session-store.test.ts && bun test src/__tests__/session-store-pruning.test.ts && bun test src/__tests__/proxy-session-store-locking.test.ts && bun test src/__tests__/proxy-context-usage-store.test.ts && bun test src/__tests__/models-auth-status.test.ts && bun test src/__tests__/proxy-passthrough-thinking.test.ts && bun test src/__tests__/proxy-session-recovery.test.ts && bun test src/__tests__/proxy-thinking-setting.test.ts && bun test src/__tests__/models.test.ts",
29
29
  "nix:lock": "bun2nix -o bun.nix",