@sema-agent/server 7.130.0 → 7.132.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 (294) hide show
  1. package/MIGRATION.md +396 -0
  2. package/README.md +13 -6
  3. package/README.zh-CN.md +9 -5
  4. package/USAGE.md +187 -50
  5. package/dist/approval-card.d.ts +3 -1
  6. package/dist/approval-card.js +2 -3
  7. package/dist/audit.d.ts +7 -10
  8. package/dist/audit.js +4 -1
  9. package/dist/auth-bridge.d.ts +4 -0
  10. package/dist/auth-bridge.js +10 -11
  11. package/dist/bench/s1/live-deps.js +2 -0
  12. package/dist/boot/deployment-mods.d.ts +31 -0
  13. package/dist/boot/deployment-mods.js +26 -0
  14. package/dist/boot/execution-env.d.ts +1 -1
  15. package/dist/boot/execution-env.js +19 -6
  16. package/dist/boot/memory-boundary.d.ts +2 -2
  17. package/dist/boot/read-deny-anchor-audit.d.ts +17 -0
  18. package/dist/boot/read-deny-anchor-audit.js +36 -0
  19. package/dist/boot/resolve-spec.d.ts +1 -5
  20. package/dist/boot/resolve-spec.js +56 -61
  21. package/dist/boot/runner-deps.d.ts +19 -8
  22. package/dist/boot/runner-deps.js +14 -2
  23. package/dist/boot/session-faces.d.ts +1 -7
  24. package/dist/boot/stage-02-web-admin-pull.js +9 -1
  25. package/dist/boot/stage-03-store-backend.d.ts +1 -1
  26. package/dist/boot/stage-03-store-backend.js +2 -2
  27. package/dist/boot/stage-04-stores.d.ts +1 -1
  28. package/dist/boot/stage-05-execution-env.d.ts +4 -4
  29. package/dist/boot/stage-05-execution-env.js +7 -10
  30. package/dist/boot/stage-06-runners.d.ts +2 -2
  31. package/dist/boot/stage-06-runners.js +5 -4
  32. package/dist/boot/stage-07-capability-layer.d.ts +2 -2
  33. package/dist/boot/stage-07-capability-layer.js +3 -3
  34. package/dist/boot/stage-08-reapers.d.ts +2 -2
  35. package/dist/boot/stage-09-leader.d.ts +3 -9
  36. package/dist/boot/stage-10-http-server.d.ts +3 -9
  37. package/dist/boot/stage-10-http-server.js +12 -10
  38. package/dist/boot/stores.d.ts +1 -1
  39. package/dist/boot/stores.js +5 -6
  40. package/dist/boot/web-admin.d.ts +0 -3
  41. package/dist/boot/web-admin.js +35 -35
  42. package/dist/brain.js +17 -22
  43. package/dist/budget.js +5 -4
  44. package/dist/caller-text-echo.d.ts +20 -0
  45. package/dist/caller-text-echo.js +24 -0
  46. package/dist/capabilities/brand-identity.d.ts +26 -0
  47. package/dist/capabilities/brand-identity.js +27 -0
  48. package/dist/capabilities/builtin-tools.js +2 -1
  49. package/dist/capabilities/center-plugins.js +1 -1
  50. package/dist/capabilities/center-prompts.d.ts +32 -59
  51. package/dist/capabilities/center-prompts.js +18 -32
  52. package/dist/capabilities/composite-members.d.ts +1 -0
  53. package/dist/capabilities/composite-members.js +1 -0
  54. package/dist/capabilities/engine-capabilities.js +2 -1
  55. package/dist/capabilities/hands-lane.d.ts +9 -6
  56. package/dist/capabilities/hands-lane.js +3 -3
  57. package/dist/capabilities/host-tool-names.d.ts +17 -0
  58. package/dist/capabilities/host-tool-names.js +15 -0
  59. package/dist/capabilities/mcp-probe.d.ts +14 -1
  60. package/dist/capabilities/mcp-probe.js +22 -133
  61. package/dist/capabilities/repo-tools.js +4 -3
  62. package/dist/capabilities/scenarios.d.ts +42 -47
  63. package/dist/capabilities/scenarios.js +19 -35
  64. package/dist/capabilities/select-environment-tool.js +2 -1
  65. package/dist/capabilities/send-user-file-tool.d.ts +0 -1
  66. package/dist/capabilities/send-user-file-tool.js +2 -2
  67. package/dist/capabilities/skills.d.ts +19 -4
  68. package/dist/capabilities/skills.js +30 -10
  69. package/dist/capabilities/tool-defer.js +2 -1
  70. package/dist/config-catalog.js +22 -11
  71. package/dist/config-invariants.js +2 -2
  72. package/dist/config-lkg.d.ts +9 -9
  73. package/dist/config-lkg.js +20 -4
  74. package/dist/config-provider.d.ts +3 -1
  75. package/dist/config-provider.js +14 -6
  76. package/dist/config-types.d.ts +13 -9
  77. package/dist/config.d.ts +2 -2
  78. package/dist/config.js +34 -20
  79. package/dist/device-ws-protocol.d.ts +1 -1
  80. package/dist/device-ws-protocol.js +1 -0
  81. package/dist/elicitation.d.ts +7 -0
  82. package/dist/elicitation.js +20 -5
  83. package/dist/env-name-allowlist-knobs.d.ts +12 -2
  84. package/dist/env-name-allowlist-knobs.js +5 -4
  85. package/dist/exec-bounds.d.ts +15 -3
  86. package/dist/exec-bounds.js +4 -0
  87. package/dist/execution-lane-caps.d.ts +9 -2
  88. package/dist/execution-lane-caps.js +11 -0
  89. package/dist/hooks/classic-stations.d.ts +86 -0
  90. package/dist/hooks/classic-stations.js +27 -0
  91. package/dist/hooks/hook-llm.d.ts +8 -5
  92. package/dist/hooks/hook-llm.js +80 -37
  93. package/dist/hooks/hook-runner.d.ts +273 -75
  94. package/dist/hooks/hook-runner.js +293 -317
  95. package/dist/http/admission.d.ts +8 -2
  96. package/dist/http/admission.js +103 -59
  97. package/dist/http/approval-sweeps.js +59 -18
  98. package/dist/http/checkpoint-ctx.d.ts +36 -18
  99. package/dist/http/checkpoint-ctx.js +14 -10
  100. package/dist/http/claimed-run.d.ts +13 -0
  101. package/dist/http/claimed-run.js +15 -0
  102. package/dist/http/device-placement.js +5 -10
  103. package/dist/http/dispatch.d.ts +0 -4
  104. package/dist/http/dispatch.js +6 -10
  105. package/dist/http/durable-run-leg.d.ts +2 -1
  106. package/dist/http/durable-run-leg.js +5 -3
  107. package/dist/http/health-self-report.d.ts +7 -5
  108. package/dist/http/health-self-report.js +4 -18
  109. package/dist/http/principal-gate.d.ts +23 -6
  110. package/dist/http/principal-gate.js +24 -5
  111. package/dist/http/refusal-body.d.ts +21 -0
  112. package/dist/http/refusal-body.js +4 -0
  113. package/dist/http/request-guards.d.ts +27 -0
  114. package/dist/http/request-guards.js +24 -3
  115. package/dist/http/resume-legs.d.ts +25 -0
  116. package/dist/http/resume-legs.js +48 -26
  117. package/dist/http/routes/approvals-assistant.js +1 -1
  118. package/dist/http/routes/capabilities.js +7 -17
  119. package/dist/http/routes/diagnostics.js +1 -1
  120. package/dist/http/routes/rules.js +2 -2
  121. package/dist/http/routes/runs.js +13 -17
  122. package/dist/http/routes/session-sync.js +4 -2
  123. package/dist/http/routes/sessions.js +23 -11
  124. package/dist/http/routes/shared-memory.js +2 -13
  125. package/dist/http/routes/side-query.js +2 -3
  126. package/dist/http/routes/tasks.js +34 -19
  127. package/dist/http/scenario-read-face.d.ts +14 -0
  128. package/dist/http/scenario-read-face.js +5 -0
  129. package/dist/http/send.d.ts +13 -1
  130. package/dist/http/send.js +12 -1
  131. package/dist/http/server.d.ts +37 -23
  132. package/dist/http/server.js +30 -4
  133. package/dist/http/stored-body-replay.d.ts +10 -3
  134. package/dist/http/stored-body-replay.js +27 -6
  135. package/dist/http/submit-ctx.d.ts +25 -0
  136. package/dist/http/submit-ctx.js +39 -0
  137. package/dist/http/wire-types.d.ts +18 -5
  138. package/dist/index.d.ts +3 -2
  139. package/dist/index.js +3 -2
  140. package/dist/lane-home-probe.d.ts +43 -0
  141. package/dist/lane-home-probe.js +35 -0
  142. package/dist/limit-refusal.d.ts +47 -0
  143. package/dist/limit-refusal.js +12 -0
  144. package/dist/main.js +1 -1
  145. package/dist/mcp-assertion-header.d.ts +3 -11
  146. package/dist/mcp-assertion-header.js +3 -9
  147. package/dist/mcp-tool-faces.d.ts +5 -0
  148. package/dist/mcp-tool-faces.js +5 -1
  149. package/dist/memory-scope.d.ts +1 -1
  150. package/dist/model-entry-refusal.d.ts +1 -7
  151. package/dist/model-provider.d.ts +39 -12
  152. package/dist/model-provider.js +19 -2
  153. package/dist/model-route-endpoint.d.ts +52 -12
  154. package/dist/model-route-endpoint.js +48 -26
  155. package/dist/model-route-gap.d.ts +5 -4
  156. package/dist/model-route-gap.js +1 -1
  157. package/dist/model-select.d.ts +58 -0
  158. package/dist/model-select.js +31 -0
  159. package/dist/namespaced-rule-name.d.ts +7 -4
  160. package/dist/namespaced-rule-name.js +4 -3
  161. package/dist/observability/cost-taxonomy.d.ts +7 -7
  162. package/dist/observability/fail-open.d.ts +53 -12
  163. package/dist/observability/fail-open.js +47 -7
  164. package/dist/observability/metrics.js +3 -1
  165. package/dist/observability/prompt-manifest.d.ts +47 -82
  166. package/dist/observability/prompt-manifest.js +119 -17
  167. package/dist/observability/run-terminal.js +14 -6
  168. package/dist/observability/secret-env-scrub.d.ts +13 -1
  169. package/dist/observability/secret-env-scrub.js +13 -1
  170. package/dist/observability/tool-trace.d.ts +16 -21
  171. package/dist/observability/tool-trace.js +51 -42
  172. package/dist/per-task-image.d.ts +7 -1
  173. package/dist/per-task-image.js +3 -3
  174. package/dist/plain-string-record.d.ts +25 -0
  175. package/dist/plain-string-record.js +32 -0
  176. package/dist/plugins/blob-backend.d.ts +68 -13
  177. package/dist/plugins/blob-backend.js +53 -10
  178. package/dist/plugins/checkpoint-store-sql.d.ts +14 -4
  179. package/dist/plugins/checkpoint-store-sql.js +39 -26
  180. package/dist/plugins/k8s-bg-scripts.d.ts +11 -0
  181. package/dist/plugins/k8s-bg-scripts.js +22 -0
  182. package/dist/plugins/local-checkpoint-store.js +5 -2
  183. package/dist/plugins/local-session-store.js +3 -3
  184. package/dist/plugins/one-time-migrations.d.ts +147 -91
  185. package/dist/plugins/one-time-migrations.js +372 -208
  186. package/dist/plugins/permission-rule-store-sql.d.ts +13 -7
  187. package/dist/plugins/permission-rule-store-sql.js +4 -18
  188. package/dist/plugins/pg-session-storage.d.ts +4 -5
  189. package/dist/plugins/pg-session-storage.js +7 -17
  190. package/dist/plugins/remote-env-adb.d.ts +6 -0
  191. package/dist/plugins/remote-env-adb.js +9 -4
  192. package/dist/plugins/remote-env-device.js +3 -0
  193. package/dist/plugins/remote-env-e2b.d.ts +1 -1
  194. package/dist/plugins/remote-env-e2b.js +21 -9
  195. package/dist/plugins/remote-env-host.d.ts +9 -9
  196. package/dist/plugins/remote-env-host.js +10 -32
  197. package/dist/plugins/remote-env-k8s.d.ts +95 -26
  198. package/dist/plugins/remote-env-k8s.js +265 -90
  199. package/dist/plugins/remote-env-local-docker.d.ts +20 -1
  200. package/dist/plugins/remote-env-local-docker.js +77 -9
  201. package/dist/plugins/remote-env-ssh.d.ts +15 -0
  202. package/dist/plugins/remote-env-ssh.js +109 -3
  203. package/dist/plugins/remote-shell.d.ts +0 -1
  204. package/dist/plugins/remote-shell.js +1 -2
  205. package/dist/plugins/retention-store-sql.js +6 -2
  206. package/dist/plugins/schema-consistency.d.ts +54 -0
  207. package/dist/plugins/schema-consistency.js +52 -0
  208. package/dist/plugins/session-epoch-anchor.d.ts +13 -0
  209. package/dist/plugins/session-epoch-anchor.js +23 -0
  210. package/dist/plugins/shared-memory-store-sql.d.ts +9 -3
  211. package/dist/plugins/shared-memory-store-sql.js +5 -16
  212. package/dist/plugins/sql-driver.d.ts +141 -0
  213. package/dist/plugins/sql-driver.js +115 -1
  214. package/dist/plugins/sql-errors.d.ts +41 -0
  215. package/dist/plugins/sql-errors.js +36 -0
  216. package/dist/plugins/store-backend.js +2 -1
  217. package/dist/plugins/tidb-session-storage.d.ts +7 -11
  218. package/dist/plugins/tidb-session-storage.js +6 -16
  219. package/dist/plugins/tidb-session-store.js +1 -1
  220. package/dist/prompts-domain-validate.d.ts +17 -61
  221. package/dist/prompts-domain-validate.js +9 -119
  222. package/dist/question.d.ts +18 -9
  223. package/dist/question.js +21 -13
  224. package/dist/request-key-closure.d.ts +4 -4
  225. package/dist/request-key-closure.js +18 -13
  226. package/dist/rule-entry-list.d.ts +134 -24
  227. package/dist/rule-entry-list.js +162 -30
  228. package/dist/run-local.d.ts +3 -3
  229. package/dist/run-local.js +26 -25
  230. package/dist/runs.d.ts +48 -2
  231. package/dist/runs.js +61 -2
  232. package/dist/server-secret-env.d.ts +44 -18
  233. package/dist/server-secret-env.js +49 -8
  234. package/dist/session-incarnation.d.ts +15 -5
  235. package/dist/session-incarnation.js +16 -9
  236. package/dist/session-titler.js +12 -2
  237. package/dist/spec-fields.d.ts +34 -18
  238. package/dist/spec-fields.js +46 -53
  239. package/dist/sql-engine-posture.d.ts +29 -4
  240. package/dist/sql-engine-posture.js +36 -1
  241. package/dist/stored-request-hooks-m20.d.ts +23 -0
  242. package/dist/stored-request-hooks-m20.js +124 -0
  243. package/dist/stored-request-tool-axes-m19.d.ts +20 -5
  244. package/dist/stored-request-tool-axes-m19.js +30 -2
  245. package/dist/task-a2a.d.ts +9 -1
  246. package/dist/task-a2a.js +73 -47
  247. package/dist/task-mcp.d.ts +54 -102
  248. package/dist/task-mcp.js +194 -92
  249. package/dist/task-settings.d.ts +47 -25
  250. package/dist/task-settings.js +46 -61
  251. package/dist/text-bidi.d.ts +3 -46
  252. package/dist/text-bidi.js +3 -3
  253. package/dist/tool-approval.d.ts +1 -1
  254. package/dist/trace/core-keyset-guard.d.ts +24 -4
  255. package/dist/trace/engine-notice-wire.d.ts +11 -3
  256. package/dist/trace/engine-notice-wire.js +8 -1
  257. package/dist/trace/ledger-events.d.ts +3 -1
  258. package/dist/trace/ledger-sink.d.ts +29 -0
  259. package/dist/trace/ledger-sink.js +15 -1
  260. package/dist/trace/model-segment.js +1 -1
  261. package/dist/trace/project.d.ts +25 -6
  262. package/dist/trace/project.js +68 -22
  263. package/dist/trace/wire-projection-faces.d.ts +80 -2
  264. package/dist/trace/wire-projection-faces.js +23 -1
  265. package/dist/user-authored-text.d.ts +7 -0
  266. package/dist/user-authored-text.js +3 -0
  267. package/dist/web-admin-client/apply-effective.js +10 -5
  268. package/dist/web-admin-client/candidate-gate.d.ts +6 -6
  269. package/dist/web-admin-client/candidate-gate.js +3 -3
  270. package/dist/web-admin-client/http-client.js +0 -6
  271. package/dist/web-admin-client/read-warnings.d.ts +13 -0
  272. package/dist/web-admin-client/read-warnings.js +10 -0
  273. package/dist/web-admin-client/skills-mcp.d.ts +3 -0
  274. package/dist/web-admin-client/skills-mcp.js +9 -6
  275. package/dist/web-admin-client/types.d.ts +3 -4
  276. package/dist/workspace-cache.d.ts +35 -0
  277. package/dist/workspace-cache.js +36 -0
  278. package/dist/workspace-space.d.ts +3 -2
  279. package/dist/workspace-space.js +2 -0
  280. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +8 -1
  281. package/package.json +4 -4
  282. package/scripts/drain-check-local.mjs +37 -3
  283. package/scripts/drain-check.mysql.sql +30 -2
  284. package/scripts/drain-check.pg.sql +29 -2
  285. package/dist/capabilities/memory-notice.d.ts +0 -138
  286. package/dist/capabilities/memory-notice.js +0 -64
  287. package/dist/capabilities/prompt.d.ts +0 -17
  288. package/dist/capabilities/prompt.js +0 -6
  289. package/dist/mcp-content-origin.d.ts +0 -34
  290. package/dist/mcp-content-origin.js +0 -10
  291. package/dist/tool-axis-words.d.ts +0 -27
  292. package/dist/tool-axis-words.js +0 -9
  293. package/dist/wire-name-bound.d.ts +0 -14
  294. package/dist/wire-name-bound.js +0 -13
package/MIGRATION.md CHANGED
@@ -7,6 +7,402 @@
7
7
  > ⚠️ 完整清单在仓库根 `CHANGELOG.md`——它**不随 npm tarball 出包**(本文件随包)。看完整迁移窗的
8
8
  > 权威姿势是源码 tag diff:`git diff v<旧>..v<新>`(每版都推 `v<版本>` tag);npm 包页也镜像 CHANGELOG。
9
9
 
10
+ ## 7.132.0 —— 引擎 7.52.0 → 7.53.0 提货:升级前停驻的子代腿 / Skill 卡在认领前被拒(先排空:随包两条新判据)· `settings.hooks` 换成引擎的扩展注册链(`wiring_manifest.hooks[]` → `mods[]`、`HOOKS_TIMEOUT_MS` 新读法)· 执行车道静默切断换码 `liveness_no_output` · 凭据按值也扣 · 一枚运维通告码退役 · 清单 `prompt_assembled` 换成 v3、`prompt_constitution_total` 下线 · `settings.hooks` 的 matcher 编不过任一事件 400、受理总额度 `budget_exceeded` · 技能 frontmatter 读不了拒载、空正文条目不装 · 标题腿 fail-open 标签按成因族分 · 投递腿不再重投提交时的图 · k8s 车道加固(两处新的起服拒)· 请求体 `mcpServers` 列表级坏形整份 400、沙箱车道的受理拒各带机读码 · 升级前停驻问答的起服一次性迁移 · 宿主 CLI 与 `LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW` 的凭据边界、`REMOTE_EXEC_HOME_DIR` 与实测家目录对证、中心鉴权桥的身份边界(安全)· 受理面拒句与拒体机读位 · 引擎收紧的会话规则与工具清单(放行侧近似名开跑前拒、规则名文法拒、存量会话规则行读不出即拒一切)· 递归读只按点名的根判(放宽;起服点名非锚定读拒行) · 子代工具清单近似名与存量会话规则孪生行的同步腿 200 + failed 改 400(码不变) · 一次性迁移 M17 遇读不出的 deny 行不改写、不盖戳 · 更正 7.131.0 第 8 条对请求体规则表的陈述(新拒的只有大小写孪生;文字更正) · 引擎 7.53.0:hook / 标题腿的 per-model 凭据绑首个发出的主机 · 请求体 `a2aPeers[].toolAxes` 与 MCP env / headers 两处收紧 · 任务流新帧 `compaction_started` 与几处随引擎变的 wire 值 · 中心提示层只有一种 wire 形 `{ schemaVersion: 2, artifact }`(web-admin 与本服务同窗升级,先中心后服务)· 场景名进 `promptBinding` · 品牌行由部署座 overlay 承载 · 「无持久记忆」披露由引擎渲染(委派子代不再带)· LKG 信封 5 · `Artifact(action:<词>)` 是规则(放行侧放宽)· 嵌入方 `snapshotBlobSqlCapBytes` → 读口 `snapshotBlobSqlCap { capBytes, source }`、`sqlEngineFacts` → `sqlEngine { dialect, facts }`(SQL 后端在场必填、值形起服判)(BREAKING)· `READ_DENY_BUILTIN_EXCLUDE` 写 `.npmrc` / `.pypirc` 起服拒
11
+
12
+ 1. **升级前先排空:7.131.x 停驻的子代腿与 Skill 卡,升级后续跑可能在认领前被拒**(零 DDL;卡留 pending,行留停驻)。引擎 7.52.0 在认领前按规范拼写读停驻行记下的工具名清单:
13
+ 子代的工具天花板里记着 `Bash(*)` / `Tool()` / 内容形 / 读不出的名 / deny `*`(7.131.x 的受理口收下 `excludeTools:["Bash(*)"]` 这类写法,引擎 7.51 按原拼写记进子代行)⇒
14
+ `409 checkpoint.config_refused`,`blockedBy: "config.tool_face_control"`、`field: "inheritedGate"`、`reason` 点名哪一臂;名字集记着重复条目(`excludeTools:["Read","Read"]`)⇒
15
+ `409 resume.parent_constraint_mismatch`;Skill 卡的清单里有读作空的条目 ⇒ `409 resume_outcome_invalid` + `field: "boundGrantDigest"`。
16
+ **怎么查**:升级**之前**、对 7.131.x 的数据跑随包排空脚本(`psql … -f node_modules/@sema-agent/server/scripts/drain-check.pg.sql` /
17
+ `mysql … < node_modules/@sema-agent/server/scripts/drain-check.mysql.sql`;`DB_BACKEND=local` 用 `node node_modules/@sema-agent/server/scripts/drain-check-local.mjs`),
18
+ 读 `parked-tool-lists` 那一行 —— 它数的是**候选**(天花板键在场且不是空数组 / 冻结约束链非空 / Skill 卡带绑定的停驻行;SQL 判不了引擎的拼写,所以是超集)。
19
+ **出路**:升级前让这些行在 7.131.x 上跑完 / 决完;已经升级的,在一台 7.131.x 的副本上续跑它们,或取消那条 run(`POST /v1/runs/:id/cancel`)后作为新任务重提
20
+ (新任务由 7.52.0 写下规范拼写,照常续跑)。拒的 409 体带 `blockedBy` / `field` / `reason`(见下一条),句子是引擎的原句。
21
+ **例外**:后台停驻的子代腿(经 parked 车道赎回)被同一类认领前拒时,引擎的回执只交成因词,本服务答 `422 parked_resume.startup_failed`(行回停驻、可再决),体只带 `cause`,
22
+ 不带 `blockedBy` / `field` / `reason`,句子仍是引擎的原句(到引擎的回执带上这三位为止)。
23
+ 2. **续跑被引擎拒的 409 体多两个键**(加性):引擎 `CheckpointError.detail.code` ⇒ `blockedBy`(挡住它的准入码,与 `409 resume_blocked_by_policy` 的同名键同义)、`detail.reason` ⇒ `reason`
24
+ (每一个引擎码都过境,含 `checkpoint.unsupported_version` 的 `version_newer` 等);`field` 照旧。`retriable` 的意思不变(卡仍 pending,不是「原样重试会成功」);
25
+ 本服务自己在重放存量体之前拒的 `409 resume_blocked_by_policy` 此版起同样带 `retriable: true`(此前缺席,把缺席读作终局的客户端会对同一处境读出相反结论)。
26
+ 后台停驻的子代腿是例外:答 `422 parked_resume.startup_failed`,体只带 `cause`,没有 `blockedBy` / `field` / `reason` / `retriable`(见上一条)。
27
+ 终态 run 的 `POST /v1/runs/:id/steer` 在铸唤醒卡之前的同类拒没有卡,体不带 `retriable`(此前 allow 读法 / 模式词读法两臂在那条路径上也带 `true`);其余键与续跑入口逐字相同。
28
+ 按 409 体键集精确比对的客户端请放宽。SLA 拒扫腿对同一张卡同一拒因只落第一条 `deny_sweep_resume_blocked`,之后看计数器 `deny_sweep_resume_blocked_total`。
29
+ 3. **回滚(7.132.0 → 7.131.x)前也先排空**:7.52.0 停驻的子代行带 `state.ownToolLists`(行上的读方要求词 `own_tool_lists`),7.131.x 读不了 ⇒ 续跑
30
+ `409 checkpoint.unsupported_version`(`writerVersion` 比 `readerVersion` 新)、卡留 pending。回滚前对 7.132.0 的数据跑同一个脚本,读 `own-tool-lists-rollback` 那一行(精确);出路 = 回滚前让它们跑完,或取消重提。
31
+ 4. **执行车道的静默切断换码**:一条命令因为没有输出被切(调用方的 `readTimeoutMs`,或 `K8S_LIVENESS_MS` / `E2B_LIVENESS_MS` 兜底)⇒ 码 `liveness_no_output` + 窗口秒数
32
+ (Bash 工具结果 `livenessNoOutput: true` / `noOutputSec: N`);墙钟到点仍是 `timeout`。7.129.1–7.131.x 两种切断同报 `timeout`。按 `timeout` 一个码判「时间切断」的读者,
33
+ 把 `liveness_no_output` 并进同一支(两词都可重试)。设备端的结局帧也可以报这一词(文件面读作 `unknown`)。
34
+ 5. **本部署的凭据按值也扣**(引擎 7.52.0 `declareWithheldEnv({ names, values })`;零配置):配置目录 secret 型变量与模型条目 `apiKeyEnv` 指的变量,它们此刻的值也交给引擎扣 ——
35
+ 同一把钥匙被抄进别的变量名,`inheritEnv:"all"` 下也不进子进程。值不是凭据(不足 8 字符 / 占位词 / 一个字符重复)的,那个值不扣、名照扣、起服不拒,起服日志点名那个变量一次;
36
+ `GET /v1/capabilities` 的新顶层布尔 `inherit_env.own_credential_values_withheld` 此时答 `false`。要它 `true`:给那个变量一个真凭据值。
37
+ 6. **auto 模式的配额烧得更快**(计费口径,零配置):引擎 7.52.0 起权限分类器与扩展的模型调用计进 run 的花费(`costMicroUsd`,「其中」行 `stats.costBreakdown.sideCallMicroUsd`),
38
+ 本服务的成本配额 / 用量批报 / 租约照记;延迟直方图只收主循环的调用。
39
+ 7. **一枚运维通告码退役**:`route.base_url_changed_key_unchanged`(目录换了主机、凭据没换)随引擎 7.52.0 退役 —— 换主机的那次请求被拒,告知改为会话流上的
40
+ `engine_notice` `route.credential_origin_mismatch`(受众 user)。按旧码过滤运维日志的告警请改读新码;`config.credential_not_withheld` 只进运维日志。
41
+ 8. **请求体 `mcpServers[i].startupTimeoutMs` 受理**(这一只 server 自己的启动界,毫秒整数;引擎 7.52.0):原样交给引擎,值形的判官只在引擎 —— 数字串 / 小数 / 越界 ⇒
42
+ 引擎拨号时只拒这一只 server(`config.mcp_startup_timeout_invalid`,兄弟照挂),不在受理口 400。7.131.x 受理口把它当表外键丢掉;升级前存下的请求体里若带着它,续跑起开始按它生效。
43
+ 9. **会话规则存量行**(只影响 7.128.x 及更早写下、还没改写过的行):`toolAllow` 里的 `*` 由引擎读成只作废它自己,名单其余照放;7.129.0–7.131.x 起服日志里
44
+ `session_rule_row_old_grammar_entries` 带 `wildcardMasked` 的行,升级后不必再手工去掉 `*`。
45
+
46
+ 10. **`wiring_manifest`:`hooks[]` 由 `mods[]` 取代**(租户面与 operator 面、live 帧与 durable 账本行同一构造器)。`mods[]` 一行 = 这条腿准入的一张扩展注册:
47
+ 挂上的 `{ name, seat, tier, events, observe?, skipped? }`,被拒的 `{ name, seat, tier, refusedBy, reason }`。本服务恒挂 `server.permission-denied-meter`(部署座),
48
+ `TOOL_TRACE=true` 时加 `server.tool-trace`,品牌部署加 `server.brand-identity`(第 55 条),请求的 `settings.hooks` 在本车道被采纳时加 `settings.hooks`(任务座)。`hooks[]` 不再发送。
49
+ **出路**:读 `mods[]`;`name` / `seat` / `tier` 按开集读,`observe` / `skipped` 缺席不折 false。缺席读作「这条腿没有注册」,不是「没有扩展判这条腿的调用」。
50
+ `name` / `refusedBy` / `reason` 与 `skipped[]` 里的串是注册方写的文本:凭据形已换成记号、超过 1000 字已截断(与通告 `mods.registration_refused` 的 `detail` 同一读法),按展示文本读,不要当可信输入。
51
+ 同批:引擎通告 `mods.registration_refused`(一张注册在准入时被判拒、没挂上;`detail: { name, seat, tier, refusedBy, reason, sessionId }`)进会话通告流的白名单;本部署今天发不出它。
52
+ 11. **`HOOKS_TIMEOUT_MS`** 是 `settings.hooks` 那张注册每个 handler 的时间预算(一个事件跑一遍本请求的钩子条目)。**未设 = 600000 ms**,即此前请求钩子所受的界;
53
+ `0` 按字面(零预算)。超预算时 `PreToolUse` 上该调用、`UserPromptSubmit` 上该提交被 fail-closed 拒,其余事件跳过这一轮。它不再约束本服务自己的两张观察注册
54
+ (拒绝计量 / 工具追踪,同步返回)。升级零动作。
55
+ 12. **审批卡 `allowRuleShadowed` 里 `member: "hook"` 的一行**,`entries` 现在点名放行的那张注册(请求自己的钩子 = `settings.hooks`);此前恒为空表。
56
+ 13. **请求钩子在审查工具调用或提交时失败**(钩子执行器自身出错、超预算),模型读到的拒句点名扩展(`extension "settings.hooks"`)。
57
+ 14. 请求 `settings.hooks` 的 `Stop` 条目在主任务结束时、也在委派子代结束时跑 —— 与此前同触达。`PermissionDenied` / `SubagentStop` / 两个 model-switch 事件仍由壳跑,服务端不跑。
58
+ 15. **本服务不再合成钩子记录**:部署座的两张注册只观察(判词不采纳、不进委派子代的约束),链里唯一的答者是请求钩子;请求钩子的答案在每个 handler 边界由引擎屏
59
+ (越约成员、读不出词的载体 ⇒ 该调用 fail-closed 拒,与此前读数相同)。
60
+ 16. 升级前记下的 run 的 `wiring_manifest` 行保留 `hooks[]`;本服务从不回读它,也不改写。停驻任务续跑零动作:续跑时请求钩子从存下的请求体照常重新铸出。
61
+ 17. **升级前钉在中心提示上的会话,下一次跑由中心源的状态决定**(零配置;所有会话后端)。7.131.x 及更早写下的会话 epoch 记录是退役形,升级后
62
+ 不改写,引擎按字段名认出它(连同它有没有钉一份中心提示)。钉了中心提示的会话(顶层会话;委派子代用父会话的中心层)下一次跑:中心源 `active` ⇒ 用当前候选组装,另记一条
63
+ `code_change` + `changed: ["published_schema"]`(对话中途换了中心文本,有记录;提示前缀缓存失效一次);`disabled` ⇒ 空中心层,同样记一条;
64
+ `unavailable`(或本部署没有中心源)⇒ 这次跑以 `prompt.snapshot_unavailable` 失败,旧记录保留,中心源有了已核状态后再试即可。
65
+ 没钉中心提示的旧记录 ⇒ `code_change`,在下一次压缩采用中心候选之前不带中心层。MySQL 协议 / PostgreSQL 后端上,记录落在加载 floor 之下
66
+ 的会话此前被读成「没有记录」(照跑并另记一条),本版起与其余会话同读。**出路**:让中心源发布一份本引擎可用的中心提示(`active`)或显式停用
67
+ (`disabled`);或者让这只会话的调用方开新会话(新会话没有旧记录)。
68
+ 18. **`GET /v1/sessions/:id` 的 `promptEpoch` 换形**(BREAKING):`{ kind: "pin", descriptor }`(`descriptor` = `{ epoch, identity, layers, activatedBy, changed?, sourceRevision? }`)
69
+ 或 `{ kind: "legacy", centerPinned }`;旧的 `artifactDigest` / `packId` / `assemblyApi` 不再出现,`activatedBy` 的 `legacy_migration` 并入 `code_change`。
70
+ **出路**:按 `kind` 分支读;键缺席仍是「这只会话没有记录」。
71
+ 19. **清单 `prompt_assembled` 换成引擎的清单 v3;计数器 `prompt_constitution_total` 下线**(账本行、`GET /v1/tasks/:id/turns` 的 `prompt-assembled` 块、trace SSE 的同名帧同一形)。键对照(v2 → v3):
72
+ `constitution` → 删(这条腿是哪一份提示读 `identity`:`artifact` / `layers` / `epoch` / `activatedBy`;选择器值读 `selectors`:`binding?` / `traits` / `host` / `facts`);
73
+ `blocks[]` 不变(`id` / `chars` / `hash`);`sections[]` 的 `slot` / `carrier` / `cadence` / `cacheClass` → 删(按 `band` 分组,`rank` 是档内次序),`hash` → `contentHash`
74
+ (`sha256:` = 发布文本,`salted:` = 带本任务 / 本 run 输入的文本),新增 `variant?` / `renderer?` / `locale` / `chain`(文本来源与施加过的 overlay);新增顶层 `superseded?`(被取代的 overlay);
75
+ `tools` / `toolDisclosure` / `totalChars` 不变。可选成员缺席即「没有」,不折成空值;在场却读不出的成员不上行(本服务计 `server.trace.projection-input-malformed`)。
76
+ `prompt_constitution_total{mode}` 不再注册:它要看的那件事(宪法层丢了)在引擎 7.52.0 下不会发生 —— 宪法档锁定、只由引擎组,逐字含锁定段的提示在开跑前被拒
77
+ (`prompt.assembled_role_refused`);抓它的看板 / 告警请删掉,没有替代计数器。升级前写下的 `prompt_assembled` 行保留 v2 形,本服务不回读、不改写,读腿原样交出;
78
+ 一条升级前停驻、升级后续跑的 run,账本里两形并存 —— 带 `constitution` 的是旧行(旧行恒带它,v3 行永不带);其余都是 v3 行,
79
+ v3 行上缺 `identity` = 引擎交出的身份读不出(已丢弃并计数),不是旧行。
80
+ 20. **`settings.hooks` 里编不过的正则 matcher,写在任一契约事件上都 `400`**(受理面收紧;行为变更)。7.126.0–7.131.x 只对 `PreToolUse` / `PostToolUse` / `PostToolUseFailure` 判,写在 `StopFailure`(`error` 词)、
81
+ `PreCompact` / `PostCompact`(`trigger` 词)上的同一串被受理,运行期该组静默不跑(只落 `hook_matcher_invalid` 日志);写在壳跑的事件(如 `SessionStart`)上同样被受理,处置归壳。现在 33 个契约事件同一判 —— 含 matcher 被忽略的 `Stop` / `UserPromptSubmit` / `PostToolBatch`
82
+ (「这一串读不读得了」是串的形问题,不分事件类)。体 `detail: { reason: "invalid_regex", event, groupIndex, path }`(`path` 是加性坐标,见受理面拒句改写那一条),`event` 现在可以是任一契约事件。「选中别名而选不中正名」(`dead_alias`)仍只属三个工具事件。
83
+ **谁受伤**:提交里在任一事件上带编不过 matcher 的客户端(此前 `202`,现在 `400`)。**做法**:修正则,或改成工具名列表(`A|B`),或删掉那个无用的 `matcher`。
84
+ 续跑重放不判(存量里的坏 matcher 照旧采纳:读 matcher 的引擎事件上那一组不跑并记 `hook_matcher_invalid`,matcher 被忽略的 `Stop` / `UserPromptSubmit` / `PostToolBatch` 照旧跑条目);多租户车道(`settings.hooks` 整只被忽略)不判。受理线程上解析 matcher 的数量上界由 48 组升到 33 事件 × 16 组(只解析不执行,每串 ≤ 8 192 字符,实测毫秒以下)。
85
+ 21. **`settings.hooks` 受理总额度:`400` 多一个 `detail.reason` 词 `budget_exceeded`**(加性)。每次求值的 `1 000 ms` 额度之外,一次受理里所有已完成的正则形求值的耗时之和超过 `5 000 ms` ⇒ `400 request.field_invalid`,
86
+ `detail: { reason: "budget_exceeded", event, groupIndex, totalMs, path }`(`groupIndex` = 越线的那一组,`path` 是它的坐标)。此前没有总界:每只都刚好低于单次额度的病态 matcher 最多(3 事件 × 16 组 × 1 别名 × 2 = 96 次)可占住事件循环近 100 s。
87
+ 良性设置不受影响(最大的良性配置总耗时在毫秒级)。**做法**:按 `detail.reason` 穷举读的消费方放行第四个词;用户侧把慢的 matcher 改成工具名列表或简化正则。
88
+ 22. **技能 frontmatter 读不了的技能不再静默变成全局技能**(`SKILLS_DIR` 与插件带的技能)。① 开头围栏行 `---` 现在容尾随空格 / 制表(此前 `--- ` 开头的文件整块读不出,`scenarios` 场景限制被丢);
89
+ ② 文件第一行是围栏而块没有收尾 ⇒ 这一条被拒载(一行 warn `skill_refused`,点名技能与文件,`reason: frontmatter_unclosed`),不再装成每个场景都有的全局技能,兄弟技能照装。第一行不是围栏的文件没有 frontmatter,仍是全局技能。
90
+ **谁受伤**:靠「开头围栏不收尾」碰巧被当作全局技能的技能文件(现在不装)。**做法**:补上收尾的 `---`,或删掉开头那行。
91
+ 23. **正文为空串的技能条目不再装成空正文技能**(`CONFIG_PROVIDER=local` 的 `config.d/skills.json` 与中心的技能域同判)。条目缺 `content` 或写成 `""` ⇒ 一行 `web_admin_skill_failed`(`reason: "body_absent"`),不装、同名出厂技能保留;
92
+ 此前名册里有这个技能、调用回空、零行日志。**做法**:给条目写上正文,或把它 `enabled: false`。
93
+ 24. **自动会话标题腿的 fail-open 标签按成因族分**(运维遥测;零配置)。此前除「端点回 3xx 被拒」外,标题那一次模型调用的所有失败都记成 `server.session-titler.gateway-unreachable`。现在:
94
+ `gateway-unreachable` 只数真不可达(连不上 / 网络层失败 / 超时);新增 `…model-unresolved`(选不出模型)· `…route-refused`(路由核验拒)· `…credential-custody-failed`(per-model key 托管读不出)· `…bad-response`(端点答了非 2xx 或 body 不是 JSON)·
95
+ `…no-content`(空应答)· `…unclassified`(失败码缺席)。**做法**:按旧 tag 统计「标题腿失败总数」的看板 / 告警把新增六只并进去;`session_title_total{outcome="llm_failed"}` 照旧逐次记账。
96
+ 25. **投递腿不再重投提交时的图**:提交体的 `images` 与 `userAttachments` 同律,是「这条目标」的一次性输入。在宿主会话上新起的一跑(workflow 决定的投递腿)与续跑腿(审批 / 资源停车 / wake / plan_review)重建配置时不再带它,
97
+ 此前宿主当初提交的图会随指路文本再投一遍。**做法**:要让模型再看一遍图,在新的提交里重新带上 `images`。
98
+ 26. **k8s 车道:两处新的起服拒、其余行为只收窄**(只在 `REMOTE_EXEC=k8s` 时求值;零 DDL)。
99
+ ① `WORKSPACE_CACHE_MOUNT`(只在 `WORKSPACE_CACHE=volume` 时读)落在保留的镜像目录闭集 `/bin /boot /dev /etc /home/sandbox /lib /lib64 /opt/sema /proc /root /run /sbin /sys /tmp /usr /var`
100
+ **上 / 之下 / 之上**(挂在 `/opt` 或 `/home` 这类祖先上也算)⇒ 起服即拒,拒句点名那一项与闭集;缺省 `/cache` 与不碰闭集的路径不受影响。**出路**:把挂载点改到别的绝对路径(如 `/cache`、`/data/cache`)。
101
+ ② `WORKSPACE_SNAPSHOT_EXCLUDE` 里只由 `*` / `?` 组成的项(`*`、`**`、`?`)⇒ 起服即拒并点名第几项(码 `config.workspace_snapshot_exclude_invalid`);`*.log`、`build-*` 这类带字面部分的项照旧。**出路**:删掉那一项,或写成真名。
102
+ ③ 行为修复(升级零动作):会话店读住客有了时限(`rpcTimeoutMs`,缺省 30 秒),超时按住客读故障拒这条 run,`workspace_space_unreadable_occupant` 的 `code` 是 `incarnation_read_timeout`;run 末快照保存前一律先停 pod 里的进程并确认(与那一刻有没有命令在飞无关:命令结束不等于它起的进程结束),
103
+ run 结束时还在飞的命令先被等(最长 `rpcTimeoutMs`);等不到的断开套接字、照样保存并出运维行 `workspace_snapshot_commands_cut`;没能确认进程停下(杀进程失败 / 复查仍有活进程)则不保存(`command_in_flight`),有没有命令在飞、等到与否都一样;`command_in_flight` 的拒还留给停驻挂起;被集群在准入时拒的 pod(`Failed`)记成这只环境的拒、不再每次操作重建,新增运维行 `sandbox_pod_refused`;
104
+ 绑不上的缓存卷按空间记一个快照时限、窗内不再等;回收座同一时刻最多 4 个空间在收,同类失败一次调用只出一行(`workspace_space_reclaim_failed` / `workspace_cache_reclaim_failed` 多一个 `count`,`space` / `sessionId` 点名调用里排第一的)。
105
+ **出路**:按这些行里的 `count` 与样本查;计数器 `server.workspace-space.reclaim-failed` / `server.workspace-cache.reclaim-failed` 仍逐空间计,抓它们的告警不用改。
106
+ 27. **请求体 `mcpServers` 的列表级坏形与词表外的键,新提交整份 400;探测口与新提交同一个答案**(受理面收紧,BREAKING)。`POST /v1/tasks` · `/v1/tasks/stream` · `/v1/runs`:
107
+ `mcpServers` 在场却不是数组 / 超过 32 条 / 某一条不是对象 ⇒ `400 request.field_invalid`(此前 `200`:整只忽略 / 挂前 32 条 / 摘掉那一条);条目层或 `transport` 层的词表外的键 ⇒
108
+ `400 request.body_shape` + `unknownKeys`(此前悄悄不读)。`POST /v1/capabilities/mcp/probe` 问同一只判官,码与句同新提交:列表不是数组、条目不是对象由 `request.body_shape`
109
+ 改为 `request.field_invalid`(缺席 / `null` 仍是 `request.body_shape`);`toolFaces[].pathTarget.skillScopeEligible: false` 一类判官的合法归一不再被拒;
110
+ 坏 URL 的拒句不再回显 URL;声明了 `transport.assertionHeader` 的条目探测口照旧整条拒(新提交照挂,本版承载、不发送)。
111
+ **谁撞**:发多于 32 台、或列表里夹着非对象 / 多余键的调用方;按探测口码分支的客户端。**出路**:把列表改成数组、删掉第 33 条起的条目与非对象条目、改正或删掉
112
+ `unknownKeys` 点名的键;探测口客户端把这两形改读 `request.field_invalid`。耐久 resume 重放存量请求不拒、处置不变。`mcpServers: null` 照常受理(与缺席同义)。
113
+ 28. **MCP `toolFaces` 的工具名不能是 `__proto__`**(所有腿;`constructor` / `toString` 等其余名字照收)。此前判合形而那份面静默消失、server 照挂;
114
+ 现在判不出形:中心 / config.d 腿跳过这条 server 并点名 warn `web_admin_mcp_tool_faces_invalid`,新提交与探测口 `400 request.field_invalid` 点名 `mcpServers[i].toolFaces`,
115
+ 重放腿整条不进。**出路**:去掉那一只工具的面(远端工具真叫这个名字时,它只能无面挂载)。
116
+ 29. **带 `sandboxImageProfile` 的提交被拒时带机读码**:执行车道不是 k8s ⇒ `501 capability.k8s_lane_required`(此前 `400`、无码);本部署没有镜像索引 ⇒
117
+ `501 capability.image_index_required`(此前 `400`、无码);没有解析出会话 ⇒ `400 request.precondition_unmet`;没有可见的已发布镜像 / 缺所需能力 ⇒ `404 not_found.image`;
118
+ 不认识的能力词 ⇒ `400 request.field_invalid`。**谁撞**:按「状态 400 + 句子子串」认前两形的客户端。**出路**:改按 `errorCode` 判(上面五个码),不读句子。
119
+
120
+ 30. **升级前停驻的问答,升级后卡、到期、决断口三处都按问答读(SQL 部署,起服一次性迁移 M21,零动作)**:7.125.x 及更早停驻、升级时还挂着的问答(7.126.0 第 22 条那一类)门上没有卡词 ——
121
+ 7.126.0–7.131.x 上队列行不标问答、到点被 SLA 拒扫(模型读到「拒」)、决断口却按工具名要 `answer`。本版第一次起服给这些行的门补上卡词 `question`(引擎今天在铸点写的那个词):
122
+ 队列行带 `approvalCard: "question"`,到点**作废**(不再当成「拒」),决断口照旧要 `answer`(`approve` 带 `answer`,拒绝照常)。引擎行摘要封过的行不改写(按构造都已带卡;有的话计数
123
+ `parkedQuestionRowsLeftAsWritten`)。**例外**:`DB_BACKEND=local` 的停驻行在引擎自己的文件店里,本服务改写不到 —— 那里的这类问答照旧按 7.126.0 第 22 条读;介意的话升级前答完或拒掉。
124
+ 31. **`GET /v1/approvals` / `/v1/approvals/stream` 的 `pending[]` 行 `gateKind` 恒在场**(此前「`gate_kind` 列诞生前停驻的旧 SQL 行」可缺席)。每次停驻都盖这一列;同一次迁移 M21
125
+ 把列为 NULL 的存量停驻行从门子树补齐,门子树里读不出登记门型的行(引擎续不了)置 expired。按 `gateKind` 路由的客户端可以删掉缺键的旧行为臂:缺键 = 读不出,按 fail-closed 处置。
126
+ 若库里仍有一行 pending 且 `gate_kind` 为 NULL(写口恒盖、迁移补齐之后不可达 —— 手改的行),队列不列它(计数 `fail_open_total` 的 `server.approvals.queue-row-gate-kind-missing`),
127
+ 与各决断口同一个读法(按门型取行的决断口本来就不认它);出路是把那一行置 expired 或删掉会话。
128
+ **怎么查**:`SELECT session_id FROM checkpoint WHERE status = 'pending' AND gate_kind IS NULL`(应为零行)。
129
+ 32. **起服一次性迁移半途失败时,拒因与起服日志带上本次已提交的全部读数**:`one_time_migrations_interrupted` 那一行的读数键与 `one_time_migrations_applied` 同名(各步骤的计数、
130
+ 落定了的形读数),随后是逐行的那几种(`permission_rule_row_removed` / `permission_rule_bucket_unreadable` / `session_rule_row_old_grammar_entries` / `session_rule_row_retired_bucket`)。
131
+ 持久规则 scope 清理半途失败不再自铸「…failed AFTER removing N row(s) — record them before retrying: …」那句拒因,已删的行在被打断的那一行与逐行 warn 里。
132
+ 按旧拒因句文字或 `one_time_migrations_interrupted` 旧键集(只有 `sessionRuleColumn` / `sessionRuleRowsWithOldGrammarEntries`;前者此前在没发布终名时写 `"in-progress"`,现在缺席)解析日志的脚本请改按读数键读。
133
+ 这一次起服的日志是这些读数唯一的出处(重试的起服看见的是已改完的行),请保存。
134
+ 33. **SLA 拒扫腿读过期审批行失败时响亮**:每次一行 warn `deny_sweep_list_failed` + 计数器 `deny_sweep_list_failed_total`,那一拍不扫(此前静默当成没有过期行)。
135
+ 34. **跳版升级库的拒启判据在 PostgreSQL 上按 `search_path` 读**(与起服列册对账同一读法):退役表 `snapshot_manifest` / 退役列 `checkpoint.pending_steer` 住在 `search_path` 后段、
136
+ 裸表名会解析到它的部署,此前不拒、本版起拒(`config.schema_mismatch`)。**出路**:7.125.x 的迁移只看 `current_schema()`,照「先在 7.125.x 起服一次」直接跑看不见后段 schema 里的那两件 ——
137
+ 用 `SELECT table_schema FROM information_schema.columns WHERE table_name = 'checkpoint' AND column_name = 'pending_steer'`(表同理查 `information_schema.tables`)找到它住的 schema,
138
+ 让 7.125.x 起服时把那一个 schema 排在 `search_path` 第一位(如 `ALTER ROLE <角色> SET search_path = <那个 schema>, <其余>`),跑完改回再起本版。其余部署零变化。
139
+ 35. **宿主上的 `docker` / `adb` CLI 不再继承本服务的凭据**(安全,零配置):local-docker / adb 车道起这两只 CLI 时,env = 整份进程环境**减**本服务凭据(配置目录 secret 型变量、
140
+ 模型条目 `apiKeyEnv` 指的变量,按名与值);`DOCKER_*` / `AWS_*` / `ADB_VENDOR_KEYS` 这类 CLI 与凭据助手要读的变量照交。7.131.x 原样继承整份环境(adb 首次调用起的常驻
141
+ adb server 也带着它)。**受伤方**:宿主上读本服务某个凭据变量的 docker 凭据助手 / CLI 插件 —— 换了名的副本也扣(按值比),所以出路是给它**一枚自己的凭据**
142
+ (值与本服务的任何凭据都不同),或改用 docker 自己的凭据存储(`docker login` / `credHelpers`)。
143
+ settings hooks 的命令腿同批改走同一只组合(严格名单十二名不变,零动作)。
144
+ 36. **剥除留痕现场改名(BREAKING,告警规则)**:host shell 那条腿 `site="server.remote-env-host.inherit-scrub"` → `site="server.remote-env-host.spawn-env"`(计数器
145
+ `secret_env_scrubbed_total{site}` 与日志行 `secret_env_scrubbed` 的 `site` 同改,无别名);新增三个现场 `server.remote-env-local-docker.spawn-env` /
146
+ `server.remote-env-adb.spawn-env` / `server.hooks.spawn-env`。**做法**:按旧标签值写的告警 / 过滤改一个值。
147
+ 37. **`LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW` 点名本服务自有凭据名 ⇒ 起服拒**(安全):例如 `LSP_ENV_ALLOW=SERVICE_AUTH_TOKEN` / `BAKE_ENV_ALLOW=MODEL_API_KEY`,拒句点名旋钮与段号、
148
+ 带引擎拒白名单的同一句(`… which is this deployment's own credential …`)。点到的变量**值**是本服务凭据的副本(换了个名)⇒ 放回时被扣下(7.131.x 照放)。
149
+ **做法**:语言服务器 / 配方脚本要凭据 ⇒ 给它一枚自己的(值与本服务的任何凭据都不同),点那个变量名。
150
+ 三只沙箱名单(`E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV` / `K8S_SANDBOX_ENV`)不受这一条影响。
151
+ 38. **装了中心鉴权桥、没配 service token 的部署有身份边界**(安全,行为收紧;中心鉴权桥 = `SEMA_WEB_ADMIN_URL` 在位即默认开,或 `AUTH_BRIDGE_ISSUER`):不带任何凭据的
152
+ `GET /v1/sessions` ⇒ `401 auth.unauthorized`(7.131.x:200,列全部会话;trace / 用量列表、fleet 读面、发出的文件链接列表同读),带登录令牌只看得到自己的行
153
+ (别人的会话 ⇒ `404 not_found.session`),`/metrics` 族匿名 ⇒ `401`。
154
+ 只配了 `TRACE_TOKEN` 的部署上 `/metrics` 族匿名同样 ⇒ `401`(7.131.x:200)。判据一句:dev-open ⇔ `/health.auth.kinds` 为空 ∧ 没配 `TRACE_TOKEN` ∧ 没开 `REQUIRE_PRINCIPAL`。
155
+ **做法**:本机工具读会话带登录令牌(Bearer);免凭据抓 `/metrics` 的采集设 `METRICS_TOKEN`。仍成立的边界:没配 service token 时,不带登录令牌的请求自报的主体头仍按身份读 ——
156
+ 要「没令牌就没有身份」配 `SERVICE_AUTH_TOKEN`。
157
+ 39. **`REMOTE_EXEC_HOME_DIR` 在 local-docker / ssh 车道上与执行环境的 `$HOME` 对证**(安全):每只执行环境开起来那一拍问一次 `$HOME`,与声明不一致(去尾斜杠后逐字比)、
158
+ 不是绝对路径或问不出 ⇒ 那只环境不开,工具结果的拒句点名两个值;7.131.x 照开、按声明守错目录。**升级前**:在这两条车道上核一遍声明 = 执行环境里 `printf %s "$HOME"`
159
+ 的输出(`docker run --rm <镜像> sh -c 'printf %s "$HOME"'` / `ssh <目标> 'printf %s "$HOME"'`);不一致就改声明,或让环境以声明的那个用户运行。没声明的部署零动作。
160
+ 40. **没有家目录的执行车道起服一行 warn `execution_lane_home_undeclared`**(随引擎 7.52.0):k8s / local-docker / ssh / adb / device 车道没设 `REMOTE_EXEC_HOME_DIR` 时,
161
+ 每一次泄内容的递归读(`grep -r` / `rg` / `git grep` / `tar c` …)全 shell 面真人问(allow / 模式 / bypass 都放不掉)、只读面拒,无人值守任务检索不了代码。
162
+ **做法**:声明 `REMOTE_EXEC_HOME_DIR=<执行环境里 $HOME 读出的绝对路径>`(local-docker / ssh 上会被对证,见上一条)。
163
+ 41. **受理面的几句拒句改写,拒体多机读位**(码都不变,`request.field_invalid`;新键加性)。锚拒句全文的客户端请改按 `errorCode` + 机读位分支;下面是改了的句子(左 = 7.131.x,右 = 7.132.0):
164
+ - 技能清单超帽:`skill.allowedTools has 1001 entries — …` → `skills[1].allowedTools has 1001 entries — …`;子代技能 `agents[0]: skills[1]: skill.disallowedTools has …` →
165
+ `agents[0].skills[1].disallowedTools has …`(句子与 `field` 点同一个体内坐标)。体加 `field` / `reason: "over_cap"` / `count` / `cap`。
166
+ - 子代定义超限:`agents[i]: agent.<键> must be …` 里**撞帽**的那几形 → `agents[i]: agent.<键>[…] has <n> entries|characters — at most <cap> are accepted`
167
+ (`name` > 64 字、`whenToUse` / `whenToUseLean` / `observer` / `observerMessage` > 4096 字、`systemPrompt` > 65536 字、`allowTools` / `denyTools` > 64 条或单条 > 128 字);
168
+ `at most 32 agents per request` → `agents has 33 entries — at most 32 are accepted`。体加 `field`(`agents[i].<键>[…]` / `agents`)/ `reason`(`over_cap` 条数 · `too_long` 字符数)/ `count` / `cap`;
169
+ 形错(不是串、空串条目)仍是原句、不带机读位。
170
+ - `additionalDirectories` / `additionalReadDirectories`:`each additionalDirectories entry must be a non-empty absolute host path (no '..' segments)` →
171
+ `additionalDirectories[i] must be a non-empty absolute host path (no '..' segments) — got "<原值>"`(体加 `field` / `reason: "bad_entry"` / `index`);
172
+ `additionalDirectories must have at most 64 entries` → `additionalDirectories has 65 entries — at most 64 are accepted`(体加 `field` / `reason: "over_cap"` / `count` / `cap`)。
173
+ - 拒句里回显调用方的串换成一只口径(剥控制与双向字符、截到 64 字 + `…`、引号形):`mcpServers[i]` 写坏的 `name` 值由 32 字变 64 字,`contentOrigin` 坏值不再是 JSON 转义形,
174
+ `toolFaces` 点名的工具名截长;子代定义的未知键 / 重名、`settings.hooks` 拒句里的坐标串段与坏键,正常长度的不变、超长或夹控制字符的按这一口径回显。
175
+ - `settings.hooks`:句子骨架不变;每一只 400 都带 `field: "settings.hooks"` + `detail`(`reason` + 坐标 `path`,见契约附录 A 的 `request.field_invalid` 行),matcher 四形(含第 21 条的 `budget_exceeded`)的 `detail` 多一个 `path`。
176
+ - 射程:请求体上其余的帽(`systemPrompt` / `appendSystemPrompt`、`settings.outputStyle` / `settings.env`、`jobId`、`clientContext`、`sandboxImageProfile`、`outputSchema`、`resumeAt`、技能 `content`、`images`)仍只有句子。
177
+ **出路**:按 `reason` 分支(`over_cap` / `too_long` 读 `field` + `count` + `cap`;`bad_entry` 读 `field` + `index`;`settings.hooks` 读 `detail.reason` + `detail.path`),句子只作展示。
178
+ 42. **工具清单写成已挂载工具的另一种拼法,放行侧也开跑前拒**(随引擎 7.52.0;更正 7.131.0 第 8 条「放行侧的退役名照 7.130.0 受理、交引擎既有通告」—— 有后继的退役名不再照跑):
179
+ 根 `allowTools: ["Task"]` / `["bash"]` ⇒ `400 config.tool_face_control`,句子 `TaskSpec.allowTools declares tool "Task" (tool_name: a retired tool name, now "Agent") in its allowed tools …`;
180
+ 子代定义 `agents[].allowTools` 同两名 ⇒ 同步腿 `400 config.agent.unknown_tool`(体 `{ errorCode, error }`,第 46 条)。两腿零模型调用。改法:写现名(`Agent` / `Bash`)。
181
+ 没有后继的退役名(`MultiEdit`)与内容形(`Agent(Explore)` / `Bash(npm:*)` / `Write(*)`)照旧照跑。
182
+ 43. **规则名在名字层读不出 ⇒ 规则文法拒,句子换成 `invalid.tool_name`**(随引擎 7.52.0)。逐入口:
183
+ - 请求 `settings.permissions.ask` / `deny`(`422`):名字以 `.` 收尾(`mcp__srv.`)、两侧带空白(`" Bash"` / `"mcp__x "`)、名字被读作 `*`(`*` / `*()` / `*(*)`)⇒ `→ invalid.tool_name`。
184
+ 此前:`mcp__srv.` 已拒(句子给铸出写法 `mcp__srv__`);带空白的修剪后照编(`deny: ["mcp__x "]` 读成整台服务器的覆盖规则);`ask: ["*"]` 收下、编出一条永不命中的规则。`allow` 照旧交引擎。
185
+ - 运维三表 `APPROVAL_REQUIRE` / `APPROVAL_DENY` / `APPROVAL_NEVER_AUTO`(起服拒)与中心 `governance.approvalRequire`(整组拒、保前值):`mcp__srv.` 与裸 `*` ⇒ `→ invalid.tool_name`
186
+ (带括号的 `*()` / `*(*)` 在三张运维表上先撞本服务「CC 形规则写法」的拒,原句);`APPROVAL_GRANT` ⇒ `reads as nothing (grammar)` / `(wildcard_all)`。
187
+ 这几个入口按逗号拆、逐项修剪空白 ⇒ 两侧带空白的名照收修剪后的名(不是这一版的变化)。
188
+ - 会话规则写口(`PUT /v1/sessions/:id/policy` 与 `sync/import` 同一只判官):`400 rules.inert_entry`,`reason` 由「这份值按哪一版文法读」定 —— 值带文法戳(`grammar`)按戳读;
189
+ 缺戳而带行 `rev` 的按最早的文法(0)读;两者都不带是新写,按本引擎文法(2)读。读不出的名字(尾 `.`、两侧空白):文法 2 ⇒ `grammar`,更早(戳 0 / 1,或缺戳带 `rev`)⇒ `earlier_grammar`;
190
+ `toolDeny` 的通配名恒 `wildcard_all`;`toolAllow` 的通配名文法 0 ⇒ `earlier_grammar`、文法 1 / 2 ⇒ `wildcard_all`。尾 `.` 此前同码、句子给铸出写法。
191
+ 锚旧句「对端段铸不出 / 写成 `mcp__srv__`」的客户端改按 `errorCode` 与 `reason` 分支。
192
+ 44. **存量会话规则行的 deny 桶带读不出的条目(空串等)⇒ 这一会话每一次调用都拒;非 operator 的规则写 `403 loosen_forbidden`**(随引擎 7.52.0,行为收紧):
193
+ 此前(7.129.0–7.131.x)非 operator 推一份更紧的规则可直接替换这样的行;现在旧行被读作「拒一切」,任何非 operator 的写都算放宽,句子
194
+ `the prior rules refuse every call — their toolDeny holds "" (grammar), which no grammar reads`,行原样不动。出路:operator 写一份不带行 rev 的新值(`PUT /v1/sessions/:id/policy`,operator 身份)。
195
+ 45. **递归读只按它点名的根判(放宽)**(随引擎 7.52.0):`grep -r x sub` 下面有被 `READ_DENY_PATTERNS` 拼写行(`**/secrets/**`)或内建档位(`wallet.dat`)命中的文件时,
196
+ 命令照跑、内容回来(此前点名拒);根点名那个目录 / 文件照拒(`grep -r x sub/secrets`)。目录多于 256 的树不再买待批卡。要护住根下面的目录:把行写成根锚(`<工作区根>/secrets/**`)或家锚(`~/secrets/**`)。
197
+ **根锚 / 家锚护不住的两类**:① 根下面链出工作区的链接被跟随(`grep -R`;BSD 上 `grep -RS`),链接目标(工作区外的文件,包括家目录下的凭据文件)的内容照样回来,`READ_FACE=roots` 也不问 ——
198
+ 容纳判断同样只按根做(点名那只链接本身,如 `cat sub/link`,照旧按根外读问);家目录被 bind 挂进工作区时同理。② 只有拼写行覆盖的凭据类文件(`.ssh/`、`id_rsa*`、`.netrc`、`.git-credentials`)
199
+ 在根下面时随遍历读到(直接点名照拒)。没有恢复旧枚举保护的开关;剩下的护法:结构化 Grep 工具(先按路径剪再读)、点名子目录作根、工作区里不链出去、不把家目录挂进工作区。
200
+ **起服与中心热发会点名你写的非锚定行**:日志一行 warn `read_deny_unanchored_rows` 列出 `READ_DENY_PATTERNS` 与中心 read-face 里既不是根锚也不是家锚的行(起服一次 —— server 与 `run-local` 同一处告知;中心热发的读拒行换了值时再一次;只告知,不改放行 / 拒绝、不拒启;内建档位的行是引擎自己的表,不在其列)。
201
+ 46. **同一类「请求 / 存量写错」的拒,同步腿只剩一种 HTTP 形(wire,码不变只换档)**:下面两码在同步腿(`POST /v1/tasks`)由 HTTP `200` + 终局 `failed` 体改答 **`400`**,
202
+ 体 `{ errorCode, error }`(与受理口先发现时同一键集;`errorCode` = 原 `terminal.code`,`error` = 原 `terminal.message`;`terminal.detail.detail.reason` 不再上 wire,原因词在句子里):
203
+ - `config.agent.unknown_tool`:子代定义 `agents[].allowTools` 里受理口放行、引擎才发现的几形 —— 有后继的退役名(`Task` · `bash`)与文法读不出的拼法(`Bash(`);受理口先拒的几形(拒侧全部、放行侧的大小写孪生)此前就是 `400`。
204
+ - `config.legacy_tool_name`:所在会话的存量规则行(升级前写口收下的)带近似名(`BASH`)· 退役名 · 无前缀 `__` 名 · 工具段为空的协议名 —— 这一会话的每一次提交在准备阶段被引擎拒,此前同样答 `200` + `failed`。
205
+ 只在零 turn、零 token 时答 `400`(verify / cascade 前几轮已经跑过的,照旧 `200` + `failed`);异步 / 活流腿的 HTTP 状态与终态体不变(2xx,同码落在终态 run 行);三条提交腿一致地不占提交幂等键(同一 `Idempotency-Key` 重发按新请求受理,此前这两码的失败占键并回放)。
206
+ **已知边界**(`config.tool_face_control` 此前即如此):引擎在 prepare 里先还原文件(请求键 `rewindFilesTo`,或 `resumeAt` 加 `restoreFiles: true`)、后判工具清单与规则名 ⇒ 带文件还原且清单 / 规则行写错的提交会先还原、再被拒,同键重发会再还原一次;请先改清单(或改写会话规则)再发还原。
207
+ 按 `terminal.code` 读 `200` 体的消费方改读 `400` 体的 `errorCode`。**出路**:子代清单写现名;存量规则行 ⇒ operator 改写那一会话的规则(`PUT /v1/sessions/:id/policy`,operator 身份,写一份读得出的新值)。
208
+ 47. **一次性迁移 M17 遇读不出的 deny 行不改写、不盖戳**(更正 7.129.0 节「`toolDeny` 一个字节不动、不点名」;只影响从 7.128.x 及更早直升 7.132.0 的 SQL 部署):M17 在改列名那一拍给每一行盖当前文法戳,盖之前先问引擎;`toolDeny` 里有引擎读不出的条目
209
+ (空串、`-deploy`、非 ASCII 名、`(` 前带不可见空白 ……)的行**整行原样留**(同行 `toolAllow` 里该删的条目也不动),起服日志逐行一条 warn `session_rule_row_unreadable_deny`
210
+ (`policyKey` / `sessionId` / `toolDenyUnreadable: [{ entry, reason }]`,`reason` 是引擎读者的原因词),读数行 `one_time_migrations_applied` 多一键 `sessionRuleRowsWithUnreadableDeny`。
211
+ 这样的会话引擎本来就每一次工具调用都拒(第 44 条);不盖戳让引擎对它继续发旧文法通告 `config.session_rules_earlier_grammar`(盖了戳,拒一切照旧、通告却消失)。
212
+ 读得出的行照改照盖戳,与此前逐字相同(`BASH` 这类读得出、只是没有对应工具的 deny 条目不算读不出)。**出路**:按点名的行(`policyKey` ↔ `sessionId`)用 operator 身份 `PUT /v1/sessions/:id/policy`
213
+ 写一份读得出的新值(不带行 rev),或删该会话(删会话即删规则)。**同一行还带退役命令桶**(`commandAllow` / `commandDeny`;该行的 warn 多一键 `retiredBuckets`,另有 `session_rule_row_retired_bucket` 点名同一行)
214
+ 的,会话规则的 `PUT` 读不了这样的行(退役桶读成 `corrupt`,operator 也一样,答 `500`):出路是在库里按新文法改写那一行(`rev` / `gen` 不动)或删会话。这条收据只在改名那一次起服的日志里,请保存;之后起服不再列出,引擎的旧文法通告仍在每一跑发。
215
+ 48. **更正 7.131.0 第 8 条对请求体规则表的陈述:`settings.permissions.deny` / `ask` 上那四族写法 7.129.1 时就已 `422`,7.131.0 在这两张表上新拒的只有大小写孪生**(文字更正;行为不变,升级零动作):
216
+ 7.131.0 第 8 条把请求体 `settings.permissions.deny` / `ask` 与工具名选择器清单并在同一节里写,标题括号里的「7.129.0–7.130.x 只告运维」读起来对它们同样成立,条目清单也像是一并新拒 —— 对这两张规则表不成立。
217
+ 下表是在 7.129.1 与 7.131.0 的代码上对同一组条目逐条实跑的读数(`deny` 与 `ask` 两桶读数一致;`422` 的句首是 `request settings cannot be applied as written: …`):
218
+
219
+ | 写法(例) | 7.129.1 | 7.131.0 | 7.132.0 |
220
+ | --- | --- | --- | --- |
221
+ | 退役名:`bash` · `Task` · `KillShell` · `MultiEdit` | `422` | `422` | `422` |
222
+ | 协议名写不成现役工具:`figma__x`(无协议前缀)· `mcp__figma__`(缺工具段)· `mcp__git*__x` · `mcp__*` · `a2a__*`(对端段带通配) | `422` | `422` | `422` |
223
+ | 协议名的对端段写成域名形:`mcp__github.com__x`(引擎铸的对端段是 `github_com`) | `422` | `422` | `422` |
224
+ | 内容文法写坏(引擎的规则文法编不出):`Bash(ps` · `Bashps)` · `Bash(echo $HOME)` · `Bash(rm * -rf)` · `Agent(x)`(缺 `param:pattern`;`Agent(agentType:*)` 受理)· `Monitor(touch:*)`(命令规则要写成 `Bash(touch:*)`) | `422` | `422` | `422` |
225
+ | **大小写孪生**:`BASH` · `BASH(*)` · `SENDUSERFILE` · `write` | 受理(名册为空,读不出孪生) | **`422`(7.131.0 新拒)** | `422` |
226
+ | 首尾空白:`" Bash"` · `"mcp__x "`;裸 `*` · `*()` | 受理 | 受理 | `422`(第 43 条) |
227
+
228
+ 所以从 7.129.x–7.130.x 升到 7.131.0,这两张规则表上**唯一**新被拒的是大小写孪生(此前名册是空的,孪生读不出、什么也不拒;7.131.0 起名册是引擎目录加本服务自铸工具名的那一张);前四族早就拒,不是 7.131.0 的行为变化。
229
+ 第 8 条「文法读不出」括号里的首尾空白与 `*`,在这两张规则表上 7.131.0 同样受理,到 7.132.0 才拒(第 43 条)。其余各表(根 / 子代工具名清单、技能清单、`permissions.allow`)不在本条射程。
230
+ **出路**:无需动手。核对自己的请求体时,孪生写现名(`BASH(*)` ⇒ `Bash(*)`),其余按拒句改。
231
+ 49. **hook / 标题腿的直连请求:per-model 凭据绑它第一次发出去的主机**(引擎 7.53.0,安全,行为收紧):直连腿(prompt 型 hook 条目与会话标题)改过引擎一方 brain 的同一只请求门 ——
232
+ 一把 per-model 凭据(`apiKeyEnv` / 托管密钥)第一次随请求发往哪台主机,本进程里它就只认那台;之后同一把凭据要发往另一台主机 ⇒ 拒,失败码 `route_refused`,
233
+ 句首带 `route.credential_origin_mismatch`,一个字节不出门(主路由的 brain 早已如此,直连腿此前只问只读判官、什么都不绑)。同主机换路径不算。
234
+ **谁要动手**:把目录条目的 `baseUrl` 换到另一台主机、凭据引用却没换的部署。**出路**:换主机的同一步换凭据引用(新凭据是新绑定),或重启本服务(绑定只活在进程里)。
235
+ 50. **请求体两处受理面收紧**(引擎 7.53.0):
236
+ - `a2aPeers[i].toolAxes` 的读者是引擎的判官(与 `mcpServers[i].toolAxes` 同一只):每只 skill 的记录里多一个成员(笔误)、出现 `__proto__` 键、不是纯数据 ⇒ 那一条对端整条不挂,
237
+ 运维 warn `a2a_injection_dropped` 点名,任务照跑;此前未知成员静默忽略、照挂。**升级前停驻的任务不受影响**:续跑重放存下的请求体时,一次性迁移 M19(与 7.130.0 节
238
+ `mcpServers[].toolAxes` 同一只、同一个窗)把判官判不过的那一格按 7.131.x 的 A2A 读法重读一次 —— 只多了成员的对端照挂、一行运维 warn `a2a_tool_axes_read_as_before_upgrade`;
239
+ 已知轴写坏(`effect` / `irreversibility` 出词表、`egress` 非布尔、某只 skill 的值不是对象、整份不是对象)的对端 7.131.x 当时就整条丢了,续跑照旧整条不挂(`a2a_injection_dropped` 点名),
240
+ 不会因迁移多挂上;窗关(2026-11-15)之后才续跑的这类停驻行回到「整条不挂」—— 停驻在那条对端 skill 上的审批请在窗内决掉。
241
+ - `mcpServers[i].transport.env` / `transport.headers` 与 `a2aPeers[i].headers` 里名叫 `__proto__` 的键 ⇒ MCP 那两键新提交 `400 request.field_invalid`,拒句点名那个成员
242
+ (`mcpServers[i].transport.headers.__proto__`);A2A 对端整条不挂。此前原样交给引擎,引擎 7.53.0 对这样的声明整单拒、一轮不跑。直接调用请求腿读器的嵌入方:
243
+ `Map`、类实例、`process.env` 作 env / headers 同判(拒句点名那个键;拷成普通对象:`{ ...process.env }`);不可枚举的自有成员也是成员(与引擎同读)。
244
+ **续跑重放存量体**(MCP 两键与 A2A `headers` 同一只读法;存体是 JSON 铸出来的,等价只对 JSON 存体成立 —— 7.131.x 的判据对带自有串属性的类实例也收,上一句的更严处置对直接递对象的嵌入方生效):复现 7.131.x —— 记录里有值不是串的成员 ⇒ 照旧不交(MCP 整键不交、server 照挂;A2A 对端照旧不挂);
245
+ 只有 `__proto__` 名的成员坏、其余全是串 ⇒ 剔掉它、其余照交、server / 对端照挂(7.131.x 整张照收;此前同版本续跑 MCP 整张不交、A2A 整条不挂)。
246
+ 真挂上的那一台一行运维 warn:`mcp_transport_members_dropped { server, lost, lostTotal?, note }` / `a2a_peer_headers_members_dropped { peer, lost, note }`,点名没交出去的键或成员,不带值。
247
+ **出路**:按拒句 / warn 点名的那一格改写声明。
248
+ 51. **消费方(壳 / SDK)要跟的 wire 变化**(引擎 7.53.0;键名不变的值变化与加性帧):
249
+ - 任务流新帧 `compaction_started { trigger, pass, startedAt }`(live 流与 `GET /v1/runs/:id/events`;trace 流不投):对帧类型穷尽分派的消费方加一臂;
250
+ 「压缩中」从它起,到同一个 `pass` 的 `compacted` / `compaction_outcome` 收(两只结束帧多一个可选 `pass`),不按「下一只 `compacted`」收。
251
+ - 通告 `config.session_rules_earlier_grammar` 进会话流(白名单 41 码,契约附录 D.3);从运维日志 `runner_config_advisory` 读它的不再读得到。
252
+ - `permission.rule_row_dropped` 的 `detail.rows[].rule` 是规则原串(至多 1 024 码点 + 截断记号,控制字符随行):渲染前自己中和,同 `entries[].entry`。
253
+ - MCP 重拨回执的 `toolCount` / `added` / `removed` / `toolNames` 与 `wiring_manifest.mcp[].toolCount` 是 run 名册的读数(阀门与部署工具面覆盖之后,`toolCount === toolNames.length`),
254
+ 不是服务器清单;服务器清单读 `POST /v1/capabilities/mcp/probe`。
255
+ - 续跑拒(409)的 `blockedBy` 闭集 9 → 40 词,`field` 多 `deps`(修部署后同一张卡再决),`reason` 多 `unknown_model_ref` / `not_a_model_ref`:把这几位钉成闭集的消费方加词。
256
+ - `config.user_attachment_invalid` 的终局 `terminal.detail` = `{ kind: "coded", detail: { path, index? } }`,同步腿 400 体多一键 `detail`(同一份 `{ path, index? }`):
257
+ `index` 在场 ⇔ 句子点名一只附件,可据它指向那一条。
258
+ - Artifact 工具的审批卡(引擎 7.53.0):带规则席的腿上卡带 `ruleOffers: [{ kind: "single", rule: "Artifact(action:<词>)", match: "name", command: "<词>" }]`,
259
+ 或在强制问等门下带一个 `ruleOffersAbsence` 词;此前两席都缺席。渲染审批卡的消费方(cli / client-core / web-client / sdk)照 Skill 卡那样渲这一条「不再问」,
260
+ 兑付时 `command` 取 offer 的 `command`;把 Artifact 卡当「没有 offer」的界面要改。
261
+ - 提交口库墙门的 `413 request.payload_too_large` 体多一键 `wallSource`(只看「拒它的那堵墙的数字是不是库自己报的」):`"reported"` = 库报的墙(句子点名库的上限与旋钮,缩小或请运维抬);
262
+ `"assumed"` = 不是库报的、按最保守的缺省墙拒 —— 引擎事实还没读到,或事实在场但这一堵墙读失败退了文档缺省(TiDB < 7.6 又无 `CONFIG` 权限读不到单行上限;`max_allowed_packet` 读不出)
263
+ (句子说「the database's … limit was not read … retry shortly; if it persists, ask the operator …」;事实没读到的窗口里稍后重试按库报的墙重判,读失败退缺省的要运维让服务读得到那个上限)——
264
+ 按它分「先重试 / 请运维」与「缩小」,不解析句子。`GET /v1/diagnostics/wiring` 的 `sqlEngine` 段多一键 `wallSources`(两堵墙各自的出处,operator 面,加性)。
265
+ `PUT /v1/sessions/:id/sync/blobs/:hash` 超帽的 `413 blob_too_large_for_sql` 同样带 `wallSource`,词多一个 `"operator"`(帽是运维设的 `SNAPSHOT_BLOB_SQL_MAX_BYTES`),句子按三形分。
266
+ 52. **中心提示层只有一种 wire 形:`{ schemaVersion: 2, artifact }`(BREAKING;settings-schema 14.0.0 + 引擎 7.53.0)**。中心下发的 `prompts` 键与本地 `config.d/prompts.json` 读出的中心层,
267
+ 判官只有一只:契约包的 `EffectivePromptsWire` 先过,再交引擎 `verifyPromptArtifact`(artifact 是引擎当期 schema 3)。下面这些 ⇒ **整份候选拒**(一个域都不应用,停在上一份好配置 / LKG / env),
268
+ `config_candidate_rejected` 点名 `prompts`,`errors` 是契约包 / 引擎的原句:
269
+ - 13.x 的两轴 wire(`{ schemaVersion: 1, declarations?, catalog? }`)—— 句:`prompts wire schemaVersion: this contract carries {schemaVersion: 2, artifact} only — the declarations / catalog axes were retired in settings-schema 14.0.0; the center serves its center artifact (upgrade the center)`;
270
+ - 外壳对、内层是 v2 artifact —— 句:`artifact payload schemaVersion: this contract carries schemaVersion 3 only — rebuild the artifact with the engine's buildPromptArtifact (an overlay's "scope": {"binding": b} becomes "when": {"binding": {"anyOf": [b]}})`;
271
+ - 同一目标一条无条件 `replace` 与一条按场景的 `replace`(可同真)—— 引擎句:`overlays "<p>" and "<q>" both replace "<target>" under one condition that can hold in the same run (write exclusive binding conditions: "noneOf": [x] beside "anyOf": [x])`;
272
+ - digest 与载荷对不上;**存储原文**放上 wire(`{ artifact }` / 13.x 的物化底座 `{ bindings: [] }` / `{}`);`null`。
273
+ **生效时序**:新会话即生效;在飞会话钉着首跑时的 artifact,到压缩 / 新会话才换(引擎按会话钉住首跑时的中心层,续跑按 digest 解析那一份)—— 发布之后已在跑的会话不立刻换。
274
+ 中心不带 `prompts` 键 = 不下发中心层,已采纳的中心层撤下(与此前同)。此前边界上的三只归一臂(两轴形 / 平铺三键 / 存储形投影 `catalog`)删除 —— 不带 worker 的拉取发来的存储原文不再被当作中心层收下。
275
+ **部署序**:web-admin(中心)与本服务**同一维护窗**升级,**先 web-admin 后本服务**;回滚**先 web-admin 后本服务**。窗内两端互拒对方的形(新服务收到旧中心的两轴 wire 或不带 worker 的拉取里的
276
+ 存储原文 / 旧服务收到新中心的 v2 外壳 ⇒ 都是整份候选拒,停在 LKG,刷新拍持续点名到另一端升级)。中心须在本服务升级前把中心 artifact 全部重发成 schema 3。
277
+ **本地 `config.d/prompts.json`**:形是 `{ artifact?, workers? }`(`artifact` = 中心编辑器导出的 artifact 身份 `{ payload, artifactDigest }`);本地腿没有 worker 身份 ⇒ 恒读 `artifact`,
278
+ 发布出处记 `local:r<配置版本>`。还带 `bindings` / `catalog` 的文件被契约包的墓碑拒(整份本地候选拒,句点名一次性迁移 SS-M10)⇒ 跑 `npx @sema-agent/settings-schema migrate <config.d 目录>`
279
+ (空的 `bindings: []` 删掉;非空的 `bindings` 本地从来没生效过,删掉;`catalog` 从中心重新导出)。
280
+ **运维日志**:`center_prompts_adopted` 只带 `phase` / `artifactDigest` / `sourceRevision` / `note`(`axes` / `packId` / `contentDigest` / `sections` / `scenarioOverrides` / `catalogDigest` 删);
281
+ 中心源启动恢复读到一份引擎拒的 / 读不出的副本 ⇒ 一行 `prompt_composition_warning`(`classification: "center-source-restore"`,句子说是哪份、为什么、之后怎样)。
282
+ 53. **钉着 v2 artifact digest 的会话没有退路**(引擎 7.53.0 原话):`a session whose record pins a v2 digest fails prompt.snapshot_unavailable on its next run, the failure's sentence and one operator line saying the copy is refused and how to republish it (no converter, no dual read)`;
283
+ `Publish v3 only and never put a v2 artifact into production — a session pinned to a v2 digest has no way back.` 7.131.x 的引擎读写的是 v1(第 17 条那一族,由中心源三态处置),v2 从未上线过的部署这一族为空。
284
+ **谁要动手**:曾在生产里发布过 v2 artifact 的中心。**出路**:只发 v3;撞上的会话重新开一只。
285
+ 54. **场景名写进 `promptBinding`**(每个请求;HTTP 腿与 `run-local` 同一写点):中心 artifact 里按场景写的段用 `when: { binding: { anyOf: ["<场景名>"] } }`(或 `noneOf`)表达,
286
+ 一条通用的 `replace` 旁边要写场景的那一条时,通用那条写 `noneOf: ["<场景名>"]`、场景那条写 `anyOf`(否则第 52 条第三形整拒)。委派子代经父 spec 座继承同一个场景名。
287
+ 55. **品牌部署的品牌行**(`CONFIG_PROVIDER=local` 与 `run-local`):`default` / `code` 两场景模型收到的 role 层以品牌行开头,字节与 7.131.0 相同;改由部署座一条 overlay 承载 ——
288
+ 清单 `prompt_assembled` 的 role 档多一段 `sema/identity`(排在 `core/role.base` 之前)。`code-review` / `scan` / 中心声明的场景与委派子代不带。按段 id 统计的消费方注意这一段。
289
+ 56. **「本会话没有持久记忆」由引擎判、引擎渲染**(零配置):目录条目 `core/memory.no-persistent-store`,条件 = 记忆面没挂 ∧ 名册不能持久(部署声明 `MEMORY_PERSISTENCE_CAPABLE` 优先,
290
+ 否则引擎按阀门留下的名册推断:本机可写的 `Write` / `Edit` / shell 才算)∧ 不是委派子代。本服务的每请求判别式退役;`MEMORY_PERSISTENCE_CAPABLE=true` 照旧撤回披露,host 车道的自动声明照旧。
291
+ **行为变化**:委派子代不再带这段披露。**谁受伤**:挂了 MCP 记忆服务器(写工具不带 `pathTarget` 面)而本机没挂记忆面的部署 —— 引擎照样说「没有持久记忆」(与 7.131.x 的判别同判)。
292
+ **出路**:`MEMORY_PERSISTENCE_CAPABLE=true`,或给那只写工具声明 `toolFaces.<tool>.pathTarget`(`access: "create"`,仅本机手带时有效)。
293
+ 57. **盘上 LKG 信封 `formatVersion` 5**(载荷的 `prompts` 是第 52 条那一种形):升级后第一次读回 4(及更早)—— 载荷不带 `prompts` ⇒ 照读,按 5 写回,一行 `config_lkg_migrated`;
294
+ 带 `prompts`(13.x 的两轴 wire)⇒ 不读回(`config_lkg_unusable` 点名 formatVersion 与 prompts),中心可达照拉、断着 env 兜底。回滚到 7.131.x 读 5 ⇒ `unknown formatVersion 5`(不可用、响亮)。
295
+ 58. **`Artifact(action:<词>)` 是一条规则**(引擎 7.53.0;行为变化,放宽 + 收紧各一面):`<词>` 取 Artifact 工具声明的动作集(精确词,不收 `*`),请求 `settings.permissions` 的
296
+ deny / ask / allow 与引擎的规则店同一读法。**限制侧**(deny / ask):7.131.x 读成「内容形工具带了内容」(`content_unsupported`)⇒ 受理口 400;现在受理并生效(deny 拒那一个动作、ask 问)。
297
+ **放行侧**(allow):7.131.x 受理但什么都不放(读成空);现在**生效** —— 一条 allow 清掉那一个动作的普通问(从不消音强制问)⇒ 升级后存量请求 / 设置里的这类 allow 条目开始放行(权限面放宽)。
298
+ Artifact 审批卡随之带 `ruleOffers`(见消费方 wire 变化那一条)。**谁要动手**:把 allow 侧这类条目的惰性当成保护的部署 —— 升级前核自己的 allow 表,不想放行的删掉;
299
+ 集外词 / 通配照旧读不成规则(限制侧 400、放行侧读成空)。
300
+ 59. **嵌入方(自建 `ServiceDeps` 的进程):`snapshotBlobSqlCapBytes`(数字)→ `snapshotBlobSqlCap`(读口 `() => { capBytes, source } | undefined`)**(包根导出型 `ServiceKnobDeps` 的座;平铺形与 `knobs` 组同判)。
301
+ 本服务每个 `PUT /v1/sessions/:id/sync/blobs/:hash` 调一次它、按答案前置 413(`undefined` = 此刻没有帽)—— 门限随库报的墙走,不再是起服时算好的一个数;`source` 说帽从哪来
302
+ (`"operator"` = 你自己设的固定帽 / `"reported"` = 由库报的墙推出 / `"assumed"` = 库的上限没读到、按最保守的缺省墙推出),413 的句子与体上的机读位 `wallSource` 同词分三形。
303
+ **改法**:固定帽 `snapshotBlobSqlCapBytes: N` → `snapshotBlobSqlCap: () => ({ capBytes: N, source: "operator" })`;按你的库事实现算的函数答对应的 `source`;不需要前置 413 就不传。
304
+ 照旧传 `snapshotBlobSqlCapBytes` ⇒ `createHttpServer` **起服拒**,拒句点名新键与新形(TypeScript 嵌入方在编译期就红);`snapshotBlobSqlCap` 不是函数 ⇒ 同样起服拒。没有兼容臂:数字不会被当成固定帽收下。
305
+ 60. **`READ_DENY_BUILTIN_EXCLUDE` 里写 `.npmrc` / `.pypirc` ⇒ 起服拒**(引擎 7.52.0;部署面 BREAKING):内建读拒表 `credentials` 档里这两行换成家锚的 `~/.npmrc` / `~/.pypirc` ——
306
+ 家目录里那一对照旧拒,项目里自带的同名文件在每种权限模式下读得到(读到即交给模型;执行环境没声明家目录时仍拒)。7.131.x 的旧行名不再是表里的行名:server 与 `run-local`
307
+ 起服拒,拒句点名新行名;中心 `readFace` 域发同一值 ⇒ 那一份发布整域拒(`web_admin_read_face_invalid`,读面停在上一份合法姿态)。
308
+ **改法**:删掉这两个值(家目录那一对仍由内建行护着,项目里那一对已不在表里);确要让模型读家目录那一只 ⇒ 改写成 `~/.npmrc` / `~/.pypirc`;
309
+ 要项目里那一对照旧拒 ⇒ 把 `.npmrc` / `.pypirc` 加进 `READ_DENY_PATTERNS`。
310
+ 61. **嵌入方(自建 `ServiceDeps` 的进程):`sqlEngineFacts` → `sqlEngine`**(`ServiceDeps` 平铺形的座):由「只给事实」的读口 `() => SqlEngineFacts | null` 改成本池的引擎读口
311
+ `{ dialect: "tidb" | "pg", facts }` —— `facts` 活读(池重连后随连接初始化刷新),`null` = 事实此刻没建立。提交受理口的库墙门、诊断面 `sqlEngine` 段与能力位 `store.sql`
312
+ 读它(`PUT /sync/blobs` 的前置帽仍是你自己传的 `snapshotBlobSqlCap` 读口,见第 59 条);墙的方言只从这里来(此前受理口另从店后端 `backend.kind` 取方言)。没有 SQL 后端就不传;**SQL 后端在场 ⇒ `sqlEngine` 必填**(你递的 `backend` 交出了 `mysqlPool()` / `pgPool()`,MySQL 协议与 PostgreSQL 同一条律):
313
+ 没给 ⇒ `createHttpServer` **起服拒**,拒句点名 `sqlEngine` 与它的形 —— 席缺席只表示「没有 SQL 后端」,不会被读成「没有墙」而放行超出库墙的体(本服务自己的装配有池就给读口)。
314
+ **改法**:把 `sqlEngineFacts: () => f()` 换成 `sqlEngine: { dialect, get facts() { return f(); } }`(`dialect`:MySQL 协议 = `"tidb"`,PostgreSQL = `"pg"`)。照旧传 `sqlEngineFacts` ⇒
315
+ `createHttpServer` **起服拒**,拒句点名新键与它的形(TypeScript 嵌入方在编译期就红)。没有兼容臂。**`sqlEngine` 的值形也在起服时判**:`{ dialect: "tidb" | "pg", facts }`,`facts` 是 `null` 或带 `maxRowBytes` / `maxPacketBytes` / `wallSources`
316
+ 的引擎事实;只改了键名、值还是 7.131.x 的 `() => facts`(或缺 `facts`、方言词出闭集、事实缺 `wallSources`)⇒ 起服拒,拒句点名键与形(此前:起得来,之后每一次提交 500)。自己手铸引擎事实的嵌入方:事实多了必填的 `wallSources`
317
+ (`{ maxRowBytes, maxPacketBytes }`,各为 `"reported"` | `"assumed"`;读到的 / 按构造没有的墙是 `"reported"`,读失败退缺省的墙是 `"assumed"`)。
318
+
319
+ ## 7.131.0 —— hooks 配置受理收紧(两类此前受理的坏形 400 · 本仓不执行的条目类型整份拒)· 模型能力位 `reasoning` / `vision` 三态(env 缺省与中心回落退役)· settings-schema 13.0.1 精确钉 · 写错的工具名恢复开跑前拒并点名现名(限制侧四类全拒、放行侧只拒近似名;一张名册;拒句有界;续跑按方向分流)
320
+
321
+ 1. **`settings.hooks` 只有一只读者,两类此前受理的坏形现在 `400 request.field_invalid`**(零 DDL):新提交(`POST /v1/tasks` · `/v1/tasks/stream` · `/v1/runs`)里
322
+ ① **未知字段里藏着守卫迹象**(此前未知字段被剥掉、配置照收,守卫就此无声消失),例:
323
+ `{"hooks":{"Stop":[{"hooks":[{"type":"command","command":"true","extra":{"PreToolUse":[{"hooks":[{"type":"command","command":"deny.sh"}]}]}}]}]}}`
324
+ ⇒ `settings.hooks.Stop.0.hooks.0: guard-entry-invalid — …`;**出路**:把守卫写到它该在的位置(`hooks.PreToolUse[…]`),删掉条目里的未知字段;
325
+ ② **JSON 里写出的 `__proto__` 事件键**(此前被跳过,配置读成空对象照收),例:`{"hooks":{"__proto__":[…]}}` ⇒ `settings.hooks.__proto__: unknown-event — …`;**出路**:删掉这个键(它从来没生效过)。
326
+ 拒句按分类器给的**码 + 坐标 + 坏字段**铸(`(field: command)` 点名坏字段);事件名等键段是用户文本,出门前过契约包的脱敏出门形(凭据形不原样回显)。
327
+ **拒句的形变了**:此前是 `settings.hooks: <原样路径>: <zod 句>`,现在是 `settings.hooks.<坐标>: <码> — <释义>`;句子不是契约,按 `errorCode` 与句中的码分派。
328
+ 服务侧上限句(命令长度 / 条目数 / 组数 / http 头)与好配置的读数逐字不变。
329
+ **升级前停驻的任务(一次性迁移 M20,窗 = 本版发布日起 30 天)**:升级前存下的请求体里 `settings.hooks` 若带上面两形之一(升级前的读者把未知字段剥掉 / 跳过 `__proto__` 键照收,同一份里真在拦的 `PreToolUse`
330
+ 钩子一直在拦),续跑时窗内把读者点名的那一块(条目 / 匹配器)里按类型确属未知的键剥掉(升级前的读者对一个条目本来就是剥掉全部未知字段、保留其余)、剩下的**重新过同一只读者**,过了就采纳,守卫照拦;每次重读恰一行运维 warn `hooks_read_as_before_upgrade`
331
+ (点名 sessionId 与被剥的坐标)。只迁这两形:拒因里混进别的(真坏的守卫条目、`hooks` 本身不是对象 …)⇒ 不迁移,整份不采纳。读者点名的字段只在它对**那一块的类型**确属未知时才迁移 —— `input` 只属 `mcp_tool` 条目,落在 `command` 条目上是未知字段;迁移的单位是块:读者点名了某个条目 / 匹配器,就把这一块里按类型确属未知的键一次全剥(契约字段一个不动;升级前的读者对一个条目本来就是剥掉全部未知字段、保留其余),再过同一只读者,剥到读者受理为止;`command` 条目自己的 `command` 字段值坏是契约字段、不迁移;条目没有 `type` 或 `type` 在契约词表外也不迁移;读者点名的键对不上文档也不迁移。重读次数只随嵌套的层增长(匹配器层、条目层、`__proto__` 事件键),不随未知字段个数增长;最后一次剥键之后一定再问读者一次。窗关之后(或不属这两形)⇒ 续跑时这份 hooks 整只不采纳 ——
332
+ **包括其中升级前真在拦的 `PreToolUse` 钩子** —— 记运维 warn `task_settings_deferred`(字段 `hooks`)并计 `fail_open_total{tag="server.hooks.resume-reader-refused"}`(只在 hooks 会被采纳的单用户车道计;
333
+ 计数非零 = 有这类停驻任务在窗关后续跑),任务照常续跑。升级零动作;想提前收口就在窗内把这些停驻任务决掉,或按上面的出路改好重新提交。会撞上窗关那一格的是活过窗的升级前停驻行:发布后才升级的部署、
334
+ 调长了 `APPROVAL_TERMINAL_BACKSTOP_MS` 的部署、滚动升级里旧副本在窗内新写的行。
335
+ 2. **`mcp_tool` 条目(以及将来契约新增、本仓不执行的条目类型)整份拒**:契约包 13.0.0 受理第 5 种条目类型 `mcp_tool`,本仓不执行它。含它的 `settings.hooks`(任何事件,守卫事件尤其)提交时
336
+ `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`;
337
+ 合法的其它条目不会被单独留下。对调用方可见的结果与 7.130.0 一致(同样 400,那时是契约包不认识这一型),变的是拒句的形与它的来源 —— 本仓自己对「会不会跑」表态,而不是借契约包的受理面代答。
338
+ 执行侧原有的「不认识的类型只记运维日志 `hook_entries_skipped`(`reason: type_not_supported`)然后跳过」那一臂已删(契约包放宽后它会让守卫条目无声失效),该日志行不再出现。
339
+ **出路**:用 `command` 条目(命令里自行调用 MCP 工具);这台 server 不跑 `mcp_tool` 条目。
340
+ 3. **模型能力位变三态(缺省退役,BREAKING 默认值)**:`MODEL_REASONING` / `MODEL_VISION`(env 腿)与中心名册条目的 `reasoning` / `vision` 现在是 `true` / `false` / **缺席 = 未声明**,缺席不铸键。
341
+ - 升级前:env `MODEL_REASONING` 未设 ⇒ `true`;`MODEL_VISION` 未设 ⇒ 收图;中心名册条目没写 `reasoning` ⇒ 继承 env 腿的值(缺省 `true`),没写 `vision` ⇒ 读成纯文本。
342
+ - 升级后:以上都是「未声明」。引擎怎么读缺席(引擎 7.51.2):`vision` 缺席 ⇒ 收图(图原样交给模型;带图预检不出信号)—— 对 env 部署与升级前的行为一致;
343
+ `reasoning` 缺席 ⇒ 与显式 `false` 同读,**思考档不随请求发出**。
344
+ - **要保持升级前的行为就显式声明**:需要思考的模型写 env `MODEL_REASONING=true` / 名册条目 `reasoning: true`(这是唯一会让存量部署行为变的一格);中心名册里没写 `vision` 的条目升级前被当纯文本
345
+ (带图请求的图被换成占位句并计数告警),升级后图会原样交给模型 —— 纯文本模型请在条目里写 `vision: false`(env 部署写 `MODEL_VISION=false`)。
346
+ - `GET /v1/models` 行的 `reasoning` / `vision` 两列:声明了给布尔,**未声明 = 键缺席**(env 部署未设这两键时,此前给 `true`);读这两列的消费方把缺席读成「未知」。
347
+ `GET /v1/config/catalog` 的 `MODEL_REASONING` / `MODEL_VISION` 两行不再带 `staticDefault`,改写缺席语义说明。
348
+ 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、无新旋钮、无升级序要求。
349
+ 同批新增的设置源表 / 层准入 / http hook 名单 / MCP 名单判决 / 界面语言片段本版不接。
350
+ 5. **嵌入方(自己装配 `createHttpServer` 的部署)两件硬破坏,tsc 红即告示**:
351
+ - **读口场景门接缝必填**:`createHttpServer` 的核心件由四变五,新增 `scenarioGate`(型 `ScenarioGate`)。用包根导出的同一只工厂铸:`createScenarioGate({ config, scenarios, principalCaps, logger }, "peek")`
352
+ (任务提交腿用的是同一个工厂的 `"enforce"` 腿;`ScenarioGateLeg` / `ScenarioGateSources` 一并导出)。此前没递接缝时三张读口自己退到「点名 404 / 缺席读部署缺省」,那两条回退臂已删 ——
353
+ 读口的场景名只有门一个来源;生产装配(`sema-server` 可执行)一直递着,**只有**自建装配需要动。
354
+ - **模型路由的拒启判在配置源应用之后**:`loadConfig()` 不再在读 env 那一拍判「每条 openai 兼容路由都解析到端点」;判官判的是**将要服务的那张表**(目录被采纳 ⇒ 目录表;否则 env 表)。
355
+ 自建装配在**自己的**配置源(目录 / 中心)应用之后调一次包根导出的 `judgeServedModelRoute(config, { envTableIsPlaceholder })`(有判决就拒启,`reason` 是给运维的原句;严格名册布防时传 `true`,
356
+ 没有名册门传 `false`)—— 与 7.104.0 执行车道不变量搬位同样的形(`assertExecutionLaneInvariants`)。只调 `loadConfig()` 的开发脚本若不调它,路由缺口会在第一次模型调用时才以引擎拒句出现。
357
+ - 包根 `judgeModelRouteGap(env)` 语义不变:它答的是 **env 腿**(没有目录在管时这份 env 起不起得来);目录在场时起服以目录表为准。
358
+ 6. **请求体 `mcpServers[i]` 的每个受理键写坏 ⇒ `400 request.field_invalid`(此前 `200`)**(零 DDL;与 7.130.0 节 `toolAxes` 那一条同形,这回覆盖条目的**每一个**受理键):
359
+ 受理键 = `name` · `transport`(`kind` / `command` / `args` / `env` / `url` / `headers` / `principalHeader` / `assertionHeader`)· `allowTools` · `toolAxes` · `toolFaces` · `contentOrigin` · `elicitation` · `source`。
360
+ 新提交(`POST /v1/tasks` · `/v1/tasks/stream` · `/v1/runs`)里这些键任何一个在场却写坏(如 `"allowTools": "x"`、`"args": "-y pkg"`、`"elicitation": null`、`"source": ""`、超长 server 名),
361
+ 此前一律 `200`:`name` / `transport` 的硬字段 / `allowTools` / `toolFaces` 写坏则这一条 server 整条不挂,`elicitation` / `source` / `transport` 的可选子键写坏则悄悄丢掉那个键、server 照挂;
362
+ 现在整份请求 `400 request.field_invalid`,拒句点名 `mcpServers[i].<键路径>`、server 名与该键判官的原因句,整份不收(不挂一半、不丢声明、不默认)。
363
+ **谁撞**:自己拼 `mcpServers` 的调用方,以及把 `.mcp.json` 投影进请求体的壳 —— 写坏一条,整份提交都 400,不再是「那台 server 没挂」「那个参数没生效」。
364
+ **出路**:按拒句改对那个键,或把它删掉(可选键缺席永远合法,在场的 `null` 不是缺席;`name` 与 `transport` 是必填键,缺席同样 400)。`POST /v1/capabilities/mcp/probe` 对同一类写坏同样 400(码相同,拒句措辞另有,拒之前零拨号),上线前可以先拿它核一遍。
365
+ 不变:耐久 resume 重放升级前存下的请求体时仍不拒(拒会把升级前停驻的任务永久卡死)—— 写坏的键照旧点名丢弃 / 丢键;条目本身不是对象、词表外的键、第 33 条起的超上限尾项不在这道门的射程。升级零动作。
366
+ 7. **嵌入方:`Model.reasoning` / `Model.input` 在运行期可以缺席,而引擎 7.51.2 的 `Model` 型面仍把这两键写成必填**(只对读 `config.model` / `config.models[…]` 的嵌入方可见,tsc 不会报):
367
+ 第 3 条之后,env 部署不设 `MODEL_REASONING` / `MODEL_VISION`、中心名册条目不写 `reasoning` / `vision` 时,铸出的 `Model` 上**没有**对应的键 —— `reasoning` 缺席,`vision` 对应的 `input` 缺席。
368
+ 自带代码里 `config.model.input.includes("image")` 这类读法照旧编译通过,运行期对缺席的 `input` 抛 `TypeError`;`config.model.reasoning` 缺席读到 `undefined`,按假值走。
369
+ **出路**:升级后先判缺席再读 —— `config.model.input?.includes("image")`、`config.model.reasoning === true`;缺席 = 未声明,不等于 `false`(引擎对缺席的读法见第 3 条)。型面放宽候引擎下一版,届时 tsc 才会替你报这类读法;在那之前请自查。
370
+ 8. **写错的工具名恢复开跑前拒并点名现名:限制侧四类全拒,放行侧只拒近似名(行为变化 · BREAKING:此前照跑的请求现在 400 / 422;7.129.0–7.130.x 只告运维)**:
371
+ - **限制侧**(根 run 的 `excludeTools`、子代定义的 `agents[i].denyTools`、请求自带技能的 `disallowedTools`、请求体 `settings.permissions.deny` / `ask`)里,引擎读成空的条目 ——
372
+ **近似名**(`BASH`)· **退役名**(`bash` · `Task` ⇒ `Agent`)· **文法读不出**(括号不配、首尾空白、空串、协议名段铸不出)· `*` · 规则表上不带内容形的工具带内容 —— 提交时拒,句子点名条目、原因词与该写的拼法,例:
373
+ `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`。
374
+ 码沿用各表此前的码:子代 `denyTools` ⇒ `400 config.agent.unknown_tool`(附录 A 新入册)· 根 `excludeTools` ⇒ `400 config.tool_face_control` · 技能 `disallowedTools` ⇒ `400 request.field_invalid`(不变)·
375
+ 请求体 `permissions.deny` / `ask` ⇒ `422`(该层既有的收紧表拒形,`request settings cannot be applied as written: …`,改法带上条目其余部分:`BASH(*)` ⇒ 写 `Bash(*)`)。
376
+ 理由:限制侧读成空 = 静默放行 —— 写了 `denyTools: ["BASH"]` 以为禁了 `Bash`,子代照用,用户面零点名。
377
+ - **放行侧只拒近似名**(请求体 `settings.permissions.allow`、根 / 子代 `allowTools`、技能 `allowedTools`):**近似名**(`BASH`)—— 现名唯一、确定是笔误,想放 `Bash` 却什么也没放(静默收窄)。
378
+ 码:根 `allowTools` ⇒ `400 config.tool_face_control`,子代 `allowTools` ⇒ `400 config.agent.unknown_tool`,`permissions.allow` / 技能 `allowedTools` ⇒ `400 request.field_invalid`。
379
+ **退役名**(`Task` · `MultiEdit` · `bash`)、**内容形**(`Agent(Explore)` 等)、**文法读不出**(`Bash(echo $HOME)` 里的 `$VAR`、带空白的名字、括号不配)、**协议名对端段通配**(`mcp__*` · `mcp__git*__x` · `a2a__*`)在放行侧**照 7.130.0 受理、交引擎既有通告**(壳转投的 CC 用户 settings 里这些极常见,尤其内容形带 `$VAR` 的 `Bash(echo $HOME)`,拒了 = 每次提交 400;不要为它改请求):引擎侧的终形是把退役名归一到现名并在用户面点名,
380
+ 到货前放行侧不拒;文法读不出的放行条目授不出去 = 收窄而不是放行:规则表(`permissions.allow` / 技能 `allowedTools`)上受理口不拒、引擎开跑时告运维;选择器清单(根 / 子代 `allowTools`)上受理口不拒、引擎开跑前自己拒(子代 ⇒ 终局 `failed` 带 `config.agent.unknown_tool`,同步腿 HTTP 200 + failed 体;根 ⇒ `400 config.tool_face_control`)。限制侧同样的退役名 / 文法读不出照旧拒(`excludeTools: ["Task"]` ⇒ 400)。`settings.permissions.allowLayer` 能力位语义不变。
381
+ - **真未知名**(拼写合法、没有近似也没有退役对应 —— 懒挂载的合法形,如一台之后才连上的 MCP 服务器的工具)⇒ **照旧**保留、运维行 `runner_config_advisory` 告知一次,不拒(7.129.0 第 5 条对这一类仍成立)。
382
+ - **选择器只取条目里工具名那一段再判名字**(根 / 子代的 `allowTools` / `excludeTools` / `denyTools`):限制侧 `Tool(content)` 的内容不读(引擎读成整只工具 —— `Agent(x)`、`Write(*)`、`Bash(npm:*)`、`Bash(echo $HOME)` 不拒);
383
+ `allowTools:["*"]`(本层不设名单)与限制侧整个协议的拒 `mcp__*` / `a2a__*` 是合法写法。规则表(`settings.permissions.*`、技能清单)里整条是规则:内容的文法读不出(`Bash(echo $HOME)`)是 `grammar`:限制侧拒,放行侧照收。
384
+ - **名册只有一张 = 引擎目录的全部工具名 ∪ 本服务自己铸给模型的工具名**(`Now` / `SendUserFile` / `SelectEnvironment` / `repo_tree` / `repo_read_file` / `repo_pull_diff`,名字取自铸它们的那一处):每一处读条目名的地方传同一张 ——
385
+ 工具名清单、请求体 `settings.permissions.*`、技能清单、运维 `APPROVAL_REQUIRE` / `APPROVAL_DENY` / `APPROVAL_NEVER_AUTO` / `APPROVAL_GRANT`、中心 `governance.approvalRequire`。此前后几处用空名册,读不出孪生(`BASH(*)` 照收、什么也不拒)。
386
+ **运维名单里写着孪生名字的部署升级后拒启**(`APPROVAL_REQUIRE=write` 要写成 `Write` —— 名表逐字比对,孪生那道门本来就没生效);中心下发的 `approvalRequire` 含孪生 ⇒ 整组被拒、活配置保前值(世代账 `approvalGate.lastRejected`、一条 warn 点名坏条目)。
387
+ 技能 `disallowedTools:["BASH"]` 这类孪生此前受理口收下、引擎在技能激活时才拒,现在提交即 `400 request.field_invalid`。嵌入方自己另挂的宿主工具不在名册里,它们的孪生仍只由引擎在开跑时告知运维。
388
+ - **拒句与扫描有界**:清单先过条数帽(与其它条目表同一个数 1000,`allowLayerEntryCountIssue`):超帽 ⇒ **整只** `400 request.field_invalid`(体带 `field` / `reason: "over_cap"` / `count` / `cap`),不逐条读;
389
+ 帽内的坏条目拒句只点名前 5 条,其余写成 `; and K more`。根 `allowTools` / `excludeTools` 此前没有条数帽 —— 写了上千条的调用方现在 400(合并成更宽的条目,或精简)。
390
+ - **升级前停驻的任务 · 条数帽不分方向、其余按方向分流**:升级前存下的请求体带上述条目(7.129.0–7.130.x 受理),续跑时:
391
+ **超帽的清单(任一方向)** ⇒ 认领之前 `409 resume_blocked_by_policy`,`blockedBy: request.field_invalid`,句子点名 `count` / `cap`;存体不改写、不进引擎(条数帽是资源帽 —— 引擎每次准备都逐条播种这些表 —— 不是语义 inert,
392
+ 放行方向的超帽存体也 409;**升级前停驻存体的限制侧清单超 1000 条同样 409**)。帽内:
393
+ **限制方向**(`excludeTools` · 子代 `denyTools` · 技能 `disallowedTools` · `settings.permissions.deny` / `ask`)读成空 ⇒ 认领之前 `409`,`blockedBy` = 该层的码(`permissions.deny` / `ask` 是 `request.unprocessable`),`error` = 同一句;
394
+ 行 / 卡 / ctx 不动(放宽方向不能默认:那条限制从没生效过)。出路 = 取消该任务、把条目改成现名(拒句里 `write "Bash"` 就是该写的)后重新提交。plan_review 的一次性 nonce 在这一拒之后仍未消费,终态 steer 不铸唤醒卡,SLA 拒扫跳过这条会话。
395
+ **放行方向**(`allowTools` · 子代 `allowTools` · `settings.permissions.allow` · 技能 `allowedTools`)读成空(近似名)⇒ **照旧续跑**,条目原样留着、仍然什么也没放(升级前它本就这样跑着,继续是安全方向),不 409;
396
+ 每次重建一行运维 warn `stored_allow_list_inert_resumed`(`sessionId` · `layer` · 有界的 `sentence`),想让条目真生效就在新提交里改成现名。
397
+ 运维日志三个事件(`resume_refused_stored_body` / `steer_wake_park_refused_stored_body` / `deny_sweep_stored_body_skipped`)的 `kind` 字段值 `skill_deny_inert` 改为 `inert_tool_list`(按旧值配了告警的部署改配;只在 409 / 跳过上出现),
398
+ `layer` 取 `skills` / `agents.skills` / `skills.allowedTools` / `agents.skills.allowedTools` / `agents.denyTools` / `agents.allowTools` / `excludeTools` / `allowTools` / `settings.permissions.allow` / `settings.permissions.deny` / `settings.permissions.ask`。
399
+ 随包 `scripts/drain-check-local.mjs` 的 `skill-deny-inert` 判据与随包 SQL 两段仍只数技能拒侧两层,**不覆盖**其余清单;想在升级前找这类停驻任务,按 `checkpoint_ctx.ctx` 里的 `body.allowTools` / `body.excludeTools` /
400
+ `body.agents[*].allowTools` / `body.agents[*].denyTools` / `body.settings.permissions` 找。
401
+ - **三问** — **谁需要**:写限制侧清单的调用方,尤其把工具名写成 `BASH` / `bash` / `Task` 的(以为禁了、其实没禁);放行侧写成 `BASH` 的(以为放了、其实没放);**谁受伤**:此前照跑的这类请求现在 400 / 422(它们的清单本来就没按字面生效);
402
+ 运维 `APPROVAL_*` 里写着孪生名字的部署升级后拒启;升级前停驻的限制方向带此类条目(或任一方向超帽)的任务续跑 409,放行方向的照旧续跑 + 一行 warn;写上千条的根清单现在 400;
403
+ **补偿**:拒句点名条目、原因词与该写的拼法,改成现名即过;放行侧的退役名 / 内容形 / 文法读不出 / 对端段通配、真未知名不受影响;无旋钮。
404
+ - **受理面只判本次请求送来的 `agents` 与规则表,上一轮的不带入**:引擎每个 run 按本次 spec 重建委派工具(不带 `agents` 的 run 只看到部署级定义);子代的保留(retain)是进程内存里的账 —— 按 run 腿建、腿收尾释放(失败子代另有按会话键的 15 分钟账),不落盘、不跨升级;升级重启后不存在带旧定义继续跑的子代。
405
+
10
406
  ## 7.130.0 —— 请求体 `mcpServers[].toolAxes` 坏形 400(含显式 `null`)· 升级前停驻任务的一次性迁移 M19 · 会话标题 / hooks 选线随主请求 · 技能 frontmatter 收尾只认整行 `---`
11
407
 
12
408
  1. **请求体 `mcpServers[].toolAxes` 与部署声明读同一只判官**(零 DDL):新提交(`POST /v1/tasks` · `/v1/tasks/stream` · `/v1/runs`)里坏形的 `toolAxes`
package/README.md CHANGED
@@ -40,6 +40,10 @@ model gateways, and cloud agent execution behind an HTTP/SSE contract.
40
40
  sessions and long-term memory, multi-scenario assembly from one image, per-task sub-agents,
41
41
  deterministic workflow orchestration, and pluggable sandbox execution lanes
42
42
  (`host`, `local-docker`, `e2b`, `k8s`, `ssh`, `adb`).
43
+ - **One center prompt layer.** A center-published (or local `config.d`) prompt layer has one wire form,
44
+ `{ schemaVersion: 2, artifact }` (an engine-built artifact): it is checked by the settings contract and then the
45
+ engine's verifier, adopted by the engine, and targets scenarios through `when.binding` (the server writes each
46
+ request's scenario name into `TaskSpec.promptBinding`). See USAGE §9.5f.
43
47
 
44
48
  **It is not:**
45
49
 
@@ -133,9 +137,12 @@ curl -s localhost:8090/v1/tasks -H "Authorization: Bearer <SERVICE_AUTH_TOKEN>"
133
137
  model-catalog entry `headers` and the API line that reads each name. Both are frozen, the very objects the server judges with.
134
138
  A shell or package that must know **before the engine starts** whether the model route would stop it imports `judgeModelRouteGap(env)`
135
139
  (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 is the very function `loadConfig()` judges the model route with — not a copy of it —
137
- so its reading and the start-up refusal are the same by construction. `none` says the route gap is absent, not that the engine will start
138
- (other invariants refuse with their own codes).
140
+ 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
141
+ 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.
142
+ 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
143
+ 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).
144
+ The start-up's own route judge runs **after** the catalog is applied, not in `loadConfig()`: a hand-built assembly calls `judgeServedModelRoute(config, { envTableIsPlaceholder })`
145
+ (package root) once after its own config-source apply, like `composeExecutionLane`.
139
146
  - **Bundled binaries**: the package ships two `bin` entries — `run-local` (single-machine local
140
147
  runner) and `sema-up` (deployment bootstrap script).
141
148
  - `run-local` boots the same engine in-process, runs one task from the CLI objective, prints the
@@ -182,17 +189,17 @@ The server is configured entirely through environment variables. The most import
182
189
 
183
190
  | Variable | Default | What it does |
184
191
  |----------|---------|--------------|
185
- | `PORT` | `8090` | HTTP listen port |
192
+ | `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
193
  | `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
194
  | `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 |
195
+ | `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
196
  | `MODEL_API_KEY` | — | Gateway API key (optional). Pairs only with `MODEL_GATEWAY_BASEURL`: with no gateway it is not loaded and sent nowhere |
190
197
  | `SERVICE_AUTH_TOKEN` | — | Callers must send `Authorization: Bearer <token>` |
191
198
  | `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 |
192
199
  | `SESSION_BACKEND` | `memory`* | `memory` / `mysql` (durable session center) / `auto`. *Defaults to durable when `DB_BACKEND` is explicitly `mysql`/`pg`. (`tidb` is a RETIRED public name — setting it refuses to boot; since 7.57.0 `GET /v1/config/catalog` also echoes the public word `mysql` instead of the internal `tidb` label) |
193
200
  | `REMOTE_EXEC` | unset | Sandbox execution lane: `host` / `local-docker` / `e2b` / `k8s` / `ssh` / `adb`; unset = in-process stub (with `CONFIG_PROVIDER=local` the default becomes `host`). Naming a lane without its required env (e.g. `e2b` without `E2B_API_KEY`) or an unrecognized value **refuses to start** — no silent downgrade to the host/in-process lane (fail-closed). Under `CONFIG_PROVIDER=local` a `config.d/remote-exec.json` **wins over** this env (deliberate: the file is the single-machine source of truth). Since 7.57.0 that preemption is **loud** — `remote_exec_lane_preempted_by_file` names the preempted lane, the effective one, and whether it is an **isolation downgrade** (an isolated lane replaced by a non-isolated one); a file naming a not-yet-wired isolated lane (`e2b`/`k8s`/`ssh`/`adb`) leaves the env lane in force and says so |
194
201
  | `SSH_HOST_FINGERPRINT` / `SSH_KNOWN_HOSTS` | unset | **SSH lane host-key verification** (`REMOTE_EXEC=ssh` only). `SSH_HOST_FINGERPRINT` = the host key's SHA256 base64 fingerprint (`SHA256:` prefix and `=` padding both optional); `SSH_KNOWN_HOSTS` = a path to a `known_hosts` file, matched literally on `host` / `[host]:port`. **Either one present ⇒ strict verification, a mismatch REFUSES the connection** (the error carries both fingerprints plus an `ssh-keyscan` line); both set ⇒ both must pass. **Both unset ⇒ a loud per-connection warning (`ssh_host_key_unverified`, man-in-the-middle risk) and the connection proceeds** — the deliberate posture for the batch-deploy lane, registered as a P-DEBT fail-open (`docs/FAIL-OPEN-CENSUS.md`). A malformed fingerprint or an unreadable `known_hosts` path **refuses to start**. Not supported in `known_hosts`, on purpose: hashed (`\|1\|…`) lines, wildcard patterns, `@cert-authority` — all skipped, so a host whose only entries are those is REFUSED (`@revoked` is honored and wins over any plain line, whatever the order) |
195
- | `HOOKS_TIMEOUT_MS` | unset (engine default 600000) | Per-invocation time bound for every hook seat (core `Hooks.timeoutMs`). Unset = the engine's own default (this server does not copy upstream defaults). `0` is honored as written (every seat expires immediately). A non-integer, negative, or above-`setTimeout`-ceiling (2147483647) value **refuses to start** |
202
+ | `HOOKS_TIMEOUT_MS` | unset (600000) | Time budget of each handler of the request `settings.hooks` registration (one event's run of the request's hook entries). Unset = 600000 ms — the bound this server runs hook processes under (one command's own timeout caps at 600 s); the engine's default for an extension (10 s) is not used. `0` is honored as written (a zero budget). The server's own two observer registrations (deny meter, tool trace) do not read it. A non-integer, negative, or above-`setTimeout`-ceiling (2147483647) value **refuses to start** |
196
203
  | `CONFIG_PROVIDER` | unset | Config source: `local` (file-backed `config.d/`, single machine) / `remote` (registry control plane). Closed set, matched exactly (7.104.0+): any other value (wrong case, surrounding spaces) refuses to boot; empty = unset |
197
204
  | `DEFAULT_SCENARIO` | `code` | Default scenario when the request body names none; must be a scenario this deployment has (boot refuses otherwise) |
198
205
  | `SANDBOX_PKG_SOURCE` | `global` | Package sources inside sandboxes: `global` (official upstreams) / `cn` (China mirrors) / `custom` / `none` |