@aragon-agent/cli 0.5.1 → 0.5.3
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 +117 -0
- package/README.md +155 -5
- package/dist/agent/controller.d.ts +51 -1
- package/dist/agent/controller.js +177 -2
- package/dist/agent/headless.d.ts +12 -0
- package/dist/agent/headless.js +41 -1
- package/dist/agent/reducer.d.ts +48 -0
- package/dist/agent/reducer.js +91 -2
- package/dist/agent/system-prompt.d.ts +17 -0
- package/dist/agent/system-prompt.js +4 -0
- package/dist/cli.js +131 -6
- package/dist/commands/builtins.js +82 -0
- package/dist/config/app-paths.d.ts +17 -0
- package/dist/config/app-paths.js +21 -0
- package/dist/config/cli-commands.d.ts +26 -2
- package/dist/config/cli-commands.js +63 -4
- package/dist/config/env.d.ts +2 -2
- package/dist/config/env.js +36 -2
- package/dist/config/history-commands.d.ts +20 -0
- package/dist/config/history-commands.js +71 -0
- package/dist/config/load.d.ts +15 -0
- package/dist/config/load.js +88 -5
- package/dist/config/migrate-state-out-of-config.d.ts +32 -0
- package/dist/config/migrate-state-out-of-config.js +85 -0
- package/dist/config/prompt-history.d.ts +86 -0
- package/dist/config/prompt-history.js +332 -0
- package/dist/config/schema.d.ts +125 -9
- package/dist/config/schema.js +90 -5
- package/dist/config/store.js +13 -4
- package/dist/config/ui-state.d.ts +52 -0
- package/dist/config/ui-state.js +149 -0
- package/dist/input/mouse-events.d.ts +61 -0
- package/dist/input/mouse-events.js +153 -0
- package/dist/input/stdin-mouse-filter.d.ts +42 -0
- package/dist/input/stdin-mouse-filter.js +153 -0
- package/dist/logging/install.d.ts +85 -0
- package/dist/logging/install.js +94 -0
- package/dist/logging/logger.d.ts +1 -1
- package/dist/session/persist.d.ts +16 -0
- package/dist/session/persist.js +19 -1
- package/dist/team/activity.d.ts +67 -0
- package/dist/team/activity.js +130 -0
- package/dist/team/bus.d.ts +190 -0
- package/dist/team/bus.js +366 -0
- package/dist/team/comm-tools.d.ts +61 -0
- package/dist/team/comm-tools.js +221 -0
- package/dist/team/human-queue.d.ts +55 -0
- package/dist/team/human-queue.js +72 -0
- package/dist/team/limits.d.ts +123 -0
- package/dist/team/limits.js +128 -0
- package/dist/team/normalize.d.ts +39 -0
- package/dist/team/normalize.js +106 -0
- package/dist/team/panel-rows.d.ts +48 -0
- package/dist/team/panel-rows.js +75 -0
- package/dist/team/prompt.d.ts +49 -0
- package/dist/team/prompt.js +87 -0
- package/dist/team/report.d.ts +58 -0
- package/dist/team/report.js +205 -0
- package/dist/team/retry.d.ts +48 -0
- package/dist/team/retry.js +56 -0
- package/dist/team/runtime.d.ts +122 -0
- package/dist/team/runtime.js +406 -0
- package/dist/team/subagent.d.ts +90 -0
- package/dist/team/subagent.js +294 -0
- package/dist/team/task-tool.d.ts +47 -0
- package/dist/team/task-tool.js +118 -0
- package/dist/team/types.d.ts +172 -0
- package/dist/team/types.js +13 -0
- package/dist/tools/index.d.ts +48 -2
- package/dist/tools/index.js +26 -1
- package/dist/ui/App.d.ts +9 -1
- package/dist/ui/App.js +140 -21
- package/dist/ui/Composer.d.ts +6 -1
- package/dist/ui/Composer.js +2 -2
- package/dist/ui/PromptInput.d.ts +15 -1
- package/dist/ui/PromptInput.js +38 -1
- package/dist/ui/StatusBar.d.ts +21 -0
- package/dist/ui/StatusBar.js +5 -2
- package/dist/ui/TeamPanel.d.ts +57 -0
- package/dist/ui/TeamPanel.js +134 -0
- package/dist/ui/Transcript.js +10 -0
- package/dist/ui/entries/TeamCard.d.ts +31 -0
- package/dist/ui/entries/TeamCard.js +39 -0
- package/dist/ui/glyphs.d.ts +22 -0
- package/dist/ui/glyphs.js +10 -0
- package/dist/ui/layout/AppShell.d.ts +20 -1
- package/dist/ui/layout/AppShell.js +39 -4
- package/dist/ui/layout/ScrollIndicator.d.ts +31 -0
- package/dist/ui/layout/ScrollIndicator.js +15 -0
- package/dist/ui/layout/ScrollViewport.d.ts +8 -2
- package/dist/ui/layout/ScrollViewport.js +24 -4
- package/dist/ui/layout/regions.d.ts +55 -0
- package/dist/ui/layout/regions.js +52 -0
- package/dist/ui/layout/scroll-indicator.d.ts +27 -0
- package/dist/ui/layout/scroll-indicator.js +35 -0
- package/dist/ui/layout/scroll.d.ts +9 -0
- package/dist/ui/layout/scroll.js +23 -0
- package/dist/ui/overlays/HelpOverlay.js +14 -0
- package/dist/ui/screen.d.ts +14 -1
- package/dist/ui/screen.js +37 -3
- package/dist/ui/transcript-text.js +20 -0
- package/dist/ui/use-startup-notices.d.ts +11 -1
- package/dist/ui/use-startup-notices.js +29 -2
- package/dist/ui/use-wheel-routing.d.ts +68 -0
- package/dist/ui/use-wheel-routing.js +139 -0
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -21,8 +21,10 @@ import { loadConfig } from './config/load.js';
|
|
|
21
21
|
import { getConfigPath, readConfigFile, updatePersistedConfig } from './config/store.js';
|
|
22
22
|
import { formatMigrationNotice, migrateLegacyState } from './config/migrate-legacy-state.js';
|
|
23
23
|
import { formatHomeMigrationNotice, migrateToHome } from './config/migrate-home.js';
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
24
|
+
import { migrateStateOutOfConfig } from './config/migrate-state-out-of-config.js';
|
|
25
|
+
import { runHistoryCommand } from './config/history-commands.js';
|
|
26
|
+
import { LOG_CONFIG_SET_KEYS, TEAM_CONFIG_SET_KEYS, applyLogConfigSet, applyTeamConfigSet, runConfigEdit, runConfigGet, runConfigHome, runConfigList, warnIfRedactionDisabled, } from './config/cli-commands.js';
|
|
27
|
+
import { attachAgentEvents, attachTeamEvents, installLogging, setScreenRestore, setSignalTerminator, } from './logging/install.js';
|
|
26
28
|
import { getLogger } from './logging/logger.js';
|
|
27
29
|
import { runLogsCommand } from './logging/cli-commands.js';
|
|
28
30
|
import { ADAPTER_PROVIDERS, clampDensity, clampTheme, clampThinkingLevel, clampAskRounds, clampHumanTimeout, clampSkillsIntegrity, clampSkillsToolPolicy, clampTranscriptWindow, coerceMaxTokens, coercePositiveInt, isAdapterProvider, DEFAULT_PLAN_HUMAN_TIMEOUT_MS, DEFAULT_PLAN_MAX_ASK_ROUNDS, DEFAULT_SKILLS_CONFIG, } from './config/schema.js';
|
|
@@ -32,6 +34,7 @@ import { runSkillsCommand } from './skills/cli-commands.js';
|
|
|
32
34
|
import { runHeadless } from './agent/headless.js';
|
|
33
35
|
import { App } from './ui/App.js';
|
|
34
36
|
import { decideRenderMode } from './ui/layout/frame.js';
|
|
37
|
+
import { tryCreateMouseFilter } from './input/stdin-mouse-filter.js';
|
|
35
38
|
import { enterAltScreen, writeExitTranscript } from './ui/screen.js';
|
|
36
39
|
import { readExitSnapshot } from './ui/exit-snapshot.js';
|
|
37
40
|
import { renderTranscriptText } from './ui/transcript-text.js';
|
|
@@ -70,11 +73,19 @@ function toFlags(opts) {
|
|
|
70
73
|
exitTranscript: opts.exitTranscript,
|
|
71
74
|
compact: opts.compact,
|
|
72
75
|
hints: opts.hints,
|
|
76
|
+
// Without this line the flag never reaches `CliFlags` and `--no-mouse` is
|
|
77
|
+
// inert — a failure with no compile error and no runtime error (P1-2).
|
|
78
|
+
mouse: opts.mouse,
|
|
73
79
|
skills: opts.skills,
|
|
74
80
|
skill: opts.skill,
|
|
75
81
|
skillsYes: opts.skillsYes,
|
|
76
82
|
skillToolPolicy: opts.skillToolPolicy,
|
|
77
83
|
plan: opts.plan,
|
|
84
|
+
// These two lines are the same failure the `mouse` note above records, one
|
|
85
|
+
// feature later (P1-4): without them `--no-team` and `--team-max` never
|
|
86
|
+
// reach `CliFlags` and are inert, with no compile error and no runtime error.
|
|
87
|
+
team: opts.team,
|
|
88
|
+
teamMax: opts.teamMax,
|
|
78
89
|
// `--verbose` is just a shorthand for `--log-level debug`; an explicit
|
|
79
90
|
// `--log-level` still wins, so the two can be combined without surprise.
|
|
80
91
|
logLevel: opts.logLevel ?? (opts.verbose ? 'debug' : undefined),
|
|
@@ -162,8 +173,38 @@ function runInteractive(flags, extras = {}) {
|
|
|
162
173
|
const logger = getLogger();
|
|
163
174
|
logger.info('cli', 'run_interactive', { mode, provider: config.provider, model: config.model });
|
|
164
175
|
attachAgentEvents(logger, controller);
|
|
176
|
+
// The team stream is CLI-local (D-10), so `attachAgentEvents` cannot see it.
|
|
177
|
+
// Without this a dispatch leaves NO trace in the log file at all — the wrong
|
|
178
|
+
// outcome for the one feature here that runs five agents the user cannot
|
|
179
|
+
// watch (§3.13 / P1-2 / R-19).
|
|
180
|
+
attachTeamEvents(logger, controller);
|
|
165
181
|
let screen = null;
|
|
166
182
|
let replayed = false;
|
|
183
|
+
// --- Mouse wheel region routing (mouse-wheel-region-routing §4.2). -------
|
|
184
|
+
//
|
|
185
|
+
// BUILD THE FILTER FIRST, THEN ENABLE REPORTING. The order is invariant I-8
|
|
186
|
+
// and it is not negotiable: `\x1b[?1000h` tells the terminal to send SGR
|
|
187
|
+
// reports, and Ink 5.2.1 has no mouse branch at all, so any report that
|
|
188
|
+
// reaches its key parser is INSERTED INTO THE USER'S MESSAGE (`[` is 0x5B, so
|
|
189
|
+
// `PromptInput`'s `isControlSeq` guard passes it). A window in which
|
|
190
|
+
// reporting is on and Ink holds the raw stream is not a degraded feature — it
|
|
191
|
+
// is every click typing `[<0;12;5M` into the composer, strictly worse than
|
|
192
|
+
// the bug being fixed.
|
|
193
|
+
//
|
|
194
|
+
// `tryCreateMouseFilter` returns `null` rather than throwing, so a
|
|
195
|
+
// construction failure degrades to "the wheel is inert" (rung 3 of the
|
|
196
|
+
// fail-safe ladder), never to "reporting on, no filter".
|
|
197
|
+
// BOTH streams have to be terminals. `stdout.isTTY` is what decides whether
|
|
198
|
+
// `enterAltScreen` writes anything at all, but the reports come back on
|
|
199
|
+
// STDIN, so a TTY stdout with a piped stdin (`aragon config < file`, which
|
|
200
|
+
// reaches `runInteractive` without the stdin check the default action makes)
|
|
201
|
+
// would wrap a stream no report can ever arrive on and ask the terminal to
|
|
202
|
+
// start reporting anyway. §4.2's "non-TTY ⇒ no filter" covers both ends.
|
|
203
|
+
const wantMouse = mode === 'fullscreen' && config.mouse && !!process.stdout.isTTY && !!process.stdin.isTTY;
|
|
204
|
+
const mouseFilter = wantMouse
|
|
205
|
+
? tryCreateMouseFilter(process.stdin, (reason) => logger.warn('cli', 'mouse_filter_failed', { reason }))
|
|
206
|
+
: null;
|
|
207
|
+
const disposeMouse = () => mouseFilter?.dispose();
|
|
167
208
|
const replayTranscript = () => {
|
|
168
209
|
if (replayed || mode !== 'fullscreen' || !config.exitTranscript)
|
|
169
210
|
return;
|
|
@@ -181,7 +222,10 @@ function runInteractive(flags, extras = {}) {
|
|
|
181
222
|
}));
|
|
182
223
|
};
|
|
183
224
|
if (mode === 'fullscreen') {
|
|
184
|
-
|
|
225
|
+
// `mouse` here means "a filter is installed", NOT "the user wants mouse
|
|
226
|
+
// support" — see `AltScreenOptions`. `screen.ts` must never derive it for
|
|
227
|
+
// itself, or a later refactor re-opens the gap I-8 closes.
|
|
228
|
+
screen = enterAltScreen(process.stdout, { mouse: mouseFilter !== null });
|
|
185
229
|
const restore = () => screen?.restore();
|
|
186
230
|
// Four idempotent restore paths (§4.4). The signal hook is NOT redundant:
|
|
187
231
|
// - `waitUntilExit().then()` is a microtask and may never be reached when
|
|
@@ -205,20 +249,43 @@ function runInteractive(flags, extras = {}) {
|
|
|
205
249
|
process.exit(128 + signo);
|
|
206
250
|
});
|
|
207
251
|
}
|
|
208
|
-
const instance = render(_jsx(App, { controller: controller, version: VERSION, mode: mode, confirmBridge: confirmBridge, humanInputBridge: humanInputBridge, initialPrompt: extras.initialPrompt, initialOverlay: extras.initialOverlay }),
|
|
252
|
+
const instance = render(_jsx(App, { controller: controller, version: VERSION, mode: mode, confirmBridge: confirmBridge, humanInputBridge: humanInputBridge, initialPrompt: extras.initialPrompt, initialOverlay: extras.initialOverlay, mouseSource: mouseFilter?.source }),
|
|
209
253
|
// Full-screen owns console.* itself (I-4): Ink's patchConsole writes
|
|
210
254
|
// straight to stdout and permanently shifts the fixed frame's accounting.
|
|
211
|
-
{
|
|
255
|
+
{
|
|
256
|
+
exitOnCtrlC: false,
|
|
257
|
+
patchConsole: mode === 'inline',
|
|
258
|
+
// I-5: with mouse support off the REAL stdin is handed over unwrapped, so
|
|
259
|
+
// every existing path is provably unchanged.
|
|
260
|
+
stdin: mouseFilter?.stdin ?? process.stdin,
|
|
261
|
+
});
|
|
212
262
|
// The return value MUST be captured — `waitUntilExit()` is the normal-exit
|
|
213
|
-
// restore path and it cannot be registered otherwise.
|
|
263
|
+
// restore path and it cannot be registered otherwise. `dispose()` runs on
|
|
264
|
+
// BOTH branches (I-7): a live `'data'` listener on stdin keeps the event loop
|
|
265
|
+
// alive and `aragon` would never return to the shell. The signal path does
|
|
266
|
+
// not need it — it calls `process.exit()`.
|
|
267
|
+
//
|
|
268
|
+
// RESTORE BEFORE DISPOSE. `restore()` writes `?1000l`, so the terminal stops
|
|
269
|
+
// reporting first and anything already in flight is still consumed by a live
|
|
270
|
+
// filter. Disposing first leaves a window — narrow, but real — in which the
|
|
271
|
+
// terminal is still reporting and nothing is draining stdin, so a notch taken
|
|
272
|
+
// in that instant survives in the OS buffer and is handed to the shell as
|
|
273
|
+
// `[<64;12;5M` on exit. That is the I-2 failure mode, just smaller.
|
|
214
274
|
void instance
|
|
215
275
|
.waitUntilExit()
|
|
216
276
|
.then(() => {
|
|
217
277
|
screen?.restore();
|
|
278
|
+
disposeMouse();
|
|
279
|
+
// Orphaned subagents survive `Ctrl+C` otherwise, and a live child holds
|
|
280
|
+
// the event loop open so `aragon` never returns to the shell (R-15). The
|
|
281
|
+
// App's unmount cleanup also calls this; `dispose()` is idempotent.
|
|
282
|
+
controller.dispose();
|
|
218
283
|
replayTranscript();
|
|
219
284
|
})
|
|
220
285
|
.catch(() => {
|
|
221
286
|
screen?.restore();
|
|
287
|
+
disposeMouse();
|
|
288
|
+
controller.dispose();
|
|
222
289
|
});
|
|
223
290
|
}
|
|
224
291
|
async function runOneShot(flags, prompt, quiet) {
|
|
@@ -232,6 +299,11 @@ async function runOneShot(flags, prompt, quiet) {
|
|
|
232
299
|
// toast channel `App.tsx` uses does not exist here.
|
|
233
300
|
logger.onFailure((reason) => process.stderr.write(`[log] ${reason}\n`));
|
|
234
301
|
attachAgentEvents(logger, controller);
|
|
302
|
+
// The team stream is CLI-local (D-10), so `attachAgentEvents` cannot see it.
|
|
303
|
+
// Without this a dispatch leaves NO trace in the log file at all — the wrong
|
|
304
|
+
// outcome for the one feature here that runs five agents the user cannot
|
|
305
|
+
// watch (§3.13 / P1-2 / R-19).
|
|
306
|
+
attachTeamEvents(logger, controller);
|
|
235
307
|
const untrusted = controller.getSkillService().untrustedDirs();
|
|
236
308
|
for (const dir of untrusted) {
|
|
237
309
|
process.stderr.write(`[skills] project skills in ${dir} skipped (untrusted). Run: aragon skills trust ${dir}\n`);
|
|
@@ -299,6 +371,12 @@ const CONFIG_SET_KEYS = new Set([
|
|
|
299
371
|
// flags and never persist -- inconsistent with every other config field.
|
|
300
372
|
'density',
|
|
301
373
|
'hints',
|
|
374
|
+
// MEMBERSHIP HERE ONLY DECIDES THAT THE KEY IS NOT REJECTED. The `switch`
|
|
375
|
+
// below is what builds the patch, and a key present here but absent there
|
|
376
|
+
// falls through every case, writes nothing, and still prints
|
|
377
|
+
// `Set mouse = false` (P1-2) -- exactly what happened to `density`/`hints`.
|
|
378
|
+
'mouse',
|
|
379
|
+
'historyEnabled',
|
|
302
380
|
'planModeDefault',
|
|
303
381
|
'planModeMaxAskRounds',
|
|
304
382
|
'planModeHumanTimeoutMs',
|
|
@@ -313,6 +391,11 @@ const CONFIG_SET_KEYS = new Set([
|
|
|
313
391
|
// `log.*` keys are handled by `applyLogConfigSet` below, NOT by the switch:
|
|
314
392
|
// that switch is already past this package's complexity ceiling.
|
|
315
393
|
...LOG_CONFIG_SET_KEYS,
|
|
394
|
+
// `team.*` keys are handled by `applyTeamConfigSet`, for the same reason
|
|
395
|
+
// `log.*` are: the switch below is already past this package's complexity
|
|
396
|
+
// ceiling, and a key listed here but forgotten there writes nothing while
|
|
397
|
+
// still printing `Set team.enabled = false`.
|
|
398
|
+
...TEAM_CONFIG_SET_KEYS,
|
|
316
399
|
]);
|
|
317
400
|
function runConfigSet(key, value) {
|
|
318
401
|
if (!CONFIG_SET_KEYS.has(key)) {
|
|
@@ -327,6 +410,16 @@ function runConfigSet(key, value) {
|
|
|
327
410
|
process.stdout.write(`Set ${key} = ${value}\n`);
|
|
328
411
|
return;
|
|
329
412
|
}
|
|
413
|
+
const teamPatch = applyTeamConfigSet(key, value);
|
|
414
|
+
if (teamPatch) {
|
|
415
|
+
// The written value is echoed from the CLAMPED result rather than from the
|
|
416
|
+
// input, so `config set team.maxSubagents 40` says 10 instead of silently
|
|
417
|
+
// reporting a number that was never stored (R-g).
|
|
418
|
+
const merged = updatePersistedConfig(teamPatch);
|
|
419
|
+
const field = key.slice('team.'.length);
|
|
420
|
+
process.stdout.write(`Set ${key} = ${String(merged.team[field])}\n`);
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
330
423
|
const patch = {};
|
|
331
424
|
switch (key) {
|
|
332
425
|
case 'provider':
|
|
@@ -371,6 +464,14 @@ function runConfigSet(key, value) {
|
|
|
371
464
|
case 'hints':
|
|
372
465
|
patch.hints = value === 'true' || value === '1';
|
|
373
466
|
break;
|
|
467
|
+
case 'mouse':
|
|
468
|
+
patch.mouse = value === 'true' || value === '1';
|
|
469
|
+
break;
|
|
470
|
+
// Turning this off stops new prompts being recorded; it does NOT delete
|
|
471
|
+
// what is already in `prompt-history.jsonl` (that is `aragon history clear`).
|
|
472
|
+
case 'historyEnabled':
|
|
473
|
+
patch.historyEnabled = value === 'true' || value === '1';
|
|
474
|
+
break;
|
|
374
475
|
// Clamped rather than rejected, for the same reason as `theme` and
|
|
375
476
|
// `skills.integrity`: hardening only the READ path leaves a bad value on
|
|
376
477
|
// disk that reverts to the default on every launch, which presents as
|
|
@@ -452,6 +553,9 @@ function buildProgram() {
|
|
|
452
553
|
.option('--no-compact', 'Comfortable transcript density')
|
|
453
554
|
.option('--hints', 'Always show the composer hint row')
|
|
454
555
|
.option('--no-hints', 'Hide the composer hint row')
|
|
556
|
+
// Positive form first, same tri-state reason as `--fullscreen` above.
|
|
557
|
+
.option('--mouse', 'Wheel scrolls the region under the pointer (full-screen mode)')
|
|
558
|
+
.option('--no-mouse', 'Leave the mouse to the terminal (the wheel does nothing)')
|
|
455
559
|
// Declaring `--fullscreen` BEFORE `--no-fullscreen` keeps the default
|
|
456
560
|
// `undefined` instead of `true`, which is what makes the tri-state work:
|
|
457
561
|
// "no opinion" must stay distinguishable from "force it".
|
|
@@ -464,6 +568,13 @@ function buildProgram() {
|
|
|
464
568
|
// config layer win when neither flag was given.
|
|
465
569
|
.option('--plan', 'Start the session in PLAN mode (read-only research + review)')
|
|
466
570
|
.option('--no-plan', 'Start the session in BUILD mode (overrides planModeDefault)')
|
|
571
|
+
// BOTH FORMS, positive first, and the pair is mandatory (team-subagents
|
|
572
|
+
// §4.4 / P1-4). Declaring only `--no-team` would make commander default
|
|
573
|
+
// `opts.team` to `true`, at which point the flag is indistinguishable from
|
|
574
|
+
// its own default and silently overrides `config.json` on every run.
|
|
575
|
+
.option('--team', 'Enable team subagents for this session (the default)')
|
|
576
|
+
.option('--no-team', 'Disable team subagents (the task tool is not registered at all)')
|
|
577
|
+
.option('--team-max <n>', 'Max subagents per dispatch for this run (1-10)')
|
|
467
578
|
.option('--no-skills', 'Disable the skill system entirely (no catalog, no skill tools)')
|
|
468
579
|
.option('--skill <name>', 'Force-load a skill at Level 2 for this run (repeatable)', (value, previous = []) => [...previous, value])
|
|
469
580
|
.option('--skills-yes', 'Approve skill installs for this run (CI / headless)')
|
|
@@ -557,6 +668,16 @@ function buildProgram() {
|
|
|
557
668
|
.action(async (subcommand, opts) => {
|
|
558
669
|
process.exitCode = await runLogsCommand(subcommand ?? 'path', opts, toFlags(program.opts()));
|
|
559
670
|
});
|
|
671
|
+
// aragon history <sub> [-n N] [--json] [--yes]
|
|
672
|
+
program
|
|
673
|
+
.command('history [subcommand]')
|
|
674
|
+
.description('Inspect the prompt history: path | list | clear')
|
|
675
|
+
.option('-n, --lines <n>', 'Entries for `history list` (default 20)')
|
|
676
|
+
.option('--json', 'Print raw entries instead of rendered lines')
|
|
677
|
+
.option('--yes', 'Confirm destructive actions (required by `history clear`)')
|
|
678
|
+
.action((subcommand, opts) => {
|
|
679
|
+
process.exitCode = runHistoryCommand(subcommand ?? 'path', opts);
|
|
680
|
+
});
|
|
560
681
|
// aragon skills <sub> [arg] [--scope] [--name] [--description] [--yes] [--json]
|
|
561
682
|
program
|
|
562
683
|
.command('skills [subcommand] [argument]')
|
|
@@ -605,6 +726,10 @@ async function main() {
|
|
|
605
726
|
// may just have created. What ① and ② did is replayed from `pending` rather
|
|
606
727
|
// than lost.
|
|
607
728
|
installLogging({ pending: migrationNotes(legacy, home) });
|
|
729
|
+
// ③.5 — after ③ because it writes a log record, and before anything that
|
|
730
|
+
// resolves config because every `loadConfig()` from here on must already see
|
|
731
|
+
// the post-migration world. Never throws, never prints (D-8).
|
|
732
|
+
migrateStateOutOfConfig();
|
|
608
733
|
// An unparseable config.json still falls back to defaults — the CLI has to
|
|
609
734
|
// start — but it must not do so in silence, or a stray comma presents as
|
|
610
735
|
// "every setting I ever chose is gone". stderr, never stdout, so
|
|
@@ -90,6 +90,88 @@ const COMMANDS = [
|
|
|
90
90
|
ctx.applyAgentMode(target);
|
|
91
91
|
},
|
|
92
92
|
},
|
|
93
|
+
{
|
|
94
|
+
name: 'team',
|
|
95
|
+
description: 'Team subagents: status | on | off | max <n>',
|
|
96
|
+
/**
|
|
97
|
+
* `/team on` CANNOT turn team mode on in a session that started without it,
|
|
98
|
+
* and this command must not pretend otherwise (§4.5 / D-17 / P0-2).
|
|
99
|
+
*
|
|
100
|
+
* `controller.ts` builds the tool array ONCE and its own comment forbids
|
|
101
|
+
* rebuilding it: `Agent.setTools()` mutates the live `ToolRegistry`, and
|
|
102
|
+
* `submit_plan` flips the session mode from INSIDE a tool execution, so a
|
|
103
|
+
* rebuild at that moment would mutate the registry mid-iteration. But
|
|
104
|
+
* `rebuildSystemPrompt()` IS a live path — so the naive reading of `/team on`
|
|
105
|
+
* (flip a flag, re-splice `<team_mode>`) produces a session where the model
|
|
106
|
+
* is told it has a `task` tool that was never registered. Every call comes
|
|
107
|
+
* back "unknown tool", and the model has no way to discover why.
|
|
108
|
+
*
|
|
109
|
+
* So the answer splits by what was decided at construction, and the honest
|
|
110
|
+
* branch — persist it, say the session is unchanged — is the whole point.
|
|
111
|
+
*/
|
|
112
|
+
run: (ctx) => {
|
|
113
|
+
const arg = ctx.args.trim().toLowerCase();
|
|
114
|
+
const [verb, value] = arg.split(/\s+/, 2);
|
|
115
|
+
const controller = ctx.controller;
|
|
116
|
+
// Refused mid-dispatch for the reason `/reload` already records: half a
|
|
117
|
+
// dispatch under one ceiling and half under another produces a result
|
|
118
|
+
// nothing can afterwards explain.
|
|
119
|
+
if (verb && verb !== 'status' && controller.isTeamBusy()) {
|
|
120
|
+
ctx.notify('warn', 'Cannot change team settings mid-dispatch.');
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (!verb || verb === 'status') {
|
|
124
|
+
const cfg = controller.getTeamConfig();
|
|
125
|
+
const state = !controller.isTeamRegistered()
|
|
126
|
+
? 'off for this session (started with --no-team)'
|
|
127
|
+
: controller.isTeamEnabled()
|
|
128
|
+
? 'on'
|
|
129
|
+
: 'off';
|
|
130
|
+
const snapshot = controller.getTeamSnapshot();
|
|
131
|
+
const roster = snapshot
|
|
132
|
+
? `\nRunning: ${snapshot.runs.map((r) => `${r.label} (${r.phase})`).join(', ')}`
|
|
133
|
+
: '';
|
|
134
|
+
ctx.notify('info', `Team mode: ${state}. Max subagents: ${cfg.maxSubagents}, ` +
|
|
135
|
+
`concurrent: ${cfg.maxConcurrent}.${roster}`);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (verb === 'on' || verb === 'off') {
|
|
139
|
+
const enabled = verb === 'on';
|
|
140
|
+
// A PARTIAL `team` section, safe only because `updatePersistedConfig`
|
|
141
|
+
// deep-merges it — the same contract `/skills disable` relies on.
|
|
142
|
+
ctx.persistConfig({ team: { enabled } });
|
|
143
|
+
if (!controller.isTeamRegistered()) {
|
|
144
|
+
// The bottom-right cell of §4.5's table. It is honest, it is one
|
|
145
|
+
// sentence, and it keeps AC-11 intact: a `--no-team` session never
|
|
146
|
+
// grows a wrapper or an entry, whatever the user types afterwards.
|
|
147
|
+
ctx.notify('info', enabled
|
|
148
|
+
? 'Team mode is off for this session (started with --no-team). Saved for next launch.'
|
|
149
|
+
: 'Team mode is already off for this session. Saved for next launch.');
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
controller.setTeamEnabled(enabled);
|
|
153
|
+
ctx.toast('success', `Team mode ${enabled ? 'on' : 'off'}.`);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (verb === 'max') {
|
|
157
|
+
const n = Number.parseInt(value ?? '', 10);
|
|
158
|
+
if (!Number.isFinite(n) || n <= 0) {
|
|
159
|
+
ctx.notify('warn', 'Usage: /team max <n>');
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
// Clamped by `setTeamConfig` and again on the persist path, so a value
|
|
163
|
+
// above the requirement's ceiling can reach neither the runtime nor the
|
|
164
|
+
// file (R-g).
|
|
165
|
+
const applied = controller.setTeamConfig({ maxSubagents: n });
|
|
166
|
+
ctx.persistConfig({
|
|
167
|
+
team: { maxSubagents: applied.maxSubagents },
|
|
168
|
+
});
|
|
169
|
+
ctx.toast('success', `Max subagents set to ${applied.maxSubagents}.`);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
ctx.notify('warn', `Unknown argument "${verb}" - use /team [status|on|off|max <n>].`);
|
|
173
|
+
},
|
|
174
|
+
},
|
|
93
175
|
{
|
|
94
176
|
name: 'expand',
|
|
95
177
|
description: 'Expand / collapse a recent tool card (default: last)',
|
|
@@ -12,11 +12,18 @@
|
|
|
12
12
|
* ~/.aragon-agent/
|
|
13
13
|
* ├── config.json persisted config (0600 on POSIX)
|
|
14
14
|
* ├── config.json.bak `aragon config edit` pre-edit backup
|
|
15
|
+
* ├── prompt-history.jsonl submitted prompts, append-only (0600 on POSIX)
|
|
16
|
+
* ├── state.json UI bookkeeping scalars (0600 on POSIX)
|
|
15
17
|
* ├── logs/ JSONL logs (0700 on POSIX)
|
|
16
18
|
* ├── sessions/ /save · /resume
|
|
17
19
|
* ├── skills/ `aragon skills install` target
|
|
18
20
|
* └── skill-usage.json
|
|
19
21
|
*
|
|
22
|
+
* The two files added by config-state-separation are DIRECT CHILDREN of the
|
|
23
|
+
* root, siblings of `skill-usage.json` rather than children of any directory
|
|
24
|
+
* here: `logs/`, `sessions/` and `skills/` are all scan roots, and every extra
|
|
25
|
+
* file inside one is another thing each walk has to recognise and skip.
|
|
26
|
+
*
|
|
20
27
|
* The same path on all three platforms, deliberately. Claude Code, git and npm
|
|
21
28
|
* all put their dot-directory in the user's home; one path means documentation,
|
|
22
29
|
* support scripts and community answers have exactly one thing to say. A user
|
|
@@ -62,6 +69,16 @@ export declare function getHomeRoot(): string;
|
|
|
62
69
|
export declare function getConfigPath(): string;
|
|
63
70
|
/** Written by `aragon config edit` before spawning the editor. */
|
|
64
71
|
export declare function getConfigBackupPath(): string;
|
|
72
|
+
/**
|
|
73
|
+
* `<home>/prompt-history.jsonl` — the prompts the user submitted, one JSON
|
|
74
|
+
* object per line. Deliberately NOT under `logs/`: that directory is governed
|
|
75
|
+
* by `log.toFile`, `log.maxFiles` and `aragon logs clear`, and turning
|
|
76
|
+
* diagnostics off or reclaiming disk must not also delete the user's `↑`
|
|
77
|
+
* recall (config-state-separation D-1).
|
|
78
|
+
*/
|
|
79
|
+
export declare function getPromptHistoryPath(): string;
|
|
80
|
+
/** `<home>/state.json` — UI bookkeeping scalars only; see `ui-state.ts`. */
|
|
81
|
+
export declare function getUiStatePath(): string;
|
|
65
82
|
export declare function getLogsDir(): string;
|
|
66
83
|
export declare function getSessionsDir(): string;
|
|
67
84
|
/** `<home>` — parent of `skills/`, and where sidecar state such as usage lives. */
|
package/dist/config/app-paths.js
CHANGED
|
@@ -12,11 +12,18 @@
|
|
|
12
12
|
* ~/.aragon-agent/
|
|
13
13
|
* ├── config.json persisted config (0600 on POSIX)
|
|
14
14
|
* ├── config.json.bak `aragon config edit` pre-edit backup
|
|
15
|
+
* ├── prompt-history.jsonl submitted prompts, append-only (0600 on POSIX)
|
|
16
|
+
* ├── state.json UI bookkeeping scalars (0600 on POSIX)
|
|
15
17
|
* ├── logs/ JSONL logs (0700 on POSIX)
|
|
16
18
|
* ├── sessions/ /save · /resume
|
|
17
19
|
* ├── skills/ `aragon skills install` target
|
|
18
20
|
* └── skill-usage.json
|
|
19
21
|
*
|
|
22
|
+
* The two files added by config-state-separation are DIRECT CHILDREN of the
|
|
23
|
+
* root, siblings of `skill-usage.json` rather than children of any directory
|
|
24
|
+
* here: `logs/`, `sessions/` and `skills/` are all scan roots, and every extra
|
|
25
|
+
* file inside one is another thing each walk has to recognise and skip.
|
|
26
|
+
*
|
|
20
27
|
* The same path on all three platforms, deliberately. Claude Code, git and npm
|
|
21
28
|
* all put their dot-directory in the user's home; one path means documentation,
|
|
22
29
|
* support scripts and community answers have exactly one thing to say. A user
|
|
@@ -119,6 +126,20 @@ export function getConfigPath() {
|
|
|
119
126
|
export function getConfigBackupPath() {
|
|
120
127
|
return join(resolution.root, 'config.json.bak');
|
|
121
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* `<home>/prompt-history.jsonl` — the prompts the user submitted, one JSON
|
|
131
|
+
* object per line. Deliberately NOT under `logs/`: that directory is governed
|
|
132
|
+
* by `log.toFile`, `log.maxFiles` and `aragon logs clear`, and turning
|
|
133
|
+
* diagnostics off or reclaiming disk must not also delete the user's `↑`
|
|
134
|
+
* recall (config-state-separation D-1).
|
|
135
|
+
*/
|
|
136
|
+
export function getPromptHistoryPath() {
|
|
137
|
+
return join(resolution.root, 'prompt-history.jsonl');
|
|
138
|
+
}
|
|
139
|
+
/** `<home>/state.json` — UI bookkeeping scalars only; see `ui-state.ts`. */
|
|
140
|
+
export function getUiStatePath() {
|
|
141
|
+
return join(resolution.root, 'state.json');
|
|
142
|
+
}
|
|
122
143
|
export function getLogsDir() {
|
|
123
144
|
return join(resolution.root, 'logs');
|
|
124
145
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `aragon config get | list | edit | home`, plus the `log.*`
|
|
3
|
-
* `config set`.
|
|
2
|
+
* `aragon config get | list | edit | home`, plus the `log.*` and `team.*`
|
|
3
|
+
* branches of `config set`.
|
|
4
4
|
*
|
|
5
5
|
* These live here rather than in `cli.tsx` because `runConfigSet` is already a
|
|
6
6
|
* 21-case switch — well past the complexity ceiling this package writes down —
|
|
@@ -10,6 +10,17 @@
|
|
|
10
10
|
import { type PersistedConfig } from './schema.js';
|
|
11
11
|
/** Config keys under `log.` that `aragon config set` accepts. */
|
|
12
12
|
export declare const LOG_CONFIG_SET_KEYS: readonly ["log.level", "log.toFile", "log.dir", "log.maxFileBytes", "log.maxFiles", "log.redactSecrets", "log.previewChars"];
|
|
13
|
+
/**
|
|
14
|
+
* Config keys under `team.` that `aragon config set` accepts (team-subagents
|
|
15
|
+
* §4.3).
|
|
16
|
+
*
|
|
17
|
+
* Handled here rather than in `cli.tsx`'s switch for the reason this module's
|
|
18
|
+
* header already gives about `log.*`: that switch is past this package's
|
|
19
|
+
* complexity ceiling, and a key added to the membership set but forgotten in the
|
|
20
|
+
* switch falls through every case, writes nothing, and still prints
|
|
21
|
+
* `Set team.enabled = false`.
|
|
22
|
+
*/
|
|
23
|
+
export declare const TEAM_CONFIG_SET_KEYS: readonly ["team.enabled", "team.maxSubagents", "team.maxConcurrent", "team.subagentTimeoutMs", "team.dispatchTimeoutMs", "team.maxTurnsPerSubagent"];
|
|
13
24
|
/**
|
|
14
25
|
* Translate a `log.*` dotted key into a patch, or `null` for anything else.
|
|
15
26
|
*
|
|
@@ -19,6 +30,19 @@ export declare const LOG_CONFIG_SET_KEYS: readonly ["log.level", "log.toFile", "
|
|
|
19
30
|
* included.
|
|
20
31
|
*/
|
|
21
32
|
export declare function applyLogConfigSet(key: string, value: string): Partial<PersistedConfig> | null;
|
|
33
|
+
/**
|
|
34
|
+
* Translate a `team.*` dotted key into a patch, or `null` for anything else.
|
|
35
|
+
*
|
|
36
|
+
* The patch is a PARTIAL `team` section, safe only because
|
|
37
|
+
* `updatePersistedConfig` deep-merges that section — with a shallow merge,
|
|
38
|
+
* setting the fan-out width would silently reset every timeout in the section.
|
|
39
|
+
*
|
|
40
|
+
* Values are CLAMPED rather than rejected, exactly like `theme` and
|
|
41
|
+
* `skills.integrity`: hardening only the read path leaves a bad value on disk
|
|
42
|
+
* that reverts to the default on every launch, which presents as "my setting
|
|
43
|
+
* won't stick". `clampTeamConfig` is the one gate both directions pass through.
|
|
44
|
+
*/
|
|
45
|
+
export declare function applyTeamConfigSet(key: string, value: string): Partial<PersistedConfig> | null;
|
|
22
46
|
/**
|
|
23
47
|
* `log.redactSecrets false` is the one setting that lets credentials reach the
|
|
24
48
|
* disk. It is command-line-only (there is no settings-screen entry) and it says
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `aragon config get | list | edit | home`, plus the `log.*`
|
|
3
|
-
* `config set`.
|
|
2
|
+
* `aragon config get | list | edit | home`, plus the `log.*` and `team.*`
|
|
3
|
+
* branches of `config set`.
|
|
4
4
|
*
|
|
5
5
|
* These live here rather than in `cli.tsx` because `runConfigSet` is already a
|
|
6
6
|
* 21-case switch — well past the complexity ceiling this package writes down —
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
import { spawnSync } from 'node:child_process';
|
|
11
11
|
import { copyFileSync, existsSync, readFileSync } from 'node:fs';
|
|
12
12
|
import process from 'node:process';
|
|
13
|
-
import { getConfigBackupPath, getConfigPath, getHomeRoot, getLogsDir, isHomeOverridden, } from './app-paths.js';
|
|
13
|
+
import { getConfigBackupPath, getConfigPath, getHomeRoot, getLogsDir, getPromptHistoryPath, getUiStatePath, isHomeOverridden, } from './app-paths.js';
|
|
14
14
|
import { getHomeMigrationBreadcrumbPath } from './migrate-home.js';
|
|
15
15
|
import { loadPersistedConfig, updatePersistedConfig } from './store.js';
|
|
16
|
-
import { coercePositiveInt, maskSecret, } from './schema.js';
|
|
16
|
+
import { coercePositiveInt, maskSecret, DEFAULT_TEAM_CONFIG, } from './schema.js';
|
|
17
17
|
import { clampLogLevel } from '../logging/levels.js';
|
|
18
18
|
/** Config keys under `log.` that `aragon config set` accepts. */
|
|
19
19
|
export const LOG_CONFIG_SET_KEYS = [
|
|
@@ -25,6 +25,24 @@ export const LOG_CONFIG_SET_KEYS = [
|
|
|
25
25
|
'log.redactSecrets',
|
|
26
26
|
'log.previewChars',
|
|
27
27
|
];
|
|
28
|
+
/**
|
|
29
|
+
* Config keys under `team.` that `aragon config set` accepts (team-subagents
|
|
30
|
+
* §4.3).
|
|
31
|
+
*
|
|
32
|
+
* Handled here rather than in `cli.tsx`'s switch for the reason this module's
|
|
33
|
+
* header already gives about `log.*`: that switch is past this package's
|
|
34
|
+
* complexity ceiling, and a key added to the membership set but forgotten in the
|
|
35
|
+
* switch falls through every case, writes nothing, and still prints
|
|
36
|
+
* `Set team.enabled = false`.
|
|
37
|
+
*/
|
|
38
|
+
export const TEAM_CONFIG_SET_KEYS = [
|
|
39
|
+
'team.enabled',
|
|
40
|
+
'team.maxSubagents',
|
|
41
|
+
'team.maxConcurrent',
|
|
42
|
+
'team.subagentTimeoutMs',
|
|
43
|
+
'team.dispatchTimeoutMs',
|
|
44
|
+
'team.maxTurnsPerSubagent',
|
|
45
|
+
];
|
|
28
46
|
function isTrue(value) {
|
|
29
47
|
return value === 'true' || value === '1';
|
|
30
48
|
}
|
|
@@ -57,6 +75,43 @@ export function applyLogConfigSet(key, value) {
|
|
|
57
75
|
return null;
|
|
58
76
|
}
|
|
59
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Translate a `team.*` dotted key into a patch, or `null` for anything else.
|
|
80
|
+
*
|
|
81
|
+
* The patch is a PARTIAL `team` section, safe only because
|
|
82
|
+
* `updatePersistedConfig` deep-merges that section — with a shallow merge,
|
|
83
|
+
* setting the fan-out width would silently reset every timeout in the section.
|
|
84
|
+
*
|
|
85
|
+
* Values are CLAMPED rather than rejected, exactly like `theme` and
|
|
86
|
+
* `skills.integrity`: hardening only the read path leaves a bad value on disk
|
|
87
|
+
* that reverts to the default on every launch, which presents as "my setting
|
|
88
|
+
* won't stick". `clampTeamConfig` is the one gate both directions pass through.
|
|
89
|
+
*/
|
|
90
|
+
export function applyTeamConfigSet(key, value) {
|
|
91
|
+
const patch = (team) => ({ team });
|
|
92
|
+
switch (key) {
|
|
93
|
+
case 'team.enabled':
|
|
94
|
+
return patch({ enabled: isTrue(value) });
|
|
95
|
+
case 'team.maxSubagents':
|
|
96
|
+
return patch({ maxSubagents: coercePositiveInt(value, DEFAULT_TEAM_CONFIG.maxSubagents) });
|
|
97
|
+
case 'team.maxConcurrent':
|
|
98
|
+
return patch({ maxConcurrent: coercePositiveInt(value, DEFAULT_TEAM_CONFIG.maxConcurrent) });
|
|
99
|
+
case 'team.subagentTimeoutMs':
|
|
100
|
+
return patch({
|
|
101
|
+
subagentTimeoutMs: coercePositiveInt(value, DEFAULT_TEAM_CONFIG.subagentTimeoutMs),
|
|
102
|
+
});
|
|
103
|
+
case 'team.dispatchTimeoutMs':
|
|
104
|
+
return patch({
|
|
105
|
+
dispatchTimeoutMs: coercePositiveInt(value, DEFAULT_TEAM_CONFIG.dispatchTimeoutMs),
|
|
106
|
+
});
|
|
107
|
+
case 'team.maxTurnsPerSubagent':
|
|
108
|
+
return patch({
|
|
109
|
+
maxTurnsPerSubagent: coercePositiveInt(value, DEFAULT_TEAM_CONFIG.maxTurnsPerSubagent),
|
|
110
|
+
});
|
|
111
|
+
default:
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
60
115
|
/**
|
|
61
116
|
* `log.redactSecrets false` is the one setting that lets credentials reach the
|
|
62
117
|
* disk. It is command-line-only (there is no settings-screen entry) and it says
|
|
@@ -177,6 +232,10 @@ export function runConfigHome() {
|
|
|
177
232
|
process.stdout.write(`${getHomeRoot()}\n`);
|
|
178
233
|
if (isHomeOverridden())
|
|
179
234
|
process.stdout.write(' (overridden by ARAGON_HOME)\n');
|
|
235
|
+
// Named here because config.json no longer holds either of them, and a user
|
|
236
|
+
// looking for "where did my prompt history go" has to land somewhere.
|
|
237
|
+
process.stdout.write(` prompt history: ${getPromptHistoryPath()}\n`);
|
|
238
|
+
process.stdout.write(` ui state: ${getUiStatePath()}\n`);
|
|
180
239
|
const breadcrumb = getHomeMigrationBreadcrumbPath();
|
|
181
240
|
if (!existsSync(breadcrumb))
|
|
182
241
|
return;
|
package/dist/config/env.d.ts
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* - Provider secrets: ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_API_KEY /
|
|
6
6
|
* GEMINI_API_KEY.
|
|
7
7
|
* - Overrides: ARAGON_PROVIDER, ARAGON_MODEL, ARAGON_BASE_URL, ARAGON_THINKING,
|
|
8
|
-
* ARAGON_MAX_TOKENS, ARAGON_THEME, ARAGON_PLAN,
|
|
9
|
-
* ARAGON_LOG_FILE, ARAGON_LOG_DIR.
|
|
8
|
+
* ARAGON_MAX_TOKENS, ARAGON_THEME, ARAGON_MOUSE, ARAGON_PLAN, ARAGON_TEAM,
|
|
9
|
+
* ARAGON_TEAM_MAX, ARAGON_LOG_LEVEL, ARAGON_LOG_FILE, ARAGON_LOG_DIR.
|
|
10
10
|
*
|
|
11
11
|
* NOT here: `ARAGON_HOME`. It is read when `config/app-paths.ts` is first
|
|
12
12
|
* evaluated, which is long before `loadDotenv()` runs, so a `.env` can never
|
package/dist/config/env.js
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* - Provider secrets: ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_API_KEY /
|
|
6
6
|
* GEMINI_API_KEY.
|
|
7
7
|
* - Overrides: ARAGON_PROVIDER, ARAGON_MODEL, ARAGON_BASE_URL, ARAGON_THINKING,
|
|
8
|
-
* ARAGON_MAX_TOKENS, ARAGON_THEME, ARAGON_PLAN,
|
|
9
|
-
* ARAGON_LOG_FILE, ARAGON_LOG_DIR.
|
|
8
|
+
* ARAGON_MAX_TOKENS, ARAGON_THEME, ARAGON_MOUSE, ARAGON_PLAN, ARAGON_TEAM,
|
|
9
|
+
* ARAGON_TEAM_MAX, ARAGON_LOG_LEVEL, ARAGON_LOG_FILE, ARAGON_LOG_DIR.
|
|
10
10
|
*
|
|
11
11
|
* NOT here: `ARAGON_HOME`. It is read when `config/app-paths.ts` is first
|
|
12
12
|
* evaluated, which is long before `loadDotenv()` runs, so a `.env` can never
|
|
@@ -71,6 +71,14 @@ export function readEnvConfig() {
|
|
|
71
71
|
if (fullscreen !== undefined && fullscreen.length > 0) {
|
|
72
72
|
partial.fullscreen = fullscreen === '1' || fullscreen === 'true' || fullscreen === 'on' || fullscreen === 'yes';
|
|
73
73
|
}
|
|
74
|
+
// ARAGON_MOUSE=0 turns off wheel region routing. Parsed by the POSITIVE list
|
|
75
|
+
// above, exactly as ARAGON_FULLSCREEN is — NOT by `envBool` below, which uses
|
|
76
|
+
// a negative list. The two disagree on `ARAGON_MOUSE=disable`, and `mouse` is
|
|
77
|
+
// modelled on `--fullscreen`, so it uses the reader `--fullscreen` uses.
|
|
78
|
+
const mouse = process.env.ARAGON_MOUSE?.trim().toLowerCase();
|
|
79
|
+
if (mouse !== undefined && mouse.length > 0) {
|
|
80
|
+
partial.mouse = mouse === '1' || mouse === 'true' || mouse === 'on' || mouse === 'yes';
|
|
81
|
+
}
|
|
74
82
|
// ARAGON_PLAN=1 starts the session in PLAN mode — the same channel story as
|
|
75
83
|
// ARAGON_FULLSCREEN, for terminals reached through a wrapper that owns argv.
|
|
76
84
|
const plan = process.env.ARAGON_PLAN?.trim().toLowerCase();
|
|
@@ -88,6 +96,32 @@ export function readEnvConfig() {
|
|
|
88
96
|
enabled: !(skills === '0' || skills === 'false' || skills === 'off' || skills === 'no'),
|
|
89
97
|
};
|
|
90
98
|
}
|
|
99
|
+
// ARAGON_TEAM=0 turns team mode off; ARAGON_TEAM_MAX=<n> overrides the fan-out
|
|
100
|
+
// width. Parsed by the POSITIVE list, exactly as ARAGON_FULLSCREEN /
|
|
101
|
+
// ARAGON_MOUSE / ARAGON_PLAN are — NOT by `envBool` below, which uses a
|
|
102
|
+
// negative list. The two readers disagree on `ARAGON_TEAM=disable`, and the
|
|
103
|
+
// documented-but-dead outcome of copying the wrong one is what `load.ts`
|
|
104
|
+
// records for `ARAGON_MOUSE`.
|
|
105
|
+
const team = process.env.ARAGON_TEAM?.trim().toLowerCase();
|
|
106
|
+
const teamMax = process.env.ARAGON_TEAM_MAX?.trim();
|
|
107
|
+
if ((team !== undefined && team.length > 0) || (teamMax !== undefined && teamMax.length > 0)) {
|
|
108
|
+
const section = {};
|
|
109
|
+
if (team !== undefined && team.length > 0) {
|
|
110
|
+
section.enabled = team === '1' || team === 'true' || team === 'on' || team === 'yes';
|
|
111
|
+
}
|
|
112
|
+
if (teamMax !== undefined && teamMax.length > 0) {
|
|
113
|
+
const parsed = Number.parseInt(teamMax, 10);
|
|
114
|
+
// Left ABSENT when unparseable rather than written as a default, so the
|
|
115
|
+
// config file can still win. A resolved default here would make the env
|
|
116
|
+
// var look permanently supplied.
|
|
117
|
+
if (Number.isFinite(parsed) && parsed > 0)
|
|
118
|
+
section.maxSubagents = parsed;
|
|
119
|
+
}
|
|
120
|
+
// A PARTIAL section, safe only because every consumer deep-merges it — the
|
|
121
|
+
// same contract `partial.skills` and `partial.log` rely on.
|
|
122
|
+
if (Object.keys(section).length > 0)
|
|
123
|
+
partial.team = section;
|
|
124
|
+
}
|
|
91
125
|
const log = readEnvLogConfig();
|
|
92
126
|
if (log)
|
|
93
127
|
partial.log = log;
|