pi-subagents 0.45.2 → 0.46.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 +24 -0
- package/README.md +2 -0
- package/docs/agents.md +342 -0
- package/docs/configuration.md +320 -0
- package/docs/extension-api.md +308 -0
- package/docs/missions.md +117 -0
- package/docs/models.md +190 -0
- package/docs/observability.md +174 -0
- package/docs/tool-reference.md +343 -0
- package/docs/watchdog.md +176 -0
- package/docs/workflows.md +163 -0
- package/package.json +4 -2
- package/skills/pi-subagents/references/execution-controls.md +2 -2
- package/src/agents/agents.ts +17 -8
- package/src/agents/frontmatter.ts +7 -3
- package/src/agents/skills.ts +2 -9
- package/src/api/project-panes.ts +30 -0
- package/src/extension/config.ts +15 -1
- package/src/extension/index.ts +36 -16
- package/src/extension/schemas.ts +3 -2
- package/src/extension/subagent-guide.ts +39 -0
- package/src/extension/tool-description.ts +4 -4
- package/src/inspectors/herdr/project-panes.ts +457 -62
- package/src/missions/actions.ts +25 -2
- package/src/missions/lifecycle.ts +21 -2
- package/src/missions/store.ts +77 -1
- package/src/missions/types.ts +33 -0
- package/src/runs/background/async-execution.ts +7 -1
- package/src/runs/background/completion-replay.ts +267 -0
- package/src/runs/background/result-watcher.ts +12 -4
- package/src/runs/background/wait-completions.ts +39 -5
- package/src/runs/background/wait-subscriptions.ts +18 -3
- package/src/runs/foreground/execution.ts +4 -0
- package/src/runs/foreground/foreground-history.ts +137 -0
- package/src/runs/foreground/subagent-executor.ts +310 -44
- package/src/shared/fork-context.ts +13 -0
- package/src/shared/prompt-resources.ts +51 -0
- package/src/shared/types.ts +30 -1
- package/src/shared/utf8.ts +11 -0
- package/src/slash/prompt-workflows.ts +2 -15
- package/src/slash/slash-commands.ts +19 -1
- package/src/tui/fleet-status.ts +8 -2
- package/src/tui/fleet.ts +135 -25
- package/src/tui/render.ts +120 -7
- package/src/workflows/scripted-workflow.ts +167 -10
package/docs/missions.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Missions and schedules
|
|
2
|
+
|
|
3
|
+
Durable records for delegated work: missions wrap runs so you can recover them later, and schedules launch work on a timer.
|
|
4
|
+
|
|
5
|
+
## Missions
|
|
6
|
+
|
|
7
|
+
Missions are durable wrappers around runs. The noun map:
|
|
8
|
+
|
|
9
|
+
- **Project/codebase** — where work happens.
|
|
10
|
+
- **Mission** — why delegated work exists and how to recover it later.
|
|
11
|
+
- **Run** — one actual subagent execution.
|
|
12
|
+
- **Receipt** — proof or a link for an external outcome, such as a PR, CI check, deployment, or release.
|
|
13
|
+
|
|
14
|
+
Ordinary workflow launches create one enclosing mission by default, with detailed JSON records under `<cwd>/.pi-subagents/missions/` linking objectives, run ids, lifecycle status, decisions, artifact paths, and delivery receipts. Workflow children do not create separate missions. Each workflow child attempt is stored in the enclosing mission with its stable workflow key, run id when known, agent, task metadata, timestamps, session and artifact paths, and latest status heartbeat.
|
|
15
|
+
|
|
16
|
+
Behavior:
|
|
17
|
+
|
|
18
|
+
- Automatic persistence failures do not block the run and are reported as `details.missionWarning`. Explicit `missionId` and `mission` requests remain strict before launch.
|
|
19
|
+
- Human receipts end with `Mission: <id> (<status>)`, while JSON/structured output text stays unchanged and `details.missionId` is authoritative.
|
|
20
|
+
- Pass `mission: false` for an intentionally ephemeral workflow. It creates no mission for the workflow or its children and has no `state` global.
|
|
21
|
+
- Set `missions.enabled: false` to disable automatic mission creation; explicit mission fields and actions still work.
|
|
22
|
+
- A workflow with a mission can use `await state.get(key)` and `await state.set(key, value)` for durable JSON state. Missing keys return `undefined`. Keys use the same format as `runs.run` keys. Each set takes the state-file lock, reads the latest file, merges the key, and atomically writes `<cwd>/.pi-subagents/missions/<mission-id>/state.json`. The complete file cannot exceed 256 KiB. Each workflow caches the file on its first `get`. A `mission:false` workflow has no `state` global.
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
const created = subagent({
|
|
26
|
+
action: "mission.create",
|
|
27
|
+
mission: { title: "Ship auth refresh", objective: "Implement and validate token refresh" }
|
|
28
|
+
})
|
|
29
|
+
subagent({
|
|
30
|
+
workflowScript: `return runs.run("main", { agent: "worker", task: "Implement the approved auth refresh plan" })`,
|
|
31
|
+
missionId: "<mission-id>"
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
// Or create and attach in one launch
|
|
35
|
+
subagent({
|
|
36
|
+
workflowScript: `return runs.run("main", { agent: "worker", task: "Implement the approved plan" })`,
|
|
37
|
+
mission: { title: "Ship auth refresh" }
|
|
38
|
+
})
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Goal missions
|
|
42
|
+
|
|
43
|
+
Set `goal: true` with a token budget to make an open mission an active continuation driver:
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
subagent({
|
|
47
|
+
action: "mission.create",
|
|
48
|
+
mission: {
|
|
49
|
+
title: "Ship auth refresh",
|
|
50
|
+
objective: "Implement and validate token refresh",
|
|
51
|
+
goal: true,
|
|
52
|
+
budget: { tokens: 400000 }
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
After each parent turn, an idle goal mission sends one needs-attention notice with its title, remaining token budget, and next ready action. The action comes from `state.nextReadyAction`, `state.nextAction`, a state item with `status: "ready"`, an open decision, or linked-run state. A workflow can write `state.nextReadyAction` to tell the next notice exactly what work is ready. When the latest linked workflow has a completed retained child, the notice names that child as the `resume` target. The extension never launches or replans goal work by itself.
|
|
58
|
+
|
|
59
|
+
Linked-run token totals are stored on each run and folded into mission `usage`. An active linked run suppresses notices. Reaching the token budget changes the goal status to `budget-exhausted` and stops notices without closing the mission or reporting success.
|
|
60
|
+
|
|
61
|
+
Pause and resume notices with `mission.update` and `{ goal: { paused: true } }` or `{ goal: { paused: false } }`. Set `{ goal: false }` to disable goal mode. `mission.close` also ends the loop.
|
|
62
|
+
|
|
63
|
+
### Managing missions
|
|
64
|
+
|
|
65
|
+
Use `mission.list`, `mission.show`, `mission.update`, `mission.resolve-decision`, `mission.attach-run`, and `mission.close`.
|
|
66
|
+
|
|
67
|
+
- Use `mission.update` to record decisions, artifacts, labels, summaries, and delivery receipts while work runs. Adding a decision gates active or completed missions as `needs_decision`; planned and waiting missions keep their lifecycle status while the decision stays visible. Resolve it with `mission.resolve-decision`, `missionId`, the decision `id`, and a resolution in `summary`. A gated mission returns to `active` after its last open decision is resolved.
|
|
68
|
+
- `mission.show` includes each workflow child's latest status, phase, update time, session path metadata, and heartbeat. The ledger is a recovery record only. It does not schedule or restart children.
|
|
69
|
+
- Receipts are durable links for pull requests, CI, deployments, or releases, each with `kind`, `status`, `title`, `url`, and optional `description`. They record delivery state only; pi-subagents does not merge, poll CI, or deploy.
|
|
70
|
+
- Use `mission.close` with a terminal status and summary when a mission is done.
|
|
71
|
+
- After compaction or restart, resume from `mission.list`/`mission.show` first: `mission.show` refreshes linked async status where available, then use the linked run ids with normal `status`, `steer`, `resume`, or `stop` actions.
|
|
72
|
+
- `mission.list` with `missionScope: "global"` reads the user-local pointer index under the Pi agent directory. Project records remain the source of truth, and missing records are reported as stale rather than hiding other projects.
|
|
73
|
+
|
|
74
|
+
### Cross-project work
|
|
75
|
+
|
|
76
|
+
Keep same-project tasks on ordinary subagents. Use an explicit `cwd` for small bounded work in another project.
|
|
77
|
+
|
|
78
|
+
For substantial or long-running work in another project, open a project-owned Herdr pane with `project.open` and give that project Pi session a narrow mission/result contract (see [extension-api.md](extension-api.md#herdr-integration)). The project pane owns its own subagents; do not model it as ordinary child nesting or expect existing headless runs to move into the pane.
|
|
79
|
+
|
|
80
|
+
Mission storage configuration (`missions.directory`, `retainTerminal`, `globalIndex`) is in [configuration.md](configuration.md#missions).
|
|
81
|
+
|
|
82
|
+
## Schedules
|
|
83
|
+
|
|
84
|
+
Durable schedules are enabled by default and stored per project under `.pi-subagents/schedules/<id>/`.
|
|
85
|
+
|
|
86
|
+
Create a one-shot schedule:
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
subagent({
|
|
90
|
+
action: "schedule.create",
|
|
91
|
+
id: "evening-review",
|
|
92
|
+
name: "Evening review",
|
|
93
|
+
at: "+30m",
|
|
94
|
+
workflowScript: `return runs.run("main", { agent: "reviewer", task: "Review the current diff." })`
|
|
95
|
+
})
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Create a fixed recurring workflow:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
subagent({ action: "schedule.create", id: "backlog", every: "6h", catchUp: "latest", workflowScript: "..." })
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Fixed intervals support `m`, `h`, `d`, and `w` units and advance from the planned time without completion drift.
|
|
105
|
+
|
|
106
|
+
Manage schedules with `schedule.list`, `schedule.show`, `schedule.history`, `schedule.pause`, `schedule.resume`, `schedule.run`, `schedule.run-due`, and `schedule.delete`.
|
|
107
|
+
|
|
108
|
+
Behavior:
|
|
109
|
+
|
|
110
|
+
- Runs always launch async with fresh context and disable automatic mission creation; mission attachment is deferred from this first slice.
|
|
111
|
+
- Definitions, bounded history, append-only events, and per-run receipts are stored with mode `0600`.
|
|
112
|
+
- `overlap` is currently fixed to `skip`; `catchUp` supports `latest` (default) and `none`.
|
|
113
|
+
- `schedule.run-due` lets an external launcher start due project work without making `pi-subagents` a daemon.
|
|
114
|
+
- Calendar recurrence, cron, queue/replace overlap, and the schedule TUI inspector are intentionally deferred to the next slice.
|
|
115
|
+
- The old `schedule`, `schedule-list`, `schedule-status`, and `schedule-cancel` actions were removed in a hard cutover.
|
|
116
|
+
|
|
117
|
+
Disable or bound schedules with the `scheduledRuns` config key in [configuration.md](configuration.md#scheduledruns).
|
package/docs/models.md
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# Models
|
|
2
|
+
|
|
3
|
+
How subagents pick models, and how to change that.
|
|
4
|
+
|
|
5
|
+
Builtin agents inherit your current Pi default model. This keeps new installs from depending on a provider you may not have configured. From there you can layer defaults and overrides:
|
|
6
|
+
|
|
7
|
+
- `subagents.defaultModel` — a default for every subagent that does not set its own model.
|
|
8
|
+
- `subagents.agentOverrides.<name>.model` — pin one role.
|
|
9
|
+
- Per-run overrides — for one launch only.
|
|
10
|
+
|
|
11
|
+
Precedence, strongest first: per-run override → agent frontmatter `model` → `agentOverrides.<name>.model` → `subagents.defaultModel` → the parent session model.
|
|
12
|
+
|
|
13
|
+
## Setting defaults and overrides
|
|
14
|
+
|
|
15
|
+
In `~/.pi/agent/settings.json` (user) or the project config settings file (`.pi/settings.json` in standard Pi; project wins):
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"defaultModel": "deepseek-v4-pro",
|
|
20
|
+
"subagents": {
|
|
21
|
+
"defaultModel": "deepseek-v4-flash",
|
|
22
|
+
"agentOverrides": {
|
|
23
|
+
"oracle": {
|
|
24
|
+
"model": "deepseek-v4-pro"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
For one run, put the override in the command:
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
/run reviewer[model=anthropic/claude-sonnet-4:high] "Review this diff"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
For a persistent role override with a backup model for provider failures:
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"subagents": {
|
|
42
|
+
"agentOverrides": {
|
|
43
|
+
"reviewer": {
|
|
44
|
+
"model": "anthropic/claude-sonnet-4",
|
|
45
|
+
"thinking": "high",
|
|
46
|
+
"fallbackModels": ["openai/gpt-5-mini"]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`subagents.defaultModel` applies to builtin, package, user, and project agents that do not set `model` in frontmatter. Per-run model overrides and `agentOverrides.<name>.model` still win, and explicit agent frontmatter still wins over the global default. The same `agentOverrides` block can change `tools`, `skills`, inherited context, prompt text, or disable a builtin (see [agents.md](agents.md)). Matching user and project agents also receive override fields that their frontmatter leaves unset, so a shared project config agent can keep the persona while local settings choose the model.
|
|
54
|
+
|
|
55
|
+
## Recommended model tiering (optional)
|
|
56
|
+
|
|
57
|
+
A setup that works well in practice: route agents by task shape instead of running everything on one model. Four tiers:
|
|
58
|
+
|
|
59
|
+
1. **Fast workhorse** — the cheapest capable model at low thinking, for recon, lookups, and mechanical edits. Example: `openai-codex/gpt-5.6-luna:low` on `scout`.
|
|
60
|
+
2. **Standard well-scoped** — a mid-tier model at medium thinking, for most delegations: routine multi-file edits, focused reviews, straightforward implementation. Example: `openai-codex/gpt-5.6-terra:medium` on `worker`, `reviewer`, and a lightweight `delegate` agent.
|
|
61
|
+
3. **Deep but bounded** — a top reasoning model at high thinking, only for hard tasks that arrive with explicit goals and completion criteria. These models tend to loop on vague goals, so keep them off open-ended work. Example: `openai-codex/gpt-5.6-sol:high` on oracle-style agents.
|
|
62
|
+
4. **Taste and intent** — a model that reads human intent well and makes judgment calls without looping, for ambiguous work: UX and design decisions, product tradeoffs, planning from vague requirements, writing quality. Example: `anthropic/claude-fable-5` at `low` for lighter passes and `medium` for harder ones.
|
|
63
|
+
|
|
64
|
+
The routing rule: use the capability tiers (1–3) when the task is well-scoped, and the intent tier (4) when scoping or judging is the task itself.
|
|
65
|
+
|
|
66
|
+
Give tier-4 agents cross-provider `fallbackModels` so subscription usage limits degrade gracefully instead of failing the run. Fallback triggers on rate-limit and overload errors automatically:
|
|
67
|
+
|
|
68
|
+
```yaml
|
|
69
|
+
---
|
|
70
|
+
name: shaper
|
|
71
|
+
description: Open-ended design/UX/product/planning agent for ambiguous tasks
|
|
72
|
+
model: anthropic/claude-fable-5
|
|
73
|
+
thinking: medium
|
|
74
|
+
fallbackModels: openai-codex/gpt-5.5:high
|
|
75
|
+
---
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
One interaction worth knowing for tier 4: forked context over an Anthropic parent transcript with signed thinking blocks forces the child's thinking off, so intent-tier agents work best with fresh context.
|
|
79
|
+
|
|
80
|
+
## Thinking level defaults
|
|
81
|
+
|
|
82
|
+
Set `subagents.defaultThinking` to give builtin, package, user, and project agents without a `thinking` value a shared thinking level, independent of the parent session's default. Project settings win over user settings. Explicit frontmatter, `agentOverrides.<name>.thinking`, and per-run thinking overrides still win. `thinking: false` remains an explicit opt-out:
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"subagents": {
|
|
87
|
+
"defaultThinking": "medium",
|
|
88
|
+
"agentOverrides": {
|
|
89
|
+
"reviewer": { "thinking": "high" }
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
If your provider rejects model IDs with thinking suffixes, set `subagents.disableThinking: true` in user or project settings. That clears bundled builtin thinking defaults in one place. An explicit higher-precedence `agentOverrides.<name>.thinking` value can opt a role back in. Existing custom-agent frontmatter remains authoritative.
|
|
96
|
+
|
|
97
|
+
## Extension defaults
|
|
98
|
+
|
|
99
|
+
Set `subagents.defaultExtensions` to give builtin, package, user, and project agents without an `extensions` field a shared extension allowlist:
|
|
100
|
+
|
|
101
|
+
- Absent: preserves Pi's normal ambient extension discovery.
|
|
102
|
+
- Empty array: sets `extensions: []` for agents that do not explicitly define it, disabling ambient extension loading.
|
|
103
|
+
- Non-empty array: supplies that allowlist to agents that do not explicitly define one.
|
|
104
|
+
|
|
105
|
+
Project settings win over user settings. Use `agentOverrides.<name>.extensions` for per-agent settings; explicit custom-agent frontmatter remains authoritative.
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"subagents": {
|
|
110
|
+
"defaultExtensions": [],
|
|
111
|
+
"agentOverrides": {
|
|
112
|
+
"researcher": {
|
|
113
|
+
"extensions": ["./tools/research.ts"]
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
A non-array value, an array containing a non-string entry, or an empty/whitespace-only string raises a settings error naming `defaultExtensions` and the offending settings file, matching the validation pattern used by `defaultModel` and `defaultThinking`.
|
|
121
|
+
|
|
122
|
+
## Inspecting the live mapping
|
|
123
|
+
|
|
124
|
+
To see what `pi-subagents` has actually loaded right now:
|
|
125
|
+
|
|
126
|
+
```text
|
|
127
|
+
/subagents-models
|
|
128
|
+
/subagents-models reviewer
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
That reports the live runtime mapping, which can differ from settings on disk until you reload Pi.
|
|
132
|
+
|
|
133
|
+
## Fuzzy model matching
|
|
134
|
+
|
|
135
|
+
You do not have to spell a model exactly. Model ids are matched fuzzily against the registry, so these all resolve to the same model:
|
|
136
|
+
|
|
137
|
+
- Provider separator variations: `anthropic/claude-sonnet-4`, `anthropic:claude-sonnet-4`, `anthropic.claude-sonnet-4`
|
|
138
|
+
- Id separator variations: `claude-haiku-4.5` vs `claude-haiku-4-5`
|
|
139
|
+
- Case differences: `Claude-Sonnet-4` vs `claude-sonnet-4`
|
|
140
|
+
- Optional trailing date stamps: `claude-haiku-4-5-20251001` or `claude-haiku-4-5-2025-10-01` vs `claude-haiku-4-5`
|
|
141
|
+
|
|
142
|
+
Exact `provider/id` matches still win, and a qualified provider query never silently switches providers — it only matches within the named provider. Ambiguous bare ids that exist under multiple providers still require a provider prefix or the current session's provider to disambiguate.
|
|
143
|
+
|
|
144
|
+
## Model scope enforcement
|
|
145
|
+
|
|
146
|
+
To keep subagents inside a budget or compliance profile, enforce a model scope. Put `subagents.modelScope` in user or project settings (project overrides user):
|
|
147
|
+
|
|
148
|
+
```json
|
|
149
|
+
{
|
|
150
|
+
"subagents": {
|
|
151
|
+
"modelScope": {
|
|
152
|
+
"enforce": true,
|
|
153
|
+
"allow": ["anthropic/*", "openai/gpt-5-*"]
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
- `allow` is a list of glob patterns matched against the resolved `provider/id` (only `*` is special, case-insensitive). A resolved model that matches none of the patterns is rejected.
|
|
160
|
+
- Models you pass explicitly — the tool-call `model`, `--model`, or a clarify pick — error and abort the run.
|
|
161
|
+
- Models that come from agent frontmatter, `subagents.defaultModel`, or the inherited parent session model only warn, so existing configurations keep working while you tighten the scope.
|
|
162
|
+
- `enforce: true` requires a non-empty `allow` list; otherwise the config is rejected at load time.
|
|
163
|
+
|
|
164
|
+
## Profiles and provider model catalogs
|
|
165
|
+
|
|
166
|
+
Profiles let you generate and save role-to-model assignments from a provider's live catalog.
|
|
167
|
+
|
|
168
|
+
Profiles are stored under:
|
|
169
|
+
|
|
170
|
+
```text
|
|
171
|
+
~/.pi/agent/profiles/pi-subagents/
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Provider model catalogs are cached under:
|
|
175
|
+
|
|
176
|
+
```text
|
|
177
|
+
~/.pi/agent/profiles/pi-subagents/providers/
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
The workflow:
|
|
181
|
+
|
|
182
|
+
```text
|
|
183
|
+
/subagents-refresh-provider-models openai-codex
|
|
184
|
+
/subagents-generate-profiles openai-codex
|
|
185
|
+
/subagents-load-profile openai-codex.quota
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
- `/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.
|
|
189
|
+
- `/subagents-generate-profiles` uses the provider catalog to produce quota and quality profiles.
|
|
190
|
+
- `/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.
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Observability
|
|
2
|
+
|
|
3
|
+
Where running subagents show up, how to inspect them, and the files and events they leave behind.
|
|
4
|
+
|
|
5
|
+
## Foreground runs
|
|
6
|
+
|
|
7
|
+
Foreground runs stream progress in the conversation while they run. They default to a generous 30-minute wall-clock timeout when neither the call nor the selected agent provides a timeout; explicit `timeoutMs`/`maxRuntimeMs` and agent defaults win.
|
|
8
|
+
|
|
9
|
+
Live progress shows compact detail for single, chain, and parallel modes: current tool, recent output, token counts, aggregate cost, duration, activity freshness, current-tool duration, and chain graph metadata when available.
|
|
10
|
+
|
|
11
|
+
Press Pi's configured expand key (`Ctrl+O` by default) to expand the full streaming view with complete output per step.
|
|
12
|
+
|
|
13
|
+
Sequential chains show a flow line like `done scout → running worker`. Chains with parallel steps show per-step cards instead. Chain status uses `label` and `phase` metadata when present, while falling back to agent names for older chains.
|
|
14
|
+
|
|
15
|
+
## Background runs
|
|
16
|
+
|
|
17
|
+
Background runs keep working after control returns to you. Inspect them with:
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
subagent({ action: "status" }) // all active runs
|
|
21
|
+
subagent({ action: "status", id: "..." }) // one run
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Or ask naturally: "Show me the current async runs."
|
|
25
|
+
|
|
26
|
+
To inspect one background child in text, use `subagent({ action: "status", id: "...", view: "transcript" })`; add `index` for a specific child in a parallel or chain run.
|
|
27
|
+
|
|
28
|
+
## FleetView
|
|
29
|
+
|
|
30
|
+
In the TUI, a persistent FleetView below the editor keeps active work visible as a compact summary. Set `fleetViewPlacement` to `"aboveEditor"` to move it above the editor.
|
|
31
|
+
|
|
32
|
+
When the focused editor is empty, press `↓` or `←` to expand the summary into `main` plus active children with task, elapsed time, and token totals. Then use `↑`/`↓` or `j`/`k` to select a child and `Enter` to inspect it. Printable navigation keys are never intercepted before activation.
|
|
33
|
+
|
|
34
|
+
FleetView replaces the legacy above-editor async widget by default. Successful background completions stay quiet so inactive Pi tabs are not marked unread, while failed or paused completions still notify the originating session. Parallel runs show every active child independently. Chains with parallel groups keep their grouped shape in progress and results, so failed or paused agents stay visible next to completed ones. When a child is explicitly allowed to fan out with `tools: subagent`, its nested runs appear under that parent child in the main status tree instead of being hidden inside the child process.
|
|
35
|
+
|
|
36
|
+
## The fleet inspector
|
|
37
|
+
|
|
38
|
+
`/subagents-fleet` opens the live fleet inspector with current-session foreground work, recent async children, structured Markdown/tool transcripts, and completed output/session paths.
|
|
39
|
+
|
|
40
|
+
Default keys:
|
|
41
|
+
|
|
42
|
+
- `↑`/`↓` or `j`/`k` — select a child
|
|
43
|
+
- `Shift+K`/`Shift+J` — scroll one line
|
|
44
|
+
- `PgUp`/`PgDn` — scroll one page
|
|
45
|
+
- `x`/`Ctrl+O` — toggle tool details
|
|
46
|
+
- `r` — refresh
|
|
47
|
+
- `Esc` — close
|
|
48
|
+
- `s` — compose an acknowledged message to a selected live async child; Tab cycles `steer`, `follow_up`, and `auto`
|
|
49
|
+
- `D` — stop a selected child's top-level async run after confirmation
|
|
50
|
+
- `H` — open the selected active async child in a Herdr inspector pane (Herdr 0.7.5+)
|
|
51
|
+
|
|
52
|
+
Set `fleetKeybindings` in the extension config to replace inspector-level keys when a terminal intercepts keys such as `PgUp`, `PgDn`, `Home`, or `End`. Prompt modes keep fixed keys such as `Esc`, `Enter`, `Tab`, and stop-confirmation `Y`/`N`.
|
|
53
|
+
|
|
54
|
+
`Ctrl+Alt+F` opens the same inspector even while a foreground turn is active and slash input is queued.
|
|
55
|
+
|
|
56
|
+
Without a TUI, `/subagents-fleet` retains the textual `subagent({ action: "status", view: "fleet" })` fallback, and mutations use explicit commands: run `/subagents-stop` and pick from the selector, or use `/subagents-stop <run-id>` / `subagent({ action: "stop", id: "..." })` when you already know the id.
|
|
57
|
+
|
|
58
|
+
Use `/subagents-detach [run-id]` only for an active foreground single-subagent run you want to leave running without terminating; the eventual result remains available through status/wait.
|
|
59
|
+
|
|
60
|
+
If something feels misconfigured, run `/subagents-doctor` or ask: "Check whether subagents and intercom are set up correctly."
|
|
61
|
+
|
|
62
|
+
## Async run artifacts
|
|
63
|
+
|
|
64
|
+
Async runs write machine-readable lifecycle artifacts for observability and workflow gates:
|
|
65
|
+
|
|
66
|
+
```text
|
|
67
|
+
<tmpdir>/pi-subagents-<scope>/async-subagent-runs/<id>/
|
|
68
|
+
status.json
|
|
69
|
+
events.jsonl
|
|
70
|
+
output-<n>.log
|
|
71
|
+
subagent-log-<id>.md
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
- `status.json` powers the widget and `subagent({ action: "status" })` output.
|
|
75
|
+
- `events.jsonl` contains wrapper events plus child Pi JSON events annotated with run and step metadata, including correlated `subagent.steer.requested`, `scheduled`, `routed`, `queued`, `delivered`, `failed`, and `recovered` events plus failure/partial/recovery notices.
|
|
76
|
+
- `output-<n>.log` is a live human-readable tail.
|
|
77
|
+
- Fallback information is persisted so background runs are debuggable after completion.
|
|
78
|
+
|
|
79
|
+
For a top-level async run, `details.asyncDir` points at that directory; the final summary is written to Pi's subagent results directory as `<runId>.json`. Nested async runs use the same shape under the nested async root and are discoverable through status projections that read the nested-run registry. These files are append/update artifacts only; interactive foreground behavior is unchanged.
|
|
80
|
+
|
|
81
|
+
The result file is consumed and deleted once its completion notice is delivered. Before deletion, the watcher writes a versioned replay record under `<resultsDir>/completion-replay/<runId>.json` and a bounded output archive under `<resultsDir>/output-archives/<runId>.json`. Replay records expire with the completion deduplication window and are best-effort temporary state, not a permanent run ledger.
|
|
82
|
+
|
|
83
|
+
`subagent_wait` surfaces a slim projection of each terminal payload it covered in its own tool-result `details.completions` — run identity, per-child agent/`runId`/success, artifact paths, and the bounded `archivePath`, without duplicating output text. It reads the replay when watcher delivery or a watcher restart has removed the one-shot result file and in-memory completion state is unavailable. Durable non-blocking wait subscriptions use the same replay in their delivered details. Workflow result files record each child's `runId` explicitly, since a workflow child's `artifactPaths` entry points at its saved output rather than the artifact files keyed by the id. Extensions observing `tool_result` events can read run and artifact identity from there instead of parsing the text summary.
|
|
84
|
+
|
|
85
|
+
Output archives reference an existing child output artifact or session file when one is available. For children without either file, the archive stores only the tail of result text, bounded to 64 KiB per run, and records whether it was truncated. Replay and archive JSON use `version: 1`; consumers must ignore unknown fields.
|
|
86
|
+
|
|
87
|
+
Nested fanout status is stored as compact sidecar event/registry metadata and merged into parent status views and result/intercom payloads; full recursive status snapshots are not embedded in parent result files.
|
|
88
|
+
|
|
89
|
+
Consumers should read these JSON files instead of scraping terminal output. Unknown fields and event types should be ignored for forward compatibility.
|
|
90
|
+
|
|
91
|
+
### Status and result fields
|
|
92
|
+
|
|
93
|
+
The status/result fields are: `lifecycleArtifactVersion`, `runId`/`id`, `sessionId`, `mode`, `state`, `startedAt`, `lastUpdate`, `endedAt`, `durationMs`, `cwd`, `asyncDir`, `sessionFile`, `outputFile`, `workflowGraph`, `steps`, `results`, `totalTokens`, `totalCost`, `model`/`attemptedModels`/`modelAttempts`, `toolCount`, `turnCount`, optional `launchResolvedExtensions`, optional `runtimeAcknowledgedExtensions`, and nested `children` when a child is allowed to launch subagents.
|
|
94
|
+
|
|
95
|
+
`launchResolvedExtensions` is parent-resolved launch intent only: it reports opaque extension identifiers and whether ambient extensions were disabled, without exposing raw extension paths or claiming the child runtime acknowledged that those extensions loaded.
|
|
96
|
+
|
|
97
|
+
### Runtime extension acknowledgement
|
|
98
|
+
|
|
99
|
+
Cooperating child extensions can acknowledge child-runtime registration by emitting `subagent:acknowledge-extension` on the child process `pi.events` bus with payload `{ id: string }`.
|
|
100
|
+
|
|
101
|
+
Acknowledgement ids are self-declared opaque strings. They must be non-empty, at most 128 characters, contain only `A-Z`, `a-z`, `0-9`, `.`, `_`, `:`, `@`, `+`, or `-`, and must not contain `/`, `\`, or `..`.
|
|
102
|
+
|
|
103
|
+
The reported `runtimeAcknowledgedExtensions` projection is `{ version: 1, source: "child-runtime", ids, omitted }`. It deduplicates ids, keeps at most 32 ids, and counts additional valid unique ids in `omitted`. It is best-effort observability only: absence means no cooperating extension acknowledged, and presence means only that the extension registered in the child runtime, not that its tools, health checks, or features succeeded. Late acknowledgements after terminal serialization are ignored.
|
|
104
|
+
|
|
105
|
+
### Lifecycle events
|
|
106
|
+
|
|
107
|
+
`events.jsonl` records lifecycle transitions such as `subagent.run.started`, `subagent.step.started`, `subagent.step.completed`/`failed`/`paused`/`stopped`, control attention events, nested interrupt failures, and `subagent.run.completed`/`stopped`. Run boundary events include the lifecycle artifact version.
|
|
108
|
+
|
|
109
|
+
### Process-terminal proof
|
|
110
|
+
|
|
111
|
+
Lifecycle artifact v3 adds `process-terminal-candidate.json` (private runner evidence) and `process-terminal.json` (the public proof projection).
|
|
112
|
+
|
|
113
|
+
A proof is `observed` only after the live parent observes the exact detached runner's `close` event, every recorded child writer has a close record, and any tracked canonical-session lease is free. If the observer is unavailable, the proof is `unknown`; do not infer process exit from `endedAt`, result-file existence, PID disappearance, or lease-directory absence.
|
|
114
|
+
|
|
115
|
+
The `subagent:process-terminal` event and RPC `ping.capabilities.processTerminalProof` expose this status. Process proof is point-in-time evidence and remains separate from execution success or stopped/non-resumable state.
|
|
116
|
+
|
|
117
|
+
### Child-protocol bounds
|
|
118
|
+
|
|
119
|
+
Foreground and async runners share bounded child-protocol handling:
|
|
120
|
+
|
|
121
|
+
- A child JSONL line above 16 MiB fails with structured `protocolError` code `protocol_output_limit`. Oversized Pi `turn_end` and `agent_end` aggregates are the exception because they duplicate granular events, so runners replace them with bounded lifecycle records while preserving `agent_end.willRetry`.
|
|
122
|
+
- Stderr retains only its latest 128 KiB.
|
|
123
|
+
- Split UTF-8 and final unterminated JSON events remain valid.
|
|
124
|
+
- `agent_end.willRetry` defers completion until the child settles.
|
|
125
|
+
- Current Pi builds use `agent_settled` as the terminal watermark; older builds retain the bounded terminal-message fallback.
|
|
126
|
+
|
|
127
|
+
## Chain and debug artifacts
|
|
128
|
+
|
|
129
|
+
Each chain run creates a scratch directory under its resolved chain root. With the default `artifactDir: "project"`, that root is `<cwd>/.pi-subagents/chain-runs/`. With `artifactDir: "session"` or `"temp"`, it is user-scoped temp storage:
|
|
130
|
+
|
|
131
|
+
```text
|
|
132
|
+
<tmpdir>/pi-subagents-<scope>/chain-runs/{runId}/
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
A run directory may contain files such as `context.md`, `plan.md`, `progress.md`, and `parallel-{stepIndex}/.../output.md`. User-scoped temp chain directories older than 24 hours are cleaned up on extension startup; project-local and explicit persistent roots are not age-scanned.
|
|
136
|
+
|
|
137
|
+
Debug artifacts live under `{sessionDir}/subagent-artifacts/`, `.pi-subagents/artifacts/` for project-scoped runs, or a user-scoped temp artifact directory. Single-run relative `output` files are saved under `{artifactsDir}/outputs/{runId}/` unless `singleRunOutputBaseDir` is configured. Per task you may see:
|
|
138
|
+
|
|
139
|
+
- `{runId}_{agent}_input.md`
|
|
140
|
+
- `{runId}_{agent}_output.md`
|
|
141
|
+
- `{runId}_{agent}.jsonl`
|
|
142
|
+
- `{runId}_{agent}_meta.json`
|
|
143
|
+
|
|
144
|
+
Metadata records timing, usage, exit code, final model, attempted models, fallback attempt outcomes, and the resolved acceptance ledger with its parsed child report.
|
|
145
|
+
|
|
146
|
+
For npm package projects, project-scoped artifacts need a `.npmignore` rule (or `.gitignore` when no `.npmignore` exists) or a `files` allowlist that does not include `.pi-subagents/`. pi-subagents warns at launch when these package settings can include the artifacts. Use `artifactDir: "session"` or `"temp"` to keep them outside the package worktree.
|
|
147
|
+
|
|
148
|
+
## Sessions
|
|
149
|
+
|
|
150
|
+
Session files are stored under a per-run session directory. With `context: "fork"`, each child starts with `--session <branched-session-file>` produced from the parent's current leaf. That is a real session fork, not an injected summary.
|
|
151
|
+
|
|
152
|
+
## Completion notifications
|
|
153
|
+
|
|
154
|
+
Async completions belong only to the originating session. The result watcher emits `subagent:async-complete`, and the extension consumes that event to record completion state.
|
|
155
|
+
|
|
156
|
+
Successful sibling completions are held briefly and delivered as a quiet grouped completion when they finish within a short window (see `completionBatch` in [configuration.md](configuration.md)), avoiding unread markers on inactive tabs. Failed and paused completions remain visible and fire immediately.
|
|
157
|
+
|
|
158
|
+
## Events
|
|
159
|
+
|
|
160
|
+
Async events:
|
|
161
|
+
|
|
162
|
+
- `subagent:async-started`
|
|
163
|
+
- `subagent:async-complete`
|
|
164
|
+
|
|
165
|
+
The `subagent:async-started` payload includes `task`, the backwards-compatible first child task truncated to 50 characters, and `goal`, the workflow-level caller task truncated to 120 characters (falling back to the first child task). Companion UI extensions can combine `goal`, `workflowGraph`, and the live lifecycle artifacts under `asyncDir` without scraping terminal output.
|
|
166
|
+
|
|
167
|
+
Intercom delivery events:
|
|
168
|
+
|
|
169
|
+
- `subagent:control-intercom`
|
|
170
|
+
- `subagent:result-intercom`
|
|
171
|
+
|
|
172
|
+
`src/extension/index.ts` registers the notification handler that consumes `subagent:async-complete`. Control/attention events are surfaced as visible parent notices and persisted for async runs. Native supervisor requests are delivered only to the exact parent session that spawned the child.
|
|
173
|
+
|
|
174
|
+
`pi.events` is in-process only. It does not reach separate Pi processes or child subagents; use the file lifecycle artifacts or `pi-intercom` for cross-process coordination.
|