@migor/agentia 0.9.4 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/CHANGELOG.md +1280 -4
  2. package/README.en.md +61 -3
  3. package/README.md +66 -4
  4. package/dist/AGENTS.md +330 -45
  5. package/dist/container/container.js +21 -0
  6. package/dist/container/container.js.map +1 -1
  7. package/dist/core/limits.d.ts +101 -14
  8. package/dist/core/limits.js +103 -13
  9. package/dist/core/limits.js.map +1 -1
  10. package/dist/core/line-framing.d.ts +36 -0
  11. package/dist/core/line-framing.js +39 -0
  12. package/dist/core/line-framing.js.map +1 -0
  13. package/dist/core/message.d.ts +11 -0
  14. package/dist/core/run.d.ts +68 -4
  15. package/dist/core/run.js +27 -1
  16. package/dist/core/run.js.map +1 -1
  17. package/dist/core/schema.js +59 -8
  18. package/dist/core/schema.js.map +1 -1
  19. package/dist/core/timeout.d.ts +43 -1
  20. package/dist/core/timeout.js +76 -3
  21. package/dist/core/timeout.js.map +1 -1
  22. package/dist/core/tool.d.ts +25 -1
  23. package/dist/core/trace.d.ts +40 -1
  24. package/dist/core/trace.js +26 -0
  25. package/dist/core/trace.js.map +1 -1
  26. package/dist/engine/budget.js +14 -0
  27. package/dist/engine/budget.js.map +1 -1
  28. package/dist/engine/defer.d.ts +49 -0
  29. package/dist/engine/defer.js +72 -0
  30. package/dist/engine/defer.js.map +1 -0
  31. package/dist/engine/errors.d.ts +23 -1
  32. package/dist/engine/errors.js +24 -0
  33. package/dist/engine/errors.js.map +1 -1
  34. package/dist/engine/forwarded.d.ts +14 -5
  35. package/dist/engine/forwarded.js +4 -1
  36. package/dist/engine/forwarded.js.map +1 -1
  37. package/dist/engine/loop-result.d.ts +65 -7
  38. package/dist/engine/loop-result.js +24 -5
  39. package/dist/engine/loop-result.js.map +1 -1
  40. package/dist/engine/loop.d.ts +2 -0
  41. package/dist/engine/loop.js +157 -17
  42. package/dist/engine/loop.js.map +1 -1
  43. package/dist/engine/mcp-server-http.d.ts +2 -0
  44. package/dist/engine/mcp-server-http.js +215 -0
  45. package/dist/engine/mcp-server-http.js.map +1 -0
  46. package/dist/engine/mcp-server-stdio.d.ts +15 -0
  47. package/dist/engine/mcp-server-stdio.js +106 -0
  48. package/dist/engine/mcp-server-stdio.js.map +1 -0
  49. package/dist/engine/mcp-server.d.ts +134 -0
  50. package/dist/engine/mcp-server.js +232 -0
  51. package/dist/engine/mcp-server.js.map +1 -0
  52. package/dist/engine/menu-drift.d.ts +34 -0
  53. package/dist/engine/menu-drift.js +81 -0
  54. package/dist/engine/menu-drift.js.map +1 -0
  55. package/dist/engine/policy.d.ts +2 -1
  56. package/dist/engine/policy.js +7 -1
  57. package/dist/engine/policy.js.map +1 -1
  58. package/dist/engine/replay.d.ts +2 -1
  59. package/dist/engine/replay.js +3 -2
  60. package/dist/engine/replay.js.map +1 -1
  61. package/dist/engine/resume-input.d.ts +10 -0
  62. package/dist/engine/resume-input.js +11 -0
  63. package/dist/engine/resume-input.js.map +1 -1
  64. package/dist/engine/run-config.d.ts +26 -2
  65. package/dist/engine/run-config.js +60 -0
  66. package/dist/engine/run-config.js.map +1 -1
  67. package/dist/engine/spec.d.ts +33 -4
  68. package/dist/engine/spec.js.map +1 -1
  69. package/dist/engine/tool-context.d.ts +10 -5
  70. package/dist/engine/tool-context.js +5 -1
  71. package/dist/engine/tool-context.js.map +1 -1
  72. package/dist/engine/tracer.d.ts +43 -5
  73. package/dist/engine/tracer.js +73 -10
  74. package/dist/engine/tracer.js.map +1 -1
  75. package/dist/engine/trimming.js +28 -2
  76. package/dist/engine/trimming.js.map +1 -1
  77. package/dist/engine/turn.d.ts +60 -3
  78. package/dist/engine/turn.js +180 -71
  79. package/dist/engine/turn.js.map +1 -1
  80. package/dist/engine/types.d.ts +89 -3
  81. package/dist/engine/types.js +38 -2
  82. package/dist/engine/types.js.map +1 -1
  83. package/dist/eval/export.d.ts +48 -0
  84. package/dist/eval/export.js +146 -0
  85. package/dist/eval/export.js.map +1 -0
  86. package/dist/eval/harvest.js +7 -2
  87. package/dist/eval/harvest.js.map +1 -1
  88. package/dist/index.d.ts +8 -5
  89. package/dist/index.js +6 -1
  90. package/dist/index.js.map +1 -1
  91. package/dist/integrations/adapter-options.d.ts +22 -0
  92. package/dist/integrations/adapter-options.js +51 -0
  93. package/dist/integrations/adapter-options.js.map +1 -1
  94. package/dist/integrations/anthropic.d.ts +4 -2
  95. package/dist/integrations/anthropic.js +27 -27
  96. package/dist/integrations/anthropic.js.map +1 -1
  97. package/dist/integrations/file-sink.d.ts +20 -0
  98. package/dist/integrations/file-sink.js +27 -0
  99. package/dist/integrations/file-sink.js.map +1 -0
  100. package/dist/integrations/mcp-http.d.ts +3 -3
  101. package/dist/integrations/mcp-http.js +1 -1
  102. package/dist/integrations/mcp-http.js.map +1 -1
  103. package/dist/integrations/mcp-protocol.d.ts +115 -0
  104. package/dist/integrations/mcp-protocol.js +69 -0
  105. package/dist/integrations/mcp-protocol.js.map +1 -0
  106. package/dist/integrations/mcp-stdio.d.ts +3 -3
  107. package/dist/integrations/mcp-stdio.js +17 -3
  108. package/dist/integrations/mcp-stdio.js.map +1 -1
  109. package/dist/integrations/mcp.d.ts +6 -95
  110. package/dist/integrations/mcp.js +24 -66
  111. package/dist/integrations/mcp.js.map +1 -1
  112. package/dist/integrations/metrics-otlp.js +52 -7
  113. package/dist/integrations/metrics-otlp.js.map +1 -1
  114. package/dist/integrations/metrics-render.d.ts +15 -0
  115. package/dist/integrations/metrics-render.js +79 -2
  116. package/dist/integrations/metrics-render.js.map +1 -1
  117. package/dist/integrations/metrics-state.d.ts +99 -1
  118. package/dist/integrations/metrics-state.js +128 -4
  119. package/dist/integrations/metrics-state.js.map +1 -1
  120. package/dist/integrations/metrics.d.ts +62 -6
  121. package/dist/integrations/metrics.js +49 -8
  122. package/dist/integrations/metrics.js.map +1 -1
  123. package/dist/integrations/openai.js +9 -31
  124. package/dist/integrations/openai.js.map +1 -1
  125. package/dist/integrations/otlp.d.ts +3 -3
  126. package/dist/integrations/report.d.ts +7 -0
  127. package/dist/integrations/report.js +20 -1
  128. package/dist/integrations/report.js.map +1 -1
  129. package/dist/runtime/memory.d.ts +61 -1
  130. package/dist/runtime/memory.js +47 -7
  131. package/dist/runtime/memory.js.map +1 -1
  132. package/dist/runtime/run.d.ts +12 -2
  133. package/dist/runtime/run.js +126 -17
  134. package/dist/runtime/run.js.map +1 -1
  135. package/dist/store/fsStore.js +10 -4
  136. package/dist/store/fsStore.js.map +1 -1
  137. package/dist/store/record.d.ts +41 -0
  138. package/dist/store/record.js +54 -0
  139. package/dist/store/record.js.map +1 -0
  140. package/dist/store/redisStore.d.ts +24 -1
  141. package/dist/store/redisStore.js +42 -7
  142. package/dist/store/redisStore.js.map +1 -1
  143. package/dist/store/sqliteStore.d.ts +23 -0
  144. package/dist/store/sqliteStore.js +103 -8
  145. package/dist/store/sqliteStore.js.map +1 -1
  146. package/dist/store/store.d.ts +65 -2
  147. package/dist/store/store.js +5 -2
  148. package/dist/store/store.js.map +1 -1
  149. package/dist/toolkit/capability-cycles.d.ts +50 -0
  150. package/dist/toolkit/capability-cycles.js +88 -0
  151. package/dist/toolkit/capability-cycles.js.map +1 -0
  152. package/dist/toolkit/capability-slice.d.ts +86 -0
  153. package/dist/toolkit/capability-slice.js +98 -0
  154. package/dist/toolkit/capability-slice.js.map +1 -0
  155. package/dist/toolkit/module.d.ts +7 -1
  156. package/dist/toolkit/module.js +65 -32
  157. package/dist/toolkit/module.js.map +1 -1
  158. package/dist/transport/approval-policy.d.ts +1 -1
  159. package/dist/transport/approval-policy.js +8 -4
  160. package/dist/transport/approval-policy.js.map +1 -1
  161. package/dist/transport/approval-supervisor.d.ts +73 -0
  162. package/dist/transport/approval-supervisor.js +252 -0
  163. package/dist/transport/approval-supervisor.js.map +1 -0
  164. package/dist/transport/async.d.ts +159 -56
  165. package/dist/transport/async.js +627 -269
  166. package/dist/transport/async.js.map +1 -1
  167. package/dist/transport/drain-gate.d.ts +6 -2
  168. package/dist/transport/drain-gate.js +14 -2
  169. package/dist/transport/drain-gate.js.map +1 -1
  170. package/dist/transport/http-endpoints.d.ts +151 -0
  171. package/dist/transport/http-endpoints.js +480 -0
  172. package/dist/transport/http-endpoints.js.map +1 -0
  173. package/dist/transport/http-io.d.ts +66 -0
  174. package/dist/transport/http-io.js +145 -0
  175. package/dist/transport/http-io.js.map +1 -0
  176. package/dist/transport/http-route.d.ts +10 -3
  177. package/dist/transport/http-route.js +18 -3
  178. package/dist/transport/http-route.js.map +1 -1
  179. package/dist/transport/http-shapes.d.ts +11 -0
  180. package/dist/transport/http-shapes.js +31 -1
  181. package/dist/transport/http-shapes.js.map +1 -1
  182. package/dist/transport/http.d.ts +22 -35
  183. package/dist/transport/http.js +98 -476
  184. package/dist/transport/http.js.map +1 -1
  185. package/dist/transport/owner-id.d.ts +29 -0
  186. package/dist/transport/owner-id.js +46 -0
  187. package/dist/transport/owner-id.js.map +1 -0
  188. package/dist/transport/owner-liveness.d.ts +8 -0
  189. package/dist/transport/owner-liveness.js +56 -0
  190. package/dist/transport/owner-liveness.js.map +1 -0
  191. package/dist/transport/resume-policy.d.ts +48 -10
  192. package/dist/transport/resume-policy.js +25 -10
  193. package/dist/transport/resume-policy.js.map +1 -1
  194. package/dist/transport/resume-scanner.d.ts +55 -0
  195. package/dist/transport/resume-scanner.js +219 -0
  196. package/dist/transport/resume-scanner.js.map +1 -0
  197. package/dist/transport/scheduler.js +9 -23
  198. package/dist/transport/scheduler.js.map +1 -1
  199. package/dist/transport/signal-supervisor.d.ts +80 -0
  200. package/dist/transport/signal-supervisor.js +129 -0
  201. package/dist/transport/signal-supervisor.js.map +1 -0
  202. package/dist/transport/slot-pool.d.ts +9 -1
  203. package/dist/transport/slot-pool.js +9 -1
  204. package/dist/transport/slot-pool.js.map +1 -1
  205. package/dist/transport/task-events.d.ts +34 -3
  206. package/dist/transport/task-events.js +93 -5
  207. package/dist/transport/task-events.js.map +1 -1
  208. package/dist/transport/wake-policy.d.ts +50 -0
  209. package/dist/transport/wake-policy.js +43 -0
  210. package/dist/transport/wake-policy.js.map +1 -0
  211. package/package.json +14 -4
@@ -0,0 +1,480 @@
1
+ import { parseTraceparent } from '../core/trace.js';
2
+ import { TaskInputError, normalizeMessages } from '../engine/spec.js';
3
+ import { TaskApproveError, TaskCancelError, TaskEventError, TaskQueueFullError, TaskStreamError, } from './async.js';
4
+ import { PARSE_FAILED, RETRY_AFTER_SECONDS, errMessage, headerValue, methodNotAllowed, parseJsonBody, sendInternalError, sendJson, sendPrometheus, sendShuttingDown, } from './http-io.js';
5
+ import { parseApproveBody, parseEventBody, toHttpBody, toTaskSubmitBody } from './http-shapes.js';
6
+ import { sseWriter } from './sse.js';
7
+ /**
8
+ * **派发表**:`HttpRoute` 的每个 kind → 谁来做。
9
+ *
10
+ * 免鉴权组(`healthz` / `metrics`)与它们的 405 也在这张表里 —— 调用方
11
+ * (`http.ts` 的 `handler`)只在**调用本函数之前**决定要不要过鉴权闸,
12
+ * 表本身不认识「鉴权」这回事(见文件头注的分界)。
13
+ *
14
+ * `case` 顺序在这里**没有语义**(不像 `http-route.ts` 那边顺序即全部内容):
15
+ * `kind` 已经是一个被判定完的判别联合成员,落到这里只剩「谁接」。
16
+ * 这点值得单独说:**判定与派发是两件事**,把顺序陷阱留在判定那一侧,
17
+ * 才能让这张表纯粹到「读一眼就是全部」。
18
+ *
19
+ * 末尾 `default` 是**编译期穷尽性断言**:12 个 kind 全被 `case` 覆盖后
20
+ * `route` 收窄为 `never`,新增成员即 TS2322 —— 而不是被静默吞成 404。
21
+ * (拆分前这里是 `const residual: 'healthz' | 'metrics' | 'notFound' = route.kind`:
22
+ * 因为免鉴权组在上游提前 return 了,只能断言「剩下的只可能是这三个」。现在 12 条
23
+ * 全在一张表里,断言可以更强、也不必再解释「前两个已经回过了」。)
24
+ */
25
+ export async function handleRoute(route, ctx) {
26
+ switch (route.kind) {
27
+ case 'healthz':
28
+ return handleHealthz(ctx);
29
+ case 'metrics':
30
+ return handleMetrics(ctx);
31
+ case 'methodNotAllowed':
32
+ return methodNotAllowed(ctx.res, ctx.method, route.allowed);
33
+ case 'run':
34
+ return handleRun(ctx);
35
+ case 'submit':
36
+ return handleSubmit(ctx);
37
+ case 'approve':
38
+ return handleApprove(route.taskId, ctx);
39
+ case 'poll':
40
+ return handlePoll(route.taskId, ctx);
41
+ case 'cancel':
42
+ return handleCancel(route.taskId, ctx);
43
+ case 'taskEvent':
44
+ return handleTaskEvent(route.taskId, ctx);
45
+ case 'taskStream':
46
+ return handleTaskStream(route.taskId, ctx);
47
+ case 'badTaskId':
48
+ // 残缺的 % 转义会抛 URIError —— 是调用方的输入问题(400),不是服务端 500
49
+ return sendJson(ctx.res, 400, { error: 'taskId 不是合法的 URL 编码' });
50
+ case 'notFound':
51
+ return sendJson(ctx.res, 404, { error: `路径不存在: ${ctx.pathname}` });
52
+ default: {
53
+ const _never = route;
54
+ void _never;
55
+ }
56
+ }
57
+ }
58
+ // ────────────────────────────────────────────────────────────────────────────
59
+ // 免鉴权组:探针与指标抓取(不鉴权、停机中也照回),以及它们的 405
60
+ // ────────────────────────────────────────────────────────────────────────────
61
+ /** GET /healthz:进程活着就回 200;就绪与否看 `draining`,在飞口径见 `HealthResponse` */
62
+ function handleHealthz(ctx) {
63
+ const { res, runner, state, startedAt } = ctx;
64
+ sendJson(res, 200, {
65
+ ok: true,
66
+ inFlight: state.inFlightRuns + runner.inFlight,
67
+ uptimeMs: Date.now() - startedAt,
68
+ draining: state.draining,
69
+ // 挂起读数(配套 6):在飞 ≠ 在等 —— 「几条在睡、最早什么时候醒」是运维要看的那半
70
+ suspended: runner.suspendedSummary,
71
+ });
72
+ }
73
+ /**
74
+ * GET /metrics(G4):输出提供者 `render()` 的文本,与 `/healthz` 同档(不鉴权、
75
+ * 停机中仍可拉 —— 抓取端在集群内网)。
76
+ *
77
+ * `?.` 只为让类型收窄(这条分支只在配了 metrics 出口时可达),行为与原来一致。
78
+ * Content-Type 跟提供者走:metricsSink 在 openmetrics 模式下会声明
79
+ * `application/openmetrics-text`(带 exemplar 的文本拿 0.0.4 的头去发,
80
+ * 严格抓取端会解析失败);纯函数形态的提供者没有这个字段 ⇒ 回落 0.0.4。
81
+ */
82
+ function handleMetrics(ctx) {
83
+ const provider = ctx.metrics;
84
+ sendPrometheus(ctx.res, typeof provider === 'function' ? provider() : (provider?.render() ?? ''), typeof provider === 'function' ? undefined : provider?.contentType);
85
+ }
86
+ // ────────────────────────────────────────────────────────────────────────────
87
+ // 同步 RPC
88
+ // ────────────────────────────────────────────────────────────────────────────
89
+ /**
90
+ * POST /run:同步 RPC。body = `RunInput`(string / messages / `{prompt|text|messages}`),
91
+ * 走 `normalizeMessages` 规整后同步执行。
92
+ *
93
+ * 两个出口:
94
+ * - `Accept: text/event-stream` ⇒ SSE 逐帧下发(`text.delta` / `trace.event` /
95
+ * `run.end`,异常时 `error`);
96
+ * - 其余 ⇒ 一元 JSON(旧行为逐字不变)。
97
+ *
98
+ * 状态码:输入无法规整为 messages → 400;并发 run 超 `maxConcurrentRuns` → 503 +
99
+ * `Retry-After`;`status=failed` **也照返 200**(`rethrow:false` 语义:硬失败以
100
+ * `error` 字段返回,不用 HTTP 错误码表达)。
101
+ */
102
+ async function handleRun(ctx) {
103
+ const { req, res, app, state, maxConcurrentRuns, sseMaxBufferedBytes } = ctx;
104
+ if (state.draining) {
105
+ sendShuttingDown(req, res);
106
+ return;
107
+ }
108
+ const input = await parseJsonBody(req, res, ctx.maxBodyBytes);
109
+ if (input === PARSE_FAILED)
110
+ return;
111
+ let messages;
112
+ try {
113
+ messages = normalizeMessages(input);
114
+ }
115
+ catch (e) {
116
+ sendJson(res, 400, { error: errMessage(e) });
117
+ return;
118
+ }
119
+ // 并发闸门:body 已读完(连接可复用),只是暂时不给跑 —— 回 503 让调用方退避重试
120
+ if (state.inFlightRuns >= maxConcurrentRuns) {
121
+ res.setHeader('retry-after', RETRY_AFTER_SECONDS);
122
+ sendJson(res, 503, {
123
+ error: `并发 run 已达上限 ${maxConcurrentRuns},请稍后重试`,
124
+ });
125
+ return;
126
+ }
127
+ state.inFlightRuns++;
128
+ // 客户端中途断开 → 中止本次 run(省 token)。res 'close' 正常结束也会触发,
129
+ // 故以 writableEnded 区分:只有响应还没写完才算「断开」。
130
+ const runAc = new AbortController();
131
+ const onClose = () => {
132
+ if (!res.writableEnded)
133
+ runAc.abort();
134
+ };
135
+ res.once('close', onClose);
136
+ // 内容协商:`Accept: text/event-stream` → SSE 逐帧下发;否则一元 JSON(旧行为逐字不变)
137
+ const wantsSse = String(req.headers.accept ?? '').includes('text/event-stream');
138
+ // 入站链路(spec §9.2):W3C `traceparent` 头 → run 根的 links。
139
+ // 畸形/缺头一律静默当作「没有上游上下文」(parseTraceparent 统一判定)——
140
+ // 链路是观测行为,不该因为一个坏头把业务请求打成 400。
141
+ const traceContext = parseTraceparent(headerValue(req, 'traceparent'));
142
+ try {
143
+ if (wantsSse) {
144
+ const sse = sseWriter(res, {
145
+ maxBufferedBytes: sseMaxBufferedBytes,
146
+ // 下游积压超限 → 收口并中止本次 run(见 sseWriter 的背压说明)
147
+ onBackpressure: () => runAc.abort(),
148
+ });
149
+ const heartbeat = setInterval(() => sse.comment('ping'), 15_000);
150
+ heartbeat.unref?.();
151
+ // 登记收口函数:drain 时强制关闭(SSE 是长连,不关会把进程吊住)。
152
+ // 收口必须同时 abort 对应 run:sse.close() → res.end() 后 writableEnded 同步
153
+ // 变 true,上面的 onClose 守卫(`!writableEnded`)在 close 事件时不成立,
154
+ // 只 close 不 abort 会让 run 在后台继续烧 token(与背压收口路径的
155
+ // onBackpressure → abort 对齐;run 已正常结束时 abort 是无害 no-op)。
156
+ const closeSse = () => {
157
+ clearInterval(heartbeat);
158
+ runAc.abort();
159
+ sse.close();
160
+ };
161
+ // `cutsRun: true` —— 收口连带中止本次 run(见 `OpenSseHandle`:drain 的返回值靠它如实)
162
+ const sseHandle = { close: closeSse, cutsRun: true };
163
+ state.openSse.add(sseHandle);
164
+ try {
165
+ // rethrow:false —— 与 AsyncRunner 对齐:硬失败也以 run.end 下发(status/error 字段)
166
+ const out = await app.run(messages, {
167
+ rethrow: false,
168
+ signal: runAc.signal,
169
+ onText: (delta) => sse.event('text.delta', { text: delta }),
170
+ // 增量记账出口(F3 当初判「架构代价大、后置」的那半):既有三帧
171
+ //(text.delta / run.end / error)逐字不变,这里只**追加**一族帧 ——
172
+ // 不认识 `trace.event` 的老客户端行为零变化。
173
+ // 帧名取一族(body 里的 type 区分)而不是每类型一帧:将来加新事件类型时,
174
+ // 老客户端只是漏掉一种 type,而不是漏掉一种**帧名**(后者更隐蔽)。
175
+ onTraceEvent: (e) => sse.event('trace.event', e),
176
+ ...(traceContext !== undefined ? { traceContext } : {}),
177
+ });
178
+ sse.event('run.end', toHttpBody(out));
179
+ }
180
+ catch (e) {
181
+ // 流已开(200 与头已发出)→ 只能以 error 事件收尾,不能再改 HTTP 状态码
182
+ sse.event('error', { message: errMessage(e) });
183
+ }
184
+ finally {
185
+ state.openSse.delete(sseHandle);
186
+ closeSse();
187
+ }
188
+ return;
189
+ }
190
+ // rethrow:false —— 与 AsyncRunner 对齐:硬失败也以 status/error 字段返回 200
191
+ const out = await app.run(messages, {
192
+ rethrow: false,
193
+ signal: runAc.signal,
194
+ ...(traceContext !== undefined ? { traceContext } : {}),
195
+ });
196
+ sendJson(res, 200, toHttpBody(out));
197
+ }
198
+ finally {
199
+ res.off('close', onClose);
200
+ state.inFlightRuns--;
201
+ }
202
+ }
203
+ /**
204
+ * POST /tasks:异步任务。body `{ input, idempotencyKey?, options? }`
205
+ * → `runner.submit` → 202 `TaskRecord`(queued,幂等键去重照常生效)。
206
+ */
207
+ async function handleSubmit(ctx) {
208
+ const { req, res, runner, state } = ctx;
209
+ // 停机中不再接单(含直接 drain 了 runner 的情况);GET /tasks/<id> 不受影响
210
+ if (state.draining || runner.isDraining) {
211
+ sendShuttingDown(req, res);
212
+ return;
213
+ }
214
+ const body = await parseJsonBody(req, res, ctx.maxBodyBytes);
215
+ if (body === PARSE_FAILED)
216
+ return;
217
+ const submitBody = toTaskSubmitBody(body);
218
+ if (!submitBody) {
219
+ sendJson(res, 400, { error: 'body 需为 { input, idempotencyKey?, options? }' });
220
+ return;
221
+ }
222
+ // 入站链路(spec §9.2):body 显式给的 `options.traceContext` 优先,否则取
223
+ // `traceparent` 头。它随 `spec.options` 落进 TaskRecord —— 所以**跨进程续跑**
224
+ // 的那次 run(另一个进程 `resumePending` 接着跑)也带得上,关联不断链。
225
+ const submitTrace = parseTraceparent(headerValue(req, 'traceparent'));
226
+ let rec;
227
+ try {
228
+ rec = runner.submit(submitBody.input, {
229
+ ...(submitBody.idempotencyKey !== undefined
230
+ ? { idempotencyKey: submitBody.idempotencyKey }
231
+ : {}),
232
+ options: submitBody.options?.traceContext !== undefined
233
+ ? submitBody.options
234
+ : {
235
+ ...submitBody.options,
236
+ ...(submitTrace !== undefined ? { traceContext: submitTrace } : {}),
237
+ },
238
+ source: 'http',
239
+ });
240
+ }
241
+ catch (e) {
242
+ // `submit` 是同步的:入参校验失败与 store 落库故障从同一个 catch 出去。
243
+ // 前者是调用方的错(400 + 原因),后者是服务端的错 —— 必须走与 500 路径同一套
244
+ // `exposeErrors` 策略,否则一次磁盘/Redis 故障会被报成「你参数写错了」,
245
+ // 并把内部错误消息原样回给调用方(500/401 路径都不这么干)。
246
+ if (e instanceof TaskInputError) {
247
+ sendJson(res, 400, { error: errMessage(e) });
248
+ return;
249
+ }
250
+ // 排队段满(T5):**不是**调用方的错,回 503 让它退避重试 —— 与 `maxConcurrentRuns`
251
+ // 那一支同款(同一种情况在「已跑满」与「排满了」两个阶段各有一道闸,状态码就该一致)。
252
+ // 放在 `TaskInputError` 之后:入参不合法仍是 400(那是它能自己修好的),
253
+ // 排在 `isDraining` 之前:停机文案说「服务正在优雅停机」,而这条说的是「排队满了」——
254
+ // 两句话对运维的指示不同(前者别再来,后者稍后再来),不能混成一句。
255
+ if (e instanceof TaskQueueFullError) {
256
+ res.setHeader('retry-after', RETRY_AFTER_SECONDS);
257
+ sendJson(res, 503, { error: errMessage(e) });
258
+ return;
259
+ }
260
+ // 上面的停机闸门与 `submit` 之间隔着一次 `await parseJsonBody`:读 body 期间
261
+ // drain 可能刚开始,此时 submit 抛「正在优雅停机」。这是 503 而不是 500 ——
262
+ // 调用方应该退避重试,跟闸门本身回的是同一句话。
263
+ if (runner.isDraining) {
264
+ sendShuttingDown(req, res);
265
+ return;
266
+ }
267
+ sendInternalError(res, e, ctx.exposeErrors);
268
+ return;
269
+ }
270
+ sendJson(res, 202, rec);
271
+ }
272
+ // ────────────────────────────────────────────────────────────────────────────
273
+ // 任务控制面(四条:approve / poll / cancel / events —— 停机中仍可用)
274
+ //
275
+ // 它们与 POST /run、POST /tasks 的关键差别是:**停机窗口里照常受理**。
276
+ // 理由是同一个 —— 挂起的任务只有人能推进(approve / events),而停机窗口正是
277
+ // 最想取消在飞任务的时候(cancel);GET 轮询更是调用方拿回在飞任务结果的唯一途径。
278
+ // ────────────────────────────────────────────────────────────────────────────
279
+ /**
280
+ * POST /tasks/<id>/approve(HITL):审批挂起的任务。body
281
+ * `{ decisions: { <tool_use_id>: { approved, reason? } }, decidedBy? }`。
282
+ * 任务不存在 → 404;不在 suspended 状态 → 409;body 非法 → 400。
283
+ */
284
+ async function handleApprove(taskId, ctx) {
285
+ const { req, res, runner } = ctx;
286
+ const body = await parseJsonBody(req, res, ctx.maxBodyBytes);
287
+ if (body === PARSE_FAILED)
288
+ return;
289
+ const parsed = parseApproveBody(body);
290
+ if (!parsed) {
291
+ sendJson(res, 400, {
292
+ error: 'body 需为 { decisions: { <tool_use_id>: { approved: boolean, reason?: string } }, decidedBy?: string }',
293
+ });
294
+ return;
295
+ }
296
+ try {
297
+ const rec = await runner.approve(taskId, parsed.decisions, parsed.decidedBy !== undefined ? { decidedBy: parsed.decidedBy } : {});
298
+ sendJson(res, 200, rec);
299
+ }
300
+ catch (e) {
301
+ // 404(任务不存在)/ 409(状态不对)是调用方语义;其余按内部错误处理
302
+ if (e instanceof TaskApproveError) {
303
+ sendJson(res, e.status, { error: errMessage(e) });
304
+ return;
305
+ }
306
+ sendInternalError(res, e, ctx.exposeErrors);
307
+ }
308
+ }
309
+ /** GET /tasks/<id>:轮询任务记录 → 200 `TaskRecord`;不存在 → 404。停机中照常可轮询 */
310
+ async function handlePoll(taskId, ctx) {
311
+ const rec = await ctx.runner.poll(taskId); // MaybePromise:异步 store 下必须 await
312
+ if (!rec) {
313
+ sendJson(ctx.res, 404, { error: `task 不存在: ${taskId}` });
314
+ return;
315
+ }
316
+ sendJson(ctx.res, 200, rec);
317
+ }
318
+ /**
319
+ * POST /tasks/<id>/cancel:取消一个任务(在跑的真中断 / 在睡的不再醒 / 在排队的绝不起跑)。
320
+ * 无 body。不存在 → 404;已终态、或在跑的 run 不在本进程 → 409。
321
+ */
322
+ async function handleCancel(taskId, ctx) {
323
+ try {
324
+ const rec = await ctx.runner.cancel(taskId); // MaybePromise:异步 store 下必须 await
325
+ sendJson(ctx.res, 200, rec);
326
+ }
327
+ catch (e) {
328
+ // 404(不存在)/ 409(已终态、或在跑的 run 不在本进程)是调用方语义
329
+ if (e instanceof TaskCancelError) {
330
+ sendJson(ctx.res, e.status, { error: errMessage(e) });
331
+ return;
332
+ }
333
+ sendInternalError(ctx.res, e, ctx.exposeErrors);
334
+ }
335
+ }
336
+ /**
337
+ * POST /tasks/<id>/events(run 事件投入口,2026-09-28 ⑥):投一个事件给挂起的任务
338
+ * —— 事件落 `pendingEvents`、状态回 running,先落库再派发,醒来进消息历史。
339
+ * body `{ eventId?, type, payload }`(白名单,全是字符串,多一个字段 → 400);
340
+ * 不存在 → 404;不在 suspended 状态、或同 eventId 重复投递 → 409。
341
+ */
342
+ async function handleTaskEvent(taskId, ctx) {
343
+ const { req, res, runner } = ctx;
344
+ const body = await parseJsonBody(req, res, ctx.maxBodyBytes);
345
+ if (body === PARSE_FAILED)
346
+ return;
347
+ const event = parseEventBody(body);
348
+ if (!event) {
349
+ sendJson(res, 400, {
350
+ error: 'body 需为 { eventId?: string, type: string, payload: string }(白名单:多一个字段即拒)',
351
+ });
352
+ return;
353
+ }
354
+ try {
355
+ const rec = await runner.signalTask(taskId, event);
356
+ sendJson(res, 200, rec);
357
+ }
358
+ catch (e) {
359
+ // 404(不存在)/ 409(不在挂起态、同 eventId 重复投递)是调用方语义
360
+ if (e instanceof TaskEventError) {
361
+ sendJson(res, e.status, { error: errMessage(e) });
362
+ return;
363
+ }
364
+ sendInternalError(res, e, ctx.exposeErrors);
365
+ }
366
+ }
367
+ /**
368
+ * GET /tasks/<id>/stream:订阅某任务的记账事件流(SSE)。帧名与语义见
369
+ * `docs/usage-guide.md §7`:`trace.event`(带 SSE `id:` 供断线续订)、
370
+ * `stream.truncated` / `stream.unavailable` / `stream.closed` / `task.end`。
371
+ *
372
+ * 与 `/run` 的 SSE 那条**气质不同**(背压时处置相反):
373
+ * 这条流的读者是**旁观者**,所以背压只收口本流(不 abort 任务)——
374
+ * 任务不该因为看的人慢而变慢。
375
+ */
376
+ async function handleTaskStream(taskId, ctx) {
377
+ const { req, res, runner, state, sseMaxBufferedBytes } = ctx;
378
+ // 先查一次记录:任务不存在要回 **404**,而 SSE 一旦写出响应头状态码就定死 200 了
379
+ //(见 sse.ts 的语义约束),所以「存不存在」必须在建流之前问。
380
+ const known = await runner.poll(taskId);
381
+ if (!known) {
382
+ sendJson(res, 404, { error: `task 不存在: ${taskId}` });
383
+ return;
384
+ }
385
+ // 续订锚点:SSE 标准头 `Last-Event-ID` 优先,其次 `?from=`(非 EventSource 的客户端用)
386
+ const fromRaw = headerValue(req, 'last-event-id') ??
387
+ new URL(req.url ?? '/', 'http://localhost').searchParams.get('from');
388
+ const from = fromRaw === null || fromRaw === undefined ? undefined : Number(fromRaw);
389
+ let unsubscribe = () => { };
390
+ let finished = false;
391
+ let heartbeat;
392
+ // 登记的收口句柄(下面三处 add / delete 都用**同一个对象**):`closeStream` 自己也要把
393
+ // 这条从 openSse 摘掉(客户端断连、任务终态、背压都走它)。句柄先声明、赋值在 add 处 ——
394
+ // `closeStream` 只会在 `runner.streamTask`(本函数尾部)之后被调用,那时它一定已就位。
395
+ let sseHandle;
396
+ const closeStream = () => {
397
+ if (finished)
398
+ return;
399
+ finished = true;
400
+ if (heartbeat)
401
+ clearInterval(heartbeat);
402
+ unsubscribe();
403
+ if (sseHandle !== undefined)
404
+ state.openSse.delete(sseHandle);
405
+ sse.close();
406
+ };
407
+ const sse = sseWriter(res, {
408
+ maxBufferedBytes: sseMaxBufferedBytes,
409
+ // ⚠️ 背压**不 abort 任务**:这条流的读者是**旁观者**,任务不该因为看的人慢而变慢。
410
+ //(对比 /run 的 SSE:那里的下游就是 run 的所有者,背压等于「别继续烧 token 了」。)
411
+ onBackpressure: () => closeStream(),
412
+ });
413
+ heartbeat = setInterval(() => sse.comment('ping'), 15_000);
414
+ heartbeat.unref?.();
415
+ // 登记收口函数:drain 时强制关闭(SSE 是长连,不关会把进程吊住)。
416
+ // `cutsRun: false` —— 关掉本流只是旁观者退订,**不**中止任务、也不损失工作(见 `OpenSseHandle`)
417
+ sseHandle = { close: closeStream, cutsRun: false };
418
+ state.openSse.add(sseHandle);
419
+ // 客户端断开 ⇒ 退订(不退订订阅者会一直挂在 runner 的表里)
420
+ res.once('close', () => {
421
+ if (!res.writableEnded)
422
+ closeStream();
423
+ });
424
+ const write = (frame) => {
425
+ if (finished)
426
+ return;
427
+ switch (frame.type) {
428
+ case 'trace':
429
+ // 帧名一族 + SSE `id:` 给流序号 ⇒ 断线重连带 Last-Event-ID 就能续上
430
+ sse.event('trace.event', frame.event, String(frame.index));
431
+ return;
432
+ case 'truncated':
433
+ sse.event('stream.truncated', { droppedBefore: frame.droppedBefore });
434
+ return;
435
+ case 'unavailable':
436
+ sse.event('stream.unavailable', { reason: frame.reason });
437
+ return;
438
+ case 'closed':
439
+ // 流级收尾(任务未终态,见 TaskStreamFrame):帧先发出去再关连接 ——
440
+ // 客户端据此知道「没有更多帧了,去轮询」,而不是对着断线猜
441
+ sse.event('stream.closed', { reason: frame.reason });
442
+ closeStream();
443
+ return;
444
+ case 'end':
445
+ sse.event('task.end', frame.record);
446
+ closeStream();
447
+ return;
448
+ default: {
449
+ // 编译期穷尽性断言:`TaskStreamFrame` 新增成员时这里会编译失败,
450
+ // 而不是被静默丢掉 —— 静默丢帧的客户端只会对着流干等,最后靠超时猜。
451
+ const _never = frame;
452
+ void _never;
453
+ }
454
+ }
455
+ };
456
+ try {
457
+ const off = await runner.streamTask(taskId, write, from !== undefined ? { from } : {});
458
+ // 重放阶段就可能已收口(终态任务):那时 finished 为真,别再登记退订
459
+ if (finished)
460
+ off();
461
+ else
462
+ unsubscribe = off;
463
+ }
464
+ catch (e) {
465
+ if (e instanceof TaskStreamError) {
466
+ // 理论上不可达(上面已查过存在性),但保留这条:状态码还没写出去就还能回 404
467
+ if (!res.headersSent) {
468
+ sendJson(res, e.status, { error: errMessage(e) });
469
+ }
470
+ else {
471
+ sse.event('error', { message: errMessage(e) });
472
+ closeStream();
473
+ }
474
+ return;
475
+ }
476
+ sse.event('error', { message: errMessage(e) });
477
+ closeStream();
478
+ }
479
+ }
480
+ //# sourceMappingURL=http-endpoints.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-endpoints.js","sourceRoot":"","sources":["../../src/transport/http-endpoints.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEtE,OAAO,EAEL,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,eAAe,GAChB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,UAAU,EACV,WAAW,EACX,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,QAAQ,EACR,cAAc,EACd,gBAAgB,GACjB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAClG,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAqIrC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,KAAgB,EAAE,GAAY;IAC9D,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,SAAS;YACZ,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC;QAC5B,KAAK,SAAS;YACZ,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC;QAC5B,KAAK,kBAAkB;YACrB,OAAO,gBAAgB,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QAC9D,KAAK,KAAK;YACR,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC;QACxB,KAAK,QAAQ;YACX,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC;QAC3B,KAAK,SAAS;YACZ,OAAO,aAAa,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC1C,KAAK,MAAM;YACT,OAAO,UAAU,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACvC,KAAK,QAAQ;YACX,OAAO,YAAY,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACzC,KAAK,WAAW;YACd,OAAO,eAAe,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC5C,KAAK,YAAY;YACf,OAAO,gBAAgB,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC7C,KAAK,WAAW;YACd,kDAAkD;YAClD,OAAO,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC,CAAC;QAClE,KAAK,UAAU;YACb,OAAO,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,UAAU,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QACrE,SAAS,CAAC;YACR,MAAM,MAAM,GAAU,KAAK,CAAC;YAC5B,KAAK,MAAM,CAAC;QACd,CAAC;IACH,CAAC;AACH,CAAC;AAED,+EAA+E;AAC/E,qCAAqC;AACrC,+EAA+E;AAE/E,sEAAsE;AACtE,SAAS,aAAa,CAAC,GAAY;IACjC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,GAAG,CAAC;IAC9C,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE;QACjB,EAAE,EAAE,IAAI;QACR,QAAQ,EAAE,KAAK,CAAC,YAAY,GAAG,MAAM,CAAC,QAAQ;QAC9C,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;QAChC,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,+CAA+C;QAC/C,SAAS,EAAE,MAAM,CAAC,gBAAgB;KACV,CAAC,CAAC;AAC9B,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,aAAa,CAAC,GAAY;IACjC,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC;IAC7B,cAAc,CACZ,GAAG,CAAC,GAAG,EACP,OAAO,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EACxE,OAAO,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,EAAE,WAAW,CACnE,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,SAAS;AACT,+EAA+E;AAE/E;;;;;;;;;;;;GAYG;AACH,KAAK,UAAU,SAAS,CAAC,GAAY;IACnC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,GAAG,GAAG,CAAC;IAC7E,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QACnB,gBAAgB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC3B,OAAO;IACT,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC;IAC9D,IAAI,KAAK,KAAK,YAAY;QAAE,OAAO;IACnC,IAAI,QAA8C,CAAC;IACnD,IAAI,CAAC;QACH,QAAQ,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;IACtC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC7C,OAAO;IACT,CAAC;IACD,iDAAiD;IACjD,IAAI,KAAK,CAAC,YAAY,IAAI,iBAAiB,EAAE,CAAC;QAC5C,GAAG,CAAC,SAAS,CAAC,aAAa,EAAE,mBAAmB,CAAC,CAAC;QAClD,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE;YACjB,KAAK,EAAE,eAAe,iBAAiB,QAAQ;SAChD,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IACD,KAAK,CAAC,YAAY,EAAE,CAAC;IACrB,oDAAoD;IACpD,sCAAsC;IACtC,MAAM,KAAK,GAAG,IAAI,eAAe,EAAE,CAAC;IACpC,MAAM,OAAO,GAAG,GAAS,EAAE;QACzB,IAAI,CAAC,GAAG,CAAC,aAAa;YAAE,KAAK,CAAC,KAAK,EAAE,CAAC;IACxC,CAAC,CAAC;IACF,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC3B,iEAAiE;IACjE,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC;IAChF,sDAAsD;IACtD,gDAAgD;IAChD,+BAA+B;IAC/B,MAAM,YAAY,GAAG,gBAAgB,CAAC,WAAW,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC;IACvE,IAAI,CAAC;QACH,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,EAAE;gBACzB,gBAAgB,EAAE,mBAAmB;gBACrC,0CAA0C;gBAC1C,cAAc,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE;aACpC,CAAC,CAAC;YACH,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;YACjE,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC;YACpB,wCAAwC;YACxC,iEAAiE;YACjE,yDAAyD;YACzD,+CAA+C;YAC/C,yDAAyD;YACzD,MAAM,QAAQ,GAAG,GAAS,EAAE;gBAC1B,aAAa,CAAC,SAAS,CAAC,CAAC;gBACzB,KAAK,CAAC,KAAK,EAAE,CAAC;gBACd,GAAG,CAAC,KAAK,EAAE,CAAC;YACd,CAAC,CAAC;YACF,oEAAoE;YACpE,MAAM,SAAS,GAAkB,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;YACpE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YAC7B,IAAI,CAAC;gBACH,sEAAsE;gBACtE,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE;oBAClC,OAAO,EAAE,KAAK;oBACd,MAAM,EAAE,KAAK,CAAC,MAAM;oBACpB,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;oBAC3D,mCAAmC;oBACnC,oDAAoD;oBACpD,gCAAgC;oBAChC,4CAA4C;oBAC5C,wCAAwC;oBACxC,YAAY,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC;oBAChD,GAAG,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBACxD,CAAC,CAAC;gBACH,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;YACxC,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,+CAA+C;gBAC/C,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACjD,CAAC;oBAAS,CAAC;gBACT,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBAChC,QAAQ,EAAE,CAAC;YACb,CAAC;YACD,OAAO;QACT,CAAC;QACD,gEAAgE;QAChE,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE;YAClC,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,GAAG,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACxD,CAAC,CAAC;QACH,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;IACtC,CAAC;YAAS,CAAC;QACT,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC1B,KAAK,CAAC,YAAY,EAAE,CAAC;IACvB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,YAAY,CAAC,GAAY;IACtC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC;IACxC,uDAAuD;IACvD,IAAI,KAAK,CAAC,QAAQ,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QACxC,gBAAgB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC3B,OAAO;IACT,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC;IAC7D,IAAI,IAAI,KAAK,YAAY;QAAE,OAAO;IAClC,MAAM,UAAU,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,8CAA8C,EAAE,CAAC,CAAC;QAC9E,OAAO;IACT,CAAC;IACD,0DAA0D;IAC1D,iEAAiE;IACjE,gDAAgD;IAChD,MAAM,WAAW,GAAG,gBAAgB,CAAC,WAAW,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC;IACtE,IAAI,GAAe,CAAC;IACpB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE;YACpC,GAAG,CAAC,UAAU,CAAC,cAAc,KAAK,SAAS;gBACzC,CAAC,CAAC,EAAE,cAAc,EAAE,UAAU,CAAC,cAAc,EAAE;gBAC/C,CAAC,CAAC,EAAE,CAAC;YACP,OAAO,EACL,UAAU,CAAC,OAAO,EAAE,YAAY,KAAK,SAAS;gBAC5C,CAAC,CAAC,UAAU,CAAC,OAAO;gBACpB,CAAC,CAAC;oBACE,GAAG,UAAU,CAAC,OAAO;oBACrB,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBACpE;YACP,MAAM,EAAE,MAAM;SACf,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,iDAAiD;QACjD,gDAAgD;QAChD,iDAAiD;QACjD,oCAAoC;QACpC,IAAI,CAAC,YAAY,cAAc,EAAE,CAAC;YAChC,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC7C,OAAO;QACT,CAAC;QACD,6DAA6D;QAC7D,6CAA6C;QAC7C,iDAAiD;QACjD,oDAAoD;QACpD,oCAAoC;QACpC,IAAI,CAAC,YAAY,kBAAkB,EAAE,CAAC;YACpC,GAAG,CAAC,SAAS,CAAC,aAAa,EAAE,mBAAmB,CAAC,CAAC;YAClD,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC7C,OAAO;QACT,CAAC;QACD,2DAA2D;QAC3D,oDAAoD;QACpD,0BAA0B;QAC1B,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;YACtB,gBAAgB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YAC3B,OAAO;QACT,CAAC;QACD,iBAAiB,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC;QAC5C,OAAO;IACT,CAAC;IACD,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAC1B,CAAC;AAED,+EAA+E;AAC/E,uDAAuD;AACvD,EAAE;AACF,kDAAkD;AAClD,kDAAkD;AAClD,gDAAgD;AAChD,+EAA+E;AAE/E;;;;GAIG;AACH,KAAK,UAAU,aAAa,CAAC,MAAc,EAAE,GAAY;IACvD,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC;IACjC,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC;IAC7D,IAAI,IAAI,KAAK,YAAY;QAAE,OAAO;IAClC,MAAM,MAAM,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE;YACjB,KAAK,EACH,sGAAsG;SACzG,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IACD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,OAAO,CAC9B,MAAM,EACN,MAAM,CAAC,SAAS,EAChB,MAAM,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CACtE,CAAC;QACF,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IAC1B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,wCAAwC;QACxC,IAAI,CAAC,YAAY,gBAAgB,EAAE,CAAC;YAClC,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAClD,OAAO;QACT,CAAC;QACD,iBAAiB,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC;IAC9C,CAAC;AACH,CAAC;AAED,mEAAmE;AACnE,KAAK,UAAU,UAAU,CAAC,MAAc,EAAE,GAAY;IACpD,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,kCAAkC;IAC7E,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,aAAa,MAAM,EAAE,EAAE,CAAC,CAAC;QACzD,OAAO;IACT,CAAC;IACD,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAC9B,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,YAAY,CAAC,MAAc,EAAE,GAAY;IACtD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,kCAAkC;QAC/E,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,0CAA0C;QAC1C,IAAI,CAAC,YAAY,eAAe,EAAE,CAAC;YACjC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACtD,OAAO;QACT,CAAC;QACD,iBAAiB,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC;IAClD,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,eAAe,CAAC,MAAc,EAAE,GAAY;IACzD,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC;IACjC,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC;IAC7D,IAAI,IAAI,KAAK,YAAY;QAAE,OAAO;IAClC,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IACnC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE;YACjB,KAAK,EACH,0EAA0E;SAC7E,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IACD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACnD,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IAC1B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,4CAA4C;QAC5C,IAAI,CAAC,YAAY,cAAc,EAAE,CAAC;YAChC,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAClD,OAAO;QACT,CAAC;QACD,iBAAiB,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC;IAC9C,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,KAAK,UAAU,gBAAgB,CAAC,MAAc,EAAE,GAAY;IAC1D,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,mBAAmB,EAAE,GAAG,GAAG,CAAC;IAC7D,mDAAmD;IACnD,oCAAoC;IACpC,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACxC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,aAAa,MAAM,EAAE,EAAE,CAAC,CAAC;QACrD,OAAO;IACT,CAAC;IACD,mEAAmE;IACnE,MAAM,OAAO,GACX,WAAW,CAAC,GAAG,EAAE,eAAe,CAAC;QACjC,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,kBAAkB,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACvE,MAAM,IAAI,GAAG,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACrF,IAAI,WAAW,GAAe,GAAG,EAAE,GAAE,CAAC,CAAC;IACvC,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,SAAqC,CAAC;IAC1C,6DAA6D;IAC7D,sDAAsD;IACtD,8DAA8D;IAC9D,IAAI,SAAoC,CAAC;IACzC,MAAM,WAAW,GAAG,GAAS,EAAE;QAC7B,IAAI,QAAQ;YAAE,OAAO;QACrB,QAAQ,GAAG,IAAI,CAAC;QAChB,IAAI,SAAS;YAAE,aAAa,CAAC,SAAS,CAAC,CAAC;QACxC,WAAW,EAAE,CAAC;QACd,IAAI,SAAS,KAAK,SAAS;YAAE,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC7D,GAAG,CAAC,KAAK,EAAE,CAAC;IACd,CAAC,CAAC;IACF,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,EAAE;QACzB,gBAAgB,EAAE,mBAAmB;QACrC,oDAAoD;QACpD,sDAAsD;QACtD,cAAc,EAAE,GAAG,EAAE,CAAC,WAAW,EAAE;KACpC,CAAC,CAAC;IACH,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;IAC3D,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC;IACpB,wCAAwC;IACxC,sEAAsE;IACtE,SAAS,GAAG,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IACnD,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC7B,qCAAqC;IACrC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE;QACrB,IAAI,CAAC,GAAG,CAAC,aAAa;YAAE,WAAW,EAAE,CAAC;IACxC,CAAC,CAAC,CAAC;IACH,MAAM,KAAK,GAAG,CAAC,KAAsB,EAAQ,EAAE;QAC7C,IAAI,QAAQ;YAAE,OAAO;QACrB,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;YACnB,KAAK,OAAO;gBACV,mDAAmD;gBACnD,GAAG,CAAC,KAAK,CAAC,aAAa,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;gBAC3D,OAAO;YACT,KAAK,WAAW;gBACd,GAAG,CAAC,KAAK,CAAC,kBAAkB,EAAE,EAAE,aAAa,EAAE,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC;gBACtE,OAAO;YACT,KAAK,aAAa;gBAChB,GAAG,CAAC,KAAK,CAAC,oBAAoB,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;gBAC1D,OAAO;YACT,KAAK,QAAQ;gBACX,6CAA6C;gBAC7C,+BAA+B;gBAC/B,GAAG,CAAC,KAAK,CAAC,eAAe,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;gBACrD,WAAW,EAAE,CAAC;gBACd,OAAO;YACT,KAAK,KAAK;gBACR,GAAG,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;gBACpC,WAAW,EAAE,CAAC;gBACd,OAAO;YACT,SAAS,CAAC;gBACR,2CAA2C;gBAC3C,sCAAsC;gBACtC,MAAM,MAAM,GAAU,KAAK,CAAC;gBAC5B,KAAK,MAAM,CAAC;YACd,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IACF,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACvF,yCAAyC;QACzC,IAAI,QAAQ;YAAE,GAAG,EAAE,CAAC;;YACf,WAAW,GAAG,GAAG,CAAC;IACzB,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,YAAY,eAAe,EAAE,CAAC;YACjC,0CAA0C;YAC1C,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;gBACrB,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACpD,CAAC;iBAAM,CAAC;gBACN,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gBAC/C,WAAW,EAAE,CAAC;YAChB,CAAC;YACD,OAAO;QACT,CAAC;QACD,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC/C,WAAW,EAAE,CAAC;IAChB,CAAC;AACH,CAAC"}
@@ -0,0 +1,66 @@
1
+ import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ /**
3
+ * Agentia —— HTTP 宿主的**收发原语**(`transport/http.ts` 拆分第三步的两半之一)。
4
+ *
5
+ * 这一组只做「把请求读进来、把话说回去」的机械动作,与**这是哪条路**
6
+ * (`http-route.ts` 的纯判定)和**走到这条路上做什么**(`http-endpoints.ts` 的端点体)
7
+ * 都无关 —— 三个模块各管一件事,谁越界就是新的乱源。
8
+ *
9
+ * 边界(本模块**不**做的事):
10
+ * - 不判路由、不选分支:`(pathname, method) → 该走哪条` 是 `http-route.ts` 的事;
11
+ * - 不决定状态码的**语义**:`sendJson(res, 400, …)` 里的 400 由调用方选,这里只负责写;
12
+ * - 不碰 AsyncRunner / store / 引擎:本模块的入参只有 req / res / 一段未知的 body。
13
+ *
14
+ * 依赖方向:本模块只 import `node:http` 的**类型**,零内部依赖 —— 所以
15
+ * `http.ts` 与 `http-endpoints.ts` 都能引它而不可能引成环。`HttpException` **刻意不在这里**
16
+ * (它是宿主契约的一部分,住在 `http.ts`)⇒ `sendUnauthorized` 也随之留在那边:
17
+ * 它是唯一一处 `instanceof HttpException` 的落点,搬过来会让本模块反向依赖宿主。
18
+ *
19
+ * 有两条**踩过坑**的兜底写进了这里,别当成可选装饰(原文见 `readBody` 与 `sendShuttingDown`):
20
+ * body 上限(未鉴权的大 body 能打满内存)与连接不可复用时的 `connection: close`
21
+ * (残留字节会被当成下一个请求)。
22
+ */
23
+ /** 503 建议重试间隔(秒):同步 run 通常秒级,给 1s 足够错峰 */
24
+ export declare const RETRY_AFTER_SECONDS = "1";
25
+ /** 写一个 JSON 响应:带 `content-type` 与 `content-length`(后者用 `Buffer.byteLength`,不是 `.length`) */
26
+ export declare function sendJson(res: ServerResponse, status: number, body: unknown): void;
27
+ /** 405 统一响应(方法不符):文案与其余错误一致用中文,并带 Allow 头。 */
28
+ export declare function methodNotAllowed(res: ServerResponse, method: string, allowed: string): void;
29
+ /** Prometheus 文本响应(G4):抓取端按 text/plain; version=0.0.4 解析 */
30
+ export declare function sendPrometheus(res: ServerResponse, body: string, contentType?: string): void;
31
+ export type BodyResult = {
32
+ ok: true;
33
+ raw: string;
34
+ } | {
35
+ ok: false;
36
+ reason: 'too-large' | 'aborted';
37
+ };
38
+ /**
39
+ * 读取 body 全文。两道兜底(原实现两者皆无):
40
+ * - **maxBytes 上限**:超限立即停止累积并 resolve 成哨兵(调用方回 413)——
41
+ * 否则一个未鉴权的大 body 就能把宿主内存打满;
42
+ * - **close 兜底**:客户端中途断开时既不会有 `end` 也不会有 `error`(Node 发 `aborted`/`close`),
43
+ * Promise 永不 settle → 每个半截请求漏一个 handler 与一份 buffer。
44
+ */
45
+ export declare function readBody(req: IncomingMessage, maxBytes: number): Promise<BodyResult>;
46
+ /** 把 unknown 异常收成一句人话(`Error` 取 `message`,其余 `String()`) */
47
+ export declare function errMessage(e: unknown): string;
48
+ /** 503:停机中不再接单(在读 body 之前就拒,省一次传输) */
49
+ export declare function sendShuttingDown(req: IncomingMessage, res: ServerResponse): void;
50
+ /**
51
+ * 500:内部错误。
52
+ *
53
+ * `exposeErrors` 为假时只回通用文案、细节走 `console.error` —— 否则
54
+ * `ECONNREFUSED 10.0.0.7:6379` 这类内部拓扑会回给未鉴权的调用方。
55
+ * `exposeErrors` 由宿主透传(它是 `HttpHandlerOptions` 的旋钮),本模块不持有策略。
56
+ */
57
+ export declare function sendInternalError(res: ServerResponse, e: unknown, exposeErrors: boolean): void;
58
+ /**
59
+ * 读单个请求头。Node 对重复头给数组(`traceparent` 语义上只该有一个)——
60
+ * 取第一个即可,多余的忽略。
61
+ */
62
+ export declare function headerValue(req: IncomingMessage, name: string): string | undefined;
63
+ /** 「body 已自行回过错误响应、调用方直接返回」的哨兵(连接已断时无响应可回,同样走它) */
64
+ export declare const PARSE_FAILED: unique symbol;
65
+ /** 读取并解析 JSON body;失败时直接回错误响应并返回哨兵(连接已断则无响应可回)。 */
66
+ export declare function parseJsonBody(req: IncomingMessage, res: ServerResponse, maxBytes: number): Promise<unknown | typeof PARSE_FAILED>;