@aragon-agent/cli 0.6.18 → 0.6.20

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/CHANGELOG.md CHANGED
@@ -7,6 +7,20 @@ were written.
7
7
 
8
8
  ## Unreleased
9
9
 
10
+ ### Fixed
11
+
12
+ - **`Ctrl+I` was dead on Windows.** The win32-input-mode translator only
13
+ recognised the chord when the console reported it as `VK_TAB` (vk 9) with
14
+ ctrl - a record shape no real console was ever probed for. Physical
15
+ Ctrl+I keeps the LETTER vk (0x49, the same Ctrl+letter rule that reports
16
+ Ctrl+A as vk 65 and Ctrl+J as vk 74 in the Shift+Enter probe captures)
17
+ while `char` carries the Tab byte, so the record fell through to the
18
+ generic Ctrl-combo branch, came out as plain Tab, and the keybinding
19
+ silently did completion instead of opening the index confirm. The
20
+ translator now tells Ctrl+I apart from Tab by `char === 0x09 && ctrl`
21
+ whatever vk it rides, with Shift still mapping to Shift+Tab; both record
22
+ shapes are pinned by regression rows in `win32-input-mode.test.ts`.
23
+
10
24
  ### Team dispatch supervisor (team-overseer)
11
25
 
12
26
  - **A time ceiling on a subagent is now an inspection trigger, not a death
@@ -103,6 +103,8 @@ export declare class ContextMeter {
103
103
  private last;
104
104
  /** Whether the history has changed since the last measurement. */
105
105
  private dirty;
106
+ /** Whether the one-per-run empty-measurement warning has been spent. */
107
+ private hasWarnedUnmeasuredInput;
106
108
  private prefixLast;
107
109
  private sampledPrompt;
108
110
  private sampledVersion;
@@ -206,6 +208,16 @@ export declare class ContextMeter {
206
208
  */
207
209
  attach(subscribe: (listener: (event: AgentEvent) => void) => () => void): void;
208
210
  dispose(): void;
211
+ /**
212
+ * ONE warn per run (context-usage-zero-input-tokens B). A single day of
213
+ * real usage logged 1,973 zero-input turns; a per-turn warning is a flood
214
+ * that teaches whoever reads the file to ignore it.
215
+ *
216
+ * The logger is resolved LAZILY rather than captured at construction:
217
+ * hosts build this class before `installLogging()`, and a child captured
218
+ * then would stay bound to the silent pre-install logger forever.
219
+ */
220
+ private warnUnmeasuredInputOnce;
209
221
  private onAgentEvent;
210
222
  /**
211
223
  * The structural belt for a missed invalidation site (I-9).
@@ -27,7 +27,8 @@
27
27
  * `CompactionWiring.settlePending`'s own synchronous code (I-9), and why
28
28
  * `current()` carries a structural belt for a missed invalidation site.
29
29
  */
30
- import { computeEstimateOffset, computePressure } from './pressure.js';
30
+ import { getLogger } from '../logging/logger.js';
31
+ import { computeEstimateOffset, computePressure, isInputSideUnmeasured } from './pressure.js';
31
32
  /**
32
33
  * How long a scheduled publish waits.
33
34
  *
@@ -103,6 +104,8 @@ export class ContextMeter {
103
104
  last = null;
104
105
  /** Whether the history has changed since the last measurement. */
105
106
  dirty = true;
107
+ /** Whether the one-per-run empty-measurement warning has been spent. */
108
+ hasWarnedUnmeasuredInput = false;
106
109
  prefixLast;
107
110
  sampledPrompt;
108
111
  sampledVersion;
@@ -174,6 +177,21 @@ export class ContextMeter {
174
177
  * better number to wait for.
175
178
  */
176
179
  onTurnEnd(usage) {
180
+ if (isInputSideUnmeasured(usage)) {
181
+ // A usage that discloses NOTHING on the input side is not a
182
+ // measurement of this history (context-usage-zero-input-tokens B):
183
+ // arming the measured branch with it collapses occupancy to the
184
+ // turn's own outputTokens and strips the `~` off the gauge. KEEP
185
+ // WHATEVER IS STILL TRUSTED - an earlier good measurement of a
186
+ // prefix that still exists (the history only grows between splices,
187
+ // so "old good base + appended estimate" stays self-consistent), and
188
+ // the estimate offset it calibrated - and re-measure NOW so the
189
+ // gauge degrades in this frame instead of after the next turn.
190
+ this.warnUnmeasuredInputOnce(usage);
191
+ this.dirty = true;
192
+ this.current();
193
+ return;
194
+ }
177
195
  const messages = this.deps.getMessages();
178
196
  const systemPrompt = this.readSystemPrompt();
179
197
  this.lastUsage = usage;
@@ -307,6 +325,21 @@ export class ContextMeter {
307
325
  // =========================================================================
308
326
  // Internals
309
327
  // =========================================================================
328
+ /**
329
+ * ONE warn per run (context-usage-zero-input-tokens B). A single day of
330
+ * real usage logged 1,973 zero-input turns; a per-turn warning is a flood
331
+ * that teaches whoever reads the file to ignore it.
332
+ *
333
+ * The logger is resolved LAZILY rather than captured at construction:
334
+ * hosts build this class before `installLogging()`, and a child captured
335
+ * then would stay bound to the silent pre-install logger forever.
336
+ */
337
+ warnUnmeasuredInputOnce(usage) {
338
+ if (this.hasWarnedUnmeasuredInput)
339
+ return;
340
+ this.hasWarnedUnmeasuredInput = true;
341
+ getLogger().warn('compaction', 'context_input_tokens_unreported', { in: usage.inputTokens, out: usage.outputTokens });
342
+ }
310
343
  onAgentEvent(event) {
311
344
  switch (event.type) {
312
345
  case 'turn_end':
@@ -40,6 +40,35 @@ import type { Pressure } from './types.js';
40
40
  * the feature is broken, and turns it off.
41
41
  */
42
42
  export declare function occupiedTokens(usage: TokenUsage): number;
43
+ /**
44
+ * Whether a usage measured ANYTHING on the input side
45
+ * (context-usage-zero-input-tokens B).
46
+ *
47
+ * THE EMPTY MEASUREMENT THIS EXISTS FOR. A turn that billed output tokens
48
+ * while reporting inputTokens 0 AND no cache terms did not measure the
49
+ * request - no real request is free of input. Anthropic-COMPATIBLE relays
50
+ * emit exactly this shape (bigmodel on every turn, Kimi intermittently),
51
+ * and the meter used to arm its measured branch with it: occupancy collapsed
52
+ * to the turn's own outputTokens, the `~` honesty marker vanished with
53
+ * `source: 'usage'`, and the trigger - which reads the same number - never
54
+ * fired. The defense is to not trust such a usage as a measurement.
55
+ *
56
+ * THE CACHE TERMS ARE PART OF THE INPUT SIDE, DELIBERATELY. Anthropic's real
57
+ * `input_tokens` EXCLUDES cached tokens, so a deeply cached turn reports a
58
+ * tiny `input_tokens` next to a huge `cache_read_input_tokens`. That IS a
59
+ * valid measurement (C-14), and judging on `inputTokens === 0` alone would
60
+ * reject it and strand a correct gauge on the estimator.
61
+ *
62
+ * `outputTokens > 0` separates "the stream ran, billed output, and disclosed
63
+ * no input" from a degenerate all-zero usage, which the reviewed predicate
64
+ * deliberately does not speak for.
65
+ *
66
+ * SINGLE AUTHORITY. The meter consumes this, and any future observability
67
+ * hint (fix C) must too: two inline copies of "did the gateway disclose
68
+ * input" would drift apart exactly the way the gauge and the trigger once
69
+ * could (R-11).
70
+ */
71
+ export declare function isInputSideUnmeasured(usage: TokenUsage): boolean;
43
72
  /**
44
73
  * The calibration offset for this turn (D-23 / P1-11).
45
74
  *
@@ -44,6 +44,38 @@ export function occupiedTokens(usage) {
44
44
  (usage.cacheWriteTokens ?? 0) +
45
45
  usage.outputTokens);
46
46
  }
47
+ /**
48
+ * Whether a usage measured ANYTHING on the input side
49
+ * (context-usage-zero-input-tokens B).
50
+ *
51
+ * THE EMPTY MEASUREMENT THIS EXISTS FOR. A turn that billed output tokens
52
+ * while reporting inputTokens 0 AND no cache terms did not measure the
53
+ * request - no real request is free of input. Anthropic-COMPATIBLE relays
54
+ * emit exactly this shape (bigmodel on every turn, Kimi intermittently),
55
+ * and the meter used to arm its measured branch with it: occupancy collapsed
56
+ * to the turn's own outputTokens, the `~` honesty marker vanished with
57
+ * `source: 'usage'`, and the trigger - which reads the same number - never
58
+ * fired. The defense is to not trust such a usage as a measurement.
59
+ *
60
+ * THE CACHE TERMS ARE PART OF THE INPUT SIDE, DELIBERATELY. Anthropic's real
61
+ * `input_tokens` EXCLUDES cached tokens, so a deeply cached turn reports a
62
+ * tiny `input_tokens` next to a huge `cache_read_input_tokens`. That IS a
63
+ * valid measurement (C-14), and judging on `inputTokens === 0` alone would
64
+ * reject it and strand a correct gauge on the estimator.
65
+ *
66
+ * `outputTokens > 0` separates "the stream ran, billed output, and disclosed
67
+ * no input" from a degenerate all-zero usage, which the reviewed predicate
68
+ * deliberately does not speak for.
69
+ *
70
+ * SINGLE AUTHORITY. The meter consumes this, and any future observability
71
+ * hint (fix C) must too: two inline copies of "did the gateway disclose
72
+ * input" would drift apart exactly the way the gauge and the trigger once
73
+ * could (R-11).
74
+ */
75
+ export function isInputSideUnmeasured(usage) {
76
+ const inputSide = usage.inputTokens + (usage.cacheReadTokens ?? 0) + (usage.cacheWriteTokens ?? 0);
77
+ return inputSide === 0 && usage.outputTokens > 0;
78
+ }
47
79
  /**
48
80
  * The calibration offset for this turn (D-23 / P1-11).
49
81
  *
@@ -216,6 +216,18 @@ export function translateWin32Key(record) {
216
216
  return char;
217
217
  if (!ctrl && alt)
218
218
  return `\x1b${char}`;
219
+ // Ctrl+I, letter-vk shape: a physical Ctrl+I keeps the LETTER vk
220
+ // (0x49, the same Ctrl+letter probe rule that reports Ctrl+A as 65
221
+ // and Ctrl+J as 74) while `char` is already the Tab byte, so the
222
+ // VK_TAB switch above never sees the record. 0x09 is both the Tab
223
+ // byte and VK_TAB, so the tell-apart from plain Tab is ctrl alone,
224
+ // and Shift keeps ESC [ Z exactly like that switch: the mode toggle
225
+ // stays intact and Ctrl+Shift+I stays on the Shift branch.
226
+ if (ctrl && record.char === VK_TAB) {
227
+ if (!shift && !alt)
228
+ return INDEX_KEY_FRAME;
229
+ return shift ? '\x1b[Z' : '\t';
230
+ }
219
231
  return ctrl ? `${alt ? '\x1b' : ''}${char}` : char;
220
232
  }
221
233
  if (isPrintable(record.char)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aragon-agent/cli",
3
- "version": "0.6.18",
3
+ "version": "0.6.20",
4
4
  "description": "AragonAgent CLI — a Claude-Code / Codex-style interactive terminal UI (TUI) on top of the @aragon-agent/core engine. Installable and runnable via npm / npx.",
5
5
  "license": "MIT",
6
6
  "author": "AragonAgent contributors",
@@ -56,7 +56,7 @@
56
56
  "prepublishOnly": "npm run build"
57
57
  },
58
58
  "dependencies": {
59
- "@aragon-agent/core": "^0.2.30",
59
+ "@aragon-agent/core": "^0.2.32",
60
60
  "chalk": "^5.6.2",
61
61
  "cli-highlight": "^2.0.0",
62
62
  "commander": "^12.1.0",