pi-legion 0.16.0 → 2.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/CONTRIBUTING.md +58 -0
- package/README.md +313 -640
- 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 +67 -31
- package/agents/scout.md +6 -2
- package/agents/worker.md +12 -3
- package/docs/contracts/esc-abort-v1.md +131 -0
- package/docs/contracts/esc-cascade-v2.md +100 -0
- package/docs/design.md +44 -25
- 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 +1462 -286
- package/package.json +12 -9
- package/skills/pi-legion/SKILL.md +74 -96
- package/src/agents/agents.ts +6 -11
- 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 +47 -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 +719 -0
- package/src/extension/legion-doctor.ts +888 -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 +655 -105
- package/src/extension/scout-guard.ts +483 -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 +51 -5
- package/src/runs/hygiene.ts +335 -0
- package/src/runs/orchestrator.ts +790 -1683
- package/src/runs/owner.ts +61 -0
- package/src/runs/provision.ts +135 -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 +243 -171
- package/src/shared/types.ts +107 -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 +530 -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
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# ESC 级联关闭契约(v2,2026-10-13 冻结)
|
|
2
|
+
|
|
3
|
+
## 背景
|
|
4
|
+
|
|
5
|
+
v1(esc-abort-v1.md,已随 e66c85f 合入)冻结了「ESC ≠ 杀 children」语义。用户裁决推翻:turn 是 children 的 owner,turn 被 abort 时 children 应级联关闭,否则是资源泄漏。
|
|
6
|
+
|
|
7
|
+
## 冻结决定
|
|
8
|
+
|
|
9
|
+
### A. ESC 打断 turn → 级联优雅关闭 children
|
|
10
|
+
|
|
11
|
+
改动点 `index.ts` 工具 execute(约 :687-691 的 `signal?.aborted` 快速返回处):
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
if (signal?.aborted && action !== "stop") {
|
|
15
|
+
// NEW: cascade-close children owned by this turn before returning.
|
|
16
|
+
await cascadeRetireAll(); // fire-and-forget 也可以,但要保证 promise 存活到 retire 完成
|
|
17
|
+
return ok("interrupted (parent turn aborted)");
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**注意**:进入 execute 时 signal 已 aborted 的情况(ESC 发生在此工具调用排队后、执行前)——此时本 turn 尚未 launch 过任何 child,cascade 是 no-op,安全。
|
|
22
|
+
|
|
23
|
+
但真正的触发点是**执行中途 abort**:wait/collect 正在 await 时 signal 才 abort。这些路径已有 interrupted 返回分支(waitAction / collectChild / collectInline / waitOne),在这些分支统一调 `cascadeRetireAll()`。
|
|
24
|
+
|
|
25
|
+
### B. cascadeRetireAll 实现(index.ts)
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
const cascadeRetireAll = async (): Promise<void> => {
|
|
29
|
+
// 1. runtime 跟踪的所有 jobs(与 stopAction all:true 相同的目标集)
|
|
30
|
+
for (const name of targets) runtime.cancelWatch(name); // 先停 watch,防 retire 期间通知噪音
|
|
31
|
+
await Promise.all([...jobs.map((job) => job.retire?.()), ...pendingUtilityRetires]); // 全部 best-effort catch
|
|
32
|
+
// 2. release 全部
|
|
33
|
+
};
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
复用 stopAction 的骨架但**不生成工具结果文案**(不是用户显式 stop,静默级联)。vexillatio 的 pendingUtilityRetires 一并级联。
|
|
37
|
+
|
|
38
|
+
### C. wait/collect 中断路径统一接线
|
|
39
|
+
|
|
40
|
+
所有「signal aborted → return interrupted」的分支(index.ts waitAction 间接层 + runtime.waitOne + collectChild + collectInline + launchFamily abandoned 路径)在返回前触发 cascade(一次性,防重入——用 module-level `cascading` promise 去重即可,同一 signal 只触发一次)。
|
|
41
|
+
|
|
42
|
+
**防重入**:同一 abort 事件可能同时命中多个 in-flight 工具调用(parallel tool calls),cascade 必须幂等:用 `let cascadePromise: Promise<void> | undefined` 缓存,已存在则复用。turn_start 时重置(新 turn 新级联)。
|
|
43
|
+
|
|
44
|
+
### D. vexillatio collect 接 signal
|
|
45
|
+
|
|
46
|
+
`index.ts` launchUtilityFamily(约 :1895):
|
|
47
|
+
```ts
|
|
48
|
+
const result = await handle.orchestrator.collect(handle.name, { timeoutMs, ...(signal ? { signal } : {}) });
|
|
49
|
+
```
|
|
50
|
+
vexillatio 工具 execute 也要把 signal 传进来(找到它的 registerTool 调用,第 3 参数接上)。ESC 后 utility children 也应被 retire(它们进了 pendingUtilityRetires,cascade 覆盖)。
|
|
51
|
+
|
|
52
|
+
### E. 契约/文档更新
|
|
53
|
+
|
|
54
|
+
- docs/contracts/esc-abort-v1.md:顶部加「v2 修订:ESC 语义改为级联关闭,推翻 v1 的『ESC ≠ 杀 children』」——保留原文供考古,加修订记录。
|
|
55
|
+
- docs/design.md、README.md、playbook(TOOL_DESCRIPTION / PARENT_PLAYBOOK)相应语句更新:ESC abort 会级联 retire children;wait 被打断时 children 一并关闭。
|
|
56
|
+
|
|
57
|
+
## 语义边界(不变)
|
|
58
|
+
|
|
59
|
+
- **stop action 不变**:显式停止仍走全链。
|
|
60
|
+
- **主 pi 正常退出(quit)不变**:session_shutdown 已级联。
|
|
61
|
+
- **SIGKILL/崩溃不覆盖**:孤儿扫描 + resume 语义不变(这是既定接受的风险)。
|
|
62
|
+
- **children 的 children**:retire 链本身递归(孙进程 sweep 在 rpc-supervisor 的 retireChild 里),无需额外处理。
|
|
63
|
+
- **blocked children**:一并 retire(stop 语义一致)。
|
|
64
|
+
|
|
65
|
+
## 文件归属
|
|
66
|
+
|
|
67
|
+
| 文件 | 改动 |
|
|
68
|
+
|---|---|
|
|
69
|
+
| index.ts | cascadeRetireAll + 各中断分支接线 + vexillatio signal |
|
|
70
|
+
| src/extension/playbook.ts | TOOL_DESCRIPTION / PARENT_PLAYBOOK 更新 |
|
|
71
|
+
| docs/contracts/esc-abort-v1.md | v2 修订记录 |
|
|
72
|
+
| docs/design.md, README.md | 语义说明 |
|
|
73
|
+
| test/unit/* | 新测试 |
|
|
74
|
+
|
|
75
|
+
## v2.1 修订(2026-10-13,会话 01a13345 实测后补)
|
|
76
|
+
|
|
77
|
+
v2 的触发点只覆盖了「abort 落在 in-flight subagent 工具调用上」(wait/collect/launch 中途)。**async launch(默认形态)在 ESC 前就早已返回**:ESC 落在模型生成、空档或别的工具上时,没有任何 subagent 工具在执行,signal 无处投递,级联不触发——子 agent 继续跑,直到 session_shutdown 或显式 stop。
|
|
78
|
+
|
|
79
|
+
实测:2026-10-13 03:20Z 会话 01a13345 async 启动 centurion(worktree),03:20:10 ESC abort 父 turn,子 agent 存活至用户退出父会话(03:20:24,走的是 session_shutdown 级联)。
|
|
80
|
+
|
|
81
|
+
**修复**:`index.ts` 注册 `pi.on("agent_settled")`——pi 核心在每个 agent run 结算时发 `{ aborted: boolean }`,ESC abort 后必为 true(见 pi AgentSettledEvent:"Whether the run ended because it was aborted, for example with Escape.")。handler 在 `event.aborted` 时 fire-and-forget `cascadeRetireAll()`(不 await:优雅 retire 可达秒级,不能拖住 settle 路径)。注册点与 turn_start 相邻、位于子进程 early-return 之前,父会话/嵌套子/工具人全部生效;无 runtime 的会话上是 no-op。幂等性由既有 cascadePromise/cascadingJobs 保证:ESC 打断 in-flight 工具时工具路径先触发一次,agent_settled 再触发一次为 no-op。
|
|
82
|
+
|
|
83
|
+
## 验收
|
|
84
|
+
|
|
85
|
+
1. typecheck + unit + integration 全绿。
|
|
86
|
+
2. 新测试:wait 中途 abort → children 被 retire(mock 验证 retire 调用 + cancelWatch + release)。
|
|
87
|
+
3. 新测试:cascade 幂等(同 signal 两次触发只 retire 一次)。
|
|
88
|
+
4. 新测试:parallel 工具调用同时 abort,cascade 只跑一次。
|
|
89
|
+
5. 新测试:vexillatio collect 在 abort 时提前返回(不长等 600s)。
|
|
90
|
+
6. 新测试(v2.1):async launch 已返回后 emitSettled(true) → 子 agent 被 retire、无完成通知。
|
|
91
|
+
7. 新测试(v2.1):emitSettled(false)(正常结算)→ 不 retire,async 子继续运行。
|
|
92
|
+
|
|
93
|
+
## 锚点(以 master bc9c205 为准)
|
|
94
|
+
|
|
95
|
+
- index.ts:687-691(execute 入口 aborted 快速返回)
|
|
96
|
+
- index.ts:832-840(session_shutdown 参考——cascade 参考其形状)
|
|
97
|
+
- index.ts:2243-2280(stopAction 骨架——cascade 复用)
|
|
98
|
+
- index.ts:1890-1900(vexillatio collect)
|
|
99
|
+
- runtime.ts:481-590(waitOne interrupted 分支)
|
|
100
|
+
- master 上无 worktree 依赖,直接在主 repo 改。
|
package/docs/design.md
CHANGED
|
@@ -584,7 +584,7 @@ preset 规则:
|
|
|
584
584
|
(`src/agents/step-model.ts`),只在该步骤确实引用了 preset 时生效
|
|
585
585
|
- **响亮失败**:引用了未定义的 preset 是错误(列出已定义的 preset),绝不静默
|
|
586
586
|
回退到父会话模型。报错会指明 model 的真实来源,不把责任误归于 preset
|
|
587
|
-
- preset 刻意高于 agentOverrides
|
|
587
|
+
- preset 刻意高于 agentOverrides:通过 `agentOverrides` 钉死的角色模型(`loadCatalog`
|
|
588
588
|
会先把它们折进 `agent.model`),若 preset 放在 frontmatter 同级则对它不可达。
|
|
589
589
|
未引用 preset 的 agent 行为不变
|
|
590
590
|
- 解析逻辑抽到 `src/agents/step-model.ts`(纯函数,便于单测)
|
|
@@ -636,41 +636,37 @@ Team selection is resolved as `PI_SUBAGENTS_TEAM` > `subagents.team` >
|
|
|
636
636
|
string/object members, with `*` expanding all roles once and later object
|
|
637
637
|
members applying the normal agent-override fields. Project team entries replace
|
|
638
638
|
same-named user entries. Non-default team names are passed to child pane/tab
|
|
639
|
-
processes as `PI_SUBAGENTS_TEAM`; `/
|
|
639
|
+
processes as `PI_SUBAGENTS_TEAM`; `/legion team` manages project or user
|
|
640
640
|
settings.
|
|
641
641
|
|
|
642
642
|
### 6.4 总开关 `subagents.enabled`
|
|
643
643
|
|
|
644
|
-
解析顺序:会话内覆盖(`/
|
|
644
|
+
解析顺序:会话内覆盖(`/legion on|off`,仅内存,`/reload` 后回落) >
|
|
645
645
|
项目 `.pi/settings.json` > 用户 `~/.pi/agent/settings.json` > 默认 `true`。
|
|
646
|
+
旧 toggle 的 `flip` 与无参数 `show` 用法已由显式 `/legion on` 或
|
|
647
|
+
`/legion off` 取代;每次执行后都会显示最终状态。
|
|
646
648
|
|
|
647
|
-
关闭后:launch 拒绝(`DISABLED` 错误码,指向 `/
|
|
649
|
+
关闭后:launch 拒绝(`DISABLED` 错误码,指向 `/legion on`);
|
|
648
650
|
system prompt 不再注入 playbook/roster;bash 派发 guard 放行手动 herdr
|
|
649
651
|
命令。已有 children 不受影响——正常完成并通知,`status`/`collect`/
|
|
650
652
|
`wait`/`resume`/`steer` 继续可用。
|
|
651
653
|
|
|
652
|
-
### 6.4
|
|
654
|
+
### 6.4.1 Scout guard `subagents.scoutGuard`
|
|
653
655
|
|
|
654
|
-
|
|
656
|
+
只在主会话生效(子进程带 `PI_SUBAGENT_ROLE`,不计数、不注入)。连续 N 次
|
|
657
|
+
读类工具调用(`read`,以及可判定为只读的 bash:`cat`/`head`/`tail`/`less`/
|
|
658
|
+
`rg`/`grep`/`find`/`ls`/`stat`/`wc`/`file`/`jq`、`git show|log|diff|status|blame`、
|
|
659
|
+
不带 `-i` 的 `sed`)且中间没有成功的 `subagent` / `vexillatio` 派发时,异步
|
|
660
|
+
注入一条 follow-up 提醒,不挡住当前 `tool_call`。
|
|
655
661
|
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
| medium | planner |
|
|
660
|
-
| strong | worker, reviewer, designer |
|
|
661
|
-
|
|
662
|
-
(advisor 为 cursor kind 自带模型,不参与 pi 档位。)
|
|
663
|
-
|
|
664
|
-
流程:`/subagents-refresh-provider-models` 拉供应商目录 →
|
|
665
|
-
`/subagents-generate-profiles` 写出 `<provider>.quota`(偏省)和
|
|
666
|
-
`<provider>.quality`(偏强)→ `/subagents-load-profile` 写入
|
|
667
|
-
`~/.pi/agent/settings.json` 的 `agentOverrides`。
|
|
668
|
-
|
|
669
|
-
Profile 文件在 `~/.pi/agent/profiles/pi-legion/`。加载只替换
|
|
670
|
-
`agentOverrides`(以及 profile 里显式带的 `subagents` 键),`modelScope` /
|
|
671
|
-
`herdr` 等其它设置保留。项目 `.pi/settings.json` 覆盖用户设置。
|
|
662
|
+
```json
|
|
663
|
+
{ "subagents": { "scoutGuard": { "enabled": true, "threshold": 8, "cooldownMs": 120000 } } }
|
|
664
|
+
```
|
|
672
665
|
|
|
673
|
-
|
|
666
|
+
默认开启,`threshold` 8,`cooldownMs` 120000。提醒发出后计数清零并进入冷却;
|
|
667
|
+
冷却期内不计数、不重复提醒。成功派发立即清零并退出冷却。`edit`/`write` 和
|
|
668
|
+
写类 bash(含 `sed -i`、写重定向)清零但不进入冷却。无法判定的 bash 不计数
|
|
669
|
+
也不清零。`/scout-guard` 开启、`/no-scout-guard` 关闭,只覆盖本会话。
|
|
674
670
|
|
|
675
671
|
## 7. 借鉴 pi-subagents:三类处理
|
|
676
672
|
|
|
@@ -893,13 +889,16 @@ function countToolErrors(turn: Turn): number {
|
|
|
893
889
|
```ts
|
|
894
890
|
subagent({
|
|
895
891
|
action?: "launch" | "continue" | "steer" | "resume" | "retire"
|
|
896
|
-
| "status" | "collect" | "list",
|
|
892
|
+
| "status" | "collect" | "wait" | "list" | "stop", // 默认 launch
|
|
897
893
|
|
|
898
894
|
// launch
|
|
899
895
|
agent?: string, task?: string,
|
|
900
896
|
tasks?: Array<{ agent: string; task: string }>, // 并行
|
|
901
897
|
async?: boolean, // 默认 true
|
|
902
898
|
model?: string, cwd?: string, placement?: string,
|
|
899
|
+
worktree?: boolean, // 隔离 git worktree(默认随角色)
|
|
900
|
+
verify?: string[], // 收集时由扩展在子 worktree 里机械执行的验证命令;
|
|
901
|
+
// 首个非零退出即 reject。子 agent 无需粘贴测试输出。
|
|
903
902
|
|
|
904
903
|
// 续跑 / 控制
|
|
905
904
|
name?: string, // handle 名,如 "reviewer-1"
|
|
@@ -907,6 +906,18 @@ subagent({
|
|
|
907
906
|
})
|
|
908
907
|
```
|
|
909
908
|
|
|
909
|
+
#### `verify` 的信任链
|
|
910
|
+
|
|
911
|
+
- launch 时 `verify` 合成一条带 `command` 的 acceptance criterion(`task-verify`),快照进子记录的 `pendingCriteria`;
|
|
912
|
+
- collect 时 `applyVerification` 在子 worktree cwd 亲自执行(bash -lc,超时 SIGKILL);
|
|
913
|
+
- 全零退出 → `accepted (verified)`;首个非零 → `rejected`,reason 指名失败的命令;
|
|
914
|
+
- 未提供 `verify` 时扩展**绝不猜命令**,保持 attested 语义不变(F44 边界)。
|
|
915
|
+
|
|
916
|
+
配套设置(`~/.pi/agent/settings.json` → `subagents.legion`):
|
|
917
|
+
|
|
918
|
+
- `worktreeBootstrap`: 新建子 worktree 后自动准备依赖环境。`"npm-install"`(按 lockfile 选 pnpm/bun/yarn/npm)或任意 shell 命令(如 `"ln -sfn /path/to/node_modules ./node_modules"`)。幂等:`.pi-provisioned` 标记文件记录已应用的 mode,同 mode 重入直接跳过(resume 不重装);mode 变更视为声明变更,重新执行。
|
|
919
|
+
- `worktreeBootstrapTimeoutMs`: bootstrap 命令预算,默认 600s。失败即失败整个 launch,worktree 走既有回滚被整体移除。
|
|
920
|
+
|
|
910
921
|
### 动作语义
|
|
911
922
|
|
|
912
923
|
| action | 用途 | 底层 |
|
|
@@ -916,7 +927,15 @@ subagent({
|
|
|
916
927
|
| `steer` | **运行中插话改方向**(F10) | `agent prompt`(不等) |
|
|
917
928
|
| `resume` | **已退出后恢复**(F12) | 新 pane + `--session <file>` |
|
|
918
929
|
| `retire` | 回收 | ctrl+d / pane close / tab close |
|
|
919
|
-
| `status` / `collect` / `list` | 查询 |
|
|
930
|
+
| `status` / `collect` / `list` | 查询 | child record / session JSONL |
|
|
931
|
+
| `wait` | 等待指定或全部活动 child 完成;由 RPC settle 事件唤醒;ESC 中断时级联优雅关闭 children 后返回 | session runtime + RPC supervisor |
|
|
932
|
+
| `stop` | 优雅停止指定或全部 tracked、非 retired children | `retire` 全链 + runtime release |
|
|
933
|
+
|
|
934
|
+
### ESC abort 语义 + stop
|
|
935
|
+
|
|
936
|
+
父 turn 的 ESC / RPC abort signal 会立即打断当前工具等待,并静默级联优雅关闭该 session 所拥有的 tracked children(包括 vexillatio utility children)。`wait` 返回 `interrupted (parent turn aborted)` 前先完成级联 retire;`collect` / inline collect 被中断时也会级联关闭 children,不生成显式 stop 的工具结果文案。级联先取消 completion watch,再并行 retire 并释放 runtime tracking;并发 abort 复用同一级联 promise。新的 turn 会建立新的级联周期。正常完成的 `wait` 仍由 RPC settle 事件立即解除,不需要等满 timeout。
|
|
937
|
+
|
|
938
|
+
`stop` 是显式停止操作:`subagent({ action: "stop", name: "worker-0" })` 停一个 child,`subagent({ action: "stop", all: true })` 逐个停止当前跟踪的全部非 retired children。它复用 `retire` 生命周期(优雅关闭、超时后升级信号、后代清理)并释放 runtime 跟踪;`stop` 本身不接父 turn abort signal。`name` 与 `all` 互斥且必须指定其一。
|
|
920
939
|
|
|
921
940
|
### `continue` vs `steer` vs `resume`
|
|
922
941
|
|