@migor/agentia 0.9.3 → 0.9.5
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.
- package/CHANGELOG.md +779 -1
- package/README.md +1 -1
- package/dist/AGENTS.md +320 -37
- package/dist/container/container.js +15 -0
- package/dist/container/container.js.map +1 -1
- package/dist/core/limits.d.ts +121 -17
- package/dist/core/limits.js +133 -16
- package/dist/core/limits.js.map +1 -1
- package/dist/core/line-framing.d.ts +36 -0
- package/dist/core/line-framing.js +39 -0
- package/dist/core/line-framing.js.map +1 -0
- package/dist/core/message.d.ts +11 -0
- package/dist/core/run.d.ts +68 -4
- package/dist/core/run.js +27 -1
- package/dist/core/run.js.map +1 -1
- package/dist/core/schema.js +59 -8
- package/dist/core/schema.js.map +1 -1
- package/dist/core/timeout.d.ts +43 -1
- package/dist/core/timeout.js +76 -3
- package/dist/core/timeout.js.map +1 -1
- package/dist/core/tool.d.ts +39 -1
- package/dist/core/trace.d.ts +40 -1
- package/dist/core/trace.js +26 -0
- package/dist/core/trace.js.map +1 -1
- package/dist/engine/defer.d.ts +49 -0
- package/dist/engine/defer.js +72 -0
- package/dist/engine/defer.js.map +1 -0
- package/dist/engine/forwarded.d.ts +14 -5
- package/dist/engine/forwarded.js +4 -1
- package/dist/engine/forwarded.js.map +1 -1
- package/dist/engine/loop-result.d.ts +65 -7
- package/dist/engine/loop-result.js +24 -5
- package/dist/engine/loop-result.js.map +1 -1
- package/dist/engine/loop.d.ts +2 -0
- package/dist/engine/loop.js +157 -17
- package/dist/engine/loop.js.map +1 -1
- package/dist/engine/mcp-server-http.d.ts +2 -0
- package/dist/engine/mcp-server-http.js +215 -0
- package/dist/engine/mcp-server-http.js.map +1 -0
- package/dist/engine/mcp-server-stdio.d.ts +15 -0
- package/dist/engine/mcp-server-stdio.js +106 -0
- package/dist/engine/mcp-server-stdio.js.map +1 -0
- package/dist/engine/mcp-server.d.ts +134 -0
- package/dist/engine/mcp-server.js +232 -0
- package/dist/engine/mcp-server.js.map +1 -0
- package/dist/engine/menu-drift.d.ts +34 -0
- package/dist/engine/menu-drift.js +81 -0
- package/dist/engine/menu-drift.js.map +1 -0
- package/dist/engine/policy.d.ts +2 -1
- package/dist/engine/policy.js +7 -1
- package/dist/engine/policy.js.map +1 -1
- package/dist/engine/replay.d.ts +2 -1
- package/dist/engine/replay.js +3 -2
- package/dist/engine/replay.js.map +1 -1
- package/dist/engine/resume-input.d.ts +10 -0
- package/dist/engine/resume-input.js +11 -0
- package/dist/engine/resume-input.js.map +1 -1
- package/dist/engine/run-config.d.ts +26 -2
- package/dist/engine/run-config.js +60 -0
- package/dist/engine/run-config.js.map +1 -1
- package/dist/engine/spec.d.ts +33 -4
- package/dist/engine/spec.js.map +1 -1
- package/dist/engine/tool-context.d.ts +10 -5
- package/dist/engine/tool-context.js +5 -1
- package/dist/engine/tool-context.js.map +1 -1
- package/dist/engine/tracer.d.ts +43 -5
- package/dist/engine/tracer.js +73 -10
- package/dist/engine/tracer.js.map +1 -1
- package/dist/engine/trimming.js +151 -17
- package/dist/engine/trimming.js.map +1 -1
- package/dist/engine/turn.d.ts +60 -3
- package/dist/engine/turn.js +180 -71
- package/dist/engine/turn.js.map +1 -1
- package/dist/engine/types.d.ts +89 -3
- package/dist/engine/types.js +38 -2
- package/dist/engine/types.js.map +1 -1
- package/dist/engine/usage.d.ts +22 -3
- package/dist/engine/usage.js +47 -6
- package/dist/engine/usage.js.map +1 -1
- package/dist/eval/export.d.ts +48 -0
- package/dist/eval/export.js +146 -0
- package/dist/eval/export.js.map +1 -0
- package/dist/eval/harvest.js +7 -2
- package/dist/eval/harvest.js.map +1 -1
- package/dist/index.d.ts +8 -5
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/integrations/adapter-options.d.ts +22 -0
- package/dist/integrations/adapter-options.js +51 -0
- package/dist/integrations/adapter-options.js.map +1 -1
- package/dist/integrations/anthropic.d.ts +4 -2
- package/dist/integrations/anthropic.js +27 -27
- package/dist/integrations/anthropic.js.map +1 -1
- package/dist/integrations/file-sink.d.ts +20 -0
- package/dist/integrations/file-sink.js +27 -0
- package/dist/integrations/file-sink.js.map +1 -0
- package/dist/integrations/mcp-http.d.ts +3 -3
- package/dist/integrations/mcp-http.js +1 -1
- package/dist/integrations/mcp-http.js.map +1 -1
- package/dist/integrations/mcp-protocol.d.ts +115 -0
- package/dist/integrations/mcp-protocol.js +69 -0
- package/dist/integrations/mcp-protocol.js.map +1 -0
- package/dist/integrations/mcp-stdio.d.ts +3 -3
- package/dist/integrations/mcp-stdio.js +17 -3
- package/dist/integrations/mcp-stdio.js.map +1 -1
- package/dist/integrations/mcp.d.ts +6 -95
- package/dist/integrations/mcp.js +24 -66
- package/dist/integrations/mcp.js.map +1 -1
- package/dist/integrations/metrics-otlp.js +52 -7
- package/dist/integrations/metrics-otlp.js.map +1 -1
- package/dist/integrations/metrics-render.d.ts +15 -0
- package/dist/integrations/metrics-render.js +79 -2
- package/dist/integrations/metrics-render.js.map +1 -1
- package/dist/integrations/metrics-state.d.ts +99 -1
- package/dist/integrations/metrics-state.js +128 -4
- package/dist/integrations/metrics-state.js.map +1 -1
- package/dist/integrations/metrics.d.ts +62 -6
- package/dist/integrations/metrics.js +49 -8
- package/dist/integrations/metrics.js.map +1 -1
- package/dist/integrations/openai.js +9 -31
- package/dist/integrations/openai.js.map +1 -1
- package/dist/integrations/otlp.d.ts +3 -3
- package/dist/integrations/report.d.ts +7 -0
- package/dist/integrations/report.js +20 -1
- package/dist/integrations/report.js.map +1 -1
- package/dist/runtime/memory.d.ts +61 -1
- package/dist/runtime/memory.js +47 -7
- package/dist/runtime/memory.js.map +1 -1
- package/dist/runtime/run.d.ts +12 -2
- package/dist/runtime/run.js +112 -15
- package/dist/runtime/run.js.map +1 -1
- package/dist/store/fsStore.js +10 -4
- package/dist/store/fsStore.js.map +1 -1
- package/dist/store/record.d.ts +41 -0
- package/dist/store/record.js +54 -0
- package/dist/store/record.js.map +1 -0
- package/dist/store/redisStore.d.ts +3 -1
- package/dist/store/redisStore.js +21 -7
- package/dist/store/redisStore.js.map +1 -1
- package/dist/store/sqliteStore.d.ts +23 -0
- package/dist/store/sqliteStore.js +103 -8
- package/dist/store/sqliteStore.js.map +1 -1
- package/dist/store/store.d.ts +65 -2
- package/dist/store/store.js +5 -2
- package/dist/store/store.js.map +1 -1
- package/dist/toolkit/capability-cycles.d.ts +50 -0
- package/dist/toolkit/capability-cycles.js +88 -0
- package/dist/toolkit/capability-cycles.js.map +1 -0
- package/dist/toolkit/capability-slice.d.ts +86 -0
- package/dist/toolkit/capability-slice.js +98 -0
- package/dist/toolkit/capability-slice.js.map +1 -0
- package/dist/toolkit/module.d.ts +7 -1
- package/dist/toolkit/module.js +65 -32
- package/dist/toolkit/module.js.map +1 -1
- package/dist/transport/approval-policy.d.ts +1 -1
- package/dist/transport/approval-policy.js +8 -4
- package/dist/transport/approval-policy.js.map +1 -1
- package/dist/transport/async.d.ts +197 -18
- package/dist/transport/async.js +871 -85
- package/dist/transport/async.js.map +1 -1
- package/dist/transport/drain-gate.d.ts +6 -2
- package/dist/transport/drain-gate.js +14 -2
- package/dist/transport/drain-gate.js.map +1 -1
- package/dist/transport/http-endpoints.d.ts +151 -0
- package/dist/transport/http-endpoints.js +480 -0
- package/dist/transport/http-endpoints.js.map +1 -0
- package/dist/transport/http-io.d.ts +66 -0
- package/dist/transport/http-io.js +145 -0
- package/dist/transport/http-io.js.map +1 -0
- package/dist/transport/http-route.d.ts +10 -3
- package/dist/transport/http-route.js +18 -3
- package/dist/transport/http-route.js.map +1 -1
- package/dist/transport/http-shapes.d.ts +11 -0
- package/dist/transport/http-shapes.js +31 -1
- package/dist/transport/http-shapes.js.map +1 -1
- package/dist/transport/http.d.ts +22 -35
- package/dist/transport/http.js +110 -467
- package/dist/transport/http.js.map +1 -1
- package/dist/transport/owner-id.d.ts +29 -0
- package/dist/transport/owner-id.js +46 -0
- package/dist/transport/owner-id.js.map +1 -0
- package/dist/transport/owner-liveness.d.ts +8 -0
- package/dist/transport/owner-liveness.js +56 -0
- package/dist/transport/owner-liveness.js.map +1 -0
- package/dist/transport/resume-policy.d.ts +48 -10
- package/dist/transport/resume-policy.js +25 -10
- package/dist/transport/resume-policy.js.map +1 -1
- package/dist/transport/scheduler.js +23 -24
- package/dist/transport/scheduler.js.map +1 -1
- package/dist/transport/slot-pool.d.ts +9 -1
- package/dist/transport/slot-pool.js +9 -1
- package/dist/transport/slot-pool.js.map +1 -1
- package/dist/transport/sse.js +8 -0
- package/dist/transport/sse.js.map +1 -1
- package/dist/transport/task-events.d.ts +34 -3
- package/dist/transport/task-events.js +86 -5
- package/dist/transport/task-events.js.map +1 -1
- package/dist/transport/wake-policy.d.ts +50 -0
- package/dist/transport/wake-policy.js +43 -0
- package/dist/transport/wake-policy.js.map +1 -0
- package/package.json +6 -4
|
@@ -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"}
|
package/dist/core/message.d.ts
CHANGED
|
@@ -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 {
|
package/dist/core/run.d.ts
CHANGED
|
@@ -6,11 +6,65 @@ import type { SpanError } from './trace.js';
|
|
|
6
6
|
* store / transport / runtime 都要读它,下沉 core 后三者都不必再向上引 runtime
|
|
7
7
|
* (此前 `store → runtime` 是未声明的兄弟层依赖,见 AGENTS.md 分层约定)。
|
|
8
8
|
*
|
|
9
|
-
* `
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* `suspended`:run **挂起了** —— **非终态**(awaitTask 继续轮询)、不占并发槽(执行段落已
|
|
10
|
+
* 收尾释放)、`resumePending` 不捡走(它不是孤儿,崩溃续跑语义不适用于「在等」);
|
|
11
|
+
* 消息历史(含未决 tool_use)与待决清单随 TaskRecord 落库。
|
|
12
|
+
*
|
|
13
|
+
* 「挂起」是**一个状态 + 一个原因**(2026-09-28 ①:从前 `awaiting_approval` 一个状态兼职
|
|
14
|
+
* 两件事):状态只说「它在等」,等什么由 `SuspendedReason` 说 —— 判据(审批超时 / 能否
|
|
15
|
+
* 审批)落在**原因**上,于是「时间挂起被审批超时提前叫醒」这类误伤在类型上就写不出来。
|
|
16
|
+
*/
|
|
17
|
+
export type RunStatus = 'queued' | 'running' | 'suspended' | 'succeeded' | 'failed'
|
|
18
|
+
/**
|
|
19
|
+
* 被**取消**(宿主调 `runner.cancel`)—— 终态,但与 `failed` **分开**:
|
|
20
|
+
* 取消不是失败(引擎侧 `abortedResult` 的注释早就这么写),运维读数不该把
|
|
21
|
+
* 「人按的」与「跑挂的」混成一类(`GROUP BY status` 是第一读者)。
|
|
22
|
+
* 机制上是 `abort signal`(与 `runTimeoutMs` 同一条),差别在**意图** ——
|
|
23
|
+
* 状态按意图落:超时仍是 `failed`。
|
|
24
|
+
*
|
|
25
|
+
* ⚠️ **可达性如实(外部深评 K3)**:这个成员**由宿主落**(`TaskRecord.status`),
|
|
26
|
+
* 进程内 `Run` **到不了它** —— 引擎侧的取消表现为 `stopReason: 'aborted'`,
|
|
27
|
+
* `Run.finish()` 按 `isSuccessStopReason` 判 ⇒ 记 `failed`(带结构化
|
|
28
|
+
* `error.type: 'aborted'`)。同一份联合类型有**两个消费者、可达集不同**,这是有意的:
|
|
29
|
+
* `TaskRecord` 是运维读数(「人按的」与「跑挂的」必须分得开,`GROUP BY status` 是第一读者),
|
|
30
|
+
* 而 `Run` 是进程内状态机(只关心跑没跑完)。要加状态时**先想清是给谁的**。
|
|
31
|
+
*/
|
|
32
|
+
| 'cancelled';
|
|
33
|
+
/**
|
|
34
|
+
* 挂起原因 —— 「等人工审批」与「等一个时刻」是两种挂起,判据必须分开:
|
|
35
|
+
* approval —— 等人工决定(HITL):`approve()` 能叫醒它,`approvalTimeoutMs` 管它;
|
|
36
|
+
* timer —— 等一个时刻(durable timer):只有到点能叫醒它。
|
|
37
|
+
* ⚠️ 拿 timer 挂起去撞 `approvalTimeoutMs` = **提前叫醒一条在睡的 run** ——
|
|
38
|
+
* 那两条闸(`approvalExpired` / `approve` 的 409)因此都 gate 在原因上,不 gate 在状态上。
|
|
12
39
|
*/
|
|
13
|
-
export type
|
|
40
|
+
export type SuspendedReason = 'approval' | 'timer';
|
|
41
|
+
/** 该状态是否已到终态(不会再变)。判定只此一处 —— 别在调用点重写。 */
|
|
42
|
+
export declare function isTerminalStatus(status: RunStatus): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* run 事件(2026-09-28 ⑥,run 事件投入口):外部系统投给一条**挂起** run 的一条输入
|
|
45
|
+
* (`AsyncRunner.signalTask` / `POST /tasks/:id/events`;审批是它的特例)。
|
|
46
|
+
*
|
|
47
|
+
* 白名单形状(设计稿 B1):**全是字符串** —— 外部永远不能构造消息块,投毒面只到
|
|
48
|
+
* 「内容注入」为止(任意 `MessageParam` 透传 = 把伪造 assistant / tool_use 块的能力
|
|
49
|
+
* 交给外部,那是协议注入,明确不做)。续跑段由引擎把它渲染成**一条 user 文本消息**
|
|
50
|
+
* 注入消息流(见 `engine/resume-input.ts` 的 `renderTaskEvent` 与 `engine/loop.ts`)。
|
|
51
|
+
*
|
|
52
|
+
* 与 `transport/task-events.ts` 的 `TaskStreamEvent` 不是一回事:那个是**记账事件流**
|
|
53
|
+
* 的一帧(出站,`GET /tasks/:id/stream`),这个是**入站**输入。
|
|
54
|
+
*/
|
|
55
|
+
export interface TaskEvent {
|
|
56
|
+
/**
|
|
57
|
+
* 幂等键(可选):给了就按它去重 —— 同一任务重复投递同一个 eventId ⇒ 409
|
|
58
|
+
* (与 cancel 的「已终态」同款:说出来,不静默)。去重簿记随 TaskRecord 落库
|
|
59
|
+
* (`deliveredEventIds`,有界 FIFO),跨进程/重启不丢。
|
|
60
|
+
* **不给则没有恰好一次**:重复投递 = 重复进消息历史(如实文档化,不假装)。
|
|
61
|
+
*/
|
|
62
|
+
eventId?: string | undefined;
|
|
63
|
+
/** 事件类型(非空字符串,如 `payment.settled`)—— 进渲染文本与 trace 留痕 */
|
|
64
|
+
type: string;
|
|
65
|
+
/** 事件正文(字符串)。HTTP 侧的字节上限与 `maxBodyBytes` 同口径(整个 body 那一道闸) */
|
|
66
|
+
payload: string;
|
|
67
|
+
}
|
|
14
68
|
/**
|
|
15
69
|
* run 的运行记录(供持久化 / 读取)。字段**必填但可为 undefined**:它们是框架在 run
|
|
16
70
|
* 生命周期各阶段**总是写进对象**的状态(`toMeta()` 一次构造全量),「缺省」在这里不是
|
|
@@ -26,4 +80,14 @@ export interface RunMeta {
|
|
|
26
80
|
startedAt: number | undefined;
|
|
27
81
|
finishedAt: number | undefined;
|
|
28
82
|
error: SpanError | undefined;
|
|
83
|
+
/**
|
|
84
|
+
* 挂起原因(仅 `status === 'suspended'` 时非空)—— 「字段在场」约定同 `error`:
|
|
85
|
+
* 它是 run 生命周期里**总会写进对象**的状态位,缺省在这里没有语义。
|
|
86
|
+
*/
|
|
87
|
+
suspendedReason: SuspendedReason | undefined;
|
|
88
|
+
/**
|
|
89
|
+
* 时间挂起的目标时刻(epoch ms,仅 `suspendedReason === 'timer'` 时非空;字段在场约定同上)
|
|
90
|
+
* —— 「这条 run 什么时候该醒」是运行记录的一部分,与「在等什么」并列。
|
|
91
|
+
*/
|
|
92
|
+
wakeAt: number | undefined;
|
|
29
93
|
}
|
package/dist/core/run.js
CHANGED
|
@@ -1,2 +1,28 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* `RunStatus` → 「是不是终态」的**唯一真源**(2026-09-28 外部深评 S1)。
|
|
3
|
+
*
|
|
4
|
+
* 为什么要有它:这个判定此前**散在三处各写一遍**,而且写法不同——
|
|
5
|
+
* `transport/async.ts` 的 `isTerminalTask` 是 `status !== 'queued' && …`(负向枚举
|
|
6
|
+
* 非终态),`store/redisStore.ts` 施加 TTL 时压根判都不判(于是**挂起中也照样过期**:
|
|
7
|
+
* 一条在等审批的任务,TTL 一到记录就没了,「批了它」的决定无家可归 —— 与
|
|
8
|
+
* `InMemoryTaskStore.evict` 里那句「suspended 不可淘汰」正好相反)。谁都没错,
|
|
9
|
+
* 合起来是错的:**同一事实的两份读数**。
|
|
10
|
+
*
|
|
11
|
+
* 形状取**穷尽的分类表**而不是负向枚举:往 `RunStatus` 加成员而不在这里表态 ⇒
|
|
12
|
+
* `tsc` 报缺属性(`TS2741`,与 `stop-reason.ts` 的分类表同款)。负向枚举会让新成员
|
|
13
|
+
* **静默变成「终态」**——那正是「挂起中的任务被当成跑完了」那类 bug 的来源。
|
|
14
|
+
*/
|
|
15
|
+
const TERMINAL_STATUS = {
|
|
16
|
+
queued: false,
|
|
17
|
+
running: false,
|
|
18
|
+
// 挂起**不是**终态:批了(或睡到点)它会接着跑
|
|
19
|
+
suspended: false,
|
|
20
|
+
succeeded: true,
|
|
21
|
+
failed: true,
|
|
22
|
+
cancelled: true,
|
|
23
|
+
};
|
|
24
|
+
/** 该状态是否已到终态(不会再变)。判定只此一处 —— 别在调用点重写。 */
|
|
25
|
+
export function isTerminalStatus(status) {
|
|
26
|
+
return TERMINAL_STATUS[status];
|
|
27
|
+
}
|
|
2
28
|
//# sourceMappingURL=run.js.map
|
package/dist/core/run.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/core/run.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/core/run.ts"],"names":[],"mappings":"AAgDA;;;;;;;;;;;;;GAaG;AACH,MAAM,eAAe,GAA+B;IAClD,MAAM,EAAE,KAAK;IACb,OAAO,EAAE,KAAK;IACd,2BAA2B;IAC3B,SAAS,EAAE,KAAK;IAChB,SAAS,EAAE,IAAI;IACf,MAAM,EAAE,IAAI;IACZ,SAAS,EAAE,IAAI;CAChB,CAAC;AAEF,yCAAyC;AACzC,MAAM,UAAU,gBAAgB,CAAC,MAAiB;IAChD,OAAO,eAAe,CAAC,MAAM,CAAC,CAAC;AACjC,CAAC"}
|
package/dist/core/schema.js
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
import { stringifySafe } from './json.js';
|
|
2
|
+
/**
|
|
3
|
+
* `check()` 的**递归深度背板**(2026-09-28 外部深评 C6 收口)。
|
|
4
|
+
*
|
|
5
|
+
* 这道闸只管一件事:**别让递归钻到 V8 的栈溢出**(`RangeError: Maximum call stack size exceeded`
|
|
6
|
+
* 的症状没有辨识度 —— 调用方看到的是「校验不了」,而不是「这个 schema 有问题」)。
|
|
7
|
+
* 环本身由 `assertNoSchemaCycle()`(前置、与值深度无关)具名抓出来;这里只兜「合法但深到失控」
|
|
8
|
+
* 那种形态(要手搓几千层嵌套 schema 才够,属于事故级输入)。
|
|
9
|
+
*
|
|
10
|
+
* 256 的判据:真实 tool schema 的深度是**个位数**,256 是它的两个数量级以上 ⇒ 不误伤;
|
|
11
|
+
* 而 V8 的栈在几千帧量级 ⇒ 256 足够早地拦住。**数量有界不是用户旋钮**,不进 `core/limits.ts`
|
|
12
|
+
* 的 0 语义表(与 `MAX_DELIVERED_EVENT_IDS` / `MAX_PENDING_EVENTS` 同档)。
|
|
13
|
+
*/
|
|
14
|
+
const MAX_SCHEMA_DEPTH = 256;
|
|
2
15
|
/**
|
|
3
16
|
* Agentia —— 最小 JSON Schema 校验子集(v1 裸 JSON Schema;zod 为可选外挂,
|
|
4
17
|
* 见 toolkit/zod.ts —— schema 上挂 __zodValidate 时先走它,core 本身不依赖 zod)。
|
|
@@ -21,18 +34,56 @@ export function validateJsonSchema(schema, input) {
|
|
|
21
34
|
if (err)
|
|
22
35
|
return `$: ${err}`;
|
|
23
36
|
}
|
|
24
|
-
|
|
37
|
+
assertNoSchemaCycle(schema);
|
|
38
|
+
return check(schema, input, '$', 0);
|
|
25
39
|
}
|
|
26
|
-
|
|
40
|
+
/**
|
|
41
|
+
* **前置环检测**:`properties` / `items` / `additionalProperties` 这几条边里有环 ⇒ 具名报错。
|
|
42
|
+
*
|
|
43
|
+
* 为什么不是「只靠深度闸」:递归深度 = min(schema 深度, 值深度),所以**环 + 浅值**根本不会爆栈
|
|
44
|
+
* (照常校验通过)—— 那是作者的一个合法但可疑的写法,不该被当成「输入非法」。真正要防的是
|
|
45
|
+
* 环**配深值**时钻进栈溢出,而那件事用深度闸只能兜住症状;这里直接点出病根(哪条路径上是环)。
|
|
46
|
+
*
|
|
47
|
+
* 用**祖先集**而不是全局 visited:共享子树(DAG,`{a: s, b: s}` 这种)是合法写法,不能报环。
|
|
48
|
+
*/
|
|
49
|
+
function assertNoSchemaCycle(root) {
|
|
50
|
+
const ancestors = new Set();
|
|
51
|
+
const walk = (schema, path) => {
|
|
52
|
+
if (typeof schema !== 'object' || schema === null || Array.isArray(schema))
|
|
53
|
+
return;
|
|
54
|
+
if (ancestors.has(schema)) {
|
|
55
|
+
throw new Error(`schema 里有**环**(循环引用):${path} 又指回了它自己的祖先。` +
|
|
56
|
+
'这会按值深度一路递归下去 —— 深值就是 RangeError: Maximum call stack size exceeded。' +
|
|
57
|
+
'修法:把自引用那段拆成不带环的子 schema,或改成显式的深度上限。');
|
|
58
|
+
}
|
|
59
|
+
ancestors.add(schema);
|
|
60
|
+
const s = schema;
|
|
61
|
+
for (const [key, sub] of Object.entries(s.properties ?? {})) {
|
|
62
|
+
walk(sub, `${path}.properties.${key}`);
|
|
63
|
+
}
|
|
64
|
+
if (s.items !== undefined)
|
|
65
|
+
walk(s.items, `${path}.items`);
|
|
66
|
+
if (typeof s.additionalProperties === 'object') {
|
|
67
|
+
walk(s.additionalProperties, `${path}.additionalProperties`);
|
|
68
|
+
}
|
|
69
|
+
ancestors.delete(schema);
|
|
70
|
+
};
|
|
71
|
+
walk(root, '$');
|
|
72
|
+
}
|
|
73
|
+
function check(schema, value, path, depth = 0) {
|
|
74
|
+
if (depth > MAX_SCHEMA_DEPTH) {
|
|
75
|
+
throw new Error(`schema 递归超过 ${MAX_SCHEMA_DEPTH} 层(路径 ${path})—— 校验在这里中止。` +
|
|
76
|
+
'(环由 assertNoSchemaCycle 前置抓;这里兜的是「合法但深到失控」的形态。)');
|
|
77
|
+
}
|
|
27
78
|
const en = schema.enum;
|
|
28
79
|
if (Array.isArray(en) && !en.some((v) => deepEqual(v, value))) {
|
|
29
80
|
return `${path}: 值不在 enum 允许范围内(得到 ${preview(value)})`;
|
|
30
81
|
}
|
|
31
82
|
switch (schema.type) {
|
|
32
83
|
case 'object':
|
|
33
|
-
return checkObject(schema, value, path);
|
|
84
|
+
return checkObject(schema, value, path, depth);
|
|
34
85
|
case 'array':
|
|
35
|
-
return checkArray(schema, value, path);
|
|
86
|
+
return checkArray(schema, value, path, depth);
|
|
36
87
|
case 'string':
|
|
37
88
|
return typeof value === 'string' ? null : `${path}: 期望 string,得到 ${typeOf(value)}`;
|
|
38
89
|
case 'number':
|
|
@@ -51,7 +102,7 @@ function check(schema, value, path) {
|
|
|
51
102
|
return null; // 未声明/未覆盖的 type 放行
|
|
52
103
|
}
|
|
53
104
|
}
|
|
54
|
-
function checkObject(schema, value, path) {
|
|
105
|
+
function checkObject(schema, value, path, depth) {
|
|
55
106
|
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
56
107
|
return `${path}: 期望 object,得到 ${typeOf(value)}`;
|
|
57
108
|
}
|
|
@@ -64,7 +115,7 @@ function checkObject(schema, value, path) {
|
|
|
64
115
|
const properties = schema.properties ?? {};
|
|
65
116
|
for (const [key, sub] of Object.entries(properties)) {
|
|
66
117
|
if (Object.hasOwn(obj, key)) {
|
|
67
|
-
const err = check(sub, obj[key], `${path}.${key}
|
|
118
|
+
const err = check(sub, obj[key], `${path}.${key}`, depth + 1);
|
|
68
119
|
if (err)
|
|
69
120
|
return err;
|
|
70
121
|
}
|
|
@@ -78,13 +129,13 @@ function checkObject(schema, value, path) {
|
|
|
78
129
|
}
|
|
79
130
|
return null;
|
|
80
131
|
}
|
|
81
|
-
function checkArray(schema, value, path) {
|
|
132
|
+
function checkArray(schema, value, path, depth) {
|
|
82
133
|
if (!Array.isArray(value))
|
|
83
134
|
return `${path}: 期望 array,得到 ${typeOf(value)}`;
|
|
84
135
|
const items = schema.items;
|
|
85
136
|
if (items && typeof items === 'object' && !Array.isArray(items)) {
|
|
86
137
|
for (let i = 0; i < value.length; i++) {
|
|
87
|
-
const err = check(items, value[i], `${path}[${i}]
|
|
138
|
+
const err = check(items, value[i], `${path}[${i}]`, depth + 1);
|
|
88
139
|
if (err)
|
|
89
140
|
return err;
|
|
90
141
|
}
|
package/dist/core/schema.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/core/schema.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE1C;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAkB,EAAE,KAAc;IACnE,0DAA0D;IAC1D,uEAAuE;IACvE,MAAM,EAAE,GAAI,MAAkC,CAAC,aAAa,CAAC;IAC7D,IAAI,OAAO,EAAE,KAAK,UAAU,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAI,EAAwC,CAAC,KAAK,CAAC,CAAC;QAC7D,IAAI,GAAG;YAAE,OAAO,MAAM,GAAG,EAAE,CAAC;IAC9B,CAAC;IACD,OAAO,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/core/schema.ts"],"names":[],"mappings":"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"}
|
package/dist/core/timeout.d.ts
CHANGED
|
@@ -6,10 +6,52 @@
|
|
|
6
6
|
* 2026-09-14 的「硬保证」收紧只落进了 `engine/concurrency.ts`,桥那一份继续用竞速判定 ——
|
|
7
7
|
* 同一个承诺两套实现,于是同一个事件在 trace 里能有两种账(见 `docs/spec.md` §10 2026-09-17 ①)。
|
|
8
8
|
* `integrations` 只能依赖 core(`tests/architecture/layering.test.ts` 强制),
|
|
9
|
-
* 所以单源的落点是 core。core
|
|
9
|
+
* 所以单源的落点是 core。core 是叶子:本文件只 import `limits.js`(**纯数据表,零 import**)
|
|
10
|
+
* —— 2026-09-28 起它要 `zeroClauseOf`:两处 `assertTimerDelay` 的文案必须与 limits 表同源
|
|
11
|
+
* (外部深评 C3/C4 的「单源断了一头」)。叶子性没变:数据表不引入任何运行期依赖,也不成环。
|
|
10
12
|
*/
|
|
11
13
|
/** 超时哨兵:区分「超时」与「工具恰好返回了 undefined」 */
|
|
12
14
|
export declare const TIMED_OUT: unique symbol;
|
|
15
|
+
/**
|
|
16
|
+
* Node 定时器延迟的**物理上限**:`2^31-1` ms(约 **24.86 天**)。
|
|
17
|
+
*
|
|
18
|
+
* 为什么它必须是单源(而不是各调用点各写一份):超过这个值的延迟**不会「等那么久」** ——
|
|
19
|
+
* Node 只会在 stderr 留一行 `TimeoutOverflowWarning`(`Timeout duration was set to 1`),
|
|
20
|
+
* 然后把延迟**钳到 1ms**。于是「30 天后触发」变成「立刻触发」、「退避到限流窗口之后」
|
|
21
|
+
* 变成「热重试打风暴」。实测(Node 22):`delay = 2^31` 在 200ms 内触发并打警告,
|
|
22
|
+
* 而 `delay = 2^31-1`(边界内)如期不触发。
|
|
23
|
+
*
|
|
24
|
+
* 本仓此前的处置是**每个旋钮各自设防**(`transport/scheduler.ts` 的 every/at 各一条,
|
|
25
|
+
* 2026-09-19 复审加的),而「派生出来的等待」没有 —— 那些正是漏网的部分:
|
|
26
|
+
* `withTimeout`(引擎 `toolTimeoutMs`)、`interruptibleSleep`(退避)、
|
|
27
|
+
* `DrainGate.waitForIdle`(drain 预算)、`#raceTimeout`(`runTimeoutMs`)、
|
|
28
|
+
* `composeSignal`(client `timeout`)五处都直喂 `setTimeout` 且只查「非负有限」。
|
|
29
|
+
* 单源收在这里(见 `docs/spec.md` §10 2026-09-28 ⑪)。
|
|
30
|
+
*/
|
|
31
|
+
export declare const MAX_TIMER_DELAY_MS = 2147483647;
|
|
32
|
+
/**
|
|
33
|
+
* 校验一个**将要喂给 `setTimeout` 的延迟**:`NaN` 与超过 [MAX_TIMER_DELAY_MS] 一律抛。
|
|
34
|
+
*
|
|
35
|
+
* 「拒绝」而不是「钳制」是刻意的 —— 钳制正是 Node 现在做的事(钳到 1ms),
|
|
36
|
+
* 它把「远期」静默变成「立即」,是本条要消灭的那种失败模式。照 `Scheduler.at`
|
|
37
|
+
* 的既有先例:超上限直接抛,让使用者在配置处就看见(远期等待请拆成多次自检)。
|
|
38
|
+
*
|
|
39
|
+
* ⚠️ **非正数不在此列**:`0` / 负数在调用点各有合法语义(「不设超时」/「一直等」/「不睡」),
|
|
40
|
+
* 各按 `core/limits.ts` 的表读 —— 本函数只挡「物理上兑现不了」的值。
|
|
41
|
+
* ⚠️ `NaN` 单独判并**必须排在调用点的「非正」早返回之前**:`!(NaN > 0)` 与 `NaN <= 0`
|
|
42
|
+
* 都不成立或都成立得不一致,不先挡就会被静默读成另一种语义(见 `withTimeout` 处的注释)。
|
|
43
|
+
*/
|
|
44
|
+
export declare function assertTimerDelay(ms: number, where: string): number;
|
|
45
|
+
/**
|
|
46
|
+
* 把**外部数据**得出的延迟夹进可兑现区间(`retry-after` 等)。
|
|
47
|
+
*
|
|
48
|
+
* 与 [assertTimerDelay] 的分工是刻意的、别合并:
|
|
49
|
+
* - 那个「**拒绝**」使用者配出来的坏值 —— 配置错误必须响亮,否则修法会被写歪;
|
|
50
|
+
* - 这个「**夹**」上游回给我们的值 —— `Retry-After: 99999999` 不是使用者的错,
|
|
51
|
+
* 为它抛错会把一次 429 升级成硬失败;但放它直通 `setTimeout` 会钳到 1ms,
|
|
52
|
+
* 把「等限流窗口过去」变成「热重试」,那才是真危害。
|
|
53
|
+
*/
|
|
54
|
+
export declare function clampTimerDelay(ms: number): number;
|
|
13
55
|
/**
|
|
14
56
|
* 超时错误(`code === 'timeout'`)。
|
|
15
57
|
*
|
package/dist/core/timeout.js
CHANGED
|
@@ -6,10 +6,68 @@
|
|
|
6
6
|
* 2026-09-14 的「硬保证」收紧只落进了 `engine/concurrency.ts`,桥那一份继续用竞速判定 ——
|
|
7
7
|
* 同一个承诺两套实现,于是同一个事件在 trace 里能有两种账(见 `docs/spec.md` §10 2026-09-17 ①)。
|
|
8
8
|
* `integrations` 只能依赖 core(`tests/architecture/layering.test.ts` 强制),
|
|
9
|
-
* 所以单源的落点是 core。core
|
|
9
|
+
* 所以单源的落点是 core。core 是叶子:本文件只 import `limits.js`(**纯数据表,零 import**)
|
|
10
|
+
* —— 2026-09-28 起它要 `zeroClauseOf`:两处 `assertTimerDelay` 的文案必须与 limits 表同源
|
|
11
|
+
* (外部深评 C3/C4 的「单源断了一头」)。叶子性没变:数据表不引入任何运行期依赖,也不成环。
|
|
10
12
|
*/
|
|
13
|
+
import { zeroClauseOf } from './limits.js';
|
|
11
14
|
/** 超时哨兵:区分「超时」与「工具恰好返回了 undefined」 */
|
|
12
15
|
export const TIMED_OUT = Symbol('agentia.timed-out');
|
|
16
|
+
/**
|
|
17
|
+
* Node 定时器延迟的**物理上限**:`2^31-1` ms(约 **24.86 天**)。
|
|
18
|
+
*
|
|
19
|
+
* 为什么它必须是单源(而不是各调用点各写一份):超过这个值的延迟**不会「等那么久」** ——
|
|
20
|
+
* Node 只会在 stderr 留一行 `TimeoutOverflowWarning`(`Timeout duration was set to 1`),
|
|
21
|
+
* 然后把延迟**钳到 1ms**。于是「30 天后触发」变成「立刻触发」、「退避到限流窗口之后」
|
|
22
|
+
* 变成「热重试打风暴」。实测(Node 22):`delay = 2^31` 在 200ms 内触发并打警告,
|
|
23
|
+
* 而 `delay = 2^31-1`(边界内)如期不触发。
|
|
24
|
+
*
|
|
25
|
+
* 本仓此前的处置是**每个旋钮各自设防**(`transport/scheduler.ts` 的 every/at 各一条,
|
|
26
|
+
* 2026-09-19 复审加的),而「派生出来的等待」没有 —— 那些正是漏网的部分:
|
|
27
|
+
* `withTimeout`(引擎 `toolTimeoutMs`)、`interruptibleSleep`(退避)、
|
|
28
|
+
* `DrainGate.waitForIdle`(drain 预算)、`#raceTimeout`(`runTimeoutMs`)、
|
|
29
|
+
* `composeSignal`(client `timeout`)五处都直喂 `setTimeout` 且只查「非负有限」。
|
|
30
|
+
* 单源收在这里(见 `docs/spec.md` §10 2026-09-28 ⑪)。
|
|
31
|
+
*/
|
|
32
|
+
export const MAX_TIMER_DELAY_MS = 2_147_483_647;
|
|
33
|
+
/**
|
|
34
|
+
* 校验一个**将要喂给 `setTimeout` 的延迟**:`NaN` 与超过 [MAX_TIMER_DELAY_MS] 一律抛。
|
|
35
|
+
*
|
|
36
|
+
* 「拒绝」而不是「钳制」是刻意的 —— 钳制正是 Node 现在做的事(钳到 1ms),
|
|
37
|
+
* 它把「远期」静默变成「立即」,是本条要消灭的那种失败模式。照 `Scheduler.at`
|
|
38
|
+
* 的既有先例:超上限直接抛,让使用者在配置处就看见(远期等待请拆成多次自检)。
|
|
39
|
+
*
|
|
40
|
+
* ⚠️ **非正数不在此列**:`0` / 负数在调用点各有合法语义(「不设超时」/「一直等」/「不睡」),
|
|
41
|
+
* 各按 `core/limits.ts` 的表读 —— 本函数只挡「物理上兑现不了」的值。
|
|
42
|
+
* ⚠️ `NaN` 单独判并**必须排在调用点的「非正」早返回之前**:`!(NaN > 0)` 与 `NaN <= 0`
|
|
43
|
+
* 都不成立或都成立得不一致,不先挡就会被静默读成另一种语义(见 `withTimeout` 处的注释)。
|
|
44
|
+
*/
|
|
45
|
+
export function assertTimerDelay(ms, where) {
|
|
46
|
+
if (Number.isNaN(ms)) {
|
|
47
|
+
throw new RangeError(`${where} 不能是 NaN —— 它既不是「不限」也不是任何时长,` +
|
|
48
|
+
`静默接受会把它读成另一件事(各旋钮 0 的含义见 core/limits.ts 的表)`);
|
|
49
|
+
}
|
|
50
|
+
if (ms > MAX_TIMER_DELAY_MS) {
|
|
51
|
+
throw new RangeError(`${where} 超过 Node 定时器延迟上限 ${MAX_TIMER_DELAY_MS}ms(约 24.86 天),收到 ${ms} —— ` +
|
|
52
|
+
`超上限的延迟不会被遵守:Node 只在 stderr 留一行 TimeoutOverflowWarning,` +
|
|
53
|
+
`随后把延迟钳到 1ms(远期变立即)。远期等待请拆成多次自检`);
|
|
54
|
+
}
|
|
55
|
+
return ms;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* 把**外部数据**得出的延迟夹进可兑现区间(`retry-after` 等)。
|
|
59
|
+
*
|
|
60
|
+
* 与 [assertTimerDelay] 的分工是刻意的、别合并:
|
|
61
|
+
* - 那个「**拒绝**」使用者配出来的坏值 —— 配置错误必须响亮,否则修法会被写歪;
|
|
62
|
+
* - 这个「**夹**」上游回给我们的值 —— `Retry-After: 99999999` 不是使用者的错,
|
|
63
|
+
* 为它抛错会把一次 429 升级成硬失败;但放它直通 `setTimeout` 会钳到 1ms,
|
|
64
|
+
* 把「等限流窗口过去」变成「热重试」,那才是真危害。
|
|
65
|
+
*/
|
|
66
|
+
export function clampTimerDelay(ms) {
|
|
67
|
+
if (!(ms > 0))
|
|
68
|
+
return 0; // 含 NaN / 负数:调用点读 0 即「不睡」(见 limits 表)
|
|
69
|
+
return ms > MAX_TIMER_DELAY_MS ? MAX_TIMER_DELAY_MS : ms;
|
|
70
|
+
}
|
|
13
71
|
/**
|
|
14
72
|
* 超时错误(`code === 'timeout'`)。
|
|
15
73
|
*
|
|
@@ -91,6 +149,12 @@ export function isTimeoutError(e) {
|
|
|
91
149
|
* SSE 心跳、metrics 刷盘)成立,对**「等待的终点」**不成立:那正是调用方在等的东西。
|
|
92
150
|
*/
|
|
93
151
|
export async function withTimeout(p, timeoutMs) {
|
|
152
|
+
// 坏值先挡(NaN / 超 2^31-1,见 assertTimerDelay)。⚠️ 必须排在下面那句
|
|
153
|
+
// 「非正 = 不设超时」**之前**:那句的判据是 `!(timeoutMs > 0)`,而 NaN 满足它
|
|
154
|
+
// ⇒ 会被**静默**读成「不设超时」,与使用者「我设了个预算」的预期正好相反
|
|
155
|
+
// (桥还会因此起自己的 60s 兜底 ⇒ 双计时器、双账本,见 limits 表 withTimeout.ms 的 note)。
|
|
156
|
+
// `0` / 负数不在此列 —— 它们是合法的「不设超时」,走下面那句原样透传。
|
|
157
|
+
assertTimerDelay(timeoutMs, `withTimeout 的 timeoutMs(引擎 toolTimeoutMs / 桥兜底超时;${zeroClauseOf('withTimeout.ms')})`);
|
|
94
158
|
if (!(timeoutMs > 0))
|
|
95
159
|
return p;
|
|
96
160
|
// 单调钟(performance.now):硬超时判定看的是**耗时**,墙钟(Date.now)会被
|
|
@@ -158,6 +222,11 @@ export async function withTimeout(p, timeoutMs) {
|
|
|
158
222
|
* 曾把它当「一致性缺口」改反过一次,被这条用例拦住 —— 保留该用例,别删。
|
|
159
223
|
*/
|
|
160
224
|
export function interruptibleSleep(ms, signal, abortMessage = '已被取消') {
|
|
225
|
+
// 坏值先挡(NaN / 超 2^31-1):这句**先于**下面的 `ms <= 0`,因为 `NaN <= 0` 是 false
|
|
226
|
+
// —— 它会穿透那句直落 `setTimeout(fn, NaN)`,被 Node 钳成 1ms ⇒「退避」静默变成
|
|
227
|
+
// 「立刻重试」;超上限同理(钳到 1ms)。`0` / 负数仍是合法的「不睡」,
|
|
228
|
+
// 且「先于 aborted 检查」那条次序(被用例钉着)不受影响 —— 见函数头注释。
|
|
229
|
+
assertTimerDelay(ms, `interruptibleSleep 的 ms(重试退避;${zeroClauseOf('interruptibleSleep.ms')})`);
|
|
161
230
|
if (ms <= 0)
|
|
162
231
|
return Promise.resolve();
|
|
163
232
|
return new Promise((resolve, reject) => {
|
|
@@ -195,11 +264,15 @@ export function interruptibleSleep(ms, signal, abortMessage = '已被取消') {
|
|
|
195
264
|
export function backoffMs(attempt, retryAfter) {
|
|
196
265
|
if (retryAfter) {
|
|
197
266
|
const secs = Number(retryAfter);
|
|
267
|
+
// ⚠️ `retry-after` 是**上游数据**,可以大到兑现不了(`Retry-After: 99999999` ⇒ 1e11 ms)。
|
|
268
|
+
// 这里走 clampTimerDelay(夹)而不是 assertTimerDelay(拒):为上游的荒谬值把一次 429
|
|
269
|
+
// 升级成硬失败是错的;而放它直通 setTimeout 会被钳到 1ms,把「等限流窗口过去」
|
|
270
|
+
// 变成「热重试打风暴」—— 那才是真危害。两条分支都要夹(HTTP-date 可以指到几个月后)。
|
|
198
271
|
if (Number.isFinite(secs))
|
|
199
|
-
return Math.max(0, Math.round(secs * 1000));
|
|
272
|
+
return clampTimerDelay(Math.max(0, Math.round(secs * 1000)));
|
|
200
273
|
const at = Date.parse(retryAfter);
|
|
201
274
|
if (Number.isFinite(at))
|
|
202
|
-
return Math.max(0, at - Date.now());
|
|
275
|
+
return clampTimerDelay(Math.max(0, at - Date.now()));
|
|
203
276
|
// 解析不了就回落指数退避
|
|
204
277
|
}
|
|
205
278
|
const base = Math.min(500 * 2 ** Math.max(0, attempt - 1), 8000);
|
package/dist/core/timeout.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeout.js","sourceRoot":"","sources":["../../src/core/timeout.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"timeout.js","sourceRoot":"","sources":["../../src/core/timeout.ts"],"names":[],"mappings":"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"}
|
package/dist/core/tool.d.ts
CHANGED
|
@@ -102,6 +102,20 @@ export interface ModelClient {
|
|
|
102
102
|
export interface ModelPricing {
|
|
103
103
|
in: number;
|
|
104
104
|
out: number;
|
|
105
|
+
/**
|
|
106
|
+
* 缓存**读**乘数(相对 `in`);缺省 `0.1`(官方标准档)。
|
|
107
|
+
* 官方有逐模型例外:Opus 5.5 是 `0.05`、Fable 5.1 / Mythos 5.1 是 `0.025` ——
|
|
108
|
+
* 这些型号不在内置表里,用 `priceOverrides` 追加时把准数写在这里,成本才不偏。
|
|
109
|
+
*/
|
|
110
|
+
cacheRead?: number;
|
|
111
|
+
/**
|
|
112
|
+
* 缓存**写**乘数(相对 `in`);缺省 `1.25`(官方 **5 分钟** 档)。
|
|
113
|
+
* ⚠️ 官方 **1 小时** 档是 `2`。而框架的 `Usage` 是四项**聚合**的,分不出这一回合的
|
|
114
|
+
* 缓存写是 5m 还是 1h(SDK 的 `Usage.cache_creation` 有拆分,但本框架没把它带上)——
|
|
115
|
+
* 所以宿主若在自己的块上用了 `cache_control: { ttl: '1h' }`,就在这里显式写 `2`,
|
|
116
|
+
* 否则成本会被低估、`maxCostUsd` 会迟触发。
|
|
117
|
+
*/
|
|
118
|
+
cacheWrite?: number;
|
|
105
119
|
}
|
|
106
120
|
/**
|
|
107
121
|
* engine 在调用每个工具时注入的执行上下文(spec §9.2:当前 span 句柄随调用传播,
|
|
@@ -155,6 +169,14 @@ export interface ToolRunContext {
|
|
|
155
169
|
* 不设(undefined)= 子循环用各自缺省(不是"不截断")。
|
|
156
170
|
*/
|
|
157
171
|
maxEventChars?: number | false;
|
|
172
|
+
/**
|
|
173
|
+
* 本 run 是否记录 assistant 文本进 llm.turn span(`RunAgentOptions.traceContent`,R8-P3a)。
|
|
174
|
+
* 嵌套能力(@SubAgent / @Skill)拉起自己的 llm 循环时必须原样传下去 —— 否则同一棵
|
|
175
|
+
* 调用树上主 agent 的回合有 `output.text`、子 agent 的没有(与 maxEventChars 同理由)。
|
|
176
|
+
*
|
|
177
|
+
* 不设(undefined)= 子循环不记录(缺省行为)。
|
|
178
|
+
*/
|
|
179
|
+
traceContent?: 'full';
|
|
158
180
|
/**
|
|
159
181
|
* 成本硬管控(C1)透传:整条 run 累计 token 上限(见 `RunAgentOptions.maxTotalTokens`)。
|
|
160
182
|
* 嵌套能力(@SubAgent / @Skill)拉起自己的 llm 循环时必须原样传下去 —— 预算是
|
|
@@ -170,6 +192,22 @@ export interface ToolRunContext {
|
|
|
170
192
|
* 批准/拒绝(审计日志、按 decidedBy 分级授权等)。
|
|
171
193
|
*/
|
|
172
194
|
approval?: ApprovalDecision;
|
|
195
|
+
/**
|
|
196
|
+
* 「现在还不是时候,T 之后再问我」(durable timer,2026-09-28 ① 的 timer 侧)。
|
|
197
|
+
* 工具在执行期调用它 = 请求**本回合到此为止**:引擎丢弃本回合这批工具的结果、
|
|
198
|
+
* 把整批 tool_use 挂起(`stopReason: 'suspended'` + `suspendedReason: 'timer'` +
|
|
199
|
+
* 目标时刻 `wakeAt`),到点后由宿主续跑并**重跑这一批**(与审批续跑的形态一致)。
|
|
200
|
+
* 真实用途:等批处理作业、等限流窗口、等收盘、等外部系统回填。
|
|
201
|
+
*
|
|
202
|
+
* ⚠️ 两条契约(都是「说错就静默出事」的位置,细则见 `engine/defer.ts`):
|
|
203
|
+
* ① **必须传将来的时刻**:T ≤ 现在(或非有限数)**当场抛 TypeError** —— 那次工具调用以
|
|
204
|
+
* is_error 的 tool_result 回给模型、run 照常往前走(不挂起)。否则到期扫描会立刻
|
|
205
|
+
* 唤醒、重跑、工具再请求同一个过去时刻=一条自己打转的 run。
|
|
206
|
+
* ② **整批语义**:挂起是回合级的(协议要求每个 tool_use 都有配对 tool_result),
|
|
207
|
+
* 所以同回合**已经执行完**的其他工具会在醒来后重跑(副作用重复)—— 要精确控制就让
|
|
208
|
+
* 模型单独调它,或把它做成幂等读。本回合多个请求取**最早**的那个时刻。
|
|
209
|
+
*/
|
|
210
|
+
deferUntil?: (at: number | Date) => void;
|
|
173
211
|
/**
|
|
174
212
|
* 工具执行被引擎**放弃等待**的信号(`toolTimeoutMs` 超时触发)。
|
|
175
213
|
*
|
|
@@ -211,7 +249,7 @@ export interface AgentTool<I = unknown, O = unknown> {
|
|
|
211
249
|
strict?: boolean;
|
|
212
250
|
/**
|
|
213
251
|
* 人工审批闸(HITL):`'required'` 时模型发起的该工具调用**不直接执行** ——
|
|
214
|
-
* 该回合挂起(`stopReason: '
|
|
252
|
+
* 该回合挂起(`stopReason: 'suspended'`,回合级全有或全无:同一回合的
|
|
215
253
|
* 其他工具也一并等待,因为协议要求每个 tool_use 都有配对 tool_result),
|
|
216
254
|
* 等宿主把决定(`ApprovalDecision`,按 tool_use_id)喂回来后恢复:
|
|
217
255
|
* 批准 → 正常执行(工具体内经 `ToolRunContext.approval` 读到自己的决定);
|