@ferris1225/pi-subagents 4.1.24 → 4.2.1
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/README.md +51 -116
- package/agents/executor.md +53 -0
- package/package.json +1 -1
- package/src/agents.ts +2 -2
- package/src/announcements.ts +0 -26
- package/src/completion.ts +0 -11
- package/src/config.ts +36 -12
- package/src/dispatch.ts +16 -310
- package/src/durable.ts +0 -5
- package/src/format.ts +0 -8
- package/src/monitor.ts +7 -143
- package/src/prompt.ts +7 -30
- package/src/rpc-run.ts +993 -993
- package/src/runtime.ts +3 -10
- package/src/setup.ts +1 -6
- package/src/spawn.ts +0 -10
- package/src/thread-lifecycle.ts +51 -219
- package/src/widget.ts +33 -240
- package/agents/cleaner.md +0 -50
- package/agents/documenter.md +0 -40
- package/agents/reviewer.md +0 -82
- package/agents/synthesizer.md +0 -39
- package/agents/worker.md +0 -43
- package/src/workflow.ts +0 -215
package/README.md
CHANGED
|
@@ -6,15 +6,15 @@
|
|
|
6
6
|

|
|
7
7
|

|
|
8
8
|
|
|
9
|
-
A managed engineering team for [pi](https://github.com/earendil-works/pi):
|
|
10
|
-
|
|
9
|
+
A managed engineering team for [pi](https://github.com/earendil-works/pi): two
|
|
10
|
+
focused sub-agents, durable threads, and Git worktree
|
|
11
11
|
isolation. You install it once and your main agent delegates on its own.
|
|
12
12
|
|
|
13
13
|
## Why
|
|
14
14
|
|
|
15
15
|
Delegation is supposed to remove coordination work. Most sub-agent launchers stop
|
|
16
16
|
at "spawn a child with a prompt" and leave the hard parts — when to delegate, how
|
|
17
|
-
wide to fan out,
|
|
17
|
+
wide to fan out, what happens when a model dies, how results come
|
|
18
18
|
back — with you. This extension owns them:
|
|
19
19
|
|
|
20
20
|
- The main model delegates without being asked, because a delegation directive is
|
|
@@ -22,8 +22,6 @@ back — with you. This extension owns them:
|
|
|
22
22
|
- Dispatching never blocks or ends the main turn, so it can start several runs and
|
|
23
23
|
keep working while they execute.
|
|
24
24
|
- Results deliver themselves. There is no status tool to poll and no lookup step.
|
|
25
|
-
- Successful implementation work goes through an independent reviewer gate, and a
|
|
26
|
-
failing gate fixes itself before it reaches you.
|
|
27
25
|
- Parallel writers get their own Git worktrees, so concurrent edits do not collide
|
|
28
26
|
and your index is never touched.
|
|
29
27
|
- Threads keep their context across resume, stop, reload, and crash; a dead model
|
|
@@ -53,14 +51,12 @@ directly when you want exact control.
|
|
|
53
51
|
|
|
54
52
|
## The team
|
|
55
53
|
|
|
56
|
-
| Agent | Access
|
|
57
|
-
| ------------- |
|
|
58
|
-
| `explorer` | Read-only
|
|
59
|
-
| `
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
| `synthesizer` | Read-only | Merging a fan-out's result artifacts or other long sources into one deduplicated, attributed brief. Conflicts and gaps stay explicit, and your main context never re-reads the inputs. |
|
|
63
|
-
| `reviewer` | Read-only (review) / full (fix stage) | Audits, code-health checks, plans, PR and issue validation, and independent gates. A failing managed gate continues into the reviewer's own write-enabled fix stage. |
|
|
54
|
+
| Agent | Access | Best for |
|
|
55
|
+
| ------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
56
|
+
| `explorer` | Read-only | Broad search, unfamiliar-area mapping, symbol and dependency tracing. Returns a retrieval index — never proof. |
|
|
57
|
+
| `executor` | Full | The default route for any non-trivial, self-contained task: implementation, fixes, refactors, tests, evidence-first cleanup, docs/comment sync, or merging a fan-out's results into one brief — carried through verification and a result-only handoff. |
|
|
58
|
+
|
|
59
|
+
Custom roles join them with a Markdown file (see [Custom agents](#custom-agents)).
|
|
64
60
|
|
|
65
61
|
Every child is an isolated leaf pi process with its own context window and no
|
|
66
62
|
memory of your conversation, so the brief is its only input. A good brief carries
|
|
@@ -70,13 +66,9 @@ injected delegation guidance produces when the main agent dispatches for you.
|
|
|
70
66
|
```text
|
|
71
67
|
You
|
|
72
68
|
└─ pi main agent
|
|
73
|
-
├─ explorer ─── retrieval
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
├─ documenter ─ explicit docs/comments task → deliver │
|
|
77
|
-
├─ synthesizer ─ merges fan-out results into one brief │
|
|
78
|
-
└─ reviewer ─── advisory report (no VERDICT), or managed gate ◀──────┘
|
|
79
|
-
└─ direct REVIEW_FAIL → findings + fix instructions → main agent fixes
|
|
69
|
+
├─ explorer ─── parallel recon, retrieval leads only
|
|
70
|
+
└─ executor ─── one deliverable per child: implement, fix, clean up,
|
|
71
|
+
sync docs, or merge fan-out results → verify → deliver
|
|
80
72
|
```
|
|
81
73
|
|
|
82
74
|
## Dispatching work
|
|
@@ -84,7 +76,7 @@ You
|
|
|
84
76
|
```ts
|
|
85
77
|
// One task
|
|
86
78
|
subagent({
|
|
87
|
-
agent: "
|
|
79
|
+
agent: "executor",
|
|
88
80
|
task: "Fix the cache invalidation bug in src/cache, add regression tests, run the checks.",
|
|
89
81
|
});
|
|
90
82
|
|
|
@@ -92,7 +84,7 @@ subagent({
|
|
|
92
84
|
subagent({
|
|
93
85
|
tasks: [
|
|
94
86
|
{ agent: "explorer", task: "Trace model fallback from dispatch to completion." },
|
|
95
|
-
{ agent: "
|
|
87
|
+
{ agent: "executor", task: "Add edge-case tests for config migration." },
|
|
96
88
|
],
|
|
97
89
|
});
|
|
98
90
|
```
|
|
@@ -106,78 +98,31 @@ as slots free.
|
|
|
106
98
|
Because queueing is pacing rather than refusal, it is always reported as such.
|
|
107
99
|
Dispatch confirmations name each waiting run's real reason — waiting for a free
|
|
108
100
|
process slot, serialized behind the shared-checkout write lane, or already
|
|
109
|
-
starting its child — alongside the slot capacity. A run that
|
|
110
|
-
|
|
111
|
-
work and serialized writers never starve new dispatches.
|
|
101
|
+
starting its child — alongside the slot capacity. A run that waits for the write
|
|
102
|
+
lane releases its slot first, so serialized writers never starve new dispatches.
|
|
112
103
|
|
|
113
104
|
One child owns one coherent deliverable and its files. Dependent work starts only
|
|
114
|
-
after its prerequisite delivers.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
```ts
|
|
119
|
-
subagent({
|
|
120
|
-
agent: "reviewer",
|
|
121
|
-
task: "Gate the current diff for correctness, regressions, and missing tests.",
|
|
122
|
-
});
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
A gate ends with exactly one verdict line, `VERDICT: REVIEW_PASS` or
|
|
126
|
-
`VERDICT: REVIEW_FAIL`. Every finding carries a concrete fix instruction, and the
|
|
127
|
-
complete finding set must arrive in one pass — findings are never rationed across
|
|
128
|
-
later rounds.
|
|
129
|
-
|
|
130
|
-
Gates are proportional to the change. A small, contained diff gets a fast review
|
|
131
|
-
of its correctness, regressions, and blast radius rather than a whole-surface
|
|
132
|
-
audit, and `review: "none"` on a `worker` or `cleaner` task skips the gate
|
|
133
|
-
outright for mechanical, low-risk edits you verify yourself: typos, comments, doc
|
|
134
|
-
strings, config value tweaks. The default remains one fresh gate whenever behavior
|
|
135
|
-
can change, and a resumed thread keeps the choice its dispatch made.
|
|
136
|
-
|
|
137
|
-
A run that changed nothing is not gated either — there is no diff to review, and
|
|
138
|
-
making zero edits is a valid outcome for a cleaner that found no safe cut. That
|
|
139
|
-
one is decided afterwards rather than at dispatch, and only on proof: an isolated
|
|
140
|
-
worktree starts at its integration base, so an empty diff against that base is
|
|
141
|
-
proof. A shared checkout is shared with you and your editor, so nothing in it can
|
|
142
|
-
be attributed to one run and the gate always runs.
|
|
143
|
-
|
|
144
|
-
A failing **managed** gate — the automatic one after a top-level `worker` or
|
|
145
|
-
`cleaner` — converges inside the workflow. The same retained reviewer session
|
|
146
|
-
gains write access and applies its own fix instructions, then a fresh gate
|
|
147
|
-
verifies those fixes and hunts regressions they introduced. Re-reviews converge on
|
|
148
|
-
the fixes instead of rescanning everything, and the loop is capped at two fix
|
|
149
|
-
rounds, after which the still-failing gate returns to the main agent with every
|
|
150
|
-
finding.
|
|
151
|
-
|
|
152
|
-
A failing gate **you dispatched directly** returns its full findings to the main
|
|
153
|
-
agent, which resolves them itself, inline or through a worker it briefs, without
|
|
154
|
-
waiting for you. Only a genuinely destructive or scope-changing fix is worth
|
|
155
|
-
asking about. It re-verifies once, then reports what remains and moves on: gate
|
|
156
|
-
dispatches never loop.
|
|
157
|
-
|
|
158
|
-
Generic audits and read-only reviews are advisory by default — no verdict, no
|
|
159
|
-
edits. Role authority stays honest in both directions: asking for an audit never
|
|
160
|
-
silently authorizes code changes, and asking for cleanup never rewards
|
|
161
|
-
speculative deletion. A top-level `documenter` is an explicit docs-writing task
|
|
162
|
-
that delivers without another gate.
|
|
105
|
+
after its prerequisite delivers. Verification belongs to whoever did the work:
|
|
106
|
+
every child runs the checks it can and reports exactly which ones ran, and the
|
|
107
|
+
main agent inspects the actual changes before calling anything done.
|
|
163
108
|
|
|
164
109
|
## Parallel edits
|
|
165
110
|
|
|
166
|
-
- Single tasks use your checkout. Every parallel write-capable agent (`
|
|
167
|
-
|
|
111
|
+
- Single tasks use your checkout. Every parallel write-capable agent (`executor`
|
|
112
|
+
and custom writers) defaults to a detached Git worktree, so
|
|
168
113
|
parallel writers run at the same time. Worktree mode needs a committed `HEAD`,
|
|
169
114
|
and read-only agents reject it.
|
|
170
115
|
- A role file can pin its own default with `isolation: worktree` or
|
|
171
116
|
`isolation: shared` in the frontmatter. Precedence is an explicit per-dispatch
|
|
172
117
|
`isolation`, then the role's declaration, then the parallel write default.
|
|
173
|
-
- An isolated
|
|
174
|
-
|
|
175
|
-
|
|
118
|
+
- An isolated run's tracked, deleted, untracked, and binary changes integrate
|
|
119
|
+
back exactly once, after the child settles. Nothing is staged and your index is
|
|
120
|
+
untouched.
|
|
176
121
|
- Integration is a three-way merge, so parallel workers that touched disjoint
|
|
177
122
|
files or regions land cleanly even when earlier patches moved the checkout
|
|
178
123
|
underneath them. A genuine overlap leaves conflict markers in the checkout and
|
|
179
124
|
keeps the worktree and patch for you to resolve.
|
|
180
|
-
- Shared-checkout writers
|
|
125
|
+
- Shared-checkout writers serialize through
|
|
181
126
|
one repository lane, so two of them never race. A run waiting there is reported
|
|
182
127
|
as a lane wait, not as slot queueing, and its process slot is already released.
|
|
183
128
|
- Setup and integration failures keep the useful patch and worktree, and record
|
|
@@ -223,34 +168,23 @@ threads that had already finished keep only their delivered result.
|
|
|
223
168
|
|
|
224
169
|
## Live status and results
|
|
225
170
|
|
|
226
|
-
The TUI widget renders one line per
|
|
171
|
+
The TUI widget renders one line per active run in fixed identity columns —
|
|
227
172
|
status icon, right-aligned `#id`, padded agent name, then the task label — so
|
|
228
173
|
every label starts at the same column, with the live activity dimmed after
|
|
229
|
-
|
|
230
|
-
badge, the token flow in the footer vocabulary (`↑` input, `↓` output,
|
|
231
|
-
`R`/`W` cache read/write), cost, the full `provider/model
|
|
232
|
-
wait state, and an elapsed time that always carries seconds.
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
chain) renders as a tree: the parent line carries the workflow-wide token/cost
|
|
236
|
-
totals and total elapsed, and every stage gets its own `├`/`└`-connected row
|
|
237
|
-
with its own model, token flow, and elapsed — settled stages keep the
|
|
238
|
-
telemetry frozen at settlement, the live stage shows its child's model and
|
|
239
|
-
current activity. A live run renders two lines: what it is — agent, task,
|
|
240
|
-
token flow, cost, provider/model, elapsed — and, dim under the label column,
|
|
241
|
-
what it is doing right now:
|
|
174
|
+
`↳` on its own line and the rest of the telemetry flowing inline after ` · `: the
|
|
175
|
+
worktree badge, the token flow in the footer vocabulary (`↑` input, `↓` output,
|
|
176
|
+
`R`/`W` cache read/write), cost, the full `provider/model` ref, the
|
|
177
|
+
wait state, and an elapsed time that always carries seconds. A live run renders
|
|
178
|
+
two lines: what it is — agent, task, token flow, cost, provider/model, elapsed —
|
|
179
|
+
and, dim under the label column, what it is doing right now:
|
|
242
180
|
|
|
243
181
|
```text
|
|
244
|
-
●
|
|
245
|
-
|
|
246
|
-
├ ✓ implement · ↑1.0k ↓12.0k R40.0k W1.2k $0.5100 · xai/grok-4/xhigh · 2m41s
|
|
247
|
-
├ ! review · ↑0.9k ↓6.0k R38.0k W0.9k $0.3300 · openai/gpt-5 · 1m12s
|
|
248
|
-
├ ● review fix — edit src/auth.ts · ↑0.2k ↓3.0k R12.0k $0.1200 · openai/gpt-5/medium · 41s
|
|
249
|
-
└ ○ re-review
|
|
182
|
+
● #12 executor src/cache.ts · wt:a91f3c · ↑5.2k ↓41.0k R210.0k W6.1k $1.9400 · 12m06s
|
|
183
|
+
↳ edit src/auth.ts
|
|
250
184
|
● #15 explorer src/models.ts · ↑1.2k ↓8.4k R31.0k W1.1k $0.0900 · openai/gpt-5-mini · 3m07s
|
|
251
185
|
↳ grep fallback
|
|
252
|
-
○ #23
|
|
253
|
-
○ #24
|
|
186
|
+
○ #23 executor src/config.ts · repo lane
|
|
187
|
+
○ #24 executor ↻ tests/config.test.ts · queued · 5m02s
|
|
254
188
|
```
|
|
255
189
|
|
|
256
190
|
Telemetry drops leftmost-first when a row runs out of width (badge, wait
|
|
@@ -258,17 +192,15 @@ state, usage, model) while the elapsed survives every width. Queued rows state
|
|
|
258
192
|
what they actually wait for — `queued` for a free process slot, `repo lane`
|
|
259
193
|
for shared-checkout write serialization, or `starting` — and a resumed thread
|
|
260
194
|
carries a dim `↻` in its agent column with its cumulative time. The widget is
|
|
261
|
-
capped at ten lines: when many runs are live, extra
|
|
262
|
-
`… +N more` marker
|
|
263
|
-
the live stage so the editor keeps its space.
|
|
195
|
+
capped at ten lines: when many runs are live, extra runs collapse into a
|
|
196
|
+
`… +N more` marker so the editor keeps its space.
|
|
264
197
|
|
|
265
198
|
Completions resume the main agent on their own, with a compact block of at most 40
|
|
266
199
|
lines by default; longer output lands unchanged in a Markdown artifact whose path
|
|
267
200
|
comes with the message. Roles write result-only handoffs — outcome, paths,
|
|
268
201
|
verification, unresolved blockers — and the main agent is told to add its
|
|
269
|
-
conclusion rather than restate what you already read. A
|
|
270
|
-
|
|
271
|
-
run adds its failed-tool diagnostics.
|
|
202
|
+
conclusion rather than restate what you already read. A failed run adds its
|
|
203
|
+
failed-tool diagnostics.
|
|
272
204
|
|
|
273
205
|
## Models, thinking, and tools
|
|
274
206
|
|
|
@@ -283,7 +215,7 @@ effective model supports. `/subagents-setup` → _Configure an agent_ also offer
|
|
|
283
215
|
manual strength, listing only the levels that model supports. There is no separate
|
|
284
216
|
vision mode — assign a multimodal model and name the image paths in the task.
|
|
285
217
|
|
|
286
|
-
Every dispatch,
|
|
218
|
+
Every dispatch, resume, retry, and fallback snapshots the parent's
|
|
287
219
|
currently active tools. A role with no explicit list inherits the full set. An
|
|
288
220
|
explicit list keeps its pi built-in boundary and gains active extension tools,
|
|
289
221
|
while its shell slot follows the parent: a role file naming `bash` runs
|
|
@@ -307,11 +239,10 @@ strength per agent. Everything else is config-file only, stored at
|
|
|
307
239
|
|
|
308
240
|
```json
|
|
309
241
|
{
|
|
310
|
-
"enabledAgents": ["explorer", "
|
|
311
|
-
"knownAgents": ["explorer", "
|
|
242
|
+
"enabledAgents": ["explorer", "executor"],
|
|
243
|
+
"knownAgents": ["explorer", "executor"],
|
|
312
244
|
"agentModels": { "explorer": "anthropic/claude-haiku-4-5" },
|
|
313
|
-
"agentThinkingLevels": { "
|
|
314
|
-
"notifyOnReviewPass": false,
|
|
245
|
+
"agentThinkingLevels": { "executor": "high" },
|
|
315
246
|
"maxResultLines": 40,
|
|
316
247
|
"agentScope": "user",
|
|
317
248
|
"idleTimeoutSec": 90
|
|
@@ -324,14 +255,18 @@ strength per agent. Everything else is config-file only, stored at
|
|
|
324
255
|
| `knownAgents` | Built-ins this config has seen; automatic bookkeeping — never edit it. |
|
|
325
256
|
| `agentModels` | Optional `provider/model-id` per agent; missing = current main model. |
|
|
326
257
|
| `agentThinkingLevels` | Optional manual level per agent; missing = Auto. |
|
|
327
|
-
| `notifyOnReviewPass` | Deliver a standalone passing gate without waking the main agent. Default `false`. |
|
|
328
258
|
| `maxResultLines` | Lines kept in a completion message before the artifact takes over. Default `40`. |
|
|
329
259
|
| `agentScope` | Discover `user`, `project`, or `both` agent directories. Default `user`. |
|
|
330
260
|
| `idleTimeoutSec` | Seconds without child RPC output before termination; `0` disables. Default `90`. |
|
|
331
261
|
|
|
332
262
|
The delegation directive is always injected; there is no toggle. Invalid values
|
|
333
263
|
fall back safely, and stale keys — including the former `proactiveInjection`,
|
|
334
|
-
`maxConcurrency`, and `
|
|
264
|
+
`maxConcurrency`, `maxFixRounds`, and `notifyOnReviewPass` knobs — are dropped
|
|
265
|
+
automatically. Built-in roles a newer package no longer ships (such as the
|
|
266
|
+
retired `worker`/`cleaner`/`documenter`/`synthesizer`/`reviewer` set) are pruned
|
|
267
|
+
from `enabledAgents`, `knownAgents`, and the model/thinking tables at first
|
|
268
|
+
load, so the setup wizard never mixes old and new roles; custom agents are
|
|
269
|
+
untouched. At session
|
|
335
270
|
start, model overrides pi no longer reports are removed with a one-time notice. If
|
|
336
271
|
pi's own session compaction fails mid-thread, a notice surfaces the error and the
|
|
337
272
|
automatic retry instead of failing quietly.
|
|
@@ -401,7 +336,7 @@ npm test
|
|
|
401
336
|
```
|
|
402
337
|
|
|
403
338
|
There are no bundled runtime dependencies; pi and TypeBox are peers. The source is
|
|
404
|
-
split by responsibility: dispatch
|
|
339
|
+
split by responsibility: dispatch policy, thread lifecycle, RPC
|
|
405
340
|
transport, worktree integration, completion delivery, tools, and TUI status.
|
|
406
341
|
|
|
407
342
|
## License
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: executor
|
|
3
|
+
description: Default route for any delegated, self-contained task — implement, fix, refactor, test, clean up, sync docs, or merge fan-out results — then verify and hand off.
|
|
4
|
+
thinking: high
|
|
5
|
+
# No `tools` field => inherits all tools (full capability).
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are an executor agent with full capabilities in an isolated context window. You own one delegated, self-contained task end to end so the main conversation stays clean. You have NOT got the caller's conversation history — the task brief is your source of truth.
|
|
9
|
+
|
|
10
|
+
Repository instructions (AGENTS.md) and any skills available in this session apply to you as to any agent: follow their process for the domains they own (language style, tests, debugging, cleanup discipline, verification). Where a skill covers the same ground as this brief, the skill's discipline wins — except for the release boundary below, which always wins.
|
|
11
|
+
|
|
12
|
+
## Procedure
|
|
13
|
+
|
|
14
|
+
1. **Context.** Read the brief fully, plus referenced files and images, before acting. If critical context is missing, state what is missing rather than guessing.
|
|
15
|
+
2. **Plan.** Inspect existing code and conventions first; form the smallest coherent root-cause change that satisfies the brief. Prefer the design that deletes complexity over one that rearranges it. No unrelated refactors or standalone docs work unless the brief asks.
|
|
16
|
+
3. **Implement.** Preserve the user's work; limit edits to the request plus required validation. Follow the project's error handling, naming, and style. Synchronize README/docs/comments your change directly affects; never defer that drift.
|
|
17
|
+
4. **Verify.** Run the project's format/build/tests when they exist. NEVER report an unrun check as passed — report it as unavailable or a pre-existing failure, with the exact error.
|
|
18
|
+
|
|
19
|
+
## Cleanup work
|
|
20
|
+
|
|
21
|
+
When the brief authorizes cleanup (dead code, duplication, simplification), a candidate is not a deletion: re-read the load-bearing files and repeat the decisive searches yourself — never inherit proof from another agent's report. Search the whole repository for consumers before removing anything, and keep a candidate when a real consumer exists, dynamic reachability is unresolved, or the cut removes a user capability, public API, persisted format, or compatibility path unless the brief explicitly approves it. Consolidate semantically equivalent duplicates by extracting the smallest stable shared helper and migrating every in-scope caller. Finding no safe cut and making zero edits is valid.
|
|
22
|
+
|
|
23
|
+
## Merging inputs
|
|
24
|
+
|
|
25
|
+
When the brief names several inputs (result artifacts, reports, logs), read every input fully before writing. Deduplicate restatements into one attributed entry, verify disagreements with a short read when a cited file settles them, and report surviving conflicts side by side instead of averaging them away. Stay within the named inputs; report what they cannot answer as a gap.
|
|
26
|
+
|
|
27
|
+
## Boundaries
|
|
28
|
+
|
|
29
|
+
- Never commit, push, publish, tag, release, or bump a package version — the caller owns every release action, even when repository instructions normally automate release after green checks.
|
|
30
|
+
- Children are leaf processes: you cannot dispatch sub-agents.
|
|
31
|
+
- Never change runtime behavior to make documentation true; report the defect instead.
|
|
32
|
+
|
|
33
|
+
## Output format
|
|
34
|
+
|
|
35
|
+
Return only the concrete outcome. Do not repeat the task brief, the plan, the root-cause investigation, or the tool chronology.
|
|
36
|
+
|
|
37
|
+
## Completed
|
|
38
|
+
|
|
39
|
+
What was done, in a few lines.
|
|
40
|
+
|
|
41
|
+
## Files Changed
|
|
42
|
+
|
|
43
|
+
- `path/to/file.ts` — what changed.
|
|
44
|
+
|
|
45
|
+
## Verification
|
|
46
|
+
|
|
47
|
+
Which checks you ACTUALLY ran and their result (e.g. `tsc --noEmit` clean; `vitest` 12 passed). State explicitly anything you could not run and why.
|
|
48
|
+
|
|
49
|
+
## Notes (only when material)
|
|
50
|
+
|
|
51
|
+
Unresolved blockers, rejected requirements, or decisions the caller must know. Omit when nothing actionable.
|
|
52
|
+
|
|
53
|
+
Keep the final response comfortably below the 40-line delivery cap unless the result genuinely requires more.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ferris1225/pi-subagents",
|
|
3
|
-
"version": "4.1
|
|
3
|
+
"version": "4.2.1",
|
|
4
4
|
"description": "A managed sub-agent team for pi: specialized roles, pre-commit documentation sync, retained threads, auto-fix chains, model fallback, and Git worktree isolation.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/agents.ts
CHANGED
|
@@ -94,8 +94,8 @@ export function resolveAgentTools(
|
|
|
94
94
|
export function isWriteCapableAgent(
|
|
95
95
|
agent: Pick<AgentConfig, "name" | "tools">,
|
|
96
96
|
): boolean {
|
|
97
|
-
if (agent.name === "explorer"
|
|
98
|
-
if (agent.name === "
|
|
97
|
+
if (agent.name === "explorer") return false;
|
|
98
|
+
if (agent.name === "executor") return true;
|
|
99
99
|
if (!agent.tools) return true;
|
|
100
100
|
return agent.tools.includes("edit") || agent.tools.includes("write");
|
|
101
101
|
}
|
package/src/announcements.ts
CHANGED
|
@@ -4,7 +4,6 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
4
4
|
import { existsSync } from "node:fs";
|
|
5
5
|
import { loadConfig, saveConfig } from "./config.ts";
|
|
6
6
|
import { availableModelsInScope, filterUnavailableModelOverrides } from "./models.ts";
|
|
7
|
-
import { formatToolActivity, monitor } from "./monitor.ts";
|
|
8
7
|
import { announceRecoveryRecords } from "./recovery.ts";
|
|
9
8
|
import type { SubagentRuntime } from "./runtime.ts";
|
|
10
9
|
import { installActiveRunsWidget } from "./widget.ts";
|
|
@@ -36,32 +35,7 @@ async function migrateUnavailableAgentModels(
|
|
|
36
35
|
}
|
|
37
36
|
}
|
|
38
37
|
|
|
39
|
-
/** Track the parent pi session itself as the widget's first row: what the
|
|
40
|
-
* current model is doing while its agent loop runs. Same activity vocabulary
|
|
41
|
-
* as subagent rows (thinking / responding / tool + target), fed by the
|
|
42
|
-
* session's own extension events; the row disappears when the loop settles. */
|
|
43
|
-
function trackMainActivity(pi: ExtensionAPI): void {
|
|
44
|
-
pi.on("agent_start", () => monitor.setMainAgentActive(true));
|
|
45
|
-
pi.on("agent_end", () => monitor.setMainAgentActive(false));
|
|
46
|
-
pi.on("agent_settled", () => monitor.setMainAgentActive(false));
|
|
47
|
-
pi.on("model_select", (event) => monitor.setMainModel(event.model?.id));
|
|
48
|
-
pi.on("thinking_level_select", (event) => monitor.setMainThinking(event.level));
|
|
49
|
-
pi.on("message_update", (event) => {
|
|
50
|
-
if (event.message.role !== "assistant") return;
|
|
51
|
-
const kind = event.assistantMessageEvent.type;
|
|
52
|
-
if (kind === "text_start" || kind === "text_delta") monitor.setMainActivity("responding");
|
|
53
|
-
else if (kind === "thinking_start" || kind === "thinking_delta") monitor.setMainActivity("thinking");
|
|
54
|
-
});
|
|
55
|
-
pi.on("tool_execution_start", (event) =>
|
|
56
|
-
monitor.recordMainToolStart(event.toolName, formatToolActivity(event.toolName, event.args)));
|
|
57
|
-
pi.on("tool_execution_end", (event) => monitor.recordMainToolEnd(event.toolName, event.isError));
|
|
58
|
-
}
|
|
59
|
-
|
|
60
38
|
export function registerAnnouncements(pi: ExtensionAPI, runtime: SubagentRuntime): void {
|
|
61
|
-
// Registered at extension load (not session_start) so a model selection
|
|
62
|
-
// made during restore is already captured when the widget appears.
|
|
63
|
-
trackMainActivity(pi);
|
|
64
|
-
|
|
65
39
|
pi.on("session_start", async (_event, ctx) => {
|
|
66
40
|
if (!existsSync(runtime.configPath)) {
|
|
67
41
|
ctx.ui.notify(
|
package/src/completion.ts
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
* failure directly so it is never delayed.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { getResultOutput, isFailedResult, reviewVerdict, type SingleResult } from "./spawn.ts";
|
|
11
10
|
import { formatUsageCompact, sumUsage, type RunWaitReason } from "./monitor.ts";
|
|
12
11
|
import type { UsageStats } from "./rpc-run.ts";
|
|
13
12
|
|
|
@@ -121,16 +120,6 @@ export function completionGroupTriggersTurn(items: readonly CompletionMessageIte
|
|
|
121
120
|
return items.some((item) => item.triggerTurn);
|
|
122
121
|
}
|
|
123
122
|
|
|
124
|
-
/** Passing reviewer notifications may opt out of waking; every other result wakes. */
|
|
125
|
-
export function completionTriggersTurn(result: SingleResult, notifyOnReviewPass: boolean): boolean {
|
|
126
|
-
if (isFailedResult(result)) return true;
|
|
127
|
-
return !(
|
|
128
|
-
notifyOnReviewPass &&
|
|
129
|
-
result.agent === "reviewer" &&
|
|
130
|
-
reviewVerdict(getResultOutput(result)) === "pass"
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
123
|
/** Minimal shape of an active run, for the "others still running" footer. Kept
|
|
135
124
|
* decoupled from the monitor's RunView so this stays a pure, easily tested
|
|
136
125
|
* formatter; the caller maps its live runs into this shape. */
|
package/src/config.ts
CHANGED
|
@@ -13,7 +13,14 @@ import { dirname, join } from "node:path";
|
|
|
13
13
|
import { getAgentDir, withFileMutationQueue } from "@earendil-works/pi-coding-agent";
|
|
14
14
|
|
|
15
15
|
/** Full catalog of agents shipped with the package (selectable in /subagents-setup). */
|
|
16
|
-
export const BUILTIN_AGENT_NAMES = ["explorer", "
|
|
16
|
+
export const BUILTIN_AGENT_NAMES = ["explorer", "executor"] as const;
|
|
17
|
+
|
|
18
|
+
/** Built-in agent names this package no longer ships. Loading an older config
|
|
19
|
+
* prunes them from every record so the setup wizard, dispatch catalog, and
|
|
20
|
+
* model-routing table never surface dead roles. Custom names stay untouched —
|
|
21
|
+
* except one that reuses a removed built-in name, which this cleanup cannot
|
|
22
|
+
* distinguish and deliberately treats as retired. */
|
|
23
|
+
export const REMOVED_BUILTIN_AGENT_NAMES = ["worker", "cleaner", "documenter", "synthesizer", "reviewer"] as const;
|
|
17
24
|
|
|
18
25
|
/** Agents enabled out of the box on a fresh install. */
|
|
19
26
|
export const DEFAULT_ENABLED_AGENTS: readonly string[] = [...BUILTIN_AGENT_NAMES];
|
|
@@ -56,11 +63,6 @@ export interface SubagentsConfig {
|
|
|
56
63
|
agentModels: Record<string, string>;
|
|
57
64
|
/** Optional per-agent thinking preference. Runtime clamps it to the effective model's supported levels. */
|
|
58
65
|
agentThinkingLevels: Record<string, ThinkingLevel>;
|
|
59
|
-
/**
|
|
60
|
-
* When a standalone review passes (REVIEW_PASS verdict), deliver it without
|
|
61
|
-
* waking the main agent. Managed workflows always wake once at final delivery.
|
|
62
|
-
*/
|
|
63
|
-
notifyOnReviewPass: boolean;
|
|
64
66
|
/**
|
|
65
67
|
* Max lines of a sub-agent result carried in the completion message. Longer
|
|
66
68
|
* results are truncated; the full text is written to a temp file whose path
|
|
@@ -82,7 +84,6 @@ export const DEFAULT_CONFIG: SubagentsConfig = {
|
|
|
82
84
|
knownAgents: [...BUILTIN_AGENT_NAMES],
|
|
83
85
|
agentModels: {},
|
|
84
86
|
agentThinkingLevels: {},
|
|
85
|
-
notifyOnReviewPass: false,
|
|
86
87
|
maxResultLines: DEFAULT_MAX_RESULT_LINES,
|
|
87
88
|
agentScope: "user",
|
|
88
89
|
idleTimeoutSec: DEFAULT_IDLE_TIMEOUT_SEC,
|
|
@@ -165,10 +166,6 @@ export function normalizeConfig(raw: unknown): SubagentsConfig {
|
|
|
165
166
|
}
|
|
166
167
|
}
|
|
167
168
|
|
|
168
|
-
if (typeof raw.notifyOnReviewPass === "boolean") {
|
|
169
|
-
config.notifyOnReviewPass = raw.notifyOnReviewPass;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
169
|
const maxResultLines = clampCount(raw.maxResultLines, MAX_RESULT_LINES_LIMIT);
|
|
173
170
|
if (maxResultLines !== undefined) config.maxResultLines = maxResultLines;
|
|
174
171
|
|
|
@@ -193,6 +190,29 @@ function defaultConfig(): SubagentsConfig {
|
|
|
193
190
|
};
|
|
194
191
|
}
|
|
195
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Drop every removed built-in role from an already-normalized config: enabled
|
|
195
|
+
* and known lists, plus per-agent model and thinking routes. The schema-upgrade
|
|
196
|
+
* persistence in loadConfig writes the pruned shape back to disk.
|
|
197
|
+
*/
|
|
198
|
+
function pruneRemovedBuiltins(config: SubagentsConfig): SubagentsConfig {
|
|
199
|
+
const removed = new Set<string>(REMOVED_BUILTIN_AGENT_NAMES);
|
|
200
|
+
const filter = (names: readonly string[]): string[] => names.filter((name) => !removed.has(name));
|
|
201
|
+
const agentModels = { ...config.agentModels };
|
|
202
|
+
const agentThinkingLevels = { ...config.agentThinkingLevels };
|
|
203
|
+
for (const name of REMOVED_BUILTIN_AGENT_NAMES) {
|
|
204
|
+
delete agentModels[name];
|
|
205
|
+
delete agentThinkingLevels[name];
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
...config,
|
|
209
|
+
enabledAgents: filter(config.enabledAgents),
|
|
210
|
+
knownAgents: filter(config.knownAgents),
|
|
211
|
+
agentModels,
|
|
212
|
+
agentThinkingLevels,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
196
216
|
/**
|
|
197
217
|
* A shipped agent the config has never recorded is new in this release; the
|
|
198
218
|
* stale allow-list must not keep it dark. Enable it and adopt explorer's
|
|
@@ -226,6 +246,7 @@ function adoptNewBuiltins(config: SubagentsConfig): SubagentsConfig {
|
|
|
226
246
|
* A file from an older version (missing newer keys or holding extra keys) is
|
|
227
247
|
* normalized and persisted back, so the on-disk config stays current. Built-in
|
|
228
248
|
* agents the file has never seen are adopted: enabled with explorer's route.
|
|
249
|
+
* Built-in roles this package retired are pruned from every record.
|
|
229
250
|
*/
|
|
230
251
|
export async function loadConfig(configPath: string = getConfigPath()): Promise<SubagentsConfig> {
|
|
231
252
|
let text: string;
|
|
@@ -243,7 +264,10 @@ export async function loadConfig(configPath: string = getConfigPath()): Promise<
|
|
|
243
264
|
return defaultConfig();
|
|
244
265
|
}
|
|
245
266
|
|
|
246
|
-
|
|
267
|
+
// Adopt newly shipped roles first so the prune below works on the final
|
|
268
|
+
// catalog, then drop roles this package stopped shipping and persist the
|
|
269
|
+
// cleaned shape back to disk.
|
|
270
|
+
const config = pruneRemovedBuiltins(adoptNewBuiltins(normalizeConfig(parsed)));
|
|
247
271
|
|
|
248
272
|
// Schema upgrade: persist the normalized shape when the file gained fields
|
|
249
273
|
// (new version) or dropped invalid ones.
|