@sema-agent/server 7.132.0 → 7.134.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 (473) hide show
  1. package/MIGRATION.md +353 -0
  2. package/README.md +8 -9
  3. package/README.zh-CN.md +7 -6
  4. package/USAGE.md +301 -64
  5. package/deploy/sema-up/README.md +35 -1
  6. package/deploy/sema-up/chart/Chart.yaml +1 -1
  7. package/deploy/sema-up/chart/templates/NOTES.txt +15 -0
  8. package/deploy/sema-up/chart/templates/_helpers.tpl +129 -0
  9. package/deploy/sema-up/chart/templates/sealed-key.yaml +24 -0
  10. package/deploy/sema-up/chart/templates/server-v4pro.yaml +19 -2
  11. package/deploy/sema-up/chart/templates/server.yaml +19 -2
  12. package/deploy/sema-up/chart/values-full-single.yaml +5 -1
  13. package/deploy/sema-up/chart/values.yaml +34 -0
  14. package/deploy/sema-up/stack/compose.yaml +3 -0
  15. package/deploy/sema-up/stack/sema-stack-cluster.yaml +4 -2
  16. package/dist/adoption/plan.d.ts +3 -2
  17. package/dist/adoption/plan.js +7 -5
  18. package/dist/adoption/runner.js +3 -10
  19. package/dist/adoption/sql.d.ts +3 -2
  20. package/dist/adoption/sql.js +3 -5
  21. package/dist/adoption/wire.d.ts +4 -1
  22. package/dist/adoption/wire.js +1 -0
  23. package/dist/approval-ask-audit-store.d.ts +32 -14
  24. package/dist/approval-ask-audit-store.js +272 -74
  25. package/dist/approval-ask-machine.d.ts +15 -0
  26. package/dist/approval-ask-machine.js +6 -0
  27. package/dist/approval-ask-retention.d.ts +136 -0
  28. package/dist/approval-ask-retention.js +102 -0
  29. package/dist/approval-card.d.ts +83 -23
  30. package/dist/approval-card.js +45 -15
  31. package/dist/approval-content-kind.d.ts +5 -2
  32. package/dist/approval-content-kind.js +16 -3
  33. package/dist/approval-deny-reasons.d.ts +1 -1
  34. package/dist/approval-gate-currency.d.ts +72 -0
  35. package/dist/approval-gate-currency.js +94 -0
  36. package/dist/approval.d.ts +15 -0
  37. package/dist/approval.js +55 -17
  38. package/dist/audit.d.ts +27 -10
  39. package/dist/audit.js +12 -16
  40. package/dist/bake-runner/main.js +3 -2
  41. package/dist/boot/coordinators.d.ts +6 -1
  42. package/dist/boot/coordinators.js +6 -3
  43. package/dist/boot/deferred-sandbox-path-env.js +1 -1
  44. package/dist/boot/execution-env.js +9 -7
  45. package/dist/boot/execution-lane.js +11 -0
  46. package/dist/boot/leader.d.ts +2 -0
  47. package/dist/boot/leader.js +4 -1
  48. package/dist/boot/memory-boundary.js +2 -2
  49. package/dist/boot/memory-consolidation.d.ts +5 -4
  50. package/dist/boot/memory-consolidation.js +7 -6
  51. package/dist/boot/org-delivery.d.ts +28 -0
  52. package/dist/boot/org-delivery.js +20 -0
  53. package/dist/boot/read-deny-anchor-audit.js +2 -2
  54. package/dist/boot/reapers.d.ts +3 -0
  55. package/dist/boot/reapers.js +21 -1
  56. package/dist/boot/resolve-spec.d.ts +3 -0
  57. package/dist/boot/resolve-spec.js +60 -81
  58. package/dist/boot/runner-deps.d.ts +18 -5
  59. package/dist/boot/runner-deps.js +10 -0
  60. package/dist/boot/session-faces.d.ts +1 -0
  61. package/dist/boot/session-faces.js +4 -2
  62. package/dist/boot/stage-02-web-admin-pull.js +4 -1
  63. package/dist/boot/stage-06-runners.d.ts +6 -3
  64. package/dist/boot/stage-06-runners.js +20 -4
  65. package/dist/boot/stage-07-capability-layer.d.ts +4 -1
  66. package/dist/boot/stage-07-capability-layer.js +24 -3
  67. package/dist/boot/stage-08-reapers.d.ts +4 -1
  68. package/dist/boot/stage-08-reapers.js +2 -2
  69. package/dist/boot/stage-09-leader.d.ts +5 -1
  70. package/dist/boot/stage-09-leader.js +2 -2
  71. package/dist/boot/stage-10-http-server.d.ts +5 -1
  72. package/dist/boot/stage-10-http-server.js +6 -6
  73. package/dist/boot/stores.d.ts +8 -0
  74. package/dist/boot/stores.js +10 -14
  75. package/dist/boot/web-admin.d.ts +10 -0
  76. package/dist/boot/web-admin.js +162 -65
  77. package/dist/boot/workflow-orchestration.d.ts +2 -0
  78. package/dist/boot/workflow-orchestration.js +2 -1
  79. package/dist/brain.js +8 -3
  80. package/dist/breaker-key.d.ts +6 -0
  81. package/dist/breaker-key.js +29 -0
  82. package/dist/budget.js +5 -1
  83. package/dist/caller-text-echo.d.ts +11 -6
  84. package/dist/caller-text-echo.js +10 -12
  85. package/dist/canonical-json.d.ts +2 -41
  86. package/dist/canonical-json.js +2 -13
  87. package/dist/capabilities/center-plugins.d.ts +7 -0
  88. package/dist/capabilities/center-plugins.js +1 -1
  89. package/dist/capabilities/composite-members.d.ts +1 -1
  90. package/dist/capabilities/composite-members.js +1 -1
  91. package/dist/capabilities/execution-lane.js +2 -2
  92. package/dist/capabilities/hands-lane.d.ts +21 -0
  93. package/dist/capabilities/hands-lane.js +10 -0
  94. package/dist/capabilities/mcp-probe.d.ts +36 -4
  95. package/dist/capabilities/mcp-probe.js +29 -6
  96. package/dist/capabilities/scenarios.d.ts +63 -7
  97. package/dist/capabilities/scenarios.js +39 -7
  98. package/dist/capability-facts.d.ts +36 -4
  99. package/dist/capability-facts.js +30 -4
  100. package/dist/center-config-tier.d.ts +69 -0
  101. package/dist/center-config-tier.js +42 -0
  102. package/dist/checkpoint-ctx-seal-m28.d.ts +42 -0
  103. package/dist/checkpoint-ctx-seal-m28.js +19 -0
  104. package/dist/checkpoint-identity.d.ts +16 -0
  105. package/dist/checkpoint-identity.js +19 -0
  106. package/dist/config-catalog.d.ts +17 -10
  107. package/dist/config-catalog.js +43 -28
  108. package/dist/config-d-migration.d.ts +3 -1
  109. package/dist/config-d-migration.js +2 -2
  110. package/dist/config-dir-trust.d.ts +114 -0
  111. package/dist/config-dir-trust.js +388 -0
  112. package/dist/config-invariants.d.ts +18 -5
  113. package/dist/config-invariants.js +35 -3
  114. package/dist/config-provider.d.ts +6 -1
  115. package/dist/config-provider.js +6 -2
  116. package/dist/config-types.d.ts +71 -14
  117. package/dist/config-types.js +8 -0
  118. package/dist/config.d.ts +13 -0
  119. package/dist/config.js +121 -59
  120. package/dist/delegated-session-owner.d.ts +185 -0
  121. package/dist/delegated-session-owner.js +119 -0
  122. package/dist/device-protocol.d.ts +5 -3
  123. package/dist/device-protocol.js +1 -1
  124. package/dist/device-ws-hub.js +21 -2
  125. package/dist/device-ws-protocol.d.ts +70 -6
  126. package/dist/device-ws-protocol.js +50 -23
  127. package/dist/elicitation.js +3 -2
  128. package/dist/entry-problems.d.ts +34 -0
  129. package/dist/entry-problems.js +14 -0
  130. package/dist/execution-lane-caps.d.ts +49 -10
  131. package/dist/execution-lane-caps.js +43 -4
  132. package/dist/field-conflict.d.ts +44 -0
  133. package/dist/field-conflict.js +21 -0
  134. package/dist/file-history-disclosure.d.ts +34 -5
  135. package/dist/file-history-disclosure.js +2 -2
  136. package/dist/fleet/fleet-bus.d.ts +84 -6
  137. package/dist/fleet/fleet-bus.js +42 -9
  138. package/dist/hooks/classic-stations.d.ts +10 -5
  139. package/dist/hooks/classic-stations.js +4 -4
  140. package/dist/hooks/command-spawn.d.ts +57 -0
  141. package/dist/hooks/command-spawn.js +139 -0
  142. package/dist/hooks/hook-llm.js +2 -2
  143. package/dist/hooks/hook-runner.d.ts +127 -25
  144. package/dist/hooks/hook-runner.js +668 -287
  145. package/dist/hooks/hook-source.d.ts +44 -0
  146. package/dist/hooks/hook-source.js +40 -0
  147. package/dist/http/active-run-conflict.d.ts +5 -13
  148. package/dist/http/active-run-conflict.js +17 -27
  149. package/dist/http/admission.js +111 -60
  150. package/dist/http/approval-sweeps.js +58 -19
  151. package/dist/http/caller-trust.d.ts +77 -0
  152. package/dist/http/caller-trust.js +143 -0
  153. package/dist/http/checkpoint-ctx.d.ts +173 -18
  154. package/dist/http/checkpoint-ctx.js +171 -12
  155. package/dist/http/claimed-run.d.ts +4 -3
  156. package/dist/http/device-placement.d.ts +2 -4
  157. package/dist/http/device-placement.js +2 -1
  158. package/dist/http/dispatch.js +13 -7
  159. package/dist/http/durable-run-leg.d.ts +4 -32
  160. package/dist/http/durable-run-leg.js +37 -12
  161. package/dist/http/idempotency.d.ts +2 -2
  162. package/dist/http/idempotency.js +2 -2
  163. package/dist/http/owner-access.d.ts +56 -0
  164. package/dist/http/owner-access.js +42 -0
  165. package/dist/http/principal-gate.d.ts +78 -27
  166. package/dist/http/principal-gate.js +64 -14
  167. package/dist/http/readiness.d.ts +11 -5
  168. package/dist/http/readiness.js +10 -3
  169. package/dist/http/refusal-body.d.ts +12 -0
  170. package/dist/http/refusal-body.js +11 -0
  171. package/dist/http/request-guards.d.ts +16 -32
  172. package/dist/http/request-guards.js +31 -30
  173. package/dist/http/resume-ctx-read.d.ts +50 -6
  174. package/dist/http/resume-ctx-read.js +33 -7
  175. package/dist/http/resume-legs.d.ts +5 -5
  176. package/dist/http/resume-legs.js +163 -91
  177. package/dist/http/retired-service-deps.d.ts +43 -0
  178. package/dist/http/retired-service-deps.js +18 -0
  179. package/dist/http/route-ctx.d.ts +44 -37
  180. package/dist/http/route-ctx.js +4 -0
  181. package/dist/http/route-table.js +2 -0
  182. package/dist/http/routes/a2a-serve.js +3 -4
  183. package/dist/http/routes/admin-config-refresh.js +1 -1
  184. package/dist/http/routes/admin-drain.js +1 -1
  185. package/dist/http/routes/admin-memory-optout.js +5 -1
  186. package/dist/http/routes/adoption.js +2 -9
  187. package/dist/http/routes/agents-roster.js +2 -3
  188. package/dist/http/routes/approvals-assistant.js +91 -56
  189. package/dist/http/routes/background.js +3 -2
  190. package/dist/http/routes/capabilities.js +20 -4
  191. package/dist/http/routes/config-catalog.d.ts +3 -3
  192. package/dist/http/routes/config-catalog.js +1 -1
  193. package/dist/http/routes/devices.js +3 -17
  194. package/dist/http/routes/diagnostics.d.ts +3 -3
  195. package/dist/http/routes/diagnostics.js +6 -2
  196. package/dist/http/routes/fleet.js +4 -4
  197. package/dist/http/routes/images.js +6 -7
  198. package/dist/http/routes/leader.js +1 -8
  199. package/dist/http/routes/mcp-reconnect.d.ts +1 -1
  200. package/dist/http/routes/mcp-reconnect.js +3 -2
  201. package/dist/http/routes/memory-bundle.js +1 -1
  202. package/dist/http/routes/memory-compliance.js +1 -1
  203. package/dist/http/routes/memory-consolidation.js +1 -1
  204. package/dist/http/routes/memory-origin.d.ts +1 -1
  205. package/dist/http/routes/memory-origin.js +1 -1
  206. package/dist/http/routes/memory-policy.d.ts +1 -1
  207. package/dist/http/routes/memory-policy.js +3 -2
  208. package/dist/http/routes/notify-wake.js +8 -21
  209. package/dist/http/routes/observability.js +2 -2
  210. package/dist/http/routes/principal-inbox.d.ts +39 -0
  211. package/dist/http/routes/principal-inbox.js +76 -0
  212. package/dist/http/routes/retention-ops.js +2 -9
  213. package/dist/http/routes/rules.js +9 -22
  214. package/dist/http/routes/runs.d.ts +5 -0
  215. package/dist/http/routes/runs.js +63 -115
  216. package/dist/http/routes/session-sync.d.ts +3 -2
  217. package/dist/http/routes/session-sync.js +53 -48
  218. package/dist/http/routes/sessions-list.d.ts +1 -1
  219. package/dist/http/routes/sessions-list.js +3 -4
  220. package/dist/http/routes/sessions.js +33 -43
  221. package/dist/http/routes/side-query.js +6 -4
  222. package/dist/http/routes/tasks.js +71 -30
  223. package/dist/http/routes/trace-usage.js +7 -9
  224. package/dist/http/routes/workflows.d.ts +351 -0
  225. package/dist/http/routes/workflows.js +153 -119
  226. package/dist/http/run-declared-families.d.ts +16 -0
  227. package/dist/http/run-declared-families.js +12 -0
  228. package/dist/http/send.d.ts +24 -3
  229. package/dist/http/send.js +14 -2
  230. package/dist/http/server.d.ts +23 -18
  231. package/dist/http/server.js +20 -15
  232. package/dist/http/stored-body-replay.js +17 -4
  233. package/dist/http/submit-ctx.d.ts +4 -3
  234. package/dist/http/submit-ctx.js +23 -6
  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 +17 -10
  238. package/dist/index.d.ts +3 -1
  239. package/dist/index.js +1 -1
  240. package/dist/lane-home-probe.d.ts +78 -9
  241. package/dist/lane-home-probe.js +78 -12
  242. package/dist/leader/endpoint.d.ts +4 -1
  243. package/dist/leader/endpoint.js +11 -10
  244. package/dist/leader/wire.d.ts +4 -0
  245. package/dist/leader/wire.js +8 -7
  246. package/dist/limit-refusal.d.ts +17 -5
  247. package/dist/limit-refusal.js +4 -1
  248. package/dist/main.js +1 -1
  249. package/dist/mcp-assertion-header.d.ts +31 -10
  250. package/dist/mcp-assertion-header.js +10 -3
  251. package/dist/memory-origin-fence.js +2 -2
  252. package/dist/memory-project-layer.d.ts +37 -0
  253. package/dist/memory-project-layer.js +46 -0
  254. package/dist/memory-scope.d.ts +23 -16
  255. package/dist/memory-scope.js +24 -8
  256. package/dist/memory-sync-client.d.ts +27 -13
  257. package/dist/memory-sync-client.js +107 -18
  258. package/dist/model-compat.d.ts +1 -1
  259. package/dist/model-entry-refusal.d.ts +9 -1
  260. package/dist/model-entry-refusal.js +6 -1
  261. package/dist/model-plane-face.d.ts +51 -0
  262. package/dist/model-plane-face.js +24 -0
  263. package/dist/model-provider.d.ts +5 -0
  264. package/dist/model-provider.js +4 -2
  265. package/dist/model-route-endpoint.d.ts +8 -11
  266. package/dist/model-route-endpoint.js +4 -7
  267. package/dist/model-route-env.d.ts +22 -0
  268. package/dist/model-route-env.js +2 -0
  269. package/dist/model-route-gap.d.ts +60 -30
  270. package/dist/model-route-gap.js +21 -15
  271. package/dist/observability/corrupt-read-seat.d.ts +7 -0
  272. package/dist/observability/corrupt-read-seat.js +9 -0
  273. package/dist/observability/err-text.d.ts +6 -0
  274. package/dist/observability/err-text.js +18 -4
  275. package/dist/observability/fail-open.d.ts +35 -9
  276. package/dist/observability/fail-open.js +35 -9
  277. package/dist/observability/metrics.js +1 -0
  278. package/dist/observability/prompt-manifest.d.ts +22 -25
  279. package/dist/observability/prompt-manifest.js +54 -50
  280. package/dist/observability/tool-trace.js +9 -10
  281. package/dist/operator-ask.d.ts +10 -0
  282. package/dist/operator-ask.js +3 -0
  283. package/dist/orchestration/workflow-agent-session-index.d.ts +12 -12
  284. package/dist/orchestration/workflow-agent-session-index.js +9 -13
  285. package/dist/orchestration/workflow-completion-inbox.d.ts +42 -15
  286. package/dist/orchestration/workflow-completion-inbox.js +13 -0
  287. package/dist/orchestration/workflow-notify-journal.d.ts +13 -32
  288. package/dist/orchestration/workflow-notify-journal.js +27 -34
  289. package/dist/orchestration/workflow-park-decision.d.ts +89 -0
  290. package/dist/orchestration/workflow-park-decision.js +119 -0
  291. package/dist/org-delivery-gate.d.ts +81 -0
  292. package/dist/org-delivery-gate.js +59 -0
  293. package/dist/panel-tool-names.d.ts +23 -0
  294. package/dist/panel-tool-names.js +8 -0
  295. package/dist/parked-decide.d.ts +43 -11
  296. package/dist/parked-decide.js +100 -51
  297. package/dist/parked-revive-surface.d.ts +9 -2
  298. package/dist/parked-revive-surface.js +11 -8
  299. package/dist/permission-rule-list.d.ts +1 -1
  300. package/dist/permission-rule-list.js +2 -2
  301. package/dist/permission-rule-vocab.d.ts +1 -23
  302. package/dist/permission-rule-vocab.js +0 -13
  303. package/dist/plugins/approval-ask-store-file.d.ts +16 -1
  304. package/dist/plugins/approval-ask-store-file.js +21 -0
  305. package/dist/plugins/approval-ask-store-memory.d.ts +18 -1
  306. package/dist/plugins/approval-ask-store-memory.js +34 -2
  307. package/dist/plugins/approval-ask-store-sql.d.ts +25 -0
  308. package/dist/plugins/approval-ask-store-sql.js +37 -1
  309. package/dist/plugins/approval-payload-fingerprint.d.ts +14 -6
  310. package/dist/plugins/approval-payload-fingerprint.js +1 -1
  311. package/dist/plugins/blob-backend.d.ts +38 -25
  312. package/dist/plugins/blob-backend.js +29 -19
  313. package/dist/plugins/breaker-state-sql.d.ts +2 -2
  314. package/dist/plugins/breaker-state-sql.js +4 -4
  315. package/dist/plugins/caching-session-store.d.ts +4 -1
  316. package/dist/plugins/caching-session-store.js +7 -3
  317. package/dist/plugins/checkpoint-store-sql.d.ts +36 -1
  318. package/dist/plugins/checkpoint-store-sql.js +65 -3
  319. package/dist/plugins/file-history-store-sql.d.ts +5 -2
  320. package/dist/plugins/file-history-store-sql.js +3 -1
  321. package/dist/plugins/file-run-store.d.ts +5 -13
  322. package/dist/plugins/file-run-store.js +9 -0
  323. package/dist/plugins/fork-routing-session-store.d.ts +9 -1
  324. package/dist/plugins/fork-routing-session-store.js +12 -1
  325. package/dist/plugins/host-platform.d.ts +2 -0
  326. package/dist/plugins/host-platform.js +2 -1
  327. package/dist/plugins/local-checkpoint-store.d.ts +15 -2
  328. package/dist/plugins/local-checkpoint-store.js +32 -1
  329. package/dist/plugins/local-session-store.d.ts +13 -10
  330. package/dist/plugins/local-session-store.js +23 -14
  331. package/dist/plugins/local-session-window.d.ts +45 -0
  332. package/dist/plugins/local-session-window.js +38 -0
  333. package/dist/plugins/mailbox-recipient-lifecycle.d.ts +3 -2
  334. package/dist/plugins/mailbox-store-sql.d.ts +30 -48
  335. package/dist/plugins/mailbox-store-sql.js +77 -52
  336. package/dist/plugins/memory-embedder-fingerprint.js +2 -1
  337. package/dist/plugins/memory-run-store.d.ts +5 -13
  338. package/dist/plugins/memory-run-store.js +8 -0
  339. package/dist/plugins/one-time-migrations.d.ts +121 -5
  340. package/dist/plugins/one-time-migrations.js +332 -8
  341. package/dist/plugins/org-delivery-quota-store.d.ts +123 -0
  342. package/dist/plugins/org-delivery-quota-store.js +228 -0
  343. package/dist/plugins/permission-rule-store-file.js +10 -6
  344. package/dist/plugins/permission-rule-store-sql.js +8 -8
  345. package/dist/plugins/pg-pool.js +39 -7
  346. package/dist/plugins/pg-session-storage.d.ts +18 -5
  347. package/dist/plugins/pg-session-storage.js +68 -23
  348. package/dist/plugins/remote-env-adb.d.ts +6 -12
  349. package/dist/plugins/remote-env-adb.js +12 -2
  350. package/dist/plugins/remote-env-device.d.ts +14 -7
  351. package/dist/plugins/remote-env-device.js +23 -15
  352. package/dist/plugins/remote-env-k8s.d.ts +8 -12
  353. package/dist/plugins/remote-env-k8s.js +23 -5
  354. package/dist/plugins/remote-env-local-docker.d.ts +10 -15
  355. package/dist/plugins/remote-env-local-docker.js +12 -9
  356. package/dist/plugins/remote-env-ssh.d.ts +10 -16
  357. package/dist/plugins/remote-env-ssh.js +17 -11
  358. package/dist/plugins/run-store-sql.d.ts +16 -14
  359. package/dist/plugins/run-store-sql.js +22 -2
  360. package/dist/plugins/schema-indexes.js +2 -0
  361. package/dist/plugins/send-user-file.js +2 -2
  362. package/dist/plugins/session-epoch-anchor.d.ts +32 -6
  363. package/dist/plugins/session-epoch-anchor.js +2 -3
  364. package/dist/plugins/session-ownership.d.ts +11 -0
  365. package/dist/plugins/session-ownership.js +11 -0
  366. package/dist/plugins/session-placement.d.ts +12 -14
  367. package/dist/plugins/session-placement.js +2 -15
  368. package/dist/plugins/session-store.d.ts +18 -8
  369. package/dist/plugins/session-store.js +15 -4
  370. package/dist/plugins/sql-driver.d.ts +74 -11
  371. package/dist/plugins/sql-driver.js +210 -12
  372. package/dist/plugins/sql-errors.d.ts +23 -0
  373. package/dist/plugins/sql-errors.js +21 -0
  374. package/dist/plugins/store-backend.d.ts +36 -1
  375. package/dist/plugins/store-backend.js +40 -14
  376. package/dist/plugins/store-contracts.d.ts +42 -0
  377. package/dist/plugins/store-contracts.js +1 -0
  378. package/dist/plugins/tidb-pool.d.ts +6 -1
  379. package/dist/plugins/tidb-pool.js +44 -10
  380. package/dist/plugins/tidb-session-storage.d.ts +3 -2
  381. package/dist/plugins/tidb-session-storage.js +2 -2
  382. package/dist/plugins/tidb-session-store.d.ts +25 -5
  383. package/dist/plugins/tidb-session-store.js +67 -21
  384. package/dist/plugins/workflow-journal-store-sql.d.ts +73 -19
  385. package/dist/plugins/workflow-journal-store-sql.js +19 -6
  386. package/dist/plugins/workflow-run-store-sql.d.ts +30 -8
  387. package/dist/plugins/workflow-run-store-sql.js +50 -9
  388. package/dist/posture-source.d.ts +18 -10
  389. package/dist/posture-source.js +4 -1
  390. package/dist/project-identity.d.ts +45 -6
  391. package/dist/project-identity.js +162 -8
  392. package/dist/project-layer-key.d.ts +28 -0
  393. package/dist/project-layer-key.js +29 -0
  394. package/dist/question.js +3 -2
  395. package/dist/read-deny-entry.d.ts +25 -0
  396. package/dist/read-deny-entry.js +12 -0
  397. package/dist/read-face-posture.d.ts +37 -11
  398. package/dist/read-face-posture.js +32 -3
  399. package/dist/request-key-closure.d.ts +7 -0
  400. package/dist/request-key-closure.js +9 -1
  401. package/dist/request-tool-axes.d.ts +12 -0
  402. package/dist/request-tool-axes.js +20 -0
  403. package/dist/request-value-closure.d.ts +5 -0
  404. package/dist/request-value-closure.js +11 -3
  405. package/dist/rule-entry-list.d.ts +26 -2
  406. package/dist/rule-entry-list.js +72 -12
  407. package/dist/rules-consent.js +3 -4
  408. package/dist/run-acceptance.d.ts +19 -3
  409. package/dist/run-acceptance.js +13 -1
  410. package/dist/run-local.js +37 -6
  411. package/dist/runs.d.ts +12 -10
  412. package/dist/runs.js +25 -6
  413. package/dist/runtime-caps-resolver.d.ts +16 -6
  414. package/dist/runtime-caps-resolver.js +18 -5
  415. package/dist/sandbox-pkg-source.d.ts +19 -1
  416. package/dist/sandbox-pkg-source.js +29 -7
  417. package/dist/sealed-key.d.ts +32 -0
  418. package/dist/sealed-key.js +55 -1
  419. package/dist/security.d.ts +95 -15
  420. package/dist/security.js +52 -9
  421. package/dist/session-error-wire.d.ts +31 -7
  422. package/dist/session-error-wire.js +9 -2
  423. package/dist/session-sync-kernel.d.ts +37 -10
  424. package/dist/session-sync-kernel.js +10 -0
  425. package/dist/session-titled-frame.d.ts +33 -0
  426. package/dist/session-titled-frame.js +4 -0
  427. package/dist/session-titler.d.ts +9 -1
  428. package/dist/session-titler.js +15 -4
  429. package/dist/spec-fields.js +21 -2
  430. package/dist/sql-engine-posture.d.ts +3 -1
  431. package/dist/task-a2a.js +4 -3
  432. package/dist/task-cwd.d.ts +1 -1
  433. package/dist/task-mcp.d.ts +5 -5
  434. package/dist/task-mcp.js +27 -74
  435. package/dist/task-settings.d.ts +93 -88
  436. package/dist/task-settings.js +71 -63
  437. package/dist/text-bidi.d.ts +0 -4
  438. package/dist/text-bidi.js +1 -3
  439. package/dist/tighten-only-seats.d.ts +150 -0
  440. package/dist/tighten-only-seats.js +196 -0
  441. package/dist/tool-approval.d.ts +451 -136
  442. package/dist/tool-approval.js +733 -256
  443. package/dist/tool-rule-names.d.ts +6 -0
  444. package/dist/trace/core-keyset-guard.d.ts +108 -25
  445. package/dist/trace/exit-seat.d.ts +40 -0
  446. package/dist/trace/exit-seat.js +130 -0
  447. package/dist/trace/ledger-events.d.ts +6 -3
  448. package/dist/trace/project.d.ts +98 -13
  449. package/dist/trace/project.js +57 -32
  450. package/dist/trace/projection-table.d.ts +100 -0
  451. package/dist/trace/projection-table.js +130 -0
  452. package/dist/trace/redact.d.ts +57 -0
  453. package/dist/trace/redact.js +962 -168
  454. package/dist/trace/self-minted-wire.d.ts +349 -1
  455. package/dist/trace/self-minted-wire.js +118 -1
  456. package/dist/trace/wire-projection-faces.d.ts +304 -2
  457. package/dist/trace/wire-projection-faces.js +160 -13
  458. package/dist/url-refusal.js +2 -0
  459. package/dist/web-admin-client/apply-effective.d.ts +12 -0
  460. package/dist/web-admin-client/apply-effective.js +56 -5
  461. package/dist/web-admin-client/facade.d.ts +1 -1
  462. package/dist/web-admin-client/facade.js +1 -1
  463. package/dist/web-admin-client/read-face.js +5 -9
  464. package/dist/web-admin-client/skills-mcp.d.ts +1 -1
  465. package/dist/web-admin-client/skills-mcp.js +6 -8
  466. package/dist/web-admin-client/types.d.ts +1 -1
  467. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +57 -9
  468. package/package.json +8 -4
  469. package/scripts/drain-check-local.mjs +34 -2
  470. package/scripts/drain-check.mysql.sql +20 -2
  471. package/scripts/drain-check.pg.sql +20 -2
  472. package/dist/mcp-tool-faces.d.ts +0 -69
  473. package/dist/mcp-tool-faces.js +0 -106
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,8 @@
11
11
 
12
12
  > 部署 = 把本仓跑在一台**能连到模型网关 + TiDB** 的机器上;那台机器的 `IP:8090` 就是你的 `<host>:8090`。
13
13
  > 该机器需要 **Node 22 / Bun**(路线 A,无 docker)**或** **Docker**(路线 B)——二选一。
14
+ > 容器镜像自 7.128.0 起不再发布,npm 包是唯一发布件;自建镜像步骤见 USAGE 9.13(路线 B 用 `docker compose up --build` 就地构建,不受影响;已发布的旧镜像仍可拉,`:latest` 停在 7.128.0 之前的版本)。
15
+ > 要自己构建引擎镜像(推私有 registry / 导入 k3s 节点 / 升到 ≥ 7.128.0):两条路径 —— 有源码仓访问权就在源码 tag 上用仓根 `Dockerfile` 构建,只有 npm 访问权就从 npm 包构建 —— 步骤见 §9.13。
14
16
 
15
17
  **路线 A — 不用 docker(单实例,最简单)**
16
18
  ```bash
@@ -60,17 +62,24 @@ docker compose up --build --scale service=4 # 4 个无状态 worker,nginx
60
62
 
61
63
  > 🔴 **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
64
  >
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` 可改成拒启) | ✅(同左) |
65
+ > | 车道 | 单用户 | 多租户(`REQUIRE_PRINCIPAL=true`) | `READ_FACE` 缺席时的读面缺省(7.133.0 起车道行声明) |
66
+ > |---|---|---|---|
67
+ > | `host`(显式 / `CONFIG_PROVIDER=local` 隐式 / 文件覆写) | ✅ | ❌ 拒启(7.104.0 起) | 单用户 `open`(来源 `posture`) |
68
+ > | `ssh`(一台常驻对端) | ✅ | ❌ 拒启(7.104.0 起) | 单用户 `open`(来源 `posture`) |
69
+ > | `adb`(一台设备) | ✅ | ❌ 拒启(7.104.0 起) | 单用户 `open`(来源 `posture`) |
70
+ > | `e2b` / `k8s` / `local-docker`(每任务一只沙箱) | ✅ | ✅ | **`open`**(来源 **`lane`**,单用户 / 多租户同;车道行显式声明)。🔴 **挂载告诫**:把宿主目录(`hostPath` / bind mount)或共享卷挂进沙箱的部署,`open` 面上这些宿主文件对模型零问可读 —— 显式 `READ_FACE=roots`(env 恒赢)或给它们加读拒行(`READ_DENY_PATTERNS`) |
71
+ > | `device`(每人自己的设备) | ✅ | ✅ | 单用户 `open`(来源 `posture`);多租户 不铸 = 引擎默认 `roots` |
72
+ > | 未点名(云形进程内 stub,零手带工具、非手带工具照常执行) | ✅(`REQUIRE_EXECUTION_LANE=true` 可改成拒启) | ✅(同左) | 单用户 `open`(来源 `posture`);多租户 不铸 = 引擎默认 `roots` |
71
73
 
72
74
  > 🔴 **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
75
 
76
+ > 🔴 **本地配置源的目录信任(7.133.0 起)**:`config.d` 是可执行配置(`mcp.json` 的 stdio `command` 以本服务用户起子进程,`models.json` 的 `baseUrl` 决定 API key 送往哪里),也是部署者在盘上声明外置 MCP 工具的唯一入口 ——
77
+ > 写在 `config.d/mcp.json`,键与中心发布的 `mcp` 域同形,没有另一个工具目录。读之前先判:从 `/` 走到 `config.d`、再到读者会读的每一份文档(符号链接照跟、跟到哪判到哪),
78
+ > 每一级属主只能是本服务的用户、root 或 `CONFIG_LOCAL_OWNER_UID`(多信的那一个 uid;形错拒启),不得组 / 他人可写(属主可信的粘滞目录如 `/tmp` 除外,但要读的一项缺在这样的目录里也拒),
79
+ > 文档是常规文件且 ≤ 16 MiB,整条路径链接合计 ≤ 40 跳、不得指向 procfs 一类内核对象。
80
+ > 起服不过 ⇒ 拒启(fatal 标题 `[config.dir_untrusted]`;`run-local` 退 2);起服之后才变坏 ⇒ 那一拍 `web_admin_refresh_failed` 带同一句、新字节不读、已应用的配置不动。拒句点名那一个路径、实测值与改法
81
+ > (`chmod go-w` / `chown` / `CONFIG_LOCAL_OWNER_UID=<uid>`)。Windows 上属主与模式位不可判:只判结构,另记一次 fail-open 告警。容器形(rootless / userns-remap / ConfigMap / fsGroup)见源码仓文档 DEPLOY-PREREQS 的 7.133.0 节(不随包)。
82
+
74
83
  > **会话 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
84
 
76
85
  > 拓扑:LB(入口)→ N 个一样的 worker → 共享 SQL 库(数据中心;MySQL 协议或 PG)+ 模型网关。无中央调度器,跨 worker 靠 DB 协调。
@@ -138,6 +147,12 @@ ANTHROPIC_API_KEY=sk-ant-… # 可选:ANTHROPIC_BASE_URL / ANTHROPIC_VERSION /
138
147
  端点与凭据照网关的来 —— 此前它们按 `provider` 字面去找一条没装的 anthropic 路由而整条拒发,会话列表没有自动标题。网关端点也解析不到才拒(`config.model_route_endpoint_missing`)。
139
148
  - **缺省推断**:`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
149
  - 启动日志的 `brain` 字段会回显当前组合(`failover` / `anthropicRoute` / `anthropicCacheBreakpoints`)。
150
+ - **在服务进程之外读同一套 env 路由**(子路径 `@sema-agent/server/model-route-env`;纯数据 + 纯函数,import 无副作用,值依赖只有 `@sema-agent/core` 与 `@sema-agent/settings-schema`):
151
+ 引擎(core)不读 `MODEL_*` / `ANTHROPIC_*` 这些键,键表和读法属本服务;壳首启、客户端运行时要在引擎起之前问「这个环境模型走哪条车道、端点 / 凭据取哪一对键」,读这一只,不各抄键名。
152
+ `MODEL_ROUTE_ENV_KEYS` 是九把路由键的闭表(网关端点 / 备用端点 / 网关凭据、provider、主 / cheap 模型 id、三把 Anthropic 键),`readModelRouteEnv(env)` 是读口(`env` 交整张 `process.env` 或等价快照;空串 = 未设),
153
+ `laneAndEndpointFor(env, model, routes)` 是值级端口:`routes` 是引擎路由表的原型(`anthropic` 键在场 ⇔ anthropic 路由装上),答 `{ lane, endpointEnvName, endpointDeclared, apiKeyEnvName }` ——
154
+ **只有键名与在场性,没有任何 env 值**(端点与凭据的值调用方自己从 env 取)。凭据取值序与引擎装配同序(`ANTHROPIC_AUTH_TOKEN` 压过 `ANTHROPIC_API_KEY`);网关凭据 `MODEL_API_KEY` 只与网关端点配对,
155
+ 网关端点没设就不报凭据键(与起服不装载那把 key 同向)。
141
156
 
142
157
  **可选 — 韧性栈:分级超时 + 断路器(core 1.38,叠在 failover 之下)**
143
158
  ```bash
@@ -164,7 +179,8 @@ AUTO_MODE_CLASSIFIER_TIMEOUT_MS=180000
164
179
  - **全部默认关**(超时=0、断路器=false)→ 不设就和以前**逐字节一致**。
165
180
  - 断路器**只在配了 `MODEL_GATEWAY_FALLBACK_URLS`(≥2 路)时才有意义**——它的价值是"开路即快速失败 → failover 立刻切备";单网关下它是 no-op(启动日志 `circuitBreakerNoop` 会提示)。只 `network/server/rate_limit` 计入连败,`auth`/`invalid_request` 不计(坏 key 熔断整网关无意义)。备用网关(最后一路)不套断路器。
166
181
  - **重试上限**(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`。
182
+ - 断路器状态:配了 `SESSION_BACKEND=mysql` 时自动用**跨副本共享态**(SQL `breaker_state` 表,写穿+刷新最终一致),否则进程内 Map。启动日志 `breakerState` 字段回显 `shared(mysql)`/`in-process`/`off`。
183
+ 断路器键 = `<provider>:<模型>@<路由身份摘要>`(摘要 24 hex;整只键 ≤ 190 字节,`<provider>:<模型>` 过长时截成前缀 + `~` + 摘要;键里不带 URL —— 它进共享表 `breaker_state`、追踪事件与开路错误文本 `circuit open for <键>`;7.132.x 及以前的表 `circuit_breaker` 由起服迁移删掉)。摘要对应哪条路由读启动日志 `brain.circuitBreakerRoutes`(每条带断路器的路由一行:展示串 + 摘要)。
168
184
 
169
185
  **可选 — WebSearch 后端(core 只留注入口,不自带任何 provider;部署 env 七键)**
170
186
  ```bash
@@ -392,7 +408,7 @@ MODEL_ID=<your-model-name> MODEL_CHEAP_ID=<your-cheap-model-name>
392
408
  存量中心配置里的 `roles.team` / `roles.synthesize` 由 settings-schema 的一次性迁移搬走,side query 带 `modelRole` 表外词(含 `advisor`)⇒ **400**
393
409
  `request.field_invalid`(不再回落主模型;带了 `model` 也一样拒)。要给委派子代配模型就绑 `subagent`,主任务绑 `default`。
394
410
  - 调用方可在 `objective` 里 `@<模型名>` 选模型,**只认已配置的名单**(`MODEL_ID` / `MODEL_CHEAP_ID`,或目录条目)——注入不了 `baseUrl`/`apiKey`;无 `@` → 默认模型;`@` 点中一条已发布但本部署不可用的目录条目 ⇒ 400 `request.model_unavailable`。
395
- - **`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。
411
+ - **`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。
396
412
  - 不设 `MODEL_CHEAP_ID` → 所有角色 = 主模型(行为不变)。
397
413
  - `MODEL_CHEAP_ID` 写成与 `MODEL_ID` **同一个 id**(7.104.0 起)→ 那就是主模型本身:不铸 cheap 槽,所有角色 = 主模型,起服一行
398
414
  `config_env_entry_cheap_same_as_main`(同时设的其余 `MODEL_CHEAP_*` 键一并不生效)。要给同一只上游 id 第二种形(另一个价 / 思考 /
@@ -547,7 +563,7 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
547
563
  全部是装配事实的投影、不是另立的规则:`auth = { required, kinds, identityAttach }` —— `required` = 全局凭据门武装(配了
548
564
  `SERVICE_AUTH_TOKEN(S)`),`kinds` ⊆ `service_token` / `sso`(装了鉴权桥:登记中心的登录令牌)/ `direct_door_jwt`(直连门),
549
565
  `identityAttach` = `available`(kinds 里有 `sso` 或 `direct_door_jwt`)/ `unavailable`;`operator` = `present`(`OPERATOR_PRINCIPALS`
550
- 非空)/ `absent`;`center = { issuer?, webBaseUrl?, sessionsVisible }` —— `issuer` = 鉴权桥的 issuer(`AUTH_BRIDGE_ISSUER`,缺席回退
566
+ 非空,或单用户声明 `OPERATOR_SINGLE_USER=true`)/ `absent`;`center = { issuer?, webBaseUrl?, sessionsVisible }` —— `issuer` = 鉴权桥的 issuer(`AUTH_BRIDGE_ISSUER`,缺席回退
551
567
  `SEMA_WEB_ADMIN_URL`;没有桥 ⇒ 缺席),`webBaseUrl` = `SEMA_WEB_ADMIN_WEB_URL` 原样(缺席即缺席),`sessionsVisible` = `true` /
552
568
  `false` / `"unknown"`:中心车道配齐 ∧ `FLEET_ADVERTISE_ADDRESS` 在 ∧ 鉴权桥在 ∧ 会话枚举面在,缺一 ⇒ `false`;齐了而多租户 ⇒ `true`,
553
569
  单用户 ⇒ `"unknown"`(一条会话归谁看提交它的请求带没带身份)。两只 URL 过展示面的同一条脱敏律(凭据 / query 值不上,尾斜杠折掉)。
@@ -706,7 +722,7 @@ SEND_USER_FILE_SANDBOX_PUT_ENDPOINT=… # 可选:沙箱直传 PUT 的端
706
722
  # k8s 集群内 pod 可指内网 MinIO 省公网带宽)
707
723
  ```
708
724
  - **词表优先级**:内网端点 `MINIO_ENDPOINT` 优先,`S3_ENDPOINT` 是外接 S3 兼容位(内网/公网同址场景可只给它,同时充当公网面);公网端点 `S3_PUBLIC_ENDPOINT` 优先,缺席回落 `S3_ENDPOINT`。空串一律按未设处理。
709
- - **🔴 云形(`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 行自己的余量:哈希键 + 长度 + 时间戳;`GET /v1/diagnostics/wiring` 的 `sqlEngine.maxRowBytes`,TiDB 才有)与库报的包墙扣 1 KiB 语句余量后折半(⌊(`sqlEngine.maxPacketBytes` − 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`,门限按请求读活事实(新连接初始化读到新值即换;调大后旧连接换代前有一段过渡窗口,见部署前置清单 DEPLOY-PREREQS(仓内)「数据库」条);帽可用 `SNAPSHOT_BLOB_SQL_MAX_BYTES` 按部署真实限值覆写,两方言生效;pg 默认无帽)。
725
+ - **🔴 云形(`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 默认无帽)。
710
726
  - **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 便利态,会话与其他持久面不动)。
711
727
  - **文件历史捕获总开关**(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)。
712
728
  - **🔴 公桶策略必须 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)。
@@ -735,9 +751,12 @@ SEMA_WEB_ADMIN_TOKEN=<SERVICE_PULL_TOKEN 的值> # 取自配置控制面主机
735
751
  # 坏值拒启。续签成功 info center_credential_renewed;真失效(refresh token 已作废)warn
736
752
  # center_credential_renew_failed 一次 + /health.centerCredential.renewal.state=failed,到期后中心请求
737
753
  # 走各自既有的失败臂、不带过期令牌出站;issuer 不同源 warn center_credential_issuer_mismatch 一次、不发。
738
- # ⚠️ 换账号(凭证身份变化)后,只在启动期物化的面 —— 技能 / MCP / A2A / 场景 —— 仍是上一个
739
- # 身份的,直到重启(`/health` 报 restartRequired,reasons 含 skills);热面(models / roles /
740
- # limits / 审批名单 …)在新身份第一份候选到达那一拍换代。「换账号即降档 + 当场拉取」候后续单独处理(不在 7.100.0)。
754
+ # 换账号(凭证身份变化,7.133.0 起):引擎读到凭据文件换了人的那一刻,自己把在用的中心配置降到「无中心配置」档
755
+ # (中心下发的热面 —— 模型目录 / roles / 档位 / limits / 审批名单 / 提示词层 … —— 回 env 底,Runner 换回 env 模型表)、当场
756
+ # 拉一次新身份的配置、通告 `/health.centerIdentityNotice`(`center_identity_demoted` 已降级 → `center_identity_landed` 已换上);
757
+ # 新身份的配置拉到之前**不回退**到上一个人的任何东西,拉取失败就停在这一档、下一拍再拉。壳不必重启引擎。
758
+ # ⚠️ 只在启动期物化的面 —— 技能 / MCP 服务器表 / A2A / 场景 —— 不在降档射程:仍是启动时那个人的,直到重启
759
+ # (新身份的候选落地后 `/health` 报 restartRequired,reasons 含 skills 等)。没有 env 模型(`MODEL_ID`)的部署,降档期间没有可派发的模型,直到新身份的目录落地。
741
760
  # 用户 JWT 形(CLI 登录)⇒ skill 正文 / prompt 产物走 /api/v1/me/…;盘上 LKG 与 prompt 状态按「worker + 凭证身份」分箱。
742
761
  SEMA_WEB_ADMIN_DRY_RUN=true # 安全灰度:只 LOG 中心配置 vs env 推导的差异,不 apply
743
762
  # SEMA_WEB_ADMIN_LIVE_URL=http://<中心活性入口>:3100 # 7.121.0:fleet **活性组**(心跳 / 注销、服务租约 —— settings-schema FLEET_LIVE_PATHS)打的中心基址。
@@ -810,7 +829,7 @@ FLEET_ADVERTISE_ADDRESS=http://<本机可达IP>:8090 # 可选:设了才启公
810
829
  | pricing / keys(env-名引用)/ prompts / teams | **热** | 值换代即生效;cost 族限额座(rate limit / cost quota)同热(限额=纯比较参数,窗内累计不动;窗长换代=记账周期重开) |
811
830
  | tier 变更(档位组 / 档位绑定) | **热**(下一 refresh 拍) | 与 models 同一条换代腿:tier 表随模型面在 commit 前原子换进全部 boot Runner,下一任务按新档位路由,`/health` 不报 `restartRequired`(7.100.0 起 `models-tiers` 重启理由退役——它在生产上从未发出过) |
812
831
  | plugins(插件引用) | 重启生效 | 插件只在启动期物化;改了之后 `/health` 报 `restartRequired`,`restart.reasons` 含 `skills`(7.100.0 前这一改动**不报**,要等一次无关的重启) |
813
- | skills / mcp / a2a / scenarios(技能 / MCP / A2A / 场景) | 重启生效 | 只在启动期物化;中心改了 ⇒ `/health` 报 `restartRequired`,`restart.reasons` 各含其名。**换账号**(凭证身份变化)同理:这些面仍是上一身份的直到重启(候后续单独处理) |
832
+ | skills / mcp / a2a / scenarios(技能 / MCP / A2A / 场景) | 重启生效 | 只在启动期物化;中心改了 ⇒ `/health` 报 `restartRequired`,`restart.reasons` 各含其名。**换账号**(凭证身份变化)同理:这些面仍是启动时那个人的直到重启(热面则在换人那一刻降档、新身份的候选落地时换代,见上文 `SEMA_WEB_ADMIN_TOKEN_FILE` 段) |
814
833
  | 审批名单(`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`,该理由退役) |
815
834
  | 记忆整理阀门判决 + 驱动席(`MEMORY_CONSOLIDATION_DRIVER=on`,或未设而出厂缺省按目录派生 —— 7.104.0 起) | 重启生效 | 阀门开关与整理跑用的模型都在启动期定一次;中心改了 roles / 档位 / 目录条目后,**判决会翻**(派生开 ↔ 关,含只把主模型挪到整理座那一条上)或开着而座漂了 ⇒ `/health` 报 `restartRequired`,`restart.reasons` 含 `memory-consolidation`(7.100.0 新理由;7.104.0 起派生关的那台同样报) |
816
835
 
@@ -856,11 +875,20 @@ QUESTION_TTL_MS=300000
856
875
  > 开关只是发帧五合取里的一项:还要**活卡腿开着 ∧ 非 local 的持久 store backend ∧ park 设施在场**
857
876
  > (`DURABLE_APPROVAL=true` + checkpoint 能力的 backend)。所以一台什么都没配的默认 worker 仍是零帧 +
858
877
  > 启动一行 `stream_approval_disabled`;而**已经配齐那套前置的部署,升级后不必再显式开这个开关就会开始发帧**
859
- > —— 壳/SDK 不实现消费就等于用户看不到审批卡。帧格式、消费端契约、五合取全表在
878
+ > —— 壳/SDK 不实现消费就等于用户看不到审批卡。🔴 **7.133.0 起一条腿一个帧族**:协议上场的 worker(`GET /v1/capabilities` 的
879
+ > `streamApproval` 为真)只发 `approval_request` / `approval_complete` / `approval_revoke`,旧族 `tool_approval` / `tool_approval_complete`
880
+ > 不再发;协议不上场的 worker 只发旧族。帧格式、消费端契约、五合取全表在
860
881
  > [`docs/ASSISTANT-WIRE-CONTRACT.md` §4a-quint](docs/ASSISTANT-WIRE-CONTRACT.md)(错误码 `feature.approval_ask_disabled` 在附录 A)。
882
+ > 🔴 **7.134.0 起卡只发给声明认得这一族的客户端**:提交体 `settings.approvalFrameFamilies: ("tool_approval" | "approval_request")[]` 声明这条 run 的客户端
883
+ > 渲染得了哪些族;一条 run 只在声明含 `GET /v1/capabilities` 顶层 `approvalFrameFamilies` 那个词时收到活卡。缺席 / 不含 ⇒ 这条 run 不呈卡(不换族、不双发)、
884
+ > 开流不重放挂着的卡,每只审批问(会话里已记住的放行照旧先兑现)当场收场,结局看这台 worker 有没有耐久店(`DURABLE_APPROVAL` 开着):在场 ⇒ 交 `UNATTENDED_APPROVAL_POLICY`:`park`(缺省)⇒ 任务停驻、`/v1/approvals/:sessionId/decide` 作答;`deny` ⇒ 当场拒,拒因句点名缺的族与出路;
885
+ > 不在场(`DURABLE_APPROVAL=false`,含 helm 图表关持久的形)⇒ 受门工具当场拒(缺省 `park` 政策下是引擎按 `approver_unavailable` 拒,结算当事方是「无人」,不是 `deny` 政策的结算,拒因句不点名缺的族与声明键;`deny` 政策下是部署政策拒)、run 跑完、无卡无 `/decide`(对 7.133.x 是有意的行为收窄:那类 worker 原本出活的 `tool_approval` 卡);两形都不出任何一个族的卡。
886
+ > 运维日志 `approver_unavailable` 的 `arm` 是 `frame_family_undeclared`。对两个族同一条(`STREAM_APPROVAL_ENABLED=false` 的 worker 呈 `tool_approval`,同样要声明)。
887
+ > 续跑读持久化的提交体(声明随体重放);耐久事件流 `GET /v1/runs/:id/events` 的开流重放读的也是这条 run 受理时落盘的那份声明(没有就按未声明:零帧);与同步流开流共读同一只判据函数,入参不全对称:关窗臂里「贴 deadline ⇒ `window_zero`」由 sync 腿判,事件流按声明与窗判。坏形(非数组 / 词表外 / 重复)⇒ `400 request.field_invalid`(`field` / `reason: "bad_entry"` / `index`)。
888
+ > 受理位 `taskSettings.approvalFrameFamilies`(恒真;老 server 对这个键 `400 request.body_shape` —— 见位才发)。读法全文在契约 §0.58。
861
889
 
862
890
  ```bash
863
- # 协议总开关。默认 true;显式 =false 是**本协议面**的干净还原键(不发 approval_request、不落 ask 行、
891
+ # 协议总开关。默认 true;显式 =false 是**本协议面**的干净还原键(不发 approval_request / approval_complete、改发旧族帧、不落 ask 行、
864
892
  # 不起收敛器腿)。⚠️ 它**不**回滚 7.34.0 的 R-13 窗到期语义——那一条的
865
893
  # 回滚键是下面的 UNATTENDED_APPROVAL_POLICY=deny(两根旋钮正交,别当一根用);它也**不**再回滚窗长
866
894
  # (窗是活卡件不是协议件,见下一键——缺省值与旧硬编码窗同为 300000,没配过就仍然字节不变)
@@ -950,6 +978,20 @@ UNREACHED_ASK_TTL_MS=3600000
950
978
  (代价是超限的 ask 走 park,要有人去审批队列捞);库压大 → 调小 `STREAM_APPROVAL_RECONCILE_BATCH`
951
979
  (代价是收敛变慢,靠队列轮转保证下轮接着扫)。
952
980
  - **停驻行最长活多久(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`(两店同辖)。
981
+ - **审批账本保留多久(`APPROVAL_ASK_RETENTION_MS`,缺省 `2592000000` = 30 天;local 文件后端与 SQL 店同一条律)**:审批在盘上有三本账 —— 问账(SQL 的 `approval_ask` / `approval_batch`,local 的
982
+ `<数据根>/approval-asks/asks.jsonl`)与 local 审计账(`<数据根>/approval-ask-audit/ledger.jsonl`,`GET /v1/approvals` 的 `crashConverged` 读它)。一问**结了**且它最后一次动静早于
983
+ `now − APPROVAL_ASK_RETENTION_MS`,它在三本账里的全部记录由维护 tick 整问删掉。「结了」= 该问在它那本账里到了终局,**且它挂靠的东西都不再能动**:挂靠 = 发起它的 run、宿主会话、
984
+ 发问那条腿的会话;还能动 = run 账上在跑 / 停驻(`running` / `suspended` / `needs_review`)的 run、后台子代账上在跑 / 停驻的子代(子代的问记在宿主 run 与宿主会话名下,可以比宿主 run 活得久),
985
+ 以及每一只待决的 checkpoint(任何一种停驻都是一只待决 checkpoint,它的会话 = 停驻那条腿的会话;读后端自己的 checkpoint 店,不看这一世开没开 `DURABLE_APPROVAL`,门型读不出的待决行也算)。
986
+ 这样的问多老都不删 —— 它们还会被读(会话授权的「本会话放行」只在这次调用在这条腿上没有任何问行时才代答;停驻问之后的迟到决议要审计账里的前置事实)。
987
+ 问账删终态(`DECIDED` / `PARKED` / `DENIED` / `VOID`)且不是 provisional 的行、以及问走光了的批行;审计账删不再欠起服收敛答案的问(活卡、批了但腿还没闭的问还欠,多老都留),
988
+ 按 taskId 记的腿记录在没有留下的问还认它、它也不在挂靠集里时随之删,认不出的行原样留,留下的行逐字节不变(原子换文件,0600;换文件之后的目录 fsync 失败不回滚,
989
+ 计入 fail-open 计数 `server.approval-ask-audit.rotate-dir-fsync-failed`)。
990
+ 非终态的问行(`STREAM_PENDING` / `PARKING`)永不按龄删 —— 它们归对账器与起服收敛。节律:首拍即清扫一次,之后每小时至多一次;每次先从 run 账、后台子代账与 checkpoint 店列出还能动的东西(任一读不出 ⇒ 这次不删),
991
+ 问账一批至多 500 行、一次清扫删到不满一批为止。读数:起服一行 `approval_ask_retention`(`windowMs`;`0` = 关),真删了东西时一行 `approval_ask_retention_swept`
992
+ (`cutoffMs` / `asks` / `batches` / `auditAsks` / `auditRecords`;半途失败时已经删掉的照样打,带 `incomplete: true`)+ 计数器 `approval_ask_retention_rows_deleted_total{ledger=asks|batches|audit}`。
993
+ **关法**:`APPROVAL_ASK_RETENTION_MS=0` = 永久保留(要在升级后的第一次清扫之前设 —— 首拍即扫);正值低于 1 天钳到 1 天并在启动告警里点名(刚结束的 run 的客户端还可能对它的卡重试 / 晚答,
994
+ 一天吸收这一段);负值 / 非整数 / 非数拒启。run 结束超过窗之后才对它的卡作答 ⇒ 决断口与流内审批口答 404。
953
995
 
954
996
  **无人值守政策(`UNATTENDED_APPROVAL_POLICY`,7.34.0 起)**
955
997
 
@@ -988,8 +1030,8 @@ UNATTENDED_APPROVAL_POLICY=park
988
1030
 
989
1031
  - 审批面 `POST /v1/approvals/:sessionId/decide {decision:"approve", remember:"session"}` 与活卡的 `allow_session` 记下的放行,两处兑现 —— **谁问的,谁读它的答案**:
990
1032
  本服务自己的问在它们自己的层上、出问之前读,命中就不问:`APPROVAL_REQUIRE` 名单的问(7.119.0 起;卡面带「本会话放行」,回执 `rememberApplied:true`,
991
- 同一会话下一次调用名单门不出问,引擎看到的只是「这次调用没出问」)、`run_workflow` 问句、acceptEdits 的域外写;**引擎自己的问**(`default`
992
- 模式下的每一次写、证不了只读的 shell 命令、没人表态的服务器工具)由服务的**审批席**(工具审批协调器)在下一次到达时代答。后一半要审批席在场:
1033
+ 同一会话下一次调用名单门不出问,引擎看到的只是「这次调用没出问」)、`run_workflow` 问句;**引擎自己的问**(`default`
1034
+ 模式下的每一次写、acceptEdits 下写根外的写、证不了只读的 shell 命令、没人表态的服务器工具)由服务的**审批席**(工具审批协调器)在下一次到达时代答。后一半要审批席在场:
993
1035
  `TOOL_APPROVAL_ENABLED` 开(多租户缺省关);不想要活卡(纯 park 部署)再配 `STREAM_ASK_WINDOW_MS=0`。
994
1036
  名单的问在引擎眼里仍是 `ask_rule`:`permissionMode: "bypassPermissions"` 放不掉、auto 模式的分类器答不了 —— 免问只来自这一会话里人亲口说过的「本会话放行」。
995
1037
  - 代答不到的:`APPROVAL_DENY` / 敏感路径守卫(是拒,不是问);`APPROVAL_NEVER_AUTO` 与耐久预算耗尽的问(「恒问人」,回执 `rememberApplied:false`);
@@ -1082,7 +1124,7 @@ WRITE_PROTECTED_TABLE_REPLACE='[{"name":".envrc","kind":"basename"}]' # 整表
1082
1124
 
1083
1125
  ```bash
1084
1126
  READ_FACE=open # 词表 open|roots;坏词启动期响亮拒
1085
- # 不设:单用户 turnkey(REQUIRE_PRINCIPAL 未设)⇒ open;多租户 ⇒ 不传给引擎(引擎默认 roots)
1127
+ # 不设:隔离型车道(e2b / k8s / local-docker)⇒ open(来源 lane);否则单用户 turnkey(REQUIRE_PRINCIPAL 未设)⇒ open;多租户 ⇒ 不传给引擎(引擎默认 roots)
1086
1128
  READ_DENY_PATTERNS='[{"pattern":"**/secrets/**"},{"pattern":".ssh","caseSensitive":true}]'
1087
1129
  ```
1088
1130
  - **`READ_FACE`**:READ 工具面(读文件/遍历/搜索)的部署级姿态。引擎默认 **`roots`** = 读被夹在任务的
@@ -1097,7 +1139,10 @@ READ_DENY_PATTERNS='[{"pattern":"**/secrets/**"},{"pattern":".ssh","caseSensitiv
1097
1139
  起服(server 与 `run-local`)与中心热发会为这类行打**一行** warn `read_deny_unanchored_rows`(点名行与改法;运维的 `READ_DENY_PATTERNS` 与中心 read-face 的行同判,一份值第一次装进 Runner 席时点名一次;只告知,不改放行 / 拒绝、不拒启)。
1098
1140
  - **坏形拒启,不静默折默认**:坏 JSON / 非数组 / 缺 `pattern` / 编不过 core 的 `compileReadDeny`
1099
1141
  一律启动期点名拒——一个手滑的 deny 集比没有 deny 集更糟(以为挡了其实没挡)。
1100
- - **缺席的语义按 posture 分两形(7.65.0 起)**:
1142
+ - **缺席的语义先看车道、再按 posture 分两形(7.65.0 起按 posture;7.133.0 起多一臂车道)**:
1143
+ · **隔离型执行车道**(`REMOTE_EXEC=e2b` / `k8s` / `local-docker`,**7.133.0 起**,单用户与多租户同):`READ_FACE` 缺席 ⇒ 铸 **`open`**,来源 **`lane`**。沙箱内部就是模型的整个世界,读面缺省放开(`auto` 连续干活不再每碰一个根外路径就出卡);
1144
+ 写面、读拒表与审批模式不受影响。这是**车道行显式声明的信任属性**(可审计),不是技术默认:🔴 把宿主目录 / 共享卷挂进沙箱的部署,`open` 面上宿主文件零问可读 —— **要钉回收敛姿态:显式 `READ_FACE=roots`**(env 恒赢),或给那些路径加读拒行。
1145
+ 多租户沙箱部署升级后读面从 `roots` 变 `open`(单用户部署读面本来就是 `open`,只有 `source` 从 `posture` 变 `lane`)。非隔离车道(`host` / `ssh` / `adb` / `device` / 未点名)**一个字节不变**,按下面两形。
1101
1146
  · **单用户 turnkey**(`REQUIRE_PRINCIPAL` 未设 —— 一台机器一个可信主人,CC 的信任模型):`READ_FACE`
1102
1147
  缺席 ⇒ 铸 **`open`**,工作区外的读默认放行。**这是 7.65.0 的行为变更**:同一台机器升级前后
1103
1148
  「读工作区外的文件」从被拒变成放行。**要钉回收敛姿态:显式 `READ_FACE=roots`。**
@@ -1126,9 +1171,9 @@ READ_DENY_PATTERNS='[{"pattern":"**/secrets/**"},{"pattern":".ssh","caseSensitiv
1126
1171
  不选 / 用 `READ_DENY_BUILTIN_EXCLUDE` 删掉那一行);三问与运维面全文见
1127
1172
  `docs/DEPLOY-PREREQS.md` 的 `core 7.25.0 提货` 段。
1128
1173
  · 「这一档到底是谁定的」由 operator 面 `GET /v1/diagnostics/wiring` 的 `readFace` 段答:
1129
- `{face, source, note}`,`source` ∈ `env`(本机 env 显式)/ `center`(组织下发)/ `posture`(单机派生)/
1130
- `engine-default`(没钉,引擎当家),`note` 是指路句。
1131
- - **显式值恒赢**:优先序 `env` > `center` > `posture` > 引擎默认;引擎抬默认/扩内建集那天,没钉过的
1174
+ `{face, source, note}`,`source` ∈ `env` / `center` / `lane` / `posture` / `engine-default`:`env`(本机 env 显式)/ `center`(组织下发)/ `lane`(隔离型执行车道的契约行声明,带挂载告诫)/
1175
+ `posture`(单机派生)/ `engine-default`(没钉,引擎当家),`note` 是指路句。
1176
+ - **显式值恒赢**:优先序 `env` > `center` > `lane` > `posture` > 引擎默认;引擎抬默认/扩内建集那天,没钉过的
1132
1177
  多租户部署自动跟上(与重试上限同款「引擎当家」纪律)。
1133
1178
  - **leader 车道同席(7.54 起)**:`LEADER_ENABLED` 的 leader 编排(planner / worker / repair / conflict 四类
1134
1179
  任务)与主车道吃**同一份**四键(连同 `MEMORY_DELEGATION_EVIDENCE` / `MEMORY_PROVENANCE` / `MEMORY_CAPTURE_POLICY`
@@ -1151,10 +1196,33 @@ READ_DENY_PATTERNS='[{"pattern":"**/secrets/**"},{"pattern":".ssh","caseSensitiv
1151
1196
  `config.read_deny_swapped`,经运维日志 `engine_notice` 行落地(不上会话流);本服务另留 `read_face_swapped` 行(只报换了哪几席)。
1152
1197
  当前生效档可从 `GET /v1/capabilities` 的 `readFace` 位读
1153
1198
  (`"open"`/`"roots"`/`null`=本部署未钉,引擎默认当家;deny 表内容属策略内容,**不上**能力面);
1154
- **来源**(env / center / posture / engine-default)在 operator 面 `GET /v1/diagnostics/wiring` 的 `readFace.source`。
1199
+ **来源**(env / center / lane / posture / engine-default)在 operator 面 `GET /v1/diagnostics/wiring` 的 `readFace.source`。
1155
1200
  ⚠️ 本地 `config.d/` 车道今天**接不上**这一域(registry-core 的可移植域表里没有它),只有
1156
1201
  `CONFIG_PROVIDER=remote` 的部署走得通。
1157
1202
 
1203
+ **请求面的「只收紧」座位(7.133.0 起)—— 读拒模式 / 写保护名的加法 / 合规额外禁项 / 永不自动放行名单,每请求各加一道**
1204
+
1205
+ ```jsonc
1206
+ // POST /v1/tasks · /v1/tasks/stream · /v1/runs 的请求体
1207
+ { "objective": "…",
1208
+ "settings": {
1209
+ "readDenyPatterns": ["**/secrets/**", { "pattern": ".ssh", "caseSensitive": true }], // 加在 READ_DENY_PATTERNS 之上
1210
+ "writeProtectedExtra": [".envrc", { "name": ".corp-key", "kind": "basename" }], // 加在 WRITE_PROTECTED_EXTRA / 引擎缺省写保护表之上
1211
+ "complianceAdditionalDenies": ["workflows"], // 加在 COMPLIANCE_ADDITIONAL_DENIES 与合规档位的内建禁项之上
1212
+ "permissions": { "neverAuto": ["Bash", "mcp__figma__*"] } // 加在 APPROVAL_NEVER_AUTO 之上:命中的工具恒问真人
1213
+ } }
1214
+ ```
1215
+ - **只收紧、取并、没有放宽臂**:每一座的有效值 = 部署表 ∪ 请求列表;空数组 ≡ 没送;请求送别的行只多不少,**部署表的任何一行删不掉、绕不过**。取并在拥有部署表的那一层做一次(前三座在引擎,
1216
+ `neverAuto` 在策略折叠里),本服务不另合一份并集。
1217
+ - **`neverAuto`** 与部署的 `APPROVAL_NEVER_AUTO` 同级:命中的调用必须由真人在**这一次**答 —— `bypassPermissions`、请求自己的 `allow`、持久规则、「本会话放行」、auto 分类器都放不掉;无人可问时按无审批面的终局拒。
1218
+ 名字文法与永不匹配的名字(退役名、无前缀短名……)的判决与 env 腿同一只判官:有这类名字 ⇒ 整请求 422。审批卡的问句是 `human approval required to run "<工具名>"`;出处串(`settings.permissions.neverAuto(<命中的工具名>)`)只在引擎闸内部消费、不上卡(部署的 `APPROVAL_NEVER_AUTO` 同样)。
1219
+ - **合规禁项是「拒而不是收窄」**:请求禁了某能力而任务自己带着它 ⇒ 整个任务以 `config.compliance_denied` 失败(`workflows` + `selfOrchestration`;`mcp_servers` + 任务带 MCP 服务器;`web_fetch` + 花名册挂着 `WebFetch`)。
1220
+ ⚠️ 单用户部署的默认工具面恒挂 `WebFetch`,请求的 `excludeTools` 补偿不了(引擎先查花名册、后卸载),所以 `web_fetch` 这个词今天只在多租户部署的请求座上可用。不需要部署配 `COMPLIANCE_PROFILE`(env 腿需要)。
1221
+ - **`readDenyPatterns` 的请求侧成本界**:引擎按路径段回溯匹配,把每一颗 `*` 编成一个回溯点,每次判路径对每条模式、每个路径段各跑一遍,所以请求里**一段最多 2 颗 `*`**(每一颗都算,相邻的也逐颗算;`**` 整段、`a**b`、`id_rsa*`、`*.pem`、`*secret*` 都在内)、**整张列表合计最多 16 颗**、**最多 32 条**;超了 ⇒ 400,句子点名坐标与上限。进任务的就是写下的那一份(不改写模式)。需要更复杂或更多的行,合并成更宽的模式、改用不带星的字面形,或让运维写进 `READ_DENY_PATTERNS`(部署表不受此界)。
1222
+ - **坏值不静默丢**:不是数组 / 超帽(读拒模式 32 条,其余三座 1000 条)/ 坏条目 ⇒ 400 `request.field_invalid` + `{ field, reason: "bad_entry" | "over_cap", index | count + cap }`;续跑重放存体时同判(409 `resume_blocked_by_policy`)。
1223
+ **能力位** `GET /v1/capabilities` → `taskSettings.tightenOnlySeats`(恒真;老 server 对这四个键 400 `request.body_shape`,见位才发)。顶层请求体的 `readDenyPatterns` 仍不受理,拒句指路 `settings.readDenyPatterns`。
1224
+ 不设信任地板:只收紧的座位任何过了凭据门的调用方都能用(多租部署上登录令牌 scope 角色低的也一样);放宽成员(`allow`)的信任档准入照旧。
1225
+
1158
1226
  **可选 — 任务层「掀开写围栏」的运维天花板(`TASK_WRITE_FACE_ESCALATION`,7.95.0 起)**
1159
1227
 
1160
1228
  ```bash
@@ -1580,6 +1648,15 @@ bake-runner 要用 ⇒ `LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW` 点名放回。
1580
1648
  `config_env_forwarded_multi_tenant`(`env` = 旋钮名、`entries` = 名单段数、`note` = 姿态句:这张 worker 级名单进每个租户的沙箱),
1581
1649
  不回显名单原文;单用户部署不出这一行。
1582
1650
 
1651
+ **区域包源 `SANDBOX_PKG_SOURCE=custom` 的六只显式地址不得带 userinfo(7.133.0 起)**:`custom` 把 `SEMA_APT_MIRROR` / `SEMA_PIP_INDEX` / `SEMA_UV_INDEX` / `SEMA_UV_PY_MIRROR` /
1652
+ `SEMA_NPM_REGISTRY` / `SEMA_NODE_MIRROR` **原样**写进每个 e2b VM / k8s pod 的环境(并映射到 `PIP_INDEX_URL` / `UV_*` / `NPM_CONFIG_REGISTRY` / `COREPACK_NPM_REGISTRY` /
1653
+ `NVM_NODEJS_ORG_MIRROR` / `NODE_MIRROR`),沙箱里模型驱动的命令 `env` 读得到,所以**凡注入模型可见环境的 URL 不得带 userinfo**:地址带 `user:pass@`(或是判官判不了无凭据的形 ——
1654
+ 无 scheme 的 `令牌:口令@host:端口`、`file://`、无 scheme 的 `host:port`、值里带反斜杠(URL 标准把 `\` 读成 `/`,所以 `https://用户\:口令@host/…` 在判官眼里没有 userinfo,pip 一类却读得出;读法不唯一的值不收);
1655
+ 这个座位只收层级式 http(s) 地址)⇒ **起服拒启**,拒句点名 env 名、只回显 `scheme://host[:port]`(**不回显用户名与口令**),
1656
+ 并给两条出路:① 地址改成不带凭据的形,凭据走包管理器自己的通道 —— 上面三只沙箱名单旋钮点名一个令牌变量(点名即承担:沙箱里所有进程含模型都读得到;pod 容器 env 里它与派生值
1657
+ 同名时名单赢),或把按主机记凭据的文件烤进镜像(npm 的 `.npmrc` 里 `//主机/:_authToken=…`、pip / uv / curl 的 `.netrc`;**别**写进 `pip.conf` 的 `index-url`,注入的 `PIP_INDEX_URL` 比配置文件优先);② 源不需要认证就去掉 userinfo。不剥、不改写、不静默继续。**只在 `custom` 时判**(`global` / `cn` / `none` 不注入这六只);读与判都在起服那一次,
1658
+ 车道的 env 合成处只读起服读到的配置位。请求面没有任何包源地址键(只有部署面);名单点名放行的值不受这条律管(照旧原样交出)—— 但名单点名的恰是这六只 `SEMA_*` 之一、profile 为 `custom` 时,读口照判环境里的值。
1659
+
1583
1660
  **`CONFIG_REQUIRE_ROSTER=true`(3.1.0,可选,缺省关)**:远程 registry 非 dryRun 车道的 fleet worker
1584
1661
  可布防「roster 落地前拒接计费提交(503)」——3.0.0 起 MODEL_ID 必填,每个 worker 都带 boot 模型起服,
1585
1662
  旧的「无 env 模型即等 roster」推断失效;要那个保护语义现在需显式声明。
@@ -1594,7 +1671,7 @@ bake-runner 要用 ⇒ `LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW` 点名放回。
1594
1671
  | 头 | 什么时候必须 | 含义 |
1595
1672
  |---|---|---|
1596
1673
  | `Authorization: Bearer <SERVICE_AUTH_TOKEN>` | 设了 `SERVICE_AUTH_TOKEN` 时,除 `/health` / `/readyz` 外的所有请求 | **谁有权调本服务**(OA 后端持有,服务到服务) |
1597
- | `x-agent-principal: user:42` | 设了 `REQUIRE_PRINCIPAL=true` 时 | **代表哪个终端用户**(决定 session 归属 + 记忆隔离;**绝不从 body 取**) |
1674
+ | `x-agent-principal: user:42` | 设了 `REQUIRE_PRINCIPAL=true` 时 | **代表哪个终端用户**(决定 session 归属 + 记忆隔离;**绝不从 body 取**)。7.134.0 起也决定 fleet 面看得见什么:带了它的 service 凭据请求只看得到、动得到这位用户的(舰队流 / 列表 / 名册 / trace / 用量 / 删除 / fork / 同步);全舰队视图不带它(直连门部署上身份是签名的 principal 令牌,不是这个头) |
1598
1675
 
1599
1676
  > **dev-open 部署的主体头是过滤 / 展示用,不是隔离。** 没接任何身份来源(没配 `SERVICE_AUTH_TOKEN(S)`、没装中心鉴权桥、没开直连门 —— 即 `/health.auth.kinds` 为空)、
1600
1677
  > 也没配 `TRACE_TOKEN`、没开 `REQUIRE_PRINCIPAL` 的部署上,`x-agent-principal` 谁都能自报:
@@ -1613,14 +1690,14 @@ bake-runner 要用 ⇒ `LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW` 点名放回。
1613
1690
  > **`401 auth.unauthorized` 的 `error` 是一句出路(7.104.0 起;此前只有一个词 `unauthorized`)**,按这道门收什么分三句,码都是 `auth.unauthorized`、都**不含任何值**,
1614
1691
  > 同一道门上「没带」与「带错」逐字同一句(区分就是探测面):① 全局凭据门 —— 说「带这台部署的 service token 作 Bearer」,并指 sema-up 部署的
1615
1692
  > 令牌在 `~/.sema-up/<project>/.env` 的 `SERVICE_AUTH_TOKEN`;② 运维口(`/metrics*`、`GET /v1/tasks/source-summary`)—— 说要运维凭据
1616
- > (service token;指标口另认只读的 `METRICS_TOKEN`);③ 过了凭据门、但这一口要调用方身份(属主寻址读写、规则、记忆)—— 说要一个验过的
1693
+ > (不带主体头的 service token —— 带主体头的 service token 请求代那位主体、不是运维凭据,7.134.0 起;指标口另认只读的 `METRICS_TOKEN`,`source-summary` 另认只读的 `TRACE_TOKEN`);③ 过了凭据门、但这一口要调用方身份(属主寻址读写、规则、记忆)—— 说要一个验过的
1617
1694
  > 调用方身份,**不**叫你再去带 service token。逐字句子见 `docs/ASSISTANT-WIRE-CONTRACT.md` 附录 A.1 码表。壳(sema CLI)接 sema-up 引擎的
1618
1695
  > 令牌交法见 `deploy/sema-up/README.md`「壳接入」。
1619
1696
 
1620
1697
  **整只请求体的上限**:8 MiB;**TiDB 部署更矮** —— 一份提交体要整只落进库的一行,上限 = 库的单行上限(TiDB `txn-entry-size-limit`,缺省 6 MiB)− 64 KiB,
1621
- 缺省约 5.94 MiB(6 225 920 字节,按体的 JSON 字节判)。超了 ⇒ `413 request.payload_too_large`,句子点名本部署的上限。服务连上库时从库读这个数(读数在
1698
+ 缺省约 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"`)。服务连上库时从库读这个数(读数在
1622
1699
  `GET /v1/diagnostics/wiring` 的 `sqlEngine.maxRowBytes`);要收更大的体,运维抬 TiDB 的 `performance.txn-entry-size-limit`(或 TiDB 7.6+ 的 `tidb_txn_entry_size_limit`),
1623
- 并让库账号读得到它(`CONFIG` 权限,或设那只会话变量;读不到时按 TiDB 缺省判)。同时并入库的**语句**上限(`max_allowed_packet`,InnoDB 与 TiDB 都有;读数在 `sqlEngine.maxPacketBytes`)。两堵墙各量各的、量店真正发出去的东西:行墙腿量体的 JSON 字节 ≤ 单行上限 − 64 KiB(行里存原字节);
1700
+ 并让库账号读得到它(`CONFIG` 权限,或设那只会话变量;读不到时按 TiDB 缺省判)。同时并入库的**语句**上限(`max_allowed_packet`,InnoDB 与 TiDB 都有;读数在 `sqlEngine.maxPacketBytes`,是最近一次初始化的那条数据库连接的值 —— 受理口按它预判,写入那条连接上的库仍是终判)。两堵墙各量各的、量店真正发出去的东西:行墙腿量体的 JSON 字节 ≤ 单行上限 − 64 KiB(行里存原字节);
1624
1701
  包墙腿量**转义后的语句字节** ≤ 包墙 − 1 KiB(体经驱动以字符串字面进语句,`"` `'` `\` 与控制字符各多一个字节,引号 / 反斜杠密集的 JSON 比自己的字节数长,至多 2×),`null` 的墙不参与,句子点名真正拒绝的那一堵。
1625
1702
  PostgreSQL 只有 8 MiB 这一道;InnoDB 没有单行墙,缺省 16 MiB(MariaDB)包墙下引号密集的大体会在 8 MiB 之内就被包墙拒,缺省 64 MiB(MySQL 8)之下仍只有 8 MiB 这一道。
1626
1703
  连接池最近一次连接初始化被库拒(读不出库的事实;到下一条连接初始化成功为止,通常秒级)时,MySQL 协议部署按最保守的**假定**墙判(行墙 6 291 456 = TiDB 缺省、包墙 16 MiB,与 SQL 快照 blob 的缺省帽同一个读法),
@@ -1639,8 +1716,8 @@ PostgreSQL 硬上限 ⇒ 只有缩小),运维日志一行 `submit_ctx_row_too_la
1639
1716
  | `sessionId` | — | 续聊:带上次返回的 `sessionId`,服务端自动 wake 历史。**只收 canonical uuid**(任意版本,小写;与会话口同一只形判):自选名 / 业务键 / 空串 ⇒ `400 request.id_invalid`,缺省请**省略这个键**(服务端现铸 uuid)。想要好记的名字,自己记 name → uuid 的映射 |
1640
1717
  | `images` | — | 图文输入 `[{data,mimeType}|{url}]`(模型需支持 vision)。**随提交的这条目标一次送达**:续跑 / 投递腿(审批 / 资源停车 / wake / plan_review 续跑、在宿主会话上新起的一跑)重建配置时不再带它(与 `userAttachments` 同律;图在首跑已是会话里的消息) |
1641
1718
  | `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 形走本地文件店。 |
1642
- | `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` |
1643
- | `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` |
1719
+ | `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` |
1720
+ | `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` |
1644
1721
  | `repo` | — | `code-review`/`scan` 必填,见 §5(7.126.0 起 `council` / `debate` / `lenses` / `rounds` / `team` / `businessContext` 是未知键 ⇒ 400:它们喂的评审团腿与 `discuss` 场景随引擎 7.49.0 删掉团队运行时一并删除) |
1645
1722
  | `systemPrompt` / `appendSystemPrompt` | — | 调用方自带的系统提示 / 追加段(各 ≤16384 字符,超限 400 `request.field_invalid`;`appendSystemPrompt` 必须非空串)—— 任务自带时优先于角色 / 全局缺省(见 §模型角色)。`systemPrompt` 是**角色层**:它整替所选角色的基底(`code-review` 场景把自己的评审员文本放在它前面),宪法、行为段、记忆段由引擎组在它之后;追加段恒排在宪法之后。逐字含引擎提示段的提示(一份已组装的提示)⇒ 400 `prompt.assembled_role_refused`(7.132.0 起) |
1646
1723
  | `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` |
@@ -1781,15 +1858,17 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
1781
1858
  ## 7. 其它接口(按需)
1782
1859
  | 端点 | 用途 |
1783
1860
  |---|---|
1784
- | `GET /v1/sessions/<id>` | 审计回溯:当前上下文 + 摘要(owner 校验);消息里模型 / 工具产出的文本与 live 帧、账本帧同一只脱敏(密钥形换占位,其余字节不变);Read 图像 / PDF 的媒体卡(工具结果消息的 `details`)的 `file.base64` 是字节编码,逐字 |
1785
- | `GET /v1/approvals?owner=user:42` | 高危写审批(durable,需 `DURABLE_APPROVAL=true`):**operator** 看待办队列(可按 owner 过滤);非 operator 只看自己的 |
1861
+ | `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(存量无主会话互认臂 —— 单用户形下 `default` 主对无主的普通会话放行,fork / 删除 / `/sync/*`(同步族自 7.134.0 起)同走这一只 —— 对这些 placed 行同样不放行,审批豁免、notify 同样不放行;fork / 删除 / `/sync/*` 的 fleet 调用方与此处同一个判据:出示 service 凭据、不带主体头,带主体头的 service 凭据请求是那位主体,7.134.0 起);SQL 后端每次起服回填这些行(起服读数行 `placedSessionOwnersRemaining` 非零 = 还有没定下的,再起直到为 0,或等它们下一次停驻)。推不出属主的子代写成属主 `_orphan:<id>`,只有 fleet-wide / operator 读得到:SQL 后端的子代行根会话已删时按它记录的父会话,父也删了才是 `_orphan:`;停驻时拷进宿主店的那一份(local 后端)只按根判,根会话已删 ⇒ `_orphan:`;子代被派出 / 批准之后、再次停驻读根之前根会话被删并由别人以同一 id 重新登记 ⇒ 其下的子代再次停驻被否决(读过根之后才换的,写进去的仍是读根那一刻的属主) |
1862
+ | `GET /v1/approvals?owner=user:42` | 高危写审批(durable,需 `DURABLE_APPROVAL=true`):**operator** 看待办队列(可按 owner 过滤);非 operator 只看自己的。队列里某一行的存储体读不出来、重试不会好时,答 `500` + `errorCode:"approvals.read_corrupt"`(**7.134.0+**;与订阅流同码、同一只判官;运维处置那一行后再读,别原样重试) |
1863
+ | `GET /v1/approvals/stream` | 同一队列的 SSE 推送(范围同上):首拍快照 → `pending` / `resolved` 增量(开了工具审批时另有 `live_pending` / `live_resolved`),空闲时心跳,15 分钟上限(`STREAM_MAX_DURATION` 帧后重连)。**心跳只表示流活着,不表示这一拍读过队列**:队列读不出时每次读失败发一帧 `error`(`approvals.read_unavailable`,流留着、下一拍再读,恢复后同一条流继续);某一行的存储体读不出来、重试不会好时发 `approvals.read_corrupt` 并收流(运维处置那一行后再重连)。15 分钟上限对读失败的拍同样算数 |
1786
1864
  | `POST /v1/approvals/<sessionId>/decide` `{"decision":"approve"|"deny","reason":"…"}` | 批/否并恢复挂起任务(CAS,重复决议 409);**仅 operator**(非 operator → 403)。5.0.0 起旧轮询腿 `POST /v1/approvals/<id>` 已退役 |
1787
- | `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) |
1865
+ | `GET /v1/tool-approvals/<approvalId>` | 按问读口(**7.133.0+**;与 `POST …/respond` 同门同身份,不是你的 ask 与查无同形 404):一只审批问此刻在哪一态,`state` ∈ `pending` / `decided` / `parked` / `void`(闭集)。停驻卡被答过 ⇒ `decided`、停驻 run 被取消 ⇒ `void`(**7.134.0+**;7.133.x 恒答 `parked`);停驻行的门已结算却读不出可用的决议(含坏行:状态词 / 种类 / 当事方不在闭集、结算记录过不了引擎的不变量筛、缺必填成员、时刻 / 方向读不出)⇒ 404 `tool_approval.not_pending`,运维日志一行 error(`defect` 首条缺陷句 · `defectCount`)。**停驻行的 `decided` 可回到 `parked`:续跑腿在被批准的动作开跑前失败(环境恢复失败 / 工具不在 / 配置被拒 / 决议输入被拒)时,引擎重开同一张门(一次已送达的拒不重开),其中工具不在与决议输入被拒要重新决;续跑被取消 ⇒ 服务端把重开的门收掉,读作 `void`;收掉之前的一瞬、以及收不掉(兜底交给 reaper)时仍可能读到 `parked`,以重读为准;重开瞬间两读之间可能读到 `void`,以重读为准。**全文见契约 §4a-bis-read |
1866
+ | `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=`(**仅** fleet 调用方:不带主体头的 service 凭据 / 只读 `TRACE_TOKEN`(只在没配 service 凭据的部署上够得着这一口)/ dev-open;带主体头的调用方 —— 含带 service 凭据的 —— 锁定自己)。回体 `{agents,total,nextBefore?,truncated?}`;`truncated:true` = 租户行数超过名册窗(500),`total` 此时是窗内数。非属主的行**不出现也不计数**(无存在性 oracle) |
1788
1867
  | `GET /v1/capabilities/scenarios/<name>` | 单个场景的只读详情:工具面、提示词概览、**本部署现在跑不跑得动**(见下) |
1789
- | `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` |
1790
- | `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` 已退役。 |
1791
- | `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 原文。 |
1792
- | `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。 |
1868
+ | `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` |
1869
+ | `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` 已退役。 |
1870
+ | `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 原文。 |
1871
+ | `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。 |
1793
1872
  | `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 **持续**时长;收敛时键缺席)—— 编排器据此摘掉「配置持续落后」的副本 |
1794
1873
  | `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 |
1795
1874
  | `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「探针」 |
@@ -1802,8 +1881,22 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
1802
1881
  > 吃 **501**;`available:true` 时 `unavailableReason` **整键缺席**(缺席 = 没有理由,别读成空串)。
1803
1882
  > 两面共用同一份判据,所以列表上画得出的场景点下去不会再突然 501。请按 `unavailableReason` 的**键**分支,
1804
1883
  > 不要去匹配英文文案——文案会改,键不会(新增取值只会追加,现役键不改语义)。
1805
-
1806
- > **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`。
1884
+ >
1885
+ > **场景的附件供给(7.133.0 起)**:场景详情另带 `attachmentSupplies`(`"path"` / `"content_text"` / `"content_image"` 的数组,顶层能力位 `userAttachments.supplies` 的子集、保持席位序、键恒在场)= 这个场景在**本部署**上受理的
1886
+ > 请求层附件供给。`path` 读的是这一 run 的工作区,只有「场景挂手 ∧ 部署的执行车道有手」的 run 才有:`scan` / `code-review`、center 声明的场景(它们的工具集不挂手)与 `provider: "in-process"` 的部署整台,
1887
+ > 详情里没有 `path`,这些 run 上的 `path` 条目由引擎整条拒 `400 config.user_attachment_invalid`;`content_text` / `content_image` 与工作区无关,恒在。判据只有一只(详情与引擎的拒读同一个「这一 run 有没有工作区」)。
1888
+
1889
+ > **operator 鉴权(审批队列与全部运维面,一条读法)**:`OPERATOR_PRINCIPALS=ops:alice,ops:bob`(CSV)= 谁能当 operator —— 列任意 owner 待办 + 决议(批/否)。非名单 principal 列待办只看自己的、且**不能决议**(防「请求方批自己的高危操作」跳过 F4 闸);
1890
+ > **名单为空 = 没有人是 operator**(每个调用方只看自己的待批 —— 不带主体的调用方看到的是匿名卡〔不带主体提交的任务停下的那几张,即决断口对它开放的那几张〕,`?owner=` 不生效;7.133.0 之前审批四个读面在名单为空时把每个调用方当 operator,那条读法已删)。
1891
+ > **单用户部署当自己的 operator**:`OPERATOR_SINGLE_USER=true` = 「这台部署的每个调用方都是 operator」,对全部运维面生效;只在 `REQUIRE_PRINCIPAL` 未开且 `OPERATOR_PRINCIPALS` 为空时成立(与任一同在 ⇒ 拒启);
1892
+ > 没配服务凭据的部署上能连上端口的都是 operator;不带主体的 operator 没有身份可记,记「谁改的」的写面(`PUT /v1/admin/memory-optout[/:principal]`)要带主体(否则 401)。
1893
+ > 单用户 + 名单空 + 没声明 + 有审批底座 ⇒ 起服 warn `operator_undeclared_single_user`(不拒启)。
1894
+ > ⚠️ **多租户形拒启**:`DURABLE_APPROVAL=true` + `REQUIRE_PRINCIPAL=true` 而 `OPERATOR_PRINCIPALS` 空 ⇒ 进程启动失败并点名修法(空名单下没人能当这台多租 worker 审批队列的 operator)。设名单,或确属单租户则不设 `REQUIRE_PRINCIPAL`。
1895
+ > 🔒 **停驻任务续跑上下文里的凭据是密封的(7.134.0 起)**:任务停驻时存下的提交体里,请求自带的凭据(`mcpServers[].transport.headers` / `.env` 的值、`a2aPeers[].headers` 的值、`settings.env` 的值、
1896
+ > `settings.webSearch.apiKey`、http hook 条目的静态头 `settings.hooks.<事件>[].hooks[].headers` 的值)只以封签落盘(对着这一域自己的保管钥匙 `<数据根>/sealed-key/checkpoint-ctx-keys/` 最新那把公钥封、
1897
+ > 绑着这一行的会话;与 sealed 模型钥匙那对钥匙不共用),续跑时开封;开不开 ⇒ `409 conflict.checkpoint_ctx_unsealable`(`field` + `reason`)。
1898
+ > 因此耐久审批在场的部署(`DURABLE_APPROVAL`;单用户 + 有耐久店时缺省开)要能建保管目录(建不了 ⇒ 起服拒;不要停驻就显式 `DURABLE_APPROVAL=false`),`<数据根>/sealed-key/` 要跨重启持久
1899
+ > (SQL 后端也一样:钥匙不进库;不持久 ⇒ 每次重启换钥匙,重启前停驻的任务续跑答 `reason:"key_not_held"`),多副本要让每个副本的 `<数据根>/sealed-key/` 是同一份(§9.10 ⑦)。升级与回滚序见 §9.10 ⑦ 与 MIGRATION 7.134.0 第 1 条。
1807
1900
  >
1808
1901
  > **部署治理三声明(7.21.0 起)**——三根都是**配置期**旋钮,「谁能改它们」≡「谁能改
1809
1902
  > 部署配置」,不引入新鉴权面;三根**都不设 = 现行为逐字不变**,坏值一律**拒启**(安全轴上的旋钮不静默失效)。
@@ -1949,10 +2042,10 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
1949
2042
  > 🔴 因此:**没配 `SERVICE_AUTH_TOKEN` 且未开 `ALLOW_UNAUTHED_WRITES` 的 worker 上这一口恒 503**,
1950
2043
  > 而能力位仍报 true(本族共有缺口,附录 B.1 已如实登记)。启动日志为此打一条
1951
2044
  > `memory_consolidation_run_credential_gated`;状态读那一口不受影响(它不烧模型)。
1952
- > 🔴 另一条启动告警:`OPERATOR_PRINCIPALS` 为空时两口恒 403(空名单 = 没有任何人是 operator),
2045
+ > 🔴 另一条启动告警:没有运维名册(`OPERATOR_PRINCIPALS` 为空且没有单用户声明 `OPERATOR_SINGLE_USER=true`)时两口恒 501 `capability.operator_principals_required`(没有运维名册 = 没有任何人是 operator),
1953
2046
  > 日志里是 `memory_consolidation_valve_unreachable`。
1954
2047
  > - `GET /v1/admin/memory/consolidation[?scope=]` —— 状态投影(阀门/座位/scope 表/上一轮的账)。
1955
- > - 能力位 `capabilities.memoryConsolidationDriver`(阀门上了场 ∧ operator 名单非空)。
2048
+ > - 能力位 `capabilities.memoryConsolidationDriver`(阀门上了场 ∧ 有运维名册:名单非空或单用户声明)。
1956
2049
  > - 逐键语义与停因闭集见 `docs/ASSISTANT-WIRE-CONTRACT.md` §10。
1957
2050
 
1958
2051
  > **⚠️ hook-wired 部署里,parked 后台子代可能赎回不了(常态,不是升级窗口)。**
@@ -2018,14 +2111,18 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
2018
2111
  > **人的持久规则的读法(7.127.0 起,引擎 7.50.0)**:每个调用读一次规则店;一条持久 deny 行在门上拒它所在那一面的**每个**工具(Read / Grep / Glob / RepoMap /
2019
2112
  > 写工具 / 声明路径目标的调用方工具),`tool_end.gate.disposition.deniedBy = "persisted_rule"`。店的某个分区读不出 ⇒ 该分区上次读到的 deny / ask 行照旧生效、allow 行不在,
2020
2113
  > 射程内的调用真人问(bypass 也问,无审批面即拒),run 一条 `permission.rule_store_unavailable`。
2021
- > **`~/…` 形的行以执行环境的家目录为基**:host / 未设 `REMOTE_EXEC` = 本机本用户,e2b = `/home/user`,k8s / local-docker / ssh / adb / device 不猜 —— 除非 `REMOTE_EXEC_HOME_DIR`
2022
- > 声明。没声明家目录的车道上,一条 `~/…` 的 deny / ask 行判不出点的是哪里 ⇒ 该面文件工具的每个调用都成真人问(bypass 也问);要么声明 `REMOTE_EXEC_HOME_DIR`,要么把规则写成绝对路径。
2023
- > 7.132.0 起(引擎 7.52.0):没声明家目录的车道上,引擎的家锚拒读行(`~/.npmrc` / `~/.sema/settings.json` …)放不下 ⇒ **每一次泄内容的递归读**(`grep -r` / `rg` / `git grep` /
2024
- > `tar c` …)在全 shell 面真人问(allow / 模式 / bypass 都放不掉)、只读面直接拒 —— 无人值守任务检索不了代码;起服一行 warn `execution_lane_home_undeclared`(`provider` + `note`)说出这件事与出路。
2025
- > **声明会被对证**:local-docker 与 ssh 车道在每只执行环境开起来那一拍问一次 `$HOME`(与命令同一条 shell、同一份 env),与 `REMOTE_EXEC_HOME_DIR` 不一致(去掉尾斜杠后逐字比)
2026
- > 或问不出 ⇒ 那只执行环境不开,工具结果里的拒句点名两个值与改法(改声明,或让环境以声明的那个用户运行);环境答了的不一致记在那只环境上(此后每次回同一句,不再起容器 / 不再问),
2114
+ > **`~/…` 形的行以执行环境的家目录为基**:host / 未设 `REMOTE_EXEC` = 本机本用户,e2b = `/home/user`,k8s / local-docker / ssh / adb / device 下单那一刻不猜 —— 除非 `REMOTE_EXEC_HOME_DIR`
2115
+ > 声明(下单门与规则基只认声明)。没声明家目录的车道上,一条 `~/…` 的 deny / ask 行判不出点的是哪里 ⇒ 该面文件工具的每个调用都成真人问(bypass 也问);要么声明 `REMOTE_EXEC_HOME_DIR`,要么把规则写成绝对路径。
2116
+ > 引擎 7.52.0 起:执行环境不带家目录时,引擎的家锚拒读行(`~/.npmrc` / `~/.sema/settings.json` …)放不下 ⇒ **每一次泄内容的递归读**(`grep -r` / `rg` / `git grep` /
2117
+ > `tar c` …)在全 shell 面真人问(allow / 模式 / bypass 都放不掉)、只读面直接拒 —— 无人值守任务检索不了代码。
2118
+ > **家目录座的取值:声明优先,缺省实测**(7.133.0 起):`REMOTE_EXEC_HOME_DIR` 在场 ⇒ 座 = 声明;没声明时 k8s / local-docker / ssh / adb 车道在建执行环境那一拍问一次执行环境自己的 `$HOME`
2119
+ > (与命令同一条 shell、同一份 env;k8s 接在建工作区根的同一条命令后面,不多一次往返;adb 经设备 shell)填座,于是代码检索不再恒问人。实测缺席(没答上 / 答的不是绝对路径,含空串)⇒ 座缺席,
2120
+ > 引擎照旧失败关闭(上面那些递归读照旧问 / 拒),环境照开,并响一行 `execution_lane_home_undeclared`(`provider` + `note`,点名读到了什么与出路;同一部署同一句只响一次)。device 车道的设备协议
2121
+ > 没有家目录读数,座缺席,起服响一行同一只 warn。`~/` 形规则的下单门只认声明(提交那一刻执行环境还没开,读不到实测值):要写 `~/` 形规则就声明 `REMOTE_EXEC_HOME_DIR`。
2122
+ > **声明会被对证**:k8s、local-docker 与 ssh 车道在每只执行环境开起来那一拍问一次 `$HOME`(与命令同一条 shell、同一份 env),与 `REMOTE_EXEC_HOME_DIR` 不一致(去掉尾斜杠后逐字比)
2123
+ > 或问不出 ⇒ 那只执行环境不开,工具结果里的拒句点名两个值与改法(改声明,或让环境以声明的那个用户运行);环境答了的不一致记在那只环境上(此后每次回同一句,不再起容器 / pod / 不再问),
2027
2124
  > 问不出的那一次不记、下一次重问。
2028
- > k8s / e2b / host / adb / device 车道今天不对证(声明照单全收)。
2125
+ > e2b / host / adb / device 车道今天不对证(声明照单全收)。
2029
2126
  >
2030
2127
  > **同批顺带:场景旧名 `team` / 工具旧名 `run_team` 退役(BREAKING)。** 此前过渡窗的别名层
2031
2128
  > (`SCENARIO_ALIASES`)整体撤销:请求体里显式传 `scenario:"team"` 从「静默归一到 `discuss`」变成
@@ -2115,7 +2212,7 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
2115
2212
  ## 8. 三类调用方怎么接
2116
2213
 
2117
2214
  **A. OA 系统(服务到服务后端)**
2118
- - OA 后端持有 `SERVICE_AUTH_TOKEN`,每个请求按当前终端用户设 `x-agent-principal: user:<id>`。
2215
+ - OA 后端持有 `SERVICE_AUTH_TOKEN`,每个请求按当前终端用户设 `x-agent-principal: user:<id>`。带了主体头的请求只看得到、动得到那位用户的(7.134.0 起 fleet 面同读);后台自己的全舰队运维视图**不带主体头**(或用只读 `TRACE_TOKEN`)。
2119
2216
  - 短问答 → `/v1/tasks`(同步);长任务/要进度条 → `/v1/runs` + `/events`(异步)。
2120
2217
  - 续聊:把上次 `sessionId` 存在 OA 会话里,下次带回。
2121
2218
 
@@ -2145,12 +2242,17 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
2145
2242
  | HTTP `429` | 限流 | 看 `Retry-After` 退避 |
2146
2243
  | HTTP `429` + `errorCode:"limit.cost_quota_exceeded"` | per-principal 累计成本配额越顶(`MAX_PRINCIPAL_COST_USD`;**进场门**,不打断在跑的 run) | 看 `Retry-After` / 体 `retryAfterSec` 退避(体 `windowResetsAt` = 当代窗滚过的时刻);窗滚过或调高上限后放行 |
2147
2244
  | 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 …`),只作展示、别锚文案 |
2245
+ | 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` 分支,句子只作展示 |
2148
2246
  | 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` 分支,句子只作展示 |
2149
- | HTTP `400` + `errorCode:"request.field_invalid"` + `reason:"over_cap"` / `"too_long"` / `"bad_entry"`(7.132.0 起:工具名清单、子代定义、两张宿主路径清单;其余的帽仍只有句子) | 撞了帽:`field` = 撞帽那一处的体内坐标(`skills[1].allowedTools` · `agents[0].denyTools[3]` · `agents` · `additionalDirectories` …),`count` = 送来的条数(`over_cap`)或字符数(`too_long`),`cap` = 帽;`bad_entry` = 清单第 `index` 条坏了(`field` = 那张清单)。句子头一句恒为 `<field> has <count> entries\|characters — at most <cap> are accepted` | 按 `field` 改那一处(合并 / 拆小条目、缩短文字、修那一条路径);句子只作展示 |
2247
+ | 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` 改那一处(合并 / 拆小条目、缩短文字、修那一条路径);句子只作展示 |
2248
+ | 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` 定位到那几个键;句子只作展示、别锚文案 |
2150
2249
  | 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` 后的现名(或删掉条目);只列这台部署挂得出的工具名 |
2151
2250
  | 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` 后的现名,或删掉条目 |
2152
2251
  | HTTP `400` + `errorCode:"prompt.assembled_role_refused"`(7.132.0 起;流式 / 后台腿是 `failed` 终局带同码) | 请求的 `systemPrompt` / `appendSystemPrompt`(含 `settings.outputStyle`)逐字含引擎提示里的一段锁定文本(宪法、安全段、`# Harness` 这样的标题行)—— 一份已组装的提示。开跑前拒、零调用,句子点名字段与命中段 | 只写角色层:删掉抄进来的引擎段(引擎自己组宪法与行为段;追加段恒排在宪法之后) |
2252
+ | 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` 的条数 ⇒ 列表被截 |
2253
+ | 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 记录答,不重判、不带;在飞合流回放的是首发那一腿铸的应答 |
2153
2254
  | 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` 后的现名(或删掉条目);超帽的合并成更宽的条目 |
2255
+ | 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` |
2154
2256
  | HTTP `429` + `errorCode:"usage.window_exhausted"` | 部署级治理窗耗尽(`USAGE_WINDOWS`,token 或 $ 天花板先满者) | 看响应体 `retryAfterSec` 退避;窗滑动/桶到期后放行 |
2155
2257
  | `terminal:{kind:"failed", code:"usage.window_exhausted"}` | 已受理的 run 在 **turn 边界**撞上治理窗且无法 durable 挂起 | 同上退避后重投;配好 checkpoint 基建则改为 `suspended` 等窗自动续跑 |
2156
2258
  | HTTP `501`(`/v1/runs`) | 内存模式不支持异步 | 配持久存储(`DB_BACKEND=mysql` / `pg` + 坐标;7.106.0 起给了坐标必须点名 `DB_BACKEND`) |
@@ -2298,7 +2400,7 @@ k8s 车道每条 run 一只新 pod(pod 寿命 = 泄漏护栏,不跨 run 复用)
2298
2400
  - **读 `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` 即恢复。
2299
2401
  - **`latest` 指向的快照对象不在了**(被删 / 被只盖 `.tar.gz` 的生命周期规则清掉)⇒ 沙箱**照常起**:一行 `workspace_latest_dangling`(`space` / `key` / `deleted`),删掉悬空的 `latest`,这条 run 从空目录开始,它的 run 末快照成为新的 `latest`(数据已被运维规则清走:响亮、可继续)。指针删不掉 ⇒ 同一行 `deleted: false` + `error`,下一条 run 再说一次。
2300
2402
  - **`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 从空目录起)。与上一条「悬空」分开:那是对象**不在**(没东西可恢复 ⇒ 空目录 + 响亮),这是对象**在**而拿不回来(⇒ 拒)。
2301
- - **run 末提交围栏失守、上传失败或 `latest` 写失败** ⇒ 一行 `workspace_snapshot_failed`(`space` / `stage` = `fence` \| `upload` \| `pointer` / `code` / `error`,预签 URL 已脱敏),这条 run 在 `GET /v1/fleet/stream` 上的终态 task 帧带 `workspaceSnapshot: "failed"`;pod 照删;`latest` 不动 ⇒ 下一条 run 恢复的是**上一份**。
2403
+ - **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 去改指针。
2302
2404
  - **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` 不动,下一轮恢复上一份 —— 取消 = 丢掉这一轮的工作。
2303
2405
  - **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:配额等)不在此列,仍是那一次尝试的失败。
2304
2406
  - **删最老的失败** ⇒ 一行 `workspace_snapshot_prune_failed`,不影响本轮(空间里只是多留几份)。
@@ -2453,6 +2555,7 @@ armed ⟺ 本次请求 permissionMode 是 "auto" ∧ 本部署装配了分类
2453
2555
  | `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 域) |
2454
2556
  | `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 轴) |
2455
2557
  | `CROSS_SESSION_DIALOG_EXPIRY` | 未设(⇒ 引擎缺省 `5m`) | 同族:CC settings 键名 `dialogExpiry` 的部署层 —— 被 hold 的跨会话消息等待人审多久后按**安全缺省**结算(过期丢弃,**带回执**告知发送方,不静默吞)。词表 `60s`/`5m`/`10m`/`never`(`never` = 不设期限);未设 ⇒ 本仓**不铸键**,由引擎落它自己的缺省(不复制上游缺省值)。词表外拒启。⚠️ 生效前提同上。另注:CC 的同名键还有第二个消费面(转发到远端客户端的审批对话框停靠时长),本旋钮**今天不驱动**那一面 |
2558
+ | `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 封。⚠️ 本版只有投递半:读信的助理腿随引擎下一版,在那之前信留在箱里没人读 |
2456
2559
 
2457
2560
  **自查读面**(`GET /v1/capabilities.permissionModeAuto`,壳的 `sema doctor permissions` 消费;server 7.106.0 起引擎只剩一种武装极性,
2458
2561
  7.107.0 起成员 `intentArming` 删除,见该条):
@@ -2482,6 +2585,16 @@ GET /v1/capabilities?permissionMode=auto (AUTONOMY=plan 的部署)
2482
2585
  - 读面按调用方身份解 per-principal caps(与引擎同一只 resolver,per-principal 缓存 60s;center 硬失败时受 8s
2483
2586
  超时 + 10s deny 缓存界)。
2484
2587
 
2588
+ **组织层天花板(center 的 per-principal entitlement;7.133.0 起两键生效)**:center 下发给某个 principal 的
2589
+ `runtimeCaps.allowBypassPermissions:false`、`runtimeCaps.allowAutoMode:false`(与既有的 `runtimeCaps.autoMode:false`)由引擎执行。
2590
+ 这个人请求的 `bypassPermissions` / `auto` **不是 4xx**:受理照常,这条腿跑在 `default`(写会问),流上一帧 `engine_notice`
2591
+ `config.permission_mode_unavailable`(`detail.by:"operator_deny"`),`wiring_manifest.permissionMode` 回执
2592
+ `{ requested, effective:"default", foldedBy:"operator_deny" }`。auto 的两键任一为 `false` 即拒,另一键的 `true` 翻不回;`true` / 缺席 = 不拒。
2593
+ 两键的值不是布尔 ⇒ 当作契约破损,这个 principal 的放大面整份 fail-closed。center 取数硬失败的降级窗(最长约 10 秒,之后整份重抓)里
2594
+ bypass 与 auto 同样折回。7.132.x 及更早这两键被服务端丢掉:组织关了 bypass,引擎照放。
2595
+ 部署级的 `TASK_WRITE_FACE_ESCALATION` 管「这台部署给不给任务层掀写围栏」,本条管「组织给不给这个人」—— 两道都过才是 bypass;
2596
+ 能力位 `taskWriteFaceOpen` 只答前一道(部署级,看不到 per-principal 的拒),实际模式读回执。
2597
+
2485
2598
  **settings 层 kill-switch(件⑥)**:请求体 `settings.permissions.disableAutoMode` 受理 **`"disable"` / `true` / `false`** 三形(两套已发布契约的拼写:CC 250 的严极词 `"disable"`,与 `@sema-agent/sdk` `SettingsPermissions.disableAutoMode?: boolean`);受理集之外的值 400 `request.field_invalid`。
2486
2599
  `"disable"` 与 `true` **同义**:把该 run 的生效模式由 auto 折成 default(座不写 ⇒ 不武装;tighten-only,只能关不能开);`false` = 显式「不禁」= **合法且无效果**(本键没有放宽臂——它不是 auto 的开关)。`disableBypassPermissionsMode` 本批不落点。
2487
2600
 
@@ -2504,7 +2617,7 @@ GET /v1/capabilities?permissionMode=auto (AUTONOMY=plan 的部署)
2504
2617
 
2505
2618
  plan 起步的任务把计划交给人之后停在 `plan_review` 门上。`POST /v1/assistant/tasks/:id/plan_review` 里把计划**带出 plan** 的决定
2506
2619
  —— `decision:"approve"`(按原计划开干)与 `decision:"edit"`(按人改过的计划开干;7.122.0 起)—— 可携 **`permissionModeAfter`**
2507
- (闭集 `"default" | "acceptEdits"`,**缺席 ⇒ `"default"`**):`acceptEdits` = 继续跑时对自己工作目录内的写不再逐次征询,`default` = 有手但每次写照常征询。
2620
+ (闭集 `"default" | "acceptEdits"`,**缺席 ⇒ `"default"`**):`acceptEdits` = 继续跑时对写根(任务根 + 请求的 `additionalDirectories`)内的写不再逐次征询(引擎的模式门答),`default` = 有手但每次写照常征询。
2508
2621
  `reject` 的语义是重做计划,plan **原样保留**,在它上面送这一位是 `400`(判据与引擎同句:只有 `reject` 不出 plan)。词表外的值、以及存储模式词不是 plan
2509
2622
  的任务(模型自己调 `EnterPlanMode` 那一形 —— 批准把它送回进 plan 之前的档)送它,同样是 `400`;`AUTONOMY=plan` 下提交的任务存储词就是 plan,照收。
2510
2623
  解除会写回这条会话的续跑输入 —— 此后每一次挂起→续跑都沿用解除后的档,不回落 plan(`AUTONOMY=plan` 也不把它拉回:地板只在提交入口盖一次)。
@@ -2616,7 +2729,12 @@ WebSearch 工具的 `tool_result` 错误文本,回到模型的对话里,**不是
2616
2729
  超过它就拒(`budget_exceeded`)。字面形(`A|B`)与非工具事件不进求值、不占总额度。良性 matcher 的求值是微秒到毫秒级,两层都给了数个数量级的余量(一份最大的良性配置 —— 3 个工具事件 × 16 组 —— 总耗时在毫秒级),
2617
2730
  所以良性设置的结局只取决于设置本身:机器忙闲不会让它一次 400、一次 202;只有求值总耗时逼近 `5 000 ms` 的病态设置才可能落在边界两侧。灾难回溯(指数级)用任何预算都跑不完。
2618
2731
  多只 matcher 时按配置顺序(事件 → 组)遇到的第一只被拒的定结局。一次受理最坏占用事件循环 ≤ 总额度 + 一次求值的额度(此前没有总界)。
2619
- - **为什么超时是拒绝、不是放行**:运行期每次工具调用都用同一个函数匹配 matcher、没有预算;一只在提交时都求不完的 matcher 放进去,会让之后每次工具调用卡住。
2732
+ - **为什么超时是拒绝、不是放行**:运行期每次工具调用对同一只 matcher 的求值也带这一份预算(7.133.0 起,见下);一只在提交时连目录里几个短名字都求不完的 matcher 放进去,运行期每次调用都要白烧一整份预算。
2733
+ - **运行期同一只口、同一个预算(7.133.0 起)**:单用户车道上 hooks 被采纳后,每次工具调用对正则形 matcher(以及条目 `if` 条件的名部)的每次求值也各拿一份同样的 `1 000 ms` 墙钟预算,字面形(`A|B` / `*` / 缺省)不进求值、不占预算 —— 不另设运行期旋钮。
2734
+ 受理面只对目录里的别名 / 正名这几个短名字求过值,运行期要对任意别的工具名(MCP 工具名等)求值:受理过了的 matcher 在某个名字上灾难回溯,此前会同步占住事件循环(此前运行期没有预算)。
2735
+ **超预算 ⇒ 这一组按「选中」处理**(守卫钩子不被跳过,组里的条目照常跑;`if` 超预算同:条目照跑;这样跑起来的条目只有否决生效 —— 放行与 `updatedInput` 不作数,托管组的条目另记没跑成、`PreToolUse` 折问人)并**响亮登记**:一行 warn `hook_matcher_over_budget`(`event` · `scope` · `expression` · `value` · `budgetMs` · `selected: true`)
2736
+ + fleet 流上一帧 `hook_notice` / `hook_non_blocking_failure`,`reason: "matcher_over_budget"`(契约附录 E.2)。每次发生各登记一次,一直在坏就一直说;最坏占用是「(超预算的组数 + 超预算的 `if` 条目数)× `1 000 ms`」每次工具调用 —— 看到这行日志就把那只 matcher(或 `if` 条件)改成工具名列表(`A|B`)或简化正则。
2737
+ 日志与帧里的 matcher / 条件 / 名字先脱敏、再剥控制与双向字符、截 200 字。
2620
2738
  - 拒句点名是**哪一只** matcher(`settings.hooks.<event>[<groupIndex>].matcher`):`over_budget` 一形带预算、`budget_exceeded` 一形带总额度、都不回显 matcher 原文;`invalid_regex` 一形回显 matcher 原文与编译错误句(各截 200 字符,`detail` 不带这两段)。
2621
2739
  - **这一判只在 hooks 会被采纳的车道上执行正则**(单用户车道;多租户车道 `settings.hooks` 整只被忽略,只做不执行正则的字面判):多租户部署不会因此多出一个在提交线程上跑调用方正则的面。
2622
2740
  - 不变的:matcher 超 `8 192` 字符不读(照旧受理,运维侧记一笔 `server.hooks.matcher-unjudged`)。
@@ -2641,9 +2759,15 @@ WebSearch 工具的 `tool_result` 错误文本,回到模型的对话里,**不是
2641
2759
  审批卡 `allowRuleShadowed` 的 `hook` 成员、钩子失败时模型读到的拒句都点名它)。本部署自己另挂两张只观察的注册:恒挂的拒绝计量
2642
2760
  `server.permission-denied-meter`,`TOOL_TRACE=true` 时的 `server.tool-trace`(品牌部署另有一张提示装配站上的 `server.brand-identity`,只答品牌行 overlay,不在工具门上)。观察者的判词不采纳,链里唯一的答者是请求的钩子。
2643
2761
  - 每个事件的语义与此前相同:`Stop` 条目在主任务结束与委派子代结束时都跑;`PermissionDenied` / `SubagentStop` / 两个 model-switch 事件属壳,服务端不跑。
2644
- - **`HOOKS_TIMEOUT_MS`** = 这张注册每个 handler 的时间预算(一个事件跑一遍本请求的钩子条目);**未设 = 600000 ms**(单条命令超时上限即 600 s);
2645
- `0` 按字面(零预算)。超预算:`PreToolUse` 上该调用被拒(fail-closed,拒句点名 `extension "settings.hooks"`)、`UserPromptSubmit` 上该提交被拒,其余事件跳过这一轮钩子。
2646
- 单条命令自己的超时(`timeout`,上限 600 s)与一个事件 120 s 的墙钟(到点不再起下一条)照旧。
2762
+ - **`HOOKS_TIMEOUT_MS`** = 这张注册的 `budgetMs`。引擎对它的定义是 "Each handler's own time" —— 每个 handler 自己的时间(一个事件跑一遍本请求的钩子条目;
2763
+ 引擎的钟,handler 等 `next` / `$` 调用的那段暂停),**不是**钩子子进程的墙钟。**未设 = 600000 ms**(= 单条命令超时的上限;引擎的钟在 handler 起跑时就开始走,
2764
+ 命令的 `timeout` 钟要等 shell 解析和起进程之后,所以 `timeout` 配到 600 s 的命令跑满时,预算会先到);`0` 按字面(零预算)。超预算:`PreToolUse` 上该调用被拒(fail-closed,拒句点名 `extension "settings.hooks"`)、
2765
+ `UserPromptSubmit` 上该提交被拒,其余事件跳过这一轮钩子。子进程自己的墙钟另有两只,照旧:单条命令的 `timeout`(上限 600 s)与一个事件 120 s 的总墙钟(到点不再起下一条)。
2766
+ - **预算到、或任务被取消 ⇒ 这个 handler 里在跑的命令钩子被叫停**(7.133.0 起;此前它要跑到自己的 `timeout` 才停,取消之后还可能接着写文件):进程组先收 SIGTERM,
2767
+ 1 秒到点组长还在就补一发组 SIGKILL(组长在宽限内退了就不再发 —— 退出的进程号可能已被回收;代价:组长一退,组里屏蔽 SIGTERM 且不攥管道的后代不再被追杀),
2768
+ 进程组长退出才算收走;这个 handler 里还没起的后续条目不再起。结局不变(上一条的 fail-closed 拒 / 跳过),运维日志多一行
2769
+ `hook_command_killed`(`event` / `type` / `killed: true` / `signal`:`SIGTERM` = 组长在宽限内就退了,`SIGKILL` = 宽限到点组长还在、升级过)。钩子确实要跑得更久:调大 `HOOKS_TIMEOUT_MS`
2770
+ (与条目自己的 `timeout`);要在被叫停时收尾,在命令里 `trap … TERM` 快速落盘(宽限 1 秒,不可配)。
2647
2771
 
2648
2772
  ### 9.5d MCP 服务器 http 传输的身份头:`principalHeader` 与 `assertionHeader` 二选一(请求体与部署声明同一种读法)
2649
2773
 
@@ -2651,13 +2775,20 @@ WebSearch 工具的 `tool_result` 错误文本,回到模型的对话里,**不是
2651
2775
  - 请求体(`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`)。
2652
2776
  - `assertionHeader` 的**值**是头名,本服务不铸、也不校验具体词(只判是字符串且与 `principalHeader` 互斥);头名由引擎(上游)定为 **`connector-assertion`**(无 `x-` 前缀;引擎发送座落地后本服务只认这一个值),条目填别的名 ⇒ 连接器侧 401。
2653
2777
  - 本版只承载、不发送断言:声明了 `assertionHeader` 的 server 照常挂载,但不带断言头;每台这样的 server 一行 info `mcp_assertion_header_not_sent`(`server` / `note`)。探测口 `POST /v1/capabilities/mcp/probe` 对声明了该键的条目整条拒 `400 request.field_invalid`(探测不拨一份少了断言头的配置)。
2778
+ **发不发有一位可读**:`GET /v1/capabilities` 的 `mcpAssertionHeaderSent`(布尔,今天恒 `false`)—— 它与剥键、那行 info、探测口的那一拒读同一只开关,**互斥**:位 `false` ⇒ 三者都在(别指望断言头会发出去);位 `true` ⇒ 三者都不再出现、声明的头名随规格交给引擎。按位分支,不要匹配 info 文字;键缺席 = 老 server,按「不发送」读。
2654
2779
 
2655
- ### 9.5e 请求体 `mcpServers` 的列表与条目键:写坏,整份新提交 400(7.131.0 起;列表级与词表外的键 7.132.0 起;`startupTimeoutMs` 的值形由引擎在拨号时判,见本节首段)
2780
+ ### 9.5e 请求体 `mcpServers` 的列表与条目键:写坏,整份新提交 400(7.131.0 起;列表级与词表外的键 7.132.0 起;`startupTimeoutMs` 与 `toolFaces` 的引擎判官 7.134.0 起)
2656
2781
 
2657
- 条目的受理键 = `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」是对其余受理键:
2658
- - **新提交**(`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` = 没声明,照常受理。
2782
+ 条目的受理键 = `name` · `transport` · `allowTools` · `toolAxes` · `toolFaces` · `contentOrigin` · `elicitation` · `source` · `startupTimeoutMs`(7.132.0 起)(`transport` 的子键:`kind` · `command` · `args` · `env`(stdio),`kind` · `url` · `headers` · `principalHeader` · `assertionHeader`(http))。**新提交上每个键只有一只判官,判官在受理口,写坏的答案只有一种**(下面第一条:400)。`startupTimeoutMs`(这一只 server 自己的启动界,毫秒整数,范围与引擎 env 旋钮 `MCP_TIMEOUT` 同一段)7.134.0 起同样在受理口判 —— 判官是引擎包根导出的 `mcpStartupTimeoutMs`(启动界的唯一读者):数字串 / 小数 / 越界 / `null` ⇒ `400 request.field_invalid`,原因句是引擎的那一句(7.132.0–7.133.x:受理口不判、请求照常 200,引擎拨号时只拒这一只 server);耐久 resume 重放存量请求不判(原样交引擎,坏值在拨号时只拒这一只 server —— `config.mcp_startup_timeout_invalid`,兄弟照挂)。
2783
+ - **新提交**(`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` 词表外的值;`toolAxes` / `toolFaces` 的原因句是引擎写的(7.134.0 起 `toolFaces` 的判官也是引擎包根导出的 `readToolFaces`):点名工具名(引擎截 80 字)与成员,`toolFaces` 还会点名闭词表拒的那个词,整句详情由引擎截到 240 字、本服务再剥控制与双向字符 —— 不回显显示文本、不回显合规的列表;`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` = 没声明,照常受理。
2659
2784
  - **耐久 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`。
2660
2785
  - `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`。
2786
+ - **`toolFaces` 的判官是引擎的 `readToolFaces`(7.134.0 起;此前本服务自带一只只判 JSON 形的读器)**:它在门口就判声明本身能说的一切 —— 成员与类型、五张闭词表、上界、跨成员律 ——
2787
+ 所以下面几形 7.133.x 受理、7.134.0 起 400(重放腿整条 server 不进、中心 / `config.d` 腿跳过该 server 并点名 warn):空工具名;保留审批卡(`renderHints.approvalCard: "question"` / `"plan"`);
2788
+ 🔴 **`pathTarget.access` 是闭集 `read` / `create` / `edit`** —— 写成 `"write"`(或任何别的词)整条拒;**`pathTarget.base: "root"` 要求 `access: "read"`**(写面落根基底整条拒);
2789
+ `patternParam` 只在 `absent: "base"` 旁有意义,单独写是一份读不到的声明(inert)、同样整条拒;未知成员(笔误)、安全轴写成面成员(`effect` 属于 `toolAxes`)、非布尔旗、错型成员。
2790
+ 部署腿(中心 / `config.d`)的坏声明若绕过本服务直接到引擎,引擎在准备门**整任务**拒(`config.tool_face_invalid`)—— 一处笔误整任务起不来,写之前对一下这三条。
2791
+ 要看物化 schema 才判得了的那一半(`param` 是不是工具的顶层参数、审批卡要的键在不在)与 `resultCards` 落在别的命名空间,仍由引擎在列举处**按工具**判(那一只工具不挂,通告 `config.tool_face_invalid`,兄弟照挂)。
2661
2792
  - 7.130.x 及更早:`allowTools` / `toolFaces` / `name` / `transport` 写坏 ⇒ 200 + 这条 server 不挂;`elicitation` / `source` / `transport` 的可选子键写坏 ⇒ 200 + 悄悄丢键。7.131.x 及更早:列表整只不是数组 ⇒ 200、整只忽略;条目不是对象 ⇒ 200 + 点名丢弃;超过 32 条 ⇒ 200、挂前 32 条;词表外的键 ⇒ 悄悄不读。升级后这些请求变 400:改对那个键(或删掉它)、把列表改成数组、删掉第 33 条起的条目。
2662
2793
 
2663
2794
  ### 9.5f 中心提示层:一种 wire 形、按场景挂段、品牌行与「无持久记忆」披露(7.132.0 起)
@@ -2880,6 +3011,11 @@ web-admin 拉取 / fleet 公告 / 凭证续签、WebSearch、对象存储、OTLP
2880
3011
  ⇒ ok;completed 非 success(零步骤 = CI 没起,如 7.103.1 的账单真案 / 有步骤 = CI 红)、排队零步骤、读不出 ⇒ red(读不到 ≠ 健康)。
2881
3012
  它**不再是发车链的一道门**:门 1.11 随同一裁定撤出(它的前提「CI 起不来 ⇒ tag 推了也发不了包」已不成立),起链不再需要 `GH_TOKEN`,
2882
3013
  放行旋钮一并删除。CI 起不来今天挡的是镜像与 main 上的全套,不挡发包。
3014
+ 读数行尾另有三键 `image=<态> imageArch=<平台逗号列表|-> imageTag=<vX.Y.Z|->` —— **镜像格**:本仓本地最新的 `vX.Y.Z` tag 的镜像 manifest 在 GHCR 上在不在、几个平台
3015
+ (`image` 闭集 `present-multiarch` / `present-single` / `absent` / `unreadable`;镜像名从 `ci.yml` 的 `image` job 读,不手写)。它**匿名**读 registry,不经 Actions、不吃 `GH_TOKEN`,
3016
+ 所以账单类的病(CI 的 run 起不来、镜像没出)里它照样有读数;也因此**它是信息格,不进 `verdict`、不改退出码**。读不到(网络 / 本地没有 release tag / `ci.yml` 读不出 / 形读不懂)
3017
+ 恒写 `unreadable` 并在 stderr 说哪一步 —— 不是缺席,更不是绿;`absent` 只在 manifest 回 `404 MANIFEST_UNKNOWN` 时给,刚推的 tag 要等 CI 的 test 走完、`image` job 构建并推送完才有镜像,
3018
+ 这段时间里读到它是常态,过了仍 `absent` 才是镜像腿没出。最新 tag 取本地 tag(发车机上有),巡查机上的 tag 不新就先 `git fetch --tags`。
2883
3019
  - **`scripts/remote-gate.sh ship`** 只把 `git ls-files --cached --others --exclude-standard` 列出的文件打包上 k3s8(已跟踪 + 未跟踪而未被 ignore;
2884
3020
  工作区未提交的改动照带),被 gitignore 的本地探针测试 / scratchpad 读数不再上船。清单生成不出来(不在 git 工作树里 / 空树)⇒ 在碰远端目录之前停。
2885
3021
  - **core 提货门**(`node scripts/core-read-table-gate.mjs`)求交的是 **(当前钉, 目标]** 区间里 core 的**每一版**清单:从目标清单沿 `from`
@@ -2967,7 +3103,10 @@ web-admin 拉取 / fleet 公告 / 凭证续签、WebSearch、对象存储、OTLP
2967
3103
  (形照 core 的 `wire-changes/core@<ver>.json`:`added` / `removed` / `changed` / `narrowed` + `closedSetMembers` + `breaking`)。源 = 契约附录
2968
3104
  A.1 ∪ A.1b 错误码(属性 = 状态码)、B.1 能力位 / B.2 `taskSettings` 子键(属性 = 谓词类)、D.3 `engine_notice` 白名单(属性 = `detail`)、
2969
3105
  `src/trace/wire-projection-faces.ts` 的 `WIRE_PROJECTION_FACES`;对比上一 tag 与本版(tag 在读 tag,不在读工作树)。源读不懂 ⇒ exit 2
2970
- (不产出「零变更」清单)。随 release commit 提交;查账门(门 1.6)第三腿 `--check` 核它在仓且与生成输出逐字节同,缺席 / 陈旧 ⇒ 红。
3106
+ (不产出「零变更」清单)。面表里带 `keys` 声明的面(7.133.0 起:`prompt_assembled` 清单行的十二层,账本行 / turns 块 / trace 帧同一形)
3107
+ 做**键级**比较,与面的增删用同一张表、同一只比较器:删一个键或改了键的值形 / 容器 / 嵌套面 / 由必选变可选 ⇒ `removed` / `narrowed`(`breaking`),
3108
+ 新增键 ⇒ `added`,由可选变必选 ⇒ `changed`;该面此前没有键声明(首次登记)时既有的键进 `registered`,不当本版新上 wire。其余帧面的键集仍由 `coreType`
3109
+ 在安装态 core 里展开,生成器看不见它们的键级增删(core 升版带来的键变化由提货批的读面 × core 清单求交门与各面的 `_Guard*` 编译钉盯)。随 release commit 提交;查账门(门 1.6)第三腿 `--check` 核它在仓且与生成输出逐字节同,缺席 / 陈旧 ⇒ 红。
2971
3110
  `wire-changes/server@7.104.0.json` 是回填的第一份。
2972
3111
  - **台账 `refs:` 列建议**:`node scripts/ledger-refs.mjs [--comms-dir <dir>] [--json]` 只读 sema-comms 的 `debts/DEBTS-server.md` /
2973
3112
  `PLANS.md`(server 段)/ `bugs/B-*.md`(文件头 `owner:` 格),按「一方点了另一方的号」双向互链,输出每行的 `refs:` 建议值(号按数值认,
@@ -3031,7 +3170,9 @@ const ciphertext = Buffer.from(sodium.crypto_box_seal(sodium.from_string(apiKey)
3031
3170
 
3032
3171
  **⑥ 保管目录与轮换。** 保管目录 = `<数据根>/sealed-key/`(数据根 = `LOCAL_DATA_ROOT` → `CONFIG_LOCAL_DIR` → `AGENT_DATA_DIR` → `~/.ai-agent`),
3033
3172
  每把钥匙一个文件 `<publicKeyId>.json`(文件 `0600`,目录 `0700`)。**私钥只在这里**:丢了,对着它封的密文全部变成 `stale_ciphertext`,
3034
- 只能重封 —— 备份时按私钥对待。
3173
+ 只能重封 —— 备份时按私钥对待。7.134.0 起它下面还有一个子目录 `checkpoint-ctx-keys/`:停驻任务续跑上下文里请求凭据的封签钥匙(一个域一对钥匙,与模型钥匙不共用;
3174
+ 同样的文件形、同样的「空目录生成一把」;不向中心登记、不出现在 caps 上)。它丢了,封在停驻行里的凭据就开不开(续跑答 `409 conflict.checkpoint_ctx_unsealable` `reason:"key_not_held"`)——
3175
+ 所以整个 `sealed-key/` 目录要**跨重启持久**(SQL 后端也一样:钥匙不进库)。下面的轮换步骤对这个子目录同样适用:旧私钥文件留到用它封的停驻行都已结束为止。
3035
3176
 
3036
3177
  - 起服时读目录里的全部钥匙文件;一个可用的都没有 ⇒ 生成一把新的。读不了的文件(坏 JSON / 不认识的格式)跳过、**不删**,warn
3037
3178
  `sealed_key_file_unreadable`。文件只在起服时读:运行中放进来的钥匙文件要重启才认。
@@ -3047,9 +3188,17 @@ const ciphertext = Buffer.from(sodium.crypto_box_seal(sodium.from_string(apiKey)
3047
3188
  或放好钥匙文件)再起其余副本 —— 空目录上并发首启会各自生成一把,各副本只持有自己那一把,直到下次重启。目录要对每个副本**可写**(每次起服
3048
3189
  会把它设成 `0700`;只读挂载 ⇒ 建店失败 ⇒ `no_key_store`)。⚠️ 只共享 `sealed-key/` 这一个子目录:`DB_BACKEND=local` 的整个数据根禁止跨容器 /
3049
3190
  跨主机共享(`docs/DEPLOY-PREREQS.md`)。
3191
+ 照随包 `deploy/sema-up` 图表部署时这一步图表替你做了:`sealedKey.persistence` 块(缺省开)把整个 `sealed-key/` 目录挂成一只 PVC,访问模式缺省单副本 `ReadWriteOnce`、多副本 `ReadWriteMany`
3192
+ (要存储类支持 RWX;空卷首装后 `rollout restart` 一次,让并发首启各生成的钥匙互相都持有);显式关掉它(`sealedKey.persistence.enabled: false`)图表会同时给 worker 设 `DURABLE_APPROVAL=false`,并拒绝再配 `server.durableApproval: true`。
3050
3193
  - **走中心形**:接 web-admin,执行侧公钥登记在中心(起服时带管理令牌自动上报,否则把公钥打印出来供手工登记),web 面对着登记的公钥封、中心存密文。
3051
3194
  调用方不必逐台去问 caps;但同一个 worker 的多个副本各有各的保管店这件事中心形不改变 —— 要它们都开得了同一份密文,仍要共享保管目录。
3052
3195
 
3196
+ **停驻任务的凭据封签(7.134.0 起)同一个目录**:共享的是整个 `<数据根>/sealed-key/`(含子目录 `checkpoint-ctx-keys/` —— 停驻行封签那一域自己的钥匙)。不共享 ⇒ A 副本停驻、带请求凭据的任务
3197
+ 在 B 副本上续跑答 `409 conflict.checkpoint_ctx_unsealable` `reason:"key_not_held"`;单副本也要让这个目录跨重启持久。**7.133.x 与 7.134.0 不能混跑**:先停掉全部 7.133.x 副本,再起第一个 7.134.0 副本
3198
+ (一次性迁移 M28 在它起服时把升级前停驻行里的明文封好);混跑时 7.133.x 副本续跑一条已封签的行会把凭据位当空值、任务无凭据照跑、不报错。降级到 7.133.x 之前先把带凭据的停驻行结掉(决定或取消)。
3199
+ 多副本升级时先起一个 7.134.0 副本、等它起好再起其余副本(耐久审批开着时它起好即打出 `checkpoint_ctx_credentials_sealed` 一行):停驻封签的钥匙在新子目录 `checkpoint-ctx-keys/` 里,
3200
+ 升级后第一个起服的副本生成它(`sealed-key/` 早已共享好也一样);同时起多个副本会各建一把、互相答 `key_not_held`,直到各自重启一次(钥匙只在起服时读目录里的全部文件)。
3201
+
3053
3202
  ### 9.11 会话规则店:抹除封键与显式恢复(7.106.0 起)
3054
3203
 
3055
3204
  适用于 `DB_BACKEND=mysql|pg`(SQL 会话规则店;`DB_BACKEND=local` 用的是引擎自带的文件店,不在本节)。
@@ -3126,3 +3275,91 @@ UPDATE session_policy_rev_mark SET rev = 0, gen = gen + 1 WHERE policy_key = '<w
3126
3275
  而且本版写下的规则条目在旧版上不生效(见 `MIGRATION.md` 7.129.0 节)。`DB_BACKEND=local` 的规则在引擎自带的文件店里,不改列名:带命令桶的规则文件读回 `corrupt`,出路在引擎的拒句里。
3127
3276
  文件店也没有上面那一步判读,也不需要:引擎按行自带的文法读,7.128.x 写下的无戳文件里 `toolAllow` 的 `Bash(rm:*)` 这类条目照旧什么都不放行(不放宽),升级后按引擎通告重写。停驻的 run 里引擎写下的
3128
3277
  祖先规则快照同理(本服务不改写),升级前决掉或取消待决的停驻 run。
3278
+
3279
+ ### 9.13 自建引擎镜像:两条路径(源码 tag 或 npm 包)
3280
+
3281
+ 自 7.128.0 起没有新的容器镜像发布(§0 开头的说明):要把引擎升到 ≥ 7.128.0,只能自己构建镜像。两条路径产出同一个 HTTP 服务 —— 入口都是 `main`(路径 1 跑源码 `src/main.ts`,
3282
+ 路径 2 跑包里编译好的 `dist/main.js`,装配次序同一张表)—— 按手里有什么访问权选:
3283
+
3284
+ - **路径 1 · 源码 tag**(有源码仓访问权):在 tag `v<版本>` 的检出上用仓根 `Dockerfile` 构建。基镜像 `oven/bun:1-slim`,Bun 直跑 TypeScript,没有构建步骤;入口 `bun run src/main.ts`(工作目录 `/app`)。
3285
+ - **路径 2 · npm 包**(只有 npm 访问权;包是公开的,匿名可装):`npm install @sema-agent/server@<版本>`。基镜像 `node:24-slim`(包的 `engines` 是 `^20.19.0 || >=22.12.0`);
3286
+ 入口 `node node_modules/@sema-agent/server/dist/main.js`。包的 `bin` 里没有 HTTP 服务(`run-local` 是单机一次性 runner、`sema-up` 是部署引导脚本),要起服务必须直接跑这个 `dist/main.js`。
3287
+
3288
+ **构建(二选一)**
3289
+
3290
+ 路径 1:
3291
+
3292
+ ```bash
3293
+ # 1. 取 tag 的检出
3294
+ git clone --depth 1 --branch v<版本> <你有权访问的源码仓地址> sema-server && cd sema-server
3295
+ # 2. 构建 —— 到不了 Docker Hub / npmjs 时用两只 build-arg 换源(下面是国内写法)
3296
+ docker build \
3297
+ --build-arg BUN_IMAGE=docker.m.daocloud.io/oven/bun:1-slim \
3298
+ --build-arg NPM_DEFAULT_REGISTRY=https://registry.npmmirror.com \
3299
+ -t sema-server:<版本> .
3300
+ ```
3301
+
3302
+ 两只 build-arg 的缺省:`BUN_IMAGE=oven/bun:1-slim`、`NPM_DEFAULT_REGISTRY=https://registry.npmmirror.com`;海外构建把后者换成 `https://registry.npmjs.org`。
3303
+ 还有第三只 `PKG_SOURCE`:缺省 `cn` 把最终阶段的 apt 源改写到 aliyun 来装 `git`(leader 把合并结果推回远端要用),官方源直连传 `--build-arg PKG_SOURCE=global`。
3304
+
3305
+ 路径 2:先写 `Dockerfile`(构建上下文里不需要别的文件)——
3306
+
3307
+ ```dockerfile
3308
+ ARG NODE_IMAGE=node:24-slim
3309
+ FROM ${NODE_IMAGE}
3310
+ ARG SEMA_SERVER_VERSION
3311
+ ARG NPM_DEFAULT_REGISTRY=https://registry.npmmirror.com
3312
+ WORKDIR /app
3313
+ # leader 把合并结果推回远端要用 git;slim 基镜像不带。不用 leader 车道可删这一行。
3314
+ RUN apt-get update && apt-get install -y --no-install-recommends git ca-certificates && rm -rf /var/lib/apt/lists/*
3315
+ RUN test -n "$SEMA_SERVER_VERSION" \
3316
+ && printf '{"name":"sema-engine-image","private":true}\n' > package.json \
3317
+ && npm install --omit=dev --save-exact --no-audit --no-fund --registry "$NPM_DEFAULT_REGISTRY" "@sema-agent/server@${SEMA_SERVER_VERSION}"
3318
+ EXPOSE 8090
3319
+ HEALTHCHECK --interval=30s --timeout=3s CMD node -e "fetch('http://127.0.0.1:'+(process.env.PORT||8090)+'/readyz').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
3320
+ CMD ["node", "node_modules/@sema-agent/server/dist/main.js"]
3321
+ ```
3322
+
3323
+ ```bash
3324
+ # 1–2. 构建 —— 换源同路径 1:`--build-arg NODE_IMAGE=docker.m.daocloud.io/library/node:24-slim`;海外或镜像源没有该版本时 `--build-arg NPM_DEFAULT_REGISTRY=https://registry.npmjs.org`
3325
+ docker build --build-arg SEMA_SERVER_VERSION=<版本> -t sema-server:<版本> .
3326
+ ```
3327
+
3328
+ `HEALTHCHECK` 指 `/readyz`:它的 unhealthy 只表示「别往我这儿路由」,不是「重启我」(§0 探针一段)。
3329
+ 包本身不带 lockfile,传递依赖按构建那天的 semver 解析;要同一版本两次构建得到同一棵依赖树:在本机用同样的 `npm install` 命令生成 `package.json` 与 `package-lock.json` 一并提交,
3330
+ `Dockerfile` 里改 `COPY package.json package-lock.json ./` 再 `RUN npm ci --omit=dev`。
3331
+
3332
+ **之后(两条路径相同)**
3333
+
3334
+ ```bash
3335
+ # 3. 让节点拿到镜像 —— 单机 docker 不用这一步。k3s:在节点上导入(每个会调度它的节点各导一次)
3336
+ docker save sema-server:<版本> | k3s ctr -n k8s.io images import -
3337
+ # 或推到你自己的 registry:docker tag sema-server:<版本> <你的 registry>/sema-server:<版本> && docker push <你的 registry>/sema-server:<版本>
3338
+ # 4. 起服 —— 数据根挂持久卷(AGENT_DATA_DIR 指到卷上)。下面是不带模型目录的最小形:缺 MODEL_ID 或 MODEL_GATEWAY_BASEURL 起服即拒,拒句点名缺的键
3339
+ docker run -d --name sema-server -p 8090:8090 \
3340
+ -e DB_BACKEND=local -e AGENT_DATA_DIR=/data -v sema-data:/data \
3341
+ -e MODEL_GATEWAY_BASEURL=https://<your-gateway>/v1 -e MODEL_ID=<your-model-name> -e MODEL_API_KEY=… \
3342
+ -e SERVICE_AUTH_TOKEN=<随机长串> \
3343
+ sema-server:<版本>
3344
+ # SERVICE_AUTH_TOKEN 不是起服必需,但没有它(且没开 ALLOW_UNAUTHED_WRITES)提交任务一律 503 auth.service_token_required —— 第 5 步只验就绪,交任务前先配它。另一形是显式 ALLOW_UNAUTHED_WRITES=1:那时没有 service 令牌的 worker 只绑 127.0.0.1(自收窄),容器里还要 BIND_HOST=0.0.0.0 才能经 -p 访问,并把第 4 步的映射改成 `-p 127.0.0.1:8090:8090`(否则无鉴权的提交口暴露给整个局域网)—— 两形的取舍见 §0「监听绑址」段。
3345
+ # 5. 验收 —— 就绪 + 版本
3346
+ curl -fsS http://127.0.0.1:8090/readyz # {"ready":true}
3347
+ curl -s http://127.0.0.1:8090/health # "version" 应等于你构建的 <版本>
3348
+ ```
3349
+
3350
+ - **随包 `deploy/sema-up`**:图表 `server.image` 必填,填你自建的引用;节点本地导入的(第 3 步 k3s)`--set server.image=sema-server:<版本> --set server.pullPolicy=Never`,推了 registry 的填完整引用并保持缺省 `IfNotPresent`。图表不覆写镜像的启动命令,两条路径的镜像都按它自己的 `CMD` 起。`sema-up` 脚本自己的入口:`full-cluster --kube=machines`(`-f machines.yaml`,≥ 3 台)在环境里设 `SEMA_SERVER_IMAGE_TAR=<docker save 出的 tar 或 .tar.gz>`,脚本逐台灌入并 `k3s ctr images import`,部署的镜像名是固定的 `docker.io/library/sema-server:local`、拉取策略 `Never` —— **所以 tar 里的镜像必须带 `sema-server:local` 这个标签**:`docker tag sema-server:<版本> sema-server:local && docker save sema-server:local -o sema-server.tar`(只存 `sema-server:<版本>` 的 tar 导入成功,但部署找不到 `:local`);或设 `server_image:` / `SEMA_SERVER_IMAGE=<你的 registry 引用>`,二选一;含 server 的 compose 档(`full-single` / `local-plus`)与 `full-cluster --kube=existing|k3s` 要的是 `SEMA_SERVER_IMAGE`。⚠️ 缺镜像时 `sema-up` 打印的提示与 `machines.example.yaml` 的注释仍写着 ghcr 引用 —— 那是曾发布的镜像,7.128.0 之后没有新版;照本节自建,把你的引用填进去。
3351
+ - **数据根**:第 4 步 `DB_BACKEND=local` 是单容器形(数据目录禁跨容器共享)。生产用 `DB_BACKEND=mysql|pg` 与 §0「生产」那组坐标替换它 —— **数据根卷仍要挂**:`<数据根>/sealed-key/` 在 SQL 后端下也不进库,跨重启要持久。不设 `AGENT_DATA_DIR` 时数据根落在容器用户家目录下的 `.ai-agent`,容器一删就没。
3352
+ - **升级 = 换一只更高版本的镜像**:升级序、先升库再升副本、排空与回滚前置,按你跨过的每一版读 `MIGRATION.md` 与源码仓文档 DEPLOY-PREREQS 的同版节(不随包);一次性迁移与新表在起服时自跑,不用手跑。
3353
+
3354
+ **与曾发布镜像的差异**
3355
+
3356
+ - **标签 / 引用**:曾发布的镜像是 `ghcr.io/sema-agent/sema-server:v<版本>`(另有 `:<提交>` 与 `:latest`),停在 7.128.0 之前;自建的引用你自己定,编排里写旧引用的地方(compose、图表 `server.image`、部署清单)要同步换成你的。
3357
+ - **入口**:路径 1 与曾发布镜像同(同一份 `Dockerfile`:`bun run src/main.ts`、端口 8090、`/readyz` 的 `HEALTHCHECK`);路径 2 跑 Node,入口与文件布局不同,编排里若覆写了启动命令成 `bun run src/main.ts`,要改成 node 入口。
3358
+ - **用户**:两条路径的 `Dockerfile` 都没有 `USER` 行,两只基镜像(`oven/bun:1-slim`、`node:24-slim`)的镜像配置里也没设 `User`,容器内以 root 运行(与曾发布镜像相同)。要降权:自己加 `USER`,并让数据根卷对那个 uid 可写。
3359
+
3360
+ **arm64 与可选原生件**
3361
+
3362
+ - 两只基镜像的清单都含 linux/amd64 与 linux/arm64(`oven/bun:1-slim`、`node:24-slim`);构建机是 arm64 就得 arm64 镜像,跨架构构建用 `docker buildx build --platform`。
3363
+ - `sharp`(图片降采样)是可选依赖,按平台装预编译平台包:linux/arm64 glibc 上是 `@img/sharp-linux-arm64`(两条路径的依赖解析在 arm64 目标上都解出了它);Alpine 一类 musl 基镜像要的是 `linuxmusl` 平台包,本节的 Debian 系 `slim` 不涉及。
3364
+ 它不是起服条件:引擎用到图片降采样时才动态加载它,装不上就没有降采样。
3365
+ - `slim` 基镜像没有编译器:`ssh2` / `cpu-features` 的可选原生件编译失败不让安装失败,起服也不依赖它们(没有这两件原生件的依赖树能起服到 `/readyz` 200)。