pi-subagents 0.66.0 → 0.68.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 +138 -0
- package/README.md +5 -4
- package/agents/evidence-auditor.md +34 -0
- package/agents/reviewer.md +3 -2
- package/docs/agents.md +43 -15
- package/docs/configuration.md +67 -23
- package/docs/extension-api.md +38 -19
- package/docs/missions.md +10 -2
- package/docs/models.md +11 -79
- package/docs/observability.md +22 -12
- package/docs/standalone-background.md +59 -0
- package/docs/tool-reference.md +33 -20
- package/docs/watchdog.md +39 -10
- package/docs/workflows.md +37 -13
- package/index.ts +5 -2
- package/inspector-runner.mjs +2 -2
- package/package.json +4 -2
- package/prompts/parallel-review.md +1 -1
- package/runner-peer-loader.mjs +24 -0
- package/runner-peer-preload.mjs +32 -0
- package/skills/pi-subagents/SKILL.md +32 -21
- package/skills/pi-subagents/references/constraints-and-recipes.md +3 -2
- package/skills/pi-subagents/references/execution-controls.md +11 -9
- package/skills/pi-subagents/references/management-authoring-rpc.md +0 -1
- package/skills/pi-subagents/references/multi-lane-orchestration.md +1 -1
- package/skills/pi-subagents/references/prompting-and-roles.md +17 -13
- package/skills/pi-subagents/references/review-and-validation.md +3 -3
- package/src/agents/advertised-agent-prompt.ts +34 -3
- package/src/agents/agent-management.ts +57 -58
- package/src/agents/agent-serializer.ts +4 -3
- package/src/agents/agents.ts +190 -71
- package/src/agents/builtin-names.ts +1 -0
- package/src/agents/chain-serializer.ts +5 -0
- package/src/agents/runtime-agent-registry.ts +7 -6
- package/src/api/delegation.ts +4 -0
- package/src/api/preflight.ts +94 -59
- package/src/api/required-child-extensions.ts +6 -0
- package/src/api/shared-types.ts +2 -0
- package/src/extension/config.ts +10 -37
- package/src/extension/fanout-child.ts +66 -4
- package/src/extension/herdr-pi-bridge.ts +160 -0
- package/src/extension/index.ts +62 -39
- package/src/extension/public-execution.ts +7 -5
- package/src/extension/rpc.ts +4 -0
- package/src/extension/schemas.ts +81 -81
- package/src/extension/tool-description.ts +30 -82
- package/src/inspectors/actions.ts +148 -0
- package/src/inspectors/ghostty/actions.ts +74 -0
- package/src/inspectors/ghostty/plugin.ts +17 -0
- package/src/inspectors/herdr/actions.ts +99 -179
- package/src/inspectors/herdr/plugin.ts +20 -0
- package/src/inspectors/herdr/project-panes.ts +1 -1
- package/src/inspectors/{herdr/inspector-runner.ts → inspector-runner.ts} +12 -12
- package/src/inspectors/plugins.ts +8 -0
- package/src/inspectors/{herdr/session-roots-codec.ts → session-roots-codec.ts} +3 -14
- package/src/inspectors/types.ts +51 -0
- package/src/intercom/intercom-bridge.ts +50 -8
- package/src/intercom/native-supervisor-channel.ts +44 -31
- package/src/policy/authority.ts +4 -0
- package/src/profiles/profiles.ts +12 -6
- package/src/runs/background/active-async-capacity.ts +4 -0
- package/src/runs/background/active-run-index.ts +17 -1
- package/src/runs/background/async-execution.ts +348 -176
- package/src/runs/background/async-job-tracker.ts +8 -6
- package/src/runs/background/async-resume.ts +17 -12
- package/src/runs/background/async-status.ts +15 -4
- package/src/runs/background/auto-drain.ts +23 -10
- package/src/runs/background/binary-bootstrap.ts +38 -0
- package/src/runs/background/chain-append.ts +1 -1
- package/src/runs/background/chain-root-attachment.ts +14 -33
- package/src/runs/background/fleet-view.ts +30 -2
- package/src/runs/background/notify.ts +105 -7
- package/src/runs/background/owned-process-tree.ts +29 -2
- package/src/runs/background/result-files.ts +8 -4
- package/src/runs/background/result-watcher.ts +19 -2
- package/src/runs/background/run-child-session.ts +81 -33
- package/src/runs/background/run-status.ts +3 -0
- package/src/runs/background/runner-aliases.ts +12 -33
- package/src/runs/background/runner-child-launch.ts +6 -1
- package/src/runs/background/runner-child-sessions.ts +5 -4
- package/src/runs/background/runner-http-dispatcher.ts +119 -0
- package/src/runs/background/scheduled-runs.ts +51 -18
- package/src/runs/background/stale-run-reconciler.ts +35 -11
- package/src/runs/background/steering.ts +20 -2
- package/src/runs/background/subagent-runner.ts +441 -304
- package/src/runs/background/subagent-wait.ts +176 -28
- package/src/runs/background/wait-completions.ts +75 -27
- package/src/runs/background/wait-subscriptions.ts +9 -3
- package/src/runs/background/wait-tool.ts +5 -3
- package/src/runs/foreground/async-steering-action.ts +18 -7
- package/src/runs/foreground/async-stop-action.ts +93 -3
- package/src/runs/foreground/execution.ts +134 -248
- package/src/runs/foreground/foreground-history.ts +2 -1
- package/src/runs/foreground/prompt-audit.ts +3 -1
- package/src/runs/foreground/subagent-executor.ts +374 -178
- package/src/runs/foreground/workflow-detach-reconcile.ts +2 -0
- package/src/runs/foreground/workflow-foreground-steering.ts +2 -1
- package/src/runs/shared/acceptance.ts +38 -11
- package/src/runs/shared/async-status-projection.ts +127 -33
- package/src/runs/shared/capability-ceiling.ts +2 -0
- package/src/runs/shared/child-hooks.ts +25 -10
- package/src/runs/shared/child-launch-plan.ts +15 -3
- package/src/runs/shared/child-launch.ts +28 -5
- package/src/runs/shared/child-lifecycle.ts +6 -3
- package/src/runs/shared/child-runtime-config.ts +8 -1
- package/src/runs/shared/child-session.ts +127 -52
- package/src/runs/shared/child-tool-plan.ts +142 -11
- package/src/runs/shared/completion-guard.ts +5 -3
- package/src/runs/shared/dynamic-fanout.ts +2 -2
- package/src/runs/shared/effective-system-prompt.ts +33 -0
- package/src/runs/shared/external-cli-contract.ts +11 -1
- package/src/runs/shared/external-cli-preflight.ts +6 -2
- package/src/runs/shared/external-cli-runner.ts +9 -7
- package/src/runs/shared/herdr-connection.ts +134 -0
- package/src/runs/shared/herdr-external-adapters.ts +169 -0
- package/src/runs/shared/herdr-machine.ts +279 -0
- package/src/runs/shared/herdr-pi-protocol.ts +59 -0
- package/src/runs/shared/herdr-placed-run.ts +263 -0
- package/src/runs/shared/llm-intent-arbiter.ts +12 -3
- package/src/runs/shared/model-resolution-diagnostic.ts +76 -0
- package/src/runs/shared/{model-fallback.ts → model-resolution.ts} +22 -235
- package/src/runs/shared/model-scope.ts +1 -1
- package/src/runs/shared/nested-events.ts +11 -2
- package/src/runs/shared/orca-progress-tabs.ts +1 -1
- package/src/runs/shared/parallel-utils.ts +7 -2
- package/src/runs/shared/pi-spawn.ts +10 -0
- package/src/runs/shared/subagent-prompt-runtime.ts +12 -4
- package/src/runs/shared/task-intent.ts +46 -13
- package/src/runs/shared/workflow-async-child-guidance.ts +18 -0
- package/src/runs/shared/worktree-setup-command.ts +27 -4
- package/src/runs/shared/worktree.ts +45 -15
- package/src/shared/child-cache-retention.ts +43 -0
- package/src/shared/fork-context.ts +15 -72
- package/src/shared/launch-contract.ts +68 -8
- package/src/shared/opencode-session-headers.ts +30 -0
- package/src/shared/pruned-fork.ts +1 -1
- package/src/shared/required-child-extensions.ts +81 -0
- package/src/shared/settings.ts +5 -2
- package/src/shared/shortcuts.ts +0 -4
- package/src/shared/types.ts +74 -30
- package/src/slash/delegation-adapters.ts +3 -1
- package/src/slash/delegation-request.ts +14 -0
- package/src/slash/slash-commands.ts +2 -7
- package/src/slash/subagents-admin.ts +24 -13
- package/src/tui/fleet-status.ts +164 -19
- package/src/tui/fleet.ts +16 -14
- package/src/tui/render.ts +168 -37
- package/src/watchdog/child-status.ts +28 -28
- package/src/watchdog/lsp-diagnostics.ts +1 -1
- package/src/watchdog/model-selection.ts +21 -1
- package/src/watchdog/permission-arbiter.ts +3 -1
- package/src/watchdog/register-child.ts +10 -2
- package/src/watchdog/register-main.ts +39 -35
- package/src/watchdog/render.ts +1 -1
- package/src/watchdog/review.ts +123 -74
- package/src/watchdog/rules.ts +1 -1
- package/src/watchdog/runtime.ts +100 -27
- package/src/watchdog/scope.ts +1 -1
- package/src/watchdog/settings.ts +3 -0
- package/src/watchdog/tool-actions.ts +13 -12
- package/src/watchdog/turn-delta.ts +23 -0
- package/src/watchdog/types.ts +5 -3
- package/src/watchdog/warning-format.ts +1 -1
- package/src/workflows/scripted-workflow.ts +279 -10
- package/src/workflows/workflow-checklist.ts +2 -2
- package/src/workflows/workflow-receipt.ts +21 -3
- package/src/workflows/workflow-resources.ts +13 -2
- package/runner-server-preload.mjs +0 -13
- package/src/runs/shared/model-exclusions.ts +0 -374
- package/src/runs/shared/readonly-model-continuation.ts +0 -69
- package/src/runs/shared/readonly-session-evidence.ts +0 -307
- /package/src/inspectors/{herdr/shell-command.ts → shell-command.ts} +0 -0
|
@@ -3,13 +3,18 @@ import * as fs from "node:fs";
|
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import type { AgentToolResult } from "@earendil-works/pi-agent-core";
|
|
5
5
|
import { writeAtomicJson } from "../../shared/atomic-json.ts";
|
|
6
|
-
import type { AsyncStatus, Details, SubagentState, ToolBudgetConfig } from "../../shared/types.ts";
|
|
6
|
+
import type { AsyncStatus, Details, SteerActionResult, SubagentState, ToolBudgetConfig } from "../../shared/types.ts";
|
|
7
7
|
import { readStatus } from "../../shared/utils.ts";
|
|
8
8
|
import { deliverInterruptRequest, queueRevivalBrief, requestAsyncSteer, type SteerDeliveryMode, type SteerRequest } from "../background/control-channel.ts";
|
|
9
9
|
import { resolveAsyncResumeTarget } from "../background/async-resume.ts";
|
|
10
10
|
import { reconcileAsyncRun } from "../background/stale-run-reconciler.ts";
|
|
11
11
|
import { actionResultFromSteeringStatus, claimSteeringRecovery, createSteeringStatus, recordSteeringRequest, remainingSteeringRecoveryLimits, steeringReceipt, updateSteeringTarget, waitForSteeringAction } from "../background/steering.ts";
|
|
12
12
|
|
|
13
|
+
function steerTargetsAccepted(result: Pick<SteerActionResult, "targets"> | undefined): boolean {
|
|
14
|
+
const targets = result?.targets ?? [];
|
|
15
|
+
return targets.length > 0 && targets.every((target) => target.state === "queued" || target.state === "delivered");
|
|
16
|
+
}
|
|
17
|
+
|
|
13
18
|
export function canQueueRetainedAsyncFollowUp(status: AsyncStatus, index?: number): boolean {
|
|
14
19
|
const steps = status.steps ?? [];
|
|
15
20
|
return status.state === "complete"
|
|
@@ -170,9 +175,11 @@ export async function steerAsyncRun(input: {
|
|
|
170
175
|
}
|
|
171
176
|
const finalStatus = readStatus(asyncDir);
|
|
172
177
|
const finalResult = finalStatus?.steering ? actionResultFromSteeringStatus(finalStatus.steering, status.runId, requestId) : undefined;
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
178
|
+
const acceptedReply = (accepted: SteerActionResult): AgentToolResult<Details> => {
|
|
179
|
+
const acceptedState = accepted.state === "delivered" ? "delivered" : "queued";
|
|
180
|
+
return { content: [{ type: "text", text: steeringReceipt(input.message, `Steering ${acceptedState} for async run ${status.runId} (request ${requestId}).`) }], details: { mode: "management", results: [], steering: accepted } };
|
|
181
|
+
};
|
|
182
|
+
if (finalResult && steerTargetsAccepted(finalResult)) return acceptedReply(finalResult);
|
|
176
183
|
const running = (finalStatus?.steps ?? status.steps ?? []).filter((step) => step.status === "running");
|
|
177
184
|
const recoveryAllowed = (input.mode ?? "steer") === "steer" && status.mode === "single" && status.isNested !== true && running.length === 1 && Boolean(finalStatus?.steering) && (input.index === undefined || input.index === 0);
|
|
178
185
|
if (recoveryAllowed && finalResult?.state !== "scheduled" && input.recover) {
|
|
@@ -186,17 +193,21 @@ export async function steerAsyncRun(input: {
|
|
|
186
193
|
try {
|
|
187
194
|
const latest = readStatus(asyncDir);
|
|
188
195
|
const latestResult = latest?.steering ? actionResultFromSteeringStatus(latest.steering, status.runId, requestId) : undefined;
|
|
189
|
-
if (latestResult
|
|
196
|
+
if (latestResult && steerTargetsAccepted(latestResult)) return acceptedReply(latestResult);
|
|
190
197
|
const committedAt = Date.now();
|
|
191
198
|
input.onBeforeRecoveryClaim?.(requestId, committedAt);
|
|
192
199
|
const { claimPath, markerPath } = claimSteeringRecovery(asyncDir, { requestId, sourceRunId: status.runId, committedAt });
|
|
193
200
|
input.onRecoveryCommitted?.(requestId, committedAt);
|
|
194
201
|
const preCommitStatus = readStatus(asyncDir);
|
|
195
202
|
const preCommitResult = preCommitStatus?.steering ? actionResultFromSteeringStatus(preCommitStatus.steering, status.runId, requestId) : undefined;
|
|
196
|
-
if (
|
|
203
|
+
if (
|
|
204
|
+
preCommitResult
|
|
205
|
+
&& preCommitResult.targets.length > 0
|
|
206
|
+
&& preCommitResult.targets.every((target) => target.state === "queued" || (target.deliveredAt !== undefined && target.deliveredAt <= committedAt))
|
|
207
|
+
) {
|
|
197
208
|
fs.rmSync(markerPath, { force: true });
|
|
198
209
|
fs.rmSync(claimPath, { force: true });
|
|
199
|
-
return
|
|
210
|
+
return acceptedReply(preCommitResult);
|
|
200
211
|
}
|
|
201
212
|
try {
|
|
202
213
|
deliverInterruptRequest({ asyncDir, source: "steering-recovery" });
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
1
2
|
import * as path from "node:path";
|
|
2
3
|
import type { AgentToolResult } from "@earendil-works/pi-agent-core";
|
|
3
|
-
import
|
|
4
|
+
import { writeAtomicJson } from "../../shared/atomic-json.ts";
|
|
5
|
+
import { DIRS, type AsyncStatus, type Details, type SubagentState } from "../../shared/types.ts";
|
|
6
|
+
import { updateActiveRunIndex } from "../background/active-run-index.ts";
|
|
4
7
|
import { deliverStopRequest } from "../background/control-channel.ts";
|
|
8
|
+
import { readProcessTerminal } from "../background/process-terminal.ts";
|
|
9
|
+
import { resultFilePath, resultPayloadPathForSessionRun, writeAsyncResultFile } from "../background/result-files.ts";
|
|
5
10
|
import { reconcileAsyncRun } from "../background/stale-run-reconciler.ts";
|
|
6
11
|
import { isStoppableAsyncStatusStep, resolveAsyncStatusChild, type ResolvedAsyncStatusChild } from "../shared/child-identity.ts";
|
|
7
12
|
|
|
@@ -21,6 +26,80 @@ function getAsyncStopTarget(
|
|
|
21
26
|
return direct ? { asyncId: direct.asyncId, asyncDir: direct.asyncDir } : undefined;
|
|
22
27
|
}
|
|
23
28
|
|
|
29
|
+
const STOP_MESSAGE = "Subagent stopped by user.";
|
|
30
|
+
|
|
31
|
+
function sealPausedRun(asyncDir: string, status: AsyncStatus): string | undefined {
|
|
32
|
+
const runnerProcessInstanceId = status.processTerminal?.runnerProcessInstanceId;
|
|
33
|
+
if (!runnerProcessInstanceId) return "runner process identity is missing";
|
|
34
|
+
const proof = readProcessTerminal(asyncDir, { runId: status.runId, runnerProcessInstanceId });
|
|
35
|
+
if (proof?.state !== "observed" || proof.runId !== status.runId || proof.runnerProcessInstanceId !== runnerProcessInstanceId) {
|
|
36
|
+
return `process-terminal proof is ${proof?.state === "unknown" ? `unknown (${proof.reason})` : proof?.state ?? "missing"}`;
|
|
37
|
+
}
|
|
38
|
+
if (!status.sessionId) return "session identity is missing";
|
|
39
|
+
const existingResultPath = resultPayloadPathForSessionRun(DIRS.results, status.sessionId, status.runId);
|
|
40
|
+
if (!existingResultPath) return "paused result is missing";
|
|
41
|
+
let existingResult: Record<string, unknown>;
|
|
42
|
+
try {
|
|
43
|
+
const parsed: unknown = JSON.parse(fs.readFileSync(existingResultPath, "utf-8"));
|
|
44
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("result is not an object");
|
|
45
|
+
existingResult = parsed as Record<string, unknown>;
|
|
46
|
+
} catch (error) {
|
|
47
|
+
return `paused result is unavailable: ${error instanceof Error ? error.message : String(error)}`;
|
|
48
|
+
}
|
|
49
|
+
const resultRunId = typeof existingResult.runId === "string" ? existingResult.runId : existingResult.id;
|
|
50
|
+
if (resultRunId !== status.runId || existingResult.sessionId !== status.sessionId) {
|
|
51
|
+
return "paused result identity does not match the run";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const now = Date.now();
|
|
55
|
+
const steps = (status.steps ?? []).map((step) => {
|
|
56
|
+
if (step.status !== "pending" && step.status !== "running" && step.status !== "paused") return step;
|
|
57
|
+
const { activityState: _activityState, ...rest } = step;
|
|
58
|
+
return {
|
|
59
|
+
...rest,
|
|
60
|
+
status: "stopped" as const,
|
|
61
|
+
error: STOP_MESSAGE,
|
|
62
|
+
exitCode: 1,
|
|
63
|
+
stopped: true,
|
|
64
|
+
endedAt: now,
|
|
65
|
+
durationMs: step.startedAt ? now - step.startedAt : 0,
|
|
66
|
+
lastActivityAt: now,
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
const results = Array.isArray(existingResult.results)
|
|
70
|
+
? existingResult.results.map((entry, index) => {
|
|
71
|
+
const stepState = status.steps?.[index]?.status;
|
|
72
|
+
if ((stepState !== "pending" && stepState !== "running" && stepState !== "paused") || !entry || typeof entry !== "object" || Array.isArray(entry)) return entry;
|
|
73
|
+
const { interrupted: _interrupted, ...result } = entry as Record<string, unknown>;
|
|
74
|
+
return { ...result, output: STOP_MESSAGE, error: STOP_MESSAGE, success: false, exitCode: 1, stopped: true };
|
|
75
|
+
})
|
|
76
|
+
: existingResult.results;
|
|
77
|
+
const stoppedResult = {
|
|
78
|
+
...existingResult,
|
|
79
|
+
success: false,
|
|
80
|
+
state: "stopped",
|
|
81
|
+
summary: STOP_MESSAGE,
|
|
82
|
+
error: STOP_MESSAGE,
|
|
83
|
+
stopped: true,
|
|
84
|
+
exitCode: 1,
|
|
85
|
+
timestamp: now,
|
|
86
|
+
...(Array.isArray(existingResult.results) ? { results } : {}),
|
|
87
|
+
};
|
|
88
|
+
const stoppedStatus: AsyncStatus = {
|
|
89
|
+
...status,
|
|
90
|
+
state: "stopped",
|
|
91
|
+
stopped: true,
|
|
92
|
+
error: STOP_MESSAGE,
|
|
93
|
+
endedAt: now,
|
|
94
|
+
lastUpdate: now,
|
|
95
|
+
steps,
|
|
96
|
+
};
|
|
97
|
+
delete stoppedStatus.activityState;
|
|
98
|
+
writeAsyncResultFile(resultFilePath(DIRS.results, status.runId), stoppedResult);
|
|
99
|
+
writeAtomicJson(path.join(asyncDir, "status.json"), stoppedStatus);
|
|
100
|
+
updateActiveRunIndex(asyncDir, "stopped", status.toolCallId, { retryCapacityErrors: true, terminalIndexBeforeRelease: true });
|
|
101
|
+
}
|
|
102
|
+
|
|
24
103
|
export function stopAsyncRun(
|
|
25
104
|
state: SubagentState,
|
|
26
105
|
runId: string | undefined,
|
|
@@ -38,7 +117,8 @@ export function stopAsyncRun(
|
|
|
38
117
|
details: { mode: "management", results: [] },
|
|
39
118
|
};
|
|
40
119
|
}
|
|
41
|
-
|
|
120
|
+
const pausedWholeRun = status?.state === "paused" && childId === undefined;
|
|
121
|
+
if (!status || (status.state !== "running" && status.state !== "queued" && !pausedWholeRun)) {
|
|
42
122
|
return {
|
|
43
123
|
content: [{ type: "text", text: `No running or queued async run was found for '${runId ?? "current"}'.` }],
|
|
44
124
|
isError: true,
|
|
@@ -66,13 +146,23 @@ export function stopAsyncRun(
|
|
|
66
146
|
}
|
|
67
147
|
try {
|
|
68
148
|
deliverStopRequest({ asyncDir: target.asyncDir, pid: typeof status.pid === "number" ? status.pid : undefined, kill, source: "stop-action", targetIndex: child?.index, childId: child?.id ?? childId });
|
|
149
|
+
if (pausedWholeRun) {
|
|
150
|
+
const failure = sealPausedRun(target.asyncDir, status);
|
|
151
|
+
if (failure) {
|
|
152
|
+
return {
|
|
153
|
+
content: [{ type: "text", text: `Stop request persisted for paused async run ${target.asyncId}, but terminal proof is not ready (${failure}). Retry stop after runner shutdown is observed.` }],
|
|
154
|
+
isError: true,
|
|
155
|
+
details: { mode: "management", results: [] },
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
69
159
|
const tracked = state.asyncJobs.get(target.asyncId);
|
|
70
160
|
if (tracked) {
|
|
71
161
|
tracked.activityState = undefined;
|
|
72
162
|
tracked.updatedAt = Date.now();
|
|
73
163
|
}
|
|
74
164
|
return {
|
|
75
|
-
content: [{ type: "text", text: child ? `Stop requested for child ${child.id} in async run ${target.asyncId}.` : `Stop requested for async run ${target.asyncId}.` }],
|
|
165
|
+
content: [{ type: "text", text: pausedWholeRun ? `Stopped paused async run ${target.asyncId}.` : child ? `Stop requested for child ${child.id} in async run ${target.asyncId}.` : `Stop requested for async run ${target.asyncId}.` }],
|
|
76
166
|
details: { mode: "management", results: [] },
|
|
77
167
|
};
|
|
78
168
|
} catch (error) {
|