pi-subagents 0.47.1 → 0.49.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/CHANGELOG.md +51 -0
- package/README.md +2 -0
- package/docs/agents.md +1 -0
- package/docs/configuration.md +74 -7
- package/docs/missions.md +4 -2
- package/docs/observability.md +27 -3
- package/docs/tool-reference.md +1 -1
- package/package.json +1 -1
- package/src/agents/agents.ts +36 -15
- package/src/api/preflight.ts +1 -1
- package/src/extension/config.ts +26 -0
- package/src/extension/doctor.ts +40 -0
- package/src/extension/index.ts +39 -8
- package/src/extension/public-execution.ts +35 -4
- package/src/extension/rpc.ts +3 -9
- package/src/extension/schemas.ts +10 -9
- package/src/extension/tool-description.ts +10 -8
- package/src/inspectors/herdr/actions.ts +11 -2
- package/src/inspectors/herdr/inspector-runner.ts +16 -3
- package/src/intercom/intercom-bridge.ts +7 -2
- package/src/missions/lifecycle.ts +4 -7
- package/src/missions/store.ts +12 -7
- package/src/missions/workflow-state.ts +6 -2
- package/src/runs/background/active-async-capacity.ts +431 -0
- package/src/runs/background/active-run-index.ts +9 -5
- package/src/runs/background/async-execution.ts +141 -65
- package/src/runs/background/async-job-tracker.ts +4 -0
- package/src/runs/background/async-resume.ts +18 -2
- package/src/runs/background/async-status.ts +11 -5
- package/src/runs/background/chain-append.ts +33 -15
- package/src/runs/background/fleet-view.ts +18 -5
- package/src/runs/background/owned-process-tree.ts +104 -0
- package/src/runs/background/process-terminal.ts +17 -3
- package/src/runs/background/resume-guidance.ts +27 -7
- package/src/runs/background/retained-children.ts +14 -6
- package/src/runs/background/run-status.ts +101 -5
- package/src/runs/background/stale-run-reconciler.ts +3 -3
- package/src/runs/background/subagent-runner.ts +60 -32
- package/src/runs/foreground/chain-execution.ts +37 -2
- package/src/runs/foreground/execution.ts +90 -19
- package/src/runs/foreground/foreground-control.ts +12 -0
- package/src/runs/foreground/prompt-audit.ts +172 -0
- package/src/runs/foreground/subagent-executor.ts +829 -203
- package/src/runs/shared/acceptance.ts +13 -4
- package/src/runs/shared/completion-guard.ts +17 -1
- package/src/runs/shared/llm-intent-arbiter.ts +302 -0
- package/src/runs/shared/parallel-utils.ts +2 -0
- package/src/runs/shared/pi-args.ts +44 -1
- package/src/runs/shared/run-fanout-budget.ts +280 -0
- package/src/runs/shared/single-output.ts +4 -2
- package/src/runs/shared/task-intent.ts +19 -3
- package/src/runs/shared/worktree.ts +17 -5
- package/src/shared/agent-stream-options.ts +5 -0
- package/src/shared/artifacts.ts +2 -6
- package/src/shared/display-text.ts +50 -0
- package/src/shared/node-executable.ts +21 -0
- package/src/shared/types.ts +115 -4
- package/src/shared/utils.ts +3 -1
- package/src/tui/fleet-status.ts +7 -5
- package/src/tui/fleet-transcript.ts +1 -48
- package/src/tui/fleet.ts +228 -13
- package/src/tui/render.ts +86 -39
- package/src/watchdog/permission-arbiter.ts +2 -1
- package/src/watchdog/review.ts +4 -3
- package/src/workflows/chat-progress.ts +8 -2
- package/src/workflows/scripted-workflow.ts +66 -10
package/src/tui/fleet.ts
CHANGED
|
@@ -17,12 +17,14 @@ import { contextModeBadge, contextModeLabel } from "../runs/shared/context-mode.
|
|
|
17
17
|
import { FLEET_STATUS_WIDGET_KEY } from "./fleet-status.ts";
|
|
18
18
|
import { readFleetTranscript, renderFleetTranscript, type FleetTranscript } from "./fleet-transcript.ts";
|
|
19
19
|
import { handleHerdrInspectorAction } from "../inspectors/herdr/actions.ts";
|
|
20
|
+
import { getLivePromptAudit, type LivePromptAudit, type PromptAuditView } from "../runs/foreground/prompt-audit.ts";
|
|
20
21
|
|
|
21
22
|
const REFRESH_MS = 750;
|
|
22
23
|
const MAX_RECENT_ASYNC_RUNS = 20;
|
|
23
24
|
const MAX_FLEET_HISTORY_CANDIDATES = 100;
|
|
24
25
|
const TRANSCRIPT_LINES = 200;
|
|
25
26
|
const OUTPUT_TAIL_BYTES = 64 * 1024;
|
|
27
|
+
const PROMPT_AUDIT_SUMMARY_WIDTH = 160;
|
|
26
28
|
|
|
27
29
|
export const DEFAULT_FLEET_KEYBINDINGS: Record<FleetKeybindingAction, string[]> = {
|
|
28
30
|
close: ["escape", "ctrl+c", "q"],
|
|
@@ -91,6 +93,7 @@ export interface FleetActionHandlers {
|
|
|
91
93
|
steer(input: { runId: string; asyncDir: string; index?: number; message: string; mode: SteerDeliveryMode }): Promise<FleetActionResult>;
|
|
92
94
|
stop(input: { runId: string; asyncDir: string; index?: number }): Promise<FleetActionResult> | FleetActionResult;
|
|
93
95
|
inspect?(input: { runId: string; asyncDir: string; index?: number }): Promise<FleetActionResult>;
|
|
96
|
+
redoPrompt?(input: { runId: string; index: number; guidance: string; control?: ForegroundRunControl }): Promise<FleetActionResult>;
|
|
94
97
|
}
|
|
95
98
|
|
|
96
99
|
export interface FleetViewOptions {
|
|
@@ -101,6 +104,7 @@ export interface FleetViewOptions {
|
|
|
101
104
|
markdownTheme?: MarkdownTheme;
|
|
102
105
|
fleetKeybindings?: FleetKeybindingsConfig;
|
|
103
106
|
actions?: FleetActionHandlers;
|
|
107
|
+
copyText?: (text: string) => Promise<void> | void;
|
|
104
108
|
}
|
|
105
109
|
|
|
106
110
|
function belongsToCurrentSession(sessionId: string | undefined, currentSessionId: string | null): boolean {
|
|
@@ -280,23 +284,62 @@ function statusGlyph(item: FleetItem, theme: Theme): string {
|
|
|
280
284
|
return theme.fg("error", "✗");
|
|
281
285
|
}
|
|
282
286
|
|
|
283
|
-
function
|
|
287
|
+
function foregroundPromptAuditCount(item: Extract<FleetItem, { kind: "foreground-active" }>, state: SubagentState): number {
|
|
288
|
+
if (!state.currentSessionId || item.control.sessionId !== state.currentSessionId) return 0;
|
|
289
|
+
if (!item.control.activeChildren) return getLivePromptAudit(item.control, item.index ?? 0) ? 1 : 0;
|
|
290
|
+
return [...item.control.activeChildren.keys()].filter((index) => getLivePromptAudit(item.control, index)).length;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function authoredPromptSummary(text: string): string | undefined {
|
|
294
|
+
const summary = text.replace(/\s+/g, " ").trim();
|
|
295
|
+
return summary ? truncateToWidth(summary, PROMPT_AUDIT_SUMMARY_WIDTH, "…") : undefined;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function foregroundAuthoredPromptSummary(item: Extract<FleetItem, { kind: "foreground-active" }>, state: SubagentState): string | undefined {
|
|
299
|
+
if (!state.currentSessionId || item.control.sessionId !== state.currentSessionId) return undefined;
|
|
300
|
+
const prompt = getLivePromptAudit(item.control, item.index ?? 0);
|
|
301
|
+
return prompt ? authoredPromptSummary(prompt.authoredTask) : undefined;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function promptAuditText(prompt: LivePromptAudit, view: PromptAuditView): string {
|
|
305
|
+
switch (view) {
|
|
306
|
+
case "authored": return prompt.authoredTask;
|
|
307
|
+
case "runtime": return prompt.runtimeAdditions;
|
|
308
|
+
case "effective": return prompt.finalEffectivePrompt;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function promptAuditViewLabel(view: PromptAuditView): string {
|
|
313
|
+
switch (view) {
|
|
314
|
+
case "authored": return "[1] Authored task";
|
|
315
|
+
case "runtime": return "[2] Runtime additions";
|
|
316
|
+
case "effective": return "[3] Final effective prompt";
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function foregroundActiveDetail(item: Extract<FleetItem, { kind: "foreground-active" }>, state: SubagentState): string[] {
|
|
284
321
|
const { control } = item;
|
|
285
322
|
const live = item.activeChild ?? control;
|
|
286
323
|
const modelThinking = formatModelThinking(live.model, live.thinking);
|
|
324
|
+
const promptAuditCount = foregroundPromptAuditCount(item, state);
|
|
325
|
+
const promptSummary = foregroundAuthoredPromptSummary(item, state);
|
|
287
326
|
const lines = [
|
|
288
327
|
`Run: ${item.runId}`,
|
|
289
328
|
"Source: foreground",
|
|
290
329
|
`State: running`,
|
|
291
330
|
`Mode: ${control.mode}`,
|
|
292
331
|
control.parentWorkflowRunId ? `Workflow child of: ${control.parentWorkflowRunId}${control.workflowKey ? ` (${control.workflowKey})` : ""}` : undefined,
|
|
332
|
+
control.sourceRunId ? `Redo source: ${control.sourceRunId}` : undefined,
|
|
333
|
+
control.supersededByRunId ? `Superseded by: ${control.supersededByRunId}` : undefined,
|
|
293
334
|
item.index !== undefined ? `Child: ${item.index} (${item.agent})` : `Agent: ${item.agent}`,
|
|
294
335
|
modelThinking ? `Model: ${modelThinking}` : undefined,
|
|
336
|
+
promptSummary ? `Task: ${promptSummary}` : undefined,
|
|
295
337
|
`Started: ${new Date(live.startedAt).toISOString()}`,
|
|
296
338
|
live.currentTool ? `Current tool: ${live.currentTool}${live.currentPath ? ` · ${shortenPath(live.currentPath)}` : ""}` : undefined,
|
|
297
339
|
live.turnCount !== undefined ? `Turns: ${live.turnCount}` : undefined,
|
|
298
340
|
live.toolCount !== undefined ? `Tools: ${live.toolCount}` : undefined,
|
|
299
341
|
live.tokens !== undefined ? `Tokens: ${formatTokens(live.tokens)}` : undefined,
|
|
342
|
+
promptAuditCount > 0 ? `Prompt audit: ${promptAuditCount} live · 3 views · p opens` : undefined,
|
|
300
343
|
"",
|
|
301
344
|
"Transcript",
|
|
302
345
|
"Live foreground output remains in the expanded subagent tool result. Persisted output and session paths appear here after the child settles.",
|
|
@@ -404,7 +447,7 @@ function asyncDetail(item: Extract<FleetItem, { kind: "async" }>): string[] {
|
|
|
404
447
|
function detailLines(item: FleetItem | undefined, error: string | undefined, state: SubagentState): string[] {
|
|
405
448
|
if (!item) return [error ? `Fleet scan failed: ${error}` : "No current-session foreground or recent async children.", "", "New runs appear here automatically while this inspector remains open."];
|
|
406
449
|
const lines = item.kind === "foreground-active"
|
|
407
|
-
? foregroundActiveDetail(item)
|
|
450
|
+
? foregroundActiveDetail(item, state)
|
|
408
451
|
: item.kind === "foreground-recent"
|
|
409
452
|
? foregroundRecentDetail(item, state)
|
|
410
453
|
: asyncDetail(item);
|
|
@@ -430,7 +473,7 @@ function fleetArtifactsRoot(state: SubagentState, cwd: string): string {
|
|
|
430
473
|
return getArtifactsDir(
|
|
431
474
|
state.parentSessionFile ?? null,
|
|
432
475
|
cwd,
|
|
433
|
-
state.artifactDirPreference
|
|
476
|
+
state.artifactDirPreference,
|
|
434
477
|
);
|
|
435
478
|
}
|
|
436
479
|
|
|
@@ -467,6 +510,7 @@ function transcriptTarget(item: FleetItem, state: SubagentState): { path: string
|
|
|
467
510
|
item.run.asyncDir,
|
|
468
511
|
fleetArtifactsRoot(state, state.baseCwd),
|
|
469
512
|
trackedJob?.cwd ? fleetArtifactsRoot(state, trackedJob.cwd) : undefined,
|
|
513
|
+
item.run.sessionFile ? getArtifactsDir(item.run.sessionFile, item.run.cwd ?? state.baseCwd, state.artifactDirPreference) : undefined,
|
|
470
514
|
]),
|
|
471
515
|
};
|
|
472
516
|
}
|
|
@@ -517,7 +561,7 @@ function itemStats(item: FleetItem): string[] {
|
|
|
517
561
|
].filter((value): value is string => Boolean(value));
|
|
518
562
|
}
|
|
519
563
|
|
|
520
|
-
function structuredHeader(item: FleetItem, width: number, theme: Theme, conversationState: string): string[] {
|
|
564
|
+
function structuredHeader(item: FleetItem, width: number, theme: Theme, conversationState: string, promptSummary?: string): string[] {
|
|
521
565
|
const lines: string[] = [];
|
|
522
566
|
lines.push(rightAligned(` ${statusGlyph(item, theme)} ${theme.bold(item.agent)}`, theme.fg("dim", item.state), width));
|
|
523
567
|
const child = item.index !== undefined ? ` · child ${item.index + 1}` : "";
|
|
@@ -526,10 +570,7 @@ function structuredHeader(item: FleetItem, width: number, theme: Theme, conversa
|
|
|
526
570
|
lines.push(` ${theme.fg("dim", identity)}`);
|
|
527
571
|
const stats = itemStats(item);
|
|
528
572
|
if (stats.length) lines.push(` ${theme.fg("muted", stats.join(" · "))}`);
|
|
529
|
-
if (
|
|
530
|
-
const task = item.description.replace(/\s+/g, " ").trim();
|
|
531
|
-
lines.push(` ${theme.fg("dim", "Task")} ${task}`);
|
|
532
|
-
}
|
|
573
|
+
if (promptSummary) lines.push(` ${theme.fg("muted", `Task: ${promptSummary}`)}`);
|
|
533
574
|
lines.push(`${theme.fg("accent", "Conversation")} ${theme.fg("dim", `· ${conversationState}`)}`);
|
|
534
575
|
return lines.map((line) => truncateToWidth(line, width));
|
|
535
576
|
}
|
|
@@ -578,8 +619,11 @@ export class SubagentFleetComponent implements Component {
|
|
|
578
619
|
private detailViewportHeight = 8;
|
|
579
620
|
private bodyHeight = 8;
|
|
580
621
|
private expandedTools = false;
|
|
622
|
+
private promptAuditOpen = false;
|
|
623
|
+
private promptAuditView: PromptAuditView = "authored";
|
|
581
624
|
private actionNotice: FleetActionResult | undefined;
|
|
582
625
|
private steerDraft: string | undefined;
|
|
626
|
+
private redoGuidanceDraft: string | undefined;
|
|
583
627
|
private steerMode: SteerDeliveryMode = "steer";
|
|
584
628
|
private stopConfirming = false;
|
|
585
629
|
private actionBusy = false;
|
|
@@ -635,8 +679,45 @@ export class SubagentFleetComponent implements Component {
|
|
|
635
679
|
this.tui.requestRender();
|
|
636
680
|
}
|
|
637
681
|
|
|
682
|
+
private selectedPromptAudit(): LivePromptAudit | undefined {
|
|
683
|
+
const item = this.snapshot.items[this.selected];
|
|
684
|
+
if (item?.kind !== "foreground-active") return undefined;
|
|
685
|
+
if (!this.state.currentSessionId || item.control.sessionId !== this.state.currentSessionId) return undefined;
|
|
686
|
+
return getLivePromptAudit(item.control, item.index ?? 0);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
private promptAuditItems(): Array<{ item: Extract<FleetItem, { kind: "foreground-active" }>; prompt: LivePromptAudit }> {
|
|
690
|
+
const selected = this.snapshot.items[this.selected];
|
|
691
|
+
if (selected?.kind !== "foreground-active" || !this.state.currentSessionId || selected.control.sessionId !== this.state.currentSessionId) return [];
|
|
692
|
+
return this.snapshot.items.flatMap((item) => {
|
|
693
|
+
if (item.kind !== "foreground-active" || item.control !== selected.control) return [];
|
|
694
|
+
const prompt = getLivePromptAudit(item.control, item.index ?? 0);
|
|
695
|
+
return prompt ? [{ item, prompt }] : [];
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
private selectedPromptText(): string | undefined {
|
|
700
|
+
const prompt = this.selectedPromptAudit();
|
|
701
|
+
return prompt ? promptAuditText(prompt, this.promptAuditView) : undefined;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
private movePromptSelection(delta: number): void {
|
|
705
|
+
const items = this.promptAuditItems();
|
|
706
|
+
if (items.length === 0) return;
|
|
707
|
+
const currentKey = this.snapshot.items[this.selected]?.key;
|
|
708
|
+
const current = Math.max(0, items.findIndex(({ item }) => item.key === currentKey));
|
|
709
|
+
const target = items[Math.max(0, Math.min(items.length - 1, current + delta))]?.item;
|
|
710
|
+
if (!target) return;
|
|
711
|
+
this.selected = this.snapshot.items.findIndex((item) => item.key === target.key);
|
|
712
|
+
this.selectedKey = target.key;
|
|
713
|
+
this.detailScroll = 0;
|
|
714
|
+
this.detailAutoFollow = false;
|
|
715
|
+
this.tui.requestRender();
|
|
716
|
+
}
|
|
717
|
+
|
|
638
718
|
private resetActionInput(): void {
|
|
639
719
|
this.steerDraft = undefined;
|
|
720
|
+
this.redoGuidanceDraft = undefined;
|
|
640
721
|
this.steerMode = "steer";
|
|
641
722
|
this.stopConfirming = false;
|
|
642
723
|
}
|
|
@@ -668,6 +749,9 @@ export class SubagentFleetComponent implements Component {
|
|
|
668
749
|
if (this.steerDraft !== undefined) {
|
|
669
750
|
lines.push(this.theme.fg("accent", `Steer message (${this.steerMode}): ${this.steerDraft}${this.theme.fg("dim", "▌")}`));
|
|
670
751
|
lines.push(this.theme.fg("dim", "Enter sends · Tab changes mode · Esc cancels · Backspace edits"));
|
|
752
|
+
} else if (this.redoGuidanceDraft !== undefined) {
|
|
753
|
+
lines.push(this.theme.fg("accent", `Redo guidance: ${this.redoGuidanceDraft}${this.theme.fg("dim", "▌")}`));
|
|
754
|
+
lines.push(this.theme.fg("dim", "Enter rewrites and reruns · Esc cancels · Backspace edits"));
|
|
671
755
|
} else if (this.stopConfirming) {
|
|
672
756
|
const selected = this.snapshot.items[this.selected];
|
|
673
757
|
lines.push(this.theme.fg("warning", `Confirm stop for async run ${selected?.runId ?? "selected run"}?`));
|
|
@@ -714,6 +798,82 @@ export class SubagentFleetComponent implements Component {
|
|
|
714
798
|
}
|
|
715
799
|
|
|
716
800
|
handleInput(data: string): void {
|
|
801
|
+
if (this.promptAuditOpen && this.redoGuidanceDraft !== undefined) {
|
|
802
|
+
if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c")) {
|
|
803
|
+
this.resetActionInput();
|
|
804
|
+
this.tui.requestRender();
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
if (matchesKey(data, "return") || data === "\r" || data === "\n") {
|
|
808
|
+
const guidance = this.redoGuidanceDraft.trim();
|
|
809
|
+
if (!guidance) {
|
|
810
|
+
this.setActionNotice({ text: "Redo guidance cannot be empty.", isError: true });
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
const selected = this.snapshot.items[this.selected];
|
|
814
|
+
if (selected?.kind !== "foreground-active" || !this.options.actions?.redoPrompt) {
|
|
815
|
+
this.setActionNotice({ text: "Redo is unavailable for this prompt.", isError: true });
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
this.runAction(() => this.options.actions!.redoPrompt!({ runId: selected.runId, index: selected.index ?? 0, guidance, control: selected.control }));
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
if (matchesKey(data, "backspace") || data === "\x7f") {
|
|
822
|
+
this.redoGuidanceDraft = this.redoGuidanceDraft.slice(0, -1);
|
|
823
|
+
this.tui.requestRender();
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
if (data.length === 1 && data >= " " && data !== "\x7f") {
|
|
827
|
+
this.redoGuidanceDraft += data;
|
|
828
|
+
this.tui.requestRender();
|
|
829
|
+
}
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
if (this.promptAuditOpen) {
|
|
833
|
+
if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c")) {
|
|
834
|
+
this.promptAuditOpen = false;
|
|
835
|
+
this.detailAutoFollow = true;
|
|
836
|
+
this.tui.requestRender();
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
if (data === "j") return this.movePromptSelection(1);
|
|
840
|
+
if (data === "k") return this.movePromptSelection(-1);
|
|
841
|
+
if (data === "1" || data === "2" || data === "3") {
|
|
842
|
+
this.promptAuditView = data === "1" ? "authored" : data === "2" ? "runtime" : "effective";
|
|
843
|
+
this.detailScroll = 0;
|
|
844
|
+
this.tui.requestRender();
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
if (data === "g") {
|
|
848
|
+
const prompt = this.selectedPromptAudit();
|
|
849
|
+
if (!prompt) this.setActionNotice({ text: "Prompt Audit is available only for a live child owned by this session.", isError: true });
|
|
850
|
+
else if (!prompt.rerun) this.setActionNotice({ text: "Redo is not safe for this prompt in this slice.", isError: true });
|
|
851
|
+
else if (!this.options.actions?.redoPrompt) this.setActionNotice({ text: "Redo controls are unavailable in this context.", isError: true });
|
|
852
|
+
else {
|
|
853
|
+
this.actionNotice = undefined;
|
|
854
|
+
this.redoGuidanceDraft = "";
|
|
855
|
+
this.detailAutoFollow = false;
|
|
856
|
+
this.detailScroll = 0;
|
|
857
|
+
this.tui.requestRender();
|
|
858
|
+
}
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
if (data === "c") {
|
|
862
|
+
const text = this.selectedPromptText();
|
|
863
|
+
if (!text) this.setActionNotice({ text: "No prompt view is available to copy.", isError: true });
|
|
864
|
+
else if (!this.options.copyText) this.setActionNotice({ text: "Clipboard is unavailable in this context.", isError: true });
|
|
865
|
+
else this.runAction(async () => {
|
|
866
|
+
await this.options.copyText!(text);
|
|
867
|
+
return { text: "Copied visible prompt view." };
|
|
868
|
+
});
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
if (matchesFleetAction(data, this.keybindings, "scrollUp")) return this.scrollDetail(-1);
|
|
872
|
+
if (matchesFleetAction(data, this.keybindings, "scrollDown")) return this.scrollDetail(1);
|
|
873
|
+
if (matchesFleetAction(data, this.keybindings, "pageUp")) return this.scrollDetail(-this.detailViewportHeight);
|
|
874
|
+
if (matchesFleetAction(data, this.keybindings, "pageDown")) return this.scrollDetail(this.detailViewportHeight);
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
717
877
|
if (this.steerDraft !== undefined) {
|
|
718
878
|
if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c")) {
|
|
719
879
|
this.resetActionInput();
|
|
@@ -785,6 +945,18 @@ export class SubagentFleetComponent implements Component {
|
|
|
785
945
|
this.tui.requestRender();
|
|
786
946
|
return;
|
|
787
947
|
}
|
|
948
|
+
if (data === "p") {
|
|
949
|
+
if (!this.selectedPromptAudit()) this.setActionNotice({ text: "Prompt Audit is available only for a live child owned by this session.", isError: true });
|
|
950
|
+
else {
|
|
951
|
+
this.promptAuditOpen = true;
|
|
952
|
+
this.promptAuditView = "authored";
|
|
953
|
+
this.detailScroll = 0;
|
|
954
|
+
this.detailAutoFollow = false;
|
|
955
|
+
this.actionNotice = undefined;
|
|
956
|
+
this.tui.requestRender();
|
|
957
|
+
}
|
|
958
|
+
return;
|
|
959
|
+
}
|
|
788
960
|
if (matchesFleetAction(data, this.keybindings, "steer")) {
|
|
789
961
|
const target = this.selectedAsyncAction();
|
|
790
962
|
if ("reason" in target || !this.options.actions) this.setActionNotice({ text: "reason" in target ? target.reason : "Fleet controls are unavailable in this context.", isError: true });
|
|
@@ -830,7 +1002,7 @@ export class SubagentFleetComponent implements Component {
|
|
|
830
1002
|
const marker = index === this.selected ? this.theme.fg("accent", "›") : " ";
|
|
831
1003
|
const context = item.kind === "async" ? contextModeBadge(this.theme, item.step?.context ?? item.run.context) : item.kind === "foreground-recent" ? contextModeBadge(this.theme, item.child.context) : "";
|
|
832
1004
|
const agent = index === this.selected ? this.theme.bold(item.agent) : item.agent;
|
|
833
|
-
const identity = item.
|
|
1005
|
+
const identity = item.runId.slice(0, 8);
|
|
834
1006
|
const left = `${marker} ${statusGlyph(item, this.theme)} ${agent}${context} ${this.theme.fg("dim", `· ${identity}`)}`;
|
|
835
1007
|
return rightAligned(left, this.theme.fg("dim", item.state), width);
|
|
836
1008
|
});
|
|
@@ -853,7 +1025,35 @@ export class SubagentFleetComponent implements Component {
|
|
|
853
1025
|
return { transcript, body: [...body] };
|
|
854
1026
|
}
|
|
855
1027
|
|
|
1028
|
+
private promptAuditDetail(width: number): FleetDetailSections {
|
|
1029
|
+
const selected = this.snapshot.items[this.selected];
|
|
1030
|
+
const prompt = this.selectedPromptAudit();
|
|
1031
|
+
const items = this.promptAuditItems();
|
|
1032
|
+
const selectedPosition = selected ? items.findIndex(({ item }) => item.key === selected.key) : -1;
|
|
1033
|
+
const live = selected?.kind === "foreground-active" ? selected.activeChild ?? selected.control : undefined;
|
|
1034
|
+
const viewLabel = promptAuditViewLabel(this.promptAuditView);
|
|
1035
|
+
const promptText = this.selectedPromptText()
|
|
1036
|
+
?? this.theme.fg("muted", "Selected prompt unavailable.");
|
|
1037
|
+
const raw = [
|
|
1038
|
+
this.theme.bold("Prompt Audit"),
|
|
1039
|
+
this.theme.fg("dim", "Retention: live memory only · no storage"),
|
|
1040
|
+
selected?.kind === "foreground-active" ? `Run: ${selected.runId}${selected.index !== undefined ? ` · Child: ${selected.index}` : ""} · Agent: ${selected.agent}` : "Selected prompt unavailable",
|
|
1041
|
+
live ? `Started: ${new Date(live.startedAt).toISOString()}` : undefined,
|
|
1042
|
+
live ? `Model: ${formatModelThinking(live.model, live.thinking) || "default"}` : undefined,
|
|
1043
|
+
prompt?.cwd ? `Cwd: ${prompt.cwd}` : undefined,
|
|
1044
|
+
prompt?.outputPath ? `Output: ${prompt.outputPath}` : undefined,
|
|
1045
|
+
this.theme.fg("dim", `Live children: ${items.map(({ item }) => item.agent).join(", ") || "none"}`),
|
|
1046
|
+
this.theme.fg("dim", selectedPosition >= 0 ? `Selected: ${selectedPosition + 1}/${items.length}` : "Selected prompt unavailable"),
|
|
1047
|
+
"",
|
|
1048
|
+
this.theme.fg("accent", viewLabel),
|
|
1049
|
+
promptText,
|
|
1050
|
+
];
|
|
1051
|
+
const body = raw.filter((line): line is string => line !== undefined).flatMap((line) => wrapTextWithAnsi(line, Math.max(1, width)));
|
|
1052
|
+
return { header: [], body: this.withActionLines(body) };
|
|
1053
|
+
}
|
|
1054
|
+
|
|
856
1055
|
private wrappedDetail(width: number): FleetDetailSections {
|
|
1056
|
+
if (this.promptAuditOpen) return this.promptAuditDetail(width);
|
|
857
1057
|
const selected = this.snapshot.items[this.selected];
|
|
858
1058
|
let transcriptWarning: string | undefined;
|
|
859
1059
|
if (selected) {
|
|
@@ -871,7 +1071,8 @@ export class SubagentFleetComponent implements Component {
|
|
|
871
1071
|
: latest?.kind === "tool"
|
|
872
1072
|
? `${latest.name} · ${latest.status}`
|
|
873
1073
|
: "activity";
|
|
874
|
-
|
|
1074
|
+
const promptSummary = selected.kind === "foreground-active" ? foregroundAuthoredPromptSummary(selected, this.state) : undefined;
|
|
1075
|
+
return { header: structuredHeader(selected, width, this.theme, conversationState, promptSummary), body: this.withActionLines(body) };
|
|
875
1076
|
}
|
|
876
1077
|
}
|
|
877
1078
|
}
|
|
@@ -880,7 +1081,7 @@ export class SubagentFleetComponent implements Component {
|
|
|
880
1081
|
if (transcriptWarning) raw.unshift(`Transcript preview warning: ${transcriptWarning}`, "");
|
|
881
1082
|
const lines: string[] = [];
|
|
882
1083
|
for (const line of raw) {
|
|
883
|
-
const styled = /^(Run|State|Mode|Source|Child|Agent|Model):/.test(line)
|
|
1084
|
+
const styled = /^(Run|State|Mode|Source|Child|Agent|Model|Task):/.test(line)
|
|
884
1085
|
? this.theme.bold(line)
|
|
885
1086
|
: /^(Transcript|Result transcript tail)/.test(line)
|
|
886
1087
|
? this.theme.fg("accent", line)
|
|
@@ -933,7 +1134,9 @@ export class SubagentFleetComponent implements Component {
|
|
|
933
1134
|
}
|
|
934
1135
|
lines.push(this.theme.fg("border", `├${"─".repeat(rosterWidth)}┴${"─".repeat(detailWidth)}┤`));
|
|
935
1136
|
const position = this.snapshot.items.length ? `${this.selected + 1}/${this.snapshot.items.length}` : "0/0";
|
|
936
|
-
const footer =
|
|
1137
|
+
const footer = this.promptAuditOpen
|
|
1138
|
+
? ` j/k child · 1/2/3 view · g redo with guidance · c copy · Esc close Prompt Audit · ${position}`
|
|
1139
|
+
: ` ${bindingLabel(this.keybindings, "selectUp")}/${bindingLabel(this.keybindings, "selectDown")} agent · p Prompt Audit · ${bindingLabel(this.keybindings, "inspect")} Herdr · ${bindingLabel(this.keybindings, "steer")} steer · ${bindingLabel(this.keybindings, "stop")} stop · ${bindingLabel(this.keybindings, "toggleTools")} tools · ${bindingLabel(this.keybindings, "refresh")} refresh · ${bindingLabel(this.keybindings, "close")} close · ${position}`;
|
|
937
1140
|
lines.push(this.theme.fg("border", "│") + fit(this.theme.fg("dim", footer), innerWidth) + this.theme.fg("border", "│"));
|
|
938
1141
|
lines.push(this.theme.fg("border", `╰${"─".repeat(innerWidth)}╯`));
|
|
939
1142
|
return lines.map((line) => truncateToWidth(line, width));
|
|
@@ -954,6 +1157,12 @@ export async function openSubagentFleet(ctx: ExtensionContext, state: SubagentSt
|
|
|
954
1157
|
const wasOpen = state.fleetInspectorOpen === true;
|
|
955
1158
|
state.fleetInspectorOpen = true;
|
|
956
1159
|
if (typeof ctx.ui.setWidget === "function") ctx.ui.setWidget(FLEET_STATUS_WIDGET_KEY, undefined);
|
|
1160
|
+
const copyText = options.copyText ?? (async (text: string) => {
|
|
1161
|
+
const module = await import("@earendil-works/pi-coding-agent");
|
|
1162
|
+
const copyToClipboard = (module as { copyToClipboard?: (value: string) => Promise<void> | void }).copyToClipboard;
|
|
1163
|
+
if (!copyToClipboard) throw new Error("Clipboard is unavailable in this Pi version.");
|
|
1164
|
+
await copyToClipboard(text);
|
|
1165
|
+
});
|
|
957
1166
|
const actions = options.actions ?? {
|
|
958
1167
|
steer: async (input: { runId: string; asyncDir: string; index?: number; message: string; mode: SteerDeliveryMode }) => firstToolResultText(await steerAsyncRun({
|
|
959
1168
|
state,
|
|
@@ -970,14 +1179,20 @@ export async function openSubagentFleet(ctx: ExtensionContext, state: SubagentSt
|
|
|
970
1179
|
...(input.index !== undefined ? { index: input.index } : {}),
|
|
971
1180
|
}, {
|
|
972
1181
|
state,
|
|
1182
|
+
sessionRoots: state.trustedSessionRoots,
|
|
973
1183
|
cwd: state.baseCwd,
|
|
974
1184
|
...(state.authorityPolicy ? { authorityPolicy: state.authorityPolicy } : {}),
|
|
975
1185
|
...(state.missionStoreConfig ? { missions: state.missionStoreConfig } : {}),
|
|
976
1186
|
}), `Failed to open Herdr inspector for async run ${input.runId}.`),
|
|
1187
|
+
redoPrompt: async (input: { runId: string; index: number; guidance: string; control?: ForegroundRunControl }) => {
|
|
1188
|
+
const control = input.control ?? state.foregroundControls.get(input.runId);
|
|
1189
|
+
if (!control?.promptAuditRedo) return { text: "Redo is not available for this live child.", isError: true };
|
|
1190
|
+
return control.promptAuditRedo(input.index, input.guidance);
|
|
1191
|
+
},
|
|
977
1192
|
} satisfies FleetActionHandlers;
|
|
978
1193
|
try {
|
|
979
1194
|
await ctx.ui.custom<undefined>(
|
|
980
|
-
(tui, theme, _keybindings, done) => new SubagentFleetComponent(tui, theme, state, done, { ...options, actions }),
|
|
1195
|
+
(tui, theme, _keybindings, done) => new SubagentFleetComponent(tui, theme, state, done, { ...options, actions, copyText }),
|
|
981
1196
|
{
|
|
982
1197
|
overlay: true,
|
|
983
1198
|
overlayOptions: { anchor: "center", width: "95%", minWidth: 60, maxHeight: "85%", margin: 1 },
|