@ferris1225/pi-subagents 4.3.4 → 4.3.6
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 +36 -0
- package/README.md +103 -83
- package/agents/artisan.md +0 -1
- package/agents/steward.md +1 -2
- package/{src/index.ts → index.ts} +19 -19
- package/package.json +4 -3
- package/src/{config.ts → configuration/config.ts} +19 -24
- package/src/configuration/setup.ts +375 -0
- package/src/configuration/ui.ts +245 -0
- package/src/{agents.ts → delegation/agents.ts} +3 -3
- package/src/{dispatch.ts → delegation/dispatch.ts} +12 -18
- package/src/{prompt.ts → delegation/prompt.ts} +5 -9
- package/src/{background.ts → execution/background.ts} +3 -6
- package/src/execution/rpc-control.ts +235 -0
- package/src/{rpc-run.ts → execution/rpc-run.ts} +35 -225
- package/src/{session-fork.ts → execution/session-fork.ts} +1 -1
- package/src/{spawn.ts → execution/spawn.ts} +10 -8
- package/src/isolation/git-command.ts +147 -0
- package/src/isolation/managed-paths.ts +145 -0
- package/src/{recovery.ts → isolation/recovery.ts} +42 -13
- package/src/{temp-hygiene.ts → isolation/temp-hygiene.ts} +7 -7
- package/src/{worktree.ts → isolation/worktree.ts} +11 -158
- package/src/{completion.ts → lifecycle/completion.ts} +2 -2
- package/src/{durable.ts → lifecycle/durable.ts} +101 -27
- package/src/{runtime.ts → lifecycle/runtime.ts} +12 -12
- package/src/{thread-lifecycle.ts → lifecycle/thread-lifecycle.ts} +25 -519
- package/src/lifecycle/thread-restore.ts +253 -0
- package/src/lifecycle/thread-shared.ts +269 -0
- package/src/{tools.ts → lifecycle/tools.ts} +51 -13
- package/src/{announcements.ts → presentation/announcements.ts} +4 -4
- package/src/{format.ts → presentation/format.ts} +3 -3
- package/src/{monitor.ts → presentation/monitor.ts} +2 -2
- package/src/{widget.ts → presentation/widget.ts} +1 -1
- package/agents/sentinel.md +0 -16
- package/src/setup.ts +0 -344
- package/src/ui.ts +0 -160
- /package/src/{models.ts → configuration/models.ts} +0 -0
- /package/src/{status.ts → presentation/status.ts} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,42 @@ Published versions of `@ferris1225/pi-subagents`. Unpublished numbers
|
|
|
4
4
|
(`4.2.3`, `4.2.6`, `4.2.9`–`4.2.11`) never shipped on npm; their changes
|
|
5
5
|
landed in the next published release.
|
|
6
6
|
|
|
7
|
+
## 4.3.6
|
|
8
|
+
|
|
9
|
+
- Add `subagent_control steer` for nonblank, parent-mediated guidance to the current active
|
|
10
|
+
RPC attempt without replacing its logical objective. Stable control serialization orders
|
|
11
|
+
steering against stop and AbortSignal shutdown; bounded ACKs keep stop responsive.
|
|
12
|
+
- Refine the lean delegation directive: main owns architecture; for one high-stakes
|
|
13
|
+
uncertainty at most two read-only scouts may examine distinct hypotheses, without
|
|
14
|
+
capping unrelated disjoint scout scopes; main reconciles cited evidence, writers
|
|
15
|
+
and briefs never overlap, and new evidence steers the existing phase instead of
|
|
16
|
+
duplicating or restarting it.
|
|
17
|
+
- Steer background completions and stop results into the next parent model boundary
|
|
18
|
+
instead of queuing them until the whole parent run settles, preventing stale notifications
|
|
19
|
+
from arriving after the main agent has already finished.
|
|
20
|
+
- Validate durable session, worktree, repository, and recovery paths against their canonical
|
|
21
|
+
project-scoped layout before restore or cleanup. Forged and junction-escaping records are
|
|
22
|
+
dropped without deleting external targets.
|
|
23
|
+
- Keep worktree and patch artifacts referenced by recovery records through durable sweeps and
|
|
24
|
+
stale project-root retention until their recovery notice can be delivered.
|
|
25
|
+
- Clarify that worktree isolation protects Git changes rather than sandboxing child privileges
|
|
26
|
+
or third-party Pi packages.
|
|
27
|
+
|
|
28
|
+
## 4.3.5
|
|
29
|
+
|
|
30
|
+
- Restore `/subagents-setup`'s nested menu flow, fuzzy model picker, and disabled
|
|
31
|
+
custom-role discovery.
|
|
32
|
+
- Retire the built-in `sentinel` role and mandatory pre-commit review workflow.
|
|
33
|
+
Config normalization, loading, and saving now remove its enabled/known entries
|
|
34
|
+
and model/thinking overrides while preserving every other custom role.
|
|
35
|
+
- Keep role prompts self-contained: direct diagnosis, test, and cleanup rules remain,
|
|
36
|
+
while external loading directives are removed.
|
|
37
|
+
- Delegate substantial independent phases more proactively while capping the child-process
|
|
38
|
+
pool at six and retaining phase leases, duplicate-dispatch rejection, and single-route delivery.
|
|
39
|
+
- Move the extension assembly to package-root `index.ts`, group `src/` by responsibility,
|
|
40
|
+
and split thread restoration, shared lifecycle coordination, RPC control, and Git command
|
|
41
|
+
execution out of the largest modules. Pi now shows the package name without a `:src` suffix.
|
|
42
|
+
|
|
7
43
|
## 4.3.4
|
|
8
44
|
|
|
9
45
|
- Keep artisan, steward, and sentinel fully usable when Ferris skills are absent.
|
package/README.md
CHANGED
|
@@ -6,15 +6,16 @@
|
|
|
6
6
|

|
|
7
7
|

|
|
8
8
|
|
|
9
|
-
A managed engineering team for [pi](https://github.com/earendil-works/pi):
|
|
9
|
+
A managed engineering team for [pi](https://github.com/earendil-works/pi): three
|
|
10
10
|
focused sub-agents, durable threads, and Git worktree isolation. You install it
|
|
11
11
|
once and your main agent delegates on its own.
|
|
12
12
|
|
|
13
13
|
## What's new
|
|
14
14
|
|
|
15
|
-
**4.3.
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
**4.3.6** — adds live parent-mediated steering for running children, delivers background
|
|
16
|
+
completions and stop results at the next safe parent boundary, and hardens durable
|
|
17
|
+
recovery paths without treating Git worktrees as a security sandbox.
|
|
18
|
+
|
|
18
19
|
See [CHANGELOG.md](./CHANGELOG.md).
|
|
19
20
|
|
|
20
21
|
## Contents
|
|
@@ -24,7 +25,7 @@ See [CHANGELOG.md](./CHANGELOG.md).
|
|
|
24
25
|
- [The team](#the-team)
|
|
25
26
|
- [Dispatching work](#dispatching-work)
|
|
26
27
|
- [Parallel edits](#parallel-edits)
|
|
27
|
-
- [Threads: resume, stop](#threads-resume-stop)
|
|
28
|
+
- [Threads: steer, resume, stop](#threads-steer-resume-stop)
|
|
28
29
|
- [Live status and results](#live-status-and-results)
|
|
29
30
|
- [Models, thinking, and tools](#models-thinking-and-tools)
|
|
30
31
|
- [Configuration](#configuration)
|
|
@@ -41,12 +42,13 @@ at "spawn a child with a prompt" and leave the hard parts — when to delegate,
|
|
|
41
42
|
wide to fan out, what happens when a model dies, how results come
|
|
42
43
|
back — with you. This extension owns them:
|
|
43
44
|
|
|
44
|
-
- The main model gets a cost-aware routing contract and delegates
|
|
45
|
-
context saves more work than its
|
|
45
|
+
- The main model gets a cost-aware routing contract and proactively delegates
|
|
46
|
+
substantial self-contained phases when a fresh context saves more work than its
|
|
47
|
+
handoff costs.
|
|
46
48
|
- One active normalized task and working directory owns its phase, so an exact
|
|
47
49
|
duplicate dispatch is rejected instead of paying twice.
|
|
48
|
-
- Background completions
|
|
49
|
-
in-turn instead. A run uses exactly one route.
|
|
50
|
+
- Background completions and stop results arrive at the next parent model boundary;
|
|
51
|
+
`wait: true` returns the same result in-turn instead. A run uses exactly one route.
|
|
50
52
|
- Parallel writers use detached Git worktrees without touching your index.
|
|
51
53
|
Worktree setup obeys the bounded queue; final integration releases its process
|
|
52
54
|
slot.
|
|
@@ -63,11 +65,14 @@ Requires **pi >= 0.84.4** and **Node.js >= 22.19.0**.
|
|
|
63
65
|
pi install npm:@ferris1225/pi-subagents
|
|
64
66
|
```
|
|
65
67
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
Pi's extension list shows `@ferris1225/pi-subagents` without an internal source-path suffix.
|
|
69
|
+
|
|
70
|
+
Open pi and run `/subagents-setup`. The original menu flow lets you enable or
|
|
71
|
+
disable roles, configure one role's model and thinking level, or run the full setup
|
|
72
|
+
again. Each screen uses the usual arrow-key/Enter/Esc navigation, and model lists
|
|
73
|
+
remain searchable. Fresh installs select all three. A newly shipped built-in is
|
|
74
|
+
surfaced once without being re-enabled after you deliberately turn it off. Then ask
|
|
75
|
+
for work:
|
|
71
76
|
|
|
72
77
|
```text
|
|
73
78
|
Map how authentication works, fix the refresh race, run the tests, and review the diff.
|
|
@@ -78,18 +83,14 @@ directly when you want exact control.
|
|
|
78
83
|
|
|
79
84
|
## The team
|
|
80
85
|
|
|
81
|
-
| Agent
|
|
82
|
-
|
|
|
83
|
-
| `scout`
|
|
84
|
-
| `artisan`
|
|
85
|
-
| `steward`
|
|
86
|
-
| `sentinel` | Review-only | A post-cleanup adversarial review using standalone evidence gates plus any matching Ferris skills; reports only evidence-backed defects and concrete test gaps. |
|
|
86
|
+
| Agent | Access | Owns |
|
|
87
|
+
| --------- | --------- | ---- |
|
|
88
|
+
| `scout` | Read-only | Broad or unfamiliar code reconnaissance and external research. Returns compact file citations or source URLs as leads, not proof. |
|
|
89
|
+
| `artisan` | Full | One substantial primary change—implementation, fix, refactor, test, or docs—through root cause, affected verification, and local hygiene. |
|
|
90
|
+
| `steward` | Full | One final cleanup and cross-cutting docs/comment sync pass after a broad or multi-writer change. |
|
|
87
91
|
|
|
88
|
-
Role prompts are
|
|
89
|
-
|
|
90
|
-
the canonical source of deeper language, platform, debugging, testing, and audit
|
|
91
|
-
guidance. Artisan and sentinel load matching skills when available; steward uses
|
|
92
|
-
`ferris-audit`; a missing skill never blocks a role or changes its ownership contract.
|
|
92
|
+
Role prompts are self-contained and directly embed root-cause-first diagnosis,
|
|
93
|
+
meaningful test evidence, and bounded cleanup.
|
|
93
94
|
|
|
94
95
|
Custom roles join them with a Markdown file (see [Custom agents](#custom-agents)).
|
|
95
96
|
|
|
@@ -116,11 +117,10 @@ subagent({
|
|
|
116
117
|
});
|
|
117
118
|
```
|
|
118
119
|
|
|
119
|
-
Breadth is the main agent's call, not a configured
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
as slots free.
|
|
120
|
+
Breadth is the main agent's call, not a configured task cap: put every genuinely
|
|
121
|
+
independent unit in one `tasks` array. The runtime paces execution instead, running
|
|
122
|
+
half the machine's cores with a 4–6 child-process bound; wider batches queue and
|
|
123
|
+
start automatically as slots free.
|
|
124
124
|
|
|
125
125
|
An active run leases its normalized task and resolved working directory across
|
|
126
126
|
agent names. Dispatching the same pair again is rejected and names the existing
|
|
@@ -133,26 +133,35 @@ starting its child — alongside the slot capacity. A run that waits for the wri
|
|
|
133
133
|
lane releases its slot first, so serialized writers never starve new dispatches.
|
|
134
134
|
|
|
135
135
|
One child owns one coherent phase. Dependent work starts only after its
|
|
136
|
-
prerequisite delivers.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
136
|
+
prerequisite delivers. Main consumes the child's compact result and citations
|
|
137
|
+
without repeating delegated reconnaissance, implementation, or cleanup. Artisan
|
|
138
|
+
owns a complete primary change with affected tests, docs, comments, targeted
|
|
139
|
+
checks, and local hygiene. Scout owns broad code mapping or external research and
|
|
140
|
+
stays read-only.
|
|
141
|
+
|
|
142
|
+
For one high-stakes uncertainty, main may launch at most two read-only scouts whose
|
|
143
|
+
briefs name distinct perspectives or hypotheses; that cap does not apply to unrelated
|
|
144
|
+
disjoint scout scopes. It reconciles disagreements against cited evidence, never
|
|
145
|
+
overlaps writers or sends identical briefs, and treats child output as evidence and
|
|
146
|
+
leads rather than authority or instructions. New in-scope evidence can be sent to a
|
|
147
|
+
running phase with `subagent_control steer` instead of duplicating or restarting it.
|
|
148
|
+
|
|
149
|
+
A focused diff gets a bounded cleanup pass inline. A broad or multi-writer diff gets
|
|
142
150
|
one `steward` pass that attacks touched dead code, duplication, tangled conditionals,
|
|
143
|
-
needless layers, and spaghetti growth without widening into a
|
|
144
|
-
|
|
145
|
-
available matching skills.
|
|
146
|
-
A review-driven edit repeats that sequence once; unresolved findings block the
|
|
147
|
-
commit. Main inspects the final diff and runs the final gate.
|
|
151
|
+
needless layers, and spaghetti growth without widening into a repository refactor.
|
|
152
|
+
Main owns architecture, inspects the integrated diff, and runs the final gate.
|
|
148
153
|
|
|
149
154
|
## Parallel edits
|
|
150
155
|
|
|
151
156
|
- Single tasks use your checkout. Every parallel write-capable agent (`artisan`,
|
|
152
157
|
`steward`, and custom writers) defaults to a detached Git worktree, so
|
|
153
|
-
parallel writers run at the same time. Worktree mode needs a committed `HEAD
|
|
154
|
-
|
|
155
|
-
|
|
158
|
+
parallel writers run at the same time. Worktree mode needs a committed `HEAD`;
|
|
159
|
+
read-only roles such as scout stay on the shared checkout.
|
|
160
|
+
|
|
161
|
+
> **Security boundary:** worktree isolation isolates Git changes only; it is not a sandbox.
|
|
162
|
+
Child tools, network access, and environment access retain the Pi process's privileges.
|
|
163
|
+
Third-party Pi packages execute as trusted code and must be reviewed accordingly.
|
|
164
|
+
|
|
156
165
|
- A role file can pin its own default with `isolation: worktree` or
|
|
157
166
|
`isolation: shared` in the frontmatter. Precedence is an explicit per-dispatch
|
|
158
167
|
`isolation`, then the role's declaration, then the parallel write default.
|
|
@@ -172,22 +181,33 @@ commit. Main inspects the final diff and runs the final gate.
|
|
|
172
181
|
already been applied and only the cleanup failed, the next session start
|
|
173
182
|
removes the retained copy itself and clears the notice.
|
|
174
183
|
|
|
175
|
-
## Threads: resume, stop
|
|
184
|
+
## Threads: steer, resume, stop
|
|
176
185
|
|
|
177
186
|
Every dispatch returns a stable `#id`, which is the handle for the thread tools:
|
|
178
187
|
|
|
179
|
-
| Tool | What it does
|
|
180
|
-
| ------------------ |
|
|
181
|
-
| `subagent_control` | `
|
|
182
|
-
| `subagent_stop` | Destructively cancel, deliver
|
|
188
|
+
| Tool | What it does |
|
|
189
|
+
| ------------------ | ------------ |
|
|
190
|
+
| `subagent_control` | `steer` a currently running RPC attempt with additional in-scope evidence/guidance, or `resume` a parked/settled thread with retained context and an optional appended `objective`. |
|
|
191
|
+
| `subagent_stop` | Destructively cancel, deliver partial output, and retire the thread. Steering and follow-up messages still queued in the child are dropped so nothing can revive it later. |
|
|
183
192
|
|
|
184
193
|
```ts
|
|
194
|
+
subagent_control({ action: "steer", id: 7, objective: "The failing request used an expired token; account for that evidence." });
|
|
185
195
|
subagent_control({ action: "resume", id: 7, objective: "Finish the tests." });
|
|
186
196
|
```
|
|
187
197
|
|
|
198
|
+
`steer` requires a nonblank `objective` and accepts only the active running RPC
|
|
199
|
+
attempt for that stable id. It adds guidance to the current phase; it does not replace
|
|
200
|
+
the thread's original task. Parked, settled, queued, starting, retrying, interrupting,
|
|
201
|
+
stopped, retired, and missing threads are rejected without changing them. Steering ACKs
|
|
202
|
+
are bounded, and steering/stop are serialized so stop can clear queued child messages
|
|
203
|
+
and abort without a stale steer landing afterward.
|
|
204
|
+
|
|
188
205
|
There is no status, polling, or separate wait tool. A background dispatch returns
|
|
189
|
-
a launch receipt, then its completion is
|
|
190
|
-
|
|
206
|
+
a launch receipt, then its completion is steered at the next safe parent boundary—after
|
|
207
|
+
the current tool calls and before the next model call. This wakes the main model without
|
|
208
|
+
waiting for its whole run to end.
|
|
209
|
+
|
|
210
|
+
`wait: true` instead holds that tool call until its new runs settle,
|
|
191
211
|
which is useful for one-shot `pi -p` sessions. It claims the delivery route before
|
|
192
212
|
launch, so the same result cannot also arrive as a background completion; if the
|
|
193
213
|
parent turn is aborted, delivery falls back to the completion path.
|
|
@@ -210,6 +230,11 @@ missing or reused. If a recorded worktree is gone, the run is surfaced as failed
|
|
|
210
230
|
and non-resumable while its retained session and recovery record remain available
|
|
211
231
|
for inspection or destructive stop.
|
|
212
232
|
|
|
233
|
+
Persisted sessions and worktrees are resumed or removed only when their canonical paths
|
|
234
|
+
match the current project's managed storage layout and repository. Invalid records are
|
|
235
|
+
dropped without following or deleting their targets. Recovery-owned worktrees and patches
|
|
236
|
+
remain protected from startup sweeps and project-root retention until recovery is announced.
|
|
237
|
+
|
|
213
238
|
Only interrupted work needs a record, so a thread that completes or fails cleanly
|
|
214
239
|
drops its own. That also means a reload keeps interrupted threads resumable, while
|
|
215
240
|
threads that had already finished keep only their delivered result.
|
|
@@ -279,17 +304,15 @@ rather than a fabricated number.
|
|
|
279
304
|
## Models, thinking, and tools
|
|
280
305
|
|
|
281
306
|
Each agent runs on the current main model or one picked in `/subagents-setup`,
|
|
282
|
-
which labels vision and text-only models.
|
|
283
|
-
|
|
284
|
-
too. If a selected model is missing, rate-limited, or fails at the provider level,
|
|
285
|
-
the **same retained
|
|
307
|
+
which labels vision and text-only models. If a selected model is missing,
|
|
308
|
+
rate-limited, or fails at the provider level, the **same retained
|
|
286
309
|
session** continues on the main model, so finished searches, reads, and edits
|
|
287
310
|
survive. Ordinary task failures do not trigger a handoff.
|
|
288
311
|
|
|
289
|
-
Thinking is a **role default** — scout `low`, artisan `high`, steward `medium
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
312
|
+
Thinking is a **role default** — scout `low`, artisan `high`, steward `medium` —
|
|
313
|
+
clamped to what the effective model supports. `/subagents-setup` →
|
|
314
|
+
_Configure an agent_ lists only the levels that model supports and marks the role
|
|
315
|
+
default; selecting it clears the stored override. There is no
|
|
293
316
|
Auto choice, no per-dispatch `thinking` flag, and
|
|
294
317
|
no `thinking` field in agent Markdown. Precedence: your setup override > the
|
|
295
318
|
role default, then the model clamp. There is no separate vision mode — assign
|
|
@@ -314,25 +337,19 @@ For external research scout prefers official documentation, specifications, rele
|
|
|
314
337
|
notes, and first-party repositories; it fetches decisive pages rather than citing
|
|
315
338
|
search snippets, records material dates/versions, and marks uncertainty.
|
|
316
339
|
|
|
317
|
-
`sentinel` has an explicit retrieval/documentation list plus a portable shell
|
|
318
|
-
slot for Git inspection and the smallest proving check. It is pinned to `shared`
|
|
319
|
-
so it sees the current uncommitted diff. Its concise prompt uses matching available
|
|
320
|
-
Ferris skills, preserves their owners, and forbids mutation; missing skills do not
|
|
321
|
-
block review. This is a review contract,
|
|
322
|
-
not a hard shell sandbox.
|
|
323
|
-
|
|
324
340
|
## Configuration
|
|
325
341
|
|
|
326
|
-
`/subagents-setup` opens
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
342
|
+
`/subagents-setup` opens the original settings menu: enable or disable roles,
|
|
343
|
+
configure one enabled role's model and thinking level, or walk through a full
|
|
344
|
+
re-setup. `Esc` moves back through the menu stack, and model lists support fuzzy
|
|
345
|
+
search. Built-in and previously configured custom roles remain available in the
|
|
346
|
+
enable menu. Other settings live in
|
|
330
347
|
`~/.pi/agent/pi-subagents.json` (following `PI_CODING_AGENT_DIR`):
|
|
331
348
|
|
|
332
349
|
```json
|
|
333
350
|
{
|
|
334
|
-
"enabledAgents": ["scout", "artisan", "steward"
|
|
335
|
-
"knownAgents": ["scout", "artisan", "steward"
|
|
351
|
+
"enabledAgents": ["scout", "artisan", "steward"],
|
|
352
|
+
"knownAgents": ["scout", "artisan", "steward"],
|
|
336
353
|
"agentModels": { "scout": "anthropic/claude-haiku-4-5" },
|
|
337
354
|
"agentThinkingLevels": { "artisan": "high" },
|
|
338
355
|
"maxResultLines": 40,
|
|
@@ -345,7 +362,7 @@ selection remains inside the overlay and supports fuzzy search. Other settings l
|
|
|
345
362
|
| --------------------- | ------- |
|
|
346
363
|
| `enabledAgents` | Agents available for discovery and delegation. `[]` disables all. |
|
|
347
364
|
| `knownAgents` | Roles already surfaced by setup; retains disabled custom roles and tracks built-in adoption. |
|
|
348
|
-
| `agentModels` | Optional model per agent; missing means
|
|
365
|
+
| `agentModels` | Optional model per agent; missing means the current main model. |
|
|
349
366
|
| `agentThinkingLevels` | Optional setup override per agent; missing means the role default. |
|
|
350
367
|
| `maxResultLines` | Lines kept in a completion message before the artifact takes over. Default `40`. |
|
|
351
368
|
| `agentScope` | Discover `user`, `project`, or `both` agent directories. Default `user`. |
|
|
@@ -354,8 +371,10 @@ selection remains inside the overlay and supports fuzzy search. Other settings l
|
|
|
354
371
|
When at least one role is enabled, the cost-aware delegation directive is injected
|
|
355
372
|
automatically. `enabledAgents` is authoritative after catalog adoption: a newly
|
|
356
373
|
shipped built-in is appended once, then `knownAgents` records that it was surfaced
|
|
357
|
-
so a deliberate later disable remains disabled.
|
|
358
|
-
|
|
374
|
+
so a deliberate later disable remains disabled. On upgrade, the retired `sentinel`
|
|
375
|
+
entry is removed from enabled and known agents plus model and thinking overrides;
|
|
376
|
+
all other custom roles and known-agent entries remain intact. Invalid known fields
|
|
377
|
+
fall back safely, and unknown fields are dropped when canonical config is persisted.
|
|
359
378
|
|
|
360
379
|
At session start, model overrides that pi no longer reports are removed with a
|
|
361
380
|
one-time notice. If pi's own session compaction fails mid-thread, a notice surfaces
|
|
@@ -398,16 +417,16 @@ that removes it, so this directory does not grow without bound:
|
|
|
398
417
|
| `ferris-pi-subagents/pi-subagents-recovery.json` | Worktree integration and cleanup failures | When the retained patch or worktree it points at is gone |
|
|
399
418
|
| `ferris-pi-subagents/<project>/pi-subagents-threads.json` | One record per interrupted thread | When the thread settles, or after 30 days |
|
|
400
419
|
| `ferris-pi-subagents/<project>/sessions/` | Retained child sessions that a resume continues from | When the thread settles or its retained record is removed |
|
|
401
|
-
| `ferris-pi-subagents/<project>/worktrees/` | Isolated checkouts for parallel writers | On integration, or when no
|
|
420
|
+
| `ferris-pi-subagents/<project>/worktrees/` | Isolated checkouts for parallel writers | On integration, or when no thread/recovery record claims them |
|
|
402
421
|
| `ferris-pi-subagents/<project>/results/` | Full text of truncated results | After 7 days, or beyond 50 per project |
|
|
403
422
|
| `ferris-pi-subagents/<project>/tmp/` | Child prompt copies and the no-retry policy shim | When its owning process exits |
|
|
404
|
-
| `ferris-pi-subagents/<project>/` | All of the above for one checkout |
|
|
423
|
+
| `ferris-pi-subagents/<project>/` | All of the above for one checkout | After 3 idle days unless a thread/recovery record claims it |
|
|
405
424
|
|
|
406
425
|
Cleanup runs at session start and is deliberately conservative. A directory goes
|
|
407
|
-
away only when the process that created it is gone and no manifest record still
|
|
408
|
-
claims it, so a live sibling pi instance never loses state and parked
|
|
409
|
-
outlives its own process by design
|
|
410
|
-
|
|
426
|
+
away only when the process that created it is gone and no valid manifest record still
|
|
427
|
+
claims it, so a live sibling pi instance never loses state and parked or recovery-owned
|
|
428
|
+
work outlives its own process by design. Thread and recovery references always beat an
|
|
429
|
+
age rule.
|
|
411
430
|
|
|
412
431
|
## Development
|
|
413
432
|
|
|
@@ -418,8 +437,9 @@ npm run check
|
|
|
418
437
|
|
|
419
438
|
`npm run check` is `tsc --noEmit` plus the unit tests (`npm test`). There are
|
|
420
439
|
no bundled runtime dependencies; pi and TypeBox are peers. The source is
|
|
421
|
-
|
|
422
|
-
|
|
440
|
+
grouped by responsibility under `src/`: configuration, delegation, execution, isolation,
|
|
441
|
+
lifecycle, and presentation. Thread restoration, shared lifecycle coordination, RPC control,
|
|
442
|
+
and Git command execution live in focused modules rather than oversized catch-all files.
|
|
423
443
|
|
|
424
444
|
## Changelog
|
|
425
445
|
|
package/agents/artisan.md
CHANGED
|
@@ -7,7 +7,6 @@ You own one primary change phase: implementation, fix, refactor, test, or substa
|
|
|
7
7
|
|
|
8
8
|
## Rules
|
|
9
9
|
|
|
10
|
-
- Use matching ferris skills when available: `ferris-debug` for unexplained failures, `ferris-tests` for test changes, and the relevant language or platform skill. Missing skills are not a blocker; the rules below are the fallback contract.
|
|
11
10
|
- For a reported defect or failure, inspect current behavior, confirm the defect before editing, and establish its root cause; a disproved issue means zero edits.
|
|
12
11
|
- Make the smallest coherent root-cause change. Preserve unrelated work and existing conventions; avoid speculative abstractions and unrelated cleanup.
|
|
13
12
|
- Own the complete primary change plus directly affected tests, README/docs, comments, and local diff hygiene. Remove debug output, dead code, stale comments, and other debris introduced in your scope.
|
package/agents/steward.md
CHANGED
|
@@ -3,11 +3,10 @@ name: steward
|
|
|
3
3
|
description: Pre-commit cleanup and cross-cutting docs/comment sync for a completed broad or multi-writer change.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
You own one final hygiene phase after primary writing has finished
|
|
6
|
+
You own one final hygiene phase after primary writing has finished. The task brief is your only context.
|
|
7
7
|
|
|
8
8
|
## Rules
|
|
9
9
|
|
|
10
|
-
- Use `ferris-audit` when available and apply its automatic pre-commit tier. Missing skills are not a blocker; the rules below are the fallback contract and must not widen into repo-wide cleanup.
|
|
11
10
|
- Require a named completed scope such as an uncommitted diff or Git range. Start there; never repeat implementation or reconnaissance, and stop if primary writing is still active.
|
|
12
11
|
- Hunt hard inside the touched scope: dead or unreachable code, unused imports/exports, duplicated facts or branches, debug residue, stale comments, one-off flags, tangled conditionals, pass-through wrappers, cast/optional fallback sprawl, feature logic in shared paths, and growth toward 1000-line files.
|
|
13
12
|
- Prefer deleting branches, state, and layers; otherwise reuse the canonical helper. Never merely move spaghetti. Prove every cut has no live consumer, and keep uncertain dynamic behavior, public APIs, persisted formats, and compatibility.
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Assembly point: builds the shared runtime and registers everything.
|
|
5
5
|
* The heavy lifting lives in focused modules:
|
|
6
|
-
* -
|
|
7
|
-
* -
|
|
8
|
-
* -
|
|
9
|
-
* -
|
|
10
|
-
* -
|
|
11
|
-
* -
|
|
6
|
+
* - delegation/ — role discovery, routing prompts, and tool contract
|
|
7
|
+
* - configuration/ — persisted settings, model routes, and setup UI
|
|
8
|
+
* - execution/ — process queue, RPC transport/control, and model handoff
|
|
9
|
+
* - lifecycle/ — durable threads, restoration, controls, and delivery
|
|
10
|
+
* - isolation/ — Git worktrees, recovery, and temporary-state hygiene
|
|
11
|
+
* - presentation/ — announcements, formatting, monitor, status, and widget
|
|
12
12
|
*
|
|
13
13
|
* Also registers the `/subagents-setup` command and a `before_agent_start` hook
|
|
14
14
|
* that injects a delegation directive into the parent system prompt so the main
|
|
@@ -20,19 +20,19 @@
|
|
|
20
20
|
|
|
21
21
|
import { getAgentDir, type ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
22
22
|
import { Text } from "@earendil-works/pi-tui";
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import { registerSubagentTool } from "./dispatch.ts";
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import { createRuntime } from "./runtime.ts";
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
import { clearActiveRunsWidget } from "./widget.ts";
|
|
23
|
+
import { getConfigPath, loadConfig } from "./src/configuration/config.ts";
|
|
24
|
+
import { runSetup } from "./src/configuration/setup.ts";
|
|
25
|
+
import { discoverAgents } from "./src/delegation/agents.ts";
|
|
26
|
+
import { registerSubagentTool } from "./src/delegation/dispatch.ts";
|
|
27
|
+
import { buildDelegationDirective } from "./src/delegation/prompt.ts";
|
|
28
|
+
import { currentSubagentDepth } from "./src/execution/spawn.ts";
|
|
29
|
+
import { createRuntime } from "./src/lifecycle/runtime.ts";
|
|
30
|
+
import { bootstrapDurableState } from "./src/lifecycle/thread-restore.ts";
|
|
31
|
+
import { registerLookupTools } from "./src/lifecycle/tools.ts";
|
|
32
|
+
import { registerAnnouncements } from "./src/presentation/announcements.ts";
|
|
33
|
+
import { matchRunIds } from "./src/presentation/format.ts";
|
|
34
|
+
import { clearActiveRunsStatus } from "./src/presentation/status.ts";
|
|
35
|
+
import { clearActiveRunsWidget } from "./src/presentation/widget.ts";
|
|
36
36
|
|
|
37
37
|
export { matchRunIds };
|
|
38
38
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ferris1225/pi-subagents",
|
|
3
|
-
"version": "4.3.
|
|
4
|
-
"description": "A managed sub-agent team for pi: scout, artisan,
|
|
3
|
+
"version": "4.3.6",
|
|
4
|
+
"description": "A managed sub-agent team for pi: scout, artisan, and steward roles, durable threads, model fallback, and Git worktree isolation.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"dead-code"
|
|
26
26
|
],
|
|
27
27
|
"files": [
|
|
28
|
+
"index.ts",
|
|
28
29
|
"src",
|
|
29
30
|
"agents",
|
|
30
31
|
"README.md",
|
|
@@ -33,7 +34,7 @@
|
|
|
33
34
|
],
|
|
34
35
|
"pi": {
|
|
35
36
|
"extensions": [
|
|
36
|
-
"./
|
|
37
|
+
"./index.ts"
|
|
37
38
|
]
|
|
38
39
|
},
|
|
39
40
|
"scripts": {
|
|
@@ -13,11 +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 = ["scout", "artisan", "steward"
|
|
16
|
+
export const BUILTIN_AGENT_NAMES = ["scout", "artisan", "steward"] as const;
|
|
17
17
|
|
|
18
|
-
// Historical catalog for configs written before
|
|
19
|
-
// frozen so future built-ins are still recognized as new.
|
|
20
|
-
const
|
|
18
|
+
// Historical catalog for configs written before built-in adoption tracking.
|
|
19
|
+
// Keep this frozen so future built-ins are still recognized as new.
|
|
20
|
+
const INITIAL_AGENT_NAMES = ["scout", "artisan", "steward"] as const;
|
|
21
|
+
|
|
22
|
+
/** Names that used to be built in and must not survive as custom roles. */
|
|
23
|
+
const RETIRED_AGENT_NAMES = new Set(["sentinel"]);
|
|
21
24
|
|
|
22
25
|
/** Agents enabled out of the box on a fresh install. */
|
|
23
26
|
export const DEFAULT_ENABLED_AGENTS: readonly string[] = [...BUILTIN_AGENT_NAMES];
|
|
@@ -38,8 +41,6 @@ export function roleThinkingLevel(agentName: string): ThinkingLevel {
|
|
|
38
41
|
return "low";
|
|
39
42
|
case "artisan":
|
|
40
43
|
return "high";
|
|
41
|
-
case "sentinel":
|
|
42
|
-
return "max";
|
|
43
44
|
case "steward":
|
|
44
45
|
return "medium";
|
|
45
46
|
default:
|
|
@@ -47,14 +48,6 @@ export function roleThinkingLevel(agentName: string): ThinkingLevel {
|
|
|
47
48
|
}
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
/** Sentinel shares artisan's configured model unless it has its own override. */
|
|
51
|
-
export function configuredModelForAgent(
|
|
52
|
-
agentModels: Readonly<Record<string, string>>,
|
|
53
|
-
agentName: string,
|
|
54
|
-
): string | undefined {
|
|
55
|
-
return agentModels[agentName] ?? (agentName === "sentinel" ? agentModels.artisan : undefined);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
51
|
/** Short responsibility line shown next to each built-in in setup lists. */
|
|
59
52
|
export interface AgentProfile {
|
|
60
53
|
/** A few words for picker rows. */
|
|
@@ -76,10 +69,6 @@ export const AGENT_PROFILES: Record<(typeof BUILTIN_AGENT_NAMES)[number], AgentP
|
|
|
76
69
|
summary: "pre-commit finish",
|
|
77
70
|
remark: "Cleans a completed broad or multi-writer diff and synchronizes cross-cutting docs/comments without changing behavior.",
|
|
78
71
|
},
|
|
79
|
-
sentinel: {
|
|
80
|
-
summary: "adversarial review",
|
|
81
|
-
remark: "Challenges every cleaned pre-commit diff with standalone evidence gates plus available matching skills; follows artisan's model with max thinking by default.",
|
|
82
|
-
},
|
|
83
72
|
};
|
|
84
73
|
|
|
85
74
|
export function agentProfile(name: string): AgentProfile | undefined {
|
|
@@ -143,8 +132,7 @@ export const DEFAULT_CONFIG: SubagentsConfig = {
|
|
|
143
132
|
|
|
144
133
|
export const FIRST_RUN_SETUP_HINT =
|
|
145
134
|
"Run /subagents-setup to choose enabled roles, models, and thinking levels. " +
|
|
146
|
-
"
|
|
147
|
-
"Scout maps code or researches external sources, artisan owns the primary change, steward cleans broad final diffs, and sentinel reviews after cleanup before every commit.";
|
|
135
|
+
"Scout maps code or researches external sources, artisan owns the primary change, and steward cleans broad final diffs.";
|
|
148
136
|
|
|
149
137
|
export function getConfigPath(agentDir: string = getAgentDir()): string {
|
|
150
138
|
return join(agentDir, CONFIG_FILE_NAME);
|
|
@@ -181,15 +169,21 @@ export function normalizeConfig(raw: unknown): SubagentsConfig {
|
|
|
181
169
|
|
|
182
170
|
if (Array.isArray(raw.enabledAgents)) {
|
|
183
171
|
const names = raw.enabledAgents.filter(
|
|
184
|
-
(name): name is string =>
|
|
172
|
+
(name): name is string =>
|
|
173
|
+
typeof name === "string" &&
|
|
174
|
+
name.trim().length > 0 &&
|
|
175
|
+
!RETIRED_AGENT_NAMES.has(name.trim()),
|
|
185
176
|
);
|
|
186
177
|
// An explicitly empty array is honored; duplicates collapse.
|
|
187
178
|
config.enabledAgents = [...new Set(names.map((name) => name.trim()))];
|
|
188
179
|
}
|
|
189
180
|
|
|
190
|
-
const rawKnownAgents = Array.isArray(raw.knownAgents) ? raw.knownAgents :
|
|
181
|
+
const rawKnownAgents = Array.isArray(raw.knownAgents) ? raw.knownAgents : INITIAL_AGENT_NAMES;
|
|
191
182
|
config.knownAgents = [...new Set(rawKnownAgents.filter(
|
|
192
|
-
(name): name is string =>
|
|
183
|
+
(name): name is string =>
|
|
184
|
+
typeof name === "string" &&
|
|
185
|
+
name.trim().length > 0 &&
|
|
186
|
+
!RETIRED_AGENT_NAMES.has(name.trim()),
|
|
193
187
|
).map((name) => name.trim()))];
|
|
194
188
|
for (const name of config.enabledAgents) {
|
|
195
189
|
if (!config.knownAgents.includes(name)) config.knownAgents.push(name);
|
|
@@ -198,7 +192,7 @@ export function normalizeConfig(raw: unknown): SubagentsConfig {
|
|
|
198
192
|
if (isRecord(raw.agentModels)) {
|
|
199
193
|
for (const [rawKey, value] of Object.entries(raw.agentModels)) {
|
|
200
194
|
const key = rawKey.trim();
|
|
201
|
-
if (key !== "" && isModelReference(value)) {
|
|
195
|
+
if (key !== "" && !RETIRED_AGENT_NAMES.has(key) && isModelReference(value)) {
|
|
202
196
|
config.agentModels[key] = value.trim();
|
|
203
197
|
}
|
|
204
198
|
}
|
|
@@ -209,6 +203,7 @@ export function normalizeConfig(raw: unknown): SubagentsConfig {
|
|
|
209
203
|
const key = rawKey.trim();
|
|
210
204
|
if (
|
|
211
205
|
key !== "" &&
|
|
206
|
+
!RETIRED_AGENT_NAMES.has(key) &&
|
|
212
207
|
typeof value === "string" &&
|
|
213
208
|
(THINKING_LEVEL_VALUES as readonly string[]).includes(value)
|
|
214
209
|
) {
|