pi-subagents 0.59.0 → 0.61.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 +69 -0
- package/docs/agents.md +2 -2
- package/docs/configuration.md +9 -5
- package/docs/extension-api.md +14 -7
- package/docs/models.md +1 -1
- package/docs/observability.md +1 -1
- package/docs/tool-reference.md +15 -3
- package/docs/workflows.md +15 -14
- package/install.mjs +2 -1
- package/package.json +1 -1
- package/skills/council-mode/SKILL.md +48 -243
- package/skills/council-mode/references/pass-contracts.md +150 -0
- package/skills/pi-subagents/SKILL.md +89 -37
- package/skills/pi-subagents/references/constraints-and-recipes.md +30 -234
- package/skills/pi-subagents/references/execution-controls.md +79 -7
- package/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
- package/skills/pi-subagents/references/multi-lane-orchestration.md +13 -1
- package/skills/pi-subagents/references/prompting-and-roles.md +35 -28
- package/skills/pi-subagents/references/review-and-validation.md +73 -0
- package/src/agents/agent-management.ts +256 -88
- package/src/agents/agents.ts +527 -221
- package/src/api/background-work.ts +7 -2
- package/src/api/external-runs.ts +67 -4
- package/src/api/preflight.ts +13 -8
- package/src/api/shared-types.ts +3 -0
- package/src/extension/index.ts +7 -4
- package/src/extension/public-execution.ts +48 -4
- package/src/extension/rpc.ts +62 -4
- package/src/extension/schemas.ts +12 -7
- package/src/extension/tool-description.ts +16 -16
- package/src/runs/background/async-execution.ts +53 -37
- package/src/runs/background/async-job-tracker.ts +65 -3
- package/src/runs/background/async-resume.ts +3 -1
- package/src/runs/background/async-status.ts +104 -11
- package/src/runs/background/auto-drain.ts +1 -1
- package/src/runs/background/control-channel.ts +3 -2
- package/src/runs/background/fleet-view.ts +1 -1
- package/src/runs/background/result-watcher.ts +1 -1
- package/src/runs/background/resume-guidance.ts +1 -1
- package/src/runs/background/run-status.ts +15 -4
- package/src/runs/background/subagent-runner.ts +6 -3
- package/src/runs/background/subagent-wait.ts +30 -23
- package/src/runs/background/wait-completions.ts +4 -1
- package/src/runs/background/wait-tool.ts +24 -18
- package/src/runs/foreground/execution.ts +72 -4
- package/src/runs/foreground/subagent-executor.ts +276 -59
- package/src/runs/shared/acceptance.ts +43 -18
- package/src/runs/shared/async-status-projection.ts +138 -4
- package/src/runs/shared/background-process-options.ts +9 -0
- package/src/runs/shared/host-step-status.ts +1 -0
- package/src/runs/shared/mcp-direct-tool-grant.ts +2 -5
- package/src/runs/shared/model-fallback.ts +61 -17
- package/src/runs/shared/mutation-evidence.ts +52 -3
- package/src/runs/shared/permissions.ts +1 -1
- package/src/runs/shared/pi-args.ts +47 -1
- package/src/runs/shared/single-output.ts +45 -18
- package/src/runs/shared/subagent-prompt-runtime.ts +20 -2
- package/src/runs/shared/tool-timeout.ts +1 -1
- package/src/runs/shared/workflow-graph.ts +16 -0
- package/src/shared/types.ts +74 -4
- package/src/shared/workflow-child-permit.ts +91 -0
- package/src/slash/prompt-template-bridge.ts +37 -1
- package/src/slash/slash-commands.ts +18 -26
- package/src/tui/fleet-status.ts +11 -3
- package/src/tui/render-helpers.ts +31 -0
- package/src/tui/render.ts +673 -145
- package/src/watchdog/change-signature.ts +40 -1
- package/src/workflows/host-command.ts +6 -1
- package/src/workflows/scripted-workflow.ts +206 -6
- package/src/workflows/workflow-child-summary.ts +1 -1
- package/src/workflows/workflow-receipt.ts +41 -4
- package/src/workflows/workflow-resources.ts +150 -0
|
@@ -3,7 +3,7 @@ import * as fs from "node:fs";
|
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { keyText, type ExtensionAPI, type ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
5
5
|
import { Key, matchesKey, truncateToWidth, type Component, type KeyId, type TUI } from "@earendil-works/pi-tui";
|
|
6
|
-
import {
|
|
6
|
+
import { discoverAgentSnapshot, discoverAgents, findBlockingAgentDiagnostic, formatUnknownAgentError, resolveAgentName, unknownAgentDiagnosticContext, type AgentConfig, type AgentDiscoveryDiagnostic, type AgentScope, type UnknownAgentDiagnosticContext } from "../agents/agents.ts";
|
|
7
7
|
import { listRuntimeAgentConfigs, mergeRuntimeAgents } from "../agents/runtime-agent-registry.ts";
|
|
8
8
|
import { resolveExistingReadPaths } from "../shared/settings.ts";
|
|
9
9
|
import {
|
|
@@ -113,9 +113,13 @@ const extractExecutionFlags = (rawArgs: string): { args: string; bg: boolean; fo
|
|
|
113
113
|
};
|
|
114
114
|
|
|
115
115
|
function discoverSlashAgents(pi: ExtensionAPI, cwd: string, scope: AgentScope): { agents: AgentConfig[]; agentDiagnostics?: AgentDiscoveryDiagnostic[]; unknownAgentDiagnosticContext: UnknownAgentDiagnosticContext } {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
116
|
+
if (listRuntimeAgentConfigs(pi).length === 0) {
|
|
117
|
+
const discovered = discoverAgents(cwd, scope);
|
|
118
|
+
return { ...discovered, unknownAgentDiagnosticContext: unknownAgentDiagnosticContext(discovered) };
|
|
119
|
+
}
|
|
120
|
+
const snapshot = discoverAgentSnapshot(cwd, scope, undefined, { includeChains: false });
|
|
121
|
+
const discovered = snapshot.effective;
|
|
122
|
+
const all = snapshot.all;
|
|
119
123
|
const configuredAgents: AgentConfig[] = [
|
|
120
124
|
...all.builtin,
|
|
121
125
|
...all.package,
|
|
@@ -133,13 +137,6 @@ const makeAgentCompletions = (pi: ExtensionAPI, state: SubagentState) => (prefix
|
|
|
133
137
|
.map((agent) => ({ value: agent.name, label: agent.name }));
|
|
134
138
|
};
|
|
135
139
|
|
|
136
|
-
const makeBuiltinAgentNameCompletions = () => (prefix: string) => {
|
|
137
|
-
if (prefix.includes(" ")) return null;
|
|
138
|
-
return BUILTIN_AGENT_NAMES
|
|
139
|
-
.filter((name) => name.startsWith(prefix))
|
|
140
|
-
.map((name) => ({ value: name, label: name }));
|
|
141
|
-
};
|
|
142
|
-
|
|
143
140
|
const makeProviderCompletions = (state: SubagentState) => (prefix: string) => {
|
|
144
141
|
if (prefix.includes(" ")) return null;
|
|
145
142
|
const available = state.lastUiContext?.modelRegistry?.getAvailable?.();
|
|
@@ -420,7 +417,7 @@ function detailsFromSessionEntry(entry: unknown): Details | undefined {
|
|
|
420
417
|
}
|
|
421
418
|
if (record.type !== "message" || !record.message || typeof record.message !== "object") return undefined;
|
|
422
419
|
const message = record.message as { role?: unknown; toolName?: unknown; details?: unknown };
|
|
423
|
-
if (message.role !== "toolResult" || (message.toolName !== "subagent" && message.toolName !== "subagent_wait")) return undefined;
|
|
420
|
+
if (message.role !== "toolResult" || (message.toolName !== "subagent" && message.toolName !== "bg_wait" && message.toolName !== "subagent_wait")) return undefined;
|
|
424
421
|
return isSubagentDetails(message.details) ? message.details : undefined;
|
|
425
422
|
}
|
|
426
423
|
|
|
@@ -1005,7 +1002,7 @@ export function registerSlashCommands(
|
|
|
1005
1002
|
ctx.ui.notify(`Foreground run ${control.runId} is not currently detachable.`, "info");
|
|
1006
1003
|
return;
|
|
1007
1004
|
}
|
|
1008
|
-
sendSlashText(pi, `Detached foreground run ${control.runId} without terminating its child. Use subagent({ action: "status", id: ${JSON.stringify(control.runId)} }) or
|
|
1005
|
+
sendSlashText(pi, `Detached foreground run ${control.runId} without terminating its child. Use subagent({ action: "status", id: ${JSON.stringify(control.runId)} }) or bg_wait({ id: ${JSON.stringify(control.runId)} }) to recover the eventual result. This does not daemonize the process or guarantee survival across Pi reload/restart.`);
|
|
1009
1006
|
};
|
|
1010
1007
|
|
|
1011
1008
|
pi.registerCommand("subagents-detach", {
|
|
@@ -1140,25 +1137,20 @@ export function registerSlashCommands(
|
|
|
1140
1137
|
});
|
|
1141
1138
|
|
|
1142
1139
|
pi.registerCommand("subagents-models", {
|
|
1143
|
-
description: "Show
|
|
1144
|
-
getArgumentCompletions:
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1140
|
+
description: "Show model mappings for discovered subagents",
|
|
1141
|
+
getArgumentCompletions: makeAgentCompletions(pi, state),
|
|
1142
|
+
handler: async (args, ctx) => {
|
|
1143
|
+
const trimmed = args.trim();
|
|
1144
|
+
if (!trimmed) {
|
|
1145
|
+
await runCommand(ctx, { action: "models" });
|
|
1149
1146
|
return;
|
|
1150
1147
|
}
|
|
1151
1148
|
const parts = trimmed.split(/\s+/).filter(Boolean);
|
|
1152
1149
|
if (parts.length !== 1) {
|
|
1153
|
-
ctx.ui.notify("Usage: /subagents-models [
|
|
1154
|
-
return;
|
|
1155
|
-
}
|
|
1156
|
-
const agent = parts[0]!;
|
|
1157
|
-
if (!(BUILTIN_AGENT_NAMES as readonly string[]).includes(agent)) {
|
|
1158
|
-
ctx.ui.notify(`Unknown builtin agent: ${agent}`, "error");
|
|
1150
|
+
ctx.ui.notify("Usage: /subagents-models [agent-name]", "error");
|
|
1159
1151
|
return;
|
|
1160
1152
|
}
|
|
1161
|
-
await runCommand(ctx, { action: "models", agent });
|
|
1153
|
+
await runCommand(ctx, { action: "models", agent: parts[0]! });
|
|
1162
1154
|
},
|
|
1163
1155
|
});
|
|
1164
1156
|
|
package/src/tui/fleet-status.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { snapshotExternalRuns } from "../api/external-runs.ts";
|
|
|
4
4
|
import { formatModelThinking } from "../shared/formatters.ts";
|
|
5
5
|
import type { AsyncJobState, AsyncJobStep, FleetViewPlacement, HerdrProjectPaneSnapshot, HostStepState, HostStepVerdict, NestedRunSummary, NestedStepSummary, SubagentState } from "../shared/types.ts";
|
|
6
6
|
import { projectAsyncWorkflowRows, type AsyncStatusWorkflowRow } from "../runs/shared/async-status-projection.ts";
|
|
7
|
+
import { contextModeLabel } from "../runs/shared/context-mode.ts";
|
|
7
8
|
import { formatWorkflowJsonPreview } from "../workflows/scripted-workflow.ts";
|
|
8
9
|
import { hostStepReportName, hostStepVerdictLabel } from "../runs/shared/host-step-status.ts";
|
|
9
10
|
import { isStaleExtensionContextError } from "../shared/extension-context.ts";
|
|
@@ -362,7 +363,7 @@ export function collectFleetStatusEntries(state: SubagentState): FleetStatusEntr
|
|
|
362
363
|
if (job.mode === "workflow") {
|
|
363
364
|
const latestEmit = job.workflow?.emits?.length ? formatWorkflowJsonPreview(job.workflow.emits.at(-1), 120) : undefined;
|
|
364
365
|
const workflowSteps = workflowStepsWithoutMaterializedChildren(job.steps, materializedChildrenByWorkflow.get(`async:${job.asyncId}`));
|
|
365
|
-
const workflowRows = projectAsyncWorkflowRows(workflowSteps, job.hostSteps, job.preflight);
|
|
366
|
+
const workflowRows = projectAsyncWorkflowRows(workflowSteps, job.workflowGraph ?? job.hostSteps, job.preflight);
|
|
366
367
|
entries.push({
|
|
367
368
|
key: `async:${job.asyncId}`,
|
|
368
369
|
...(linkedParentKey ? { parentKey: linkedParentKey } : {}),
|
|
@@ -550,7 +551,12 @@ export class SubagentFleetStatus {
|
|
|
550
551
|
this.lastRenderKey = renderKey;
|
|
551
552
|
return;
|
|
552
553
|
}
|
|
553
|
-
if (renderKey === this.lastRenderKey)
|
|
554
|
+
if (renderKey === this.lastRenderKey) {
|
|
555
|
+
// Repaint anyway while anything is running so the wall-clock
|
|
556
|
+
// spinner animates between state changes (500ms tick).
|
|
557
|
+
if (this.entries.some((entry) => entry.state === "running")) this.tui?.requestRender();
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
554
560
|
this.lastRenderKey = renderKey;
|
|
555
561
|
this.tui?.requestRender();
|
|
556
562
|
}
|
|
@@ -728,6 +734,7 @@ export class SubagentFleetStatus {
|
|
|
728
734
|
const marker = last ? "└─" : "├─";
|
|
729
735
|
const indent = " ";
|
|
730
736
|
if (row.overflow !== undefined) return truncateToWidth(`${indent}${marker} ${theme.fg("dim", `+${row.overflow} hidden workflow steps`)}`, width);
|
|
737
|
+
const context = contextModeLabel(row.context);
|
|
731
738
|
const modelThinking = row.modelThinking ? ` (${row.modelThinking})` : "";
|
|
732
739
|
const activity = row.activity ? ` · ${row.activity}` : "";
|
|
733
740
|
const kind = row.kind ? `${row.kind}: ` : "";
|
|
@@ -738,7 +745,7 @@ export class SubagentFleetStatus {
|
|
|
738
745
|
row.preflight.expectedOutput ? `expected:${row.preflight.expectedOutput}` : undefined,
|
|
739
746
|
row.preflight.independence ? `independence:${row.preflight.independence}` : undefined,
|
|
740
747
|
].filter((value): value is string => Boolean(value)).join(" · ") : "";
|
|
741
|
-
const left = `${indent}${marker} ${this.workflowRowGlyph(row, theme)} ${theme.fg("muted", `${kind}${row.name}${modelThinking}`)} · ${this.workflowRowStateLabel(row, theme)}${activity}${hints ? ` · ${hints}` : ""}`;
|
|
748
|
+
const left = `${indent}${marker} ${this.workflowRowGlyph(row, theme)} ${theme.fg("muted", `${kind}${row.name}${context ? ` ${context}` : ""}${modelThinking}`)} · ${this.workflowRowStateLabel(row, theme)}${activity}${hints ? ` · ${hints}` : ""}`;
|
|
742
749
|
const details = [
|
|
743
750
|
row.startedAt !== undefined ? formatFleetElapsed(Date.now() - row.startedAt) : undefined,
|
|
744
751
|
row.tokens !== undefined ? formatFleetTokens(row.tokens, row.window) : undefined,
|
|
@@ -806,6 +813,7 @@ export class SubagentFleetStatus {
|
|
|
806
813
|
row.kind,
|
|
807
814
|
row.name,
|
|
808
815
|
row.state,
|
|
816
|
+
row.context,
|
|
809
817
|
row.modelThinking,
|
|
810
818
|
row.activity,
|
|
811
819
|
row.startedAt,
|
|
@@ -30,6 +30,37 @@ export function fuzzyFilter<T extends { name: string; description: string; model
|
|
|
30
30
|
.map((x) => x.item);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
/** Remove a repeated job name from a child label when a safe separator follows it. */
|
|
34
|
+
export function stripRepeatedAgentPrefix(label: string, jobName: string | undefined): string {
|
|
35
|
+
const value = label.trim();
|
|
36
|
+
const prefix = jobName?.trim();
|
|
37
|
+
if (!prefix || !value.startsWith(prefix)) return value;
|
|
38
|
+
|
|
39
|
+
const suffix = value.slice(prefix.length);
|
|
40
|
+
if (!suffix || !/^(?::|·|\s)/u.test(suffix)) return value;
|
|
41
|
+
return suffix.replace(/^\s*(?::|·)?\s*/u, "").trim();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Whether a status label may omit the one logical step marker. */
|
|
45
|
+
export function shouldSuppressSingleStep(chainStepCount?: number, stepsTotal?: number): boolean {
|
|
46
|
+
return (chainStepCount ?? stepsTotal) === 1;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Add an Agent fraction only when visible candidates collide. */
|
|
50
|
+
export function withDuplicateLabelDiscriminators<T extends { index: number; displayName: string }>(
|
|
51
|
+
rows: readonly T[],
|
|
52
|
+
total: number,
|
|
53
|
+
): Array<T & { rowLabel: string }> {
|
|
54
|
+
const counts = new Map<string, number>();
|
|
55
|
+
for (const row of rows) counts.set(row.displayName, (counts.get(row.displayName) ?? 0) + 1);
|
|
56
|
+
return rows.map((row) => ({
|
|
57
|
+
...row,
|
|
58
|
+
rowLabel: (counts.get(row.displayName) ?? 0) > 1
|
|
59
|
+
? `Agent ${row.index + 1}/${total}: ${row.displayName}`
|
|
60
|
+
: row.displayName,
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
|
|
33
64
|
export function pad(s: string, len: number): string {
|
|
34
65
|
const vis = visibleWidth(s);
|
|
35
66
|
return s + " ".repeat(Math.max(0, len - vis));
|