@sema-agent/server 7.131.0 → 7.133.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 (509) hide show
  1. package/MIGRATION.md +575 -0
  2. package/README.md +12 -9
  3. package/README.zh-CN.md +9 -6
  4. package/USAGE.md +326 -98
  5. package/deploy/sema-up/README.md +19 -1
  6. package/deploy/sema-up/chart/templates/server-v4pro.yaml +5 -2
  7. package/deploy/sema-up/chart/templates/server.yaml +5 -2
  8. package/deploy/sema-up/stack/compose.yaml +3 -0
  9. package/deploy/sema-up/stack/sema-stack-cluster.yaml +4 -2
  10. package/dist/adoption/plan.d.ts +3 -2
  11. package/dist/adoption/plan.js +7 -5
  12. package/dist/adoption/runner.js +3 -10
  13. package/dist/adoption/sql.d.ts +3 -2
  14. package/dist/adoption/sql.js +3 -5
  15. package/dist/adoption/wire.d.ts +4 -1
  16. package/dist/adoption/wire.js +1 -0
  17. package/dist/approval-ask-audit-store.d.ts +31 -13
  18. package/dist/approval-ask-audit-store.js +272 -74
  19. package/dist/approval-ask-machine.d.ts +15 -0
  20. package/dist/approval-ask-machine.js +6 -0
  21. package/dist/approval-ask-retention.d.ts +136 -0
  22. package/dist/approval-ask-retention.js +102 -0
  23. package/dist/approval-card.d.ts +82 -20
  24. package/dist/approval-card.js +46 -16
  25. package/dist/approval-deny-reasons.d.ts +1 -1
  26. package/dist/approval.d.ts +9 -0
  27. package/dist/approval.js +24 -3
  28. package/dist/audit.d.ts +33 -19
  29. package/dist/audit.js +12 -16
  30. package/dist/auth-bridge.d.ts +4 -0
  31. package/dist/auth-bridge.js +10 -11
  32. package/dist/bake-runner/main.js +3 -2
  33. package/dist/boot/deferred-sandbox-path-env.js +1 -1
  34. package/dist/boot/deployment-mods.d.ts +31 -0
  35. package/dist/boot/deployment-mods.js +26 -0
  36. package/dist/boot/execution-env.d.ts +1 -1
  37. package/dist/boot/execution-env.js +27 -12
  38. package/dist/boot/execution-lane.js +11 -0
  39. package/dist/boot/leader.js +2 -0
  40. package/dist/boot/memory-boundary.d.ts +2 -2
  41. package/dist/boot/memory-boundary.js +2 -2
  42. package/dist/boot/memory-consolidation.d.ts +5 -4
  43. package/dist/boot/memory-consolidation.js +7 -6
  44. package/dist/boot/org-delivery.d.ts +28 -0
  45. package/dist/boot/org-delivery.js +20 -0
  46. package/dist/boot/read-deny-anchor-audit.d.ts +17 -0
  47. package/dist/boot/read-deny-anchor-audit.js +36 -0
  48. package/dist/boot/reapers.d.ts +3 -0
  49. package/dist/boot/reapers.js +21 -1
  50. package/dist/boot/resolve-spec.d.ts +4 -5
  51. package/dist/boot/resolve-spec.js +74 -123
  52. package/dist/boot/runner-deps.d.ts +19 -8
  53. package/dist/boot/runner-deps.js +15 -2
  54. package/dist/boot/session-faces.d.ts +2 -7
  55. package/dist/boot/session-faces.js +4 -2
  56. package/dist/boot/stage-02-web-admin-pull.js +4 -1
  57. package/dist/boot/stage-03-store-backend.d.ts +1 -1
  58. package/dist/boot/stage-03-store-backend.js +2 -2
  59. package/dist/boot/stage-04-stores.d.ts +1 -1
  60. package/dist/boot/stage-05-execution-env.d.ts +4 -4
  61. package/dist/boot/stage-05-execution-env.js +7 -10
  62. package/dist/boot/stage-06-runners.d.ts +4 -2
  63. package/dist/boot/stage-06-runners.js +11 -7
  64. package/dist/boot/stage-07-capability-layer.d.ts +4 -2
  65. package/dist/boot/stage-07-capability-layer.js +27 -6
  66. package/dist/boot/stage-08-reapers.d.ts +4 -2
  67. package/dist/boot/stage-08-reapers.js +2 -2
  68. package/dist/boot/stage-09-leader.d.ts +6 -9
  69. package/dist/boot/stage-10-http-server.d.ts +6 -9
  70. package/dist/boot/stage-10-http-server.js +7 -9
  71. package/dist/boot/stores.d.ts +1 -1
  72. package/dist/boot/stores.js +7 -8
  73. package/dist/boot/web-admin.d.ts +4 -3
  74. package/dist/boot/web-admin.js +186 -98
  75. package/dist/boot/workflow-orchestration.d.ts +2 -0
  76. package/dist/boot/workflow-orchestration.js +2 -1
  77. package/dist/brain.js +25 -25
  78. package/dist/breaker-key.d.ts +6 -0
  79. package/dist/breaker-key.js +29 -0
  80. package/dist/budget.js +5 -4
  81. package/dist/caller-text-echo.d.ts +23 -0
  82. package/dist/caller-text-echo.js +21 -0
  83. package/dist/capabilities/brand-identity.d.ts +26 -0
  84. package/dist/capabilities/brand-identity.js +27 -0
  85. package/dist/capabilities/center-plugins.d.ts +7 -0
  86. package/dist/capabilities/center-plugins.js +1 -1
  87. package/dist/capabilities/center-prompts.d.ts +32 -59
  88. package/dist/capabilities/center-prompts.js +18 -32
  89. package/dist/capabilities/composite-members.d.ts +1 -1
  90. package/dist/capabilities/composite-members.js +1 -1
  91. package/dist/capabilities/engine-capabilities.js +2 -1
  92. package/dist/capabilities/execution-lane.js +2 -2
  93. package/dist/capabilities/hands-lane.d.ts +30 -6
  94. package/dist/capabilities/hands-lane.js +11 -1
  95. package/dist/capabilities/mcp-probe.d.ts +48 -3
  96. package/dist/capabilities/mcp-probe.js +47 -135
  97. package/dist/capabilities/scenarios.d.ts +95 -45
  98. package/dist/capabilities/scenarios.js +53 -38
  99. package/dist/capabilities/skills.d.ts +19 -4
  100. package/dist/capabilities/skills.js +30 -10
  101. package/dist/capability-facts.d.ts +34 -4
  102. package/dist/capability-facts.js +28 -2
  103. package/dist/center-config-tier.d.ts +69 -0
  104. package/dist/center-config-tier.js +42 -0
  105. package/dist/checkpoint-identity.d.ts +16 -0
  106. package/dist/checkpoint-identity.js +19 -0
  107. package/dist/config-catalog.d.ts +17 -10
  108. package/dist/config-catalog.js +47 -32
  109. package/dist/config-d-migration.d.ts +3 -1
  110. package/dist/config-d-migration.js +2 -2
  111. package/dist/config-dir-trust.d.ts +114 -0
  112. package/dist/config-dir-trust.js +388 -0
  113. package/dist/config-invariants.d.ts +18 -5
  114. package/dist/config-invariants.js +35 -3
  115. package/dist/config-lkg.d.ts +9 -9
  116. package/dist/config-lkg.js +20 -4
  117. package/dist/config-provider.d.ts +9 -2
  118. package/dist/config-provider.js +18 -6
  119. package/dist/config-types.d.ts +75 -20
  120. package/dist/config-types.js +8 -0
  121. package/dist/config.d.ts +13 -0
  122. package/dist/config.js +137 -62
  123. package/dist/delegated-session-owner.d.ts +185 -0
  124. package/dist/delegated-session-owner.js +119 -0
  125. package/dist/device-protocol.d.ts +5 -3
  126. package/dist/device-protocol.js +1 -1
  127. package/dist/device-ws-hub.js +21 -2
  128. package/dist/device-ws-protocol.d.ts +70 -6
  129. package/dist/device-ws-protocol.js +50 -22
  130. package/dist/elicitation.js +3 -2
  131. package/dist/entry-problems.d.ts +34 -0
  132. package/dist/entry-problems.js +14 -0
  133. package/dist/env-name-allowlist-knobs.d.ts +12 -2
  134. package/dist/env-name-allowlist-knobs.js +5 -4
  135. package/dist/exec-bounds.d.ts +15 -3
  136. package/dist/exec-bounds.js +4 -0
  137. package/dist/execution-lane-caps.d.ts +56 -10
  138. package/dist/execution-lane-caps.js +50 -0
  139. package/dist/field-conflict.d.ts +43 -0
  140. package/dist/field-conflict.js +20 -0
  141. package/dist/fleet/fleet-bus.d.ts +80 -6
  142. package/dist/fleet/fleet-bus.js +41 -9
  143. package/dist/hooks/classic-stations.d.ts +91 -0
  144. package/dist/hooks/classic-stations.js +27 -0
  145. package/dist/hooks/command-spawn.d.ts +57 -0
  146. package/dist/hooks/command-spawn.js +139 -0
  147. package/dist/hooks/hook-llm.d.ts +8 -5
  148. package/dist/hooks/hook-llm.js +79 -36
  149. package/dist/hooks/hook-runner.d.ts +349 -95
  150. package/dist/hooks/hook-runner.js +855 -568
  151. package/dist/hooks/hook-source.d.ts +44 -0
  152. package/dist/hooks/hook-source.js +40 -0
  153. package/dist/http/active-run-conflict.d.ts +5 -13
  154. package/dist/http/active-run-conflict.js +17 -27
  155. package/dist/http/admission.d.ts +8 -2
  156. package/dist/http/admission.js +188 -101
  157. package/dist/http/approval-sweeps.js +99 -21
  158. package/dist/http/caller-trust.d.ts +77 -0
  159. package/dist/http/caller-trust.js +143 -0
  160. package/dist/http/checkpoint-ctx.d.ts +41 -24
  161. package/dist/http/checkpoint-ctx.js +23 -16
  162. package/dist/http/claimed-run.d.ts +14 -0
  163. package/dist/http/claimed-run.js +15 -0
  164. package/dist/http/device-placement.d.ts +2 -4
  165. package/dist/http/device-placement.js +7 -11
  166. package/dist/http/dispatch.d.ts +0 -4
  167. package/dist/http/dispatch.js +15 -13
  168. package/dist/http/durable-run-leg.d.ts +6 -33
  169. package/dist/http/durable-run-leg.js +36 -10
  170. package/dist/http/health-self-report.d.ts +7 -5
  171. package/dist/http/health-self-report.js +4 -18
  172. package/dist/http/owner-access.d.ts +56 -0
  173. package/dist/http/owner-access.js +42 -0
  174. package/dist/http/principal-gate.d.ts +70 -28
  175. package/dist/http/principal-gate.js +61 -15
  176. package/dist/http/readiness.d.ts +11 -5
  177. package/dist/http/readiness.js +10 -3
  178. package/dist/http/refusal-body.d.ts +21 -0
  179. package/dist/http/refusal-body.js +4 -0
  180. package/dist/http/request-guards.d.ts +43 -23
  181. package/dist/http/request-guards.js +53 -17
  182. package/dist/http/resume-ctx-read.d.ts +2 -5
  183. package/dist/http/resume-ctx-read.js +7 -4
  184. package/dist/http/resume-legs.d.ts +28 -4
  185. package/dist/http/resume-legs.js +169 -91
  186. package/dist/http/retired-service-deps.d.ts +43 -0
  187. package/dist/http/retired-service-deps.js +18 -0
  188. package/dist/http/route-ctx.d.ts +44 -28
  189. package/dist/http/route-ctx.js +4 -0
  190. package/dist/http/route-table.js +2 -0
  191. package/dist/http/routes/a2a-serve.js +3 -4
  192. package/dist/http/routes/admin-config-refresh.js +1 -1
  193. package/dist/http/routes/admin-drain.js +1 -1
  194. package/dist/http/routes/admin-memory-optout.js +5 -1
  195. package/dist/http/routes/adoption.js +2 -9
  196. package/dist/http/routes/approvals-assistant.js +49 -48
  197. package/dist/http/routes/background.js +3 -2
  198. package/dist/http/routes/capabilities.js +21 -5
  199. package/dist/http/routes/config-catalog.d.ts +3 -3
  200. package/dist/http/routes/config-catalog.js +1 -1
  201. package/dist/http/routes/devices.js +3 -17
  202. package/dist/http/routes/diagnostics.js +6 -2
  203. package/dist/http/routes/images.js +6 -7
  204. package/dist/http/routes/leader.js +1 -8
  205. package/dist/http/routes/mcp-reconnect.d.ts +1 -1
  206. package/dist/http/routes/mcp-reconnect.js +3 -2
  207. package/dist/http/routes/memory-bundle.js +1 -1
  208. package/dist/http/routes/memory-compliance.js +1 -1
  209. package/dist/http/routes/memory-consolidation.js +1 -1
  210. package/dist/http/routes/memory-origin.js +1 -1
  211. package/dist/http/routes/memory-policy.d.ts +1 -1
  212. package/dist/http/routes/memory-policy.js +3 -2
  213. package/dist/http/routes/notify-wake.js +8 -21
  214. package/dist/http/routes/observability.js +2 -2
  215. package/dist/http/routes/principal-inbox.d.ts +39 -0
  216. package/dist/http/routes/principal-inbox.js +76 -0
  217. package/dist/http/routes/retention-ops.js +2 -9
  218. package/dist/http/routes/rules.js +5 -18
  219. package/dist/http/routes/runs.d.ts +5 -0
  220. package/dist/http/routes/runs.js +34 -107
  221. package/dist/http/routes/session-sync.js +15 -26
  222. package/dist/http/routes/sessions.js +30 -40
  223. package/dist/http/routes/shared-memory.js +2 -13
  224. package/dist/http/routes/side-query.js +8 -7
  225. package/dist/http/routes/tasks.js +67 -21
  226. package/dist/http/routes/workflows.d.ts +351 -0
  227. package/dist/http/routes/workflows.js +153 -119
  228. package/dist/http/send.d.ts +33 -10
  229. package/dist/http/send.js +21 -4
  230. package/dist/http/server.d.ts +27 -18
  231. package/dist/http/server.js +34 -6
  232. package/dist/http/stored-body-replay.js +17 -4
  233. package/dist/http/submit-ctx.d.ts +25 -0
  234. package/dist/http/submit-ctx.js +40 -0
  235. package/dist/http/submit-replay.d.ts +4 -3
  236. package/dist/http/submit-replay.js +5 -4
  237. package/dist/http/wire-types.d.ts +11 -8
  238. package/dist/index.d.ts +2 -2
  239. package/dist/index.js +1 -2
  240. package/dist/lane-home-probe.d.ts +112 -0
  241. package/dist/lane-home-probe.js +101 -0
  242. package/dist/leader/endpoint.d.ts +4 -1
  243. package/dist/leader/endpoint.js +11 -10
  244. package/dist/limit-refusal.d.ts +59 -0
  245. package/dist/limit-refusal.js +15 -0
  246. package/dist/main.js +1 -1
  247. package/dist/mcp-assertion-header.d.ts +32 -19
  248. package/dist/mcp-assertion-header.js +10 -9
  249. package/dist/mcp-tool-faces.d.ts +5 -0
  250. package/dist/mcp-tool-faces.js +6 -4
  251. package/dist/memory-project-layer.d.ts +37 -0
  252. package/dist/memory-project-layer.js +46 -0
  253. package/dist/memory-scope.d.ts +24 -17
  254. package/dist/memory-scope.js +24 -8
  255. package/dist/memory-sync-client.d.ts +27 -13
  256. package/dist/memory-sync-client.js +107 -18
  257. package/dist/model-entry-refusal.d.ts +9 -1
  258. package/dist/model-entry-refusal.js +6 -1
  259. package/dist/model-plane-face.d.ts +51 -0
  260. package/dist/model-plane-face.js +24 -0
  261. package/dist/model-provider.d.ts +44 -12
  262. package/dist/model-provider.js +21 -2
  263. package/dist/model-route-endpoint.d.ts +9 -12
  264. package/dist/model-route-endpoint.js +8 -11
  265. package/dist/model-route-env.d.ts +22 -0
  266. package/dist/model-route-env.js +2 -0
  267. package/dist/model-route-gap.d.ts +60 -30
  268. package/dist/model-route-gap.js +21 -15
  269. package/dist/observability/corrupt-read-seat.d.ts +7 -0
  270. package/dist/observability/corrupt-read-seat.js +9 -0
  271. package/dist/observability/cost-taxonomy.d.ts +7 -7
  272. package/dist/observability/err-text.d.ts +6 -0
  273. package/dist/observability/err-text.js +18 -4
  274. package/dist/observability/fail-open.d.ts +76 -14
  275. package/dist/observability/fail-open.js +76 -14
  276. package/dist/observability/metrics.js +4 -1
  277. package/dist/observability/prompt-manifest.d.ts +33 -82
  278. package/dist/observability/prompt-manifest.js +122 -17
  279. package/dist/observability/run-terminal.js +14 -6
  280. package/dist/observability/secret-env-scrub.d.ts +13 -1
  281. package/dist/observability/secret-env-scrub.js +13 -1
  282. package/dist/observability/tool-trace.d.ts +16 -21
  283. package/dist/observability/tool-trace.js +58 -49
  284. package/dist/operator-ask.d.ts +10 -0
  285. package/dist/operator-ask.js +3 -0
  286. package/dist/orchestration/workflow-agent-session-index.d.ts +12 -12
  287. package/dist/orchestration/workflow-agent-session-index.js +9 -13
  288. package/dist/orchestration/workflow-completion-inbox.d.ts +42 -15
  289. package/dist/orchestration/workflow-completion-inbox.js +13 -0
  290. package/dist/orchestration/workflow-notify-journal.d.ts +13 -32
  291. package/dist/orchestration/workflow-notify-journal.js +27 -34
  292. package/dist/orchestration/workflow-park-decision.d.ts +89 -0
  293. package/dist/orchestration/workflow-park-decision.js +118 -0
  294. package/dist/org-delivery-gate.d.ts +81 -0
  295. package/dist/org-delivery-gate.js +59 -0
  296. package/dist/panel-tool-names.d.ts +23 -0
  297. package/dist/panel-tool-names.js +8 -0
  298. package/dist/parked-decide.d.ts +39 -6
  299. package/dist/parked-decide.js +98 -51
  300. package/dist/parked-revive-surface.d.ts +9 -2
  301. package/dist/parked-revive-surface.js +11 -8
  302. package/dist/per-task-image.d.ts +7 -1
  303. package/dist/per-task-image.js +3 -3
  304. package/dist/permission-rule-list.d.ts +1 -1
  305. package/dist/permission-rule-list.js +2 -2
  306. package/dist/plain-string-record.d.ts +25 -0
  307. package/dist/plain-string-record.js +32 -0
  308. package/dist/plugins/approval-ask-store-file.d.ts +16 -1
  309. package/dist/plugins/approval-ask-store-file.js +21 -0
  310. package/dist/plugins/approval-ask-store-memory.d.ts +18 -1
  311. package/dist/plugins/approval-ask-store-memory.js +34 -2
  312. package/dist/plugins/approval-ask-store-sql.d.ts +25 -0
  313. package/dist/plugins/approval-ask-store-sql.js +37 -1
  314. package/dist/plugins/blob-backend.d.ts +83 -15
  315. package/dist/plugins/blob-backend.js +68 -15
  316. package/dist/plugins/breaker-state-sql.d.ts +2 -2
  317. package/dist/plugins/breaker-state-sql.js +4 -4
  318. package/dist/plugins/caching-session-store.d.ts +3 -0
  319. package/dist/plugins/caching-session-store.js +3 -0
  320. package/dist/plugins/checkpoint-store-sql.d.ts +28 -4
  321. package/dist/plugins/checkpoint-store-sql.js +59 -28
  322. package/dist/plugins/file-history-store-sql.d.ts +5 -2
  323. package/dist/plugins/file-history-store-sql.js +3 -1
  324. package/dist/plugins/file-run-store.d.ts +5 -13
  325. package/dist/plugins/file-run-store.js +9 -0
  326. package/dist/plugins/fork-routing-session-store.d.ts +9 -1
  327. package/dist/plugins/fork-routing-session-store.js +12 -1
  328. package/dist/plugins/host-platform.d.ts +2 -0
  329. package/dist/plugins/host-platform.js +2 -1
  330. package/dist/plugins/k8s-bg-scripts.d.ts +11 -0
  331. package/dist/plugins/k8s-bg-scripts.js +22 -0
  332. package/dist/plugins/local-checkpoint-store.d.ts +2 -0
  333. package/dist/plugins/local-checkpoint-store.js +11 -3
  334. package/dist/plugins/local-session-store.d.ts +1 -1
  335. package/dist/plugins/local-session-store.js +1 -1
  336. package/dist/plugins/local-session-window.d.ts +39 -0
  337. package/dist/plugins/local-session-window.js +54 -0
  338. package/dist/plugins/mailbox-recipient-lifecycle.d.ts +3 -2
  339. package/dist/plugins/mailbox-store-sql.d.ts +30 -48
  340. package/dist/plugins/mailbox-store-sql.js +77 -52
  341. package/dist/plugins/memory-embedder-fingerprint.js +2 -1
  342. package/dist/plugins/memory-run-store.d.ts +5 -13
  343. package/dist/plugins/memory-run-store.js +8 -0
  344. package/dist/plugins/one-time-migrations.d.ts +245 -93
  345. package/dist/plugins/one-time-migrations.js +668 -207
  346. package/dist/plugins/org-delivery-quota-store.d.ts +123 -0
  347. package/dist/plugins/org-delivery-quota-store.js +228 -0
  348. package/dist/plugins/permission-rule-store-sql.d.ts +13 -7
  349. package/dist/plugins/permission-rule-store-sql.js +4 -18
  350. package/dist/plugins/pg-pool.js +39 -7
  351. package/dist/plugins/pg-session-storage.d.ts +20 -8
  352. package/dist/plugins/pg-session-storage.js +34 -21
  353. package/dist/plugins/remote-env-adb.d.ts +12 -12
  354. package/dist/plugins/remote-env-adb.js +21 -6
  355. package/dist/plugins/remote-env-device.js +3 -0
  356. package/dist/plugins/remote-env-e2b.d.ts +1 -1
  357. package/dist/plugins/remote-env-e2b.js +21 -9
  358. package/dist/plugins/remote-env-host.d.ts +9 -9
  359. package/dist/plugins/remote-env-host.js +10 -32
  360. package/dist/plugins/remote-env-k8s.d.ts +103 -38
  361. package/dist/plugins/remote-env-k8s.js +288 -95
  362. package/dist/plugins/remote-env-local-docker.d.ts +27 -13
  363. package/dist/plugins/remote-env-local-docker.js +82 -11
  364. package/dist/plugins/remote-env-ssh.d.ts +21 -12
  365. package/dist/plugins/remote-env-ssh.js +117 -5
  366. package/dist/plugins/remote-shell.d.ts +0 -1
  367. package/dist/plugins/remote-shell.js +1 -2
  368. package/dist/plugins/retention-store-sql.js +6 -2
  369. package/dist/plugins/run-store-sql.d.ts +16 -14
  370. package/dist/plugins/run-store-sql.js +22 -2
  371. package/dist/plugins/schema-consistency.d.ts +54 -0
  372. package/dist/plugins/schema-consistency.js +52 -0
  373. package/dist/plugins/schema-indexes.js +2 -0
  374. package/dist/plugins/send-user-file.js +2 -2
  375. package/dist/plugins/session-epoch-anchor.d.ts +39 -0
  376. package/dist/plugins/session-epoch-anchor.js +22 -0
  377. package/dist/plugins/session-store.d.ts +18 -8
  378. package/dist/plugins/session-store.js +15 -4
  379. package/dist/plugins/shared-memory-store-sql.d.ts +9 -3
  380. package/dist/plugins/shared-memory-store-sql.js +5 -16
  381. package/dist/plugins/sql-driver.d.ts +205 -1
  382. package/dist/plugins/sql-driver.js +325 -13
  383. package/dist/plugins/sql-errors.d.ts +64 -0
  384. package/dist/plugins/sql-errors.js +57 -0
  385. package/dist/plugins/store-backend.d.ts +36 -1
  386. package/dist/plugins/store-backend.js +42 -15
  387. package/dist/plugins/store-contracts.d.ts +42 -0
  388. package/dist/plugins/store-contracts.js +1 -0
  389. package/dist/plugins/tidb-pool.d.ts +6 -1
  390. package/dist/plugins/tidb-pool.js +44 -10
  391. package/dist/plugins/tidb-session-storage.d.ts +8 -11
  392. package/dist/plugins/tidb-session-storage.js +6 -16
  393. package/dist/plugins/tidb-session-store.d.ts +24 -4
  394. package/dist/plugins/tidb-session-store.js +29 -5
  395. package/dist/plugins/workflow-journal-store-sql.d.ts +73 -19
  396. package/dist/plugins/workflow-journal-store-sql.js +19 -6
  397. package/dist/plugins/workflow-run-store-sql.d.ts +30 -8
  398. package/dist/plugins/workflow-run-store-sql.js +50 -9
  399. package/dist/posture-source.d.ts +18 -10
  400. package/dist/posture-source.js +4 -1
  401. package/dist/project-identity.d.ts +45 -6
  402. package/dist/project-identity.js +162 -8
  403. package/dist/project-layer-key.d.ts +28 -0
  404. package/dist/project-layer-key.js +29 -0
  405. package/dist/prompts-domain-validate.d.ts +17 -61
  406. package/dist/prompts-domain-validate.js +9 -119
  407. package/dist/question.js +3 -2
  408. package/dist/read-deny-entry.d.ts +25 -0
  409. package/dist/read-deny-entry.js +12 -0
  410. package/dist/read-face-posture.d.ts +37 -11
  411. package/dist/read-face-posture.js +32 -3
  412. package/dist/request-key-closure.d.ts +9 -2
  413. package/dist/request-key-closure.js +11 -5
  414. package/dist/request-value-closure.d.ts +5 -0
  415. package/dist/request-value-closure.js +11 -3
  416. package/dist/rule-entry-list.d.ts +38 -11
  417. package/dist/rule-entry-list.js +106 -30
  418. package/dist/rules-consent.js +3 -4
  419. package/dist/run-acceptance.d.ts +19 -3
  420. package/dist/run-acceptance.js +13 -1
  421. package/dist/run-local.js +54 -24
  422. package/dist/runs.d.ts +15 -8
  423. package/dist/runs.js +35 -1
  424. package/dist/runtime-caps-resolver.d.ts +16 -6
  425. package/dist/runtime-caps-resolver.js +18 -5
  426. package/dist/sandbox-pkg-source.d.ts +19 -1
  427. package/dist/sandbox-pkg-source.js +29 -7
  428. package/dist/security.d.ts +87 -9
  429. package/dist/security.js +52 -9
  430. package/dist/server-secret-env.d.ts +44 -18
  431. package/dist/server-secret-env.js +49 -8
  432. package/dist/session-error-wire.d.ts +19 -2
  433. package/dist/session-error-wire.js +5 -1
  434. package/dist/session-incarnation.d.ts +15 -5
  435. package/dist/session-incarnation.js +16 -9
  436. package/dist/session-titled-frame.d.ts +33 -0
  437. package/dist/session-titled-frame.js +4 -0
  438. package/dist/session-titler.d.ts +9 -1
  439. package/dist/session-titler.js +27 -6
  440. package/dist/spec-fields.d.ts +28 -16
  441. package/dist/spec-fields.js +66 -54
  442. package/dist/sql-engine-posture.d.ts +31 -4
  443. package/dist/sql-engine-posture.js +36 -1
  444. package/dist/stored-request-tool-axes-m19.d.ts +19 -4
  445. package/dist/stored-request-tool-axes-m19.js +30 -2
  446. package/dist/task-a2a.d.ts +9 -1
  447. package/dist/task-a2a.js +73 -47
  448. package/dist/task-cwd.d.ts +1 -1
  449. package/dist/task-mcp.d.ts +46 -58
  450. package/dist/task-mcp.js +82 -107
  451. package/dist/task-settings.d.ts +95 -108
  452. package/dist/task-settings.js +77 -98
  453. package/dist/text-bidi.d.ts +1 -48
  454. package/dist/text-bidi.js +3 -5
  455. package/dist/tighten-only-seats.d.ts +150 -0
  456. package/dist/tighten-only-seats.js +196 -0
  457. package/dist/tool-approval.d.ts +304 -88
  458. package/dist/tool-approval.js +623 -239
  459. package/dist/trace/core-keyset-guard.d.ts +122 -20
  460. package/dist/trace/engine-notice-wire.d.ts +11 -3
  461. package/dist/trace/engine-notice-wire.js +8 -1
  462. package/dist/trace/exit-seat.d.ts +28 -0
  463. package/dist/trace/exit-seat.js +76 -0
  464. package/dist/trace/ledger-events.d.ts +6 -3
  465. package/dist/trace/ledger-sink.js +4 -1
  466. package/dist/trace/model-segment.js +1 -1
  467. package/dist/trace/project.d.ts +98 -17
  468. package/dist/trace/project.js +98 -50
  469. package/dist/trace/projection-table.d.ts +100 -0
  470. package/dist/trace/projection-table.js +130 -0
  471. package/dist/trace/redact.d.ts +54 -0
  472. package/dist/trace/redact.js +778 -152
  473. package/dist/trace/self-minted-wire.d.ts +345 -1
  474. package/dist/trace/self-minted-wire.js +117 -1
  475. package/dist/trace/wire-projection-faces.d.ts +377 -3
  476. package/dist/trace/wire-projection-faces.js +164 -1
  477. package/dist/url-refusal.js +2 -0
  478. package/dist/web-admin-client/apply-effective.d.ts +12 -0
  479. package/dist/web-admin-client/apply-effective.js +56 -5
  480. package/dist/web-admin-client/candidate-gate.d.ts +6 -6
  481. package/dist/web-admin-client/candidate-gate.js +3 -3
  482. package/dist/web-admin-client/facade.d.ts +1 -1
  483. package/dist/web-admin-client/facade.js +1 -1
  484. package/dist/web-admin-client/http-client.js +0 -6
  485. package/dist/web-admin-client/read-face.js +5 -9
  486. package/dist/web-admin-client/read-warnings.d.ts +13 -0
  487. package/dist/web-admin-client/read-warnings.js +10 -0
  488. package/dist/web-admin-client/skills-mcp.d.ts +3 -0
  489. package/dist/web-admin-client/skills-mcp.js +11 -9
  490. package/dist/web-admin-client/types.d.ts +2 -3
  491. package/dist/workspace-cache.d.ts +35 -0
  492. package/dist/workspace-cache.js +36 -0
  493. package/dist/workspace-space.d.ts +3 -2
  494. package/dist/workspace-space.js +2 -0
  495. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +65 -10
  496. package/package.json +8 -4
  497. package/scripts/drain-check-local.mjs +34 -1
  498. package/scripts/drain-check.mysql.sql +30 -2
  499. package/scripts/drain-check.pg.sql +29 -2
  500. package/dist/capabilities/memory-notice.d.ts +0 -138
  501. package/dist/capabilities/memory-notice.js +0 -64
  502. package/dist/capabilities/prompt.d.ts +0 -17
  503. package/dist/capabilities/prompt.js +0 -6
  504. package/dist/mcp-content-origin.d.ts +0 -34
  505. package/dist/mcp-content-origin.js +0 -10
  506. package/dist/tool-axis-words.d.ts +0 -27
  507. package/dist/tool-axis-words.js +0 -9
  508. package/dist/wire-name-bound.d.ts +0 -14
  509. package/dist/wire-name-bound.js +0 -13
package/USAGE.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # 调用方手册(OA / 智能体 / 你自己)
2
2
 
3
3
  > 一句话心智模型:**你只对一个地址发普通 HTTP 请求——LB `http://<host>:8090`;请求体里只写"要做什么"。**
4
- > 工具面与安全策略由服务端 `resolveSpec` 注入,请求体**替换不了**它们(`tools` 不是受理键,传了 400;按请求只能**减**:`allowTools`(7.126.0 起,只留列出的)/ `excludeTools` / `settings.permissions` 的 deny · ask;7.117.0 起 `settings.permissions.allow` 整份交给引擎的 allow 层:表里的调用免问(普通问与没人表态两类,内置工具的写门 / shell 普通问也清),deny / ask 规则与运维的强制类照赢;能力位 `settings.permissions.allowLayer`)。
4
+ > 工具面与安全策略由服务端 `resolveSpec` 注入,请求体**替换不了**它们(`tools` 不是受理键,传了 400;按请求只能**减**:`allowTools`(7.126.0 起,只留列出的)/ `excludeTools` / `settings.permissions` 的 deny · ask;7.117.0 起 `settings.permissions.allow` 整份交给引擎的 allow 层:表里的调用免问(普通问与没人表态两类,内置工具的写门 / shell 普通问也清),deny / ask 规则与运维的强制类照赢;能力位 `settings.permissions.allowLayer`;7.133.0 起多租部署上按调用方的信任档受理 —— 档只由验过的凭据推,低于 `global` 的调用方带非空 `allow`(或按名点用带 `allowedTools` 的自带技能)⇒ 400,能力位 `settings.permissions.callerTrust`,见下方排障表)。
5
5
  > `model` 与 `systemPrompt` / `appendSystemPrompt` 是受理键,但 `model` 只能点服务端已配置目录里的名字 / 档位词 / 模型 id(未知 ⇒ 400)。凭据永远不进模型 / 请求体。
6
6
  > 你不"调度 worker",你只发任务;LB + TiDB 自己协调。每个字段都来自当前真实代码。
7
7
 
@@ -11,6 +11,7 @@
11
11
 
12
12
  > 部署 = 把本仓跑在一台**能连到模型网关 + TiDB** 的机器上;那台机器的 `IP:8090` 就是你的 `<host>:8090`。
13
13
  > 该机器需要 **Node 22 / Bun**(路线 A,无 docker)**或** **Docker**(路线 B)——二选一。
14
+ > 容器镜像自 7.128.0 起暂停发布,npm 包是唯一发布件;恢复另行通告(路线 B 用 `docker compose up --build` 就地构建,不受影响;已发布的旧镜像仍可拉,`:latest` 停在 7.128.0 之前的版本)。
14
15
 
15
16
  **路线 A — 不用 docker(单实例,最简单)**
16
17
  ```bash
@@ -60,17 +61,24 @@ docker compose up --build --scale service=4 # 4 个无状态 worker,nginx
60
61
 
61
62
  > 🔴 **7.104.0 起多租户姿态不许跑不分租户的执行车道**:`REQUIRE_PRINCIPAL=true` ∧ 最终生效的车道在执行车道能力表上不分租户 ⇒ **拒启**。按表不按词,今天覆盖 `host` / `ssh` / `adb`(所有任务落同一个共享目标:worker 宿主 / 一台 SSH 对端 / 一台 ADB 设备)。判的是合成后的车道(env 的 `REMOTE_EXEC`、`CONFIG_PROVIDER=local` 而 `REMOTE_EXEC` 未设的隐式 host、local 形 `config.d/remote-exec.json` 的覆写,在中心拉取与任何配置应用之前合成)。拒因(逐字,以 host 为例):`REQUIRE_PRINCIPAL=true (multi-tenant posture) with the "host" execution lane runs every tenant's Bash on one shared target — this lane does not separate tenants; set REMOTE_EXEC to e2b / k8s / local-docker / device, or unset REQUIRE_PRINCIPAL for a single-user box`。车道 × 租户形:
62
63
  >
63
- > | 车道 | 单用户 | 多租户(`REQUIRE_PRINCIPAL=true`) |
64
- > |---|---|---|
65
- > | `host`(显式 / `CONFIG_PROVIDER=local` 隐式 / 文件覆写) | ✅ | ❌ 拒启(7.104.0 起) |
66
- > | `ssh`(一台常驻对端) | ✅ | ❌ 拒启(7.104.0 起) |
67
- > | `adb`(一台设备) | ✅ | ❌ 拒启(7.104.0 起) |
68
- > | `e2b` / `k8s` / `local-docker`(每任务一只沙箱) | ✅ | ✅ |
69
- > | `device`(每人自己的设备) | ✅ | ✅ |
70
- > | 未点名(云形进程内 stub,零手带工具、非手带工具照常执行) | ✅(`REQUIRE_EXECUTION_LANE=true` 可改成拒启) | ✅(同左) |
64
+ > | 车道 | 单用户 | 多租户(`REQUIRE_PRINCIPAL=true`) | `READ_FACE` 缺席时的读面缺省(7.133.0 起车道行声明) |
65
+ > |---|---|---|---|
66
+ > | `host`(显式 / `CONFIG_PROVIDER=local` 隐式 / 文件覆写) | ✅ | ❌ 拒启(7.104.0 起) | 单用户 `open`(来源 `posture`) |
67
+ > | `ssh`(一台常驻对端) | ✅ | ❌ 拒启(7.104.0 起) | 单用户 `open`(来源 `posture`) |
68
+ > | `adb`(一台设备) | ✅ | ❌ 拒启(7.104.0 起) | 单用户 `open`(来源 `posture`) |
69
+ > | `e2b` / `k8s` / `local-docker`(每任务一只沙箱) | ✅ | ✅ | **`open`**(来源 **`lane`**,单用户 / 多租户同;车道行显式声明)。🔴 **挂载告诫**:把宿主目录(`hostPath` / bind mount)或共享卷挂进沙箱的部署,`open` 面上这些宿主文件对模型零问可读 —— 显式 `READ_FACE=roots`(env 恒赢)或给它们加读拒行(`READ_DENY_PATTERNS`) |
70
+ > | `device`(每人自己的设备) | ✅ | ✅ | 单用户 `open`(来源 `posture`);多租户 不铸 = 引擎默认 `roots` |
71
+ > | 未点名(云形进程内 stub,零手带工具、非手带工具照常执行) | ✅(`REQUIRE_EXECUTION_LANE=true` 可改成拒启) | ✅(同左) | 单用户 `open`(来源 `posture`);多租户 不铸 = 引擎默认 `roots` |
71
72
 
72
73
  > 🔴 **7.104.0 起 `CONFIG_PROVIDER` 是闭集**:只认 `local` / `remote` / 不设(空串 = 不设),**逐字**比较 —— `Local`、` local`、`remote ` 这类写法**拒启**,拒因句点名旋钮并列出合法词。此前认不出的词照起,却被配置源当成 `local`、被隐式 host 车道当成「不是 local」,local 配置源的根还落回进程 cwd(与车道文件覆写读的本地根分家)。本地配置根只有一条链:`CONFIG_LOCAL_DIR` → `AGENT_DATA_DIR` → `~/.ai-agent`,配置源与 `config.d/remote-exec.json` 读同一个。
73
74
 
75
+ > 🔴 **本地配置源的目录信任(7.133.0 起)**:`config.d` 是可执行配置(`mcp.json` 的 stdio `command` 以本服务用户起子进程,`models.json` 的 `baseUrl` 决定 API key 送往哪里),也是部署者在盘上声明外置 MCP 工具的唯一入口 ——
76
+ > 写在 `config.d/mcp.json`,键与中心发布的 `mcp` 域同形,没有另一个工具目录。读之前先判:从 `/` 走到 `config.d`、再到读者会读的每一份文档(符号链接照跟、跟到哪判到哪),
77
+ > 每一级属主只能是本服务的用户、root 或 `CONFIG_LOCAL_OWNER_UID`(多信的那一个 uid;形错拒启),不得组 / 他人可写(属主可信的粘滞目录如 `/tmp` 除外,但要读的一项缺在这样的目录里也拒),
78
+ > 文档是常规文件且 ≤ 16 MiB,整条路径链接合计 ≤ 40 跳、不得指向 procfs 一类内核对象。
79
+ > 起服不过 ⇒ 拒启(fatal 标题 `[config.dir_untrusted]`;`run-local` 退 2);起服之后才变坏 ⇒ 那一拍 `web_admin_refresh_failed` 带同一句、新字节不读、已应用的配置不动。拒句点名那一个路径、实测值与改法
80
+ > (`chmod go-w` / `chown` / `CONFIG_LOCAL_OWNER_UID=<uid>`)。Windows 上属主与模式位不可判:只判结构,另记一次 fail-open 告警。容器形(rootless / userns-remap / ConfigMap / fsGroup)见源码仓文档 DEPLOY-PREREQS 的 7.133.0 节(不随包)。
81
+
74
82
  > **会话 scratchpad 与 `SANDBOX_ENV_FACTS`(7.104.0 起)**:`SANDBOX_ENV_FACTS=false` 只关沙箱事实(profile / capabilities / pkgSource / egress / resume);会话 scratchpad 的宣告不归它管 —— 宣告是 Write / Edit 能写进 scratchpad 的唯一来源(引擎据它开根围栏的洞,并用同一次裁决在提示词里写出 `# Scratchpad Directory` 一节)。缺省部署此前每轮一句 `scratchpad advertised writable directory skipped` 的噪音随之消失。
75
83
 
76
84
  > 拓扑:LB(入口)→ N 个一样的 worker → 共享 SQL 库(数据中心;MySQL 协议或 PG)+ 模型网关。无中央调度器,跨 worker 靠 DB 协调。
@@ -138,6 +146,12 @@ ANTHROPIC_API_KEY=sk-ant-… # 可选:ANTHROPIC_BASE_URL / ANTHROPIC_VERSION /
138
146
  端点与凭据照网关的来 —— 此前它们按 `provider` 字面去找一条没装的 anthropic 路由而整条拒发,会话列表没有自动标题。网关端点也解析不到才拒(`config.model_route_endpoint_missing`)。
139
147
  - **缺省推断**:`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`,与以前逐字节一致。
140
148
  - 启动日志的 `brain` 字段会回显当前组合(`failover` / `anthropicRoute` / `anthropicCacheBreakpoints`)。
149
+ - **在服务进程之外读同一套 env 路由**(子路径 `@sema-agent/server/model-route-env`;纯数据 + 纯函数,import 无副作用,值依赖只有 `@sema-agent/core` 与 `@sema-agent/settings-schema`):
150
+ 引擎(core)不读 `MODEL_*` / `ANTHROPIC_*` 这些键,键表和读法属本服务;壳首启、客户端运行时要在引擎起之前问「这个环境模型走哪条车道、端点 / 凭据取哪一对键」,读这一只,不各抄键名。
151
+ `MODEL_ROUTE_ENV_KEYS` 是九把路由键的闭表(网关端点 / 备用端点 / 网关凭据、provider、主 / cheap 模型 id、三把 Anthropic 键),`readModelRouteEnv(env)` 是读口(`env` 交整张 `process.env` 或等价快照;空串 = 未设),
152
+ `laneAndEndpointFor(env, model, routes)` 是值级端口:`routes` 是引擎路由表的原型(`anthropic` 键在场 ⇔ anthropic 路由装上),答 `{ lane, endpointEnvName, endpointDeclared, apiKeyEnvName }` ——
153
+ **只有键名与在场性,没有任何 env 值**(端点与凭据的值调用方自己从 env 取)。凭据取值序与引擎装配同序(`ANTHROPIC_AUTH_TOKEN` 压过 `ANTHROPIC_API_KEY`);网关凭据 `MODEL_API_KEY` 只与网关端点配对,
154
+ 网关端点没设就不报凭据键(与起服不装载那把 key 同向)。
141
155
 
142
156
  **可选 — 韧性栈:分级超时 + 断路器(core 1.38,叠在 failover 之下)**
143
157
  ```bash
@@ -164,7 +178,8 @@ AUTO_MODE_CLASSIFIER_TIMEOUT_MS=180000
164
178
  - **全部默认关**(超时=0、断路器=false)→ 不设就和以前**逐字节一致**。
165
179
  - 断路器**只在配了 `MODEL_GATEWAY_FALLBACK_URLS`(≥2 路)时才有意义**——它的价值是"开路即快速失败 → failover 立刻切备";单网关下它是 no-op(启动日志 `circuitBreakerNoop` 会提示)。只 `network/server/rate_limit` 计入连败,`auth`/`invalid_request` 不计(坏 key 熔断整网关无意义)。备用网关(最后一路)不套断路器。
166
180
  - **重试上限**(2026-07-31):两个键**不设就不传给引擎** —— 引擎默认当家。以前这里是 server 侧硬编码 `2`(openai 腿甚至零配置出口),而**显式传参压过引擎默认**,于是引擎抬默认对 server 部署毫无效果;第三方限流 provider 下"重试两次就放弃"正是由此而来。现在:不设=继承引擎默认(抬默认那天自动跟上),设了=按设的走。钉在 `test/brain.test.ts` 的「网关腿重试次数」两条上。
167
- - 断路器状态:配了 `SESSION_BACKEND=mysql` 时自动用**跨副本共享态**(SQL `circuit_breaker` 表,写穿+刷新最终一致),否则进程内 Map。启动日志 `breakerState` 字段回显 `shared(mysql)`/`in-process`/`off`。
181
+ - 断路器状态:配了 `SESSION_BACKEND=mysql` 时自动用**跨副本共享态**(SQL `breaker_state` 表,写穿+刷新最终一致),否则进程内 Map。启动日志 `breakerState` 字段回显 `shared(mysql)`/`in-process`/`off`。
182
+ 断路器键 = `<provider>:<模型>@<路由身份摘要>`(摘要 24 hex;整只键 ≤ 190 字节,`<provider>:<模型>` 过长时截成前缀 + `~` + 摘要;键里不带 URL —— 它进共享表 `breaker_state`、追踪事件与开路错误文本 `circuit open for <键>`;7.132.x 及以前的表 `circuit_breaker` 由起服迁移删掉)。摘要对应哪条路由读启动日志 `brain.circuitBreakerRoutes`(每条带断路器的路由一行:展示串 + 摘要)。
168
183
 
169
184
  **可选 — WebSearch 后端(core 只留注入口,不自带任何 provider;部署 env 七键)**
170
185
  ```bash
@@ -265,7 +280,8 @@ MODEL_DEGRADE_ON=rate_limit,breaker_open # 可选,反应式触发器子集;词
265
280
  - **成本天花板要有价才咬得住**:`MAX_TASK_COST_USD` / `MAX_PRINCIPAL_COST_USD` 量的是 `costUsd`,未定价的模型算不出成本 ⇒ 这两道闸**永不触发**。目录里一只带价模型都没有却配了天花板时,boot 会打一条 `cost_ceiling_without_pricing` 告警(不拒启);token 轴的 `MAX_TASK_TOKENS` 不受影响。
266
281
  - 成本计量**自动开**:从 config 的 `model.cost`(per-1M 绝对 USD)注入 `pricing`,core 算出权威的整数 `costMicroUsd`(避免浮点累计误差)。`/metrics` 新增:`model_cost_micro_usd_total{model}`(覆盖所有 brain 调用=主任务+异步+委派子任务的总花费)、`brain_first_token_ms`(网关 hang 早警)、`brain_call_latency_ms`、`tool_calls_total{name,ok}`、`budget_exceeded_total{code}`、`cost_quota_rejected_total`、`degraded_total{reason}`(1.40 降级)。
267
282
  - **近预算降级 vs 硬闸**:降级(`MODEL_DEGRADE_TO`,到 `atCostFraction` 切便宜模型)是**撑长**预算、任务仍完成(出口质量下降、发 `task.degraded` 事件可告警);硬闸(`maxCostUsd` 全额)仍在,切了便宜模型还超全额 → `limits.max_cost_exceeded` 停。
268
- - **`METRICS_TOKEN`**(可选,只读):设了它,`GET /metrics`+`/metrics/summary` 接受**它或** `SERVICE_AUTH_TOKEN`。**全 fleet 设同一个值** → 管理端(sema-web,规划名 sema-admin)用**一个** token 拉所有 worker 的指标,**无需持有各 worker 的全权 `SERVICE_AUTH_TOKEN`**(不破坏 secret 边界)。即使外泄也只暴露指标(只读)。
283
+ - **`METRICS_TOKEN`**(可选,只读):设了它,`GET /metrics`+`/metrics/summary` 接受**它或** `SERVICE_AUTH_TOKEN`。都没配时 `/metrics` 族只在 dev-open 部署上免凭据(见 §1 dev-open 那一段:
284
+ 装了中心鉴权桥、配了 `TRACE_TOKEN` 或开了 `REQUIRE_PRINCIPAL` 的部署上匿名 `401` —— 7.132.0 起,此前前两形照开;`/metrics/plan-cache` 按主体分键,匿名可读 = 名册外泄)。**全 fleet 设同一个值** → 管理端(sema-web,规划名 sema-admin)用**一个** token 拉所有 worker 的指标,**无需持有各 worker 的全权 `SERVICE_AUTH_TOKEN`**(不破坏 secret 边界)。即使外泄也只暴露指标(只读)。
269
285
  - **`GET /metrics/summary`**(token-gated,同 `/metrics`):`/metrics` 的**精炼 JSON**——`{model, runsActive, tasks{status}, tokensTotal, costUsd, costUsdByModel, taskDurationAvgSec, brainFirstTokenAvgMs, brainCallAvgMs, cacheHitRateAvg, rateLimited, costQuotaRejected, budgetExceeded, limitCeilingAfterAnswer, degraded, cascade, verifications, toolErrors, httpErrorsByClass, httpErrorsByRoute}`(**7.126.0 起没有 `councilRuns`** —— 它数的 code-review 多镜头评审团随引擎 7.49.0 删掉团队运行时一并删除,Prometheus 的 `council_runs_total` / `council_tokens` 同删;按 v1 形解析这张表的看板升级前先把这一键改成可缺)(`httpErrorsByClass` / `httpErrorsByRoute` 自 **7.87.0**,additive:窗内 HTTP **失败**请求按状态码类聚合(键只有 `4xx` / `5xx`,2xx/3xx 不进;⚠️ `4xx` 含本仓给「客户端提前断连」铸的 `499`,SSE 长连上是常态)+ 失败最多的前 10 条 route 标签(`routeLabel` 低基数形,id 折成 `:id`,未命中路由表的落 `other`)—— 这两格是**不装 Prometheus 的部署**看见一场 404 风暴的唯一地方;两键**恒在场**,零失败时是 `{}`,不用缺席表示零)(`limitCeilingAfterAnswer` 自 7.71.0,按 `origin` 分桶的「答后撞顶」计数;与 `budgetExceeded` **总体不同** —— 前者每条引擎 run 都过,后者只由 HTTP 提交腿递增 —— 不要无条件相加)。给**轻量 fleet 看板**用(管理端 sema-web 的 fleet 页按 worker 拉、渲染卡片、按轮询算速率;**不用 Prometheus/Grafana**)。counter 是累计值、histogram 报均值。
270
286
  - 预算闸是 core 强制的:`maxCostUsd` pre-call 估算(没花钱就拒)+ 流中途取消 + turn 边界(`limits.max_cost_exceeded`);`maxTokens` 超 → `limits.max_tokens_exceeded`(core 5.8.0 起 `budget.*` 前缀退役)。**review 网关**建议设 `MAX_TASK_COST_USD` 防单任务烧掉共享云 key。
271
287
  - 🔴 **三道 $ 闸的分工(别拿进场门当中途停机用)**:三根旋钮名字都带 cost,但**判的时刻**和**能不能打断正在烧的那张单**完全不同。按「谁触发 / 什么时刻判 / 拒或停长什么样」对表:
@@ -391,7 +407,7 @@ MODEL_ID=<your-model-name> MODEL_CHEAP_ID=<your-cheap-model-name>
391
407
  存量中心配置里的 `roles.team` / `roles.synthesize` 由 settings-schema 的一次性迁移搬走,side query 带 `modelRole` 表外词(含 `advisor`)⇒ **400**
392
408
  `request.field_invalid`(不再回落主模型;带了 `model` 也一样拒)。要给委派子代配模型就绑 `subagent`,主任务绑 `default`。
393
409
  - 调用方可在 `objective` 里 `@<模型名>` 选模型,**只认已配置的名单**(`MODEL_ID` / `MODEL_CHEAP_ID`,或目录条目)——注入不了 `baseUrl`/`apiKey`;无 `@` → 默认模型;`@` 点中一条已发布但本部署不可用的目录条目 ⇒ 400 `request.model_unavailable`。
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。
410
+ - **`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。 7.133.0 起另有**恒在场**的顶层键 `modelPlane: { servedTable, catalog, refusalCode?, gap? }` —— 壳问 server,不猜:`servedTable`(`"env"` | `"catalog"`)= 此刻服务哪张模型表;`catalog`(`"absent"` | `"adopted"` | `"refused"`)= 最近一份目录候选在两道判官手里的结局(`refused` = 候选判官整拒,或模型面组拒;`adopted` 说的是「没有被判官整拒」,不是「最新候选已落地」,后者问 `/health` 的世代账;没有 LKG 时服务的是 env 表,有 LKG 时目录表仍在服,谁在服归 `servedTable`;逐条被拒的条目不算整拒);`refusalCode` / `gap` 是起服路由判官(`judgeServedModelRoute`)对当前 config 的判决,通过 ⇒ 两键缺席(进程起来了,正常缺席;自建装配 / 不拒启的入口可能在场),不带拒因句。只在起服后可答,没有起服前的预判口;新键追加在应答最末。契约 §A.1d。
395
411
  - 不设 `MODEL_CHEAP_ID` → 所有角色 = 主模型(行为不变)。
396
412
  - `MODEL_CHEAP_ID` 写成与 `MODEL_ID` **同一个 id**(7.104.0 起)→ 那就是主模型本身:不铸 cheap 槽,所有角色 = 主模型,起服一行
397
413
  `config_env_entry_cheap_same_as_main`(同时设的其余 `MODEL_CHEAP_*` 键一并不生效)。要给同一只上游 id 第二种形(另一个价 / 思考 /
@@ -497,7 +513,7 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
497
513
  **模型路由端点的拒启同理判在目录应用之后**:`judgeServedModelRoute(config, { envTableIsPlaceholder })`(包根导出,HTTP 服务与 `run-local` 调的就是它)只判将要服务的那张表(目录表或 env 表,句子随视图);
498
514
  只调 `loadConfig()` 的装配不再在读配置那一拍拿到路由拒启 —— 在**自己的**配置源应用之后调它一次,有判决就拒启(`reason` 是给运维的原句)。严格名册布防(env 表是占位)传 `envTableIsPlaceholder: true`,没有名册门的装配传 `false`。
499
515
  - **库形嵌入的 caps `store` 位**(7.106.0 起;7.106.0 合并复审 X9 补一句):`createHttpServer` 的 `storePosture` dep **不传** ⇒ `GET /v1/capabilities` 的
500
- `store` 取诚实下限 `{declared: <config.dbBackend>, backend: "memory", durable: false}`,且**没有 `store.sql`**(即使递了 `sqlEngineFacts`、真有 SQL 后端 ——
516
+ `store` 取诚实下限 `{declared: <config.dbBackend>, backend: "memory", durable: false}`,且**没有 `store.sql`**(即使递了引擎读口 `sqlEngine`、真有 SQL 后端 ——
501
517
  7.105.0 上这类嵌入方的顶层 `sql` 是有值的)。要 caps 说真话就把你装出来的后端事实经 `storePosture` 递进来(形 = `src/store-posture.ts` 的 `StorePosture`)。
502
518
  - **`PORT=0` = 让 OS 分口,口由引擎自己报**:引擎自己绑内核分给它的口,`listening` 启动日志行(stdout,info 级;`LOG_LEVEL` 高于 info 会滤掉它)的 `port` 与 `/health.port` 报**实际绑定口**
503
519
  (`PORT` 非 0 时两者与配置值相等)。要用 OS 分的口起引擎的宿主:子进程以 `PORT=0` 起,读 stdout 那一行 `listening` 的 `port`,再用 `/health` 的 `pid` / `dataRoot` 验答话的是自己起的那个进程;
@@ -546,7 +562,7 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
546
562
  全部是装配事实的投影、不是另立的规则:`auth = { required, kinds, identityAttach }` —— `required` = 全局凭据门武装(配了
547
563
  `SERVICE_AUTH_TOKEN(S)`),`kinds` ⊆ `service_token` / `sso`(装了鉴权桥:登记中心的登录令牌)/ `direct_door_jwt`(直连门),
548
564
  `identityAttach` = `available`(kinds 里有 `sso` 或 `direct_door_jwt`)/ `unavailable`;`operator` = `present`(`OPERATOR_PRINCIPALS`
549
- 非空)/ `absent`;`center = { issuer?, webBaseUrl?, sessionsVisible }` —— `issuer` = 鉴权桥的 issuer(`AUTH_BRIDGE_ISSUER`,缺席回退
565
+ 非空,或单用户声明 `OPERATOR_SINGLE_USER=true`)/ `absent`;`center = { issuer?, webBaseUrl?, sessionsVisible }` —— `issuer` = 鉴权桥的 issuer(`AUTH_BRIDGE_ISSUER`,缺席回退
550
566
  `SEMA_WEB_ADMIN_URL`;没有桥 ⇒ 缺席),`webBaseUrl` = `SEMA_WEB_ADMIN_WEB_URL` 原样(缺席即缺席),`sessionsVisible` = `true` /
551
567
  `false` / `"unknown"`:中心车道配齐 ∧ `FLEET_ADVERTISE_ADDRESS` 在 ∧ 鉴权桥在 ∧ 会话枚举面在,缺一 ⇒ `false`;齐了而多租户 ⇒ `true`,
552
568
  单用户 ⇒ `"unknown"`(一条会话归谁看提交它的请求带没带身份)。两只 URL 过展示面的同一条脱敏律(凭据 / query 值不上,尾斜杠折掉)。
@@ -705,7 +721,7 @@ SEND_USER_FILE_SANDBOX_PUT_ENDPOINT=… # 可选:沙箱直传 PUT 的端
705
721
  # k8s 集群内 pod 可指内网 MinIO 省公网带宽)
706
722
  ```
707
723
  - **词表优先级**:内网端点 `MINIO_ENDPOINT` 优先,`S3_ENDPOINT` 是外接 S3 兼容位(内网/公网同址场景可只给它,同时充当公网面);公网端点 `S3_PUBLIC_ENDPOINT` 优先,缺席回落 `S3_ENDPOINT`。空串一律按未设处理。
708
- - **🔴 云形(`DB_BACKEND=mysql|pg`)快照 blob 强制对象存储(1.295+,clay 拍)**:缺 MinIO 三键 ⇒ **boot 拒启**——单行 SQL blob 写会撞 TiDB `txn-entry-size-limit`(默认 6MiB,真库实测矮墙)/ mysql 协议 `max_allowed_packet`,大字节归对象存储(与 D-1 附件面同裁定)。单机/测试台显式逃生:`SNAPSHOT_BLOB_ALLOW_SQL_BYTES=true`(SQL 店此时带 per-blob 帽,tidb 默认 6MiB,超限 PUT 413 `blob_too_large_for_sql`;帽可用 `SNAPSHOT_BLOB_SQL_MAX_BYTES` 按部署真实限值覆写,两方言生效;pg 默认无帽)。
724
+ - **🔴 云形(`DB_BACKEND=mysql|pg`)快照 blob 强制对象存储(1.295+,clay 拍)**:缺 MinIO 三键 ⇒ **boot 拒启**——单行 SQL blob 写会撞 TiDB `txn-entry-size-limit`(默认 6MiB,真库实测矮墙)/ mysql 协议 `max_allowed_packet`,大字节归对象存储(与 D-1 附件面同裁定)。单机/测试台显式逃生:`SNAPSHOT_BLOB_ALLOW_SQL_BYTES=true`(SQL 店此时带 per-blob 帽,MySQL 协议(InnoDB 与 TiDB)默认 = **写入那条数据库连接**报的两堵已知墙取最小:单行上限减 1 KiB(blob 行自己的余量:哈希键 + 长度 + 时间戳;TiDB 才有)与包墙 `max_allowed_packet` 扣 1 KiB 语句余量后折半(⌊(包墙 − 1 KiB) ÷ 2⌋:文本协议把 blob 写成十六进制,语句至少是它的 2 倍);任一读数读不出时按该库族文档缺省并留一行运维日志 `fail_open`(tag `server.sql.packet-cap-unread` / `server.sql.tidb-row-cap-unread`),超限 PUT 413 `blob_too_large_for_sql`,句子按帽的出处分三形(运维旋钮 / 库报的墙 / 读不出而按文档缺省假定的墙),机读位 `wallSource` 同词 `operator` / `reported` / `assumed`。包墙是每条连接自己的会话值(7.133.0 起按连接判):DBA 调大 `max_allowed_packet` 之后,池里的旧连接在它第一次按包墙拒 blob 时自换(运维日志一行 `sql_connection_packet_wall_stale`,那一笔在新连接上重判),不必重启副本;`GET /v1/diagnostics/wiring` 的 `sqlEngine.maxRowBytes` / `sqlEngine.maxPacketBytes` 是最近一次初始化的那条连接的读数(见部署前置清单 DEPLOY-PREREQS(仓内)「数据库」条);帽可用 `SNAPSHOT_BLOB_SQL_MAX_BYTES` 按部署真实限值覆写,两方言生效;pg 默认无帽)。
709
725
  - **per-file rewind 历史的边界上限**(与 workspace 浏览面同一个整树快照纪元退役批留下的保留条款):`FILE_HISTORY_RETENTION_KEEP` —— 每个 scope(会话)存活的**最新** boundary 数,**每次 boundary 提交后自剪**(最旧者先亡,刚提交者恒存活;失引用的版本行级联删,blob 字节走异步 grace 窗 GC;v1 基线永存)。未设=core 缺省 **100**(由 core 的 `resolveFileHistoryRetention` 兑现,server 不复制那个数);`unbounded` 是**唯一**的「不剪」拼法(整店自剪关闭,GC 归部署自排 `reap`);`0`/负数/小数/非数 ⇒ **boot 拒启**(码 `config.retention_policy_invalid`,与 core 内置后端同码同判——**省略这个键不等于不剪**,它选的是缺省 100)。三条车道(TiDB / PG / local 文件店)同旋钮同判。⚠️ 升级即生效:存量超 100 boundary 的 scope 在下一次提交时被剪到 100;要保全量先写 `unbounded`。被剪 entry 的键**已花掉**(再次 snapshot 同 entry 典型拒,不复铸)。排序=DB 分配的 per-scope 发布序号 `file_history_boundary.publish_seq`(不是墙钟):**自 7.50.x 有存量 `file_history_boundary` 的部署升级本版会在 boot 期被 `assertFileHistorySchema` 拒启并指明 `DROP TABLE file_history_boundary`**(本仓无 ALTER;丢的是 per-file rewind 便利态,会话与其他持久面不动)。
710
726
  - **文件历史捕获总开关**(7.97.0 立,7.104.0 改语义):`FILE_HISTORY_CAPTURE`(缺省 `true`;布尔词表 true/false/1/0/yes/no/on/off,未知词 ⇒ boot 拒启;TiDB / PG / local 三条车道同义)。`false` ⇒ **只关捕获**:历史店照接,引擎拿到部署捕获位 `RunnerDeps.fileHistoryCapture: "off"`,改动文件的首次快照**一个都不捕获**、不铸回合边界(`wiring_manifest.fileHistory` 报 `"disabled"`);**已存历史照常可用** —— `resumeAt`+`restoreFiles` / `rewindFilesTo` 照常还原、2c session-sync 照常导出 / 导入。`GET /v1/capabilities` 的 `fileHistoryCapture` 报 `"off"`,而 `rewindFiles` / `restoreFiles` / `rewindFilesTo` / `sessionSync` 仍为 `true`(缺省部署报 `"on-optional"` = 捕获中且请求可按 run 关掉,见上一段)。🔴 **7.97.0–7.103.x 的旧语义**:`false` = 不把店交给引擎,连已存历史也读不了(还原走 `rewind.store_unconfigured`、sync 501)—— 把本旋钮当成「整个文件历史功能关掉」来用的部署,升级后已存历史**重新可以被还原 / 同步**。要让已存历史也不可用,今天的出路只有**逐会话** `DELETE /v1/sessions/:id`(擦除面不受本旋钮管)与 SQL 后端的留存清扫(`RETENTION_MODE=enforce` + `RETENTION_MAX_AGE_DAYS` + `RETENTION_SWEEP_INTERVAL_SEC`,按会话年龄删,local 车道没有留存车道);**没有**「整库历史一键不可用」的旋钮。⚠️ **关掉 ≠ 删掉**:翻旋钮不批量删除存量历史;local 车道上删会话走 `reap` —— 边界删光、v1 基线按 core 契约留存,既有残余。关着时 `FILE_HISTORY_RETENTION_KEEP` 没有效果(不再有边界提交,自剪不会触发),值仍照常校验。没有存储后端的部署(env-only worker)本来就没有历史店,旋钮在那里无事可关、不下发捕获位(引擎对「off 但没店」拒 prepare)。
711
727
  - **🔴 公桶策略必须 GetObject-only**:`mc anonymous set download` 会**连带打开 ListBucket**——匿名 `GET /<bucket>/?list-type=2` 能枚举全部不可猜 key,能力链接设计即告失效。正确姿势=`mc anonymous set-json`,policy 只含 `Action:["s3:GetObject"]` on `arn:aws:s3:::<bucket>/*`(验证:对象 GET 200、桶 LIST 403)。
@@ -734,9 +750,12 @@ SEMA_WEB_ADMIN_TOKEN=<SERVICE_PULL_TOKEN 的值> # 取自配置控制面主机
734
750
  # 坏值拒启。续签成功 info center_credential_renewed;真失效(refresh token 已作废)warn
735
751
  # center_credential_renew_failed 一次 + /health.centerCredential.renewal.state=failed,到期后中心请求
736
752
  # 走各自既有的失败臂、不带过期令牌出站;issuer 不同源 warn center_credential_issuer_mismatch 一次、不发。
737
- # ⚠️ 换账号(凭证身份变化)后,只在启动期物化的面 —— 技能 / MCP / A2A / 场景 —— 仍是上一个
738
- # 身份的,直到重启(`/health` 报 restartRequired,reasons 含 skills);热面(models / roles /
739
- # limits / 审批名单 …)在新身份第一份候选到达那一拍换代。「换账号即降档 + 当场拉取」候后续单独处理(不在 7.100.0)。
753
+ # 换账号(凭证身份变化,7.133.0 起):引擎读到凭据文件换了人的那一刻,自己把在用的中心配置降到「无中心配置」档
754
+ # (中心下发的热面 —— 模型目录 / roles / 档位 / limits / 审批名单 / 提示词层 … —— 回 env 底,Runner 换回 env 模型表)、当场
755
+ # 拉一次新身份的配置、通告 `/health.centerIdentityNotice`(`center_identity_demoted` 已降级 → `center_identity_landed` 已换上);
756
+ # 新身份的配置拉到之前**不回退**到上一个人的任何东西,拉取失败就停在这一档、下一拍再拉。壳不必重启引擎。
757
+ # ⚠️ 只在启动期物化的面 —— 技能 / MCP 服务器表 / A2A / 场景 —— 不在降档射程:仍是启动时那个人的,直到重启
758
+ # (新身份的候选落地后 `/health` 报 restartRequired,reasons 含 skills 等)。没有 env 模型(`MODEL_ID`)的部署,降档期间没有可派发的模型,直到新身份的目录落地。
740
759
  # 用户 JWT 形(CLI 登录)⇒ skill 正文 / prompt 产物走 /api/v1/me/…;盘上 LKG 与 prompt 状态按「worker + 凭证身份」分箱。
741
760
  SEMA_WEB_ADMIN_DRY_RUN=true # 安全灰度:只 LOG 中心配置 vs env 推导的差异,不 apply
742
761
  # SEMA_WEB_ADMIN_LIVE_URL=http://<中心活性入口>:3100 # 7.121.0:fleet **活性组**(心跳 / 注销、服务租约 —— settings-schema FLEET_LIVE_PATHS)打的中心基址。
@@ -809,7 +828,7 @@ FLEET_ADVERTISE_ADDRESS=http://<本机可达IP>:8090 # 可选:设了才启公
809
828
  | pricing / keys(env-名引用)/ prompts / teams | **热** | 值换代即生效;cost 族限额座(rate limit / cost quota)同热(限额=纯比较参数,窗内累计不动;窗长换代=记账周期重开) |
810
829
  | tier 变更(档位组 / 档位绑定) | **热**(下一 refresh 拍) | 与 models 同一条换代腿:tier 表随模型面在 commit 前原子换进全部 boot Runner,下一任务按新档位路由,`/health` 不报 `restartRequired`(7.100.0 起 `models-tiers` 重启理由退役——它在生产上从未发出过) |
811
830
  | plugins(插件引用) | 重启生效 | 插件只在启动期物化;改了之后 `/health` 报 `restartRequired`,`restart.reasons` 含 `skills`(7.100.0 前这一改动**不报**,要等一次无关的重启) |
812
- | skills / mcp / a2a / scenarios(技能 / MCP / A2A / 场景) | 重启生效 | 只在启动期物化;中心改了 ⇒ `/health` 报 `restartRequired`,`restart.reasons` 各含其名。**换账号**(凭证身份变化)同理:这些面仍是上一身份的直到重启(候后续单独处理) |
831
+ | skills / mcp / a2a / scenarios(技能 / MCP / A2A / 场景) | 重启生效 | 只在启动期物化;中心改了 ⇒ `/health` 报 `restartRequired`,`restart.reasons` 各含其名。**换账号**(凭证身份变化)同理:这些面仍是启动时那个人的直到重启(热面则在换人那一刻降档、新身份的候选落地时换代,见上文 `SEMA_WEB_ADMIN_TOKEN_FILE` 段) |
813
832
  | 审批名单(`governance.approvalRequire`) | **热**(下一 refresh 拍) | 7.100.0 起:下一个任务调名单里的工具即停车等审批;撤键回 env 底(`APPROVAL_REQUIRE`)。**没有 checkpoint 店**的部署(`DURABLE_APPROVAL` 关 / 后端无 checkpoint 面)发非空名单 ⇒ 审批组**整组拒**(旧名单继续服务、`configAppliedVersion` 停旧代、拒因见日志 `web_admin_approval_require_invalid` 与诊断端点 `configApply.groups.approvalGate`)—— 与启动期「名单已配却无 durable 门 ⇒ 拒启」同一条判据。此前 `/health` 报 `restartRequired`(`runtime-gates`,该理由退役) |
814
833
  | 记忆整理阀门判决 + 驱动席(`MEMORY_CONSOLIDATION_DRIVER=on`,或未设而出厂缺省按目录派生 —— 7.104.0 起) | 重启生效 | 阀门开关与整理跑用的模型都在启动期定一次;中心改了 roles / 档位 / 目录条目后,**判决会翻**(派生开 ↔ 关,含只把主模型挪到整理座那一条上)或开着而座漂了 ⇒ `/health` 报 `restartRequired`,`restart.reasons` 含 `memory-consolidation`(7.100.0 新理由;7.104.0 起派生关的那台同样报) |
815
834
 
@@ -855,11 +874,13 @@ QUESTION_TTL_MS=300000
855
874
  > 开关只是发帧五合取里的一项:还要**活卡腿开着 ∧ 非 local 的持久 store backend ∧ park 设施在场**
856
875
  > (`DURABLE_APPROVAL=true` + checkpoint 能力的 backend)。所以一台什么都没配的默认 worker 仍是零帧 +
857
876
  > 启动一行 `stream_approval_disabled`;而**已经配齐那套前置的部署,升级后不必再显式开这个开关就会开始发帧**
858
- > —— 壳/SDK 不实现消费就等于用户看不到审批卡。帧格式、消费端契约、五合取全表在
877
+ > —— 壳/SDK 不实现消费就等于用户看不到审批卡。🔴 **7.133.0 起一条腿一个帧族**:协议上场的 worker(`GET /v1/capabilities` 的
878
+ > `streamApproval` 为真)只发 `approval_request` / `approval_complete` / `approval_revoke`,旧族 `tool_approval` / `tool_approval_complete`
879
+ > 不再发;协议不上场的 worker 只发旧族。帧格式、消费端契约、五合取全表在
859
880
  > [`docs/ASSISTANT-WIRE-CONTRACT.md` §4a-quint](docs/ASSISTANT-WIRE-CONTRACT.md)(错误码 `feature.approval_ask_disabled` 在附录 A)。
860
881
 
861
882
  ```bash
862
- # 协议总开关。默认 true;显式 =false 是**本协议面**的干净还原键(不发 approval_request、不落 ask 行、
883
+ # 协议总开关。默认 true;显式 =false 是**本协议面**的干净还原键(不发 approval_request / approval_complete、改发旧族帧、不落 ask 行、
863
884
  # 不起收敛器腿)。⚠️ 它**不**回滚 7.34.0 的 R-13 窗到期语义——那一条的
864
885
  # 回滚键是下面的 UNATTENDED_APPROVAL_POLICY=deny(两根旋钮正交,别当一根用);它也**不**再回滚窗长
865
886
  # (窗是活卡件不是协议件,见下一键——缺省值与旧硬编码窗同为 300000,没配过就仍然字节不变)
@@ -949,6 +970,20 @@ UNREACHED_ASK_TTL_MS=3600000
949
970
  (代价是超限的 ask 走 park,要有人去审批队列捞);库压大 → 调小 `STREAM_APPROVAL_RECONCILE_BATCH`
950
971
  (代价是收敛变慢,靠队列轮转保证下轮接着扫)。
951
972
  - **停驻行最长活多久(local 文件后端与 SQL 店同一公式)**:一张 pending 的 checkpoint 行在 `max(createdAt + APPROVAL_TERMINAL_BACKSTOP_MS, deadline + 1h)` 到点后由后台 reaper(每 `REAP_INTERVAL_SEC`,缺省 60 s 一拍)expire;`deadline` 只在配了审批 SLA / TTL 时有,没配就是 `createdAt + 后备上限`。旋钮缺省 30 天、下钳 60 s、非有限值落缺省。到点的行先 expire,挂起的 run 行随后由 reaper 翻成 `failed`(`errorCode: approval.expired`)。所以「老停驻行在 local 后端最长活多久」= 后备上限(缺省 30 天)+ 最多一拍;要缩短就调 `APPROVAL_TERMINAL_BACKSTOP_MS`(两店同辖)。
973
+ - **审批账本保留多久(`APPROVAL_ASK_RETENTION_MS`,缺省 `2592000000` = 30 天;local 文件后端与 SQL 店同一条律)**:审批在盘上有三本账 —— 问账(SQL 的 `approval_ask` / `approval_batch`,local 的
974
+ `<数据根>/approval-asks/asks.jsonl`)与 local 审计账(`<数据根>/approval-ask-audit/ledger.jsonl`,`GET /v1/approvals` 的 `crashConverged` 读它)。一问**结了**且它最后一次动静早于
975
+ `now − APPROVAL_ASK_RETENTION_MS`,它在三本账里的全部记录由维护 tick 整问删掉。「结了」= 该问在它那本账里到了终局,**且它挂靠的东西都不再能动**:挂靠 = 发起它的 run、宿主会话、
976
+ 发问那条腿的会话;还能动 = run 账上在跑 / 停驻(`running` / `suspended` / `needs_review`)的 run、后台子代账上在跑 / 停驻的子代(子代的问记在宿主 run 与宿主会话名下,可以比宿主 run 活得久),
977
+ 以及每一只待决的 checkpoint(任何一种停驻都是一只待决 checkpoint,它的会话 = 停驻那条腿的会话;读后端自己的 checkpoint 店,不看这一世开没开 `DURABLE_APPROVAL`,门型读不出的待决行也算)。
978
+ 这样的问多老都不删 —— 它们还会被读(会话授权的「本会话放行」只在这次调用在这条腿上没有任何问行时才代答;停驻问之后的迟到决议要审计账里的前置事实)。
979
+ 问账删终态(`DECIDED` / `PARKED` / `DENIED` / `VOID`)且不是 provisional 的行、以及问走光了的批行;审计账删不再欠起服收敛答案的问(活卡、批了但腿还没闭的问还欠,多老都留),
980
+ 按 taskId 记的腿记录在没有留下的问还认它、它也不在挂靠集里时随之删,认不出的行原样留,留下的行逐字节不变(原子换文件,0600;换文件之后的目录 fsync 失败不回滚,
981
+ 计入 fail-open 计数 `server.approval-ask-audit.rotate-dir-fsync-failed`)。
982
+ 非终态的问行(`STREAM_PENDING` / `PARKING`)永不按龄删 —— 它们归对账器与起服收敛。节律:首拍即清扫一次,之后每小时至多一次;每次先从 run 账、后台子代账与 checkpoint 店列出还能动的东西(任一读不出 ⇒ 这次不删),
983
+ 问账一批至多 500 行、一次清扫删到不满一批为止。读数:起服一行 `approval_ask_retention`(`windowMs`;`0` = 关),真删了东西时一行 `approval_ask_retention_swept`
984
+ (`cutoffMs` / `asks` / `batches` / `auditAsks` / `auditRecords`;半途失败时已经删掉的照样打,带 `incomplete: true`)+ 计数器 `approval_ask_retention_rows_deleted_total{ledger=asks|batches|audit}`。
985
+ **关法**:`APPROVAL_ASK_RETENTION_MS=0` = 永久保留(要在升级后的第一次清扫之前设 —— 首拍即扫);正值低于 1 天钳到 1 天并在启动告警里点名(刚结束的 run 的客户端还可能对它的卡重试 / 晚答,
986
+ 一天吸收这一段);负值 / 非整数 / 非数拒启。run 结束超过窗之后才对它的卡作答 ⇒ 决断口与流内审批口答 404。
952
987
 
953
988
  **无人值守政策(`UNATTENDED_APPROVAL_POLICY`,7.34.0 起)**
954
989
 
@@ -987,8 +1022,8 @@ UNATTENDED_APPROVAL_POLICY=park
987
1022
 
988
1023
  - 审批面 `POST /v1/approvals/:sessionId/decide {decision:"approve", remember:"session"}` 与活卡的 `allow_session` 记下的放行,两处兑现 —— **谁问的,谁读它的答案**:
989
1024
  本服务自己的问在它们自己的层上、出问之前读,命中就不问:`APPROVAL_REQUIRE` 名单的问(7.119.0 起;卡面带「本会话放行」,回执 `rememberApplied:true`,
990
- 同一会话下一次调用名单门不出问,引擎看到的只是「这次调用没出问」)、`run_workflow` 问句、acceptEdits 的域外写;**引擎自己的问**(`default`
991
- 模式下的每一次写、证不了只读的 shell 命令、没人表态的服务器工具)由服务的**审批席**(工具审批协调器)在下一次到达时代答。后一半要审批席在场:
1025
+ 同一会话下一次调用名单门不出问,引擎看到的只是「这次调用没出问」)、`run_workflow` 问句;**引擎自己的问**(`default`
1026
+ 模式下的每一次写、acceptEdits 下写根外的写、证不了只读的 shell 命令、没人表态的服务器工具)由服务的**审批席**(工具审批协调器)在下一次到达时代答。后一半要审批席在场:
992
1027
  `TOOL_APPROVAL_ENABLED` 开(多租户缺省关);不想要活卡(纯 park 部署)再配 `STREAM_ASK_WINDOW_MS=0`。
993
1028
  名单的问在引擎眼里仍是 `ask_rule`:`permissionMode: "bypassPermissions"` 放不掉、auto 模式的分类器答不了 —— 免问只来自这一会话里人亲口说过的「本会话放行」。
994
1029
  - 代答不到的:`APPROVAL_DENY` / 敏感路径守卫(是拒,不是问);`APPROVAL_NEVER_AUTO` 与耐久预算耗尽的问(「恒问人」,回执 `rememberApplied:false`);
@@ -1081,7 +1116,7 @@ WRITE_PROTECTED_TABLE_REPLACE='[{"name":".envrc","kind":"basename"}]' # 整表
1081
1116
 
1082
1117
  ```bash
1083
1118
  READ_FACE=open # 词表 open|roots;坏词启动期响亮拒
1084
- # 不设:单用户 turnkey(REQUIRE_PRINCIPAL 未设)⇒ open;多租户 ⇒ 不传给引擎(引擎默认 roots)
1119
+ # 不设:隔离型车道(e2b / k8s / local-docker)⇒ open(来源 lane);否则单用户 turnkey(REQUIRE_PRINCIPAL 未设)⇒ open;多租户 ⇒ 不传给引擎(引擎默认 roots)
1085
1120
  READ_DENY_PATTERNS='[{"pattern":"**/secrets/**"},{"pattern":".ssh","caseSensitive":true}]'
1086
1121
  ```
1087
1122
  - **`READ_FACE`**:READ 工具面(读文件/遍历/搜索)的部署级姿态。引擎默认 **`roots`** = 读被夹在任务的
@@ -1091,9 +1126,15 @@ READ_DENY_PATTERNS='[{"pattern":"**/secrets/**"},{"pattern":".ssh","caseSensitiv
1091
1126
  **路径段窗**匹配:pattern 按 `/` 分段,`*` 是唯一元字符(段内通配,不跨段),整段序列可命中路径**任意
1092
1127
  位置**(`.ssh` 同时盖住 `~/.ssh/…` 和别处的 `.ssh`);默认 ASCII 大小写**不敏感**,单条 `caseSensitive:true`
1093
1128
  退出。这些条目**叠在** core 内建 deny 集之上(内建先、追加后,重复折叠),不是替换。
1129
+ ⚠️ **7.132.0(引擎 7.52.0)起 shell 递归读(`grep -r` 一类)只按命令点名的根判这张表**:既不以 `/` 起(根锚,如 `<工作区根>/secrets/**`)、也不以 `~` 起(家锚,如 `~/secrets/**`)的
1130
+ 拼写行(`**/secrets/**`、`.ssh`、`id_rsa*`),护不住根下面被父目录递归读到的文件(点名那个目录 / 文件本身照拒)。要护住就把行写成根锚或家锚(MIGRATION 7.132.0 第 45 条);
1131
+ 起服(server 与 `run-local`)与中心热发会为这类行打**一行** warn `read_deny_unanchored_rows`(点名行与改法;运维的 `READ_DENY_PATTERNS` 与中心 read-face 的行同判,一份值第一次装进 Runner 席时点名一次;只告知,不改放行 / 拒绝、不拒启)。
1094
1132
  - **坏形拒启,不静默折默认**:坏 JSON / 非数组 / 缺 `pattern` / 编不过 core 的 `compileReadDeny`
1095
1133
  一律启动期点名拒——一个手滑的 deny 集比没有 deny 集更糟(以为挡了其实没挡)。
1096
- - **缺席的语义按 posture 分两形(7.65.0 起)**:
1134
+ - **缺席的语义先看车道、再按 posture 分两形(7.65.0 起按 posture;7.133.0 起多一臂车道)**:
1135
+ · **隔离型执行车道**(`REMOTE_EXEC=e2b` / `k8s` / `local-docker`,**7.133.0 起**,单用户与多租户同):`READ_FACE` 缺席 ⇒ 铸 **`open`**,来源 **`lane`**。沙箱内部就是模型的整个世界,读面缺省放开(`auto` 连续干活不再每碰一个根外路径就出卡);
1136
+ 写面、读拒表与审批模式不受影响。这是**车道行显式声明的信任属性**(可审计),不是技术默认:🔴 把宿主目录 / 共享卷挂进沙箱的部署,`open` 面上宿主文件零问可读 —— **要钉回收敛姿态:显式 `READ_FACE=roots`**(env 恒赢),或给那些路径加读拒行。
1137
+ 多租户沙箱部署升级后读面从 `roots` 变 `open`(单用户部署读面本来就是 `open`,只有 `source` 从 `posture` 变 `lane`)。非隔离车道(`host` / `ssh` / `adb` / `device` / 未点名)**一个字节不变**,按下面两形。
1097
1138
  · **单用户 turnkey**(`REQUIRE_PRINCIPAL` 未设 —— 一台机器一个可信主人,CC 的信任模型):`READ_FACE`
1098
1139
  缺席 ⇒ 铸 **`open`**,工作区外的读默认放行。**这是 7.65.0 的行为变更**:同一台机器升级前后
1099
1140
  「读工作区外的文件」从被拒变成放行。**要钉回收敛姿态:显式 `READ_FACE=roots`。**
@@ -1110,6 +1151,9 @@ READ_DENY_PATTERNS='[{"pattern":"**/secrets/**"},{"pattern":".ssh","caseSensitiv
1110
1151
  (每种模式都拒、不出卡)。agent-config 的十五行改写成 `~/…`(`~/.claude/settings.json`、`~/.sema/settings.json` …)、只在执行环境的 home 正下方
1111
1152
  命中 —— 工作区里的 `.claude/settings.json` 不再是内建行;🔴 `READ_DENY_BUILTIN_EXCLUDE` 里写旧行名(`.claude/settings.json`)的部署**起服拒**
1112
1153
  (core 的拒句点名新行名),改写成 `~/.claude/settings.json`。
1154
+ 🔴 **7.132.0(引擎 7.52.0)**:`credentials` 档的 `.npmrc` / `.pypirc` 两行同样改成家锚(`~/.npmrc` / `~/.pypirc`)—— 家目录那一对照旧拒,项目里自带的同名文件
1155
+ 读得到(执行环境没声明家目录时仍拒);`READ_DENY_BUILTIN_EXCLUDE` 里写 `.npmrc` / `.pypirc` 的部署**起服拒**(拒句点名新行名):删掉这两个值(家目录那一对仍护着),
1156
+ 确要读家目录那一只就改写成 `~/…`;要项目里那一对照旧拒,把 `.npmrc` / `.pypirc` 加进 `READ_DENY_PATTERNS`。
1113
1157
  · 🔴 **命中这三张表的结局自 core 7.25.0 起是「拒」,不是「问」**(三只旋钮的名字 / 语义 / 缺席读法一字
1114
1158
  未变,变的是结局词):一条命中拒表的读在**每张脸每个时刻**都被拒 —— 不出审批卡、持久 allow 规则
1115
1159
  清不掉、值守的人点「同意」也放不掉。拒因在 `tool_end` 上:`errorCode` 与 `structured` 都是
@@ -1119,9 +1163,9 @@ READ_DENY_PATTERNS='[{"pattern":"**/secrets/**"},{"pattern":".ssh","caseSensitiv
1119
1163
  不选 / 用 `READ_DENY_BUILTIN_EXCLUDE` 删掉那一行);三问与运维面全文见
1120
1164
  `docs/DEPLOY-PREREQS.md` 的 `core 7.25.0 提货` 段。
1121
1165
  · 「这一档到底是谁定的」由 operator 面 `GET /v1/diagnostics/wiring` 的 `readFace` 段答:
1122
- `{face, source, note}`,`source` ∈ `env`(本机 env 显式)/ `center`(组织下发)/ `posture`(单机派生)/
1123
- `engine-default`(没钉,引擎当家),`note` 是指路句。
1124
- - **显式值恒赢**:优先序 `env` > `center` > `posture` > 引擎默认;引擎抬默认/扩内建集那天,没钉过的
1166
+ `{face, source, note}`,`source` ∈ `env` / `center` / `lane` / `posture` / `engine-default`:`env`(本机 env 显式)/ `center`(组织下发)/ `lane`(隔离型执行车道的契约行声明,带挂载告诫)/
1167
+ `posture`(单机派生)/ `engine-default`(没钉,引擎当家),`note` 是指路句。
1168
+ - **显式值恒赢**:优先序 `env` > `center` > `lane` > `posture` > 引擎默认;引擎抬默认/扩内建集那天,没钉过的
1125
1169
  多租户部署自动跟上(与重试上限同款「引擎当家」纪律)。
1126
1170
  - **leader 车道同席(7.54 起)**:`LEADER_ENABLED` 的 leader 编排(planner / worker / repair / conflict 四类
1127
1171
  任务)与主车道吃**同一份**四键(连同 `MEMORY_DELEGATION_EVIDENCE` / `MEMORY_PROVENANCE` / `MEMORY_CAPTURE_POLICY`
@@ -1144,10 +1188,33 @@ READ_DENY_PATTERNS='[{"pattern":"**/secrets/**"},{"pattern":".ssh","caseSensitiv
1144
1188
  `config.read_deny_swapped`,经运维日志 `engine_notice` 行落地(不上会话流);本服务另留 `read_face_swapped` 行(只报换了哪几席)。
1145
1189
  当前生效档可从 `GET /v1/capabilities` 的 `readFace` 位读
1146
1190
  (`"open"`/`"roots"`/`null`=本部署未钉,引擎默认当家;deny 表内容属策略内容,**不上**能力面);
1147
- **来源**(env / center / posture / engine-default)在 operator 面 `GET /v1/diagnostics/wiring` 的 `readFace.source`。
1191
+ **来源**(env / center / lane / posture / engine-default)在 operator 面 `GET /v1/diagnostics/wiring` 的 `readFace.source`。
1148
1192
  ⚠️ 本地 `config.d/` 车道今天**接不上**这一域(registry-core 的可移植域表里没有它),只有
1149
1193
  `CONFIG_PROVIDER=remote` 的部署走得通。
1150
1194
 
1195
+ **请求面的「只收紧」座位(7.133.0 起)—— 读拒模式 / 写保护名的加法 / 合规额外禁项 / 永不自动放行名单,每请求各加一道**
1196
+
1197
+ ```jsonc
1198
+ // POST /v1/tasks · /v1/tasks/stream · /v1/runs 的请求体
1199
+ { "objective": "…",
1200
+ "settings": {
1201
+ "readDenyPatterns": ["**/secrets/**", { "pattern": ".ssh", "caseSensitive": true }], // 加在 READ_DENY_PATTERNS 之上
1202
+ "writeProtectedExtra": [".envrc", { "name": ".corp-key", "kind": "basename" }], // 加在 WRITE_PROTECTED_EXTRA / 引擎缺省写保护表之上
1203
+ "complianceAdditionalDenies": ["workflows"], // 加在 COMPLIANCE_ADDITIONAL_DENIES 与合规档位的内建禁项之上
1204
+ "permissions": { "neverAuto": ["Bash", "mcp__figma__*"] } // 加在 APPROVAL_NEVER_AUTO 之上:命中的工具恒问真人
1205
+ } }
1206
+ ```
1207
+ - **只收紧、取并、没有放宽臂**:每一座的有效值 = 部署表 ∪ 请求列表;空数组 ≡ 没送;请求送别的行只多不少,**部署表的任何一行删不掉、绕不过**。取并在拥有部署表的那一层做一次(前三座在引擎,
1208
+ `neverAuto` 在策略折叠里),本服务不另合一份并集。
1209
+ - **`neverAuto`** 与部署的 `APPROVAL_NEVER_AUTO` 同级:命中的调用必须由真人在**这一次**答 —— `bypassPermissions`、请求自己的 `allow`、持久规则、「本会话放行」、auto 分类器都放不掉;无人可问时按无审批面的终局拒。
1210
+ 名字文法与永不匹配的名字(退役名、无前缀短名……)的判决与 env 腿同一只判官:有这类名字 ⇒ 整请求 422。审批卡的问句是 `human approval required to run "<工具名>"`;出处串(`settings.permissions.neverAuto(<命中的工具名>)`)只在引擎闸内部消费、不上卡(部署的 `APPROVAL_NEVER_AUTO` 同样)。
1211
+ - **合规禁项是「拒而不是收窄」**:请求禁了某能力而任务自己带着它 ⇒ 整个任务以 `config.compliance_denied` 失败(`workflows` + `selfOrchestration`;`mcp_servers` + 任务带 MCP 服务器;`web_fetch` + 花名册挂着 `WebFetch`)。
1212
+ ⚠️ 单用户部署的默认工具面恒挂 `WebFetch`,请求的 `excludeTools` 补偿不了(引擎先查花名册、后卸载),所以 `web_fetch` 这个词今天只在多租户部署的请求座上可用。不需要部署配 `COMPLIANCE_PROFILE`(env 腿需要)。
1213
+ - **`readDenyPatterns` 的请求侧成本界**:引擎按路径段回溯匹配,把每一颗 `*` 编成一个回溯点,每次判路径对每条模式、每个路径段各跑一遍,所以请求里**一段最多 2 颗 `*`**(每一颗都算,相邻的也逐颗算;`**` 整段、`a**b`、`id_rsa*`、`*.pem`、`*secret*` 都在内)、**整张列表合计最多 16 颗**、**最多 32 条**;超了 ⇒ 400,句子点名坐标与上限。进任务的就是写下的那一份(不改写模式)。需要更复杂或更多的行,合并成更宽的模式、改用不带星的字面形,或让运维写进 `READ_DENY_PATTERNS`(部署表不受此界)。
1214
+ - **坏值不静默丢**:不是数组 / 超帽(读拒模式 32 条,其余三座 1000 条)/ 坏条目 ⇒ 400 `request.field_invalid` + `{ field, reason: "bad_entry" | "over_cap", index | count + cap }`;续跑重放存体时同判(409 `resume_blocked_by_policy`)。
1215
+ **能力位** `GET /v1/capabilities` → `taskSettings.tightenOnlySeats`(恒真;老 server 对这四个键 400 `request.body_shape`,见位才发)。顶层请求体的 `readDenyPatterns` 仍不受理,拒句指路 `settings.readDenyPatterns`。
1216
+ 不设信任地板:只收紧的座位任何过了凭据门的调用方都能用(多租部署上登录令牌 scope 角色低的也一样);放宽成员(`allow`)的信任档准入照旧。
1217
+
1151
1218
  **可选 — 任务层「掀开写围栏」的运维天花板(`TASK_WRITE_FACE_ESCALATION`,7.95.0 起)**
1152
1219
 
1153
1220
  ```bash
@@ -1224,10 +1291,11 @@ TASK_WRITE_FACE_ESCALATION=allow # 词表 allow|refuse;坏词启动期响亮
1224
1291
 
1225
1292
  1. **出厂技能 = `SKILLS_DIR` 这一个目录**(缺席 = 包内自带的 `skills/`,按模块位置解析,不是进程 cwd)。目录里每个 `<名>.md`(frontmatter `name` / `description` / `scenarios`)
1226
1293
  或 `<名>/SKILL.md`(同目录的其余文件作附件)是一条。**设了 `SKILLS_DIR` 就整个替换包内出厂技能,不叠加** —— 想在出厂集上加几条,把包内 `skills/` 拷过去再加;
1227
- 目录不存在 = 零出厂技能(不报错)。
1294
+ 目录不存在 = 零出厂技能(不报错)。**frontmatter 读不了的技能不装**:文件第一行是围栏 `---`(行尾只许空白 / 制表)而块没有收尾 ⇒ 这一条被拒载(一行 warn `skill_refused`,点名技能与文件,`reason: frontmatter_unclosed`),
1295
+ 不会降成每个场景都有的全局技能;第一行不是围栏的文件没有 frontmatter,仍是全局技能。兄弟技能照装。
1228
1296
  2. **配置源的技能**:配了中心 = web-admin 的 skills 域;`CONFIG_PROVIDER=local` = `config.d/skills.json`(`{ "skills": [{ "name", "description", "content", "scenarios"?, "enabled"? }] }`,
1229
1297
  正文写在 `content` 里)。**按名盖在出厂技能上**(同名 ⇒ 配置源这条赢),`enabled: false` 的不装;两种配置源同一条路径(7.128.0 起;此前本地形整域不生效)。
1230
- 起服每装上一条一行 `web_admin_skill`(`overrodeBuiltin` 说盖没盖出厂的);拿不到正文的一条一行 `web_admin_skill_failed`(`reason`:`body_absent` / `read_failed`),
1298
+ 起服每装上一条一行 `web_admin_skill`(`overrodeBuiltin` 说盖没盖出厂的);拿不到正文的一条一行 `web_admin_skill_failed`(`reason`:`body_absent` / `read_failed`;**正文为空串视同缺席**:`content` 缺省或写成 `""` 的条目,本地与中心同判 `body_absent`),
1231
1299
  这一条不装、同名出厂技能保留、起服照常。重启生效(与 MCP 同:改了之后 `/health` 的 `restart.reasons` 含 `skills`)。
1232
1300
  3. **插件带的技能**(plugins 域):只补**没被占的名字**,撞名让位(起服行 `center_plugins_applied` 的 `letGo`)。
1233
1301
  4. **请求体自带的技能**(`skills[]`):只补这一 run **没被占的名字** —— 与部署的技能同名的那条被丢(一行 `user_skills_dropped`),不覆盖;
@@ -1253,7 +1321,20 @@ TASK_WRITE_FACE_ESCALATION=allow # 词表 allow|refuse;坏词启动期响亮
1253
1321
  的窗内(窗口见 `src/plugins/one-time-migrations.ts` 登记表)按升级前的读法重读一次(非对象含 `null` ⇒ 没声明;对象里过不了形的成员剔、好的留),产物仍过同一只判官 ⇒ server 照挂、
1254
1322
  一行运维 warn `mcp_tool_axes_read_as_before_upgrade`(`server` / `migration` / `problem` / `note`);重读的产物仍判不过、或窗已关 ⇒ 这一条 server 整条不进,
1255
1323
  运维 warn `mcp_injection_dropped`(`malformed_entry` / `field: toolAxes`)+ 用户面通告 `mcp.injection_dropped`(续跑家族同投);`POST /v1/capabilities/mcp/probe` 同拒同句,
1256
- 判官受理的声明与提交腿同读(只有空条目的工具 = 没声明,受理)。`a2aPeers[].toolAxes` 不变。
1324
+ 判官受理的声明与提交腿同读(只有空条目的工具 = 没声明,受理)。
1325
+ - **请求体自带的 `a2aPeers[].toolAxes`**(键 = 对端的远端 skill id)读同一只判官(引擎根导出的 `readToolAxes`,A2A 一面;形同上):读不出 —— 出词表、`egress` 非布尔、
1326
+ **未知成员**、某只 skill 的值不是对象、`__proto__` 键、不是纯数据 —— ⇒ 那一条对端整条不挂、点名进运维 warn `a2a_injection_dropped`,任务照跑(与这条腿上其余字段坏形同一处置)。
1327
+ 此前本服务对 A2A 一面用自己的读法、未知成员静默忽略;引擎的准备门对同一份声明本就整单拒,两层现在是同一只判官、同一个方向。
1328
+ 续跑重放升级前存下的请求体时与 `mcpServers[].toolAxes` 同一只一次性迁移 M19:窗内判官判不过的那一格按 7.131.x 的 A2A 读法重读一次(未知成员剔、三轴留,产物再过同一只判官),
1329
+ 对端照挂、一行运维 warn `a2a_tool_axes_read_as_before_upgrade`(`peer` / `migration` / `problem` / `note`);已知轴写坏(出词表、`egress` 非布尔、某只 skill 的值不是对象、
1330
+ 整份不是对象)的对端 7.131.x 当时就整条丢了,重读照旧整条不挂(`a2a_injection_dropped` 点名);窗关之后回到上面的读法(整条不挂)。
1331
+ - **请求体自带的 `mcpServers[].transport.env` / `transport.headers`**(「名 → 串」记录,与 `a2aPeers[].headers` 同一只读法):名叫 `__proto__` 的成员、值不是串的成员、
1332
+ 整只不是纯数据 ⇒ 新提交 `400 request.field_invalid` 点名第一处(`__proto__` 成员点名成员级路径 `…headers.__proto__`;值不是串 / 整只读不成点名那个键,原因句点名成员)。
1333
+ 续跑重放存量请求体(JSON 文本铸出来的记录)时复现 7.131.x 的读法,唯一偏离是引擎拒的 `__proto__` 成员:记录里有值不是串的成员 ⇒ 整键不交(7.131.x 原跑就是这样挂的,不半程换身份);
1334
+ 等价只对 JSON 存体成立 —— 直接递 `Map`、类实例、`process.env` 的嵌入方按上面的更严读法(7.131.x 对带自有串属性的类实例照收);
1335
+ 只有 `__proto__` 名的成员坏 ⇒ 剔掉它、其余照交。server 照挂,运维 warn `mcp_transport_members_dropped`(`server` / `lost` / `lostTotal?` / `note`)点名没交出去的键 / 成员与整键没交的
1336
+ `transport.args` / `transport.principalHeader`。A2A 对端 `headers` 同读:值不是串 ⇒ 对端照旧不挂;只有 `__proto__` 成员坏 ⇒ 续跑照挂、剔掉它,一行 `a2a_peer_headers_members_dropped`
1337
+ (`peer` / `lost` / `note`);新提交上两形都整条不挂。
1257
1338
 
1258
1339
  **可选 — MCP 入站表单(elicitation,E23):默认关**
1259
1340
 
@@ -1483,12 +1564,20 @@ invalid_using_default` 警告 + 退回缺省值」这条臂随词表放宽一并
1483
1564
 
1484
1565
  | 键 | 缺省 | 写法 | 行为 |
1485
1566
  |---|---|---|---|
1486
- | `LSP_ENV_ALLOW` | 未设 = 空名单 = 全剥(7.100.0 行为) | 逗号分隔的**裸变量名**,如 `LSP_ENV_ALLOW=NPM_TOKEN,CARGO_REGISTRIES_MY_REG_TOKEN` | 点到的键从服务进程环境**原样**交给语言服务器(密钥形也照交 —— 点名即你显式承担把这个值交给仓里代码的后果);没点到的照旧剥,`SERVICE_AUTH_TOKENS` 这类不会因为别的键被放行而跟着进去。空串 = 未设。**形错拒启**:空段(`A,,B`、尾逗号)、非变量名形(`1BAD`、带空格、`NAME=value` 整段贴进来)⇒ 起服失败,拒句点名键与第几段,**不回显原值**。形对但本进程环境里**没有**这个名的段 ⇒ 不放行,起服一行 `config_env_entry_rejected` 只报第几段(常见成因是 `LSP_ENV_ALLOW=$NPM_TOKEN` 这种把**值**展开进来的手滑 —— `npm_` 令牌恰好是合法变量名形,所以名单原文在任何日志 / 配置目录回显里都不出现)。旋钮**原文**按凭据类对待:配置目录只回在场位,`LSP_ENV_ALLOW` 这个键本身不进语言服务器 / host shell / git 子进程(点名它自己,或点名两只沙箱名单 `E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV` ⇒ 拒启);生效名单看起服那行的 `allowed`。`REQUIRE_PRINCIPAL=true`(多租户 / 托管)⇒ 忽略名单、仍全剥,起服一行 `config_env_set_but_ineffective`(host 腿语言服务器在这一姿态下本来就不起)。只作用于 host 腿;沙箱腿的语言服务器跑在沙箱里,环境由沙箱车道自己的旋钮决定 |
1567
+ | `LSP_ENV_ALLOW` | 未设 = 空名单 = 全剥(7.100.0 行为) | 逗号分隔的**裸变量名**,如 `LSP_ENV_ALLOW=NPM_TOKEN,CARGO_REGISTRIES_MY_REG_TOKEN` | 点到的键从服务进程环境**原样**交给语言服务器(密钥形也照交 —— 点名即你显式承担把这个值交给仓里代码的后果);没点到的照旧剥,`SERVICE_AUTH_TOKENS` 这类不会因为别的键被放行而跟着进去。空串 = 未设。**形错拒启**:空段(`A,,B`、尾逗号)、非变量名形(`1BAD`、带空格、`NAME=value` 整段贴进来)⇒ 起服失败,拒句点名键与第几段,**不回显原值**。形对但本进程环境里**没有**这个名的段 ⇒ 不放行,起服一行 `config_env_entry_rejected` 只报第几段(常见成因是 `LSP_ENV_ALLOW=$NPM_TOKEN` 这种把**值**展开进来的手滑 —— `npm_` 令牌恰好是合法变量名形,所以名单原文在任何日志 / 配置目录回显里都不出现)。旋钮**原文**按凭据类对待:配置目录只回在场位,`LSP_ENV_ALLOW` 这个键本身不进语言服务器 / host shell / git 子进程(点名它自己,或点名两只沙箱名单 `E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV` ⇒ 拒启)。**点名本服务自己的凭据名**(配置目录里的 secret 型变量,如 `SERVICE_AUTH_TOKEN` / `APPROVAL_HMAC_KEYS` / `MODEL_API_KEY`)⇒ 拒启(7.132.0 起):放回在剥除之上,点到它就把刚扣下的本服务凭据交回语言服务器;拒句带引擎拒白名单的同一句。放回本身经引擎:点到的变量值若是本服务凭据的副本(换了个名),同样扣下。语言服务器要凭据 ⇒ 给它一枚自己的(值与本服务的任何凭据都不同)。生效名单看起服那行的 `allowed`。`REQUIRE_PRINCIPAL=true`(多租户 / 托管)⇒ 忽略名单、仍全剥,起服一行 `config_env_set_but_ineffective`(host 腿语言服务器在这一姿态下本来就不起)。只作用于 host 腿;沙箱腿的语言服务器跑在沙箱里,环境由沙箱车道自己的旋钮决定 |
1487
1568
 
1488
1569
  **「剥了什么」这一行**:起服(host 腿语言服务器管理器构造时)一行 `warn` `secret_env_scrubbed`,`site:"server.lsp.spawn-env"`,
1489
1570
  字段 `keys`(这次剥掉的键名)· `allowed`(当前放行名单)· `hint`(出路句,点名 `LSP_ENV_ALLOW=NAME,NAME`)· `summary`;**值永不进日志**。
1490
1571
  放回的键不算剥除(不在 `keys` 里、不进计数 `secret_env_scrubbed_total`);同一个键每进程只报一次;没有剥除 ⇒ 不发。
1491
- 另两条剥除现场(模型可驱动的 host shell、project-memory 的 git 子进程)没有部署级放行旋钮,它们的同名行照旧是 `info`、不带 `allowed` / `hint`。
1572
+ 其余剥除现场(模型可驱动的 host shell `server.remote-env-host.spawn-env`、三条 git 腿、local-docker / adb 两条 CLI 腿、settings hooks 的命令腿)没有部署级放行旋钮,它们的同名行照旧是 `info`、不带 `allowed` / `hint`。
1573
+ 现场按**腿**命名(`server.<模块>.<腿>`),不按继承词:7.131.x 及以前 host shell 那条腿叫 `server.remote-env-host.inherit-scrub`,7.132.0 起改名(计数器 `secret_env_scrubbed_total{site}` 与日志字段 `site` 同改,无别名)。
1574
+
1575
+ **每一条宿主子进程腿的 env 都经同一只组合(7.132.0 起射程写全)**:本服务在宿主上起的每一只子进程,env 都由引擎按这条腿的继承词组合,本服务自己的凭据(起服声明的名与值)任何词都不交。
1576
+ 腿的继承词按「子进程里跑的是谁选的代码」定:模型命令(host shell:交任务自己的词,缺省形状剥)、仓里的钩子与凭据助手(project-memory / center-plugins / leader 三条 git 腿)、
1577
+ 执行仓代码的语言服务器、bake-runner 的配方脚本 ⇒ 形状剥(语言服务器与 bake-runner 另有点名放回);请求带来的 settings hooks 命令 ⇒ 严格名单(`PATH` / `HOME` / `SHELL` /
1578
+ `USER` / `LOGNAME` / `TERM` / `TZ` / `LANG` / `LC_ALL` / `LC_CTYPE` / `LC_MESSAGES` / `TMPDIR`,外加请求自己的 `settings.env`);local-docker 车道的 `docker` CLI 与 adb 车道的 `adb` CLI
1579
+ (部署方装在宿主上的二进制,命令本身跑在容器 / 设备里)⇒ 整份环境**减**本服务凭据 —— CLI 与它拉起的凭据助手 / CLI 插件 / 常驻 adb server 要读的 `DOCKER_*` / `AWS_*` /
1580
+ `ADB_VENDOR_KEYS` 照交(形状剥会剥后两类)。7.131.x 及以前这两条 CLI 腿原样继承整份环境(本服务凭据在内)。
1492
1581
 
1493
1582
  **🔴 模型可驱动的 host shell 没有点名放回旋钮(刻意,7.104.0 写明)**:凭据形变量不交给模型能驱动的 shell —— 这条腿恒走缺省剥除
1494
1583
  (本服务不给它传点名名单),部署配置里没有任何键能改它的继承模式;`inheritEnv:[names]` 是**嵌入方**用本包构造执行环境时的选项
@@ -1513,8 +1602,8 @@ invalid_using_default` 警告 + 退回缺省值」这条臂随词表放宽一并
1513
1602
  `CONTEXT_TOKENS`)与两个开关(`DB_FOREIGN_KEYS` `JSON_SORT_KEYS`)不再被剥,三条子进程腿同时生效(7.100.0 上这五个 7.30.1 误剥的名
1514
1603
  `MAX_NEW_TOKENS` / `MAX_COMPLETION_TOKENS` / `MAX_BATCH_PREFILL_TOKENS` / `DB_FOREIGN_KEYS` / `JSON_SORT_KEYS` 需要点名放行,现在不用)。
1515
1604
  **精确名,不是形**:`USERS_MAX_NEW_TOKENS`、`MAX_NEW_TOKENS_2` 这类装饰过的变体照剥。引擎自己的「剥了什么」通告码
1516
- `config.secret_env_scrubbed` 只在引擎**自己**剥的子进程上发;本服务六条宿主腿(host shell / project-memory / center-plugins / leader 三条 git 腿 / 语言服务器 /
1517
- bake-runner)都交本服务剥好的环境,所以运维面看的是上面这行
1605
+ `config.secret_env_scrubbed` 只在引擎**自己**剥的子进程上发;本服务九条宿主腿(host shell / project-memory / center-plugins / leader 三条 git 腿 / 语言服务器 /
1606
+ bake-runner / local-docker 与 adb 两条 CLI 腿 / settings hooks 命令腿)都交本服务组合好的环境,所以运维面看的是上面这行
1518
1607
  `secret_env_scrubbed`,不是那只通告码。这只通告码**每进程一次**、引擎里几处剥除共用一闩(工具 shell / 语言服务器 / 7.103.2 起还有 `runSpec` 的
1519
1608
  oracle 门子进程,core 7.33.8):oracle 门若先于工具 shell 剥,通告只进 oracle 门那一处(`RunSpecOptions.onNotice`,没接就进 console),接在执行环境上的
1520
1609
  结构化通告收不到。本服务不调 `runSpec`,不受影响;嵌入引擎、自接 `runSpec` 的宿主给它交同一只 `onNotice`。
@@ -1551,6 +1640,15 @@ bake-runner 要用 ⇒ `LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW` 点名放回。
1551
1640
  `config_env_forwarded_multi_tenant`(`env` = 旋钮名、`entries` = 名单段数、`note` = 姿态句:这张 worker 级名单进每个租户的沙箱),
1552
1641
  不回显名单原文;单用户部署不出这一行。
1553
1642
 
1643
+ **区域包源 `SANDBOX_PKG_SOURCE=custom` 的六只显式地址不得带 userinfo(7.133.0 起)**:`custom` 把 `SEMA_APT_MIRROR` / `SEMA_PIP_INDEX` / `SEMA_UV_INDEX` / `SEMA_UV_PY_MIRROR` /
1644
+ `SEMA_NPM_REGISTRY` / `SEMA_NODE_MIRROR` **原样**写进每个 e2b VM / k8s pod 的环境(并映射到 `PIP_INDEX_URL` / `UV_*` / `NPM_CONFIG_REGISTRY` / `COREPACK_NPM_REGISTRY` /
1645
+ `NVM_NODEJS_ORG_MIRROR` / `NODE_MIRROR`),沙箱里模型驱动的命令 `env` 读得到,所以**凡注入模型可见环境的 URL 不得带 userinfo**:地址带 `user:pass@`(或是判官判不了无凭据的形 ——
1646
+ 无 scheme 的 `令牌:口令@host:端口`、`file://`、无 scheme 的 `host:port`、值里带反斜杠(URL 标准把 `\` 读成 `/`,所以 `https://用户\:口令@host/…` 在判官眼里没有 userinfo,pip 一类却读得出;读法不唯一的值不收);
1647
+ 这个座位只收层级式 http(s) 地址)⇒ **起服拒启**,拒句点名 env 名、只回显 `scheme://host[:port]`(**不回显用户名与口令**),
1648
+ 并给两条出路:① 地址改成不带凭据的形,凭据走包管理器自己的通道 —— 上面三只沙箱名单旋钮点名一个令牌变量(点名即承担:沙箱里所有进程含模型都读得到;pod 容器 env 里它与派生值
1649
+ 同名时名单赢),或把按主机记凭据的文件烤进镜像(npm 的 `.npmrc` 里 `//主机/:_authToken=…`、pip / uv / curl 的 `.netrc`;**别**写进 `pip.conf` 的 `index-url`,注入的 `PIP_INDEX_URL` 比配置文件优先);② 源不需要认证就去掉 userinfo。不剥、不改写、不静默继续。**只在 `custom` 时判**(`global` / `cn` / `none` 不注入这六只);读与判都在起服那一次,
1650
+ 车道的 env 合成处只读起服读到的配置位。请求面没有任何包源地址键(只有部署面);名单点名放行的值不受这条律管(照旧原样交出)—— 但名单点名的恰是这六只 `SEMA_*` 之一、profile 为 `custom` 时,读口照判环境里的值。
1651
+
1554
1652
  **`CONFIG_REQUIRE_ROSTER=true`(3.1.0,可选,缺省关)**:远程 registry 非 dryRun 车道的 fleet worker
1555
1653
  可布防「roster 落地前拒接计费提交(503)」——3.0.0 起 MODEL_ID 必填,每个 worker 都带 boot 模型起服,
1556
1654
  旧的「无 env 模型即等 roster」推断失效;要那个保护语义现在需显式声明。
@@ -1567,9 +1665,13 @@ bake-runner 要用 ⇒ `LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW` 点名放回。
1567
1665
  | `Authorization: Bearer <SERVICE_AUTH_TOKEN>` | 设了 `SERVICE_AUTH_TOKEN` 时,除 `/health` / `/readyz` 外的所有请求 | **谁有权调本服务**(OA 后端持有,服务到服务) |
1568
1666
  | `x-agent-principal: user:42` | 设了 `REQUIRE_PRINCIPAL=true` 时 | **代表哪个终端用户**(决定 session 归属 + 记忆隔离;**绝不从 body 取**) |
1569
1667
 
1570
- > **dev-open 部署的主体头是过滤 / 展示用,不是隔离。** 没配 `SERVICE_AUTH_TOKEN(S)`、没配 `TRACE_TOKEN`、没开 `REQUIRE_PRINCIPAL` 的部署上,`x-agent-principal` 谁都能自报:
1571
- > 会话列表与单会话的读写口(视图 / head / 事件流 / 改名置顶 / 记忆状态与抹除 / 后台任务 / MCP 重连 / fork / delete)都不按它钉人,带谁的头都读得到同一批会话。
1572
- > 它仍决定新会话记在谁名下、记忆按谁的 scope 走;规则口(`…/policy`)与 `…/notify` 用它选落哪一把属主键。要按用户隔离,给部署设上面三者之一(典型:service token + 写主体头的网关)。
1668
+ > **dev-open 部署的主体头是过滤 / 展示用,不是隔离。** 没接任何身份来源(没配 `SERVICE_AUTH_TOKEN(S)`、没装中心鉴权桥、没开直连门 —— 即 `/health.auth.kinds` 为空)、
1669
+ > 也没配 `TRACE_TOKEN`、没开 `REQUIRE_PRINCIPAL` 的部署上,`x-agent-principal` 谁都能自报:
1670
+ > 会话列表与单会话的读写口(视图 / head / 事件流 / 改名置顶 / 记忆状态与抹除 / 后台任务 / MCP 重连 / fork / delete)都不按它钉人,带谁的头都读得到同一批会话;`/metrics` 族不要凭据(没配 `METRICS_TOKEN` 时)。
1671
+ > 它仍决定新会话记在谁名下、记忆按谁的 scope 走;规则口(`…/policy`)与 `…/notify` 用它选落哪一把属主键。要按用户隔离,给部署设上面几者之一(典型:service token + 写主体头的网关)。
1672
+ > **装了中心鉴权桥(`SEMA_WEB_ADMIN_URL` 在位即默认开,或 `AUTH_BRIDGE_ISSUER`)就不是 dev-open**(7.132.0 起;此前这一形被读成 dev-open):带登录令牌的调用方按令牌里的身份钉人,
1673
+ > 不带任何凭据的列表 `401 auth.unauthorized`、`/metrics` 族 `401`(要 `METRICS_TOKEN`)。仍成立的边界:没配 service token 的部署上,不带登录令牌的请求自报的主体头仍按身份读
1674
+ > (与 `REQUIRE_PRINCIPAL` 无 token 形同读)—— 要让「没令牌就没有身份」成立,配 `SERVICE_AUTH_TOKEN`(全局凭据门:每个请求要么带 service token、要么带登录令牌)。
1573
1675
 
1574
1676
  | env 键 | 缺省 | 说明 |
1575
1677
  |---|---|---|
@@ -1584,17 +1686,32 @@ bake-runner 要用 ⇒ `LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW` 点名放回。
1584
1686
  > 调用方身份,**不**叫你再去带 service token。逐字句子见 `docs/ASSISTANT-WIRE-CONTRACT.md` 附录 A.1 码表。壳(sema CLI)接 sema-up 引擎的
1585
1687
  > 令牌交法见 `deploy/sema-up/README.md`「壳接入」。
1586
1688
 
1689
+ **整只请求体的上限**:8 MiB;**TiDB 部署更矮** —— 一份提交体要整只落进库的一行,上限 = 库的单行上限(TiDB `txn-entry-size-limit`,缺省 6 MiB)− 64 KiB,
1690
+ 缺省约 5.94 MiB(6 225 920 字节,按体的 JSON 字节判)。超了 ⇒ `413 request.payload_too_large`,句子点名本部署的上限,体另带 `field:"body"` / `reason:"too_large"` / `count`(这一腿量到的字节)/ `cap`(这一腿的门限)(7.133.0 起;`images` 超 20 张或单张内联图超帽的 413 同组键:`field:"images"` / `"images[i].data"`;读体时撞 8 MiB 上限的 413 体没读完、量不出送来多少:`field:"body"` / `reason:"too_large"` / `cap`,没有 `count`;写续跑输入那一笔被库拒的 413 只有 `field:"body"` / `reason:"too_large"`)。服务连上库时从库读这个数(读数在
1691
+ `GET /v1/diagnostics/wiring` 的 `sqlEngine.maxRowBytes`);要收更大的体,运维抬 TiDB 的 `performance.txn-entry-size-limit`(或 TiDB 7.6+ 的 `tidb_txn_entry_size_limit`),
1692
+ 并让库账号读得到它(`CONFIG` 权限,或设那只会话变量;读不到时按 TiDB 缺省判)。同时并入库的**语句**上限(`max_allowed_packet`,InnoDB 与 TiDB 都有;读数在 `sqlEngine.maxPacketBytes`,是最近一次初始化的那条数据库连接的值 —— 受理口按它预判,写入那条连接上的库仍是终判)。两堵墙各量各的、量店真正发出去的东西:行墙腿量体的 JSON 字节 ≤ 单行上限 − 64 KiB(行里存原字节);
1693
+ 包墙腿量**转义后的语句字节** ≤ 包墙 − 1 KiB(体经驱动以字符串字面进语句,`"` `'` `\` 与控制字符各多一个字节,引号 / 反斜杠密集的 JSON 比自己的字节数长,至多 2×),`null` 的墙不参与,句子点名真正拒绝的那一堵。
1694
+ PostgreSQL 只有 8 MiB 这一道;InnoDB 没有单行墙,缺省 16 MiB(MariaDB)包墙下引号密集的大体会在 8 MiB 之内就被包墙拒,缺省 64 MiB(MySQL 8)之下仍只有 8 MiB 这一道。
1695
+ 连接池最近一次连接初始化被库拒(读不出库的事实;到下一条连接初始化成功为止,通常秒级)时,MySQL 协议部署按最保守的**假定**墙判(行墙 6 291 456 = TiDB 缺省、包墙 16 MiB,与 SQL 快照 blob 的缺省帽同一个读法),
1696
+ PostgreSQL 仍只有 8 MiB。这一窗的 413 句子说是假定(`the database's … limit was not read (engine facts not read yet, or the limit could not be read); refusing conservatively at … — retry shortly; if it persists, …`),不点名库的上限与旋钮;两形体上都带 `wallSource`,
1697
+ 只看拒它的那堵墙的数字是不是库自己报的:`"reported"` = 库报的墙,缩小或请运维抬;`"assumed"` = 不是库报的 —— 事实没建立的窗口里稍后重试按库报的墙重判(InnoDB / MariaDB 上多半就过,TiDB 上同一份体多半仍超);
1698
+ 事实在场、但这一堵墙读失败退了文档缺省(TiDB < 7.6 又无 `CONFIG` 权限读不到单行上限;`max_allowed_packet` 读不出)的不会自己变真,请运维让库账号读得到那个上限(`GET /v1/diagnostics/wiring` 的 `sqlEngine.wallSources` 逐堵说出处)。
1699
+ `PUT /v1/sessions/:id/sync/blobs/:hash` 超 SQL 快照 blob 帽的 `413 blob_too_large_for_sql` 同样带 `wallSource`,词多一个 `"operator"`(帽是你设的 `SNAPSHOT_BLOB_SQL_MAX_BYTES`),句子按三形分:旋钮 / 库报的墙 / 没读到的假定墙。
1700
+ 受理口放行、写续跑输入那一笔被库以「一行装不下」拒时(开了 durable 审批的部署)答同一个 413,出路按拒它的那堵墙给(TiDB 单行墙 ⇒ 抬 `txn-entry-size-limit`;语句墙 ⇒ 抬 `max_allowed_packet`;
1701
+ PostgreSQL 硬上限 ⇒ 只有缩小),运维日志一行 `submit_ctx_row_too_large`(`wall` 点名那堵墙);刚认领的 run 当场撤掉,撤认领的库写自己也失败时一行 `submit_ctx_run_release_failed`(带步名)
1702
+ 并计 `submit_ctx_run_release_failed_total{step}`,会话到 `REAP_RUN_STALE_SEC` 才放。
1703
+
1587
1704
  **请求体字段(常用;受理**闭集**以 `docs/ASSISTANT-WIRE-CONTRACT.md` §1 为准 —— 本表不再重抄一份):**
1588
1705
  | 字段 | 必填 | 说明 |
1589
1706
  |---|---|---|
1590
1707
  | `objective` | ✅ | 要做什么(自然语言)。**唯一必填。** |
1591
1708
  | `sessionId` | — | 续聊:带上次返回的 `sessionId`,服务端自动 wake 历史。**只收 canonical uuid**(任意版本,小写;与会话口同一只形判):自选名 / 业务键 / 空串 ⇒ `400 request.id_invalid`,缺省请**省略这个键**(服务端现铸 uuid)。想要好记的名字,自己记 name → uuid 的映射 |
1592
- | `images` | — | 图文输入 `[{data,mimeType}|{url}]`(模型需支持 vision) |
1709
+ | `images` | — | 图文输入 `[{data,mimeType}|{url}]`(模型需支持 vision)。**随提交的这条目标一次送达**:续跑 / 投递腿(审批 / 资源停车 / wake / plan_review 续跑、在宿主会话上新起的一跑)重建配置时不再带它(与 `userAttachments` 同律;图在首跑已是会话里的消息) |
1593
1710
  | `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` |
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` |
1711
+ | `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` 是席位能承载的供给,不按场景裁 —— **这个场景在本部署上受理哪些**读 `GET /v1/capabilities/scenarios/<name>` 的 `attachmentSupplies`(7.133.0 起;席位 `supplies` 的子集,不含 `path` 的场景发 path 条目必同码 400,发之前读它,不必撞 400);读不到的文件不拒,它的消息里说明没附上,结局见流上目标那一帧 `human_input.attachments`。只随目标走:steer / interrupt 带它 400,续跑不重发。带附件的提交在第一次回复出来之前被切断(halt / interrupt)时,这次 run 不落回退锚 —— 拿它的 taskId 去 `resumeAt` ⇒ `404 resume_at.unknown_event`,请显式给 resumeAt。与 `attachmentIds` 分工:那条把文件放进工作区让模型按需去读(内容不进上下文),这条把内容直接送进上下文。能力位 `userAttachments` |
1712
+ | `scenario` | — | 省略时取本部署的 `DEFAULT_SCENARIO`(工厂缺省场景名 = **`code`**——CC 编码 persona 蒸馏版,default 全量工具面,终验旋钮独立;`default` 场景**已非出厂缺省**,要用它须显式传 `scenario:"default"`,或部署方显式设 `DEFAULT_SCENARIO=default`)。可选内建名:`default`(通用/助理场景,域中性 persona,CC agent loop 全量工具面)/ **`cowork`**(办公向场景,**7.133.0+**:`default` 的工具面去掉编码类工具 —— 不带 `Bash` / `Edit` / `Write` / `NotebookEdit` / `RepoMap` / `LSP` / `EnterWorktree` / `ExitWorktree` / `Monitor`,留 `Read` / `Glob` / `Grep` 文件读、浏览(WebFetch / WebSearch)与连接器挂载;role 层是 core 的 Cowork 角色基底,调用方的 `systemPrompt` 照样整替它;仍挂手,所以附件 `path` 在有执行环境的部署上照常受理;你自己请求体里的 `excludeTools` 与它取并,只会多卸)/ `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` |
1596
1713
  | `repo` | — | `code-review`/`scan` 必填,见 §5(7.126.0 起 `council` / `debate` / `lenses` / `rounds` / `team` / `businessContext` 是未知键 ⇒ 400:它们喂的评审团腿与 `discuss` 场景随引擎 7.49.0 删掉团队运行时一并删除) |
1597
- | `systemPrompt` / `appendSystemPrompt` | — | 调用方自带的系统提示 / 追加段(各 ≤16384 字符,超限 400 `request.field_invalid`;`appendSystemPrompt` 必须非空串)—— 任务自带时优先于角色 / 全局缺省(见 §模型角色) |
1714
+ | `systemPrompt` / `appendSystemPrompt` | — | 调用方自带的系统提示 / 追加段(各 ≤16384 字符,超限 400 `request.field_invalid`;`appendSystemPrompt` 必须非空串)—— 任务自带时优先于角色 / 全局缺省(见 §模型角色)。`systemPrompt` 是**角色层**:它整替所选角色的基底(`code-review` 场景把自己的评审员文本放在它前面),宪法、行为段、记忆段由引擎组在它之后;追加段恒排在宪法之后。逐字含引擎提示段的提示(一份已组装的提示)⇒ 400 `prompt.assembled_role_refused`(7.132.0 起) |
1598
1715
  | `outputStyle` / `settings.outputStyle` | — | 输出风格**两个座位、两件事**:风格的**正文**(指南全文)走 `settings.outputStyle`(折进系统提示的追加段);**每轮一句**走顶层 `outputStyle: { name, turnReminder? }`(7.121.0+,能力位 `outputStyle`)—— 原样交引擎,引擎每个回合用自己的标记帧说「`<name> output style is active. <turnReminder>`」(`turnReminder` 缺席用引擎缺省句),流上回声 `steering_injected` `source:"output_style"`。只在非缺省风格时声明顶层键;它的值形由引擎判(`name` 非空、≤256 字;`turnReminder` 在场即非空串;不许别的键),坏值这一轮终态 `failed`、码 `config.output_style_invalid` |
1599
1716
  | `model` | — | 本次用哪个模型:只能点服务端**已配置目录**里的名字 / 档位词 / 模型 id(目录见 `GET /v1/models`;请求体带不进新模型)。未知 ⇒ 400 `request.unknown_reference`(带 `available` 列表);目录里有、但本部署服务不了 ⇒ 400 `request.model_unavailable`;空串 / 非字符串 ⇒ 400 `request.field_invalid` |
1600
1717
  | ~~tools~~ | 🚫 | **不是受理键**(传了 ⇒ 400 `request.body_shape`,`unknownKeys` 点名)——工具面走场景装配;按请求只能**减**:`allowTools`(7.126.0 起;引擎的可用性选择器,只留列出的,原样透传)/ `excludeTools` / `settings.permissions` 的 deny · ask(`allow` 7.115.0 起是 allow 规则:给出「有人表态」,回答没人表态的服务器工具 / A2A 调用;不加工具、不放掉任何一层自己的问) |
@@ -1733,15 +1850,16 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
1733
1850
  ## 7. 其它接口(按需)
1734
1851
  | 端点 | 用途 |
1735
1852
  |---|---|
1736
- | `GET /v1/sessions/<id>` | 审计回溯:当前上下文 + 摘要(owner 校验);消息里模型 / 工具产出的文本与 live 帧、账本帧同一只脱敏(密钥形换占位,其余字节不变);Read 图像 / PDF 的媒体卡(工具结果消息的 `details`)的 `file.base64` 是字节编码,逐字 |
1853
+ | `GET /v1/sessions/<id>` | 审计回溯:当前上下文 + 摘要(owner 校验);消息里模型 / 工具产出的文本与 live 帧、账本帧同一只脱敏(密钥形换占位,其余字节不变);Read 图像 / PDF 的媒体卡(工具结果消息的 `details`)的 `file.base64` 是字节编码,逐字。**委派子代的转录**也走这一口:`<id>` = 子代帧 / fleet 子代行上的 `transcriptId`(未压缩过的子代首条用户消息是委派 prompt;子代跑过的工具是 assistant 消息里的 `toolCall` 段 + 同 `toolCallId` 的 `toolResult` 行;整段逐字条目走 `…/sync/entries`)。体顶层加性键 `agent: { id?, parentSessionId?, rootSessionId? }`(**7.133.0+**)说这是哪只子代:只投影会话行上存下来的委派元组,`id` 与 fleet 行的 `id`、`GET /v1/agents/roster` 的 `handle` 同一个串;键缺席 = 普通会话 / 老 server / 会话店不存元组(local 后端),读作「没存」、回落 fleet 行 / 名册上的 id。子代会话的属主是铸行那一刻它所在委派树的根会话的属主(**7.133.0+**,写下那一行时按当时读到的写入;此前 SQL 后端上子代会话无主、谁都读得到)⇒ 用发起子代的那位主体读;别的主体与不存在的 id 同一个 `404 not_found.session`。属主为空的委派子代行(根会话本就无主,或升级前写下、还没回填)只有 fleet 调用方读得到(出示 service 凭据、不带主体头;dev-open 部署不钉人),任何具名主体、没有 service 凭据的无身份调用方同样 404(fork / 删除对 `default` 主的存量无主会话互认、审批豁免、notify 对这些行同样不放行;fork / 删除 / `/sync/*` 把带 service 凭据的调用方读成 fleet-wide、不看主体头,这一点不变);SQL 后端每次起服回填这些行(起服读数行 `placedSessionOwnersRemaining` 非零 = 还有没定下的,再起直到为 0,或等它们下一次停驻)。推不出属主的子代写成属主 `_orphan:<id>`,只有 fleet-wide / operator 读得到:SQL 后端的子代行根会话已删时按它记录的父会话,父也删了才是 `_orphan:`;停驻时拷进宿主店的那一份(local 后端)只按根判,根会话已删 ⇒ `_orphan:`;子代被派出 / 批准之后、再次停驻读根之前根会话被删并由别人以同一 id 重新登记 ⇒ 其下的子代再次停驻被否决(读过根之后才换的,写进去的仍是读根那一刻的属主) |
1737
1854
  | `GET /v1/approvals?owner=user:42` | 高危写审批(durable,需 `DURABLE_APPROVAL=true`):**operator** 看待办队列(可按 owner 过滤);非 operator 只看自己的 |
1855
+ | `GET /v1/approvals/stream` | 同一队列的 SSE 推送(范围同上):首拍快照 → `pending` / `resolved` 增量(开了工具审批时另有 `live_pending` / `live_resolved`),空闲时心跳,15 分钟上限(`STREAM_MAX_DURATION` 帧后重连)。**心跳只表示流活着,不表示这一拍读过队列**:队列读不出时每次读失败发一帧 `error`(`approvals.read_unavailable`,流留着、下一拍再读,恢复后同一条流继续);某一行的存储体读不出来、重试不会好时发 `approvals.read_corrupt` 并收流(运维处置那一行后再重连)。15 分钟上限对读失败的拍同样算数 |
1738
1856
  | `POST /v1/approvals/<sessionId>/decide` `{"decision":"approve"|"deny","reason":"…"}` | 批/否并恢复挂起任务(CAS,重复决议 409);**仅 operator**(非 operator → 403)。5.0.0 起旧轮询腿 `POST /v1/approvals/<id>` 已退役 |
1739
1857
  | `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) |
1740
1858
  | `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` |
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` 已退役。 |
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 原文。 |
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。 |
1859
+ | `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`(先于场景门)。每一行(两条口同形)在 §G.7 的行形之外多两个可选键(**7.133.0+**):`toolNames`(这台会挂上的工具名,引擎命名空间形 `mcp__<server>__<tool>`,读数序)+ `toolNamesTruncated: true`(名单超过 200 只时只截表,`toolCount` 仍是真数;读数用 `toolCount`,别用 `toolNames.length`);该台读数里没有名单 ⇒ 两键缺席(不编 `[]`),没连上的台是在场的 `[]`;老 server 没有这两键,按缺席读。自带 `.mcp.json` 的探测口是 `POST /v1/capabilities/mcp/probe` |
1860
+ | `POST /v1/admin/drain` `{"actionId":"…","worker":"…","deadlineAt":…}` | **排空口**(7.121.0 起体 = settings-schema `DrainRequest`;operator-only —— principal 须在 `OPERATOR_PRINCIPALS` 内,或单用户部署声明了 `OPERATOR_SINGLE_USER=true`;两条出路都没有〔名单为空且没声明〕⇒ 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` 已退役。 |
1861
+ | `GET /v1/config/catalog` | **配置目录自描述**(operator-only —— `explicitOperatorOk`:没有运维名册〔`OPERATOR_PRINCIPALS` 为空且没有单用户声明 `OPERATOR_SINGLE_USER=true`〕⇒ 501 `capability.operator_principals_required`,有名册而调用方不是运维 ⇒ 403)。逐 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`(合规部署)⇒ 501 `feature.config_catalog_disabled`(路径恒挂载)+ 能力位 `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 原文。 |
1862
+ | `POST /v1/admin/config/refresh` | **配置手动刷新**(operator-only,与上一行同门:没有运维名册〔`OPERATOR_PRINCIPALS` 为空且没有单用户声明 `OPERATOR_SINGLE_USER=true`〕⇒ 501;名单非空而调用方不在名单上 ⇒ 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。 |
1745
1863
  | `GET /health` | 健康(无需鉴权)。**7.67.0+** 恒带 `startedAt`(引擎进程起点 epoch ms,一次铸;与 `pid` 并列的换代锚——值变了就是换了一条命)。7.37.0+ 配置管道在场时带世代账键:`configTargetVersion`(本副本**最后见到**的配置版本,身份标)、`configAppliedVersion`(最后一次真落地的版本)、`configTargetOrdinal`/`configAppliedOrdinal`(7.38.0-rc.2 起,**收货序数**——`CONFIG_PROVIDER=local` 下 version 是内容哈希不承诺序,「改了没生效」看序数对不对齐)、`configApplyStaleMs`(target≠applied **持续**时长;收敛时键缺席)—— 编排器据此摘掉「配置持续落后」的副本 |
1746
1864
  | `GET /v1/sessions/:id/background` · `POST /v1/sessions/:id/background/stop` `{"includeRetained":true\|false}` | **会话后台任务的读口与停止口**(**7.102.0+**;能力位 `background.listFace` / `background.exitFaces`,老 server 404)。读口列**本会话**、**本副本**上的后台任务行(`{ rows: [{ id, description?, type, status, createdAt, sessionScoped, retained? }] }`,`retained:true` = 声明了保留的活服务,正常退出不会停它);停止口「软 → 等一次 `HOST_BG_KILL_GRACE_MS` → 硬」收掉本会话的后台 shell(SIGTERM 处理器先有一次宽限跑完,之后组 SIGKILL,屏蔽 SIGTERM 的循环也活不过去;有后台行时应答慢一次宽限)并逐字返回受据 `{ receipts: [{ id, outcome }] }`:`killed` / `already_gone` / `retained_skipped`(`includeRetained:false` 留下的活服务)/ `no_pgid`(远端 / 沙箱车道没有本机进程组可发,已派普通 kill,「可能还在跑」;host 车道的 `already_gone` 只由观察得出 —— 引擎已收割组长或内核答组已空,7.103.0 起;darwin 上梯子那一枪先落地、引擎还没收割的那一拍如实答 `no_pgid`,判「收掉没有」读进程)。`includeRetained` 必填:`true` = 连活服务一起停(壳的「Exit and stop tasks」),`false` = 活服务照留(`sema kill`)。鉴权 = 会话属主或显式 operator(未知与他人同 404);停止口在无凭据写门内(没配 service 凭证且未开 `ALLOW_UNAUTHED_WRITES` ⇒ 503 `auth.service_token_required`)。`DELETE /v1/sessions/:id` 走同一只收敛(`includeRetained:false`);run 取消(Esc)**不**收后台 shell。契约 §16 |
1747
1865
  | `GET /readyz` | **就绪探针**(**7.102.0+**,无需鉴权;7.121.0 起是唯一的探针路径)。就绪 ⇒ `200 {"ready":true}`;未就绪 ⇒ `503 {"ready":false,"reasons":[…]}` + `Retry-After: 5`,`reasons` ⊆ {`draining`, `model_roster_pending`}(按优先序)。与 `/health` 的 `ready` / `readyReason` / `reasons` 同一只判;库连不上**不算**未就绪(`storeLive` 只在 `/health` 披露)。给只认状态码的**摘流**探针用(k8s `readinessProbe.httpGet`、LB 健康检查、镜像 `HEALTHCHECK`);存活探针用 TCP 探端口,不打 HTTP,见 §0「探针」 |
@@ -1754,8 +1872,17 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
1754
1872
  > 吃 **501**;`available:true` 时 `unavailableReason` **整键缺席**(缺席 = 没有理由,别读成空串)。
1755
1873
  > 两面共用同一份判据,所以列表上画得出的场景点下去不会再突然 501。请按 `unavailableReason` 的**键**分支,
1756
1874
  > 不要去匹配英文文案——文案会改,键不会(新增取值只会追加,现役键不改语义)。
1757
-
1758
- > **operator 鉴权(审批队列)**:`OPERATOR_PRINCIPALS=ops:alice,ops:bob`(CSV)= 谁能当 operator——列任意 owner 待办 + 决议(批/否)。**单租户部署不设=旧行为**(握 service token 即 operator,向后兼容);设了之后,非名单 principal 列待办只看自己的、且**不能决议**(403,防"请求方批自己的高危操作"跳过 F4 闸)。⚠️ **多租户形拒启**:`DURABLE_APPROVAL=true` + `REQUIRE_PRINCIPAL=true` 而 `OPERATOR_PRINCIPALS` 空 ⇒ 进程启动失败并点名修法——否则空名单会让任一已验证租户读到其他租户的待批队列(读面 true-for-all)。设名单,或确属单租户则不设 `REQUIRE_PRINCIPAL`。
1875
+ >
1876
+ > **场景的附件供给(7.133.0 起)**:场景详情另带 `attachmentSupplies`(`"path"` / `"content_text"` / `"content_image"` 的数组,顶层能力位 `userAttachments.supplies` 的子集、保持席位序、键恒在场)= 这个场景在**本部署**上受理的
1877
+ > 请求层附件供给。`path` 读的是这一 run 的工作区,只有「场景挂手 ∧ 部署的执行车道有手」的 run 才有:`scan` / `code-review`、center 声明的场景(它们的工具集不挂手)与 `provider: "in-process"` 的部署整台,
1878
+ > 详情里没有 `path`,这些 run 上的 `path` 条目由引擎整条拒 `400 config.user_attachment_invalid`;`content_text` / `content_image` 与工作区无关,恒在。判据只有一只(详情与引擎的拒读同一个「这一 run 有没有工作区」)。
1879
+
1880
+ > **operator 鉴权(审批队列与全部运维面,一条读法)**:`OPERATOR_PRINCIPALS=ops:alice,ops:bob`(CSV)= 谁能当 operator —— 列任意 owner 待办 + 决议(批/否)。非名单 principal 列待办只看自己的、且**不能决议**(防「请求方批自己的高危操作」跳过 F4 闸);
1881
+ > **名单为空 = 没有人是 operator**(每个调用方只看自己的待批 —— 不带主体的调用方看到的是匿名卡〔不带主体提交的任务停下的那几张,即决断口对它开放的那几张〕,`?owner=` 不生效;7.133.0 之前审批四个读面在名单为空时把每个调用方当 operator,那条读法已删)。
1882
+ > **单用户部署当自己的 operator**:`OPERATOR_SINGLE_USER=true` = 「这台部署的每个调用方都是 operator」,对全部运维面生效;只在 `REQUIRE_PRINCIPAL` 未开且 `OPERATOR_PRINCIPALS` 为空时成立(与任一同在 ⇒ 拒启);
1883
+ > 没配服务凭据的部署上能连上端口的都是 operator;不带主体的 operator 没有身份可记,记「谁改的」的写面(`PUT /v1/admin/memory-optout[/:principal]`)要带主体(否则 401)。
1884
+ > 单用户 + 名单空 + 没声明 + 有审批底座 ⇒ 起服 warn `operator_undeclared_single_user`(不拒启)。
1885
+ > ⚠️ **多租户形拒启**:`DURABLE_APPROVAL=true` + `REQUIRE_PRINCIPAL=true` 而 `OPERATOR_PRINCIPALS` 空 ⇒ 进程启动失败并点名修法(空名单下没人能当这台多租 worker 审批队列的 operator)。设名单,或确属单租户则不设 `REQUIRE_PRINCIPAL`。
1759
1886
  >
1760
1887
  > **部署治理三声明(7.21.0 起)**——三根都是**配置期**旋钮,「谁能改它们」≡「谁能改
1761
1888
  > 部署配置」,不引入新鉴权面;三根**都不设 = 现行为逐字不变**,坏值一律**拒启**(安全轴上的旋钮不静默失效)。
@@ -1901,10 +2028,10 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
1901
2028
  > 🔴 因此:**没配 `SERVICE_AUTH_TOKEN` 且未开 `ALLOW_UNAUTHED_WRITES` 的 worker 上这一口恒 503**,
1902
2029
  > 而能力位仍报 true(本族共有缺口,附录 B.1 已如实登记)。启动日志为此打一条
1903
2030
  > `memory_consolidation_run_credential_gated`;状态读那一口不受影响(它不烧模型)。
1904
- > 🔴 另一条启动告警:`OPERATOR_PRINCIPALS` 为空时两口恒 403(空名单 = 没有任何人是 operator),
2031
+ > 🔴 另一条启动告警:没有运维名册(`OPERATOR_PRINCIPALS` 为空且没有单用户声明 `OPERATOR_SINGLE_USER=true`)时两口恒 501 `capability.operator_principals_required`(没有运维名册 = 没有任何人是 operator),
1905
2032
  > 日志里是 `memory_consolidation_valve_unreachable`。
1906
2033
  > - `GET /v1/admin/memory/consolidation[?scope=]` —— 状态投影(阀门/座位/scope 表/上一轮的账)。
1907
- > - 能力位 `capabilities.memoryConsolidationDriver`(阀门上了场 ∧ operator 名单非空)。
2034
+ > - 能力位 `capabilities.memoryConsolidationDriver`(阀门上了场 ∧ 有运维名册:名单非空或单用户声明)。
1908
2035
  > - 逐键语义与停因闭集见 `docs/ASSISTANT-WIRE-CONTRACT.md` §10。
1909
2036
 
1910
2037
  > **⚠️ hook-wired 部署里,parked 后台子代可能赎回不了(常态,不是升级窗口)。**
@@ -1916,16 +2043,17 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
1916
2043
  > **7.7.0(引擎 5.20.0)起 `TOOL_TRACE` 已退出射程。** 引擎 5.20.0 新增「这条 PreToolUse 面只观察、
1917
2044
  > 不裁决」的声明口,本服务的诊断 tracer(`TOOL_TRACE=true` 装的那只,恒不出判词)已按实声明 ⇒ 它**不再
1918
2045
  > 铸筛查席**,该部署的层数回到 1、赎回照常通。**开着 `TOOL_TRACE` 不再需要在「诊断」与「后台子代能不能
1919
- > 赎回」之间二选一。**
2046
+ > 赎回」之间二选一。** 7.132.0(引擎 7.52.0)起扩展点换成 mods 注册,读法不变:追踪是部署座上的观察注册
2047
+ > `server.tool-trace`,观察者永不铸筛查席。
1920
2048
  >
1921
2049
  > 📎 连带的一处**日志**变化(不是回归):引擎那行「可写工具面没有 effect-aware 门」的启动告警,此前把
1922
2050
  > 诊断 tracer 当成一道门而被抑制;声明之后不再抑制,所以「没配任何工具策略层 + 开着 `TOOL_TRACE`」的
1923
2051
  > 部署升级后会多出那行告警。tracer 从来没有门住任何东西,原先的抑制本身才是问题。
1924
2052
  >
1925
2053
  > **仍在射程内的只剩一条**:调用方提交里带 `settings.hooks.PreToolUse`(未开 `REQUIRE_PRINCIPAL` 的部署
1926
- > 对外开放此面)。那是**会真裁决**的面(出得来 deny/ask),按契约**不能**打观察标——打了等于让引擎把
1927
- > 调用方的判词静默丢弃,比拒绝本身坏得多。这条面上的席位是每请求闭包、park 时未持久化,跨副本重建不出,
1928
- > 所以拒绝仍是正确行为。不带该字段的部署**完全不受影响**。
2054
+ > 对外开放此面)。那是**会真裁决**的面(出得来 deny/ask),不能当观察者挂 —— 那等于让引擎把调用方的判词
2055
+ > 静默丢弃,比拒绝本身坏得多。本服务的赎回腿只重建调用方 policy 一层,所以拒绝仍是本版的行为
2056
+ > (7.132.0 起这张注册可以从存下的请求体按原配置重铸,引擎也认那条路;接上是后续版本的事)。不带该字段的部署**完全不受影响**。
1929
2057
  >
1930
2058
  > **精确的兼容矩阵**(本服务恒供 1 层;引擎只比层数,所以下表就是全部情形。5.18.1 / 5.19.0 / 5.20.0 三个
1931
2059
  > 引擎上都实测过):
@@ -1969,8 +2097,18 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
1969
2097
  > **人的持久规则的读法(7.127.0 起,引擎 7.50.0)**:每个调用读一次规则店;一条持久 deny 行在门上拒它所在那一面的**每个**工具(Read / Grep / Glob / RepoMap /
1970
2098
  > 写工具 / 声明路径目标的调用方工具),`tool_end.gate.disposition.deniedBy = "persisted_rule"`。店的某个分区读不出 ⇒ 该分区上次读到的 deny / ask 行照旧生效、allow 行不在,
1971
2099
  > 射程内的调用真人问(bypass 也问,无审批面即拒),run 一条 `permission.rule_store_unavailable`。
1972
- > **`~/…` 形的行以执行环境的家目录为基**:host / 未设 `REMOTE_EXEC` = 本机本用户,e2b = `/home/user`,k8s / local-docker / ssh / adb / device 不猜 —— 除非 `REMOTE_EXEC_HOME_DIR`
1973
- > 声明。没声明家目录的车道上,一条 `~/…` 的 deny / ask 行判不出点的是哪里 ⇒ 该面文件工具的每个调用都成真人问(bypass 也问);要么声明 `REMOTE_EXEC_HOME_DIR`,要么把规则写成绝对路径。
2100
+ > **`~/…` 形的行以执行环境的家目录为基**:host / 未设 `REMOTE_EXEC` = 本机本用户,e2b = `/home/user`,k8s / local-docker / ssh / adb / device 下单那一刻不猜 —— 除非 `REMOTE_EXEC_HOME_DIR`
2101
+ > 声明(下单门与规则基只认声明)。没声明家目录的车道上,一条 `~/…` 的 deny / ask 行判不出点的是哪里 ⇒ 该面文件工具的每个调用都成真人问(bypass 也问);要么声明 `REMOTE_EXEC_HOME_DIR`,要么把规则写成绝对路径。
2102
+ > 引擎 7.52.0 起:执行环境不带家目录时,引擎的家锚拒读行(`~/.npmrc` / `~/.sema/settings.json` …)放不下 ⇒ **每一次泄内容的递归读**(`grep -r` / `rg` / `git grep` /
2103
+ > `tar c` …)在全 shell 面真人问(allow / 模式 / bypass 都放不掉)、只读面直接拒 —— 无人值守任务检索不了代码。
2104
+ > **家目录座的取值:声明优先,缺省实测**(7.133.0 起):`REMOTE_EXEC_HOME_DIR` 在场 ⇒ 座 = 声明;没声明时 k8s / local-docker / ssh / adb 车道在建执行环境那一拍问一次执行环境自己的 `$HOME`
2105
+ > (与命令同一条 shell、同一份 env;k8s 接在建工作区根的同一条命令后面,不多一次往返;adb 经设备 shell)填座,于是代码检索不再恒问人。实测缺席(没答上 / 答的不是绝对路径,含空串)⇒ 座缺席,
2106
+ > 引擎照旧失败关闭(上面那些递归读照旧问 / 拒),环境照开,并响一行 `execution_lane_home_undeclared`(`provider` + `note`,点名读到了什么与出路;同一部署同一句只响一次)。device 车道的设备协议
2107
+ > 没有家目录读数,座缺席,起服响一行同一只 warn。`~/` 形规则的下单门只认声明(提交那一刻执行环境还没开,读不到实测值):要写 `~/` 形规则就声明 `REMOTE_EXEC_HOME_DIR`。
2108
+ > **声明会被对证**:k8s、local-docker 与 ssh 车道在每只执行环境开起来那一拍问一次 `$HOME`(与命令同一条 shell、同一份 env),与 `REMOTE_EXEC_HOME_DIR` 不一致(去掉尾斜杠后逐字比)
2109
+ > 或问不出 ⇒ 那只执行环境不开,工具结果里的拒句点名两个值与改法(改声明,或让环境以声明的那个用户运行);环境答了的不一致记在那只环境上(此后每次回同一句,不再起容器 / pod / 不再问),
2110
+ > 问不出的那一次不记、下一次重问。
2111
+ > e2b / host / adb / device 车道今天不对证(声明照单全收)。
1974
2112
  >
1975
2113
  > **同批顺带:场景旧名 `team` / 工具旧名 `run_team` 退役(BREAKING)。** 此前过渡窗的别名层
1976
2114
  > (`SCENARIO_ALIASES`)整体撤销:请求体里显式传 `scenario:"team"` 从「静默归一到 `discuss`」变成
@@ -2090,10 +2228,17 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
2090
2228
  | HTTP `429` | 限流 | 看 `Retry-After` 退避 |
2091
2229
  | HTTP `429` + `errorCode:"limit.cost_quota_exceeded"` | per-principal 累计成本配额越顶(`MAX_PRINCIPAL_COST_USD`;**进场门**,不打断在跑的 run) | 看 `Retry-After` / 体 `retryAfterSec` 退避(体 `windowResetsAt` = 当代窗滚过的时刻);窗滚过或调高上限后放行 |
2092
2230
  | 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 …`),只作展示、别锚文案 |
2231
+ | HTTP `400` + `errorCode:"request.invalid_json"` + `reason:"not_json"` \| `"not_object"`(`reason` 7.133.0 起) | 请求体不是合法 JSON(`not_json`;所有读体的口同一个体 —— 含 `notify` / `wake` / 权限规则这几口,它们 7.132.x 及以前答 `request.body_shape`,其余多数口答 `request.rejected`),或合法 JSON 却不是这一口要的对象(`not_object`,`POST /v1/runs/:id/compact`)。读体超 8 MiB 是 `413 request.payload_too_large`(`field:"body"` / `reason:"too_large"` / `cap`,没有 `count`),不是本码 | 修请求体;按 `errorCode` + `reason` 分支,句子只作展示 |
2093
2232
  | 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` 分支,句子只作展示 |
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` 后的现名,或删掉条目 |
2233
+ | HTTP `400` + `errorCode:"request.field_invalid"`(撞 413 的帽见 `request.payload_too_large`)+ `reason:"over_cap"` / `"too_long"` / `"bad_entry"`(7.132.0 起:工具名清单、子代定义、两张宿主路径清单;7.133.0 起提交体上其余的帽同样带:`jobId` · `systemPrompt` · `appendSystemPrompt` · `settings.outputStyle` · `settings.env` · `clientContext.*` · `sandboxImageProfile` · `outputSchema` · `resumeAt` · 顶层技能 `content` / `baseDir`;子代定义里技能自己的 `content` / `baseDir` 同样带,坐标 `agents[i].skills[j].content`,句子仍是形句) | 撞了帽:`field` = 撞帽那一处的体内坐标(`skills[1].allowedTools` · `agents[0].denyTools[3]` · `agents` · `additionalDirectories` · `systemPrompt` · `settings.env.<变量名>` …),`count` = 送来的条数(`over_cap`)或字符数(`too_long`),`cap` = 帽;`bad_entry` = 清单第 `index` 条坏了(`field` = 那张清单)。7.132.0 起覆盖的面句子头一句恒为 `<field> has <count> entries\|characters — at most <cap> are accepted`;7.133.0 起补上的面句子不变、只加这一组键。同一个字段的形错(不是串、空串)只有句子 | 按 `field` 改那一处(合并 / 拆小条目、缩短文字、修那一条路径);句子只作展示 |
2234
+ | HTTP `400` + `errorCode:"request.field_conflict"`(7.133.0 起带 `detail`;`requireExistingSession` 缺 `sessionId` 一处是 422) | 请求里的字段互斥或互为前置(`verify` × `cascade`、`capabilitiesNeeded` 没有 `sandboxImageProfile`、`disableSkills` 同体带技能、决定口的 `remember` / `answer` / `editedPlan` 错门 …)。体带 `detail: { kind, fields }`:`kind` 是闭集词(`cascade_verify` · `image_profile` · `resume_anchor` · `system_prompt_fold` · `file_history_restore` · `skills_disabled` · `existing_session` · `scratchpad_root` · `ask_answer` · `policy_person_ask` · `plan_edit` · `plan_mode_after` · `bound_call_door` · `remember_decision`,全表见契约附录 A),`fields` = 冲突涉及的请求体坐标(句子主语在前;互斥的类只点请求里真带了的键,「X 要求 Y」的类 Y 是缺席的前置、也在里面);`system_prompt_fold` 另带 `count` / `cap`(折叠后的字符数与帽) | 按 `detail.kind` 分支(例:`cascade_verify` ⇒ 去掉 `verify` / `cascade` 重提),按 `detail.fields` 定位到那几个键;句子只作展示、别锚文案 |
2235
+ | 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` 的**内容形**(`Agent(Explore)`)与没有后继的退役名(`MultiEdit`)照 7.130.0 受理、交引擎既有通告(运维行 `runner_config_advisory`);**有后继的退役名**(`Task` · `bash`)7.132.0 起由引擎开跑前拒(`config.tool_face_control`,同步腿 400)。**真未知名**(拼写合法、这台部署没有工具叫它,懒挂载的合法形)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` 后的现名(或删掉条目);只列这台部署挂得出的工具名 |
2236
+ | HTTP `400` + `errorCode:"config.agent.unknown_tool"`(7.131.0 起) | 请求 `agents[i].denyTools`(限制侧)里有读成空的条目:近似名(`BASH`)、退役名(`bash`)、任何工具名都不可能有的拼法、`*`;`agents[i].allowTools`(放行侧)里的**近似名**。开跑前拒、主模型零调用、一个 token 没烧,句子点名条目、原因词与现名。限制侧 `Tool(content)` 的内容不读(读成整只工具)、限制侧整个协议的拒 `mcp__*` / `a2a__*`、真未知名(`NotMounted`,照跑、告运维一次)不拒;放行侧的内容形(`Agent(Explore)`)与没有后继的退役名(`MultiEdit`)照 7.130.0 受理、交引擎既有通告,选择器清单里**有后继的退役名**(`Task` · `bash`)7.132.0 起由引擎开跑前拒;文法读不出的放行条目授不出去 = 收窄而不是放行:规则表(`permissions.allow` / 技能 `allowedTools`)上受理口不拒、引擎开跑时告运维;选择器清单(根 / 子代 `allowTools`)上受理口不拒、引擎开跑前自己拒(子代 ⇒ `config.agent.unknown_tool`、根 ⇒ `config.tool_face_control`,同步腿都是 `400`〔7.132.0 起;此前子代这一形答 HTTP 200 + failed 终局体〕;异步 / 活流腿 HTTP 2xx、同码落在终态 run 行) | 改成句子里 `write` 后的现名,或删掉条目 |
2237
+ | HTTP `400` + `errorCode:"prompt.assembled_role_refused"`(7.132.0 起;流式 / 后台腿是 `failed` 终局带同码) | 请求的 `systemPrompt` / `appendSystemPrompt`(含 `settings.outputStyle`)逐字含引擎提示里的一段锁定文本(宪法、安全段、`# Harness` 这样的标题行)—— 一份已组装的提示。开跑前拒、零调用,句子点名字段与命中段 | 只写角色层:删掉抄进来的引擎段(引擎自己组宪法与行为段;追加段恒排在宪法之后) |
2238
+ | HTTP `400`(`request.field_invalid` · `config.agent.unknown_tool` · `config.tool_face_control`)· HTTP `422`(设置规则桶)· HTTP `409` `resume_blocked_by_policy` 的拒体另带 `field` + `detail`(7.133.0 起;additive,句子与码不变) | 工具名清单与设置规则里**读成空**的条目被拒时,句子仍只点名先撞上的那一张清单(前 5 条 + `; and K more`);机读位把这一遍判下来的**全部**问题一次列全:`detail.problems` 每条 `{ code, field, entry, reason }`(`code` = 这一条所在清单被拒时的码,`field` = 清单的体内坐标,`entry` = 条目的脱敏原值,`reason` = 原因词),`detail.total` = 问题总数(`problems` 至多 1000 条),顶层 `field` = 第一条问题的坐标(句子点名的那张清单),体的 `errorCode` = 第一条问题的 `code`;只有一条问题就是长度 1 的列表,不是另一种形。超帽的清单整只拒、不逐条读,不在列表里(第一处命中就是它时体只带撞帽的 `field` / `reason` / `count` / `cap`)。同步提交腿上引擎开跑前拒这两张工具名清单(`config.agent.unknown_tool` / `config.tool_face_control`:有后继的退役名、文法读不出的放行条目)的 400 体同形:句子是引擎的,只点名它先撞上的一条,`detail.problems` 是受理口对放行侧选择器清单的读数(只列与体的码同码的那一族清单,所以第一条问题的 `code` 恒等于 `errorCode`) | 按 `detail.problems` 逐条提示 / 改;不要解析句子;`total` 大于 `problems` 的条数 ⇒ 列表被截 |
2239
+ | HTTP `200` / `202` 应答里的 `warnings`(7.133.0 起;同步腿 200 体 · 流式腿 `meta` 帧 · 异步腿 202 回执;**有话说才在场**) | 请求的放行侧选择器清单(根 `allowTools`、子代定义的 `allowTools`)里有什么也不放的条目,而受理口没拒(放行侧只拒近似名,其余交引擎):`reason` 是引擎条目读法的原因词(`tool_name` · `grammar` · …)或 `not_in_roster`(读得出、却不在名册 —— 引擎目录 ∪ 本服务自铸工具 ∪ 本请求挂的调用方工具(主名 · 别名 · 子代工具池);协议命名空间名 `mcp__*` / `a2a__*` 懒挂载、`*` 是「不设名单」,都不判)。每条 `{ code, field, entry, reason }`,形同上一行。这不是拒:请求照常受理、照跑,引擎对这些条目的既有处置不变 | 把 `warnings` 当提示给用户看(「这几条什么也没放」);同一 `Idempotency-Key` 的重放从 run 记录答,不重判、不带;在飞合流回放的是首发那一腿铸的应答 |
2096
2240
  | 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` 后的现名(或删掉条目);超帽的合并成更宽的条目 |
2241
+ | HTTP `400` + `errorCode:"request.field_invalid"` + `detail.problems[].reason:"below_trust_floor"`(7.133.0 起) | 多租部署(`REQUIRE_PRINCIPAL=true`)上调用方的信任档低于 `settings.permissions.allow`(或 `userInvokedSkills` 点用的自带技能的 `allowedTools`,`field: skills[i].allowedTools`)要的 `global`:档只由这次请求验过的凭据推 —— 部署的 service 凭据、或中心登录令牌在活动 scope 里的角色 ≥ `editor`(settings-schema 的治理写角色)⇒ `global`;角色更低 / 令牌不带角色 / 直连门 principal 令牌 / 没有验过的凭据 ⇒ `project`(`detail.callerTrust`)。`deny` / `ask` 与空 `allow` 照收;单用户部署任何调用方都是 `global` | 去掉 `allow`、开跑时答卡;请运维把规则放进 `APPROVAL_GRANT`;或请 scope 管理员把成员角色抬到 `editor` 及以上。提交前可读 `GET /v1/capabilities` 的 `settings.permissions.callerTrust` |
2097
2242
  | HTTP `429` + `errorCode:"usage.window_exhausted"` | 部署级治理窗耗尽(`USAGE_WINDOWS`,token 或 $ 天花板先满者) | 看响应体 `retryAfterSec` 退避;窗滑动/桶到期后放行 |
2098
2243
  | `terminal:{kind:"failed", code:"usage.window_exhausted"}` | 已受理的 run 在 **turn 边界**撞上治理窗且无法 durable 挂起 | 同上退避后重投;配好 checkpoint 基建则改为 `suspended` 等窗自动续跑 |
2099
2244
  | HTTP `501`(`/v1/runs`) | 内存模式不支持异步 | 配持久存储(`DB_BACKEND=mysql` / `pg` + 坐标;7.106.0 起给了坐标必须点名 `DB_BACKEND`) |
@@ -2116,9 +2261,9 @@ tag 词表与每条的最坏后果见 `docs/FAIL-OPEN-CENSUS.md`。引擎装配
2116
2261
  按 `code` / `classification` / `level` 打标;其中 `shell-gate-off` 在本 session 显式声明 `permissionMode:
2117
2262
  "bypassPermissions"` 时是调用方要的姿态,降为 **info** 并标 `expected`——只有没表态落到引擎默认 off 的无壳直连
2118
2263
  形才 warn。子代 `agents[].denyTools` / `allowTools` 的**名字**归引擎开跑前一只判(7.126.0 起与根 run 的 `allowTools` / `excludeTools` 是同一只工具选择器):
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` /
2264
+ 7.129.0 起(引擎 7.51.0)没有任何工具答应、也不是某只已挂载工具另一种拼法的名字(**真未知名**)**引擎不再拒跑**:条目原样保留(今天什么都不拒 / 不准,之后挂上同名工具即生效),运维行 `runner_config_advisory` 每条告知一次(句子带该写的拼法;委派工具声明了逐次注入工具的名册,引擎列得出子代工具池);
2265
+ 引擎开跑前拒(`config.agent.unknown_tool`)的是任何工具名都不可能有的拼法(空白、括号不配、空名)、非字符串条目,以及(引擎 7.52.0 起,两侧同判)写成**已挂载工具另一种拼法**的条目:大小写孪生(`BASH`)与有后继的退役名(`Task` ⇒ `Agent`、`bash` ⇒ `Bash`)。**7.131.0 起本服务把其中写错现名的在受理口前置拒:限制侧四类全拒、放行侧只拒近似名** —— `denyTools:["BASH"]` / `["bash"]` 以为禁了 `Bash`、子代照用(静默放行),`allowTools:["BASH"]` 想放 `Bash` 却什么都没放(静默收窄);
2266
+ 提交 400 `config.agent.unknown_tool`(根 run 的清单是 `config.tool_face_control`),句子点名条目、原因词与现名;放行侧的**内容形**(`Agent(Explore)`)与没有后继的退役名(`MultiEdit`)仍照 7.130.0 受理、交引擎既有通告,选择器清单(根 / 子代 `allowTools`)里**有后继的退役名**(`Task` · `bash`)7.132.0 起由引擎开跑前拒,规则表里的退役名照旧受理、引擎开跑时告运维;文法读不出的放行条目授不出去 = 收窄而不是放行:规则表(`permissions.allow` / 技能 `allowedTools`)上受理口不拒、引擎开跑时告运维;选择器清单(根 / 子代 `allowTools`)上受理口不拒、引擎开跑前自己拒(子代 ⇒ `config.agent.unknown_tool`、根 ⇒ `config.tool_face_control`,同步腿都是 `400`〔7.132.0 起;此前子代这一形答 HTTP 200 + failed 终局体〕;异步 / 活流腿 HTTP 2xx、同码落在终态 run 行);存量体续跑见 MIGRATION 7.131.0 第 8 条。判官仍是引擎的条目读法。**真未知名**(没有近似也没有退役对应:懒挂载的合法形)照旧只告运维、不拒。名字按工具名原拼写 `Bash`。这两张表管子代可挂载的**全部**工具 —— 委派池、内建 hands(`Read` / `Edit` / `Write` / `NotebookEdit` / `Bash` / `Grep` /
2122
2267
  `Glob` …)、引擎自己的工具(`Monitor` / `Artifact` / `Skill` / 记忆工具 …)与父的 MCP 挂载:`denyTools:["Write"]` ⇒ 子代没有 `Write`,`denyTools:["Monitor"]` ⇒ 子代没有
2123
2268
  `Monitor`;`allowTools` 只列调用方工具 ⇒ 子代只剩那几只与它这条腿的协议工具(要什么就点名什么,`mcp__<srv>` 点整台服务器)。内建只读子代(Explore / Plan)没有
2124
2269
  `Edit` / `Write` / `NotebookEdit`。要按路径 / 命令拦而保留工具,写调用方权限策略的 deny 规则。
@@ -2222,7 +2367,7 @@ k8s 车道每条 run 一只新 pod(pod 寿命 = 泄漏护栏,不跨 run 复用)
2222
2367
  - **指针**:`space/latest`,体 = 最近一份 snapshotId。run 结束时先把 `/workspace` 打包上传(与挂起同一条车道:沙箱只拿到限时预签 URL,凭据留在 server),成功后**整体覆写** `latest`,再删 pod。下一条 run 第一次用到沙箱时读 `latest`:在 ⇒ 新 pod 上恢复那一份;不在(空间第一条 run)⇒ 空目录。
2223
2368
  - **保留**:每个空间只留最近 `WORKSPACE_SNAPSHOT_KEEP` 份(缺省 3),写新快照后删最老的;`latest` 指向的那份恒留。没有另外的索引表 —— 桶里列出来的就是全部。
2224
2369
  - **只盖 `/workspace`**:进程、内存态、`/tmp` 不跨轮(与挂起 / 续跑同一口径)。大小上界 = 快照时限 `WORKSPACE_SNAPSHOT_TIMEOUT_MS`(缺省 300000 = 5 分钟);超时 = 快照失败,不截断。
2225
- - **排除表**(`WORKSPACE_SNAPSHOT_EXCLUDE`,7.126.0 起):逗号分隔的 tar 排除模式,相对工作区根(`build,.gradle,node_modules/.cache`),每项一个 `--exclude=./<项>`,落在**那一只**打包上传上 —— run 末快照与停驻挂起是同一张表(挂起后续跑也没有被排除的目录;构建产物、依赖缓存本就可再生)。沙箱镜像的 GNU tar 按工作区根锚定(`build` 只排除 `/workspace/build`,不排除 `/workspace/app/build`;要排子模块的就写 `app/build`,或写通配 `*/build`);镜像若换成 busybox / libarchive 的 tar,同一项会在任意深度生效。不读项目的 `.gitignore`(那是另一套文法、按项目变、与 tar 的匹配不等价);中心若要下发模式表,下发的就是这一个 env 的值。项形是闭的:相对路径段,不含 `..`、前导 `/`、空白、引号(坏项拒启,码 `config.workspace_snapshot_exclude_invalid`,点名第几项)。⚠️ 已知形:打包先落 pod 里的临时文件再上传(对象存储的预签 PUT 要 `Content-Length`,流式上传不行)⇒ 打包期间工作区占双倍盘;排除表把它缩小。
2370
+ - **排除表**(`WORKSPACE_SNAPSHOT_EXCLUDE`,7.126.0 起):逗号分隔的 tar 排除模式,相对工作区根(`build,.gradle,node_modules/.cache`),每项一个 `--exclude=./<项>`,落在**那一只**打包上传上 —— run 末快照与停驻挂起是同一张表(挂起后续跑也没有被排除的目录;构建产物、依赖缓存本就可再生)。沙箱镜像的 GNU tar 按工作区根锚定(`build` 只排除 `/workspace/build`,不排除 `/workspace/app/build`;要排子模块的就写 `app/build`,或写通配 `*/build`);镜像若换成 busybox / libarchive 的 tar,同一项会在任意深度生效。不读项目的 `.gitignore`(那是另一套文法、按项目变、与 tar 的匹配不等价);中心若要下发模式表,下发的就是这一个 env 的值。项形是闭的:相对路径段,不含 `..`、前导 `/`、空白、引号,也不是整体通配(只由 `*` / `?` 组成的项 —— `*` 会让快照一个顶层项都不剩;`*.log`、`build-*` 这类带字面部分的仍是正常项)(坏项拒启,码 `config.workspace_snapshot_exclude_invalid`,点名第几项)。⚠️ 已知形:打包先落 pod 里的临时文件再上传(对象存储的预签 PUT 要 `Content-Length`,流式上传不行)⇒ 打包期间工作区占双倍盘;排除表把它缩小。
2226
2371
  - **提交围栏(下一条 run 等上一条存完)**:「这个空间有提交在飞」是 run 店里的一行(表 `workspace_space_commit`;local 后端是数据根下 `space-commits/` 的一份记录),不在对象存储里。写点只有一个次序:run 的终态写入与释放会话占用**同一个事务**里落 `pending`(只在这条 run 碰过沙箱时)→ run 末保存开始时改成提交中 → 上传完、写 `latest` 之前再认领一次(改成发布中)→ 存完或失败都删行。**一行一个期限,写者写、所有人读**:每次写行都把行的期限定为「此刻 + 写它的那台副本的 `WORKSPACE_SNAPSHOT_TIMEOUT_MS`」;读的一方只看行里的期限,不拿自己副本的数判别人的提交 —— 所以滚动改这个数时,新旧副本互不误判。下一条 run 连沙箱前读这一行:有 ⇒ **等**,每 1 秒重读一次,直到行消失(再按新的 `latest` 恢复)或过了行里的期限。读到过了期限 = 上一条的 worker 死在终态与保存之间、死在上传里、或死在写 `latest` 时 —— 也可能写者正在这一刻续期,所以只读一眼不算数:下一条 run 在一个事务里锁住这一行、按锁下读到的库钟再判一次,仍是那条 run 的、仍过期限才删掉它(弃)。删成了 ⇒ 一行 `workspace_commit_abandoned`(`space` / `remainingMs`:≤ 0,过了期限多久 / `state`),照常恢复 `latest`(它仍指上一份**完整**快照);没删成(写者恰好续了期、行已换主或已删)⇒ 立刻重读、照常等。弃与续期在行锁上排队,被弃的写者再认领时找不到自己的行 ⇒ 不推进 `latest`。期限按存储自己的钟算(SQL 后端 = 数据库的钟),不拿两台副本的墙钟比。提交自己也守同一个期限:开始保存时它的行已过期限(下一条 run 可能已按「放弃」继续)⇒ 不上传、不推进 `latest`(`workspace_snapshot_failed`、`stage: fence`);上传完、写 `latest` 前那次认领发现行已过期限或已不是自己的 ⇒ 同样不推进(`stage: fence`)。所以引擎拆环境的 15 秒上界、存储慢都不会让下一条 run 恢复到旧的一份。终态帧上的 `workspaceSnapshot` 只承载本 run 在终态帧之前落定的结局:保存比终态帧晚落定时这条 run 的帧不带,那次结局只留运维行,也不会挂到同一会话下一条 run 的帧上。
2227
2372
  - **仍余的一条残窗(如实)**:`latest` 指针写是单对象预签 PUT,这次 PUT 的时限 = 围栏剩余时长。PUT 自己到点没答、写者放弃它(一行 `workspace_snapshot_failed`,`stage: "pointer"`、`code: "connect_failed"`,`error` 点名指针写)之后,对象存储侧那次 PUT 仍可能迟到落地 ⇒ 一个已失围栏的写者的 `latest` 覆盖新写者刚发布的。围栏丢失是另一臂(`stage: "fence"`、`code: "timeout"`):那一臂不发 PUT,构造不出这扇窗。只在多副本、围栏到期、且存储侧延迟大于剩余时长时构造得出;本版不读 ETag、不做条件写,只在此披露。收法(下一版):存储支持 `If-Match` 时用 ETag 条件 PUT,不支持时 `latest` 改为带提交世代的指针对象、读者取有围栏记录的最高世代;两形由能力位如实披露。
2228
2373
  - **指针 / 列举 / 删除由 server 直接请求 `MINIO_ENDPOINT`**(快照字节仍是 pod ↔ MinIO 直传)。server 进程必须够得着这个端点(会话快照 blob 卸载本来就从 server 进程请求同一个端点)。
@@ -2238,19 +2383,22 @@ k8s 车道每条 run 一只新 pod(pod 寿命 = 泄漏护栏,不跨 run 复用)
2238
2383
 
2239
2384
  - **没配快照存储**(`MINIO_ENDPOINT` / `MINIO_ACCESS_KEY` / `MINIO_SECRET_KEY` 缺任一)⇒ 工作区每轮为空,`GET /v1/capabilities` 不带 `workspaceSync`;起服一行 `workspace_persist_unavailable`(`missing` 点名缺的键;缺省 `space` 也出,设 `WORKSPACE_PERSIST=off` 即不出)。
2240
2385
  - **没有存储后端**(`DB_BACKEND=memory`;配了 SQL 的 `DB_BACKEND` 却显式 `SESSION_BACKEND=memory` 时库不开,同一形)× `space`(含缺省,且配了快照存储)⇒ **拒启**,拒句一行点名 `WORKSPACE_PERSIST=space`、缺的存储后端、会话店种类(与 `/health.sessionBackend` 同一读法)与可用的后端。出路:`DB_BACKEND=local`,或 SQL(`DB_BACKEND=mysql|pg`,`SESSION_BACKEND` 不设 `memory`),或设 `WORKSPACE_PERSIST=off`。`SESSION_BACKEND=memory` 配 `DB_BACKEND=local` 不在此列:被收编成 local 店,照起。拒启而不是降级:否则能力位报 `space`、每条 run 却一份都不存。
2241
- - **读 `latest` 失败**(非 404 的 HTTP 错、网络错)、**读或弃提交围栏那一行失败**或**读会话住客失败**(会话店报错,或答出的不是住客令牌)⇒ 这条 run 连沙箱失败(`connect_failed`,工具调用报沙箱不可用;停驻 run 续跑时的恢复同样失败),**不会**悄悄给一个空目录、也不猜「没有提交在飞」—— 「以为恢复了其实没有」比失败更坏。读住客失败时一只 pod 都不建,另有一行 `workspace_space_unreadable_occupant`(`sessionId` / `code`:会话店错误的码,不带正文),每条 run 至多一次。修好存储 / 数据库后重跑,或设 `WORKSPACE_PERSIST=off` 即恢复。
2386
+ - **读 `latest` 失败**(非 404 的 HTTP 错、网络错)、**读或弃提交围栏那一行失败**或**读会话住客失败**(会话店报错、答出的不是住客令牌,或没在 `rpcTimeoutMs`〔30 秒,与读围栏行、读 `latest` 同一只墙〕内答 —— 挂住的会话店不再让 run 的起步永挂)⇒ 这条 run 连沙箱失败(`connect_failed`,工具调用报沙箱不可用;停驻 run 续跑时的恢复同样失败),**不会**悄悄给一个空目录、也不猜「没有提交在飞」—— 「以为恢复了其实没有」比失败更坏。读住客失败时一只 pod 都不建,另有一行 `workspace_space_unreadable_occupant`(`sessionId` / `code`:会话店错误的码,不带正文;读超时的码是 `incarnation_read_timeout`),每条 run 至多一次。修好存储 / 数据库后重跑,或设 `WORKSPACE_PERSIST=off` 即恢复。
2242
2387
  - **`latest` 指向的快照对象不在了**(被删 / 被只盖 `.tar.gz` 的生命周期规则清掉)⇒ 沙箱**照常起**:一行 `workspace_latest_dangling`(`space` / `key` / `deleted`),删掉悬空的 `latest`,这条 run 从空目录开始,它的 run 末快照成为新的 `latest`(数据已被运维规则清走:响亮、可继续)。指针删不掉 ⇒ 同一行 `deleted: false` + `error`,下一条 run 再说一次。
2243
- - **`latest` 指向的快照在、却拿不回来**(桶答了却不是 200 / 解压失败 / 超过快照时限 / 传输中断)⇒ 这条 run **仍然连沙箱失败**(`connect_failed`)、**不**退到空目录 —— 用户的改动还在桶里,静默空目录 = 让人以为丢了。**一条 run 只恢复一次**:瞬时类(`timeout` 超过快照时限 / `transport` 传输中断:执行通道断了,或 pod 取包的 GET 没拿到完整应答 —— 连不上、被重置、传到一半被截)在恢复这一步里换一只新 pod 再试一次(约 1 秒后),再试成了 ⇒ 照常起、不出行;`store_busy`(桶答 `408` / `429` / `500` / `502` / `503` / `504` —— 「现在不行」,同一请求再发可能换答案)同属瞬时类;非瞬时类(`download` 桶答了、答的是其余非 200 —— 如 `401` / `403` / `501`;`untar` 包完整下来了却解不开)不重试。每次没成的尝试,新起的 pod 当场删掉(残缺目录既不给工具用,也不会在 run 末被快照回去覆盖 `latest`)。恢复终局是拒 ⇒ **恰一行** `workspace_restore_failed`(`space` / `snapshotId` / `class` = `store_busy` \| `download` \| `untar` \| `timeout` \| `transport` / `attempts` = 这次恢复的尝试次数(`1` 或 `2`)/ `boundMs` = 每次尝试的时限 / `error`,预签 URL 已脱敏),这条 run 之后的每次沙箱操作都答**同一句**拒(不再建 pod、不再读 `latest`、不再出行);建 pod 本身失败不是恢复的结局(不出这行,下一次操作重来)。拒句写明类别与尝试次数,点名两条出路:**调大 `WORKSPACE_SNAPSHOT_TIMEOUT_MS`**(大工作区撞上界),或 **`WORKSPACE_PERSIST=off` 跑一轮**(空目录起,不删任何快照;改回 `space` 后下一条 run 照常恢复那一份)。没有「删指针」的接口:丢弃这份快照 = 丢弃用户数据,要做就由运维在对象存储侧有意识地删 `<前缀>/<化身>/space/latest`(下一条 run 从空目录起)。与上一条「悬空」分开:那是对象**不在**(没东西可恢复 ⇒ 空目录 + 响亮),这是对象**在**而拿不回来(⇒ 拒)。
2244
- - **run 末提交围栏失守、上传失败或 `latest` 写失败** ⇒ 一行 `workspace_snapshot_failed`(`space` / `stage` = `fence` \| `upload` \| `pointer` / `code` / `error`,预签 URL 已脱敏),这条 run 在 `GET /v1/fleet/stream` 上的终态 task 帧带 `workspaceSnapshot: "failed"`;pod 照删;`latest` 不动 ⇒ 下一条 run 恢复的是**上一份**。
2388
+ - **`latest` 指向的快照在、却拿不回来**(桶答了却不是 200 / 解压失败 / 超过快照时限 / 传输中断)⇒ 这条 run **仍然连沙箱失败**(`connect_failed`)、**不**退到空目录 —— 用户的改动还在桶里,静默空目录 = 让人以为丢了。**一条 run 只恢复一次**(同一只执行环境上并发的连沙箱 —— 显式 `connect()` 与首个工具调用的懒连接 —— 共用同一次建连:一只 pod、一次恢复、一个终局,被拒也是同一个拒;恢复还没完成时才到的工具调用和 `reconnect` 也等这一次建连 —— pod 先于它的工作区存在,不在半恢复的 `/workspace` 上先跑):瞬时类(`timeout` 超过快照时限 / `transport` 传输中断:执行通道断了,或 pod 取包的 GET 没拿到完整应答 —— 连不上、被重置、传到一半被截)在恢复这一步里换一只新 pod 再试一次(约 1 秒后),再试成了 ⇒ 照常起、不出行;`store_busy`(桶答 `408` / `429` / `500` / `502` / `503` / `504` —— 「现在不行」,同一请求再发可能换答案)同属瞬时类;非瞬时类(`download` 桶答了、答的是其余非 200 —— 如 `401` / `403` / `501`;`untar` 包完整下来了却解不开)不重试。每次没成的尝试,新起的 pod 当场删掉(残缺目录既不给工具用,也不会在 run 末被快照回去覆盖 `latest`)。恢复终局是拒 ⇒ **恰一行** `workspace_restore_failed`(`space` / `snapshotId` / `class` = `store_busy` \| `download` \| `untar` \| `timeout` \| `transport` / `attempts` = 这次恢复的尝试次数(`1` 或 `2`)/ `boundMs` = 每次尝试的时限 / `error`,预签 URL 已脱敏),这条 run 之后的每次沙箱操作都答**同一句**拒(不再建 pod、不再读 `latest`、不再出行);建 pod 本身失败不是恢复的结局(不出这行,下一次操作重来)。拒句写明类别与尝试次数,点名两条出路:**调大 `WORKSPACE_SNAPSHOT_TIMEOUT_MS`**(大工作区撞上界),或 **`WORKSPACE_PERSIST=off` 跑一轮**(空目录起,不删任何快照;改回 `space` 后下一条 run 照常恢复那一份)。没有「删指针」的接口:丢弃这份快照 = 丢弃用户数据,要做就由运维在对象存储侧有意识地删 `<前缀>/<化身>/space/latest`(下一条 run 从空目录起)。与上一条「悬空」分开:那是对象**不在**(没东西可恢复 ⇒ 空目录 + 响亮),这是对象**在**而拿不回来(⇒ 拒)。
2389
+ - **run 末提交围栏失守、上传失败或 `latest` 写失败** ⇒ 一行 `workspace_snapshot_failed`(`space` / `stage` = `fence` \| `upload` \| `pointer` / `code` / `error`,预签 URL 已脱敏),这条 run 在 `GET /v1/fleet/stream` 上的终态 task 帧带 `workspaceSnapshot: "failed"`;pod 照删;`latest` 不动 ⇒ 下一条 run 恢复的是**上一份**。`latest` 只指向真写出的对象:run 末保存走到上传那一步时沙箱 pod 已不在(它在提交围栏认领期间消失)也按上传失败处理(`stage: upload`、`code: connect_failed`),不铸一个没有对象的 snapshotId 去改指针。
2390
+ - **run 末快照保存前一律先停 pod 里的进程并确认**(与那一刻有没有命令在飞无关)。命令「结束」(自己退出、被取消、超时)不等于它起的进程结束(断开 exec 套接字不会让 pod 里的进程停),在飞登记里已经没有命令也不能证明 pod 静了,所以保存前都先杀掉 pod 里除 init 与容器保活进程外的全部进程并确认;没连过沙箱的 run 没有可存的工作区,不走这一步。**run 结束时还有命令在跑**(取消 / 超时结束的 run 的命令没来得及停)⇒ 杀之前**先等**它们结束:最长一只 `rpcTimeoutMs` 墙(缺省 30 秒;等待算在这次提交的快照时限里,上传拿到的是剩下的);等不到的还要断开仍开着的 exec 套接字(那条命令的调用方读到 `aborted`),**照样保存**,并出一行 `workspace_snapshot_commands_cut`(`space` / `inFlight` / `waitedMs` / `killed`)。**没能确认进程停下**(杀进程那一步失败,或几次复查后仍有活进程;有没有命令在飞、等到与否都一样)⇒ **不保存**:一行 `workspace_snapshot_failed`(`stage: upload`、`code: command_in_flight`),`latest` 不动 —— 可能还在变的工作区不当 `latest`。**确认不了的四种形**(工作区不存,下一轮恢复的是上一份 —— 本轮的工作在下一轮看不到;7.131.x 在「run 结束时没有命令在飞」时照存):镜像里经 `sudo` / setuid 程序起的、以别的 uid 跑的进程(`kill -9` 没有权限,例如 `sudo -u postgres` 起的库进程)· 不可中断睡眠(D 态)的进程 · `podSpecPatch` 打开了 `shareProcessNamespace`(同 pod 别的容器的进程也在 `/proc` 里)· 镜像里没有 `tr`、而容器保活进程不是 1 号进程(pod 起动先跑包源钩子的那一形:认不出保活进程、把它杀掉,容器随之结束)。**出路**:k8s 车道镜像以一个非 root 用户跑、不经 sudo / setuid 起常驻进程,带 `tr`,不开 `shareProcessNamespace`;按 `workspace_snapshot_failed{stage: upload, code: command_in_flight}` 告警;不需要跨轮保留的部署设 `WORKSPACE_PERSIST=off`。杀进程脚本只在「该杀的都不活了」时退出 0(僵尸进程算死:容器保活进程不收尸)。从 destroy 开始起这只执行环境不再收新命令(工具调用报沙箱已销毁)。`command_in_flight` 的拒只留给停驻挂起(run 还活着,撕裂的归档才是真风险)。此前 run 结束时只要还有命令在飞,快照就整份被拒(`workspace_snapshot_failed`,`stage: upload`),`latest` 不动,下一轮恢复上一份 —— 取消 = 丢掉这一轮的工作。
2391
+ - **pod 被集群在准入时拒**(pod 建出来后、跑起来之前就到了 `Failed`:如 `K8S_SANDBOX_IPV6=disabled` 而 kubelet 没放开那两只 sysctl 时的 `SysctlForbidden`、节点亲和不符、节点放不下)⇒ 这只执行环境**记住这次拒**直到销毁:这条 run 的每次操作答同一句 `connect_failed`(沙箱不可用;句子点名 pod 的阶段与 kubelet 的原因),**不再每次操作重建 pod**(此前一条 run 里可建出几十只 `Failed` pod),与「恢复被拒」同一只记住的槽、同一个写口;一行 `sandbox_pod_refused`(`pod` / `phase` / `reason` / `detail`:kubelet 的原话,截断 / `sessionId`),每只执行环境至多一次。重跑这条 run 是一只新的执行环境,重新问集群。API 拒建 pod(HTTP 非 2xx:配额等)不在此列,仍是那一次尝试的失败。
2245
2392
  - **删最老的失败** ⇒ 一行 `workspace_snapshot_prune_failed`,不影响本轮(空间里只是多留几份)。
2246
- - **空间结束时回收快照对象失败**(列举失败、列举被截断〔分区里超过 1000 个对象〕、某个对象删失败)⇒ 会话照删,每个失败的分区 / 阶段一行 `workspace_space_reclaim_failed`(`space` 前缀 / `sessionId` / `lane` = `space` \| `park` / `stage` = `list` \| `delete` \| `budget` \| `seat_threw`(回收座违约抛了,响亮接住、不炸进程)/ `error`,预签 URL 不进行)+ 计数 `server.workspace-space.reclaim-failed`;
2247
- `budget` = 回收座这一次调用的绝对截止(= 快照时限;会话删除 = 一个空间,托管留存 sweep = 每个域每拍一次;卷回收也在内)到点,这个空间余下的列举与删除没做 —— 截止在轮到这个空间之前就过了 ⇒ 它一个请求都没发,同样一行 `budget`(配了缓存卷的,它的卷再一行 `workspace_cache_reclaim_failed{reason: "budget"}`)。截止之前的失败互不牵连(另一个分区、构建缓存卷照收)。对象留在桶里,按行里的空间前缀 / 会话 id 去桶里手清,或等桶的生命周期规则;到期会话已删、不会被下一拍重新列出;一拍最长 = 有结束空间的域数 × 快照时限,超过 sweep 间隔时中间的拍被跳过(防重入守卫照旧);**快照时限 ≥ 2 × sweep 间隔(= 租约 TTL)时,一个域内的回收可把本副本的租约耗过期 ⇒ 这一拍余下的域不跑、下一拍再起(一行 `retention_sweep_lease_lost`)—— 配法:快照时限要明显小于租约 TTL,即 `WORKSPACE_SNAPSHOT_TIMEOUT_MS`(毫秒)< 2 × `RETENTION_SWEEP_INTERVAL_SEC` × 1000 再留出该域 SQL 步骤的余量;缺省时限 300 000 ms 对 sweep 间隔 ≥ 300 s 的部署成立,间隔更短的部署调大间隔或调小时限(时限下限 30 000 ms)**。
2393
+ - **空间结束时回收快照对象失败**(列举失败、列举被截断〔分区里超过 1000 个对象〕、某个对象删失败)⇒ 会话照删,每个失败的分区 / 阶段一行 `workspace_space_reclaim_failed`(`space` 前缀 / `sessionId` / `lane` = `space` \| `park` / `stage` = `list` \| `delete` \| `budget` \| `seat_threw`(回收座违约抛了,响亮接住、不炸进程)/ `count` / `error`,预签 URL 不进行;**一次调用里同类(分区 × 阶段)的失败合成一行**:`count` = 撞上它的空间数,`space` / `sessionId` / `error` 是调用里排第一的那个 —— sweep 一拍几百个空间遇上同一个 RBAC 漂移只出一行;卷那一行 `workspace_cache_reclaim_failed` 同形)+ 计数 `server.workspace-space.reclaim-failed`(仍逐空间计);
2394
+ `budget` = 回收座这一次调用的绝对截止(= 快照时限;会话删除 = 一个空间,托管留存 sweep = 每个域每拍一次;卷回收也在内)到点,这个空间余下的列举与删除没做 —— 截止在轮到这个空间之前就过了 ⇒ 它一个请求都没发,同样记进 `budget` 那一类(配了缓存卷的,它的卷记进 `workspace_cache_reclaim_failed{reason: "budget"}` 那一类)—— `budget` 类行里的 `count` 就是被截止留下的空间数。截止之前的失败互不牵连(另一个分区、构建缓存卷照收)。对象留在桶里,按行里的空间前缀 / 会话 id 去桶里手清,或等桶的生命周期规则;到期会话已删、不会被下一拍重新列出;一拍最长 = 有结束空间的域数 × 快照时限,超过 sweep 间隔时中间的拍被跳过(防重入守卫照旧);**快照时限 ≥ 2 × sweep 间隔(= 租约 TTL)时,一个域内的回收可把本副本的租约耗过期 ⇒ 这一拍余下的域不跑、下一拍再起(一行 `retention_sweep_lease_lost`)—— 配法:快照时限要明显小于租约 TTL,即 `WORKSPACE_SNAPSHOT_TIMEOUT_MS`(毫秒)< 2 × `RETENTION_SWEEP_INTERVAL_SEC` × 1000 再留出该域 SQL 步骤的余量;缺省时限 300 000 ms 对 sweep 间隔 ≥ 300 s 的部署成立,间隔更短的部署调大间隔或调小时限(时限下限 30 000 ms)**。
2248
2395
  - **提交围栏那一行删不掉**(数据库 / 数据根故障)⇒ 下一条 run 等到行里的期限过去,按 `workspace_commit_abandoned` 继续(有界、响亮)。
2249
2396
 
2250
2397
  **能力位**:`GET /v1/capabilities.workspaceSync = { persist, keep, timeoutMs }`(`timeoutMs` 7.126.0 起 = 快照时限,壳据此告知用户「快照上界」;排除表不上 wire),只在 k8s 车道 ∧ 配了快照存储时在场;`persist` 如实报生效值(缺省 `"space"`);报 `"space"` 的部署,会话店一定读得出住客(读不出的组合起服即拒)。壳 / 中心只在 `persist:"space"` 时渲「工作文件跨轮保留」。
2251
2398
 
2252
2399
  **须知**:把 `/workspace` 当一次性临时盘用的调用方,下一条 run 会看到上一条留下的文件;回到每轮为空 = `WORKSPACE_PERSIST=off`。存储增长 = 每个空间 `KEEP` 份 tar;**空间随会话结束而回收**:`DELETE /v1/sessions/:id` 的删除协调器与托管留存 sweep 删掉会话(树)之后,同一只回收座(见 9.3d;**不在删除应答路径里** —— `DELETE` 摘掉旧订阅即答,回收在应答前已起、不被等待,一次调用一份绝对截止 = 快照时限,卷回收也在内)删该空间 `<前缀>/<化身>/space/` 下的全部对象(快照与 `latest`)和该会话停驻车道 `<前缀>/<会话 id>/` 里名字带本住客归属记号的 `*.tar.gz`
2253
2400
  (用 `MINIO_*` 同一组凭据对桶做列举与删除,修剪旧快照走的也是这条车道;停驻分区按会话 id 分区,会话 id 删后可被别人重新登记 ⇒ 只按快照名字里的归属记号判、不读任何钟,后来的住客写的带它自己的记号,不碰;升级前写的不带记号的旧形与无住客的 run 写的 `none` 不删);删后前一位住客的空间不再可达(新住客是新的化身)。回收失败不挡删除:一行 `workspace_space_reclaim_failed` + 计数 `server.workspace-space.reclaim-failed`,对象留着。
2401
+ **回收座是一只带并发上界的队列**:同一时刻最多 4 个空间在收(小常数,不是旋钮),托管留存 sweep 的空间与每一次会话删除的回收**共用这一只门** —— 一阵会话删除不会同时起一阵回收,排队的空间在它那次调用的截止之下等;一个慢的空间不再拖住排在它后面的空间。
2254
2402
  **桶的生命周期规则只作兜底**(回收失败的对象、回收截止到点没删的对象、回收没跑完进程就退留下的对象、会话删除那一刻上一条 run 的提交恰好才落下的对象、本版之前已结束的空间留下的对象、不带归属记号的旧形停驻快照与 `none` 停驻快照,只有它能清):规则要对**整个 `<前缀>/`** 按龄过期(跨轮保留在 `<前缀>/<化身>/space/`、停驻快照在 `<前缀>/<会话 id>/`,都在它下面;指针与快照同一条规则);只对 `.tar.gz` 配会留下指向空处的 `latest` —— 那个空间的下一条 run 按上一条「悬空」处置从空目录起(一行 `workspace_latest_dangling`),旧文件已被规则清走。
2255
2403
 
2256
2404
  ### 9.3c k8s 车道:点名 env 转发(`K8S_SANDBOX_ENV`)
@@ -2287,6 +2435,7 @@ k8s 车道每条 run 一只新 pod(pod 寿命 = 泄漏护栏,不跨 run 复用)
2287
2435
  - **卷拿不到 ⇒ 不拒这一轮**:卷建不成(Role 少动词 / API 拒 / 网络,`reason: claim_failed`)、卷在快照时限(`WORKSPACE_SNAPSHOT_TIMEOUT_MS`)内没绑定(集群没有缺省 StorageClass /
2288
2436
  供给器不在,`claim_unbound`)、卷绑上了而挂着它的 pod 起不来(挂载失败 / 卷所在节点没了,`claim_unmountable`)—— 后两种把等它的那只 pod 删掉、换一只不挂卷的 —— 、没有这只会话(会话店答「没有」,`incarnation_unknown`)⇒ run **照常起**:不挂卷、不注入缓存 env,
2289
2437
  计数 `server.workspace-cache.unavailable`(fail-open 登记表第 129 行)+ 一行 `workspace_cache_unavailable`(`space` / `claim` / `reason` / `error`),每条 run 至多一次。
2438
+ **没绑上 / 没挂上(`claim_unbound` / `claim_unmountable`)按空间记一个快照时限**:这个空间的后续 run 在窗内直接不挂卷 —— 不发 claim 请求、不起等卷的 pod(此前每条 run 都等满一个快照时限、删掉第一只 pod 再起第二只),照样计数 + 一行(`error` 点名「记着的」);窗过了再问一次(运维补上缺省 StorageClass 后,窗后第一条 run 就挂上)。记忆放在部署的 per-run 绑定里(进程内、不跨副本、不落盘)。
2290
2439
  - **读住客失败 ⇒ 拒这一轮**:会话店报错(或答出的不是住客令牌)不是「没有空间」—— 与 9.3b 同一只 `connect_failed`(一只 pod 都不建,一行 `workspace_space_unreadable_occupant`,不计数);拒句点名开着的旋钮
2291
2440
  (只开卷时是 `WORKSPACE_CACHE=off`)。重跑即重新读。
2292
2441
  - **委派子代 / 后台代理不挂卷**(与不开空间同一条法:它们与父并发,不共享父的空间)—— 不计数、不出行。
@@ -2299,7 +2448,7 @@ k8s 车道每条 run 一只新 pod(pod 寿命 = 泄漏护栏,不跨 run 复用)
2299
2448
  |---|---|---|
2300
2449
  | `WORKSPACE_CACHE` | `off` | `volume` = 每个空间一只构建缓存卷;`off` = 不建卷(pod 与旧版逐字同)。词表外 ⇒ 拒启。前提:有存储后端(`DB_BACKEND=memory` × `volume` ⇒ 拒启,出路 `WORKSPACE_CACHE=off`)。只在 `REMOTE_EXEC=k8s` 时读 |
2301
2450
  | `WORKSPACE_CACHE_SIZE` | `20Gi` | 卷的申请量(k8s quantity 形,如 `20Gi` / `500Mi`;形不合 ⇒ 拒启) |
2302
- | `WORKSPACE_CACHE_MOUNT` | `/cache` | 卷在 pod 里的挂载点(绝对路径;与工作区挂载 `K8S_MOUNT_PATH` 相同 / 互相嵌套 ⇒ 拒启) |
2451
+ | `WORKSPACE_CACHE_MOUNT` | `/cache` | 卷在 pod 里的挂载点(绝对路径;与工作区挂载 `K8S_MOUNT_PATH` 相同 / 互相嵌套 ⇒ 拒启;落在保留的镜像目录闭集 —— `/bin /boot /dev /etc /home/sandbox /lib /lib64 /opt/sema /proc /root /run /sbin /sys /tmp /usr /var` —— 上 / 之下 / 之上 ⇒ 拒启并点名那一项:卷挂在那里会遮住镜像在那条路径下放的东西) |
2303
2452
 
2304
2453
  **RBAC**:沙箱 Role 要有 `persistentvolumeclaims` 的 `get` / `create` / `delete` / `deletecollection`(回收按标签删 = `deletecollection`;chart 与 cluster 静态清单 7.126.0 起已带)。
2305
2454
  **能力位**:`GET /v1/capabilities.workspaceSync.cache = { mount, size }`,present-iff k8s 车道 ∧ `WORKSPACE_CACHE=volume`(与快照存储无关:没配存储而开了卷的部署,
@@ -2365,9 +2514,10 @@ e2b SDK 自己的建命令握手另有一界(缺省 60 s),比命令的界早时
2365
2514
  **有显式超时时这两只旋钮不参与**:调大不会让一条带超时的 Bash 跑得更久,调小也不会更早切它 —— 要更早切就给命令更短的超时。
2366
2515
 
2367
2516
  切断时报的是真正触发的那一界:活性切断的句子是 `no output for <N> s — cut off by the exec liveness bound (set K8S_LIVENESS_MS or give the command an explicit timeout)`
2368
- (e2b 点名 `E2B_LIVENESS_MS`);错误码沿用引擎的 `timeout`(引擎码表把墙钟与活性两种超界归同一词,靠句子区分)。Bash 工具结果里的 `timedOut: true` / `timeoutSec: N`
2369
- 不再是活性切断的投影,但仍不全是「跑满了 N 秒」:e2b 上建命令卡住被那只 60 s 握手切掉、或沙箱中途死掉时,这一发仍以 `timeout` 码回来,Bash 照旧报
2370
- 「Command timed out after <N>s」(把它们与超时分开的独立码属于引擎的码表)。host 车道没有活性界(命令只受调用方的超时,缓冲 exec 不带超时时受 `HOST_COMMAND_TIMEOUT_MS`)。
2517
+ (e2b 点名 `E2B_LIVENESS_MS`)。**错误码按触发的那一界**(7.132.0 起,引擎 7.52.0 的码表两种超界两个词):静默切断报 `liveness_no_output` 并带窗口秒数
2518
+ (Bash 工具结果是 `livenessNoOutput: true` / `noOutputSec: N`,模型读到「no output for N s」—— 不是「调大超时」的提示),墙钟到点报 `timeout`(`timedOut: true` /
2519
+ `timeoutSec: N`,「Command timed out after <N>s」)。7.129.1–7.131.x 两种切断同报 `timeout`、靠句子区分。e2b 上建命令卡住被那只 60 s 握手切掉、或沙箱中途死掉时,
2520
+ 这一发仍以 `timeout` 码回来(那是供应商自己的界,不是本服务的两只界)。host 车道没有活性界(命令只受调用方的超时,缓冲 exec 不带超时时受 `HOST_COMMAND_TIMEOUT_MS`)。
2371
2521
  ssh / adb / local-docker 三条车道的流式调用同读这一条律:没有车道活性界,只受自己说的静默界 `readTimeoutMs` 约束,到点的切断句与 host 同一句
2372
2522
  (`no output for <N> s — cut off by the command's own idle bound (readTimeoutMs)`);`readTimeoutMs` 为 `0` / 负数 / 非数不算说了界。
2373
2523
 
@@ -2391,6 +2541,7 @@ armed ⟺ 本次请求 permissionMode 是 "auto" ∧ 本部署装配了分类
2391
2541
  | `AUTO_MODE_CLASSIFIER_TIMEOUT_MS` | 未设(⇒ core 缺省) | 7.102.0 起:auto 分类器**一级梯子**的判决上限(整数 ms)→ core `RunnerDeps.autoMode.timeoutMs`。一级到点没裁 ⇒ 走下一级(`AUTO_MODE_CLASSIFIER_FALLBACK` 决定有没有下一级),走完仍没裁 ⇒ 这次门控调用**整条拒**、不出卡;最坏等待 ≈ 级数 × 本值 + 一次装配。**自托管慢网关**(排队 + prefill 以分钟计)在缺省上限内答不出 ⇒ auto 下工具一律被拒 —— 按后端实测留足(如 `180000`)。未设 / 空串 ⇒ 本仓**不铸键**,由引擎落它自己的缺省(不复制上游数字);合法域 `[1000, 2147483647]` 内的十进制整数,`0`(本座没有「关」)/ 亚秒 / 小数 / `1e5` 这类拼法 / 越 Node 定时器上界一律**拒启**。旋钮无条件(与 auto 是否武装无关);不影响 `permissionModeAuto` 读面。目录行见 `GET /v1/config/catalog`(approval 域) |
2392
2542
  | `CROSS_SESSION_INBOUND` | 未设 | 跨终端会话设计线:CC settings 键名 `crossSessionInbound` 的**部署/组织层**(引擎层形的 `managed` 层,CC `policySettings` 对位)—— 本部署对**入站跨会话消息**的治理四态(词表取自引擎闭集,core 7.30.0 起):`wake` 投递且**宿主**可在收件箱增长时起一个 turn / `next-turn` 在下一个 turn 边界投递 / `hold` 停在收件会话的待审队列(模型看不见、不能据此行动)/ `refuse` 本部署整体退出这条车道。⚠️ 本服务宿主**不起 turn**,所以在本服务上 `wake` 按引擎契约的降级臂读作 `next-turn`。🔴 **旧词 `accept` 自 7.100.0(core 7.30.0 提货)起拒启** —— 它的原义就是 `next-turn`,改成那个词即可(零别名,拒启即通告)。🔴 **未设 ≠ `next-turn`**:未设 = 这一层不表态,由其余层与引擎的 mode-parity 判定决定。词表外的值**拒启**。⚠️ **生效前提**:引擎的 peer 目录席(`RunnerDeps.peerDirectory`)在场 —— 7.91.0 起已接线(会话枚举面 ∧ mailbox 店 ∧ 收件人生命周期面三合取),缺任一合取项的部署上本旋钮仍「就位待命」;目录行见 `GET /v1/config/catalog`(approval 域,security 轴) |
2393
2543
  | `CROSS_SESSION_DIALOG_EXPIRY` | 未设(⇒ 引擎缺省 `5m`) | 同族:CC settings 键名 `dialogExpiry` 的部署层 —— 被 hold 的跨会话消息等待人审多久后按**安全缺省**结算(过期丢弃,**带回执**告知发送方,不静默吞)。词表 `60s`/`5m`/`10m`/`never`(`never` = 不设期限);未设 ⇒ 本仓**不铸键**,由引擎落它自己的缺省(不复制上游缺省值)。词表外拒启。⚠️ 生效前提同上。另注:CC 的同名键还有第二个消费面(转发到远端客户端的审批对话框停靠时长),本旋钮**今天不驱动**那一面 |
2544
+ | `PRINCIPAL_INBOX_ASSISTANT` | 未设 | 7.133.0 起:本部署 **principal 收件箱**的消费方声明 —— 独占读它的那只助理的 agent handle(→ 引擎 `RunnerDeps.principalInboxConsumer: { assistant }`)。**它就是跨主体投递的开关**:设了 ⇒ 挂上组织投递门与受理口 `POST /v1/principal-inbox/messages`(同一组织里给另一个用户投一封信,契约 §22;能力位 `principalInboxDelivery`),同时**要求**耐久 mailbox(`DB_BACKEND=mysql\|pg\|local`)、组织成员目录(中心,或 `MEMORY_ORG_DIRECTORY_JSON`)与配额店(SQL 后端起服时建三张配额表),缺一**拒启**并点名缺哪件;空白 handle 拒启;未设 / 空串 ⇒ 没有这条车道,一切与上一版逐字节同。额度写死在门里:发件人每小时 60 次尝试(成没成都算)、同一对主体每小时 10 封、一位收件人每小时合计 30 封、收件箱上界 50 封。⚠️ 本版只有投递半:读信的助理腿随引擎下一版,在那之前信留在箱里没人读 |
2394
2545
 
2395
2546
  **自查读面**(`GET /v1/capabilities.permissionModeAuto`,壳的 `sema doctor permissions` 消费;server 7.106.0 起引擎只剩一种武装极性,
2396
2547
  7.107.0 起成员 `intentArming` 删除,见该条):
@@ -2420,6 +2571,16 @@ GET /v1/capabilities?permissionMode=auto (AUTONOMY=plan 的部署)
2420
2571
  - 读面按调用方身份解 per-principal caps(与引擎同一只 resolver,per-principal 缓存 60s;center 硬失败时受 8s
2421
2572
  超时 + 10s deny 缓存界)。
2422
2573
 
2574
+ **组织层天花板(center 的 per-principal entitlement;7.133.0 起两键生效)**:center 下发给某个 principal 的
2575
+ `runtimeCaps.allowBypassPermissions:false`、`runtimeCaps.allowAutoMode:false`(与既有的 `runtimeCaps.autoMode:false`)由引擎执行。
2576
+ 这个人请求的 `bypassPermissions` / `auto` **不是 4xx**:受理照常,这条腿跑在 `default`(写会问),流上一帧 `engine_notice`
2577
+ `config.permission_mode_unavailable`(`detail.by:"operator_deny"`),`wiring_manifest.permissionMode` 回执
2578
+ `{ requested, effective:"default", foldedBy:"operator_deny" }`。auto 的两键任一为 `false` 即拒,另一键的 `true` 翻不回;`true` / 缺席 = 不拒。
2579
+ 两键的值不是布尔 ⇒ 当作契约破损,这个 principal 的放大面整份 fail-closed。center 取数硬失败的降级窗(最长约 10 秒,之后整份重抓)里
2580
+ bypass 与 auto 同样折回。7.132.x 及更早这两键被服务端丢掉:组织关了 bypass,引擎照放。
2581
+ 部署级的 `TASK_WRITE_FACE_ESCALATION` 管「这台部署给不给任务层掀写围栏」,本条管「组织给不给这个人」—— 两道都过才是 bypass;
2582
+ 能力位 `taskWriteFaceOpen` 只答前一道(部署级,看不到 per-principal 的拒),实际模式读回执。
2583
+
2423
2584
  **settings 层 kill-switch(件⑥)**:请求体 `settings.permissions.disableAutoMode` 受理 **`"disable"` / `true` / `false`** 三形(两套已发布契约的拼写:CC 250 的严极词 `"disable"`,与 `@sema-agent/sdk` `SettingsPermissions.disableAutoMode?: boolean`);受理集之外的值 400 `request.field_invalid`。
2424
2585
  `"disable"` 与 `true` **同义**:把该 run 的生效模式由 auto 折成 default(座不写 ⇒ 不武装;tighten-only,只能关不能开);`false` = 显式「不禁」= **合法且无效果**(本键没有放宽臂——它不是 auto 的开关)。`disableBypassPermissionsMode` 本批不落点。
2425
2586
 
@@ -2442,7 +2603,7 @@ GET /v1/capabilities?permissionMode=auto (AUTONOMY=plan 的部署)
2442
2603
 
2443
2604
  plan 起步的任务把计划交给人之后停在 `plan_review` 门上。`POST /v1/assistant/tasks/:id/plan_review` 里把计划**带出 plan** 的决定
2444
2605
  —— `decision:"approve"`(按原计划开干)与 `decision:"edit"`(按人改过的计划开干;7.122.0 起)—— 可携 **`permissionModeAfter`**
2445
- (闭集 `"default" | "acceptEdits"`,**缺席 ⇒ `"default"`**):`acceptEdits` = 继续跑时对自己工作目录内的写不再逐次征询,`default` = 有手但每次写照常征询。
2606
+ (闭集 `"default" | "acceptEdits"`,**缺席 ⇒ `"default"`**):`acceptEdits` = 继续跑时对写根(任务根 + 请求的 `additionalDirectories`)内的写不再逐次征询(引擎的模式门答),`default` = 有手但每次写照常征询。
2446
2607
  `reject` 的语义是重做计划,plan **原样保留**,在它上面送这一位是 `400`(判据与引擎同句:只有 `reject` 不出 plan)。词表外的值、以及存储模式词不是 plan
2447
2608
  的任务(模型自己调 `EnterPlanMode` 那一形 —— 批准把它送回进 plan 之前的档)送它,同样是 `400`;`AUTONOMY=plan` 下提交的任务存储词就是 plan,照收。
2448
2609
  解除会写回这条会话的续跑输入 —— 此后每一次挂起→续跑都沿用解除后的档,不回落 plan(`AUTONOMY=plan` 也不把它拉回:地板只在提交入口盖一次)。
@@ -2529,23 +2690,44 @@ WebSearch 工具的 `tool_result` 错误文本,回到模型的对话里,**不是
2529
2690
  这纯粹是**客户端约定**,本仓不读取、不校验、也不感知任何这类客户端侧 env 命名(这条命名事实来自任务书,
2530
2691
  本车未读 cli 源码核实,列入收车档「未闭环」)。
2531
2692
 
2532
- ### 9.5b `settings.hooks` 的工具事件 matcher:提交时判什么、拒什么(7.126.0 起结局只取决于配置)
2693
+ ### 9.5b `settings.hooks` 的 matcher:提交时判什么、拒什么(7.126.0 起良性设置的结局只取决于配置)
2533
2694
 
2534
2695
  `PreToolUse` / `PostToolUse` / `PostToolUseFailure` 三个工具事件的 `matcher` 按调用派发到的工具的 **wire 名**匹配(不是别名)。**新提交**(`POST /v1/runs` · `/v1/tasks` · `/v1/tasks/stream`;续跑重放不判)
2535
- 对每只 matcher 做判断,下面三种情形都答 **`400 request.field_invalid`**,体带 `field: "settings.hooks"` 与 `detail`,按 `detail.reason` 分形:
2696
+ 对每只 matcher 做判断,下面四种情形都答 **`400 request.field_invalid`**,体带 `field: "settings.hooks"` 与 `detail`,按 `detail.reason` 分形(闭集,穷举读):
2536
2697
 
2537
2698
  | `detail.reason` | 触发 | `detail` 其余键 | 怎么改 |
2538
2699
  |---|---|---|---|
2539
2700
  | `dead_alias` | matcher 选中某个工具的别名、却选不中它的正名(钩子永不命中) | `event` · `alias` · `canonical` | 改成正名(`ListPeers` → `ListAgents`) |
2540
2701
  | `over_budget` | 正则形 matcher 的一次求值在 `1 000 ms` 内没有结束(灾难回溯) | `event` · `groupIndex`(该事件下第几组,从 0 起)· `budgetMs` | 改成工具名列表(`A\|B`),或去掉嵌套重复的正则写法(如 `(.*)*`) |
2541
- | `invalid_regex` | 正则形 matcher 编不过(如 `(`、`[Read`) | `event` · `groupIndex` | 修正则,或改成工具名列表(`A\|B`) |
2702
+ | `budget_exceeded`(7.132.0 起) | 这次提交里所有已完成的正则形求值的耗时之和超过受理总额度 `5 000 ms`(每只都在单次额度之内,但合起来太慢) | `event` · `groupIndex`(越线的那一组)· `totalMs` | 把慢的 matcher 改成工具名列表(`A\|B`)或简化正则;正则形 matcher 越少越好 |
2703
+ | `invalid_regex` | 正则形 matcher 编不过(如 `(`、`[Read`);**写在任一契约事件上都判**(7.132.0 起;此前只判三个工具事件) | `event`(任一契约事件)· `groupIndex` | 修正则,或改成工具名列表(`A\|B`);matcher 没有用处的事件(如 `Stop`)直接去掉这个字段 |
2704
+
2705
+ `detail` 恒带坐标 `path`(7.132.0 起;matcher 四形 = `["hooks", <event>, <组下标>, "matcher"]`;串段与 `key` 过拒句回显口径:剥控制与双向字符、截到 64 字 + `…`)。**配置本身读不出**的拒(7.132.0 起同带 `field: "settings.hooks"` + `detail`,句子骨架不变):
2542
2706
 
2543
- - **预算是每只 matcher、每次求值各一份**:不共享、不随同一份设置里 matcher 的多少耗尽,判据里不读钟。所以同一份设置的结局只取决于设置本身 —— 机器忙闲不会让它一次 400、一次 202。
2544
- 良性 matcher 的求值是微秒到毫秒级,预算给的是数个数量级的余量;灾难回溯(指数级)用任何预算都跑不完。多只 matcher 时按配置顺序(事件 → 组)遇到的第一只被拒的定结局。
2545
- - **为什么超时是拒绝、不是放行**:运行期每次工具调用都用同一个函数匹配 matcher、没有预算;一只在提交时都求不完的 matcher 放进去,会让之后每次工具调用卡住。
2546
- - 拒句点名是**哪一只** matcher(`settings.hooks.<event>[<groupIndex>].matcher`):`over_budget` 一形带预算、不回显 matcher 原文;`invalid_regex` 一形回显 matcher 原文与编译错误句(各截 200 字符,`detail` 不带这两段)。
2707
+ | `detail.reason` | 触发 | `detail` 其余键 |
2708
+ |---|---|---|
2709
+ | 契约分类器的码(`guard-entry-invalid` 等十一词,连字符拼法) | 分类器整份拒(一块可能藏着守卫钩子)或要剔掉一块(提交面不剔、整份拒) | `path`(那一块)· `key`(那一块上让它坏的键,点得出时) |
2710
+ | `unsupported_entry_type` | 本服务不执行的条目类型(`mcp_tool`) | `path`(那一条的 `type`)· `type` |
2711
+ | `over_cap` / `too_long` | 服务侧上限:组数 16、一个事件的条目总数 32(`path` 指第一条超出的条目)、命令 / URL 8192 字、header 32 个 / 合计 8192 字、`allowedEnvVars` 32 个 | `path` · `count` · `cap` |
2712
+ | `unnamed_refusal` | 分类器说拒却一块都没点名(契约漂移,fail-closed) | `path` |
2713
+
2714
+ - **预算有两层**:① 每只 matcher、每次求值各一份(`1 000 ms`)—— 不共享、不随同一份设置里 matcher 的多少缩水,这一份不读钟;② 一次受理的总额度(`5 000 ms`,7.132.0 起)—— 所有已完成的正则形求值的耗时之和,
2715
+ 超过它就拒(`budget_exceeded`)。字面形(`A|B`)与非工具事件不进求值、不占总额度。良性 matcher 的求值是微秒到毫秒级,两层都给了数个数量级的余量(一份最大的良性配置 —— 3 个工具事件 × 16 组 —— 总耗时在毫秒级),
2716
+ 所以良性设置的结局只取决于设置本身:机器忙闲不会让它一次 400、一次 202;只有求值总耗时逼近 `5 000 ms` 的病态设置才可能落在边界两侧。灾难回溯(指数级)用任何预算都跑不完。
2717
+ 多只 matcher 时按配置顺序(事件 → 组)遇到的第一只被拒的定结局。一次受理最坏占用事件循环 ≤ 总额度 + 一次求值的额度(此前没有总界)。
2718
+ - **为什么超时是拒绝、不是放行**:运行期每次工具调用对同一只 matcher 的求值也带这一份预算(7.133.0 起,见下);一只在提交时连目录里几个短名字都求不完的 matcher 放进去,运行期每次调用都要白烧一整份预算。
2719
+ - **运行期同一只口、同一个预算(7.133.0 起)**:单用户车道上 hooks 被采纳后,每次工具调用对正则形 matcher(以及条目 `if` 条件的名部)的每次求值也各拿一份同样的 `1 000 ms` 墙钟预算,字面形(`A|B` / `*` / 缺省)不进求值、不占预算 —— 不另设运行期旋钮。
2720
+ 受理面只对目录里的别名 / 正名这几个短名字求过值,运行期要对任意别的工具名(MCP 工具名等)求值:受理过了的 matcher 在某个名字上灾难回溯,此前会同步占住事件循环(此前运行期没有预算)。
2721
+ **超预算 ⇒ 这一组按「选中」处理**(守卫钩子不被跳过,组里的条目照常跑;`if` 超预算同:条目照跑;这样跑起来的条目只有否决生效 —— 放行与 `updatedInput` 不作数,托管组的条目另记没跑成、`PreToolUse` 折问人)并**响亮登记**:一行 warn `hook_matcher_over_budget`(`event` · `scope` · `expression` · `value` · `budgetMs` · `selected: true`)
2722
+ + fleet 流上一帧 `hook_notice` / `hook_non_blocking_failure`,`reason: "matcher_over_budget"`(契约附录 E.2)。每次发生各登记一次,一直在坏就一直说;最坏占用是「(超预算的组数 + 超预算的 `if` 条目数)× `1 000 ms`」每次工具调用 —— 看到这行日志就把那只 matcher(或 `if` 条件)改成工具名列表(`A|B`)或简化正则。
2723
+ 日志与帧里的 matcher / 条件 / 名字先脱敏、再剥控制与双向字符、截 200 字。
2724
+ - 拒句点名是**哪一只** matcher(`settings.hooks.<event>[<groupIndex>].matcher`):`over_budget` 一形带预算、`budget_exceeded` 一形带总额度、都不回显 matcher 原文;`invalid_regex` 一形回显 matcher 原文与编译错误句(各截 200 字符,`detail` 不带这两段)。
2547
2725
  - **这一判只在 hooks 会被采纳的车道上执行正则**(单用户车道;多租户车道 `settings.hooks` 整只被忽略,只做不执行正则的字面判):多租户部署不会因此多出一个在提交线程上跑调用方正则的面。
2548
- - 不变的:matcher 超 `8 192` 字符不读(照旧受理,运维侧记一笔 `server.hooks.matcher-unjudged`)。编不过的正则:三个工具事件上 `400`(上表第三行,7.126.0 起;此前受理而运行期该组从不跑);写在 `StopFailure` / `PreCompact` / `PostCompact` 上的仍受理,运行期该组不跑并记 `hook_matcher_invalid`。
2726
+ - 不变的:matcher 超 `8 192` 字符不读(照旧受理,运维侧记一笔 `server.hooks.matcher-unjudged`)。
2727
+ - **编不过的正则与事件无关**(7.132.0 起):写在 33 个契约事件里的哪一个上都 `400`(上表第四行)—— `StopFailure` 的 `error` 词、`PreCompact` / `PostCompact` 的 `trigger` 词、`SessionStart` 等事件,以及 matcher 被忽略的 `Stop` / `UserPromptSubmit` / `PostToolBatch`。
2728
+ 7.126.0–7.131.x 只判三个工具事件,别的事件上同一串被受理:读 matcher 的引擎事件(`StopFailure` / `PreCompact` / `PostCompact`)运行期该组静默不跑(只落 `hook_matcher_invalid` 日志),壳跑的事件处置归壳。
2729
+ 「选中别名而选不中正名」(上表第一行)仍只属三个工具事件。
2730
+ 续跑重放不判:存量里的坏 matcher 照旧采纳 —— 读 matcher 的引擎事件上那一组不跑(记 `hook_matcher_invalid`),**matcher 被忽略的 `Stop` / `UserPromptSubmit` / `PostToolBatch` 照旧跑条目**。多租户车道(`settings.hooks` 整只被忽略)不判。
2549
2731
 
2550
2732
  ### 9.5c `settings.hooks` 的 `http` 钩子:header 里的环境变量展开(7.126.0 起与壳同一只读法)
2551
2733
 
@@ -2557,20 +2739,55 @@ WebSearch 工具的 `tool_result` 错误文本,回到模型的对话里,**不是
2557
2739
  - 置空(名单外 / 凭据名)时日志一行 `hook_http_header_env_blanked`(名字与原因,不带值)。
2558
2740
  例:`"headers": { "Authorization": "Bearer ${HOOK_TOKEN}" }`、`"allowedEnvVars": ["HOOK_TOKEN"]`,并在请求 `settings.env` 里给 `HOOK_TOKEN`。7.125.x 及更早的读法不同(升级见 MIGRATION)。
2559
2741
 
2742
+ ### 9.5c-bis `settings.hooks` 的执行模型与时限(7.132.0 起)
2743
+
2744
+ - 被采纳的 `settings.hooks`(单用户车道)在引擎里是**一张**扩展注册,名字 `settings.hooks`(`wiring_manifest.mods[]` 里看得到它;
2745
+ 审批卡 `allowRuleShadowed` 的 `hook` 成员、钩子失败时模型读到的拒句都点名它)。本部署自己另挂两张只观察的注册:恒挂的拒绝计量
2746
+ `server.permission-denied-meter`,`TOOL_TRACE=true` 时的 `server.tool-trace`(品牌部署另有一张提示装配站上的 `server.brand-identity`,只答品牌行 overlay,不在工具门上)。观察者的判词不采纳,链里唯一的答者是请求的钩子。
2747
+ - 每个事件的语义与此前相同:`Stop` 条目在主任务结束与委派子代结束时都跑;`PermissionDenied` / `SubagentStop` / 两个 model-switch 事件属壳,服务端不跑。
2748
+ - **`HOOKS_TIMEOUT_MS`** = 这张注册的 `budgetMs`。引擎对它的定义是 "Each handler's own time" —— 每个 handler 自己的时间(一个事件跑一遍本请求的钩子条目;
2749
+ 引擎的钟,handler 等 `next` / `$` 调用的那段暂停),**不是**钩子子进程的墙钟。**未设 = 600000 ms**(= 单条命令超时的上限;引擎的钟在 handler 起跑时就开始走,
2750
+ 命令的 `timeout` 钟要等 shell 解析和起进程之后,所以 `timeout` 配到 600 s 的命令跑满时,预算会先到);`0` 按字面(零预算)。超预算:`PreToolUse` 上该调用被拒(fail-closed,拒句点名 `extension "settings.hooks"`)、
2751
+ `UserPromptSubmit` 上该提交被拒,其余事件跳过这一轮钩子。子进程自己的墙钟另有两只,照旧:单条命令的 `timeout`(上限 600 s)与一个事件 120 s 的总墙钟(到点不再起下一条)。
2752
+ - **预算到、或任务被取消 ⇒ 这个 handler 里在跑的命令钩子被叫停**(7.133.0 起;此前它要跑到自己的 `timeout` 才停,取消之后还可能接着写文件):进程组先收 SIGTERM,
2753
+ 1 秒到点组长还在就补一发组 SIGKILL(组长在宽限内退了就不再发 —— 退出的进程号可能已被回收;代价:组长一退,组里屏蔽 SIGTERM 且不攥管道的后代不再被追杀),
2754
+ 进程组长退出才算收走;这个 handler 里还没起的后续条目不再起。结局不变(上一条的 fail-closed 拒 / 跳过),运维日志多一行
2755
+ `hook_command_killed`(`event` / `type` / `killed: true` / `signal`:`SIGTERM` = 组长在宽限内就退了,`SIGKILL` = 宽限到点组长还在、升级过)。钩子确实要跑得更久:调大 `HOOKS_TIMEOUT_MS`
2756
+ (与条目自己的 `timeout`);要在被叫停时收尾,在命令里 `trap … TERM` 快速落盘(宽限 1 秒,不可配)。
2757
+
2560
2758
  ### 9.5d MCP 服务器 http 传输的身份头:`principalHeader` 与 `assertionHeader` 二选一(请求体与部署声明同一种读法)
2561
2759
 
2562
2760
  `mcpServers[].transport`(`kind:"http"`)上有两个「用哪个头送身份」的可选键,**只能设一个**:`principalHeader`(runner 把该任务已认证的终端用户 principal 注入这个头)与 `assertionHeader`(托管连接器断言所用的头**名**)。
2563
2761
  - 请求体(`POST /v1/tasks` · `/v1/tasks/stream` · `/v1/runs`)与部署声明(中心下发 / 本地 `config.d/mcp.json`)判据相同:两键同设 ⇒ 请求体新提交腿 `400 request.body_shape`(拒句点名条目与两键,与契约包解析期的拒句逐字相同);`assertionHeader` 不是字符串 ⇒ `400 request.field_invalid`;部署声明里同设的那一条不进。耐久 resume 腿重放存量请求体时不拒、也不摘 server:server 照挂,`principalHeader` 胜、`assertionHeader` 不生效,一行 warn `mcp_assertion_header_ignored`(`server` / `reason` / `note`)。
2564
2762
  - `assertionHeader` 的**值**是头名,本服务不铸、也不校验具体词(只判是字符串且与 `principalHeader` 互斥);头名由引擎(上游)定为 **`connector-assertion`**(无 `x-` 前缀;引擎发送座落地后本服务只认这一个值),条目填别的名 ⇒ 连接器侧 401。
2565
2763
  - 本版只承载、不发送断言:声明了 `assertionHeader` 的 server 照常挂载,但不带断言头;每台这样的 server 一行 info `mcp_assertion_header_not_sent`(`server` / `note`)。探测口 `POST /v1/capabilities/mcp/probe` 对声明了该键的条目整条拒 `400 request.field_invalid`(探测不拨一份少了断言头的配置)。
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:改对那个键,或删掉它。
2764
+ **发不发有一位可读**:`GET /v1/capabilities` 的 `mcpAssertionHeaderSent`(布尔,今天恒 `false`)—— 它与剥键、那行 info、探测口的那一拒读同一只开关,**互斥**:位 `false` ⇒ 三者都在(别指望断言头会发出去);位 `true` ⇒ 三者都不再出现、声明的头名随规格交给引擎。按位分支,不要匹配 info 文字;键缺席 = 老 server,按「不发送」读。
2765
+
2766
+ ### 9.5e 请求体 `mcpServers` 的列表与条目键:写坏,整份新提交 400(7.131.0 起;列表级与词表外的键 7.132.0 起;`startupTimeoutMs` 的值形由引擎在拨号时判,见本节首段)
2767
+
2768
+ 条目的受理键 = `name` · `transport` · `allowTools` · `toolAxes` · `toolFaces` · `contentOrigin` · `elicitation` · `source` · `startupTimeoutMs`(7.132.0 起)(`transport` 的子键:`kind` · `command` · `args` · `env`(stdio),`kind` · `url` · `headers` · `principalHeader` · `assertionHeader`(http))。**新提交上每个键只有一只判官;除 `startupTimeoutMs` 外,判官在受理口,写坏的答案只有一种**(下面第一条:400)。`startupTimeoutMs`(这一只 server 自己的启动界,毫秒整数)的判官只在引擎里(引擎包根没给读口),受理口不判它的值形、原样交给引擎:数字串 / 小数 / 越界 ⇒ 请求照常 **200**,引擎拨号时**只拒这一只 server**(`config.mcp_startup_timeout_invalid`,兄弟照挂)。下面各条说的「写坏 ⇒ 400」是对其余受理键:
2769
+ - **新提交**(`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 名与该键判官的原因句,**整份请求不收** —— 不挂一半、不丢声明、不默认。原因句不回显声明的值,有几处例外,回显只有一只口径(剥控制与双向字符、截到 64 字、超出加 `…`):server 名(含写坏的 `name` 值,另报送来的原长度)、`contentOrigin` 词表外的值、`toolFaces` 点名的工具名;`toolAxes` 点名的工具名在引擎写的原因句里,由引擎截长(80 字);`transport.url` 的值从不回显:它常带凭据。可选键**缺席**永远合法(真「没声明」),必填的 `name` / `transport` 缺席同样 400;在场的 `null` 不是缺席。与单用户 / 多租户无关。列表本身同一条律(7.132.0 起,先于条目判):`mcpServers` 在场却不是数组、超过 32 条(整份拒,不挂前 32 条)、某一条不是对象 ⇒ `400 request.field_invalid`,拒句点名 `mcpServers` 或 `mcpServers[i]` 与形的种类;条目层或 `transport` 层出现词表外的键 ⇒ `400 request.body_shape`,拒体带 `unknownKeys`(全路径、至多 20 条)。`mcpServers` 缺席或为 `null` = 没声明,照常受理。
2770
+ - **耐久 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`/`principalHeader` 与词表外的键 ⇒ 丢掉那一个键、server 照挂;`transport` 的 `env`/`headers`(名 → 串记录)对 JSON 存体按 7.131.x 的读法交:有值不是串的成员 ⇒ 整键不交,只有 `__proto__` 名的成员坏 ⇒ 剔掉它、其余照交;server 照挂,一行运维 warn `mcp_transport_members_dropped` 点名(见 9.5c-bis 同段);条目不是对象 ⇒ 这一条不进(`malformed_entry`);超过 32 条 ⇒ 第 33 条起只记 `over_cap`;整只不是数组 ⇒ 整只忽略 + 运维 warn `mcp_injection_ignored`。
2771
+ - `POST /v1/capabilities/mcp/probe` 问的是新提交那**同一只判官**:上面每一形同码同句(含 `unknownKeys`),拒之前零拨号;判官受理的申报,探测口拨的就是一条任务提交会交给引擎的那一份(判官的合法归一照收)。它多出的两形:本口的体就是这份列表,`mcpServers` 缺席或为 `null` ⇒ `400 request.body_shape`(缺必需字段);声明了 `transport.assertionHeader` 的条目 ⇒ `400 request.field_invalid`(见 9.5d:本版承载、不发送,新提交照挂,探测不拨一份少了断言头的配置 —— 同一只谓词,后果按门分)。7.131.x 上本口另有一只判法(申报原文与读器产物逐字比),列表不是数组 / 条目不是对象答 `request.body_shape`。
2772
+ - 7.130.x 及更早:`allowTools` / `toolFaces` / `name` / `transport` 写坏 ⇒ 200 + 这条 server 不挂;`elicitation` / `source` / `transport` 的可选子键写坏 ⇒ 200 + 悄悄丢键。7.131.x 及更早:列表整只不是数组 ⇒ 200、整只忽略;条目不是对象 ⇒ 200 + 点名丢弃;超过 32 条 ⇒ 200、挂前 32 条;词表外的键 ⇒ 悄悄不读。升级后这些请求变 400:改对那个键(或删掉它)、把列表改成数组、删掉第 33 条起的条目。
2773
+
2774
+ ### 9.5f 中心提示层:一种 wire 形、按场景挂段、品牌行与「无持久记忆」披露(7.132.0 起)
2775
+
2776
+ - **形**:中心层只有一种 —— 中心(web-admin)下发的 `prompts` 键与本地 `config.d/prompts.json` 读出的中心层都是 `{ schemaVersion: 2, artifact }`(`artifact` = 引擎 `buildPromptArtifact` 铸的发布形,
2777
+ `payload.schemaVersion: 3`)。判官一只:契约包 `EffectivePromptsWire` 先过,再交引擎 `verifyPromptArtifact`;任一不过 ⇒ **整份候选拒**(一个域都不应用,停在上一份好配置),
2778
+ `config_candidate_rejected` 点名 `prompts`,`errors` 是两处的原句。中心不带 `prompts` 键 = 不下发中心层(已采纳的撤下)。存储原文(`{ artifact }` / `{}` / 13.x 的 `{ bindings: [] }`)放上 wire 也整拒。
2779
+ - **本地 `config.d/prompts.json`**:`{ artifact?, workers? }`(`artifact` = 中心编辑器导出的 artifact 身份 `{ payload, artifactDigest }`);本地没有 worker 身份 ⇒ 恒用 `artifact`,
2780
+ `workers` 在场时起服与之后每次采纳一份变了的候选时各一行 `config_unread_key`(与 `config_unread_file` 同频;`key: "workers"`、`names` 点名条目;不判拒)—— 只写了 `workers` 的本地文件不下发中心层。
2781
+ 带 `bindings` / `catalog` 的旧文件被契约包拒 ⇒ `npx @sema-agent/settings-schema migrate <config.d 目录>`。
2782
+ - **按场景挂段**:本服务把每个请求的场景名写进 `TaskSpec.promptBinding`;artifact 里按场景的段写 `when: { binding: { anyOf: ["code"] } }`(或 `noneOf`)。同一目标一条通用 `replace` 加一条
2783
+ 场景 `replace` 时两条的条件必须互斥(通用那条 `noneOf: [场景]`),否则引擎拒整份 artifact。委派子代继承父任务的场景名。
2784
+ - **生效**:新会话(与顶层会话的每次压缩)采用中心源的当期候选,进行中的会话钉着它开始时那一份;采纳日志 `center_prompts_adopted { artifactDigest, sourceRevision }`。
2785
+ 中心源启动恢复读到一份引擎拒的 / 读不出的副本 ⇒ 一行 `prompt_composition_warning`(`classification: "center-source-restore"`)。
2786
+ - **部署序**:web-admin 与本服务同一维护窗升级、先中心后服务;回滚同序(窗内两端互拒对方的形,停在 LKG)。
2787
+ - **品牌行**(`CONFIG_PROVIDER=local` 与 `run-local`):`default` / `code` 两场景的 role 层以 Sema 品牌行开头(部署座的一条 overlay,清单段 `sema/identity`);`code-review` / `scan` /
2788
+ 中心声明的场景与委派子代不带。
2789
+ - **「本会话没有持久记忆」**:引擎判、引擎渲染(记忆面没挂 ∧ 名册不能持久 ∧ 不是委派子代)。本部署有引擎推断看不见的持久通道(MCP 记忆服务器、自定义写工具)⇒ `MEMORY_PERSISTENCE_CAPABLE=true`
2790
+ 撤回这段披露;host 车道自动声明。
2574
2791
 
2575
2792
  ### 9.6 trace 脱敏的射程:与出站请求体无关
2576
2793
 
@@ -2631,8 +2848,8 @@ docker 推送的鉴权走构建宿主上的 docker 凭据存储(`docker login`),
2631
2848
  **`BAKE_ENV_ALLOW=NAME,NAME`。** 配方真要一枚密钥形 env(例如 build.sh 里自己做 registry 登录要 `REGISTRY_PASSWORD`,或
2632
2849
  `git fetch` 的 askpass 读 `GIT_TOKEN`)⇒ 点名它,runner 把该键从本进程 env **原样**放回给每一只子进程组(部署方显式承担)。
2633
2850
  与服务侧 `LSP_ENV_ALLOW` 同一只解析器、同一只放行谓词:
2634
- - 形错(空段 / 尾逗号 / 非变量名形,如 `$NPM_TOKEN`、`a b`、`NAME=value` 整段贴入 / 点名任一只名单型旋钮)⇒ runner **拒启**,
2635
- `bake_runner_fatal` 那一行点名旋钮与第几段,**不回显值**;
2851
+ - 形错(空段 / 尾逗号 / 非变量名形,如 `$NPM_TOKEN`、`a b`、`NAME=value` 整段贴入 / 点名任一只名单型旋钮 / 点名本服务自有凭据名,7.132.0 起)⇒ runner **拒启**,
2852
+ `bake_runner_fatal` 那一行点名旋钮与第几段,**不回显值**(自有凭据那一形带引擎拒白名单的同一句);
2636
2853
  - 本进程环境里没有的名 ⇒ 起服一行 `config_env_entry_rejected`(只报序号)、不放行;
2637
2854
  - 旋钮原文本身按凭据类对待,不进子进程;
2638
2855
  - 生效名单(点到且环境里真有的名)见起服行 `bake_runner_start.envAllow`(只键名);
@@ -2774,6 +2991,11 @@ web-admin 拉取 / fleet 公告 / 凭证续签、WebSearch、对象存储、OTLP
2774
2991
  ⇒ ok;completed 非 success(零步骤 = CI 没起,如 7.103.1 的账单真案 / 有步骤 = CI 红)、排队零步骤、读不出 ⇒ red(读不到 ≠ 健康)。
2775
2992
  它**不再是发车链的一道门**:门 1.11 随同一裁定撤出(它的前提「CI 起不来 ⇒ tag 推了也发不了包」已不成立),起链不再需要 `GH_TOKEN`,
2776
2993
  放行旋钮一并删除。CI 起不来今天挡的是镜像与 main 上的全套,不挡发包。
2994
+ 读数行尾另有三键 `image=<态> imageArch=<平台逗号列表|-> imageTag=<vX.Y.Z|->` —— **镜像格**:本仓本地最新的 `vX.Y.Z` tag 的镜像 manifest 在 GHCR 上在不在、几个平台
2995
+ (`image` 闭集 `present-multiarch` / `present-single` / `absent` / `unreadable`;镜像名从 `ci.yml` 的 `image` job 读,不手写)。它**匿名**读 registry,不经 Actions、不吃 `GH_TOKEN`,
2996
+ 所以账单类的病(CI 的 run 起不来、镜像没出)里它照样有读数;也因此**它是信息格,不进 `verdict`、不改退出码**。读不到(网络 / 本地没有 release tag / `ci.yml` 读不出 / 形读不懂)
2997
+ 恒写 `unreadable` 并在 stderr 说哪一步 —— 不是缺席,更不是绿;`absent` 只在 manifest 回 `404 MANIFEST_UNKNOWN` 时给,刚推的 tag 要等 CI 的 test 走完、`image` job 构建并推送完才有镜像,
2998
+ 这段时间里读到它是常态,过了仍 `absent` 才是镜像腿没出。最新 tag 取本地 tag(发车机上有),巡查机上的 tag 不新就先 `git fetch --tags`。
2777
2999
  - **`scripts/remote-gate.sh ship`** 只把 `git ls-files --cached --others --exclude-standard` 列出的文件打包上 k3s8(已跟踪 + 未跟踪而未被 ignore;
2778
3000
  工作区未提交的改动照带),被 gitignore 的本地探针测试 / scratchpad 读数不再上船。清单生成不出来(不在 git 工作树里 / 空树)⇒ 在碰远端目录之前停。
2779
3001
  - **core 提货门**(`node scripts/core-read-table-gate.mjs`)求交的是 **(当前钉, 目标]** 区间里 core 的**每一版**清单:从目标清单沿 `from`
@@ -2861,7 +3083,10 @@ web-admin 拉取 / fleet 公告 / 凭证续签、WebSearch、对象存储、OTLP
2861
3083
  (形照 core 的 `wire-changes/core@<ver>.json`:`added` / `removed` / `changed` / `narrowed` + `closedSetMembers` + `breaking`)。源 = 契约附录
2862
3084
  A.1 ∪ A.1b 错误码(属性 = 状态码)、B.1 能力位 / B.2 `taskSettings` 子键(属性 = 谓词类)、D.3 `engine_notice` 白名单(属性 = `detail`)、
2863
3085
  `src/trace/wire-projection-faces.ts` 的 `WIRE_PROJECTION_FACES`;对比上一 tag 与本版(tag 在读 tag,不在读工作树)。源读不懂 ⇒ exit 2
2864
- (不产出「零变更」清单)。随 release commit 提交;查账门(门 1.6)第三腿 `--check` 核它在仓且与生成输出逐字节同,缺席 / 陈旧 ⇒ 红。
3086
+ (不产出「零变更」清单)。面表里带 `keys` 声明的面(7.133.0 起:`prompt_assembled` 清单行的十二层,账本行 / turns 块 / trace 帧同一形)
3087
+ 做**键级**比较,与面的增删用同一张表、同一只比较器:删一个键或改了键的值形 / 容器 / 嵌套面 / 由必选变可选 ⇒ `removed` / `narrowed`(`breaking`),
3088
+ 新增键 ⇒ `added`,由可选变必选 ⇒ `changed`;该面此前没有键声明(首次登记)时既有的键进 `registered`,不当本版新上 wire。其余帧面的键集仍由 `coreType`
3089
+ 在安装态 core 里展开,生成器看不见它们的键级增删(core 升版带来的键变化由提货批的读面 × core 清单求交门与各面的 `_Guard*` 编译钉盯)。随 release commit 提交;查账门(门 1.6)第三腿 `--check` 核它在仓且与生成输出逐字节同,缺席 / 陈旧 ⇒ 红。
2865
3090
  `wire-changes/server@7.104.0.json` 是回填的第一份。
2866
3091
  - **台账 `refs:` 列建议**:`node scripts/ledger-refs.mjs [--comms-dir <dir>] [--json]` 只读 sema-comms 的 `debts/DEBTS-server.md` /
2867
3092
  `PLANS.md`(server 段)/ `bugs/B-*.md`(文件头 `owner:` 格),按「一方点了另一方的号」双向互链,输出每行的 `refs:` 建议值(号按数值认,
@@ -2996,7 +3221,8 @@ UPDATE session_policy_rev_mark SET rev = 0, gen = gen + 1 WHERE policy_key = '<w
2996
3221
 
2997
3222
  一条什么都不拒的 deny 已经在库里时,会话属主自己删得掉(删它不算放宽)。
2998
3223
 
2999
- **行自带文法戳。** 7.129.0 起每一行会话规则自带规则文法戳 `grammar`(写口判官盖,本版写下的每一行都是 `1`;店整只存、读回原样交出)—— `GET` / `PUT` 回包的 `rules` 与同步导出的
3224
+ **行自带文法戳。** 7.129.0 起每一行会话规则自带规则文法戳 `grammar`(写口判官盖引擎当前的文法:7.129.0–7.131.x 写下的行是 `1`,7.132.0 起写下的行是 `2`;
3225
+ 早先写下的行保留自己的戳、引擎按它的戳读;店整只存、读回原样交出)—— `GET` / `PUT` 回包的 `rules` 与同步导出的
3000
3226
  记录里多这一个键。客户端改规则时只写三只桶,不要把读回的 `rev` / `gen` / `grammar` 一起回传:带着 `rev` 却没有 `grammar` 的值引擎按存量行的文法(0)读,括号形 allow 条目答
3001
3227
  `400 rules.inert_entry`(`reason: "earlier_grammar"`;出路在句子里);`grammar` 坏或比引擎新 ⇒ `400 rules.invalid_shape`(`field: "grammar"`)。
3002
3228
 
@@ -3005,11 +3231,13 @@ UPDATE session_policy_rev_mark SET rev = 0, gen = gen + 1 WHERE policy_key = '<w
3005
3231
  `session_rule_row_retired_bucket`(`policyKey` / `sessionId` / `buckets`)。出路是在库里把那一行改写成上面的写法(`rev` / `gen` 不动),或删掉会话(删会话即删规则)。
3006
3232
  同一次改名里每一行改写成当前文法的同一句话:没有戳的行是 7.128.x 写的,引擎按它存下时的文法读 —— 那种文法把 `toolAllow` 的条目当整串工具名比,`Bash(rm:*)` / `Edit(src/**)` / `Bash(*)`
3007
3233
  这类条目匹配不到任何工具、不放行任何东西,7.129.0 读成规则;引擎读成「名了不存在的工具」的 `toolAllow` 条目删掉,名单保持在场(删到空 = 什么都不放行,与升级前同判),其余条目原样,
3008
- 每一行盖 `grammar: 1`,`rev` / `gen` 不动。`toolDeny` 里的条目一个字节不动、不点名(deny 规则在两种文法下同义;7.128.x 上不拒,升级后拒)。`toolAllow` 里的 `*` 不删:引擎在任何文法下都把
3009
- 带 `*` 的 allow 名单在引擎 7.51.2 的旧文法读法下整张作废(7.128.x 其余条目照放;引擎已知限制,已请托修正)—— 改名那一拍对这类行不改写、不盖戳、只点名(`wildcardMasked`),升级后这种会话每个调用都拒,operator 去掉 `*` 重写即恢复。有被删条目的行改名那一次起服各一条 warn `session_rule_row_old_grammar_entries`
3234
+ 每一行盖当前文法的戳(7.129.0–7.131.x 盖 `grammar: 1`,7.132.0 起盖 `2`),`rev` / `gen` 不动。`toolDeny` 里的条目一个字节不动、不点名(deny 规则在两种文法下同义;7.128.x 上不拒,升级后拒),**唯一的例外**(7.132.0 起):`toolDeny` 里有引擎读成「不是任何一条规则」的条目(空串、`-deploy`、非 ASCII 名 ……)的行整行不改写、不盖戳,改名那一次起服各一条 warn `session_rule_row_unreadable_deny`(`policyKey` / `sessionId` / `toolDenyUnreadable`);这样的会话引擎每一次工具调用都拒,出路是 operator 用 `PUT /v1/sessions/:id/policy` 写一份读得出的新值,或删会话;同一行还带退役命令桶的(warn 多一键 `retiredBuckets`)`PUT` 读不了,只能在库里按新文法改写那一行(`rev` / `gen` 不动)或删会话。`toolAllow` 里的 `*`
3235
+ 与别的读不出的条目同一条判(7.132.0 起):引擎在旧文法里把它读成什么都不放行的一条、名单其余照放(与 7.128.x 同),所以删掉、盖戳。7.129.0–7.131.x 的引擎把带 `*` 的名单整张读成作废,
3236
+ 那几版的改名对这类行不改写、只点名 —— 那样留下的无戳行,7.132.0 起照上面这条读(`*` 不放行、其余照放),要它按新文法生效就 `PUT` 写回。有被删条目的行改名那一次起服各一条 warn `session_rule_row_old_grammar_entries`
3010
3237
  (`policyKey` / `sessionId` / `toolAllowDropped`),之后不再报;被删的条目要按新文法生效,升级后 `PUT` 写回。改写在它下面跑的那一拍列叫
3011
- `rule_entries_old_grammar`,改完才改成 `rule_entries`:起服在改写中途或其后的任何一步(终名改名、扫描、后续迁移)失败 ⇒ 拒因带有被删条目的行的收据、起服日志先打
3012
- `one_time_migrations_interrupted` 与逐行的 `session_rule_row_old_grammar_entries` 再抛(这些收据只在改写发生的那一拍的起服日志里出现,请保存;下一次起服不再有;只盖戳的改写不记);
3238
+ `rule_entries_old_grammar`,改完才改成 `rule_entries`:起服在改写中途或其后的任何一步(终名改名、扫描、后续迁移)失败 ⇒ 拒因带本次起服已提交的全部读数(7.132.0 起每一条一次性迁移
3239
+ 同一只收据账:各步的计数、落定了的形读数、有被删条目的行与已删的规则行),起服日志先打 `one_time_migrations_interrupted`(读数键与 `one_time_migrations_applied` 同名)与逐行的那几种
3240
+ (`session_rule_row_old_grammar_entries` 等)再抛(这些收据只在改写发生的那一拍的起服日志里出现,请保存;下一次起服不再有;只盖戳的改写不记);
3013
3241
  失败或被杀时列停在进行中的名,下一次起服续改;回滚时从那个名改回(`grammar` 对旧版是一个它不读的多余键)。
3014
3242
 
3015
3243
  **升级与回滚。** 7.128.x 及以前的副本按整串工具名读规则,读不懂 `Bash(rm:*)` 这类条目。改名之后它们在这只库上找不到规则列:还在跑的旧副本读写规则的请求失败、会话任务起不来,