@aragon-agent/cli 0.6.38 → 0.6.39
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 +2523 -2326
- package/README.md +3614 -3173
- package/dist/agent/controller.d.ts +142 -1
- package/dist/agent/controller.js +171 -3
- package/dist/agent/reducer.d.ts +41 -1
- package/dist/agent/reducer.js +25 -0
- package/dist/agent/system-prompt.d.ts +14 -0
- package/dist/agent/system-prompt.js +5 -0
- package/dist/cli.js +85 -27
- package/dist/commands/builtins.js +13 -1
- package/dist/commands/keys-command.d.ts +23 -0
- package/dist/commands/keys-command.js +38 -0
- package/dist/commands/registry.d.ts +39 -0
- package/dist/commands/task-command.d.ts +39 -0
- package/dist/commands/task-command.js +102 -0
- package/dist/config/cli-commands.d.ts +23 -0
- package/dist/config/cli-commands.js +36 -0
- package/dist/config/env.js +17 -4
- package/dist/config/load.js +6 -1
- package/dist/config/schema.d.ts +43 -4
- package/dist/config/schema.js +31 -5
- package/dist/config/store.js +17 -2
- package/dist/diagnostics/doctor.d.ts +9 -0
- package/dist/diagnostics/doctor.js +34 -0
- package/dist/diagnostics/info.d.ts +71 -0
- package/dist/diagnostics/info.js +91 -5
- package/dist/exec/bridge/caller-tools.d.ts +96 -0
- package/dist/exec/bridge/caller-tools.js +213 -0
- package/dist/exec/bridge/decisions.d.ts +78 -0
- package/dist/exec/bridge/decisions.js +116 -0
- package/dist/exec/bridge/human-answers.d.ts +38 -0
- package/dist/exec/bridge/human-answers.js +164 -0
- package/dist/exec/bridge/limits.d.ts +68 -0
- package/dist/exec/bridge/limits.js +68 -0
- package/dist/exec/bridge/ndjson.d.ts +17 -0
- package/dist/exec/bridge/ndjson.js +83 -0
- package/dist/exec/bridge/pending.d.ts +70 -0
- package/dist/exec/bridge/pending.js +132 -0
- package/dist/exec/bridge/protocol.d.ts +346 -0
- package/dist/exec/bridge/protocol.js +256 -0
- package/dist/exec/bridge/server.d.ts +142 -0
- package/dist/exec/bridge/server.js +502 -0
- package/dist/exec/bridge/source.d.ts +58 -0
- package/dist/exec/bridge/source.js +145 -0
- package/dist/exec/bridge/writer.d.ts +26 -0
- package/dist/exec/bridge/writer.js +156 -0
- package/dist/exec/cli-commands.js +30 -2
- package/dist/exec/emitter.d.ts +16 -2
- package/dist/exec/emitter.js +26 -6
- package/dist/exec/events.d.ts +465 -10
- package/dist/exec/events.js +47 -1
- package/dist/exec/hooks/dispatch.d.ts +76 -0
- package/dist/exec/hooks/dispatch.js +169 -0
- package/dist/exec/hooks/index.d.ts +83 -0
- package/dist/exec/hooks/index.js +205 -0
- package/dist/exec/hooks/limits.d.ts +56 -0
- package/dist/exec/hooks/limits.js +49 -0
- package/dist/exec/hooks/registry.d.ts +49 -0
- package/dist/exec/hooks/registry.js +91 -0
- package/dist/exec/hooks/tool-wrapper.d.ts +94 -0
- package/dist/exec/hooks/tool-wrapper.js +154 -0
- package/dist/exec/hooks/turn.d.ts +61 -0
- package/dist/exec/hooks/turn.js +59 -0
- package/dist/exec/hooks/validate.d.ts +85 -0
- package/dist/exec/hooks/validate.js +310 -0
- package/dist/exec/index.d.ts +130 -4
- package/dist/exec/index.js +501 -10
- package/dist/exec/options.d.ts +124 -2
- package/dist/exec/options.js +413 -6
- package/dist/exec/permission.d.ts +9 -1
- package/dist/exec/permission.js +7 -1
- package/dist/exec/request/limits.d.ts +51 -0
- package/dist/exec/request/limits.js +49 -0
- package/dist/exec/request/scope.d.ts +106 -0
- package/dist/exec/request/scope.js +149 -0
- package/dist/exec/result/limits.d.ts +74 -0
- package/dist/exec/result/limits.js +72 -0
- package/dist/exec/result/prompt.d.ts +28 -0
- package/dist/exec/result/prompt.js +74 -0
- package/dist/exec/result/schema.d.ts +99 -0
- package/dist/exec/result/schema.js +464 -0
- package/dist/exec/result/sink.d.ts +56 -0
- package/dist/exec/result/sink.js +50 -0
- package/dist/exec/result/tool.d.ts +37 -0
- package/dist/exec/result/tool.js +118 -0
- package/dist/exec/runner.d.ts +395 -1
- package/dist/exec/runner.js +860 -8
- package/dist/exec/threads/aggregate.d.ts +34 -0
- package/dist/exec/threads/aggregate.js +30 -0
- package/dist/exec/threads/capacity.d.ts +23 -0
- package/dist/exec/threads/capacity.js +38 -0
- package/dist/exec/threads/dispatch-source.d.ts +60 -0
- package/dist/exec/threads/dispatch-source.js +106 -0
- package/dist/exec/threads/execute-threads.d.ts +53 -0
- package/dist/exec/threads/execute-threads.js +234 -0
- package/dist/exec/threads/gate.d.ts +42 -0
- package/dist/exec/threads/gate.js +81 -0
- package/dist/exec/threads/identity.d.ts +36 -0
- package/dist/exec/threads/identity.js +67 -0
- package/dist/exec/threads/interrupt.d.ts +29 -0
- package/dist/exec/threads/interrupt.js +79 -0
- package/dist/exec/threads/limits.d.ts +58 -0
- package/dist/exec/threads/limits.js +35 -0
- package/dist/exec/threads/outcome.d.ts +47 -0
- package/dist/exec/threads/outcome.js +26 -0
- package/dist/exec/threads/registry.d.ts +81 -0
- package/dist/exec/threads/registry.js +427 -0
- package/dist/exec/threads/runtime.d.ts +107 -0
- package/dist/exec/threads/runtime.js +523 -0
- package/dist/exec/threads/thread-source.d.ts +20 -0
- package/dist/exec/threads/thread-source.js +134 -0
- package/dist/fast/limits.d.ts +54 -0
- package/dist/fast/limits.js +54 -0
- package/dist/fast/summary-call.d.ts +72 -0
- package/dist/fast/summary-call.js +133 -0
- package/dist/fast/summary-prompt.d.ts +106 -0
- package/dist/fast/summary-prompt.js +320 -0
- package/dist/fast/task-summary-types.d.ts +66 -0
- package/dist/fast/task-summary-types.js +17 -0
- package/dist/logging/logger.d.ts +1 -1
- package/dist/proc/limits.d.ts +7 -0
- package/dist/proc/limits.js +7 -0
- package/dist/proc/output-streams.d.ts +50 -0
- package/dist/proc/output-streams.js +54 -0
- package/dist/team/runtime.d.ts +9 -0
- package/dist/team/runtime.js +8 -0
- package/dist/team/subagent.d.ts +15 -0
- package/dist/team/subagent.js +45 -1
- package/dist/tools/bash-tool.d.ts +10 -0
- package/dist/tools/bash-tool.js +71 -6
- package/dist/tools/index.d.ts +93 -1
- package/dist/tools/index.js +112 -3
- package/dist/tools/tool-gate.d.ts +100 -0
- package/dist/tools/tool-gate.js +87 -0
- package/dist/ui/App.js +224 -11
- package/dist/ui/Header.d.ts +28 -16
- package/dist/ui/Header.js +21 -19
- package/dist/ui/interaction-hints.js +6 -0
- package/dist/ui/layout/OverlayFrame.d.ts +18 -0
- package/dist/ui/layout/OverlayFrame.js +19 -4
- package/dist/ui/layout/header-hints.d.ts +55 -62
- package/dist/ui/layout/header-hints.js +59 -166
- package/dist/ui/layout/header-left.d.ts +115 -0
- package/dist/ui/layout/header-left.js +206 -0
- package/dist/ui/layout/shortcut-panel-layout.d.ts +191 -0
- package/dist/ui/layout/shortcut-panel-layout.js +321 -0
- package/dist/ui/layout/status-detail-layout.js +5 -1
- package/dist/ui/layout/task-summary-cell.d.ts +61 -0
- package/dist/ui/layout/task-summary-cell.js +65 -0
- package/dist/ui/overlays/HelpOverlay.d.ts +12 -0
- package/dist/ui/overlays/HelpOverlay.js +29 -2
- package/dist/ui/overlays/ShortcutPanel.d.ts +39 -0
- package/dist/ui/overlays/ShortcutPanel.js +106 -0
- package/dist/ui/shortcuts.d.ts +81 -0
- package/dist/ui/shortcuts.js +221 -0
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -25,7 +25,7 @@ import { formatMigrationNotice, migrateLegacyState } from './config/migrate-lega
|
|
|
25
25
|
import { formatHomeMigrationNotice, migrateToHome } from './config/migrate-home.js';
|
|
26
26
|
import { migrateStateOutOfConfig } from './config/migrate-state-out-of-config.js';
|
|
27
27
|
import { runHistoryCommand } from './config/history-commands.js';
|
|
28
|
-
import { COMPACTION_CONFIG_SET_KEYS, FAST_CONFIG_SET_KEYS, LOG_CONFIG_SET_KEYS, RETRY_CONFIG_SET_KEYS, TEAM_CONFIG_SET_KEYS, JOBS_CONFIG_SET_KEYS, UPDATE_CONFIG_SET_KEYS, applyCompactionConfigSet, applyFastConfigSet, applyLogConfigSet, applyRetryConfigSet, applyTeamConfigSet, applyJobsConfigSet, applyUpdateConfigSet, runConfigEdit, runConfigGet, runConfigHome, runConfigList, warnIfRedactionDisabled, } from './config/cli-commands.js';
|
|
28
|
+
import { COMPACTION_CONFIG_SET_KEYS, FAST_CONFIG_SET_KEYS, LOG_CONFIG_SET_KEYS, RETRY_CONFIG_SET_KEYS, TEAM_CONFIG_SET_KEYS, JOBS_CONFIG_SET_KEYS, TASK_SUMMARY_CONFIG_SET_KEYS, UPDATE_CONFIG_SET_KEYS, applyCompactionConfigSet, applyFastConfigSet, applyLogConfigSet, applyRetryConfigSet, applyTeamConfigSet, applyJobsConfigSet, applyTaskSummaryConfigSet, applyUpdateConfigSet, runConfigEdit, runConfigGet, runConfigHome, runConfigList, warnIfRedactionDisabled, } from './config/cli-commands.js';
|
|
29
29
|
import { attachAgentEvents, attachTeamEvents, installLogging, setScreenRestore, setSignalTerminator, } from './logging/install.js';
|
|
30
30
|
import { getLogger } from './logging/logger.js';
|
|
31
31
|
import { runLogsCommand } from './logging/cli-commands.js';
|
|
@@ -276,15 +276,19 @@ function makeController(flags, opts = { interactive: true }) {
|
|
|
276
276
|
version: VERSION,
|
|
277
277
|
todoPanelCapable,
|
|
278
278
|
confirm: (req) => confirmBridge.handler ? confirmBridge.handler(req) : Promise.resolve(true),
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
279
|
+
// THE TWO CAN NEVER BOTH BE PRESENT: `interactive: false` is the only path
|
|
280
|
+
// that supplies a bridge gate, and the interactive branch is the only one that
|
|
281
|
+
// reads the overlay bridge. `??` keeps `-p`'s deny-all default byte-identical.
|
|
282
|
+
approval: opts.approval ??
|
|
283
|
+
(opts.interactive
|
|
284
|
+
? {
|
|
285
|
+
// Read live on every call: the App's effect cleanup nulls the handler
|
|
286
|
+
// on unmount, and a cached `true` would keep the gate open after the
|
|
287
|
+
// human channel is gone.
|
|
288
|
+
canPrompt: () => confirmBridge.handler !== null,
|
|
289
|
+
request: (req) => confirmBridge.handler ? confirmBridge.handler(req) : Promise.resolve(false),
|
|
290
|
+
}
|
|
291
|
+
: DENY_ALL_APPROVAL),
|
|
288
292
|
notify: (level, text) => {
|
|
289
293
|
if (!opts.interactive)
|
|
290
294
|
process.stderr.write(`[skills] ${level}: ${text}\n`);
|
|
@@ -294,17 +298,36 @@ function makeController(flags, opts = { interactive: true }) {
|
|
|
294
298
|
// render a question wizard or a plan card. Headless gets the fail-closed
|
|
295
299
|
// gate, whose STATIC `neverPrompts` is also what stops `createPlanTools`
|
|
296
300
|
// registering two tools the model could never use there.
|
|
297
|
-
humanInput: opts.
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
301
|
+
humanInput: opts.humanInput ??
|
|
302
|
+
(opts.interactive
|
|
303
|
+
? {
|
|
304
|
+
// Live probe, never cached - same contract as `approval` above.
|
|
305
|
+
canPrompt: () => humanInputBridge.handler !== null,
|
|
306
|
+
request: (req, signal) => humanInputBridge.handler
|
|
307
|
+
? humanInputBridge.handler(req, signal)
|
|
308
|
+
: Promise.resolve(null),
|
|
309
|
+
}
|
|
310
|
+
: DENY_ALL_HUMAN_INPUT),
|
|
306
311
|
...(opts.permission ? { permission: opts.permission } : {}),
|
|
307
312
|
...(opts.appendSystemPrompt ? { appendSystemPrompt: opts.appendSystemPrompt } : {}),
|
|
313
|
+
// SPREAD, never `toolGate: opts.toolGate`: with no gate the deps bag has no
|
|
314
|
+
// such key, `createBuiltinTools` never enters its mapping branch, and the tool
|
|
315
|
+
// array stays provably identical outside bridge mode (I-1).
|
|
316
|
+
...(opts.toolGate ? { toolGate: opts.toolGate } : {}),
|
|
317
|
+
...(opts.toolGateForChild ? { toolGateForChild: opts.toolGateForChild } : {}),
|
|
318
|
+
...(opts.decisionBudgetMs !== undefined ? { decisionBudgetMs: opts.decisionBudgetMs } : {}),
|
|
319
|
+
// THE R3 PAIR, AND THIS LINE IS THE DIFFERENCE BETWEEN A WORKING FEATURE AND A DEAD
|
|
320
|
+
// ONE (headless-request-scope-and-typed-result section 2.6 rows 11/17). This factory
|
|
321
|
+
// takes `deps` as a bag and FORWARDS ONLY WHAT IS NAMED HERE, so a new dep that is
|
|
322
|
+
// not spread below is dropped in silence: every unit test that stubs the factory
|
|
323
|
+
// keeps passing while the real binary never registers the tool. That is the D-12
|
|
324
|
+
// defect this project has already paid for once, which is why both keys are spread
|
|
325
|
+
// conditionally and adjacent to each other.
|
|
326
|
+
...(opts.resultTool ? { resultTool: opts.resultTool } : {}),
|
|
327
|
+
...(opts.structuredResultBlock
|
|
328
|
+
? { structuredResultBlock: opts.structuredResultBlock }
|
|
329
|
+
: {}),
|
|
330
|
+
...(opts.hookRunner ? { hookRunner: opts.hookRunner } : {}),
|
|
308
331
|
});
|
|
309
332
|
return { controller, confirmBridge, humanInputBridge };
|
|
310
333
|
}
|
|
@@ -322,15 +345,34 @@ function makeExecController(flags, deps) {
|
|
|
322
345
|
const { controller } = makeController(flags, { interactive: false, ...deps });
|
|
323
346
|
void controller.refreshModelMetadata();
|
|
324
347
|
const logger = getLogger();
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
348
|
+
// PROCESS-LEVEL WORK HAPPENS ONCE, PER-CONTROLLER WORK HAPPENS EVERY TIME
|
|
349
|
+
// (headless-threads-and-lifecycle-hooks section 2.3.4, review P1-8).
|
|
350
|
+
//
|
|
351
|
+
// The four statements in this branch attach to a PROCESS-WIDE singleton - the logger's
|
|
352
|
+
// handler list, the log file, the same untrusted skill directories - so running them per
|
|
353
|
+
// thread would emit N `run_exec` lines and N `[log]` handlers for one process.
|
|
354
|
+
//
|
|
355
|
+
// `attachAgentEvents` / `attachTeamEvents` ARE NOT IN THIS BRANCH and must never be:
|
|
356
|
+
// each binds to THIS controller and returns its own disposer, so skipping them would
|
|
357
|
+
// mean seven of eight tenants' agent events never reach the log at all. That is an
|
|
358
|
+
// observability regression, not an optimisation - the opposite of what the first draft of
|
|
359
|
+
// the design said. The disposers go back to the caller (the thread runtime) and are
|
|
360
|
+
// invoked when the thread closes (I-T6).
|
|
361
|
+
// `processLevelMounts` IS EXPLICIT RATHER THAN DERIVED FROM `threadId` (review P1-8):
|
|
362
|
+
// the default thread carries `threadId: 'main'` like every other thread, so a guard on
|
|
363
|
+
// "has a thread id" would skip these four statements for ALL of them and leave a
|
|
364
|
+
// thread-mode run with no `run_exec` line and no `onFailure` handler at all.
|
|
365
|
+
if (deps.processLevelMounts !== false) {
|
|
366
|
+
logger.info('cli', 'run_exec', {});
|
|
367
|
+
logger.onFailure((reason) => process.stderr.write(`[log] ${reason}\n`));
|
|
368
|
+
for (const dir of controller.getSkillService().untrustedDirs()) {
|
|
369
|
+
// STDERR, NEVER STDOUT. In `json` / `stream-json` mode stdout carries
|
|
370
|
+
// nothing but the schema, and a notice on it would corrupt every consumer.
|
|
371
|
+
process.stderr.write(`[skills] project skills in ${dir} skipped (untrusted). Run: aragon skills trust ${dir}\n`);
|
|
372
|
+
}
|
|
333
373
|
}
|
|
374
|
+
const disposers = [attachAgentEvents(logger, controller), attachTeamEvents(logger, controller)];
|
|
375
|
+
deps.onAttach?.(disposers);
|
|
334
376
|
return controller;
|
|
335
377
|
}
|
|
336
378
|
/**
|
|
@@ -1551,6 +1593,12 @@ const CONFIG_SET_KEYS = new Set([
|
|
|
1551
1593
|
// own assertion: the static guard in `config.test.ts` matches LITERAL keys in
|
|
1552
1594
|
// this file, so a spread set is invisible to it (RV-P2-8).
|
|
1553
1595
|
...JOBS_CONFIG_SET_KEYS,
|
|
1596
|
+
// `taskSummary.*` keys are handled by `applyTaskSummaryConfigSet`, same reasoning
|
|
1597
|
+
// again (tui-shortcut-drawer-and-task-summary P0-3). WITHOUT THIS LINE the keys
|
|
1598
|
+
// fall through to `Unknown config key`, and WITHOUT the matching case below the
|
|
1599
|
+
// membership set accepts a write that never happens - the two halves the static
|
|
1600
|
+
// guard in `config.test.ts` exists to pair up.
|
|
1601
|
+
...TASK_SUMMARY_CONFIG_SET_KEYS,
|
|
1554
1602
|
]);
|
|
1555
1603
|
/**
|
|
1556
1604
|
* `aragon config set maxTokens <n|auto|default>`.
|
|
@@ -1701,6 +1749,16 @@ function runConfigSetValue(key, value) {
|
|
|
1701
1749
|
process.stdout.write(`Set ${key} = ${String(merged.jobs[field])}\n`);
|
|
1702
1750
|
return;
|
|
1703
1751
|
}
|
|
1752
|
+
const taskSummaryPatch = applyTaskSummaryConfigSet(key, value);
|
|
1753
|
+
if (taskSummaryPatch) {
|
|
1754
|
+
// Echoed from the STORED value, like every section above: `config set
|
|
1755
|
+
// taskSummary.enabled banana` prints `false`, which is what the read path will
|
|
1756
|
+
// report and therefore the only honest answer.
|
|
1757
|
+
const merged = updatePersistedConfig(taskSummaryPatch);
|
|
1758
|
+
const field = key.slice('taskSummary.'.length);
|
|
1759
|
+
process.stdout.write(`Set ${key} = ${String(merged.taskSummary[field])}\n`);
|
|
1760
|
+
return;
|
|
1761
|
+
}
|
|
1704
1762
|
const patch = {};
|
|
1705
1763
|
switch (key) {
|
|
1706
1764
|
case 'provider':
|
|
@@ -27,6 +27,12 @@ import { registerQueueCommand } from './queue-command.js';
|
|
|
27
27
|
import { registerPromptCommands } from '../prompts/command.js';
|
|
28
28
|
import { registerKitCommand } from './kit-command.js';
|
|
29
29
|
import { registerAgentsCommand } from './agents-command.js';
|
|
30
|
+
// THE DRAWER AND THE HEADER LABEL (tui-shortcut-drawer-and-task-summary §4.7):
|
|
31
|
+
// two single-purpose commands in their own modules, registered from here. Both are
|
|
32
|
+
// UNCONDITIONAL, even with `taskSummary.enabled=false`, because they are the only
|
|
33
|
+
// surfaces that can explain the switch.
|
|
34
|
+
import { keysCommand } from './keys-command.js';
|
|
35
|
+
import { taskCommand } from './task-command.js';
|
|
30
36
|
import { perfCommand } from './perf.js';
|
|
31
37
|
import { runCompactCommand } from '../compaction/command.js';
|
|
32
38
|
import { runClearCommand } from './clear-command.js';
|
|
@@ -1502,6 +1508,11 @@ export function registerBuiltinCommands(registry) {
|
|
|
1502
1508
|
// The read-only subagent board (tui-subagent-panel §6.4). Same shape again: the
|
|
1503
1509
|
// body is `commands/agents-command.ts` and the registration is this one line.
|
|
1504
1510
|
registerAgentsCommand(registry);
|
|
1511
|
+
// The keyboard drawer (`Ctrl+G` / `/keys`) and the task label (`/task`). Two
|
|
1512
|
+
// registrations, no new mechanism: `/keys` sets the panel's pre-filter and opens
|
|
1513
|
+
// the same overlay `Ctrl+G` opens, and `/task` reports what the header cell says.
|
|
1514
|
+
registry.register(keysCommand);
|
|
1515
|
+
registry.register(taskCommand);
|
|
1505
1516
|
}
|
|
1506
1517
|
/**
|
|
1507
1518
|
* Every built-in command name INCLUDING aliases (`quit`), for documentation and
|
|
@@ -1511,7 +1522,8 @@ export function registerBuiltinCommands(registry) {
|
|
|
1511
1522
|
* `registry.get(name)` instead, because that is the only check that stays
|
|
1512
1523
|
* correct when commands are registered dynamically (§7.1 / P1-2).
|
|
1513
1524
|
*/
|
|
1514
|
-
export const BUILTIN_COMMAND_NAMES = ['queue', 'prompts', 'agents',
|
|
1525
|
+
export const BUILTIN_COMMAND_NAMES = ['queue', 'prompts', 'agents', 'keys', 'task',
|
|
1526
|
+
...COMMANDS.flatMap((c) => [
|
|
1515
1527
|
c.name,
|
|
1516
1528
|
...(c.aliases ?? []),
|
|
1517
1529
|
])].sort();
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/keys [query]` — open the keyboard drawer (tui-shortcut-drawer-and-task-summary
|
|
3
|
+
* §4.7).
|
|
4
|
+
*
|
|
5
|
+
* ASCII ONLY: `src/commands/**` is inside the glyph scanner's scope.
|
|
6
|
+
*
|
|
7
|
+
* THE COMMAND IS THE SECOND ENTRANCE TO THE SAME ROOM, not a second room: it sets
|
|
8
|
+
* the pre-filter and opens `overlay: 'shortcuts'`, exactly as `Ctrl+G` does. That
|
|
9
|
+
* pairing (`/agents` <-> `Ctrl+B`, `/help` <-> `?`) is the established one, and it
|
|
10
|
+
* exists because a key is unreachable in a script, a paste and a screen reader.
|
|
11
|
+
*
|
|
12
|
+
* NOTE ON NAMES: `CommandRegistry.register` overwrites unconditionally while the
|
|
13
|
+
* skill loader only claims a bare `/<name>` when nothing else has it, so a user with
|
|
14
|
+
* a skill called `keys` now reaches it as `/skill:keys`. That is registered in
|
|
15
|
+
* `CHANGELOG.md`; `/keys` deliberately has NO alias, because an alias would multiply
|
|
16
|
+
* the same shadowing.
|
|
17
|
+
*/
|
|
18
|
+
import type { SlashCommand } from './registry.js';
|
|
19
|
+
/** The pure part: what to open and with what filter. Separated so it is testable. */
|
|
20
|
+
export declare function parseKeysArgs(args: string): {
|
|
21
|
+
query: string;
|
|
22
|
+
};
|
|
23
|
+
export declare const keysCommand: SlashCommand;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/keys [query]` — open the keyboard drawer (tui-shortcut-drawer-and-task-summary
|
|
3
|
+
* §4.7).
|
|
4
|
+
*
|
|
5
|
+
* ASCII ONLY: `src/commands/**` is inside the glyph scanner's scope.
|
|
6
|
+
*
|
|
7
|
+
* THE COMMAND IS THE SECOND ENTRANCE TO THE SAME ROOM, not a second room: it sets
|
|
8
|
+
* the pre-filter and opens `overlay: 'shortcuts'`, exactly as `Ctrl+G` does. That
|
|
9
|
+
* pairing (`/agents` <-> `Ctrl+B`, `/help` <-> `?`) is the established one, and it
|
|
10
|
+
* exists because a key is unreachable in a script, a paste and a screen reader.
|
|
11
|
+
*
|
|
12
|
+
* NOTE ON NAMES: `CommandRegistry.register` overwrites unconditionally while the
|
|
13
|
+
* skill loader only claims a bare `/<name>` when nothing else has it, so a user with
|
|
14
|
+
* a skill called `keys` now reaches it as `/skill:keys`. That is registered in
|
|
15
|
+
* `CHANGELOG.md`; `/keys` deliberately has NO alias, because an alias would multiply
|
|
16
|
+
* the same shadowing.
|
|
17
|
+
*/
|
|
18
|
+
/** The pure part: what to open and with what filter. Separated so it is testable. */
|
|
19
|
+
export function parseKeysArgs(args) {
|
|
20
|
+
// ONE TOKEN, TRIMMED: the filter matches `id` / `keys` / `keyword` / aliases, and
|
|
21
|
+
// a multi-word query would only ever match a phrase none of them contain.
|
|
22
|
+
return { query: args.trim() };
|
|
23
|
+
}
|
|
24
|
+
export const keysCommand = {
|
|
25
|
+
name: 'keys',
|
|
26
|
+
description: 'List every keyboard shortcut (opens the drawer)',
|
|
27
|
+
run: (ctx) => {
|
|
28
|
+
const { query } = parseKeysArgs(ctx.args);
|
|
29
|
+
// WITHOUT THE PORT THERE IS NO PANEL TO OPEN, and saying so is better than
|
|
30
|
+
// opening nothing: this is the one command whose failure mode would otherwise be
|
|
31
|
+
// a keystroke that appears to do nothing at all.
|
|
32
|
+
if (!ctx.taskSummary) {
|
|
33
|
+
ctx.notify('warn', 'The shortcut drawer is not available in this build.');
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
ctx.taskSummary.openPanel(query);
|
|
37
|
+
},
|
|
38
|
+
};
|
|
@@ -175,6 +175,45 @@ export interface CommandContext {
|
|
|
175
175
|
* needs the SHAPE of the port, never a value at run time.
|
|
176
176
|
*/
|
|
177
177
|
prompts?: PromptCommandPort;
|
|
178
|
+
/**
|
|
179
|
+
* The task label's three capabilities (`/keys`, `/task`; tui-shortcut-drawer-and-
|
|
180
|
+
* task-summary §6.4).
|
|
181
|
+
*
|
|
182
|
+
* OPTIONAL, and this is the sixth time this file records the same rule: every
|
|
183
|
+
* fixture in this package builds `CommandContext` by literal, so a REQUIRED field
|
|
184
|
+
* would fail to compile every one of them for a feature none of them is about.
|
|
185
|
+
* Absent means the build has no label machinery, and both commands say so instead
|
|
186
|
+
* of crashing - which matters most for `/task`, whose entire job is to explain why
|
|
187
|
+
* there is no label.
|
|
188
|
+
*
|
|
189
|
+
* TYPE-ONLY import, for the reason the `branch` note above records.
|
|
190
|
+
*/
|
|
191
|
+
taskSummary?: TaskSummaryCommandPort;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* What `/keys` and `/task` may do.
|
|
195
|
+
*
|
|
196
|
+
* THREE METHODS, NOT ONE: opening the drawer, reading the label and forcing a
|
|
197
|
+
* regeneration are three different user intentions, and `/task refresh` must not be
|
|
198
|
+
* expressible as "open the panel and press r".
|
|
199
|
+
*/
|
|
200
|
+
export interface TaskSummaryCommandPort {
|
|
201
|
+
/** Open the drawer, optionally pre-filtered (`/keys <query>`). */
|
|
202
|
+
openPanel(query?: string): void;
|
|
203
|
+
/** The current label, its provenance and why it is a fallback. */
|
|
204
|
+
read(): {
|
|
205
|
+
view: {
|
|
206
|
+
text: string;
|
|
207
|
+
tone: 'model' | 'fallback';
|
|
208
|
+
reason: string | null;
|
|
209
|
+
by: string;
|
|
210
|
+
at: number;
|
|
211
|
+
} | null;
|
|
212
|
+
pending: boolean;
|
|
213
|
+
enabled: boolean;
|
|
214
|
+
};
|
|
215
|
+
/** Regenerate from the last user message. Returns the anchor it used, or `''`. */
|
|
216
|
+
refresh(): string;
|
|
178
217
|
}
|
|
179
218
|
/** What `/mouse` can see and do. Read at command time, never captured. */
|
|
180
219
|
export interface MouseCommandPort {
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/task` and `/task refresh` (tui-shortcut-drawer-and-task-summary §4.7).
|
|
3
|
+
*
|
|
4
|
+
* ASCII ONLY: `src/commands/**` is inside the glyph scanner's scope.
|
|
5
|
+
*
|
|
6
|
+
* WHY "task summary" AND NEVER BARE "task": this package already has a TOOL named
|
|
7
|
+
* `task` (the subagent dispatcher, `team/task-tool.ts`), and `HelpOverlay`'s team
|
|
8
|
+
* table prints that name. A help line that said only "task" would make two unrelated
|
|
9
|
+
* things share one word on the same screen.
|
|
10
|
+
*
|
|
11
|
+
* WHY A NOTICE AND NOT AN OVERLAY: three lines of provenance do not deserve a
|
|
12
|
+
* frame, and the guaranteed reporting surface for "why is there no label" has to be
|
|
13
|
+
* somewhere the user can scroll back to - a toast disappears.
|
|
14
|
+
*
|
|
15
|
+
* BOTH COMMANDS ARE REGISTERED UNCONDITIONALLY, even with
|
|
16
|
+
* `taskSummary.enabled = false`: they are the only surface that can EXPLAIN the
|
|
17
|
+
* switch, and a missing command answers "unknown command" instead of "off" (the
|
|
18
|
+
* `/prompts` D-19 rule).
|
|
19
|
+
*/
|
|
20
|
+
import type { SlashCommand } from './registry.js';
|
|
21
|
+
/** One line per fact, or a single honest line when there is no label. */
|
|
22
|
+
export declare function formatTaskSummaryReport(input: {
|
|
23
|
+
view: {
|
|
24
|
+
text: string;
|
|
25
|
+
tone: 'model' | 'fallback';
|
|
26
|
+
reason: string | null;
|
|
27
|
+
by: string;
|
|
28
|
+
at: number;
|
|
29
|
+
} | null;
|
|
30
|
+
pending: boolean;
|
|
31
|
+
enabled: boolean;
|
|
32
|
+
now: number;
|
|
33
|
+
}): string[];
|
|
34
|
+
/** `''` for a bare `/task`, `'refresh'` for `/task refresh`, else the raw argument. */
|
|
35
|
+
export declare function parseTaskArgs(args: string): {
|
|
36
|
+
action: 'show' | 'refresh' | 'unknown';
|
|
37
|
+
raw: string;
|
|
38
|
+
};
|
|
39
|
+
export declare const taskCommand: SlashCommand;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/task` and `/task refresh` (tui-shortcut-drawer-and-task-summary §4.7).
|
|
3
|
+
*
|
|
4
|
+
* ASCII ONLY: `src/commands/**` is inside the glyph scanner's scope.
|
|
5
|
+
*
|
|
6
|
+
* WHY "task summary" AND NEVER BARE "task": this package already has a TOOL named
|
|
7
|
+
* `task` (the subagent dispatcher, `team/task-tool.ts`), and `HelpOverlay`'s team
|
|
8
|
+
* table prints that name. A help line that said only "task" would make two unrelated
|
|
9
|
+
* things share one word on the same screen.
|
|
10
|
+
*
|
|
11
|
+
* WHY A NOTICE AND NOT AN OVERLAY: three lines of provenance do not deserve a
|
|
12
|
+
* frame, and the guaranteed reporting surface for "why is there no label" has to be
|
|
13
|
+
* somewhere the user can scroll back to - a toast disappears.
|
|
14
|
+
*
|
|
15
|
+
* BOTH COMMANDS ARE REGISTERED UNCONDITIONALLY, even with
|
|
16
|
+
* `taskSummary.enabled = false`: they are the only surface that can EXPLAIN the
|
|
17
|
+
* switch, and a missing command answers "unknown command" instead of "off" (the
|
|
18
|
+
* `/prompts` D-19 rule).
|
|
19
|
+
*/
|
|
20
|
+
/** One line per fact, or a single honest line when there is no label. */
|
|
21
|
+
export function formatTaskSummaryReport(input) {
|
|
22
|
+
if (!input.enabled) {
|
|
23
|
+
return [
|
|
24
|
+
'Task summary is off (config taskSummary.enabled = false).',
|
|
25
|
+
'Turn it back on with: aragon config set taskSummary.enabled true',
|
|
26
|
+
];
|
|
27
|
+
}
|
|
28
|
+
if (input.view === null) {
|
|
29
|
+
if (input.pending)
|
|
30
|
+
return ['Task summary: still being generated.'];
|
|
31
|
+
return [
|
|
32
|
+
'No task summary yet.',
|
|
33
|
+
'It appears after the first prompt of a task; /task refresh regenerates it.',
|
|
34
|
+
];
|
|
35
|
+
}
|
|
36
|
+
const age = formatAge(input.now - input.view.at);
|
|
37
|
+
const source = input.view.tone === 'fallback'
|
|
38
|
+
? `your own words (${input.view.reason ?? 'fallback'})`
|
|
39
|
+
: input.view.by;
|
|
40
|
+
return [
|
|
41
|
+
`Task summary: ${input.view.text}`,
|
|
42
|
+
`Source: ${source}${age === '' ? '' : `, ${age}`}`,
|
|
43
|
+
'Regenerate with /task refresh, or open the drawer (Ctrl+G) to see why.',
|
|
44
|
+
];
|
|
45
|
+
}
|
|
46
|
+
/** `12s ago` / `3m ago` / `1h ago`, or `''` for a clock that went backwards. */
|
|
47
|
+
function formatAge(ageMs) {
|
|
48
|
+
if (!Number.isFinite(ageMs) || ageMs < 0)
|
|
49
|
+
return '';
|
|
50
|
+
const seconds = Math.floor(ageMs / 1000);
|
|
51
|
+
if (seconds < 60)
|
|
52
|
+
return `${seconds}s ago`;
|
|
53
|
+
const minutes = Math.floor(seconds / 60);
|
|
54
|
+
if (minutes < 60)
|
|
55
|
+
return `${minutes}m ago`;
|
|
56
|
+
return `${Math.floor(minutes / 60)}h ago`;
|
|
57
|
+
}
|
|
58
|
+
/** `''` for a bare `/task`, `'refresh'` for `/task refresh`, else the raw argument. */
|
|
59
|
+
export function parseTaskArgs(args) {
|
|
60
|
+
const raw = args.trim();
|
|
61
|
+
if (raw.length === 0)
|
|
62
|
+
return { action: 'show', raw };
|
|
63
|
+
if (raw.toLowerCase() === 'refresh')
|
|
64
|
+
return { action: 'refresh', raw };
|
|
65
|
+
return { action: 'unknown', raw };
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The command body: parse, report, or regenerate.
|
|
69
|
+
*
|
|
70
|
+
* NAMED FOR WHAT IT DOES, not for the port it touches (review fix RF-5): the old
|
|
71
|
+
* name described a variable, and this function is the whole of `/task`.
|
|
72
|
+
*/
|
|
73
|
+
function runTaskCommand(ctx) {
|
|
74
|
+
if (!ctx.taskSummary) {
|
|
75
|
+
ctx.notify('warn', 'The task summary is not available in this build.');
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const port = ctx.taskSummary;
|
|
79
|
+
const { action, raw } = parseTaskArgs(ctx.args);
|
|
80
|
+
if (action === 'unknown') {
|
|
81
|
+
ctx.notify('warn', `Unknown argument "${raw}" - use /task or /task refresh.`);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (action === 'refresh') {
|
|
85
|
+
const anchor = port.refresh();
|
|
86
|
+
if (anchor === '') {
|
|
87
|
+
ctx.notify('info', 'No prompt to summarise yet - send one first.');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
ctx.notify('info', 'Regenerating the task summary...');
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const state = port.read();
|
|
94
|
+
for (const line of formatTaskSummaryReport({ ...state, now: Date.now() })) {
|
|
95
|
+
ctx.notify('info', line);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
export const taskCommand = {
|
|
99
|
+
name: 'task',
|
|
100
|
+
description: 'Show the header task summary (/task refresh regenerates it)',
|
|
101
|
+
run: runTaskCommand,
|
|
102
|
+
};
|
|
@@ -34,6 +34,17 @@ export declare const TEAM_CONFIG_SET_KEYS: readonly ["team.enabled", "team.maxSu
|
|
|
34
34
|
* so `jobs.waitSeconds` resolves and `jobs.mirror.enabled` would not.
|
|
35
35
|
*/
|
|
36
36
|
export declare const JOBS_CONFIG_SET_KEYS: readonly ["jobs.enabled", "jobs.waitSeconds", "jobs.stallSeconds", "jobs.persistLog", "jobs.noticeSteer"];
|
|
37
|
+
/**
|
|
38
|
+
* Config keys under `taskSummary.` that `aragon config set` accepts
|
|
39
|
+
* (tui-shortcut-drawer-and-task-summary §4.8).
|
|
40
|
+
*
|
|
41
|
+
* BOTH SWITCHES ARE WRITABLE, which is why this section needs the same pair every
|
|
42
|
+
* other writable section has: a membership set here AND a case in `cli.tsx`'s
|
|
43
|
+
* `runConfigSet`. Only the second makes the key actually write; only the first
|
|
44
|
+
* makes it pass the `Unknown config key` gate. `config.test.ts`'s static guard is
|
|
45
|
+
* what catches a half-done pair, and `jobs` is the precedent it was written for.
|
|
46
|
+
*/
|
|
47
|
+
export declare const TASK_SUMMARY_CONFIG_SET_KEYS: readonly ["taskSummary.enabled", "taskSummary.fallback"];
|
|
37
48
|
/**
|
|
38
49
|
* Config keys under `retry.` that `aragon config set` accepts
|
|
39
50
|
* (llm-api-retry-backoff §8.2).
|
|
@@ -108,6 +119,18 @@ export declare function applyLogConfigSet(key: string, value: string): Partial<P
|
|
|
108
119
|
* recorded in `store.ts`.
|
|
109
120
|
*/
|
|
110
121
|
export declare function applyJobsConfigSet(key: string, value: string): Partial<PersistedConfig> | null;
|
|
122
|
+
/**
|
|
123
|
+
* Translate a `taskSummary.*` dotted key into a patch, or `null` for anything else.
|
|
124
|
+
*
|
|
125
|
+
* THE PATCH IS A PARTIAL SECTION, safe only because `updatePersistedConfig`
|
|
126
|
+
* deep-merges `taskSummary` - the eighth time that trap is recorded in `store.ts`.
|
|
127
|
+
* Both values are booleans, so `isTrue` is the whole parser. It accepts `1` and
|
|
128
|
+
* `true` only, which is NARROWER than the env layer's `on`/`yes` on purpose: an
|
|
129
|
+
* environment switch is typed by a human at a shell prompt, while `config set` is
|
|
130
|
+
* also driven by scripts, and `config set taskSummary.enabled on` should fail loudly
|
|
131
|
+
* rather than silently mean `false`.
|
|
132
|
+
*/
|
|
133
|
+
export declare function applyTaskSummaryConfigSet(key: string, value: string): Partial<PersistedConfig> | null;
|
|
111
134
|
/**
|
|
112
135
|
* Translate a `team.*` dotted key into a patch, or `null` for anything else.
|
|
113
136
|
*
|
|
@@ -67,6 +67,20 @@ export const JOBS_CONFIG_SET_KEYS = [
|
|
|
67
67
|
'jobs.persistLog',
|
|
68
68
|
'jobs.noticeSteer',
|
|
69
69
|
];
|
|
70
|
+
/**
|
|
71
|
+
* Config keys under `taskSummary.` that `aragon config set` accepts
|
|
72
|
+
* (tui-shortcut-drawer-and-task-summary §4.8).
|
|
73
|
+
*
|
|
74
|
+
* BOTH SWITCHES ARE WRITABLE, which is why this section needs the same pair every
|
|
75
|
+
* other writable section has: a membership set here AND a case in `cli.tsx`'s
|
|
76
|
+
* `runConfigSet`. Only the second makes the key actually write; only the first
|
|
77
|
+
* makes it pass the `Unknown config key` gate. `config.test.ts`'s static guard is
|
|
78
|
+
* what catches a half-done pair, and `jobs` is the precedent it was written for.
|
|
79
|
+
*/
|
|
80
|
+
export const TASK_SUMMARY_CONFIG_SET_KEYS = [
|
|
81
|
+
'taskSummary.enabled',
|
|
82
|
+
'taskSummary.fallback',
|
|
83
|
+
];
|
|
70
84
|
/**
|
|
71
85
|
* Config keys under `retry.` that `aragon config set` accepts
|
|
72
86
|
* (llm-api-retry-backoff §8.2).
|
|
@@ -219,6 +233,28 @@ export function applyJobsConfigSet(key, value) {
|
|
|
219
233
|
return null;
|
|
220
234
|
}
|
|
221
235
|
}
|
|
236
|
+
/**
|
|
237
|
+
* Translate a `taskSummary.*` dotted key into a patch, or `null` for anything else.
|
|
238
|
+
*
|
|
239
|
+
* THE PATCH IS A PARTIAL SECTION, safe only because `updatePersistedConfig`
|
|
240
|
+
* deep-merges `taskSummary` - the eighth time that trap is recorded in `store.ts`.
|
|
241
|
+
* Both values are booleans, so `isTrue` is the whole parser. It accepts `1` and
|
|
242
|
+
* `true` only, which is NARROWER than the env layer's `on`/`yes` on purpose: an
|
|
243
|
+
* environment switch is typed by a human at a shell prompt, while `config set` is
|
|
244
|
+
* also driven by scripts, and `config set taskSummary.enabled on` should fail loudly
|
|
245
|
+
* rather than silently mean `false`.
|
|
246
|
+
*/
|
|
247
|
+
export function applyTaskSummaryConfigSet(key, value) {
|
|
248
|
+
const patch = (taskSummary) => ({ taskSummary });
|
|
249
|
+
switch (key) {
|
|
250
|
+
case 'taskSummary.enabled':
|
|
251
|
+
return patch({ enabled: isTrue(value) });
|
|
252
|
+
case 'taskSummary.fallback':
|
|
253
|
+
return patch({ fallback: isTrue(value) });
|
|
254
|
+
default:
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
222
258
|
/**
|
|
223
259
|
* Translate a `team.*` dotted key into a patch, or `null` for anything else.
|
|
224
260
|
*
|
package/dist/config/env.js
CHANGED
|
@@ -308,16 +308,29 @@ export function readEnvConfig() {
|
|
|
308
308
|
partial.jobs = section;
|
|
309
309
|
}
|
|
310
310
|
// ARAGON_PROMPTS=0 is the kill switch for the whole quick-prompt surface: no
|
|
311
|
-
// `Ctrl+P
|
|
312
|
-
//
|
|
313
|
-
//
|
|
314
|
-
//
|
|
311
|
+
// `Ctrl+P` and `/prompts` reports that the feature is off (D-19 / AC-31). ONLY
|
|
312
|
+
// `enabled` IS REACHABLE HERE. `insertMode` is a preference a human sets once, and
|
|
313
|
+
// an environment variable that could silently flip a fill into "overwrite what you
|
|
314
|
+
// had typed" is not a kill switch.
|
|
315
315
|
const prompts = process.env.ARAGON_PROMPTS?.trim().toLowerCase();
|
|
316
316
|
if (prompts !== undefined && prompts.length > 0) {
|
|
317
317
|
partial.prompts = {
|
|
318
318
|
enabled: prompts === '1' || prompts === 'true' || prompts === 'on' || prompts === 'yes',
|
|
319
319
|
};
|
|
320
320
|
}
|
|
321
|
+
// ARAGON_TASK_SUMMARY is the header label's kill switch
|
|
322
|
+
// (tui-shortcut-drawer-and-task-summary §4.8 / P2-7), and it maps `enabled` and
|
|
323
|
+
// NOTHING ELSE - the same wording and the same reason as the block above. A
|
|
324
|
+
// variable that could also flip `fallback` would let an environment decide
|
|
325
|
+
// whether a FAILED call shows the user's own words or an empty cell, which is a
|
|
326
|
+
// silent change to what the header claims to know.
|
|
327
|
+
const taskSummary = process.env.ARAGON_TASK_SUMMARY?.trim().toLowerCase();
|
|
328
|
+
if (taskSummary !== undefined && taskSummary.length > 0) {
|
|
329
|
+
partial.taskSummary = {
|
|
330
|
+
enabled: taskSummary === '1' || taskSummary === 'true'
|
|
331
|
+
|| taskSummary === 'on' || taskSummary === 'yes',
|
|
332
|
+
};
|
|
333
|
+
}
|
|
321
334
|
const retry = process.env.ARAGON_RETRY?.trim().toLowerCase();
|
|
322
335
|
const retryMax = process.env.ARAGON_RETRY_MAX?.trim();
|
|
323
336
|
if ((retry !== undefined && retry.length > 0) || (retryMax !== undefined && retryMax.length > 0)) {
|
package/dist/config/load.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import process from 'node:process';
|
|
9
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, clampPromptsConfig, clampUpdateConfig, clampTheme, clampThinkingLevel, clampContextWindow, clampMaxTokens, clampMaxRenderInterval, clampScrollMode, clampScrollResumeMs, clampTranscriptRetain, clampTranscriptWindow, coercePositiveInt, isAutoToken, isSkillsToolPolicyMode, legacyTeamCeilingPatch, parseThresholdInput, } from './schema.js';
|
|
10
|
+
import { DEFAULT_CONFIG, DEFAULT_SCROLL_MODE, IDLE_TIMEOUT_MARGIN_MS, clampAskRounds, clampDensity, clampHumanTimeout, clampCompactionConfig, clampLogConfig, clampFastConfig, clampRetryConfig, clampSkillsConfig, clampTeamConfig, clampTodoConfig, clampBashConfig, clampJobsConfig, clampPromptsConfig, clampTaskSummaryConfig, clampUpdateConfig, clampTheme, clampThinkingLevel, clampContextWindow, clampMaxTokens, clampMaxRenderInterval, clampScrollMode, clampScrollResumeMs, clampTranscriptRetain, clampTranscriptWindow, coercePositiveInt, isAutoToken, isSkillsToolPolicyMode, legacyTeamCeilingPatch, parseThresholdInput, } from './schema.js';
|
|
11
11
|
import { loadDotenv, readEnvConfig, readEnvDisabledSkills } from './env.js';
|
|
12
12
|
import { readConfigFile, updatePersistedConfig } from './store.js';
|
|
13
13
|
import { projectModelProfiles, resolveModelProfileState, resolveModelRoleKey, } from './model-profile-resolution.js';
|
|
@@ -738,6 +738,11 @@ export function loadConfig(flags = {}, options = {}) {
|
|
|
738
738
|
// two different shapes on `CliConfig`.
|
|
739
739
|
prompts: clampPromptsConfig({ ...DEFAULT_CONFIG.prompts, ...(env.partial.prompts ?? {}),
|
|
740
740
|
...(file.prompts ?? {}) }),
|
|
741
|
+
// The header task label's two switches, composed EXACTLY like `prompts` above:
|
|
742
|
+
// env + file + clamp, so an `ARAGON_TASK_SUMMARY=0` and a file-only
|
|
743
|
+
// `taskSummary.enabled: false` cannot arrive as two different shapes.
|
|
744
|
+
taskSummary: clampTaskSummaryConfig({ ...DEFAULT_CONFIG.taskSummary,
|
|
745
|
+
...(env.partial.taskSummary ?? {}), ...(file.taskSummary ?? {}) }),
|
|
741
746
|
cwd,
|
|
742
747
|
color,
|
|
743
748
|
colorLevel,
|