@migor/agentia 0.9.5 → 0.10.1

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 +740 -1
  2. package/README.en.md +61 -3
  3. package/README.md +66 -4
  4. package/dist/AGENTS.md +54 -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 +11 -2
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.1`(`@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
@@ -198,6 +198,40 @@ token 的作用是挡住**本机其它进程**,别把它当网络边界:这
198
198
  当前用哪个 id 记在 `.agentia/dev-session-id`(**不是** `session.json` 的一部分):只放内存的话,
199
199
  重启 `npm run dev` 之后刚清空的对话会**自己回来**。有 run 在飞时不能清空(会 409)。
200
200
 
201
+ **每次 run 的 trace 都落盘**:收尾时追加一行到 `.agentia/traces.jsonl`(已被 `.gitignore` 挡住)。
202
+ 那个格式**恰好就是** `agentia report` / `diff` / `harvest` / `export` 四个命令的输入 ——
203
+ 所以面板里跑过的每一次 run,都可以直接:
204
+
205
+ ```bash
206
+ agentia report .agentia/traces.jsonl # 哪个能力慢 / 贵 / 爱失败
207
+ agentia diff .agentia/traces.jsonl ... # 改 prompt 前后逐 span 比对(有差异时退出码 1)
208
+ agentia harvest .agentia/traces.jsonl --failed # 失败的 run → eval 用例骨架
209
+ agentia export .agentia/traces.jsonl # → 训练数据集 JSONL
210
+ ```
211
+
212
+ 落盘**不替代**右栏的实时树:那是两条缝(落盘管「事后能查」,`onTraceEvent` 管「此刻看到」)。
213
+ 三条如实标注:① 文件**只增不减**(要清是你自己 `rm`);② 落盘是**同步写**,单条 trace 以百 KB
214
+ 计、且 `traceLimits.maxEvents` 本来兜上限;③ 面板的 run 列表是**内存** ring(50 条),
215
+ 重启就空了 —— 落盘让数据**没丢**,但面板不会替你回看历史。
216
+
217
+ **并排 A/B(比两条 run差在哪)**:run 列表里 **⌘/Ctrl + 单击**选两条(再点一次取消、
218
+ 选第三条挤掉最早那条),右栏切**双栏**并排两棵调用树,顶部一条差异摘要
219
+ (run 级 summary + 逐 span 字段级差异,与 `agentia diff` 逐字同源)。
220
+
221
+ ⚠️ **可比性分三档,显式不静默**:能力选择与工作目录是 **app 级**的(换它们要重启进程)⇒
222
+ 两条跨了能力选择的 run,配置整个变了,diff 出来的差异**多半与你改的那句话无关**。
223
+ 所以跨能力 / 跨目录 / 元信息缺失都给出**显式降级提示**(含可执行的出路:建议只改 prompt 后重跑),
224
+ 而不是静默给一份没意义的 diff。可比时**不显示**提示条(每次都飘噪声等于没有提示)。
225
+
226
+ ⚠️ **并排是「事后回看」,不是「同时跑」**:单飞闸没解除 —— 解除会连带改变「文件变更延后重启」
227
+ 的语义(两条判据今天刻意共用同一条忙闲判据),且 A/B 的收益全在「事后并排看差在哪」,
228
+ 同时跑还多烧一倍 token。普通单击任意一条即退出并排。
229
+
230
+ **重跑后自动补对**:已经选了一条(还没选第二条)时,新 run 一收尾就**自动**并排 ——
231
+ 面板会播一条 notice 说清「为什么屏幕变双栏了」。三种情况**不**自动:空选择(没表达
232
+ 对比意图,自动切双栏是替用户决定)、已选两条(再塞一条会挤掉你刚选的)、同一条。
233
+ 所以「改一句 prompt → 重跑 → 看差在哪」是**两步**,不是三步。
234
+
201
235
  **中止在飞的 run**:run 跑着的时候输入条上会多一个**「■ 中止」**按钮(没在飞时不显示)。
202
236
  点了之后:
203
237
  - run 在**回合边界**以 `stopReason='aborted'` **正常收尾**,**trace 照常落盘**(面板也会说「已中止」,
@@ -868,17 +902,22 @@ runner.submit(msg.value, {
868
902
  **具体 span**(回合 / 能力调用)的 link,而不是只到 run 粒度:
869
903
 
870
904
  ```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();
905
+ import { Tool, currentTraceparent } from '@migor/agentia';
906
+
907
+ class OrderTools {
908
+ @Tool({
909
+ description: '把工单交给订单服务',
910
+ schema: { type: 'object', properties: { sku: { type: 'string' } }, required: ['sku'], additionalProperties: false },
911
+ })
912
+ async placeOrder(input: { sku: string }) {
913
+ const tp = currentTraceparent(); // 不在 run 内 → undefined(那时不该编一个)
914
+ const res = await fetch(orderSvc, {
915
+ method: 'POST',
916
+ headers: { 'content-type': 'application/json', ...(tp ? { traceparent: tp } : {}) },
917
+ body: JSON.stringify(input),
918
+ });
919
+ return res.json();
920
+ }
882
921
  }
883
922
  ```
884
923
 
@@ -1548,7 +1587,7 @@ const waitForBatch: AgentTool = {
1548
1587
  async run(_input, ctx) {
1549
1588
  const due = await nextBatchWindow(); // 你自己的判据(外部系统回填 / 限流窗口 / 收盘)
1550
1589
  if (due.getTime() > Date.now()) {
1551
- ctx!.deferUntil(due); // 「现在还不是时候,T 之后再问我」
1590
+ ctx?.deferUntil?.(due); // 「现在还不是时候,T 之后再问我」
1552
1591
  return 'deferred'; // 返回值会被丢弃(见整批语义)
1553
1592
  }
1554
1593
  return await fetchReport();
@@ -1667,6 +1706,7 @@ const callable = {
1667
1706
  | 任务进度流的边界(内存 / 跨进程) | `GET /tasks/:id/stream` 的事件缓冲在**跑任务的进程内存**里:每任务最近 500 条(可用 `AsyncRunner` 的 `streamBufferEvents` 调,须为正整数,坏值构造期抛错),超限丢**最旧**并先发一帧 `stream.truncated`;终态流只留最近 16 条。**没跑完**的流另有一道**缓冲配额**(`streamNonTerminalBuffers`,缺省 32 条流;`0` = 不留):超出即从最旧、**没有订阅者**的流开始回收**回放历史** —— 表项与序号都留着,连上来的读者先收一帧 `stream.truncated` 再转实时(**不会**变成 `stream.unavailable`,那是对别的进程说的话);正在被订阅的流不受配额约束(不抽走正在读的数据)。**跨进程**(队列消费者在别的进程)时没有实时流:发一帧 `stream.unavailable` 后**立即收口** —— 任务已终态则补 `task.end`(随后关连接);**非终态**则补一帧 `stream.closed`(流级收尾,不是伪造终态)并关连接,客户端此后应转去轮询 `GET /tasks/:id`。要跨进程实时请用 `onTraceEvent` 把事件转发到宿主自己的总线(Redis Streams / Kafka) |
1668
1707
  | 异步任务的**排队段**边界(内存) | `concurrency` 管「同时在跑几个」,**不管**「跑不上、排在后面」那一段 —— 缺省**排队段无上限**:`submit` 总是收单、`POST /tasks` 总是 202,灌多少就在内存里排多少(每条 = 一条记录 + 一棵悬挂的 `#execute` promise + 一个槽位等待者)。要背压就配 `AsyncRunner({ maxQueued })`:超限 `submit` 抛 `TaskQueueFullError`、HTTP 回 **503 + `Retry-After`**(**只在 `submit` 判** —— 恢复路径推进的是已受理任务,拦下来等于把它搁死在 store 里;幂等键命中的重复提交也不吃 503)。`runner.queued` 是实时读数(与闸同一判据)。⚠️ 进程内拒单**不等于**集群级背压:多副本各算各的,要全局就得上队列 |
1669
1708
  | `traceLimits` 与 `maxEventChars` 各管一头 | `maxEventChars` 管**单个事件正文多长**(既有),`traceLimits.maxEvents` 管**整条 trace 记多少条记录事件**(本版)。两者正交、都「不设 = 不限」;超限即**停止记账**,并在 run 根留**两笔** `trace.truncated`:**起点标记** `{limit}`(断点在这里)+ **收尾摘要** `{droppedEvents, limit}`(丢了多少)—— 两笔**同时**进交付的 trace 与 `onTraceEvent` 增量流(⇒ 按 `seq` 折回仍逐字等于收尾那份;两笔簿记**不占** `maxEvents` 配额)。⚠️ 实测 `maxEventChars: false` + 大出参会让 trace 放大 **13.7×**(`npm run bench:trace` 可复现)—— 先收长度再谈采样,收益顺序比反过来大 |
1709
+ | 回给模型的 `tool_result` **不截断、也没有大小闸** | `maxEventChars` / `traceContent` 管的是**记账侧**(trace 记多长、记不记正文)—— 回给模型的工具结果**永远完整**,框架里没有「单条工具结果超过多大就裁」的那条缝。一次 `cat` 大文件、一次刷屏回显就能让**下一回合**的输入爆掉上下文窗口,以 api 错误收尾(长跑里尤其容易撞)。要设输出上限请在**工具实现内部**做(按行数 / 字节截断并附「已截断」标记)—— 框架不替你兜 |
1670
1710
  | **采样是导出决策,不是记账决策** | 采样在 `sink` 外做(配方见 `docs/observability.md` 2.3):被采样掉的 trace 在框架内**仍然完整记账**,只是没发给下游。所以别拿「有采样」当「可以少记账」;也正因如此,出站 `traceparent` 的 flags 恒 `00`(记录/导出决策发生在收尾之后,运行期不可知——不替下游声明) |
1671
1711
  | 菜单漂移只**标记**、不判失败 | 续跑时未决 tool_use 的工具不在当前菜单 ⇒ 三处信号(`menu.drift` 事件 / 父 span attribute / `console.warn`),run **照常收尾**(那些 tool_use 以 `unknown tool` 回给模型)。**射程如实标注**:判据只看**名字** —— 「工具还在、schema 变了」能被 `tools.menuHash` 比出来,但**不会**在续跑时报 `menu.drift`;**只改 `description`** 连签名都不变(有意:润色文案不算漂移)。严格模式(带具名 error 的收尾)与「结果级字段」记在 spec §11 开放项 |
1672
1712
  | `approve` 只收**待决的** id(多一个 → 400 整批拒) | `decisions` 的键必须都在该任务的 `pendingApprovals` 里(调用方从记录的 `pendingApprovals` 读)。多出任何一个 ⇒ **400 + 整批拒 + 记录一字不动**。不校验的代价(已修,2026-09-28 外部深评):多出来的键会写进 `TaskRecord.approvals`,随每次 `save` 全文重写、且终态也不清 ⇒ 一个**认证调用方**单次请求就能把记录撑大(实证:一次 5000 个无关键 = +419 KB),反复调用可无限叠加 |
@@ -1684,7 +1724,7 @@ const callable = {
1684
1724
  | 观测失败被吞 | sink 抛错不影响 run(观测是辅助动作);但吞之前落一条 `console.warn`(含「trace sink」字样)—— sink 天天挂不再零信号。同理记忆水合/回写失败也不击穿 run |
1685
1725
  | 框架不自动读 .env | 除 `AGENTIA_MODEL`(缺省模型覆盖)与 `OPENAI_API_KEY`(OpenAI 适配器)外,框架自己不去翻环境变量,也不读 `.env`;要读就在启动代码里调 `loadEnvFile()`(脚手架已内置那行),**真实环境变量优先**于文件 |
1686
1726
  | 鉴权只是缝 | 框架**不实现** token / JWT / 签名策略,也不碰凭据 env —— `authenticate` 只承诺「拦在入口、读 body 之前」;策略是宿主或反代的事 |
1687
- | 运行时是 Node | 按 Node ≥ 18 设计与测试(`engines` 写明,CI 在 18/20/22 上守);**未对 Deno / edge 做验证**。`SqliteTaskStore` 需 Node ≥ 22.5(`node:sqlite`),未提供时构造期抛可读报错 |
1727
+ | 运行时是 Node | 按 Node ≥ 18 设计与测试(`engines` 写明;CI 覆盖 18 / 20 / 24 —— `import-floor` 矩阵跑下限、其余 job 跟当前 LTS);**未对 Deno / edge 做验证**。`SqliteTaskStore` 需 Node ≥ 22.5(`node:sqlite`),未提供时构造期抛可读报错 |
1688
1728
  | 停机不由框架触发 | 框架给 `drain()` 但**不订阅** `SIGTERM`/`SIGINT`(不做进程级决策);信号处理是宿主的 |
1689
1729
  | 停机可能切断 SSE | `drain()` 收口时会强制关闭仍开着的 SSE 流,`/run` 的流被收口时其 run 以 `stopReason='aborted'` 收尾(客户端应把断流当作可重试)。**没有 `timeoutMs` 时也收口**(否则等待没有终点,见上);只关旁观者长连(`/tasks/:id/stream`)**不损失工作**,那种收口不影响 `drain()` 的返回值 |
1690
1730
  | 多进程共库:续跑认领靠**租约**,不靠时间(2026-09-28) | `resumePending({ staleAfterMs })` 的 `staleAfterMs` **不是**租约而是**兜底启发式**。`> 0` 时按两档判他进程的记录:① `ownerId` 里的主机名是本机 ⇒ **问那个 pid 还在不在**(`process.kill(pid, 0)`)——**在的绝不抢**(不管记录多老)、**不在的立刻可抢**(崩溃孤儿不等保鲜期);② **判不了**(异主机 / 升级前写下的旧格式 `ownerId` / 自定义串)⇒ 才用 `staleAfterMs`(「记录看起来还新」)。缺省 `0` = 不问他进程,与升级前逐字一致 |
@@ -1740,6 +1780,7 @@ const callable = {
1740
1780
  | Scheduler 调度表不落库 | `every` / `at` 的调度本身只在内存:已 submit 的任务记录能经 `resumePending` 续跑,但「未来某刻再触发」的调度在重启后不存在(远期单发由宿主自己的 cron 驱动)。另:`drain()` 不停 Scheduler —— 停机窗口内到点的 tick 会打一条触发失败日志(无害但吵),介意就 `scheduler.stop()` 先行 |
1741
1781
  | file store 的撕裂写只在启动时自愈 | 写入中途失败(磁盘满等)留下的残行由 `healTail` 在**构造期**修复;同进程内继续 append 会把新记录粘在残行尾部、下次启动时一起丢弃 —— 磁盘满告警后先恢复写入能力再继续依赖它 |
1742
1782
  | 终态落库失败 ⇒ 重启会重跑 | AsyncRunner 终态 `save` 失败**不遮罩主流程**(「不击穿业务」的代价):store 抖动时任务可能永远停在 `running`,重启后 `resumePending` 会重跑一个**实际已成功**(副作用已发生)的任务 —— 所以副作用工具必须自身幂等。**但失败本身不再静默**:`new AsyncRunner(app, { onPersistError })` 会收到 `{ record, error, phase }`(`phase: 'initial' \| 'outcome'`,后者就是这条)。⚠️ 框架**修不了**它(写不进去就是写不进去)—— 出口的职责是让你能对账、让「记录无声丢失」不再是默认行为(与 `createOtlpExporter({ onExportError })` 同因同形) |
1783
+ | 缺省**不裁剪**上下文(长跑每回合重发全量历史 ⇒ 成本随回合数平方增长) | `contextPolicy` 不注入就一个回合都不裁:每回合把**整段历史**重发给模型,于是回合数翻倍、总输入约翻两番。框架给了缝与一份参考实现(`createBudgetPolicy`),但**不替你决定** —— 裁剪有损(截在哪、留什么会影响答案),是宿主的策略。长任务(几十回合以上)请在 `createApp` 里显式注入 `contextPolicy` |
1743
1784
  | `contextPolicy` 不进子循环 | 应用级 `contextPolicy` / `onText` 只对主循环生效:@SubAgent / @Skill 的子运行不做上下文裁剪(长跑子 agent 撞上下文上限会以 api 错误收尾)。预算护栏(`maxTotalTokens` / `maxCostUsd`)正常透传 |
1744
1785
  | 记忆没有删除语义 | `MemoryStore` 只有 load/save:run 内 `ctx.delete` 掉的键回写时不会从 store 移除(下一轮水合会复活)。要真删请直接操作 store 实现 |
1745
1786
  | 同 keys 的并发回写不合并 | CAS(`loadWithRev` + `saveIfRev`)只做到「冲突时**不写 + 出声**」:框架**不合并、不重试**(合并语义是 store 与宿主的决定,配方见 §6.5)。只有 `load` / `save` 的 store 连冲突都**看不见** —— 版本号是唯一依据,没有它就检测不了覆盖;`InMemoryMemoryStore` 的版本粒度是**整店一个计数**(宁可多报冲突,不漏报真实覆盖) |
@@ -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":"AAoCA,MAAM,UAAU,UAAU,CAAC,CAAU;IACnC,OAAO,CACL,OAAO,CAAC,KAAK,QAAQ;QACrB,CAAC,KAAK,IAAI;QACV,OAAQ,CAA2B,CAAC,OAAO,KAAK,QAAQ;QACxD,CAAC,UAAU,IAAK,CAAY,IAAI,UAAU,IAAK,CAAY,IAAI,YAAY,IAAK,CAAY,CAAC,CAC9F,CAAC;AACJ,CAAC;AAED,MAAM,OAAO,SAAS;IACH,QAAQ,GAAG,IAAI,GAAG,EAAmB,CAAC;IACtC,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IACnD,6BAA6B;IACrB,SAAS,GAAY,EAAE,CAAC;IAEhC;;;;;;;OAOG;IACH,QAAQ,CAAC,GAAG,SAAqB;QAC/B,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;YAC1B,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;gBACnB,MAAM,IAAI,KAAK,CAAC,gBAAgB,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACvD,CAAC;YACD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YAChC,6CAA6C;YAC7C,4CAA4C;YAC5C,uCAAuC;YACvC,4BAA4B;YAC5B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;YAC7B,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC;gBAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACzE,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,iDAAiD;IACzC,YAAY,CAAC,IAAW;QAC9B,MAAM,GAAG,GAAG,IAAI,GAAG,EAAS,CAAC;QAC7B,MAAM,KAAK,GAAY,CAAC,IAAI,CAAC,CAAC;QAC9B,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,EAAG,CAAC;YAC3B,KAAK,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAC7C,IAAI,KAAK,KAAK,IAAI,IAAI,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC;oBAAE,SAAS;gBAC/C,MAAM,IAAI,GAAI,OAA8B,CAAC,IAAI,CAAC;gBAClD,IAAI,IAAI,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;oBACxB,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;oBACf,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACpB,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,GAAG,CAAC,KAAY;QACd,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC;IAED,OAAO,CAAc,KAAY;QAC/B,6DAA6D;QAC7D,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAM,CAAC;QAE7D,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC9C,IAAI,OAAO,KAAK,CAAC,CAAC,EAAE,CAAC;YACnB,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACrE,MAAM,IAAI,KAAK,CAAC,aAAa,KAAK,EAAE,CAAC,CAAC;QACxC,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACzC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,mBAAmB,KAAK,GAAG,CAAC,CAAC;QAC/C,CAAC;QAED,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3B,IAAI,KAAc,CAAC;QACnB,IAAI,CAAC;YACH,IAAI,UAAU,IAAI,OAAO,EAAE,CAAC;gBAC1B,KAAK,GAAG,OAAO,CAAC,QAAQ,CAAC;YAC3B,CAAC;iBAAM,IAAI,UAAU,IAAI,OAAO,EAAE,CAAC;gBACjC,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC9D,KAAK,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAI,IAAgB,CAAC,CAAC;YACrD,CAAC;iBAAM,CAAC;gBACN,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC9D,uDAAuD;gBACvD,KAAK,GAAI,OAAO,CAAC,UAA2C,CAAC,GAAG,IAAI,CAAC,CAAC;YACxE,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC;QACvB,CAAC;QAED,6DAA6D;QAC7D,sDAAsD;QACtD,sDAAsD;QACtD,EAAE;QACF,kEAAkE;QAClE,4CAA4C;QAC5C,2DAA2D;QAC3D,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,UAAU,CAAC,EAAE,CAAC;YACjF,MAAM,IAAI,GAAI,KAA4B,CAAC,IAAI,CAAC;YAChD,IAAI,OAAO,IAAI,KAAK,UAAU,EAAE,CAAC;gBAC/B,MAAM,IAAI,SAAS,CACjB,aAAa,KAAK,qCAAqC;oBACrD,qDAAqD;oBACrD,6EAA6E,CAChF,CAAC;YACJ,CAAC;QACH,CAAC;QAED,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAC7B,OAAO,KAAU,CAAC;IACpB,CAAC;IAED,WAAW;IACX,UAAU;QACR,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACnC,CAAC;CACF"}
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":"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"}
@@ -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.1";
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.1';
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';