pi-legion 0.16.0 → 2.0.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/CHANGELOG.md +117 -0
- package/CONTRIBUTING.md +109 -18
- package/README.md +389 -630
- package/agents/advisor.md +7 -5
- package/agents/centurion.md +53 -0
- package/agents/designer.md +33 -33
- package/agents/planner.md +1 -2
- package/agents/prototype.md +2 -2
- package/agents/reviewer.md +66 -31
- package/agents/scout.md +6 -2
- package/agents/worker.md +11 -3
- package/docs/contracts/esc-abort-v1.md +131 -0
- package/docs/contracts/esc-cascade-v2.md +101 -0
- package/docs/design.md +73 -37
- package/docs/legion-v2-contract.md +461 -0
- package/docs/legion-v2-delivery.md +62 -0
- package/docs/retire-hardening-contract.md +125 -0
- package/docs/subagents-teams.md +76 -0
- package/docs/tui-viewer-contract.md +276 -0
- package/docs/vexillatio-contract.md +162 -0
- package/index.ts +1568 -288
- package/package.json +19 -12
- package/skills/pi-legion/SKILL.md +86 -94
- package/src/agents/agents.ts +66 -16
- package/src/agents/overrides.ts +2 -1
- package/src/agents/paths.ts +1 -1
- package/src/agents/settings.ts +268 -20
- package/src/api.ts +49 -48
- package/src/{herdr → backends/legacy}/client.ts +14 -69
- package/src/{shared → backends/legacy}/cursor-chat.ts +2 -2
- package/src/{herdr → backends/legacy}/runner.ts +22 -9
- package/src/backends/legacy/types.ts +138 -0
- package/src/extension/auto-doctor.ts +144 -0
- package/src/extension/budget.ts +2 -2
- package/src/extension/child-guard.ts +214 -201
- package/src/extension/event-bus.ts +263 -0
- package/src/extension/legion-commands.ts +773 -0
- package/src/extension/legion-doctor.ts +975 -0
- package/src/extension/legion-status.ts +172 -0
- package/src/extension/legion-stop.ts +150 -0
- package/src/extension/legion-tools.ts +816 -0
- package/src/extension/notify.ts +24 -19
- package/src/extension/playbook.ts +20 -89
- package/src/extension/recycle.ts +2 -7
- package/src/extension/runtime.ts +736 -107
- package/src/extension/scout-guard.ts +492 -0
- package/src/extension/slash.ts +89 -438
- package/src/extension/spawn-hints.ts +79 -0
- package/src/extension/summary.ts +17 -58
- package/src/legion/budget.ts +165 -0
- package/src/legion/db.ts +242 -0
- package/src/legion/events.ts +150 -0
- package/src/legion/mail.ts +412 -0
- package/src/legion/nodes.ts +461 -0
- package/src/legion/rebuild.ts +533 -0
- package/src/legion/state-machine.ts +313 -0
- package/src/legion/usage.ts +101 -0
- package/src/runs/acceptance.ts +82 -25
- package/src/runs/args.ts +117 -15
- package/src/runs/hygiene.ts +335 -0
- package/src/runs/orchestrator.ts +808 -1681
- package/src/runs/owner.ts +61 -0
- package/src/runs/provider-pin.ts +59 -0
- package/src/runs/provision.ts +135 -0
- package/src/runs/role-tool-gates.ts +103 -0
- package/src/runs/store.ts +44 -17
- package/src/runs/verify-queue.ts +195 -0
- package/src/shared/name.ts +29 -0
- package/src/shared/output-cap.ts +57 -0
- package/src/shared/progress.ts +57 -135
- package/src/shared/session.ts +280 -173
- package/src/shared/types.ts +115 -208
- package/src/supervisor/child-memory.ts +140 -0
- package/src/supervisor/proc-tree.ts +103 -0
- package/src/supervisor/rpc-supervisor.ts +1033 -0
- package/src/supervisor/types.ts +153 -0
- package/src/supervisor/ui-proxy.ts +226 -0
- package/src/tui/chat-items.ts +330 -0
- package/src/tui/live-overlay.ts +874 -0
- package/src/tui/status.ts +553 -27
- package/src/tui/tree-view.ts +808 -0
- package/src/tui/viz/canvas.ts +119 -0
- package/src/tui/viz/layout.ts +111 -0
- package/src/tui/viz/model.ts +123 -0
- package/src/tui/viz/palette.ts +188 -0
- package/src/tui/viz/panels.ts +532 -0
- package/src/tui/viz/railway.ts +313 -0
- package/src/tui/viz/rate.ts +211 -0
- package/src/tui/viz/view.ts +228 -0
- package/src/extension/parent-label.ts +0 -115
- package/src/profiles/classify.ts +0 -375
- package/src/profiles/profiles.ts +0 -730
- package/src/runs/layout.ts +0 -258
package/README.md
CHANGED
|
@@ -1,715 +1,474 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pi-legion
|
|
3
3
|
npm: "pi-legion"
|
|
4
|
-
description: "
|
|
4
|
+
description: "Pi Legion v2: recursive centurion trees on headless Pi RPC; shared SQLite WAL legion.db. Four-stage planning/implementing/reviewing/verifying state machine, parent/child/sibling legion_mail, three-layer /legion UI, upward MR trees, squad resume."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# pi-legion
|
|
8
8
|
|
|
9
|
-
A
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
A herdr subagent is a **supervised live process**, not a one-shot function call.
|
|
13
|
-
It stays resident across turns, can be steered mid-flight, resumed after its
|
|
14
|
-
pane is gone, and stays visible to you. The parent session never has to invent
|
|
15
|
-
`herdr pane split` / `herdr agent start` — one `subagent` tool call is the
|
|
16
|
-
whole launch path.
|
|
9
|
+
A Pi extension for building recursive, resumable agent teams. A `centurion`
|
|
10
|
+
leads each squad; Pi RPC runs its children as headless processes, while a
|
|
11
|
+
shared SQLite ledger records the whole tree.
|
|
17
12
|
|
|
18
13
|
> **Name note.** This package was formerly published as
|
|
19
|
-
> `@zzjcool/pi-herdr-subagents` (through 0.14.3; renamed after an npm
|
|
20
|
-
>
|
|
21
|
-
>
|
|
22
|
-
>
|
|
23
|
-
>
|
|
24
|
-
> `pi install npm:pi-legion`.
|
|
14
|
+
> `@zzjcool/pi-herdr-subagents` (through 0.14.3; renamed after an npm name
|
|
15
|
+
> collision), then as `pi-herdr-swarm` (0.15.0), before becoming `pi-legion`
|
|
16
|
+
> in 0.16.0. **2.0.0: the legion era — recursive centurion tree on pi RPC transport; herdr pane execution archived to legacy**
|
|
17
|
+
>
|
|
18
|
+
> **What changed in 2.x?** See [CHANGELOG.md](CHANGELOG.md) (Keep a Changelog). Published notes also land on [GitHub Releases](https://github.com/zzjcool/pi-legion/releases).
|
|
25
19
|
|
|
26
20
|
## Contents
|
|
27
21
|
|
|
28
|
-
- [
|
|
29
|
-
- [Install](#install)
|
|
30
|
-
- [What the plugin does for you](#what-the-plugin-does-for-you)
|
|
22
|
+
- [Architecture](#architecture)
|
|
31
23
|
- [Quick start](#quick-start)
|
|
32
|
-
- [
|
|
33
|
-
- [
|
|
34
|
-
- [
|
|
35
|
-
- [
|
|
36
|
-
- [
|
|
37
|
-
- [
|
|
38
|
-
- [
|
|
39
|
-
- [
|
|
40
|
-
- [
|
|
41
|
-
- [Success and failure](#success-and-failure)
|
|
42
|
-
- [Programmatic API](#programmatic-api)
|
|
24
|
+
- [Legion tools and phases](#legion-tools-and-phases)
|
|
25
|
+
- [Three-layer visualization](#three-layer-visualization)
|
|
26
|
+
- [Settings and budget gates](#settings-and-budget-gates)
|
|
27
|
+
- [Scout guard](#scout-guard)
|
|
28
|
+
- [Team 配置](#team-配置)
|
|
29
|
+
- [Parent/child context conventions](#parentchild-context-conventions)
|
|
30
|
+
- [Delivery and resume](#delivery-and-resume)
|
|
31
|
+
- [Herdr transition and role migration](#herdr-transition-and-role-migration)
|
|
32
|
+
- [Changelog](CHANGELOG.md)
|
|
43
33
|
- [Development](#development)
|
|
44
|
-
- [Known limitations](#known-limitations)
|
|
45
|
-
|
|
46
|
-
## Requirements
|
|
47
|
-
|
|
48
|
-
- **Pi** (the coding agent that loads this package as an extension)
|
|
49
|
-
- **Herdr**, with the parent session running **inside** a Herdr pane
|
|
50
|
-
(`HERDR_ENV=1`). The plugin talks to herdr to create tabs/panes and start
|
|
51
|
-
child agents; it will not launch anything from a plain terminal Pi.
|
|
52
|
-
- Node.js 22+ if you are developing the package itself
|
|
53
34
|
|
|
54
|
-
|
|
55
|
-
The parent model must not either — the plugin blocks that ritual and checks
|
|
56
|
-
herdr on every launch.
|
|
35
|
+
## Architecture
|
|
57
36
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
37
|
+
```text
|
|
38
|
+
User (CEO) — root Pi session (TUI)
|
|
39
|
+
├─ pi-legion extension
|
|
40
|
+
│ ├─ LegionSupervisor (root)
|
|
41
|
+
│ │ ├─ RpcClient × N → headless `pi --mode rpc` child processes
|
|
42
|
+
│ │ ├─ mail postman (inbox delivery) + four-phase state machine
|
|
43
|
+
│ │ └─ orphan scan + child lifecycle / usage accounting
|
|
44
|
+
│ ├─ `legion.db` (one shared SQLite/WAL tree ledger)
|
|
45
|
+
│ └─ LegionEventBus → resident tree | `/legion` full view | live overlay
|
|
46
|
+
└─ centurion child (RpcClient; cwd = its subtree worktree)
|
|
47
|
+
└─ pi-legion in child mode
|
|
48
|
+
├─ LegionSupervisor for this squad
|
|
49
|
+
├─ `legion_mail` / `legion_tree` / `legion_phase` tools
|
|
50
|
+
├─ worker / scout / reviewer … children (`worktree: false`)
|
|
51
|
+
└─ nested centurions → recursive squads
|
|
62
52
|
```
|
|
63
53
|
|
|
64
|
-
|
|
54
|
+
Transport, state, decision-making, and display are separate layers:
|
|
65
55
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
56
|
+
- **Transport:** each child is a headless Pi RPC process managed by a
|
|
57
|
+
`LegionSupervisor`; a centurion can supervise another squad recursively.
|
|
58
|
+
- **State:** all nodes in the tree share the `legion.db` SQLite ledger in WAL
|
|
59
|
+
mode. Per-run `run.json` records and session files remain the resume source
|
|
60
|
+
for each child.
|
|
61
|
+
- **Decision:** centurions and squad members do the LLM work. The supervisor
|
|
62
|
+
owns machine state and validates phase transitions.
|
|
63
|
+
- **Display:** the event bus feeds the resident tree, the `/legion` full view,
|
|
64
|
+
and an on-demand live overlay.
|
|
65
|
+
|
|
66
|
+
## Quick start
|
|
69
67
|
|
|
70
|
-
|
|
68
|
+
Install and load the extension in Pi:
|
|
71
69
|
|
|
72
70
|
```bash
|
|
73
|
-
pi install
|
|
71
|
+
pi install npm:pi-legion
|
|
74
72
|
```
|
|
75
73
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
`prototype`) are available
|
|
79
|
-
with no extra files to copy — see [Bundled roles](#bundled-roles).
|
|
80
|
-
|
|
81
|
-
To update a git install: `pi update`. To switch from a path/git install to npm
|
|
82
|
-
once a release is published, remove the old package and `pi install npm:pi-legion`.
|
|
83
|
-
|
|
84
|
-
## What the plugin does for you
|
|
85
|
-
|
|
86
|
-
These used to be “please remember this in the prompt.” They are now
|
|
87
|
-
**scripts**. Neither the parent model nor the child has to cooperate for them
|
|
88
|
-
to hold.
|
|
89
|
-
|
|
90
|
-
| Concern | What happens |
|
|
91
|
-
| --- | --- |
|
|
92
|
-
| Launch | One `subagent` call creates the type tab, splits a pane, starts `pi`, and begins watching. Same agent type shares one tab (each child is a pane). |
|
|
93
|
-
| Status | Running children are painted **above the parent input** with model (`:thinking` when set), kind (if not `pi`), turn / in-flight tool, and worktree branch. Elapsed time keeps ticking after the tool call returns. Non-pi kinds (cursor, claude, …) fill the same slots from `agent get` labels / pane title when jsonl is absent. |
|
|
94
|
-
| Completion | When a turn finishes, the plugin injects `Background task completed: **name**` into the parent. If the parent is idle it wakes immediately; if it is still in a tool loop, the notice waits (`followUp`) instead of steering mid-turn. Every finish is shown in the transcript: a completed child renders as one truncated row (`✓ name · success · acceptance · size`, clamped to the live pane width), failures/stops keep the full block, and `ctrl+o` expands a collapsed row. |
|
|
95
|
-
| Recycle | Terminal children get `ctrl+d`, then pane close (and the type tab when it is empty). You do not `retire` or close panes. `blocked` is the exception — the pane stays. |
|
|
96
|
-
| Child must not ping the parent | Every child Pi loads a **child-guard** extension. `herdr agent prompt/wait/send-keys/start`, pane split, tab create/close, and reading someone else’s pane are blocked. |
|
|
97
|
-
| Task-card boilerplate | A frozen appendix is appended to every child task: no wakeup, no nested agents, end with `{"ok": true\|false, "reason": "..."}`. |
|
|
98
|
-
| Read-only roles | `acceptance.role: read-only` (scout, reviewer, …) may still have `bash` for `rg` / `git log` / `ls`, but writes (`rm`, `git commit`, `echo > file`, `npm install`, `sed -i`, …) are blocked. |
|
|
99
|
-
| Acceptance | `{"ok": true}` is only **attested**. If a required criterion lists `evidence: [verification-output]` **and** carries an explicit `command`, collect then runs that command in the child’s cwd and promotes the result to **verified** or rejects it. Without a `command` the result stays attested with the criterion surfaced as pending — the parent confirms it from the child’s pasted output; nothing is guessed. |
|
|
100
|
-
| Tool approval (`blocked`) | `onBlocked: auto-approve` (**default**; a subagent pane has no human, so an approval dialog is a deadlock) silently approves a blocked child via `send-keys y` and keeps watching. `forward` pops a **confirm** in the parent TUI (Yes → `send-keys y`, No → deny; no TUI → a notify is queued). `notify` only notifies. The default is also what cursor children launch with: `--force` (Run Everything) unless the agent sets `forward`/`notify` — set `subagents.defaultOnBlocked` in settings.json to change the default globally. |
|
|
101
|
-
| Late `collect` / `retire` | If watch already collected, `collect` returns the cached snapshot. If the pane is already gone, `retire` is a no-op that points at the session file. |
|
|
102
|
-
|
|
103
|
-
What is still a **judgment** (and must stay one):
|
|
104
|
-
|
|
105
|
-
- whether to spawn a child, which role, and what the task should say
|
|
106
|
-
- what to do with the completion (accept, `steer`, `continue`, ignore)
|
|
107
|
-
- whether a semantic checklist item like “no secrets in the diff” actually holds
|
|
108
|
-
|
|
109
|
-
## Quick start
|
|
110
|
-
|
|
111
|
-
Use the `subagent` tool. **Do not run herdr CLI or env checks first.** Do not
|
|
112
|
-
tell the child to prompt the parent.
|
|
74
|
+
Ask a `centurion` to lead a recursive squad, with a goal and concrete
|
|
75
|
+
acceptance checklist in its task:
|
|
113
76
|
|
|
114
77
|
```text
|
|
115
78
|
subagent({
|
|
116
|
-
agent: "
|
|
117
|
-
task: "
|
|
79
|
+
agent: "centurion",
|
|
80
|
+
task: "Goal: implement the account export flow. Acceptance: contract is recorded; implementation is reviewed; the verification commands pass; return a squash MR and verdict."
|
|
118
81
|
})
|
|
119
82
|
```
|
|
120
83
|
|
|
121
|
-
The
|
|
122
|
-
|
|
123
|
-
|
|
84
|
+
The centurion plans the work, writes the squad contract, dispatches ordinary
|
|
85
|
+
roles such as `worker`, `scout`, and `reviewer`, and coordinates the squad
|
|
86
|
+
through its phases. For a single leaf task, dispatch an ordinary role directly:
|
|
124
87
|
|
|
125
88
|
```text
|
|
126
|
-
subagent({ agent: "
|
|
127
|
-
subagent({ agent: "worker", task: "<hotfix>", worktree: false }) // edit this checkout
|
|
89
|
+
subagent({ agent: "scout", task: "Map the export code path and report relevant files and risks." })
|
|
128
90
|
```
|
|
129
91
|
|
|
130
|
-
|
|
92
|
+
No manual child-process launch is needed. Use `/legion` to inspect the tree;
|
|
93
|
+
see [Three-layer visualization](#three-layer-visualization) for navigation.
|
|
94
|
+
`/legion status` prints that tree in the chat, and `/legion status <name>`
|
|
95
|
+
prints one node — see [Status](#status-legion-status).
|
|
96
|
+
|
|
97
|
+
The `subagent` control actions include event-driven `wait` and graceful `stop`:
|
|
98
|
+
`subagent({ action: "wait", name: "worker-0" })` returns as soon as the child
|
|
99
|
+
settles (or the parent turn is interrupted with Esc). Aborting a parent turn
|
|
100
|
+
with Esc gracefully retires its tracked children, including those being waited
|
|
101
|
+
on or collected; the cascade is silent. Launch stays async by default. Under
|
|
102
|
+
`pi --print` or JSON mode the process would otherwise exit at the end of the
|
|
103
|
+
parent turn and retire those children before they write an assistant message.
|
|
104
|
+
That shutdown waits for each live async child to settle, bounded by the
|
|
105
|
+
child's turn timeout, and only then retires whatever is still running. A
|
|
106
|
+
child killed by that shutdown is recorded as aborted because the parent
|
|
107
|
+
session shut down, not because the model produced no reply. To include the
|
|
108
|
+
child's result in the same print turn, call `wait` or `collect` before the
|
|
109
|
+
turn ends, or launch with `async: false`. To stop children explicitly, use
|
|
110
|
+
`subagent({ action: "stop", name: "worker-0" })` or
|
|
111
|
+
`subagent({ action: "stop", all: true })`; this runs the normal graceful-retire
|
|
112
|
+
sequence for each selected child.
|
|
113
|
+
|
|
114
|
+
### Stopping async subagents yourself
|
|
115
|
+
|
|
116
|
+
Async children keep running after the main turn ends, so Esc-abort no longer
|
|
117
|
+
reaches them. Three user controls run the same graceful `stop` (a stopped
|
|
118
|
+
parent retires its own subtree on shutdown):
|
|
119
|
+
|
|
120
|
+
- **`/legion stop`** stops every live subagent; **`/legion stop <name>`** stops
|
|
121
|
+
one (`all` is an alias for no name). Names complete from the live children;
|
|
122
|
+
nested agents complete as `parent/name` and are reported as owned by their
|
|
123
|
+
parent (stop the parent to retire them).
|
|
124
|
+
- **Esc** while the main agent is idle, the editor is empty, no dialog or
|
|
125
|
+
overlay is open and subagents are live asks "Stop N running subagents?".
|
|
126
|
+
In every other state Esc keeps pi's normal behaviour.
|
|
127
|
+
- **`x`** on the selected live agent in the `/legion` view or its live
|
|
128
|
+
overlay asks `y` to confirm in place, then stops it.
|
|
129
|
+
|
|
130
|
+
While the main agent is idle and subagents are live, the railway header shows
|
|
131
|
+
a dim `esc stop all · /legion stop [name]` hint (shortened on narrow terminals,
|
|
132
|
+
hidden while the main agent is working).
|
|
133
|
+
|
|
134
|
+
### Status (`/legion status`)
|
|
135
|
+
|
|
136
|
+
The railway and `/legion` view are TUI surfaces. `/legion status` is the chat
|
|
137
|
+
snapshot of the same tree:
|
|
138
|
+
|
|
139
|
+
- `/legion status` prints every node with the same lines as `legion_tree`
|
|
140
|
+
(`id role=… status=… phase=… model=… depth=…`), plus a count of each status.
|
|
141
|
+
Live jobs that are not in the tree yet are listed under that summary.
|
|
142
|
+
- `/legion status <name>` prints one node with the same text as
|
|
143
|
+
`subagent({ action: "status", name })`. Living children lead with
|
|
144
|
+
`alive · quiet Ns` (session JSONL mtime); after ~30s of silence the line
|
|
145
|
+
becomes `alive · quiet Ns (living)` so 「活着但安静」is obvious. Run-store
|
|
146
|
+
execution/acceptance fields are labeled snapshots, and collect-deadline /
|
|
147
|
+
transient-connection reasons are omitted from the snapshot line while the
|
|
148
|
+
child is alive so they do not steal attention from quiet age. A ledger-only
|
|
149
|
+
node still uses the same renderer with the fields the tree has.
|
|
150
|
+
- Quiet age is not a heartbeat: there is no child-side progress ping. Minutes
|
|
151
|
+
of silence can still mean the model is reasoning (especially high-thinking
|
|
152
|
+
turns). Quiet alone does not mean the child is stuck; pair it with `alive`.
|
|
153
|
+
- Both forms work outside the TUI. A short name that matches more than one
|
|
154
|
+
node needs the full node id (`/legion status <node-id>`).
|
|
155
|
+
|
|
156
|
+
## Legion tools and phases
|
|
157
|
+
|
|
158
|
+
Child nodes receive these tree-aware tools:
|
|
131
159
|
|
|
132
160
|
```text
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
{ agent: "reviewer", task: "<task C>" },
|
|
137
|
-
] })
|
|
161
|
+
legion_mail({ to, subject, body, kind?, urgency? })
|
|
162
|
+
legion_tree({ scope?: "children" | "squad" | "subtree" | "tree", depth? })
|
|
163
|
+
legion_phase({ to, note?, evidence? })
|
|
138
164
|
```
|
|
139
165
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
`
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
| `worker` | Implement a frozen plan on an isolated worktree; open an MR; run tests; self-report a verdict. | **verified** via the project’s own verifier |
|
|
184
|
-
| `reviewer` | Read-only adversarial review; findings with path + severity. | attested, **read-only** bash |
|
|
185
|
-
| `advisor` | Decision counsel and final arbitration on contested options (A-vs-B rulings). Grok via cursor. | attested |
|
|
186
|
-
| `designer` | Front-end UI/visual/interaction design: design tokens, full-state matrices, and the component implementation. UI layer only — business logic stays with `worker`. | **verified** via build |
|
|
187
|
-
| `prototype` | Single-file clickable HTML prototypes to validate an interaction flow before real implementation. | attested, writer |
|
|
188
|
-
|
|
189
|
-
Bundled roles do **not** pin a vendor model. A child uses the parent session’s
|
|
190
|
-
model unless you set `subagents.defaultModel`,
|
|
191
|
-
`subagents.agentOverrides.<name>.model`, assign a named
|
|
192
|
-
[preset](#presets-kindmodelthinking), pass `model` on the tool call, or
|
|
193
|
-
[load a model profile](#model-profiles-cheap--medium--strong).
|
|
194
|
-
|
|
195
|
-
## Presets (kind+model+thinking)
|
|
196
|
-
|
|
197
|
-
Named bundles in settings, referenced from agent frontmatter (`preset: strong`),
|
|
198
|
-
from `agentOverrides.<name>.preset`, or from the tool’s `preset` param (single
|
|
199
|
-
launch and `tasks[]` both accept it):
|
|
166
|
+
- **`legion_mail`** sends asynchronous messages over permitted tree edges:
|
|
167
|
+
parent, children, or same-parent siblings. `to` may be a short name, node ID,
|
|
168
|
+
`parent`, `children`, or `squad`. Cross-squad direct mail and bypassing the
|
|
169
|
+
parent are refused; route cross-squad requests through the relevant
|
|
170
|
+
centurions. Keep large artifacts in the shared worktree and mail a file
|
|
171
|
+
pointer. The body has a 4 KB soft limit and a 32 KB hard limit.
|
|
172
|
+
- **`legion_tree`** is a read-only view of machine-recorded node state. It does
|
|
173
|
+
not interrupt the nodes being observed.
|
|
174
|
+
- **`legion_phase`** is available to centurions. It requests a state transition;
|
|
175
|
+
the supervisor checks the transition's invariants and records the result.
|
|
176
|
+
|
|
177
|
+
A squad moves through **planning → implementing → reviewing → verifying**.
|
|
178
|
+
Planning produces a non-empty `.pi-legion/contract.md` with file ownership
|
|
179
|
+
and acceptance commands. During reviewing, an independent reviewer supplies
|
|
180
|
+
the judgment; in verifying, the supervisor runs the contract's `legion-verify`
|
|
181
|
+
commands without an LLM. Passing verification advances the squad to `done`.
|
|
182
|
+
|
|
183
|
+
## Three-layer visualization
|
|
184
|
+
|
|
185
|
+
1. **Railway strip above the input:** one git-graph lane per live subagent
|
|
186
|
+
(max 4, 3 below 80 columns; oldest/shallowest first, `+N` for the rest).
|
|
187
|
+
When an agent ends its lane merges back and lower lanes shift up. The
|
|
188
|
+
header shows the live count and tok/s (trailing 60s average). Hidden when
|
|
189
|
+
no subagent is live. Set `PI_LEGION_STRIP=tree` for the classic roster.
|
|
190
|
+
2. **`/legion` full view:** galaxy graph of the agent tree, stacked tok/s
|
|
191
|
+
chart by agent type (peak marker, now/avg/peak/total, per-role share), and
|
|
192
|
+
a scrollable `log --graph` history. Tab switches panes, arrows scroll or
|
|
193
|
+
move the selection, Enter selects an agent (its own rate line, tok/s,
|
|
194
|
+
tokens and share are overlaid), `o` opens its live overlay, `x` stops it
|
|
195
|
+
(confirm with `y`), Esc clears the
|
|
196
|
+
selection, `q`/Esc closes. Layouts: wide (≥140 cols), stacked, and portrait
|
|
197
|
+
(<80 cols, or tall and <100 cols). `/legion --tree` opens the classic tree
|
|
198
|
+
(`j`/`k`, Enter/Space fold, `/` search, PageUp/PageDown).
|
|
199
|
+
3. **Live overlay:** press `o` on the selected node, use a component-routed
|
|
200
|
+
mouse click in the strip or classic tree, or open it directly with `/legion <name>`.
|
|
201
|
+
The overlay subscribes to that node's live feed only while open; Esc closes
|
|
202
|
+
it (returning to `/legion` when opened from there) and unsubscribes; `x`
|
|
203
|
+
stops the agent after a `y` confirmation. OSC 8 links are visual anchors only, not overlay links.
|
|
204
|
+
|
|
205
|
+
## Settings and budget gates
|
|
206
|
+
|
|
207
|
+
Add the v2 `legion` block to `~/.pi/agent/settings.json` or the project
|
|
208
|
+
`.pi/settings.json`, merging it with any existing `subagents` keys:
|
|
200
209
|
|
|
201
210
|
```json
|
|
202
211
|
{
|
|
203
212
|
"subagents": {
|
|
204
|
-
"
|
|
205
|
-
"
|
|
206
|
-
"
|
|
207
|
-
"
|
|
208
|
-
"
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
flash for implementation (same-card benchmarks had it out-coding flagships at
|
|
219
|
-
1/20 the price), a reasoning-strong model for adversarial review, and grok for
|
|
220
|
-
decision arbitration.
|
|
221
|
-
|
|
222
|
-
Model precedence, strongest first:
|
|
223
|
-
|
|
224
|
-
```text
|
|
225
|
-
per-run tool `model` (beats the preset's MODEL, but the preset's KIND still applies)
|
|
226
|
-
→ preset kind / model / thinking
|
|
227
|
-
→ agentOverridesByProvider.<provider>.<name>
|
|
228
|
-
→ agentOverrides.<name>
|
|
229
|
-
→ agent frontmatter `model`
|
|
230
|
-
→ subagents.defaultModel
|
|
231
|
-
→ the dispatching (parent) session model
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
A preset deliberately beats `agentOverrides` — that is the point of the level:
|
|
235
|
-
profiles pin roles via `agentOverrides` (`loadCatalog` folds them into
|
|
236
|
-
`agent.model` before anything resolves), so a frontmatter-adjacent preset would
|
|
237
|
-
be unreachable. Two guarantees:
|
|
238
|
-
|
|
239
|
-
- **Atomic.** The preset's `kind` and the model the child is *actually launched
|
|
240
|
-
with* are checked together. Whichever level supplied the model — the preset
|
|
241
|
-
itself, a per-run tool `model`, `agentOverrides`, `defaultModel`, the parent
|
|
242
|
-
session — a pair the kind cannot accept is refused loudly instead of being
|
|
243
|
-
silently dropped at start (`kind: cursor` with a pi-shaped `provider/id` is
|
|
244
|
-
the canonical case).
|
|
245
|
-
- **Loud.** Referencing an undefined preset is an error naming the defined
|
|
246
|
-
presets — never a silent fallback to the parent model.
|
|
247
|
-
|
|
248
|
-
Agents with no `preset` resolve exactly as before.
|
|
249
|
-
|
|
250
|
-
Precedence, lowest to highest — later layers override earlier ones by name:
|
|
251
|
-
|
|
252
|
-
```text
|
|
253
|
-
builtin (shipped) < extra dirs < ~/.pi/agent/agents < <project>/.pi/agents
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
Shadow any bundled role by putting a same-name `*.md` in `~/.pi/agent/agents`
|
|
257
|
-
or `<repo>/.pi/agents`.
|
|
258
|
-
|
|
259
|
-
- `subagents.disableBuiltins: true` drops the bundled layer entirely.
|
|
260
|
-
- `PI_HERDR_SUBAGENTS_EXTRA_AGENT_DIRS` (PATH-style) injects extra read-only
|
|
261
|
-
role directories — Nix store, container, read-only mount.
|
|
262
|
-
- `subagent({ action: "list" })` shows every role with its provenance tag.
|
|
263
|
-
|
|
264
|
-
## Model profiles (cheap / medium / strong)
|
|
265
|
-
|
|
266
|
-
Same workflow as pi-subagents: classify a provider’s models into three
|
|
267
|
-
capability tiers, then bind roles to those tiers so you do not have to change
|
|
268
|
-
the parent session model every time.
|
|
269
|
-
|
|
270
|
-
| Tier | Roles |
|
|
271
|
-
| --- | --- |
|
|
272
|
-
| cheap | `scout`, `prototype` |
|
|
273
|
-
| medium | `planner` |
|
|
274
|
-
| strong | `worker`, `reviewer`, `designer` |
|
|
275
|
-
|
|
276
|
-
(`advisor` is cursor-kind and carries its own model, so it never takes a pi-tier override.)
|
|
277
|
-
|
|
278
|
-
```text
|
|
279
|
-
/subagents-refresh-provider-models <provider>
|
|
280
|
-
/subagents-generate-profiles <provider>
|
|
281
|
-
/subagents-load-profile <provider>.quota
|
|
282
|
-
```
|
|
283
|
-
|
|
284
|
-
That writes two profiles (`<provider>.quota` leans cheaper,
|
|
285
|
-
`<provider>.quality` leans stronger) under
|
|
286
|
-
`~/.pi/agent/profiles/pi-legion/` and, on load, copies
|
|
287
|
-
`agentOverrides` into `~/.pi/agent/settings.json`. Project `.pi/settings.json`
|
|
288
|
-
still wins on overlapping keys.
|
|
289
|
-
|
|
290
|
-
Other commands: `/subagents-profiles` lists saved profiles;
|
|
291
|
-
`/subagents-check-profile <name>` re-checks each assigned model against the
|
|
292
|
-
current registry (and a live probe unless you pass `--no-probe`). Refresh and
|
|
293
|
-
generate accept `--force` and `--no-probe`.
|
|
294
|
-
|
|
295
|
-
`/subagents-agents [user|project|both]` lists the roles available to the
|
|
296
|
-
`subagent` tool in the CURRENT session — the human-facing version of the
|
|
297
|
-
roster injected into the model's system prompt, plus what a launch would
|
|
298
|
-
actually resolve:
|
|
299
|
-
|
|
300
|
-
```text
|
|
301
|
-
builtin (7)
|
|
302
|
-
advisor
|
|
303
|
-
决策顾问:……
|
|
304
|
-
kind=cursor · alias: ask, consult, oracle · model: (agent CLI default)
|
|
305
|
-
file: /pkg/agents/advisor.md
|
|
306
|
-
scout
|
|
307
|
-
侦察员:……
|
|
308
|
-
model: cb/glm-5.3 (agentOverrides)
|
|
309
|
-
file: ~/.pi/agent/agents/scout.md
|
|
310
|
-
project (1)
|
|
311
|
-
deploy
|
|
312
|
-
deploys things
|
|
313
|
-
model: cb/kimi-k3 (the parent session model)
|
|
314
|
-
file: <repo>/.pi/agents/deploy.md
|
|
315
|
-
|
|
316
|
-
Directories
|
|
317
|
-
builtin: /pkg/agents
|
|
318
|
-
user: ~/.pi/agent/agents
|
|
319
|
-
project: /repo/.pi/agents
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
Each role shows its layer (`builtin` / `user` / `project`), the model a launch
|
|
323
|
-
would actually resolve — with the same provenance labels and refusal checks
|
|
324
|
-
the launch path uses (`the agent's frontmatter`, `agentOverrides`, `the
|
|
325
|
-
preset`, `the parent session model`, …; `✗ outside model scope`, `✗ cannot
|
|
326
|
-
be used with kind 'cursor'`, or `dropped at launch` for a model the target
|
|
327
|
-
CLI cannot express) — plus kind, aliases, and the definition file, so a role
|
|
328
|
-
that is not doing what you expect can be traced to the exact file that
|
|
329
|
-
defines it in one step. The optional scope argument mirrors the tool's
|
|
330
|
-
`agentScope` (`user` by default; `project` skips the user layers, the bundled
|
|
331
|
-
roles always load); it re-reads agent files on every invocation, so edits
|
|
332
|
-
show up immediately.
|
|
333
|
-
|
|
334
|
-
`/subagents-summary` renders a Markdown table of the current parent pane's
|
|
335
|
-
subagent runs, including role outcomes, turns, token usage, cost, and cumulative
|
|
336
|
-
agent-time. Use `/subagents-summary --all` to include runs from every parent
|
|
337
|
-
pane in the current working directory, or `/subagents-summary <name>` for one
|
|
338
|
-
child's detailed state, timing, usage, and session path. Child names are
|
|
339
|
-
available through command completion.
|
|
340
|
-
|
|
341
|
-
you can still hand-write the same mapping:
|
|
342
|
-
|
|
343
|
-
```json
|
|
344
|
-
{
|
|
345
|
-
"subagents": {
|
|
346
|
-
"agentOverrides": {
|
|
347
|
-
"scout": { "model": "your-provider/fast-model" },
|
|
348
|
-
"planner": { "model": "your-provider/mid-model" },
|
|
349
|
-
"worker": { "model": "your-provider/strong-coder" },
|
|
350
|
-
"reviewer": { "model": "your-provider/strong-reasoner" },
|
|
351
|
-
"designer": { "model": "your-provider/strong-model" }
|
|
213
|
+
"legion": {
|
|
214
|
+
"maxDepth": 4,
|
|
215
|
+
"maxChildrenPerNode": 8,
|
|
216
|
+
"maxActiveNodes": 30,
|
|
217
|
+
"phaseTimeouts": {
|
|
218
|
+
"planning": 900000,
|
|
219
|
+
"implementing": 3600000,
|
|
220
|
+
"reviewing": 1800000,
|
|
221
|
+
"verifying": 900000
|
|
222
|
+
},
|
|
223
|
+
"verifyCommandTimeoutMs": 600000,
|
|
224
|
+
"mailRatePer5Min": 10,
|
|
225
|
+
"usagePollMs": 30000,
|
|
226
|
+
"orphanStaleMs": 90000
|
|
352
227
|
}
|
|
353
228
|
}
|
|
354
229
|
}
|
|
355
230
|
```
|
|
356
231
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
Two levels, both temporary-by-design:
|
|
360
|
-
|
|
361
|
-
- **Session** — `/subagents-toggle` flips the switch for the current session
|
|
362
|
-
only. No files change; `/reload` restores whatever the settings say.
|
|
363
|
-
`/subagents-toggle` (no args) shows the current state; `on`/`off` are
|
|
364
|
-
explicit; the status line also warns how many children are still running.
|
|
365
|
-
- **Project / user settings** — `"subagents": { "enabled": false }` in
|
|
366
|
-
`<repo>/.pi/settings.json` (project wins over `~/.pi/agent/settings.json`)
|
|
367
|
-
persists the off state across sessions.
|
|
232
|
+
The three launch budget gates are enforced against the tree ledger:
|
|
368
233
|
|
|
369
|
-
|
|
234
|
+
| Gate | Setting | Default | Limit |
|
|
235
|
+
| --- | --- | ---: | --- |
|
|
236
|
+
| Depth | `maxDepth` | 4 | Maximum tree depth; the hard cap is 4 (`MAX_NESTED_PATH_ENTRIES`). |
|
|
237
|
+
| Fan-out | `maxChildrenPerNode` | 8 | Direct children for one node. |
|
|
238
|
+
| Concurrency | `maxActiveNodes` | 30 | Whole-tree `running` + `starting` nodes. |
|
|
370
239
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
and `status`/`collect`/`wait`/`resume`/`steer` keep working, so a disable
|
|
378
|
-
mid-flight stays observable.
|
|
240
|
+
The effective tree depth cap is bounded by the operator override,
|
|
241
|
+
`subagents.legion.maxDepth`, and the hard cap of 4. **INCL semantics:** a
|
|
242
|
+
launched role's `maxSubagentDepth = R` counts generations in that role's
|
|
243
|
+
subtree, including itself; when launched by a parent at depth `d`, it inherits
|
|
244
|
+
`C_child = min(C_parent, d + R, 4)` (`R = 1` makes it a leaf), so it can tighten
|
|
245
|
+
the inherited cap but cannot widen it.
|
|
379
246
|
|
|
380
|
-
|
|
247
|
+
### Scout guard
|
|
381
248
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
> turn settles — the label is the visible "waiting on subagents" cue in the
|
|
389
|
-
> sidebar. No setting; automatic when running inside herdr.
|
|
249
|
+
`subagents.scoutGuard` watches the main session. After `threshold` consecutive
|
|
250
|
+
read-like tool calls with no `subagent` launch or `vexillatio` call in
|
|
251
|
+
between, it injects one follow-up reminder and then waits out `cooldownMs`.
|
|
252
|
+
It is on by default. Set it in `~/.pi/agent/settings.json` or the project
|
|
253
|
+
`.pi/settings.json`; a project file overrides the same fields from the user
|
|
254
|
+
file.
|
|
390
255
|
|
|
391
256
|
```json
|
|
392
257
|
{
|
|
393
258
|
"subagents": {
|
|
394
|
-
"
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
"frontend": {
|
|
399
|
-
"description": "UI work",
|
|
400
|
-
"members": ["designer", "prototype", { "agent": "worker", "model": "provider/strong" }]
|
|
401
|
-
}
|
|
402
|
-
},
|
|
403
|
-
"disableBuiltins": false,
|
|
404
|
-
"maxSubagentSpawnsPerSession": 8,
|
|
405
|
-
"agentOverrides": {
|
|
406
|
-
"worker": { "model": "provider/id", "thinking": "medium" }
|
|
407
|
-
},
|
|
408
|
-
"agentOverridesByProvider": {
|
|
409
|
-
"cb": { "worker": { "model": "cb/glm-5.3" } }
|
|
410
|
-
},
|
|
411
|
-
"modelScope": { "allow": ["cb/*", "openai/*"] },
|
|
412
|
-
"herdr": {
|
|
413
|
-
"defaultPlacement": "split-down",
|
|
414
|
-
"maxConcurrentAgents": 6,
|
|
415
|
-
"startRetries": 40,
|
|
416
|
-
"startRetryBackoffMs": 150,
|
|
417
|
-
"sessionRetentionDays": 7
|
|
259
|
+
"scoutGuard": {
|
|
260
|
+
"enabled": true,
|
|
261
|
+
"threshold": 8,
|
|
262
|
+
"cooldownMs": 120000
|
|
418
263
|
}
|
|
419
264
|
}
|
|
420
265
|
}
|
|
421
266
|
```
|
|
422
267
|
|
|
423
|
-
|
|
|
268
|
+
| Field | Default | Meaning |
|
|
269
|
+
| --- | ---: | --- |
|
|
270
|
+
| `enabled` | `true` | Count and remind. `false` skips the guard until it is turned back on. |
|
|
271
|
+
| `threshold` | `8` | Consecutive read-like calls before one reminder. Positive integer. |
|
|
272
|
+
| `cooldownMs` | `120000` | Quiet period after a reminder, in milliseconds. Integer ≥ 0. |
|
|
273
|
+
|
|
274
|
+
`/scout-guard` turns the guard on for this session and `/no-scout-guard`
|
|
275
|
+
turns it off. Both override `enabled` in memory only; settings files stay
|
|
276
|
+
unchanged. Child sessions do not register these commands. Behavior in full
|
|
277
|
+
is in [docs/design.md §6.4.1](docs/design.md#641-scout-guard-subagentsscoutguard).
|
|
278
|
+
|
|
279
|
+
### Team 配置
|
|
280
|
+
|
|
281
|
+
Use `subagents.team` to select the default named team from `subagents.teams`.
|
|
282
|
+
The `PI_SUBAGENTS_TEAM` environment variable takes precedence over
|
|
283
|
+
`subagents.team`; if neither selects a named team, the full discovered roster
|
|
284
|
+
is used. Team definitions have a description and an ordered `members` list;
|
|
285
|
+
each member can be a role-name string or an object with an `agent` name and
|
|
286
|
+
per-member overrides. Object member overrides (for example `model` or
|
|
287
|
+
`thinking`) take precedence over those fields in the role card's frontmatter;
|
|
288
|
+
omitted override fields continue to use their normal role/configuration
|
|
289
|
+
values.
|
|
290
|
+
|
|
291
|
+
See [docs/subagents-teams.md](docs/subagents-teams.md) for four editable
|
|
292
|
+
frontend/backend × premium/value team templates. The model IDs in that template
|
|
293
|
+
are placeholders for your provider, not required defaults.
|
|
294
|
+
|
|
295
|
+
### Parent/child context conventions
|
|
296
|
+
|
|
297
|
+
Settings can provide `subagents.parentContext` and `subagents.childContext`
|
|
298
|
+
inline or through `@path` references. If either setting is omitted, Legion
|
|
299
|
+
looks for `subagents-parent-context.md` or `subagents-child-context.md`
|
|
300
|
+
respectively in the directory containing that settings file. These are
|
|
301
|
+
convention-based fallbacks: an explicit setting takes precedence, and setting
|
|
302
|
+
either field to `false` disables its context. The convention filenames are
|
|
303
|
+
`PARENT_CONTEXT_CONVENTION` and `CHILD_CONTEXT_CONVENTION` in
|
|
304
|
+
[`src/agents/settings.ts`](src/agents/settings.ts#L30-L31); missing convention
|
|
305
|
+
files simply contribute no context. The repository examples are
|
|
306
|
+
[`examples/subagents-parent-context.md`](examples/subagents-parent-context.md)
|
|
307
|
+
and [`examples/subagents-child-context.md`](examples/subagents-child-context.md).
|
|
308
|
+
|
|
309
|
+
### Self-diagnosis (`/legion doctor`)
|
|
310
|
+
|
|
311
|
+
When something about subagents misbehaves (model not found, spawn exit, memory
|
|
312
|
+
gate, missing lens tools, orphan temps, …), ask Legion rather than guessing —
|
|
313
|
+
same idea as `pi list` / `pi auth check`.
|
|
314
|
+
|
|
315
|
+
- `/legion doctor` — full report in the chat (works outside TUI too).
|
|
316
|
+
- `/legion doctor [section]` — one of: `summary`, `extensions`, `models`,
|
|
317
|
+
`spawn`, `memory`, `tools`, `storage`, `runtime`, `next`.
|
|
318
|
+
- `subagent({ action: "diagnose" })` — same full report as tool output so the
|
|
319
|
+
main agent can call it without waiting for you. The tool has no section
|
|
320
|
+
field; filter with `/legion doctor [section]`.
|
|
321
|
+
|
|
322
|
+
The report is structured for the model: each section ends in **Issues** /
|
|
323
|
+
**Next steps** with copy-pasteable fixes (env exports, role frontmatter,
|
|
324
|
+
`/legion stop`, …). Spawn failures that look like a missing model also append
|
|
325
|
+
a short Hint automatically. **Gated auto-doctor:** on model-not-found,
|
|
326
|
+
`INSUFFICIENT_MEMORY`, extension load failures, and child exit during
|
|
327
|
+
startup / first prompt, the tool error itself includes a focused doctor
|
|
328
|
+
snippet (relevant sections + Next steps) so the main agent can self-fix
|
|
329
|
+
without anyone typing `/legion doctor`. Full `/legion doctor` remains for
|
|
330
|
+
manual deep dives.
|
|
331
|
+
|
|
332
|
+
Coverage:
|
|
333
|
+
|
|
334
|
+
| Section | What it checks |
|
|
424
335
|
| --- | --- |
|
|
425
|
-
| `
|
|
426
|
-
|
|
|
427
|
-
|
|
|
428
|
-
|
|
|
429
|
-
|
|
|
430
|
-
| `
|
|
431
|
-
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
a missing, unreadable, or whitespace-only convention file is silently ignored.
|
|
448
|
-
|
|
449
|
-
Within one settings layer, explicit inline text or `@path` references (including
|
|
450
|
-
arrays) take precedence. `false` disables that layer's context and suppresses
|
|
451
|
-
its convention file; otherwise the convention file is the default. Without any
|
|
452
|
-
resolved context, that layer contributes no value. Existing cross-layer
|
|
453
|
-
precedence is unchanged: a project context value—including one from a project
|
|
454
|
-
convention file—replaces the user context wholesale, while a layer with no
|
|
455
|
-
value leaves the other layer's context intact. Sharing these named files with
|
|
456
|
-
a project means machines do not need absolute context paths in `settings.json`.
|
|
457
|
-
|
|
458
|
-
Run records and child session jsonl live under `<cwd>/.pi-subagents/`. That
|
|
459
|
-
directory is the resume credential; do not delete it while you still want to
|
|
460
|
-
`resume` a child.
|
|
461
|
-
|
|
462
|
-
## Teams
|
|
463
|
-
|
|
464
|
-
Teams select a named subset of discovered roles without changing the role files:
|
|
465
|
-
|
|
466
|
-
```json
|
|
467
|
-
{
|
|
468
|
-
"subagents": {
|
|
469
|
-
"teams": {
|
|
470
|
-
"frontend": {
|
|
471
|
-
"description": "UI work",
|
|
472
|
-
"members": [
|
|
473
|
-
"designer",
|
|
474
|
-
"prototype",
|
|
475
|
-
{ "agent": "worker", "model": "provider/strong" }
|
|
476
|
-
]
|
|
477
|
-
},
|
|
478
|
-
"full+": { "members": ["*", { "agent": "worker", "model": "provider/strong" }] }
|
|
479
|
-
},
|
|
480
|
-
"team": "frontend"
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
```
|
|
484
|
-
|
|
485
|
-
Use `/subagents-team` to show the active team, `/subagents-team list` to list
|
|
486
|
-
teams, `/subagents-team use <name>` to select one in project settings, and add
|
|
487
|
-
`--global` to write user settings. `/subagents-team create <name> a,b,c`
|
|
488
|
-
creates a team. The selection priority is
|
|
489
|
-
`PI_SUBAGENTS_TEAM` > `subagents.team` > `default`; whitespace-only environment
|
|
490
|
-
values are ignored. `default` means all discovered roles and returns the
|
|
491
|
-
unfiltered catalog. A missing selected team also falls back to all roles with a
|
|
492
|
-
warning.
|
|
336
|
+
| extensions | Host `packages`, resolved `--extension` paths, `--no-extensions` / inherit knobs, per-role effective extension list |
|
|
337
|
+
| models | Host vs child model lists, which package restores providers, team/roster gaps |
|
|
338
|
+
| spawn | Sample child argv, recent spawn failures + stderr, nested-spawn flags |
|
|
339
|
+
| memory | Free RAM, memory gate, child V8 flags, verify concurrency slots in use |
|
|
340
|
+
| tools | `/legion` slash list (status, team, agents, summary, on/off, scout-guard, doctor, stop), idle-Esc / strip hints, tool names |
|
|
341
|
+
| storage | `legion.db` sizes, orphan `rpc-child-*` temps, effective hygiene limits (default vs env), last sweep marker plus a hint when it is missing or stale |
|
|
342
|
+
| runtime | Live children, tree statuses, acceptance-ish / active nodes |
|
|
343
|
+
|
|
344
|
+
### Child extensions
|
|
345
|
+
|
|
346
|
+
Children start with `--no-extensions`, so they do not load the host's
|
|
347
|
+
discovered extensions (settings `packages`, `~/.pi/agent/extensions`, project
|
|
348
|
+
extensions). Each extension a child loads is a memory cost multiplied by every
|
|
349
|
+
concurrent child; an LSP stack is ~1 GB (#43). A child still loads pi-legion
|
|
350
|
+
itself (guard and nested spawning) and every path listed in the role's
|
|
351
|
+
`extensions` / `subagentOnlyExtensions`.
|
|
352
|
+
|
|
353
|
+
| Knob | Effect |
|
|
354
|
+
| --- | --- |
|
|
355
|
+
| role frontmatter `inheritExtensions: true` | That role loads all host extensions, as before. |
|
|
356
|
+
| `PI_LEGION_CHILD_INHERIT_EXTENSIONS=1` | Every child loads all host extensions, as before. |
|
|
357
|
+
| `PI_LEGION_CHILD_EXTENSIONS=/a.ts,/b.ts` | Extra extension paths every child loads explicitly, e.g. a provider extension the child's model needs. |
|
|
493
358
|
|
|
494
|
-
|
|
495
|
-
preserving first-seen order. An object member reuses the normal
|
|
496
|
-
`agentOverrides` fields (including `model`, `thinking`, `kind`, and `disabled`);
|
|
497
|
-
its fields are applied after earlier members, so later objects win. The active
|
|
498
|
-
team name is injected into child pane/tab processes as `PI_SUBAGENTS_TEAM` for
|
|
499
|
-
non-default teams, so nested children inherit the same selection.
|
|
359
|
+
Bundled planner, reviewer, and worker do not list `lens_*` tools. pi-lens starts an LSP stack (~1 GB per child, #43) and is not part of the default `--no-extensions` child. To give a role those tools, add the tool names to its `tools:` list and point `extensions:` / `subagentOnlyExtensions` (or `PI_LEGION_CHILD_EXTENSIONS`) at the pi-lens extension file. `/legion doctor` reports `ROLE_MISSING_LENS` when a role lists the tools without that path.
|
|
500
360
|
|
|
501
|
-
|
|
361
|
+
### Extra agent directories
|
|
502
362
|
|
|
503
|
-
|
|
363
|
+
PATH-style directories scanned for agent `*.md` definitions. They load as
|
|
364
|
+
user-level, **below** the real user agents dir in precedence (a same-name file
|
|
365
|
+
in `~/.pi/agent/agents` still wins). Useful for hermetic installs (Nix store,
|
|
366
|
+
container, read-only mount).
|
|
504
367
|
|
|
505
|
-
|
|
368
|
+
| Env | Effect |
|
|
369
|
+
| --- | --- |
|
|
370
|
+
| `PI_LEGION_EXTRA_AGENT_DIRS` | Canonical name. Colon-/semicolon-separated absolute dirs (`path.delimiter`). |
|
|
371
|
+
| `PI_HERDR_SUBAGENTS_EXTRA_AGENT_DIRS` | Deprecated compatibility alias (kept for at least one minor version). If **both** are set, `PI_LEGION_EXTRA_AGENT_DIRS` wins and the legacy value is ignored (one stderr warning). |
|
|
372
|
+
|
|
373
|
+
### Child memory controls
|
|
374
|
+
|
|
375
|
+
| Env | Default | Effect |
|
|
376
|
+
| --- | ---: | --- |
|
|
377
|
+
| `PI_LEGION_CHILD_SEMI_SPACE_MB` | 1 | Child pi `--max-semi-space-size` (via `NODE_OPTIONS`). `0` = off. |
|
|
378
|
+
| `PI_LEGION_CHILD_MAX_OLD_SPACE_MB` | 1024 | Soft child heap cap `--max-old-space-size`. `0` = off. A host `NODE_OPTIONS` value wins. |
|
|
379
|
+
| `PI_LEGION_MIN_FREE_MEM_MB` | 1024 | Memory gate: a spawn is refused (`INSUFFICIENT_MEMORY`, plus a UI notice) when available memory minus reservations falls below this. `0` = off. |
|
|
380
|
+
| `PI_LEGION_CHILD_RESERVE_MB` | 160 | Per-child reservation for children still starting, so a parallel burst cannot all pass one reading. |
|
|
381
|
+
| `PI_LEGION_VERIFY_CONCURRENCY` | 2 | Machine-wide limit on concurrent acceptance / legion-verify commands, using slot lock files. `0` = unlimited. |
|
|
382
|
+
| `PI_LEGION_VERIFY_QUEUE_MAX_MS` | 1800000 | Overall queue-wait cap. After it expires the command runs without a slot. Queue wait does not count toward the command timeout. |
|
|
383
|
+
| `PI_LEGION_VERIFY_LOCK_DIR` | `$TMPDIR/pi-legion-verify-<uid>` | Slot directory. Set it to scope the limit more narrowly. |
|
|
384
|
+
|
|
385
|
+
The child restores the host `NODE_OPTIONS` when its extension loads, so bash
|
|
386
|
+
commands it runs (`tsc`, `npm test`) keep the default heap.
|
|
387
|
+
|
|
388
|
+
### Storage hygiene
|
|
389
|
+
|
|
390
|
+
Each root pi session runs one deferred, best-effort sweep of `.pi-subagents/`
|
|
391
|
+
at startup. A step only touches state whose owner process is gone: runs and
|
|
392
|
+
`rpc-child-*` temp dirs record the owner pid (`owner.json` / `.owner`). Set any
|
|
393
|
+
limit to `0` to disable that step.
|
|
394
|
+
|
|
395
|
+
| Env | Default | Effect |
|
|
396
|
+
| --- | ---: | --- |
|
|
397
|
+
| `PI_LEGION_HYGIENE` | on | `0`/`off` disables the whole sweep. |
|
|
398
|
+
| `PI_LEGION_RUN_RETENTION_DAYS` | 14 | Delete run dirs idle this long. Runs with a `worktrees/` entry are kept as resume credentials. |
|
|
399
|
+
| `PI_LEGION_STALE_NODE_HOURS` | 72 | Non-root ledger nodes still `starting`/`running`/`blocked` this long after their owner died are marked `failed` (`node_abandoned` event). |
|
|
400
|
+
| `PI_LEGION_STALE_TEMP_HOURS` | 24 | Age threshold for legacy `rpc-child-*` dirs without an owner marker. |
|
|
401
|
+
| `PI_LEGION_EVENT_RETENTION_DAYS` | 30 | Drop ledger events of settled/failed nodes older than this (`orphan_detected`, `budget_refused`, `node_abandoned` are kept). |
|
|
402
|
+
| `PI_LEGION_EVENT_MAX_ROWS` | 200000 | Soft cap on the events table. Oldest terminal-node rows are deleted first. |
|
|
403
|
+
|
|
404
|
+
The live `legion.db` gets event retention and a `wal_checkpoint(TRUNCATE)` on
|
|
405
|
+
session shutdown. A large delete is followed by `VACUUM`, which is skipped when
|
|
406
|
+
another connection holds the db.
|
|
407
|
+
|
|
408
|
+
## Delivery and resume
|
|
409
|
+
|
|
410
|
+
A centurion owns a Git worktree for its subtree; its worker/scout/reviewer
|
|
411
|
+
children work in that subtree, and a nested centurion gets a nested worktree.
|
|
412
|
+
Each subtree is delivered as a squash MR to its parent level. MRs move upward
|
|
413
|
+
through the tree until the root reviews and merges into the default branch.
|
|
414
|
+
|
|
415
|
+
Resuming follows the tree's saved sessions and node records. If a supervisor
|
|
416
|
+
process ends, its child processes do not become detached workers; resuming the
|
|
417
|
+
parent restores the squad, while already-settled children are not restarted.
|
|
418
|
+
Keep the run records and session files while the tree may need to resume.
|
|
419
|
+
|
|
420
|
+
## Herdr transition and role migration
|
|
421
|
+
|
|
422
|
+
Version **0.16.x is the final Herdr-era line**. Pi Legion 2.x is RPC-only:
|
|
423
|
+
child execution uses headless `pi --mode rpc` processes, and the legacy backend
|
|
424
|
+
is archived under `src/backends/legacy/`, not offered as a v2 runtime option.
|
|
425
|
+
Pin to `pi-legion` 0.16.x if a workflow must stay on the former Herdr-based
|
|
426
|
+
execution model. A short breaking-change summary lives in
|
|
427
|
+
[CHANGELOG.md](CHANGELOG.md#200---2026-10-14).
|
|
428
|
+
|
|
429
|
+
Pi Legion 2.x supports `kind: pi` only. Migrate a custom role that uses another
|
|
430
|
+
CLI kind by changing its frontmatter, for example:
|
|
431
|
+
|
|
432
|
+
```yaml
|
|
506
433
|
---
|
|
507
|
-
name:
|
|
508
|
-
description: Read-only paper/code survey
|
|
509
|
-
tools: read, grep, find, ls, bash
|
|
510
|
-
timeoutMs: 600000
|
|
511
|
-
acceptance:
|
|
512
|
-
level: attested
|
|
513
|
-
role: read-only
|
|
434
|
+
name: search
|
|
514
435
|
kind: pi
|
|
515
|
-
placement: split-down
|
|
516
|
-
steer: true
|
|
517
|
-
onBlocked: forward
|
|
518
436
|
---
|
|
519
|
-
|
|
520
|
-
You are a read-only researcher. Facts with paths. No edits.
|
|
521
437
|
```
|
|
522
438
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
`description`. Useful optional fields: `model`, `preset`, `thinking`, `tools`, `skills`,
|
|
526
|
-
`timeoutMs`, `placement`, `onBlocked` (`auto-approve` default / `forward` / `notify`),
|
|
527
|
-
`acceptance.role` (`read-only` / `writer`), `acceptance.criteria` with
|
|
528
|
-
`evidence: [verification-output]` (and optional `command:`) if collect should
|
|
529
|
-
run a check, `worktree`, `toolBudget` / `turnBudget` / `toolTimeoutMs`,
|
|
530
|
-
`fallbackModels`, `alias`, `completionGuard`, `allowNestedSubagents`.
|
|
531
|
-
|
|
532
|
-
If `subagent({ action: "list" })` prints `⚠ not enforced yet: …`, that key is
|
|
533
|
-
parsed but inert. A listed key that does nothing is worse than an unknown key
|
|
534
|
-
— if you see that warning, the field is decoration. The shipped roles currently
|
|
535
|
-
report none.
|
|
536
|
-
|
|
537
|
-
## Isolation
|
|
538
|
-
|
|
539
|
-
Herdr panes are **one trust domain**. A child can in principle `herdr pane
|
|
540
|
-
read` any pane in the session (measured: F23). We now **enforce** a subset of
|
|
541
|
-
that discipline; the rest is still on you.
|
|
542
|
-
|
|
543
|
-
**Enforced in the child process**
|
|
544
|
-
|
|
545
|
-
- `herdr agent prompt|wait|send-keys|start`
|
|
546
|
-
- `herdr pane split`, `herdr tab create|close`
|
|
547
|
-
- `herdr pane read|close` of a pane that is not this child’s
|
|
548
|
-
- for `acceptance.role: read-only`: filesystem writes, `git` mutations,
|
|
549
|
-
package-manager installs, `sed -i`, redirects onto files
|
|
550
|
-
- `toolBudget.maxToolCalls` / `turnBudget.maxTurns` (further tool calls are
|
|
551
|
-
blocked); `toolTimeoutMs` wraps bash with GNU `timeout`
|
|
552
|
-
- `worktree: true` → `git worktree add -b` under the run dir (named
|
|
553
|
-
`pi-subagent/<name>-…` branch). The pane’s cwd is that tree. The **parent**
|
|
554
|
-
decides per launch (`subagent({ worktree: true|false })`); omitting it uses
|
|
555
|
-
the role default. Writer roles (`acceptance.role: writer`, including bundled
|
|
556
|
-
`worker`) default this on so concurrent parent Pis do not share a dirty
|
|
557
|
-
checkout; the child commits on the branch and opens an MR. `worktree: false`
|
|
558
|
-
opts out. Retire leaves the tree on disk. Requires a git repository.
|
|
559
|
-
- child tabs are pinned to the **parent herdr Space**
|
|
560
|
-
(`HERDR_WORKSPACE_ID` → `tab create --workspace`). A focused Space elsewhere
|
|
561
|
-
cannot steal the child, and adopt will not reuse a same-label tab in another
|
|
562
|
-
Space.
|
|
563
|
-
|
|
564
|
-
**Still not a sandbox**
|
|
565
|
-
|
|
566
|
-
- `HERDR_SOCKET_PATH` cannot be overridden per pane (F24)
|
|
567
|
-
- a true isolated session would break the visible/steerable model (F25)
|
|
568
|
-
- never put secrets on screen in any pane while children run
|
|
569
|
-
- owner tokens: recycle only panes the tree registered; skip + warn otherwise
|
|
570
|
-
|
|
571
|
-
If a task genuinely needs isolation, run it **outside** herdr (plain `pi`, no
|
|
572
|
-
`HERDR_ENV`), rather than pretending panes are sandboxed.
|
|
573
|
-
|
|
574
|
-
## Success and failure
|
|
575
|
-
|
|
576
|
-
**herdr `agent_status` cannot tell you whether an agent succeeded.** Success,
|
|
577
|
-
model error, and kill all report `done`. The plugin derives outcome from the
|
|
578
|
-
child’s session jsonl (`stopReason` of the last turn) and from the
|
|
579
|
-
machine-readable verdict.
|
|
580
|
-
|
|
581
|
-
Two orthogonal dimensions — never conflate them:
|
|
582
|
-
|
|
583
|
-
- **Lifecycle** (`launching → working → awaiting → blocked → retired/exited`):
|
|
584
|
-
is the process alive?
|
|
585
|
-
- **Execution** (`success / failed / aborted / truncated / running`): did the
|
|
586
|
-
task succeed?
|
|
587
|
-
|
|
588
|
-
`state: retired` + `execution.status: failed` is a normal combination.
|
|
589
|
-
|
|
590
|
-
| Session signal | Execution |
|
|
591
|
-
| --- | --- |
|
|
592
|
-
| Last turn `stopReason: "stop"` | success |
|
|
593
|
-
| `stopReason: "error"` without “aborted” | failed |
|
|
594
|
-
| `stopReason: "error"` + “This operation was aborted” | aborted |
|
|
595
|
-
| Last user prompt with **no** assistant message | aborted (killed mid-turn) |
|
|
596
|
-
| Last assistant `stopReason: "toolUse"` and the agent is gone | aborted |
|
|
597
|
-
| Last assistant `toolUse` and herdr says `blocked` | not terminal — confirm / wait |
|
|
598
|
-
| `stopReason: "length"` | truncated |
|
|
599
|
-
| No messages | unknown |
|
|
600
|
-
|
|
601
|
-
On top of that:
|
|
602
|
-
|
|
603
|
-
- **Only the last turn decides execution.** A tool error in turn 1 followed by
|
|
604
|
-
a clean turn 2 is overall success. `toolErrors` is diagnostic only.
|
|
605
|
-
- **Self-report is attested, not verified.** `{"ok": true}` is the agent
|
|
606
|
-
marking its own homework. Worker’s `verification-output` criterion is the
|
|
607
|
-
exception: when it carries an explicit `command`, the plugin runs it itself,
|
|
608
|
-
with a timeout; without one, the criterion is surfaced as pending for the
|
|
609
|
-
parent to confirm from the pasted output — the plugin never guesses a
|
|
610
|
-
project-unaware command. `completionGuard:
|
|
611
|
-
true` rejects a successful turn that forgot the `{"ok":…}` verdict.
|
|
612
|
-
- **Recycle destroys live evidence** (`agent get` → `agent_not_found`). The
|
|
613
|
-
plugin collects and snapshots `execution` into `.pi-subagents/run.json`
|
|
614
|
-
**before** closing the pane. Resume uses the session file, not the pane.
|
|
615
|
-
|
|
616
|
-
## Programmatic API
|
|
617
|
-
|
|
618
|
-
Hosts that are not the Pi `subagent` tool can drive the same machinery:
|
|
619
|
-
|
|
620
|
-
```ts
|
|
621
|
-
import {
|
|
622
|
-
createHerdrClient,
|
|
623
|
-
Orchestrator,
|
|
624
|
-
loadAgentsFromDir,
|
|
625
|
-
} from "pi-legion/api";
|
|
626
|
-
|
|
627
|
-
const client = createHerdrClient();
|
|
628
|
-
const [scout] = loadAgentsFromDir("/path/to/agents", "user");
|
|
629
|
-
const orch = new Orchestrator({
|
|
630
|
-
client,
|
|
631
|
-
runDir: "/tmp/run",
|
|
632
|
-
cwd: process.cwd(),
|
|
633
|
-
});
|
|
634
|
-
const handle = await orch.launch({ agent: scout, task: "survey the repo" });
|
|
635
|
-
const result = await orch.collect(handle.name);
|
|
636
|
-
```
|
|
637
|
-
|
|
638
|
-
Everything re-exported from `./api` is covered by the test suite. Do not import
|
|
639
|
-
from `src/` directly.
|
|
439
|
+
A non-Pi kind is rejected by v2. If that role must continue to run through
|
|
440
|
+
another CLI, keep it on the 0.16.x line or replace it with a Pi role.
|
|
640
441
|
|
|
641
442
|
## Development
|
|
642
443
|
|
|
643
444
|
```bash
|
|
644
|
-
git clone https://github.com/zzjcool/pi-legion.git
|
|
645
|
-
cd pi-legion
|
|
646
445
|
npm install
|
|
647
446
|
npm run typecheck
|
|
648
|
-
npm test
|
|
649
|
-
npm run test:integration
|
|
447
|
+
npm test # unit tests
|
|
448
|
+
npm run test:integration # integration tests
|
|
449
|
+
npm run test:all # typecheck + unit + integration tests
|
|
650
450
|
```
|
|
651
451
|
|
|
652
|
-
|
|
653
|
-
`HERDR_ENV=1`. They are skipped otherwise.
|
|
452
|
+
### Mock LLM (offline pi / legion smoke)
|
|
654
453
|
|
|
655
|
-
|
|
656
|
-
|
|
454
|
+
A scriptable OpenAI-compatible server lives under [`test/mock-llm/`](test/mock-llm/README.md).
|
|
455
|
+
It registers with Pi like any local gateway (`models.json` → `api: "openai-completions"`),
|
|
456
|
+
so spawn / stop / doctor / verify paths can run without real API keys:
|
|
657
457
|
|
|
658
458
|
```bash
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
bash test/docker/verify-4-acceptance.sh
|
|
663
|
-
bash test/docker/verify-5-blocked.sh
|
|
664
|
-
bash test/docker/verify-6-cache.sh
|
|
665
|
-
bash test/docker/verify-7-budget.sh
|
|
666
|
-
bash test/docker/verify-8-verify-command.sh
|
|
667
|
-
bash test/docker/verify-9-worktree.sh
|
|
668
|
-
bash test/docker/verify-10-fallback.sh
|
|
669
|
-
bash test/docker/verify-11-nested-alias.sh
|
|
670
|
-
bash test/docker/verify-12-workspace.sh
|
|
671
|
-
bash test/docker/verify-13-kinds.sh # kinds + models; live CodeBuddy/Cursor when mounted
|
|
459
|
+
npm run mock-llm # http://127.0.0.1:8765/v1 scenario=echo
|
|
460
|
+
npm run test:mock-llm # unit + integration smoke for the mock
|
|
461
|
+
MOCK_LLM_SCENARIO=spawn-scout npm run mock-llm
|
|
672
462
|
```
|
|
673
463
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
## Known limitations
|
|
685
|
-
|
|
686
|
-
- **Not a sandbox** (F23–F25): child-guard covers bash/herdr dispatch and
|
|
687
|
-
read-only writes; it does not give you process isolation. The read-only
|
|
688
|
-
bash rules are a **text heuristic** over the command line, so they miss
|
|
689
|
-
indirect writes — `node -e "require('fs').writeFileSync(…)"`,
|
|
690
|
-
`python3 -c "open('f','w')…"` and `perl -i` all pass, as does any child whose
|
|
691
|
-
agent definition omits `acceptance.role` (the guard is keyed off it). Treat it
|
|
692
|
-
as a guard-rail against accidents, not a boundary against intent.
|
|
693
|
-
- **`agent_status` has no success/failure semantics** (F26).
|
|
694
|
-
- **Non-pi kinds degrade** (F7): same Herdr control plane (`start` → `prompt` → `wait`), but no usage and outcome is `unknown` unless the pane text includes a verdict JSON that is not just the echoed launch prompt. Cursor starts with `--trust --force` (Run Everything) unless the agent opts back into a human gate via `onBlocked: forward`/`notify`, so workspace-trust and shell-approval dialogs cannot block the first prompt. Resume depends on each CLI.
|
|
695
|
-
- **A model the kind cannot express**: `--model` is built per CLI (pi wants
|
|
696
|
-
`provider/id`, cursor wants slugs like `cursor-grok-4.6-high`). A model that
|
|
697
|
-
cannot be expressed is omitted rather than mangled. Whether that is refused or
|
|
698
|
-
allowed depends on how it was chosen — an **explicit** choice (frontmatter,
|
|
699
|
-
`agentOverrides`, a preset, a `model` param) is refused before any pane is
|
|
700
|
-
created, while an **inherited** one (`subagents.defaultModel`, the parent
|
|
701
|
-
session's model) is dropped on purpose and reported as `modelDropped`, because
|
|
702
|
-
the parent's pi model simply means nothing to another CLI.
|
|
703
|
-
- **`blocked` is screen-heuristic**: false positives exist; the confirm is
|
|
704
|
-
paired with the collect timeout as a backstop.
|
|
705
|
-
- **Verification** runs `criterion.command` when a required criterion asks
|
|
706
|
-
for `verification-output` and carries one. A criterion without a `command`
|
|
707
|
-
stays attested and is surfaced as pending for the parent — semantic `must`
|
|
708
|
-
strings are never NLP-parsed (F32 / F44) and no default command is guessed.
|
|
709
|
-
Other evidence types stay a checklist.
|
|
710
|
-
- **Panes are scarce**: cap fan-out (`maxSubagentSpawnsPerSession` /
|
|
711
|
-
`herdr.maxConcurrentAgents`). Warm-pane pooling is not worth it (~0.9s).
|
|
712
|
-
|
|
713
|
-
## License
|
|
464
|
+
Use `writeMockPiHome()` from `test/mock-llm` to generate an isolated
|
|
465
|
+
`PI_CODING_AGENT_DIR` with dummy `apiKey: "mock"` only — never copy host
|
|
466
|
+
`auth.json` into fixtures.
|
|
467
|
+
|
|
468
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md#project-workflow) for the project
|
|
469
|
+
workflow: issues, pull requests, the merge gate, and npm releases.
|
|
470
|
+
|
|
471
|
+
Version history for consumers: [CHANGELOG.md](CHANGELOG.md). Tag-by-tag
|
|
472
|
+
release bodies: [GitHub Releases](https://github.com/zzjcool/pi-legion/releases).
|
|
714
473
|
|
|
715
474
|
MIT. See [LICENSE](LICENSE).
|