@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
package/dist/core/trace.d.ts
CHANGED
|
@@ -25,6 +25,34 @@ export interface SpanError {
|
|
|
25
25
|
/** 是否可安全重试(429/5xx/网络 vs 400/400类) */
|
|
26
26
|
retryable: boolean;
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* 工具**入参**事件的 `SpanEvent.name`(`TOOL_OUTPUT_EVENT` 是它的另一半)。
|
|
30
|
+
*
|
|
31
|
+
* 为什么单源在 `core/`(2026-09-28 外部深评 E8):这两个名字跨 **engine → eval →
|
|
32
|
+
* integrations** 三层被手写(12 处取值)—— 改名要改三遍,而漏一处不是「少记一条」,
|
|
33
|
+
* 是**静默归零**:事件在引擎里叫 A、报表按 B 过滤 ⇒ 报表说「没有工具调用」,而 trace 里
|
|
34
|
+
* 明明有(`integrations/report.ts` 与 `metrics-state.ts` 那两处都按名字过滤)。
|
|
35
|
+
*
|
|
36
|
+
* ⚠️ 报告建议的落点(`engine/tool-events.ts`,与载荷构造器同处)**不可行**:
|
|
37
|
+
* `integrations` 只许依赖 `core`(`tests/architecture/layering.test.ts`),拿不到 engine 的
|
|
38
|
+
* 导出 —— 那样 integrations 侧只能再手写一份,等于把「单源」补成「两处」。
|
|
39
|
+
* 所以词汇表落在最底层:**载荷构造器(engine)与名字(core)分开**,谁都能引。
|
|
40
|
+
*/
|
|
41
|
+
export declare const TOOL_INPUT_EVENT = "tool.input";
|
|
42
|
+
export declare const TOOL_OUTPUT_EVENT = "tool.output";
|
|
43
|
+
/**
|
|
44
|
+
* 「上游**没回报 usage**」事件名(2026-09-28 外部深评 S2)。
|
|
45
|
+
*
|
|
46
|
+
* 为什么也单源在这里:与上面那两条同一理由 —— 名字是跨层契约。写它的是
|
|
47
|
+
* `engine/turn.ts`,按名字数的是 `integrations/report.ts` 与 `metrics-state.ts`。
|
|
48
|
+
* 手写三遍 ⇒ 一处改名就是**静默归零**(报表说「没有缺失」,而 trace 里明明有)。
|
|
49
|
+
*
|
|
50
|
+
* 语义:该回合的上游响应**没有**带 token 计量,于是 `MessageUsage` 里那几个 `0` 是
|
|
51
|
+
* **替身值**(不是读数)—— 成本估算会把它读成「真的花了 0」,`maxCostUsd` 因此静默失效。
|
|
52
|
+
*/
|
|
53
|
+
export declare const USAGE_MISSING_EVENT = "usage.missing";
|
|
54
|
+
/** 工具 I/O 事件名的联合 —— `eval/export.ts` 的工具事件过滤器按它取参数 */
|
|
55
|
+
export type ToolIoEventName = typeof TOOL_INPUT_EVENT | typeof TOOL_OUTPUT_EVENT;
|
|
28
56
|
/**
|
|
29
57
|
* 结构化事件(日志)。工具入参/出参**正文默认截断**(入参/成功出参 2000 字符、失败出参 1000),
|
|
30
58
|
* 完整正文需显式开启:`RunInvocationOptions.maxEventChars: false`(缺省关)。
|
|
@@ -32,6 +60,11 @@ export interface SpanError {
|
|
|
32
60
|
*/
|
|
33
61
|
export interface SpanEvent {
|
|
34
62
|
time: number;
|
|
63
|
+
/**
|
|
64
|
+
* 事件名。工具 I/O 的两条走 `TOOL_INPUT_EVENT` / `TOOL_OUTPUT_EVENT` 常量
|
|
65
|
+
* (见上:这个名字是跨层契约,不许在别处再写一遍字面量);
|
|
66
|
+
* 其余(`'compaction'` 等)目前仍是字面量,尚未单源 —— 如实记在这里,别当已成网。
|
|
67
|
+
*/
|
|
35
68
|
name: string;
|
|
36
69
|
body: unknown;
|
|
37
70
|
}
|
|
@@ -168,7 +201,7 @@ export declare function attachScore(trace: Trace, score: Score): void;
|
|
|
168
201
|
export declare function capabilityKindOf(span: Span): string;
|
|
169
202
|
/**
|
|
170
203
|
* trace 出口:run 收尾(成功或失败)后,框架把【完整 Trace】交给每个 sink。
|
|
171
|
-
* sink
|
|
204
|
+
* sink 抛错由框架吞掉(吞之前落一条 console.warn 告警),绝不影响 run 结果(与 memory 回写同款防护)。
|
|
172
205
|
* 形状与 OtlpExporter 一致 —— createOtlpExporter() 的返回值天然满足本接口。
|
|
173
206
|
*/
|
|
174
207
|
export interface TraceSink {
|
|
@@ -189,6 +222,12 @@ export interface TraceSink {
|
|
|
189
222
|
* ⇒ 这条不变量一次钉住四件事:不丢、不重、顺序正确、增量与终态同源。要改记账点而不派发事件,
|
|
190
223
|
* 那个用例会红。
|
|
191
224
|
*
|
|
225
|
+
* ⚠️ **没有例外**(2026-09-28 PR #164 复核 §4 定的口径):截断簿记(`traceLimits.maxEvents`
|
|
226
|
+
* 超限时 run 根上的 `trace.truncated`)也走**普通 `span.event` 帧**,因此同样折得出来。
|
|
227
|
+
* 「只在一条缝上出现的事件」正是让这条规则变成假话的方式 —— 判据是「两处同一份载荷」,
|
|
228
|
+
* 不是「哪条缝更需要它」。折叠侧的类型分派是**编译期穷尽**的(新帧类型会编译失败,
|
|
229
|
+
* 而不是被当成 `span.link` 静默吞掉)。
|
|
230
|
+
*
|
|
192
231
|
* `seq`:recorder 内**每次记账动作都自增**(与当时有没有订阅者无关)—— 于是「订阅早」与
|
|
193
232
|
* 「订阅晚」看到的同一个事件序号一致。SSE 的 `Last-Event-ID` 重放、去重、跨段对齐都靠它。
|
|
194
233
|
*
|
package/dist/core/trace.js
CHANGED
|
@@ -2,6 +2,32 @@
|
|
|
2
2
|
* Agentia —— Trace / 调用树 类型草稿。
|
|
3
3
|
* 设计见 docs/spec.md §9。一次 run == 一条 trace(v1 中 traceId == runId)。
|
|
4
4
|
*/
|
|
5
|
+
/**
|
|
6
|
+
* 工具**入参**事件的 `SpanEvent.name`(`TOOL_OUTPUT_EVENT` 是它的另一半)。
|
|
7
|
+
*
|
|
8
|
+
* 为什么单源在 `core/`(2026-09-28 外部深评 E8):这两个名字跨 **engine → eval →
|
|
9
|
+
* integrations** 三层被手写(12 处取值)—— 改名要改三遍,而漏一处不是「少记一条」,
|
|
10
|
+
* 是**静默归零**:事件在引擎里叫 A、报表按 B 过滤 ⇒ 报表说「没有工具调用」,而 trace 里
|
|
11
|
+
* 明明有(`integrations/report.ts` 与 `metrics-state.ts` 那两处都按名字过滤)。
|
|
12
|
+
*
|
|
13
|
+
* ⚠️ 报告建议的落点(`engine/tool-events.ts`,与载荷构造器同处)**不可行**:
|
|
14
|
+
* `integrations` 只许依赖 `core`(`tests/architecture/layering.test.ts`),拿不到 engine 的
|
|
15
|
+
* 导出 —— 那样 integrations 侧只能再手写一份,等于把「单源」补成「两处」。
|
|
16
|
+
* 所以词汇表落在最底层:**载荷构造器(engine)与名字(core)分开**,谁都能引。
|
|
17
|
+
*/
|
|
18
|
+
export const TOOL_INPUT_EVENT = 'tool.input';
|
|
19
|
+
export const TOOL_OUTPUT_EVENT = 'tool.output';
|
|
20
|
+
/**
|
|
21
|
+
* 「上游**没回报 usage**」事件名(2026-09-28 外部深评 S2)。
|
|
22
|
+
*
|
|
23
|
+
* 为什么也单源在这里:与上面那两条同一理由 —— 名字是跨层契约。写它的是
|
|
24
|
+
* `engine/turn.ts`,按名字数的是 `integrations/report.ts` 与 `metrics-state.ts`。
|
|
25
|
+
* 手写三遍 ⇒ 一处改名就是**静默归零**(报表说「没有缺失」,而 trace 里明明有)。
|
|
26
|
+
*
|
|
27
|
+
* 语义:该回合的上游响应**没有**带 token 计量,于是 `MessageUsage` 里那几个 `0` 是
|
|
28
|
+
* **替身值**(不是读数)—— 成本估算会把它读成「真的花了 0」,`maxCostUsd` 因此静默失效。
|
|
29
|
+
*/
|
|
30
|
+
export const USAGE_MISSING_EVENT = 'usage.missing';
|
|
5
31
|
/** W3C traceparent:`<2位版本>-<32位trace>-<16位span>-<2位flags>`,全小写 hex */
|
|
6
32
|
const TRACEPARENT_RE = /^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/;
|
|
7
33
|
/**
|
package/dist/core/trace.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trace.js","sourceRoot":"","sources":["../../src/core/trace.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"trace.js","sourceRoot":"","sources":["../../src/core/trace.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 延后请求(durable timer)的**纯判定与收集**:工具说「现在还不是时候,T 之后再问我」。
|
|
3
|
+
*
|
|
4
|
+
* 为什么单独成件:这是挂起的第二个**原因**(`core/run.ts` 的 `SuspendedReason` —— timer 侧)。
|
|
5
|
+
* 它与审批(approval)的关键差别决定了这个文件里的每一条规则:
|
|
6
|
+
*
|
|
7
|
+
* | | 审批 | 延后 |
|
|
8
|
+
* |---|---|---|
|
|
9
|
+
* | 谁提 | **宿主**在执行前给决定(引擎只查 `args.approvals`) | **工具**在跑的时候提 |
|
|
10
|
+
* | 判据 | 「决定在不在」(没有就不执行) | 「时刻到了没」(没到就挂起) |
|
|
11
|
+
* | 非法值 | 不适用(决定是布尔) | **有**:T 在过去 / 非有限数 ⇒ 必须响亮拒绝 |
|
|
12
|
+
*
|
|
13
|
+
* 后一行是本文件存在的理由:延后是**工具运行时**提出的,取值就成了一条外部契约。
|
|
14
|
+
* 让「T ≤ 现在」成立的话,到期扫描读到它会立刻唤醒、续跑、工具再请求同一个过去时刻 ——
|
|
15
|
+
* **一条自己打转的 run**(每次都不花钱,但 store / trace / 判据全在空转,且永不收尾)。
|
|
16
|
+
* 所以 `resolveWakeAt` 把「必须在将来」做成**当场抛错**:那次工具调用以 is_error 的
|
|
17
|
+
* tool_result 回给模型(既有路径),run 照常往前走 —— 不静默、不挂起成僵尸。
|
|
18
|
+
*
|
|
19
|
+
* 第二件审批不需要解决的事:**一个回合一挂起**(协议要求每个 tool_use 都有配对的
|
|
20
|
+
* tool_result),所以同回合的多个延后请求只能合并成一个时刻 —— 取**最早**的那个
|
|
21
|
+
* (「早醒」是可补救的:醒来看还没到就再请求一次;「晚醒」则白等)。
|
|
22
|
+
*
|
|
23
|
+
* 纯的边界:只吃 `(at, now)`,只吐结论;不碰 recorder、不碰 ctx、不改任何 rec。
|
|
24
|
+
* 时刻口径是**绝对毫秒时间戳**(epoch ms)—— 它要落库、要跨进程比较,相对毫秒不行。
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* 校验并归一化一次延后请求的时刻(epoch ms)。
|
|
28
|
+
*
|
|
29
|
+
* - `Date` 取 `getTime()`,数字原样用;
|
|
30
|
+
* - 非有限数(NaN / Infinity / 非数字)⇒ TypeError;
|
|
31
|
+
* - **不晚于现在**(`at <= now`)⇒ TypeError。
|
|
32
|
+
*
|
|
33
|
+
* 边界是**严格大于**:恰好等于「现在」不算将来(同一毫秒里到期扫描会立刻捞起它)。
|
|
34
|
+
*/
|
|
35
|
+
export declare function resolveWakeAt(input: number | Date, now: number): number;
|
|
36
|
+
/** 一个回合收到的全部延后请求(每个 `executeTurnTools` 一次,随回合消亡) */
|
|
37
|
+
export interface DeferRequest {
|
|
38
|
+
/** 记一次请求。同一个 tool_use_id 重复请求取**最早**的那个(早醒可补救,晚醒白等) */
|
|
39
|
+
request(toolUseId: string, at: number | Date): void;
|
|
40
|
+
/** 本回合的目标时刻:所有请求里**最早**的那个;一个请求都没有 ⇒ undefined */
|
|
41
|
+
earliest(): number | undefined;
|
|
42
|
+
/** 请求过延后的 tool_use_id(按请求到达顺序;并行工具下即完成顺序) */
|
|
43
|
+
ids(): string[];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* 建一个回合级的延后请求收集器。`clock` 只为单测注入(默认 `Date.now`)——
|
|
47
|
+
* 「什么是过去」由它决定,测试不该依赖墙钟。
|
|
48
|
+
*/
|
|
49
|
+
export declare function createDeferRequest(clock?: () => number): DeferRequest;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 延后请求(durable timer)的**纯判定与收集**:工具说「现在还不是时候,T 之后再问我」。
|
|
3
|
+
*
|
|
4
|
+
* 为什么单独成件:这是挂起的第二个**原因**(`core/run.ts` 的 `SuspendedReason` —— timer 侧)。
|
|
5
|
+
* 它与审批(approval)的关键差别决定了这个文件里的每一条规则:
|
|
6
|
+
*
|
|
7
|
+
* | | 审批 | 延后 |
|
|
8
|
+
* |---|---|---|
|
|
9
|
+
* | 谁提 | **宿主**在执行前给决定(引擎只查 `args.approvals`) | **工具**在跑的时候提 |
|
|
10
|
+
* | 判据 | 「决定在不在」(没有就不执行) | 「时刻到了没」(没到就挂起) |
|
|
11
|
+
* | 非法值 | 不适用(决定是布尔) | **有**:T 在过去 / 非有限数 ⇒ 必须响亮拒绝 |
|
|
12
|
+
*
|
|
13
|
+
* 后一行是本文件存在的理由:延后是**工具运行时**提出的,取值就成了一条外部契约。
|
|
14
|
+
* 让「T ≤ 现在」成立的话,到期扫描读到它会立刻唤醒、续跑、工具再请求同一个过去时刻 ——
|
|
15
|
+
* **一条自己打转的 run**(每次都不花钱,但 store / trace / 判据全在空转,且永不收尾)。
|
|
16
|
+
* 所以 `resolveWakeAt` 把「必须在将来」做成**当场抛错**:那次工具调用以 is_error 的
|
|
17
|
+
* tool_result 回给模型(既有路径),run 照常往前走 —— 不静默、不挂起成僵尸。
|
|
18
|
+
*
|
|
19
|
+
* 第二件审批不需要解决的事:**一个回合一挂起**(协议要求每个 tool_use 都有配对的
|
|
20
|
+
* tool_result),所以同回合的多个延后请求只能合并成一个时刻 —— 取**最早**的那个
|
|
21
|
+
* (「早醒」是可补救的:醒来看还没到就再请求一次;「晚醒」则白等)。
|
|
22
|
+
*
|
|
23
|
+
* 纯的边界:只吃 `(at, now)`,只吐结论;不碰 recorder、不碰 ctx、不改任何 rec。
|
|
24
|
+
* 时刻口径是**绝对毫秒时间戳**(epoch ms)—— 它要落库、要跨进程比较,相对毫秒不行。
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* 校验并归一化一次延后请求的时刻(epoch ms)。
|
|
28
|
+
*
|
|
29
|
+
* - `Date` 取 `getTime()`,数字原样用;
|
|
30
|
+
* - 非有限数(NaN / Infinity / 非数字)⇒ TypeError;
|
|
31
|
+
* - **不晚于现在**(`at <= now`)⇒ TypeError。
|
|
32
|
+
*
|
|
33
|
+
* 边界是**严格大于**:恰好等于「现在」不算将来(同一毫秒里到期扫描会立刻捞起它)。
|
|
34
|
+
*/
|
|
35
|
+
export function resolveWakeAt(input, now) {
|
|
36
|
+
const at = input instanceof Date ? input.getTime() : input;
|
|
37
|
+
if (typeof at !== 'number' || !Number.isFinite(at)) {
|
|
38
|
+
throw new TypeError(`deferUntil 需要一个将来时刻(epoch 毫秒或 Date),收到 ${String(input)} —— ` +
|
|
39
|
+
'非有限值会在落库/比较时静默失效');
|
|
40
|
+
}
|
|
41
|
+
if (at <= now) {
|
|
42
|
+
throw new TypeError(`deferUntil 的时刻必须在将来(收到 ${at},现在是 ${now})—— ` +
|
|
43
|
+
'「现在就能续」表达不了「T 之后再问我」,而且到期扫描会立刻唤醒它、原样再请求一次(自己打转的 run)');
|
|
44
|
+
}
|
|
45
|
+
return at;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 建一个回合级的延后请求收集器。`clock` 只为单测注入(默认 `Date.now`)——
|
|
49
|
+
* 「什么是过去」由它决定,测试不该依赖墙钟。
|
|
50
|
+
*/
|
|
51
|
+
export function createDeferRequest(clock = Date.now) {
|
|
52
|
+
const asked = new Map();
|
|
53
|
+
return {
|
|
54
|
+
request(toolUseId, at) {
|
|
55
|
+
const wakeAt = resolveWakeAt(at, clock());
|
|
56
|
+
const prev = asked.get(toolUseId);
|
|
57
|
+
if (prev === undefined || wakeAt < prev)
|
|
58
|
+
asked.set(toolUseId, wakeAt);
|
|
59
|
+
},
|
|
60
|
+
earliest() {
|
|
61
|
+
let min;
|
|
62
|
+
for (const at of asked.values())
|
|
63
|
+
if (min === undefined || at < min)
|
|
64
|
+
min = at;
|
|
65
|
+
return min;
|
|
66
|
+
},
|
|
67
|
+
ids() {
|
|
68
|
+
return [...asked.keys()];
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=defer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defer.js","sourceRoot":"","sources":["../../src/engine/defer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,KAAoB,EAAE,GAAW;IAC7D,MAAM,EAAE,GAAG,KAAK,YAAY,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;IAC3D,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;QACnD,MAAM,IAAI,SAAS,CACjB,0CAA0C,MAAM,CAAC,KAAK,CAAC,MAAM;YAC3D,kBAAkB,CACrB,CAAC;IACJ,CAAC;IACD,IAAI,EAAE,IAAI,GAAG,EAAE,CAAC;QACd,MAAM,IAAI,SAAS,CACjB,0BAA0B,EAAE,QAAQ,GAAG,MAAM;YAC3C,sDAAsD,CACzD,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAYD;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAK,GAAiB,IAAI,CAAC,GAAG;IAC/D,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,OAAO;QACL,OAAO,CAAC,SAAS,EAAE,EAAE;YACnB,MAAM,MAAM,GAAG,aAAa,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;YAC1C,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YAClC,IAAI,IAAI,KAAK,SAAS,IAAI,MAAM,GAAG,IAAI;gBAAE,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QACxE,CAAC;QACD,QAAQ;YACN,IAAI,GAAuB,CAAC;YAC5B,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,MAAM,EAAE;gBAAE,IAAI,GAAG,KAAK,SAAS,IAAI,EAAE,GAAG,GAAG;oBAAE,GAAG,GAAG,EAAE,CAAC;YAC7E,OAAO,GAAG,CAAC;QACb,CAAC;QACD,GAAG;YACD,OAAO,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QAC3B,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* 本文件的机制有两半,缺一不可:
|
|
15
15
|
*
|
|
16
16
|
* 1. **取值收一处**:`forwardToolContext(ctx)` 是唯一的取值点,映射类型写成
|
|
17
|
-
* `{ [K in Key]-?: … }` ——
|
|
17
|
+
* `{ [K in Key]-?: … }` —— 全部键**必填**(值可以是 `undefined`)。少写一行 = 编译错误,
|
|
18
18
|
* 而不是静默漏。调用点播 `...forwardToolContext(ctx)` 即可,**没有**可漏的地方。
|
|
19
19
|
* 2. **归类要穷尽**:`ToolRunContext` 的每个键必须在「转发」或「引擎自装配」里各就各位。
|
|
20
20
|
* 新增一个可选字段而两边都没归类 ⇒ `UnclassifiedToolContextKey` 不再是 `never`
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* ⚠️ 唯一需要**覆盖**而不是直取的是 `signal`:两个调用点交下去的是
|
|
24
24
|
* `combineSignals(ctx.signal, ctx.abandoned)`(子循环还要能被「放弃等待」打断),
|
|
25
25
|
* 所以那里写成 `{ ...forwardToolContext(ctx), signal: combined }` —— 覆盖是显式的,
|
|
26
|
-
*
|
|
26
|
+
* 其余键仍然不可能漏。
|
|
27
27
|
*
|
|
28
28
|
* ⚠️ 本模块不是公共 API(不进 `src/index.ts`):它是内部纪律的落点。
|
|
29
29
|
*/
|
|
@@ -42,13 +42,15 @@ export declare const FORWARDED_TOOL_CONTEXT_KEYS: readonly [
|
|
|
42
42
|
'onUnpricedModel',
|
|
43
43
|
/** 正文截断口径(漏了 ⇒ 同一棵树上主/子 agent 的可见性不一致) */
|
|
44
44
|
'maxEventChars',
|
|
45
|
+
/** assistant 文本记录口径(漏了 ⇒ 同一棵树上主 agent 有 output.text、子 agent 没有) */
|
|
46
|
+
'traceContent',
|
|
45
47
|
/** 成本护栏(漏了 ⇒ 护栏在子循环期间离线) */
|
|
46
48
|
'maxTotalTokens', 'maxCostUsd',
|
|
47
49
|
/** 超时裁判权(漏了 ⇒ 永不超时 + 桥另起计时器,见文件头) */
|
|
48
50
|
'toolTimeoutMs'];
|
|
49
51
|
export type ForwardedToolContextKey = (typeof FORWARDED_TOOL_CONTEXT_KEYS)[number];
|
|
50
52
|
/**
|
|
51
|
-
*
|
|
53
|
+
* 交给子循环的那一组值:**全部键必填**(值可以是 `undefined` —— 那表示「本 run 没设」,
|
|
52
54
|
* 与「忘了转发」是两件事,后者在类型上写不出来)。
|
|
53
55
|
*/
|
|
54
56
|
export type ForwardedToolContext = {
|
|
@@ -68,14 +70,21 @@ export type ToolContextNotForwardedKey =
|
|
|
68
70
|
/** 「放弃等待」信号:折进 `signal`(见文件头的 combine),子循环的工具会拿到**它自己**的 */
|
|
69
71
|
| 'abandoned'
|
|
70
72
|
/** 审批决定:属于**本次工具调用**(外层那个工具被批没批),不该灌进子循环的每一次调用 */
|
|
71
|
-
| 'approval'
|
|
73
|
+
| 'approval'
|
|
74
|
+
/**
|
|
75
|
+
* 延后请求口:与 `approval` 同一类 —— 它是**本次工具调用所在的那个回合**的产物
|
|
76
|
+
* (请求由外层哪一次调用提出、就记在外层那个回合上)。灌进子循环会让子循环里某次工具
|
|
77
|
+
* 调用的「再问我」记到**外层回合**的收集器上:外层整批作废重跑,而请求它的那个工具
|
|
78
|
+
* 可能根本不在这批里 —— 张冠李戴。子循环自己的回合有自己的收集器(同一个入口装配)。
|
|
79
|
+
*/
|
|
80
|
+
| 'deferUntil';
|
|
72
81
|
/**
|
|
73
82
|
* 未归类的键。全部归类时是 `never`;一旦 `ToolRunContext` 新增字段而上面两组都没收,
|
|
74
83
|
* 它就变成那个字段名 —— 于是 `tests/types/forwarding.types.ts` 里那句赋 `true` 编译失败。
|
|
75
84
|
*/
|
|
76
85
|
export type UnclassifiedToolContextKey = Exclude<keyof ToolRunContext, ForwardedToolContextKey | ToolContextNotForwardedKey>;
|
|
77
86
|
/**
|
|
78
|
-
* 取值:把 `ctx`
|
|
87
|
+
* 取值:把 `ctx` 上该转发的键原样取出(**不判空** —— `undefined` 是有信息的值,
|
|
79
88
|
* 表示「本 run 没设这个旋钮」,子循环该用它自己的缺省;判空会把它吃成「没转发」)。
|
|
80
89
|
*
|
|
81
90
|
* ⚠️ 不做真值判定:`maxEventChars: false`(不截断)与 `maxTotalTokens: 0` 都是有意义的值,
|
package/dist/engine/forwarded.js
CHANGED
|
@@ -12,6 +12,8 @@ export const FORWARDED_TOOL_CONTEXT_KEYS = [
|
|
|
12
12
|
'onUnpricedModel',
|
|
13
13
|
/** 正文截断口径(漏了 ⇒ 同一棵树上主/子 agent 的可见性不一致) */
|
|
14
14
|
'maxEventChars',
|
|
15
|
+
/** assistant 文本记录口径(漏了 ⇒ 同一棵树上主 agent 有 output.text、子 agent 没有) */
|
|
16
|
+
'traceContent',
|
|
15
17
|
/** 成本护栏(漏了 ⇒ 护栏在子循环期间离线) */
|
|
16
18
|
'maxTotalTokens',
|
|
17
19
|
'maxCostUsd',
|
|
@@ -19,7 +21,7 @@ export const FORWARDED_TOOL_CONTEXT_KEYS = [
|
|
|
19
21
|
'toolTimeoutMs',
|
|
20
22
|
];
|
|
21
23
|
/**
|
|
22
|
-
* 取值:把 `ctx`
|
|
24
|
+
* 取值:把 `ctx` 上该转发的键原样取出(**不判空** —— `undefined` 是有信息的值,
|
|
23
25
|
* 表示「本 run 没设这个旋钮」,子循环该用它自己的缺省;判空会把它吃成「没转发」)。
|
|
24
26
|
*
|
|
25
27
|
* ⚠️ 不做真值判定:`maxEventChars: false`(不截断)与 `maxTotalTokens: 0` 都是有意义的值,
|
|
@@ -31,6 +33,7 @@ export function forwardToolContext(ctx) {
|
|
|
31
33
|
priceOverrides: ctx.priceOverrides,
|
|
32
34
|
onUnpricedModel: ctx.onUnpricedModel,
|
|
33
35
|
maxEventChars: ctx.maxEventChars,
|
|
36
|
+
traceContent: ctx.traceContent,
|
|
34
37
|
maxTotalTokens: ctx.maxTotalTokens,
|
|
35
38
|
maxCostUsd: ctx.maxCostUsd,
|
|
36
39
|
toolTimeoutMs: ctx.toolTimeoutMs,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"forwarded.js","sourceRoot":"","sources":["../../src/engine/forwarded.ts"],"names":[],"mappings":"AA+BA;;;;GAIG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG;IACzC,oCAAoC;IACpC,QAAQ;IACR,wDAAwD;IACxD,gBAAgB;IAChB,mCAAmC;IACnC,iBAAiB;IACjB,0CAA0C;IAC1C,eAAe;IACf,4BAA4B;IAC5B,gBAAgB;IAChB,YAAY;IACZ,qCAAqC;IACrC,eAAe;CACP,CAAC;
|
|
1
|
+
{"version":3,"file":"forwarded.js","sourceRoot":"","sources":["../../src/engine/forwarded.ts"],"names":[],"mappings":"AA+BA;;;;GAIG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG;IACzC,oCAAoC;IACpC,QAAQ;IACR,wDAAwD;IACxD,gBAAgB;IAChB,mCAAmC;IACnC,iBAAiB;IACjB,0CAA0C;IAC1C,eAAe;IACf,mEAAmE;IACnE,cAAc;IACd,4BAA4B;IAC5B,gBAAgB;IAChB,YAAY;IACZ,qCAAqC;IACrC,eAAe;CACP,CAAC;AA4CX;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAmB;IACpD,OAAO;QACL,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,cAAc,EAAE,GAAG,CAAC,cAAc;QAClC,eAAe,EAAE,GAAG,CAAC,eAAe;QACpC,aAAa,EAAE,GAAG,CAAC,aAAa;QAChC,YAAY,EAAE,GAAG,CAAC,YAAY;QAC9B,cAAc,EAAE,GAAG,CAAC,cAAc;QAClC,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,aAAa,EAAE,GAAG,CAAC,aAAa;KACjC,CAAC;AACJ,CAAC"}
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import type { MessageParam } from '../core/message.js';
|
|
2
2
|
import type { SpanError } from '../core/trace.js';
|
|
3
|
+
import type { SuspendedReason } from '../core/run.js';
|
|
3
4
|
import type { AgentStopReason } from './types.js';
|
|
4
5
|
/**
|
|
5
6
|
* Agentia —— 循环出口的**结果形状单源**(engine/loop.ts 的拆分第二步)。
|
|
6
7
|
*
|
|
7
|
-
* `AgentLoopResult` 属「结果/状态记录」型(`docs/guards.md` 的类型角色表):
|
|
8
|
+
* `AgentLoopResult` 属「结果/状态记录」型(`docs/guards.md` 的类型角色表):10 个字段
|
|
8
9
|
* **必须全在场**,值可以为 undefined —— 消费方按 `result.suspendedMessages === undefined`
|
|
9
10
|
* 判「没挂起」,而「字段缺席」与「字段为 undefined」在 JSON / OTLP 上是两回事。
|
|
10
11
|
*
|
|
11
12
|
* 此前这 7 个字段由 loop.ts 的五处对象字面量各写一遍(= 35 次「记得写」):加一个字段
|
|
12
13
|
* 要改五处,漏一处就是无声的形状漂移。现在出口只剩四个具名构造器,各自的语义写在名字上:
|
|
13
14
|
*
|
|
14
|
-
* suspendedResult
|
|
15
|
+
* suspendedResult 挂起(等人审批 / 等一个时刻)—— 不是失败;交出的历史是**拷贝**
|
|
15
16
|
* abortedResult 已取消 —— 带结构化 error(取消不是失败,但原因要可查)
|
|
16
17
|
* failedResult 抛出被兜住 —— 由 `classifyError` 翻成结构化 error
|
|
17
18
|
* finishedResult 循环这次**结束**了 —— 成败由 stopReason 表达,error 由调用方给
|
|
@@ -28,21 +29,67 @@ export interface AgentLoopResult<T = unknown> {
|
|
|
28
29
|
/** submit_result 校验通过的结构化结果;未提交则为 undefined(类型由 resultSchema 推导) */
|
|
29
30
|
typed: T | undefined;
|
|
30
31
|
/**
|
|
31
|
-
* HITL 挂起(stopReason === '
|
|
32
|
+
* HITL 挂起(stopReason === 'suspended')时的完整消息历史
|
|
32
33
|
* (末尾是含未决 tool_use 的 assistant 消息);未挂起为 undefined(字段在场)。
|
|
33
34
|
*/
|
|
34
35
|
suspendedMessages: MessageParam[] | undefined;
|
|
35
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* HITL 挂起时待决的 tool_use_id 列表;未挂起为 undefined(字段在场)
|
|
38
|
+
*/
|
|
36
39
|
pendingApprovals: string[] | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* 挂起原因(`stopReason === 'suspended'` 时非空,2026-09-28 ①):
|
|
42
|
+
* `approval` = 等人工决定、`timer` = 等一个时刻。**由出口的调用方给** ——
|
|
43
|
+
* 出口自己不认识「为什么挂起」,它只保证这个位一定在场。
|
|
44
|
+
*/
|
|
45
|
+
suspendedReason: SuspendedReason | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* 时间挂起的目标时刻(epoch ms):`suspendedReason === 'timer'` 时在场,
|
|
48
|
+
* 其余(含 `approval` 挂起与一切非挂起收尾)为 undefined(字段在场)。
|
|
49
|
+
*
|
|
50
|
+
* 宿主(`AsyncRunner`)据此落 `TaskRecord.wakeAt`,到期扫描靠它判「醒了没」——
|
|
51
|
+
* 与「原因」同一个道理:**在哪个出口写、什么时候非空**由调用方(循环)说,
|
|
52
|
+
* 出口只保证这个位一定在场。
|
|
53
|
+
*/
|
|
54
|
+
wakeAt: number | undefined;
|
|
55
|
+
/**
|
|
56
|
+
* 本段是否把传入的 `events`(run 事件投入口,2026-09-28 ⑥)**真注入了消息流**
|
|
57
|
+
* (字段在场)。宿主据此清 `TaskRecord.pendingEvents`:注入过 ⇒ 事件已在历史里,
|
|
58
|
+
* 簿记清掉(否则下次续跑会**重复注入**);没注入(续跑入口的再挂起出口在注入点
|
|
59
|
+
* 之前返回)⇒ 簿记必须留住,等跑通的那次。
|
|
60
|
+
*/
|
|
61
|
+
eventsDelivered: boolean;
|
|
37
62
|
}
|
|
38
|
-
/**
|
|
63
|
+
/**
|
|
64
|
+
* 挂起请求:**为什么**挂起由调用方说(出口不认识业务原因),`pending` 是本批未决的 id。
|
|
65
|
+
*
|
|
66
|
+
* 判别联合让「说 timer 却不给时刻」在类型上写不出来(`wakeAt` 是 timer 分支的必填项)——
|
|
67
|
+
* 落到结果里就成了 `suspendedReason === 'timer' ⇔ wakeAt 在场` 这条不变量,
|
|
68
|
+
* 到期扫描读的就是它(`wake-policy.ts` 的 `timerDue`)。
|
|
69
|
+
*/
|
|
70
|
+
export type Suspension = {
|
|
71
|
+
reason: 'approval';
|
|
72
|
+
pending: string[];
|
|
73
|
+
} | {
|
|
74
|
+
reason: 'timer';
|
|
75
|
+
pending: string[];
|
|
76
|
+
wakeAt: number;
|
|
77
|
+
};
|
|
78
|
+
/** 挂起收尾(四处出口共用:循环中途挂起 / 恢复模式进来决定仍不齐 / 两处延后请求) */
|
|
39
79
|
export declare function suspendedResult<T>(ctx: {
|
|
40
80
|
messages: MessageParam[];
|
|
81
|
+
/**
|
|
82
|
+
* 段级进度簿记。`eventsDelivered` **必须从这里读**(不是 ctx 上的独立字段):
|
|
83
|
+
* 它跨段共享、外层收尾也读同一份(见 `loop.ts` 的 catch 与外部深评 E4)。
|
|
84
|
+
* ⚠️ 别把它挪回 ctx 的顶层字段 —— 那样四个挂起出口会**静默报 false**
|
|
85
|
+
* (可选字段缺失不报错),2026-09-28 第一版就是这么改的,被既有用例当场抓住。
|
|
86
|
+
*/
|
|
41
87
|
progress: {
|
|
42
88
|
iterations: number;
|
|
89
|
+
eventsDelivered?: boolean;
|
|
43
90
|
};
|
|
44
91
|
typed: T | undefined;
|
|
45
|
-
},
|
|
92
|
+
}, suspension: Suspension,
|
|
46
93
|
/** 挂起前那一回合的文本(循环中途挂起时给;恢复模式再次挂起时没有新回合,留空) */
|
|
47
94
|
finalText?: string): AgentLoopResult<T>;
|
|
48
95
|
/**
|
|
@@ -56,8 +103,18 @@ export declare function abortedResult<T = never>(iterations?: number): AgentLoop
|
|
|
56
103
|
* 抛出被兜住的收尾:`cause` 是**任何**抛出来的东西(SDK 异常、类型错误、abort 信号……),
|
|
57
104
|
* 出口处一律经 `classifyError` 翻成结构化 SpanError —— 「非正常收尾必带结构化 error」
|
|
58
105
|
* 这条不变量落在这一处,而不是散在各 catch 里。
|
|
106
|
+
*
|
|
107
|
+
* ⚠️ `eventsDelivered` 必须由调用方**按事实**传入,不在这里硬写 `false`(外部深评 E4):
|
|
108
|
+
* `false` 的含义是「本段没注入过事件」,而这条出口完全可能发生在注入**之后**
|
|
109
|
+
* (事件注入 → 后续回合请求失败 ⇒ 落到这里)。硬写就把「已注入」报成「未注入」——
|
|
110
|
+
* 该字段随 `rec.result` **落库、在导出面上**,消费者据此判断「这批事件进没进历史」,
|
|
111
|
+
* 报错方向恰好会让宿主以为该重投(重投 = 模型看到两条)。
|
|
112
|
+
*
|
|
113
|
+
* 当日为什么没被发现:`pendingEvents` 的清理在**终态**分支是无条件的(`async.ts`),
|
|
114
|
+
* 而这条出口只走终态 ⇒ 框架内部的簿记恰好被掩盖。它坏的是**对外字段的语义**,不是内部行为 ——
|
|
115
|
+
* 所以这是「如实类」修复:**零行为变更,一个字段不再说谎**。
|
|
59
116
|
*/
|
|
60
|
-
export declare function failedResult<T = never>(cause: unknown, iterations: number): AgentLoopResult<T>;
|
|
117
|
+
export declare function failedResult<T = never>(cause: unknown, iterations: number, eventsDelivered?: boolean): AgentLoopResult<T>;
|
|
61
118
|
/**
|
|
62
119
|
* 循环这次结束了。名字刻意不含成败:`stopReason` 才是成败的表达
|
|
63
120
|
* (`end_turn` / `stop_sequence` 成功,`budget_exceeded` / `max_iterations` / `refusal` 一类
|
|
@@ -69,4 +126,5 @@ export declare function finishedResult<T>(f: {
|
|
|
69
126
|
iterations: number;
|
|
70
127
|
typed: T | undefined;
|
|
71
128
|
error?: SpanError | undefined;
|
|
129
|
+
eventsDelivered?: boolean;
|
|
72
130
|
}): AgentLoopResult<T>;
|
|
@@ -13,15 +13,18 @@ function loopResult(f) {
|
|
|
13
13
|
typed: f.typed,
|
|
14
14
|
suspendedMessages: undefined,
|
|
15
15
|
pendingApprovals: undefined,
|
|
16
|
+
suspendedReason: undefined,
|
|
17
|
+
wakeAt: undefined,
|
|
18
|
+
eventsDelivered: f.eventsDelivered ?? false,
|
|
16
19
|
};
|
|
17
20
|
}
|
|
18
|
-
/**
|
|
19
|
-
export function suspendedResult(ctx,
|
|
21
|
+
/** 挂起收尾(四处出口共用:循环中途挂起 / 恢复模式进来决定仍不齐 / 两处延后请求) */
|
|
22
|
+
export function suspendedResult(ctx, suspension,
|
|
20
23
|
/** 挂起前那一回合的文本(循环中途挂起时给;恢复模式再次挂起时没有新回合,留空) */
|
|
21
24
|
finalText = '') {
|
|
22
25
|
return {
|
|
23
26
|
...loopResult({
|
|
24
|
-
stopReason: '
|
|
27
|
+
stopReason: 'suspended',
|
|
25
28
|
finalText,
|
|
26
29
|
// 挂起不是失败:等人不该被看板算成失败(区分由 stopReason 承担)
|
|
27
30
|
error: undefined,
|
|
@@ -31,7 +34,11 @@ finalText = '') {
|
|
|
31
34
|
// 历史是**拷贝**:宿主落库后仍可能继续用同一条数组,挂起段的消息不能再被改
|
|
32
35
|
suspendedMessages: [...ctx.messages],
|
|
33
36
|
// 待决 id 列表原样交出(同一引用)—— 调用方此后不再改动它,保留原行为,不顺手加拷贝
|
|
34
|
-
pendingApprovals: pending,
|
|
37
|
+
pendingApprovals: suspension.pending,
|
|
38
|
+
suspendedReason: suspension.reason,
|
|
39
|
+
// 只有时间挂起有目标时刻;判别联合让这一行不可能「说 timer 却没时刻」
|
|
40
|
+
wakeAt: suspension.reason === 'timer' ? suspension.wakeAt : undefined,
|
|
41
|
+
eventsDelivered: ctx.progress.eventsDelivered ?? false,
|
|
35
42
|
};
|
|
36
43
|
}
|
|
37
44
|
/**
|
|
@@ -53,14 +60,25 @@ export function abortedResult(iterations = 0) {
|
|
|
53
60
|
* 抛出被兜住的收尾:`cause` 是**任何**抛出来的东西(SDK 异常、类型错误、abort 信号……),
|
|
54
61
|
* 出口处一律经 `classifyError` 翻成结构化 SpanError —— 「非正常收尾必带结构化 error」
|
|
55
62
|
* 这条不变量落在这一处,而不是散在各 catch 里。
|
|
63
|
+
*
|
|
64
|
+
* ⚠️ `eventsDelivered` 必须由调用方**按事实**传入,不在这里硬写 `false`(外部深评 E4):
|
|
65
|
+
* `false` 的含义是「本段没注入过事件」,而这条出口完全可能发生在注入**之后**
|
|
66
|
+
* (事件注入 → 后续回合请求失败 ⇒ 落到这里)。硬写就把「已注入」报成「未注入」——
|
|
67
|
+
* 该字段随 `rec.result` **落库、在导出面上**,消费者据此判断「这批事件进没进历史」,
|
|
68
|
+
* 报错方向恰好会让宿主以为该重投(重投 = 模型看到两条)。
|
|
69
|
+
*
|
|
70
|
+
* 当日为什么没被发现:`pendingEvents` 的清理在**终态**分支是无条件的(`async.ts`),
|
|
71
|
+
* 而这条出口只走终态 ⇒ 框架内部的簿记恰好被掩盖。它坏的是**对外字段的语义**,不是内部行为 ——
|
|
72
|
+
* 所以这是「如实类」修复:**零行为变更,一个字段不再说谎**。
|
|
56
73
|
*/
|
|
57
|
-
export function failedResult(cause, iterations) {
|
|
74
|
+
export function failedResult(cause, iterations, eventsDelivered) {
|
|
58
75
|
return loopResult({
|
|
59
76
|
stopReason: 'error',
|
|
60
77
|
finalText: '',
|
|
61
78
|
error: classifyError(cause),
|
|
62
79
|
iterations,
|
|
63
80
|
typed: undefined,
|
|
81
|
+
...(eventsDelivered !== undefined ? { eventsDelivered } : {}),
|
|
64
82
|
});
|
|
65
83
|
}
|
|
66
84
|
/**
|
|
@@ -75,6 +93,7 @@ export function finishedResult(f) {
|
|
|
75
93
|
error: f.error,
|
|
76
94
|
iterations: f.iterations,
|
|
77
95
|
typed: f.typed,
|
|
96
|
+
...(f.eventsDelivered !== undefined ? { eventsDelivered: f.eventsDelivered } : {}),
|
|
78
97
|
});
|
|
79
98
|
}
|
|
80
99
|
//# sourceMappingURL=loop-result.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loop-result.js","sourceRoot":"","sources":["../../src/engine/loop-result.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"loop-result.js","sourceRoot":"","sources":["../../src/engine/loop-result.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AA+DzC;;;GAGG;AACH,SAAS,UAAU,CAAI,CAQtB;IACC,OAAO;QACL,UAAU,EAAE,CAAC,CAAC,UAAU;QACxB,SAAS,EAAE,CAAC,CAAC,SAAS;QACtB,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,UAAU,EAAE,CAAC,CAAC,UAAU;QACxB,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,iBAAiB,EAAE,SAAS;QAC5B,gBAAgB,EAAE,SAAS;QAC3B,eAAe,EAAE,SAAS;QAC1B,MAAM,EAAE,SAAS;QACjB,eAAe,EAAE,CAAC,CAAC,eAAe,IAAI,KAAK;KAC5C,CAAC;AACJ,CAAC;AAaD,iDAAiD;AACjD,MAAM,UAAU,eAAe,CAC7B,GAUC,EACD,UAAsB;AACtB,6CAA6C;AAC7C,SAAS,GAAG,EAAE;IAEd,OAAO;QACL,GAAG,UAAU,CAAI;YACf,UAAU,EAAE,WAAW;YACvB,SAAS;YACT,wCAAwC;YACxC,KAAK,EAAE,SAAS;YAChB,UAAU,EAAE,GAAG,CAAC,QAAQ,CAAC,UAAU;YACnC,KAAK,EAAE,GAAG,CAAC,KAAK;SACjB,CAAC;QACF,yCAAyC;QACzC,iBAAiB,EAAE,CAAC,GAAG,GAAG,CAAC,QAAQ,CAAC;QACpC,+CAA+C;QAC/C,gBAAgB,EAAE,UAAU,CAAC,OAAO;QACpC,eAAe,EAAE,UAAU,CAAC,MAAM;QAClC,wCAAwC;QACxC,MAAM,EAAE,UAAU,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;QACrE,eAAe,EAAE,GAAG,CAAC,QAAQ,CAAC,eAAe,IAAI,KAAK;KACvD,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAY,UAAU,GAAG,CAAC;IACrD,OAAO,UAAU,CAAI;QACnB,UAAU,EAAE,SAAS;QACrB,SAAS,EAAE,EAAE;QACb,KAAK,EAAE,YAAY,EAAE;QACrB,UAAU;QACV,KAAK,EAAE,SAAS;KACjB,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,YAAY,CAC1B,KAAc,EACd,UAAkB,EAClB,eAAyB;IAEzB,OAAO,UAAU,CAAI;QACnB,UAAU,EAAE,OAAO;QACnB,SAAS,EAAE,EAAE;QACb,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC;QAC3B,UAAU;QACV,KAAK,EAAE,SAAS;QAChB,GAAG,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC9D,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAI,CAOjC;IACC,OAAO,UAAU,CAAI;QACnB,UAAU,EAAE,CAAC,CAAC,UAAU;QACxB,SAAS,EAAE,CAAC,CAAC,SAAS;QACtB,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,UAAU,EAAE,CAAC,CAAC,UAAU;QACxB,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,GAAG,CAAC,CAAC,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACnF,CAAC,CAAC;AACL,CAAC"}
|
package/dist/engine/loop.d.ts
CHANGED
|
@@ -35,6 +35,8 @@ export declare function runAgentScoped<S extends JsonSchema = JsonSchema>(opts:
|
|
|
35
35
|
maxToolConcurrency?: number | undefined;
|
|
36
36
|
/** 事件正文截断上限;同 RunAgentOptions.maxEventChars */
|
|
37
37
|
maxEventChars?: number | false | undefined;
|
|
38
|
+
/** opt-in 记录 assistant 文本(R8-P3a);同 RunAgentOptions.traceContent(由转发机制透传下来) */
|
|
39
|
+
traceContent?: 'full' | undefined;
|
|
38
40
|
/** 价格表覆盖(F1):由发起它的能力从 ToolRunContext.priceOverrides 透传 */
|
|
39
41
|
priceOverrides?: Record<string, ModelPricing> | undefined;
|
|
40
42
|
/** 未定价模型回调(F2):由发起它的能力透传 */
|