pi-subagents 0.65.1 → 0.67.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 +123 -0
- package/README.md +5 -4
- package/agents/evidence-auditor.md +34 -0
- package/agents/researcher.md +23 -13
- package/agents/reviewer.md +3 -2
- package/docs/agents.md +20 -3
- package/docs/configuration.md +25 -5
- package/docs/extension-api.md +124 -18
- package/docs/missions.md +8 -0
- package/docs/models.md +59 -2
- package/docs/observability.md +46 -6
- package/docs/standalone-background.md +49 -0
- package/docs/tool-reference.md +20 -10
- package/docs/watchdog.md +35 -4
- package/docs/workflows.md +40 -19
- package/inspector-runner.mjs +2 -2
- package/package.json +2 -1
- package/prompts/parallel-review.md +1 -1
- package/{runner-server-preload.mjs → runner-peer-preload.mjs} +8 -3
- package/skills/pi-subagents/SKILL.md +14 -0
- package/skills/pi-subagents/references/execution-controls.md +20 -5
- package/skills/pi-subagents/references/management-authoring-rpc.md +2 -1
- package/skills/pi-subagents/references/prompting-and-roles.md +2 -2
- package/src/agents/advertised-agent-prompt.ts +94 -0
- package/src/agents/agent-management.ts +14 -1
- package/src/agents/agent-serializer.ts +2 -0
- package/src/agents/agents.ts +14 -0
- package/src/agents/builtin-names.ts +1 -0
- package/src/api/delegation.ts +4 -0
- package/src/api/preflight.ts +76 -45
- package/src/api/shared-types.ts +3 -1
- package/src/api/workflow-resources.ts +6 -0
- package/src/extension/fanout-child.ts +63 -4
- package/src/extension/index.ts +58 -8
- package/src/extension/public-execution.ts +4 -3
- package/src/extension/rpc.ts +8 -21
- package/src/extension/schemas.ts +71 -80
- package/src/extension/tool-description.ts +29 -81
- 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 +104 -67
- package/src/runs/background/active-async-capacity.ts +22 -18
- package/src/runs/background/async-execution.ts +45 -56
- package/src/runs/background/async-job-tracker.ts +35 -3
- package/src/runs/background/async-resume.ts +5 -9
- package/src/runs/background/async-status-snapshot.ts +10 -12
- package/src/runs/background/async-status.ts +17 -9
- package/src/runs/background/auto-drain.ts +44 -30
- package/src/runs/background/binary-bootstrap.ts +33 -0
- package/src/runs/background/chain-root-attachment.ts +8 -0
- package/src/runs/background/control-channel.ts +78 -44
- package/src/runs/background/fleet-view.ts +30 -2
- package/src/runs/background/notify.ts +117 -13
- package/src/runs/background/owned-process-tree.ts +35 -8
- package/src/runs/background/process-terminal.ts +23 -23
- package/src/runs/background/run-child-session.ts +121 -36
- package/src/runs/background/run-status.ts +78 -5
- package/src/runs/background/runner-aliases.ts +28 -9
- package/src/runs/background/runner-child-launch.ts +88 -0
- package/src/runs/background/runner-child-sessions.ts +5 -4
- package/src/runs/background/scheduled-runs.ts +40 -13
- package/src/runs/background/stale-run-reconciler.ts +3 -1
- package/src/runs/background/steering.ts +20 -2
- package/src/runs/background/subagent-runner.ts +458 -239
- package/src/runs/background/subagent-wait.ts +54 -8
- package/src/runs/background/wait-completions.ts +4 -0
- package/src/runs/background/wait-tool.ts +1 -1
- package/src/runs/foreground/async-steering-action.ts +37 -7
- package/src/runs/foreground/execution.ts +145 -56
- package/src/runs/foreground/prompt-audit.ts +3 -1
- package/src/runs/foreground/subagent-executor.ts +584 -297
- package/src/runs/foreground/workflow-detach-reconcile.ts +10 -5
- package/src/runs/foreground/workflow-foreground-steering.ts +57 -2
- package/src/runs/shared/acceptance.ts +7 -4
- package/src/runs/shared/agent-contract.ts +1 -1
- package/src/runs/shared/async-status-projection.ts +51 -47
- package/src/runs/shared/capability-ceiling.ts +2 -0
- package/src/runs/shared/child-hooks.ts +167 -3
- package/src/runs/shared/child-launch.ts +28 -13
- package/src/runs/shared/child-lifecycle.ts +6 -3
- package/src/runs/shared/child-runtime-config.ts +3 -1
- package/src/runs/shared/child-session.ts +75 -8
- package/src/runs/shared/child-tool-plan.ts +124 -5
- package/src/runs/shared/completion-evidence.ts +2 -2
- package/src/runs/shared/completion-guard.ts +6 -3
- package/src/runs/shared/effective-system-prompt.ts +33 -0
- package/src/runs/shared/external-cli-runner.ts +9 -7
- package/src/runs/shared/host-step-status.ts +11 -11
- package/src/runs/shared/llm-intent-arbiter.ts +21 -11
- package/src/runs/shared/model-fallback.ts +12 -6
- package/src/runs/shared/nested-events.ts +5 -5
- package/src/runs/shared/orca-progress-tabs.ts +7 -1
- package/src/runs/shared/parallel-handoff.ts +57 -12
- package/src/runs/shared/parallel-utils.ts +2 -2
- package/src/runs/shared/pi-spawn.ts +10 -0
- package/src/runs/shared/readonly-drain-observation.ts +42 -0
- package/src/runs/shared/readonly-model-continuation.ts +69 -0
- package/src/runs/shared/readonly-session-evidence.ts +307 -0
- package/src/runs/shared/run-fanout-budget.ts +8 -8
- package/src/runs/shared/runtime-acknowledged-extensions.ts +3 -3
- package/src/runs/shared/subagent-prompt-runtime.ts +20 -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 +190 -0
- package/src/runs/shared/worktree.ts +366 -208
- package/src/shared/fork-context.ts +15 -72
- package/src/shared/launch-contract.ts +65 -2
- package/src/shared/opencode-session-headers.ts +30 -0
- package/src/shared/types.ts +85 -61
- package/src/shared/utils.ts +7 -2
- package/src/shared/workflow-child-permit.ts +18 -13
- package/src/slash/delegation-adapters.ts +3 -1
- package/src/slash/delegation-request.ts +14 -0
- package/src/slash/slash-commands.ts +2 -1
- package/src/slash/subagents-admin.ts +11 -4
- package/src/tui/fleet-status.ts +164 -19
- package/src/tui/fleet.ts +27 -19
- package/src/tui/render.ts +172 -33
- package/src/watchdog/child-status.ts +8 -0
- package/src/watchdog/model-selection.ts +20 -0
- package/src/watchdog/permission-arbiter.ts +3 -1
- package/src/watchdog/register-child.ts +1 -0
- package/src/watchdog/register-main.ts +31 -27
- package/src/watchdog/review.ts +132 -67
- package/src/watchdog/runtime.ts +82 -20
- package/src/watchdog/scope.ts +1 -1
- package/src/watchdog/settings.ts +9 -3
- package/src/watchdog/tool-actions.ts +13 -12
- package/src/watchdog/turn-delta.ts +23 -0
- package/src/watchdog/types.ts +4 -0
- package/src/workflows/chat-progress.ts +3 -3
- package/src/workflows/scripted-workflow.ts +275 -17
- package/src/workflows/workflow-checklist.ts +13 -17
- package/src/workflows/workflow-child-summary.ts +57 -8
- package/src/workflows/workflow-preflight.ts +19 -19
- package/src/workflows/workflow-receipt.ts +3 -3
- package/src/workflows/workflow-resources.ts +96 -21
- package/src/workflows/workflow-settlement.ts +3 -0
- /package/src/inspectors/{herdr/shell-command.ts → shell-command.ts} +0 -0
package/docs/extension-api.md
CHANGED
|
@@ -2,6 +2,97 @@
|
|
|
2
2
|
|
|
3
3
|
Public seams for other Pi extensions and host integrations: the in-process RPC, the structured delegation API, launch preflight, capability ceilings, the background-work provider contract, and the Herdr integration.
|
|
4
4
|
|
|
5
|
+
## Trusted workflow resources
|
|
6
|
+
|
|
7
|
+
Loaded trusted TypeScript extensions can import `registerWorkflowResource` from `pi-subagents/workflow-resources`. This subpath does not load the main extension and exposes no resolver or permit constructor. Its exported types are `RegisterWorkflowResourceInput`, `WorkflowResourceDefinition`, and `WorkflowResourceRegistration`:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
registerWorkflowResource({
|
|
11
|
+
sessionId: string,
|
|
12
|
+
definition: {
|
|
13
|
+
name: string,
|
|
14
|
+
version: number,
|
|
15
|
+
resolve(args: Readonly<Record<string, unknown>>):
|
|
16
|
+
| { script: string; hostCommands?: readonly { key: string; command: string }[] }
|
|
17
|
+
| { error: string },
|
|
18
|
+
},
|
|
19
|
+
}): { dispose(): void }
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Names are case-sensitive, at most 128 characters, and match `[A-Za-z0-9][A-Za-z0-9._-]*`; use an extension prefix. Versions are positive safe integers. Registration throws for invalid input, protected builtins (`review`, `run-ci`), or duplicate names within the same session. Different sessions may register the same name. Dispose before replacement; there is no silent overwrite.
|
|
23
|
+
|
|
24
|
+
Register in `session_start` using **`ctx.sessionManager.getSessionId()`**, not the session file path or a tool argument. Dispose in `session_shutdown`. New/resumed/forked sessions and reloads need registration from the replacement runtime's `session_start`; do not retain old `pi`/`ctx` references. The extension owns cleanup, not an automatic registration lifecycle manager. Disposal is idempotent and cannot remove a newer replacement. Missing cleanup can cause a duplicate-registration failure on reload.
|
|
25
|
+
|
|
26
|
+
`resolve` must do synchronous, bounded validation and string construction, without I/O, SDK calls, timers or process work. Core deep-copies plain JSON args: at most 16 KiB encoded, nesting depth 8, 16 fields per object, 64 items per array, finite numbers, and nonempty strings of at most 16 KiB. The extension must additionally reject unsupported fields and validate resource-specific semantics. Throws, promises/thenables and malformed expansions fail before authority is issued; errors are bounded to 4096 characters.
|
|
27
|
+
|
|
28
|
+
Host grants bind **exact key/trimmed-command pairs**, not independent sets of keys and commands. At most 32 grants are accepted, with unique safe workflow keys and nonempty commands bounded to 16 KiB without NUL. Omitted grants give no host authority. Core snapshots the expansion and grants at resolution. Disposing stops future lookup, but already-admitted workflows retain captured grants, even after replacement. Use existing stop/deadline controls for cancellation; this does not promise survival of host shutdown or durable named scheduling. Existing child admission and capability ceilings still apply.
|
|
29
|
+
|
|
30
|
+
### Mixed child and finite host example
|
|
31
|
+
|
|
32
|
+
This extension owns two fixed commands; `scripts/finite-check.mjs` must be an existing trusted finite helper in the workflow cwd. It runs a reviewer first, then a check. No command or flags come from free-form public args.
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
36
|
+
import { registerWorkflowResource } from "pi-subagents/workflow-resources";
|
|
37
|
+
|
|
38
|
+
export default function (pi: ExtensionAPI) {
|
|
39
|
+
let registration: { dispose(): void } | undefined;
|
|
40
|
+
pi.on("session_start", (_event, ctx) => {
|
|
41
|
+
registration?.dispose();
|
|
42
|
+
registration = registerWorkflowResource({
|
|
43
|
+
sessionId: ctx.sessionManager.getSessionId(),
|
|
44
|
+
definition: {
|
|
45
|
+
name: "acme.review-check",
|
|
46
|
+
version: 1,
|
|
47
|
+
resolve(args) {
|
|
48
|
+
if (Object.keys(args).some(k => k !== "task" && k !== "check"))
|
|
49
|
+
return { error: "Only task and check are supported." };
|
|
50
|
+
if (typeof args.task !== "string" || !args.task.trim() || args.task.length > 4000)
|
|
51
|
+
return { error: "task must contain 1–4000 characters." };
|
|
52
|
+
if (args.check !== "quick" && args.check !== "full")
|
|
53
|
+
return { error: "check must be quick or full." };
|
|
54
|
+
const command = args.check === "quick"
|
|
55
|
+
? "node ./scripts/finite-check.mjs --mode quick"
|
|
56
|
+
: "node ./scripts/finite-check.mjs --mode full";
|
|
57
|
+
const host = { kind: "command", command, timeoutMs: 120000 };
|
|
58
|
+
return {
|
|
59
|
+
hostCommands: [{ key: "check", command }],
|
|
60
|
+
script: `
|
|
61
|
+
const review = await runs.run("review", {
|
|
62
|
+
agent: "reviewer", task: ${JSON.stringify(args.task)}
|
|
63
|
+
});
|
|
64
|
+
if (!review.ok) throw new Error("Review child failed");
|
|
65
|
+
const check = await runs.host("check", ${JSON.stringify(host)});
|
|
66
|
+
return { review: review.output, check };
|
|
67
|
+
`,
|
|
68
|
+
};
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
pi.on("session_shutdown", () => {
|
|
74
|
+
registration?.dispose();
|
|
75
|
+
registration = undefined;
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Invoke through the public `subagent` tool (use `async: false` for foreground):
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"workflow": "acme.review-check",
|
|
85
|
+
"args": { "task": "Review the current change; return findings only.", "check": "quick" },
|
|
86
|
+
"async": true
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The parent evaluates child findings and ordinary command logs/status/terminal receipts; child success is not approval or proof of a clean review. The timeout above bounds the host command, not the whole workflow.
|
|
91
|
+
|
|
92
|
+
**Trust boundary:** this API composes already-loaded trusted code; it is neither authentication nor a sandbox. Session IDs scope lookup, not authorization between malicious extensions. Core owns opaque permits and provenance; caller-supplied issuer/trust/permit metadata cannot grant authority. Raw public scripts and script paths do not gain host authority, and registration is not an arbitrary-command entry point for public args.
|
|
93
|
+
|
|
94
|
+
The existing shell runner uses workflow cwd and inherited environment. Exact matching does not pin PATH resolution, executable bytes, repository helpers, or credentials. Those remain operator/extension trust responsibilities. `JSON.stringify` embeds data in JavaScript source; **it is not shell escaping**. Keep commands fixed as above, or validate strictly bounded numeric/hex tokens before binding known positions; never concatenate arbitrary task text or flags into shell commands. No new runner, cwd confinement, CI/merge policy, or SDK lifecycle framework is provided.
|
|
95
|
+
|
|
5
96
|
## In-process event-bus RPC
|
|
6
97
|
|
|
7
98
|
Other Pi extensions can use the in-process event-bus RPC instead of scraping slash output or calling internal modules. Listen for `subagents:rpc:v1:ready`, send requests on `subagents:rpc:v1:request`, and read replies from `subagents:rpc:v1:reply:<requestId>`.
|
|
@@ -135,7 +226,7 @@ unregisterExternalRun(ctx.sessionManager.getSessionId(), "dependency-review");
|
|
|
135
226
|
|
|
136
227
|
The API validates and caches bounded display fields when the caller registers or updates a job. FleetView reads that cache only. It does not poll caller code. `snapshotExternalRuns(sessionId)` and `listExternalRuns(sessionId)` return bounded current-session snapshots. Snapshots filter the session-qualified cache key before inspecting record fields; API-written records avoid repeated normalization through module-private provenance, while records replaced or mutated through the process-local registry are validated on demand. By default, malformed records for the requested session throw with the validation error. Display-only Fleet callers can pass `{ ignoreMalformed: true, onMalformedRecord }` to remove bad records and keep rendering with a programmatic diagnostic.
|
|
137
228
|
|
|
138
|
-
External jobs are observational. The caller owns execution, persistence, cancellation, and result delivery. FleetView does not expose stop, steer, resume, cancel, or
|
|
229
|
+
External jobs are observational. The caller owns execution, persistence, cancellation, and result delivery. FleetView does not expose stop, steer, resume, cancel, or inspector controls for them. Supplied report and transcript paths are shown as bounded text only; FleetView does not read arbitrary external paths.
|
|
139
230
|
|
|
140
231
|
## Launch contract preflight
|
|
141
232
|
|
|
@@ -155,7 +246,8 @@ const result = await resolveSubagentLaunchContract({
|
|
|
155
246
|
|
|
156
247
|
if (!result.ok) {
|
|
157
248
|
// missing_agent, ambiguous_agent, missing_skill, denied_required_tool,
|
|
158
|
-
// invalid_artifact_dir, invalid_cwd,
|
|
249
|
+
// invalid_artifact_dir, invalid_cwd, unsupported_mode, restricted_agent,
|
|
250
|
+
// thinking_ceiling, invalid_extension_bindings, or invalid_intercom_bridge
|
|
159
251
|
throw new Error(result.message);
|
|
160
252
|
}
|
|
161
253
|
|
|
@@ -165,11 +257,17 @@ console.log(result.contract.digest, result.contract.tools.effectiveAllowlist);
|
|
|
165
257
|
Preflight covers ordinary single-agent launch resolution:
|
|
166
258
|
|
|
167
259
|
- Selected agent identity and shadowed candidates.
|
|
168
|
-
- A parsed-definition digest, including system prompt and launch-affecting model, tool, skill, extension, output, and memory fields.
|
|
260
|
+
- A parsed-definition digest, including system prompt and launch-affecting model, tool, skill, extension, output, and memory fields. Runtime overlays such as the Intercom bridge never change it.
|
|
169
261
|
- Fresh/fork context, effective model and thinking, skill and tool resolution, direct MCP selections, runtime/configured extensions.
|
|
262
|
+
- The resolved Intercom bridge state (`intercomBridge.mode` and `intercomBridge.active`). An active bridge appends the bridge instruction to the child prompt and adds `contact_supervisor` to a declared tool list, exactly as execution does.
|
|
170
263
|
- Artifact/session paths, async lifecycle/status/result/event/process-terminal paths, package/lifecycle versions, capability-ceiling audit data, and stable digests.
|
|
171
264
|
|
|
172
|
-
`launchContractDigest` is the canonical digest of the caller task, effective system prompt, model candidates, effective tools/extensions/MCP (including inherited capability ceilings), output binding, and structured-output schema that ordinary foreground and async execution report in results/status/events and metadata.
|
|
265
|
+
`launchContractDigest` is the canonical digest of the caller task, effective system prompt (including an active bridge instruction), model candidates, effective tools/extensions/MCP (including inherited capability ceilings and the bridge tool), output binding, and structured-output schema that ordinary foreground and async execution report in results/status/events and metadata. Preflight and each execution path that reports the digest assemble it through one shared binding, so equal inputs produce equal digests.
|
|
266
|
+
|
|
267
|
+
Bridge inputs:
|
|
268
|
+
|
|
269
|
+
- `intercomBridge` replaces the global `intercomBridge` config for this launch, with the same semantics as the `subagent` tool and delegation overrides. Pass the same value to the launch you compare against. Preflight reads the global config from disk on each call while the running extension keeps the config it loaded at startup, so pass the override when the digest must not depend on that file.
|
|
270
|
+
- The default bridge instruction never names the parent session, so most hosts need no further input. When the configured `instructionFile` interpolates `{orchestratorTarget}`, preflight reports a `host_required` diagnostic unless the host supplies a non-empty `orchestratorTarget`; the executor derives that target with `resolveIntercomSessionTarget` from `pi-subagents/intercom-bridge`, given the parent session name and id.
|
|
173
271
|
|
|
174
272
|
Boundaries:
|
|
175
273
|
|
|
@@ -239,6 +337,7 @@ Bounds:
|
|
|
239
337
|
|
|
240
338
|
- Schemas are capped at 64 KiB; tasks and returned text/structured values are capped at 1 MiB, with smaller bounds on identity/configuration strings and a maximum `timeoutMs` of 2,147,483,647.
|
|
241
339
|
- Structured delegation accepts `toolBudget: { hard: 0, block: "*" }` to block the first tool call and run a zero-tool leaf; ordinary model-facing/configured budgets keep their existing minimum of one.
|
|
340
|
+
- `intercomBridge` optionally replaces the global bridge config for one delegation, for example `{ mode: "off" }` when no supervisor session will answer the child. Pass the same value to `resolveSubagentLaunchContract` to compare `launchContractDigest` against the terminal response.
|
|
242
341
|
- The foreground bridge retains up to 8,192 exact pending-cancellation and settled-attempt identities per extension context. If either history fills, it fails closed with `unavailable_context` for later starts rather than evicting identity facts; lifecycle reset clears the bounded history.
|
|
243
342
|
|
|
244
343
|
Constraints:
|
|
@@ -336,6 +435,27 @@ The provider returns handles with `providerJobId`, `state`, optional `handleUrl`
|
|
|
336
435
|
|
|
337
436
|
The async runner process does not import provider internals. It writes operation requests into its async run directory. The parent Pi process services those requests against the registered provider and writes operation responses. If the provider is not registered, the bridge fails closed with an actionable error. If a run is recovered after provider job metadata exists, the runner calls `reattach` and `result`; it does not call `start` or `follow-up` again.
|
|
338
437
|
|
|
438
|
+
## Inspect integration
|
|
439
|
+
|
|
440
|
+
Inspect is the portable command and action surface for an existing async run. The public actions are:
|
|
441
|
+
|
|
442
|
+
```ts
|
|
443
|
+
subagent({ action: "inspector.command", id: "<run-id>", index: 0 })
|
|
444
|
+
subagent({ action: "inspector.open", id: "<run-id>", index: 0, focus: true })
|
|
445
|
+
subagent({ action: "inspector.status", id: "<run-id>", index: 0 })
|
|
446
|
+
subagent({ action: "inspector.close", id: "<run-id>", index: 0 })
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
`inspector.command` returns a standalone runner command without contacting a host or writing a binding. `inspector.open` selects an available bundled inspector plugin. `status` and `close` select the plugin that owns the run binding and report clearly when that plugin does not support the requested lifecycle action. Without an available plugin, `open` fails closed with an actionable message; ordinary launches remain headless. Closing an inspector never stops the run.
|
|
450
|
+
|
|
451
|
+
### Herdr inspector plugin
|
|
452
|
+
|
|
453
|
+
The bundled Herdr inspector plugin supports Herdr 0.7.5+. It opens a raw dashboard pane, not the child session and not a literal attach. It reads lifecycle, status, output, and mission artifacts; steer and stop continue through pi-subagents' existing control inbox. Use `focus` only with `inspector.open`; Herdr 0.7.5 cannot focus an arbitrary existing raw pane id.
|
|
454
|
+
|
|
455
|
+
### Ghostty inspector plugin
|
|
456
|
+
|
|
457
|
+
Ghostty 1.3+ on macOS is the second bundled open-only plugin, using Ghostty's preview AppleScript API. It splits the focused terminal and launches the read-only inspector command; status and close are unavailable because it writes no binding. Ghostty Automation permission is required.
|
|
458
|
+
|
|
339
459
|
## Herdr integration
|
|
340
460
|
|
|
341
461
|
When Pi runs inside [Herdr](https://herdr.dev), pi-subagents automatically reports active async-run counts through Herdr pane metadata.
|
|
@@ -355,20 +475,6 @@ rows = [
|
|
|
355
475
|
]
|
|
356
476
|
```
|
|
357
477
|
|
|
358
|
-
### Inspector panes
|
|
359
|
-
|
|
360
|
-
Herdr 0.7.5+ can open an on-demand inspector for an existing async run:
|
|
361
|
-
|
|
362
|
-
```ts
|
|
363
|
-
subagent({ action: "inspector.open", id: "<run-id>", index: 0, focus: true })
|
|
364
|
-
subagent({ action: "inspector.status", id: "<run-id>", index: 0 })
|
|
365
|
-
subagent({ action: "inspector.close", id: "<run-id>", index: 0 })
|
|
366
|
-
```
|
|
367
|
-
|
|
368
|
-
The inspector is a raw dashboard pane, not the child session and not a literal attach. It reads lifecycle/status/output/mission artifacts and sends `steer` or `stop` through pi-subagents' existing control inbox. Closing it never stops the run.
|
|
369
|
-
|
|
370
|
-
Herdr remains optional. Ordinary launches stay headless, and missing/older Herdr versions affect only Herdr-specific inspector and project-pane actions. FleetView opens the selected active async child with `H`. Use `focus` only with `inspector.open`; Herdr 0.7.5 cannot focus an arbitrary existing raw pane id.
|
|
371
|
-
|
|
372
478
|
### Project panes
|
|
373
479
|
|
|
374
480
|
For substantial work in another codebase, Herdr 0.7.5+ can open a project-owned Pi pane rooted in that repository:
|
package/docs/missions.md
CHANGED
|
@@ -108,6 +108,12 @@ subagent({ action: "schedule.create", id: "backlog", every: "6h", catchUp: "late
|
|
|
108
108
|
|
|
109
109
|
Fixed intervals support `m`, `h`, `d`, and `w` units and advance from the planned time without completion drift.
|
|
110
110
|
|
|
111
|
+
Create a quiet recurring workflow whose successful completions stay visible but do not wake the parent session:
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
subagent({ action: "schedule.create", id: "nightly-sweep", every: "24h", quiet: true, workflowScript: "..." })
|
|
115
|
+
```
|
|
116
|
+
|
|
111
117
|
Manage schedules with `schedule.list`, `schedule.show`, `schedule.history`, `schedule.pause`, `schedule.resume`, `schedule.run`, `schedule.run-due`, and `schedule.delete`.
|
|
112
118
|
|
|
113
119
|
Behavior:
|
|
@@ -116,6 +122,8 @@ Behavior:
|
|
|
116
122
|
- An optional top-level `baseRef` selects the safe Git ref used by managed worktrees (default `HEAD`); it is persisted with the schedule and forwarded on every fire. The source checkout must still be clean.
|
|
117
123
|
- Definitions, bounded history, append-only events, and per-run receipts are stored with mode `0600`.
|
|
118
124
|
- `overlap` is currently fixed to `skip`; `catchUp` supports `latest` (default) and `none`.
|
|
125
|
+
- A successful `schedule.run` satisfies the next natural fire; a failed manual launch does not skip it.
|
|
126
|
+
- `quiet` persists only on recurring (`every`) schedules. Successful automatic fires stay visible without a parent turn; failed, stopped, or paused outcomes still wake the session. One-shot `at` schedules and `schedule.run` stay noisy unless that launch passes `quiet: true`.
|
|
119
127
|
- `schedule.run-due` lets an external launcher start due project work without making `pi-subagents` a daemon.
|
|
120
128
|
- Calendar recurrence, cron, queue/replace overlap, and the schedule TUI inspector are intentionally deferred to the next slice.
|
|
121
129
|
- The old `schedule`, `schedule-list`, `schedule-status`, and `schedule-cancel` actions were removed in a hard cutover.
|
package/docs/models.md
CHANGED
|
@@ -100,7 +100,11 @@ A setup that works well in practice: route agents by task shape instead of runni
|
|
|
100
100
|
|
|
101
101
|
The routing rule: use the capability tiers (1–3) when the task is well-scoped, and the intent tier (4) when scoping or judging is the task itself.
|
|
102
102
|
|
|
103
|
-
Give tier-4 agents
|
|
103
|
+
Give tier-4 agents `fallbackModels` for retryable provider/model failures such as rate-limit, overload, unavailable-model, and provider-reported timeout errors **before any tool activity**. After tool activity, failures remain terminal except for the narrow native read-only HTTP 429 continuation below; the task is never automatically replayed after tool work. Ordinary task failures and the outer run-level `timeoutMs` / `maxRuntimeMs` deadline do not trigger fallback.
|
|
104
|
+
|
|
105
|
+
Fallback uses native Pi sessions, not fresh `pi` CLI processes. Even when an exact session file is reopened, normal fallback resubmits the original task; retained history alone does not make automatic continuation after tool work safe.
|
|
106
|
+
|
|
107
|
+
Example fallback configuration:
|
|
104
108
|
|
|
105
109
|
```yaml
|
|
106
110
|
---
|
|
@@ -112,7 +116,60 @@ fallbackModels: openai-codex/gpt-5.5:high
|
|
|
112
116
|
---
|
|
113
117
|
```
|
|
114
118
|
|
|
115
|
-
One interaction worth knowing for tier 4: forked context over an Anthropic parent transcript
|
|
119
|
+
One interaction worth knowing for tier 4: forked context over an Anthropic parent transcript strips the parent's signed thinking blocks from the child session, because a thinking signature cannot be replayed into a branch. The child still runs at its requested thinking level and reasons fresh from its first turn.
|
|
120
|
+
|
|
121
|
+
### Native read-only continuation after HTTP 429
|
|
122
|
+
|
|
123
|
+
A native foreground or background child can continue once on an eligible later `fallbackModels` entry after completed read-only tool work and an observed HTTP 429. This is not general mid-run fallback and does not apply to external runners. Current coverage is Pi SDK **0.85.1**, the configured **`baseten` / `openai-completions`** provider and its observed request path, not arbitrary providers, APIs, provider extensions, or error text containing “429”.
|
|
124
|
+
|
|
125
|
+
Admission requires the default child factory's owned profile: an explicit allowlist containing only builtin `read` and/or `ls`, no ambient or custom extensions/tools or registered background-work providers, and verified idle settlement and shutdown. Wait, supervisor coordination, nested/fanout work, permissions/watchdogs, structured output, fast mode and configured tool budgets exclude this continuation on both hosts. A read-only role name or prompt alone is not enough; default coordinated profiles are excluded.
|
|
126
|
+
|
|
127
|
+
Usage-budget admission differs by host:
|
|
128
|
+
|
|
129
|
+
- **Foreground:** any configured usage budget, including a workflow-owned budget, denies continuation because this host does not certify remaining allowance.
|
|
130
|
+
- **Native background:** an unexhausted token-only budget can qualify only when the run owner's authoritative ledger has received the current attempt's events and has complete coverage, including concurrent work. Configured cost budgets, missing/unknown usage, or unsupported external/import/dynamic coverage deny continuation. This does not introduce new accounting or renew allowances.
|
|
131
|
+
|
|
132
|
+
The child must have an **exact assigned session file**: either valid persisted history or an initially absent assigned file that the SDK initializes and persists during this attempt. In-memory or directory-only storage is insufficient. A missing or changed checkpoint at handoff fails closed; recovery never repairs it or promotes storage. Normal executor launches assign the child file and pass it to the native host; lower-level directory-only launches remain ineligible. No new storage option is needed.
|
|
133
|
+
|
|
134
|
+
The next model must resolve through the same configured provider runtime, have the same provider/API and a different, untried model identity, and pass conservative retained-input compatibility checks. Cross-provider candidates are skipped without launch; unknown resolution or unsupported/unknown capacity denies continuation. Both hosts reject images and unknown content; these are conservative checks, not exact token estimates:
|
|
135
|
+
|
|
136
|
+
- **Foreground:** accepts text and supported assistant tool-call/result history. Its UTF-8 byte ceiling includes retained history, actual system prompt and tool definitions, 4096 bytes of framing/continuation headroom, and the candidate's full output allowance. Equal-window models can qualify if this bound fits.
|
|
137
|
+
- **Native background:** resolves exact registry identities and accepts retained text, thinking and tool-call blocks. It reserves the entire source context window plus retained-context UTF-8 bytes and fixed-prompt bytes, and requires the candidate's positive output allowance to be no larger than the source's. Equal/smaller context windows therefore deny continuation; choose a sufficiently larger same-provider sibling.
|
|
138
|
+
|
|
139
|
+
The sibling reopens the **same session/file**, preserving the original task, completed tool results and terminal provider error. Its new prompt is a fixed instruction to continue from those results without restarting or repeating completed work; it does not resubmit the original task. One recovery allowance is shared with compaction-abort recovery and consumed before sibling creation. Any sibling outcome ends recovery, including startup failure, abort or another 429; it cannot cascade into startup fallback or change model exclusions. Cancellation, stop/detach and the original run deadline remain authoritative and are rechecked at handoff. Newly billed attempt usage is aggregated, not historical usage restored from the file.
|
|
140
|
+
|
|
141
|
+
For a deliberately non-coordinated reader, merge these existing keys into `~/.pi/agent/extensions/subagent/config.json` (see [configuration.md](configuration.md)):
|
|
142
|
+
|
|
143
|
+
```json
|
|
144
|
+
{
|
|
145
|
+
"waitTool": { "enabled": false },
|
|
146
|
+
"intercomBridge": { "mode": "off" }
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
These settings affect other children too; do not disable required coordination just to obtain recovery. Define a custom agent using existing frontmatter (replace `model-a` and `model-b` with actual text-capable models in your configured Baseten catalog):
|
|
151
|
+
|
|
152
|
+
```yaml
|
|
153
|
+
---
|
|
154
|
+
name: reader
|
|
155
|
+
description: Read-only file analysis without coordination
|
|
156
|
+
tools: read, ls
|
|
157
|
+
extensions:
|
|
158
|
+
model: baseten/model-a
|
|
159
|
+
fallbackModels: baseten/model-b
|
|
160
|
+
systemPromptMode: append
|
|
161
|
+
inheritProjectContext: false
|
|
162
|
+
inheritGlobalContext: false
|
|
163
|
+
inheritSkills: false
|
|
164
|
+
allowNestedSubagents: false
|
|
165
|
+
async: false
|
|
166
|
+
---
|
|
167
|
+
Read the assigned files and return your findings without editing.
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Launch with `subagent({ agent: "reader", task: "Read README.md and summarize it", async: false, context: "fresh", output: false })`. Keep `forceTopLevelAsync` disabled and omit tool/usage budgets and the excluded runtime features above. No new recovery flag is required: these settings make the profile eligible, but continuation still requires actual completed read-only work, observed 429 and all checkpoint/provider/lifecycle checks. This is a trusted-host compatibility boundary, not sandboxing or universal provider attestation.
|
|
171
|
+
|
|
172
|
+
For native background execution, use the same call with `async: true`, which overrides the agent's foreground default. Keep the explicit empty `extensions:` field: omitting it allows ambient extensions in background children and does not certify this profile. Select a fallback model satisfying the stricter background capacity bound above; unconfigured budgets are simplest, while token-only budgets still require the authoritative allowance check. Do not disable needed coordination or ambient capabilities merely to obtain continuation.
|
|
116
173
|
|
|
117
174
|
## Thinking level defaults
|
|
118
175
|
|
package/docs/observability.md
CHANGED
|
@@ -40,6 +40,26 @@ async subagent worker · background
|
|
|
40
40
|
|
|
41
41
|
To inspect one background child in text, use `subagent({ action: "status", id: "...", view: "transcript" })`; add `index` for a specific child in a parallel or chain run.
|
|
42
42
|
|
|
43
|
+
### Reducing status display noise
|
|
44
|
+
|
|
45
|
+
Chat records tool-call history; FleetView and the async widget show live run/child updates. Separate `subagent({ action: "status", id: "..." })` calls leave separate historical entries even when their `Status target: run …` labels match. A matching run ID identifies the queried run, not the tool call, and is not evidence of duplicate execution. Live Fleet/widget refreshes do not merge those entries.
|
|
46
|
+
|
|
47
|
+
For compact chat results with FleetView as the only live editor surface, merge these top-level keys into `~/.pi/agent/extensions/subagent/config.json` (not Pi's `settings.json` or a `subagents` object), then restart Pi:
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"inlineToolDisplay": "summary",
|
|
52
|
+
"fleetView": true,
|
|
53
|
+
"asyncWidget": false
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
- `inlineToolDisplay: "summary"` keeps one static result row per call, alongside its call heading. A completed status query is not proof that the queried child has finished.
|
|
58
|
+
- `fleetView: true` retains live progress. Open `/subagents-fleet` or press `Ctrl+Alt+F` for details instead of repeatedly requesting status just to watch progress. Pi's expand key does not expand summary results; keep `"rich"` if you want expandable inline output.
|
|
59
|
+
- `asyncWidget: false` hides only the additional under-editor async widget, leaving FleetView available. This configuration reduces visible surfaces; it does not guarantee ordering relative to other extensions.
|
|
60
|
+
|
|
61
|
+
Thanks to [DraconDev](https://github.com/DraconDev) for reporting the display noise and suggesting summary mode in [#1931](https://github.com/nicobailon/pi-subagents/issues/1931).
|
|
62
|
+
|
|
43
63
|
## FleetView
|
|
44
64
|
|
|
45
65
|
In the TUI, a persistent FleetView below the editor keeps active work visible as a compact summary. Set `fleetViewPlacement` to `"aboveEditor"` to move it above the editor.
|
|
@@ -58,9 +78,9 @@ After you expand it:
|
|
|
58
78
|
reviewer · running 38s · ↓ 1.1k window · 1.4k spent
|
|
59
79
|
```
|
|
60
80
|
|
|
61
|
-
When the focused editor is empty, press `↓` or `←` to expand the summary into `main` plus active children with agent name, state, elapsed time, and token usage. When providers report usage, `window` is the latest assistant turn's input plus cache-read tokens, while `spent` keeps the cumulative input-plus-output total. Old run artifacts without window data keep the existing token-total label. The compact line counts active current-session work and Herdr project panes. Then use `↑`/`↓` or `j`/`k` to select a child and `Enter` to open the Fleet lobby; press `Enter` or `H` there to open its child-specific
|
|
81
|
+
When the focused editor is empty, press `↓` or `←` to expand the summary into `main` plus active children with agent name, state, elapsed time, and token usage. When providers report usage, `window` is the latest assistant turn's input plus cache-read tokens, while `spent` keeps the cumulative input-plus-output total. Old run artifacts without window data keep the existing token-total label. The compact line counts active current-session work and Herdr project panes. Then use `↑`/`↓` or `j`/`k` to select a child and `Enter` to open the Fleet lobby; press `Enter` or `H` there to open its child-specific inspector through an available Inspect plugin. Printable navigation keys are never intercepted before activation.
|
|
62
82
|
|
|
63
|
-
FleetView
|
|
83
|
+
FleetView and the under-editor async widget are both enabled by default; set `asyncWidget: false` to keep only FleetView. Successful background completions stay quiet so inactive Pi tabs are not marked unread, while failed or paused completions still notify the originating session. Parallel runs show every active child independently. Chains with parallel groups keep their grouped shape in progress and results, so failed or paused agents stay visible next to completed ones. When a child is explicitly allowed to fan out with `tools: subagent` or `allowNestedSubagents: true`, its nested runs appear under that parent child in the main status tree instead of being hidden inside the child session.
|
|
64
84
|
|
|
65
85
|
## The fleet inspector
|
|
66
86
|
|
|
@@ -74,16 +94,16 @@ Default keys:
|
|
|
74
94
|
- `x`/`Ctrl+O` — toggle tool details
|
|
75
95
|
- `r` — refresh
|
|
76
96
|
- `Esc` — close
|
|
77
|
-
- `Enter` — open the selected inspectable async child
|
|
97
|
+
- `Enter` — open the selected inspectable async child through the available Inspect plugin
|
|
78
98
|
- `s` — compose an acknowledged message to a selected live async child; Tab cycles `steer`, `follow_up`, and `auto`
|
|
79
99
|
- `D` — stop a selected child's top-level async run after confirmation
|
|
80
|
-
- `H` — open the selected active async child
|
|
100
|
+
- `H` — open the selected active async child through the available Inspect plugin
|
|
81
101
|
|
|
82
102
|
Set `fleetKeybindings` in the extension config to replace inspector-level keys when a terminal intercepts keys such as `PgUp`, `PgDn`, `Home`, or `End`. Prompt modes keep fixed keys such as `Esc`, `Enter`, `Tab`, and stop-confirmation `Y`/`N`.
|
|
83
103
|
|
|
84
104
|
`Ctrl+Alt+F` opens the same inspector even while a foreground turn is active and slash input is queued.
|
|
85
105
|
|
|
86
|
-
Enter and `H` use the
|
|
106
|
+
Enter and `H` use the available Inspect plugin. On macOS with Ghostty 1.3+ (TERM_PROGRAM=ghostty), this includes the other bundled open-only plugin using Ghostty's preview AppleScript API; status and close are unavailable because no binding is written. In a child-specific inspector, type ordinary guidance and press Enter to send it through the acknowledged steer channel; `steer <message>`, `status`, and `stop` remain available as explicit controls. The bundled Herdr plugin uses Herdr 0.7.5+.
|
|
87
107
|
|
|
88
108
|
Without a TUI, `/subagents-fleet` retains the textual `subagent({ action: "status", view: "fleet" })` fallback, and mutations use explicit commands: run `/subagents-stop` and pick from the selector, or use `/subagents-stop <run-id>` / `subagent({ action: "stop", id: "..." })` when you already know the id.
|
|
89
109
|
|
|
@@ -203,7 +223,7 @@ The reported `runtimeAcknowledgedExtensions` projection is `{ version: 1, source
|
|
|
203
223
|
|
|
204
224
|
### Process-terminal proof
|
|
205
225
|
|
|
206
|
-
Lifecycle
|
|
226
|
+
Lifecycle artifacts include `process-terminal-candidate.json` (private runner evidence) and `process-terminal.json` (the public proof projection).
|
|
207
227
|
|
|
208
228
|
A proof is `observed` only after the live parent observes the exact detached runner's `close` event and any tracked canonical-session lease is free. Children run inside the runner process, so the candidate records no separate writer processes. If the observer is unavailable, the proof is `unknown`; do not infer process exit from `endedAt`, result-file existence, PID disappearance, or lease-directory absence.
|
|
209
229
|
|
|
@@ -213,6 +233,26 @@ The `subagent:process-terminal` event and RPC `ping.capabilities.processTerminal
|
|
|
213
233
|
|
|
214
234
|
Both launch paths subscribe to the child session's event stream directly; there is no stdout protocol. The `events.jsonl` artifact mirrors those events with `message_update` dropped, and the transcript records them with `message_update` projected the same way pi's JSON mode prints it. `agent_end.willRetry` defers completion until the child settles, and `agent_settled` is the terminal watermark; a child whose run does not settle shortly after its terminal event is aborted and finished without it.
|
|
215
235
|
|
|
236
|
+
### Completion notification diagnostics
|
|
237
|
+
|
|
238
|
+
For an instrumented parent session, enable Node's opt-in debug sink **before starting Pi**:
|
|
239
|
+
|
|
240
|
+
```sh
|
|
241
|
+
NODE_DEBUG=pi-subagents-notify pi 2>notification-debug.log
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
This writes bounded JSON records prefixed `PI-SUBAGENTS-NOTIFY <pid>:` to stderr, not run artifacts or chat. The capture also contains other stderr output; review it before sharing. Records contain only `reason`, sanitized `id`/`runId` (up to 128 characters each), and `source`; task/output text, paths, credentials, and exception bodies are not included.
|
|
245
|
+
|
|
246
|
+
- `disposed`, `missing_session`, `foreground_session_mismatch`, `not_owned`: delivery rejected by an existing guard.
|
|
247
|
+
- `emit_foreground_session_mismatch`, `emit_not_owned`: ownership/session recheck rejected emission.
|
|
248
|
+
- `intercom_delivered`, `deduped_ttl`: already acknowledged; no new message needed.
|
|
249
|
+
- `deduped_pending`: shares an in-flight delivery promise.
|
|
250
|
+
- `batch_deferred`: held for batching, **not lost**; look for a later emission or disposal record for the same run.
|
|
251
|
+
- `send_accepted`, `send_failed`: `sendMessage` returned or threw, respectively. Acceptance is not proof the model read the message; failures remain retryable.
|
|
252
|
+
- `dispose_pending`: notifier shutdown left held results unacknowledged for later delivery.
|
|
253
|
+
|
|
254
|
+
Without `NODE_DEBUG`, tracing only checks the debug-enabled flag: no identity sanitization/serialization, diagnostic buffering, or log I/O. Existing delivery guards, TTL, timers and batching are unchanged. Traces cover notifier decisions only, not discovery gaps; absence of a trace does not diagnose the original missing-notification symptom.
|
|
255
|
+
|
|
216
256
|
## Workflow and debug artifacts
|
|
217
257
|
|
|
218
258
|
Each scripted workflow stores runtime artifacts under a workflow artifact directory. The on-disk directory is still named `chain-runs` for compatibility. With the default `artifactDir: "session"` or with `"temp"`, it is user-scoped temp storage. With `artifactDir: "project"`, the root is `<cwd>/.pi/subagents/chain-runs/`:
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Standalone background execution
|
|
2
|
+
|
|
3
|
+
Supported standalone target: **official Pi 0.85.1, Linux x64**. Keep its adjacent release assets with the executable. Other versions, operating systems, architectures and packagers are not covered.
|
|
4
|
+
|
|
5
|
+
Pi's extension loader supplies its embedded SDK to `binary-bootstrap.ts`, which awaits the existing configured runner before exiting. Startup authorization, revival leases, controls, disposal and process-close observation remain shared with npm. Each independent run has its own host; native sessions inside that run share it. No per-session CLI protocol, runtime download/install, alternate SDK or foreground fallback is introduced. Npm Pi keeps its Node runner, peer aliases and detected npm `PI_PACKAGE_DIR` override (including refusal when no npm root exists).
|
|
6
|
+
|
|
7
|
+
Implementation and lifecycle fixtures derive from [@xz-dev](https://github.com/xz-dev)'s [PR #2049](https://github.com/nicobailon/pi-subagents/pull/2049), source commit `910807bfefcf9ee41d73fa25ec86dcd75ab8f4b2` (Xiangzhe, `xiangzhedev@gmail.com`). Integration retains the lifecycle contract and reduces commentary rather than removing its evidence gates.
|
|
8
|
+
|
|
9
|
+
## Official binary gate
|
|
10
|
+
|
|
11
|
+
On Linux x64 with Node, npm, tar and bubblewrap installed, provision dependencies and the checksum-pinned release separately from execution:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm ci --ignore-scripts
|
|
15
|
+
release_dir="$(mktemp -d)"
|
|
16
|
+
url="$(node -p 'require("./test/smoke/standalone-release.json").url')"
|
|
17
|
+
sha="$(node -p 'require("./test/smoke/standalone-release.json").archiveSha256')"
|
|
18
|
+
curl --fail --location --retry 3 "$url" --output "$release_dir/release.tar.gz"
|
|
19
|
+
printf '%s %s\n' "$sha" "$release_dir/release.tar.gz" | sha256sum --check -
|
|
20
|
+
tar -xzf "$release_dir/release.tar.gz" -C "$release_dir"
|
|
21
|
+
node test/smoke/standalone-matrix.mjs "$release_dir/pi/pi" "$(mktemp -d)/matrix"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The `official-standalone` CI job runs this gate. Both archive and executable hashes are pinned. Each of 18 modes gets a fresh stage with no filesystem core SDK/shim, empty installation caches and isolated network/PID namespaces. Bare-Bun SDK import must fail; accepted execution uses Pi's actual loader. Missing sandbox support fails rather than skips. Use disk-backed storage: retained stages can occupy several GiB.
|
|
25
|
+
|
|
26
|
+
The matrix covers public launch/notification, workflows, same-run concurrent sessions, parallel stop, targeted steer/interrupt, child/tool/run deadlines, missing bootstrap, post-spawn persistence/authorization failures, SDK initialization failure, malformed bootstrap input/EOF with an authorized positive control, and competing revival. The provider is deterministic, but SDK sessions, runner and public extension are real. Only startup-failure writes are faulted.
|
|
27
|
+
|
|
28
|
+
`matrix.json` records complete/partial results; `inputs.json` freezes source identities and every mode must use the same package hash. Inspect per-mode logs, `identity.json`, lifecycle/notification evidence, `status.json` and `process-terminal.json`. A persisted result is not exit proof: the gate separately awaits observed close, verifies dead PIDs before sandbox teardown and checks session shutdown/lease release. CI retains receipts and at most 32 MiB compressed lifecycle evidence. Contributor-head passes do not establish acceptance for a different integration snapshot.
|
|
29
|
+
|
|
30
|
+
For a focused diagnostic, use `node test/smoke/standalone-background.mjs "$release_dir/pi/pi" "$(mktemp -d)/check" bootstrap-errors` (or another matrix mode). A focused pass is not the complete gate.
|
|
31
|
+
|
|
32
|
+
## Npm regressions and local trial
|
|
33
|
+
|
|
34
|
+
Existing npm clean-install CI covers real SDK 0.85.0 and 0.85.1. The standalone CI job also checks the public npm launch path without execution-time network:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm_checks="$(mktemp -d)"
|
|
38
|
+
node test/smoke/pi085-clean-install.mjs "$npm_checks/sdk" 0.85.1
|
|
39
|
+
node test/smoke/npm-background.mjs "$npm_checks/sdk" "$npm_checks/launch"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
To try a checkout without replacing your installation, start a separate supported Pi process with an isolated agent directory:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
PI_CODING_AGENT_DIR="$(mktemp -d)" "$release_dir/pi/pi" \
|
|
46
|
+
--no-extensions --no-skills --no-prompt-templates --extension "$PWD/index.ts"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Configure a provider in that isolated session, ask for a read-only background child and inspect its notification/run artifacts. This loads only the checkout for that process; it does not install the candidate or reuse normal credentials. Keep the parent alive for notifications.
|
package/docs/tool-reference.md
CHANGED
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
Parameters and actions for the `subagent` tool. These are what the LLM passes when it calls the tool; most users ask naturally or use slash commands instead.
|
|
4
4
|
|
|
5
|
+
Call `{ action: "guide", topic: "tool-reference" }` for this reference or `topic: "workflows"` for [workflow recipes](workflows.md). Use `topic: "agents"` for authoring, `topic: "missions"` for missions/schedules, and `topic: "watchdog"` for watchdog controls. Guide reads do not change the schema or grant authority.
|
|
6
|
+
|
|
5
7
|
## Execution examples
|
|
6
8
|
|
|
7
9
|
Chaining is code-driven through `workflowScript`. Use `await runs.run(...)` for sequential steps and `await runs.all([{ key, agent, task }, ...])` for ordinary parallel fanout. `runs.all` resolves to an ordered array, not a key map, so use indexes, destructuring, or `.map(...)`, not `results.<key>`. Do not read `.output` from an unawaited `runs.run` launch. Stored `runs.run` promises are only for the advanced rolling fanout pattern under [Workflow steering](#workflow-steering), where every promise is later observed with direct `await`, `Promise.race`, or `Promise.all`. Legacy top-level `chain`, `tasks`, and `parallel` inputs are not supported. Helper functions must be plain functions or explicit Promise chains. Nested `async function` helpers, async arrows, and async methods are rejected so child-launch tracking stays portable across Node and Bun. For permission-sensitive host calls, use an extension-owned named resource such as `{ workflow: "run-ci", args: { command: "npm test" } }`; raw public `workflowScript`/`workflowScriptPath` inputs have unknown resource provenance and cannot call `runs.host`. A resolved resource may internally use `runs.host(key, { kind: "command", command, timeoutMs, output?, role?, provider? })` within its authority ceiling; there is no per-step `cwd`, and commands and relative output paths use the workflow `cwd`. Set `cwd` on the outer `subagent({...})` request instead, or put a trusted directory change in the command (for example, `cd /path/to/worktree && npm test`).
|
|
8
10
|
|
|
9
|
-
Use `{ action: "validate", workflowScript }` to check statically decidable syntax and structure without launching children. It returns `{ ok, errors }` and fails the tool call when `ok` is false. Dynamic keys and values remain
|
|
11
|
+
Use `{ action: "validate", workflowScript }` to check statically decidable syntax and structure without launching children. It returns `{ ok, errors }` and fails the tool call when `ok` is false. Literal child `baseRef` values are checked against the runtime ref policy. Dynamic keys and values remain subject to runtime checks; static validation does not guess them.
|
|
10
12
|
|
|
11
13
|
Use `workflowScriptPath` instead of `workflowScript` to load the same JavaScript statement body from a file. The two fields are mutually exclusive. Relative paths resolve against the request `cwd`, and absolute paths pass through. The host reads the file before validation, scheduling, or sandbox execution. The workflow sandbox still has no filesystem access. Missing, unreadable, and empty files fail as file input errors.
|
|
12
14
|
|
|
@@ -86,11 +88,13 @@ The complete plain-JSON inventory is validated before the first launch (maximum
|
|
|
86
88
|
|
|
87
89
|
| Param | Type | Default | Description |
|
|
88
90
|
|-------|------|---------|-------------|
|
|
89
|
-
| `agent` | string | - |
|
|
90
|
-
| `
|
|
91
|
+
| `agent` | string | - | One direct child or agent-management target. Workflow child agents are set inside `runs.run` or `runs.all`. |
|
|
92
|
+
| `task` | string | agent default | Direct child's task; requires `agent`, excludes `action` and workflow inputs. `agent` may also select a management target. |
|
|
93
|
+
| `action` | string | - | Offline workflow `validate`, agent management (including `guide`, `children.list`, and `refine`/`refine.show`/`refine.rollback`), lane evidence (`lane.status`, `lane.recordMerge`, `lane.recordSupersession`), mission (`mission.create/list/show/update/resolve-decision/attach-run/close`), Inspect actions (`inspector.command/open/status/close`), Herdr project pane (`project.open/status/close`), status/control, plan-only `worktree.cleanup`, schedule, watchdog, or doctor action. |
|
|
91
94
|
| `topic` | `overview \| workflows \| agents \| missions \| observability \| tool-reference \| configuration \| models \| watchdog \| extension-api` | `overview` | Packaged guide topic for `action: "guide"`. |
|
|
92
95
|
| `config` | object/string | - | Agent config for management create/update. |
|
|
93
|
-
| `context` | `fresh \| fork` | global or per-agent default, else `fresh` | Explicit `fresh` or `fork` overrides every workflow child. When omitted, [`defaultSubagentContext`](configuration.md#defaultsubagentcontext) wins over each agent's `defaultContext`;
|
|
96
|
+
| `context` | `fresh \| fork \| profile` | global or per-agent default, else `fresh` | Explicit `fresh` or `fork` overrides every workflow child. `profile` requires the selected agent's declared `defaultContext` and ignores config `defaultSubagentContext`; missing agent defaults fail. When omitted, [`defaultSubagentContext`](configuration.md#defaultsubagentcontext) wins over each agent's `defaultContext`; implicit fork falls back to fresh without a persisted parent session and leaf. Explicit fork is strict. Packaged `worker`, `oracle`, and `advisor` default to `fork`. |
|
|
97
|
+
| `model` | string | agent default | Call `{action:"models"}` first and copy an exact `provider/id`; bare ids resolve only if unique, and agent names are not model ids. A suffix such as `provider/id:high` (`off/minimal/low/medium/high/xhigh/max`) overrides agent thinking. The `thinking` field is only for `watchdog.configure`, ignored on dispatch. |
|
|
94
98
|
| `missionId` | string | - | Attach a workflow to an existing project mission instead of creating its default enclosing mission. |
|
|
95
99
|
| `mission` | object/false | auto-create | Override the default enclosing mission with `{ title \| summary, objective?, goal?, budget?, labels? }`. Set exactly one non-empty `title` or `summary`; `objective` and `labels` are optional. `goal` may only be `true`, requires `budget.tokens`, and enables continuation notices. Pass `false` for an intentionally ephemeral workflow with no mission for it or its children and no `state` global. Explicit mission persistence failures are strict. |
|
|
96
100
|
| `handoffPath` | string | - | Aggregate handoff manifest for `action: "worktree.discard"` or lane evidence actions, or optional explicit metadata for `action: "worktree.cleanup"`. |
|
|
@@ -100,7 +104,7 @@ The complete plain-JSON inventory is validated before the first launch (maximum
|
|
|
100
104
|
| `laneId` | string | - | Exact `runId` stored in the handoff manifest for `lane.status`, `lane.recordMerge`, or `lane.recordSupersession`. |
|
|
101
105
|
| `merge` | object | - | Attested merge evidence for `lane.recordMerge`; requires a positive PR number, full reviewed/merge SHAs, tree-equivalence and post-merge-check statuses, attestor, and timestamp. |
|
|
102
106
|
| `supersession` | object | - | Attested replacement-lane evidence for `lane.recordSupersession`; requires a different replacement lane id, attestor, and timestamp. |
|
|
103
|
-
| `focus` | boolean | false | Focus the newly split pane for `action: "inspector.open"` or `action: "project.open"`; not a standalone action. Panes open in the background unless you set `focus: true`. Existing saved project panes can be focused through the public project-pane API when Herdr reports a tab or workspace id. |
|
|
107
|
+
| `focus` | boolean | false | Focus the newly split host inspector pane for `action: "inspector.open"` or the new Herdr project pane for `action: "project.open"`; not a standalone action. `inspector.command` is read-only and does not contact Herdr or write a binding. Panes open in the background unless you set `focus: true`. Existing saved project panes can be focused through the public project-pane API when Herdr reports a tab or workspace id. |
|
|
104
108
|
| `view` | `fleet \| transcript` | - | Optional `status` view for the active fleet surface or transcript tail inspection. |
|
|
105
109
|
| `lines` | number | `80` | Maximum transcript lines for `action: "status", view: "transcript"`; capped at 500. |
|
|
106
110
|
| `agentScope` | `user \| project \| both` | `both` | Agent discovery scope. Project wins on collisions. |
|
|
@@ -108,7 +112,7 @@ The complete plain-JSON inventory is validated before the first launch (maximum
|
|
|
108
112
|
| `async` | boolean | default-on | Background execution. Workflows default to background. `async:false` blocks the parent until completion and runs the child as a session inside the parent Pi process; such foreground children never load the parent's ambient extensions, so agents that need MCP tools (`mcpDirectTools`, or MCP tools from an ambient adapter such as pi-mcp-adapter) or models from a provider extension must run as background children, which load them inside the detached runner process. |
|
|
109
113
|
| `chatProgress` | `auto \| off \| live-card` | `auto` | WorkflowScript chat projection. `auto` renders a live in-chat card only for watched foreground workflows in the same Git repository, including managed worktrees; it is off otherwise. Explicit `live-card` requires `async:false` and the same Git repository. Async workflows have no inline live card, so omit `chatProgress` or use `auto`/`off`; use `async:false` only when the parent must block. |
|
|
110
114
|
| `isolation` | `none \| worktree` | - | Workflow child isolation. `none` runs in the shared cwd and does not need Git. `worktree` requires a managed Git worktree. Do not combine it with a contradictory `worktree` value. |
|
|
111
|
-
| `baseRef` | string | `HEAD` |
|
|
115
|
+
| `baseRef` | string | `HEAD` | `HEAD` or a supported named ref such as `refs/heads/release`, `refs/tags/v1`, or `origin/main`. Full 40/64-character commit IDs and revision expressions such as `HEAD~1` are unsupported. The ref must resolve to a commit at worktree allocation; omitted values default to `HEAD` resolved at that time. Source-checkout cleanliness is still checked. For workflowScript, set it on the outer request as a default or on an individual `runs.run`/`runs.all` child to override it. |
|
|
112
116
|
| `timeoutMs` / `maxRuntimeMs` | number | config `timeoutMs`, else 30 min foreground / single-agent async | Optional run-level max runtime in milliseconds. When omitted, the global [`timeoutMs`](configuration.md#timeoutms) config provides the default; absent that, foreground and plain single-agent async runs fall back to 30 minutes, while composite async runs (chains, parallel tasks, workflows) stay unbounded at the top level. Expiration of this run-level deadline is terminal and does not trigger `fallbackModels`. |
|
|
113
117
|
| `toolTimeoutMs` | number | fast-tool default | Optional positive hard per-tool-call deadline in milliseconds. Precedence: call value → agent frontmatter → config → `PI_SUBAGENT_TOOL_TIMEOUT_MS`. The timer starts on `tool_execution_start`, clears on the matching `tool_execution_end`, and terminates the run with `timedOut: true` if the tool remains open. When omitted, known-fast built-in tools get a five-minute default; long-running tools get attention notices but no hard default. It never extends the run deadline; `contact_supervisor`, `intercom`, and `bg_wait` are exempt. |
|
|
114
118
|
| `toolBudget` | object | none | Optional child tool-call budget `{ soft?, hard, block? }`. At `soft` the child is nudged to finalize. After `hard`, configured tools are blocked; `block` defaults to `read`, `grep`, `find`, and `ls`, while `"*"` blocks every tool call. Final assistant text is never blocked. |
|
|
@@ -132,7 +136,7 @@ Bound writer work with a narrow task and an outer `timeoutMs` or `maxRuntimeMs`
|
|
|
132
136
|
|
|
133
137
|
Explicit `context: "fork"` fails fast when the parent session is not persisted, the current leaf is missing, or the branched child session cannot be created. By contrast, global `defaultSubagentContext: "fork"` and agent-level `defaultContext: fork` are preferences: when the parent has no persisted session file or current leaf yet, the launch uses `fresh` immediately instead of failing and requiring a retry. Global `defaultSubagentContext: "fresh"` starts fresh. Explicit `context: "fresh"` always wins over both preferences.
|
|
134
138
|
|
|
135
|
-
When the inherited transcript contains signed Anthropic `thinking` / `redacted_thinking` blocks, `pi-subagents` strips those provider-private blocks from the forked child session
|
|
139
|
+
When the inherited transcript contains signed Anthropic `thinking` / `redacted_thinking` blocks, `pi-subagents` strips those provider-private blocks from the forked child session: a thinking signature is bound to the session that produced it and cannot be replayed into a branch. The child keeps its requested thinking level and reasons fresh from its first turn; sanitizing the inherited transcript is not a downgrade. Explicit `context: "fork"` never silently downgrades to `fresh`.
|
|
136
140
|
|
|
137
141
|
In workflow runs that omit `context`, each `runs.run` child follows the global `defaultSubagentContext` when set, then its own `defaultContext`. Without the global setting, a fresh-default scout can run fresh beside a fork-default worker. If the parent session file or current leaf is not available yet, implicit fork-default children run fresh. Pass explicit `context: "fork"` or `context: "fresh"` when you intentionally want one context for every child.
|
|
138
142
|
|
|
@@ -140,7 +144,7 @@ In workflow runs that omit `context`, each `runs.run` child follows the global `
|
|
|
140
144
|
|
|
141
145
|
`runs.steer(key, message, options?)` targets a stable key already launched by `runs.run` or `runs.all`. It does not accept a raw run id. Options are `mode?: "steer" | "follow_up" | "auto"`, `index?: number`, and `ackTimeoutMs?: number`. The promise returns `{ key, state, requestId?, deliveryStatus?, targets?, error? }`, where `state` is `queued`, `delivered`, `missed`, or `failed`.
|
|
142
146
|
|
|
143
|
-
The workflow trace records the attempt and receipt. Always await, return, or include the promise in an awaited standard Promise combinator. Unawaited steering calls reject workflow completion after the side effect settles. `Promise.race` remains the rolling primitive. Foreground children are steered through their in-process session (`steer` and `auto`
|
|
147
|
+
The workflow trace records the attempt and receipt. Always await, return, or include the promise in an awaited standard Promise combinator. Unawaited steering calls reject workflow completion after the side effect settles. `Promise.race` remains the rolling primitive. Foreground children are steered through their in-process session (`steer` and `auto` report `delivered` when that transport accepts the input; `follow_up` reports `queued` when accepted into Pi's queue). Async children use the file control inbox and report correlated consumption by the child, not merely inbox acceptance. Steering recovery is disabled in both cases.
|
|
144
148
|
|
|
145
149
|
For advanced rolling fanout, keep the launched `runs.run` promises in ordinary JavaScript data only when every promise is later observed with direct `await`, `Promise.race`, or `Promise.all`. `Promise.race` gives the next completed child, `runs.steer` can challenge a still-running keyed sibling, and `Promise.all` collects the rest. No separate `runs.start`, `runs.next`, or `runs.collect` API is exposed.
|
|
146
150
|
|
|
@@ -260,6 +264,10 @@ Rules:
|
|
|
260
264
|
|
|
261
265
|
`refine`, `refine.show`, and `refine.rollback` manage project-local refinement overlays for one agent. `/subagents-refine <agent>` is the slash equivalent of `refine`. See [agents.md](agents.md#refinement-overlays) for behavior and storage.
|
|
262
266
|
|
|
267
|
+
### Schedule controls
|
|
268
|
+
|
|
269
|
+
Use `schedule.create` with `workflowScript` or `workflowScriptPath`, not a direct child. `at` accepts a delay like `+10m` or an ISO timestamp with timezone; `every` accepts fixed intervals. `sessionOnly:true` binds restoration/execution to the creating session file; omitted/false is project-wide. Recurring `quiet:true` keeps successful automatic fires visible without a parent turn; failed, stopped or paused runs still wake the parent. One-shot `at` and manual `schedule.run` stay noisy unless that launch passes `quiet:true`. See [missions and schedules](missions.md#schedules) for examples and list/show/history/pause/resume/run/run-due/delete. Calendar selectors (`on`, `timezone`) and schedule mission attachment are deferred. `baseRef` resolves only at worktree allocation and still requires a clean source checkout.
|
|
270
|
+
|
|
263
271
|
## Lane merge evidence and cleanup eligibility
|
|
264
272
|
|
|
265
273
|
Lane evidence actions update an existing parallel handoff manifest at an explicit update boundary. They do not verify GitHub state, run Git commands, or remove worktrees. Pass the manifest path and its exact `runId` as `laneId`:
|
|
@@ -356,9 +364,9 @@ subagent({ action: "doctor" })
|
|
|
356
364
|
|
|
357
365
|
### steer
|
|
358
366
|
|
|
359
|
-
`steer` waits up to three seconds for a correlated
|
|
367
|
+
`steer` waits up to three seconds for a correlated receipt and returns a request id with `delivered`, `scheduled`, `pending`, `partial`, `recovered`, or `failed` plus per-child states. The receipt also has `deliveryStatus: "delivered" | "queued"`. For async runs, delivery means the child consumed the correlated user input; foreground delivery means the in-process Pi transport accepted it. Neither means model compliance. A pending indexed child returns `scheduled`.
|
|
360
368
|
|
|
361
|
-
The optional `mode` is `steer` by default and keeps the current interrupt behavior. `follow_up` waits for the next turn boundary. `auto`
|
|
369
|
+
The optional `mode` is `steer` by default and keeps the current interrupt behavior. `follow_up` waits for the next turn boundary. `auto` uses the same native steer delivery path as `steer`, without automatic pause-and-revive recovery after a missed acknowledgment. The retained revival-brief queue holds 20 messages and returns a clear error when full; this is not a live follow-up queue bound. A live follow-up acknowledgment reports queue acceptance, not consumption. Async runs later record correlated consumption or fail unconsumed requests at settlement; foreground follow-ups have no later correlated receipt. A `follow_up` sent to a completed retained workflow child becomes the first brief for its next `resume`; it does not revive the child by itself.
|
|
362
370
|
|
|
363
371
|
Only a top-level single run may interrupt after the acknowledgment deadline and recover after a further 15-second pause/revival bound; durable multi-child and nested runs never auto-interrupt. Recovery launches a replacement only after the source is confirmed paused, a valid persisted session exists, and deadline, turn, and tool budgets remain. It preserves the original child contract and remaining limits; otherwise the source stays paused with an explicit failure. Late acceptance is recorded but cannot cancel committed recovery.
|
|
364
372
|
|
|
@@ -370,6 +378,8 @@ The `/subagents-steer <run-id> [--child <child-id>] <message>` slash command is
|
|
|
370
378
|
|
|
371
379
|
Every run resolves an effective acceptance policy. Callers may omit `acceptance` for the inferred default, or set it on single runs, top-level parallel task items, chain steps, static parallel tasks, and dynamic fanout templates.
|
|
372
380
|
|
|
381
|
+
Prefer an inline JSON object. JSON-encoded object strings are tolerated only during input normalization; invalid strings fail closed. `true` is invalid. Supported evidence kinds are `changed-files`, `tests-added`, `commands-run`, `validation-output`, `residual-risks`, `no-staged-files`, `diff-summary`, `review-findings`, and `manual-notes`. For example: `{level:"checked",evidence:["commands-run","changed-files"],review:{required:true}}`. Evidence levels end at `verified`; independent review is a separate gate, not a stronger evidence level.
|
|
382
|
+
|
|
373
383
|
```ts
|
|
374
384
|
{
|
|
375
385
|
agent: "worker",
|