@sema-agent/server 7.129.1 → 7.131.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.
- package/MIGRATION.md +102 -0
- package/README.md +8 -5
- package/README.zh-CN.md +6 -4
- package/USAGE.md +48 -9
- package/dist/audit.js +4 -1
- package/dist/bench/s1/live-deps.js +2 -0
- package/dist/boot/resolve-spec.js +43 -16
- package/dist/boot/stage-02-web-admin-pull.js +9 -1
- package/dist/boot/stage-10-http-server.d.ts +1 -1
- package/dist/boot/stage-10-http-server.js +7 -2
- package/dist/capabilities/builtin-tools.js +2 -1
- package/dist/capabilities/composite-members.d.ts +1 -0
- package/dist/capabilities/composite-members.js +1 -0
- package/dist/capabilities/hands-lane.js +2 -2
- package/dist/capabilities/host-tool-names.d.ts +17 -0
- package/dist/capabilities/host-tool-names.js +15 -0
- package/dist/capabilities/mcp-probe.js +19 -6
- package/dist/capabilities/repo-tools.js +4 -3
- package/dist/capabilities/scenarios.d.ts +34 -0
- package/dist/capabilities/scenarios.js +20 -4
- package/dist/capabilities/select-environment-tool.js +2 -1
- package/dist/capabilities/send-user-file-tool.d.ts +0 -1
- package/dist/capabilities/send-user-file-tool.js +2 -2
- package/dist/capabilities/skills.js +1 -1
- package/dist/capabilities/tool-defer.js +2 -1
- package/dist/config-catalog.js +16 -5
- package/dist/config-invariants.js +2 -2
- package/dist/config-provider.js +2 -2
- package/dist/config-types.d.ts +6 -0
- package/dist/config.d.ts +2 -2
- package/dist/config.js +18 -17
- package/dist/elicitation.d.ts +7 -0
- package/dist/elicitation.js +20 -5
- package/dist/exec-bounds.d.ts +6 -6
- package/dist/hooks/hook-llm.d.ts +11 -1
- package/dist/hooks/hook-llm.js +11 -14
- package/dist/hooks/hook-runner.d.ts +55 -9
- package/dist/hooks/hook-runner.js +86 -16
- package/dist/http/admission.js +2 -2
- package/dist/http/approval-sweeps.js +2 -2
- package/dist/http/checkpoint-ctx.d.ts +12 -11
- package/dist/http/checkpoint-ctx.js +4 -4
- package/dist/http/dispatch.js +1 -1
- package/dist/http/resume-legs.js +6 -5
- package/dist/http/routes/approvals-assistant.js +1 -1
- package/dist/http/routes/capabilities.js +13 -5
- package/dist/http/routes/runs.js +18 -14
- package/dist/http/routes/sessions.js +22 -10
- package/dist/http/routes/tasks.js +16 -11
- package/dist/http/scenario-read-face.d.ts +14 -0
- package/dist/http/scenario-read-face.js +5 -0
- package/dist/http/send.d.ts +10 -0
- package/dist/http/send.js +6 -0
- package/dist/http/server.d.ts +16 -4
- package/dist/http/server.js +2 -2
- package/dist/http/stored-body-replay.d.ts +10 -3
- package/dist/http/stored-body-replay.js +27 -6
- package/dist/http/wire-types.d.ts +18 -5
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/mcp-assertion-header.d.ts +2 -2
- package/dist/mcp-assertion-header.js +3 -3
- package/dist/model-entry-refusal.d.ts +1 -7
- package/dist/model-route-endpoint.d.ts +51 -11
- package/dist/model-route-endpoint.js +45 -23
- package/dist/model-route-gap.d.ts +5 -4
- package/dist/model-route-gap.js +1 -1
- package/dist/model-select.d.ts +58 -0
- package/dist/model-select.js +31 -0
- package/dist/namespaced-rule-name.d.ts +7 -4
- package/dist/namespaced-rule-name.js +4 -3
- package/dist/observability/fail-open.d.ts +16 -7
- package/dist/observability/fail-open.js +10 -2
- package/dist/plugins/local-session-store.js +2 -2
- package/dist/plugins/one-time-migrations.d.ts +21 -1
- package/dist/plugins/one-time-migrations.js +33 -1
- package/dist/plugins/remote-env-adb.js +5 -4
- package/dist/plugins/remote-env-local-docker.js +5 -4
- package/dist/plugins/remote-env-ssh.js +5 -4
- package/dist/question.d.ts +18 -9
- package/dist/question.js +21 -13
- package/dist/request-key-closure.d.ts +2 -2
- package/dist/request-key-closure.js +16 -9
- package/dist/rule-entry-list.d.ts +124 -17
- package/dist/rule-entry-list.js +140 -24
- package/dist/run-local.d.ts +3 -3
- package/dist/run-local.js +8 -7
- package/dist/runs.d.ts +42 -2
- package/dist/runs.js +45 -2
- package/dist/runtime-caps-resolver.d.ts +7 -0
- package/dist/runtime-caps-resolver.js +1 -0
- package/dist/session-titler.d.ts +4 -1
- package/dist/session-titler.js +13 -3
- package/dist/spec-fields.d.ts +8 -4
- package/dist/stored-request-hooks-m20.d.ts +23 -0
- package/dist/stored-request-hooks-m20.js +124 -0
- package/dist/stored-request-tool-axes-m19.d.ts +29 -0
- package/dist/stored-request-tool-axes-m19.js +27 -0
- package/dist/task-mcp.d.ts +55 -60
- package/dist/task-mcp.js +197 -96
- package/dist/task-settings.d.ts +41 -5
- package/dist/task-settings.js +39 -25
- package/dist/tool-axis-words.d.ts +14 -17
- package/dist/trace/core-keyset-guard.d.ts +2 -2
- package/dist/trace/ledger-sink.d.ts +29 -0
- package/dist/trace/ledger-sink.js +11 -0
- package/dist/trace/project.d.ts +6 -0
- package/dist/trace/project.js +11 -0
- package/dist/trace/wire-projection-faces.d.ts +1 -1
- package/dist/trace/wire-projection-faces.js +1 -1
- package/dist/user-authored-text.d.ts +7 -0
- package/dist/user-authored-text.js +3 -0
- package/dist/web-admin-client/apply-effective.js +10 -5
- package/dist/web-admin-client/skills-mcp.d.ts +1 -1
- package/dist/web-admin-client/types.d.ts +1 -1
- package/package.json +2 -2
- package/scripts/drain-check-local.mjs +3 -2
package/MIGRATION.md
CHANGED
|
@@ -7,6 +7,108 @@
|
|
|
7
7
|
> ⚠️ 完整清单在仓库根 `CHANGELOG.md`——它**不随 npm tarball 出包**(本文件随包)。看完整迁移窗的
|
|
8
8
|
> 权威姿势是源码 tag diff:`git diff v<旧>..v<新>`(每版都推 `v<版本>` tag);npm 包页也镜像 CHANGELOG。
|
|
9
9
|
|
|
10
|
+
## 7.131.0 —— hooks 配置受理收紧(两类此前受理的坏形 400 · 本仓不执行的条目类型整份拒)· 模型能力位 `reasoning` / `vision` 三态(env 缺省与中心回落退役)· settings-schema 13.0.1 精确钉 · 写错的工具名恢复开跑前拒并点名现名(限制侧四类全拒、放行侧只拒近似名;一张名册;拒句有界;续跑按方向分流)
|
|
11
|
+
|
|
12
|
+
1. **`settings.hooks` 只有一只读者,两类此前受理的坏形现在 `400 request.field_invalid`**(零 DDL):新提交(`POST /v1/tasks` · `/v1/tasks/stream` · `/v1/runs`)里
|
|
13
|
+
① **未知字段里藏着守卫迹象**(此前未知字段被剥掉、配置照收,守卫就此无声消失),例:
|
|
14
|
+
`{"hooks":{"Stop":[{"hooks":[{"type":"command","command":"true","extra":{"PreToolUse":[{"hooks":[{"type":"command","command":"deny.sh"}]}]}}]}]}}`
|
|
15
|
+
⇒ `settings.hooks.Stop.0.hooks.0: guard-entry-invalid — …`;**出路**:把守卫写到它该在的位置(`hooks.PreToolUse[…]`),删掉条目里的未知字段;
|
|
16
|
+
② **JSON 里写出的 `__proto__` 事件键**(此前被跳过,配置读成空对象照收),例:`{"hooks":{"__proto__":[…]}}` ⇒ `settings.hooks.__proto__: unknown-event — …`;**出路**:删掉这个键(它从来没生效过)。
|
|
17
|
+
拒句按分类器给的**码 + 坐标 + 坏字段**铸(`(field: command)` 点名坏字段);事件名等键段是用户文本,出门前过契约包的脱敏出门形(凭据形不原样回显)。
|
|
18
|
+
**拒句的形变了**:此前是 `settings.hooks: <原样路径>: <zod 句>`,现在是 `settings.hooks.<坐标>: <码> — <释义>`;句子不是契约,按 `errorCode` 与句中的码分派。
|
|
19
|
+
服务侧上限句(命令长度 / 条目数 / 组数 / http 头)与好配置的读数逐字不变。
|
|
20
|
+
**升级前停驻的任务(一次性迁移 M20,窗 = 本版发布日起 30 天)**:升级前存下的请求体里 `settings.hooks` 若带上面两形之一(升级前的读者把未知字段剥掉 / 跳过 `__proto__` 键照收,同一份里真在拦的 `PreToolUse`
|
|
21
|
+
钩子一直在拦),续跑时窗内把读者点名的那一块(条目 / 匹配器)里按类型确属未知的键剥掉(升级前的读者对一个条目本来就是剥掉全部未知字段、保留其余)、剩下的**重新过同一只读者**,过了就采纳,守卫照拦;每次重读恰一行运维 warn `hooks_read_as_before_upgrade`
|
|
22
|
+
(点名 sessionId 与被剥的坐标)。只迁这两形:拒因里混进别的(真坏的守卫条目、`hooks` 本身不是对象 …)⇒ 不迁移,整份不采纳。读者点名的字段只在它对**那一块的类型**确属未知时才迁移 —— `input` 只属 `mcp_tool` 条目,落在 `command` 条目上是未知字段;迁移的单位是块:读者点名了某个条目 / 匹配器,就把这一块里按类型确属未知的键一次全剥(契约字段一个不动;升级前的读者对一个条目本来就是剥掉全部未知字段、保留其余),再过同一只读者,剥到读者受理为止;`command` 条目自己的 `command` 字段值坏是契约字段、不迁移;条目没有 `type` 或 `type` 在契约词表外也不迁移;读者点名的键对不上文档也不迁移。重读次数只随嵌套的层增长(匹配器层、条目层、`__proto__` 事件键),不随未知字段个数增长;最后一次剥键之后一定再问读者一次。窗关之后(或不属这两形)⇒ 续跑时这份 hooks 整只不采纳 ——
|
|
23
|
+
**包括其中升级前真在拦的 `PreToolUse` 钩子** —— 记运维 warn `task_settings_deferred`(字段 `hooks`)并计 `fail_open_total{tag="server.hooks.resume-reader-refused"}`(只在 hooks 会被采纳的单用户车道计;
|
|
24
|
+
计数非零 = 有这类停驻任务在窗关后续跑),任务照常续跑。升级零动作;想提前收口就在窗内把这些停驻任务决掉,或按上面的出路改好重新提交。会撞上窗关那一格的是活过窗的升级前停驻行:发布后才升级的部署、
|
|
25
|
+
调长了 `APPROVAL_TERMINAL_BACKSTOP_MS` 的部署、滚动升级里旧副本在窗内新写的行。
|
|
26
|
+
2. **`mcp_tool` 条目(以及将来契约新增、本仓不执行的条目类型)整份拒**:契约包 13.0.0 受理第 5 种条目类型 `mcp_tool`,本仓不执行它。含它的 `settings.hooks`(任何事件,守卫事件尤其)提交时
|
|
27
|
+
`400 request.field_invalid`:`settings.hooks.PreToolUse.0.hooks.0.type: this server does not run hook entries of type mcp_tool; supported: command / prompt / agent / http`;
|
|
28
|
+
合法的其它条目不会被单独留下。对调用方可见的结果与 7.130.0 一致(同样 400,那时是契约包不认识这一型),变的是拒句的形与它的来源 —— 本仓自己对「会不会跑」表态,而不是借契约包的受理面代答。
|
|
29
|
+
执行侧原有的「不认识的类型只记运维日志 `hook_entries_skipped`(`reason: type_not_supported`)然后跳过」那一臂已删(契约包放宽后它会让守卫条目无声失效),该日志行不再出现。
|
|
30
|
+
**出路**:用 `command` 条目(命令里自行调用 MCP 工具);这台 server 不跑 `mcp_tool` 条目。
|
|
31
|
+
3. **模型能力位变三态(缺省退役,BREAKING 默认值)**:`MODEL_REASONING` / `MODEL_VISION`(env 腿)与中心名册条目的 `reasoning` / `vision` 现在是 `true` / `false` / **缺席 = 未声明**,缺席不铸键。
|
|
32
|
+
- 升级前:env `MODEL_REASONING` 未设 ⇒ `true`;`MODEL_VISION` 未设 ⇒ 收图;中心名册条目没写 `reasoning` ⇒ 继承 env 腿的值(缺省 `true`),没写 `vision` ⇒ 读成纯文本。
|
|
33
|
+
- 升级后:以上都是「未声明」。引擎怎么读缺席(引擎 7.51.2):`vision` 缺席 ⇒ 收图(图原样交给模型;带图预检不出信号)—— 对 env 部署与升级前的行为一致;
|
|
34
|
+
`reasoning` 缺席 ⇒ 与显式 `false` 同读,**思考档不随请求发出**。
|
|
35
|
+
- **要保持升级前的行为就显式声明**:需要思考的模型写 env `MODEL_REASONING=true` / 名册条目 `reasoning: true`(这是唯一会让存量部署行为变的一格);中心名册里没写 `vision` 的条目升级前被当纯文本
|
|
36
|
+
(带图请求的图被换成占位句并计数告警),升级后图会原样交给模型 —— 纯文本模型请在条目里写 `vision: false`(env 部署写 `MODEL_VISION=false`)。
|
|
37
|
+
- `GET /v1/models` 行的 `reasoning` / `vision` 两列:声明了给布尔,**未声明 = 键缺席**(env 部署未设这两键时,此前给 `true`);读这两列的消费方把缺席读成「未知」。
|
|
38
|
+
`GET /v1/config/catalog` 的 `MODEL_REASONING` / `MODEL_VISION` 两行不再带 `staticDefault`,改写缺席语义说明。
|
|
39
|
+
4. **settings-schema 精确钉 13.0.1**(`12.2.1` → `13.0.1`,跨 12.3.0 与 13.0.0 major):上面三条是它在本仓的全部可见变化(13.0.1 的修补 —— 「藏守卫迹象的未知字段」那一形的拒绝项带上点名那个键的 `field` —— 是第 1 条里 M20 剥键的前提);无 DDL、无新旋钮、无升级序要求。
|
|
40
|
+
同批新增的设置源表 / 层准入 / http hook 名单 / MCP 名单判决 / 界面语言片段本版不接。
|
|
41
|
+
5. **嵌入方(自己装配 `createHttpServer` 的部署)两件硬破坏,tsc 红即告示**:
|
|
42
|
+
- **读口场景门接缝必填**:`createHttpServer` 的核心件由四变五,新增 `scenarioGate`(型 `ScenarioGate`)。用包根导出的同一只工厂铸:`createScenarioGate({ config, scenarios, principalCaps, logger }, "peek")`
|
|
43
|
+
(任务提交腿用的是同一个工厂的 `"enforce"` 腿;`ScenarioGateLeg` / `ScenarioGateSources` 一并导出)。此前没递接缝时三张读口自己退到「点名 404 / 缺席读部署缺省」,那两条回退臂已删 ——
|
|
44
|
+
读口的场景名只有门一个来源;生产装配(`sema-server` 可执行)一直递着,**只有**自建装配需要动。
|
|
45
|
+
- **模型路由的拒启判在配置源应用之后**:`loadConfig()` 不再在读 env 那一拍判「每条 openai 兼容路由都解析到端点」;判官判的是**将要服务的那张表**(目录被采纳 ⇒ 目录表;否则 env 表)。
|
|
46
|
+
自建装配在**自己的**配置源(目录 / 中心)应用之后调一次包根导出的 `judgeServedModelRoute(config, { envTableIsPlaceholder })`(有判决就拒启,`reason` 是给运维的原句;严格名册布防时传 `true`,
|
|
47
|
+
没有名册门传 `false`)—— 与 7.104.0 执行车道不变量搬位同样的形(`assertExecutionLaneInvariants`)。只调 `loadConfig()` 的开发脚本若不调它,路由缺口会在第一次模型调用时才以引擎拒句出现。
|
|
48
|
+
- 包根 `judgeModelRouteGap(env)` 语义不变:它答的是 **env 腿**(没有目录在管时这份 env 起不起得来);目录在场时起服以目录表为准。
|
|
49
|
+
6. **请求体 `mcpServers[i]` 的每个受理键写坏 ⇒ `400 request.field_invalid`(此前 `200`)**(零 DDL;与 7.130.0 节 `toolAxes` 那一条同形,这回覆盖条目的**每一个**受理键):
|
|
50
|
+
受理键 = `name` · `transport`(`kind` / `command` / `args` / `env` / `url` / `headers` / `principalHeader` / `assertionHeader`)· `allowTools` · `toolAxes` · `toolFaces` · `contentOrigin` · `elicitation` · `source`。
|
|
51
|
+
新提交(`POST /v1/tasks` · `/v1/tasks/stream` · `/v1/runs`)里这些键任何一个在场却写坏(如 `"allowTools": "x"`、`"args": "-y pkg"`、`"elicitation": null`、`"source": ""`、超长 server 名),
|
|
52
|
+
此前一律 `200`:`name` / `transport` 的硬字段 / `allowTools` / `toolFaces` 写坏则这一条 server 整条不挂,`elicitation` / `source` / `transport` 的可选子键写坏则悄悄丢掉那个键、server 照挂;
|
|
53
|
+
现在整份请求 `400 request.field_invalid`,拒句点名 `mcpServers[i].<键路径>`、server 名与该键判官的原因句,整份不收(不挂一半、不丢声明、不默认)。
|
|
54
|
+
**谁撞**:自己拼 `mcpServers` 的调用方,以及把 `.mcp.json` 投影进请求体的壳 —— 写坏一条,整份提交都 400,不再是「那台 server 没挂」「那个参数没生效」。
|
|
55
|
+
**出路**:按拒句改对那个键,或把它删掉(可选键缺席永远合法,在场的 `null` 不是缺席;`name` 与 `transport` 是必填键,缺席同样 400)。`POST /v1/capabilities/mcp/probe` 对同一类写坏同样 400(码相同,拒句措辞另有,拒之前零拨号),上线前可以先拿它核一遍。
|
|
56
|
+
不变:耐久 resume 重放升级前存下的请求体时仍不拒(拒会把升级前停驻的任务永久卡死)—— 写坏的键照旧点名丢弃 / 丢键;条目本身不是对象、词表外的键、第 33 条起的超上限尾项不在这道门的射程。升级零动作。
|
|
57
|
+
7. **嵌入方:`Model.reasoning` / `Model.input` 在运行期可以缺席,而引擎 7.51.2 的 `Model` 型面仍把这两键写成必填**(只对读 `config.model` / `config.models[…]` 的嵌入方可见,tsc 不会报):
|
|
58
|
+
第 3 条之后,env 部署不设 `MODEL_REASONING` / `MODEL_VISION`、中心名册条目不写 `reasoning` / `vision` 时,铸出的 `Model` 上**没有**对应的键 —— `reasoning` 缺席,`vision` 对应的 `input` 缺席。
|
|
59
|
+
自带代码里 `config.model.input.includes("image")` 这类读法照旧编译通过,运行期对缺席的 `input` 抛 `TypeError`;`config.model.reasoning` 缺席读到 `undefined`,按假值走。
|
|
60
|
+
**出路**:升级后先判缺席再读 —— `config.model.input?.includes("image")`、`config.model.reasoning === true`;缺席 = 未声明,不等于 `false`(引擎对缺席的读法见第 3 条)。型面放宽候引擎下一版,届时 tsc 才会替你报这类读法;在那之前请自查。
|
|
61
|
+
8. **写错的工具名恢复开跑前拒并点名现名:限制侧四类全拒,放行侧只拒近似名(行为变化 · BREAKING:此前照跑的请求现在 400 / 422;7.129.0–7.130.x 只告运维)**:
|
|
62
|
+
- **限制侧**(根 run 的 `excludeTools`、子代定义的 `agents[i].denyTools`、请求自带技能的 `disallowedTools`、请求体 `settings.permissions.deny` / `ask`)里,引擎读成空的条目 ——
|
|
63
|
+
**近似名**(`BASH`)· **退役名**(`bash` · `Task` ⇒ `Agent`)· **文法读不出**(括号不配、首尾空白、空串、协议名段铸不出)· `*` · 规则表上不带内容形的工具带内容 —— 提交时拒,句子点名条目、原因词与该写的拼法,例:
|
|
64
|
+
`agents[0].denyTools entry "BASH" reads as nothing (near_miss): the tool name differs from a mounted tool only by case — write "Bash" — a deny entry that reads as nothing refuses nothing, so it is refused here rather than taken as in effect`。
|
|
65
|
+
码沿用各表此前的码:子代 `denyTools` ⇒ `400 config.agent.unknown_tool`(附录 A 新入册)· 根 `excludeTools` ⇒ `400 config.tool_face_control` · 技能 `disallowedTools` ⇒ `400 request.field_invalid`(不变)·
|
|
66
|
+
请求体 `permissions.deny` / `ask` ⇒ `422`(该层既有的收紧表拒形,`request settings cannot be applied as written: …`,改法带上条目其余部分:`BASH(*)` ⇒ 写 `Bash(*)`)。
|
|
67
|
+
理由:限制侧读成空 = 静默放行 —— 写了 `denyTools: ["BASH"]` 以为禁了 `Bash`,子代照用,用户面零点名。
|
|
68
|
+
- **放行侧只拒近似名**(请求体 `settings.permissions.allow`、根 / 子代 `allowTools`、技能 `allowedTools`):**近似名**(`BASH`)—— 现名唯一、确定是笔误,想放 `Bash` 却什么也没放(静默收窄)。
|
|
69
|
+
码:根 `allowTools` ⇒ `400 config.tool_face_control`,子代 `allowTools` ⇒ `400 config.agent.unknown_tool`,`permissions.allow` / 技能 `allowedTools` ⇒ `400 request.field_invalid`。
|
|
70
|
+
**退役名**(`Task` · `MultiEdit` · `bash`)、**内容形**(`Agent(Explore)` 等)、**文法读不出**(`Bash(echo $HOME)` 里的 `$VAR`、带空白的名字、括号不配)、**协议名对端段通配**(`mcp__*` · `mcp__git*__x` · `a2a__*`)在放行侧**照 7.130.0 受理、交引擎既有通告**(壳转投的 CC 用户 settings 里这些极常见,尤其内容形带 `$VAR` 的 `Bash(echo $HOME)`,拒了 = 每次提交 400;不要为它改请求):引擎侧的终形是把退役名归一到现名并在用户面点名,
|
|
71
|
+
到货前放行侧不拒;文法读不出的放行条目授不出去 = 收窄而不是放行:规则表(`permissions.allow` / 技能 `allowedTools`)上受理口不拒、引擎开跑时告运维;选择器清单(根 / 子代 `allowTools`)上受理口不拒、引擎开跑前自己拒(子代 ⇒ 终局 `failed` 带 `config.agent.unknown_tool`,同步腿 HTTP 200 + failed 体;根 ⇒ `400 config.tool_face_control`)。限制侧同样的退役名 / 文法读不出照旧拒(`excludeTools: ["Task"]` ⇒ 400)。`settings.permissions.allowLayer` 能力位语义不变。
|
|
72
|
+
- **真未知名**(拼写合法、没有近似也没有退役对应 —— 懒挂载的合法形,如一台之后才连上的 MCP 服务器的工具)⇒ **照旧**保留、运维行 `runner_config_advisory` 告知一次,不拒(7.129.0 第 5 条对这一类仍成立)。
|
|
73
|
+
- **选择器只取条目里工具名那一段再判名字**(根 / 子代的 `allowTools` / `excludeTools` / `denyTools`):限制侧 `Tool(content)` 的内容不读(引擎读成整只工具 —— `Agent(x)`、`Write(*)`、`Bash(npm:*)`、`Bash(echo $HOME)` 不拒);
|
|
74
|
+
`allowTools:["*"]`(本层不设名单)与限制侧整个协议的拒 `mcp__*` / `a2a__*` 是合法写法。规则表(`settings.permissions.*`、技能清单)里整条是规则:内容的文法读不出(`Bash(echo $HOME)`)是 `grammar`:限制侧拒,放行侧照收。
|
|
75
|
+
- **名册只有一张 = 引擎目录的全部工具名 ∪ 本服务自己铸给模型的工具名**(`Now` / `SendUserFile` / `SelectEnvironment` / `repo_tree` / `repo_read_file` / `repo_pull_diff`,名字取自铸它们的那一处):每一处读条目名的地方传同一张 ——
|
|
76
|
+
工具名清单、请求体 `settings.permissions.*`、技能清单、运维 `APPROVAL_REQUIRE` / `APPROVAL_DENY` / `APPROVAL_NEVER_AUTO` / `APPROVAL_GRANT`、中心 `governance.approvalRequire`。此前后几处用空名册,读不出孪生(`BASH(*)` 照收、什么也不拒)。
|
|
77
|
+
**运维名单里写着孪生名字的部署升级后拒启**(`APPROVAL_REQUIRE=write` 要写成 `Write` —— 名表逐字比对,孪生那道门本来就没生效);中心下发的 `approvalRequire` 含孪生 ⇒ 整组被拒、活配置保前值(世代账 `approvalGate.lastRejected`、一条 warn 点名坏条目)。
|
|
78
|
+
技能 `disallowedTools:["BASH"]` 这类孪生此前受理口收下、引擎在技能激活时才拒,现在提交即 `400 request.field_invalid`。嵌入方自己另挂的宿主工具不在名册里,它们的孪生仍只由引擎在开跑时告知运维。
|
|
79
|
+
- **拒句与扫描有界**:清单先过条数帽(与其它条目表同一个数 1000,`allowLayerEntryCountIssue`):超帽 ⇒ **整只** `400 request.field_invalid`(体带 `field` / `reason: "over_cap"` / `count` / `cap`),不逐条读;
|
|
80
|
+
帽内的坏条目拒句只点名前 5 条,其余写成 `; and K more`。根 `allowTools` / `excludeTools` 此前没有条数帽 —— 写了上千条的调用方现在 400(合并成更宽的条目,或精简)。
|
|
81
|
+
- **升级前停驻的任务 · 条数帽不分方向、其余按方向分流**:升级前存下的请求体带上述条目(7.129.0–7.130.x 受理),续跑时:
|
|
82
|
+
**超帽的清单(任一方向)** ⇒ 认领之前 `409 resume_blocked_by_policy`,`blockedBy: request.field_invalid`,句子点名 `count` / `cap`;存体不改写、不进引擎(条数帽是资源帽 —— 引擎每次准备都逐条播种这些表 —— 不是语义 inert,
|
|
83
|
+
放行方向的超帽存体也 409;**升级前停驻存体的限制侧清单超 1000 条同样 409**)。帽内:
|
|
84
|
+
**限制方向**(`excludeTools` · 子代 `denyTools` · 技能 `disallowedTools` · `settings.permissions.deny` / `ask`)读成空 ⇒ 认领之前 `409`,`blockedBy` = 该层的码(`permissions.deny` / `ask` 是 `request.unprocessable`),`error` = 同一句;
|
|
85
|
+
行 / 卡 / ctx 不动(放宽方向不能默认:那条限制从没生效过)。出路 = 取消该任务、把条目改成现名(拒句里 `write "Bash"` 就是该写的)后重新提交。plan_review 的一次性 nonce 在这一拒之后仍未消费,终态 steer 不铸唤醒卡,SLA 拒扫跳过这条会话。
|
|
86
|
+
**放行方向**(`allowTools` · 子代 `allowTools` · `settings.permissions.allow` · 技能 `allowedTools`)读成空(近似名)⇒ **照旧续跑**,条目原样留着、仍然什么也没放(升级前它本就这样跑着,继续是安全方向),不 409;
|
|
87
|
+
每次重建一行运维 warn `stored_allow_list_inert_resumed`(`sessionId` · `layer` · 有界的 `sentence`),想让条目真生效就在新提交里改成现名。
|
|
88
|
+
运维日志三个事件(`resume_refused_stored_body` / `steer_wake_park_refused_stored_body` / `deny_sweep_stored_body_skipped`)的 `kind` 字段值 `skill_deny_inert` 改为 `inert_tool_list`(按旧值配了告警的部署改配;只在 409 / 跳过上出现),
|
|
89
|
+
`layer` 取 `skills` / `agents.skills` / `skills.allowedTools` / `agents.skills.allowedTools` / `agents.denyTools` / `agents.allowTools` / `excludeTools` / `allowTools` / `settings.permissions.allow` / `settings.permissions.deny` / `settings.permissions.ask`。
|
|
90
|
+
随包 `scripts/drain-check-local.mjs` 的 `skill-deny-inert` 判据与随包 SQL 两段仍只数技能拒侧两层,**不覆盖**其余清单;想在升级前找这类停驻任务,按 `checkpoint_ctx.ctx` 里的 `body.allowTools` / `body.excludeTools` /
|
|
91
|
+
`body.agents[*].allowTools` / `body.agents[*].denyTools` / `body.settings.permissions` 找。
|
|
92
|
+
- **三问** — **谁需要**:写限制侧清单的调用方,尤其把工具名写成 `BASH` / `bash` / `Task` 的(以为禁了、其实没禁);放行侧写成 `BASH` 的(以为放了、其实没放);**谁受伤**:此前照跑的这类请求现在 400 / 422(它们的清单本来就没按字面生效);
|
|
93
|
+
运维 `APPROVAL_*` 里写着孪生名字的部署升级后拒启;升级前停驻的限制方向带此类条目(或任一方向超帽)的任务续跑 409,放行方向的照旧续跑 + 一行 warn;写上千条的根清单现在 400;
|
|
94
|
+
**补偿**:拒句点名条目、原因词与该写的拼法,改成现名即过;放行侧的退役名 / 内容形 / 文法读不出 / 对端段通配、真未知名不受影响;无旋钮。
|
|
95
|
+
- **受理面只判本次请求送来的 `agents` 与规则表,上一轮的不带入**:引擎每个 run 按本次 spec 重建委派工具(不带 `agents` 的 run 只看到部署级定义);子代的保留(retain)是进程内存里的账 —— 按 run 腿建、腿收尾释放(失败子代另有按会话键的 15 分钟账),不落盘、不跨升级;升级重启后不存在带旧定义继续跑的子代。
|
|
96
|
+
|
|
97
|
+
## 7.130.0 —— 请求体 `mcpServers[].toolAxes` 坏形 400(含显式 `null`)· 升级前停驻任务的一次性迁移 M19 · 会话标题 / hooks 选线随主请求 · 技能 frontmatter 收尾只认整行 `---`
|
|
98
|
+
|
|
99
|
+
1. **请求体 `mcpServers[].toolAxes` 与部署声明读同一只判官**(零 DDL):新提交(`POST /v1/tasks` · `/v1/tasks/stream` · `/v1/runs`)里坏形的 `toolAxes`
|
|
100
|
+
—— 效应词出表、未知成员、某只工具的值不是对象、整份不是对象(**含显式 `"toolAxes": null`**)、工具名为空 —— 此前剔掉坏成员、其余照挂、请求照常 200,
|
|
101
|
+
现在 `400 request.field_invalid`,拒句点名 `mcpServers[i].toolAxes` 与原因(整份声明不收)。**出路**:不想声明就不写这个键;写了就按拒句改对。
|
|
102
|
+
`POST /v1/capabilities/mcp/probe` 同构造同拒。部署侧(`config.d/mcp.json` / 配置中心)读法不变。
|
|
103
|
+
2. **升级前停驻的任务(一次性迁移 M19,窗 = 本版发布日起 30 天)**:升级前存下的请求体里 `toolAxes` 判不过的,续跑时窗内按升级前的读法重读一次
|
|
104
|
+
(不是对象读成没声明;对象里过不了形的成员剔掉、好的留下),这台 server 照挂,任务批准后照常续跑;每次重读记一行运维 warn
|
|
105
|
+
`mcp_tool_axes_read_as_before_upgrade`。窗关之后(或重读仍判不过)该 server 整条不挂,运维 warn `mcp_injection_dropped` + 用户面通告 `mcp.injection_dropped`。
|
|
106
|
+
升级零动作;想提前收口就在窗内把这些停驻任务决掉。
|
|
107
|
+
3. **会话自动标题与 settings hooks 的 prompt / agent 条目**:`MODEL_PROVIDER=anthropic` 而只配了网关(未配 anthropic 路由)的部署上,这两条腿此前整条拒发
|
|
108
|
+
(会话列表无自动标题、hook 的 LLM 判定不生效),现在与主请求同向走网关 —— 升级后每个新会话会向网关多发一次标题请求(开着自动标题时;会话已有标题就不再发)。零动作。
|
|
109
|
+
4. **技能 frontmatter**:收尾只认整行 `---`(行尾只许空白 / CR)。此前正文里以 `---` 开头的行(如 `---x`)会提前收尾,其后的 `name` / `description` / `scenarios`
|
|
110
|
+
被丢 —— 这类技能升级后读到完整元数据,其中 `scenarios` 限制从此生效(此前因丢失而对所有场景可见)。部署了这类技能的,升级后核一次它出现在哪些场景。
|
|
111
|
+
|
|
10
112
|
## 7.129.1 —— k8s / e2b 车道:带超时的命令不再被静默切断(行为修复)· 新旋钮 `K8S_LIVENESS_MS` · 两只活性旋钮非正值拒启
|
|
11
113
|
|
|
12
114
|
1. **一条命令的界只来自调用方说出口的那些**(零 DDL):墙钟 `timeout`、流式面自己的 `readTimeoutMs`;两样都没说时才由车道活性界(无输出满 N 毫秒即切,
|
package/README.md
CHANGED
|
@@ -133,9 +133,12 @@ curl -s localhost:8090/v1/tasks -H "Authorization: Bearer <SERVICE_AUTH_TOKEN>"
|
|
|
133
133
|
model-catalog entry `headers` and the API line that reads each name. Both are frozen, the very objects the server judges with.
|
|
134
134
|
A shell or package that must know **before the engine starts** whether the model route would stop it imports `judgeModelRouteGap(env)`
|
|
135
135
|
(pure; takes an env snapshot) from the package root: `{ gap: "endpoint_missing" | "lane_credential_missing" | "none" }`, plus `code` and the
|
|
136
|
-
refusal sentence `reason` when there is a gap. It
|
|
137
|
-
|
|
138
|
-
(
|
|
136
|
+
refusal sentence `reason` when there is a gap. It answers for the **env leg** — "with no catalog in charge, does this env start?" — and is the
|
|
137
|
+
very judgment the start-up makes on the env model table, not a copy of it, so the reading and the refusal sentence are the same by construction.
|
|
138
|
+
When a catalog is adopted the start-up judges the catalog table instead (env `MODEL_ID` does not serve), so a shell that predicts the refusal must
|
|
139
|
+
know whether a catalog is coming. `none` says the route gap is absent, not that the engine will start (other invariants refuse with their own codes).
|
|
140
|
+
The start-up's own route judge runs **after** the catalog is applied, not in `loadConfig()`: a hand-built assembly calls `judgeServedModelRoute(config, { envTableIsPlaceholder })`
|
|
141
|
+
(package root) once after its own config-source apply, like `composeExecutionLane`.
|
|
139
142
|
- **Bundled binaries**: the package ships two `bin` entries — `run-local` (single-machine local
|
|
140
143
|
runner) and `sema-up` (deployment bootstrap script).
|
|
141
144
|
- `run-local` boots the same engine in-process, runs one task from the CLI objective, prints the
|
|
@@ -182,10 +185,10 @@ The server is configured entirely through environment variables. The most import
|
|
|
182
185
|
|
|
183
186
|
| Variable | Default | What it does |
|
|
184
187
|
|----------|---------|--------------|
|
|
185
|
-
| `PORT` | `8090` | HTTP listen port |
|
|
188
|
+
| `PORT` | `8090` | HTTP listen port. `0` = let the OS pick one: the `listening` log line (stdout, info level) and `/health.port` report the port actually bound |
|
|
186
189
|
| `BIND_HOST` (alias `HOST`) | see note | Listen address. An explicit `BIND_HOST` **always wins**. Default: `127.0.0.1` when the write face is unauthenticated (`ALLOW_UNAUTHED_WRITES=true` **and** no service token configured), otherwise all interfaces — deployments with a token are unaffected. Since 3.15.0 the `HOST` alias is **not** fully equivalent: shells commonly set `HOST` to the machine name without the operator knowing, so when the narrowing condition above holds it wins over an inherited `HOST` (logged as `bind_host_from_HOST_env_overridden`). Set `BIND_HOST` explicitly, or configure a service token, to expose the write face. |
|
|
187
190
|
| `MODEL_GATEWAY_BASEURL` | **no default** | OpenAI-compatible gateway base URL (without `/chat/completions`). **No factory default**: every route that rides the openai-compatible lane must resolve to an explicit endpoint — the model's own catalog `baseUrl`, or this knob — else the server refuses to boot, naming the models and printing the explicit local spelling (`MODEL_GATEWAY_BASEURL=http://127.0.0.1:8000/v1`). An Anthropic-only deployment may leave it unset. Setting `MODEL_GATEWAY_FALLBACK_URLS` without it is refused too |
|
|
188
|
-
| `MODEL_ID` | **required unless a catalog supplies the default** | Default model id — **no factory default since 3.0.0**. Since 7.101.0 the check runs **after** the catalog is applied: `MODEL_ID` **or** a usable catalog default (`config.d/models.json` / the web-admin) is enough; with neither the server refuses to boot (`config.default_model_missing`, naming both ways out). The old baked-in default was an internal-only model name, so every external deployment failed later and further from the cause: a gateway `400` plus a cascade of title-hook warnings |
|
|
191
|
+
| `MODEL_ID` | **required unless a catalog supplies the default** | Default model id — **no factory default since 3.0.0**. Since 7.101.0 the check runs **after** the catalog is applied: `MODEL_ID` **or** a usable catalog default (`config.d/models.json` / the web-admin) is enough; with neither the server refuses to boot (`config.default_model_missing`, naming both ways out). A catalog that is adopted replaces the env model table, so `MODEL_ID` is then not served: the boot is not refused (even with no gateway, when the catalog entries carry their own `baseUrl`) and logs one `config_env_set_but_ineffective` line naming the catalog default that serves (exactly once, at the moment the catalog is first adopted — at boot, or later when it arrives after the boot fetch budget / on a refresh). The old baked-in default was an internal-only model name, so every external deployment failed later and further from the cause: a gateway `400` plus a cascade of title-hook warnings |
|
|
189
192
|
| `MODEL_API_KEY` | — | Gateway API key (optional). Pairs only with `MODEL_GATEWAY_BASEURL`: with no gateway it is not loaded and sent nowhere |
|
|
190
193
|
| `SERVICE_AUTH_TOKEN` | — | Callers must send `Authorization: Bearer <token>` |
|
|
191
194
|
| `DB_BACKEND` | `local`* | `mysql` (any MySQL-protocol DB: MySQL/TiDB/MariaDB) / `pg` (PostgreSQL) / `local` (file-backed, no DB) / `memory` (explicit in-memory: nothing survives a restart, durable-runs faces 501). *Bare boot (no DB env at all) defaults to `local` so a single-user machine keeps its runs across restarts; any SQL signal (`SESSION_BACKEND` or `MYSQL_/PG_HOST`) keeps the `mysql` engine default, and `REQUIRE_PRINCIPAL=true` bare boots stay `memory`. (`TIDB_HOST`/`_PORT`/`_USER`/`_PASSWORD`/`_DATABASE`/`_POOL_SIZE` are retired names, not an alias — since 5.0.0 setting any of them refuses to boot, pointing at the `MYSQL_*` replacement; TiDB itself connects fine through `MYSQL_*`, since it speaks the MySQL protocol.) (the local file store has no tenant isolation — a warning says so). A DEFAULT-derived `local` that cannot create its data root degrades to memory with a warning + the `store_backend_degraded` gauge; an EXPLICIT `DB_BACKEND=local` fails loud instead. Setting `mysql`/`pg` explicitly also switches sessions to durable |
|
package/README.zh-CN.md
CHANGED
|
@@ -115,8 +115,10 @@ curl -s localhost:8090/v1/tasks -H "Authorization: Bearer <SERVICE_AUTH_TOKEN>"
|
|
|
115
115
|
构建期工具(如目录编辑页)从包根取两张表、不手抄:`RETIRED_ENVS`(退役 env 墓碑)与 7.110.0 起的 `CATALOG_ENTRY_HEADER_LANES` /
|
|
116
116
|
`CATALOG_ENTRY_HEADER_NAMES`(+ 型 `CatalogEntryHeaderName`)—— 模型目录条目 `headers` 的闭集白名单及读每个名字的线路。两者都冻结,就是服务端自己判据用的那一只。
|
|
117
117
|
壳 / 包要在**引擎起来之前**知道模型路由会不会让它起不来,从包根调 `judgeModelRouteGap(env)`(纯函数,吃一份 env 快照):
|
|
118
|
-
`{ gap: "endpoint_missing" | "lane_credential_missing" | "none" }`,有缺口时另带 `code` 与拒启句 `reason
|
|
119
|
-
(不是它的副本)
|
|
118
|
+
`{ gap: "endpoint_missing" | "lane_credential_missing" | "none" }`,有缺口时另带 `code` 与拒启句 `reason`。它答的是 **env 腿** ——「没有目录在管时,这份 env 起不起得来」——
|
|
119
|
+
就是起服对 env 模型表做的那一判(不是它的副本),所以读数与拒启句同源。目录被采纳时起服判的是目录表(env `MODEL_ID` 不服务),
|
|
120
|
+
壳要预判拒启得自己知道有没有目录要来。`none` 只说路由缺口不在,不担保起得来(别的不变量各自带码拒启)。
|
|
121
|
+
起服自己的路由判官在**目录应用之后**判,不在 `loadConfig()` 里:自建装配在自己的配置源应用之后调一次包根的 `judgeServedModelRoute(config, { envTableIsPlaceholder })`(与 `composeExecutionLane` 同形)。
|
|
120
122
|
- **随包二进制**:包内带两个 `bin` —— `run-local`(单机本地 runner)与
|
|
121
123
|
`sema-up`(部署引导脚本)。
|
|
122
124
|
- `run-local` 在进程内启同一套引擎,把命令行给的目标跑完一次、打印结果、退出——没有 HTTP 服务,
|
|
@@ -153,10 +155,10 @@ curl -s localhost:8090/v1/tasks -H "Authorization: Bearer <SERVICE_AUTH_TOKEN>"
|
|
|
153
155
|
|
|
154
156
|
| 变量 | 默认 | 说明 |
|
|
155
157
|
|------|------|------|
|
|
156
|
-
| `PORT` | `8090` | HTTP
|
|
158
|
+
| `PORT` | `8090` | HTTP 监听端口。`0` = 让 OS 分一个:`listening` 日志行(stdout,info 级)与 `/health.port` 报实际绑到的口 |
|
|
157
159
|
| `BIND_HOST`(别名 `HOST`) | 见说明 | 监听地址。显式 `BIND_HOST` **恒生效**。缺省:写面无鉴权时(`ALLOW_UNAUTHED_WRITES=true` **且**未配任何 service token)= `127.0.0.1`,否则全接口——配了 token 的部署不受影响。自 3.15.0 起 `HOST` 别名**不再完全等效**:shell 常把 `HOST` 设成机器名而 operator 并不知情,于是上述收窄条件成立时,收窄压过继承来的 `HOST`(告警事件 `bind_host_from_HOST_env_overridden`)。要暴露写面,显式设 `BIND_HOST`,或配一个 service token。 |
|
|
158
160
|
| `MODEL_GATEWAY_BASEURL` | **无缺省** | OpenAI 兼容网关地址(不带 `/chat/completions`)。**无出厂缺省**:每一条落 openai 兼容腿的路由都必须解析到显式端点 —— 目录条目自己的 `baseUrl`,或本键 —— 否则拒启,拒因点名模型并给出本机写法(`MODEL_GATEWAY_BASEURL=http://127.0.0.1:8000/v1`)。anthropic 单路由部署可不设。只设 `MODEL_GATEWAY_FALLBACK_URLS` 不设本键同拒 |
|
|
159
|
-
| `MODEL_ID` | **必填(目录给出缺省时可省)** | 缺省模型 id ——**3.0.0 起无出厂缺省**。7.101.0 起判据挪到目录应用**之后**:`MODEL_ID` **或**目录里一条可用的缺省(`config.d/models.json` / 配置控制面)任一在场即可;两者皆无 = 拒启(码 `config.default_model_missing`,指路二选一)。旧的烤死缺省是内网模型名,外部部署必炸且炸在离根因最远处:网关 `400` + 标题 hook 连环告警 |
|
|
161
|
+
| `MODEL_ID` | **必填(目录给出缺省时可省)** | 缺省模型 id ——**3.0.0 起无出厂缺省**。7.101.0 起判据挪到目录应用**之后**:`MODEL_ID` **或**目录里一条可用的缺省(`config.d/models.json` / 配置控制面)任一在场即可;两者皆无 = 拒启(码 `config.default_model_missing`,指路二选一)。目录一旦被采纳就整张换掉 env 表,`MODEL_ID` 此时不服务:起服不拒(目录条目自带 `baseUrl` 时没有网关也能起),并说一行 `config_env_set_but_ineffective` 点名在服务的目录缺省(目录第一次被采纳的那一拍恰好说一次 —— 启动时,或目录晚到 / 刷新时才第一次采纳)。旧的烤死缺省是内网模型名,外部部署必炸且炸在离根因最远处:网关 `400` + 标题 hook 连环告警 |
|
|
160
162
|
| `MODEL_API_KEY` | — | 网关 key(可选)。只与 `MODEL_GATEWAY_BASEURL` 配对:没设网关时不装载、不发往任何路由 |
|
|
161
163
|
| `SERVICE_AUTH_TOKEN` | — | 调用方需带 `Authorization: Bearer <token>` |
|
|
162
164
|
| `DB_BACKEND` | `local`* | SQL 引擎:`mysql`(任何 MySQL 协议库:MySQL/TiDB/MariaDB —— TiDB 走这个值,**没有** `tidb` 别名,写它启动即拒)/ `pg`(PostgreSQL)/ `local`(免 DB 文件持久化)/ `memory`(显式纯内存)。显式设置 `mysql`/`pg` 时 session 自动转 durable。*单租户裸 boot 缺省 `local`;`REQUIRE_PRINCIPAL=true` 的多租户裸 boot 缺省 `memory`(local 与多租户互斥) |
|
package/USAGE.md
CHANGED
|
@@ -102,12 +102,18 @@ MODEL_GATEWAY_BASEURL=http://gw-a:8000/v1 MODEL_GATEWAY_FALLBACK_URLS=http://gw
|
|
|
102
102
|
ANTHROPIC_API_KEY=sk-ant-… # 可选:ANTHROPIC_BASE_URL / ANTHROPIC_VERSION / ANTHROPIC_CACHE_BREAKPOINTS=true
|
|
103
103
|
```
|
|
104
104
|
- 两个变量都不设 = 和以前**逐字节一致**(单网关)。
|
|
105
|
-
- 🔴 **`MODEL_GATEWAY_BASEURL` 没有出厂缺省**(BREAKING)
|
|
105
|
+
- 🔴 **`MODEL_GATEWAY_BASEURL` 没有出厂缺省**(BREAKING):**将要服务的那张表**里每一条落 openai 兼容腿的路由(主模型 / cheap 槽 /
|
|
106
106
|
目录条目 / 降级目标 / titler 所选模型)都必须解析到显式端点 —— 条目自己的 `baseUrl`,或本键 —— 否则拒启,拒因
|
|
107
107
|
(码 `config.model_route_endpoint_missing`)点名解析不到的模型并给出本机写法 `MODEL_GATEWAY_BASEURL=http://127.0.0.1:8000/v1`。
|
|
108
|
-
|
|
108
|
+
**判哪张表**:判在目录应用之后(不在读 env 的那一拍)—— 一份目录被采纳(`config.d/models.json` 或中心的启用条目落了模型面)⇒ 判目录表,
|
|
109
|
+
拒因教的出路是「设网关,或在目录里给条目自己的 `baseUrl`」;目录缺席 / 被整拒 / 没有一条启用条目 ⇒ 服务的是 env 表(`MODEL_ID` / `MODEL_CHEAP_ID`),
|
|
110
|
+
判 env 表,拒因教的是 env 的三条出路:设 `MODEL_GATEWAY_BASEURL` / 挂 Anthropic 路由(`MODEL_PROVIDER=anthropic` + `ANTHROPIC_API_KEY` 或 `ANTHROPIC_AUTH_TOKEN`)/ 不设 `MODEL_ID` 让目录缺省服务
|
|
111
|
+
(env 行没有自己的 `baseUrl`,「给条目自己的 baseUrl」不是它的出路)。
|
|
112
|
+
只设 `MODEL_GATEWAY_FALLBACK_URLS` 不设主网关同拒(`config.gateway_fallback_without_primary`,与服务哪张表无关)。只走 Anthropic 路由的部署
|
|
109
113
|
不设网关合法;此时 `MODEL_API_KEY`(网关的凭据)不装载、不发往任何路由(启动通知 `model_gateway_key_unpaired`)。
|
|
110
114
|
中心 / config.d 下发的候选目录里有解析不到的条目 ⇒ 候选的模型面这一组被拒、上一代模型面继续服务(同候选其余组照落)。
|
|
115
|
+
- **目录与 `MODEL_ID` 并存**:目录一旦被采纳就整张换掉 env 表 —— 缺省模型取目录缺省,env 的 `MODEL_ID` 不服务。此时起服**不拒**(哪怕没设网关:目录条目自带 `baseUrl` 即可),
|
|
116
|
+
而是说一行 warn `config_env_set_but_ineffective`(`env: "MODEL_ID"`,点名目录缺省是谁),让你知道改 `MODEL_ID` 没有效果 —— 目录第一次被采纳的那一拍说,恰好一次(启动时就采纳、或目录晚到 / 刷新时才第一次采纳都一样)。`MODEL_ID` 仍是目录缺席时的兜底(目录迟到且无上一份 / 候选被整拒 / 没有启用条目时由它服务,届时按 env 表判)。
|
|
111
117
|
- **凭据什么时候生效**:网关 key `MODEL_API_KEY`(以及 `ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN`)在启动时读一次,**换值 = 重启本服务**
|
|
112
118
|
—— 没有热应用通道(以前的版本同样如此:这几把 key 只来自启动时的环境变量;引擎 7.51.0 起模型路由的构造配置也只读一次)。
|
|
113
119
|
目录条目**自己的**凭据(`apiKeyEnv` 点名的变量、`sealedApiKey`)跟着中心 / config.d 的模型面热应用走,不用重启:新起的任务与调用
|
|
@@ -127,6 +133,9 @@ ANTHROPIC_API_KEY=sk-ant-… # 可选:ANTHROPIC_BASE_URL / ANTHROPIC_VERSION /
|
|
|
127
133
|
而档位表会铸出这个词时,这份候选的模型面整组被拒(码 `config.model_refused_name_minted`,warn `web_admin_model_plane_refused` 点名
|
|
128
134
|
「被拒名 → 目标条目」)—— 否则显式点它、或绑它的角色,会静默跑在另一条条目的端点与账号上;出路:改名 / 修好条目,或改绑那条档位绑定。
|
|
129
135
|
- **failover ≠ Anthropic↔vLLM**:failover 给所有 brain 发**同一个 model**(同协议同 id 的冗余);云↔本地是**按 `model.provider` 路由**的选择,不是故障转移(两者 model id/参数不同,不能透明互切)。设 `MODEL_PROVIDER=anthropic` + `MODEL_ID=claude-…` 让整个服务走 Anthropic。
|
|
136
|
+
- **会话自动标题与 hooks 的 `prompt` / `agent` 条目走哪条线路,和主请求读同一只判据**:`provider="anthropic"` 的模型只在 anthropic 路由装上(配了 `ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN`)时走云 `/v1/messages`;
|
|
137
|
+
没装(如 `MODEL_PROVIDER=anthropic` 却只配了 `MODEL_GATEWAY_BASEURL` + `MODEL_API_KEY`)时主请求落网关,这两个面的廉价直连调用同样落网关的 openai 兼容线路(`/chat/completions`),
|
|
138
|
+
端点与凭据照网关的来 —— 此前它们按 `provider` 字面去找一条没装的 anthropic 路由而整条拒发,会话列表没有自动标题。网关端点也解析不到才拒(`config.model_route_endpoint_missing`)。
|
|
130
139
|
- **缺省推断**:`MODEL_PROVIDER` **未设**、但显式配了 Anthropic 协议 base URL(`ANTHROPIC_BASE_URL`)**和**对应凭证(`ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN`)时,缺省自动判 `anthropic` 并打一行启动 warn(`model_provider_inferred`)——纯净机直连 Anthropic 兼容上游不再需要显式第七个键。显式 `MODEL_PROVIDER` 恒赢;只给 base URL 或只给凭证不推断;两者皆无 = `gateway`,与以前逐字节一致。
|
|
131
140
|
- 启动日志的 `brain` 字段会回显当前组合(`failover` / `anthropicRoute` / `anthropicCacheBreakpoints`)。
|
|
132
141
|
|
|
@@ -382,7 +391,7 @@ MODEL_ID=<your-model-name> MODEL_CHEAP_ID=<your-cheap-model-name>
|
|
|
382
391
|
存量中心配置里的 `roles.team` / `roles.synthesize` 由 settings-schema 的一次性迁移搬走,side query 带 `modelRole` 表外词(含 `advisor`)⇒ **400**
|
|
383
392
|
`request.field_invalid`(不再回落主模型;带了 `model` 也一样拒)。要给委派子代配模型就绑 `subagent`,主任务绑 `default`。
|
|
384
393
|
- 调用方可在 `objective` 里 `@<模型名>` 选模型,**只认已配置的名单**(`MODEL_ID` / `MODEL_CHEAP_ID`,或目录条目)——注入不了 `baseUrl`/`apiKey`;无 `@` → 默认模型;`@` 点中一条已发布但本部署不可用的目录条目 ⇒ 400 `request.model_unavailable`。
|
|
385
|
-
- **`GET /v1/models`**(带 Bearer)→ `{models:[{name,id,provider,reasoning,vision,…}],default,defaultModel,droppedEntries}`,只含名字/能力、**不含密钥** —— 消费方(如 OA)拿它做 `@` 自动补全下拉的数据源;`droppedEntries`(7.123.0 起恒在场,零条 = `[]`)是本代目录里被读腿逐条丢掉的条目(下标 + 闭集原因词 + 可披露身份),契约 §A.1d。 7.126.0 起另有**可选**顶层键 `candidateRejected: { domains, at, source }`:最近一次配置候选被候选判官整拒时在场(`domains` = 判形失败的配置域名,`source` = `local` 本机 `config.d` / `remote` 中心,`at` = 判下它的时刻)—— 在场就说明这份目录是回落的那一份(整份候选没收,候选里的模型一个都不在),真因域在 `domains` 里;后来的候选被判 clean 即缺席;dry-run 姿势(web-admin 腿 compare-only)恒缺席(拒判只走日志)。**缺席不等于配置一定干净**(也可能是更早的 server)。只含域名 / 时刻 / 源形态,拒因句与文件路径只在引擎日志 `config_candidate_rejected` 里,契约 §A.1d。
|
|
394
|
+
- **`GET /v1/models`**(带 Bearer)→ `{models:[{name,id,provider,reasoning,vision,…}],default,defaultModel,droppedEntries}`,只含名字/能力、**不含密钥** —— 消费方(如 OA)拿它做 `@` 自动补全下拉的数据源;`reasoning` / `vision` 两列是**三态**:声明了给布尔,**未声明 = 键缺席**(7.131.0 起;env 部署不设 `MODEL_REASONING` / `MODEL_VISION` 就是未声明,没有 `true` 缺省 —— 需要思考的模型显式写 `MODEL_REASONING=true`,纯文本模型写 `MODEL_VISION=false`;缺席时引擎对图收、对思考档不发),契约 §A.1d;`droppedEntries`(7.123.0 起恒在场,零条 = `[]`)是本代目录里被读腿逐条丢掉的条目(下标 + 闭集原因词 + 可披露身份),契约 §A.1d。 7.126.0 起另有**可选**顶层键 `candidateRejected: { domains, at, source }`:最近一次配置候选被候选判官整拒时在场(`domains` = 判形失败的配置域名,`source` = `local` 本机 `config.d` / `remote` 中心,`at` = 判下它的时刻)—— 在场就说明这份目录是回落的那一份(整份候选没收,候选里的模型一个都不在),真因域在 `domains` 里;后来的候选被判 clean 即缺席;dry-run 姿势(web-admin 腿 compare-only)恒缺席(拒判只走日志)。**缺席不等于配置一定干净**(也可能是更早的 server)。只含域名 / 时刻 / 源形态,拒因句与文件路径只在引擎日志 `config_candidate_rejected` 里,契约 §A.1d。
|
|
386
395
|
- 不设 `MODEL_CHEAP_ID` → 所有角色 = 主模型(行为不变)。
|
|
387
396
|
- `MODEL_CHEAP_ID` 写成与 `MODEL_ID` **同一个 id**(7.104.0 起)→ 那就是主模型本身:不铸 cheap 槽,所有角色 = 主模型,起服一行
|
|
388
397
|
`config_env_entry_cheap_same_as_main`(同时设的其余 `MODEL_CHEAP_*` 键一并不生效)。要给同一只上游 id 第二种形(另一个价 / 思考 /
|
|
@@ -484,9 +493,15 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
|
|
|
484
493
|
只调 `loadConfig()` 的装配对这几形照起。`ctx.configSource === "local"` 时它先读 `config.d/remote-exec.json` 覆写车道(`drivesFromFile`
|
|
485
494
|
= 你的装配驱动得了的文件车道),再判;不走文件覆写的装配直接调同一份判据 `assertExecutionLaneInvariants(config)`。
|
|
486
495
|
`ctx` 的型 `ExecutionLaneComposeCtx` 也从包根导出(自己的装配里要写它的签名 / 抽 helper 时直接 import,不必手抄一份形)。
|
|
496
|
+
读口场景门 `scenarioGate` 是 `createHttpServer` 的必填件(7.131.0+):用包根导出的 `createScenarioGate({ config, scenarios, principalCaps, logger }, "peek")` 铸(与任务提交腿同一个工厂,型 `ScenarioGate` / `ScenarioGateLeg` / `ScenarioGateSources` 一并导出),没递就是 tsc 红。
|
|
497
|
+
**模型路由端点的拒启同理判在目录应用之后**:`judgeServedModelRoute(config, { envTableIsPlaceholder })`(包根导出,HTTP 服务与 `run-local` 调的就是它)只判将要服务的那张表(目录表或 env 表,句子随视图);
|
|
498
|
+
只调 `loadConfig()` 的装配不再在读配置那一拍拿到路由拒启 —— 在**自己的**配置源应用之后调它一次,有判决就拒启(`reason` 是给运维的原句)。严格名册布防(env 表是占位)传 `envTableIsPlaceholder: true`,没有名册门的装配传 `false`。
|
|
487
499
|
- **库形嵌入的 caps `store` 位**(7.106.0 起;7.106.0 合并复审 X9 补一句):`createHttpServer` 的 `storePosture` dep **不传** ⇒ `GET /v1/capabilities` 的
|
|
488
500
|
`store` 取诚实下限 `{declared: <config.dbBackend>, backend: "memory", durable: false}`,且**没有 `store.sql`**(即使递了 `sqlEngineFacts`、真有 SQL 后端 ——
|
|
489
501
|
7.105.0 上这类嵌入方的顶层 `sql` 是有值的)。要 caps 说真话就把你装出来的后端事实经 `storePosture` 递进来(形 = `src/store-posture.ts` 的 `StorePosture`)。
|
|
502
|
+
- **`PORT=0` = 让 OS 分口,口由引擎自己报**:引擎自己绑内核分给它的口,`listening` 启动日志行(stdout,info 级;`LOG_LEVEL` 高于 info 会滤掉它)的 `port` 与 `/health.port` 报**实际绑定口**
|
|
503
|
+
(`PORT` 非 0 时两者与配置值相等)。要用 OS 分的口起引擎的宿主:子进程以 `PORT=0` 起,读 stdout 那一行 `listening` 的 `port`,再用 `/health` 的 `pid` / `dataRoot` 验答话的是自己起的那个进程;
|
|
504
|
+
**不要**先在外面挑一个空闲口、关掉、再交给子进程去绑 —— 关与子进程真正 `listen` 之间有窗,并行负载下别的进程能抢走这个号。`configHash` 仍按**配置值**铸(配置指纹不随 OS 分的口漂)。
|
|
490
505
|
- `/health` 身份字段承诺:`pid`(= 引擎进程)、`startedAt`(**7.67.0+**,= 引擎进程起点的 epoch ms,
|
|
491
506
|
一次铸)与 `dataRoot`(= 生效数据根,解析恒回退 `~/.ai-agent`,与 DB_BACKEND 无关)**恒在**——宿主用
|
|
492
507
|
它们验证「这个端口上的 /health 是不是我起的那个引擎」。`startedAt` 变了 = 这个端口后面换了一条命
|
|
@@ -815,6 +830,9 @@ QUESTION_TTL_MS=300000
|
|
|
815
830
|
- 三个值都有出厂缺省(即上面写的值),**不设=行为不变**;越界值**启动期响亮拒**(不静默夹取)。
|
|
816
831
|
- 窗用尽 / 到期 **不是替人作答**:服务只报「此刻无人可答」,落点由引擎选——**durable 部署**(`DURABLE_APPROVAL=true` 且这条腿没有活的流)把问题 **park** 成待办,运维随后经审批面带答案补答;**无 durable 的部署**则让模型被告知"没人在"后按自己的判断继续(run 永不挂死)。
|
|
817
832
|
- 想让人有更长时间答就调大 `QUESTION_TTL_MS`;想让 agent 少打扰人就调小 `QUESTION_MAX_TOTAL_PER_RUN`。
|
|
833
|
+
- **问题卡随 run 账本回放**:`question` / `question_complete` 在每条腿都落进 run 账本(`/v1/tasks/stream` 腿在有 durable run 行时先落账、再写 SSE;`/v1/runs` 腿本来就只有账本)。
|
|
834
|
+
页面重载 / 重连后,经 `GET /v1/runs/:id/events` 取回仍在等的卡(待答 = 没有同 `questionId` 的 `question_complete` 且 `expiresAtMs` 未到)。
|
|
835
|
+
**答题口不变**:`POST /v1/questions/:id/respond` 只在受理这一问的副本内、窗(`QUESTION_TTL_MS`)内有效;没有「列出待答问题」的读口。无 durable run 行的部署没有账本,仍只有 live 帧。
|
|
818
836
|
- 🔴 **`/v1/tasks/stream` 的开帧投递失败是响亮的(逐腿,不是部署级)**:这条腿(**且仅这条腿**)向引擎
|
|
819
837
|
声明 `interactionPosture: "interactive"` —— 人就在这条 SSE 上,一次开帧投递失败(壳断连/流被撕裂)
|
|
820
838
|
不再合成「没有人可问,你自己判」的续跑卡,而是让那次工具调用返回 coded 失败
|
|
@@ -1230,7 +1248,12 @@ TASK_WRITE_FACE_ESCALATION=allow # 词表 allow|refuse;坏词启动期响亮
|
|
|
1230
1248
|
起服日志一行 `web_admin_mcp_tool_axes_invalid { server, problem, note }`(`problem` 指到工具名与成员名,不带值);它的名字仍被部署占着,请求体里的同名条目补不了位。
|
|
1231
1249
|
一只工具坏,整条的声明都不收。缺席、`{}`、全空条目 = 没声明。
|
|
1232
1250
|
- **重启生效**(与 MCP 条目其余部分同:改了之后 `/health` 的 `restart.reasons` 含 `mcp`)。
|
|
1233
|
-
- **请求体自带的 `mcpServers[].toolAxes`**(单用户闸:调用方即信任根)
|
|
1251
|
+
- **请求体自带的 `mcpServers[].toolAxes`**(单用户闸:调用方即信任根)与部署声明读**同一只判官**(引擎根导出的 `readMcpToolAxes`;形同上一条):坏形 ⇒ 新提交 `400 request.field_invalid`,
|
|
1252
|
+
点名 `mcpServers[i].toolAxes`、server 名与引擎的原因句,**整份声明都不收**(不剔坏成员、好的那几只工具也不留);续跑重放存量请求时不拒:升级前存下的请求体在一次性迁移 M19
|
|
1253
|
+
的窗内(窗口见 `src/plugins/one-time-migrations.ts` 登记表)按升级前的读法重读一次(非对象含 `null` ⇒ 没声明;对象里过不了形的成员剔、好的留),产物仍过同一只判官 ⇒ server 照挂、
|
|
1254
|
+
一行运维 warn `mcp_tool_axes_read_as_before_upgrade`(`server` / `migration` / `problem` / `note`);重读的产物仍判不过、或窗已关 ⇒ 这一条 server 整条不进,
|
|
1255
|
+
运维 warn `mcp_injection_dropped`(`malformed_entry` / `field: toolAxes`)+ 用户面通告 `mcp.injection_dropped`(续跑家族同投);`POST /v1/capabilities/mcp/probe` 同拒同句,
|
|
1256
|
+
判官受理的声明与提交腿同读(只有空条目的工具 = 没声明,受理)。`a2aPeers[].toolAxes` 不变。
|
|
1234
1257
|
|
|
1235
1258
|
**可选 — MCP 入站表单(elicitation,E23):默认关**
|
|
1236
1259
|
|
|
@@ -1244,6 +1267,8 @@ MCP_ELICITATION_TTL_MS=300000 # 一张没人填的表单挂多久后释放(
|
|
|
1244
1267
|
- 四个节流钮都是 `numEnvBounded`:**越界启动期响亮拒,不静默夹取**(一个手滑的多小时 TTL 会让表单实际上永不过期)。
|
|
1245
1268
|
- 总开关关着时四个节流钮解析照跑但无消费者——不设=行为不变。帧格式见
|
|
1246
1269
|
[`docs/ASSISTANT-WIRE-CONTRACT.md` §4a-quater](docs/ASSISTANT-WIRE-CONTRACT.md)(`elicitation` / `elicitation_complete`)。
|
|
1270
|
+
- 两帧同样**随 run 账本回放**(与上面的问题卡同一条规则:`/v1/tasks/stream` 腿有 durable run 行时先落账再直写,`/v1/runs` 腿只有账本),重载后经 `GET /v1/runs/:id/events` 取回表单卡;
|
|
1271
|
+
`elicitation` 开帧带 `expiresInMs` / `expiresAtMs`,待答判法与问题卡同一把。**答题口不变**:`POST /v1/elicitations/:id/respond` 只在受理那一张表单的副本内、窗内有效。
|
|
1247
1272
|
|
|
1248
1273
|
**可选 — 把这台 worker 挂进别人的 A2A 拓扑(server-as-peer,DESIGN-269 车2):默认关**
|
|
1249
1274
|
|
|
@@ -1566,6 +1591,7 @@ bake-runner 要用 ⇒ `LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW` 点名放回。
|
|
|
1566
1591
|
| `sessionId` | — | 续聊:带上次返回的 `sessionId`,服务端自动 wake 历史。**只收 canonical uuid**(任意版本,小写;与会话口同一只形判):自选名 / 业务键 / 空串 ⇒ `400 request.id_invalid`,缺省请**省略这个键**(服务端现铸 uuid)。想要好记的名字,自己记 name → uuid 的映射 |
|
|
1567
1592
|
| `images` | — | 图文输入 `[{data,mimeType}|{url}]`(模型需支持 vision) |
|
|
1568
1593
|
| `attachmentIds` | — | D-1 通用文件上传(1.289+):先 `POST /v1/attachments?name=…`(raw body,content-type=mime)拿句柄,提交时引用 ≤16 个;文件物化到执行环境工作目录 `attachments/` 下,objective 尾部自动追加文件清单(内容不进会话流)。单文件缺省 ≤32 MiB(`ATTACHMENT_MAX_BYTES`);可配 mime 白名单(`ATTACHMENT_MIME_ALLOWLIST` CSV,缺省不限);上传后未引用的按 `ATTACHMENT_UNBOUND_TTL_MS`(缺省 24h)回收。**云形态(tidb/pg)字节本体存对象存储——MinIO 必配**(`MINIO_ENDPOINT/MINIO_ACCESS_KEY/MINIO_SECRET_KEY`,与快照 lane 同一组变量),未配则附件面 501;local 形走本地文件店。 |
|
|
1594
|
+
| `userAttachments` | — | 请求层附件(≤20 条),原样交给引擎、随**这条目标**送到模型:`{ path, lines? }` 读这次 run 的工作区 / `{ name, text, startLine? }` 宿主给的文本 / `{ name, image: { data, mimeType } }` 宿主给的图片(base64)。形判属主是引擎:坏形、带行号超 Read 上限(100 000 字符)的 text、过不了准入的图 ⇒ `400 config.user_attachment_invalid`(`error` 以字段路径起);`path` 只在有手场景(这次 run 有工作区)受理 —— 无手场景(`scan` / `code-review`、无手叠层、`provider: "in-process"` 的部署)上整条 `path` 条目同码 400,改发 text / image 条目;能力位 `supplies` 是席位能承载的供给,不按场景裁;读不到的文件不拒,它的消息里说明没附上,结局见流上目标那一帧 `human_input.attachments`。只随目标走:steer / interrupt 带它 400,续跑不重发。带附件的提交在第一次回复出来之前被切断(halt / interrupt)时,这次 run 不落回退锚 —— 拿它的 taskId 去 `resumeAt` ⇒ `404 resume_at.unknown_event`,请显式给 resumeAt。与 `attachmentIds` 分工:那条把文件放进工作区让模型按需去读(内容不进上下文),这条把内容直接送进上下文。能力位 `userAttachments` |
|
|
1569
1595
|
| `scenario` | — | 省略时取本部署的 `DEFAULT_SCENARIO`(工厂缺省场景名 = **`code`**——CC 编码 persona 蒸馏版,default 全量工具面,终验旋钮独立;`default` 场景**已非出厂缺省**,要用它须显式传 `scenario:"default"`,或部署方显式设 `DEFAULT_SCENARIO=default`)。可选内建名:`default`(通用/助理场景,域中性 persona,CC agent loop 全量工具面)/ `code-review`(见 §5)/ `scan`(同 §5 的 repo 只读工具但**中性无框架提示词**——objective+中心下发 skill 全权主导输出,OA 扫描类用;与 `code-review` 同属 clone-free 无执行环境场景,见 §5 末)/ **配置控制面可声明任意新场景**(`{name, toolset: none\|repo-readonly, prompt?}`,组合即配置、能力写死在部署;restart-to-apply;center 可覆盖内建名,boot 日志 `config_center_scenarios.shadowsBuiltin` 可审计)。**缺省与指派都必须是本部署在册的场景名,悬空不回落 `default`**:`DEFAULT_SCENARIO` 不在册 ⇒ **起服拒**(拒句点名那个名字与在册词表);中心按主体指派的场景名本部署没有 ⇒ 该主体的 run 答 **503 `state.scenario_assignment_unknown`**(体 `detail: { scenario, source: "ruling" }`,不带 `Retry-After`;修法 = 中心改指派,或把那个场景加回本部署,然后原样重试);请求体显式给的未知名仍是 400 `scenario_unknown` |
|
|
1570
1596
|
| `repo` | — | `code-review`/`scan` 必填,见 §5(7.126.0 起 `council` / `debate` / `lenses` / `rounds` / `team` / `businessContext` 是未知键 ⇒ 400:它们喂的评审团腿与 `discuss` 场景随引擎 7.49.0 删掉团队运行时一并删除) |
|
|
1571
1597
|
| `systemPrompt` / `appendSystemPrompt` | — | 调用方自带的系统提示 / 追加段(各 ≤16384 字符,超限 400 `request.field_invalid`;`appendSystemPrompt` 必须非空串)—— 任务自带时优先于角色 / 全局缺省(见 §模型角色) |
|
|
@@ -1712,6 +1738,7 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
|
|
|
1712
1738
|
| `POST /v1/approvals/<sessionId>/decide` `{"decision":"approve"|"deny","reason":"…"}` | 批/否并恢复挂起任务(CAS,重复决议 409);**仅 operator**(非 operator → 403)。5.0.0 起旧轮询腿 `POST /v1/approvals/<id>` 已退役 |
|
|
1713
1739
|
| `GET /v1/agents/roster` | 后台 agent **名册**(7.29 起,需 durable background-agent store,否则 501 `capability.background_agent_store_required`):调用方自己 scope 的 `a…` 行,新→旧。行只带 `handle`/`name`/`agentType`/`status`/`spawnedAt`/`updatedAt`/`settledAt`(content-free;子代产物正文走 `GET /v1/runs/<id>/subagents/<handle>/output`)。查询:`?limit=`(1..100,默认 20)、`?before=`(上一页的 `nextBefore` 游标)、`?status=`(running\|parked\|completed\|failed\|killed)、`?session=`(按会话树窄化)、`?scope=`(**仅** service-token/单用户形;带 principal 的调用方锁定自己)。回体 `{agents,total,nextBefore?,truncated?}`;`truncated:true` = 租户行数超过名册窗(500),`total` 此时是窗内数。非属主的行**不出现也不计数**(无存在性 oracle) |
|
|
1714
1740
|
| `GET /v1/capabilities/scenarios/<name>` | 单个场景的只读详情:工具面、提示词概览、**本部署现在跑不跑得动**(见下) |
|
|
1741
|
+
| `GET /v1/capabilities/mcp[?scenario=<场景名>]` | **无需先跑 run 的逐台 MCP 状态**(现连、列工具、关掉;按调用方限速 + 30 秒复用)。答的是**某个场景**会挂哪些中心声明的 MCP 服务器:不带参数 = 这位主体 run 时的场景(**7.131.0+**:中心按主体指派的场景胜出,否则部署缺省场景;此前恒读部署缺省场景;指派悬空 ⇒ `503 state.scenario_assignment_unknown`,与提交任务时同一句;`GET /v1/sessions/:id/mcp` 与 `…/a2a` 两张会话面板同读这位主体 run 时的场景:a2a 面板悬空时同样整面 503,而 mcp 面板另有「这个会话上一次真挂上了什么」那一半(`lastLegMcp`),悬空时答 `200`、`lastLegMcp` 照答、`servers` 缺席、拒因在 `scenarioRefusal: { errorCode, error, detail }`(与状态面整面 503 的体是同一份成员,`detail` 是门拒的机读事实 `{ scenario, source }`;`servers` 缺席 = 场景这一半没答,`servers: []` = 该场景没配 MCP));`?scenario=<名>`(**7.130.0+**)= 点名场景,可见集与同场景任务 run 时是同一只解析、过同一只场景门 —— 不在册 `400 scenario_unknown`(附在册词表;空串 `?scenario=` 同此,与提交 `scenario: ""` 同答)、这位 principal 的场景白名单集外 `400 scenario_not_allowed`(附 allowlist),两者的体与提交任务时的拒逐字同形;重复给出参数 `400 request.field_invalid`(提交腿没有这一形)。场景门接缝 `scenarioGate` 是 `createHttpServer` 的必填件:自建 `ServiceDeps` 的嵌入方用包根导出的 `createScenarioGate` 铸它(`"peek"` 腿给读面),没递就是 tsc 红。老 server 忽略这个参数、回缺省场景那一份,按 `GET /v1/capabilities` 的 `version` 判。`REQUIRE_PRINCIPAL` 下无身份 `401`(先于场景门)。自带 `.mcp.json` 的探测口是 `POST /v1/capabilities/mcp/probe` |
|
|
1715
1742
|
| `POST /v1/admin/drain` `{"actionId":"…","worker":"…","deadlineAt":…}` | **排空口**(7.121.0 起体 = settings-schema `DrainRequest`;operator-only —— principal 须在 `OPERATOR_PRINCIPALS` 内,空名单 ⇒ 501)。体 `{ actionId, worker, deadlineAt, scope?, expectedGeneration?, instanceId?, term? }`:`actionId` 幂等键(人手形自取,如 `manual:<uuid>`)、`worker` 本引擎的 worker 名(`SEMA_WEB_ADMIN_WORKER`)、`deadlineAt` 到期自停的 epoch ms、`scope` 本引擎的租户 scope(`FLEET_SCOPE`;不带 = 缺省 scope `global`,与没设 `FLEET_SCOPE` 的引擎相符 —— 多租引擎要带它自己的)、`expectedGeneration` fleet 成员的代际(`FLEET_GENERATION`;非成员不带)、`instanceId` 本进程身份(`GET /health` 读;不带则不比)。身份逐字段比(`worker` / `expectedGeneration` 缺席对缺席),不符 ⇒ `409 conflict.fleet_identity`(什么都不变);首次 ⇒ `200 {actionId, since, deadlineAt, state:"draining"}` 并**进入排空**(拒新提交、本副本可抢占的在飞腿当拍让出、活的审批卡改路耐久停驻、在飞归零或到期限即停);再来 ⇒ `200 … state:"already_draining"` 回显第一次的 `since` / `deadlineAt`,不重置。体坏 ⇒ `400 request.field_invalid`;`deadlineAt` 离本引擎此刻超过 `FLEET_LEASE_OUTAGE_MAX_MS`(缺省 24 h,一台实例活着而不服务的最长时长;7.122.0 起)⇒ 同码 400(什么都不变;拒句点名字段与上界的值;只在这一发会进入排空时判 —— 已在排空或硬停已在进行时任何重发都回 `already_draining`),已过的期限照收(进入即到期);嵌入宿主(无收尾链)⇒ `501 capability.drain_state_required`。没有 worker 身份的引擎(没配 `SEMA_WEB_ADMIN_WORKER`)恒 409 —— 用 SIGTERM。经本口进的排空之后,平台随后发的第一发 SIGTERM 被吸收、不缩短期限(第二发判据见「排空期的第二发信号」)。7.120.x 的 `{"reason":"…"}` 声明形与 `/health.drainReason` 已退役。 |
|
|
1716
1743
|
| `GET /v1/config/catalog` | **配置目录自描述**(operator-only —— `explicitOperatorOk`,空名单=谁都不是)。逐 env 键一行:`{name, domain, type, semantics, writeLanes, effectiveLane, envHeld, envTiming, staticDefault?/derivedDefaultNote?, effectiveValue, valueClass, danger, center?{domain,path,precedence,timing,restartSlice?,managedNow,effectiveNow}}`(🔴 7.106.0 起 `center.domainExists` 删除:座位在加载期对契约 `DOMAIN_SCHEMAS` 核过域 + 路径,核不过即拒启 ⇒ 有 `center` 块 ⇔ 中心可写;readFace 四键 / `PERMISSIONS_DISABLE_AUTO_MODE` / `MODEL_DEGRADE_ON` / `_REACTIVE` 七行的座位随之删除);顶层带 `restartSlices`(重启片闭集)与 `centerDomains`(settings-schema 现行域表)。**两轴不混**:`writeLanes`=谁能写,`effectiveLane`=现在谁在生效(env-wins 族 env 占位 ⇒ center 腿 `effectiveNow:false`)。secret 类值**永不回传**(只回在场位;opaque 回在场位+长度;URL 剥 userinfo)。完备性由仓内 AST 双向对账门执法(分母=全 `src/` 生产 env 读取点)。旋钮 `CONFIG_CATALOG_ENABLED=false`(合规部署)⇒ 路径 404 + 能力位 `configCatalog:false`。响应 `Cache-Control: no-store`。**7.57.0 起**:`enum` 行的 `effectiveValue` **恒落在该行自己的 `enumValues` 闭集内**(仓内机器门执法)——此前 `SESSION_BACKEND` 回的是**内部标签** `tidb`(自 5.0.0 起是**退役公名**,写回去 boot 拒),现归一成公名 `mysql`;`MODEL_DEFAULT_THINKING` 补上一直被解析器接受、却漏在目录闭集外的 `xhigh`,并改为回**实算值**(`off`/未设 ⇒ `null`)而不是 env 原文。 |
|
|
1717
1744
|
| `POST /v1/admin/config/refresh` | **配置手动刷新**(operator-only,与上一行同门:空 `OPERATOR_PRINCIPALS` ⇒ 恒 403)。改完中心配置 / `config.d` 之后不想等 60s 轮询就调它:触发**一次既有**的 refresh 拍(在飞则汇入那一拍,绝不并发双拍),返回时该拍已落地。200 体 `{triggered:true,targetVersion,appliedVersion}` —— 两个世代号与 `/health` 的 `configTargetVersion`/`configAppliedVersion` 同源。候选被判官整拒(坏域 / 坏 prompts / 带事实的退役域 —— 后者 7.126.0 起)是这一拍的结论,不是失败:照样 200,`targetVersion ≠ appliedVersion`,拒因在 `config_candidate_rejected` 日志行;拍真的失败(配置源不可达 / 读不了 / 应用抛错)⇒ `502 internal.config_refresh_failed`。本部署没有配置管道(纯 env worker:既没配 `CONFIG_CENTER`、也不是 `CONFIG_PROVIDER=local`)⇒ 409 `config.refresh_unavailable` 指路,不会回一个「触发了但什么都没发生」的 200。 |
|
|
@@ -2064,7 +2091,9 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
|
|
|
2064
2091
|
| HTTP `429` + `errorCode:"limit.cost_quota_exceeded"` | per-principal 累计成本配额越顶(`MAX_PRINCIPAL_COST_USD`;**进场门**,不打断在跑的 run) | 看 `Retry-After` / 体 `retryAfterSec` 退避(体 `windowResetsAt` = 当代窗滚过的时刻);窗滚过或调高上限后放行 |
|
|
2065
2092
|
| HTTP `400` + `errorCode:"request.id_invalid"` | 调用方给的 id 形不对。**会话 id 的形门只有一只** = 任意版本 canonical uuid(壳预设的 v4 也过门):会话口 `:id`(fork / 抹除 / 会话策略 / 2c 同步)与提交口(`POST /v1/tasks` / `/v1/tasks/stream` / `/v1/runs` 体里的 `sessionId`,A2A 的 `contextId` 同)同判,提交口的句子在句首之后点名「改用 uuid、自己记映射」;`GET /v1/sendfile-links?before=` 游标与 `body.taskId` 幂等键要求 uuidv7(时间序 / 幂等键契约) | 按 `errorCode` 分支;句子各自点名要求的形(`must be a canonical uuid` / `must be a uuidv7 …`),只作展示、别锚文案 |
|
|
2066
2093
|
| HTTP `400` + `errorCode:"request.field_invalid"`(`PATCH /v1/sessions/:id` / 2c 导入的 `title`) | 标题不合形:1–120 个 Unicode 字符、**首尾不得有空白**(服务端不代为 trim,先于 C0 判)、不得含 C0 控制字符;`title: null` 清回自动标题;空串是 `request.body_shape` | 客户端送出前自行 trim;按 `errorCode` 分支,句子只作展示 |
|
|
2067
|
-
| HTTP `400` + `errorCode:"config.tool_face_control"`(7.126.0 起;流式 /
|
|
2094
|
+
| HTTP `400` + `errorCode:"config.tool_face_control"`(7.126.0 起;流式 / 后台腿的引擎侧拒是 `failed` 终局带同码,受理口拒三条腿都是提交时 400) | 请求的 `allowTools` / `excludeTools` 里有一个**读成空**的条目:任何工具名都不可能有的拼法(空白、括号不配、空名)、`allowTools` 写了 `mcp__*`(引擎拒)、`excludeTools` 写了 `*`,或(7.131.0 起,受理口拒)**近似名**(大小写孪生 `BASH`;放行侧 `allowTools` 与限制侧 `excludeTools` 都拒 —— 想放的什么也没放 / 想禁的照用)与**限制侧的退役名**(`excludeTools: ["Task"]`)。放行侧 `allowTools` 的**退役名**(`Task` · `bash`)与**内容形**(`Agent(Explore)`)照 7.130.0 受理、交引擎既有通告(运维行 `runner_config_advisory`)。**真未知名**(拼写合法、这台部署没有工具叫它,懒挂载的合法形)7.129.0 起照跑:条目原样留在表里,运维行告知一次。选择器只取工具名那一段判名字(限制侧 `Bash(echo $HOME)` 不拒),`allowTools:["*"]` 与限制侧 `mcp__*` / `a2a__*`(整个协议)合法。清单超 1000 条 ⇒ 整只 `400 request.field_invalid`(`count` / `cap`),帽内拒句只点名前 5 条 + `; and K more`。拒时:主模型零调用、标题器零调用(7.127.0 起;7.126.x 上标题器仍会为这次提交调一次模型)。这次提交留下的会话无标题、零消息 | 改成句子里 `write` 后的现名(或删掉条目);只列这台部署挂得出的工具名 |
|
|
2095
|
+
| HTTP `400` + `errorCode:"config.agent.unknown_tool"`(7.131.0 起) | 请求 `agents[i].denyTools`(限制侧)里有读成空的条目:近似名(`BASH`)、退役名(`bash`)、任何工具名都不可能有的拼法、`*`;`agents[i].allowTools`(放行侧)里的**近似名**。开跑前拒、主模型零调用、一个 token 没烧,句子点名条目、原因词与现名。限制侧 `Tool(content)` 的内容不读(读成整只工具)、限制侧整个协议的拒 `mcp__*` / `a2a__*`、真未知名(`NotMounted`,照跑、告运维一次)不拒;放行侧的退役名(`Task`)与内容形(`Agent(Explore)`)照 7.130.0 受理、交引擎既有通告;文法读不出的放行条目授不出去 = 收窄而不是放行:规则表(`permissions.allow` / 技能 `allowedTools`)上受理口不拒、引擎开跑时告运维;选择器清单(根 / 子代 `allowTools`)上受理口不拒、引擎开跑前自己拒(子代 ⇒ 终局 `failed` 带 `config.agent.unknown_tool`,同步腿 HTTP 200 + failed 体;根 ⇒ `400 config.tool_face_control`) | 改成句子里 `write` 后的现名,或删掉条目 |
|
|
2096
|
+
| HTTP `422`(`request settings cannot be applied as written: …`)/ HTTP `400` + `errorCode:"request.field_invalid"`(7.131.0 起的读成空) | 请求体 `settings.permissions.deny` / `ask` 里的孪生与退役名 ⇒ 422、句子点名带内容的现名(`BASH(*)` ⇒ 写 `Bash(*)`);`settings.permissions.allow` 与技能 `allowedTools` 里的**近似名**(`BASH`)⇒ 400(退役名 · 内容形 · 文法读不出(带空白 / 括号不配 / 规则内容文法不成立,如 `Bash(echo $HOME)`)· 对端段通配照 7.130.0 受理,引擎通告)—— 全部读**同一张名册**(引擎目录 ∪ 本服务自铸的工具名)。运维 `APPROVAL_REQUIRE` / `APPROVAL_DENY` / `APPROVAL_NEVER_AUTO` / `APPROVAL_GRANT` 里的孪生 **拒启**,中心下发的 `governance.approvalRequire` 里的孪生整组被拒、保前值。续跑:停驻在升级前的任务,超 1000 条的清单(任一方向)与限制方向的存量孪生 `409 resume_blocked_by_policy`,放行方向的存量近似名照旧续跑 + 一行 warn `stored_allow_list_inert_resumed` | 改成句子里 `write` 后的现名(或删掉条目);超帽的合并成更宽的条目 |
|
|
2068
2097
|
| HTTP `429` + `errorCode:"usage.window_exhausted"` | 部署级治理窗耗尽(`USAGE_WINDOWS`,token 或 $ 天花板先满者) | 看响应体 `retryAfterSec` 退避;窗滑动/桶到期后放行 |
|
|
2069
2098
|
| `terminal:{kind:"failed", code:"usage.window_exhausted"}` | 已受理的 run 在 **turn 边界**撞上治理窗且无法 durable 挂起 | 同上退避后重投;配好 checkpoint 基建则改为 `suspended` 等窗自动续跑 |
|
|
2070
2099
|
| HTTP `501`(`/v1/runs`) | 内存模式不支持异步 | 配持久存储(`DB_BACKEND=mysql` / `pg` + 坐标;7.106.0 起给了坐标必须点名 `DB_BACKEND`) |
|
|
@@ -2087,9 +2116,9 @@ tag 词表与每条的最坏后果见 `docs/FAIL-OPEN-CENSUS.md`。引擎装配
|
|
|
2087
2116
|
按 `code` / `classification` / `level` 打标;其中 `shell-gate-off` 在本 session 显式声明 `permissionMode:
|
|
2088
2117
|
"bypassPermissions"` 时是调用方要的姿态,降为 **info** 并标 `expected`——只有没表态落到引擎默认 off 的无壳直连
|
|
2089
2118
|
形才 warn。子代 `agents[].denyTools` / `allowTools` 的**名字**归引擎开跑前一只判(7.126.0 起与根 run 的 `allowTools` / `excludeTools` 是同一只工具选择器):
|
|
2090
|
-
7.129.0 起(引擎 7.51.0)
|
|
2091
|
-
|
|
2092
|
-
|
|
2119
|
+
7.129.0 起(引擎 7.51.0)没有任何工具答应的名字**引擎不再拒跑**:条目原样保留(今天什么都不拒 / 不准,之后挂上同名工具即生效),运维行 `runner_config_advisory` 每条告知一次(句子带该写的拼法;委派工具声明了逐次注入工具的名册,引擎列得出子代工具池);
|
|
2120
|
+
引擎仍开跑前拒(`config.agent.unknown_tool`)的只剩任何工具名都不可能有的拼法(空白、括号不配、空名)与非字符串条目。**7.131.0 起本服务把其中写错现名的在受理口前置拒:限制侧四类全拒、放行侧只拒近似名** —— `denyTools:["BASH"]` / `["bash"]` 以为禁了 `Bash`、子代照用(静默放行),`allowTools:["BASH"]` 想放 `Bash` 却什么都没放(静默收窄);
|
|
2121
|
+
提交 400 `config.agent.unknown_tool`(根 run 的清单是 `config.tool_face_control`),句子点名条目、原因词与现名;放行侧的**退役名**(`Task`)与**内容形**(`Agent(Explore)`)仍照 7.130.0 受理、交引擎既有通告;文法读不出的放行条目授不出去 = 收窄而不是放行:规则表(`permissions.allow` / 技能 `allowedTools`)上受理口不拒、引擎开跑时告运维;选择器清单(根 / 子代 `allowTools`)上受理口不拒、引擎开跑前自己拒(子代 ⇒ 终局 `failed` 带 `config.agent.unknown_tool`,同步腿 HTTP 200 + failed 体;根 ⇒ `400 config.tool_face_control`);存量体续跑见 MIGRATION 7.131.0 第 8 条。判官仍是引擎的条目读法。**真未知名**(没有近似也没有退役对应:懒挂载的合法形)照旧只告运维、不拒。名字按工具名原拼写 `Bash`。这两张表管子代可挂载的**全部**工具 —— 委派池、内建 hands(`Read` / `Edit` / `Write` / `NotebookEdit` / `Bash` / `Grep` /
|
|
2093
2122
|
`Glob` …)、引擎自己的工具(`Monitor` / `Artifact` / `Skill` / 记忆工具 …)与父的 MCP 挂载:`denyTools:["Write"]` ⇒ 子代没有 `Write`,`denyTools:["Monitor"]` ⇒ 子代没有
|
|
2094
2123
|
`Monitor`;`allowTools` 只列调用方工具 ⇒ 子代只剩那几只与它这条腿的协议工具(要什么就点名什么,`mcp__<srv>` 点整台服务器)。内建只读子代(Explore / Plan)没有
|
|
2095
2124
|
`Edit` / `Write` / `NotebookEdit`。要按路径 / 命令拦而保留工具,写调用方权限策略的 deny 规则。
|
|
@@ -2339,6 +2368,8 @@ e2b SDK 自己的建命令握手另有一界(缺省 60 s),比命令的界早时
|
|
|
2339
2368
|
(e2b 点名 `E2B_LIVENESS_MS`);错误码沿用引擎的 `timeout`(引擎码表把墙钟与活性两种超界归同一词,靠句子区分)。Bash 工具结果里的 `timedOut: true` / `timeoutSec: N`
|
|
2340
2369
|
不再是活性切断的投影,但仍不全是「跑满了 N 秒」:e2b 上建命令卡住被那只 60 s 握手切掉、或沙箱中途死掉时,这一发仍以 `timeout` 码回来,Bash 照旧报
|
|
2341
2370
|
「Command timed out after <N>s」(把它们与超时分开的独立码属于引擎的码表)。host 车道没有活性界(命令只受调用方的超时,缓冲 exec 不带超时时受 `HOST_COMMAND_TIMEOUT_MS`)。
|
|
2371
|
+
ssh / adb / local-docker 三条车道的流式调用同读这一条律:没有车道活性界,只受自己说的静默界 `readTimeoutMs` 约束,到点的切断句与 host 同一句
|
|
2372
|
+
(`no output for <N> s — cut off by the command's own idle bound (readTimeoutMs)`);`readTimeoutMs` 为 `0` / 负数 / 非数不算说了界。
|
|
2342
2373
|
|
|
2343
2374
|
### 9.4 auto 模式(`permissionMode:"auto"`):武装三态、本地 org deny、自查(与 core 的意图武装式合修)
|
|
2344
2375
|
|
|
@@ -2521,7 +2552,7 @@ WebSearch 工具的 `tool_result` 错误文本,回到模型的对话里,**不是
|
|
|
2521
2552
|
`http` 条目 header **值**里的 `$NAME` / `${NAME}` 按条目的 `allowedEnvVars` 展开,读法是契约包 settings-schema 的 `interpolateHookEnvVars`(壳与本服务同调,同一份钩子定义到端点的字节相同):
|
|
2522
2553
|
- 名字只认大写 `[A-Z_][A-Z0-9_]*`;小写名(`$my_token`)是普通文字,原样发出。
|
|
2523
2554
|
- 列在 `allowedEnvVars` 里 ⇒ 展开成钩子环境里的值(钩子环境 = 净化后的进程环境 allowlist + 同请求 `settings.env`;服务进程的密钥不在其中);钩子环境里没有这个名字 ⇒ 空串。
|
|
2524
|
-
- 名单外的引用 ⇒ 空串;凭据名表里的名字(`
|
|
2555
|
+
- 名单外的引用 ⇒ 空串;凭据名表里的名字(契约包 `HOOK_ENV_CREDENTIAL_NAMES`:`CLAUDE_CODE_OAUTH_TOKEN` 一类 27 个名字 × 裸名与 `INPUT_` 前缀;**不含** `ANTHROPIC_API_KEY` / `MODEL_API_KEY` 这类模型凭据名)一律空串,点进名单也一样。本服务自己的模型凭据不靠这张表护:它们从不进钩子环境(钩子环境 = 净化后的进程环境 allowlist + 同请求 `settings.env`,服务进程的密钥名在扣留名单里),所以 `${ANTHROPIC_API_KEY}` 在本服务上展开成空串是因为钩子环境里没有它,不是因为它在凭据名表里;调用方自己在 `settings.env` 里给的同名变量会照常展开。
|
|
2525
2556
|
- 每个 header 值剥 CR / LF / NUL;header 名不展开。
|
|
2526
2557
|
- 置空(名单外 / 凭据名)时日志一行 `hook_http_header_env_blanked`(名字与原因,不带值)。
|
|
2527
2558
|
例:`"headers": { "Authorization": "Bearer ${HOOK_TOKEN}" }`、`"allowedEnvVars": ["HOOK_TOKEN"]`,并在请求 `settings.env` 里给 `HOOK_TOKEN`。7.125.x 及更早的读法不同(升级见 MIGRATION)。
|
|
@@ -2533,6 +2564,14 @@ WebSearch 工具的 `tool_result` 错误文本,回到模型的对话里,**不是
|
|
|
2533
2564
|
- `assertionHeader` 的**值**是头名,本服务不铸、也不校验具体词(只判是字符串且与 `principalHeader` 互斥);头名由引擎(上游)定为 **`connector-assertion`**(无 `x-` 前缀;引擎发送座落地后本服务只认这一个值),条目填别的名 ⇒ 连接器侧 401。
|
|
2534
2565
|
- 本版只承载、不发送断言:声明了 `assertionHeader` 的 server 照常挂载,但不带断言头;每台这样的 server 一行 info `mcp_assertion_header_not_sent`(`server` / `note`)。探测口 `POST /v1/capabilities/mcp/probe` 对声明了该键的条目整条拒 `400 request.field_invalid`(探测不拨一份少了断言头的配置)。
|
|
2535
2566
|
|
|
2567
|
+
### 9.5e 请求体 `mcpServers[i]` 的条目键:任一受理键写坏,整份新提交 400(7.131.0 起)
|
|
2568
|
+
|
|
2569
|
+
条目的受理键 = `name` · `transport` · `allowTools` · `toolAxes` · `toolFaces` · `contentOrigin` · `elicitation` · `source`(`transport` 的子键:`kind` · `command` · `args` · `env`(stdio),`kind` · `url` · `headers` · `principalHeader` · `assertionHeader`(http))。**新提交上每个键只有一只判官,写坏的答案只有一种**:
|
|
2570
|
+
- **新提交**(`POST /v1/tasks` · `/v1/tasks/stream` · `/v1/runs`):键在场却判不出形 ⇒ `400 request.field_invalid`,拒句形如 `mcpServers[2].allowTools (server "crm") is not a usable declaration — must be an array of strings (omit the key to leave the server's tools unrestricted) — got a string of 4 characters. …`:点名 `mcpServers[i].<键路径>`、server 名与该键判官的原因句,**整份请求不收** —— 不挂一半、不丢声明、不默认。原因句不回显声明的值,有几处例外(都剥控制与双向字符):server 名(含写坏的 `name` 值;截 64 字)、`contentOrigin` 词表外的值(截 64 字)、`toolAxes` 点名的工具名(由引擎截长)、`toolFaces` 点名的工具名(不截长,以请求体自带的长度为限);`transport.url` 的值从不回显:它常带凭据。可选键**缺席**永远合法(真「没声明」),必填的 `name` / `transport` 缺席同样 400;在场的 `null` 不是缺席。与单用户 / 多租户无关。只判前 32 条(能被挂载的那一段):第 33 条起的超上限尾项照旧只记 `over_cap` 点名,不参与逐键判形;条目本身不是对象、词表外的键也不在这道门的射程(既有处置不变)。
|
|
2571
|
+
- **耐久 resume 重放存量请求**:不拒(拒会把升级前 park 的任务永久卡死)。写坏的 `name` · `transport` 的 `kind`/`command`/`url` · `allowTools` · `toolAxes` · `toolFaces` ⇒ 这一条 server 整条不进,运维 warn `mcp_injection_dropped`(`reason: malformed_entry`,`field` 点名)并投用户面通告 `mcp.injection_dropped`(其中 `toolAxes` 在一次性迁移 M19 的窗开着时先按升级前读法重读一次,过了就挂,见上文 `toolAxes` 一节);写坏的 `contentOrigin` · `elicitation` · `source` · `transport` 的 `args`/`env`/`headers`/`principalHeader` ⇒ 丢掉那一个键、server 照挂。
|
|
2572
|
+
- `POST /v1/capabilities/mcp/probe` 对同一类写坏同样整条拒(400,拒之前零拨号);它问的是「这台到底能不能用」,射程比提交腿宽,另有三形:超过 32 条 ⇒ 整只 `400 request.field_invalid`(不截断成部分答案);条目不是对象 ⇒ `400 request.body_shape`;词表外的键(条目层或 `transport` 层)⇒ `400 request.body_shape`,拒体带 `unknownKeys`(闭集;提交腿对这类键宽容)。
|
|
2573
|
+
- 7.130.x 及更早:`allowTools` / `toolFaces` / `name` / `transport` 写坏 ⇒ 200 + 这条 server 不挂;`elicitation` / `source` / `transport` 的可选子键写坏 ⇒ 200 + 悄悄丢键。升级后这些请求变 400:改对那个键,或删掉它。
|
|
2574
|
+
|
|
2536
2575
|
### 9.6 trace 脱敏的射程:与出站请求体无关
|
|
2537
2576
|
|
|
2538
2577
|
🔴 **脱敏 ≠ 出站控制**:trace 脱敏(账本 / SSE / turns / 审批预览里的 `«redacted»` 标记)只作用于
|
package/dist/audit.js
CHANGED
|
@@ -5,7 +5,7 @@ import { createLocalSessionRepo } from "./plugins/local-session-store.js";
|
|
|
5
5
|
import { redactModelText } from "./observability/run-terminal.js";
|
|
6
6
|
import { redactDeepWith } from "./trace/redact.js";
|
|
7
7
|
import { projectToolStructured } from "./trace/tool-output-media.js";
|
|
8
|
-
import { INJECTED_BLOCK_RE } from "./user-authored-text.js";
|
|
8
|
+
import { INJECTED_BLOCK_RE, isAttachmentFrame } from "./user-authored-text.js";
|
|
9
9
|
export function buildRewindHandles(anchors, reader) {
|
|
10
10
|
const byEntry = new Map();
|
|
11
11
|
for (const a of anchors) {
|
|
@@ -153,9 +153,12 @@ const MESSAGE_READ_TREATMENT = {
|
|
|
153
153
|
unsummarizedMessages: "verbatim", tokensAfter: "verbatim", firstKeptEntryId: "verbatim",
|
|
154
154
|
},
|
|
155
155
|
};
|
|
156
|
+
const ATTACHMENT_FRAME_READ_TREATMENT = { ...MESSAGE_READ_TREATMENT.user, content: "parts" };
|
|
156
157
|
function readMessage(m) {
|
|
157
158
|
if (isPlainRecord(m)) {
|
|
158
159
|
const role = m.role;
|
|
160
|
+
if (role === "user" && isAttachmentFrame(m))
|
|
161
|
+
return readByTable(m, ATTACHMENT_FRAME_READ_TREATMENT);
|
|
159
162
|
if (typeof role === "string" && Object.hasOwn(MESSAGE_READ_TREATMENT, role)) {
|
|
160
163
|
return readByTable(m, MESSAGE_READ_TREATMENT[role]);
|
|
161
164
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { outwardPlane } from "../../terminal.js";
|
|
2
2
|
import { Runner, runWithVerification, resumeWithVerification, runRepairLoop, combinePolicies, createDurableQuestionPolicy, QUESTION_AWAITS_RESUME, } from "@sema-agent/core";
|
|
3
3
|
import { loadConfig } from "../../config.js";
|
|
4
|
+
import { assertServedModelRoute } from "../../model-route-endpoint.js";
|
|
4
5
|
import { withUnattendedLeg } from "../../unattended-permission-mode.js";
|
|
5
6
|
import { createBrain } from "../../brain.js";
|
|
6
7
|
import { buildPricing } from "../../budget.js";
|
|
@@ -51,6 +52,7 @@ function buildBrainIngredients(rt) {
|
|
|
51
52
|
if (rt.costCacheWrite != null)
|
|
52
53
|
process.env.MODEL_COST_CACHE_WRITE = String(rt.costCacheWrite);
|
|
53
54
|
const config = loadConfig();
|
|
55
|
+
assertServedModelRoute(config, { envTableIsPlaceholder: false });
|
|
54
56
|
const brain = createBrain(config, {});
|
|
55
57
|
const pricing = buildPricing(config.models);
|
|
56
58
|
const p = pricing[rt.modelId];
|