pi-subagents-lite 1.0.2 → 1.1.0
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/README.md +43 -6
- package/package.json +1 -1
- package/src/agent-manager.ts +81 -62
- package/src/agent-runner.ts +194 -167
- package/src/config-io.ts +9 -1
- package/src/config-mutator.ts +183 -0
- package/src/context.ts +1 -1
- package/src/format.ts +173 -0
- package/src/index.ts +124 -176
- package/src/menus.ts +188 -136
- package/src/model-precedence.ts +5 -0
- package/src/output-file.ts +1 -68
- package/src/renderer.ts +157 -0
- package/src/result-viewer.ts +2 -1
- package/src/state.ts +80 -0
- package/src/tool-execution.ts +145 -53
- package/src/types.ts +100 -31
- package/src/ui/agent-widget.ts +148 -145
- package/src/usage.ts +5 -0
- package/src/stop-agent-tool.ts +0 -77
package/src/menus.ts
CHANGED
|
@@ -2,27 +2,46 @@
|
|
|
2
2
|
* menus.ts — /agents command menu system.
|
|
3
3
|
*
|
|
4
4
|
* All menu-related functions extracted from index.ts.
|
|
5
|
-
* Imports shared state (config, manager, piInstance) from
|
|
5
|
+
* Imports shared state (config, manager, piInstance) from state.ts.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
9
9
|
import { getAgentConfig, getAvailableTypes, getAllTypes } from "./agent-types.js";
|
|
10
10
|
import type { AgentRecord } from "./types.js";
|
|
11
|
-
import { SHORT_ID_LENGTH } from "./types.js";
|
|
11
|
+
import { SHORT_ID_LENGTH, CONFIG_AGENT_NON_MODEL_KEYS } from "./types.js";
|
|
12
12
|
import { ModelSelectorDialog, type ModelOption } from "./model-selector.js";
|
|
13
13
|
import { ResultViewer, type ResultViewerStats } from "./result-viewer.js";
|
|
14
|
-
import { getDisplayName } from "./
|
|
14
|
+
import { getDisplayName } from "./format.js";
|
|
15
15
|
import { buildSnapshotMarkdown } from "./context.js";
|
|
16
16
|
|
|
17
17
|
import { parseModelKey } from "./utils.js";
|
|
18
18
|
import {
|
|
19
19
|
__config,
|
|
20
20
|
sessionOverrides,
|
|
21
|
-
manager,
|
|
22
21
|
piInstance,
|
|
23
|
-
|
|
22
|
+
getManager,
|
|
23
|
+
} from "./state.js";
|
|
24
24
|
import { resolveModel } from "./model-precedence.js";
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
setModelOverride,
|
|
27
|
+
setDefaultModel,
|
|
28
|
+
clearModelOverride,
|
|
29
|
+
clearAllModelOverrides,
|
|
30
|
+
setForceBackground,
|
|
31
|
+
setShowCost,
|
|
32
|
+
setGraceTurns,
|
|
33
|
+
setWidgetCompact,
|
|
34
|
+
setWidgetMaxLines,
|
|
35
|
+
setWidgetMaxLinesCompact,
|
|
36
|
+
setWidgetShortcut,
|
|
37
|
+
setAgent,
|
|
38
|
+
setConcurrencyDefault,
|
|
39
|
+
setConcurrencyProvider,
|
|
40
|
+
setConcurrencyModel,
|
|
41
|
+
removeConcurrencyProvider,
|
|
42
|
+
removeConcurrencyModel,
|
|
43
|
+
resetConcurrency,
|
|
44
|
+
} from "./config-mutator.js";
|
|
26
45
|
|
|
27
46
|
// ============================================================================
|
|
28
47
|
// Helpers
|
|
@@ -113,45 +132,50 @@ async function applyModelOverride(
|
|
|
113
132
|
}
|
|
114
133
|
|
|
115
134
|
/**
|
|
116
|
-
*
|
|
117
|
-
|
|
118
|
-
function applyConcurrencyConfig(): void {
|
|
119
|
-
saveConfigAtomic(__config);
|
|
120
|
-
manager?.setConcurrency(__config.concurrency);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Parse a concurrency input: prompt, validate (integer ≥ 1), return parsed value or undefined.
|
|
135
|
+
* Prompt for numeric input, validate (integer ≥ min), return parsed value or undefined.
|
|
136
|
+
* Returns undefined if the user cancels or the value is invalid.
|
|
125
137
|
*/
|
|
126
|
-
async function
|
|
138
|
+
async function parseNumericInput(
|
|
127
139
|
ctx: ExtensionCommandContext,
|
|
128
140
|
label: string,
|
|
129
141
|
initialValue: string,
|
|
142
|
+
min: number,
|
|
143
|
+
minLabel: string,
|
|
130
144
|
): Promise<number | undefined> {
|
|
131
145
|
const input = await ctx.ui.input(label, initialValue);
|
|
132
146
|
if (input === undefined) return undefined;
|
|
133
147
|
const parsed = parseInt(input.trim(), 10);
|
|
134
|
-
if (isNaN(parsed) || parsed <
|
|
135
|
-
ctx.ui.notify(
|
|
148
|
+
if (isNaN(parsed) || parsed < min) {
|
|
149
|
+
ctx.ui.notify(`Invalid value — must be a number ${minLabel}`, "error");
|
|
136
150
|
return undefined;
|
|
137
151
|
}
|
|
138
152
|
return parsed;
|
|
139
153
|
}
|
|
140
154
|
|
|
141
155
|
/**
|
|
142
|
-
*
|
|
143
|
-
|
|
156
|
+
* Parse a concurrency input: prompt, validate (integer ≥ 1), return parsed value or undefined.
|
|
157
|
+
*/
|
|
158
|
+
async function parseConcurrencyInput(
|
|
159
|
+
ctx: ExtensionCommandContext,
|
|
160
|
+
label: string,
|
|
161
|
+
initialValue: string,
|
|
162
|
+
): Promise<number | undefined> {
|
|
163
|
+
return parseNumericInput(ctx, label, initialValue, 1, "≥ 1");
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Prompt for a concurrency value, validate, and apply via setter.
|
|
168
|
+
* The setter handles save + sync internally.
|
|
144
169
|
*/
|
|
145
170
|
async function promptConcurrencyInput(
|
|
146
171
|
ctx: ExtensionCommandContext,
|
|
147
172
|
label: string,
|
|
148
173
|
currentValue: number,
|
|
149
|
-
|
|
174
|
+
setter: (value: number) => void,
|
|
150
175
|
): Promise<void> {
|
|
151
176
|
const parsed = await parseConcurrencyInput(ctx, label, String(currentValue));
|
|
152
177
|
if (parsed === undefined) return;
|
|
153
|
-
|
|
154
|
-
applyConcurrencyConfig();
|
|
178
|
+
setter(parsed);
|
|
155
179
|
ctx.ui.notify(
|
|
156
180
|
`${label.replace("Concurrency slots for ", "")} concurrency set to ${parsed}`,
|
|
157
181
|
"info",
|
|
@@ -160,16 +184,16 @@ async function promptConcurrencyInput(
|
|
|
160
184
|
|
|
161
185
|
/**
|
|
162
186
|
* Prompt to add a new concurrency limit for a named entity.
|
|
187
|
+
* Calls the setter which handles save + sync internally.
|
|
163
188
|
*/
|
|
164
189
|
async function promptAddConcurrencyLimit(
|
|
165
190
|
ctx: ExtensionCommandContext,
|
|
166
191
|
label: string,
|
|
167
|
-
|
|
192
|
+
setter: (key: string, value: number) => void,
|
|
168
193
|
): Promise<void> {
|
|
169
194
|
const parsed = await parseConcurrencyInput(ctx, "Concurrency slots", "1");
|
|
170
195
|
if (parsed === undefined) return;
|
|
171
|
-
|
|
172
|
-
applyConcurrencyConfig();
|
|
196
|
+
setter(label, parsed);
|
|
173
197
|
ctx.ui.notify(`${label} concurrency set to ${parsed}`, "info");
|
|
174
198
|
}
|
|
175
199
|
|
|
@@ -246,13 +270,12 @@ export async function showModelSettingsMenu(
|
|
|
246
270
|
|
|
247
271
|
// Handle "clear" — remove all overrides (session + config) and save
|
|
248
272
|
if (mode === "clear") {
|
|
249
|
-
|
|
273
|
+
clearModelOverride(targetKey);
|
|
250
274
|
if (targetKey !== "default") {
|
|
251
275
|
delete sessionOverrides[targetKey];
|
|
252
276
|
} else {
|
|
253
277
|
sessionOverrides.default = null;
|
|
254
278
|
}
|
|
255
|
-
saveConfigAtomic(__config);
|
|
256
279
|
ctx.ui.notify(`${label} overrides cleared`, "info");
|
|
257
280
|
return;
|
|
258
281
|
}
|
|
@@ -264,12 +287,9 @@ export async function showModelSettingsMenu(
|
|
|
264
287
|
isSession
|
|
265
288
|
? (chosen) => { sessionOverrides[targetKey] = chosen; }
|
|
266
289
|
: (chosen) => {
|
|
267
|
-
|
|
290
|
+
setModelOverride(targetKey, chosen);
|
|
268
291
|
},
|
|
269
292
|
);
|
|
270
|
-
if (!isSession) {
|
|
271
|
-
saveConfigAtomic(__config);
|
|
272
|
-
}
|
|
273
293
|
};
|
|
274
294
|
|
|
275
295
|
// Global default — show session value if present
|
|
@@ -293,31 +313,28 @@ export async function showModelSettingsMenu(
|
|
|
293
313
|
: "Force background · OFF";
|
|
294
314
|
items.push(forceBgLabel);
|
|
295
315
|
actions.push(async () => {
|
|
296
|
-
|
|
297
|
-
saveConfigAtomic(__config);
|
|
316
|
+
setForceBackground(!__config.agent.forceBackground);
|
|
298
317
|
ctx.ui.notify(
|
|
299
318
|
`Force background ${__config.agent.forceBackground ? "ON" : "OFF"}`,
|
|
300
319
|
"info",
|
|
301
320
|
);
|
|
302
321
|
});
|
|
303
322
|
|
|
323
|
+
// Cost display toggle
|
|
324
|
+
const showCost = __config.agent.showCost === true; // default false
|
|
325
|
+
items.push(`Cost display · ${showCost ? "ON" : "OFF"}`);
|
|
326
|
+
actions.push(async () => {
|
|
327
|
+
setShowCost(!showCost);
|
|
328
|
+
ctx.ui.notify(`Cost display ${showCost ? "OFF" : "ON"}`, "info");
|
|
329
|
+
});
|
|
330
|
+
|
|
304
331
|
// Grace turns setting
|
|
305
332
|
const graceTurns = __config.agent.graceTurns ?? 6;
|
|
306
333
|
items.push(`Grace turns · ${graceTurns}`);
|
|
307
334
|
actions.push(async () => {
|
|
308
|
-
const
|
|
309
|
-
if (
|
|
310
|
-
|
|
311
|
-
if (isNaN(parsed)) {
|
|
312
|
-
ctx.ui.notify("Invalid value — must be a number", "error");
|
|
313
|
-
return;
|
|
314
|
-
}
|
|
315
|
-
if (parsed < 0) {
|
|
316
|
-
ctx.ui.notify("Invalid value — must be ≥ 0", "error");
|
|
317
|
-
return;
|
|
318
|
-
}
|
|
319
|
-
__config.agent.graceTurns = parsed;
|
|
320
|
-
saveConfigAtomic(__config);
|
|
335
|
+
const parsed = await parseNumericInput(ctx, "Grace turns (≥ 0)", String(graceTurns), 0, "≥ 0");
|
|
336
|
+
if (parsed === undefined) return;
|
|
337
|
+
setGraceTurns(parsed);
|
|
321
338
|
ctx.ui.notify(`Grace turns set to ${parsed}`, "info");
|
|
322
339
|
});
|
|
323
340
|
|
|
@@ -395,21 +412,13 @@ export async function showModelSettingsMenu(
|
|
|
395
412
|
items.push("Clear all overrides");
|
|
396
413
|
actions.push(async () => {
|
|
397
414
|
const hasOverrides = Object.entries(__config.agent).some(
|
|
398
|
-
([k, v]) => k
|
|
415
|
+
([k, v]) => !CONFIG_AGENT_NON_MODEL_KEYS.includes(k) && v != null,
|
|
399
416
|
);
|
|
400
417
|
if (!hasOverrides && __config.agent.default === null) {
|
|
401
418
|
ctx.ui.notify("No overrides to clear", "info");
|
|
402
419
|
return;
|
|
403
420
|
}
|
|
404
|
-
|
|
405
|
-
default: __config.agent.default,
|
|
406
|
-
forceBackground: __config.agent.forceBackground,
|
|
407
|
-
};
|
|
408
|
-
if (__config.agent.graceTurns != null) {
|
|
409
|
-
preserved.graceTurns = __config.agent.graceTurns;
|
|
410
|
-
}
|
|
411
|
-
__config.agent = preserved as typeof __config.agent;
|
|
412
|
-
saveConfigAtomic(__config);
|
|
421
|
+
clearAllModelOverrides();
|
|
413
422
|
ctx.ui.notify("All model overrides cleared", "info");
|
|
414
423
|
});
|
|
415
424
|
|
|
@@ -417,33 +426,48 @@ export async function showModelSettingsMenu(
|
|
|
417
426
|
});
|
|
418
427
|
}
|
|
419
428
|
|
|
429
|
+
/** Map menu choice to handler. Matches by number prefix or first word. */
|
|
430
|
+
function matchMenuChoice(
|
|
431
|
+
choice: string,
|
|
432
|
+
handlers: Record<string, () => Promise<void>>,
|
|
433
|
+
): (() => Promise<void>) | undefined {
|
|
434
|
+
// Try number prefix first (e.g., "1." from "1. Running agents")
|
|
435
|
+
const numMatch = choice.match(/^(\d+)/);
|
|
436
|
+
if (numMatch) return handlers[numMatch[1]];
|
|
437
|
+
// Fall back to first word
|
|
438
|
+
const key = choice.split(" ")[0].toLowerCase();
|
|
439
|
+
return handlers[key];
|
|
440
|
+
}
|
|
441
|
+
|
|
420
442
|
export async function showAgentsMainMenu(
|
|
421
443
|
ctx: ExtensionCommandContext,
|
|
422
444
|
modelOptions: string[],
|
|
423
445
|
): Promise<void> {
|
|
424
446
|
const menuItems = [
|
|
425
|
-
"1.
|
|
426
|
-
"2.
|
|
427
|
-
"3.
|
|
428
|
-
"4.
|
|
447
|
+
"1. Running agents — List running/queued agents",
|
|
448
|
+
"2. Model settings — Set global default and per-type model overrides",
|
|
449
|
+
"3. Concurrency settings — Set per-model slot limits",
|
|
450
|
+
"4. Widget settings — Configure widget display options",
|
|
451
|
+
"5. Debug — Agent types, briefing, diagnostics",
|
|
429
452
|
"",
|
|
430
453
|
"Press Escape to close",
|
|
431
454
|
];
|
|
432
455
|
|
|
456
|
+
const handlers: Record<string, () => Promise<void>> = {
|
|
457
|
+
"1": () => showRunningAgentsMenu(ctx),
|
|
458
|
+
"2": () => showModelSettingsMenu(ctx, modelOptions),
|
|
459
|
+
"3": () => showConcurrencySettingsMenu(ctx, modelOptions),
|
|
460
|
+
"4": () => showWidgetSettingsMenu(ctx),
|
|
461
|
+
"5": () => showDebugMenu(ctx),
|
|
462
|
+
};
|
|
463
|
+
|
|
433
464
|
// Loop so sub-menus navigate back to root; only Escape at root closes
|
|
434
465
|
while (true) {
|
|
435
466
|
const choice = await ctx.ui.select("Subagents Management", menuItems);
|
|
436
467
|
if (choice === undefined || choice === "Press Escape to close") return;
|
|
437
468
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
} else if (choice.startsWith("2.")) {
|
|
441
|
-
await showConcurrencySettingsMenu(ctx, modelOptions);
|
|
442
|
-
} else if (choice.startsWith("3.")) {
|
|
443
|
-
await showRunningAgentsMenu(ctx);
|
|
444
|
-
} else if (choice.startsWith("4.")) {
|
|
445
|
-
await showDebugMenu(ctx);
|
|
446
|
-
}
|
|
469
|
+
const action = matchMenuChoice(choice, handlers);
|
|
470
|
+
if (action) await action();
|
|
447
471
|
}
|
|
448
472
|
}
|
|
449
473
|
|
|
@@ -453,18 +477,65 @@ async function showDebugMenu(ctx: ExtensionCommandContext): Promise<void> {
|
|
|
453
477
|
"2. Agent briefing — Send agent types/capabilities info to LLM (Optional, if having issues)",
|
|
454
478
|
];
|
|
455
479
|
|
|
480
|
+
const handlers: Record<string, () => Promise<void>> = {
|
|
481
|
+
"1": () => showAgentTypes(ctx),
|
|
482
|
+
"2": () => handleAgentBriefing(ctx),
|
|
483
|
+
};
|
|
484
|
+
|
|
456
485
|
while (true) {
|
|
457
486
|
const choice = await ctx.ui.select("Debug", menuItems);
|
|
458
487
|
if (choice === undefined) return;
|
|
459
488
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
} else if (choice.startsWith("2.")) {
|
|
463
|
-
await handleAgentBriefing(ctx);
|
|
464
|
-
}
|
|
489
|
+
const action = matchMenuChoice(choice, handlers);
|
|
490
|
+
if (action) await action();
|
|
465
491
|
}
|
|
466
492
|
}
|
|
467
493
|
|
|
494
|
+
export async function showWidgetSettingsMenu(ctx: ExtensionCommandContext): Promise<void> {
|
|
495
|
+
return runMenuLoop(ctx, "Widget Settings", () => {
|
|
496
|
+
const items: string[] = [];
|
|
497
|
+
const actions: Array<() => Promise<void>> = [];
|
|
498
|
+
|
|
499
|
+
// Force compact mode toggle
|
|
500
|
+
const isForceCompact = __config.agent.widgetCompact === true;
|
|
501
|
+
items.push(`Force compact mode · ${isForceCompact ? "ON" : "OFF"}`);
|
|
502
|
+
actions.push(async () => {
|
|
503
|
+
setWidgetCompact(!isForceCompact);
|
|
504
|
+
ctx.ui.notify(`Force compact mode ${__config.agent.widgetCompact ? "ON" : "OFF"}`, "info");
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
// Max lines (full mode)
|
|
508
|
+
const maxLines = __config.agent.widgetMaxLines ?? 12;
|
|
509
|
+
items.push(`Max lines (full) · ${maxLines}`);
|
|
510
|
+
actions.push(async () => {
|
|
511
|
+
const parsed = await parseNumericInput(ctx, "Max lines (full mode, ≥ 2)", String(maxLines), 2, "≥ 2");
|
|
512
|
+
if (parsed === undefined) return;
|
|
513
|
+
setWidgetMaxLines(parsed);
|
|
514
|
+
ctx.ui.notify(`Max lines (full) set to ${parsed}`, "info");
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
// Max lines (compact mode)
|
|
518
|
+
const maxLinesCompact = __config.agent.widgetMaxLinesCompact ?? Math.floor(maxLines / 2);
|
|
519
|
+
items.push(`Max lines (compact) · ${maxLinesCompact}`);
|
|
520
|
+
actions.push(async () => {
|
|
521
|
+
const parsed = await parseNumericInput(ctx, "Max lines (compact mode, ≥ 1)", String(maxLinesCompact), 1, "≥ 1");
|
|
522
|
+
if (parsed === undefined) return;
|
|
523
|
+
setWidgetMaxLinesCompact(parsed);
|
|
524
|
+
ctx.ui.notify(`Max lines (compact) set to ${parsed}`, "info");
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
// Ctrl+o shortcut toggle
|
|
528
|
+
const shortcutEnabled = __config.agent.widgetShortcut === true;
|
|
529
|
+
items.push(`Ctrl+o shortcut · ${shortcutEnabled ? "ON" : "OFF"}`);
|
|
530
|
+
actions.push(async () => {
|
|
531
|
+
setWidgetShortcut(!shortcutEnabled);
|
|
532
|
+
ctx.ui.notify(`Ctrl+o shortcut ${__config.agent.widgetShortcut ? "ON" : "OFF"}`, "info");
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
return { items, actions };
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
|
|
468
539
|
async function handleAgentBriefing(ctx: ExtensionCommandContext): Promise<void> {
|
|
469
540
|
const types = getAvailableTypes();
|
|
470
541
|
const agents = types.map((t) => ({ name: t, config: getAgentConfig(t) }));
|
|
@@ -515,6 +586,7 @@ async function handleAgentBriefing(ctx: ExtensionCommandContext): Promise<void>
|
|
|
515
586
|
/**
|
|
516
587
|
* Build a sub-menu for a single per-provider or per-model entry:
|
|
517
588
|
* "Edit limit" to change the value, or "Remove limit" to delete it.
|
|
589
|
+
* Callers pass setter callbacks that handle save + sync internally.
|
|
518
590
|
*/
|
|
519
591
|
async function editOrRemoveConcurrencyEntry(
|
|
520
592
|
ctx: ExtensionCommandContext,
|
|
@@ -522,8 +594,8 @@ async function editOrRemoveConcurrencyEntry(
|
|
|
522
594
|
entityType: "provider" | "model",
|
|
523
595
|
entityKey: string,
|
|
524
596
|
currentValue: number,
|
|
525
|
-
|
|
526
|
-
|
|
597
|
+
setEntry: (key: string, value: number) => void,
|
|
598
|
+
removeEntry: () => void,
|
|
527
599
|
): Promise<void> {
|
|
528
600
|
await runMenu(ctx, `${entityKey} concurrency`, [
|
|
529
601
|
"Edit limit",
|
|
@@ -531,13 +603,12 @@ async function editOrRemoveConcurrencyEntry(
|
|
|
531
603
|
], [
|
|
532
604
|
async () => {
|
|
533
605
|
await promptConcurrencyInput(
|
|
534
|
-
ctx,
|
|
535
|
-
|
|
606
|
+
ctx, entityKey, currentValue,
|
|
607
|
+
(value) => setEntry(entityKey, value),
|
|
536
608
|
);
|
|
537
609
|
},
|
|
538
610
|
async () => {
|
|
539
|
-
|
|
540
|
-
applyConcurrencyConfig();
|
|
611
|
+
removeEntry();
|
|
541
612
|
ctx.ui.notify(
|
|
542
613
|
`Removed per-${entityType} limit for ${entityKey}`,
|
|
543
614
|
"info",
|
|
@@ -561,15 +632,14 @@ export async function showConcurrencySettingsMenu(
|
|
|
561
632
|
actions.push(async () => {
|
|
562
633
|
await promptConcurrencyInput(
|
|
563
634
|
ctx, "Default limit", __config.concurrency.default,
|
|
564
|
-
(value) =>
|
|
635
|
+
(value) => setConcurrencyDefault(value),
|
|
565
636
|
);
|
|
566
637
|
});
|
|
567
638
|
|
|
568
639
|
// Reset all to defaults
|
|
569
640
|
items.push("Reset all to defaults");
|
|
570
641
|
actions.push(async () => {
|
|
571
|
-
|
|
572
|
-
applyConcurrencyConfig();
|
|
642
|
+
resetConcurrency();
|
|
573
643
|
ctx.ui.notify("Concurrency reset to defaults", "info");
|
|
574
644
|
});
|
|
575
645
|
|
|
@@ -592,16 +662,8 @@ export async function showConcurrencySettingsMenu(
|
|
|
592
662
|
"provider",
|
|
593
663
|
provider,
|
|
594
664
|
limit,
|
|
595
|
-
(value) =>
|
|
596
|
-
|
|
597
|
-
__config.concurrency.providers = { ...current, [provider]: value };
|
|
598
|
-
},
|
|
599
|
-
() => {
|
|
600
|
-
const providers = __config.concurrency.providers;
|
|
601
|
-
if (providers) {
|
|
602
|
-
delete providers[provider];
|
|
603
|
-
}
|
|
604
|
-
},
|
|
665
|
+
(key, value) => setConcurrencyProvider(key, value),
|
|
666
|
+
() => removeConcurrencyProvider(provider),
|
|
605
667
|
);
|
|
606
668
|
});
|
|
607
669
|
}
|
|
@@ -614,10 +676,7 @@ export async function showConcurrencySettingsMenu(
|
|
|
614
676
|
if (provider === undefined) return;
|
|
615
677
|
await promptAddConcurrencyLimit(
|
|
616
678
|
ctx, provider,
|
|
617
|
-
(key, value) =>
|
|
618
|
-
const current = __config.concurrency.providers ?? {};
|
|
619
|
-
__config.concurrency.providers = { ...current, [key]: value };
|
|
620
|
-
},
|
|
679
|
+
(key, value) => setConcurrencyProvider(key, value),
|
|
621
680
|
);
|
|
622
681
|
});
|
|
623
682
|
|
|
@@ -640,16 +699,8 @@ export async function showConcurrencySettingsMenu(
|
|
|
640
699
|
"model",
|
|
641
700
|
modelKey,
|
|
642
701
|
limit,
|
|
643
|
-
(value) =>
|
|
644
|
-
|
|
645
|
-
__config.concurrency.models = { ...current, [modelKey]: value };
|
|
646
|
-
},
|
|
647
|
-
() => {
|
|
648
|
-
const models = __config.concurrency.models;
|
|
649
|
-
if (models) {
|
|
650
|
-
delete models[modelKey];
|
|
651
|
-
}
|
|
652
|
-
},
|
|
702
|
+
(key, value) => setConcurrencyModel(key, value),
|
|
703
|
+
() => removeConcurrencyModel(modelKey),
|
|
653
704
|
);
|
|
654
705
|
});
|
|
655
706
|
}
|
|
@@ -664,10 +715,7 @@ export async function showConcurrencySettingsMenu(
|
|
|
664
715
|
if (modelKey === null) return;
|
|
665
716
|
await promptAddConcurrencyLimit(
|
|
666
717
|
ctx, modelKey.trim(),
|
|
667
|
-
(key, value) =>
|
|
668
|
-
const current = __config.concurrency.models ?? {};
|
|
669
|
-
__config.concurrency.models = { ...current, [key]: value };
|
|
670
|
-
},
|
|
718
|
+
(key, value) => setConcurrencyModel(key, value),
|
|
671
719
|
);
|
|
672
720
|
});
|
|
673
721
|
|
|
@@ -678,27 +726,31 @@ export async function showConcurrencySettingsMenu(
|
|
|
678
726
|
async function showRunningAgentsMenu(
|
|
679
727
|
ctx: ExtensionCommandContext,
|
|
680
728
|
): Promise<void> {
|
|
681
|
-
const records =
|
|
729
|
+
const records = getManager()?.listAgents() ?? [];
|
|
682
730
|
if (records.length === 0) {
|
|
683
731
|
ctx.ui.notify("No agents have been spawned this session", "info");
|
|
684
732
|
return;
|
|
685
733
|
}
|
|
686
734
|
|
|
687
735
|
return runMenuLoop(ctx, "Running Agents", () => {
|
|
688
|
-
const records =
|
|
689
|
-
const running = records.filter((r) => r.status === "running" || r.status === "queued");
|
|
736
|
+
const records = getManager()?.listAgents() ?? [];
|
|
737
|
+
const running = records.filter((r) => r.lifecycle.status === "running" || r.lifecycle.status === "queued");
|
|
690
738
|
|
|
691
739
|
const items: string[] = [];
|
|
692
740
|
const actions: Array<() => Promise<void>> = [];
|
|
693
741
|
|
|
694
742
|
for (const record of records) {
|
|
695
|
-
const elapsed = Math.round((Date.now() - record.startedAt) / 1000);
|
|
696
|
-
const statusIcon = record.status === "running" ? "▶" :
|
|
697
|
-
record.status === "completed" ? "✓" :
|
|
698
|
-
record.status === "queued" ? "⏳" :
|
|
699
|
-
record.status === "error" ? "✗" : "•";
|
|
743
|
+
const elapsed = Math.round((Date.now() - record.lifecycle.startedAt) / 1000);
|
|
744
|
+
const statusIcon = record.lifecycle.status === "running" ? "▶" :
|
|
745
|
+
record.lifecycle.status === "completed" ? "✓" :
|
|
746
|
+
record.lifecycle.status === "queued" ? "⏳" :
|
|
747
|
+
record.lifecycle.status === "error" ? "✗" : "•";
|
|
748
|
+
const headline = record.display.description
|
|
749
|
+
? (record.display.description.length > 50 ? record.display.description.slice(0, 47) + "..." : record.display.description)
|
|
750
|
+
: "";
|
|
751
|
+
const suffix = headline ? ` — ${headline}` : "";
|
|
700
752
|
items.push(
|
|
701
|
-
`${statusIcon} ${record.id.slice(0, SHORT_ID_LENGTH)} ${record.type} ${record.status} ${elapsed}s`,
|
|
753
|
+
`${statusIcon} ${record.id.slice(0, SHORT_ID_LENGTH)} ${record.display.type} ${record.lifecycle.status} ${elapsed}s${suffix}`,
|
|
702
754
|
);
|
|
703
755
|
|
|
704
756
|
actions.push(async () => {
|
|
@@ -715,7 +767,7 @@ async function showRunningAgentsMenu(
|
|
|
715
767
|
items.push(`Stop ${running.length} running agent(s)`);
|
|
716
768
|
actions.push(async () => {
|
|
717
769
|
for (const record of running) {
|
|
718
|
-
|
|
770
|
+
getManager()?.abort(record.id);
|
|
719
771
|
}
|
|
720
772
|
ctx.ui.notify(`Stopped ${running.length} agent(s)`, "info");
|
|
721
773
|
});
|
|
@@ -741,19 +793,19 @@ async function showResultViewer(
|
|
|
741
793
|
? `snapshot \u00b7 ${record.id.slice(0, SHORT_ID_LENGTH)}`
|
|
742
794
|
: "Error";
|
|
743
795
|
const stats: ResultViewerStats = {
|
|
744
|
-
lifetimeUsage: record.lifetimeUsage,
|
|
745
|
-
turnCount: record.turnCount,
|
|
746
|
-
durationMs: (record.completedAt ?? Date.now()) - record.startedAt,
|
|
796
|
+
lifetimeUsage: record.stats.lifetimeUsage,
|
|
797
|
+
turnCount: record.stats.turnCount,
|
|
798
|
+
durationMs: (record.lifecycle.completedAt ?? Date.now()) - record.lifecycle.startedAt,
|
|
747
799
|
};
|
|
748
800
|
const refreshCallback =
|
|
749
|
-
kind === "snapshot" && record.session
|
|
750
|
-
? () => buildSnapshotMarkdown(record.session!.messages)
|
|
801
|
+
kind === "snapshot" && record.execution.session
|
|
802
|
+
? () => buildSnapshotMarkdown(record.execution.session!.messages)
|
|
751
803
|
: undefined;
|
|
752
804
|
|
|
753
805
|
await ctx.ui.custom<void>(
|
|
754
806
|
(tui, theme, _kb, done) =>
|
|
755
807
|
new ResultViewer(
|
|
756
|
-
`${getDisplayName(record.type)} · ${titleSuffix}`,
|
|
808
|
+
`${getDisplayName(record.display.type)} · ${titleSuffix}`,
|
|
757
809
|
text,
|
|
758
810
|
{ onClose: () => done(), onRefresh: refreshCallback },
|
|
759
811
|
theme,
|
|
@@ -770,16 +822,16 @@ async function steerAgentById(
|
|
|
770
822
|
agentId: string,
|
|
771
823
|
ctx: ExtensionCommandContext,
|
|
772
824
|
): Promise<void> {
|
|
773
|
-
const record =
|
|
825
|
+
const record = getManager()?.getRecord(agentId);
|
|
774
826
|
if (!record) {
|
|
775
827
|
ctx.ui.notify("Agent not found", "error");
|
|
776
828
|
return;
|
|
777
829
|
}
|
|
778
830
|
|
|
779
|
-
const message = await ctx.ui.input(`Steer ${record.type}`);
|
|
831
|
+
const message = await ctx.ui.input(`Steer ${record.display.type}`);
|
|
780
832
|
if (!message?.trim()) return;
|
|
781
833
|
|
|
782
|
-
const sent = await
|
|
834
|
+
const sent = await getManager().steer(agentId, message.trim());
|
|
783
835
|
if (sent) {
|
|
784
836
|
ctx.ui.notify(`Steer sent to ${record.id.slice(0, SHORT_ID_LENGTH)}…`, "info");
|
|
785
837
|
} else {
|
|
@@ -798,16 +850,16 @@ export async function showAgentActions(
|
|
|
798
850
|
const items: string[] = [];
|
|
799
851
|
const actions: Array<() => Promise<void>> = [];
|
|
800
852
|
|
|
801
|
-
const isRunning = record.status === "running" || record.status === "queued";
|
|
802
|
-
const hasSession = !!record.session;
|
|
853
|
+
const isRunning = record.lifecycle.status === "running" || record.lifecycle.status === "queued";
|
|
854
|
+
const hasSession = !!record.execution.session;
|
|
803
855
|
const hasResult = !!record.result && record.result.length > 0;
|
|
804
856
|
const hasError = !!record.error && record.error.length > 0;
|
|
805
857
|
|
|
806
858
|
// View actions first
|
|
807
|
-
if (record.status === "running" && hasSession) {
|
|
859
|
+
if (record.lifecycle.status === "running" && hasSession) {
|
|
808
860
|
items.push("View snapshot");
|
|
809
861
|
actions.push(async () => {
|
|
810
|
-
const messages = record.session!.messages;
|
|
862
|
+
const messages = record.execution.session!.messages;
|
|
811
863
|
const markdown = buildSnapshotMarkdown(messages);
|
|
812
864
|
await showResultViewer(ctx, record, "snapshot", markdown);
|
|
813
865
|
});
|
|
@@ -836,7 +888,7 @@ export async function showAgentActions(
|
|
|
836
888
|
|
|
837
889
|
items.push("Stop");
|
|
838
890
|
actions.push(async () => {
|
|
839
|
-
|
|
891
|
+
getManager()?.abort(record.id);
|
|
840
892
|
ctx.ui.notify(`Stopped ${record.id.slice(0, SHORT_ID_LENGTH)}`, "info");
|
|
841
893
|
});
|
|
842
894
|
}
|
package/src/model-precedence.ts
CHANGED
|
@@ -18,6 +18,11 @@ export interface SubagentsConfig {
|
|
|
18
18
|
default: string | null;
|
|
19
19
|
forceBackground: boolean;
|
|
20
20
|
graceTurns?: number;
|
|
21
|
+
showCost?: boolean;
|
|
22
|
+
widgetMaxLines?: number;
|
|
23
|
+
widgetMaxLinesCompact?: number;
|
|
24
|
+
widgetCompact?: boolean;
|
|
25
|
+
widgetShortcut?: boolean;
|
|
21
26
|
[agentType: string]: string | null | undefined | boolean | number;
|
|
22
27
|
};
|
|
23
28
|
concurrency: {
|