claude-use 6.1.0 → 6.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -215,10 +215,18 @@ var ConfigProfileSchema = import_zod.z.strictObject({
215
215
  entries: EntriesSchema.optional(),
216
216
  launch: LaunchSchema.optional()
217
217
  });
218
+ var POOL_SELECTOR_PREFIX = "pool:";
219
+ var POOL_NAME_PATTERN = "[A-Za-z0-9][A-Za-z0-9._-]*";
220
+ var IDENTITY_NAME_PATTERN = "[A-Za-z0-9][A-Za-z0-9._@-]*";
221
+ var PoolNameSchema = import_zod.z.string().min(1).regex(new RegExp(`^${POOL_NAME_PATTERN}$`));
222
+ var IdentitySelectorSchema = import_zod.z.string().min(1).regex(new RegExp(`^(${POOL_SELECTOR_PREFIX}${POOL_NAME_PATTERN}|${IDENTITY_NAME_PATTERN})$`));
223
+ var PoolSchema = import_zod.z.strictObject({
224
+ identities: import_zod.z.array(import_zod.z.string().min(1).regex(new RegExp(`^${IDENTITY_NAME_PATTERN}$`))).min(1).refine((names) => new Set(names).size === names.length, { message: "pool members must be unique" })
225
+ });
218
226
  var DirectoryRuleSchema = ConfigProfileSchema.omit({ description: true }).extend({
219
227
  path: import_zod.z.string().min(1),
220
228
  configProfile: import_zod.z.string().min(1).optional(),
221
- identity: import_zod.z.string().min(1).optional(),
229
+ identity: IdentitySelectorSchema.optional(),
222
230
  when: WhenSchema.optional()
223
231
  });
224
232
  var DirectoryRulesSchema = import_zod.z.strictObject({
@@ -227,7 +235,7 @@ var DirectoryRulesSchema = import_zod.z.strictObject({
227
235
  });
228
236
  var PortableConfigSchema = ConfigProfileSchema.omit({ description: true }).extend({
229
237
  configProfile: import_zod.z.string().min(1).optional(),
230
- identity: import_zod.z.string().min(1).optional(),
238
+ identity: IdentitySelectorSchema.optional(),
231
239
  when: WhenSchema.optional()
232
240
  });
233
241
  var HEADROOM_MODES = ["cache", "token"];
@@ -275,7 +283,8 @@ var GlobalConfigSchema = import_zod.z.strictObject({
275
283
  entries: EntriesSchema.optional(),
276
284
  launch: LaunchSchema.optional(),
277
285
  headroom: HeadroomGlobalConfigSchema.optional(),
278
- frontdoor: FrontDoorGlobalConfigSchema.optional()
286
+ frontdoor: FrontDoorGlobalConfigSchema.optional(),
287
+ pools: import_zod.z.record(PoolNameSchema, PoolSchema).optional()
279
288
  });
280
289
  var IdentitySchema = import_zod.z.strictObject({
281
290
  $schema: import_zod.z.string().optional(),
@@ -2802,7 +2811,7 @@ var HeadroomStateSchema = import_zod4.z.strictObject({
2802
2811
  /** The last fatal error, kept across shutdowns so `headroom status` can explain a daemon that is not running. */
2803
2812
  lastError: import_zod4.z.string().optional()
2804
2813
  });
2805
- var HeadroomSessionSchema = import_zod4.z.strictObject({ pid: import_zod4.z.number().int().positive(), startedAt: import_zod4.z.number() });
2814
+ var HeadroomSessionSchema = import_zod4.z.strictObject({ pid: import_zod4.z.number().int().positive(), startedAt: import_zod4.z.number(), supervisorPid: import_zod4.z.number().int().positive() });
2806
2815
  var HeadroomLockSchema = import_zod4.z.strictObject({ pid: import_zod4.z.number().int().positive(), at: import_zod4.z.number() });
2807
2816
  function parseJson(raw, schema) {
2808
2817
  if (raw === void 0 || raw.trim() === "") {
@@ -2882,6 +2891,9 @@ function listSessions(fs2, sessionsDir) {
2882
2891
  }
2883
2892
  return sessions.sort((a, b) => a.pid - b.pid);
2884
2893
  }
2894
+ function sessionsForSupervisor(fs2, sessionsDir, supervisorPid) {
2895
+ return listSessions(fs2, sessionsDir).filter((session) => session.supervisorPid === supervisorPid);
2896
+ }
2885
2897
  function pruneDeadSessions(fs2, sessionsDir, isRunning) {
2886
2898
  const removed = [];
2887
2899
  for (const session of listSessions(fs2, sessionsDir)) {
@@ -3181,7 +3193,7 @@ function ensureHeadroom(params) {
3181
3193
  if (state?.supervisorPid !== void 0 && ports.isRunning(state.supervisorPid)) {
3182
3194
  const daemonUp = state.port !== void 0 && state.headroomPid !== void 0 && ports.isRunning(state.headroomPid);
3183
3195
  if (daemonUp && state.port !== void 0) {
3184
- writeSession(ports.fs, paths.headroomSessionsDir, { pid: params.launcherPid, startedAt: ports.now() });
3196
+ writeSession(ports.fs, paths.headroomSessionsDir, { pid: params.launcherPid, startedAt: ports.now(), supervisorPid: state.supervisorPid });
3185
3197
  return { port: state.port };
3186
3198
  }
3187
3199
  if (state.lastError !== void 0) {
@@ -3318,15 +3330,28 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3318
3330
  const { fs: fs2, paths } = ports;
3319
3331
  let config = initialConfig;
3320
3332
  const previousState = readHeadroomState(fs2, paths.headroomStateFile);
3333
+ if (previousState?.supervisorPid !== void 0 && previousState.supervisorPid !== ports.ownPid && ports.isRunning(previousState.supervisorPid)) {
3334
+ ports.log(`claude-use headroom supervisor ${String(ports.ownPid)}: supervisor ${String(previousState.supervisorPid)} already owns the daemon; exiting`);
3335
+ return 0;
3336
+ }
3321
3337
  let installedSource = previousState?.installedSource;
3322
3338
  let lastPort = previousState?.lastPort;
3323
3339
  const sticky = () => lastPort === void 0 ? {} : { lastPort };
3340
+ const ownsState = () => {
3341
+ const owner = readHeadroomState(fs2, paths.headroomStateFile)?.supervisorPid;
3342
+ return owner === void 0 || owner === ports.ownPid || !ports.isRunning(owner);
3343
+ };
3344
+ const writeOwnState = (state) => {
3345
+ if (!ownsState()) {
3346
+ return false;
3347
+ }
3348
+ writeHeadroomState(fs2, paths.headroomStateFile, state);
3349
+ return true;
3350
+ };
3324
3351
  const fail = (message) => {
3325
- writeHeadroomState(fs2, paths.headroomStateFile, {
3326
- lastError: message,
3327
- ...sticky()
3328
- });
3329
- fs2.removeRecursive(paths.headroomLockFile);
3352
+ if (writeOwnState({ lastError: message, ...sticky() })) {
3353
+ fs2.removeRecursive(paths.headroomLockFile);
3354
+ }
3330
3355
  ports.log(`claude-use headroom supervisor: ${message}`);
3331
3356
  return 1;
3332
3357
  };
@@ -3349,7 +3374,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3349
3374
  return fail(`could not install headroom from ${config.source}: install failed (is uv installed and on PATH?)`);
3350
3375
  }
3351
3376
  const version = ports.headroomVersion() ?? "unknown";
3352
- writeHeadroomState(fs2, paths.headroomStateFile, { supervisorPid: ports.ownPid, version, installedSource, ...sticky() });
3377
+ writeOwnState({ supervisorPid: ports.ownPid, version, installedSource, ...sticky() });
3353
3378
  ports.log(`claude-use headroom supervisor ${String(ports.ownPid)}: managing headroom on allowlist [${allowlistOf(ports).join(", ")}]`);
3354
3379
  const orphanPid = previousState?.headroomPid;
3355
3380
  if (orphanPid !== void 0 && ports.isRunning(orphanPid)) {
@@ -3361,6 +3386,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3361
3386
  let runningSettingsHash;
3362
3387
  let consecutiveFailures = 0;
3363
3388
  let idleSince;
3389
+ let superseded = false;
3364
3390
  let ticks = 0;
3365
3391
  for (; ; ) {
3366
3392
  if (options.tickLimit !== void 0 && ticks >= options.tickLimit) {
@@ -3372,6 +3398,11 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3372
3398
  const allowlist = allowlistOf(ports);
3373
3399
  const allowlistHash = hashAllowlist(allowlist);
3374
3400
  pruneDeadSessions(fs2, paths.headroomSessionsDir, ports.isRunning);
3401
+ const ownsNow = ownsState();
3402
+ if (!ownsNow && !superseded) {
3403
+ ports.log("claude-use headroom supervisor: another supervisor now owns state.json; serving this daemon's own sessions until they end");
3404
+ }
3405
+ superseded = !ownsNow;
3375
3406
  const crashed = headroomPid !== void 0 && !ports.isRunning(headroomPid);
3376
3407
  if (headroomPid === void 0 || crashed) {
3377
3408
  if (crashed) {
@@ -3379,7 +3410,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3379
3410
  headroomPid = void 0;
3380
3411
  runningHash = void 0;
3381
3412
  runningSettingsHash = void 0;
3382
- writeHeadroomState(fs2, paths.headroomStateFile, {
3413
+ writeOwnState({
3383
3414
  supervisorPid: ports.ownPid,
3384
3415
  version,
3385
3416
  installedSource,
@@ -3407,7 +3438,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3407
3438
  `claude-use headroom supervisor: sticky port ${String(previousSticky)} was occupied; moving to ${String(port)} (sessions launched against the old port are stale until they relaunch)`
3408
3439
  );
3409
3440
  }
3410
- writeHeadroomState(fs2, paths.headroomStateFile, {
3441
+ const recorded = writeOwnState({
3411
3442
  supervisorPid: ports.ownPid,
3412
3443
  headroomPid: pid,
3413
3444
  port,
@@ -3417,7 +3448,9 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3417
3448
  installedSource: config.source,
3418
3449
  ...sticky()
3419
3450
  });
3420
- fs2.removeRecursive(paths.headroomLockFile);
3451
+ if (recorded) {
3452
+ fs2.removeRecursive(paths.headroomLockFile);
3453
+ }
3421
3454
  idleSince = void 0;
3422
3455
  ports.log(`claude-use headroom supervisor: headroom pid ${String(pid)} ready on 127.0.0.1:${String(port)}`);
3423
3456
  } else {
@@ -3431,7 +3464,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3431
3464
  }
3432
3465
  } else {
3433
3466
  if (runningHash !== allowlistHash || runningSettingsHash !== settingsHash || installedSource !== config.source) {
3434
- if (listSessions(fs2, paths.headroomSessionsDir).length === 0) {
3467
+ if (sessionsForSupervisor(fs2, paths.headroomSessionsDir, ports.ownPid).length === 0) {
3435
3468
  ports.log("claude-use headroom supervisor: configuration drifted and no sessions are live; restarting headroom");
3436
3469
  ports.stopProcess(headroomPid);
3437
3470
  headroomPid = void 0;
@@ -3440,7 +3473,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3440
3473
  continue;
3441
3474
  }
3442
3475
  }
3443
- const sessions = listSessions(fs2, paths.headroomSessionsDir);
3476
+ const sessions = sessionsForSupervisor(fs2, paths.headroomSessionsDir, ports.ownPid);
3444
3477
  if (sessions.length === 0) {
3445
3478
  idleSince ??= ports.now();
3446
3479
  const idleMs = ports.now() - idleSince;
@@ -3449,7 +3482,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3449
3482
  `claude-use headroom supervisor: no sessions for ${String(config.idleShutdownMinutes)} minute(s); stopping headroom and exiting`
3450
3483
  );
3451
3484
  ports.stopProcess(headroomPid);
3452
- writeHeadroomState(fs2, paths.headroomStateFile, {
3485
+ writeOwnState({
3453
3486
  version,
3454
3487
  ...sticky()
3455
3488
  });
package/dist/index.mjs CHANGED
@@ -141,10 +141,18 @@ var ConfigProfileSchema = z.strictObject({
141
141
  entries: EntriesSchema.optional(),
142
142
  launch: LaunchSchema.optional()
143
143
  });
144
+ var POOL_SELECTOR_PREFIX = "pool:";
145
+ var POOL_NAME_PATTERN = "[A-Za-z0-9][A-Za-z0-9._-]*";
146
+ var IDENTITY_NAME_PATTERN = "[A-Za-z0-9][A-Za-z0-9._@-]*";
147
+ var PoolNameSchema = z.string().min(1).regex(new RegExp(`^${POOL_NAME_PATTERN}$`));
148
+ var IdentitySelectorSchema = z.string().min(1).regex(new RegExp(`^(${POOL_SELECTOR_PREFIX}${POOL_NAME_PATTERN}|${IDENTITY_NAME_PATTERN})$`));
149
+ var PoolSchema = z.strictObject({
150
+ identities: z.array(z.string().min(1).regex(new RegExp(`^${IDENTITY_NAME_PATTERN}$`))).min(1).refine((names) => new Set(names).size === names.length, { message: "pool members must be unique" })
151
+ });
144
152
  var DirectoryRuleSchema = ConfigProfileSchema.omit({ description: true }).extend({
145
153
  path: z.string().min(1),
146
154
  configProfile: z.string().min(1).optional(),
147
- identity: z.string().min(1).optional(),
155
+ identity: IdentitySelectorSchema.optional(),
148
156
  when: WhenSchema.optional()
149
157
  });
150
158
  var DirectoryRulesSchema = z.strictObject({
@@ -153,7 +161,7 @@ var DirectoryRulesSchema = z.strictObject({
153
161
  });
154
162
  var PortableConfigSchema = ConfigProfileSchema.omit({ description: true }).extend({
155
163
  configProfile: z.string().min(1).optional(),
156
- identity: z.string().min(1).optional(),
164
+ identity: IdentitySelectorSchema.optional(),
157
165
  when: WhenSchema.optional()
158
166
  });
159
167
  var HEADROOM_MODES = ["cache", "token"];
@@ -201,7 +209,8 @@ var GlobalConfigSchema = z.strictObject({
201
209
  entries: EntriesSchema.optional(),
202
210
  launch: LaunchSchema.optional(),
203
211
  headroom: HeadroomGlobalConfigSchema.optional(),
204
- frontdoor: FrontDoorGlobalConfigSchema.optional()
212
+ frontdoor: FrontDoorGlobalConfigSchema.optional(),
213
+ pools: z.record(PoolNameSchema, PoolSchema).optional()
205
214
  });
206
215
  var IdentitySchema = z.strictObject({
207
216
  $schema: z.string().optional(),
@@ -2728,7 +2737,7 @@ var HeadroomStateSchema = z4.strictObject({
2728
2737
  /** The last fatal error, kept across shutdowns so `headroom status` can explain a daemon that is not running. */
2729
2738
  lastError: z4.string().optional()
2730
2739
  });
2731
- var HeadroomSessionSchema = z4.strictObject({ pid: z4.number().int().positive(), startedAt: z4.number() });
2740
+ var HeadroomSessionSchema = z4.strictObject({ pid: z4.number().int().positive(), startedAt: z4.number(), supervisorPid: z4.number().int().positive() });
2732
2741
  var HeadroomLockSchema = z4.strictObject({ pid: z4.number().int().positive(), at: z4.number() });
2733
2742
  function parseJson(raw, schema) {
2734
2743
  if (raw === void 0 || raw.trim() === "") {
@@ -2808,6 +2817,9 @@ function listSessions(fs2, sessionsDir) {
2808
2817
  }
2809
2818
  return sessions.sort((a, b) => a.pid - b.pid);
2810
2819
  }
2820
+ function sessionsForSupervisor(fs2, sessionsDir, supervisorPid) {
2821
+ return listSessions(fs2, sessionsDir).filter((session) => session.supervisorPid === supervisorPid);
2822
+ }
2811
2823
  function pruneDeadSessions(fs2, sessionsDir, isRunning) {
2812
2824
  const removed = [];
2813
2825
  for (const session of listSessions(fs2, sessionsDir)) {
@@ -3107,7 +3119,7 @@ function ensureHeadroom(params) {
3107
3119
  if (state?.supervisorPid !== void 0 && ports.isRunning(state.supervisorPid)) {
3108
3120
  const daemonUp = state.port !== void 0 && state.headroomPid !== void 0 && ports.isRunning(state.headroomPid);
3109
3121
  if (daemonUp && state.port !== void 0) {
3110
- writeSession(ports.fs, paths.headroomSessionsDir, { pid: params.launcherPid, startedAt: ports.now() });
3122
+ writeSession(ports.fs, paths.headroomSessionsDir, { pid: params.launcherPid, startedAt: ports.now(), supervisorPid: state.supervisorPid });
3111
3123
  return { port: state.port };
3112
3124
  }
3113
3125
  if (state.lastError !== void 0) {
@@ -3244,15 +3256,28 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3244
3256
  const { fs: fs2, paths } = ports;
3245
3257
  let config = initialConfig;
3246
3258
  const previousState = readHeadroomState(fs2, paths.headroomStateFile);
3259
+ if (previousState?.supervisorPid !== void 0 && previousState.supervisorPid !== ports.ownPid && ports.isRunning(previousState.supervisorPid)) {
3260
+ ports.log(`claude-use headroom supervisor ${String(ports.ownPid)}: supervisor ${String(previousState.supervisorPid)} already owns the daemon; exiting`);
3261
+ return 0;
3262
+ }
3247
3263
  let installedSource = previousState?.installedSource;
3248
3264
  let lastPort = previousState?.lastPort;
3249
3265
  const sticky = () => lastPort === void 0 ? {} : { lastPort };
3266
+ const ownsState = () => {
3267
+ const owner = readHeadroomState(fs2, paths.headroomStateFile)?.supervisorPid;
3268
+ return owner === void 0 || owner === ports.ownPid || !ports.isRunning(owner);
3269
+ };
3270
+ const writeOwnState = (state) => {
3271
+ if (!ownsState()) {
3272
+ return false;
3273
+ }
3274
+ writeHeadroomState(fs2, paths.headroomStateFile, state);
3275
+ return true;
3276
+ };
3250
3277
  const fail = (message) => {
3251
- writeHeadroomState(fs2, paths.headroomStateFile, {
3252
- lastError: message,
3253
- ...sticky()
3254
- });
3255
- fs2.removeRecursive(paths.headroomLockFile);
3278
+ if (writeOwnState({ lastError: message, ...sticky() })) {
3279
+ fs2.removeRecursive(paths.headroomLockFile);
3280
+ }
3256
3281
  ports.log(`claude-use headroom supervisor: ${message}`);
3257
3282
  return 1;
3258
3283
  };
@@ -3275,7 +3300,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3275
3300
  return fail(`could not install headroom from ${config.source}: install failed (is uv installed and on PATH?)`);
3276
3301
  }
3277
3302
  const version = ports.headroomVersion() ?? "unknown";
3278
- writeHeadroomState(fs2, paths.headroomStateFile, { supervisorPid: ports.ownPid, version, installedSource, ...sticky() });
3303
+ writeOwnState({ supervisorPid: ports.ownPid, version, installedSource, ...sticky() });
3279
3304
  ports.log(`claude-use headroom supervisor ${String(ports.ownPid)}: managing headroom on allowlist [${allowlistOf(ports).join(", ")}]`);
3280
3305
  const orphanPid = previousState?.headroomPid;
3281
3306
  if (orphanPid !== void 0 && ports.isRunning(orphanPid)) {
@@ -3287,6 +3312,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3287
3312
  let runningSettingsHash;
3288
3313
  let consecutiveFailures = 0;
3289
3314
  let idleSince;
3315
+ let superseded = false;
3290
3316
  let ticks = 0;
3291
3317
  for (; ; ) {
3292
3318
  if (options.tickLimit !== void 0 && ticks >= options.tickLimit) {
@@ -3298,6 +3324,11 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3298
3324
  const allowlist = allowlistOf(ports);
3299
3325
  const allowlistHash = hashAllowlist(allowlist);
3300
3326
  pruneDeadSessions(fs2, paths.headroomSessionsDir, ports.isRunning);
3327
+ const ownsNow = ownsState();
3328
+ if (!ownsNow && !superseded) {
3329
+ ports.log("claude-use headroom supervisor: another supervisor now owns state.json; serving this daemon's own sessions until they end");
3330
+ }
3331
+ superseded = !ownsNow;
3301
3332
  const crashed = headroomPid !== void 0 && !ports.isRunning(headroomPid);
3302
3333
  if (headroomPid === void 0 || crashed) {
3303
3334
  if (crashed) {
@@ -3305,7 +3336,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3305
3336
  headroomPid = void 0;
3306
3337
  runningHash = void 0;
3307
3338
  runningSettingsHash = void 0;
3308
- writeHeadroomState(fs2, paths.headroomStateFile, {
3339
+ writeOwnState({
3309
3340
  supervisorPid: ports.ownPid,
3310
3341
  version,
3311
3342
  installedSource,
@@ -3333,7 +3364,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3333
3364
  `claude-use headroom supervisor: sticky port ${String(previousSticky)} was occupied; moving to ${String(port)} (sessions launched against the old port are stale until they relaunch)`
3334
3365
  );
3335
3366
  }
3336
- writeHeadroomState(fs2, paths.headroomStateFile, {
3367
+ const recorded = writeOwnState({
3337
3368
  supervisorPid: ports.ownPid,
3338
3369
  headroomPid: pid,
3339
3370
  port,
@@ -3343,7 +3374,9 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3343
3374
  installedSource: config.source,
3344
3375
  ...sticky()
3345
3376
  });
3346
- fs2.removeRecursive(paths.headroomLockFile);
3377
+ if (recorded) {
3378
+ fs2.removeRecursive(paths.headroomLockFile);
3379
+ }
3347
3380
  idleSince = void 0;
3348
3381
  ports.log(`claude-use headroom supervisor: headroom pid ${String(pid)} ready on 127.0.0.1:${String(port)}`);
3349
3382
  } else {
@@ -3357,7 +3390,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3357
3390
  }
3358
3391
  } else {
3359
3392
  if (runningHash !== allowlistHash || runningSettingsHash !== settingsHash || installedSource !== config.source) {
3360
- if (listSessions(fs2, paths.headroomSessionsDir).length === 0) {
3393
+ if (sessionsForSupervisor(fs2, paths.headroomSessionsDir, ports.ownPid).length === 0) {
3361
3394
  ports.log("claude-use headroom supervisor: configuration drifted and no sessions are live; restarting headroom");
3362
3395
  ports.stopProcess(headroomPid);
3363
3396
  headroomPid = void 0;
@@ -3366,7 +3399,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3366
3399
  continue;
3367
3400
  }
3368
3401
  }
3369
- const sessions = listSessions(fs2, paths.headroomSessionsDir);
3402
+ const sessions = sessionsForSupervisor(fs2, paths.headroomSessionsDir, ports.ownPid);
3370
3403
  if (sessions.length === 0) {
3371
3404
  idleSince ??= ports.now();
3372
3405
  const idleMs = ports.now() - idleSince;
@@ -3375,7 +3408,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3375
3408
  `claude-use headroom supervisor: no sessions for ${String(config.idleShutdownMinutes)} minute(s); stopping headroom and exiting`
3376
3409
  );
3377
3410
  ports.stopProcess(headroomPid);
3378
- writeHeadroomState(fs2, paths.headroomStateFile, {
3411
+ writeOwnState({
3379
3412
  version,
3380
3413
  ...sticky()
3381
3414
  });
@@ -422,6 +422,15 @@ export declare const ConfigProfileSchema: z.ZodObject<{
422
422
  }, z.core.$strict>>;
423
423
  }, z.core.$strict>;
424
424
  export type ConfigProfile = z.infer<typeof ConfigProfileSchema>;
425
+ /** The prefix that turns a pool name into an identity selector. `:` is outside an identity name's character set, so a selector can never be mistaken for an identity. */
426
+ export declare const POOL_SELECTOR_PREFIX = "pool:";
427
+ /** A pool's own name: the identity alphabet minus `@`, since a pool is never an email address. */
428
+ export declare const PoolNameSchema: z.ZodString;
429
+ /** A named, explicit set of identities to choose among at launch. Never implicit: a pool lists its members, so an identity that bills a client is only ever picked when someone put it there. */
430
+ declare const PoolSchema: z.ZodObject<{
431
+ identities: z.ZodArray<z.ZodString>;
432
+ }, z.core.$strict>;
433
+ export type Pool = z.infer<typeof PoolSchema>;
425
434
  /** One directory rule in `~/.claude-use/directory-rules.json`, scoped to an explicit absolute (or `~`-rooted) path. */
426
435
  export declare const DirectoryRuleSchema: z.ZodObject<{
427
436
  entries: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
@@ -657,6 +666,9 @@ export declare const GlobalConfigSchema: z.ZodObject<{
657
666
  frontdoor: z.ZodOptional<z.ZodObject<{
658
667
  idleShutdownMinutes: z.ZodOptional<z.ZodNumber>;
659
668
  }, z.core.$strict>>;
669
+ pools: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
670
+ identities: z.ZodArray<z.ZodString>;
671
+ }, z.core.$strict>>>;
660
672
  }, z.core.$strict>;
661
673
  export type GlobalConfig = z.infer<typeof GlobalConfigSchema>;
662
674
  /**
@@ -20,10 +20,13 @@ export declare const HeadroomStateSchema: z.ZodObject<{
20
20
  export type HeadroomState = z.infer<typeof HeadroomStateSchema>;
21
21
  /** The filesystem operations headroom coordination needs: the same shape as `FarmFs`, injected so every decision over state, sessions, and the start lock runs against an in-memory fake in tests. */
22
22
  export type HeadroomFs = Pick<FarmFs, "mkdirp" | "mkdirPrivate" | "readFileUtf8" | "writeFileUtf8" | "writeFilePrivate" | "writeFileExclusive" | "readdir" | "removeRecursive">;
23
- /** The record held in one session-registry file. */
23
+ /**
24
+ * The record held in one session-registry file. `supervisorPid` names the supervisor whose daemon the launch connected to: several supervisors can be alive at once (a superseded one keeps serving the sessions it already has), and each must judge idle shutdown and drift restarts by its own sessions only, or none would ever retire.
25
+ */
24
26
  declare const HeadroomSessionSchema: z.ZodObject<{
25
27
  pid: z.ZodNumber;
26
28
  startedAt: z.ZodNumber;
29
+ supervisorPid: z.ZodNumber;
27
30
  }, z.core.$strict>;
28
31
  export type HeadroomSession = z.infer<typeof HeadroomSessionSchema>;
29
32
  /** The exclusive-create start lock's recorded holder. */
@@ -63,6 +66,8 @@ export declare function writeSession(fs: HeadroomFs, sessionsDir: string, sessio
63
66
  export declare function removeSession(fs: HeadroomFs, sessionsDir: string, pid: number): void;
64
67
  /** Every registry entry on disk, unreadable files skipped, sorted by pid: what `headroom status` prints and what the supervisor's idle and drift decisions read. */
65
68
  export declare function listSessions(fs: HeadroomFs, sessionsDir: string): readonly HeadroomSession[];
69
+ /** The registry entries recorded against one supervisor: the sessions whose daemon that supervisor is responsible for keeping up. */
70
+ export declare function sessionsForSupervisor(fs: HeadroomFs, sessionsDir: string, supervisorPid: number): readonly HeadroomSession[];
66
71
  /**
67
72
  * Removes every registry entry whose pid is no longer running: a launcher that died without releasing its entry must not keep the daemon awake or block a drift restart forever. The predicate must be zombie-aware (see `realIsProcessRunning`): a launcher that exited but was never reaped still answers signal 0 as alive, which would keep its session registered indefinitely.
68
73
  */
@@ -58,6 +58,8 @@ export interface LayoutPaths {
58
58
  readonly usageLogDir: string;
59
59
  /** Directory of the latest-state snapshots, one `<identity>.json` per identity: the stable file other tools read. */
60
60
  readonly usageSnapshotsDir: string;
61
+ /** The last identity picked from a pool for each directory, so a conversation stays on the account whose prompt cache is warm. */
62
+ readonly usagePicksFile: string;
61
63
  }
62
64
  /**
63
65
  * Resolves the current CLAUDE_USE_HOME root: the environment variable when set to a non-empty string, otherwise `~/.claude-use`. An empty string counts as unset, consistent with how this project treats empty-string environment variables elsewhere (see the ambient-credential guard).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-use",
3
- "version": "6.1.0",
3
+ "version": "6.2.1",
4
4
  "description": "A profile manager and launcher for Claude Code that lets one person run multiple logins from one machine while controlling what gets shared between them.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Joseph Mearman <joseph@mearman.co.uk>",