claude-usage-limits 1.40.1 → 1.40.3
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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +8 -1
- package/package.json +1 -1
- package/skills/usage-limits/scripts/brief.js +15 -4
- package/skills/usage-limits/scripts/defer.js +38 -6
- package/skills/usage-limits/scripts/mode.js +10 -0
- package/skills/usage-limits/scripts/pulse.js +10 -4
- package/skills/usage-limits/scripts/relay.js +8 -1
- package/skills/usage-limits/scripts/stop.js +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "usage-limits",
|
|
3
3
|
"displayName": "Usage Limits",
|
|
4
|
-
"version": "1.40.
|
|
4
|
+
"version": "1.40.3",
|
|
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.40.
|
|
3
|
+
"version": "1.40.3",
|
|
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/README.md
CHANGED
|
@@ -418,6 +418,13 @@ would fire into a session that never stopped. A weekly wall still arms. The
|
|
|
418
418
|
acknowledgement lapses by itself when the 5-hour window it was made against
|
|
419
419
|
resets.
|
|
420
420
|
|
|
421
|
+
A deferral is different from a wake, and is treated differently. `defer` is a
|
|
422
|
+
time **you** named, so it is always scheduled — but if you defer to `reset` while
|
|
423
|
+
low-priority is acknowledged, the confirmation says that this session carries
|
|
424
|
+
past the 5-hour reset and waiting for it buys nothing. The mid-turn pulse follows
|
|
425
|
+
the brief onto the weekly too, out of the same function, so the two can never
|
|
426
|
+
report different windows.
|
|
427
|
+
|
|
421
428
|
**The graceful wrap-up note.** Claude Code can inject its own "finish up"
|
|
422
429
|
instruction at the wall. The mechanism is real, and the gate is
|
|
423
430
|
`tengu_lantern_wick_mode` — the bundle's own normalizer keeps only `"wrap-up"`
|
|
@@ -1295,7 +1302,7 @@ test/ node --test, no dependencies
|
|
|
1295
1302
|
node --test
|
|
1296
1303
|
```
|
|
1297
1304
|
|
|
1298
|
-
|
|
1305
|
+
901 tests over the pricing, the window arithmetic, plan and credit detection,
|
|
1299
1306
|
the status line, the before-prompt line, the mid-turn pulse, the after-reply tally and the session history, job forecasting,
|
|
1300
1307
|
per-project attribution, the Codex reader and its installer, the CLI,
|
|
1301
1308
|
packaging, the settings save/restore, and what Claude Code's own wall-time
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-usage-limits",
|
|
3
|
-
"version": "1.40.
|
|
3
|
+
"version": "1.40.3",
|
|
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",
|
|
@@ -1454,7 +1454,7 @@ function relayState(now, hookInput, binding, sessionId) {
|
|
|
1454
1454
|
return { enabled: true, armed: mine, config, last: recent };
|
|
1455
1455
|
}
|
|
1456
1456
|
const work = relay.detectWork(hookInput && hookInput.transcript_path, {});
|
|
1457
|
-
const able = relay.armable({ config, binding, sessionId, work });
|
|
1457
|
+
const able = relay.armable({ config, binding, sessionId, work, hostName: usage.currentHost() });
|
|
1458
1458
|
if (!able.ok) return { enabled: true, why: able.why, config, last: recent, work };
|
|
1459
1459
|
// Registering a scheduled task measured 936 ms, and this hook has ten
|
|
1460
1460
|
// seconds of which the live reading may take four and the scan five. A
|
|
@@ -1500,8 +1500,19 @@ function relayState(now, hookInput, binding, sessionId) {
|
|
|
1500
1500
|
// usage-mode --low-priority on, and that record lapses at the reset of the
|
|
1501
1501
|
// window it was recorded against. Whether low-priority is really running is in
|
|
1502
1502
|
// the CLI's process memory and readable nowhere; see lowpri.js.
|
|
1503
|
-
|
|
1503
|
+
// Claude Code only, and settled before a single file is read.
|
|
1504
|
+
//
|
|
1505
|
+
// Every one of these features is Claude Code's: /low-priority and /limit-reset
|
|
1506
|
+
// are its slash commands, the wrap-up note is its injection, and
|
|
1507
|
+
// autoContinueAtUsageLimit is its setting. None of them exist in Codex or in
|
|
1508
|
+
// Antigravity, and ~/.claude.json describes an account neither of them is
|
|
1509
|
+
// running on. Reading it from inside another host would put a Claude Code
|
|
1510
|
+
// command in front of an agent that cannot type it, against a window it is not
|
|
1511
|
+
// spending - the same mistake as naming /effort in Codex, which this plugin is
|
|
1512
|
+
// otherwise careful never to make.
|
|
1513
|
+
function wallFeatures(now, binding, windows, hostName) {
|
|
1504
1514
|
const out = { binding, lowPriority: null, hostWrapsUp: false, swapped: false };
|
|
1515
|
+
if (hostName !== host.CLAUDE) return out;
|
|
1505
1516
|
try {
|
|
1506
1517
|
const account = lowpri.snapshot();
|
|
1507
1518
|
const info = lowpri.forBrief({ account, now, binding, windows });
|
|
@@ -1776,7 +1787,7 @@ async function run(now, hookInput, opts) {
|
|
|
1776
1787
|
|
|
1777
1788
|
// Settled before the pressure, the relay and the line are decided, so all
|
|
1778
1789
|
// three describe the same window.
|
|
1779
|
-
const wall = wallFeatures(now, view.binding, view.windows);
|
|
1790
|
+
const wall = wallFeatures(now, view.binding, view.windows, usage.currentHost());
|
|
1780
1791
|
const binding = wall.binding;
|
|
1781
1792
|
// The turn count belongs to whichever window is now binding. Carrying the
|
|
1782
1793
|
// 5-hour count onto a weekly window would put two budgets in one sentence.
|
|
@@ -2000,7 +2011,7 @@ function withBugcheck(text) {
|
|
|
2000
2011
|
return text;
|
|
2001
2012
|
}
|
|
2002
2013
|
|
|
2003
|
-
module.exports = { sweepDebris, withBugcheck, sayOnce, shapeOf, saidFile, REPEAT_MS, staleVersionFor, relayNewsFor, installedVersion, runningVersion,
|
|
2014
|
+
module.exports = { wallFeatures, sweepDebris, withBugcheck, sayOnce, shapeOf, saidFile, REPEAT_MS, staleVersionFor, relayNewsFor, installedVersion, runningVersion,
|
|
2004
2015
|
readSaid, standingSaid, markStanding, standingShortFor, STANDING_SHORT, cacheMissWhyFor, missReason, MISS_RECENT_MS,
|
|
2005
2016
|
DEFAULTS,
|
|
2006
2017
|
HOOK_BUDGET_MS,
|
|
@@ -140,7 +140,10 @@ function confirmation(parts) {
|
|
|
140
140
|
const tail = [];
|
|
141
141
|
tail.push('Nothing has been started');
|
|
142
142
|
if (parts.items) tail.push(parts.items + ' saved');
|
|
143
|
-
|
|
143
|
+
// Every note, not only the first: two can apply at once.
|
|
144
|
+
for (const note of parts.notes && parts.notes.length ? parts.notes : [parts.resetNote]) {
|
|
145
|
+
if (note) tail.push(note);
|
|
146
|
+
}
|
|
144
147
|
return line + tail.join('; ') + '. Run "defer cancel" to call it off.';
|
|
145
148
|
}
|
|
146
149
|
|
|
@@ -154,7 +157,7 @@ function bindingReset(now) {
|
|
|
154
157
|
usage.setHost(host.detect(process.argv.slice(2), process.env));
|
|
155
158
|
const collected = usage.collect(now);
|
|
156
159
|
const utilization = collected && collected.utilization;
|
|
157
|
-
if (!utilization) return { resetsAt: null, percent: null };
|
|
160
|
+
if (!utilization) return { resetsAt: null, percent: null, windowKey: null };
|
|
158
161
|
let worst = null;
|
|
159
162
|
for (const key of Object.keys(utilization)) {
|
|
160
163
|
const window = utilization[key];
|
|
@@ -163,12 +166,24 @@ function bindingReset(now) {
|
|
|
163
166
|
if (!Number.isFinite(percent)) continue;
|
|
164
167
|
const resets = Date.parse(window.resets_at);
|
|
165
168
|
if (!worst || percent > worst.percent) {
|
|
166
|
-
worst = { percent, resetsAt: Number.isFinite(resets) ? resets : null };
|
|
169
|
+
worst = { percent, resetsAt: Number.isFinite(resets) ? resets : null, windowKey: key };
|
|
167
170
|
}
|
|
168
171
|
}
|
|
169
172
|
return worst || { resetsAt: null, percent: null };
|
|
170
173
|
} catch (err) {
|
|
171
|
-
return { resetsAt: null, percent: null };
|
|
174
|
+
return { resetsAt: null, percent: null, windowKey: null };
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Claude Code only, like every other reader of this fact: /low-priority is its
|
|
179
|
+
// slash command against its account, and Codex files a five-hour window under the
|
|
180
|
+
// same key.
|
|
181
|
+
function lowPriorityAcknowledged(now) {
|
|
182
|
+
try {
|
|
183
|
+
if (host.detect(process.argv.slice(2), process.env) !== host.CLAUDE) return false;
|
|
184
|
+
return Boolean(require('./lowpri.js').readAck(now));
|
|
185
|
+
} catch (err) {
|
|
186
|
+
return false;
|
|
172
187
|
}
|
|
173
188
|
}
|
|
174
189
|
|
|
@@ -198,13 +213,29 @@ function plan(options) {
|
|
|
198
213
|
notes.push('note: that is before the window resets at ' + formatClock(binding.resetsAt));
|
|
199
214
|
}
|
|
200
215
|
}
|
|
216
|
+
// A deferral is a time the user named, so it is honoured whatever else is
|
|
217
|
+
// true - unlike an automatic relay wake, which the acknowledgement refuses.
|
|
218
|
+
// But waiting for a 5-hour reset this session carries straight past is
|
|
219
|
+
// waiting for nothing, and that is worth one line before the task is
|
|
220
|
+
// registered rather than after it fires.
|
|
221
|
+
if (options.lowPriorityAck && binding.windowKey === 'five_hour') {
|
|
222
|
+
notes.push(
|
|
223
|
+
'note: you have said /low-priority is on, so this session carries past the 5-hour reset ' +
|
|
224
|
+
'and waiting for it buys nothing - the weekly is the window that still stops work'
|
|
225
|
+
);
|
|
226
|
+
}
|
|
201
227
|
return {
|
|
202
228
|
ok: true,
|
|
203
229
|
at: when.at,
|
|
204
230
|
label: when.label,
|
|
205
231
|
clock: formatClock(when.at),
|
|
206
232
|
in: when.at - now,
|
|
233
|
+
// resetNote is the first of them, kept because the confirmation and the
|
|
234
|
+
// tests have always read that field. `notes` is all of them, because there
|
|
235
|
+
// can now be two and dropping the second would have hidden the one fact
|
|
236
|
+
// worth printing: that waiting for this reset buys nothing.
|
|
207
237
|
resetNote: notes[0] || null,
|
|
238
|
+
notes,
|
|
208
239
|
};
|
|
209
240
|
}
|
|
210
241
|
|
|
@@ -256,7 +287,7 @@ function main(argv, now) {
|
|
|
256
287
|
}
|
|
257
288
|
|
|
258
289
|
const binding = bindingReset(at);
|
|
259
|
-
const decided = plan({ now: at, when: first, binding });
|
|
290
|
+
const decided = plan({ now: at, when: first, binding, lowPriorityAck: lowPriorityAcknowledged(at) });
|
|
260
291
|
if (!decided.ok) return decided.error;
|
|
261
292
|
|
|
262
293
|
const id = sessionId(args, process.env);
|
|
@@ -304,6 +335,7 @@ function main(argv, now) {
|
|
|
304
335
|
in: decided.in,
|
|
305
336
|
items,
|
|
306
337
|
resetNote: decided.resetNote,
|
|
338
|
+
notes: decided.notes,
|
|
307
339
|
});
|
|
308
340
|
}
|
|
309
341
|
|
|
@@ -317,4 +349,4 @@ if (require.main === module) {
|
|
|
317
349
|
}
|
|
318
350
|
}
|
|
319
351
|
|
|
320
|
-
module.exports = { parseWhen, formatClock, formatSpan, confirmation, plan, status, cancel, main, MINUTE, HOUR };
|
|
352
|
+
module.exports = { lowPriorityAcknowledged, parseWhen, formatClock, formatSpan, confirmation, plan, status, cancel, main, MINUTE, HOUR };
|
|
@@ -1473,6 +1473,16 @@ function main(argv) {
|
|
|
1473
1473
|
// Nothing else may write this. The model cannot type a slash command, so it
|
|
1474
1474
|
// cannot have turned low-priority on, so it has nothing to acknowledge.
|
|
1475
1475
|
if (flag('--low-priority')) {
|
|
1476
|
+
// Claude Code only, said plainly rather than by doing nothing. /low-priority
|
|
1477
|
+
// is a Claude Code slash command against a Claude account; recording it under
|
|
1478
|
+
// Codex or Antigravity would write a fact about the wrong agent's budget into
|
|
1479
|
+
// the wrong agent's config directory.
|
|
1480
|
+
const hostHere = host.detect(process.argv.slice(2), process.env);
|
|
1481
|
+
if (hostHere !== host.CLAUDE) {
|
|
1482
|
+
return '/low-priority is a Claude Code command and does not exist in ' +
|
|
1483
|
+
(hostHere === host.CODEX ? 'Codex' : 'Antigravity') +
|
|
1484
|
+
', so there is nothing to record here. Use that host\'s own controls for the task in hand.';
|
|
1485
|
+
}
|
|
1476
1486
|
const asked = String(value('--low-priority') || '').trim().toLowerCase();
|
|
1477
1487
|
const account = lowpri.snapshot();
|
|
1478
1488
|
const provisioned = lowpri.offer(account);
|
|
@@ -342,11 +342,17 @@ async function run(now, hookInput) {
|
|
|
342
342
|
if (quiet) return '';
|
|
343
343
|
|
|
344
344
|
const data = await usage.report(now, { sessionId, budgetMs: SCAN_BUDGET_MS });
|
|
345
|
-
|
|
346
|
-
|
|
345
|
+
if (!data.binding) return '';
|
|
346
|
+
// The same swap the brief makes, out of the same function so the two can never
|
|
347
|
+
// disagree: once the user has said /low-priority is on, the weekly is the only
|
|
348
|
+
// brake left, and a mid-turn line still counting down the 5-hour window would
|
|
349
|
+
// undo the brief on every tool call. Claude Code only; see brief.wallFeatures.
|
|
350
|
+
const wall = brief.wallFeatures(now, data.binding, data.windows || [], usage.currentHost());
|
|
351
|
+
const binding = wall.binding;
|
|
347
352
|
// This turn paid for the scan, so leave the corrected figure where the
|
|
348
|
-
// status line and --status can read it without paying for one.
|
|
349
|
-
|
|
353
|
+
// status line and --status can read it without paying for one. Every window,
|
|
354
|
+
// not the one that happens to be binding after the swap.
|
|
355
|
+
reading.recordAll(data.windows || [data.binding], now, usage.isCodex() ? require('./codex.js').homeDir() : null);
|
|
350
356
|
|
|
351
357
|
// The same count and the same split as the brief, so the two lines never
|
|
352
358
|
// disagree about how many sessions there are or how much of the budget is
|
|
@@ -1185,7 +1185,14 @@ function armable(input) {
|
|
|
1185
1185
|
// This reads the plugin's own acknowledgement, never a guess. Whether
|
|
1186
1186
|
// low-priority is actually running lives in the CLI's process memory and is
|
|
1187
1187
|
// readable nowhere; see lowpri.js.
|
|
1188
|
-
|
|
1188
|
+
//
|
|
1189
|
+
// Claude Code only. Codex files a five-hour window under the same key, and
|
|
1190
|
+
// /low-priority is a Claude Code slash command against a Claude account, so an
|
|
1191
|
+
// acknowledgement made there must not refuse a Codex relay. The host is taken
|
|
1192
|
+
// from the caller where it knows, and detected otherwise - the Codex and
|
|
1193
|
+
// Antigravity hooks both pass --host.
|
|
1194
|
+
const hostHere = options.hostName || host.detect(process.argv.slice(2), process.env);
|
|
1195
|
+
if (binding.key === 'five_hour' && hostHere === host.CLAUDE) {
|
|
1189
1196
|
let ack = null;
|
|
1190
1197
|
try {
|
|
1191
1198
|
ack = lowpri.readAck(options.now);
|
|
@@ -87,7 +87,7 @@ function armAtCompletion(now, hookInput, sessionId) {
|
|
|
87
87
|
const binding = bindingFromCollect(usage.collect(now), now);
|
|
88
88
|
if (!binding) return null;
|
|
89
89
|
|
|
90
|
-
const able = relay.armable({ config, binding, sessionId, work, atCompletion: true });
|
|
90
|
+
const able = relay.armable({ config, binding, sessionId, work, atCompletion: true, hostName: usage.currentHost() });
|
|
91
91
|
if (!able.ok) return null;
|
|
92
92
|
|
|
93
93
|
const armed = relay.arm({
|