@migor/agentia 0.9.5 → 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 (38) hide show
  1. package/CHANGELOG.md +593 -1
  2. package/README.en.md +61 -3
  3. package/README.md +66 -4
  4. package/dist/AGENTS.md +18 -13
  5. package/dist/container/container.js +17 -11
  6. package/dist/container/container.js.map +1 -1
  7. package/dist/core/limits.d.ts +12 -5
  8. package/dist/core/limits.js +4 -4
  9. package/dist/core/limits.js.map +1 -1
  10. package/dist/engine/budget.js +14 -0
  11. package/dist/engine/budget.js.map +1 -1
  12. package/dist/engine/errors.d.ts +23 -1
  13. package/dist/engine/errors.js +24 -0
  14. package/dist/engine/errors.js.map +1 -1
  15. package/dist/index.d.ts +1 -1
  16. package/dist/index.js +1 -1
  17. package/dist/index.js.map +1 -1
  18. package/dist/runtime/run.js +14 -2
  19. package/dist/runtime/run.js.map +1 -1
  20. package/dist/store/redisStore.d.ts +21 -0
  21. package/dist/store/redisStore.js +21 -0
  22. package/dist/store/redisStore.js.map +1 -1
  23. package/dist/store/store.d.ts +1 -1
  24. package/dist/transport/approval-supervisor.d.ts +73 -0
  25. package/dist/transport/approval-supervisor.js +252 -0
  26. package/dist/transport/approval-supervisor.js.map +1 -0
  27. package/dist/transport/async.d.ts +29 -105
  28. package/dist/transport/async.js +85 -513
  29. package/dist/transport/async.js.map +1 -1
  30. package/dist/transport/resume-scanner.d.ts +55 -0
  31. package/dist/transport/resume-scanner.js +219 -0
  32. package/dist/transport/resume-scanner.js.map +1 -0
  33. package/dist/transport/signal-supervisor.d.ts +80 -0
  34. package/dist/transport/signal-supervisor.js +129 -0
  35. package/dist/transport/signal-supervisor.js.map +1 -0
  36. package/dist/transport/task-events.js +8 -1
  37. package/dist/transport/task-events.js.map +1 -1
  38. package/package.json +9 -1
package/README.en.md CHANGED
@@ -8,6 +8,11 @@
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** — the answer to the first question when
12
+ choosing a library (how big is the install?): `npm i @migor/agentia` puts exactly **one** package into
13
+ `node_modules` and pulls in no vendor SDK. These numbers are recomputed by
14
+ `tests/docs/scoreboard.test.ts` on every run; hand-written figures rot.
15
+
11
16
  > This is a **summary**. The authoritative and always-current documentation is Chinese:
12
17
  > [`docs/usage-guide.md`](./docs/usage-guide.md) (API reference, type wiring, known limits) and the
13
18
  > [website](https://agentia-web.pages.dev). We keep this file short on purpose — a hand-maintained
@@ -15,9 +20,11 @@ observable call tree (trace, cost, metrics).
15
20
 
16
21
  ## Requirements
17
22
 
18
- Node.js **≥ 18** (the only `engines` requirement; CI runs 18 / 20 / 22). Deno and edge runtimes are
19
- **not** validated. The only store touching a Node builtin is `SqliteTaskStore` (needs Node ≥ 22.5 for
20
- `node:sqlite`) — it fails with a readable error at construction time, and never breaks importing the package.
23
+ Node.js **≥ 18** (the only `engines` requirement). CI covers **18 / 20 / 24** — the `import-floor`
24
+ matrix pins the floor declared in `engines`, every other job follows the current LTS. Deno and edge
25
+ runtimes are **not** validated. The only store touching a Node builtin is `SqliteTaskStore` (needs
26
+ Node ≥ 22.5 for `node:sqlite`) — it fails with a readable error at construction time, and never breaks
27
+ importing the package.
21
28
 
22
29
  ## Install
23
30
 
@@ -143,6 +150,57 @@ One run == one trace (`traceId === runId`), **built in from turn 0** — not a b
143
150
  | Design spec & roadmap | [`docs/spec.md`](./docs/spec.md) · [`docs/roadmap.md`](./docs/roadmap.md) |
144
151
  | Website · Playground | [agentia-web.pages.dev](https://agentia-web.pages.dev) · [playground](https://agentia-web.pages.dev/playground) |
145
152
 
153
+ ## Stability & versioning
154
+
155
+ **What `0.x` means here:** versions follow SemVer, but during `0.x` a **minor release may contain a
156
+ breaking change**. There is exactly one rule — **a breaking change must leave a trace**: that version's
157
+ `CHANGELOG.md` entry carries a dedicated section (its heading contains 「迁移」 or 「破坏性变更」)
158
+ stating what **you** have to change.
159
+
160
+ ```bash
161
+ grep -nE '^#{3,4} .*(迁移|破坏性)' CHANGELOG.md # every "you must act" section
162
+ ```
163
+
164
+ > Honest caveat: this discipline was **tightened over time** — early versions (`0.7.1`, a type-level
165
+ > change) put the action in the entry's leading blockquote instead of its own section. The command
166
+ > above is a **lower bound**, not the whole set. And this README deliberately prints **no**
167
+ > "N releases / M migrations" count: hand-written counts rot, so we ship the command instead.
168
+
169
+ **What is covered by the compatibility promise:** the **named exports** of `@migor/agentia`
170
+ (`src/index.ts` is the source of truth) and the `@migor/cli` command surface, plus the Node range in
171
+ `engines.node` — CI runs the full chain on every version in that range. **Not covered:** deep imports
172
+ (`dist/**`) — **sealed by the `exports` field** in `package.json` (guarded by
173
+ `tests/architecture/package-exports.test.ts`; before `exports` existed they really did resolve) —
174
+ internal module paths, `packages/*`, `examples/*`, Deno / Bun / edge runtimes (unvalidated),
175
+ and anything a release does not spell out in its migration note. Runtime third-party dependencies are
176
+ **zero** — enforced by `tests/architecture/no-runtime-deps.test.ts`, not by promise.
177
+
178
+ **What 1.0 waits for** (all three are checkable): every open item in `docs/spec.md` §11 is either
179
+ shipped or explicitly struck out; `docs/guards.md` §2 ("awaiting a guard") is empty; and the public
180
+ export surface has gone **three consecutive minors with no breaking change**.
181
+
182
+ ## Performance order of magnitude
183
+
184
+ Five benchmarks ship with the repo (`scripts/bench-*.ts`). They answer **shape** questions — which
185
+ cost you pay under which condition, and what a different implementation could remove — not an SLA:
186
+
187
+ | Question | Local reading (macOS / Node 24 — **order of magnitude**) | Shape (the part that **does not** move with the machine) | Re-run |
188
+ | --- | --- | --- | --- |
189
+ | Cost of assembling an app | pure assembly `0.4 ms`; discover + assemble `8.1 ms`; whole-process boot + assemble `4040 ms` (of which cold assembly ≈2963 ms; the bare process boot alone is 1077 ms) | Swapping capability selection in a **resident runner** pays only `S2` (≈8 ms), not `S6b` (≈4 s) ⇒ **~500×**. ~1.1 s of that 4 s is the `tsx` interpreter booting — nothing to do with the framework | `npm run bench:assembly` |
190
+ | When an MCP connector costs you | cold (spawn + initialize + list) `279.5 ms`; warm (reused connector, one `tools/list` round trip) `0.3 ms` | Only **rebuilding** the connector pays the cold one ⇒ **~900×**. So whether "rebuilding the app includes an MCP handshake" is true **depends on what the connector's lifetime is** | `npm run bench:assembly` |
191
+ | What recording trace content costs | large tool output (≈37 KB/call), 5 calls: default `17.2 KB` / untruncated `233.9 KB` (**13.6×**) / truncate-200 `6.2 KB`; 20 calls: `65 KB → 932 KB` (14.3×) | The default **already truncates** (the ≈14× gap comes from turning truncation off). `traceContent:'full'` is priced **per output byte** — at small output size the measured gap is 1.0× | `PAYLOAD_ROWS=1000 CALLS='[5,20]' npm run bench:trace` |
192
+ | Cost of a metrics snapshot / OTLP flush | `snapshot()` `6.56 ms` (30 series × 2 percentiles); `buildOtlpPayload()` `0.073 ms` | Flush cost is **almost entirely `snapshot()`** ⇒ skipping it saves ≈6.5 ms per flush, **proportional to flush frequency**, unrelated to whether OTLP is consumed | `npm run bench:otlp` |
193
+ | Scanning suspended tasks for due time | sqlite N=10000: full `list()` `61.9 ms` / due index `listDue` `0.1 ms`; file store N=10000: `load` `69.5 ms`, `list` `0.0 ms` | **Having an index** is structural (**~600×**), not a tuning knob. The file store keeps records in memory ⇒ its expensive pass is the **full `load` at construction** | `npx tsx scripts/bench-resume-scan.ts 10000` |
194
+ | Shape of the redis store's `list()` | N=10000: **10001** round trips, **42.1 MB** parsed, `104.1 ms` pure CPU; with a helper ZSET (`score = wakeAt`) ⇒ **1001** round trips | Round-trip count is **structural** (that is what another implementation can remove); no local redis here, so latency is extrapolated only (RTT 0.2 ms ⇒ 2 s) | `npx tsx scripts/bench-redis-due.ts 10000` |
195
+
196
+ > ⚠️ **The milliseconds are not a promise** — they move with the machine, the Node version and the
197
+ > load; copying them as an SLA will be wrong. What is actually constant is the **shape** (how many
198
+ > times larger, how many round trips, what scales with what).
199
+ >
200
+ > The five benchmarks are **deliberately not in CI** (timing benchmarks only add noise on CI machines;
201
+ > `docs/plans/2026-09-22-dev-debug-loop.md` settled on "run them when you want to look") ⇒ nothing
202
+ > guards the numbers below — re-run the command in the last column.
203
+
146
204
  ## Contributing
147
205
 
148
206
  See [`CONTRIBUTING.md`](./CONTRIBUTING.md) — and note that [`AGENTS.md`](./AGENTS.md) is the single
package/README.md CHANGED
@@ -6,6 +6,10 @@
6
6
 
7
7
  面向应用开发的声明式 agent 服务开发框架:装饰器 + DI 声明四类能力,主 agent 编排执行;每次 run 产出结构化结果与可观测调用树(trace、成本、指标),交付可直接上线的服务。
8
8
 
9
+ **1 个包 · ≈2 MB · 0 运行时依赖 · 90+ 条守卫** —— 这一组数是选型第一个问题(安装面有多大)的答案:
10
+ `npm i @migor/agentia` 只往 `node_modules` 放**一个**包,不装任何厂商 SDK。
11
+ 数字由 `tests/docs/scoreboard.test.ts` 每次现场复算,**手写的读数会腐烂**(本仓的老病)。
12
+
9
13
  - [安装与配置](#安装与配置)
10
14
  - [快速开始](#快速开始)
11
15
  - [四类能力](#四类能力)
@@ -13,6 +17,8 @@
13
17
  - [可观测性与成本](#可观测性与成本)
14
18
  - [集成](#集成)
15
19
  - [API 速查](#api-速查)
20
+ - [稳定性与版本策略](#稳定性与版本策略)
21
+ - [性能量级](#性能量级)
16
22
  - [开发本仓库](#开发本仓库)
17
23
  - [延伸阅读](#延伸阅读)
18
24
 
@@ -36,11 +42,12 @@ npm run dev # = agentia dev:本地 inspector 面板
36
42
  > 从不执行 main.ts —— 写错一侧会让 `npm run dev` 静默读不到 `.env` 而 `npm start` 读得到。
37
43
  > 真实环境变量优先(CI / docker / 命令行永远赢过文件),`loadEnvFile({ override: true })` 才反过来。
38
44
 
39
- > **版本**:`0.9.5`(`@migor/agentia` 与 `@migor/cli` 均已发布到 npm)。`examples/` **刻意**用
45
+ > **版本**:`0.10.0`(`@migor/agentia` 与 `@migor/cli` 均已发布到 npm)。`examples/` **刻意**用
40
46
  > `file:../..` 指向本仓库而不是 npm 版本 —— 跑的是**工作区代码**,理由见
41
47
  > [`examples/README.md`](examples/README.md) 的「依赖说明」。
42
48
 
43
- > **运行时**:Node ≥ 18(`engines` 唯一要求,CI 在 18/20/22 上守)。按 Node 设计并测试,
49
+ > **运行时**:Node ≥ 18(`engines` 唯一要求)。CI 覆盖 **18 / 20 / 24** —— `import-floor` 矩阵跑
50
+ > `engines` 声明的**下限**,其余 job 跟**当前 LTS** 走。按 Node 设计并测试,
44
51
  > **未对 Deno / edge 做验证**。唯一碰内置模块的 store 是 `SqliteTaskStore`(需 Node ≥ 22.5 的
45
52
  > `node:sqlite`)—— 未提供时**构造期给可读报错**,不影响包本身被导入。
46
53
 
@@ -200,7 +207,7 @@ await runner.awaitTask(task.taskId);
200
207
 
201
208
  // 定时
202
209
  const scheduler = new Scheduler(runner);
203
- scheduler.every(60_000, '巡检一次', { idempotencyKey: 'patrol' });
210
+ scheduler.every(60_000, '巡检一次', { idempotencyPrefix: 'patrol' });
204
211
  ```
205
212
 
206
213
  ### 长上下文预算
@@ -359,6 +366,60 @@ const { result } = await app.run(messages, {
359
366
  | `fromZod` | zod schema 接入(peer 可选) |
360
367
  | `runAgent` / `executeRun` | 裸引擎入口(不走装配) |
361
368
 
369
+ ## 稳定性与版本策略
370
+
371
+ **`0.x` 的含义**:版本号按 SemVer,但 `0.x` 期间 **minor 版本可以包含破坏性变更**。
372
+ 规矩只有一条 —— **破坏性变更必须留痕**:`CHANGELOG.md` 对应的那个版本里要有一段专门写它
373
+ (小节标题含「迁移」或「破坏性变更」),写清「**你要改什么**」。
374
+
375
+ ```bash
376
+ grep -nE '^#{3,4} .*(迁移|破坏性)' CHANGELOG.md # 列出所有「要你做动作」的小节
377
+ ```
378
+
379
+ > ⚠️ **如实标注**:这条纪律是**逐步收紧**的 —— 早期的 `0.7.1`(类型面变更)把动作写在版本条目
380
+ > 开头的引用块里、没有独立小节,`0.8.1`~`0.8.3` 用一行「**迁移**:无」表态。⇒ 上面那条命令是
381
+ > **下限**而非全集,要读全就通读该版本的条目。
382
+ > **「N 个版本 / M 次迁移」这类读数本 README 刻意不写死** —— 写死的读数会腐烂(本仓为此吃过教训),
383
+ > 所以给命令、不给数字。
384
+
385
+ **承诺的范围**:
386
+
387
+ | 面 | 承诺 | **不**承诺 |
388
+ |---|---|---|
389
+ | 公共 API | `@migor/agentia` 的**具名导出**(真源 `src/index.ts`;官网 API 页有反向全覆盖守卫)+ `@migor/cli` 的命令行面 | 深路径导入(`dist/**`)、`src/**` 内部模块路径、`packages/*`、`examples/*` —— 这条边界**由 `package.json` 的 `exports` 字段封着**(`tests/architecture/package-exports.test.ts` 守;加 `exports` 之前 `dist/**` 真的导得进来) |
390
+ | 行为 | 该版本条目里写明的**迁移动作** | 没写进迁移说明的任何行为变化 |
391
+ | 运行环境 | `engines.node` 声明的区间;CI 在**该区间内每个版本**上跑完整条链 | Deno / Bun / edge runtime(**未验证**);已 EOL 的 Node(会随 minor 去掉) |
392
+ | 依赖 | **运行时零第三方依赖**(`tests/architecture/no-runtime-deps.test.ts` 守着,非口头承诺) | `devDependencies` 与 `examples/*` 的依赖面 |
393
+
394
+ **1.0 的门槛**(三条都是可核验的,不是口号):
395
+
396
+ 1. `docs/spec.md` §11「开放项」里每一条要么落地、要么被**明确划掉**(写清为什么不阻塞);
397
+ 2. `docs/guards.md` §2「待守」为空 —— 已声明的边界都由机器守着,而不是靠人记得;
398
+ 3. 公共导出面**连续 3 个 minor 无破坏性变更**。
399
+
400
+ 在此之前,请把每个 minor 当成「**这次修的可能是某个承诺没兑现的地方**」——这正是 `0.x` 的含义,
401
+ 也是 `CHANGELOG.md` 每条都写「为什么」而不是「做了什么」的原因。
402
+
403
+ ## 性能量级
404
+
405
+ 框架自带五个基准(`scripts/bench-*.ts`)。它们要回答的是**形状**问题(哪笔钱在什么条件下付、
406
+ 换实现能省掉的是什么),不是给你一个 SLA:
407
+
408
+ | 你想问的 | 本机读数(macOS / Node 24,**量级**) | 形状结论(**不会随机器变**的那部分) | 复跑 |
409
+ | --- | --- | --- | --- |
410
+ | 装配一次 app 多贵 | 纯装配 `0.4 ms`;发现+装配 `8.1 ms`;整进程启动+装配 `4040 ms`(其中「冷装配」≈2963 ms,整进程启动本身就 1077 ms) | 「换一次能力选择」在**常驻 runner** 里只付 `S2`(≈8 ms),**不需要**付 `S6b`(≈4 s)⇒ 差 **~500×**。那 4 s 里 ~1.1 s 是 `tsx` 解释器启动,与框架无关 | `npm run bench:assembly` |
411
+ | MCP 连接器什么时候付钱 | 冷(spawn + initialize + list)`279.5 ms`;热(复用连接器,仅一次 `tools/list`)`0.3 ms` | 只有**重建连接器**才付冷的那一笔 ⇒ 差 **~900×**。所以「重建 app 含 MCP 握手」成不成立,**取决于连接器跟谁同寿** | `npm run bench:assembly` |
412
+ | trace 记不记内容,差多少 | 大出参(≈37 KB/次)5 次调用:缺省 `17.2 KB` / 不截断 `233.9 KB`(**13.6×**)/ 截断 200 `6.2 KB`;20 次:`65 KB → 932 KB`(14.3×) | 缺省**已经截断**(≈14× 的量级差来自关掉截断)。`traceContent:'full'` 是**按出参字节付费**的开关 —— 小出参档实测只差 1.0× ⇒ 代价与你的出参多大成正比 | `PAYLOAD_ROWS=1000 CALLS='[5,20]' npm run bench:trace` |
413
+ | 指标快照 / OTLP flush 的成本 | `snapshot()` `6.56 ms`(30 个组合 × 2 次分位);`buildOtlpPayload()` `0.073 ms` | flush 的成本**几乎全在 `snapshot()`** ⇒ 省掉它 ≈ 每次 flush 省 6.5 ms,**与 flush 频率成正比**,与 OTLP 是否被消费无关 | `npm run bench:otlp` |
414
+ | 挂起任务的到期扫描 | sqlite N=10000:全表 `list()` `61.9 ms` / 走到期索引 `listDue` `0.1 ms`;file store N=10000:`load` `69.5 ms`、`list` `0.0 ms` | **有没有索引**是结构性的(差 **~600×**),不是调优问题。file store 的记录在内存里 ⇒ 它贵的那笔是**构造期的全表 `load`** | `npx tsx scripts/bench-resume-scan.ts 10000` |
415
+ | redis store 的 `list()` 形状 | N=10000:往返 **10001** 次、解析 **42.1 MB**、纯 CPU `104.1 ms`;有辅助 ZSET(`score = wakeAt`)⇒ 往返 **1001** | 往返次数是**结构量**(换实现能省掉的就是它);本机无 redis 实例,延迟只做外推(RTT 0.2ms ⇒ 2 s) | `npx tsx scripts/bench-redis-due.ts 10000` |
416
+
417
+ > ⚠️ **毫秒数不是承诺**:它们随机器 / Node 版本 / 负载而变,抄走当 SLA 会错。
418
+ > 真正恒定的是**形状**(差多少倍、往返几次、哪一笔跟什么成正比)—— 那才是这五个基准要说的东西。
419
+ >
420
+ > 五个基准**刻意不进 CI**(计时类基准在 CI 机器上只会制造抖动;`docs/plans/2026-09-22-dev-debug-loop.md`
421
+ > 定下的处置是「要看时跑」)⇒ 上表的数**没有守卫帮你盯着**,要看就按「复跑」那一列自己跑一遍。
422
+
362
423
  ## 开发本仓库
363
424
 
364
425
  ```bash
@@ -379,6 +440,7 @@ npm run e2e # 端到端:CLI 脚手架 → 装配 mock run;再真跑
379
440
  - 完整导出清单:[`src/index.ts`](src/index.ts)
380
441
  - 设计规格:[`docs/spec.md`](docs/spec.md)
381
442
  - 生产可观测配方(落库检索 / 日志关联 / 采样 / 脱敏):[`docs/observability.md`](docs/observability.md)
443
+ - 上线清单(store 选型 / 鉴权边界 / 优雅停机 / 预算 / 回滚 / 上线自检):[`docs/deployment.md`](docs/deployment.md)
382
444
  - 完整示例(四类能力 + 三种触发 + 全观测栈):[`examples/complete/`](examples/complete/)
383
445
  - 最小部署示例:[`examples/deploy/`](examples/deploy/)
384
- - 官网:[agentia-web.pages.dev](https://agentia-web.pages.dev)([在线 Playground](https://agentia-web.pages.dev/playground) · [文档](https://agentia-web.pages.dev/docs))
446
+ - 官网:[agentia-web.pages.dev](https://agentia-web.pages.dev)([在线 Playground](https://agentia-web.pages.dev/playground) · [文档](https://agentia-web.pages.dev/docs) · [取舍对照:不做什么 / 什么场景该选别人](https://agentia-web.pages.dev/tradeoffs))
package/dist/AGENTS.md CHANGED
@@ -868,17 +868,22 @@ runner.submit(msg.value, {
868
868
  **具体 span**(回合 / 能力调用)的 link,而不是只到 run 粒度:
869
869
 
870
870
  ```ts
871
- import { currentTraceparent } from '@migor/agentia';
872
-
873
- @Tool({ description: '把工单交给订单服务' })
874
- async placeOrder(input: { sku: string }) {
875
- const tp = currentTraceparent(); // 不在 run 内 → undefined(那时不该编一个)
876
- const res = await fetch(orderSvc, {
877
- method: 'POST',
878
- headers: { 'content-type': 'application/json', ...(tp ? { traceparent: tp } : {}) },
879
- body: JSON.stringify(input),
880
- });
881
- return res.json();
871
+ import { Tool, currentTraceparent } from '@migor/agentia';
872
+
873
+ class OrderTools {
874
+ @Tool({
875
+ description: '把工单交给订单服务',
876
+ schema: { type: 'object', properties: { sku: { type: 'string' } }, required: ['sku'], additionalProperties: false },
877
+ })
878
+ async placeOrder(input: { sku: string }) {
879
+ const tp = currentTraceparent(); // 不在 run 内 → undefined(那时不该编一个)
880
+ const res = await fetch(orderSvc, {
881
+ method: 'POST',
882
+ headers: { 'content-type': 'application/json', ...(tp ? { traceparent: tp } : {}) },
883
+ body: JSON.stringify(input),
884
+ });
885
+ return res.json();
886
+ }
882
887
  }
883
888
  ```
884
889
 
@@ -1548,7 +1553,7 @@ const waitForBatch: AgentTool = {
1548
1553
  async run(_input, ctx) {
1549
1554
  const due = await nextBatchWindow(); // 你自己的判据(外部系统回填 / 限流窗口 / 收盘)
1550
1555
  if (due.getTime() > Date.now()) {
1551
- ctx!.deferUntil(due); // 「现在还不是时候,T 之后再问我」
1556
+ ctx?.deferUntil?.(due); // 「现在还不是时候,T 之后再问我」
1552
1557
  return 'deferred'; // 返回值会被丢弃(见整批语义)
1553
1558
  }
1554
1559
  return await fetchReport();
@@ -1684,7 +1689,7 @@ const callable = {
1684
1689
  | 观测失败被吞 | sink 抛错不影响 run(观测是辅助动作);但吞之前落一条 `console.warn`(含「trace sink」字样)—— sink 天天挂不再零信号。同理记忆水合/回写失败也不击穿 run |
1685
1690
  | 框架不自动读 .env | 除 `AGENTIA_MODEL`(缺省模型覆盖)与 `OPENAI_API_KEY`(OpenAI 适配器)外,框架自己不去翻环境变量,也不读 `.env`;要读就在启动代码里调 `loadEnvFile()`(脚手架已内置那行),**真实环境变量优先**于文件 |
1686
1691
  | 鉴权只是缝 | 框架**不实现** token / JWT / 签名策略,也不碰凭据 env —— `authenticate` 只承诺「拦在入口、读 body 之前」;策略是宿主或反代的事 |
1687
- | 运行时是 Node | 按 Node ≥ 18 设计与测试(`engines` 写明,CI 在 18/20/22 上守);**未对 Deno / edge 做验证**。`SqliteTaskStore` 需 Node ≥ 22.5(`node:sqlite`),未提供时构造期抛可读报错 |
1692
+ | 运行时是 Node | 按 Node ≥ 18 设计与测试(`engines` 写明;CI 覆盖 18 / 20 / 24 —— `import-floor` 矩阵跑下限、其余 job 跟当前 LTS);**未对 Deno / edge 做验证**。`SqliteTaskStore` 需 Node ≥ 22.5(`node:sqlite`),未提供时构造期抛可读报错 |
1688
1693
  | 停机不由框架触发 | 框架给 `drain()` 但**不订阅** `SIGTERM`/`SIGINT`(不做进程级决策);信号处理是宿主的 |
1689
1694
  | 停机可能切断 SSE | `drain()` 收口时会强制关闭仍开着的 SSE 流,`/run` 的流被收口时其 run 以 `stopReason='aborted'` 收尾(客户端应把断流当作可重试)。**没有 `timeoutMs` 时也收口**(否则等待没有终点,见上);只关旁观者长连(`/tasks/:id/stream`)**不损失工作**,那种收口不影响 `drain()` 的返回值 |
1690
1695
  | 多进程共库:续跑认领靠**租约**,不靠时间(2026-09-28) | `resumePending({ staleAfterMs })` 的 `staleAfterMs` **不是**租约而是**兜底启发式**。`> 0` 时按两档判他进程的记录:① `ownerId` 里的主机名是本机 ⇒ **问那个 pid 还在不在**(`process.kill(pid, 0)`)——**在的绝不抢**(不管记录多老)、**不在的立刻可抢**(崩溃孤儿不等保鲜期);② **判不了**(异主机 / 升级前写下的旧格式 `ownerId` / 自定义串)⇒ 才用 `staleAfterMs`(「记录看起来还新」)。缺省 `0` = 不问他进程,与升级前逐字一致 |
@@ -86,20 +86,26 @@ export class Container {
86
86
  finally {
87
87
  this.resolving.pop();
88
88
  }
89
- // thenable 检测(外部深评 K4):容器是**同步**解析的 —— `async` 工厂返回的 Promise
90
- // 会原样被当成「值」缓存,下游注入到的是 Promise 本身:首次属性访问全 `undefined`、
91
- // 零报错、`tsc` 也看不出来(类型断言成 T 了)。这类「静默给错东西」必须在构造/解析期响亮失败。
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
+ // 几乎不出现,真要注入本体请走下面的逃逸口。
92
101
  //
93
102
  // ⚠️ 检测器**就地写**、不从别处 import:`container` 是纯叶子(`layering.test.ts` 里
94
- // `container: []`,不许引内部任何模块),为两行逻辑破叶子边界不值得。
103
+ // `container: []`,不许引内部任何模块),为几行逻辑破叶子边界不值得。
95
104
  // 逃逸口:确实要注入 Promise **本体**,包一层即可(`useValue: { promise }`)。
96
- if (value !== null && (typeof value === 'object' || typeof value === 'function')) {
97
- const then = value.then;
98
- if (typeof then === 'function') {
99
- throw new TypeError(`provider "${token}" 解析出的是 Promise:容器同步解析,它会被原样缓存成「值」,` +
100
- '下游拿到 Promise 而非 await 过的结果(首次属性访问全 undefined、且零报错)。' +
101
- '异步初始化请在装配前 await 好再用 useValue 注入;确实要注入 Promise 本体,包一层:useValue: { promise }');
102
- }
105
+ if (value instanceof Promise) {
106
+ throw new TypeError(`provider "${token}" 解析出的是 Promise:容器同步解析,它会被原样缓存成「值」,` +
107
+ '下游拿到 Promise 而非 await 过的结果(首次属性访问全 undefined、且零报错)。' +
108
+ '异步初始化请在装配前 await 好再用 useValue 注入;确实要注入 Promise 本体,包一层:useValue: { promise }');
103
109
  }
104
110
  this.cache.set(token, value);
105
111
  return value;
@@ -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;
@@ -197,16 +204,16 @@ export declare const LIMIT_SEMANTICS: readonly [{
197
204
  readonly unit: 'count';
198
205
  readonly zero: 'immediate';
199
206
  readonly zeroClause: '0 = 第一次记账就超预算(等价于「任何用量都不许」)';
200
- readonly badValue: 'none';
201
- readonly note: '闸门判据是 `totalTokens > maxTotalTokens`(**严格大于**):0 时任何非零用量立刻撞线;负数同理(`> -1` 恒真)。这里刻意**不抛错** —— 预算是运行期累计量,配成 0 是「这次 run 一条都别花」的合法意图(与 `maxIterations: 0` 同档),不是坏配置。⚠️ 与 `maxCostUsd` 是两个独立闸,**tokens 先判、撞了就返回**(不再看 cost)。⚠️ 2026-09-28 补登:这两个旋钮自 v0.5.0 起就存在,却一直没进这张表(「改 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 的读法不改表就构建红」这条纪律对它们曾是空的)。';
202
209
  }, {
203
210
  readonly knob: 'BudgetGuardOptions.maxCostUsd';
204
211
  readonly where: 'engine/budget.ts(createBudgetGuard;由 RunAgentOptions.maxCostUsd 传进来)';
205
212
  readonly unit: 'n/a';
206
213
  readonly zero: 'immediate';
207
214
  readonly zeroClause: '0 = 第一笔成本就超预算(等价于「一分钱都不许花」)';
208
- readonly badValue: 'none';
209
- readonly note: '判据 `costUsd > maxCostUsd`,而 `costUsd` 取 `usage.costEstimate ?? 0` ⇒ **模型不在价格表内时它恒为 0**,这个闸对未定价模型**永不触发**(静默失效;要无条件兜底请用 `maxTotalTokens`)。这一句在 `BudgetGuardOptions` 的注释里已如实写着,这里登记是为了让「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`。';
210
217
  }, {
211
218
  readonly knob: 'metricsSink.maxCapabilities';
212
219
  readonly where: 'integrations/metrics.ts(metricsSink)';
@@ -175,8 +175,8 @@ export const LIMIT_SEMANTICS = [
175
175
  unit: 'count',
176
176
  zero: 'immediate',
177
177
  zeroClause: '0 = 第一次记账就超预算(等价于「任何用量都不许」)',
178
- badValue: 'none',
179
- note: '闸门判据是 `totalTokens > maxTotalTokens`(**严格大于**):0 时任何非零用量立刻撞线;负数同理(`> -1` 恒真)。这里刻意**不抛错** —— 预算是运行期累计量,配成 0 是「这次 run 一条都别花」的合法意图(与 `maxIterations: 0` 同档),不是坏配置。⚠️ 与 `maxCostUsd` 是两个独立闸,**tokens 先判、撞了就返回**(不再看 cost)。⚠️ 2026-09-28 补登:这两个旋钮自 v0.5.0 起就存在,却一直没进这张表(「改 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
180
  },
181
181
  {
182
182
  knob: 'BudgetGuardOptions.maxCostUsd',
@@ -184,8 +184,8 @@ export const LIMIT_SEMANTICS = [
184
184
  unit: 'n/a',
185
185
  zero: 'immediate',
186
186
  zeroClause: '0 = 第一笔成本就超预算(等价于「一分钱都不许花」)',
187
- badValue: 'none',
188
- note: '判据 `costUsd > maxCostUsd`,而 `costUsd` 取 `usage.costEstimate ?? 0` ⇒ **模型不在价格表内时它恒为 0**,这个闸对未定价模型**永不触发**(静默失效;要无条件兜底请用 `maxTotalTokens`)。这一句在 `BudgetGuardOptions` 的注释里已如实写着,这里登记是为了让「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
189
  },
190
190
  {
191
191
  knob: 'metricsSink.maxCapabilities',
@@ -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":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAoDH;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,gEAAgE;IAChE;QACE,IAAI,EAAE,0BAA0B;QAChC,KAAK,EAAE,oBAAoB;QAC3B,IAAI,EAAE,IAAI;QACV,IAAI,EAAE,WAAW;QACjB,UAAU,EAAE,QAAQ;QACpB,QAAQ,EAAE,QAAQ;QAClB,IAAI,EAAE,qOAAqO;KAC5O;IACD;QACE,IAAI,EAAE,+BAA+B;QACrC,KAAK,EAAE,oBAAoB;QAC3B,IAAI,EAAE,IAAI;QACV,IAAI,EAAE,WAAW;QACjB,UAAU,EAAE,QAAQ;QACpB,QAAQ,EAAE,QAAQ;QAClB,IAAI,EAAE,2CAA2C;KAClD;IACD;QACE,IAAI,EAAE,uBAAuB;QAC7B,KAAK,EAAE,oBAAoB;QAC3B,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,WAAW;QACjB,UAAU,EAAE,gBAAgB;QAC5B,QAAQ,EAAE,QAAQ;QAClB,IAAI,EAAE,+TAA+T;KACtU;IACD;QACE,IAAI,EAAE,6BAA6B;QACnC,KAAK,EAAE,yBAAyB;QAChC,IAAI,EAAE,IAAI;QACV,IAAI,EAAE,WAAW;QACjB,UAAU,EAAE,UAAU;QACtB,QAAQ,EAAE,MAAM;QAChB,IAAI,EAAE,uRAAuR;KAC9R;IACD;QACE,IAAI,EAAE,0BAA0B;QAChC,KAAK,EAAE,uBAAuB;QAC9B,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,WAAW;QACjB,UAAU,EAAE,SAAS;QACrB,QAAQ,EAAE,sBAAsB;QAChC,IAAI,EAAE,yMAAyM;KAChN;IAED,uDAAuD;IACvD;QACE,IAAI,EAAE,uBAAuB;QAC7B,KAAK,EAAE,kBAAkB;QACzB,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,UAAU;QAChB,UAAU,EAAE,WAAW;QACvB,QAAQ,EAAE,QAAQ;QAClB,IAAI,EAAE,6EAA6E;KACpF;IACD;QACE,IAAI,EAAE,YAAY;QAClB,KAAK,EAAE,iCAAiC;QACxC,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,UAAU;QAChB,UAAU,EAAE,SAAS;QACrB,QAAQ,EAAE,QAAQ;QAClB,IAAI,EAAE,mIAAmI;KAC1I;IACD;QACE,IAAI,EAAE,eAAe;QACrB,KAAK,EAAE,uBAAuB;QAC9B,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,UAAU;QAChB,UAAU,EAAE,uBAAuB;QACnC,QAAQ,EAAE,MAAM;QAChB,IAAI,EAAE,0FAA0F;KACjG;IACD;QACE,IAAI,EAAE,wBAAwB;QAC9B,KAAK,EAAE,gBAAgB;QACvB,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,UAAU;QAChB,UAAU,EAAE,WAAW;QACvB,QAAQ,EAAE,MAAM;QAChB,IAAI,EAAE,gJAAgJ;KACvJ;IACD;QACE,IAAI,EAAE,gBAAgB;QACtB,KAAK,EAAE,iBAAiB;QACxB,IAAI,EAAE,IAAI;QACV,IAAI,EAAE,UAAU;QAChB,UAAU,EAAE,iBAAiB;QAC7B,QAAQ,EAAE,QAAQ;QAClB,IAAI,EAAE,oWAAoW;KAC3W;IAED;QACE,IAAI,EAAE,iCAAiC;QACvC,KAAK,EAAE,0BAA0B;QACjC,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,UAAU;QAChB,UAAU,EAAE,iBAAiB;QAC7B,QAAQ,EAAE,QAAQ;QAClB,IAAI,EAAE,mIAAmI;KAC1I;IAED;QACE,IAAI,EAAE,qCAAqC;QAC3C,KAAK,EAAE,0BAA0B;QACjC,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,UAAU;QAChB,UAAU,EAAE,iCAAiC;QAC7C,QAAQ,EAAE,QAAQ;QAClB,IAAI,EAAE,wSAAwS;KAC/S;IAED,0DAA0D;IAC1D;QACE,IAAI,EAAE,uBAAuB;QAC7B,KAAK,EAAE,iBAAiB;QACxB,IAAI,EAAE,IAAI;QACV,IAAI,EAAE,WAAW;QACjB,UAAU,EAAE,SAAS;QACrB,QAAQ,EAAE,QAAQ;QAClB,IAAI,EAAE,mXAAmX;KAC1X;IACD;QACE,IAAI,EAAE,wBAAwB;QAC9B,KAAK,EAAE,yBAAyB;QAChC,IAAI,EAAE,IAAI;QACV,IAAI,EAAE,WAAW;QACjB,UAAU,EAAE,UAAU;QACtB,QAAQ,EAAE,MAAM;QAChB,IAAI,EAAE,iHAAiH;KACxH;IAED;QACE,IAAI,EAAE,mCAAmC;QACzC,KAAK,EAAE,0EAA0E;QACjF,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,WAAW;QACjB,UAAU,EAAE,6BAA6B;QACzC,QAAQ,EAAE,aAAa;QACvB,IAAI,EAAE,ydAAyd;KAChe;IACD;QACE,IAAI,EAAE,+BAA+B;QACrC,KAAK,EAAE,sEAAsE;QAC7E,IAAI,EAAE,KAAK;QACX,IAAI,EAAE,WAAW;QACjB,UAAU,EAAE,6BAA6B;QACzC,QAAQ,EAAE,aAAa;QACvB,IAAI,EAAE,oUAAoU;KAC3U;IACD;QACE,IAAI,EAAE,6BAA6B;QACnC,KAAK,EAAE,sCAAsC;QAC7C,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,SAAS;QACf,UAAU,EAAE,oBAAoB;QAChC,QAAQ,EAAE,QAAQ;QAClB,IAAI,EAAE,sLAAsL;KAC7L;IACD;QACE,IAAI,EAAE,uBAAuB;QAC7B,KAAK,EAAE,sCAAsC;QAC7C,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,SAAS;QACf,UAAU,EAAE,oBAAoB;QAChC,QAAQ,EAAE,QAAQ;QAClB,IAAI,EAAE,mCAAmC;KAC1C;IACD;QACE,IAAI,EAAE,uBAAuB;QAC7B,KAAK,EAAE,sCAAsC;QAC7C,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,SAAS;QACf,UAAU,EAAE,oBAAoB;QAChC,QAAQ,EAAE,QAAQ;QAClB,IAAI,EAAE,sBAAsB;KAC7B;IAED,iEAAiE;IACjE;QACE,IAAI,EAAE,yBAAyB;QAC/B,KAAK,EAAE,oBAAoB;QAC3B,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,SAAS;QACf,UAAU,EAAE,eAAe;QAC3B,QAAQ,EAAE,QAAQ;QAClB,IAAI,EAAE,uCAAuC;KAC9C;IACD;QACE,IAAI,EAAE,6BAA6B;QACnC,KAAK,EAAE,wBAAwB;QAC/B,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,SAAS;QACf,UAAU,EAAE,oCAAoC;QAChD,QAAQ,EAAE,QAAQ;QAClB,IAAI,EAAE,iSAAiS;KACxS;IACD;QACE,IAAI,EAAE,iCAAiC;QACvC,KAAK,EAAE,sCAAsC;QAC7C,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,SAAS;QACf,UAAU,EAAE,uBAAuB;QACnC,QAAQ,EAAE,QAAQ;QAClB,IAAI,EAAE,uUAAuU;KAC9U;IACD;QACE,IAAI,EAAE,sCAAsC;QAC5C,KAAK,EAAE,sCAAsC;QAC7C,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,SAAS;QACf,UAAU,EAAE,4BAA4B;QACxC,QAAQ,EAAE,QAAQ;QAClB,IAAI,EAAE,wLAAwL;KAC/L;IACD;QACE,IAAI,EAAE,mCAAmC;QACzC,KAAK,EACH,4FAA4F;QAC9F,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,SAAS;QACf,UAAU,EAAE,YAAY;QACxB,QAAQ,EAAE,QAAQ;QAClB,IAAI,EAAE,0NAA0N;KACjO;IACD;QACE,IAAI,EAAE,gCAAgC;QACtC,KAAK,EAAE,0BAA0B;QACjC,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,SAAS;QACf,UAAU,EAAE,yBAAyB;QACrC,QAAQ,EAAE,QAAQ;QAClB,IAAI,EAAE,+IAA+I;KACtJ;IACD;QACE,IAAI,EAAE,4BAA4B;QAClC,KAAK,EAAE,wBAAwB;QAC/B,IAAI,EAAE,IAAI;QACV,IAAI,EAAE,SAAS;QACf,UAAU,EAAE,gBAAgB;QAC5B,QAAQ,EAAE,QAAQ;QAClB,IAAI,EAAE,6MAA6M;KACpN;IACD;QACE,IAAI,EAAE,+BAA+B;QACrC,KAAK,EAAE,2BAA2B;QAClC,IAAI,EAAE,IAAI;QACV,IAAI,EAAE,SAAS;QACf,UAAU,EAAE,gBAAgB;QAC5B,QAAQ,EAAE,QAAQ;QAClB,IAAI,EAAE,0IAA0I;KACjJ;IACD;QACE,IAAI,EAAE,wBAAwB;QAC9B,KAAK,EAAE,yBAAyB;QAChC,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,SAAS;QACf,UAAU,EAAE,cAAc;QAC1B,QAAQ,EAAE,QAAQ;QAClB,IAAI,EAAE,mEAAmE;KAC1E;IACD;QACE,IAAI,EAAE,4BAA4B;QAClC,KAAK,EAAE,yBAAyB;QAChC,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,SAAS;QACf,UAAU,EAAE,eAAe;QAC3B,QAAQ,EAAE,QAAQ;QAClB,IAAI,EAAE,+LAA+L;KACtM;IACD;QACE,IAAI,EAAE,4BAA4B;QAClC,KAAK,EAAE,yBAAyB;QAChC,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,SAAS;QACf,UAAU,EAAE,uBAAuB;QACnC,QAAQ,EAAE,QAAQ;QAClB,IAAI,EAAE,qQAAqQ;KAC5Q;CAC0C,CAAC;AAK9C,4CAA4C;AAC5C,MAAM,UAAU,eAAe,CAAC,IAAe;IAC7C,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IAC3D,sDAAsD;IACtD,uCAAuC;IACvC,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,UAAU,CAAC,CAAC;IAC7D,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,IAAe;IAC1C,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC;AAC1C,CAAC"}
@@ -1,9 +1,23 @@
1
+ /**
2
+ * 预算旋钮收到 `NaN` ⇒ 构造期响亮失败(对应 `core/limits.ts` 的 `rejects-nan`)。
3
+ * `undefined` = 不设上限(合法);只挡 `NaN`(`Number.isNaN`)。
4
+ */
5
+ function assertNotNaN(value, name) {
6
+ if (value !== undefined && Number.isNaN(value)) {
7
+ throw new RangeError(`${name} 收到 NaN —— 闸门判据是「实际值 > 上限」,而 \`x > NaN\` 恒为 false ⇒ 闸永不触发、` +
8
+ '静默失效(方向与「设了个上限」正好相反)。0 / 负数 / ±Infinity 均可;要「不限」请省略该选项。');
9
+ }
10
+ }
1
11
  /**
2
12
  * 建一个预算护栏。`check` 只看 **totalUsage**(已由 recorder 按 llm.turn 求和,
3
13
  * 子 agent 的往返也在内 —— 所以这是**整条 run** 的口径,不只是主循环)。
4
14
  */
5
15
  export function createBudgetGuard(opts = {}) {
6
16
  const { maxTotalTokens, maxCostUsd, onExceed } = opts;
17
+ // NaN 让判据 `x > max` 恒 false ⇒ 闸永不触发、**静默失效**,方向与「设了个上限」相反
18
+ // (2026-09-29 盘点 C5b)。0 / 负数 / ±Infinity 都还有可辩护的读法(全停 / 不限),照旧放行。
19
+ assertNotNaN(maxTotalTokens, 'maxTotalTokens');
20
+ assertNotNaN(maxCostUsd, 'maxCostUsd');
7
21
  return {
8
22
  check(input) {
9
23
  const u = input.totalUsage;
@@ -1 +1 @@
1
- {"version":3,"file":"budget.js","sourceRoot":"","sources":["../../src/engine/budget.ts"],"names":[],"mappings":"AA4DA;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAI,GAAuB,EAAE;IAC7D,MAAM,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;IACtD,OAAO;QACL,KAAK,CAAC,KAAqC;YACzC,MAAM,CAAC,GAAG,KAAK,CAAC,UAAU,CAAC;YAC3B,MAAM,WAAW,GACf,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,YAAY,GAAG,CAAC,CAAC,eAAe,GAAG,CAAC,CAAC,mBAAmB,CAAC;YAC7E,MAAM,OAAO,GAAG,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC;YACpC,IAAI,cAAc,IAAI,IAAI,IAAI,WAAW,GAAG,cAAc,EAAE,CAAC;gBAC3D,QAAQ,EAAE,CAAC;oBACT,IAAI,EAAE,QAAQ;oBACd,KAAK,EAAE,cAAc;oBACrB,MAAM,EAAE,WAAW;oBACnB,WAAW;oBACX,OAAO;iBACR,CAAC,CAAC;gBACH,OAAO,QAAQ,CAAC;YAClB,CAAC;YACD,IAAI,UAAU,IAAI,IAAI,IAAI,OAAO,GAAG,UAAU,EAAE,CAAC;gBAC/C,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC;gBACvF,OAAO,MAAM,CAAC;YAChB,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"budget.js","sourceRoot":"","sources":["../../src/engine/budget.ts"],"names":[],"mappings":"AA4DA;;;GAGG;AACH,SAAS,YAAY,CAAC,KAAyB,EAAE,IAAY;IAC3D,IAAI,KAAK,KAAK,SAAS,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,UAAU,CAClB,GAAG,IAAI,4DAA4D;YACjE,yDAAyD,CAC5D,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAI,GAAuB,EAAE;IAC7D,MAAM,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;IACtD,0DAA0D;IAC1D,kEAAkE;IAClE,YAAY,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAC;IAC/C,YAAY,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IACvC,OAAO;QACL,KAAK,CAAC,KAAqC;YACzC,MAAM,CAAC,GAAG,KAAK,CAAC,UAAU,CAAC;YAC3B,MAAM,WAAW,GACf,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,YAAY,GAAG,CAAC,CAAC,eAAe,GAAG,CAAC,CAAC,mBAAmB,CAAC;YAC7E,MAAM,OAAO,GAAG,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC;YACpC,IAAI,cAAc,IAAI,IAAI,IAAI,WAAW,GAAG,cAAc,EAAE,CAAC;gBAC3D,QAAQ,EAAE,CAAC;oBACT,IAAI,EAAE,QAAQ;oBACd,KAAK,EAAE,cAAc;oBACrB,MAAM,EAAE,WAAW;oBACnB,WAAW;oBACX,OAAO;iBACR,CAAC,CAAC;gBACH,OAAO,QAAQ,CAAC;YAClB,CAAC;YACD,IAAI,UAAU,IAAI,IAAI,IAAI,OAAO,GAAG,UAAU,EAAE,CAAC;gBAC/C,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC;gBACvF,OAAO,MAAM,CAAC;YAChB,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -13,5 +13,27 @@ import type { SpanError } from '../core/trace.js';
13
13
  */
14
14
  /** 是否中断异常(DOMException / 普通 Error 都可能是,靠 name 判定) */
15
15
  export declare function isAbortError(e: unknown): boolean;
16
+ /**
17
+ * 分类器**能产出的全部类别** —— 单一真源(2026-09-29 ⑤)。
18
+ *
19
+ * 为什么需要这个常量:在此之前「到底有哪几类」只活在 `classifyError` 的字面量里,
20
+ * 任何想按类别表态的地方(浸泡验证的判据、看板、`trace-diff`)都只能**再手写一份清单**,
21
+ * 而漏一类的表现是**静默**的。实测过一次(就是加这个常量的原因):
22
+ * `scripts/e2e-soak.ts` 里手写的 `['api','server','rate_limit']` 漏了 `connection`,
23
+ * 默认 60 秒的跑法永远撞不到 —— 而 2 小时那轮(719 万 run / 811 万请求)撞到 **1 次**
24
+ * 就让整轮报红,红的是判据、不是被测的代码(spec §10 2026-09-29 ⑤)。
25
+ *
26
+ * 消费方式:`Record<ErrorType, …>` 的**穷尽表** —— 加一类而不表态 ⇒ `TS2741`
27
+ * (与 `SUCCESS_STOP_REASON` / `KIND_SPEC` 同款护栏,见 `docs/guards.md`)。
28
+ * ⚠️ 本常量只收窄**分类器的输出**;`SpanError.type` 仍然是公共类型上的 `string`
29
+ * (收紧它是对使用者的破坏性变更,不做)。
30
+ */
31
+ export declare const ERROR_TYPES: readonly ['aborted', 'rate_limit', 'server', 'api', 'timeout', 'connection', 'unknown'];
32
+ /** 分类器能产出的类别(`ERROR_TYPES` 的成员) */
33
+ export type ErrorType = (typeof ERROR_TYPES)[number];
34
+ /** `classifyError` 的产出:`SpanError` 的收窄版(`type` 是闭合联合,不是裸 `string`) */
35
+ export interface ClassifiedError extends SpanError {
36
+ type: ErrorType;
37
+ }
16
38
  /** 把任意异常分类成 trace 可用的 SpanError(可重试 vs 不可重试)。 */
17
- export declare function classifyError(e: unknown): SpanError;
39
+ export declare function classifyError(e: unknown): ClassifiedError;
@@ -15,6 +15,30 @@ import { isTimeoutError } from '../core/timeout.js';
15
15
  export function isAbortError(e) {
16
16
  return typeof e === 'object' && e !== null && e.name === 'AbortError';
17
17
  }
18
+ /**
19
+ * 分类器**能产出的全部类别** —— 单一真源(2026-09-29 ⑤)。
20
+ *
21
+ * 为什么需要这个常量:在此之前「到底有哪几类」只活在 `classifyError` 的字面量里,
22
+ * 任何想按类别表态的地方(浸泡验证的判据、看板、`trace-diff`)都只能**再手写一份清单**,
23
+ * 而漏一类的表现是**静默**的。实测过一次(就是加这个常量的原因):
24
+ * `scripts/e2e-soak.ts` 里手写的 `['api','server','rate_limit']` 漏了 `connection`,
25
+ * 默认 60 秒的跑法永远撞不到 —— 而 2 小时那轮(719 万 run / 811 万请求)撞到 **1 次**
26
+ * 就让整轮报红,红的是判据、不是被测的代码(spec §10 2026-09-29 ⑤)。
27
+ *
28
+ * 消费方式:`Record<ErrorType, …>` 的**穷尽表** —— 加一类而不表态 ⇒ `TS2741`
29
+ * (与 `SUCCESS_STOP_REASON` / `KIND_SPEC` 同款护栏,见 `docs/guards.md`)。
30
+ * ⚠️ 本常量只收窄**分类器的输出**;`SpanError.type` 仍然是公共类型上的 `string`
31
+ * (收紧它是对使用者的破坏性变更,不做)。
32
+ */
33
+ export const ERROR_TYPES = [
34
+ 'aborted',
35
+ 'rate_limit',
36
+ 'server',
37
+ 'api',
38
+ 'timeout',
39
+ 'connection',
40
+ 'unknown',
41
+ ];
18
42
  /** 取错误的可读 message(Error 取 .message,其余 String 兜底) */
19
43
  function messageOf(e) {
20
44
  return e instanceof Error ? e.message : String(e);
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/engine/errors.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD;;;;;;;;;;;GAWG;AAEH,qDAAqD;AACrD,MAAM,UAAU,YAAY,CAAC,CAAU;IACrC,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAK,CAAwB,CAAC,IAAI,KAAK,YAAY,CAAC;AAChG,CAAC;AAED,oDAAoD;AACpD,SAAS,SAAS,CAAC,CAAU;IAC3B,OAAO,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AACpD,CAAC;AAED,iDAAiD;AACjD,MAAM,gBAAgB,GACpB,uGAAuG,CAAC;AAE1G;;;;;;;;;GASG;AACH,SAAS,iBAAiB,CAAC,CAAS;IAClC,IAAI,CAAC,YAAY,SAAS,IAAK,CAAyB,CAAC,KAAK,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IACpF,MAAM,IAAI,GAAI,CAAwB,CAAC,IAAI,CAAC;IAC5C,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACzE,OAAO,KAAK,CAAC;AACf,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,aAAa,CAAC,CAAU;IACtC,IAAI,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;QACpB,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IACpE,CAAC;IACD,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;QACxC,MAAM,MAAM,GAAI,CAA0B,CAAC,MAAM,CAAC;QAClD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1D,IAAI,MAAM,KAAK,GAAG;gBAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;YAC1F,IAAI,MAAM,IAAI,GAAG;gBAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;YACrF,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;QAClE,CAAC;QACD,uEAAuE;QACvE,mEAAmE;QACnE,6DAA6D;QAC7D,wDAAwD;QACxD,IAAI,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC;YACtB,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;QACrE,CAAC;QACD,IAAI,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAAC;YACzB,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;QACxE,CAAC;IACH,CAAC;IACD,IAAI,CAAC,YAAY,KAAK,EAAE,CAAC;QACvB,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IACnE,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;AACnE,CAAC"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/engine/errors.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export declare const AGENTIA_VERSION = "0.9.5";
1
+ export declare const AGENTIA_VERSION = "0.10.0";
2
2
  export type { Trace, Span, SpanEvent, SpanError, SpanId, SpanKind, SpanLink, SpanStatus, TraceId, TraceContext, TraceSink, TraceRecordEvent, CapabilityType, Usage, Score, } from './core/trace.js';
3
3
  export { attachScore, parseTraceparent } from './core/trace.js';
4
4
  export type { AgentTool, ApprovalDecision, JsonSchema, ModelClient, RecorderBackend, SchemaInput, SchemaType, ToolRunContext, TypedSchema, } from './core/tool.js';
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export const AGENTIA_VERSION = '0.9.5';
1
+ export const AGENTIA_VERSION = '0.10.0';
2
2
  export { attachScore, parseTraceparent } from './core/trace.js';
3
3
  export { validateJsonSchema } from './core/schema.js';
4
4
  export { combineSignals } from './core/abort.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -147,8 +147,10 @@ export async function executeRun(options) {
147
147
  try {
148
148
  memoryRev = await hydrateMemory(memory, ctx);
149
149
  }
150
- catch {
151
- /* ignore:辅助动作失败不影响 run */
150
+ catch (e) {
151
+ // 水合失败此前是**完全无声**的:它的后果(CAS 回写被拒)发生在几十行之后、还被归因成
152
+ // 「并发抢写」(2026-09-29 外部深评 A1)—— 在这里留下**第一因**,别让它隔着整条 run。
153
+ console.warn('[agentia] 记忆水合(load)失败,本次 run 当作「无记忆」继续(辅助动作失败不击穿 run):', e instanceof Error ? e.message : e);
152
154
  }
153
155
  }
154
156
  const result = await runAgent({
@@ -360,6 +362,16 @@ async function flushMemory(memory, ctx, rev) {
360
362
  }
361
363
  const { store } = memory;
362
364
  if (typeof store.saveIfRev === 'function') {
365
+ // rev === undefined ⇒ 水合(load)失败被上面 catch 吞掉(能走到本分支的 store 必有
366
+ // loadWithRev —— `assertMemoryStoreShape` 在入口保证成对)⇒ 此时做 CAS 没有意义:**读都没
367
+ // 成功**,谈「读之后有没有人写」是伪命题;而 `saveIfRev(entries, undefined)` 会被 CAS store
368
+ // 判成冲突 ⇒ 报出「并发抢写」的**假归因**(2026-09-29 外部深评 A1)。跳过 + 说真话。
369
+ if (rev === undefined) {
370
+ console.warn('[agentia] 记忆回写被跳过:水合(load)失败 ⇒ 没有可用的版本号,不做 CAS 回写(写了也只会被拒)。' +
371
+ `keys=[${memory.keys.join(', ')}] —— 真因是「这一次读失败了」,**不是**并发抢写;` +
372
+ '要查的是 store 连通性(Redis 挂了 / 网络抖动),不是并发。');
373
+ return;
374
+ }
363
375
  const outcome = await store.saveIfRev(entries, rev);
364
376
  // 判据是 `!== true`(不是 `=== false`):`saveIfRev` 忘返回结果(`undefined`)时
365
377
  // 按「没提交」处理 —— 宁可多报一次,也不把「不知道写没写」记成写成功。