@aragon-agent/cli 0.6.1 → 0.6.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/CHANGELOG.md +216 -0
- package/README.md +241 -18
- package/dist/agent/agent-mode.d.ts +39 -0
- package/dist/agent/agent-mode.js +36 -0
- package/dist/agent/controller.d.ts +74 -1
- package/dist/agent/controller.js +200 -1
- package/dist/agent/headless.d.ts +8 -0
- package/dist/agent/headless.js +47 -0
- package/dist/agent/reducer.d.ts +120 -0
- package/dist/agent/reducer.js +201 -2
- package/dist/cli.js +297 -6
- package/dist/commands/builtins.js +73 -23
- package/dist/commands/registry.d.ts +27 -0
- package/dist/compaction/archive.d.ts +175 -0
- package/dist/compaction/archive.js +267 -0
- package/dist/compaction/child.d.ts +97 -0
- package/dist/compaction/child.js +113 -0
- package/dist/compaction/command.d.ts +41 -0
- package/dist/compaction/command.js +342 -0
- package/dist/compaction/compactor.d.ts +268 -0
- package/dist/compaction/compactor.js +689 -0
- package/dist/compaction/digest.d.ts +62 -0
- package/dist/compaction/digest.js +164 -0
- package/dist/compaction/limits.d.ts +165 -0
- package/dist/compaction/limits.js +172 -0
- package/dist/compaction/pressure.d.ts +135 -0
- package/dist/compaction/pressure.js +167 -0
- package/dist/compaction/summarize-call.d.ts +70 -0
- package/dist/compaction/summarize-call.js +86 -0
- package/dist/compaction/summary-prompt.d.ts +85 -0
- package/dist/compaction/summary-prompt.js +197 -0
- package/dist/compaction/tail-budget.d.ts +52 -0
- package/dist/compaction/tail-budget.js +67 -0
- package/dist/compaction/types.d.ts +179 -0
- package/dist/compaction/types.js +22 -0
- package/dist/compaction/wiring.d.ts +255 -0
- package/dist/compaction/wiring.js +556 -0
- package/dist/config/app-paths.d.ts +14 -0
- package/dist/config/app-paths.js +16 -0
- package/dist/config/cli-commands.d.ts +41 -0
- package/dist/config/cli-commands.js +77 -1
- package/dist/config/env.d.ts +4 -1
- package/dist/config/env.js +92 -2
- package/dist/config/load.d.ts +31 -0
- package/dist/config/load.js +63 -1
- package/dist/config/schema.d.ts +159 -0
- package/dist/config/schema.js +137 -0
- package/dist/config/store.js +17 -1
- package/dist/config/ui-state.d.ts +44 -6
- package/dist/config/ui-state.js +21 -12
- package/dist/diagnostics/doctor.js +41 -1
- package/dist/exec/events.d.ts +50 -1
- package/dist/exec/index.js +8 -4
- package/dist/exec/runner.js +44 -2
- package/dist/input/mouse-events.d.ts +31 -5
- package/dist/input/mouse-events.js +86 -27
- package/dist/input/stdin-mouse-filter.d.ts +8 -3
- package/dist/logging/install.d.ts +51 -1
- package/dist/logging/install.js +28 -0
- package/dist/logging/logger.d.ts +1 -1
- package/dist/session/cli-commands.js +23 -1
- package/dist/session/persist.js +19 -0
- package/dist/session/store.d.ts +18 -0
- package/dist/session/store.js +59 -2
- package/dist/team/report.js +7 -1
- package/dist/team/runtime.d.ts +10 -0
- package/dist/team/runtime.js +7 -0
- package/dist/team/subagent.d.ts +31 -1
- package/dist/team/subagent.js +42 -15
- package/dist/team/types.d.ts +8 -0
- package/dist/ui/ActivityLine.d.ts +19 -1
- package/dist/ui/ActivityLine.js +10 -2
- package/dist/ui/App.d.ts +48 -1
- package/dist/ui/App.js +317 -17
- package/dist/ui/Composer.d.ts +26 -1
- package/dist/ui/Composer.js +6 -6
- package/dist/ui/PromptInput.d.ts +34 -1
- package/dist/ui/PromptInput.js +54 -3
- package/dist/ui/StatusBar.d.ts +40 -0
- package/dist/ui/StatusBar.js +13 -4
- package/dist/ui/TeamPanel.js +7 -1
- package/dist/ui/Transcript.d.ts +21 -1
- package/dist/ui/Transcript.js +32 -2
- package/dist/ui/clipboard.d.ts +52 -0
- package/dist/ui/clipboard.js +91 -0
- package/dist/ui/entries/CompactionCard.d.ts +75 -0
- package/dist/ui/entries/CompactionCard.js +158 -0
- package/dist/ui/frame-differ.d.ts +46 -0
- package/dist/ui/frame-differ.js +94 -10
- package/dist/ui/gauge.d.ts +16 -1
- package/dist/ui/gauge.js +25 -6
- package/dist/ui/glyphs.d.ts +11 -0
- package/dist/ui/glyphs.js +2 -0
- package/dist/ui/layout/ScrollViewport.d.ts +43 -6
- package/dist/ui/layout/ScrollViewport.js +128 -21
- package/dist/ui/layout/budget.d.ts +10 -3
- package/dist/ui/layout/budget.js +10 -3
- package/dist/ui/layout/follow-state.d.ts +137 -0
- package/dist/ui/layout/follow-state.js +150 -0
- package/dist/ui/layout/virtual-window.d.ts +11 -0
- package/dist/ui/layout/virtual-window.js +34 -0
- package/dist/ui/overlays/HelpOverlay.js +18 -2
- package/dist/ui/overlays/SettingsScreen.d.ts +47 -1
- package/dist/ui/overlays/SettingsScreen.js +60 -1
- package/dist/ui/palettes.d.ts +16 -0
- package/dist/ui/palettes.js +6 -0
- package/dist/ui/screen.d.ts +22 -0
- package/dist/ui/screen.js +70 -8
- package/dist/ui/selection/highlight.d.ts +39 -0
- package/dist/ui/selection/highlight.js +64 -0
- package/dist/ui/selection/screen-mirror.d.ts +24 -0
- package/dist/ui/selection/screen-mirror.js +35 -0
- package/dist/ui/selection/selection-controller.d.ts +88 -0
- package/dist/ui/selection/selection-controller.js +282 -0
- package/dist/ui/selection/selection.d.ts +90 -0
- package/dist/ui/selection/selection.js +223 -0
- package/dist/ui/stdout-frame-writer.d.ts +33 -0
- package/dist/ui/stdout-frame-writer.js +15 -0
- package/dist/ui/theme.d.ts +11 -0
- package/dist/ui/theme.js +2 -0
- package/dist/ui/transcript-text.js +23 -0
- package/dist/ui/use-startup-notices.d.ts +62 -5
- package/dist/ui/use-startup-notices.js +81 -17
- package/dist/ui/use-wheel-routing.js +8 -0
- package/dist/ui/win-vt-force.d.ts +136 -0
- package/dist/ui/win-vt-force.js +237 -0
- package/package.json +4 -2
package/dist/config/schema.js
CHANGED
|
@@ -169,6 +169,42 @@ export const DEFAULT_DIFF_RENDER = true;
|
|
|
169
169
|
*/
|
|
170
170
|
export const DEFAULT_SYNC_OUTPUT = true;
|
|
171
171
|
// ---------------------------------------------------------------------------
|
|
172
|
+
// Selection + scroll follow (tui-selection-and-scroll-follow §6.1)
|
|
173
|
+
// ---------------------------------------------------------------------------
|
|
174
|
+
/**
|
|
175
|
+
* Drag-select + copy, and — because §4.4.1 gates `\x1b[?1002h` on exactly this
|
|
176
|
+
* value — whether motion reporting is requested from the terminal at all.
|
|
177
|
+
*
|
|
178
|
+
* That gating is what makes `false` BYTE-IDENTICAL to a pre-feature session
|
|
179
|
+
* rather than merely equivalent (AC-8 / D-15). Ignored when `mouse` is false,
|
|
180
|
+
* since there is no capture to select with.
|
|
181
|
+
*/
|
|
182
|
+
export const DEFAULT_MOUSE_SELECT = true;
|
|
183
|
+
/**
|
|
184
|
+
* Idle delay before a paused viewport returns to the newest line, in ms.
|
|
185
|
+
*
|
|
186
|
+
* Five seconds is chosen to be LONGER than a glance and SHORTER than a read.
|
|
187
|
+
* `0` disables auto-resume entirely, and it is a real value rather than a
|
|
188
|
+
* sentinel-shaped default: `shouldArmResume` refuses on it before any timer is
|
|
189
|
+
* created (AC-9), so the revert for Rule B is one config key.
|
|
190
|
+
*
|
|
191
|
+
* NO CLI FLAG (§6.1). It is a comfort preference, set once
|
|
192
|
+
* (`aragon config set scrollResumeMs 8000`) and then forgotten — the same
|
|
193
|
+
* argument this file already makes for `historyEnabled`. Adding a flag for every
|
|
194
|
+
* scalar is how a CLI ends up with sixty of them.
|
|
195
|
+
*/
|
|
196
|
+
export const DEFAULT_SCROLL_RESUME_MS = 5000;
|
|
197
|
+
const SCROLL_RESUME_RANGE = { min: 0, max: 120_000 };
|
|
198
|
+
/**
|
|
199
|
+
* `clampIntAllowingZero`, and the choice is load-bearing: `clampInt` delegates to
|
|
200
|
+
* `coercePositiveInt`, which returns the FALLBACK for `n <= 0` — so `0` would
|
|
201
|
+
* silently become 5000 and the documented off switch would not be an off switch.
|
|
202
|
+
* Exactly the trap `retry.maxRetries` needed this helper for.
|
|
203
|
+
*/
|
|
204
|
+
export function clampScrollResumeMs(v, fallback) {
|
|
205
|
+
return clampIntAllowingZero(v, fallback, SCROLL_RESUME_RANGE);
|
|
206
|
+
}
|
|
207
|
+
// ---------------------------------------------------------------------------
|
|
172
208
|
// Plan mode (plan-mode §4.4)
|
|
173
209
|
//
|
|
174
210
|
// THREE FLAT KEYS, not a nested `planMode` object. `store.ts` documents that its
|
|
@@ -669,6 +705,99 @@ function isRegistryUrl(raw) {
|
|
|
669
705
|
return false;
|
|
670
706
|
}
|
|
671
707
|
}
|
|
708
|
+
export const DEFAULT_COMPACTION_CONFIG = {
|
|
709
|
+
enabled: true,
|
|
710
|
+
threshold: 0.9,
|
|
711
|
+
warnThreshold: 0.75,
|
|
712
|
+
keepRecentTurns: 4,
|
|
713
|
+
useFastTier: true,
|
|
714
|
+
onFailure: 'truncate',
|
|
715
|
+
subagents: true,
|
|
716
|
+
archive: true,
|
|
717
|
+
};
|
|
718
|
+
const THRESHOLD_RANGE = { min: 0.5, max: 0.95 };
|
|
719
|
+
const WARN_THRESHOLD_FLOOR = 0.4;
|
|
720
|
+
/** The gap the warn mark must stay below the trigger by. */
|
|
721
|
+
const WARN_THRESHOLD_GAP = 0.05;
|
|
722
|
+
const KEEP_RECENT_TURNS_RANGE = { min: 1, max: 20 };
|
|
723
|
+
const COMPACTION_FAILURE_MODES = ['truncate', 'stop'];
|
|
724
|
+
/**
|
|
725
|
+
* Round a ratio to two decimals — the precision a percentage actually has.
|
|
726
|
+
*
|
|
727
|
+
* BINARY FLOATING POINT MAKES `0.95 - 0.05` EQUAL `0.8999999999999999`, and both
|
|
728
|
+
* numbers here are produced by subtraction (`threshold - WARN_THRESHOLD_GAP`) and
|
|
729
|
+
* by division (`parseThresholdInput` turning `90` into `90/100`). Without this,
|
|
730
|
+
* `aragon config list` shows a seventeen-digit value for a setting the user typed
|
|
731
|
+
* as `90%`, and `config.json` records one — which looks like corruption and
|
|
732
|
+
* invites a hand-edit that the clamp then has to defend against.
|
|
733
|
+
*
|
|
734
|
+
* Two decimals is exact for every value the range can hold: the clamp is
|
|
735
|
+
* `[0.5, 0.95]` and the UI reads it as a whole percentage.
|
|
736
|
+
*/
|
|
737
|
+
function ratio(value) {
|
|
738
|
+
return Math.round(value * 100) / 100;
|
|
739
|
+
}
|
|
740
|
+
function failureMode(value, fallback) {
|
|
741
|
+
return typeof value === 'string' && COMPACTION_FAILURE_MODES.includes(value)
|
|
742
|
+
? value
|
|
743
|
+
: fallback;
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* THE single gate for every compaction-config read AND write, mirroring the six
|
|
747
|
+
* clamps above for the reason all of them record: hardening only the read path
|
|
748
|
+
* leaves a bad value on disk that reverts to the default on every launch, which
|
|
749
|
+
* presents to the user as "my setting won't stick".
|
|
750
|
+
*
|
|
751
|
+
* THE ORDER IS FIXED AND IT MATTERS (§4.2 / P2-5). `threshold` is clamped to
|
|
752
|
+
* `[0.5, 0.95]` FIRST, then `warnThreshold` to
|
|
753
|
+
* `[0.4, clampedThreshold - 0.05]`. The other order lets a file holding
|
|
754
|
+
* `{threshold: 0.6, warnThreshold: 0.9}` produce a warn mark ABOVE the trigger —
|
|
755
|
+
* a gauge that turns amber after it has already gone red. Both bounds of the
|
|
756
|
+
* second range stay reachable at the extremes: `threshold` floored at `0.5`
|
|
757
|
+
* gives `warnThreshold` a range of `[0.4, 0.45]`.
|
|
758
|
+
*/
|
|
759
|
+
export function clampCompactionConfig(raw) {
|
|
760
|
+
const src = (raw && typeof raw === 'object' ? raw : {});
|
|
761
|
+
const threshold = ratio(clampNumber(src.threshold, DEFAULT_COMPACTION_CONFIG.threshold, THRESHOLD_RANGE));
|
|
762
|
+
const warnCeiling = ratio(threshold - WARN_THRESHOLD_GAP);
|
|
763
|
+
return {
|
|
764
|
+
enabled: bool(src.enabled, DEFAULT_COMPACTION_CONFIG.enabled),
|
|
765
|
+
threshold,
|
|
766
|
+
warnThreshold: ratio(clampNumber(src.warnThreshold, DEFAULT_COMPACTION_CONFIG.warnThreshold, {
|
|
767
|
+
min: Math.min(WARN_THRESHOLD_FLOOR, warnCeiling),
|
|
768
|
+
max: warnCeiling,
|
|
769
|
+
})),
|
|
770
|
+
keepRecentTurns: clampInt(src.keepRecentTurns, DEFAULT_COMPACTION_CONFIG.keepRecentTurns, KEEP_RECENT_TURNS_RANGE),
|
|
771
|
+
useFastTier: bool(src.useFastTier, DEFAULT_COMPACTION_CONFIG.useFastTier),
|
|
772
|
+
// CLAMPED TO THE DEFAULT, NEVER REJECTED — the discipline every other key in
|
|
773
|
+
// this file follows. `ARAGON_COMPACTION_ON_FAILURE=garbage` resolves to
|
|
774
|
+
// `truncate` and throws nothing.
|
|
775
|
+
onFailure: failureMode(src.onFailure, DEFAULT_COMPACTION_CONFIG.onFailure),
|
|
776
|
+
subagents: bool(src.subagents, DEFAULT_COMPACTION_CONFIG.subagents),
|
|
777
|
+
archive: bool(src.archive, DEFAULT_COMPACTION_CONFIG.archive),
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
/**
|
|
781
|
+
* Parse `0.9`, `90%` or `90` into a ratio, or `null` when it is neither.
|
|
782
|
+
*
|
|
783
|
+
* `null` RATHER THAN A CLAMPED DEFAULT, because this is a COMMAND argument and
|
|
784
|
+
* not a config value: `/compact threshold banana` must say so rather than
|
|
785
|
+
* silently setting 0.9 and reporting success.
|
|
786
|
+
*/
|
|
787
|
+
export function parseThresholdInput(raw) {
|
|
788
|
+
const trimmed = raw.trim().replace(/%$/, '');
|
|
789
|
+
if (trimmed.length === 0)
|
|
790
|
+
return null;
|
|
791
|
+
const n = Number.parseFloat(trimmed);
|
|
792
|
+
if (!Number.isFinite(n))
|
|
793
|
+
return null;
|
|
794
|
+
// `90` means 90 %, `0.9` means 0.9. The ambiguity is only at `1`, which is
|
|
795
|
+
// above the clamp ceiling either way.
|
|
796
|
+
const ratio = n > 1 ? n / 100 : n;
|
|
797
|
+
if (ratio <= 0 || ratio > 1)
|
|
798
|
+
return null;
|
|
799
|
+
return ratio;
|
|
800
|
+
}
|
|
672
801
|
export const DEFAULT_CONFIG = {
|
|
673
802
|
version: CONFIG_VERSION,
|
|
674
803
|
provider: 'anthropic',
|
|
@@ -703,6 +832,8 @@ export const DEFAULT_CONFIG = {
|
|
|
703
832
|
density: 'comfortable',
|
|
704
833
|
hints: true,
|
|
705
834
|
mouse: true,
|
|
835
|
+
mouseSelect: DEFAULT_MOUSE_SELECT,
|
|
836
|
+
scrollResumeMs: DEFAULT_SCROLL_RESUME_MS,
|
|
706
837
|
planModeDefault: false,
|
|
707
838
|
planModeMaxAskRounds: DEFAULT_PLAN_MAX_ASK_ROUNDS,
|
|
708
839
|
planModeHumanTimeoutMs: DEFAULT_PLAN_HUMAN_TIMEOUT_MS,
|
|
@@ -728,6 +859,12 @@ export const DEFAULT_CONFIG = {
|
|
|
728
859
|
// (`cfg.update.mode !== 'off'`) would read TRUE and the feature would be on
|
|
729
860
|
// for a reason nobody could point at.
|
|
730
861
|
update: DEFAULT_UPDATE_CONFIG,
|
|
862
|
+
// And one section later again, for the fourth time. Without this line a user
|
|
863
|
+
// whose `config.json` predates compaction resolves `config.compaction.enabled`
|
|
864
|
+
// to `undefined`, which is FALSY — so a feature that is `true` by default would
|
|
865
|
+
// arrive OFF for every existing user, which is the one direction this
|
|
866
|
+
// particular default must never fail in.
|
|
867
|
+
compaction: DEFAULT_COMPACTION_CONFIG,
|
|
731
868
|
};
|
|
732
869
|
/**
|
|
733
870
|
* Runtime state that used to be stored in `config.json` by mistake
|
package/dist/config/store.js
CHANGED
|
@@ -14,7 +14,7 @@ import { dirname } from 'node:path';
|
|
|
14
14
|
import process from 'node:process';
|
|
15
15
|
import { getConfigPath, getSessionsDir } from './app-paths.js';
|
|
16
16
|
import { registerSecretsFrom } from '../logging/secret-registry.js';
|
|
17
|
-
import { CONFIG_VERSION, DEFAULT_CONFIG, clampFastConfig, clampLogConfig, clampMaxTokens, clampRetryConfig, clampSkillsConfig, clampTeamConfig, clampTodoConfig, clampUpdateConfig, isAutoToken, stripLegacyStateKeys, } from './schema.js';
|
|
17
|
+
import { CONFIG_VERSION, DEFAULT_CONFIG, clampCompactionConfig, clampFastConfig, clampLogConfig, clampMaxTokens, clampRetryConfig, clampSkillsConfig, clampTeamConfig, clampTodoConfig, clampUpdateConfig, isAutoToken, stripLegacyStateKeys, } from './schema.js';
|
|
18
18
|
/**
|
|
19
19
|
* Normalize `maxTokens` for both directions of the config file.
|
|
20
20
|
*
|
|
@@ -139,6 +139,15 @@ export function loadPersistedConfig() {
|
|
|
139
139
|
// predates auto-update, which is all of them. Without it `config.update.mode`
|
|
140
140
|
// reads `undefined`, and the §3.8 gate tests `!== 'off'`.
|
|
141
141
|
update: clampUpdateConfig({ ...DEFAULT_CONFIG.update, ...(partial.update ?? {}) }),
|
|
142
|
+
// `compaction` is the ninth. Same one-level rule, same merge — and the READ
|
|
143
|
+
// half supplies a whole section to every user whose `config.json` predates
|
|
144
|
+
// this feature, which is all of them. Without it `config.compaction.enabled`
|
|
145
|
+
// reads `undefined`, which is FALSY, so a feature that is `true` by default
|
|
146
|
+
// would arrive off for every existing user.
|
|
147
|
+
compaction: clampCompactionConfig({
|
|
148
|
+
...DEFAULT_CONFIG.compaction,
|
|
149
|
+
...(partial.compaction ?? {}),
|
|
150
|
+
}),
|
|
142
151
|
};
|
|
143
152
|
}
|
|
144
153
|
/**
|
|
@@ -208,6 +217,13 @@ export function updatePersistedConfig(patch) {
|
|
|
208
217
|
// enterprise mirror the user configured last month is gone with no error
|
|
209
218
|
// anywhere. AC-20 is the assertion that proves both halves exist.
|
|
210
219
|
update: clampUpdateConfig({ ...current.update, ...(patch.update ?? {}) }),
|
|
220
|
+
// The ninth section, and the fourth time this file records the same trap.
|
|
221
|
+
// `/compact threshold 0.8` sends `{ compaction: { threshold: 0.8 } }`;
|
|
222
|
+
// without this line the shallow top-level spread REPLACES the section,
|
|
223
|
+
// `enabled` is written absent, and a user who deliberately turned compaction
|
|
224
|
+
// OFF finds it back on tomorrow — a feature that spends money on their behalf
|
|
225
|
+
// re-enabling itself because they nudged a threshold.
|
|
226
|
+
compaction: clampCompactionConfig({ ...current.compaction, ...(patch.compaction ?? {}) }),
|
|
211
227
|
};
|
|
212
228
|
writeConfigFile(merged);
|
|
213
229
|
return merged;
|
|
@@ -32,11 +32,35 @@ export interface UiState {
|
|
|
32
32
|
schema: number;
|
|
33
33
|
/** Lifetime submit count; drives the composer hint fade (`HINT_FADE_AFTER`). */
|
|
34
34
|
submitCount: number;
|
|
35
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* Whether the one-shot "hold Shift to select text" notice has been shown.
|
|
37
|
+
*
|
|
38
|
+
* DELIBERATELY NOT READ BY THE LIVE GATE ANY MORE (tui-selection-and-scroll-
|
|
39
|
+
* follow P1-8). It is kept in the shape so `importLegacyUiState` still has
|
|
40
|
+
* somewhere to put a migrated value and so a file written by 0.6.2 round-trips
|
|
41
|
+
* unchanged; `mouseNoticeVersion` below is what the notice now consults.
|
|
42
|
+
*/
|
|
36
43
|
mouseNoticeSeen: boolean;
|
|
37
44
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
45
|
+
* Which revision of the mouse notice this user has already been shown
|
|
46
|
+
* (`0` = none).
|
|
47
|
+
*
|
|
48
|
+
* A VERSION BESIDE THE BOOLEAN, NOT A BUMPED SCHEMA, and this is the second
|
|
49
|
+
* time this file has had to say so. The audience for the CORRECTED text — "drag
|
|
50
|
+
* to select, releasing copies" — is exactly the set of users who already have
|
|
51
|
+
* `mouseNoticeSeen: true` on disk from the old "hold Shift" text, so a boolean
|
|
52
|
+
* can only say "shown", never "shown WHAT". And bumping `UI_STATE_SCHEMA`, the
|
|
53
|
+
* nearest thing to "bump the key", makes `readFromDisk` discard the WHOLE
|
|
54
|
+
* object: every user's `submitCount` resets and the composer hint un-fades for
|
|
55
|
+
* all of them, to re-show one notice.
|
|
56
|
+
*
|
|
57
|
+
* `vtInputNoticeVersion` below is the precedent, written about this exact
|
|
58
|
+
* situation one feature earlier.
|
|
59
|
+
*/
|
|
60
|
+
mouseNoticeVersion: number;
|
|
61
|
+
/**
|
|
62
|
+
* Which revision of the "this Windows console cannot deliver Shift+Tab or the
|
|
63
|
+
* wheel" notice this user has already been shown (`0` = none).
|
|
40
64
|
*
|
|
41
65
|
* A SEPARATE KEY FROM `mouseNoticeSeen`, AND THAT IS THE WHOLE POINT. The
|
|
42
66
|
* people this notice exists for are precisely the ones who have already
|
|
@@ -44,12 +68,22 @@ export interface UiState {
|
|
|
44
68
|
* them, and therefore already have `mouseNoticeSeen: true` on disk. Folding
|
|
45
69
|
* the two would make the fix inert for its entire audience, in silence.
|
|
46
70
|
*
|
|
71
|
+
* A VERSION RATHER THAN A BOOLEAN, for the second turn of that same screw.
|
|
72
|
+
* The predecessor of this field was `vtInputNoticeSeen: boolean`, written by
|
|
73
|
+
* builds whose text knew nothing about `MODE_TOGGLE_KEYS.fallback` — and the
|
|
74
|
+
* audience for the text that DOES name it is, once again, exactly the set of
|
|
75
|
+
* users who already have the old flag set. A boolean can only say "shown",
|
|
76
|
+
* never "shown WHAT", so the only way to reach them without re-showing the
|
|
77
|
+
* notice on every launch is to let the wording carry a number. That old key
|
|
78
|
+
* is deliberately not read: it is absent from the shape below, so a file
|
|
79
|
+
* written by 0.6.1 reads as `0` and replays exactly once.
|
|
80
|
+
*
|
|
47
81
|
* NOT ADDED TO `UI_STATE_SCHEMA`: bumping the schema makes `readFromDisk`
|
|
48
82
|
* discard the whole object, which would reset everyone's submit counter and
|
|
49
83
|
* re-show the mouse notice. A new field simply reads as its default on an
|
|
50
84
|
* older file, which is the behaviour wanted here anyway.
|
|
51
85
|
*/
|
|
52
|
-
|
|
86
|
+
vtInputNoticeVersion: number;
|
|
53
87
|
}
|
|
54
88
|
/** Read the state, caching it for the life of the process. Never throws. */
|
|
55
89
|
export declare function loadUiState(): UiState;
|
|
@@ -58,8 +92,12 @@ export declare function readSubmitCount(): number;
|
|
|
58
92
|
export declare function bumpSubmitCount(): number;
|
|
59
93
|
export declare function getMouseNoticeSeen(): boolean;
|
|
60
94
|
export declare function setMouseNoticeSeen(seen: boolean): void;
|
|
61
|
-
|
|
62
|
-
export declare function
|
|
95
|
+
/** `0` on a file written before the field existed — see the field's comment. */
|
|
96
|
+
export declare function getMouseNoticeVersion(): number;
|
|
97
|
+
export declare function setMouseNoticeVersion(version: number): void;
|
|
98
|
+
/** `0` on a file written before the field existed — see the field's comment. */
|
|
99
|
+
export declare function getVtInputNoticeVersion(): number;
|
|
100
|
+
export declare function setVtInputNoticeVersion(version: number): void;
|
|
63
101
|
/**
|
|
64
102
|
* Migration-only: fill in fields that are still at their default, and never
|
|
65
103
|
* overwrite a value already here. A second import — after a half-finished
|
package/dist/config/ui-state.js
CHANGED
|
@@ -35,13 +35,15 @@ const DEFAULT_UI_STATE = {
|
|
|
35
35
|
schema: UI_STATE_SCHEMA,
|
|
36
36
|
submitCount: 0,
|
|
37
37
|
mouseNoticeSeen: false,
|
|
38
|
-
|
|
38
|
+
mouseNoticeVersion: 0,
|
|
39
|
+
vtInputNoticeVersion: 0,
|
|
39
40
|
};
|
|
40
41
|
let memory = null;
|
|
41
|
-
|
|
42
|
+
/** Non-negative integer or `fallback`; shared by both numeric fields. */
|
|
43
|
+
function coerceCount(value, fallback) {
|
|
42
44
|
const n = typeof value === 'number' ? value : Number.parseInt(String(value ?? ''), 10);
|
|
43
45
|
if (!Number.isFinite(n) || n < 0)
|
|
44
|
-
return
|
|
46
|
+
return fallback;
|
|
45
47
|
return Math.floor(n);
|
|
46
48
|
}
|
|
47
49
|
/**
|
|
@@ -61,13 +63,12 @@ function readFromDisk() {
|
|
|
61
63
|
return { ...DEFAULT_UI_STATE };
|
|
62
64
|
return {
|
|
63
65
|
schema: UI_STATE_SCHEMA,
|
|
64
|
-
submitCount: coerceCount(parsed.submitCount),
|
|
66
|
+
submitCount: coerceCount(parsed.submitCount, DEFAULT_UI_STATE.submitCount),
|
|
65
67
|
mouseNoticeSeen: typeof parsed.mouseNoticeSeen === 'boolean'
|
|
66
68
|
? parsed.mouseNoticeSeen
|
|
67
69
|
: DEFAULT_UI_STATE.mouseNoticeSeen,
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
: DEFAULT_UI_STATE.vtInputNoticeSeen,
|
|
70
|
+
mouseNoticeVersion: coerceCount(parsed.mouseNoticeVersion, DEFAULT_UI_STATE.mouseNoticeVersion),
|
|
71
|
+
vtInputNoticeVersion: coerceCount(parsed.vtInputNoticeVersion, DEFAULT_UI_STATE.vtInputNoticeVersion),
|
|
71
72
|
};
|
|
72
73
|
}
|
|
73
74
|
catch {
|
|
@@ -127,11 +128,19 @@ export function getMouseNoticeSeen() {
|
|
|
127
128
|
export function setMouseNoticeSeen(seen) {
|
|
128
129
|
update({ mouseNoticeSeen: seen });
|
|
129
130
|
}
|
|
130
|
-
|
|
131
|
-
|
|
131
|
+
/** `0` on a file written before the field existed — see the field's comment. */
|
|
132
|
+
export function getMouseNoticeVersion() {
|
|
133
|
+
return loadUiState().mouseNoticeVersion;
|
|
132
134
|
}
|
|
133
|
-
export function
|
|
134
|
-
update({
|
|
135
|
+
export function setMouseNoticeVersion(version) {
|
|
136
|
+
update({ mouseNoticeVersion: version });
|
|
137
|
+
}
|
|
138
|
+
/** `0` on a file written before the field existed — see the field's comment. */
|
|
139
|
+
export function getVtInputNoticeVersion() {
|
|
140
|
+
return loadUiState().vtInputNoticeVersion;
|
|
141
|
+
}
|
|
142
|
+
export function setVtInputNoticeVersion(version) {
|
|
143
|
+
update({ vtInputNoticeVersion: version });
|
|
135
144
|
}
|
|
136
145
|
/**
|
|
137
146
|
* Migration-only: fill in fields that are still at their default, and never
|
|
@@ -143,7 +152,7 @@ export function importLegacyUiState(patch) {
|
|
|
143
152
|
const next = {};
|
|
144
153
|
if (patch.submitCount !== undefined &&
|
|
145
154
|
current.submitCount === DEFAULT_UI_STATE.submitCount) {
|
|
146
|
-
next.submitCount = coerceCount(patch.submitCount);
|
|
155
|
+
next.submitCount = coerceCount(patch.submitCount, DEFAULT_UI_STATE.submitCount);
|
|
147
156
|
}
|
|
148
157
|
if (patch.mouseNoticeSeen !== undefined &&
|
|
149
158
|
current.mouseNoticeSeen === DEFAULT_UI_STATE.mouseNoticeSeen) {
|
|
@@ -17,9 +17,11 @@ import { getHomeResolutionWarning, getSessionsDir, getUserDataDir } from '../con
|
|
|
17
17
|
import { readConfigFile } from '../config/store.js';
|
|
18
18
|
import { loadConfig, makeGetApiKey } from '../config/load.js';
|
|
19
19
|
import { isAdapterProvider, toRetryPolicy } from '../config/schema.js';
|
|
20
|
+
import { MODE_TOGGLE_KEYS } from '../agent/agent-mode.js';
|
|
21
|
+
import { supportsWindowsVtInput } from '../ui/win-vt-input.js';
|
|
20
22
|
const MIN_NODE_MAJOR = 18;
|
|
21
23
|
export async function runDoctorChecks(flags, opts = {}) {
|
|
22
|
-
const checks = [checkNode(), checkHome(), checkConfigFile()];
|
|
24
|
+
const checks = [checkNode(), ...checkVtInput(), checkHome(), checkConfigFile()];
|
|
23
25
|
const config = loadConfig(flags);
|
|
24
26
|
checks.push(checkProviderKey(config));
|
|
25
27
|
checks.push(checkModel(config));
|
|
@@ -42,6 +44,44 @@ function checkNode() {
|
|
|
42
44
|
remedy: `Install Node ${MIN_NODE_MAJOR} or newer.`,
|
|
43
45
|
};
|
|
44
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Whether this console can deliver `Shift+Tab`
|
|
49
|
+
* (shift-tab-mode-toggle-still-dead-on-windows, C3-2).
|
|
50
|
+
*
|
|
51
|
+
* `MIN_NODE_MAJOR` STAYS AT 18 and this is a separate check on purpose. The
|
|
52
|
+
* install floor is about whether aragon runs; this is about one key, and raising
|
|
53
|
+
* the floor to 22.17 would lock out a large, otherwise perfectly healthy Node 20
|
|
54
|
+
* population over it.
|
|
55
|
+
*
|
|
56
|
+
* `warn`, never `fail`: aragon turns the console bit on itself at startup, and
|
|
57
|
+
* `MODE_TOGGLE_KEYS.fallback` works whether or not that succeeded, so nothing
|
|
58
|
+
* here is actually broken - the exit code must not say it is.
|
|
59
|
+
*
|
|
60
|
+
* WHY IT EXISTS AT ALL: `checkNode` passes every version this bug affects, so a
|
|
61
|
+
* user whose mode toggle is dead ran the tool built to explain their machine and
|
|
62
|
+
* was told it was fine (analysis section 3.7). A diagnostic that is confidently
|
|
63
|
+
* wrong is worse than one that is silent - it ends the investigation.
|
|
64
|
+
*
|
|
65
|
+
* Returned as a list so the row simply does not exist off Windows, where there
|
|
66
|
+
* is no such thing as a console input mode to report on.
|
|
67
|
+
*/
|
|
68
|
+
function checkVtInput() {
|
|
69
|
+
if (process.platform !== 'win32')
|
|
70
|
+
return [];
|
|
71
|
+
if (supportsWindowsVtInput(process.platform, process.versions.node)) {
|
|
72
|
+
return [pass('vt-input', `v${process.versions.node} delivers Shift+Tab and mouse reports`)];
|
|
73
|
+
}
|
|
74
|
+
return [
|
|
75
|
+
{
|
|
76
|
+
name: 'vt-input',
|
|
77
|
+
verdict: 'warn',
|
|
78
|
+
detail: `Node v${process.versions.node} never enables ENABLE_VIRTUAL_TERMINAL_INPUT, so this ` +
|
|
79
|
+
'console reports Shift+Tab as a plain Tab and sends no mouse events',
|
|
80
|
+
remedy: `aragon turns that bit on at startup; if a policy blocks it, ${MODE_TOGGLE_KEYS.fallback} ` +
|
|
81
|
+
'and /plan toggle the mode too. Node 22.17+ (or 24.2+) removes the problem.',
|
|
82
|
+
},
|
|
83
|
+
];
|
|
84
|
+
}
|
|
45
85
|
function checkHome() {
|
|
46
86
|
const home = getUserDataDir();
|
|
47
87
|
const warning = getHomeResolutionWarning();
|
package/dist/exec/events.d.ts
CHANGED
|
@@ -157,6 +157,55 @@ export interface ExecFastReviewEvent {
|
|
|
157
157
|
model: string;
|
|
158
158
|
text: string;
|
|
159
159
|
}
|
|
160
|
+
/**
|
|
161
|
+
* Context compaction (context-auto-compaction §4.6).
|
|
162
|
+
*
|
|
163
|
+
* ONE ADDITIVE EVENT TYPE, AND `EXEC_SCHEMA_VERSION` DOES NOT MOVE — the rule
|
|
164
|
+
* this file's header states in the same words: adding a field or an event type
|
|
165
|
+
* never bumps it; renaming or removing one does. A consumer that ignores event
|
|
166
|
+
* types it does not know is unaffected.
|
|
167
|
+
*
|
|
168
|
+
* THE SUMMARY TEXT IS DELIBERATELY ABSENT (D-18). It can be tens of thousands of
|
|
169
|
+
* characters, no wrapper needs it, and the transcript card and the log both have
|
|
170
|
+
* it. What a wrapper does need is the SHAPE of what happened, which is here.
|
|
171
|
+
*/
|
|
172
|
+
export interface ExecCompactionEvent {
|
|
173
|
+
type: 'compaction';
|
|
174
|
+
sessionId: string;
|
|
175
|
+
turn: number;
|
|
176
|
+
subtype: 'start' | 'end';
|
|
177
|
+
trigger: 'pressure' | 'overflow' | 'manual';
|
|
178
|
+
/** `end` only. */
|
|
179
|
+
applied?: boolean;
|
|
180
|
+
/**
|
|
181
|
+
* `relieved` means the RETAINED turns were clipped and nothing was dropped
|
|
182
|
+
* (context-auto-compaction-hardening §3.3 / W2).
|
|
183
|
+
*
|
|
184
|
+
* A NEW MEMBER OF AN EXISTING UNION, WHICH DOES NOT MOVE `EXEC_SCHEMA_VERSION`
|
|
185
|
+
* either - the rule this file's header states covers adding, not renaming or
|
|
186
|
+
* removing. A consumer switching on this field already has to handle a value it
|
|
187
|
+
* does not know, because `none` and `truncated` were both already possible.
|
|
188
|
+
*/
|
|
189
|
+
mode?: 'summarized' | 'truncated' | 'relieved' | 'none';
|
|
190
|
+
reason?: string;
|
|
191
|
+
messagesBefore?: number;
|
|
192
|
+
messagesAfter?: number;
|
|
193
|
+
tokensBefore?: number;
|
|
194
|
+
tokensAfter?: number;
|
|
195
|
+
/**
|
|
196
|
+
* Present only when tail relief fired (§4.5).
|
|
197
|
+
*
|
|
198
|
+
* A BOUNDED, ANNOUNCED DATA LOSS INSIDE THE TURNS THE RUN CONTINUES FROM, and
|
|
199
|
+
* therefore something a wrapper is entitled to see. The clipped bodies
|
|
200
|
+
* themselves are not on the stream, for the same reason `summary` is not
|
|
201
|
+
* (D-18).
|
|
202
|
+
*/
|
|
203
|
+
tailRelief?: {
|
|
204
|
+
messages: number;
|
|
205
|
+
charsRemoved: number;
|
|
206
|
+
};
|
|
207
|
+
durationMs?: number;
|
|
208
|
+
}
|
|
160
209
|
export interface ExecRetryEvent {
|
|
161
210
|
type: 'retry';
|
|
162
211
|
sessionId: string;
|
|
@@ -202,7 +251,7 @@ export interface ExecResultEvent {
|
|
|
202
251
|
todos: ExecTodoCounts | null;
|
|
203
252
|
error: ExecErrorInfo | null;
|
|
204
253
|
}
|
|
205
|
-
export type ExecEvent = ExecSystemInitEvent | ExecUserEvent | ExecTextDeltaEvent | ExecThinkingEvent | ExecAssistantEvent | ExecToolCallEvent | ExecToolResultEvent | ExecTodoEvent | ExecTeamEvent | ExecFastReviewEvent | ExecRetryEvent | ExecErrorEvent | ExecResultEvent;
|
|
254
|
+
export type ExecEvent = ExecSystemInitEvent | ExecUserEvent | ExecTextDeltaEvent | ExecThinkingEvent | ExecAssistantEvent | ExecToolCallEvent | ExecToolResultEvent | ExecTodoEvent | ExecTeamEvent | ExecFastReviewEvent | ExecCompactionEvent | ExecRetryEvent | ExecErrorEvent | ExecResultEvent;
|
|
206
255
|
/** Everything `system/init` needs that the runner cannot derive for itself. */
|
|
207
256
|
export interface ExecInitParams {
|
|
208
257
|
sessionId: string;
|
package/dist/exec/index.js
CHANGED
|
@@ -22,7 +22,7 @@ import process from 'node:process';
|
|
|
22
22
|
import { runHeadless } from '../agent/headless.js';
|
|
23
23
|
import { computeCost } from '../agent/usage.js';
|
|
24
24
|
import { loadConfig } from '../config/load.js';
|
|
25
|
-
import { latestSessionForCwd, readSessionFile, resolveSessionRef, sessionPathFor, tryAcquireSessionLock, writeSession, } from '../session/store.js';
|
|
25
|
+
import { latestSessionForCwd, readSessionFile, resolveSessionRef, resumedSessionId, sessionPathFor, tryAcquireSessionLock, writeSession, } from '../session/store.js';
|
|
26
26
|
import { createEmitter } from './emitter.js';
|
|
27
27
|
import { resolveExecOptions } from './options.js';
|
|
28
28
|
import { resolvePermission } from './permission.js';
|
|
@@ -352,11 +352,15 @@ function planSession(options, cwd, err) {
|
|
|
352
352
|
if (options.resume !== undefined) {
|
|
353
353
|
const path = resolveSessionRef(options.resume, cwd);
|
|
354
354
|
const saved = path ? readSessionFile(path) : null;
|
|
355
|
-
if (!saved) {
|
|
355
|
+
if (!path || !saved) {
|
|
356
356
|
err.write(`session_not_found: no readable session for --resume "${options.resume}".\n`);
|
|
357
357
|
return null;
|
|
358
358
|
}
|
|
359
|
-
|
|
359
|
+
// NOT `options.resume` AND NOT `meta.id` UNCHECKED (R-7). The flag accepts
|
|
360
|
+
// `<id|path>`, so the value the caller typed is frequently a path, and
|
|
361
|
+
// `meta.id` comes out of a file - neither is usable as a filename without
|
|
362
|
+
// being checked. `resumedSessionId` is the one place that decision is made.
|
|
363
|
+
return { id: resumedSessionId(path, saved), saved, saving, cwd };
|
|
360
364
|
}
|
|
361
365
|
if (options.continueSession) {
|
|
362
366
|
const entry = latestSessionForCwd(cwd);
|
|
@@ -365,7 +369,7 @@ function planSession(options, cwd, err) {
|
|
|
365
369
|
err.write(`no_session_for_cwd: no previous aragon exec session for ${cwd}.\n`);
|
|
366
370
|
return null;
|
|
367
371
|
}
|
|
368
|
-
return { id: entry.
|
|
372
|
+
return { id: resumedSessionId(entry.path, saved), saved, saving, cwd };
|
|
369
373
|
}
|
|
370
374
|
if (options.sessionId !== undefined) {
|
|
371
375
|
// THE UPSERT. A wrapper that mints its own id must not have to ask whether
|
package/dist/exec/runner.js
CHANGED
|
@@ -121,6 +121,48 @@ export class ExecRunner {
|
|
|
121
121
|
});
|
|
122
122
|
if (fast)
|
|
123
123
|
this.unsubscribers.push(fast);
|
|
124
|
+
// Context compaction (context-auto-compaction §4.6). Usage is folded into the
|
|
125
|
+
// run's totals for the reason the team and fast branches above give: a
|
|
126
|
+
// summarization is real spend, and `result.usage` is the only cost readout a
|
|
127
|
+
// wrapper gets.
|
|
128
|
+
const compaction = controller.subscribeCompaction?.((event) => {
|
|
129
|
+
if (event.type === 'usage') {
|
|
130
|
+
this.usage.inputTokens += event.usage.inputTokens;
|
|
131
|
+
this.usage.outputTokens += event.usage.outputTokens;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (event.type === 'compaction_start') {
|
|
135
|
+
this.emit({
|
|
136
|
+
type: 'compaction',
|
|
137
|
+
sessionId: this.sessionId,
|
|
138
|
+
turn: this.turns,
|
|
139
|
+
subtype: 'start',
|
|
140
|
+
trigger: event.trigger,
|
|
141
|
+
});
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (event.type !== 'compaction_end')
|
|
145
|
+
return;
|
|
146
|
+
const r = event.record;
|
|
147
|
+
this.emit({
|
|
148
|
+
type: 'compaction',
|
|
149
|
+
sessionId: this.sessionId,
|
|
150
|
+
turn: this.turns,
|
|
151
|
+
subtype: 'end',
|
|
152
|
+
trigger: r.trigger,
|
|
153
|
+
applied: r.applied,
|
|
154
|
+
mode: r.mode,
|
|
155
|
+
...(r.reason !== undefined ? { reason: r.reason } : {}),
|
|
156
|
+
messagesBefore: r.messagesBefore,
|
|
157
|
+
messagesAfter: r.messagesAfter,
|
|
158
|
+
tokensBefore: r.tokensBefore,
|
|
159
|
+
tokensAfter: r.tokensAfter,
|
|
160
|
+
...(r.tailRelief ? { tailRelief: r.tailRelief } : {}),
|
|
161
|
+
durationMs: r.durationMs,
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
if (compaction)
|
|
165
|
+
this.unsubscribers.push(compaction);
|
|
124
166
|
this.installTerminator();
|
|
125
167
|
}
|
|
126
168
|
/**
|
|
@@ -193,8 +235,8 @@ export class ExecRunner {
|
|
|
193
235
|
const followThrough = this.opts.followThrough ?? 'notify';
|
|
194
236
|
for (;;) {
|
|
195
237
|
const next = await source.next();
|
|
196
|
-
//
|
|
197
|
-
// TERMS IS NEVER MISREPORTED (IF-3). `next === null` first is what makes
|
|
238
|
+
// THE SOURCE IS POLLED BEFORE THE EXHAUSTION CHECK, SO A RUN THAT ENDS ON
|
|
239
|
+
// ITS OWN TERMS IS NEVER MISREPORTED (IF-3). `next === null` first is what makes
|
|
198
240
|
// that true: a conversation of exactly `--max-turns` turns whose caller has
|
|
199
241
|
// nothing more to say exits 0 with `end_turn`, not 3 with `max_turns`.
|
|
200
242
|
if (next === null)
|
|
@@ -15,8 +15,10 @@
|
|
|
15
15
|
* module exists so `stdin-mouse-filter.ts` can remove every byte of every
|
|
16
16
|
* report before Ink ever sees the chunk (invariant I-1).
|
|
17
17
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
18
|
+
* Every mouse report is CONSUMED here whatever it turns out to be; what changed
|
|
19
|
+
* with tui-selection-and-scroll-follow is that press / drag / release are now
|
|
20
|
+
* EMITTED as well as consumed, so `SelectionController` can build a selection
|
|
21
|
+
* out of them (§4.4.2). They were previously recognized and thrown away.
|
|
20
22
|
*/
|
|
21
23
|
export interface WheelEvent {
|
|
22
24
|
readonly kind: 'wheel';
|
|
@@ -29,14 +31,33 @@ export interface WheelEvent {
|
|
|
29
31
|
readonly alt: boolean;
|
|
30
32
|
readonly ctrl: boolean;
|
|
31
33
|
}
|
|
34
|
+
export interface ButtonEvent {
|
|
35
|
+
readonly kind: 'press' | 'drag' | 'release';
|
|
36
|
+
/** 0 = left, 1 = middle, 2 = right. Never 3 — see `decodeReport`. */
|
|
37
|
+
readonly button: 0 | 1 | 2;
|
|
38
|
+
/** 1-based terminal column, as reported. */
|
|
39
|
+
readonly x: number;
|
|
40
|
+
/** 1-based terminal row, as reported. */
|
|
41
|
+
readonly y: number;
|
|
42
|
+
readonly shift: boolean;
|
|
43
|
+
readonly alt: boolean;
|
|
44
|
+
readonly ctrl: boolean;
|
|
45
|
+
}
|
|
46
|
+
export type MouseEvent = WheelEvent | ButtonEvent;
|
|
32
47
|
export interface MouseSplit {
|
|
33
|
-
/**
|
|
34
|
-
readonly events:
|
|
48
|
+
/** Mouse events, in arrival order. Unrecognized reports are dropped, not returned. */
|
|
49
|
+
readonly events: MouseEvent[];
|
|
35
50
|
/** Everything that was not a mouse report — forward this to Ink verbatim. */
|
|
36
51
|
readonly text: string;
|
|
37
52
|
/** Trailing strict prefix of a possible report; feed it back in with the next chunk. */
|
|
38
53
|
readonly pending: string;
|
|
39
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Which wire encoding a report arrived in. It is a PARAMETER rather than a
|
|
57
|
+
* remembered rule because the rule it expresses is invariant I-11 and the cost
|
|
58
|
+
* of forgetting it is a permanently frozen viewport (see `decodeReport`).
|
|
59
|
+
*/
|
|
60
|
+
export type MouseEncoding = 'sgr' | 'x10';
|
|
40
61
|
/**
|
|
41
62
|
* Ceiling on the held-back tail. A malformed sequence that looks like a prefix
|
|
42
63
|
* forever must never be able to wedge input: past this length the tail is
|
|
@@ -51,11 +72,16 @@ export declare const MAX_PENDING_MOUSE_CHARS = 32;
|
|
|
51
72
|
*/
|
|
52
73
|
export declare function isMousePrefix(s: string): boolean;
|
|
53
74
|
/**
|
|
54
|
-
* Split a decoded stdin chunk into
|
|
75
|
+
* Split a decoded stdin chunk into mouse events, passthrough text, and a
|
|
55
76
|
* trailing prefix to carry into the next chunk.
|
|
56
77
|
*
|
|
57
78
|
* The caller is expected to feed `pending + nextChunk` back in; ConPTY does
|
|
58
79
|
* split writes mid-sequence, and a report torn across two chunks would
|
|
59
80
|
* otherwise leak its tail into the composer (R-6).
|
|
81
|
+
*
|
|
82
|
+
* ONE CHANNEL, NOT TWO (§4.4.2). Widening this list to carry buttons alongside
|
|
83
|
+
* wheel notches is what keeps ARRIVAL ORDER intact between them; a second
|
|
84
|
+
* channel would let a press overtake the notch that preceded it, and the
|
|
85
|
+
* selection would then be anchored against rows that had already scrolled.
|
|
60
86
|
*/
|
|
61
87
|
export declare function splitMouseEvents(buffer: string): MouseSplit;
|