@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.
Files changed (201) hide show
  1. package/CHANGELOG.md +779 -1
  2. package/README.md +1 -1
  3. package/dist/AGENTS.md +320 -37
  4. package/dist/container/container.js +15 -0
  5. package/dist/container/container.js.map +1 -1
  6. package/dist/core/limits.d.ts +121 -17
  7. package/dist/core/limits.js +133 -16
  8. package/dist/core/limits.js.map +1 -1
  9. package/dist/core/line-framing.d.ts +36 -0
  10. package/dist/core/line-framing.js +39 -0
  11. package/dist/core/line-framing.js.map +1 -0
  12. package/dist/core/message.d.ts +11 -0
  13. package/dist/core/run.d.ts +68 -4
  14. package/dist/core/run.js +27 -1
  15. package/dist/core/run.js.map +1 -1
  16. package/dist/core/schema.js +59 -8
  17. package/dist/core/schema.js.map +1 -1
  18. package/dist/core/timeout.d.ts +43 -1
  19. package/dist/core/timeout.js +76 -3
  20. package/dist/core/timeout.js.map +1 -1
  21. package/dist/core/tool.d.ts +39 -1
  22. package/dist/core/trace.d.ts +40 -1
  23. package/dist/core/trace.js +26 -0
  24. package/dist/core/trace.js.map +1 -1
  25. package/dist/engine/defer.d.ts +49 -0
  26. package/dist/engine/defer.js +72 -0
  27. package/dist/engine/defer.js.map +1 -0
  28. package/dist/engine/forwarded.d.ts +14 -5
  29. package/dist/engine/forwarded.js +4 -1
  30. package/dist/engine/forwarded.js.map +1 -1
  31. package/dist/engine/loop-result.d.ts +65 -7
  32. package/dist/engine/loop-result.js +24 -5
  33. package/dist/engine/loop-result.js.map +1 -1
  34. package/dist/engine/loop.d.ts +2 -0
  35. package/dist/engine/loop.js +157 -17
  36. package/dist/engine/loop.js.map +1 -1
  37. package/dist/engine/mcp-server-http.d.ts +2 -0
  38. package/dist/engine/mcp-server-http.js +215 -0
  39. package/dist/engine/mcp-server-http.js.map +1 -0
  40. package/dist/engine/mcp-server-stdio.d.ts +15 -0
  41. package/dist/engine/mcp-server-stdio.js +106 -0
  42. package/dist/engine/mcp-server-stdio.js.map +1 -0
  43. package/dist/engine/mcp-server.d.ts +134 -0
  44. package/dist/engine/mcp-server.js +232 -0
  45. package/dist/engine/mcp-server.js.map +1 -0
  46. package/dist/engine/menu-drift.d.ts +34 -0
  47. package/dist/engine/menu-drift.js +81 -0
  48. package/dist/engine/menu-drift.js.map +1 -0
  49. package/dist/engine/policy.d.ts +2 -1
  50. package/dist/engine/policy.js +7 -1
  51. package/dist/engine/policy.js.map +1 -1
  52. package/dist/engine/replay.d.ts +2 -1
  53. package/dist/engine/replay.js +3 -2
  54. package/dist/engine/replay.js.map +1 -1
  55. package/dist/engine/resume-input.d.ts +10 -0
  56. package/dist/engine/resume-input.js +11 -0
  57. package/dist/engine/resume-input.js.map +1 -1
  58. package/dist/engine/run-config.d.ts +26 -2
  59. package/dist/engine/run-config.js +60 -0
  60. package/dist/engine/run-config.js.map +1 -1
  61. package/dist/engine/spec.d.ts +33 -4
  62. package/dist/engine/spec.js.map +1 -1
  63. package/dist/engine/tool-context.d.ts +10 -5
  64. package/dist/engine/tool-context.js +5 -1
  65. package/dist/engine/tool-context.js.map +1 -1
  66. package/dist/engine/tracer.d.ts +43 -5
  67. package/dist/engine/tracer.js +73 -10
  68. package/dist/engine/tracer.js.map +1 -1
  69. package/dist/engine/trimming.js +151 -17
  70. package/dist/engine/trimming.js.map +1 -1
  71. package/dist/engine/turn.d.ts +60 -3
  72. package/dist/engine/turn.js +180 -71
  73. package/dist/engine/turn.js.map +1 -1
  74. package/dist/engine/types.d.ts +89 -3
  75. package/dist/engine/types.js +38 -2
  76. package/dist/engine/types.js.map +1 -1
  77. package/dist/engine/usage.d.ts +22 -3
  78. package/dist/engine/usage.js +47 -6
  79. package/dist/engine/usage.js.map +1 -1
  80. package/dist/eval/export.d.ts +48 -0
  81. package/dist/eval/export.js +146 -0
  82. package/dist/eval/export.js.map +1 -0
  83. package/dist/eval/harvest.js +7 -2
  84. package/dist/eval/harvest.js.map +1 -1
  85. package/dist/index.d.ts +8 -5
  86. package/dist/index.js +6 -1
  87. package/dist/index.js.map +1 -1
  88. package/dist/integrations/adapter-options.d.ts +22 -0
  89. package/dist/integrations/adapter-options.js +51 -0
  90. package/dist/integrations/adapter-options.js.map +1 -1
  91. package/dist/integrations/anthropic.d.ts +4 -2
  92. package/dist/integrations/anthropic.js +27 -27
  93. package/dist/integrations/anthropic.js.map +1 -1
  94. package/dist/integrations/file-sink.d.ts +20 -0
  95. package/dist/integrations/file-sink.js +27 -0
  96. package/dist/integrations/file-sink.js.map +1 -0
  97. package/dist/integrations/mcp-http.d.ts +3 -3
  98. package/dist/integrations/mcp-http.js +1 -1
  99. package/dist/integrations/mcp-http.js.map +1 -1
  100. package/dist/integrations/mcp-protocol.d.ts +115 -0
  101. package/dist/integrations/mcp-protocol.js +69 -0
  102. package/dist/integrations/mcp-protocol.js.map +1 -0
  103. package/dist/integrations/mcp-stdio.d.ts +3 -3
  104. package/dist/integrations/mcp-stdio.js +17 -3
  105. package/dist/integrations/mcp-stdio.js.map +1 -1
  106. package/dist/integrations/mcp.d.ts +6 -95
  107. package/dist/integrations/mcp.js +24 -66
  108. package/dist/integrations/mcp.js.map +1 -1
  109. package/dist/integrations/metrics-otlp.js +52 -7
  110. package/dist/integrations/metrics-otlp.js.map +1 -1
  111. package/dist/integrations/metrics-render.d.ts +15 -0
  112. package/dist/integrations/metrics-render.js +79 -2
  113. package/dist/integrations/metrics-render.js.map +1 -1
  114. package/dist/integrations/metrics-state.d.ts +99 -1
  115. package/dist/integrations/metrics-state.js +128 -4
  116. package/dist/integrations/metrics-state.js.map +1 -1
  117. package/dist/integrations/metrics.d.ts +62 -6
  118. package/dist/integrations/metrics.js +49 -8
  119. package/dist/integrations/metrics.js.map +1 -1
  120. package/dist/integrations/openai.js +9 -31
  121. package/dist/integrations/openai.js.map +1 -1
  122. package/dist/integrations/otlp.d.ts +3 -3
  123. package/dist/integrations/report.d.ts +7 -0
  124. package/dist/integrations/report.js +20 -1
  125. package/dist/integrations/report.js.map +1 -1
  126. package/dist/runtime/memory.d.ts +61 -1
  127. package/dist/runtime/memory.js +47 -7
  128. package/dist/runtime/memory.js.map +1 -1
  129. package/dist/runtime/run.d.ts +12 -2
  130. package/dist/runtime/run.js +112 -15
  131. package/dist/runtime/run.js.map +1 -1
  132. package/dist/store/fsStore.js +10 -4
  133. package/dist/store/fsStore.js.map +1 -1
  134. package/dist/store/record.d.ts +41 -0
  135. package/dist/store/record.js +54 -0
  136. package/dist/store/record.js.map +1 -0
  137. package/dist/store/redisStore.d.ts +3 -1
  138. package/dist/store/redisStore.js +21 -7
  139. package/dist/store/redisStore.js.map +1 -1
  140. package/dist/store/sqliteStore.d.ts +23 -0
  141. package/dist/store/sqliteStore.js +103 -8
  142. package/dist/store/sqliteStore.js.map +1 -1
  143. package/dist/store/store.d.ts +65 -2
  144. package/dist/store/store.js +5 -2
  145. package/dist/store/store.js.map +1 -1
  146. package/dist/toolkit/capability-cycles.d.ts +50 -0
  147. package/dist/toolkit/capability-cycles.js +88 -0
  148. package/dist/toolkit/capability-cycles.js.map +1 -0
  149. package/dist/toolkit/capability-slice.d.ts +86 -0
  150. package/dist/toolkit/capability-slice.js +98 -0
  151. package/dist/toolkit/capability-slice.js.map +1 -0
  152. package/dist/toolkit/module.d.ts +7 -1
  153. package/dist/toolkit/module.js +65 -32
  154. package/dist/toolkit/module.js.map +1 -1
  155. package/dist/transport/approval-policy.d.ts +1 -1
  156. package/dist/transport/approval-policy.js +8 -4
  157. package/dist/transport/approval-policy.js.map +1 -1
  158. package/dist/transport/async.d.ts +197 -18
  159. package/dist/transport/async.js +871 -85
  160. package/dist/transport/async.js.map +1 -1
  161. package/dist/transport/drain-gate.d.ts +6 -2
  162. package/dist/transport/drain-gate.js +14 -2
  163. package/dist/transport/drain-gate.js.map +1 -1
  164. package/dist/transport/http-endpoints.d.ts +151 -0
  165. package/dist/transport/http-endpoints.js +480 -0
  166. package/dist/transport/http-endpoints.js.map +1 -0
  167. package/dist/transport/http-io.d.ts +66 -0
  168. package/dist/transport/http-io.js +145 -0
  169. package/dist/transport/http-io.js.map +1 -0
  170. package/dist/transport/http-route.d.ts +10 -3
  171. package/dist/transport/http-route.js +18 -3
  172. package/dist/transport/http-route.js.map +1 -1
  173. package/dist/transport/http-shapes.d.ts +11 -0
  174. package/dist/transport/http-shapes.js +31 -1
  175. package/dist/transport/http-shapes.js.map +1 -1
  176. package/dist/transport/http.d.ts +22 -35
  177. package/dist/transport/http.js +110 -467
  178. package/dist/transport/http.js.map +1 -1
  179. package/dist/transport/owner-id.d.ts +29 -0
  180. package/dist/transport/owner-id.js +46 -0
  181. package/dist/transport/owner-id.js.map +1 -0
  182. package/dist/transport/owner-liveness.d.ts +8 -0
  183. package/dist/transport/owner-liveness.js +56 -0
  184. package/dist/transport/owner-liveness.js.map +1 -0
  185. package/dist/transport/resume-policy.d.ts +48 -10
  186. package/dist/transport/resume-policy.js +25 -10
  187. package/dist/transport/resume-policy.js.map +1 -1
  188. package/dist/transport/scheduler.js +23 -24
  189. package/dist/transport/scheduler.js.map +1 -1
  190. package/dist/transport/slot-pool.d.ts +9 -1
  191. package/dist/transport/slot-pool.js +9 -1
  192. package/dist/transport/slot-pool.js.map +1 -1
  193. package/dist/transport/sse.js +8 -0
  194. package/dist/transport/sse.js.map +1 -1
  195. package/dist/transport/task-events.d.ts +34 -3
  196. package/dist/transport/task-events.js +86 -5
  197. package/dist/transport/task-events.js.map +1 -1
  198. package/dist/transport/wake-policy.d.ts +50 -0
  199. package/dist/transport/wake-policy.js +43 -0
  200. package/dist/transport/wake-policy.js.map +1 -0
  201. package/package.json +6 -4
@@ -0,0 +1,106 @@
1
+ import { frameLimitText, MAX_FRAME_CHARS } from '../core/line-framing.js';
2
+ /**
3
+ * Agentia —— MCP 反向桥的 **stdio 传输**(2026-09-28 自 `mcp-server.ts` 切出,
4
+ * 与正向桥 `integrations/mcp-stdio.ts` 同款排布:桥居中、传输各居其文件)。
5
+ *
6
+ * 形状:stdin/stdout **换行分隔 JSON-RPC**(每行一个完整报文);日志只去 stderr
7
+ * —— stdout 是协议面,往里写一个字节都会污染报文流。
8
+ *
9
+ * **依赖方向(单向,运行期无环)**:本文件从 `mcp-server.js` 只取**类型**
10
+ * (`McpCore` / `McpServer`,编译期擦除),协议与执行**由 `core` 注入**。
11
+ * ⚠️ 与正向桥的差别(那边的 `mcp.ts ↔ mcp-stdio.ts` 是一条**真实的值环**:桥 re-export
12
+ * 连接器、连接器反向 import 桥的共享 helper,靠 ESM 的函数提升与调用时机侥幸无恙)——
13
+ * 这里刻意做成注入式:无环是**构造性**的,不靠运气。
14
+ */
15
+ export function startStdioTransport(core) {
16
+ // stdout 的 error **必须吞**:宿主先关读端、stdin 仍开着时,下一次应答写入就是异步
17
+ // `write EPIPE` —— 未捕获会把 server 打成栈回溯 + exit 1(2026-09-27 ⑩),而它只是
18
+ // 「对端没了」的次生现象,不是根因。正向连接器对子进程 stdin 是同一处置
19
+ // (`integrations/mcp-stdio.ts` 的 `p.stdin?.on('error', () => {})`),方向对称。
20
+ // 吞掉 ≠ 静默:真该知道的人(宿主)已经从自己的管道拿到 EOF 了。
21
+ process.stdout.on('error', () => {
22
+ /* EPIPE:对端已走 */
23
+ });
24
+ let buf = '';
25
+ let closed = false;
26
+ /**
27
+ * 正在丢弃一条**超限行**(2026-09-28 外部深评 S6)。
28
+ *
29
+ * 宿主(或任何写 stdin 的一方)吐了一条超过分帧上限、且至今没有换行的「报文」——
30
+ * 不做处理就是 `buf` 无界增长 ⇒ 先 OOM。这里**不静默吞**:回一条协议错(说清是超限),
31
+ * 然后丢弃字节直到下一个换行 —— 那是唯一能重新对齐帧边界的地方(丢掉半截行之后,
32
+ * 剩下的字节是完整的下一帧,照常派发)。
33
+ */
34
+ let oversized = false;
35
+ const onData = (chunk) => {
36
+ // 先归位:上一块把我们带进了「丢弃超限行」的状态,处理完再谈攒缓冲
37
+ let rest = chunk;
38
+ for (;;) {
39
+ if (oversized) {
40
+ const nl = rest.indexOf('\n');
41
+ if (nl < 0)
42
+ return; // 还在那条超长行里,继续丢
43
+ oversized = false;
44
+ rest = rest.slice(nl + 1);
45
+ continue; // 换行之后的这段可能又超限,回环再判
46
+ }
47
+ if (buf.length + rest.length > MAX_FRAME_CHARS) {
48
+ // 丢弃已攒的(含超限尾块)并重新同步 —— 报文边界已丢,这些字节拼不回一条合法报文
49
+ buf = '';
50
+ oversized = true;
51
+ // 协议侧定义形状:传输层只说「这条超限了」(与下面坏 JSON 那条同一处置口径)
52
+ process.stdout.write(`${JSON.stringify(core.rpcError(null, -32700, `报文超过分帧上限(${frameLimitText()}),已丢弃`))}\n`);
53
+ continue;
54
+ }
55
+ buf += rest;
56
+ break;
57
+ }
58
+ for (;;) {
59
+ const nl = buf.indexOf('\n');
60
+ if (nl < 0)
61
+ break;
62
+ const line = buf.slice(0, nl);
63
+ buf = buf.slice(nl + 1);
64
+ if (line.trim() === '')
65
+ continue;
66
+ let msg;
67
+ try {
68
+ msg = JSON.parse(line);
69
+ }
70
+ catch {
71
+ // 坏报文由**协议侧**定义形状:传输层不该知道 JSON-RPC 的错误码(见 McpCore.rpcError)
72
+ process.stdout.write(`${JSON.stringify(core.rpcError(null, -32700, '行不是合法 JSON'))}\n`);
73
+ continue;
74
+ }
75
+ const ac = new AbortController();
76
+ const signal = core.trackCall(ac);
77
+ void core
78
+ .dispatch(msg, signal)
79
+ .then((resp) => {
80
+ if (resp !== null && !closed) {
81
+ process.stdout.write(`${JSON.stringify(resp)}\n`);
82
+ }
83
+ })
84
+ .catch(() => {
85
+ /* 分派自身不该抛(callTool 内部已兜住);真抛了也不许把 server 带崩 */
86
+ })
87
+ .finally(() => core.untrackCall(ac));
88
+ }
89
+ };
90
+ process.stdin.setEncoding('utf8');
91
+ process.stdin.on('data', onData);
92
+ return {
93
+ url: undefined,
94
+ ready: Promise.resolve(),
95
+ close: () => {
96
+ if (closed)
97
+ return Promise.resolve();
98
+ closed = true;
99
+ core.abortAll();
100
+ process.stdin.off('data', onData);
101
+ process.stdin.pause(); // 摘掉读端,宿主进程的事件循环不再被我们吊住
102
+ return Promise.resolve();
103
+ },
104
+ };
105
+ }
106
+ //# sourceMappingURL=mcp-server-stdio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-server-stdio.js","sourceRoot":"","sources":["../../src/engine/mcp-server-stdio.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,134 @@
1
+ import type { IncomingMessage, Server } from 'node:http';
2
+ import type { AgentTool, ModelClient } from '../core/tool.js';
3
+ import type { TraceSink } from '../core/trace.js';
4
+ /**
5
+ * Agentia —— **MCP 反向桥**(R8-P5):把 app 的能力菜单暴露成 MCP server
6
+ * (正向桥 `integrations/mcp.ts` 是把外部 MCP server 的工具接进来,本文件是反方向)。
7
+ *
8
+ * **本文件是「桥」**:协议面(`initialize` / `tools/list` / `tools/call` 的报文分派与
9
+ * 入参校验)、执行面(一次能力调用的 trace 记账 + 超时 + 错误分类 + sink 投递)、
10
+ * 以及装配(校验 → 状态 → 选传输)。两条**传输**各居其文件
11
+ * (`mcp-server-stdio.ts` / `mcp-server-http.ts`,2026-09-28 纯结构拆分、零行为变化),
12
+ * 与正向桥的排布(`mcp.ts` + `mcp-stdio.ts` / `mcp-http.ts`)同款。
13
+ * ⚠️ 一处**刻意不同**:正向桥的 `mcp.ts ↔ mcp-stdio/mcp-http` 是一条**真实的值环**
14
+ * (桥 re-export 连接器、连接器又反向 import 桥的共享 helper,靠 ESM 函数提升与调用时机
15
+ * 侥幸无恙);这里改成**注入式单向** —— 传输只从本文件取**类型**,值由 `createMcpServer`
16
+ * 经 `McpCore` 传进去 ⇒ 无环是**构造性**的,不靠运气。
17
+ *
18
+ * **落点为什么在 engine**(2026-09-28 用真实 import 复核过,结论成立):
19
+ * 本文件需要 **engine 侧四个值** —— `TraceRecorder`(每次 tools/call 一棵 trace)、
20
+ * `classifyError`、`buildToolRunContext`、`tool-events` 的记账载荷;同时需要
21
+ * **integrations 侧两个值** —— `DEFAULT_PROTOCOL_VERSION`(正向桥那份协议版本常量)与
22
+ * `createAnthropicClient`(`ToolRunContext.client` 的惰性缺省)。而
23
+ * `integrations` 只许依赖 core,`transport` 够不到 integrations,engine 是分层图上**唯一**
24
+ * 能同时够到两边的层。放进 integrations 需要一条 `integrations → engine`(4 个值),
25
+ * 既越权又与既有的 `engine → integrations` 成环。
26
+ * ⚠️ 顺带订正 AGENTS.md 的一句旧文案:`engine → integrations` 这条边**不止**
27
+ * 「取默认 ModelClient」一个用途 —— 本文件用的 `DEFAULT_PROTOCOL_VERSION` 是第二个
28
+ * (`loop.ts` 那处才是取 client)。它仍是一条边(不新增方向),故分层表的 ALLOWED 不动。
29
+ *
30
+ * **协议范围只到 tools**:`initialize` / `notifications/initialized` / `tools/list` /
31
+ * `tools/call`(顺手支持 `ping`);其余 method 一律 -32601,params 形状坏 → -32602。
32
+ *
33
+ * **trace 叙事不破**:每次 `tools/call` 造一个 `TraceRecorder`:根 span(kind `run`,
34
+ * name `mcp.tools/call`)下挂一个 capability span(kind `capability`、name = 工具名 ——
35
+ * metrics 的 `capabilityKindOf` 把它归为 `capability:<name>` 标签,自动进能力指标),
36
+ * 并按 `tool-events.ts` 的口径记同名 `tool.input` / `tool.output` 事件(账目形状与引擎
37
+ * 一致);收尾 snapshot 投递 `opts.sinks`(sink 抛错吞掉 + `console.warn`,观测不击穿业务)。
38
+ * `tools/list` / `initialize` 不建 trace。
39
+ *
40
+ * **不做(YAGNI,与正向桥同款清单)**:resources / prompts / sampling(server 反向请求模型)/
41
+ * SSE 推送(server→client 流)/ 会话强制校验(HTTP 侧发 `mcp-session-id` 头但**不校验**:
42
+ * 无状态 server,带了接受、不带也服务 —— 宽容选择,文档写明)。
43
+ */
44
+ /** MCP server 的入口应用面(鸭子类型):`AgentApp` 结构满足(`app.tools` 即装配后的菜单) */
45
+ export interface McpServerApp {
46
+ tools: AgentTool[];
47
+ }
48
+ export interface McpServerOptions {
49
+ /** 传输:`'stdio'`(CLI 场景,父进程 spawn)或 `'http'`(服务场景,StreamableHTTP) */
50
+ transport: 'stdio' | 'http';
51
+ /** http:监听地址,缺省 `'127.0.0.1'`(MCP server 不该默认暴露到公网) */
52
+ host?: string;
53
+ /** http:监听端口,缺省 `0`(系统分配,读返回值的 `url` 拿实际端口)。自带 `server` 时无效 */
54
+ port?: number;
55
+ /** http:endpoint 路径,缺省 `'/mcp'` */
56
+ path?: string;
57
+ /**
58
+ * http:**挂进既有的 `http.Server`**(框架不替它 listen;`close()` 只摘除本 handler,
59
+ * 不关 server)。此时非本 `path` 的请求本 handler 直接忽略(交给宿主自己的路由)。
60
+ */
61
+ server?: Server;
62
+ /**
63
+ * `ToolRunContext.client` 是必填字段(子 agent / skill 类工具要它拉子循环)。
64
+ * 缺省**惰性**走 `createAnthropicClient()`(与引擎默认同款)—— 第一次 `tools/call`
65
+ * 才构造,不在 `createMcpServer` 时构造(纯工具菜单的 server 不该要求 API key 在场)。
66
+ */
67
+ client?: ModelClient;
68
+ /** trace 出口:每次 `tools/call` 收尾投递(抛错被吞,观测不击穿业务) */
69
+ sinks?: TraceSink[];
70
+ /**
71
+ * 单次 `tools/call` 的工具执行超时(毫秒),语义与引擎 `toolTimeoutMs` **同款**:
72
+ * 非正数 / 不设 = 不限;超时 = **放弃等待**(abort `abandoned` 通知工具自行收尾,
73
+ * 该次调用回 `isError`,server 不挂)。复用既有语义,故不进 `limits.ts` 真源表。
74
+ */
75
+ toolTimeoutMs?: number;
76
+ /**
77
+ * http 侧鉴权钩子(与 `transport/http.ts` 的 `authenticate` **同纪律**):框架不实现
78
+ * token / JWT 策略,只留钩子 —— 在**读 body 之前**调用,抛错即 401(原文只进服务端日志)。
79
+ * stdio 侧信任父进程,本项不生效。
80
+ */
81
+ auth?: (req: IncomingMessage) => unknown | Promise<unknown>;
82
+ /** `serverInfo.name`(`initialize` 握手回给客户端),缺省 `'agentia'` */
83
+ name?: string;
84
+ }
85
+ /** `createMcpServer` 的返回值(也是两条传输各自的返回值) */
86
+ export interface McpServer {
87
+ /** http 模式的实际 endpoint(如 `http://127.0.0.1:54321/mcp`);stdio 模式恒为 `undefined` */
88
+ readonly url: string | undefined;
89
+ /** http:listen 完成的承诺(`url` 此时可用);stdio:立即 resolve */
90
+ readonly ready: Promise<void>;
91
+ /**
92
+ * 关闭(幂等):中止全部在飞 `tools/call` 的 signal;stdio 侧停读 stdin;
93
+ * http 侧关掉**自有** server(挂进来的既有 server 只摘除本 handler)。
94
+ */
95
+ close(): Promise<void>;
96
+ }
97
+ /**
98
+ * 传输层看得见的**协议面**(`createMcpServer` 组装好后注入,两条传输共用同一份)。
99
+ *
100
+ * 为什么是这几个:传输的职责是「把字节搬进搬出」,凡是**协议语义**(这条报文该怎么应答、
101
+ * 什么形状才算合法报文、在飞调用怎么记账)都不该由它决定 —— 但它需要在正确的时刻调用它们。
102
+ */
103
+ export interface McpCore {
104
+ /**
105
+ * 报文分派(两条传输共用)。返回 `null` = 通知,不应答。
106
+ * 协议范围只到 tools(见文件头);其余 method 一律 -32601,params 形状坏 → -32602。
107
+ */
108
+ dispatch(msg: unknown, signal: AbortSignal): Promise<Record<string, unknown> | null>;
109
+ /**
110
+ * 造一条 JSON-RPC 错误应答 —— 传输层**自己发现坏报文**时用它
111
+ * (目前只有 stdio:一行不是合法 JSON ⇒ -32700)。让传输层调它而不是自己拼
112
+ * `{ error: { code } }`:错误码是协议面的知识,api 面漂移时只该改一处。
113
+ */
114
+ rpcError(id: JsonRpcId, code: number, message: string): Record<string, unknown>;
115
+ /** 为一次请求登记中止句柄(close() / 传输侧断连时中止它),返回其 signal */
116
+ trackCall(ac: AbortController): AbortSignal;
117
+ untrackCall(ac: AbortController): void;
118
+ /** 中止全部在飞调用(close() 用) */
119
+ abortAll(): void;
120
+ }
121
+ type JsonRpcId = number | string | null;
122
+ /**
123
+ * 把 app 的能力菜单暴露成 MCP server。两个传输都只用标准库:
124
+ * - **stdio**:stdin/stdout 换行分隔 JSON-RPC(每行一个完整报文);日志只去 stderr;
125
+ * - **StreamableHTTP**:POST 收 JSON-RPC,应答 `application/json`(简单应答不上 SSE);
126
+ * `initialize` 响应发 `mcp-session-id` 头(生成的 uuid),后续请求带了就接受、不带也服务
127
+ * (无状态 server,**宽容是有意的**);GET(server→client 流)→ 405;DELETE → 200;
128
+ * 客户端断连会中止该次工具调用的 `signal`。
129
+ *
130
+ * 本函数只做**装配**:构造期校验 → 状态(惰性 client / 在飞句柄表)→ 组装 `McpCore`
131
+ * → 交给选中的传输。协议与执行见 `dispatch` / `callTool`,传输见各自的文件。
132
+ */
133
+ export declare function createMcpServer(app: McpServerApp, opts: McpServerOptions): McpServer;
134
+ export {};
@@ -0,0 +1,232 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { stringifySafe } from '../core/json.js';
3
+ import { validateJsonSchema } from '../core/schema.js';
4
+ import { TIMED_OUT, isTimeoutError, withTimeout } from '../core/timeout.js';
5
+ import { TOOL_INPUT_EVENT, TOOL_OUTPUT_EVENT } from '../core/trace.js';
6
+ import { DEFAULT_PROTOCOL_VERSION } from '../integrations/mcp.js';
7
+ import { createAnthropicClient } from '../integrations/anthropic.js';
8
+ import { classifyError } from './errors.js';
9
+ import { startHttpTransport } from './mcp-server-http.js';
10
+ import { startStdioTransport } from './mcp-server-stdio.js';
11
+ import { buildToolRunContext } from './tool-context.js';
12
+ import { toolInputPayload, toolOutputPayload } from './tool-events.js';
13
+ import { TraceRecorder } from './tracer.js';
14
+ /** serverInfo.version 刻意不写真版本字面量(与 DEFAULT_CLIENT_INFO 同纪律,release-surface 按精确版本串计数) */
15
+ const SERVER_INFO_VERSION = '0.0.0';
16
+ /** 造一条 JSON-RPC 错误应答 */
17
+ function rpcError(id, code, message) {
18
+ return { jsonrpc: '2.0', id, error: { code, message } };
19
+ }
20
+ /** 造一条 JSON-RPC 成功应答 */
21
+ function rpcResult(id, result) {
22
+ return { jsonrpc: '2.0', id, result };
23
+ }
24
+ /**
25
+ * 执行一次 `tools/call`:造一棵 trace(run 根 + capability span + tool.input/tool.output
26
+ * 事件),收尾投递 sinks,然后把结果映射成 MCP 形状。
27
+ *
28
+ * 独立成顶层函数(而不是 `createMcpServer` 闭包里的一支):它是本文件里最大的一块,
29
+ * 且**只依赖 `deps` 三件**,不碰装配期的任何状态 —— 拆前它藏在 441 行的闭包里无名可分。
30
+ */
31
+ async function callTool(tool, args, signal, deps) {
32
+ const { sinks, toolTimeoutMs } = deps;
33
+ const recorder = new TraceRecorder();
34
+ const rootId = recorder.begin('run', 'mcp.tools/call', null);
35
+ const capId = recorder.begin('capability', tool.name, rootId);
36
+ // tool_use_id 与引擎同义(同名工具并行时靠 id 配对入参/出参事件)
37
+ const use = { type: 'tool_use', id: randomUUID(), name: tool.name, input: args };
38
+ const startedAt = Date.now();
39
+ recorder.event(capId, TOOL_INPUT_EVENT, toolInputPayload(use));
40
+ // 超时「放弃等待」的通知信号(与引擎 executeOneTool 同款:判超时分支里 abort)
41
+ const abandonAc = new AbortController();
42
+ const toolCtx = buildToolRunContext({
43
+ client: deps.clientOf(),
44
+ recorder,
45
+ parentSpanId: capId,
46
+ abandoned: abandonAc.signal,
47
+ signal,
48
+ toolTimeoutMs,
49
+ });
50
+ let ok = true;
51
+ let content = '';
52
+ let errorKind;
53
+ let spanError;
54
+ try {
55
+ // 与引擎同一判定原语(core/timeout.ts,实测耗时兜底):超时 = 放弃等待,不杀 server
56
+ const out = await withTimeout(Promise.resolve(tool.run(args, toolCtx)), toolTimeoutMs ?? 0);
57
+ if (out === TIMED_OUT) {
58
+ abandonAc.abort(); // 通知工具「没人等结果了」(@SubAgent/@Skill 靠它自中止)
59
+ ok = false;
60
+ errorKind = 'timeout';
61
+ content = `error(timeout): 工具执行超过 ${toolTimeoutMs}ms`;
62
+ spanError = { type: 'timeout', message: String(content), retryable: true };
63
+ }
64
+ else {
65
+ content = out;
66
+ }
67
+ }
68
+ catch (e) {
69
+ ok = false;
70
+ if (isTimeoutError(e)) {
71
+ // 工具自判的超时与引擎判的归同一类账(与 turn.ts 同口径)
72
+ errorKind = 'timeout';
73
+ content = `error(timeout): ${e instanceof Error ? e.message : String(e)}`;
74
+ spanError = { type: 'timeout', message: String(content), retryable: true };
75
+ }
76
+ else {
77
+ errorKind = 'threw';
78
+ const err = classifyError(e);
79
+ content = `error(${err.type}): ${err.message}`;
80
+ spanError = err;
81
+ }
82
+ }
83
+ recorder.event(capId, TOOL_OUTPUT_EVENT, toolOutputPayload({ use, ok, errorKind, startedAt, now: Date.now(), content }));
84
+ recorder.end(capId, ok
85
+ ? {}
86
+ : {
87
+ status: 'error',
88
+ // exactOptionalPropertyTypes:显式 undefined 不是合法的 `error?: SpanError`
89
+ ...(spanError !== undefined ? { error: spanError } : {}),
90
+ });
91
+ recorder.end(rootId, ok
92
+ ? {}
93
+ : {
94
+ status: 'error',
95
+ ...(spanError !== undefined ? { error: spanError } : {}),
96
+ });
97
+ const trace = recorder.snapshot(ok ? 'ok' : 'error');
98
+ // 观测不击穿业务:sink 抛错吞掉 + console.warn(与 runtime 的 flushSinks 同款文案)
99
+ for (const sink of sinks) {
100
+ try {
101
+ await sink.export(trace);
102
+ }
103
+ catch (e) {
104
+ console.warn('[agentia] trace sink 投递失败:', e instanceof Error ? e.message : e);
105
+ }
106
+ }
107
+ // 结果映射:抛错 → 协议层成功 + isError(MCP 惯例;正向桥那头正好把 isError 转回抛错,
108
+ // 方向对称);string 原样进 text;其他 JSON 化进 text
109
+ if (!ok)
110
+ return { content: [{ type: 'text', text: String(content) }], isError: true };
111
+ const text = typeof content === 'string' ? content : stringifySafe(content);
112
+ return { content: [{ type: 'text', text }] };
113
+ }
114
+ /**
115
+ * 报文分派(两个传输共用)。返回 `null` = 通知,不应答。
116
+ * 协议范围只到 tools(见文件头);其余 method 一律 -32601,params 形状坏 → -32602。
117
+ */
118
+ async function dispatch(msg, signal, deps) {
119
+ if (typeof msg !== 'object' || msg === null || Array.isArray(msg)) {
120
+ return rpcError(null, -32600, '报文不是 JSON-RPC 对象');
121
+ }
122
+ const m = msg;
123
+ const id = typeof m.id === 'number' || typeof m.id === 'string' || m.id === null ? m.id : null;
124
+ if (typeof m.method !== 'string') {
125
+ return m.id === undefined ? null : rpcError(id, -32600, '报文缺 method 字段');
126
+ }
127
+ const method = m.method;
128
+ // 通知(含 notifications/initialized)一律不应答;无 id 的非通知报文不是请求
129
+ if (method.startsWith('notifications/') || m.id === undefined)
130
+ return null;
131
+ switch (method) {
132
+ case 'initialize':
133
+ return rpcResult(id, {
134
+ protocolVersion: DEFAULT_PROTOCOL_VERSION,
135
+ capabilities: { tools: {} },
136
+ serverInfo: deps.serverInfo,
137
+ });
138
+ case 'ping':
139
+ return rpcResult(id, {});
140
+ case 'tools/list':
141
+ // 不建 trace(见文件头):菜单查询不是一次「能力调用」
142
+ return rpcResult(id, {
143
+ tools: deps.tools.map((t) => ({
144
+ name: t.name,
145
+ description: t.description,
146
+ inputSchema: t.inputSchema,
147
+ })),
148
+ });
149
+ case 'tools/call': {
150
+ const params = m.params;
151
+ if (typeof params !== 'object' || params === null || Array.isArray(params)) {
152
+ return rpcError(id, -32602, 'tools/call 的 params 必须是对象({ name, arguments? })');
153
+ }
154
+ const name = params.name;
155
+ if (typeof name !== 'string' || name === '') {
156
+ return rpcError(id, -32602, 'tools/call 的 params.name 必须是非空字符串');
157
+ }
158
+ const rawArgs = params.arguments;
159
+ if (rawArgs !== undefined &&
160
+ (typeof rawArgs !== 'object' || rawArgs === null || Array.isArray(rawArgs))) {
161
+ return rpcError(id, -32602, 'tools/call 的 params.arguments 必须是对象');
162
+ }
163
+ const tool = deps.tools.find((t) => t.name === name);
164
+ if (!tool) {
165
+ return rpcError(id, -32602, `未知工具: ${name}`);
166
+ }
167
+ // 入参契约与引擎**同一份校验器**(core/schema.ts,与 turn.ts 进方法体之前那次同源):
168
+ // 反向桥若跳过它,同一份 @Tool 就会有两条契约 —— 引擎那边缺必填项记 errorKind
169
+ // 'invalid_input' 且不进方法体,这边却把畸形入参一路带进副作用。MCP 规范把
170
+ // 「未知工具 / 入参不合法」都归为协议错误(Invalid params),故回 -32602 而非
171
+ // result.isError(后者留给「工具真执行了但失败了」)。
172
+ const badInput = validateJsonSchema(tool.inputSchema, rawArgs ?? {});
173
+ if (badInput !== null) {
174
+ return rpcError(id, -32602, `工具 ${name} 的入参不满足 inputSchema:${badInput}`);
175
+ }
176
+ return rpcResult(id, await callTool(tool, (rawArgs ?? {}), signal, deps));
177
+ }
178
+ default:
179
+ return rpcError(id, -32601, `method not found: ${method}`);
180
+ }
181
+ }
182
+ /**
183
+ * 把 app 的能力菜单暴露成 MCP server。两个传输都只用标准库:
184
+ * - **stdio**:stdin/stdout 换行分隔 JSON-RPC(每行一个完整报文);日志只去 stderr;
185
+ * - **StreamableHTTP**:POST 收 JSON-RPC,应答 `application/json`(简单应答不上 SSE);
186
+ * `initialize` 响应发 `mcp-session-id` 头(生成的 uuid),后续请求带了就接受、不带也服务
187
+ * (无状态 server,**宽容是有意的**);GET(server→client 流)→ 405;DELETE → 200;
188
+ * 客户端断连会中止该次工具调用的 `signal`。
189
+ *
190
+ * 本函数只做**装配**:构造期校验 → 状态(惰性 client / 在飞句柄表)→ 组装 `McpCore`
191
+ * → 交给选中的传输。协议与执行见 `dispatch` / `callTool`,传输见各自的文件。
192
+ */
193
+ export function createMcpServer(app, opts) {
194
+ if (typeof app !== 'object' || app === null || !Array.isArray(app.tools)) {
195
+ throw new TypeError('createMcpServer:app 必须是鸭子类型 { tools: AgentTool[] }(如 AgentApp)');
196
+ }
197
+ if (opts.transport !== 'stdio' && opts.transport !== 'http') {
198
+ throw new TypeError(`createMcpServer:transport 只认 'stdio' | 'http',收到 ${String(opts.transport)}`);
199
+ }
200
+ /** client 惰性构造(见 McpServerOptions.client):第一次 tools/call 才建 */
201
+ let cachedClient;
202
+ const deps = {
203
+ tools: app.tools,
204
+ serverInfo: { name: opts.name ?? 'agentia', version: SERVER_INFO_VERSION },
205
+ sinks: opts.sinks ?? [],
206
+ toolTimeoutMs: opts.toolTimeoutMs,
207
+ clientOf: () => {
208
+ cachedClient ??= opts.client ?? createAnthropicClient();
209
+ return cachedClient;
210
+ },
211
+ };
212
+ /** 在飞 tools/call 的中止句柄 —— close() 中止它们(服务器关闭 = 调用方没了) */
213
+ const inFlight = new Set();
214
+ const core = {
215
+ dispatch: (msg, signal) => dispatch(msg, signal, deps),
216
+ rpcError,
217
+ trackCall: (ac) => {
218
+ inFlight.add(ac);
219
+ return ac.signal;
220
+ },
221
+ untrackCall: (ac) => {
222
+ inFlight.delete(ac);
223
+ },
224
+ abortAll: () => {
225
+ for (const ac of inFlight)
226
+ ac.abort();
227
+ inFlight.clear();
228
+ },
229
+ };
230
+ return opts.transport === 'stdio' ? startStdioTransport(core) : startHttpTransport(core, opts);
231
+ }
232
+ //# sourceMappingURL=mcp-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-server.js","sourceRoot":"","sources":["../../src/engine/mcp-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import type { ToolUseBlock } from '../core/message.js';
2
+ import type { AgentTool } from '../core/tool.js';
3
+ /**
4
+ * 菜单签名:`names` 给人读(截断 + 省略标记),`hash` 给比对。
5
+ *
6
+ * 摘要材料 = 排序后的 `名字 \0 规范 schema`,所以①与菜单顺序无关(`tools` 数组顺序
7
+ * 变了不算漂移);②任一工具的 schema 变了就算漂移。用 sha1 截 12 位而不是自造哈希:
8
+ * 碰撞在这里是**静默失效**(两段不同的菜单判成同一段),不值得为省一个内置模块自造。
9
+ */
10
+ export declare function menuSignature(tools: readonly AgentTool[]): {
11
+ names: string;
12
+ hash: string;
13
+ };
14
+ /** 续跑时的菜单漂移判定结果 */
15
+ export interface MenuDrift {
16
+ /** 未决 tool_use 引用的、当前菜单里**没有**的工具名(去重、排序) */
17
+ missing: string[];
18
+ /** 这些名字对应的 tool_use_id(与 `missing` 不同长:同名可被引用多次) */
19
+ toolUseIds: string[];
20
+ }
21
+ /**
22
+ * 续跑入口的漂移判定:未决 tool_use 的名字 vs 当前菜单。
23
+ *
24
+ * `resultSchema` 在场时把隐藏的 `submit_result` 当**在册** —— 它是引擎内部追加的提交工具、
25
+ * 从不进 `args.tools`,忘了这一条会把每一次「带 resultSchema 的续跑」都误报成漂移
26
+ * (这正是本函数要 `resultSchema` 而不是让调用方传名字清单的原因:口径只有一份)。
27
+ *
28
+ * 只看**名字**:schema 漂移由签名比对覆盖(`menuSignature` 的 hash 覆盖 schema),
29
+ * 而「输入还合不合法」不该在这里判 —— 那段输入在挂起时**也没**校验过,拿它当漂移证据
30
+ * 是误报(模型本就可以给一份不合法的入参)。
31
+ */
32
+ export declare function detectMenuDrift(uses: readonly ToolUseBlock[], tools: readonly AgentTool[], opts?: {
33
+ resultSchema?: unknown;
34
+ }): MenuDrift;
@@ -0,0 +1,81 @@
1
+ /**
2
+ * 菜单漂移(R8 候选 3 / durable 调研 §4.1 + §6 候选 3)—— **纯判定**,两个函数各管一件事:
3
+ *
4
+ * ① `menuSignature`:把「这一段的菜单长什么样」压成两样东西 —— 给人读的名字清单
5
+ * 与给比对用的摘要。落 run 根(见 loop.ts),与 `prompts.versions` 同一动机:
6
+ * 「质量回归能定位到具体菜单版本」。摘要**覆盖输入 schema**,所以「工具还在、
7
+ * 签名变了」也能被两段签名比出来 —— ⚠️ 但比对是**离线的**:跨段 hash 对比由
8
+ * 人 / 外部工具做(trace 里两段 run 根各有一份签名),续跑入口**不自动比**;
9
+ * 自动的只有 ② 的名字级检查。
10
+ * ② `detectMenuDrift`:**续跑**时未决 tool_use 引用的工具,在当前菜单里还找不找得到。
11
+ *
12
+ * 为什么单独成件:这两个判定是「菜单变了」这件事的**唯一口径**,混在 900 行的 loop.ts
13
+ * 里只能靠注释约定(本仓正在把这类纯判定陆续外移,见 AGENTS.md 的 engine 段)。
14
+ *
15
+ * ⚠️ 与「模型中途编了一个不存在的工具名」不是一回事:那个是幻觉、模型拿一句
16
+ * `unknown tool` 就能自我修正(turn.ts 的既有路径,已钉现状);这里只管**续跑**时
17
+ * 名字对不上 —— 那是我们的部署动作把一条在飞 run 的意图作废了。两者的动作不同,
18
+ * 别把本件接进回合内的工具执行路径。
19
+ */
20
+ import { createHash } from 'node:crypto';
21
+ import { truncateWithMark } from '../core/json.js';
22
+ import { SUBMIT_RESULT } from './turn.js';
23
+ /** 名字清单的字符上限:trace 属性要读得懂,也要有界(菜单可能几十个工具) */
24
+ const NAMES_CAP = 512;
25
+ /**
26
+ * schema 的**规范序列化**:对象键排序、递归、丢掉 `undefined`。
27
+ *
28
+ * 只做「同一份 schema 得到同一个串」,**不做语义等价** —— `{a:1}` 与 `{a:1,required:[]}`
29
+ * 是两份不同的 schema,就该算出不同签名。漂移的判据宁可敏感(多报一次签名变化),
30
+ * 不要漏(漏了就是 G4 那个静默)。
31
+ */
32
+ function canonical(value) {
33
+ if (value === null || typeof value !== 'object')
34
+ return JSON.stringify(value) ?? 'null';
35
+ if (Array.isArray(value))
36
+ return `[${value.map(canonical).join(',')}]`;
37
+ const entries = Object.entries(value)
38
+ .filter(([, v]) => v !== undefined)
39
+ .sort(([a], [b]) => (a < b ? -1 : 1));
40
+ return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonical(v)}`).join(',')}}`;
41
+ }
42
+ /**
43
+ * 菜单签名:`names` 给人读(截断 + 省略标记),`hash` 给比对。
44
+ *
45
+ * 摘要材料 = 排序后的 `名字 \0 规范 schema`,所以①与菜单顺序无关(`tools` 数组顺序
46
+ * 变了不算漂移);②任一工具的 schema 变了就算漂移。用 sha1 截 12 位而不是自造哈希:
47
+ * 碰撞在这里是**静默失效**(两段不同的菜单判成同一段),不值得为省一个内置模块自造。
48
+ */
49
+ export function menuSignature(tools) {
50
+ const sorted = [...tools].sort((a, b) => (a.name < b.name ? -1 : 1));
51
+ const names = truncateWithMark(sorted.map((t) => t.name).join(','), NAMES_CAP);
52
+ const material = sorted.map((t) => `${t.name}\u0000${canonical(t.inputSchema)}`).join('\u0001');
53
+ const hash = createHash('sha1').update(material).digest('hex').slice(0, 12);
54
+ return { names, hash };
55
+ }
56
+ /**
57
+ * 续跑入口的漂移判定:未决 tool_use 的名字 vs 当前菜单。
58
+ *
59
+ * `resultSchema` 在场时把隐藏的 `submit_result` 当**在册** —— 它是引擎内部追加的提交工具、
60
+ * 从不进 `args.tools`,忘了这一条会把每一次「带 resultSchema 的续跑」都误报成漂移
61
+ * (这正是本函数要 `resultSchema` 而不是让调用方传名字清单的原因:口径只有一份)。
62
+ *
63
+ * 只看**名字**:schema 漂移由签名比对覆盖(`menuSignature` 的 hash 覆盖 schema),
64
+ * 而「输入还合不合法」不该在这里判 —— 那段输入在挂起时**也没**校验过,拿它当漂移证据
65
+ * 是误报(模型本就可以给一份不合法的入参)。
66
+ */
67
+ export function detectMenuDrift(uses, tools, opts = {}) {
68
+ const known = new Set(tools.map((t) => t.name));
69
+ if (opts.resultSchema !== undefined)
70
+ known.add(SUBMIT_RESULT);
71
+ const missing = new Set();
72
+ const toolUseIds = [];
73
+ for (const use of uses) {
74
+ if (known.has(use.name))
75
+ continue;
76
+ missing.add(use.name);
77
+ toolUseIds.push(use.id);
78
+ }
79
+ return { missing: [...missing].sort(), toolUseIds };
80
+ }
81
+ //# sourceMappingURL=menu-drift.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"menu-drift.js","sourceRoot":"","sources":["../../src/engine/menu-drift.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAGnD,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE1C,6CAA6C;AAC7C,MAAM,SAAS,GAAG,GAAG,CAAC;AAEtB;;;;;;GAMG;AACH,SAAS,SAAS,CAAC,KAAc;IAC/B,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC;IACxF,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IACvE,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC;SAC7D,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC;SAClC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxC,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;AAC1F,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,KAA2B;IACvD,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrE,MAAM,KAAK,GAAG,gBAAgB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,SAAS,CAAC,CAAC;IAC/E,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,SAAS,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAChG,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC5E,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AACzB,CAAC;AAUD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAC7B,IAA6B,EAC7B,KAA2B,EAC3B,IAAI,GAA+B,EAAE;IAErC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAChD,IAAI,IAAI,CAAC,YAAY,KAAK,SAAS;QAAE,KAAK,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IAC9D,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,SAAS;QAClC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACtB,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC1B,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,UAAU,EAAE,CAAC;AACtD,CAAC"}
@@ -7,7 +7,8 @@ import type { ContextPolicy } from './types.js';
7
7
  * 2. 超预算:先 **context editing**(trimToolPairs 丢旧工具对,不额外调模型);
8
8
  * 若裁剪后回到预算内即止。
9
9
  * 3. 仍超预算且有 summarize(compaction 摘要器)→ 压缩:旧前缀做成摘要,只留最近 keepRecent 条。
10
- * 距上次压缩 < compactEvery 个回合则跳过(滞回,防抖)。
10
+ * 距上次**成功**压缩 < compactEvery 个回合则跳过(滞回,防抖);压缩抛错**不**记额度
11
+ * (失败不该连带吃掉后面几回合的重试机会,见 beforeTurn 里的注)。
11
12
  * 4. 无 summarize 时 compaction 不可用,编辑已是上限(返回裁剪后消息)。
12
13
  *
13
14
  * summarize 未提供时 policy 不会自己调模型 —— 框架不替你造 token(spec:不主动黑名单,