claude-usage-limits 1.7.0 → 1.7.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.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "usage-limits",
|
|
3
3
|
"displayName": "Usage Limits",
|
|
4
|
-
"version": "1.7.
|
|
4
|
+
"version": "1.7.1",
|
|
5
5
|
"description": "Puts your remaining Claude Code usage limit into Claude's context before every prompt, so it opens with what fits in the budget instead of starting work that gets cut off. Reports headroom as turns rather than percentages, prices a job before you start it, and detects your plan tier.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Ridelink",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "usage-limits",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"description": "Reports how much of your Codex usage limit is left as turns of work rather than a percentage, prices a job before you start it, and counts the other agents sharing the same budget.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Ridelink",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-usage-limits",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"description": "Puts your remaining Claude Code usage limit into Claude's context before every prompt, so it opens with what fits in the budget instead of starting work that gets cut off. Reports headroom as turns rather than percentages, prices a job before you start it, and detects your plan tier.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|
|
@@ -361,6 +361,13 @@ function briefText(parts) {
|
|
|
361
361
|
? '[usage-limits] binding window is ' + bound.join(', ') + '.'
|
|
362
362
|
: '[usage-limits] no usable window reading.'
|
|
363
363
|
);
|
|
364
|
+
if (parts.planChanged) {
|
|
365
|
+
sentences.push(
|
|
366
|
+
'The plan has changed since these figures were learned, so the reading ' +
|
|
367
|
+
'above was measured against a different allowance and may predate the ' +
|
|
368
|
+
'change; run /usage before trusting it.'
|
|
369
|
+
);
|
|
370
|
+
}
|
|
364
371
|
if (parts.rebuilt) {
|
|
365
372
|
sentences.push(
|
|
366
373
|
'That figure was rebuilt from local history because the ' +
|
|
@@ -478,6 +485,7 @@ async function run(now, hookInput) {
|
|
|
478
485
|
othersSummary: summariseOthers(data.windows, binding && binding.key),
|
|
479
486
|
sessions: data.sessions,
|
|
480
487
|
staleWindows: data.staleWindows,
|
|
488
|
+
planChanged: data.planChanged,
|
|
481
489
|
critical: usage.criticalOthers(data.windows, binding && binding.key).map((w) => ({
|
|
482
490
|
label: w.label,
|
|
483
491
|
percentUsed: w.percentUsed,
|
|
@@ -526,6 +534,7 @@ async function run(now, hookInput) {
|
|
|
526
534
|
session: view.session,
|
|
527
535
|
rebuilt: Boolean(binding && binding.estimated),
|
|
528
536
|
staleWindows: view.staleWindows || 0,
|
|
537
|
+
planChanged: Boolean(view.planChanged),
|
|
529
538
|
critical: view.critical || [],
|
|
530
539
|
pointsSinceSnapshot: (binding && binding.pointsSinceSnapshot) || 0,
|
|
531
540
|
snapshotAge: view.snapshotAge,
|
|
@@ -678,6 +678,53 @@ function writeCalibration(all) {
|
|
|
678
678
|
}
|
|
679
679
|
}
|
|
680
680
|
|
|
681
|
+
// Everything learned about a budget belongs to the plan it was learned on.
|
|
682
|
+
//
|
|
683
|
+
// A point of a window is a share of an allowance, so changing the allowance
|
|
684
|
+
// changes what a point is worth, and every figure derived from the old one is
|
|
685
|
+
// then wrong by the ratio between the plans. Upgrading Pro to Max 5x is roughly
|
|
686
|
+
// a fivefold move: a calibration saying a point costs $0.40 keeps being applied
|
|
687
|
+
// to a point now worth several times that, and the turn estimates built on it
|
|
688
|
+
// are wrong in the direction that promises room there is not.
|
|
689
|
+
//
|
|
690
|
+
// There is no timestamp anywhere that says when the plan changed.
|
|
691
|
+
// `subscriptionCreatedAt` is the original signup, not the upgrade. So the plan
|
|
692
|
+
// is stamped onto the calibration instead, and a stamp that no longer matches
|
|
693
|
+
// is itself the proof that it moved.
|
|
694
|
+
function calibrationForPlan(all, planId) {
|
|
695
|
+
const kept = {};
|
|
696
|
+
let dropped = false;
|
|
697
|
+
for (const key of Object.keys(all || {})) {
|
|
698
|
+
const entry = all[key];
|
|
699
|
+
if (!entry || typeof entry !== 'object') continue;
|
|
700
|
+
// A stamp that no longer matches is proof the plan moved, and worth saying
|
|
701
|
+
// so: the reading on disk was measured against the other allowance too.
|
|
702
|
+
if (entry.plan && planId && entry.plan !== planId) {
|
|
703
|
+
dropped = true;
|
|
704
|
+
continue;
|
|
705
|
+
}
|
|
706
|
+
// An entry saved before the stamp existed cannot be shown to belong to this
|
|
707
|
+
// plan. Keeping it would be assuming the answer, and the cost of assuming
|
|
708
|
+
// wrong is the whole bug this guards: a point priced for Pro applied to a
|
|
709
|
+
// Max window, promising several times the turns that exist. It is dropped
|
|
710
|
+
// instead, which costs one relearn and says "unknown" in the meantime.
|
|
711
|
+
// Unknown is not claimed as a plan change, because it is not evidence of
|
|
712
|
+
// one; every install upgrading to this version passes through here once.
|
|
713
|
+
if (!entry.plan && planId) continue;
|
|
714
|
+
kept[key] = entry;
|
|
715
|
+
}
|
|
716
|
+
return { learned: kept, planChanged: dropped };
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
function stampPlan(all, planId) {
|
|
720
|
+
if (!planId) return all;
|
|
721
|
+
const stamped = {};
|
|
722
|
+
for (const key of Object.keys(all || {})) {
|
|
723
|
+
stamped[key] = Object.assign({}, all[key], { plan: planId });
|
|
724
|
+
}
|
|
725
|
+
return stamped;
|
|
726
|
+
}
|
|
727
|
+
|
|
681
728
|
// A sample is better when it rests on more turns. Percentages read in whole
|
|
682
729
|
// numbers, so a bigger percentage also divides more precisely.
|
|
683
730
|
function betterCalibration(current, candidate) {
|
|
@@ -1280,7 +1327,11 @@ async function report(now, options) {
|
|
|
1280
1327
|
}
|
|
1281
1328
|
|
|
1282
1329
|
const onDisk = readCalibration();
|
|
1283
|
-
|
|
1330
|
+
// Anything learned on a different plan is void, and its absence is what makes
|
|
1331
|
+
// the report say so rather than quietly pricing this plan with the last one's
|
|
1332
|
+
// numbers.
|
|
1333
|
+
const calibrated = calibrationForPlan(onDisk, base.planId);
|
|
1334
|
+
const learned = Object.assign({}, calibrated.learned);
|
|
1284
1335
|
|
|
1285
1336
|
// Codex logs the meter next to every request, so the price of a point can be
|
|
1286
1337
|
// measured outright instead of inferred. A measurement from this session
|
|
@@ -1299,7 +1350,11 @@ async function report(now, options) {
|
|
|
1299
1350
|
base.snapshotFetchedAt,
|
|
1300
1351
|
learned,
|
|
1301
1352
|
base.windowSpecs,
|
|
1302
|
-
|
|
1353
|
+
// A refusal describes the allowance that refused it. After a plan change
|
|
1354
|
+
// that allowance is gone, so anchoring the new window to it, or warning
|
|
1355
|
+
// that the room "ran out last time", is describing a budget that no longer
|
|
1356
|
+
// exists.
|
|
1357
|
+
calibrated.planChanged ? new Map() : rejections
|
|
1303
1358
|
);
|
|
1304
1359
|
|
|
1305
1360
|
// Keep the best sample seen so far, so a thin baseline never has to guess.
|
|
@@ -1311,11 +1366,11 @@ async function report(now, options) {
|
|
|
1311
1366
|
}
|
|
1312
1367
|
// Compared against what is actually on disk, so a measurement taken during
|
|
1313
1368
|
// this run is saved too rather than only the ones inferred from a window.
|
|
1314
|
-
let changed =
|
|
1369
|
+
let changed = calibrated.planChanged;
|
|
1315
1370
|
for (const key of Object.keys(updated)) {
|
|
1316
1371
|
if (updated[key] !== onDisk[key]) changed = true;
|
|
1317
1372
|
}
|
|
1318
|
-
if (changed) writeCalibration(updated);
|
|
1373
|
+
if (changed) writeCalibration(stampPlan(updated, base.planId));
|
|
1319
1374
|
|
|
1320
1375
|
const recentEvents = events.filter((event) => event.at >= now - HOUR);
|
|
1321
1376
|
const recent = totals(recentEvents);
|
|
@@ -1332,6 +1387,10 @@ async function report(now, options) {
|
|
|
1332
1387
|
windows,
|
|
1333
1388
|
binding,
|
|
1334
1389
|
otherLimits: otherLimits(base.utilization),
|
|
1390
|
+
// The plan moved since anything was last learned about it, so the cached
|
|
1391
|
+
// percentage was measured against a different allowance and everything
|
|
1392
|
+
// derived from the old one has been dropped.
|
|
1393
|
+
planChanged: calibrated.planChanged,
|
|
1335
1394
|
// The last time the account actually refused work, so a report taken just
|
|
1336
1395
|
// after a cutoff says so rather than describing the fresh window as though
|
|
1337
1396
|
// nothing happened.
|
|
@@ -1462,6 +1521,12 @@ function render(data) {
|
|
|
1462
1521
|
(data.snapshotAgeMs === null ? 'none on disk' : formatDuration(data.snapshotAgeMs) + ' old')
|
|
1463
1522
|
);
|
|
1464
1523
|
lines.push(' Settings model=' + data.settings.model + ' effort=' + data.settings.effortLevel);
|
|
1524
|
+
if (data.planChanged) {
|
|
1525
|
+
lines.push(' Plan change this is a different plan from the one the figures below');
|
|
1526
|
+
lines.push(' were learned on, so what a point of a window is worth has');
|
|
1527
|
+
lines.push(' changed with it. The cached reading may predate the change:');
|
|
1528
|
+
lines.push(' run /usage for one measured against this plan.');
|
|
1529
|
+
}
|
|
1465
1530
|
const credits = data.credits;
|
|
1466
1531
|
if (credits && credits.unlimited) {
|
|
1467
1532
|
lines.push(' Credits unlimited');
|
|
@@ -1884,6 +1949,8 @@ module.exports = {
|
|
|
1884
1949
|
bindingWindow,
|
|
1885
1950
|
criticalOthers,
|
|
1886
1951
|
betterCalibration,
|
|
1952
|
+
calibrationForPlan,
|
|
1953
|
+
stampPlan,
|
|
1887
1954
|
calibrationFile,
|
|
1888
1955
|
CRITICAL_PERCENT,
|
|
1889
1956
|
dominantEffort,
|