@aragon-agent/cli 0.5.6 → 0.5.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 +356 -0
- package/README.md +1429 -1105
- package/dist/agent/coalesce.d.ts +13 -0
- package/dist/agent/coalesce.js +50 -11
- package/dist/agent/controller.d.ts +141 -2
- package/dist/agent/controller.js +396 -4
- package/dist/agent/entry-limits.d.ts +65 -0
- package/dist/agent/entry-limits.js +133 -0
- package/dist/agent/headless.d.ts +8 -0
- package/dist/agent/headless.js +40 -0
- package/dist/agent/reducer.d.ts +253 -3
- package/dist/agent/reducer.js +553 -28
- package/dist/agent/retry-view.d.ts +68 -0
- package/dist/agent/retry-view.js +95 -0
- package/dist/agent/system-prompt.d.ts +11 -0
- package/dist/agent/system-prompt.js +2 -0
- package/dist/cli.js +122 -4
- package/dist/commands/builtins.d.ts +1 -1
- package/dist/commands/builtins.js +313 -2
- package/dist/commands/perf.d.ts +45 -0
- package/dist/commands/perf.js +78 -0
- package/dist/config/cli-commands.d.ts +53 -2
- package/dist/config/cli-commands.js +136 -3
- package/dist/config/env.d.ts +6 -1
- package/dist/config/env.js +131 -1
- package/dist/config/load.d.ts +63 -0
- package/dist/config/load.js +125 -2
- package/dist/config/schema.d.ts +273 -10
- package/dist/config/schema.js +250 -7
- package/dist/config/store.js +20 -1
- package/dist/fast/critique.d.ts +43 -0
- package/dist/fast/critique.js +62 -0
- package/dist/fast/digest.d.ts +42 -0
- package/dist/fast/digest.js +85 -0
- package/dist/fast/frames.d.ts +99 -0
- package/dist/fast/frames.js +180 -0
- package/dist/fast/limits.d.ts +98 -0
- package/dist/fast/limits.js +98 -0
- package/dist/fast/prompt.d.ts +72 -0
- package/dist/fast/prompt.js +94 -0
- package/dist/fast/resolve.d.ts +43 -0
- package/dist/fast/resolve.js +105 -0
- package/dist/fast/review-call.d.ts +99 -0
- package/dist/fast/review-call.js +130 -0
- package/dist/fast/reviewer.d.ts +248 -0
- package/dist/fast/reviewer.js +664 -0
- package/dist/fast/types.d.ts +128 -0
- package/dist/fast/types.js +14 -0
- package/dist/fast/wiring.d.ts +170 -0
- package/dist/fast/wiring.js +316 -0
- package/dist/logging/install.d.ts +15 -1
- package/dist/logging/install.js +37 -4
- package/dist/logging/logger.d.ts +1 -1
- package/dist/session/persist.js +57 -1
- package/dist/team/normalize.d.ts +26 -1
- package/dist/team/normalize.js +23 -3
- package/dist/team/prompt.d.ts +11 -0
- package/dist/team/prompt.js +10 -0
- package/dist/team/report.d.ts +18 -0
- package/dist/team/report.js +27 -1
- package/dist/team/runtime.d.ts +13 -2
- package/dist/team/runtime.js +33 -6
- package/dist/team/subagent.d.ts +19 -1
- package/dist/team/subagent.js +72 -10
- package/dist/team/task-tool.d.ts +26 -0
- package/dist/team/task-tool.js +48 -4
- package/dist/team/types.d.ts +80 -1
- package/dist/tools/bash-tool.js +19 -3
- package/dist/tools/diff.d.ts +25 -5
- package/dist/tools/diff.js +72 -41
- package/dist/tools/file-change-store.d.ts +34 -0
- package/dist/tools/file-change-store.js +57 -0
- package/dist/tools/fs-tools.d.ts +26 -0
- package/dist/tools/fs-tools.js +62 -5
- package/dist/tools/index.d.ts +31 -0
- package/dist/tools/index.js +7 -1
- package/dist/tools/patch.d.ts +115 -0
- package/dist/tools/patch.js +453 -0
- package/dist/tools/terminal-output.d.ts +107 -0
- package/dist/tools/terminal-output.js +198 -0
- package/dist/tools/tool-output-store.d.ts +68 -0
- package/dist/tools/tool-output-store.js +102 -0
- package/dist/ui/ActivityLine.d.ts +48 -0
- package/dist/ui/ActivityLine.js +22 -0
- package/dist/ui/App.d.ts +1 -4
- package/dist/ui/App.js +357 -44
- package/dist/ui/BottomStatusRow.d.ts +43 -0
- package/dist/ui/BottomStatusRow.js +14 -0
- package/dist/ui/Composer.d.ts +1 -6
- package/dist/ui/Composer.js +2 -2
- package/dist/ui/Markdown.d.ts +7 -1
- package/dist/ui/Markdown.js +15 -48
- package/dist/ui/PromptInput.d.ts +1 -15
- package/dist/ui/PromptInput.js +1 -38
- package/dist/ui/StatusBar.d.ts +50 -0
- package/dist/ui/StatusBar.js +7 -3
- package/dist/ui/TeamPanel.d.ts +5 -4
- package/dist/ui/TeamPanel.js +1 -1
- package/dist/ui/TodoPanel.d.ts +5 -4
- package/dist/ui/Transcript.d.ts +80 -5
- package/dist/ui/Transcript.js +179 -11
- package/dist/ui/activity-phrases.d.ts +33 -0
- package/dist/ui/activity-phrases.js +50 -0
- package/dist/ui/entries/AssistantEntry.d.ts +38 -1
- package/dist/ui/entries/AssistantEntry.js +34 -3
- package/dist/ui/entries/DiffView.d.ts +67 -0
- package/dist/ui/entries/DiffView.js +103 -0
- package/dist/ui/entries/FastCard.d.ts +53 -0
- package/dist/ui/entries/FastCard.js +87 -0
- package/dist/ui/entries/RetryCard.d.ts +56 -0
- package/dist/ui/entries/RetryCard.js +60 -0
- package/dist/ui/entries/TeamCard.d.ts +9 -1
- package/dist/ui/entries/TeamCard.js +28 -2
- package/dist/ui/entries/TodoCard.d.ts +9 -1
- package/dist/ui/entries/TodoCard.js +23 -1
- package/dist/ui/entries/ToolCard.d.ts +50 -1
- package/dist/ui/entries/ToolCard.js +104 -7
- 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 +9 -0
- package/dist/ui/glyphs.js +2 -0
- package/dist/ui/layout/AppShell.d.ts +13 -22
- package/dist/ui/layout/AppShell.js +3 -38
- package/dist/ui/layout/MeasuredEntry.d.ts +21 -0
- package/dist/ui/layout/MeasuredEntry.js +30 -0
- package/dist/ui/layout/ScrollViewport.js +8 -2
- 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 +308 -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 +16 -2
- package/dist/ui/overlays/SettingsScreen.d.ts +82 -1
- package/dist/ui/overlays/SettingsScreen.js +147 -1
- 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 +32 -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/dist/ui/use-wheel-routing.d.ts +16 -32
- package/dist/ui/use-wheel-routing.js +21 -38
- package/package.json +2 -2
- package/dist/ui/layout/regions.d.ts +0 -55
- package/dist/ui/layout/regions.js +0 -52
package/dist/config/schema.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* See spec §3.5 (resolution pipeline), §6.1 (config file), and §3.8 (timeout
|
|
6
6
|
* invariant).
|
|
7
7
|
*/
|
|
8
|
-
import { type ThinkingLevel } from '@aragon-agent/core';
|
|
8
|
+
import { type RetryPolicy, type ThinkingLevel } from '@aragon-agent/core';
|
|
9
9
|
import { type LogLevelName } from '../logging/levels.js';
|
|
10
10
|
import type { FollowThroughMode } from '../todo/types.js';
|
|
11
11
|
/**
|
|
@@ -64,15 +64,40 @@ export declare const DEFAULT_IDLE_TIMEOUT_MS: number;
|
|
|
64
64
|
*/
|
|
65
65
|
export declare const PROMPT_HISTORY_CAP = 100;
|
|
66
66
|
/**
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
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.
|
|
71
76
|
*/
|
|
72
|
-
export declare const DEFAULT_TRANSCRIPT_WINDOW =
|
|
77
|
+
export declare const DEFAULT_TRANSCRIPT_WINDOW = 1000;
|
|
73
78
|
export declare const MIN_TRANSCRIPT_WINDOW = 50;
|
|
74
|
-
export declare const MAX_TRANSCRIPT_WINDOW =
|
|
79
|
+
export declare const MAX_TRANSCRIPT_WINDOW = 20000;
|
|
75
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;
|
|
76
101
|
/** `ask_user` calls allowed per user turn. */
|
|
77
102
|
export declare const DEFAULT_PLAN_MAX_ASK_ROUNDS = 4;
|
|
78
103
|
/**
|
|
@@ -274,12 +299,171 @@ export declare const DEFAULT_TODO_CONFIG: TodoConfig;
|
|
|
274
299
|
* setting won't stick".
|
|
275
300
|
*/
|
|
276
301
|
export declare function clampTodoConfig(raw: unknown): TodoConfig;
|
|
302
|
+
export interface RetryConfig {
|
|
303
|
+
/**
|
|
304
|
+
* The user's switch. KEPT SEPARATE FROM `maxRetries: 0` on purpose: `enabled`
|
|
305
|
+
* survives round-tripping through the settings screen, while `maxRetries: 0` is
|
|
306
|
+
* what `toRetryPolicy` produces from it. Folding them would make
|
|
307
|
+
* `/retry off; /retry on` forget the user's chosen count.
|
|
308
|
+
*/
|
|
309
|
+
enabled: boolean;
|
|
310
|
+
/** Retries AFTER the initial attempt. `0` is the kill switch, not a typo. */
|
|
311
|
+
maxRetries: number;
|
|
312
|
+
initialDelayMs: number;
|
|
313
|
+
maxDelayMs: number;
|
|
314
|
+
/** Growth factor. THE ONLY FLOAT KEY in this section. */
|
|
315
|
+
multiplier: number;
|
|
316
|
+
jitter: boolean;
|
|
317
|
+
respectRetryAfter: boolean;
|
|
318
|
+
/** Wall-clock budget from the first failure of one request. */
|
|
319
|
+
maxElapsedMs: number;
|
|
320
|
+
/** Allow a restart after content has already streamed to the transcript. */
|
|
321
|
+
onPartialStream: boolean;
|
|
322
|
+
}
|
|
323
|
+
export declare const DEFAULT_RETRY_CONFIG: RetryConfig;
|
|
324
|
+
/**
|
|
325
|
+
* `hardMaxRetries` re-exported rather than re-spelled, for the reason
|
|
326
|
+
* `HARD_MAX_SUBAGENTS` records: the ceiling is enforced in TWO places — here on
|
|
327
|
+
* the config value, and again by `normalizePolicy` inside the engine — and two
|
|
328
|
+
* copies of the same 20 is exactly how a hand-edited `config.json` ends up able
|
|
329
|
+
* to raise a ceiling the mechanism fixed.
|
|
330
|
+
*/
|
|
331
|
+
export declare const HARD_MAX_RETRIES: 20;
|
|
332
|
+
/**
|
|
333
|
+
* THE single gate for every retry-config read AND write, mirroring
|
|
334
|
+
* `clampSkillsConfig` / `clampLogConfig` / `clampTeamConfig` / `clampTodoConfig`
|
|
335
|
+
* for the reason those record: hardening only the read path leaves a bad value on
|
|
336
|
+
* disk that reverts to the default on every launch, which presents to the user as
|
|
337
|
+
* "my setting won't stick".
|
|
338
|
+
*
|
|
339
|
+
* Three invariants live inside the gate:
|
|
340
|
+
*
|
|
341
|
+
* - `maxDelayMs = max(maxDelayMs, initialDelayMs)` — a ceiling under the floor
|
|
342
|
+
* makes the ladder a flat line and the countdown a lie.
|
|
343
|
+
* - `multiplier` goes through `clampNumber`, not `clampInt`: `2.5` is a
|
|
344
|
+
* legitimate factor and `coercePositiveInt` would floor it to 2.
|
|
345
|
+
* - `maxRetries` goes through `clampIntAllowingZero`. THIS IS THE ONE THAT SHIPS
|
|
346
|
+
* BROKEN WITHOUT IT (R-16): `clampInt` returns the fallback for `n <= 0`, so
|
|
347
|
+
* `clampRetryConfig({ maxRetries: 0 })` would yield 10 — the maximum instead
|
|
348
|
+
* of the minimum, silently, for the key that gates the whole feature.
|
|
349
|
+
*
|
|
350
|
+
* `maxElapsedMs` intentionally keeps plain `clampInt`: its floor is 10 000, so it
|
|
351
|
+
* has no `0` to express.
|
|
352
|
+
*/
|
|
353
|
+
export declare function clampRetryConfig(raw: unknown): RetryConfig;
|
|
354
|
+
/**
|
|
355
|
+
* `RetryConfig` (user intent) -> `RetryPolicy` (engine policy), or `null` for
|
|
356
|
+
* "the user said no".
|
|
357
|
+
*
|
|
358
|
+
* `null` rather than a policy with `maxRetries: 0` because `ProviderRegistry`
|
|
359
|
+
* short-circuits on a null policy and returns the adapter's iterator DIRECTLY —
|
|
360
|
+
* an opted-out session is then allocation-identical to a pre-feature build.
|
|
361
|
+
*
|
|
362
|
+
* TOTAL, AND IT ACCEPTS A MISSING SECTION. `loadConfig` always supplies one, but
|
|
363
|
+
* a `CliConfig` built by hand — every controller test in this package does exactly
|
|
364
|
+
* that, and so would an embedder — legitimately omits it, and a `TypeError` from a
|
|
365
|
+
* pure translation function is the wrong way to find out. Routing through
|
|
366
|
+
* `clampRetryConfig` also means an out-of-range hand-built value can never reach
|
|
367
|
+
* the engine, which is the same "one gate, both directions" rule every other
|
|
368
|
+
* section in this file follows.
|
|
369
|
+
*/
|
|
370
|
+
export declare function toRetryPolicy(cfg: RetryConfig | undefined): RetryPolicy | null;
|
|
371
|
+
export interface FastConfig {
|
|
372
|
+
/**
|
|
373
|
+
* Resolve the tier at all. Defaults to FALSE (R-d).
|
|
374
|
+
*
|
|
375
|
+
* OFF IS BYTE-IDENTICAL (I-2): with this false the system prompt, `task`'s
|
|
376
|
+
* schema, the tool array, the transcript, the status bar and the request
|
|
377
|
+
* payloads are exactly what a pre-feature build produces.
|
|
378
|
+
*/
|
|
379
|
+
enabled: boolean;
|
|
380
|
+
/** `''` INHERITS the main provider — what makes `fast.model` alone a complete
|
|
381
|
+
* configuration for the common "same vendor, cheaper model" case. */
|
|
382
|
+
provider: string;
|
|
383
|
+
/** The fast model id. Empty means NOT CONFIGURED, never "same as main": that
|
|
384
|
+
* distinction is what keeps R-e (opt in to fast == main) from being
|
|
385
|
+
* indistinguishable from a typo (D-4). */
|
|
386
|
+
model: string;
|
|
387
|
+
/** `''` inherits only when the provider matches — a base URL belongs to one
|
|
388
|
+
* API, so a fast tier on a different vendor must not borrow it. */
|
|
389
|
+
baseUrl: string;
|
|
390
|
+
/** Applied to fast-tier children and to the review call. Defaults to `'off'`:
|
|
391
|
+
* a "fast" model asked to think for 32 768 tokens is not fast (D-13). */
|
|
392
|
+
thinkingLevel: ThinkingLevel;
|
|
393
|
+
/** Allow `model: "fast"` on `task`. */
|
|
394
|
+
delegate: boolean;
|
|
395
|
+
/** Run the periodic asynchronous review. */
|
|
396
|
+
review: boolean;
|
|
397
|
+
/** Completed turns between reviews. */
|
|
398
|
+
reviewEveryTurns: number;
|
|
399
|
+
/** Turn frames included in one digest. */
|
|
400
|
+
reviewContextTurns: number;
|
|
401
|
+
/** Ceiling on an injected critique, in characters. */
|
|
402
|
+
reviewMaxChars: number;
|
|
403
|
+
/**
|
|
404
|
+
* Reviews STARTED per session, across all runs (fast-model-tier-hardening
|
|
405
|
+
* §4.1 / W1).
|
|
406
|
+
*
|
|
407
|
+
* The one bound on spend the user never asked for. `reviewEveryTurns`
|
|
408
|
+
* controls the PACE; this controls the TOTAL — and the two are different
|
|
409
|
+
* questions, because a session is not one run. `reviewsThisRun` is reset by
|
|
410
|
+
* every user message, so thirty messages buys thirty runs' worth of reviews
|
|
411
|
+
* with nothing counting across them.
|
|
412
|
+
*
|
|
413
|
+
* DENOMINATED IN REVIEWS, NOT CURRENCY (D-H1). The fast tier is by
|
|
414
|
+
* construction the likeliest place in the product to name a model the static
|
|
415
|
+
* cost table has never seen, and `buildRuntimeModel` prices exactly those at
|
|
416
|
+
* zero (C-11) — so a dollar budget would silently never trigger for the
|
|
417
|
+
* configuration that most needs it. A count is exact for every model, and
|
|
418
|
+
* because per-review cost is already bounded on both sides (a 4 000-char
|
|
419
|
+
* digest in, 512 tokens out), `count x bound` is an honest ceiling.
|
|
420
|
+
*/
|
|
421
|
+
reviewMaxPerSession: number;
|
|
422
|
+
}
|
|
423
|
+
export declare const DEFAULT_FAST_CONFIG: FastConfig;
|
|
424
|
+
/**
|
|
425
|
+
* THE single gate for every fast-config read AND write, mirroring
|
|
426
|
+
* `clampSkillsConfig` / `clampLogConfig` / `clampTeamConfig` / `clampTodoConfig`
|
|
427
|
+
* for the reason those four record: hardening only the read path leaves a bad
|
|
428
|
+
* value on disk that reverts to the default on every launch, which presents to
|
|
429
|
+
* the user as "my setting won't stick".
|
|
430
|
+
*
|
|
431
|
+
* `provider` is CLAMPED TO `''` rather than rejected when it names something
|
|
432
|
+
* with no adapter. `''` means "inherit the main provider", which is the safe
|
|
433
|
+
* reading of a typo — and `resolveFastTier` reports `no_adapter` for the case a
|
|
434
|
+
* user really did configure a dead vendor as the MAIN one.
|
|
435
|
+
*/
|
|
436
|
+
export declare function clampFastConfig(raw: unknown): FastConfig;
|
|
277
437
|
export interface PersistedConfig {
|
|
278
438
|
version: number;
|
|
279
439
|
provider: string;
|
|
280
440
|
model: string;
|
|
281
441
|
baseUrl: string | null;
|
|
282
442
|
thinkingLevel: ThinkingLevel;
|
|
443
|
+
/**
|
|
444
|
+
* Whether the terminal DRAWS the reasoning that came back
|
|
445
|
+
* (agent-activity-presentation §4.1).
|
|
446
|
+
*
|
|
447
|
+
* ADJACENT TO `thinkingLevel` AND DELIBERATELY NAMED DIFFERENTLY: that key is
|
|
448
|
+
* the effort the provider is asked to SPEND, this one is whether the transcript
|
|
449
|
+
* shows it. They are one word apart in the config file and must never be
|
|
450
|
+
* confused, which is why the flag is `--show-thinking` rather than the shorter
|
|
451
|
+
* `--thinking` the existing key already resembles.
|
|
452
|
+
*/
|
|
453
|
+
showThinking: boolean;
|
|
454
|
+
/**
|
|
455
|
+
* Whether a RUNNING tool card draws a bounded tail of its live output
|
|
456
|
+
* (agent-activity-presentation-live §4.1).
|
|
457
|
+
*
|
|
458
|
+
* DEFAULT `true`, unlike `showThinking` next door (D-28). That key hides
|
|
459
|
+
* information by default because reasoning is noise most of the time; this one
|
|
460
|
+
* ADDS the information the requirement asks for, and its cost is bounded by
|
|
461
|
+
* construction -- eight rows per call, sixteen calls, whatever the command
|
|
462
|
+
* emits. It exists as a KILL SWITCH, not as an opt-in: "a build's output
|
|
463
|
+
* redraws the transcript" is exactly the kind of change a user on a slow SSH
|
|
464
|
+
* link may want off.
|
|
465
|
+
*/
|
|
466
|
+
liveToolOutput: boolean;
|
|
283
467
|
/**
|
|
284
468
|
* Output token cap.
|
|
285
469
|
*
|
|
@@ -300,8 +484,14 @@ export interface PersistedConfig {
|
|
|
300
484
|
fullscreen: boolean;
|
|
301
485
|
/** Replay a plain-text session summary after leaving the alternate screen. */
|
|
302
486
|
exitTranscript: boolean;
|
|
303
|
-
/**
|
|
487
|
+
/** Entries reachable by scrolling; older ones show the "collapsed" line (I-3). */
|
|
304
488
|
transcriptWindow: number;
|
|
489
|
+
/** Entries kept in `ViewState`; older ones are dropped and counted (L1). */
|
|
490
|
+
transcriptRetain: number;
|
|
491
|
+
/** Adaptive coalescing under load (L4). */
|
|
492
|
+
renderGovernor: boolean;
|
|
493
|
+
/** Ceiling of the governor ladder in ms; `33` flattens it (L4). */
|
|
494
|
+
maxRenderIntervalMs: number;
|
|
305
495
|
confirmTools: boolean;
|
|
306
496
|
toolTimeoutMs: number;
|
|
307
497
|
idleTimeoutMs: number;
|
|
@@ -322,7 +512,8 @@ export interface PersistedConfig {
|
|
|
322
512
|
hints: boolean;
|
|
323
513
|
/**
|
|
324
514
|
* Enable SGR mouse reporting in full-screen mode so the wheel scrolls the
|
|
325
|
-
*
|
|
515
|
+
* transcript, whatever row the pointer is on (wheel-scrolls-transcript-only
|
|
516
|
+
* §4.1).
|
|
326
517
|
*
|
|
327
518
|
* A PLAIN BOOLEAN, not a `fullscreen`-style tri-state: there are no
|
|
328
519
|
* heuristics for an explicit `true` to override (D-10).
|
|
@@ -342,6 +533,10 @@ export interface PersistedConfig {
|
|
|
342
533
|
team: TeamConfig;
|
|
343
534
|
/** Todo planning. The FIFTH nested object; scalars only (see TodoConfig). */
|
|
344
535
|
todo: TodoConfig;
|
|
536
|
+
/** API retry. The SIXTH nested object; scalars only (see RetryConfig). */
|
|
537
|
+
retry: RetryConfig;
|
|
538
|
+
/** Fast model tier. The SEVENTH nested object; scalars only (see FastConfig). */
|
|
539
|
+
fast: FastConfig;
|
|
345
540
|
}
|
|
346
541
|
export declare const DEFAULT_CONFIG: PersistedConfig;
|
|
347
542
|
/**
|
|
@@ -373,6 +568,28 @@ export interface CliConfig {
|
|
|
373
568
|
model: string;
|
|
374
569
|
baseUrl?: string;
|
|
375
570
|
thinkingLevel: ThinkingLevel;
|
|
571
|
+
/**
|
|
572
|
+
* Seeds `ViewState.thinkingVisible` on the first frame (§4.1 / P1-2).
|
|
573
|
+
*
|
|
574
|
+
* IT MUST EXIST HERE AND NOT ONLY IN `PersistedConfig`. `App` reads `cfg.*`,
|
|
575
|
+
* where `cfg` is a `CliConfig`; a key added to the persisted shape alone would
|
|
576
|
+
* round-trip through `config set`, appear in `config list` — and never reach
|
|
577
|
+
* `initialViewState`. `--show-thinking` and `ARAGON_SHOW_THINKING` would parse
|
|
578
|
+
* and be discarded. Nothing would fail; the setting would simply do nothing.
|
|
579
|
+
*/
|
|
580
|
+
showThinking: boolean;
|
|
581
|
+
/**
|
|
582
|
+
* Whether `AgentController` allocates the live-output store and binds
|
|
583
|
+
* `recordOutput` (agent-activity-presentation-live §4.1).
|
|
584
|
+
*
|
|
585
|
+
* IT MUST EXIST HERE AND NOT ONLY IN `PersistedConfig`, for the reason its
|
|
586
|
+
* neighbour records one comment up: the controller reads `config.*`, where
|
|
587
|
+
* `config` is a `CliConfig`, so a key added to the persisted shape alone would
|
|
588
|
+
* round-trip through `config set`, appear in `config list` -- and never reach
|
|
589
|
+
* the constructor. `--live-tool-output` and `ARAGON_LIVE_TOOL_OUTPUT` would
|
|
590
|
+
* parse and be discarded, with nothing failing.
|
|
591
|
+
*/
|
|
592
|
+
liveToolOutput: boolean;
|
|
376
593
|
/** The resolved cap, or `undefined` for AUTO (per-model ceiling, ≤ 64000). */
|
|
377
594
|
maxTokens?: number;
|
|
378
595
|
theme: ThemeName;
|
|
@@ -390,8 +607,30 @@ export interface CliConfig {
|
|
|
390
607
|
fullscreen?: boolean;
|
|
391
608
|
/** Replay a plain-text summary on exit (full-screen only — see §4.4). */
|
|
392
609
|
exitTranscript: boolean;
|
|
393
|
-
/**
|
|
610
|
+
/** Entries reachable by scrolling; older ones show the "collapsed" line (I-3). */
|
|
394
611
|
transcriptWindow: number;
|
|
612
|
+
/**
|
|
613
|
+
* Entries kept in `ViewState` (L1).
|
|
614
|
+
*
|
|
615
|
+
* Resolved to be `>= transcriptWindow`: retaining fewer entries than the
|
|
616
|
+
* window can scroll to would silently make part of the window unreachable.
|
|
617
|
+
*/
|
|
618
|
+
transcriptRetain: number;
|
|
619
|
+
/**
|
|
620
|
+
* What the user actually asked `transcriptRetain` to be, present ONLY when
|
|
621
|
+
* that value had to be raised (§5.1).
|
|
622
|
+
*
|
|
623
|
+
* A setting ignored without a word is the one failure mode this feature is
|
|
624
|
+
* not allowed to add: the eco chip, `/perf` and the dropped-entry line all
|
|
625
|
+
* exist so that nothing it does happens unannounced, and a config key
|
|
626
|
+
* overwritten in silence would be the exception. `undefined` means nothing
|
|
627
|
+
* was overridden, which is every default session.
|
|
628
|
+
*/
|
|
629
|
+
transcriptRetainRequested?: number;
|
|
630
|
+
/** Adaptive coalescing under load (L4). */
|
|
631
|
+
renderGovernor: boolean;
|
|
632
|
+
/** Ceiling of the governor ladder in ms; `33` flattens it (L4). */
|
|
633
|
+
maxRenderIntervalMs: number;
|
|
395
634
|
confirmTools: boolean;
|
|
396
635
|
toolTimeoutMs: number;
|
|
397
636
|
idleTimeoutMs: number;
|
|
@@ -446,6 +685,30 @@ export interface CliConfig {
|
|
|
446
685
|
* must call `setTodoConfig` as well as persisting (P1-2).
|
|
447
686
|
*/
|
|
448
687
|
todo: TodoConfig;
|
|
688
|
+
/**
|
|
689
|
+
* Resolved API-retry settings (llm-api-retry-backoff §6.2).
|
|
690
|
+
*
|
|
691
|
+
* Unlike `team.enabled` / `todo.enabled` this decides NOTHING at construction
|
|
692
|
+
* time: the policy lives on the `ProviderRegistry` and `setRetryConfig()`
|
|
693
|
+
* replaces it live, so `/retry off` takes effect in the running session. That is
|
|
694
|
+
* why the controller has no `retryRegistered` twin of the two flags above.
|
|
695
|
+
*/
|
|
696
|
+
retry: RetryConfig;
|
|
697
|
+
/**
|
|
698
|
+
* Resolved fast-tier settings (fast-model-tier §4.2).
|
|
699
|
+
*
|
|
700
|
+
* `enabled` here decides `fastRegistered` — whether `task`'s schema carries a
|
|
701
|
+
* `model` property at all — which happens once at construction and can never
|
|
702
|
+
* be undone (C-2). The live on/off switch `/fast` flips is a separate flag on
|
|
703
|
+
* `FastWiring`.
|
|
704
|
+
*
|
|
705
|
+
* THE OTHER NINE KEYS ARE READ LIVE, and that is not the same statement.
|
|
706
|
+
* `provider` / `model` / `baseUrl` feed `resolveFastTier`, which is re-run on
|
|
707
|
+
* every config mutation (§3.2 rule 8 / RV-3) because `''` INHERITS from
|
|
708
|
+
* `config.provider` / `config.baseUrl` and those are rewritten by `/model`,
|
|
709
|
+
* `/provider`, `/reload` and the settings screen.
|
|
710
|
+
*/
|
|
711
|
+
fast: FastConfig;
|
|
449
712
|
/**
|
|
450
713
|
* Lifetime submit count driving the composer hint fade (`HINT_FADE_AFTER`).
|
|
451
714
|
*
|
package/dist/config/schema.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* invariant).
|
|
7
7
|
*/
|
|
8
8
|
import { isAbsolute } from 'node:path';
|
|
9
|
-
import { ABSOLUTE_MAX_OUTPUT_TOKENS, DEFAULT_MAX_OUTPUT_TOKENS, MIN_MAX_OUTPUT_TOKENS, } from '@aragon-agent/core';
|
|
9
|
+
import { ABSOLUTE_MAX_OUTPUT_TOKENS, DEFAULT_MAX_OUTPUT_TOKENS, DEFAULT_RETRY_POLICY, MIN_MAX_OUTPUT_TOKENS, RETRY_LIMITS, } from '@aragon-agent/core';
|
|
10
10
|
import { clampLogLevel } from '../logging/levels.js';
|
|
11
11
|
import { TEAM_LIMITS } from '../team/limits.js';
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
@@ -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
|
//
|
|
@@ -179,6 +213,46 @@ function clampInt(v, fallback, range) {
|
|
|
179
213
|
const n = coercePositiveInt(v, fallback);
|
|
180
214
|
return Math.min(range.max, Math.max(range.min, n));
|
|
181
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* `clampInt` for a key whose range legitimately includes ZERO.
|
|
218
|
+
*
|
|
219
|
+
* `clampInt` delegates to `coercePositiveInt`, which returns the FALLBACK for
|
|
220
|
+
* `n <= 0` — so a range whose floor is `0` cannot express `0` through it, and the
|
|
221
|
+
* value silently becomes the default. That trap is invisible at the call site and
|
|
222
|
+
* is what `retry.maxRetries` needs this helper for: `0` is that key's documented
|
|
223
|
+
* floor and its kill switch, and four documented controls send it
|
|
224
|
+
* (`/retry max 0`, `--retry-max 0`, `ARAGON_RETRY_MAX=0`,
|
|
225
|
+
* `aragon config set retry.maxRetries 0`). Through `clampInt` all four would set
|
|
226
|
+
* the MAXIMUM instead of the minimum, with no error anywhere.
|
|
227
|
+
*
|
|
228
|
+
* Own coercion: `Number.parseInt`, reject only non-finite and negative, then
|
|
229
|
+
* floor and clamp.
|
|
230
|
+
*/
|
|
231
|
+
function clampIntAllowingZero(v, fallback, range) {
|
|
232
|
+
if (v === null || v === undefined || v === '')
|
|
233
|
+
return clampToRange(fallback, range);
|
|
234
|
+
const n = typeof v === 'number' ? v : Number.parseInt(String(v), 10);
|
|
235
|
+
if (!Number.isFinite(n) || n < 0)
|
|
236
|
+
return clampToRange(fallback, range);
|
|
237
|
+
return clampToRange(Math.floor(n), range);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* `clampInt` for a key that is legitimately a FLOAT.
|
|
241
|
+
*
|
|
242
|
+
* `coercePositiveInt` floors, and `2.5` is a legitimate backoff factor — the same
|
|
243
|
+
* coercion trap `clampIntAllowingZero` above closes for `0`, one type over.
|
|
244
|
+
*/
|
|
245
|
+
function clampNumber(v, fallback, range) {
|
|
246
|
+
if (v === null || v === undefined || v === '')
|
|
247
|
+
return clampToRange(fallback, range);
|
|
248
|
+
const n = typeof v === 'number' ? v : Number.parseFloat(String(v));
|
|
249
|
+
if (!Number.isFinite(n))
|
|
250
|
+
return clampToRange(fallback, range);
|
|
251
|
+
return clampToRange(n, range);
|
|
252
|
+
}
|
|
253
|
+
function clampToRange(n, range) {
|
|
254
|
+
return Math.min(range.max, Math.max(range.min, n));
|
|
255
|
+
}
|
|
182
256
|
function stringArray(v, fallback) {
|
|
183
257
|
if (!Array.isArray(v))
|
|
184
258
|
return fallback;
|
|
@@ -346,12 +420,169 @@ export function clampTodoConfig(raw) {
|
|
|
346
420
|
followThrough: followMode(src.followThrough, DEFAULT_TODO_CONFIG.followThrough),
|
|
347
421
|
};
|
|
348
422
|
}
|
|
423
|
+
export const DEFAULT_RETRY_CONFIG = {
|
|
424
|
+
enabled: true,
|
|
425
|
+
maxRetries: DEFAULT_RETRY_POLICY.maxRetries,
|
|
426
|
+
initialDelayMs: DEFAULT_RETRY_POLICY.initialDelayMs,
|
|
427
|
+
maxDelayMs: DEFAULT_RETRY_POLICY.maxDelayMs,
|
|
428
|
+
multiplier: DEFAULT_RETRY_POLICY.multiplier,
|
|
429
|
+
jitter: DEFAULT_RETRY_POLICY.jitter,
|
|
430
|
+
respectRetryAfter: DEFAULT_RETRY_POLICY.respectRetryAfter,
|
|
431
|
+
maxElapsedMs: DEFAULT_RETRY_POLICY.maxElapsedMs,
|
|
432
|
+
onPartialStream: DEFAULT_RETRY_POLICY.onPartialStream,
|
|
433
|
+
};
|
|
434
|
+
/**
|
|
435
|
+
* `hardMaxRetries` re-exported rather than re-spelled, for the reason
|
|
436
|
+
* `HARD_MAX_SUBAGENTS` records: the ceiling is enforced in TWO places — here on
|
|
437
|
+
* the config value, and again by `normalizePolicy` inside the engine — and two
|
|
438
|
+
* copies of the same 20 is exactly how a hand-edited `config.json` ends up able
|
|
439
|
+
* to raise a ceiling the mechanism fixed.
|
|
440
|
+
*/
|
|
441
|
+
export const HARD_MAX_RETRIES = RETRY_LIMITS.hardMaxRetries;
|
|
442
|
+
/** FLOOR 0 — and `clampIntAllowingZero` is what makes that reachable. */
|
|
443
|
+
const RETRY_MAX_RANGE = { min: 0, max: HARD_MAX_RETRIES };
|
|
444
|
+
const RETRY_INITIAL_DELAY_RANGE = { min: 100, max: 30_000 };
|
|
445
|
+
const RETRY_MAX_DELAY_RANGE = { min: 1000, max: RETRY_LIMITS.absoluteMaxDelayMs };
|
|
446
|
+
const RETRY_MULTIPLIER_RANGE = { min: 1, max: 5 };
|
|
447
|
+
const RETRY_ELAPSED_RANGE = { min: 10_000, max: 1_800_000 };
|
|
448
|
+
/**
|
|
449
|
+
* THE single gate for every retry-config read AND write, mirroring
|
|
450
|
+
* `clampSkillsConfig` / `clampLogConfig` / `clampTeamConfig` / `clampTodoConfig`
|
|
451
|
+
* for the reason those record: hardening only the read path leaves a bad value on
|
|
452
|
+
* disk that reverts to the default on every launch, which presents to the user as
|
|
453
|
+
* "my setting won't stick".
|
|
454
|
+
*
|
|
455
|
+
* Three invariants live inside the gate:
|
|
456
|
+
*
|
|
457
|
+
* - `maxDelayMs = max(maxDelayMs, initialDelayMs)` — a ceiling under the floor
|
|
458
|
+
* makes the ladder a flat line and the countdown a lie.
|
|
459
|
+
* - `multiplier` goes through `clampNumber`, not `clampInt`: `2.5` is a
|
|
460
|
+
* legitimate factor and `coercePositiveInt` would floor it to 2.
|
|
461
|
+
* - `maxRetries` goes through `clampIntAllowingZero`. THIS IS THE ONE THAT SHIPS
|
|
462
|
+
* BROKEN WITHOUT IT (R-16): `clampInt` returns the fallback for `n <= 0`, so
|
|
463
|
+
* `clampRetryConfig({ maxRetries: 0 })` would yield 10 — the maximum instead
|
|
464
|
+
* of the minimum, silently, for the key that gates the whole feature.
|
|
465
|
+
*
|
|
466
|
+
* `maxElapsedMs` intentionally keeps plain `clampInt`: its floor is 10 000, so it
|
|
467
|
+
* has no `0` to express.
|
|
468
|
+
*/
|
|
469
|
+
export function clampRetryConfig(raw) {
|
|
470
|
+
const src = (raw && typeof raw === 'object' ? raw : {});
|
|
471
|
+
const initialDelayMs = clampInt(src.initialDelayMs, DEFAULT_RETRY_CONFIG.initialDelayMs, RETRY_INITIAL_DELAY_RANGE);
|
|
472
|
+
const maxDelayMs = Math.max(initialDelayMs, clampInt(src.maxDelayMs, DEFAULT_RETRY_CONFIG.maxDelayMs, RETRY_MAX_DELAY_RANGE));
|
|
473
|
+
return {
|
|
474
|
+
enabled: bool(src.enabled, DEFAULT_RETRY_CONFIG.enabled),
|
|
475
|
+
maxRetries: clampIntAllowingZero(src.maxRetries, DEFAULT_RETRY_CONFIG.maxRetries, RETRY_MAX_RANGE),
|
|
476
|
+
initialDelayMs,
|
|
477
|
+
maxDelayMs,
|
|
478
|
+
multiplier: clampNumber(src.multiplier, DEFAULT_RETRY_CONFIG.multiplier, RETRY_MULTIPLIER_RANGE),
|
|
479
|
+
jitter: bool(src.jitter, DEFAULT_RETRY_CONFIG.jitter),
|
|
480
|
+
respectRetryAfter: bool(src.respectRetryAfter, DEFAULT_RETRY_CONFIG.respectRetryAfter),
|
|
481
|
+
maxElapsedMs: clampInt(src.maxElapsedMs, DEFAULT_RETRY_CONFIG.maxElapsedMs, RETRY_ELAPSED_RANGE),
|
|
482
|
+
onPartialStream: bool(src.onPartialStream, DEFAULT_RETRY_CONFIG.onPartialStream),
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* `RetryConfig` (user intent) -> `RetryPolicy` (engine policy), or `null` for
|
|
487
|
+
* "the user said no".
|
|
488
|
+
*
|
|
489
|
+
* `null` rather than a policy with `maxRetries: 0` because `ProviderRegistry`
|
|
490
|
+
* short-circuits on a null policy and returns the adapter's iterator DIRECTLY —
|
|
491
|
+
* an opted-out session is then allocation-identical to a pre-feature build.
|
|
492
|
+
*
|
|
493
|
+
* TOTAL, AND IT ACCEPTS A MISSING SECTION. `loadConfig` always supplies one, but
|
|
494
|
+
* a `CliConfig` built by hand — every controller test in this package does exactly
|
|
495
|
+
* that, and so would an embedder — legitimately omits it, and a `TypeError` from a
|
|
496
|
+
* pure translation function is the wrong way to find out. Routing through
|
|
497
|
+
* `clampRetryConfig` also means an out-of-range hand-built value can never reach
|
|
498
|
+
* the engine, which is the same "one gate, both directions" rule every other
|
|
499
|
+
* section in this file follows.
|
|
500
|
+
*/
|
|
501
|
+
export function toRetryPolicy(cfg) {
|
|
502
|
+
const safe = clampRetryConfig(cfg);
|
|
503
|
+
if (!safe.enabled || safe.maxRetries <= 0)
|
|
504
|
+
return null;
|
|
505
|
+
return {
|
|
506
|
+
maxRetries: safe.maxRetries,
|
|
507
|
+
initialDelayMs: safe.initialDelayMs,
|
|
508
|
+
maxDelayMs: safe.maxDelayMs,
|
|
509
|
+
multiplier: safe.multiplier,
|
|
510
|
+
jitter: safe.jitter,
|
|
511
|
+
respectRetryAfter: safe.respectRetryAfter,
|
|
512
|
+
maxElapsedMs: safe.maxElapsedMs,
|
|
513
|
+
onPartialStream: safe.onPartialStream,
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
export const DEFAULT_FAST_CONFIG = {
|
|
517
|
+
// R-d, and the one default in this file that is a REQUIREMENT rather than a
|
|
518
|
+
// judgement: a feature that silently spends money on a second provider is not
|
|
519
|
+
// a default (D-3).
|
|
520
|
+
enabled: false,
|
|
521
|
+
provider: '',
|
|
522
|
+
model: '',
|
|
523
|
+
baseUrl: '',
|
|
524
|
+
thinkingLevel: 'off',
|
|
525
|
+
delegate: true,
|
|
526
|
+
review: true,
|
|
527
|
+
reviewEveryTurns: 5,
|
|
528
|
+
reviewContextTurns: 3,
|
|
529
|
+
reviewMaxChars: 280,
|
|
530
|
+
// 40 is roughly 200 reviewed turns at the default cadence: comfortably past a
|
|
531
|
+
// full working session, short of a runaway (D-H2).
|
|
532
|
+
reviewMaxPerSession: 40,
|
|
533
|
+
};
|
|
534
|
+
const REVIEW_EVERY_RANGE = { min: 1, max: 50 };
|
|
535
|
+
const REVIEW_CONTEXT_RANGE = { min: 1, max: 10 };
|
|
536
|
+
const REVIEW_CHARS_RANGE = { min: 80, max: 600 };
|
|
537
|
+
/** No "unlimited" sentinel: a magic `0` reads as "disabled" to the next person
|
|
538
|
+
* who meets it, and 500 is already past any session a human sits through. */
|
|
539
|
+
const REVIEW_SESSION_RANGE = { min: 1, max: 500 };
|
|
540
|
+
/** Trim a string field, falling back when the value is not a string at all. */
|
|
541
|
+
function text(v, fallback) {
|
|
542
|
+
return typeof v === 'string' ? v.trim() : fallback;
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* THE single gate for every fast-config read AND write, mirroring
|
|
546
|
+
* `clampSkillsConfig` / `clampLogConfig` / `clampTeamConfig` / `clampTodoConfig`
|
|
547
|
+
* for the reason those four record: hardening only the read path leaves a bad
|
|
548
|
+
* value on disk that reverts to the default on every launch, which presents to
|
|
549
|
+
* the user as "my setting won't stick".
|
|
550
|
+
*
|
|
551
|
+
* `provider` is CLAMPED TO `''` rather than rejected when it names something
|
|
552
|
+
* with no adapter. `''` means "inherit the main provider", which is the safe
|
|
553
|
+
* reading of a typo — and `resolveFastTier` reports `no_adapter` for the case a
|
|
554
|
+
* user really did configure a dead vendor as the MAIN one.
|
|
555
|
+
*/
|
|
556
|
+
export function clampFastConfig(raw) {
|
|
557
|
+
const src = (raw && typeof raw === 'object' ? raw : {});
|
|
558
|
+
const provider = text(src.provider, DEFAULT_FAST_CONFIG.provider);
|
|
559
|
+
return {
|
|
560
|
+
enabled: bool(src.enabled, DEFAULT_FAST_CONFIG.enabled),
|
|
561
|
+
provider: provider.length === 0 || isAdapterProvider(provider) ? provider : '',
|
|
562
|
+
model: text(src.model, DEFAULT_FAST_CONFIG.model),
|
|
563
|
+
baseUrl: text(src.baseUrl, DEFAULT_FAST_CONFIG.baseUrl),
|
|
564
|
+
thinkingLevel: clampThinkingLevel(src.thinkingLevel, DEFAULT_FAST_CONFIG.thinkingLevel),
|
|
565
|
+
delegate: bool(src.delegate, DEFAULT_FAST_CONFIG.delegate),
|
|
566
|
+
review: bool(src.review, DEFAULT_FAST_CONFIG.review),
|
|
567
|
+
reviewEveryTurns: clampInt(src.reviewEveryTurns, DEFAULT_FAST_CONFIG.reviewEveryTurns, REVIEW_EVERY_RANGE),
|
|
568
|
+
reviewContextTurns: clampInt(src.reviewContextTurns, DEFAULT_FAST_CONFIG.reviewContextTurns, REVIEW_CONTEXT_RANGE),
|
|
569
|
+
reviewMaxChars: clampInt(src.reviewMaxChars, DEFAULT_FAST_CONFIG.reviewMaxChars, REVIEW_CHARS_RANGE),
|
|
570
|
+
reviewMaxPerSession: clampInt(src.reviewMaxPerSession, DEFAULT_FAST_CONFIG.reviewMaxPerSession, REVIEW_SESSION_RANGE),
|
|
571
|
+
};
|
|
572
|
+
}
|
|
349
573
|
export const DEFAULT_CONFIG = {
|
|
350
574
|
version: CONFIG_VERSION,
|
|
351
575
|
provider: 'anthropic',
|
|
352
576
|
model: DEFAULT_MODEL,
|
|
353
577
|
baseUrl: null,
|
|
354
578
|
thinkingLevel: 'off',
|
|
579
|
+
// Off by default — the product default this whole round exists to establish
|
|
580
|
+
// (D-1). One config key restores the old behaviour permanently.
|
|
581
|
+
showThinking: false,
|
|
582
|
+
// ON by default (D-28), and the OPPOSITE of the key above it on purpose: this
|
|
583
|
+
// one shows work that is otherwise invisible for the whole duration of the
|
|
584
|
+
// most expensive thing the agent does.
|
|
585
|
+
liveToolOutput: true,
|
|
355
586
|
// The engine owns this number; spelling it here again is how the CLI and the
|
|
356
587
|
// provider adapters end up disagreeing about what "the default" is.
|
|
357
588
|
maxTokens: DEFAULT_MAX_OUTPUT_TOKENS,
|
|
@@ -360,6 +591,9 @@ export const DEFAULT_CONFIG = {
|
|
|
360
591
|
fullscreen: true,
|
|
361
592
|
exitTranscript: true,
|
|
362
593
|
transcriptWindow: DEFAULT_TRANSCRIPT_WINDOW,
|
|
594
|
+
transcriptRetain: DEFAULT_TRANSCRIPT_RETAIN,
|
|
595
|
+
renderGovernor: DEFAULT_RENDER_GOVERNOR,
|
|
596
|
+
maxRenderIntervalMs: DEFAULT_MAX_RENDER_INTERVAL_MS,
|
|
363
597
|
confirmTools: false,
|
|
364
598
|
toolTimeoutMs: DEFAULT_TOOL_TIMEOUT_MS,
|
|
365
599
|
idleTimeoutMs: DEFAULT_IDLE_TIMEOUT_MS,
|
|
@@ -378,6 +612,15 @@ export const DEFAULT_CONFIG = {
|
|
|
378
612
|
// `todo` key at all, so this line is as required as the two merges in
|
|
379
613
|
// `store.ts` (P0-1).
|
|
380
614
|
todo: DEFAULT_TODO_CONFIG,
|
|
615
|
+
// Same requirement one section later: without this line a user with no `retry`
|
|
616
|
+
// key resolves `config.retry` to `undefined` and `toRetryPolicy` throws.
|
|
617
|
+
retry: DEFAULT_RETRY_CONFIG,
|
|
618
|
+
// And one section later again: the load path's first spread is what supplies
|
|
619
|
+
// `fast` to a user whose `config.json` predates this feature, which is every
|
|
620
|
+
// existing user. Without it `config.fast.enabled` reads `undefined`, which is
|
|
621
|
+
// falsy and so LOOKS correct — right up until `/fast on` writes a section that
|
|
622
|
+
// has no other keys in it (P0-1's shape, one feature later).
|
|
623
|
+
fast: DEFAULT_FAST_CONFIG,
|
|
381
624
|
};
|
|
382
625
|
/**
|
|
383
626
|
* Runtime state that used to be stored in `config.json` by mistake
|