@zhushanwen/pi-subagent-workflow 0.1.0 → 0.2.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.
Files changed (76) hide show
  1. package/agents/context-builder.md +1 -3
  2. package/agents/oracle.md +2 -2
  3. package/agents/planner.md +1 -3
  4. package/agents/researcher.md +0 -2
  5. package/agents/reviewer.md +2 -2
  6. package/agents/scout.md +13 -3
  7. package/agents/worker.md +0 -2
  8. package/package.json +5 -3
  9. package/skills/workflow-script-format/SKILL.md +6 -6
  10. package/src/execution/__tests__/agent-result-mapper.test.ts +24 -2
  11. package/src/execution/__tests__/concurrency-pool.test.ts +33 -0
  12. package/src/execution/__tests__/execute-options-mapper.test.ts +40 -8
  13. package/src/execution/__tests__/gui-mode-dispatch.test.ts +60 -0
  14. package/src/execution/__tests__/sdk-contract.test.ts +5 -2
  15. package/src/execution/__tests__/session-reconstructor.test.ts +20 -0
  16. package/src/execution/__tests__/subagent-service-abort.test.ts +60 -0
  17. package/src/execution/__tests__/subprocess-agent-runner.test.ts +72 -3
  18. package/src/execution/__tests__/tool-action.test.ts +26 -4
  19. package/src/execution/agent-result-mapper.ts +4 -1
  20. package/src/execution/concurrency-pool.ts +38 -6
  21. package/src/execution/execute-options-mapper.ts +21 -4
  22. package/src/execution/execution-record.ts +5 -0
  23. package/src/execution/record-store.ts +2 -0
  24. package/src/execution/session-reconstructor.ts +11 -0
  25. package/src/execution/session-runner.ts +12 -0
  26. package/src/execution/stream-sink.ts +83 -0
  27. package/src/execution/subagent-service.ts +68 -43
  28. package/src/execution/subprocess-agent-runner.ts +16 -4
  29. package/src/execution/types.ts +23 -3
  30. package/src/index.ts +15 -2
  31. package/src/interface/__tests__/subagent-tool-prompt.test.ts +84 -0
  32. package/src/interface/__tests__/workflow-state-file-exposure.test.ts +38 -0
  33. package/src/interface/__tests__/workflow-tool-prompt.test.ts +50 -0
  34. package/src/interface/command-actions.ts +77 -0
  35. package/src/interface/commands.ts +40 -4
  36. package/src/interface/gui-mappers.ts +83 -0
  37. package/src/interface/helpers.ts +52 -9
  38. package/src/interface/list-component.ts +3 -1
  39. package/src/interface/subagent-actions.ts +35 -22
  40. package/src/interface/subagent-tool.ts +54 -23
  41. package/src/interface/subagents.ts +45 -5
  42. package/src/interface/tool-render.ts +16 -5
  43. package/src/interface/tool-workflow-script.ts +113 -15
  44. package/src/interface/tool-workflow.ts +92 -34
  45. package/src/interface/views/WorkflowsView.ts +13 -4
  46. package/src/interface/views/__tests__/detail-content-session-file.test.ts +70 -0
  47. package/src/interface/views/detail-content.ts +20 -0
  48. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +208 -0
  49. package/src/orchestration/__tests__/agent-call-stream.test.ts +157 -0
  50. package/src/orchestration/__tests__/error-recovery-handlers.test.ts +2 -0
  51. package/src/orchestration/__tests__/execute-agent-call.test.ts +171 -0
  52. package/src/orchestration/__tests__/jsonl-run-store-session-file.test.ts +177 -0
  53. package/src/orchestration/__tests__/worker-script-builder.test.ts +15 -0
  54. package/src/orchestration/agent-opts-resolver.ts +11 -2
  55. package/src/orchestration/error-recovery.ts +131 -23
  56. package/src/orchestration/execute-agent-call.ts +12 -3
  57. package/src/orchestration/jsonl-run-store.ts +10 -0
  58. package/src/orchestration/lifecycle.ts +1 -1
  59. package/src/orchestration/models/agent-call.ts +7 -0
  60. package/src/orchestration/models/ports.ts +15 -2
  61. package/src/orchestration/models/run-spec.ts +6 -0
  62. package/src/orchestration/models/trace.ts +1 -0
  63. package/src/orchestration/models/types.ts +19 -0
  64. package/src/orchestration/node-ops.ts +2 -0
  65. package/src/orchestration/worker-script-builder.ts +1 -0
  66. package/workflows/README.md +58 -0
  67. package/workflows/chain.js +107 -0
  68. package/workflows/map-reduce.js +142 -0
  69. package/workflows/parallel.js +131 -0
  70. package/workflows/scatter-gather.js +146 -0
  71. package/examples/README.md +0 -43
  72. package/examples/chain.example.js +0 -92
  73. package/examples/map-reduce.example.js +0 -99
  74. package/examples/parallel.example.js +0 -82
  75. package/examples/scatter-gather.example.js +0 -106
  76. package/src/interface/gui-adapter.ts +0 -136
@@ -1,43 +0,0 @@
1
- # Workflow Examples
2
-
3
- 4 个预制编排模板,展示 `workflow()` 嵌套调用 + `parallel()`/`pipeline()` 组合模式。
4
-
5
- ## 文件清单
6
-
7
- | 模板 | 模式 | 说明 |
8
- |------|------|------|
9
- | `chain.example.js` | 顺序编排(UC-1) | `workflow("extract") → workflow("transform") → workflow("load") → agent(verify)`,每步输出作下步输入 |
10
- | `parallel.example.js` | 并行扇出(UC-2) | `parallel()` 同时跑多个独立 `agent()`,适合无依赖任务并发 |
11
- | `scatter-gather.example.js` | 分散-聚合(UC-3) | `workflow("split")` 分片 → `parallel()` 并行处理 → `workflow("merge")` 聚合 |
12
- | `map-reduce.example.js` | Map-Reduce(UC-4) | `workflow("map")` 映射 → `workflow("reduce")` 归约 |
13
-
14
- ## ⚠️ 这些是模板,不能直接运行
15
-
16
- 每个模板调用的子 workflow(如 `workflow("extract")`、`workflow("split")`)**未在本文件内定义**。运行前你必须:
17
-
18
- 1. **复制模板到 workflows 目录**:
19
- ```bash
20
- cp chain.example.js ~/.pi/agent/workflows/chain.js
21
- # 或项目级:cp chain.example.js .pi/workflows/chain.js
22
- ```
23
-
24
- 2. **定义被引用的子 workflow**:在 workflows 目录下创建 `extract.js`、`transform.js`、`load.js` 等,每个含 `meta` + `execute()` + `agent()`/`parallel()`/`pipeline()` 入口。
25
-
26
- 3. **运行**:
27
- ```bash
28
- workflow run chain --args inputPath=/path/to/input.json
29
- ```
30
-
31
- ## 嵌套 workflow() 说明
32
-
33
- `workflow("name", args)` 在 worker 线程内调用另一个已注册的 workflow(详见 SKILL.md `workflow()` section):
34
-
35
- - **返回值**:`AgentResult`——成功 `{ content, parsedOutput? }`,失败 `{ content: "", error }`
36
- - **循环检测**:自动追踪调用链(A→B→A 立即拒绝)
37
- - **预算继承**:子 workflow 继承父剩余预算,消耗累加回父
38
- - **并发配额**:嵌套按 depth 分层分配(`max(1, 6-depth)`),保底 1 槽防饿死
39
-
40
- ## 相关文档
41
-
42
- - `skills/workflow-script-format/SKILL.md` — workflow script 完整 API
43
- - `docs/adr/030-subagents-workflow-merge.md` — 合并决策(决策 3 嵌套护栏)
@@ -1,92 +0,0 @@
1
- // chain.example.js — 顺序编排模板(UC-1)
2
- //
3
- // 模式:workflow("step-a") → workflow("step-b") → workflow("step-c") → agent(verify)
4
- // 每步输出作下步输入。展示 workflow() 顺序嵌套调用 + try-catch 错误处理。
5
- //
6
- // 用法:复制本文件到 .pi/workflows/ 或 ~/.pi/agent/workflows/,改 workflow 名后:
7
- // workflow run chain --args inputPath=/path/to/input.json
8
- //
9
- // ⚠️ lintScript 约束(本模板已遵守):
10
- // - 必须含 agent()/parallel()/pipeline() 入口之一(本模板末端 agent verify 兼满足 + 真实模式)
11
- // - 禁止 bare IIFE(用 top-level await)
12
- // - 禁止用 result 作变量名(lintScript 对 result 变量的 .output / .parsedOutput / .content 访问报 error)
13
-
14
- const meta = {
15
- name: "chain",
16
- description: "顺序编排模板:workflow A→B→C,每步输出作下步输入,末端 agent 校验",
17
- phases: ["extract", "transform", "load", "verify"],
18
- };
19
-
20
- const fs = require("fs");
21
-
22
- // ── 入参($ARGS)──────────────────────────────────────────────────
23
- const inputPath = $ARGS.inputPath;
24
- if (!inputPath) {
25
- // 缺参直接 throw(在 try 外,不会被 catch 吞掉,workflow 引擎报清晰错误)
26
- throw new Error("chain 缺少必需参数 inputPath。用法:workflow run chain --args inputPath=/path/to/input.json");
27
- }
28
- if (!fs.existsSync(inputPath)) {
29
- throw new Error("inputPath 不存在: " + inputPath);
30
- }
31
-
32
- log("chain 开始,inputPath=" + inputPath);
33
-
34
- let currentPhase = "init";
35
- let outcome;
36
-
37
- try {
38
- // ── 段 1:extract(workflow 嵌套调用)─────────────────────────────
39
- phase("extract");
40
- currentPhase = "extract";
41
- // workflow() 返回 AgentResult { content, parsedOutput?, usage?, error? }
42
- // 调用名为 "extract" 的子 workflow,传入 inputPath
43
- const a = await workflow("extract", { source: inputPath });
44
- if (a.error) throw new Error("extract 失败: " + a.error);
45
-
46
- // ── 段 2:transform(a.content 作下步输入)────────────────────────
47
- phase("transform");
48
- currentPhase = "transform";
49
- const b = await workflow("transform", { raw: a.content });
50
- if (b.error) throw new Error("transform 失败: " + b.error);
51
-
52
- // ── 段 3:load(b.content 作下步输入)──────────────────────────────
53
- phase("load");
54
- currentPhase = "load";
55
- const c = await workflow("load", { normalized: b.content });
56
- if (c.error) throw new Error("load 失败: " + c.error);
57
-
58
- // ── 段 4:verify(agent 校验,兼满足 lintScript entry-point)──────
59
- phase("verify");
60
- currentPhase = "verify";
61
- const verify = await agent({
62
- prompt: "校验以下 chain 输出是否完整、无遗漏关键字段。输出 valid + summary。\n\n" + c.content,
63
- schema: {
64
- type: "object",
65
- properties: {
66
- valid: { type: "boolean", description: "chain 输出是否通过校验" },
67
- summary: { type: "string", description: "校验摘要" },
68
- },
69
- required: ["valid", "summary"],
70
- },
71
- description: "chain-verify",
72
- });
73
- if (!verify.valid) throw new Error("chain verify 失败: " + verify.summary);
74
-
75
- outcome = {
76
- status: "ok",
77
- phase: currentPhase,
78
- final: c.content,
79
- verify: verify.summary,
80
- message: "chain 完成:extract → transform → load → verify 全绿",
81
- };
82
- } catch (err) {
83
- // 错误处理:返回 error 对象,不 crash(workflow 引擎据 status 决策,非 throw 中断)
84
- outcome = {
85
- status: "error",
86
- phase: currentPhase,
87
- error: err && err.message ? err.message : String(err),
88
- message: "chain 在 " + currentPhase + " 段失败",
89
- };
90
- }
91
-
92
- return outcome;
@@ -1,99 +0,0 @@
1
- // map-reduce.example.js — 映射-归约模板(UC-4)
2
- //
3
- // 模式(两段):
4
- // 段 1 map: parallel(items.map(i => workflow("map", {item:i}))) → mapped[]
5
- // 段 2 reduce: workflow("reduce", mapped) → 归约结果
6
- //
7
- // 与 scatter-gather 的区别:scatter-gather 强调数据分片(split 决定分片数);
8
- // map-reduce 强调对固定 items 数组的变换+聚合(items 已知,map 变换、reduce 聚合)。
9
- //
10
- // 用法:复制本文件到 .pi/workflows/ 或 ~/.pi/agent/workflows/,改 workflow 名后:
11
- // workflow run map-reduce --args itemsPath=/path/to/items.json
12
- //
13
- // ⚠️ lintScript 约束(本模板已遵守):
14
- // - 含 parallel() 入口(兼展示 workflow() 嵌套:map + reduce)
15
- // - 禁止 bare IIFE / 禁止变量名 result
16
-
17
- const meta = {
18
- name: "map-reduce",
19
- description: "映射-归约模板:parallel map → reduce 两段",
20
- phases: ["map", "reduce"],
21
- };
22
-
23
- const fs = require("fs");
24
-
25
- // ── 入参($ARGS)──────────────────────────────────────────────────
26
- const itemsPath = $ARGS.itemsPath;
27
- if (!itemsPath) {
28
- throw new Error("map-reduce 缺少必需参数 itemsPath。用法:workflow run map-reduce --args itemsPath=/path/to/items.json");
29
- }
30
- if (!fs.existsSync(itemsPath)) {
31
- throw new Error("itemsPath 不存在: " + itemsPath);
32
- }
33
-
34
- log("map-reduce 开始,itemsPath=" + itemsPath);
35
-
36
- let currentPhase = "init";
37
- let outcome;
38
-
39
- try {
40
- // 读 items 数组(外部数据,非 workflow 状态文件)
41
- const rawItems = JSON.parse(fs.readFileSync(itemsPath, "utf-8"));
42
- if (!Array.isArray(rawItems) || rawItems.length === 0) {
43
- throw new Error("itemsPath 内容非数组或为空");
44
- }
45
- log("读入 " + rawItems.length + " 个 item");
46
-
47
- // ── 段 1:map(parallel 对每个 item 并行变换)────────────────────
48
- phase("map");
49
- currentPhase = "map";
50
- // parallel() allSettled 语义:单个 map 失败不 reject
51
- const mappedRaw = await parallel(
52
- rawItems.map((item, idx) => workflow("map", { item, itemIndex: idx })),
53
- );
54
-
55
- const mapped = [];
56
- let mapFailed = 0;
57
- for (let i = 0; i < mappedRaw.length; i++) {
58
- const m = mappedRaw[i];
59
- if (!m || m.error) {
60
- // map 失败的 item 用 error 占位传入 reduce,由 reduce 决定跳过还是报错
61
- mapped.push({ itemIndex: i, status: "failed", error: m ? m.error : "无返回" });
62
- mapFailed++;
63
- } else {
64
- mapped.push({ itemIndex: i, status: "ok", content: m.content });
65
- }
66
- }
67
- if (mapFailed === rawItems.length) {
68
- throw new Error("全部 map 失败(" + mapFailed + "/" + rawItems.length + ")");
69
- }
70
- log("map 完成:ok=" + (rawItems.length - mapFailed) + " failed=" + mapFailed);
71
-
72
- // ── 段 2:reduce(workflow 聚合所有 map 结果)───────────────────
73
- phase("reduce");
74
- currentPhase = "reduce";
75
- const reduced = await workflow("reduce", {
76
- mapped,
77
- totalItems: rawItems.length,
78
- mapFailed,
79
- });
80
- if (reduced.error) throw new Error("reduce 失败: " + reduced.error);
81
-
82
- outcome = {
83
- status: mapFailed > 0 ? "partial" : "ok",
84
- phase: currentPhase,
85
- items_total: rawItems.length,
86
- map_failed: mapFailed,
87
- reduced: reduced.content,
88
- message: "map-reduce 完成:map " + rawItems.length + " 项(失败 " + mapFailed + ")→ reduce",
89
- };
90
- } catch (err) {
91
- outcome = {
92
- status: "error",
93
- phase: currentPhase,
94
- error: err && err.message ? err.message : String(err),
95
- message: "map-reduce 在 " + currentPhase + " 段失败",
96
- };
97
- }
98
-
99
- return outcome;
@@ -1,82 +0,0 @@
1
- // parallel.example.js — 并行编排模板(UC-2)
2
- //
3
- // 模式:parallel([workflow("analyze-a"), workflow("analyze-b"), workflow("analyze-c")])
4
- // 多个 workflow 并行执行,Promise.allSettled 语义(部分失败不 reject,在 outcome 标 failed)。
5
- //
6
- // 用法:复制本文件到 .pi/workflows/ 或 ~/.pi/agent/workflows/,改 workflow 名后:
7
- // workflow run parallel --args target=src/main.ts
8
- //
9
- // ⚠️ 分层配额规则(来源:T2 system-architecture §并发池分层配额,ADR-030 并发上限来源):
10
- // - 全局并发上限 maxConcurrent = 6(ConcurrencyPool 默认值,T2 实现)
11
- // - 嵌套 workflow 时按 depth 分层:depth=N 时该层可用配额 = max(1, 6 - N)
12
- // 例:顶层 workflow(depth=0)可用 6 槽;其内再 fork workflow(depth=1)可用 5 槽;
13
- // depth=5 时保底 1 槽(max(1, 6-5)=1),防饿死。
14
- // - parallel() 内的 workflow() 调用共享父 workflow 的配额池,超出自动排队(不报错)。
15
- // - 本模板 parallel 3 个 workflow:顶层配额 6 足够,无需排队。
16
- //
17
- // ⚠️ lintScript 约束(本模板已遵守):
18
- // - 含 parallel() 入口(兼展示 workflow() 嵌套)
19
- // - 禁止 bare IIFE / 禁止变量名 result
20
-
21
- const meta = {
22
- name: "parallel",
23
- description: "并行编排模板:parallel([workflow A, B, C]),allSettled 语义,分层配额注释",
24
- phases: ["parallel-analyze"],
25
- };
26
-
27
- // ── 入参($ARGS)──────────────────────────────────────────────────
28
- const target = $ARGS.target;
29
- const tasks = $ARGS.tasks || ["analyze-a", "analyze-b", "analyze-c"]; // 默认 3 路并行
30
- if (!target) {
31
- throw new Error("parallel 缺少必需参数 target。用法:workflow run parallel --args target=src/main.ts");
32
- }
33
-
34
- log("parallel 开始,target=" + target + " tasks=" + JSON.stringify(tasks));
35
-
36
- let outcome;
37
-
38
- try {
39
- phase("parallel-analyze");
40
-
41
- // parallel() 接受 Promise 数组;workflow() 返回 Promise<AgentResult>。
42
- // allSettled 语义:单个 workflow 失败(返回 error 字段)不会让 parallel reject,
43
- // 全部完成后统一收集,在 outcome 里按 per-task 标 ok/failed。
44
- const rawResults = await parallel(
45
- tasks.map((taskName) => workflow(taskName, { target, task: taskName })),
46
- );
47
-
48
- // 逐个检查 error 字段(workflow 失败入 error 字段,不 throw)
49
- const perTask = [];
50
- let hasFailed = false;
51
- for (let i = 0; i < rawResults.length; i++) {
52
- const r = rawResults[i];
53
- if (!r || r.error) {
54
- perTask.push({ task: tasks[i], status: "failed", error: r ? r.error : "workflow 无返回" });
55
- hasFailed = true;
56
- } else {
57
- perTask.push({ task: tasks[i], status: "ok", content: r.content });
58
- }
59
- }
60
-
61
- outcome = {
62
- status: hasFailed ? "partial" : "ok",
63
- phase: "parallel-analyze",
64
- tasks: perTask,
65
- ok_count: perTask.filter((t) => t.status === "ok").length,
66
- failed_count: perTask.filter((t) => t.status === "failed").length,
67
- message: hasFailed
68
- ? "parallel 完成(部分失败):ok=" + perTask.filter((t) => t.status === "ok").length +
69
- " failed=" + perTask.filter((t) => t.status === "failed").length
70
- : "parallel 全绿:全部 " + perTask.length + " 个 workflow 成功",
71
- };
72
- } catch (err) {
73
- // parallel 整体 reject 罕见(通常是引擎级故障),仍兜底返回 error 对象不 crash
74
- outcome = {
75
- status: "error",
76
- phase: "parallel-analyze",
77
- error: err && err.message ? err.message : String(err),
78
- message: "parallel 执行抛异常",
79
- };
80
- }
81
-
82
- return outcome;
@@ -1,106 +0,0 @@
1
- // scatter-gather.example.js — 分发-收集模板(UC-3)
2
- //
3
- // 模式(三段):
4
- // 段 1 scatter: workflow("split", data) → 出 shards[]
5
- // 段 2 process: parallel(shards.map(s => workflow("process", {shard:s}))) → 并行处理
6
- // 段 3 gather: workflow("merge", results) → 合并
7
- //
8
- // 用法:复制本文件到 .pi/workflows/ 或 ~/.pi/agent/workflows/,改 workflow 名后:
9
- // workflow run scatter-gather --args dataPath=/path/to/big.json
10
- //
11
- // ⚠️ lintScript 约束(本模板已遵守):
12
- // - 含 parallel() 入口(兼展示 workflow() 嵌套:split/merge + process)
13
- // - 禁止 bare IIFE / 禁止变量名 result
14
-
15
- const meta = {
16
- name: "scatter-gather",
17
- description: "分发-收集模板:split → parallel process → merge 三段",
18
- phases: ["scatter", "process", "gather"],
19
- };
20
-
21
- const fs = require("fs");
22
-
23
- // ── 入参($ARGS)──────────────────────────────────────────────────
24
- const dataPath = $ARGS.dataPath;
25
- if (!dataPath) {
26
- throw new Error("scatter-gather 缺少必需参数 dataPath。用法:workflow run scatter-gather --args dataPath=/path/to/big.json");
27
- }
28
- if (!fs.existsSync(dataPath)) {
29
- throw new Error("dataPath 不存在: " + dataPath);
30
- }
31
-
32
- log("scatter-gather 开始,dataPath=" + dataPath);
33
-
34
- let currentPhase = "init";
35
- let outcome;
36
-
37
- try {
38
- // ── 段 1:scatter(split workflow 分片数据)──────────────────────
39
- phase("scatter");
40
- currentPhase = "scatter";
41
- const splitOutcome = await workflow("split", {
42
- source: dataPath,
43
- // split workflow 决定分片数(按数据量/可用配额),返回 shards 数组
44
- });
45
- if (splitOutcome.error) throw new Error("split 失败: " + splitOutcome.error);
46
-
47
- // split workflow 返回的 content 应是分片清单(JSON 字符串或 parsedOutput 数组)
48
- // 用 schema 让 split 直接返回结构化 shards(参考 workflow-script-format 结构化输出规则)
49
- const shards = splitOutcome.parsedOutput || JSON.parse(splitOutcome.content);
50
- if (!Array.isArray(shards) || shards.length === 0) {
51
- throw new Error("split 返回的 shards 非数组或为空");
52
- }
53
- log("split 出 " + shards.length + " 个分片");
54
-
55
- // ── 段 2:process(parallel 并行处理每个分片)────────────────────
56
- phase("process");
57
- currentPhase = "process";
58
- // parallel() allSettled 语义:单个分片处理失败不 reject,收集后统一判断
59
- const processed = await parallel(
60
- shards.map((shard, idx) => workflow("process", { shard, shardIndex: idx })),
61
- );
62
-
63
- const shardResults = [];
64
- let failedShards = 0;
65
- for (let i = 0; i < processed.length; i++) {
66
- const p = processed[i];
67
- if (!p || p.error) {
68
- shardResults.push({ shardIndex: i, status: "failed", error: p ? p.error : "无返回" });
69
- failedShards++;
70
- } else {
71
- shardResults.push({ shardIndex: i, status: "ok", content: p.content });
72
- }
73
- }
74
- if (failedShards === shards.length) {
75
- throw new Error("全部分片处理失败(" + failedShards + "/" + shards.length + ")");
76
- }
77
- log("process 完成:ok=" + (shards.length - failedShards) + " failed=" + failedShards);
78
-
79
- // ── 段 3:gather(merge workflow 合并结果)───────────────────────
80
- phase("gather");
81
- currentPhase = "gather";
82
- const merged = await workflow("merge", {
83
- shardResults,
84
- totalShards: shards.length,
85
- failedShards,
86
- });
87
- if (merged.error) throw new Error("merge 失败: " + merged.error);
88
-
89
- outcome = {
90
- status: failedShards > 0 ? "partial" : "ok",
91
- phase: currentPhase,
92
- shards_total: shards.length,
93
- shards_failed: failedShards,
94
- merged: merged.content,
95
- message: "scatter-gather 完成:split " + shards.length + " → process(失败 " + failedShards + ")→ merge",
96
- };
97
- } catch (err) {
98
- outcome = {
99
- status: "error",
100
- phase: currentPhase,
101
- error: err && err.message ? err.message : String(err),
102
- message: "scatter-gather 在 " + currentPhase + " 段失败",
103
- };
104
- }
105
-
106
- return outcome;
@@ -1,136 +0,0 @@
1
- /**
2
- * GUI 协议适配层 —— TUI/RPC 双模渲染支持。
3
- *
4
- * 封装 @xyz-agent/extension-protocol 的 helpers,供 subagent/workflow/bg-notify 使用。
5
- * 当 extension-protocol 包可用后,替换 import 即可(stub 接口完全对齐)。
6
- *
7
- * 协议核心:
8
- * - isGuiCapable(ctx) → RPC 模式返回 true
9
- * - guiComponent(type, props) → 构造结构化 GuiComponent
10
- * - guiResult(component) → 包装为 GuiRenderResult,放进 details.__gui__
11
- *
12
- * 渲染入口:
13
- * - tool result: details.__gui__(execute 返回时构造)
14
- * - message: details.__gui__(sendMessage 时附带)
15
- *
16
- * @see docs/extensions/gui-protocol-guide.md
17
- */
18
-
19
- // ============================================================
20
- // 类型(对齐 @xyz-agent/extension-protocol)
21
- // ============================================================
22
-
23
- /** GuiContext —— Pi ExtensionContext 的结构化子集。 */
24
- export interface GuiContext {
25
- hasUI?: boolean;
26
- }
27
-
28
- /** GuiComponent 类型枚举(本扩展实际使用的子集)。 */
29
- export type GuiComponentType =
30
- | "task-list"
31
- | "workflow-runs"
32
- | "subagent-trace"
33
- | "stats-line";
34
-
35
- /** 各类型对应的 props 形状。 */
36
- export interface TaskListProps {
37
- title: string;
38
- items: Array<{
39
- label: string;
40
- status: "pending" | "in_progress" | "completed" | "failed" | "cancelled";
41
- detail?: string;
42
- }>;
43
- summary?: string;
44
- }
45
-
46
- export interface WorkflowRunsProps {
47
- runs: Array<{
48
- runId: string;
49
- name: string;
50
- status: string;
51
- reason?: string;
52
- durationMs?: number;
53
- error?: string;
54
- }>;
55
- }
56
-
57
- export interface SubagentTraceProps {
58
- agent: string;
59
- status: "running" | "done" | "failed" | "cancelled";
60
- stats?: {
61
- turns?: number;
62
- tokens?: number;
63
- durationMs?: number;
64
- };
65
- result?: string;
66
- }
67
-
68
- export interface StatsLineProps {
69
- items: Array<{
70
- label: string;
71
- value: string;
72
- severity?: "ok" | "warn" | "danger";
73
- }>;
74
- }
75
-
76
- export type GuiComponentProps = {
77
- "task-list": TaskListProps;
78
- "workflow-runs": WorkflowRunsProps;
79
- "subagent-trace": SubagentTraceProps;
80
- "stats-line": StatsLineProps;
81
- };
82
-
83
- export interface GuiComponent<T extends GuiComponentType = GuiComponentType> {
84
- type: T;
85
- props: GuiComponentProps[T];
86
- }
87
-
88
- export interface GuiRenderResult {
89
- v: 1;
90
- component: GuiComponent;
91
- }
92
-
93
- // ============================================================
94
- // Helpers(stub 实现,对齐 @xyz-agent/extension-protocol)
95
- // ============================================================
96
-
97
- /**
98
- * 检测当前环境是否支持 GUI 渲染(RPC 模式)。
99
- * Pi TUI 模式下 hasUI 为 true,RPC 模式下为 false。
100
- */
101
- export function isGuiCapable(ctx: GuiContext): boolean {
102
- return ctx.hasUI === false;
103
- }
104
-
105
- /**
106
- * 构造 GuiComponent,带类型推断。
107
- */
108
- export function guiComponent<T extends GuiComponentType>(
109
- type: T,
110
- props: GuiComponentProps[T],
111
- ): GuiComponent<T> {
112
- return { type, props };
113
- }
114
-
115
- /**
116
- * 构造 GuiRenderResult,放进 details.__gui__。
117
- * 递归删除 undefined 字段。
118
- */
119
- export function guiResult(component: GuiComponent): GuiRenderResult {
120
- return { v: 1, component: stripUndefined(component) as GuiComponent };
121
- }
122
-
123
- /**
124
- * 递归删除 undefined 字段(JSON.stringify 会丢弃 undefined,但数组中会变 null)。
125
- */
126
- function stripUndefined(obj: unknown): unknown {
127
- if (obj === null || typeof obj !== "object") return obj;
128
- if (Array.isArray(obj)) return obj.map(stripUndefined);
129
- const result: Record<string, unknown> = {};
130
- for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {
131
- if (value !== undefined) {
132
- result[key] = stripUndefined(value);
133
- }
134
- }
135
- return result;
136
- }