@migor/agentia 0.9.4 → 0.10.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 (211) hide show
  1. package/CHANGELOG.md +1280 -4
  2. package/README.en.md +61 -3
  3. package/README.md +66 -4
  4. package/dist/AGENTS.md +330 -45
  5. package/dist/container/container.js +21 -0
  6. package/dist/container/container.js.map +1 -1
  7. package/dist/core/limits.d.ts +101 -14
  8. package/dist/core/limits.js +103 -13
  9. package/dist/core/limits.js.map +1 -1
  10. package/dist/core/line-framing.d.ts +36 -0
  11. package/dist/core/line-framing.js +39 -0
  12. package/dist/core/line-framing.js.map +1 -0
  13. package/dist/core/message.d.ts +11 -0
  14. package/dist/core/run.d.ts +68 -4
  15. package/dist/core/run.js +27 -1
  16. package/dist/core/run.js.map +1 -1
  17. package/dist/core/schema.js +59 -8
  18. package/dist/core/schema.js.map +1 -1
  19. package/dist/core/timeout.d.ts +43 -1
  20. package/dist/core/timeout.js +76 -3
  21. package/dist/core/timeout.js.map +1 -1
  22. package/dist/core/tool.d.ts +25 -1
  23. package/dist/core/trace.d.ts +40 -1
  24. package/dist/core/trace.js +26 -0
  25. package/dist/core/trace.js.map +1 -1
  26. package/dist/engine/budget.js +14 -0
  27. package/dist/engine/budget.js.map +1 -1
  28. package/dist/engine/defer.d.ts +49 -0
  29. package/dist/engine/defer.js +72 -0
  30. package/dist/engine/defer.js.map +1 -0
  31. package/dist/engine/errors.d.ts +23 -1
  32. package/dist/engine/errors.js +24 -0
  33. package/dist/engine/errors.js.map +1 -1
  34. package/dist/engine/forwarded.d.ts +14 -5
  35. package/dist/engine/forwarded.js +4 -1
  36. package/dist/engine/forwarded.js.map +1 -1
  37. package/dist/engine/loop-result.d.ts +65 -7
  38. package/dist/engine/loop-result.js +24 -5
  39. package/dist/engine/loop-result.js.map +1 -1
  40. package/dist/engine/loop.d.ts +2 -0
  41. package/dist/engine/loop.js +157 -17
  42. package/dist/engine/loop.js.map +1 -1
  43. package/dist/engine/mcp-server-http.d.ts +2 -0
  44. package/dist/engine/mcp-server-http.js +215 -0
  45. package/dist/engine/mcp-server-http.js.map +1 -0
  46. package/dist/engine/mcp-server-stdio.d.ts +15 -0
  47. package/dist/engine/mcp-server-stdio.js +106 -0
  48. package/dist/engine/mcp-server-stdio.js.map +1 -0
  49. package/dist/engine/mcp-server.d.ts +134 -0
  50. package/dist/engine/mcp-server.js +232 -0
  51. package/dist/engine/mcp-server.js.map +1 -0
  52. package/dist/engine/menu-drift.d.ts +34 -0
  53. package/dist/engine/menu-drift.js +81 -0
  54. package/dist/engine/menu-drift.js.map +1 -0
  55. package/dist/engine/policy.d.ts +2 -1
  56. package/dist/engine/policy.js +7 -1
  57. package/dist/engine/policy.js.map +1 -1
  58. package/dist/engine/replay.d.ts +2 -1
  59. package/dist/engine/replay.js +3 -2
  60. package/dist/engine/replay.js.map +1 -1
  61. package/dist/engine/resume-input.d.ts +10 -0
  62. package/dist/engine/resume-input.js +11 -0
  63. package/dist/engine/resume-input.js.map +1 -1
  64. package/dist/engine/run-config.d.ts +26 -2
  65. package/dist/engine/run-config.js +60 -0
  66. package/dist/engine/run-config.js.map +1 -1
  67. package/dist/engine/spec.d.ts +33 -4
  68. package/dist/engine/spec.js.map +1 -1
  69. package/dist/engine/tool-context.d.ts +10 -5
  70. package/dist/engine/tool-context.js +5 -1
  71. package/dist/engine/tool-context.js.map +1 -1
  72. package/dist/engine/tracer.d.ts +43 -5
  73. package/dist/engine/tracer.js +73 -10
  74. package/dist/engine/tracer.js.map +1 -1
  75. package/dist/engine/trimming.js +28 -2
  76. package/dist/engine/trimming.js.map +1 -1
  77. package/dist/engine/turn.d.ts +60 -3
  78. package/dist/engine/turn.js +180 -71
  79. package/dist/engine/turn.js.map +1 -1
  80. package/dist/engine/types.d.ts +89 -3
  81. package/dist/engine/types.js +38 -2
  82. package/dist/engine/types.js.map +1 -1
  83. package/dist/eval/export.d.ts +48 -0
  84. package/dist/eval/export.js +146 -0
  85. package/dist/eval/export.js.map +1 -0
  86. package/dist/eval/harvest.js +7 -2
  87. package/dist/eval/harvest.js.map +1 -1
  88. package/dist/index.d.ts +8 -5
  89. package/dist/index.js +6 -1
  90. package/dist/index.js.map +1 -1
  91. package/dist/integrations/adapter-options.d.ts +22 -0
  92. package/dist/integrations/adapter-options.js +51 -0
  93. package/dist/integrations/adapter-options.js.map +1 -1
  94. package/dist/integrations/anthropic.d.ts +4 -2
  95. package/dist/integrations/anthropic.js +27 -27
  96. package/dist/integrations/anthropic.js.map +1 -1
  97. package/dist/integrations/file-sink.d.ts +20 -0
  98. package/dist/integrations/file-sink.js +27 -0
  99. package/dist/integrations/file-sink.js.map +1 -0
  100. package/dist/integrations/mcp-http.d.ts +3 -3
  101. package/dist/integrations/mcp-http.js +1 -1
  102. package/dist/integrations/mcp-http.js.map +1 -1
  103. package/dist/integrations/mcp-protocol.d.ts +115 -0
  104. package/dist/integrations/mcp-protocol.js +69 -0
  105. package/dist/integrations/mcp-protocol.js.map +1 -0
  106. package/dist/integrations/mcp-stdio.d.ts +3 -3
  107. package/dist/integrations/mcp-stdio.js +17 -3
  108. package/dist/integrations/mcp-stdio.js.map +1 -1
  109. package/dist/integrations/mcp.d.ts +6 -95
  110. package/dist/integrations/mcp.js +24 -66
  111. package/dist/integrations/mcp.js.map +1 -1
  112. package/dist/integrations/metrics-otlp.js +52 -7
  113. package/dist/integrations/metrics-otlp.js.map +1 -1
  114. package/dist/integrations/metrics-render.d.ts +15 -0
  115. package/dist/integrations/metrics-render.js +79 -2
  116. package/dist/integrations/metrics-render.js.map +1 -1
  117. package/dist/integrations/metrics-state.d.ts +99 -1
  118. package/dist/integrations/metrics-state.js +128 -4
  119. package/dist/integrations/metrics-state.js.map +1 -1
  120. package/dist/integrations/metrics.d.ts +62 -6
  121. package/dist/integrations/metrics.js +49 -8
  122. package/dist/integrations/metrics.js.map +1 -1
  123. package/dist/integrations/openai.js +9 -31
  124. package/dist/integrations/openai.js.map +1 -1
  125. package/dist/integrations/otlp.d.ts +3 -3
  126. package/dist/integrations/report.d.ts +7 -0
  127. package/dist/integrations/report.js +20 -1
  128. package/dist/integrations/report.js.map +1 -1
  129. package/dist/runtime/memory.d.ts +61 -1
  130. package/dist/runtime/memory.js +47 -7
  131. package/dist/runtime/memory.js.map +1 -1
  132. package/dist/runtime/run.d.ts +12 -2
  133. package/dist/runtime/run.js +126 -17
  134. package/dist/runtime/run.js.map +1 -1
  135. package/dist/store/fsStore.js +10 -4
  136. package/dist/store/fsStore.js.map +1 -1
  137. package/dist/store/record.d.ts +41 -0
  138. package/dist/store/record.js +54 -0
  139. package/dist/store/record.js.map +1 -0
  140. package/dist/store/redisStore.d.ts +24 -1
  141. package/dist/store/redisStore.js +42 -7
  142. package/dist/store/redisStore.js.map +1 -1
  143. package/dist/store/sqliteStore.d.ts +23 -0
  144. package/dist/store/sqliteStore.js +103 -8
  145. package/dist/store/sqliteStore.js.map +1 -1
  146. package/dist/store/store.d.ts +65 -2
  147. package/dist/store/store.js +5 -2
  148. package/dist/store/store.js.map +1 -1
  149. package/dist/toolkit/capability-cycles.d.ts +50 -0
  150. package/dist/toolkit/capability-cycles.js +88 -0
  151. package/dist/toolkit/capability-cycles.js.map +1 -0
  152. package/dist/toolkit/capability-slice.d.ts +86 -0
  153. package/dist/toolkit/capability-slice.js +98 -0
  154. package/dist/toolkit/capability-slice.js.map +1 -0
  155. package/dist/toolkit/module.d.ts +7 -1
  156. package/dist/toolkit/module.js +65 -32
  157. package/dist/toolkit/module.js.map +1 -1
  158. package/dist/transport/approval-policy.d.ts +1 -1
  159. package/dist/transport/approval-policy.js +8 -4
  160. package/dist/transport/approval-policy.js.map +1 -1
  161. package/dist/transport/approval-supervisor.d.ts +73 -0
  162. package/dist/transport/approval-supervisor.js +252 -0
  163. package/dist/transport/approval-supervisor.js.map +1 -0
  164. package/dist/transport/async.d.ts +159 -56
  165. package/dist/transport/async.js +627 -269
  166. package/dist/transport/async.js.map +1 -1
  167. package/dist/transport/drain-gate.d.ts +6 -2
  168. package/dist/transport/drain-gate.js +14 -2
  169. package/dist/transport/drain-gate.js.map +1 -1
  170. package/dist/transport/http-endpoints.d.ts +151 -0
  171. package/dist/transport/http-endpoints.js +480 -0
  172. package/dist/transport/http-endpoints.js.map +1 -0
  173. package/dist/transport/http-io.d.ts +66 -0
  174. package/dist/transport/http-io.js +145 -0
  175. package/dist/transport/http-io.js.map +1 -0
  176. package/dist/transport/http-route.d.ts +10 -3
  177. package/dist/transport/http-route.js +18 -3
  178. package/dist/transport/http-route.js.map +1 -1
  179. package/dist/transport/http-shapes.d.ts +11 -0
  180. package/dist/transport/http-shapes.js +31 -1
  181. package/dist/transport/http-shapes.js.map +1 -1
  182. package/dist/transport/http.d.ts +22 -35
  183. package/dist/transport/http.js +98 -476
  184. package/dist/transport/http.js.map +1 -1
  185. package/dist/transport/owner-id.d.ts +29 -0
  186. package/dist/transport/owner-id.js +46 -0
  187. package/dist/transport/owner-id.js.map +1 -0
  188. package/dist/transport/owner-liveness.d.ts +8 -0
  189. package/dist/transport/owner-liveness.js +56 -0
  190. package/dist/transport/owner-liveness.js.map +1 -0
  191. package/dist/transport/resume-policy.d.ts +48 -10
  192. package/dist/transport/resume-policy.js +25 -10
  193. package/dist/transport/resume-policy.js.map +1 -1
  194. package/dist/transport/resume-scanner.d.ts +55 -0
  195. package/dist/transport/resume-scanner.js +219 -0
  196. package/dist/transport/resume-scanner.js.map +1 -0
  197. package/dist/transport/scheduler.js +9 -23
  198. package/dist/transport/scheduler.js.map +1 -1
  199. package/dist/transport/signal-supervisor.d.ts +80 -0
  200. package/dist/transport/signal-supervisor.js +129 -0
  201. package/dist/transport/signal-supervisor.js.map +1 -0
  202. package/dist/transport/slot-pool.d.ts +9 -1
  203. package/dist/transport/slot-pool.js +9 -1
  204. package/dist/transport/slot-pool.js.map +1 -1
  205. package/dist/transport/task-events.d.ts +34 -3
  206. package/dist/transport/task-events.js +93 -5
  207. package/dist/transport/task-events.js.map +1 -1
  208. package/dist/transport/wake-policy.d.ts +50 -0
  209. package/dist/transport/wake-policy.js +43 -0
  210. package/dist/transport/wake-policy.js.map +1 -0
  211. package/package.json +14 -4
@@ -86,6 +86,27 @@ export class Container {
86
86
  finally {
87
87
  this.resolving.pop();
88
88
  }
89
+ // Promise 检测(外部深评 K4;判据 2026-09-29 收紧 —— 外部深评 D1):容器是**同步**解析的 ——
90
+ // `async` 工厂返回的 Promise 会原样被当成「值」缓存,下游注入到的是 Promise 本身:
91
+ // 首次属性访问全 `undefined`、零报错、`tsc` 也看不出来(类型断言成 T 了)。这类
92
+ // 「静默给错东西」必须在解析期响亮失败。
93
+ //
94
+ // ⚠️ 判据是 `instanceof Promise`,**不是**「有 `then`」(或「有 `then` + `catch`」):后者会
95
+ // 误伤一大类**同步**对象 —— knex / mongoose 的 query builder、各类 chainable 为实现
96
+ // 「可被 await」都带 `then`,而 knex / mongoose 连 `catch` 都有(2026-09-29 查两家源码实证),
97
+ // 它们是完全合法的注入值。「忘了 await」的产物**恒为原生 Promise**(async 函数语义),
98
+ // 故 `instanceof` 精确命中本意、不误伤 builder。
99
+ // 如实边界:**跨 realm** 的 Promise(`vm` / 其他上下文产物)`instanceof` 会漏 —— 装配期
100
+ // 几乎不出现,真要注入本体请走下面的逃逸口。
101
+ //
102
+ // ⚠️ 检测器**就地写**、不从别处 import:`container` 是纯叶子(`layering.test.ts` 里
103
+ // `container: []`,不许引内部任何模块),为几行逻辑破叶子边界不值得。
104
+ // 逃逸口:确实要注入 Promise **本体**,包一层即可(`useValue: { promise }`)。
105
+ if (value instanceof Promise) {
106
+ throw new TypeError(`provider "${token}" 解析出的是 Promise:容器同步解析,它会被原样缓存成「值」,` +
107
+ '下游拿到 Promise 而非 await 过的结果(首次属性访问全 undefined、且零报错)。' +
108
+ '异步初始化请在装配前 await 好再用 useValue 注入;确实要注入 Promise 本体,包一层:useValue: { promise }');
109
+ }
89
110
  this.cache.set(token, value);
90
111
  return value;
91
112
  }
@@ -1 +1 @@
1
- {"version":3,"file":"container.js","sourceRoot":"","sources":["../../src/container/container.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"container.js","sourceRoot":"","sources":["../../src/container/container.ts"],"names":[],"mappings":"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"}
@@ -59,7 +59,14 @@ export type BadValuePolicy =
59
59
  /** 一律视为「不限」(旧行为),不报错 */
60
60
  | 'coerced-to-unlimited'
61
61
  /** 无校验(该旋钮只做算术,坏值无处可藏) */
62
- | 'none';
62
+ | 'none'
63
+ /**
64
+ * 只拒 `NaN`(2026-09-29 盘点 C5b):`NaN` 是唯一让「实际值 > 上限」恒假、**闸静默失效**的
65
+ * 坏值(方向与「设了个上限」相反);`0` / 负数 / `±Infinity` 都还有可辩护的读法(全停 / 不限),
66
+ * 照旧放行。需要独立一类,是因为现有三类都表达不了它 —— `throws` 会把负数的合法读法一起拒掉,
67
+ * `coerced-to-unlimited` / `none` 又不会拒 `NaN`。
68
+ */
69
+ | 'rejects-nan';
63
70
  export interface LimitSemantic {
64
71
  /** 稳定标识(用例按它建表;改名 = 用例红,故意的) */
65
72
  readonly knob: string;
@@ -86,7 +93,7 @@ export declare const LIMIT_SEMANTICS: readonly [{
86
93
  readonly zero: 'unlimited';
87
94
  readonly zeroClause: '0 = 不限';
88
95
  readonly badValue: 'throws';
89
- readonly note: 'NaN/Infinity 会被 setTimeout 钳到 1ms ⇒ 每个任务立即「超时」失败,且 NaN 会绕过 `< 0` 静默通过;所以要「不限」必须显式传 0。';
96
+ readonly note: 'NaN/Infinity 会被 setTimeout 钳到 1ms ⇒ 每个任务立即「超时」失败,且 NaN 会绕过 `< 0` 静默通过;所以要「不限」必须显式传 0。⚠️ 2026-09-28 补上界:**超过 2^31-1ms(约 24.86 天)同样会被钳到 1ms**,构造期一并拒掉 —— 此前只查「非负有限」,于是「配 30 天超时 = 每个任务立即超时」这种配置合法、行为相反的坑能静默上线(上限单源在 core/timeout.ts)。';
90
97
  }, {
91
98
  readonly knob: 'AsyncRunner.approvalTimeoutMs';
92
99
  readonly where: 'transport/async.ts';
@@ -95,6 +102,14 @@ export declare const LIMIT_SEMANTICS: readonly [{
95
102
  readonly zeroClause: '0 = 不限';
96
103
  readonly badValue: 'throws';
97
104
  readonly note: '同 runTimeoutMs:非有限数会让「已挂起多久」的比较静默失效或立即超时。';
105
+ }, {
106
+ readonly knob: 'AsyncRunner.maxQueued';
107
+ readonly where: 'transport/async.ts';
108
+ readonly unit: 'count';
109
+ readonly zero: 'unlimited';
110
+ readonly zeroClause: '0 = 不限(不设排队上限)';
111
+ readonly badValue: 'throws';
112
+ readonly note: '2026-09-28 外部深评 T5 补登的**新旋钮**,也刻意是「数量 + 0 = 不限」的**第二个**样本(第一个是 `mapWithConcurrency.limit`)。⚠️ 它必须登记的理由不是形式:`maxQueued: 0` 的直觉读法是「一条都不许排」(= `disabled`),而**那样读的后果正是本旋钮要防的反面** —— 并发一满,每个提交都 503。所以 0 = 不限是**刻意选的**(缺省行为与加本旋钮之前逐字一致),探针必须把这两种读法分开:`concurrency: 1` + `maxQueued: 0` 下灌 3 条,断言**都受理**且都跑完(若读成 disabled,第 2 条就抛)。';
98
113
  }, {
99
114
  readonly knob: 'AsyncRunner.drain.timeoutMs';
100
115
  readonly where: 'transport/drain-gate.ts';
@@ -102,7 +117,7 @@ export declare const LIMIT_SEMANTICS: readonly [{
102
117
  readonly zero: 'unlimited';
103
118
  readonly zeroClause: '非正 = 一直等';
104
119
  readonly badValue: 'none';
105
- readonly note: '**历史事故现场**:`http.ts` 的优雅停机把剩余预算算成 `timeoutMs - elapsed` 再交给它,deadline 已过时算出 1 或 0 ⇒ 旧读法(`0` = 已到点)让它跨过 deadline 后**永不返回**。现在 `http.ts` 自己先判「已到点就认账」,不把 0 交给下游去猜。';
120
+ readonly note: '**历史事故现场**:`http.ts` 的优雅停机把剩余预算算成 `timeoutMs - elapsed` 再交给它,deadline 已过时算出 1 或 0 ⇒ 旧读法(`0` = 已到点)让它跨过 deadline 后**永不返回**。现在 `http.ts` 自己先判「已到点就认账」,不把 0 交给下游去猜。⚠️ 2026-09-28:`NaN` 与超过 2^31-1ms 的预算在闸口拒掉(超上限会被钳到 1ms ⇒「等 30 天排空」变成「1ms 后返回 false」,而 `false` 的语义是「宿主必须退出」,静默拿到它代价很高)。';
106
121
  }, {
107
122
  readonly knob: 'mapWithConcurrency.limit';
108
123
  readonly where: 'engine/concurrency.ts';
@@ -149,8 +164,8 @@ export declare const LIMIT_SEMANTICS: readonly [{
149
164
  readonly unit: 'ms';
150
165
  readonly zero: 'disabled';
151
166
  readonly zeroClause: '非正 = 不设超时(原样透传)';
152
- readonly badValue: 'none';
153
- readonly note: '`toolTimeoutMs` 的底座。⚠️ 漏透传这个值不是「少一层保险」而是**反的**:`withTimeout(p, 0)` 直接返回原 promise = 永不超时,同时 MCP 桥找不到引擎预算又起自己的 60s 兜底 ⇒ 双计时器、双账本(`toolkit/subagent.ts` / `skill.ts` 的透传注释指的就是这条)。';
167
+ readonly badValue: 'throws';
168
+ readonly note: '`toolTimeoutMs` 的底座。⚠️ 漏透传这个值不是「少一层保险」而是**反的**:`withTimeout(p, 0)` 直接返回原 promise = 永不超时,同时 MCP 桥找不到引擎预算又起自己的 60s 兜底 ⇒ 双计时器、双账本(`toolkit/subagent.ts` / `skill.ts` 的透传注释指的就是这条)。⚠️ 2026-09-28 起 `NaN` 与超过 2^31-1ms 的值在入口拒掉(`assertTimerDelay`):`NaN` 会**静默**落进上面那句「非正 = 不设超时」(判据是 `!(x > 0)`,NaN 满足),与「我设了个预算」的预期正好相反;超上限则被钳到 1ms ⇒ 每个工具调用立即超时。`0` / 负数仍是合法的「不设超时」。';
154
169
  }, {
155
170
  readonly knob: 'TaskEventStreams.retainTerminal';
156
171
  readonly where: 'transport/task-events.ts';
@@ -159,14 +174,22 @@ export declare const LIMIT_SEMANTICS: readonly [{
159
174
  readonly zeroClause: '0 = 不留终态流(终态即忘)';
160
175
  readonly badValue: 'throws';
161
176
  readonly note: '判定是 `excess <= 0 提前返回` ⇒ NaN 恒假、**全部**无订阅者的终态流被清空(保留机制静默失效,方向与 maxEvents 的「闸失效」相反、同族)。⚠️ 只跳过「还有订阅者」的终态流 —— 那是还没被读完的流,丢了下游会莫名断在半路。';
177
+ }, {
178
+ readonly knob: 'TaskEventStreams.nonTerminalBuffers';
179
+ readonly where: 'transport/task-events.ts';
180
+ readonly unit: 'count';
181
+ readonly zero: 'disabled';
182
+ readonly zeroClause: '0 = 不为没人读的非终态流保留回放缓冲(有订阅者的照旧留着)';
183
+ readonly badValue: 'throws';
184
+ readonly note: '判定是 `buffered <= quota 提前返回` ⇒ NaN 恒假 ⇒ **每条**非终态流的回放缓冲都被立刻回收(回放历史静默全没,与 retainTerminal 那条「保留机制失效」同族、方向相反)。⚠️ 回收的是**缓冲**不是**表项**:`has()` 仍为真、序号接着走,缺口由 `firstAvailable` 明示(读者先收一帧 `stream.truncated`)—— 连表项一起删会让恢复段的序号从 1 重来,拿 `Last-Event-ID` 续订的读者从此静默收不到东西。⚠️ 只跳过「还有订阅者」的流(正在被读的不抽走)⇒ 被实时读的流不受配额约束。';
162
185
  }, {
163
186
  readonly knob: 'interruptibleSleep.ms';
164
187
  readonly where: 'core/timeout.ts';
165
188
  readonly unit: 'ms';
166
189
  readonly zero: 'immediate';
167
190
  readonly zeroClause: '非正 = 不睡';
168
- readonly badValue: 'none';
169
- readonly note: '⚠️ 这一判**先于** aborted 检查 —— 别把它「修」成「已中止就该 reject」。2026-09-19 外部复核把它当一致性缺口改反过一次,被 `tests/core/timeout.test.ts` + `tests/core/sse-text-stats.test.ts` 拦住。';
191
+ readonly badValue: 'throws';
192
+ readonly note: '⚠️ 这一判**先于** aborted 检查 —— 别把它「修」成「已中止就该 reject」。2026-09-19 外部复核把它当一致性缺口改反过一次,被 `tests/core/timeout.test.ts` + `tests/core/sse-text-stats.test.ts` 拦住。⚠️ 2026-09-28 起 `NaN` 与超 2^31-1ms 在入口拒掉(`assertTimerDelay`,判在「非正」**之前** —— `NaN <= 0` 是 false,不先挡就会穿透直落 `setTimeout(NaN)`,被钳成 1ms ⇒「退避」变成「立刻重试」)。上游给的荒谬 `Retry-After` 不走这条:它在 `backoffMs` 里被**夹**到上限(外部数据不该把一次 429 升级成硬失败)。';
170
193
  }, {
171
194
  readonly knob: 'metricsSink.intervalMs';
172
195
  readonly where: 'integrations/metrics.ts';
@@ -175,12 +198,52 @@ export declare const LIMIT_SEMANTICS: readonly [{
175
198
  readonly zeroClause: '0 = 立即导出';
176
199
  readonly badValue: 'none';
177
200
  readonly note: '⚠️ **同名反义**:`Scheduler.every` 的 `intervalMs: 0` 是**配置错误**(抛错),这里却是「关掉定时器、每次累加后立即导出」。两个同名旋钮两种读法 —— 改任何一个之前先看这张表。';
201
+ }, {
202
+ readonly knob: 'BudgetGuardOptions.maxTotalTokens';
203
+ readonly where: 'engine/budget.ts(createBudgetGuard;由 RunAgentOptions.maxTotalTokens 传进来)';
204
+ readonly unit: 'count';
205
+ readonly zero: 'immediate';
206
+ readonly zeroClause: '0 = 第一次记账就超预算(等价于「任何用量都不许」)';
207
+ readonly badValue: 'rejects-nan';
208
+ readonly note: '闸门判据是 `totalTokens > maxTotalTokens`(**严格大于**):0 时任何非零用量立刻撞线;负数同理(`> -1` 恒真)。对 `0` / 负数**刻意不抛错** —— 预算是运行期累计量,配成 0 是「这次 run 一条都别花」的合法意图(与 `maxIterations: 0` 同档),不是坏配置。⚠️ **但 `NaN` 必须构造期拒**(2026-09-29 盘点 C5b):`totalTokens > NaN` **恒 false** ⇒ 闸永不触发、**静默失效**,方向与「设了个上限」正好相反;`createBudgetGuard` 对 `NaN` 抛 `RangeError`(`0` / 负数 / `±Infinity` 照旧)。⚠️ 与 `maxCostUsd` 是两个独立闸,**tokens 先判、撞了就返回**(不再看 cost)。⚠️ 2026-09-28 补登:这两个旋钮自 v0.5.0 起就存在,却一直没进这张表(「改 0 的读法不改表就构建红」这条纪律对它们曾是空的)。';
209
+ }, {
210
+ readonly knob: 'BudgetGuardOptions.maxCostUsd';
211
+ readonly where: 'engine/budget.ts(createBudgetGuard;由 RunAgentOptions.maxCostUsd 传进来)';
212
+ readonly unit: 'n/a';
213
+ readonly zero: 'immediate';
214
+ readonly zeroClause: '0 = 第一笔成本就超预算(等价于「一分钱都不许花」)';
215
+ readonly badValue: 'rejects-nan';
216
+ readonly note: '判据 `costUsd > maxCostUsd`,而 `costUsd` 取 `usage.costEstimate ?? 0` ⇒ **模型不在价格表内时它恒为 0**,这个闸对未定价模型**永不触发**(静默失效;要无条件兜底请用 `maxTotalTokens`)。这一句在 `BudgetGuardOptions` 的注释里已如实写着,这里登记是为了让「0 的读法」也有单一真源。⚠️ 同 `maxTotalTokens`:`NaN`(2026-09-29 盘点 C5b)会让 `costUsd > NaN` 恒 false ⇒ 闸静默失效,`createBudgetGuard` 对 `NaN` 构造期抛 `RangeError`。';
217
+ }, {
218
+ readonly knob: 'metricsSink.maxCapabilities';
219
+ readonly where: 'integrations/metrics.ts(metricsSink)';
220
+ readonly unit: 'count';
221
+ readonly zero: 'invalid';
222
+ readonly zeroClause: '0 = 一个能力都认不下(配置错误)';
223
+ readonly badValue: 'throws';
224
+ readonly note: '与 `maxModels` / `maxScores` / `maxLabelValues` / `maxLabelCombos` 同族:都是**基数封顶**,0 会让所有条目折进同一个桶 ⇒ 看板看起来「只剩一个能力」,而没有任何报错。2026-09-28 补登(这三个此前不在表里 —— 与 `BudgetGuardOptions` 那两个同一种漏登记)。';
225
+ }, {
226
+ readonly knob: 'metricsSink.maxModels';
227
+ readonly where: 'integrations/metrics.ts(metricsSink)';
228
+ readonly unit: 'count';
229
+ readonly zero: 'invalid';
230
+ readonly zeroClause: '0 = 一个模型都认不下(配置错误)';
231
+ readonly badValue: 'throws';
232
+ readonly note: '同 `maxCapabilities`(该组同款判据、同款文案)。';
233
+ }, {
234
+ readonly knob: 'metricsSink.maxScores';
235
+ readonly where: 'integrations/metrics.ts(metricsSink)';
236
+ readonly unit: 'count';
237
+ readonly zero: 'invalid';
238
+ readonly zeroClause: '0 = 一个分数都认不下(配置错误)';
239
+ readonly badValue: 'throws';
240
+ readonly note: '同 `maxCapabilities`。';
178
241
  }, {
179
242
  readonly knob: 'AsyncRunner.concurrency';
180
243
  readonly where: 'transport/async.ts';
181
244
  readonly unit: 'count';
182
245
  readonly zero: 'invalid';
183
- readonly zeroClause: '必须为正数(0 = 没有 worker)';
246
+ readonly zeroClause: '0 = 没有 worker';
184
247
  readonly badValue: 'throws';
185
248
  readonly note: '0 个 worker 的池子会让每个任务永远排在队列里(既不跑也不失败)。';
186
249
  }, {
@@ -188,7 +251,7 @@ export declare const LIMIT_SEMANTICS: readonly [{
188
251
  readonly where: 'transport/scheduler.ts';
189
252
  readonly unit: 'count';
190
253
  readonly zero: 'invalid';
191
- readonly zeroClause: '必须为正数(Infinity = 关闸门;0 = 没有在飞名额,周期任务永不派发)';
254
+ readonly zeroClause: 'Infinity = 关闸门;0 = 没有在飞名额,周期任务永不派发';
192
255
  readonly badValue: 'throws';
193
256
  readonly note: '与 `AsyncRunner.concurrency` **同族**(「在飞名额」数量旋钮):闸门判据是 `inFlight.size >= maxInFlight`,0 时恒真 ⇒ 每次 tick 都跳过,周期任务**既不跑也不失败**(探针实测 60ms 内派发 0 次、无任何报错/日志;负数同理)。⚠️ 旧实现 `opts.maxInFlight ?? 1` 把 0 静默留下,而写成 `||` 又会把它静默抬成 1 —— **两个方向都在替使用者改配置**(与 `streamBufferEvents` 记的旧 `Math.max(1, …)` 是同一个反模式)。';
194
257
  }, {
@@ -196,15 +259,23 @@ export declare const LIMIT_SEMANTICS: readonly [{
196
259
  readonly where: 'transport/http.ts(createHttpHandler)';
197
260
  readonly unit: 'bytes';
198
261
  readonly zero: 'invalid';
199
- readonly zeroClause: '必须为正数(0 = 每个带 body 的请求都 413)';
262
+ readonly zeroClause: '0 = 每个带 body 的请求都 413';
200
263
  readonly badValue: 'throws';
201
264
  readonly note: "与**同一个选项接口里**的 `maxConcurrentRuns` 同款 —— 那条写着「0 / 负数则全部 503 —— 都是配置错误,宁可在构造期响亮失败」,这条是同一形状:`readBody` 的判据是 `size > maxBytes`,0 时**任何字节**都超限 ⇒ `POST /run` / `POST /tasks` / 审批全部 413(实测 3 条 POST 全 413),服务器看着在跑却收不了任何输入。⚠️ 还挡一类事故:`Number('') === 0` —— 从**空的环境变量**读出来的配置会静默变成「拒绝一切」。要「不限」用 `Infinity`(`size > Infinity` 恒假),**别用 0**。";
265
+ }, {
266
+ readonly knob: 'HttpHandlerOptions.maxConcurrentRuns';
267
+ readonly where: 'transport/http.ts(createHttpHandler)';
268
+ readonly unit: 'count';
269
+ readonly zero: 'invalid';
270
+ readonly zeroClause: 'Infinity = 无上限(0 = 全部 503)';
271
+ readonly badValue: 'throws';
272
+ readonly note: '闸门判据是 `inFlightRuns >= maxConcurrentRuns`:0 / 负数时恒真 ⇒ 每个 `POST /run` 都 503(服务器看着在跑却一个 run 都不收);NaN 让它恒假 ⇒ 闸门静默失效。它是**存量旋钮**(校验早就有),2026-09-27 复核时发现始终没登记进这张表(文案也没走 `zeroClauseOf`),补登。';
202
273
  }, {
203
274
  readonly knob: 'SseWriterOptions.maxBufferedBytes';
204
275
  readonly where: 'transport/sse.ts(sseWriter)+ transport/http.ts(createHttpHandler 的 sseMaxBufferedBytes 透传)';
205
276
  readonly unit: 'bytes';
206
277
  readonly zero: 'invalid';
207
- readonly zeroClause: '必须为正数(0 = 流活不过一帧;Infinity = 不限)';
278
+ readonly zeroClause: '0 = 流活不过一帧';
208
279
  readonly badValue: 'throws';
209
280
  readonly note: '判据是**写入前**的 `pending > limitBytes` ⇒ 0 时第 1 帧照写(pending 还是 0),**第 2 帧必收口**(实测:`closed=true` / `ended=true` / `onBackpressure` 回调 1 次)—— 流活不过一帧,没有任何正当用法:这个旋钮的定位是「只拦连上但不读的病态消费者」,而 0 会让**每个**消费者都被判成病态。要「关掉」用 `Infinity`。';
210
281
  }, {
@@ -222,7 +293,7 @@ export declare const LIMIT_SEMANTICS: readonly [{
222
293
  readonly zero: 'invalid';
223
294
  readonly zeroClause: '必须为正有限数(0 会空转)';
224
295
  readonly badValue: 'throws';
225
- readonly note: '`setInterval(0)` 退化成「尽快重复」的空转循环(Node 钳到 1ms,仍是每毫秒一轮忙轮询);超过 2^31-1ms 会被**静默**钳到 1ms,同款失败。';
296
+ readonly note: '`setInterval(0)` 退化成「尽快重复」的空转循环(Node 钳到 1ms,仍是每毫秒一轮忙轮询);超过 2^31-1ms 的延迟**不会被遵守**(Node 只在 stderr 留一行 `TimeoutOverflowWarning`,随后钳到 1ms),同款失败。上限单源 2026-09-28 上提到 core/timeout.ts,与其余五处直喂 `setTimeout` 的站点共用。';
226
297
  }, {
227
298
  readonly knob: 'createAnthropicClient.timeout';
228
299
  readonly where: 'integrations/anthropic.ts';
@@ -230,15 +301,31 @@ export declare const LIMIT_SEMANTICS: readonly [{
230
301
  readonly zero: 'invalid';
231
302
  readonly zeroClause: '不设 = 不限;0 = 非法';
232
303
  readonly badValue: 'throws';
233
- readonly note: '「不限」的表达方式是**不传**,不是传 0 —— 与 `runTimeoutMs` 的读法**相反**(那个要求显式传 0)。';
304
+ readonly note: '「不限」的表达方式是**不传**,不是传 0 —— 与 `runTimeoutMs` 的读法**相反**(那个要求显式传 0)。⚠️ 2026-09-28 补上界:超过 2^31-1ms 的值同样被钳到 1ms ⇒「30 天超时」变成「每个请求立即超时」,构造期一并拒掉。';
234
305
  }, {
235
306
  readonly knob: 'metricsSink.windowSize';
236
307
  readonly where: 'integrations/metrics.ts';
237
308
  readonly unit: 'count';
238
309
  readonly zero: 'invalid';
239
- readonly zeroClause: '必须为正数(0 = 窗口里什么都没有)';
310
+ readonly zeroClause: '0 = 窗口里什么都没有';
240
311
  readonly badValue: 'throws';
241
312
  readonly note: '同款的还有 `maxCapabilities` / `maxModels` / `maxScores` —— 都是「必须为正数」。';
313
+ }, {
314
+ readonly knob: 'metricsSink.maxLabelValues';
315
+ readonly where: 'integrations/metrics.ts';
316
+ readonly unit: 'count';
317
+ readonly zero: 'invalid';
318
+ readonly zeroClause: '0 = 一个标签值都认不下';
319
+ readonly badValue: 'throws';
320
+ readonly note: '归因标签(R8-P4)的基数上限,与 `maxCapabilities` / `maxModels` / `maxScores` 同族。0 时**每个**值都折叠进 `__other__` —— 「opt-in 了标签维度却一个值都区分不开」没有正当读法,是配置错误;且这道上限刻意不可关(opt-in 挡不住「明知几千租户偏要上」,内存不变量要求每个新基数维度都有 cap)。';
321
+ }, {
322
+ readonly knob: 'metricsSink.maxLabelCombos';
323
+ readonly where: 'integrations/metrics.ts';
324
+ readonly unit: 'count';
325
+ readonly zero: 'invalid';
326
+ readonly zeroClause: '0 = 一个标签组合都认不下,全折进一个桶';
327
+ readonly badValue: 'throws';
328
+ readonly note: '**组合数**上限(2026-09-27 ⑧ 补的第四条基数上限)。`maxLabelValues` 只封每个键的**值域**,而进内存的是组合,数量是叉乘 `(maxLabelValues+1)^labelKeys.length` —— 缺省 100 × 3 键就是一百万条,且每键的 dropped 计数看不见它。与另外三道同款:超限折进一个全 `__other__` 的桶(量不丢),折叠数见 `droppedLabelCombos` / `dropped_keys{kind="label:combos"}`。';
242
329
  }];
243
330
  /** 表里全部旋钮的标识(用例按它建穷尽表:少一项 `typecheck:tests` 就红) */
244
331
  export type LimitKnob = (typeof LIMIT_SEMANTICS)[number]['knob'];
@@ -48,7 +48,7 @@ export const LIMIT_SEMANTICS = [
48
48
  zero: 'unlimited',
49
49
  zeroClause: '0 = 不限',
50
50
  badValue: 'throws',
51
- note: 'NaN/Infinity 会被 setTimeout 钳到 1ms ⇒ 每个任务立即「超时」失败,且 NaN 会绕过 `< 0` 静默通过;所以要「不限」必须显式传 0。',
51
+ note: 'NaN/Infinity 会被 setTimeout 钳到 1ms ⇒ 每个任务立即「超时」失败,且 NaN 会绕过 `< 0` 静默通过;所以要「不限」必须显式传 0。⚠️ 2026-09-28 补上界:**超过 2^31-1ms(约 24.86 天)同样会被钳到 1ms**,构造期一并拒掉 —— 此前只查「非负有限」,于是「配 30 天超时 = 每个任务立即超时」这种配置合法、行为相反的坑能静默上线(上限单源在 core/timeout.ts)。',
52
52
  },
53
53
  {
54
54
  knob: 'AsyncRunner.approvalTimeoutMs',
@@ -59,6 +59,15 @@ export const LIMIT_SEMANTICS = [
59
59
  badValue: 'throws',
60
60
  note: '同 runTimeoutMs:非有限数会让「已挂起多久」的比较静默失效或立即超时。',
61
61
  },
62
+ {
63
+ knob: 'AsyncRunner.maxQueued',
64
+ where: 'transport/async.ts',
65
+ unit: 'count',
66
+ zero: 'unlimited',
67
+ zeroClause: '0 = 不限(不设排队上限)',
68
+ badValue: 'throws',
69
+ note: '2026-09-28 外部深评 T5 补登的**新旋钮**,也刻意是「数量 + 0 = 不限」的**第二个**样本(第一个是 `mapWithConcurrency.limit`)。⚠️ 它必须登记的理由不是形式:`maxQueued: 0` 的直觉读法是「一条都不许排」(= `disabled`),而**那样读的后果正是本旋钮要防的反面** —— 并发一满,每个提交都 503。所以 0 = 不限是**刻意选的**(缺省行为与加本旋钮之前逐字一致),探针必须把这两种读法分开:`concurrency: 1` + `maxQueued: 0` 下灌 3 条,断言**都受理**且都跑完(若读成 disabled,第 2 条就抛)。',
70
+ },
62
71
  {
63
72
  knob: 'AsyncRunner.drain.timeoutMs',
64
73
  where: 'transport/drain-gate.ts',
@@ -66,7 +75,7 @@ export const LIMIT_SEMANTICS = [
66
75
  zero: 'unlimited',
67
76
  zeroClause: '非正 = 一直等',
68
77
  badValue: 'none',
69
- note: '**历史事故现场**:`http.ts` 的优雅停机把剩余预算算成 `timeoutMs - elapsed` 再交给它,deadline 已过时算出 1 或 0 ⇒ 旧读法(`0` = 已到点)让它跨过 deadline 后**永不返回**。现在 `http.ts` 自己先判「已到点就认账」,不把 0 交给下游去猜。',
78
+ note: '**历史事故现场**:`http.ts` 的优雅停机把剩余预算算成 `timeoutMs - elapsed` 再交给它,deadline 已过时算出 1 或 0 ⇒ 旧读法(`0` = 已到点)让它跨过 deadline 后**永不返回**。现在 `http.ts` 自己先判「已到点就认账」,不把 0 交给下游去猜。⚠️ 2026-09-28:`NaN` 与超过 2^31-1ms 的预算在闸口拒掉(超上限会被钳到 1ms ⇒「等 30 天排空」变成「1ms 后返回 false」,而 `false` 的语义是「宿主必须退出」,静默拿到它代价很高)。',
70
79
  },
71
80
  {
72
81
  knob: 'mapWithConcurrency.limit',
@@ -120,8 +129,8 @@ export const LIMIT_SEMANTICS = [
120
129
  unit: 'ms',
121
130
  zero: 'disabled',
122
131
  zeroClause: '非正 = 不设超时(原样透传)',
123
- badValue: 'none',
124
- note: '`toolTimeoutMs` 的底座。⚠️ 漏透传这个值不是「少一层保险」而是**反的**:`withTimeout(p, 0)` 直接返回原 promise = 永不超时,同时 MCP 桥找不到引擎预算又起自己的 60s 兜底 ⇒ 双计时器、双账本(`toolkit/subagent.ts` / `skill.ts` 的透传注释指的就是这条)。',
132
+ badValue: 'throws',
133
+ note: '`toolTimeoutMs` 的底座。⚠️ 漏透传这个值不是「少一层保险」而是**反的**:`withTimeout(p, 0)` 直接返回原 promise = 永不超时,同时 MCP 桥找不到引擎预算又起自己的 60s 兜底 ⇒ 双计时器、双账本(`toolkit/subagent.ts` / `skill.ts` 的透传注释指的就是这条)。⚠️ 2026-09-28 起 `NaN` 与超过 2^31-1ms 的值在入口拒掉(`assertTimerDelay`):`NaN` 会**静默**落进上面那句「非正 = 不设超时」(判据是 `!(x > 0)`,NaN 满足),与「我设了个预算」的预期正好相反;超上限则被钳到 1ms ⇒ 每个工具调用立即超时。`0` / 负数仍是合法的「不设超时」。',
125
134
  },
126
135
  {
127
136
  knob: 'TaskEventStreams.retainTerminal',
@@ -132,6 +141,15 @@ export const LIMIT_SEMANTICS = [
132
141
  badValue: 'throws',
133
142
  note: '判定是 `excess <= 0 提前返回` ⇒ NaN 恒假、**全部**无订阅者的终态流被清空(保留机制静默失效,方向与 maxEvents 的「闸失效」相反、同族)。⚠️ 只跳过「还有订阅者」的终态流 —— 那是还没被读完的流,丢了下游会莫名断在半路。',
134
143
  },
144
+ {
145
+ knob: 'TaskEventStreams.nonTerminalBuffers',
146
+ where: 'transport/task-events.ts',
147
+ unit: 'count',
148
+ zero: 'disabled',
149
+ zeroClause: '0 = 不为没人读的非终态流保留回放缓冲(有订阅者的照旧留着)',
150
+ badValue: 'throws',
151
+ note: '判定是 `buffered <= quota 提前返回` ⇒ NaN 恒假 ⇒ **每条**非终态流的回放缓冲都被立刻回收(回放历史静默全没,与 retainTerminal 那条「保留机制失效」同族、方向相反)。⚠️ 回收的是**缓冲**不是**表项**:`has()` 仍为真、序号接着走,缺口由 `firstAvailable` 明示(读者先收一帧 `stream.truncated`)—— 连表项一起删会让恢复段的序号从 1 重来,拿 `Last-Event-ID` 续订的读者从此静默收不到东西。⚠️ 只跳过「还有订阅者」的流(正在被读的不抽走)⇒ 被实时读的流不受配额约束。',
152
+ },
135
153
  // ── 0 = 立即执行(等待预算为 0 ⇒ 这次等待不存在)─────────────────────────
136
154
  {
137
155
  knob: 'interruptibleSleep.ms',
@@ -139,8 +157,8 @@ export const LIMIT_SEMANTICS = [
139
157
  unit: 'ms',
140
158
  zero: 'immediate',
141
159
  zeroClause: '非正 = 不睡',
142
- badValue: 'none',
143
- note: '⚠️ 这一判**先于** aborted 检查 —— 别把它「修」成「已中止就该 reject」。2026-09-19 外部复核把它当一致性缺口改反过一次,被 `tests/core/timeout.test.ts` + `tests/core/sse-text-stats.test.ts` 拦住。',
160
+ badValue: 'throws',
161
+ note: '⚠️ 这一判**先于** aborted 检查 —— 别把它「修」成「已中止就该 reject」。2026-09-19 外部复核把它当一致性缺口改反过一次,被 `tests/core/timeout.test.ts` + `tests/core/sse-text-stats.test.ts` 拦住。⚠️ 2026-09-28 起 `NaN` 与超 2^31-1ms 在入口拒掉(`assertTimerDelay`,判在「非正」**之前** —— `NaN <= 0` 是 false,不先挡就会穿透直落 `setTimeout(NaN)`,被钳成 1ms ⇒「退避」变成「立刻重试」)。上游给的荒谬 `Retry-After` 不走这条:它在 `backoffMs` 里被**夹**到上限(外部数据不该把一次 429 升级成硬失败)。',
144
162
  },
145
163
  {
146
164
  knob: 'metricsSink.intervalMs',
@@ -151,13 +169,58 @@ export const LIMIT_SEMANTICS = [
151
169
  badValue: 'none',
152
170
  note: '⚠️ **同名反义**:`Scheduler.every` 的 `intervalMs: 0` 是**配置错误**(抛错),这里却是「关掉定时器、每次累加后立即导出」。两个同名旋钮两种读法 —— 改任何一个之前先看这张表。',
153
171
  },
172
+ {
173
+ knob: 'BudgetGuardOptions.maxTotalTokens',
174
+ where: 'engine/budget.ts(createBudgetGuard;由 RunAgentOptions.maxTotalTokens 传进来)',
175
+ unit: 'count',
176
+ zero: 'immediate',
177
+ zeroClause: '0 = 第一次记账就超预算(等价于「任何用量都不许」)',
178
+ badValue: 'rejects-nan',
179
+ note: '闸门判据是 `totalTokens > maxTotalTokens`(**严格大于**):0 时任何非零用量立刻撞线;负数同理(`> -1` 恒真)。对 `0` / 负数**刻意不抛错** —— 预算是运行期累计量,配成 0 是「这次 run 一条都别花」的合法意图(与 `maxIterations: 0` 同档),不是坏配置。⚠️ **但 `NaN` 必须构造期拒**(2026-09-29 盘点 C5b):`totalTokens > NaN` **恒 false** ⇒ 闸永不触发、**静默失效**,方向与「设了个上限」正好相反;`createBudgetGuard` 对 `NaN` 抛 `RangeError`(`0` / 负数 / `±Infinity` 照旧)。⚠️ 与 `maxCostUsd` 是两个独立闸,**tokens 先判、撞了就返回**(不再看 cost)。⚠️ 2026-09-28 补登:这两个旋钮自 v0.5.0 起就存在,却一直没进这张表(「改 0 的读法不改表就构建红」这条纪律对它们曾是空的)。',
180
+ },
181
+ {
182
+ knob: 'BudgetGuardOptions.maxCostUsd',
183
+ where: 'engine/budget.ts(createBudgetGuard;由 RunAgentOptions.maxCostUsd 传进来)',
184
+ unit: 'n/a',
185
+ zero: 'immediate',
186
+ zeroClause: '0 = 第一笔成本就超预算(等价于「一分钱都不许花」)',
187
+ badValue: 'rejects-nan',
188
+ note: '判据 `costUsd > maxCostUsd`,而 `costUsd` 取 `usage.costEstimate ?? 0` ⇒ **模型不在价格表内时它恒为 0**,这个闸对未定价模型**永不触发**(静默失效;要无条件兜底请用 `maxTotalTokens`)。这一句在 `BudgetGuardOptions` 的注释里已如实写着,这里登记是为了让「0 的读法」也有单一真源。⚠️ 同 `maxTotalTokens`:`NaN`(2026-09-29 盘点 C5b)会让 `costUsd > NaN` 恒 false ⇒ 闸静默失效,`createBudgetGuard` 对 `NaN` 构造期抛 `RangeError`。',
189
+ },
190
+ {
191
+ knob: 'metricsSink.maxCapabilities',
192
+ where: 'integrations/metrics.ts(metricsSink)',
193
+ unit: 'count',
194
+ zero: 'invalid',
195
+ zeroClause: '0 = 一个能力都认不下(配置错误)',
196
+ badValue: 'throws',
197
+ note: '与 `maxModels` / `maxScores` / `maxLabelValues` / `maxLabelCombos` 同族:都是**基数封顶**,0 会让所有条目折进同一个桶 ⇒ 看板看起来「只剩一个能力」,而没有任何报错。2026-09-28 补登(这三个此前不在表里 —— 与 `BudgetGuardOptions` 那两个同一种漏登记)。',
198
+ },
199
+ {
200
+ knob: 'metricsSink.maxModels',
201
+ where: 'integrations/metrics.ts(metricsSink)',
202
+ unit: 'count',
203
+ zero: 'invalid',
204
+ zeroClause: '0 = 一个模型都认不下(配置错误)',
205
+ badValue: 'throws',
206
+ note: '同 `maxCapabilities`(该组同款判据、同款文案)。',
207
+ },
208
+ {
209
+ knob: 'metricsSink.maxScores',
210
+ where: 'integrations/metrics.ts(metricsSink)',
211
+ unit: 'count',
212
+ zero: 'invalid',
213
+ zeroClause: '0 = 一个分数都认不下(配置错误)',
214
+ badValue: 'throws',
215
+ note: '同 `maxCapabilities`。',
216
+ },
154
217
  // ── 0 = 非法配置(响亮失败)─────────────────────────────────────────────
155
218
  {
156
219
  knob: 'AsyncRunner.concurrency',
157
220
  where: 'transport/async.ts',
158
221
  unit: 'count',
159
222
  zero: 'invalid',
160
- zeroClause: '必须为正数(0 = 没有 worker)',
223
+ zeroClause: '0 = 没有 worker',
161
224
  badValue: 'throws',
162
225
  note: '0 个 worker 的池子会让每个任务永远排在队列里(既不跑也不失败)。',
163
226
  },
@@ -166,7 +229,7 @@ export const LIMIT_SEMANTICS = [
166
229
  where: 'transport/scheduler.ts',
167
230
  unit: 'count',
168
231
  zero: 'invalid',
169
- zeroClause: '必须为正数(Infinity = 关闸门;0 = 没有在飞名额,周期任务永不派发)',
232
+ zeroClause: 'Infinity = 关闸门;0 = 没有在飞名额,周期任务永不派发',
170
233
  badValue: 'throws',
171
234
  note: '与 `AsyncRunner.concurrency` **同族**(「在飞名额」数量旋钮):闸门判据是 `inFlight.size >= maxInFlight`,0 时恒真 ⇒ 每次 tick 都跳过,周期任务**既不跑也不失败**(探针实测 60ms 内派发 0 次、无任何报错/日志;负数同理)。⚠️ 旧实现 `opts.maxInFlight ?? 1` 把 0 静默留下,而写成 `||` 又会把它静默抬成 1 —— **两个方向都在替使用者改配置**(与 `streamBufferEvents` 记的旧 `Math.max(1, …)` 是同一个反模式)。',
172
235
  },
@@ -175,16 +238,25 @@ export const LIMIT_SEMANTICS = [
175
238
  where: 'transport/http.ts(createHttpHandler)',
176
239
  unit: 'bytes',
177
240
  zero: 'invalid',
178
- zeroClause: '必须为正数(0 = 每个带 body 的请求都 413)',
241
+ zeroClause: '0 = 每个带 body 的请求都 413',
179
242
  badValue: 'throws',
180
243
  note: "与**同一个选项接口里**的 `maxConcurrentRuns` 同款 —— 那条写着「0 / 负数则全部 503 —— 都是配置错误,宁可在构造期响亮失败」,这条是同一形状:`readBody` 的判据是 `size > maxBytes`,0 时**任何字节**都超限 ⇒ `POST /run` / `POST /tasks` / 审批全部 413(实测 3 条 POST 全 413),服务器看着在跑却收不了任何输入。⚠️ 还挡一类事故:`Number('') === 0` —— 从**空的环境变量**读出来的配置会静默变成「拒绝一切」。要「不限」用 `Infinity`(`size > Infinity` 恒假),**别用 0**。",
181
244
  },
245
+ {
246
+ knob: 'HttpHandlerOptions.maxConcurrentRuns',
247
+ where: 'transport/http.ts(createHttpHandler)',
248
+ unit: 'count',
249
+ zero: 'invalid',
250
+ zeroClause: 'Infinity = 无上限(0 = 全部 503)',
251
+ badValue: 'throws',
252
+ note: '闸门判据是 `inFlightRuns >= maxConcurrentRuns`:0 / 负数时恒真 ⇒ 每个 `POST /run` 都 503(服务器看着在跑却一个 run 都不收);NaN 让它恒假 ⇒ 闸门静默失效。它是**存量旋钮**(校验早就有),2026-09-27 复核时发现始终没登记进这张表(文案也没走 `zeroClauseOf`),补登。',
253
+ },
182
254
  {
183
255
  knob: 'SseWriterOptions.maxBufferedBytes',
184
256
  where: 'transport/sse.ts(sseWriter)+ transport/http.ts(createHttpHandler 的 sseMaxBufferedBytes 透传)',
185
257
  unit: 'bytes',
186
258
  zero: 'invalid',
187
- zeroClause: '必须为正数(0 = 流活不过一帧;Infinity = 不限)',
259
+ zeroClause: '0 = 流活不过一帧',
188
260
  badValue: 'throws',
189
261
  note: '判据是**写入前**的 `pending > limitBytes` ⇒ 0 时第 1 帧照写(pending 还是 0),**第 2 帧必收口**(实测:`closed=true` / `ended=true` / `onBackpressure` 回调 1 次)—— 流活不过一帧,没有任何正当用法:这个旋钮的定位是「只拦连上但不读的病态消费者」,而 0 会让**每个**消费者都被判成病态。要「关掉」用 `Infinity`。',
190
262
  },
@@ -204,7 +276,7 @@ export const LIMIT_SEMANTICS = [
204
276
  zero: 'invalid',
205
277
  zeroClause: '必须为正有限数(0 会空转)',
206
278
  badValue: 'throws',
207
- note: '`setInterval(0)` 退化成「尽快重复」的空转循环(Node 钳到 1ms,仍是每毫秒一轮忙轮询);超过 2^31-1ms 会被**静默**钳到 1ms,同款失败。',
279
+ note: '`setInterval(0)` 退化成「尽快重复」的空转循环(Node 钳到 1ms,仍是每毫秒一轮忙轮询);超过 2^31-1ms 的延迟**不会被遵守**(Node 只在 stderr 留一行 `TimeoutOverflowWarning`,随后钳到 1ms),同款失败。上限单源 2026-09-28 上提到 core/timeout.ts,与其余五处直喂 `setTimeout` 的站点共用。',
208
280
  },
209
281
  {
210
282
  knob: 'createAnthropicClient.timeout',
@@ -213,17 +285,35 @@ export const LIMIT_SEMANTICS = [
213
285
  zero: 'invalid',
214
286
  zeroClause: '不设 = 不限;0 = 非法',
215
287
  badValue: 'throws',
216
- note: '「不限」的表达方式是**不传**,不是传 0 —— 与 `runTimeoutMs` 的读法**相反**(那个要求显式传 0)。',
288
+ note: '「不限」的表达方式是**不传**,不是传 0 —— 与 `runTimeoutMs` 的读法**相反**(那个要求显式传 0)。⚠️ 2026-09-28 补上界:超过 2^31-1ms 的值同样被钳到 1ms ⇒「30 天超时」变成「每个请求立即超时」,构造期一并拒掉。',
217
289
  },
218
290
  {
219
291
  knob: 'metricsSink.windowSize',
220
292
  where: 'integrations/metrics.ts',
221
293
  unit: 'count',
222
294
  zero: 'invalid',
223
- zeroClause: '必须为正数(0 = 窗口里什么都没有)',
295
+ zeroClause: '0 = 窗口里什么都没有',
224
296
  badValue: 'throws',
225
297
  note: '同款的还有 `maxCapabilities` / `maxModels` / `maxScores` —— 都是「必须为正数」。',
226
298
  },
299
+ {
300
+ knob: 'metricsSink.maxLabelValues',
301
+ where: 'integrations/metrics.ts',
302
+ unit: 'count',
303
+ zero: 'invalid',
304
+ zeroClause: '0 = 一个标签值都认不下',
305
+ badValue: 'throws',
306
+ note: '归因标签(R8-P4)的基数上限,与 `maxCapabilities` / `maxModels` / `maxScores` 同族。0 时**每个**值都折叠进 `__other__` —— 「opt-in 了标签维度却一个值都区分不开」没有正当读法,是配置错误;且这道上限刻意不可关(opt-in 挡不住「明知几千租户偏要上」,内存不变量要求每个新基数维度都有 cap)。',
307
+ },
308
+ {
309
+ knob: 'metricsSink.maxLabelCombos',
310
+ where: 'integrations/metrics.ts',
311
+ unit: 'count',
312
+ zero: 'invalid',
313
+ zeroClause: '0 = 一个标签组合都认不下,全折进一个桶',
314
+ badValue: 'throws',
315
+ note: '**组合数**上限(2026-09-27 ⑧ 补的第四条基数上限)。`maxLabelValues` 只封每个键的**值域**,而进内存的是组合,数量是叉乘 `(maxLabelValues+1)^labelKeys.length` —— 缺省 100 × 3 键就是一百万条,且每键的 dropped 计数看不见它。与另外三道同款:超限折进一个全 `__other__` 的桶(量不丢),折叠数见 `droppedLabelCombos` / `dropped_keys{kind="label:combos"}`。',
316
+ },
227
317
  ];
228
318
  /** 按旋钮取那行(拼错旋钮名 = 编译期报错,不是运行期 undefined) */
229
319
  export function limitSemanticOf(knob) {
@@ -1 +1 @@
1
- {"version":3,"file":"limits.js","sourceRoot":"","sources":["../../src/core/limits.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"limits.js","sourceRoot":"","sources":["../../src/core/limits.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Agentia —— 换行分隔 JSON-RPC 的**分帧缓冲上限**(两条 MCP 桥**共用一份**)。
3
+ *
4
+ * 为什么要有这个文件:两个方向的桥各自攒一个字符串缓冲按 `\n` 切帧 ——
5
+ * ① 正向连接器 `integrations/mcp-stdio.ts` 读**子进程 stdout**;
6
+ * ② 反向桥 `engine/mcp-server-stdio.ts` 读**宿主 stdin**。
7
+ * 两份都是 `buf += chunk` 然后找换行,**没有任何长度上限**(2026-09-28 外部深评 S6):
8
+ * 对端只要一直吐不带换行的字节(坏 server / 坏宿主 / 一条 5GB 的「一行」),缓冲区就无界
9
+ * 增长 ⇒ 先 OOM 再谈协议。这不是「罕见输入」:stdout 里混日志的 MCP server 本来就常见
10
+ * (连接器那条的注释已认下这点),而**一个不换行的日志行**就是这形态。
11
+ *
12
+ * 为什么两份共用一份常量而不是各写一个:这正是本仓反复吃过的「同一契约两份实现」的坑
13
+ * (见 `docs/guards.md`)—— 两侧上限漂了不会有任何症状,直到某天一侧先 OOM。
14
+ * 放 `core` 是因为 `integrations` 与 `engine` 都只许依赖 `core`(分层守卫),
15
+ * 谁也不能把常量挂在对方身上。
16
+ *
17
+ * ⚠️ 单位是 **UTF-16 码元**(`string.length`),不是字节。为什么不按字节:两侧的缓冲都是
18
+ * `setEncoding('utf8')` 之后的**字符串**,逐块算 `Buffer.byteLength` 要么每次重算整段
19
+ * (O(n²)),要么另维护一个字节计数器(同一段缓冲两套长度,迟早漂)。码元长度是现成的、
20
+ * 单调递增的,做**上限**足够 —— 而且它是**保守**的:每个码元在内存里最少占 2 字节,
21
+ * 所以真实内存占用 ≤ 2× 这个值,不会低估到危险。
22
+ *
23
+ * 取值口径:**远大于任何合法报文,远小于能让进程 OOM**。MCP 的单条报文里可能装着工具结果
24
+ * (整份文件、整段日志),给小了会把正常调用打成协议错 —— 那比 OOM 更难查(症状是「偶发
25
+ * 失败」)。800 万码元是「一次性工具结果」的宽裕上界,同时上千个并发连接各自缓冲一份也仍
26
+ * 在几 GB 量级之外。
27
+ *
28
+ * ⚠️ 这是**硬上限,不是旋钮**:它没有「0 = 不限」的读法,所以不进 `core/limits.ts` 的旋钮表
29
+ * (那张表管的是「使用者能设的旋钮,0 是什么意思」)。要改它只能改代码 + 两侧用例。
30
+ */
31
+ export declare const MAX_FRAME_CHARS: number;
32
+ /**
33
+ * 上限的人类可读说法(两条桥的报错文案共用 —— 免得一处写「8 MiB」一处写「8388608」)。
34
+ * 数值与 `MAX_FRAME_CHARS` 同源,改上限不会让文案漂。
35
+ */
36
+ export declare function frameLimitText(): string;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Agentia —— 换行分隔 JSON-RPC 的**分帧缓冲上限**(两条 MCP 桥**共用一份**)。
3
+ *
4
+ * 为什么要有这个文件:两个方向的桥各自攒一个字符串缓冲按 `\n` 切帧 ——
5
+ * ① 正向连接器 `integrations/mcp-stdio.ts` 读**子进程 stdout**;
6
+ * ② 反向桥 `engine/mcp-server-stdio.ts` 读**宿主 stdin**。
7
+ * 两份都是 `buf += chunk` 然后找换行,**没有任何长度上限**(2026-09-28 外部深评 S6):
8
+ * 对端只要一直吐不带换行的字节(坏 server / 坏宿主 / 一条 5GB 的「一行」),缓冲区就无界
9
+ * 增长 ⇒ 先 OOM 再谈协议。这不是「罕见输入」:stdout 里混日志的 MCP server 本来就常见
10
+ * (连接器那条的注释已认下这点),而**一个不换行的日志行**就是这形态。
11
+ *
12
+ * 为什么两份共用一份常量而不是各写一个:这正是本仓反复吃过的「同一契约两份实现」的坑
13
+ * (见 `docs/guards.md`)—— 两侧上限漂了不会有任何症状,直到某天一侧先 OOM。
14
+ * 放 `core` 是因为 `integrations` 与 `engine` 都只许依赖 `core`(分层守卫),
15
+ * 谁也不能把常量挂在对方身上。
16
+ *
17
+ * ⚠️ 单位是 **UTF-16 码元**(`string.length`),不是字节。为什么不按字节:两侧的缓冲都是
18
+ * `setEncoding('utf8')` 之后的**字符串**,逐块算 `Buffer.byteLength` 要么每次重算整段
19
+ * (O(n²)),要么另维护一个字节计数器(同一段缓冲两套长度,迟早漂)。码元长度是现成的、
20
+ * 单调递增的,做**上限**足够 —— 而且它是**保守**的:每个码元在内存里最少占 2 字节,
21
+ * 所以真实内存占用 ≤ 2× 这个值,不会低估到危险。
22
+ *
23
+ * 取值口径:**远大于任何合法报文,远小于能让进程 OOM**。MCP 的单条报文里可能装着工具结果
24
+ * (整份文件、整段日志),给小了会把正常调用打成协议错 —— 那比 OOM 更难查(症状是「偶发
25
+ * 失败」)。800 万码元是「一次性工具结果」的宽裕上界,同时上千个并发连接各自缓冲一份也仍
26
+ * 在几 GB 量级之外。
27
+ *
28
+ * ⚠️ 这是**硬上限,不是旋钮**:它没有「0 = 不限」的读法,所以不进 `core/limits.ts` 的旋钮表
29
+ * (那张表管的是「使用者能设的旋钮,0 是什么意思」)。要改它只能改代码 + 两侧用例。
30
+ */
31
+ export const MAX_FRAME_CHARS = 8 * 1024 * 1024;
32
+ /**
33
+ * 上限的人类可读说法(两条桥的报错文案共用 —— 免得一处写「8 MiB」一处写「8388608」)。
34
+ * 数值与 `MAX_FRAME_CHARS` 同源,改上限不会让文案漂。
35
+ */
36
+ export function frameLimitText() {
37
+ return `${MAX_FRAME_CHARS} 码元(约 ${MAX_FRAME_CHARS / (1024 * 1024)} MiB)`;
38
+ }
39
+ //# sourceMappingURL=line-framing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"line-framing.js","sourceRoot":"","sources":["../../src/core/line-framing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AAE/C;;;GAGG;AACH,MAAM,UAAU,cAAc;IAC5B,OAAO,GAAG,eAAe,SAAS,eAAe,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC;AAC3E,CAAC"}
@@ -134,6 +134,17 @@ export interface MessageUsage {
134
134
  output_tokens: number;
135
135
  cache_creation_input_tokens?: number | null;
136
136
  cache_read_input_tokens?: number | null;
137
+ /**
138
+ * 上游**根本没回报 usage**(整条流 / 整个响应都没带计量)时为 `true` ——
139
+ * 此时上面那些 `0` 是**替身值**,不是读数(2026-09-28 外部深评 S2)。
140
+ * 缺省 `undefined` = 上游真的回报了。
141
+ *
142
+ * 为什么必须显式标出来:`0` 与「不知道」在数值上无法区分,而成本估算拿到的就是
143
+ * 这个 `0` —— 于是 `maxCostUsd` 这条护栏**静默失效**(花销看起来恒为 0),
144
+ * 连「算不出成本」的信号都没有。引擎侧据此在 llm.turn span 上记 `usage.missing`
145
+ * 事件(见 `engine/turn.ts` 的 `recordTurnUsage`),报告与指标各有一处出声。
146
+ */
147
+ unreported?: true;
137
148
  }
138
149
  /** 一次模型往返的最终响应(`ModelClient.messages.stream().finalMessage()` 的产物) */
139
150
  export interface Message {