pi-subagents 0.49.0 → 0.51.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 +97 -0
- package/agents/gpt-pro.md +17 -0
- package/agents/oracle.md +7 -5
- package/agents/researcher.md +1 -1
- package/agents/reviewer.md +2 -2
- package/agents/scout.md +1 -1
- package/agents/worker.md +1 -1
- package/async-retention-discovery-worker.mjs +180 -0
- package/docs/agents.md +37 -2
- package/docs/configuration.md +76 -14
- package/docs/extension-api.md +78 -1
- package/docs/missions.md +1 -1
- package/docs/observability.md +20 -4
- package/docs/tool-reference.md +55 -39
- package/docs/workflows.md +171 -5
- package/package.json +4 -2
- package/skills/pi-subagents/SKILL.md +5 -4
- package/skills/pi-subagents/references/constraints-and-recipes.md +9 -6
- package/skills/pi-subagents/references/execution-controls.md +22 -18
- package/skills/pi-subagents/references/management-authoring-rpc.md +5 -5
- package/skills/pi-subagents/references/prompting-and-roles.md +33 -17
- package/src/agents/agent-management.ts +100 -345
- package/src/agents/agent-serializer.ts +2 -0
- package/src/agents/agents.ts +135 -25
- package/src/api/external-job-provider.ts +185 -0
- package/src/api/external-runs.ts +174 -84
- package/src/api/preflight.ts +42 -11
- package/src/api/shared-types.ts +2 -0
- package/src/extension/config.ts +36 -3
- package/src/extension/doctor.ts +3 -6
- package/src/extension/fanout-child.ts +2 -2
- package/src/extension/index.ts +210 -90
- package/src/extension/public-execution.ts +31 -2
- package/src/extension/rpc.ts +5 -1
- package/src/extension/schemas.ts +14 -36
- package/src/extension/tool-description.ts +37 -24
- package/src/inspectors/herdr/actions.ts +5 -9
- package/src/inspectors/herdr/inspector-runner.ts +2 -1
- package/src/inspectors/herdr/project-panes.ts +4 -8
- package/src/inspectors/herdr/shell-command.ts +16 -0
- package/src/intercom/intercom-bridge.ts +2 -3
- package/src/intercom/native-supervisor-channel.ts +49 -51
- package/src/missions/goal-driver.ts +3 -1
- package/src/missions/lifecycle.ts +6 -1
- package/src/missions/store.ts +4 -9
- package/src/profiles/profiles.ts +3 -1
- package/src/runs/background/active-run-index.ts +94 -1
- package/src/runs/background/async-execution.ts +98 -38
- package/src/runs/background/async-job-tracker.ts +21 -4
- package/src/runs/background/async-resume.ts +30 -17
- package/src/runs/background/async-retention.ts +888 -0
- package/src/runs/background/async-status-snapshot.ts +277 -0
- package/src/runs/background/async-status.ts +47 -56
- package/src/runs/background/chain-append.ts +3 -33
- package/src/runs/background/chain-root-attachment.ts +2 -2
- package/src/runs/background/completion-replay.ts +11 -1
- package/src/runs/background/control-channel.ts +14 -68
- package/src/runs/background/fleet-view.ts +3 -1
- package/src/runs/background/index-segment.ts +59 -0
- package/src/runs/background/notify.ts +3 -1
- package/src/runs/background/result-files.ts +505 -0
- package/src/runs/background/result-watcher.ts +250 -51
- package/src/runs/background/retained-children.ts +79 -20
- package/src/runs/background/run-id-query.ts +7 -0
- package/src/runs/background/run-id-resolver.ts +37 -29
- package/src/runs/background/run-status.ts +34 -20
- package/src/runs/background/scheduled-runs.ts +71 -27
- package/src/runs/background/stale-run-reconciler.ts +33 -16
- package/src/runs/background/steering.ts +11 -1
- package/src/runs/background/subagent-runner.ts +535 -161
- package/src/runs/background/subagent-wait.ts +9 -7
- package/src/runs/background/terminal-run-index.ts +129 -0
- package/src/runs/background/wait-completions.ts +22 -2
- package/src/runs/background/wait-subscriptions.ts +80 -1
- package/src/runs/foreground/async-dismiss-action.ts +2 -1
- package/src/runs/foreground/async-steering-action.ts +21 -14
- package/src/runs/foreground/execution.ts +221 -15
- package/src/runs/foreground/subagent-executor.ts +529 -1519
- package/src/runs/foreground/workflow-foreground-steering.ts +6 -5
- package/src/runs/shared/chain-outputs.ts +1 -3
- package/src/runs/shared/completion-guard.ts +96 -6
- package/src/runs/shared/external-cli-runner.ts +4 -0
- package/src/runs/shared/external-job-bridge.ts +450 -0
- package/src/runs/shared/external-job-runner.ts +286 -0
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +14 -0
- package/src/runs/shared/model-fallback.ts +34 -3
- package/src/runs/shared/nested-events.ts +66 -62
- package/src/runs/shared/orca-progress-tabs.ts +437 -0
- package/src/runs/shared/parallel-handoff.ts +46 -4
- package/src/runs/shared/parallel-utils.ts +6 -15
- package/src/runs/shared/permissions.ts +5 -1
- package/src/runs/shared/pi-args.ts +8 -1
- package/src/runs/shared/subagent-control.ts +41 -4
- package/src/runs/shared/subagent-prompt-runtime.ts +13 -15
- package/src/runs/shared/subagent-startup-retry.ts +12 -0
- package/src/runs/shared/tool-timeout.ts +93 -0
- package/src/runs/shared/workflow-graph.ts +1 -23
- package/src/runs/shared/worktree.ts +12 -1
- package/src/shared/atomic-json.ts +22 -2
- package/src/shared/capacity-resilient-json.ts +102 -0
- package/src/shared/completion-owner.ts +14 -0
- package/src/shared/file-system-retry.ts +49 -1
- package/src/shared/fork-context.ts +42 -0
- package/src/shared/prompt-resources.ts +0 -40
- package/src/shared/settings.ts +3 -27
- package/src/shared/types.ts +88 -27
- package/src/shared/utils.ts +8 -0
- package/src/shared/watch-strategy.ts +10 -0
- package/src/slash/slash-commands.ts +45 -28
- package/src/slash/slash-live-state.ts +3 -0
- package/src/tui/fleet-status.ts +160 -45
- package/src/tui/fleet.ts +186 -28
- package/src/tui/render.ts +41 -10
- package/src/workflows/chat-progress.ts +7 -5
- package/src/workflows/scripted-workflow.ts +424 -125
- package/src/runs/foreground/chain-clarify.ts +0 -1354
- package/src/runs/foreground/chain-execution.ts +0 -1565
package/docs/configuration.md
CHANGED
|
@@ -24,18 +24,10 @@ By default, project settings resolve from the nearest parent directory that cont
|
|
|
24
24
|
{ "toolDescriptionMode": "compact" }
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
Controls the parent-facing `subagent` tool description registered at startup. `full`
|
|
27
|
+
Controls the parent-facing `subagent` tool description registered at startup. The default registers split prompt metadata: a short tool description plus `promptSnippet` and `promptGuidelines`. Set `"full"` to register the complete description as one tool description, or `"compact"` to keep the execution modes, async/`subagent_wait` guidance, child-safety boundary, management/action split, one-writer review guidance, and artifact/status essentials with less prompt bloat.
|
|
28
28
|
|
|
29
29
|
`custom` reads `subagent-tool-description.md` from the project config directory, then from `~/.pi/agent/subagent-tool-description.md`. Missing, empty, unreadable, or oversized custom files fall back to the full description. Custom templates may use `{{fullDescription}}`, `{{compactDescription}}`, `{{safetyGuidance}}`, `{{agentDir}}`, and `{{projectConfigDir}}`; the safety guidance is always present so custom prose cannot remove the runtime guardrails. Restart Pi after changing the mode or custom file.
|
|
30
30
|
|
|
31
|
-
## `legacyChainControls`
|
|
32
|
-
|
|
33
|
-
```json
|
|
34
|
-
{ "legacyChainControls": true }
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Defaults to `false`. The default registered model-facing tool schema and description omit the legacy `append-step` `step` schema and legacy checkpoint controls. This does not change runtime support for existing durable legacy chains. Set this to `true` before directly managing a legacy chain with `append-step`, `approve-checkpoint`, or `reject-checkpoint`.
|
|
38
|
-
|
|
39
31
|
## `inlineToolDisplay`
|
|
40
32
|
|
|
41
33
|
```json
|
|
@@ -67,6 +59,38 @@ With `"summary"`, a tool result looks like this:
|
|
|
67
59
|
✓ reviewer · completed
|
|
68
60
|
```
|
|
69
61
|
|
|
62
|
+
## `foregroundDetachShortcut`
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{ "foregroundDetachShortcut": "ctrl+b" }
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Optionally binds a shortcut that detaches the active foreground single-subagent run without terminating it. The running foreground card shows the configured shortcut beside its live-detail hint. The default is unset, so pi-subagents does not reserve a global key.
|
|
69
|
+
|
|
70
|
+
Pi binds `Ctrl+B` to editor cursor-left by default. The extension shortcut takes precedence, but Pi reports the conflict at startup. To reserve the key without that warning, override the editor action in `~/.pi/agent/keybindings.json`:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"tui.editor.cursorLeft": "left"
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## `orcaProgressTabs` (experimental)
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"orcaProgressTabs": {
|
|
83
|
+
"enabled": true
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Opt in to a best-effort Orca observer that creates one Orca terminal tab for each subagent child and mirrors its live tool, assistant, stdout, and stderr progress. Tab titles use a persistent worktree-local sequence (`subagent · <agent> · 1`, `... · 2`, and so on), so separate workflows and concurrent children do not reuse the same number. For the same worktree, `orca terminal create` runs one at a time in that sequence so the UI can append tabs from left to right as `1`, then `2`, then `3`. This does **not** replace Pi as the child runner: native Pi children keep the same process, lifecycle, status, control, artifact, and result paths. External CLI profiles also keep their existing runner and can mirror their stdout/stderr.
|
|
89
|
+
|
|
90
|
+
The integration is off by default and supports macOS and Linux. It is disabled on Windows. When enabled, `pi-subagents` looks for executable `orca` on `PATH`, or uses the executable path in `PI_SUBAGENT_ORCA_BINARY`. If no executable is available, Orca is not running, the cwd is not an Orca-managed worktree, or `terminal create` fails, the authoritative subagent still runs normally. Tab creation is deliberately best-effort and never changes the child result.
|
|
91
|
+
|
|
92
|
+
Set `enabled` to `false` (or remove the block) as a kill switch. In that state, `pi-subagents` does not invoke `orca` and creates no Orca tabs. The temporary mirror files contain child output, use private file modes where supported, and are removed shortly after the child finishes. Each mirror is capped at 1 MiB. The observer stops accepting progress when the cap or stream backpressure is reached and appends a truncation notice. The viewer removes terminal control sequences with parser state that persists across file reads. On completion, the viewer exits back to the Orca terminal's shell prompt; the tab and its terminal scrollback remain open until the user closes the tab. A successfully completed native Pi child with a recorded session ends with a safely quoted `rm -- <exact-session-path>` command; failed, stopped, timed-out, and sessionless children do not show the removal command.
|
|
93
|
+
|
|
70
94
|
## `asyncByDefault`
|
|
71
95
|
|
|
72
96
|
```json
|
|
@@ -75,6 +99,16 @@ With `"summary"`, a tool result looks like this:
|
|
|
75
99
|
|
|
76
100
|
WorkflowScript calls use background execution when the request omits `async`. Set `asyncByDefault` to `false` to restore foreground-by-default behavior for tool launches that still use the internal single-run primitive. Callers can still force foreground with `async: false` unless `forceTopLevelAsync` is enabled.
|
|
77
101
|
|
|
102
|
+
## `defaultSubagentContext`
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{ "defaultSubagentContext": "fresh" }
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Sets `fresh` or `fork` for every subagent launch that omits `context`. This global preference replaces each agent-level `defaultContext`. Explicit `context: "fresh"` or `context: "fork"` still wins.
|
|
109
|
+
|
|
110
|
+
With `"fork"`, the setting uses the existing implicit-fork behavior. A launch starts fresh when the parent session file or current leaf is not available. `"fresh"` starts fresh even when the selected agent defaults to fork. Scheduled runs continue to set fresh context explicitly. A runner or provider that does not support fork context keeps its existing rejection behavior.
|
|
111
|
+
|
|
78
112
|
## `fleetView`
|
|
79
113
|
|
|
80
114
|
```json
|
|
@@ -150,6 +184,18 @@ Use it when foreground orchestration or plain async single-agent runs need a lon
|
|
|
150
184
|
|
|
151
185
|
Composite async runs (async chains, parallel tasks, and scripted workflows) stay unbounded at the top level by design. Their runner children are bounded individually by their own agent or runner defaults, so this value does not cap them. Must be a positive integer no greater than `2147483647` (the largest delay a Node.js timer can honor, roughly 24.8 days); invalid or out-of-range values are ignored and the built-in defaults apply.
|
|
152
186
|
|
|
187
|
+
## `toolTimeoutMs`
|
|
188
|
+
|
|
189
|
+
```json
|
|
190
|
+
{ "toolTimeoutMs": 600000 }
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Optional hard per-tool-call deadline in milliseconds. When configured, a child that emits `tool_execution_start` but not `tool_execution_end` is terminated with `timedOut: true` and a tool-specific error. The effective value is resolved per child: explicit `subagent` call value, then agent frontmatter, then this config value, then `PI_SUBAGENT_TOOL_TIMEOUT_MS`.
|
|
194
|
+
|
|
195
|
+
Without a configured value, Pi still applies a five-minute hard timeout to known-fast built-in tools: `read`, `grep`, `find`, `ls`, `edit`, `write`, and `structured_output`. Long-running tools such as `bash`, custom tools, and MCP tools do not get a hard default. They get the normal open-tool attention notice after `activeNoticeAfterMs` and remain bounded by the run-level deadline.
|
|
196
|
+
|
|
197
|
+
The tool timer tracks each active `toolCallId` separately and never extends the run-level deadline: when the remaining run budget is shorter, the ordinary run-level timeout wins. `contact_supervisor`, `intercom`, and `subagent_wait` are exempt because their legitimate purpose can be to wait for a human, supervisor, or child run. Use hard tool timeouts only for wedge protection; an elapsed timeout is not a mutation-safe boundary. Configured values must be positive integers no greater than `2147483647`; invalid or out-of-range values are rejected with a visible error rather than silently ignored.
|
|
198
|
+
|
|
153
199
|
## `globalConcurrencyLimit`
|
|
154
200
|
|
|
155
201
|
```json
|
|
@@ -275,7 +321,7 @@ Use `file` on hosts where endpoint protection (EDR) pre-execution scanning denie
|
|
|
275
321
|
}
|
|
276
322
|
```
|
|
277
323
|
|
|
278
|
-
Controls whether subagents receive runtime
|
|
324
|
+
Controls whether subagents receive runtime coordination instructions and whether `contact_supervisor` is auto-added to their tool allowlist when needed.
|
|
279
325
|
|
|
280
326
|
Fields:
|
|
281
327
|
|
|
@@ -283,9 +329,9 @@ Fields:
|
|
|
283
329
|
- `instructionFile`: optional Markdown template replacing the default bridge instructions. `{orchestratorTarget}` is interpolated. Relative paths resolve from `~/.pi/agent/extensions/subagent/`.
|
|
284
330
|
- `resultDelivery`: default `false`; set `true` only when an external listener consumes `subagent:result-intercom` and acknowledges the grouped completion payload. This is optional external result delivery, not native supervisor messaging. Enabled delivery waits for acknowledgement and reports acknowledgement failures. It does not change supervisor asks or progress updates.
|
|
285
331
|
|
|
286
|
-
Bridge activation requires a targetable current parent session id, which `pi-subagents` passes to children automatically. Native supervisor messaging does not require an external `pi-intercom` installation or per-agent extension allowlists: children use `contact_supervisor`, and parents use `subagent_supervisor` to inspect or reply.
|
|
332
|
+
Bridge activation requires a targetable current parent session id, which `pi-subagents` passes to children automatically. Native supervisor messaging does not require an external `pi-intercom` installation or per-agent extension allowlists: children use `contact_supervisor`, and parents use `subagent_supervisor` to inspect or reply. Agents can still use an external `intercom` tool when they explicitly request a provider that supplies it.
|
|
287
333
|
|
|
288
|
-
The default injected guidance tells children to use `contact_supervisor` with `reason: "need_decision"` when blocked or needing a decision, `reason: "progress_update"` only for meaningful blocked/progress updates,
|
|
334
|
+
The default injected guidance tells children to use `contact_supervisor` with `reason: "need_decision"` when blocked or needing a decision, `reason: "progress_update"` only for meaningful blocked/progress updates, and avoid routine completion handoffs.
|
|
289
335
|
|
|
290
336
|
## `worktreeBaseDir`
|
|
291
337
|
|
|
@@ -362,9 +408,9 @@ Controls where subagent artifact files (inputs, outputs, transcripts, metadata)
|
|
|
362
408
|
- `"session"` (default): stores artifacts under pi's session directory (`~/.pi/agent/sessions/<session>/subagent-artifacts/`), keeping the working directory clean. It falls back to the OS temp directory when no session file exists.
|
|
363
409
|
- `"temp"`: uses the OS temp directory.
|
|
364
410
|
|
|
365
|
-
This preference also controls the default
|
|
411
|
+
This preference also controls the default workflow artifact directory used by scripted chaining. `"project"` uses `<cwd>/.pi/subagents/chain-runs/`; the directory keeps its legacy name for compatibility. The default `"session"` and `"temp"` use the user-scoped temp workflow artifact directory.
|
|
366
412
|
|
|
367
|
-
The `"session"` option uses the same directory that `cleanupAllArtifactDirs` already scans for age-based cleanup, so artifacts are still cleaned up automatically. Temporary
|
|
413
|
+
The `"session"` option uses the same directory that `cleanupAllArtifactDirs` already scans for age-based cleanup, so artifacts are still cleaned up automatically. Temporary workflow artifact directories are cleaned up separately after 24 hours.
|
|
368
414
|
|
|
369
415
|
When a project-scoped launch runs from an npm package directory, pi-subagents warns if package settings can include `.pi/subagents/` in the published package. Add `.pi/subagents/` to `.npmignore` (or `.gitignore` when no `.npmignore` exists), use a `files` allowlist that does not include `.pi/subagents/`, or select `"session"` or `"temp"`.
|
|
370
416
|
|
|
@@ -393,3 +439,19 @@ Controls smart batching of async-completion notifications. When several backgrou
|
|
|
393
439
|
## `permissions`
|
|
394
440
|
|
|
395
441
|
Native child tool permission rules. See [watchdog.md](watchdog.md#native-child-tool-permissions).
|
|
442
|
+
|
|
443
|
+
## `PI_SUBAGENT_FS_RETRY_MAX_TOTAL_MS`
|
|
444
|
+
|
|
445
|
+
Caps the total time a single retried filesystem operation may sleep, in milliseconds. Environment-only; there is no config key.
|
|
446
|
+
|
|
447
|
+
Atomic status and result writes retry on `EACCES`, `EBUSY`, and `EPERM`, which on Windows are usually a scanner or a sibling process holding the destination of a rename for a moment. The retry ladder sleeps up to about 7.9s in total, and it sleeps *synchronously* — `Atomics.wait` parks the calling thread rather than spinning.
|
|
448
|
+
|
|
449
|
+
That is the right trade-off for a CLI. It is the wrong one for a long-lived process that loads `pi-subagents` in-process and runs those writers on its event loop: one contended rename stalls everything it serves for the length of the ladder, and because the thread is parked rather than busy, it presents as an unresponsive process sitting at 0% CPU. A wide fanout makes contention on a single `status.json` likely.
|
|
450
|
+
|
|
451
|
+
Set this to bound that stall. The ladder keeps its number of attempts and only the sleeps shrink, because `run-fanout-budget` and mission state locking use the ladder's length as their attempt budget:
|
|
452
|
+
|
|
453
|
+
```text
|
|
454
|
+
PI_SUBAGENT_FS_RETRY_MAX_TOTAL_MS=1000
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
Unset by default, so behaviour is unchanged unless you opt in. Opting in trades lock-wait tolerance for responsiveness: entries clamped to `0` return immediately, so contention that would previously have been waited out surfaces as an error sooner. Values that are not a non-negative integer fail instead of being coerced.
|
package/docs/extension-api.md
CHANGED
|
@@ -56,6 +56,42 @@ The DTO intentionally never exposes run, async, or tool IDs. Clients must ignore
|
|
|
56
56
|
|
|
57
57
|
`pi.events` is in-process only. It does not reach separate Pi processes or child subagents; use the file lifecycle artifacts or `pi-intercom` for cross-process coordination.
|
|
58
58
|
|
|
59
|
+
## External jobs in FleetView
|
|
60
|
+
|
|
61
|
+
Use `pi-subagents/external-runs` to publish display-only current-session jobs owned by another extension:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import {
|
|
65
|
+
registerExternalRun,
|
|
66
|
+
updateExternalRun,
|
|
67
|
+
unregisterExternalRun,
|
|
68
|
+
} from "pi-subagents/external-runs";
|
|
69
|
+
|
|
70
|
+
registerExternalRun({
|
|
71
|
+
id: "dependency-review",
|
|
72
|
+
sessionId: ctx.sessionManager.getSessionId(),
|
|
73
|
+
source: "interactive-shell",
|
|
74
|
+
label: "Dependency review",
|
|
75
|
+
state: "running",
|
|
76
|
+
startedAt: Date.now(),
|
|
77
|
+
currentAction: "Inspecting package metadata",
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
updateExternalRun(ctx.sessionManager.getSessionId(), "dependency-review", {
|
|
81
|
+
state: "completed",
|
|
82
|
+
updatedAt: Date.now(),
|
|
83
|
+
endedAt: Date.now(),
|
|
84
|
+
preview: "No dependency blockers found.",
|
|
85
|
+
reportPath: "/tmp/dependency-review.md",
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
unregisterExternalRun(ctx.sessionManager.getSessionId(), "dependency-review");
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
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. By default, malformed cached records 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.
|
|
92
|
+
|
|
93
|
+
External jobs are observational. The caller owns execution, persistence, cancellation, and result delivery. FleetView does not expose stop, steer, resume, cancel, or Herdr controls for them. Supplied report and transcript paths are shown as bounded text only; FleetView does not read arbitrary external paths.
|
|
94
|
+
|
|
59
95
|
## Launch contract preflight
|
|
60
96
|
|
|
61
97
|
Use `pi-subagents/preflight` when an extension needs to inspect the resolved child launch contract before deciding whether to run anything:
|
|
@@ -96,6 +132,7 @@ Boundaries:
|
|
|
96
132
|
- Raw prompts are not exposed in public contract output.
|
|
97
133
|
- It is side-effect-free for launch state: it does not create child sessions, temp prompt files, structured-output runtimes, tool-diagnostic files, or run artifacts.
|
|
98
134
|
- Some host-owned facts, such as exact fork snapshots, nested async roots, and live model registries, can only be proven by the Pi host; those appear as `host_required` diagnostics instead of silently pretending to be exact.
|
|
135
|
+
- Preflight reads the extension config, so `defaultSubagentContext: "fresh"` or `"fork"` affects omitted context in the same way as execution. Explicit `context` still wins.
|
|
99
136
|
|
|
100
137
|
## Structured delegation API
|
|
101
138
|
|
|
@@ -226,6 +263,26 @@ Semantics:
|
|
|
226
263
|
|
|
227
264
|
Child processes do not gain provider tools or extensions automatically. Add `subagent_wait` to the child agent's `tools` allowlist and load each provider through `extensions` or `subagentOnlyExtensions`. The parent's effective `waitTool` setting is serialized through foreground, async, resume, chain, parallel, and fanout launch paths; `PI_SUBAGENT_WAIT_TOOL_ENABLED` keeps precedence.
|
|
228
265
|
|
|
266
|
+
## External job provider bridge
|
|
267
|
+
|
|
268
|
+
Extensions that own long-running advisor jobs can register a process-local provider for `runner.type: external-job` agents:
|
|
269
|
+
|
|
270
|
+
```ts
|
|
271
|
+
import { registerExternalJobProvider } from "pi-subagents/external-job-provider";
|
|
272
|
+
|
|
273
|
+
const dispose = registerExternalJobProvider({
|
|
274
|
+
name: "surf-oracle",
|
|
275
|
+
start: ({ prompt, promptDigest, cwd, runId, stepIndex, agent, options }) => startSurfJob({ prompt, promptDigest, cwd, runId, stepIndex, agent, options }),
|
|
276
|
+
status: (providerJobId) => getSurfJobStatus(providerJobId),
|
|
277
|
+
result: (providerJobId) => getSurfJobResult(providerJobId),
|
|
278
|
+
reattach: (providerJobId) => reattachSurfJob(providerJobId),
|
|
279
|
+
});
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
The provider returns handles with `providerJobId`, `state`, optional `handleUrl`/`conversationUrl`, optional `failureCode`/`failureMessage`, and optional `blockingJobId` for capacity conflicts. `result` can also return `output` and/or `artifactPath`.
|
|
283
|
+
|
|
284
|
+
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` again.
|
|
285
|
+
|
|
229
286
|
## Herdr integration
|
|
230
287
|
|
|
231
288
|
When Pi runs inside [Herdr](https://herdr.dev), pi-subagents automatically reports active async-run counts through Herdr pane metadata.
|
|
@@ -287,6 +344,26 @@ const closed = await closeProjectPane({ cwd: "/path/to/repo", requireIdle: true
|
|
|
287
344
|
|
|
288
345
|
The API returns discriminated structured results with canonical project root, binding path, pane identity, bounded Herdr runtime fields, and stable error codes. `requireIdle: true` fails closed unless Herdr explicitly reports `agent_status: "idle"`; use it when an owning extension must not close a working or blocked pane. The API deliberately reports `trust: "human-verification-required"`: it never bypasses or claims to attest Pi's project-trust prompt. `PROJECT_PANES_API_VERSION` is currently `1`.
|
|
289
346
|
|
|
347
|
+
## Host session lifetime and completion wakes
|
|
348
|
+
|
|
349
|
+
A host that embeds this extension owns whether completion wakes can be delivered at all.
|
|
350
|
+
|
|
351
|
+
Ordinary async and foreground completion wakes use `registerSubagentNotify` and `sendCompletion`. They listen for completion events and deliver through `pi.sendMessage(..., { triggerTurn })`. Session shutdown stops the result watcher and disposes this completion notifier. `createWaitSubscriptionManager` is separate: it is the explicit non-blocking `subagent_wait` subscription path, not the ordinary completion wake path.
|
|
352
|
+
|
|
353
|
+
Detached children do not stop when the session does. They are the host process's children, not the session's, so the run keeps going, completes, and notifies nobody. What is lost is the notification, not the work.
|
|
354
|
+
|
|
355
|
+
This matters because "is the parent busy?" is the wrong idle signal. A parent that launches a detached run and hands control back — which is what the async launch output tells it to do — is not prompting, streaming, compacting, or running a shell command. A host that reaps sessions on those signals alone will dispose exactly the session that was waiting to be woken.
|
|
356
|
+
|
|
357
|
+
If your host reclaims idle sessions, keep a session alive while it still has live detached work:
|
|
358
|
+
|
|
359
|
+
- Read run state from the status files under the async run directory rather than from event traffic. A long, quiet workflow sends almost nothing to the parent, so recent-activity heuristics conclude the wrong thing.
|
|
360
|
+
- Treat `queued` and `running` as live, matching `isActiveAsyncState`. `paused` is not: an interrupted run is finalized as paused.
|
|
361
|
+
- Do not treat `lastUpdate` as a heartbeat. The runner advances it in memory every second but only rewrites `status.json` when the activity classification changes, so a live run inside one long quiet tool call leaves a stale file behind. Judging liveness by file age will reap exactly the run you meant to protect.
|
|
362
|
+
- Prefer the recorded runner `pid`, which stays true through a silent tool call and goes false when the runner dies. Keep file age only as a fallback for runs that record no pid, and give it a wide window.
|
|
363
|
+
- Match `sessionId` in `status.json` against both forms. It is resolved as `getSessionFile() ?? getSessionId()`, so it is normally the parent's session *file path*, but a session that is not persisted records a bare session id instead.
|
|
364
|
+
|
|
365
|
+
The symptom when this is missed is quiet and easy to misattribute: subagents appear never to report back, which looks like a fault in this extension rather than in the host that disposed the listener.
|
|
366
|
+
|
|
290
367
|
## Runtime files
|
|
291
368
|
|
|
292
369
|
The main runtime files in this repository:
|
|
@@ -300,7 +377,7 @@ The main runtime files in this repository:
|
|
|
300
377
|
| `src/runs/background/subagent-runner.ts` | Detached async runner. |
|
|
301
378
|
| `src/runs/background/async-execution.ts` | Background launch support. |
|
|
302
379
|
| `src/runs/background/async-status.ts` | Status discovery and formatting for async runs. |
|
|
303
|
-
| `src/
|
|
380
|
+
| `src/workflows/scripted-workflow.ts` / `src/runs/foreground/subagent-executor.ts` | Scripted workflow orchestration and child launch routing. |
|
|
304
381
|
| `src/shared/settings.ts` | Chain behavior, instructions, and config helpers. |
|
|
305
382
|
| `src/runs/shared/worktree.ts` | Git worktree isolation. |
|
|
306
383
|
| `src/intercom/intercom-bridge.ts` | Runtime intercom bridge instructions and diagnostics. |
|
package/docs/missions.md
CHANGED
|
@@ -58,7 +58,7 @@ subagent({
|
|
|
58
58
|
})
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
After each parent turn, an idle goal mission sends one needs-attention notice with its title, remaining token budget, and next ready action. The action comes from `state.nextReadyAction`, `state.nextAction`, a state item with `status: "ready"`, an open decision, or linked-run state. A workflow can write `state.nextReadyAction` to tell the next notice exactly what work is ready. When the latest linked workflow has a
|
|
61
|
+
After each parent turn, an idle goal mission sends one needs-attention notice with its title, remaining token budget, and next ready action. The action comes from `state.nextReadyAction`, `state.nextAction`, a state item with `status: "ready"`, an open decision, or linked-run state. A workflow can write `state.nextReadyAction` to tell the next notice exactly what work is ready. When the latest linked workflow has a resumable retained child, the notice names that child as the `resume` target. Non-resumable retained children stay visible in `children.list` with their reason, but goal notices do not present them as resume targets. The extension never launches or replans goal work by itself.
|
|
62
62
|
|
|
63
63
|
Linked-run token totals are stored on each run and folded into mission `usage`. An active linked run suppresses notices. Reaching the token budget changes the goal status to `budget-exhausted` and stops notices without closing the mission or reporting success.
|
|
64
64
|
|
package/docs/observability.md
CHANGED
|
@@ -81,6 +81,22 @@ Without a TUI, `/subagents-fleet` retains the textual `subagent({ action: "statu
|
|
|
81
81
|
|
|
82
82
|
Use `/subagents-detach [run-id]` only for an active foreground single-subagent run you want to leave running without terminating; the eventual result remains available through status/wait.
|
|
83
83
|
|
|
84
|
+
Set `foregroundDetachShortcut` in `~/.pi/agent/extensions/subagent/config.json` to bind the same action to a shortcut. The running foreground card shows the configured shortcut beside its live-detail hint:
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"foregroundDetachShortcut": "ctrl+b"
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Pi binds `Ctrl+B` to editor cursor-left by default. The extension shortcut takes precedence, but Pi reports the conflict at startup. To reserve the key without that warning, override the editor action in `~/.pi/agent/keybindings.json`:
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"tui.editor.cursorLeft": "left"
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
84
100
|
If something feels misconfigured, run `/subagents-doctor` or ask: "Check whether subagents and intercom are set up correctly."
|
|
85
101
|
|
|
86
102
|
## Async run artifacts
|
|
@@ -148,15 +164,15 @@ Foreground and async runners share bounded child-protocol handling:
|
|
|
148
164
|
- `agent_end.willRetry` defers completion until the child settles.
|
|
149
165
|
- Current Pi builds use `agent_settled` as the terminal watermark; older builds retain the bounded terminal-message fallback.
|
|
150
166
|
|
|
151
|
-
##
|
|
167
|
+
## Workflow and debug artifacts
|
|
152
168
|
|
|
153
|
-
Each
|
|
169
|
+
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/`:
|
|
154
170
|
|
|
155
171
|
```text
|
|
156
172
|
<tmpdir>/pi-subagents-<scope>/chain-runs/{runId}/
|
|
157
173
|
```
|
|
158
174
|
|
|
159
|
-
A run directory may contain files such as `context.md`, `plan.md`, `progress.md`, and `parallel-{stepIndex}/.../output.md`. User-scoped temp
|
|
175
|
+
A run directory may contain files such as `context.md`, `plan.md`, `progress.md`, and `parallel-{stepIndex}/.../output.md`. User-scoped temp workflow artifact directories older than 24 hours are cleaned up on extension startup; project-local and explicit persistent roots are not age-scanned.
|
|
160
176
|
|
|
161
177
|
Debug artifacts live under `{sessionDir}/subagent-artifacts/`, `.pi/subagents/artifacts/` for project-scoped runs, or a user-scoped temp artifact directory. Single-run relative `output` files are saved under `{artifactsDir}/outputs/{runId}/` unless `singleRunOutputBaseDir` is configured. Per task you may see:
|
|
162
178
|
|
|
@@ -171,7 +187,7 @@ For npm package projects, project-scoped artifacts need a `.npmignore` rule (or
|
|
|
171
187
|
|
|
172
188
|
## Sessions
|
|
173
189
|
|
|
174
|
-
Session files are stored under a per-run session directory. With `context: "fork"`, each child starts with `--session <branched-session-file>` produced from the parent's current leaf. That is a real session fork, not an injected summary.
|
|
190
|
+
Session files are stored under a per-run session directory. With `context: "fork"`, each child starts with `--session <branched-session-file>` produced from the parent's current leaf. That is a real session fork, not an injected summary. An omitted launch `context` that resolves through `defaultContext: fork` uses the same branch when the parent session file and current leaf exist, and otherwise starts fresh.
|
|
175
191
|
|
|
176
192
|
## Completion notifications
|
|
177
193
|
|
package/docs/tool-reference.md
CHANGED
|
@@ -4,6 +4,8 @@ Parameters and actions for the `subagent` tool. These are what the LLM passes wh
|
|
|
4
4
|
|
|
5
5
|
## Execution examples
|
|
6
6
|
|
|
7
|
+
Chaining is code-driven through `workflowScript`. Use `await runs.run(...)` for sequential steps and `await runs.all([{ key, agent, task }, ...])` for ordinary parallel fanout. 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.
|
|
8
|
+
|
|
7
9
|
```js
|
|
8
10
|
// One child; return the child promise explicitly
|
|
9
11
|
{ workflowScript: `return runs.run("main", { agent: "scout", task: "Analyze the auth flow" })` }
|
|
@@ -31,19 +33,20 @@ Parameters and actions for the `subagent` tool. These are what the LLM passes wh
|
|
|
31
33
|
| `agent` | string | - | Agent target for management actions. Workflow child agents are set inside `runs.run` or `runs.all`. |
|
|
32
34
|
| `action` | string | - | Agent management (including `guide`, `children.list`, and `refine`/`refine.show`/`refine.rollback`), mission (`mission.create/list/show/update/resolve-decision/attach-run/close`), Herdr inspector (`inspector.open/status/close`), status/control, schedule, watchdog, or doctor action. |
|
|
33
35
|
| `topic` | `overview \| workflows \| agents \| missions \| observability \| tool-reference \| configuration \| models \| watchdog \| extension-api` | `overview` | Packaged guide topic for `action: "guide"`. |
|
|
34
|
-
| `
|
|
35
|
-
| `
|
|
36
|
-
| `context` | `fresh \| fork` | per-agent default or `fresh` | Explicit `fresh` or `fork` overrides every workflow child. When omitted, each child agent uses its own `defaultContext`; `fork` creates real branched sessions from the parent leaf. Packaged `worker`, `oracle`, and `advisor` default to `fork`. |
|
|
36
|
+
| `config` | object/string | - | Agent config for management create/update. |
|
|
37
|
+
| `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`; `"fork"` creates a real branched session when the parent session file and current leaf exist, otherwise it falls back to `fresh`. Packaged `worker`, `oracle`, and `advisor` default to `fork`. |
|
|
37
38
|
| `missionId` | string | - | Attach a workflow to an existing project mission instead of creating its default enclosing mission. |
|
|
38
39
|
| `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. |
|
|
39
40
|
| `handoffPath` | string | - | Aggregate handoff manifest required by `action: "worktree.discard"`. |
|
|
40
|
-
| `focus` | boolean |
|
|
41
|
+
| `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`. |
|
|
41
42
|
| `view` | `fleet \| transcript` | - | Optional `status` view for the active fleet surface or transcript tail inspection. |
|
|
42
43
|
| `lines` | number | `80` | Maximum transcript lines for `action: "status", view: "transcript"`; capped at 500. |
|
|
43
44
|
| `agentScope` | `user \| project \| both` | `both` | Agent discovery scope. Project wins on collisions. |
|
|
44
|
-
| `async` | boolean | default-on | Background execution. Workflows default to background
|
|
45
|
-
| `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. |
|
|
45
|
+
| `async` | boolean | default-on | Background execution. Workflows default to background. `async:false` blocks the parent until completion. |
|
|
46
|
+
| `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. |
|
|
47
|
+
| `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. |
|
|
46
48
|
| `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. |
|
|
49
|
+
| `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 `subagent_wait` are exempt. |
|
|
47
50
|
| `turnBudget` | object | none | Optional assistant-turn budget `{ maxTurns, graceTurns }`. At `maxTurns` the child is warned to wrap up. After the grace window (default 1), termination occurs at the next assistant boundary; a response that starts tool work records `termination-deferred` until a later boundary. Partial output is returned on abort. |
|
|
48
51
|
| `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. |
|
|
49
52
|
| `usageBudget` | object | none | Optional root-only reported-usage budget `{ tokens?: { soft?, hard }, costUsd?: { soft?, hard } }`. Soft limits are status-only. Hard limits prevent later child launches after reported usage is reconciled; already-running children are not stopped and no reservations are made. |
|
|
@@ -64,11 +67,34 @@ Bound writer work with a narrow task and an outer `timeoutMs` or `maxRuntimeMs`
|
|
|
64
67
|
|
|
65
68
|
### Fork context details
|
|
66
69
|
|
|
67
|
-
`context: "fork"` fails fast when the parent session is not persisted, the current leaf is missing, or the branched child session cannot be created.
|
|
70
|
+
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.
|
|
71
|
+
|
|
72
|
+
When the inherited transcript contains signed Anthropic `thinking` / `redacted_thinking` blocks, `pi-subagents` strips those provider-private blocks from the forked child session. It forces thinking `off` only when the child's effective primary or fallback model resolves through the model registry to the Anthropic provider or `anthropic-messages` API; unresolved models are treated conservatively. The result reports every affected child, including on failed runs. Use `context: "fresh"` when an Anthropic child needs thinking. Explicit `context: "fork"` never silently downgrades to `fresh`.
|
|
73
|
+
|
|
74
|
+
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.
|
|
75
|
+
|
|
76
|
+
### Workflow steering
|
|
68
77
|
|
|
69
|
-
|
|
78
|
+
`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`.
|
|
70
79
|
|
|
71
|
-
|
|
80
|
+
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. This slice reuses the foreground and async steering transports and disables steering recovery.
|
|
81
|
+
|
|
82
|
+
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.
|
|
83
|
+
|
|
84
|
+
```js
|
|
85
|
+
{ workflowScript: `
|
|
86
|
+
let pending = [
|
|
87
|
+
{ key: "writer", promise: runs.run("writer", { agent: "worker", task: "Draft the fix" }).then((result) => ({ key: "writer", result })) },
|
|
88
|
+
{ key: "reviewer", promise: runs.run("reviewer", { agent: "reviewer", task: "Review likely risks" }).then((result) => ({ key: "reviewer", result })) }
|
|
89
|
+
];
|
|
90
|
+
const first = await Promise.race(pending.map((child) => child.promise));
|
|
91
|
+
pending = pending.filter((child) => child.key !== first.key);
|
|
92
|
+
const target = pending[0];
|
|
93
|
+
const receipt = await runs.steer(target.key, "Use this early review:\n" + first.result.output, { mode: "auto" });
|
|
94
|
+
const rest = await Promise.all(pending.map((child) => child.promise));
|
|
95
|
+
return { first: first.key, rest: rest.map((child) => child.key), receipt };
|
|
96
|
+
` }
|
|
97
|
+
```
|
|
72
98
|
|
|
73
99
|
### Output mode details
|
|
74
100
|
|
|
@@ -78,23 +104,16 @@ In workflowScript, give each child an explicit output path when later script ste
|
|
|
78
104
|
|
|
79
105
|
Workflows get `await state.get(key)` and `await state.set(key, value)` through their default or explicit mission. Use them to share durable JSON values across later workflows attached with the same `missionId`. Each `set` takes the state-file lock and merges its key with the latest on-disk state. Missing keys return `undefined`, and the complete state file has a strict 256 KiB limit. `mission:false` workflows have no `state` global.
|
|
80
106
|
|
|
81
|
-
### Prompt fragments
|
|
82
|
-
|
|
83
|
-
Use `await prompts.render(ref, vars?)` to render reusable plain task text. Refs require an explicit scope: `package:<name>` reads the installed package `prompts/` directory, `user:<name>` reads the Pi agent `prompts/` directory, and `project:<name>` reads the current workflow project's config `prompts/` directory. Each ref names a top-level `<name>.md` file. Frontmatter is removed. Scalar string, number, and boolean variables replace matching `{{name}}` placeholders. Unknown placeholders stay unchanged.
|
|
84
|
-
|
|
85
|
-
Rendering only returns text to the sandbox. It does not give the script filesystem access and does not change child launch parameters, worktree capture, or cleanup. Pass the rendered result explicitly as `task`.
|
|
86
|
-
|
|
87
107
|
### Retained children
|
|
88
108
|
|
|
89
|
-
Completed workflow children from the current parent session stay addressable as retained children. `{ action: "children.list" }` lists up to the last 10 with their run ids. A later workflow continues
|
|
109
|
+
Completed workflow children from the current parent session stay addressable as retained children. `{ action: "children.list" }` lists up to the last 10 with their run ids and explicit `resumable` or `not resumable` state. Resume only rows reported `resumable`; if no row is resumable, start a same-role fallback challenge and label it as fallback. A later workflow continues a resumable child by passing `resume` instead of `agent`:
|
|
90
110
|
|
|
91
111
|
```js
|
|
92
112
|
{ workflowScript: `
|
|
93
113
|
let writer = await runs.run("implement", { agent: "worker", task: "Implement the accepted contract" });
|
|
94
114
|
for (const pass of [1, 2]) {
|
|
95
115
|
if (!writer.runId) throw new Error("writer did not return a retained run id");
|
|
96
|
-
|
|
97
|
-
writer = await runs.run("followup-" + pass, { resume: writer.runId, task });
|
|
116
|
+
writer = await runs.run("followup-" + pass, { resume: writer.runId, task: "Revisit pass " + pass + ": " + writer.output });
|
|
98
117
|
}
|
|
99
118
|
return writer;
|
|
100
119
|
` }
|
|
@@ -102,6 +121,8 @@ Completed workflow children from the current parent session stay addressable as
|
|
|
102
121
|
|
|
103
122
|
Inside `workflowScript`, `await runs.run(key, { resume, task })` waits for the revived child to finish and returns its completed output and new `runId`. Each resume can return a new retained run id, so loops must continue from the latest returned `runId`. Top-level `{ action: "resume" }` remains detached and returns a background-run receipt.
|
|
104
123
|
|
|
124
|
+
For a simple implementation challenge outside a workflow script, send the challenge through `subagent({ action: "resume", id: "<retained-writer-run>", message: "Reconsider the implementation and make any better current-scope change." })` only when `children.list` reports that retained writer as `resumable`. If no retained writer is resumable, start a same-role fallback challenge and record why it is a fallback. Use workflow `runs.run({ resume })` only when the script must await the revived writer output before the next step. Do not use `steer` as the sole challenge action for a completed retained child; `steer` with `mode: "follow_up"` only queues text for the next `resume`.
|
|
125
|
+
|
|
105
126
|
`resume` and `agent` are mutually exclusive. The revived child keeps its stored agent, model, and tool contract. `gate` is rejected on retained resume items because resume uses the retained child contract.
|
|
106
127
|
|
|
107
128
|
## Management actions
|
|
@@ -110,7 +131,7 @@ Inside `workflowScript`, `await runs.run(key, { resume, task })` waits for the r
|
|
|
110
131
|
|
|
111
132
|
`{ action: "guide" }` reads the packaged `README.md` from the installed version. Pass `topic` to read its packaged `docs/<topic>.md` file instead. Valid topics are `overview`, `workflows`, `agents`, `missions`, `observability`, `tool-reference`, `configuration`, `models`, `watchdog`, and `extension-api`. Unknown topics list the valid values and do not change files. Use `/subagents-guide [topic]` for the slash equivalent.
|
|
112
133
|
|
|
113
|
-
Agent definitions are not loaded into context by default. Management actions let the LLM discover, inspect, create, update, and delete agents
|
|
134
|
+
Agent definitions are not loaded into context by default. Management actions let the LLM discover, inspect, create, update, and delete agents at runtime. An unknown action returns safe next steps (`status` and `list`) and may suggest a close non-destructive action. Destructive actions are only named for a near-complete one-character typo, and suggestions never execute an action.
|
|
114
135
|
|
|
115
136
|
```ts
|
|
116
137
|
{ action: "list" }
|
|
@@ -119,7 +140,6 @@ Agent definitions are not loaded into context by default. Management actions let
|
|
|
119
140
|
{ action: "models" }
|
|
120
141
|
{ action: "models", agent: "reviewer" }
|
|
121
142
|
{ action: "get", agent: "code-analysis.scout" }
|
|
122
|
-
{ action: "get", chainName: "review-pipeline" }
|
|
123
143
|
|
|
124
144
|
{ action: "create", config: {
|
|
125
145
|
name: "Code Scout",
|
|
@@ -143,22 +163,11 @@ Agent definitions are not loaded into context by default. Management actions let
|
|
|
143
163
|
progress: true
|
|
144
164
|
}}
|
|
145
165
|
|
|
146
|
-
{ action: "create", config: {
|
|
147
|
-
name: "review-pipeline",
|
|
148
|
-
description: "Scout then review",
|
|
149
|
-
scope: "project",
|
|
150
|
-
steps: [
|
|
151
|
-
{ agent: "scout", task: "Scan {task}", output: "context.md" },
|
|
152
|
-
{ agent: "reviewer", task: "Review {previous}", reads: ["context.md"] }
|
|
153
|
-
]
|
|
154
|
-
}}
|
|
155
166
|
|
|
156
167
|
{ action: "update", agent: "code-analysis.scout", config: { model: "openai/gpt-4o" } }
|
|
157
168
|
{ action: "update", agent: "code-analysis.scout", config: { acceptance: "" } } // clear the frontmatter default
|
|
158
169
|
{ action: "update", agent: "code-analysis.scout", config: { acceptanceRole: false } } // restore inferred name fallback
|
|
159
|
-
{ action: "update", chainName: "review-pipeline", config: { steps: [...] } }
|
|
160
170
|
{ action: "delete", agent: "scout" }
|
|
161
|
-
{ action: "delete", chainName: "review-pipeline" }
|
|
162
171
|
|
|
163
172
|
{ action: "eject", agent: "reviewer" }
|
|
164
173
|
{ action: "eject", agent: "reviewer", agentScope: "project" }
|
|
@@ -198,9 +207,6 @@ subagent({ action: "resume", id: "<nested-run-id>", message: "follow-up for a ne
|
|
|
198
207
|
subagent({ action: "steer", id: "<run-id>", message: "guidance for the running child" })
|
|
199
208
|
subagent({ action: "steer", id: "<run-id>", mode: "follow_up", message: "check this after the current turn" })
|
|
200
209
|
subagent({ action: "steer", id: "<run-id>", index: 1, mode: "auto", message: "guidance for child 2" })
|
|
201
|
-
subagent({ action: "append-step", id: "<run-id>", step: { agent: "worker", task: "Continue from {previous}" } })
|
|
202
|
-
subagent({ action: "approve-checkpoint", id: "<run-id>" })
|
|
203
|
-
subagent({ action: "reject-checkpoint", id: "<run-id>" })
|
|
204
210
|
subagent({ action: "doctor" })
|
|
205
211
|
```
|
|
206
212
|
|
|
@@ -238,16 +244,12 @@ subagent({ action: "doctor" })
|
|
|
238
244
|
|
|
239
245
|
`steer` waits up to three seconds for a correlated child-Pi input acceptance and returns a request id with `delivered`, `scheduled`, `pending`, `partial`, `recovered`, or `failed` plus per-child states. The receipt also has `deliveryStatus: "delivered" | "queued"`. Delivery means Pi accepted the user message, not model compliance. A pending indexed child returns `scheduled`.
|
|
240
246
|
|
|
241
|
-
The optional `mode` is `steer` by default and keeps the current interrupt behavior. `follow_up` waits for the next turn boundary. `auto` queues during an active turn and delivers immediately between turns. The bounded FIFO holds 20 messages and returns a clear error when full. Terminal details report queued messages that the run did not deliver. A `follow_up` sent to a completed retained workflow child becomes the first brief for its next `resume
|
|
247
|
+
The optional `mode` is `steer` by default and keeps the current interrupt behavior. `follow_up` waits for the next turn boundary. `auto` queues during an active turn and delivers immediately between turns. The bounded FIFO holds 20 messages and returns a clear error when full. Terminal details report queued messages that the run did not deliver. 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.
|
|
242
248
|
|
|
243
249
|
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.
|
|
244
250
|
|
|
245
251
|
The persisted `steering` ledger retains 20 requests and replaces the old `steerCount`/`lastSteerAt` fields.
|
|
246
252
|
|
|
247
|
-
### append-step
|
|
248
|
-
|
|
249
|
-
`append-step` requires `legacyChainControls: true`. The default registered model-facing schema omits this legacy control surface. When enabled, it accepts exactly one `step` object for an existing durable chain for a top-level async chain whose status is still `running`. The step is persisted in the run directory and becomes eligible only after the chain's already-queued steps finish. Completed, failed, rejected, paused, foreground, single, and non-chain runs reject appends.
|
|
250
|
-
|
|
251
253
|
## Acceptance gates
|
|
252
254
|
|
|
253
255
|
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.
|
|
@@ -315,6 +317,20 @@ The parser canonicalizes known enum synonyms, snake_case report keys and wrapper
|
|
|
315
317
|
|
|
316
318
|
Acceptance fences are removed from normal output artifacts, while the raw child transcript remains intact and per-child metadata stores the complete acceptance ledger and parsed report. Explicit failed gates fail the run. Inferred gates remain observable without failing the run.
|
|
317
319
|
|
|
320
|
+
## Orca progress tabs (experimental observer)
|
|
321
|
+
|
|
322
|
+
Orca progress tabs are a global, opt-in observer, not an agent runner. Enable them in the extension config:
|
|
323
|
+
|
|
324
|
+
```json
|
|
325
|
+
{ "orcaProgressTabs": { "enabled": true } }
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Every foreground or background child keeps running through its normal native Pi or `external-cli` path. For each logical child, the observer asks Orca to create a background terminal tab in that child's current worktree and mirrors progress into it. Titles receive a persistent worktree-local sequence number, including across separate workflow calls. Creates for the same worktree are serialized in that sequence so tabs appear to the right in order (`1`, then `2`, then `3`) instead of racing. Model/startup retries reuse the same tab. Parallel and chain children each receive their own tab; attaching an already-running async root does not create a duplicate. Terminal control sequences are removed at the viewer sink across read boundaries. Each mirror is capped at 1 MiB and truncates when the cap or stream backpressure is reached. After the child finishes, its viewer returns to the terminal shell instead of ending the terminal session, so the tab and scrollback remain until the user closes them. Successful native Pi children with a known session append a safely quoted removal command for the exact verified session path; unsuccessful and sessionless children append only their terminal status.
|
|
329
|
+
|
|
330
|
+
The observer supports macOS and Linux and is disabled on Windows. It requires executable `orca` on `PATH` (or `PI_SUBAGENT_ORCA_BINARY`) and a running Orca runtime that recognizes the child cwd. Availability and tab creation are best-effort: failures never fail, stop, or delay the subagent. Set `orcaProgressTabs.enabled` to `false` to guarantee that no Orca command or tab is created.
|
|
331
|
+
|
|
332
|
+
Agent profile `runner.type` supports native Pi (the default), `external-cli`, and `external-job`. Orca is intentionally not a profile runner and does not own subagent execution, completion, cancellation, artifacts, or result delivery.
|
|
333
|
+
|
|
318
334
|
## External CLI agent profiles
|
|
319
335
|
|
|
320
336
|
Agent profiles can opt into a local one-shot command instead of a Pi child. External runners add no install dependency, but the configured executable must exist at runtime. They are async-only, receive one combined system/task prompt over stdin, and use argv arrays without a shell:
|