@migor/agentia 0.10.4 → 0.10.6

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 (162) hide show
  1. package/CHANGELOG.md +281 -1
  2. package/README.en.md +1 -1
  3. package/README.md +2 -2
  4. package/dist/AGENTS.md +93 -11
  5. package/dist/container/container.js +3 -0
  6. package/dist/container/container.js.map +1 -1
  7. package/dist/core/abort.js +6 -1
  8. package/dist/core/abort.js.map +1 -1
  9. package/dist/core/json.js +4 -0
  10. package/dist/core/json.js.map +1 -1
  11. package/dist/core/limits.d.ts +13 -5
  12. package/dist/core/limits.js +14 -5
  13. package/dist/core/limits.js.map +1 -1
  14. package/dist/core/sse.d.ts +0 -13
  15. package/dist/core/sse.js +28 -2
  16. package/dist/core/sse.js.map +1 -1
  17. package/dist/core/timeout.d.ts +8 -0
  18. package/dist/core/timeout.js +19 -1
  19. package/dist/core/timeout.js.map +1 -1
  20. package/dist/core/tool.d.ts +18 -0
  21. package/dist/core/trace.d.ts +9 -0
  22. package/dist/core/trace.js +21 -0
  23. package/dist/core/trace.js.map +1 -1
  24. package/dist/engine/forwarded.d.ts +3 -1
  25. package/dist/engine/forwarded.js +5 -0
  26. package/dist/engine/forwarded.js.map +1 -1
  27. package/dist/engine/loop.d.ts +5 -1
  28. package/dist/engine/loop.js +45 -12
  29. package/dist/engine/loop.js.map +1 -1
  30. package/dist/engine/mcp-server.d.ts +17 -1
  31. package/dist/engine/mcp-server.js +36 -2
  32. package/dist/engine/mcp-server.js.map +1 -1
  33. package/dist/engine/policy.js +5 -1
  34. package/dist/engine/policy.js.map +1 -1
  35. package/dist/engine/replay.js +63 -1
  36. package/dist/engine/replay.js.map +1 -1
  37. package/dist/engine/run-config.d.ts +6 -0
  38. package/dist/engine/run-config.js +18 -6
  39. package/dist/engine/run-config.js.map +1 -1
  40. package/dist/engine/spec.d.ts +11 -1
  41. package/dist/engine/spec.js.map +1 -1
  42. package/dist/engine/stop-reason.js +19 -1
  43. package/dist/engine/stop-reason.js.map +1 -1
  44. package/dist/engine/tool-context.d.ts +3 -1
  45. package/dist/engine/tool-context.js +3 -1
  46. package/dist/engine/tool-context.js.map +1 -1
  47. package/dist/engine/tool-events.d.ts +7 -2
  48. package/dist/engine/tool-events.js +24 -3
  49. package/dist/engine/tool-events.js.map +1 -1
  50. package/dist/engine/tracer.js +10 -3
  51. package/dist/engine/tracer.js.map +1 -1
  52. package/dist/engine/turn.d.ts +9 -13
  53. package/dist/engine/turn.js +96 -10
  54. package/dist/engine/turn.js.map +1 -1
  55. package/dist/engine/types.d.ts +19 -2
  56. package/dist/engine/types.js +1 -0
  57. package/dist/engine/types.js.map +1 -1
  58. package/dist/engine/usage.js +12 -4
  59. package/dist/engine/usage.js.map +1 -1
  60. package/dist/index.d.ts +11 -3
  61. package/dist/index.js +7 -1
  62. package/dist/index.js.map +1 -1
  63. package/dist/integrations/anthropic.d.ts +9 -1
  64. package/dist/integrations/anthropic.js +42 -7
  65. package/dist/integrations/anthropic.js.map +1 -1
  66. package/dist/integrations/file-sink.d.ts +1 -0
  67. package/dist/integrations/file-sink.js +35 -32
  68. package/dist/integrations/file-sink.js.map +1 -1
  69. package/dist/integrations/mcp-http.js +33 -9
  70. package/dist/integrations/mcp-http.js.map +1 -1
  71. package/dist/integrations/mcp-protocol.js +33 -16
  72. package/dist/integrations/mcp-protocol.js.map +1 -1
  73. package/dist/integrations/metrics-otlp.js +4 -0
  74. package/dist/integrations/metrics-otlp.js.map +1 -1
  75. package/dist/integrations/metrics.js +17 -2
  76. package/dist/integrations/metrics.js.map +1 -1
  77. package/dist/integrations/openai.d.ts +6 -1
  78. package/dist/integrations/openai.js +45 -8
  79. package/dist/integrations/openai.js.map +1 -1
  80. package/dist/integrations/otlp.js +6 -0
  81. package/dist/integrations/otlp.js.map +1 -1
  82. package/dist/runtime/memory.d.ts +0 -8
  83. package/dist/runtime/memory.js +24 -2
  84. package/dist/runtime/memory.js.map +1 -1
  85. package/dist/runtime/run.js +34 -9
  86. package/dist/runtime/run.js.map +1 -1
  87. package/dist/store/fsStore.d.ts +2 -0
  88. package/dist/store/fsStore.js +38 -5
  89. package/dist/store/fsStore.js.map +1 -1
  90. package/dist/store/record.d.ts +40 -0
  91. package/dist/store/record.js +72 -0
  92. package/dist/store/record.js.map +1 -1
  93. package/dist/store/redisStore.js +15 -4
  94. package/dist/store/redisStore.js.map +1 -1
  95. package/dist/store/sqliteStore.js +39 -14
  96. package/dist/store/sqliteStore.js.map +1 -1
  97. package/dist/store/store.d.ts +6 -1
  98. package/dist/store/store.js +10 -4
  99. package/dist/store/store.js.map +1 -1
  100. package/dist/toolkit/capability-loop.d.ts +92 -0
  101. package/dist/toolkit/capability-loop.js +81 -0
  102. package/dist/toolkit/capability-loop.js.map +1 -0
  103. package/dist/toolkit/manifest.d.ts +21 -0
  104. package/dist/toolkit/manifest.js +38 -0
  105. package/dist/toolkit/manifest.js.map +1 -0
  106. package/dist/toolkit/module.d.ts +5 -0
  107. package/dist/toolkit/module.js +9 -0
  108. package/dist/toolkit/module.js.map +1 -1
  109. package/dist/toolkit/prompt.d.ts +3 -1
  110. package/dist/toolkit/prompt.js +22 -3
  111. package/dist/toolkit/prompt.js.map +1 -1
  112. package/dist/toolkit/skill.d.ts +3 -1
  113. package/dist/toolkit/skill.js +2 -0
  114. package/dist/toolkit/skill.js.map +1 -1
  115. package/dist/toolkit/subagent.d.ts +3 -1
  116. package/dist/toolkit/subagent.js +2 -0
  117. package/dist/toolkit/subagent.js.map +1 -1
  118. package/dist/toolkit/tool.d.ts +3 -1
  119. package/dist/toolkit/tool.js +2 -0
  120. package/dist/toolkit/tool.js.map +1 -1
  121. package/dist/transport/approval-policy.d.ts +1 -1
  122. package/dist/transport/approval-policy.js +18 -3
  123. package/dist/transport/approval-policy.js.map +1 -1
  124. package/dist/transport/approval-supervisor.d.ts +15 -3
  125. package/dist/transport/approval-supervisor.js +37 -11
  126. package/dist/transport/approval-supervisor.js.map +1 -1
  127. package/dist/transport/async.d.ts +3 -4
  128. package/dist/transport/async.js +157 -30
  129. package/dist/transport/async.js.map +1 -1
  130. package/dist/transport/drain-gate.js +13 -1
  131. package/dist/transport/drain-gate.js.map +1 -1
  132. package/dist/transport/http-endpoints.d.ts +13 -0
  133. package/dist/transport/http-endpoints.js +108 -28
  134. package/dist/transport/http-endpoints.js.map +1 -1
  135. package/dist/transport/http-shapes.d.ts +26 -3
  136. package/dist/transport/http-shapes.js +12 -2
  137. package/dist/transport/http-shapes.js.map +1 -1
  138. package/dist/transport/http.d.ts +46 -2
  139. package/dist/transport/http.js +43 -18
  140. package/dist/transport/http.js.map +1 -1
  141. package/dist/transport/resume-scanner.d.ts +3 -0
  142. package/dist/transport/resume-scanner.js +10 -1
  143. package/dist/transport/resume-scanner.js.map +1 -1
  144. package/dist/transport/run-call-options.js +46 -1
  145. package/dist/transport/run-call-options.js.map +1 -1
  146. package/dist/transport/run-options.d.ts +98 -0
  147. package/dist/transport/run-options.js +121 -0
  148. package/dist/transport/run-options.js.map +1 -0
  149. package/dist/transport/run-outcome.js +21 -1
  150. package/dist/transport/run-outcome.js.map +1 -1
  151. package/dist/transport/serve.js +10 -0
  152. package/dist/transport/serve.js.map +1 -1
  153. package/dist/transport/signal-supervisor.d.ts +3 -0
  154. package/dist/transport/signal-supervisor.js +5 -1
  155. package/dist/transport/signal-supervisor.js.map +1 -1
  156. package/dist/transport/stream-janitor.d.ts +45 -0
  157. package/dist/transport/stream-janitor.js +137 -0
  158. package/dist/transport/stream-janitor.js.map +1 -0
  159. package/dist/transport/task-wake-gate.d.ts +40 -0
  160. package/dist/transport/task-wake-gate.js +69 -0
  161. package/dist/transport/task-wake-gate.js.map +1 -0
  162. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -5,6 +5,284 @@
5
5
  (0.x 阶段:minor 可含破坏性变更,每个破坏性变更都在对应版本的「迁移」小节里写明)。
6
6
  决策的完整证据链在 `docs/spec.md` §10(带时间线的决策日志)。
7
7
 
8
+ ## [0.10.6] - 2026-11-09
9
+
10
+ ### 变更
11
+
12
+ 本版是一次**正确性收口**,不新增能力面:修掉八类「静默失效」,其中三处会改变调用方能观察到的
13
+ 行为,需要在升级前确认。两包同步 0.10.5 → 0.10.6。
14
+
15
+ - ⚠️ **MCP 反向桥默认不再暴露 `approval: 'required'` 的工具**(安全修)。MCP 没有审批挂起通道,
16
+ 暴露即等于绕过审批。需要这些人批工具的人显式设 `exposeApprovalRequiredTools: true`;
17
+ 被隐藏时构造期会打一行 warn **点名数量**,不静默少工具。
18
+ - ⚠️ **所有出站请求不再跟随重定向**(安全修)。307/308 会把鉴权头与请求体原样转到另一个
19
+ origin。依赖 3xx 跳转的外部端点现在会**报错**而不是静默跟过去,请把最终地址配进去。
20
+ - ⚠️ **坏值从「静默忽略」改成「响亮拒绝」**:`maxEventChars: 0` 现在真的截到空串
21
+ (此前被当成「不限」);`toolTimeoutMs` / `maxEventChars` / `maxToolResultChars` /
22
+ `awaitTask` 的 `timeoutMs`·`intervalMs` / OTLP·metrics 的 `timeoutMs` 在入口校验,
23
+ NaN·负数·小数直接抛。`metricsSink.intervalMs` 的负数与 `0` 同义(立即导出),已写进
24
+ `LIMIT_SEMANTICS` 表。
25
+ - 四条唤醒路径(`approve × signalTask` / `wakeDue × signalTask`)共用一把 per-task 互斥闸,
26
+ 同一任务不再被派发两遍;`cancel` 等同一张表的 tail。
27
+ - 审批决定带作用域:只认 `requestedAt` 与本次挂起匹配的决定,同一 `tool_use_id` 每段只消费一次
28
+ —— 修掉 OpenAI 兼容端点复用 `call_1` 导致的审批绕过。
29
+ - 失败尝试的部分 usage 落账:流中断前已收到的计量挂到错误上并写 `usage.partial`,
30
+ `maxCostUsd` / `maxTotalTokens` 不再系统性少算。
31
+ - 跨进程 SSE 兜底从「每连接一个 5s 定时器」提到 runner 级单例:一拍里每个任务只读一次 store,
32
+ 定时器有登记才存在;`streamTask` 的 `pollMs` 从此是开关(`<= 0` = 该流不参与兜底)。
33
+ - 落库剥离 `client` / `tools` 时留下可读痕迹,恢复段按 taskId 告警一次 —— 此前 per-task 自定义
34
+ client 的任务重启续跑会**静默**打到 runner 的端点。
35
+ - JSON / SQLite / Redis 三条 store 的恢复路径加固(读失败出声、半行写入回滚、迁移事务化、
36
+ SCAN 去重、水合失败跳过回写),详见 `docs/spec.md` §10。
37
+ - 构建面:四个 `package-lock.json` 的 `resolved` 从第三方镜像站改回官方 registry(共 463 处),
38
+ 两个示例 `Dockerfile` 加拉包重试。此前 2026-11-09 镜像站 5xx 让「构建 deploy 示例镜像」连挂
39
+ 两轮(`ER_NO_SUCH_TABLE` / `504`);改后该 job 从 174–368s 降到 20s。⚠️ 它与 `src/` 无关,
40
+ 但影响**从零安装**:`npm ci` 的 tarball 来源现在是官方源(镜像站用户仍可由自己的 npm 配置改写)。
41
+
42
+ ### ⚠️ 行为变更(安全修):MCP 反向桥默认隐藏 HITL 工具(2026-11-07 ref-deep-review-2026-11-07)
43
+
44
+ - **修的是什么**:`createMcpServer` 此前把 `app.tools` 整个交给 MCP 客户端。而
45
+ `@Tool({ approval: 'required' })` 的语义是「调用前必须有人批」,MCP 这条通道**没有**挂起与
46
+ 回调的通道 —— 工具被列出去就等于「审批被绕过」,且没有任何一处会报错。
47
+ - **现在**:默认过滤掉 `approval: 'required'` 的工具;确有需要(例如客户端自己实现了外部审批)
48
+ 显式设 `exposeApprovalRequiredTools: true`。过滤发生时打一行 warn 说明数量与原因 ——
49
+ 「明明写了 `@Tool`,MCP 客户端却看不到」不该靠猜。
50
+ - **迁移**:升级后如果 MCP 客户端少了一批工具,先看启动日志里那行 warn。要恢复旧行为就开那个开关,
51
+ 但请自行确保这些工具的人批在你的客户端侧成立。
52
+
53
+ ### ⚠️ 行为变更(安全修):出站 `fetch` 不跟随重定向
54
+
55
+ - **修的是什么**:默认的 `fetch` 跟 3xx,而 307/308 **保留方法与请求体** —— 一次被中间人控制或
56
+ 被错误配置的重定向,会把 `Authorization` 头与 prompt 原文发到另一个 origin。这条链上没有一个
57
+ 环节会报错,日志里只有一次成功的 `POST`。
58
+ - **现在**:所有出站调用(模型适配器、OTLP/metrics 导出、MCP HTTP)一律 `redirect: 'error'`,
59
+ 收到 3xx 直接失败。
60
+ - **迁移**:如果你的模型网关 / OTLP collector 依赖 301/302/307 跳转,请把最终地址直接配进去。
61
+ 报错信息里带原始状态码与 `Location`,照它改即可。
62
+
63
+ ### 唤醒互斥与取消优先(2026-11-07 ref-deep-review-2026-11-07)
64
+
65
+ - **修的是什么**:`approve` 与 `signalTask`、`wakeDue` 与 `signalTask` 各自凭**同一份** suspended
66
+ 快照判断「该我唤醒」,于是同一任务被派发两遍;两次执行都落库,后写的覆盖前写的,
67
+ 审批决定可能凭空消失,而 usage 记了两份。
68
+ - **现在**:四条唤醒路径共用 `transport/task-wake-gate.ts` 的 per-task 串行闸,同步段一次性登记
69
+ (无 await,无 TOCTOU 窗口);`cancel` 排在同一个 key 的 tail 上,不会插进正在进行的唤醒。
70
+ - **取消优先**:引擎的续跑段与主循环的 approval / deferred 出口统一补 abort 守卫;宿主在
71
+ suspended 分支再判一次取消意图 —— 修掉「cancel 等不到终态、超时定时器到点又跑一遍」。
72
+ - **验证**:`tests/transport/wake-gate.test.ts`、`tests/transport/drain-gate.test.ts`;反向变异
73
+ 确认「摘掉 gates」能复现双派发。
74
+
75
+ ### 审批决定的作用域(2026-11-07 ref-deep-review-2026-11-07)
76
+
77
+ - **修的是什么**:审批决定此前只按 `tool_use_id` 匹配。OpenAI 兼容端点每次都从 `call_1` 开始编号,
78
+ 于是**上一段**挂起时批过的 `call_1`,会被下一段的 `call_1` 当成「已批准」直接放行 ——
79
+ 一次静默的审批绕过。
80
+ - **现在**:`RunAgentOptions.approvalEpoch` 标出本次挂起的身份(异步宿主自动注入
81
+ `TaskRecord.suspendedSince`),只认 `requestedAt` 匹配的决定;同一段内每个 `tool_use_id` 也只
82
+ 消费一次(`consumedApprovals`)。三处判据(写入时的 `requestedAt`、`fillTimeoutDenials` 的
83
+ stale 覆盖、`approvalsComplete(rec, since)`)用的是同一个 epoch。
84
+ - **验证**:`tests/engine/approval-epoch.test.ts`(含「旧决定不得跨段复用」与「同段只消费一次」
85
+ 两条正面用例)。
86
+
87
+ ### 失败尝试的计量落账(2026-11-07 ref-deep-review-2026-11-07)
88
+
89
+ - **修的是什么**:适配器在流中断(网络断、上游 5xx、超时)时把整个 turn 判为失败,而**中断前已
90
+ 收到的** `input_tokens` / `output_tokens` 随之丢掉。重试三次的实际花费是三次之和,
91
+ `maxCostUsd` 只看到最后一次 —— 预算闸系统性偏松。
92
+ - **现在**:适配器把已收到的计量挂到错误对象上,引擎记进失败 turn 的 span 并写 `usage.partial`
93
+ 事件;`usageTotal` 因此包含失败尝试。
94
+ - **验证**:`tests/engine/review-fixes.test.ts` 覆盖「首两次失败 + 第三次成功」的总账。
95
+
96
+ ### 流生命周期与跨进程兜底(2026-11-07 / 2026-11-09 ref-stream-janitor-singleton)
97
+
98
+ - **修的是什么(跨进程)**:任务被另一个副本接管或已落终态时,本进程那条 SSE 不会自己结束 ——
99
+ 客户端永远等不到 `end` / `closed`。此前由 `streamTask` 给**每个订阅**各建一个 5s 定时器去
100
+ `store.get` 判断:N 个并发连接 = N 个定时器 + N/5 QPS,SQLite 下就是 N/5 次读锁;`unref()`
101
+ 只保证不挂住进程,不减读放大。
102
+ - **现在**:`transport/stream-janitor.ts` 改成「组」的形状 —— 一拍里每个**任务**只读一次 store,
103
+ 再把结论分发到该任务的所有订阅;定时器**有登记才存在**,最后一条注销即停。一拍里的意外
104
+ (判据抛错)被 catch 住,不会变成 `unhandledRejection` 打死宿主。
105
+ - **其余收口**:`streamTask` 的 `onDone` 补 rejection 处理(store 抖动不再打死进程);
106
+ SSE 单行复用 8M 码元上限并 flush 尾字节;上限判据改成**切出来的那一帧**(此前判整个 `buf`,
107
+ 一次 read 交付多行大 chunk 会被误报成「单行超限」);MCP HTTP 的 SSE 改增量读
108
+ (server 不关流不再让 `tools/call` 永挂)。
109
+ - **验证**:`tests/transport/stream-janitor.test.ts`(六条故障路径 + 两条结构不变量),
110
+ 行覆盖 100%、分支 89.58%;`tests/core/sse-text-stats.test.ts` 含「单帧 9M 仍要拒」的对照。
111
+
112
+ ### 持久化与恢复(2026-11-07 / 2026-11-09 ref-resume-dropped-refs)
113
+
114
+ - **修的是什么(落库)**:`JSON.stringify` 把 `AbortSignal` 变成 `{}`、把 `ModelClient` 变成
115
+ `{messages:{}}`、把 `tools` 里的 run 函数丢掉。读回后这些空壳会在恢复段被当成真对象使用
116
+ (`combineSignals({})` 直接 TypeError,工具菜单半残)。现在三条 JSON store 统一在落库前剥离。
117
+ - **修的是什么(剥离的副作用)**:剥离之后,「提交时本来没传 `client`」与「传了但被剥掉」在
118
+ 读回时**完全同形**,于是 per-task 自定义 client(典型是 OpenAI 兼容端点)的任务重启续跑时会
119
+ **静默**退回 runner 的端点。现在剥离时记下清单(`__agentiaDroppedRefs`),装配处按 taskId
120
+ 去重告警一次,写明「本段已退回 runner client / 装配菜单」与出路;键在装配前剔除,不进引擎。
121
+ - **其余加固**:FileTaskStore 读失败出声且禁止 compact、半行写入回滚;SQLite 迁移事务化、
122
+ `healRow` 用旧 json 做 CAS;Redis `list()` 对 SCAN 结果去重、单 key 错不再整表 reject;
123
+ Memory 水合失败跳过回写;runtime 的手工 HITL 续跑不再重复 load/append。
124
+ - **验证**:`tests/store/serialize-options.test.ts`(剥离标记三态 + 告警只响一次);
125
+ `tests/transport/resume-scanner.test.ts`。
126
+
127
+ ### 依赖
128
+
129
+ - 官网构建链:`astro` 7.3.8 → 7.4.5、`gsap` 3.15.0 → 3.15.1、`lenis` 1.3.26 → 1.3.27。
130
+ - 开发链:dev-dependencies 组 5 项升级;`@biomejs/biome` 适配到 2.6.4
131
+ (排除文档/YAML 格式化、`useMathMinMax`)。
132
+ - 框架本身**运行时依赖仍为 0**。
133
+
134
+ ## [0.10.5] - 2026-11-05
135
+
136
+ ### 变更
137
+
138
+ 本版包含**可信边界收紧、可授权可对外暴露的生产宿主面、以及「顺序由代码定」的子循环**:
139
+
140
+ - ⚠️ **行为变更(安全修)**:`POST /tasks` 的 `options` 只认 `traceContext` / `maxCostUsd` /
141
+ `maxTotalTokens`,**其余键一律 400 并点名**;身份与租户改由宿主新增的
142
+ `resolveRunOptions(req, { route, principal })` 按请求注入(`authenticate` 的返回值从此不再被丢弃)。
143
+ 在 HTTP 的 `options` 里传过 `model` / `blackboard` / `maxIterations` 的调用方**必须改接法**,
144
+ 迁移见下方该条目。
145
+ - 新增**任务对象级授权** `authorizeTask`(poll / approve / cancel / event / stream 逐动作判定,
146
+ 读任务快照不触发惰性推进)与**可信审批人** `resolveApprovalActor`(由 principal 生成 `decidedBy`,
147
+ 覆盖请求体自报身份);两者缺省不配 = 旧行为。
148
+ - 新增 `responseMode: 'full' | 'public'` 与 `exposeTraceEvents`:把 trace 的**存储面**与
149
+ **HTTP 暴露面**分开 —— 对外服务可以不返回 trace / 任务 spec / 挂起历史 / `ownerId`,
150
+ 而 `TaskStore` 与 sinks 仍保留完整记录。
151
+ - 新增 `runCapabilityLoop(ctx, opts)`:顺序由**代码**定、但活不过这次请求的子循环;
152
+ 配 `@Tool({ approval: 'required' })` 就能挂起等人批,且与调用方**同一棵** trace。
153
+ - 新增**能力契约** `buildCapabilityManifest` / `app.getCapabilityManifest()` 与 CLI `agentia manifest`
154
+ (真装配、不调模型);四类能力可标 `metadata.{owner,version,risk,sideEffect}`,
155
+ hash 覆盖描述 / schema / 审批 / 元数据且**与输入顺序无关**,可当 PR 留档的比较基座。
156
+ - 挂起恢复的**预算按任务累计**(`TaskRecord.usageTotal` + 段内 `usageOffset`),恢复段的会话输入
157
+ 随任务落库(`sessionInput`);新增 `maxToolResultChars`,把「回模型的截断」与「记账的截断」
158
+ 拆成两条**互相独立**的闸。
159
+ - 新增 `docs/multi-replica.md`(三种拓扑 + 六个会坏的点 + 故障转移 runbook)与
160
+ `examples/super-agent` 多工单编排示例(按产物自己复算 trace 图,已纳入 e2e 门禁)。
161
+ - 另含公共导出补齐(`parseTaskRecord`、`CLIENT_RUN_OPTION_KEYS`、`TaskHttpResponse`、
162
+ `CapabilityMetadata` 等类型面扩展),详细条目如下。
163
+
164
+ ### ⚠️ 行为变更(安全修):`POST /tasks` 的 `options` 白名单化 + 宿主 `resolveRunOptions` 缝(2026-11-04 ref-run-options-trust-boundary)
165
+
166
+ - **修的是什么**:`POST /tasks` 此前把 `body.options` **整体透传**给引擎,而它是
167
+ `RunInvocationOptions` 的全套旋钮。任何能调这个端点的人都能写
168
+ `{ blackboard: { tenant: '别人的租户' } }`,而 usage-guide §6.6 的多租户配额配方第一步正是
169
+ 「用黑板存 tenant」⇒ 额度记到别人头上。同一件事在 `POST /run` 上却是**死路**(body 是 `RunInput`,
170
+ 没有 options 入口):需要身份的那条拿不到,拿得到的那条不可信。
171
+ - **现在**:客户端只能设 `traceContext` / `maxCostUsd` / `maxTotalTokens`,**其余键一律 400 并点名**
172
+ (`blackboard` / `tools` / `model` / `fallbacks` / `priceOverrides` …)。身份 / 租户由新增的
173
+ `HttpHandlerOptions.resolveRunOptions(req, { route, principal })` 按请求注入 —— `authenticate`
174
+ 的返回值从此**不再被丢弃**,作为 `principal` 传进该钩子(此前注释建议「存在钩子自己的闭包里」,
175
+ 但闭包与 run 之间没有任何关联手段)。`/run` 与 `/tasks` 都过这条缝。
176
+ - **合并语义(三条各不相同)**:`blackboard` **整体替换**(身份载体不允许与客户端键合并);
177
+ `labels` 按键盘合并、服务端胜;预算与客户端值取**更严者** —— 上限不能由叫价方抬高。
178
+ - **迁移**:若你在 HTTP 的 `options` 里传了上述按键之外的字段(例如 `maxIterations` / `model` /
179
+ `blackboard`),请求现在会 400。这些旋钮请改成应用级配置(`createApp` / `RunnerOptions`),
180
+ 或由宿主在 `resolveRunOptions` 里注入。TS 侧 `TaskSubmitBody.options` 从 `RunInvocationOptions`
181
+ 收窄为 `ClientRunOptions`,这类写法会**当场编译报错**。
182
+ - **验证**:`tests/transport/http-run-options.test.ts`(9 例)+ `tests/transport/run-options.test.ts`(9 例)。
183
+ 反向变异 4/4 咬住(放行未知键 / 上限取更宽 / 钩子结果不生效 / `principal` 不转交);
184
+ 实测读数:`options.blackboard` → 400 且任务表为空;宿主注入后工具方法体里读到的是
185
+ `tenant-of-u-42`(来自 `authenticate`)。决策:`docs/spec.md` §10 ref-run-options-trust-boundary。
186
+
187
+ ### 新增:`runCapabilityLoop(ctx, opts)` —— 代码控制顺序的子循环(2026-11-04 ref-run-capability-loop)
188
+
189
+ - **补的是哪一格**:顺序由**代码**定、但活**不过**这次请求 —— `@SubAgent` / `@Skill` 的子循环审批挂不起
190
+ 整条 run、`contextPolicy` 也不进子循环;`AsyncRunner` 的 task 能挂起能续跑,但要为它造一条任务记录。
191
+ 中间这一格此前只能手写 `runAgentScoped` + 自己转发 `ToolRunContext` 的七个旋钮(漏一个是**静默**的)。
192
+ - **形状**:`runCapabilityLoop(ctx, { name?, input, system?, tools?, model?, contextPolicy?, resultSchema?, onText? })`
193
+ → `{ text, typed, stopReason, iterations, spanId, error? }`。内部 `...forwardToolContext(ctx)` 取自唯一单源、
194
+ 会 combine「放弃等待」信号、自建一个 `capability` span(**同树**,`runId` 不多一个)。
195
+ - **配 `@Tool({ approval: 'required' })` 就能挂起等人批**:审批落在**外层工具调用**上,于是
196
+ 「代码定顺序 + 子循环可裁剪 + 人批可挂起 + 同一棵树」四样可以同时要。
197
+ - **刻意不导出 `runAgentScoped`**:它把「哪些旋钮必须下交」的责任交给调用方,而 TS 对漏写可选字段
198
+ 不报错。公共面只给「不可能漏」的那个形状。`@SubAgent` / `@Skill` 的行为一个字没变。
199
+ - **验证**:`tests/toolkit/capability-loop.test.ts`(5 例:同树 / `typed` / `contextPolicy` 生效 /
200
+ 预算护栏经 ctx 透传 / 工具菜单不继承)。反向变异:去掉 `...forwardToolContext(ctx)` ⇒ 预算用例红。
201
+
202
+ ### 新增:`examples/super-agent` 多工单编排示例,并纳入 e2e 门禁(2026-11-03)
203
+
204
+ - **示例形状**:Router 把一句目标拆成可并行工单,Worker 走 `AsyncRunner` 的 **task**(不是 `@SubAgent`)、
205
+ 各自独立上下文;改数据的动作标 `approval: 'required'`,挂起等人批后恢复;四棵 trace 靠 `traceContext`
206
+ 连成一条链。`npm run demo` 零网络、零 key、约 30ms 自检。
207
+ - **门禁**:`scripts/e2e-examples.ts` 第 11 步(`verifySuperAgent`)按示例自己的 `npm run build` 构建,
208
+ 再跑 dist 形态的 demo,然后**从它落的产物自己复算一遍 trace 图**(唯一的根 / 每条树几跳回家 /
209
+ 哪条是恢复段),不转发 demo 的 `hopsTo` 结论。产物只在 `SUPERAGENT_DEMO_OUT` 指向文件时写,
210
+ 交互式 demo 保持零副作用。`examples/super-agent/src` 同时进了 `tsconfig.tests.json`(类型层面也纳入门禁)。
211
+ - **反向验证(三个变异 + 一个反方向探针)**:中和 demo 全部断言后 —— 去掉 Worker 的 `traceContext`
212
+ ⇒ 门禁红「每条 trace 都要沿 links 回得到 Router,断了的是 […]」;摘掉 `write_file` 的 `approval`
213
+ ⇒ 门禁红「要审批的工单应产生两棵 trace(挂起段 + 恢复段),实际 1」。反方向:实现不动、只让 demo 的
214
+ `hopsTo` 谎报一跳 ⇒ 门禁**仍绿**且自己算出 `worker-fix@2` —— 它算的是产物,不是文案。还原后 `sha256` 逐字节一致。
215
+
216
+ ### 文档:多副本部署参考架构(新增 `docs/multi-replica.md`)
217
+
218
+ - **补的是哪一格**:`deployment.md` §8 此前只有一句话结论(「单宿主写者 + 认领语义不等于分布式锁」),
219
+ 没有回答「那多副本到底怎么摆」。文档缺口会让每个接入方各自试错,而试错的代价是**同一任务跑两遍**。
220
+ - **内容**:三种拓扑的选型判据(单副本 / 单机多进程 / 单写者+只读副本 / 按 `taskId` 分片)、
221
+ 六个会坏的点(写路径交错、审批双派发、唤醒抢所有权、`cancel` 打不到 owner、跨进程 SSE、
222
+ 定时任务每个副本各触发一次)、故障转移 runbook、粘性路由三种实现、多副本自检清单。
223
+ - **一个此前没被写下来的陷阱**:`createHttpHandler` 的 `runner` 是可选的,缺省会造一个
224
+ `InMemoryTaskStore` 的 runner(`http.ts` + `async.ts` 两处缺省)。漏接线的副本表现为
225
+ 「同一个任务 id 在 A 查得到、B 返 404」,**不报错也不告警**。
226
+ - **如实标注的边界**:`approve` / `signal` 的互斥是**进程内**的(`#inflight` Map),
227
+ 跨进程并发会各派发一次;`drain()` 是单向闩,停机后被拒的记录只能等下次启动认领。
228
+ 这些是设计取舍,不是待修缺陷 —— 文档的职责是让部署方知道要自己兜。
229
+
230
+ ### 新增:任务对象级授权 `authorizeTask` 与可信审批人 `resolveApprovalActor`(2026-11-05 ref-http-task-authorization)
231
+
232
+ - **补的是哪个洞**:`authenticate` 只回答「你是谁」,不回答「你能不能碰这个任务」。多租户宿主
233
+ 此前只能在**每个**端点里自己查一次,漏一处的表现是「别人的任务能被取消 / 被批准 / 被读事件」。
234
+ - **形状**:`authorizeTask(req, { action, taskId, task, principal })` —— action 取
235
+ `poll` / `approve` / `cancel` / `event` / `stream`,返回 `false` → 403,抛 `HttpException`
236
+ 按其状态码回;审批与事件的判定发生在**读 body 之前**。缺省不配 = 旧行为(不拦)。
237
+ - **为什么不走 poll 取快照**:它直接读 `runner.store.get`。走 poll 会让「查一次权限」触发
238
+ 惰性超时判定与到期推进 —— 授权检查不该有副作用(`docs/spec.md` §10 该条有完整理由)。
239
+ - **`decidedBy` 不再是自报字段**:配 `resolveApprovalActor` 后由 principal 生成并**覆盖**请求体;
240
+ 返回 `undefined` = 不记审批人,空字符串算宿主配置错误(500)。不配则 `decidedBy` 仍只是
241
+ 调用方填写的审计字段,不是可信身份。
242
+ - **验证**:`tests/transport/http-authorization.test.ts`(五个动作 × 拒/过/抛错映射)+
243
+ `tests/transport/httpApproval.test.ts` 的可信审批人用例。
244
+
245
+ ### 新增:`responseMode: 'public'` —— trace 的存储面与 HTTP 暴露面分开(2026-11-05 ref-public-response-mode)
246
+
247
+ - **补的是哪个洞**:trace 既是本框架的卖点,也是**信息泄露面** —— 它含工具入参出参、
248
+ 任务 `spec.messages`、挂起历史与 `ownerId`。此前接对外服务只有「全关端点」或「全露」两个选择。
249
+ - **形状**:`responseMode: 'full'`(缺省,历史形状)/ `'public'`;后者从 run / task 响应里去掉
250
+ trace、任务 spec、`suspendedMessages`、`ownerId` / `pendingEvents`,并**默认关闭** SSE 的
251
+ `trace.event` 帧。`exposeTraceEvents: true` 可单独开帧,但不会把完整 trace 加回 `run.end`。
252
+ - **边界**:它只改 HTTP 输出 —— `TaskStore` 与 `TraceSink` 的记录**保持完整**(脱敏不是存储职责);
253
+ 也不能替代 `authorizeTask`,两者一个管「这条响应里有什么」,一个管「你能不能读这条任务」。
254
+ - **单源**:公共投影只在 `http-shapes.ts` 一处实现,所有任务响应与 `task.end` 共用,
255
+ 避免某条路由漏脱敏。验证:`tests/transport/http-response-mode.test.ts`。
256
+
257
+ ### 新增:能力契约 `agentia manifest` 与四类能力的 `metadata`(2026-11-05 ref-capability-manifest)
258
+
259
+ - **补的是哪个洞**:多人协作时「这次 PR 到底改了哪个能力的哪个 schema」只能靠读 diff。
260
+ - **形状**:`@Tool` / `@Skill` / `@SubAgent` / `@Prompt` 增加可选
261
+ `metadata: { owner?, version?, risk?, sideEffect? }`;`buildCapabilityManifest` /
262
+ `app.getCapabilityManifest()` 从**最终菜单**产出 `{ app, hash, capabilities }`,
263
+ hash 覆盖名称 / 类别 / description / schema / strict / 审批 / 元数据,**与输入顺序无关**
264
+ ⇒ 可以直接当 PR 留档的比较基座。CLI `agentia manifest` 真装配 `src/app.ts`、**不调用模型**。
265
+ - **不做什么**:它是留档与比较基座,不替代 code review,也不执行安全策略。
266
+ - **验证**:`tests/toolkit/manifest.test.ts`;`scripts/e2e-cli.ts` 真跑产物路径。
267
+
268
+ ### 改动:恢复段预算按任务累计 + 工具结果回模型截断(2026-11-05 ref-durable-resume-accounting / ref-tool-result-limit)
269
+
270
+ - **预算**:`TaskRecord` 新增 `usageTotal`(每个已收尾段折进去)与 `sessionInput`(首个挂起段
271
+ 存下本轮输入,恢复成功后补写会话、终态清除)。恢复段经段内 `usageOffset` 参与
272
+ `maxTotalTokens` / `maxCostUsd`,主/子循环同口径;`trace.totalUsage` 仍只代表本段。
273
+ - **如实标注的边界**:进程崩在「模型已花 / 工具已执行、结果尚未落库」那个窗口时,那笔用量
274
+ **追不回来** —— 它不是精确的全局账单。旧版本留下的挂起记录没有 `sessionInput`,仍只能跳过补写。
275
+ - **`maxToolResultChars`**:新旋钮,只裁**回模型**的 `tool_result`(缺省 / `false` = 不截断,
276
+ `0` = 截到零长度并留 `…(+N)`),经 `forwardToolContext` 透传嵌套循环;与 `maxEventChars`
277
+ (只管记账)**互相独立**、不改变副作用。验证:`tests/engine/tool-result-limit.test.ts`
278
+ 真穿两回合,断言模型看到截断而 trace 仍完整。
279
+
280
+ ### 新增:`parseTaskRecord` 公共导出(2026-11-05 ref-task-record-parser-public)
281
+
282
+ - 外部 `TaskStore` 不再用 `JSON.parse(raw) as TaskRecord` 绕过旧形状归一:
283
+ 公共入口做 JSON 解析 + 形状守卫 + 旧版本归一,坏 JSON 抛错、非记录返回 `undefined`
284
+ (坏数据吞不吞仍由各 store 决定)。`docs/database.md` 的 MySQL 骨架改走该入口,代码围栏真编译。
285
+
8
286
  ## [Unreleased]
9
287
 
10
288
  ## [0.10.4] - 2026-10-28
@@ -4148,7 +4426,9 @@ toolkit 142 / runtime 55 / engine 403 / container 10 / store 55 —— 全绿。
4148
4426
  首个公开发布:`@migor/agentia` + `@migor/cli`(scope `@migor/*`),两包版本同步。
4149
4427
  框架本体单包;CLI 独立成包(workspaces)。
4150
4428
 
4151
- [Unreleased]: https://github.com/migoric/agentia/compare/v0.10.4...HEAD
4429
+ [Unreleased]: https://github.com/migoric/agentia/compare/v0.10.6...HEAD
4430
+ [0.10.6]: https://github.com/migoric/agentia/releases/tag/v0.10.6
4431
+ [0.10.5]: https://github.com/migoric/agentia/releases/tag/v0.10.5
4152
4432
  [0.10.4]: https://github.com/migoric/agentia/releases/tag/v0.10.4
4153
4433
  [0.10.3]: https://github.com/migoric/agentia/releases/tag/v0.10.3
4154
4434
  [0.10.2]: https://github.com/migoric/agentia/releases/tag/v0.10.2
package/README.en.md CHANGED
@@ -8,7 +8,7 @@
8
8
  with decorators + DI and a main agent orchestrates them; every run yields structured output and an
9
9
  observable call tree (trace, cost, metrics).
10
10
 
11
- **1 package · ≈2 MB · 0 runtime dependencies · 90+ guards** — recomputed by
11
+ **1 package · ≈3 MB · 0 runtime dependencies · 90+ guards** — recomputed by
12
12
  `tests/docs/scoreboard.test.ts` on every run.
13
13
 
14
14
  > This is a **summary**, kept short on purpose (a hand-maintained full translation drifts). The
package/README.md CHANGED
@@ -10,7 +10,7 @@ observable call tree (trace, cost, metrics).
10
10
 
11
11
  面向应用开发的声明式 agent 服务开发框架:装饰器 + DI 声明四类能力,主 agent 编排执行;每次 run 产出结构化结果与可观测调用树(trace、成本、指标),交付可直接上线的服务。
12
12
 
13
- **1 个包 · ≈2 MB · 0 运行时依赖 · 90+ 条守卫** —— 由 `tests/docs/scoreboard.test.ts` 每次现场复算。
13
+ **1 个包 · ≈3 MB · 0 运行时依赖 · 90+ 条守卫** —— 由 `tests/docs/scoreboard.test.ts` 每次现场复算。
14
14
 
15
15
  - [安装与配置](#安装与配置)
16
16
  - [快速开始](#快速开始)
@@ -43,7 +43,7 @@ npm run dev -- "你的问题" # = tsx src/main.ts,跑一次并
43
43
  - 工程内命令:`npx agentia g tool fetch-weather` / `npx agentia doctor` / `npx agentia --version`;
44
44
  想全局装:`npm i -g @migor/cli`。
45
45
 
46
- > **版本**:`0.10.4`(`@migor/agentia` 与 `@migor/cli` 均已发布到 npm)。
46
+ > **版本**:`0.10.6`(`@migor/agentia` 与 `@migor/cli` 均已发布到 npm)。
47
47
 
48
48
  ## 快速开始
49
49