@aragon-agent/cli 0.6.2 → 0.6.4
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 +321 -0
- package/README.md +297 -2
- package/dist/agent/controller.d.ts +193 -1
- package/dist/agent/controller.js +475 -2
- package/dist/agent/headless.d.ts +8 -0
- package/dist/agent/headless.js +47 -0
- package/dist/agent/reducer.d.ts +189 -0
- package/dist/agent/reducer.js +301 -2
- package/dist/agent/system-prompt.d.ts +14 -0
- package/dist/agent/system-prompt.js +2 -0
- package/dist/cli.js +297 -16
- package/dist/commands/builtins.js +165 -24
- 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 +118 -0
- package/dist/compaction/command.d.ts +41 -0
- package/dist/compaction/command.js +355 -0
- package/dist/compaction/compactor.d.ts +302 -0
- package/dist/compaction/compactor.js +810 -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 +215 -0
- package/dist/compaction/types.js +22 -0
- package/dist/compaction/wiring.d.ts +264 -0
- package/dist/compaction/wiring.js +577 -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 +5 -2
- package/dist/config/env.js +140 -3
- package/dist/config/load.d.ts +42 -0
- package/dist/config/load.js +101 -1
- package/dist/config/schema.d.ts +244 -0
- package/dist/config/schema.js +170 -0
- package/dist/config/store.js +37 -1
- package/dist/config/ui-state.d.ts +28 -1
- package/dist/config/ui-state.js +9 -0
- package/dist/exec/events.d.ts +50 -1
- package/dist/exec/runner.js +42 -0
- package/dist/input/limits.d.ts +72 -0
- package/dist/input/limits.js +63 -0
- package/dist/input/mouse-events.d.ts +31 -5
- package/dist/input/mouse-events.js +86 -27
- 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 +53 -1
- package/dist/logging/install.js +81 -1
- package/dist/logging/logger.d.ts +1 -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 +55 -1
- 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/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/ActivityLine.d.ts +19 -1
- package/dist/ui/ActivityLine.js +10 -2
- package/dist/ui/App.d.ts +62 -2
- package/dist/ui/App.js +546 -16
- package/dist/ui/Composer.d.ts +66 -1
- package/dist/ui/Composer.js +50 -6
- package/dist/ui/PromptInput.d.ts +82 -3
- package/dist/ui/PromptInput.js +232 -49
- package/dist/ui/StatusBar.d.ts +59 -0
- package/dist/ui/StatusBar.js +16 -4
- package/dist/ui/TeamPanel.js +7 -1
- package/dist/ui/Transcript.d.ts +21 -1
- package/dist/ui/Transcript.js +61 -3
- package/dist/ui/clipboard.d.ts +52 -0
- package/dist/ui/clipboard.js +91 -0
- 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/CompactionCard.d.ts +75 -0
- package/dist/ui/entries/CompactionCard.js +158 -0
- 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/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 +44 -9
- package/dist/ui/layout/budget.js +42 -10
- 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 +85 -1
- package/dist/ui/overlays/HelpOverlay.js +9 -1
- package/dist/ui/overlays/PlanReviewOverlay.js +10 -2
- package/dist/ui/overlays/QuestionOverlay.js +5 -1
- package/dist/ui/overlays/SettingsScreen.d.ts +47 -1
- package/dist/ui/overlays/SettingsScreen.js +74 -2
- package/dist/ui/palettes.d.ts +16 -0
- package/dist/ui/palettes.js +6 -0
- 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 +38 -0
- package/dist/ui/screen.js +113 -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 +72 -0
- package/dist/ui/use-startup-notices.d.ts +27 -1
- package/dist/ui/use-startup-notices.js +43 -11
- package/dist/ui/use-wheel-routing.d.ts +1 -1
- package/dist/ui/use-wheel-routing.js +8 -0
- package/package.json +4 -2
- package/dist/input/stdin-mouse-filter.d.ts +0 -42
- package/dist/input/stdin-mouse-filter.js +0 -153
|
@@ -88,6 +88,20 @@ export interface SystemPromptParams {
|
|
|
88
88
|
* for a fixed `tools` array. Every existing caller passes nothing.
|
|
89
89
|
*/
|
|
90
90
|
appendSystemPrompt?: string;
|
|
91
|
+
/**
|
|
92
|
+
* The rendered `<background_services>` block, or `''`
|
|
93
|
+
* (background-service-supervision §5.2).
|
|
94
|
+
*
|
|
95
|
+
* Spliced conditionally exactly as `skillsBlock`, the plan block, `teamBlock`,
|
|
96
|
+
* `todoBlock` and `fastBlock` are, which is what keeps I-2 true:
|
|
97
|
+
* `bash.background: false` produces the pre-feature prompt BYTE FOR BYTE for a
|
|
98
|
+
* fixed tool array.
|
|
99
|
+
*
|
|
100
|
+
* A CHILD'S PROMPT NEVER CARRIES IT: subagents build tools from the same
|
|
101
|
+
* factory but are given no supervisor, so telling one about `bash_output` /
|
|
102
|
+
* `bash_kill` would describe tools it does not have.
|
|
103
|
+
*/
|
|
104
|
+
backgroundBlock?: string;
|
|
91
105
|
}
|
|
92
106
|
/** Describe the active shell so the model does not emit incompatible syntax. */
|
|
93
107
|
export declare function describeShell(): {
|
|
@@ -41,6 +41,7 @@ export function buildSystemPrompt(params) {
|
|
|
41
41
|
const subagentBlock = params.subagentBlock ?? '';
|
|
42
42
|
const todoBlock = params.todoBlock ?? '';
|
|
43
43
|
const fastBlock = params.fastBlock ?? '';
|
|
44
|
+
const backgroundBlock = params.backgroundBlock ?? '';
|
|
44
45
|
const appended = (params.appendSystemPrompt ?? '').trim();
|
|
45
46
|
return [
|
|
46
47
|
'You are AragonAgent, an autonomous coding assistant operating inside a terminal (TUI).',
|
|
@@ -59,6 +60,7 @@ export function buildSystemPrompt(params) {
|
|
|
59
60
|
...(subagentBlock ? ['', subagentBlock] : []),
|
|
60
61
|
...(todoBlock ? ['', todoBlock] : []),
|
|
61
62
|
...(fastBlock ? ['', fastBlock] : []),
|
|
63
|
+
...(backgroundBlock ? ['', backgroundBlock] : []),
|
|
62
64
|
'',
|
|
63
65
|
'Operating guidance:',
|
|
64
66
|
'- Prefer reading files and inspecting the workspace before making changes.',
|
package/dist/cli.js
CHANGED
|
@@ -23,11 +23,11 @@ import { formatMigrationNotice, migrateLegacyState } from './config/migrate-lega
|
|
|
23
23
|
import { formatHomeMigrationNotice, migrateToHome } from './config/migrate-home.js';
|
|
24
24
|
import { migrateStateOutOfConfig } from './config/migrate-state-out-of-config.js';
|
|
25
25
|
import { runHistoryCommand } from './config/history-commands.js';
|
|
26
|
-
import { FAST_CONFIG_SET_KEYS, LOG_CONFIG_SET_KEYS, RETRY_CONFIG_SET_KEYS, TEAM_CONFIG_SET_KEYS, UPDATE_CONFIG_SET_KEYS, applyFastConfigSet, applyLogConfigSet, applyRetryConfigSet, applyTeamConfigSet, applyUpdateConfigSet, runConfigEdit, runConfigGet, runConfigHome, runConfigList, warnIfRedactionDisabled, } from './config/cli-commands.js';
|
|
26
|
+
import { COMPACTION_CONFIG_SET_KEYS, FAST_CONFIG_SET_KEYS, LOG_CONFIG_SET_KEYS, RETRY_CONFIG_SET_KEYS, TEAM_CONFIG_SET_KEYS, UPDATE_CONFIG_SET_KEYS, applyCompactionConfigSet, applyFastConfigSet, applyLogConfigSet, applyRetryConfigSet, applyTeamConfigSet, applyUpdateConfigSet, runConfigEdit, runConfigGet, runConfigHome, runConfigList, warnIfRedactionDisabled, } from './config/cli-commands.js';
|
|
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, clampTranscriptRetain, clampTranscriptWindow, coercePositiveInt, isAdapterProvider, parseMaxTokensInput, DEFAULT_MAX_RENDER_INTERVAL_MS, DEFAULT_MAX_TOKENS, DEFAULT_PLAN_HUMAN_TIMEOUT_MS, DEFAULT_PLAN_MAX_ASK_ROUNDS, DEFAULT_SKILLS_CONFIG, DEFAULT_TRANSCRIPT_RETAIN, DEFAULT_TRANSCRIPT_WINDOW, } from './config/schema.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';
|
|
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';
|
|
@@ -52,6 +52,9 @@ import { readExitSnapshot } from './ui/exit-snapshot.js';
|
|
|
52
52
|
import { renderTranscriptText } from './ui/transcript-text.js';
|
|
53
53
|
import { detectCapabilities } from './ui/capabilities.js';
|
|
54
54
|
import { pickGlyphs } from './ui/glyphs.js';
|
|
55
|
+
import { getTheme } from './ui/theme.js';
|
|
56
|
+
import { copyText } from './ui/clipboard.js';
|
|
57
|
+
import { createSelectionController, } from './ui/selection/selection-controller.js';
|
|
55
58
|
import { DENY_ALL_HUMAN_INPUT } from './tools/human-input.js';
|
|
56
59
|
// A VALUE import, and the one deliberate exception to the paragraph above
|
|
57
60
|
// (cli-auto-update-hardening P2-7). `boot/guard.js` statically pulls
|
|
@@ -105,6 +108,16 @@ function toFlags(opts) {
|
|
|
105
108
|
// Without this line the flag never reaches `CliFlags` and `--no-mouse` is
|
|
106
109
|
// inert — a failure with no compile error and no runtime error (P1-2).
|
|
107
110
|
mouse: opts.mouse,
|
|
111
|
+
// Site three of three for `--mouse-select` / `--no-mouse-select` — NINE
|
|
112
|
+
// features, one trap. A flag needs the `.option()` declaration below, a field
|
|
113
|
+
// on `CliFlags`, AND a line here; `config.test.ts` is the regression pin that
|
|
114
|
+
// exists because `--no-mouse` shipped inert once already.
|
|
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,
|
|
108
121
|
skills: opts.skills,
|
|
109
122
|
skill: opts.skill,
|
|
110
123
|
skillsYes: opts.skillsYes,
|
|
@@ -171,6 +184,15 @@ function toFlags(opts) {
|
|
|
171
184
|
// means `--no-update` reports nothing, does nothing, and leaves the updater
|
|
172
185
|
// running.
|
|
173
186
|
update: opts.update,
|
|
187
|
+
// Site three of three for `--compaction` / `--no-compaction` — NINE
|
|
188
|
+
// features, one trap. A flag needs the `.option()` declaration below, a field
|
|
189
|
+
// on `CliFlags`, AND a line here; miss this one and the flag is inert with no
|
|
190
|
+
// compile error and no runtime error. It bites here for the same reason it
|
|
191
|
+
// does one field up: this is the KILL SWITCH for a feature that spends the
|
|
192
|
+
// user's money on a summarizer, so a missing line means `--no-compaction`
|
|
193
|
+
// reports nothing, does nothing, and leaves compaction armed.
|
|
194
|
+
compaction: opts.compaction,
|
|
195
|
+
compactionThreshold: opts.compactionThreshold,
|
|
174
196
|
};
|
|
175
197
|
}
|
|
176
198
|
// ---------------------------------------------------------------------------
|
|
@@ -399,7 +421,7 @@ function runInteractive(flags, extras = {}) {
|
|
|
399
421
|
// before it reaches this process, so `\x1b[?1000h` is a pure no-op and the
|
|
400
422
|
// filter has nothing to filter. Gating `wantMouse` — rather than the three
|
|
401
423
|
// things downstream of it — is what keeps the three in agreement: the
|
|
402
|
-
// `?1000h` write (`enterAltScreen(..., { mouse:
|
|
424
|
+
// `?1000h` write (`enterAltScreen(..., { mouse: mouseOn })`), the
|
|
403
425
|
// filter itself, and the `mouseSource` prop that decides whether the user is
|
|
404
426
|
// advised about a mode that is not running. Wiring any of them separately
|
|
405
427
|
// re-opens the gap where the app claims a capability it does not have.
|
|
@@ -417,10 +439,50 @@ function runInteractive(flags, extras = {}) {
|
|
|
417
439
|
!!process.stdout.isTTY &&
|
|
418
440
|
!!process.stdin.isTTY &&
|
|
419
441
|
vtInputSupported;
|
|
420
|
-
|
|
421
|
-
|
|
442
|
+
// --- Paste handling (tui-paste-handling section 5.1.1 / D-13 / D-17). ----
|
|
443
|
+
//
|
|
444
|
+
// THE GATE IS WIDER THAN THE MOUSE'S, AND `paste` DEFAULTS TO TRUE. Paste
|
|
445
|
+
// needs no `vtInputSupported` and no full-screen: Tier 2 is a heuristic on
|
|
446
|
+
// chunk shape and needs no terminal cooperation at all, which is exactly why
|
|
447
|
+
// it exists for the consoles that never deliver `\x1b[200~` (R-11).
|
|
448
|
+
//
|
|
449
|
+
// WHAT THIS WIDENING BREAKS IF IT IS NOT PAIRED WITH `mouseOn` BELOW (P0-1).
|
|
450
|
+
// `filter !== null` used to be the answer to FIVE questions, only one of which
|
|
451
|
+
// is really about the stream. With paste able to build a filter on its own,
|
|
452
|
+
// `aragon --no-mouse` would get a non-null handle and every one of those five
|
|
453
|
+
// would flip true: `?1000h`/`?1006h`/`?1002h` written, terminal selection taken
|
|
454
|
+
// away (the ONLY reason anyone passes `--no-mouse`), and a startup notice
|
|
455
|
+
// advising the user about a mode they just disabled.
|
|
456
|
+
const wantPaste = config.paste && !!process.stdin.isTTY;
|
|
457
|
+
const pasteBridge = { notify: null };
|
|
458
|
+
const filter = wantMouse || wantPaste
|
|
459
|
+
? tryCreateStdinFilter(process.stdin, { mouse: wantMouse, paste: wantPaste }, (reason) => logger.warn('cli', 'stdin_filter_failed', { reason }), pasteBridge)
|
|
422
460
|
: null;
|
|
423
|
-
|
|
461
|
+
/**
|
|
462
|
+
* THE ONE EXPRESSION EVERY MOUSE SITE READS (I-11 / D-17).
|
|
463
|
+
*
|
|
464
|
+
* `filter !== null` now answers exactly one question -- "is a stream wrapped?"
|
|
465
|
+
* -- and the only site allowed to keep reading it is the `stdin` handed to
|
|
466
|
+
* `render()`.
|
|
467
|
+
*/
|
|
468
|
+
const mouseOn = wantMouse && filter !== null;
|
|
469
|
+
/** Same discipline for DEC 2004: already decided here, never re-derived. */
|
|
470
|
+
const pasteOn = wantPaste && filter !== null;
|
|
471
|
+
const disposeStdinFilter = () => filter?.dispose();
|
|
472
|
+
// --- Drag-select (tui-selection-and-scroll-follow §4.4). ------------------
|
|
473
|
+
//
|
|
474
|
+
// ONE EXPRESSION, READ BY FOUR THINGS, and that is the same discipline
|
|
475
|
+
// `wantMouse` above follows for the same reason: the `?1002h` write, the
|
|
476
|
+
// controller, the differ's `decorate` hook and the startup notice must never
|
|
477
|
+
// disagree about whether drag-select is running. With it false the session is
|
|
478
|
+
// byte-identical to a pre-feature build — no extra escape on the wire (AC-8),
|
|
479
|
+
// no controller, no `decorate`, no mirror.
|
|
480
|
+
const wantSelect = mouseOn && config.mouseSelect;
|
|
481
|
+
const selectionBridge = {
|
|
482
|
+
controller: null,
|
|
483
|
+
onCopied: null,
|
|
484
|
+
requestRedraw: null,
|
|
485
|
+
};
|
|
424
486
|
// --- Frame differ (tui-input-flicker-fix §3.2 / §4.2). -------------------
|
|
425
487
|
//
|
|
426
488
|
// FULL-SCREEN ONLY, and that is invariant I-3 rather than caution: inline mode
|
|
@@ -443,6 +505,25 @@ function runInteractive(flags, extras = {}) {
|
|
|
443
505
|
// edge inside the differ: a notice per fallback would be a repaint
|
|
444
506
|
// storm triggered by a repaint problem (§5.6).
|
|
445
507
|
onFirstFallback: () => console.warn(FRAME_FALLBACK_NOTICE),
|
|
508
|
+
// ABSENT ENTIRELY without drag-select, not a no-op function: the
|
|
509
|
+
// differ's identity path then allocates nothing and copies nothing,
|
|
510
|
+
// which is what makes `mouseSelect: false` a statement about the code
|
|
511
|
+
// rather than about how cheap an empty function is.
|
|
512
|
+
...(wantSelect
|
|
513
|
+
? {
|
|
514
|
+
decorate: (lines) => selectionBridge.controller?.decorate(lines) ?? lines,
|
|
515
|
+
// P1-7: a pass-through frame writes the RAW Ink frame verbatim,
|
|
516
|
+
// so the highlight is wiped off the terminal while the
|
|
517
|
+
// controller still believes it is painted. Deferred out of the
|
|
518
|
+
// current stack because this fires from inside `transform`,
|
|
519
|
+
// which is inside Ink's own `stdout.write`.
|
|
520
|
+
onInvalidate: () => {
|
|
521
|
+
const controller = selectionBridge.controller;
|
|
522
|
+
if (controller)
|
|
523
|
+
setImmediate(() => controller.clear());
|
|
524
|
+
},
|
|
525
|
+
}
|
|
526
|
+
: {}),
|
|
446
527
|
}))
|
|
447
528
|
: null;
|
|
448
529
|
// `null` when no writer was built, so `/perf` can say "diff render off" rather
|
|
@@ -476,10 +557,21 @@ function runInteractive(flags, extras = {}) {
|
|
|
476
557
|
}));
|
|
477
558
|
};
|
|
478
559
|
if (mode === 'fullscreen') {
|
|
479
|
-
// `mouse` here means "a filter is installed", NOT "the user
|
|
480
|
-
// support" — see `AltScreenOptions`. `screen.ts` must never
|
|
481
|
-
// itself, or a later refactor re-opens the gap I-8 closes.
|
|
482
|
-
|
|
560
|
+
// `mouse` here means "a mouse-parsing filter is installed", NOT "the user
|
|
561
|
+
// wants mouse support" — see `AltScreenOptions`. `screen.ts` must never
|
|
562
|
+
// derive it for itself, or a later refactor re-opens the gap I-8 closes.
|
|
563
|
+
// `motion` carries the same discipline for `?1002h`, and `bracketedPaste`
|
|
564
|
+
// for `?2004h`: all three are already decided, none is re-derived.
|
|
565
|
+
//
|
|
566
|
+
// IT IS `mouseOn`, NOT `filter !== null` (P0-1). A `--no-mouse` session with
|
|
567
|
+
// paste at its default `true` still builds a filter, and passing the handle
|
|
568
|
+
// here would write `?1000h` at a user who disabled the mouse precisely to
|
|
569
|
+
// keep their terminal's own selection.
|
|
570
|
+
screen = enterAltScreen(process.stdout, {
|
|
571
|
+
mouse: mouseOn,
|
|
572
|
+
motion: wantSelect,
|
|
573
|
+
bracketedPaste: pasteOn && mode === 'fullscreen',
|
|
574
|
+
});
|
|
483
575
|
const restore = () => screen?.restore();
|
|
484
576
|
// Four idempotent restore paths (§4.4). The signal hook is NOT redundant:
|
|
485
577
|
// - `waitUntilExit().then()` is a microtask and may never be reached when
|
|
@@ -516,19 +608,136 @@ function runInteractive(flags, extras = {}) {
|
|
|
516
608
|
// `CI` is excluded on top of the two TTY tests because a CI runner with a pty
|
|
517
609
|
// allocated is otherwise a machine that silently mutates its own global
|
|
518
610
|
// toolchain mid-pipeline (D-11).
|
|
611
|
+
// --- The foreign-write door (§4.4.5). ------------------------------------
|
|
612
|
+
//
|
|
613
|
+
// Where bytes that are OURS but are NOT a frame go — today, the OSC 52
|
|
614
|
+
// clipboard sequence. THREE SHAPES, ONE DOOR, and the third one is the reason
|
|
615
|
+
// this is a resolved value rather than an inline arrow:
|
|
616
|
+
//
|
|
617
|
+
// * with the differ in front of stdout it MUST be `frameWriter.writeForeign`,
|
|
618
|
+
// which invalidates the cache and skips the fallback accounting — through
|
|
619
|
+
// the proxy the same bytes are an unrecognised chunk, which is
|
|
620
|
+
// `passThrough(true)`, which prints `FRAME_FALLBACK_NOTICE` at a user who
|
|
621
|
+
// did nothing but copy something (P1-6);
|
|
622
|
+
// * without one — `--no-diff-render`, and inline mode, which never builds a
|
|
623
|
+
// differ at all — stdout IS the real stream, so a direct write is both safe
|
|
624
|
+
// and what §4.4.5 prescribes. OSC 52 emits no newline, so Ink's own line
|
|
625
|
+
// accounting is untouched and there is nothing to invalidate;
|
|
626
|
+
// * on a non-TTY there is no terminal to accept it, so the door is ABSENT.
|
|
627
|
+
//
|
|
628
|
+
// It must never be a function that accepts a chunk and writes NOWHERE.
|
|
629
|
+
// `copyText` reports `'osc52'` whenever its door took the text, so a silent
|
|
630
|
+
// no-op door — which `frameWriter?.writeForeign(chunk)` is under
|
|
631
|
+
// `--no-diff-render` — makes the toast name a mechanism that was never
|
|
632
|
+
// attempted. Naming the mechanism honestly is the entire reason that return
|
|
633
|
+
// value exists (R-7), and a door that lies is worse than no door.
|
|
634
|
+
const writeForeign = frameWriter
|
|
635
|
+
? (text) => frameWriter.writeForeign(text)
|
|
636
|
+
: process.stdout.isTTY
|
|
637
|
+
? (text) => {
|
|
638
|
+
try {
|
|
639
|
+
process.stdout.write(text);
|
|
640
|
+
}
|
|
641
|
+
catch {
|
|
642
|
+
// A closed or broken stdout must never turn a copy into a crash.
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
: null;
|
|
646
|
+
// --- The selection controller (§4.4). ------------------------------------
|
|
647
|
+
//
|
|
648
|
+
// BUILT AFTER `enterAltScreen` AND BEFORE `render()`. After, because it must
|
|
649
|
+
// not exist on a session that never asked the terminal to report motion;
|
|
650
|
+
// before, because `decorate` is read by the differ from the very first frame
|
|
651
|
+
// Ink produces.
|
|
652
|
+
//
|
|
653
|
+
// `mouseCaptured` is the run-time answer for `/mouse`, and it lives here rather
|
|
654
|
+
// than inside `screen.ts`'s closure because `App` needs to READ it and the
|
|
655
|
+
// handle's own flag is private (it has to be — `restore()` reads it, and
|
|
656
|
+
// exposing a setter would be a second way to get the two out of step).
|
|
657
|
+
let mouseCaptured = mouseOn;
|
|
658
|
+
if (wantSelect && filter) {
|
|
659
|
+
selectionBridge.controller = createSelectionController({
|
|
660
|
+
source: filter.source,
|
|
661
|
+
// The frame writer is absent under `--no-diff-render`, where there is no
|
|
662
|
+
// cache to repaint from at all; the controller then falls back to asking
|
|
663
|
+
// `App` for a React redraw on every drag flush, which is slower and
|
|
664
|
+
// correct. That is rung 2 of the fail-safe ladder doing its job.
|
|
665
|
+
repaint: () => frameWriter?.repaint() ?? false,
|
|
666
|
+
requestRedraw: () => selectionBridge.requestRedraw?.(),
|
|
667
|
+
// `?? undefined` and not a wrapper: `copyText` skips OSC 52 entirely when
|
|
668
|
+
// it has no door, and reports `'native'`/`'none'` accordingly.
|
|
669
|
+
copy: (text) => copyText(text, writeForeign ?? undefined),
|
|
670
|
+
onCopied: (via, lines, chars) => selectionBridge.onCopied?.(via, lines, chars),
|
|
671
|
+
// Read at PAINT time, not captured: `/theme` rebuilds the theme mid-session.
|
|
672
|
+
theme: () => getTheme(config.theme, detectCapabilities(process.env, process.stdout)),
|
|
673
|
+
caps: detectCapabilities(process.env, process.stdout),
|
|
674
|
+
cols: () => process.stdout.columns ?? 80,
|
|
675
|
+
// The STATIC half of the gate. The dynamic half — "no overlay is open" —
|
|
676
|
+
// belongs to `App`, which owns that state and calls `setEnabled`.
|
|
677
|
+
isSelectable: () => mouseCaptured,
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
const disposeSelection = () => {
|
|
681
|
+
selectionBridge.controller?.dispose();
|
|
682
|
+
selectionBridge.controller = null;
|
|
683
|
+
};
|
|
684
|
+
/**
|
|
685
|
+
* The terminal-facing services `App` cannot build for itself (§4.4).
|
|
686
|
+
*
|
|
687
|
+
* PRESENT IN INLINE MODE TOO, and only for the foreign-write door: §4.4.5
|
|
688
|
+
* routes `/copy` through `ui/clipboard.ts` precisely so it gains OSC 52 — the
|
|
689
|
+
* one mechanism that reaches the clipboard of the machine the USER is sitting
|
|
690
|
+
* at over SSH — and gating the whole bridge on full-screen would have handed
|
|
691
|
+
* that back only to full-screen users while the command is available in both.
|
|
692
|
+
* Every OTHER field stays absent there, so the inline tree is unchanged: there
|
|
693
|
+
* is no filter, hence no `setMouseCapture` and no `ctx.mouse`; `mouseSelect`
|
|
694
|
+
* is `wantSelect`, which is already false without one; and there is no
|
|
695
|
+
* controller, so no `selection`.
|
|
696
|
+
*/
|
|
697
|
+
const terminalBridge = mode === 'fullscreen' || writeForeign
|
|
698
|
+
? {
|
|
699
|
+
mouseSelect: wantSelect,
|
|
700
|
+
...(mouseOn
|
|
701
|
+
? {
|
|
702
|
+
setMouseCapture: (on) => {
|
|
703
|
+
screen?.setMouseCapture(on);
|
|
704
|
+
mouseCaptured = on;
|
|
705
|
+
// A released mouse cannot be dragging, and a selection left
|
|
706
|
+
// painted over a screen the terminal is now selecting on its
|
|
707
|
+
// own would be two highlights claiming the same rows.
|
|
708
|
+
if (!on)
|
|
709
|
+
selectionBridge.controller?.clear();
|
|
710
|
+
},
|
|
711
|
+
isMouseCaptured: () => mouseCaptured,
|
|
712
|
+
}
|
|
713
|
+
: {}),
|
|
714
|
+
// The same door the controller copies through, so `/copy` and a
|
|
715
|
+
// drag-release can never disagree about which stream OSC 52 goes to.
|
|
716
|
+
...(writeForeign ? { writeForeign } : {}),
|
|
717
|
+
...(selectionBridge.controller ? { selection: selectionBridge } : {}),
|
|
718
|
+
}
|
|
719
|
+
: undefined;
|
|
519
720
|
const updateBridge = { service: null, onAttach: null };
|
|
520
721
|
let updateService = null;
|
|
521
722
|
const updateEligible = !!process.stdout.isTTY &&
|
|
522
723
|
!!process.stdin.isTTY &&
|
|
523
724
|
!process.env.CI &&
|
|
524
725
|
config.update.mode !== 'off';
|
|
525
|
-
const instance = render(_jsx(App, { controller: controller, version: VERSION, mode: mode, confirmBridge: confirmBridge, humanInputBridge: humanInputBridge, initialPrompt: extras.initialPrompt, initialOverlay: extras.initialOverlay,
|
|
726
|
+
const instance = render(_jsx(App, { controller: controller, version: VERSION, mode: mode, confirmBridge: confirmBridge, humanInputBridge: humanInputBridge, initialPrompt: extras.initialPrompt, initialOverlay: extras.initialOverlay,
|
|
727
|
+
// `mouseOn`, NOT the handle (I-11). `App.tsx:186-188` documents this
|
|
728
|
+
// prop's contract as "absent under `--no-mouse`, inline, or non-TTY", and
|
|
729
|
+
// it feeds `enabled: !!mouseSource` on a wheel subscription that would
|
|
730
|
+
// otherwise be a channel no event can ever arrive on.
|
|
731
|
+
mouseSource: mouseOn && filter ? filter.source : undefined,
|
|
526
732
|
// Present ONLY when this console swallows `CSI Z` and mouse reports.
|
|
527
733
|
// Passed in rather than probed inside `App` so that every test renders
|
|
528
734
|
// the same tree on every machine — reading `process.versions.node` from
|
|
529
735
|
// a component would make the notice appear or vanish according to which
|
|
530
736
|
// Node the developer happened to have installed.
|
|
531
|
-
vtInputWarning: vtInputSupported ? undefined : { nodeVersion: process.versions.node }, updateBridge: updateEligible ? updateBridge : undefined
|
|
737
|
+
vtInputWarning: vtInputSupported ? undefined : { nodeVersion: process.versions.node }, updateBridge: updateEligible ? updateBridge : undefined,
|
|
738
|
+
// Absent when nothing can refuse a paste, so a `--no-paste` tree is
|
|
739
|
+
// byte-identical to a build without this feature.
|
|
740
|
+
pasteBridge: pasteOn ? pasteBridge : undefined, terminal: terminalBridge }),
|
|
532
741
|
// Full-screen owns console.* itself (I-4): Ink's patchConsole writes
|
|
533
742
|
// straight to stdout and permanently shifts the fixed frame's accounting.
|
|
534
743
|
{
|
|
@@ -536,7 +745,7 @@ function runInteractive(flags, extras = {}) {
|
|
|
536
745
|
patchConsole: mode === 'inline',
|
|
537
746
|
// I-5: with mouse support off the REAL stdin is handed over unwrapped, so
|
|
538
747
|
// every existing path is provably unchanged.
|
|
539
|
-
stdin:
|
|
748
|
+
stdin: filter?.stdin ?? process.stdin,
|
|
540
749
|
// SPREAD, not `frameWriter?.stdout ?? process.stdout`. This call passed no
|
|
541
750
|
// `stdout` at all before, and Ink defaults it to `process.stdout`; omitting
|
|
542
751
|
// the key entirely when the writer is off keeps the options object the same
|
|
@@ -597,7 +806,12 @@ function runInteractive(flags, extras = {}) {
|
|
|
597
806
|
.waitUntilExit()
|
|
598
807
|
.then(() => {
|
|
599
808
|
screen?.restore();
|
|
600
|
-
|
|
809
|
+
// BEFORE `disposeStdinFilter()`, which clears the listener set it subscribed to,
|
|
810
|
+
// and after `restore()` for the reason that call records: the terminal must
|
|
811
|
+
// stop reporting first, and anything already in flight is still consumed by
|
|
812
|
+
// a live filter.
|
|
813
|
+
disposeSelection();
|
|
814
|
+
disposeStdinFilter();
|
|
601
815
|
// BOTH BRANCHES, the same discipline `controller.dispose()` and
|
|
602
816
|
// `updateService?.dispose()` already follow. It drops the `'resize'`
|
|
603
817
|
// listener of I-8 and detaches the `/perf` provider so a later `/perf` in a
|
|
@@ -621,7 +835,12 @@ function runInteractive(flags, extras = {}) {
|
|
|
621
835
|
})
|
|
622
836
|
.catch(() => {
|
|
623
837
|
screen?.restore();
|
|
624
|
-
|
|
838
|
+
// BEFORE `disposeStdinFilter()`, which clears the listener set it subscribed to,
|
|
839
|
+
// and after `restore()` for the reason that call records: the terminal must
|
|
840
|
+
// stop reporting first, and anything already in flight is still consumed by
|
|
841
|
+
// a live filter.
|
|
842
|
+
disposeSelection();
|
|
843
|
+
disposeStdinFilter();
|
|
625
844
|
disposeFrameWriter();
|
|
626
845
|
controller.dispose();
|
|
627
846
|
updateService?.dispose();
|
|
@@ -737,6 +956,15 @@ const CONFIG_SET_KEYS = new Set([
|
|
|
737
956
|
// falls through every case, writes nothing, and still prints
|
|
738
957
|
// `Set mouse = false` (P1-2) -- exactly what happened to `density`/`hints`.
|
|
739
958
|
'mouse',
|
|
959
|
+
// Both cased in the switch below, for the reason the note above `mouse`
|
|
960
|
+
// records: a key listed here but absent there writes nothing and still prints
|
|
961
|
+
// `Set mouseSelect = false`.
|
|
962
|
+
'mouseSelect',
|
|
963
|
+
// Listed here AND cased in the switch below, for the reason the note above
|
|
964
|
+
// `mouse` records: a key present here but absent there falls through every
|
|
965
|
+
// case, writes nothing, and still prints `Set paste = false`.
|
|
966
|
+
'paste',
|
|
967
|
+
'scrollResumeMs',
|
|
740
968
|
'historyEnabled',
|
|
741
969
|
'planModeDefault',
|
|
742
970
|
'planModeMaxAskRounds',
|
|
@@ -763,6 +991,11 @@ const CONFIG_SET_KEYS = new Set([
|
|
|
763
991
|
...FAST_CONFIG_SET_KEYS,
|
|
764
992
|
// `update.*` keys are handled by `applyUpdateConfigSet`, same reasoning again.
|
|
765
993
|
...UPDATE_CONFIG_SET_KEYS,
|
|
994
|
+
// `compaction.*` keys are handled by `applyCompactionConfigSet`, same
|
|
995
|
+
// reasoning again — and this set is what makes the kill switch reachable from
|
|
996
|
+
// a script at all: `config list` enumerates the section dynamically and so
|
|
997
|
+
// shows these keys whether they are here or not, while `config set` does not.
|
|
998
|
+
...COMPACTION_CONFIG_SET_KEYS,
|
|
766
999
|
]);
|
|
767
1000
|
/**
|
|
768
1001
|
* `aragon config set maxTokens <n|auto|default>`.
|
|
@@ -849,6 +1082,18 @@ function runConfigSet(key, value) {
|
|
|
849
1082
|
process.stdout.write(`Set ${key} = ${String(merged.update[field])}\n`);
|
|
850
1083
|
return;
|
|
851
1084
|
}
|
|
1085
|
+
const compactionPatch = applyCompactionConfigSet(key, value);
|
|
1086
|
+
if (compactionPatch) {
|
|
1087
|
+
// Echoed from the STORED value, so `config set compaction.threshold 0.2`
|
|
1088
|
+
// prints the clamped 0.5 rather than reporting a number that was never
|
|
1089
|
+
// written — the rule `team.*`, `retry.*`, `fast.*` and `update.*` each state
|
|
1090
|
+
// above. It matters most for `warnThreshold`, which is clamped against the
|
|
1091
|
+
// stored `threshold` and so can move for a reason the caller cannot see.
|
|
1092
|
+
const merged = updatePersistedConfig(compactionPatch);
|
|
1093
|
+
const field = key.slice('compaction.'.length);
|
|
1094
|
+
process.stdout.write(`Set ${key} = ${String(merged.compaction[field])}\n`);
|
|
1095
|
+
return;
|
|
1096
|
+
}
|
|
852
1097
|
const patch = {};
|
|
853
1098
|
switch (key) {
|
|
854
1099
|
case 'provider':
|
|
@@ -914,6 +1159,19 @@ function runConfigSet(key, value) {
|
|
|
914
1159
|
case 'mouse':
|
|
915
1160
|
patch.mouse = value === 'true' || value === '1';
|
|
916
1161
|
break;
|
|
1162
|
+
case 'mouseSelect':
|
|
1163
|
+
patch.mouseSelect = value === 'true' || value === '1';
|
|
1164
|
+
break;
|
|
1165
|
+
case 'paste':
|
|
1166
|
+
patch.paste = value === 'true' || value === '1';
|
|
1167
|
+
break;
|
|
1168
|
+
// Clamped rather than rejected, for the same reason `planModeDefault` below
|
|
1169
|
+
// records: hardening only the READ path leaves a bad value on disk that
|
|
1170
|
+
// reverts to the default on every launch, which presents as "my setting
|
|
1171
|
+
// won't stick". `0` is a legal value here and its documented off switch.
|
|
1172
|
+
case 'scrollResumeMs':
|
|
1173
|
+
patch.scrollResumeMs = clampScrollResumeMs(value, DEFAULT_SCROLL_RESUME_MS);
|
|
1174
|
+
break;
|
|
917
1175
|
// Turning this off stops new prompts being recorded; it does NOT delete
|
|
918
1176
|
// what is already in `prompt-history.jsonl` (that is `aragon history clear`).
|
|
919
1177
|
case 'historyEnabled':
|
|
@@ -1172,6 +1430,19 @@ function buildProgram() {
|
|
|
1172
1430
|
// Positive form first, same tri-state reason as `--fullscreen` above.
|
|
1173
1431
|
.option('--mouse', 'Wheel scrolls the transcript (full-screen mode)')
|
|
1174
1432
|
.option('--no-mouse', 'Leave the mouse to the terminal (the wheel does nothing)')
|
|
1433
|
+
// Positive form FIRST, for the tri-state reason `--fullscreen` records
|
|
1434
|
+
// below: `mouseSelect` is persisted and defaults to `true`, so a lone
|
|
1435
|
+
// `--no-mouse-select` would make commander default `opts.mouseSelect` to
|
|
1436
|
+
// `true` — indistinguishable from silence, and silently overriding a stored
|
|
1437
|
+
// `false` on every run that passes no flag at all.
|
|
1438
|
+
.option('--mouse-select', 'Drag with the mouse to select text; releasing copies it')
|
|
1439
|
+
.option('--no-mouse-select', 'Keep wheel scrolling, but leave drag-select off')
|
|
1440
|
+
// Positive form FIRST, same tri-state reason as `--mouse-select` above:
|
|
1441
|
+
// `paste` is persisted and defaults to `true`, so a lone `--no-paste` would
|
|
1442
|
+
// make commander default `opts.paste` to `true` -- indistinguishable from
|
|
1443
|
+
// silence, and silently overriding a stored `false` on every run.
|
|
1444
|
+
.option('--paste', 'Collapse large pastes into a placeholder (the default)')
|
|
1445
|
+
.option('--no-paste', 'Treat pasted bytes as keystrokes (pre-0.6.3 behavior)')
|
|
1175
1446
|
// Declaring `--fullscreen` BEFORE `--no-fullscreen` keeps the default
|
|
1176
1447
|
// `undefined` instead of `true`, which is what makes the tri-state work:
|
|
1177
1448
|
// "no opinion" must stay distinguishable from "force it".
|
|
@@ -1228,6 +1499,16 @@ function buildProgram() {
|
|
|
1228
1499
|
// feature that installs software would quietly un-set itself (C-11).
|
|
1229
1500
|
.option('--update', 'Check for and install updates in the background (the default)')
|
|
1230
1501
|
.option('--no-update', 'Disable auto-update for this session')
|
|
1502
|
+
// BOTH FORMS, positive first, for the reason recorded eight times above —
|
|
1503
|
+
// and this pair shares `--update`'s polarity, which is the worst one.
|
|
1504
|
+
// `compaction.enabled` is PERSISTED and defaults to `true`, so a lone
|
|
1505
|
+
// `--no-compaction` would make commander default `opts.compaction` to `true`
|
|
1506
|
+
// and silently overwrite a stored `false` on EVERY run that passed no flag at
|
|
1507
|
+
// all: the kill switch for a feature that spends the user's money on a
|
|
1508
|
+
// summarizer would quietly un-set itself (C-13 / P1-5).
|
|
1509
|
+
.option('--compaction', 'Compact the conversation when the context window fills (the default)')
|
|
1510
|
+
.option('--no-compaction', 'Never compact; a full context window ends the run as before')
|
|
1511
|
+
.option('--compaction-threshold <n>', 'Occupancy that triggers compaction for this run: 0.9 or 90% (0.5-0.95)')
|
|
1231
1512
|
.option('--no-skills', 'Disable the skill system entirely (no catalog, no skill tools)')
|
|
1232
1513
|
.option('--skill <name>', 'Force-load a skill at Level 2 for this run (repeatable)', (value, previous = []) => [...previous, value])
|
|
1233
1514
|
.option('--skills-yes', 'Approve skill installs for this run (CI / headless)')
|