pi-subagents 0.74.0 → 0.76.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 +83 -0
- package/docs/agents.md +15 -5
- package/docs/configuration.md +14 -2
- package/docs/extension-api.md +2 -2
- package/docs/missions.md +27 -3
- package/docs/models.md +6 -2
- package/docs/observability.md +8 -3
- package/docs/tool-reference.md +48 -8
- package/docs/workflows.md +3 -3
- package/package.json +2 -1
- package/src/agents/agent-memory.js +3 -3
- package/src/agents/agent-memory.js.map +1 -1
- package/src/agents/agent-serializer.d.ts.map +1 -1
- package/src/agents/agent-serializer.js +8 -1
- package/src/agents/agent-serializer.js.map +1 -1
- package/src/agents/agents.d.ts +1 -0
- package/src/agents/agents.d.ts.map +1 -1
- package/src/agents/agents.js +137 -95
- package/src/agents/agents.js.map +1 -1
- package/src/api/preflight.d.ts +1 -1
- package/src/api/preflight.d.ts.map +1 -1
- package/src/api/preflight.js +44 -1
- package/src/api/preflight.js.map +1 -1
- package/src/extension/config.d.ts.map +1 -1
- package/src/extension/config.js +4 -1
- package/src/extension/config.js.map +1 -1
- package/src/extension/index.d.ts.map +1 -1
- package/src/extension/index.js +62 -22
- package/src/extension/index.js.map +1 -1
- package/src/extension/public-execution.d.ts.map +1 -1
- package/src/extension/public-execution.js +4 -0
- package/src/extension/public-execution.js.map +1 -1
- package/src/extension/reply-workflow-script.d.ts.map +1 -1
- package/src/extension/reply-workflow-script.js +21 -12
- package/src/extension/reply-workflow-script.js.map +1 -1
- package/src/extension/rpc.js +1 -1
- package/src/extension/rpc.js.map +1 -1
- package/src/extension/schemas.d.ts +2 -1
- package/src/extension/schemas.d.ts.map +1 -1
- package/src/extension/schemas.js +13 -11
- package/src/extension/schemas.js.map +1 -1
- package/src/extension/tool-description.js +2 -2
- package/src/extension/tool-description.js.map +1 -1
- package/src/intercom/result-intercom.d.ts.map +1 -1
- package/src/intercom/result-intercom.js +2 -1
- package/src/intercom/result-intercom.js.map +1 -1
- package/src/missions/goal-driver.d.ts +1 -0
- package/src/missions/goal-driver.d.ts.map +1 -1
- package/src/missions/goal-driver.js +49 -34
- package/src/missions/goal-driver.js.map +1 -1
- package/src/missions/store.d.ts.map +1 -1
- package/src/missions/store.js +11 -1
- package/src/missions/store.js.map +1 -1
- package/src/profiles/profiles.d.ts +1 -1
- package/src/profiles/profiles.d.ts.map +1 -1
- package/src/profiles/profiles.js +29 -22
- package/src/profiles/profiles.js.map +1 -1
- package/src/runs/background/active-async-capacity.d.ts +15 -1
- package/src/runs/background/active-async-capacity.d.ts.map +1 -1
- package/src/runs/background/active-async-capacity.js +51 -6
- package/src/runs/background/active-async-capacity.js.map +1 -1
- package/src/runs/background/async-execution.d.ts +13 -0
- package/src/runs/background/async-execution.d.ts.map +1 -1
- package/src/runs/background/async-execution.js +89 -6
- package/src/runs/background/async-execution.js.map +1 -1
- package/src/runs/background/async-job-tracker.d.ts +4 -2
- package/src/runs/background/async-job-tracker.d.ts.map +1 -1
- package/src/runs/background/async-job-tracker.js +4 -3
- package/src/runs/background/async-job-tracker.js.map +1 -1
- package/src/runs/background/await-async-run.d.ts.map +1 -1
- package/src/runs/background/await-async-run.js +4 -2
- package/src/runs/background/await-async-run.js.map +1 -1
- package/src/runs/background/calendar-schedule.d.ts +27 -0
- package/src/runs/background/calendar-schedule.d.ts.map +1 -0
- package/src/runs/background/calendar-schedule.js +94 -0
- package/src/runs/background/calendar-schedule.js.map +1 -0
- package/src/runs/background/control-channel.d.ts +10 -0
- package/src/runs/background/control-channel.d.ts.map +1 -1
- package/src/runs/background/control-channel.js +51 -0
- package/src/runs/background/control-channel.js.map +1 -1
- package/src/runs/background/notify.d.ts +6 -0
- package/src/runs/background/notify.d.ts.map +1 -1
- package/src/runs/background/notify.js +24 -4
- package/src/runs/background/notify.js.map +1 -1
- package/src/runs/background/result-watcher.d.ts +5 -1
- package/src/runs/background/result-watcher.d.ts.map +1 -1
- package/src/runs/background/result-watcher.js +44 -7
- package/src/runs/background/result-watcher.js.map +1 -1
- package/src/runs/background/run-child-session.d.ts +2 -0
- package/src/runs/background/run-child-session.d.ts.map +1 -1
- package/src/runs/background/run-child-session.js +32 -7
- package/src/runs/background/run-child-session.js.map +1 -1
- package/src/runs/background/runner-aliases.d.ts +1 -0
- package/src/runs/background/runner-aliases.d.ts.map +1 -1
- package/src/runs/background/runner-aliases.js +6 -2
- package/src/runs/background/runner-aliases.js.map +1 -1
- package/src/runs/background/scheduled-runs.d.ts +5 -2
- package/src/runs/background/scheduled-runs.d.ts.map +1 -1
- package/src/runs/background/scheduled-runs.js +210 -56
- package/src/runs/background/scheduled-runs.js.map +1 -1
- package/src/runs/background/stale-run-reconciler.d.ts +1 -1
- package/src/runs/background/stale-run-reconciler.d.ts.map +1 -1
- package/src/runs/background/stale-run-reconciler.js +15 -2
- package/src/runs/background/stale-run-reconciler.js.map +1 -1
- package/src/runs/background/subagent-runner.d.ts +1 -0
- package/src/runs/background/subagent-runner.d.ts.map +1 -1
- package/src/runs/background/subagent-runner.js +58 -9
- package/src/runs/background/subagent-runner.js.map +1 -1
- package/src/runs/background/wait-completions.d.ts.map +1 -1
- package/src/runs/background/wait-completions.js +1 -0
- package/src/runs/background/wait-completions.js.map +1 -1
- package/src/runs/background/workflow-terminal-proof.d.ts +14 -1
- package/src/runs/background/workflow-terminal-proof.d.ts.map +1 -1
- package/src/runs/background/workflow-terminal-proof.js +41 -28
- package/src/runs/background/workflow-terminal-proof.js.map +1 -1
- package/src/runs/foreground/command-action.d.ts +13 -0
- package/src/runs/foreground/command-action.d.ts.map +1 -0
- package/src/runs/foreground/command-action.js +38 -0
- package/src/runs/foreground/command-action.js.map +1 -0
- package/src/runs/foreground/execution.d.ts.map +1 -1
- package/src/runs/foreground/execution.js +42 -8
- package/src/runs/foreground/execution.js.map +1 -1
- package/src/runs/foreground/foreground-history.d.ts.map +1 -1
- package/src/runs/foreground/foreground-history.js +1 -0
- package/src/runs/foreground/foreground-history.js.map +1 -1
- package/src/runs/foreground/subagent-executor.d.ts +2 -1
- package/src/runs/foreground/subagent-executor.d.ts.map +1 -1
- package/src/runs/foreground/subagent-executor.js +108 -17
- package/src/runs/foreground/subagent-executor.js.map +1 -1
- package/src/runs/shared/child-commands.d.ts +28 -0
- package/src/runs/shared/child-commands.d.ts.map +1 -0
- package/src/runs/shared/child-commands.js +250 -0
- package/src/runs/shared/child-commands.js.map +1 -0
- package/src/runs/shared/child-session.d.ts +4 -0
- package/src/runs/shared/child-session.d.ts.map +1 -1
- package/src/runs/shared/child-session.js +107 -22
- package/src/runs/shared/child-session.js.map +1 -1
- package/src/runs/shared/child-tool-plan.js +1 -1
- package/src/runs/shared/child-tool-plan.js.map +1 -1
- package/src/runs/shared/claude-code-adapter.d.ts +56 -0
- package/src/runs/shared/claude-code-adapter.d.ts.map +1 -1
- package/src/runs/shared/claude-code-adapter.js +138 -2
- package/src/runs/shared/claude-code-adapter.js.map +1 -1
- package/src/runs/shared/dynamic-fanout.js +1 -1
- package/src/runs/shared/dynamic-fanout.js.map +1 -1
- package/src/runs/shared/external-cli-preflight.d.ts +10 -0
- package/src/runs/shared/external-cli-preflight.d.ts.map +1 -1
- package/src/runs/shared/external-cli-preflight.js +50 -6
- package/src/runs/shared/external-cli-preflight.js.map +1 -1
- package/src/runs/shared/external-cli-runner.d.ts.map +1 -1
- package/src/runs/shared/external-cli-runner.js +4 -2
- package/src/runs/shared/external-cli-runner.js.map +1 -1
- package/src/runs/shared/mcp-direct-tool-allowlist.d.ts.map +1 -1
- package/src/runs/shared/mcp-direct-tool-allowlist.js +8 -5
- package/src/runs/shared/mcp-direct-tool-allowlist.js.map +1 -1
- package/src/runs/shared/model-resolution.d.ts +6 -2
- package/src/runs/shared/model-resolution.d.ts.map +1 -1
- package/src/runs/shared/model-resolution.js +9 -2
- package/src/runs/shared/model-resolution.js.map +1 -1
- package/src/runs/shared/parallel-handoff.d.ts +10 -4
- package/src/runs/shared/parallel-handoff.d.ts.map +1 -1
- package/src/runs/shared/parallel-handoff.js +53 -6
- package/src/runs/shared/parallel-handoff.js.map +1 -1
- package/src/runs/shared/parallel-utils.d.ts +2 -0
- package/src/runs/shared/parallel-utils.d.ts.map +1 -1
- package/src/runs/shared/parallel-utils.js.map +1 -1
- package/src/runs/shared/partial-output.d.ts +18 -0
- package/src/runs/shared/partial-output.d.ts.map +1 -0
- package/src/runs/shared/partial-output.js +29 -0
- package/src/runs/shared/partial-output.js.map +1 -0
- package/src/runs/shared/run-history.d.ts +47 -0
- package/src/runs/shared/run-history.d.ts.map +1 -1
- package/src/runs/shared/run-history.js +70 -0
- package/src/runs/shared/run-history.js.map +1 -1
- package/src/runs/shared/subagent-control.d.ts.map +1 -1
- package/src/runs/shared/subagent-control.js +30 -17
- package/src/runs/shared/subagent-control.js.map +1 -1
- package/src/runs/shared/subagent-prompt-runtime.d.ts +1 -0
- package/src/runs/shared/subagent-prompt-runtime.d.ts.map +1 -1
- package/src/runs/shared/subagent-prompt-runtime.js +12 -5
- package/src/runs/shared/subagent-prompt-runtime.js.map +1 -1
- package/src/runs/shared/worktree-cleanup-apply.d.ts +37 -0
- package/src/runs/shared/worktree-cleanup-apply.d.ts.map +1 -0
- package/src/runs/shared/worktree-cleanup-apply.js +179 -0
- package/src/runs/shared/worktree-cleanup-apply.js.map +1 -0
- package/src/runs/shared/worktree-cleanup-plan.d.ts +7 -0
- package/src/runs/shared/worktree-cleanup-plan.d.ts.map +1 -1
- package/src/runs/shared/worktree-cleanup-plan.js +41 -19
- package/src/runs/shared/worktree-cleanup-plan.js.map +1 -1
- package/src/runs/shared/worktree-lock.d.ts +6 -0
- package/src/runs/shared/worktree-lock.d.ts.map +1 -0
- package/src/runs/shared/worktree-lock.js +41 -0
- package/src/runs/shared/worktree-lock.js.map +1 -0
- package/src/runs/shared/worktree.d.ts +3 -0
- package/src/runs/shared/worktree.d.ts.map +1 -1
- package/src/runs/shared/worktree.js +9 -1
- package/src/runs/shared/worktree.js.map +1 -1
- package/src/shared/file-lease.d.ts +5 -0
- package/src/shared/file-lease.d.ts.map +1 -0
- package/src/shared/file-lease.js +101 -0
- package/src/shared/file-lease.js.map +1 -0
- package/src/shared/required-child-extensions.d.ts +16 -1
- package/src/shared/required-child-extensions.d.ts.map +1 -1
- package/src/shared/required-child-extensions.js +37 -5
- package/src/shared/required-child-extensions.js.map +1 -1
- package/src/shared/settings-file-lease.d.ts +5 -0
- package/src/shared/settings-file-lease.d.ts.map +1 -0
- package/src/shared/settings-file-lease.js +39 -0
- package/src/shared/settings-file-lease.js.map +1 -0
- package/src/shared/types.d.ts +16 -1
- package/src/shared/types.d.ts.map +1 -1
- package/src/shared/types.js +1 -1
- package/src/shared/types.js.map +1 -1
- package/src/slash/subagent-cost.d.ts.map +1 -1
- package/src/slash/subagent-cost.js +8 -4
- package/src/slash/subagent-cost.js.map +1 -1
- package/src/tui/render.d.ts +1 -1
- package/src/tui/render.d.ts.map +1 -1
- package/src/tui/render.js +11 -6
- package/src/tui/render.js.map +1 -1
- package/src/watchdog/diff-tool.js +2 -2
- package/src/watchdog/diff-tool.js.map +1 -1
- package/src/watchdog/register-main.js +1 -2
- package/src/watchdog/register-main.js.map +1 -1
- package/src/watchdog/runtime.d.ts +3 -3
- package/src/watchdog/runtime.d.ts.map +1 -1
- package/src/watchdog/runtime.js +7 -3
- package/src/watchdog/runtime.js.map +1 -1
- package/src/watchdog/settings.d.ts.map +1 -1
- package/src/watchdog/settings.js +22 -17
- package/src/watchdog/settings.js.map +1 -1
- package/src/workflows/workflow-receipt.js +2 -2
- package/src/workflows/workflow-receipt.js.map +1 -1
- package/src/workflows/workflow-resources.d.ts +4 -0
- package/src/workflows/workflow-resources.d.ts.map +1 -1
- package/src/workflows/workflow-resources.js +7 -4
- package/src/workflows/workflow-resources.js.map +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,89 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.76.0] - 2026-10-04
|
|
6
|
+
|
|
7
|
+
### Highlights
|
|
8
|
+
|
|
9
|
+
- Schedules can run every day or every week at a local time, in a timezone you choose, and on the weekdays you choose.
|
|
10
|
+
- `worktree.cleanup` can now delete the worktrees in a plan you reviewed and saved. Before, it could only list them.
|
|
11
|
+
- Background results are no longer lost in headless sessions (`pi -p`, SDK hosts) or when pi-web replaces a session it thinks is idle.
|
|
12
|
+
- Two Pi sessions running at the same time no longer overwrite each other's settings changes, schedules, or schedule history.
|
|
13
|
+
- A child that times out or crashes now returns the text it had written so far. A child with `outputSchema` now saves its structured result to the output file, not a closing sentence.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- Schedules accept `every: "day"` or `every: "week"` with a local `HH:mm` time and an IANA timezone, plus the weekdays to run on for weekly schedules. If a daylight-saving change skips the time, that run is skipped. If the time happens twice, it runs once. After a restart, the next run is calculated again from the local date. Overlap, catch-up, quiet, and mission options work the same as on other schedules. Thanks to [@quifox](https://github.com/quifox) for [#815](https://github.com/nicobailon/pi-subagents/issues/815).
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- `worktree.cleanup` can now apply a plan you saved and reviewed, once you authorize discarding those worktrees. Before it deletes anything, it checks Git state and ownership again. It keeps any worktree whose path now points outside the directory it was created in, any worktree that is locked or has been resumed, and any ignored files. It never deletes local branches, and each plan can be applied only once. Worktrees created before this version are always kept. Thanks to [@quifox](https://github.com/quifox) for [#1622](https://github.com/nicobailon/pi-subagents/issues/1622).
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- In a headless session (`pi -p`, or an SDK host without a UI), a background run's completion message could be lost. The turn could end before the message was sent, and the host then closed the session. Finished results are now handed to Pi before the turn ends, and Pi runs the completion turn next. Thanks to [@felores](https://github.com/felores) for [#2644](https://github.com/nicobailon/pi-subagents/issues/2644).
|
|
26
|
+
- pi-web could see a session as idle while a child's result was still on its way to the parent, so it could replace the session and lose the result. The session now counts as busy until Pi starts processing the result. Thanks to [@brettinternet](https://github.com/brettinternet) for [#2674](https://github.com/nicobailon/pi-subagents/pull/2674).
|
|
27
|
+
- A child with `outputSchema` and an `output` path wrote its final text to that file whenever the text was not empty. A short closing line such as "Enough; writing up." could replace the structured result. If the child returns a structured result and does not write the file itself, the file now holds that result as indented JSON. The child's returned output and output artifact show the same JSON. Thanks to [@tara-marchand](https://github.com/tara-marchand) for [#2672](https://github.com/nicobailon/pi-subagents/issues/2672).
|
|
28
|
+
- A child that timed out or crashed partway through a reply lost the text it was still writing. The parent now gets that text, labeled `Partial output before timeout:` or `Partial output before child error:`, and the result has `outputPartial: true`. The run still fails. The partial text is never saved as the output file and never counts as acceptance evidence. Runs that are stopped, interrupted, aborted, or exit with an ordinary error are unchanged. Thanks to [@yanqianglu](https://github.com/yanqianglu) for [#2653](https://github.com/nicobailon/pi-subagents/pull/2653) (#2549).
|
|
29
|
+
- An async workflow could keep its `maxActiveAsyncRunsPerSession` slot forever after it ended. This happened when one of its async children failed and there was no record that the child's process had exited, for example after a reload. The new `capacity.abandonedSlotReleaseAfterMs` setting frees the slot once the workflow has been over for longer than that time and each such child has failed, has no running runner, and has been inactive for a while. Set it to `false` to keep the slot. Thanks to [@jihunkim0](https://github.com/jihunkim0) for [#2658](https://github.com/nicobailon/pi-subagents/issues/2658).
|
|
30
|
+
- Two Pi sessions that changed extension settings at the same time could lose one of the changes. Agent override saves, `/subagents-load-profile`, and Watchdog setting changes now lock `settings.json.write-lock` beside the settings file while they read and save, so the second session waits for the first. If the settings directory is read-only, the lock cannot be created and the save fails with an error. Thanks to [@quifox](https://github.com/quifox) for [#2676](https://github.com/nicobailon/pi-subagents/pull/2676).
|
|
31
|
+
- Two sessions recording runs for the same schedule could each rewrite its run history from an old copy. A run could disappear from the history and its completion was never recorded, so the schedule stayed marked as running. History updates now use a short lock. Completion, restore, and delete now read the record of the run that is actually active. Thanks to [@quifox](https://github.com/quifox) for [#2677](https://github.com/nicobailon/pi-subagents/pull/2677).
|
|
32
|
+
- A session that lost the race to start a scheduled run could save its outdated copy of the schedule over the copy used by the session running it. That no longer happens. The session that lost still protects the active run from being deleted, and only postpones its own timer while the other session starts the run. Thanks to [@quifox](https://github.com/quifox) for [#2675](https://github.com/nicobailon/pi-subagents/pull/2675).
|
|
33
|
+
- If a session claimed a scheduled run but could not write the run id into the claim, for example because the disk was full, the empty claim file stayed behind. Every later run of that schedule was then skipped. The failed claim is now removed unless another session has already replaced it, and the original error is reported. Thanks to [@quifox](https://github.com/quifox) for [#2678](https://github.com/nicobailon/pi-subagents/pull/2678).
|
|
34
|
+
- On Windows, some file checks lost precision on large file ids, so two different files or folders could look like the same one. A schedule claim that failed to write could then delete another session's lock, and the agent-memory and worktree-cleanup checks could treat two directories as one. File ids are now compared exactly. ([#2679](https://github.com/nicobailon/pi-subagents/pull/2679), [#2685](https://github.com/nicobailon/pi-subagents/issues/2685))
|
|
35
|
+
- An agent that named a built-in MCP server with `_` in place of `-`, such as `mcp:codebase_memory` for the server `codebase-memory`, got none of the server's tools. The launch failed 10 seconds later saying the tools did not register. Both spellings now work, the same way Pi's MCP treats `-` and `_` as the same server. Thanks to [@trellis-zz](https://github.com/trellis-zz) for [#2668](https://github.com/nicobailon/pi-subagents/issues/2668).
|
|
36
|
+
- `workflow: true` failed with "found 0" when the model marked its script as plain ```` ```js ```` instead of ```` ```js workflow ````. If a reply has exactly one plain `js` block and no `js workflow` block, that block now runs. When no block is found, the error shows the exact opening line it looked for and suggests passing a script file path instead. Thanks to [@thetechpadawan](https://github.com/thetechpadawan) for [#2659](https://github.com/nicobailon/pi-subagents/issues/2659).
|
|
37
|
+
- `workflow: true` also said "found 0" when the model's reply reached the session with no text at all, which made it look as if the model had left out the script. Some providers drop the reply's visible text before Pi saves it. The error now says the reply text never arrived and suggests passing a script file path. ([#2669](https://github.com/nicobailon/pi-subagents/issues/2669))
|
|
38
|
+
- Creating an agent with a description longer than one line, or updating only the prompt of such an agent, saved only the first line of the description. The description is now saved as a YAML block, so every line is kept. Thanks to [@March-7](https://github.com/March-7) for [#2681](https://github.com/nicobailon/pi-subagents/pull/2681).
|
|
39
|
+
- When jobs leave a crowded async widget, it now shrinks to fit the jobs that remain instead of keeping blank rows. While the same jobs are listed, its height still stays fixed. Thanks to [@tara-marchand](https://github.com/tara-marchand) for [#2651](https://github.com/nicobailon/pi-subagents/issues/2651).
|
|
40
|
+
- The main watchdog no longer flags work you asked for mid-run as scope drift. Messages you send while the agent is running (steer or follow-up) now count as part of the task. Its review and `watchdog_diff` now also say that the list of changed paths covers the whole working tree. That list can include edits made before the session or by another session in the same checkout. Thanks to [@tara-marchand](https://github.com/tara-marchand) for [#2652](https://github.com/nicobailon/pi-subagents/issues/2652).
|
|
41
|
+
|
|
42
|
+
## [0.75.0] - 2026-10-02
|
|
43
|
+
|
|
44
|
+
### Highlights
|
|
45
|
+
|
|
46
|
+
- Background subagents work on Pi 1.0.0 again. In 0.74.0 they failed to start with "does not provide @earendil-works/pi-agent-core/node".
|
|
47
|
+
- The built-in `claude-code` and `claude-code-writer` agents can pick a Claude model and effort level per launch, for example `model: "claude-opus-5.5:high"`.
|
|
48
|
+
- On Windows, `claude-code`, `codex-exec`, and `cursor-agent` can now launch CLIs installed with npm.
|
|
49
|
+
- Children on a virtual model from an extension, such as a model router set as `subagents.defaultModel`, now launch and pass model verification.
|
|
50
|
+
- An invalid config value no longer silently turns off `authorityPolicy`, `permissions`, or `toolBudget`. Loading the config fails instead.
|
|
51
|
+
|
|
52
|
+
### Added
|
|
53
|
+
|
|
54
|
+
- The built-in `claude-code` and `claude-code-writer` agents accept a model and thinking level per launch or in agent frontmatter, and pass them to Claude Code as `--model` and `--effort`. Before, the model could only be set in your global Claude Code settings. `model: "claude-opus-5.5:high"` sets both; `model: ":high"` sets only the effort. An unknown model or level fails the launch and names it. `subagents.maxThinking` and an enforced `subagents.modelScope` apply to these launches; with an enforced scope, a launch that sets no model fails, because Claude Code's default model cannot be checked. Other external runners still reject both fields. See [agents](docs/agents.md). Thanks to [@lexxbyte](https://github.com/lexxbyte) for [#2602](https://github.com/nicobailon/pi-subagents/issues/2602).
|
|
55
|
+
- The parent can now manage a long `bash` command inside a native Pi child that is granted both `bash` and `subagent_command`: `command.status` shows it, `command.yield` lets the child continue while the command keeps running, and `command.cancel` stops only that command. The child can do the same itself with `bash`'s `yieldTimeMs` and the `subagent_command` tool. Commands use Pi's own shell and are stopped when the child exits, and a child that finishes with a command still running fails. Thanks to [@jiuai233](https://github.com/jiuai233) for [#2598](https://github.com/nicobailon/pi-subagents/pull/2598).
|
|
56
|
+
- Hosts that require child extensions with `registerRequiredChildExtensions` can pass `requireForAllRunners: true`. Only local Pi children can load those extensions, so with this flag a launch on an external CLI, an external job, or another machine is refused before it starts instead of running without them. The requirement stays with the run, including nested, resumed, and workflow children, even after the host removes its registration. Without the flag, external runners are skipped as before. See [agents](docs/agents.md). Thanks to [@doc-krieger](https://github.com/doc-krieger) for [#2639](https://github.com/nicobailon/pi-subagents/issues/2639).
|
|
57
|
+
- `schedule.create` accepts an existing `missionId`, so a scheduled workflow keeps the same mission state across runs and session restarts. Schedules with a mission are saved in a new format that older pi-subagents versions reject instead of silently dropping the mission; other schedules keep the old format. Thanks to [@quifox](https://github.com/quifox) for [#2616](https://github.com/nicobailon/pi-subagents/pull/2616).
|
|
58
|
+
- `asyncWidgetCollapsed: true` starts the async widget under the editor folded to one line. Clicking its header still unfolds it, and the default is unchanged. Thanks to [@unrelentingfox](https://github.com/unrelentingfox) for [#2621](https://github.com/nicobailon/pi-subagents/pull/2621).
|
|
59
|
+
|
|
60
|
+
### Changed
|
|
61
|
+
|
|
62
|
+
- CI now runs the clean-install test against both Pi `0.86.1` and `1.0.0`, as separate jobs with separate logs. Thanks to [@EightDoor](https://github.com/EightDoor) for [#2648](https://github.com/nicobailon/pi-subagents/pull/2648).
|
|
63
|
+
|
|
64
|
+
### Fixed
|
|
65
|
+
|
|
66
|
+
- Background subagents failed to start on Pi 1.0.0 with "does not provide @earendil-works/pi-agent-core/node", because Pi 1.0.0 no longer ships that module. They now start without it, and still fail if Pi includes the module but the file is missing. Thanks to [@albertgwo](https://github.com/albertgwo) for [#2634](https://github.com/nicobailon/pi-subagents/pull/2634).
|
|
67
|
+
- An invalid value for any config key no longer silently drops `authorityPolicy`, `permissions`, or `toolBudget`. If the config file sets any of them, loading now fails instead of continuing with an empty config that would allow what those settings forbid. (#2622)
|
|
68
|
+
- A required child extension (`registerRequiredChildExtensions`) that throws during `session_start` now stops the launch. Before, Pi only reported the error, so the child still started and could send model requests without the policy the extension was meant to set up. Ordinary extensions are unchanged: their startup errors are reported and the child still starts. Thanks to [@doc-krieger](https://github.com/doc-krieger) for reporting [#2639](https://github.com/nicobailon/pi-subagents/issues/2639).
|
|
69
|
+
- On Windows, external-CLI agents such as `claude-code`, `codex-exec`, and `cursor-agent` failed with `EINVAL` when the CLI was installed with npm, because npm installs it as a `.cmd` file that Node cannot start without a shell. The runner now runs the CLI's script with Node directly, still without a shell, and refuses any other `.cmd` or `.bat` file. Thanks to [@lexxbyte](https://github.com/lexxbyte) for tracing the cause in [#2631](https://github.com/nicobailon/pi-subagents/issues/2631).
|
|
70
|
+
- A child could not launch on a virtual model that an extension registers with `pi.registerVirtualModel()`, such as a router set as `subagents.defaultModel`. The child looked up its model before the extension's virtual models were registered, so the name matched a model on another provider and the launch failed on that provider's missing API key. Virtual models are now registered first, and a required extension whose registration fails stops the launch. Thanks to [@mauroziux](https://github.com/mauroziux) for [#2636](https://github.com/nicobailon/pi-subagents/pull/2636) (#2635).
|
|
71
|
+
- A child on a virtual model failed with `model_verification_failed` even when it ran correctly, because the check compared the launch model with the real model the router picked. For virtual models, the check now compares the child's selected model with the launch model. Other children are checked as before. Thanks to [@mauroziux](https://github.com/mauroziux) for reporting [#2637](https://github.com/nicobailon/pi-subagents/issues/2637) (#2635).
|
|
72
|
+
- A background chain whose `expand` step runs an external-CLI agent such as `claude-code` failed with "Dynamic chain step N parallel does not support field 'runner'". Each item now runs through that agent's runner, and its output is collected like any parallel child. Thanks to [@lexxbyte](https://github.com/lexxbyte) for reporting [#2629](https://github.com/nicobailon/pi-subagents/issues/2629).
|
|
73
|
+
- Saving builtin agent overrides no longer risks a half-written settings file: if the save is interrupted, the previous settings stay readable. Thanks to [@quifox](https://github.com/quifox) for [#2627](https://github.com/nicobailon/pi-subagents/pull/2627).
|
|
74
|
+
- Codex children now keep tool-call IDs in the `call_id|item_id` form when both parts are at most 64 characters and use only letters, digits, `_`, or `-`. Other IDs are shortened as before. Thanks to [@jtabke](https://github.com/jtabke) for [#2628](https://github.com/nicobailon/pi-subagents/pull/2628) (#2623).
|
|
75
|
+
- With Pi's built-in MCP, `mcp:` entries whose server or tool name contains `-`, such as `mcp:srv/get-item`, failed with "selects MCP tools that Pi's built-in MCP does not offer". pi-subagents now converts `-` to `_` the same way Pi names the tool, so you keep writing names as the server reports them. Thanks to [@sheurich](https://github.com/sheurich) for [#2607](https://github.com/nicobailon/pi-subagents/issues/2607).
|
|
76
|
+
- Some MCP clients, such as pi-claude-bridge, send `workflow: true` as the string `"true"`, which failed with an error asking for `workflow: true`. The string now works the same as the boolean (#2600).
|
|
77
|
+
- `action: "validate"` now checks workflow `args` against the same limits as a launch: 16 fields per object, 64 items per array, depth 8, and 16 KiB total. A script that validates no longer fails at launch because of its `args`. ([#2608](https://github.com/nicobailon/pi-subagents/issues/2608))
|
|
78
|
+
- The parent is now told about every tool call that stays open past the attention threshold, not only the first one in each child. Thanks to [@jiuai233](https://github.com/jiuai233) for [#2598](https://github.com/nicobailon/pi-subagents/pull/2598).
|
|
79
|
+
- Async runs now reach `run-history.jsonl`. Before, only foreground runs were recorded, so per-agent lookups missed every background launch. Single-step runs record the same row as a foreground run, multi-step runs record one row per child step, and a paused run records as `interrupted`, with a later resume recording again. Thanks to [@limin411](https://github.com/limin411) for [#2620](https://github.com/nicobailon/pi-subagents/pull/2620).
|
|
80
|
+
- `/subagent-cost` and the RPC `cost` method now count every round of a resumed foreground workflow child. Before, every round after the first was dropped as a duplicate, so totals came out low with no warning. Results recorded before this fix still undercount. Thanks to [@chagwood](https://github.com/chagwood) for [#2601](https://github.com/nicobailon/pi-subagents/issues/2601).
|
|
81
|
+
- `/subagent-cost` and the RPC `cost` method no longer log a missing-receipt error on every call for foreground workflows. An async workflow that has no receipt yet, such as one still running, is now listed as `Async child usage unavailable` instead of being left out of the total without notice. (#2614)
|
|
82
|
+
- The global mission list now takes title, status, update time, and latest run from each mission's own record, so a mission no longer looks out of date because its index entry is stale. Thanks to [@quifox](https://github.com/quifox) for [#2618](https://github.com/nicobailon/pi-subagents/pull/2618).
|
|
83
|
+
- Headless sessions now deliver goal-mission notices even when finishing background work fails. The failure is still reported to the caller. Thanks to [@quifox](https://github.com/quifox) for [#2605](https://github.com/nicobailon/pi-subagents/pull/2605).
|
|
84
|
+
- One mission with a damaged run status or state no longer stops continuation notices for healthy goal missions. The damaged mission is reported separately. Thanks to [@quifox](https://github.com/quifox) for [#2604](https://github.com/nicobailon/pi-subagents/pull/2604).
|
|
85
|
+
- On Linux, a runner process that exited but was not yet cleaned up by the system no longer leaves its run marked as running, or a workflow waiting for a result that will never arrive. Thanks to [@quifox](https://github.com/quifox) for [#2606](https://github.com/nicobailon/pi-subagents/pull/2606).
|
|
86
|
+
- Saved subagent profiles with an invalid `machine` value are now rejected when loaded or checked, before any settings are written. `machine: false` still clears a pin. Thanks to [@quifox](https://github.com/quifox) for [#2619](https://github.com/nicobailon/pi-subagents/pull/2619).
|
|
87
|
+
|
|
5
88
|
## [0.74.0] - 2026-09-30
|
|
6
89
|
|
|
7
90
|
### Highlights
|
package/docs/agents.md
CHANGED
|
@@ -110,12 +110,20 @@ The built-in `claude-code` and `claude-code-writer` profiles are the supported C
|
|
|
110
110
|
|
|
111
111
|
Both adapters own `claude -p` argv with stream JSON, strict empty MCP configuration, user-only setting sources, no session persistence, disabled slash commands, and disabled Chrome integration. The writer mode does not include Bash or any permission bypass. Neither mode uses `--bare`, which does not read normal OAuth/keychain authentication. Neither mode requires `--safe-mode`, which is absent from the installed 2.1.150 help. User profiles cannot add argv. Selecting the code-owned `claude-code-writer` adapter identity is the only way to opt into its write tools; the read-only adapter cannot be widened with user argv.
|
|
112
112
|
|
|
113
|
+
A `model`, and a thinking level carried on it as a `:level` suffix, are the one thing these two adapters do accept from a launch; the same two fields work in agent frontmatter. They become Claude Code's own `--model` and `--effort` rather than a Pi child model, so one launch can pin `claude-opus-5.5` with `high` effort without changing the operator's global Claude Code settings. The model must be an alias or a model id, and both values are checked before launch, so an unusable value fails the launch by name and no other token can be added.
|
|
114
|
+
|
|
115
|
+
The level travels as the same `:level` suffix Pi children use, on the `model` field: `model: "claude-opus-5.5:high"` pins the model and the effort, and `model: ":high"` asks for the effort alone. A suffix wins over the frontmatter `thinking` value. `off` passes no `--effort` and `minimal` maps to `low`, and a launch that requests no level leaves the flag out, so Claude Code's own default effort applies and this repository cannot bound it. `off` therefore means "pass no flag", not "no thinking".
|
|
116
|
+
|
|
117
|
+
`subagents.maxThinking` applies here the way it does to a native Pi child: a request above the ceiling fails before the child starts, compared on the requested level rather than on the effort it maps to. An enforced `subagents.modelScope` applies too, checked against the id that will reach the CLI; a launch that pins no model at all fails closed, because the CLI's own default cannot be checked. Every other external runner still refuses both fields.
|
|
118
|
+
|
|
113
119
|
Run it asynchronously:
|
|
114
120
|
|
|
115
121
|
```text
|
|
116
122
|
Use claude-code to analyze this handoff without editing files.
|
|
117
123
|
|
|
118
124
|
Use claude-code-writer to make the requested file changes.
|
|
125
|
+
|
|
126
|
+
Use claude-code-writer with model claude-sonnet-5:high to make the requested file changes.
|
|
119
127
|
```
|
|
120
128
|
|
|
121
129
|
The adapter validates `claude --version` and `claude --help` only when a run launches. Discovery, list, status, and native Pi launches do not execute Claude Code or probe authentication. A capabilities listing performs only a passive PATH/PATHEXT/X_OK lookup and exposes the command as `runner.available`; that does not prove authentication or launch compatibility. JSONL, stderr, and stdout are untrusted. A run succeeds only after bounded valid JSONL contains exactly one successful terminal `result` with non-empty final text. Missing or revoked local authentication, limit stops, malformed JSON, duplicate terminal results, and EOF before a terminal result fail closed.
|
|
@@ -351,8 +359,8 @@ Field notes:
|
|
|
351
359
|
| `allowedAgents` | Restricts which canonical, case-sensitive agent names this agent may launch. Omitted adds no restriction; an empty list denies every descendant launch. This only narrows an existing nesting grant: `tools: subagent` or `allowNestedSubagents: true` is still required. Inherited/runtime allowlists are intersected and cannot be widened. |
|
|
352
360
|
| `extensions` | Omitted means a background child loads the parent's ambient extensions; empty means no ambient extensions; list values load exactly those extensions. Foreground children never load ambient extensions, so for them only listed values apply. |
|
|
353
361
|
| `subagentOnlyExtensions` | Extension paths loaded only in this agent's child sessions. Tools registered there are unavailable to the main agent unless also installed through normal Pi extension configuration. |
|
|
354
|
-
| `model` | Default model. Bare ids prefer the current provider when possible, then unique registry matches. |
|
|
355
|
-
| `thinking` | Appended as a `:level` suffix at runtime unless a suffix is already present. |
|
|
362
|
+
| `model` | Default model. Bare ids prefer the current provider when possible, then unique registry matches. On the two Claude Code adapters this is Claude Code's own alias or id, and a trailing `:level` picks the effort. |
|
|
363
|
+
| `thinking` | Appended as a `:level` suffix at runtime unless a suffix is already present. On the two Claude Code adapters it becomes `--effort`, and a suffix on `model` wins over it. |
|
|
356
364
|
| `systemPromptMode` | `replace` by default; `append` keeps Pi's base prompt. |
|
|
357
365
|
| `inheritProjectContext` | Keeps or strips inherited repository instruction blocks. |
|
|
358
366
|
| `inheritGlobalContext` | Keeps or strips the operator's global context file from the Pi config agent directory (e.g. `~/.pi/agent/AGENTS.md`). It has an effect only when `inheritProjectContext` is `true`; otherwise all context files are already disabled. Defaults to `false`. |
|
|
@@ -375,9 +383,11 @@ Field notes:
|
|
|
375
383
|
|
|
376
384
|
### Required host extensions
|
|
377
385
|
|
|
378
|
-
Hosts can import `registerRequiredChildExtensions` from `pi-subagents/required-child-extensions` and register `{ sessionId, extensions: [{ id, path }] }`. Paths resolve to existing files and are canonicalized into an immutable launch snapshot; bounded safe IDs appear in evidence instead of paths. One registration is allowed per parent session until its idempotent `dispose()` runs, normally on `session_shutdown`.
|
|
386
|
+
Hosts can import `registerRequiredChildExtensions` from `pi-subagents/required-child-extensions` and register `{ sessionId, extensions: [{ id, path }], requireForAllRunners? }`. Paths resolve to existing files and are canonicalized into an immutable launch snapshot; bounded safe IDs appear in evidence instead of paths. One registration is allowed per parent session until its idempotent `dispose()` runs, normally on `session_shutdown`.
|
|
387
|
+
|
|
388
|
+
Required paths follow ordinary extension resolution and survive agent defaults and `extensions: []` across native foreground, detached, nested, and recovery launches. A `capabilityCeiling.denyExtensions` conflict or required load/provider-registration failure rejects before model resolution. An error a required extension reports while the child binds its extensions, such as a throwing `session_start` handler, disposes the child and rejects the launch before its first prompt. By default external runners are excluded (they run without the required extensions), and status/watch paths do not query the registry.
|
|
379
389
|
|
|
380
|
-
|
|
390
|
+
With `requireForAllRunners: true`, a launch that resolves to an external CLI runner, an external-job runner, or a machine placement is rejected before a runner process, provider job, or remote owner starts, because only local native Pi children load these extensions. The requirement stays with the run through nested, detached, resumed, recovered, workflow-script, and appended launches, so disposing the registration or setting `extensions: []` cannot drop it. Launch preflight previews the runner-type rejection for the session's current registration; machine placement and requirements retained by nested or resumed runs are checked at launch.
|
|
381
391
|
|
|
382
392
|
Successful completion is determined by observable gates such as process outcome, required outputs, explicit acceptance, verification commands, independent review, and staged-index integrity. Best-effort mutation observations remain diagnostic: unchanged or unknown evidence does not fail a run, and observed changes do not prove correctness.
|
|
383
393
|
|
|
@@ -462,7 +472,7 @@ Examples:
|
|
|
462
472
|
- `allowNestedSubagents: true` with `tools` omitted: normal builtin tools (and, for background children, ambient extensions) remain inherited, and the child-safe nested `subagent` runtime is added.
|
|
463
473
|
- `tools: read, fixture_search` plus `subagentOnlyExtensions: ./tools/fixture-search.ts`: the provider loads only in this agent's child sessions, and the registered `fixture_search` name survives the strict allowlist.
|
|
464
474
|
|
|
465
|
-
When [pi-mcp-adapter](https://github.com/nicobailon/pi-mcp-adapter) is not installed and Pi has built-in MCP (Pi 0.99 or later), `mcp:server` and `mcp:server/tool` entries resolve against Pi's built-in MCP. The child connects the servers in Pi's `mcp.json` (the project's `.pi/mcp.json` only when the parent trusts the project) and gets only the selected tools, as direct tools, whatever exposure the server is configured with; tools configured as `hidden` cannot be selected. Servers that an extension adds with `pi.registerMcpServer()` work in background children, because the registering extension loads there too. They do not work in foreground children, which load no ambient extensions, so a foreground launch that selects one fails and says to use `async: true`. The launch fails if a selected tool does not register in the child within 10 seconds.
|
|
475
|
+
When [pi-mcp-adapter](https://github.com/nicobailon/pi-mcp-adapter) is not installed and Pi has built-in MCP (Pi 0.99 or later), `mcp:server` and `mcp:server/tool` entries resolve against Pi's built-in MCP. Name the server and tool as the server reports them: `mcp:my-docs/get-item` selects the tool Pi registers as `mcp__my_docs__get_item`. The server part may also use the `-`→`_` form of its name that Pi's MCP namespace uses, such as `mcp:my_docs`. The child connects the servers in Pi's `mcp.json` (the project's `.pi/mcp.json` only when the parent trusts the project) and gets only the selected tools, as direct tools, whatever exposure the server is configured with; tools configured as `hidden` cannot be selected. Servers that an extension adds with `pi.registerMcpServer()` work in background children, because the registering extension loads there too. They do not work in foreground children, which load no ambient extensions, so a foreground launch that selects one fails and says to use `async: true`. The launch fails if a selected tool does not register in the child within 10 seconds.
|
|
466
476
|
|
|
467
477
|
When pi-mcp-adapter is installed, it keeps priority and provides the direct MCP tools. Subagents only receive direct MCP tools when `mcp:` entries are listed in their frontmatter; global `directTools: true` in the adapter's MCP config is not enough by itself. The generic `mcp` proxy tool can still be used for discovery when available. The adapter caches tool metadata at startup, so after connecting a new MCP server for the first time, restart Pi before relying on direct tools. Server `includeTools` and `excludeTools` policies are enforced while resolving cached metadata for children: both accept exact names and `*`/`?` glob patterns against raw, generated-resource, and server/short/mcp/none-prefixed names, with `excludeTools` taking precedence. `mcp:` entries must name servers from the adapter's configuration files. A server that exists only in the adapter's runtime snapshot (registered at runtime, not persisted) cannot be provided to a child: children are pi sessions inside the parent or the runner process, not `pi` processes that could receive an MCP config argument, so such a launch fails with an error saying that MCP tools must come from an ambient adapter extension in a background child. An `mcp:` entry named `subagent` does not authorize nested fanout; declare the builtin `subagent` tool or set `allowNestedSubagents: true`. If a resolved direct MCP name is missing from the child registry, pi-subagents keeps the launch failed under the strict allowlist and reports the registration mismatch; check the resolved names against what the host or pi-mcp-adapter actually registers before child startup.
|
|
468
478
|
|
package/docs/configuration.md
CHANGED
|
@@ -76,6 +76,8 @@ For a native Pi `model_verification_failed` where your proxy accepts `claude-hai
|
|
|
76
76
|
|
|
77
77
|
Replace `YOUR_PROVIDER` with the resolved Pi provider ID. Keep the outgoing model alias unchanged. This native remedy already exists in v0.65.1; it does not infer equivalence from provider prefixes or dates. The built-in external `claude-code` adapter does not invoke this verifier or use this setting. If an external run shows this diagnostic, identify the installed version, resolved runner kind/adapter, and error location before applying a native remedy. Thanks to [sixtus](https://github.com/sixtus) for the concrete request-ID/response-ID example in [#1922](https://github.com/nicobailon/pi-subagents/issues/1922).
|
|
78
78
|
|
|
79
|
+
A local Pi child whose selected model is a virtual model (`pi.registerVirtualModel()`, such as a router) needs no alias. Its assistant messages name whichever physical model the router picked, so verification compares the child's selected virtual `provider/id` with the launch model instead, exactly. A different virtual selection fails, and `modelResponseAliases` does not override it.
|
|
80
|
+
|
|
79
81
|
## Tool activation lifecycle
|
|
80
82
|
|
|
81
83
|
On Pi 0.86.1 or newer, when the model can take a new tool mid-conversation (see [`toolActivation`](#toolactivation)), a fresh unrestricted parent starts with `subagents_enable`, `bg_wait`, and `subagent_supervisor` active while `subagent` stays registered but inactive. Calling `subagents_enable({})` preserves unrelated active tools and exposes `subagent` on the next model request. It does not launch a child or infer authority from prompt keywords. With other models, a fresh parent starts with `subagent`, `bg_wait`, and `subagent_supervisor` active and no `subagents_enable`.
|
|
@@ -294,6 +296,14 @@ Prompt modes keep their fixed keys. For example, `Esc` still cancels steer text
|
|
|
294
296
|
|
|
295
297
|
Controls the under-editor widget for active background runs. It defaults to `true`, including when FleetView is enabled, so active work remains visible after reload. Set it to `false` to hide this widget while keeping FleetView available.
|
|
296
298
|
|
|
299
|
+
## `asyncWidgetCollapsed`
|
|
300
|
+
|
|
301
|
+
```json
|
|
302
|
+
{ "asyncWidgetCollapsed": true }
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Starts each newly mounted under-editor async widget in its one-line folded state. It defaults to `false`. A header click still toggles the widget, and the folded state still resets when the widget is removed or Pi reloads.
|
|
306
|
+
|
|
297
307
|
## `waitTool`
|
|
298
308
|
|
|
299
309
|
```json
|
|
@@ -302,7 +312,7 @@ Controls the under-editor widget for active background runs. It defaults to `tru
|
|
|
302
312
|
|
|
303
313
|
`defaultTimeoutMs` sets the blocking window used when a `bg_wait` call omits `timeoutMs`; explicit call values win, followed by this setting, then the 30-minute fallback. `bg_wait` is the only registered wait tool. When the window elapses, the tool returns a non-error `window_elapsed` result with the still-active work identities, and that work keeps running. Set `enabled` to `false` to make direct calls return immediately instead of blocking. The default is enabled. You can also set `"waitTool": false`; set `PI_SUBAGENT_WAIT_TOOL_ENABLED=false` (or `0`, `off`, `disabled`) to override config for one process. The effective enabled and default-timeout values are passed explicitly to child runtimes. Headless `agent_end` auto-drain retains its own strict deadline and fails if required work remains unresolved. Invalid config or environment values fail instead of being coerced.
|
|
304
314
|
|
|
305
|
-
Blocking `bg_wait({ id: "..." })` keeps the current tool call open until that run changes. By default it returns when a run needs attention. Use `bg_wait({ stopOnAttention: false })` only for run-to-completion flows that should wait through idle or long-thinking attention; supervisor/contact requests still stop the wait. In a long-lived interactive parent session, `bg_wait({ id: "...", nonBlocking: true })` instead resolves the prefix once, persists the exact run identity, returns a subscription token immediately, and wakes that session on completion, failure, attention, reconciliation failure, or timeout. Use it for provider, detached, or other background work without a native completion notification; ordinary async subagent runs notify the parent natively and do not need a wait subscription. Armed subscriptions appear in ordinary `subagent({ action: "status" })` output and are not counted as active child work.
|
|
315
|
+
Blocking `bg_wait({ id: "..." })` keeps the current tool call open until that run changes. By default it returns when a run needs attention. Use `bg_wait({ stopOnAttention: false })` only for run-to-completion flows that should wait through idle or long-thinking attention; supervisor/contact requests still stop the wait. In a long-lived interactive parent session, `bg_wait({ id: "...", nonBlocking: true })` instead resolves the prefix once, persists the exact run identity, returns a subscription token immediately, and wakes that session on completion, failure, attention, reconciliation failure, or timeout. Use it for provider, detached, or other background work without a native completion notification; ordinary async subagent runs notify the parent natively and do not need a wait subscription. Armed subscriptions appear in ordinary `subagent({ action: "status" })` output and are not counted as active child work. Native completion notifications wake the session through the Pi process that launched the run, while that process is still running; a session reopened in a new process does not get that wake and should read the result with `subagent({ action: "status" })`. `nonBlocking` subscriptions need a host with a UI context, such as interactive or RPC mode; plain SDK embedding without a UI context rejects them.
|
|
306
316
|
|
|
307
317
|
This is different from `waitTool.enabled=false`, which returns immediately without registering any future wake. Provider items remain available only to blocking fleet-wide waits; non-blocking subscriptions require one async or remembered detached foreground run id.
|
|
308
318
|
|
|
@@ -400,7 +410,7 @@ The budget counts single launches, expanded `tasks`/`count`, static chain steps
|
|
|
400
410
|
|
|
401
411
|
Optionally caps concurrently active top-level async runs owned by one parent session. Unset or `0` keeps the existing unlimited behavior. A positive integer reserves one slot before an async single, parallel, chain, or workflow creates run artifacts or starts children. Foreground runs and nested/workflow children do not reserve another slot.
|
|
402
412
|
|
|
403
|
-
Queued, running, paused, and needs-attention runs retain capacity. Runner-backed slots release only after terminal logical state and matching observed process-terminal proof from #1030. Missing, malformed, or unknown cleanup proof retains the slot. A terminal async workflow releases after its controller is gone and every launched child is accounted for: awaited foreground children are covered by workflow settlement, while actual background children still require observed process-terminal proof. Resume transfers the source slot without a second charge. Dismissal and history cleanup do not release capacity.
|
|
413
|
+
Queued, running, paused, and needs-attention runs retain capacity. Runner-backed slots release only after terminal logical state and matching observed process-terminal proof from #1030. Missing, malformed, or unknown cleanup proof retains the slot. A terminal async workflow releases after its controller is gone and every launched child is accounted for: awaited foreground children are covered by workflow settlement, while actual background children still require observed process-terminal proof. Two cases release without that proof: a run whose runner failed before child startup, recorded as `not-started` with an error, and the abandoned-slot policy below. Resume transfers the source slot without a second charge. Dismissal and history cleanup do not release capacity.
|
|
404
414
|
|
|
405
415
|
When the runner is gone but process cleanup proof remains unknown, configure a bounded policy reclaim under `capacity.abandonedSlotReleaseAfterMs`:
|
|
406
416
|
|
|
@@ -410,6 +420,8 @@ When the runner is gone but process cleanup proof remains unknown, configure a b
|
|
|
410
420
|
|
|
411
421
|
The default is `1200000` milliseconds (20 minutes). The policy releases only a failed terminal run whose runner PID is dead and whose last activity is older than the threshold. A live or unknown PID, a non-failed terminal state, a recent run, or missing activity timestamp retains the slot. Set the value to `false` to keep strict retention. Valid configured durations range from 5 minutes through 24 hours. Policy release is reported as `abandoned-timeout` with `processProof: unknown`; it is not observed process-terminal proof and may reclaim capacity while an orphan child still exists.
|
|
412
422
|
|
|
423
|
+
A terminal workflow whose controller is gone uses the same policy when async children lack proof: the workflow must have ended longer ago than the threshold, and every async child without proof must itself be a failed run with a dead runner PID and old activity. Any other child without proof keeps the slot. The release event lists those children under `abandonedChildren`.
|
|
424
|
+
|
|
413
425
|
This limit bounds current top-level async load. It is separate from cumulative `maxSubagentSpawnsPerSession`, `maxSubagentSpawnsPerRun`, and `globalConcurrencyLimit`.
|
|
414
426
|
|
|
415
427
|
`subagent({ action: "status" })`, fleet status, and `subagent({ action: "doctor" })` expose used, effective limit, and remaining active capacity. Static chains and parallel calls fail before creating run artifacts or starting partial work when their declared capacity cannot fit. Later retries or unbounded dynamic work are not guaranteed by that preflight.
|
package/docs/extension-api.md
CHANGED
|
@@ -251,7 +251,7 @@ const result = await resolveSubagentLaunchContract({
|
|
|
251
251
|
if (!result.ok) {
|
|
252
252
|
// missing_agent, ambiguous_agent, missing_skill, denied_required_tool,
|
|
253
253
|
// invalid_artifact_dir, invalid_cwd, unsupported_mode, restricted_agent,
|
|
254
|
-
// thinking_ceiling, invalid_extension_bindings, or invalid_intercom_bridge
|
|
254
|
+
// thinking_ceiling, model_scope, invalid_extension_bindings, or invalid_intercom_bridge
|
|
255
255
|
throw new Error(result.message);
|
|
256
256
|
}
|
|
257
257
|
|
|
@@ -587,7 +587,7 @@ Detached children do not stop when the session does. They are the host process's
|
|
|
587
587
|
|
|
588
588
|
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.
|
|
589
589
|
|
|
590
|
-
When pi-subagents runs inside a compatible pi-web host, it discovers the versioned `Symbol.for("@agegr/pi-web/session-liveness/v1")` registry and registers one provider for the current session. The provider reports live `queued`/`running` async jobs, active nested descendants (including foreground routes retained after their direct parent settles), foreground controls that still have a scheduling owner or active child, and completion notifications
|
|
590
|
+
When pi-subagents runs inside a compatible pi-web host, it discovers the versioned `Symbol.for("@agegr/pi-web/session-liveness/v1")` registry and registers one provider for the current session. The provider reports live `queued`/`running` async jobs, active nested descendants (including foreground routes retained after their direct parent settles), foreground controls that still have a scheduling owner or active child, async runs that ended but whose result has not reached the parent (including a rejected send waiting for retry), and completion notifications until Pi starts the wake message (`message_start`), even when Pi queued it behind the current turn or past `agent_settled`. A wake Pi discards without starting keeps the session live until it shuts down. Retained terminal history, future schedules, and wait subscriptions do not make a session live by themselves. The registration is replaced on session changes and released during runtime shutdown or reload; other hosts remain unaffected.
|
|
591
591
|
|
|
592
592
|
If your host reclaims idle sessions, keep a session alive while it still has live detached work:
|
|
593
593
|
|
package/docs/missions.md
CHANGED
|
@@ -106,6 +106,15 @@ Create a fixed recurring workflow from a script file:
|
|
|
106
106
|
subagent({ action: "schedule.create", id: "backlog", every: "6h", catchUp: "latest", workflow: "./.pi/workflows/backlog.js", args: { task: "Maintain core" } })
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
+
Create a daily or weekly local-time schedule:
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
subagent({ action: "schedule.create", every: "day", at: "09:00", timezone: "Asia/Taipei", workflow: "./.pi/workflows/review.js" })
|
|
113
|
+
subagent({ action: "schedule.create", every: "week", on: ["mon", "tue", "wed", "thu", "fri"], at: "09:00", timezone: "America/New_York", workflow: "./.pi/workflows/review.js" })
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Calendar schedules require `HH:mm` and an explicit IANA `timezone` or `UTC`. Weekly `on` is a non-empty weekday array; duplicates are removed and weekdays sorted. Daily schedules do not accept `on`. Missing local times and skipped dates are skipped; a repeated time fires at its first instant only. The pending local date is persisted with a UTC cache that is refreshed on restoration using the host's current timezone data. Use a calendar-capable version in every session sharing these definitions; older schedulers reject the unknown trigger kind.
|
|
117
|
+
|
|
109
118
|
Fixed intervals support `m`, `h`, `d`, and `w` units and advance from the planned time without completion drift. The schedule stores the script text read at creation, so later edits to the file do not change it. Schedule arguments are normalized and persisted for exact replay after reload; do not put secrets in them.
|
|
110
119
|
|
|
111
120
|
Create a quiet recurring workflow whose successful completions stay visible but do not wake the parent session:
|
|
@@ -116,16 +125,31 @@ subagent({ action: "schedule.create", id: "nightly-sweep", every: "24h", quiet:
|
|
|
116
125
|
|
|
117
126
|
Manage schedules with `schedule.list`, `schedule.show`, `schedule.history`, `schedule.pause`, `schedule.resume`, `schedule.run`, `schedule.run-due`, and `schedule.delete`.
|
|
118
127
|
|
|
128
|
+
Attach an existing mission to give each scheduled workflow access to the same durable `state.get/set`:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
subagent({ action: "schedule.create", id: "backlog", every: "6h", workflow: "./.pi/workflows/backlog.js", missionId: "<mission-id>" })
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
The mission must be readable in the store resolved from the schedule's target `cwd` and current mission configuration. Creation checks it without changing its status. Every fire uses the normal explicit mission launch path, including after session restoration; missing or invalid records fail before workflow execution. Schedules accept only an existing `missionId`, not mission creation or updates. The script's fixed `args` and its mutable mission state remain separate.
|
|
135
|
+
|
|
136
|
+
Attachment uses ordinary mission lifecycle and retention rules. A non-goal mission can become terminal after a run, and the next fire reactivates it. `mission.close` does not pause the schedule; use `schedule.pause` or `schedule.delete` to stop future fires. A schedule does not protect its mission from terminal retention (default 200 records). If it is removed, later fires record `failed_launch` rather than creating a replacement. Open mission decisions and goal notice pause/budget settings do not gate schedule launches. Multiple schedules sharing a mission still have independent overlap controls; attachment does not serialize their workflows.
|
|
137
|
+
|
|
138
|
+
`missions.enabled:false` still permits explicit attachment; `disabledFeatures:["missions"]` rejects it. Each fire resolves the mission store using its target `cwd`, current mission configuration and Pi agent directory; the schedule does not pin the creation-time store. Sessions must resolve the same effective store to reuse the same state. Attachment does not search other worktrees or copy mission records; an explicitly shared `missions.directory` follows the existing storage rules.
|
|
139
|
+
|
|
140
|
+
Mission-bound definitions use schedule schema version 2 so older versions reject them instead of dropping the attachment. Existing unbound definitions keep version 1 and need no migration. A project using mission attachment should use a version that supports it in every scheduler session.
|
|
141
|
+
|
|
119
142
|
Behavior:
|
|
120
143
|
|
|
121
|
-
- Runs always launch async with fresh context
|
|
144
|
+
- Runs always launch async with fresh context. Without `missionId`, they disable mission creation and have no `state` global.
|
|
122
145
|
- 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.
|
|
123
146
|
- Definitions, bounded history, append-only events, and per-run receipts are stored with mode `0600`.
|
|
124
147
|
- `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.
|
|
148
|
+
- A successful `schedule.run` satisfies the next natural fire; a failed manual launch does not skip it. For calendars, a manual launch before today's pending fire consumes today; if today has already fired, it consumes the next pending date. When overdue, it consumes the latest pending occurrence. The next fire is after both that occurrence and the current time.
|
|
149
|
+
- If a natural calendar fire overlaps a manual launch that later fails, the pending fire remains due. Pausing the schedule while that launch is pending still prevents automatic execution after the failure.
|
|
126
150
|
- `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`.
|
|
127
151
|
- `schedule.run-due` lets an external launcher start due project work without making `pi-subagents` a daemon.
|
|
128
|
-
-
|
|
152
|
+
- Month/year recurrence, cron, queue/replace overlap, and the schedule TUI inspector are intentionally deferred to the next slice.
|
|
129
153
|
- The old `schedule`, `schedule-list`, `schedule-status`, and `schedule-cancel` actions were removed in a hard cutover.
|
|
130
154
|
|
|
131
155
|
Disable or bound schedules with the `scheduledRuns` config key in [configuration.md](configuration.md#scheduledruns).
|
package/docs/models.md
CHANGED
|
@@ -13,6 +13,8 @@ Builtin agents inherit your current Pi default model. This keeps new installs fr
|
|
|
13
13
|
|
|
14
14
|
Precedence, strongest first: per-run override → provider-scoped role override → `agentOverrides.<name>.model` → agent frontmatter `model` → `subagents.defaultModel` → the parent session model. A provider preference does not replace this order; it only resolves bare model ids when the active registry has more than one match. Fully qualified `provider/model` strings still win exactly.
|
|
15
15
|
|
|
16
|
+
On the two Claude Code adapters a model is Claude Code's own alias or id rather than a Pi child model, so a value set for one of them resolves nothing against the registry. `subagents.defaultModel` is ignored there, because it is a default for Pi children. An explicit `agentOverrides.<name>.model` or agent frontmatter `model` is passed on as given, and Claude Code reports it when it does not recognize the value. See [agents](agents.md).
|
|
17
|
+
|
|
16
18
|
Each launch resolves one model. Provider errors, including HTTP 429 responses, are returned from that model rather than selecting another one. Separately, a verified compaction abort after useful progress may continue the retained child session once on the same resolved model; this lifecycle recovery preserves work and is not model fallback.
|
|
17
19
|
|
|
18
20
|
Use `model: "inherit"` in agent frontmatter or `agentOverrides.<name>.model` to select the current parent session model explicitly.
|
|
@@ -122,7 +124,7 @@ If your provider rejects model IDs with thinking suffixes, set `subagents.disabl
|
|
|
122
124
|
|
|
123
125
|
### Thinking ceiling
|
|
124
126
|
|
|
125
|
-
Set `subagents.maxThinking` to enforce a hard maximum for every native Pi
|
|
127
|
+
Set `subagents.maxThinking` to enforce a hard maximum for every child that can ask for a thinking level: native Pi children and the two Claude Code adapters. The supported levels, from least to most thinking, are `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`:
|
|
126
128
|
|
|
127
129
|
```json
|
|
128
130
|
{
|
|
@@ -133,7 +135,7 @@ Set `subagents.maxThinking` to enforce a hard maximum for every native Pi child.
|
|
|
133
135
|
}
|
|
134
136
|
```
|
|
135
137
|
|
|
136
|
-
Requests above the ceiling fail before child startup; the setting covers frontmatter, `agentOverrides`, per-run overrides, parallel/chain children, nested launches, and resumed children. Project settings take precedence over user settings. External runners
|
|
138
|
+
Requests above the ceiling fail before child startup; the setting covers frontmatter, `agentOverrides`, per-run overrides, parallel/chain children, nested launches, and resumed children. Project settings take precedence over user settings. External runners keep their existing behavior, except the two Claude Code adapters, which translate the requested level into `--effort` and enforce the same ceiling. An enforced `subagents.modelScope` also covers them: the model id that will reach the CLI is checked, and a launch that pins no model fails closed.
|
|
137
139
|
|
|
138
140
|
## Extension defaults
|
|
139
141
|
|
|
@@ -247,3 +249,5 @@ The workflow:
|
|
|
247
249
|
- `/subagents-refresh-provider-models` writes a serialized provider model catalog with observed registry data, simple role-oriented classification, and live probe results from tiny one-shot `pi -p --model ... --no-tools` checks. The cache refreshes when missing or stale; use `--force` to ignore freshness and probe again immediately.
|
|
248
250
|
- `/subagents-generate-profiles` uses the provider catalog to produce quota and quality profiles.
|
|
249
251
|
- `/subagents-check-profile` re-checks each assigned model in a saved profile against the current registry and a live probe, so you can detect model removals, auth problems, or stale assignments.
|
|
252
|
+
|
|
253
|
+
Hand-authored profile entries can also set `machine` to a non-empty string or `false`. Loading and checking validate the whole saved profile, including machine syntax; malformed values are rejected before loading writes settings or checking probes models. Valid machine strings are trimmed and use the same limits as agent settings. `false` clears a machine pin, while an omitted field preserves an existing string pin when loading. Validation does not query the live machine catalog.
|
package/docs/observability.md
CHANGED
|
@@ -10,6 +10,8 @@ A foreground child is a pi session created inside the parent Pi process, not a s
|
|
|
10
10
|
|
|
11
11
|
A background child is a pi session created inside the detached runner process. The runner mirrors session events into `events.jsonl`, `output-<index>.log`, and the transcript. Interrupt and stop abort the child session; steer requests are delivered with the session's `steer` or `followUp`.
|
|
12
12
|
|
|
13
|
+
Long-open `bash` attention notices identify the exact tool call and include status and child-specific transcript commands. Each distinct long-open call notifies once. Inspect the running command and recent output before intervening: development servers and watch commands may intentionally never return, while builds and tests may legitimately take time. A queued steer does not cancel the shell command. For local native Pi children explicitly granted `subagent_command`, `command.yield` returns control while the selected command remains managed; `command.cancel` stops that command without interrupting the child. Query `command.status` to confirm its terminal state. Unsupported children require the existing run-scoped interrupt/resume path, which may affect siblings. See [command controls](tool-reference.md#command-controls).
|
|
14
|
+
|
|
13
15
|
Live progress shows compact detail for single, chain, and parallel modes: a bounded one-line task, current tool, recent output, token counts, aggregate cost, duration, activity freshness, current-tool duration, and chain graph metadata when available. Workflow `label` metadata wins over raw task text in compact multi-child cards.
|
|
14
16
|
|
|
15
17
|
Press Pi's configured expand key (`Ctrl+O` by default) to expand the full streaming view with complete output per step.
|
|
@@ -53,9 +55,11 @@ or keyboard shortcuts is needed. The summary counts the widget's tracked runs,
|
|
|
53
55
|
including workflow parents and children, rather than unique agents.
|
|
54
56
|
|
|
55
57
|
Folding stays in effect across progress updates and does not change Pi's global
|
|
56
|
-
expand setting, run execution, or completion notifications.
|
|
57
|
-
|
|
58
|
-
widget
|
|
58
|
+
expand setting, run execution, or completion notifications. Set
|
|
59
|
+
`asyncWidgetCollapsed: true` in the extension configuration to start each newly
|
|
60
|
+
mounted widget folded. Task rows, drag and wheel events, and modifier clicks are left
|
|
61
|
+
unhandled. The state resets when the widget is removed or Pi reloads. Regular mode
|
|
62
|
+
keeps the existing keyboard controls.
|
|
59
63
|
|
|
60
64
|
### Reducing status display noise
|
|
61
65
|
|
|
@@ -74,6 +78,7 @@ For compact chat results with FleetView as the only live editor surface, merge t
|
|
|
74
78
|
- `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.
|
|
75
79
|
- `fleetView: true` retains live progress. Open `/subagents-fleet` 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.
|
|
76
80
|
- `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.
|
|
81
|
+
- `asyncWidgetCollapsed: true` starts each newly mounted async widget as a one-line live status summary; click its header to expand it.
|
|
77
82
|
|
|
78
83
|
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).
|
|
79
84
|
|
package/docs/tool-reference.md
CHANGED
|
@@ -71,17 +71,17 @@ The complete plain-JSON inventory is validated before the first launch (maximum
|
|
|
71
71
|
|-------|------|---------|-------------|
|
|
72
72
|
| `agent` | string | - | One direct child or agent-management target. Workflow child agents are set inside `runs.run` or `runs.all`. |
|
|
73
73
|
| `task` | string | agent default | Direct child's task; requires `agent`, excludes `action` and workflow inputs. `agent` may also select a management target. |
|
|
74
|
-
| `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,
|
|
74
|
+
| `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, reviewed `worktree.cleanup`, schedule, watchdog, or doctor action. |
|
|
75
75
|
| `topic` | `overview \| workflows \| agents \| missions \| observability \| tool-reference \| configuration \| models \| watchdog \| extension-api \| council` | `overview` | Packaged guide topic for `action: "guide"`. |
|
|
76
76
|
| `config` | object/string | - | Agent config for management create/update. |
|
|
77
77
|
| `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` defaults to `fresh`; packaged `oracle` and `advisor` default to `fork`. |
|
|
78
|
-
| `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. |
|
|
79
|
-
| `missionId` | string | - | Attach a workflow to an existing project mission
|
|
78
|
+
| `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. On the `claude-code` and `claude-code-writer` adapters the model is Claude Code's own alias or id, and the same suffix becomes its `--effort`, bounded by `subagents.maxThinking` and by an enforced `subagents.modelScope`. |
|
|
79
|
+
| `missionId` | string | - | Attach a workflow, including each fire of `schedule.create`, to an existing project mission. Scheduled attachment uses ordinary lifecycle and retention rules. |
|
|
80
80
|
| `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. |
|
|
81
81
|
| `handoffPath` | string | - | Aggregate handoff manifest for `action: "worktree.discard"` or lane evidence actions, or optional explicit metadata for `action: "worktree.cleanup"`. |
|
|
82
|
-
| `repo` | string | runtime cwd | Repository path for `action: "worktree.cleanup"`;
|
|
83
|
-
| `planId` | string | - |
|
|
84
|
-
| `mode` | `steer \| follow_up \| auto \| plan \| apply` | - | Delivery mode for `action: "steer"`; `worktree.cleanup`
|
|
82
|
+
| `repo` | string | runtime cwd | Repository path for `action: "worktree.cleanup"`; required explicitly for apply. The configured worktree base filters candidates by their per-project folder under it but never discovers them. |
|
|
83
|
+
| `planId` | string | - | Saved, reviewed cleanup plan ID; required for apply, rejected in plan mode. |
|
|
84
|
+
| `mode` | `steer \| follow_up \| auto \| plan \| apply` | - | Delivery mode for `action: "steer"`; `worktree.cleanup` accepts `plan` or `apply`; apply requires an explicit repository and saved plan ID. |
|
|
85
85
|
| `laneId` | string | - | Exact `runId` stored in the handoff manifest for `lane.status`, `lane.recordMerge`, or `lane.recordSupersession`. |
|
|
86
86
|
| `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. |
|
|
87
87
|
| `supersession` | object | - | Attested replacement-lane evidence for `lane.recordSupersession`; requires a different replacement lane id, attestor, and timestamp. |
|
|
@@ -150,7 +150,7 @@ return { first: first.key, rest: rest.map((child) => child.key), receipt };
|
|
|
150
150
|
|
|
151
151
|
Use `outputMode: "file-only"` when a saved output may be large and the parent only needs a pointer. The returned text is a compact reference like `Output saved to: /abs/report.md (48.2 KB, 2847 lines). Read this file if needed.` Failed runs and save errors still return normal inline output for debugging.
|
|
152
152
|
|
|
153
|
-
In a workflow script, give each child an explicit output path when later script steps need a durable file reference. A child with only read-only tools does not need direct filesystem access for `output`: it returns the complete artifact in its final response and the runtime persists it. Children with mutation-capable tools retain the direct-write instruction.
|
|
153
|
+
In a workflow script, give each child an explicit output path when later script steps need a durable file reference. A child with only read-only tools does not need direct filesystem access for `output`: it returns the complete artifact in its final response and the runtime persists it. Children with mutation-capable tools retain the direct-write instruction. With `outputSchema`, the runtime persists the structured result as indented JSON instead of the final prose, unless the child wrote the file itself.
|
|
154
154
|
|
|
155
155
|
The `output` field is the API binding; a filename mentioned in task text (for example, `Write your findings to exactly this path: report.md`) is only instruction and does not override runtime routing. When a later workflow step or parent needs a durable file, set `output` on `runs.run`/`runs.all` and return the child’s `outputReference`, `outputPathMapping`, or `artifactPaths`; arbitrary literal strings returned by workflow JavaScript are not rewritten. Omitted child output may use a managed aggregate-derived sibling path.
|
|
156
156
|
|
|
@@ -246,7 +246,7 @@ Rules:
|
|
|
246
246
|
|
|
247
247
|
### Schedule controls
|
|
248
248
|
|
|
249
|
-
Use `schedule.create` with `workflow: true` or a script path, 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.
|
|
249
|
+
Use `schedule.create` with `workflow: true` or a script path, not a direct child. `at` accepts a delay like `+10m` or an ISO timestamp with timezone; `every` accepts fixed intervals or `day`/`week` with `at:"HH:mm"` and an explicit IANA `timezone` (or `UTC`). Weekly `on` is a non-empty array of `mon`–`sun`; daily schedules omit it. Missing local times are skipped; repeated times fire once at their first instant. `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`. An optional `missionId` attaches an existing mission on every fire and exposes its workflow state, using ordinary mission lifecycle and retention rules; `mission.close` does not pause the schedule. See [missions and schedules](missions.md#schedules) for examples and list/show/history/pause/resume/run/run-due/delete. `baseRef` resolves only at worktree allocation and still requires a clean source checkout.
|
|
250
250
|
|
|
251
251
|
## Lane merge evidence and cleanup eligibility
|
|
252
252
|
|
|
@@ -320,6 +320,31 @@ subagent({ action: "doctor" })
|
|
|
320
320
|
- Inside child-safe fanout mode, bare `status` requires an id when no local foreground run is active, so children cannot enumerate unrelated top-level async runs.
|
|
321
321
|
- Bare `interrupt` still targets only the visible top-level run; interrupting a nested run requires its explicit nested id.
|
|
322
322
|
|
|
323
|
+
### Command controls
|
|
324
|
+
|
|
325
|
+
Command controls target direct local native Pi children that explicitly select both `bash` and `subagent_command`, in foreground or async runs. They require the owning parent session, a run `id`, an explicit `index` for multi-child runs, and the exact `toolCallId` for mutations. Nested, external, and custom shell backends do not expose this controller. Custom bash extensions keep their own registered `subagent_command` tool. If a custom bash backend does not provide that tool, an explicitly required `subagent_command` remains available and reports an unsupported-backend error. Native supervisor command controls are unavailable for custom backends.
|
|
326
|
+
|
|
327
|
+
```ts
|
|
328
|
+
subagent({ action: "command.status", id: "<run-id>", index: 0 })
|
|
329
|
+
subagent({ action: "command.yield", id: "<run-id>", index: 0, toolCallId: "<call-id>" })
|
|
330
|
+
subagent({ action: "command.cancel", id: "<run-id>", index: 0, toolCallId: "<call-id>" })
|
|
331
|
+
subagent({ action: "command.status", id: "<run-id>", index: 0, toolCallId: "<call-id>" })
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
`command.yield` releases the selected blocking tool call without restarting the process. `command.cancel` aborts only that command through Pi's native bash cancellation; the child receives a tool error and can continue. `cancel_requested` acknowledges the request, while `cancelled` confirms the backend settled. A stale id never targets a later command. Run-scoped `interrupt` remains separate.
|
|
335
|
+
|
|
336
|
+
Enable command controls by including both `bash` and `subagent_command` in the agent or call's `tools` allowlist. Tool ceilings and `excludeTools` still apply. Without this explicit selection, bash keeps its original tool schema and backend; no command controller, inbox watcher, or command-state writes are added. Inside an enabled child:
|
|
337
|
+
|
|
338
|
+
```ts
|
|
339
|
+
bash({ command: "npm run dev", yieldTimeMs: 1000 })
|
|
340
|
+
subagent_command({ action: "status", toolCallId: "<returned-call-id>", waitMs: 1000 })
|
|
341
|
+
subagent_command({ action: "cancel", toolCallId: "<returned-call-id>", waitMs: 1000 })
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
`yieldTimeMs` and `waitMs` range from 0 to 30,000 milliseconds. Omitting `yieldTimeMs` preserves completion-waiting behavior. Yielded handles are not successful exits. Status includes running/yielded/cancel-requested states and completed/failed/cancelled terminal states, a bounded output tail, and Pi's full-output file path when available. Recent terminal history retains 20 commands; active commands remain tracked.
|
|
345
|
+
|
|
346
|
+
Commands remain owned by the child session. Normal completion with unfinished commands cancels them and fails the run. Interrupt, stop, timeout, and disposal clean up owned commands. Controls do not transfer processes out of the child or create detached services. Native bash `timeout` remains command-scoped; `toolTimeoutMs` covers an open tool call and does not become a yielded process deadline.
|
|
347
|
+
|
|
323
348
|
### resume
|
|
324
349
|
|
|
325
350
|
`resume` revives a paused, completed, or failed async/foreground child by starting a new child from its stored session file. Stopped runs remain non-resumable, and it does not interrupt a live top-level async child. Use `steer` for acknowledged live async guidance.
|
|
@@ -498,3 +523,18 @@ subagent({ workflow: true, share: true });
|
|
|
498
523
|
```
|
|
499
524
|
|
|
500
525
|
This is disabled by default. Session data may contain source code, paths, environment variables, credentials, or other sensitive output. You need `gh` installed and authenticated.
|
|
526
|
+
|
|
527
|
+
### Reviewed worktree cleanup
|
|
528
|
+
|
|
529
|
+
`worktree.cleanup` first saves a plan without removing anything:
|
|
530
|
+
|
|
531
|
+
```ts
|
|
532
|
+
subagent({ action: "worktree.cleanup", repo: "/path/to/repo", mode: "plan" })
|
|
533
|
+
subagent({ action: "worktree.cleanup", repo: "/path/to/repo", mode: "apply", planId: "<reviewed-plan-id>" })
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
Apply uses the existing `authorityPolicy.discardWorktree` (`confirm` by default). It checks the saved plan's repository, hash and 30-minute expiry, then rechecks only reviewed candidates under a repository lock shared with retained resume admission. Only a clean, contained, provably owned terminal worktree with durable evidence can be removed. Dirty or untracked files, ignored files, Git locks, active ownership, recorded native session dependencies, retained resume admission, changed heads and missing artifacts keep the tree. Every local branch is retained; apply never forces removal or prunes unrelated Git metadata.
|
|
537
|
+
|
|
538
|
+
Cleanup also requires the canonical base directory recorded when the worktree was created. Older handoffs without that proof remain kept; planning never infers it from the current location. A path redirected outside its creation directory or an invalid handoff manifest prevents removal. Apply validates the full manifest under its write lock before recording a removal attempt or invoking Git.
|
|
539
|
+
|
|
540
|
+
The plan is claimed once before deletion and records progress in a durable receipt. Repeating a claimed plan displays its receipt without attempting more removals. It can repair a journaled removal's handoff fact only when that directory and Git registration are both gone; a recreated path is retained. After an interruption, inspect the receipt and create a fresh plan for additional work. Cleanup failures leave child outcomes and artifacts intact. `worktree.discard` remains the separate explicitly authorized destructive discard operation.
|
package/docs/workflows.md
CHANGED
|
@@ -51,9 +51,9 @@ Use direct `{ agent, task }` for one bounded child. Use a workflow script when t
|
|
|
51
51
|
|
|
52
52
|
The `workflow` field selects the script source:
|
|
53
53
|
|
|
54
|
-
- `workflow: true` runs the one ```` ```js workflow ```` fenced block written in the same assistant reply as the `subagent` call. The script is plain text in the reply, so it needs no JSON string escaping. A reply can carry exactly one such block and one `workflow: true` call; zero or several blocks fail. A line containing only ```` ``` ```` (three or more backticks, optionally followed by spaces or tabs) ends the block, so keep Markdown fences inside quoted strings.
|
|
54
|
+
- `workflow: true` runs the one ```` ```js workflow ```` fenced block written in the same assistant reply as the `subagent` call. The script is plain text in the reply, so it needs no JSON string escaping. A reply can carry exactly one such block and one `workflow: true` call; zero or several blocks fail. A reply with no tagged block may instead carry exactly one plain ```` ```js ```` block, which then runs. A line containing only ```` ``` ```` (three or more backticks, optionally followed by spaces or tabs) ends the block, so keep Markdown fences inside quoted strings.
|
|
55
55
|
- A string containing `/` (or `\` in a Windows path), such as `workflow: "./workflows/review.js"`, is a script file.
|
|
56
|
-
- Any other string, such as `workflow: "review"`, is a [named workflow resource](#named-workflow-resources-for-permission-extensions).
|
|
56
|
+
- Any other string, such as `workflow: "review"`, is a [named workflow resource](#named-workflow-resources-for-permission-extensions). The string `"true"` is read as `workflow: true`, because some MCP clients send the boolean as a string.
|
|
57
57
|
|
|
58
58
|
`workflow: true` works only from a model tool call. Slash commands, RPC, and schedules pass scripts through their own inputs. Examples below that show only a ```` ```js workflow ```` block run with `subagent({ workflow: true })` in the same reply.
|
|
59
59
|
|
|
@@ -395,7 +395,7 @@ A plain workflow creates one enclosing mission by default. Its children do not c
|
|
|
395
395
|
|
|
396
396
|
### Repeatable workflows
|
|
397
397
|
|
|
398
|
-
Use stable child keys and keep process logic in ordinary JavaScript. `runs.run` launches one child, `runs.all` launches independent children together, and later steps can use each completed child's `output`. Put long task text in arrays joined with `"\n"` so Markdown fences do not conflict with the script string.
|
|
398
|
+
Use stable child keys and keep process logic in ordinary JavaScript. `runs.run` launches one child, `runs.all` launches independent children together, and later steps can use each completed child's `output`. Put long task text in arrays joined with `"\n"` so Markdown fences do not conflict with the script string. Pass long child briefs as files and put only their paths in task text, not the briefs themselves in `args`, which is limited to 16 KiB.
|
|
399
399
|
|
|
400
400
|
For a process you run often, save the task as a prompt template under `.pi/prompts/` or `~/.pi/agent/prompts/` and launch it with `/prompt-workflow`. The adapter compiles prompt steps into a workflow script, so templates describe the work instead of embedding raw `subagent` tool calls. You can ask the parent agent to create or update these prompt files from a process described in natural language.
|
|
401
401
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "pi-subagents",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.76.0",
|
|
5
5
|
"description": "Pi extension for single-agent delegation and scripted multi-agent workflows",
|
|
6
6
|
"author": "Nico Bailon",
|
|
7
7
|
"license": "MIT",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"pi-subagents": "install.mjs"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
+
"@js-temporal/polyfill": "0.5.1",
|
|
20
21
|
"acorn": "8.18.0",
|
|
21
22
|
"jiti": "2.7.0",
|
|
22
23
|
"undici": "8.10.2",
|
|
@@ -89,9 +89,9 @@ function samePath(left, right) {
|
|
|
89
89
|
return false;
|
|
90
90
|
// Git for Windows and Node can spell the same temp path through different
|
|
91
91
|
// drive or 8.3 aliases, so use filesystem identity as the fail-closed fallback.
|
|
92
|
-
const leftStat = fs.statSync(left);
|
|
93
|
-
const rightStat = fs.statSync(right);
|
|
94
|
-
return leftStat.dev !==
|
|
92
|
+
const leftStat = fs.statSync(left, { bigint: true });
|
|
93
|
+
const rightStat = fs.statSync(right, { bigint: true });
|
|
94
|
+
return leftStat.dev !== 0n && leftStat.ino !== 0n && leftStat.dev === rightStat.dev && leftStat.ino === rightStat.ino;
|
|
95
95
|
}
|
|
96
96
|
function readBoundedRegularFile(file) {
|
|
97
97
|
const stat = fs.lstatSync(file);
|