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
|
@@ -12,32 +12,38 @@ export function registerWaitTool(
|
|
|
12
12
|
subscriptions?: Pick<WaitSubscriptionManager, "arm">,
|
|
13
13
|
defaultTimeoutMs?: number,
|
|
14
14
|
): void {
|
|
15
|
-
const
|
|
16
|
-
name: "subagent_wait",
|
|
17
|
-
label: "Subagent Wait",
|
|
18
|
-
description: `Block until background work owned by this session changes, then return.
|
|
15
|
+
const description = `Wait for background, provider, or detached work that has no native completion notification, then return.
|
|
19
16
|
|
|
20
|
-
|
|
17
|
+
Ordinary async subagent runs already notify this session natively when they complete or need attention. In an interactive chat, return control instead of calling this merely to wait. Use this tool for provider jobs, remembered detached foreground runs, or other background work without a native notification path. Headless runs auto-drain current-session subagent work at agent_end; use this tool only when the current turn must receive non-notifying background work results.
|
|
21
18
|
|
|
22
19
|
• { } — return when the first initially active async run or registered provider item finishes, or when a subagent needs attention.
|
|
23
20
|
• { all: true } — wait for every async run and provider item that was active when the call began.
|
|
24
21
|
• { id: "..." } — wait for one async or remembered detached foreground subagent run (id or prefix).
|
|
25
|
-
• { id: "...", nonBlocking: true } — resolve the prefix once, persist an exact-run wake subscription, and return immediately.
|
|
22
|
+
• { id: "...", nonBlocking: true } — resolve the prefix once, persist an exact-run wake subscription, and return immediately. Use this for detached work without native completion delivery; the originating interactive session wakes on completion, failure, attention, reconciliation failure, or timeout.
|
|
26
23
|
• { stopOnAttention: false } — for blocking waits only, keep waiting through idle or long-thinking attention; supervisor/contact requests still stop the wait.
|
|
27
24
|
• { timeoutMs: 600000 } — stop waiting after N ms; active work keeps running. Omitted values use waitTool.defaultTimeoutMs, then 30 minutes. Window expiry returns a non-error window_elapsed result with active work identities.
|
|
28
25
|
|
|
29
|
-
Non-blocking subscriptions are visible in subagent status and differ from disabling waitTool: waitTool.enabled=false returns immediately without registering any future wake. Provider jobs are session-scoped and identified exactly, so replacing one job with another cannot hide a completion. Provider extensions must be explicitly loaded in this process. In a child agent, keep \`subagent_wait\` in the child tool allowlist and load each provider through the agent's extensions or subagentOnlyExtensions; this tool never loads providers or grants tools itself.${enabled ? "" : "\n\nConfigured behavior: subagent_wait
|
|
26
|
+
Non-blocking subscriptions are visible in subagent status and differ from disabling waitTool: waitTool.enabled=false returns immediately without registering any future wake. Provider jobs are session-scoped and identified exactly, so replacing one job with another cannot hide a completion. Provider extensions must be explicitly loaded in this process. In a child agent, keep \`bg_wait\` (or the deprecated \`subagent_wait\` compatibility alias) in the child tool allowlist and load each provider through the agent's extensions or subagentOnlyExtensions; this tool never loads providers or grants tools itself.${enabled ? "" : "\n\nConfigured behavior: bg_wait and its subagent_wait compatibility alias are disabled by config.waitTool or PI_SUBAGENT_WAIT_TOOL_ENABLED and return immediately without blocking."}`;
|
|
27
|
+
const execute: ToolDefinition<typeof SubagentWaitParams, Details>["execute"] = async (_id, params, signal, onUpdate, ctx) => finalizeToolResult(await waitForSubagents(params, signal, {
|
|
28
|
+
state,
|
|
29
|
+
events: pi.events,
|
|
30
|
+
enabled,
|
|
31
|
+
...(defaultTimeoutMs !== undefined ? { defaultTimeoutMs } : {}),
|
|
32
|
+
onUpdate,
|
|
33
|
+
...(subscriptions && ctx?.hasUI ? { subscribe: (input) => subscriptions.arm(input) } : {}),
|
|
34
|
+
}));
|
|
35
|
+
const primaryTool: ToolDefinition<typeof SubagentWaitParams, Details> = {
|
|
36
|
+
name: "bg_wait",
|
|
37
|
+
label: "Background Wait",
|
|
38
|
+
description,
|
|
30
39
|
parameters: SubagentWaitParams,
|
|
31
|
-
|
|
32
|
-
return finalizeToolResult(await waitForSubagents(params, signal, {
|
|
33
|
-
state,
|
|
34
|
-
events: pi.events,
|
|
35
|
-
enabled,
|
|
36
|
-
...(defaultTimeoutMs !== undefined ? { defaultTimeoutMs } : {}),
|
|
37
|
-
onUpdate,
|
|
38
|
-
...(subscriptions && ctx?.hasUI ? { subscribe: (input) => subscriptions.arm(input) } : {}),
|
|
39
|
-
}));
|
|
40
|
-
},
|
|
40
|
+
execute,
|
|
41
41
|
};
|
|
42
|
-
pi.registerTool(
|
|
42
|
+
pi.registerTool(primaryTool);
|
|
43
|
+
pi.registerTool({
|
|
44
|
+
...primaryTool,
|
|
45
|
+
name: "subagent_wait",
|
|
46
|
+
label: "Subagent Wait (deprecated)",
|
|
47
|
+
description: "Deprecated compatibility alias for `bg_wait`. Use `bg_wait` for background, provider, or detached work without a native completion notification. It has the same parameters and behavior.",
|
|
48
|
+
});
|
|
43
49
|
}
|
|
@@ -49,6 +49,7 @@ import {
|
|
|
49
49
|
formatEmptyTerminalAssistantResponseError,
|
|
50
50
|
extractToolArgsPreview,
|
|
51
51
|
extractTextFromContent,
|
|
52
|
+
MAX_STREAMED_RECENT_TOOLS,
|
|
52
53
|
boundStreamedRecentTools,
|
|
53
54
|
boundStreamedRecentOutput,
|
|
54
55
|
boundStreamedToolCalls,
|
|
@@ -66,7 +67,7 @@ import { createJsonlWriter } from "../../shared/jsonl-writer.ts";
|
|
|
66
67
|
import { createOrcaProgressTab, type OrcaProgressTab } from "../shared/orca-progress-tabs.ts";
|
|
67
68
|
import { attachPostExitStdioGuard, trySignalChild } from "../../shared/post-exit-stdio-guard.ts";
|
|
68
69
|
import { resolvePermissionRules } from "../shared/permissions.ts";
|
|
69
|
-
import { applyThinkingSuffix, buildPiArgs, cleanupTempDir, projectLaunchResolvedChildExtensions, resolvePiLaunchToolPlan, type SubagentTaskDelivery } from "../shared/pi-args.ts";
|
|
70
|
+
import { applyThinkingSuffix, buildPiArgs, cleanupTempDir, deriveForkPromptCacheKey, projectLaunchResolvedChildExtensions, resolvePiLaunchToolPlan, type SubagentTaskDelivery } from "../shared/pi-args.ts";
|
|
70
71
|
import { deriveChildSessionName } from "../../shared/child-session-name.ts";
|
|
71
72
|
import { readRuntimeAcknowledgedExtensions } from "../shared/runtime-acknowledged-extensions.ts";
|
|
72
73
|
import { assertAgentAllowedByCapabilityCeiling, decodeSubagentCapabilityCeiling, intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling, SUBAGENT_CAPABILITY_CEILING_ENV } from "../shared/capability-ceiling.ts";
|
|
@@ -76,7 +77,7 @@ import { MISSING_STRUCTURED_OUTPUT_CALL_ERROR, readStructuredOutput, readStructu
|
|
|
76
77
|
import { formatMidToolExitError, formatProcessSignalError, isOrdinaryToolForMidToolExit, isUnexplainedProcessSignal } from "../shared/process-signal.ts";
|
|
77
78
|
import { readChildToolDiagnosticError } from "../shared/tool-availability.ts";
|
|
78
79
|
import { buildTimeoutRecoverySummary, collectTrackedMutationEvidence, snapshotTrackedMutations } from "../shared/mutation-evidence.ts";
|
|
79
|
-
import { captureSingleOutputSnapshot, extractChildWrittenOutput, finalizeSingleOutput, formatSavedOutputReference, injectOutputPathSystemPrompt, resolveSingleOutput, validateFileOnlyOutputMode, type SingleOutputSnapshot } from "../shared/single-output.ts";
|
|
80
|
+
import { captureSingleOutputSnapshot, extractChildWrittenOutput, finalizeSingleOutput, formatSavedOutputReference, hasSingleOutputChangedSinceSnapshot, injectOutputPathSystemPrompt, resolveSingleOutput, validateFileOnlyOutputMode, type SingleOutputSnapshot } from "../shared/single-output.ts";
|
|
80
81
|
import {
|
|
81
82
|
buildModelCandidates,
|
|
82
83
|
formatSubagentModelVerificationError,
|
|
@@ -297,6 +298,55 @@ function snapshotStreamResult(result: SingleResult, progress: AgentProgress): Si
|
|
|
297
298
|
return snapshot;
|
|
298
299
|
}
|
|
299
300
|
|
|
301
|
+
interface StructuredDelegationProgressState {
|
|
302
|
+
currentTool?: string;
|
|
303
|
+
currentToolArgs?: string;
|
|
304
|
+
recentOutput: string[];
|
|
305
|
+
recentTools: Array<{ tool: string; args: string }>;
|
|
306
|
+
activityState?: AgentProgress["activityState"];
|
|
307
|
+
model?: string;
|
|
308
|
+
toolCount: number;
|
|
309
|
+
tokens: number;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function captureStructuredDelegationProgressState(progress: AgentProgress, result: SingleResult): StructuredDelegationProgressState {
|
|
313
|
+
return {
|
|
314
|
+
currentTool: progress.currentTool,
|
|
315
|
+
currentToolArgs: progress.currentToolArgs,
|
|
316
|
+
recentOutput: [...progress.recentOutput],
|
|
317
|
+
recentTools: progress.recentTools.slice(-MAX_STREAMED_RECENT_TOOLS).map(({ tool, args }) => ({ tool, args })),
|
|
318
|
+
activityState: progress.activityState,
|
|
319
|
+
model: progress.model ?? result.model,
|
|
320
|
+
toolCount: progress.toolCount,
|
|
321
|
+
tokens: progress.tokens,
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function structuredDelegationProgressChanged(
|
|
326
|
+
previous: StructuredDelegationProgressState,
|
|
327
|
+
progress: AgentProgress,
|
|
328
|
+
result: SingleResult,
|
|
329
|
+
): boolean {
|
|
330
|
+
if (previous.currentTool !== progress.currentTool
|
|
331
|
+
|| previous.currentToolArgs !== progress.currentToolArgs
|
|
332
|
+
|| previous.activityState !== progress.activityState
|
|
333
|
+
|| previous.model !== (progress.model ?? result.model)
|
|
334
|
+
|| previous.toolCount !== progress.toolCount
|
|
335
|
+
|| previous.tokens !== progress.tokens
|
|
336
|
+
|| previous.recentOutput.length !== progress.recentOutput.length) return true;
|
|
337
|
+
for (let index = 0; index < progress.recentOutput.length; index++) {
|
|
338
|
+
if (previous.recentOutput[index] !== progress.recentOutput[index]) return true;
|
|
339
|
+
}
|
|
340
|
+
const recentToolsStart = Math.max(0, progress.recentTools.length - MAX_STREAMED_RECENT_TOOLS);
|
|
341
|
+
if (previous.recentTools.length !== progress.recentTools.length - recentToolsStart) return true;
|
|
342
|
+
for (let index = recentToolsStart; index < progress.recentTools.length; index++) {
|
|
343
|
+
const previousTool = previous.recentTools[index - recentToolsStart];
|
|
344
|
+
const currentTool = progress.recentTools[index];
|
|
345
|
+
if (previousTool?.tool !== currentTool?.tool || previousTool?.args !== currentTool?.args) return true;
|
|
346
|
+
}
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
349
|
+
|
|
300
350
|
const AFTER_COMPACTION_SETTLEMENT = Symbol("afterCompactionSettlement");
|
|
301
351
|
type AbortRecoverySingleResult = SingleResult & { [AFTER_COMPACTION_SETTLEMENT]?: true };
|
|
302
352
|
|
|
@@ -380,6 +430,7 @@ async function runSingleAttempt(
|
|
|
380
430
|
parentCapabilityToken: options.nestedRoute?.capabilityToken,
|
|
381
431
|
runFanoutBudget: options.runFanoutBudget,
|
|
382
432
|
parentSessionId: options.parentSessionId,
|
|
433
|
+
forkCacheKey: options.context === "fork" ? deriveForkPromptCacheKey(options.parentSessionId) : undefined,
|
|
383
434
|
steerInboxDir: options.steerInboxDir,
|
|
384
435
|
steerCapabilityPath: options.steerCapabilityPath,
|
|
385
436
|
steerAckDir: options.steerAckDir,
|
|
@@ -935,6 +986,7 @@ async function runSingleAttempt(
|
|
|
935
986
|
};
|
|
936
987
|
|
|
937
988
|
|
|
989
|
+
let lastStructuredDelegationProgressState: StructuredDelegationProgressState | undefined;
|
|
938
990
|
const emitUpdateSnapshot = (text: string) => {
|
|
939
991
|
if (!options.onUpdate || processClosed) return;
|
|
940
992
|
const progressSnapshot = snapshotProgress(progress);
|
|
@@ -954,6 +1006,10 @@ async function runSingleAttempt(
|
|
|
954
1006
|
const fireUpdate = () => {
|
|
955
1007
|
if (!options.onUpdate || processClosed) return;
|
|
956
1008
|
progress.durationMs = Date.now() - startTime;
|
|
1009
|
+
if (options.suppressUnchangedDelegationUpdates) {
|
|
1010
|
+
if (lastStructuredDelegationProgressState && !structuredDelegationProgressChanged(lastStructuredDelegationProgressState, progress, result)) return;
|
|
1011
|
+
lastStructuredDelegationProgressState = captureStructuredDelegationProgressState(progress, result);
|
|
1012
|
+
}
|
|
957
1013
|
const output = result.timedOut && result.finalOutput ? result.finalOutput : getFinalOutput(result.messages ?? []);
|
|
958
1014
|
emitUpdateSnapshot(output || "(running...)");
|
|
959
1015
|
};
|
|
@@ -1527,9 +1583,17 @@ async function runSingleAttempt(
|
|
|
1527
1583
|
result.outputState = fullOutput.trim() || result.structuredOutput !== undefined ? "present" : "absent";
|
|
1528
1584
|
if (result.timedOut) {
|
|
1529
1585
|
const timeoutMessage = formatTimeoutMessage(options.timeoutMs ?? 0);
|
|
1586
|
+
let requiredOutputMissing: boolean | undefined;
|
|
1587
|
+
if (options.outputMode === "file-only" && options.outputPath) {
|
|
1588
|
+
const outputChanged = hasSingleOutputChangedSinceSnapshot(options.outputPath, shared.outputSnapshot);
|
|
1589
|
+
requiredOutputMissing = outputChanged === undefined ? undefined : !outputChanged;
|
|
1590
|
+
} else if (options.structuredOutput) {
|
|
1591
|
+
requiredOutputMissing = !existsSync(options.structuredOutput.outputPath);
|
|
1592
|
+
}
|
|
1530
1593
|
result.timeoutRecovery = buildTimeoutRecoverySummary({
|
|
1531
1594
|
termination: "timed-out",
|
|
1532
1595
|
evidence: mutationEvidence,
|
|
1596
|
+
requiredOutputMissing,
|
|
1533
1597
|
currentTool: progress.currentTool,
|
|
1534
1598
|
currentToolArgs: progress.currentToolArgs,
|
|
1535
1599
|
currentPath: progress.currentPath,
|
|
@@ -1807,7 +1871,11 @@ async function runSyncCompletionInner(
|
|
|
1807
1871
|
agent.fallbackModels,
|
|
1808
1872
|
options.availableModels,
|
|
1809
1873
|
agent.modelProvider ?? options.preferredModelProvider,
|
|
1810
|
-
{
|
|
1874
|
+
{
|
|
1875
|
+
scope: options.modelScope,
|
|
1876
|
+
primaryModelFromParent: options.modelOverrideFromParent,
|
|
1877
|
+
origin: options.modelOrigin ?? (options.modelOverrideFromParent ? "inherited" : "configured"),
|
|
1878
|
+
},
|
|
1811
1879
|
);
|
|
1812
1880
|
if (options.workflowChildPermitLaunch && candidates.length > 1) {
|
|
1813
1881
|
const error = "Workflow child permit does not support model fallback.";
|
|
@@ -2150,7 +2218,7 @@ async function runSyncCompletionInner(
|
|
|
2150
2218
|
report: (result as SingleResult & { structuredAcceptanceReport?: import("../../shared/types.ts").AcceptanceReport; structuredAcceptanceReportError?: string }).structuredAcceptanceReport,
|
|
2151
2219
|
reportError: (result as SingleResult & { structuredAcceptanceReport?: import("../../shared/types.ts").AcceptanceReport; structuredAcceptanceReportError?: string }).structuredAcceptanceReportError,
|
|
2152
2220
|
fileOutput: childWrittenOutput !== undefined && options.outputPath
|
|
2153
|
-
? { content: childWrittenOutput, path: options.outputPath, authoritative: options.outputMode === "file-only" }
|
|
2221
|
+
? { content: childWrittenOutput, path: options.outputPath, authoritative: options.outputMode === "file-only", durable: result.savedOutputPath !== undefined }
|
|
2154
2222
|
: undefined,
|
|
2155
2223
|
cwd: options.cwd ?? runtimeCwd,
|
|
2156
2224
|
reportOptional: isAgentContractV1(options.agentContract),
|