pi-subagents 0.63.0 → 0.65.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.
Files changed (107) hide show
  1. package/CHANGELOG.md +57 -1
  2. package/README.md +2 -2
  3. package/agents/reviewer.md +1 -1
  4. package/agents/scout.md +1 -1
  5. package/docs/agents.md +18 -16
  6. package/docs/configuration.md +7 -15
  7. package/docs/extension-api.md +15 -6
  8. package/docs/missions.md +2 -0
  9. package/docs/observability.md +10 -12
  10. package/docs/tool-reference.md +7 -6
  11. package/docs/watchdog.md +92 -114
  12. package/docs/workflows.md +5 -3
  13. package/package.json +3 -4
  14. package/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
  15. package/skills/pi-subagents/references/execution-controls.md +6 -5
  16. package/src/agents/agent-management.ts +47 -15
  17. package/src/api/capability-ceiling.ts +0 -1
  18. package/src/api/{pi-args.ts → child-tool-plan.ts} +1 -1
  19. package/src/api/preflight.ts +2 -3
  20. package/src/extension/doctor.ts +2 -10
  21. package/src/extension/fanout-child.ts +9 -11
  22. package/src/extension/index.ts +27 -5
  23. package/src/extension/public-execution.ts +14 -0
  24. package/src/extension/rpc.ts +3 -2
  25. package/src/extension/schemas.ts +2 -1
  26. package/src/extension/tool-description.ts +9 -8
  27. package/src/intercom/native-supervisor-channel.ts +138 -60
  28. package/src/intercom/supervisor-ui.ts +243 -0
  29. package/src/runs/background/async-execution.ts +46 -24
  30. package/src/runs/background/async-job-tracker.ts +11 -0
  31. package/src/runs/background/async-resume.ts +11 -2
  32. package/src/runs/background/control-channel.ts +2 -204
  33. package/src/runs/background/notify.ts +41 -2
  34. package/src/runs/background/process-terminal.ts +1 -1
  35. package/src/runs/background/run-child-session.ts +613 -0
  36. package/src/runs/background/run-status.ts +0 -1
  37. package/src/runs/background/runner-aliases.ts +125 -0
  38. package/src/runs/background/runner-child-sessions.ts +31 -0
  39. package/src/runs/background/scheduled-runs.ts +18 -4
  40. package/src/runs/background/subagent-runner.ts +203 -898
  41. package/src/runs/foreground/async-steering-action.ts +1 -17
  42. package/src/runs/foreground/execution.ts +196 -378
  43. package/src/runs/foreground/foreground-control.ts +4 -0
  44. package/src/runs/foreground/subagent-executor.ts +121 -58
  45. package/src/runs/foreground/workflow-foreground-steering.ts +24 -98
  46. package/src/runs/shared/abort-recovery.ts +3 -3
  47. package/src/runs/shared/acceptance.ts +10 -0
  48. package/src/runs/shared/async-status-projection.ts +11 -43
  49. package/src/runs/shared/capability-ceiling.ts +1 -2
  50. package/src/runs/shared/child-hooks.ts +25 -0
  51. package/src/runs/shared/child-identity.ts +13 -2
  52. package/src/runs/shared/child-launch.ts +314 -0
  53. package/src/runs/shared/child-lifecycle.ts +25 -0
  54. package/src/runs/shared/child-runtime-config.ts +126 -0
  55. package/src/runs/shared/child-session.ts +342 -0
  56. package/src/runs/shared/child-tool-plan.ts +530 -0
  57. package/src/runs/shared/claude-code-adapter.ts +5 -1
  58. package/src/runs/shared/completion-guard.ts +1 -1
  59. package/src/runs/shared/external-cli-preflight.ts +16 -0
  60. package/src/runs/shared/mcp-direct-tool-allowlist.ts +5 -4
  61. package/src/runs/shared/model-exclusions.ts +82 -14
  62. package/src/runs/shared/model-fallback.ts +47 -4
  63. package/src/runs/shared/nested-events.ts +29 -45
  64. package/src/runs/shared/nested-path.ts +0 -14
  65. package/src/runs/shared/orca-progress-tabs.ts +12 -7
  66. package/src/runs/shared/parallel-utils.ts +0 -2
  67. package/src/runs/shared/permissions.ts +0 -13
  68. package/src/runs/shared/process-signal.ts +4 -1
  69. package/src/runs/shared/run-fanout-budget.ts +0 -13
  70. package/src/runs/shared/runtime-acknowledged-extensions.ts +0 -27
  71. package/src/runs/shared/structured-output.ts +17 -4
  72. package/src/runs/shared/subagent-control.ts +6 -2
  73. package/src/runs/shared/subagent-prompt-runtime.ts +87 -384
  74. package/src/runs/shared/tool-availability.ts +18 -62
  75. package/src/runs/shared/tool-budget.ts +0 -14
  76. package/src/runs/shared/worktree-cleanup-plan.ts +25 -6
  77. package/src/runs/shared/worktree.ts +117 -30
  78. package/src/shared/child-session-name.ts +1 -1
  79. package/src/shared/jsonl-writer.ts +11 -0
  80. package/src/shared/thinking-ceiling.ts +0 -6
  81. package/src/shared/types.ts +64 -30
  82. package/src/shared/utils.ts +3 -4
  83. package/src/slash/slash-commands.ts +0 -6
  84. package/src/tui/fleet.ts +0 -1
  85. package/src/tui/render.ts +238 -31
  86. package/src/watchdog/child-status.ts +54 -34
  87. package/src/watchdog/diff-tool.ts +77 -0
  88. package/src/watchdog/emission-guard.ts +5 -3
  89. package/src/watchdog/guidance.ts +20 -0
  90. package/src/watchdog/register-child.ts +28 -25
  91. package/src/watchdog/register-main.ts +10 -9
  92. package/src/watchdog/render.ts +4 -5
  93. package/src/watchdog/review.ts +15 -4
  94. package/src/watchdog/rules.ts +70 -0
  95. package/src/watchdog/runtime.ts +75 -92
  96. package/src/watchdog/scope.ts +0 -11
  97. package/src/watchdog/settings.ts +48 -104
  98. package/src/watchdog/types.ts +18 -32
  99. package/src/watchdog/warning-format.ts +0 -1
  100. package/src/workflows/chat-progress.ts +3 -2
  101. package/src/workflows/scripted-workflow.ts +48 -1
  102. package/src/workflows/workflow-checklist.ts +10 -12
  103. package/src/workflows/workflow-preflight.ts +28 -1
  104. package/src/runs/shared/child-protocol.ts +0 -415
  105. package/src/runs/shared/pi-args.ts +0 -1062
  106. package/src/runs/shared/subagent-startup-retry.ts +0 -116
  107. package/src/shared/post-exit-stdio-guard.ts +0 -85
package/docs/watchdog.md CHANGED
@@ -1,145 +1,142 @@
1
1
  # Watchdog and child permissions
2
2
 
3
- The watchdog is an opt-in adversarial reviewer for repo edits. This page covers what it reviews, how to pick its model, scope monitoring, LSP diagnostics, and the native child tool permission gate that uses the child watchdog as its arbiter.
3
+ The watchdog is an opt-in second model that reviews what the agent just did and pushes findings back into the transcript. It looks for missed constraints, correctness risks, test gaps, unsafe changes, loop risks, and scope drift, and says nothing when the turn is clean. It is not the `reviewer` subagent; `subagents.defaultModel` and `agentOverrides.reviewer` do not configure it.
4
4
 
5
- ## What the watchdog reviews
5
+ ## When it runs
6
6
 
7
- The watchdog is not the `reviewer` subagent. `subagents.defaultModel` and `subagents.agentOverrides.reviewer` do not configure it.
7
+ | Timing | Trigger | Gate | Delivery |
8
+ |---|---|---|---|
9
+ | Boundary review | `agent_end` of every main or child turn | Repo changed | Steered into the transcript; the agent gets one continuation, then that turn is reviewed again |
10
+ | Cadence review | Every `cadence.everyNTools` tool results, minimum 5 | Opt-in | Steered after the current tool, before the next step |
11
+ | LSP pre-pass | Before boundary review | Changed TypeScript/JavaScript files | Diagnostics become watchdog findings without a model call |
8
12
 
9
- It reviews repo edits, not ordinary conversation:
13
+ Boundary reviews coalesce a turn's edits into one final-state review. Unchanged or reverted diffs are skipped, as are `.pi/subagents/` and `tmp/` artifacts. In orchestrated runs, each writing child reviews its own worktree and the parent reviews the aggregate diff after child changes land. There is no timer or "every turn regardless of edits" mode; the closest is a low cadence such as `everyNTools: 5`. Cadence monitoring is inspired by [Scopey](https://github.com/ArchAstro/scopey).
10
14
 
11
- - It runs at the safe `agent_end` boundary, only when the current agent or child writer changed the final repo state since the start of that turn.
12
- - Multiple edits in one turn are coalesced into one review of the final changed state.
13
- - Unchanged/reverted diffs are skipped.
14
- - Generated `.pi/subagents/` or `tmp/` artifacts do not trigger review.
15
- - In orchestrated runs, each writing child can review its own edited worktree, and the parent can still review the aggregate repo diff after child changes are applied.
15
+ Children get the same boundary, cadence, and LSP behavior. Child cadence resolves from `children.overrides.<agent>.cadence`, then `children.cadence`, then top-level `cadence`:
16
16
 
17
- ## Choosing a model
17
+ ```json
18
+ {
19
+ "subagents": {
20
+ "watchdog": {
21
+ "enabled": true,
22
+ "cadence": { "everyNTools": 10 },
23
+ "children": {
24
+ "enabled": true,
25
+ "cadence": { "everyNTools": 20 },
26
+ "overrides": {
27
+ "worker": { "cadence": { "everyNTools": 5 } },
28
+ "reviewer": { "enabled": false }
29
+ }
30
+ }
31
+ }
32
+ }
33
+ }
34
+ ```
18
35
 
19
- Because the watchdog is an adversarial change reviewer, it should usually use a strong complementary model rather than a cheap/light one.
36
+ That means: main every 10 tools, worker every 5, other children every 20, reviewer never.
20
37
 
21
- Ask pi-subagents for the current strong pairing:
38
+ ## What you see
22
39
 
23
- ```text
24
- /subagents-watchdog recommend-model
25
- /subagents-watchdog session model recommended
26
- /subagents-watchdog model recommended
40
+ Every finding is an ordinary transcript message: expandable, scrollable, and persisted in session JSONL. A clean review shows nothing.
41
+
42
+ ```
43
+ you ─▶ agent turn ─▶ edits repo ─▶ agent_end ─▶ watchdog review
44
+ ├─ clean: turn ends
45
+ └─ warning: steered in; agent continues once
27
46
  ```
28
47
 
29
- The current recommendation policy is Opus 4.8 with thinking high or GPT 5.5 with thinking high. If your main session is using one, the watchdog should use the other when that model is authenticated.
48
+ Collapsed warnings show the title and evidence line. Expanded warnings show evidence, recommended action, category, and source:
30
49
 
31
- - `session model recommended` changes only the current Pi session.
32
- - `model recommended` saves the recommendation to `~/.pi/agent/settings.json`. It does not turn the watchdog on; enable it separately with `/subagents-watchdog on`.
50
+ ```
51
+ Subagent watchdog Blocker (displayed): Claims tests passed without running them
52
+ Evidence: The transcript claims `npm test` passed but no test command appears in the tool log.
53
+ Recommended action: Run the focused test before finishing.
54
+ Category: Test Gap · Source: main
55
+ ```
33
56
 
34
- Or set the model explicitly:
57
+ When consecutive boundary reviews raise the same warning, the agent is not making progress. After `stalemateRepeats` identical warnings in a row (default 3), the warning is shown as `stalemate`, no continuation is triggered, and the turn ends. Your next prompt resets the count.
35
58
 
36
- ```text
37
- /subagents-watchdog model anthropic/claude-opus-4-8:high
38
- /subagents-watchdog model openai-codex/gpt-5.5:high
39
- /subagents-watchdog model inherit
40
- /subagents-watchdog check
41
- ```
59
+ Child watchdog findings are lifted into the parent in three ways:
42
60
 
43
- In settings files, use `subagents.watchdog.main.model` and `subagents.watchdog.main.thinking` for the main watchdog:
61
+ - The result envelope contains `watchdog.warnings` with severity, category, summary, evidence, recommended action, `addressed`, and `stalemate`, bounded to the last 20.
62
+ - The acceptance runtime check `watchdog-blocker` fails on blockers that are unaddressed or stalemate.
63
+ - Completion notices include `Watchdog blockers:` lines, and Fleet/status views show `wd:<n>` plus `resolve watchdog blockers`.
44
64
 
45
- - If `main.model` is omitted, the main watchdog uses the current session model and thinking level.
46
- - If `main.model` is set without a thinking suffix or `main.thinking`, it runs with thinking off. Prefer `:high` or `"thinking": "high"` for the strong-watchdog pairing.
65
+ `/subagents-watchdog status` shows setting sources, enabled state, runtime state, review trigger, scope, cadence, LSP status, selected model/thinking, child overrides, timeout, stalemate count, launch-rule count, review backend, last warning, changed paths, and config errors when present.
47
66
 
48
- Default strong-reviewer profile:
67
+ ## What the reviewer is given
49
68
 
50
- ```json
51
- {
52
- "subagents": {
53
- "watchdog": {
54
- "enabled": true,
55
- "main": {
56
- "model": "anthropic/claude-opus-4-8",
57
- "thinking": "high"
58
- }
59
- }
60
- }
61
- }
62
- ```
69
+ - **Turn delta** with changed repo paths. Over-long input keeps the first 6,000 characters and the tail.
70
+ - **Current scope** (`scope.enabled`, default on): bounded real user prompts, with newer prompts superseding older ones.
71
+ - **`watchdog_diff`** when inside git: diff since the session-start commit, including later commits, plus untracked paths to inspect with `read`; accepts `path` and `stat:true`.
72
+ - **`WATCHDOG.md`** standing instructions, read fresh on every review: `<project>/.pi/WATCHDOG.md` first, then `~/.pi/agent/WATCHDOG.md`, capped at 8,000 characters. Set `guidance.watchdogMd: false` to ignore them.
73
+ - **LSP diagnostics** from `typescript-language-server`, auto-detected in `node_modules/.bin` or `PATH`; it is never installed and never run over the whole workspace. Errors become blockers, warnings concerns, and info/hints stay in status.
63
74
 
64
- ## Scope monitoring
75
+ ## Choosing a model
65
76
 
66
- When enabled, the watchdog keeps a bounded in-memory current-scope artifact from real user prompts and prepends it to review input by default (`subagents.watchdog.scope.enabled`). Newer prompts supersede and mutate older prompts, so the reviewer can flag work that no longer serves the current scope as `scope-drift`. Watchdog auto-follow prompts are not recorded as scope.
77
+ One model setting serves both boundary and cadence reviews per endpoint. Use a strong complementary model for rare adversarial boundary reviews, or a cheap one for frequent cadence monitoring.
67
78
 
68
- You can opt into Scopey-style scope monitoring, inspired by [Scopey](https://github.com/ArchAstro/scopey), by setting `subagents.watchdog.cadence.everyNTools` to run additional non-blocking reviews every N tool results. Cadence warnings are transcript-visible and delivered with Pi's `steer` mode after the current tool boundary; they are never hidden. The same configured watchdog model is used for all checks, so choose a cheap model for frequent monitoring or a strong model for rarer adversarial review.
79
+ ```text
80
+ /subagents-watchdog recommend-model
81
+ /subagents-watchdog session model recommended
82
+ /subagents-watchdog model recommended
83
+ /subagents-watchdog model anthropic/claude-opus-4-8:high
84
+ /subagents-watchdog model openai-codex/gpt-5.5:high
85
+ /subagents-watchdog model inherit
86
+ /subagents-watchdog check
87
+ /subagents-watchdog on
88
+ ```
69
89
 
70
- Scopey-style profile:
90
+ The recommendation is Opus 4.8 or GPT 5.5 at thinking high, whichever your main session is not using and is authenticated. Saving a model does not enable the watchdog; use `on` separately.
71
91
 
72
92
  ```json
73
93
  {
74
94
  "subagents": {
75
95
  "watchdog": {
76
96
  "enabled": true,
77
- "main": {
78
- "model": "anthropic/claude-haiku-4-5",
79
- "thinking": "medium"
80
- },
97
+ "main": { "model": "anthropic/claude-opus-4-8", "thinking": "high" },
81
98
  "scope": { "enabled": true },
82
99
  "cadence": { "everyNTools": 10 },
83
- "autoFollow": {
84
- "blockers": true,
85
- "maxAttempts": 3,
86
- "stalemateRepeats": 3
87
- }
100
+ "stalemateRepeats": 3
88
101
  }
89
102
  }
90
103
  }
91
104
  ```
92
105
 
93
- ## Auto-follow
94
-
95
- When the watchdog displays a blocker at `agent_end`, the `subagents.watchdog.autoFollow` policy can queue a visible follow-up user message asking the agent to address it. Auto-follow only runs while the watchdog is enabled, respects `maxAttempts`, and stops on repeated identical blockers using `stalemateRepeats`.
96
-
97
- ## LSP diagnostics
98
-
99
- When the watchdog is enabled, it also checks changed TypeScript and JavaScript files for fresh language-server diagnostics before the model review.
106
+ Omit `main.model` to inherit the session model and thinking level. A `main.model` without a thinking suffix or `main.thinking` runs with thinking off, so prefer `:high` for the strong pairing.
100
107
 
101
- - It auto-detects `typescript-language-server` from the project `node_modules/.bin` or `PATH`. It never installs tools or scans the whole workspace.
102
- - LSP errors surface as watchdog blockers, warnings as concerns, and info/hints stay in status details.
103
- - Slow or missing servers are reported in `/subagents-watchdog status` without blocking the turn or emitting late mid-turn warnings.
104
- - Configure the bounds with `subagents.watchdog.lsp.enabled`, `timeoutMs`, `maxFiles`, and `maxDiagnostics`.
108
+ Agents can call `subagent({ action: "watchdog.recommend-model" })` and `subagent({ action: "watchdog.configure", model: "recommended", scope: "session" | "user" | "project" })`. They should use `scope: "session"` unless you ask for a lasting default.
105
109
 
106
110
  ## Child watchdogs
107
111
 
108
- For child subagent watchdogs, use `subagents.watchdog.children.model` as the default child watchdog model, or `subagents.watchdog.children.overrides.<agent>.model` for a specific child role.
109
-
110
- Child watchdogs are opt-in and follow the same edit-gated rule: read-only children do not trigger watchdog reviews, while writer children are reviewed at their own `agent_end` if their worktree changed.
111
-
112
- ## Agent-driven configuration
113
-
114
- Agents can configure the same values through the tool when you ask them to set up the watchdog:
115
-
116
- ```ts
117
- subagent({ action: "watchdog.recommend-model" })
118
- subagent({ action: "watchdog.configure", model: "recommended", scope: "session" })
119
- subagent({ action: "watchdog.configure", model: "recommended", scope: "project" })
120
- ```
121
-
122
- Persistent scopes (`user` or `project`) should only be used when you ask for a lasting default. Otherwise the agent should use `scope: "session"`.
112
+ Opt in under `subagents.watchdog.children`. `model` and `thinking` set the default child watchdog; `overrides.<agent>` can set `model`, `thinking`, `enabled`, or `cadence` per role.
123
113
 
124
- ## Native child tool permissions
114
+ ## Launch rules
125
115
 
126
- Native permissions are opt-in and apply only to Pi child runtimes. With no rules configured, every tool call passes through unchanged.
127
-
128
- Configure explicit non-bash rules globally in `~/.pi/agent/extensions/subagent/config.json`:
116
+ `subagents.watchdog.rules` pins which models each role may run on. It runs before a child starts, needs no model call, and applies even when model review is off.
129
117
 
130
118
  ```json
131
119
  {
132
- "permissions": {
133
- "rules": {
134
- "read": "allow",
135
- "write": "ask",
136
- "edit": "deny"
120
+ "subagents": {
121
+ "watchdog": {
122
+ "rules": {
123
+ "action": "warn",
124
+ "roleModels": {
125
+ "scout": { "allow": ["openai-codex/gpt-5.6-luna:max"] },
126
+ "oracle": { "deny": ["*"], "note": "oracle is for hard questions only; ask before launching" },
127
+ "worker": { "deny": ["openai-codex/gpt-5.6-sol:high"] }
128
+ }
129
+ }
137
130
  }
138
131
  }
139
132
  }
140
133
  ```
141
134
 
142
- Custom agents can override matching global rules with a `permission:` or `permissions:` frontmatter block:
135
+ `action: "warn"` steers a concern into the orchestrator transcript and lets the launch proceed. `action: "block"` returns a tool error and starts nothing. `allow` and `deny` are anchored, case-sensitive globs (`*`, `?`) matched against `provider/id[:thinking]` and bare `provider/id`; `deny` wins. Rules apply to direct launches, workflow children, and chain/parallel steps using settings visible at the launch cwd.
136
+
137
+ ## Native child tool permissions
138
+
139
+ Opt-in, Pi child runtimes only. With no rules, every tool call passes through. Global non-bash rules live in `~/.pi/agent/extensions/subagent/config.json`; agents override matching rules in `permission:` or `permissions:` frontmatter:
143
140
 
144
141
  ```yaml
145
142
  ---
@@ -150,27 +147,8 @@ permission:
150
147
  ---
151
148
  ```
152
149
 
153
- Rules support `allow`, `ask`, and `deny`:
154
-
155
- - Agent rules override matching global rules.
156
- - Omitted and unknown tools default to `allow`.
157
- - Explicit `allow` removes an inherited restriction.
158
- - The gate is not registered when the resolved policy has no `ask` or `deny` rules.
159
-
160
- ### How `ask` works
161
-
162
- An explicit `ask` pauses that exact tool call and sends a bounded, redacted preview to a one-call permission arbiter owned by the built-in child watchdog. The arbiter uses the configured child-watchdog model and returns only `approve` or `deny`; it does not notify the parent agent.
163
-
164
- Enable and configure `subagents.watchdog.children` before using `ask` rules. A disabled watchdog, missing model/auth, timeout, malformed response, or runtime error denies the call with a clear error.
165
-
166
- Asked requests and decisions are written to bounded audit JSONL, including `decisionSource: "watchdog"` and bounded failure reasons. Ordinary direction and clarification through `contact_supervisor` or the optional `pi-intercom` extension remain separate and are never permission-gated.
167
-
168
- ### Bash is out of scope
169
-
170
- `bash` is always passed through by pi-subagents. Bash rules are rejected rather than parsed, gated, denied, or audited. Install and configure `pi-guard` when command-level bash policy is needed.
171
-
172
- A pi-subagents child is headless, so a pi-guard rule that resolves to `ask` cannot request approval from the parent Pi UI. Native permissions do not forward pi-guard decisions; they only apply to the separate non-bash child permission gate. For child-specific policy, use `PI_GUARD` through a `PI_SUBAGENT_PI_BINARY` wrapper or an equivalent launch wrapper, and configure explicit `allow` or `deny` rules. An `allow` rule grants execution; it is not approval forwarding, so retain explicit denies for commands the child must not run.
150
+ Values are `allow`, `ask`, and `deny`. Agent rules override global ones, omitted and unknown tools default to `allow`, an explicit `allow` removes an inherited restriction, and the gate is not registered when the resolved policy has no `ask` or `deny`.
173
151
 
174
- ### External CLI profiles
152
+ `ask` pauses that exact tool call and sends a bounded, redacted preview to a one-call arbiter owned by the child watchdog, using the configured child-watchdog model. The arbiter returns only `approve` or `deny` and does not notify the parent. A disabled watchdog, missing model/auth, timeout, malformed response, or runtime error denies the call with a clear error. Requests and decisions are written to bounded audit JSONL. `contact_supervisor` and the optional `pi-intercom` extension are never permission-gated.
175
153
 
176
- External CLI profiles are opaque processes, so native permissions cannot intercept their tools. A launch with effective `ask` or `deny` rules is rejected for an external CLI agent instead of claiming enforcement.
154
+ Bash is always passed through; bash rules are rejected. Use `pi-guard` for command-level policy. Children are pi sessions inside the parent process (foreground) or the detached runner process (background), not separate `pi` binaries, so there is no per-child command wrapper; load `pi-guard` into a child through the agent's `extensions` or `subagentOnlyExtensions`, and background children also pick it up as an ambient extension. External CLI profiles are opaque processes, so native permissions cannot intercept their tools; launches with effective `ask` or `deny` rules are rejected for external CLI agents.
package/docs/workflows.md CHANGED
@@ -14,7 +14,7 @@ Packaged `worker`, `oracle`, and `advisor` default to forked context when a laun
14
14
 
15
15
  Child-safety boundaries are enforced at runtime:
16
16
 
17
- - Spawned child sessions do not receive the bundled `pi-subagents` skill.
17
+ - Child sessions do not receive the bundled `pi-subagents` skill.
18
18
  - Forked child context filtering removes parent-only subagent artifacts (including old hidden orchestration-instruction messages, slash/status/control messages, and prior parent `subagent` tool-call/tool-result history) while preserving ordinary prose and unrelated tool calls/results.
19
19
  - By default, children do not register the `subagent` tool and receive boundary instructions that they are not the parent orchestrator and must not propose or run subagents.
20
20
  - The explicit exception is an agent whose resolved builtin `tools` includes `subagent`; that child gets a child-safe `subagent` tool for the fanout work the parent assigned, still bounded by `maxSubagentDepth`.
@@ -369,7 +369,9 @@ Each child uses the existing worktree lifecycle: it branches from clean HEAD, jo
369
369
 
370
370
  A top-level `{ workflowScript, worktree: true }` makes isolation the default for every workflow child. An individual child can override that default with `worktree: false`. Keep one writer when parallel writes are not intentionally isolated.
371
371
 
372
- Configure the worktree base directory and setup hook in [configuration.md](configuration.md).
372
+ Use `baseRef` to branch managed worktrees from a named commit or branch instead of the default `HEAD`. For example, `{ workflowScript, worktree: true, baseRef: "refs/heads/release" }` applies the release ref to children unless a child supplies its own `baseRef`. The source checkout must still be clean, and the ref must resolve to a commit before any worktree is allocated.
373
+
374
+ Configure the worktree provider, native path layout, base directory, and setup hook in [configuration.md](configuration.md).
373
375
 
374
376
  ### Lane metadata lifecycle
375
377
 
@@ -446,7 +448,7 @@ export PI_SUBAGENT_MAX_DEPTH=1
446
448
  export PI_SUBAGENT_MAX_DEPTH=0
447
449
  ```
448
450
 
449
- `PI_SUBAGENT_DEPTH` is internal and propagated automatically. Do not set it manually.
451
+ `PI_SUBAGENT_MAX_DEPTH` applies to the top-level parent; children inherit their limit through their runtime config, and their own depth is tracked there too.
450
452
 
451
453
  ## Prompt-template integration
452
454
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-subagents",
3
- "version": "0.63.0",
3
+ "version": "0.65.0",
4
4
  "description": "Pi extension for single-agent delegation and scripted multi-agent workflows",
5
5
  "author": "Nico Bailon",
6
6
  "license": "MIT",
@@ -16,7 +16,7 @@
16
16
  "./preflight": "./src/api/preflight.ts",
17
17
  "./control-channel": "./src/api/control-channel.ts",
18
18
  "./intercom-bridge": "./src/api/intercom-bridge.ts",
19
- "./pi-args": "./src/api/pi-args.ts",
19
+ "./child-tool-plan": "./src/api/child-tool-plan.ts",
20
20
  "./shared-types": "./src/api/shared-types.ts",
21
21
  "./project-panes": "./src/api/project-panes.ts"
22
22
  },
@@ -56,8 +56,7 @@
56
56
  "test": "npm run test:unit",
57
57
  "test:unit": "node --experimental-strip-types --import ./test/support/isolated-temp-root.mjs --test test/unit/*.test.ts",
58
58
  "test:integration": "node --experimental-strip-types --import ./test/support/register-loader.mjs --test test/integration/*.test.ts",
59
- "test:e2e": "node --experimental-strip-types --import ./test/support/register-loader.mjs --test test/e2e/*.test.ts",
60
- "test:all": "npm run test:unit && npm run test:integration && npm run test:e2e"
59
+ "test:all": "npm run test:unit && npm run test:integration"
61
60
  },
62
61
  "pi": {
63
62
  "extensions": [
@@ -21,7 +21,7 @@ This file is a detailed reference loaded from `skills/pi-subagents/SKILL.md`.
21
21
  become second decision-makers.
22
22
  - **Respect the fixed authority policy.** `authorityPolicy` is a small `auto` / `confirm` / `forbid` map for supported operational actions. Worktree discard, destructive cleanup, and spawn-budget grants default to confirmation; stop, steer, and schedule creation remain automatic. Use `worktree.discard` with the durable `handoffPath`; confirm-required actions refuse safely without an interactive UI and retained paths include manual Git recovery commands.
23
23
 
24
- Runtime config can change orchestration behavior. `intercomBridge.resultDelivery: false` disables only external acknowledged grouped-result delivery when native parent notifications own completion; supervisor asks/progress stay active, and enabled transport failures are still reported. `asyncByDefault` and `forceTopLevelAsync` affect whether launches detach; `waitTool` can make direct `bg_wait()` calls return immediately while headless auto-drain remains active, and its effective value is propagated to child runtimes; `globalConcurrencyLimit` bounds concurrent fanout, while a positive `maxSubagentSpawnsPerSession` optionally caps cumulative launches (`0` or unset is unlimited). Status and doctor report the budget; static work preflights declared capacity; only the settled root interactive parent can use `grant-spawn-budget` after native confirmation, with total grants bounded by the original cap. Compaction does not reset usage or grants; `singleRunOutputBaseDir` and `worktreeBaseDir` route outputs and worktrees; `completionBatch` groups async notifications. `artifactDir` is `session` (default), `project`, or `temp` and chooses where subagent artifacts are stored. Set `asyncWidget: false` to hide the above-editor background-run widget when a companion footer or dashboard owns that space (fleet inspector remains available). Per-run `artifacts: false` disables artifact capture for that launch. Async status and result artifacts include `lifecycleArtifactVersion` and fields such as `workflowGraph`, `steps`, `results`, `totalTokens`, `totalCost`, `turnCount`, `toolCount`, and nested `children`. Child protocol failures expose a structured `protocolError`; `protocol_output_limit` means a child emitted a JSONL line above the 16 MiB live-parser cap. Prefer these artifacts and `status` views over scraping terminal output.
24
+ Runtime config can change orchestration behavior. `intercomBridge.resultDelivery: false` disables only external acknowledged grouped-result delivery when native parent notifications own completion; supervisor asks/progress stay active, and enabled transport failures are still reported. `asyncByDefault` and `forceTopLevelAsync` affect whether launches detach; `waitTool` can make direct `bg_wait()` calls return immediately while headless auto-drain remains active, and its effective value is propagated to child runtimes; `globalConcurrencyLimit` bounds concurrent fanout, while a positive `maxSubagentSpawnsPerSession` optionally caps cumulative launches (`0` or unset is unlimited). Status and doctor report the budget; static work preflights declared capacity; only the settled root interactive parent can use `grant-spawn-budget` after native confirmation, with total grants bounded by the original cap. Compaction does not reset usage or grants; `singleRunOutputBaseDir` and `worktreeBaseDir` route outputs and worktrees; `completionBatch` groups async notifications. `artifactDir` is `session` (default), `project`, or `temp` and chooses where subagent artifacts are stored. Set `asyncWidget: false` to hide the above-editor background-run widget when a companion footer or dashboard owns that space (fleet inspector remains available). Per-run `artifacts: false` disables artifact capture for that launch. Async status and result artifacts include `lifecycleArtifactVersion` and fields such as `workflowGraph`, `steps`, `results`, `totalTokens`, `totalCost`, `turnCount`, `toolCount`, and nested `children`. Prefer these artifacts and `status` views over scraping terminal output.
25
25
 
26
26
  ### Keep report artifacts out of the repository root
27
27
 
@@ -236,8 +236,8 @@ Resume behavior:
236
236
  - Multi-child async runs require `index` unless only one running child is selectable.
237
237
  - Completed foreground single, parallel, and chain runs can also be revived by `index` while their run metadata remains in extension state.
238
238
  - Nested runs can be resumed by nested id when a live route or persisted nested session metadata is available.
239
- - Revive starts a new child process from the old session context; it does not restart the same OS process.
240
- - Direct revival holds an exclusive cross-process lease on the canonical child session file until the new child finishes. Concurrent attempts fail before Pi starts and identify the owning revived run; stale ownership is reclaimed only when the recorded process is demonstrably gone or reused.
239
+ - Revive starts a new child session from the old session context; it does not resume the original child session.
240
+ - Direct revival holds an exclusive cross-process lease on the canonical child session file until the new child finishes. Concurrent attempts fail before the child session starts and identify the owning revived run; stale ownership is reclaimed only when the recorded process is demonstrably gone or reused.
241
241
  - If the chosen child has no persisted `.jsonl` session file, resume fails and reports that directly.
242
242
 
243
243
  Use diagnostics when setup or child startup looks wrong:
@@ -337,9 +337,10 @@ child changes land. Enabled watchdogs also run changed-file TypeScript/JavaScrip
337
337
  LSP diagnostics before the model pass when `typescript-language-server` is available.
338
338
  They keep bounded current-scope context from real user prompts (`watchdog.scope.enabled`)
339
339
  and can optionally run non-blocking Scopey-style cadence reviews every N tool results
340
- (`watchdog.cadence.everyNTools`). Cadence corrections and blocker auto-follow prompts
341
- are always transcript-visible; choose the watchdog model that matches the desired
342
- cheap-monitor vs strong-reviewer policy.
340
+ (`watchdog.cadence.everyNTools`). Cadence corrections and boundary warnings are always
341
+ transcript-visible; a boundary warning continues the run so the agent can act on it, and
342
+ repeated identical warnings stop after `watchdog.stalemateRepeats`. Choose the watchdog
343
+ model that matches the desired cheap-monitor vs strong-reviewer policy.
343
344
 
344
345
  Prefer a strong complementary model (for example Opus 4.8 high paired against a
345
346
  GPT 5.5 main session, or the reverse). Recommendation and configuration:
@@ -32,6 +32,7 @@ import { resolveSubagentModelOverride, type ParentModel } from "../runs/shared/m
32
32
  import { validateToolBudgetConfig } from "../runs/shared/tool-budget.ts";
33
33
  import { validateAcceptanceInput } from "../runs/shared/acceptance.ts";
34
34
  import { CODE_OWNED_EXTERNAL_CLI_ADAPTER_LABEL, isCodeOwnedExternalCliAdapterId, resolveExternalCliRunnerStatus, validateCodeOwnedProfileRunner } from "../runs/shared/external-cli-contract.ts";
35
+ import { resolveExternalCliBinaryAvailability, type ExternalCliBinaryAvailability } from "../runs/shared/external-cli-preflight.ts";
35
36
  import type { AcceptanceInput, AgentCapabilitiesSnapshot, AgentCapabilityRow, Details, ExtensionConfig, ToolBudgetConfig } from "../shared/types.ts";
36
37
  import { getProjectConfigDir } from "../shared/utils.ts";
37
38
  import { previewDisplayText } from "../shared/display-text.ts";
@@ -704,17 +705,34 @@ function externalJobProviderSuffix(provider: string, names: Set<string> | undefi
704
705
  return names.has(provider) ? "✓" : "missing";
705
706
  }
706
707
 
707
- function runnerListBadge(agent: AgentConfig, providerNames: Set<string> | undefined): string | undefined {
708
+ type ExternalCliAvailabilityByCommand = ReadonlyMap<string, ExternalCliBinaryAvailability>;
709
+
710
+ function externalCliAvailabilityForAgents(agents: readonly AgentConfig[]): ExternalCliAvailabilityByCommand {
711
+ const availability = new Map<string, ExternalCliBinaryAvailability>();
712
+ for (const agent of agents) {
713
+ const runner = agent.runner;
714
+ if (runner?.type === "external-cli" && !availability.has(runner.command)) {
715
+ availability.set(runner.command, resolveExternalCliBinaryAvailability(runner.command, process.env));
716
+ }
717
+ }
718
+ return availability;
719
+ }
720
+
721
+ function runnerListBadge(agent: AgentConfig, providerNames: Set<string> | undefined, externalCliAvailability?: ExternalCliAvailabilityByCommand): string | undefined {
708
722
  if (agent.runner?.type === "external-job") return `external-job:${agent.runner.provider} ${externalJobProviderSuffix(agent.runner.provider, providerNames)}`;
709
- if (agent.runner?.type === "external-cli") return "external-cli";
723
+ if (agent.runner?.type === "external-cli") {
724
+ const availability = externalCliAvailability?.get(agent.runner.command);
725
+ if (!availability) return "external-cli";
726
+ return `external-cli:${agent.runner.command} ${availability.available ? "✓" : "missing"}`;
727
+ }
710
728
  return undefined;
711
729
  }
712
730
 
713
- function agentListMetadata(agent: AgentConfig, providerNames: Set<string> | undefined): string {
731
+ function agentListMetadata(agent: AgentConfig, providerNames: Set<string> | undefined, externalCliAvailability?: ExternalCliAvailabilityByCommand): string {
714
732
  const source = agent.source === "package" ? packageSourceLabel(agent) : agent.source;
715
733
  return [
716
734
  source,
717
- runnerListBadge(agent, providerNames),
735
+ runnerListBadge(agent, providerNames, externalCliAvailability),
718
736
  agent.defaultContext ? `context: ${agent.defaultContext}` : undefined,
719
737
  agent.aliases?.length ? `aliases: ${agent.aliases.join(", ")}` : undefined,
720
738
  ].filter((part): part is string => Boolean(part)).join(", ");
@@ -724,7 +742,7 @@ function formatAgentListLine(agent: AgentConfig, providerNames: Set<string> | un
724
742
  return `- ${agent.name} (${agentListMetadata(agent, providerNames)}): ${agent.description}`;
725
743
  }
726
744
 
727
- function formatAgentCapabilitiesLine(agent: AgentConfig, providerNames: Set<string> | undefined): string {
745
+ function formatAgentCapabilitiesLine(agent: AgentConfig, providerNames: Set<string> | undefined, externalCliAvailability?: ExternalCliAvailabilityByCommand): string {
728
746
  const declaredTools = [
729
747
  ...(agent.tools ?? []),
730
748
  ...(agent.mcpDirectTools ?? []).map((tool) => `mcp:${tool}`),
@@ -742,7 +760,7 @@ function formatAgentCapabilitiesLine(agent: AgentConfig, providerNames: Set<stri
742
760
  if (agent.modelProvider && !agent.model.includes("/")) model = `${agent.modelProvider}/${agent.model}`;
743
761
  }
744
762
  const thinking = agent.thinking === false ? "off" : agent.thinking ?? "default";
745
- return `- ${agent.name} (${agentListMetadata(agent, providerNames)}): Description: ${previewDisplayText(agent.description, 240)}; Tools: ${tools}; Model: ${model}; Thinking: ${thinking}`;
763
+ return `- ${agent.name} (${agentListMetadata(agent, providerNames, externalCliAvailability)}): Description: ${previewDisplayText(agent.description, 240)}; Tools: ${tools}; Model: ${model}; Thinking: ${thinking}`;
746
764
  }
747
765
 
748
766
  const EXTERNAL_JOB_CAPABILITIES = { stop: false, steer: false, resume: false, structuredOutput: false, toolEvents: false } as const;
@@ -752,10 +770,19 @@ function listOrEmpty<T>(values: T[] | undefined): T[] {
752
770
  return values ?? [];
753
771
  }
754
772
 
755
- function agentCapabilityRunner(agent: AgentConfig, providerNames: Set<string> | undefined): AgentCapabilityRow["runner"] {
773
+ function agentCapabilityRunner(agent: AgentConfig, providerNames: Set<string> | undefined, externalCliAvailability: ExternalCliAvailabilityByCommand): AgentCapabilityRow["runner"] {
756
774
  const runner = agent.runner;
757
775
  if (!runner || runner.type === "pi") return PI_AGENT_RUNNER;
758
- if (runner.type === "external-cli") return { type: "external-cli", adapter: runner.adapter, capabilities: resolveExternalCliRunnerStatus(runner).capabilities };
776
+ if (runner.type === "external-cli") {
777
+ const availability = externalCliAvailability.get(runner.command)!;
778
+ return {
779
+ type: "external-cli",
780
+ adapter: runner.adapter,
781
+ command: runner.command,
782
+ ...availability,
783
+ capabilities: resolveExternalCliRunnerStatus(runner).capabilities,
784
+ };
785
+ }
759
786
  return { type: "external-job", provider: runner.provider, available: providerNames?.has(runner.provider), capabilities: EXTERNAL_JOB_CAPABILITIES };
760
787
  }
761
788
 
@@ -769,7 +796,7 @@ function agentCapabilityTools(agent: AgentConfig): AgentCapabilityRow["tools"] {
769
796
  };
770
797
  }
771
798
 
772
- function agentCapabilityRow(agent: AgentConfig, options: { executable: boolean; providerNames?: Set<string>; restrictionSources?: string[] }): AgentCapabilityRow {
799
+ function agentCapabilityRow(agent: AgentConfig, options: { executable: boolean; providerNames?: Set<string>; externalCliAvailability: ExternalCliAvailabilityByCommand; restrictionSources?: string[] }): AgentCapabilityRow {
773
800
  return {
774
801
  name: agent.name,
775
802
  description: previewDisplayText(agent.description, 1000),
@@ -777,7 +804,7 @@ function agentCapabilityRow(agent: AgentConfig, options: { executable: boolean;
777
804
  executable: options.executable,
778
805
  restrictionSources: options.executable ? undefined : options.restrictionSources ?? [],
779
806
  aliases: agent.aliases ? [...agent.aliases] : undefined,
780
- runner: agentCapabilityRunner(agent, options.providerNames),
807
+ runner: agentCapabilityRunner(agent, options.providerNames, options.externalCliAvailability),
781
808
  tools: agentCapabilityTools(agent),
782
809
  model: presentDetails({ value: agent.model, fallbackModels: agent.fallbackModels, thinking: agent.thinking }),
783
810
  execution: presentDetails({ defaultAsync: agent.defaultAsync, timeoutMs: agent.defaultTimeoutMs }),
@@ -786,11 +813,11 @@ function agentCapabilityRow(agent: AgentConfig, options: { executable: boolean;
786
813
  };
787
814
  }
788
815
 
789
- function agentCapabilitiesSnapshot(input: { agents: AgentConfig[]; restrictedAgents: AgentConfig[]; providerNames?: Set<string>; restrictedSources?: string[] }): AgentCapabilitiesSnapshot {
816
+ function agentCapabilitiesSnapshot(input: { agents: AgentConfig[]; restrictedAgents: AgentConfig[]; providerNames?: Set<string>; externalCliAvailability: ExternalCliAvailabilityByCommand; restrictedSources?: string[] }): AgentCapabilitiesSnapshot {
790
817
  return {
791
818
  agents: [
792
- ...input.agents.map((agent) => agentCapabilityRow(agent, { executable: true, providerNames: input.providerNames })),
793
- ...input.restrictedAgents.map((agent) => agentCapabilityRow(agent, { executable: false, providerNames: input.providerNames, restrictionSources: input.restrictedSources })),
819
+ ...input.agents.map((agent) => agentCapabilityRow(agent, { executable: true, providerNames: input.providerNames, externalCliAvailability: input.externalCliAvailability })),
820
+ ...input.restrictedAgents.map((agent) => agentCapabilityRow(agent, { executable: false, providerNames: input.providerNames, externalCliAvailability: input.externalCliAvailability, restrictionSources: input.restrictedSources })),
794
821
  ],
795
822
  restrictedCount: input.restrictedAgents.length,
796
823
  ...(input.restrictedSources?.length ? { capabilityCeilingSources: [...input.restrictedSources] } : {}),
@@ -824,13 +851,14 @@ function appendAgentDiagnosticLines(lines: string[], diagnostics: AgentDiscovery
824
851
  );
825
852
  }
826
853
 
827
- function agentCapabilityDetails(input: { capabilityMode: boolean; agents: AgentConfig[]; restrictedAgents: AgentConfig[]; providerNames?: Set<string>; restrictedSources?: string[] }): Partial<Details> | undefined {
854
+ function agentCapabilityDetails(input: { capabilityMode: boolean; agents: AgentConfig[]; restrictedAgents: AgentConfig[]; providerNames?: Set<string>; externalCliAvailability: ExternalCliAvailabilityByCommand; restrictedSources?: string[] }): Partial<Details> | undefined {
828
855
  if (!input.capabilityMode) return undefined;
829
856
  return {
830
857
  agentCapabilities: jsonDetails(agentCapabilitiesSnapshot({
831
858
  agents: input.agents,
832
859
  restrictedAgents: input.restrictedAgents,
833
860
  providerNames: input.providerNames,
861
+ externalCliAvailability: input.externalCliAvailability,
834
862
  restrictedSources: input.restrictedSources,
835
863
  })),
836
864
  };
@@ -936,7 +964,10 @@ export function handleList(params: ManagementParams, ctx: ManagementContext): Ag
936
964
  const providerStatus = registeredExternalJobProviderStatus();
937
965
  const providerNameSet = providerNames(providerStatus);
938
966
  const capabilityMode = params.capabilities === true;
939
- const formatLine = capabilityMode ? formatAgentCapabilitiesLine : formatAgentListLine;
967
+ const externalCliAvailability = capabilityMode ? externalCliAvailabilityForAgents([...agents, ...restrictedAgents]) : undefined;
968
+ const formatLine = capabilityMode
969
+ ? (agent: AgentConfig, names: Set<string> | undefined) => formatAgentCapabilitiesLine(agent, names, externalCliAvailability)
970
+ : formatAgentListLine;
940
971
  const lines = [
941
972
  capabilityMode ? "Executable agents (capabilities):" : "Executable agents:",
942
973
  ...formatAgentListSections(agents, providerNameSet, formatLine),
@@ -950,6 +981,7 @@ export function handleList(params: ManagementParams, ctx: ManagementContext): Ag
950
981
  agents,
951
982
  restrictedAgents,
952
983
  providerNames: providerNameSet,
984
+ externalCliAvailability: externalCliAvailability ?? new Map(),
953
985
  restrictedSources,
954
986
  }));
955
987
  }
@@ -1,5 +1,4 @@
1
1
  export {
2
- SUBAGENT_CAPABILITY_CEILING_ENV,
3
2
  SUBAGENT_CAPABILITY_CEILING_REGISTRY_KEY,
4
3
  SUBAGENT_CAPABILITY_CEILING_VERSION,
5
4
  decodeSubagentCapabilityCeiling,
@@ -2,4 +2,4 @@ export {
2
2
  resolvePiLaunchToolPlan,
3
3
  type PiLaunchToolPlan,
4
4
  type ResolvePiLaunchToolPlanInput,
5
- } from "../runs/shared/pi-args.ts";
5
+ } from "../runs/shared/child-tool-plan.ts";
@@ -7,11 +7,11 @@ import { buildSkillInjection, normalizeSkillInput, resolveSkillsWithFallback } f
7
7
  import { buildAgentMemoryInjection } from "../agents/agent-memory.ts";
8
8
  import { buildModelCandidates, inheritsParentModel, resolveEffectiveSubagentModel, resolveModelOrigin, type AvailableModelInfo, type ParentModel } from "../runs/shared/model-fallback.ts";
9
9
  import { resolveModelScopesForAgent } from "../runs/shared/model-scope.ts";
10
- import { applyThinkingSuffix, resolvePiLaunchToolPlan, type PiLaunchToolPlan } from "../runs/shared/pi-args.ts";
10
+ import { applyThinkingSuffix, resolvePiLaunchToolPlan, type PiLaunchToolPlan } from "../runs/shared/child-tool-plan.ts";
11
11
  import { injectOutputPathSystemPrompt, normalizeSingleOutputOverride, resolveSingleOutputPath } from "../runs/shared/single-output.ts";
12
12
  import { getArtifactPaths, getArtifactsDir } from "../shared/artifacts.ts";
13
13
  import { resolveEffectiveThinking } from "../shared/model-info.ts";
14
- import { assertThinkingWithinCeiling, decodeThinkingCeiling, intersectThinkingCeilings, SUBAGENT_THINKING_CEILING_ENV, type ThinkingLevel } from "../shared/thinking-ceiling.ts";
14
+ import { assertThinkingWithinCeiling, intersectThinkingCeilings, type ThinkingLevel } from "../shared/thinking-ceiling.ts";
15
15
  import { SUBAGENT_LIFECYCLE_ARTIFACT_VERSION, type ArtifactDirPreference, type ArtifactPaths, type JsonSchemaObject, type OutputMode } from "../shared/types.ts";
16
16
  import { capabilityCeilingAgentRestrictionMessage, intersectSubagentCapabilityCeilings, type ResolvedSubagentCapabilityCeiling, type SubagentCapabilityAudit } from "../runs/shared/capability-ceiling.ts";
17
17
  import { resolvePermissionRules } from "../runs/shared/permissions.ts";
@@ -330,7 +330,6 @@ export async function resolveSubagentLaunchContract(input: SubagentLaunchContrac
330
330
  discovered.maxThinking,
331
331
  input.thinkingCeiling,
332
332
  input.inheritedThinkingCeiling,
333
- decodeThinkingCeiling(process.env[SUBAGENT_THINKING_CEILING_ENV]),
334
333
  );
335
334
  const model = externalRunner ? undefined : applyThinkingSuffix(primaryModel, effectiveThinkingConfig, input.thinking !== undefined);
336
335
  const modelCandidates = externalRunner
@@ -4,7 +4,7 @@ import { discoverAgentsAll, type AgentSource } from "../agents/agents.ts";
4
4
  import { isAsyncAvailable } from "../runs/background/async-execution.ts";
5
5
  import { formatSpawnBudgetSummary, getSpawnBudgetSnapshot } from "../runs/shared/spawn-budget.ts";
6
6
  import { getActiveAsyncCapacitySnapshot, resolveAbandonedSlotReleaseAfterMs, resolveMaxActiveAsyncRunsPerSession } from "../runs/background/active-async-capacity.ts";
7
- import { decodeRunFanoutBudgetDescriptor, formatRunFanoutBudget, getRunFanoutBudgetSnapshot, RUN_FANOUT_BUDGET_ENV } from "../runs/shared/run-fanout-budget.ts";
7
+
8
8
  import { diagnoseIntercomBridge, type IntercomBridgeDiagnostic } from "../intercom/intercom-bridge.ts";
9
9
  import { discoverAvailableSkills, type SkillSource } from "../agents/skills.ts";
10
10
  import {
@@ -178,14 +178,6 @@ function formatSpawnBudgetSection(input: DoctorReportInput): string[] {
178
178
  }
179
179
 
180
180
  function formatRunFanoutSection(input: DoctorReportInput): string[] {
181
- try {
182
- const inherited = decodeRunFanoutBudgetDescriptor(process.env[RUN_FANOUT_BUDGET_ENV]);
183
- if (inherited) {
184
- return [`- usage: ${formatRunFanoutBudget(getRunFanoutBudgetSnapshot(inherited)).replace(/^Run fan-out: /, "")}`, `- root run: ${inherited.rootRunId}`, "- reset boundary: cumulative claims are never released; a new top-level run creates a new budget"];
185
- }
186
- } catch (error) {
187
- return [`- inherited budget: invalid — ${errorText(error)}`];
188
- }
189
181
  const configured = resolveMaxSubagentSpawnsPerRun(input.config.maxSubagentSpawnsPerRun);
190
182
  const source = normalizeMaxSubagentSpawnsPerRun(process.env.PI_SUBAGENT_MAX_SPAWNS_PER_RUN) !== undefined
191
183
  ? "environment"
@@ -214,7 +206,7 @@ function formatPermissionSystemSection(): string[] {
214
206
  if (trimmed) {
215
207
  lines.push(`- parent session: set (${trimmed})`);
216
208
  } else {
217
- lines.push("- parent session: not set — ask forwarding from subprocess children will not reach a parent UI");
209
+ lines.push("- parent session: not set — ask forwarding from background children will not reach a parent UI");
218
210
  }
219
211
  const isChild = process.env["PI_SUBAGENT_CHILD"] === "1";
220
212
  lines.push(`- subagent process: ${isChild ? "yes (PI_SUBAGENT_CHILD=1)" : "no"}`);