@aragon-agent/cli 0.6.7 → 0.6.9
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 +27 -0
- package/README.md +107 -77
- package/dist/agent/controller.d.ts +11 -7
- package/dist/agent/controller.js +35 -48
- package/dist/agent/headless.d.ts +2 -1
- package/dist/agent/prompt-options.d.ts +15 -0
- package/dist/agent/prompt-options.js +1 -0
- package/dist/agent/reducer.d.ts +7 -113
- package/dist/agent/reducer.js +0 -62
- package/dist/cli.js +111 -182
- package/dist/commands/builtins.js +3 -1
- package/dist/commands/perf.d.ts +32 -2
- package/dist/commands/perf.js +32 -2
- package/dist/commands/registry.d.ts +4 -5
- package/dist/config/env.js +0 -130
- package/dist/config/load.d.ts +0 -2
- package/dist/config/load.js +2 -31
- package/dist/config/schema.d.ts +1 -14
- package/dist/config/schema.js +0 -1
- package/dist/config/store.js +2 -0
- package/dist/input/interrupt-gesture.d.ts +18 -0
- package/dist/input/interrupt-gesture.js +19 -0
- package/dist/input/pointer-router.d.ts +11 -0
- package/dist/input/pointer-router.js +14 -0
- package/dist/logging/install.d.ts +2 -2
- package/dist/logging/install.js +2 -2
- package/dist/todo/follow-through.d.ts +0 -4
- package/dist/todo/follow-through.js +0 -4
- package/dist/todo/limits.d.ts +0 -10
- package/dist/todo/limits.js +0 -10
- package/dist/todo/prompt.d.ts +0 -15
- package/dist/todo/store.d.ts +8 -13
- package/dist/todo/store.js +13 -14
- package/dist/todo/types.d.ts +2 -1
- package/dist/ui/ActivityLine.d.ts +26 -34
- package/dist/ui/ActivityLine.js +30 -19
- package/dist/ui/App.d.ts +3 -41
- package/dist/ui/App.js +223 -247
- package/dist/ui/BottomStatusRow.d.ts +1 -3
- package/dist/ui/BottomStatusRow.js +4 -8
- package/dist/ui/Composer.d.ts +38 -5
- package/dist/ui/Composer.js +45 -23
- package/dist/ui/Header.d.ts +2 -16
- package/dist/ui/Header.js +3 -18
- package/dist/ui/Logo.d.ts +1 -17
- package/dist/ui/Logo.js +0 -16
- package/dist/ui/ModeChip.d.ts +7 -18
- package/dist/ui/ModeChip.js +9 -0
- package/dist/ui/PromptCaret.d.ts +11 -0
- package/dist/ui/PromptCaret.js +31 -0
- package/dist/ui/PromptInput.d.ts +10 -33
- package/dist/ui/PromptInput.js +37 -20
- package/dist/ui/StatusBar.d.ts +1 -11
- package/dist/ui/StatusBar.js +14 -8
- package/dist/ui/ToastStack.d.ts +2 -18
- package/dist/ui/ToastStack.js +4 -11
- package/dist/ui/Transcript.d.ts +5 -71
- package/dist/ui/Transcript.js +10 -199
- package/dist/ui/composer-rows.js +50 -9
- package/dist/ui/entries/AssistantEntry.d.ts +0 -23
- package/dist/ui/entries/AssistantEntry.js +3 -4
- package/dist/ui/entries/DiffView.d.ts +1 -7
- package/dist/ui/entries/DiffView.js +2 -2
- package/dist/ui/entries/ToolCard.d.ts +0 -6
- package/dist/ui/entries/ToolCard.js +4 -13
- package/dist/ui/frame-differ.d.ts +13 -0
- package/dist/ui/frame-differ.js +37 -24
- package/dist/ui/frame-observer.d.ts +17 -0
- package/dist/ui/frame-observer.js +81 -0
- package/dist/ui/frame-parser.d.ts +7 -0
- package/dist/ui/frame-parser.js +18 -0
- package/dist/ui/ink-runtime.d.ts +1 -0
- package/dist/ui/ink-runtime.js +21 -0
- package/dist/ui/layout/AppShell.d.ts +4 -58
- package/dist/ui/layout/AppShell.js +3 -6
- package/dist/ui/layout/OverlayFrame.d.ts +1 -25
- package/dist/ui/layout/OverlayFrame.js +0 -24
- package/dist/ui/layout/ScrollIndicator.d.ts +4 -3
- package/dist/ui/layout/ScrollIndicator.js +4 -4
- package/dist/ui/layout/ScrollViewport.d.ts +16 -66
- package/dist/ui/layout/ScrollViewport.js +195 -185
- package/dist/ui/layout/budget.d.ts +3 -51
- package/dist/ui/layout/budget.js +4 -53
- package/dist/ui/layout/document-layout.d.ts +13 -0
- package/dist/ui/layout/document-layout.js +11 -0
- package/dist/ui/layout/follow-state.d.ts +1 -0
- package/dist/ui/layout/follow-state.js +1 -3
- package/dist/ui/layout/frame.d.ts +0 -22
- package/dist/ui/layout/frame.js +0 -36
- package/dist/ui/layout/scroll-indicator.d.ts +1 -1
- package/dist/ui/layout/scroll-indicator.js +3 -2
- package/dist/ui/layout/scrollbar-geometry.d.ts +17 -0
- package/dist/ui/layout/scrollbar-geometry.js +13 -0
- package/dist/ui/layout/todo-layout.d.ts +2 -3
- package/dist/ui/layout/todo-layout.js +9 -9
- package/dist/ui/layout/viewport-geometry.d.ts +2 -1
- package/dist/ui/layout/viewport-geometry.js +2 -1
- package/dist/ui/layout/virtual-window.d.ts +1 -0
- package/dist/ui/layout/virtual-window.js +1 -1
- package/dist/ui/live-clamp.d.ts +1 -29
- package/dist/ui/live-clamp.js +1 -29
- package/dist/ui/overlays/HelpOverlay.js +6 -2
- package/dist/ui/run-status-row.d.ts +61 -0
- package/dist/ui/run-status-row.js +70 -0
- package/dist/ui/screen.d.ts +1 -1
- package/dist/ui/screen.js +4 -6
- package/dist/ui/scrollbar-controller.d.ts +30 -0
- package/dist/ui/scrollbar-controller.js +124 -0
- package/dist/ui/transcript-text.d.ts +0 -8
- package/dist/ui/transcript-text.js +0 -8
- package/dist/ui/use-startup-notices.d.ts +1 -57
- package/dist/ui/use-startup-notices.js +3 -54
- package/dist/ui/use-wheel-routing.d.ts +3 -1
- package/dist/ui/use-wheel-routing.js +6 -0
- package/package.json +2 -2
- package/dist/ui/TodoStrip.d.ts +0 -36
- package/dist/ui/TodoStrip.js +0 -32
package/dist/config/env.js
CHANGED
|
@@ -77,8 +77,6 @@ export function readEnvConfig() {
|
|
|
77
77
|
if (process.env.ARAGON_THINKING) {
|
|
78
78
|
partial.thinkingLevel = clampThinkingLevel(process.env.ARAGON_THINKING.trim(), 'off');
|
|
79
79
|
}
|
|
80
|
-
// Parsed by the POSITIVE list, like ARAGON_FULLSCREEN: anything else is left
|
|
81
|
-
// ABSENT so the config file underneath still wins.
|
|
82
80
|
const showThinking = process.env.ARAGON_SHOW_THINKING?.trim().toLowerCase();
|
|
83
81
|
if (showThinking !== undefined && showThinking.length > 0) {
|
|
84
82
|
partial.showThinking =
|
|
@@ -130,16 +128,7 @@ export function readEnvConfig() {
|
|
|
130
128
|
if (process.env.ARAGON_THEME) {
|
|
131
129
|
partial.theme = clampTheme(process.env.ARAGON_THEME.trim(), 'auto');
|
|
132
130
|
}
|
|
133
|
-
// ARAGON_FULLSCREEN=0|1 — the one channel that works where neither a flag nor
|
|
134
131
|
// a config file is reachable (containers, SSH, being spawned by another tool).
|
|
135
|
-
const fullscreen = process.env.ARAGON_FULLSCREEN?.trim().toLowerCase();
|
|
136
|
-
if (fullscreen !== undefined && fullscreen.length > 0) {
|
|
137
|
-
partial.fullscreen = fullscreen === '1' || fullscreen === 'true' || fullscreen === 'on' || fullscreen === 'yes';
|
|
138
|
-
}
|
|
139
|
-
// ARAGON_MOUSE=0 turns off wheel region routing. Parsed by the POSITIVE list
|
|
140
|
-
// above, exactly as ARAGON_FULLSCREEN is — NOT by `envBool` below, which uses
|
|
141
|
-
// a negative list. The two disagree on `ARAGON_MOUSE=disable`, and `mouse` is
|
|
142
|
-
// modelled on `--fullscreen`, so it uses the reader `--fullscreen` uses.
|
|
143
132
|
const mouse = process.env.ARAGON_MOUSE?.trim().toLowerCase();
|
|
144
133
|
if (mouse !== undefined && mouse.length > 0) {
|
|
145
134
|
partial.mouse = mouse === '1' || mouse === 'true' || mouse === 'on' || mouse === 'yes';
|
|
@@ -174,8 +163,6 @@ export function readEnvConfig() {
|
|
|
174
163
|
if (Number.isFinite(n) && n >= 0)
|
|
175
164
|
partial.scrollResumeMs = n;
|
|
176
165
|
}
|
|
177
|
-
// ARAGON_PLAN=1 starts the session in PLAN mode — the same channel story as
|
|
178
|
-
// ARAGON_FULLSCREEN, for terminals reached through a wrapper that owns argv.
|
|
179
166
|
const plan = process.env.ARAGON_PLAN?.trim().toLowerCase();
|
|
180
167
|
if (plan !== undefined && plan.length > 0) {
|
|
181
168
|
partial.planModeDefault = plan === '1' || plan === 'true' || plan === 'on' || plan === 'yes';
|
|
@@ -191,12 +178,6 @@ export function readEnvConfig() {
|
|
|
191
178
|
enabled: !(skills === '0' || skills === 'false' || skills === 'off' || skills === 'no'),
|
|
192
179
|
};
|
|
193
180
|
}
|
|
194
|
-
// ARAGON_TEAM=0 turns team mode off; ARAGON_TEAM_MAX=<n> overrides the fan-out
|
|
195
|
-
// width. Parsed by the POSITIVE list, exactly as ARAGON_FULLSCREEN /
|
|
196
|
-
// ARAGON_MOUSE / ARAGON_PLAN are — NOT by `envBool` below, which uses a
|
|
197
|
-
// negative list. The two readers disagree on `ARAGON_TEAM=disable`, and the
|
|
198
|
-
// documented-but-dead outcome of copying the wrong one is what `load.ts`
|
|
199
|
-
// records for `ARAGON_MOUSE`.
|
|
200
181
|
const team = process.env.ARAGON_TEAM?.trim().toLowerCase();
|
|
201
182
|
const teamMax = process.env.ARAGON_TEAM_MAX?.trim();
|
|
202
183
|
if ((team !== undefined && team.length > 0) || (teamMax !== undefined && teamMax.length > 0)) {
|
|
@@ -217,27 +198,6 @@ export function readEnvConfig() {
|
|
|
217
198
|
if (Object.keys(section).length > 0)
|
|
218
199
|
partial.team = section;
|
|
219
200
|
}
|
|
220
|
-
// ARAGON_TODO=0 turns todo planning off entirely (no tool, no rail). Parsed by
|
|
221
|
-
// the POSITIVE list, exactly as ARAGON_FULLSCREEN / ARAGON_MOUSE / ARAGON_PLAN
|
|
222
|
-
// / ARAGON_TEAM are — NOT by `envBool` below, which uses a negative list. The
|
|
223
|
-
// two readers disagree on values like `ARAGON_TODO=disable`, and this file
|
|
224
|
-
// already records that once for team mode.
|
|
225
|
-
//
|
|
226
|
-
// There is no `ARAGON_TODO_PANEL`: the panel is a per-machine display
|
|
227
|
-
// preference, which is what `config.json` is for, and every env var here
|
|
228
|
-
// exists because a flag and a config file are BOTH unreachable in the channel
|
|
229
|
-
// it serves (containers, SSH, being spawned by another tool).
|
|
230
|
-
//
|
|
231
|
-
// ARAGON_TODO_FOLLOW=notify|auto|off PASSES THAT SAME TEST (D-18), which is
|
|
232
|
-
// why it exists where `ARAGON_TODO_PANEL` does not: follow-through is exactly
|
|
233
|
-
// the knob an unattended `-p` run in a container with a fixed entrypoint needs
|
|
234
|
-
// and can reach no other way.
|
|
235
|
-
//
|
|
236
|
-
// ONE ACCUMULATED SECTION, ASSIGNED ONCE — the shape the `team` branch above
|
|
237
|
-
// already uses, and NOT two `if` blocks each assigning `partial.todo` (P1-4).
|
|
238
|
-
// THE CAST IS WHAT WOULD MAKE THAT MISTAKE SILENT: a second assignment
|
|
239
|
-
// compiles clean and drops `enabled`, so `ARAGON_TODO=0 ARAGON_TODO_FOLLOW=auto`
|
|
240
|
-
// would quietly re-enable the tool the user turned off.
|
|
241
201
|
const todo = process.env.ARAGON_TODO?.trim().toLowerCase();
|
|
242
202
|
const todoFollow = process.env.ARAGON_TODO_FOLLOW?.trim().toLowerCase();
|
|
243
203
|
if ((todo !== undefined && todo.length > 0) ||
|
|
@@ -260,24 +220,6 @@ export function readEnvConfig() {
|
|
|
260
220
|
if (Object.keys(section).length > 0)
|
|
261
221
|
partial.todo = section;
|
|
262
222
|
}
|
|
263
|
-
// ARAGON_BASH_BACKGROUND=0 turns background services off entirely (no
|
|
264
|
-
// `background` param, no `bash_output`/`bash_kill`, no prompt block, no chip);
|
|
265
|
-
// ARAGON_BASH_AUTO_BACKGROUND=0 keeps them but stops the classifier firing on
|
|
266
|
-
// an unset `background`. Parsed by the POSITIVE list, exactly as
|
|
267
|
-
// ARAGON_FULLSCREEN / ARAGON_MOUSE / ARAGON_PLAN / ARAGON_TEAM / ARAGON_TODO
|
|
268
|
-
// are - NOT by `envBool` below, which uses a negative list.
|
|
269
|
-
//
|
|
270
|
-
// Both pass the test every env var in this file has to pass: an unattended run
|
|
271
|
-
// in a container with a fixed entrypoint can reach neither a flag nor a config
|
|
272
|
-
// file, and "do not leave a dev server running in my CI job" is exactly the
|
|
273
|
-
// kind of decision that channel needs to make.
|
|
274
|
-
//
|
|
275
|
-
// ONE ACCUMULATED SECTION, ASSIGNED ONCE - the shape the `team` and `todo`
|
|
276
|
-
// branches above already use, and NOT two `if` blocks each assigning
|
|
277
|
-
// `partial.bash` (the `todo` P1-4 trap). THE CAST IS WHAT WOULD MAKE THAT
|
|
278
|
-
// MISTAKE SILENT: a second assignment compiles clean and drops the first key,
|
|
279
|
-
// so `ARAGON_BASH_BACKGROUND=0 ARAGON_BASH_AUTO_BACKGROUND=0` would quietly
|
|
280
|
-
// re-register the tools the user turned off.
|
|
281
223
|
const bashBackground = process.env.ARAGON_BASH_BACKGROUND?.trim().toLowerCase();
|
|
282
224
|
const bashAuto = process.env.ARAGON_BASH_AUTO_BACKGROUND?.trim().toLowerCase();
|
|
283
225
|
if ((bashBackground !== undefined && bashBackground.length > 0) ||
|
|
@@ -300,22 +242,6 @@ export function readEnvConfig() {
|
|
|
300
242
|
if (Object.keys(section).length > 0)
|
|
301
243
|
partial.bash = section;
|
|
302
244
|
}
|
|
303
|
-
// ARAGON_RETRY=0 is the kill switch for API retry; ARAGON_RETRY_MAX=<n>
|
|
304
|
-
// overrides the count. Parsed by the POSITIVE list, exactly as
|
|
305
|
-
// ARAGON_FULLSCREEN / ARAGON_MOUSE / ARAGON_PLAN / ARAGON_TEAM / ARAGON_TODO
|
|
306
|
-
// are — NOT by `envBool` below, which uses a negative list.
|
|
307
|
-
//
|
|
308
|
-
// TWO VARS ONLY, and no third for the delays: every env var in this file exists
|
|
309
|
-
// because a flag and a config file are BOTH unreachable in the channel it
|
|
310
|
-
// serves, and nobody tunes a backoff multiplier from a container entrypoint.
|
|
311
|
-
//
|
|
312
|
-
// `>= 0`, NOT `> 0`, and the difference is the whole point (R-16).
|
|
313
|
-
// `ARAGON_TEAM_MAX` above guards `parsed > 0`, which is right for a fan-out
|
|
314
|
-
// width and wrong here: `0` is this key's documented floor AND its kill switch,
|
|
315
|
-
// so a `> 0` guard would drop `ARAGON_RETRY_MAX=0` on the floor and leave the
|
|
316
|
-
// user with the default 10. "Left ABSENT when unparseable" still holds and
|
|
317
|
-
// still matters — an unparseable value must not look permanently supplied, or
|
|
318
|
-
// the config file can never win again.
|
|
319
245
|
const retry = process.env.ARAGON_RETRY?.trim().toLowerCase();
|
|
320
246
|
const retryMax = process.env.ARAGON_RETRY_MAX?.trim();
|
|
321
247
|
if ((retry !== undefined && retry.length > 0) || (retryMax !== undefined && retryMax.length > 0)) {
|
|
@@ -333,26 +259,6 @@ export function readEnvConfig() {
|
|
|
333
259
|
if (Object.keys(section).length > 0)
|
|
334
260
|
partial.retry = section;
|
|
335
261
|
}
|
|
336
|
-
// ARAGON_FAST=1 turns the fast model tier on; the other three name it
|
|
337
|
-
// (fast-model-tier §4.3). Parsed by the POSITIVE list, exactly as
|
|
338
|
-
// ARAGON_FULLSCREEN / ARAGON_MOUSE / ARAGON_PLAN / ARAGON_TEAM / ARAGON_TODO /
|
|
339
|
-
// ARAGON_RETRY are — NOT by `envBool` below, whose NEGATIVE list disagrees with
|
|
340
|
-
// it on values like `ARAGON_FAST=disable`.
|
|
341
|
-
//
|
|
342
|
-
// FOUR VARS, AND DELIBERATELY NO `ARAGON_FAST_REVIEW_EVERY` / `_MAX_CHARS` /
|
|
343
|
-
// `_DELEGATE` (D-14). The rule this file states is that an environment variable
|
|
344
|
-
// exists only where a flag AND a config file are BOTH unreachable — a
|
|
345
|
-
// container, an ssh session, being spawned by another tool. "Which model, and
|
|
346
|
-
// is it on" passes that test; tuning a review cadence does not, and every key
|
|
347
|
-
// that fails it is documentation debt plus a fifth place for the value to
|
|
348
|
-
// disagree with itself.
|
|
349
|
-
//
|
|
350
|
-
// ONE ACCUMULATED SECTION, ASSIGNED ONCE — the shape the `team`, `todo` and
|
|
351
|
-
// `retry` branches above all use, and NOT four `if` blocks each assigning
|
|
352
|
-
// `partial.fast`. THE CAST IS WHAT WOULD MAKE THAT MISTAKE SILENT: a second
|
|
353
|
-
// assignment compiles clean and drops `enabled`, so
|
|
354
|
-
// `ARAGON_FAST=1 ARAGON_FAST_MODEL=x` would quietly resolve with the tier off
|
|
355
|
-
// (the P1-4 defect this file records for `todo`).
|
|
356
262
|
const fast = process.env.ARAGON_FAST?.trim().toLowerCase();
|
|
357
263
|
const fastProvider = process.env.ARAGON_FAST_PROVIDER?.trim();
|
|
358
264
|
const fastModel = process.env.ARAGON_FAST_MODEL?.trim();
|
|
@@ -376,25 +282,6 @@ export function readEnvConfig() {
|
|
|
376
282
|
if (Object.keys(section).length > 0)
|
|
377
283
|
partial.fast = section;
|
|
378
284
|
}
|
|
379
|
-
// ARAGON_COMPACTION=0 turns context compaction off; the other two tune it
|
|
380
|
-
// (context-auto-compaction §4.3). Parsed by the POSITIVE list, exactly as
|
|
381
|
-
// ARAGON_FULLSCREEN / ARAGON_MOUSE / ARAGON_PLAN / ARAGON_TEAM / ARAGON_TODO /
|
|
382
|
-
// ARAGON_RETRY / ARAGON_FAST are — NOT by `envBool` below, whose NEGATIVE list
|
|
383
|
-
// disagrees with it on values like `ARAGON_COMPACTION=disable`.
|
|
384
|
-
//
|
|
385
|
-
// THREE VARS, AND DELIBERATELY NO `ARAGON_COMPACTION_ON_FAILURE` /
|
|
386
|
-
// `_USE_FAST_TIER` / `_WARN_THRESHOLD`. The rule this file states is that an
|
|
387
|
-
// environment variable exists only where a flag AND a config file are BOTH
|
|
388
|
-
// unreachable — a container, an ssh session, being spawned by another tool.
|
|
389
|
-
// "Is it on, when does it fire, and how much does it keep" passes that test;
|
|
390
|
-
// "what should it do if the summarizer 500s" does not, and every key that fails
|
|
391
|
-
// it is documentation debt plus a fifth place for the value to disagree with
|
|
392
|
-
// itself.
|
|
393
|
-
//
|
|
394
|
-
// ONE ACCUMULATED SECTION, ASSIGNED ONCE — the shape the four branches above
|
|
395
|
-
// all use, and NOT three `if` blocks each assigning `partial.compaction`. THE
|
|
396
|
-
// CAST IS WHAT WOULD MAKE THAT MISTAKE SILENT: a second assignment compiles
|
|
397
|
-
// clean and drops `enabled`.
|
|
398
285
|
const compaction = process.env.ARAGON_COMPACTION?.trim().toLowerCase();
|
|
399
286
|
const compactionThreshold = process.env.ARAGON_COMPACTION_THRESHOLD?.trim();
|
|
400
287
|
const compactionKeepTurns = process.env.ARAGON_COMPACTION_KEEP_TURNS?.trim();
|
|
@@ -471,23 +358,12 @@ export function readEnvConfig() {
|
|
|
471
358
|
partial.update = { mode };
|
|
472
359
|
}
|
|
473
360
|
}
|
|
474
|
-
// Render bounds (tui-render-performance §5.3). Same channel story as
|
|
475
|
-
// ARAGON_FULLSCREEN: a container or a wrapper that owns argv can reach neither
|
|
476
|
-
// a flag nor a config file, and a session that freezes is exactly the case
|
|
477
|
-
// where the user needs a knob they can turn from the outside.
|
|
478
|
-
//
|
|
479
|
-
// ARAGON_TRANSCRIPT_RETAIN / ARAGON_MAX_RENDER_INTERVAL_MS are left ABSENT
|
|
480
|
-
// when unparseable rather than written as a default, so the config file can
|
|
481
|
-
// still win — the rule `ARAGON_TEAM_MAX` above records.
|
|
482
361
|
const retain = process.env.ARAGON_TRANSCRIPT_RETAIN?.trim();
|
|
483
362
|
if (retain !== undefined && retain.length > 0) {
|
|
484
363
|
const parsed = Number.parseInt(retain, 10);
|
|
485
364
|
if (Number.isFinite(parsed) && parsed > 0)
|
|
486
365
|
partial.transcriptRetain = parsed;
|
|
487
366
|
}
|
|
488
|
-
// Parsed by the POSITIVE list, exactly as ARAGON_FULLSCREEN / ARAGON_MOUSE /
|
|
489
|
-
// ARAGON_PLAN / ARAGON_TEAM / ARAGON_TODO are — NOT by `envBool` below, which
|
|
490
|
-
// uses a negative list. The two readers disagree on `ARAGON_RENDER_GOVERNOR=disable`.
|
|
491
367
|
const governor = process.env.ARAGON_RENDER_GOVERNOR?.trim().toLowerCase();
|
|
492
368
|
if (governor !== undefined && governor.length > 0) {
|
|
493
369
|
partial.renderGovernor =
|
|
@@ -499,12 +375,6 @@ export function readEnvConfig() {
|
|
|
499
375
|
if (Number.isFinite(parsed) && parsed > 0)
|
|
500
376
|
partial.maxRenderIntervalMs = parsed;
|
|
501
377
|
}
|
|
502
|
-
// ARAGON_DIFF_RENDER=0 restores the pre-fix full-frame repaint;
|
|
503
|
-
// ARAGON_SYNC_OUTPUT=0 drops the DEC 2026 envelope (tui-input-flicker-fix
|
|
504
|
-
// §5.3). Parsed by the POSITIVE list, exactly as ARAGON_FULLSCREEN /
|
|
505
|
-
// ARAGON_MOUSE / ARAGON_PLAN / ARAGON_TEAM / ARAGON_TODO / ARAGON_RENDER_GOVERNOR
|
|
506
|
-
// are — NOT by `envBool` below, whose NEGATIVE list disagrees with it on values
|
|
507
|
-
// like `ARAGON_DIFF_RENDER=disable`.
|
|
508
378
|
const diffRender = process.env.ARAGON_DIFF_RENDER?.trim().toLowerCase();
|
|
509
379
|
if (diffRender !== undefined && diffRender.length > 0) {
|
|
510
380
|
partial.diffRender =
|
package/dist/config/load.d.ts
CHANGED
|
@@ -19,8 +19,6 @@ export interface CliFlags {
|
|
|
19
19
|
confirm?: boolean;
|
|
20
20
|
toolTimeout?: string | number;
|
|
21
21
|
idleTimeout?: string | number;
|
|
22
|
-
/** `--fullscreen` ⇒ true, `--no-fullscreen` ⇒ false, absent ⇒ undefined. */
|
|
23
|
-
fullscreen?: boolean;
|
|
24
22
|
/** `--no-exit-transcript` ⇒ false. */
|
|
25
23
|
exitTranscript?: boolean;
|
|
26
24
|
/** `--compact` ⇒ true, `--no-compact` ⇒ false, absent ⇒ undefined. */
|
package/dist/config/load.js
CHANGED
|
@@ -23,32 +23,11 @@ function envFlagTrue(value) {
|
|
|
23
23
|
const v = value.trim().toLowerCase();
|
|
24
24
|
return v === '1' || v === 'true' || v === 'on' || v === 'yes';
|
|
25
25
|
}
|
|
26
|
-
/**
|
|
27
|
-
* Resolve the tri-state full-screen preference (see `CliConfig.fullscreen`).
|
|
28
|
-
* Only a flag or the env var counts as an explicit FORCE; a config file holding
|
|
29
|
-
* the default `true` must not disable the automatic downgrades.
|
|
30
|
-
*/
|
|
31
|
-
function resolveFullscreen(flags, env, file) {
|
|
32
|
-
if (flags.fullscreen !== undefined)
|
|
33
|
-
return flags.fullscreen;
|
|
34
|
-
if (env.fullscreen !== undefined)
|
|
35
|
-
return env.fullscreen;
|
|
36
|
-
if (file.fullscreen === false)
|
|
37
|
-
return false;
|
|
38
|
-
return undefined;
|
|
39
|
-
}
|
|
40
26
|
/**
|
|
41
27
|
* Resolve wheel region routing: flag › env › file › default `true`.
|
|
42
28
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* reads NO ENV AT ALL — copying that line would ship `ARAGON_MOUSE` as
|
|
46
|
-
* documented-but-dead (P1-9). Only `fullscreen` has an env layer, and only
|
|
47
|
-
* because `resolveFullscreen` above was written to give it one.
|
|
48
|
-
*
|
|
49
|
-
* Unlike `fullscreen` this is a plain boolean rather than a tri-state: there
|
|
50
|
-
* are no heuristics for an explicit `true` to override, so a persisted `true`
|
|
51
|
-
* is just a value and not an opinion that has to be told apart from silence.
|
|
29
|
+
* Unlike `hints`, mouse routing accepts an environment override. Keep that
|
|
30
|
+
* layer explicit so ARAGON_MOUSE works even when a wrapper owns the arguments.
|
|
52
31
|
*/
|
|
53
32
|
function resolveMouse(flags, env, file) {
|
|
54
33
|
if (flags.mouse !== undefined)
|
|
@@ -463,12 +442,6 @@ export function loadConfig(flags = {}) {
|
|
|
463
442
|
const read = readConfigFile();
|
|
464
443
|
const file = read.config ?? {};
|
|
465
444
|
const env = readEnvConfig();
|
|
466
|
-
// A config file that exists but will not parse used to reset EVERY setting
|
|
467
|
-
// with nothing anywhere explaining it. The fallback is unchanged; what is new
|
|
468
|
-
// is that it leaves a trace. The record alone is not the explanation, so the
|
|
469
|
-
// two owners of a user-visible channel say it too: `main()` writes one stderr
|
|
470
|
-
// line (headless, inline and every subcommand) and `App.tsx` raises a toast
|
|
471
|
-
// (full-screen, where the alternate screen wipes that line away).
|
|
472
445
|
if (read.parseError) {
|
|
473
446
|
getLogger().error('config', 'config_parse_failed', { error: read.parseError });
|
|
474
447
|
}
|
|
@@ -580,7 +553,6 @@ export function loadConfig(flags = {}) {
|
|
|
580
553
|
contextWindow,
|
|
581
554
|
theme,
|
|
582
555
|
reducedMotion,
|
|
583
|
-
fullscreen: resolveFullscreen(flags, env.partial, file),
|
|
584
556
|
exitTranscript: flags.exitTranscript !== undefined
|
|
585
557
|
? flags.exitTranscript
|
|
586
558
|
: file.exitTranscript ?? DEFAULT_CONFIG.exitTranscript,
|
|
@@ -658,7 +630,6 @@ function recordResolvedConfig(config) {
|
|
|
658
630
|
// "why is my output truncated" was unanswerable after the fact.
|
|
659
631
|
maxTokens: config.maxTokens ?? 'auto',
|
|
660
632
|
theme: config.theme,
|
|
661
|
-
fullscreen: config.fullscreen,
|
|
662
633
|
toolTimeoutMs: config.toolTimeoutMs,
|
|
663
634
|
idleTimeoutMs: config.idleTimeoutMs,
|
|
664
635
|
historyEnabled: config.historyEnabled,
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -729,7 +729,6 @@ export interface PersistedConfig {
|
|
|
729
729
|
/** Replace spinners with a static glyph for calmer, low-motion output. */
|
|
730
730
|
reducedMotion: boolean;
|
|
731
731
|
/** Full-screen TUI (still subject to the automatic downgrades in §4.1). */
|
|
732
|
-
fullscreen: boolean;
|
|
733
732
|
/** Replay a plain-text session summary after leaving the alternate screen. */
|
|
734
733
|
exitTranscript: boolean;
|
|
735
734
|
/** Entries reachable by scrolling; older ones show the "collapsed" line (I-3). */
|
|
@@ -767,8 +766,7 @@ export interface PersistedConfig {
|
|
|
767
766
|
* transcript, whatever row the pointer is on (wheel-scrolls-transcript-only
|
|
768
767
|
* §4.1).
|
|
769
768
|
*
|
|
770
|
-
* A
|
|
771
|
-
* heuristics for an explicit `true` to override (D-10).
|
|
769
|
+
* A plain boolean: persisted values and per-run overrides use the same meaning.
|
|
772
770
|
*/
|
|
773
771
|
mouse: boolean;
|
|
774
772
|
/**
|
|
@@ -881,17 +879,6 @@ export interface CliConfig {
|
|
|
881
879
|
contextWindow: number | null;
|
|
882
880
|
theme: ThemeName;
|
|
883
881
|
reducedMotion: boolean;
|
|
884
|
-
/**
|
|
885
|
-
* TRI-STATE, and deliberately so (spec §4.1 vs §6.2):
|
|
886
|
-
* `false` — an explicit opt-out from any layer; always inline.
|
|
887
|
-
* `true` — an explicit FORCE, only from `--fullscreen` / `ARAGON_FULLSCREEN=1`;
|
|
888
|
-
* overrides the TERM/CI/size heuristics.
|
|
889
|
-
* `undefined` — no explicit choice (including a config file holding the
|
|
890
|
-
* default `true`); full-screen with the heuristics live.
|
|
891
|
-
* Folding the persisted default into `true` would turn every user's config
|
|
892
|
-
* file into a permanent "ignore the CI and TERM=dumb downgrades" switch.
|
|
893
|
-
*/
|
|
894
|
-
fullscreen?: boolean;
|
|
895
882
|
/** Replay a plain-text summary on exit (full-screen only — see §4.4). */
|
|
896
883
|
exitTranscript: boolean;
|
|
897
884
|
/** Entries reachable by scrolling; older ones show the "collapsed" line (I-3). */
|
package/dist/config/schema.js
CHANGED
package/dist/config/store.js
CHANGED
|
@@ -69,6 +69,8 @@ export function readConfigFile() {
|
|
|
69
69
|
const raw = readFileSync(file, 'utf-8');
|
|
70
70
|
const parsed = JSON.parse(raw);
|
|
71
71
|
if (parsed && typeof parsed === 'object') {
|
|
72
|
+
// Retired layout preference: tolerate old files without restoring a UI mode.
|
|
73
|
+
delete parsed.fullscreen;
|
|
72
74
|
return { config: parsed };
|
|
73
75
|
}
|
|
74
76
|
return { config: null, parseError: `${file} does not contain a JSON object` };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Confirmation window for two independent decoded Escape events. */
|
|
2
|
+
export declare const INTERRUPT_CONFIRM_MS = 1500;
|
|
3
|
+
export type InterruptGestureState = {
|
|
4
|
+
phase: 'ready';
|
|
5
|
+
} | {
|
|
6
|
+
phase: 'armed';
|
|
7
|
+
armedAt: number;
|
|
8
|
+
} | {
|
|
9
|
+
phase: 'stopping';
|
|
10
|
+
};
|
|
11
|
+
export interface InterruptGestureResult {
|
|
12
|
+
state: InterruptGestureState;
|
|
13
|
+
action: 'hint' | 'abort' | 'force-stop';
|
|
14
|
+
}
|
|
15
|
+
/** Advance using a monotonic clock; the caller owns the current run's lifetime. */
|
|
16
|
+
export declare function advanceInterruptGesture(state: InterruptGestureState, now: number): InterruptGestureResult;
|
|
17
|
+
/** Other input cancels confirmation, but cannot undo an already requested stop. */
|
|
18
|
+
export declare function cancelInterruptConfirmation(state: InterruptGestureState): InterruptGestureState;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Confirmation window for two independent decoded Escape events. */
|
|
2
|
+
export const INTERRUPT_CONFIRM_MS = 1500;
|
|
3
|
+
/** Advance using a monotonic clock; the caller owns the current run's lifetime. */
|
|
4
|
+
export function advanceInterruptGesture(state, now) {
|
|
5
|
+
if (state.phase === 'stopping') {
|
|
6
|
+
return { state: { phase: 'ready' }, action: 'force-stop' };
|
|
7
|
+
}
|
|
8
|
+
if (state.phase === 'armed') {
|
|
9
|
+
const elapsed = now - state.armedAt;
|
|
10
|
+
if (elapsed >= 0 && elapsed <= INTERRUPT_CONFIRM_MS) {
|
|
11
|
+
return { state: { phase: 'stopping' }, action: 'abort' };
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return { state: { phase: 'armed', armedAt: now }, action: 'hint' };
|
|
15
|
+
}
|
|
16
|
+
/** Other input cancels confirmation, but cannot undo an already requested stop. */
|
|
17
|
+
export function cancelInterruptConfirmation(state) {
|
|
18
|
+
return state.phase === 'armed' ? { phase: 'ready' } : state;
|
|
19
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { MouseEvent } from './mouse-events.js';
|
|
2
|
+
import type { MouseSource } from './stdin-filter.js';
|
|
3
|
+
export interface PointerRouter {
|
|
4
|
+
selectionSource: MouseSource;
|
|
5
|
+
dispose(): void;
|
|
6
|
+
}
|
|
7
|
+
/** Deliver each button gesture to the scrollbar before the selection source. */
|
|
8
|
+
export declare function createPointerRouter(options: {
|
|
9
|
+
source: MouseSource;
|
|
10
|
+
handle(event: MouseEvent): boolean;
|
|
11
|
+
}): PointerRouter;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Deliver each button gesture to the scrollbar before the selection source. */
|
|
2
|
+
export function createPointerRouter(options) {
|
|
3
|
+
const listeners = new Set();
|
|
4
|
+
const unsubscribe = options.source.subscribe((event) => {
|
|
5
|
+
if (event.kind !== 'wheel' && options.handle(event))
|
|
6
|
+
return;
|
|
7
|
+
for (const listener of listeners)
|
|
8
|
+
listener(event);
|
|
9
|
+
});
|
|
10
|
+
return { selectionSource: { subscribe: (listener) => {
|
|
11
|
+
listeners.add(listener);
|
|
12
|
+
return () => { listeners.delete(listener); };
|
|
13
|
+
} }, dispose: () => { unsubscribe(); listeners.clear(); } };
|
|
14
|
+
}
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* Adding a listener suppresses it, turning a crash into a silent hang.
|
|
26
26
|
*
|
|
27
27
|
* `SIGINT` / `SIGTERM` / `SIGHUP` — the handlers in `runInteractive()` exist
|
|
28
|
-
* ONLY in its full-screen branch, so
|
|
28
|
+
* ONLY in its full-screen branch, so `-p` and every
|
|
29
29
|
* subcommand had no handler at all and took Node's default termination,
|
|
30
30
|
* which does not run `'exit'` listeners: the whole queue was lost. But a
|
|
31
31
|
* signal listener registered here runs BEFORE the one `runInteractive`
|
|
@@ -45,7 +45,7 @@ import { Logger, type LogScope } from './logger.js';
|
|
|
45
45
|
export declare function setScreenRestore(fn: () => void): void;
|
|
46
46
|
/**
|
|
47
47
|
* Replace what a signal does after the flush. The default (a bare
|
|
48
|
-
* `exit(128 + signo)`) is correct for headless
|
|
48
|
+
* `exit(128 + signo)`) is correct for headless and every subcommand;
|
|
49
49
|
* full-screen substitutes one that restores the terminal first.
|
|
50
50
|
*
|
|
51
51
|
* A TERMINATOR MAY NOW RETURN WITHOUT EXITING, AND THAT IS A CONTRACT CHANGE
|
package/dist/logging/install.js
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* Adding a listener suppresses it, turning a crash into a silent hang.
|
|
26
26
|
*
|
|
27
27
|
* `SIGINT` / `SIGTERM` / `SIGHUP` — the handlers in `runInteractive()` exist
|
|
28
|
-
* ONLY in its full-screen branch, so
|
|
28
|
+
* ONLY in its full-screen branch, so `-p` and every
|
|
29
29
|
* subcommand had no handler at all and took Node's default termination,
|
|
30
30
|
* which does not run `'exit'` listeners: the whole queue was lost. But a
|
|
31
31
|
* signal listener registered here runs BEFORE the one `runInteractive`
|
|
@@ -63,7 +63,7 @@ export function setScreenRestore(fn) {
|
|
|
63
63
|
}
|
|
64
64
|
/**
|
|
65
65
|
* Replace what a signal does after the flush. The default (a bare
|
|
66
|
-
* `exit(128 + signo)`) is correct for headless
|
|
66
|
+
* `exit(128 + signo)`) is correct for headless and every subcommand;
|
|
67
67
|
* full-screen substitutes one that restores the terminal first.
|
|
68
68
|
*
|
|
69
69
|
* A TERMINATOR MAY NOW RETURN WITHOUT EXITING, AND THAT IS A CONTRACT CHANGE
|
|
@@ -84,10 +84,6 @@ export type FollowThroughDecision = {
|
|
|
84
84
|
};
|
|
85
85
|
/** The zero budget. A fresh App, a fresh headless run, and every cleared list. */
|
|
86
86
|
export declare function emptyBudget(): FollowThroughBudget;
|
|
87
|
-
/**
|
|
88
|
-
* The decision. EVALUATED IN THIS ORDER, AND THE ORDER IS SEMANTICS, NOT STYLE —
|
|
89
|
-
* the same sentence `decideRenderMode` carries.
|
|
90
|
-
*/
|
|
91
87
|
export declare function decideFollowThrough(input: FollowThroughInput): FollowThroughDecision;
|
|
92
88
|
/**
|
|
93
89
|
* Fold a finished run into the budget. Called BEFORE `decideFollowThrough`, on
|
|
@@ -36,10 +36,6 @@ function legacyUnfinishedNotice(left) {
|
|
|
36
36
|
function advise(interactive, text) {
|
|
37
37
|
return interactive ? ` ${text}` : '';
|
|
38
38
|
}
|
|
39
|
-
/**
|
|
40
|
-
* The decision. EVALUATED IN THIS ORDER, AND THE ORDER IS SEMANTICS, NOT STYLE —
|
|
41
|
-
* the same sentence `decideRenderMode` carries.
|
|
42
|
-
*/
|
|
43
39
|
export function decideFollowThrough(input) {
|
|
44
40
|
const { mode, snapshot, runEnd, budget, interactive } = input;
|
|
45
41
|
// 1. Nothing is unfinished. `beginUserTurn` already drops a fully-completed
|
package/dist/todo/limits.d.ts
CHANGED
|
@@ -38,16 +38,6 @@ export declare const TODO_LIMITS: {
|
|
|
38
38
|
readonly panelFullRows: 6;
|
|
39
39
|
/** Below this many terminal columns the status-bar counter degrades to `[3/7]`. */
|
|
40
40
|
readonly statusCompactCols: 100;
|
|
41
|
-
/**
|
|
42
|
-
* Below this many columns the inline strip drops its `+N done` suffix
|
|
43
|
-
* (todo-plan-followthrough §3.7 / D-19).
|
|
44
|
-
*
|
|
45
|
-
* NAMED RATHER THAN INLINED, and the reason is that it sits between two other
|
|
46
|
-
* real bounds — `statusCompactCols` (100) above and `MIN_FULLSCREEN_COLS` (40)
|
|
47
|
-
* in `frame.ts` — with nothing to tell a reader whether a bare `80` in the
|
|
48
|
-
* component was a third policy or a typo for one of them.
|
|
49
|
-
*/
|
|
50
|
-
readonly stripDoneCols: 80;
|
|
51
41
|
/**
|
|
52
42
|
* Consecutive user turns an UNFINISHED list survives without a `todo_write`
|
|
53
43
|
* before `beginUserTurn()` drops it (§3.2 / P1-7). Three keeps the common
|
package/dist/todo/limits.js
CHANGED
|
@@ -38,16 +38,6 @@ export const TODO_LIMITS = {
|
|
|
38
38
|
panelFullRows: 6,
|
|
39
39
|
/** Below this many terminal columns the status-bar counter degrades to `[3/7]`. */
|
|
40
40
|
statusCompactCols: 100,
|
|
41
|
-
/**
|
|
42
|
-
* Below this many columns the inline strip drops its `+N done` suffix
|
|
43
|
-
* (todo-plan-followthrough §3.7 / D-19).
|
|
44
|
-
*
|
|
45
|
-
* NAMED RATHER THAN INLINED, and the reason is that it sits between two other
|
|
46
|
-
* real bounds — `statusCompactCols` (100) above and `MIN_FULLSCREEN_COLS` (40)
|
|
47
|
-
* in `frame.ts` — with nothing to tell a reader whether a bare `80` in the
|
|
48
|
-
* component was a third policy or a typo for one of them.
|
|
49
|
-
*/
|
|
50
|
-
stripDoneCols: 80,
|
|
51
41
|
/**
|
|
52
42
|
* Consecutive user turns an UNFINISHED list survives without a `todo_write`
|
|
53
43
|
* before `beginUserTurn()` drops it (§3.2 / P1-7). Three keeps the common
|
package/dist/todo/prompt.d.ts
CHANGED
|
@@ -22,21 +22,6 @@
|
|
|
22
22
|
import { TODO_BLOCK_VERSION } from './limits.js';
|
|
23
23
|
export { TODO_BLOCK_VERSION };
|
|
24
24
|
export interface TodoBlockParams {
|
|
25
|
-
/**
|
|
26
|
-
* Whether this session can actually render the rail.
|
|
27
|
-
*
|
|
28
|
-
* FOUR SUPPORTED PATHS HAVE THE TOOL AND NO PANEL — `-p` (§3.12), inline mode
|
|
29
|
-
* (non-goal 4), `--no-todo-panel` (§4.3), and a terminal under 80 columns or 6
|
|
30
|
-
* viewport rows (§6.4) — and in the first three the answer is fixed for the
|
|
31
|
-
* whole session, before the prompt is composed. Telling the model about a
|
|
32
|
-
* panel the user does not have is the same error `composeSystemPrompt` already
|
|
33
|
-
* guards against for team mode with its TWO flags (P1-6 / D-24).
|
|
34
|
-
*
|
|
35
|
-
* Dropping the block instead would be wrong: the planning discipline is worth
|
|
36
|
-
* having without the column, which is precisely why `enabled` and `panel` are
|
|
37
|
-
* separate config keys, and a screen-reader user is the case that argument was
|
|
38
|
-
* written for.
|
|
39
|
-
*/
|
|
40
25
|
panelVisible: boolean;
|
|
41
26
|
}
|
|
42
27
|
/**
|
package/dist/todo/store.d.ts
CHANGED
|
@@ -9,15 +9,9 @@
|
|
|
9
9
|
* is why `AgentController` can own this object as unconditionally as it owns
|
|
10
10
|
* `TeamRuntime`.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* `TodoClearReason = 'user'`. Two commands come through that door: `/todo clear`
|
|
16
|
-
* and `/clear`. Neither tells the model anything, and that is fine, because what
|
|
17
|
-
* they leave behind is "panel absent, model still remembers" - the direction I-9
|
|
18
|
-
* below calls safe, and one the model's next full-replacement write heals on its
|
|
19
|
-
* own. `/reset` (reason `'reset'`) remains the only clear caused by the belief
|
|
20
|
-
* itself being destroyed, because it clears `messages`.
|
|
12
|
+
* The list projects the current plan. Explicit clear/reset and a new TUI task
|
|
13
|
+
* may remove that projection while the model still remembers the old plan.
|
|
14
|
+
* Continuations preserve it; legacy callers retain completion/staleness rules.
|
|
21
15
|
*
|
|
22
16
|
* Every write is a FULL REPLACEMENT and every clear is a FULL REMOVAL - there is
|
|
23
17
|
* no partial update anywhere, which is what makes I-9 ("the panel is never a
|
|
@@ -73,11 +67,12 @@ export declare class TodoStore {
|
|
|
73
67
|
* asymmetry for free, which is the behaviour you want: a steer must not age
|
|
74
68
|
* out the plan it is steering.
|
|
75
69
|
*
|
|
76
|
-
* A
|
|
77
|
-
*
|
|
78
|
-
*
|
|
70
|
+
* A new TUI task drops the old plan here, while a continuation preserves it.
|
|
71
|
+
* With no policy, legacy callers retain completed/stale-plan expiration.
|
|
72
|
+
* Clearing at `agent_end` would erase the final progress before the user can
|
|
73
|
+
* inspect it and remove the plan that automatic continuation still needs.
|
|
79
74
|
*/
|
|
80
|
-
beginUserTurn(): void;
|
|
75
|
+
beginUserTurn(policy?: 'new-task' | 'continue'): void;
|
|
81
76
|
/**
|
|
82
77
|
* Report a refusal that never reached `write()` (§3.3 steps 1 and 2).
|
|
83
78
|
*
|
package/dist/todo/store.js
CHANGED
|
@@ -9,15 +9,9 @@
|
|
|
9
9
|
* is why `AgentController` can own this object as unconditionally as it owns
|
|
10
10
|
* `TeamRuntime`.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* `TodoClearReason = 'user'`. Two commands come through that door: `/todo clear`
|
|
16
|
-
* and `/clear`. Neither tells the model anything, and that is fine, because what
|
|
17
|
-
* they leave behind is "panel absent, model still remembers" - the direction I-9
|
|
18
|
-
* below calls safe, and one the model's next full-replacement write heals on its
|
|
19
|
-
* own. `/reset` (reason `'reset'`) remains the only clear caused by the belief
|
|
20
|
-
* itself being destroyed, because it clears `messages`.
|
|
12
|
+
* The list projects the current plan. Explicit clear/reset and a new TUI task
|
|
13
|
+
* may remove that projection while the model still remembers the old plan.
|
|
14
|
+
* Continuations preserve it; legacy callers retain completion/staleness rules.
|
|
21
15
|
*
|
|
22
16
|
* Every write is a FULL REPLACEMENT and every clear is a FULL REMOVAL - there is
|
|
23
17
|
* no partial update anywhere, which is what makes I-9 ("the panel is never a
|
|
@@ -137,13 +131,18 @@ export class TodoStore {
|
|
|
137
131
|
* asymmetry for free, which is the behaviour you want: a steer must not age
|
|
138
132
|
* out the plan it is steering.
|
|
139
133
|
*
|
|
140
|
-
* A
|
|
141
|
-
*
|
|
142
|
-
*
|
|
134
|
+
* A new TUI task drops the old plan here, while a continuation preserves it.
|
|
135
|
+
* With no policy, legacy callers retain completed/stale-plan expiration.
|
|
136
|
+
* Clearing at `agent_end` would erase the final progress before the user can
|
|
137
|
+
* inspect it and remove the plan that automatic continuation still needs.
|
|
143
138
|
*/
|
|
144
|
-
beginUserTurn() {
|
|
145
|
-
if (this.items.length === 0)
|
|
139
|
+
beginUserTurn(policy) {
|
|
140
|
+
if (this.items.length === 0 || policy === 'continue')
|
|
141
|
+
return;
|
|
142
|
+
if (policy === 'new-task') {
|
|
143
|
+
this.clear('turn');
|
|
146
144
|
return;
|
|
145
|
+
}
|
|
147
146
|
if (this.items.every((item) => item.status === 'completed')) {
|
|
148
147
|
this.clear('turn');
|
|
149
148
|
return;
|
package/dist/todo/types.d.ts
CHANGED
|
@@ -45,7 +45,8 @@ export interface TodoSnapshot {
|
|
|
45
45
|
* `user` - `/todo clear`, or `/clear`. Both are an explicit user instruction,
|
|
46
46
|
* which is I-2's standing exception; neither touches `messages`.
|
|
47
47
|
* `reset` - `/reset`, or a `/resume` whose file carried no list (§3.13).
|
|
48
|
-
* `turn` -
|
|
48
|
+
* `turn` - a new TUI task started, or a legacy caller began a turn after
|
|
49
|
+
* every item was completed.
|
|
49
50
|
* `stale` - unfinished, and unwritten for more than `TODO_LIMITS.staleTurns`
|
|
50
51
|
* consecutive turns (§3.2 / P1-7).
|
|
51
52
|
*/
|