pi-subagents 0.63.0 → 0.65.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 +57 -1
- package/README.md +2 -2
- package/agents/reviewer.md +1 -1
- package/agents/scout.md +1 -1
- package/docs/agents.md +18 -16
- package/docs/configuration.md +7 -15
- package/docs/extension-api.md +15 -6
- package/docs/missions.md +2 -0
- package/docs/observability.md +10 -12
- package/docs/tool-reference.md +7 -6
- package/docs/watchdog.md +92 -114
- package/docs/workflows.md +5 -3
- package/package.json +3 -4
- package/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
- package/skills/pi-subagents/references/execution-controls.md +6 -5
- package/src/agents/agent-management.ts +47 -15
- package/src/api/capability-ceiling.ts +0 -1
- package/src/api/{pi-args.ts → child-tool-plan.ts} +1 -1
- package/src/api/preflight.ts +2 -3
- package/src/extension/doctor.ts +2 -10
- package/src/extension/fanout-child.ts +9 -11
- package/src/extension/index.ts +27 -5
- package/src/extension/public-execution.ts +14 -0
- package/src/extension/rpc.ts +3 -2
- package/src/extension/schemas.ts +2 -1
- package/src/extension/tool-description.ts +9 -8
- package/src/intercom/native-supervisor-channel.ts +138 -60
- package/src/intercom/supervisor-ui.ts +243 -0
- package/src/runs/background/async-execution.ts +46 -24
- package/src/runs/background/async-job-tracker.ts +11 -0
- package/src/runs/background/async-resume.ts +11 -2
- package/src/runs/background/control-channel.ts +2 -204
- package/src/runs/background/notify.ts +41 -2
- package/src/runs/background/process-terminal.ts +1 -1
- package/src/runs/background/run-child-session.ts +613 -0
- package/src/runs/background/run-status.ts +0 -1
- package/src/runs/background/runner-aliases.ts +125 -0
- package/src/runs/background/runner-child-sessions.ts +31 -0
- package/src/runs/background/scheduled-runs.ts +18 -4
- package/src/runs/background/subagent-runner.ts +203 -898
- package/src/runs/foreground/async-steering-action.ts +1 -17
- package/src/runs/foreground/execution.ts +196 -378
- package/src/runs/foreground/foreground-control.ts +4 -0
- package/src/runs/foreground/subagent-executor.ts +121 -58
- package/src/runs/foreground/workflow-foreground-steering.ts +24 -98
- package/src/runs/shared/abort-recovery.ts +3 -3
- package/src/runs/shared/acceptance.ts +10 -0
- package/src/runs/shared/async-status-projection.ts +11 -43
- package/src/runs/shared/capability-ceiling.ts +1 -2
- package/src/runs/shared/child-hooks.ts +25 -0
- package/src/runs/shared/child-identity.ts +13 -2
- package/src/runs/shared/child-launch.ts +314 -0
- package/src/runs/shared/child-lifecycle.ts +25 -0
- package/src/runs/shared/child-runtime-config.ts +126 -0
- package/src/runs/shared/child-session.ts +342 -0
- package/src/runs/shared/child-tool-plan.ts +530 -0
- package/src/runs/shared/claude-code-adapter.ts +5 -1
- package/src/runs/shared/completion-guard.ts +1 -1
- package/src/runs/shared/external-cli-preflight.ts +16 -0
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +5 -4
- package/src/runs/shared/model-exclusions.ts +82 -14
- package/src/runs/shared/model-fallback.ts +47 -4
- package/src/runs/shared/nested-events.ts +29 -45
- package/src/runs/shared/nested-path.ts +0 -14
- package/src/runs/shared/orca-progress-tabs.ts +12 -7
- package/src/runs/shared/parallel-utils.ts +0 -2
- package/src/runs/shared/permissions.ts +0 -13
- package/src/runs/shared/process-signal.ts +4 -1
- package/src/runs/shared/run-fanout-budget.ts +0 -13
- package/src/runs/shared/runtime-acknowledged-extensions.ts +0 -27
- package/src/runs/shared/structured-output.ts +17 -4
- package/src/runs/shared/subagent-control.ts +6 -2
- package/src/runs/shared/subagent-prompt-runtime.ts +87 -384
- package/src/runs/shared/tool-availability.ts +18 -62
- package/src/runs/shared/tool-budget.ts +0 -14
- package/src/runs/shared/worktree-cleanup-plan.ts +25 -6
- package/src/runs/shared/worktree.ts +117 -30
- package/src/shared/child-session-name.ts +1 -1
- package/src/shared/jsonl-writer.ts +11 -0
- package/src/shared/thinking-ceiling.ts +0 -6
- package/src/shared/types.ts +64 -30
- package/src/shared/utils.ts +3 -4
- package/src/slash/slash-commands.ts +0 -6
- package/src/tui/fleet.ts +0 -1
- package/src/tui/render.ts +238 -31
- package/src/watchdog/child-status.ts +54 -34
- package/src/watchdog/diff-tool.ts +77 -0
- package/src/watchdog/emission-guard.ts +5 -3
- package/src/watchdog/guidance.ts +20 -0
- package/src/watchdog/register-child.ts +28 -25
- package/src/watchdog/register-main.ts +10 -9
- package/src/watchdog/render.ts +4 -5
- package/src/watchdog/review.ts +15 -4
- package/src/watchdog/rules.ts +70 -0
- package/src/watchdog/runtime.ts +75 -92
- package/src/watchdog/scope.ts +0 -11
- package/src/watchdog/settings.ts +48 -104
- package/src/watchdog/types.ts +18 -32
- package/src/watchdog/warning-format.ts +0 -1
- package/src/workflows/chat-progress.ts +3 -2
- package/src/workflows/scripted-workflow.ts +48 -1
- package/src/workflows/workflow-checklist.ts +10 -12
- package/src/workflows/workflow-preflight.ts +28 -1
- package/src/runs/shared/child-protocol.ts +0 -415
- package/src/runs/shared/pi-args.ts +0 -1062
- package/src/runs/shared/subagent-startup-retry.ts +0 -116
- package/src/shared/post-exit-stdio-guard.ts +0 -85
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import type { Usage } from "../../shared/types.ts";
|
|
2
|
-
import { formatProcessSignalError } from "./process-signal.ts";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Retry delays for child processes that exit before any model or tool activity.
|
|
6
|
-
* The schedule is intentionally short and bounded to avoid amplifying persistent
|
|
7
|
-
* launch failures while allowing concurrent Pi startup locks to clear.
|
|
8
|
-
*/
|
|
9
|
-
export const SUBAGENT_STARTUP_RETRY_DELAYS_MS = [250, 750, 1500] as const;
|
|
10
|
-
|
|
11
|
-
/** A genuine startup race should fail well before a model request can complete. */
|
|
12
|
-
export const MAX_SUBAGENT_STARTUP_FAILURE_DURATION_MS = 2000;
|
|
13
|
-
|
|
14
|
-
const SIGKILL_PROCESS_SIGNAL_ERROR = formatProcessSignalError("SIGKILL");
|
|
15
|
-
const EXTENSION_REGISTRATION_CONFLICT_PATTERN = /Failed to load extension\b[^\n]*:\s*(?:Tool|Flag)\s+"[^"]+"\s+conflicts with\b/i;
|
|
16
|
-
|
|
17
|
-
export interface SubagentStartupFailureEvidence {
|
|
18
|
-
exitCode: number | null | undefined;
|
|
19
|
-
error?: string;
|
|
20
|
-
finalOutput?: string;
|
|
21
|
-
messageCount: number;
|
|
22
|
-
toolCount: number;
|
|
23
|
-
usage: Usage;
|
|
24
|
-
durationMs: number;
|
|
25
|
-
protocolError?: unknown;
|
|
26
|
-
processSignal?: string | null;
|
|
27
|
-
observedMutationAttempt?: boolean;
|
|
28
|
-
detached?: boolean;
|
|
29
|
-
interrupted?: boolean;
|
|
30
|
-
timedOut?: boolean;
|
|
31
|
-
stopped?: boolean;
|
|
32
|
-
turnBudgetExceeded?: boolean;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function formatSubagentExtensionConflictError(
|
|
36
|
-
error: string | undefined,
|
|
37
|
-
input: { agent: string; ambientExtensionsEnabled: boolean },
|
|
38
|
-
): string | undefined {
|
|
39
|
-
if (!error || !input.ambientExtensionsEnabled || !EXTENSION_REGISTRATION_CONFLICT_PATTERN.test(error)) return error;
|
|
40
|
-
const settings = JSON.stringify({
|
|
41
|
-
subagents: { agentOverrides: { [input.agent]: { extensions: [] } } },
|
|
42
|
-
});
|
|
43
|
-
return `${error}\n\nSubagent startup loaded conflicting ambient Pi extensions. Retry this run after disabling ambient extension discovery for agent ${JSON.stringify(input.agent)} in project .pi/settings.json: ${settings}. Replace the empty array with only the required extension entry paths when this agent needs extension tools.`;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function hasUsage(usage: Usage): boolean {
|
|
47
|
-
return usage.input !== 0
|
|
48
|
-
|| usage.output !== 0
|
|
49
|
-
|| usage.cacheRead !== 0
|
|
50
|
-
|| usage.cacheWrite !== 0
|
|
51
|
-
|| usage.cost !== 0
|
|
52
|
-
|| usage.turns !== 0;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Classify only unexplained, zero-activity child exits as startup failures.
|
|
57
|
-
* This intentionally fails closed: any model output, tool activity, usage,
|
|
58
|
-
* mutation evidence, diagnostic, or lifecycle interruption prevents a retry.
|
|
59
|
-
*/
|
|
60
|
-
export function isRetryableSubagentStartupFailure(evidence: SubagentStartupFailureEvidence): boolean {
|
|
61
|
-
return evidence.exitCode !== undefined
|
|
62
|
-
&& evidence.exitCode !== null
|
|
63
|
-
&& evidence.exitCode !== 0
|
|
64
|
-
&& (!evidence.error?.trim() || evidence.error === SIGKILL_PROCESS_SIGNAL_ERROR)
|
|
65
|
-
&& !evidence.finalOutput?.trim()
|
|
66
|
-
&& evidence.messageCount === 0
|
|
67
|
-
&& evidence.toolCount === 0
|
|
68
|
-
&& !hasUsage(evidence.usage)
|
|
69
|
-
&& evidence.durationMs >= 0
|
|
70
|
-
&& evidence.durationMs <= MAX_SUBAGENT_STARTUP_FAILURE_DURATION_MS
|
|
71
|
-
&& evidence.protocolError === undefined
|
|
72
|
-
&& (evidence.processSignal === undefined || evidence.processSignal === null || evidence.processSignal === "SIGKILL")
|
|
73
|
-
&& evidence.observedMutationAttempt !== true
|
|
74
|
-
&& evidence.detached !== true
|
|
75
|
-
&& evidence.interrupted !== true
|
|
76
|
-
&& evidence.timedOut !== true
|
|
77
|
-
&& evidence.stopped !== true
|
|
78
|
-
&& evidence.turnBudgetExceeded !== true;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export function formatSubagentStartupRetryNote(input: {
|
|
82
|
-
model: string;
|
|
83
|
-
attempt: number;
|
|
84
|
-
maxAttempts: number;
|
|
85
|
-
delayMs: number;
|
|
86
|
-
}): string {
|
|
87
|
-
return `[startup-retry] ${input.model} exited before model or tool activity (attempt ${input.attempt}/${input.maxAttempts}). Retrying the same model in ${input.delayMs}ms.`;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export function formatSubagentStartupRetryExhaustedError(input: {
|
|
91
|
-
model: string;
|
|
92
|
-
attempts: number;
|
|
93
|
-
}): string {
|
|
94
|
-
return `Subagent failed to start after ${input.attempts} attempts on ${input.model}; no model, tool, output, usage, or diagnostic activity was observed. This may be a concurrent Pi startup race. Retry the run or temporarily lower subagent concurrency.`;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/** Wait for a retry delay, returning false when any lifecycle signal aborts it. */
|
|
98
|
-
export async function waitForSubagentStartupRetry(delayMs: number, signals: Array<AbortSignal | undefined>): Promise<boolean> {
|
|
99
|
-
const activeSignals = signals.filter((signal): signal is AbortSignal => Boolean(signal));
|
|
100
|
-
if (activeSignals.some((signal) => signal.aborted)) return false;
|
|
101
|
-
if (delayMs <= 0) return true;
|
|
102
|
-
|
|
103
|
-
return await new Promise<boolean>((resolve) => {
|
|
104
|
-
let settled = false;
|
|
105
|
-
const finish = (shouldRetry: boolean): void => {
|
|
106
|
-
if (settled) return;
|
|
107
|
-
settled = true;
|
|
108
|
-
clearTimeout(timer);
|
|
109
|
-
for (const signal of activeSignals) signal.removeEventListener("abort", abort);
|
|
110
|
-
resolve(shouldRetry);
|
|
111
|
-
};
|
|
112
|
-
const abort = (): void => finish(false);
|
|
113
|
-
const timer = setTimeout(() => finish(true), delayMs);
|
|
114
|
-
for (const signal of activeSignals) signal.addEventListener("abort", abort, { once: true });
|
|
115
|
-
});
|
|
116
|
-
}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import type { ChildProcess } from "node:child_process";
|
|
2
|
-
|
|
3
|
-
interface PostExitStdioGuardOptions {
|
|
4
|
-
idleMs: number;
|
|
5
|
-
hardMs: number;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
interface ChildWithPipedStdio {
|
|
9
|
-
stdout: ChildProcess["stdout"];
|
|
10
|
-
stderr: ChildProcess["stderr"];
|
|
11
|
-
on: ChildProcess["on"];
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
interface ChildWithKill {
|
|
15
|
-
kill(signal?: NodeJS.Signals | number): boolean;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function trySignalChild(child: ChildWithKill, signal: NodeJS.Signals): boolean {
|
|
19
|
-
try {
|
|
20
|
-
return child.kill(signal);
|
|
21
|
-
} catch {
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function attachPostExitStdioGuard(
|
|
27
|
-
child: ChildWithPipedStdio,
|
|
28
|
-
options: PostExitStdioGuardOptions,
|
|
29
|
-
): () => void {
|
|
30
|
-
const { idleMs, hardMs } = options;
|
|
31
|
-
let exited = false;
|
|
32
|
-
let stdoutEnded = false;
|
|
33
|
-
let stderrEnded = false;
|
|
34
|
-
let idleTimer: NodeJS.Timeout | undefined;
|
|
35
|
-
let hardTimer: NodeJS.Timeout | undefined;
|
|
36
|
-
|
|
37
|
-
const destroyUnendedStdio = () => {
|
|
38
|
-
if (!stdoutEnded) {
|
|
39
|
-
try { child.stdout?.destroy(); } catch {}
|
|
40
|
-
}
|
|
41
|
-
if (!stderrEnded) {
|
|
42
|
-
try { child.stderr?.destroy(); } catch {}
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const clearTimers = () => {
|
|
47
|
-
if (idleTimer) {
|
|
48
|
-
clearTimeout(idleTimer);
|
|
49
|
-
idleTimer = undefined;
|
|
50
|
-
}
|
|
51
|
-
if (hardTimer) {
|
|
52
|
-
clearTimeout(hardTimer);
|
|
53
|
-
hardTimer = undefined;
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const armIdleTimer = () => {
|
|
58
|
-
if (!exited) return;
|
|
59
|
-
if (idleTimer) clearTimeout(idleTimer);
|
|
60
|
-
idleTimer = setTimeout(destroyUnendedStdio, idleMs);
|
|
61
|
-
idleTimer.unref?.();
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
child.stdout?.on("data", armIdleTimer);
|
|
65
|
-
child.stderr?.on("data", armIdleTimer);
|
|
66
|
-
child.stdout?.on("end", () => {
|
|
67
|
-
stdoutEnded = true;
|
|
68
|
-
if (stdoutEnded && stderrEnded) clearTimers();
|
|
69
|
-
});
|
|
70
|
-
child.stderr?.on("end", () => {
|
|
71
|
-
stderrEnded = true;
|
|
72
|
-
if (stdoutEnded && stderrEnded) clearTimers();
|
|
73
|
-
});
|
|
74
|
-
child.on("exit", () => {
|
|
75
|
-
exited = true;
|
|
76
|
-
armIdleTimer();
|
|
77
|
-
if (hardTimer) return;
|
|
78
|
-
hardTimer = setTimeout(destroyUnendedStdio, hardMs);
|
|
79
|
-
hardTimer.unref?.();
|
|
80
|
-
});
|
|
81
|
-
child.on("close", clearTimers);
|
|
82
|
-
child.on("error", clearTimers);
|
|
83
|
-
|
|
84
|
-
return clearTimers;
|
|
85
|
-
}
|