claude-use 11.2.0 → 11.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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.2.0",
2737
+ version: "11.3.0",
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>",
@@ -4010,7 +4010,7 @@ function realHeadroomPort(paths, options) {
4010
4010
  ensure: ({ routesProvider }) => {
4011
4011
  const up = ensureHeadroom({
4012
4012
  paths,
4013
- launcherPid: process.pid,
4013
+ launcherPid: options.registerAs ?? process.pid,
4014
4014
  ...routesProvider ? { requiredUpstreams: () => launchRequiredUpstreams(realFarmFs, paths) } : {},
4015
4015
  ports: {
4016
4016
  fs: realFarmFs,
@@ -4028,7 +4028,7 @@ function realHeadroomPort(paths, options) {
4028
4028
  };
4029
4029
  },
4030
4030
  release: () => {
4031
- removeSession(realFarmFs, paths.headroomSessionsDir, process.pid);
4031
+ removeSession(realFarmFs, paths.headroomSessionsDir, options.registerAs ?? process.pid);
4032
4032
  }
4033
4033
  };
4034
4034
  }
@@ -5106,6 +5106,28 @@ function windowBurn(records, which) {
5106
5106
  }
5107
5107
  return (last.utilization - first.utilization) / (last.atMs - first.atMs);
5108
5108
  }
5109
+ function billedTokensOf(record) {
5110
+ const usage = record.usage;
5111
+ return (usage?.inputTokens ?? 0) + (usage?.cacheCreationInputTokens ?? 0) + (usage?.outputTokens ?? 0);
5112
+ }
5113
+ function utilisationPerBilledToken(records) {
5114
+ const points = [];
5115
+ for (const record of records) {
5116
+ const window = record.rateLimitHeaders === void 0 ? void 0 : parseUnifiedRateLimit(record.rateLimitHeaders)?.fiveHour;
5117
+ if (window?.utilization !== void 0 && window.resetsAt !== void 0) {
5118
+ points.push({ resetsAt: window.resetsAt, utilization: window.utilization, billed: billedTokensOf(record) });
5119
+ }
5120
+ }
5121
+ const latest = points.at(-1)?.resetsAt;
5122
+ const current = points.filter((point) => point.resetsAt === latest);
5123
+ const first = current.at(0);
5124
+ const last = current.at(-1);
5125
+ if (first === void 0 || last === void 0 || last.utilization <= first.utilization) {
5126
+ return void 0;
5127
+ }
5128
+ const billed = current.slice(1).reduce((sum, point) => sum + point.billed, 0);
5129
+ return billed > 0 ? (last.utilization - first.utilization) / billed : void 0;
5130
+ }
5109
5131
  function windowRatio(records) {
5110
5132
  const points = [];
5111
5133
  for (const record of records) {
@@ -5282,6 +5304,16 @@ function rankPool(input) {
5282
5304
  drySpots.push(`7d window would run dry in ${formatAge(runsDryInMs)}, before it resets in ${formatAge(sevenDay.untilResetMs)}`);
5283
5305
  }
5284
5306
  }
5307
+ const { sticky: sticky2 } = input;
5308
+ const warmHere = sticky2?.identity === candidate.identity && nowMs - Date.parse(sticky2.at) <= PROMPT_CACHE_TTL_MS;
5309
+ if (input.resuming && input.resumeContextTokens !== void 0 && fiveHour !== void 0 && !warmHere) {
5310
+ const perToken = utilisationPerBilledToken(assessment.member.records);
5311
+ const cost = perToken === void 0 ? void 0 : input.resumeContextTokens * perToken;
5312
+ if (cost !== void 0 && cost > fiveHour.remaining) {
5313
+ dryAfterMs.push(0);
5314
+ drySpots.push(`resuming a ${String(input.resumeContextTokens)}-token conversation here would cost about ${percent(cost)} of the 5h window, which has ${percent(fiveHour.remaining)} left`);
5315
+ }
5316
+ }
5285
5317
  if (drySpots.length === 0) {
5286
5318
  return { ...candidate, feasible: true };
5287
5319
  }
@@ -5631,6 +5663,7 @@ function rankNamedPool(input, poolName, stack, sticky) {
5631
5663
  members,
5632
5664
  nowMs: input.nowMs,
5633
5665
  resuming: input.resuming,
5666
+ ...input.resumeContextTokens === void 0 ? {} : { resumeContextTokens: input.resumeContextTokens },
5634
5667
  ...sticky === void 0 ? {} : { sticky },
5635
5668
  ...pool.preference === void 0 ? {} : { preference: pool.preference }
5636
5669
  });
@@ -5773,6 +5806,7 @@ function buildLayoutPaths(root) {
5773
5806
  claudeShimFile: path23.join(root, "claude-shim.json"),
5774
5807
  headroomDir: path23.join(root, "headroom"),
5775
5808
  headroomStateFile: path23.join(root, "headroom", "state.json"),
5809
+ headroomRoutingFile: path23.join(root, "headroom", "routing.json"),
5776
5810
  headroomLegacyStateFile: path23.join(root, "headroom", "state.v2.json"),
5777
5811
  headroomAllowlistFile: path23.join(root, "headroom", "run", "allowlist.txt"),
5778
5812
  headroomSocketDir: path23.join(root, "headroom", "run"),
@@ -7246,6 +7280,7 @@ function resolvePoolLaunch(params) {
7246
7280
  poolName,
7247
7281
  nowMs,
7248
7282
  resuming,
7283
+ ...params.resumeContextTokens === void 0 ? {} : { resumeContextTokens: params.resumeContextTokens },
7249
7284
  identityExists: (name) => loadIdentity(params.paths.identitiesDir, name, params.fs) !== void 0,
7250
7285
  ...stickyRead.sticky === void 0 ? {} : { sticky: stickyRead.sticky }
7251
7286
  });
@@ -7316,6 +7351,90 @@ var init_pool = __esm({
7316
7351
  }
7317
7352
  });
7318
7353
 
7354
+ // src/usage/resumeContext.ts
7355
+ import path37 from "node:path";
7356
+ function projectDirNameOf(cwd) {
7357
+ return cwd.replace(/[^A-Za-z0-9]/g, "-");
7358
+ }
7359
+ function resumeTargetOf(passthrough) {
7360
+ const end = passthrough.indexOf("--");
7361
+ const tokens = end === -1 ? passthrough : passthrough.slice(0, end);
7362
+ for (const [index, token] of tokens.entries()) {
7363
+ if (token === "--continue" || token === "-c") {
7364
+ return { kind: "latest" };
7365
+ }
7366
+ if (token === "--resume" || token === "-r") {
7367
+ const next = tokens[index + 1];
7368
+ return next === void 0 || next.startsWith("-") ? void 0 : { kind: "id", id: next };
7369
+ }
7370
+ if (token.startsWith("--resume=")) {
7371
+ const id = token.slice("--resume=".length);
7372
+ return id === "" ? void 0 : { kind: "id", id };
7373
+ }
7374
+ }
7375
+ return void 0;
7376
+ }
7377
+ function countOf(usage, key) {
7378
+ const value = key in usage ? Reflect.get(usage, key) : void 0;
7379
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : 0;
7380
+ }
7381
+ function contextTokensOfTranscript(text) {
7382
+ const lines = text.split("\n");
7383
+ for (let index = lines.length - 1; index >= 0; index -= 1) {
7384
+ const line = lines[index];
7385
+ if (line === void 0 || line === "") {
7386
+ continue;
7387
+ }
7388
+ let parsed;
7389
+ try {
7390
+ parsed = JSON.parse(line);
7391
+ } catch {
7392
+ continue;
7393
+ }
7394
+ if (typeof parsed !== "object" || parsed === null || !("type" in parsed) || parsed.type !== "assistant" || !("message" in parsed)) {
7395
+ continue;
7396
+ }
7397
+ const { message } = parsed;
7398
+ if (typeof message !== "object" || message === null || !("usage" in message) || typeof message.usage !== "object" || message.usage === null) {
7399
+ continue;
7400
+ }
7401
+ const { usage } = message;
7402
+ return countOf(usage, "input_tokens") + countOf(usage, "cache_read_input_tokens") + countOf(usage, "cache_creation_input_tokens");
7403
+ }
7404
+ return void 0;
7405
+ }
7406
+ function resumedContextTokens(fs12, params) {
7407
+ const target = resumeTargetOf(params.passthrough);
7408
+ if (target === void 0) {
7409
+ return void 0;
7410
+ }
7411
+ const projectDir = path37.join(params.claudeHome, "projects", projectDirNameOf(params.cwd));
7412
+ let file;
7413
+ if (target.kind === "id") {
7414
+ file = path37.join(projectDir, `${target.id}.jsonl`);
7415
+ } else {
7416
+ let newestMs = -Infinity;
7417
+ for (const name of fs12.readdir(projectDir)) {
7418
+ if (!name.endsWith(".jsonl")) {
7419
+ continue;
7420
+ }
7421
+ const candidate = path37.join(projectDir, name);
7422
+ const stat = fs12.lstat(candidate);
7423
+ if (stat?.kind === "file" && stat.mtimeMs > newestMs) {
7424
+ newestMs = stat.mtimeMs;
7425
+ file = candidate;
7426
+ }
7427
+ }
7428
+ }
7429
+ const text = file === void 0 ? void 0 : fs12.readFileUtf8(file);
7430
+ return text === void 0 ? void 0 : contextTokensOfTranscript(text);
7431
+ }
7432
+ var init_resumeContext = __esm({
7433
+ "src/usage/resumeContext.ts"() {
7434
+ "use strict";
7435
+ }
7436
+ });
7437
+
7319
7438
  // src/launcher/spawn.ts
7320
7439
  import os7 from "node:os";
7321
7440
  function childExitCode(result) {
@@ -7416,7 +7535,7 @@ var init_launchHook = __esm({
7416
7535
 
7417
7536
  // src/launcher.ts
7418
7537
  import { randomUUID as randomUUID4 } from "node:crypto";
7419
- import path37 from "node:path";
7538
+ import path38 from "node:path";
7420
7539
  function resolveIdentityCredential(identity, credential, env, port) {
7421
7540
  if (port === void 0) {
7422
7541
  return { ok: false, status: 1, message: `agent-shim: identity ${identity} needs its credential resolved, but this launcher has no credential port wired` };
@@ -7434,6 +7553,7 @@ function pickFromPool(decided, params, parsedArgv, readOnly) {
7434
7553
  log.error(`agent-shim: pool "${decided.pool}" was selected, but this launcher has no farm wired to read usage and pick a member with.`);
7435
7554
  return proc.exit(1);
7436
7555
  }
7556
+ const resumeContextTokens = resumedContextTokens(farmRuntime.fs, { claudeHome: farmRuntime.claudeHome, cwd: farmRuntime.cwd, passthrough: parsedArgv.rest });
7437
7557
  const resolution = resolvePoolLaunch({
7438
7558
  poolName: decided.pool,
7439
7559
  selectedVia: decided.source,
@@ -7445,6 +7565,7 @@ function pickFromPool(decided, params, parsedArgv, readOnly) {
7445
7565
  now: farmRuntime.now,
7446
7566
  sleep: farmRuntime.lock.sleep,
7447
7567
  passthrough: parsedArgv.rest,
7568
+ ...resumeContextTokens === void 0 ? {} : { resumeContextTokens },
7448
7569
  // A read-only decision never sleeps for a refused pool and never records its pick, so asking what a launch would pick leaves the next real launch's keep-warm preference as it was.
7449
7570
  wait: !readOnly && parsedArgv.wait === true,
7450
7571
  recordPick: !readOnly,
@@ -7526,7 +7647,7 @@ function decideLaunch(params, readOnly) {
7526
7647
  identityDefaultConfigProfile: loadedIdentity?.config.defaultConfigProfile,
7527
7648
  globalDefaultConfigProfile: params.globalDefaultConfigProfile
7528
7649
  });
7529
- if (configProfileDecision.name !== void 0 && params.allowMissingConfigProfile !== true && fs12.readConfigFile(path37.join(paths.configProfilesDir, `${configProfileDecision.name}.json`)) === void 0) {
7650
+ if (configProfileDecision.name !== void 0 && params.allowMissingConfigProfile !== true && fs12.readConfigFile(path38.join(paths.configProfilesDir, `${configProfileDecision.name}.json`)) === void 0) {
7530
7651
  log.error(
7531
7652
  `agent-shim: no configuration profile named "${configProfileDecision.name}" (selected via ${configProfileDecision.source}). Run \`agent-shim profile add ${configProfileDecision.name}\` first.`
7532
7653
  );
@@ -7636,7 +7757,7 @@ function decideLaunch(params, readOnly) {
7636
7757
  extraFlags: splitExtraFlags(env.CLAUDE_EXTRA_FLAGS),
7637
7758
  passthrough: parsedArgv.rest
7638
7759
  });
7639
- const configDir = configDirEscapeHatchApplies || identityDecision.name === void 0 ? env.CLAUDE_CONFIG_DIR : path37.join(paths.identitiesDir, identityDecision.name);
7760
+ const configDir = configDirEscapeHatchApplies || identityDecision.name === void 0 ? env.CLAUDE_CONFIG_DIR : path38.join(paths.identitiesDir, identityDecision.name);
7640
7761
  const decision = {
7641
7762
  identitySource: identityDecision.source,
7642
7763
  configDirEscapeHatch: identityDecision.configDirEscapeHatch,
@@ -7677,7 +7798,7 @@ function prepareLaunch(params) {
7677
7798
  );
7678
7799
  const updateCheck = runLaunchUpdateCheck({
7679
7800
  mode: params.updateMode ?? "off",
7680
- checkPath: path37.join(paths.root, "update.check"),
7801
+ checkPath: path38.join(paths.root, "update.check"),
7681
7802
  currentVersion: package_default.version,
7682
7803
  port: params.update
7683
7804
  });
@@ -7821,6 +7942,7 @@ var init_launcher = __esm({
7821
7942
  init_flags();
7822
7943
  init_extraFlags();
7823
7944
  init_pool();
7945
+ init_resumeContext();
7824
7946
  init_lock();
7825
7947
  init_spawn();
7826
7948
  init_providersStore();
@@ -7837,12 +7959,12 @@ var init_launcher = __esm({
7837
7959
  });
7838
7960
 
7839
7961
  // src/ownCli.ts
7840
- import path38 from "node:path";
7962
+ import path39 from "node:path";
7841
7963
  function agentShimCliPath(distDir = typeof __AGENT_SHIM_DIST_DIR__ === "string" ? __AGENT_SHIM_DIST_DIR__ : void 0) {
7842
7964
  if (distDir === void 0) {
7843
7965
  throw new OwnCliPathUnknownError();
7844
7966
  }
7845
- return path38.join(distDir, OWN_CLI_FILE_NAME);
7967
+ return path39.join(distDir, OWN_CLI_FILE_NAME);
7846
7968
  }
7847
7969
  var OWN_CLI_FILE_NAME, OwnCliPathUnknownError;
7848
7970
  var init_ownCli = __esm({
@@ -7931,10 +8053,10 @@ request.on("error", (error) => finish(failed, (error.code ? error.code + ": " :
7931
8053
 
7932
8054
  // src/frontdoor/trust.ts
7933
8055
  import { createHash as createHash6 } from "node:crypto";
7934
- import path39 from "node:path";
8056
+ import path40 from "node:path";
7935
8057
  function resolveTrustBundle(params) {
7936
8058
  const { caCertFile, bundlesDir, variable, inherited, fs: fs12 } = params;
7937
- if (inherited === void 0 || inherited === "" || path39.resolve(inherited) === path39.resolve(caCertFile)) {
8059
+ if (inherited === void 0 || inherited === "" || path40.resolve(inherited) === path40.resolve(caCertFile)) {
7938
8060
  return { path: caCertFile };
7939
8061
  }
7940
8062
  const caPem = fs12.readFileUtf8(caCertFile);
@@ -7952,7 +8074,7 @@ function resolveTrustBundle(params) {
7952
8074
  }
7953
8075
  const combined = `${inheritedPem.trimEnd()}
7954
8076
  ${caPem}`;
7955
- const bundle = path39.join(bundlesDir, `${createHash6("sha256").update(combined).digest("hex").slice(0, BUNDLE_NAME_HEX_DIGITS)}.pem`);
8077
+ const bundle = path40.join(bundlesDir, `${createHash6("sha256").update(combined).digest("hex").slice(0, BUNDLE_NAME_HEX_DIGITS)}.pem`);
7956
8078
  if (!fs12.exists(bundle)) {
7957
8079
  fs12.mkdirp(bundlesDir);
7958
8080
  fs12.writeFileAtomic(bundle, combined);
@@ -9013,8 +9135,8 @@ function parseConnectTarget(authority) {
9013
9135
  function isInterceptedHost(host, interceptHosts) {
9014
9136
  return interceptHosts.includes(host);
9015
9137
  }
9016
- function servedByPipeline(path40) {
9017
- return path40?.startsWith(ROUTED_PATH_PREFIX) === true;
9138
+ function servedByPipeline(path41) {
9139
+ return path41?.startsWith(ROUTED_PATH_PREFIX) === true;
9018
9140
  }
9019
9141
  var HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
9020
9142
  "connection",
@@ -9808,11 +9930,11 @@ function createRcSessionTracker(deps) {
9808
9930
  };
9809
9931
  const observeExchange = (request4) => {
9810
9932
  const method = (request4.method ?? "").toUpperCase();
9811
- const path40 = new URL(request4.url ?? "/", "http://127.0.0.1").pathname;
9812
- if (!path40.startsWith(RC_SESSIONS_PATH_PREFIX)) {
9933
+ const path41 = new URL(request4.url ?? "/", "http://127.0.0.1").pathname;
9934
+ if (!path41.startsWith(RC_SESSIONS_PATH_PREFIX)) {
9813
9935
  return void 0;
9814
9936
  }
9815
- const tail = path40.slice(RC_SESSIONS_PATH_PREFIX.length);
9937
+ const tail = path41.slice(RC_SESSIONS_PATH_PREFIX.length);
9816
9938
  const facts = {
9817
9939
  oauthAuthorization: oauthAuthorizationOf(request4.headers),
9818
9940
  anthropicVersion: singleHeader(request4.headers, "anthropic-version"),
@@ -10202,8 +10324,8 @@ var RC_READ_FILE_ENCODINGS = ["utf-8", "base64"];
10202
10324
  function isRcReadFileEncoding(value) {
10203
10325
  return RC_READ_FILE_ENCODINGS.some((encoding) => encoding === value);
10204
10326
  }
10205
- function buildRcReadFilePayload(requestId, path40, options) {
10206
- return buildRcControlRequestPayload(requestId, { subtype: "read_file", path: path40, ...options?.maxBytes === void 0 ? {} : { max_bytes: options.maxBytes }, ...options?.encoding === void 0 ? {} : { encoding: options.encoding } });
10327
+ function buildRcReadFilePayload(requestId, path41, options) {
10328
+ return buildRcControlRequestPayload(requestId, { subtype: "read_file", path: path41, ...options?.maxBytes === void 0 ? {} : { max_bytes: options.maxBytes }, ...options?.encoding === void 0 ? {} : { encoding: options.encoding } });
10207
10329
  }
10208
10330
  function buildRcFileSuggestionsPayload(requestId, query) {
10209
10331
  return buildRcControlRequestPayload(requestId, { subtype: "file_suggestions", query });
@@ -10305,15 +10427,15 @@ async function getRcSessionContextUsage(deps, sessionId, detail) {
10305
10427
  const result = await deliverRcClientPayload(deps, sessionId, buildRcGetContextUsagePayload(requestId, detail), `request the context usage of session ${sessionId}`);
10306
10428
  return result.ok ? { ...result, requestId } : result;
10307
10429
  }
10308
- async function readRcSessionFile(deps, sessionId, path40, options) {
10309
- if (path40 === "") {
10430
+ async function readRcSessionFile(deps, sessionId, path41, options) {
10431
+ if (path41 === "") {
10310
10432
  return { ok: false, message: "a read-file write needs a non-empty path: the SDK's own field names the file the worker resolves against the session's cwd, and an empty string names none" };
10311
10433
  }
10312
10434
  if (options?.maxBytes !== void 0 && (!Number.isInteger(options.maxBytes) || options.maxBytes <= 0)) {
10313
10435
  return { ok: false, message: "a read-file write's max-bytes is a positive whole number of bytes: the SDK's own field caps the read, and a zero or negative cap reads nothing" };
10314
10436
  }
10315
10437
  const requestId = deps.newUuid();
10316
- const result = await deliverRcClientPayload(deps, sessionId, buildRcReadFilePayload(requestId, path40, options), `read ${path40} from session ${sessionId}`);
10438
+ const result = await deliverRcClientPayload(deps, sessionId, buildRcReadFilePayload(requestId, path41, options), `read ${path41} from session ${sessionId}`);
10317
10439
  return result.ok ? { ...result, requestId } : result;
10318
10440
  }
10319
10441
  async function suggestRcSessionFiles(deps, sessionId, query) {
@@ -10482,11 +10604,11 @@ function createRcControlHandler(deps) {
10482
10604
  path: "read-file",
10483
10605
  read: (body) => {
10484
10606
  const session = bodyString(body, "session");
10485
- const path40 = bodyString(body, "path");
10607
+ const path41 = bodyString(body, "path");
10486
10608
  const maxBytes = "maxBytes" in body ? bodyPositiveInteger(body, "maxBytes") : void 0;
10487
10609
  const encoding = "encoding" in body && isRcReadFileEncoding(body.encoding) ? body.encoding : void 0;
10488
- const refused2 = session === void 0 || path40 === void 0 || "maxBytes" in body && maxBytes === void 0 || "encoding" in body && encoding === void 0;
10489
- return refused2 ? { error: `the read-file body must be JSON naming a non-empty session id and path, maxBytes only as a positive whole number or absent, and an encoding the SDK's own type permits (${RC_READ_FILE_ENCODINGS.join(", ")}) or none` } : { session, run: async () => await deps.readFile(session, path40, { ...maxBytes === void 0 ? {} : { maxBytes }, ...encoding === void 0 ? {} : { encoding } }) };
10610
+ const refused2 = session === void 0 || path41 === void 0 || "maxBytes" in body && maxBytes === void 0 || "encoding" in body && encoding === void 0;
10611
+ return refused2 ? { error: `the read-file body must be JSON naming a non-empty session id and path, maxBytes only as a positive whole number or absent, and an encoding the SDK's own type permits (${RC_READ_FILE_ENCODINGS.join(", ")}) or none` } : { session, run: async () => await deps.readFile(session, path41, { ...maxBytes === void 0 ? {} : { maxBytes }, ...encoding === void 0 ? {} : { encoding } }) };
10490
10612
  }
10491
10613
  },
10492
10614
  {
@@ -10796,9 +10918,9 @@ function requestIdOfBody(body) {
10796
10918
  }
10797
10919
  function frontDoorRcControl(transport, token) {
10798
10920
  const headers = { authorization: `Bearer ${token}` };
10799
- const listAnswerOf = async (path40, what) => {
10921
+ const listAnswerOf = async (path41, what) => {
10800
10922
  try {
10801
- return await transport.request({ method: "GET", path: path40, headers });
10923
+ return await transport.request({ method: "GET", path: path41, headers });
10802
10924
  } catch (error) {
10803
10925
  throw new Error(`${what}: could not reach the front door's control listener: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
10804
10926
  }
@@ -10870,10 +10992,10 @@ function frontDoorRcControl(transport, token) {
10870
10992
  }
10871
10993
  return pending;
10872
10994
  };
10873
- const writeOf = async (path40, body, describeFailure2) => {
10995
+ const writeOf = async (path41, body, describeFailure2) => {
10874
10996
  let answer;
10875
10997
  try {
10876
- answer = await transport.request({ method: "POST", path: path40, headers: { ...headers, "content-type": "application/json" }, body });
10998
+ answer = await transport.request({ method: "POST", path: path41, headers: { ...headers, "content-type": "application/json" }, body });
10877
10999
  } catch (error) {
10878
11000
  return { ok: false, message: `could not reach the front door's control listener: ${error instanceof Error ? error.message : String(error)}` };
10879
11001
  }
@@ -10904,10 +11026,10 @@ function frontDoorRcControl(transport, token) {
10904
11026
  endSession: async (sessionId, reason) => await writeOf(`${CONTROL_PATH_PREFIX}/end-session`, JSON.stringify({ session: sessionId, ...reason === void 0 ? {} : { reason } }), (status, message) => `ending ${sessionId} failed (HTTP ${String(status)}): ${message}`),
10905
11027
  getUsage: async (sessionId, skipBehaviors) => await writeOf(`${CONTROL_PATH_PREFIX}/get-usage`, JSON.stringify({ session: sessionId, ...skipBehaviors === void 0 ? {} : { skipBehaviors } }), (status, message) => `requesting the usage of ${sessionId} failed (HTTP ${String(status)}): ${message}`),
10906
11028
  getContextUsage: async (sessionId, detail) => await writeOf(`${CONTROL_PATH_PREFIX}/get-context-usage`, JSON.stringify({ session: sessionId, ...detail === void 0 ? {} : { detail } }), (status, message) => `requesting the context usage of ${sessionId} failed (HTTP ${String(status)}): ${message}`),
10907
- readFile: async (sessionId, path40, options) => await writeOf(
11029
+ readFile: async (sessionId, path41, options) => await writeOf(
10908
11030
  `${CONTROL_PATH_PREFIX}/read-file`,
10909
- JSON.stringify({ session: sessionId, path: path40, ...options?.maxBytes === void 0 ? {} : { maxBytes: options.maxBytes }, ...options?.encoding === void 0 ? {} : { encoding: options.encoding } }),
10910
- (status, message) => `reading ${path40} from ${sessionId} failed (HTTP ${String(status)}): ${message}`
11031
+ JSON.stringify({ session: sessionId, path: path41, ...options?.maxBytes === void 0 ? {} : { maxBytes: options.maxBytes }, ...options?.encoding === void 0 ? {} : { encoding: options.encoding } }),
11032
+ (status, message) => `reading ${path41} from ${sessionId} failed (HTTP ${String(status)}): ${message}`
10911
11033
  ),
10912
11034
  fileSuggestions: async (sessionId, query) => await writeOf(`${CONTROL_PATH_PREFIX}/file-suggestions`, JSON.stringify({ session: sessionId, query }), (status, message) => `requesting file suggestions from ${sessionId} failed (HTTP ${String(status)}): ${message}`),
10913
11035
  keepAlive: async (sessionId) => await writeOf(`${CONTROL_PATH_PREFIX}/keep-alive`, JSON.stringify({ session: sessionId }), (status, message) => `keeping ${sessionId} alive failed (HTTP ${String(status)}): ${message}`),
@@ -11066,12 +11188,12 @@ function tlsFacts(options) {
11066
11188
  }
11067
11189
  function realRcEventDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_PORT }) {
11068
11190
  const agent = new ExemptTlsAgent();
11069
- const post = async (path40, headers, body) => await new Promise((resolve, reject) => {
11191
+ const post = async (path41, headers, body) => await new Promise((resolve, reject) => {
11070
11192
  const options = {
11071
11193
  host: target.host,
11072
11194
  port: target.port,
11073
11195
  method: "POST",
11074
- path: path40,
11196
+ path: path41,
11075
11197
  // The Host header names the host whose API this is, not the address dialled: a redirected test dials a loopback stand-in while still speaking to the API host by name, exactly as the forwarded paths present their SNI.
11076
11198
  headers: { ...headers, host: target.servername ?? target.host, "content-length": String(Buffer.byteLength(body, "utf8")) },
11077
11199
  agent
@@ -11136,12 +11258,12 @@ function lateRcStreamDial(resolve) {
11136
11258
  }
11137
11259
  function realRcStreamDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_PORT }) {
11138
11260
  const agent = new ExemptTlsAgent();
11139
- const request4 = async (method, path40, headers, body, signal) => await new Promise((resolve, reject) => {
11261
+ const request4 = async (method, path41, headers, body, signal) => await new Promise((resolve, reject) => {
11140
11262
  const options = {
11141
11263
  host: target.host,
11142
11264
  port: target.port,
11143
11265
  method,
11144
- path: path40,
11266
+ path: path41,
11145
11267
  // The Host header names the host whose API this is, not the address dialled: a redirected test dials a loopback stand-in while still speaking to the API host by name, exactly as the forwarded paths present their SNI.
11146
11268
  headers: { ...headers, host: target.servername ?? target.host, ...body === void 0 ? {} : { "content-length": String(Buffer.byteLength(body, "utf8")) } },
11147
11269
  agent,
@@ -11183,9 +11305,9 @@ function realRcStreamDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_P
11183
11305
  return { status, body: await readAll(response) };
11184
11306
  },
11185
11307
  openStream: async (sessionId, headers, resume, signal) => {
11186
- const path40 = resume === void 0 ? `/v1/code/sessions/${encodeURIComponent(sessionId)}/events/stream` : `/v1/code/sessions/${encodeURIComponent(sessionId)}/events/stream?from_sequence_num=${String(resume.fromSequenceNum)}`;
11308
+ const path41 = resume === void 0 ? `/v1/code/sessions/${encodeURIComponent(sessionId)}/events/stream` : `/v1/code/sessions/${encodeURIComponent(sessionId)}/events/stream?from_sequence_num=${String(resume.fromSequenceNum)}`;
11187
11309
  const streamHeaders = resume === void 0 ? headers : { ...headers, "last-event-id": String(resume.fromSequenceNum) };
11188
- const { status, response } = await request4("GET", path40, streamHeaders, void 0, signal);
11310
+ const { status, response } = await request4("GET", path41, streamHeaders, void 0, signal);
11189
11311
  if (!isSuccessful2(status)) {
11190
11312
  await readAll(response);
11191
11313
  return { status, chunks: NO_CHUNKS };
@@ -12337,19 +12459,19 @@ function writePools(paths, pools) {
12337
12459
  applyPatch(paths.globalConfigFile, GlobalConfigSchema, { pools }, { defaults: {} });
12338
12460
  }
12339
12461
  function poolCycleOf(pools, name) {
12340
- const walk = (current, path40) => {
12462
+ const walk = (current, path41) => {
12341
12463
  for (const entry of pools[current]?.identities ?? []) {
12342
12464
  const nested = poolNameOf(poolMemberIdentity(entry));
12343
12465
  if (nested === void 0) {
12344
12466
  continue;
12345
12467
  }
12346
12468
  if (nested === name) {
12347
- return [...path40, nested];
12469
+ return [...path41, nested];
12348
12470
  }
12349
- if (path40.includes(nested)) {
12471
+ if (path41.includes(nested)) {
12350
12472
  continue;
12351
12473
  }
12352
- const found = walk(nested, [...path40, nested]);
12474
+ const found = walk(nested, [...path41, nested]);
12353
12475
  if (found !== void 0) {
12354
12476
  return found;
12355
12477
  }
@@ -14560,7 +14682,7 @@ function profileValue(name, paths) {
14560
14682
  function createConfigApiRouter(deps) {
14561
14683
  const authed = doorApiAuth(deps.expectedToken);
14562
14684
  const { paths } = deps;
14563
- const route = (path40, summary) => authed.meta(openapi3({ method: "POST", path: path40, summary, tags: [CONFIG_API_TAG] }));
14685
+ const route = (path41, summary) => authed.meta(openapi3({ method: "POST", path: path41, summary, tags: [CONFIG_API_TAG] }));
14564
14686
  return {
14565
14687
  config: {
14566
14688
  identity: {
@@ -1707,6 +1707,8 @@ interface LayoutPaths {
1707
1707
  readonly headroomDir: string;
1708
1708
  /** Path to the headroom supervisor's state file: pids, socket path, version, the hash of the allowlist the daemon serves, last error. One unversioned name every release shares, arbitrated by the ownership rules in `headroom/state.ts` rather than by a version in the file name. */
1709
1709
  readonly headroomStateFile: string;
1710
+ /** The live routing setting `agent-shim headroom enable|disable` writes and the front door re-reads per request: which sessions go through headroom, whatever their launch asked for. */
1711
+ readonly headroomRoutingFile: string;
1710
1712
  /** The previous release's versioned name for the state file, read only while a supervisor from that release is still alive and removed once its generation has drained (see `migrateHeadroomState`). */
1711
1713
  readonly headroomLegacyStateFile: string;
1712
1714
  /** Path to the file the headroom daemon reads its upstream allowlist from (`--allowed-base-urls-file`): the direct origin of every live front door, one per line. It lives in the owner-only socket directory because headroom refuses an allowlist file whose directory another user could write to, and that directory is the one this tool already creates mode 0700 (narrowing it if it existed wider) and checks before every daemon start. The supervisor replaces the file owner-only and atomically whenever a door's origin changes, and the daemon re-reads it on its next request, so a moved or retired front door never needs a restart. */
@@ -12753,6 +12755,8 @@ interface RankPoolInput {
12753
12755
  readonly sticky?: StickyPick;
12754
12756
  /** True when the launch continues or resumes a conversation, which belongs on the account it started on whatever the cache lifetime. */
12755
12757
  readonly resuming: boolean;
12758
+ /** The size in tokens of the context a resumed conversation continues with. A member that is not the warm account of that conversation must pay to write this context again, so one whose five-hour window cannot cover it is not feasible. */
12759
+ readonly resumeContextTokens?: number;
12756
12760
  /** How members are ordered: "score" (the default when absent) by the use-it-or-lose-it ranking, "listed" strictly in `members` order. */
12757
12761
  readonly preference?: "score" | "listed";
12758
12762
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-use",
3
- "version": "11.2.0",
3
+ "version": "11.3.0",
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>",