@zhushanwen/pi-subagent-workflow 8.14.0 → 8.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -33,7 +33,7 @@ Pi 的 subagent + workflow 合并包:任务委派 + 多 agent 编排(chain /
33
33
 
34
34
  orchestrator 是纯协调器角色:拆解任务 → 委派 subagent → 汇总结果,自身不做执行类工作。orchestrator agent 自身也可递归委派子 orchestrator,实现分层任务拆解(深度受 `Depth: N/10` 护栏保护)。
35
35
 
36
- **工具约束变化(C1/D-5)**:内置模板不再携带 `tools:` frontmatter 白名单,subagent 不再以 `--tools` 白名单启动——工具约束回归宿主默认工具面,orchestrator 靠角色职责(职责边界段)约束自身只做协调。想要白名单的用户在 `<workspace>/.agents/agents/` 放同名 `.md` 覆写(project 级源稳定遮蔽内置,是唯一逃生门),或沿用 pi CLI 的 `--tools` 白名单(临时验证):
36
+ **工具约束(C1/D-5)**:内置模板不携带 `tools:` frontmatter 白名单,subagent 不以 `--tools` 白名单启动——工具约束回归宿主默认工具面,orchestrator 靠角色职责(职责边界段)约束自身只做协调。想要白名单的用户在 `<workspace>/.agents/agents/` 放同名 `.md` 覆写(project 级源稳定遮蔽内置,是唯一逃生门),或沿用 pi CLI 的 `--tools` 白名单(临时验证):
37
37
 
38
38
  ```bash
39
39
  pi --tools todo,goal_control,workflow,subagent,ask_user
@@ -57,14 +57,14 @@ Workflow run 是一次性执行,状态机两态:`running → done`(`done`
57
57
 
58
58
  Runs are one-shot: there is no pause/resume — to stop a run early use abort; for a fresh result start a new run.
59
59
 
60
- - **abort 是唯一的提前停止方式**:`{"action":"abort","runId":"<id>"}`(可选 `"error":"<reason>"`)。pause/resume action 已移除,调用会被 pi schema 校验拒绝(`Validation failed for tool "workflow"`);`/workflows pause|resume <id>` 返回 removed 提示
60
+ - **abort 是唯一的提前停止方式**:`{"action":"abort","runId":"<id>"}`(可选 `"error":"<reason>"`)。不存在 pause/resume action,调用会被 pi schema 校验拒绝(`Validation failed for tool "workflow"`);`/workflows pause|resume <id>` 返回 removed 提示
61
61
  - **session 切换/关闭时**,所有 running run 当即作废转 `done,failed`(state.error 为 `Session switched: run terminated` / `Session shutdown: run terminated`),已投入的 token 作废;需要结果就重新 run
62
62
  - **快照格式 `wf-run-v2`**(status 两态、无 `pausedAt`);旧 `wf-run-v1` 文件加载时静默跳过
63
63
  - **worker 崩溃自动重建重试**(默认 3 次):重建时在飞 agent 调用被清除重跑,已完成的调用保留 replay 缓存,不重复消耗 token
64
64
 
65
65
  ## 性能:sessions-index.json 持久化索引
66
66
 
67
- 冷启动首扫的 identity 探测结论持久化为 `<enc>/sessions-index.json`(stat 戳自校验、tmp(pid)+rename 原子写、60s 节流、损坏/版本不符静默回退全量探测),真实目录(1744 jsonl / 671MB)实测冷扫描中位数 972.8ms → 80.6ms(12.1x,预算 ≤300ms)。可复现验收脚本:`bench/cold-scan.bench.ts`(冷扫描计时 + 输出等价断言)、`bench/concurrent-scan.bench.ts`(3 实例并发 + 随机变异四判定),设计文档见 `.xyz-harness/2026-08-15-subagent-workflow-perf/sessions-index-design.md`。
67
+ 冷启动首扫的 identity 探测结论持久化为 `<enc>/sessions-index.json`(stat 戳自校验、tmp(pid)+rename 原子写、60s 节流、损坏/版本不符静默回退全量探测),真实目录(1744 jsonl / 671MB)实测冷扫描中位数 972.8ms → 80.6ms(12.1x,预算 ≤300ms)。可复现验收脚本:`bench/cold-scan.bench.ts`(冷扫描计时 + 输出等价断言)、`bench/concurrent-scan.bench.ts`(3 实例并发 + 随机变异四判定)。
68
68
 
69
69
  ## 安装
70
70
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhushanwen/pi-subagent-workflow",
3
- "version": "8.14.0",
3
+ "version": "8.14.1",
4
4
  "type": "module",
5
5
  "main": "index.ts",
6
6
  "description": "Unified subagent execution and multi-agent workflow orchestration for Pi — spawned-process agent runtime with sync/background modes, stateful workflow management with persistence, state machine, and execution tracing.",
@@ -29,7 +29,7 @@
29
29
  "src/injectors/",
30
30
  "src/interface/"
31
31
  ],
32
- "xyz-agent": {
32
+ "taiji": {
33
33
  "role": "universal",
34
34
  "subagentEngines": [
35
35
  "pi",
@@ -54,13 +54,13 @@
54
54
  ]
55
55
  },
56
56
  "dependencies": {
57
- "@xyz-agent/extension-protocol": "0.11.0",
58
- "@xyz-agent/session-delivery": "0.4.1",
57
+ "@zhushanwen/extension-protocol": "0.11.0",
59
58
  "@zhushanwen/pi-ext-guards": "0.4.0",
60
- "@zhushanwen/subagent-core": "0.10.1",
59
+ "@zhushanwen/session-delivery": "0.10.0",
60
+ "@zhushanwen/pi-extension-logger": "0.6.0",
61
+ "@zhushanwen/subagent-core": "0.10.2",
61
62
  "@zhushanwen/pi-subagent-cli": "0.4.0",
62
- "@zhushanwen/zcode-subagent-cli": "0.3.1",
63
- "@zhushanwen/pi-extension-logger": "0.6.0"
63
+ "@zhushanwen/zcode-subagent-cli": "0.3.1"
64
64
  },
65
65
  "peerDependencies": {
66
66
  "@earendil-works/pi-ai": "^0.84.4",
package/relay/relay.mjs CHANGED
@@ -14,9 +14,9 @@
14
14
  *
15
15
  * | relay-env.ts 常量 | 本文件常量 | 值 |
16
16
  * |---------------------------|---------------------------|-----------------------------|
17
- * | RELAY_ENV_SOCKET | RELAY_ENV_SOCKET | 'XYZ_SUBAGENT_RELAY_SOCKET' |
18
- * | RELAY_ENV_SESSION_ID | RELAY_ENV_SESSION_ID | 'XYZ_SUBAGENT_RELAY_SESSION_ID' |
19
- * | RELAY_ENV_RECORD_ID | RELAY_ENV_RECORD_ID | 'XYZ_SUBAGENT_RELAY_RECORD_ID' |
17
+ * | RELAY_ENV_SOCKET | RELAY_ENV_SOCKET | 'TAIJI_SUBAGENT_RELAY_SOCKET' |
18
+ * | RELAY_ENV_SESSION_ID | RELAY_ENV_SESSION_ID | 'TAIJI_SUBAGENT_RELAY_SESSION_ID' |
19
+ * | RELAY_ENV_RECORD_ID | RELAY_ENV_RECORD_ID | 'TAIJI_SUBAGENT_RELAY_RECORD_ID' |
20
20
  * | RELAY_PROTOCOL_VERSION | RELAY_PROTOCOL_VERSION | 1 |
21
21
  * | RELAY_EXIT_CODES.* | RELAY_EXIT_CODES.* | 10/11/12/13 |
22
22
  *
@@ -46,9 +46,9 @@ import net from "node:net";
46
46
  import os from "node:os";
47
47
 
48
48
  // ---- 镜像常量(与 relay-env.ts 逐字一致,见头部对照表)----
49
- const RELAY_ENV_SOCKET = "XYZ_SUBAGENT_RELAY_SOCKET";
50
- const RELAY_ENV_SESSION_ID = "XYZ_SUBAGENT_RELAY_SESSION_ID";
51
- const RELAY_ENV_RECORD_ID = "XYZ_SUBAGENT_RELAY_RECORD_ID";
49
+ const RELAY_ENV_SOCKET = "TAIJI_SUBAGENT_RELAY_SOCKET";
50
+ const RELAY_ENV_SESSION_ID = "TAIJI_SUBAGENT_RELAY_SESSION_ID";
51
+ const RELAY_ENV_RECORD_ID = "TAIJI_SUBAGENT_RELAY_RECORD_ID";
52
52
 
53
53
  const RELAY_PROTOCOL_VERSION = 1;
54
54
 
@@ -137,7 +137,7 @@ function connectWithRetry(socketPath) {
137
137
  fail(
138
138
  RELAY_EXIT_CODES.SOCKET_UNREACHABLE,
139
139
  `relay socket unreachable after retry: ${socketPath} (${err.message}). ` +
140
- `Recovery: 重试任务;持续失败请重启 xyz-agent(runtime 未运行或已重启)`,
140
+ `Recovery: 重试任务;持续失败请重启 taiji(runtime 未运行或已重启)`,
141
141
  );
142
142
  }
143
143
  });
@@ -146,7 +146,7 @@ function connectWithRetry(socketPath) {
146
146
  fail(
147
147
  RELAY_EXIT_CODES.SOCKET_CLOSED,
148
148
  "relay socket closed by runtime(生命线断,可能是 runtime 崩溃/重启). " +
149
- "Recovery: 重启 xyz-agent 后重试任务",
149
+ "Recovery: 重启 taiji 后重试任务",
150
150
  );
151
151
  }
152
152
  });
@@ -205,7 +205,7 @@ function handleLine(line) {
205
205
  RELAY_EXIT_CODES.VERSION_MISMATCH,
206
206
  `relay protocol version mismatch: agent v${RELAY_PROTOCOL_VERSION}, ` +
207
207
  `runtime supports ${JSON.stringify(frame.supported)}. ` +
208
- "Recovery: 升级 xyz-agent(runtime 与代理资产同包分发,版本不一致意味着安装损坏,重装应用)",
208
+ "Recovery: 升级 taiji(runtime 与代理资产同包分发,版本不一致意味着安装损坏,重装应用)",
209
209
  );
210
210
  return;
211
211
  }
@@ -16,10 +16,10 @@ config.json 位于 pi agent 目录下的 `subagents/config.json`,随环境不
16
16
  | 环境 | 路径 |
17
17
  |------|------|
18
18
  | 独立 pi CLI | `~/.pi/agent/subagents/config.json` |
19
- | xyz-agent dev | `~/.xyz-agent-dev/pi/agent/subagents/config.json` |
20
- | xyz-agent prod | `~/.xyz-agent/pi/agent/subagents/config.json` |
19
+ | taiji dev | `~/.taiji-dev/pi/agent/subagents/config.json` |
20
+ | taiji prod | `~/.taiji/pi/agent/subagents/config.json` |
21
21
 
22
- **动态推导(推荐)**:agentDir 由 pi 核心 `getAgentDir()` 决定(读 `PI_CODING_AGENT_DIR`,默认 `~/.pi/agent`);xyz-agent 通过 `XYZ_AGENT_DATA_DIR` 隔离数据目录。排查时先查这两个 env 变量组合出实际路径(`<agentDir>/subagents/config.json`),不要假设单一环境——写错环境的配置文件改了也不生效。
22
+ **动态推导(推荐)**:agentDir 由 pi 核心 `getAgentDir()` 决定(读 `PI_CODING_AGENT_DIR`,默认 `~/.pi/agent`);taiji 通过 `TAIJI_AGENT_DATA_DIR` 隔离数据目录。排查时先查这两个 env 变量组合出实际路径(`<agentDir>/subagents/config.json`),不要假设单一环境——写错环境的配置文件改了也不生效。
23
23
 
24
24
  文件不存在 / JSON 解析失败 / 字段缺失时全部回默认配置,不报错。旧版 `categories` / `fallback` / `yoloByDefault` 等字段读取时忽略(模型解析已退化为「主 agent model 优先」)。
25
25
 
@@ -86,8 +86,8 @@ config.json 位于 pi agent 目录下的 `subagents/config.json`,随环境不
86
86
 
87
87
  1. **新建 session**(必须——当前 session 不重读配置)。
88
88
  2. 让主 agent 派一个 subagent(例:用 `subagents` 工具发个简单任务)。
89
- 3. xyz-agent 侧边栏 **Agents tab** 看该项最左的引擎 icon(pi / zcode)——这是统一验证面。
90
- 4. journal 落点 `~/.xyz-agent-dev/engines/<engineId>/` **仅适用非 pi 引擎**(zcode 分支建 journal);pi 分支不建 journal,pi 任务以 icon 为验证面。
89
+ 3. taiji 侧边栏 **Agents tab** 看该项最左的引擎 icon(pi / zcode)——这是统一验证面。
90
+ 4. journal 落点 `~/.taiji-dev/engines/<engineId>/` **仅适用非 pi 引擎**(zcode 分支建 journal);pi 分支不建 journal,pi 任务以 icon 为验证面。
91
91
 
92
92
  ## 常见错误排查
93
93
 
@@ -31,7 +31,7 @@ vi.mock("@zhushanwen/pi-extension-logger", () => extensionLoggerMock);
31
31
  vi.mock("@zhushanwen/subagent-core", () => ({ getInFlightSnapshot: mockSnapshot }));
32
32
 
33
33
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
34
- import { INFLIGHT_REPORT_ACK, SUBAGENT_INFLIGHT_MARKER } from "@xyz-agent/extension-protocol";
34
+ import { INFLIGHT_REPORT_ACK, SUBAGENT_INFLIGHT_MARKER } from "@zhushanwen/extension-protocol";
35
35
 
36
36
  import { createInFlightReporter } from "../inflight-reporter.ts";
37
37
 
@@ -41,12 +41,12 @@ const sessionDeliveryMock = vi.hoisted(() => ({
41
41
  vi.mock("@earendil-works/pi-coding-agent", () => piCodingAgentMock);
42
42
  vi.mock("@zhushanwen/pi-extension-logger", () => extensionLoggerMock);
43
43
  vi.mock("@zhushanwen/pi-pending-notifications", () => pendingNotificationsMock);
44
- vi.mock("@xyz-agent/session-delivery", () => sessionDeliveryMock);
44
+ vi.mock("@zhushanwen/session-delivery", () => sessionDeliveryMock);
45
45
 
46
46
  import { getAgentDir } from "@earendil-works/pi-coding-agent";
47
47
  import { getLogger } from "@zhushanwen/pi-extension-logger";
48
48
  import { countActiveFromEntries } from "@zhushanwen/pi-pending-notifications";
49
- import { createDelivery } from "@xyz-agent/session-delivery";
49
+ import { createDelivery } from "@zhushanwen/session-delivery";
50
50
 
51
51
  import { createPiHostServices, createPiNotifyDomainPorts } from "../pi-host.ts";
52
52
 
@@ -15,7 +15,7 @@
15
15
  // ② 事件产生点在 core、上报出口在壳层——本文件即出口(core 零 pi SDK);
16
16
  // ③ marker 路由不广播前端是 u7b(runtime 侧)的约束,本文件不涉及。
17
17
  //
18
- // 环境门控(2026-09-13 oe-audit + 裸 TUI 闪框事故):上报的消费方是 xyz-agent
18
+ // 环境门控(2026-09-13 oe-audit + 裸 TUI 闪框事故):上报的消费方是 taiji
19
19
  // runtime 的 event-adapter marker 路由,而 runtime spawn pi 恒为 --mode rpc——
20
20
  // ctx.mode !== 'rpc' 即无拦截方(裸 pi TUI 下 marker select 会弹真框超时,2026-09-12
21
21
  // 实测无限闪框),此时不启动上报(ask-user channel-handler 的 mode 二值判定同款
@@ -37,7 +37,7 @@
37
37
  // (30min 有界),不丢 errs-safe 兜底。
38
38
 
39
39
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
40
- import { SUBAGENT_INFLIGHT_MARKER, callMarkerRpc, isInFlightReportAck } from "@xyz-agent/extension-protocol";
40
+ import { SUBAGENT_INFLIGHT_MARKER, callMarkerRpc, isInFlightReportAck } from "@zhushanwen/extension-protocol";
41
41
  import { getInFlightSnapshot } from "@zhushanwen/subagent-core";
42
42
  import { getLogger } from "@zhushanwen/pi-extension-logger";
43
43
  import { toErrorMessage } from "@zhushanwen/pi-ext-guards";
@@ -51,7 +51,7 @@ const SELECT_TIMEOUT_MS = 2_000;
51
51
  const RETRY_DELAY_MS = 2_000;
52
52
 
53
53
  /** 累计失败放弃上限(对齐 plugin-bridge MAX_SYNC_ATTEMPTS:60s 窗口覆盖 attach 竞态,
54
- * 有界防 rpc-but-非-xyz orchestrator 场景的永久空转)。 */
54
+ * 有界防 rpc-but-非-taiji orchestrator 场景的永久空转)。 */
55
55
  const MAX_REPORT_ATTEMPTS = 30;
56
56
 
57
57
  /** 在途上报器(组合根 index.ts 持有;per-factory 实例,session_start/shutdown 驱动)。 */
@@ -5,12 +5,12 @@
5
5
  //
6
6
  // 本文件属壳侧(shell),不进 core 切面——对 pi SDK 与 pi 宿主协作件
7
7
  // (@earendil-works/pi-coding-agent / @zhushanwen/pi-extension-logger /
8
- // @zhushanwen/pi-pending-notifications / @xyz-agent/session-delivery)的运行时
8
+ // @zhushanwen/pi-pending-notifications / @zhushanwen/session-delivery)的运行时
9
9
  // import 收敛在此层,core 闭包(D9 守卫对象)不得出现这些包。
10
10
  //
11
11
  // 端口语义:
12
12
  // - dataRoot / discoveryRoots 每次调用现取 getAgentDir(),禁止模块级缓存:
13
- // getAgentDir 尊重 PI_CODING_AGENT_DIR 实例隔离(xyz-agent 按 session dir
13
+ // getAgentDir 尊重 PI_CODING_AGENT_DIR 实例隔离(taiji 按 session dir
14
14
  // 隔离 pi 实例),缓存会把后续切换实例的进程钉死在首个 agentDir。
15
15
  // - discoveryRoots 的根清单/顺序/source 标签与 shared/resource-discovery.ts
16
16
  // buildScanTargets、orchestration/skill-discovery.ts resolveSkillPath 的现推导
@@ -22,7 +22,7 @@
22
22
  // - countActiveFromEntries 适配:pi 侧真函数返回 CountActiveResult 对象,core
23
23
  // 端口契约是 number(core 消费面只读 .count,notify-ports.ts 契约注释)——
24
24
  // foundation 单元登记给本单元的适配责任。
25
- // - createDelivery 透传:@xyz-agent/session-delivery 的 createDelivery 与 core
25
+ // - createDelivery 透传:@zhushanwen/session-delivery 的 createDelivery 与 core
26
26
  // 的 Delivery* 结构化类型逐字段结构兼容(DeliveryHandle 的 sendChecked/depth
27
27
  // 是结构超集成员,多不碍兼容)——结构兼容由本注入点 typecheck 守护,上游签名
28
28
  // 漂移即红(notify-ports.ts「闭包红线」段)。
@@ -34,7 +34,7 @@ import { fileURLToPath } from "node:url";
34
34
  import { getAgentDir } from "@earendil-works/pi-coding-agent";
35
35
  import { getLogger } from "@zhushanwen/pi-extension-logger";
36
36
  import { countActiveFromEntries } from "@zhushanwen/pi-pending-notifications";
37
- import { createDelivery } from "@xyz-agent/session-delivery";
37
+ import { createDelivery } from "@zhushanwen/session-delivery";
38
38
 
39
39
  import type { DiscoveryRoot, HostServices } from "@zhushanwen/subagent-core";
40
40
  import type { LogLevel } from "@zhushanwen/subagent-core";
@@ -120,7 +120,7 @@ function skillRoots(): DiscoveryRoot[] {
120
120
 
121
121
  /**
122
122
  * 引擎包发现根(W4 L1 第二通道,设计 §3.4)。打包态主通道 = env
123
- * XYZ_AGENT_ENGINE_ROOTS(W9 注入),此处承载 pi 宿主的常规安装路径:
123
+ * TAIJI_AGENT_ENGINE_ROOTS(W9 注入),此处承载 pi 宿主的常规安装路径:
124
124
  * 1. 宿主包自身 node_modules——引擎包(<engine>-subagent-cli)作为本包
125
125
  * dependencies 安装位(W9「扩展 package.json 声明引擎包为 dependencies」);
126
126
  * 2. <agentDir>/npm/node_modules——pi npm 安装位(org 分组二层布局由扫描器
package/src/index.ts CHANGED
@@ -163,7 +163,7 @@ export default function subagentsWorkflowExtension(pi: ExtensionAPI): void {
163
163
  setInFlightListener(inflightReporter.onInFlightChanged);
164
164
 
165
165
  // [U7b] 引擎列表在 extension 模块加载时即同步 engines.json(不等 session_start——
166
- // 用户体验拍板 2026-08-25:xyz-agent 打开后激活任意 session 的第一时间(含 TUI 等价
166
+ // 用户体验拍板 2026-08-25:taiji 打开后激活任意 session 的第一时间(含 TUI 等价
167
167
  // 场景)GUI 引擎选择器就该有数据;session_start 处保留幂等重写兜底 jiti 双路径/
168
168
  // 模块重载场景的刷新)。
169
169
  syncEnginesFile(getAgentDir());
@@ -495,7 +495,7 @@ export default function subagentsWorkflowExtension(pi: ExtensionAPI): void {
495
495
  //
496
496
  // - SIGTERM:pi 各 mode(rpc/interactive/print)自带 SIGTERM handler 负责退出编排,
497
497
  // 本 extension 的 handler 只做收割 + 设 exitCode(不 re-raise、不抢 pi 的退出语义;
498
- // xyz-agent 桌面 supervisor 用 SIGTERM 杀 pi 走这条路)。
498
+ // taiji 桌面 supervisor 用 SIGTERM 杀 pi 走这条路)。
499
499
  // - SIGINT:pi 本体不注册常规 SIGINT handler(interactive/print/rpc 均 SIGTERM only),
500
500
  // 依赖 Node 默认终止。本 extension 注册 listener 即取消默认终止——若只设 exitCode,
501
501
  // 本地 pi CLI 的 Ctrl-C 杀不死进程(TUI/stdin/agent loop 仍在事件循环)。故收割
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * subagent / workflow 两个资源清单 injector 的同构骨架单实现(原两文件逐字同构的
5
5
  * 缓存对 / 唯一写点 / 发现函数 / 三 handler 收敛于此,改 fallback 策略只改一处):
6
- * - 缓存对:entries + 渲染快照。per-process = per-session——xyz-agent session-pool
6
+ * - 缓存对:entries + 渲染快照。per-process = per-session——taiji session-pool
7
7
  * 模型下每 pi 子进程 = 一 session = 独立扩展实例,闭包级缓存天然 per-session 隔离
8
8
  * (split mode 多 session 各自独立进程)。渲染快照与数据缓存同步更新:before_agent_start
9
9
  * 每个 turn 都要注入,format(escapeXml 多趟正则 × 全部字段)在数据不变时输出完全
@@ -135,21 +135,24 @@ describe("C5③ run 内置名(fake registry)", () => {
135
135
  expect(vi.mocked(runWorkflow)).not.toHaveBeenCalled();
136
136
  });
137
137
 
138
- it("两者都 miss → 报错文案与改造前一致(含建议清单与 location 指引)", async () => {
138
+ it("两者都 miss → 报错含建议清单且逐条附绝对路径 location(按名失败后的自救指引)", async () => {
139
139
  const registry = {
140
140
  get: vi.fn().mockResolvedValue(undefined),
141
141
  getPath: vi.fn().mockResolvedValue(undefined),
142
142
  loadAll: vi.fn().mockResolvedValue([makeScript("chain", "/builtin/workflows/chain.js")]),
143
143
  };
144
- await expect(
145
- actionRun(
146
- { action: "run", name: "no-such" } as never,
147
- makeDeps(registry) as never,
148
- undefined,
149
- ),
150
- ).rejects.toThrow(
151
- "Workflow 'no-such' not found. Available:\n - chain: chain workflow\nUse <location> from <available_workflows> for the absolute .js path.",
144
+ const err = await actionRun(
145
+ { action: "run", name: "no-such" } as never,
146
+ makeDeps(registry) as never,
147
+ undefined,
148
+ ).catch((e: unknown) => e as Error);
149
+ // 逐条 location:模型按清单里的名字重试注定再 throw(8.6.0 getPath-only 时代
150
+ // 实测失败形态)——摘要必须直接给出可用的绝对路径,失败一次即自救。
151
+ expect(err.message).toContain(
152
+ "Workflow 'no-such' not found. Available (name — use the absolute location path as 'name' when the bare name is rejected):",
152
153
  );
154
+ expect(err.message).toContain(" - chain: chain workflow");
155
+ expect(err.message).toContain(" location: /builtin/workflows/chain.js");
153
156
  });
154
157
  });
155
158
 
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * command-actions — RPC 模式 slash command action 解析纯函数。
3
3
  *
4
- * xyz-agent GUI 通过 `client.prompt("/subagents cancel <id>")` 等触发生命周期操作,
4
+ * taiji GUI 通过 `client.prompt("/subagents cancel <id>")` 等触发生命周期操作,
5
5
  * 不经 LLM(pi 的 _tryExecuteExtensionCommand 在 agent loop 前短路)。command handler
6
6
  * 在 RPC 模式下用这两个函数解析 action 字符串,分发到对应 service/lifecycle 调用。
7
7
  *
@@ -96,7 +96,7 @@ export function registerWorkflowsCommand(
96
96
  return null;
97
97
  },
98
98
  handler: async (args: string, ctx: ExtensionCommandContext) => {
99
- // ── RPC 模式(xyz-agent GUI):解析 lifecycle action 直接执行,不打开 TUI ──
99
+ // ── RPC 模式(taiji GUI):解析 lifecycle action 直接执行,不打开 TUI ──
100
100
  // hasUI 在 TUI 和 RPC 都为 true,不能用于区分;用 ctx.mode === "rpc" 判定 GUI 通道。
101
101
  if (ctx.mode === "rpc") {
102
102
  await handleRpcMode(parseWorkflowRpcCommand(args), ctx, deps);
@@ -125,7 +125,7 @@ export function registerWorkflowsCommand(
125
125
  // ── Helpers ──────────────────────────────────────────────────
126
126
 
127
127
  /**
128
- * RPC 模式分支(xyz-agent GUI):解析后的 lifecycle action 直接执行,不打开 TUI。
128
+ * RPC 模式分支(taiji GUI):解析后的 lifecycle action 直接执行,不打开 TUI。
129
129
  *
130
130
  * 各 action 语义:
131
131
  * - abort → 调 lifecycle abortRun,成功/失败均 notify(不向上抛)
@@ -13,7 +13,7 @@ import os from "node:os";
13
13
 
14
14
  import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
15
15
 
16
- import { firstContentText } from "@xyz-agent/extension-protocol";
16
+ import { firstContentText } from "@zhushanwen/extension-protocol";
17
17
 
18
18
  import type { AgentEventLogEntry, DisplayItem, ExecutionStatus } from "@zhushanwen/subagent-core";
19
19
  import { DEFAULT_AGENT_NAME } from "@zhushanwen/subagent-core";
@@ -1,14 +1,14 @@
1
1
  /**
2
2
  * GUI 协议映射辅助函数 —— run/subagent 状态字符串 → 协议 TreeItem 状态 + 图标。
3
3
  *
4
- * 协议包 @xyz-agent/extension-protocol 的 list-tree 组件用 TreeItem.status(三态)
4
+ * 协议包 @zhushanwen/extension-protocol 的 list-tree 组件用 TreeItem.status(三态)
5
5
  * + TreeItem.icon 表达运行态。本模块把 workflow/subagent 领域的丰富状态字符串收口
6
6
  * 到这两个枚举,供 helpers.ts / tool-workflow.ts / subagent-actions.ts 复用。
7
7
  *
8
- * 参考:@xyz-agent/extension-protocol GuiComponentProps['list-tree']。
8
+ * 参考:@zhushanwen/extension-protocol GuiComponentProps['list-tree']。
9
9
  */
10
10
 
11
- import type { GuiContext, TreeItem, TreeItemIcon } from "@xyz-agent/extension-protocol";
11
+ import type { GuiContext, TreeItem, TreeItemIcon } from "@zhushanwen/extension-protocol";
12
12
 
13
13
  /**
14
14
  * 从 Pi ExtensionContext 构造协议 GuiContext 的最小子集。
@@ -26,7 +26,7 @@ import {
26
26
  type GuiRenderResult,
27
27
  guiResult,
28
28
  isGuiCapable,
29
- } from "@xyz-agent/extension-protocol";
29
+ } from "@zhushanwen/extension-protocol";
30
30
  import { mapRunIcon, mapRunStatus } from "./gui-mappers.ts";
31
31
 
32
32
  // ── 常量 ─────────────────────────────────────────────────────
@@ -19,7 +19,7 @@ import {
19
19
  type GuiContext,
20
20
  guiResult,
21
21
  isGuiCapable,
22
- } from "@xyz-agent/extension-protocol";
22
+ } from "@zhushanwen/extension-protocol";
23
23
 
24
24
  import type {
25
25
  CancelHandlerResult,
@@ -95,7 +95,7 @@ export const SubagentParams = Type.Object({
95
95
  "Idle-recycle cadence for ALL subagents (modeless: every subagent stays continuable — this is NOT a mode switch). Controls how long an idle subagent (between rounds, no activity) stays before being automatically archived. " +
96
96
  "Default: 300000 (5min). Raise it for long-interval collaboration where your next message may arrive more than 5min after a round ends. " +
97
97
  "Pass 0 or a negative value to DISABLE idle recycling entirely (subagent stays available until you close it). " +
98
- "Priority: this param > env XYZ_SUBAGENT_IDLE_TIMEOUT_MS > default.",
98
+ "Priority: this param > env TAIJI_SUBAGENT_IDLE_TIMEOUT_MS > default.",
99
99
  })),
100
100
  engine: Type.Optional(StringEnum(["pi", "zcode"], {
101
101
  description:
@@ -173,7 +173,7 @@ export const SubagentParams = Type.Object({
173
173
  // 源文件只读不续写);旧记录/状态机不动。pi 引擎限定(非 pi 在 execute 层拒绝)。
174
174
  forkFromParam: Type.Optional(Type.Object({
175
175
  sourceSubagentId: Type.String({
176
- description: "REQUIRED for action:'fork-from'. The OLD subagentId whose conversation history becomes the inherited context of the new subagent. Works for any idle record — disconnected by a session restart, already finished, or previously closed/cancelled. Rejections: still-running sources (message them instead), sources held by another live process, worktree-bound sources, and unknown ids; an unparseable history anchor is guided to action:'message' (same-id reopen) instead.",
176
+ description: "REQUIRED for action:'fork-from'. The OLD subagentId whose conversation history becomes the inherited context of the new subagent. Works for any idle record — disconnected by a session restart, already finished, or previously closed/cancelled. Rejections: still-running sources (message them instead; sync-collect members cannot be messaged — close them first, then fork-from), sources held by another live process, worktree-bound sources, and unknown ids; an unparseable history anchor is guided to action:'message' (same-id reopen) instead.",
177
177
  }),
178
178
  prompt: Type.Optional(Type.String({
179
179
  description: "Continuation instruction for the new subagent (what to do next on top of the inherited history). When omitted, a standard handover frame is injected: reconstruct done/decided/remaining from the inherited history, then continue to completion. Whitespace-only treated as omitted.",
@@ -207,7 +207,7 @@ Items over budget are truncated with a pointer: session_read {"action":"result",
207
207
  ## Continuing a subagent (modeless)
208
208
 
209
209
  Every subagent stays continuable — no mode switch: action:"message" revives an idle record in place (or joins a running one's round), action:"fork-from" branches a new subagent from old history, action:"close" archives it.
210
- idleTimeoutMs: idle-recycle cadence for ALL subagents — idle records auto-archive on expiry (default 300000 / 5min; 0/negative disables; env XYZ_SUBAGENT_IDLE_TIMEOUT_MS: global default, per-call wins).
210
+ idleTimeoutMs: idle-recycle cadence for ALL subagents — idle records auto-archive on expiry (default 300000 / 5min; 0/negative disables; env TAIJI_SUBAGENT_IDLE_TIMEOUT_MS: global default, per-call wins).
211
211
 
212
212
  ## You cannot
213
213
 
@@ -240,7 +240,7 @@ Do NOT recurse when: the work is linear/flat; the child needs your context; or y
240
240
 
241
241
  // ponytail: renderCall 每次 TUI invalidate 都触发。streaming 中 args 是 partial JSON
242
242
  // 解析结果(如 model="deep" 来自未流完的 "deepseek-router/ds-pro"),解析失败是预期。
243
- // 不走 appendEntry(非真实错误),只走 logger.debug(默认 no-op,XYZ_AGENT_DEBUG=1 写文件)。
243
+ // 不走 appendEntry(非真实错误),只走 logger.debug(默认 no-op,TAIJI_AGENT_DEBUG=1 写文件)。
244
244
  const renderCallLogger = getLogger("subagents");
245
245
 
246
246
  const subagentRenderCall: SubagentRenderCallCb = (args, theme, ctx) => {
@@ -260,7 +260,7 @@ const subagentRenderCall: SubagentRenderCallCb = (args, theme, ctx) => {
260
260
  if (r) resolved = { model: `${r.model.provider}/${r.model.id}`, thinkingLevel: r.thinkingLevel };
261
261
  } catch (err) {
262
262
  // streaming 中间态(partial JSON)或 service 未就绪 → 降级不显示 model(renderCall 不应崩)。
263
- // 不阻断渲染,不污染 TUI。开发期开 XYZ_AGENT_DEBUG=1 可写文件日志排查。
263
+ // 不阻断渲染,不污染 TUI。开发期开 TAIJI_AGENT_DEBUG=1 可写文件日志排查。
264
264
  renderCallLogger.debug("renderCall model resolution failed, degrading", {
265
265
  reason: toErrorMessage(err),
266
266
  });
@@ -3,7 +3,7 @@
3
3
  // /subagents 命令。薄壳——打开 list overlay(等同原 /subagents list [<id>])。
4
4
  //
5
5
  // 解析:args[0] 直接作可选 <id>(聚焦该 record)。
6
- // RPC 模式(xyz-agent GUI):解析 cancel/message/start action 直接执行,不打开 TUI。
6
+ // RPC 模式(taiji GUI):解析 cancel/message/start action 直接执行,不打开 TUI。
7
7
  // message/start 为 GUI 定向消息通道(设计 §3.3.3):GUI 经 client.prompt 短路
8
8
  // extension 命令(不经主 agent LLM),TUI 分支不消费这两个 verb(行为零变化)。
9
9
 
@@ -129,14 +129,14 @@ async function rpcStart(
129
129
  task: string,
130
130
  ctx: ExtensionCommandContext,
131
131
  ): Promise<void> {
132
- // GUI 定向新建(设计 §3.3.3):conversation 固定 true(GUI 定向对话场景需要可续聊)
132
+ // GUI 定向新建(设计 §3.3.3):万物可续(modeless),start 无续聊参数——
133
+ // 续聊资格由引擎 conversation 能力轴在 message 面把关。
133
134
  try {
134
135
  const result = await startHandler(
135
136
  service,
136
137
  {
137
138
  slug,
138
139
  task,
139
- conversation: true,
140
140
  },
141
141
  // RPC 命令无外层 AbortSignal(GUI 请求生命周期不映射到 subagent 取消——
142
142
  // start 是 detached 后台语义,取消走 /subagents cancel)
@@ -156,7 +156,7 @@ async function rpcStart(
156
156
  }
157
157
 
158
158
  /**
159
- * RPC 模式(xyz-agent GUI):解析后的 action 分发执行,不打开 TUI。
159
+ * RPC 模式(taiji GUI):解析后的 action 分发执行,不打开 TUI。
160
160
  * 行为等价拆分自 handler(fallow 圈复杂度 21 > 15):三个执行体
161
161
  * (cancel/message/start)各自成函数,本函数只做 switch 分发 +
162
162
  * usage notify + exhaustiveness 断言。
@@ -252,7 +252,7 @@ export function registerSubagentsCommand(pi: ExtensionAPI): void {
252
252
  return;
253
253
  }
254
254
 
255
- // ── RPC 模式(xyz-agent GUI):解析 action 直接执行,不打开 TUI ──
255
+ // ── RPC 模式(taiji GUI):解析 action 直接执行,不打开 TUI ──
256
256
  // hasUI 在 TUI 和 RPC 都为 true,不能用于区分;用 ctx.mode === "rpc" 判定 GUI 通道。
257
257
  if (ctx.mode === "rpc") {
258
258
  await executeRpcAction(pi, service, parseSubagentRpcCommand(argsStr), ctx);
@@ -32,7 +32,7 @@ import {
32
32
  type GuiRenderResult,
33
33
  guiResult,
34
34
  isGuiCapable,
35
- } from "@xyz-agent/extension-protocol";
35
+ } from "@zhushanwen/extension-protocol";
36
36
  // C5②/C5⑦:创作闭环统一走 core barrel(generateWorkflowScript/saveWorkflow/
37
37
  // deleteWorkflow/lintScript 均为 barrel 导出面;深路径在 npm/vendored 形态不可达)
38
38
  import {
@@ -28,7 +28,7 @@ import {
28
28
  type GuiRenderResult,
29
29
  guiResult,
30
30
  isGuiCapable,
31
- } from "@xyz-agent/extension-protocol";
31
+ } from "@zhushanwen/extension-protocol";
32
32
  import { type Static, Type } from "typebox";
33
33
 
34
34
  import { SLUG_MAX_LENGTH } from "@zhushanwen/subagent-core";
@@ -393,10 +393,16 @@ export async function actionRun(
393
393
  const all = await deps.registry.loadAll();
394
394
  const available = all.filter((wf) => wf.available);
395
395
  const suggestions = available
396
- .map((wf) => ` - ${wf.name}: ${wf.meta.description || "(no description)"}`)
396
+ .map((wf) => ` - ${wf.name}: ${wf.meta.description || "(no description)"}\n location: ${wf.path}`)
397
397
  .join("\n");
398
+ // [按名解析自救指引] 摘要逐条附绝对路径 location:run 的 name 形参最贴近的
399
+ // 读取面就是本清单(<available_workflows> 注入面在 start 时已过时/可能不在
400
+ // 上下文)——模型按清单里的名字重试(8.6.0 实装 getPath-only 时代的实测失败
401
+ // 形态,2026-09-14 嵌套 subagent 现场)还是注定 throw;带上 location 后失败
402
+ // 一次即可按绝对路径自救。main 的 C5③ 按名解析(registry.get 内置/已保存名
403
+ // 优先)是主修复,本行是清单侧恢复指引闭环。
398
404
  throw new Error(
399
- `Workflow '${name}' not found. Available:\n${suggestions || " (none)"}\nUse <location> from <available_workflows> for the absolute .js path.`,
405
+ `Workflow '${name}' not found. Available (name — use the absolute location path as 'name' when the bare name is rejected):\n${suggestions || " (none)"}`,
400
406
  );
401
407
  }
402
408
 
@@ -248,11 +248,11 @@ const DEFAULT_SAVE_DEBOUNCE_MS = 200;
248
248
  * - 非法值(非有限数/≤0)→ 不清理(显式 opt-out 通道:用户意图不明时不动
249
249
  * 磁盘,对齐 prune 内部「任何失败都不抛」的保守哲学)。
250
250
  *
251
- * 用 XYZ_ 前缀而非 PI_:本 env 是 pi 进程内读的配置 env,xyz-agent 桌面 spawn 链按
252
- * ENV_WHITELIST_PREFIXES(只有 XYZ_ 等)过滤,PI_ 前缀在桌面场景被静默丢弃——
253
- * 同 XYZ_SUBAGENT_IDLE_TIMEOUT_MS 的改名教训(lifecycle-manager.ts)。
251
+ * 用 TAIJI_ 前缀而非 PI_:本 env 是 pi 进程内读的配置 env,taiji 桌面 spawn 链按
252
+ * ENV_WHITELIST_PREFIXES(只有 TAIJI_ 等)过滤,PI_ 前缀在桌面场景被静默丢弃——
253
+ * 同 TAIJI_SUBAGENT_IDLE_TIMEOUT_MS 的改名教训(lifecycle-manager.ts)。
254
254
  */
255
- export const STATE_MAX_RUNS_ENV = "XYZ_SUBAGENT_STATE_MAX_RUNS";
255
+ export const STATE_MAX_RUNS_ENV = "TAIJI_SUBAGENT_STATE_MAX_RUNS";
256
256
 
257
257
  /** 解析保留上限;env 未设/空 → 默认上限,显式非法/≤0 → undefined(不清理)。 */
258
258
  function getEnvStateMaxRuns(): number | undefined {
@@ -230,7 +230,7 @@ function createOrReuseServices(pi: ExtensionAPI, ctx: ExtensionContext): Service
230
230
  // 注入 ctx.ui.setWidget 作为 streaming sink(只绑方法,不持有整个 ctx)。
231
231
  // background subagent 执行期间,text_delta 经 SubagentStream 合并后由此通道转发。
232
232
  // [W1 修复] ctx.mode === 'rpc' 守卫:TUI/json/print 下 streamSink = undefined(无 widget 噪音),
233
- // rpc mode(GUI/xyz-agent)下保持原行为(ctx.ui.setWidget → sidecar → chatStore)。
233
+ // rpc mode(GUI/taiji)下保持原行为(ctx.ui.setWidget → sidecar → chatStore)。
234
234
  // streamSink API 不变(SubagentStream.onDelta 仍可调,只是 TUI 下 stream 不会被创建)。
235
235
  streamSink: ctx.mode === "rpc"
236
236
  ? { setWidget: (key, lines) => ctx.ui.setWidget(key, lines) }