pi-subagents 0.53.0 → 0.55.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 +66 -5
- package/README.md +1 -1
- package/agents/reviewer.md +12 -2
- package/docs/agents.md +4 -4
- package/docs/configuration.md +6 -6
- package/docs/extension-api.md +14 -4
- package/docs/models.md +41 -6
- package/docs/observability.md +14 -3
- package/docs/tool-reference.md +21 -6
- package/docs/workflows.md +7 -1
- package/index.ts +10 -1
- package/package.json +1 -1
- package/prompts/council.md +19 -7
- package/prompts/parallel-review.md +5 -1
- package/prompts/review-loop.md +9 -5
- package/skills/council-mode/SKILL.md +50 -26
- package/skills/pi-subagents/SKILL.md +5 -1
- package/skills/pi-subagents/references/constraints-and-recipes.md +16 -1
- package/skills/pi-subagents/references/execution-controls.md +13 -4
- package/skills/pi-subagents/references/multi-lane-orchestration.md +3 -3
- package/skills/pi-subagents/references/prompting-and-roles.md +14 -7
- package/src/agents/agent-management.ts +115 -14
- package/src/agents/agent-serializer.ts +2 -2
- package/src/agents/agents.ts +195 -47
- package/src/api/external-job-provider.ts +10 -1
- package/src/api/preflight.ts +33 -19
- package/src/api/project-panes.ts +2 -0
- package/src/extension/doctor.ts +10 -0
- package/src/extension/fanout-child.ts +3 -2
- package/src/extension/index.ts +24 -4
- package/src/extension/public-execution.ts +12 -9
- package/src/extension/rpc.ts +77 -3
- package/src/extension/schemas.ts +2 -1
- package/src/extension/tool-description.ts +9 -6
- package/src/extension/tool-result.ts +19 -0
- package/src/inspectors/herdr/client.ts +3 -3
- package/src/inspectors/herdr/focus.ts +55 -0
- package/src/inspectors/herdr/project-panes.ts +228 -44
- package/src/integrations/herdr-status.ts +26 -4
- package/src/runs/background/async-execution.ts +112 -13
- package/src/runs/background/async-job-tracker.ts +33 -14
- package/src/runs/background/async-resume.ts +20 -2
- package/src/runs/background/async-retention.ts +1 -1
- package/src/runs/background/async-status.ts +10 -0
- package/src/runs/background/chain-root-attachment.ts +5 -0
- package/src/runs/background/control-channel.ts +98 -10
- package/src/runs/background/notify.ts +62 -1
- package/src/runs/background/result-watcher.ts +4 -3
- package/src/runs/background/run-status.ts +15 -1
- package/src/runs/background/stale-run-reconciler.ts +3 -0
- package/src/runs/background/subagent-runner.ts +251 -38
- package/src/runs/background/wait-completions.ts +2 -0
- package/src/runs/background/wait-tool.ts +4 -3
- package/src/runs/foreground/async-stop-action.ts +23 -2
- package/src/runs/foreground/execution.ts +110 -8
- package/src/runs/foreground/subagent-executor.ts +361 -45
- package/src/runs/foreground/workflow-detach-reconcile.ts +3 -0
- package/src/runs/shared/acceptance.ts +1 -0
- package/src/runs/shared/child-identity.ts +36 -0
- package/src/runs/shared/completion-guard.ts +50 -1
- package/src/runs/shared/external-job-bridge.ts +53 -37
- package/src/runs/shared/external-job-runner.ts +126 -23
- package/src/runs/shared/model-fallback.ts +46 -15
- package/src/runs/shared/model-scope.ts +106 -39
- package/src/runs/shared/orca-progress-tabs.ts +71 -9
- package/src/runs/shared/parallel-utils.ts +12 -0
- package/src/runs/shared/pi-args.ts +31 -1
- package/src/runs/shared/subagent-prompt-runtime.ts +39 -10
- package/src/runs/shared/tool-availability.ts +1 -3
- package/src/shared/launch-contract.ts +6 -5
- package/src/shared/thinking-ceiling.ts +52 -0
- package/src/shared/types.ts +72 -5
- package/src/tui/fleet-status.ts +66 -13
- package/src/tui/render.ts +5 -4
- package/src/watchdog/permission-arbiter.ts +59 -51
- package/src/workflows/scripted-workflow.ts +107 -10
|
@@ -37,6 +37,7 @@ import type { AcceptanceInput, Details, ExtensionConfig, ToolBudgetConfig } from
|
|
|
37
37
|
import { getProjectConfigDir } from "../shared/utils.ts";
|
|
38
38
|
import { capabilityCeilingAgentRestrictionSources, isAgentAllowedByCapabilityCeiling, resolveCurrentSubagentCapabilityCeiling } from "../runs/shared/capability-ceiling.ts";
|
|
39
39
|
import { listRuntimeAgentConfigs, mergeRuntimeAgents, type RuntimeAgentOwner } from "./runtime-agent-registry.ts";
|
|
40
|
+
import { listExternalJobProviders } from "../api/external-job-provider.ts";
|
|
40
41
|
|
|
41
42
|
type ManagementAction = "list" | "get" | "models" | "create" | "update" | "delete" | "eject" | "disable" | "enable" | "reset";
|
|
42
43
|
type ManagementScope = "user" | "project";
|
|
@@ -57,18 +58,23 @@ function parseCsv(value: string): string[] {
|
|
|
57
58
|
return [...new Set(value.split(",").map((v) => v.trim()).filter(Boolean))];
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
|
|
61
|
+
type ConfigObjectResult =
|
|
62
|
+
| { status: "ok"; value: Record<string, unknown> }
|
|
63
|
+
| { status: "missing" }
|
|
64
|
+
| { status: "error"; message: string };
|
|
65
|
+
|
|
66
|
+
function configObject(config: unknown): ConfigObjectResult {
|
|
61
67
|
let val = config;
|
|
62
68
|
if (typeof val === "string") {
|
|
63
69
|
try {
|
|
64
70
|
val = JSON.parse(val);
|
|
65
71
|
} catch (error) {
|
|
66
72
|
const message = error instanceof Error ? error.message : String(error);
|
|
67
|
-
return { error: `config must be valid JSON: ${message}` };
|
|
73
|
+
return { status: "error", message: `config must be valid JSON: ${message}` };
|
|
68
74
|
}
|
|
69
75
|
}
|
|
70
|
-
if (!val || typeof val !== "object" || Array.isArray(val)) return {};
|
|
71
|
-
return { value: val as Record<string, unknown> };
|
|
76
|
+
if (!val || typeof val !== "object" || Array.isArray(val)) return { status: "missing" };
|
|
77
|
+
return { status: "ok", value: val as Record<string, unknown> };
|
|
72
78
|
}
|
|
73
79
|
|
|
74
80
|
function hasKey(obj: Record<string, unknown>, key: string): boolean {
|
|
@@ -176,7 +182,8 @@ function isMutableSource(source: AgentSource): source is ManagementScope {
|
|
|
176
182
|
function modelWarning(ctx: ManagementContext, model: string | undefined): string | undefined {
|
|
177
183
|
if (!model) return undefined;
|
|
178
184
|
const found = ctx.modelRegistry.getAvailable().some((m) => `${m.provider}/${m.id}` === model || m.id === model);
|
|
179
|
-
|
|
185
|
+
if (found) return undefined;
|
|
186
|
+
return `Warning: model '${model}' is not in the current model registry. Run subagent({ action: "models" }) to list valid provider/id selectors, then use the exact provider/id form (bare ids resolve only when unique).`;
|
|
180
187
|
}
|
|
181
188
|
|
|
182
189
|
function fallbackModelsWarning(ctx: ManagementContext, fallbackModels: string[] | undefined): string | undefined {
|
|
@@ -214,7 +221,9 @@ export function editableAgentConfig(agent: AgentConfig): AgentConfig {
|
|
|
214
221
|
const base = agent.override?.base;
|
|
215
222
|
const {
|
|
216
223
|
override: _override,
|
|
224
|
+
output: _output,
|
|
217
225
|
outputMode: _outputMode,
|
|
226
|
+
defaultReads: _defaultReads,
|
|
218
227
|
model: _model,
|
|
219
228
|
fallbackModels: _fallbackModels,
|
|
220
229
|
thinking: _thinking,
|
|
@@ -242,7 +251,9 @@ export function editableAgentConfig(agent: AgentConfig): AgentConfig {
|
|
|
242
251
|
|
|
243
252
|
return withDeclaredExtensionPaths({
|
|
244
253
|
...editable,
|
|
254
|
+
...(base.output !== undefined ? { output: base.output } : {}),
|
|
245
255
|
...(base.outputMode !== undefined ? { outputMode: base.outputMode } : {}),
|
|
256
|
+
...(base.defaultReads !== undefined ? { defaultReads: [...base.defaultReads] } : {}),
|
|
246
257
|
...(base.model !== undefined ? { model: base.model } : {}),
|
|
247
258
|
...(base.fallbackModels !== undefined ? { fallbackModels: [...base.fallbackModels] } : {}),
|
|
248
259
|
...(base.thinking !== undefined ? { thinking: base.thinking } : {}),
|
|
@@ -590,9 +601,83 @@ function renamePath(currentPath: string, newName: string, scope: ManagementScope
|
|
|
590
601
|
return { filePath };
|
|
591
602
|
}
|
|
592
603
|
|
|
604
|
+
function packageSourceLabel(agent: AgentConfig): string {
|
|
605
|
+
if (!agent.packageSourceName) return agent.source;
|
|
606
|
+
return agent.packageSourceVersion ? `${agent.packageSourceName}@${agent.packageSourceVersion}` : agent.packageSourceName;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
type ExternalJobProviderStatus =
|
|
610
|
+
| { ok: true; names: Set<string> }
|
|
611
|
+
| { ok: false; error: string };
|
|
612
|
+
|
|
613
|
+
function errorMessage(error: unknown): string {
|
|
614
|
+
return error instanceof Error ? error.message : String(error);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
function registeredExternalJobProviderStatus(): ExternalJobProviderStatus {
|
|
618
|
+
try {
|
|
619
|
+
return { ok: true, names: new Set(listExternalJobProviders().map((provider) => provider.name)) };
|
|
620
|
+
} catch (error) {
|
|
621
|
+
return { ok: false, error: errorMessage(error) };
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
function externalJobProviderSuffix(provider: string, names: Set<string> | undefined): string {
|
|
626
|
+
if (!names) return "?";
|
|
627
|
+
return names.has(provider) ? "✓" : "missing";
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
function runnerListBadge(agent: AgentConfig, providerNames: Set<string> | undefined): string | undefined {
|
|
631
|
+
if (agent.runner?.type === "external-job") return `external-job:${agent.runner.provider} ${externalJobProviderSuffix(agent.runner.provider, providerNames)}`;
|
|
632
|
+
if (agent.runner?.type === "external-cli") return "external-cli";
|
|
633
|
+
return undefined;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
function formatAgentListLine(agent: AgentConfig, providerNames: Set<string> | undefined): string {
|
|
637
|
+
const source = agent.source === "package" ? packageSourceLabel(agent) : agent.source;
|
|
638
|
+
const parts = [
|
|
639
|
+
source,
|
|
640
|
+
runnerListBadge(agent, providerNames),
|
|
641
|
+
agent.defaultContext ? `context: ${agent.defaultContext}` : undefined,
|
|
642
|
+
agent.aliases?.length ? `aliases: ${agent.aliases.join(", ")}` : undefined,
|
|
643
|
+
].filter((part): part is string => Boolean(part));
|
|
644
|
+
return `- ${agent.name} (${parts.join(", ")}): ${agent.description}`;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
function formatAgentListSections(agents: AgentConfig[], providerNames: Set<string> | undefined): string[] {
|
|
648
|
+
if (agents.length === 0) return ["- (none)"];
|
|
649
|
+
const sections: Array<[AgentSource, string]> = [
|
|
650
|
+
["package", "Package agents"],
|
|
651
|
+
["user", "User agents"],
|
|
652
|
+
["project", "Project agents"],
|
|
653
|
+
["runtime", "Runtime agents"],
|
|
654
|
+
["builtin", "Builtin agents"],
|
|
655
|
+
];
|
|
656
|
+
const lines: string[] = [];
|
|
657
|
+
for (const [source, label] of sections) {
|
|
658
|
+
const matches = agents.filter((agent) => agent.source === source);
|
|
659
|
+
if (matches.length === 0) continue;
|
|
660
|
+
if (lines.length > 0) lines.push("");
|
|
661
|
+
lines.push(label, ...matches.map((agent) => formatAgentListLine(agent, providerNames)));
|
|
662
|
+
}
|
|
663
|
+
return lines;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
function formatRunnerDetail(agent: AgentConfig, providerNames: Set<string> | undefined): string | undefined {
|
|
667
|
+
if (!agent.runner) return undefined;
|
|
668
|
+
if (agent.runner.type === "external-job") return `Runner: external-job via ${agent.runner.provider} ${externalJobProviderSuffix(agent.runner.provider, providerNames)}`;
|
|
669
|
+
if (agent.runner.type === "external-cli") return `Runner: external-cli ${agent.runner.command}`;
|
|
670
|
+
return `Runner: ${JSON.stringify(agent.runner)}`;
|
|
671
|
+
}
|
|
672
|
+
|
|
593
673
|
function formatAgentDetail(agent: AgentConfig): string {
|
|
594
674
|
const tools = [...(agent.tools ?? []), ...(agent.mcpDirectTools ?? []).map((t) => `mcp:${t}`)];
|
|
675
|
+
const providerStatus = registeredExternalJobProviderStatus();
|
|
595
676
|
const lines: string[] = [`Agent: ${agent.name} (${agent.source})`, `Path: ${agent.filePath}`, `Description: ${agent.description}`];
|
|
677
|
+
if (agent.source === "package" && agent.packageSourceName) {
|
|
678
|
+
lines.push(`Source package: ${packageSourceLabel(agent)}`);
|
|
679
|
+
if (agent.packageSourceRoot) lines.push(`Package root: ${agent.packageSourceRoot}`);
|
|
680
|
+
}
|
|
596
681
|
if (agent.packageName) {
|
|
597
682
|
lines.push(`Local name: ${frontmatterNameForConfig(agent)}`);
|
|
598
683
|
lines.push(`Package: ${agent.packageName}`);
|
|
@@ -604,7 +689,12 @@ function formatAgentDetail(agent: AgentConfig): string {
|
|
|
604
689
|
if (agent.skills?.length) lines.push(`Skills: ${agent.skills.join(", ")}`);
|
|
605
690
|
if (agent.skillPath?.length) lines.push(`Skill paths: ${agent.skillPath.join(", ")}`);
|
|
606
691
|
lines.push(`System prompt mode: ${agent.systemPromptMode}`);
|
|
607
|
-
|
|
692
|
+
const runnerDetail = formatRunnerDetail(agent, providerStatus.ok ? providerStatus.names : undefined);
|
|
693
|
+
if (runnerDetail) {
|
|
694
|
+
lines.push(runnerDetail);
|
|
695
|
+
if (agent.runner?.type === "external-job" && !providerStatus.ok) lines.push(`External-job provider registry unavailable: ${providerStatus.error}`);
|
|
696
|
+
if (agent.runner?.type === "external-job" && agent.runner.options) lines.push(`Runner options: ${JSON.stringify(agent.runner.options)}`);
|
|
697
|
+
}
|
|
608
698
|
lines.push(`Inherit project context: ${agent.inheritProjectContext ? "true" : "false"}`);
|
|
609
699
|
lines.push(`Inherit skills: ${agent.inheritSkills ? "true" : "false"}`);
|
|
610
700
|
if (agent.defaultContext) lines.push(`Default context: ${agent.defaultContext}`);
|
|
@@ -654,16 +744,16 @@ export function handleList(params: ManagementParams, ctx: ManagementContext): Ag
|
|
|
654
744
|
...(ctx.config?.proactiveSkillSubagents !== undefined ? { config: ctx.config.proactiveSkillSubagents } : {}),
|
|
655
745
|
discoverAvailableSkills: () => discoverAvailableSkills(ctx.cwd),
|
|
656
746
|
});
|
|
747
|
+
const providerStatus = registeredExternalJobProviderStatus();
|
|
657
748
|
const lines = [
|
|
658
749
|
"Executable agents:",
|
|
659
|
-
...(agents.
|
|
660
|
-
? agents.map((a) => `- ${a.name} (${a.source}${a.defaultContext ? `, context: ${a.defaultContext}` : ""}${a.aliases?.length ? `, aliases: ${a.aliases.join(", ")}` : ""}): ${a.description}`)
|
|
661
|
-
: ["- (none)"]),
|
|
750
|
+
...formatAgentListSections(agents, providerStatus.ok ? providerStatus.names : undefined),
|
|
662
751
|
...(restrictedAgents.length ? [
|
|
663
752
|
"",
|
|
664
753
|
`Restricted agents (not executable in this session${restrictedSources?.length ? `; capability ceiling: ${restrictedSources.join(", ")}` : ""}):`,
|
|
665
|
-
...restrictedAgents.map((a) =>
|
|
754
|
+
...restrictedAgents.map((a) => formatAgentListLine(a, providerStatus.ok ? providerStatus.names : undefined)),
|
|
666
755
|
] : []),
|
|
756
|
+
...(!providerStatus.ok && [...agents, ...restrictedAgents].some((agent) => agent.runner?.type === "external-job") ? ["", `External-job provider registry unavailable: ${providerStatus.error}`] : []),
|
|
667
757
|
...(d.agentDiagnostics?.length ? [
|
|
668
758
|
"",
|
|
669
759
|
"Invalid agent definitions:",
|
|
@@ -752,6 +842,17 @@ function handleModels(params: ManagementParams, ctx: ManagementContext): AgentTo
|
|
|
752
842
|
lines.push("");
|
|
753
843
|
}
|
|
754
844
|
|
|
845
|
+
const availableFullIds = availableModels.map((m) => m.fullId).sort();
|
|
846
|
+
if (availableFullIds.length > 0) {
|
|
847
|
+
lines.push("Available models in this session's registry (copy an exact provider/id when passing model):");
|
|
848
|
+
lines.push("");
|
|
849
|
+
const shown = availableFullIds.slice(0, 80);
|
|
850
|
+
for (const fullId of shown) lines.push(` ${fullId}`);
|
|
851
|
+
if (availableFullIds.length > shown.length) lines.push(` ... and ${availableFullIds.length - shown.length} more`);
|
|
852
|
+
lines.push("");
|
|
853
|
+
lines.push("Use an exact provider/id from this list when you pass model; bare ids resolve only when unique in the registry.");
|
|
854
|
+
}
|
|
855
|
+
|
|
755
856
|
return result(lines.join("\n"));
|
|
756
857
|
}
|
|
757
858
|
|
|
@@ -777,9 +878,9 @@ function handleGet(params: ManagementParams, ctx: ManagementContext): AgentToolR
|
|
|
777
878
|
|
|
778
879
|
export function handleCreate(params: ManagementParams, ctx: ManagementContext): AgentToolResult<Details> {
|
|
779
880
|
const parsedConfig = configObject(params.config);
|
|
780
|
-
if (parsedConfig.error) return result(parsedConfig.
|
|
881
|
+
if (parsedConfig.status === "error") return result(parsedConfig.message, true);
|
|
882
|
+
if (parsedConfig.status === "missing") return result("config required for create.", true);
|
|
781
883
|
const cfg = parsedConfig.value;
|
|
782
|
-
if (!cfg) return result("config required for create.", true);
|
|
783
884
|
if (typeof cfg.name !== "string" || !cfg.name.trim()) return result("config.name is required and must be a non-empty string.", true);
|
|
784
885
|
if (typeof cfg.description !== "string" || !cfg.description.trim()) return result("config.description is required and must be a non-empty string.", true);
|
|
785
886
|
const name = sanitizeName(cfg.name);
|
|
@@ -827,9 +928,9 @@ export function handleCreate(params: ManagementParams, ctx: ManagementContext):
|
|
|
827
928
|
export function handleUpdate(params: ManagementParams, ctx: ManagementContext): AgentToolResult<Details> {
|
|
828
929
|
if (!params.agent) return result("Specify 'agent' for update.", true);
|
|
829
930
|
const parsedConfig = configObject(params.config);
|
|
830
|
-
if (parsedConfig.error) return result(parsedConfig.
|
|
931
|
+
if (parsedConfig.status === "error") return result(parsedConfig.message, true);
|
|
932
|
+
if (parsedConfig.status === "missing") return result("config required for update.", true);
|
|
831
933
|
const cfg = parsedConfig.value;
|
|
832
|
-
if (!cfg) return result("config required for update.", true);
|
|
833
934
|
if (hasKey(cfg, "steps")) return result("Durable chain definitions were removed; use workflowScript or /prompt-workflow for repeatable workflows.", true);
|
|
834
935
|
const warnings: string[] = [];
|
|
835
936
|
const scopeHint = asDisambiguationScope(params.agentScope);
|
|
@@ -113,11 +113,11 @@ export function serializeAgent(config: AgentConfig, options: SerializeAgentOptio
|
|
|
113
113
|
lines.push(`subagentOnlyExtensions: ${subagentOnlyExtensionsValue ?? ""}`);
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
if (config.output) lines.push(`output: ${config.output}`);
|
|
116
|
+
if (config.output || preserve("output")) lines.push(`output: ${config.output ?? ""}`);
|
|
117
117
|
if (config.outputMode || preserve("outputMode")) lines.push(`outputMode: ${config.outputMode ?? ""}`);
|
|
118
118
|
|
|
119
119
|
const readsValue = joinComma(config.defaultReads);
|
|
120
|
-
if (readsValue) lines.push(`defaultReads: ${readsValue}`);
|
|
120
|
+
if (readsValue || preserve("defaultReads")) lines.push(`defaultReads: ${readsValue ?? ""}`);
|
|
121
121
|
|
|
122
122
|
if (config.defaultProgress) lines.push("defaultProgress: true");
|
|
123
123
|
if (config.interactive) lines.push("interactive: true");
|