claude-usage-limits 1.16.1 → 1.18.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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "usage-limits",
3
3
  "displayName": "Usage Limits",
4
- "version": "1.16.1",
4
+ "version": "1.18.0",
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.16.1",
3
+ "version": "1.18.0",
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.16.1",
3
+ "version": "1.18.0",
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",
@@ -395,6 +395,30 @@ and cannot change one already running. The budget line prints whichever
395
395
  vocabulary the host it is running in actually understands, so use the command
396
396
  it gives you rather than the one you remember.
397
397
 
398
+ **Check the setting against the work, not against the window.** Once there is
399
+ evidence for it, the budget line tells you what your current effort actually
400
+ costs against the cheapest level measured on this account - "ultra, measured at
401
+ 6 times the cost of medium a turn (8 turns against 6)". It says this from the
402
+ first prompt, at any percentage, because a mechanical hour at the top setting is
403
+ waste at 10 per cent used exactly as much as at 80; the only difference is that
404
+ at 80 somebody notices.
405
+
406
+ It cannot judge how hard your work is. You can. So when you see it, look at what
407
+ is actually in front of you and take one of three levers yourself, saying which
408
+ in one line:
409
+
410
+ 1. **Drop the effort** for that stretch - a rename, a docs pass, running tests,
411
+ applying a fix you have already worked out.
412
+ 2. **Hand the stretch to a cheaper model**, and keep the dear one for the
413
+ decisions that need it.
414
+ 3. **Do less of it at this setting** - a fan-out multiplies the setting across
415
+ every agent, so six agents at ultra is six ultra turns, not one.
416
+
417
+ Put it back when the work gets hard again. The question is asked once per
418
+ setting per session, not every prompt, and never at all unless there is a real
419
+ measured comparison behind it - a ratio from a price list would have you drop
420
+ effort on a hunch and call it evidence.
421
+
398
422
  **This is a lever you may pull yourself, not only an emergency exit.** Use it
399
423
  whenever the current setting is dearer than the work in front of you needs,
400
424
  without being asked and long before any window is tight: a mechanical rename,
@@ -552,8 +552,12 @@ function briefText(parts) {
552
552
  // The command, in the host's own vocabulary. /effort does not exist
553
553
  // in Codex, and naming it there is telling Codex to do nothing while
554
554
  // believing it acted.
555
+ // The host this line is being written FOR, not whatever the process
556
+ // happens to be set to. Reading global state here meant the sentence
557
+ // was only accidentally right, and a caller that built a line for
558
+ // the other host got a command that does not exist there.
555
559
  (warning.cheaper && warning.cheaper.effort
556
- ? ' To drop it: ' + usage.levers(usage.currentHost()).effort(warning.cheaper.effort) + '.'
560
+ ? ' To drop it: ' + usage.levers(parts.host || usage.currentHost()).effort(warning.cheaper.effort) + '.'
557
561
  : '')
558
562
  );
559
563
  }
@@ -662,6 +666,27 @@ function briefText(parts) {
662
666
  );
663
667
  }
664
668
 
669
+ // Is the setting bigger than the work needs? Asked from the setting, not
670
+ // from the budget: a mechanical hour at the top setting is waste at 10 per
671
+ // cent used exactly as much as at 80. The plugin supplies the measured half
672
+ // and asks for the judgement, because only the reader knows what is coming.
673
+ //
674
+ // Said once per setting per session. Every prompt would be nagging, and the
675
+ // question only changes when the setting does.
676
+ if (parts.fit) {
677
+ sentences.push(
678
+ 'Your effort is ' + parts.fit.effort + ', measured at ' + parts.fit.multiple +
679
+ ' times the cost of ' + parts.fit.cheaper + ' a turn on this account (' +
680
+ parts.fit.sample + ' turns against ' + parts.fit.cheaperSample + '). Judge what is ' +
681
+ 'actually in front of you before the next stretch: mechanical work - a rename, a docs ' +
682
+ 'pass, running tests, applying a fix you have already worked out - does not need it. ' +
683
+ 'If this stretch is that, take one of the three levers yourself and say which in one ' +
684
+ 'line: drop the effort (' + parts.fit.command + '), hand the stretch to a cheaper ' +
685
+ 'model, or do less of it at this setting - a fan-out multiplies the setting across ' +
686
+ 'every agent. Put it back when the work gets hard again.'
687
+ );
688
+ }
689
+
665
690
  if (parts.voiceNote) sentences.push('How this user wants to be written to: ' + parts.voiceNote);
666
691
 
667
692
  // What the relay changes about all of this.
@@ -965,6 +990,10 @@ async function run(now, hookInput) {
965
990
  if (!base.utilization) return '';
966
991
 
967
992
  const all = readCache();
993
+ // Which setting the fit question was already asked for, read BEFORE anything
994
+ // rewrites the slot. Reading it afterwards meant comparing the new answer
995
+ // against itself, so the question could never be asked at all.
996
+ const askedFitFor = all && all[sessionId || '_'] ? all[sessionId || '_'].fitFor || null : null;
968
997
  let view = pickCached(all, sessionId, now, config.cacheSeconds * SECOND);
969
998
 
970
999
  // Everything shown has to come from one pass. Deriving the turns from a
@@ -999,7 +1028,9 @@ async function run(now, hookInput) {
999
1028
  snapshotAge: usage.formatDuration(data.snapshotAgeMs),
1000
1029
  binding: cacheableBinding(binding),
1001
1030
  effortWarning: data.effortWarning || null,
1031
+ fit: usage.settingFit(data.events || null, data.effortNow || null, usage.currentHost()),
1002
1032
  };
1033
+ view.fitFor = view.fit ? view.fit.effort : askedFitFor;
1003
1034
  writeCache(mergeCache(all, sessionId, view, KEEP_SESSIONS));
1004
1035
  }
1005
1036
 
@@ -1063,8 +1094,12 @@ async function run(now, hookInput) {
1063
1094
  : null,
1064
1095
  othersSummary: view.othersSummary,
1065
1096
  escape: view.escape || null,
1097
+ host: usage.currentHost(),
1066
1098
  turnsLeft: view.turnsLeft,
1067
1099
  effortWarning: view.effortWarning || null,
1100
+ // Once per setting. The slot below records which effort it was said for,
1101
+ // so a change of setting asks the question again and a repeat does not.
1102
+ fit: view.fit && askedFitFor !== view.fit.effort ? view.fit : null,
1068
1103
  // Outside the cache: it is cheap, and it belongs to the other agent's
1069
1104
  // clock rather than this session's.
1070
1105
  codex: codexSummary(now),
@@ -2160,6 +2160,54 @@ function criticalOthers(windows, bindingKey, threshold) {
2160
2160
  );
2161
2161
  }
2162
2162
 
2163
+ // Is the setting bigger than the work needs?
2164
+ //
2165
+ // Everything else here is budget-triggered: it speaks when a window is filling.
2166
+ // That is the wrong trigger for this question, because a mechanical hour at the
2167
+ // top setting is waste at 10 per cent used exactly as much as at 80 - the only
2168
+ // difference is that at 80 somebody notices. The trigger is the SETTING, not
2169
+ // the window.
2170
+ //
2171
+ // The plugin cannot judge how hard the work is; only the agent reading this
2172
+ // can. So this supplies the half that is measurable - what the current effort
2173
+ // actually costs against the cheapest level with real evidence behind it, on
2174
+ // this account - and the brief asks for the judgement.
2175
+ //
2176
+ // Returns null unless there is a measured alternative. A ratio invented from a
2177
+ // price list would be worse than silence: it would have an agent drop effort on
2178
+ // a hunch and call it evidence.
2179
+ function settingFit(events, effortNow, which) {
2180
+ // Guarded here rather than trusted from the caller: a cache hit has no event
2181
+ // list to hand over, and dominantEffort iterates without checking.
2182
+ const list = Array.isArray(events) ? events : [];
2183
+ const current = effortNow || dominantEffort(list);
2184
+ if (!current) return null;
2185
+ const rates = effortRates(list);
2186
+ const here = rates.find((row) => row.effort === current);
2187
+ if (!here || here.turns < MIN_EFFORT_SAMPLE) return null;
2188
+ const measure = (row) => (Number.isFinite(row.outputPerTurn) && row.outputPerTurn > 0 ? row.outputPerTurn : row.perTurn);
2189
+ const mine = measure(here);
2190
+ if (!Number.isFinite(mine) || mine <= 0) return null;
2191
+ let best = null;
2192
+ for (const row of rates) {
2193
+ if (row.effort === current || row.turns < MIN_EFFORT_SAMPLE) continue;
2194
+ const theirs = measure(row);
2195
+ if (!Number.isFinite(theirs) || theirs <= 0) continue;
2196
+ const multiple = mine / theirs;
2197
+ if (multiple < EFFORT_DEARER_BY) continue;
2198
+ if (!best || multiple > best.multiple) best = { effort: row.effort, multiple, sample: row.turns };
2199
+ }
2200
+ if (!best) return null;
2201
+ return {
2202
+ effort: current,
2203
+ sample: here.turns,
2204
+ cheaper: best.effort,
2205
+ cheaperSample: best.sample,
2206
+ multiple: Number(best.multiple.toFixed(1)),
2207
+ command: levers(which || currentHost()).effort(best.effort),
2208
+ };
2209
+ }
2210
+
2163
2211
  // The way out that is not stopping.
2164
2212
  //
2165
2213
  // This exists because of a session that ended for no reason. The Fable weekly
@@ -3837,6 +3885,7 @@ module.exports = {
3837
3885
  bindingWindow,
3838
3886
  criticalOthers,
3839
3887
  escapeRoute,
3888
+ settingFit,
3840
3889
  levers,
3841
3890
  betterCalibration,
3842
3891
  calibrationForPlan,