@aragon-agent/cli 0.6.3 → 0.6.7
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 +142 -0
- package/README.md +203 -13
- package/dist/agent/controller.d.ts +158 -2
- package/dist/agent/controller.js +400 -1
- package/dist/agent/reducer.d.ts +102 -19
- package/dist/agent/reducer.js +162 -71
- package/dist/agent/system-prompt.d.ts +14 -0
- package/dist/agent/system-prompt.js +2 -0
- package/dist/agent/usage.d.ts +34 -0
- package/dist/agent/usage.js +43 -0
- package/dist/cli.js +132 -23
- package/dist/commands/builtins.js +101 -1
- package/dist/compaction/child.js +5 -0
- package/dist/compaction/command.js +14 -1
- package/dist/compaction/compactor.d.ts +94 -24
- package/dist/compaction/compactor.js +247 -98
- package/dist/compaction/context-command.d.ts +33 -0
- package/dist/compaction/context-command.js +166 -0
- package/dist/compaction/meter.d.ts +251 -0
- package/dist/compaction/meter.js +428 -0
- package/dist/compaction/pressure.d.ts +6 -0
- package/dist/compaction/pressure.js +4 -0
- package/dist/compaction/types.d.ts +77 -0
- package/dist/compaction/wiring.d.ts +56 -0
- package/dist/compaction/wiring.js +107 -25
- package/dist/config/cli-commands.js +5 -0
- package/dist/config/env.d.ts +1 -1
- package/dist/config/env.js +63 -2
- package/dist/config/load.d.ts +28 -0
- package/dist/config/load.js +83 -1
- package/dist/config/schema.d.ts +129 -0
- package/dist/config/schema.js +69 -0
- package/dist/config/store.js +47 -1
- package/dist/exec/events.d.ts +138 -1
- package/dist/exec/events.js +42 -0
- package/dist/exec/index.js +43 -6
- package/dist/exec/progress-reporter.d.ts +23 -0
- package/dist/exec/progress-reporter.js +88 -0
- package/dist/exec/runner.d.ts +34 -0
- package/dist/exec/runner.js +137 -25
- package/dist/fast/reviewer.d.ts +19 -1
- package/dist/fast/reviewer.js +22 -0
- package/dist/fast/types.d.ts +15 -0
- package/dist/fast/wiring.d.ts +10 -1
- package/dist/fast/wiring.js +18 -1
- package/dist/input/limits.d.ts +72 -0
- package/dist/input/limits.js +63 -0
- package/dist/input/paste-parse.d.ts +71 -0
- package/dist/input/paste-parse.js +95 -0
- package/dist/input/stdin-filter.d.ts +81 -0
- package/dist/input/stdin-filter.js +379 -0
- package/dist/logging/install.d.ts +2 -0
- package/dist/logging/install.js +53 -1
- package/dist/proc/classify.d.ts +52 -0
- package/dist/proc/classify.js +143 -0
- package/dist/proc/kill-tree.d.ts +42 -0
- package/dist/proc/kill-tree.js +119 -0
- package/dist/proc/limits.d.ts +84 -0
- package/dist/proc/limits.js +84 -0
- package/dist/proc/log-ring.d.ts +68 -0
- package/dist/proc/log-ring.js +102 -0
- package/dist/proc/prompt.d.ts +24 -0
- package/dist/proc/prompt.js +49 -0
- package/dist/proc/readiness.d.ts +102 -0
- package/dist/proc/readiness.js +219 -0
- package/dist/proc/supervisor.d.ts +103 -0
- package/dist/proc/supervisor.js +465 -0
- package/dist/proc/types.d.ts +175 -0
- package/dist/proc/types.js +15 -0
- package/dist/session/persist.js +36 -1
- package/dist/todo/limits.d.ts +4 -17
- package/dist/todo/limits.js +4 -17
- package/dist/todo/panel-rows.d.ts +8 -6
- package/dist/todo/panel-rows.js +19 -6
- package/dist/tools/bash-tool.d.ts +51 -2
- package/dist/tools/bash-tool.js +321 -51
- package/dist/tools/fs-tools.d.ts +17 -0
- package/dist/tools/index.d.ts +55 -2
- package/dist/tools/index.js +39 -1
- package/dist/tools/proc-tools.d.ts +25 -0
- package/dist/tools/proc-tools.js +102 -0
- package/dist/ui/App.d.ts +15 -2
- package/dist/ui/App.js +330 -69
- package/dist/ui/AutocompletePopup.d.ts +3 -1
- package/dist/ui/AutocompletePopup.js +9 -11
- package/dist/ui/BottomStatusRow.d.ts +1 -1
- package/dist/ui/Composer.d.ts +56 -1
- package/dist/ui/Composer.js +50 -6
- package/dist/ui/PromptInput.d.ts +52 -3
- package/dist/ui/PromptInput.js +201 -55
- package/dist/ui/StatusBar.d.ts +40 -15
- package/dist/ui/StatusBar.js +51 -9
- package/dist/ui/TeamPanel.d.ts +3 -1
- package/dist/ui/TeamPanel.js +13 -9
- package/dist/ui/TodoPanel.d.ts +5 -5
- package/dist/ui/TodoPanel.js +56 -24
- package/dist/ui/Transcript.js +29 -1
- package/dist/ui/UpdateLine.d.ts +1 -1
- package/dist/ui/capabilities.js +5 -5
- package/dist/ui/composer-limits.d.ts +31 -0
- package/dist/ui/composer-limits.js +50 -0
- package/dist/ui/composer-rows.d.ts +73 -0
- package/dist/ui/composer-rows.js +201 -0
- package/dist/ui/editor-reducer.d.ts +37 -0
- package/dist/ui/editor-reducer.js +88 -12
- package/dist/ui/entries/ServiceCard.d.ts +66 -0
- package/dist/ui/entries/ServiceCard.js +116 -0
- package/dist/ui/entries/UserEntry.d.ts +17 -3
- package/dist/ui/entries/UserEntry.js +34 -5
- package/dist/ui/exit-snapshot.d.ts +9 -0
- package/dist/ui/layout/ScrollViewport.d.ts +1 -1
- package/dist/ui/layout/autocomplete.d.ts +18 -0
- package/dist/ui/layout/autocomplete.js +26 -0
- package/dist/ui/layout/budget.d.ts +34 -6
- package/dist/ui/layout/budget.js +32 -7
- package/dist/ui/layout/rail.d.ts +4 -53
- package/dist/ui/layout/rail.js +10 -62
- package/dist/ui/layout/team-panel.d.ts +18 -0
- package/dist/ui/layout/team-panel.js +22 -0
- package/dist/ui/layout/todo-layout.d.ts +24 -0
- package/dist/ui/layout/todo-layout.js +27 -0
- package/dist/ui/layout/virtual-window.js +51 -1
- package/dist/ui/overlays/HelpOverlay.js +3 -0
- package/dist/ui/overlays/PlanReviewOverlay.js +10 -2
- package/dist/ui/overlays/QuestionOverlay.js +5 -1
- package/dist/ui/overlays/SettingsScreen.js +14 -1
- package/dist/ui/paste-frames.d.ts +87 -0
- package/dist/ui/paste-frames.js +112 -0
- package/dist/ui/paste-tokens.d.ts +90 -0
- package/dist/ui/paste-tokens.js +147 -0
- package/dist/ui/screen.d.ts +16 -0
- package/dist/ui/screen.js +45 -2
- package/dist/ui/selection/selection-controller.d.ts +1 -1
- package/dist/ui/transcript-text.js +55 -2
- package/dist/ui/use-wheel-routing.d.ts +1 -1
- package/package.json +2 -2
- package/dist/input/stdin-mouse-filter.d.ts +0 -47
- package/dist/input/stdin-mouse-filter.js +0 -153
package/dist/cli.js
CHANGED
|
@@ -27,7 +27,7 @@ import { COMPACTION_CONFIG_SET_KEYS, FAST_CONFIG_SET_KEYS, LOG_CONFIG_SET_KEYS,
|
|
|
27
27
|
import { attachAgentEvents, attachTeamEvents, installLogging, setScreenRestore, setSignalTerminator, } from './logging/install.js';
|
|
28
28
|
import { getLogger } from './logging/logger.js';
|
|
29
29
|
import { runLogsCommand } from './logging/cli-commands.js';
|
|
30
|
-
import { ADAPTER_PROVIDERS, clampDensity, clampTheme, clampThinkingLevel, clampAskRounds, clampHumanTimeout, clampSkillsIntegrity, clampSkillsToolPolicy, clampMaxRenderInterval, clampScrollResumeMs, clampTranscriptRetain, clampTranscriptWindow, coercePositiveInt, isAdapterProvider, parseMaxTokensInput, DEFAULT_MAX_RENDER_INTERVAL_MS, DEFAULT_MAX_TOKENS, DEFAULT_PLAN_HUMAN_TIMEOUT_MS, DEFAULT_PLAN_MAX_ASK_ROUNDS, DEFAULT_SCROLL_RESUME_MS, DEFAULT_SKILLS_CONFIG, DEFAULT_TRANSCRIPT_RETAIN, DEFAULT_TRANSCRIPT_WINDOW, } from './config/schema.js';
|
|
30
|
+
import { ADAPTER_PROVIDERS, clampDensity, clampTheme, clampThinkingLevel, clampAskRounds, clampHumanTimeout, clampSkillsIntegrity, clampSkillsToolPolicy, clampContextWindow, clampMaxRenderInterval, clampScrollResumeMs, clampTranscriptRetain, clampTranscriptWindow, coercePositiveInt, isAdapterProvider, isAutoToken, parseMaxTokensInput, DEFAULT_MAX_RENDER_INTERVAL_MS, DEFAULT_MAX_TOKENS, DEFAULT_PLAN_HUMAN_TIMEOUT_MS, DEFAULT_PLAN_MAX_ASK_ROUNDS, DEFAULT_SCROLL_RESUME_MS, DEFAULT_SKILLS_CONFIG, DEFAULT_TRANSCRIPT_RETAIN, DEFAULT_TRANSCRIPT_WINDOW, } from './config/schema.js';
|
|
31
31
|
import { makeGetApiKey } from './config/load.js';
|
|
32
32
|
import { AgentController, DENY_ALL_APPROVAL } from './agent/controller.js';
|
|
33
33
|
import { runSkillsCommand } from './skills/cli-commands.js';
|
|
@@ -44,7 +44,7 @@ import { decideRenderMode } from './ui/layout/frame.js';
|
|
|
44
44
|
import { createFrameDiffer } from './ui/frame-differ.js';
|
|
45
45
|
import { wrapStdoutForFrames } from './ui/stdout-frame-writer.js';
|
|
46
46
|
import { setFrameStatsProvider } from './commands/perf.js';
|
|
47
|
-
import {
|
|
47
|
+
import { tryCreateStdinFilter } from './input/stdin-filter.js';
|
|
48
48
|
import { enterAltScreen, writeExitTranscript } from './ui/screen.js';
|
|
49
49
|
import { supportsWindowsVtInput } from './ui/win-vt-input.js';
|
|
50
50
|
import { forceWindowsVtInput } from './ui/win-vt-force.js';
|
|
@@ -113,6 +113,11 @@ function toFlags(opts) {
|
|
|
113
113
|
// on `CliFlags`, AND a line here; `config.test.ts` is the regression pin that
|
|
114
114
|
// exists because `--no-mouse` shipped inert once already.
|
|
115
115
|
mouseSelect: opts.mouseSelect,
|
|
116
|
+
// Site three of three for `--paste` / `--no-paste` -- TEN features, one
|
|
117
|
+
// trap. A flag needs the `.option()` declaration below, a field on
|
|
118
|
+
// `CliFlags`, AND a line here; `config.test.ts` is the regression pin that
|
|
119
|
+
// exists because `--no-mouse` shipped inert once already.
|
|
120
|
+
paste: opts.paste,
|
|
116
121
|
skills: opts.skills,
|
|
117
122
|
skill: opts.skill,
|
|
118
123
|
skillsYes: opts.skillsYes,
|
|
@@ -148,6 +153,11 @@ function toFlags(opts) {
|
|
|
148
153
|
fastModel: opts.fastModel,
|
|
149
154
|
fastProvider: opts.fastProvider,
|
|
150
155
|
fastReview: opts.fastReview,
|
|
156
|
+
// …and again for `--fast-delegate` / `--no-fast-delegate`, one feature
|
|
157
|
+
// later — NINE features, one trap. It is `fast.delegate`'s only channel:
|
|
158
|
+
// there is no `ARAGON_FAST_DELEGATE`, so missing this line leaves a wrapper
|
|
159
|
+
// with no way at all to say "do not delegate on this run".
|
|
160
|
+
fastDelegate: opts.fastDelegate,
|
|
151
161
|
// `--verbose` is just a shorthand for `--log-level debug`; an explicit
|
|
152
162
|
// `--log-level` still wins, so the two can be combined without surprise.
|
|
153
163
|
logLevel: opts.logLevel ?? (opts.verbose ? 'debug' : undefined),
|
|
@@ -416,7 +426,7 @@ function runInteractive(flags, extras = {}) {
|
|
|
416
426
|
// before it reaches this process, so `\x1b[?1000h` is a pure no-op and the
|
|
417
427
|
// filter has nothing to filter. Gating `wantMouse` — rather than the three
|
|
418
428
|
// things downstream of it — is what keeps the three in agreement: the
|
|
419
|
-
// `?1000h` write (`enterAltScreen(..., { mouse:
|
|
429
|
+
// `?1000h` write (`enterAltScreen(..., { mouse: mouseOn })`), the
|
|
420
430
|
// filter itself, and the `mouseSource` prop that decides whether the user is
|
|
421
431
|
// advised about a mode that is not running. Wiring any of them separately
|
|
422
432
|
// re-opens the gap where the app claims a capability it does not have.
|
|
@@ -434,10 +444,36 @@ function runInteractive(flags, extras = {}) {
|
|
|
434
444
|
!!process.stdout.isTTY &&
|
|
435
445
|
!!process.stdin.isTTY &&
|
|
436
446
|
vtInputSupported;
|
|
437
|
-
|
|
438
|
-
|
|
447
|
+
// --- Paste handling (tui-paste-handling section 5.1.1 / D-13 / D-17). ----
|
|
448
|
+
//
|
|
449
|
+
// THE GATE IS WIDER THAN THE MOUSE'S, AND `paste` DEFAULTS TO TRUE. Paste
|
|
450
|
+
// needs no `vtInputSupported` and no full-screen: Tier 2 is a heuristic on
|
|
451
|
+
// chunk shape and needs no terminal cooperation at all, which is exactly why
|
|
452
|
+
// it exists for the consoles that never deliver `\x1b[200~` (R-11).
|
|
453
|
+
//
|
|
454
|
+
// WHAT THIS WIDENING BREAKS IF IT IS NOT PAIRED WITH `mouseOn` BELOW (P0-1).
|
|
455
|
+
// `filter !== null` used to be the answer to FIVE questions, only one of which
|
|
456
|
+
// is really about the stream. With paste able to build a filter on its own,
|
|
457
|
+
// `aragon --no-mouse` would get a non-null handle and every one of those five
|
|
458
|
+
// would flip true: `?1000h`/`?1006h`/`?1002h` written, terminal selection taken
|
|
459
|
+
// away (the ONLY reason anyone passes `--no-mouse`), and a startup notice
|
|
460
|
+
// advising the user about a mode they just disabled.
|
|
461
|
+
const wantPaste = config.paste && !!process.stdin.isTTY;
|
|
462
|
+
const pasteBridge = { notify: null };
|
|
463
|
+
const filter = wantMouse || wantPaste
|
|
464
|
+
? tryCreateStdinFilter(process.stdin, { mouse: wantMouse, paste: wantPaste }, (reason) => logger.warn('cli', 'stdin_filter_failed', { reason }), pasteBridge)
|
|
439
465
|
: null;
|
|
440
|
-
|
|
466
|
+
/**
|
|
467
|
+
* THE ONE EXPRESSION EVERY MOUSE SITE READS (I-11 / D-17).
|
|
468
|
+
*
|
|
469
|
+
* `filter !== null` now answers exactly one question -- "is a stream wrapped?"
|
|
470
|
+
* -- and the only site allowed to keep reading it is the `stdin` handed to
|
|
471
|
+
* `render()`.
|
|
472
|
+
*/
|
|
473
|
+
const mouseOn = wantMouse && filter !== null;
|
|
474
|
+
/** Same discipline for DEC 2004: already decided here, never re-derived. */
|
|
475
|
+
const pasteOn = wantPaste && filter !== null;
|
|
476
|
+
const disposeStdinFilter = () => filter?.dispose();
|
|
441
477
|
// --- Drag-select (tui-selection-and-scroll-follow §4.4). ------------------
|
|
442
478
|
//
|
|
443
479
|
// ONE EXPRESSION, READ BY FOUR THINGS, and that is the same discipline
|
|
@@ -446,7 +482,7 @@ function runInteractive(flags, extras = {}) {
|
|
|
446
482
|
// disagree about whether drag-select is running. With it false the session is
|
|
447
483
|
// byte-identical to a pre-feature build — no extra escape on the wire (AC-8),
|
|
448
484
|
// no controller, no `decorate`, no mirror.
|
|
449
|
-
const wantSelect =
|
|
485
|
+
const wantSelect = mouseOn && config.mouseSelect;
|
|
450
486
|
const selectionBridge = {
|
|
451
487
|
controller: null,
|
|
452
488
|
onCopied: null,
|
|
@@ -526,11 +562,21 @@ function runInteractive(flags, extras = {}) {
|
|
|
526
562
|
}));
|
|
527
563
|
};
|
|
528
564
|
if (mode === 'fullscreen') {
|
|
529
|
-
// `mouse` here means "a filter is installed", NOT "the user
|
|
530
|
-
// support" — see `AltScreenOptions`. `screen.ts` must never
|
|
531
|
-
// itself, or a later refactor re-opens the gap I-8 closes.
|
|
532
|
-
// the same discipline for `?1002h
|
|
533
|
-
|
|
565
|
+
// `mouse` here means "a mouse-parsing filter is installed", NOT "the user
|
|
566
|
+
// wants mouse support" — see `AltScreenOptions`. `screen.ts` must never
|
|
567
|
+
// derive it for itself, or a later refactor re-opens the gap I-8 closes.
|
|
568
|
+
// `motion` carries the same discipline for `?1002h`, and `bracketedPaste`
|
|
569
|
+
// for `?2004h`: all three are already decided, none is re-derived.
|
|
570
|
+
//
|
|
571
|
+
// IT IS `mouseOn`, NOT `filter !== null` (P0-1). A `--no-mouse` session with
|
|
572
|
+
// paste at its default `true` still builds a filter, and passing the handle
|
|
573
|
+
// here would write `?1000h` at a user who disabled the mouse precisely to
|
|
574
|
+
// keep their terminal's own selection.
|
|
575
|
+
screen = enterAltScreen(process.stdout, {
|
|
576
|
+
mouse: mouseOn,
|
|
577
|
+
motion: wantSelect,
|
|
578
|
+
bracketedPaste: pasteOn && mode === 'fullscreen',
|
|
579
|
+
});
|
|
534
580
|
const restore = () => screen?.restore();
|
|
535
581
|
// Four idempotent restore paths (§4.4). The signal hook is NOT redundant:
|
|
536
582
|
// - `waitUntilExit().then()` is a microtask and may never be reached when
|
|
@@ -613,10 +659,10 @@ function runInteractive(flags, extras = {}) {
|
|
|
613
659
|
// than inside `screen.ts`'s closure because `App` needs to READ it and the
|
|
614
660
|
// handle's own flag is private (it has to be — `restore()` reads it, and
|
|
615
661
|
// exposing a setter would be a second way to get the two out of step).
|
|
616
|
-
let mouseCaptured =
|
|
617
|
-
if (wantSelect &&
|
|
662
|
+
let mouseCaptured = mouseOn;
|
|
663
|
+
if (wantSelect && filter) {
|
|
618
664
|
selectionBridge.controller = createSelectionController({
|
|
619
|
-
source:
|
|
665
|
+
source: filter.source,
|
|
620
666
|
// The frame writer is absent under `--no-diff-render`, where there is no
|
|
621
667
|
// cache to repaint from at all; the controller then falls back to asking
|
|
622
668
|
// `App` for a React redraw on every drag flush, which is slower and
|
|
@@ -656,7 +702,7 @@ function runInteractive(flags, extras = {}) {
|
|
|
656
702
|
const terminalBridge = mode === 'fullscreen' || writeForeign
|
|
657
703
|
? {
|
|
658
704
|
mouseSelect: wantSelect,
|
|
659
|
-
...(
|
|
705
|
+
...(mouseOn
|
|
660
706
|
? {
|
|
661
707
|
setMouseCapture: (on) => {
|
|
662
708
|
screen?.setMouseCapture(on);
|
|
@@ -682,13 +728,21 @@ function runInteractive(flags, extras = {}) {
|
|
|
682
728
|
!!process.stdin.isTTY &&
|
|
683
729
|
!process.env.CI &&
|
|
684
730
|
config.update.mode !== 'off';
|
|
685
|
-
const instance = render(_jsx(App, { controller: controller, version: VERSION, mode: mode, confirmBridge: confirmBridge, humanInputBridge: humanInputBridge, initialPrompt: extras.initialPrompt, initialOverlay: extras.initialOverlay,
|
|
731
|
+
const instance = render(_jsx(App, { controller: controller, version: VERSION, mode: mode, confirmBridge: confirmBridge, humanInputBridge: humanInputBridge, initialPrompt: extras.initialPrompt, initialOverlay: extras.initialOverlay,
|
|
732
|
+
// `mouseOn`, NOT the handle (I-11). `App.tsx:186-188` documents this
|
|
733
|
+
// prop's contract as "absent under `--no-mouse`, inline, or non-TTY", and
|
|
734
|
+
// it feeds `enabled: !!mouseSource` on a wheel subscription that would
|
|
735
|
+
// otherwise be a channel no event can ever arrive on.
|
|
736
|
+
mouseSource: mouseOn && filter ? filter.source : undefined,
|
|
686
737
|
// Present ONLY when this console swallows `CSI Z` and mouse reports.
|
|
687
738
|
// Passed in rather than probed inside `App` so that every test renders
|
|
688
739
|
// the same tree on every machine — reading `process.versions.node` from
|
|
689
740
|
// a component would make the notice appear or vanish according to which
|
|
690
741
|
// Node the developer happened to have installed.
|
|
691
|
-
vtInputWarning: vtInputSupported ? undefined : { nodeVersion: process.versions.node }, updateBridge: updateEligible ? updateBridge : undefined,
|
|
742
|
+
vtInputWarning: vtInputSupported ? undefined : { nodeVersion: process.versions.node }, updateBridge: updateEligible ? updateBridge : undefined,
|
|
743
|
+
// Absent when nothing can refuse a paste, so a `--no-paste` tree is
|
|
744
|
+
// byte-identical to a build without this feature.
|
|
745
|
+
pasteBridge: pasteOn ? pasteBridge : undefined, terminal: terminalBridge }),
|
|
692
746
|
// Full-screen owns console.* itself (I-4): Ink's patchConsole writes
|
|
693
747
|
// straight to stdout and permanently shifts the fixed frame's accounting.
|
|
694
748
|
{
|
|
@@ -696,7 +750,7 @@ function runInteractive(flags, extras = {}) {
|
|
|
696
750
|
patchConsole: mode === 'inline',
|
|
697
751
|
// I-5: with mouse support off the REAL stdin is handed over unwrapped, so
|
|
698
752
|
// every existing path is provably unchanged.
|
|
699
|
-
stdin:
|
|
753
|
+
stdin: filter?.stdin ?? process.stdin,
|
|
700
754
|
// SPREAD, not `frameWriter?.stdout ?? process.stdout`. This call passed no
|
|
701
755
|
// `stdout` at all before, and Ink defaults it to `process.stdout`; omitting
|
|
702
756
|
// the key entirely when the writer is off keeps the options object the same
|
|
@@ -757,12 +811,12 @@ function runInteractive(flags, extras = {}) {
|
|
|
757
811
|
.waitUntilExit()
|
|
758
812
|
.then(() => {
|
|
759
813
|
screen?.restore();
|
|
760
|
-
// BEFORE `
|
|
814
|
+
// BEFORE `disposeStdinFilter()`, which clears the listener set it subscribed to,
|
|
761
815
|
// and after `restore()` for the reason that call records: the terminal must
|
|
762
816
|
// stop reporting first, and anything already in flight is still consumed by
|
|
763
817
|
// a live filter.
|
|
764
818
|
disposeSelection();
|
|
765
|
-
|
|
819
|
+
disposeStdinFilter();
|
|
766
820
|
// BOTH BRANCHES, the same discipline `controller.dispose()` and
|
|
767
821
|
// `updateService?.dispose()` already follow. It drops the `'resize'`
|
|
768
822
|
// listener of I-8 and detaches the `/perf` provider so a later `/perf` in a
|
|
@@ -786,12 +840,12 @@ function runInteractive(flags, extras = {}) {
|
|
|
786
840
|
})
|
|
787
841
|
.catch(() => {
|
|
788
842
|
screen?.restore();
|
|
789
|
-
// BEFORE `
|
|
843
|
+
// BEFORE `disposeStdinFilter()`, which clears the listener set it subscribed to,
|
|
790
844
|
// and after `restore()` for the reason that call records: the terminal must
|
|
791
845
|
// stop reporting first, and anything already in flight is still consumed by
|
|
792
846
|
// a live filter.
|
|
793
847
|
disposeSelection();
|
|
794
|
-
|
|
848
|
+
disposeStdinFilter();
|
|
795
849
|
disposeFrameWriter();
|
|
796
850
|
controller.dispose();
|
|
797
851
|
updateService?.dispose();
|
|
@@ -875,6 +929,11 @@ const CONFIG_SET_KEYS = new Set([
|
|
|
875
929
|
'baseUrl',
|
|
876
930
|
'thinkingLevel',
|
|
877
931
|
'maxTokens',
|
|
932
|
+
// context-usage-gauge-accuracy §3.6. Listed here AND given its own branch in
|
|
933
|
+
// `runConfigSet` below - a key present here but absent there falls through
|
|
934
|
+
// every case, writes nothing, and still prints `Set contextWindow = 1000000`
|
|
935
|
+
// (P1-2).
|
|
936
|
+
'contextWindow',
|
|
878
937
|
'theme',
|
|
879
938
|
'confirmTools',
|
|
880
939
|
'toolTimeoutMs',
|
|
@@ -911,6 +970,10 @@ const CONFIG_SET_KEYS = new Set([
|
|
|
911
970
|
// records: a key listed here but absent there writes nothing and still prints
|
|
912
971
|
// `Set mouseSelect = false`.
|
|
913
972
|
'mouseSelect',
|
|
973
|
+
// Listed here AND cased in the switch below, for the reason the note above
|
|
974
|
+
// `mouse` records: a key present here but absent there falls through every
|
|
975
|
+
// case, writes nothing, and still prints `Set paste = false`.
|
|
976
|
+
'paste',
|
|
914
977
|
'scrollResumeMs',
|
|
915
978
|
'historyEnabled',
|
|
916
979
|
'planModeDefault',
|
|
@@ -967,6 +1030,30 @@ function runConfigSetMaxTokens(value) {
|
|
|
967
1030
|
const merged = updatePersistedConfig({ maxTokens: stored });
|
|
968
1031
|
process.stdout.write(`Set maxTokens = ${merged.maxTokens ?? 'auto'}\n`);
|
|
969
1032
|
}
|
|
1033
|
+
/**
|
|
1034
|
+
* `aragon config set contextWindow <n|auto>`.
|
|
1035
|
+
*
|
|
1036
|
+
* ITS OWN BRANCH, AHEAD OF THE SWITCH, for the reason `maxTokens` has one: it
|
|
1037
|
+
* must echo the STORED value rather than the typed one, so
|
|
1038
|
+
* `config set contextWindow 99999999` reports the clamped 5000000 instead of a
|
|
1039
|
+
* number that was never written. It also REJECTS nonsense rather than clamping
|
|
1040
|
+
* it - a non-numeric argument here is almost always a typo for a number, and
|
|
1041
|
+
* storing AUTO for `20O000` would quietly discard the user's intent.
|
|
1042
|
+
*/
|
|
1043
|
+
function runConfigSetContextWindow(value) {
|
|
1044
|
+
const trimmed = value.trim();
|
|
1045
|
+
const auto = isAutoToken(trimmed);
|
|
1046
|
+
if (!auto && !/^\d+$/.test(trimmed)) {
|
|
1047
|
+
process.stderr.write(`Invalid contextWindow "${value}". Use a number of tokens or "auto".\n`);
|
|
1048
|
+
process.exitCode = 2;
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1051
|
+
// `null` GOES IN UNCLAMPED (RV-12). Running AUTO through the clamp yields
|
|
1052
|
+
// 8000 and removes "auto" from the config permanently.
|
|
1053
|
+
const stored = auto ? null : clampContextWindow(trimmed, null);
|
|
1054
|
+
const merged = updatePersistedConfig({ contextWindow: stored });
|
|
1055
|
+
process.stdout.write(`Set contextWindow = ${merged.contextWindow ?? 'auto'}\n`);
|
|
1056
|
+
}
|
|
970
1057
|
function runConfigSet(key, value) {
|
|
971
1058
|
if (!CONFIG_SET_KEYS.has(key)) {
|
|
972
1059
|
process.stderr.write(`Unknown config key "${key}". Known keys: ${[...CONFIG_SET_KEYS].join(', ')}.\n`);
|
|
@@ -987,6 +1074,11 @@ function runConfigSet(key, value) {
|
|
|
987
1074
|
runConfigSetMaxTokens(value);
|
|
988
1075
|
return;
|
|
989
1076
|
}
|
|
1077
|
+
// The second tri-state key, and it needs its own branch for the same reason.
|
|
1078
|
+
if (key === 'contextWindow') {
|
|
1079
|
+
runConfigSetContextWindow(value);
|
|
1080
|
+
return;
|
|
1081
|
+
}
|
|
990
1082
|
const teamPatch = applyTeamConfigSet(key, value);
|
|
991
1083
|
if (teamPatch) {
|
|
992
1084
|
// The written value is echoed from the CLAMPED result rather than from the
|
|
@@ -1109,6 +1201,9 @@ function runConfigSet(key, value) {
|
|
|
1109
1201
|
case 'mouseSelect':
|
|
1110
1202
|
patch.mouseSelect = value === 'true' || value === '1';
|
|
1111
1203
|
break;
|
|
1204
|
+
case 'paste':
|
|
1205
|
+
patch.paste = value === 'true' || value === '1';
|
|
1206
|
+
break;
|
|
1112
1207
|
// Clamped rather than rejected, for the same reason `planModeDefault` below
|
|
1113
1208
|
// records: hardening only the READ path leaves a bad value on disk that
|
|
1114
1209
|
// reverts to the default on every launch, which presents as "my setting
|
|
@@ -1381,6 +1476,12 @@ function buildProgram() {
|
|
|
1381
1476
|
// `false` on every run that passes no flag at all.
|
|
1382
1477
|
.option('--mouse-select', 'Drag with the mouse to select text; releasing copies it')
|
|
1383
1478
|
.option('--no-mouse-select', 'Keep wheel scrolling, but leave drag-select off')
|
|
1479
|
+
// Positive form FIRST, same tri-state reason as `--mouse-select` above:
|
|
1480
|
+
// `paste` is persisted and defaults to `true`, so a lone `--no-paste` would
|
|
1481
|
+
// make commander default `opts.paste` to `true` -- indistinguishable from
|
|
1482
|
+
// silence, and silently overriding a stored `false` on every run.
|
|
1483
|
+
.option('--paste', 'Collapse large pastes into a placeholder (the default)')
|
|
1484
|
+
.option('--no-paste', 'Treat pasted bytes as keystrokes (pre-0.6.3 behavior)')
|
|
1384
1485
|
// Declaring `--fullscreen` BEFORE `--no-fullscreen` keeps the default
|
|
1385
1486
|
// `undefined` instead of `true`, which is what makes the tri-state work:
|
|
1386
1487
|
// "no opinion" must stay distinguishable from "force it".
|
|
@@ -1429,6 +1530,14 @@ function buildProgram() {
|
|
|
1429
1530
|
.option('--fast-model <id>', 'The fast model id (implies nothing about --fast)')
|
|
1430
1531
|
.option('--fast-provider <id>', 'Provider for the fast model (default: the main provider)')
|
|
1431
1532
|
.option('--fast-review <n|off>', 'Turns between automatic fast reviews, or "off"')
|
|
1533
|
+
// BOTH FORMS, POSITIVE FIRST, and here it matters more than anywhere above:
|
|
1534
|
+
// `fast.delegate` is PERSISTED and defaults to TRUE, so declaring only
|
|
1535
|
+
// `--no-fast-delegate` would make commander default `opts.fastDelegate` to
|
|
1536
|
+
// `true` and force `delegate: true` on EVERY run that passed no flag at all
|
|
1537
|
+
// — silently overwriting a user's stored `false`. Same polarity, same trap
|
|
1538
|
+
// and same fix as `--update` / `--compaction`.
|
|
1539
|
+
.option('--fast-delegate', 'Let the fast tier take mechanical sub-steps (the default)')
|
|
1540
|
+
.option('--no-fast-delegate', 'Run every sub-step on the main model')
|
|
1432
1541
|
// BOTH FORMS, positive first, for the reason recorded seven times above —
|
|
1433
1542
|
// and this is the pair whose omission would be worst. `update.mode` is
|
|
1434
1543
|
// PERSISTED and defaults to `'auto'`, so a lone `--no-update` would make
|
|
@@ -7,7 +7,7 @@ import { existsSync, statSync } from 'node:fs';
|
|
|
7
7
|
import { isAbsolute, resolve } from 'node:path';
|
|
8
8
|
import { resolveOutputTokens } from '@aragon-agent/core';
|
|
9
9
|
import { clampThinkingLevel, clampTheme, isAdapterProvider, isThemeName, parseMaxTokensInput, ADAPTER_PROVIDERS, DEFAULT_MAX_TOKENS, HARD_MAX_RETRIES, LEGACY_DARK_THEME, THEME_NAMES, THINKING_LEVELS, } from '../config/schema.js';
|
|
10
|
-
import { computeCost, formatCost, formatTokens } from '../agent/usage.js';
|
|
10
|
+
import { computeCost, formatCost, formatDuration, formatTokens } from '../agent/usage.js';
|
|
11
11
|
import { TODO_FOLLOW_LIMITS } from '../todo/limits.js';
|
|
12
12
|
import { buildContinuationMessage } from '../todo/follow-through.js';
|
|
13
13
|
import { MODE_LABEL, nextMode } from '../agent/agent-mode.js';
|
|
@@ -17,6 +17,7 @@ import { getLogger } from '../logging/logger.js';
|
|
|
17
17
|
import { copyText } from '../ui/clipboard.js';
|
|
18
18
|
import { perfCommand } from './perf.js';
|
|
19
19
|
import { runCompactCommand } from '../compaction/command.js';
|
|
20
|
+
import { runContextCommand } from '../compaction/context-command.js';
|
|
20
21
|
/**
|
|
21
22
|
* `/fast status` — THE GUARANTEED REPORTING SURFACE for the tier (§4.4 / R-8).
|
|
22
23
|
*
|
|
@@ -809,6 +810,14 @@ const COMMANDS = [
|
|
|
809
810
|
description: 'Context compaction: status | on | off | threshold <n> | keep <n> | <instructions>',
|
|
810
811
|
run: (ctx) => runCompactCommand(ctx),
|
|
811
812
|
},
|
|
813
|
+
{
|
|
814
|
+
// REGISTRATION ONLY, for the reason `/compact` records one entry up: this
|
|
815
|
+
// file is past the 1000-line guideline and the report has five sections.
|
|
816
|
+
// The body is in `compaction/context-command.ts`.
|
|
817
|
+
name: 'context',
|
|
818
|
+
description: 'Show context occupancy, the window it is measured against, and session spend',
|
|
819
|
+
run: (ctx) => runContextCommand(ctx),
|
|
820
|
+
},
|
|
812
821
|
{
|
|
813
822
|
name: 'expand',
|
|
814
823
|
// DELIBERATELY LEFT TOOL-ONLY, and the asymmetry with Ctrl+O is a decision
|
|
@@ -1048,6 +1057,95 @@ const COMMANDS = [
|
|
|
1048
1057
|
// render layer, and threading `ui/render-cache.js` into this file would make
|
|
1049
1058
|
// every other command's import graph pull the renderer in with it.
|
|
1050
1059
|
perfCommand,
|
|
1060
|
+
{
|
|
1061
|
+
name: 'bg',
|
|
1062
|
+
description: 'Background services: list | logs <id> [n] | stop <id|all> | status',
|
|
1063
|
+
/**
|
|
1064
|
+
* THE USER'S HANDLE ON WHAT THE AGENT LEFT RUNNING (§5.3).
|
|
1065
|
+
*
|
|
1066
|
+
* It is the LIVE view, and that is why it exists at all: the transcript
|
|
1067
|
+
* records EVENTS about a service (D-11) — a card printed into `<Static>`
|
|
1068
|
+
* cannot be rewritten — so "what is running right now" lives here, in the
|
|
1069
|
+
* status chip, and in `bash_output`. Without this command a user whose
|
|
1070
|
+
* server card scrolled away an hour ago has no way to name it.
|
|
1071
|
+
*/
|
|
1072
|
+
run: async (ctx) => {
|
|
1073
|
+
const arg = ctx.args.trim();
|
|
1074
|
+
const [verb = 'list', value, count] = arg.split(/\s+/);
|
|
1075
|
+
const controller = ctx.controller;
|
|
1076
|
+
if (!controller.isBackgroundRegistered()) {
|
|
1077
|
+
ctx.notify('info', 'Background services are off for this session (bash.background).');
|
|
1078
|
+
return;
|
|
1079
|
+
}
|
|
1080
|
+
const services = controller.listServices();
|
|
1081
|
+
if (verb === 'status') {
|
|
1082
|
+
const live = controller.liveServiceCount();
|
|
1083
|
+
ctx.notify('info', live === 0
|
|
1084
|
+
? 'No background services running.'
|
|
1085
|
+
: `${live} background service${live === 1 ? '' : 's'} running.`);
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
1088
|
+
if (verb === 'logs') {
|
|
1089
|
+
if (!value) {
|
|
1090
|
+
ctx.notify('warn', 'Usage: /bg logs <id> [n]');
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
const service = controller.getServiceSnapshot(value);
|
|
1094
|
+
if (!service) {
|
|
1095
|
+
ctx.notify('warn', `No service "${value}".`);
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1098
|
+
const page = controller.readServiceLog(value);
|
|
1099
|
+
const n = count ? Number.parseInt(count, 10) : DEFAULT_BG_LOG_ROWS;
|
|
1100
|
+
const rows = page?.rows ?? [];
|
|
1101
|
+
const take = Number.isFinite(n) && n > 0 ? n : DEFAULT_BG_LOG_ROWS;
|
|
1102
|
+
const shown = rows.slice(-take);
|
|
1103
|
+
ctx.notify('info', shown.length === 0
|
|
1104
|
+
? `service ${service.id}: no output yet.`
|
|
1105
|
+
: `service ${service.id} (last ${shown.length} rows)\n${shown.join('\n')}`);
|
|
1106
|
+
return;
|
|
1107
|
+
}
|
|
1108
|
+
if (verb === 'stop') {
|
|
1109
|
+
if (!value) {
|
|
1110
|
+
ctx.notify('warn', 'Usage: /bg stop <id|all>');
|
|
1111
|
+
return;
|
|
1112
|
+
}
|
|
1113
|
+
// AWAITED, unlike `doExit`'s force path: a slash command runs on a live
|
|
1114
|
+
// event loop, so the graceful SIGTERM -> SIGKILL ladder can run and a
|
|
1115
|
+
// dev server gets its chance to release its port cleanly.
|
|
1116
|
+
const stopped = await controller.stopService(value);
|
|
1117
|
+
if (stopped.length === 0) {
|
|
1118
|
+
ctx.notify('info', 'Nothing to stop.');
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
1121
|
+
const leaked = stopped.filter((svc) => svc.killIncomplete);
|
|
1122
|
+
ctx.toast('success', `Stopped ${stopped.length} service${stopped.length === 1 ? '' : 's'}.`);
|
|
1123
|
+
if (leaked.length > 0) {
|
|
1124
|
+
// AN HONEST WARNING BEATS A SILENT LEAK (R-2 / P2-6). `taskkill /t`
|
|
1125
|
+
// cannot reach a grandchild that re-parented itself, and saying so is
|
|
1126
|
+
// the difference between a puzzle and a `netstat` the user knows to run.
|
|
1127
|
+
ctx.notify('warn', `${leaked.map((svc) => svc.id).join(', ')} may have left a detached child - ` +
|
|
1128
|
+
'check with netstat/lsof if a port is still held.');
|
|
1129
|
+
}
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
if (verb !== 'list' && verb !== '') {
|
|
1133
|
+
ctx.notify('warn', `Unknown argument "${verb}" - use /bg [list|logs|stop|status].`);
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1136
|
+
if (services.length === 0) {
|
|
1137
|
+
ctx.notify('info', 'No background services this session.');
|
|
1138
|
+
return;
|
|
1139
|
+
}
|
|
1140
|
+
const now = Date.now();
|
|
1141
|
+
const lines = services.map((svc) => {
|
|
1142
|
+
const uptime = formatDuration((svc.endedAt ?? now) - svc.startedAt);
|
|
1143
|
+
const where = svc.url ? ` ${svc.url}` : '';
|
|
1144
|
+
return `${svc.id} ${svc.status} ${uptime}${where} $ ${svc.command}`;
|
|
1145
|
+
});
|
|
1146
|
+
ctx.notify('info', `Background services:\n${lines.join('\n')}`);
|
|
1147
|
+
},
|
|
1148
|
+
},
|
|
1051
1149
|
{
|
|
1052
1150
|
name: 'exit',
|
|
1053
1151
|
aliases: ['quit'],
|
|
@@ -1055,6 +1153,8 @@ const COMMANDS = [
|
|
|
1055
1153
|
run: (ctx) => ctx.exit(),
|
|
1056
1154
|
},
|
|
1057
1155
|
];
|
|
1156
|
+
/** Rows `/bg logs <id>` shows when the user names no count. */
|
|
1157
|
+
const DEFAULT_BG_LOG_ROWS = 40;
|
|
1058
1158
|
export function registerBuiltinCommands(registry) {
|
|
1059
1159
|
for (const command of COMMANDS)
|
|
1060
1160
|
registry.register(command);
|
package/dist/compaction/child.js
CHANGED
|
@@ -85,6 +85,11 @@ export function createChildContextManager(req, deps) {
|
|
|
85
85
|
}
|
|
86
86
|
if (event.type !== 'compaction_end')
|
|
87
87
|
return;
|
|
88
|
+
// DEFENCE IN DEPTH SINCE quiet-noop D-8: a child compaction that declines
|
|
89
|
+
// before committing to work no longer emits this event at all, so this
|
|
90
|
+
// filter is now unreachable on that path. It is retained deliberately -
|
|
91
|
+
// it is this tree's own record that a no-op compaction is a non-event, and
|
|
92
|
+
// deleting it would make the child depend on that rule holding forever.
|
|
88
93
|
if (!event.record.applied && event.record.mode === 'none')
|
|
89
94
|
return;
|
|
90
95
|
req.onCompacted();
|
|
@@ -66,9 +66,22 @@ export function formatCompactionStatus(ctx) {
|
|
|
66
66
|
lines.push(` Summarizer: ${snapshot.model || '(none)'}` +
|
|
67
67
|
(config.useFastTier ? ' (fast tier preferred)' : ' (fast tier not used)'));
|
|
68
68
|
lines.push(` On summarize failure: ${config.onFailure}`);
|
|
69
|
+
// THE PARENTHETICAL IS BUILT FROM WHATEVER APPLIES, AND OMITTED WHEN NOTHING
|
|
70
|
+
// DOES (quiet-noop D-9). `declined` counts checkpoints that fired and chose to
|
|
71
|
+
// do nothing - the only human-readable trace of a decline once the transcript
|
|
72
|
+
// stopped carrying a card for one. It renders ONLY when non-zero: a counter
|
|
73
|
+
// that says `(0 checkpoints declined)` in every healthy session would have
|
|
74
|
+
// replaced one piece of noise with another.
|
|
75
|
+
const sessionNotes = [];
|
|
76
|
+
if (snapshot.generation > 0)
|
|
77
|
+
sessionNotes.push(`generation ${snapshot.generation}`);
|
|
78
|
+
const declined = snapshot.declined ?? 0;
|
|
79
|
+
if (declined > 0) {
|
|
80
|
+
sessionNotes.push(`${declined} ${declined === 1 ? 'checkpoint' : 'checkpoints'} declined`);
|
|
81
|
+
}
|
|
69
82
|
lines.push(` This session: ${snapshot.compactions} ` +
|
|
70
83
|
`${snapshot.compactions === 1 ? 'compaction' : 'compactions'}` +
|
|
71
|
-
(
|
|
84
|
+
(sessionNotes.length > 0 ? ` (${sessionNotes.join(', ')})` : '') +
|
|
72
85
|
`, ${formatTokens(snapshot.tokensReclaimed)} tokens reclaimed`);
|
|
73
86
|
const cost = snapshot.pricingUnknown
|
|
74
87
|
? `unknown (no price table for ${snapshot.model || 'the summarizer'})`
|