ppxans-harness 2.4.0 → 3.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 (186) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +218 -265
  3. package/bin/ppx-channels.js +2 -2
  4. package/bin/ppx-serve.js +5 -5
  5. package/bin/ppx-setup.js +124 -0
  6. package/bin/ppx-web.js +140 -0
  7. package/bin/ppx.js +2 -2
  8. package/config/identity.md +6 -6
  9. package/config/ishiki.md +16 -16
  10. package/config/ppx.json +15 -151
  11. package/config/ppx.json.example +143 -0
  12. package/package.json +17 -10
  13. package/skills/.usage.json +6 -0
  14. package/skills/agent-professional-training/SKILL.md +94 -0
  15. package/skills/brainstorm/SKILL.md +24 -0
  16. package/skills/cupid-lover-comms/SKILL.md +37 -0
  17. package/skills/debug/SKILL.md +26 -0
  18. package/skills/plan/SKILL.md +25 -0
  19. package/skills/ponytail/SKILL.md +25 -0
  20. package/skills/ppx-memory/SKILL.md +91 -0
  21. package/skills/ppx-memory/scripts/cli.js +192 -0
  22. package/skills/ppx-memory/scripts/experience.js +133 -0
  23. package/skills/ppx-memory/scripts/fact-store.js +842 -0
  24. package/skills/ppx-memory/scripts/l0.js +52 -0
  25. package/skills/ppx-memory/scripts/l2.js +146 -0
  26. package/skills/ppx-memory/scripts/l3.js +112 -0
  27. package/skills/ppx-memory/scripts/memory-ticker.js +238 -0
  28. package/skills/ppx-memory/scripts/pii.js +42 -0
  29. package/skills/ppx-memory/scripts/schema.js +80 -0
  30. package/skills/ppx-memory/scripts/session.js +398 -0
  31. package/skills/ppx-memory/scripts/similarity.js +43 -0
  32. package/skills/ppx-memory/scripts/store.js +116 -0
  33. package/skills/ppx-memory/scripts/wal.js +38 -0
  34. package/skills/ppx-selfheal/SKILL.md +24 -0
  35. package/skills/ppx-selfheal/scripts/cli.js +80 -0
  36. package/skills/ppx-selfheal/scripts/healer.js +184 -0
  37. package/skills/ppx-selfheal/scripts/logger.js +17 -0
  38. package/skills/ppx-selfheal/scripts/store.js +116 -0
  39. package/skills/prompt-depth-kit/SKILL.md +28 -0
  40. package/skills/session-naming/SKILL.md +36 -0
  41. package/skills/verify/SKILL.md +25 -0
  42. package/src/agent/index.js +1340 -717
  43. package/src/agent/prompts.js +47 -3
  44. package/src/aml-server.js +197 -151
  45. package/src/ans/eviction.js +123 -143
  46. package/src/ans/guard.js +159 -120
  47. package/src/ans/lifecycle.js +96 -93
  48. package/src/ans/proactive.js +112 -129
  49. package/src/ans/reward.js +95 -111
  50. package/src/ans/values.js +15 -15
  51. package/src/audit/audit-chain.js +43 -7
  52. package/src/audit/verifier.js +157 -120
  53. package/src/bus/circuit-breaker.js +9 -1
  54. package/src/bus/runtime-bus.js +107 -93
  55. package/src/channels/base.js +57 -34
  56. package/src/channels/feishu.js +118 -126
  57. package/src/channels/http.js +1090 -592
  58. package/src/channels/index.js +111 -110
  59. package/src/channels/log.js +29 -29
  60. package/src/channels/wechat-crypto.js +73 -74
  61. package/src/channels/wechat.js +191 -197
  62. package/src/channels/workspace.js +94 -0
  63. package/src/channels-cli.js +126 -124
  64. package/src/cli.js +129 -120
  65. package/src/commands/index.js +142 -0
  66. package/src/config/channels.js +137 -170
  67. package/src/config/index.js +277 -224
  68. package/src/config/placeholder.js +31 -0
  69. package/src/config/providers.js +148 -188
  70. package/src/config/settings.js +156 -182
  71. package/src/core/policy.js +69 -18
  72. package/src/core/trace.js +8 -6
  73. package/src/edit/editblock.js +266 -0
  74. package/src/edit/snapshot.js +67 -0
  75. package/src/evidence/index.js +153 -0
  76. package/src/evolve/playbook.js +9 -10
  77. package/src/hooks/index.js +113 -0
  78. package/src/llm/client.js +188 -446
  79. package/src/llm/dsml.js +74 -74
  80. package/src/llm/embedder.js +41 -35
  81. package/src/llm/fence.js +52 -105
  82. package/src/llm/index.js +4 -4
  83. package/src/llm/local-embedder.js +94 -0
  84. package/src/llm/presets.js +113 -0
  85. package/src/llm/pricing.js +93 -0
  86. package/src/llm/retry.js +73 -73
  87. package/src/llm/router.js +92 -97
  88. package/src/mcp/admin.js +326 -0
  89. package/src/mcp/client.js +487 -375
  90. package/src/mcp/http.js +203 -0
  91. package/src/mcp/index.js +116 -116
  92. package/src/mcp/server.js +392 -0
  93. package/src/mcp/tasks.js +133 -0
  94. package/src/memory/asset-hub.js +6 -11
  95. package/src/memory/canvas.js +2 -5
  96. package/src/memory/compaction.js +28 -28
  97. package/src/memory/experience.js +133 -122
  98. package/src/memory/fact-store.js +914 -698
  99. package/src/memory/failure-episode.js +20 -11
  100. package/src/memory/fork.js +17 -8
  101. package/src/memory/index.js +8 -6
  102. package/src/memory/l0.js +53 -52
  103. package/src/memory/l2.js +145 -130
  104. package/src/memory/l3.js +111 -111
  105. package/src/memory/legion-board.js +71 -0
  106. package/src/memory/memory-ticker.js +239 -240
  107. package/src/memory/session.js +398 -397
  108. package/src/memory/sqlite-store.js +581 -0
  109. package/src/mode/blackboard.js +49 -49
  110. package/src/mode/graph.js +42 -41
  111. package/src/mode/index.js +64 -64
  112. package/src/mode/legion.js +54 -51
  113. package/src/mode/plan-exec.js +50 -50
  114. package/src/mode/router.js +28 -40
  115. package/src/orchestrator/agent-worker.js +69 -69
  116. package/src/orchestrator/dag.js +90 -83
  117. package/src/orchestrator/experts.js +76 -0
  118. package/src/orchestrator/index.js +1 -1
  119. package/src/orchestrator/legion.js +179 -187
  120. package/src/orchestrator/supervisor.js +6 -8
  121. package/src/permissions/index.js +378 -0
  122. package/src/persona/index.js +28 -29
  123. package/src/plugin/builtin.js +313 -212
  124. package/src/plugin/context.js +80 -79
  125. package/src/plugin/index.js +62 -62
  126. package/src/plugin/v3.js +73 -0
  127. package/src/protocol/index.js +148 -0
  128. package/src/repomap/index.js +309 -0
  129. package/src/review/index.js +393 -0
  130. package/src/seam/registry.js +3 -0
  131. package/src/seam/shell.js +55 -55
  132. package/src/security/injection.js +79 -0
  133. package/src/selfheal/evolve.js +67 -67
  134. package/src/selfheal/healer.js +184 -167
  135. package/src/selfheal/run.js +9 -9
  136. package/src/server.js +63 -60
  137. package/src/services/diagnose.js +180 -0
  138. package/src/services/learning-service.js +9 -0
  139. package/src/services/memory-health.js +34 -6
  140. package/src/services/memory-service.js +42 -9
  141. package/src/services/triage.js +138 -0
  142. package/src/session/parts.js +76 -0
  143. package/src/session/projection.js +73 -0
  144. package/src/session/rollout.js +54 -0
  145. package/src/session/turn.js +137 -0
  146. package/src/skills/lint.js +72 -0
  147. package/src/skills/loader.js +231 -150
  148. package/src/skills/search.js +58 -0
  149. package/src/skills/verify.js +95 -100
  150. package/src/tools/advanced.js +388 -352
  151. package/src/tools/builtin.js +384 -297
  152. package/src/tools/catalog.js +283 -159
  153. package/src/tools/command-guard.js +112 -112
  154. package/src/tools/custom.js +47 -47
  155. package/src/tools/delegate.js +383 -297
  156. package/src/tools/document.js +254 -253
  157. package/src/tools/git.js +151 -0
  158. package/src/tools/governance.js +47 -20
  159. package/src/tools/index.js +16 -11
  160. package/src/tools/methods.js +178 -178
  161. package/src/tools/ocr.js +59 -59
  162. package/src/tools/sandbox-worker.js +40 -0
  163. package/src/tools/sandbox.js +92 -0
  164. package/src/tools/seam.js +162 -125
  165. package/src/tools/selfmod.js +196 -176
  166. package/src/tools/v3.js +225 -0
  167. package/src/tools/vad.js +176 -0
  168. package/src/tools/voice.js +238 -0
  169. package/src/utils/async.js +14 -0
  170. package/src/utils/config-file.js +53 -0
  171. package/src/utils/crashguard.js +88 -0
  172. package/src/utils/http.js +53 -0
  173. package/src/utils/id.js +8 -0
  174. package/src/utils/json-state.js +33 -0
  175. package/src/utils/logger.js +17 -17
  176. package/src/utils/ndjson.js +25 -0
  177. package/src/utils/pii.js +42 -42
  178. package/src/utils/rate-limit.js +50 -0
  179. package/src/utils/schema.js +80 -0
  180. package/src/utils/similarity.js +43 -0
  181. package/src/utils/store.js +170 -108
  182. package/src/utils/text.js +15 -15
  183. package/src/utils/trace.js +153 -153
  184. package/src/utils/wal.js +39 -0
  185. package/src/utils/winutf8.js +16 -15
  186. package/src/wiki/index.js +170 -0
@@ -0,0 +1,326 @@
1
+ // src/mcp/admin.js - MCP 管理虚拟工具 (零依赖)
2
+ // 把会话管理 / 提供方 CRUD / 设置读写 / 任务面板 暴露为标准 MCP 工具,
3
+ // 让 web 前端完全走 MCP 协议, 逐步退役 /api/* REST 端点。
4
+ // 这些是"虚拟工具" (注入 McpServer extraTools), 不进 catalog, 不污染 LLM 工具列表。
5
+ import {
6
+ listProviders, addProvider, updateProvider, removeProvider, reorderProviders,
7
+ } from "../config/providers.js";
8
+ import { getSettings, updateSettings } from "../config/settings.js";
9
+ import { createTaskBoard } from "./tasks.js";
10
+ import { ensureDir } from "../utils/store.js";
11
+ import { warn, debug } from "../utils/logger.js";
12
+ import path from "node:path";
13
+
14
+ // 任务面板技能模板库: 按技能预置步骤列表, 新建任务时可选 (供 web 前端渲染下拉)
15
+ // 每个模板 = { id, label, steps: string[] }
16
+ export const TASK_TEMPLATES = [
17
+ {
18
+ id: "apt",
19
+ label: "Agent 专业训练评估 (agent-professional-training)",
20
+ steps: [
21
+ "读取 README 与流程设计说明,理解技能定位",
22
+ "精读 SKILL.md 与全部 references",
23
+ "检查安装脚本与目录结构规范性",
24
+ "按技能规范逐项评估并给出改进建议",
25
+ ],
26
+ },
27
+ {
28
+ id: "session-naming",
29
+ label: "会话重命名整理 (session-naming)",
30
+ steps: [
31
+ "列出全部对话及其创建时间",
32
+ "按 MMDD|类型|主题 提炼新名称 (类型限定: 功能/设计/修复/优化/发布/探索/文档/研究)",
33
+ "输出两列表格 (原名称|新名称) 等确认",
34
+ "确认后修改, 仅报告结果",
35
+ ],
36
+ },
37
+ {
38
+ id: "prompt-depth",
39
+ label: "回答深度提示词方案 (prompt-depth-kit)",
40
+ steps: [
41
+ "明确目标: 更敢说 / 更有逻辑 / 极简专业",
42
+ "从三套方案中选一 (主动边界探索 / 情境化优先级 / 极简优雅)",
43
+ "落地到 config/ppx.json 的 agent.system_extra 或场景 persona",
44
+ "热重载并验证回答质量提升",
45
+ ],
46
+ },
47
+ {
48
+ id: "code-review",
49
+ label: "代码审查 (code-review)",
50
+ steps: [
51
+ "拉取变更范围 (diff / PR 描述)",
52
+ "逐文件审查: 逻辑/安全/性能/可维护性",
53
+ "输出问题清单 (严重度分级)",
54
+ "给出可落地修改建议",
55
+ ],
56
+ },
57
+ {
58
+ id: "mcp-audit",
59
+ label: "MCP 合规性评估",
60
+ steps: [
61
+ "读取协议实现 (server.js / http.js)",
62
+ "对照 MCP 2026-07-28 规范逐条核对",
63
+ "检查错误码/版本协商/传输语义",
64
+ "输出合规报告与改进项",
65
+ ],
66
+ },
67
+ {
68
+ id: "skill-import",
69
+ label: "技能吸收入库",
70
+ steps: [
71
+ "读透技能 (README → SKILL.md → 配套数据)",
72
+ "复制进 skills/learned/ 并注册索引",
73
+ "验证 SkillLoader 可发现",
74
+ "沉淀吸收报告",
75
+ ],
76
+ },
77
+ ];
78
+
79
+ // 写配置前确保 config 目录存在 (空 root / 首次启动时 agent 自愈建目录是异步的,
80
+ // 若目录缺失, withFileLock 的 openSync(wx) 抛 ENOENT 会被误判为"锁冲突"并超时)
81
+ function ensureConfigDir(root) {
82
+ try { ensureDir(path.join(root, "config")); } catch (e) { debug(`[mcp/admin] 已忽略异常: ${e && e.message ? e.message : e}`); }
83
+ }
84
+
85
+ const str = (v, d = "") => (v == null ? d : String(v));
86
+
87
+ // 工具执行封装: 抛错转 JSON-RPC 工具错误 (isError)
88
+ function wrap(fn) {
89
+ return async (args) => {
90
+ try {
91
+ return { content: [{ type: "text", text: JSON.stringify(await fn(args || {})) }] };
92
+ } catch (e) {
93
+ warn(`[mcp-admin] ${e.message}`);
94
+ return { content: [{ type: "text", text: JSON.stringify({ error: String(e.message) }) }], isError: true };
95
+ }
96
+ };
97
+ }
98
+
99
+ /**
100
+ * 生成 MCP 管理虚拟工具列表。
101
+ * @param {object} agent - PPXAgent 实例
102
+ * @returns {{ tools: object[], taskBoard: object }}
103
+ */
104
+ // 2026-09-18 重构: 原先是一个 189 行的方法体, 内含 20 个工具定义字面量。
105
+ // 现按「会话 / 提供方 / 设置 / 任务」四个域拆成独立工厂函数, 每个域可单独阅读与修改;
106
+ // createAdminTools 的签名与返回结构 ({ tools, taskBoard }) 完全不变。
107
+ export function createAdminTools(agent) {
108
+ const root = agent.root;
109
+ const taskBoard = createTaskBoard(root);
110
+ return {
111
+ tools: [
112
+ ...sessionTools(agent),
113
+ ...providerTools(agent, root),
114
+ ...settingsTools(agent, root),
115
+ ...taskTools(agent, taskBoard),
116
+ ],
117
+ taskBoard,
118
+ };
119
+ }
120
+
121
+ // ---- 域 1: 会话管理 (5 个工具) ----
122
+ function sessionTools(agent) {
123
+ return [
124
+ {
125
+ name: "ppx.sessions.list",
126
+ title: "会话列表",
127
+ description: "列出全部会话 (key/count/lastTs/title)。等价于旧 REST /sessions。",
128
+ inputSchema: { type: "object", properties: {} },
129
+ execute: wrap(() => (agent.sessionStore && typeof agent.sessionStore.list === "function" ? agent.sessionStore.list() : [])),
130
+ },
131
+ {
132
+ name: "ppx.sessions.history",
133
+ title: "会话历史",
134
+ description: "读取指定会话的消息历史 (role/content 数组)。等价于旧 REST /sessions/:key/history。",
135
+ inputSchema: { type: "object", properties: { key: { type: "string", description: "会话 key" } }, required: ["key"] },
136
+ execute: wrap((args) => (agent.sessionStore && typeof agent.sessionStore.deriveMessages === "function" ? agent.sessionStore.deriveMessages(String(args.key)) : [])),
137
+ },
138
+ {
139
+ name: "ppx.sessions.rename",
140
+ title: "会话重命名",
141
+ description: "重命名会话 (复制事件到新 key 并删旧 key)。",
142
+ inputSchema: { type: "object", properties: { from: { type: "string" }, to: { type: "string" } }, required: ["from", "to"] },
143
+ execute: wrap((args) => (agent.sessionStore && typeof agent.sessionStore.rename === "function" ? agent.sessionStore.rename(String(args.from), String(args.to)) : false)),
144
+ },
145
+ {
146
+ name: "ppx.sessions.delete",
147
+ title: "删除会话",
148
+ description: "删除指定会话及其历史。",
149
+ inputSchema: { type: "object", properties: { key: { type: "string" } }, required: ["key"] },
150
+ execute: wrap((args) => { if (agent.sessionStore && typeof agent.sessionStore.delete === "function") agent.sessionStore.delete(String(args.key)); return { ok: true }; }),
151
+ },
152
+ {
153
+ name: "ppx.session.reset",
154
+ title: "重置会话",
155
+ description: "清空会话历史 (等同新建)。",
156
+ inputSchema: { type: "object", properties: { sessionId: { type: "string", description: "会话 key, 默认 default" } } },
157
+ execute: wrap((args) => { agent.resetSession(String(args.sessionId || "default")); return { ok: true }; }),
158
+ },
159
+
160
+ ];
161
+ }
162
+
163
+ // ---- 域 2: 提供方 CRUD (模型配置, 6 个工具) ----
164
+ function providerTools(agent, root) {
165
+ return [
166
+ {
167
+ name: "ppx.providers.list",
168
+ title: "模型提供方列表",
169
+ description: "列出全部 LLM 提供方 (key 已抹掉, 只留 api_key_set 标志)。等价于旧 REST /api/providers。",
170
+ inputSchema: { type: "object", properties: {} },
171
+ execute: wrap(() => { ensureConfigDir(root); const p = listProviders(root); return { providers: p, default_id: p[0] ? p[0].id : null }; }),
172
+ },
173
+ {
174
+ name: "ppx.providers.add",
175
+ title: "新增提供方",
176
+ description: "新增一个 LLM 提供方 (http 后端, 需 base_url + 模型 + key/env)。热重载立即生效。",
177
+ inputSchema: {
178
+ type: "object",
179
+ properties: { provider: { type: "object", description: "提供方配置: {id, base_url, api_key/api_key_env, model, vision?, timeout_ms?}" } },
180
+ required: ["provider"],
181
+ },
182
+ execute: wrap(async (args) => { ensureConfigDir(root); const created = addProvider(root, args.provider || {}); agent.reloadProviders(); return { ok: true, provider: created }; }),
183
+ },
184
+ {
185
+ name: "ppx.providers.update",
186
+ title: "更新提供方",
187
+ description: "更新指定提供方字段 (patch)。热重载立即生效。",
188
+ inputSchema: { type: "object", properties: { id: { type: "string" }, patch: { type: "object" } }, required: ["id"] },
189
+ execute: wrap(async (args) => { ensureConfigDir(root); const updated = updateProvider(root, String(args.id), args.patch || {}); agent.reloadProviders(); return { ok: true, provider: updated }; }),
190
+ },
191
+ {
192
+ name: "ppx.providers.delete",
193
+ title: "删除提供方",
194
+ description: "删除指定提供方。热重载立即生效。",
195
+ inputSchema: { type: "object", properties: { id: { type: "string" } }, required: ["id"] },
196
+ execute: wrap(async (args) => { ensureConfigDir(root); const removed = removeProvider(root, String(args.id)); agent.reloadProviders(); return { ok: true, provider: removed }; }),
197
+ },
198
+ {
199
+ name: "ppx.providers.test",
200
+ title: "探测提供方",
201
+ description: "健康探测指定提供方 (复用 agent 客户端, 无真实网络时返回可读结果)。",
202
+ inputSchema: { type: "object", properties: { id: { type: "string" } }, required: ["id"] },
203
+ execute: wrap(async (args) => { ensureConfigDir(root);
204
+ const id = String(args.id);
205
+ let client = (agent.allProviders || []).find((c) => c.providerId === id);
206
+ let fromCache = !!client;
207
+ if (!client && agent.llm && agent.llm.providerId === id) { client = agent.llm; fromCache = true; }
208
+ if (!client) {
209
+ const { readConfig } = await import("../config/providers.js");
210
+ const { providers } = readConfig(root);
211
+ const p = providers.find((x) => x.id === id);
212
+ if (!p) throw new Error("提供方不存在");
213
+ const { LLMClient } = await import("../llm/client.js");
214
+ client = new LLMClient(p);
215
+ }
216
+ const healthy = await client.health();
217
+ return { ok: true, healthy, detail: healthy ? "API 端点可达" : "探测失败, 请检查 key/base_url", source: fromCache ? "agent-cache" : "disk-config" };
218
+ }),
219
+ },
220
+ {
221
+ name: "ppx.providers.reorder",
222
+ title: "重排提供方",
223
+ description: "按给定 id 顺序重排提供方列表 (决定主模型优先级)。",
224
+ inputSchema: { type: "object", properties: { order: { type: "array", items: { type: "string" } } }, required: ["order"] },
225
+ execute: wrap(async (args) => { ensureConfigDir(root); const providers = reorderProviders(root, args.order || []); agent.reloadProviders(); return { ok: true, providers }; }),
226
+ },
227
+
228
+ ];
229
+ }
230
+
231
+ // ---- 域 3: 设置读写 (2 个工具) ----
232
+ function settingsTools(agent, root) {
233
+ return [
234
+ {
235
+ name: "ppx.settings.get",
236
+ title: "读取设置",
237
+ description: "读取可编辑设置 (用户名/HTTP 端口/安全/agent 预设)。敏感字段只回 set 标志。",
238
+ inputSchema: { type: "object", properties: {} },
239
+ execute: wrap(() => { ensureConfigDir(root); return { settings: getSettings(root) }; }),
240
+ },
241
+ {
242
+ name: "ppx.settings.update",
243
+ title: "更新设置",
244
+ description: "更新设置 (patch: {user?, http?, security?, agent?, mcp?, tools?})。热重载立即生效。",
245
+ inputSchema: { type: "object", properties: { patch: { type: "object" } }, required: ["patch"] },
246
+ execute: wrap(async (args) => { ensureConfigDir(root); const settings = updateSettings(root, args.patch || {}); agent.reloadSettings(); return { ok: true, settings }; }),
247
+ },
248
+
249
+ ];
250
+ }
251
+
252
+ // ---- 域 4: 任务面板 (7 个工具) ----
253
+ function taskTools(agent, taskBoard) {
254
+ return [
255
+ {
256
+ name: "ppx.task.templates",
257
+ title: "任务模板列表",
258
+ description: "列出任务面板内置技能模板 (按技能预置步骤), 供新建任务时选用。含 agent 训练评估/会话重命名/提示词方案/代码审查/MCP 合规/技能吸收。",
259
+ inputSchema: { type: "object", properties: {} },
260
+ execute: wrap(() => TASK_TEMPLATES),
261
+ },
262
+ {
263
+ name: "ppx.task.create",
264
+ title: "创建任务",
265
+ description: "创建任务面板条目 (title/description/steps/template_id)。steps 为步骤标题数组, 每步初始 pending; 也可传 template_id 使用内置模板的步骤。",
266
+ inputSchema: { type: "object", properties: { title: { type: "string" }, description: { type: "string" }, steps: { type: "array", items: { type: "string" } }, template_id: { type: "string", description: "内置模板 id (见 ppx.task.templates)" } }, required: ["title"] },
267
+ execute: wrap((args) => {
268
+ const tpl = args.template_id ? TASK_TEMPLATES.find((t) => t.id === args.template_id) : null;
269
+ const steps = tpl ? tpl.steps : (args.steps || []);
270
+ return taskBoard.create({ title: args.title, description: args.description || (tpl ? tpl.label : ""), steps });
271
+ }),
272
+ },
273
+ {
274
+ name: "ppx.task.list",
275
+ title: "任务列表",
276
+ description: "列出全部任务 (含每步状态)。等价于任务面板读操作。",
277
+ inputSchema: { type: "object", properties: {} },
278
+ execute: wrap(() => taskBoard.list()),
279
+ },
280
+ {
281
+ name: "ppx.task.update",
282
+ title: "更新任务",
283
+ description: "更新任务状态或字段: {id, status?} status 为 todo|running|done|failed。",
284
+ inputSchema: { type: "object", properties: { id: { type: "string" }, status: { type: "string" } }, required: ["id"] },
285
+ execute: wrap((args) => taskBoard.update(args)),
286
+ },
287
+ {
288
+ name: "ppx.task.step",
289
+ title: "更新任务步骤",
290
+ description: "更新某任务某步骤: {id, index, status?, detail?} index 从 0 开始, status 为 pending|running|done|failed。",
291
+ inputSchema: { type: "object", properties: { id: { type: "string" }, index: { type: "number" }, status: { type: "string" }, detail: { type: "string" } }, required: ["id", "index"] },
292
+ execute: wrap((args) => taskBoard.step(args)),
293
+ },
294
+ {
295
+ name: "ppx.task.delete",
296
+ title: "删除任务",
297
+ description: "删除任务面板条目。",
298
+ inputSchema: { type: "object", properties: { id: { type: "string" } }, required: ["id"] },
299
+ execute: wrap((args) => taskBoard.delete(args)),
300
+ },
301
+ {
302
+ name: "ppx.task.run",
303
+ title: "运行任务",
304
+ description: "把任务交给 agent 执行: 标 running → 调 agent.chat 处理任务描述 → 全部步骤标 done → 结果回填任务 result。返回 agent 回复。",
305
+ inputSchema: { type: "object", properties: { id: { type: "string" }, prompt: { type: "string", description: "给 agent 的执行指令 (默认用任务 title+description)" } }, required: ["id"] },
306
+ execute: wrap(async (args) => {
307
+ const id = String(args.id);
308
+ const t = taskBoard.get(id);
309
+ if (!t) throw new Error("任务不存在");
310
+ taskBoard.update({ id, status: "running" });
311
+ const prompt = str(args.prompt) || `执行任务「${t.title}」: ${t.description || ""}`;
312
+ let reply;
313
+ try {
314
+ reply = await agent.chat(prompt, { sessionKey: "task:" + id });
315
+ } catch (e) {
316
+ // 2026-10-03 修复 (P1): chat 抛错时任务永久卡 running (僵尸运行态), 回滚为 failed
317
+ try { taskBoard.update({ id, status: "failed" }); } catch { /* 状态回滚失败不再抛 */ }
318
+ throw e;
319
+ }
320
+ // 全部步骤置 done, 任务置 done
321
+ taskBoard.complete(id, reply);
322
+ return { ok: true, id, result: reply };
323
+ }),
324
+ },
325
+ ];
326
+ }