agent-shim 8.31.0 → 8.32.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/cli.cjs +38 -13
- package/dist/index.cjs +38 -13
- package/dist/index.mjs +38 -13
- package/dist/types/index.d.ts +31 -2
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -61796,7 +61796,7 @@ function registerIdentityCommand(program2, deps) {
|
|
|
61796
61796
|
// package.json
|
|
61797
61797
|
var package_default = {
|
|
61798
61798
|
name: "agent-shim",
|
|
61799
|
-
version: "8.
|
|
61799
|
+
version: "8.32.0",
|
|
61800
61800
|
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.",
|
|
61801
61801
|
license: "MIT",
|
|
61802
61802
|
author: "Joseph Mearman <joseph@mearman.co.uk>",
|
|
@@ -62775,10 +62775,10 @@ function windowBlockedUntil(windows) {
|
|
|
62775
62775
|
const resets = windows.flatMap((window) => window.status === "rejected" && window.resetsAtMs !== void 0 ? [window.resetsAtMs] : []);
|
|
62776
62776
|
return resets.length === 0 ? void 0 : Math.max(...resets);
|
|
62777
62777
|
}
|
|
62778
|
-
function
|
|
62778
|
+
function windowBurn(records, which) {
|
|
62779
62779
|
const points = [];
|
|
62780
62780
|
for (const record2 of records) {
|
|
62781
|
-
const window = record2.rateLimitHeaders === void 0 ? void 0 : parseUnifiedRateLimit(record2.rateLimitHeaders)?.
|
|
62781
|
+
const window = record2.rateLimitHeaders === void 0 ? void 0 : parseUnifiedRateLimit(record2.rateLimitHeaders)?.[which];
|
|
62782
62782
|
if (window?.utilization !== void 0 && window.resetsAt !== void 0) {
|
|
62783
62783
|
points.push({ resetsAt: window.resetsAt, point: { atMs: Date.parse(record2.at), utilization: window.utilization } });
|
|
62784
62784
|
}
|
|
@@ -62872,11 +62872,16 @@ function assessIdentity(member, nowMs) {
|
|
|
62872
62872
|
const score = remaining * plan.capacity / (untilResetMs / MS_PER_HOUR);
|
|
62873
62873
|
const fiveRemaining = five?.utilization === void 0 ? void 0 : Math.max(0, 1 - five.utilization);
|
|
62874
62874
|
const fiveUntil = five?.resetsAtMs === void 0 ? FIVE_HOUR_WINDOW_MS : Math.max(five.resetsAtMs - nowMs, MS_PER_SECOND3);
|
|
62875
|
-
const burn =
|
|
62875
|
+
const burn = windowBurn(member.records, "fiveHour");
|
|
62876
|
+
const sevenRemaining = seven?.utilization === void 0 ? void 0 : Math.max(0, 1 - seven.utilization);
|
|
62877
|
+
const sevenUntil = seven?.resetsAtMs === void 0 ? SEVEN_DAY_WINDOW_MS : Math.max(seven.resetsAtMs - nowMs, MS_PER_SECOND3);
|
|
62878
|
+
const sevenDayBurn = windowBurn(member.records, "sevenDay");
|
|
62876
62879
|
return {
|
|
62877
62880
|
candidate: { ...base, class: "scored", score, reasons: [...windowReasons.map((reason, index) => index === windowReasons.length - 1 ? `${reason}${observedAgo}` : reason), ...planReasons] },
|
|
62878
62881
|
...fiveRemaining === void 0 ? {} : { fiveHour: { remaining: fiveRemaining, untilResetMs: fiveUntil } },
|
|
62879
|
-
...burn === void 0 ? {} : { burn }
|
|
62882
|
+
...burn === void 0 ? {} : { burn },
|
|
62883
|
+
...sevenRemaining === void 0 ? {} : { sevenDay: { remaining: sevenRemaining, untilResetMs: sevenUntil } },
|
|
62884
|
+
...sevenDayBurn === void 0 ? {} : { sevenDayBurn }
|
|
62880
62885
|
};
|
|
62881
62886
|
}
|
|
62882
62887
|
function rankPool(input2) {
|
|
@@ -62887,14 +62892,27 @@ function rankPool(input2) {
|
|
|
62887
62892
|
const candidates = assessments.map((assessment) => {
|
|
62888
62893
|
const { candidate, fiveHour, burn } = assessment;
|
|
62889
62894
|
const pace = burn ?? (sharedDemand === void 0 ? void 0 : sharedDemand / capacityOf(candidate.plan));
|
|
62890
|
-
|
|
62895
|
+
const { sevenDay, sevenDayBurn } = assessment;
|
|
62896
|
+
if (candidate.class !== "scored") {
|
|
62891
62897
|
return { ...candidate, feasible: true };
|
|
62892
62898
|
}
|
|
62893
|
-
const
|
|
62894
|
-
if (
|
|
62899
|
+
const drySpots = [];
|
|
62900
|
+
if (fiveHour !== void 0 && pace !== void 0) {
|
|
62901
|
+
const runsDryInMs = fiveHour.remaining / pace;
|
|
62902
|
+
if (runsDryInMs < fiveHour.untilResetMs) {
|
|
62903
|
+
drySpots.push(`5h window would run dry in ${formatAge(runsDryInMs)}, before it resets in ${formatAge(fiveHour.untilResetMs)}`);
|
|
62904
|
+
}
|
|
62905
|
+
}
|
|
62906
|
+
if (sevenDay !== void 0 && sevenDayBurn !== void 0) {
|
|
62907
|
+
const runsDryInMs = sevenDay.remaining / sevenDayBurn;
|
|
62908
|
+
if (runsDryInMs < sevenDay.untilResetMs) {
|
|
62909
|
+
drySpots.push(`7d window would run dry in ${formatAge(runsDryInMs)}, before it resets in ${formatAge(sevenDay.untilResetMs)}`);
|
|
62910
|
+
}
|
|
62911
|
+
}
|
|
62912
|
+
if (drySpots.length === 0) {
|
|
62895
62913
|
return { ...candidate, feasible: true };
|
|
62896
62914
|
}
|
|
62897
|
-
return { ...candidate, feasible: false, reasons: [
|
|
62915
|
+
return { ...candidate, feasible: false, reasons: [...drySpots, ...candidate.reasons] };
|
|
62898
62916
|
});
|
|
62899
62917
|
const ordered = input2.preference === "listed" ? [...candidates] : [...candidates].sort(
|
|
62900
62918
|
(left, right) => CLASS_ORDER[left.class] - CLASS_ORDER[right.class] || Number(right.feasible) - Number(left.feasible) || (right.score ?? 0) - (left.score ?? 0) || left.identity.localeCompare(right.identity)
|
|
@@ -62902,7 +62920,8 @@ function rankPool(input2) {
|
|
|
62902
62920
|
const sticky = input2.sticky;
|
|
62903
62921
|
const stickyIndex = sticky === void 0 ? -1 : ordered.findIndex((candidate) => candidate.identity === sticky.identity);
|
|
62904
62922
|
const stickyCandidate = ordered[stickyIndex];
|
|
62905
|
-
|
|
62923
|
+
const keepSticky = stickyCandidate?.class === "scored" && stickyCandidate.feasible;
|
|
62924
|
+
if (sticky !== void 0 && keepSticky) {
|
|
62906
62925
|
const ageMs = nowMs - Date.parse(sticky.at);
|
|
62907
62926
|
const warm = ageMs <= PROMPT_CACHE_TTL_MS;
|
|
62908
62927
|
if (input2.resuming || warm) {
|
|
@@ -62911,12 +62930,14 @@ function rankPool(input2) {
|
|
|
62911
62930
|
ordered.unshift({ ...stickyCandidate, reasons: [why, ...stickyCandidate.reasons] });
|
|
62912
62931
|
}
|
|
62913
62932
|
}
|
|
62933
|
+
const movedOff = sticky !== void 0 && input2.resuming && !keepSticky ? { identity: sticky.identity, reason: stickyCandidate?.reasons[0] ?? "no longer among the pool's members" } : void 0;
|
|
62914
62934
|
const pick2 = ordered.find((candidate) => candidate.class !== "ineligible");
|
|
62915
62935
|
const returns = ordered.flatMap((candidate) => candidate.blockedUntilMs === void 0 ? [] : [{ identity: candidate.identity, atMs: candidate.blockedUntilMs }]);
|
|
62916
62936
|
const earliestReturn = returns.length === 0 ? void 0 : returns.reduce((soonest, entry) => entry.atMs < soonest.atMs ? entry : soonest);
|
|
62917
62937
|
return {
|
|
62918
62938
|
candidates: ordered,
|
|
62919
|
-
...pick2 === void 0 ? { ...earliestReturn === void 0 ? {} : { earliestReturn } } : { pick: pick2 }
|
|
62939
|
+
...pick2 === void 0 ? { ...earliestReturn === void 0 ? {} : { earliestReturn } } : { pick: pick2 },
|
|
62940
|
+
...movedOff === void 0 || pick2 === void 0 ? {} : { movedOff }
|
|
62920
62941
|
};
|
|
62921
62942
|
}
|
|
62922
62943
|
|
|
@@ -63332,6 +63353,7 @@ function collectPoolPick(params) {
|
|
|
63332
63353
|
candidates: ranking.candidates.map(candidateView),
|
|
63333
63354
|
missing: missing.map(({ identity }) => identity),
|
|
63334
63355
|
...ranking.earliestReturn === void 0 ? {} : { earliestReturn: { identity: ranking.earliestReturn.identity, at: new Date(ranking.earliestReturn.atMs).toISOString() } },
|
|
63356
|
+
...ranking.movedOff === void 0 ? {} : { movedOff: ranking.movedOff },
|
|
63335
63357
|
...sticky.problem === void 0 ? {} : { stickyProblem: sticky.problem }
|
|
63336
63358
|
};
|
|
63337
63359
|
}
|
|
@@ -69611,6 +69633,8 @@ var PoolPickReportSchema = external_exports.strictObject({
|
|
|
69611
69633
|
missing: external_exports.array(external_exports.string()),
|
|
69612
69634
|
/** When nothing can be picked, the soonest any member returns. */
|
|
69613
69635
|
earliestReturn: external_exports.strictObject({ identity: external_exports.string(), at: external_exports.iso.datetime() }).optional(),
|
|
69636
|
+
/** The member a resumed conversation started on but moved off, and why, when the ranking could not keep it. */
|
|
69637
|
+
movedOff: external_exports.strictObject({ identity: external_exports.string(), reason: external_exports.string() }).optional(),
|
|
69614
69638
|
/** Why the last-pick record could not be read, when that is the case. */
|
|
69615
69639
|
stickyProblem: external_exports.string().optional()
|
|
69616
69640
|
});
|
|
@@ -81187,10 +81211,11 @@ function resolvePoolLaunch(params) {
|
|
|
81187
81211
|
if (graph.ranking.candidates.length === 0) {
|
|
81188
81212
|
return { ok: false, message: `agent-shim: no member of pool "${poolName}" is an existing identity.` };
|
|
81189
81213
|
}
|
|
81190
|
-
const { pick: pick2, earliestReturn } = graph.ranking;
|
|
81214
|
+
const { pick: pick2, earliestReturn, movedOff } = graph.ranking;
|
|
81191
81215
|
if (pick2 !== void 0) {
|
|
81192
81216
|
recordStickyPick(params.usageFs, params.paths.usagePicksFile, params.cwd, pick2.identity, nowMs);
|
|
81193
|
-
|
|
81217
|
+
const move = movedOff === void 0 ? "" : `moved off ${movedOff.identity} (${movedOff.reason}); `;
|
|
81218
|
+
return { ok: true, identity: pick2.identity, explanation: `pool ${poolName}: ${move}${pick2.reasons.slice(0, REASONS_LOGGED).join("; ")}` };
|
|
81194
81219
|
}
|
|
81195
81220
|
if (earliestReturn === void 0) {
|
|
81196
81221
|
return { ok: false, message: `agent-shim: no member of pool "${poolName}" can be picked.` };
|
package/dist/index.cjs
CHANGED
|
@@ -5355,7 +5355,7 @@ var import_undici = require("undici");
|
|
|
5355
5355
|
// package.json
|
|
5356
5356
|
var package_default = {
|
|
5357
5357
|
name: "agent-shim",
|
|
5358
|
-
version: "8.
|
|
5358
|
+
version: "8.32.0",
|
|
5359
5359
|
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.",
|
|
5360
5360
|
license: "MIT",
|
|
5361
5361
|
author: "Joseph Mearman <joseph@mearman.co.uk>",
|
|
@@ -6537,10 +6537,10 @@ function windowBlockedUntil(windows) {
|
|
|
6537
6537
|
const resets = windows.flatMap((window) => window.status === "rejected" && window.resetsAtMs !== void 0 ? [window.resetsAtMs] : []);
|
|
6538
6538
|
return resets.length === 0 ? void 0 : Math.max(...resets);
|
|
6539
6539
|
}
|
|
6540
|
-
function
|
|
6540
|
+
function windowBurn(records, which) {
|
|
6541
6541
|
const points = [];
|
|
6542
6542
|
for (const record of records) {
|
|
6543
|
-
const window = record.rateLimitHeaders === void 0 ? void 0 : parseUnifiedRateLimit(record.rateLimitHeaders)?.
|
|
6543
|
+
const window = record.rateLimitHeaders === void 0 ? void 0 : parseUnifiedRateLimit(record.rateLimitHeaders)?.[which];
|
|
6544
6544
|
if (window?.utilization !== void 0 && window.resetsAt !== void 0) {
|
|
6545
6545
|
points.push({ resetsAt: window.resetsAt, point: { atMs: Date.parse(record.at), utilization: window.utilization } });
|
|
6546
6546
|
}
|
|
@@ -6634,11 +6634,16 @@ function assessIdentity(member, nowMs) {
|
|
|
6634
6634
|
const score = remaining * plan.capacity / (untilResetMs / MS_PER_HOUR);
|
|
6635
6635
|
const fiveRemaining = five?.utilization === void 0 ? void 0 : Math.max(0, 1 - five.utilization);
|
|
6636
6636
|
const fiveUntil = five?.resetsAtMs === void 0 ? FIVE_HOUR_WINDOW_MS : Math.max(five.resetsAtMs - nowMs, MS_PER_SECOND4);
|
|
6637
|
-
const burn =
|
|
6637
|
+
const burn = windowBurn(member.records, "fiveHour");
|
|
6638
|
+
const sevenRemaining = seven?.utilization === void 0 ? void 0 : Math.max(0, 1 - seven.utilization);
|
|
6639
|
+
const sevenUntil = seven?.resetsAtMs === void 0 ? SEVEN_DAY_WINDOW_MS : Math.max(seven.resetsAtMs - nowMs, MS_PER_SECOND4);
|
|
6640
|
+
const sevenDayBurn = windowBurn(member.records, "sevenDay");
|
|
6638
6641
|
return {
|
|
6639
6642
|
candidate: { ...base, class: "scored", score, reasons: [...windowReasons.map((reason, index) => index === windowReasons.length - 1 ? `${reason}${observedAgo}` : reason), ...planReasons] },
|
|
6640
6643
|
...fiveRemaining === void 0 ? {} : { fiveHour: { remaining: fiveRemaining, untilResetMs: fiveUntil } },
|
|
6641
|
-
...burn === void 0 ? {} : { burn }
|
|
6644
|
+
...burn === void 0 ? {} : { burn },
|
|
6645
|
+
...sevenRemaining === void 0 ? {} : { sevenDay: { remaining: sevenRemaining, untilResetMs: sevenUntil } },
|
|
6646
|
+
...sevenDayBurn === void 0 ? {} : { sevenDayBurn }
|
|
6642
6647
|
};
|
|
6643
6648
|
}
|
|
6644
6649
|
function rankPool(input) {
|
|
@@ -6649,14 +6654,27 @@ function rankPool(input) {
|
|
|
6649
6654
|
const candidates = assessments.map((assessment) => {
|
|
6650
6655
|
const { candidate, fiveHour, burn } = assessment;
|
|
6651
6656
|
const pace = burn ?? (sharedDemand === void 0 ? void 0 : sharedDemand / capacityOf(candidate.plan));
|
|
6652
|
-
|
|
6657
|
+
const { sevenDay, sevenDayBurn } = assessment;
|
|
6658
|
+
if (candidate.class !== "scored") {
|
|
6653
6659
|
return { ...candidate, feasible: true };
|
|
6654
6660
|
}
|
|
6655
|
-
const
|
|
6656
|
-
if (
|
|
6661
|
+
const drySpots = [];
|
|
6662
|
+
if (fiveHour !== void 0 && pace !== void 0) {
|
|
6663
|
+
const runsDryInMs = fiveHour.remaining / pace;
|
|
6664
|
+
if (runsDryInMs < fiveHour.untilResetMs) {
|
|
6665
|
+
drySpots.push(`5h window would run dry in ${formatAge(runsDryInMs)}, before it resets in ${formatAge(fiveHour.untilResetMs)}`);
|
|
6666
|
+
}
|
|
6667
|
+
}
|
|
6668
|
+
if (sevenDay !== void 0 && sevenDayBurn !== void 0) {
|
|
6669
|
+
const runsDryInMs = sevenDay.remaining / sevenDayBurn;
|
|
6670
|
+
if (runsDryInMs < sevenDay.untilResetMs) {
|
|
6671
|
+
drySpots.push(`7d window would run dry in ${formatAge(runsDryInMs)}, before it resets in ${formatAge(sevenDay.untilResetMs)}`);
|
|
6672
|
+
}
|
|
6673
|
+
}
|
|
6674
|
+
if (drySpots.length === 0) {
|
|
6657
6675
|
return { ...candidate, feasible: true };
|
|
6658
6676
|
}
|
|
6659
|
-
return { ...candidate, feasible: false, reasons: [
|
|
6677
|
+
return { ...candidate, feasible: false, reasons: [...drySpots, ...candidate.reasons] };
|
|
6660
6678
|
});
|
|
6661
6679
|
const ordered = input.preference === "listed" ? [...candidates] : [...candidates].sort(
|
|
6662
6680
|
(left, right) => CLASS_ORDER[left.class] - CLASS_ORDER[right.class] || Number(right.feasible) - Number(left.feasible) || (right.score ?? 0) - (left.score ?? 0) || left.identity.localeCompare(right.identity)
|
|
@@ -6664,7 +6682,8 @@ function rankPool(input) {
|
|
|
6664
6682
|
const sticky = input.sticky;
|
|
6665
6683
|
const stickyIndex = sticky === void 0 ? -1 : ordered.findIndex((candidate) => candidate.identity === sticky.identity);
|
|
6666
6684
|
const stickyCandidate = ordered[stickyIndex];
|
|
6667
|
-
|
|
6685
|
+
const keepSticky = stickyCandidate?.class === "scored" && stickyCandidate.feasible;
|
|
6686
|
+
if (sticky !== void 0 && keepSticky) {
|
|
6668
6687
|
const ageMs = nowMs - Date.parse(sticky.at);
|
|
6669
6688
|
const warm = ageMs <= PROMPT_CACHE_TTL_MS;
|
|
6670
6689
|
if (input.resuming || warm) {
|
|
@@ -6673,12 +6692,14 @@ function rankPool(input) {
|
|
|
6673
6692
|
ordered.unshift({ ...stickyCandidate, reasons: [why, ...stickyCandidate.reasons] });
|
|
6674
6693
|
}
|
|
6675
6694
|
}
|
|
6695
|
+
const movedOff = sticky !== void 0 && input.resuming && !keepSticky ? { identity: sticky.identity, reason: stickyCandidate?.reasons[0] ?? "no longer among the pool's members" } : void 0;
|
|
6676
6696
|
const pick = ordered.find((candidate) => candidate.class !== "ineligible");
|
|
6677
6697
|
const returns = ordered.flatMap((candidate) => candidate.blockedUntilMs === void 0 ? [] : [{ identity: candidate.identity, atMs: candidate.blockedUntilMs }]);
|
|
6678
6698
|
const earliestReturn = returns.length === 0 ? void 0 : returns.reduce((soonest, entry) => entry.atMs < soonest.atMs ? entry : soonest);
|
|
6679
6699
|
return {
|
|
6680
6700
|
candidates: ordered,
|
|
6681
|
-
...pick === void 0 ? { ...earliestReturn === void 0 ? {} : { earliestReturn } } : { pick }
|
|
6701
|
+
...pick === void 0 ? { ...earliestReturn === void 0 ? {} : { earliestReturn } } : { pick },
|
|
6702
|
+
...movedOff === void 0 || pick === void 0 ? {} : { movedOff }
|
|
6682
6703
|
};
|
|
6683
6704
|
}
|
|
6684
6705
|
|
|
@@ -6936,6 +6957,7 @@ function collectPoolPick(params) {
|
|
|
6936
6957
|
candidates: ranking.candidates.map(candidateView),
|
|
6937
6958
|
missing: missing.map(({ identity }) => identity),
|
|
6938
6959
|
...ranking.earliestReturn === void 0 ? {} : { earliestReturn: { identity: ranking.earliestReturn.identity, at: new Date(ranking.earliestReturn.atMs).toISOString() } },
|
|
6960
|
+
...ranking.movedOff === void 0 ? {} : { movedOff: ranking.movedOff },
|
|
6939
6961
|
...sticky.problem === void 0 ? {} : { stickyProblem: sticky.problem }
|
|
6940
6962
|
};
|
|
6941
6963
|
}
|
|
@@ -9207,6 +9229,8 @@ var PoolPickReportSchema = import_zod13.z.strictObject({
|
|
|
9207
9229
|
missing: import_zod13.z.array(import_zod13.z.string()),
|
|
9208
9230
|
/** When nothing can be picked, the soonest any member returns. */
|
|
9209
9231
|
earliestReturn: import_zod13.z.strictObject({ identity: import_zod13.z.string(), at: import_zod13.z.iso.datetime() }).optional(),
|
|
9232
|
+
/** The member a resumed conversation started on but moved off, and why, when the ranking could not keep it. */
|
|
9233
|
+
movedOff: import_zod13.z.strictObject({ identity: import_zod13.z.string(), reason: import_zod13.z.string() }).optional(),
|
|
9210
9234
|
/** Why the last-pick record could not be read, when that is the case. */
|
|
9211
9235
|
stickyProblem: import_zod13.z.string().optional()
|
|
9212
9236
|
});
|
|
@@ -12953,10 +12977,11 @@ function resolvePoolLaunch(params) {
|
|
|
12953
12977
|
if (graph.ranking.candidates.length === 0) {
|
|
12954
12978
|
return { ok: false, message: `agent-shim: no member of pool "${poolName}" is an existing identity.` };
|
|
12955
12979
|
}
|
|
12956
|
-
const { pick, earliestReturn } = graph.ranking;
|
|
12980
|
+
const { pick, earliestReturn, movedOff } = graph.ranking;
|
|
12957
12981
|
if (pick !== void 0) {
|
|
12958
12982
|
recordStickyPick(params.usageFs, params.paths.usagePicksFile, params.cwd, pick.identity, nowMs);
|
|
12959
|
-
|
|
12983
|
+
const move = movedOff === void 0 ? "" : `moved off ${movedOff.identity} (${movedOff.reason}); `;
|
|
12984
|
+
return { ok: true, identity: pick.identity, explanation: `pool ${poolName}: ${move}${pick.reasons.slice(0, REASONS_LOGGED).join("; ")}` };
|
|
12960
12985
|
}
|
|
12961
12986
|
if (earliestReturn === void 0) {
|
|
12962
12987
|
return { ok: false, message: `agent-shim: no member of pool "${poolName}" can be picked.` };
|
package/dist/index.mjs
CHANGED
|
@@ -5043,7 +5043,7 @@ import { Agent as Agent3, fetch as undiciFetch } from "undici";
|
|
|
5043
5043
|
// package.json
|
|
5044
5044
|
var package_default = {
|
|
5045
5045
|
name: "agent-shim",
|
|
5046
|
-
version: "8.
|
|
5046
|
+
version: "8.32.0",
|
|
5047
5047
|
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.",
|
|
5048
5048
|
license: "MIT",
|
|
5049
5049
|
author: "Joseph Mearman <joseph@mearman.co.uk>",
|
|
@@ -6225,10 +6225,10 @@ function windowBlockedUntil(windows) {
|
|
|
6225
6225
|
const resets = windows.flatMap((window) => window.status === "rejected" && window.resetsAtMs !== void 0 ? [window.resetsAtMs] : []);
|
|
6226
6226
|
return resets.length === 0 ? void 0 : Math.max(...resets);
|
|
6227
6227
|
}
|
|
6228
|
-
function
|
|
6228
|
+
function windowBurn(records, which) {
|
|
6229
6229
|
const points = [];
|
|
6230
6230
|
for (const record of records) {
|
|
6231
|
-
const window = record.rateLimitHeaders === void 0 ? void 0 : parseUnifiedRateLimit(record.rateLimitHeaders)?.
|
|
6231
|
+
const window = record.rateLimitHeaders === void 0 ? void 0 : parseUnifiedRateLimit(record.rateLimitHeaders)?.[which];
|
|
6232
6232
|
if (window?.utilization !== void 0 && window.resetsAt !== void 0) {
|
|
6233
6233
|
points.push({ resetsAt: window.resetsAt, point: { atMs: Date.parse(record.at), utilization: window.utilization } });
|
|
6234
6234
|
}
|
|
@@ -6322,11 +6322,16 @@ function assessIdentity(member, nowMs) {
|
|
|
6322
6322
|
const score = remaining * plan.capacity / (untilResetMs / MS_PER_HOUR);
|
|
6323
6323
|
const fiveRemaining = five?.utilization === void 0 ? void 0 : Math.max(0, 1 - five.utilization);
|
|
6324
6324
|
const fiveUntil = five?.resetsAtMs === void 0 ? FIVE_HOUR_WINDOW_MS : Math.max(five.resetsAtMs - nowMs, MS_PER_SECOND4);
|
|
6325
|
-
const burn =
|
|
6325
|
+
const burn = windowBurn(member.records, "fiveHour");
|
|
6326
|
+
const sevenRemaining = seven?.utilization === void 0 ? void 0 : Math.max(0, 1 - seven.utilization);
|
|
6327
|
+
const sevenUntil = seven?.resetsAtMs === void 0 ? SEVEN_DAY_WINDOW_MS : Math.max(seven.resetsAtMs - nowMs, MS_PER_SECOND4);
|
|
6328
|
+
const sevenDayBurn = windowBurn(member.records, "sevenDay");
|
|
6326
6329
|
return {
|
|
6327
6330
|
candidate: { ...base, class: "scored", score, reasons: [...windowReasons.map((reason, index) => index === windowReasons.length - 1 ? `${reason}${observedAgo}` : reason), ...planReasons] },
|
|
6328
6331
|
...fiveRemaining === void 0 ? {} : { fiveHour: { remaining: fiveRemaining, untilResetMs: fiveUntil } },
|
|
6329
|
-
...burn === void 0 ? {} : { burn }
|
|
6332
|
+
...burn === void 0 ? {} : { burn },
|
|
6333
|
+
...sevenRemaining === void 0 ? {} : { sevenDay: { remaining: sevenRemaining, untilResetMs: sevenUntil } },
|
|
6334
|
+
...sevenDayBurn === void 0 ? {} : { sevenDayBurn }
|
|
6330
6335
|
};
|
|
6331
6336
|
}
|
|
6332
6337
|
function rankPool(input) {
|
|
@@ -6337,14 +6342,27 @@ function rankPool(input) {
|
|
|
6337
6342
|
const candidates = assessments.map((assessment) => {
|
|
6338
6343
|
const { candidate, fiveHour, burn } = assessment;
|
|
6339
6344
|
const pace = burn ?? (sharedDemand === void 0 ? void 0 : sharedDemand / capacityOf(candidate.plan));
|
|
6340
|
-
|
|
6345
|
+
const { sevenDay, sevenDayBurn } = assessment;
|
|
6346
|
+
if (candidate.class !== "scored") {
|
|
6341
6347
|
return { ...candidate, feasible: true };
|
|
6342
6348
|
}
|
|
6343
|
-
const
|
|
6344
|
-
if (
|
|
6349
|
+
const drySpots = [];
|
|
6350
|
+
if (fiveHour !== void 0 && pace !== void 0) {
|
|
6351
|
+
const runsDryInMs = fiveHour.remaining / pace;
|
|
6352
|
+
if (runsDryInMs < fiveHour.untilResetMs) {
|
|
6353
|
+
drySpots.push(`5h window would run dry in ${formatAge(runsDryInMs)}, before it resets in ${formatAge(fiveHour.untilResetMs)}`);
|
|
6354
|
+
}
|
|
6355
|
+
}
|
|
6356
|
+
if (sevenDay !== void 0 && sevenDayBurn !== void 0) {
|
|
6357
|
+
const runsDryInMs = sevenDay.remaining / sevenDayBurn;
|
|
6358
|
+
if (runsDryInMs < sevenDay.untilResetMs) {
|
|
6359
|
+
drySpots.push(`7d window would run dry in ${formatAge(runsDryInMs)}, before it resets in ${formatAge(sevenDay.untilResetMs)}`);
|
|
6360
|
+
}
|
|
6361
|
+
}
|
|
6362
|
+
if (drySpots.length === 0) {
|
|
6345
6363
|
return { ...candidate, feasible: true };
|
|
6346
6364
|
}
|
|
6347
|
-
return { ...candidate, feasible: false, reasons: [
|
|
6365
|
+
return { ...candidate, feasible: false, reasons: [...drySpots, ...candidate.reasons] };
|
|
6348
6366
|
});
|
|
6349
6367
|
const ordered = input.preference === "listed" ? [...candidates] : [...candidates].sort(
|
|
6350
6368
|
(left, right) => CLASS_ORDER[left.class] - CLASS_ORDER[right.class] || Number(right.feasible) - Number(left.feasible) || (right.score ?? 0) - (left.score ?? 0) || left.identity.localeCompare(right.identity)
|
|
@@ -6352,7 +6370,8 @@ function rankPool(input) {
|
|
|
6352
6370
|
const sticky = input.sticky;
|
|
6353
6371
|
const stickyIndex = sticky === void 0 ? -1 : ordered.findIndex((candidate) => candidate.identity === sticky.identity);
|
|
6354
6372
|
const stickyCandidate = ordered[stickyIndex];
|
|
6355
|
-
|
|
6373
|
+
const keepSticky = stickyCandidate?.class === "scored" && stickyCandidate.feasible;
|
|
6374
|
+
if (sticky !== void 0 && keepSticky) {
|
|
6356
6375
|
const ageMs = nowMs - Date.parse(sticky.at);
|
|
6357
6376
|
const warm = ageMs <= PROMPT_CACHE_TTL_MS;
|
|
6358
6377
|
if (input.resuming || warm) {
|
|
@@ -6361,12 +6380,14 @@ function rankPool(input) {
|
|
|
6361
6380
|
ordered.unshift({ ...stickyCandidate, reasons: [why, ...stickyCandidate.reasons] });
|
|
6362
6381
|
}
|
|
6363
6382
|
}
|
|
6383
|
+
const movedOff = sticky !== void 0 && input.resuming && !keepSticky ? { identity: sticky.identity, reason: stickyCandidate?.reasons[0] ?? "no longer among the pool's members" } : void 0;
|
|
6364
6384
|
const pick = ordered.find((candidate) => candidate.class !== "ineligible");
|
|
6365
6385
|
const returns = ordered.flatMap((candidate) => candidate.blockedUntilMs === void 0 ? [] : [{ identity: candidate.identity, atMs: candidate.blockedUntilMs }]);
|
|
6366
6386
|
const earliestReturn = returns.length === 0 ? void 0 : returns.reduce((soonest, entry) => entry.atMs < soonest.atMs ? entry : soonest);
|
|
6367
6387
|
return {
|
|
6368
6388
|
candidates: ordered,
|
|
6369
|
-
...pick === void 0 ? { ...earliestReturn === void 0 ? {} : { earliestReturn } } : { pick }
|
|
6389
|
+
...pick === void 0 ? { ...earliestReturn === void 0 ? {} : { earliestReturn } } : { pick },
|
|
6390
|
+
...movedOff === void 0 || pick === void 0 ? {} : { movedOff }
|
|
6370
6391
|
};
|
|
6371
6392
|
}
|
|
6372
6393
|
|
|
@@ -6624,6 +6645,7 @@ function collectPoolPick(params) {
|
|
|
6624
6645
|
candidates: ranking.candidates.map(candidateView),
|
|
6625
6646
|
missing: missing.map(({ identity }) => identity),
|
|
6626
6647
|
...ranking.earliestReturn === void 0 ? {} : { earliestReturn: { identity: ranking.earliestReturn.identity, at: new Date(ranking.earliestReturn.atMs).toISOString() } },
|
|
6648
|
+
...ranking.movedOff === void 0 ? {} : { movedOff: ranking.movedOff },
|
|
6627
6649
|
...sticky.problem === void 0 ? {} : { stickyProblem: sticky.problem }
|
|
6628
6650
|
};
|
|
6629
6651
|
}
|
|
@@ -8895,6 +8917,8 @@ var PoolPickReportSchema = z13.strictObject({
|
|
|
8895
8917
|
missing: z13.array(z13.string()),
|
|
8896
8918
|
/** When nothing can be picked, the soonest any member returns. */
|
|
8897
8919
|
earliestReturn: z13.strictObject({ identity: z13.string(), at: z13.iso.datetime() }).optional(),
|
|
8920
|
+
/** The member a resumed conversation started on but moved off, and why, when the ranking could not keep it. */
|
|
8921
|
+
movedOff: z13.strictObject({ identity: z13.string(), reason: z13.string() }).optional(),
|
|
8898
8922
|
/** Why the last-pick record could not be read, when that is the case. */
|
|
8899
8923
|
stickyProblem: z13.string().optional()
|
|
8900
8924
|
});
|
|
@@ -12641,10 +12665,11 @@ function resolvePoolLaunch(params) {
|
|
|
12641
12665
|
if (graph.ranking.candidates.length === 0) {
|
|
12642
12666
|
return { ok: false, message: `agent-shim: no member of pool "${poolName}" is an existing identity.` };
|
|
12643
12667
|
}
|
|
12644
|
-
const { pick, earliestReturn } = graph.ranking;
|
|
12668
|
+
const { pick, earliestReturn, movedOff } = graph.ranking;
|
|
12645
12669
|
if (pick !== void 0) {
|
|
12646
12670
|
recordStickyPick(params.usageFs, params.paths.usagePicksFile, params.cwd, pick.identity, nowMs);
|
|
12647
|
-
|
|
12671
|
+
const move = movedOff === void 0 ? "" : `moved off ${movedOff.identity} (${movedOff.reason}); `;
|
|
12672
|
+
return { ok: true, identity: pick.identity, explanation: `pool ${poolName}: ${move}${pick.reasons.slice(0, REASONS_LOGGED).join("; ")}` };
|
|
12648
12673
|
}
|
|
12649
12674
|
if (earliestReturn === void 0) {
|
|
12650
12675
|
return { ok: false, message: `agent-shim: no member of pool "${poolName}" can be picked.` };
|
package/dist/types/index.d.ts
CHANGED
|
@@ -2816,6 +2816,10 @@ declare const CheckReportJsonSchema: z.ZodObject<{
|
|
|
2816
2816
|
identity: z.ZodString;
|
|
2817
2817
|
at: z.ZodISODateTime;
|
|
2818
2818
|
}, z.core.$strict>>;
|
|
2819
|
+
movedOff: z.ZodOptional<z.ZodObject<{
|
|
2820
|
+
identity: z.ZodString;
|
|
2821
|
+
reason: z.ZodString;
|
|
2822
|
+
}, z.core.$strict>>;
|
|
2819
2823
|
stickyProblem: z.ZodOptional<z.ZodString>;
|
|
2820
2824
|
}, z.core.$strict>>;
|
|
2821
2825
|
configProfile: z.ZodObject<{
|
|
@@ -3293,6 +3297,10 @@ declare const PoolPickReportSchema: z.ZodObject<{
|
|
|
3293
3297
|
identity: z.ZodString;
|
|
3294
3298
|
at: z.ZodISODateTime;
|
|
3295
3299
|
}, z.core.$strict>>;
|
|
3300
|
+
movedOff: z.ZodOptional<z.ZodObject<{
|
|
3301
|
+
identity: z.ZodString;
|
|
3302
|
+
reason: z.ZodString;
|
|
3303
|
+
}, z.core.$strict>>;
|
|
3296
3304
|
stickyProblem: z.ZodOptional<z.ZodString>;
|
|
3297
3305
|
}, z.core.$strict>;
|
|
3298
3306
|
type PoolPickReport = z.infer<typeof PoolPickReportSchema>;
|
|
@@ -4240,6 +4248,10 @@ declare function createControlApiRouter(deps: ControlApiDeps): {
|
|
|
4240
4248
|
identity: zod.ZodString;
|
|
4241
4249
|
at: zod.ZodISODateTime;
|
|
4242
4250
|
}, zod_v4_core.$strict>>;
|
|
4251
|
+
movedOff: zod.ZodOptional<zod.ZodObject<{
|
|
4252
|
+
identity: zod.ZodString;
|
|
4253
|
+
reason: zod.ZodString;
|
|
4254
|
+
}, zod_v4_core.$strict>>;
|
|
4243
4255
|
stickyProblem: zod.ZodOptional<zod.ZodString>;
|
|
4244
4256
|
}, zod_v4_core.$strict>>;
|
|
4245
4257
|
configProfile: zod.ZodObject<{
|
|
@@ -4557,6 +4569,10 @@ declare function createControlApiRouter(deps: ControlApiDeps): {
|
|
|
4557
4569
|
identity: zod.ZodString;
|
|
4558
4570
|
at: zod.ZodISODateTime;
|
|
4559
4571
|
}, zod_v4_core.$strict>>;
|
|
4572
|
+
movedOff: zod.ZodOptional<zod.ZodObject<{
|
|
4573
|
+
identity: zod.ZodString;
|
|
4574
|
+
reason: zod.ZodString;
|
|
4575
|
+
}, zod_v4_core.$strict>>;
|
|
4560
4576
|
stickyProblem: zod.ZodOptional<zod.ZodString>;
|
|
4561
4577
|
}, zod_v4_core.$strict>, Record<never, never>, Record<never, never>>;
|
|
4562
4578
|
};
|
|
@@ -4813,6 +4829,10 @@ declare function createDoorApiRouter(deps: DoorApiDeps): {
|
|
|
4813
4829
|
identity: zod.ZodString;
|
|
4814
4830
|
at: zod.ZodISODateTime;
|
|
4815
4831
|
}, zod_v4_core.$strict>>;
|
|
4832
|
+
movedOff: zod.ZodOptional<zod.ZodObject<{
|
|
4833
|
+
identity: zod.ZodString;
|
|
4834
|
+
reason: zod.ZodString;
|
|
4835
|
+
}, zod_v4_core.$strict>>;
|
|
4816
4836
|
stickyProblem: zod.ZodOptional<zod.ZodString>;
|
|
4817
4837
|
}, zod_v4_core.$strict>>;
|
|
4818
4838
|
configProfile: zod.ZodObject<{
|
|
@@ -5130,6 +5150,10 @@ declare function createDoorApiRouter(deps: DoorApiDeps): {
|
|
|
5130
5150
|
identity: zod.ZodString;
|
|
5131
5151
|
at: zod.ZodISODateTime;
|
|
5132
5152
|
}, zod_v4_core.$strict>>;
|
|
5153
|
+
movedOff: zod.ZodOptional<zod.ZodObject<{
|
|
5154
|
+
identity: zod.ZodString;
|
|
5155
|
+
reason: zod.ZodString;
|
|
5156
|
+
}, zod_v4_core.$strict>>;
|
|
5133
5157
|
stickyProblem: zod.ZodOptional<zod.ZodString>;
|
|
5134
5158
|
}, zod_v4_core.$strict>, Record<never, never>, Record<never, never>>;
|
|
5135
5159
|
};
|
|
@@ -6065,7 +6089,7 @@ interface Candidate {
|
|
|
6065
6089
|
readonly class: CandidateClass;
|
|
6066
6090
|
/** Plan-size-weighted remaining quota per hour until its reset: higher is more wasted if left unused. Present for `scored` only. */
|
|
6067
6091
|
readonly score?: number;
|
|
6068
|
-
/** False when
|
|
6092
|
+
/** False when a quota window would run dry, at the observed pace, before it resets (the five-hour at its own or the rescaled shared pace, the seven-day at its own). A `scored` candidate that is not feasible ranks below every feasible one. */
|
|
6069
6093
|
readonly feasible: boolean;
|
|
6070
6094
|
/** When an `ineligible` candidate can be used again. */
|
|
6071
6095
|
readonly blockedUntilMs?: number;
|
|
@@ -6083,11 +6107,16 @@ interface PoolRanking {
|
|
|
6083
6107
|
readonly identity: string;
|
|
6084
6108
|
readonly atMs: number;
|
|
6085
6109
|
};
|
|
6110
|
+
/** The member a resumed or continued conversation started on but moved off, and why, when a launch that resumes could not keep it. One sentence for the launch decision line and `check`. */
|
|
6111
|
+
readonly movedOff?: {
|
|
6112
|
+
readonly identity: string;
|
|
6113
|
+
readonly reason: string;
|
|
6114
|
+
};
|
|
6086
6115
|
}
|
|
6087
6116
|
/**
|
|
6088
6117
|
* Ranks `members` for a launch.
|
|
6089
6118
|
*
|
|
6090
|
-
* A member is `ineligible` while a window of its plan, or a refusal it last hit, still binds; it is `unknown` without recorded quota, `pay-per-use` when its usage bills by use, and otherwise `scored`. Scored members order by how much plan-size-weighted quota would expire unused per hour of runway, with any whose five-hour window would run dry at the observed pace (their own, or the person's pace on another member rescaled by plan size) placed behind those that would not. The member last picked for this directory then moves to the front if it is
|
|
6119
|
+
* A member is `ineligible` while a window of its plan, or a refusal it last hit, still binds; it is `unknown` without recorded quota, `pay-per-use` when its usage bills by use, and otherwise `scored`. Scored members order by how much plan-size-weighted quota would expire unused per hour of runway, with any whose five-hour window would run dry at the observed pace (their own, or the person's pace on another member rescaled by plan size) or whose seven-day window would run dry at its own observed pace placed behind those that would not. The member last picked for this directory then moves to the front if it still serves on plan quota and is not projected to run dry, and either the launch resumes a conversation or its prompt cache is still warm; a resumed conversation that cannot keep its member re-ranks and continues on the pick, with the move named in `movedOff`.
|
|
6091
6120
|
*
|
|
6092
6121
|
* How members are ordered depends on `preference`. The default, "score", is the order above: class first, then feasibility, then score, with ties by name. "listed" keeps the order of `members`, which callers build in pool member order, so member order, not class, decides: an `unknown` (no usage recorded) or `pay-per-use` member can be picked before a later `scored` one, which is the list owner's stated preference, and a `scored` member that is not feasible keeps its place and its reason line rather than being demoted. In both modes the pick is the first non-`ineligible` candidate, `earliestReturn` still reports the soonest returning refused member, and the sticky-pick promotion applies unchanged.
|
|
6093
6122
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-shim",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.32.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>",
|