@mickorz/opencode-agentic-workflow 0.3.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 (144) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +406 -0
  3. package/dist/metrics/collector.d.ts +98 -0
  4. package/dist/metrics/collector.js +246 -0
  5. package/dist/metrics/collector.js.map +1 -0
  6. package/dist/observability/events.d.ts +99 -0
  7. package/dist/observability/events.js +55 -0
  8. package/dist/observability/events.js.map +1 -0
  9. package/dist/observability/observe.d.ts +7 -0
  10. package/dist/observability/observe.js +29 -0
  11. package/dist/observability/observe.js.map +1 -0
  12. package/dist/observability/trace.d.ts +26 -0
  13. package/dist/observability/trace.js +52 -0
  14. package/dist/observability/trace.js.map +1 -0
  15. package/dist/plugin/checkpoint-rpc.d.ts +74 -0
  16. package/dist/plugin/checkpoint-rpc.js +73 -0
  17. package/dist/plugin/checkpoint-rpc.js.map +1 -0
  18. package/dist/plugin/index.d.ts +24 -0
  19. package/dist/plugin/index.js +271 -0
  20. package/dist/plugin/index.js.map +1 -0
  21. package/dist/plugin/interactive-checkpoint-gate.d.ts +35 -0
  22. package/dist/plugin/interactive-checkpoint-gate.js +86 -0
  23. package/dist/plugin/interactive-checkpoint-gate.js.map +1 -0
  24. package/dist/plugin/opencode-v2-executor.d.ts +55 -0
  25. package/dist/plugin/opencode-v2-executor.js +92 -0
  26. package/dist/plugin/opencode-v2-executor.js.map +1 -0
  27. package/dist/plugin/policy-checkpoint-gate.d.ts +18 -0
  28. package/dist/plugin/policy-checkpoint-gate.js +27 -0
  29. package/dist/plugin/policy-checkpoint-gate.js.map +1 -0
  30. package/dist/plugin/price-table.d.ts +46 -0
  31. package/dist/plugin/price-table.js +44 -0
  32. package/dist/plugin/price-table.js.map +1 -0
  33. package/dist/plugin/tui.d.ts +12 -0
  34. package/dist/plugin/tui.js +53 -0
  35. package/dist/plugin/tui.js.map +1 -0
  36. package/dist/quality/check.d.ts +30 -0
  37. package/dist/quality/check.js +51 -0
  38. package/dist/quality/check.js.map +1 -0
  39. package/dist/quality/checkpoint.d.ts +44 -0
  40. package/dist/quality/checkpoint.js +55 -0
  41. package/dist/quality/checkpoint.js.map +1 -0
  42. package/dist/quality/json.d.ts +10 -0
  43. package/dist/quality/json.js +31 -0
  44. package/dist/quality/json.js.map +1 -0
  45. package/dist/quality/predicates.d.ts +17 -0
  46. package/dist/quality/predicates.js +44 -0
  47. package/dist/quality/predicates.js.map +1 -0
  48. package/dist/quality/verify.d.ts +50 -0
  49. package/dist/quality/verify.js +110 -0
  50. package/dist/quality/verify.js.map +1 -0
  51. package/dist/registry/definition.d.ts +66 -0
  52. package/dist/registry/definition.js +19 -0
  53. package/dist/registry/definition.js.map +1 -0
  54. package/dist/registry/errors.d.ts +31 -0
  55. package/dist/registry/errors.js +48 -0
  56. package/dist/registry/errors.js.map +1 -0
  57. package/dist/registry/index.d.ts +9 -0
  58. package/dist/registry/index.js +9 -0
  59. package/dist/registry/index.js.map +1 -0
  60. package/dist/registry/registry.d.ts +45 -0
  61. package/dist/registry/registry.js +117 -0
  62. package/dist/registry/registry.js.map +1 -0
  63. package/dist/registry/runner.d.ts +56 -0
  64. package/dist/registry/runner.js +199 -0
  65. package/dist/registry/runner.js.map +1 -0
  66. package/dist/registry/schema.d.ts +18 -0
  67. package/dist/registry/schema.js +73 -0
  68. package/dist/registry/schema.js.map +1 -0
  69. package/dist/runtime/command.d.ts +38 -0
  70. package/dist/runtime/command.js +61 -0
  71. package/dist/runtime/command.js.map +1 -0
  72. package/dist/runtime/engine.d.ts +17 -0
  73. package/dist/runtime/engine.js +31 -0
  74. package/dist/runtime/engine.js.map +1 -0
  75. package/dist/runtime/errors.d.ts +56 -0
  76. package/dist/runtime/errors.js +88 -0
  77. package/dist/runtime/errors.js.map +1 -0
  78. package/dist/runtime/executor.d.ts +46 -0
  79. package/dist/runtime/executor.js +16 -0
  80. package/dist/runtime/executor.js.map +1 -0
  81. package/dist/runtime/semaphore.d.ts +24 -0
  82. package/dist/runtime/semaphore.js +57 -0
  83. package/dist/runtime/semaphore.js.map +1 -0
  84. package/dist/state/file-store.d.ts +25 -0
  85. package/dist/state/file-store.js +138 -0
  86. package/dist/state/file-store.js.map +1 -0
  87. package/dist/state/journal.d.ts +83 -0
  88. package/dist/state/journal.js +59 -0
  89. package/dist/state/journal.js.map +1 -0
  90. package/dist/state/recorder.d.ts +63 -0
  91. package/dist/state/recorder.js +138 -0
  92. package/dist/state/recorder.js.map +1 -0
  93. package/dist/state/store.d.ts +23 -0
  94. package/dist/state/store.js +14 -0
  95. package/dist/state/store.js.map +1 -0
  96. package/dist/workflow/agent.d.ts +8 -0
  97. package/dist/workflow/agent.js +38 -0
  98. package/dist/workflow/agent.js.map +1 -0
  99. package/dist/workflow/fallback.d.ts +15 -0
  100. package/dist/workflow/fallback.js +32 -0
  101. package/dist/workflow/fallback.js.map +1 -0
  102. package/dist/workflow/parallel.d.ts +14 -0
  103. package/dist/workflow/parallel.js +62 -0
  104. package/dist/workflow/parallel.js.map +1 -0
  105. package/dist/workflow/phase.d.ts +7 -0
  106. package/dist/workflow/phase.js +12 -0
  107. package/dist/workflow/phase.js.map +1 -0
  108. package/dist/workflow/reliable.d.ts +50 -0
  109. package/dist/workflow/reliable.js +92 -0
  110. package/dist/workflow/reliable.js.map +1 -0
  111. package/dist/workflow/retry.d.ts +28 -0
  112. package/dist/workflow/retry.js +53 -0
  113. package/dist/workflow/retry.js.map +1 -0
  114. package/dist/workflow/sequence.d.ts +40 -0
  115. package/dist/workflow/sequence.js +100 -0
  116. package/dist/workflow/sequence.js.map +1 -0
  117. package/dist/workflow/smoke.d.ts +17 -0
  118. package/dist/workflow/smoke.js +38 -0
  119. package/dist/workflow/smoke.js.map +1 -0
  120. package/dist/workflows/artifact.d.ts +24 -0
  121. package/dist/workflows/artifact.js +71 -0
  122. package/dist/workflows/artifact.js.map +1 -0
  123. package/dist/workflows/index.d.ts +19 -0
  124. package/dist/workflows/index.js +40 -0
  125. package/dist/workflows/index.js.map +1 -0
  126. package/dist/workflows/reliable.d.ts +18 -0
  127. package/dist/workflows/reliable.js +32 -0
  128. package/dist/workflows/reliable.js.map +1 -0
  129. package/dist/workflows/smoke.d.ts +13 -0
  130. package/dist/workflows/smoke.js +26 -0
  131. package/dist/workflows/smoke.js.map +1 -0
  132. package/dist/workspace/ambient.d.ts +12 -0
  133. package/dist/workspace/ambient.js +17 -0
  134. package/dist/workspace/ambient.js.map +1 -0
  135. package/dist/workspace/git-worktree.d.ts +29 -0
  136. package/dist/workspace/git-worktree.js +110 -0
  137. package/dist/workspace/git-worktree.js.map +1 -0
  138. package/dist/workspace/index.d.ts +6 -0
  139. package/dist/workspace/index.js +6 -0
  140. package/dist/workspace/index.js.map +1 -0
  141. package/dist/workspace/provider.d.ts +61 -0
  142. package/dist/workspace/provider.js +15 -0
  143. package/dist/workspace/provider.js.map +1 -0
  144. package/package.json +71 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 mickorz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,406 @@
1
+ # opencode-agentic-workflow
2
+
3
+ **Durable Agentic Workflow Runtime for OpenCode.**
4
+
5
+ > 在 OpenCode V2 上编排多 agent 工作流:崩溃后可恢复、执行全程可观测、
6
+ > 每次 run 文件系统级隔离。workflow 定义带语义版本注册,失败现场保留取证,
7
+ > 跨目录/跨服务精确续跑。
8
+
9
+ ```text
10
+ ✓ Multi-agent orchestration agent / parallel / sequence / phase
11
+ ✓ Deterministic checks 可重复的谓词验证(check / assert)
12
+ ✓ Semantic verification 多 reviewer 语义评审(verify)
13
+ ✓ Retry / fallback 失败语义与降级路径
14
+ ✓ Human checkpoints 策略门 + TUI 交互式审批(RPC)
15
+ ✓ Journal & resume 崩溃后跨进程恢复,completed 步骤跳过
16
+ ✓ Workflow version registry 语义版本身份 + args schema 校验
17
+ ✓ Execution tracing 全原语事件流(events.jsonl)
18
+ ✓ Token & cost metrics 事件消费式聚合(workflow_metrics 工具)
19
+ ✓ Git worktree isolation 每 run 独立 worktree,resume 原样 reattach
20
+ ```
21
+
22
+ 与前代 `opencode-dynamic-workflows`(V1 平台)的根本区别:**workflow 不再是
23
+ 运行时生成的脚本,而是带版本身份的声明式定义**——由此获得 durability:
24
+ journal 记录每一步,崩溃后 resume 按精确版本解析定义、跳过已完成步骤、
25
+ 重新附着原 worktree 继续执行。
26
+
27
+ ---
28
+
29
+ ## 总架构
30
+
31
+ ```text
32
+ Human
33
+ │
34
+ interactive checkpoint
35
+ (TUI dialog / RPC / 策略门)
36
+ │
37
+ ▼
38
+ Workflow Registry
39
+ (版本化定义 · args schema 校验)
40
+ │
41
+ ▼
42
+ Workflow Runner
43
+ start / resume / cleanup
44
+ │
45
+ ┌───────────────┼───────────────┐
46
+ ▼ ▼ ▼
47
+ agent() check() verify()
48
+ (编排原语: 确定性谓词 多 reviewer
49
+ parallel / 验证 语义评审
50
+ sequence / phase)
51
+ │
52
+ ▼
53
+ AgentExecutor ←—— Core 的唯一边界抽象
54
+ │
55
+ ▼
56
+ OpenCode V2 Adapter ←—— 唯一允许触碰 OpenCode API 的位置
57
+ │
58
+ ▼
59
+ isolated sub-session (cwd = workspace 根)
60
+ │
61
+ ▼
62
+ Git Worktree (agw/<runId> · 失败保留现场)
63
+
64
+ Runtime side channels(旁路,不侵入主链):
65
+ Journal ──► Resume 跨目录/跨服务 · journal.workflow 精确版本解析
66
+ Trace ──► Observability events.jsonl 全事件流
67
+ Metrics ──► Token / Cost workflow_metrics 工具 + metrics.json 快照
68
+ ```
69
+
70
+ **架构红线**:Workflow Core(`src/workflow` `src/runtime` `src/quality`
71
+ `src/state` `src/observability` `src/registry` `src/metrics` `src/workspace`)
72
+ 零 OpenCode 依赖;只有 `src/plugin/` 允许 import `@opencode/plugin`。
73
+ 所有宿主能力(会话执行、审批交互、工作区)都经注入的抽象进入 Core。
74
+
75
+ ---
76
+
77
+ ## 5 分钟 Quick Start
78
+
79
+ **前置**:OpenCode V2(`opencode` CLI 可用且有能正常对话的模型)、Node.js 20+、git。
80
+
81
+ ```jsonc
82
+ // 1. 在你的项目里配置插件(<你的项目>/opencode.json)
83
+ // package 用 npm 包名;model 换成你的 providerID/modelId
84
+ {
85
+ "plugins": [
86
+ {
87
+ "package": "@mickorz/opencode-agentic-workflow",
88
+ "options": {
89
+ "model": { "providerID": "glm", "id": "glm-5.3-flash" },
90
+ "agent": "build"
91
+ }
92
+ }
93
+ ]
94
+ }
95
+ ```
96
+
97
+ ```bash
98
+ # 2. 在项目目录里跑第一个 workflow(3 路并行分析 + 汇总)
99
+ cd <你的项目>
100
+ opencode run --model glm/glm-5.3-flash \
101
+ "调用 workflow 工具:flow=smoke, topic=Rust 内存安全。完成后报告输出。"
102
+ ```
103
+
104
+ 主 agent 会调用 `workflow` 工具,输出形如:
105
+
106
+ ```text
107
+ [smoke@1.0.0 runId=run_1790994713691_b9lhmqcs]
108
+ (research → summary 两步的最终报告)
109
+ ```
110
+
111
+ 到这里执行链已通。接下来按需打开持久化 / 观测 / 隔离——见下方示例。
112
+
113
+ <details>
114
+ <summary><b>从源码安装(插件开发 / 未发布版本)</b></summary>
115
+
116
+ ```bash
117
+ git clone https://github.com/mickorz/opencode-agentic-workflow.git
118
+ cd opencode-agentic-workflow
119
+ npm install && npm run build # 产物在 dist/plugin
120
+ ```
121
+
122
+ 然后把 opencode.json 的 `package` 换成克隆目录下 `dist/plugin` 的**绝对路径**。
123
+ 修改插件代码后需重启 opencode service(服务在启动时加载 dist/;
124
+ 插件相对路径以项目目录而非服务 cwd 为基准——见 `dev-docs/experience/`)。
125
+
126
+ </details>
127
+
128
+ > 提示:验收类长任务请给 `opencode run` 配看门狗超时
129
+ > (见 `dev-docs/experience/opencode-run-hang-watchdog.md`)。
130
+
131
+ ---
132
+
133
+ ## 内置 Workflow
134
+
135
+ | flow | 步骤 | 说明 |
136
+ |------|------|------|
137
+ | `smoke` | research → summary | 3 路并行分析 agent + 1 个汇总 agent(冒烟演示) |
138
+ | `reliable` | execute → check → verify → checkpoint | agent 执行 → 确定性检查 → 多 reviewer 语义评审 → 审批 |
139
+ | `artifact` | write → check → checkpoint | 在隔离 workspace 中生成文件产物并验证(隔离验收载体) |
140
+
141
+ 新增 workflow = 在 `src/workflows/` 写一个 `WorkflowDefinition` 并在
142
+ `src/plugin/index.ts` 注册;工具描述、枚举、路由全部由注册表驱动。
143
+
144
+ ---
145
+
146
+ ## 示例
147
+
148
+ ### 1. 最小 workflow
149
+
150
+ 见 Quick Start(`smoke` 即最小示例:一个 `topic` 参数,两步)。
151
+
152
+ ### 2. Reliable workflow(确定性检查 + 语义评审 + 审批)
153
+
154
+ ```jsonc
155
+ "options": {
156
+ "model": { "providerID": "glm", "id": "glm-5.3-flash" },
157
+ "agent": "build",
158
+ "checkCommand": "npm test", // check 步骤执行的确定性命令(可选)
159
+ "checkpoint": { "mode": "auto-approve" }
160
+ }
161
+ ```
162
+
163
+ ```text
164
+ 调用 workflow 工具:flow=reliable, topic=重构方案评审
165
+ ```
166
+
167
+ 链路:`execute`(agent 产出方案)→ `check`(跑 `npm test`,退出码判定)
168
+ → `verify`(默认 2 个 reviewer agent 独立评审,聚合 verdicts)
169
+ → `checkpoint`(审批门)。任一步失败即 fail-fast,journal 记录现场。
170
+
171
+ ### 3. Crash + Resume(崩溃恢复)
172
+
173
+ 打开 journalDir(建议绝对路径,跨目录共享即可跨服务恢复):
174
+
175
+ ```jsonc
176
+ "options": {
177
+ "model": { "providerID": "glm", "id": "glm-5.3-flash" },
178
+ "agent": "build",
179
+ "checkpoint": { "mode": "auto-reject" }, // 演示:让 run 在审批处失败
180
+ "journalDir": "/abs/path/to/journal"
181
+ }
182
+ ```
183
+
184
+ 失败输出自带恢复句柄:
185
+
186
+ ```text
187
+ [agentic-workflow] workflow failed: workflow artifact@1.0.0 run run_1790994713691_b9lhmqcs
188
+ failed: sequence failed (1 step): step #2 (checkpoint) failed: checkpoint rejected:
189
+ artifact-review (rejected by policy gate (auto-reject))
190
+ (runId: run_1790994713691_b9lhmqcs; 可用 resumeRunId="run_1790994713691_b9lhmqcs" 恢复本次执行)
191
+ ```
192
+
193
+ 换一个**全新目录**(模拟进程重启/换机器;journalDir 用同一个绝对路径),
194
+ 审批门改为 `auto-approve`,然后:
195
+
196
+ ```text
197
+ 调用 workflow 工具:resumeRunId=run_1790994713691_b9lhmqcs
198
+ ```
199
+
200
+ Resume 语义:
201
+
202
+ - workflow 按 **journal 记录的精确版本** 解析(`artifact@1.0.0`,绝不隐式取最新);
203
+ - args 取自 journal,调用方无需重传;
204
+ - **completed 步骤全部跳过**(原始时间戳保留),从首个未完成步骤续跑;
205
+ - 已完成的 run 幂等重放(零步骤执行,仅重建最终报告)。
206
+
207
+ 一条可复用的全链路验收脚本:`scripts/e2e-durable-restart.sh`
208
+ (31 项断言,覆盖 Registry→Worktree→Journal→Trace→Metrics→崩溃→重启→
209
+ Resume→Complete→清理全链)。
210
+
211
+ ### 4. Interactive checkpoint(真正的人工审批)
212
+
213
+ ```jsonc
214
+ "options": {
215
+ "checkpoint": {
216
+ "mode": "interactive",
217
+ "timeoutMs": 300000, // 缺省 5 分钟
218
+ "onTimeout": "reject" // 超时裁决:reject(缺省,安全失败)/ approve
219
+ }
220
+ }
221
+ ```
222
+
223
+ TUI 中 workflow 会停在 checkpoint 处弹出审批对话框;无应答超时按
224
+ `onTimeout` 裁决。headless / CI 场景用策略门(`auto-approve` /
225
+ `auto-reject`)。
226
+
227
+ ### 5. Worktree isolation(文件系统隔离)
228
+
229
+ ```jsonc
230
+ "options": {
231
+ "isolation": {
232
+ "mode": "git-worktree", // 缺省 off
233
+ "dir": "/abs/path/to/worktrees", // 可选;缺省 <仓库同级>/<项目名>-worktrees
234
+ "baseRef": "main", // 可选;缺省当前 HEAD
235
+ "cleanup": "on-success" // always / on-success(缺省)/ never
236
+ }
237
+ }
238
+ ```
239
+
240
+ 启用后每个 run:
241
+
242
+ 1. `git worktree add -b agw/<runId> <dir>/<runId>`(worktree 在仓库**同级**目录,不污染仓库);
243
+ 2. 子 agent 会话的 **cwd 绑定到 worktree 根**(产物文件落在隔离目录,真实 e2e 已验证);
244
+ 3. workspace 身份写入 journal;**resume 时 reattach 原 worktree**(文件系统状态
245
+ 随 journal 一起恢复;目录缺失则报错,绝不静默重建"假恢复");
246
+ 4. 结束后按策略清理:
247
+
248
+ | cleanup | 成功 | 失败 |
249
+ |---------|------|------|
250
+ | `on-success`(默认) | 移除 worktree、清 journal 身份 | **保留现场**(debug / resume 取证) |
251
+ | `always` | 同上 | 同样清理 |
252
+ | `never` | 保留 | 保留 |
253
+
254
+ 清理只移除 worktree,**分支保留**(删除是破坏性操作;
255
+ `git branch --list 'agw/*'` 可批量清理)。
256
+
257
+ ### 6. Metrics / Trace(用量与成本观测)
258
+
259
+ 跑过 workflow 后,直接问主 agent:
260
+
261
+ ```text
262
+ 调用 workflow_metrics 工具(format=text),报告 token 与成本
263
+ ```
264
+
265
+ 输出(真实 e2e 数据,3 个 agent / 40.1s):
266
+
267
+ ```text
268
+ ## Agents
269
+ - 调用 3 次(失败 0)
270
+ - tokens:in 47.5k / out 833 / reasoning 1.2k / cache r 27.7k w 0
271
+ - 成本:$0.0090
272
+ - 按模型:
273
+ - glm/glm-5.3-flash: 3 次, in 47.5k / out 833, $0.0090
274
+ ...
275
+ ```
276
+
277
+ 成本取值优先级:**用户 `prices` 覆盖 > 宿主价目表(ctx.model.list)> 宿主消息
278
+ cost**;宿主记账为 0 时按 token 用量 × 价目估算(`prices` 选项可补新模型价目,
279
+ 见 `dev-docs/experience/宿主cost为0与models-dev缓存滞后.md`)。
280
+
281
+ Trace:配置 `traceDir` 后全原语事件流落盘 `events.jsonl`
282
+ (agent/check/verify/checkpoint/step/workflow 全生命周期),
283
+ workflow 结束时同步写 `metrics.json` 快照(失败也写)。
284
+
285
+ ---
286
+
287
+ ## 配置参考
288
+
289
+ | 选项 | 类型 | 缺省 | 说明 |
290
+ |------|------|------|------|
291
+ | `model` | `{providerID, id}` | — | 子会话使用的模型 |
292
+ | `agent` | `string` | — | 子会话 agent(如 `"build"`,有文件/命令工具) |
293
+ | `concurrency` | `number` | `3` | 模型 API 并发上限(信号量) |
294
+ | `checkpoint.mode` | `auto-approve` / `auto-reject` / `interactive` | `auto-approve` | 审批门形态 |
295
+ | `checkpoint.timeoutMs` | `number` | `300000` | interactive 等待应答超时 |
296
+ | `checkpoint.onTimeout` | `reject` / `approve` | `reject` | interactive 超时裁决 |
297
+ | `checkCommand` | `string` | — | reliable workflow 的 check 步骤命令 |
298
+ | `journalDir` | `string` | 关闭 | journal 目录(`<runId>.json`);不配置则不持久化、不可恢复 |
299
+ | `traceDir` | `string` | 关闭 | 事件 trace 目录(`events.jsonl` + `metrics.json`) |
300
+ | `prices` | `Record<string, {input, output, cacheRead, cacheWrite}>` | — | 价目覆盖(USD/M tokens),key 为 `providerID/modelId` |
301
+ | `isolation.mode` | `off` / `git-worktree` | `off` | 工作区隔离 |
302
+ | `isolation.dir` | `string` | 仓库同级 | worktree 父目录 |
303
+ | `isolation.baseRef` | `string` | HEAD | worktree 基准 ref |
304
+ | `isolation.cleanup` | `always` / `on-success` / `never` | `on-success` | 清理策略 |
305
+
306
+ 相对路径一律以**项目目录**(不是 service 进程 cwd)为基准解析。
307
+
308
+ ## Tools
309
+
310
+ **`workflow`** —— 执行或恢复 workflow:
311
+
312
+ | 参数 | 说明 |
313
+ |------|------|
314
+ | `flow` | workflow id(枚举由注册表驱动;缺省 `smoke`) |
315
+ | `topic` | 主题参数 |
316
+ | `resumeRunId` | 恢复指定 run(优先于 flow/topic;用失败输出里的 runId) |
317
+
318
+ **`workflow_metrics`** —— 只读查询本服务累计指标(`format: "text" | "json"`)。
319
+
320
+ ## Journal 数据模型
321
+
322
+ `<journalDir>/<runId>.json`(每次状态变更原子落盘):
323
+
324
+ ```jsonc
325
+ {
326
+ "runId": "run_1790994713691_b9lhmqcs",
327
+ "workflow": { "id": "artifact", "version": "1.0.0" }, // resume 精确版本解析依据
328
+ "args": { "topic": "火星基地能源方案" },
329
+ "workspace": { "provider": "git-worktree", "path": "...", "branch": "agw/run_..." },
330
+ "status": "failed", // running / completed / failed / aborted
331
+ "steps": [
332
+ { "name": "write", "status": "completed", "startedAt": 1760000000000 },
333
+ { "name": "check", "status": "completed" },
334
+ { "name": "checkpoint", "status": "failed" }
335
+ ],
336
+ "failure": { "message": "..." }
337
+ }
338
+ ```
339
+
340
+ **版本纪律**:workflow 步骤结构(stepNames 数量/顺序/语义)变更必须升
341
+ `version`——resume 依赖 journal.workflow.version 精确解析定义。
342
+
343
+ ---
344
+
345
+ ## 从 dynamic-workflows(v1)迁移
346
+
347
+ | | v1 `opencode-dynamic-workflows` | v2 `opencode-agentic-workflow` |
348
+ |---|---|---|
349
+ | 平台 | OpenCode **V1** 插件 API | OpenCode **V2** 插件 API |
350
+ | workflow 形态 | 主 agent **运行时生成 JS 编排脚本**(VM 沙箱) | **声明式定义** + 语义版本注册(代码内) |
351
+ | 调用方式 | 自然语言 → 生成脚本 → 执行 | `flow=<id>` + args(或 `resumeRunId`) |
352
+ | 原语 | agent / parallel / sequence / fallback / race | agent / parallel / sequence / phase + check / verify / checkpoint |
353
+ | 崩溃恢复 | 无 | journal + resume(completed 跳过、精确版本、幂等重放) |
354
+ | 隔离 | 子会话 | git worktree per run(resume reattach) |
355
+ | 观测 | TUI 进度树 | events.jsonl trace + metrics/成本聚合 |
356
+ | 人工审批 | checkpoint(V1 API) | 策略门 + TUI 交互式审批(RPC) |
357
+ | 安装 | `npx @mickorz/opencode-dynamic-workflows install` | npm 包 `@mickorz/opencode-agentic-workflow`(或源码路径) |
358
+
359
+ 迁移要点:
360
+
361
+ 1. **从"生成脚本"到"注册定义"**:v1 里由主 agent 即兴生成的编排逻辑,在 v2
362
+ 中固化为 `WorkflowDefinition`(`src/workflows/` 有三个完整样例)。换来的是
363
+ 可恢复、可版本化、可审计。
364
+ 2. **一次性的动态编排仍有价值**:不需要 durable 的临时任务,直接让主 agent
365
+ 自己并行开子会话完成即可(v2 明确拒绝嵌套 workflow 调用防递归死锁)。
366
+ 3. OpenCode 平台自身 V1→V2 的配置/插件迁移见
367
+ `dev-docs/guides/Migrate from V1.md`(官方指南剪藏)。
368
+
369
+ ---
370
+
371
+ ## 开发
372
+
373
+ ```bash
374
+ npm run typecheck # tsc --noEmit(strict + noUncheckedIndexedAccess)
375
+ npm test # node:test + tsx,158 个测试
376
+ npm run build # 产物 dist/(插件入口 dist/plugin)
377
+
378
+ ./scripts/e2e-durable-restart.sh # 真实全链路验收(需要可用模型与 git)
379
+ ```
380
+
381
+ 架构不变量(详见 `dev-docs/`):
382
+
383
+ ```text
384
+ Workflow Core 零 OpenCode 依赖;只有 src/plugin/ 触碰 @opencode/plugin
385
+ workflow → AgentExecutor → OpenCodeV2Executor → ctx.session
386
+ WorkspaceProvider / CheckpointGate / CommandRunner / ExecutionStore
387
+ 全部是注入抽象——Core 只认识接口,宿主能力在 plugin 层实现
388
+ ```
389
+
390
+ ## 阶段与文档
391
+
392
+ ```text
393
+ P0 ✅ Execution —— 能跑(编排原语 + V2 适配) v0.1.0
394
+ P1 ✅ Reliability —— 跑得可靠(check/verify/重试/审批) v0.2.0
395
+ P2 ✅ Production Runtime —— 扛得住生产
396
+ Journal & Resume · Registry · Observability · Metrics · Isolation
397
+ v0.3.0
398
+ ```
399
+
400
+ - 开发过程:`dev-docs/progress/执行进度.md`(逐 commit 交付清单)
401
+ - 设计方法论:`dev-docs/design/从P0到P2:Agentic-Workflow-Runtime演进方法论.md`
402
+ - 踩坑实录:`dev-docs/experience/`(11 篇,验收与调试前先查)
403
+
404
+ ## License
405
+
406
+ MIT
@@ -0,0 +1,98 @@
1
+ /**
2
+ * MetricsCollector(P2.6)—— 纯事件总线消费者
3
+ *
4
+ * EventBus ─→ MetricsCollector ─→ snapshot(内存聚合,随时可查)
5
+ *
6
+ * 设计约束(与 P2.4 一脉相承):
7
+ * - 零侵入:不改动 agent()/sequence()/verify() 等原语的任何逻辑,
8
+ * 只订阅事件流;token/cost 经 agent.completed 事件透传(数据源是
9
+ * 宿主 executor 提取的 AgentResult.usage/costUSD)。
10
+ * - 只聚合、不采样:计数与求和,不保存明细(明细在 journal/trace)。
11
+ * - 按 workflowId / 模型维度聚合;单 run 的明细数据以 journal 为准
12
+ * (journal 有每步时间戳与状态,run 级 rollup 未来可由 store 派生)。
13
+ */
14
+ import { type EventBus } from "../observability/events.js";
15
+ export interface DurationStats {
16
+ count: number;
17
+ totalMs: number;
18
+ minMs: number;
19
+ maxMs: number;
20
+ lastMs: number;
21
+ }
22
+ export interface TokenTotals {
23
+ input: number;
24
+ output: number;
25
+ reasoning: number;
26
+ cacheRead: number;
27
+ cacheWrite: number;
28
+ }
29
+ export interface ModelStats {
30
+ calls: number;
31
+ tokens: TokenTotals;
32
+ costUSD: number;
33
+ }
34
+ export interface WorkflowStats {
35
+ started: number;
36
+ completed: number;
37
+ failed: number;
38
+ duration: DurationStats;
39
+ }
40
+ export interface MetricsSnapshot {
41
+ /** 开始收集的时间(epoch ms) */
42
+ since: number;
43
+ /** 最近一次事件时间(epoch ms) */
44
+ updatedAt: number;
45
+ agents: {
46
+ calls: number;
47
+ failed: number;
48
+ duration: DurationStats;
49
+ tokens: TokenTotals;
50
+ costUSD: number;
51
+ /** key = "providerID/modelId" */
52
+ byModel: Record<string, ModelStats>;
53
+ };
54
+ /** key = workflowId */
55
+ workflows: Record<string, WorkflowStats>;
56
+ steps: {
57
+ completed: number;
58
+ failed: number;
59
+ totalMs: number;
60
+ };
61
+ checks: {
62
+ total: number;
63
+ passed: number;
64
+ };
65
+ verifies: {
66
+ total: number;
67
+ passed: number;
68
+ reviewerPassed: number;
69
+ reviewerTotal: number;
70
+ };
71
+ checkpoints: {
72
+ waiting: number;
73
+ approved: number;
74
+ rejected: number;
75
+ };
76
+ }
77
+ export declare class MetricsCollector {
78
+ private readonly bus;
79
+ private readonly unsubscribe;
80
+ private state;
81
+ /** 订阅指定 bus(缺省全局 bus) */
82
+ constructor(bus?: EventBus);
83
+ private workflowStats;
84
+ private handle;
85
+ /** 当前聚合快照(深拷贝,调用方修改不影响内部状态) */
86
+ snapshot(): MetricsSnapshot;
87
+ /** 清零重新收集 */
88
+ reset(): void;
89
+ /** 停止订阅 */
90
+ dispose(): void;
91
+ /**
92
+ * 快照落盘:workflow 结束(completed/failed)时把当前聚合写到 <file>。
93
+ * 写失败只记录日志,绝不影响 workflow(观测设施不能成为故障源)。
94
+ */
95
+ subscribeFileSink(file: string): void;
96
+ }
97
+ /** 人读快照(工具返回文本用) */
98
+ export declare function formatMetrics(m: MetricsSnapshot): string;