@aragon-agent/cli 0.6.29 → 0.6.32
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 +2223 -2103
- package/README.md +2957 -2759
- package/dist/agent/controller.d.ts +46 -0
- package/dist/agent/controller.js +175 -7
- package/dist/agent/job-notices.d.ts +143 -0
- package/dist/agent/job-notices.js +182 -0
- package/dist/agent/project-guidance-prompt.js +20 -20
- package/dist/agent/reducer.d.ts +21 -1
- package/dist/agent/reducer.js +7 -0
- package/dist/agent/system-prompt.d.ts +12 -0
- package/dist/agent/system-prompt.js +2 -0
- package/dist/cli.js +399 -62
- package/dist/commands/builtins.js +27 -8
- package/dist/commands/perf.d.ts +79 -2
- package/dist/commands/perf.js +51 -3
- package/dist/commands/registry.d.ts +6 -0
- package/dist/config/cli-commands.d.ts +27 -0
- package/dist/config/cli-commands.js +54 -1
- package/dist/config/env.js +42 -0
- package/dist/config/load.d.ts +3 -1
- package/dist/config/load.js +61 -1
- package/dist/config/schema.d.ts +100 -0
- package/dist/config/schema.js +43 -0
- package/dist/config/store.js +14 -1
- package/dist/config/ui-state.d.ts +9 -0
- package/dist/config/ui-state.js +9 -0
- package/dist/exec/events.d.ts +29 -3
- package/dist/exec/events.js +3 -12
- package/dist/exec/runner.d.ts +24 -0
- package/dist/exec/runner.js +79 -0
- package/dist/input/cursor-report.d.ts +61 -0
- package/dist/input/cursor-report.js +97 -0
- package/dist/input/stdin-filter.d.ts +22 -0
- package/dist/input/stdin-filter.js +48 -3
- package/dist/proc/limits.d.ts +76 -2
- package/dist/proc/limits.js +83 -2
- package/dist/proc/log-mirror.d.ts +59 -0
- package/dist/proc/log-mirror.js +203 -0
- package/dist/proc/log-ring.d.ts +22 -0
- package/dist/proc/log-ring.js +20 -1
- package/dist/proc/notices.d.ts +57 -0
- package/dist/proc/notices.js +101 -0
- package/dist/proc/progress.d.ts +40 -0
- package/dist/proc/progress.js +135 -0
- package/dist/proc/prompt.d.ts +24 -2
- package/dist/proc/prompt.js +51 -2
- package/dist/proc/supervisor.d.ts +35 -2
- package/dist/proc/supervisor.js +142 -43
- package/dist/proc/types.d.ts +89 -1
- package/dist/proc/wait.d.ts +57 -0
- package/dist/proc/wait.js +133 -0
- package/dist/session/persist.js +3 -1
- package/dist/session/validate-session.js +17 -0
- package/dist/tools/bash-tool.d.ts +12 -1
- package/dist/tools/bash-tool.js +100 -13
- package/dist/tools/index.d.ts +26 -2
- package/dist/tools/index.js +43 -2
- package/dist/tools/job-tools.d.ts +54 -0
- package/dist/tools/job-tools.js +329 -0
- package/dist/tools/proc-tools.d.ts +1 -1
- package/dist/tools/proc-tools.js +49 -17
- package/dist/ui/ActivityLine.js +7 -0
- package/dist/ui/App.d.ts +11 -1
- package/dist/ui/App.js +181 -63
- package/dist/ui/ScrollbackShell.d.ts +54 -0
- package/dist/ui/ScrollbackShell.js +15 -0
- package/dist/ui/StatusBar.d.ts +5 -0
- package/dist/ui/StatusBar.js +1 -0
- package/dist/ui/Transcript.d.ts +14 -0
- package/dist/ui/Transcript.js +14 -2
- package/dist/ui/entries/ServiceCard.d.ts +17 -1
- package/dist/ui/entries/ServiceCard.js +39 -4
- package/dist/ui/frame-differ.d.ts +12 -0
- package/dist/ui/layout/status-layout.d.ts +13 -1
- package/dist/ui/layout/status-layout.js +10 -0
- package/dist/ui/layout/virtual-window.js +14 -1
- package/dist/ui/overlays/HelpOverlay.d.ts +1 -1
- package/dist/ui/overlays/HelpOverlay.js +21 -3
- package/dist/ui/run-status-row.d.ts +12 -0
- package/dist/ui/screen.d.ts +29 -0
- package/dist/ui/screen.js +40 -0
- package/dist/ui/scrollback/LiveTail.d.ts +64 -0
- package/dist/ui/scrollback/LiveTail.js +27 -0
- package/dist/ui/scrollback/band.d.ts +105 -0
- package/dist/ui/scrollback/band.js +109 -0
- package/dist/ui/scrollback/budget.d.ts +72 -0
- package/dist/ui/scrollback/budget.js +81 -0
- package/dist/ui/scrollback/canvas.d.ts +99 -0
- package/dist/ui/scrollback/canvas.js +113 -0
- package/dist/ui/scrollback/cursor-probe.d.ts +71 -0
- package/dist/ui/scrollback/cursor-probe.js +133 -0
- package/dist/ui/scrollback/drain.d.ts +32 -0
- package/dist/ui/scrollback/drain.js +64 -0
- package/dist/ui/scrollback/exit-summary.d.ts +23 -0
- package/dist/ui/scrollback/exit-summary.js +25 -0
- package/dist/ui/scrollback/limits.d.ts +74 -0
- package/dist/ui/scrollback/limits.js +74 -0
- package/dist/ui/scrollback/live-preview.d.ts +26 -0
- package/dist/ui/scrollback/live-preview.js +106 -0
- package/dist/ui/scrollback/renderer.d.ts +38 -0
- package/dist/ui/scrollback/renderer.js +158 -0
- package/dist/ui/scrollback/scrollback-writer.d.ts +162 -0
- package/dist/ui/scrollback/scrollback-writer.js +549 -0
- package/dist/ui/scrollback/settled-boundary.d.ts +42 -0
- package/dist/ui/scrollback/settled-boundary.js +106 -0
- package/dist/ui/scrollback/use-scrollback-view.d.ts +103 -0
- package/dist/ui/scrollback/use-scrollback-view.js +294 -0
- package/dist/ui/scrollback/writer-shapes.d.ts +127 -0
- package/dist/ui/scrollback/writer-shapes.js +159 -0
- package/dist/ui/stdout-frame-writer.js +12 -2
- package/dist/ui/terminal-plan.d.ts +52 -0
- package/dist/ui/terminal-plan.js +49 -0
- package/dist/ui/transcript-text.d.ts +11 -0
- package/dist/ui/transcript-text.js +21 -7
- package/dist/ui/use-startup-notices.d.ts +12 -1
- package/dist/ui/use-startup-notices.js +28 -2
- package/package.json +3 -3
- package/skills/project-indexer/SKILL.md +163 -163
- package/skills/project-indexer/templates/cleancode-overrides.md +46 -46
- package/skills/project-indexer/templates/cleancode-template.md +94 -94
- package/skills/project-indexer/templates/config-template.md +69 -69
- package/skills/project-indexer/templates/index-template.md +129 -129
- package/skills/skill-creator/SKILL.md +96 -96
|
@@ -9,6 +9,7 @@ import { existsSync, statSync } from 'node:fs';
|
|
|
9
9
|
import { isAbsolute, resolve } from 'node:path';
|
|
10
10
|
import { resolveOutputTokens } from '@aragon-agent/core';
|
|
11
11
|
import { clampThinkingLevel, clampTheme, isAdapterProvider, isThemeName, parseMaxTokensInput, ADAPTER_PROVIDERS, DEFAULT_MAX_TOKENS, HARD_MAX_RETRIES, LEGACY_DARK_THEME, THEME_NAMES, THINKING_LEVELS, } from '../config/schema.js';
|
|
12
|
+
import { isTerminalStatus } from '../proc/types.js';
|
|
12
13
|
import { computeCost, formatCost, formatDuration, formatTokens } from '../agent/usage.js';
|
|
13
14
|
import { mergePendingEntries } from '../agent/queued-messages.js';
|
|
14
15
|
import { TODO_FOLLOW_LIMITS } from '../todo/limits.js';
|
|
@@ -1122,6 +1123,10 @@ const COMMANDS = [
|
|
|
1122
1123
|
name: 'mouse',
|
|
1123
1124
|
description: 'Release the mouse to the terminal, or take it back',
|
|
1124
1125
|
run: (ctx) => {
|
|
1126
|
+
if (ctx.nativeScroll) {
|
|
1127
|
+
ctx.notify('info', 'Mouse capture is not used in native scroll mode; your terminal handles scrolling and selection.');
|
|
1128
|
+
return;
|
|
1129
|
+
}
|
|
1125
1130
|
const port = ctx.mouse;
|
|
1126
1131
|
if (!port) {
|
|
1127
1132
|
// Inline mode, `--no-mouse`, a non-TTY, or a console that cannot deliver
|
|
@@ -1252,7 +1257,7 @@ const COMMANDS = [
|
|
|
1252
1257
|
perfCommand,
|
|
1253
1258
|
{
|
|
1254
1259
|
name: 'bg',
|
|
1255
|
-
description: 'Background
|
|
1260
|
+
description: 'Background jobs: list | logs <id> [n] | stop <id|all> | status',
|
|
1256
1261
|
/**
|
|
1257
1262
|
* THE USER'S HANDLE ON WHAT THE AGENT LEFT RUNNING (§5.3).
|
|
1258
1263
|
*
|
|
@@ -1273,9 +1278,18 @@ const COMMANDS = [
|
|
|
1273
1278
|
const services = controller.listServices();
|
|
1274
1279
|
if (verb === 'status') {
|
|
1275
1280
|
const live = controller.liveServiceCount();
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1281
|
+
if (live === 0) {
|
|
1282
|
+
ctx.notify('info', 'No background jobs running.');
|
|
1283
|
+
return;
|
|
1284
|
+
}
|
|
1285
|
+
// THE KIND SPLIT IS THE INFORMATION (long-task-jobs §4.9): "2 jobs
|
|
1286
|
+
// running" is not the same as "1 task, 1 service". A task will end on its
|
|
1287
|
+
// own; a service will not, and that difference decides whether the user
|
|
1288
|
+
// has to do anything.
|
|
1289
|
+
const running = services.filter((job) => !isTerminalStatus(job.status));
|
|
1290
|
+
const tasks = running.filter((job) => job.kind === 'task').length;
|
|
1291
|
+
ctx.notify('info', `${live} background job${live === 1 ? '' : 's'} running ` +
|
|
1292
|
+
`(${tasks} task, ${running.length - tasks} service).`);
|
|
1279
1293
|
return;
|
|
1280
1294
|
}
|
|
1281
1295
|
if (verb === 'logs') {
|
|
@@ -1285,7 +1299,7 @@ const COMMANDS = [
|
|
|
1285
1299
|
}
|
|
1286
1300
|
const service = controller.getServiceSnapshot(value);
|
|
1287
1301
|
if (!service) {
|
|
1288
|
-
ctx.notify('warn', `No
|
|
1302
|
+
ctx.notify('warn', `No job "${value}".`);
|
|
1289
1303
|
return;
|
|
1290
1304
|
}
|
|
1291
1305
|
const page = controller.readServiceLog(value);
|
|
@@ -1331,12 +1345,17 @@ const COMMANDS = [
|
|
|
1331
1345
|
return;
|
|
1332
1346
|
}
|
|
1333
1347
|
const now = Date.now();
|
|
1348
|
+
// `id kind status elapsed progress|url $ command` - the same column
|
|
1349
|
+
// grammar `job_list` uses for the model, so a user reading `/bg list` and a
|
|
1350
|
+
// model reading `job_list` see the same facts in the same order.
|
|
1334
1351
|
const lines = services.map((svc) => {
|
|
1335
1352
|
const uptime = formatDuration((svc.endedAt ?? now) - svc.startedAt);
|
|
1336
|
-
const
|
|
1337
|
-
|
|
1353
|
+
const aside = svc.progress?.text ?? svc.url;
|
|
1354
|
+
const where = aside ? ` ${aside}` : '';
|
|
1355
|
+
const stalled = svc.stalled === true ? ' [no output]' : '';
|
|
1356
|
+
return `${svc.id} ${svc.kind} ${svc.status} ${uptime}${where}${stalled} $ ${svc.command}`;
|
|
1338
1357
|
});
|
|
1339
|
-
ctx.notify('info', `Background
|
|
1358
|
+
ctx.notify('info', `Background jobs:\n${lines.join('\n')}`);
|
|
1340
1359
|
},
|
|
1341
1360
|
},
|
|
1342
1361
|
{
|
package/dist/commands/perf.d.ts
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import type { FrameWriterStats } from '../ui/frame-differ.js';
|
|
19
19
|
import type { ScrollbarBridge } from '../ui/scrollbar-controller.js';
|
|
20
|
+
import type { AnchorSource } from '../ui/scrollback/canvas.js';
|
|
20
21
|
import type { SlashCommand } from './registry.js';
|
|
21
22
|
export interface PerfSnapshot {
|
|
22
23
|
/** Governor rung, its resolved interval, and the last measured commit cost. */
|
|
@@ -34,11 +35,38 @@ export interface PerfSnapshot {
|
|
|
34
35
|
heightsEstimated: number;
|
|
35
36
|
cols: number;
|
|
36
37
|
/** Geometry. */
|
|
37
|
-
mode: 'fullscreen';
|
|
38
|
+
mode: 'fullscreen' | 'native';
|
|
39
|
+
/** Full-screen: the transcript viewport. Native: the live region height. */
|
|
38
40
|
viewportRows: number;
|
|
39
41
|
offset: number;
|
|
40
42
|
/** Right-edge scrollbar diagnostics; read lazily so it reflects the live bridge. */
|
|
41
43
|
scrollbar?: ScrollbarPerf;
|
|
44
|
+
/**
|
|
45
|
+
* The native panel band, or `null` outside native mode
|
|
46
|
+
* (release-gate-and-native-panels §6.10).
|
|
47
|
+
*
|
|
48
|
+
* REQUIRED, WITH AN EXPLICIT `null`. This feature has six ways to show nothing and
|
|
49
|
+
* four of them are invisible, so "the rail is gone" has to be answerable from one
|
|
50
|
+
* line. An optional field would let a future caller forget it and leave `/perf`
|
|
51
|
+
* silently unable to explain the case the user is reporting.
|
|
52
|
+
*/
|
|
53
|
+
band: BandPerf | null;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The band's projection, as `/perf` reads it. Counts only - the allocation itself lives
|
|
57
|
+
* in `ui/scrollback/band.ts` (`LiveBand`), and this type exists so the command layer
|
|
58
|
+
* does not have to import a UI module to describe it.
|
|
59
|
+
*/
|
|
60
|
+
export interface BandPerf {
|
|
61
|
+
reason: 'visible' | 'empty' | 'disabled' | 'overlay' | 'narrow' | 'short';
|
|
62
|
+
railRows: number;
|
|
63
|
+
railCols: number;
|
|
64
|
+
teamRows: number;
|
|
65
|
+
previewRows: number;
|
|
66
|
+
contentCols: number;
|
|
67
|
+
previewCols: number;
|
|
68
|
+
heldRailRows: number;
|
|
69
|
+
heldTeamRows: number;
|
|
42
70
|
}
|
|
43
71
|
/**
|
|
44
72
|
* Why the right-edge scrollbar is or is not usable, in one record
|
|
@@ -74,6 +102,55 @@ export declare function setPerfResetHook(fn: (() => void) | null): void;
|
|
|
74
102
|
/** Called by `cli.tsx` after building the writer. Passing `null` detaches. */
|
|
75
103
|
export declare function setFrameStatsProvider(fn: (() => FrameWriterStats) | null): void;
|
|
76
104
|
export declare function readFrameStats(): FrameWriterStats | null;
|
|
77
|
-
/**
|
|
105
|
+
/**
|
|
106
|
+
* Native-scroll writer counters (tui-native-terminal-scroll §5.1). A third
|
|
107
|
+
* module-level slot for the reason the one above records: the writer lives outside
|
|
108
|
+
* React. `guardHitsOutsideResize > 0` on a session with no resize is the defect
|
|
109
|
+
* signal (a live region reached the terminal height); `clippedRows > 0` means Ink
|
|
110
|
+
* and the terminal disagreed about a character's width (I-S13).
|
|
111
|
+
*/
|
|
112
|
+
export interface ScrollbackPerf {
|
|
113
|
+
/** LIVE REGION height (the Ink frame), NOT the canvas — see `anchorCanvasRows`. */
|
|
114
|
+
regionRows: number;
|
|
115
|
+
framesSeed: number;
|
|
116
|
+
framesDiff: number;
|
|
117
|
+
framesFull: number;
|
|
118
|
+
staticRowsWritten: number;
|
|
119
|
+
elidedEntries: number;
|
|
120
|
+
guardHits: number;
|
|
121
|
+
guardHitsOutsideResize: number;
|
|
122
|
+
clippedRows: number;
|
|
123
|
+
fallbacks: number;
|
|
124
|
+
resizes: number;
|
|
125
|
+
repaints: number;
|
|
126
|
+
/** `probe` = measured by CPR; `fill` = the one-off screen fill; `none` = unmeasured. */
|
|
127
|
+
anchorSource: AnchorSource;
|
|
128
|
+
/**
|
|
129
|
+
* Canvas height of the most recent batch, which is also the next erase distance.
|
|
130
|
+
*
|
|
131
|
+
* A SEPARATE FIELD RATHER THAN A RENAMED `regionRows` (P1-4): `/perf`'s
|
|
132
|
+
* "scrollback region N rows" is the first number anyone reads when the frame
|
|
133
|
+
* looks wrong, and quietly turning it into the canvas height would make a 6-row
|
|
134
|
+
* region report 24 during a short conversation.
|
|
135
|
+
*/
|
|
136
|
+
anchorCanvasRows: number;
|
|
137
|
+
/** Content rows inside that canvas — how much of the conversation is on screen. */
|
|
138
|
+
anchorHeldRows: number;
|
|
139
|
+
/** Blank rows above the content. Non-zero is expected on a short conversation. */
|
|
140
|
+
anchorGapRows: number;
|
|
141
|
+
/** Rows this session pushed into the terminal's own scrollback. */
|
|
142
|
+
anchorScrollRows: number;
|
|
143
|
+
/** Times the geometry was dropped on purpose (column change, shrink, geometry lost). */
|
|
144
|
+
anchorResets: number;
|
|
145
|
+
/** Measurements accepted after the first: the re-probe count, not the resize count. */
|
|
146
|
+
anchorReprobes: number;
|
|
147
|
+
}
|
|
148
|
+
/** Called by `cli.tsx` in native mode. Passing `null` detaches. */
|
|
149
|
+
export declare function setScrollbackStatsProvider(fn: (() => ScrollbackPerf) | null): void;
|
|
150
|
+
/**
|
|
151
|
+
* Format a snapshot as the seven-line block documented in §5.4 (plus the scrollbar
|
|
152
|
+
* line). The `band:` line is the newest one and the only place that answers "why is
|
|
153
|
+
* that panel not on screen".
|
|
154
|
+
*/
|
|
78
155
|
export declare function formatPerfReport(snap: PerfSnapshot): string;
|
|
79
156
|
export declare const perfCommand: SlashCommand;
|
package/dist/commands/perf.js
CHANGED
|
@@ -68,6 +68,28 @@ export function setFrameStatsProvider(fn) {
|
|
|
68
68
|
export function readFrameStats() {
|
|
69
69
|
return frameStats ? frameStats() : null;
|
|
70
70
|
}
|
|
71
|
+
let scrollbackStats = null;
|
|
72
|
+
/** Called by `cli.tsx` in native mode. Passing `null` detaches. */
|
|
73
|
+
export function setScrollbackStatsProvider(fn) {
|
|
74
|
+
scrollbackStats = fn;
|
|
75
|
+
}
|
|
76
|
+
function formatScrollbackLine() {
|
|
77
|
+
const stats = scrollbackStats?.();
|
|
78
|
+
if (!stats)
|
|
79
|
+
return [];
|
|
80
|
+
return [
|
|
81
|
+
`scrollback region ${stats.regionRows} rows - canvas ${stats.anchorCanvasRows} rows - ` +
|
|
82
|
+
`content ${stats.anchorHeldRows} rows - gap ${stats.anchorGapRows} rows - ` +
|
|
83
|
+
`anchor bottom via ${stats.anchorSource} - scrolled ${stats.anchorScrollRows} rows - ` +
|
|
84
|
+
`anchor resets ${stats.anchorResets} - reprobes ${stats.anchorReprobes}`,
|
|
85
|
+
` batches seed ${stats.framesSeed}, ` +
|
|
86
|
+
`diff ${stats.framesDiff}, full ${stats.framesFull}, repaint ${stats.repaints} - ` +
|
|
87
|
+
`static rows ${stats.staticRowsWritten} - elided ${stats.elidedEntries} - ` +
|
|
88
|
+
`guardHits ${stats.guardHits} (outside resize ${stats.guardHitsOutsideResize}) - ` +
|
|
89
|
+
`clippedRows ${stats.clippedRows} - fallbacks ${stats.fallbacks} - ` +
|
|
90
|
+
`resizes ${stats.resizes}`,
|
|
91
|
+
];
|
|
92
|
+
}
|
|
71
93
|
function mb(bytes) {
|
|
72
94
|
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
73
95
|
}
|
|
@@ -102,12 +124,34 @@ function formatScrollbarLine(bar) {
|
|
|
102
124
|
? `${bar.trackCol}x${bar.trackRows + 4}` : 'the terminal size';
|
|
103
125
|
return `${line}\n waiting for a full frame matching ${size}`;
|
|
104
126
|
}
|
|
105
|
-
/**
|
|
127
|
+
/**
|
|
128
|
+
* `band:` - who got which rows, and why a panel is not on screen.
|
|
129
|
+
*
|
|
130
|
+
* `rail=<rows>x<cols>` reads `0x0` when the rail is not drawn, which is the only
|
|
131
|
+
* unambiguous "there is no rail" - a conservative width would look like a rail that
|
|
132
|
+
* merely has no height. One line, because `/perf` is a notice entry and a wrapping
|
|
133
|
+
* readout costs the repaint it exists to explain.
|
|
134
|
+
*/
|
|
135
|
+
function formatBandLine(band) {
|
|
136
|
+
if (!band)
|
|
137
|
+
return 'band n/a (full-screen layout)';
|
|
138
|
+
return `band rail=${band.railRows}x${band.railCols} team=${band.teamRows} ` +
|
|
139
|
+
`preview=${band.previewRows} cols=${band.contentCols}/${band.previewCols} ` +
|
|
140
|
+
`reason=${band.reason} held=${band.heldRailRows}/${band.heldTeamRows}`;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Format a snapshot as the seven-line block documented in §5.4 (plus the scrollbar
|
|
144
|
+
* line). The `band:` line is the newest one and the only place that answers "why is
|
|
145
|
+
* that panel not on screen".
|
|
146
|
+
*/
|
|
106
147
|
export function formatPerfReport(snap) {
|
|
107
148
|
const caches = renderCacheStats();
|
|
108
149
|
const eco = snap.rung > 0 ? ' - eco' : '';
|
|
109
150
|
const governor = snap.governorEnabled ? '' : ' - governor off';
|
|
110
|
-
const
|
|
151
|
+
const native = snap.mode === 'native';
|
|
152
|
+
const scrollbar = native
|
|
153
|
+
? ['scrollbar n/a (native scroll)']
|
|
154
|
+
: snap.scrollbar ? [formatScrollbarLine(snap.scrollbar)] : [];
|
|
111
155
|
return [
|
|
112
156
|
`render rung ${snap.rung} - interval ${snap.intervalMs}ms - last commit ` +
|
|
113
157
|
`${Math.round(snap.lastCommitMs)}ms${eco}${governor}`,
|
|
@@ -118,8 +162,12 @@ export function formatPerfReport(snap) {
|
|
|
118
162
|
`caches md ${caches.markdownEntries} (${mb(caches.markdownBytes)}) - ` +
|
|
119
163
|
`hl ${caches.highlightEntries} (${mb(caches.highlightBytes)}) - ` +
|
|
120
164
|
`lines ${caches.lineEntries}`,
|
|
121
|
-
|
|
165
|
+
native
|
|
166
|
+
? `mode native - live region ${snap.viewportRows} rows`
|
|
167
|
+
: `mode ${snap.mode} - viewport ${snap.viewportRows} rows - offset ${snap.offset}`,
|
|
168
|
+
formatBandLine(snap.band),
|
|
122
169
|
formatWriterLine(),
|
|
170
|
+
...formatScrollbackLine(),
|
|
123
171
|
...scrollbar,
|
|
124
172
|
].join('\n');
|
|
125
173
|
}
|
|
@@ -100,6 +100,12 @@ export interface CommandContext {
|
|
|
100
100
|
* missing, which is the same reason `update` above it is optional.
|
|
101
101
|
*/
|
|
102
102
|
mouse?: MouseCommandPort;
|
|
103
|
+
/**
|
|
104
|
+
* True in native-scroll mode (tui-native-terminal-scroll): the terminal owns
|
|
105
|
+
* scrolling and selection, so there is no capture to toggle. Lets `/mouse` say so
|
|
106
|
+
* instead of the generic "not captured" answer.
|
|
107
|
+
*/
|
|
108
|
+
nativeScroll?: boolean;
|
|
103
109
|
/**
|
|
104
110
|
* The foreign-write door, for `/copy`'s OSC 52 half (§4.4.5).
|
|
105
111
|
*
|
|
@@ -21,6 +21,19 @@ export declare const LOG_CONFIG_SET_KEYS: readonly ["log.level", "log.toFile", "
|
|
|
21
21
|
* `Set team.enabled = false`.
|
|
22
22
|
*/
|
|
23
23
|
export declare const TEAM_CONFIG_SET_KEYS: readonly ["team.enabled", "team.maxSubagents", "team.maxConcurrent", "team.subagentTimeoutMs", "team.dispatchTimeoutMs", "team.maxTurnsPerSubagent", "team.overseer", "team.overseerIntervalMs", "team.model", "team.modelProfileId"];
|
|
24
|
+
/**
|
|
25
|
+
* Config keys under `jobs.` that `aragon config set` accepts
|
|
26
|
+
* (long-task-jobs §6.5).
|
|
27
|
+
*
|
|
28
|
+
* THE FIRST NESTED SECTION WHOSE KEYS ARE ALL WRITABLE, so it is also the first
|
|
29
|
+
* one where `JOBS_CONFIG_SET_KEYS ⊆ CONFIG_SET_KEYS` needs its own assertion:
|
|
30
|
+
* `__tests__/config.test.ts`'s static guard matches LITERAL keys in `cli.tsx`, so
|
|
31
|
+
* a set that is spread in is invisible to it (RV-P2-8).
|
|
32
|
+
*
|
|
33
|
+
* Every key is ONE level deep on purpose — `runConfigGet` splits on a single dot,
|
|
34
|
+
* so `jobs.waitSeconds` resolves and `jobs.mirror.enabled` would not.
|
|
35
|
+
*/
|
|
36
|
+
export declare const JOBS_CONFIG_SET_KEYS: readonly ["jobs.enabled", "jobs.waitSeconds", "jobs.stallSeconds", "jobs.persistLog", "jobs.noticeSteer"];
|
|
24
37
|
/**
|
|
25
38
|
* Config keys under `retry.` that `aragon config set` accepts
|
|
26
39
|
* (llm-api-retry-backoff §8.2).
|
|
@@ -81,6 +94,20 @@ export declare const COMPACTION_CONFIG_SET_KEYS: readonly ["compaction.enabled",
|
|
|
81
94
|
* included.
|
|
82
95
|
*/
|
|
83
96
|
export declare function applyLogConfigSet(key: string, value: string): Partial<PersistedConfig> | null;
|
|
97
|
+
/**
|
|
98
|
+
* Translate a `jobs.*` dotted key into a patch, or `null` for anything else.
|
|
99
|
+
*
|
|
100
|
+
* THE TEMPLATE IS `applyTeamConfigSet`, NOT an `applyBashConfigSet` — no such
|
|
101
|
+
* function exists, and `bash.*` is not `config set`-able to this day (RV-P2-8).
|
|
102
|
+
* Every numeric key routes through `clampJobsConfig` rather than a local
|
|
103
|
+
* coercion, so the caller echoes the STORED value and an out-of-range input is
|
|
104
|
+
* visible: `config set jobs.waitSeconds 1` prints `5`.
|
|
105
|
+
*
|
|
106
|
+
* The patch is a PARTIAL section, which is only safe because
|
|
107
|
+
* `updatePersistedConfig` deep-merges `jobs` — the sixth time that trap is
|
|
108
|
+
* recorded in `store.ts`.
|
|
109
|
+
*/
|
|
110
|
+
export declare function applyJobsConfigSet(key: string, value: string): Partial<PersistedConfig> | null;
|
|
84
111
|
/**
|
|
85
112
|
* Translate a `team.*` dotted key into a patch, or `null` for anything else.
|
|
86
113
|
*
|
|
@@ -14,7 +14,7 @@ import { getConfigBackupPath, getConfigPath, getHomeRoot, getLogsDir, getPromptH
|
|
|
14
14
|
import { getHomeMigrationBreadcrumbPath } from './migrate-home.js';
|
|
15
15
|
import { loadPersistedConfig, updatePersistedConfig } from './store.js';
|
|
16
16
|
import { normalizeModelProfiles, validateModelProfiles } from './model-profiles.js';
|
|
17
|
-
import { clampFastConfig, clampRetryConfig, clampCompactionConfig, clampTeamConfig, parseThresholdInput, clampUpdateConfig, coercePositiveInt, maskSecret, DEFAULT_FAST_CONFIG, DEFAULT_RETRY_CONFIG, DEFAULT_TEAM_CONFIG, DEFAULT_COMPACTION_CONFIG, DEFAULT_UPDATE_CONFIG, } from './schema.js';
|
|
17
|
+
import { clampFastConfig, clampRetryConfig, clampCompactionConfig, clampTeamConfig, parseThresholdInput, clampUpdateConfig, coercePositiveInt, maskSecret, DEFAULT_FAST_CONFIG, DEFAULT_RETRY_CONFIG, DEFAULT_TEAM_CONFIG, DEFAULT_COMPACTION_CONFIG, DEFAULT_UPDATE_CONFIG, DEFAULT_JOBS_CONFIG, clampJobsConfig, } from './schema.js';
|
|
18
18
|
import { clampLogLevel } from '../logging/levels.js';
|
|
19
19
|
/** Config keys under `log.` that `aragon config set` accepts. */
|
|
20
20
|
export const LOG_CONFIG_SET_KEYS = [
|
|
@@ -48,6 +48,25 @@ export const TEAM_CONFIG_SET_KEYS = [
|
|
|
48
48
|
'team.model',
|
|
49
49
|
'team.modelProfileId',
|
|
50
50
|
];
|
|
51
|
+
/**
|
|
52
|
+
* Config keys under `jobs.` that `aragon config set` accepts
|
|
53
|
+
* (long-task-jobs §6.5).
|
|
54
|
+
*
|
|
55
|
+
* THE FIRST NESTED SECTION WHOSE KEYS ARE ALL WRITABLE, so it is also the first
|
|
56
|
+
* one where `JOBS_CONFIG_SET_KEYS ⊆ CONFIG_SET_KEYS` needs its own assertion:
|
|
57
|
+
* `__tests__/config.test.ts`'s static guard matches LITERAL keys in `cli.tsx`, so
|
|
58
|
+
* a set that is spread in is invisible to it (RV-P2-8).
|
|
59
|
+
*
|
|
60
|
+
* Every key is ONE level deep on purpose — `runConfigGet` splits on a single dot,
|
|
61
|
+
* so `jobs.waitSeconds` resolves and `jobs.mirror.enabled` would not.
|
|
62
|
+
*/
|
|
63
|
+
export const JOBS_CONFIG_SET_KEYS = [
|
|
64
|
+
'jobs.enabled',
|
|
65
|
+
'jobs.waitSeconds',
|
|
66
|
+
'jobs.stallSeconds',
|
|
67
|
+
'jobs.persistLog',
|
|
68
|
+
'jobs.noticeSteer',
|
|
69
|
+
];
|
|
51
70
|
/**
|
|
52
71
|
* Config keys under `retry.` that `aragon config set` accepts
|
|
53
72
|
* (llm-api-retry-backoff §8.2).
|
|
@@ -166,6 +185,40 @@ export function applyLogConfigSet(key, value) {
|
|
|
166
185
|
return null;
|
|
167
186
|
}
|
|
168
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* Translate a `jobs.*` dotted key into a patch, or `null` for anything else.
|
|
190
|
+
*
|
|
191
|
+
* THE TEMPLATE IS `applyTeamConfigSet`, NOT an `applyBashConfigSet` — no such
|
|
192
|
+
* function exists, and `bash.*` is not `config set`-able to this day (RV-P2-8).
|
|
193
|
+
* Every numeric key routes through `clampJobsConfig` rather than a local
|
|
194
|
+
* coercion, so the caller echoes the STORED value and an out-of-range input is
|
|
195
|
+
* visible: `config set jobs.waitSeconds 1` prints `5`.
|
|
196
|
+
*
|
|
197
|
+
* The patch is a PARTIAL section, which is only safe because
|
|
198
|
+
* `updatePersistedConfig` deep-merges `jobs` — the sixth time that trap is
|
|
199
|
+
* recorded in `store.ts`.
|
|
200
|
+
*/
|
|
201
|
+
export function applyJobsConfigSet(key, value) {
|
|
202
|
+
const patch = (jobs) => ({ jobs });
|
|
203
|
+
const clampOne = (field) => {
|
|
204
|
+
const merged = clampJobsConfig({ ...DEFAULT_JOBS_CONFIG, [field]: value });
|
|
205
|
+
return { [field]: merged[field] };
|
|
206
|
+
};
|
|
207
|
+
switch (key) {
|
|
208
|
+
case 'jobs.enabled':
|
|
209
|
+
return patch({ enabled: isTrue(value) });
|
|
210
|
+
case 'jobs.waitSeconds':
|
|
211
|
+
return patch(clampOne('waitSeconds'));
|
|
212
|
+
case 'jobs.stallSeconds':
|
|
213
|
+
return patch(clampOne('stallSeconds'));
|
|
214
|
+
case 'jobs.persistLog':
|
|
215
|
+
return patch({ persistLog: isTrue(value) });
|
|
216
|
+
case 'jobs.noticeSteer':
|
|
217
|
+
return patch({ noticeSteer: isTrue(value) });
|
|
218
|
+
default:
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
169
222
|
/**
|
|
170
223
|
* Translate a `team.*` dotted key into a patch, or `null` for anything else.
|
|
171
224
|
*
|
package/dist/config/env.js
CHANGED
|
@@ -254,6 +254,44 @@ export function readEnvConfig() {
|
|
|
254
254
|
if (Object.keys(section).length > 0)
|
|
255
255
|
partial.bash = section;
|
|
256
256
|
}
|
|
257
|
+
const jobsEnabled = process.env.ARAGON_JOBS_ENABLED?.trim().toLowerCase();
|
|
258
|
+
const jobsWait = process.env.ARAGON_JOBS_WAIT_SECONDS?.trim();
|
|
259
|
+
const jobsStall = process.env.ARAGON_JOBS_STALL_SECONDS?.trim();
|
|
260
|
+
const jobsPersist = process.env.ARAGON_JOBS_PERSIST_LOG?.trim().toLowerCase();
|
|
261
|
+
const jobsSteer = process.env.ARAGON_JOBS_NOTICE_STEER?.trim().toLowerCase();
|
|
262
|
+
if ((jobsEnabled !== undefined && jobsEnabled.length > 0) ||
|
|
263
|
+
(jobsWait !== undefined && jobsWait.length > 0) ||
|
|
264
|
+
(jobsStall !== undefined && jobsStall.length > 0) ||
|
|
265
|
+
(jobsPersist !== undefined && jobsPersist.length > 0) ||
|
|
266
|
+
(jobsSteer !== undefined && jobsSteer.length > 0)) {
|
|
267
|
+
const section = {};
|
|
268
|
+
// ONE TRUTHINESS LADDER FOR ALL THREE BOOLEANS, because three slightly
|
|
269
|
+
// different spellings of "is this true" is how `ARAGON_JOBS_ENABLED=off`
|
|
270
|
+
// ends up enabling the feature.
|
|
271
|
+
const flag = (value) => value === '1' || value === 'true' || value === 'on' || value === 'yes';
|
|
272
|
+
if (jobsEnabled !== undefined && jobsEnabled.length > 0)
|
|
273
|
+
section.enabled = flag(jobsEnabled);
|
|
274
|
+
if (jobsPersist !== undefined && jobsPersist.length > 0)
|
|
275
|
+
section.persistLog = flag(jobsPersist);
|
|
276
|
+
if (jobsSteer !== undefined && jobsSteer.length > 0)
|
|
277
|
+
section.noticeSteer = flag(jobsSteer);
|
|
278
|
+
if (jobsWait !== undefined && jobsWait.length > 0) {
|
|
279
|
+
const parsed = Number.parseInt(jobsWait, 10);
|
|
280
|
+
// LEFT ABSENT WHEN UNPARSEABLE, so `clampJobsConfig` supplies the default
|
|
281
|
+
// rather than an env var that silently means 0.
|
|
282
|
+
if (Number.isFinite(parsed))
|
|
283
|
+
section.waitSeconds = parsed;
|
|
284
|
+
}
|
|
285
|
+
if (jobsStall !== undefined && jobsStall.length > 0) {
|
|
286
|
+
const parsed = Number.parseInt(jobsStall, 10);
|
|
287
|
+
if (Number.isFinite(parsed))
|
|
288
|
+
section.stallSeconds = parsed;
|
|
289
|
+
}
|
|
290
|
+
// A PARTIAL section, safe only because every consumer deep-merges it - the
|
|
291
|
+
// same contract `partial.skills` / `partial.log` / `partial.team` rely on.
|
|
292
|
+
if (Object.keys(section).length > 0)
|
|
293
|
+
partial.jobs = section;
|
|
294
|
+
}
|
|
257
295
|
const retry = process.env.ARAGON_RETRY?.trim().toLowerCase();
|
|
258
296
|
const retryMax = process.env.ARAGON_RETRY_MAX?.trim();
|
|
259
297
|
if ((retry !== undefined && retry.length > 0) || (retryMax !== undefined && retryMax.length > 0)) {
|
|
@@ -392,6 +430,10 @@ export function readEnvConfig() {
|
|
|
392
430
|
partial.diffRender =
|
|
393
431
|
diffRender === '1' || diffRender === 'true' || diffRender === 'on' || diffRender === 'yes';
|
|
394
432
|
}
|
|
433
|
+
// Invalid values are ignored, like every other ARAGON_* key.
|
|
434
|
+
const scrollMode = process.env.ARAGON_SCROLL_MODE?.trim().toLowerCase();
|
|
435
|
+
if (scrollMode === 'native' || scrollMode === 'app')
|
|
436
|
+
partial.scrollMode = scrollMode;
|
|
395
437
|
const syncOutput = process.env.ARAGON_SYNC_OUTPUT?.trim().toLowerCase();
|
|
396
438
|
if (syncOutput !== undefined && syncOutput.length > 0) {
|
|
397
439
|
partial.syncOutput =
|
package/dist/config/load.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* layers, `idleTimeout` is re-derived to be strictly ≥ `toolTimeout` so a
|
|
6
6
|
* legitimately long single tool run can never be watchdog-killed.
|
|
7
7
|
*/
|
|
8
|
-
import { type CliConfig } from './schema.js';
|
|
8
|
+
import { type CliConfig, type ScrollMode } from './schema.js';
|
|
9
9
|
import type { ModelRole } from './model-profiles.js';
|
|
10
10
|
export interface CliFlags {
|
|
11
11
|
provider?: string;
|
|
@@ -149,6 +149,8 @@ export interface CliFlags {
|
|
|
149
149
|
* all — i.e. the documented escape hatch would quietly un-set itself.
|
|
150
150
|
*/
|
|
151
151
|
diffRender?: boolean;
|
|
152
|
+
/** `--scroll-mode <native|app>`; already validated by `cli.tsx` (exit 2 on junk). */
|
|
153
|
+
scrollMode?: ScrollMode;
|
|
152
154
|
/** `--sync-output` ⇒ true, `--no-sync-output` ⇒ false, absent ⇒ undefined (§5.2). */
|
|
153
155
|
syncOutput?: boolean;
|
|
154
156
|
/**
|
package/dist/config/load.js
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* legitimately long single tool run can never be watchdog-killed.
|
|
7
7
|
*/
|
|
8
8
|
import process from 'node:process';
|
|
9
|
-
import {
|
|
9
|
+
import { JOB_WAIT_MARGIN_MS } from '../proc/limits.js';
|
|
10
|
+
import { DEFAULT_CONFIG, DEFAULT_SCROLL_MODE, IDLE_TIMEOUT_MARGIN_MS, clampAskRounds, clampDensity, clampHumanTimeout, clampCompactionConfig, clampLogConfig, clampFastConfig, clampRetryConfig, clampSkillsConfig, clampTeamConfig, clampTodoConfig, clampBashConfig, clampJobsConfig, clampUpdateConfig, clampTheme, clampThinkingLevel, clampContextWindow, clampMaxTokens, clampMaxRenderInterval, clampScrollMode, clampScrollResumeMs, clampTranscriptRetain, clampTranscriptWindow, coercePositiveInt, isAutoToken, isSkillsToolPolicyMode, legacyTeamCeilingPatch, parseThresholdInput, } from './schema.js';
|
|
10
11
|
import { loadDotenv, readEnvConfig, readEnvDisabledSkills } from './env.js';
|
|
11
12
|
import { readConfigFile, updatePersistedConfig } from './store.js';
|
|
12
13
|
import { projectModelProfiles, resolveModelProfileState, resolveModelRoleKey, } from './model-profile-resolution.js';
|
|
@@ -182,6 +183,23 @@ function resolveBashConfig(env, file) {
|
|
|
182
183
|
...(env.bash ?? {}),
|
|
183
184
|
});
|
|
184
185
|
}
|
|
186
|
+
/**
|
|
187
|
+
* Merge the `jobs` section across the three layers that supply it
|
|
188
|
+
* (defaults > file > env), long-task-jobs §6.5.
|
|
189
|
+
*
|
|
190
|
+
* THREE LAYERS, NOT FOUR, and that is the same decision `resolveBashConfig`
|
|
191
|
+
* records: both switches this section carries are already reachable from
|
|
192
|
+
* `config.json` (a per-machine preference) and from the environment (the channel
|
|
193
|
+
* a container entrypoint has). A fourth spelling would be a fourth place for the
|
|
194
|
+
* answer to differ.
|
|
195
|
+
*/
|
|
196
|
+
function resolveJobsConfig(env, file) {
|
|
197
|
+
return clampJobsConfig({
|
|
198
|
+
...DEFAULT_CONFIG.jobs,
|
|
199
|
+
...(file.jobs ?? {}),
|
|
200
|
+
...(env.jobs ?? {}),
|
|
201
|
+
});
|
|
202
|
+
}
|
|
185
203
|
/**
|
|
186
204
|
* Merge the `retry` section across all four layers (defaults › file › env ›
|
|
187
205
|
* flags), llm-api-retry-backoff §6.2.
|
|
@@ -334,6 +352,16 @@ function resolveDiffRender(flags, env, file) {
|
|
|
334
352
|
return file.diffRender;
|
|
335
353
|
return DEFAULT_CONFIG.diffRender;
|
|
336
354
|
}
|
|
355
|
+
/** flags > env > file > default, like `resolveDiffRender` (tui-native-terminal-scroll §6.1). */
|
|
356
|
+
function resolveScrollMode(flags, env, file) {
|
|
357
|
+
if (flags.scrollMode !== undefined)
|
|
358
|
+
return flags.scrollMode;
|
|
359
|
+
if (env.scrollMode !== undefined)
|
|
360
|
+
return env.scrollMode;
|
|
361
|
+
if (file.scrollMode !== undefined)
|
|
362
|
+
return clampScrollMode(file.scrollMode, DEFAULT_SCROLL_MODE);
|
|
363
|
+
return DEFAULT_SCROLL_MODE;
|
|
364
|
+
}
|
|
337
365
|
/** Same ladder for the DEC 2026 envelope (§5.1). */
|
|
338
366
|
function resolveSyncOutput(flags, env, file) {
|
|
339
367
|
if (flags.syncOutput !== undefined)
|
|
@@ -544,10 +572,32 @@ export function loadConfig(flags = {}, options = {}) {
|
|
|
544
572
|
const planModeMaxAskRounds = clampAskRounds(file.planModeMaxAskRounds, DEFAULT_CONFIG.planModeMaxAskRounds);
|
|
545
573
|
const planModeHumanTimeoutMs = clampHumanTimeout(file.planModeHumanTimeoutMs, DEFAULT_CONFIG.planModeHumanTimeoutMs);
|
|
546
574
|
const toolTimeoutMs = coercePositiveInt(pick(flags.toolTimeout, file.toolTimeoutMs), DEFAULT_CONFIG.toolTimeoutMs);
|
|
575
|
+
// THE `jobs` SECTION IS RESOLVED HERE, ABOVE THE TIMEOUT BLOCK, and the
|
|
576
|
+
// position is the whole of RV-P1-2. The design's first draft put the I-4 clamp
|
|
577
|
+
// next to the `idleTimeoutMs` invariant below, but `jobs` is parsed inside the
|
|
578
|
+
// big config literal a hundred lines further down — a reference to it here would
|
|
579
|
+
// be a use-before-declaration, and "resolve it later and patch the object" is
|
|
580
|
+
// how the invariant gets silently skipped. Resolving it first makes the clamp
|
|
581
|
+
// impossible to omit.
|
|
582
|
+
const jobs = resolveJobsConfig(env.partial, file);
|
|
547
583
|
let idleTimeoutMs = coercePositiveInt(pick(flags.idleTimeout, file.idleTimeoutMs), DEFAULT_CONFIG.idleTimeoutMs);
|
|
548
584
|
// Timeout invariant (R1): idle must be strictly greater than the tool ceiling
|
|
549
585
|
// so a long single tool run is never aborted by the idle watchdog.
|
|
550
586
|
idleTimeoutMs = Math.max(idleTimeoutMs, toolTimeoutMs + IDLE_TIMEOUT_MARGIN_MS);
|
|
587
|
+
// INVARIANT I-4 (long-task-jobs): a wait must fit inside the tool call that
|
|
588
|
+
// hosts it, `jobs.waitSeconds * 1000 + JOB_WAIT_MARGIN_MS <= toolTimeoutMs`.
|
|
589
|
+
//
|
|
590
|
+
// `Math.min`, AND THE DIRECTION IS THE POINT. The invariant above RAISES the
|
|
591
|
+
// idle window to accommodate the tool; this one LOWERS the wait to accommodate
|
|
592
|
+
// the tool. Copying `Math.max` here would produce a wait that outlives its own
|
|
593
|
+
// tool call — which is exactly the `Command timed out` failure the feature
|
|
594
|
+
// exists to remove, re-created by its own config check.
|
|
595
|
+
//
|
|
596
|
+
// With an operator-set ceiling below the margin the floor of 1s applies rather
|
|
597
|
+
// than a negative wait: the invariant wins over the minimum, because a wait
|
|
598
|
+
// longer than its host is worse than a short wait.
|
|
599
|
+
const waitCeilingSeconds = Math.floor((toolTimeoutMs - JOB_WAIT_MARGIN_MS) / 1000);
|
|
600
|
+
jobs.waitSeconds = Math.max(1, Math.min(jobs.waitSeconds, waitCeilingSeconds));
|
|
551
601
|
// Resolve API keys: config-file key wins over the provider env var.
|
|
552
602
|
const fileKeys = file.apiKeys ?? {};
|
|
553
603
|
const apiKeys = {};
|
|
@@ -638,6 +688,7 @@ export function loadConfig(flags = {}, options = {}) {
|
|
|
638
688
|
renderGovernor: resolveRenderGovernor(flags, env.partial, file),
|
|
639
689
|
maxRenderIntervalMs: clampMaxRenderInterval(pick(flags.maxRenderInterval, env.partial.maxRenderIntervalMs, file.maxRenderIntervalMs), DEFAULT_CONFIG.maxRenderIntervalMs),
|
|
640
690
|
diffRender: resolveDiffRender(flags, env.partial, file),
|
|
691
|
+
scrollMode: resolveScrollMode(flags, env.partial, file),
|
|
641
692
|
syncOutput: resolveSyncOutput(flags, env.partial, file),
|
|
642
693
|
confirmTools,
|
|
643
694
|
toolTimeoutMs,
|
|
@@ -676,6 +727,10 @@ export function loadConfig(flags = {}, options = {}) {
|
|
|
676
727
|
update: resolveUpdateConfig(flags, env.partial, file),
|
|
677
728
|
compaction: resolveCompactionConfig(flags, env.partial, file),
|
|
678
729
|
bash: resolveBashConfig(env.partial, file),
|
|
730
|
+
// The already-resolved-and-converged section from the timeout block above.
|
|
731
|
+
// NOT `resolveJobsConfig(...)` a second time: that would re-apply the
|
|
732
|
+
// un-converged `waitSeconds` and silently undo I-4.
|
|
733
|
+
jobs,
|
|
679
734
|
cwd,
|
|
680
735
|
color,
|
|
681
736
|
colorLevel,
|
|
@@ -719,6 +774,11 @@ function recordResolvedConfig(config) {
|
|
|
719
774
|
// trace — so "why did my failing call sit there for three minutes" (or
|
|
720
775
|
// "why did it not retry at all") has an answer after the fact.
|
|
721
776
|
retry: config.retry.enabled ? config.retry.maxRetries : 'off',
|
|
777
|
+
// The CONVERGED value, not the configured one: after I-4 they can differ, and
|
|
778
|
+
// "my 30s wait became 10s" is answerable only if the resolved number is
|
|
779
|
+
// recorded rather than the one on disk.
|
|
780
|
+
jobWaitSeconds: config.jobs.waitSeconds,
|
|
781
|
+
jobsEnabled: config.jobs.enabled,
|
|
722
782
|
hasApiKey: Boolean(config.apiKeys[config.provider]),
|
|
723
783
|
configPath: getConfigPath(),
|
|
724
784
|
});
|