@aragon-agent/cli 0.5.4 → 0.5.8
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 +747 -577
- package/README.md +1163 -995
- package/dist/agent/controller.d.ts +71 -1
- package/dist/agent/controller.js +135 -1
- package/dist/agent/entry-limits.d.ts +65 -0
- package/dist/agent/entry-limits.js +133 -0
- package/dist/agent/headless.d.ts +29 -0
- package/dist/agent/headless.js +86 -1
- package/dist/agent/reducer.d.ts +53 -0
- package/dist/agent/reducer.js +145 -14
- package/dist/agent/system-prompt.d.ts +8 -0
- package/dist/agent/system-prompt.js +2 -0
- package/dist/cli.js +76 -3
- package/dist/commands/builtins.js +171 -0
- package/dist/commands/perf.d.ts +45 -0
- package/dist/commands/perf.js +78 -0
- package/dist/commands/registry.d.ts +27 -1
- package/dist/config/env.d.ts +3 -1
- package/dist/config/env.js +74 -1
- package/dist/config/load.d.ts +41 -0
- package/dist/config/load.js +67 -2
- package/dist/config/schema.d.ts +108 -8
- package/dist/config/schema.js +81 -6
- package/dist/config/store.js +10 -1
- package/dist/session/persist.d.ts +19 -1
- package/dist/session/persist.js +21 -2
- package/dist/team/subagent.js +11 -0
- package/dist/todo/follow-through.d.ts +117 -0
- package/dist/todo/follow-through.js +186 -0
- package/dist/todo/limits.d.ts +114 -0
- package/dist/todo/limits.js +114 -0
- package/dist/todo/normalize.d.ts +33 -0
- package/dist/todo/normalize.js +117 -0
- package/dist/todo/panel-rows.d.ts +53 -0
- package/dist/todo/panel-rows.js +103 -0
- package/dist/todo/prompt.d.ts +54 -0
- package/dist/todo/prompt.js +76 -0
- package/dist/todo/store.d.ts +86 -0
- package/dist/todo/store.js +183 -0
- package/dist/todo/todo-tool.d.ts +38 -0
- package/dist/todo/todo-tool.js +134 -0
- package/dist/todo/types.d.ts +62 -0
- package/dist/todo/types.js +12 -0
- package/dist/tools/index.d.ts +16 -2
- package/dist/tools/index.js +13 -0
- package/dist/ui/App.js +328 -14
- package/dist/ui/Markdown.d.ts +7 -1
- package/dist/ui/Markdown.js +15 -48
- package/dist/ui/StatusBar.d.ts +31 -0
- package/dist/ui/StatusBar.js +7 -2
- package/dist/ui/TodoPanel.d.ts +48 -0
- package/dist/ui/TodoPanel.js +74 -0
- package/dist/ui/TodoStrip.d.ts +36 -0
- package/dist/ui/TodoStrip.js +32 -0
- package/dist/ui/Transcript.d.ts +56 -4
- package/dist/ui/Transcript.js +123 -11
- package/dist/ui/entries/AssistantEntry.d.ts +18 -1
- package/dist/ui/entries/AssistantEntry.js +33 -3
- package/dist/ui/entries/TeamCard.d.ts +9 -1
- package/dist/ui/entries/TeamCard.js +22 -1
- package/dist/ui/entries/TodoCard.d.ts +39 -0
- package/dist/ui/entries/TodoCard.js +57 -0
- package/dist/ui/entries/ToolCard.d.ts +13 -1
- package/dist/ui/entries/ToolCard.js +39 -5
- package/dist/ui/entries/ToolPreview.d.ts +2 -2
- package/dist/ui/entries/UserEntry.d.ts +9 -1
- package/dist/ui/entries/UserEntry.js +17 -1
- package/dist/ui/exit-snapshot.d.ts +7 -0
- package/dist/ui/glyphs.d.ts +12 -0
- package/dist/ui/glyphs.js +8 -0
- package/dist/ui/layout/AppShell.d.ts +34 -1
- package/dist/ui/layout/AppShell.js +3 -3
- package/dist/ui/layout/MeasuredEntry.d.ts +21 -0
- package/dist/ui/layout/MeasuredEntry.js +30 -0
- package/dist/ui/layout/ScrollViewport.d.ts +13 -1
- package/dist/ui/layout/ScrollViewport.js +10 -4
- package/dist/ui/layout/rail.d.ts +56 -0
- package/dist/ui/layout/rail.js +68 -0
- package/dist/ui/layout/viewport-geometry.d.ts +28 -0
- package/dist/ui/layout/viewport-geometry.js +27 -0
- package/dist/ui/layout/virtual-window.d.ts +100 -0
- package/dist/ui/layout/virtual-window.js +204 -0
- package/dist/ui/live-clamp.d.ts +42 -0
- package/dist/ui/live-clamp.js +74 -0
- package/dist/ui/markdown-blocks.d.ts +25 -1
- package/dist/ui/markdown-blocks.js +43 -0
- package/dist/ui/overlays/HelpOverlay.js +4 -0
- package/dist/ui/render-cache.d.ts +81 -0
- package/dist/ui/render-cache.js +195 -0
- package/dist/ui/render-governor.d.ts +54 -0
- package/dist/ui/render-governor.js +78 -0
- package/dist/ui/transcript-text.d.ts +11 -0
- package/dist/ui/transcript-text.js +5 -0
- package/dist/ui/use-height-store.d.ts +31 -0
- package/dist/ui/use-height-store.js +76 -0
- package/dist/ui/use-render-governor.d.ts +39 -0
- package/dist/ui/use-render-governor.js +57 -0
- package/dist/ui/use-startup-notices.d.ts +9 -1
- package/dist/ui/use-startup-notices.js +35 -1
- package/package.json +3 -2
package/dist/config/schema.d.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { type ThinkingLevel } from '@aragon-agent/core';
|
|
9
9
|
import { type LogLevelName } from '../logging/levels.js';
|
|
10
|
+
import type { FollowThroughMode } from '../todo/types.js';
|
|
10
11
|
/**
|
|
11
12
|
* `ModelRegistry` seeds builtin lists for five providers, but `initProviders()`
|
|
12
13
|
* registers only three adapters. Running a model whose provider has no adapter
|
|
@@ -63,15 +64,40 @@ export declare const DEFAULT_IDLE_TIMEOUT_MS: number;
|
|
|
63
64
|
*/
|
|
64
65
|
export declare const PROMPT_HISTORY_CAP = 100;
|
|
65
66
|
/**
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
67
|
+
* The SCROLL HORIZON: entries older than this are not reachable inside the app
|
|
68
|
+
* and render as the "N earlier entries collapsed" line instead (invariant I-3).
|
|
69
|
+
*
|
|
70
|
+
* It used to be a rendering budget as well, because yoga laid out every child
|
|
71
|
+
* even when `overflow: hidden` clipped it. The viewport is virtualised now
|
|
72
|
+
* (tui-render-performance L3) — off-screen entries are not mounted at all — so
|
|
73
|
+
* the horizon costs O(entries) of arithmetic rather than O(characters) of layout
|
|
74
|
+
* and the default rises 300 -> 1000 with a 20000 ceiling. An existing persisted
|
|
75
|
+
* `300` is still valid and still clamps; no config migration is required.
|
|
70
76
|
*/
|
|
71
|
-
export declare const DEFAULT_TRANSCRIPT_WINDOW =
|
|
77
|
+
export declare const DEFAULT_TRANSCRIPT_WINDOW = 1000;
|
|
72
78
|
export declare const MIN_TRANSCRIPT_WINDOW = 50;
|
|
73
|
-
export declare const MAX_TRANSCRIPT_WINDOW =
|
|
79
|
+
export declare const MAX_TRANSCRIPT_WINDOW = 20000;
|
|
74
80
|
export declare function clampTranscriptWindow(v: unknown, fallback: number): number;
|
|
81
|
+
/**
|
|
82
|
+
* Entries kept in `ViewState` at all (tui-render-performance L1 / R5).
|
|
83
|
+
*
|
|
84
|
+
* A DIFFERENT BOUND FROM `transcriptWindow`, and the two must not be merged:
|
|
85
|
+
* the window is what the user can scroll to, this is what the process keeps in
|
|
86
|
+
* memory and what `/save` can export. Dropped entries are COUNTED and reported
|
|
87
|
+
* (`/perf`, and a leading line in the exported transcript) — never silent.
|
|
88
|
+
*/
|
|
89
|
+
export declare const DEFAULT_TRANSCRIPT_RETAIN = 1000;
|
|
90
|
+
export declare const MIN_TRANSCRIPT_RETAIN = 200;
|
|
91
|
+
export declare const MAX_TRANSCRIPT_RETAIN = 20000;
|
|
92
|
+
export declare function clampTranscriptRetain(v: unknown, fallback: number): number;
|
|
93
|
+
/** Adaptive coalescing under load. On by default; `--no-render-governor` kills it. */
|
|
94
|
+
export declare const DEFAULT_RENDER_GOVERNOR = true;
|
|
95
|
+
/**
|
|
96
|
+
* Ceiling of the governor ladder, in ms. `33` flattens the ladder to a single
|
|
97
|
+
* rung, which is the pre-feature behaviour without needing the kill switch.
|
|
98
|
+
*/
|
|
99
|
+
export declare const DEFAULT_MAX_RENDER_INTERVAL_MS = 320;
|
|
100
|
+
export declare function clampMaxRenderInterval(v: unknown, fallback: number): number;
|
|
75
101
|
/** `ask_user` calls allowed per user turn. */
|
|
76
102
|
export declare const DEFAULT_PLAN_MAX_ASK_ROUNDS = 4;
|
|
77
103
|
/**
|
|
@@ -239,6 +265,40 @@ export declare const DEFAULT_TEAM_CONFIG: TeamConfig;
|
|
|
239
265
|
* the two disagree would make the panel's `n/m` readout confusing for no gain.
|
|
240
266
|
*/
|
|
241
267
|
export declare function clampTeamConfig(raw: unknown): TeamConfig;
|
|
268
|
+
export interface TodoConfig {
|
|
269
|
+
/** Register `todo_write` at all. Default TRUE. */
|
|
270
|
+
enabled: boolean;
|
|
271
|
+
/**
|
|
272
|
+
* Render the right rail.
|
|
273
|
+
*
|
|
274
|
+
* INDEPENDENT OF `enabled` ON PURPOSE (D-16): a screen-reader user wants the
|
|
275
|
+
* planning discipline without the column, and the prompt block varies one
|
|
276
|
+
* sentence rather than disappearing (§3.7).
|
|
277
|
+
*/
|
|
278
|
+
panel: boolean;
|
|
279
|
+
/**
|
|
280
|
+
* What happens when a run ends with unfinished steps
|
|
281
|
+
* (todo-plan-followthrough §4.1). Default `'notify'`, which reproduces round
|
|
282
|
+
* 1's behaviour byte for byte.
|
|
283
|
+
*
|
|
284
|
+
* THE THIRD SCALAR, and it stays a scalar for the reason the section header
|
|
285
|
+
* above gives: `store.ts` merges this section BY HAND, and both of its merges
|
|
286
|
+
* spread the whole object (`{ ...DEFAULT_CONFIG.todo, ...(partial.todo ?? {}) }`
|
|
287
|
+
* and `{ ...current.todo, ...(patch.todo ?? {}) }`), so a third scalar key is
|
|
288
|
+
* carried automatically and needs no change there. A NESTED field would not
|
|
289
|
+
* be, and would mean replacing both merges first.
|
|
290
|
+
*/
|
|
291
|
+
followThrough: FollowThroughMode;
|
|
292
|
+
}
|
|
293
|
+
export declare const DEFAULT_TODO_CONFIG: TodoConfig;
|
|
294
|
+
/**
|
|
295
|
+
* THE single gate for every todo-config read AND write, mirroring
|
|
296
|
+
* `clampSkillsConfig` / `clampLogConfig` / `clampTeamConfig` for the reason
|
|
297
|
+
* those record: hardening only the read path leaves a bad value on disk that
|
|
298
|
+
* reverts to the default on every launch, which presents to the user as "my
|
|
299
|
+
* setting won't stick".
|
|
300
|
+
*/
|
|
301
|
+
export declare function clampTodoConfig(raw: unknown): TodoConfig;
|
|
242
302
|
export interface PersistedConfig {
|
|
243
303
|
version: number;
|
|
244
304
|
provider: string;
|
|
@@ -265,8 +325,14 @@ export interface PersistedConfig {
|
|
|
265
325
|
fullscreen: boolean;
|
|
266
326
|
/** Replay a plain-text session summary after leaving the alternate screen. */
|
|
267
327
|
exitTranscript: boolean;
|
|
268
|
-
/**
|
|
328
|
+
/** Entries reachable by scrolling; older ones show the "collapsed" line (I-3). */
|
|
269
329
|
transcriptWindow: number;
|
|
330
|
+
/** Entries kept in `ViewState`; older ones are dropped and counted (L1). */
|
|
331
|
+
transcriptRetain: number;
|
|
332
|
+
/** Adaptive coalescing under load (L4). */
|
|
333
|
+
renderGovernor: boolean;
|
|
334
|
+
/** Ceiling of the governor ladder in ms; `33` flattens it (L4). */
|
|
335
|
+
maxRenderIntervalMs: number;
|
|
270
336
|
confirmTools: boolean;
|
|
271
337
|
toolTimeoutMs: number;
|
|
272
338
|
idleTimeoutMs: number;
|
|
@@ -305,6 +371,8 @@ export interface PersistedConfig {
|
|
|
305
371
|
log: LogConfig;
|
|
306
372
|
/** Team mode. The FOURTH nested object; scalars only (see TeamConfig). */
|
|
307
373
|
team: TeamConfig;
|
|
374
|
+
/** Todo planning. The FIFTH nested object; scalars only (see TodoConfig). */
|
|
375
|
+
todo: TodoConfig;
|
|
308
376
|
}
|
|
309
377
|
export declare const DEFAULT_CONFIG: PersistedConfig;
|
|
310
378
|
/**
|
|
@@ -353,8 +421,30 @@ export interface CliConfig {
|
|
|
353
421
|
fullscreen?: boolean;
|
|
354
422
|
/** Replay a plain-text summary on exit (full-screen only — see §4.4). */
|
|
355
423
|
exitTranscript: boolean;
|
|
356
|
-
/**
|
|
424
|
+
/** Entries reachable by scrolling; older ones show the "collapsed" line (I-3). */
|
|
357
425
|
transcriptWindow: number;
|
|
426
|
+
/**
|
|
427
|
+
* Entries kept in `ViewState` (L1).
|
|
428
|
+
*
|
|
429
|
+
* Resolved to be `>= transcriptWindow`: retaining fewer entries than the
|
|
430
|
+
* window can scroll to would silently make part of the window unreachable.
|
|
431
|
+
*/
|
|
432
|
+
transcriptRetain: number;
|
|
433
|
+
/**
|
|
434
|
+
* What the user actually asked `transcriptRetain` to be, present ONLY when
|
|
435
|
+
* that value had to be raised (§5.1).
|
|
436
|
+
*
|
|
437
|
+
* A setting ignored without a word is the one failure mode this feature is
|
|
438
|
+
* not allowed to add: the eco chip, `/perf` and the dropped-entry line all
|
|
439
|
+
* exist so that nothing it does happens unannounced, and a config key
|
|
440
|
+
* overwritten in silence would be the exception. `undefined` means nothing
|
|
441
|
+
* was overridden, which is every default session.
|
|
442
|
+
*/
|
|
443
|
+
transcriptRetainRequested?: number;
|
|
444
|
+
/** Adaptive coalescing under load (L4). */
|
|
445
|
+
renderGovernor: boolean;
|
|
446
|
+
/** Ceiling of the governor ladder in ms; `33` flattens it (L4). */
|
|
447
|
+
maxRenderIntervalMs: number;
|
|
358
448
|
confirmTools: boolean;
|
|
359
449
|
toolTimeoutMs: number;
|
|
360
450
|
idleTimeoutMs: number;
|
|
@@ -399,6 +489,16 @@ export interface CliConfig {
|
|
|
399
489
|
* `/team` flips is a separate flag on the controller.
|
|
400
490
|
*/
|
|
401
491
|
team: TeamConfig;
|
|
492
|
+
/**
|
|
493
|
+
* Resolved todo-planning settings (todo-plan-execution §4.2).
|
|
494
|
+
*
|
|
495
|
+
* `enabled` here decides whether `todo_write` is REGISTERED, which happens
|
|
496
|
+
* once at construction and can never be undone (C-1). The live on/off switch
|
|
497
|
+
* `/todo` flips is a separate flag on the controller; `panel` is read at
|
|
498
|
+
* RENDER time through `controller.getConfig()`, which is why `/todo panel`
|
|
499
|
+
* must call `setTodoConfig` as well as persisting (P1-2).
|
|
500
|
+
*/
|
|
501
|
+
todo: TodoConfig;
|
|
402
502
|
/**
|
|
403
503
|
* Lifetime submit count driving the composer hint fade (`HINT_FADE_AFTER`).
|
|
404
504
|
*
|
package/dist/config/schema.js
CHANGED
|
@@ -98,18 +98,52 @@ export const DEFAULT_IDLE_TIMEOUT_MS = DEFAULT_TOOL_TIMEOUT_MS + IDLE_TIMEOUT_MA
|
|
|
98
98
|
*/
|
|
99
99
|
export const PROMPT_HISTORY_CAP = 100;
|
|
100
100
|
/**
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
101
|
+
* The SCROLL HORIZON: entries older than this are not reachable inside the app
|
|
102
|
+
* and render as the "N earlier entries collapsed" line instead (invariant I-3).
|
|
103
|
+
*
|
|
104
|
+
* It used to be a rendering budget as well, because yoga laid out every child
|
|
105
|
+
* even when `overflow: hidden` clipped it. The viewport is virtualised now
|
|
106
|
+
* (tui-render-performance L3) — off-screen entries are not mounted at all — so
|
|
107
|
+
* the horizon costs O(entries) of arithmetic rather than O(characters) of layout
|
|
108
|
+
* and the default rises 300 -> 1000 with a 20000 ceiling. An existing persisted
|
|
109
|
+
* `300` is still valid and still clamps; no config migration is required.
|
|
105
110
|
*/
|
|
106
|
-
export const DEFAULT_TRANSCRIPT_WINDOW =
|
|
111
|
+
export const DEFAULT_TRANSCRIPT_WINDOW = 1000;
|
|
107
112
|
export const MIN_TRANSCRIPT_WINDOW = 50;
|
|
108
|
-
export const MAX_TRANSCRIPT_WINDOW =
|
|
113
|
+
export const MAX_TRANSCRIPT_WINDOW = 20_000;
|
|
109
114
|
export function clampTranscriptWindow(v, fallback) {
|
|
110
115
|
const n = coercePositiveInt(v, fallback);
|
|
111
116
|
return Math.min(MAX_TRANSCRIPT_WINDOW, Math.max(MIN_TRANSCRIPT_WINDOW, n));
|
|
112
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* Entries kept in `ViewState` at all (tui-render-performance L1 / R5).
|
|
120
|
+
*
|
|
121
|
+
* A DIFFERENT BOUND FROM `transcriptWindow`, and the two must not be merged:
|
|
122
|
+
* the window is what the user can scroll to, this is what the process keeps in
|
|
123
|
+
* memory and what `/save` can export. Dropped entries are COUNTED and reported
|
|
124
|
+
* (`/perf`, and a leading line in the exported transcript) — never silent.
|
|
125
|
+
*/
|
|
126
|
+
export const DEFAULT_TRANSCRIPT_RETAIN = 1000;
|
|
127
|
+
export const MIN_TRANSCRIPT_RETAIN = 200;
|
|
128
|
+
export const MAX_TRANSCRIPT_RETAIN = 20_000;
|
|
129
|
+
export function clampTranscriptRetain(v, fallback) {
|
|
130
|
+
const n = coercePositiveInt(v, fallback);
|
|
131
|
+
return Math.min(MAX_TRANSCRIPT_RETAIN, Math.max(MIN_TRANSCRIPT_RETAIN, n));
|
|
132
|
+
}
|
|
133
|
+
// ---------------------------------------------------------------------------
|
|
134
|
+
// Adaptive render governor (tui-render-performance L4 / R7)
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
/** Adaptive coalescing under load. On by default; `--no-render-governor` kills it. */
|
|
137
|
+
export const DEFAULT_RENDER_GOVERNOR = true;
|
|
138
|
+
/**
|
|
139
|
+
* Ceiling of the governor ladder, in ms. `33` flattens the ladder to a single
|
|
140
|
+
* rung, which is the pre-feature behaviour without needing the kill switch.
|
|
141
|
+
*/
|
|
142
|
+
export const DEFAULT_MAX_RENDER_INTERVAL_MS = 320;
|
|
143
|
+
const RENDER_INTERVAL_RANGE = { min: 33, max: 1000 };
|
|
144
|
+
export function clampMaxRenderInterval(v, fallback) {
|
|
145
|
+
return clampInt(v, fallback, RENDER_INTERVAL_RANGE);
|
|
146
|
+
}
|
|
113
147
|
// ---------------------------------------------------------------------------
|
|
114
148
|
// Plan mode (plan-mode §4.4)
|
|
115
149
|
//
|
|
@@ -312,6 +346,40 @@ export function clampTeamConfig(raw) {
|
|
|
312
346
|
maxTurnsPerSubagent: clampInt(src.maxTurnsPerSubagent, DEFAULT_TEAM_CONFIG.maxTurnsPerSubagent, TURNS_RANGE),
|
|
313
347
|
};
|
|
314
348
|
}
|
|
349
|
+
export const DEFAULT_TODO_CONFIG = {
|
|
350
|
+
enabled: true,
|
|
351
|
+
panel: true,
|
|
352
|
+
followThrough: 'notify',
|
|
353
|
+
};
|
|
354
|
+
/**
|
|
355
|
+
* There is no enum helper in this file, so this follows the `isThinkingLevel`
|
|
356
|
+
* shape already here rather than inventing a second idiom.
|
|
357
|
+
*/
|
|
358
|
+
const FOLLOW_THROUGH_MODES = ['notify', 'auto', 'off'];
|
|
359
|
+
function followMode(value, fallback) {
|
|
360
|
+
return typeof value === 'string' && FOLLOW_THROUGH_MODES.includes(value)
|
|
361
|
+
? value
|
|
362
|
+
: fallback;
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* THE single gate for every todo-config read AND write, mirroring
|
|
366
|
+
* `clampSkillsConfig` / `clampLogConfig` / `clampTeamConfig` for the reason
|
|
367
|
+
* those record: hardening only the read path leaves a bad value on disk that
|
|
368
|
+
* reverts to the default on every launch, which presents to the user as "my
|
|
369
|
+
* setting won't stick".
|
|
370
|
+
*/
|
|
371
|
+
export function clampTodoConfig(raw) {
|
|
372
|
+
const src = (raw && typeof raw === 'object' ? raw : {});
|
|
373
|
+
return {
|
|
374
|
+
enabled: bool(src.enabled, DEFAULT_TODO_CONFIG.enabled),
|
|
375
|
+
panel: bool(src.panel, DEFAULT_TODO_CONFIG.panel),
|
|
376
|
+
// CLAMPED TO THE DEFAULT, NEVER REJECTED — the discipline every other key in
|
|
377
|
+
// this file follows, and the trade round 1 already made for `--thinking`.
|
|
378
|
+
// `ARAGON_TODO_FOLLOW=garbage` therefore resolves to `'notify'` and throws
|
|
379
|
+
// nothing (AC-5).
|
|
380
|
+
followThrough: followMode(src.followThrough, DEFAULT_TODO_CONFIG.followThrough),
|
|
381
|
+
};
|
|
382
|
+
}
|
|
315
383
|
export const DEFAULT_CONFIG = {
|
|
316
384
|
version: CONFIG_VERSION,
|
|
317
385
|
provider: 'anthropic',
|
|
@@ -326,6 +394,9 @@ export const DEFAULT_CONFIG = {
|
|
|
326
394
|
fullscreen: true,
|
|
327
395
|
exitTranscript: true,
|
|
328
396
|
transcriptWindow: DEFAULT_TRANSCRIPT_WINDOW,
|
|
397
|
+
transcriptRetain: DEFAULT_TRANSCRIPT_RETAIN,
|
|
398
|
+
renderGovernor: DEFAULT_RENDER_GOVERNOR,
|
|
399
|
+
maxRenderIntervalMs: DEFAULT_MAX_RENDER_INTERVAL_MS,
|
|
329
400
|
confirmTools: false,
|
|
330
401
|
toolTimeoutMs: DEFAULT_TOOL_TIMEOUT_MS,
|
|
331
402
|
idleTimeoutMs: DEFAULT_IDLE_TIMEOUT_MS,
|
|
@@ -340,6 +411,10 @@ export const DEFAULT_CONFIG = {
|
|
|
340
411
|
skills: DEFAULT_SKILLS_CONFIG,
|
|
341
412
|
log: DEFAULT_LOG_CONFIG,
|
|
342
413
|
team: DEFAULT_TEAM_CONFIG,
|
|
414
|
+
// The load path's first spread is what supplies the section to a user with no
|
|
415
|
+
// `todo` key at all, so this line is as required as the two merges in
|
|
416
|
+
// `store.ts` (P0-1).
|
|
417
|
+
todo: DEFAULT_TODO_CONFIG,
|
|
343
418
|
};
|
|
344
419
|
/**
|
|
345
420
|
* Runtime state that used to be stored in `config.json` by mistake
|
package/dist/config/store.js
CHANGED
|
@@ -14,7 +14,7 @@ import { dirname } from 'node:path';
|
|
|
14
14
|
import process from 'node:process';
|
|
15
15
|
import { getConfigPath, getSessionsDir } from './app-paths.js';
|
|
16
16
|
import { registerSecretsFrom } from '../logging/secret-registry.js';
|
|
17
|
-
import { CONFIG_VERSION, DEFAULT_CONFIG, clampLogConfig, clampMaxTokens, clampSkillsConfig, clampTeamConfig, isAutoToken, stripLegacyStateKeys, } from './schema.js';
|
|
17
|
+
import { CONFIG_VERSION, DEFAULT_CONFIG, clampLogConfig, clampMaxTokens, clampSkillsConfig, clampTeamConfig, clampTodoConfig, isAutoToken, stripLegacyStateKeys, } from './schema.js';
|
|
18
18
|
/**
|
|
19
19
|
* Normalize `maxTokens` for both directions of the config file.
|
|
20
20
|
*
|
|
@@ -122,6 +122,10 @@ export function loadPersistedConfig() {
|
|
|
122
122
|
log: clampLogConfig({ ...DEFAULT_CONFIG.log, ...(partial.log ?? {}) }),
|
|
123
123
|
// `team` is the fourth. Same one-level rule, same merge.
|
|
124
124
|
team: clampTeamConfig({ ...DEFAULT_CONFIG.team, ...(partial.team ?? {}) }),
|
|
125
|
+
// `todo` is the fifth. Same one-level rule, same merge — and this line is
|
|
126
|
+
// the READ half of the pair that keeps `/todo panel off` from silently
|
|
127
|
+
// unregistering the tool on the next launch (P0-1 / AC-35).
|
|
128
|
+
todo: clampTodoConfig({ ...DEFAULT_CONFIG.todo, ...(partial.todo ?? {}) }),
|
|
125
129
|
};
|
|
126
130
|
}
|
|
127
131
|
/**
|
|
@@ -168,6 +172,11 @@ export function updatePersistedConfig(patch) {
|
|
|
168
172
|
// 4` sends `{ team: { maxSubagents: 4 } }`, which a shallow spread would
|
|
169
173
|
// turn into "changing the fan-out width silently reverted every timeout".
|
|
170
174
|
team: clampTeamConfig({ ...current.team, ...(patch.team ?? {}) }),
|
|
175
|
+
// The fifth section, and its omission is the worst of the five (P0-1):
|
|
176
|
+
// `/todo panel off` sends `{ todo: { panel: false } }`, which a shallow
|
|
177
|
+
// spread turns into "turning the panel off silently unregisters the tool",
|
|
178
|
+
// permanently, from the next launch onward, with nothing anywhere saying so.
|
|
179
|
+
todo: clampTodoConfig({ ...current.todo, ...(patch.todo ?? {}) }),
|
|
171
180
|
};
|
|
172
181
|
writeConfigFile(merged);
|
|
173
182
|
return merged;
|
|
@@ -7,22 +7,40 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type { Message, ModelRef } from '@aragon-agent/core';
|
|
9
9
|
import type { Entry } from '../agent/reducer.js';
|
|
10
|
+
import type { TodoItem } from '../todo/types.js';
|
|
10
11
|
export interface SavedSession {
|
|
11
12
|
version: number;
|
|
12
13
|
savedAt: number;
|
|
13
14
|
model: ModelRef;
|
|
14
15
|
messages: Message[];
|
|
15
16
|
entries: Entry[];
|
|
17
|
+
/**
|
|
18
|
+
* The live todo list at save time (todo-plan-execution §3.13).
|
|
19
|
+
*
|
|
20
|
+
* `SESSION_VERSION` STAYS 1: the field is additive and optional, `loadSession`
|
|
21
|
+
* validates only that `messages` and `entries` are arrays, and an older file
|
|
22
|
+
* simply yields `undefined` — which `restoreTodos` treats as "clear", the
|
|
23
|
+
* right answer for a session that predates the feature (D-20 / D-22).
|
|
24
|
+
*/
|
|
25
|
+
todos?: TodoItem[];
|
|
16
26
|
}
|
|
17
27
|
/** Resolve a `/save` target: absolute/relative path, or a name in the data dir. */
|
|
18
28
|
export declare function resolveSessionPath(nameOrPath: string | undefined, cwd: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* `data` GAINS `todos`, AND WIDENING `SavedSession` ALONE IS NOT ENOUGH (P0-2):
|
|
31
|
+
* the writer below builds its payload from THIS parameter, not from the type, so
|
|
32
|
+
* a `todos?` field on the interface with no field here would ship a key nothing
|
|
33
|
+
* ever writes.
|
|
34
|
+
*/
|
|
19
35
|
export declare function saveSession(filePath: string, data: {
|
|
20
36
|
model: ModelRef;
|
|
21
37
|
messages: Message[];
|
|
22
38
|
entries: Entry[];
|
|
39
|
+
todos: TodoItem[];
|
|
23
40
|
}): void;
|
|
24
41
|
/**
|
|
25
|
-
* Settle any team entry that was saved mid-dispatch (team-subagents §5.3 / P1-5)
|
|
42
|
+
* Settle any team entry that was saved mid-dispatch (team-subagents §5.3 / P1-5),
|
|
43
|
+
* and any todo card that was saved mid-run (todo-plan-execution §3.8 / C-5).
|
|
26
44
|
*
|
|
27
45
|
* `saveSession` writes `entries` verbatim and `loadSession` validates only
|
|
28
46
|
* array-ness, so a session saved while a dispatch was in flight resumes with a
|
package/dist/session/persist.js
CHANGED
|
@@ -28,6 +28,12 @@ function defaultStamp() {
|
|
|
28
28
|
// Filesystem-safe timestamp without punctuation that breaks on Windows.
|
|
29
29
|
return new Date().toISOString().replace(/[:.]/g, '-');
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* `data` GAINS `todos`, AND WIDENING `SavedSession` ALONE IS NOT ENOUGH (P0-2):
|
|
33
|
+
* the writer below builds its payload from THIS parameter, not from the type, so
|
|
34
|
+
* a `todos?` field on the interface with no field here would ship a key nothing
|
|
35
|
+
* ever writes.
|
|
36
|
+
*/
|
|
31
37
|
export function saveSession(filePath, data) {
|
|
32
38
|
const dir = dirname(filePath);
|
|
33
39
|
if (!existsSync(dir))
|
|
@@ -38,11 +44,13 @@ export function saveSession(filePath, data) {
|
|
|
38
44
|
model: data.model,
|
|
39
45
|
messages: data.messages,
|
|
40
46
|
entries: data.entries,
|
|
47
|
+
todos: data.todos,
|
|
41
48
|
};
|
|
42
49
|
writeFileSync(filePath, `${JSON.stringify(payload, null, 2)}\n`, 'utf-8');
|
|
43
50
|
}
|
|
44
51
|
/**
|
|
45
|
-
* Settle any team entry that was saved mid-dispatch (team-subagents §5.3 / P1-5)
|
|
52
|
+
* Settle any team entry that was saved mid-dispatch (team-subagents §5.3 / P1-5),
|
|
53
|
+
* and any todo card that was saved mid-run (todo-plan-execution §3.8 / C-5).
|
|
46
54
|
*
|
|
47
55
|
* `saveSession` writes `entries` verbatim and `loadSession` validates only
|
|
48
56
|
* array-ness, so a session saved while a dispatch was in flight resumes with a
|
|
@@ -57,7 +65,18 @@ export function saveSession(filePath, data) {
|
|
|
57
65
|
* never legitimately sees a stale-active entry — only the file does.
|
|
58
66
|
*/
|
|
59
67
|
export function normalizeLoadedEntries(entries) {
|
|
60
|
-
return entries.map((entry) =>
|
|
68
|
+
return entries.map((entry) => {
|
|
69
|
+
if (entry.kind === 'team' && entry.active) {
|
|
70
|
+
return { ...entry, active: false, aborted: true };
|
|
71
|
+
}
|
|
72
|
+
// `interrupted: true` is the todo analogue of the team entry's `aborted`,
|
|
73
|
+
// and true for the same reason: the run behind the card died with the
|
|
74
|
+
// process (P2-6). Without it a resumed `2/7` reads as a run still in flight.
|
|
75
|
+
if (entry.kind === 'todo' && entry.live) {
|
|
76
|
+
return { ...entry, live: false, interrupted: true };
|
|
77
|
+
}
|
|
78
|
+
return entry;
|
|
79
|
+
});
|
|
61
80
|
}
|
|
62
81
|
export function loadSession(filePath) {
|
|
63
82
|
const raw = readFileSync(filePath, 'utf-8');
|
package/dist/team/subagent.js
CHANGED
|
@@ -89,6 +89,17 @@ export function buildSubagentTools(spec, deps, getAgent) {
|
|
|
89
89
|
// registering tools it could never use burns a turn — the same argument that
|
|
90
90
|
// keeps them out of headless mode.
|
|
91
91
|
planTools: [],
|
|
92
|
+
// NO `todo_write` FOR CHILDREN (todo-plan-execution §3.11 / D-14 / AC-34).
|
|
93
|
+
// Children run concurrently against ONE list under a full-replacement
|
|
94
|
+
// protocol: the last writer would win and the lead's plan would be destroyed
|
|
95
|
+
// by a subagent's private checklist. A list PER child would be a second
|
|
96
|
+
// panel and a second scroll model for work the lead already tracks as one
|
|
97
|
+
// item.
|
|
98
|
+
//
|
|
99
|
+
// EXPLICIT, WITH THIS COMMENT, even though the option's absence already
|
|
100
|
+
// yields an empty array: a silent default is not a decision anyone can find
|
|
101
|
+
// later.
|
|
102
|
+
todoTools: [],
|
|
92
103
|
teamTools: commTools,
|
|
93
104
|
// `spec.readOnly` forces the plan gate on for this child even in a BUILD
|
|
94
105
|
// session. TIGHTENING ONLY (D-13): there is no field anywhere that gives a
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* follow-through — what happens when a run ends with unfinished steps
|
|
3
|
+
* (todo-plan-followthrough §3.2 / §3.3).
|
|
4
|
+
*
|
|
5
|
+
* ASCII ONLY: `src/todo/**` is inside the glyph scanner's scope
|
|
6
|
+
* (`glyphs.test.ts`), and every string this module builds is user-visible.
|
|
7
|
+
*
|
|
8
|
+
* PURE. No I/O, no timers, no React, no `Date.now()` — the discipline
|
|
9
|
+
* `TodoStore`'s injected clock already establishes. The TUI (`App.tsx`) and
|
|
10
|
+
* headless mode (`agent/headless.ts`) both call `decideFollowThrough`, which is
|
|
11
|
+
* the only reason the two cannot drift; a second copy of this table in either
|
|
12
|
+
* caller is the defect this file exists to prevent.
|
|
13
|
+
*
|
|
14
|
+
* IT NEVER WRITES TO `TodoStore` (I-1 / C-3). Follow-through reads snapshots and
|
|
15
|
+
* produces a MESSAGE; the plan changes only when the model calls `todo_write`,
|
|
16
|
+
* which keeps the list a projection of what the model believes.
|
|
17
|
+
*/
|
|
18
|
+
import type { TodoSnapshot } from './types.js';
|
|
19
|
+
/**
|
|
20
|
+
* `notify` — say something true and stop (the default; round 1's behaviour).
|
|
21
|
+
* `auto` — continue the plan, under the economy below.
|
|
22
|
+
* `off` — say nothing at all.
|
|
23
|
+
*/
|
|
24
|
+
export type FollowThroughMode = 'notify' | 'auto' | 'off';
|
|
25
|
+
/** Carried across runs by the caller. Serializable, comparable, inert. */
|
|
26
|
+
export interface FollowThroughBudget {
|
|
27
|
+
/**
|
|
28
|
+
* Auto-continuations issued against this list LINEAGE.
|
|
29
|
+
*
|
|
30
|
+
* MONOTONIC UNTIL THE LIST IS GONE (P0-1 / D-16 / I-8). It is NOT reset by a
|
|
31
|
+
* re-plan: a model that emits a differently-sized list on every continuation
|
|
32
|
+
* would otherwise zero both counters every turn while `staleTurns` also never
|
|
33
|
+
* fires (it IS writing), which is an unbounded loop reachable by ordinary
|
|
34
|
+
* "re-plan as you learn" behaviour rather than by anything adversarial.
|
|
35
|
+
*
|
|
36
|
+
* A refactor that "simplifies" `advanceBudget` back to returning a zeroed
|
|
37
|
+
* budget on a `total` change re-opens that hole, and the symptom is a bill
|
|
38
|
+
* rather than a test failure. `advanceBudget`'s own test pins it.
|
|
39
|
+
*/
|
|
40
|
+
used: number;
|
|
41
|
+
/** Consecutive auto-continuations after which `doneCount` did not increase. */
|
|
42
|
+
noProgressStreak: number;
|
|
43
|
+
/** `TodoSnapshot.total` the budget was opened against. Identity, not a count. */
|
|
44
|
+
anchorTotal: number;
|
|
45
|
+
/**
|
|
46
|
+
* `doneCount` as observed at the PREVIOUS `agent_end` — which is what
|
|
47
|
+
* `advanceBudget` assigns, in every branch, and therefore what the next call
|
|
48
|
+
* compares against. An implementer who captures it at `runStart` instead
|
|
49
|
+
* builds a different state machine that happens to agree in the normal case.
|
|
50
|
+
*/
|
|
51
|
+
doneAtRunStart: number;
|
|
52
|
+
}
|
|
53
|
+
export interface FollowThroughInput {
|
|
54
|
+
mode: FollowThroughMode;
|
|
55
|
+
snapshot: TodoSnapshot | null;
|
|
56
|
+
/**
|
|
57
|
+
* Why the run ended. Derived by the caller, never carried by `AgentEndEvent`
|
|
58
|
+
* (C-2): core's event has `messages` and nothing else, and this round does not
|
|
59
|
+
* change core to make it otherwise.
|
|
60
|
+
*/
|
|
61
|
+
runEnd: {
|
|
62
|
+
aborted: boolean;
|
|
63
|
+
errored: boolean;
|
|
64
|
+
};
|
|
65
|
+
budget: FollowThroughBudget;
|
|
66
|
+
/**
|
|
67
|
+
* `false` in headless: no grace window (there is nobody to use it), and a
|
|
68
|
+
* different notice verb, because `-p` writes `[todo] ...` lines rather than
|
|
69
|
+
* transcript notices.
|
|
70
|
+
*/
|
|
71
|
+
interactive: boolean;
|
|
72
|
+
}
|
|
73
|
+
export type FollowThroughDecision = {
|
|
74
|
+
kind: 'none';
|
|
75
|
+
} | {
|
|
76
|
+
kind: 'notify';
|
|
77
|
+
level: 'info' | 'warn';
|
|
78
|
+
text: string;
|
|
79
|
+
} | {
|
|
80
|
+
kind: 'continue';
|
|
81
|
+
message: string;
|
|
82
|
+
graceMs: number;
|
|
83
|
+
notice: string;
|
|
84
|
+
};
|
|
85
|
+
/** The zero budget. A fresh App, a fresh headless run, and every cleared list. */
|
|
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
|
+
export declare function decideFollowThrough(input: FollowThroughInput): FollowThroughDecision;
|
|
92
|
+
/**
|
|
93
|
+
* Fold a finished run into the budget. Called BEFORE `decideFollowThrough`, on
|
|
94
|
+
* every `agent_end` (and on every resolved `prompt()` in headless).
|
|
95
|
+
*
|
|
96
|
+
* `used` is NOT incremented here — a decision that is never acted on costs
|
|
97
|
+
* nothing. Both fire points charge it themselves (I-5): the TUI's grace timer
|
|
98
|
+
* and the headless loop, and BOTH are required. An uncharged counter in `-p` is
|
|
99
|
+
* an uncapped loop in the one environment with no human to press Esc (P1-2).
|
|
100
|
+
*/
|
|
101
|
+
export declare function advanceBudget(prev: FollowThroughBudget, snapshot: TodoSnapshot | null, wasAutoContinuation: boolean): FollowThroughBudget;
|
|
102
|
+
/**
|
|
103
|
+
* The text an auto-continuation — or `/todo continue` — submits.
|
|
104
|
+
*
|
|
105
|
+
* IT ENUMERATES RATHER THAN REFERRING (D-11). Round 1 submitted the fixed string
|
|
106
|
+
* "Continue with the remaining todo items.", which is a reference to a
|
|
107
|
+
* `todo_write` call that in a long session may have scrolled far enough back
|
|
108
|
+
* that the model reconstructs it wrong — and reconstructing it wrong is SILENT,
|
|
109
|
+
* because whatever it writes next is a full replacement and becomes the new
|
|
110
|
+
* truth.
|
|
111
|
+
*
|
|
112
|
+
* `content` and never `activeForm`: the imperative is what an instruction wants.
|
|
113
|
+
* Numbers are the item's 1-based position in the WHOLE list, so "step 5" means
|
|
114
|
+
* the same thing here as it does in the rail. Completed items are omitted.
|
|
115
|
+
* `TODO_LIMITS.maxItems` bounds the whole message at roughly 1.8 kB.
|
|
116
|
+
*/
|
|
117
|
+
export declare function buildContinuationMessage(snapshot: TodoSnapshot): string;
|