pi-legion 2.0.4 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +71 -1
- package/CONTRIBUTING.md +27 -24
- package/README.md +103 -19
- package/docs/contracts/osc-busy-plugin.md +72 -0
- package/docs/contracts/spawn-default-inprocess-v3.md +322 -0
- package/docs/contracts/wait-not-stop.md +30 -0
- package/docs/design.md +2 -2
- package/docs/headless-child-contract.md +83 -0
- package/docs/inprocess-supervisor.md +140 -0
- package/docs/qa-hunting-round.md +150 -0
- package/docs/subagents-teams.md +55 -18
- package/docs/tui-qa-playbook.md +284 -0
- package/index.ts +315 -49
- package/package.json +15 -8
- package/skills/pi-legion/SKILL.md +5 -0
- package/src/agents/agents.ts +34 -6
- package/src/agents/context.ts +83 -1
- package/src/agents/overrides.ts +1 -0
- package/src/agents/settings.ts +122 -1
- package/src/api.ts +23 -0
- package/src/extension/child-guard.ts +42 -3
- package/src/extension/legion-commands.ts +9 -2
- package/src/extension/legion-doctor.ts +54 -0
- package/src/extension/legion-stop.ts +6 -2
- package/src/extension/playbook.ts +3 -3
- package/src/extension/runtime.ts +57 -8
- package/src/extension/scout-guard.ts +39 -1
- package/src/extension/session-errors.ts +191 -0
- package/src/runs/args.ts +8 -0
- package/src/runs/orchestrator.ts +125 -16
- package/src/shared/output-cap.ts +2 -2
- package/src/shared/session.ts +2 -1
- package/src/shared/types.ts +74 -0
- package/src/supervisor/child-context.ts +90 -0
- package/src/supervisor/child-memory.ts +27 -3
- package/src/supervisor/inprocess-depth.ts +71 -0
- package/src/supervisor/inprocess-supervisor.ts +1011 -0
- package/src/supervisor/routing-supervisor.ts +179 -0
- package/src/supervisor/rpc-supervisor.ts +14 -0
- package/src/supervisor/spawn-mode.ts +35 -0
- package/src/supervisor/types.ts +13 -0
- package/src/supervisor/ui-proxy.ts +78 -0
- package/src/tui/live-overlay.ts +147 -55
- package/src/tui/status.ts +71 -35
- package/src/tui/viz/model.ts +6 -0
- package/src/tui/viz/panels.ts +8 -9
- package/src/tui/viz/railway.ts +154 -21
- package/src/tui/viz/rate.ts +15 -14
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,75 @@ summary so you can see **2.x vs 0.16** without leaving the tree.
|
|
|
11
11
|
|
|
12
12
|
## [Unreleased]
|
|
13
13
|
|
|
14
|
+
## [3.0.0] — 2026-10-18
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- In-process `AgentSession` spawn mode (#197): `spawnMode: "rpc" | "inprocess"`,
|
|
19
|
+
`InProcessSupervisor` + `RoutingSupervisor`, and separate
|
|
20
|
+
`subagents.inprocess.*` limit keys. See `docs/inprocess-supervisor.md`.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- **BREAKING (#197):** `spawnMode` now defaults to `inprocess`, not `rpc`.
|
|
25
|
+
When the key is omitted and the resolved spawn needs an OS worktree
|
|
26
|
+
(`worktree: true` — the writer/centurion default), Legion falls back to
|
|
27
|
+
`rpc` automatically. Non-`pi` roles are unaffected by this fallback: they
|
|
28
|
+
are refused with `INVALID_PARAMS` before the backend is chosen, because
|
|
29
|
+
only `kind: pi` children are supported at all. An explicit
|
|
30
|
+
`spawnMode: "inprocess"` combined with `worktree: true` is still refused
|
|
31
|
+
with `INVALID_PARAMS` — the fallback only covers the implicit default.
|
|
32
|
+
- **BREAKING (#197):** omitting the whole `subagents.inprocess` block no
|
|
33
|
+
longer means "unlimited". The in-process supervisor now falls back to the
|
|
34
|
+
`legion.*` defaults (`maxDepth: 4`, `maxChildrenPerNode: 8`,
|
|
35
|
+
`maxActive: 30`); an explicit block that omits `maxActive` still gets 8.
|
|
36
|
+
`maxSpawnsPerSession` / `waitTimeoutMs` keep no default.
|
|
37
|
+
- #197 in-process children read their identity (node id, db path, acceptance
|
|
38
|
+
role, nesting budget) from an AsyncLocalStorage child context instead of
|
|
39
|
+
`PI_SUBAGENT_*` / `PI_LEGION_*` env. In-process sessions share the parent's
|
|
40
|
+
`process.env`, so the env channel delivered the parent's values — or none at
|
|
41
|
+
all in a root parent — and mis-scoped read-only role enforcement. ALS does
|
|
42
|
+
not cross `spawn()`, so `bash` subprocesses can no longer inherit a tree
|
|
43
|
+
identity.
|
|
44
|
+
- TUI: drop the footer `N agents running` segment (#192); the aboveEditor
|
|
45
|
+
widget already shows the same count. `STATUS_FOOTER_KEY` / `formatFooterStatus`
|
|
46
|
+
remain exported but `@deprecated` (soft-land).
|
|
47
|
+
- Railway lane clock: the leading clock is now "time since the last task
|
|
48
|
+
started" — it keeps ticking through the quiet gap after a turn settles
|
|
49
|
+
(previously it froze at that turn's end, i.e. the *previous* task's elapsed)
|
|
50
|
+
and freezes only when the agent itself ends.
|
|
51
|
+
- Railway lane clocks show from round 1. #159 gated them behind `turns >= 2`,
|
|
52
|
+
but a child is usually dispatched with a single user message, so that gate
|
|
53
|
+
hid the clocks almost always and starved the detail column.
|
|
54
|
+
- Round 1 prints one clock, not two: that turn starts with the agent, so
|
|
55
|
+
`16s / 17s` was one fact plus the spawn gap.
|
|
56
|
+
- Railway column is content-driven: the right-hand name/detail block takes what
|
|
57
|
+
the labels need and the time axis absorbs the rest (floor
|
|
58
|
+
`RAIL_AXIS_MIN_COLS`), so a row fills the line instead of trailing blanks.
|
|
59
|
+
|
|
60
|
+
### Fixed
|
|
61
|
+
|
|
62
|
+
- Dropped the `now` / `live` wording from every tok/s surface. "now" meant two
|
|
63
|
+
different things at once (the live-only header rate vs. the chart's x-axis
|
|
64
|
+
right edge), which read as a bug; the stat row key is now `rate`.
|
|
65
|
+
- Rail lane detail drops a field for *every* lane when it fits none, instead of
|
|
66
|
+
stripping the role from one long-named row only.
|
|
67
|
+
- `make rebuild` rebuilds the sandbox image and swaps the container.
|
|
68
|
+
- Real-model sandbox: children get `PI_LEGION_CHILD_EXTENSIONS` so they load
|
|
69
|
+
the provider package (#43) — without it every subagent failed with
|
|
70
|
+
`Unknown provider "any-endpoint"`.
|
|
71
|
+
- Subagent detail modal: the area around the frame is no longer black. It was
|
|
72
|
+
mounted as a full-screen overlay, and pi-tui composites an overlay by
|
|
73
|
+
discarding every base cell in its rectangle (there is no transparency option),
|
|
74
|
+
so the viewer's own dim-mask spaces were erasing the host chat / `/legion`
|
|
75
|
+
view and leaving the terminal's default background. The overlay now covers the
|
|
76
|
+
frame rect only, so the host paints everything around it (#193).
|
|
77
|
+
- Geometry is a percentage width with no `maxHeight`, so pi-tui re-derives the
|
|
78
|
+
rect every frame; absolute cells froze at the open-time terminal size and
|
|
79
|
+
clipped the frame's bottom border and Esc hint after a resize.
|
|
80
|
+
- "Click the mask to dismiss" is gone — clicks outside the frame now reach the
|
|
81
|
+
host, as they should. Esc and the title-bar Esc hint still close the modal.
|
|
82
|
+
|
|
14
83
|
## [2.0.4] — 2026-10-15
|
|
15
84
|
|
|
16
85
|
### Added
|
|
@@ -164,7 +233,8 @@ branch — pin `pi-legion@0.16.x` if you still need that model. See
|
|
|
164
233
|
Earlier 0.11–0.15 history (including the `pi-herdr-swarm` /
|
|
165
234
|
`@zzjcool/pi-herdr-subagents` renames) lives in git tags and older Releases.
|
|
166
235
|
|
|
167
|
-
[Unreleased]: https://github.com/zzjcool/pi-legion/compare/
|
|
236
|
+
[Unreleased]: https://github.com/zzjcool/pi-legion/compare/v3.0.0...HEAD
|
|
237
|
+
[3.0.0]: https://github.com/zzjcool/pi-legion/compare/v2.0.4...v3.0.0
|
|
168
238
|
[2.0.4]: https://github.com/zzjcool/pi-legion/compare/v2.0.3...v2.0.4
|
|
169
239
|
[2.0.3]: https://github.com/zzjcool/pi-legion/compare/v2.0.2...v2.0.3
|
|
170
240
|
[2.0.2]: https://github.com/zzjcool/pi-legion/compare/v2.0.1...v2.0.2
|
package/CONTRIBUTING.md
CHANGED
|
@@ -21,13 +21,13 @@ directly to `master`. Implementers do not merge their own pull requests.
|
|
|
21
21
|
**Before you push.** Run:
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
pnpm run typecheck
|
|
25
|
+
pnpm test
|
|
26
|
+
pnpm run test:integration # or pnpm run test:all
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
The required GitHub Actions check `ci` runs typecheck, unit, integration, and
|
|
30
|
-
mock-llm. Run `
|
|
30
|
+
mock-llm. Run `pnpm run test:integration` (or `pnpm run test:all`) locally
|
|
31
31
|
before you push, especially when the change touches `src/runs/`, `src/legion/`,
|
|
32
32
|
or `src/extension/runtime.ts`.
|
|
33
33
|
|
|
@@ -45,9 +45,10 @@ timeout the caller is awaiting stays referenced, as with the wait timer in
|
|
|
45
45
|
`src/extension/runtime.ts`.
|
|
46
46
|
|
|
47
47
|
**npm release.** A `v*.*.*` tag runs the release workflow
|
|
48
|
-
(`.github/workflows/release.yml`).
|
|
49
|
-
`npm stage publish` and a stage-only token.
|
|
50
|
-
version on npm with 2FA. Do not use plain
|
|
48
|
+
(`.github/workflows/release.yml`). Dependencies are installed with **pnpm**;
|
|
49
|
+
staging still uses the npm CLI (`npm stage publish`) and a stage-only token.
|
|
50
|
+
A maintainer promotes the staged version on npm with 2FA. Do not use plain
|
|
51
|
+
`npm publish` in GitHub Actions.
|
|
51
52
|
|
|
52
53
|
**Communication.** Coordinate in the team channel, or with Alice. Do not ping
|
|
53
54
|
the maintainer user for a routine review or merge ask.
|
|
@@ -57,34 +58,36 @@ the maintainer user for a routine review or merge ask.
|
|
|
57
58
|
```bash
|
|
58
59
|
git clone https://github.com/zzjcool/pi-legion.git
|
|
59
60
|
cd pi-legion
|
|
60
|
-
|
|
61
|
+
pnpm install
|
|
61
62
|
```
|
|
62
63
|
|
|
63
|
-
Requires Node.js 22+ (`
|
|
64
|
-
there is no compile step
|
|
64
|
+
Requires Node.js 22+ and **pnpm** 9+ (`packageManager` in `package.json`;
|
|
65
|
+
`node --experimental-strip-types` is how tests run; there is no compile step
|
|
66
|
+
to ship). Prefer Corepack (`corepack enable`) or install pnpm 9.x. Do not use
|
|
67
|
+
`npm install` in this repo.
|
|
65
68
|
|
|
66
69
|
## Checks
|
|
67
70
|
|
|
68
71
|
The required GitHub Actions check `ci` (`.github/workflows/ci.yml`, on pull
|
|
69
72
|
requests and pushes to `master`) runs, in order:
|
|
70
73
|
|
|
71
|
-
1. `
|
|
72
|
-
2. `
|
|
73
|
-
3. `
|
|
74
|
-
4. `
|
|
74
|
+
1. `pnpm run typecheck`
|
|
75
|
+
2. `pnpm test` (unit)
|
|
76
|
+
3. `pnpm run test:integration`
|
|
77
|
+
4. `pnpm run test:mock-llm`
|
|
75
78
|
|
|
76
79
|
A failing integration step fails `ci`, so nesting and orchestrator regressions
|
|
77
80
|
are blocked before merge. `test:live` and `test:docker` stay out of that job.
|
|
78
81
|
|
|
79
82
|
```bash
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
pnpm run typecheck
|
|
84
|
+
pnpm test # test/unit/*.test.ts
|
|
85
|
+
pnpm run test:integration # FakeSupervisor / nested fakes; no live `pi` binary
|
|
86
|
+
pnpm run test:all # typecheck + unit + integration
|
|
87
|
+
pnpm run test:mock-llm # also part of the required ci check
|
|
85
88
|
```
|
|
86
89
|
|
|
87
|
-
`
|
|
90
|
+
`pnpm run test:live` is a **pi RPC live** smoke (`test/live/rpc-live.test.ts`).
|
|
88
91
|
It skips itself when the `pi` binary is missing from `PATH` (see the probe at
|
|
89
92
|
the top of that file). Do not fail CI on it.
|
|
90
93
|
|
|
@@ -102,8 +105,8 @@ For offline Pi / legion smoke without provider quota, start the scriptable
|
|
|
102
105
|
OpenAI-compatible mock (see [`test/mock-llm/README.md`](test/mock-llm/README.md)):
|
|
103
106
|
|
|
104
107
|
```bash
|
|
105
|
-
|
|
106
|
-
|
|
108
|
+
pnpm run mock-llm # default scenario=echo on :8765
|
|
109
|
+
pnpm run test:mock-llm # engine + HTTP + optional pi --print smoke
|
|
107
110
|
```
|
|
108
111
|
|
|
109
112
|
`test/docker/mock-llm.mjs` launches the same CLI (default scenario `tool-once`
|
|
@@ -142,12 +145,12 @@ bash test/docker/verify-11-nested-alias.sh # nested + alias + completionGuard
|
|
|
142
145
|
Or all at once (RPC mainline **1–11** only):
|
|
143
146
|
|
|
144
147
|
```bash
|
|
145
|
-
|
|
148
|
+
pnpm run test:docker
|
|
146
149
|
```
|
|
147
150
|
|
|
148
151
|
**Legacy Herdr / Space / kind checks** live under `test/legacy/docker/` (need
|
|
149
152
|
pi-legion **0.16.x** Herdr-era setup). They are **not** part of
|
|
150
|
-
`
|
|
153
|
+
`pnpm run test:docker`:
|
|
151
154
|
|
|
152
155
|
```bash
|
|
153
156
|
bash test/legacy/docker/verify-12-workspace.sh # Herdr Space pin (legacy)
|
package/README.md
CHANGED
|
@@ -20,6 +20,8 @@ shared SQLite ledger records the whole tree.
|
|
|
20
20
|
## Contents
|
|
21
21
|
|
|
22
22
|
- [Architecture](#architecture)
|
|
23
|
+
- [Spawn mode](#spawn-mode)
|
|
24
|
+
- [Headless child contract](#headless-child-contract)
|
|
23
25
|
- [Quick start](#quick-start)
|
|
24
26
|
- [Legion tools and phases](#legion-tools-and-phases)
|
|
25
27
|
- [Three-layer visualization](#three-layer-visualization)
|
|
@@ -63,6 +65,50 @@ Transport, state, decision-making, and display are separate layers:
|
|
|
63
65
|
- **Display:** the event bus feeds the resident tree, the `/legion` full view,
|
|
64
66
|
and an on-demand live overlay.
|
|
65
67
|
|
|
68
|
+
## Spawn mode
|
|
69
|
+
|
|
70
|
+
`spawnMode` picks the backend that runs one child. Per role it comes from agent
|
|
71
|
+
frontmatter → `subagents.agentOverrides.<role>` → team member override:
|
|
72
|
+
|
|
73
|
+
```yaml
|
|
74
|
+
# agents/scout.md
|
|
75
|
+
---
|
|
76
|
+
spawnMode: inprocess # or: rpc
|
|
77
|
+
---
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Default:** omitting `spawnMode` means `inprocess` — the child runs as an
|
|
81
|
+
`AgentSession` inside the parent Node process (`createAgentSession`), which
|
|
82
|
+
trades process isolation for spawn latency.
|
|
83
|
+
|
|
84
|
+
**Automatic fallback to `rpc`:** when `spawnMode` is omitted *and* the resolved
|
|
85
|
+
spawn cannot be in-process, Legion launches it over `pi --mode rpc` instead.
|
|
86
|
+
That happens when the role needs an OS worktree (`worktree: true` — the writer
|
|
87
|
+
and centurion default). A `kind !== "pi"` role is rejected earlier with
|
|
88
|
+
`INVALID_PARAMS` (only `kind: pi` children are supported at all), so it never
|
|
89
|
+
reaches this fallback.
|
|
90
|
+
|
|
91
|
+
**Explicit is strict:** `spawnMode: "inprocess"` with `worktree: true` is
|
|
92
|
+
refused with `INVALID_PARAMS` rather than silently downgraded, so a role that
|
|
93
|
+
asks for in-process never gets a different transport behind its back.
|
|
94
|
+
|
|
95
|
+
Limits for the in-process backend are separate (`subagents.inprocess.*`) and are
|
|
96
|
+
not shared with the `legion.*` tree budgets — see
|
|
97
|
+
[docs/inprocess-supervisor.md](docs/inprocess-supervisor.md).
|
|
98
|
+
|
|
99
|
+
## Headless child contract
|
|
100
|
+
|
|
101
|
+
Legion children on the **`rpc` backend** are rpc only (`pi --mode rpc`).
|
|
102
|
+
Headless modes have no
|
|
103
|
+
interactive tool-approval UI. Project `.pi/settings.json` / project extensions
|
|
104
|
+
load under headless only after `/trust` (or `defaultProjectTrust=always`), or
|
|
105
|
+
when you opt in with `subagents.rpc.approve: true` (appends `--approve`). Legion
|
|
106
|
+
`onBlocked` is a parent subagent block strategy — **not** a pi tool-approval
|
|
107
|
+
replacement; keep the layers separate.
|
|
108
|
+
|
|
109
|
+
Full mapping (exit → `SettleResult` → node state) and the three upstream headless
|
|
110
|
+
modes vs legion: [docs/headless-child-contract.md](docs/headless-child-contract.md).
|
|
111
|
+
|
|
66
112
|
## Quick start
|
|
67
113
|
|
|
68
114
|
Install and load the extension in Pi:
|
|
@@ -109,7 +155,8 @@ child's result in the same print turn, call `wait` or `collect` before the
|
|
|
109
155
|
turn ends, or launch with `async: false`. To stop children explicitly, use
|
|
110
156
|
`subagent({ action: "stop", name: "worker-0" })` or
|
|
111
157
|
`subagent({ action: "stop", all: true })`; this runs the normal graceful-retire
|
|
112
|
-
sequence for each selected child.
|
|
158
|
+
sequence for each selected child. Pass `force: true` to escalate to hard retire
|
|
159
|
+
when a child ignores soft steer / graceful close.
|
|
113
160
|
|
|
114
161
|
### Stopping async subagents yourself
|
|
115
162
|
|
|
@@ -123,6 +170,16 @@ shutdown); process exit / session shutdown also retires:
|
|
|
123
170
|
parent (stop the parent to retire them).
|
|
124
171
|
- **`x`** on the selected live agent in the `/legion` view or its live
|
|
125
172
|
overlay asks `y` to confirm in place, then stops it.
|
|
173
|
+
- **Tool**: `subagent({ action: "stop", name })` is graceful; pass
|
|
174
|
+
`force: true` for hard retire (SIGTERM → SIGKILL + descendant sweep per
|
|
175
|
+
`docs/retire-hardening-contract.md`). Example:
|
|
176
|
+
`subagent({ action: "stop", name: "worker-0", force: true })`.
|
|
177
|
+
TUI `/legion stop --force` is an optional follow-up (same `force: true`).
|
|
178
|
+
|
|
179
|
+
**`wait` ≠ stop.** A `wait` timeout (or still-running result) does **not**
|
|
180
|
+
retire children. The tool result includes live child names, elapsed ms, and
|
|
181
|
+
suggested decisions (`extend` / `steer_again` / `stop` / `force_stop`); the
|
|
182
|
+
parent chooses the next tool call. See `docs/contracts/wait-not-stop.md`.
|
|
126
183
|
|
|
127
184
|
While subagents are live, the railway header can show a dim
|
|
128
185
|
`/legion stop [name|all]` hint (shortened on narrow terminals; idle gating /
|
|
@@ -182,10 +239,15 @@ commands without an LLM. Passing verification advances the squad to `done`.
|
|
|
182
239
|
1. **Railway strip above the input:** one git-graph lane per live subagent
|
|
183
240
|
(max 4, 3 below 80 columns; oldest/shallowest first, `+N` for the rest).
|
|
184
241
|
When an agent ends its lane merges back and lower lanes shift up. The
|
|
185
|
-
header shows the live count and tok/s (trailing 60s average).
|
|
186
|
-
|
|
242
|
+
header shows the live count and tok/s (trailing 60s average). The lane
|
|
243
|
+
label reads `role · <time since the last task started> / <lifetime> · <age
|
|
244
|
+
of last activity>`; the leading clock keeps ticking through the quiet gap
|
|
245
|
+
after a turn settles and freezes only when the agent itself ends. The lane
|
|
246
|
+
column is measured from what the labels need and the time axis takes the
|
|
247
|
+
rest (down to a floor), so a row always fills the line. Hidden when no
|
|
248
|
+
subagent is live. Set `PI_LEGION_STRIP=tree` for the classic roster.
|
|
187
249
|
2. **`/legion` full view:** galaxy graph of the agent tree, stacked tok/s
|
|
188
|
-
chart by agent type (peak marker,
|
|
250
|
+
chart by agent type (peak marker, rate/avg/peak/total, per-role share), and
|
|
189
251
|
a scrollable `log --graph` history. Tab switches panes, arrows scroll or
|
|
190
252
|
move the selection, Enter selects an agent (its own rate line, tok/s,
|
|
191
253
|
tokens and share are overlaid), `o` opens its live overlay, `x` stops it
|
|
@@ -285,9 +347,20 @@ per-member overrides. Object member overrides (for example `model` or
|
|
|
285
347
|
omitted override fields continue to use their normal role/configuration
|
|
286
348
|
values.
|
|
287
349
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
350
|
+
Teams may also set optional `parentContext` / `childContext` (same value forms
|
|
351
|
+
as the global keys, including `@path` and `false`). When the active team
|
|
352
|
+
defines a side, that value **replaces** the global context for that side — it
|
|
353
|
+
does not merge. Omit the field to fall back to global (or convention files);
|
|
354
|
+
set `false` to disable that side with no fallthrough. Unknown team names keep
|
|
355
|
+
today's warn + full roster behaviour and still use global context.
|
|
356
|
+
|
|
357
|
+
See [docs/subagents-teams.md](docs/subagents-teams.md) for editable
|
|
358
|
+
frontend/backend team templates with **distinct workflow text** (not only
|
|
359
|
+
different models). The model IDs in that template are placeholders for your
|
|
360
|
+
provider, not required defaults. For running a multi-agent QA hunting round in
|
|
361
|
+
this repo, see [docs/qa-hunting-round.md](docs/qa-hunting-round.md) (round
|
|
362
|
+
orchestration) and [docs/tui-qa-playbook.md](docs/tui-qa-playbook.md)
|
|
363
|
+
(evidence rules).
|
|
291
364
|
|
|
292
365
|
### Parent/child context conventions
|
|
293
366
|
|
|
@@ -296,8 +369,11 @@ inline or through `@path` references. If either setting is omitted, Legion
|
|
|
296
369
|
looks for `subagents-parent-context.md` or `subagents-child-context.md`
|
|
297
370
|
respectively in the directory containing that settings file. These are
|
|
298
371
|
convention-based fallbacks: an explicit setting takes precedence, and setting
|
|
299
|
-
either field to `false` disables its context.
|
|
300
|
-
|
|
372
|
+
either field to `false` disables its context. Named teams can override each
|
|
373
|
+
side via `subagents.teams.<name>.parentContext` /
|
|
374
|
+
`childContext` (replace, never merge; see [Team 配置](#team-配置)). The
|
|
375
|
+
convention filenames are `PARENT_CONTEXT_CONVENTION` and
|
|
376
|
+
`CHILD_CONTEXT_CONVENTION` in
|
|
301
377
|
[`src/agents/settings.ts`](src/agents/settings.ts#L30-L31); missing convention
|
|
302
378
|
files simply contribute no context. The repository examples are
|
|
303
379
|
[`examples/subagents-parent-context.md`](examples/subagents-parent-context.md)
|
|
@@ -311,7 +387,7 @@ same idea as `pi list` / `pi auth check`.
|
|
|
311
387
|
|
|
312
388
|
- `/legion doctor` — full report in the chat (works outside TUI too).
|
|
313
389
|
- `/legion doctor [section]` — one of: `summary`, `extensions`, `models`,
|
|
314
|
-
`spawn`, `memory`, `tools`, `storage`, `runtime`, `next`.
|
|
390
|
+
`spawn`, `memory`, `tools`, `storage`, `runtime`, `errors`, `next`.
|
|
315
391
|
- `subagent({ action: "diagnose" })` — same full report as tool output so the
|
|
316
392
|
main agent can call it without waiting for you. The tool has no section
|
|
317
393
|
field; filter with `/legion doctor [section]`.
|
|
@@ -326,6 +402,12 @@ snippet (relevant sections + Next steps) so the main agent can self-fix
|
|
|
326
402
|
without anyone typing `/legion doctor`. Full `/legion doctor` remains for
|
|
327
403
|
manual deep dives.
|
|
328
404
|
|
|
405
|
+
**Session errors (#160):** tool validation / call failures from `subagent`
|
|
406
|
+
(and similar) append to `.pi-subagents/session-errors.jsonl` (JSONL, rotated
|
|
407
|
+
to ~500 lines). `/legion doctor` / `diagnose` show counts, by-code tallies,
|
|
408
|
+
and the last few one-line titles under the `errors` section; when the log is
|
|
409
|
+
large they print only that summary plus the log path.
|
|
410
|
+
|
|
329
411
|
Coverage:
|
|
330
412
|
|
|
331
413
|
| Section | What it checks |
|
|
@@ -337,6 +419,7 @@ Coverage:
|
|
|
337
419
|
| tools | `/legion` slash list (status, team, agents, summary, on/off, scout-guard, doctor, stop), idle-Esc / strip hints, tool names |
|
|
338
420
|
| 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 |
|
|
339
421
|
| runtime | Live children, tree statuses, acceptance-ish / active nodes |
|
|
422
|
+
| errors | Recent session/tool errors (validation failures, etc.) as a short summary; full journal at `.pi-subagents/session-errors.jsonl` (rotated ~500 lines). Large logs: summary + path only — doctor never dumps the whole file |
|
|
340
423
|
|
|
341
424
|
### Child extensions
|
|
342
425
|
|
|
@@ -439,11 +522,11 @@ another CLI, keep it on the 0.16.x line or replace it with a Pi role.
|
|
|
439
522
|
## Development
|
|
440
523
|
|
|
441
524
|
```bash
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
525
|
+
pnpm install
|
|
526
|
+
pnpm run typecheck
|
|
527
|
+
pnpm test # unit tests
|
|
528
|
+
pnpm run test:integration # integration tests
|
|
529
|
+
pnpm run test:all # typecheck + unit + integration tests
|
|
447
530
|
```
|
|
448
531
|
|
|
449
532
|
### Mock LLM (offline pi / legion smoke)
|
|
@@ -453,9 +536,9 @@ It registers with Pi like any local gateway (`models.json` → `api: "openai-com
|
|
|
453
536
|
so spawn / stop / doctor / verify paths can run without real API keys:
|
|
454
537
|
|
|
455
538
|
```bash
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
MOCK_LLM_SCENARIO=spawn-scout
|
|
539
|
+
pnpm run mock-llm # http://127.0.0.1:8765/v1 scenario=echo
|
|
540
|
+
pnpm run test:mock-llm # unit + integration smoke for the mock
|
|
541
|
+
MOCK_LLM_SCENARIO=spawn-scout pnpm run mock-llm
|
|
459
542
|
```
|
|
460
543
|
|
|
461
544
|
Use `writeMockPiHome()` from `test/mock-llm` to generate an isolated
|
|
@@ -463,7 +546,8 @@ Use `writeMockPiHome()` from `test/mock-llm` to generate an isolated
|
|
|
463
546
|
`auth.json` into fixtures.
|
|
464
547
|
|
|
465
548
|
See [CONTRIBUTING.md](CONTRIBUTING.md#project-workflow) for the project
|
|
466
|
-
workflow: issues, pull requests, the merge gate, and
|
|
549
|
+
workflow: issues, pull requests, the merge gate, and releases
|
|
550
|
+
(deps via pnpm; npm stage publish for staging).
|
|
467
551
|
|
|
468
552
|
Version history for consumers: [CHANGELOG.md](CHANGELOG.md). Tag-by-tag
|
|
469
553
|
release bodies: [GitHub Releases](https://github.com/zzjcool/pi-legion/releases).
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# OSC 7501 busy / plugin contract (pi-legion #156)
|
|
2
|
+
|
|
3
|
+
Legion keeps the **root** program-status record `working` while any live child
|
|
4
|
+
or registered extension activity remains. OSC truth is owned by Pi's
|
|
5
|
+
`ProgramStatusReporter` — **plugins and legion must not `printf` OSC 7501**.
|
|
6
|
+
|
|
7
|
+
## Handshake
|
|
8
|
+
|
|
9
|
+
- Terminals may answer `OSC 7501;?` / `PI_PROGRAM_STATUS=1`.
|
|
10
|
+
- Prefer Pi APIs (`ui.setActivity`, `ui.setProgramRecord`). Do not emit raw OSC.
|
|
11
|
+
|
|
12
|
+
## Plugin / child-side rules
|
|
13
|
+
|
|
14
|
+
1. **Raise**: while async work is unfinished →
|
|
15
|
+
`ui.setActivity(source, { message })` and/or
|
|
16
|
+
`ui.setProgramRecord({ id, state: "working", message })`.
|
|
17
|
+
2. **Clear**: on settle / cancel / error →
|
|
18
|
+
`ui.setActivity(source, undefined)` and
|
|
19
|
+
`ui.setProgramRecord({ id, state: "clear" })`.
|
|
20
|
+
3. **Ids**: use a stable hierarchical id (`plugin/<name>/…` or
|
|
21
|
+
`legion/<slug>`). Do **not** write an id-less root `working` that steals
|
|
22
|
+
Pi's root semantics — register an activity source or a child id.
|
|
23
|
+
4. **RPC children**: in RPC mode, Pi forwards `ui.setActivity` /
|
|
24
|
+
`ui.setProgramRecord` as `extension_activity` to the parent. Legion folds
|
|
25
|
+
those into the parent reporter (`legion/<child>/<source>`). **Do not**
|
|
26
|
+
parse child PTY OSC bytes.
|
|
27
|
+
|
|
28
|
+
## Legion aggregation
|
|
29
|
+
|
|
30
|
+
| Source | Mechanism |
|
|
31
|
+
|--------|-----------|
|
|
32
|
+
| Live RPC child | `emitBusy` → `ui.setActivity("legion", …)` + per-child `id=legion/<slug>` |
|
|
33
|
+
| Child-side plugin busy | RPC `extension_activity` → parent `setActivity` / `setProgramRecord` |
|
|
34
|
+
| Same-process plugin | Direct `ui.setActivity` / `setProgramRecord` |
|
|
35
|
+
|
|
36
|
+
Root stays `working` while any activity source or uncleared `legion/*` child
|
|
37
|
+
id remains. `/legion stop` and runtime dispose clear the legion source and
|
|
38
|
+
child ids. **Esc** (wait interrupt) does **not** clear activity.
|
|
39
|
+
|
|
40
|
+
Priority inside Pi (unchanged): `blocked` > compacting > (`runActive` OR
|
|
41
|
+
`activities.size > 0` → root `working`) > resting (`done`/`idle`/`error`).
|
|
42
|
+
|
|
43
|
+
## Mock example (copy-paste)
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
// Same-process plugin / test double
|
|
47
|
+
ui.setActivity?.("plugin/bg-tasks", { message: "2 background tasks" });
|
|
48
|
+
ui.setProgramRecord?.({
|
|
49
|
+
id: "plugin/bg-tasks/job-1",
|
|
50
|
+
state: "working",
|
|
51
|
+
message: "job-1",
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// …work finishes…
|
|
55
|
+
ui.setProgramRecord?.({ id: "plugin/bg-tasks/job-1", state: "clear" });
|
|
56
|
+
ui.setActivity?.("plugin/bg-tasks", undefined);
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Parent-side mock of a child fan-in (what legion does after RPC):
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
// Child "scout" raised setActivity("bg", { message: "compiling" })
|
|
63
|
+
ui.setActivity("legion/scout/bg", { message: "compiling" });
|
|
64
|
+
// Child cleared
|
|
65
|
+
ui.setActivity("legion/scout/bg", undefined);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Non-goals
|
|
69
|
+
|
|
70
|
+
- Herdr / inbox OSC consumers
|
|
71
|
+
- Parsing child stdout for OSC 7501
|
|
72
|
+
- Big TUI railway/footer changes in this wave
|