@rynfar/meridian 1.71.0 → 1.72.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -28,7 +28,7 @@ import {
28
28
  profileBarHtml,
29
29
  profileBarJs,
30
30
  themeCss
31
- } from "./cli-8yp89fan.js";
31
+ } from "./cli-hj61zpt7.js";
32
32
  import {
33
33
  CANONICAL_SONNET_MODEL,
34
34
  OAUTH_CLIENT_ID,
@@ -75,9 +75,10 @@ import {
75
75
  PRIORITY_ATTESTATION_HEADER,
76
76
  checkPluginConfigured,
77
77
  init_priorityAttestation,
78
+ isPluginlessOpenCodeRequest,
78
79
  notePluginlessOpenCodeRequest,
79
80
  verifyPriorityAttestation
80
- } from "./cli-zdbv40d5.js";
81
+ } from "./cli-hxxy0m1z.js";
81
82
  import {
82
83
  __commonJS,
83
84
  __esm,
@@ -22111,6 +22112,9 @@ var WINDOWS_START_PATTERN = /^[1-9][0-9]{0,31}$/;
22111
22112
  var DARWIN_START_PATTERN = /^(Sun|Mon|Tue|Wed|Thu|Fri|Sat) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) [ 0-3][0-9] [0-2][0-9]:[0-5][0-9]:[0-6][0-9] [0-9]{4}$/;
22112
22113
  var PROBE_TIMEOUT_MS = 2000;
22113
22114
  var WINDOWS_PROBE_TIMEOUT_MS = 1e4;
22115
+ function processIncarnationProbeBudgetMs() {
22116
+ return process.platform === "win32" ? WINDOWS_PROBE_TIMEOUT_MS : PROBE_TIMEOUT_MS;
22117
+ }
22114
22118
  var cachedLocalBootIdentity;
22115
22119
  var cachedCurrentProcessIncarnation;
22116
22120
  function hashIdentity(value) {
@@ -23785,6 +23789,14 @@ function classifyError(errMsg, model) {
23785
23789
  message: `Claude Code process exited unexpectedly (code ${code}). Check proxy logs for details. If this persists, try 'claude login' to refresh authentication.`
23786
23790
  };
23787
23791
  }
23792
+ if (lower.includes("timed out waiting for") && lower.includes(".lock") || lower.includes("ownership backlog is full") || lower.includes("ownership capacity is full")) {
23793
+ const reason = lower.includes("ownership backlog is full") ? "the retirement backlog is full" : lower.includes("ownership capacity is full") ? "the ownership capacity is full" : "a bookkeeping lock is busy";
23794
+ return {
23795
+ status: 503,
23796
+ type: "overloaded_error",
23797
+ message: `Meridian's session bookkeeping is saturated: ${reason}. This is proxy load, not the request; retry shortly.`
23798
+ };
23799
+ }
23788
23800
  if (lower.includes("timeout") || lower.includes("timed out")) {
23789
23801
  return {
23790
23802
  status: 504,
@@ -32685,6 +32697,7 @@ import {
32685
32697
  closeSync as closeSync2,
32686
32698
  existsSync as existsSync6,
32687
32699
  fchmodSync,
32700
+ fstatSync,
32688
32701
  fsyncSync as fsyncSync2,
32689
32702
  linkSync,
32690
32703
  lstatSync as lstatSync3,
@@ -33438,6 +33451,38 @@ function validateStoreMeta(value) {
33438
33451
  priorityRollbackMappings
33439
33452
  };
33440
33453
  }
33454
+ var storeDocumentCache;
33455
+ function readStoreDocumentCached(path3) {
33456
+ let fd;
33457
+ try {
33458
+ const info = statSync(path3);
33459
+ const cached2 = storeDocumentCache;
33460
+ if (cached2?.path === path3 && cached2.ino === info.ino && cached2.mtimeMs === info.mtimeMs && cached2.size === info.size) {
33461
+ return cached2.document;
33462
+ }
33463
+ fd = openSync2(path3, "r");
33464
+ const identity = fstatSync(fd);
33465
+ const document = parseStoreDocument(readFileSync6(fd, "utf8"));
33466
+ storeDocumentCache = { path: path3, ino: identity.ino, mtimeMs: identity.mtimeMs, size: identity.size, document };
33467
+ return document;
33468
+ } catch (error51) {
33469
+ if (error51.code !== "ENOENT")
33470
+ throw error51;
33471
+ storeDocumentCache = undefined;
33472
+ return emptyStoreDocument();
33473
+ } finally {
33474
+ if (fd !== undefined)
33475
+ closeSync2(fd);
33476
+ }
33477
+ }
33478
+ function publishStoreCache(path3, document) {
33479
+ try {
33480
+ const info = statSync(path3);
33481
+ storeDocumentCache = { path: path3, ino: info.ino, mtimeMs: info.mtimeMs, size: info.size, document };
33482
+ } catch {
33483
+ storeDocumentCache = undefined;
33484
+ }
33485
+ }
33441
33486
  function readStoreDocumentStrict(path3) {
33442
33487
  let data;
33443
33488
  try {
@@ -33447,6 +33492,9 @@ function readStoreDocumentStrict(path3) {
33447
33492
  return emptyStoreDocument();
33448
33493
  throw error51;
33449
33494
  }
33495
+ return parseStoreDocument(data);
33496
+ }
33497
+ function parseStoreDocument(data) {
33450
33498
  const parsed = JSON.parse(data);
33451
33499
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
33452
33500
  throw new Error("session store must contain a JSON object");
@@ -33479,13 +33527,13 @@ function readStoreDocumentStrict(path3) {
33479
33527
  return { sessions, meta: meta3 };
33480
33528
  }
33481
33529
  function readStoreStrict(path3) {
33482
- return readStoreDocumentStrict(path3).sessions;
33530
+ return readStoreDocumentCached(path3).sessions;
33483
33531
  }
33484
33532
  function readSessionStoreSnapshot() {
33485
33533
  return readStoreStrict(getStorePath());
33486
33534
  }
33487
33535
  function readSessionStoreGenerationSnapshot(adapterSessionId, profileIds = []) {
33488
- const document = readStoreDocumentStrict(getStorePath());
33536
+ const document = readStoreDocumentCached(getStorePath());
33489
33537
  const keys = new Set(Object.keys(document.sessions).filter((key) => key === adapterSessionId || key.endsWith(`:${adapterSessionId}`)));
33490
33538
  keys.add(adapterSessionId);
33491
33539
  for (const profileId of profileIds) {
@@ -33517,7 +33565,7 @@ function writeStore(path3, document) {
33517
33565
  fd = openSync2(tmp, "wx", 384);
33518
33566
  fchmodSync(fd, 384);
33519
33567
  const serialized = { [STORE_META_KEY]: document.meta, ...document.sessions };
33520
- writeFileSync2(fd, JSON.stringify(serialized, null, 2), "utf8");
33568
+ writeFileSync2(fd, JSON.stringify(serialized), "utf8");
33521
33569
  fsyncSync2(fd);
33522
33570
  closeSync2(fd);
33523
33571
  fd = undefined;
@@ -33546,8 +33594,10 @@ function mutateStore(mutator) {
33546
33594
  const lock = acquireLock(`${path3}.lock`);
33547
33595
  try {
33548
33596
  const document = readStoreDocumentStrict(path3);
33549
- if (mutator(document))
33597
+ if (mutator(document)) {
33550
33598
  writeStore(path3, document);
33599
+ publishStoreCache(path3, document);
33600
+ }
33551
33601
  } finally {
33552
33602
  releaseLock(lock);
33553
33603
  }
@@ -33558,7 +33608,7 @@ function hasLegacyUserDenialBoundary(session) {
33558
33608
  }
33559
33609
  function lookupSharedSessionResult(key) {
33560
33610
  try {
33561
- const document = readStoreDocumentStrict(getStorePath());
33611
+ const document = readStoreDocumentCached(getStorePath());
33562
33612
  const session = document.sessions[key];
33563
33613
  const generation = keyGeneration(key, session, document.meta);
33564
33614
  if (!session)
@@ -33578,7 +33628,7 @@ function lookupSharedSession(key) {
33578
33628
  }
33579
33629
  function lookupPriorityAssignmentResult(routeKey) {
33580
33630
  try {
33581
- const document = readStoreDocumentStrict(getStorePath());
33631
+ const document = readStoreDocumentCached(getStorePath());
33582
33632
  const assignment = document.meta.version === PRIORITY_STORE_META_VERSION ? document.meta.priorityAssignments[routeKey] : undefined;
33583
33633
  const generation = priorityAssignmentGeneration(routeKey, assignment, document.meta);
33584
33634
  const attempt = document.meta.version === PRIORITY_STORE_META_VERSION ? document.meta.priorityAttempts[routeKey] : undefined;
@@ -33591,7 +33641,7 @@ function lookupPriorityAssignmentResult(routeKey) {
33591
33641
  }
33592
33642
  function lookupSharedSessionByClaudeIdResult(claudeSessionId) {
33593
33643
  try {
33594
- const document = readStoreDocumentStrict(getStorePath());
33644
+ const document = readStoreDocumentCached(getStorePath());
33595
33645
  let newest;
33596
33646
  let newestKey;
33597
33647
  for (const [key, session] of Object.entries(document.sessions)) {
@@ -33672,8 +33722,8 @@ function storeSharedSession(key, claudeSessionId, messageCount, lineageHash, mes
33672
33722
  passthroughToolCallAssistantUuid: passthroughToolCallAssistantUuid === undefined ? existing?.passthroughToolCallAssistantUuid : passthroughToolCallAssistantUuid ?? undefined,
33673
33723
  passthroughToolCallIds: passthroughToolCallIds === undefined ? existing?.passthroughToolCallIds : passthroughToolCallIds ?? undefined,
33674
33724
  contextUsage: contextUsage ?? existing?.contextUsage,
33675
- ...resolvedCurrentTranscript ? { currentTranscript: resolvedCurrentTranscript } : {},
33676
- ...previousTranscript ? { previousTranscript } : {},
33725
+ ...resolvedCurrentTranscript ? { currentTranscript: { ...resolvedCurrentTranscript } } : {},
33726
+ ...previousTranscript ? { previousTranscript: { ...previousTranscript } } : {},
33677
33727
  ...previousClaudeSessionId ? { previousClaudeSessionId } : {}
33678
33728
  };
33679
33729
  const maxEntries = getMaxStoredSessionsLimit();
@@ -34099,7 +34149,7 @@ function attachSharedTranscriptLocator(key, expectedClaudeSessionId, locator, ex
34099
34149
  if (expectedGeneration !== undefined && getStoredSessionGeneration(existing, key) !== expectedGeneration)
34100
34150
  return false;
34101
34151
  if (!sameTranscriptLocator(existing.currentTranscript, locator)) {
34102
- existing.currentTranscript = locator;
34152
+ existing.currentTranscript = { ...locator };
34103
34153
  existing.revision = (existing.revision ?? 0) + 1;
34104
34154
  existing.generationId = randomUUID2();
34105
34155
  advanceKeySlot(key, meta3);
@@ -35092,7 +35142,7 @@ class CrossProcessTurnCoordinator {
35092
35142
 
35093
35143
  // src/proxy/sessionLifecycle.ts
35094
35144
  import { createHash as createHash9, randomUUID as randomUUID4 } from "node:crypto";
35095
- import { spawn } from "node:child_process";
35145
+ import { spawn, spawnSync as spawnSync2 } from "node:child_process";
35096
35146
  import { realpathSync as realpathSync2 } from "node:fs";
35097
35147
  import {
35098
35148
  chmod as chmod2,
@@ -35119,9 +35169,11 @@ var DEFAULT_LOCK_RETRY_MS = 25;
35119
35169
  var DEFAULT_LOCK_STALE_MS = 60000;
35120
35170
  var DEFAULT_PREPARED_GRACE_MS = 5 * 60000;
35121
35171
  var DEFAULT_RETIRED_GRACE_MS = 11 * 60000;
35172
+ var DEFAULT_UNARMED_LEASE_TTL_MS = 11 * 60000;
35122
35173
  var DEFAULT_RETRY_BASE_MS = 5000;
35123
35174
  var DEFAULT_RETRY_MAX_MS = 60 * 60000;
35124
35175
  var DEFAULT_DELETE_TIMEOUT_MS = 30000;
35176
+ var SESSION_GC_NOT_FOUND_EXIT_CODE = 69;
35125
35177
 
35126
35178
  class SessionLifecycleError extends Error {
35127
35179
  }
@@ -35160,13 +35212,14 @@ async function acquireActiveTranscriptLease(locators, options = {}) {
35160
35212
  const token = randomUUID4();
35161
35213
  await withSidecarLock(options, async (paths) => {
35162
35214
  const sidecar = await readSidecar(paths.sidecar);
35215
+ const unarmedLeaseTtlMs = nonNegativeOption(options.unarmedLeaseTtlMs, DEFAULT_UNARMED_LEASE_TTL_MS, "unarmedLeaseTtlMs");
35163
35216
  for (const [key, locator] of normalized) {
35164
35217
  const resource = sidecar.resources[key];
35165
35218
  if (!resource)
35166
35219
  throw new SessionLifecycleError(`cannot lease unjournaled transcript ${key}`);
35167
35220
  assertSameLocator(resource.locator, locator);
35168
35221
  assertExactLifecycleGeneration(resource, locator);
35169
- pruneDeadActiveLeases(resource);
35222
+ pruneDeadActiveLeases(resource, nowMs(options), unarmedLeaseTtlMs);
35170
35223
  if (Object.values(resource.activeLeases ?? {}).some((lease) => lease.purpose !== "publication")) {
35171
35224
  throw new SessionLifecycleError(`transcript ${key} already has an active SDK writer`);
35172
35225
  }
@@ -35483,8 +35536,9 @@ async function reconcile(pins, options = {}) {
35483
35536
  const now = nowMs(options);
35484
35537
  const preparedCutoff = now - nonNegativeOption(options.preparedGraceMs, DEFAULT_PREPARED_GRACE_MS, "preparedGraceMs");
35485
35538
  let changed = false;
35539
+ const unarmedLeaseTtlMs = nonNegativeOption(options.unarmedLeaseTtlMs, DEFAULT_UNARMED_LEASE_TTL_MS, "unarmedLeaseTtlMs");
35486
35540
  for (const resource of Object.values(sidecar.resources)) {
35487
- if (pruneDeadActiveLeases(resource))
35541
+ if (pruneDeadActiveLeases(resource, now, unarmedLeaseTtlMs))
35488
35542
  changed = true;
35489
35543
  }
35490
35544
  let pending = pendingResourceCount(sidecar);
@@ -35493,7 +35547,7 @@ async function reconcile(pins, options = {}) {
35493
35547
  for (const resource of Object.values(sidecar.resources)) {
35494
35548
  if (resource.state !== "deleting")
35495
35549
  continue;
35496
- const executorDead = resource.deletionExecutor && resource.deletionProcessGroupId !== undefined ? processIncarnationIsDead(resource.deletionExecutor) && processGroupIsEmpty(resource.deletionProcessGroupId) : false;
35550
+ const executorDead = resource.deletionExecutor && resource.deletionProcessGroupId !== undefined ? processIncarnationIsDead(resource.deletionExecutor) && (process.platform === "win32" || processGroupIsEmpty(resource.deletionProcessGroupId)) : false;
35497
35551
  const ownerDiedBeforeHandshake = !resource.deletionExecutor && resource.deletionOwner !== undefined && processIncarnationIsDead(resource.deletionOwner);
35498
35552
  if (!executorDead && !ownerDiedBeforeHandshake)
35499
35553
  continue;
@@ -35549,14 +35603,6 @@ async function reconcile(pins, options = {}) {
35549
35603
  async function runGc(pins, options = {}) {
35550
35604
  await reconcile(pins, options);
35551
35605
  let currentPins = pins.map(canonicalizeTranscriptLocator);
35552
- if (!options.deleter && process.platform === "win32") {
35553
- return {
35554
- deleted: 0,
35555
- notFound: 0,
35556
- failed: 0,
35557
- deferred: await countDeferred(currentPins, options)
35558
- };
35559
- }
35560
35606
  const limit = option(options.maxDeletesPerRun, DEFAULT_MAX_DELETES, "maxDeletesPerRun");
35561
35607
  const result = { deleted: 0, notFound: 0, failed: 0, deferred: 0 };
35562
35608
  const runTimeoutMs = option(options.runTimeoutMs, DEFAULT_DELETE_TIMEOUT_MS, "runTimeoutMs");
@@ -35610,9 +35656,10 @@ async function claimDeletion(pins, options) {
35610
35656
  const sidecar = await readSidecar(paths.sidecar);
35611
35657
  const finalPins = (options.pinProvider?.() ?? pins).map(canonicalizeTranscriptLocator);
35612
35658
  const now = nowMs(options);
35659
+ const unarmedLeaseTtlMs = nonNegativeOption(options.unarmedLeaseTtlMs, DEFAULT_UNARMED_LEASE_TTL_MS, "unarmedLeaseTtlMs");
35613
35660
  let leasesChanged = false;
35614
35661
  for (const resource of Object.values(sidecar.resources)) {
35615
- if (pruneDeadActiveLeases(resource))
35662
+ if (pruneDeadActiveLeases(resource, now, unarmedLeaseTtlMs))
35616
35663
  leasesChanged = true;
35617
35664
  }
35618
35665
  const candidate = Object.values(sidecar.resources).filter((resource) => resource.state === "retired" && !resourceIsPinned(resource, finalPins) && !hasActiveTranscriptLease(resource) && (resource.nextAttemptAt ?? 0) <= now).sort((left, right) => left.updatedAt - right.updatedAt || left.key.localeCompare(right.key))[0];
@@ -35686,6 +35733,9 @@ async function countDeferred(pins, options) {
35686
35733
 
35687
35734
  class DeletionStillRunningError extends Error {
35688
35735
  }
35736
+
35737
+ class TranscriptAlreadyAbsentError extends Error {
35738
+ }
35689
35739
  async function awaitCustomDeleter(deletion, timeoutMs) {
35690
35740
  let timer;
35691
35741
  const timeout = new Promise((_resolve, reject) => {
@@ -35707,8 +35757,9 @@ async function awaitCustomDeleter(deletion, timeoutMs) {
35707
35757
  }
35708
35758
  }
35709
35759
  function processGroupIsEmpty(processGroupId) {
35710
- if (process.platform === "win32")
35711
- return false;
35760
+ if (process.platform === "win32") {
35761
+ throw new SessionLifecycleError("process-group probes are POSIX-only");
35762
+ }
35712
35763
  try {
35713
35764
  process.kill(-processGroupId, 0);
35714
35765
  return false;
@@ -35717,8 +35768,13 @@ function processGroupIsEmpty(processGroupId) {
35717
35768
  }
35718
35769
  }
35719
35770
  function signalProcessGroup(processGroupId, signal) {
35720
- if (process.platform === "win32")
35771
+ if (process.platform === "win32") {
35772
+ spawnSync2("taskkill", ["/PID", String(processGroupId), "/T", "/F"], {
35773
+ stdio: "ignore",
35774
+ windowsHide: true
35775
+ });
35721
35776
  return;
35777
+ }
35722
35778
  try {
35723
35779
  process.kill(-processGroupId, signal);
35724
35780
  } catch (error51) {
@@ -35752,7 +35808,7 @@ async function waitForDeletionExit(exited, timeoutMs) {
35752
35808
  }
35753
35809
  }
35754
35810
  async function deleteWithSdkChild(locator, deletionToken, timeoutMs, attachExecutor, options) {
35755
- const sdkUrl = import.meta.resolve("@anthropic-ai/claude-agent-sdk");
35811
+ const sdkUrl = options.sdkModuleUrl ?? import.meta.resolve("@anthropic-ai/claude-agent-sdk");
35756
35812
  const gateDirectory = join10(getStoreDir(options), "deletion-gates");
35757
35813
  await mkdir5(gateDirectory, { recursive: true, mode: 448 });
35758
35814
  const gatePath = join10(gateDirectory, `${deletionToken}.go`);
@@ -35769,15 +35825,31 @@ while (!existsSync(process.env.MERIDIAN_GC_GATE_PATH)) {
35769
35825
  await wait(10);
35770
35826
  }
35771
35827
  const sdk = await import(process.env.MERIDIAN_GC_SDK_URL);
35828
+ const sessionId = process.env.MERIDIAN_GC_SESSION_ID;
35772
35829
  const options = process.env.MERIDIAN_GC_PROJECT_DIR
35773
35830
  ? { dir: process.env.MERIDIAN_GC_PROJECT_DIR }
35774
35831
  : undefined;
35832
+ // Only the SDK's session-specific verdict means "already absent". A generic
35833
+ // "not found" can be the SDK or the child itself failing to load, and must stay
35834
+ // a retryable failure rather than tombstone a transcript still on disk.
35835
+ const absent = (message) => message.includes(process.env.MERIDIAN_GC_ABSENT_PHRASE);
35775
35836
  try {
35776
- await sdk.deleteSession(process.env.MERIDIAN_GC_SESSION_ID, options);
35837
+ await sdk.deleteSession(sessionId, options);
35777
35838
  } catch (error) {
35778
35839
  const message = error instanceof Error ? error.message : String(error);
35779
- if (!options || !message.includes("not found")) throw error;
35780
- await sdk.deleteSession(process.env.MERIDIAN_GC_SESSION_ID);
35840
+ if (!message.includes("not found")) throw error;
35841
+ if (options) {
35842
+ try {
35843
+ await sdk.deleteSession(sessionId);
35844
+ process.exit(0); // The dir-less retry deleted it: a deletion, not an absence.
35845
+ } catch (fallbackError) {
35846
+ const fallbackMessage = fallbackError instanceof Error ? fallbackError.message : String(fallbackError);
35847
+ if (!absent(fallbackMessage)) throw fallbackError;
35848
+ process.exit(${SESSION_GC_NOT_FOUND_EXIT_CODE});
35849
+ }
35850
+ }
35851
+ if (!absent(message)) throw error;
35852
+ process.exit(${SESSION_GC_NOT_FOUND_EXIT_CODE});
35781
35853
  }
35782
35854
  `;
35783
35855
  const child = spawn(getSessionGcNodeExecutable(), ["--input-type=module", "--eval", script], {
@@ -35786,9 +35858,10 @@ try {
35786
35858
  CLAUDE_CONFIG_DIR: locator.configDir,
35787
35859
  MERIDIAN_GC_SDK_URL: sdkUrl,
35788
35860
  MERIDIAN_GC_SESSION_ID: locator.sessionId,
35861
+ MERIDIAN_GC_ABSENT_PHRASE: sessionAbsentPhrase(locator.sessionId),
35789
35862
  MERIDIAN_GC_PROJECT_DIR: locator.projectDir ?? "",
35790
35863
  MERIDIAN_GC_GATE_PATH: gatePath,
35791
- MERIDIAN_GC_GATE_TIMEOUT_MS: String(timeoutMs)
35864
+ MERIDIAN_GC_GATE_TIMEOUT_MS: String(timeoutMs + processIncarnationProbeBudgetMs())
35792
35865
  },
35793
35866
  stdio: ["ignore", "pipe", "pipe"],
35794
35867
  windowsHide: true,
@@ -35810,15 +35883,21 @@ try {
35810
35883
  let timer;
35811
35884
  const joinTimeoutMs = Math.max(100, Math.min(2000, timeoutMs));
35812
35885
  const processGroupId = child.pid;
35886
+ let deletionTreeKilled = false;
35887
+ const killDeletionTree = () => {
35888
+ if (!processGroupId || deletionTreeKilled)
35889
+ return;
35890
+ if (process.platform === "win32" && (child.exitCode !== null || child.signalCode !== null))
35891
+ return;
35892
+ signalProcessGroup(processGroupId, "SIGKILL");
35893
+ deletionTreeKilled = true;
35894
+ };
35813
35895
  try {
35814
35896
  if (!processGroupId)
35815
35897
  throw new Error("session deletion child has no PID");
35816
35898
  const executor = captureProcessIncarnation(processGroupId);
35817
35899
  if (!executor)
35818
35900
  throw new Error("cannot capture session deletion executor incarnation");
35819
- if (process.platform === "win32") {
35820
- throw new SessionLifecycleError("fenced session deletion is unavailable on win32");
35821
- }
35822
35901
  await attachExecutor(executor, processGroupId);
35823
35902
  const gateHandle = await open3(gatePath, "wx", 384);
35824
35903
  try {
@@ -35830,27 +35909,30 @@ try {
35830
35909
  }
35831
35910
  const timeout = new Promise((_resolve, reject) => {
35832
35911
  timer = setTimeout(() => {
35833
- signalProcessGroup(processGroupId, "SIGKILL");
35912
+ killDeletionTree();
35834
35913
  reject(new Error("session deletion process group timed out and was killed"));
35835
35914
  }, timeoutMs);
35836
35915
  timer.unref?.();
35837
35916
  });
35838
35917
  const status = await Promise.race([exited, timeout]);
35839
- joined = await waitForProcessGroupEmpty(processGroupId, joinTimeoutMs);
35918
+ joined = process.platform === "win32" ? true : await waitForProcessGroupEmpty(processGroupId, joinTimeoutMs);
35840
35919
  if (!joined) {
35841
35920
  throw new DeletionStillRunningError("session deletion process group remains active");
35842
35921
  }
35922
+ if (status.code === SESSION_GC_NOT_FOUND_EXIT_CODE) {
35923
+ throw new TranscriptAlreadyAbsentError(`session deletion child reported transcript ${locator.sessionId} already absent`);
35924
+ }
35843
35925
  if (status.code !== 0) {
35844
- throw new Error(`session deletion child exited ${status.code ?? status.signal}: ${output.slice(-4000)}`);
35926
+ throw new Error(`session deletion child exited ${status.code ?? status.signal}: ${clipChildOutput(output)}`);
35845
35927
  }
35846
35928
  } finally {
35847
35929
  if (timer)
35848
35930
  clearTimeout(timer);
35849
35931
  if (!joined && processGroupId) {
35850
- signalProcessGroup(processGroupId, "SIGKILL");
35932
+ killDeletionTree();
35851
35933
  const [leaderExited, groupEmpty] = await Promise.all([
35852
35934
  waitForDeletionExit(exited, joinTimeoutMs),
35853
- waitForProcessGroupEmpty(processGroupId, joinTimeoutMs)
35935
+ process.platform === "win32" ? Promise.resolve(true) : waitForProcessGroupEmpty(processGroupId, joinTimeoutMs)
35854
35936
  ]);
35855
35937
  joined = leaderExited && groupEmpty;
35856
35938
  unjoined = !joined;
@@ -35865,6 +35947,15 @@ try {
35865
35947
  }
35866
35948
  }
35867
35949
  }
35950
+ function clipChildOutput(output) {
35951
+ const halfBudget = 2000;
35952
+ if (output.length <= halfBudget * 2)
35953
+ return output;
35954
+ const elided = output.length - halfBudget * 2;
35955
+ return `${output.slice(0, halfBudget)}
35956
+ …[${elided} chars elided]…
35957
+ ${output.slice(-halfBudget)}`;
35958
+ }
35868
35959
  function getStoreDir(options) {
35869
35960
  if (options.storeDir)
35870
35961
  return options.storeDir;
@@ -36174,7 +36265,7 @@ async function writeSidecar(path3, sidecar) {
36174
36265
  let handle;
36175
36266
  try {
36176
36267
  handle = await open3(temp, "wx", 384);
36177
- await handle.writeFile(`${JSON.stringify(sidecar, null, 2)}
36268
+ await handle.writeFile(`${JSON.stringify(sidecar)}
36178
36269
  `, "utf8");
36179
36270
  await handle.sync();
36180
36271
  await handle.close();
@@ -36317,14 +36408,15 @@ function releasePublicationLease(resource) {
36317
36408
  delete resource.activeLeases;
36318
36409
  return changed;
36319
36410
  }
36320
- function pruneDeadActiveLeases(resource) {
36411
+ function pruneDeadActiveLeases(resource, now, unarmedLeaseTtlMs) {
36321
36412
  if (!resource.activeLeases)
36322
36413
  return false;
36323
36414
  let changed = false;
36324
36415
  for (const [token, lease] of Object.entries(resource.activeLeases)) {
36325
36416
  const executorDead = lease.executor && lease.executorRecoverable !== false ? processIncarnationIsDead(lease.executor) : false;
36326
36417
  const unarmedOwnerDead = !lease.executor && processIncarnationIsDead(lease.owner);
36327
- if (!executorDead && !unarmedOwnerDead)
36418
+ const unarmedLeaseExpired = !lease.executor && now - lease.createdAt > unarmedLeaseTtlMs;
36419
+ if (!executorDead && !unarmedOwnerDead && !unarmedLeaseExpired)
36328
36420
  continue;
36329
36421
  delete resource.activeLeases[token];
36330
36422
  changed = true;
@@ -36394,11 +36486,32 @@ function assertSameLocator(left, right) {
36394
36486
  function isState(value) {
36395
36487
  return value === "prepared" || value === "live" || value === "retired" || value === "deleting" || value === "deleted";
36396
36488
  }
36489
+ function sessionAbsentPhrase(sessionId) {
36490
+ return `Session ${sessionId} not found`;
36491
+ }
36397
36492
  function isNotFoundError(error51, sessionId) {
36398
- return errorMessage(error51).includes(`Session ${sessionId} not found`);
36493
+ if (error51 instanceof TranscriptAlreadyAbsentError)
36494
+ return true;
36495
+ return errorMessage(error51).includes(sessionAbsentPhrase(sessionId));
36399
36496
  }
36497
+ var sessionGcNodeExecutable;
36400
36498
  function getSessionGcNodeExecutable() {
36401
- return typeof process.versions.bun === "string" ? "node" : process.execPath;
36499
+ if (typeof process.versions.bun !== "string")
36500
+ return process.execPath;
36501
+ if (sessionGcNodeExecutable)
36502
+ return sessionGcNodeExecutable;
36503
+ const probe = spawnSync2("node", ["-p", "process.execPath"], {
36504
+ encoding: "utf8",
36505
+ timeout: 5000,
36506
+ maxBuffer: 16 * 1024,
36507
+ windowsHide: true
36508
+ });
36509
+ const executable = probe.stdout?.trim();
36510
+ if (probe.error || probe.status !== 0 || !executable || !isAbsolute5(executable)) {
36511
+ throw new SessionLifecycleError("cannot resolve Node executable for session deletion");
36512
+ }
36513
+ sessionGcNodeExecutable = realpathSync2(executable);
36514
+ return sessionGcNodeExecutable;
36402
36515
  }
36403
36516
  function errorMessage(error51) {
36404
36517
  return error51 instanceof Error ? error51.message : String(error51);
@@ -36916,6 +37029,8 @@ function createProxyServer(config2 = {}) {
36916
37029
  maxDeletesPerRun: Math.max(1, envInt("SESSION_GC_MAX_DELETES", 16)),
36917
37030
  preparedGraceMs: Math.max(SESSION_TURN_MAX_HOLD_MS + 60000, envInt("SESSION_GC_PREPARED_GRACE_MS", SESSION_TURN_MAX_HOLD_MS + 60000)),
36918
37031
  retiredGraceMs: Math.max(0, envInt("SESSION_GC_GRACE_MS", SESSION_TURN_MAX_HOLD_MS + 60000)),
37032
+ lockWaitMs: Math.max(100, envInt("SESSION_GC_LOCK_WAIT_MS", 2000)),
37033
+ unarmedLeaseTtlMs: SESSION_TURN_MAX_HOLD_MS + 60000,
36919
37034
  deletionTimeoutMs: Math.max(1000, envInt("SESSION_GC_DELETE_TIMEOUT_MS", 30000)),
36920
37035
  runTimeoutMs: Math.max(1000, envInt("SESSION_GC_RUN_TIMEOUT_MS", 30000))
36921
37036
  };
@@ -37841,6 +37956,10 @@ data: ${JSON.stringify(lastError)}
37841
37956
  const taskBudget = Number.isFinite(parsedBudget) ? { total: parsedBudget } : body.task_budget ? { total: body.task_budget.total ?? body.task_budget } : undefined;
37842
37957
  const betas = betaFilter.forwarded;
37843
37958
  const agentSessionId = adapter.getSessionId(c, body);
37959
+ const pluginlessOpenCode = isPluginlessOpenCodeRequest({
37960
+ userAgent: c.req.header("user-agent"),
37961
+ agentModeHeader: c.req.header("x-opencode-agent-mode")
37962
+ });
37844
37963
  const pluginlessWarning = notePluginlessOpenCodeRequest({
37845
37964
  userAgent: c.req.header("user-agent"),
37846
37965
  agentModeHeader: c.req.header("x-opencode-agent-mode"),
@@ -37907,8 +38026,9 @@ data: ${JSON.stringify(lastError)}
37907
38026
  if (lineageResult.type === "undo" && adapterBase === "opencode" && !agentSessionId) {
37908
38027
  lineageResult = { type: "diverged", reason: "missing-session-header" };
37909
38028
  }
38029
+ const protocolRunsConcurrentTurnsPerSessionKey = adapter.runsConcurrentTurnsPerSessionKey === true || pluginlessOpenCode;
37910
38030
  const declaresPerRequestConcurrentFlow = requestSource?.startsWith("fork-") === true || isSubagentRequest;
37911
- const declaresConcurrentFlow = declaresPerRequestConcurrentFlow || adapter.runsConcurrentTurnsPerSessionKey === true;
38031
+ const declaresConcurrentFlow = declaresPerRequestConcurrentFlow || protocolRunsConcurrentTurnsPerSessionKey;
37912
38032
  const durableCheckpointIds = durableMappingAtTurn.status === "found" ? durableMappingAtTurn.session.passthroughToolCallIds : undefined;
37913
38033
  const trailingSystemReminderOptions = adapterBase === "claude-code" ? { allowTrailingSystemReminder: true } : undefined;
37914
38034
  const durableCheckpointContinuation = durableCheckpointIds?.length && durableMappingAtTurn.status === "found" && matchesStoredLineagePrefix(durableMappingAtTurn.session, lineageMessages) ? coalesceCompleteToolResultContinuation((body.messages || []).slice(durableMappingAtTurn.session.messageCount), durableCheckpointIds, trailingSystemReminderOptions) : undefined;
@@ -37968,7 +38088,7 @@ data: ${JSON.stringify(lastError)}
37968
38088
  headers: { "Content-Type": "application/json" }
37969
38089
  });
37970
38090
  }
37971
- if (lostRaceWhileWaiting && !declaresPerRequestConcurrentFlow && adapter.runsConcurrentTurnsPerSessionKey === true && lineageResult.type === "undo") {
38091
+ if (lostRaceWhileWaiting && !declaresPerRequestConcurrentFlow && protocolRunsConcurrentTurnsPerSessionKey && lineageResult.type === "undo") {
37972
38092
  lineageResult = { type: "diverged", reason: "concurrent-race" };
37973
38093
  }
37974
38094
  if (options.forceFreshPriorityReplay) {
@@ -38125,7 +38245,7 @@ data: ${JSON.stringify(lastError)}
38125
38245
  if (managedForkTarget)
38126
38246
  return;
38127
38247
  managedFreshTarget = false;
38128
- managedForkSource = cachedSession?.currentTranscript?.sessionId === sourceSessionId ? cachedSession.currentTranscript : transcriptLocator(sourceSessionId);
38248
+ managedForkSource = cachedSession?.currentTranscript?.sessionId === sourceSessionId ? { ...cachedSession.currentTranscript } : transcriptLocator(sourceSessionId);
38129
38249
  managedForkTarget = transcriptLocator(randomUUID6());
38130
38250
  releaseManagedForkPins = pinActiveSessionGcLocators(managedForkSource, managedForkTarget);
38131
38251
  const attachedGeneration = lifecycleMappingKey ? await attachPinnedTranscript(managedForkSource, () => {
@@ -40110,7 +40230,8 @@ data: ${JSON.stringify({
40110
40230
  if (!recoverySourceId || !lifecycleMappingKey) {
40111
40231
  throw new Error("Silent recovery has no durable source mapping");
40112
40232
  }
40113
- recoveryForkSource = lookupSharedSession(lifecycleMappingKey)?.currentTranscript ?? transcriptLocator(recoverySourceId);
40233
+ const storedRecoverySource = lookupSharedSession(lifecycleMappingKey)?.currentTranscript;
40234
+ recoveryForkSource = storedRecoverySource ? { ...storedRecoverySource } : transcriptLocator(recoverySourceId);
40114
40235
  const recoveryAttachedGeneration = recoveryForkSource.sessionId === recoverySourceId ? await attachPinnedTranscript(recoveryForkSource, () => {
40115
40236
  assertDurableWritesAllowed();
40116
40237
  return attachSharedTranscriptLocator(lifecycleMappingKey, recoverySourceId, recoveryForkSource, mappingExpectedGeneration ?? undefined);
@@ -41464,7 +41585,7 @@ data: ${JSON.stringify({
41464
41585
  });
41465
41586
  });
41466
41587
  app.get("/profiles", async (c) => {
41467
- const { profilePageHtml } = await import("./profilePage-vfj2dbhn.js");
41588
+ const { profilePageHtml } = await import("./profilePage-mamej7kp.js");
41468
41589
  return c.html(profilePageHtml);
41469
41590
  });
41470
41591
  app.post("/profiles/active", async (c) => {
@@ -41532,7 +41653,7 @@ data: ${JSON.stringify({
41532
41653
  }
41533
41654
  });
41534
41655
  app.get("/plugins", async (c) => {
41535
- const { pluginPageHtml } = await import("./pluginPage-n3gdtbmj.js");
41656
+ const { pluginPageHtml } = await import("./pluginPage-f5gdmbxc.js");
41536
41657
  return c.html(pluginPageHtml);
41537
41658
  });
41538
41659
  app.post("/auth/refresh", async (c) => {
@@ -900,17 +900,20 @@ var pluginlessWarned = new LRUMap(256);
900
900
  function clearPluginlessWarnings() {
901
901
  pluginlessWarned.clear();
902
902
  }
903
- function notePluginlessOpenCodeRequest(input) {
903
+ function isPluginlessOpenCodeRequest(input) {
904
904
  if (!input.userAgent?.toLowerCase().startsWith("opencode/"))
905
- return;
906
- if (input.agentModeHeader)
905
+ return false;
906
+ return !input.agentModeHeader;
907
+ }
908
+ function notePluginlessOpenCodeRequest(input) {
909
+ if (!isPluginlessOpenCodeRequest(input))
907
910
  return;
908
911
  const key = input.sessionId || "(keyless)";
909
912
  if (pluginlessWarned.get(key))
910
913
  return;
911
914
  pluginlessWarned.set(key, true);
912
915
  const shortId = input.sessionId ? `${input.sessionId.slice(0, 12)}…` : "(no session header)";
913
- 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).`;
916
+ 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: concurrent turns are admitted rather ` + `than refused, but the one that loses the race replays against a cold prompt cache — ` + `slower and billed as uncached input. Fix: meridian setup (or update the plugin).`;
914
917
  }
915
918
  function runSetup(pluginPath, configPath, generation = "v1") {
916
919
  const path = configPath ?? findOpencodeConfigPath();
@@ -971,4 +974,4 @@ function runSetup(pluginPath, configPath, generation = "v1") {
971
974
  return { configPath: path, pluginPath, alreadyConfigured, removedStale, created: false };
972
975
  }
973
976
 
974
- export { LRUMap, PRIORITY_ATTESTATION_HEADER, verifyPriorityAttestation, init_priorityAttestation, UnparseableConfigError, MissingV1PluginError, MissingV2PluginError, DuplicateMeridianConfigError, findOpencodeConfigPath, findPluginPath, SUPPORTED_OPENCODE_V2_VERSIONS, findV2PluginPath, classifyOpenCodeVersion, detectOpenCodeGeneration, pluginPathForGeneration, checkPluginConfigured, clearPluginlessWarnings, notePluginlessOpenCodeRequest, runSetup };
977
+ export { LRUMap, PRIORITY_ATTESTATION_HEADER, verifyPriorityAttestation, init_priorityAttestation, UnparseableConfigError, MissingV1PluginError, MissingV2PluginError, DuplicateMeridianConfigError, findOpencodeConfigPath, findPluginPath, SUPPORTED_OPENCODE_V2_VERSIONS, findV2PluginPath, classifyOpenCodeVersion, detectOpenCodeGeneration, pluginPathForGeneration, checkPluginConfigured, clearPluginlessWarnings, isPluginlessOpenCodeRequest, notePluginlessOpenCodeRequest, runSetup };
package/dist/cli.js CHANGED
@@ -1,16 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startProxyServer
4
- } from "./cli-6c6dj69q.js";
4
+ } from "./cli-ab7zkp1a.js";
5
5
  import"./cli-5jxyma6z.js";
6
6
  import"./cli-sry5aqdj.js";
7
- import"./cli-8yp89fan.js";
7
+ import"./cli-hj61zpt7.js";
8
8
  import {
9
9
  resolveClaudeExecutableAsync
10
10
  } from "./cli-9e5cxp89.js";
11
11
  import"./cli-khhjyk04.js";
12
12
  import"./cli-vj9cv18n.js";
13
- import"./cli-zdbv40d5.js";
13
+ import"./cli-hxxy0m1z.js";
14
14
  import {
15
15
  __require
16
16
  } from "./cli-p9swy5t3.js";
@@ -93,7 +93,7 @@ if (args[0] === "setup") {
93
93
  runSetup,
94
94
  SUPPORTED_OPENCODE_V2_VERSIONS,
95
95
  UnparseableConfigError
96
- } = await import("./setup-b3ymd9z8.js");
96
+ } = await import("./setup-ndmjpy23.js");
97
97
  const forceV1 = args.includes("--v1");
98
98
  const forceV2 = args.includes("--v2");
99
99
  if (forceV1 && forceV2) {
@@ -205,7 +205,7 @@ async function runCli(start = startProxyServer, runAuthCheck = async () => {
205
205
  return execFile(claudePath, ["auth", "status"], { timeout: 5000 });
206
206
  }) {
207
207
  try {
208
- const { findOpencodeConfigPath, checkPluginConfigured, findPluginPath } = await import("./setup-b3ymd9z8.js");
208
+ const { findOpencodeConfigPath, checkPluginConfigured, findPluginPath } = await import("./setup-ndmjpy23.js");
209
209
  const configPath = findOpencodeConfigPath();
210
210
  const { existsSync } = await import("fs");
211
211
  if (existsSync(configPath) && !checkPluginConfigured(configPath)) {
@@ -4,7 +4,7 @@ import {
4
4
  profileBarHtml,
5
5
  profileBarJs,
6
6
  themeCss
7
- } from "./cli-8yp89fan.js";
7
+ } from "./cli-hj61zpt7.js";
8
8
  import"./cli-p9swy5t3.js";
9
9
 
10
10
  // src/proxy/plugins/pluginPage.ts
@@ -4,7 +4,7 @@ import {
4
4
  profileBarHtml,
5
5
  profileBarJs,
6
6
  themeCss
7
- } from "./cli-8yp89fan.js";
7
+ } from "./cli-hj61zpt7.js";
8
8
  import"./cli-p9swy5t3.js";
9
9
 
10
10
  // src/telemetry/profilePage.ts