@aragon-agent/cli 0.6.34 → 0.6.36
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 +21 -0
- package/README.md +173 -7
- package/dist/agent/agent-mode.d.ts +20 -4
- package/dist/agent/agent-mode.js +20 -4
- package/dist/agent/controller.d.ts +67 -3
- package/dist/agent/controller.js +106 -5
- package/dist/agent/headless.js +31 -0
- package/dist/agent/kit-apply.d.ts +56 -0
- package/dist/agent/kit-apply.js +209 -0
- package/dist/agent/model-profile-settings.d.ts +28 -0
- package/dist/agent/model-profile-settings.js +142 -29
- package/dist/agent/reducer.d.ts +13 -2
- package/dist/agent/reducer.js +1 -0
- package/dist/cli.js +12 -0
- package/dist/commands/agents-command.d.ts +15 -0
- package/dist/commands/agents-command.js +27 -0
- package/dist/commands/builtins.js +135 -4
- package/dist/commands/kit-command.d.ts +20 -0
- package/dist/commands/kit-command.js +131 -0
- package/dist/commands/perf.d.ts +11 -0
- package/dist/commands/perf.js +2 -1
- package/dist/commands/registry.d.ts +14 -0
- package/dist/config/app-paths.d.ts +15 -0
- package/dist/config/app-paths.js +33 -2
- package/dist/config/env.d.ts +4 -1
- package/dist/config/env.js +28 -2
- package/dist/config/kit-resolution.d.ts +93 -0
- package/dist/config/kit-resolution.js +226 -0
- package/dist/config/load.d.ts +10 -0
- package/dist/config/load.js +8 -1
- package/dist/config/model-kits.d.ts +224 -0
- package/dist/config/model-kits.js +302 -0
- package/dist/config/model-profile-store.d.ts +25 -0
- package/dist/config/model-profiles.d.ts +41 -3
- package/dist/config/model-profiles.js +46 -5
- package/dist/config/schema.d.ts +84 -0
- package/dist/config/schema.js +39 -0
- package/dist/config/store.js +12 -1
- package/dist/config/ui-state.d.ts +18 -0
- package/dist/config/ui-state.js +9 -0
- package/dist/exec/events.d.ts +25 -0
- package/dist/exec/events.js +4 -0
- package/dist/exec/index.js +16 -1
- package/dist/exec/runner.js +10 -0
- package/dist/input/keymap.js +18 -4
- package/dist/proc/limits.d.ts +16 -0
- package/dist/proc/limits.js +16 -0
- package/dist/proc/supervisor.d.ts +20 -1
- package/dist/proc/supervisor.js +42 -14
- package/dist/prompts/command.d.ts +34 -0
- package/dist/prompts/command.js +237 -0
- package/dist/prompts/copy.d.ts +89 -0
- package/dist/prompts/copy.js +131 -0
- package/dist/prompts/fill.d.ts +51 -0
- package/dist/prompts/fill.js +46 -0
- package/dist/prompts/limits.d.ts +53 -0
- package/dist/prompts/limits.js +53 -0
- package/dist/prompts/normalize.d.ts +53 -0
- package/dist/prompts/normalize.js +190 -0
- package/dist/prompts/resolve.d.ts +31 -0
- package/dist/prompts/resolve.js +60 -0
- package/dist/prompts/search.d.ts +58 -0
- package/dist/prompts/search.js +146 -0
- package/dist/prompts/store.d.ts +86 -0
- package/dist/prompts/store.js +453 -0
- package/dist/prompts/types.d.ts +204 -0
- package/dist/prompts/types.js +13 -0
- package/dist/session/checkpoint.d.ts +8 -1
- package/dist/session/checkpoint.js +6 -0
- package/dist/session/persist.d.ts +19 -1
- package/dist/session/persist.js +13 -1
- package/dist/session/startup-session.d.ts +7 -2
- package/dist/session/startup-session.js +5 -2
- package/dist/session/store.js +3 -0
- package/dist/session/validate-session.js +64 -1
- package/dist/team/limits.d.ts +25 -0
- package/dist/team/limits.js +26 -0
- package/dist/team/runtime.js +27 -2
- package/dist/team/settled.d.ts +57 -0
- package/dist/team/settled.js +87 -0
- package/dist/team/subagent.js +48 -2
- package/dist/team/timeline.d.ts +74 -0
- package/dist/team/timeline.js +101 -0
- package/dist/team/types.d.ts +24 -1
- package/dist/todo/acceptance.d.ts +120 -0
- package/dist/todo/acceptance.js +399 -0
- package/dist/todo/follow-through.d.ts +48 -23
- package/dist/todo/follow-through.js +110 -44
- package/dist/todo/limits.d.ts +62 -1
- package/dist/todo/limits.js +62 -1
- package/dist/todo/panel-rows.d.ts +29 -39
- package/dist/todo/panel-rows.js +37 -83
- package/dist/todo/plan-sections.d.ts +60 -0
- package/dist/todo/plan-sections.js +108 -0
- package/dist/todo/prompt.d.ts +17 -12
- package/dist/todo/prompt.js +59 -40
- package/dist/todo/row-window.d.ts +51 -0
- package/dist/todo/row-window.js +107 -0
- package/dist/todo/store.d.ts +139 -31
- package/dist/todo/store.js +271 -73
- package/dist/todo/todo-tool.d.ts +33 -8
- package/dist/todo/todo-tool.js +186 -30
- package/dist/todo/types.d.ts +58 -0
- package/dist/ui/App.js +414 -54
- package/dist/ui/Composer.d.ts +6 -2
- package/dist/ui/Composer.js +2 -2
- package/dist/ui/Header.d.ts +17 -1
- package/dist/ui/Header.js +8 -2
- package/dist/ui/PromptInput.d.ts +21 -1
- package/dist/ui/PromptInput.js +22 -2
- package/dist/ui/ScrollbackShell.d.ts +22 -7
- package/dist/ui/ScrollbackShell.js +8 -1
- package/dist/ui/StatusBar.d.ts +18 -0
- package/dist/ui/StatusBar.js +11 -3
- package/dist/ui/TeamPanel.d.ts +35 -41
- package/dist/ui/TeamPanel.js +53 -163
- package/dist/ui/TodoPanel.d.ts +17 -14
- package/dist/ui/TodoPanel.js +146 -38
- package/dist/ui/Transcript.js +23 -3
- package/dist/ui/composer-input.d.ts +10 -0
- package/dist/ui/composer-input.js +57 -24
- package/dist/ui/entries/TeamCard.d.ts +18 -2
- package/dist/ui/entries/TeamCard.js +47 -13
- package/dist/ui/entries/TodoCard.d.ts +26 -11
- package/dist/ui/entries/TodoCard.js +56 -15
- package/dist/ui/glyphs.d.ts +19 -0
- package/dist/ui/glyphs.js +4 -0
- package/dist/ui/layout/agents-board.d.ts +102 -0
- package/dist/ui/layout/agents-board.js +389 -0
- package/dist/ui/layout/cells.d.ts +61 -0
- package/dist/ui/layout/cells.js +156 -0
- package/dist/ui/layout/header-hints.d.ts +23 -3
- package/dist/ui/layout/header-hints.js +73 -7
- package/dist/ui/layout/kit-chip.d.ts +50 -0
- package/dist/ui/layout/kit-chip.js +81 -0
- package/dist/ui/layout/status-detail-layout.js +9 -0
- package/dist/ui/layout/status-layout.d.ts +13 -0
- package/dist/ui/layout/team-row.d.ts +139 -0
- package/dist/ui/layout/team-row.js +246 -0
- package/dist/ui/layout/todo-layout.d.ts +38 -0
- package/dist/ui/layout/todo-layout.js +75 -0
- package/dist/ui/layout/virtual-window.js +32 -3
- package/dist/ui/overlays/AgentsBoardOverlay.d.ts +57 -0
- package/dist/ui/overlays/AgentsBoardOverlay.js +183 -0
- package/dist/ui/overlays/HelpOverlay.js +24 -2
- package/dist/ui/overlays/KitEditor.d.ts +23 -0
- package/dist/ui/overlays/KitEditor.js +101 -0
- package/dist/ui/overlays/KitManager.d.ts +23 -0
- package/dist/ui/overlays/KitManager.js +104 -0
- package/dist/ui/overlays/KitPicker.d.ts +49 -0
- package/dist/ui/overlays/KitPicker.js +58 -0
- package/dist/ui/overlays/ModelProfilePicker.js +16 -6
- package/dist/ui/overlays/PromptEditorPage.d.ts +48 -0
- package/dist/ui/overlays/PromptEditorPage.js +227 -0
- package/dist/ui/overlays/PromptLibraryOverlay.d.ts +37 -0
- package/dist/ui/overlays/PromptLibraryOverlay.js +191 -0
- package/dist/ui/overlays/PromptPicker.d.ts +36 -0
- package/dist/ui/overlays/PromptPicker.js +129 -0
- package/dist/ui/overlays/PromptTextArea.d.ts +86 -0
- package/dist/ui/overlays/PromptTextArea.js +141 -0
- package/dist/ui/overlays/SettingsScreen.js +139 -16
- package/dist/ui/overlays/kit-picker-state.d.ts +87 -0
- package/dist/ui/overlays/kit-picker-state.js +136 -0
- package/dist/ui/overlays/kit-settings-state.d.ts +129 -0
- package/dist/ui/overlays/kit-settings-state.js +267 -0
- package/dist/ui/overlays/model-profile-state.d.ts +26 -3
- package/dist/ui/overlays/model-profile-state.js +59 -6
- package/dist/ui/overlays/prompt-library-state.d.ts +162 -0
- package/dist/ui/overlays/prompt-library-state.js +335 -0
- package/dist/ui/overlays/prompt-picker-state.d.ts +71 -0
- package/dist/ui/overlays/prompt-picker-state.js +98 -0
- package/dist/ui/scrollback/LiveTail.d.ts +2 -2
- package/dist/ui/scrollback/LiveTail.js +7 -2
- package/dist/ui/scrollback/band.js +14 -1
- package/dist/ui/scrollback/live-preview.js +10 -3
- package/dist/ui/scrollback/tail-window.d.ts +12 -0
- package/dist/ui/scrollback/tail-window.js +17 -2
- package/dist/ui/scrollback/use-scrollback-view.js +7 -0
- package/dist/ui/team-status.d.ts +166 -0
- package/dist/ui/team-status.js +252 -0
- package/dist/ui/transcript-text.js +13 -4
- package/dist/ui/use-startup-notices.d.ts +35 -1
- package/dist/ui/use-startup-notices.js +29 -2
- package/dist/ui/use-wheel-routing.js +10 -2
- package/package.json +2 -2
|
@@ -4,7 +4,10 @@ import { mergePersistedConfig } from '../config/store.js';
|
|
|
4
4
|
import { adaptLegacyConnectionPatch, assertLegacyFastTarget, commitModelSettings, readModelSettingsDisk, } from '../config/model-profile-store.js';
|
|
5
5
|
import { projectModelProfiles, resolveModelProfileState } from '../config/model-profile-resolution.js';
|
|
6
6
|
import { validateModelProfiles } from '../config/model-profiles.js';
|
|
7
|
+
import { applyHeldToLive, carrySessionKit } from '../config/kit-resolution.js';
|
|
8
|
+
import { findKit } from '../config/model-kits.js';
|
|
7
9
|
import { registerProfileSecrets } from '../logging/secret-registry.js';
|
|
10
|
+
import { getLogger } from '../logging/logger.js';
|
|
8
11
|
/** Capture both baselines when the editor opens, without migrating or writing anything. */
|
|
9
12
|
export function createModelSettingsDraft(live, liveRevision) {
|
|
10
13
|
const disk = readModelSettingsDisk();
|
|
@@ -15,6 +18,11 @@ export function createModelSettingsDraft(live, liveRevision) {
|
|
|
15
18
|
profiles: disk.config?.modelProfiles
|
|
16
19
|
? structuredClone(disk.config.modelProfiles) : undefined,
|
|
17
20
|
activateRoles: [], patch: {},
|
|
21
|
+
// SEEDED FROM THE LIVE CONFIG (RV-P0-2). Without this line `draft.sessionKit`
|
|
22
|
+
// is always `undefined`, the `undefined` case below means "release", and any
|
|
23
|
+
// save made from the settings screen would silently drop a session kit the
|
|
24
|
+
// user just applied - the exact opposite of "this session only".
|
|
25
|
+
sessionKit: live.sessionKit ? structuredClone(live.sessionKit) : undefined,
|
|
18
26
|
...(!disk.ok ? { readError: 'Cannot read config. Repair it with aragon config edit.' } : {}),
|
|
19
27
|
};
|
|
20
28
|
}
|
|
@@ -61,6 +69,47 @@ function hasConnectionPatch(patch, role) {
|
|
|
61
69
|
const section = role === 'main' ? patch : patch.fast;
|
|
62
70
|
return !!section && ['provider', 'model', 'baseUrl'].some((key) => key in section);
|
|
63
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* The slots THIS transaction explicitly decided, and that therefore leave the
|
|
74
|
+
* session kit (§4.3 / D-6).
|
|
75
|
+
*
|
|
76
|
+
* Released BY SLOT, never wholesale: a user who only retargeted the subagent
|
|
77
|
+
* must not lose the main and fast choices the kit made for this session, and a
|
|
78
|
+
* user who retargeted main must not have it silently ignored. Per-slot is the
|
|
79
|
+
* only rule shaped like the data (three independent slots).
|
|
80
|
+
*/
|
|
81
|
+
export function releasedSlots(draft) {
|
|
82
|
+
const held = draft.sessionKit?.held ?? [];
|
|
83
|
+
if (!held.length)
|
|
84
|
+
return [];
|
|
85
|
+
// THE APPLY ITSELF RELEASES NOTHING (`kit-apply.ts` sets this marker). Without
|
|
86
|
+
// it the apply's own writes - `activateRoles`, `patch.team`, the re-bound
|
|
87
|
+
// library - read as "the user decided every slot", and the save would drop the
|
|
88
|
+
// three slots it was just handed.
|
|
89
|
+
if (draft.sessionKitFresh)
|
|
90
|
+
return [];
|
|
91
|
+
const kit = findKit(draft.profiles ?? draft.baseline.modelProfiles, draft.sessionKit?.id);
|
|
92
|
+
if (!kit)
|
|
93
|
+
return [...held];
|
|
94
|
+
const released = new Set();
|
|
95
|
+
for (const role of ['main', 'fast']) {
|
|
96
|
+
if (draft.activateRoles.includes(role) || connectionChanged(draft, role)
|
|
97
|
+
|| hasConnectionPatch(draft.patch, role))
|
|
98
|
+
released.add(role);
|
|
99
|
+
}
|
|
100
|
+
if (draft.patch.team !== undefined)
|
|
101
|
+
released.add('subagent');
|
|
102
|
+
return [...released].filter((slot) => held.includes(slot));
|
|
103
|
+
}
|
|
104
|
+
/** The session kit as it stands AFTER this transaction's explicit decisions. */
|
|
105
|
+
function narrowedSessionKit(draft) {
|
|
106
|
+
const current = draft.sessionKit;
|
|
107
|
+
if (!current)
|
|
108
|
+
return null;
|
|
109
|
+
const released = releasedSlots(draft);
|
|
110
|
+
const held = current.held.filter((slot) => !released.includes(slot));
|
|
111
|
+
return held.length ? { id: current.id, held } : null;
|
|
112
|
+
}
|
|
64
113
|
function liveCandidate(live, candidate, draft) {
|
|
65
114
|
const patch = draft.patch;
|
|
66
115
|
const normalizedPatch = {};
|
|
@@ -128,8 +177,16 @@ function liveCandidate(live, candidate, draft) {
|
|
|
128
177
|
next.apiKeyOverrideTarget = undefined;
|
|
129
178
|
}
|
|
130
179
|
else {
|
|
131
|
-
|
|
132
|
-
|
|
180
|
+
// I-13 (RV-P1-7): `projectModelProfiles` projects fast ONLY when the
|
|
181
|
+
// library binds one. With `fastId: null` the projection's `fast` is just the
|
|
182
|
+
// file's untouched section (`projected` starts as `{...file}`), so copying it
|
|
183
|
+
// here would resurrect the disk's fast endpoint underneath a kit that
|
|
184
|
+
// deliberately turned the tier off - the live config would then say "off"
|
|
185
|
+
// while naming a model nobody chose.
|
|
186
|
+
if (projected.modelProfiles?.fastId) {
|
|
187
|
+
next.fast = { ...next.fast, provider: projected.fast.provider,
|
|
188
|
+
model: projected.fast.model, baseUrl: projected.fast.baseUrl };
|
|
189
|
+
}
|
|
133
190
|
}
|
|
134
191
|
}
|
|
135
192
|
if (next.apiKeyOverride && !next.apiKeyOverrideTarget
|
|
@@ -139,49 +196,100 @@ function liveCandidate(live, candidate, draft) {
|
|
|
139
196
|
next.modelProfileState = resolveModelProfileState(next);
|
|
140
197
|
return next;
|
|
141
198
|
}
|
|
142
|
-
/**
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const issues = validateModelProfiles(draft.profiles ??
|
|
199
|
+
/**
|
|
200
|
+
* Build both candidates and the snapshot.
|
|
201
|
+
*
|
|
202
|
+
* TWO CANDIDATES, NAMED DIFFERENTLY ON PURPOSE (RV-P0-1 / I-14). `diskCandidate`
|
|
203
|
+
* is the commit side and never learns about a held slot; `liveCandidateInput`
|
|
204
|
+
* is what the session sees and is never committed. Collapsing them into one
|
|
205
|
+
* variable is the defect this split exists to prevent: one unrelated save would
|
|
206
|
+
* then write the session's kit into the user's permanent defaults, and nothing
|
|
207
|
+
* visible would change.
|
|
208
|
+
*/
|
|
209
|
+
function prepareModelSettings(input) {
|
|
210
|
+
const { draft, controller, current } = input;
|
|
211
|
+
const issues = validateModelProfiles(draft.profiles ?? current.modelProfiles);
|
|
155
212
|
if (issues.length)
|
|
156
|
-
return { ...rejected('invalid'), issues };
|
|
157
|
-
let snapshot;
|
|
158
|
-
let candidate;
|
|
213
|
+
return { failure: { ...rejected('invalid'), issues } };
|
|
159
214
|
try {
|
|
160
|
-
const current = disk.config ?? structuredClone(DEFAULT_CONFIG);
|
|
161
215
|
const profilesChanged = draft.activateRoles.length > 0
|
|
162
216
|
|| JSON.stringify(draft.profiles) !== JSON.stringify(draft.baseline.modelProfiles);
|
|
163
217
|
const implicitLegacy = draft.profiles === undefined || !profilesChanged;
|
|
164
218
|
const patch = adaptLegacyConnectionPatch({ current, patch: implicitLegacy
|
|
165
219
|
? draft.patch : { ...draft.patch, modelProfiles: draft.profiles } });
|
|
166
|
-
|
|
167
|
-
registerProfileSecrets(
|
|
168
|
-
const
|
|
220
|
+
const diskCandidate = mergePersistedConfig(current, patch);
|
|
221
|
+
registerProfileSecrets(diskCandidate);
|
|
222
|
+
const sessionKit = narrowedSessionKit(draft);
|
|
223
|
+
const applied = applyHeldToLive(diskCandidate, sessionKit);
|
|
224
|
+
if (applied.dropped.length) {
|
|
225
|
+
getLogger().warn('agent', 'session_kit_slots_dropped', { slots: applied.dropped });
|
|
226
|
+
}
|
|
227
|
+
const next = liveCandidate(controller.getConfig(), applied.candidate, draft);
|
|
228
|
+
next.sessionKit = sessionKit ?? undefined;
|
|
169
229
|
// Disk preferences cannot reveal startup/session gateways retained by the live main role.
|
|
170
230
|
if (implicitLegacy && current.modelProfiles?.fastId && hasConnectionPatch(draft.patch, 'fast')) {
|
|
171
231
|
assertLegacyFastTarget(next.fast, next);
|
|
172
232
|
}
|
|
173
|
-
snapshot = controller.prepareModelSettingsSnapshot(next);
|
|
233
|
+
const snapshot = controller.prepareModelSettingsSnapshot(next);
|
|
234
|
+
return { prepared: { diskCandidate, liveCandidateInput: applied.candidate, snapshot } };
|
|
174
235
|
}
|
|
175
236
|
catch (error) {
|
|
176
237
|
const code = error.code ?? 'invalid';
|
|
177
|
-
return rejected(code);
|
|
238
|
+
return { failure: rejected(code) };
|
|
178
239
|
}
|
|
179
|
-
|
|
240
|
+
}
|
|
241
|
+
/** Validate and prepare everything before the one synchronous, atomic disk commit. */
|
|
242
|
+
export function saveModelSettings(input) {
|
|
243
|
+
const { draft, controller } = input;
|
|
244
|
+
if (controller.isModelSettingsBusy())
|
|
245
|
+
return rejected('busy');
|
|
246
|
+
if (draft.liveRevision !== controller.getSettingsRevision())
|
|
247
|
+
return rejected('conflict');
|
|
248
|
+
const disk = readModelSettingsDisk();
|
|
249
|
+
if (!disk.ok || draft.readError)
|
|
250
|
+
return rejected('read_failed');
|
|
251
|
+
if (disk.revision !== draft.diskRevision)
|
|
252
|
+
return rejected('conflict');
|
|
253
|
+
const prepared = prepareModelSettings({ draft, controller,
|
|
254
|
+
current: disk.config ?? structuredClone(DEFAULT_CONFIG) });
|
|
255
|
+
if (!prepared.prepared)
|
|
256
|
+
return prepared.failure;
|
|
257
|
+
const commit = commitModelSettings({ candidate: prepared.prepared.diskCandidate,
|
|
258
|
+
diskRevision: draft.diskRevision });
|
|
180
259
|
if (!commit.ok)
|
|
181
260
|
return rejected(commit.code ?? 'write_failed');
|
|
182
|
-
return applyPreparedSnapshot(controller, snapshot, true);
|
|
261
|
+
return applyPreparedSnapshot(controller, prepared.prepared.snapshot, true);
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* The SAME prepare chain, without the commit: a session kit (§4.2 exit B).
|
|
265
|
+
*
|
|
266
|
+
* NO `diskRevision` GATE: nothing is written, so a save that landed elsewhere is
|
|
267
|
+
* not this operation's business. It still refuses while work is in flight
|
|
268
|
+
* (`busy`) and still refuses a draft whose disk could not be read, so "nothing
|
|
269
|
+
* changed" is guaranteed for every other failure too (I-10).
|
|
270
|
+
*/
|
|
271
|
+
export function previewModelSettings(draft, controller) {
|
|
272
|
+
if (controller.isModelSettingsBusy())
|
|
273
|
+
return rejected('busy');
|
|
274
|
+
if (draft.readError)
|
|
275
|
+
return rejected('read_failed');
|
|
276
|
+
const disk = readModelSettingsDisk();
|
|
277
|
+
if (!disk.ok)
|
|
278
|
+
return rejected('read_failed');
|
|
279
|
+
const prepared = prepareModelSettings({ draft, controller,
|
|
280
|
+
current: disk.config ?? structuredClone(DEFAULT_CONFIG) });
|
|
281
|
+
if (!prepared.prepared)
|
|
282
|
+
return prepared.failure;
|
|
283
|
+
return applyPreparedSnapshot(controller, prepared.prepared.snapshot, false);
|
|
183
284
|
}
|
|
184
|
-
|
|
285
|
+
/**
|
|
286
|
+
* Apply a prepared snapshot.
|
|
287
|
+
*
|
|
288
|
+
* `persisted` HAS NO DEFAULT (RV-P1-4): the one attribute that distinguishes
|
|
289
|
+
* "the file now says this" from "only this process says this" is not something
|
|
290
|
+
* a call site should be able to forget.
|
|
291
|
+
*/
|
|
292
|
+
export function applyPreparedSnapshot(controller, snapshot, persisted) {
|
|
185
293
|
try {
|
|
186
294
|
controller.applyModelSettingsSnapshot(snapshot);
|
|
187
295
|
}
|
|
@@ -220,14 +328,19 @@ export function reloadModelSettings(controller) {
|
|
|
220
328
|
if (live.unicode !== undefined)
|
|
221
329
|
resolved.unicode = live.unicode;
|
|
222
330
|
resolved.reducedMotion ||= !live.color;
|
|
331
|
+
// THE SESSION KIT SURVIVES A RELOAD (D-6): the three connections it holds
|
|
332
|
+
// must come from the kit, not from the file we just re-read, and the
|
|
333
|
+
// projection has to be recomputed so the live config stays equivalent to
|
|
334
|
+
// what its own bindings say (I-3).
|
|
335
|
+
const carried = carrySessionKit({ resolved, previous: live });
|
|
223
336
|
const after = readModelSettingsDisk();
|
|
224
337
|
if (!after.ok)
|
|
225
338
|
return rejected('read_failed');
|
|
226
339
|
if (after.revision !== disk.revision)
|
|
227
340
|
return rejected('conflict');
|
|
228
|
-
if (
|
|
341
|
+
if (carried.modelProfileState?.main.invalid)
|
|
229
342
|
return rejected('invalid');
|
|
230
|
-
return applyPreparedSnapshot(controller, controller.prepareModelSettingsSnapshot(
|
|
343
|
+
return applyPreparedSnapshot(controller, controller.prepareModelSettingsSnapshot(carried), false);
|
|
231
344
|
}
|
|
232
345
|
catch {
|
|
233
346
|
return rejected('invalid');
|
package/dist/agent/reducer.d.ts
CHANGED
|
@@ -16,13 +16,13 @@ import type { AgentMode } from './agent-mode.js';
|
|
|
16
16
|
import type { DispatchOutcome, SubagentRun, SubagentSpec, TeamSnapshot } from '../team/types.js';
|
|
17
17
|
import type { FastReview, FastSnapshot } from '../fast/types.js';
|
|
18
18
|
import type { CompactionMode, CompactionRecord, CompactionSnapshot, CompactionUiTrigger, ContextUsageSnapshot } from '../compaction/types.js';
|
|
19
|
-
import type { TodoItem, TodoSnapshot } from '../todo/types.js';
|
|
19
|
+
import type { AcceptanceSnapshot, TodoItem, TodoSnapshot } from '../todo/types.js';
|
|
20
20
|
import type { JobKind, ServiceSnapshot, ServiceStatus } from '../proc/types.js';
|
|
21
21
|
import type { RetryPhase } from './retry-view.js';
|
|
22
22
|
import type { FilePatch } from '../tools/patch.js';
|
|
23
23
|
export type NoticeLevel = 'info' | 'warn' | 'error';
|
|
24
24
|
export type ToastLevel = NoticeLevel | 'success';
|
|
25
|
-
export type Overlay = null | 'settings' | 'model' | 'help' | 'queue' | 'confirm' | 'question' | 'plan';
|
|
25
|
+
export type Overlay = null | 'settings' | 'model' | 'help' | 'queue' | 'confirm' | 'question' | 'plan' | 'kit' | 'prompts' | 'prompt-pick' | 'agents';
|
|
26
26
|
export type ToolStatus = 'pending' | 'running' | 'done' | 'error';
|
|
27
27
|
/** Ephemeral, auto-dismissed acknowledgement (spec §3.9). */
|
|
28
28
|
export interface Toast {
|
|
@@ -147,6 +147,17 @@ export type Entry = {
|
|
|
147
147
|
items: TodoItem[];
|
|
148
148
|
doneCount: number;
|
|
149
149
|
total: number;
|
|
150
|
+
/**
|
|
151
|
+
* The acceptance projection, copied from `TodoSnapshot.acceptance` on every
|
|
152
|
+
* `todoUpdate` (todo-acceptance-criteria §6.10).
|
|
153
|
+
*
|
|
154
|
+
* OPTIONAL, NOT `AcceptanceSnapshot | null` REQUIRED, and that asymmetry with
|
|
155
|
+
* the snapshot is deliberate (P1-4): `TodoSnapshot` has exactly one writer, so
|
|
156
|
+
* it can fail closed; an `Entry` does not - its sources include `entries` read
|
|
157
|
+
* back from a session file written before this feature existed, where the key
|
|
158
|
+
* is simply absent.
|
|
159
|
+
*/
|
|
160
|
+
acceptance?: AcceptanceSnapshot | null;
|
|
150
161
|
/** The turn that owns this card is still running, so it may be rewritten. */
|
|
151
162
|
live: boolean;
|
|
152
163
|
/**
|
package/dist/agent/reducer.js
CHANGED
|
@@ -1020,6 +1020,7 @@ export function viewReducer(state, action) {
|
|
|
1020
1020
|
items: action.snapshot.items,
|
|
1021
1021
|
doneCount: action.snapshot.doneCount,
|
|
1022
1022
|
total: action.snapshot.total,
|
|
1023
|
+
acceptance: action.snapshot.acceptance,
|
|
1023
1024
|
live: true,
|
|
1024
1025
|
});
|
|
1025
1026
|
// Rewrite the turn's card when there is one; otherwise append and record
|
package/dist/cli.js
CHANGED
|
@@ -170,6 +170,10 @@ function toFlags(opts) {
|
|
|
170
170
|
// C-9). A value flag is no less inert than a boolean one when this line is
|
|
171
171
|
// missing.
|
|
172
172
|
todoFollow: opts.todoFollow,
|
|
173
|
+
// Site three of three once more, for `--todo-acceptance`: a flag declared in
|
|
174
|
+
// commander and on `CliFlags` but missing HERE is inert, with no compile error
|
|
175
|
+
// and no runtime error (todo-acceptance-criteria §6.8.2).
|
|
176
|
+
todoAcceptance: opts.todoAcceptance,
|
|
173
177
|
// Site three of three for `--retry` / `--retry-max` — five features, same
|
|
174
178
|
// trap (`mouse` P1-2, `team` P1-4, `todo` C-9, render L4, now retry). A flag
|
|
175
179
|
// needs the `.option()` declaration below, a field on `CliFlags`, AND a line
|
|
@@ -1319,6 +1323,9 @@ function createSessionWriter(input) {
|
|
|
1319
1323
|
readView: input.readView,
|
|
1320
1324
|
readHistory: () => controller.getSessionSnapshot(),
|
|
1321
1325
|
readTodos: () => controller.getTodoSnapshot()?.items ?? [],
|
|
1326
|
+
// The verdicts travel with the plan; read from the STORE, not from the render
|
|
1327
|
+
// projection, so a checkpoint taken while the capability is off still saves them.
|
|
1328
|
+
readAcceptance: () => controller.getAcceptanceItems(),
|
|
1322
1329
|
// READ AT WRITE TIME: `/model` rebuilds the config, and a checkpoint that
|
|
1323
1330
|
// recorded the model from launch would name the wrong one after a switch.
|
|
1324
1331
|
readModel: () => {
|
|
@@ -2068,6 +2075,11 @@ function buildProgram() {
|
|
|
2068
2075
|
// passed, so `!== undefined` in `resolveTodoConfig` already distinguishes it
|
|
2069
2076
|
// from silence. An unrecognized mode is clamped to `notify`, not rejected.
|
|
2070
2077
|
.option('--todo-follow <mode>', 'What to do when a run ends with unfinished steps: notify (default) | auto | off')
|
|
2078
|
+
// A PAIR, and for the same reason `--todo-panel` needs one: `acceptance` is
|
|
2079
|
+
// PERSISTED, so a lone `--no-todo-acceptance` would make commander default
|
|
2080
|
+
// `opts.todoAcceptance` to `true` and overwrite a stored `false`.
|
|
2081
|
+
.option('--todo-acceptance', 'Plan acceptance criteria with every todo list (the default)')
|
|
2082
|
+
.option('--no-todo-acceptance', 'Plan steps only: no acceptance criteria, no verify phase')
|
|
2071
2083
|
// BOTH FORMS, positive first, for the reason recorded five times above:
|
|
2072
2084
|
// `retry.enabled` is PERSISTED, so a lone `--no-retry` would make commander
|
|
2073
2085
|
// default `opts.retry` to `true` and silently overwrite a stored `false`.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/agents` — open the subagent board (tui-subagent-panel §6.4).
|
|
3
|
+
*
|
|
4
|
+
* A SECOND DOOR, not a fallback. `Ctrl+B` is the fast path, but a key is undiscoverable
|
|
5
|
+
* unless the user reads `/help`, and `/agents` is what the palette advertises. Both
|
|
6
|
+
* entries share ONE guard (`state.team === null && lastTeamEntry(...) === null`) so the
|
|
7
|
+
* key and the command can never disagree about whether there is a board to show: an
|
|
8
|
+
* empty board would read as a broken feature, and the honest answer is a notice that
|
|
9
|
+
* says no dispatch has happened yet.
|
|
10
|
+
*
|
|
11
|
+
* The body lives in its own module for the reason `kit-command.ts` and
|
|
12
|
+
* `queue-command.ts` do: `builtins.ts` is already well past a thousand lines.
|
|
13
|
+
*/
|
|
14
|
+
import type { CommandRegistry } from './registry.js';
|
|
15
|
+
export declare function registerAgentsCommand(registry: CommandRegistry): void;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/agents` — open the subagent board (tui-subagent-panel §6.4).
|
|
3
|
+
*
|
|
4
|
+
* A SECOND DOOR, not a fallback. `Ctrl+B` is the fast path, but a key is undiscoverable
|
|
5
|
+
* unless the user reads `/help`, and `/agents` is what the palette advertises. Both
|
|
6
|
+
* entries share ONE guard (`state.team === null && lastTeamEntry(...) === null`) so the
|
|
7
|
+
* key and the command can never disagree about whether there is a board to show: an
|
|
8
|
+
* empty board would read as a broken feature, and the honest answer is a notice that
|
|
9
|
+
* says no dispatch has happened yet.
|
|
10
|
+
*
|
|
11
|
+
* The body lives in its own module for the reason `kit-command.ts` and
|
|
12
|
+
* `queue-command.ts` do: `builtins.ts` is already well past a thousand lines.
|
|
13
|
+
*/
|
|
14
|
+
import { lastTeamEntry } from '../ui/team-status.js';
|
|
15
|
+
export function registerAgentsCommand(registry) {
|
|
16
|
+
registry.register({
|
|
17
|
+
name: 'agents',
|
|
18
|
+
description: 'Open the subagent board: every child, its facts and its timeline',
|
|
19
|
+
run: (ctx) => {
|
|
20
|
+
if (ctx.state.team === null && lastTeamEntry(ctx.state.entries) === null) {
|
|
21
|
+
ctx.notify('info', 'No subagent dispatch yet.');
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
ctx.setOverlay('agents');
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
}
|
|
@@ -14,6 +14,7 @@ import { computeCost, formatCost, formatDuration, formatTokens } from '../agent/
|
|
|
14
14
|
import { mergePendingEntries } from '../agent/queued-messages.js';
|
|
15
15
|
import { TODO_FOLLOW_LIMITS } from '../todo/limits.js';
|
|
16
16
|
import { buildContinuationMessage } from '../todo/follow-through.js';
|
|
17
|
+
import { planIsResolved } from '../todo/acceptance.js';
|
|
17
18
|
import { MODE_LABEL, nextMode } from '../agent/agent-mode.js';
|
|
18
19
|
import { loadSession, resolveSessionPath, saveSession } from '../session/persist.js';
|
|
19
20
|
import { applyRestoredSession } from '../session/startup-session.js';
|
|
@@ -23,6 +24,9 @@ import { getLogger } from '../logging/logger.js';
|
|
|
23
24
|
import { resolveFastTier } from '../fast/resolve.js';
|
|
24
25
|
import { subagentModelSummary } from '../ui/overlays/model-profile-state.js';
|
|
25
26
|
import { registerQueueCommand } from './queue-command.js';
|
|
27
|
+
import { registerPromptCommands } from '../prompts/command.js';
|
|
28
|
+
import { registerKitCommand } from './kit-command.js';
|
|
29
|
+
import { registerAgentsCommand } from './agents-command.js';
|
|
26
30
|
import { perfCommand } from './perf.js';
|
|
27
31
|
import { runCompactCommand } from '../compaction/command.js';
|
|
28
32
|
import { runClearCommand } from './clear-command.js';
|
|
@@ -157,6 +161,102 @@ function runTeamModelCommand(ctx, subject) {
|
|
|
157
161
|
ctx.persistConfig({ team: teamPatch });
|
|
158
162
|
ctx.toast('success', `Subagent model: ${entry.name}${disambiguation} (${entry.provider}:${entry.model}).`);
|
|
159
163
|
}
|
|
164
|
+
/** Rows `/todo status` prints before it says how many it did not. */
|
|
165
|
+
const ACCEPT_STATUS_ROWS = 8;
|
|
166
|
+
/**
|
|
167
|
+
* `/todo acceptance on|off|reset`, and the bare form equal to `/todo status`'s
|
|
168
|
+
* acceptance section (todo-acceptance-criteria §6.14).
|
|
169
|
+
*
|
|
170
|
+
* EVERY MUTATING BRANCH IS "TWO CALLS PLUS PERSIST", the rule `/todo panel` and
|
|
171
|
+
* `/todo follow` already follow: `setTodoConfig` (or `setTodoAcceptanceEnabled`) is
|
|
172
|
+
* what the session sees, and `persistConfig` is what survives it. Only one of the two
|
|
173
|
+
* reports success while changing nothing.
|
|
174
|
+
*/
|
|
175
|
+
function runTodoAcceptance(ctx, value) {
|
|
176
|
+
const controller = ctx.controller;
|
|
177
|
+
if (!value || value === 'status') {
|
|
178
|
+
const lines = acceptanceStatusLines(controller.getTodoSnapshot()?.acceptance ?? null, controller.getTodoConfig().acceptance);
|
|
179
|
+
if (lines.length === 0) {
|
|
180
|
+
ctx.notify('info', 'No acceptance criteria. Todo planning: ' +
|
|
181
|
+
(controller.isTodoEnabled() ? 'on' : 'off') + '.');
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
for (const line of lines)
|
|
185
|
+
ctx.notify('info', line);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (value === 'on' || value === 'off') {
|
|
189
|
+
const acceptance = value === 'on';
|
|
190
|
+
controller.setTodoAcceptanceEnabled(acceptance);
|
|
191
|
+
ctx.persistConfig({ todo: { acceptance } });
|
|
192
|
+
ctx.toast('success', `Acceptance criteria ${acceptance ? 'on' : 'off'}.`);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (value === 'reset') {
|
|
196
|
+
if (controller.isRunning()) {
|
|
197
|
+
ctx.notify('warn', 'Cannot reset the acceptance criteria while a run is in progress.');
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
// THE COUNT COMES FROM THE STORE (a named writer, §6.2.3), never recomputed here,
|
|
201
|
+
// and `0` means the table was already fully unchecked - in which case a notice
|
|
202
|
+
// would announce that nothing happened.
|
|
203
|
+
const reopened = controller.reopenAcceptance();
|
|
204
|
+
if (reopened === 0) {
|
|
205
|
+
ctx.notify('info', 'No checked criteria to reset.');
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
ctx.toast('info', `${reopened} ${reopened === 1 ? 'criterion is' : 'criteria are'} ` +
|
|
209
|
+
'unchecked again.');
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
ctx.notify('warn', 'Usage: /todo acceptance [on|off|reset]');
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* The acceptance section of `/todo status`, as at most two notification bodies.
|
|
216
|
+
*
|
|
217
|
+
* RETURNS NOTHING WHEN THERE IS NOTHING TO SAY, which is what keeps the disabled
|
|
218
|
+
* path and the no-criteria path byte-identical to the pre-feature command (I-5).
|
|
219
|
+
* The list is bounded at eight rows by "unchecked and failed first, then the rest",
|
|
220
|
+
* with `... and N more` closing it: the full table lives in the transcript card, and
|
|
221
|
+
* a command that floods the transcript is not a status command.
|
|
222
|
+
*/
|
|
223
|
+
function acceptanceStatusLines(acceptance, enabled) {
|
|
224
|
+
if (!enabled || !acceptance || acceptance.total === 0)
|
|
225
|
+
return [];
|
|
226
|
+
const header = `Acceptance ${acceptance.checkedCount}/${acceptance.total} checked` +
|
|
227
|
+
(acceptance.failedCount > 0 ? ` (${acceptance.failedCount} failed)` : '');
|
|
228
|
+
const rows = acceptance.items
|
|
229
|
+
.map((item, index) => ({ item, index }))
|
|
230
|
+
.sort((left, right) => rank(left.item.status) - rank(right.item.status));
|
|
231
|
+
const shown = rows.slice(0, ACCEPT_STATUS_ROWS);
|
|
232
|
+
const lines = shown.map(({ item, index }) => ` ${String(index + 1).padStart(2)}. ${markerWord(item.status)} ${item.content}` +
|
|
233
|
+
(item.status === 'failed' && item.evidence.length > 0 ? `\n ${item.evidence}` : ''));
|
|
234
|
+
if (rows.length > shown.length)
|
|
235
|
+
lines.push(` ... and ${rows.length - shown.length} more`);
|
|
236
|
+
return [header, ...lines];
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Failures first, then everything still open, then the settled verdicts: `/todo status`
|
|
240
|
+
* is read to find out what is WRONG, and the eight-row budget means the order decides
|
|
241
|
+
* what survives the cut.
|
|
242
|
+
*/
|
|
243
|
+
function rank(status) {
|
|
244
|
+
if (status === 'failed')
|
|
245
|
+
return 0;
|
|
246
|
+
if (status === 'in_progress' || status === 'pending')
|
|
247
|
+
return 1;
|
|
248
|
+
return 2;
|
|
249
|
+
}
|
|
250
|
+
/** ASCII markers: this module is inside the glyph scanner's scope. */
|
|
251
|
+
function markerWord(status) {
|
|
252
|
+
if (status === 'passed')
|
|
253
|
+
return '[x]';
|
|
254
|
+
if (status === 'failed')
|
|
255
|
+
return '[!]';
|
|
256
|
+
if (status === 'in_progress')
|
|
257
|
+
return ' > ';
|
|
258
|
+
return '[ ]';
|
|
259
|
+
}
|
|
160
260
|
function formatFastStatus(controller) {
|
|
161
261
|
const status = controller.getFastStatus();
|
|
162
262
|
const cfg = controller.getFastConfig();
|
|
@@ -687,7 +787,8 @@ const COMMANDS = [
|
|
|
687
787
|
},
|
|
688
788
|
{
|
|
689
789
|
name: 'todo',
|
|
690
|
-
description: 'Todo planning: status | on | off | panel on|off | follow <mode> |
|
|
790
|
+
description: 'Todo planning: status | on | off | panel on|off | follow <mode> | ' +
|
|
791
|
+
'acceptance [on|off|reset] | clear | continue',
|
|
691
792
|
/**
|
|
692
793
|
* `/todo on` CANNOT turn planning on in a session that started with
|
|
693
794
|
* `--no-todo`, for the reason `/team on` above records at length: the tool
|
|
@@ -738,6 +839,16 @@ const COMMANDS = [
|
|
|
738
839
|
: '';
|
|
739
840
|
ctx.notify('info', `Todos: ${snapshot.doneCount}/${snapshot.total} done.${active} Panel: ${panel}. ` +
|
|
740
841
|
`Follow-through: ${follow}.`);
|
|
842
|
+
// THE ACCEPTANCE SECTION, appended and NOTHING ELSE (todo-acceptance-criteria
|
|
843
|
+
// §6.14): with no criteria the two calls above are byte-identical to the
|
|
844
|
+
// version without this feature.
|
|
845
|
+
for (const line of acceptanceStatusLines(snapshot.acceptance, todoCfg.acceptance)) {
|
|
846
|
+
ctx.notify('info', line);
|
|
847
|
+
}
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
if (verb === 'acceptance') {
|
|
851
|
+
runTodoAcceptance(ctx, value);
|
|
741
852
|
return;
|
|
742
853
|
}
|
|
743
854
|
if (verb === 'on' || verb === 'off') {
|
|
@@ -819,7 +930,10 @@ const COMMANDS = [
|
|
|
819
930
|
return;
|
|
820
931
|
}
|
|
821
932
|
const snapshot = controller.getTodoSnapshot();
|
|
822
|
-
|
|
933
|
+
// `planIsResolved`, NOT `doneCount >= total` (§6.14): in the verification
|
|
934
|
+
// phase every step is done and the command has to keep working, or the user's
|
|
935
|
+
// only manual route into the last stage is refused with "Nothing left".
|
|
936
|
+
if (!snapshot || planIsResolved(snapshot.items, snapshot.acceptance?.items ?? null)) {
|
|
823
937
|
ctx.notify('info', 'Nothing left to continue.');
|
|
824
938
|
return;
|
|
825
939
|
}
|
|
@@ -838,7 +952,8 @@ const COMMANDS = [
|
|
|
838
952
|
return;
|
|
839
953
|
}
|
|
840
954
|
ctx.notify('warn', `Unknown argument "${verb}" - use ` +
|
|
841
|
-
'/todo [status|on|off|panel on|off|follow <mode>|
|
|
955
|
+
'/todo [status|on|off|panel on|off|follow <mode>|acceptance [on|off|reset]|' +
|
|
956
|
+
'clear|continue].');
|
|
842
957
|
},
|
|
843
958
|
},
|
|
844
959
|
{
|
|
@@ -1016,6 +1131,11 @@ const COMMANDS = [
|
|
|
1016
1131
|
// `/resume` restores `messages`, so the belief that justifies the
|
|
1017
1132
|
// panel comes back — and the panel has to come back with it (§3.13).
|
|
1018
1133
|
todos: ctx.controller.getTodoSnapshot()?.items ?? [],
|
|
1134
|
+
// The verdicts travel with the plan, for the same reason: a resume that
|
|
1135
|
+
// restored the steps and dropped the conclusions would show a plan as
|
|
1136
|
+
// "not started" that the user had already watched being verified. Read
|
|
1137
|
+
// from the STORE, so a save taken while the capability is off keeps them.
|
|
1138
|
+
acceptance: ctx.controller.getAcceptanceItems(),
|
|
1019
1139
|
});
|
|
1020
1140
|
ctx.notify('info', `Saved session to ${path}`);
|
|
1021
1141
|
}
|
|
@@ -1371,6 +1491,17 @@ export function registerBuiltinCommands(registry) {
|
|
|
1371
1491
|
for (const command of COMMANDS)
|
|
1372
1492
|
registry.register(command);
|
|
1373
1493
|
registerQueueCommand(registry);
|
|
1494
|
+
// The command BODY lives in its own module (builtins.ts is already 1.5k lines);
|
|
1495
|
+
// this line is the whole registration (agent-model-kits §4.9).
|
|
1496
|
+
registerKitCommand(registry);
|
|
1497
|
+
// Same shape, one feature later (quick-prompt-library §4.3 / P1-3): the body is
|
|
1498
|
+
// `prompts/command.ts` and its capabilities arrive on `CommandContext.prompts`.
|
|
1499
|
+
// REGISTERED UNCONDITIONALLY, even with `prompts.enabled=false` - that is the only
|
|
1500
|
+
// way the command can report that the feature is off (D-19 / AC-30).
|
|
1501
|
+
registerPromptCommands(registry);
|
|
1502
|
+
// The read-only subagent board (tui-subagent-panel §6.4). Same shape again: the
|
|
1503
|
+
// body is `commands/agents-command.ts` and the registration is this one line.
|
|
1504
|
+
registerAgentsCommand(registry);
|
|
1374
1505
|
}
|
|
1375
1506
|
/**
|
|
1376
1507
|
* Every built-in command name INCLUDING aliases (`quit`), for documentation and
|
|
@@ -1380,7 +1511,7 @@ export function registerBuiltinCommands(registry) {
|
|
|
1380
1511
|
* `registry.get(name)` instead, because that is the only check that stays
|
|
1381
1512
|
* correct when commands are registered dynamically (§7.1 / P1-2).
|
|
1382
1513
|
*/
|
|
1383
|
-
export const BUILTIN_COMMAND_NAMES = ['queue', ...COMMANDS.flatMap((c) => [
|
|
1514
|
+
export const BUILTIN_COMMAND_NAMES = ['queue', 'prompts', 'agents', ...COMMANDS.flatMap((c) => [
|
|
1384
1515
|
c.name,
|
|
1385
1516
|
...(c.aliases ?? []),
|
|
1386
1517
|
])].sort();
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/kit` - the slash surface for agent model kits (agent-model-kits §4.9).
|
|
3
|
+
*
|
|
4
|
+
* ASCII ONLY - `src/commands/**` is inside `glyphs.test.ts::inScope`.
|
|
5
|
+
*
|
|
6
|
+
* WHY A COMMAND AT ALL, WHEN `ctrl+K` EXISTS: a key that a terminal, tmux, an
|
|
7
|
+
* input method or a IME can swallow needs an equivalent that cannot be swallowed
|
|
8
|
+
* (`R-4`), and the same reading is what a user pastes into an issue. The two
|
|
9
|
+
* surfaces call the SAME controller methods, so they can never diverge in
|
|
10
|
+
* behaviour - only in how the answer is rendered.
|
|
11
|
+
*
|
|
12
|
+
* THERE IS NO `/kit default` (D-7): the session path is forbidden from writing
|
|
13
|
+
* the file, so a "make this the default" verb here would be the one hole in that
|
|
14
|
+
* promise. Setting a default is a settings action, with a Save the user sees.
|
|
15
|
+
*/
|
|
16
|
+
import type { CommandRegistry, CommandContext } from './registry.js';
|
|
17
|
+
/** The command body; `registerKitCommand` is the only registration site. */
|
|
18
|
+
export declare function runKitCommand(ctx: CommandContext): void;
|
|
19
|
+
/** Registered from `builtins.ts` with one line, next to every other subsystem. */
|
|
20
|
+
export declare function registerKitCommand(registry: CommandRegistry): void;
|