claude-use 9.19.0 → 10.0.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
@@ -450,7 +450,7 @@ var init_schema = __esm({
450
450
  context.addIssue({ code: "custom", path: ["readMaturation"], message: "readMaturation needs rolloutChannel beta or dev" });
451
451
  }
452
452
  });
453
- HEADROOM_DEFAULT_SOURCE = "headroom-ai[proxy] @ git+https://github.com/ExaDev/headroom@53525f479f2f48e8ef6b08cf729601feab7d2382";
453
+ HEADROOM_DEFAULT_SOURCE = "headroom-ai[proxy] @ git+https://github.com/ExaDev/headroom@ca7589c574bfd52919503a74f09f4d1fe5ecf106";
454
454
  HEADROOM_DEFAULT_IDLE_SHUTDOWN_MINUTES = 15;
455
455
  FrontDoorGlobalConfigSchema = z.strictObject({
456
456
  /** How long the front door may sit with no registered sessions before it closes and exits. Defaults to FRONTDOOR_DEFAULT_IDLE_SHUTDOWN_MINUTES. */
@@ -2585,9 +2585,71 @@ function writeFrontDoorState(fs12, stateFile, state) {
2585
2585
  fs12.writeFilePrivate(stateFile, `${JSON.stringify(state, null, 2)}
2586
2586
  `);
2587
2587
  }
2588
+ function directOrigin(port) {
2589
+ return `http://127.0.0.1:${String(port)}`;
2590
+ }
2588
2591
  function frontDoorOrigin(state) {
2589
2592
  const port = state?.directPort ?? state?.lastDirectPort;
2590
- return port === void 0 ? void 0 : `http://127.0.0.1:${String(port)}`;
2593
+ return port === void 0 ? void 0 : directOrigin(port);
2594
+ }
2595
+ function writeFrontDoorDirectRecord(fs12, recordsDir, record) {
2596
+ fs12.mkdirp(recordsDir);
2597
+ fs12.writeFilePrivate(path8.join(recordsDir, `${String(record.pid)}.json`), `${JSON.stringify({ directPort: record.directPort })}
2598
+ `);
2599
+ }
2600
+ function removeFrontDoorDirectRecord(fs12, recordsDir, pid) {
2601
+ fs12.removeRecursive(path8.join(recordsDir, `${String(pid)}.json`));
2602
+ }
2603
+ function recordPid(file) {
2604
+ const pidText = /^([1-9]\d*)\.json$/.exec(file)?.[1];
2605
+ const pid = pidText === void 0 ? void 0 : Number(pidText);
2606
+ return pid !== void 0 && Number.isSafeInteger(pid) ? pid : void 0;
2607
+ }
2608
+ function readDirectRecordEntry(fs12, recordsDir, file) {
2609
+ const pid = recordPid(file);
2610
+ if (pid === void 0) {
2611
+ return void 0;
2612
+ }
2613
+ let raw;
2614
+ try {
2615
+ raw = fs12.readFileUtf8(path8.join(recordsDir, file));
2616
+ } catch {
2617
+ return void 0;
2618
+ }
2619
+ if (raw === void 0) {
2620
+ return void 0;
2621
+ }
2622
+ try {
2623
+ const parsed = FrontDoorDirectRecordSchema.safeParse(JSON.parse(raw));
2624
+ return parsed.success ? { record: { pid, directPort: parsed.data.directPort } } : { malformed: true };
2625
+ } catch {
2626
+ return { malformed: true };
2627
+ }
2628
+ }
2629
+ function listFrontDoorDirectRecords(fs12, recordsDir) {
2630
+ return fs12.readdir(recordsDir).flatMap((file) => {
2631
+ const entry = readDirectRecordEntry(fs12, recordsDir, file);
2632
+ return entry?.record === void 0 ? [] : [entry.record];
2633
+ }).sort((a, b) => a.pid - b.pid);
2634
+ }
2635
+ function pruneMalformedFrontDoorDirectFiles(fs12, recordsDir, isRunning) {
2636
+ for (const file of fs12.readdir(recordsDir)) {
2637
+ const writer = RECORD_TEMPORARY_FILE.exec(file)?.[1];
2638
+ const stale = writer !== void 0 && !isRunning(Number(writer));
2639
+ if (stale || readDirectRecordEntry(fs12, recordsDir, file)?.malformed === true) {
2640
+ fs12.removeRecursive(path8.join(recordsDir, file));
2641
+ }
2642
+ }
2643
+ }
2644
+ function liveFrontDoorDirectRecords(records, liveness) {
2645
+ return records.filter((record) => liveness.doors.has(record.pid) || liveness.unknown.has(record.pid));
2646
+ }
2647
+ function pruneDeadFrontDoorDirectRecords(fs12, recordsDir, records, liveness) {
2648
+ for (const record of records) {
2649
+ if (!liveness.doors.has(record.pid) && !liveness.unknown.has(record.pid)) {
2650
+ removeFrontDoorDirectRecord(fs12, recordsDir, record.pid);
2651
+ }
2652
+ }
2591
2653
  }
2592
2654
  function writeFrontDoorSession(fs12, sessionsDir, session) {
2593
2655
  fs12.mkdirPrivate(sessionsDir);
@@ -2624,7 +2686,7 @@ function pruneDeadFrontDoorSessions(fs12, sessionsDir, isRunning) {
2624
2686
  }
2625
2687
  return removed;
2626
2688
  }
2627
- var FRONT_DOOR_PROTOCOL, FrontDoorSessionSchema, FrontDoorStateSchema;
2689
+ var FRONT_DOOR_PROTOCOL, FrontDoorSessionSchema, FrontDoorStateSchema, MAX_TCP_PORT, FrontDoorDirectRecordSchema, RECORD_TEMPORARY_FILE;
2628
2690
  var init_state = __esm({
2629
2691
  "src/frontdoor/state.ts"() {
2630
2692
  "use strict";
@@ -2652,7 +2714,7 @@ var init_state = __esm({
2652
2714
  */
2653
2715
  lastConnectPort: z3.number().int().positive().optional(),
2654
2716
  /**
2655
- * The loopback port the direct listener serves on: the listener a headroom hop tells the daemon to forward routed traffic back to. Sticky like the others, because headroom's allowlist admits its exact origin and an ephemeral port would invalidate the allowlist on every restart.
2717
+ * The loopback port the direct listener serves on: the listener a headroom hop tells the daemon to forward routed traffic back to. Sticky like the others, because headroom's allowlist admits its exact origin: the headroom supervisor rewrites the daemon's allowlist file when the origin moves, but a provider launch waits at its gate until it has, so an ephemeral port would hold launches up after every restart.
2656
2718
  */
2657
2719
  directPort: z3.number().int().positive().optional(),
2658
2720
  /** The sticky port preference for the direct listener, for the reason `directPort` gives. */
@@ -2660,6 +2722,9 @@ var init_state = __esm({
2660
2722
  /** The last fatal error, kept across shutdowns so `frontdoor status` can explain a front door that is not running. */
2661
2723
  lastError: z3.string().optional()
2662
2724
  });
2725
+ MAX_TCP_PORT = 65535;
2726
+ FrontDoorDirectRecordSchema = z3.strictObject({ directPort: z3.number().int().positive().max(MAX_TCP_PORT) });
2727
+ RECORD_TEMPORARY_FILE = /^[1-9]\d*\.json\.([1-9]\d*)\.tmp$/;
2663
2728
  }
2664
2729
  });
2665
2730
 
@@ -2669,7 +2734,7 @@ var init_package = __esm({
2669
2734
  "package.json"() {
2670
2735
  package_default = {
2671
2736
  name: "claude-use",
2672
- version: "9.19.0",
2737
+ version: "10.0.0",
2673
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.",
2674
2739
  license: "MIT",
2675
2740
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -2794,11 +2859,75 @@ function listFrontDoorSupervisors(run, uid) {
2794
2859
  }
2795
2860
  return { ok: true, pids: parseFrontDoorSupervisorPids(result.stdout) };
2796
2861
  }
2797
- var FRONT_DOOR_SUPERVISOR_ARGUMENT;
2862
+ function frontDoorLivenessFromPs(run, asked) {
2863
+ if (refusedEveryPidAsTooLarge(run, asked)) {
2864
+ return { doors: /* @__PURE__ */ new Set(), unknown: /* @__PURE__ */ new Map() };
2865
+ }
2866
+ const failure = psFailure(run);
2867
+ if (failure !== void 0) {
2868
+ return { doors: /* @__PURE__ */ new Set(), unknown: new Map(asked.map((pid) => [pid, failure])) };
2869
+ }
2870
+ const askedSet = new Set(asked);
2871
+ const seen = /* @__PURE__ */ new Set();
2872
+ const doors = /* @__PURE__ */ new Set();
2873
+ for (const line of run.stdout.split("\n")) {
2874
+ const [pidText, stat, ...argumentsList] = line.trim().split(/\s+/);
2875
+ const pid = Number(pidText);
2876
+ if (!askedSet.has(pid) || seen.has(pid)) {
2877
+ continue;
2878
+ }
2879
+ seen.add(pid);
2880
+ if (stat !== void 0 && !stat.startsWith("Z") && argumentsList.includes(FRONT_DOOR_SUPERVISOR_ARGUMENT)) {
2881
+ doors.add(pid);
2882
+ }
2883
+ }
2884
+ return { doors, unknown: /* @__PURE__ */ new Map() };
2885
+ }
2886
+ function refusedEveryPidAsTooLarge(run, asked) {
2887
+ const lines = run.stderr.split("\n").filter((line) => line.trim() !== "");
2888
+ const tooLarge = new Set(lines.map((line) => Number(PID_TOO_LARGE.exec(line.trim())?.[1])));
2889
+ return run.status === 1 && run.stdout.trim() === "" && lines.length > 0 && lines.every((line) => PID_TOO_LARGE.test(line.trim())) && asked.every((pid) => tooLarge.has(pid));
2890
+ }
2891
+ function psFailure(run) {
2892
+ if (run.error !== void 0) {
2893
+ return run.error.message;
2894
+ }
2895
+ if (run.status === null) {
2896
+ return "ps was terminated before it answered";
2897
+ }
2898
+ const noneExist = run.status === 1 && run.stdout.trim() === "" && run.stderr.trim() === "";
2899
+ if (run.status !== 0 && !noneExist) {
2900
+ return `ps exited ${String(run.status)}${run.stderr.trim() === "" ? "" : `: ${run.stderr.trim()}`}`;
2901
+ }
2902
+ return void 0;
2903
+ }
2904
+ function frontDoorLivenessOf(pids, runPs) {
2905
+ if (pids.length === 0) {
2906
+ return { doors: /* @__PURE__ */ new Set(), unknown: /* @__PURE__ */ new Map() };
2907
+ }
2908
+ const batch = frontDoorLivenessFromPs(runPs(pids), pids);
2909
+ if (batch.unknown.size === 0 || pids.length === 1) {
2910
+ return batch;
2911
+ }
2912
+ const doors = /* @__PURE__ */ new Set();
2913
+ const unknown = /* @__PURE__ */ new Map();
2914
+ for (const pid of pids) {
2915
+ const single = frontDoorLivenessFromPs(runPs([pid]), [pid]);
2916
+ for (const door of single.doors) {
2917
+ doors.add(door);
2918
+ }
2919
+ for (const [unanswered, reason] of single.unknown) {
2920
+ unknown.set(unanswered, reason);
2921
+ }
2922
+ }
2923
+ return { doors, unknown };
2924
+ }
2925
+ var FRONT_DOOR_SUPERVISOR_ARGUMENT, PID_TOO_LARGE;
2798
2926
  var init_supervisorProcesses = __esm({
2799
2927
  "src/frontdoor/supervisorProcesses.ts"() {
2800
2928
  "use strict";
2801
2929
  FRONT_DOOR_SUPERVISOR_ARGUMENT = "__frontdoor-supervisor";
2930
+ PID_TOO_LARGE = /^ps: process id too large: (\d+)$/;
2802
2931
  }
2803
2932
  });
2804
2933
 
@@ -2851,22 +2980,49 @@ function migrateHeadroomState(fs12, files, isRunning) {
2851
2980
  }
2852
2981
  fs12.removeRecursive(files.legacyStateFile);
2853
2982
  }
2854
- function headroomAllowlist(providers) {
2855
- const urls = /* @__PURE__ */ new Set([HEADROOM_ANTHROPIC_UPSTREAM]);
2856
- for (const provider of providers) {
2857
- urls.add(provider.baseUrl);
2983
+ function hashAllowlist(allowlist) {
2984
+ return createHash3("sha256").update(allowlist.join("\n")).digest("hex");
2985
+ }
2986
+ function formatAllowlistFile(allowlist) {
2987
+ return allowlist.map((entry) => `${entry}
2988
+ `).join("");
2989
+ }
2990
+ function hashAllowlistFile(content) {
2991
+ return hashAllowlist([...new Set(parseAllowlistFile(content))].sort());
2992
+ }
2993
+ function isBareHost(entry) {
2994
+ if (/[/?#@]/.test(entry)) {
2995
+ return false;
2996
+ }
2997
+ if (entry.startsWith("[")) {
2998
+ const close = entry.indexOf("]");
2999
+ return close > 1 && !entry.slice(close + 1).includes(":");
2858
3000
  }
2859
- return [...urls].sort();
3001
+ return entry !== "" && !entry.includes(":");
2860
3002
  }
2861
- function headroomUpstreams(providers, frontDoorOrigin2) {
2862
- const upstreams = providers.filter((provider) => !isCodexProvider(provider)).map((provider) => ({ baseUrl: provider.baseUrl }));
2863
- if (providers.length > 0 && frontDoorOrigin2 !== void 0) {
2864
- upstreams.push({ baseUrl: frontDoorOrigin2 });
3003
+ function isAdmissibleUrl(entry) {
3004
+ try {
3005
+ const url = new URL(entry);
3006
+ return ALLOWLIST_SAFE_SCHEMES.has(url.protocol) && url.hostname !== "";
3007
+ } catch {
3008
+ return false;
2865
3009
  }
2866
- return upstreams;
2867
3010
  }
2868
- function hashAllowlist(allowlist) {
2869
- return createHash3("sha256").update(allowlist.join("\n")).digest("hex");
3011
+ function parseAllowlistFile(content) {
3012
+ return content.replace(/^\uFEFF/, "").replace(ALLOWLIST_FILE_COMMENT, "").split(ALLOWLIST_FILE_SEPARATORS).map((entry) => entry.replace(ALLOWLIST_SURROUNDING_WHITESPACE, "")).filter((entry) => entry !== "" && !ALLOWLIST_ENTRY_WHITESPACE.test(entry) && (entry.includes("://") ? isAdmissibleUrl(entry) : isBareHost(entry)));
3013
+ }
3014
+ function writeAllowlistFile(fs12, allowlistFile, allowlist) {
3015
+ fs12.mkdirPrivate(path11.dirname(allowlistFile));
3016
+ fs12.writeFilePrivate(allowlistFile, formatAllowlistFile(allowlist));
3017
+ }
3018
+ function pruneStaleAllowlistTemporaries(fs12, allowlistFile, isRunning) {
3019
+ const prefix = `${path11.basename(allowlistFile)}.`;
3020
+ for (const name of fs12.readdir(path11.dirname(allowlistFile))) {
3021
+ const writer = name.startsWith(prefix) ? /^([1-9]\d*)\.tmp$/.exec(name.slice(prefix.length))?.[1] : void 0;
3022
+ if (writer !== void 0 && !isRunning(Number(writer))) {
3023
+ fs12.removeRecursive(path11.join(path11.dirname(allowlistFile), name));
3024
+ }
3025
+ }
2870
3026
  }
2871
3027
  function readAllProviders(fs12, providersDir) {
2872
3028
  const result = [];
@@ -2930,16 +3086,15 @@ function readStartLock(fs12, lockFile) {
2930
3086
  return void 0;
2931
3087
  }
2932
3088
  }
2933
- var HEADROOM_ANTHROPIC_UPSTREAM, HeadroomStateSchema, HeadroomSessionSchema, HeadroomLockSchema;
3089
+ var HeadroomStateSchema, HeadroomSessionSchema, HeadroomLockSchema, HEADROOM_ALLOWLIST_FILE_FLAG, ALLOWLIST_LINE_BREAKS, ALLOWLIST_WHITESPACE, ALLOWLIST_FILE_SEPARATORS, ALLOWLIST_FILE_COMMENT, ALLOWLIST_ENTRY_WHITESPACE, ALLOWLIST_SURROUNDING_WHITESPACE, ALLOWLIST_SAFE_SCHEMES;
2934
3090
  var init_state2 = __esm({
2935
3091
  "src/headroom/state.ts"() {
2936
3092
  "use strict";
2937
3093
  init_schema();
2938
- HEADROOM_ANTHROPIC_UPSTREAM = "https://api.anthropic.com";
2939
3094
  HeadroomStateSchema = z8.strictObject({
2940
3095
  /** The supervisor process owning the daemon. Alive means someone is keeping headroom running. */
2941
3096
  supervisorPid: z8.number().int().positive().optional(),
2942
- /** The headroom proxy process itself. Absent while the supervisor is between restarts. */
3097
+ /** The headroom proxy process itself, recorded as soon as it is spawned (before it is ready, which `socketPath` signals) so a successor can find a daemon whose supervisor died during its start. Absent while the supervisor is between restarts. */
2943
3098
  headroomPid: z8.number().int().positive().optional(),
2944
3099
  /**
2945
3100
  * The unix socket the proxy serves on, inside the owner-only socket directory. Absent until the proxy has passed its readiness check, so "socketPath is set" is itself the ready signal a launcher polls for. The front door reads it on every hop, so a restart (which keeps the generation's path) is followed without anything being relaunched.
@@ -2947,7 +3102,9 @@ var init_state2 = __esm({
2947
3102
  socketPath: z8.string().min(1).optional(),
2948
3103
  /** The `headroom --version` output of the running install. */
2949
3104
  version: z8.string().optional(),
2950
- /** Hash of the allowlist the running proxy was started with, so a provider-file change is detected as drift. */
3105
+ /**
3106
+ * Hash of the allowlist the running proxy serves. The supervisor records it in the same tick it writes that allowlist to `LayoutPaths.headroomAllowlistFile`, which the proxy re-reads on its next request, so a provider launch, whose requests come back through its front door's direct origin, waits until this names an allowlist holding that origin. A supervisor from an earlier release records here the hash of the allowlist its daemon was started with instead (see `earlierRelease.ts`).
3107
+ */
2951
3108
  allowlistHash: z8.string().optional(),
2952
3109
  /** Hash of the token-saving settings the running proxy was started with, so a settings change is detected as drift. */
2953
3110
  settingsHash: z8.string().optional(),
@@ -2958,33 +3115,133 @@ var init_state2 = __esm({
2958
3115
  });
2959
3116
  HeadroomSessionSchema = z8.strictObject({ pid: z8.number().int().positive(), startedAt: z8.number(), supervisorPid: z8.number().int().positive() });
2960
3117
  HeadroomLockSchema = z8.strictObject({ pid: z8.number().int().positive(), at: z8.number() });
3118
+ HEADROOM_ALLOWLIST_FILE_FLAG = "--allowed-base-urls-file";
3119
+ ALLOWLIST_LINE_BREAKS = "\\n\\r\\u000b\\u000c\\u001c\\u001d\\u001e\\u0085\\u2028\\u2029";
3120
+ ALLOWLIST_WHITESPACE = "\\t\\n\\u000b\\u000c\\r\\u001c-\\u001f \\u0085\\u00a0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000";
3121
+ ALLOWLIST_FILE_SEPARATORS = new RegExp(`[,\\t${ALLOWLIST_LINE_BREAKS}]`, "u");
3122
+ ALLOWLIST_FILE_COMMENT = new RegExp(`(?:^|(?<=[${ALLOWLIST_WHITESPACE}]))#[^${ALLOWLIST_LINE_BREAKS}]*`, "gu");
3123
+ ALLOWLIST_ENTRY_WHITESPACE = new RegExp(`[${ALLOWLIST_WHITESPACE}]`, "u");
3124
+ ALLOWLIST_SURROUNDING_WHITESPACE = new RegExp(`^[${ALLOWLIST_WHITESPACE}]+|[${ALLOWLIST_WHITESPACE}]+$`, "gu");
3125
+ ALLOWLIST_SAFE_SCHEMES = /* @__PURE__ */ new Set(["http:", "https:", "ws:", "wss:"]);
3126
+ }
3127
+ });
3128
+
3129
+ // src/headroom/earlierRelease.ts
3130
+ function withoutOurSocket(commandLine, socketPath) {
3131
+ for (const argument of [`--uds ${socketPath}`, `--uds=${socketPath}`]) {
3132
+ let from = 0;
3133
+ for (; ; ) {
3134
+ const index = commandLine.indexOf(argument, from);
3135
+ if (index === -1) {
3136
+ break;
3137
+ }
3138
+ const end = index + argument.length;
3139
+ const startsArgument = index === 0 || /\s/.test(commandLine.charAt(index - 1));
3140
+ const endsArgument = end === commandLine.length || /\s/.test(commandLine.charAt(end));
3141
+ if (startsArgument && endsArgument) {
3142
+ return `${commandLine.slice(0, index)} ${commandLine.slice(end)}`;
3143
+ }
3144
+ from = index + 1;
3145
+ }
3146
+ }
3147
+ return void 0;
3148
+ }
3149
+ function daemonIdentity(commandLine, socketPath) {
3150
+ if (commandLine === void 0) {
3151
+ return "unreadable";
3152
+ }
3153
+ const rest = withoutOurSocket(commandLine, socketPath);
3154
+ const argumentsList = rest === void 0 ? [] : rest.trim().split(/\s+/);
3155
+ if (rest === void 0 || !argumentsList.includes("proxy")) {
3156
+ return "not-headroom";
3157
+ }
3158
+ return argumentsList.some((argument) => argument === HEADROOM_ALLOWLIST_FILE_FLAG || argument.startsWith(`${HEADROOM_ALLOWLIST_FILE_FLAG}=`)) ? "this-release" : "earlier-release";
3159
+ }
3160
+ function earlierReleaseAllowlistHash(providers, doorOrigin) {
3161
+ const entries = /* @__PURE__ */ new Set([EARLIER_RELEASE_ANTHROPIC_UPSTREAM]);
3162
+ for (const provider of providers) {
3163
+ if (!isCodexProvider(provider)) {
3164
+ entries.add(provider.baseUrl);
3165
+ }
3166
+ }
3167
+ if (providers.length > 0 && doorOrigin !== void 0) {
3168
+ entries.add(doorOrigin);
3169
+ }
3170
+ return hashAllowlist([...entries].sort());
3171
+ }
3172
+ function earlierReleaseDaemon(fs12, paths, daemon) {
3173
+ if (daemonIdentity(daemon.commandLine, daemon.socketPath) !== "earlier-release") {
3174
+ return void 0;
3175
+ }
3176
+ const providers = readAllProviders(fs12, paths.providersDir).map((entry) => entry.provider);
3177
+ const expected = earlierReleaseAllowlistHash(providers, frontDoorOrigin(readFrontDoorState(fs12, paths.frontdoorStateFile)));
3178
+ return { admitsServingDoor: daemon.recordedHash === expected };
3179
+ }
3180
+ var EARLIER_RELEASE_ALLOWLIST, EARLIER_RELEASE_ANTHROPIC_UPSTREAM;
3181
+ var init_earlierRelease = __esm({
3182
+ "src/headroom/earlierRelease.ts"() {
3183
+ "use strict";
3184
+ init_schema();
3185
+ init_state();
3186
+ init_state2();
3187
+ EARLIER_RELEASE_ALLOWLIST = "started by an earlier release with an allowlist fixed at start; it restarts once its sessions end";
3188
+ EARLIER_RELEASE_ANTHROPIC_UPSTREAM = "https://api.anthropic.com";
2961
3189
  }
2962
3190
  });
2963
3191
 
2964
3192
  // src/headroom/ensure.ts
3193
+ function awaitingCause(daemonPid, identity, allowlistFile) {
3194
+ if (identity === "unreadable") {
3195
+ return `could not read the command line of the headroom daemon (pid ${String(daemonPid)}), so whether it reads an allowlist file is unknown, and its allowlist was not recorded as admitting this launch's upstreams within ${String(HEADROOM_START_TIMEOUT_MS)}ms`;
3196
+ }
3197
+ if (identity === "not-headroom") {
3198
+ return `the headroom daemon pid ${String(daemonPid)} the state records is no longer a headroom proxy on its socket, and no daemon was recorded as admitting this launch's upstreams within ${String(HEADROOM_START_TIMEOUT_MS)}ms`;
3199
+ }
3200
+ return `the headroom supervisor has not written an allowlist admitting this launch's upstreams to ${allowlistFile} within ${String(HEADROOM_START_TIMEOUT_MS)}ms`;
3201
+ }
3202
+ function daemonAdmits(fs12, allowlistFile, recordedHash, required) {
3203
+ const content = fs12.readFileUtf8(allowlistFile);
3204
+ if (content === void 0 || recordedHash === void 0) {
3205
+ return false;
3206
+ }
3207
+ const served = parseAllowlistFile(content);
3208
+ return recordedHash === hashAllowlistFile(content) && required.every((upstream) => served.includes(upstream));
3209
+ }
2965
3210
  function ensureHeadroom(params) {
2966
3211
  const { paths, ports } = params;
2967
3212
  const deadline = ports.now() + HEADROOM_START_TIMEOUT_MS;
2968
3213
  let spawnedSupervisor = false;
2969
- let awaitingAllowlist = false;
2970
3214
  migrateHeadroomState(ports.fs, { stateFile: paths.headroomStateFile, legacyStateFile: paths.headroomLegacyStateFile }, ports.isRunning);
3215
+ const commandLines = /* @__PURE__ */ new Map();
3216
+ const commandLineOf = (pid) => {
3217
+ if (!commandLines.has(pid)) {
3218
+ commandLines.set(pid, ports.daemonCommandLine(pid));
3219
+ }
3220
+ return commandLines.get(pid);
3221
+ };
2971
3222
  for (; ; ) {
3223
+ let awaiting;
2972
3224
  const state = resolveServingHeadroomState(ports.fs, { stateFile: paths.headroomStateFile, legacyStateFile: paths.headroomLegacyStateFile }, ports.isRunning)?.state;
2973
3225
  if (state?.supervisorPid !== void 0 && ports.isRunning(state.supervisorPid)) {
2974
3226
  const daemonUp = state.socketPath !== void 0 && state.headroomPid !== void 0 && ports.isRunning(state.headroomPid);
2975
- if (daemonUp && state.socketPath !== void 0) {
2976
- const required = params.requiredAllowlistHash?.();
2977
- if (required === void 0 || state.allowlistHash === required) {
3227
+ if (daemonUp && state.socketPath !== void 0 && state.headroomPid !== void 0) {
3228
+ const required = params.requiredUpstreams?.();
3229
+ if (required === void 0 || daemonAdmits(ports.fs, paths.headroomAllowlistFile, state.allowlistHash, required)) {
2978
3230
  writeSession(ports.fs, paths.headroomSessionsDir, { pid: params.launcherPid, startedAt: ports.now(), supervisorPid: state.supervisorPid });
2979
3231
  return { socketPath: state.socketPath };
2980
3232
  }
2981
- const holders = sessionsForSupervisor(ports.fs, paths.headroomSessionsDir, state.supervisorPid).filter((session) => ports.isRunning(session.pid));
2982
- if (holders.length > 0) {
3233
+ const commandLine = commandLineOf(state.headroomPid);
3234
+ const earlierRelease = earlierReleaseDaemon(ports.fs, paths, { commandLine, socketPath: state.socketPath, recordedHash: state.allowlistHash });
3235
+ if (earlierRelease !== void 0) {
3236
+ if (earlierRelease.admitsServingDoor) {
3237
+ writeSession(ports.fs, paths.headroomSessionsDir, { pid: params.launcherPid, startedAt: ports.now(), supervisorPid: state.supervisorPid });
3238
+ return { socketPath: state.socketPath };
3239
+ }
2983
3240
  throw new HeadroomStartError(
2984
- `the running headroom daemon was started before this launch's front door address and would refuse every request this launch sends through it. It restarts once the launches using it end (pid ${holders.map((session) => String(session.pid)).join(", ")}); end them and launch again, or launch with --no-headroom.`
3241
+ `the running headroom daemon (pid ${String(state.headroomPid)}) was ${EARLIER_RELEASE_ALLOWLIST}, and its allowlist cannot be shown to admit the serving front door; launch with --no-headroom until then. Supervisor and daemon log: ${paths.headroomLogPath}`
2985
3242
  );
2986
3243
  }
2987
- awaitingAllowlist = true;
3244
+ awaiting = { daemonPid: state.headroomPid, identity: daemonIdentity(commandLine, state.socketPath) };
2988
3245
  }
2989
3246
  if (state.lastError !== void 0) {
2990
3247
  throw new HeadroomStartError(
@@ -3010,10 +3267,11 @@ function ensureHeadroom(params) {
3010
3267
  }
3011
3268
  }
3012
3269
  if (ports.now() >= deadline) {
3013
- const lastError = state?.lastError;
3014
- throw new HeadroomStartError(
3015
- `the headroom daemon did not become ready within ${String(HEADROOM_START_TIMEOUT_MS)}ms` + (lastError === void 0 ? "" : ` (last error: ${lastError})`) + (awaitingAllowlist ? " (the running daemon's allowlist does not admit this launch's front door, and it did not restart)" : "") + `. Daemon log: ${paths.headroomLogPath}`
3016
- );
3270
+ const lastError = state?.lastError === void 0 ? "" : ` (last error: ${state.lastError})`;
3271
+ if (awaiting !== void 0) {
3272
+ throw new HeadroomStartError(`${awaitingCause(awaiting.daemonPid, awaiting.identity, paths.headroomAllowlistFile)}${lastError}, so the daemon would refuse every request this launch sends through it; launch with --no-headroom to bypass it. Supervisor and daemon log: ${paths.headroomLogPath}`);
3273
+ }
3274
+ throw new HeadroomStartError(`the headroom daemon did not become ready within ${String(HEADROOM_START_TIMEOUT_MS)}ms${lastError}. Daemon log: ${paths.headroomLogPath}`);
3017
3275
  }
3018
3276
  ports.sleep(HEADROOM_LAUNCHER_POLL_MS);
3019
3277
  }
@@ -3023,6 +3281,7 @@ var init_ensure = __esm({
3023
3281
  "src/headroom/ensure.ts"() {
3024
3282
  "use strict";
3025
3283
  init_cliError();
3284
+ init_earlierRelease();
3026
3285
  init_state2();
3027
3286
  HEADROOM_START_TIMEOUT_MS = 12e4;
3028
3287
  HEADROOM_LAUNCHER_POLL_MS = 250;
@@ -3069,6 +3328,22 @@ var init_settings = __esm({
3069
3328
  }
3070
3329
  });
3071
3330
 
3331
+ // src/frontdoor/capture.ts
3332
+ function redactText(text) {
3333
+ return SECRET_BODY_PATTERNS.reduce((accumulated, { pattern, replacement }) => accumulated.replace(pattern, replacement), text);
3334
+ }
3335
+ var SECRET_BODY_PATTERNS;
3336
+ var init_capture = __esm({
3337
+ "src/frontdoor/capture.ts"() {
3338
+ "use strict";
3339
+ SECRET_BODY_PATTERNS = [
3340
+ { pattern: /sk-ant-[A-Za-z0-9_-]{8,}/g, replacement: "<redacted>" },
3341
+ { pattern: /Bearer\s+[A-Za-z0-9._~+/=-]{8,}/gi, replacement: "Bearer <redacted>" },
3342
+ { pattern: /eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]{8,}/g, replacement: "<redacted>" }
3343
+ ];
3344
+ }
3345
+ });
3346
+
3072
3347
  // src/headroom/socket.ts
3073
3348
  import path12 from "node:path";
3074
3349
  function headroomSocketPath(paths, supervisorPid) {
@@ -3082,10 +3357,10 @@ function ownerOnlyRefusal(label, target, stat, expected, uid) {
3082
3357
  return `the headroom ${label} ${target} does not exist`;
3083
3358
  }
3084
3359
  if (stat.kind === "symlink") {
3085
- return `the headroom ${label} ${target} is a symlink; it must be the real ${expected === "dir" ? "directory" : "socket"}`;
3360
+ return `the headroom ${label} ${target} is a symlink; it must be the real ${KIND_NAMES[expected]}`;
3086
3361
  }
3087
3362
  if (stat.kind !== expected) {
3088
- return `the headroom ${label} ${target} is not a ${expected === "dir" ? "directory" : "socket"}`;
3363
+ return `the headroom ${label} ${target} is not a ${KIND_NAMES[expected]}`;
3089
3364
  }
3090
3365
  if (stat.uid !== uid) {
3091
3366
  return `the headroom ${label} ${target} is owned by uid ${String(stat.uid)}, not by this user (uid ${String(uid)})`;
@@ -3105,6 +3380,86 @@ function verifyHeadroomSocket(socketPath, ports) {
3105
3380
  const refusal2 = ownerOnlyRefusal("socket directory", directory, ports.lstat(directory), "dir", uid) ?? ownerOnlyRefusal("socket", socketPath, ports.lstat(socketPath), "socket", uid);
3106
3381
  return refusal2 === void 0 ? { socketPath } : { refused: refusal2 };
3107
3382
  }
3383
+ function pathDirectories(target, ports) {
3384
+ const pending = target.split("/").reverse();
3385
+ let current = "/";
3386
+ const directories = [];
3387
+ let hops = 0;
3388
+ for (let name = pending.pop(); name !== void 0; name = pending.pop()) {
3389
+ if (name === "" || name === ".") {
3390
+ continue;
3391
+ }
3392
+ if (name === "..") {
3393
+ current = path12.dirname(current);
3394
+ continue;
3395
+ }
3396
+ if (!directories.includes(current)) {
3397
+ directories.push(current);
3398
+ }
3399
+ const entry = path12.join(current, name);
3400
+ if (pending.length > 0 && ports.lstat(entry)?.kind === "symlink") {
3401
+ hops += 1;
3402
+ if (hops > MAX_SYMLINK_HOPS) {
3403
+ return `the symlinks on the allowlist file's path go more than ${String(MAX_SYMLINK_HOPS)} deep`;
3404
+ }
3405
+ const linkTarget = ports.readlink(entry);
3406
+ if (path12.isAbsolute(linkTarget)) {
3407
+ current = "/";
3408
+ }
3409
+ pending.push(...linkTarget.split("/").reverse());
3410
+ } else if (pending.length > 0) {
3411
+ current = entry;
3412
+ }
3413
+ }
3414
+ return directories;
3415
+ }
3416
+ function headroomDirectoryRefusal(directory, stat, uid, remedy) {
3417
+ if (stat?.kind !== "dir") {
3418
+ return `the directory ${directory} on the allowlist file's path is ${stat === void 0 ? "missing" : "not a directory"}, so headroom refuses the file`;
3419
+ }
3420
+ if (stat.uid !== uid && stat.uid !== ROOT_UID) {
3421
+ return `the directory ${directory} on the allowlist file's path is owned by uid ${String(stat.uid)}, not by this user (uid ${String(uid)}) or root, so headroom refuses the file${remedy ? `; ${MOVE_ROOT_REMEDY}` : ""}`;
3422
+ }
3423
+ if ((stat.mode & GROUP_OR_OTHER_WRITE_BITS) !== 0) {
3424
+ return `the directory ${directory} on the allowlist file's path is writable by group or others (mode ${stat.mode.toString(OCTAL_RADIX).padStart(MODE_DIGITS, "0")}), so headroom refuses the file${remedy ? `; ${stat.uid === uid && (stat.mode & STICKY_BIT) === 0 ? `run \`chmod go-w ${directory}\`` : MOVE_ROOT_REMEDY}` : ""}`;
3425
+ }
3426
+ return void 0;
3427
+ }
3428
+ function reportableAllowlistFileProblem(allowlistFile, ports, served) {
3429
+ const problem = verifyAllowlistFile(allowlistFile, ports);
3430
+ return problem?.repairable === false || served ? problem : void 0;
3431
+ }
3432
+ function verifyAllowlistFile(allowlistFile, ports) {
3433
+ const uid = ports.currentUid();
3434
+ const directories = pathDirectories(allowlistFile, ports);
3435
+ if (typeof directories === "string") {
3436
+ return { message: directories, repairable: false };
3437
+ }
3438
+ const socketDirectory = directories.at(-1);
3439
+ const headroomDirectory = socketDirectory === void 0 ? void 0 : path12.dirname(socketDirectory);
3440
+ for (const directory of directories) {
3441
+ const stat = ports.lstat(directory);
3442
+ if (directory === socketDirectory) {
3443
+ const refusal4 = ownerOnlyRefusal("allowlist directory", directory, stat, "dir", uid);
3444
+ if (refusal4 !== void 0) {
3445
+ return { message: refusal4, repairable: true };
3446
+ }
3447
+ continue;
3448
+ }
3449
+ const own = directory === headroomDirectory;
3450
+ const refusal3 = headroomDirectoryRefusal(directory, stat, uid, !own);
3451
+ if (refusal3 !== void 0) {
3452
+ return { message: refusal3, repairable: own };
3453
+ }
3454
+ }
3455
+ const fileStat = ports.lstat(allowlistFile);
3456
+ const refusal2 = ownerOnlyRefusal("allowlist file", allowlistFile, fileStat, "file", uid);
3457
+ if (refusal2 === void 0) {
3458
+ return void 0;
3459
+ }
3460
+ const replaceable = fileStat === void 0 || fileStat.kind === "file" || fileStat.kind === "symlink";
3461
+ return { message: refusal2, repairable: replaceable };
3462
+ }
3108
3463
  function prepareHeadroomSocket(fs12, ports, paths, supervisorPid) {
3109
3464
  const unsupported = platformRefusal(ports.platform);
3110
3465
  if (unsupported !== void 0) {
@@ -3136,7 +3491,7 @@ function pruneStaleHeadroomSockets(fs12, paths, isRunning) {
3136
3491
  }
3137
3492
  return removed;
3138
3493
  }
3139
- var GROUP_OR_OTHER_BITS, OCTAL_RADIX, MODE_DIGITS, HEADROOM_SOCKET_SUFFIX, HEADROOM_SOCKET_NAME, UNIX_SOCKET_PATH_MAX_BYTES, PLATFORMS_WITHOUT_UNIX_SOCKETS;
3494
+ var GROUP_OR_OTHER_BITS, OCTAL_RADIX, MODE_DIGITS, HEADROOM_SOCKET_SUFFIX, HEADROOM_SOCKET_NAME, UNIX_SOCKET_PATH_MAX_BYTES, PLATFORMS_WITHOUT_UNIX_SOCKETS, KIND_NAMES, MAX_SYMLINK_HOPS, ROOT_UID, GROUP_OR_OTHER_WRITE_BITS, STICKY_BIT, MOVE_ROOT_REMEDY;
3140
3495
  var init_socket = __esm({
3141
3496
  "src/headroom/socket.ts"() {
3142
3497
  "use strict";
@@ -3147,6 +3502,12 @@ var init_socket = __esm({
3147
3502
  HEADROOM_SOCKET_NAME = /^(\d+)\.sock$/;
3148
3503
  UNIX_SOCKET_PATH_MAX_BYTES = 103;
3149
3504
  PLATFORMS_WITHOUT_UNIX_SOCKETS = ["win32"];
3505
+ KIND_NAMES = { dir: "directory", file: "file", socket: "socket" };
3506
+ MAX_SYMLINK_HOPS = 40;
3507
+ ROOT_UID = 0;
3508
+ GROUP_OR_OTHER_WRITE_BITS = 18;
3509
+ STICKY_BIT = 512;
3510
+ MOVE_ROOT_REMEDY = "move the agent-shim root (AGENT_SHIM_HOME) to where every directory above it is owned by this user or root and not writable by group or others";
3150
3511
  }
3151
3512
  });
3152
3513
 
@@ -3175,6 +3536,7 @@ var init_source = __esm({
3175
3536
  });
3176
3537
 
3177
3538
  // src/headroom/supervisor.ts
3539
+ import path13 from "node:path";
3178
3540
  function resolveSupervisorConfig(configured) {
3179
3541
  return {
3180
3542
  source: configured.source ?? HEADROOM_DEFAULT_SOURCE,
@@ -3263,6 +3625,36 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3263
3625
  ports.log(`agent-shim headroom supervisor: ${message}`);
3264
3626
  return 1;
3265
3627
  };
3628
+ const syncAllowlistFile = (allowlist) => {
3629
+ try {
3630
+ if (fs12.readFileUtf8(paths.headroomAllowlistFile) !== formatAllowlistFile(allowlist)) {
3631
+ writeAllowlistFile(fs12, paths.headroomAllowlistFile, allowlist);
3632
+ ports.log(`agent-shim headroom supervisor: wrote allowlist [${allowlist.join(", ")}] to ${paths.headroomAllowlistFile}`);
3633
+ }
3634
+ return void 0;
3635
+ } catch (error) {
3636
+ return `could not write the headroom allowlist file ${paths.headroomAllowlistFile}: ${error instanceof Error ? error.message : String(error)}`;
3637
+ }
3638
+ };
3639
+ const allowlistFileRefusal = (allowlist) => {
3640
+ const problem = verifyAllowlistFile(paths.headroomAllowlistFile, ports.socketTrust);
3641
+ if (problem === void 0) {
3642
+ return void 0;
3643
+ }
3644
+ if (!problem.repairable) {
3645
+ return `headroom would refuse its allowlist file, and agent-shim cannot repair it: ${problem.message}`;
3646
+ }
3647
+ ports.log(`agent-shim headroom supervisor: repairing the allowlist file: ${problem.message}`);
3648
+ try {
3649
+ fs12.mkdirPrivate(paths.headroomDir);
3650
+ fs12.mkdirPrivate(path13.dirname(paths.headroomAllowlistFile));
3651
+ writeAllowlistFile(fs12, paths.headroomAllowlistFile, allowlist);
3652
+ } catch (error) {
3653
+ return `could not write the headroom allowlist file ${paths.headroomAllowlistFile}: ${error instanceof Error ? error.message : String(error)}`;
3654
+ }
3655
+ const remaining = verifyAllowlistFile(paths.headroomAllowlistFile, ports.socketTrust);
3656
+ return remaining === void 0 ? void 0 : `the allowlist file breaks agent-shim's rules for it, and rewriting it did not repair it: ${remaining.message}`;
3657
+ };
3266
3658
  const ensureInstalled = () => {
3267
3659
  const version2 = ports.headroomVersion();
3268
3660
  const versionOk = version2 !== void 0 && versionSatisfies(version2, config.source);
@@ -3286,14 +3678,20 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3286
3678
  }
3287
3679
  const { socketPath } = socket;
3288
3680
  pruneStaleHeadroomSockets(fs12, paths, ports.isRunning);
3681
+ pruneStaleAllowlistTemporaries(fs12, paths.headroomAllowlistFile, ports.isRunning);
3289
3682
  if (!ensureInstalled()) {
3290
3683
  return fail(`could not install headroom from ${config.source}: install failed (is uv installed and on PATH?)`);
3291
3684
  }
3292
3685
  const version = ports.headroomVersion() ?? "unknown";
3293
3686
  writeOwnState({ supervisorPid: ports.ownPid, version, installedSource });
3294
- ports.log(`agent-shim headroom supervisor ${String(ports.ownPid)}: managing headroom on allowlist [${allowlistOf(ports).join(", ")}]`);
3687
+ ports.log(`agent-shim headroom supervisor ${String(ports.ownPid)}: managing headroom on allowlist [${currentHeadroomAllowlistInputs(fs12, paths, ports.frontDoorLiveness).frontDoorOrigins.join(", ")}]`);
3295
3688
  const orphanPid = previousState?.headroomPid;
3296
- if (orphanPid !== void 0 && ports.isRunning(orphanPid)) {
3689
+ const orphanSocket = previousState?.socketPath ?? (previousState?.supervisorPid === void 0 ? void 0 : headroomSocketPath(paths, previousState.supervisorPid));
3690
+ const orphanIdentity = orphanPid === void 0 || orphanSocket === void 0 || !ports.isRunning(orphanPid) ? void 0 : daemonIdentity(ports.daemonCommandLine(orphanPid), orphanSocket);
3691
+ if (orphanPid !== void 0 && (orphanIdentity === "not-headroom" || orphanIdentity === "unreadable")) {
3692
+ ports.log(`agent-shim headroom supervisor: leaving pid ${String(orphanPid)} alone: the state records it as the previous daemon, but it ${orphanIdentity === "unreadable" ? "cannot be identified" : "is no longer a headroom proxy on that generation's socket"}`);
3693
+ }
3694
+ if (orphanPid !== void 0 && (orphanIdentity === "this-release" || orphanIdentity === "earlier-release")) {
3297
3695
  ports.log(`agent-shim headroom supervisor: stopping daemon pid ${String(orphanPid)} left behind by the previous supervisor`);
3298
3696
  ports.stopProcess(orphanPid);
3299
3697
  }
@@ -3301,9 +3699,19 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3301
3699
  let runningHash;
3302
3700
  let runningSettingsHash;
3303
3701
  let consecutiveFailures = 0;
3702
+ let lastExitOutput;
3304
3703
  let idleSince;
3305
3704
  let superseded = false;
3306
3705
  let ticks = 0;
3706
+ const servingState = (pid) => ({
3707
+ supervisorPid: ports.ownPid,
3708
+ headroomPid: pid,
3709
+ socketPath,
3710
+ version,
3711
+ allowlistHash: runningHash,
3712
+ settingsHash: runningSettingsHash,
3713
+ installedSource
3714
+ });
3307
3715
  for (; ; ) {
3308
3716
  if (options.tickLimit !== void 0 && ticks >= options.tickLimit) {
3309
3717
  return HEADROOM_SUPERVISOR_STILL_RUNNING;
@@ -3311,7 +3719,10 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3311
3719
  ticks += 1;
3312
3720
  config = ports.readConfig();
3313
3721
  const settingsHash = hashSettings(config.settings);
3314
- const allowlist = allowlistOf(ports);
3722
+ const inputs = currentHeadroomAllowlistInputs(fs12, paths, ports.frontDoorLiveness);
3723
+ pruneDeadFrontDoorDirectRecords(fs12, paths.frontdoorDirectRecordsDir, inputs.frontDoorRecords, inputs.frontDoorLiveness);
3724
+ pruneMalformedFrontDoorDirectFiles(fs12, paths.frontdoorDirectRecordsDir, ports.isRunning);
3725
+ const allowlist = inputs.frontDoorOrigins;
3315
3726
  const allowlistHash = hashAllowlist(allowlist);
3316
3727
  pruneDeadSessions(fs12, paths.headroomSessionsDir, ports.isRunning);
3317
3728
  const ownsNow = ownsState();
@@ -3319,6 +3730,13 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3319
3730
  ports.log("agent-shim headroom supervisor: another supervisor now owns the state file; serving this daemon's own sessions until they end");
3320
3731
  }
3321
3732
  superseded = !ownsNow;
3733
+ const allowlistFailure = ownsNow ? syncAllowlistFile(allowlist) ?? allowlistFileRefusal(allowlist) : void 0;
3734
+ if (allowlistFailure !== void 0) {
3735
+ if (headroomPid !== void 0 && ports.isRunning(headroomPid)) {
3736
+ ports.stopProcess(headroomPid);
3737
+ }
3738
+ return fail(allowlistFailure);
3739
+ }
3322
3740
  const crashed = headroomPid !== void 0 && !ports.isRunning(headroomPid);
3323
3741
  if (headroomPid === void 0 || crashed) {
3324
3742
  if (crashed) {
@@ -3333,43 +3751,53 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3333
3751
  });
3334
3752
  }
3335
3753
  if (consecutiveFailures >= HEADROOM_START_RETRY_BUDGET) {
3336
- return fail(`headroom failed to become ready ${String(consecutiveFailures)} times in a row; giving up`);
3754
+ return fail(
3755
+ `headroom failed to become ready ${String(consecutiveFailures)} times in a row; giving up` + (lastExitOutput === void 0 ? "" : `; the last attempt exited before it was ready, and its log ends: ${lastExitOutput}`)
3756
+ );
3337
3757
  }
3338
3758
  if (installedSource !== config.source && !ensureInstalled()) {
3339
3759
  return fail(`could not install headroom from ${config.source}: install failed (is uv installed and on PATH?)`);
3340
3760
  }
3341
- const pid = ports.spawnHeadroom(socketPath, allowlist, config.settings);
3342
- const ready = await waitUntilReady(ports, socketPath);
3343
- if (ready) {
3761
+ const logOffset = ports.daemonLogSize();
3762
+ const pid = ports.spawnHeadroom(socketPath, paths.headroomAllowlistFile, config.settings);
3763
+ writeOwnState({ supervisorPid: ports.ownPid, headroomPid: pid, version, installedSource });
3764
+ const outcome = await waitUntilReady(ports, socketPath, pid);
3765
+ if (outcome === "ready") {
3344
3766
  consecutiveFailures = 0;
3767
+ lastExitOutput = void 0;
3345
3768
  headroomPid = pid;
3346
3769
  runningHash = allowlistHash;
3347
3770
  runningSettingsHash = settingsHash;
3348
- const recorded = writeOwnState({
3349
- supervisorPid: ports.ownPid,
3350
- headroomPid: pid,
3351
- socketPath,
3352
- version,
3353
- allowlistHash,
3354
- settingsHash,
3355
- installedSource: config.source
3356
- });
3771
+ const recorded = writeOwnState(servingState(pid));
3357
3772
  if (recorded) {
3358
3773
  fs12.removeRecursive(paths.headroomLockFile);
3359
3774
  }
3360
3775
  idleSince = void 0;
3361
3776
  ports.log(`agent-shim headroom supervisor: headroom pid ${String(pid)} ready on unix socket ${socketPath}`);
3362
3777
  } else {
3363
- ports.stopProcess(pid);
3364
3778
  consecutiveFailures += 1;
3365
- ports.log(
3366
- `agent-shim headroom supervisor: headroom did not become ready on unix socket ${socketPath} (attempt ${String(consecutiveFailures)} of ${String(HEADROOM_START_RETRY_BUDGET)})`
3367
- );
3779
+ if (outcome === "exited") {
3780
+ lastExitOutput = lastLines(ports.readDaemonLogTail(logOffset, DAEMON_LOG_TAIL_BYTES));
3781
+ ports.log(
3782
+ `agent-shim headroom supervisor: headroom exited before it was ready on unix socket ${socketPath} (attempt ${String(consecutiveFailures)} of ${String(HEADROOM_START_RETRY_BUDGET)})`
3783
+ );
3784
+ } else {
3785
+ lastExitOutput = void 0;
3786
+ ports.stopProcess(pid);
3787
+ ports.log(
3788
+ `agent-shim headroom supervisor: headroom did not become ready on unix socket ${socketPath} (attempt ${String(consecutiveFailures)} of ${String(HEADROOM_START_RETRY_BUDGET)})`
3789
+ );
3790
+ }
3368
3791
  await ports.sleep(backoffForAttempt(consecutiveFailures));
3369
3792
  continue;
3370
3793
  }
3371
3794
  } else {
3372
- if (runningHash !== allowlistHash || runningSettingsHash !== settingsHash || installedSource !== config.source) {
3795
+ if (runningHash !== allowlistHash) {
3796
+ runningHash = allowlistHash;
3797
+ writeOwnState(servingState(headroomPid));
3798
+ ports.log(`agent-shim headroom supervisor: headroom pid ${String(headroomPid)} now serves allowlist [${allowlist.join(", ")}]`);
3799
+ }
3800
+ if (runningSettingsHash !== settingsHash || installedSource !== config.source) {
3373
3801
  if (sessionsForSupervisor(fs12, paths.headroomSessionsDir, ports.ownPid).length === 0) {
3374
3802
  ports.log("agent-shim headroom supervisor: configuration drifted and no sessions are live; restarting headroom");
3375
3803
  ports.stopProcess(headroomPid);
@@ -3398,45 +3826,65 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3398
3826
  await ports.sleep(HEADROOM_POLL_MS);
3399
3827
  }
3400
3828
  }
3401
- function currentHeadroomAllowlistInputs(fs12, paths) {
3829
+ function undeterminedFrontDoors(inputs) {
3830
+ return inputs.frontDoorRecords.flatMap((record) => {
3831
+ const reason = inputs.frontDoorLiveness.unknown.get(record.pid);
3832
+ return reason === void 0 ? [] : [{ pid: record.pid, origin: directOrigin(record.directPort), reason }];
3833
+ });
3834
+ }
3835
+ function currentHeadroomAllowlistInputs(fs12, paths, frontDoorLiveness) {
3836
+ const named = frontDoorOrigin(readFrontDoorState(fs12, paths.frontdoorStateFile));
3837
+ const records = listFrontDoorDirectRecords(fs12, paths.frontdoorDirectRecordsDir);
3838
+ const liveness = frontDoorLiveness(records.map((record) => record.pid));
3839
+ const live = liveFrontDoorDirectRecords(records, liveness).map((record) => directOrigin(record.directPort));
3402
3840
  return {
3403
- providers: readAllProviders(fs12, paths.providersDir).map((entry) => entry.provider),
3404
- frontDoorOrigin: frontDoorOrigin(readFrontDoorState(fs12, paths.frontdoorStateFile))
3841
+ frontDoorRecords: records,
3842
+ frontDoorLiveness: liveness,
3843
+ frontDoorOrigins: [.../* @__PURE__ */ new Set([...named === void 0 ? [] : [named], ...live])].sort()
3405
3844
  };
3406
3845
  }
3407
- function currentHeadroomAllowlist(fs12, paths) {
3408
- const inputs = currentHeadroomAllowlistInputs(fs12, paths);
3409
- return headroomAllowlist(headroomUpstreams(inputs.providers, inputs.frontDoorOrigin));
3410
- }
3411
- function allowlistOf(ports) {
3412
- return currentHeadroomAllowlist(ports.fs, ports.paths);
3846
+ function launchRequiredUpstreams(fs12, paths) {
3847
+ const door = frontDoorOrigin(readFrontDoorState(fs12, paths.frontdoorStateFile));
3848
+ return door === void 0 ? [] : [door];
3413
3849
  }
3414
- async function waitUntilReady(ports, socketPath) {
3850
+ async function waitUntilReady(ports, socketPath, pid) {
3415
3851
  const deadline = ports.now() + HEADROOM_READY_TIMEOUT_MS;
3416
3852
  for (; ; ) {
3417
3853
  if (await ports.ready(socketPath)) {
3418
- return true;
3854
+ return "ready";
3855
+ }
3856
+ if (!ports.isRunning(pid)) {
3857
+ return "exited";
3419
3858
  }
3420
3859
  if (ports.now() >= deadline) {
3421
- return false;
3860
+ return "timed-out";
3422
3861
  }
3423
3862
  await ports.sleep(HEADROOM_POLL_MS);
3424
3863
  }
3425
3864
  }
3426
- var HEADROOM_BACKOFF_BASE_MS, HEADROOM_BACKOFF_CAP_MS, HEADROOM_START_RETRY_BUDGET, HEADROOM_READY_TIMEOUT_MS, HEADROOM_POLL_MS, MS_PER_MINUTE2, HEADROOM_SUPERVISOR_STILL_RUNNING;
3865
+ function lastLines(output) {
3866
+ const lines = redactText(output).split("\n").map((line) => line.trim()).filter((line) => line !== "").map((line) => line.length > DAEMON_EXIT_LOG_LINE_CHARS ? `${line.slice(0, DAEMON_EXIT_LOG_LINE_CHARS)}...` : line);
3867
+ return lines.length === 0 ? "(the daemon wrote nothing)" : lines.slice(-DAEMON_EXIT_LOG_LINES).join(" | ");
3868
+ }
3869
+ var HEADROOM_BACKOFF_BASE_MS, HEADROOM_BACKOFF_CAP_MS, HEADROOM_START_RETRY_BUDGET, HEADROOM_READY_TIMEOUT_MS, DAEMON_EXIT_LOG_LINES, DAEMON_EXIT_LOG_LINE_CHARS, DAEMON_LOG_TAIL_BYTES, HEADROOM_POLL_MS, MS_PER_MINUTE2, HEADROOM_SUPERVISOR_STILL_RUNNING;
3427
3870
  var init_supervisor = __esm({
3428
3871
  "src/headroom/supervisor.ts"() {
3429
3872
  "use strict";
3430
3873
  init_schema();
3431
3874
  init_state();
3432
3875
  init_settings();
3876
+ init_capture();
3433
3877
  init_socket();
3878
+ init_earlierRelease();
3434
3879
  init_source();
3435
3880
  init_state2();
3436
3881
  HEADROOM_BACKOFF_BASE_MS = 500;
3437
3882
  HEADROOM_BACKOFF_CAP_MS = 3e4;
3438
3883
  HEADROOM_START_RETRY_BUDGET = 5;
3439
3884
  HEADROOM_READY_TIMEOUT_MS = 15e3;
3885
+ DAEMON_EXIT_LOG_LINES = 6;
3886
+ DAEMON_EXIT_LOG_LINE_CHARS = 400;
3887
+ DAEMON_LOG_TAIL_BYTES = 8192;
3440
3888
  HEADROOM_POLL_MS = 1e3;
3441
3889
  MS_PER_MINUTE2 = 6e4;
3442
3890
  HEADROOM_SUPERVISOR_STILL_RUNNING = -1;
@@ -3448,7 +3896,7 @@ import { spawn as spawn2, spawnSync } from "node:child_process";
3448
3896
  import { createHash as createHash5 } from "node:crypto";
3449
3897
  import fs3 from "node:fs";
3450
3898
  import os3 from "node:os";
3451
- import path13 from "node:path";
3899
+ import path14 from "node:path";
3452
3900
  import { isSea } from "node:sea";
3453
3901
  function isEnoent3(error) {
3454
3902
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
@@ -3478,8 +3926,18 @@ function realIsFrontDoorSupervisor(pid) {
3478
3926
  const result = spawnSync("ps", ["-o", "pid=,command=", "-p", String(pid)], { encoding: "utf8" });
3479
3927
  return result.status === 0 && parseFrontDoorSupervisorPids(result.stdout).includes(pid);
3480
3928
  }
3929
+ function realProcessCommandLine(pid) {
3930
+ const result = spawnSync("ps", ["-o", "command=", "-p", String(pid)], { encoding: "utf8", timeout: PS_TIMEOUT_MS });
3931
+ return result.status === 0 ? result.stdout.trim() : void 0;
3932
+ }
3933
+ function realFrontDoorLiveness(pids) {
3934
+ return frontDoorLivenessOf(pids, (asked) => {
3935
+ const result = spawnSync("ps", ["-o", "pid=,stat=,command=", "-p", asked.map(String).join(",")], { encoding: "utf8", timeout: PS_TIMEOUT_MS });
3936
+ return { status: result.status, stdout: result.stdout, stderr: result.stderr, ...result.error === void 0 ? {} : { error: result.error } };
3937
+ });
3938
+ }
3481
3939
  function expandHome2(filePath) {
3482
- return filePath.startsWith("~/") ? path13.join(os3.homedir(), filePath.slice(1)) : filePath;
3940
+ return filePath.startsWith("~/") ? path14.join(os3.homedir(), filePath.slice(1)) : filePath;
3483
3941
  }
3484
3942
  function hasDesktopSession() {
3485
3943
  if (process.platform === "darwin") {
@@ -3523,7 +3981,7 @@ function spawnDaemonThroughScript(script) {
3523
3981
  return (paths, subcommand, logPath) => spawnDetachedDaemon({ command: process.execPath, args: [script, subcommand] }, paths, subcommand, logPath);
3524
3982
  }
3525
3983
  function spawnDetachedDaemon(invocation, paths, subcommand, logPath) {
3526
- fs3.mkdirSync(path13.dirname(logPath), { recursive: true });
3984
+ fs3.mkdirSync(path14.dirname(logPath), { recursive: true });
3527
3985
  const logFd = fs3.openSync(logPath, "a");
3528
3986
  try {
3529
3987
  const child = spawn2(invocation.command, invocation.args, detachedDaemonSpawnOptions(logFd, { ...process.env, AGENT_SHIM_HOME: paths.root }));
@@ -3545,13 +4003,14 @@ function realHeadroomPort(paths, options) {
3545
4003
  const up = ensureHeadroom({
3546
4004
  paths,
3547
4005
  launcherPid: process.pid,
3548
- ...routesProvider ? { requiredAllowlistHash: () => hashAllowlist(currentHeadroomAllowlist(realFarmFs, paths)) } : {},
4006
+ ...routesProvider ? { requiredUpstreams: () => launchRequiredUpstreams(realFarmFs, paths) } : {},
3549
4007
  ports: {
3550
4008
  fs: realFarmFs,
3551
4009
  // Zombie-aware on purpose: a supervisor that died while still a child of this launcher sits unreaped until the launcher itself exits, and a defunct supervisor answering signal 0 as alive would stretch every launch to the full start timeout.
3552
4010
  isRunning: realIsProcessRunning,
3553
4011
  now: () => Date.now(),
3554
4012
  sleep: realSleepSync,
4013
+ daemonCommandLine: realProcessCommandLine,
3555
4014
  spawnSupervisor: (layout) => spawnHeadroomSupervisor(layout, options.spawnDaemon)
3556
4015
  }
3557
4016
  });
@@ -3576,7 +4035,7 @@ function listVersionsDir(dir) {
3576
4035
  throw error;
3577
4036
  }
3578
4037
  return entries.filter((entry) => entry.isFile()).map((entry) => {
3579
- const fullPath = path13.join(dir, entry.name);
4038
+ const fullPath = path14.join(dir, entry.name);
3580
4039
  const stat = fs3.statSync(fullPath);
3581
4040
  return {
3582
4041
  name: entry.name,
@@ -3590,7 +4049,7 @@ function windowsPathExtensions(pathext) {
3590
4049
  return (pathext ?? WINDOWS_DEFAULT_PATHEXT).split(";").map((ext) => ext.trim()).filter((ext) => ext.length > 0);
3591
4050
  }
3592
4051
  function resolveExecutableCandidate(dir, name, env) {
3593
- const candidate = path13.join(dir, name);
4052
+ const candidate = path14.join(dir, name);
3594
4053
  if (env.platform === "win32") {
3595
4054
  const extensions = windowsPathExtensions(env.pathext);
3596
4055
  const alreadyHasRecognisedExtension = extensions.some((ext) => candidate.toLowerCase().endsWith(ext.toLowerCase()));
@@ -3639,13 +4098,13 @@ function resolveOwnExecutablePath(env) {
3639
4098
  if (env.argv1 !== void 0 && !env.argv1.includes("/") && !env.argv1.includes("\\")) {
3640
4099
  return searchPathForExecutable(env.pathDirs, env.argv1, env.findExecutableInDir) ?? env.execPath;
3641
4100
  }
3642
- const candidate = env.argv1 === void 0 ? env.execPath : path13.resolve(env.cwd, env.argv1);
3643
- const candidateDir = path13.dirname(candidate);
3644
- const candidateDirOnPath = env.pathDirs.some((dir) => path13.resolve(dir) === candidateDir);
4101
+ const candidate = env.argv1 === void 0 ? env.execPath : path14.resolve(env.cwd, env.argv1);
4102
+ const candidateDir = path14.dirname(candidate);
4103
+ const candidateDirOnPath = env.pathDirs.some((dir) => path14.resolve(dir) === candidateDir);
3645
4104
  if (candidateDirOnPath) {
3646
4105
  return candidate;
3647
4106
  }
3648
- const viaPath = searchPathForExecutable(env.pathDirs, path13.basename(candidate), env.findExecutableInDir);
4107
+ const viaPath = searchPathForExecutable(env.pathDirs, path14.basename(candidate), env.findExecutableInDir);
3649
4108
  return viaPath ?? candidate;
3650
4109
  }
3651
4110
  function realOwnExecutablePath() {
@@ -3653,7 +4112,7 @@ function realOwnExecutablePath() {
3653
4112
  argv1: process.argv[1],
3654
4113
  execPath: process.execPath,
3655
4114
  cwd: process.cwd(),
3656
- pathDirs: (process.env.PATH ?? "").split(path13.delimiter).filter((dir) => dir !== ""),
4115
+ pathDirs: (process.env.PATH ?? "").split(path14.delimiter).filter((dir) => dir !== ""),
3657
4116
  findExecutableInDir
3658
4117
  });
3659
4118
  }
@@ -3666,7 +4125,7 @@ function realContentSourcePath() {
3666
4125
  function realResolveClaudeBinary(isOwnBinary) {
3667
4126
  return (request4) => discoverClaudeBinary({
3668
4127
  listVersionsDir,
3669
- pathDirs: (process.env.PATH ?? "").split(path13.delimiter).filter((dir) => dir !== ""),
4128
+ pathDirs: (process.env.PATH ?? "").split(path14.delimiter).filter((dir) => dir !== ""),
3670
4129
  findExecutableInDir,
3671
4130
  isOwnBinary,
3672
4131
  ...request4?.version === void 0 ? {} : { version: request4.version }
@@ -3675,7 +4134,7 @@ function realResolveClaudeBinary(isOwnBinary) {
3675
4134
  function realInstalledClaudeVersions() {
3676
4135
  return installedVersions(listVersionsDir(defaultVersionsDir()));
3677
4136
  }
3678
- var realFsPort, OWNER_ONLY_DIR_MODE, OWNER_ONLY_FILE_MODE, PERMISSION_BITS, realFarmFs, INT32_BYTE_LENGTH, realRunPort, MACOS_GUI_SESSION_MANAGER, realCredentialPort, SOCKET_PERMISSION_BITS, realHeadroomSocketTrust, realSpawnPort, realProcPort, EXECUTE_BITS_MASK, WINDOWS_DEFAULT_PATHEXT;
4137
+ var realFsPort, OWNER_ONLY_DIR_MODE, OWNER_ONLY_FILE_MODE, PERMISSION_BITS, realFarmFs, INT32_BYTE_LENGTH, PS_TIMEOUT_MS, realRunPort, MACOS_GUI_SESSION_MANAGER, realCredentialPort, SOCKET_PERMISSION_BITS, realHeadroomSocketTrust, realSpawnPort, realProcPort, EXECUTE_BITS_MASK, WINDOWS_DEFAULT_PATHEXT;
3679
4138
  var init_realPorts = __esm({
3680
4139
  "src/realPorts.ts"() {
3681
4140
  "use strict";
@@ -3769,9 +4228,14 @@ var init_realPorts = __esm({
3769
4228
  },
3770
4229
  writeFilePrivate(filePath, contents) {
3771
4230
  const temp = `${filePath}.${String(process.pid)}.tmp`;
3772
- fs3.writeFileSync(temp, contents, { encoding: "utf8", mode: OWNER_ONLY_FILE_MODE });
3773
- fs3.chmodSync(temp, OWNER_ONLY_FILE_MODE);
3774
- fs3.renameSync(temp, filePath);
4231
+ try {
4232
+ fs3.writeFileSync(temp, contents, { encoding: "utf8", mode: OWNER_ONLY_FILE_MODE });
4233
+ fs3.chmodSync(temp, OWNER_ONLY_FILE_MODE);
4234
+ fs3.renameSync(temp, filePath);
4235
+ } catch (error) {
4236
+ fs3.rmSync(temp, { force: true });
4237
+ throw error;
4238
+ }
3775
4239
  },
3776
4240
  appendFilePrivate(filePath, contents) {
3777
4241
  const descriptor = fs3.openSync(filePath, "a", OWNER_ONLY_FILE_MODE);
@@ -3811,6 +4275,7 @@ var init_realPorts = __esm({
3811
4275
  }
3812
4276
  };
3813
4277
  INT32_BYTE_LENGTH = 4;
4278
+ PS_TIMEOUT_MS = 2e3;
3814
4279
  realRunPort = {
3815
4280
  run(command, args) {
3816
4281
  const result = spawnSync(command, [...args], { encoding: "utf8" });
@@ -3849,7 +4314,7 @@ var init_realPorts = __esm({
3849
4314
  },
3850
4315
  personPresent: () => process.stdin.isTTY || hasDesktopSession()
3851
4316
  };
3852
- SOCKET_PERMISSION_BITS = 511;
4317
+ SOCKET_PERMISSION_BITS = 4095;
3853
4318
  realHeadroomSocketTrust = {
3854
4319
  platform: process.platform,
3855
4320
  currentUid: () => {
@@ -3868,9 +4333,10 @@ var init_realPorts = __esm({
3868
4333
  }
3869
4334
  throw error;
3870
4335
  }
3871
- const kind = stat.isSymbolicLink() ? "symlink" : stat.isDirectory() ? "dir" : stat.isSocket() ? "socket" : "other";
4336
+ const kind = stat.isSymbolicLink() ? "symlink" : stat.isDirectory() ? "dir" : stat.isFile() ? "file" : stat.isSocket() ? "socket" : "other";
3872
4337
  return { kind, uid: stat.uid, mode: stat.mode & SOCKET_PERMISSION_BITS };
3873
- }
4338
+ },
4339
+ readlink: (target) => fs3.readlinkSync(target, "utf8")
3874
4340
  };
3875
4341
  realSpawnPort = {
3876
4342
  spawnSync(command, args, options) {
@@ -3889,18 +4355,18 @@ var init_realPorts = __esm({
3889
4355
  });
3890
4356
 
3891
4357
  // src/pathNorm.ts
3892
- import path14 from "node:path";
4358
+ import path15 from "node:path";
3893
4359
  function expandTilde(value, home) {
3894
4360
  if (value === "~") {
3895
4361
  return home;
3896
4362
  }
3897
4363
  if (value.startsWith("~/")) {
3898
- return path14.join(home, value.slice(2));
4364
+ return path15.join(home, value.slice(2));
3899
4365
  }
3900
4366
  return value;
3901
4367
  }
3902
4368
  function normaliseRulePath(rulePath, home) {
3903
- return path14.resolve(expandTilde(rulePath, home));
4369
+ return path15.resolve(expandTilde(rulePath, home));
3904
4370
  }
3905
4371
  var init_pathNorm = __esm({
3906
4372
  "src/pathNorm.ts"() {
@@ -3909,12 +4375,12 @@ var init_pathNorm = __esm({
3909
4375
  });
3910
4376
 
3911
4377
  // src/launcher/cascade.ts
3912
- import path15 from "node:path";
4378
+ import path16 from "node:path";
3913
4379
  function loadCascadeInput(params) {
3914
4380
  const globalConfig = loadConfigFile(params.paths.globalConfigFile, GlobalConfigSchema, params.read);
3915
4381
  const directoryRules = loadConfigFile(params.paths.directoryRulesFile, DirectoryRulesSchema, params.read);
3916
4382
  const loadProfile = (name) => {
3917
- const filepath = path15.join(params.paths.configProfilesDir, `${name}.json`);
4383
+ const filepath = path16.join(params.paths.configProfilesDir, `${name}.json`);
3918
4384
  const loaded = loadConfigFile(filepath, ConfigProfileSchema, params.read);
3919
4385
  if (loaded === void 0) {
3920
4386
  return void 0;
@@ -3929,8 +4395,8 @@ function loadCascadeInput(params) {
3929
4395
  });
3930
4396
  const levels = [];
3931
4397
  for (const dir of dirs) {
3932
- const portable = loadConfigFile(path15.join(dir, PORTABLE_CONFIG_FILENAME), PortableConfigSchema, params.read) ?? loadConfigFile(path15.join(dir, LEGACY_PORTABLE_CONFIG_FILENAME), PortableConfigSchema, params.read);
3933
- const portableLocal = loadConfigFile(path15.join(dir, PORTABLE_LOCAL_CONFIG_FILENAME), PortableConfigSchema, params.read) ?? loadConfigFile(path15.join(dir, LEGACY_PORTABLE_LOCAL_CONFIG_FILENAME), PortableConfigSchema, params.read);
4398
+ const portable = loadConfigFile(path16.join(dir, PORTABLE_CONFIG_FILENAME), PortableConfigSchema, params.read) ?? loadConfigFile(path16.join(dir, LEGACY_PORTABLE_CONFIG_FILENAME), PortableConfigSchema, params.read);
4399
+ const portableLocal = loadConfigFile(path16.join(dir, PORTABLE_LOCAL_CONFIG_FILENAME), PortableConfigSchema, params.read) ?? loadConfigFile(path16.join(dir, LEGACY_PORTABLE_LOCAL_CONFIG_FILENAME), PortableConfigSchema, params.read);
3934
4400
  const rules = (directoryRules?.config.rules ?? []).map((rule, index) => ({ rule, index })).filter(({ rule }) => normaliseRulePath(rule.path, params.home) === dir).map(({ rule, index }) => {
3935
4401
  const entryOrder = directoryRules?.ruleEntryOrders[index];
3936
4402
  return {
@@ -3992,7 +4458,7 @@ var init_cascade = __esm({
3992
4458
  });
3993
4459
 
3994
4460
  // src/launcher/identity.ts
3995
- import path16 from "node:path";
4461
+ import path17 from "node:path";
3996
4462
  function poolNameOf(selector) {
3997
4463
  return selector.startsWith(POOL_SELECTOR_PREFIX) ? selector.slice(POOL_SELECTOR_PREFIX.length) : void 0;
3998
4464
  }
@@ -4045,7 +4511,7 @@ function decideConfigProfile(params) {
4045
4511
  return { source: "none" };
4046
4512
  }
4047
4513
  function loadIdentity(identitiesDir, name, fs12) {
4048
- return loadConfigFile(path16.join(identitiesDir, name, "identity.json"), IdentitySchema, fs12.readConfigFile);
4514
+ return loadConfigFile(path17.join(identitiesDir, name, "identity.json"), IdentitySchema, fs12.readConfigFile);
4049
4515
  }
4050
4516
  var IDENTITY_DECISION_SOURCES, CONFIG_PROFILE_DECISION_SOURCES;
4051
4517
  var init_identity = __esm({
@@ -4265,7 +4731,7 @@ var init_schema2 = __esm({
4265
4731
  });
4266
4732
 
4267
4733
  // src/usage/account.ts
4268
- import path17 from "node:path";
4734
+ import path18 from "node:path";
4269
4735
  function isObject(value) {
4270
4736
  return typeof value === "object" && value !== null && !Array.isArray(value);
4271
4737
  }
@@ -4273,7 +4739,7 @@ function isIdentityName(name) {
4273
4739
  return IdentitySchema.shape.name.safeParse(name).success;
4274
4740
  }
4275
4741
  function claudeJsonPath(identitiesDir, identity) {
4276
- return path17.join(identitiesDir, identity, CLAUDE_JSON);
4742
+ return path18.join(identitiesDir, identity, CLAUDE_JSON);
4277
4743
  }
4278
4744
  function readAccountMetadata(fs12, identitiesDir, identity) {
4279
4745
  if (!isIdentityName(identity)) {
@@ -4865,7 +5331,7 @@ var init_pick = __esm({
4865
5331
  });
4866
5332
 
4867
5333
  // src/usage/read.ts
4868
- import path18 from "node:path";
5334
+ import path19 from "node:path";
4869
5335
  function segmentDay(epochMs) {
4870
5336
  return new Date(epochMs).toISOString().slice(0, "YYYY-MM-DD".length);
4871
5337
  }
@@ -4890,7 +5356,7 @@ function readUsageLog(fs12, logDir, options = {}) {
4890
5356
  if (sinceMs !== void 0 && segment.endsAt <= sinceMs) {
4891
5357
  continue;
4892
5358
  }
4893
- const contents = fs12.readFileUtf8(path18.join(logDir, segment.name));
5359
+ const contents = fs12.readFileUtf8(path19.join(logDir, segment.name));
4894
5360
  if (contents === void 0) {
4895
5361
  continue;
4896
5362
  }
@@ -4924,7 +5390,7 @@ function snapshotPath(snapshotsDir, identity) {
4924
5390
  if (!isIdentityName(identity)) {
4925
5391
  throw new Error(`"${identity}" is not a valid identity name.`);
4926
5392
  }
4927
- return path18.join(snapshotsDir, `${identity}.json`);
5393
+ return path19.join(snapshotsDir, `${identity}.json`);
4928
5394
  }
4929
5395
  function readUsageSnapshot(fs12, snapshotsDir, identity) {
4930
5396
  const file = snapshotPath(snapshotsDir, identity);
@@ -4977,12 +5443,12 @@ var init_read = __esm({
4977
5443
  });
4978
5444
 
4979
5445
  // src/usage/store.ts
4980
- import path19 from "node:path";
5446
+ import path20 from "node:path";
4981
5447
  function pruneUsageLog(fs12, logDir, nowMs) {
4982
5448
  const cutoff = nowMs - USAGE_RETENTION_MS;
4983
5449
  for (const segment of listLogSegments(fs12, logDir)) {
4984
5450
  if (segment.endsAt <= cutoff) {
4985
- fs12.removeRecursive(path19.join(logDir, segment.name));
5451
+ fs12.removeRecursive(path20.join(logDir, segment.name));
4986
5452
  }
4987
5453
  }
4988
5454
  }
@@ -5064,7 +5530,7 @@ function createUsageStore(deps) {
5064
5530
  pruneUsageLog(fs12, paths.usageLogDir, nowMs);
5065
5531
  prunedDay = day;
5066
5532
  }
5067
- fs12.appendFilePrivate(path19.join(paths.usageLogDir, segmentName(day, deps.pid)), `${JSON.stringify(record)}
5533
+ fs12.appendFilePrivate(path20.join(paths.usageLogDir, segmentName(day, deps.pid)), `${JSON.stringify(record)}
5068
5534
  `);
5069
5535
  if (record.identity !== void 0) {
5070
5536
  rewriteSnapshot(record.identity, (current) => ({ ...current?.providers, [record.provider]: foldProviderState(current?.providers[record.provider], record) }));
@@ -5102,7 +5568,7 @@ var init_store2 = __esm({
5102
5568
  });
5103
5569
 
5104
5570
  // src/usage/poolPick.ts
5105
- import path20 from "node:path";
5571
+ import path21 from "node:path";
5106
5572
  import { z as z13 } from "zod";
5107
5573
  function errorMessage(error) {
5108
5574
  return error instanceof Error ? error.message : String(error);
@@ -5230,7 +5696,7 @@ function recordStickyPick(fs12, picksFile, directory, identity, nowMs) {
5230
5696
  const current = readPicksFile(fs12, picksFile).file.picks;
5231
5697
  const kept = Object.entries(current).filter(([, entry]) => nowMs - Date.parse(entry.at) <= USAGE_RETENTION_MS);
5232
5698
  const next = { schemaVersion: SCHEMA_VERSION, picks: { ...Object.fromEntries(kept), [directory]: { identity, at: new Date(nowMs).toISOString() } } };
5233
- fs12.mkdirPrivate(path20.dirname(picksFile));
5699
+ fs12.mkdirPrivate(path21.dirname(picksFile));
5234
5700
  fs12.writeFilePrivate(picksFile, `${JSON.stringify(next, null, 2)}
5235
5701
  `);
5236
5702
  }
@@ -5264,14 +5730,14 @@ var init_poolPick = __esm({
5264
5730
  // src/paths.ts
5265
5731
  import fs5 from "node:fs";
5266
5732
  import os5 from "node:os";
5267
- import path22 from "node:path";
5733
+ import path23 from "node:path";
5268
5734
  function resolveAgentShimHome(env = process.env, home = os5.homedir(), isDirectory = isExistingDirectory) {
5269
5735
  const fromEnv = env.AGENT_SHIM_HOME;
5270
5736
  if (fromEnv !== void 0 && fromEnv !== "") {
5271
5737
  return fromEnv;
5272
5738
  }
5273
- const current = path22.join(home, HOME_DIRNAME);
5274
- const legacy = path22.join(home, LEGACY_HOME_DIRNAME);
5739
+ const current = path23.join(home, HOME_DIRNAME);
5740
+ const legacy = path23.join(home, LEGACY_HOME_DIRNAME);
5275
5741
  return !isDirectory(current) && isDirectory(legacy) ? legacy : current;
5276
5742
  }
5277
5743
  function isExistingDirectory(candidate) {
@@ -5282,47 +5748,49 @@ function resolveClaudeHome() {
5282
5748
  if (fromEnv !== void 0 && fromEnv !== "") {
5283
5749
  return fromEnv;
5284
5750
  }
5285
- return path22.join(os5.homedir(), ".claude");
5751
+ return path23.join(os5.homedir(), ".claude");
5286
5752
  }
5287
5753
  function buildLayoutPaths(root) {
5288
5754
  return {
5289
5755
  root,
5290
- identitiesDir: path22.join(root, "identities"),
5291
- configProfilesDir: path22.join(root, "config-profiles"),
5292
- providersDir: path22.join(root, "providers"),
5293
- directoryRulesFile: path22.join(root, "directory-rules.json"),
5294
- activeIdentityFile: path22.join(root, "active-identity"),
5295
- globalConfigFile: path22.join(root, "config.json"),
5296
- categoriesLocalFile: path22.join(root, "categories.local.json"),
5297
- claudeShimFile: path22.join(root, "claude-shim.json"),
5298
- headroomDir: path22.join(root, "headroom"),
5299
- headroomStateFile: path22.join(root, "headroom", "state.json"),
5300
- headroomLegacyStateFile: path22.join(root, "headroom", "state.v2.json"),
5301
- headroomSocketDir: path22.join(root, "headroom", "run"),
5302
- headroomLockFile: path22.join(root, "headroom", "start.lock"),
5303
- headroomSessionsDir: path22.join(root, "headroom", "sessions"),
5304
- logsDir: path22.join(root, "logs"),
5305
- headroomLogPath: path22.join(root, "logs", "headroom.log"),
5306
- frontdoorDir: path22.join(root, "frontdoor"),
5307
- frontdoorCaDir: path22.join(root, "frontdoor", "ca"),
5308
- frontdoorCaCertFile: path22.join(root, "frontdoor", "ca", "ca.pem"),
5309
- frontdoorCaKeyFile: path22.join(root, "frontdoor", "ca", "ca.key"),
5310
- frontdoorCaBundlesDir: path22.join(root, "frontdoor", "ca", "bundles"),
5311
- chatgptSignInFile: path22.join(root, "codex", "chatgpt-sign-in.json"),
5312
- frontdoorStateFile: path22.join(root, "frontdoor", "state.json"),
5313
- frontdoorLockFile: path22.join(root, "frontdoor", "start.lock"),
5314
- frontdoorControlTokenFile: path22.join(root, "frontdoor", "control-token"),
5315
- frontdoorSessionsDir: path22.join(root, "frontdoor", "sessions"),
5316
- frontdoorRcCredentialsDir: path22.join(root, "frontdoor", "rc-credentials"),
5317
- frontdoorLogPath: path22.join(root, "logs", "frontdoor.log"),
5318
- usageDir: path22.join(root, "usage"),
5319
- usageLogDir: path22.join(root, "usage", "log"),
5320
- usageSnapshotsDir: path22.join(root, "usage", "snapshots"),
5321
- usagePicksFile: path22.join(root, "usage", "picks.json"),
5322
- usageAnchorsFile: path22.join(root, "usage", "anchors.json"),
5323
- usageJobsFile: path22.join(root, "usage", "jobs.json"),
5324
- jobLogsDir: path22.join(root, "logs", "jobs"),
5325
- mcpPermissionsFile: path22.join(root, "mcp", "permissions.json")
5756
+ identitiesDir: path23.join(root, "identities"),
5757
+ configProfilesDir: path23.join(root, "config-profiles"),
5758
+ providersDir: path23.join(root, "providers"),
5759
+ directoryRulesFile: path23.join(root, "directory-rules.json"),
5760
+ activeIdentityFile: path23.join(root, "active-identity"),
5761
+ globalConfigFile: path23.join(root, "config.json"),
5762
+ categoriesLocalFile: path23.join(root, "categories.local.json"),
5763
+ claudeShimFile: path23.join(root, "claude-shim.json"),
5764
+ headroomDir: path23.join(root, "headroom"),
5765
+ headroomStateFile: path23.join(root, "headroom", "state.json"),
5766
+ headroomLegacyStateFile: path23.join(root, "headroom", "state.v2.json"),
5767
+ headroomAllowlistFile: path23.join(root, "headroom", "run", "allowlist.txt"),
5768
+ headroomSocketDir: path23.join(root, "headroom", "run"),
5769
+ headroomLockFile: path23.join(root, "headroom", "start.lock"),
5770
+ headroomSessionsDir: path23.join(root, "headroom", "sessions"),
5771
+ logsDir: path23.join(root, "logs"),
5772
+ headroomLogPath: path23.join(root, "logs", "headroom.log"),
5773
+ frontdoorDir: path23.join(root, "frontdoor"),
5774
+ frontdoorCaDir: path23.join(root, "frontdoor", "ca"),
5775
+ frontdoorCaCertFile: path23.join(root, "frontdoor", "ca", "ca.pem"),
5776
+ frontdoorCaKeyFile: path23.join(root, "frontdoor", "ca", "ca.key"),
5777
+ frontdoorCaBundlesDir: path23.join(root, "frontdoor", "ca", "bundles"),
5778
+ chatgptSignInFile: path23.join(root, "codex", "chatgpt-sign-in.json"),
5779
+ frontdoorStateFile: path23.join(root, "frontdoor", "state.json"),
5780
+ frontdoorLockFile: path23.join(root, "frontdoor", "start.lock"),
5781
+ frontdoorControlTokenFile: path23.join(root, "frontdoor", "control-token"),
5782
+ frontdoorSessionsDir: path23.join(root, "frontdoor", "sessions"),
5783
+ frontdoorDirectRecordsDir: path23.join(root, "frontdoor", "direct"),
5784
+ frontdoorRcCredentialsDir: path23.join(root, "frontdoor", "rc-credentials"),
5785
+ frontdoorLogPath: path23.join(root, "logs", "frontdoor.log"),
5786
+ usageDir: path23.join(root, "usage"),
5787
+ usageLogDir: path23.join(root, "usage", "log"),
5788
+ usageSnapshotsDir: path23.join(root, "usage", "snapshots"),
5789
+ usagePicksFile: path23.join(root, "usage", "picks.json"),
5790
+ usageAnchorsFile: path23.join(root, "usage", "anchors.json"),
5791
+ usageJobsFile: path23.join(root, "usage", "jobs.json"),
5792
+ jobLogsDir: path23.join(root, "logs", "jobs"),
5793
+ mcpPermissionsFile: path23.join(root, "mcp", "permissions.json")
5326
5794
  };
5327
5795
  }
5328
5796
  function resolveLayoutPaths() {
@@ -5414,7 +5882,7 @@ var init_credentialCache = __esm({
5414
5882
  // src/realCredentialCache.ts
5415
5883
  import { spawnSync as spawnSync2 } from "node:child_process";
5416
5884
  import fs6 from "node:fs";
5417
- import path23 from "node:path";
5885
+ import path24 from "node:path";
5418
5886
  function cacheFileNameFor(owner) {
5419
5887
  return `${owner.replace(/[^A-Za-z0-9._-]/g, "_")}.json`;
5420
5888
  }
@@ -5438,8 +5906,8 @@ function security(args, stdin) {
5438
5906
  return { status: result.status, stdout: result.stdout };
5439
5907
  }
5440
5908
  function createRealCredentialCache(root) {
5441
- const dir = path23.join(root, "credential-cache");
5442
- const filePath = (owner) => path23.join(dir, cacheFileNameFor(owner));
5909
+ const dir = path24.join(root, "credential-cache");
5910
+ const filePath = (owner) => path24.join(dir, cacheFileNameFor(owner));
5443
5911
  return {
5444
5912
  read(store, owner) {
5445
5913
  if (store === "file") {
@@ -5671,9 +6139,9 @@ var init_credential = __esm({
5671
6139
 
5672
6140
  // src/providersStore.ts
5673
6141
  import fs7 from "node:fs";
5674
- import path24 from "node:path";
6142
+ import path25 from "node:path";
5675
6143
  function providerJsonPath(paths, name) {
5676
- return path24.join(paths.providersDir, `${name}.json`);
6144
+ return path25.join(paths.providersDir, `${name}.json`);
5677
6145
  }
5678
6146
  function providerExists(paths, name) {
5679
6147
  return fs7.existsSync(providerJsonPath(paths, name));
@@ -5733,7 +6201,7 @@ function readProvider(paths, name) {
5733
6201
  return parseProviderFile(filePath, raw);
5734
6202
  }
5735
6203
  function loadProvider(providersDir, name, port) {
5736
- const filePath = path24.join(providersDir, `${name}.json`);
6204
+ const filePath = path25.join(providersDir, `${name}.json`);
5737
6205
  const raw = port.readConfigFile(filePath);
5738
6206
  return raw === void 0 ? void 0 : parseProviderFile(filePath, raw);
5739
6207
  }
@@ -6106,17 +6574,17 @@ var init_commandDeps = __esm({
6106
6574
 
6107
6575
  // src/claudeShim.ts
6108
6576
  import fs8 from "node:fs";
6109
- import path26 from "node:path";
6577
+ import path27 from "node:path";
6110
6578
  import { z as z19 } from "zod";
6111
6579
  function commandFilename(ownExecutablePath, commandName) {
6112
6580
  return ownExecutablePath.toLowerCase().endsWith(".exe") ? `${commandName}.exe` : commandName;
6113
6581
  }
6114
6582
  function resolveOwnBinaryCheck(paths, contentSourcePath) {
6115
6583
  const state = readJson(paths.claudeShimFile, ClaudeShimStateSchema);
6116
- const recordedTarget = state === void 0 ? void 0 : path26.resolve(state.targetPath);
6584
+ const recordedTarget = state === void 0 ? void 0 : path27.resolve(state.targetPath);
6117
6585
  let ownStat;
6118
6586
  return (candidate) => {
6119
- if (recordedTarget !== void 0 && path26.resolve(candidate) === recordedTarget) {
6587
+ if (recordedTarget !== void 0 && path27.resolve(candidate) === recordedTarget) {
6120
6588
  return true;
6121
6589
  }
6122
6590
  ownStat ??= statOrUndefined(contentSourcePath);
@@ -6139,15 +6607,15 @@ function statOrUndefined(target) {
6139
6607
  }
6140
6608
  }
6141
6609
  function findPathShadow(params) {
6142
- const resolvedTargetDir = path26.resolve(params.targetDir);
6610
+ const resolvedTargetDir = path27.resolve(params.targetDir);
6143
6611
  for (const dir of params.pathDirs) {
6144
6612
  const found = params.findExecutableInDir(dir, params.targetFilename);
6145
6613
  if (found === void 0) {
6146
6614
  continue;
6147
6615
  }
6148
- return path26.resolve(dir) === resolvedTargetDir ? { status: "ok" } : { status: "shadowed", by: found };
6616
+ return path27.resolve(dir) === resolvedTargetDir ? { status: "ok" } : { status: "shadowed", by: found };
6149
6617
  }
6150
- return params.pathDirs.some((dir) => path26.resolve(dir) === resolvedTargetDir) ? { status: "ok" } : { status: "not-on-path" };
6618
+ return params.pathDirs.some((dir) => path27.resolve(dir) === resolvedTargetDir) ? { status: "ok" } : { status: "not-on-path" };
6151
6619
  }
6152
6620
  var ClaudeShimStateSchema, nodeLinkFs;
6153
6621
  var init_claudeShim = __esm({
@@ -6635,7 +7103,7 @@ var init_headers = __esm({
6635
7103
  });
6636
7104
 
6637
7105
  // src/launcher/flags.ts
6638
- import path35 from "node:path";
7106
+ import path36 from "node:path";
6639
7107
  function resolveLaunchFlags(params) {
6640
7108
  return {
6641
7109
  skipPermissions: params.flags?.skipPermissions ?? parseEnvBool("AGENT_SHIM_SKIP_PERMISSIONS", params.env.AGENT_SHIM_SKIP_PERMISSIONS) ?? params.cascade?.skipPermissions ?? false,
@@ -6666,7 +7134,7 @@ function buildEnv(params) {
6666
7134
  const parentCustomHeaders = params.baseEnv.ANTHROPIC_CUSTOM_HEADERS;
6667
7135
  let providerCustomHeaders;
6668
7136
  if (!params.configDirEscapeHatch && params.resolvedIdentityName !== void 0) {
6669
- env.CLAUDE_CONFIG_DIR = path35.join(params.identitiesDir, params.resolvedIdentityName);
7137
+ env.CLAUDE_CONFIG_DIR = path36.join(params.identitiesDir, params.resolvedIdentityName);
6670
7138
  }
6671
7139
  if (params.provider !== void 0) {
6672
7140
  const providerEnv = params.provider.definition.env ?? {};
@@ -6938,7 +7406,7 @@ var init_launchHook = __esm({
6938
7406
 
6939
7407
  // src/launcher.ts
6940
7408
  import { randomUUID as randomUUID4 } from "node:crypto";
6941
- import path36 from "node:path";
7409
+ import path37 from "node:path";
6942
7410
  function resolveIdentityCredential(identity, credential, env, port) {
6943
7411
  if (port === void 0) {
6944
7412
  return { ok: false, status: 1, message: `agent-shim: identity ${identity} needs its credential resolved, but this launcher has no credential port wired` };
@@ -7048,7 +7516,7 @@ function decideLaunch(params, readOnly) {
7048
7516
  identityDefaultConfigProfile: loadedIdentity?.config.defaultConfigProfile,
7049
7517
  globalDefaultConfigProfile: params.globalDefaultConfigProfile
7050
7518
  });
7051
- if (configProfileDecision.name !== void 0 && params.allowMissingConfigProfile !== true && fs12.readConfigFile(path36.join(paths.configProfilesDir, `${configProfileDecision.name}.json`)) === void 0) {
7519
+ if (configProfileDecision.name !== void 0 && params.allowMissingConfigProfile !== true && fs12.readConfigFile(path37.join(paths.configProfilesDir, `${configProfileDecision.name}.json`)) === void 0) {
7052
7520
  log.error(
7053
7521
  `agent-shim: no configuration profile named "${configProfileDecision.name}" (selected via ${configProfileDecision.source}). Run \`agent-shim profile add ${configProfileDecision.name}\` first.`
7054
7522
  );
@@ -7160,7 +7628,7 @@ function decideLaunch(params, readOnly) {
7160
7628
  extraFlags: splitExtraFlags(env.CLAUDE_EXTRA_FLAGS),
7161
7629
  passthrough: parsedArgv.rest
7162
7630
  });
7163
- const configDir = configDirEscapeHatchApplies || identityDecision.name === void 0 ? env.CLAUDE_CONFIG_DIR : path36.join(paths.identitiesDir, identityDecision.name);
7631
+ const configDir = configDirEscapeHatchApplies || identityDecision.name === void 0 ? env.CLAUDE_CONFIG_DIR : path37.join(paths.identitiesDir, identityDecision.name);
7164
7632
  const decision = {
7165
7633
  identitySource: identityDecision.source,
7166
7634
  configDirEscapeHatch: identityDecision.configDirEscapeHatch,
@@ -7201,7 +7669,7 @@ function prepareLaunch(params) {
7201
7669
  );
7202
7670
  const updateCheck = runLaunchUpdateCheck({
7203
7671
  mode: params.updateMode ?? "off",
7204
- checkPath: path36.join(paths.root, "update.check"),
7672
+ checkPath: path37.join(paths.root, "update.check"),
7205
7673
  currentVersion: package_default.version,
7206
7674
  port: params.update
7207
7675
  });
@@ -7361,12 +7829,12 @@ var init_launcher = __esm({
7361
7829
  });
7362
7830
 
7363
7831
  // src/ownCli.ts
7364
- import path37 from "node:path";
7832
+ import path38 from "node:path";
7365
7833
  function agentShimCliPath(distDir = typeof __AGENT_SHIM_DIST_DIR__ === "string" ? __AGENT_SHIM_DIST_DIR__ : void 0) {
7366
7834
  if (distDir === void 0) {
7367
7835
  throw new OwnCliPathUnknownError();
7368
7836
  }
7369
- return path37.join(distDir, OWN_CLI_FILE_NAME);
7837
+ return path38.join(distDir, OWN_CLI_FILE_NAME);
7370
7838
  }
7371
7839
  var OWN_CLI_FILE_NAME, OwnCliPathUnknownError;
7372
7840
  var init_ownCli = __esm({
@@ -7455,10 +7923,10 @@ request.on("error", (error) => finish(failed, (error.code ? error.code + ": " :
7455
7923
 
7456
7924
  // src/frontdoor/trust.ts
7457
7925
  import { createHash as createHash6 } from "node:crypto";
7458
- import path38 from "node:path";
7926
+ import path39 from "node:path";
7459
7927
  function resolveTrustBundle(params) {
7460
7928
  const { caCertFile, bundlesDir, variable, inherited, fs: fs12 } = params;
7461
- if (inherited === void 0 || inherited === "" || path38.resolve(inherited) === path38.resolve(caCertFile)) {
7929
+ if (inherited === void 0 || inherited === "" || path39.resolve(inherited) === path39.resolve(caCertFile)) {
7462
7930
  return { path: caCertFile };
7463
7931
  }
7464
7932
  const caPem = fs12.readFileUtf8(caCertFile);
@@ -7476,7 +7944,7 @@ function resolveTrustBundle(params) {
7476
7944
  }
7477
7945
  const combined = `${inheritedPem.trimEnd()}
7478
7946
  ${caPem}`;
7479
- const bundle = path38.join(bundlesDir, `${createHash6("sha256").update(combined).digest("hex").slice(0, BUNDLE_NAME_HEX_DIGITS)}.pem`);
7947
+ const bundle = path39.join(bundlesDir, `${createHash6("sha256").update(combined).digest("hex").slice(0, BUNDLE_NAME_HEX_DIGITS)}.pem`);
7480
7948
  if (!fs12.exists(bundle)) {
7481
7949
  fs12.mkdirp(bundlesDir);
7482
7950
  fs12.writeFileAtomic(bundle, combined);
@@ -8337,6 +8805,7 @@ function pumpTapSession(secure, openUpstream, tap, prefix = Buffer.alloc(0)) {
8337
8805
  // src/frontdoor/connect.ts
8338
8806
  init_capability();
8339
8807
  init_route();
8808
+ init_state();
8340
8809
 
8341
8810
  // src/frontdoor/supervisor.ts
8342
8811
  init_state();
@@ -8408,9 +8877,10 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
8408
8877
  return fail(`could not start the direct listener: ${error instanceof Error ? error.message : String(error)}`);
8409
8878
  }
8410
8879
  if (previousStickyDirect !== void 0 && direct.port !== previousStickyDirect) {
8411
- ports.log(`agent-shim frontdoor supervisor: sticky direct port ${String(previousStickyDirect)} was occupied; moving to ${String(direct.port)} (headroom must be restarted to admit the new address)`);
8880
+ ports.log(`agent-shim frontdoor supervisor: sticky direct port ${String(previousStickyDirect)} was occupied; moving to ${String(direct.port)} (headroom admits the new address once its supervisor rewrites the allowlist file)`);
8412
8881
  }
8413
8882
  lastDirectPort = direct.port;
8883
+ writeFrontDoorDirectRecord(fs12, paths.frontdoorDirectRecordsDir, { pid: ports.ownPid, directPort: direct.port });
8414
8884
  writeFrontDoorState(fs12, paths.frontdoorStateFile, { protocol: FRONT_DOOR_PROTOCOL, supervisorPid: ports.ownPid, port: http3.port, connectPort: connect3.port, directPort: direct.port, ...sticky() });
8415
8885
  ports.doorHealth.generation({ providerPort: http3.port, connectPort: connect3.port, directPort: direct.port });
8416
8886
  fs12.removeRecursive(paths.frontdoorLockFile);
@@ -8432,6 +8902,7 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
8432
8902
  await connect3.close();
8433
8903
  await http3.close();
8434
8904
  ports.closeRcStreamHub();
8905
+ removeFrontDoorDirectRecord(fs12, paths.frontdoorDirectRecordsDir, ports.ownPid);
8435
8906
  };
8436
8907
  const shutdown = async () => {
8437
8908
  await closeServing();
@@ -8495,7 +8966,6 @@ var CONNECT_INTERCEPT_HOST = "api.anthropic.com";
8495
8966
  var CONNECT_INTERCEPT_HOSTS = [CONNECT_INTERCEPT_HOST, "platform.claude.com"];
8496
8967
  var ROUTED_PATH_PREFIX = "/v1/";
8497
8968
  var HTTPS_PORT = 443;
8498
- var MAX_PORT = 65535;
8499
8969
  var HTTP_BAD_GATEWAY = 502;
8500
8970
  var CA_KEY_FILE_MODE = 384;
8501
8971
  var CONNECT_LINE_TOKENS = 3;
@@ -8527,7 +8997,7 @@ function parseConnectTarget(authority) {
8527
8997
  }
8528
8998
  const host = authority.slice(0, colon).trim().toLowerCase();
8529
8999
  const port = Number.parseInt(authority.slice(colon + 1), 10);
8530
- if (host === "" || !Number.isInteger(port) || port <= 0 || port > MAX_PORT) {
9000
+ if (host === "" || !Number.isInteger(port) || port <= 0 || port > MAX_TCP_PORT) {
8531
9001
  return void 0;
8532
9002
  }
8533
9003
  return { host, port };
@@ -8535,8 +9005,8 @@ function parseConnectTarget(authority) {
8535
9005
  function isInterceptedHost(host, interceptHosts) {
8536
9006
  return interceptHosts.includes(host);
8537
9007
  }
8538
- function servedByPipeline(path39) {
8539
- return path39?.startsWith(ROUTED_PATH_PREFIX) === true;
9008
+ function servedByPipeline(path40) {
9009
+ return path40?.startsWith(ROUTED_PATH_PREFIX) === true;
8540
9010
  }
8541
9011
  var HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
8542
9012
  "connection",
@@ -9330,11 +9800,11 @@ function createRcSessionTracker(deps) {
9330
9800
  };
9331
9801
  const observeExchange = (request4) => {
9332
9802
  const method = (request4.method ?? "").toUpperCase();
9333
- const path39 = new URL(request4.url ?? "/", "http://127.0.0.1").pathname;
9334
- if (!path39.startsWith(RC_SESSIONS_PATH_PREFIX)) {
9803
+ const path40 = new URL(request4.url ?? "/", "http://127.0.0.1").pathname;
9804
+ if (!path40.startsWith(RC_SESSIONS_PATH_PREFIX)) {
9335
9805
  return void 0;
9336
9806
  }
9337
- const tail = path39.slice(RC_SESSIONS_PATH_PREFIX.length);
9807
+ const tail = path40.slice(RC_SESSIONS_PATH_PREFIX.length);
9338
9808
  const facts = {
9339
9809
  oauthAuthorization: oauthAuthorizationOf(request4.headers),
9340
9810
  anthropicVersion: singleHeader(request4.headers, "anthropic-version"),
@@ -9724,8 +10194,8 @@ var RC_READ_FILE_ENCODINGS = ["utf-8", "base64"];
9724
10194
  function isRcReadFileEncoding(value) {
9725
10195
  return RC_READ_FILE_ENCODINGS.some((encoding) => encoding === value);
9726
10196
  }
9727
- function buildRcReadFilePayload(requestId, path39, options) {
9728
- return buildRcControlRequestPayload(requestId, { subtype: "read_file", path: path39, ...options?.maxBytes === void 0 ? {} : { max_bytes: options.maxBytes }, ...options?.encoding === void 0 ? {} : { encoding: options.encoding } });
10197
+ function buildRcReadFilePayload(requestId, path40, options) {
10198
+ return buildRcControlRequestPayload(requestId, { subtype: "read_file", path: path40, ...options?.maxBytes === void 0 ? {} : { max_bytes: options.maxBytes }, ...options?.encoding === void 0 ? {} : { encoding: options.encoding } });
9729
10199
  }
9730
10200
  function buildRcFileSuggestionsPayload(requestId, query) {
9731
10201
  return buildRcControlRequestPayload(requestId, { subtype: "file_suggestions", query });
@@ -9827,15 +10297,15 @@ async function getRcSessionContextUsage(deps, sessionId, detail) {
9827
10297
  const result = await deliverRcClientPayload(deps, sessionId, buildRcGetContextUsagePayload(requestId, detail), `request the context usage of session ${sessionId}`);
9828
10298
  return result.ok ? { ...result, requestId } : result;
9829
10299
  }
9830
- async function readRcSessionFile(deps, sessionId, path39, options) {
9831
- if (path39 === "") {
10300
+ async function readRcSessionFile(deps, sessionId, path40, options) {
10301
+ if (path40 === "") {
9832
10302
  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" };
9833
10303
  }
9834
10304
  if (options?.maxBytes !== void 0 && (!Number.isInteger(options.maxBytes) || options.maxBytes <= 0)) {
9835
10305
  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" };
9836
10306
  }
9837
10307
  const requestId = deps.newUuid();
9838
- const result = await deliverRcClientPayload(deps, sessionId, buildRcReadFilePayload(requestId, path39, options), `read ${path39} from session ${sessionId}`);
10308
+ const result = await deliverRcClientPayload(deps, sessionId, buildRcReadFilePayload(requestId, path40, options), `read ${path40} from session ${sessionId}`);
9839
10309
  return result.ok ? { ...result, requestId } : result;
9840
10310
  }
9841
10311
  async function suggestRcSessionFiles(deps, sessionId, query) {
@@ -10004,11 +10474,11 @@ function createRcControlHandler(deps) {
10004
10474
  path: "read-file",
10005
10475
  read: (body) => {
10006
10476
  const session = bodyString(body, "session");
10007
- const path39 = bodyString(body, "path");
10477
+ const path40 = bodyString(body, "path");
10008
10478
  const maxBytes = "maxBytes" in body ? bodyPositiveInteger(body, "maxBytes") : void 0;
10009
10479
  const encoding = "encoding" in body && isRcReadFileEncoding(body.encoding) ? body.encoding : void 0;
10010
- const refused2 = session === void 0 || path39 === void 0 || "maxBytes" in body && maxBytes === void 0 || "encoding" in body && encoding === void 0;
10011
- 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, path39, { ...maxBytes === void 0 ? {} : { maxBytes }, ...encoding === void 0 ? {} : { encoding } }) };
10480
+ const refused2 = session === void 0 || path40 === void 0 || "maxBytes" in body && maxBytes === void 0 || "encoding" in body && encoding === void 0;
10481
+ 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 } }) };
10012
10482
  }
10013
10483
  },
10014
10484
  {
@@ -10318,9 +10788,9 @@ function requestIdOfBody(body) {
10318
10788
  }
10319
10789
  function frontDoorRcControl(transport, token) {
10320
10790
  const headers = { authorization: `Bearer ${token}` };
10321
- const listAnswerOf = async (path39, what) => {
10791
+ const listAnswerOf = async (path40, what) => {
10322
10792
  try {
10323
- return await transport.request({ method: "GET", path: path39, headers });
10793
+ return await transport.request({ method: "GET", path: path40, headers });
10324
10794
  } catch (error) {
10325
10795
  throw new Error(`${what}: could not reach the front door's control listener: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
10326
10796
  }
@@ -10392,10 +10862,10 @@ function frontDoorRcControl(transport, token) {
10392
10862
  }
10393
10863
  return pending;
10394
10864
  };
10395
- const writeOf = async (path39, body, describeFailure2) => {
10865
+ const writeOf = async (path40, body, describeFailure2) => {
10396
10866
  let answer;
10397
10867
  try {
10398
- answer = await transport.request({ method: "POST", path: path39, headers: { ...headers, "content-type": "application/json" }, body });
10868
+ answer = await transport.request({ method: "POST", path: path40, headers: { ...headers, "content-type": "application/json" }, body });
10399
10869
  } catch (error) {
10400
10870
  return { ok: false, message: `could not reach the front door's control listener: ${error instanceof Error ? error.message : String(error)}` };
10401
10871
  }
@@ -10426,10 +10896,10 @@ function frontDoorRcControl(transport, token) {
10426
10896
  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}`),
10427
10897
  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}`),
10428
10898
  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}`),
10429
- readFile: async (sessionId, path39, options) => await writeOf(
10899
+ readFile: async (sessionId, path40, options) => await writeOf(
10430
10900
  `${CONTROL_PATH_PREFIX}/read-file`,
10431
- JSON.stringify({ session: sessionId, path: path39, ...options?.maxBytes === void 0 ? {} : { maxBytes: options.maxBytes }, ...options?.encoding === void 0 ? {} : { encoding: options.encoding } }),
10432
- (status, message) => `reading ${path39} from ${sessionId} failed (HTTP ${String(status)}): ${message}`
10901
+ JSON.stringify({ session: sessionId, path: path40, ...options?.maxBytes === void 0 ? {} : { maxBytes: options.maxBytes }, ...options?.encoding === void 0 ? {} : { encoding: options.encoding } }),
10902
+ (status, message) => `reading ${path40} from ${sessionId} failed (HTTP ${String(status)}): ${message}`
10433
10903
  ),
10434
10904
  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}`),
10435
10905
  keepAlive: async (sessionId) => await writeOf(`${CONTROL_PATH_PREFIX}/keep-alive`, JSON.stringify({ session: sessionId }), (status, message) => `keeping ${sessionId} alive failed (HTTP ${String(status)}): ${message}`),
@@ -10588,12 +11058,12 @@ function tlsFacts(options) {
10588
11058
  }
10589
11059
  function realRcEventDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_PORT }) {
10590
11060
  const agent = new ExemptTlsAgent();
10591
- const post = async (path39, headers, body) => await new Promise((resolve, reject) => {
11061
+ const post = async (path40, headers, body) => await new Promise((resolve, reject) => {
10592
11062
  const options = {
10593
11063
  host: target.host,
10594
11064
  port: target.port,
10595
11065
  method: "POST",
10596
- path: path39,
11066
+ path: path40,
10597
11067
  // 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.
10598
11068
  headers: { ...headers, host: target.servername ?? target.host, "content-length": String(Buffer.byteLength(body, "utf8")) },
10599
11069
  agent
@@ -10658,12 +11128,12 @@ function lateRcStreamDial(resolve) {
10658
11128
  }
10659
11129
  function realRcStreamDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_PORT }) {
10660
11130
  const agent = new ExemptTlsAgent();
10661
- const request4 = async (method, path39, headers, body, signal) => await new Promise((resolve, reject) => {
11131
+ const request4 = async (method, path40, headers, body, signal) => await new Promise((resolve, reject) => {
10662
11132
  const options = {
10663
11133
  host: target.host,
10664
11134
  port: target.port,
10665
11135
  method,
10666
- path: path39,
11136
+ path: path40,
10667
11137
  // 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.
10668
11138
  headers: { ...headers, host: target.servername ?? target.host, ...body === void 0 ? {} : { "content-length": String(Buffer.byteLength(body, "utf8")) } },
10669
11139
  agent,
@@ -10705,9 +11175,9 @@ function realRcStreamDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_P
10705
11175
  return { status, body: await readAll(response) };
10706
11176
  },
10707
11177
  openStream: async (sessionId, headers, resume, signal) => {
10708
- const path39 = resume === void 0 ? `/v1/code/sessions/${encodeURIComponent(sessionId)}/events/stream` : `/v1/code/sessions/${encodeURIComponent(sessionId)}/events/stream?from_sequence_num=${String(resume.fromSequenceNum)}`;
11178
+ 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)}`;
10709
11179
  const streamHeaders = resume === void 0 ? headers : { ...headers, "last-event-id": String(resume.fromSequenceNum) };
10710
- const { status, response } = await request4("GET", path39, streamHeaders, void 0, signal);
11180
+ const { status, response } = await request4("GET", path40, streamHeaders, void 0, signal);
10711
11181
  if (!isSuccessful2(status)) {
10712
11182
  await readAll(response);
10713
11183
  return { status, chunks: NO_CHUNKS };
@@ -11651,7 +12121,7 @@ init_schema();
11651
12121
  init_cliError();
11652
12122
  init_load();
11653
12123
  import fs4 from "node:fs";
11654
- import path21 from "node:path";
12124
+ import path22 from "node:path";
11655
12125
  var ProfileNotFoundError = class extends CliError {
11656
12126
  constructor(profileName) {
11657
12127
  super(`No configuration profile named "${profileName}". Run \`agent-shim profile add ${profileName}\` first.`);
@@ -11677,7 +12147,7 @@ var InvalidCategoryNameError = class extends CliError {
11677
12147
  categoryName;
11678
12148
  };
11679
12149
  function profileJsonPath(paths, name) {
11680
- return path21.join(paths.configProfilesDir, `${name}.json`);
12150
+ return path22.join(paths.configProfilesDir, `${name}.json`);
11681
12151
  }
11682
12152
  function profileExists(paths, name) {
11683
12153
  return fs4.existsSync(profileJsonPath(paths, name));
@@ -11859,19 +12329,19 @@ function writePools(paths, pools) {
11859
12329
  applyPatch(paths.globalConfigFile, GlobalConfigSchema, { pools }, { defaults: {} });
11860
12330
  }
11861
12331
  function poolCycleOf(pools, name) {
11862
- const walk = (current, path39) => {
12332
+ const walk = (current, path40) => {
11863
12333
  for (const entry of pools[current]?.identities ?? []) {
11864
12334
  const nested = poolNameOf(poolMemberIdentity(entry));
11865
12335
  if (nested === void 0) {
11866
12336
  continue;
11867
12337
  }
11868
12338
  if (nested === name) {
11869
- return [...path39, nested];
12339
+ return [...path40, nested];
11870
12340
  }
11871
- if (path39.includes(nested)) {
12341
+ if (path40.includes(nested)) {
11872
12342
  continue;
11873
12343
  }
11874
- const found = walk(nested, [...path39, nested]);
12344
+ const found = walk(nested, [...path40, nested]);
11875
12345
  if (found !== void 0) {
11876
12346
  return found;
11877
12347
  }
@@ -11981,7 +12451,7 @@ init_guard();
11981
12451
  init_providersStore();
11982
12452
  init_projects();
11983
12453
  init_pipeline();
11984
- import path25 from "node:path";
12454
+ import path26 from "node:path";
11985
12455
  import { z as z15 } from "zod";
11986
12456
  var HISTORY_PROJECTS_KEY_PREFIX = "history/projects/";
11987
12457
  function projectFragmentOf(rawKey) {
@@ -12319,10 +12789,10 @@ function collectCheckReport(params) {
12319
12789
  }).input;
12320
12790
  const providerName = cascadeProviderName(cascade);
12321
12791
  const provider = providerName === void 0 ? void 0 : loadCheckProvider(paths, providerName);
12322
- const farmRoot = identityDecision.name === void 0 ? void 0 : path25.join(paths.identitiesDir, identityDecision.name);
12792
+ const farmRoot = identityDecision.name === void 0 ? void 0 : path26.join(paths.identitiesDir, identityDecision.name);
12323
12793
  const settingsFiles = {
12324
- "settings.json": realFsPort.readFileUtf8(path25.join(claudeHome, "settings.json")),
12325
- "settings.local.json": realFsPort.readFileUtf8(path25.join(claudeHome, "settings.local.json"))
12794
+ "settings.json": realFsPort.readFileUtf8(path26.join(claudeHome, "settings.json")),
12795
+ "settings.local.json": realFsPort.readFileUtf8(path26.join(claudeHome, "settings.local.json"))
12326
12796
  };
12327
12797
  const report = runCheck({
12328
12798
  cwd,
@@ -12614,7 +13084,7 @@ var ResetRowSchema = z18.strictObject({
12614
13084
  var ResetScheduleSchema = z18.strictObject({ resets: z18.readonly(z18.array(ResetRowSchema)) });
12615
13085
 
12616
13086
  // src/frontdoor/controlSchemas.ts
12617
- import path30 from "node:path";
13087
+ import path31 from "node:path";
12618
13088
  import { z as z23 } from "zod";
12619
13089
 
12620
13090
  // src/doctorReport.ts
@@ -12628,7 +13098,7 @@ init_schema();
12628
13098
  init_cliError();
12629
13099
  init_identity();
12630
13100
  import fs9 from "node:fs";
12631
- import path27 from "node:path";
13101
+ import path28 from "node:path";
12632
13102
  init_lock();
12633
13103
  var IdentityNotFoundError = class extends CliError {
12634
13104
  constructor(name) {
@@ -12657,7 +13127,7 @@ var InvalidIdentityNameError = class extends CliError {
12657
13127
  attemptedName;
12658
13128
  };
12659
13129
  function identityJsonPath(paths, name) {
12660
- return path27.join(paths.identitiesDir, name, "identity.json");
13130
+ return path28.join(paths.identitiesDir, name, "identity.json");
12661
13131
  }
12662
13132
  function requireIdentityNames(paths, names) {
12663
13133
  const existing = new Set(listIdentities(paths).map((entry) => entry.name));
@@ -12772,7 +13242,7 @@ function setIdentityCredential(paths, identityName, change) {
12772
13242
  });
12773
13243
  }
12774
13244
  function belongsToIdentity(entry, name) {
12775
- return entry === name || entry === path27.basename(identityLockPath("", name)) || entry.startsWith(`.${name}.scratch.`) || entry.startsWith(`.${name}.previous.`);
13245
+ return entry === name || entry === path28.basename(identityLockPath("", name)) || entry.startsWith(`.${name}.scratch.`) || entry.startsWith(`.${name}.previous.`);
12776
13246
  }
12777
13247
  function removeIdentity(paths, name) {
12778
13248
  if (!identityExists(paths, name)) {
@@ -12780,7 +13250,7 @@ function removeIdentity(paths, name) {
12780
13250
  }
12781
13251
  for (const entry of fs9.readdirSync(paths.identitiesDir)) {
12782
13252
  if (belongsToIdentity(entry, name)) {
12783
- fs9.rmSync(path27.join(paths.identitiesDir, entry), { recursive: true, force: true });
13253
+ fs9.rmSync(path28.join(paths.identitiesDir, entry), { recursive: true, force: true });
12784
13254
  }
12785
13255
  }
12786
13256
  if (readActiveIdentity2(paths) === name) {
@@ -12791,10 +13261,10 @@ function removeIdentity(paths, name) {
12791
13261
  // src/doctorReport.ts
12792
13262
  init_realPorts();
12793
13263
  import fs10 from "node:fs";
12794
- import path29 from "node:path";
13264
+ import path30 from "node:path";
12795
13265
 
12796
13266
  // src/usage/signIn.ts
12797
- import path28 from "node:path";
13267
+ import path29 from "node:path";
12798
13268
  var CREDENTIALS_FILE = ".credentials.json";
12799
13269
  function keychainHasItem(run, account) {
12800
13270
  return run.run("security", ["find-generic-password", "-a", account]).status === 0;
@@ -12812,7 +13282,7 @@ function decideSignIn(facts) {
12812
13282
  return { state: "signed-out", detail: facts.platform === "darwin" ? `no Keychain item and no ${CREDENTIALS_FILE}` : `no ${CREDENTIALS_FILE}` };
12813
13283
  }
12814
13284
  function credentialsFilePath(farmRoot) {
12815
- return path28.join(farmRoot, CREDENTIALS_FILE);
13285
+ return path29.join(farmRoot, CREDENTIALS_FILE);
12816
13286
  }
12817
13287
 
12818
13288
  // src/codex/siwcStore.ts
@@ -12901,17 +13371,75 @@ init_realCredentialCache();
12901
13371
  init_source();
12902
13372
 
12903
13373
  // src/headroom/allowlistDrift.ts
13374
+ init_earlierRelease();
12904
13375
  init_state2();
12905
13376
  function diagnoseAllowlistDrift(params) {
12906
- const { recordedHash, providers, frontDoorOrigin: frontDoorOrigin2 } = params;
12907
- if (recordedHash === void 0 || recordedHash === hashAllowlist(headroomAllowlist(headroomUpstreams(providers, frontDoorOrigin2)))) {
13377
+ const { allowlist, fileContent, recordedHash, earlierRelease } = params;
13378
+ if (recordedHash === void 0) {
12908
13379
  return void 0;
12909
13380
  }
12910
- const originIsInput = providers.length > 0 && frontDoorOrigin2 !== void 0;
12911
- if (!originIsInput) {
12912
- return ["provider-files"];
13381
+ if (earlierRelease !== void 0) {
13382
+ return earlierRelease.admitsServingDoor ? void 0 : { startedByEarlierRelease: true };
13383
+ }
13384
+ const served = fileContent === void 0 ? [] : parseAllowlistFile(fileContent);
13385
+ const listed = new Set(served);
13386
+ const computed = new Set(allowlist);
13387
+ const missingFromFile = allowlist.filter((entry) => !listed.has(entry));
13388
+ const staleInFile = served.filter((entry) => !computed.has(entry)).sort();
13389
+ const hashLags = recordedHash !== (fileContent === void 0 ? void 0 : hashAllowlistFile(fileContent));
13390
+ if (missingFromFile.length === 0 && staleInFile.length === 0 && !hashLags) {
13391
+ return void 0;
13392
+ }
13393
+ return { missingFromFile, staleInFile, hashLags };
13394
+ }
13395
+ function describeAllowlistDrift(params) {
13396
+ const { drift, allowlistFile, liveSupervisorPid } = params;
13397
+ if (drift.startedByEarlierRelease === true) {
13398
+ return `the daemon was ${EARLIER_RELEASE_ALLOWLIST}, its allowlist cannot be shown to admit the serving front door (the door moved, or a provider changed, since it started), and until then provider launches are refused`;
13399
+ }
13400
+ const parts = [];
13401
+ if (drift.missingFromFile.length > 0) {
13402
+ parts.push(`${allowlistFile} lacks ${drift.missingFromFile.join(", ")} (live front doors' direct origins, so the daemon refuses every request a provider launch sends back through those doors)`);
13403
+ }
13404
+ if (drift.staleInFile.length > 0) {
13405
+ parts.push(`${allowlistFile} still lists ${drift.staleInFile.join(", ")}`);
13406
+ }
13407
+ if (drift.hashLags) {
13408
+ parts.push("the supervisor has not yet recorded the file's allowlist as served, and provider launches through headroom wait until it does");
13409
+ }
13410
+ const healing = liveSupervisorPid === void 0 ? "the supervisor is not running, so nothing rewrites the file until the next launch through headroom starts one" : `supervisor ${String(liveSupervisorPid)} rewrites the file on its next tick, so drift that persists means it cannot write it (see ${params.logPath})`;
13411
+ return `${parts.join("; ")}; ${healing}`;
13412
+ }
13413
+
13414
+ // src/doctorReport.ts
13415
+ init_earlierRelease();
13416
+ init_socket();
13417
+
13418
+ // src/doctorHeadroomAllowlist.ts
13419
+ function pushRefusedAllowlistFile(push, allowlist) {
13420
+ const problem = allowlist.fileProblem;
13421
+ if (problem?.repairable === false) {
13422
+ push("headroom", "fail", `Headroom refuses its allowlist file, so it admits nothing and every provider request through headroom fails: ${problem.message}.`, allowlist.file);
13423
+ }
13424
+ }
13425
+ function pushHeadroomAllowlist(push, allowlist, daemon) {
13426
+ const { drift } = allowlist;
13427
+ const problem = allowlist.fileProblem;
13428
+ if (problem?.repairable === true) {
13429
+ push("headroom", "warn", `Headroom's allowlist file breaks agent-shim's owner-only rule for it: ${problem.message}. Its supervisor repairs this on its next tick, and stops the daemon if it cannot.`, allowlist.file);
13430
+ }
13431
+ if (drift !== void 0) {
13432
+ const blocking = drift.startedByEarlierRelease === true || drift.missingFromFile.length > 0 || drift.hashLags;
13433
+ push(
13434
+ "headroom",
13435
+ blocking ? "fail" : "warn",
13436
+ `Headroom daemon ${String(daemon.headroomPid)}'s allowlist lags the current one: ` + describeAllowlistDrift({ drift, allowlistFile: allowlist.file, liveSupervisorPid: daemon.liveSupervisorPid, logPath: allowlist.logPath }) + ". `--no-headroom` launches are unaffected.",
13437
+ String(daemon.headroomPid)
13438
+ );
13439
+ }
13440
+ for (const door of allowlist.undeterminedFrontDoors) {
13441
+ push("headroom", "warn", `Could not tell whether front door ${String(door.pid)} is still running (${door.reason}), so headroom keeps admitting its direct origin ${door.origin} until a later read can.`, String(door.pid));
12913
13442
  }
12914
- return recordedHash === hashAllowlist(headroomAllowlist(headroomUpstreams(providers, void 0))) ? ["front-door-origin"] : ["provider-files", "front-door-origin"];
12915
13443
  }
12916
13444
 
12917
13445
  // src/doctorReport.ts
@@ -12920,7 +13448,7 @@ init_supervisor();
12920
13448
  init_state();
12921
13449
 
12922
13450
  // src/doctorFrontDoor.ts
12923
- function pushFrontDoor(push, supervisors, registeredPid) {
13451
+ function pushFrontDoor(push, supervisors, registeredPid, liveSessions) {
12924
13452
  if (!supervisors.ok) {
12925
13453
  push("frontdoor", "warn", `Could not list processes to look for duplicate front door supervisors: ${supervisors.message}.`);
12926
13454
  return;
@@ -12937,6 +13465,16 @@ function pushFrontDoor(push, supervisors, registeredPid) {
12937
13465
  }
12938
13466
  const serving = registeredPid !== void 0 && pids.includes(registeredPid) ? `The front door state file names pid ${String(registeredPid)} as the serving supervisor.` : "The front door state file names no running supervisor.";
12939
13467
  for (const pid of pids.filter((candidate) => candidate !== registeredPid)) {
13468
+ const served = liveSessions.filter((session) => session.supervisorPid === pid).length;
13469
+ if (registeredPid !== void 0 && pids.includes(registeredPid) && served > 0) {
13470
+ push(
13471
+ "frontdoor",
13472
+ "warn",
13473
+ `Front door supervisor pid ${String(pid)} has been superseded by pid ${String(registeredPid)} and still serves ${String(served)} launch${served === 1 ? "" : "es"} registered against it; it exits once they end.`,
13474
+ String(pid)
13475
+ );
13476
+ continue;
13477
+ }
12940
13478
  push(
12941
13479
  "frontdoor",
12942
13480
  "fail",
@@ -13006,7 +13544,7 @@ function tcpPortEraRecord(raw) {
13006
13544
  return (has("port") || has("lastPort")) && !has("socketPath");
13007
13545
  }
13008
13546
  function pushPathResolution(push, resolution) {
13009
- const ownDir = path29.dirname(resolution.ownExecutablePath);
13547
+ const ownDir = path30.dirname(resolution.ownExecutablePath);
13010
13548
  switch (resolution.agentShim.status) {
13011
13549
  case "ok":
13012
13550
  push("path-resolution", "pass", `\`agent-shim\` on PATH resolves to this running executable, ${resolution.ownExecutablePath}.`, "agent-shim");
@@ -13105,14 +13643,14 @@ function pushProvider(push, entry, cacheEnv, signIn) {
13105
13643
  }
13106
13644
  function pushLegacyName(push, params) {
13107
13645
  const legacyVariables = Object.keys(params.env).filter((name) => name.startsWith(LEGACY_ENV_PREFIX) && params.env[name] !== void 0).sort();
13108
- if (legacyVariables.length === 0 && path29.basename(params.rootPath) !== LEGACY_HOME_DIRNAME) {
13646
+ if (legacyVariables.length === 0 && path30.basename(params.rootPath) !== LEGACY_HOME_DIRNAME) {
13109
13647
  push("legacy-name", "pass", "Nothing here still uses the former `claude-use` name.");
13110
13648
  return;
13111
13649
  }
13112
13650
  for (const name of legacyVariables) {
13113
13651
  push("legacy-name", "warn", `${name} is the former name of ${name.replace(LEGACY_ENV_PREFIX, ENV_PREFIX)}. Rename it; the old name still works but is only a fallback.`, name);
13114
13652
  }
13115
- if (path29.basename(params.rootPath) === LEGACY_HOME_DIRNAME) {
13653
+ if (path30.basename(params.rootPath) === LEGACY_HOME_DIRNAME) {
13116
13654
  push(
13117
13655
  "legacy-name",
13118
13656
  "warn",
@@ -13368,15 +13906,11 @@ function runDoctor(params) {
13368
13906
  );
13369
13907
  }
13370
13908
  const allowlist = params.headroom.allowlist;
13371
- if (supervisorAlive && headroomAlive && state.socketPath !== void 0 && allowlist?.driftInputs?.includes("front-door-origin") === true) {
13372
- const origin = allowlist.frontDoorOrigin ?? "";
13373
- const certainty = allowlist.driftInputs.includes("provider-files") ? "may exclude" : "excludes";
13374
- push(
13375
- "headroom",
13376
- "fail",
13377
- `Headroom daemon ${String(state.headroomPid)} was started with an allowlist that ${certainty} the serving front door's direct origin ${origin}, so it refuses every request a provider launch sends back through the door. Its supervisor restarts it once no launch is registered against it; \`agent-shim headroom status\` lists the registered launches, and \`--no-headroom\` launches are unaffected.`,
13378
- String(state.headroomPid)
13379
- );
13909
+ if (allowlist !== void 0) {
13910
+ pushRefusedAllowlistFile(push, allowlist);
13911
+ }
13912
+ if (allowlist !== void 0 && headroomAlive && state.headroomPid !== void 0) {
13913
+ pushHeadroomAllowlist(push, allowlist, { headroomPid: state.headroomPid, liveSupervisorPid: supervisorAlive ? state.supervisorPid : void 0 });
13380
13914
  }
13381
13915
  if (state.lastError !== void 0 && supervisorAlive) {
13382
13916
  push("headroom", "warn", `Headroom recorded a previous error: ${state.lastError}`);
@@ -13392,7 +13926,7 @@ function runDoctor(params) {
13392
13926
  }
13393
13927
  if (params.frontDoor !== void 0) {
13394
13928
  const validated3 = params.frontDoor.state.raw === void 0 ? void 0 : validateJson(FrontDoorStateSchema, params.frontDoor.state);
13395
- pushFrontDoor(push, params.frontDoor.supervisors, validated3?.ok === true ? validated3.data.supervisorPid : void 0);
13929
+ pushFrontDoor(push, params.frontDoor.supervisors, validated3?.ok === true ? validated3.data.supervisorPid : void 0, params.frontDoor.liveSessions);
13396
13930
  }
13397
13931
  if (params.activeIdentity.raw === void 0) {
13398
13932
  push("active-identity", "pass", "No active identity set.");
@@ -13485,18 +14019,18 @@ function collectDoctorReport(params) {
13485
14019
  const { paths } = params;
13486
14020
  const identityNames = fs10.existsSync(paths.identitiesDir) ? fs10.readdirSync(paths.identitiesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && isIdentityDirectoryName(entry.name)).map((entry) => entry.name).sort() : [];
13487
14021
  const identities = identityNames.map((name) => {
13488
- const farmRoot = path29.join(paths.identitiesDir, name);
13489
- const identityPath = path29.join(farmRoot, "identity.json");
14022
+ const farmRoot = path30.join(paths.identitiesDir, name);
14023
+ const identityPath = path30.join(farmRoot, "identity.json");
13490
14024
  return { name, path: identityPath, raw: realFsPort.readFileUtf8(identityPath), farmRoot, credentialsFilePresent: fs10.existsSync(credentialsFilePath(farmRoot)) };
13491
14025
  });
13492
14026
  const profileNames = fs10.existsSync(paths.configProfilesDir) ? fs10.readdirSync(paths.configProfilesDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -".json".length)).sort() : [];
13493
14027
  const configProfiles = profileNames.map((name) => {
13494
- const profilePath = path29.join(paths.configProfilesDir, `${name}.json`);
14028
+ const profilePath = path30.join(paths.configProfilesDir, `${name}.json`);
13495
14029
  return { name, path: profilePath, raw: realFsPort.readFileUtf8(profilePath) };
13496
14030
  });
13497
14031
  const providerNames = fs10.existsSync(paths.providersDir) ? fs10.readdirSync(paths.providersDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -".json".length)).sort() : [];
13498
14032
  const providers = providerNames.map((name) => {
13499
- const providerPath = path29.join(paths.providersDir, `${name}.json`);
14033
+ const providerPath = path30.join(paths.providersDir, `${name}.json`);
13500
14034
  return { name, path: providerPath, raw: realFsPort.readFileUtf8(providerPath) };
13501
14035
  });
13502
14036
  const ownExecutablePath = realOwnExecutablePath();
@@ -13508,20 +14042,24 @@ function collectDoctorReport(params) {
13508
14042
  binaryDiscovery = { ok: false, message: error instanceof Error ? error.message : String(error) };
13509
14043
  }
13510
14044
  const shimState = readJson(paths.claudeShimFile, ClaudeShimStateSchema);
13511
- const pathDirs = (params.env.PATH ?? "").split(path29.delimiter).filter((dir) => dir !== "");
14045
+ const pathDirs = (params.env.PATH ?? "").split(path30.delimiter).filter((dir) => dir !== "");
13512
14046
  const claudeShimShadow = shimState === void 0 ? void 0 : refinePathShadow(
13513
14047
  findPathShadow({
13514
14048
  pathDirs,
13515
- targetDir: path29.dirname(shimState.targetPath),
13516
- targetFilename: path29.basename(shimState.targetPath),
14049
+ targetDir: path30.dirname(shimState.targetPath),
14050
+ targetFilename: path30.basename(shimState.targetPath),
13517
14051
  findExecutableInDir
13518
14052
  }),
13519
14053
  shimState.targetPath,
13520
14054
  realpathOrSelf
13521
14055
  );
13522
14056
  const uid = process.getuid?.();
13523
- const allowlistInputs = currentHeadroomAllowlistInputs(realFarmFs, paths);
14057
+ const allowlistContent = realFarmFs.readFileUtf8(paths.headroomAllowlistFile);
14058
+ const allowlistInputs = currentHeadroomAllowlistInputs(realFarmFs, paths, realFrontDoorLiveness);
13524
14059
  const servingHeadroom = resolveServingHeadroomState(realFarmFs, { stateFile: paths.headroomStateFile, legacyStateFile: paths.headroomLegacyStateFile }, realIsProcessRunning);
14060
+ const servingState = servingHeadroom?.state;
14061
+ const servingDaemon = servingState?.headroomPid !== void 0 && servingState.socketPath !== void 0 && realIsProcessRunning(servingState.headroomPid) ? { pid: servingState.headroomPid, socketPath: servingState.socketPath } : void 0;
14062
+ const servingEarlierRelease = servingDaemon === void 0 ? void 0 : earlierReleaseDaemon(realFarmFs, paths, { commandLine: realProcessCommandLine(servingDaemon.pid), socketPath: servingDaemon.socketPath, recordedHash: servingState?.allowlistHash });
13525
14063
  const report = runDoctor({
13526
14064
  env: params.env,
13527
14065
  credentialCache: realCredentialCacheEnv(paths),
@@ -13542,7 +14080,7 @@ function collectDoctorReport(params) {
13542
14080
  agentShim: refinePathShadow(
13543
14081
  findPathShadow({
13544
14082
  pathDirs,
13545
- targetDir: path29.dirname(ownExecutablePath),
14083
+ targetDir: path30.dirname(ownExecutablePath),
13546
14084
  targetFilename: commandFilename(ownExecutablePath, "agent-shim"),
13547
14085
  findExecutableInDir
13548
14086
  }),
@@ -13562,11 +14100,20 @@ function collectDoctorReport(params) {
13562
14100
  })(),
13563
14101
  isRunning: realIsProcessRunning,
13564
14102
  allowlist: {
13565
- driftInputs: diagnoseAllowlistDrift({ recordedHash: servingHeadroom?.state.allowlistHash, ...allowlistInputs }),
13566
- frontDoorOrigin: allowlistInputs.frontDoorOrigin
14103
+ drift: diagnoseAllowlistDrift({
14104
+ allowlist: allowlistInputs.frontDoorOrigins,
14105
+ fileContent: allowlistContent,
14106
+ recordedHash: servingHeadroom?.state.allowlistHash,
14107
+ earlierRelease: servingEarlierRelease
14108
+ }),
14109
+ file: paths.headroomAllowlistFile,
14110
+ undeterminedFrontDoors: undeterminedFrontDoors(allowlistInputs),
14111
+ // An earlier release's daemon reads no allowlist file, so there is nothing to check while one serves.
14112
+ fileProblem: servingEarlierRelease === void 0 ? reportableAllowlistFileProblem(paths.headroomAllowlistFile, realHeadroomSocketTrust, servingHeadroom?.state.allowlistHash !== void 0) : void 0,
14113
+ logPath: paths.headroomLogPath
13567
14114
  }
13568
14115
  },
13569
- ...uid === void 0 ? {} : { frontDoor: { state: { path: paths.frontdoorStateFile, raw: realFsPort.readFileUtf8(paths.frontdoorStateFile) }, supervisors: listFrontDoorSupervisors(realRunPort, uid) } }
14116
+ ...uid === void 0 ? {} : { frontDoor: { state: { path: paths.frontdoorStateFile, raw: realFsPort.readFileUtf8(paths.frontdoorStateFile) }, supervisors: listFrontDoorSupervisors(realRunPort, uid), liveSessions: listFrontDoorSessions(realFarmFs, paths.frontdoorSessionsDir).filter((session) => realIsProcessRunning(session.pid)) } }
13570
14117
  });
13571
14118
  return report;
13572
14119
  }
@@ -13621,11 +14168,11 @@ var FrontDoorSessionsOutputSchema = z23.strictObject({ sessions: z23.readonly(z2
13621
14168
  var CheckRunInputSchema = z23.strictObject({
13622
14169
  path: z23.string().min(1),
13623
14170
  identity: z23.string().min(1).optional()
13624
- }).refine((input) => path30.isAbsolute(input.path), { message: "path must be absolute: the door cannot resolve a relative path against the caller's working directory" });
14171
+ }).refine((input) => path31.isAbsolute(input.path), { message: "path must be absolute: the door cannot resolve a relative path against the caller's working directory" });
13625
14172
  var PoolPickInputSchema = z23.strictObject({
13626
14173
  pool: z23.string().min(1),
13627
14174
  path: z23.string().min(1)
13628
- }).refine((input) => path30.isAbsolute(input.path), { message: "path must be absolute: the door cannot resolve a relative path against the caller's working directory" });
14175
+ }).refine((input) => path31.isAbsolute(input.path), { message: "path must be absolute: the door cannot resolve a relative path against the caller's working directory" });
13629
14176
  var DoctorFindingSchema = z23.strictObject({
13630
14177
  section: z23.enum(DOCTOR_SECTIONS),
13631
14178
  subject: z23.string().optional(),
@@ -14005,7 +14552,7 @@ function profileValue(name, paths) {
14005
14552
  function createConfigApiRouter(deps) {
14006
14553
  const authed = doorApiAuth(deps.expectedToken);
14007
14554
  const { paths } = deps;
14008
- const route = (path39, summary) => authed.meta(openapi3({ method: "POST", path: path39, summary, tags: [CONFIG_API_TAG] }));
14555
+ const route = (path40, summary) => authed.meta(openapi3({ method: "POST", path: path40, summary, tags: [CONFIG_API_TAG] }));
14009
14556
  return {
14010
14557
  config: {
14011
14558
  identity: {
@@ -14369,7 +14916,7 @@ import { ORPCError as ORPCError5 } from "@orpc/client";
14369
14916
  import { openapi as openapi6 } from "@orpc/openapi";
14370
14917
 
14371
14918
  // src/frontdoor/launchSchemas.ts
14372
- import path31 from "node:path";
14919
+ import path32 from "node:path";
14373
14920
  import { z as z26 } from "zod";
14374
14921
  init_claudeVersion();
14375
14922
  init_identity();
@@ -14379,7 +14926,7 @@ var LaunchResolveInputSchema = z26.strictObject({
14379
14926
  argv: z26.readonly(z26.array(z26.string())).optional(),
14380
14927
  /** The launch's inherited environment. Absent means an empty one: the door's own environment is never a substitute for the caller's, so a caller that wants `AGENT_SHIM_*` switches or a `CLAUDE_CONFIG_DIR` considered names them here. Values are read, never reported. */
14381
14928
  env: z26.record(z26.string(), z26.string()).optional()
14382
- }).refine((input) => path31.isAbsolute(input.path), { message: "path must be absolute: the door cannot resolve a relative path against the caller's working directory" });
14929
+ }).refine((input) => path32.isAbsolute(input.path), { message: "path must be absolute: the door cannot resolve a relative path against the caller's working directory" });
14383
14930
  var LaunchPoolDecisionSchema = z26.strictObject({
14384
14931
  name: z26.string(),
14385
14932
  reasons: z26.readonly(z26.array(z26.string())),
@@ -16547,7 +17094,7 @@ data: ${JSON.stringify(envelope)}
16547
17094
 
16548
17095
  // src/frontdoor/rcSelfHostMint.ts
16549
17096
  init_account();
16550
- import path32 from "node:path";
17097
+ import path33 from "node:path";
16551
17098
  var CREDENTIALS_FILE2 = ".credentials.json";
16552
17099
  var CLAUDE_JSON2 = ".claude.json";
16553
17100
  var RC_SELF_HOST_RECORD_DIR = "rc-selfhost";
@@ -16571,7 +17118,7 @@ function readJsonObject(fs12, file) {
16571
17118
  return isRecord12(parsed) ? parsed : void 0;
16572
17119
  }
16573
17120
  function readRcSelfHostRecord(fs12, frontdoorDir) {
16574
- const parsed = readJsonObject(fs12, path32.join(frontdoorDir, RC_SELF_HOST_RECORD_DIR, RC_SELF_HOST_RECORD_FILE));
17121
+ const parsed = readJsonObject(fs12, path33.join(frontdoorDir, RC_SELF_HOST_RECORD_DIR, RC_SELF_HOST_RECORD_FILE));
16575
17122
  if (parsed === void 0) {
16576
17123
  return void 0;
16577
17124
  }
@@ -16585,11 +17132,11 @@ function mintRcSelfHostCredential(deps) {
16585
17132
  if (!isIdentityName(deps.identity)) {
16586
17133
  throw new Error(`"${deps.identity}" is not a valid identity name.`);
16587
17134
  }
16588
- const identityDir = path32.join(deps.identitiesDir, deps.identity);
16589
- const credentialsFile = path32.join(identityDir, CREDENTIALS_FILE2);
16590
- const claudeJsonFile = path32.join(identityDir, CLAUDE_JSON2);
16591
- const recordDir = path32.join(deps.frontdoorDir, RC_SELF_HOST_RECORD_DIR);
16592
- const recordFile = path32.join(recordDir, RC_SELF_HOST_RECORD_FILE);
17135
+ const identityDir = path33.join(deps.identitiesDir, deps.identity);
17136
+ const credentialsFile = path33.join(identityDir, CREDENTIALS_FILE2);
17137
+ const claudeJsonFile = path33.join(identityDir, CLAUDE_JSON2);
17138
+ const recordDir = path33.join(deps.frontdoorDir, RC_SELF_HOST_RECORD_DIR);
17139
+ const recordFile = path33.join(recordDir, RC_SELF_HOST_RECORD_FILE);
16593
17140
  const previousRecord = readRcSelfHostRecord(deps.fs, deps.frontdoorDir);
16594
17141
  const existingCredentials = readJsonObject(deps.fs, credentialsFile);
16595
17142
  const existingOauth = isRecord12(existingCredentials?.claudeAiOauth) ? existingCredentials.claudeAiOauth : void 0;
@@ -16605,7 +17152,7 @@ function mintRcSelfHostCredential(deps) {
16605
17152
  const organizationUuid = deps.newUuid();
16606
17153
  const accountUuid = deps.newUuid();
16607
17154
  const mintedAt = new Date(deps.now()).toISOString();
16608
- deps.fs.mkdirPrivate(path32.dirname(credentialsFile));
17155
+ deps.fs.mkdirPrivate(path33.dirname(credentialsFile));
16609
17156
  deps.fs.writeFilePrivate(credentialsFile, `${JSON.stringify({ ...existingCredentials, claudeAiOauth: { accessToken, refreshToken, expiresAt: null, scopes: [...RC_SELF_HOST_SCOPE_LIST], subscriptionType: SELF_HOST_SUBSCRIPTION_TYPE, rateLimitTier: null } }, null, 2)}
16610
17157
  `);
16611
17158
  const claudeJson = readJsonObject(deps.fs, claudeJsonFile) ?? {};
@@ -16647,7 +17194,7 @@ init_plan2();
16647
17194
  init_poolPick();
16648
17195
 
16649
17196
  // src/agents/read.ts
16650
- import path33 from "node:path";
17197
+ import path34 from "node:path";
16651
17198
  import { z as z28 } from "zod";
16652
17199
  var PROJECTS_DIR = "projects";
16653
17200
  var TASKS_DIR = "tasks";
@@ -16673,15 +17220,15 @@ function parseEntry2(project, entry) {
16673
17220
  }
16674
17221
  function listSessions2(fs12, configDir) {
16675
17222
  const found = [];
16676
- for (const project of fs12.readdir(path33.join(configDir, PROJECTS_DIR))) {
16677
- const projectDir = path33.join(configDir, PROJECTS_DIR, project);
17223
+ for (const project of fs12.readdir(path34.join(configDir, PROJECTS_DIR))) {
17224
+ const projectDir = path34.join(configDir, PROJECTS_DIR, project);
16678
17225
  const entries = /* @__PURE__ */ new Set();
16679
17226
  for (const name of fs12.readdir(projectDir)) {
16680
17227
  if (name.endsWith(TRANSCRIPT_SUFFIX)) {
16681
17228
  entries.add(name.slice(0, -TRANSCRIPT_SUFFIX.length));
16682
17229
  continue;
16683
17230
  }
16684
- const children = fs12.readdir(path33.join(projectDir, name));
17231
+ const children = fs12.readdir(path34.join(projectDir, name));
16685
17232
  if (children.includes(SUBAGENTS_DIR) || children.includes(WORKFLOWS_DIR)) {
16686
17233
  entries.add(name);
16687
17234
  }
@@ -16701,8 +17248,8 @@ function locateAgents(fs12, configDir, relativeDir, workflowRunId, report) {
16701
17248
  const id = name.slice(AGENT_FILE_PREFIX.length, -META_SUFFIX.length);
16702
17249
  located.push({
16703
17250
  id,
16704
- metaPath: path33.join(relativeDir, name),
16705
- transcriptPath: path33.join(relativeDir, `${AGENT_FILE_PREFIX}${id}${TRANSCRIPT_SUFFIX}`),
17251
+ metaPath: path34.join(relativeDir, name),
17252
+ transcriptPath: path34.join(relativeDir, `${AGENT_FILE_PREFIX}${id}${TRANSCRIPT_SUFFIX}`),
16706
17253
  workflowRunId
16707
17254
  });
16708
17255
  }
@@ -16745,7 +17292,7 @@ function describeFailure(error) {
16745
17292
  }
16746
17293
  function readFileOrReport(fs12, configDir, relative, report) {
16747
17294
  try {
16748
- return fs12.readFileUtf8(path33.join(configDir, relative));
17295
+ return fs12.readFileUtf8(path34.join(configDir, relative));
16749
17296
  } catch (error) {
16750
17297
  report({ path: relative, reason: `could not be read: ${describeFailure(error)}` });
16751
17298
  return void 0;
@@ -16753,7 +17300,7 @@ function readFileOrReport(fs12, configDir, relative, report) {
16753
17300
  }
16754
17301
  function listDirOrReport(fs12, configDir, relative, report) {
16755
17302
  try {
16756
- return fs12.readdir(path33.join(configDir, relative));
17303
+ return fs12.readdir(path34.join(configDir, relative));
16757
17304
  } catch (error) {
16758
17305
  report({ path: relative, reason: `could not be listed: ${describeFailure(error)}` });
16759
17306
  return [];
@@ -16777,18 +17324,18 @@ var WorkflowRunFileSchema = z28.object({
16777
17324
  summary: z28.string().optional()
16778
17325
  });
16779
17326
  function summariseSession(fs12, configDir, ref) {
16780
- const sessionRelative = path33.join(PROJECTS_DIR, ref.project, ref.entry);
16781
- const subagentsRelative = path33.join(sessionRelative, SUBAGENTS_DIR);
16782
- const workflowsUnderSubagents = path33.join(subagentsRelative, WORKFLOWS_DIR);
17327
+ const sessionRelative = path34.join(PROJECTS_DIR, ref.project, ref.entry);
17328
+ const subagentsRelative = path34.join(sessionRelative, SUBAGENTS_DIR);
17329
+ const workflowsUnderSubagents = path34.join(subagentsRelative, WORKFLOWS_DIR);
16783
17330
  const isMeta = (name) => name.startsWith(AGENT_FILE_PREFIX) && name.endsWith(META_SUFFIX);
16784
- let agentCount = fs12.readdir(path33.join(configDir, subagentsRelative)).filter(isMeta).length;
16785
- for (const runDir of fs12.readdir(path33.join(configDir, workflowsUnderSubagents))) {
17331
+ let agentCount = fs12.readdir(path34.join(configDir, subagentsRelative)).filter(isMeta).length;
17332
+ for (const runDir of fs12.readdir(path34.join(configDir, workflowsUnderSubagents))) {
16786
17333
  if (runDir.startsWith(WORKFLOW_RUN_PREFIX)) {
16787
- agentCount += fs12.readdir(path33.join(configDir, workflowsUnderSubagents, runDir)).filter(isMeta).length;
17334
+ agentCount += fs12.readdir(path34.join(configDir, workflowsUnderSubagents, runDir)).filter(isMeta).length;
16788
17335
  }
16789
17336
  }
16790
- const workflowRunCount = fs12.readdir(path33.join(configDir, sessionRelative, WORKFLOWS_DIR)).filter((name) => name.startsWith(WORKFLOW_RUN_PREFIX) && name.endsWith(JSON_SUFFIX)).length;
16791
- const transcript = fs12.lstat(path33.join(configDir, PROJECTS_DIR, ref.project, `${ref.entry}${TRANSCRIPT_SUFFIX}`));
17337
+ const workflowRunCount = fs12.readdir(path34.join(configDir, sessionRelative, WORKFLOWS_DIR)).filter((name) => name.startsWith(WORKFLOW_RUN_PREFIX) && name.endsWith(JSON_SUFFIX)).length;
17338
+ const transcript = fs12.lstat(path34.join(configDir, PROJECTS_DIR, ref.project, `${ref.entry}${TRANSCRIPT_SUFFIX}`));
16792
17339
  return { ...ref, agentCount, workflowRunCount, ...transcript === void 0 ? {} : { updatedAt: new Date(transcript.mtimeMs).toISOString() } };
16793
17340
  }
16794
17341
  function readSessionAgentTree(fs12, configDir, ref, options = {}) {
@@ -16796,13 +17343,13 @@ function readSessionAgentTree(fs12, configDir, ref, options = {}) {
16796
17343
  const report = (problem) => {
16797
17344
  problems.push(problem);
16798
17345
  };
16799
- const sessionRelative = path33.join(PROJECTS_DIR, ref.project, ref.entry);
16800
- const subagentsRelative = path33.join(sessionRelative, SUBAGENTS_DIR);
16801
- const workflowsUnderSubagents = path33.join(subagentsRelative, WORKFLOWS_DIR);
17346
+ const sessionRelative = path34.join(PROJECTS_DIR, ref.project, ref.entry);
17347
+ const subagentsRelative = path34.join(sessionRelative, SUBAGENTS_DIR);
17348
+ const workflowsUnderSubagents = path34.join(subagentsRelative, WORKFLOWS_DIR);
16802
17349
  const located = locateAgents(fs12, configDir, subagentsRelative, void 0, report);
16803
17350
  for (const runDir of listDirOrReport(fs12, configDir, workflowsUnderSubagents, report)) {
16804
17351
  if (runDir.startsWith(WORKFLOW_RUN_PREFIX)) {
16805
- located.push(...locateAgents(fs12, configDir, path33.join(workflowsUnderSubagents, runDir), runDir, report));
17352
+ located.push(...locateAgents(fs12, configDir, path34.join(workflowsUnderSubagents, runDir), runDir, report));
16806
17353
  }
16807
17354
  }
16808
17355
  const agents = /* @__PURE__ */ new Map();
@@ -16843,7 +17390,7 @@ function readSessionAgentTree(fs12, configDir, ref, options = {}) {
16843
17390
  superseded: ref.superseded,
16844
17391
  agents: roots.sort().map(build),
16845
17392
  tasks: readTasks(fs12, configDir, ownTaskLists(ref.sessionId), [.../* @__PURE__ */ new Set([...teamNames, ...options.taskListIds ?? []])].filter((id) => !ownTaskLists(ref.sessionId).includes(id)), report),
16846
- workflowRuns: readWorkflowRuns(fs12, configDir, path33.join(sessionRelative, WORKFLOWS_DIR), report),
17393
+ workflowRuns: readWorkflowRuns(fs12, configDir, path34.join(sessionRelative, WORKFLOWS_DIR), report),
16847
17394
  problems
16848
17395
  };
16849
17396
  }
@@ -16908,12 +17455,12 @@ function ownTaskLists(sessionId) {
16908
17455
  function readTasks(fs12, configDir, ownLists, sharedLists, report) {
16909
17456
  const tasks = [];
16910
17457
  [...ownLists, ...sharedLists].forEach((scope, index) => {
16911
- const relativeDir = path33.join(TASKS_DIR, scope);
17458
+ const relativeDir = path34.join(TASKS_DIR, scope);
16912
17459
  for (const name of [...listDirOrReport(fs12, configDir, relativeDir, report)].sort()) {
16913
17460
  if (name.startsWith(".") || !name.endsWith(JSON_SUFFIX)) {
16914
17461
  continue;
16915
17462
  }
16916
- const relative = path33.join(relativeDir, name);
17463
+ const relative = path34.join(relativeDir, name);
16917
17464
  const text = readFileOrReport(fs12, configDir, relative, report);
16918
17465
  const parsed = TaskFileSchema.safeParse(text === void 0 ? void 0 : parseJson2(text));
16919
17466
  if (parsed.success) {
@@ -16931,7 +17478,7 @@ function readWorkflowRuns(fs12, configDir, relativeDir, report) {
16931
17478
  if (!name.startsWith(WORKFLOW_RUN_PREFIX) || !name.endsWith(JSON_SUFFIX)) {
16932
17479
  continue;
16933
17480
  }
16934
- const relative = path33.join(relativeDir, name);
17481
+ const relative = path34.join(relativeDir, name);
16935
17482
  const text = readFileOrReport(fs12, configDir, relative, report);
16936
17483
  const parsed = WorkflowRunFileSchema.safeParse(text === void 0 ? void 0 : parseJson2(text));
16937
17484
  if (!parsed.success) {
@@ -16948,7 +17495,7 @@ function readWorkflowRuns(fs12, configDir, relativeDir, report) {
16948
17495
 
16949
17496
  // src/agents/sessions.ts
16950
17497
  init_cliError();
16951
- import path34 from "node:path";
17498
+ import path35 from "node:path";
16952
17499
  var SessionLookupError = class extends CliError {
16953
17500
  constructor(message) {
16954
17501
  super(message);
@@ -16956,13 +17503,13 @@ var SessionLookupError = class extends CliError {
16956
17503
  }
16957
17504
  };
16958
17505
  function identityConfigDir(identitiesDir, identity) {
16959
- return path34.join(identitiesDir, identity);
17506
+ return path35.join(identitiesDir, identity);
16960
17507
  }
16961
17508
  function historyStores(fs12, identitiesDir, identities) {
16962
17509
  const byRealPath = /* @__PURE__ */ new Map();
16963
17510
  for (const identity of identities) {
16964
17511
  const configDir = identityConfigDir(identitiesDir, identity);
16965
- const real = fs12.realpath(path34.join(configDir, "projects"));
17512
+ const real = fs12.realpath(path35.join(configDir, "projects"));
16966
17513
  if (real === void 0) {
16967
17514
  continue;
16968
17515
  }
@@ -17414,6 +17961,7 @@ export {
17414
17961
  isRcReadFileEncoding,
17415
17962
  lateRcEventDial,
17416
17963
  lateRcStreamDial,
17964
+ launchRequiredUpstreams,
17417
17965
  listDirectoryRules,
17418
17966
  listIdentities,
17419
17967
  listProfiles,