claude-use 11.4.1 → 11.5.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.
package/dist/cli.cjs CHANGED
@@ -23007,7 +23007,7 @@ var require_dumper = __commonJS({
23007
23007
  }
23008
23008
  return false;
23009
23009
  }
23010
- function isWhitespace(c10) {
23010
+ function isWhitespace2(c10) {
23011
23011
  return c10 === CHAR_SPACE || c10 === CHAR_TAB;
23012
23012
  }
23013
23013
  function isPrintable(c10) {
@@ -23019,19 +23019,19 @@ var require_dumper = __commonJS({
23019
23019
  }
23020
23020
  function isPlainSafe(c10, prev, inblock) {
23021
23021
  const cIsNsCharOrWhitespace = isNsCharOrWhitespace(c10);
23022
- const cIsNsChar = cIsNsCharOrWhitespace && !isWhitespace(c10);
23022
+ const cIsNsChar = cIsNsCharOrWhitespace && !isWhitespace2(c10);
23023
23023
  return (
23024
23024
  // ns-plain-safe
23025
23025
  (inblock ? cIsNsCharOrWhitespace : cIsNsCharOrWhitespace && // - c-flow-indicator
23026
23026
  c10 !== CHAR_COMMA && c10 !== CHAR_LEFT_SQUARE_BRACKET && c10 !== CHAR_RIGHT_SQUARE_BRACKET && c10 !== CHAR_LEFT_CURLY_BRACKET && c10 !== CHAR_RIGHT_CURLY_BRACKET) && // ns-plain-char
23027
23027
  c10 !== CHAR_SHARP && // false on '#'
23028
23028
  !(prev === CHAR_COLON && !cIsNsChar) || // false on ': '
23029
- isNsCharOrWhitespace(prev) && !isWhitespace(prev) && c10 === CHAR_SHARP || // change to true on '[^ ]#'
23029
+ isNsCharOrWhitespace(prev) && !isWhitespace2(prev) && c10 === CHAR_SHARP || // change to true on '[^ ]#'
23030
23030
  prev === CHAR_COLON && cIsNsChar
23031
23031
  );
23032
23032
  }
23033
23033
  function isPlainSafeFirst(c10) {
23034
- return isPrintable(c10) && c10 !== CHAR_BOM && !isWhitespace(c10) && // - s-white
23034
+ return isPrintable(c10) && c10 !== CHAR_BOM && !isWhitespace2(c10) && // - s-white
23035
23035
  // - (c-indicator ::=
23036
23036
  // “-” | “?” | “:” | “,” | “[” | “]” | “{” | “}”
23037
23037
  c10 !== CHAR_MINUS && c10 !== CHAR_QUESTION && c10 !== CHAR_COLON && c10 !== CHAR_COMMA && c10 !== CHAR_LEFT_SQUARE_BRACKET && c10 !== CHAR_RIGHT_SQUARE_BRACKET && c10 !== CHAR_LEFT_CURLY_BRACKET && c10 !== CHAR_RIGHT_CURLY_BRACKET && // | “#” | “&” | “*” | “!” | “|” | “=” | “>” | “'” | “"”
@@ -23039,7 +23039,7 @@ var require_dumper = __commonJS({
23039
23039
  c10 !== CHAR_PERCENT && c10 !== CHAR_COMMERCIAL_AT && c10 !== CHAR_GRAVE_ACCENT;
23040
23040
  }
23041
23041
  function isPlainSafeLast(c10) {
23042
- return !isWhitespace(c10) && c10 !== CHAR_COLON;
23042
+ return !isWhitespace2(c10) && c10 !== CHAR_COLON;
23043
23043
  }
23044
23044
  function codePointAt(string4, pos) {
23045
23045
  const first = string4.charCodeAt(pos);
@@ -31952,9 +31952,9 @@ function referencesFact(when, key) {
31952
31952
  if (when === void 0 || !("kind" in when)) {
31953
31953
  return false;
31954
31954
  }
31955
- const walk = (node2) => {
31955
+ const walk2 = (node2) => {
31956
31956
  if (Array.isArray(node2)) {
31957
- return node2.some(walk);
31957
+ return node2.some(walk2);
31958
31958
  }
31959
31959
  if (typeof node2 !== "object" || node2 === null) {
31960
31960
  return false;
@@ -31962,9 +31962,9 @@ function referencesFact(when, key) {
31962
31962
  if ("kind" in node2 && node2.kind === "reference" && "key" in node2 && node2.key === key) {
31963
31963
  return true;
31964
31964
  }
31965
- return Object.values(node2).some(walk);
31965
+ return Object.values(node2).some(walk2);
31966
31966
  };
31967
- return walk(when);
31967
+ return walk2(when);
31968
31968
  }
31969
31969
  function isVacuousWhen(when) {
31970
31970
  return when !== void 0 && !("kind" in when) && Object.keys(when).length === 0;
@@ -33976,7 +33976,7 @@ var init_package = __esm({
33976
33976
  "package.json"() {
33977
33977
  package_default = {
33978
33978
  name: "claude-use",
33979
- version: "11.4.1",
33979
+ version: "11.5.1",
33980
33980
  description: "A profile manager and launcher for Claude Code that lets one person run multiple logins from one machine while controlling what gets shared between them.",
33981
33981
  license: "MIT",
33982
33982
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -34635,7 +34635,7 @@ function createUsageMiddleware(deps) {
34635
34635
  const endedAt = deps.now();
34636
34636
  deps.defer(() => {
34637
34637
  try {
34638
- deps.record(buildRecord(event, { headAt, endedAt, outcome, scan: scanning && scanner !== void 0 ? scanner.result() : void 0 }));
34638
+ deps.record(buildRecord(event, { headAt, endedAt, outcome, scan: scanning && scanner !== void 0 ? scanner.result() : void 0 }), event);
34639
34639
  } catch (error63) {
34640
34640
  deps.log(`usage: could not record ${event.method} ${event.path} (${String(event.status)}): ${error63 instanceof Error ? error63.message : String(error63)}`);
34641
34641
  }
@@ -40329,7 +40329,7 @@ var require_webidl = __commonJS({
40329
40329
  var { runtimeFeatures } = require_runtime_features();
40330
40330
  var UNDEFINED = 1;
40331
40331
  var BOOLEAN = 2;
40332
- var STRING = 3;
40332
+ var STRING2 = 3;
40333
40333
  var SYMBOL = 4;
40334
40334
  var NUMBER = 5;
40335
40335
  var BIGINT = 6;
@@ -40400,7 +40400,7 @@ var require_webidl = __commonJS({
40400
40400
  case "boolean":
40401
40401
  return BOOLEAN;
40402
40402
  case "string":
40403
- return STRING;
40403
+ return STRING2;
40404
40404
  case "symbol":
40405
40405
  return SYMBOL;
40406
40406
  case "number":
@@ -40419,7 +40419,7 @@ var require_webidl = __commonJS({
40419
40419
  webidl.util.Types = {
40420
40420
  UNDEFINED,
40421
40421
  BOOLEAN,
40422
- STRING,
40422
+ STRING: STRING2,
40423
40423
  SYMBOL,
40424
40424
  NUMBER,
40425
40425
  BIGINT,
@@ -40432,7 +40432,7 @@ var require_webidl = __commonJS({
40432
40432
  return "Undefined";
40433
40433
  case BOOLEAN:
40434
40434
  return "Boolean";
40435
- case STRING:
40435
+ case STRING2:
40436
40436
  return "String";
40437
40437
  case SYMBOL:
40438
40438
  return "Symbol";
@@ -40518,7 +40518,7 @@ var require_webidl = __commonJS({
40518
40518
  return `Symbol(${V7.description})`;
40519
40519
  case OBJECT:
40520
40520
  return inspect(V7);
40521
- case STRING:
40521
+ case STRING2:
40522
40522
  return `"${V7}"`;
40523
40523
  case BIGINT:
40524
40524
  return `${V7}n`;
@@ -60955,8 +60955,8 @@ var require_eventsource_stream = __commonJS({
60955
60955
  var BOM = [239, 187, 191];
60956
60956
  var LF3 = 10;
60957
60957
  var CR3 = 13;
60958
- var COLON = 58;
60959
- var SPACE2 = 32;
60958
+ var COLON2 = 58;
60959
+ var SPACE3 = 32;
60960
60960
  var DATA = Buffer.from("data");
60961
60961
  var EVENT = Buffer.from("event");
60962
60962
  var ID2 = Buffer.from("id");
@@ -61101,7 +61101,7 @@ var require_eventsource_stream = __commonJS({
61101
61101
  if (line.length === 0) {
61102
61102
  return;
61103
61103
  }
61104
- const colonPosition = line.indexOf(COLON);
61104
+ const colonPosition = line.indexOf(COLON2);
61105
61105
  if (colonPosition === 0) {
61106
61106
  return;
61107
61107
  }
@@ -61110,7 +61110,7 @@ var require_eventsource_stream = __commonJS({
61110
61110
  if (colonPosition !== -1) {
61111
61111
  fieldLength = colonPosition;
61112
61112
  valueStart = colonPosition + 1;
61113
- if (line[valueStart] === SPACE2) {
61113
+ if (line[valueStart] === SPACE3) {
61114
61114
  ++valueStart;
61115
61115
  }
61116
61116
  }
@@ -61304,7 +61304,7 @@ var require_eventsource = __commonJS({
61304
61304
  var experimentalWarned = false;
61305
61305
  var defaultReconnectionTime = 3e3;
61306
61306
  var CONNECTING = 0;
61307
- var OPEN = 1;
61307
+ var OPEN2 = 1;
61308
61308
  var CLOSED = 2;
61309
61309
  var ANONYMOUS = "anonymous";
61310
61310
  var USE_CREDENTIALS = "use-credentials";
@@ -61437,7 +61437,7 @@ var require_eventsource = __commonJS({
61437
61437
  this.dispatchEvent(new Event("error"));
61438
61438
  return;
61439
61439
  }
61440
- this.#readyState = OPEN;
61440
+ this.#readyState = OPEN2;
61441
61441
  this.dispatchEvent(new Event("open"));
61442
61442
  this.#state.origin = response.urlList[response.urlList.length - 1].origin;
61443
61443
  const eventSourceStream = new EventSourceStream({
@@ -61547,7 +61547,7 @@ var require_eventsource = __commonJS({
61547
61547
  __proto__: null,
61548
61548
  configurable: false,
61549
61549
  enumerable: true,
61550
- value: OPEN,
61550
+ value: OPEN2,
61551
61551
  writable: false
61552
61552
  },
61553
61553
  CLOSED: {
@@ -61807,6 +61807,10 @@ function ensureFrontDoor(params) {
61807
61807
  let replaced = false;
61808
61808
  const distrusted = /* @__PURE__ */ new Set();
61809
61809
  let lastRejection;
61810
+ let busyDoor;
61811
+ const liveSessionsOn = (doorPid) => listFrontDoorSessions(ports.fs, paths.frontdoorSessionsDir).filter(
61812
+ (session) => session.pid !== params.launcherPid && ports.isRunning(session.pid) && (session.supervisorPid === void 0 || session.supervisorPid === doorPid)
61813
+ ).length;
61810
61814
  const verifyWithRetry = (port) => {
61811
61815
  let verdict = ports.verifyListener(port);
61812
61816
  for (let attempt = 1; !verdict.ok && attempt < FRONTDOOR_PROBE_ATTEMPTS; attempt += 1) {
@@ -61848,11 +61852,17 @@ function ensureFrontDoor(params) {
61848
61852
  throw new FrontDoorStartError(`${lastRejection}; refusing to send this session's credentials to it. Daemon log: ${paths.frontdoorLogPath}`);
61849
61853
  }
61850
61854
  if (ports.isFrontDoorSupervisor(supervisorPid)) {
61851
- stopAndAwaitExit(supervisorPid, `the front door (pid ${String(supervisorPid)}) stopped answering and did not exit when asked to`);
61855
+ const stranded = liveSessionsOn(supervisorPid);
61856
+ if (stranded > 0) {
61857
+ busyDoor = { pid: supervisorPid, sessions: stranded };
61858
+ } else {
61859
+ stopAndAwaitExit(supervisorPid, `the front door (pid ${String(supervisorPid)}) stopped answering and did not exit when asked to`);
61860
+ continue;
61861
+ }
61862
+ } else {
61863
+ distrusted.add(supervisorPid);
61852
61864
  continue;
61853
61865
  }
61854
- distrusted.add(supervisorPid);
61855
- continue;
61856
61866
  }
61857
61867
  if (state?.lastError !== void 0) {
61858
61868
  throw new FrontDoorStartError(`the front door reported a fatal error and is not serving: ${state.lastError} (daemon log: ${paths.frontdoorLogPath})`);
@@ -61875,7 +61885,7 @@ function ensureFrontDoor(params) {
61875
61885
  if (ports.now() >= deadline) {
61876
61886
  const lastError = state?.lastError;
61877
61887
  throw new FrontDoorStartError(
61878
- `the front door did not become ready within ${String(FRONTDOOR_START_TIMEOUT_MS)}ms` + (lastError === void 0 ? "" : ` (last error: ${lastError})`) + (lastRejection === void 0 ? "" : ` (${lastRejection})`) + `. Daemon log: ${paths.frontdoorLogPath}`
61888
+ `the front door did not become ready within ${String(FRONTDOOR_START_TIMEOUT_MS)}ms` + (lastError === void 0 ? "" : ` (last error: ${lastError})`) + (lastRejection === void 0 ? "" : ` (${lastRejection})`) + (busyDoor === void 0 ? "" : `. It was not replaced because ${String(busyDoor.sessions)} live session(s) are using it and would lose their connections; run \`agent-shim frontdoor restart\` to replace it deliberately`) + `. Daemon log: ${paths.frontdoorLogPath}`
61879
61889
  );
61880
61890
  }
61881
61891
  ports.sleep(FRONTDOOR_LAUNCHER_POLL_MS);
@@ -62357,7 +62367,7 @@ function isAnthropicUsageStale(member, nowMs) {
62357
62367
  function poolIdentityNames(pools, poolName) {
62358
62368
  const seenPools = /* @__PURE__ */ new Set();
62359
62369
  const identities = /* @__PURE__ */ new Set();
62360
- const walk = (name) => {
62370
+ const walk2 = (name) => {
62361
62371
  const pool = pools[name];
62362
62372
  if (pool === void 0 || seenPools.has(name)) {
62363
62373
  return;
@@ -62369,11 +62379,11 @@ function poolIdentityNames(pools, poolName) {
62369
62379
  if (nested === void 0) {
62370
62380
  identities.add(member);
62371
62381
  } else {
62372
- walk(nested);
62382
+ walk2(nested);
62373
62383
  }
62374
62384
  }
62375
62385
  };
62376
- walk(poolName);
62386
+ walk2(poolName);
62377
62387
  return [...identities];
62378
62388
  }
62379
62389
  function describeError(error63) {
@@ -109499,7 +109509,7 @@ function writePools(paths, pools) {
109499
109509
  applyPatch(paths.globalConfigFile, GlobalConfigSchema, { pools }, { defaults: {} });
109500
109510
  }
109501
109511
  function poolCycleOf(pools, name) {
109502
- const walk = (current, path59) => {
109512
+ const walk2 = (current, path59) => {
109503
109513
  for (const entry of pools[current]?.identities ?? []) {
109504
109514
  const nested = poolNameOf(poolMemberIdentity(entry));
109505
109515
  if (nested === void 0) {
@@ -109511,14 +109521,14 @@ function poolCycleOf(pools, name) {
109511
109521
  if (path59.includes(nested)) {
109512
109522
  continue;
109513
109523
  }
109514
- const found = walk(nested, [...path59, nested]);
109524
+ const found = walk2(nested, [...path59, nested]);
109515
109525
  if (found !== void 0) {
109516
109526
  return found;
109517
109527
  }
109518
109528
  }
109519
109529
  return void 0;
109520
109530
  };
109521
- return walk(name, [name]);
109531
+ return walk2(name, [name]);
109522
109532
  }
109523
109533
  function validateMembers(pools, name, members2) {
109524
109534
  for (const entry of members2) {
@@ -112452,7 +112462,7 @@ function registerCodexCommand(program2, deps) {
112452
112462
  }
112453
112463
 
112454
112464
  // src/frontdoor/commands.ts
112455
- var import_node_crypto20 = require("node:crypto");
112465
+ var import_node_crypto21 = require("node:crypto");
112456
112466
  var import_node_path51 = __toESM(require("node:path"), 1);
112457
112467
  var import_node_fs18 = __toESM(require("node:fs"), 1);
112458
112468
  init_commandDeps();
@@ -113671,8 +113681,11 @@ function createRoutingSettingReader(paths) {
113671
113681
 
113672
113682
  // src/frontdoor/headroomDecision.ts
113673
113683
  init_pick();
113684
+ var CONSECUTIVE_PREFIX_MISSES = 2;
113685
+ var SHORTEST_PROMPT_CACHE_TTL_MS = 3e5;
113674
113686
  function createHeadroomDecision(setting) {
113675
113687
  const sessions = /* @__PURE__ */ new Map();
113688
+ const threads = /* @__PURE__ */ new Map();
113676
113689
  return {
113677
113690
  decide: async (sessionId, launchFlag, nowMs, available) => {
113678
113691
  const choice = choiceFor(setting(), sessionId);
@@ -113683,29 +113696,46 @@ function createHeadroomDecision(setting) {
113683
113696
  const state = sessions.get(sessionId);
113684
113697
  const forced = choice?.force !== void 0 && choice.force !== state?.appliedForce;
113685
113698
  const atBoundary = forced || state === void 0 || nowMs - state.lastEndedAtMs > PROMPT_CACHE_TTL_MS || state.cacheSurplusTokens <= 0;
113686
- const viaHeadroom = atBoundary ? wanted && await available() : state.viaHeadroom;
113699
+ const prefixBroken = state?.prefixBroken ?? false;
113700
+ const viaHeadroom = atBoundary ? wanted && !prefixBroken && await available() : state.viaHeadroom;
113687
113701
  sessions.set(sessionId, {
113688
113702
  viaHeadroom,
113689
113703
  lastEndedAtMs: state?.lastEndedAtMs ?? nowMs,
113690
113704
  cacheSurplusTokens: state?.cacheSurplusTokens ?? 0,
113691
- appliedForce: choice?.force ?? state?.appliedForce
113705
+ appliedForce: choice?.force ?? state?.appliedForce,
113706
+ prefixMisses: state?.prefixMisses ?? 0,
113707
+ prefixBroken
113692
113708
  });
113693
113709
  return viaHeadroom;
113694
113710
  },
113695
- observe: ({ sessionId, endedAtMs, cache }) => {
113711
+ observe: ({ sessionId, startedAtMs, endedAtMs, viaHeadroom, lineage, cache }) => {
113696
113712
  if (sessionId === void 0 || cache === void 0) {
113697
113713
  return;
113698
113714
  }
113699
113715
  const cacheSurplusTokens = cache.readTokens - cache.writtenTokens - cache.uncachedTokens;
113700
113716
  const state = sessions.get(sessionId);
113717
+ let { prefixMisses, prefixBroken } = state ?? { prefixMisses: 0, prefixBroken: false };
113718
+ if (lineage !== void 0) {
113719
+ const previous = lineage.parentKey === void 0 ? void 0 : threads.get(`${sessionId}:${lineage.parentKey}`);
113720
+ if (previous !== void 0 && previous.viaHeadroom && viaHeadroom && startedAtMs - previous.endedAtMs <= SHORTEST_PROMPT_CACHE_TTL_MS) {
113721
+ prefixMisses = cache.readTokens < previous.cachedTokens ? prefixMisses + 1 : 0;
113722
+ prefixBroken = prefixBroken || prefixMisses >= CONSECUTIVE_PREFIX_MISSES;
113723
+ }
113724
+ threads.set(`${sessionId}:${lineage.ownKey}`, { cachedTokens: cache.readTokens + cache.writtenTokens, endedAtMs, viaHeadroom });
113725
+ }
113701
113726
  if (state !== void 0) {
113702
- sessions.set(sessionId, { ...state, lastEndedAtMs: endedAtMs, cacheSurplusTokens });
113727
+ sessions.set(sessionId, { ...state, lastEndedAtMs: endedAtMs, cacheSurplusTokens, prefixMisses, prefixBroken });
113703
113728
  }
113704
113729
  for (const [id2, held] of sessions) {
113705
113730
  if (endedAtMs - held.lastEndedAtMs > PROMPT_CACHE_TTL_MS) {
113706
113731
  sessions.delete(id2);
113707
113732
  }
113708
113733
  }
113734
+ for (const [id2, point] of threads) {
113735
+ if (endedAtMs - point.endedAtMs > PROMPT_CACHE_TTL_MS) {
113736
+ threads.delete(id2);
113737
+ }
113738
+ }
113709
113739
  }
113710
113740
  };
113711
113741
  }
@@ -126414,6 +126444,212 @@ function createLaunchEventPublisher(hub, now) {
126414
126444
  var http4 = __toESM(require("node:http"), 1);
126415
126445
  var import_consumers = require("node:stream/consumers");
126416
126446
  init_route();
126447
+
126448
+ // src/frontdoor/lineage.ts
126449
+ var import_node_crypto18 = require("node:crypto");
126450
+ var CACHE_MARKER_KEY = '"cache_control"';
126451
+ var THREAD_MEMBER_KEYS = /* @__PURE__ */ new Set(['"model"', '"system"', '"tools"']);
126452
+ var MESSAGES_KEY = '"messages"';
126453
+ var NESTING_DEPTH_LIMIT = 64;
126454
+ var MESSAGES_PER_REQUEST_LIMIT = 1e5;
126455
+ var CONTEXT_WINDOW_TOKENS = 1e6;
126456
+ var BYTES_PER_TOKEN = 4;
126457
+ var LINEAGE_BODY_BYTES_LIMIT = CONTEXT_WINDOW_TOKENS * BYTES_PER_TOKEN;
126458
+ var MESSAGES_ARRAY_DEPTH = 2;
126459
+ var MESSAGES_PER_TURN = 2;
126460
+ var QUOTE = 34;
126461
+ var BACKSLASH = 92;
126462
+ var OPEN_BRACE = 123;
126463
+ var CLOSE_BRACE = 125;
126464
+ var OPEN_BRACKET = 91;
126465
+ var CLOSE_BRACKET = 93;
126466
+ var COLON = 58;
126467
+ var COMMA = 44;
126468
+ var SEPARATOR = Buffer.from([0]);
126469
+ var SPACE2 = 32;
126470
+ var TAB = 9;
126471
+ var LINE_FEED2 = 10;
126472
+ var CARRIAGE_RETURN = 13;
126473
+ function isWhitespace(byte) {
126474
+ return byte === SPACE2 || byte === TAB || byte === LINE_FEED2 || byte === CARRIAGE_RETURN;
126475
+ }
126476
+ var STRING = 1;
126477
+ var OPEN = 2;
126478
+ var CLOSE = 3;
126479
+ var COLON_TOKEN = 4;
126480
+ var COMMA_TOKEN = 5;
126481
+ var SCALAR = 6;
126482
+ var END = 0;
126483
+ var tokenStart = 0;
126484
+ var tokenEnd = 0;
126485
+ function nextToken(body, from) {
126486
+ let index = from;
126487
+ while (index < body.length && isWhitespace(body[index] ?? 0)) {
126488
+ index += 1;
126489
+ }
126490
+ if (index >= body.length) {
126491
+ return END;
126492
+ }
126493
+ tokenStart = index;
126494
+ const byte = body[index] ?? 0;
126495
+ if (byte === QUOTE) {
126496
+ let end = index + 1;
126497
+ while (end < body.length && body[end] !== QUOTE) {
126498
+ end += body[end] === BACKSLASH ? 2 : 1;
126499
+ }
126500
+ tokenEnd = Math.min(end + 1, body.length);
126501
+ return STRING;
126502
+ }
126503
+ tokenEnd = index + 1;
126504
+ if (byte === OPEN_BRACE || byte === OPEN_BRACKET) {
126505
+ return OPEN;
126506
+ }
126507
+ if (byte === CLOSE_BRACE || byte === CLOSE_BRACKET) {
126508
+ return CLOSE;
126509
+ }
126510
+ if (byte === COLON) {
126511
+ return COLON_TOKEN;
126512
+ }
126513
+ if (byte === COMMA) {
126514
+ return COMMA_TOKEN;
126515
+ }
126516
+ while (tokenEnd < body.length) {
126517
+ const next = body[tokenEnd] ?? 0;
126518
+ if (isWhitespace(next) || next === COMMA || next === CLOSE_BRACE || next === CLOSE_BRACKET || next === COLON) {
126519
+ break;
126520
+ }
126521
+ tokenEnd += 1;
126522
+ }
126523
+ return SCALAR;
126524
+ }
126525
+ function walk(body, hashing, parentAfter) {
126526
+ const thread = (0, import_node_crypto18.createHash)("sha256");
126527
+ const messages = (0, import_node_crypto18.createHash)("sha256");
126528
+ let parentDigest;
126529
+ let elements = 0;
126530
+ let depth = 0;
126531
+ let topKey;
126532
+ let inMessages = false;
126533
+ let sawMessages = false;
126534
+ const expectKey = [];
126535
+ let skipUntilDepth;
126536
+ let skipNextValue = false;
126537
+ const elementEnded = () => {
126538
+ elements += 1;
126539
+ if (hashing && parentAfter !== void 0 && elements === parentAfter) {
126540
+ parentDigest = messages.copy().digest("hex");
126541
+ }
126542
+ return elements > MESSAGES_PER_REQUEST_LIMIT;
126543
+ };
126544
+ const overLimit = () => ({ abandoned: true, sawMessages, elements, threadDigest: "", messagesDigest: "", parentDigest: void 0 });
126545
+ let cursor3 = 0;
126546
+ let lexicalTokens = 0;
126547
+ for (; ; ) {
126548
+ const kind = nextToken(body, cursor3);
126549
+ if (kind === END) {
126550
+ break;
126551
+ }
126552
+ lexicalTokens += 1;
126553
+ if (lexicalTokens > CONTEXT_WINDOW_TOKENS) {
126554
+ return overLimit();
126555
+ }
126556
+ const start = tokenStart;
126557
+ const end = tokenEnd;
126558
+ cursor3 = end;
126559
+ if (skipUntilDepth !== void 0) {
126560
+ if (kind === OPEN) {
126561
+ depth += 1;
126562
+ } else if (kind === CLOSE) {
126563
+ depth -= 1;
126564
+ if (depth === skipUntilDepth) {
126565
+ skipUntilDepth = void 0;
126566
+ }
126567
+ }
126568
+ continue;
126569
+ }
126570
+ if (kind === COMMA_TOKEN) {
126571
+ if (depth >= 1) {
126572
+ expectKey[depth - 1] = true;
126573
+ }
126574
+ continue;
126575
+ }
126576
+ if (kind === COLON_TOKEN) {
126577
+ if (depth >= 1) {
126578
+ expectKey[depth - 1] = false;
126579
+ }
126580
+ if (skipNextValue) {
126581
+ skipNextValue = false;
126582
+ const valueKind = nextToken(body, cursor3);
126583
+ if (valueKind !== END) {
126584
+ cursor3 = tokenEnd;
126585
+ if (valueKind === OPEN) {
126586
+ skipUntilDepth = depth;
126587
+ depth += 1;
126588
+ }
126589
+ }
126590
+ }
126591
+ continue;
126592
+ }
126593
+ const isKey = kind === STRING && depth >= 1 && expectKey[depth - 1] === true;
126594
+ const text2 = isKey ? body.toString("latin1", start, end) : void 0;
126595
+ if (isKey && text2 === CACHE_MARKER_KEY) {
126596
+ skipNextValue = true;
126597
+ continue;
126598
+ }
126599
+ if (depth === 1 && isKey) {
126600
+ topKey = text2;
126601
+ }
126602
+ const closesMessages = kind === CLOSE && inMessages && depth === MESSAGES_ARRAY_DEPTH;
126603
+ if (hashing && !closesMessages && !(depth === 1 && isKey && !THREAD_MEMBER_KEYS.has(text2 ?? ""))) {
126604
+ const target = inMessages ? messages : depth >= 1 && topKey !== void 0 && THREAD_MEMBER_KEYS.has(topKey) ? thread : void 0;
126605
+ if (target !== void 0) {
126606
+ target.update(body.subarray(start, end));
126607
+ target.update(SEPARATOR);
126608
+ }
126609
+ }
126610
+ if (kind === OPEN) {
126611
+ if (depth >= NESTING_DEPTH_LIMIT) {
126612
+ return { abandoned: true, sawMessages, elements, threadDigest: "", messagesDigest: "", parentDigest: void 0 };
126613
+ }
126614
+ const startsMessages = depth === 1 && topKey === MESSAGES_KEY && body[start] === OPEN_BRACKET && !sawMessages;
126615
+ depth += 1;
126616
+ expectKey.push(body[start] === OPEN_BRACE);
126617
+ if (startsMessages) {
126618
+ inMessages = true;
126619
+ sawMessages = true;
126620
+ }
126621
+ } else if (kind === CLOSE) {
126622
+ expectKey.pop();
126623
+ depth -= 1;
126624
+ if (inMessages && depth === 1) {
126625
+ inMessages = false;
126626
+ } else if (inMessages && depth === 2 && elementEnded()) {
126627
+ return overLimit();
126628
+ }
126629
+ } else if (inMessages && depth === 2 && kind === SCALAR && elementEnded()) {
126630
+ return overLimit();
126631
+ }
126632
+ if (depth < 0) {
126633
+ break;
126634
+ }
126635
+ }
126636
+ return { abandoned: false, sawMessages, elements, threadDigest: thread.digest("hex"), messagesDigest: messages.digest("hex"), parentDigest };
126637
+ }
126638
+ function lineageOf(body) {
126639
+ if (body.length > LINEAGE_BODY_BYTES_LIMIT) {
126640
+ return void 0;
126641
+ }
126642
+ const counted = walk(body, false, void 0);
126643
+ if (counted.abandoned || !counted.sawMessages) {
126644
+ return void 0;
126645
+ }
126646
+ const parentAfter = counted.elements > MESSAGES_PER_TURN ? counted.elements - MESSAGES_PER_TURN : void 0;
126647
+ const hashed = walk(body, true, parentAfter);
126648
+ const combine = (messagesDigest) => (0, import_node_crypto18.createHash)("sha256").update(hashed.threadDigest).update(messagesDigest).digest("hex");
126649
+ return { ownKey: combine(hashed.messagesDigest), parentKey: hashed.parentDigest === void 0 ? void 0 : combine(hashed.parentDigest) };
126650
+ }
126651
+
126652
+ // src/frontdoor/headroomHop.ts
126417
126653
  var HTTP_BAD_GATEWAY2 = HTTP_STATUS.badGateway;
126418
126654
  function responseHeaders(headers) {
126419
126655
  const result = {};
@@ -126425,7 +126661,7 @@ function responseHeaders(headers) {
126425
126661
  }
126426
126662
  return result;
126427
126663
  }
126428
- async function applyHeadroomHop(request7, response, upstream, deps) {
126664
+ async function applyHeadroomHop(request7, response, upstream, deps, onThread) {
126429
126665
  const target = deps.headroomSocket();
126430
126666
  if (target === void 0) {
126431
126667
  await answerBadGateway(response, "agent-shim front door: the headroom daemon is restarting");
@@ -126461,7 +126697,7 @@ async function applyHeadroomHop(request7, response, upstream, deps) {
126461
126697
  }
126462
126698
  headers[HOP_SECRET_HEADER] = deps.hopSecret;
126463
126699
  try {
126464
- await forwardThroughHeadroom(request7, response, { socketPath, headers, log: deps.log });
126700
+ await forwardThroughHeadroom(request7, response, { socketPath, headers, log: deps.log, onThread });
126465
126701
  } finally {
126466
126702
  if (hopId !== void 0) {
126467
126703
  deps.custody.release(hopId);
@@ -126503,19 +126739,59 @@ async function headroomReady(deps, signal) {
126503
126739
  }
126504
126740
  var HOP_RESET_RETRIES = 1;
126505
126741
  var HOP_RESET_CODES = /* @__PURE__ */ new Set(["ECONNRESET", "EPIPE"]);
126506
- async function collectBody(request7) {
126507
- try {
126508
- return await (0, import_consumers.buffer)(request7.body);
126509
- } catch {
126510
- return void 0;
126511
- }
126742
+ var BYTES_PER_MIB = 1048576;
126743
+ var MESSAGES_API_LIMIT_MIB = 32;
126744
+ var MAX_BODY_BYTES = MESSAGES_API_LIMIT_MIB * BYTES_PER_MIB;
126745
+ async function collectBody(body) {
126746
+ return await new Promise((resolve2) => {
126747
+ const chunks = [];
126748
+ let size = 0;
126749
+ let settled = false;
126750
+ const settle2 = (result) => {
126751
+ if (!settled) {
126752
+ settled = true;
126753
+ resolve2(result);
126754
+ }
126755
+ };
126756
+ body.on("data", (chunk) => {
126757
+ if (settled) {
126758
+ return;
126759
+ }
126760
+ const piece = typeof chunk === "string" ? Buffer.from(chunk) : chunk instanceof Uint8Array ? Buffer.from(chunk) : void 0;
126761
+ if (piece === void 0) {
126762
+ settle2({ kind: "failed" });
126763
+ return;
126764
+ }
126765
+ size += piece.length;
126766
+ if (size > MAX_BODY_BYTES) {
126767
+ chunks.length = 0;
126768
+ settle2({ kind: "too-large" });
126769
+ return;
126770
+ }
126771
+ chunks.push(piece);
126772
+ });
126773
+ body.on("end", () => {
126774
+ settle2({ kind: "body", body: Buffer.concat(chunks) });
126775
+ });
126776
+ body.on("error", () => {
126777
+ settle2({ kind: "failed" });
126778
+ });
126779
+ });
126512
126780
  }
126513
126781
  async function forwardThroughHeadroom(request7, response, target) {
126514
- const body = await collectBody(request7);
126515
- if (body === void 0) {
126782
+ const collected = await collectBody(request7.body);
126783
+ if (collected.kind === "failed") {
126516
126784
  response.destroy();
126517
126785
  return;
126518
126786
  }
126787
+ if (collected.kind === "too-large") {
126788
+ response.start(HTTP_STATUS.payloadTooLarge, { "Content-Type": "application/json" });
126789
+ await response.write(JSON.stringify({ type: "error", error: { type: "request_too_large", message: "agent-shim front door: the request body is larger than the Messages API accepts" } }));
126790
+ response.end();
126791
+ return;
126792
+ }
126793
+ const { body } = collected;
126794
+ target.onThread(lineageOf(body));
126519
126795
  for (let retriesLeft = HOP_RESET_RETRIES; ; retriesLeft -= 1) {
126520
126796
  const outcome = await hopOnce(request7, response, target, body, retriesLeft > 0);
126521
126797
  if (outcome === "settled") {
@@ -126655,7 +126931,7 @@ function createRoutedResponse(response, context) {
126655
126931
  start: (status, headers) => {
126656
126932
  for (const observe of deps.responseObservers) {
126657
126933
  try {
126658
- const body = observe({ session, route, method: request7.method, path: request7.path, receivedAt: request7.receivedAt, viaHeadroom: request7.viaHeadroom, status, headers });
126934
+ const body = observe({ session, route, method: request7.method, path: request7.path, receivedAt: request7.receivedAt, viaHeadroom: request7.viaHeadroom, lineage: request7.thread.lineage, status, headers });
126659
126935
  if (body !== void 0) {
126660
126936
  bodyObservers.push(body);
126661
126937
  }
@@ -126696,7 +126972,7 @@ function createRoutedResponse(response, context) {
126696
126972
  };
126697
126973
  }
126698
126974
  async function serveRouted(request7, deps) {
126699
- const observed = { method: request7.method, path: pathOf(request7.url), receivedAt: deps.now(), viaHeadroom: false };
126975
+ const observed = { method: request7.method, path: pathOf(request7.url), receivedAt: deps.now(), viaHeadroom: false, thread: { lineage: void 0 } };
126700
126976
  const identified = identifyRequest(request7.headers);
126701
126977
  const admission = deps.admit({ url: request7.url, headers: request7.headers, forwardable: identified.forwardableHeaders });
126702
126978
  if (!admission.ok) {
@@ -126723,12 +126999,14 @@ async function serveRouted(request7, deps) {
126723
126999
  return;
126724
127000
  }
126725
127001
  const { headroom } = deps;
126726
- const wantsHeadroom = await deps.headroomDecision.decide(identified.session.sessionId, identified.session.headroom, deps.now(), async () => headroom !== void 0 && await headroomReady(headroom, request7.signal));
127002
+ const wantsHeadroom = headroom !== void 0 && await deps.headroomDecision.decide(identified.session.sessionId, identified.session.headroom, deps.now(), async () => await headroomReady(headroom, request7.signal));
126727
127003
  const viaHeadroom = wantsHeadroom && resolution.route.headroomEligible && deps.headroom !== void 0;
126728
127004
  const response = createRoutedResponse(request7.response, { deps, session: identified.session, route: resolution.route.name, request: { ...observed, viaHeadroom } });
126729
127005
  try {
126730
127006
  if (viaHeadroom) {
126731
- await applyHeadroomHop(routed, response, resolution.route.headroomUpstream, deps.headroom);
127007
+ await applyHeadroomHop(routed, response, resolution.route.headroomUpstream, deps.headroom, (lineage) => {
127008
+ observed.thread.lineage = lineage;
127009
+ });
126732
127010
  } else {
126733
127011
  await resolution.route.serve(routed, response);
126734
127012
  }
@@ -126752,7 +127030,7 @@ init_load();
126752
127030
  init_schema();
126753
127031
 
126754
127032
  // src/codex/translate.ts
126755
- var import_node_crypto18 = require("node:crypto");
127033
+ var import_node_crypto19 = require("node:crypto");
126756
127034
  init_schema();
126757
127035
 
126758
127036
  // src/codex/anthropic.ts
@@ -126867,7 +127145,7 @@ function shortToolName(name, names) {
126867
127145
  if (name.length <= CODEX_TOOL_NAME_LIMIT) {
126868
127146
  return name;
126869
127147
  }
126870
- const hash2 = (0, import_node_crypto18.createHash)("sha256").update(name).digest("hex").slice(0, TOOL_NAME_HASH_CHARS);
127148
+ const hash2 = (0, import_node_crypto19.createHash)("sha256").update(name).digest("hex").slice(0, TOOL_NAME_HASH_CHARS);
126871
127149
  const shortened = `${name.slice(0, CODEX_TOOL_NAME_LIMIT - TOOL_NAME_HASH_CHARS - 1)}_${hash2}`;
126872
127150
  names.set(shortened, name);
126873
127151
  return shortened;
@@ -127914,7 +128192,7 @@ function attachingProviderCredential(route, deps) {
127914
128192
  init_realFrontDoorPort();
127915
128193
 
127916
128194
  // src/frontdoor/rcCommands.ts
127917
- var import_node_crypto19 = require("node:crypto");
128195
+ var import_node_crypto20 = require("node:crypto");
127918
128196
  init_commandDeps();
127919
128197
  init_cliError();
127920
128198
  init_realPorts();
@@ -128521,8 +128799,8 @@ function registerRcCommand(frontdoor, paths) {
128521
128799
  identitiesDir: paths.identitiesDir,
128522
128800
  frontdoorDir: paths.frontdoorDir,
128523
128801
  identity,
128524
- newUuid: import_node_crypto19.randomUUID,
128525
- randomToken: () => (0, import_node_crypto19.randomBytes)(MINTED_TOKEN_RANDOM_BYTES).toString("base64url"),
128802
+ newUuid: import_node_crypto20.randomUUID,
128803
+ randomToken: () => (0, import_node_crypto20.randomBytes)(MINTED_TOKEN_RANDOM_BYTES).toString("base64url"),
128526
128804
  force: options.force === true,
128527
128805
  now: () => Date.now()
128528
128806
  });
@@ -130775,8 +131053,8 @@ function realFrontDoorSupervisorPorts(paths) {
130775
131053
  };
130776
131054
  const rcSelfHostSurface = rcSelfHost ? createRcSelfHostSurface({
130777
131055
  now: () => Date.now(),
130778
- newUuid: import_node_crypto20.randomUUID,
130779
- randomToken: () => (0, import_node_crypto20.randomBytes)(MINTED_TOKEN_RANDOM_BYTES).toString("base64url"),
131056
+ newUuid: import_node_crypto21.randomUUID,
131057
+ randomToken: () => (0, import_node_crypto21.randomBytes)(MINTED_TOKEN_RANDOM_BYTES).toString("base64url"),
130780
131058
  credentialRecord: () => readRcSelfHostRecord(realFarmFs, paths.frontdoorDir),
130781
131059
  webFetch: realRcWebFetch({ allowPrivate: rcWebFetchAllowsPrivateFromEnv(process.env) }),
130782
131060
  log
@@ -130799,7 +131077,7 @@ function realFrontDoorSupervisorPorts(paths) {
130799
131077
  saveSequenceNum: rcCredentialStore.writeCursor,
130800
131078
  dial: rcSelfHost ? lateRcStreamDial(rcSelfHostDialTarget) : realRcStreamDial(),
130801
131079
  fanout: rcFanout,
130802
- newClientId: import_node_crypto20.randomUUID,
131080
+ newClientId: import_node_crypto21.randomUUID,
130803
131081
  backoffMs: RC_STREAM_BACKOFF_MS,
130804
131082
  sleep: async (ms2) => {
130805
131083
  await new Promise((resolve2) => {
@@ -130811,7 +131089,7 @@ function realFrontDoorSupervisorPorts(paths) {
130811
131089
  }
130812
131090
  });
130813
131091
  const routingSnapshots = /* @__PURE__ */ new Map();
130814
- const rcControlToken = (0, import_node_crypto20.randomUUID)();
131092
+ const rcControlToken = (0, import_node_crypto21.randomUUID)();
130815
131093
  const resolveRoute = rcObservingResolver(
130816
131094
  createProviderRouteResolver({
130817
131095
  fs: realFsPort,
@@ -130902,10 +131180,13 @@ function realFrontDoorSupervisorPorts(paths) {
130902
131180
  const recordUsage = withQuotaRefresh(usageStore.record, quotaRefresher);
130903
131181
  const usageMiddleware = createUsageMiddleware({
130904
131182
  // Each finished response also tells the per-session headroom decision when the session's cache was last touched and whether it read from it.
130905
- record: (record2) => {
131183
+ record: (record2, event) => {
130906
131184
  const { usage } = record2;
130907
131185
  headroomDecision.observe({
130908
131186
  sessionId: record2.sessionId,
131187
+ startedAtMs: Date.parse(record2.at),
131188
+ viaHeadroom: record2.viaHeadroom === true,
131189
+ lineage: event.lineage,
130909
131190
  endedAtMs: Date.parse(record2.at) + record2.durationMs,
130910
131191
  cache: usage === void 0 ? void 0 : { readTokens: usage.cacheReadInputTokens ?? 0, writtenTokens: usage.cacheCreationInputTokens ?? 0, uncachedTokens: usage.inputTokens ?? 0 }
130911
131192
  });
@@ -130924,9 +131205,9 @@ function realFrontDoorSupervisorPorts(paths) {
130924
131205
  now: () => Date.now(),
130925
131206
  headroomSocket: () => headroomSocketTarget(realFarmFs, realHeadroomSocketTrust, paths),
130926
131207
  // The per-generation capability the direct listener demands: held only in this process's memory, so a loopback process that discovers the direct port still cannot use it.
130927
- hopSecret: (0, import_node_crypto20.randomUUID)(),
131208
+ hopSecret: (0, import_node_crypto21.randomUUID)(),
130928
131209
  headroomDecision,
130929
- custody: createCredentialCustody(() => (0, import_node_crypto20.randomUUID)()),
131210
+ custody: createCredentialCustody(() => (0, import_node_crypto21.randomUUID)()),
130930
131211
  log
130931
131212
  });
130932
131213
  const onListenerError = (name) => (error63) => {
@@ -130949,9 +131230,9 @@ function realFrontDoorSupervisorPorts(paths) {
130949
131230
  startProviderListener: async (preferredPort) => {
130950
131231
  const authority = loadCa();
130951
131232
  const rcDial = rcSelfHost ? lateRcEventDial(rcSelfHostDialTarget) : realRcEventDial();
130952
- const rcInject = async (sessionId, text2) => await injectRcUserMessage({ credentialOf: rcTracker.credentialOf, noteSequenceNums: rcTracker.noteSequenceNums, dial: rcDial, newUuid: import_node_crypto20.randomUUID }, sessionId, text2);
131233
+ const rcInject = async (sessionId, text2) => await injectRcUserMessage({ credentialOf: rcTracker.credentialOf, noteSequenceNums: rcTracker.noteSequenceNums, dial: rcDial, newUuid: import_node_crypto21.randomUUID }, sessionId, text2);
130953
131234
  const rcAnswer = async (sessionId, requestId, decision) => await answerRcControlRequest({ credentialOf: rcTracker.credentialOf, pendingOf: (id2) => rcTracker.pendingOf(id2), completePending: rcTracker.completePending, noteSequenceNums: rcTracker.noteSequenceNums, dial: rcDial }, sessionId, requestId, decision);
130954
- const rcControlRequestDeps = { credentialOf: rcTracker.credentialOf, noteSequenceNums: rcTracker.noteSequenceNums, dial: rcDial, newUuid: import_node_crypto20.randomUUID };
131235
+ const rcControlRequestDeps = { credentialOf: rcTracker.credentialOf, noteSequenceNums: rcTracker.noteSequenceNums, dial: rcDial, newUuid: import_node_crypto21.randomUUID };
130955
131236
  const rcInterrupt = async (sessionId) => await interruptRcSession(rcControlRequestDeps, sessionId);
130956
131237
  const rcSetModel = async (sessionId, model) => await setRcSessionModel(rcControlRequestDeps, sessionId, model);
130957
131238
  const rcSetPermissionMode = async (sessionId, mode) => await setRcSessionPermissionMode(rcControlRequestDeps, sessionId, mode);
@@ -131050,7 +131331,7 @@ function realFrontDoorSupervisorPorts(paths) {
131050
131331
  },
131051
131332
  startConnectListener: async (preferredPort) => {
131052
131333
  const wantedTransparentPort = process.env.AGENT_SHIM_TRANSPARENT_SURFACE === void 0 ? void 0 : Number(process.env.AGENT_SHIM_TRANSPARENT_SURFACE);
131053
- const transparentCapability = wantedTransparentPort === void 0 || Number.isNaN(wantedTransparentPort) ? void 0 : (0, import_node_crypto20.randomUUID)();
131334
+ const transparentCapability = wantedTransparentPort === void 0 || Number.isNaN(wantedTransparentPort) ? void 0 : (0, import_node_crypto21.randomUUID)();
131054
131335
  if (transparentCapability !== void 0) {
131055
131336
  writeFrontDoorSession(realFarmFs, paths.frontdoorSessionsDir, { pid: process.pid, startedAt: Date.now(), token: transparentCapability });
131056
131337
  }
package/dist/index.cjs CHANGED
@@ -2758,7 +2758,7 @@ var init_package = __esm({
2758
2758
  "package.json"() {
2759
2759
  package_default = {
2760
2760
  name: "claude-use",
2761
- version: "11.4.1",
2761
+ version: "11.5.1",
2762
2762
  description: "A profile manager and launcher for Claude Code that lets one person run multiple logins from one machine while controlling what gets shared between them.",
2763
2763
  license: "MIT",
2764
2764
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -6758,6 +6758,10 @@ function ensureFrontDoor(params) {
6758
6758
  let replaced = false;
6759
6759
  const distrusted = /* @__PURE__ */ new Set();
6760
6760
  let lastRejection;
6761
+ let busyDoor;
6762
+ const liveSessionsOn = (doorPid) => listFrontDoorSessions(ports.fs, paths.frontdoorSessionsDir).filter(
6763
+ (session) => session.pid !== params.launcherPid && ports.isRunning(session.pid) && (session.supervisorPid === void 0 || session.supervisorPid === doorPid)
6764
+ ).length;
6761
6765
  const verifyWithRetry = (port) => {
6762
6766
  let verdict = ports.verifyListener(port);
6763
6767
  for (let attempt = 1; !verdict.ok && attempt < FRONTDOOR_PROBE_ATTEMPTS; attempt += 1) {
@@ -6799,11 +6803,17 @@ function ensureFrontDoor(params) {
6799
6803
  throw new FrontDoorStartError(`${lastRejection}; refusing to send this session's credentials to it. Daemon log: ${paths.frontdoorLogPath}`);
6800
6804
  }
6801
6805
  if (ports.isFrontDoorSupervisor(supervisorPid)) {
6802
- stopAndAwaitExit(supervisorPid, `the front door (pid ${String(supervisorPid)}) stopped answering and did not exit when asked to`);
6806
+ const stranded = liveSessionsOn(supervisorPid);
6807
+ if (stranded > 0) {
6808
+ busyDoor = { pid: supervisorPid, sessions: stranded };
6809
+ } else {
6810
+ stopAndAwaitExit(supervisorPid, `the front door (pid ${String(supervisorPid)}) stopped answering and did not exit when asked to`);
6811
+ continue;
6812
+ }
6813
+ } else {
6814
+ distrusted.add(supervisorPid);
6803
6815
  continue;
6804
6816
  }
6805
- distrusted.add(supervisorPid);
6806
- continue;
6807
6817
  }
6808
6818
  if (state?.lastError !== void 0) {
6809
6819
  throw new FrontDoorStartError(`the front door reported a fatal error and is not serving: ${state.lastError} (daemon log: ${paths.frontdoorLogPath})`);
@@ -6826,7 +6836,7 @@ function ensureFrontDoor(params) {
6826
6836
  if (ports.now() >= deadline) {
6827
6837
  const lastError = state?.lastError;
6828
6838
  throw new FrontDoorStartError(
6829
- `the front door did not become ready within ${String(FRONTDOOR_START_TIMEOUT_MS)}ms` + (lastError === void 0 ? "" : ` (last error: ${lastError})`) + (lastRejection === void 0 ? "" : ` (${lastRejection})`) + `. Daemon log: ${paths.frontdoorLogPath}`
6839
+ `the front door did not become ready within ${String(FRONTDOOR_START_TIMEOUT_MS)}ms` + (lastError === void 0 ? "" : ` (last error: ${lastError})`) + (lastRejection === void 0 ? "" : ` (${lastRejection})`) + (busyDoor === void 0 ? "" : `. It was not replaced because ${String(busyDoor.sessions)} live session(s) are using it and would lose their connections; run \`agent-shim frontdoor restart\` to replace it deliberately`) + `. Daemon log: ${paths.frontdoorLogPath}`
6830
6840
  );
6831
6841
  }
6832
6842
  ports.sleep(FRONTDOOR_LAUNCHER_POLL_MS);
package/dist/index.mjs CHANGED
@@ -2734,7 +2734,7 @@ var init_package = __esm({
2734
2734
  "package.json"() {
2735
2735
  package_default = {
2736
2736
  name: "claude-use",
2737
- version: "11.4.1",
2737
+ version: "11.5.1",
2738
2738
  description: "A profile manager and launcher for Claude Code that lets one person run multiple logins from one machine while controlling what gets shared between them.",
2739
2739
  license: "MIT",
2740
2740
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -6732,6 +6732,10 @@ function ensureFrontDoor(params) {
6732
6732
  let replaced = false;
6733
6733
  const distrusted = /* @__PURE__ */ new Set();
6734
6734
  let lastRejection;
6735
+ let busyDoor;
6736
+ const liveSessionsOn = (doorPid) => listFrontDoorSessions(ports.fs, paths.frontdoorSessionsDir).filter(
6737
+ (session) => session.pid !== params.launcherPid && ports.isRunning(session.pid) && (session.supervisorPid === void 0 || session.supervisorPid === doorPid)
6738
+ ).length;
6735
6739
  const verifyWithRetry = (port) => {
6736
6740
  let verdict = ports.verifyListener(port);
6737
6741
  for (let attempt = 1; !verdict.ok && attempt < FRONTDOOR_PROBE_ATTEMPTS; attempt += 1) {
@@ -6773,11 +6777,17 @@ function ensureFrontDoor(params) {
6773
6777
  throw new FrontDoorStartError(`${lastRejection}; refusing to send this session's credentials to it. Daemon log: ${paths.frontdoorLogPath}`);
6774
6778
  }
6775
6779
  if (ports.isFrontDoorSupervisor(supervisorPid)) {
6776
- stopAndAwaitExit(supervisorPid, `the front door (pid ${String(supervisorPid)}) stopped answering and did not exit when asked to`);
6780
+ const stranded = liveSessionsOn(supervisorPid);
6781
+ if (stranded > 0) {
6782
+ busyDoor = { pid: supervisorPid, sessions: stranded };
6783
+ } else {
6784
+ stopAndAwaitExit(supervisorPid, `the front door (pid ${String(supervisorPid)}) stopped answering and did not exit when asked to`);
6785
+ continue;
6786
+ }
6787
+ } else {
6788
+ distrusted.add(supervisorPid);
6777
6789
  continue;
6778
6790
  }
6779
- distrusted.add(supervisorPid);
6780
- continue;
6781
6791
  }
6782
6792
  if (state?.lastError !== void 0) {
6783
6793
  throw new FrontDoorStartError(`the front door reported a fatal error and is not serving: ${state.lastError} (daemon log: ${paths.frontdoorLogPath})`);
@@ -6800,7 +6810,7 @@ function ensureFrontDoor(params) {
6800
6810
  if (ports.now() >= deadline) {
6801
6811
  const lastError = state?.lastError;
6802
6812
  throw new FrontDoorStartError(
6803
- `the front door did not become ready within ${String(FRONTDOOR_START_TIMEOUT_MS)}ms` + (lastError === void 0 ? "" : ` (last error: ${lastError})`) + (lastRejection === void 0 ? "" : ` (${lastRejection})`) + `. Daemon log: ${paths.frontdoorLogPath}`
6813
+ `the front door did not become ready within ${String(FRONTDOOR_START_TIMEOUT_MS)}ms` + (lastError === void 0 ? "" : ` (last error: ${lastError})`) + (lastRejection === void 0 ? "" : ` (${lastRejection})`) + (busyDoor === void 0 ? "" : `. It was not replaced because ${String(busyDoor.sessions)} live session(s) are using it and would lose their connections; run \`agent-shim frontdoor restart\` to replace it deliberately`) + `. Daemon log: ${paths.frontdoorLogPath}`
6804
6814
  );
6805
6815
  }
6806
6816
  ports.sleep(FRONTDOOR_LAUNCHER_POLL_MS);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-use",
3
- "version": "11.4.1",
3
+ "version": "11.5.1",
4
4
  "description": "A profile manager and launcher for Claude Code that lets one person run multiple logins from one machine while controlling what gets shared between them.",
5
5
  "license": "MIT",
6
6
  "author": "Joseph Mearman <joseph@mearman.co.uk>",