agent-shim 11.79.0 → 11.80.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/README.md CHANGED
@@ -57,7 +57,7 @@ That's it — with no further configuration, everything in `~/.claude` that isn'
57
57
  - **An identity** is a login: the thing that owns credentials and daemon state, selected with `agent-shim run @<name>` or, once `agent-shim shim enable` has run, `claude @<name>`.
58
58
  - **A configuration profile** is a reusable, named bundle of sharing rules, independent of any identity, resolved per working directory via directory rules or a committed `.agent-shim.json`.
59
59
 
60
- A third, optional thing sits on top of identities: **a pool** is a named list of them. Select `pool:<name>` where an identity name goes (`claude @pool:subs`) and agent-shim picks the member whose unused quota is closest to expiring, skipping any that are refused right now, using the usage it already records. A pool can instead declare `--preference listed`, which tries the members in the order the pool lists them, skipping only one that is refused right now. A member entry may itself name another pool (`pool:<name>`), which is ranked with its own policy and contributes its pick at that position, so "this identity first, then the fleet" nests the fleet rather than copying its member list; cycles are refused. See [docs/configuration-model.md](docs/configuration-model.md#pools-picking-an-account-at-launch). On a terminal, `agent-shim run` asks which identity to run as when none resolves, when a pool pick would change account, or when the identity is refused right now (`--pick` forces the question, `--no-pick` never asks); see [docs/configuration-model.md](docs/configuration-model.md#choosing-the-identity-at-launch-the-picker).
60
+ A third, optional thing sits on top of identities: **a pool** is a named list of them. Select `pool:<name>` where an identity name goes (`claude @pool:subs`) and agent-shim picks the member whose unused quota is closest to expiring, skipping any that are refused right now, using the usage it already records. A pool can instead declare `--preference listed`, which tries the members in the order the pool lists them, skipping only one that is refused right now. A member entry may itself name another pool (`pool:<name>`), which is ranked with its own policy and contributes its pick at that position, so "this identity first, then the fleet" nests the fleet rather than copying its member list; cycles are refused. Members that hold the same login (the same account in the same organisation) share one quota window, so a pool ranks them as one candidate and never fails over from one to the other; `pool add`, `pool set` and `doctor` warn when a pool lists two of them. See [docs/configuration-model.md](docs/configuration-model.md#pools-picking-an-account-at-launch). On a terminal, `agent-shim run` asks which identity to run as when none resolves, when a pool pick would change account, or when the identity is refused right now (`--pick` forces the question, `--no-pick` never asks); see [docs/configuration-model.md](docs/configuration-model.md#choosing-the-identity-at-launch-the-picker).
61
61
 
62
62
  Every top-level entry in `~/.claude` is classified into one of five categories, shipped as a default map (`config/categories.default.json`):
63
63
 
package/dist/cli.cjs CHANGED
@@ -38192,13 +38192,95 @@ var init_account = __esm({
38192
38192
  }
38193
38193
  });
38194
38194
 
38195
+ // src/usage/accountDescription.ts
38196
+ function describeAccountOrganisation(account) {
38197
+ const parts = [account.organizationName, account.organizationType].filter((part) => part !== void 0);
38198
+ return parts.length === 0 ? void 0 : parts.join(", ");
38199
+ }
38200
+ function describeAccountPlan(account) {
38201
+ const parts = [account.billingType === void 0 ? void 0 : `billing ${account.billingType}`, account.seatTier === void 0 ? void 0 : `seat tier ${account.seatTier}`].filter((part) => part !== void 0);
38202
+ return parts.length === 0 ? void 0 : parts.join(", ");
38203
+ }
38204
+ var init_accountDescription = __esm({
38205
+ "src/usage/accountDescription.ts"() {
38206
+ "use strict";
38207
+ }
38208
+ });
38209
+
38210
+ // src/identityAccounts.ts
38211
+ function readIdentityAccount(fs31, identitiesDir, identity) {
38212
+ try {
38213
+ const account = readAccountMetadata(fs31, identitiesDir, identity);
38214
+ return account === void 0 ? void 0 : { account };
38215
+ } catch (error63) {
38216
+ if (error63 instanceof AccountMetadataError) {
38217
+ return { problem: error63.message };
38218
+ }
38219
+ throw error63;
38220
+ }
38221
+ }
38222
+ function summariseIdentityAccount(account) {
38223
+ const summary = {
38224
+ ...account.emailAddress === void 0 ? {} : { emailAddress: account.emailAddress },
38225
+ ...account.organizationName === void 0 ? {} : { organizationName: account.organizationName },
38226
+ ...account.organizationType === void 0 ? {} : { organizationType: account.organizationType },
38227
+ ...account.organizationRole === void 0 ? {} : { organizationRole: account.organizationRole },
38228
+ ...account.billingType === void 0 ? {} : { billingType: account.billingType },
38229
+ ...account.seatTier === void 0 ? {} : { seatTier: account.seatTier }
38230
+ };
38231
+ return Object.keys(summary).length === 0 ? void 0 : summary;
38232
+ }
38233
+ function loginKeyOf(account) {
38234
+ return account.accountUuid === void 0 ? void 0 : JSON.stringify([account.accountUuid, account.organizationUuid ?? null]);
38235
+ }
38236
+ function collectByLogin(items, loginOf) {
38237
+ const groups = /* @__PURE__ */ new Map();
38238
+ for (const item of items) {
38239
+ const key = loginOf(item) ?? /* @__PURE__ */ Symbol("no login");
38240
+ const group = groups.get(key);
38241
+ if (group === void 0) {
38242
+ groups.set(key, [item]);
38243
+ } else {
38244
+ group.push(item);
38245
+ }
38246
+ }
38247
+ return [...groups.values()];
38248
+ }
38249
+ function groupByLogin(items, loginOf) {
38250
+ return collectByLogin(items, loginOf).flat();
38251
+ }
38252
+ function groupSharedLogins(items, loginOf) {
38253
+ return collectByLogin(items, loginOf).filter((group) => group.length > 1);
38254
+ }
38255
+ function joinNames(names) {
38256
+ const last = names.at(-1);
38257
+ return names.length < 2 || last === void 0 ? names.join("") : `${names.slice(0, -1).join(", ")} and ${last}`;
38258
+ }
38259
+ function describeAccountHolder(summary) {
38260
+ const organisation = describeAccountOrganisation(summary);
38261
+ if (summary.emailAddress === void 0) {
38262
+ return organisation;
38263
+ }
38264
+ return organisation === void 0 ? summary.emailAddress : `${summary.emailAddress} (${organisation})`;
38265
+ }
38266
+ function describeIdentityAccount(summary) {
38267
+ return [describeAccountHolder(summary), summary.organizationRole === void 0 ? void 0 : `role ${summary.organizationRole}`, describeAccountPlan(summary)].filter((part) => part !== void 0).join("; ");
38268
+ }
38269
+ var init_identityAccounts = __esm({
38270
+ "src/identityAccounts.ts"() {
38271
+ "use strict";
38272
+ init_account();
38273
+ init_accountDescription();
38274
+ }
38275
+ });
38276
+
38195
38277
  // package.json
38196
38278
  var package_default;
38197
38279
  var init_package = __esm({
38198
38280
  "package.json"() {
38199
38281
  package_default = {
38200
38282
  name: "agent-shim",
38201
- version: "11.79.0",
38283
+ version: "11.80.0",
38202
38284
  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.",
38203
38285
  license: "MIT",
38204
38286
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -38918,6 +39000,9 @@ function assessRefusedNested(member, nested, nowMs) {
38918
39000
  };
38919
39001
  }
38920
39002
  function assess(member, nowMs) {
39003
+ if (member.sameLoginAs !== void 0) {
39004
+ return { candidate: { identity: member.identity, class: "ineligible", plan: planOf(member.account), reasons: [`same login as ${member.sameLoginAs}`] } };
39005
+ }
38921
39006
  const nested = member.nested;
38922
39007
  if (nested === void 0) {
38923
39008
  return assessIdentity(member, nowMs);
@@ -39461,6 +39546,29 @@ function loadPoolMembers(fs31, paths, identities, nowMs) {
39461
39546
  }
39462
39547
  });
39463
39548
  }
39549
+ function snapshotAgeKey(member) {
39550
+ return member.snapshot === void 0 ? Number.NEGATIVE_INFINITY : Date.parse(member.snapshot.updatedAt);
39551
+ }
39552
+ function collapseSharedLogins(members2) {
39553
+ const groups = groupSharedLogins(members2, (member) => member.nested === void 0 && member.account !== void 0 ? loginKeyOf(member.account) : void 0);
39554
+ const representativeOf = /* @__PURE__ */ new Map();
39555
+ for (const group of groups) {
39556
+ const representative = group.reduce((freshest, member) => snapshotAgeKey(member) > snapshotAgeKey(freshest) ? member : freshest);
39557
+ for (const member of group) {
39558
+ if (member !== representative) {
39559
+ representativeOf.set(member, representative.identity);
39560
+ }
39561
+ }
39562
+ }
39563
+ return members2.map((member) => {
39564
+ const representative = representativeOf.get(member);
39565
+ return representative === void 0 ? member : { ...member, sameLoginAs: representative };
39566
+ });
39567
+ }
39568
+ function stickyOnRepresentative(sticky, members2) {
39569
+ const twin = sticky === void 0 ? void 0 : members2.find((member) => member.identity === sticky.identity && member.sameLoginAs !== void 0);
39570
+ return sticky === void 0 || twin?.sameLoginAs === void 0 ? sticky : { ...sticky, identity: twin.sameLoginAs };
39571
+ }
39464
39572
  function rankPoolGraph(input2) {
39465
39573
  return rankNamedPool(input2, input2.poolName, [], input2.sticky);
39466
39574
  }
@@ -39488,13 +39596,15 @@ function rankNamedPool(input2, poolName, stack, sticky) {
39488
39596
  members2.push(typeof entry === "string" ? nested.member : { ...nested.member, policy: entry.when });
39489
39597
  missing.push(...nested.missing);
39490
39598
  }
39599
+ const collapsed = collapseSharedLogins(members2);
39600
+ const stickyPick = stickyOnRepresentative(sticky, collapsed);
39491
39601
  const ranking = rankPool({
39492
- members: members2,
39602
+ members: collapsed,
39493
39603
  nowMs: input2.nowMs,
39494
39604
  resuming: input2.resuming,
39495
39605
  ...input2.resumeContextTokens === void 0 ? {} : { resumeContextTokens: input2.resumeContextTokens },
39496
39606
  ...input2.committedContextTokens === void 0 ? {} : { committedContextTokens: input2.committedContextTokens },
39497
- ...sticky === void 0 ? {} : { sticky },
39607
+ ...stickyPick === void 0 ? {} : { sticky: stickyPick },
39498
39608
  ...pool.preference === void 0 ? {} : { preference: pool.preference }
39499
39609
  });
39500
39610
  return { ranking, missing };
@@ -39561,6 +39671,7 @@ var init_poolPick = __esm({
39561
39671
  init_cliError();
39562
39672
  init_identity();
39563
39673
  init_schema();
39674
+ init_identityAccounts();
39564
39675
  init_account();
39565
39676
  init_pick();
39566
39677
  init_read();
@@ -117704,77 +117815,8 @@ function registerProfileCommand(program2, deps) {
117704
117815
  init_providersStore();
117705
117816
  init_identity();
117706
117817
  init_realPorts();
117707
-
117708
- // src/identityAccounts.ts
117709
- init_account();
117710
-
117711
- // src/usage/accountDescription.ts
117712
- function describeAccountOrganisation(account) {
117713
- const parts = [account.organizationName, account.organizationType].filter((part) => part !== void 0);
117714
- return parts.length === 0 ? void 0 : parts.join(", ");
117715
- }
117716
- function describeAccountPlan(account) {
117717
- const parts = [account.billingType === void 0 ? void 0 : `billing ${account.billingType}`, account.seatTier === void 0 ? void 0 : `seat tier ${account.seatTier}`].filter((part) => part !== void 0);
117718
- return parts.length === 0 ? void 0 : parts.join(", ");
117719
- }
117720
-
117721
- // src/identityAccounts.ts
117722
- function readIdentityAccount(fs31, identitiesDir, identity) {
117723
- try {
117724
- const account = readAccountMetadata(fs31, identitiesDir, identity);
117725
- return account === void 0 ? void 0 : { account };
117726
- } catch (error63) {
117727
- if (error63 instanceof AccountMetadataError) {
117728
- return { problem: error63.message };
117729
- }
117730
- throw error63;
117731
- }
117732
- }
117733
- function summariseIdentityAccount(account) {
117734
- const summary = {
117735
- ...account.emailAddress === void 0 ? {} : { emailAddress: account.emailAddress },
117736
- ...account.organizationName === void 0 ? {} : { organizationName: account.organizationName },
117737
- ...account.organizationType === void 0 ? {} : { organizationType: account.organizationType },
117738
- ...account.organizationRole === void 0 ? {} : { organizationRole: account.organizationRole },
117739
- ...account.billingType === void 0 ? {} : { billingType: account.billingType },
117740
- ...account.seatTier === void 0 ? {} : { seatTier: account.seatTier }
117741
- };
117742
- return Object.keys(summary).length === 0 ? void 0 : summary;
117743
- }
117744
- function loginKeyOf(account) {
117745
- return account.accountUuid === void 0 ? void 0 : JSON.stringify([account.accountUuid, account.organizationUuid ?? null]);
117746
- }
117747
- function collectByLogin(items, loginOf) {
117748
- const groups = /* @__PURE__ */ new Map();
117749
- for (const item of items) {
117750
- const key = loginOf(item) ?? /* @__PURE__ */ Symbol("no login");
117751
- const group = groups.get(key);
117752
- if (group === void 0) {
117753
- groups.set(key, [item]);
117754
- } else {
117755
- group.push(item);
117756
- }
117757
- }
117758
- return [...groups.values()];
117759
- }
117760
- function groupByLogin(items, loginOf) {
117761
- return collectByLogin(items, loginOf).flat();
117762
- }
117763
- function groupSharedLogins(items, loginOf) {
117764
- return collectByLogin(items, loginOf).filter((group) => group.length > 1);
117765
- }
117766
- function describeAccountHolder(summary) {
117767
- const organisation = describeAccountOrganisation(summary);
117768
- if (summary.emailAddress === void 0) {
117769
- return organisation;
117770
- }
117771
- return organisation === void 0 ? summary.emailAddress : `${summary.emailAddress} (${organisation})`;
117772
- }
117773
- function describeIdentityAccount(summary) {
117774
- return [describeAccountHolder(summary), summary.organizationRole === void 0 ? void 0 : `role ${summary.organizationRole}`, describeAccountPlan(summary)].filter((part) => part !== void 0).join("; ");
117775
- }
117776
-
117777
- // src/identityManager.ts
117818
+ init_identityAccounts();
117819
+ init_accountDescription();
117778
117820
  async function runIdentityWizard(prompts, paths, name, options = {}) {
117779
117821
  if (!IdentitySchema.safeParse({ name, allowAmbientCredential: false }).success) {
117780
117822
  throw new InvalidIdentityNameError(name);
@@ -123234,6 +123276,7 @@ init_realPorts();
123234
123276
  var import_node_fs20 = __toESM(require("node:fs"), 1);
123235
123277
  var import_node_path47 = __toESM(require("node:path"), 1);
123236
123278
  init_signIn();
123279
+ init_identityAccounts();
123237
123280
  init_load();
123238
123281
  init_unrecognisedKeys();
123239
123282
  init_schema();
@@ -123508,6 +123551,27 @@ init_providerAliases();
123508
123551
  init_providersStore();
123509
123552
  init_guard();
123510
123553
  init_identity();
123554
+
123555
+ // src/poolSharedLogins.ts
123556
+ init_identityAccounts();
123557
+ init_identity();
123558
+ function sharedLoginMembers(members2, accountOf) {
123559
+ const known = members2.flatMap((identity) => {
123560
+ const account = poolNameOf(identity) === void 0 ? accountOf(identity) : void 0;
123561
+ return account === void 0 ? [] : [{ identity, account }];
123562
+ });
123563
+ return groupSharedLogins(known, (entry) => loginKeyOf(entry.account)).flatMap((group) => {
123564
+ const first = group[0];
123565
+ return first === void 0 ? [] : [{ members: group.map((entry) => entry.identity), account: first.account }];
123566
+ });
123567
+ }
123568
+ function describeSharedLoginMembers(poolName, shared) {
123569
+ const summary = summariseIdentityAccount(shared.account);
123570
+ const holder = summary === void 0 ? void 0 : describeAccountHolder(summary);
123571
+ return `Pool "${poolName}" lists ${joinNames(shared.members)}, which hold the same login${holder === void 0 ? "" : ` (${holder})`}, so they share one quota window. A launch ranks them as one candidate and never fails over from one to another; keep one of them in the pool.`;
123572
+ }
123573
+
123574
+ // src/doctorReport.ts
123511
123575
  init_extends();
123512
123576
  var DOCTOR_SEVERITIES = ["pass", "warn", "fail"];
123513
123577
  var DOCTOR_SECTIONS = [
@@ -123530,10 +123594,6 @@ var DOCTOR_SECTIONS = [
123530
123594
  "frontdoor",
123531
123595
  "models"
123532
123596
  ];
123533
- function joinNames(names) {
123534
- const last = names.at(-1);
123535
- return names.length < 2 || last === void 0 ? names.join("") : `${names.slice(0, -1).join(", ")} and ${last}`;
123536
- }
123537
123597
  function refinePathShadow(status, ownExecutablePath, realpath) {
123538
123598
  if (status.status !== "shadowed") {
123539
123599
  return status;
@@ -123846,6 +123906,7 @@ function runDoctor(params) {
123846
123906
  }
123847
123907
  const validatedGlobalConfig = params.globalConfig.raw === void 0 ? void 0 : validateJson(GlobalConfigSchema, params.globalConfig);
123848
123908
  const pools = validatedGlobalConfig?.ok === true ? validatedGlobalConfig.data.pools ?? {} : {};
123909
+ const accountByIdentity = new Map(params.identities.flatMap((entry) => validIdentityNames.has(entry.name) && entry.account !== void 0 ? [[entry.name, entry.account]] : []));
123849
123910
  for (const [poolName, pool] of Object.entries(pools)) {
123850
123911
  const missing = pool.identities.filter((member) => poolNameOf(poolMemberIdentity(member)) === void 0 && !validIdentityNames.has(poolMemberIdentity(member)));
123851
123912
  const undefinedPools = pool.identities.flatMap((member) => {
@@ -123861,6 +123922,9 @@ function runDoctor(params) {
123861
123922
  ];
123862
123923
  push("pool", "fail", `Pool "${poolName}" names ${problems.join(" and ")}.`, poolName);
123863
123924
  }
123925
+ for (const shared of sharedLoginMembers(pool.identities.map(poolMemberIdentity), (identityName) => accountByIdentity.get(identityName))) {
123926
+ push("pool", "warn", describeSharedLoginMembers(poolName, shared), poolName);
123927
+ }
123864
123928
  }
123865
123929
  const reportedCycles = /* @__PURE__ */ new Set();
123866
123930
  for (const poolName of Object.keys(pools).sort()) {
@@ -156376,6 +156440,7 @@ init_schema();
156376
156440
  init_realPorts();
156377
156441
  init_candidateText();
156378
156442
  init_preflight();
156443
+ init_identityAccounts();
156379
156444
  function formatPoolPick(report, nowMs) {
156380
156445
  const head = report.pick === void 0 ? `Pool ${report.pool}: every member is refused right now${report.earliestReturn === void 0 ? "" : `; ${report.earliestReturn.identity} returns at ${report.earliestReturn.at} (in ${formatAge(Date.parse(report.earliestReturn.at) - nowMs)})`}` : `Pool ${report.pool}: a launch from ${report.directory} would run as ${report.pick}`;
156381
156446
  const rows = report.candidates.map((candidate, index) => {
@@ -156384,6 +156449,11 @@ function formatPoolPick(report, nowMs) {
156384
156449
  });
156385
156450
  return [head, ...rows, ...report.missing.map((name) => ` (skipped: ${name} is not an identity)`), ...report.stickyProblem === void 0 ? [] : [`Note: ${report.stickyProblem}; the last-pick record was ignored.`]];
156386
156451
  }
156452
+ function warnSharedLogins(paths, poolName, members2) {
156453
+ for (const shared of sharedLoginMembers(members2, (identity) => readIdentityAccount(realFsPort, paths.identitiesDir, identity)?.account)) {
156454
+ console.error(`agent-shim: warning: ${describeSharedLoginMembers(poolName, shared)}`);
156455
+ }
156456
+ }
156387
156457
  function policyNotes(entry) {
156388
156458
  const notes = [
156389
156459
  ...entry?.preference === void 0 ? [] : [`preference: ${entry.preference}`],
@@ -156416,6 +156486,7 @@ function registerPoolCommand(program2, deps) {
156416
156486
  reportMutation(options.json, { action: "created", kind: "pool", name, value: created }, () => {
156417
156487
  console.log(`Created pool "${name}" with ${members2.join(", ")}.`);
156418
156488
  });
156489
+ warnSharedLogins(paths, name, created.identities.map(poolMemberIdentity));
156419
156490
  }),
156420
156491
  ["agent-shim pool add subs --identity work --identity personal", "agent-shim pool add client --identity client-main --identity spare --preference listed"]
156421
156492
  );
@@ -156433,6 +156504,7 @@ function registerPoolCommand(program2, deps) {
156433
156504
  reportMutation(options.json, { action: "updated", kind: "pool", name, value: updated }, () => {
156434
156505
  console.log(`Pool "${name}" now has ${updated.identities.map(poolMemberIdentity).join(", ")}.`);
156435
156506
  });
156507
+ warnSharedLogins(paths, name, updated.identities.map(poolMemberIdentity));
156436
156508
  }),
156437
156509
  ["agent-shim pool set subs --identity work --identity personal --identity spare", "agent-shim pool set subs --preference listed", "agent-shim pool set subs --resume-on-limit --compact-before-limit --compact-at-utilisation 85"]
156438
156510
  );
@@ -157481,6 +157553,7 @@ var import_node_path70 = __toESM(require("node:path"), 1);
157481
157553
  init_commandDeps();
157482
157554
  init_realPorts();
157483
157555
  init_account();
157556
+ init_accountDescription();
157484
157557
  init_signIn();
157485
157558
  init_resets();
157486
157559
  init_read();
@@ -157839,6 +157912,7 @@ function promptsIdentityPicker(prompts) {
157839
157912
  // src/launcher/runPicker.ts
157840
157913
  init_cliError();
157841
157914
  init_schema();
157915
+ init_identityAccounts();
157842
157916
  init_account();
157843
157917
  init_candidateText();
157844
157918
  init_pick();
package/dist/index.cjs CHANGED
@@ -4764,7 +4764,7 @@ var init_package = __esm({
4764
4764
  "package.json"() {
4765
4765
  package_default = {
4766
4766
  name: "agent-shim",
4767
- version: "11.79.0",
4767
+ version: "11.80.0",
4768
4768
  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.",
4769
4769
  license: "MIT",
4770
4770
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -7037,6 +7037,78 @@ var init_account = __esm({
7037
7037
  }
7038
7038
  });
7039
7039
 
7040
+ // src/usage/accountDescription.ts
7041
+ function describeAccountOrganisation(account) {
7042
+ const parts = [account.organizationName, account.organizationType].filter((part) => part !== void 0);
7043
+ return parts.length === 0 ? void 0 : parts.join(", ");
7044
+ }
7045
+ var init_accountDescription = __esm({
7046
+ "src/usage/accountDescription.ts"() {
7047
+ "use strict";
7048
+ }
7049
+ });
7050
+
7051
+ // src/identityAccounts.ts
7052
+ function readIdentityAccount(fs14, identitiesDir, identity) {
7053
+ try {
7054
+ const account = readAccountMetadata(fs14, identitiesDir, identity);
7055
+ return account === void 0 ? void 0 : { account };
7056
+ } catch (error) {
7057
+ if (error instanceof AccountMetadataError) {
7058
+ return { problem: error.message };
7059
+ }
7060
+ throw error;
7061
+ }
7062
+ }
7063
+ function summariseIdentityAccount(account) {
7064
+ const summary = {
7065
+ ...account.emailAddress === void 0 ? {} : { emailAddress: account.emailAddress },
7066
+ ...account.organizationName === void 0 ? {} : { organizationName: account.organizationName },
7067
+ ...account.organizationType === void 0 ? {} : { organizationType: account.organizationType },
7068
+ ...account.organizationRole === void 0 ? {} : { organizationRole: account.organizationRole },
7069
+ ...account.billingType === void 0 ? {} : { billingType: account.billingType },
7070
+ ...account.seatTier === void 0 ? {} : { seatTier: account.seatTier }
7071
+ };
7072
+ return Object.keys(summary).length === 0 ? void 0 : summary;
7073
+ }
7074
+ function loginKeyOf(account) {
7075
+ return account.accountUuid === void 0 ? void 0 : JSON.stringify([account.accountUuid, account.organizationUuid ?? null]);
7076
+ }
7077
+ function collectByLogin(items, loginOf) {
7078
+ const groups = /* @__PURE__ */ new Map();
7079
+ for (const item of items) {
7080
+ const key = loginOf(item) ?? /* @__PURE__ */ Symbol("no login");
7081
+ const group = groups.get(key);
7082
+ if (group === void 0) {
7083
+ groups.set(key, [item]);
7084
+ } else {
7085
+ group.push(item);
7086
+ }
7087
+ }
7088
+ return [...groups.values()];
7089
+ }
7090
+ function groupSharedLogins(items, loginOf) {
7091
+ return collectByLogin(items, loginOf).filter((group) => group.length > 1);
7092
+ }
7093
+ function joinNames(names) {
7094
+ const last = names.at(-1);
7095
+ return names.length < 2 || last === void 0 ? names.join("") : `${names.slice(0, -1).join(", ")} and ${last}`;
7096
+ }
7097
+ function describeAccountHolder(summary) {
7098
+ const organisation = describeAccountOrganisation(summary);
7099
+ if (summary.emailAddress === void 0) {
7100
+ return organisation;
7101
+ }
7102
+ return organisation === void 0 ? summary.emailAddress : `${summary.emailAddress} (${organisation})`;
7103
+ }
7104
+ var init_identityAccounts = __esm({
7105
+ "src/identityAccounts.ts"() {
7106
+ "use strict";
7107
+ init_account();
7108
+ init_accountDescription();
7109
+ }
7110
+ });
7111
+
7040
7112
  // src/usage/rateLimit.ts
7041
7113
  function epochSecondsToIso(value) {
7042
7114
  if (value === void 0) {
@@ -7404,6 +7476,9 @@ function assessRefusedNested(member, nested, nowMs) {
7404
7476
  };
7405
7477
  }
7406
7478
  function assess(member, nowMs) {
7479
+ if (member.sameLoginAs !== void 0) {
7480
+ return { candidate: { identity: member.identity, class: "ineligible", plan: planOf(member.account), reasons: [`same login as ${member.sameLoginAs}`] } };
7481
+ }
7407
7482
  const nested = member.nested;
7408
7483
  if (nested === void 0) {
7409
7484
  return assessIdentity(member, nowMs);
@@ -7876,6 +7951,29 @@ function loadPoolMembers(fs14, paths, identities, nowMs) {
7876
7951
  }
7877
7952
  });
7878
7953
  }
7954
+ function snapshotAgeKey(member) {
7955
+ return member.snapshot === void 0 ? Number.NEGATIVE_INFINITY : Date.parse(member.snapshot.updatedAt);
7956
+ }
7957
+ function collapseSharedLogins(members) {
7958
+ const groups = groupSharedLogins(members, (member) => member.nested === void 0 && member.account !== void 0 ? loginKeyOf(member.account) : void 0);
7959
+ const representativeOf = /* @__PURE__ */ new Map();
7960
+ for (const group of groups) {
7961
+ const representative = group.reduce((freshest, member) => snapshotAgeKey(member) > snapshotAgeKey(freshest) ? member : freshest);
7962
+ for (const member of group) {
7963
+ if (member !== representative) {
7964
+ representativeOf.set(member, representative.identity);
7965
+ }
7966
+ }
7967
+ }
7968
+ return members.map((member) => {
7969
+ const representative = representativeOf.get(member);
7970
+ return representative === void 0 ? member : { ...member, sameLoginAs: representative };
7971
+ });
7972
+ }
7973
+ function stickyOnRepresentative(sticky, members) {
7974
+ const twin = sticky === void 0 ? void 0 : members.find((member) => member.identity === sticky.identity && member.sameLoginAs !== void 0);
7975
+ return sticky === void 0 || twin?.sameLoginAs === void 0 ? sticky : { ...sticky, identity: twin.sameLoginAs };
7976
+ }
7879
7977
  function rankPoolGraph(input) {
7880
7978
  return rankNamedPool(input, input.poolName, [], input.sticky);
7881
7979
  }
@@ -7903,13 +8001,15 @@ function rankNamedPool(input, poolName, stack, sticky) {
7903
8001
  members.push(typeof entry === "string" ? nested.member : { ...nested.member, policy: entry.when });
7904
8002
  missing.push(...nested.missing);
7905
8003
  }
8004
+ const collapsed = collapseSharedLogins(members);
8005
+ const stickyPick = stickyOnRepresentative(sticky, collapsed);
7906
8006
  const ranking = rankPool({
7907
- members,
8007
+ members: collapsed,
7908
8008
  nowMs: input.nowMs,
7909
8009
  resuming: input.resuming,
7910
8010
  ...input.resumeContextTokens === void 0 ? {} : { resumeContextTokens: input.resumeContextTokens },
7911
8011
  ...input.committedContextTokens === void 0 ? {} : { committedContextTokens: input.committedContextTokens },
7912
- ...sticky === void 0 ? {} : { sticky },
8012
+ ...stickyPick === void 0 ? {} : { sticky: stickyPick },
7913
8013
  ...pool.preference === void 0 ? {} : { preference: pool.preference }
7914
8014
  });
7915
8015
  return { ranking, missing };
@@ -7997,6 +8097,7 @@ var init_poolPick = __esm({
7997
8097
  init_cliError();
7998
8098
  init_identity();
7999
8099
  init_schema();
8100
+ init_identityAccounts();
8000
8101
  init_account();
8001
8102
  init_pick();
8002
8103
  init_read();
@@ -17084,65 +17185,7 @@ init_realPorts();
17084
17185
  var import_node_fs10 = __toESM(require("node:fs"), 1);
17085
17186
  var import_node_path29 = __toESM(require("node:path"), 1);
17086
17187
  init_signIn();
17087
-
17088
- // src/identityAccounts.ts
17089
- init_account();
17090
-
17091
- // src/usage/accountDescription.ts
17092
- function describeAccountOrganisation(account) {
17093
- const parts = [account.organizationName, account.organizationType].filter((part) => part !== void 0);
17094
- return parts.length === 0 ? void 0 : parts.join(", ");
17095
- }
17096
-
17097
- // src/identityAccounts.ts
17098
- function readIdentityAccount(fs14, identitiesDir, identity) {
17099
- try {
17100
- const account = readAccountMetadata(fs14, identitiesDir, identity);
17101
- return account === void 0 ? void 0 : { account };
17102
- } catch (error) {
17103
- if (error instanceof AccountMetadataError) {
17104
- return { problem: error.message };
17105
- }
17106
- throw error;
17107
- }
17108
- }
17109
- function summariseIdentityAccount(account) {
17110
- const summary = {
17111
- ...account.emailAddress === void 0 ? {} : { emailAddress: account.emailAddress },
17112
- ...account.organizationName === void 0 ? {} : { organizationName: account.organizationName },
17113
- ...account.organizationType === void 0 ? {} : { organizationType: account.organizationType },
17114
- ...account.organizationRole === void 0 ? {} : { organizationRole: account.organizationRole },
17115
- ...account.billingType === void 0 ? {} : { billingType: account.billingType },
17116
- ...account.seatTier === void 0 ? {} : { seatTier: account.seatTier }
17117
- };
17118
- return Object.keys(summary).length === 0 ? void 0 : summary;
17119
- }
17120
- function loginKeyOf(account) {
17121
- return account.accountUuid === void 0 ? void 0 : JSON.stringify([account.accountUuid, account.organizationUuid ?? null]);
17122
- }
17123
- function collectByLogin(items, loginOf) {
17124
- const groups = /* @__PURE__ */ new Map();
17125
- for (const item of items) {
17126
- const key = loginOf(item) ?? /* @__PURE__ */ Symbol("no login");
17127
- const group = groups.get(key);
17128
- if (group === void 0) {
17129
- groups.set(key, [item]);
17130
- } else {
17131
- group.push(item);
17132
- }
17133
- }
17134
- return [...groups.values()];
17135
- }
17136
- function groupSharedLogins(items, loginOf) {
17137
- return collectByLogin(items, loginOf).filter((group) => group.length > 1);
17138
- }
17139
- function describeAccountHolder(summary) {
17140
- const organisation = describeAccountOrganisation(summary);
17141
- if (summary.emailAddress === void 0) {
17142
- return organisation;
17143
- }
17144
- return organisation === void 0 ? summary.emailAddress : `${summary.emailAddress} (${organisation})`;
17145
- }
17188
+ init_identityAccounts();
17146
17189
 
17147
17190
  // src/codex/siwcStore.ts
17148
17191
  var import_zod26 = require("zod");
@@ -17496,6 +17539,27 @@ init_providerAliases();
17496
17539
  init_providersStore();
17497
17540
  init_guard();
17498
17541
  init_identity();
17542
+
17543
+ // src/poolSharedLogins.ts
17544
+ init_identityAccounts();
17545
+ init_identity();
17546
+ function sharedLoginMembers(members, accountOf) {
17547
+ const known = members.flatMap((identity) => {
17548
+ const account = poolNameOf(identity) === void 0 ? accountOf(identity) : void 0;
17549
+ return account === void 0 ? [] : [{ identity, account }];
17550
+ });
17551
+ return groupSharedLogins(known, (entry) => loginKeyOf(entry.account)).flatMap((group) => {
17552
+ const first = group[0];
17553
+ return first === void 0 ? [] : [{ members: group.map((entry) => entry.identity), account: first.account }];
17554
+ });
17555
+ }
17556
+ function describeSharedLoginMembers(poolName, shared) {
17557
+ const summary = summariseIdentityAccount(shared.account);
17558
+ const holder = summary === void 0 ? void 0 : describeAccountHolder(summary);
17559
+ return `Pool "${poolName}" lists ${joinNames(shared.members)}, which hold the same login${holder === void 0 ? "" : ` (${holder})`}, so they share one quota window. A launch ranks them as one candidate and never fails over from one to another; keep one of them in the pool.`;
17560
+ }
17561
+
17562
+ // src/doctorReport.ts
17499
17563
  init_extends();
17500
17564
  var DOCTOR_SEVERITIES = ["pass", "warn", "fail"];
17501
17565
  var DOCTOR_SECTIONS = [
@@ -17518,10 +17582,6 @@ var DOCTOR_SECTIONS = [
17518
17582
  "frontdoor",
17519
17583
  "models"
17520
17584
  ];
17521
- function joinNames(names) {
17522
- const last = names.at(-1);
17523
- return names.length < 2 || last === void 0 ? names.join("") : `${names.slice(0, -1).join(", ")} and ${last}`;
17524
- }
17525
17585
  function refinePathShadow(status, ownExecutablePath, realpath) {
17526
17586
  if (status.status !== "shadowed") {
17527
17587
  return status;
@@ -17834,6 +17894,7 @@ function runDoctor(params) {
17834
17894
  }
17835
17895
  const validatedGlobalConfig = params.globalConfig.raw === void 0 ? void 0 : validateJson(GlobalConfigSchema, params.globalConfig);
17836
17896
  const pools = validatedGlobalConfig?.ok === true ? validatedGlobalConfig.data.pools ?? {} : {};
17897
+ const accountByIdentity = new Map(params.identities.flatMap((entry) => validIdentityNames.has(entry.name) && entry.account !== void 0 ? [[entry.name, entry.account]] : []));
17837
17898
  for (const [poolName, pool] of Object.entries(pools)) {
17838
17899
  const missing = pool.identities.filter((member) => poolNameOf(poolMemberIdentity(member)) === void 0 && !validIdentityNames.has(poolMemberIdentity(member)));
17839
17900
  const undefinedPools = pool.identities.flatMap((member) => {
@@ -17849,6 +17910,9 @@ function runDoctor(params) {
17849
17910
  ];
17850
17911
  push("pool", "fail", `Pool "${poolName}" names ${problems.join(" and ")}.`, poolName);
17851
17912
  }
17913
+ for (const shared of sharedLoginMembers(pool.identities.map(poolMemberIdentity), (identityName) => accountByIdentity.get(identityName))) {
17914
+ push("pool", "warn", describeSharedLoginMembers(poolName, shared), poolName);
17915
+ }
17852
17916
  }
17853
17917
  const reportedCycles = /* @__PURE__ */ new Set();
17854
17918
  for (const poolName of Object.keys(pools).sort()) {
package/dist/index.mjs CHANGED
@@ -4740,7 +4740,7 @@ var init_package = __esm({
4740
4740
  "package.json"() {
4741
4741
  package_default = {
4742
4742
  name: "agent-shim",
4743
- version: "11.79.0",
4743
+ version: "11.80.0",
4744
4744
  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.",
4745
4745
  license: "MIT",
4746
4746
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -7012,6 +7012,78 @@ var init_account = __esm({
7012
7012
  }
7013
7013
  });
7014
7014
 
7015
+ // src/usage/accountDescription.ts
7016
+ function describeAccountOrganisation(account) {
7017
+ const parts = [account.organizationName, account.organizationType].filter((part) => part !== void 0);
7018
+ return parts.length === 0 ? void 0 : parts.join(", ");
7019
+ }
7020
+ var init_accountDescription = __esm({
7021
+ "src/usage/accountDescription.ts"() {
7022
+ "use strict";
7023
+ }
7024
+ });
7025
+
7026
+ // src/identityAccounts.ts
7027
+ function readIdentityAccount(fs14, identitiesDir, identity) {
7028
+ try {
7029
+ const account = readAccountMetadata(fs14, identitiesDir, identity);
7030
+ return account === void 0 ? void 0 : { account };
7031
+ } catch (error) {
7032
+ if (error instanceof AccountMetadataError) {
7033
+ return { problem: error.message };
7034
+ }
7035
+ throw error;
7036
+ }
7037
+ }
7038
+ function summariseIdentityAccount(account) {
7039
+ const summary = {
7040
+ ...account.emailAddress === void 0 ? {} : { emailAddress: account.emailAddress },
7041
+ ...account.organizationName === void 0 ? {} : { organizationName: account.organizationName },
7042
+ ...account.organizationType === void 0 ? {} : { organizationType: account.organizationType },
7043
+ ...account.organizationRole === void 0 ? {} : { organizationRole: account.organizationRole },
7044
+ ...account.billingType === void 0 ? {} : { billingType: account.billingType },
7045
+ ...account.seatTier === void 0 ? {} : { seatTier: account.seatTier }
7046
+ };
7047
+ return Object.keys(summary).length === 0 ? void 0 : summary;
7048
+ }
7049
+ function loginKeyOf(account) {
7050
+ return account.accountUuid === void 0 ? void 0 : JSON.stringify([account.accountUuid, account.organizationUuid ?? null]);
7051
+ }
7052
+ function collectByLogin(items, loginOf) {
7053
+ const groups = /* @__PURE__ */ new Map();
7054
+ for (const item of items) {
7055
+ const key = loginOf(item) ?? /* @__PURE__ */ Symbol("no login");
7056
+ const group = groups.get(key);
7057
+ if (group === void 0) {
7058
+ groups.set(key, [item]);
7059
+ } else {
7060
+ group.push(item);
7061
+ }
7062
+ }
7063
+ return [...groups.values()];
7064
+ }
7065
+ function groupSharedLogins(items, loginOf) {
7066
+ return collectByLogin(items, loginOf).filter((group) => group.length > 1);
7067
+ }
7068
+ function joinNames(names) {
7069
+ const last = names.at(-1);
7070
+ return names.length < 2 || last === void 0 ? names.join("") : `${names.slice(0, -1).join(", ")} and ${last}`;
7071
+ }
7072
+ function describeAccountHolder(summary) {
7073
+ const organisation = describeAccountOrganisation(summary);
7074
+ if (summary.emailAddress === void 0) {
7075
+ return organisation;
7076
+ }
7077
+ return organisation === void 0 ? summary.emailAddress : `${summary.emailAddress} (${organisation})`;
7078
+ }
7079
+ var init_identityAccounts = __esm({
7080
+ "src/identityAccounts.ts"() {
7081
+ "use strict";
7082
+ init_account();
7083
+ init_accountDescription();
7084
+ }
7085
+ });
7086
+
7015
7087
  // src/usage/rateLimit.ts
7016
7088
  function epochSecondsToIso(value) {
7017
7089
  if (value === void 0) {
@@ -7379,6 +7451,9 @@ function assessRefusedNested(member, nested, nowMs) {
7379
7451
  };
7380
7452
  }
7381
7453
  function assess(member, nowMs) {
7454
+ if (member.sameLoginAs !== void 0) {
7455
+ return { candidate: { identity: member.identity, class: "ineligible", plan: planOf(member.account), reasons: [`same login as ${member.sameLoginAs}`] } };
7456
+ }
7382
7457
  const nested = member.nested;
7383
7458
  if (nested === void 0) {
7384
7459
  return assessIdentity(member, nowMs);
@@ -7853,6 +7928,29 @@ function loadPoolMembers(fs14, paths, identities, nowMs) {
7853
7928
  }
7854
7929
  });
7855
7930
  }
7931
+ function snapshotAgeKey(member) {
7932
+ return member.snapshot === void 0 ? Number.NEGATIVE_INFINITY : Date.parse(member.snapshot.updatedAt);
7933
+ }
7934
+ function collapseSharedLogins(members) {
7935
+ const groups = groupSharedLogins(members, (member) => member.nested === void 0 && member.account !== void 0 ? loginKeyOf(member.account) : void 0);
7936
+ const representativeOf = /* @__PURE__ */ new Map();
7937
+ for (const group of groups) {
7938
+ const representative = group.reduce((freshest, member) => snapshotAgeKey(member) > snapshotAgeKey(freshest) ? member : freshest);
7939
+ for (const member of group) {
7940
+ if (member !== representative) {
7941
+ representativeOf.set(member, representative.identity);
7942
+ }
7943
+ }
7944
+ }
7945
+ return members.map((member) => {
7946
+ const representative = representativeOf.get(member);
7947
+ return representative === void 0 ? member : { ...member, sameLoginAs: representative };
7948
+ });
7949
+ }
7950
+ function stickyOnRepresentative(sticky, members) {
7951
+ const twin = sticky === void 0 ? void 0 : members.find((member) => member.identity === sticky.identity && member.sameLoginAs !== void 0);
7952
+ return sticky === void 0 || twin?.sameLoginAs === void 0 ? sticky : { ...sticky, identity: twin.sameLoginAs };
7953
+ }
7856
7954
  function rankPoolGraph(input) {
7857
7955
  return rankNamedPool(input, input.poolName, [], input.sticky);
7858
7956
  }
@@ -7880,13 +7978,15 @@ function rankNamedPool(input, poolName, stack, sticky) {
7880
7978
  members.push(typeof entry === "string" ? nested.member : { ...nested.member, policy: entry.when });
7881
7979
  missing.push(...nested.missing);
7882
7980
  }
7981
+ const collapsed = collapseSharedLogins(members);
7982
+ const stickyPick = stickyOnRepresentative(sticky, collapsed);
7883
7983
  const ranking = rankPool({
7884
- members,
7984
+ members: collapsed,
7885
7985
  nowMs: input.nowMs,
7886
7986
  resuming: input.resuming,
7887
7987
  ...input.resumeContextTokens === void 0 ? {} : { resumeContextTokens: input.resumeContextTokens },
7888
7988
  ...input.committedContextTokens === void 0 ? {} : { committedContextTokens: input.committedContextTokens },
7889
- ...sticky === void 0 ? {} : { sticky },
7989
+ ...stickyPick === void 0 ? {} : { sticky: stickyPick },
7890
7990
  ...pool.preference === void 0 ? {} : { preference: pool.preference }
7891
7991
  });
7892
7992
  return { ranking, missing };
@@ -7972,6 +8072,7 @@ var init_poolPick = __esm({
7972
8072
  init_cliError();
7973
8073
  init_identity();
7974
8074
  init_schema();
8075
+ init_identityAccounts();
7975
8076
  init_account();
7976
8077
  init_pick();
7977
8078
  init_read();
@@ -16669,65 +16770,7 @@ init_realPorts();
16669
16770
  import fs11 from "node:fs";
16670
16771
  import path29 from "node:path";
16671
16772
  init_signIn();
16672
-
16673
- // src/identityAccounts.ts
16674
- init_account();
16675
-
16676
- // src/usage/accountDescription.ts
16677
- function describeAccountOrganisation(account) {
16678
- const parts = [account.organizationName, account.organizationType].filter((part) => part !== void 0);
16679
- return parts.length === 0 ? void 0 : parts.join(", ");
16680
- }
16681
-
16682
- // src/identityAccounts.ts
16683
- function readIdentityAccount(fs14, identitiesDir, identity) {
16684
- try {
16685
- const account = readAccountMetadata(fs14, identitiesDir, identity);
16686
- return account === void 0 ? void 0 : { account };
16687
- } catch (error) {
16688
- if (error instanceof AccountMetadataError) {
16689
- return { problem: error.message };
16690
- }
16691
- throw error;
16692
- }
16693
- }
16694
- function summariseIdentityAccount(account) {
16695
- const summary = {
16696
- ...account.emailAddress === void 0 ? {} : { emailAddress: account.emailAddress },
16697
- ...account.organizationName === void 0 ? {} : { organizationName: account.organizationName },
16698
- ...account.organizationType === void 0 ? {} : { organizationType: account.organizationType },
16699
- ...account.organizationRole === void 0 ? {} : { organizationRole: account.organizationRole },
16700
- ...account.billingType === void 0 ? {} : { billingType: account.billingType },
16701
- ...account.seatTier === void 0 ? {} : { seatTier: account.seatTier }
16702
- };
16703
- return Object.keys(summary).length === 0 ? void 0 : summary;
16704
- }
16705
- function loginKeyOf(account) {
16706
- return account.accountUuid === void 0 ? void 0 : JSON.stringify([account.accountUuid, account.organizationUuid ?? null]);
16707
- }
16708
- function collectByLogin(items, loginOf) {
16709
- const groups = /* @__PURE__ */ new Map();
16710
- for (const item of items) {
16711
- const key = loginOf(item) ?? /* @__PURE__ */ Symbol("no login");
16712
- const group = groups.get(key);
16713
- if (group === void 0) {
16714
- groups.set(key, [item]);
16715
- } else {
16716
- group.push(item);
16717
- }
16718
- }
16719
- return [...groups.values()];
16720
- }
16721
- function groupSharedLogins(items, loginOf) {
16722
- return collectByLogin(items, loginOf).filter((group) => group.length > 1);
16723
- }
16724
- function describeAccountHolder(summary) {
16725
- const organisation = describeAccountOrganisation(summary);
16726
- if (summary.emailAddress === void 0) {
16727
- return organisation;
16728
- }
16729
- return organisation === void 0 ? summary.emailAddress : `${summary.emailAddress} (${organisation})`;
16730
- }
16773
+ init_identityAccounts();
16731
16774
 
16732
16775
  // src/codex/siwcStore.ts
16733
16776
  import { z as z26 } from "zod";
@@ -17081,6 +17124,27 @@ init_providerAliases();
17081
17124
  init_providersStore();
17082
17125
  init_guard();
17083
17126
  init_identity();
17127
+
17128
+ // src/poolSharedLogins.ts
17129
+ init_identityAccounts();
17130
+ init_identity();
17131
+ function sharedLoginMembers(members, accountOf) {
17132
+ const known = members.flatMap((identity) => {
17133
+ const account = poolNameOf(identity) === void 0 ? accountOf(identity) : void 0;
17134
+ return account === void 0 ? [] : [{ identity, account }];
17135
+ });
17136
+ return groupSharedLogins(known, (entry) => loginKeyOf(entry.account)).flatMap((group) => {
17137
+ const first = group[0];
17138
+ return first === void 0 ? [] : [{ members: group.map((entry) => entry.identity), account: first.account }];
17139
+ });
17140
+ }
17141
+ function describeSharedLoginMembers(poolName, shared) {
17142
+ const summary = summariseIdentityAccount(shared.account);
17143
+ const holder = summary === void 0 ? void 0 : describeAccountHolder(summary);
17144
+ return `Pool "${poolName}" lists ${joinNames(shared.members)}, which hold the same login${holder === void 0 ? "" : ` (${holder})`}, so they share one quota window. A launch ranks them as one candidate and never fails over from one to another; keep one of them in the pool.`;
17145
+ }
17146
+
17147
+ // src/doctorReport.ts
17084
17148
  init_extends();
17085
17149
  var DOCTOR_SEVERITIES = ["pass", "warn", "fail"];
17086
17150
  var DOCTOR_SECTIONS = [
@@ -17103,10 +17167,6 @@ var DOCTOR_SECTIONS = [
17103
17167
  "frontdoor",
17104
17168
  "models"
17105
17169
  ];
17106
- function joinNames(names) {
17107
- const last = names.at(-1);
17108
- return names.length < 2 || last === void 0 ? names.join("") : `${names.slice(0, -1).join(", ")} and ${last}`;
17109
- }
17110
17170
  function refinePathShadow(status, ownExecutablePath, realpath) {
17111
17171
  if (status.status !== "shadowed") {
17112
17172
  return status;
@@ -17419,6 +17479,7 @@ function runDoctor(params) {
17419
17479
  }
17420
17480
  const validatedGlobalConfig = params.globalConfig.raw === void 0 ? void 0 : validateJson(GlobalConfigSchema, params.globalConfig);
17421
17481
  const pools = validatedGlobalConfig?.ok === true ? validatedGlobalConfig.data.pools ?? {} : {};
17482
+ const accountByIdentity = new Map(params.identities.flatMap((entry) => validIdentityNames.has(entry.name) && entry.account !== void 0 ? [[entry.name, entry.account]] : []));
17422
17483
  for (const [poolName, pool] of Object.entries(pools)) {
17423
17484
  const missing = pool.identities.filter((member) => poolNameOf(poolMemberIdentity(member)) === void 0 && !validIdentityNames.has(poolMemberIdentity(member)));
17424
17485
  const undefinedPools = pool.identities.flatMap((member) => {
@@ -17434,6 +17495,9 @@ function runDoctor(params) {
17434
17495
  ];
17435
17496
  push("pool", "fail", `Pool "${poolName}" names ${problems.join(" and ")}.`, poolName);
17436
17497
  }
17498
+ for (const shared of sharedLoginMembers(pool.identities.map(poolMemberIdentity), (identityName) => accountByIdentity.get(identityName))) {
17499
+ push("pool", "warn", describeSharedLoginMembers(poolName, shared), poolName);
17500
+ }
17437
17501
  }
17438
17502
  const reportedCycles = /* @__PURE__ */ new Set();
17439
17503
  for (const poolName of Object.keys(pools).sort()) {
@@ -26336,6 +26336,8 @@ interface PoolMember {
26336
26336
  readonly records: readonly UsageRecord[];
26337
26337
  /** Present when this member stands for a nested `pool:<name>` entry rather than a direct identity. */
26338
26338
  readonly nested?: NestedContribution;
26339
+ /** The member that represents this member's login in the ranking, when both hold the same login and so draw on one quota window. This member is then ineligible as a candidate of its own, since ranking it beside its representative would count one window twice and could fail over to a login that is limited too. */
26340
+ readonly sameLoginAs?: string;
26339
26341
  /** The member entry's policy condition, from the object form of a pool member: definite false skips the member as ineligible by policy, indeterminate demotes it to last within its class, naming the missing fact either way. */
26340
26342
  readonly policy?: WhenCondition;
26341
26343
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-shim",
3
- "version": "11.79.0",
3
+ "version": "11.80.0",
4
4
  "description": "A profile manager and launcher for Claude Code that lets one person run multiple logins from one machine while controlling what gets shared between them.",
5
5
  "license": "MIT",
6
6
  "author": "Joseph Mearman <joseph@mearman.co.uk>",