@esso0428/pi-subagents 0.17.13 → 0.17.15
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 +16 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +38 -158
- package/dist/index.js.map +1 -1
- package/dist/prompts.d.ts +2 -2
- package/dist/settings.d.ts +1 -8
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +0 -5
- package/dist/settings.js.map +1 -1
- package/dist/ui/agent-mention.d.ts +2 -2
- package/dist/ui/agent-widget.d.ts +0 -6
- package/dist/ui/agent-widget.d.ts.map +1 -1
- package/dist/ui/agent-widget.js +1 -16
- package/dist/ui/agent-widget.js.map +1 -1
- package/dist/ui/workflow-menu.d.ts +3 -22
- package/dist/ui/workflow-menu.d.ts.map +1 -1
- package/dist/ui/workflow-menu.js +4 -25
- package/dist/ui/workflow-menu.js.map +1 -1
- package/package.json +4 -3
- package/src/index.ts +38 -162
- package/src/prompts.ts +2 -2
- package/src/settings.ts +1 -12
- package/src/ui/agent-mention.ts +2 -2
- package/src/ui/agent-widget.ts +1 -16
- package/src/ui/workflow-menu.ts +4 -31
- package/dist/ui/fleet-list.d.ts +0 -198
- package/dist/ui/fleet-list.d.ts.map +0 -1
- package/dist/ui/fleet-list.js +0 -487
- package/dist/ui/fleet-list.js.map +0 -1
- package/src/ui/fleet-list.ts +0 -558
package/src/index.ts
CHANGED
|
@@ -58,10 +58,9 @@ import {
|
|
|
58
58
|
type Theme,
|
|
59
59
|
type UICtx,
|
|
60
60
|
} from "./ui/agent-widget.js";
|
|
61
|
-
import { FleetList, type FleetUICtx, type FleetWorkflow } from "./ui/fleet-list.js";
|
|
62
61
|
import { showSchedulesMenu } from "./ui/schedule-menu.js";
|
|
63
62
|
import { renderWorkflowCard, renderWorkflowEntryCard } from "./ui/workflow-card.js";
|
|
64
|
-
import {
|
|
63
|
+
import { showWorkflowsMenu, type WorkflowMenuDeps } from "./ui/workflow-menu.js";
|
|
65
64
|
import { getLifetimeCost, getLifetimeTotal, getSessionContextPercent, type LifetimeUsage, PendingUsagePool, toReportedUsage } from "./usage.js";
|
|
66
65
|
import { decideWorkflowCollision, FOREIGN_WORKFLOW_TOOL_NAMES } from "./workflow/collisions.js";
|
|
67
66
|
import { WORKFLOW_ENTRY_TYPE, type WorkflowEntryData, workflowEntryData } from "./workflow/entry.js";
|
|
@@ -425,7 +424,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
425
424
|
/** Show `~$X` next to token counts in the subagent surfaces. */
|
|
426
425
|
let showCost = false;
|
|
427
426
|
function isShowCostEnabled(): boolean { return showCost; }
|
|
428
|
-
function setShowCost(b: boolean): void { showCost = b; widget.update();
|
|
427
|
+
function setShowCost(b: boolean): void { showCost = b; widget.update(); }
|
|
429
428
|
/** Name the model and thinking level on the widget's running rows. */
|
|
430
429
|
let showModel = false;
|
|
431
430
|
function isShowModelEnabled(): boolean { return showModel; }
|
|
@@ -438,16 +437,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
438
437
|
let viewerMarkdown: ViewerMarkdownMode = "assistant";
|
|
439
438
|
function getViewerMarkdown(): ViewerMarkdownMode { return viewerMarkdown; }
|
|
440
439
|
function setViewerMarkdown(mode: ViewerMarkdownMode): void { viewerMarkdown = mode; }
|
|
441
|
-
/**
|
|
442
|
-
* The viewer's `m` key, from either entry point: set the mode and persist it,
|
|
443
|
-
* so the key and `/agents → Settings` stay one setting rather than one per
|
|
444
|
-
* entry point. `ctx` carries only the warning a failed write notifies with,
|
|
445
|
-
* and the fleet list may be acting without one.
|
|
446
|
-
*/
|
|
447
|
-
function chooseViewerMarkdown(mode: ViewerMarkdownMode, ctx?: ExtensionCommandContext): void {
|
|
448
|
-
setViewerMarkdown(mode);
|
|
449
|
-
persistSettings(ctx, `Viewer markdown set to ${mode}`);
|
|
450
|
-
}
|
|
451
440
|
const pendingUsage = new PendingUsagePool();
|
|
452
441
|
|
|
453
442
|
// ---- Cancellable pending notifications ----
|
|
@@ -493,7 +482,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
493
482
|
function sendIndividualNudge(record: AgentRecord) {
|
|
494
483
|
agentActivity.delete(record.id);
|
|
495
484
|
widget.markFinished(record.id);
|
|
496
|
-
fleet.onAgentFinished(record.id);
|
|
497
485
|
scheduleNudge(record.id, () => emitIndividualNudge(record));
|
|
498
486
|
widget.update();
|
|
499
487
|
}
|
|
@@ -501,7 +489,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
501
489
|
// ---- Group join manager ----
|
|
502
490
|
const groupJoin = new GroupJoinManager(
|
|
503
491
|
(records, partial) => {
|
|
504
|
-
for (const r of records) { agentActivity.delete(r.id); widget.markFinished(r.id);
|
|
492
|
+
for (const r of records) { agentActivity.delete(r.id); widget.markFinished(r.id); }
|
|
505
493
|
|
|
506
494
|
const groupKey = `group:${records.map(r => r.id).join(",")}`;
|
|
507
495
|
scheduleNudge(groupKey, () => {
|
|
@@ -605,7 +593,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
605
593
|
if (record.resultConsumed) {
|
|
606
594
|
agentActivity.delete(record.id);
|
|
607
595
|
widget.markFinished(record.id);
|
|
608
|
-
fleet.onAgentFinished(record.id);
|
|
609
596
|
widget.update();
|
|
610
597
|
return;
|
|
611
598
|
}
|
|
@@ -686,7 +673,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
686
673
|
// Every programmatic spawn lands here — cross-extension RPC, both `@handle`
|
|
687
674
|
// mention paths, and the `Symbol.for("pi-subagents:manager")` registry — and
|
|
688
675
|
// none came through the Agent tool, which is where the UI activity tracker is
|
|
689
|
-
// otherwise created. Without one the widget
|
|
676
|
+
// otherwise created. Without one the widget has no tool name
|
|
690
677
|
// and no turn count, so the row reads `thinking…` for the agent's whole life
|
|
691
678
|
// while the header's tool-use count climbs beside it (#181). Double-tracking
|
|
692
679
|
// is not possible: the Agent tool calls `manager.spawn` directly. The tracker
|
|
@@ -700,7 +687,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
700
687
|
// leaves the displayed ceiling stale.
|
|
701
688
|
const { state, callbacks } = createActivityTracker(resolveEffectiveMaxTurns(dispatch.type, options?.maxTurns));
|
|
702
689
|
// Repaints are left to the manager's `onStart` callback, which already starts
|
|
703
|
-
// the widget
|
|
690
|
+
// the widget timer for agents that enter this way.
|
|
704
691
|
const id = manager.spawn(piRef, ctxRef, dispatch.type, prompt, { ...options, ...callbacks });
|
|
705
692
|
agentActivity.set(id, state);
|
|
706
693
|
return id;
|
|
@@ -812,8 +799,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
812
799
|
if (ctx.hasUI && (ctx.mode === undefined || ctx.mode === "tui")) {
|
|
813
800
|
widget.setUICtx(ctx.ui);
|
|
814
801
|
widget.update();
|
|
815
|
-
fleet.setUICtx(ctx.ui as any, false);
|
|
816
|
-
fleet.setCwd(ctx.cwd);
|
|
817
802
|
}
|
|
818
803
|
manager.clearCompleted(true);
|
|
819
804
|
// Guard mirrors the `!scheduler.isActive()` pattern below: session_start
|
|
@@ -859,8 +844,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
859
844
|
ctx.ui.addAutocompleteProvider(current =>
|
|
860
845
|
createMentionProvider(
|
|
861
846
|
current,
|
|
862
|
-
// Plain text, not renderAgentName: the same label
|
|
863
|
-
//
|
|
847
|
+
// Plain text, not renderAgentName: the same label the widget shows,
|
|
848
|
+
// but the autocomplete description cannot carry ANSI.
|
|
864
849
|
() => mentionRoster(manager, mentionTypes(), type => getConfig(type).displayName),
|
|
865
850
|
isAgentMentionsEnabled,
|
|
866
851
|
),
|
|
@@ -970,7 +955,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
970
955
|
|
|
971
956
|
// Evicted, but its conversation is still on disk: reopen it. This is an
|
|
972
957
|
// ordinary spawn carrying a session file, so the new record picks up the
|
|
973
|
-
// widget
|
|
958
|
+
// widget row, transcript and completion notification unchanged —
|
|
974
959
|
// and `reclaim` hands it back the names the tombstone was holding.
|
|
975
960
|
if (resolved?.kind === "tombstone") {
|
|
976
961
|
const entry = resolved.entry;
|
|
@@ -1092,8 +1077,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
1092
1077
|
try {
|
|
1093
1078
|
// Nothing else to pass: runAgent resolves model, thinking and max turns
|
|
1094
1079
|
// from the agent's own config when the spawn omits them, and the
|
|
1095
|
-
// manager's onStart/onComplete callbacks own the widget
|
|
1096
|
-
//
|
|
1080
|
+
// manager's onStart/onComplete callbacks own the widget and completion
|
|
1081
|
+
// notification — the same contract the scheduler and
|
|
1097
1082
|
// cross-extension RPC spawns run under.
|
|
1098
1083
|
const id = spawnTopLevel(pi, ctx, type, mention.message, {
|
|
1099
1084
|
description: describeMention(mention.message),
|
|
@@ -1138,7 +1123,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
1138
1123
|
for (const timer of pendingNudges.values()) clearTimeout(timer);
|
|
1139
1124
|
pendingNudges.clear();
|
|
1140
1125
|
widget.dispose();
|
|
1141
|
-
fleet.dispose();
|
|
1142
1126
|
// Awaited: it emits `session_shutdown` into every retained child session so
|
|
1143
1127
|
// extensions bound there can release what they armed in `session_start` (#242).
|
|
1144
1128
|
// pi awaits this handler, and the process exits right after — unawaited, those
|
|
@@ -1162,16 +1146,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
1162
1146
|
}, isShowModelEnabled);
|
|
1163
1147
|
function setWidgetMode(m: WidgetMode): void { widgetMode = m; widget.update(); }
|
|
1164
1148
|
|
|
1165
|
-
// Claude Code-style FleetView: navigable list of main + subagents below the editor.
|
|
1166
|
-
// The last two arguments keep a conversation overlay opened here identical to
|
|
1167
|
-
// one opened from `/agents`: same setting on the way in, same persist out.
|
|
1168
|
-
const fleet = new FleetList(manager, agentActivity, isShowCostEnabled, getViewerMarkdown,
|
|
1169
|
-
(mode) => chooseViewerMarkdown(mode, currentCtx as unknown as ExtensionCommandContext | undefined),
|
|
1170
|
-
process.cwd());
|
|
1171
|
-
let fleetViewEnabled = true;
|
|
1172
|
-
function isFleetViewEnabled(): boolean { return fleetViewEnabled; }
|
|
1173
|
-
function setFleetViewEnabled(b: boolean): void { fleetViewEnabled = b; fleet.setEnabled(b); }
|
|
1174
|
-
|
|
1175
1149
|
// Claude Code-style `@handle message` prompt mentions. Read live by both the
|
|
1176
1150
|
// `input` hook and the stacked autocomplete provider, so the toggle applies
|
|
1177
1151
|
// immediately — the provider itself can never be unregistered (pi's wrapper
|
|
@@ -1297,7 +1271,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1297
1271
|
/**
|
|
1298
1272
|
* Launch a detached resume of an existing agent and wire everything a
|
|
1299
1273
|
* re-running agent needs: transcript anchoring, activity tracking, join-mode
|
|
1300
|
-
* batching, the widget
|
|
1274
|
+
* batching, the widget refresh, and the `subagents:created` event.
|
|
1301
1275
|
*
|
|
1302
1276
|
* Shared by the Agent tool's `resume` + `run_in_background` branch and the
|
|
1303
1277
|
* `@handle message` prompt mention — they differ only in how they report the
|
|
@@ -1371,8 +1345,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
1371
1345
|
widget.markRunning(id);
|
|
1372
1346
|
widget.ensureTimer();
|
|
1373
1347
|
widget.update();
|
|
1374
|
-
fleet.ensureTimer();
|
|
1375
|
-
fleet.update();
|
|
1376
1348
|
|
|
1377
1349
|
// Resume ignores subagent_type (the record keeps the type it was
|
|
1378
1350
|
// spawned with), so report the record's own identity — a "created"
|
|
@@ -1391,7 +1363,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
1391
1363
|
// Grab UI context from first tool execution + clear lingering widget on new turn
|
|
1392
1364
|
pi.on("tool_execution_start", async (_event, ctx) => {
|
|
1393
1365
|
if (ctx.hasUI && (ctx.mode === undefined || ctx.mode === "tui")) widget.setUICtx(ctx.ui as UICtx);
|
|
1394
|
-
if (ctx.hasUI && ctx.mode === undefined) fleet.setUICtx(ctx.ui as unknown as FleetUICtx, true);
|
|
1395
1366
|
widget.onTurnStart();
|
|
1396
1367
|
});
|
|
1397
1368
|
|
|
@@ -1444,7 +1415,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
1444
1415
|
setStrictAgentFiles: (b) => { strictAgentFiles = b; },
|
|
1445
1416
|
setDisableDefaultAgents: setDisableDefaultAgents,
|
|
1446
1417
|
setToolDescriptionMode: setToolDescriptionMode,
|
|
1447
|
-
setFleetView: setFleetViewEnabled,
|
|
1448
1418
|
setAgentMentions: setAgentMentionMode,
|
|
1449
1419
|
setRememberAgents,
|
|
1450
1420
|
setWidgetMode: setWidgetMode,
|
|
@@ -2129,9 +2099,6 @@ Terse command-style prompts produce shallow, generic work.
|
|
|
2129
2099
|
agentActivity.set(id, bgState);
|
|
2130
2100
|
widget.ensureTimer();
|
|
2131
2101
|
widget.update();
|
|
2132
|
-
fleet.ensureTimer();
|
|
2133
|
-
fleet.update();
|
|
2134
|
-
|
|
2135
2102
|
// Emit created event
|
|
2136
2103
|
pi.events.emit("subagents:created", {
|
|
2137
2104
|
id,
|
|
@@ -2214,8 +2181,6 @@ Terse command-style prompts produce shallow, generic work.
|
|
|
2214
2181
|
fgId = a.id;
|
|
2215
2182
|
agentActivity.set(a.id, fgState);
|
|
2216
2183
|
widget.ensureTimer();
|
|
2217
|
-
fleet.ensureTimer();
|
|
2218
|
-
fleet.update();
|
|
2219
2184
|
break;
|
|
2220
2185
|
}
|
|
2221
2186
|
}
|
|
@@ -2271,8 +2236,7 @@ Terse command-style prompts produce shallow, generic work.
|
|
|
2271
2236
|
if (fgId) {
|
|
2272
2237
|
agentActivity.delete(fgId);
|
|
2273
2238
|
widget.markFinished(fgId);
|
|
2274
|
-
|
|
2275
|
-
}
|
|
2239
|
+
}
|
|
2276
2240
|
}
|
|
2277
2241
|
|
|
2278
2242
|
// Get final token count — from the record, like the cost below it, so the
|
|
@@ -2344,30 +2308,6 @@ Terse command-style prompts produce shallow, generic work.
|
|
|
2344
2308
|
*/
|
|
2345
2309
|
const workflowTasks = new Map<string, WorkflowTask>();
|
|
2346
2310
|
|
|
2347
|
-
/**
|
|
2348
|
-
* Workflow runs as the fleet list wants them.
|
|
2349
|
-
*
|
|
2350
|
-
* Mapped here rather than handing `WorkflowTask` over the seam: the list is
|
|
2351
|
-
* deliberately ignorant of the workflow engine, and a run's counters live in
|
|
2352
|
-
* the progress log rather than on the record, so they are derived per call
|
|
2353
|
-
* the same way the card derives them.
|
|
2354
|
-
*/
|
|
2355
|
-
function fleetWorkflows(): FleetWorkflow[] {
|
|
2356
|
-
// Cached counters only, no derivation: the fleet list calls this on a
|
|
2357
|
-
// 200ms tick and reads the roster several times per update, so walking a
|
|
2358
|
-
// run's progress log here would put O(log) work in the render loop.
|
|
2359
|
-
return [...workflowTasks.values()].map(task => ({
|
|
2360
|
-
id: task.id,
|
|
2361
|
-
name: task.meta?.name ?? task.workflowName ?? task.id,
|
|
2362
|
-
status: task.status,
|
|
2363
|
-
doneCount: task.doneCount,
|
|
2364
|
-
totalCount: task.agentCount,
|
|
2365
|
-
startedAt: task.startTime,
|
|
2366
|
-
...(task.endTime !== undefined ? { completedAt: task.endTime } : {}),
|
|
2367
|
-
tokens: task.totalTokens,
|
|
2368
|
-
}));
|
|
2369
|
-
}
|
|
2370
|
-
|
|
2371
2311
|
/**
|
|
2372
2312
|
* Run a task to completion against the real manager, settling the record
|
|
2373
2313
|
* either way. Never rejects: a run that cannot start (bad `meta`, oversized
|
|
@@ -2412,7 +2352,6 @@ Terse command-style prompts produce shallow, generic work.
|
|
|
2412
2352
|
*/
|
|
2413
2353
|
function notifyWorkflowFinished(task: WorkflowTask) {
|
|
2414
2354
|
widget.update();
|
|
2415
|
-
fleet.update();
|
|
2416
2355
|
const result = workflowResultText(task);
|
|
2417
2356
|
scheduleNudge(task.id, () => {
|
|
2418
2357
|
pi.sendMessage<NotificationDetails>({
|
|
@@ -2586,7 +2525,6 @@ Terse command-style prompts produce shallow, generic work.
|
|
|
2586
2525
|
// surfaces — nothing else would register the widget for a run whose
|
|
2587
2526
|
// first agent has not started yet.
|
|
2588
2527
|
widget.update();
|
|
2589
|
-
fleet.update();
|
|
2590
2528
|
|
|
2591
2529
|
// Background, like Claude Code: the id comes back now and the run keeps
|
|
2592
2530
|
// going without the tool call.
|
|
@@ -2665,7 +2603,6 @@ Terse command-style prompts produce shallow, generic work.
|
|
|
2665
2603
|
|
|
2666
2604
|
workflowsEnabled = false; // not setWorkflowsEnabled: this is not the user pinning it
|
|
2667
2605
|
widget.update();
|
|
2668
|
-
fleet.update();
|
|
2669
2606
|
warn(verdict.message);
|
|
2670
2607
|
|
|
2671
2608
|
if (!verdict.withdraw) return;
|
|
@@ -2740,7 +2677,6 @@ Terse command-style prompts produce shallow, generic work.
|
|
|
2740
2677
|
const task = createWorkflowTask({ id: workflowRunId(), script, scriptPath: path, meta });
|
|
2741
2678
|
workflowTasks.set(task.id, task);
|
|
2742
2679
|
widget.update();
|
|
2743
|
-
fleet.update();
|
|
2744
2680
|
report(`Running workflow ${meta.name}…`, "info");
|
|
2745
2681
|
|
|
2746
2682
|
// Detached: session_start is awaited by the host, and a workflow can run for
|
|
@@ -2756,7 +2692,6 @@ Terse command-style prompts produce shallow, generic work.
|
|
|
2756
2692
|
display: false,
|
|
2757
2693
|
}, { deliverAs: "nextTurn" });
|
|
2758
2694
|
widget.update();
|
|
2759
|
-
fleet.update();
|
|
2760
2695
|
});
|
|
2761
2696
|
}
|
|
2762
2697
|
|
|
@@ -2978,15 +2913,7 @@ Terse command-style prompts produce shallow, generic work.
|
|
|
2978
2913
|
ctx.ui.notify(noAgentsMsg, "info");
|
|
2979
2914
|
}
|
|
2980
2915
|
|
|
2981
|
-
|
|
2982
|
-
// while the top-level menu owns the same arrow/Enter keys.
|
|
2983
|
-
widget.suspendNavigation();
|
|
2984
|
-
let choice: string | undefined;
|
|
2985
|
-
try {
|
|
2986
|
-
choice = await ctx.ui.select("Agents", options);
|
|
2987
|
-
} finally {
|
|
2988
|
-
widget.resumeNavigation();
|
|
2989
|
-
}
|
|
2916
|
+
const choice = await ctx.ui.select("Agents", options);
|
|
2990
2917
|
if (!choice) return;
|
|
2991
2918
|
|
|
2992
2919
|
if (choice.startsWith("Running agents (")) {
|
|
@@ -3118,36 +3045,30 @@ Terse command-style prompts produce shallow, generic work.
|
|
|
3118
3045
|
}
|
|
3119
3046
|
};
|
|
3120
3047
|
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
done(item.value);
|
|
3133
|
-
};
|
|
3134
|
-
list.onCancel = () => done(undefined);
|
|
3048
|
+
const choice = await ctx.ui.custom<string | undefined>((_tui, _theme, _kb, done) => {
|
|
3049
|
+
const list = new SelectList(options, Math.min(options.length, 10), getSelectListTheme());
|
|
3050
|
+
list.setSelectedIndex(initialIndex);
|
|
3051
|
+
const initialItem = options[initialIndex];
|
|
3052
|
+
if (initialItem) remember(initialItem.value);
|
|
3053
|
+
list.onSelectionChange = item => remember(item.value);
|
|
3054
|
+
list.onSelect = item => {
|
|
3055
|
+
remember(item.value);
|
|
3056
|
+
done(item.value);
|
|
3057
|
+
};
|
|
3058
|
+
list.onCancel = () => done(undefined);
|
|
3135
3059
|
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
} finally {
|
|
3149
|
-
widget.resumeNavigation();
|
|
3150
|
-
}
|
|
3060
|
+
const container = new Container();
|
|
3061
|
+
container.addChild(new Text(title, 0, 0));
|
|
3062
|
+
container.addChild(new Spacer(1));
|
|
3063
|
+
container.addChild(list);
|
|
3064
|
+
return {
|
|
3065
|
+
render: (width: number) => container.render(width),
|
|
3066
|
+
invalidate: () => container.invalidate(),
|
|
3067
|
+
handleInput: (data: string) => {
|
|
3068
|
+
if (!isKeyRelease(data)) list.handleInput(data);
|
|
3069
|
+
},
|
|
3070
|
+
};
|
|
3071
|
+
});
|
|
3151
3072
|
|
|
3152
3073
|
if (!choice) return undefined;
|
|
3153
3074
|
return pairs.find(({ record }) => record.id === choice)?.record;
|
|
@@ -3589,7 +3510,6 @@ Write the file using the write tool. Only write the file, nothing else.`;
|
|
|
3589
3510
|
strictAgentFiles,
|
|
3590
3511
|
disableDefaultAgents: isDefaultsDisabled(),
|
|
3591
3512
|
toolDescriptionMode: getToolDescriptionMode(),
|
|
3592
|
-
fleetView: isFleetViewEnabled(),
|
|
3593
3513
|
agentMentions: getAgentMentionMode(),
|
|
3594
3514
|
rememberAgents: getRememberAgents(),
|
|
3595
3515
|
widgetMode: getWidgetMode(),
|
|
@@ -3767,7 +3687,7 @@ Write the file using the write tool. Only write the file, nothing else.`;
|
|
|
3767
3687
|
id: "showCost",
|
|
3768
3688
|
label: "Show cost",
|
|
3769
3689
|
description:
|
|
3770
|
-
"Show an estimated `~$0.0042` beside subagent token counts in the widget,
|
|
3690
|
+
"Show an estimated `~$0.0042` beside subagent token counts in the widget, results and notifications. Priced by pi from the model's rates — omitted entirely for a model it has no rates for.",
|
|
3771
3691
|
currentValue: isShowCostEnabled() ? "on" : "off",
|
|
3772
3692
|
values: ["on", "off"],
|
|
3773
3693
|
},
|
|
@@ -3787,13 +3707,6 @@ Write the file using the write tool. Only write the file, nothing else.`;
|
|
|
3787
3707
|
currentValue: getViewerMarkdown(),
|
|
3788
3708
|
values: ["off", "assistant", "all"],
|
|
3789
3709
|
},
|
|
3790
|
-
{
|
|
3791
|
-
id: "fleetView",
|
|
3792
|
-
label: "Fleet view",
|
|
3793
|
-
description: "Claude Code-style main+subagents list below the editor (↓/← to navigate, Enter to view)",
|
|
3794
|
-
currentValue: isFleetViewEnabled() ? "on" : "off",
|
|
3795
|
-
values: ["on", "off"],
|
|
3796
|
-
},
|
|
3797
3710
|
{
|
|
3798
3711
|
id: "agentMentions",
|
|
3799
3712
|
label: "Agent mentions",
|
|
@@ -3961,10 +3874,6 @@ Write the file using the write tool. Only write the file, nothing else.`;
|
|
|
3961
3874
|
} else if (id === "viewerMarkdown") {
|
|
3962
3875
|
setViewerMarkdown(value as ViewerMarkdownMode);
|
|
3963
3876
|
notifyApplied(ctx, `Viewer markdown set to ${value}`);
|
|
3964
|
-
} else if (id === "fleetView") {
|
|
3965
|
-
const enabled = value === "on";
|
|
3966
|
-
setFleetViewEnabled(enabled);
|
|
3967
|
-
notifyApplied(ctx, `Fleet view ${enabled ? "enabled" : "disabled"}`);
|
|
3968
3877
|
} else if (id === "agentMentions") {
|
|
3969
3878
|
const mode = value as AgentMentionMode;
|
|
3970
3879
|
setAgentMentionMode(mode);
|
|
@@ -4069,31 +3978,6 @@ Write the file using the write tool. Only write the file, nothing else.`;
|
|
|
4069
3978
|
}
|
|
4070
3979
|
}
|
|
4071
3980
|
|
|
4072
|
-
// Persist the current snapshot, emit `subagents:settings_changed`, and surface
|
|
4073
|
-
// the right toast. Successful saves show info; persistence failures downgrade
|
|
4074
|
-
// to warning so users aren't silently reverted on restart. Event fires regardless
|
|
4075
|
-
// of outcome so listeners see the in-memory change.
|
|
4076
|
-
/**
|
|
4077
|
-
* Persist + broadcast the settings, silent on success — for a change whose
|
|
4078
|
-
* feedback is the UI it just changed: the viewer's `m` key, where a
|
|
4079
|
-
* notification per press would talk over the overlay it is describing.
|
|
4080
|
-
*
|
|
4081
|
-
* A *failed* write still speaks. Every other settings path warns when the
|
|
4082
|
-
* value is session-only, and swallowing it here would leave a preference
|
|
4083
|
-
* looking persisted when the next session will not have it.
|
|
4084
|
-
*/
|
|
4085
|
-
function persistSettings(ctx: ExtensionCommandContext | undefined, changeMsg: string): void {
|
|
4086
|
-
const { message, level } = saveAndEmitChanged(
|
|
4087
|
-
snapshotSettings(),
|
|
4088
|
-
changeMsg,
|
|
4089
|
-
(event, payload) => pi.events.emit(event, payload),
|
|
4090
|
-
);
|
|
4091
|
-
// `ctx` is absent only on the fleet path between sessions, where
|
|
4092
|
-
// `currentCtx` has been cleared and there is no UI to carry the warning to.
|
|
4093
|
-
// The write still happens.
|
|
4094
|
-
if (level === "warning") ctx?.ui.notify(message, level);
|
|
4095
|
-
}
|
|
4096
|
-
|
|
4097
3981
|
function notifyApplied(ctx: ExtensionCommandContext, successMsg: string) {
|
|
4098
3982
|
const { message, level } = saveAndEmitChanged(
|
|
4099
3983
|
snapshotSettings(),
|
|
@@ -4108,19 +3992,11 @@ Write the file using the write tool. Only write the file, nothing else.`;
|
|
|
4108
3992
|
handler: async (_args, ctx) => { await showAgentsMenu(ctx); },
|
|
4109
3993
|
});
|
|
4110
3994
|
|
|
4111
|
-
/**
|
|
4112
|
-
* What `/agents → Workflows` and the fleet list's `workflow` rows need from
|
|
4113
|
-
* here. One object, built once: both entry points open the same inspector,
|
|
4114
|
-
* and handing them different views of the session would let the two drift.
|
|
4115
|
-
*/
|
|
3995
|
+
/** Dependencies shared by `/agents → Workflows` and its inspector. */
|
|
4116
3996
|
const workflowMenuDeps: WorkflowMenuDeps = {
|
|
4117
3997
|
tasks: workflowTasks,
|
|
4118
3998
|
getRecord: id => manager.getRecord(id),
|
|
4119
3999
|
viewAgentConversation,
|
|
4120
|
-
// Read lazily: `currentCtx` is rebound on every session_start, and the
|
|
4121
|
-
// fleet list may act between sessions, when there is none.
|
|
4122
|
-
getCtx: () => currentCtx as unknown as ExtensionCommandContext | undefined,
|
|
4123
4000
|
};
|
|
4124
4001
|
|
|
4125
|
-
fleet.setWorkflowSource(fleetWorkflows, id => openWorkflowFromFleet(id, workflowMenuDeps));
|
|
4126
4002
|
}
|
package/src/prompts.ts
CHANGED
|
@@ -29,8 +29,8 @@ export interface PromptExtras {
|
|
|
29
29
|
* that makes the documentation true.
|
|
30
30
|
*
|
|
31
31
|
* Deliberately NOT applied to every subagent. In pi an ordinary agent's
|
|
32
|
-
* output IS read by a human — through
|
|
33
|
-
* `get_subagent_result` — so terse raw data would be
|
|
32
|
+
* output IS read by a human — through the Agents widget, the conversation
|
|
33
|
+
* viewer and `get_subagent_result` — so terse raw data would be wrong there.
|
|
34
34
|
*/
|
|
35
35
|
workflowChild?: boolean;
|
|
36
36
|
}
|
package/src/settings.ts
CHANGED
|
@@ -118,12 +118,6 @@ export interface SubagentsSettings {
|
|
|
118
118
|
* next pi session.
|
|
119
119
|
*/
|
|
120
120
|
toolDescriptionMode?: ToolDescriptionMode;
|
|
121
|
-
/**
|
|
122
|
-
* Whether the Claude Code-style FleetView (the navigable main+subagents list
|
|
123
|
-
* rendered below the editor) is shown. Defaults to `true`. Pure-UI: when off,
|
|
124
|
-
* the list never registers and the global key handler never captures input.
|
|
125
|
-
*/
|
|
126
|
-
fleetView?: boolean;
|
|
127
121
|
/**
|
|
128
122
|
* Whether `@handle message` typed at the prompt is routed to that subagent
|
|
129
123
|
* instead of the main model, and whether `@` offers running agents alongside
|
|
@@ -266,7 +260,7 @@ export interface SubagentsSettings {
|
|
|
266
260
|
reportUsage?: boolean;
|
|
267
261
|
/**
|
|
268
262
|
* Whether the subagent surfaces show an estimated dollar cost next to their
|
|
269
|
-
* token counts (widget,
|
|
263
|
+
* token counts (widget, conversation viewer, foreground results,
|
|
270
264
|
* completion notifications). Defaults to `false`. Applied live.
|
|
271
265
|
*
|
|
272
266
|
* Rendered as `~$0.0042` — the tilde marks it as pi's reported estimate
|
|
@@ -319,7 +313,6 @@ export interface SettingsAppliers {
|
|
|
319
313
|
setStrictAgentFiles: (b: boolean) => void;
|
|
320
314
|
setDisableDefaultAgents: (b: boolean) => void;
|
|
321
315
|
setToolDescriptionMode: (mode: ToolDescriptionMode) => void;
|
|
322
|
-
setFleetView: (b: boolean) => void;
|
|
323
316
|
setAgentMentions: (mode: AgentMentionMode) => void;
|
|
324
317
|
setRememberAgents: (b: boolean) => void;
|
|
325
318
|
setWidgetMode: (mode: WidgetMode) => void;
|
|
@@ -414,9 +407,6 @@ function sanitize(raw: unknown): SubagentsSettings {
|
|
|
414
407
|
if (typeof r.toolDescriptionMode === "string" && VALID_TOOL_DESCRIPTION_MODES.has(r.toolDescriptionMode)) {
|
|
415
408
|
out.toolDescriptionMode = r.toolDescriptionMode as ToolDescriptionMode;
|
|
416
409
|
}
|
|
417
|
-
if (typeof r.fleetView === "boolean") {
|
|
418
|
-
out.fleetView = r.fleetView;
|
|
419
|
-
}
|
|
420
410
|
// Was a boolean before the `model` mode existed. A hand-written or
|
|
421
411
|
// previously-written `true` means "on", which is now the default `model`.
|
|
422
412
|
if (typeof r.agentMentions === "boolean") {
|
|
@@ -526,7 +516,6 @@ export function applySettings(s: SubagentsSettings, appliers: SettingsAppliers):
|
|
|
526
516
|
if (typeof s.strictAgentFiles === "boolean") appliers.setStrictAgentFiles(s.strictAgentFiles);
|
|
527
517
|
if (typeof s.disableDefaultAgents === "boolean") appliers.setDisableDefaultAgents(s.disableDefaultAgents);
|
|
528
518
|
if (s.toolDescriptionMode) appliers.setToolDescriptionMode(s.toolDescriptionMode);
|
|
529
|
-
if (typeof s.fleetView === "boolean") appliers.setFleetView(s.fleetView);
|
|
530
519
|
if (s.agentMentions) appliers.setAgentMentions(s.agentMentions);
|
|
531
520
|
if (typeof s.rememberAgents === "boolean") appliers.setRememberAgents(s.rememberAgents);
|
|
532
521
|
if (s.widgetMode) appliers.setWidgetMode(s.widgetMode);
|
package/src/ui/agent-mention.ts
CHANGED
|
@@ -50,8 +50,8 @@ import type { AgentRecord, AgentTombstone } from "../types.js";
|
|
|
50
50
|
/**
|
|
51
51
|
* One thing `@` can address, and what sending to it will do. `typeLabel` is the
|
|
52
52
|
* agent's `display_name`, resolved by the caller: this module stays independent
|
|
53
|
-
* of the type registry, but the popup must agree with
|
|
54
|
-
* which
|
|
53
|
+
* of the type registry, but the popup must agree with the Agents widget,
|
|
54
|
+
* which renders the label rather than the raw type.
|
|
55
55
|
*/
|
|
56
56
|
export type MentionTarget =
|
|
57
57
|
| { kind: "record"; handle: string; record: AgentRecord; typeLabel: string }
|
package/src/ui/agent-widget.ts
CHANGED
|
@@ -267,8 +267,6 @@ export class AgentWidget {
|
|
|
267
267
|
private inputUnsub: (() => void) | undefined;
|
|
268
268
|
/** Whether arrow keys currently navigate the agent roster. */
|
|
269
269
|
private navigationActive = false;
|
|
270
|
-
/** Number of modal surfaces currently owning this listener's input suspension. */
|
|
271
|
-
private navigationSuspensionDepth = 0;
|
|
272
270
|
/** Stable identity of the selected row, so roster changes do not jump selection. */
|
|
273
271
|
private selectedAgentId: string | undefined;
|
|
274
272
|
/** Last logical roster index of the selected row, used when it disappears. */
|
|
@@ -408,16 +406,6 @@ export class AgentWidget {
|
|
|
408
406
|
return this.cachedRoster;
|
|
409
407
|
}
|
|
410
408
|
|
|
411
|
-
/** Suspend only this listener's keyboard ownership; never alter the widget. */
|
|
412
|
-
suspendNavigation(): void {
|
|
413
|
-
this.navigationSuspensionDepth++;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
/** Restore one layer of this listener's keyboard ownership. */
|
|
417
|
-
resumeNavigation(): void {
|
|
418
|
-
if (this.navigationSuspensionDepth > 0) this.navigationSuspensionDepth--;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
409
|
/** Set the UI context (grabbed from first tool execution). */
|
|
422
410
|
setUICtx(ctx: UICtx): boolean {
|
|
423
411
|
if (ctx === this.uiCtx) return false;
|
|
@@ -431,7 +419,6 @@ export class AgentWidget {
|
|
|
431
419
|
this.tui = undefined;
|
|
432
420
|
this.lastStatusText = undefined;
|
|
433
421
|
this.lastRenderKey = undefined;
|
|
434
|
-
this.navigationSuspensionDepth = 0;
|
|
435
422
|
this.navigationActive = false;
|
|
436
423
|
this.selectedAgentId = undefined;
|
|
437
424
|
this.selectedRosterIndex = 0;
|
|
@@ -450,7 +437,7 @@ export class AgentWidget {
|
|
|
450
437
|
}
|
|
451
438
|
|
|
452
439
|
/** Request a render on the currently registered TUI without touching input. */
|
|
453
|
-
requestUiRefresh(force =
|
|
440
|
+
requestUiRefresh(force = false): boolean {
|
|
454
441
|
if (!this.tui || typeof this.tui.requestRender !== "function") return false;
|
|
455
442
|
this.tui.requestRender(force);
|
|
456
443
|
return true;
|
|
@@ -573,7 +560,6 @@ export class AgentWidget {
|
|
|
573
560
|
/** Handle terminal input before it reaches the focused prompt editor. */
|
|
574
561
|
handleKey(data: string): { consume?: boolean; data?: string } | undefined {
|
|
575
562
|
if (!this.uiCtx || isKeyRelease(data)) return undefined;
|
|
576
|
-
if (this.navigationSuspensionDepth > 0) return undefined;
|
|
577
563
|
if (!this.editorHasFocus()) {
|
|
578
564
|
if (this.navigationActive) this.deactivate();
|
|
579
565
|
return undefined;
|
|
@@ -1111,7 +1097,6 @@ export class AgentWidget {
|
|
|
1111
1097
|
this.tui = undefined;
|
|
1112
1098
|
this.lastStatusText = undefined;
|
|
1113
1099
|
this.lastRenderKey = undefined;
|
|
1114
|
-
this.navigationSuspensionDepth = 0;
|
|
1115
1100
|
this.navigationActive = false;
|
|
1116
1101
|
this.selectedAgentId = undefined;
|
|
1117
1102
|
this.selectedRosterIndex = 0;
|
package/src/ui/workflow-menu.ts
CHANGED
|
@@ -3,13 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The same shape `schedule-menu.ts` has for `/agents → Scheduled jobs`: the
|
|
5
5
|
* submenu and the overlay it opens live here, and everything they need arrives
|
|
6
|
-
* as {@link WorkflowMenuDeps} rather than through a closure.
|
|
7
|
-
* reached from two places — this menu and a `workflow` row in the fleet list —
|
|
8
|
-
* and both go through `showWorkflowDialog`, so the two entry points cannot
|
|
9
|
-
* drift apart on what the keys do.
|
|
6
|
+
* as {@link WorkflowMenuDeps} rather than through a closure.
|
|
10
7
|
*
|
|
11
|
-
* Lives in the agents menu rather than as a top-level `/workflows` command:
|
|
12
|
-
*
|
|
8
|
+
* Lives in the agents menu rather than as a top-level `/workflows` command: a
|
|
9
|
+
* second command name would only add
|
|
13
10
|
* a collision surface (pi renames duplicate commands to `/workflows:1` and
|
|
14
11
|
* `/workflows:2`, which breaks the bare name for both).
|
|
15
12
|
*/
|
|
@@ -31,12 +28,6 @@ export interface WorkflowMenuDeps {
|
|
|
31
28
|
getRecord(id: string): AgentRecord | undefined;
|
|
32
29
|
/** The conversation overlay `c` opens on an agent row. */
|
|
33
30
|
viewAgentConversation(ctx: ExtensionCommandContext, record: AgentRecord): Promise<void>;
|
|
34
|
-
/**
|
|
35
|
-
* The session context, for the fleet-list entry point — that one is a
|
|
36
|
-
* keypress in a list that holds no `ctx` of its own. Undefined between
|
|
37
|
-
* sessions, which is a no-op rather than an error.
|
|
38
|
-
*/
|
|
39
|
-
getCtx(): ExtensionCommandContext | undefined;
|
|
40
31
|
}
|
|
41
32
|
|
|
42
33
|
/**
|
|
@@ -54,9 +45,7 @@ export async function showWorkflowDialog(
|
|
|
54
45
|
task: WorkflowTask,
|
|
55
46
|
deps: WorkflowMenuDeps,
|
|
56
47
|
): Promise<void> {
|
|
57
|
-
//
|
|
58
|
-
// reached the same way: both are rows of the fleet list, and opening one
|
|
59
|
-
// must not behave unlike opening the other. Inline, the frame would render
|
|
48
|
+
// Use an overlay like the conversation viewer. Inline, the frame would render
|
|
60
49
|
// into the conversation and stay in the scrollback after it closed.
|
|
61
50
|
const { VIEWPORT_HEIGHT_PCT } = await import("./conversation-viewer.js");
|
|
62
51
|
/**
|
|
@@ -147,22 +136,6 @@ export async function showWorkflowDialog(
|
|
|
147
136
|
);
|
|
148
137
|
}
|
|
149
138
|
|
|
150
|
-
/**
|
|
151
|
-
* Open a run from the fleet list.
|
|
152
|
-
*
|
|
153
|
-
* The list hands back an id rather than a task, so a run that settled and was
|
|
154
|
-
* swept between render and keypress is a no-op instead of a crash. `esc` in the
|
|
155
|
-
* dialog closes it and control returns to the list — which is why the promise
|
|
156
|
-
* is handed back: the list puts the cursor back on the run rather than dropping
|
|
157
|
-
* the reader at `main`.
|
|
158
|
-
*/
|
|
159
|
-
export function openWorkflowFromFleet(id: string, deps: WorkflowMenuDeps): Promise<void> | void {
|
|
160
|
-
const task = deps.tasks.get(id);
|
|
161
|
-
const ctx = deps.getCtx();
|
|
162
|
-
if (task === undefined || ctx === undefined) return;
|
|
163
|
-
return showWorkflowDialog(ctx, task, deps);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
139
|
/** `/agents → Workflows` — list this session's runs, open one. */
|
|
167
140
|
export async function showWorkflowsMenu(
|
|
168
141
|
ctx: ExtensionCommandContext,
|