@aragon-agent/cli 0.6.16 → 0.6.18
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 +107 -51
- package/README.md +91 -48
- package/dist/agent/controller.js +52 -47
- package/dist/agent/headless.js +7 -0
- package/dist/agent/reducer.js +0 -4
- package/dist/cli.js +0 -13
- package/dist/commands/builtins.js +21 -20
- package/dist/commands/index-build.d.ts +39 -0
- package/dist/commands/index-build.js +61 -0
- package/dist/compaction/child.d.ts +10 -8
- package/dist/compaction/child.js +23 -25
- package/dist/compaction/limits.d.ts +0 -2
- package/dist/compaction/limits.js +0 -2
- package/dist/config/cli-commands.d.ts +2 -2
- package/dist/config/cli-commands.js +19 -15
- package/dist/config/load.d.ts +0 -17
- package/dist/config/load.js +0 -6
- package/dist/config/schema.d.ts +41 -9
- package/dist/config/schema.js +18 -11
- package/dist/exec/events.d.ts +16 -9
- package/dist/exec/events.js +2 -2
- package/dist/exec/runner.js +17 -1
- package/dist/fast/limits.d.ts +2 -2
- package/dist/fast/limits.js +2 -2
- package/dist/fast/prompt.d.ts +10 -11
- package/dist/fast/prompt.js +19 -20
- package/dist/fast/reviewer.d.ts +1 -1
- package/dist/fast/reviewer.js +1 -1
- package/dist/fast/types.d.ts +0 -2
- package/dist/fast/wiring.d.ts +18 -34
- package/dist/fast/wiring.js +20 -51
- package/dist/input/csiu-keys.js +13 -2
- package/dist/input/limits.d.ts +15 -0
- package/dist/input/limits.js +15 -0
- package/dist/input/win32-input-mode.js +7 -1
- package/dist/logging/install.d.ts +9 -0
- package/dist/logging/install.js +11 -0
- package/dist/session/validate-session.js +3 -1
- package/dist/team/child-reviewer.d.ts +105 -0
- package/dist/team/child-reviewer.js +117 -0
- package/dist/team/human-queue.d.ts +11 -0
- package/dist/team/human-queue.js +18 -1
- package/dist/team/limits.d.ts +40 -0
- package/dist/team/limits.js +41 -0
- package/dist/team/normalize.d.ts +7 -26
- package/dist/team/normalize.js +9 -22
- package/dist/team/overseer.d.ts +157 -0
- package/dist/team/overseer.js +348 -0
- package/dist/team/prompt.d.ts +0 -11
- package/dist/team/prompt.js +3 -12
- package/dist/team/report.d.ts +4 -19
- package/dist/team/report.js +27 -28
- package/dist/team/runtime.d.ts +76 -16
- package/dist/team/runtime.js +410 -57
- package/dist/team/subagent.d.ts +48 -19
- package/dist/team/subagent.js +88 -31
- package/dist/team/task-tool.d.ts +6 -26
- package/dist/team/task-tool.js +17 -48
- package/dist/team/types.d.ts +65 -41
- package/dist/ui/App.d.ts +1 -1
- package/dist/ui/App.js +64 -4
- package/dist/ui/BottomStatusRow.js +2 -1
- package/dist/ui/Header.js +13 -2
- package/dist/ui/PromptInput.js +11 -1
- package/dist/ui/StatusBar.js +2 -1
- package/dist/ui/TeamPanel.js +1 -1
- package/dist/ui/entries/TeamCard.js +1 -6
- package/dist/ui/layout/status-detail-layout.js +12 -2
- package/dist/ui/layout/status-layout.d.ts +4 -2
- package/dist/ui/layout/status-layout.js +24 -1
- package/dist/ui/overlays/HelpOverlay.js +2 -2
- package/package.json +2 -2
- package/skills/project-indexer/SKILL.md +163 -0
- package/skills/project-indexer/templates/cleancode-overrides.md +46 -0
- package/skills/project-indexer/templates/cleancode-template.md +94 -0
- package/skills/project-indexer/templates/config-template.md +69 -0
- package/skills/project-indexer/templates/index-template.md +129 -0
- package/dist/ui/ModeChip.d.ts +0 -18
- package/dist/ui/ModeChip.js +0 -24
package/dist/agent/controller.js
CHANGED
|
@@ -12,7 +12,9 @@ import { createBuiltinTools } from '../tools/index.js';
|
|
|
12
12
|
import { createFileChangeStore } from '../tools/file-change-store.js';
|
|
13
13
|
import { createToolOutputStore, } from '../tools/tool-output-store.js';
|
|
14
14
|
import { TeamRuntime } from '../team/runtime.js';
|
|
15
|
+
import { createChildReviewer } from '../team/child-reviewer.js';
|
|
15
16
|
import { TeamHumanQueue } from '../team/human-queue.js';
|
|
17
|
+
import { createFastOverseerProvider } from '../team/overseer.js';
|
|
16
18
|
import { createTaskTool } from '../team/task-tool.js';
|
|
17
19
|
import { buildTeamBlock } from '../team/prompt.js';
|
|
18
20
|
import { buildFastBlock } from '../fast/prompt.js';
|
|
@@ -161,14 +163,14 @@ export class AgentController {
|
|
|
161
163
|
// Fast model tier (fast-model-tier §3.1 / §3.3)
|
|
162
164
|
//
|
|
163
165
|
// ONE FIELD AND FOUR THIN FORWARDERS. The flag pair, the tier cache, the
|
|
164
|
-
// reviewer's lifetime and the
|
|
166
|
+
// reviewer's lifetime and the shared fail-fast review transport all live in
|
|
165
167
|
// `fast/wiring.ts`, which is a BUDGET decision rather than an aesthetic one
|
|
166
168
|
// (C-12 / RV-5): `CLAUDE.md` caps a source file at 1000 lines and this one had
|
|
167
169
|
// 40 to spare when the design was written.
|
|
168
170
|
//
|
|
169
171
|
// `null` when the tier could not be resolved at construction, which is the
|
|
170
|
-
// byte-identity branch: no reviewer is subscribed
|
|
171
|
-
//
|
|
172
|
+
// byte-identity branch: no reviewer is subscribed and `<fast_tier>` is
|
|
173
|
+
// never spliced (I-2).
|
|
172
174
|
// -----------------------------------------------------------------------
|
|
173
175
|
fast;
|
|
174
176
|
// -----------------------------------------------------------------------
|
|
@@ -311,8 +313,8 @@ export class AgentController {
|
|
|
311
313
|
this.todos = this.todoRegistered ? new TodoStore() : null;
|
|
312
314
|
// --- Fast model tier ---------------------------------------------------
|
|
313
315
|
//
|
|
314
|
-
// RESOLVED BEFORE THE
|
|
315
|
-
// `
|
|
316
|
+
// RESOLVED BEFORE THE AGENT, because `fastRegistered` decides whether the
|
|
317
|
+
// `FastWiring` (reviewer, tier cache) exists at all and the wiring is built
|
|
316
318
|
// ONCE (C-2). `FastWiring` re-runs the same pure function in its own
|
|
317
319
|
// constructor; the two agree by construction because they read the same
|
|
318
320
|
// config through the same resolver.
|
|
@@ -324,24 +326,47 @@ export class AgentController {
|
|
|
324
326
|
this.teamRuntime = this.teamRegistered
|
|
325
327
|
? new TeamRuntime({
|
|
326
328
|
getConfig: () => this.config,
|
|
327
|
-
// Read LIVE
|
|
328
|
-
//
|
|
329
|
-
//
|
|
330
|
-
// settings-screen edit reaches the next dispatch.
|
|
329
|
+
// Read LIVE: the closure is only ever called from inside a dispatch,
|
|
330
|
+
// and `childFactory()` re-reads `compaction.subagents` on every call
|
|
331
|
+
// so a settings-screen edit reaches the next dispatch.
|
|
331
332
|
contextManagerFor: (req) => this.compaction?.childFactory()?.(req),
|
|
332
|
-
//
|
|
333
|
-
//
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
333
|
+
// PER-CHILD FAST REVIEW (main-agent parity): one reviewer per
|
|
334
|
+
// child, sharing the lead wiring's tier, policy and fail-fast
|
|
335
|
+
// transport. `this.fast` is null until after the Agent exists; these
|
|
336
|
+
// closures run only from inside a dispatch, long after construction.
|
|
337
|
+
review: {
|
|
338
|
+
// EVERY `this.fast` READ LIVES INSIDE A CLOSURE: the wiring is
|
|
339
|
+
// assigned AFTER this constructor block (it needs the Agent), and
|
|
340
|
+
// a read in the constructor's own flow would be a use-before-assign.
|
|
341
|
+
// The closures run only from inside a dispatch, long after
|
|
342
|
+
// construction, when the field has its final value.
|
|
343
|
+
active: () => this.fast !== null && this.fast.available() && this.config.fast.review,
|
|
344
|
+
modelId: () => {
|
|
345
|
+
const tier = this.fast?.getTier();
|
|
346
|
+
return tier && tier.ok ? tier.ref.modelId : '';
|
|
344
347
|
},
|
|
348
|
+
create: (request) => this.fast
|
|
349
|
+
? createChildReviewer(request, {
|
|
350
|
+
getConfig: () => this.config,
|
|
351
|
+
hasKey: (id, role) => this.hasApiKey(id, role),
|
|
352
|
+
getApiKey: (id, role) => this.resolveKey(id, role),
|
|
353
|
+
complete: (providerId, llmRequest) => this.fast.completeViaFastRegistry(providerId, llmRequest),
|
|
354
|
+
available: () => this.fast?.available() === true,
|
|
355
|
+
})
|
|
356
|
+
: undefined,
|
|
357
|
+
},
|
|
358
|
+
// THE DISPATCH SUPERVISOR (team-overseer): the fast tier that powers the
|
|
359
|
+
// per-child reviewers also powers the supervisor one-shot decision
|
|
360
|
+
// calls. Same live-predicate discipline as review above - every
|
|
361
|
+
// this.fast read lives in a closure that only runs from inside a
|
|
362
|
+
// dispatch, long after the wiring exists.
|
|
363
|
+
overseer: createFastOverseerProvider({
|
|
364
|
+
getConfig: () => this.config,
|
|
365
|
+
hasKey: (id, role) => this.hasApiKey(id, role),
|
|
366
|
+
getApiKey: (id, role) => this.resolveKey(id, role),
|
|
367
|
+
complete: (providerId, llmRequest) => this.fast.completeViaFastRegistry(providerId, llmRequest),
|
|
368
|
+
available: () => this.fast?.available() === true,
|
|
369
|
+
}),
|
|
345
370
|
providerRegistry: this.providerRegistry,
|
|
346
371
|
getCwd: () => this.cwd,
|
|
347
372
|
getMode: () => this.effectiveMode,
|
|
@@ -488,23 +513,12 @@ export class AgentController {
|
|
|
488
513
|
runtime: this.teamRuntime,
|
|
489
514
|
// Read LIVE: `/team off` flips the flag, it cannot unregister.
|
|
490
515
|
isTeamEnabled: () => this.teamEnabled,
|
|
491
|
-
// FROZEN AT CONSTRUCTION (C-2). `/fast on` in a session launched
|
|
492
|
-
// without the tier cannot add a schema property, and it says so
|
|
493
|
-
// rather than advertising one that is not there.
|
|
494
|
-
fastRegistered,
|
|
495
|
-
// Read LIVE at dispatch time (RV-3): the tier can stop resolving
|
|
496
|
-
// mid-session, and the normalizer and the subagent factory must
|
|
497
|
-
// agree about that at the same instant.
|
|
498
|
-
fastAvailable: () => this.fast?.delegationAvailable() === true,
|
|
499
516
|
maxSubagents: () => this.config.team.maxSubagents,
|
|
500
517
|
hasApiKey: () => this.hasApiKey(),
|
|
501
518
|
activeProvider: () => this.config.provider,
|
|
519
|
+
// Every child runs the lead's own model, so the lead's cost table
|
|
520
|
+
// is the one honest price for the dispatch total.
|
|
502
521
|
modelCost: () => this.getModelInfo().cost,
|
|
503
|
-
fastModelCost: () => {
|
|
504
|
-
const ref = this.fast?.fastRef();
|
|
505
|
-
return ref ? this.getModelInfoFor(ref).cost : undefined;
|
|
506
|
-
},
|
|
507
|
-
fastPricingUnknown: () => this.fast?.snapshot().pricingUnknown === true,
|
|
508
522
|
withPausedWatchdog: (fn) => this.withPausedWatchdog(fn),
|
|
509
523
|
}),
|
|
510
524
|
]
|
|
@@ -603,6 +617,10 @@ export class AgentController {
|
|
|
603
617
|
// number only bites BECAUSE `task` races `ctx.signal` — the
|
|
604
618
|
// executor's timeout is cooperative, so an override is inert for any
|
|
605
619
|
// tool that does not listen (I-2).
|
|
620
|
+
//
|
|
621
|
+
// `dispatchTimeoutMs: 0` (the default) reaches the executor as an
|
|
622
|
+
// override of `0` = NO TIMER AT ALL: the dispatch lasts as long as its
|
|
623
|
+
// slowest child, and only an Esc ends it early (main-agent parity).
|
|
606
624
|
...(this.teamRegistered ? { task: config.team.dispatchTimeoutMs } : {}),
|
|
607
625
|
},
|
|
608
626
|
},
|
|
@@ -672,13 +690,6 @@ export class AgentController {
|
|
|
672
690
|
});
|
|
673
691
|
if (this.fast) {
|
|
674
692
|
this.rebuildSystemPrompt();
|
|
675
|
-
// Fast-tier children are counted from the outcome rather than from the
|
|
676
|
-
// specs, so a downgraded child is not miscounted as a saving (§3.6).
|
|
677
|
-
this.teamRuntime?.subscribe((event) => {
|
|
678
|
-
if (event.type !== 'dispatch_end')
|
|
679
|
-
return;
|
|
680
|
-
this.fast?.noteDelegated(event.outcome.runs.filter((r) => r.tier === 'fast').length);
|
|
681
|
-
});
|
|
682
693
|
}
|
|
683
694
|
// ONE LINE FOR THE TWO CONFIGURATION MISTAKES THAT ARE OTHERWISE INVISIBLE
|
|
684
695
|
// (§3.3): the tier is on but unnamed, or named against a provider with no
|
|
@@ -738,11 +749,6 @@ export class AgentController {
|
|
|
738
749
|
? buildTeamBlock({
|
|
739
750
|
maxSubagents: config.team.maxSubagents,
|
|
740
751
|
maxConcurrent: config.team.maxConcurrent,
|
|
741
|
-
// One cross-reference sentence, present only when the capability
|
|
742
|
-
// is (§3.8). `this.fast` is null during the FIRST compose (the
|
|
743
|
-
// wiring needs the Agent), which is why the constructor composes
|
|
744
|
-
// again once it exists.
|
|
745
|
-
fastDelegation: !!fastAvailable && config.fast.delegate,
|
|
746
752
|
})
|
|
747
753
|
: '',
|
|
748
754
|
// BOTH FLAGS, exactly as team mode uses both, and for the identical
|
|
@@ -762,7 +768,6 @@ export class AgentController {
|
|
|
762
768
|
fastBlock: fastAvailable
|
|
763
769
|
? buildFastBlock({
|
|
764
770
|
model: tier.ok ? tier.ref.modelId : '',
|
|
765
|
-
delegate: config.fast.delegate,
|
|
766
771
|
review: config.fast.review,
|
|
767
772
|
})
|
|
768
773
|
: '',
|
package/dist/agent/headless.js
CHANGED
|
@@ -116,6 +116,13 @@ export async function runHeadless(controller, prompt, options = {}) {
|
|
|
116
116
|
}
|
|
117
117
|
return;
|
|
118
118
|
}
|
|
119
|
+
if (event.type === 'overseer') {
|
|
120
|
+
// ONE LINE PER DECISION, on stderr with the rest of the team feed:
|
|
121
|
+
// a machine consumer reads the JSON events; a human watching a long
|
|
122
|
+
// dispatch reads who the supervisor touched and why.
|
|
123
|
+
err.write(`[team] supervisor ${event.label}: ${event.decision.action} (${event.trigger}) ${event.decision.reason}\n`);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
119
126
|
if (event.type === 'dispatch_end') {
|
|
120
127
|
const ok = event.outcome.runs.filter((r) => r.phase === 'done' && !r.error).length;
|
|
121
128
|
err.write(`[team] done ${ok}/${event.outcome.runs.length} ok` +
|
package/dist/agent/reducer.js
CHANGED
|
@@ -860,10 +860,6 @@ export function viewReducer(state, action) {
|
|
|
860
860
|
const runs = action.specs.map((spec) => ({
|
|
861
861
|
label: spec.label,
|
|
862
862
|
description: spec.description,
|
|
863
|
-
// Already downgraded by the normalizer when the tier was unavailable, so
|
|
864
|
-
// the card and the report agree from the first frame (fast-model-tier
|
|
865
|
-
// §3.4).
|
|
866
|
-
tier: spec.tier,
|
|
867
863
|
phase: 'queued',
|
|
868
864
|
turns: 0,
|
|
869
865
|
toolCalls: 0,
|
package/dist/cli.js
CHANGED
|
@@ -160,11 +160,6 @@ function toFlags(opts) {
|
|
|
160
160
|
fastModel: opts.fastModel,
|
|
161
161
|
fastProvider: opts.fastProvider,
|
|
162
162
|
fastReview: opts.fastReview,
|
|
163
|
-
// …and again for `--fast-delegate` / `--no-fast-delegate`, one feature
|
|
164
|
-
// later — NINE features, one trap. It is `fast.delegate`'s only channel:
|
|
165
|
-
// there is no `ARAGON_FAST_DELEGATE`, so missing this line leaves a wrapper
|
|
166
|
-
// with no way at all to say "do not delegate on this run".
|
|
167
|
-
fastDelegate: opts.fastDelegate,
|
|
168
163
|
// `--verbose` is just a shorthand for `--log-level debug`; an explicit
|
|
169
164
|
// `--log-level` still wins, so the two can be combined without surprise.
|
|
170
165
|
logLevel: opts.logLevel ?? (opts.verbose ? 'debug' : undefined),
|
|
@@ -1537,14 +1532,6 @@ function buildProgram() {
|
|
|
1537
1532
|
.option('--fast-model <id>', 'The fast model id (implies nothing about --fast)')
|
|
1538
1533
|
.option('--fast-provider <id>', 'Provider for the fast model (default: the main provider)')
|
|
1539
1534
|
.option('--fast-review <n|off>', 'Turns between automatic fast reviews, or "off"')
|
|
1540
|
-
// BOTH FORMS, POSITIVE FIRST, and here it matters more than anywhere above:
|
|
1541
|
-
// `fast.delegate` is PERSISTED and defaults to TRUE, so declaring only
|
|
1542
|
-
// `--no-fast-delegate` would make commander default `opts.fastDelegate` to
|
|
1543
|
-
// `true` and force `delegate: true` on EVERY run that passed no flag at all
|
|
1544
|
-
// — silently overwriting a user's stored `false`. Same polarity, same trap
|
|
1545
|
-
// and same fix as `--update` / `--compaction`.
|
|
1546
|
-
.option('--fast-delegate', 'Let the fast tier take mechanical sub-steps (the default)')
|
|
1547
|
-
.option('--no-fast-delegate', 'Run every sub-step on the main model')
|
|
1548
1535
|
// BOTH FORMS, positive first, for the reason recorded seven times above —
|
|
1549
1536
|
// and this is the pair whose omission would be worst. `update.mode` is
|
|
1550
1537
|
// PERSISTED and defaults to `'auto'`, so a lone `--no-update` would make
|
|
@@ -106,8 +106,7 @@ function formatFastStatus(controller) {
|
|
|
106
106
|
else {
|
|
107
107
|
lines.push(`Fast tier: ${state}. Model: ${cfg.model || '(not set)'}.`);
|
|
108
108
|
}
|
|
109
|
-
lines.push(`Review: ${cfg.review ? `every ${cfg.reviewEveryTurns} turns` : 'off'}
|
|
110
|
-
`Delegation: ${cfg.delegate ? 'on' : 'off'}.`);
|
|
109
|
+
lines.push(`Review: ${cfg.review ? `every ${cfg.reviewEveryTurns} turns` : 'off'}.`);
|
|
111
110
|
const snapshot = status.snapshot;
|
|
112
111
|
const cost = snapshot.pricingUnknown
|
|
113
112
|
? `unknown (no price table for ${snapshot.model})`
|
|
@@ -124,7 +123,6 @@ function formatFastStatus(controller) {
|
|
|
124
123
|
// numbers are `0` placeholders and `reviews >= reviewBudget` would be true.
|
|
125
124
|
const budget = snapshot.budgetReached ? ' (budget reached - raise with /fast budget <n>)' : '';
|
|
126
125
|
lines.push(`This session: ${snapshot.reviews}/${snapshot.reviewBudget} reviews${budget}, ` +
|
|
127
|
-
`${snapshot.delegated} delegated subagents, ` +
|
|
128
126
|
`in ${formatTokens(snapshot.usage.inputTokens)} / out ` +
|
|
129
127
|
`${formatTokens(snapshot.usage.outputTokens)}, cost ${cost}.`);
|
|
130
128
|
return lines.join('\n');
|
|
@@ -256,7 +254,7 @@ const COMMANDS = [
|
|
|
256
254
|
},
|
|
257
255
|
{
|
|
258
256
|
name: 'team',
|
|
259
|
-
description: 'Team subagents: status | on | off | max <n>',
|
|
257
|
+
description: 'Team subagents: status | on | off | max <n> | overseer on|off',
|
|
260
258
|
/**
|
|
261
259
|
* `/team on` CANNOT turn team mode on in a session that started without it,
|
|
262
260
|
* and this command must not pretend otherwise (§4.5 / D-17 / P0-2).
|
|
@@ -296,7 +294,7 @@ const COMMANDS = [
|
|
|
296
294
|
? `\nRunning: ${snapshot.runs.map((r) => `${r.label} (${r.phase})`).join(', ')}`
|
|
297
295
|
: '';
|
|
298
296
|
ctx.notify('info', `Team mode: ${state}. Max subagents: ${cfg.maxSubagents}, ` +
|
|
299
|
-
`concurrent: ${cfg.maxConcurrent}.${roster}`);
|
|
297
|
+
`concurrent: ${cfg.maxConcurrent}, supervisor: ${cfg.overseer ? 'on' : 'off'} (needs the fast tier).${roster}`);
|
|
300
298
|
return;
|
|
301
299
|
}
|
|
302
300
|
if (verb === 'on' || verb === 'off') {
|
|
@@ -333,13 +331,28 @@ const COMMANDS = [
|
|
|
333
331
|
ctx.toast('success', `Max subagents set to ${applied.maxSubagents}.`);
|
|
334
332
|
return;
|
|
335
333
|
}
|
|
336
|
-
|
|
334
|
+
if (verb === 'overseer') {
|
|
335
|
+
if (value !== 'on' && value !== 'off') {
|
|
336
|
+
ctx.notify('warn', 'Usage: /team overseer on|off');
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
// Read LIVE per dispatch by the runtime, so no rebuild is needed - the
|
|
340
|
+
// same reason this command does not touch the tool array (its own header
|
|
341
|
+
// comment). Clamped by `setTeamConfig` and again on the persist path.
|
|
342
|
+
const applied = controller.setTeamConfig({ overseer: value === 'on' });
|
|
343
|
+
ctx.persistConfig({
|
|
344
|
+
team: { overseer: applied.overseer },
|
|
345
|
+
});
|
|
346
|
+
ctx.toast('success', `Dispatch supervisor ${applied.overseer ? 'on' : 'off'}.`);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
ctx.notify('warn', `Unknown argument "${verb}" - use /team [status|on|off|max <n>|overseer on|off].`);
|
|
337
350
|
},
|
|
338
351
|
},
|
|
339
352
|
{
|
|
340
353
|
name: 'fast',
|
|
341
354
|
description: 'Fast model tier: status | on | off | model <id> | provider <id> | same | ' +
|
|
342
|
-
'review <n|off> | budget <n>
|
|
355
|
+
'review <n|off> | budget <n>',
|
|
343
356
|
/**
|
|
344
357
|
* `/fast on` CANNOT turn the tier on in a session that started without it,
|
|
345
358
|
* for the reason `/team on` above records at length: the tool array is built
|
|
@@ -516,20 +529,8 @@ const COMMANDS = [
|
|
|
516
529
|
`${snapshot.reviews} used.`);
|
|
517
530
|
return;
|
|
518
531
|
}
|
|
519
|
-
if (verb === 'delegate') {
|
|
520
|
-
const raw = (value ?? '').trim().toLowerCase();
|
|
521
|
-
if (raw !== 'on' && raw !== 'off') {
|
|
522
|
-
ctx.notify('warn', 'Usage: /fast delegate <on|off>');
|
|
523
|
-
return;
|
|
524
|
-
}
|
|
525
|
-
if (!persist({ delegate: raw === 'on' }))
|
|
526
|
-
return;
|
|
527
|
-
ctx.toast('success', `Fast delegation ${raw}.`);
|
|
528
|
-
return;
|
|
529
|
-
}
|
|
530
532
|
ctx.notify('warn', `Unknown argument "${verb}" - use /fast ` +
|
|
531
|
-
'[status|on|off|model <id>|provider <id>|same|review <n|off>|budget <n
|
|
532
|
-
'delegate on|off].');
|
|
533
|
+
'[status|on|off|model <id>|provider <id>|same|review <n|off>|budget <n>].');
|
|
533
534
|
},
|
|
534
535
|
},
|
|
535
536
|
{
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ctrl+I project-index trigger (bundled `project-indexer` skill).
|
|
3
|
+
*
|
|
4
|
+
* WHY A MODULE AND NOT INLINE IN `App.tsx`. `App.tsx` is a documented
|
|
5
|
+
* historical oversize file (see `.claude-index/config.md`); the convention
|
|
6
|
+
* those entries record is that a feature only wires into it and keeps its
|
|
7
|
+
* logic in a small named module. Everything here is pure and unit-tested:
|
|
8
|
+
* the key handler in `App.tsx` guards, confirms, and dispatches; this file
|
|
9
|
+
* owns the words and the names.
|
|
10
|
+
*
|
|
11
|
+
* THE TRIGGER IS A SKILL COMMAND, NOT A REIMPLEMENTATION. Ctrl+I dispatches
|
|
12
|
+
* `/skill:project-indexer <args>` through the ordinary command path, so the
|
|
13
|
+
* load/activate/queue-frame/submit lifecycle is the SAME one a typed command
|
|
14
|
+
* gets, and the whole indexing run streams into the transcript like any
|
|
15
|
+
* agent turn. All strings here are ASCII-only: `commands/` is inside the
|
|
16
|
+
* `glyphs.test.ts` scan scope and every byte reaches a terminal.
|
|
17
|
+
*/
|
|
18
|
+
/** The bundled skill Ctrl+I runs. Bundled skills are read-only by scope. */
|
|
19
|
+
export declare const INDEX_SKILL_NAME = "project-indexer";
|
|
20
|
+
/** True when a previous run left a main index file behind (rebuild, not first build). */
|
|
21
|
+
export declare function hasProjectIndex(cwd: string): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Args handed to `/skill:project-indexer`. They land in the skill body's
|
|
24
|
+
* `$ARGUMENTS` placeholder, so the model sees WHY the turn started - a
|
|
25
|
+
* confirmed keypress, not a typed request - and skips asking "proceed?" a
|
|
26
|
+
* second time (the skill's own edge-case list says exactly that).
|
|
27
|
+
*/
|
|
28
|
+
export declare function indexBuildInvocationArgs(hasExistingIndex: boolean): string;
|
|
29
|
+
export interface IndexConfirmInput {
|
|
30
|
+
cwd: string;
|
|
31
|
+
/** True when `.claude-index/index.md` already exists (rebuild, not first build). */
|
|
32
|
+
hasExistingIndex: boolean;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The Yes/No dialog body. It names WHAT WILL BE WRITTEN - an index run is a
|
|
36
|
+
* batch of file writes the user never typed, and the one question a confirm
|
|
37
|
+
* gate must answer is "what did I just agree to?".
|
|
38
|
+
*/
|
|
39
|
+
export declare function buildIndexConfirmSummary(input: IndexConfirmInput): string;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ctrl+I project-index trigger (bundled `project-indexer` skill).
|
|
3
|
+
*
|
|
4
|
+
* WHY A MODULE AND NOT INLINE IN `App.tsx`. `App.tsx` is a documented
|
|
5
|
+
* historical oversize file (see `.claude-index/config.md`); the convention
|
|
6
|
+
* those entries record is that a feature only wires into it and keeps its
|
|
7
|
+
* logic in a small named module. Everything here is pure and unit-tested:
|
|
8
|
+
* the key handler in `App.tsx` guards, confirms, and dispatches; this file
|
|
9
|
+
* owns the words and the names.
|
|
10
|
+
*
|
|
11
|
+
* THE TRIGGER IS A SKILL COMMAND, NOT A REIMPLEMENTATION. Ctrl+I dispatches
|
|
12
|
+
* `/skill:project-indexer <args>` through the ordinary command path, so the
|
|
13
|
+
* load/activate/queue-frame/submit lifecycle is the SAME one a typed command
|
|
14
|
+
* gets, and the whole indexing run streams into the transcript like any
|
|
15
|
+
* agent turn. All strings here are ASCII-only: `commands/` is inside the
|
|
16
|
+
* `glyphs.test.ts` scan scope and every byte reaches a terminal.
|
|
17
|
+
*/
|
|
18
|
+
import { existsSync } from 'node:fs';
|
|
19
|
+
import { join } from 'node:path';
|
|
20
|
+
/** The bundled skill Ctrl+I runs. Bundled skills are read-only by scope. */
|
|
21
|
+
export const INDEX_SKILL_NAME = 'project-indexer';
|
|
22
|
+
/** True when a previous run left a main index file behind (rebuild, not first build). */
|
|
23
|
+
export function hasProjectIndex(cwd) {
|
|
24
|
+
try {
|
|
25
|
+
return existsSync(join(cwd, '.claude-index', 'index.md'));
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// A cwd that cannot be probed simply has no index to reuse.
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Args handed to `/skill:project-indexer`. They land in the skill body's
|
|
34
|
+
* `$ARGUMENTS` placeholder, so the model sees WHY the turn started - a
|
|
35
|
+
* confirmed keypress, not a typed request - and skips asking "proceed?" a
|
|
36
|
+
* second time (the skill's own edge-case list says exactly that).
|
|
37
|
+
*/
|
|
38
|
+
export function indexBuildInvocationArgs(hasExistingIndex) {
|
|
39
|
+
return hasExistingIndex
|
|
40
|
+
? 'The user pressed Ctrl+I and confirmed. Rebuild the project index for this workspace now, reusing the existing .claude-index/config.md preferences.'
|
|
41
|
+
: 'The user pressed Ctrl+I and confirmed. Build the project index for this workspace now, asking the configuration questions first.';
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The Yes/No dialog body. It names WHAT WILL BE WRITTEN - an index run is a
|
|
45
|
+
* batch of file writes the user never typed, and the one question a confirm
|
|
46
|
+
* gate must answer is "what did I just agree to?".
|
|
47
|
+
*/
|
|
48
|
+
export function buildIndexConfirmSummary(input) {
|
|
49
|
+
const action = input.hasExistingIndex ? 'Rebuild' : 'Build';
|
|
50
|
+
const existing = input.hasExistingIndex
|
|
51
|
+
? '\nThe existing .claude-index/config.md preferences are reused.'
|
|
52
|
+
: '\nThe bundled skill asks a few configuration questions first.';
|
|
53
|
+
return (`${action} the project index for this workspace?\n` +
|
|
54
|
+
`\nRuns the bundled "${INDEX_SKILL_NAME}" skill as a normal agent turn:` +
|
|
55
|
+
`\n- scans the workspace (exclusions apply), extracts exported symbols` +
|
|
56
|
+
`\n- writes .claude-index/index.md and .claude-index/config.md` +
|
|
57
|
+
`\n- updates CLAUDE.md (index section + Clean Code Guidelines)` +
|
|
58
|
+
`${existing}` +
|
|
59
|
+
`\nWorkspace: ${input.cwd}` +
|
|
60
|
+
`\nEvery step is visible in the transcript; Esc interrupts.`);
|
|
61
|
+
}
|
|
@@ -13,15 +13,17 @@
|
|
|
13
13
|
* turn the child already ran, and receives one partial sentence for all of it.
|
|
14
14
|
* Two fast-model summarizations is the cheap side of that trade.
|
|
15
15
|
*
|
|
16
|
-
* A CHILD MANAGER
|
|
17
|
-
* overlay
|
|
16
|
+
* SINCE MAIN-AGENT PARITY, A CHILD MANAGER RUNS THE LEAD'S OWN POLICY. The only
|
|
17
|
+
* overlay left is the CONNECTION (provider / model / base URL / manual window),
|
|
18
|
+
* which must follow the model the child actually runs on. `keepRecentTurns`,
|
|
19
|
+
* `onFailure`, `useFastTier`, `archive` and the per-run compaction bound are all
|
|
20
|
+
* read from the SAME live config the lead reads, so a user who tunes compaction
|
|
21
|
+
* tunes every child identically - the requirement "same mode and configuration
|
|
22
|
+
* as the main agent", taken literally.
|
|
18
23
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* path by design (`limits.ts` header). Without it the child silently
|
|
23
|
-
* inherits the lead's 5 - two and a half times the bound the reversal above
|
|
24
|
-
* rests on.
|
|
24
|
+
* THE INSTANCE BOUND stays (mechanism B of RV-2) because `maxPerRun` is
|
|
25
|
+
* STRUCTURAL with no config path by design (`limits.ts` header); it is now the
|
|
26
|
+
* SAME `COMPACTION_LIMITS.maxPerRun` the lead's own manager runs under.
|
|
25
27
|
*
|
|
26
28
|
* NO UI. A child's compaction produces no transcript card (DH-7): the lead's
|
|
27
29
|
* transcript describes the LEAD's context, and a card there would claim the
|
package/dist/compaction/child.js
CHANGED
|
@@ -13,15 +13,17 @@
|
|
|
13
13
|
* turn the child already ran, and receives one partial sentence for all of it.
|
|
14
14
|
* Two fast-model summarizations is the cheap side of that trade.
|
|
15
15
|
*
|
|
16
|
-
* A CHILD MANAGER
|
|
17
|
-
* overlay
|
|
16
|
+
* SINCE MAIN-AGENT PARITY, A CHILD MANAGER RUNS THE LEAD'S OWN POLICY. The only
|
|
17
|
+
* overlay left is the CONNECTION (provider / model / base URL / manual window),
|
|
18
|
+
* which must follow the model the child actually runs on. `keepRecentTurns`,
|
|
19
|
+
* `onFailure`, `useFastTier`, `archive` and the per-run compaction bound are all
|
|
20
|
+
* read from the SAME live config the lead reads, so a user who tunes compaction
|
|
21
|
+
* tunes every child identically - the requirement "same mode and configuration
|
|
22
|
+
* as the main agent", taken literally.
|
|
18
23
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* path by design (`limits.ts` header). Without it the child silently
|
|
23
|
-
* inherits the lead's 5 - two and a half times the bound the reversal above
|
|
24
|
-
* rests on.
|
|
24
|
+
* THE INSTANCE BOUND stays (mechanism B of RV-2) because `maxPerRun` is
|
|
25
|
+
* STRUCTURAL with no config path by design (`limits.ts` header); it is now the
|
|
26
|
+
* SAME `COMPACTION_LIMITS.maxPerRun` the lead's own manager runs under.
|
|
25
27
|
*
|
|
26
28
|
* NO UI. A child's compaction produces no transcript card (DH-7): the lead's
|
|
27
29
|
* transcript describes the LEAD's context, and a card there would claim the
|
|
@@ -45,11 +47,11 @@ export function createChildContextManager(req, deps) {
|
|
|
45
47
|
* which is the same reason `subagent.ts` reuses the lead's `getApiKey` closure
|
|
46
48
|
* rather than the resolved key.
|
|
47
49
|
*
|
|
48
|
-
* `useFastTier
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
50
|
+
* `useFastTier` is read LIVE from the base config (main-agent
|
|
51
|
+
* parity - no overlay): it expresses a PREFERENCE, not an outcome.
|
|
52
|
+
* `resolveFastTier` still decides, and when nothing resolves the child
|
|
53
|
+
* falls back exactly as the lead does - to its OWN model, because
|
|
54
|
+
* `provider` / `model` are overlaid here too.
|
|
53
55
|
* Failed summaries preserve the child's task just as they preserve the lead's.
|
|
54
56
|
*/
|
|
55
57
|
const childConfig = () => {
|
|
@@ -63,16 +65,11 @@ export function createChildContextManager(req, deps) {
|
|
|
63
65
|
baseUrl: req.model.baseUrl,
|
|
64
66
|
// The lead's manual limit describes its connection, not every worker.
|
|
65
67
|
contextWindow: sameConnection ? base.contextWindow : null,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
// ONE ARCHIVE PER LEAD COMPACTION IS AUDITABLE; TWENTY PER DISPATCH IS
|
|
72
|
-
// NOISE. The child's dropped slice is therefore never retained either -
|
|
73
|
-
// `runCompaction` reads this key before it stashes anything.
|
|
74
|
-
archive: false,
|
|
75
|
-
},
|
|
68
|
+
// NO POLICY OVERLAY (main-agent parity): `keepRecentTurns`, `onFailure`,
|
|
69
|
+
// `useFastTier` and `archive` are read LIVE from the base config, so the
|
|
70
|
+
// child compacts under exactly the policy the lead compacts under. A
|
|
71
|
+
// settings-screen edit mid-dispatch reaches a running child because this
|
|
72
|
+
// view is re-read on every call.
|
|
76
73
|
};
|
|
77
74
|
};
|
|
78
75
|
const meter = new ContextMeter({
|
|
@@ -121,8 +118,9 @@ export function createChildContextManager(req, deps) {
|
|
|
121
118
|
// user's transcript with no card to explain which agent it is about.
|
|
122
119
|
notify: () => { },
|
|
123
120
|
},
|
|
124
|
-
// MECHANISM B - the instance bound (RV-2 / DH-16).
|
|
125
|
-
|
|
121
|
+
// MECHANISM B - the instance bound (RV-2 / DH-16). The SAME bound the
|
|
122
|
+
// lead's own manager runs under (main-agent parity).
|
|
123
|
+
{ maxPerRun: COMPACTION_LIMITS.maxPerRun });
|
|
126
124
|
return {
|
|
127
125
|
// THERE IS NO `/compact` FOR A CHILD, so nothing is ever queued and the
|
|
128
126
|
// manual branch of `shouldCompact` is unreachable by construction.
|
|
@@ -28,8 +28,6 @@ export declare const COMPACTION_LIMITS: {
|
|
|
28
28
|
readonly archiveMaxFiles: 20;
|
|
29
29
|
readonly archiveMaxAgeMs: 604800000;
|
|
30
30
|
readonly archiveMaxBytes: 8000000;
|
|
31
|
-
readonly childKeepRecentTurns: 2;
|
|
32
|
-
readonly childMaxPerRun: 2;
|
|
33
31
|
readonly cardTextRows: 6;
|
|
34
32
|
readonly statusCompactCols: 100;
|
|
35
33
|
};
|
|
@@ -20,7 +20,7 @@ export declare const LOG_CONFIG_SET_KEYS: readonly ["log.level", "log.toFile", "
|
|
|
20
20
|
* switch falls through every case, writes nothing, and still prints
|
|
21
21
|
* `Set team.enabled = false`.
|
|
22
22
|
*/
|
|
23
|
-
export declare const TEAM_CONFIG_SET_KEYS: readonly ["team.enabled", "team.maxSubagents", "team.maxConcurrent", "team.subagentTimeoutMs", "team.dispatchTimeoutMs", "team.maxTurnsPerSubagent"];
|
|
23
|
+
export declare const TEAM_CONFIG_SET_KEYS: readonly ["team.enabled", "team.maxSubagents", "team.maxConcurrent", "team.subagentTimeoutMs", "team.dispatchTimeoutMs", "team.maxTurnsPerSubagent", "team.overseer"];
|
|
24
24
|
/**
|
|
25
25
|
* Config keys under `retry.` that `aragon config set` accepts
|
|
26
26
|
* (llm-api-retry-backoff §8.2).
|
|
@@ -42,7 +42,7 @@ export declare const RETRY_CONFIG_SET_KEYS: readonly ["retry.enabled", "retry.ma
|
|
|
42
42
|
* would silently never take effect, and `config set` would still print
|
|
43
43
|
* `Set fast.model = haiku`.
|
|
44
44
|
*/
|
|
45
|
-
export declare const FAST_CONFIG_SET_KEYS: readonly ["fast.enabled", "fast.provider", "fast.model", "fast.baseUrl", "fast.thinkingLevel", "fast.
|
|
45
|
+
export declare const FAST_CONFIG_SET_KEYS: readonly ["fast.enabled", "fast.provider", "fast.model", "fast.baseUrl", "fast.thinkingLevel", "fast.review", "fast.reviewEveryTurns", "fast.reviewContextTurns", "fast.reviewMaxChars", "fast.reviewMaxPerSession"];
|
|
46
46
|
/**
|
|
47
47
|
* Config keys under `update.` that `aragon config set` accepts (cli-auto-update
|
|
48
48
|
* §4.1).
|