@migor/agentia 0.9.3 → 0.9.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +779 -1
- package/README.md +1 -1
- package/dist/AGENTS.md +320 -37
- package/dist/container/container.js +15 -0
- package/dist/container/container.js.map +1 -1
- package/dist/core/limits.d.ts +121 -17
- package/dist/core/limits.js +133 -16
- package/dist/core/limits.js.map +1 -1
- package/dist/core/line-framing.d.ts +36 -0
- package/dist/core/line-framing.js +39 -0
- package/dist/core/line-framing.js.map +1 -0
- package/dist/core/message.d.ts +11 -0
- package/dist/core/run.d.ts +68 -4
- package/dist/core/run.js +27 -1
- package/dist/core/run.js.map +1 -1
- package/dist/core/schema.js +59 -8
- package/dist/core/schema.js.map +1 -1
- package/dist/core/timeout.d.ts +43 -1
- package/dist/core/timeout.js +76 -3
- package/dist/core/timeout.js.map +1 -1
- package/dist/core/tool.d.ts +39 -1
- package/dist/core/trace.d.ts +40 -1
- package/dist/core/trace.js +26 -0
- package/dist/core/trace.js.map +1 -1
- package/dist/engine/defer.d.ts +49 -0
- package/dist/engine/defer.js +72 -0
- package/dist/engine/defer.js.map +1 -0
- package/dist/engine/forwarded.d.ts +14 -5
- package/dist/engine/forwarded.js +4 -1
- package/dist/engine/forwarded.js.map +1 -1
- package/dist/engine/loop-result.d.ts +65 -7
- package/dist/engine/loop-result.js +24 -5
- package/dist/engine/loop-result.js.map +1 -1
- package/dist/engine/loop.d.ts +2 -0
- package/dist/engine/loop.js +157 -17
- package/dist/engine/loop.js.map +1 -1
- package/dist/engine/mcp-server-http.d.ts +2 -0
- package/dist/engine/mcp-server-http.js +215 -0
- package/dist/engine/mcp-server-http.js.map +1 -0
- package/dist/engine/mcp-server-stdio.d.ts +15 -0
- package/dist/engine/mcp-server-stdio.js +106 -0
- package/dist/engine/mcp-server-stdio.js.map +1 -0
- package/dist/engine/mcp-server.d.ts +134 -0
- package/dist/engine/mcp-server.js +232 -0
- package/dist/engine/mcp-server.js.map +1 -0
- package/dist/engine/menu-drift.d.ts +34 -0
- package/dist/engine/menu-drift.js +81 -0
- package/dist/engine/menu-drift.js.map +1 -0
- package/dist/engine/policy.d.ts +2 -1
- package/dist/engine/policy.js +7 -1
- package/dist/engine/policy.js.map +1 -1
- package/dist/engine/replay.d.ts +2 -1
- package/dist/engine/replay.js +3 -2
- package/dist/engine/replay.js.map +1 -1
- package/dist/engine/resume-input.d.ts +10 -0
- package/dist/engine/resume-input.js +11 -0
- package/dist/engine/resume-input.js.map +1 -1
- package/dist/engine/run-config.d.ts +26 -2
- package/dist/engine/run-config.js +60 -0
- package/dist/engine/run-config.js.map +1 -1
- package/dist/engine/spec.d.ts +33 -4
- package/dist/engine/spec.js.map +1 -1
- package/dist/engine/tool-context.d.ts +10 -5
- package/dist/engine/tool-context.js +5 -1
- package/dist/engine/tool-context.js.map +1 -1
- package/dist/engine/tracer.d.ts +43 -5
- package/dist/engine/tracer.js +73 -10
- package/dist/engine/tracer.js.map +1 -1
- package/dist/engine/trimming.js +151 -17
- package/dist/engine/trimming.js.map +1 -1
- package/dist/engine/turn.d.ts +60 -3
- package/dist/engine/turn.js +180 -71
- package/dist/engine/turn.js.map +1 -1
- package/dist/engine/types.d.ts +89 -3
- package/dist/engine/types.js +38 -2
- package/dist/engine/types.js.map +1 -1
- package/dist/engine/usage.d.ts +22 -3
- package/dist/engine/usage.js +47 -6
- package/dist/engine/usage.js.map +1 -1
- package/dist/eval/export.d.ts +48 -0
- package/dist/eval/export.js +146 -0
- package/dist/eval/export.js.map +1 -0
- package/dist/eval/harvest.js +7 -2
- package/dist/eval/harvest.js.map +1 -1
- package/dist/index.d.ts +8 -5
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/integrations/adapter-options.d.ts +22 -0
- package/dist/integrations/adapter-options.js +51 -0
- package/dist/integrations/adapter-options.js.map +1 -1
- package/dist/integrations/anthropic.d.ts +4 -2
- package/dist/integrations/anthropic.js +27 -27
- package/dist/integrations/anthropic.js.map +1 -1
- package/dist/integrations/file-sink.d.ts +20 -0
- package/dist/integrations/file-sink.js +27 -0
- package/dist/integrations/file-sink.js.map +1 -0
- package/dist/integrations/mcp-http.d.ts +3 -3
- package/dist/integrations/mcp-http.js +1 -1
- package/dist/integrations/mcp-http.js.map +1 -1
- package/dist/integrations/mcp-protocol.d.ts +115 -0
- package/dist/integrations/mcp-protocol.js +69 -0
- package/dist/integrations/mcp-protocol.js.map +1 -0
- package/dist/integrations/mcp-stdio.d.ts +3 -3
- package/dist/integrations/mcp-stdio.js +17 -3
- package/dist/integrations/mcp-stdio.js.map +1 -1
- package/dist/integrations/mcp.d.ts +6 -95
- package/dist/integrations/mcp.js +24 -66
- package/dist/integrations/mcp.js.map +1 -1
- package/dist/integrations/metrics-otlp.js +52 -7
- package/dist/integrations/metrics-otlp.js.map +1 -1
- package/dist/integrations/metrics-render.d.ts +15 -0
- package/dist/integrations/metrics-render.js +79 -2
- package/dist/integrations/metrics-render.js.map +1 -1
- package/dist/integrations/metrics-state.d.ts +99 -1
- package/dist/integrations/metrics-state.js +128 -4
- package/dist/integrations/metrics-state.js.map +1 -1
- package/dist/integrations/metrics.d.ts +62 -6
- package/dist/integrations/metrics.js +49 -8
- package/dist/integrations/metrics.js.map +1 -1
- package/dist/integrations/openai.js +9 -31
- package/dist/integrations/openai.js.map +1 -1
- package/dist/integrations/otlp.d.ts +3 -3
- package/dist/integrations/report.d.ts +7 -0
- package/dist/integrations/report.js +20 -1
- package/dist/integrations/report.js.map +1 -1
- package/dist/runtime/memory.d.ts +61 -1
- package/dist/runtime/memory.js +47 -7
- package/dist/runtime/memory.js.map +1 -1
- package/dist/runtime/run.d.ts +12 -2
- package/dist/runtime/run.js +112 -15
- package/dist/runtime/run.js.map +1 -1
- package/dist/store/fsStore.js +10 -4
- package/dist/store/fsStore.js.map +1 -1
- package/dist/store/record.d.ts +41 -0
- package/dist/store/record.js +54 -0
- package/dist/store/record.js.map +1 -0
- package/dist/store/redisStore.d.ts +3 -1
- package/dist/store/redisStore.js +21 -7
- package/dist/store/redisStore.js.map +1 -1
- package/dist/store/sqliteStore.d.ts +23 -0
- package/dist/store/sqliteStore.js +103 -8
- package/dist/store/sqliteStore.js.map +1 -1
- package/dist/store/store.d.ts +65 -2
- package/dist/store/store.js +5 -2
- package/dist/store/store.js.map +1 -1
- package/dist/toolkit/capability-cycles.d.ts +50 -0
- package/dist/toolkit/capability-cycles.js +88 -0
- package/dist/toolkit/capability-cycles.js.map +1 -0
- package/dist/toolkit/capability-slice.d.ts +86 -0
- package/dist/toolkit/capability-slice.js +98 -0
- package/dist/toolkit/capability-slice.js.map +1 -0
- package/dist/toolkit/module.d.ts +7 -1
- package/dist/toolkit/module.js +65 -32
- package/dist/toolkit/module.js.map +1 -1
- package/dist/transport/approval-policy.d.ts +1 -1
- package/dist/transport/approval-policy.js +8 -4
- package/dist/transport/approval-policy.js.map +1 -1
- package/dist/transport/async.d.ts +197 -18
- package/dist/transport/async.js +871 -85
- package/dist/transport/async.js.map +1 -1
- package/dist/transport/drain-gate.d.ts +6 -2
- package/dist/transport/drain-gate.js +14 -2
- package/dist/transport/drain-gate.js.map +1 -1
- package/dist/transport/http-endpoints.d.ts +151 -0
- package/dist/transport/http-endpoints.js +480 -0
- package/dist/transport/http-endpoints.js.map +1 -0
- package/dist/transport/http-io.d.ts +66 -0
- package/dist/transport/http-io.js +145 -0
- package/dist/transport/http-io.js.map +1 -0
- package/dist/transport/http-route.d.ts +10 -3
- package/dist/transport/http-route.js +18 -3
- package/dist/transport/http-route.js.map +1 -1
- package/dist/transport/http-shapes.d.ts +11 -0
- package/dist/transport/http-shapes.js +31 -1
- package/dist/transport/http-shapes.js.map +1 -1
- package/dist/transport/http.d.ts +22 -35
- package/dist/transport/http.js +110 -467
- package/dist/transport/http.js.map +1 -1
- package/dist/transport/owner-id.d.ts +29 -0
- package/dist/transport/owner-id.js +46 -0
- package/dist/transport/owner-id.js.map +1 -0
- package/dist/transport/owner-liveness.d.ts +8 -0
- package/dist/transport/owner-liveness.js +56 -0
- package/dist/transport/owner-liveness.js.map +1 -0
- package/dist/transport/resume-policy.d.ts +48 -10
- package/dist/transport/resume-policy.js +25 -10
- package/dist/transport/resume-policy.js.map +1 -1
- package/dist/transport/scheduler.js +23 -24
- package/dist/transport/scheduler.js.map +1 -1
- package/dist/transport/slot-pool.d.ts +9 -1
- package/dist/transport/slot-pool.js +9 -1
- package/dist/transport/slot-pool.js.map +1 -1
- package/dist/transport/sse.js +8 -0
- package/dist/transport/sse.js.map +1 -1
- package/dist/transport/task-events.d.ts +34 -3
- package/dist/transport/task-events.js +86 -5
- package/dist/transport/task-events.js.map +1 -1
- package/dist/transport/wake-policy.d.ts +50 -0
- package/dist/transport/wake-policy.js +43 -0
- package/dist/transport/wake-policy.js.map +1 -0
- package/package.json +6 -4
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agentia —— HTTP 宿主的**收发原语**(`transport/http.ts` 拆分第三步的两半之一)。
|
|
3
|
+
*
|
|
4
|
+
* 这一组只做「把请求读进来、把话说回去」的机械动作,与**这是哪条路**
|
|
5
|
+
* (`http-route.ts` 的纯判定)和**走到这条路上做什么**(`http-endpoints.ts` 的端点体)
|
|
6
|
+
* 都无关 —— 三个模块各管一件事,谁越界就是新的乱源。
|
|
7
|
+
*
|
|
8
|
+
* 边界(本模块**不**做的事):
|
|
9
|
+
* - 不判路由、不选分支:`(pathname, method) → 该走哪条` 是 `http-route.ts` 的事;
|
|
10
|
+
* - 不决定状态码的**语义**:`sendJson(res, 400, …)` 里的 400 由调用方选,这里只负责写;
|
|
11
|
+
* - 不碰 AsyncRunner / store / 引擎:本模块的入参只有 req / res / 一段未知的 body。
|
|
12
|
+
*
|
|
13
|
+
* 依赖方向:本模块只 import `node:http` 的**类型**,零内部依赖 —— 所以
|
|
14
|
+
* `http.ts` 与 `http-endpoints.ts` 都能引它而不可能引成环。`HttpException` **刻意不在这里**
|
|
15
|
+
* (它是宿主契约的一部分,住在 `http.ts`)⇒ `sendUnauthorized` 也随之留在那边:
|
|
16
|
+
* 它是唯一一处 `instanceof HttpException` 的落点,搬过来会让本模块反向依赖宿主。
|
|
17
|
+
*
|
|
18
|
+
* 有两条**踩过坑**的兜底写进了这里,别当成可选装饰(原文见 `readBody` 与 `sendShuttingDown`):
|
|
19
|
+
* body 上限(未鉴权的大 body 能打满内存)与连接不可复用时的 `connection: close`
|
|
20
|
+
* (残留字节会被当成下一个请求)。
|
|
21
|
+
*/
|
|
22
|
+
/** 503 建议重试间隔(秒):同步 run 通常秒级,给 1s 足够错峰 */
|
|
23
|
+
export const RETRY_AFTER_SECONDS = '1';
|
|
24
|
+
/** 写一个 JSON 响应:带 `content-type` 与 `content-length`(后者用 `Buffer.byteLength`,不是 `.length`) */
|
|
25
|
+
export function sendJson(res, status, body) {
|
|
26
|
+
const payload = JSON.stringify(body);
|
|
27
|
+
res.writeHead(status, {
|
|
28
|
+
'content-type': 'application/json; charset=utf-8',
|
|
29
|
+
'content-length': Buffer.byteLength(payload),
|
|
30
|
+
});
|
|
31
|
+
res.end(payload);
|
|
32
|
+
}
|
|
33
|
+
/** 405 统一响应(方法不符):文案与其余错误一致用中文,并带 Allow 头。 */
|
|
34
|
+
export function methodNotAllowed(res, method, allowed) {
|
|
35
|
+
res.setHeader('allow', allowed);
|
|
36
|
+
sendJson(res, 405, { error: `方法 ${method} 不被允许,请用 ${allowed}` });
|
|
37
|
+
}
|
|
38
|
+
/** Prometheus 文本响应(G4):抓取端按 text/plain; version=0.0.4 解析 */
|
|
39
|
+
export function sendPrometheus(res, body, contentType = 'text/plain; version=0.0.4; charset=utf-8') {
|
|
40
|
+
res.writeHead(200, {
|
|
41
|
+
'content-type': contentType,
|
|
42
|
+
'content-length': Buffer.byteLength(body),
|
|
43
|
+
'cache-control': 'no-store',
|
|
44
|
+
});
|
|
45
|
+
res.end(body);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 读取 body 全文。两道兜底(原实现两者皆无):
|
|
49
|
+
* - **maxBytes 上限**:超限立即停止累积并 resolve 成哨兵(调用方回 413)——
|
|
50
|
+
* 否则一个未鉴权的大 body 就能把宿主内存打满;
|
|
51
|
+
* - **close 兜底**:客户端中途断开时既不会有 `end` 也不会有 `error`(Node 发 `aborted`/`close`),
|
|
52
|
+
* Promise 永不 settle → 每个半截请求漏一个 handler 与一份 buffer。
|
|
53
|
+
*/
|
|
54
|
+
export function readBody(req, maxBytes) {
|
|
55
|
+
return new Promise((resolve) => {
|
|
56
|
+
const chunks = [];
|
|
57
|
+
let size = 0;
|
|
58
|
+
let done = false;
|
|
59
|
+
const cleanup = () => {
|
|
60
|
+
req.off('data', onData);
|
|
61
|
+
req.off('end', onEnd);
|
|
62
|
+
req.off('error', onAbort);
|
|
63
|
+
req.off('close', onAbort);
|
|
64
|
+
};
|
|
65
|
+
const finish = (r) => {
|
|
66
|
+
if (done)
|
|
67
|
+
return; // end 之后仍会来一次 close
|
|
68
|
+
done = true;
|
|
69
|
+
cleanup();
|
|
70
|
+
resolve(r);
|
|
71
|
+
};
|
|
72
|
+
const onData = (c) => {
|
|
73
|
+
size += c.length;
|
|
74
|
+
if (size > maxBytes) {
|
|
75
|
+
finish({ ok: false, reason: 'too-large' });
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
chunks.push(c);
|
|
79
|
+
};
|
|
80
|
+
const onEnd = () => finish({ ok: true, raw: Buffer.concat(chunks).toString('utf8') });
|
|
81
|
+
const onAbort = () => finish({ ok: false, reason: 'aborted' });
|
|
82
|
+
req.on('data', onData);
|
|
83
|
+
req.on('end', onEnd);
|
|
84
|
+
req.on('error', onAbort);
|
|
85
|
+
req.on('close', onAbort);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/** 把 unknown 异常收成一句人话(`Error` 取 `message`,其余 `String()`) */
|
|
89
|
+
export function errMessage(e) {
|
|
90
|
+
return e instanceof Error ? e.message : String(e);
|
|
91
|
+
}
|
|
92
|
+
/** 503:停机中不再接单(在读 body 之前就拒,省一次传输) */
|
|
93
|
+
export function sendShuttingDown(req, res) {
|
|
94
|
+
// body 未消费 → 连接不可复用(残留字节会被当成下一个请求,与 413 同理)
|
|
95
|
+
if (!req.complete)
|
|
96
|
+
res.setHeader('connection', 'close');
|
|
97
|
+
res.setHeader('retry-after', RETRY_AFTER_SECONDS);
|
|
98
|
+
sendJson(res, 503, { error: '服务正在优雅停机,不再接受新任务' });
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* 500:内部错误。
|
|
102
|
+
*
|
|
103
|
+
* `exposeErrors` 为假时只回通用文案、细节走 `console.error` —— 否则
|
|
104
|
+
* `ECONNREFUSED 10.0.0.7:6379` 这类内部拓扑会回给未鉴权的调用方。
|
|
105
|
+
* `exposeErrors` 由宿主透传(它是 `HttpHandlerOptions` 的旋钮),本模块不持有策略。
|
|
106
|
+
*/
|
|
107
|
+
export function sendInternalError(res, e, exposeErrors) {
|
|
108
|
+
if (exposeErrors) {
|
|
109
|
+
sendJson(res, 500, { error: errMessage(e) });
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
// 细节只进服务端日志,响应里不回内部拓扑
|
|
113
|
+
console.error('[agentia:http] 请求处理异常:', e);
|
|
114
|
+
sendJson(res, 500, { error: '内部错误' });
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* 读单个请求头。Node 对重复头给数组(`traceparent` 语义上只该有一个)——
|
|
118
|
+
* 取第一个即可,多余的忽略。
|
|
119
|
+
*/
|
|
120
|
+
export function headerValue(req, name) {
|
|
121
|
+
const v = req.headers[name];
|
|
122
|
+
return Array.isArray(v) ? v[0] : v;
|
|
123
|
+
}
|
|
124
|
+
/** 「body 已自行回过错误响应、调用方直接返回」的哨兵(连接已断时无响应可回,同样走它) */
|
|
125
|
+
export const PARSE_FAILED = Symbol('parse-failed');
|
|
126
|
+
/** 读取并解析 JSON body;失败时直接回错误响应并返回哨兵(连接已断则无响应可回)。 */
|
|
127
|
+
export async function parseJsonBody(req, res, maxBytes) {
|
|
128
|
+
const body = await readBody(req, maxBytes);
|
|
129
|
+
if (!body.ok) {
|
|
130
|
+
if (body.reason === 'too-large') {
|
|
131
|
+
// body 未读完就回响应:连接不能复用,否则残留字节会被当成下一个请求
|
|
132
|
+
res.setHeader('connection', 'close');
|
|
133
|
+
sendJson(res, 413, { error: `请求 body 超过上限 ${maxBytes} 字节` });
|
|
134
|
+
}
|
|
135
|
+
return PARSE_FAILED; // aborted:客户端已走,写了也没人收
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
return body.raw ? JSON.parse(body.raw) : undefined;
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
sendJson(res, 400, { error: '请求 body 不是合法 JSON' });
|
|
142
|
+
return PARSE_FAILED;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=http-io.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-io.js","sourceRoot":"","sources":["../../src/transport/http-io.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,9 +13,10 @@
|
|
|
13
13
|
* 而没配 metrics 出口时 `/metrics` **不是**免鉴权路径(会先过鉴权,最后 404)。
|
|
14
14
|
* ② **其余一律先鉴权、再判方法与路径**:所以鉴权失败时 `DELETE /run` 回 401 而不是 405,
|
|
15
15
|
* `POST /不存在的路径` 也先过鉴权 —— 「你路径写错了」不该泄露给未鉴权的调用方。
|
|
16
|
-
* ③ **`/tasks/<id>/approve
|
|
17
|
-
*
|
|
18
|
-
* `GET /tasks/x/approve` 回 **405(Allow: POST)**而不是 404
|
|
16
|
+
* ③ **`/tasks/<id>/approve`、`/tasks/<id>/cancel`、`/tasks/<id>/events` 与
|
|
17
|
+
* `/tasks/<id>/stream` 都先于 `/tasks/<id>`,且「方法不对」压过「id 坏了」**:
|
|
18
|
+
* `GET /tasks/x/approve` 回 **405(Allow: POST)**而不是 404、也不走轮询
|
|
19
|
+
* (cancel 同款:`GET /tasks/x/cancel` = 405);
|
|
19
20
|
* `POST /tasks/x/stream` 回 **405(Allow: GET)**;
|
|
20
21
|
* `DELETE /tasks/%zz/approve` 回 **405 而不是 400** —— 方法不对就轮不到判 id。
|
|
21
22
|
* 可观测的是这个结果,不是源码里的书写次序:解码经 `decodeSegment`(**不抛错**,
|
|
@@ -38,6 +39,12 @@ export type HttpRoute = {
|
|
|
38
39
|
} | {
|
|
39
40
|
kind: 'approve';
|
|
40
41
|
taskId: string;
|
|
42
|
+
} | {
|
|
43
|
+
kind: 'cancel';
|
|
44
|
+
taskId: string;
|
|
45
|
+
} | {
|
|
46
|
+
kind: 'taskEvent';
|
|
47
|
+
taskId: string;
|
|
41
48
|
} | {
|
|
42
49
|
kind: 'taskStream';
|
|
43
50
|
taskId: string;
|
|
@@ -13,9 +13,10 @@
|
|
|
13
13
|
* 而没配 metrics 出口时 `/metrics` **不是**免鉴权路径(会先过鉴权,最后 404)。
|
|
14
14
|
* ② **其余一律先鉴权、再判方法与路径**:所以鉴权失败时 `DELETE /run` 回 401 而不是 405,
|
|
15
15
|
* `POST /不存在的路径` 也先过鉴权 —— 「你路径写错了」不该泄露给未鉴权的调用方。
|
|
16
|
-
* ③ **`/tasks/<id>/approve
|
|
17
|
-
*
|
|
18
|
-
* `GET /tasks/x/approve` 回 **405(Allow: POST)**而不是 404
|
|
16
|
+
* ③ **`/tasks/<id>/approve`、`/tasks/<id>/cancel`、`/tasks/<id>/events` 与
|
|
17
|
+
* `/tasks/<id>/stream` 都先于 `/tasks/<id>`,且「方法不对」压过「id 坏了」**:
|
|
18
|
+
* `GET /tasks/x/approve` 回 **405(Allow: POST)**而不是 404、也不走轮询
|
|
19
|
+
* (cancel 同款:`GET /tasks/x/cancel` = 405);
|
|
19
20
|
* `POST /tasks/x/stream` 回 **405(Allow: GET)**;
|
|
20
21
|
* `DELETE /tasks/%zz/approve` 回 **405 而不是 400** —— 方法不对就轮不到判 id。
|
|
21
22
|
* 可观测的是这个结果,不是源码里的书写次序:解码经 `decodeSegment`(**不抛错**,
|
|
@@ -64,6 +65,20 @@ export function routeRequest(pathname, method, hasMetrics) {
|
|
|
64
65
|
const id = decodeSegment(rest.slice(0, -'/approve'.length));
|
|
65
66
|
return id === undefined ? { kind: 'badTaskId' } : { kind: 'approve', taskId: id };
|
|
66
67
|
}
|
|
68
|
+
// 取消(2026-09-28 ④):与 approve 同款排法 —— 先判方法、再解码
|
|
69
|
+
if (rest.endsWith('/cancel')) {
|
|
70
|
+
if (method !== 'POST')
|
|
71
|
+
return refuse('POST', false);
|
|
72
|
+
const id = decodeSegment(rest.slice(0, -'/cancel'.length));
|
|
73
|
+
return id === undefined ? { kind: 'badTaskId' } : { kind: 'cancel', taskId: id };
|
|
74
|
+
}
|
|
75
|
+
// 事件投入口(2026-09-28 ⑥):同款排法 —— 先判方法、再解码
|
|
76
|
+
if (rest.endsWith('/events')) {
|
|
77
|
+
if (method !== 'POST')
|
|
78
|
+
return refuse('POST', false);
|
|
79
|
+
const id = decodeSegment(rest.slice(0, -'/events'.length));
|
|
80
|
+
return id === undefined ? { kind: 'badTaskId' } : { kind: 'taskEvent', taskId: id };
|
|
81
|
+
}
|
|
67
82
|
if (rest.endsWith('/stream')) {
|
|
68
83
|
if (method !== 'GET')
|
|
69
84
|
return refuse('GET', false);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-route.js","sourceRoot":"","sources":["../../src/transport/http-route.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"http-route.js","sourceRoot":"","sources":["../../src/transport/http-route.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Trace, SpanError } from '../core/trace.js';
|
|
2
2
|
import type { RunStatus } from '../core/run.js';
|
|
3
|
+
import type { TaskEvent } from '../core/run.js';
|
|
3
4
|
import type { AgentRunResult, AgentStopReason } from '../engine/types.js';
|
|
4
5
|
import type { RunInvocationOptions } from '../engine/spec.js';
|
|
5
6
|
import type { ApprovalDecisions } from './async.js';
|
|
@@ -55,3 +56,13 @@ export declare function parseApproveBody(body: unknown): {
|
|
|
55
56
|
decisions: ApprovalDecisions;
|
|
56
57
|
decidedBy?: string;
|
|
57
58
|
} | undefined;
|
|
59
|
+
/**
|
|
60
|
+
* 解析 `POST /tasks/<id>/events` 的 body(2026-09-28 ⑥,run 事件投入口);
|
|
61
|
+
* 形状不合法返回 undefined(调用方回 400)。
|
|
62
|
+
*
|
|
63
|
+
* **白名单**(B1):只接受 `{ eventId?, type, payload }` 三个键,**多一个字段即拒** ——
|
|
64
|
+
* 这是外部输入第一次进入消息历史,「能救则救」在这里等于放行协议注入面
|
|
65
|
+
* (与 parseApproveBody 的「全有或全无」同一条纪律)。三个值全是字符串:
|
|
66
|
+
* 外部永远不能构造消息块。
|
|
67
|
+
*/
|
|
68
|
+
export declare function parseEventBody(body: unknown): TaskEvent | undefined;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
export function toHttpBody(out) {
|
|
3
3
|
return {
|
|
4
4
|
runId: out.run.runId,
|
|
5
|
-
// status 取 **run 状态机**的口径(它才知道
|
|
5
|
+
// status 取 **run 状态机**的口径(它才知道 suspended 不是终态),
|
|
6
6
|
// 不从 stopReason 反推 —— 两者语义不同,反推会把挂起当成失败
|
|
7
7
|
status: out.run.status,
|
|
8
8
|
stopReason: out.result.stopReason,
|
|
@@ -53,4 +53,34 @@ export function parseApproveBody(body) {
|
|
|
53
53
|
...(o.decidedBy !== undefined ? { decidedBy: o.decidedBy } : {}),
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* 解析 `POST /tasks/<id>/events` 的 body(2026-09-28 ⑥,run 事件投入口);
|
|
58
|
+
* 形状不合法返回 undefined(调用方回 400)。
|
|
59
|
+
*
|
|
60
|
+
* **白名单**(B1):只接受 `{ eventId?, type, payload }` 三个键,**多一个字段即拒** ——
|
|
61
|
+
* 这是外部输入第一次进入消息历史,「能救则救」在这里等于放行协议注入面
|
|
62
|
+
* (与 parseApproveBody 的「全有或全无」同一条纪律)。三个值全是字符串:
|
|
63
|
+
* 外部永远不能构造消息块。
|
|
64
|
+
*/
|
|
65
|
+
export function parseEventBody(body) {
|
|
66
|
+
if (!body || typeof body !== 'object' || Array.isArray(body))
|
|
67
|
+
return undefined;
|
|
68
|
+
const o = body;
|
|
69
|
+
for (const key of Object.keys(o)) {
|
|
70
|
+
if (key !== 'eventId' && key !== 'type' && key !== 'payload')
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
if (typeof o.type !== 'string' || o.type === '')
|
|
74
|
+
return undefined;
|
|
75
|
+
if (typeof o.payload !== 'string')
|
|
76
|
+
return undefined;
|
|
77
|
+
if (o.eventId !== undefined && (typeof o.eventId !== 'string' || o.eventId === '')) {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
type: o.type,
|
|
82
|
+
payload: o.payload,
|
|
83
|
+
...(o.eventId !== undefined ? { eventId: o.eventId } : {}),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
56
86
|
//# sourceMappingURL=http-shapes.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-shapes.js","sourceRoot":"","sources":["../../src/transport/http-shapes.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"http-shapes.js","sourceRoot":"","sources":["../../src/transport/http-shapes.ts"],"names":[],"mappings":"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"}
|
package/dist/transport/http.d.ts
CHANGED
|
@@ -7,25 +7,12 @@ import type { AppCallable } from './async.js';
|
|
|
7
7
|
* 只产出一个 (req, res) handler,不做 listen —— 交给用户
|
|
8
8
|
* `http.createServer(createHttpHandler(app)).listen(...)`,可挂进任意 Node HTTP 框架。
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* 并发 run 超 maxConcurrentRuns → 503 + Retry-After(见该选项)。
|
|
17
|
-
* - POST /tasks 异步任务。body { input, idempotencyKey?, options? } →
|
|
18
|
-
* AsyncRunner.submit → 202 TaskRecord(queued,幂等键去重照常生效)。
|
|
19
|
-
* - GET /tasks/<id> 轮询任务记录 → 200 TaskRecord;不存在 → 404。
|
|
20
|
-
* - POST /tasks/<id>/approve 人工审批(HITL)。body
|
|
21
|
-
* `{ decisions: { <tool_use_id>: { approved, reason? } }, decidedBy? }`
|
|
22
|
-
* → 200 TaskRecord;任务不存在 → 404;不在 awaiting_approval 状态 → 409;
|
|
23
|
-
* body 非法 → 400。**停机中仍可用**(与 GET 轮询同理由:
|
|
24
|
-
* 挂起的任务只有人能推进,停机不该连「批准」也拒掉)。
|
|
25
|
-
* - GET /healthz 健康检查 → 200 { ok, inFlight, uptimeMs, draining };**不鉴权**
|
|
26
|
-
* (探针不该带凭据)。停机中仍回 200(进程活着),就绪与否看 draining。
|
|
27
|
-
*
|
|
28
|
-
* 方法不符 405;路径不符 404;body 非法 JSON 400。runner 缺省内部 new AsyncRunner(app)。
|
|
10
|
+
* **本文件管三件事**(模块拆分第三步之后,另两件事各有其主,别再往这里塞):
|
|
11
|
+
* ① **宿主契约**:选项与类型(`HttpHandlerOptions` / `HttpHandler` / `HttpException` / 转出);
|
|
12
|
+
* ② **宿主级状态**:在飞同步 run 计数、停机态、SSE 收口表(`HttpState`,一处创建、按引用共享);
|
|
13
|
+
* ③ **准入与停机**:构造期把坏配置**响亮拒掉**、每个请求过鉴权闸、`drain()` 的排空编排。
|
|
14
|
+
* 端点契约(每条路读什么 body、回什么、什么状态码)见 `http-endpoints.ts`;
|
|
15
|
+
* 路由判定见 `http-route.ts`;收发原语见 `http-io.ts`。
|
|
29
16
|
*
|
|
30
17
|
* 鉴权(B1):配了 `authenticate` 时,**除 /healthz 与 /metrics 外的所有路径**先过钩子,
|
|
31
18
|
* 且必须在读 body 之前 —— 未通过即回错误并断开连接,**不接收 body**(省资源)。框架只给缝:
|
|
@@ -40,6 +27,11 @@ import type { AppCallable } from './async.js';
|
|
|
40
27
|
* `./http-shapes.js`;这里转出以保持既有 import 路径不变(公开面名字与位置都未变)。
|
|
41
28
|
*/
|
|
42
29
|
export type { RunHttpResponse, TaskSubmitBody } from './http-shapes.js';
|
|
30
|
+
/**
|
|
31
|
+
* `GET /healthz` 的响应形态随它的实现搬到了 `./http-endpoints.js`,这里转出 ——
|
|
32
|
+
* 与上面两个形状类型同一个理由(`src/index.ts` 一行未改,公开面逐字不变)。
|
|
33
|
+
*/
|
|
34
|
+
export type { HealthResponse } from './http-endpoints.js';
|
|
43
35
|
export interface HttpHandlerOptions {
|
|
44
36
|
/** 异步任务宿主;缺省 new AsyncRunner(app)(InMemoryTaskStore) */
|
|
45
37
|
runner?: AsyncRunner;
|
|
@@ -81,9 +73,14 @@ export interface HttpHandlerOptions {
|
|
|
81
73
|
*/
|
|
82
74
|
authenticate?: (req: IncomingMessage) => unknown | Promise<unknown>;
|
|
83
75
|
/**
|
|
84
|
-
* 指标出口(G4):提供后 `GET /metrics` 输出其
|
|
76
|
+
* 指标出口(G4):提供后 `GET /metrics` 输出其 `render()` 的文本。
|
|
85
77
|
* 通常直接传 `metricsSink()`(它有 `render()`)。
|
|
86
78
|
*
|
|
79
|
+
* 响应的 Content-Type 跟提供者的 `contentType` 字段走(metricsSink 会按自己的
|
|
80
|
+
* `export` 模式声明:openmetrics 模式 ⇒ `application/openmetrics-text`,
|
|
81
|
+
* 带 exemplar 的文本必须配这个头);没有这个字段(或传的是纯函数)⇒
|
|
82
|
+
* 缺省 `text/plain; version=0.0.4`,与既有行为一致。
|
|
83
|
+
*
|
|
87
84
|
* 与 `/healthz` 同档处理:**不鉴权**、停机中仍可拉(拉取端在集群内网)。要保护它,
|
|
88
85
|
* 请放到反代之后,或不要传这个选项、自己在 handler 外层挂路由。
|
|
89
86
|
*
|
|
@@ -91,26 +88,16 @@ export interface HttpHandlerOptions {
|
|
|
91
88
|
*/
|
|
92
89
|
metrics?: {
|
|
93
90
|
render(): string;
|
|
91
|
+
contentType?: string;
|
|
94
92
|
} | (() => string);
|
|
95
93
|
}
|
|
96
|
-
/** GET /healthz 的响应形态 */
|
|
97
|
-
export interface HealthResponse {
|
|
98
|
-
/** 恒为 true:能回这个响应就说明进程活着(停机中也是 true,就绪与否看 draining) */
|
|
99
|
-
ok: true;
|
|
100
|
-
/**
|
|
101
|
-
* 在飞工作量 = **正在处理的同步 run**(并发闸门计数,含 SSE 流)
|
|
102
|
-
* + **已受理未完成的异步任务**(queued + running)。
|
|
103
|
-
* 与 `drain()` 等的范围一致 —— 这样健康检查和停机判断看的是同一个数。
|
|
104
|
-
*/
|
|
105
|
-
inFlight: number;
|
|
106
|
-
/** 本 handler 创建至今的毫秒数 */
|
|
107
|
-
uptimeMs: number;
|
|
108
|
-
/** 是否已进入优雅停机(drain 之后)—— 负载均衡据此摘流量 */
|
|
109
|
-
draining: boolean;
|
|
110
|
-
}
|
|
111
94
|
/**
|
|
112
95
|
* 鉴权钩子可抛出的异常:显式携带 HTTP 状态与响应体。
|
|
113
96
|
* 钩子抛别的错误一律按 401 处理(设计 F4:与「工具抛错即 is_error」的既有风格一致)。
|
|
97
|
+
*
|
|
98
|
+
* 它**刻意留在本文件**(而不是随应答原语去 `http-io.ts`):`HttpException` 是**宿主契约**
|
|
99
|
+
* 的一部分(公开导出、鉴权钩子的抛出类型),而 `http-io.ts` 是零内部依赖的原语层 ——
|
|
100
|
+
* 搬过去会让原语层反向依赖宿主,只为了让 `sendUnauthorized` 能 `instanceof` 一下。
|
|
114
101
|
*/
|
|
115
102
|
export declare class HttpException extends Error {
|
|
116
103
|
readonly status: number;
|