@sema-agent/server 7.146.0 → 7.147.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 (398) hide show
  1. package/MIGRATION.md +384 -0
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/USAGE.md +170 -66
  5. package/deploy/facts/lanes.json +7 -0
  6. package/deploy/sema-up/README.md +2 -2
  7. package/deploy/sema-up/chart/templates/_helpers.tpl +22 -0
  8. package/deploy/sema-up/chart/templates/server-v4pro.yaml +5 -4
  9. package/deploy/sema-up/chart/templates/server.yaml +5 -4
  10. package/deploy/sema-up/chart/values.yaml +1 -1
  11. package/deploy/sema-up/cluster-up.sh +2 -1
  12. package/deploy/sema-up/lib/flagshape.sh +4 -0
  13. package/deploy/sema-up/lib/lanes.json +2 -5
  14. package/deploy/sema-up/sema-up.sh +12 -9
  15. package/deploy/sema-up/stack/compose.yaml +5 -4
  16. package/deploy/sema-up/stack/sema-stack-cluster.yaml +4 -3
  17. package/dist/approval-card.d.ts +4 -4
  18. package/dist/approval.d.ts +3 -3
  19. package/dist/audit.js +2 -2
  20. package/dist/bench/s1/live-deps.js +3 -1
  21. package/dist/boot/crash-last.js +2 -1
  22. package/dist/boot/device-lane.js +5 -5
  23. package/dist/boot/engine-lease.d.ts +12 -11
  24. package/dist/boot/execution-env.d.ts +2 -1
  25. package/dist/boot/execution-env.js +45 -33
  26. package/dist/boot/execution-lane.d.ts +37 -9
  27. package/dist/boot/execution-lane.js +74 -20
  28. package/dist/boot/exit-boundary.d.ts +1 -1
  29. package/dist/boot/exit-last.js +2 -1
  30. package/dist/boot/governance-seams.d.ts +2 -2
  31. package/dist/boot/leader.js +1 -1
  32. package/dist/boot/memory-boundary.d.ts +11 -11
  33. package/dist/boot/memory-boundary.js +3 -3
  34. package/dist/boot/memory-consolidation.d.ts +13 -24
  35. package/dist/boot/memory-consolidation.js +0 -13
  36. package/dist/boot/org-memory.js +3 -2
  37. package/dist/boot/parked-revive-gate.d.ts +1 -1
  38. package/dist/boot/parked-revive-gate.js +1 -1
  39. package/dist/boot/reapers.js +1 -1
  40. package/dist/boot/resolve-spec.js +91 -111
  41. package/dist/boot/retention-lane.d.ts +4 -42
  42. package/dist/boot/session-faces.d.ts +1 -0
  43. package/dist/boot/session-faces.js +8 -2
  44. package/dist/boot/stage-01-config.d.ts +1 -1
  45. package/dist/boot/stage-01-config.js +6 -2
  46. package/dist/boot/stage-02-web-admin-pull.d.ts +1 -1
  47. package/dist/boot/stage-02-web-admin-pull.js +10 -8
  48. package/dist/boot/stage-03-store-backend.d.ts +1 -1
  49. package/dist/boot/stage-04-stores.d.ts +1 -1
  50. package/dist/boot/stage-05-execution-env.d.ts +2 -2
  51. package/dist/boot/stage-05-execution-env.js +3 -3
  52. package/dist/boot/stage-06-runners.d.ts +3 -3
  53. package/dist/boot/stage-06-runners.js +1 -3
  54. package/dist/boot/stage-07-capability-layer.d.ts +3 -3
  55. package/dist/boot/stage-07-capability-layer.js +4 -9
  56. package/dist/boot/stage-08-reapers.d.ts +3 -3
  57. package/dist/boot/stage-09-leader.d.ts +4 -3
  58. package/dist/boot/stage-10-http-server.d.ts +4 -3
  59. package/dist/boot/stage-10-http-server.js +2 -1
  60. package/dist/boot/stores.d.ts +9 -0
  61. package/dist/boot/stores.js +18 -7
  62. package/dist/boot/web-admin.js +3 -3
  63. package/dist/capabilities/center-plugins.d.ts +2 -0
  64. package/dist/capabilities/center-plugins.js +6 -2
  65. package/dist/capabilities/composite-members.d.ts +1 -0
  66. package/dist/capabilities/composite-members.js +1 -0
  67. package/dist/capabilities/execution-lane.d.ts +7 -7
  68. package/dist/capabilities/execution-lane.js +2 -2
  69. package/dist/capabilities/hands-lane.d.ts +1 -1
  70. package/dist/capabilities/scenarios.d.ts +32 -18
  71. package/dist/capabilities/scenarios.js +13 -9
  72. package/dist/capabilities/settings-contract.d.ts +29 -0
  73. package/dist/capabilities/settings-contract.js +14 -0
  74. package/dist/capabilities/skills.d.ts +5 -5
  75. package/dist/capability-facts.d.ts +38 -0
  76. package/dist/capability-facts.js +34 -1
  77. package/dist/center-credential-renewer.js +3 -2
  78. package/dist/config-catalog.js +6 -5
  79. package/dist/config-invariants.d.ts +18 -3
  80. package/dist/config-invariants.js +36 -19
  81. package/dist/config-lkg.d.ts +1 -1
  82. package/dist/config-lkg.js +5 -6
  83. package/dist/config-provider.js +2 -0
  84. package/dist/config-types.d.ts +190 -186
  85. package/dist/config.d.ts +68 -5
  86. package/dist/config.js +80 -54
  87. package/dist/degenerate-instrument.js +1 -1
  88. package/dist/deploy-facts.js +3 -2
  89. package/dist/deployment-shape.d.ts +21 -12
  90. package/dist/deployment-shape.js +24 -6
  91. package/dist/env-facts.d.ts +7 -5
  92. package/dist/env-facts.js +10 -6
  93. package/dist/execution-config-root.d.ts +5 -5
  94. package/dist/execution-config-root.js +5 -5
  95. package/dist/execution-env-purpose.d.ts +14 -0
  96. package/dist/execution-env-purpose.js +29 -0
  97. package/dist/execution-lane-caps.d.ts +49 -86
  98. package/dist/execution-lane-caps.js +52 -62
  99. package/dist/execution-placement.d.ts +18 -2
  100. package/dist/execution-placement.js +19 -0
  101. package/dist/file-history-disclosure.d.ts +2 -2
  102. package/dist/fleet/drain-entry.d.ts +17 -10
  103. package/dist/fleet/drain-entry.js +1 -1
  104. package/dist/fleet/serving-lease.js +3 -4
  105. package/dist/history-anchor.d.ts +22 -0
  106. package/dist/history-anchor.js +14 -0
  107. package/dist/hooks/command-spawn.d.ts +1 -4
  108. package/dist/hooks/command-spawn.js +0 -11
  109. package/dist/hooks/hook-runner.d.ts +52 -8
  110. package/dist/hooks/hook-runner.js +160 -36
  111. package/dist/http/approval-sweeps.d.ts +2 -5
  112. package/dist/http/approval-sweeps.js +3 -2
  113. package/dist/http/checkpoint-ctx.d.ts +17 -0
  114. package/dist/http/checkpoint-ctx.js +6 -0
  115. package/dist/http/claim-latency.d.ts +10 -0
  116. package/dist/http/claim-latency.js +12 -0
  117. package/dist/http/dispatch.js +1 -1
  118. package/dist/http/durable-run-leg.js +2 -1
  119. package/dist/http/execution-context.d.ts +3 -1
  120. package/dist/http/execution-context.js +4 -0
  121. package/dist/http/memory-compliance-failure-reply.d.ts +1 -1
  122. package/dist/http/memory-compliance-failure-reply.js +3 -0
  123. package/dist/http/memory-store-refusal-reply.d.ts +3 -3
  124. package/dist/http/memory-store-refusal-reply.js +3 -0
  125. package/dist/http/owner-access.d.ts +11 -0
  126. package/dist/http/owner-access.js +7 -4
  127. package/dist/http/principal-gate.d.ts +3 -14
  128. package/dist/http/principal-gate.js +0 -13
  129. package/dist/http/request-guards.d.ts +1 -1
  130. package/dist/http/resume-legs.d.ts +7 -10
  131. package/dist/http/resume-legs.js +79 -50
  132. package/dist/http/rewind-files-reply.d.ts +34 -0
  133. package/dist/http/rewind-files-reply.js +36 -0
  134. package/dist/http/route-ctx.d.ts +32 -16
  135. package/dist/http/route-table.js +2 -0
  136. package/dist/http/routes/approvals-assistant.js +5 -25
  137. package/dist/http/routes/capabilities.js +8 -2
  138. package/dist/http/routes/devices.js +1 -1
  139. package/dist/http/routes/memory-origin.d.ts +1 -1
  140. package/dist/http/routes/notify-wake.js +2 -1
  141. package/dist/http/routes/projects.d.ts +70 -0
  142. package/dist/http/routes/projects.js +86 -0
  143. package/dist/http/routes/rules.js +30 -2
  144. package/dist/http/routes/runs.js +8 -8
  145. package/dist/http/routes/session-sync.js +27 -10
  146. package/dist/http/routes/sessions.d.ts +7 -1
  147. package/dist/http/routes/sessions.js +104 -2
  148. package/dist/http/routes/tasks.js +9 -3
  149. package/dist/http/server.d.ts +24 -10
  150. package/dist/http/server.js +17 -6
  151. package/dist/http/stored-body-replay.d.ts +5 -3
  152. package/dist/http/stored-body-replay.js +10 -1
  153. package/dist/http/wire-types.d.ts +19 -10
  154. package/dist/index.d.ts +2 -2
  155. package/dist/index.js +1 -1
  156. package/dist/instruction-source-notice.d.ts +1 -1
  157. package/dist/memory-embedder-reembed.d.ts +38 -0
  158. package/dist/memory-embedder-reembed.js +20 -0
  159. package/dist/memory-incident-sink.d.ts +0 -28
  160. package/dist/memory-incident-sink.js +5 -0
  161. package/dist/memory-scope.d.ts +2 -2
  162. package/dist/memory-scope.js +1 -1
  163. package/dist/memory-store-refusal.d.ts +17 -4
  164. package/dist/memory-store-refusal.js +1 -1
  165. package/dist/memory-sync-client.d.ts +2 -0
  166. package/dist/memory-sync-client.js +13 -11
  167. package/dist/memory-sync.js +1 -1
  168. package/dist/model-compat.d.ts +35 -21
  169. package/dist/model-compat.js +49 -21
  170. package/dist/model-entry-refusal.d.ts +11 -1
  171. package/dist/model-entry-refusal.js +7 -0
  172. package/dist/namespaced-rule-name.d.ts +18 -1
  173. package/dist/namespaced-rule-name.js +12 -0
  174. package/dist/observability/fail-open.d.ts +17 -9
  175. package/dist/observability/fail-open.js +17 -9
  176. package/dist/observability/metrics.d.ts +2 -0
  177. package/dist/observability/metrics.js +2 -0
  178. package/dist/observability/secret-env-scrub.d.ts +30 -4
  179. package/dist/observability/secret-env-scrub.js +15 -3
  180. package/dist/orchestration/workflow-agent-session-index.d.ts +7 -12
  181. package/dist/orchestration/workflow-agent-session-index.js +1 -6
  182. package/dist/orchestration/workflow-completion-inbox.d.ts +26 -6
  183. package/dist/orchestration/workflow-completion-inbox.js +63 -45
  184. package/dist/org-memory-admission.d.ts +5 -2
  185. package/dist/org-memory-admission.js +25 -10
  186. package/dist/parked-decide.d.ts +33 -26
  187. package/dist/parked-decide.js +36 -44
  188. package/dist/plugins/approval-ask-store-file.d.ts +10 -2
  189. package/dist/plugins/approval-ask-store-file.js +37 -4
  190. package/dist/plugins/approval-ask-store-memory.d.ts +5 -3
  191. package/dist/plugins/approval-ask-store-memory.js +11 -2
  192. package/dist/plugins/approval-ask-store-sql.d.ts +3 -0
  193. package/dist/plugins/approval-ask-store-sql.js +3 -1
  194. package/dist/plugins/blob-backend.d.ts +38 -195
  195. package/dist/plugins/blob-backend.js +33 -229
  196. package/dist/plugins/caching-session-store.d.ts +9 -4
  197. package/dist/plugins/checkpoint-index-columns.d.ts +1 -1
  198. package/dist/plugins/checkpoint-index-columns.js +3 -2
  199. package/dist/plugins/checkpoint-store-face.d.ts +1 -1
  200. package/dist/plugins/device-store-sql.d.ts +2 -2
  201. package/dist/plugins/device-store-sql.js +5 -10
  202. package/dist/plugins/execution-lane-rename-m40.d.ts +20 -0
  203. package/dist/plugins/execution-lane-rename-m40.js +10 -0
  204. package/dist/plugins/execution-placement-sql.d.ts +63 -88
  205. package/dist/plugins/execution-placement-sql.js +94 -54
  206. package/dist/plugins/file-run-store.d.ts +26 -12
  207. package/dist/plugins/file-run-store.js +63 -63
  208. package/dist/plugins/imported-layer-root-retirement.d.ts +17 -8
  209. package/dist/plugins/imported-layer-root-retirement.js +4 -8
  210. package/dist/plugins/inbox-payload-column.d.ts +14 -0
  211. package/dist/plugins/inbox-payload-column.js +13 -0
  212. package/dist/plugins/local-checkpoint-store.d.ts +5 -0
  213. package/dist/plugins/local-checkpoint-store.js +11 -7
  214. package/dist/plugins/local-data-dir-modes-m39.d.ts +48 -0
  215. package/dist/plugins/local-data-dir-modes-m39.js +182 -0
  216. package/dist/plugins/local-engine-high-water.d.ts +11 -0
  217. package/dist/plugins/local-engine-high-water.js +59 -0
  218. package/dist/plugins/local-session-store.d.ts +91 -25
  219. package/dist/plugins/local-session-store.js +124 -45
  220. package/dist/plugins/local-task-attachment-store.d.ts +2 -0
  221. package/dist/plugins/local-task-attachment-store.js +10 -11
  222. package/dist/plugins/memory-embedder-fingerprint.d.ts +33 -10
  223. package/dist/plugins/memory-embedder-fingerprint.js +52 -12
  224. package/dist/plugins/memory-embedder.d.ts +1 -1
  225. package/dist/plugins/memory-embedder.js +2 -2
  226. package/dist/plugins/memory-engine-pg.d.ts +4 -3
  227. package/dist/plugins/memory-engine-pg.js +4 -4
  228. package/dist/plugins/memory-engine-sql-apply.d.ts +13 -4
  229. package/dist/plugins/memory-engine-sql-apply.js +1 -1
  230. package/dist/plugins/memory-engine-tidb.d.ts +8 -5
  231. package/dist/plugins/memory-engine-tidb.js +7 -8
  232. package/dist/plugins/one-time-migrations.d.ts +145 -35
  233. package/dist/plugins/one-time-migrations.js +272 -120
  234. package/dist/plugins/owner-only-file-mode.d.ts +29 -0
  235. package/dist/plugins/owner-only-file-mode.js +141 -0
  236. package/dist/plugins/permission-rule-store-file.d.ts +8 -8
  237. package/dist/plugins/permission-rule-store-file.js +14 -2
  238. package/dist/plugins/pg-pool.d.ts +2 -1
  239. package/dist/plugins/pg-pool.js +11 -4
  240. package/dist/plugins/release-version.d.ts +20 -0
  241. package/dist/plugins/release-version.js +28 -0
  242. package/dist/plugins/remote-env-adb.d.ts +20 -4
  243. package/dist/plugins/remote-env-adb.js +39 -11
  244. package/dist/plugins/remote-env-e2b.d.ts +12 -3
  245. package/dist/plugins/remote-env-e2b.js +6 -4
  246. package/dist/plugins/remote-env-host.d.ts +9 -1
  247. package/dist/plugins/remote-env-host.js +6 -5
  248. package/dist/plugins/remote-env-k8s.d.ts +5 -2
  249. package/dist/plugins/remote-env-k8s.js +15 -4
  250. package/dist/plugins/remote-env-local-docker.d.ts +4 -1
  251. package/dist/plugins/remote-env-local-docker.js +3 -3
  252. package/dist/plugins/remote-env-ssh.d.ts +8 -2
  253. package/dist/plugins/remote-env-ssh.js +26 -24
  254. package/dist/plugins/remote-shell.d.ts +59 -30
  255. package/dist/plugins/remote-shell.js +175 -62
  256. package/dist/plugins/retention-declaration.d.ts +37 -0
  257. package/dist/plugins/retention-declaration.js +9 -0
  258. package/dist/plugins/retention-deployment.d.ts +126 -0
  259. package/dist/plugins/retention-deployment.js +88 -0
  260. package/dist/plugins/retention-lane-store-sql.d.ts +74 -4
  261. package/dist/plugins/retention-lane-store-sql.js +103 -1
  262. package/dist/plugins/retention-sql.d.ts +29 -0
  263. package/dist/plugins/retention-sql.js +8 -0
  264. package/dist/plugins/schema-consistency.d.ts +0 -6
  265. package/dist/plugins/schema-consistency.js +1 -2
  266. package/dist/plugins/schema-ensure-legs.d.ts +1 -1
  267. package/dist/plugins/schema-ensure-legs.js +11 -1
  268. package/dist/plugins/session-ext-table.d.ts +74 -0
  269. package/dist/plugins/session-ext-table.js +121 -0
  270. package/dist/plugins/session-fork.d.ts +1 -1
  271. package/dist/plugins/session-row-sql.d.ts +8 -7
  272. package/dist/plugins/session-row-sql.js +15 -7
  273. package/dist/plugins/session-store-sql.d.ts +131 -0
  274. package/dist/plugins/session-store-sql.js +298 -0
  275. package/dist/plugins/session-workspace-cell.d.ts +2 -2
  276. package/dist/plugins/session-workspace-cell.js +2 -2
  277. package/dist/plugins/shared-memory-store-sql.d.ts +10 -0
  278. package/dist/plugins/shared-memory-store-sql.js +13 -13
  279. package/dist/plugins/snapshot-bucket-presence.js +1 -1
  280. package/dist/plugins/sql-driver.d.ts +23 -7
  281. package/dist/plugins/sql-driver.js +41 -8
  282. package/dist/plugins/sql-errors.d.ts +5 -2
  283. package/dist/plugins/sql-errors.js +4 -0
  284. package/dist/plugins/store-backend.d.ts +35 -8
  285. package/dist/plugins/store-backend.js +184 -62
  286. package/dist/plugins/store-contracts.d.ts +9 -2
  287. package/dist/plugins/store-contracts.js +1 -0
  288. package/dist/plugins/task-attachment-store.d.ts +6 -8
  289. package/dist/plugins/task-attachment-store.js +15 -10
  290. package/dist/plugins/task-list-store-sql.d.ts +1 -1
  291. package/dist/plugins/text-column-capacity.d.ts +21 -0
  292. package/dist/plugins/text-column-capacity.js +5 -0
  293. package/dist/plugins/tidb-pool.d.ts +3 -2
  294. package/dist/plugins/tidb-pool.js +19 -31
  295. package/dist/plugins/user-input-ledger-sql.d.ts +1 -1
  296. package/dist/read-face-posture.d.ts +6 -6
  297. package/dist/read-face-posture.js +2 -2
  298. package/dist/request-value-closure.d.ts +5 -5
  299. package/dist/request-value-closure.js +1 -1
  300. package/dist/rules-consent.d.ts +41 -13
  301. package/dist/rules-consent.js +101 -53
  302. package/dist/run-local.js +17 -13
  303. package/dist/runs.d.ts +48 -30
  304. package/dist/runs.js +26 -16
  305. package/dist/runtime-caps-resolver.d.ts +15 -8
  306. package/dist/runtime-caps-resolver.js +14 -10
  307. package/dist/sealed-key-layout.d.ts +2 -3
  308. package/dist/sealed-key-layout.js +0 -1
  309. package/dist/sealed-key.js +7 -5
  310. package/dist/security.d.ts +5 -3
  311. package/dist/sema-config-dir.js +2 -2
  312. package/dist/server-secret-env.d.ts +2 -0
  313. package/dist/service-version.d.ts +2 -1
  314. package/dist/service-version.js +2 -2
  315. package/dist/session-error-wire.d.ts +1 -1
  316. package/dist/session-incarnation.d.ts +8 -5
  317. package/dist/session-resident-background.d.ts +85 -32
  318. package/dist/session-resident-background.js +75 -7
  319. package/dist/session-row.d.ts +1 -1
  320. package/dist/session-sync-kernel.d.ts +1 -1
  321. package/dist/session-workspace.d.ts +11 -2
  322. package/dist/session-workspace.js +5 -0
  323. package/dist/sighup-idle.d.ts +1 -1
  324. package/dist/store-posture.d.ts +2 -1
  325. package/dist/stored-request-hooks-m20.d.ts +3 -2
  326. package/dist/stored-request-hooks-m20.js +19 -14
  327. package/dist/stores-sql/blob-index-sql.d.ts +148 -0
  328. package/dist/stores-sql/blob-index-sql.js +154 -0
  329. package/dist/stores-sql/checkpoint-store-sql.d.ts +1 -1
  330. package/dist/stores-sql/checkpoint-store-sql.js +11 -27
  331. package/dist/{plugins → stores-sql}/file-history-store-sql.d.ts +23 -11
  332. package/dist/{plugins → stores-sql}/file-history-store-sql.js +23 -18
  333. package/dist/{plugins → stores-sql}/pg-session-storage.d.ts +23 -76
  334. package/dist/{plugins → stores-sql}/pg-session-storage.js +80 -209
  335. package/dist/{plugins → stores-sql}/retention-store-sql.d.ts +123 -217
  336. package/dist/{plugins → stores-sql}/retention-store-sql.js +66 -190
  337. package/dist/{plugins → stores-sql}/session-event-payload.d.ts +1 -1
  338. package/dist/{plugins → stores-sql}/session-event-payload.js +1 -1
  339. package/dist/stores-sql/session-policy-store-sql.d.ts +1 -1
  340. package/dist/stores-sql/session-write-hooks.d.ts +59 -0
  341. package/dist/stores-sql/session-write-hooks.js +2 -0
  342. package/dist/{plugins → stores-sql}/tidb-session-storage.d.ts +10 -11
  343. package/dist/{plugins → stores-sql}/tidb-session-storage.js +10 -21
  344. package/dist/{plugins → stores-sql}/tidb-session-store.d.ts +32 -122
  345. package/dist/{plugins → stores-sql}/tidb-session-store.js +71 -171
  346. package/dist/stores-sql/tool-result-store-sql.d.ts +1 -1
  347. package/dist/stores-sql/tool-result-store-sql.js +1 -1
  348. package/dist/stores-sql/windowed-session-storage.d.ts +56 -0
  349. package/dist/stores-sql/windowed-session-storage.js +61 -0
  350. package/dist/task-a2a.d.ts +7 -1
  351. package/dist/task-a2a.js +15 -14
  352. package/dist/task-cwd.d.ts +11 -3
  353. package/dist/task-cwd.js +17 -1
  354. package/dist/task-mcp.d.ts +10 -2
  355. package/dist/task-mcp.js +13 -16
  356. package/dist/task-settings.d.ts +9 -4
  357. package/dist/task-settings.js +17 -6
  358. package/dist/tool-approval.d.ts +21 -8
  359. package/dist/tool-approval.js +10 -6
  360. package/dist/trace/core-keyset-guard.d.ts +9 -3
  361. package/dist/trace/engine-notice-wire.d.ts +1 -1
  362. package/dist/trace/engine-notice-wire.js +2 -0
  363. package/dist/trace/exit-seat.d.ts +23 -1
  364. package/dist/trace/exit-seat.js +3 -0
  365. package/dist/trace/project.d.ts +33 -16
  366. package/dist/trace/project.js +12 -9
  367. package/dist/trace/redact.d.ts +1 -1
  368. package/dist/trace/redact.js +61 -73
  369. package/dist/trace/self-minted-wire.d.ts +263 -10
  370. package/dist/trace/self-minted-wire.js +90 -2
  371. package/dist/trace/sema-provenance.d.ts +45 -41
  372. package/dist/trace/sema-provenance.js +81 -10
  373. package/dist/trace/task-notification-facets.d.ts +70 -2
  374. package/dist/trace/task-notification-facets.js +71 -1
  375. package/dist/trace/wire-projection-faces.d.ts +38 -2
  376. package/dist/trace/wire-projection-faces.js +26 -1
  377. package/dist/unread-request-members.d.ts +66 -0
  378. package/dist/unread-request-members.js +41 -0
  379. package/dist/user-input-ledger.d.ts +2 -2
  380. package/dist/web-admin-client/apply-effective.js +22 -26
  381. package/dist/web-admin-client/candidate-gate.d.ts +35 -3
  382. package/dist/web-admin-client/candidate-gate.js +53 -5
  383. package/dist/web-admin-client/center-request.d.ts +4 -1
  384. package/dist/web-admin-client/center-request.js +2 -1
  385. package/dist/web-admin-client/effective-keys.js +0 -1
  386. package/dist/web-admin-client/facade.d.ts +1 -1
  387. package/dist/web-admin-client/http-client.d.ts +1 -0
  388. package/dist/web-admin-client/http-client.js +17 -9
  389. package/dist/web-admin-client/restart-signal.js +5 -2
  390. package/dist/web-admin-client/skills-mcp.d.ts +20 -6
  391. package/dist/web-admin-client/skills-mcp.js +26 -21
  392. package/dist/web-admin-client/types.d.ts +34 -49
  393. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +2 -2
  394. package/package.json +13 -12
  395. package/scripts/drain-check-local.mjs +2 -28
  396. package/scripts/drain-check.mysql.sql +2 -18
  397. package/scripts/drain-check.pg.sql +2 -18
  398. package/wire-changes/server@7.147.0.json +813 -0
package/USAGE.md CHANGED
@@ -61,20 +61,21 @@ docker compose up --build --scale service=4 # 4 个无状态 worker,nginx
61
61
 
62
62
  > 🔴 7.96.0 起根 `docker-compose.yml` 的 `REMOTE_EXEC`(执行车道)是**必填**:不设 ⇒ `docker compose config` 当场失败(此前静默起成一个没有手的 worker)。单人自用 `REMOTE_EXEC=host`(手落在 worker 容器里);多人 / 生产选隔离车道 `e2b`(配 `E2B_API_KEY`)或 `k8s`(`K8S_SANDBOX_IMAGE`、`K8S_NAMESPACE`〔没有内置缺省〕都是必需件,未设、空串或只含空白一律按缺 ⇒ 起服拒;`K8S_API_URL` 与 `K8S_TOKEN` 也要设;根 compose 透传前两名〔空串透传 = 按缺,要填〕,后两名要在 compose override 里加进服务的 `environment`,或改走随包 chart `deploy/sema-up`)。
63
63
 
64
- > 🔴 **`REMOTE_EXEC` 的形是部署声明的有序车道表**(契约 `runtime.execution.lanes` 渲成 env 的形:逗号分隔、首项是缺省车道;单词 = 一条车道的表,读法与以前逐字同)。每段逐字是一个车道词(不 trim、不折大小写),不许空段、不许同一车道出现两次;写着契约退役车道值(如 `remote-docker`)⇒ 拒句给出去向。**本引擎每个进程只装一条车道的执行座**:表里不止一条(`REMOTE_EXEC=k8s,device`)⇒ 起服拒,拒句点名全部声明的车道与出路(一台引擎一条车道:`REMOTE_EXEC=k8s`,其余车道各起一台)—— 不只服务首项而让其余车道无声地什么都不跑。
64
+ > 🔴 **`REMOTE_EXEC` 的形是部署声明的有序车道表**(契约 `runtime.execution.lanes` 渲成 env 的形:逗号分隔、首项是缺省车道;单词 = 一条车道的表,读法与以前逐字同)。每段逐字是一个车道词(不 trim、不折大小写),不许空段(只含空白也算空)、不许同一车道出现两次,`none`(不给工具执行环境)只能独成一表;拒句一次列出全部问题,以契约的车道表规则句收尾;写着契约退役车道值(如 `remote-docker`)⇒ 拒句给出去向。**本引擎每个进程只装一条车道的执行座**:表里不止一条(`REMOTE_EXEC=k8s,device`)⇒ 起服拒,拒句点名全部声明的车道与出路(一台引擎一条车道:`REMOTE_EXEC=k8s`,其余车道各起一台)—— 不只服务首项而让其余车道无声地什么都不跑。
65
65
 
66
- > 🔴 **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`。车道 × 租户形:
66
+ > 🔴 **7.104.0 起多租户姿态不许跑不分租户的执行车道**:`REQUIRE_PRINCIPAL=true` ∧ 最终生效的车道在执行车道能力表上不分租户 ⇒ **拒启**。按表不按词,今天覆盖 `host` / `ssh` / `adb`(所有任务落同一个共享目标:worker 宿主 / 一台 SSH 对端 / 一台 ADB 设备)。判的是合成后的车道(env 的 `REMOTE_EXEC`、local 形 `config.d/remote-exec.json` 的声明或覆写,在中心拉取与任何配置应用之前合成;7.147.0 起两处都没有 ⇒ 拒启)。拒因(逐字,以 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`。车道 × 租户形:
67
67
  >
68
68
  > | 车道 | 单用户 | 多租户(`REQUIRE_PRINCIPAL=true`) | `READ_FACE` 缺席时的读面缺省(7.133.0 起车道行声明) |
69
69
  > |---|---|---|---|
70
- > | `host`(显式 / `CONFIG_PROVIDER=local` 隐式 / 文件覆写) | ✅ | ❌ 拒启(7.104.0 起) | 单用户 `open`(来源 `posture`) |
70
+ > | `host`(env 显式 / 文件声明或覆写) | ✅ | ❌ 拒启(7.104.0 起) | 单用户 `open`(来源 `posture`) |
71
71
  > | `ssh`(一台常驻对端) | ✅ | ❌ 拒启(7.104.0 起) | 单用户 `open`(来源 `posture`) |
72
72
  > | `adb`(一台设备) | ✅ | ❌ 拒启(7.104.0 起) | 单用户 `open`(来源 `posture`) |
73
73
  > | `e2b` / `k8s` / `local-docker`(每任务一只沙箱) | ✅ | ✅ | **`open`**(来源 **`lane`**,单用户 / 多租户同;车道行显式声明)。🔴 **挂载告诫**:把宿主目录(`hostPath` / bind mount)或共享卷挂进沙箱的部署,`open` 面上这些宿主文件对模型零问可读 —— 显式 `READ_FACE=roots`(env 恒赢)或给它们加读拒行(`READ_DENY_PATTERNS`) |
74
74
  > | `device`(每人自己的设备) | ✅ | ✅ | 单用户 `open`(来源 `posture`);多租户 不铸 = 引擎默认 `roots` |
75
- > | 未点名(云形进程内 stub,零手带工具、非手带工具照常执行) | ✅(`REQUIRE_EXECUTION_LANE=true` 可改成拒启) | ✅(同左) | 单用户 `open`(来源 `posture`);多租户 不铸 = 引擎默认 `roots` |
75
+ > | 未点名(7.147.0 起) | ❌ 拒启(拒句一行给修法;旧拒启旋钮 `REQUIRE_EXECUTION_LANE` 已退役,设了即拒启) | ❌ 拒启 | — |
76
+ > | `none`(声明「不给智能体的工具执行环境」:执行环境工厂与执行环境座都不给,零手带工具、非手带工具照常执行;`GET /v1/capabilities` 报 `none`;7.146.0 及以前「非 local 没写车道」的进程内桩改名而来) | ✅ | ✅ | 单用户 `open`(来源 `posture`);多租户 不铸 = 引擎默认 `roots` |
76
77
 
77
- > 🔴 **硬断预告(7.147.0 起)**:每个部署都要显式声明自己最终生效的执行车道 —— env `REMOTE_EXEC`(`CONFIG_PROVIDER=local` 形下 `config.d/remote-exec.json` 也算);没有声明 ⇒ 拒启(`CONFIG_PROVIDER=local` 不再隐含 `host`,非 local 未设也不再静默落进「不挂手」的进程内桩)。「不给智能体的工具提供执行环境」改写成车道词 `none`,取代词表外的状态名 `in-process`(`GET /v1/capabilities` 的 `executionLane.provider`、`GET /v1/sessions/:id/execution-placement` 的 `lane`、409 的 `boundLane`、403 `execution_lane_not_allowed` 的 `extra.lane` 换值);`REQUIRE_EXECUTION_LANE` 退役(设了任何非空值 ⇒ 拒启,拒句指向 `REMOTE_EXEC`),自己拼的清单或拷过随包部署产物的 env 里写着它的,升级前要去掉。库里或数据根里已有记着 `in-process` 的会话 / 审批行的部署(跑过未点名车道的进程内桩的),须停全部旧副本、跑一次性迁移、再起 7.147.0,不可滚动混跑;web-admin 渲染 env 的中心 worker,须先停渲 `REQUIRE_EXECUTION_LANE`,再升到 7.147.0。照旧行为的一行修法:7.146.0 里非 local 且未设车道的 ⇒ 7.147.0 写 `REMOTE_EXEC=none`,`CONFIG_PROVIDER=local` 且未设的 ⇒ 写 `REMOTE_EXEC=host`(7.146.0 及以前不认 `none` 这个词,别提前写)。7.146.0 本身零行为变化。
78
+ > 🔴 **硬断(自 7.147.0 起)**:每个部署都要显式声明自己最终生效的执行车道 —— env `REMOTE_EXEC`(`CONFIG_PROVIDER=local` 形下 `config.d/remote-exec.json` 也算);没有声明 ⇒ 拒启,拒句以 `No execution lane is declared` 起、一行给出照旧行为的修法(`CONFIG_PROVIDER=local` 不再隐含 `host`,非 local 未设也不再静默落进「不挂手」的进程内桩)。「不给智能体的工具提供执行环境」写成车道词 `none`,取代词表外的状态名 `in-process`(wire 五处换值,零别名:`GET /v1/capabilities` 的 `executionLane.provider`、`GET /v1/sessions/:id/execution-placement` 的 `lane`、409 `execution_lane.not_served` 与 409 `execution_lane.tree_bound` 的 `boundLane`、403 `execution_lane_not_allowed` 的 `extra.lane`);`REQUIRE_EXECUTION_LANE` 已退役(设了任何非空值 ⇒ 拒启,拒句指向 `REMOTE_EXEC`),自己拼的清单或拷过随包部署产物的 env 里写着它的,升级前要去掉。库里或数据根里记着 `in-process` 的会话 / 审批行由一次性迁移 M40 在起服时改成 `none`:停全部旧副本、再起 7.147.0,不可滚动混跑;SQL 后端上判活窗(`REAP_RUN_STALE_SEC`)内还有 run 在心跳 ⇒ 起服拒 `config.execution_lane_rename_refused`,拒句给出可再起的时刻;守卫只看心跳,没有在跑的 run 的空闲旧副本它看不见 —— 停全部旧副本是运维的动作(细节与回滚见 MIGRATION 7.147.0 节「执行车道恒点名」一条)。web-admin 渲染 env 的中心 worker,须先停渲 `REQUIRE_EXECUTION_LANE`、同时让每个 worker 的 env 带 `REMOTE_EXEC`,再升到 7.147.0。照旧行为的一行修法:7.146.0 里非 local 且未设车道的 ⇒ 7.147.0 写 `REMOTE_EXEC=none`,`CONFIG_PROVIDER=local` 且未设的 ⇒ 写 `REMOTE_EXEC=host`(7.146.0 及以前不认 `none` 这个词,别提前写)。
78
79
 
79
80
  > **设备 WS 未鉴权握手的 per-remote 帽(`DEVICE_MAX_PREAUTH_PER_REMOTE`,缺省 8)按真实对端计**:缺省按 socket 对端计 —— 引擎在反代 / 托管入口 / 公司 NAT 之后时,全员同一个 socket 对端,共用这 8 个名额。把**自己的**反代写进 `DEVICE_TRUSTED_PROXY_CIDRS`(CIDR,逗号分隔;缺省空 = 不读任何代理头):socket 对端落在名单内才读 `X-Forwarded-For`,取自右向左第一个不在名单内的地址作帽键;名单外的对端一律不采代理头;请求头行数到顶(100 行)/ 头缺席 / 坏形 / 全在名单内 ⇒ 退回 socket 对端 + 一行 warn `device_preauth_forwarded_unusable`。名单每项是 IP 地址或 CIDR(裸地址 = 单台);名单里的来源能改帽键,所以只放自己控制的反代;前缀 0、主机位非零、不是 IP 地址或 CIDR 的项、空项拒启。不读 `Forwarded` / `X-Real-IP`,不支持 PROXY protocol。全局帽 64 不变。这根旋钮的语义行与缺省句也由 `GET /v1/config/catalog` 报出(域 `orchestration`,安全轴)。
80
81
 
@@ -173,6 +174,7 @@ ANTHROPIC_API_KEY=sk-ant-… # 可选:ANTHROPIC_BASE_URL / ANTHROPIC_VERSION /
173
174
  - **会话自动标题与 hooks 的 `prompt` / `agent` 条目走哪条线路,和主请求读同一只判据**:`provider="anthropic"` 的模型只在 anthropic 路由装上(配了 `ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN`)时走云 `/v1/messages`;
174
175
  没装(如 `MODEL_PROVIDER=anthropic` 却只配了 `MODEL_GATEWAY_BASEURL` + `MODEL_API_KEY`)时主请求落网关,这两个面的廉价直连调用同样落网关的 openai 兼容线路(`/chat/completions`),
175
176
  端点与凭据照网关的来 —— 此前它们按 `provider` 字面去找一条没装的 anthropic 路由而整条拒发,会话列表没有自动标题。网关端点也解析不到才拒(`config.model_route_endpoint_missing`)。
177
+ - **已知限制:hook 的 LLM 出口不拦明文非回环(会话自动标题与 hooks 的 `prompt` 条目的直连调用)**:这两个面的请求由本服务自己发,发送前过引擎的路由门(凭据 ↔ 端点配对、来源绑定),但那一道门不问「经什么链路发」—— 目录条目的 `baseUrl`(没写时落到所在腿的根:网关腿是 `MODEL_GATEWAY_BASEURL`,anthropic 腿是 `ANTHROPIC_BASE_URL`)写成 `http://<非回环主机>` 且带 key,请求照带着 key 经明文链路发出,**今天不拒**。引擎的明文判官到货前由部署自审:被这两个面选中的目录条目(或它们落到的腿的根)只写 `https://` 或回环地址。判官到货后本条改为拒(届时 MIGRATION 另写升级前做)。
176
178
  - **预览与自动标题只读你自己写的那段文字**:会话列表的 `objectivePreview`、run / leader 行的 `objective_preview`、自动标题取材、收件箱 `objective` 与任务视图 `name` 读同一张闭集 ——
177
179
  引擎注入的 `<system-reminder>` / `<task-notification>` / `<attachment>` 块,以及壳铸进用户轮的**本地命令回显**(`<command-name>` / `<command-message>` / `<command-args>`、`<local-command-stdout>` / `<local-command-stderr>` / `<local-command-caveat>`、
178
180
  `<bash-input>` / `<bash-stdout>` / `<bash-stderr>` / `<bash-exit-code>`、`<user-memory-input>`)都不算你写的:`/compact` 一类本地命令之后的下一条提交,预览与标题取的是随后那句话,不再是回显;只有回显、没有你的话 ⇒ run / leader 行的预览是空串(任务名折成 `(notification turn)`),会话列表的预览回落到更早的一句、都没有则缺席(`null`)。
@@ -311,12 +313,22 @@ MODEL_DEGRADE_ON=rate_limit,breaker_open # 可选,反应式触发器子集;词
311
313
  只有 `qwen-chat-template`(`chat_template_kwargs.enable_thinking=false`)关得掉,而 `MODEL_EXTRA_BODY`
312
314
  穿不过引擎的 OpenAI 保留键表 —— 不设它,这类部署每一轮都白烧一段思考且读数上看不出来。它与
313
315
  `MODEL_REASONING_EFFORT_LEVELS` 铸进**同一个** `compat` 对象(两根旋钮同时设两键都在)。
314
- - 🔴 **逐模型声明(目录腿):`models.models[].compat`**(settings-schema ≥1.10.0;7.69.0 消费)——
315
- 同一张 `OpenAICompletionsCompat` 五键(`thinkingFormat` / `reasoningEffortLevels` / `maxTokensField` /
316
- `requiresReasoningContentOnAssistantMessages` / `supportsReasoningEffort`)可以**按模型**写在
317
- `config.d/models.json` 或配置控制面的目录条目上,逐键盖过上面两根 env 缺省(BL-8:没写的键仍继承 env)。
318
- **坏形/坏词/未知键 ⇒ 丢掉这条 `compat` 声明、模型本体保留**,并打一条 `model_compat_dropped`
316
+ - 🔴 **逐模型声明(目录腿):`models.models[].compat`**(settings-schema ≥1.10.0;7.69.0 消费;7.147.0 起按线路读)——
317
+ 键集由条目的 `api` 定:`openai-completions` 条目是 `OpenAICompletionsCompat` 五键(`thinkingFormat` / `reasoningEffortLevels` / `maxTokensField` /
318
+ `requiresReasoningContentOnAssistantMessages` / `supportsReasoningEffort`);`anthropic-messages` 条目是 `AnthropicMessagesCompat` 五键
319
+ (`thinkingMode`:`budget` | `adaptive`、`effortLevels`、`supportsTemperature` / `contextManagement` / `interleavedThinking` 三个布尔;7.146.x 及更早这一线路的 `compat` 整只丢)。
320
+ 可以**按模型**写在 `config.d/models.json` 或配置控制面的目录条目上,逐键盖过上面 env 缺省(BL-8:没写的键仍继承 env);
321
+ env 缺省只铸 openai 线路的键,只盖同一线路的条目(anthropic 条目不继承它们)。
322
+ **坏形/坏词/未知键(含写了别的线路的键)⇒ 丢掉这条 `compat` 声明、模型本体保留**,并打一条 `model_compat_dropped`
319
323
  点名 warn(引擎回落到自己的推断 = 今天的行为);一条写坏的声明**不会**让这只模型或整套目录消失。
324
+ - 🔴 **逐档思考声明(目录腿):`models.models[].thinkingLevelMap`**(settings-schema 20.0.0;7.147.0 消费)—— 这只端点每一档思考发什么值:
325
+ 键是 `off` / `minimal` / `low` / `medium` / `high` / `xhigh` / `max`,值是非空串(那一档发给端点的拼写)或 `null`(这一档端点不支持),没写的档引擎照自己的推断。
326
+ 本服务原样交给引擎(不补缺省、不另判),`GET /v1/models` 的行原样带它;openai-completions / responses 条目上引擎据它发请求、答 `supportsThinkingOff` / `supportedEffortLevels`
327
+ (`null` 档不进 `supportedEffortLevels`;`openai-completions` 条目的 `compat.thinkingFormat` 缺席或是 `openai` 时,`off` 写成非空串 ⇒ 关得掉,写 `null` 或不写 ⇒ 最低一席是 `low`;
328
+ 其余 `thinkingFormat` 关不关得掉由引擎按方言自判,不读 `off`)。⚠️ **anthropic-messages 条目上当前引擎(core 7.65.0)整个不读这张表**:原样带、零效果 ——
329
+ `null` 档照旧在选项集里、请求照发,`off: null` 不改「关得掉」;那条线上要「关不掉」写 `compat.thinkingMode: "adaptive"`,要收窄档位写 `compat.effortLevels`。
330
+ 形由契约包判:未知键 / 非对象 / 空串 / 非串非 `null` ⇒ **那一条被丢**(读腿点名);被 `roles` / `atMention.only` / `tierGroups` 引用的条目坏了 ⇒ 契约包把整个 `models` 域读成缺省,本服务据此**整份拒候选**(停在上一份好配置;本地 `config.d` 起服回落 env)。
331
+ env 腿(`AGENT_MODEL_*`)没有这一席。
320
332
  - 反应式降级 brain 包在**最外层**;fallback brain 用自己的凭据(decorator 清掉主模型的 per-call key 防外泄给别的 provider)。**7.106.0+**:
321
333
  降级目标的自有凭据按**启动那一代的身份**取值(env 变量名 / 托管密钥),与目标端点同代 —— 同名变量换值热生效;目录换 key 名字 / 重贴托管密钥 / 换端点
322
334
  要按 `/health.restart.reasons ∋ degrade-route` 重启。**坑**:`MODEL_DEGRADE_TO` 最好别和被限流的是同一网关/账号,否则反应式切过去照样撞同一个 rate_limit。
@@ -513,18 +525,22 @@ MODEL_CODE_ROLES=default,subagent # 不设=全中立;仅这些角色在「
513
525
  | `MEMORY_EMBEDDER_DIM` | 缺省 | 声明维度(**正整数**,如 `1024`)。必须与模型真实维度一致 |
514
526
  | `MEMORY_EMBEDDER_TIMEOUT_MS` | `30000`(`[1000, 300000]`) | 单次 embed 的活性上限。挂死的端点会把整条记忆写入路径吊住 |
515
527
  | `MEMORY_EMBEDDER_API_KEY` | 缺省 | 可选 Bearer(自托管 TEI/vllm/ollama 通常不需要;公有云端点需要) |
528
+ | `MEMORY_EMBEDDER_REEMBED` | 缺省 | **清列重嵌的确认口令**,值 = 要确认的那个身份 `<model>/<dimensions>`(维度在**最后一个** `/` 之后,模型名本身可带 `/`,如 `BAAI/bge-m3/1024`)。只在下条指纹门判「对不上」时起作用:在场且恰好点名本次配置的 `MODEL`/`DIM` ⇒ 清掉向量列、按这个身份重嵌一次;缺席 ⇒ 对不上就拒启、一行不清。形不对(`true` / `1` 这类开关形一律不是口令)或对不上本次配置 ⇒ 拒启;三键不全时单独在场同上面的半配拒 |
516
529
 
517
530
  ⚠️ **半配 = 拒启**:上表前三键是一个整体,少任何一件都启动报错并**点名缺哪几键**。静默忽略的后果不是「功能缺席」而是「operator 以为向量面开着」——检索照常出结果(词面档),差异只在答案质量里,零日志。同理:只配 `TIMEOUT`/`API_KEY` 而三键不全、或 `MEMORY_ENGINE=off` 却配了 embedder,都是死旋钮,一律拒启。
518
- ⚠️ **维度守卫是拒启/抛错,不是 warn**:向量列写错维度是**静默毒库**——本仓 pg 记忆表的 embedding 列是 `jsonb`(无维度约束),错长度向量既不会让 DB 报错,也不会让检索报错(那些行只是悄悄退回词面档)。所以坏 `DIM`(非正整数)启动即拒;运行期端点返回的向量长度与 `DIM` 不符,embedder **直接抛错**,绝不截断/补零。换 `MODEL`/`DIM` 的**清空由指纹门自动做**(见下条),不必手动清。
531
+ ⚠️ **维度守卫是拒启/抛错,不是 warn**:向量列写错维度是**静默毒库**——本仓 pg 记忆表的 embedding 列是 `jsonb`(无维度约束),错长度向量既不会让 DB 报错,也不会让检索报错(那些行只是悄悄退回词面档)。所以坏 `DIM`(非正整数)启动即拒;运行期端点返回的向量长度与 `DIM` 不符,embedder **直接抛错**,绝不截断/补零。换 `MODEL`/`DIM` 时**起服拒、不自动清**(指纹门,见下条):要清列重嵌须显式给确认口令 `MEMORY_EMBEDDER_REEMBED`。
519
532
  ⚠️ **embedder 出故障的方向**:端点 5xx/超时会让当次记忆**写入**报一条 `io error: …` 冲突(不写坏向量),检索腿则退回词面档继续服务——即「坏了退回 lexical」,不是「坏了写坏数据」。故障有专门信号:metric `memory_embed_failed_total{backend}` + 日志 `memory_embedder_failed`(只看 PatchReport 里的冲突会把供应商故障误读成并发改动)。
520
533
  ⚠️ **只对新写入的条目生效(没有回填腿)**:开启前就存在的记忆条目 embedding 列是空的,只有被再次写入时才会补上向量;这些行在检索里落**词面档**。🔴 **词面档不是「排名靠后」,是有门槛**:与查询**零词交集**的行被直接**丢出结果集**(词面距离在无交集时无定义,检索当场跳过该行),而带向量的行有余弦读数可用、**不会被这道门丢掉**(前提是 embedder 当时也把**查询**嵌出来了;embedder 故障那一刻查询侧拿不到向量,整轮检索退回词面档,于是零交集的行连同它们一起被丢)。所以没有向量的行对**改述 / 同义 / 跨语言**这类查询**检索不到**——那恰恰是向量档买的东西。要立刻全量生效,得自己重写一遍语料。
521
- 🔴 **换 `MODEL`/`DIM` 的行为:下次 boot 自动清空向量列(embedder 指纹门,7.15.0 之后的下一个发布起)**。本服务在 `agent_memory_engine_meta` 单行元表里记住当前 embedder 的**指纹** `{model, dimensions}`(明文,便于排障;**endpoint 不进指纹**——换域名/代理不换语义空间)。每次启动比对一次:
522
- - **相等** ⇒ 零动作、零写;
523
- - **不等 / 元表还没有这一行而库里已有向量 / 那一行读不出来**(手改、回滚残留)⇒ **先把 `agent_memory_engine_entry.embedding` 整列清成 NULL,再写新指纹**(次序不可换:反过来若中途崩溃,新指纹已记而旧向量还在,此后每次 boot 都判「相等」,残留永不复检);
524
- - 启动日志 `memory_embedder_identity_changed` 带**旧值/新值/清了几行/耗时 ms/原因**(`changed` | `unattributed` | `unreadable`),首次记录指纹是 `memory_embedder_identity_armed`;比对腿**自身失败一律拒启**(吞掉 = 门在场却没跑成)。⚠️ 这条行按「identity 变了」发,**不是**按「清了几行」发:当时库里恰好没有向量可清(上一轮已清过 / 缺席窗内被写成 NULL)照样发,`cleared: 0` —— 换 embedder 这件事在启动日志里不该无痕。真正零响亮的只有「相等」那一档。⚠️ **这句承诺以「进程活到把日志发出来」为界**:清列与写元表是两条各自提交的语句,日志在两步都完成之后才发,所以进程恰好在清完之后崩溃(或被杀),下次启动会按当时的真实状态重判(报「首次记录指纹、没有向量可清」,或者元表已写完就直接判「相等」不发声)——**那一次清掉了多少行会从日志里丢失**。数据面不受影响(次序钉保证的就是这个:向量已 NULL、不会残留旧空间),丢的只是审计行;要精确到行数的审计请以 DB 侧的变更记录为准。
525
- - 清空**只丢可再生的向量缓存**(正文/frontmatter 分毫不动);清完**不回填**,行被下次写入时自然按新模型重嵌,期间检索退回词面档。🔴 **代价按上面那条读**:退词面档 = 与查询零词交集的行**检索不到**(不是排到后面),所以清空之后、重嵌之前,那些行对改述/同义/跨语言查询是**不可达**的。⚠️ **误配代价诚实说**:把 `MODEL` 改错一次再改回来,向量**不会自动回来**——只随行被再次写入才重嵌;对写完就不动的记忆库,误配一次 = 向量面实质长期缺席(正文零损失,检索退 lexical,零交集查询够不着)。要立刻恢复只能自己重写一遍语料(或接受词面档)。
534
+ 🔴 **换 `MODEL`/`DIM` 的行为:下次 boot 拒启,向量一行不动;清列重嵌只在运维显式确认后做(embedder 指纹门)**。本服务在 `agent_memory_engine_meta` 单行元表里记住当前 embedder 的**指纹** `{model, dimensions}`(明文,便于排障;**endpoint 不进指纹**——换域名/代理不换语义空间)。每次启动比对一次:
535
+ - **相等** ⇒ 零动作、零写;元表还没有这一行且库里没有向量 ⇒ 只记下指纹(`memory_embedder_identity_armed`);
536
+ - **对不上**,三态:`changed`(记下的与配置的不等)/ `unattributed`(元表还没有这一行而库里已有向量)/ `unreadable`(那一行读不出来:手改、回滚残留)⇒ **缺省拒启**,一行不清、元表不写;拒句点名所处的态、记下的与配置的身份和出路:
537
+ ① `changed` 才有的一条 —— 把 `MEMORY_EMBEDDER_MODEL` / `MEMORY_EMBEDDER_DIM` 改回记下的身份;② **显式确认重嵌** —— 设 `MEMORY_EMBEDDER_REEMBED=<本次配置的 model>/<dim>`(拒句里给出可逐字照抄的那一条),**停机**(`scale 0`)后再起;③ 撤掉 `MEMORY_EMBEDDER_*`,服务以词面档起来,存量向量原样留着(回头配回同一身份可续用,见下面「没配」一条)。
538
+ 为什么缺省是拒启:清列**不可逆**(行只在下次被写时才重嵌),拒启**可逆**;换模型 / 改维度的一次手滑不该在起服时就丢掉全部向量。
539
+ - **确认了**(口令恰好点名本次配置的身份)⇒ **先把 `agent_memory_engine_entry.embedding` 整列清成 NULL,再写新指纹**(次序不可换:反过来若中途崩溃,新指纹已记而旧向量还在,此后每次 boot 都判「相等」,残留永不复检)。口令绑定身份,所以它天然是一次性的:新指纹记下之后下一次起服判「相等」,口令不再有东西可确认(留着无害,可随时撤);之后再换一次模型,留着的旧口令对不上新配置 ⇒ 起服拒,不会替下一次换模型静默清列。
540
+ - 已确认的清列,启动日志 `memory_embedder_identity_changed` 带**旧值/新值/清了几行/耗时 ms/原因**(`changed` | `unattributed` | `unreadable`),`note` 点名确认口令;首次记录指纹是 `memory_embedder_identity_armed`;比对腿**自身失败一律拒启**(吞掉 = 门在场却没跑成)。⚠️ 这条行按「identity 变了」发,**不是**按「清了几行」发:当时库里恰好没有向量可清(上一轮已清过 / 缺席窗内被写成 NULL)照样发,`cleared: 0` —— 换 embedder 这件事在启动日志里不该无痕。真正零响亮的只有「相等」那一档。⚠️ **这句承诺以「进程活到把日志发出来」为界**:清列与写元表是两条各自提交的语句,日志在两步都完成之后才发,所以进程恰好在清完之后崩溃(或被杀),下次启动会按当时的真实状态重判(报「首次记录指纹、没有向量可清」,或者元表已写完就直接判「相等」不发声)——**那一次清掉了多少行会从日志里丢失**。数据面不受影响(次序钉保证的就是这个:向量已 NULL、不会残留旧空间),丢的只是审计行;要精确到行数的审计请以 DB 侧的变更记录为准。
541
+ - 清空(已确认时)**只丢可再生的向量缓存**(正文/frontmatter 分毫不动);清完**不回填**,行被下次写入时自然按新模型重嵌,期间检索退回词面档。🔴 **代价按上面那条读**:退词面档 = 与查询零词交集的行**检索不到**(不是排到后面),所以清空之后、重嵌之前,那些行对改述/同义/跨语言查询是**不可达**的。⚠️ **误配的代价**:把 `MODEL` 改错一次,起服就拒,改回来即照常起 —— 向量一行不丢;只有在**给了确认口令**之后,清掉的向量才**不会自动回来**(只随行被再次写入才重嵌;对写完就不动的记忆库 = 向量面实质长期缺席,正文零损失,检索退 lexical,零交集查询够不着,要立刻恢复只能自己重写一遍语料)。
526
542
  - ⚠️ **换 embedder 请停机换:先 `scale 0`,再起**。指纹门管的是 **boot 面**;**滚动升级窗**里新副本清完列之后,仍在服役的旧副本还在按**旧**模型写向量,滚动结束后表里混着两个空间而元表已是新指纹 ⇒ 此后恒判「相等」,门再也不会复检。这是纪律面的残余,不假装根治(行级 embedder 记账列可结构性根治,超出本件射程)。滚动事故后的补救 = 手动清一次:`UPDATE agent_memory_engine_entry SET embedding = NULL WHERE embedding IS NOT NULL;`(同一条也可用于「误配后立刻重置」)。
527
- 🔴 **同族的第二格:回滚到指纹门之前的版本**(7.14.x / 7.15.x —— 有 embedder、无指纹门)。那些版本照写 `embedding` 列却**根本不认识这张元表**,所以「装本版(元表记 B)→ 回滚到旧版跑一段(往列里写 A 空间向量,元表纹丝不动)→ 再升回本版、配置仍是 B」这条路走完,元表与配置都说 B 而列里躺着 A 空间向量 ⇒ 此后恒判「相等」,门再也不会复检。**跨门回滚与滚动窗要做同一件事**:回滚之前(或重新升级之后)手动跑上面那条清列 SQL,或干脆 `DELETE FROM agent_memory_engine_meta WHERE meta_key = 'embedder_identity';` 让下次启动按「无主向量」保守清一次。
543
+ 🔴 **同族的第二格:回滚到指纹门之前的版本**(7.14.x / 7.15.x —— 有 embedder、无指纹门)。那些版本照写 `embedding` 列却**根本不认识这张元表**,所以「装本版(元表记 B)→ 回滚到旧版跑一段(往列里写 A 空间向量,元表纹丝不动)→ 再升回本版、配置仍是 B」这条路走完,元表与配置都说 B 而列里躺着 A 空间向量 ⇒ 此后恒判「相等」,门再也不会复检。**跨门回滚与滚动窗要做同一件事**:回滚之前(或重新升级之后)手动跑上面那条清列 SQL,或干脆 `DELETE FROM agent_memory_engine_meta WHERE meta_key = 'embedder_identity';` 让下次启动按「无主向量」判(拒启;带上确认口令 `MEMORY_EMBEDDER_REEMBED` 起一次即清一次)。
528
544
  - **没配 `MEMORY_EMBEDDER_*` 时门整个不装**(元表不动,旧指纹保留),启动日志 `memory_embedder_identity_absent` 说明:缺席期间**被改写过**的行,其 embedding 本来就被写成 NULL,所以「配置回归同一 identity 后向量续用」只对缺席期间**未被触碰**的行成立。
529
545
  - 残余:同名模型在不同供应商若其实是不同实现(自托管 finetune 撞名),指纹辨不出——明知异实现时改 `MEMORY_EMBEDDER_MODEL` 名,或手动跑上面那条清列 SQL。`MEMORY_ENGINE_BACKEND=mysql` 无向量面(v1 词面档),没有这张元表也没有这道门。
530
546
  📋 **档位对运维可见**:启动日志 `memory_engine_enabled` 行带 `vectorMode` 字段(取自后端实例的真值,不是配置推断)——`lexical` 在跑就必须看得见;配了 embedder 时同行还带 `embedderModel`/`embedderDim`,换模型这件事在日志里留痕。
@@ -552,7 +568,7 @@ MODEL_CODE_ROLES=default,subagent # 不设=全中立;仅这些角色在「
552
568
  | env | 缺省 | 说明 |
553
569
  |---|---|---|
554
570
  | `MEMORY_ORG_ADMISSION_MODE` | `enforce` | `enforce`=判决即结果;`audit`=**运维诊断位**——准入面判决照算、拒绝降为日志+metric,零行为变化(不整拒、不窄化写面);`/v1/memory/export`、`/v2/memory/sync/:scope` 的 `org:` 属主门在 audit 下**逐字保持收编前的 operator-only**(未验证的目录不得开数据面)。两模式都要求目录 client 在场 |
555
- | `MEMORY_ORG_DIRECTORY_JSON` | 缺省 | 单机形静态授权表 `{"<principal>":{"org:acme":{"write":true}}}`。**启动期整表校验,坏表拒启动**。有 web-admin 时被忽略(center 胜 + warn) |
571
+ | `MEMORY_ORG_DIRECTORY_JSON` | 缺省 | 单机形静态授权表 `{"<principal>":{"org:acme":{"write":true}}}`;scope 键须过引擎的 org scope 键判官(`org:` 加一段名:`[A-Za-z0-9._-]` 直写、其余字符写成大写 `%XX`,段至多 64 个编码字符;可用引擎的 `formatOrgScope` 铸,例 `org:team%40acme`);web-admin 远程腿下发的目录同判 —— 一代目录里有一把键不过,该主体的这一代整代不采用(瞬时拒,可重试;服务日志 `org_directory_generation_refused` 点名坏键与版本)。**启动期整表校验,坏表拒启动**。有 web-admin 时被忽略(center 胜 + warn) |
556
572
  | `MEMORY_ORG_GRANT_TTL_MS` | `60000`(`[1000, 3600000]`) | 授予/负结果的缓存 TTL = 「有界 LKG」:**新任务/新 resume 腿**的准入判决滞后 ≤ 此值;**在跑任务不受吊销影响**(判决点在 prepare 期) |
557
573
  | `MEMORY_ORG_UNAVAILABLE_BACKOFF_MS` | `10000`(`[500, 600000]`) | 目录取不到之后的退避窗(只用于取数失败臂,不用于负结果) |
558
574
  | `MEMORY_DELEGATION_EVIDENCE` | 缺省(=引擎自缺省 `static-face`) | **委派臂的证据标准**(core 5.45.0 起)。`static-face`=今日行为:委派的 attestation 缺失/未知**且**静态工具面够得着外部内容 ⇒ 标记本会话记忆为已污染(可能性即暴露)。`attested-only`=**只**豁免那一条静态面标记,且**真的豁免了一次**才响亮通告(`engine_notice` 的 `memory.delegation_static_mark_waived`,每个 prepared leg **至多**一次);送达 `external` attestation 照标、非委派的污染类工具照标、委派工具自身被分类为污染的照标。⚠️ 通告**不是 leg 计数器**:不含委派的 leg、污染面没挂载的 leg、送达了 attestation 的委派都不发 —— 看不到通告的常态含义是「没有可豁免的事」,不是「配置坏了」。坏值**启动期拒**。**部署席 only**(TaskSpec 无同名键,任务/受治脚本无法据此放松部署)。⚠️ **已接受的代价**:`attested-only` 下**后台**子代的真实外部接触不标记本会话(其内容经 TaskOutput / 任务通知注入 / AgentTranscript 摘要回流,三条都不带 attestation);异常收尾的前台子代同理。已标记的会话永不回滚清洗。运维读面 = `GET /v1/diagnostics/wiring` 的 `memoryPosture.delegationEvidence`(`null`=本部署未设,引擎自缺省) |
@@ -579,8 +595,9 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
579
595
  - **库形嵌入**(包根 `.` 导出:`loadConfig` + `createHttpServer` + 自建 `StoreBackend` / `ServiceDeps`):`loadConfig()` 之后调
580
596
  `composeExecutionLane(config, ctx)`(HTTP 服务与 `run-local` 都这么做;7.104.0+)。执行车道的四条拒启(`leader-host-lane` /
581
597
  `multi-tenant-shared-lane` / `device-durable-store` / `device-durable-approval`)自 7.104.0 起判在这一步,**不在** `loadConfig()` 里 ——
582
- 只调 `loadConfig()` 的装配对这几形照起。`ctx.configSource === "local"` 时它先读 `config.d/remote-exec.json` 覆写车道(`drivesFromFile`
583
- = 你的装配驱动得了的文件车道),再判;不走文件覆写的装配直接调同一份判据 `assertExecutionLaneInvariants(config)`。
598
+ 只调 `loadConfig()` 的装配对这几形照起。`ctx.configSource === "local"` 时它先读 `config.d/remote-exec.json` 覆写车道,再判。`ctx.entry`
599
+ = 你的装配这只入口:`runs` 是它跑得了的车道闭集(文件或 env 声明了表外的一条 ⇒ 拒),`declares` 可选(文件与 env 都没点名时由入口声明,排在二者之后;文件在场即终结选择 —— 文件点的车道取不到坐标 ⇒ 拒,不由 env 的另一条或入口声明顶上;
600
+ `run-local` 声明 `host`);自建 HTTP 装配递包根导出的 `HTTP_SERVICE_LANE_ENTRY`(全部车道、不替部署声明);不走文件覆写的装配直接调同一份判据 `assertExecutionLaneInvariants(config)`。
584
601
  `ctx` 的型 `ExecutionLaneComposeCtx` 也从包根导出(自己的装配里要写它的签名 / 抽 helper 时直接 import,不必手抄一份形)。
585
602
  读口场景门 `scenarioGate` 是 `createHttpServer` 的必填件(7.131.0+):用包根导出的 `createScenarioGate({ config, scenarios, principalCaps, logger }, "peek")` 铸(与任务提交腿同一个工厂,型 `ScenarioGate` / `ScenarioGateLeg` / `ScenarioGateSources` 一并导出),没递就是 tsc 红。
586
603
  **模型路由端点的拒启同理判在目录应用之后**:`judgeServedModelRoute(config, { envTableIsPlaceholder })`(包根导出,HTTP 服务与 `run-local` 调的就是它)只判将要服务的那张表(目录表或 env 表,句子随视图);
@@ -597,6 +614,22 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
597
614
  (另一个宿主重启了共享引擎):壳据此把「引擎换代」当**一等事实**处理,不必等某个带鉴权的请求先撞一次
598
615
  401 才发现(`/health` 无鉴权,心跳恒绿)。⚠️ 只给这一个数:service token 本体与代际计数**不上**这一面。
599
616
  钉:`test/health-identity-contract.test.ts`。
617
+ - **数据根引擎版本高水位 marker(7.147.0+;只 `DB_BACKEND=local`)**:`<数据根>/engine-high-water.json`(`/health.dataRoot` 那只目录的顶层),
618
+ 形 = **恰一键** `{"highWater":"x.y.z"}` —— 打开过这只数据根的最高引擎(本包)版本(引擎判过之后、往根里写第一个字节之前就抬,所以它是「可能写过这只根的最高版本」,不是服务成功的记录)。`highWater` 是纯 `x.y.z`(无前导零、无 `v` 前缀、无预发布 / 构建后缀、
619
+ 每段是安全整数),三段按数比。宿主(壳侧判官)与引擎读**同一只文件、同一读法**,不另记第二份高水位:
620
+ - 文件不在 ⇒ **缺席**:还没有 7.147.0 及以后的引擎打开过这只根 —— 不比;
621
+ - 在而读不出(I/O 错;悬空符号链接 —— 链接在、目标不在)/ 不是 JSON 对象 / 缺 `highWater` / 有别的键 / `highWater` 不是纯 `x.y.z` ⇒ **读不出**:引擎起服拒,**不当缺席**;有别的键 = 更新的引擎写下的形(数据根比本构建新);
622
+ - 读得出 ⇒ 与本端版本比:`highWater` 高于本端 ⇒ 本端引擎起服拒 `config.schema_mismatch`(拒句点名这只文件、高水位与本构建版本;
623
+ 包根导出的 `LocalDataRootSchemaMismatchError.findings` 里一段 `problem: "newer-engine"`,同一段必带 `engineHighWater: { highWater, engine }` —— 读数类型 `LocalDataRootMismatch` 是以 `problem` 为判别位的联合),不必先起一只注定被拒的引擎再解析 stderr。
624
+ - **写者只有引擎**,判与抬在**打开数据根的那一刻**一段做完(数据根锁在手 = 单写者;先于引擎这一侧的任何一只店被建起 —— 店在构造里就会写):先判,判拒 ⇒ 本仓零字节写、不抬;
625
+ 判过 ⇒ 先抬再写 —— 本构建高于记录(或缺席)⇒ tmp + rename 原子替换(属主独占 `0600`,目录项也要证落盘;并发读者只见整份旧值或整份新值),相等不写(只证它在盘上:fsync 文件与目录)。所以店构造里的自修、
626
+ 起服对账、一次性迁移与任何服务写都在抬之后:写到一半进程死了,水位已是本构建,比它旧的引擎重开被拒,本构建重开续跑。对账(严格边档 / 快照消化)或迁移在抬之后被拒 ⇒ 水位留在本构建。
627
+ 本构建版本按**发布线**读:`x.y.z`,可带 `-预发布` / `+构建` 后缀,后缀只剥不比(`7.147.0-rc.1` 按 `7.147.0` 判与抬,marker 恒写纯三段);读不出(例:读不出 `package.json` 时的 `unknown`)⇒ 引擎拒开这只根(marker 不读不写)。
628
+ 宿主只读:不写、不删、不改。写不进(含目录项证不了落盘)⇒ 不以这只根服务(起服拒;缺省推导的 local 在起服时为空的根上按既有降级臂退内存)。
629
+ 残余(如实):引擎内核 `FileStorageBackend` 的构造把取锁与它自己的店放在同一步(建子目录、店目录锁、内核账本撕裂尾的收拢)—— 那几笔在判之前。
630
+ - SQL 后端不读不写它(滚动升级窗内合法混跑,另有列册对账)。只有**旧的那一端**是 7.147.0 及以后才守得住(更早的引擎不读它)。
631
+ 回滚的唯一支持形 = 恢复升级前的数据根备份(marker 随备份回到旧值);同一台机器要并行跑多个引擎版本 ⇒ 各给一个数据根(`LOCAL_DATA_ROOT`)。不设绕过旋钮。
632
+ 钉:`test/local-engine-high-water.test.ts`。
600
633
  - `/health` 活体 store 探测(5.14.0,SQL 后端专属 additive):后台探针(`STORE_PROBE_INTERVAL_MS`,
601
634
  默认 15000;0=关;负/非数启动响亮拒)缓存一份 DB 真往返结果,`storeProbe:{live,ageMs,error?}` 在
602
635
  探针接线时恒在,顶层告警键 `storeLive:false` 只在死时出现。**status 保持 "ok"**(liveness≠readiness
@@ -678,7 +711,7 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
678
711
  行与编号高水位一并抹掉(同 anchors/附件/快照的级联)——会话 id 可被重新认领,残留就等于把上一位
679
712
  主人的任务标题与编号进度交给下一个化身。
680
713
  - **会话的行事实只住在数据根的 `session-incarnations.json`**(每只会话一格:属主 / 化身 / 执行放置 / 建档时刻 / fork 谱系);`sessions/*.jsonl` 是匿名的对话内容。
681
- 备份 / 迁数据根时两样一起搬;更早版本写下的带属主戳的存档由起服一次性迁移改写(改不了的点名,fork 与改日志推送答 409 `conflict.session_archive_stamped`;会话同步导入的第一阶段对 fork / stale 且没带同意的推送也直接答它),快照目录 `session-archive-m33/` 非空且消化不了 ⇒ 拒启(MIGRATION 写了出路)。
714
+ 备份 / 迁数据根时两样一起搬;更早版本写下的带属主戳的存档由起服一次性迁移改写(改不了的点名,fork 与改日志推送答 409 `conflict.session_archive_stamped`;会话同步导入的第一阶段对 fork / stale 且没带同意的推送也直接答它,7.147.0 起干跑 `POST …/sync/plan` 对这两形同样),快照目录 `session-archive-m33/` 非空且消化不了 ⇒ 拒启(MIGRATION 写了出路)。
682
715
  `session-incarnations.json` 本构建读不出(更新的引擎写过、格里多出不认得的键;或文件坏了 / 读不了)⇒ 起服拒 `config.schema_mismatch`(与 SQL 后端列册对账同一个码,拒句点名文件与键;
683
716
  出路 = 部署写下它的那一版,或恢复升级前的数据根备份;读不了 ⇒ 修好权限 / 盘再起);起服之后才读不出(跳过了 `ensureSchema()` 的嵌入方;或违规共享的数据根上另一只引擎写了它)⇒ 同码拒,
684
717
  形读不出则那只边档的每一读每一写拒到重启,盘上字节不被盖掉。
@@ -718,10 +751,9 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
718
751
  那是文档化的通道,而不是两条路各有一套默认。
719
752
  🔴 **同一只窗 7.119.0 起也管「已配置的 SQL 库此刻连不上」**:`DB_BACKEND=mysql|pg`(含 `SESSION_BACKEND=auto`)boot 期开不了店 ⇒ 窗内每 250ms 重试、到窗拒启(点名后端与 host:port;
720
753
  永久错如凭据 / DDL 权限也拖到窗尾才拒,要当场拒设 `0`;唯一例外是**确定的拒启表态**,见下一条「跳版升级的旧库」,它不等窗)。k8s 清单里库与 server 同时起,所以清单把窗给到 `300000`。
721
- ⚠️ **裸 boot(没有显式 `DB_BACKEND`)那一形的时序也跟着变**:它的锁冲突走的不是拒启而是既有的
722
- 「退回内存并 warn」降级臂(`store_db_unreachable_fallback_memory`),所以第二只实例此前**当场**降级、
723
- 现在要**等满窗**才降级 —— 方向仍是好的(窗内拿到就用上真数据根,而不是静默丢掉持久面),但它是一次
724
- 真实的启动时长变化。显式 `DB_BACKEND=local` 不走降级臂(拒启),`0` 关掉两形的等待。
754
+ ⚠️ **裸 boot(没有显式 `DB_BACKEND`)撞锁与显式 `DB_BACKEND=local` 同形**:等满窗仍拿不到 ⇒ 拒启(同一句拒句)。
755
+ 「退回内存并 warn」降级臂(`store_db_unreachable_fallback_memory`)只对**空 / 不存在**的数据根开 —— 有人持锁的根上
756
+ 一定有东西,开不了就拒,不退内存(退了 = 这只根上已有的会话 / run 账本整片看不见)。`0` 关掉等待。
725
757
  🔴 **新引擎的锁等待窗应 ≥ 前任的排空窗 + 15 s 终局 + 一拍**(前任的锁要到它收尾链尾部才还回来;前任最晚何时退出见 `DRAIN_GRACE_MS` 下面的「停机上界(契约句)」):
726
758
  壳 / 桌面按**自己的**排空窗给新引擎进程注入这个值 —— 那是配置渠道的本意;server 不改缺省(缺省 `60000`:改缺省会让所有部署的起服失败延后)。
727
759
  三问成文见 `docs/DEPLOY-PREREQS.md`。
@@ -776,8 +808,9 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
776
808
  —— 没有任何东西把 B 的路径映到 A。要么把服务(或 `run-local`)跑在放着文件的那台机器上,要么走容器车道
777
809
  (`e2b`/`k8s`/`local-docker`),那里的工作区是沙箱自己的,`cwd` 被忽略。
778
810
  - **读面(7.86.0 起)**:`GET /v1/capabilities` 的 `executionLane` 把本部署的车道广告成
779
- `{provider:<执行车道八词>, toolsOnThisHost:boolean}`。7.97.0 起 `REMOTE_EXEC` 未设 ⇒
780
- `provider:"in-process"`、`toolsOnThisHost:false`(本部署没有执行车道 = 无手;改前折报 `"host"`/`true`;7.147.0 起这一形拒启、这个状态名改叫车道词 `none`,见上文硬断预告)。
811
+ `{provider:<执行车道八词>, toolsOnThisHost:boolean}`。没有执行环境的部署(无手)报
812
+ `provider:"none"`、`toolsOnThisHost:false`(`REMOTE_EXEC=none` 显式声明;7.97.0 起到 7.147.0 之前 `REMOTE_EXEC` 未设落进的是同一个无手状态,在这个读面上叫 `in-process`,此前折报 `"host"`/`true`);
813
+ 7.147.0 起未声明车道即拒启,`in-process` 这个状态名随之改名 `none`(见上文车道表之后的「硬断(自 7.147.0 起)」一段)。
781
814
  要本机有手,显式设 `REMOTE_EXEC=host`(sema-up 产物已显式写车道)。
782
815
  壳/客户端判「工具跑不跑在本机」**读这一位**,不要再从自己这一侧的 `REMOTE_EXEC` 猜 —— 连远端引擎时
783
816
  那个 env 说的是**壳自己这台**的事。⚠️ 它是**文件系统同一性**位,**不是**权限位:`cwd` 兑不兑现仍由
@@ -795,20 +828,20 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
795
828
  - `writeFile`(Write / Edit / NotebookEdit 落盘的那一步)= **整文件替换:目标要么整份是旧内容、要么整份是新内容;取消(`aborted`)⇒ 目标未碰**。
796
829
  host 车道 = 引擎的整文件替换(同目录暂存 + fsync + rename);ssh / adb / local-docker / k8s / e2b = 先在目标目录下建一只生来 0700 的私有目录
797
830
  `.<名>.stage-<12 位十六进制>/`,整份传进去,再在目标机上一次 `mv -f` 换上去;device = 设备侧执行器在同一步里暂存再换上。
798
- 传输中途断、取消、写到一半出错 ⇒ 目标逐字是旧内容,私有目录被删(删不掉 ⇒ 错误句点名残留的目录)。修前 host 与五条远端车道是就地截断写:中途中断留下新内容的前半截,原件找不回。
831
+ 传输中途断、取消、写到一半出错 ⇒ 目标逐字是旧内容,私有目录被删(删不掉 ⇒ 错误句点名残留的目录,跟一跳重新暂存的那只也点名;脚本点名的残留目录补清一次,仍在的才计 `server.remote-write.staging-dir-left`)。修前 host 与五条远端车道是就地截断写:中途中断留下新内容的前半截,原件找不回。
799
832
  - **服务进程被 `SIGKILL` 时,暂存目录会留在目标旁(可直接删)**:上一条的清理是服务进程自己做的 —— `kill -9`、内存杀手、容器被强停这类**没有机会跑清理**的退出,正在传输的那一次写留下 `.<名>.stage-<12 位十六进制>/`,里面是在传的新内容(上传途中被杀只有前半截,传完才被杀则是整份);**目标逐字是旧内容**,整文件替换的保证不受影响,多出来的只是这只目录。ssh 车道实测(被强杀的写都留下了);其余四条远端车道走同一段暂存脚本,同形。取消(Ctrl-C 中断那一次写)不留 —— 取消路径上清理那一往返照跑。**本版不自动清**:下一次对同一目标的写不会去扫陈旧的暂存目录,它会一直留着,确认没有另一个进程正在写同一个目标之后 `rm -rf` 即可(名字里的十六进制每次写都不同,在写的那一只和陈旧的那一只光看名字分不出)。host 车道的暂存是引擎的,同形的残留名是 `<目标>.tmp.<进程号>.<十六进制>`,同样可以删。
800
833
  - 暂存自创建起不比目标更可读:远端五车道的暂存只在私有目录里(别的用户进不去;替换 0600 的文件时新内容在传输期间只有写入方与 root 读得到),device 的暂存件以 0600 打开;
801
834
  host 车道的这一保证在引擎原语里:引擎 7.60.0 起目标存在时暂存件以 0600 打开。新建的文件是写入方身份的 umask 形(local-docker = 容器内镜像用户的)。
802
835
  私有目录建出后要读回核实是 0700 且属写入方、且目录上没有 ACL 条目:macOS 的原生 ACL 与 FreeBSD / illumos ZFS 的 NFSv4 ACL 不经模式位 —— 工作区父目录带可继承的允许条目时,新建的私有目录一建出来就带着它,读回 `700` 也挡不住,
803
- 所以读到条目就去掉(macOS `chmod -N`、FreeBSD `setfacl -b`、illumos `chmod A-`)、去完再读;**核不过(`chmod` 被拒、文件系统不认权限位、缺省 ACL 的掩码收不下来、继承来的 ACL 条目去不掉或读不出、目标机的系统不在 Linux / macOS / FreeBSD / illumos 之内、目标机没有 `stat`)⇒ 不暂存,退回就地写目标**
836
+ 所以读到条目就去掉(macOS `chmod -N`、FreeBSD `setfacl -b`、illumos `chmod A-`)、去完再读;**核不过(`chmod` 被拒、文件系统不认权限位、缺省 ACL 的掩码收不下来、继承来的 ACL 条目去不掉或读不出、目标机的系统不在 Linux / macOS / FreeBSD / illumos 之内、目标机读不出模式串〔`ls` 不可用 / 输出读不懂〕)⇒ 不暂存,退回就地写目标**
804
837
  (两只文件面、五条腿都有;非原子;ssh / local-docker / k8s 写进原 inode、权限位不变,e2b 的 `files.write` 与 adb 的 `adb push` 整份写目标、inode / 权限位由对端定;
805
838
  遥测计 `server.remote-write.stage-seal-fallback`);读 ACL 的臂按**目标机**的 `uname -s` 选(Linux:POSIX ACL 由模式位读回的掩码覆盖,不另读 · Darwin · FreeBSD · SunOS);FreeBSD / illumos 两臂按系统文档写、未上真机,读 ACL 的工具报错 / 输出形不对 / `uname` 退出状态不成都按读不出处理(读 ACL 的工具自己静默吞掉的读取错误看不出);
806
839
  Linux 上经 NFS 挂载、由服务端自己执行的 NFSv4 ACL 在客户端没有可读的条目,不在覆盖内;其它系统(OpenBSD / NetBSD / AIX / Cygwin 一类)的目标机每一次写都走就地写并计数。
807
840
  host 车道的暂存是引擎的,device 车道的暂存是设备侧执行器的 —— 这一道不管它们;adb 的追加 = 7.138.x 的形:读旧 + 拼 + 整份 push(push 没有追加写)。Android 共享存储(`/sdcard` 一类)上 adb 的写按 sdcardfs / MediaProvider FUSE 的权限语义**推断**每一次都走这一形(未上真机,真机读数候 test)—— adb 的工作区放在 `/data/local/tmp` 一类认权限位的目录即照常暂存。
808
841
  - 一个身份:准备目录、上传、提交、清理都以同一个身份做。local-docker 的上传是容器内以镜像的用户经 stdin 收字节(不是守护进程以 root 的 `docker cp`)——
809
842
  写文件与代理的 shell 同一套权限,镜像用户写不了的路径答 `permission_denied` —— 镜像里没预建的工作区目录由容器启动以 root 建出,镜像用户 ≠ root 时要在镜像里预建并归镜像用户。
810
- - 回落(host 与五条远端车道,与引擎同一套;device 由设备侧执行器自定,非常规文件拒写):单跳符号链接 ⇒ 替换它指的那只常规文件(链接仍是链接;远端车道先在那只文件的目录里重新暂存,rename 只在一个目录之内);链接链 / FIFO / 设备文件 / rename 被拒(如单文件绑定挂载)⇒ 由已落定的暂存件就地拷入(非原子);
811
- 目录不收新名字(只读目录里的可写文件)⇒ 直接写目标一次(旧形,同样非原子;远端车道在这一形里写目标那一步因取消中断答 `unknown`「目标可能只写了一半」,不答 `aborted`;取消落在那一步开始之前(读旧也在这之前)照答 `aborted`、目标未碰;那一步不收取消(ssh 的 sftp)⇒ 照样写完、答成功;
843
+ - 回落(host 与五条远端车道,与引擎同一套;device 由设备侧执行器自定,非常规文件拒写):单跳符号链接 ⇒ 替换它指的那只常规文件(链接仍是链接;远端车道先在那只文件的目录里重新暂存,rename 只在一个目录之内;那里的私有目录同样核不过 ⇒ 与上面同一条退路:清掉原暂存件、直接写目标、计 `stage-seal-fallback`);链接链 / FIFO / 设备文件 / rename 被拒(如单文件绑定挂载)⇒ 由已落定的暂存件就地拷入(非原子);
844
+ 目录不收新名字(只读目录里的可写文件)⇒ 直接写目标一次(旧形,同样非原子;远端车道在这一形里分两段读取消:备件阶段〔第一个字节发往目标机之前:adb 在本机落临时件、ssh 开 sftp 通道含懒重连、k8s 建连、追加的读旧 + 拼〕取消 ⇒ `aborted`、目标未碰;传输那一步因取消中断 ⇒ `unknown`「目标可能只写了一半」,不答 `aborted`;那一步不收取消(ssh 的 sftp)⇒ 照样写完、答成功;
812
845
  host 车道的回落形由引擎就地写,中途取消目前仍答 `aborted` —— 已知缺口,修在引擎)。
813
846
  已有文件的权限位带过去;属主不带(替换后的文件属于写入方)。
814
847
  - 已知语义变化(与引擎 / 参照实现同形):rename 换的是目录项 ⇒ **硬链接被断开**(另一条链接留旧内容);目录可写时**没有写权限位的文件也会被替换**(权限位保留);
@@ -947,6 +980,7 @@ FLEET_ADVERTISE_ADDRESS=http://<本机可达IP>:8090 # 可选:设了才启公
947
980
  上一代 —— 上一代若还是 env,env 模型就一直服务)。颗粒更粗的失效(整个域读不懂、拼错键、坏文件)仍整拒候选、保 LKG。
948
981
  - **没有 `MODEL_ID` 也能起**(7.101.0 起):目录(`config.d/models.json` 或中心)有可用条目时 env 可以不写 `MODEL_ID` —— 缺省模型取
949
982
  目录缺省。判据在目录应用**之后**:env `MODEL_ID` 或目录缺省任一在场即过;两者皆无 ⇒ 拒启(码 `config.default_model_missing`,指路二选一)。
983
+ 若是因本地候选被判官整拒(悬空符号链接 / 坏 JSON / 空文件 / 域判形失败 …)才落到 env 表,7.147.0 起拒启句同一行多一段回落原因:被拒的文档名、拒码、原因类词(悬空链接 / 读不出 / 解析错误 / 空文件 …)与文档路径(悬空链接给出链接自己的路径,不带它指向的目标路径;不带配置值,原句只在 `config_candidate_rejected` 行);句首 `MODEL_ID is required when no catalog default resolves [config.default_model_missing]` 不变。
950
984
  中心拉取超出 `CONFIG_BOOT_FETCH_BUDGET_MS` 又没有 LKG 时,boot 那一刻没有目录 ⇒ 没有 `MODEL_ID` 兜底就拒启(调大预算让 boot 等目录,
951
985
  或写一个 `MODEL_ID` 兜底)。
952
986
  - **灰度姿势**(配置控制面 AI 建议):先 `SEMA_WEB_ADMIN_DRY_RUN=true` 起一轮,看日志 `web_admin_dry_run`(中心给的 models/roles/teams + 会否覆盖 default、per-model apiKeyEnv)对得上 env 再去掉该 flag 真正 apply。
@@ -1494,11 +1528,10 @@ TASK_WRITE_FACE_ESCALATION=allow # 词表 allow|refuse;坏词启动期响亮
1494
1528
  整份不是对象)的对端 7.131.x 当时就整条丢了,重读照旧整条不挂(`a2a_injection_dropped` 点名);窗关之后回到上面的读法(整条不挂)。
1495
1529
  - **请求体自带的 `mcpServers[].transport.env` / `transport.headers`**(「名 → 串」记录,与 `a2aPeers[].headers` 同一只读法):名叫 `__proto__` 的成员、值不是串的成员、
1496
1530
  整只不是纯数据 ⇒ 新提交 `400 request.field_invalid` 点名第一处(`__proto__` 成员点名成员级路径 `…headers.__proto__`;值不是串 / 整只读不成点名那个键,原因句点名成员)。
1497
- 续跑重放存量请求体(JSON 文本铸出来的记录)时复现 7.131.x 的读法,唯一偏离是引擎拒的 `__proto__` 成员:记录里有值不是串的成员 ⇒ 整键不交(7.131.x 原跑就是这样挂的,不半程换身份);
1498
- 等价只对 JSON 存体成立 —— 直接递 `Map`、类实例、`process.env` 的嵌入方按上面的更严读法(7.131.x 对带自有串属性的类实例照收);
1499
- 只有 `__proto__` 名的成员坏 ⇒ 剔掉它、其余照交。server 照挂,运维 warn `mcp_transport_members_dropped`(`server` / `lost` / `lostTotal?` / `note`)点名没交出去的键 / 成员与整键没交的
1500
- `transport.args` / `transport.principalHeader`。A2A 对端 `headers` 同读:值不是串 ⇒ 对端照旧不挂;只有 `__proto__` 成员坏 ⇒ 续跑照挂、剔掉它,一行 `a2a_peer_headers_members_dropped`
1501
- (`peer` / `lost` / `note`);新提交上两形都整条不挂。
1531
+ 续跑重放存量请求体时,按 7.131.x 的读法会交不出去的成员 —— `__proto__` 名的成员、有值不是串的成员的整键、`transport.args` / `transport.principalHeader` —— 在本部署兑现请求 MCP 时**拒续跑**(名字被部署占住的条目不算:它整条输给部署):
1532
+ `409 resume_blocked_by_policy`(`blockedBy: "request.field_invalid"`,`field` = 第一处成员坐标如 `mcpServers[1].transport.headers`;句子列出全部坐标,只给键名、不回显值),卡 / 任务仍待决、行不动;
1533
+ A2A 对端 `headers` 只在 `__proto__` 名的成员上同拒(`a2aPeers[i].headers.__proto__`,本部署兑现请求 A2A 时);`headers` 里有值不是串的成员 ⇒ 那一条对端整条不挂(`a2a_injection_dropped` 点名)、任务照跑 —— 整条不进不在拒的射程里(MCP 一面同:整条不进的条目照旧 `mcp_injection_dropped`)。7.146.x 及以前续跑照挂、剔掉那几处,一行运维 warn `mcp_transport_members_dropped` / `a2a_peer_headers_members_dropped` ——
1534
+ 调用方只看到续跑成功,而 MCP 请求没带它写的鉴权头;这两行 warn 本版退役。出路:续跑请求带 `mcpServers` / `a2aPeers` 的合形整表(下一条「续跑时换表」;判的是盖过这张表之后的体),或取消那只任务、按本版的形重提。新提交上两形都 `400 request.field_invalid`(同一只判)。
1502
1535
  - **续跑时换表(7.143.0 起)**:停着的任务续跑(`POST /v1/approvals/:sessionId/decide` · `POST /v1/assistant/tasks/:id/plan_review` · `POST /v1/assistant/tasks/:id/resume` · `POST /v1/sessions/:id/wake`,各续各的门:工具审批卡 / `plan_review` 门 / 资源限停驻 / `task_done` 停驻)默认重放提交时存下的 `mcpServers` / `a2aPeers`;续跑请求体带上同名键(与提交体同形、同一只判官)就以它为准,
1503
1536
  并写回成之后续跑的表 —— 用户移除过一台 MCP 服务器之后,壳送此刻的整张表,续跑就不再重连那一台(停在那一台调用上的任务由引擎判:引擎 7.61.0 起三码各答各形,以 core 为准:那只工具不在了 ⇒ `resume.tool_unavailable`(引擎重开检查点:卡仍 pending、可再决);同名而已不是批准时那一只 ⇒ `resume.tool_contract_mismatch`(同样重开);还是那一只而它的面或声明变了 ⇒ `resume.target_moved`(这次调用不执行、批准用掉,须重新发起),本服务逐字过境);直连门部署的两道回决门拒收这两键(契约 §4e-bis)。
1504
1537
 
@@ -1654,17 +1687,28 @@ ENGINE_HANDOVER_LINGER_MS=1800000 # 可选,默认 30 min,下界 60000(壳经 POS
1654
1687
  两键都缺席 ⇒ 整件不装配,零定时器。
1655
1688
  - **自退判据 = 三合取,且必须连续满**「当前窗」(窗长由车道定,从上到下第一条成立者:交接 = `ENGINE_HANDOVER_LINGER_MS`;父没了 =
1656
1689
  `ENGINE_ORPHAN_LINGER_MS`;还没被碰过 = `max(ENGINE_LINGER_MS, ENGINE_FIRST_ATTACH_MS)`;其余(父还活着、已被碰过)= `ENGINE_LINGER_MS`;孤儿窗见上一节,「首次附着前的宽限」与「交接车道」见下面各自一条。判据一条,窗长按车道取):① 没有附着的 SSE 流;② 没有在飞的 run
1657
- (读的是**阻断集** `http + leader`,与排空等的、SIGHUP 空闲窗同一个读数;`/health` 的 `inflight` 是它再加会话驻留的后台 shell / monitor 的**总数**,见下一条);③ 没有非探针 HTTP 触点(`/health`、`/readyz`、`/metrics*` 不算 ——
1690
+ (读的是**阻断集** `http + leader + backgroundAgents` —— 含活过起它的那条腿、还在跑的后台子代理 —— 与排空等的、SIGHUP 空闲窗同一个读数;`/health` 的 `inflight` 是它再加会话驻留的后台 shell / monitor 的**总数**,见下一条);③ 没有非探针 HTTP 触点(`/health`、`/readyz`、`/metrics*` 不算 ——
1658
1691
  探针不是「有人在用」),**且**最后一条附着流断开也已满窗。任何一条不满足即把计时**清零**。
1659
1692
  (最后那半句不是修辞:一条活得比 15s 拍频还短的流整个生命周期都落在两拍之间,自查腿从没看见过它 ——
1660
1693
  只有在**关闭点**记一笔离场时刻,「已经没人附着满 N 秒」才是精确的。)
1661
- - **`/health.inflight` 的两个事实(7.146.0 起)**:模型在会话里 `run_in_background` 起的 shell / monitor 活过起它的那一条腿 —— 此前 `/health.inflight` 对它们读 0,读 `/health` 判「引擎是否空闲」的判官只好另拼谓词。
1662
- 现在 `/health.inflight` 是**总数**:这台引擎此刻托管着多少活,把仍在跑的、会话驻留的、**没声明 retain** 的后台 shell / monitor 一并数进去;分项在 `/health.inflightBreakdown { http, leader, sessionResident }`,`inflight` 恒等于三项之和。
1663
- **判停读的是另一个和 —— 阻断集 `http + leader`**(还有没有必须等完的活):自退、排空、SIGHUP 空闲窗都读它,驻留 shell 只报、不阻断,所以**判停行为与 7.145.x 逐字同**
1664
- (壳没了且没人附着时引擎照退,驻留进程随受控退被收掉;一只不退出的 `while true` 不会让孤儿引擎永生,也不会让排空等满窗)。
1665
- **谁受影响**:把 `inflight` 当「HTTP 腿数」读的监控面(数值会比 7.145.x 大,原口径 = `inflight − inflightBreakdown.sessionResident` = `http + leader`)。**补偿**:分项键;要不要因为引擎托管着驻留 shell 而推迟换代 / 回收由判官自己定。
1694
+ - **`/health.inflight` 的两个事实(7.146.0 起;后台子代理 7.147.0 起)**:模型在会话里 `run_in_background` 起的 shell / monitor 与(单用户形下)后台子代理活过起它的那一条腿 —— 此前 `/health.inflight` 对它们读 0,读 `/health` 判「引擎是否空闲」的判官只好另拼谓词。
1695
+ 现在 `/health.inflight` 是**总数**:这台引擎此刻托管着多少活,把仍在跑的后台子代理与仍在跑的、会话驻留的、**没声明 retain** 的后台 shell / monitor 一并数进去;
1696
+ 分项在 `/health.inflightBreakdown { http, leader, backgroundAgents, sessionResident }`,`inflight` 恒等于四项之和。
1697
+ **判停读的是另一个和 —— 阻断集 `http + leader + backgroundAgents`**(还有没有必须等完的活):自退、排空、SIGHUP 空闲窗都读它。后台子代理在内 —— 它是一条在干活的模型腿,
1698
+ 壳没了、没人附着时引擎等它干完再退(7.147.0 起;此前随受控退被中断);停在审批门上的子代理不计(耐久停驻,下次起来仍可续)。驻留 shell 只报、不阻断
1699
+ (驻留进程随受控退被收掉;一只不退出的 `while true` 不会让孤儿引擎永生,也不会让排空等满窗)。活过起它的腿的 workflow 不计(任务表分不出它在干活还是在等审批)。
1700
+ **谁受影响**:把 `inflight` 当「HTTP 腿数」读的监控面(数值变大,原口径 = `http + leader`);排空会多等还在跑的后台子代理(上界仍是排空期限);一只挂死的后台子代理撑着孤儿窗 / 租约窗,至多一个停滞窗(下一条)。
1701
+ **补偿**:分项键;`POST /v1/sessions/:id/children/retire` 结清以该会话为根的后台子代;要不要因为引擎托管着驻留 shell 而推迟换代 / 回收由判官自己定。
1702
+ - **后台子代理的停滞收割(`BG_AGENT_STALL_MS`,7.147.0 起)**:读在飞数之前(判停的每一拍、`/health`),引擎先把**最后一次工具活动**距今超过这个窗的会话驻留后台子代理收掉 ——
1703
+ 行落 `killed`、归因 `system`、它的 run 中止,运维日志一行 `background_agent_stall_reaped`。于是一只不再有进展的子代理(挂死的,或收尾时引擎等的那次账本写挂住的)最多撑住自退窗一个停滞窗。
1704
+ 「静默」只按工具活动算:子代的一次工具开始 / 结束是一拍;一轮很长的模型输出、一条很长的工具调用、在审批 / 问答卡上等人,在窗内都只是静默。
1705
+ 缺省 = 下限 = 本部署等人的四个窗取大(`STREAM_ASK_WINDOW_MS` / `UNREACHED_ASK_TTL_MS` / `QUESTION_TTL_MS` / `MCP_ELICITATION_TTL_MS`)+ 引擎单命令上限(`BASH_MAX_TIMEOUT_MS`,缺省 600 s);
1706
+ 单用户形 = 24 h(活审批卡窗)+ 10 min。**只许拉大**:写得比下限小(含 `0`)⇒ 钳到下限并在起服 warn 点名 —— 比本部署自己允许的最长等人还短的窗,会把一只等你回来批的子代当僵尸收掉;
1707
+ 想让僵尸收得更快,调小那几个等人窗(没开审批 / 问答的部署,那几个窗本来就不起作用,调小它们没有别的代价)。
1708
+ **谁受伤**:单步静默超过窗的合法子代会被收 —— MCP 调用(引擎时限缺省约 27.8 h,调用中的进度通知不算活动)、前台嵌套委派(子委派里的工具活动与等人都不给它打拍)、
1709
+ 一轮极长的模型输出;引擎按墙钟量静默,墙钟前跳或整机睡眠超过窗,醒来第一读就会收掉睡前之后再没打拍的子代。补偿 = 把 `BG_AGENT_STALL_MS` 拉大。
1666
1710
  读数只会少数、不会多数:枚举的会话集是本进程提交过的会话(LRU 上界 4096、删会话即清),被挤出的会话、不经提交链的内部车道起的驻留行、删会话级联中途失败的会话不可见。
1667
- 某个会话的驻留行读失败时按 1 只在跑计(读不出 ⇒ 非空闲)并记一行 `session_resident_background_unreadable`(每会话一次),读口本身不抛。
1711
+ 某个会话的任务表读失败时按 1 只在跑的后台子代理计(读不出 ⇒ 非空闲,判停也看得见)并记一行 `session_resident_background_unreadable`(每会话一次),读口本身不抛。
1668
1712
  - **「run 在跑就不退」是结构性保护,没有旋钮可以关掉它** —— 最后一个壳关掉、后台 run 还在烧的场景里,
1669
1713
  引擎必须活到那条 run 落地。
1670
1714
  - **`ENGINE_LINGER_MS` 防的是壳重启窗**:壳崩了/重启的 1-3s 里附着数确实是 0,窗太短就在用户眼皮底下
@@ -1791,7 +1835,8 @@ invalid_using_default` 警告 + 退回缺省值」这条臂随词表放宽一并
1791
1835
  **「剥了什么」这一行**:起服(host 腿语言服务器管理器构造时)一行 `warn` `secret_env_scrubbed`,`site:"server.lsp.spawn-env"`,
1792
1836
  字段 `keys`(这次剥掉的键名)· `allowed`(当前放行名单)· `hint`(出路句,点名 `LSP_ENV_ALLOW=NAME,NAME`)· `summary`;**值永不进日志**。
1793
1837
  放回的键不算剥除(不在 `keys` 里、不进计数 `secret_env_scrubbed_total`);同一个键每进程只报一次;没有剥除 ⇒ 不发。
1794
- 其余剥除现场(模型可驱动的 host shell `server.remote-env-host.spawn-env`、三条 git 腿、local-docker / adb 两条 CLI 腿、settings hooks 的命令腿)没有部署级放行旋钮,它们的同名行照旧是 `info`、不带 `allowed` / `hint`。
1838
+ 其余剥除现场(三条 git 腿、local-docker / adb 两条 CLI 腿、settings hooks 的命令腿、本服务自己的 git worktree 基环境 `server.remote-env-host.git-base`〔它从不交给引擎〕)没有部署级放行旋钮,它们的同名行照旧是 `info`、不带 `allowed` / `hint`。
1839
+ **模型可驱动的 host shell(`site:"server.remote-env-host.spawn-env"`)不写这一行**:它扣了哪些变量名由引擎说,不再有第二个读者 —— 根腿第一次调 shell 工具时一条通告 `inherit_env.names_withheld`(`detail.names` 全部被扣的名、`detail.ownCredentials` 其中本部署自有凭据那一半,每会话每名字集一次〔引擎的去重记录有界,尽力而为〕;委派子代调 shell 不出这条;**只给名字、从不带值**),运维在服务日志的结构化 `engine_notice` 行里读同一条;计数 `secret_env_scrubbed_total{site="server.remote-env-host.spawn-env"}` 照记(不带名字)。按这条腿的 `secret_env_scrubbed` 行告警 / 过滤的运维脚本改读 `engine_notice` 行(`code` = `inherit_env.names_withheld`)。
1795
1840
  现场按**腿**命名(`server.<模块>.<腿>`),不按继承词:7.131.x 及以前 host shell 那条腿叫 `server.remote-env-host.inherit-scrub`,7.132.0 起改名(计数器 `secret_env_scrubbed_total{site}` 与日志字段 `site` 同改,无别名)。
1796
1841
 
1797
1842
  **每一条宿主子进程腿的 env 都经同一只组合(7.132.0 起射程写全)**:本服务在宿主上起的每一只子进程,env 都由引擎按这条腿的继承词组合,本服务自己的凭据(起服声明的名与值)任何词都不交。
@@ -1817,7 +1862,7 @@ invalid_using_default` 警告 + 退回缺省值」这条臂随词表放宽一并
1817
1862
  交给模型 shell(能力位 `inherit_env.own_credentials_withheld` = 最近一次声明 ⊇ 当前生效配置应声明的集合;真要让模型 shell 持某枚令牌 ⇒ 放在引擎进程环境里一个不在配置目录、也不是任何模型条目 `apiKeyEnv` 的名下,配 `inheritEnv:"all"`;7.146.0 起请求 `settings.env` 不收凭据,见 9.5j);这些变量此刻的**值**也交给引擎按值扣(同值换名的副本同样进不了模型 shell),引擎判某个值「不是凭据值」(空白 / 占位词 / 一个字符重复 / 不足 8 字符)⇒
1818
1863
  那一个值不按值扣(名照扣、不拒启,能力位 `inherit_env.own_credential_values_withheld` 答 false),起服与模型目录换代时每个这样的变量出**一条** warn 结构化行 `own_credential_value_not_withheld { name, reason }`
1819
1864
  (`reason` 是引擎的拒句原文,从不回显值;起服第一步 logger 还没建时,这一行当拍以同一行形写 stderr,不缓冲;logger 接上之后——模型目录换代并进的条目的值被拒——这一行受 `LOG_LEVEL` 约束,`error` 级读不到,机读事实读上面那个能力位)—— 按事件名告警,给该变量一个真值即可。值是**网关密钥**(`MODEL_API_KEY` / `ANTHROPIC_API_KEY`,起服交给模型适配器的那一个)时,进程输出里在这一行之外还有**引擎自己**的一行裸文本(`a credential the engine was handed is …`):引擎在适配器构造口收下密钥的那一刻对同一个值说一次,这个收值口没有通告席,落引擎自己的 `console.warn`(引擎已知限制;本服务不是它的写者,也不改写它);给密钥一个真值两行一起消失。`"scrub"` / 缺席 = 上面的缺省剥除。🔴 **`"all"` 只在单用户部署兑现**:`REQUIRE_PRINCIPAL=true`
1820
- ⇒ 422 `request.host_posture_refused`(射程与写容纳面那条腿同一只谓词,`TASK_WRITE_FACE_ESCALATION` 不管它);非 host 车道带 `"all"` ⇒ 400。词表外的值(`"ALL"` · `""` · 名单 `["PATH"]` · 布尔 · `null` …)、非 host 车道带 `"all"`、部署不接 authorizer 时带 `"all"` 这三只都是 `400 request.field_invalid`,7.137.0 起体另带 `field: "inheritEnv"` 与 `reason`(`"unknown_word"` = 是字符串但不在词表;`"not_a_string"` = 根本不是字符串;`"lane_not_host"` = `"all"` 落在非 host 车道;`"no_authorizer_session"` = `"all"` 而部署不接 authorizer),壳按这两位认拒因、不解析句子(句子 / 码 / 状态不变;续跑腿同判,折叠 409 随体带同一组)。托管部署的 `422 request.host_posture_refused` 不在这组:它说的是「这台部署的姿态不给」,不是这个键写错,按 `errorCode` 认、不带这两位。部署方
1865
+ ⇒ 422 `request.host_posture_refused`(射程与写容纳面那条腿同一只谓词,`TASK_WRITE_FACE_ESCALATION` 不管它);非 host 车道带 `"all"` ⇒ 400。词表外的值(`"ALL"` · `""` · 名单 `["PATH"]` · 布尔 · `null` …)、非 host 车道带 `"all"` 这两类都是 `400 request.field_invalid`,7.137.0 起体另带 `field: "inheritEnv"` 与 `reason`(`"unknown_word"` = 是字符串但不在词表;`"not_a_string"` = 根本不是字符串;`"lane_not_host"` = `"all"` 落在非 host 车道;7.147.0 起不再有第四词 `"no_authorizer_session"` —— 一次提交的根会话恒由 authorizer 交出),壳按这两位认拒因、不解析句子(句子 / 码 / 状态不变;续跑腿同判,折叠 409 随体带同一组)。托管部署的 `422 request.host_posture_refused` 不在这组:它说的是「这台部署的姿态不给」,不是这个键写错,按 `errorCode` 认、不带这两位。部署方
1821
1866
  **没有**旋钮能替调用方打开它,也没有旋钮能在单用户部署上关掉它(单用户部署的用户把他自己的环境交给他自己驱动的 shell);起服一行 info
1822
1867
  `task_inherit_env_honored {honored}` 告诉你这台机器认不认。上面「部署级名单不给模型驱动 shell」那条不变 —— 两件事是两条轴(部署名单 vs 调用方 × 单用户射程)。
1823
1868
 
@@ -1827,8 +1872,8 @@ invalid_using_default` 警告 + 退回缺省值」这条臂随词表放宽一并
1827
1872
  `MAX_NEW_TOKENS` / `MAX_COMPLETION_TOKENS` / `MAX_BATCH_PREFILL_TOKENS` / `DB_FOREIGN_KEYS` / `JSON_SORT_KEYS` 需要点名放行,现在不用)。
1828
1873
  **精确名,不是形**:`USERS_MAX_NEW_TOKENS`、`MAX_NEW_TOKENS_2` 这类装饰过的变体照剥。引擎自己的「剥了什么」通告码
1829
1874
  `config.secret_env_scrubbed` 只在引擎**自己**剥的子进程上发;本服务九条宿主腿(host shell / project-memory / center-plugins / leader 三条 git 腿 / 语言服务器 /
1830
- bake-runner / local-docker 与 adb 两条 CLI 腿 / settings hooks 命令腿)都交本服务组合好的环境,所以运维面看的是上面这行
1831
- `secret_env_scrubbed`,不是那只通告码。这只通告码**每进程一次**、引擎里几处剥除共用一闩(工具 shell / 语言服务器 / 7.103.2 起还有 `runSpec` 的
1875
+ bake-runner / local-docker 与 adb 两条 CLI 腿 / settings hooks 命令腿)都交本服务组合好的环境,所以运维面看的不是那只通告码:除 host shell 外的各条腿看上面这行
1876
+ `secret_env_scrubbed`,host shell 腿看 `engine_notice` 行里的 `inherit_env.names_withheld`(上一段)。这只通告码**每进程一次**、引擎里几处剥除共用一闩(工具 shell / 语言服务器 / 7.103.2 起还有 `runSpec` 的
1832
1877
  oracle 门子进程,core 7.33.8):oracle 门若先于工具 shell 剥,通告只进 oracle 门那一处(`RunSpecOptions.onNotice`,没接就进 console),接在执行环境上的
1833
1878
  结构化通告收不到。本服务不调 `runSpec`,不受影响;嵌入引擎、自接 `runSpec` 的宿主给它交同一只 `onNotice`。
1834
1879
  **OTLP 头变量一族(7.103.2 起,core 7.33.7)**:`OTEL_EXPORTER_OTLP_HEADERS` 与按信号分的 `_TRACES_` / `_METRICS_` / `_LOGS_` / `_PROFILES_HEADERS`、
@@ -2123,13 +2168,14 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
2123
2168
  | `GET /v1/tool-approvals/<approvalId>` | 按问读口(**7.133.0+**;与 `POST …/respond` 同门同身份,不是你的 ask 与查无同形 404;**7.136.0+** 两扇决议口 —— `POST …/respond` 与耐久决断口 `POST /v1/tasks/<taskId>/asks/<askId>/decision` —— 与本口同一只属主判据:有行的问读行上当下的属主,收编之后新属主三口都认、前属主三口都 404;本口在店读不出时只对本副本自己铸的活登记回落登记(登记属主认你 ⇒ `pending`),起服接管的登记不回落、答 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`:续跑腿在被批准的动作开跑前失败时,引擎重开同一张门 —— 读口答 `parked` + `decidePath` 并另带 `reopened`(**7.135.0+**):`reason` 是引擎的重开因由词(`env_failed` 按原决议经 `decidePath` 再交一次 —— 本副本还留着首次受理回执时(缺省 1 小时内)`…/respond` 回放那张回执、不驱续跑;`tool_unavailable` / `constraint_rejected` 重新决;`config_refused` 改好配置再交或重新决),`decision` / `decidedAtMs` / `decisionNote?` 是重开之前的那次决议(读不出 ⇒ 只带 `reason`);再决之后回到 `decided`。续跑被取消 ⇒ 重开的门被收掉,读作 `void`。全文见契约 §4a-bis-read |
2124
2169
  | `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) |
2125
2170
  | `GET /v1/capabilities/scenarios/<name>` | 单个场景的只读详情:工具面、提示词概览、**本部署现在跑不跑得动**、附件供给、**技能清单**(见下) |
2126
- | `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 没有这两键,按缺席读。中心声明里有两台归一后挂在同一个工具命名空间前缀下(`my.srv` / `my_srv` 同挂 `mcp__my_srv__`)⇒ **7.143.0+** `409 config.mcp_server_name_collision` + 引擎原句(点名两个原拼写;此前无码 `500`),与同一份声明集在 `POST /v1/tasks` 上的答复同码、同状态、同句 —— 出路 = 改配置里的名字。自带 `.mcp.json` 的探测口是 `POST /v1/capabilities/mcp/probe` |
2171
+ | `GET /v1/capabilities/mcp[?scenario=<场景名>]` | **无需先跑 run 的逐台 MCP 状态**(现连、列工具、关掉;按调用方限速 + 30 秒复用)。答的是**某个场景**会挂哪些中心声明的 MCP 服务器:不带参数 = 这位主体 run 时的场景(**7.131.0+**:中心按主体指派的场景胜出,否则部署缺省场景;此前恒读部署缺省场景;指派悬空 ⇒ `503 state.scenario_assignment_unknown`,与提交任务时同一句;**7.147.0+** 部署接了中心而中心答不出这位主体的裁定(不可达 / 超时 / 答复读不出 / 指派两键形不对)⇒ 点名与不点名同答 `503 state.scenario_assignment_unavailable`(体无 `detail`、带 `permanence: "transient"`),与提交任务时同一句,不再按「无治理」读部署缺省场景;`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 没有这两键,按缺席读。中心声明里有两台归一后挂在同一个工具命名空间前缀下(`my.srv` / `my_srv` 同挂 `mcp__my_srv__`)⇒ **7.143.0+** `409 config.mcp_server_name_collision` + 引擎原句(点名两个原拼写;此前无码 `500`),与同一份声明集在 `POST /v1/tasks` 上的答复同码、同状态、同句 —— 出路 = 改配置里的名字。自带 `.mcp.json` 的探测口是 `POST /v1/capabilities/mcp/probe` |
2127
2172
  | `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` 已退役。 |
2128
2173
  | `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 原文。 |
2129
2174
  | `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 起 / 本机 `config.d` 里读不出的文档,门控域在内)是这一拍的结论,不是失败:照样 200,`targetVersion ≠ appliedVersion`,拒因在 `config_candidate_rejected` 日志行;拍真的失败(配置源不可达 / 读不了 / 应用抛错)⇒ `502 internal.config_refresh_failed`。本部署没有配置管道(纯 env worker:既没配 `CONFIG_CENTER`、也不是 `CONFIG_PROVIDER=local`)⇒ 409 `config.refresh_unavailable` 指路,不会回一个「触发了但什么都没发生」的 200。 |
2130
2175
  | `POST /v1/adoption/<adoptionId>/sessions` `{"sessions":[{"clientSessionId":"…","entries":[…]}]}` | **收编批量导入**(**7.146.0+**;能力位 `adoptionImport`,按调用方):收编的**源主体本人**把本机会话导进那条收编的目的主体名下(operator 不能代,请求体不能点名目的地);导进来的是只读镜像,在云端接着跑先 fork。`adoptionId` 从 `GET /v1/capabilities` 的 `adoptionImport.adoptionId` 读(只对源主体本人在场,另带两只帽 `maxSessions` / `maxSessionBytes`)。云端会话 id 由 `(adoptionId, clientSessionId)` 确定性派生 ⇒ 重投安全。200 + 逐元素结局 `imported` / `already_imported` / `rejected{code: too_large \| invalid_envelope \| duplicate_client_session_id}`;信封恰好两键,多一个键(如 `cwd`)⇒ 这个元素 `invalid_envelope`。整请求拒:401 / 501 `capability.adoption_store_required` / 404 `not_found.adoption` / 403 `auth.adoption_source_only` / 409 `adoption.not_adopted` / 400(体形、元素数超帽)。帽 = `ADOPTION_IMPORT_MAX_SESSIONS`(缺省 100)/ `ADOPTION_IMPORT_MAX_SESSION_BYTES`(缺省 6 MiB)。契约 §26 |
2131
2176
  | `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 **持续**时长;收敛时键缺席)—— 编排器据此摘掉「配置持续落后」的副本 |
2132
2177
  | `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?, sessionId? }] }`,`retained:true` = 声明了保留的活服务,正常退出不会停它;`sessionId` = 后台 agent 行的子代会话 id,绑定之后才在);停止口「软 → 等一次 `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 |
2178
+ | `POST /v1/sessions/:id/rewind-files` `{"to":"<提问的 taskId>","dryRun":true\|false}` | **run 外只恢复代码**(**7.147.0+**;能力位 `sessionRewindFiles` / `sessionRewindFilesPreview`,老 server 404):不跑模型、不分叉对话,把这只会话被代理改过的文件收敛到那条提问开始时的样子(`to` 与提交体 `rewindFilesTo` 同一个手柄,会话视图消息行的 `messages[].taskId`)。会话属主或运维可调;只在 `DB_BACKEND=local` 且工作区活过一次执行的部署上开(mysql / pg 多副本共用会话店、`local-docker` / `e2b` / k8s / 多租 host ⇒ 能力位假、口 501);同会话有 run 在跑或停在审批上 ⇒ `409 conflict.session_active_run`(带 `activeTaskId`,等它结束或取消再发);引擎进程内仍有在途工作 ⇒ `409 rewind.session_busy`;那条提问没有可用的文件边界 ⇒ `422 rewind_snapshot.unresolvable`;未知手柄 ⇒ `404 rewind_files_to.unknown_event`;host 车道上 server 重启之后、这只会话下一条带 `cwd` 的提交之前,或登记的目录已被移走 ⇒ `409 conflict.session_workspace_unknown`(本进程不知道它的工作目录,先发一条带 `cwd` 的提交再回退);只读镜像会话 ⇒ `409 conflict.session_mirror`。取执行环境时不物化会话附件(预览与真回退都不改写 `attachments/`)。`dryRun:true` ⇒ `{ dryRun:true, preview }`:`preview: null` = 这台给不出统计(`sessionRewindFilesPreview` 为假,今天 SQL 后端如此),**不是**「什么都不会变」;`dryRun` 缺席 / `false` ⇒ `{ dryRun:false, restore }` 台账,`outcome` 是 `complete` 或 `partial`(`partial` 带每一个被拒 / 失败的文件,原样再发会继续收敛)。没有 `acceptPartialRestore`(带了 ⇒ 400 `request.body_shape`)。不计费;无凭证部署上与提交面同一道门 |
2133
2179
  | `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「探针」 |
2134
2180
  | `GET /metrics` | Prometheus 指标(有 token 时需带) |
2135
2181
 
@@ -2153,8 +2199,11 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
2153
2199
  > **名单为空 = 没有人是 operator**(每个调用方只看自己的待批 —— 不带主体的调用方看到的是匿名卡〔不带主体提交的任务停下的那几张,即决断口对它开放的那几张〕,`?owner=` 不生效;7.133.0 之前审批四个读面在名单为空时把每个调用方当 operator,那条读法已删)。
2154
2200
  > **单用户部署当自己的 operator**:`OPERATOR_SINGLE_USER=true` = 「这台部署的每个调用方都是 operator」,对全部运维面生效;只在 `REQUIRE_PRINCIPAL` 未开且 `OPERATOR_PRINCIPALS` 为空时成立(与任一同在 ⇒ 拒启);
2155
2201
  > 没配服务凭据的部署上能连上端口的都是 operator;不带主体的 operator 没有身份可记,记「谁改的」的写面(`PUT /v1/admin/memory-optout[/:principal]`)要带主体(否则 401)。
2156
- > 部署形不是输入:单用户部署上 `GET /v1/diagnostics/wiring` 与 `GET /v1/outcomes` 也只认运维 —— 名单空且没声明 ⇒ 两口 501 `capability.operator_principals_required`,具名名单在单用户部署上同样生效(此前诊断面在名单为空的单用户形上、outcomes 在任何单用户形上都不问名单)。
2157
- > 单用户 + 名单空 + 没声明 ⇒ 起服 warn `operator_undeclared_single_user`(不拒启;句子点名这两口与审批队列,出路 = 一行 `OPERATOR_SINGLE_USER=true`)。
2202
+ > 部署形不是输入:单用户部署上 `GET /v1/diagnostics/wiring` 与 `GET /v1/outcomes` 也只认运维,具名名单在单用户部署上同样生效。
2203
+ > 🔴 **单用户部署必须声明谁是运维**:`REQUIRE_PRINCIPAL` 未开、`OPERATOR_PRINCIPALS` 为空、`OPERATOR_SINGLE_USER` 不是 `true` ⇒ 装配运维面时拒(服务进程起服拒;直接调 `createHttpServer` 的嵌入方同拒;不变量 `operator-declaration-required`),
2204
+ > stderr 一行拒句点名两条出路 —— `OPERATOR_SINGLE_USER=true`(每个调用方都是运维)或 `OPERATOR_PRINCIPALS=<主体,…>`(只有点名的主体);不止一个人调用的 worker 是多租,设 `REQUIRE_PRINCIPAL=true`。
2205
+ > 没有旋钮、没有宽限、没有缺省;`OPERATOR_SINGLE_USER=false` 与没写同判。`sema-up --users=single` 与 chart(`server.requirePrincipal` 未开)渲 `OPERATOR_SINGLE_USER=true`。
2206
+ > 不挂 HTTP 面的 `run-local` 不判这一条;运维面 501 `capability.operator_principals_required` 从此只出现在「多租 + 空名单」上。
2158
2207
  > ⚠️ **多租户形拒启**:`DURABLE_APPROVAL=true` + `REQUIRE_PRINCIPAL=true` 而 `OPERATOR_PRINCIPALS` 空 ⇒ 进程启动失败并点名修法(空名单下没人能当这台多租 worker 审批队列的 operator)。设名单,或确属单租户则不设 `REQUIRE_PRINCIPAL`。
2159
2208
  > 🔒 **停驻任务续跑上下文里的凭据是密封的(7.134.0 起)**:任务停驻时存下的提交体里,请求自带的凭据(`mcpServers[].transport.headers` / `.env` 的值、`a2aPeers[].headers` 的值、`settings.env` 的值、
2160
2209
  > `settings.webSearch.apiKey`、http hook 条目的静态头 `settings.hooks.<事件>[].hooks[].headers` 的值、command hook 条目的插件选项 `settings.hooks.<事件>[].hooks[].plugin.options` 的值〔7.135.0 起〕、子进程的命令行 —— stdio 传输的 `mcpServers[].transport.command` / `.args` 与 command hook 条目的 `settings.hooks.<事件>[].hooks[].command` / `.args`〔7.136.0 起;`args` 整只数组封成一个值〕)只以封签落盘(对着这一域自己的保管钥匙 `<数据根>/sealed-key/checkpoint-ctx-keys/` 最新那把公钥封、
@@ -2182,7 +2231,7 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
2182
2231
  > 治理拍),锁上会让**每一条**任务在引擎门口被拒,而那把锁本就无物可守(策略已是部署所有、请求放松不了);
2183
2232
  > ②`mcp` + 部署自己配了 MCP 服务器——部署的服务器走同一个字段,锁上同样每条腿必拒。两者都在**启动期**点名拒,
2184
2233
  > 不留给运维在每条任务上猜。
2185
- > 同一条判据也管 A2A:`LOCKED_CONFIG_KEYS` 含 `a2a`,或合规档位禁 `a2a_peers`,而部署自己配了 A2A 对端(中心 `a2a` 域)⇒ 同样在启动期点名拒。
2234
+ > 同一条判据也管 A2A:`LOCKED_CONFIG_KEYS` 含 `a2a`,或合规档位禁 `a2a_peers`,而部署自己配了 A2A 对端(中心 `a2a` 域,或本地 `config.d/a2a.json`,见 9.5i-bis)⇒ 同样在启动期点名拒。
2186
2235
  > - `RETENTION_MAX_AGE_DAYS=<非负数>`:托管留存**视界**(天;`0` 合法=立即可删)。坏值(非数/负数/设了但为空)
2187
2236
  > 一律拒启——删数据的旋钮不许 NaN 流通。**它只是"保留多久"这句声明**;真正按它删数据的是下面两根。
2188
2237
  > - `RETENTION_SWEEP_INTERVAL_SEC=<非负整数>`(默认 `0` = **关**;上限 `2147483` 秒 ≈ 24.8 天):
@@ -2207,7 +2256,7 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
2207
2256
  > - 能力位 `capabilities.retention` = `{mode, maxAgeDays}`(lane 开着)或 `null`(关着)。
2208
2257
  > - **子代转录(placed 分区)的留存天数没有独立旋钮座**(设计判定 2026-08-30):placed 转录的
2209
2258
  > 删除权**独家**归联合 reap(`reapDurableAgents`:行赢删才 release 转录;上面这条 lane 的候选谓词显式排除
2210
- > placed,见 `plugins/retention-store-sql.ts` 第五腿)——生效视界 = `BG_AGENT_RETENTION_MS`(缺省 7 天,
2259
+ > placed,见 `stores-sql/retention-store-sql.ts` 第五腿)——生效视界 = `BG_AGENT_RETENTION_MS`(缺省 7 天,
2211
2260
  > 毫秒形,同时是 agent 行的留存旋钮;分区孤儿腿的 `olderThanMs` 同源)。core 的
2212
2261
  > `SUBAGENT_TRANSCRIPT_RETENTION_DAYS_DEFAULT`(30 天)是共享词表常量,**本服务未按它跑**;天数形专用
2213
2262
  > 旋钮要等 core 在 reap policy 开出转录独立年龄座后再铸(届时名 `SUBAGENT_TRANSCRIPT_RETENTION_DAYS`)。
@@ -2313,8 +2362,8 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
2313
2362
  > 🔴 因此:**没配 `SERVICE_AUTH_TOKEN` 且未开 `ALLOW_UNAUTHED_WRITES` 的 worker 上这一口恒 503**,
2314
2363
  > 而能力位仍报 true(本族共有缺口,附录 B.1 已如实登记)。启动日志为此打一条
2315
2364
  > `memory_consolidation_run_credential_gated`;状态读那一口不受影响(它不烧模型)。
2316
- > 🔴 另一条启动告警:没有运维名册(`OPERATOR_PRINCIPALS` 为空且没有单用户声明 `OPERATOR_SINGLE_USER=true`)时两口恒 501 `capability.operator_principals_required`(没有运维名册 = 没有任何人是 operator),
2317
- > 日志里是 `memory_consolidation_valve_unreachable`。
2365
+ > 谁是运维:阀门开得起来的只有单用户 + file 记忆后端的部署,而单用户部署起服就得声明运维(`OPERATOR_SINGLE_USER=true` 或具名 `OPERATOR_PRINCIPALS`,
2366
+ > 见本文 `OPERATOR_SINGLE_USER` 段);具名名单外的调用方答 403 `auth.operator_only`。
2318
2367
  > - `GET /v1/admin/memory/consolidation[?scope=]` —— 状态投影(阀门/座位/scope 表/上一轮的账)。
2319
2368
  > - 能力位 `capabilities.memoryConsolidationDriver`(阀门上了场 ∧ 有运维名册:名单非空或单用户声明)。
2320
2369
  > - 逐键语义与停因闭集见 `docs/ASSISTANT-WIRE-CONTRACT.md` §10。
@@ -2519,7 +2568,7 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
2519
2568
  | HTTP `429` | 限流 | 看 `Retry-After` 退避 |
2520
2569
  | 任一 4xx / 5xx 的 `errorCode`(7.146.0 起查契约附录 A 的**码族**列) | 目录每个码恰属五族之一:`transient`(原样重发等一等可能成)· `deterministic`(重发改不了判,别自动重发)· `store_judged`(逐次由店判,读体键 `permanence`)· `pending_decision`(卡仍待决,读体键 `retriable`:它说「卡还在」,不说「重发会成功」)· `retention_expired`(留存已被收走,新开) | 「要不要重试」「要不要重读待决列表」「要不要新开」按这一列读,不自己抄码表(契约文档附录 A.1g;MIGRATION 第 17 条)。表外码(core 原码镜像)没有族,照旧看状态与前缀 |
2521
2570
  | HTTP `429` + `errorCode:"limit.rate_exceeded"` + `reason:"run_concurrency_cap"` | 本副本在飞 run 已到并发帽(`RUN_MAX_CONCURRENT` 全局,或 `RUN_MAX_CONCURRENT_PER_PRINCIPAL` 按提交主体;缺省关;新提交的四个口,幂等重放不吃) | 看 `Retry-After`(5 秒)/ 体 `retryAfterSec` 退避后重投;或等在飞的 run 收尾 / 调高帽 |
2522
- | HTTP `409` + `errorCode:"conflict.session_archive_stamped"`(7.144.0 起;`DB_BACKEND=local`;`POST /v1/sessions/:id/fork` 与会话同步导入的提交 `POST …/sync/import/:stagingId/entries`;7.145.0 起第一阶段 `POST …/sync/import` 对按 id 分类出 fork / stale 且没带同意的推送也直接答它,此前先答通用 `conflict`;7.141.0–7.143.0 答的码是 `conflict`) | 这只会话的存档仍带更早版本写下的属主戳(一次性迁移改写不了的,或跳过带迁移的版本升上来的数据根;起服 warn `local_archive_kept_stamped` 点名):不在它上面造新存档、不原地换它的日志(干净追加 / 载荷分叉 / fork / stale 各形的推送同答),什么都没写;读 / 续跑 / 拉取照常,「id 与内容都相同」的重推照常落定 | 按体里的固定句办:删掉这只会话(`DELETE /v1/sessions/:id`)再推。与暂存提交时属主变了的 `errorCode:"conflict"`(出路相反:从第一阶段重来)按码分开 |
2571
+ | HTTP `409` + `errorCode:"conflict.session_archive_stamped"`(7.144.0 起;`DB_BACKEND=local`;`POST /v1/sessions/:id/fork` 与会话同步导入的提交 `POST …/sync/import/:stagingId/entries`;7.145.0 起第一阶段 `POST …/sync/import` 对按 id 分类出 fork / stale 且没带同意的推送也直接答它,此前先答通用 `conflict`;7.147.0 起干跑 `POST …/sync/plan` 对这两形同样答它〔此刻读,提交里持锁的那一问仍是权威〕,此前答 `200` + `relation`;7.141.0–7.143.0 答的码是 `conflict`) | 这只会话的存档仍带更早版本写下的属主戳(一次性迁移改写不了的,或跳过带迁移的版本升上来的数据根;起服 warn `local_archive_kept_stamped` 点名):不在它上面造新存档、不原地换它的日志(干净追加 / 载荷分叉 / fork / stale 各形的推送同答),什么都没写;读 / 续跑 / 拉取照常,「id 与内容都相同」的重推照常落定 | 按体里的固定句办:删掉这只会话(`DELETE /v1/sessions/:id`)再推。与暂存提交时属主变了的 `errorCode:"conflict"`(出路相反:从第一阶段重来)按码分开 |
2523
2572
  | HTTP `429` + `errorCode:"limit.cost_quota_exceeded"` | per-principal 累计成本配额越顶(`MAX_PRINCIPAL_COST_USD`;**进场门**,不打断在跑的 run) | 看 `Retry-After` / 体 `retryAfterSec` 退避(体 `windowResetsAt` = 当代窗滚过的时刻);窗滚过或调高上限后放行 |
2524
2573
  | 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 …`),只作展示、别锚文案 |
2525
2574
  | 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` 分支,句子只作展示 |
@@ -3193,11 +3242,16 @@ WebSearch 工具的 `tool_result` 错误文本,回到模型的对话里,**不是
3193
3242
  ### 9.5c-ter `settings.hooks` 的键:写了就要被读,否则整份新提交 400(7.135.0 起)
3194
3243
 
3195
3244
  - 一只匹配组 / 一个条目上出现 hooks 读者不读的键 ⇒ 新提交(`POST /v1/tasks` · `/v1/tasks/stream` · `/v1/runs`)`400 request.body_shape`,体里 `unknownKeys` 逐个点名全坐标
3196
- (如 `settings.hooks.PreToolUse.0.hooks.0.onFailure`),与 `settings` 子树、请求体顶层的未知键同一种拒法。读不读以读者为准:组上是 `matcher` / `hooks` / `source`,
3197
- command 条目上是契约字段加 `plugin` / `args`,其余条目类型上是各自的契约字段(`plugin` / `args` 写在 http / prompt / agent 条目上同样被拒)。
3198
- - 常见撞上的形:参照实现 2.1.296 新增的 `onFailure`(本服务不执行这个键 —— 7.134.x 及更早它被悄悄剥掉,写了 `"block"` 的守卫起不来时照放行)、拼错的 `timout` / `matchr`。
3199
- 出路:删掉那个键,或改成读者读的拼法;拒体里的坐标就是要动的那一处。
3200
- - 停驻任务续跑时不判这一条(存量请求里的这些键照旧被剥掉、其余钩子照采纳)。
3245
+ (如 `settings.hooks.Stop.0.hooks.0.onFailure`),与 `settings` 子树、请求体顶层的未知键同一种拒法。读不读以读者为准:组上是 `matcher` / `hooks` / `source`,
3246
+ command 条目上是契约字段(含 `args` / `onFailure`)加 `plugin`,http 条目上是契约字段(含 `onFailure`),其余条目类型上是各自的契约字段
3247
+ (`plugin` / `args` 写在 http / prompt / agent 条目上、`onFailure` 写在 prompt / agent 条目上同样被拒)。每一型的契约字段读能力位 `settingsContract.hookEntryFields`。
3248
+ - 常见撞上的形:写错条目类型的键(`plugin` / `args` 写在 http / prompt / agent 上、`onFailure` 写在 prompt / agent 上、`continueOnBlock` 写在 prompt 之外、
3249
+ `rewakeMessage` / `rewakeSummary` 写在 command 之外、`cloud` 写在 command / http 之外)、拼错的 `timout` / `matchr`。
3250
+ 出路:删掉那个键,或改成读者读的拼法;拒体里的坐标就是要动的那一处。(7.135.0–7.146.x 上 command / http 条目的 `onFailure` 也在这里被拒;7.147.0 起它是契约字段、本服务执行它,见 9.5c-quinquies。)
3251
+ - 参照实现的四个键在各自的归属型上受理(7.147.0 起;此前在这里被拒):command 的 `rewakeMessage` / `rewakeSummary` / `cloud`、http 的 `cloud`、prompt 的 `continueOnBlock` ——
3252
+ **本服务如实带、不执行**:唤醒文本不加前缀、没有摘要行、`cloud` 写什么都照跑、`continueOnBlock` 不改 `Stop` 拦下的结局。`rewakeMessage` / `rewakeSummary` 要非空串、
3253
+ `continueOnBlock` 要布尔,值坏 ⇒ 与其它契约字段写坏同一种拒(守卫事件整份拒);`cloud` 任意值。
3254
+ - 停驻任务续跑时不判这一条(存量请求里读者不携带的键照旧被剥掉、其余钩子照采纳)。
3201
3255
 
3202
3256
  ### 9.5c-quater `settings.hooks` 的托管标签:调用方自报,服务端只校闭集、不验身份,失败方向只收紧(7.133.0 起)
3203
3257
 
@@ -3205,20 +3259,37 @@ WebSearch 工具的 `tool_result` 错误文本,回到模型的对话里,**不是
3205
3259
  由请求体里的调用方写,服务端只判它在不在闭集里(集外词 / 形错 ⇒ `400 request.field_invalid`,`field: "settings.hooks"`),**不验身份**,也不按调用方的档位剥掉、改写或拒绝托管词。
3206
3260
  本服务没有用户 / 项目 / 本地 / 托管层的设置文件:请求体 `settings` 是启动方(壳 / 嵌入方)端合并之后交来的那一份,所以「托管」今天的唯一来源就是壳 / 嵌入方的自报;
3207
3261
  `settings.hooks` 只在单用户车道被采纳(多租户车道整只忽略),能自报的就是持有这台部署凭据的调用方(壳 / 嵌入方)。不要把它读成「组织策略来源」的证明。
3208
- 标签只改两件事,失败方向**只收紧**:① 位置 —— 非托管相 → 托管插件相 → 托管策略相(终判相),托管条目在非托管改写之后的最终输入上判,托管相自己改写了输入则整相按新输入重判一遍;
3262
+ 标签只改两件事,失败方向**只收紧**(比的是下方 —— 同座非托管车道、运维 handler 与常规门:托管答案不比下方宽;与不带标签的同一条钩子逐员比则不全同,见本节末条):① 位置 —— 非托管相 → 托管插件相 → 托管策略相(终判相),托管条目在非托管改写之后的最终输入上判,托管相自己改写了输入则整相按新输入重判一遍;
3209
3263
  所以托管条目的 `allow` 绑定的是最终要执行的那份输入(一条非托管 `allow` 若排在另一条非托管改写之前,会因输入被改而作废;托管的不会 —— 它是改写之后才判的,效果等同于调用方把同一条钩子不带标签写在数组末尾);
3210
3264
  ② 失败方向 —— 托管条目没跑成、或判词不可信,`PreToolUse` 折 `ask`(非托管相里没跑成的条目照旧只是无意见;判词是闭集外的词则两相都折 `ask`);引擎 handler 预算、事件墙钟、prompt 条数、上下文份额各车道独立计量(每条车道一张注册)。
3211
3265
  **`allow` 不看相**:放行只看「判词是 `allow` ∧ 判词可信 ∧ 它判过的就是最终输入」,出口对象不带车道 —— 同一条钩子、同一个判词、同一份输入,自报 `managedPolicy` 拿不到任何额外放行(任务座之下有部署座链成员的站上同样如此 —— 见本节末条)。
3212
3266
  所以调用方给自己的钩子写 `source: "managedPolicy"` 不是抬权口,而是「请按失效关闭跑这一组、并让它最后判」的开关;不要这层严格,去掉 `source` 即可(缺席 = 非托管)。
3213
3267
  过渡(7.146.0 起,至引擎为钩子提供「请求收场」席位止):同一请求同时带托管与非托管的 Stop 钩组、且两车道意见相左(一边 `continue:false`、一边 `block`)时,`continue:false` 只撤本车道的推回,另一车道的 `block` 作数 —— 这是托管标签在 Stop 上会改变的第三件事。目标形 = 任一 `continue:false` 即请求钩子整体无意见(7.145.0 形,与 CC 一致),席位到货当版恢复并删本句。
3214
3268
  - **任务座之下有部署座链成员的站上,托管条目只许收紧**(7.146.0 起):运维声明的 mod 包(`MODS_ROOT` / `MODS_PACKAGES`)在 `classic.PreToolUse` 上以 `append` 档(排在请求钩子所在的任务座之后)挂了链成员
3215
- (观察者不算;读引擎的准入记录)⇒ 托管车道照旧最后判,但那一站上只许收紧:否决 / 问(含托管条目没跑成折的问)/ 上下文照收;托管的 `allow` ⇒ 这次调用拒(与不带标签的 `allow` 被引擎座信任律拒同一个结局,不落到常规门);
3269
+ (观察者不算;读引擎的准入记录)⇒ 托管车道照旧最后判,但那一站上只许收紧 —— 律是**托管答案不比下方(运维 handler 与常规门)宽**:否决 / 问(含托管条目没跑成折的问)/ 上下文照收;托管的 `allow` ⇒ 这次调用拒(与不带标签的 `allow` 被引擎座信任律拒同一个结局,不落到常规门);
3216
3270
  托管的 `updatedInput`(带 `ask` 的也算)给不进运维 handler(它只判过改写之前的那一份)⇒ 这次调用拒(失效关闭);运维 handler 要真人批的问,托管作答之后照旧要真人批。
3217
- 否则自报的托管组会等运维 handler 判完原输入、再在它之上放行或改写 —— 改写后的输入运维 handler 从没见过。在装了运维 mod 的部署上,覆盖站上的标签换不来任何额外放行(与不带标签同;与 7.145.x 同)。
3271
+ 否则自报的托管组会等运维 handler 判完原输入、再在它之上放行或改写 —— 改写后的输入运维 handler 从没见过。
3272
+ 运维 handler 在这一次调用的链上时,与不带标签的同一条钩子逐员比:放行 / 改写 / 否决同一个结局(与 7.145.x 同);问与只给上下文两员与不带标签不同 —— 不带标签的这两种答复内层挂、跳过下方没判过的运维 handler,被引擎座信任律拒(7.145.x 上带标签的同形同样被拒);
3273
+ 带标签的在运维 handler 判过之后作数:问照问(要人批),只给上下文照收、调用落到常规门(与不挂这条钩子同一个结局)。两员都不越过运维 handler 与常规门,所以覆盖站上的标签换不来越过下方的放行。
3274
+ 运维 mod 带匹配器而这一次调用不中时(见下一句),带标签的放行 / 改写照样拒、不带标签的作数(链上没有运维 handler);问与只给上下文两形下两者同结局 —— 只多收紧。
3275
+ 卡面:托管条目在下方的问(运维 handler 或同座非托管车道;不论下方的问带哪个成因词)之上也问 ⇒ 问句先列下方那一句、再列托管那一句(`… (a managed PreToolUse hook also asks: …)`);卡上的注册名与问因项(`askReasons`)由引擎按作答的那一张注册铸,托管车道作答时只点名 `settings.hooks.managed`(已知限制)。
3218
3276
  挂法不随站点变:同座的非托管车道与运维 handler 恒先判、否决恒跑 —— 运维 mod 只管某些工具(带匹配器)时,别的工具的调用上同样如此,那些调用上托管条目也按这一条收紧(准入记录不带匹配器;只多收紧,不跳过否决)。
3219
3277
  不带托管标签的组照旧:运维 handler 在这一次调用的链上时,它们的非否决答复被引擎的座信任律拒。
3220
3278
  只挂观察者的部署(本服务自铸的拒绝计量 / 工具追踪)、链成员只在任务座之上(`prepend` 档,或同档 `user` —— 同档里部署座在前)的部署不收紧:那里运维 handler 已先判、并在答回来的结果上再判。
3221
3279
 
3280
+ ### 9.5c-quinquies `onFailure`:钩子自己坏了时,放行还是拦下(7.147.0 起)
3281
+
3282
+ - 写在 command / http 条目上,两个词:`"continue"`(缺席同义)= 钩子坏了只报一笔故障、动作照常;`"block"` = 钩子坏了**按退出码 2 算** —— 它守的那个动作被拦下。
3283
+ 拦下的形就是这个事件上 exit 2 的形:`PreToolUse` 工具调用被拒、`UserPromptSubmit` 这次提交被拒、`PreCompact` 这次压缩不做;`PostToolUse` / `PostToolUseFailure` / `PostToolBatch`
3284
+ 上工具已经跑过,拦不了,那句话作为反馈交给模型。拦下的理由是一句 `<事件> hook did not reach a verdict (<成因>); blocking because onFailure is "block"`,成因词与故障帧的 `reason` 相同。
3285
+ - **「坏了」怎么算**(本服务比参照实现宽,失效关闭):起不来、超时、退出码非 0 非 2(http:非 2xx)、输出读不成判词、`PreToolUse` 的 `permissionDecision` 不是 allow / deny / ask 之一;
3286
+ 以及本服务自己的门让它没跑成 —— URL 名单拦下、shell 形写了 powershell、`if` 条件评不出、组的 matcher 编不过、这个事件的墙钟用完(墙钟用完之后同一车道里排在后面、被选中的 `block` 条目同样拦下,不看它排第几)。
3287
+ 不算坏:`once` 已经跑过一次、`if` 不匹配、matcher 没选中(本就不该跑)。
3288
+ - **不生效**:`async` / `asyncRewake` 的条目(后台跑,本就不拦);`Stop` 事件(拦停止会让该停的停不下);观察站 `StopFailure` / `PostCompact`(那里没有可拦的动作)。
3289
+ - 运维要看懂一次拦下:故障帧 / 运维日志照旧点名是哪一条、什么成因(`hook_non_blocking_failure` 帧的名字是历史名,`block` 条目的那一次随后就被拦下);改条目、改 URL 名单,或把这一条改回 `continue`。
3290
+ - 停驻任务:升级前存下的请求里写了 `onFailure: "block"` 的守卫,续跑时开始按 block 执行(7.146.x 及更早这个键被读者剥掉);存量里写的是两词之外的值 ⇒ 这条车道采纳请求体 hooks(单用户车道)时**拒续跑**
3291
+ `409 resume_blocked_by_policy`(`blockedBy: "request.field_invalid"`,`field: "settings.hooks"`;卡 / 任务仍待决;7.146.x 及以前是整份 hooks 不采纳照跑)—— 见 MIGRATION。
3292
+
3222
3293
  ### 9.5d MCP 服务器 http 传输的身份头:`principalHeader` 与 `assertionHeader` 二选一(请求体与部署声明同一种读法)
3223
3294
 
3224
3295
  `mcpServers[].transport`(`kind:"http"`)上有两个「用哪个头送身份」的可选键,**只能设一个**:`principalHeader`(runner 把该任务已认证的终端用户 principal 注入这个头)与 `assertionHeader`(托管连接器断言所用的头**名**)。
@@ -3232,7 +3303,7 @@ WebSearch 工具的 `tool_result` 错误文本,回到模型的对话里,**不是
3232
3303
  条目的受理键 = `name` · `transport` · `allowTools` · `toolAxes` · `toolFaces` · `contentOrigin` · `elicitation` · `source` · `startupTimeoutMs`(7.132.0 起)· `sessionMetaKey`(7.138.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`,兄弟照挂)。`sessionMetaKey`(声明了它的服务器在每次 `tools/call` 的 `_meta` 下收这条腿的会话 id)7.138.0 起同样在受理口判 —— 判官是引擎包根导出的 `judgeSessionMetaKey`(键名格式、保留前缀、上界都是它的):坏形 ⇒ `400 request.field_invalid`,原因句是引擎的那一句;耐久 resume 重放存量请求时坏形让这一条 server 整条不进。
3233
3304
  - **新提交**(`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`):点名成员与闭词表本身,调用方写的串(工具名这类键、闭词表拒的那个词)一律只给记号与码位数(`[redacted] (N characters)`),整句详情由引擎截到 240 字、本服务再剥控制与双向字符 —— 不回显显示文本、不回显合规的列表;`transport.url` 的值从不回显(带凭据的 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` = 没声明,照常受理。
3234
3305
  - **`transport.url` 不得带凭据(7.135.0 起)**:http 传输的 `url` 带 userinfo(`https://user:pass@host/mcp`,只有用户名也算),或带名字在凭据名表里的 query / fragment 参数(`?api_key=` · `?key=` · `?token=` · `#access_token=` …;表 = 本服务日志脱敏器认作凭据名的那一张,全表见契约附录 A `request.field_invalid` 行;名字大小写不敏感,按解一次百分号编码读)⇒ 新提交 `400 request.field_invalid`,体另带 `field: "mcpServers[i].transport.url"` 与 `reason`(`url_userinfo` | `url_credential_param`),原因句点名出路:**凭据改写进 `transport.headers`**(如 `"Authorization": "Bearer …"`)。为什么:任务停驻时提交体整份进续跑上下文,`transport.headers` 的值在那里只以封签存在,`url` 照原样存(日志与读面同理)。值从不回显;解析不了的 url 不在这一问里判(拨号时失败)。7.134.x 及以前照收、原样落停驻上下文;带 userinfo 的 url 本就连不上(fetch 对带凭据的 URL 构造即抛),带名表参数的升级后要改。同一条规则在 A2A 对端 / http 钩子 / 搜索后端 / 图片 URL 上的几座见 9.5g。
3235
- - **耐久 resume 重放存量请求**:不拒(拒会把升级前 park 的任务永久卡死;`transport.url` 带凭据的存量条目照挂、url 逐字交引擎)。写坏的 `name` · `transport` 的 `kind`/`command`/`url` · `allowTools` · `toolAxes` · `toolFaces` · `sessionMetaKey` ⇒ 这一条 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`。
3306
+ - **耐久 resume 重放存量请求**:写坏的条目整条不进或丢键照挂,不拒(拒会把升级前 park 的任务永久卡死;`transport.url` 带凭据的存量条目照挂、url 逐字交引擎)—— 唯一的拒是 `transport` 里交不出去的成员(放行变宽的那一形,见本条后半)。写坏的 `name` · `transport` 的 `kind`/`command`/`url` · `allowTools` · `toolAxes` · `toolFaces` · `sessionMetaKey` ⇒ 这一条 server 整条不进,运维 warn `mcp_injection_dropped`(`reason: malformed_entry`,`field` 点名)并投用户面通告 `mcp.injection_dropped`(其中 `toolAxes` 在一次性迁移 M19 的窗开着时先按升级前读法重读一次,过了就挂,见上文 `toolAxes` 一节);写坏的 `contentOrigin` · `elicitation` · `source` 与词表外的键 ⇒ 丢掉那一个键、server 照挂;`transport` 的 `args` / `principalHeader` 写坏,或 `env` / `headers`(名 → 串记录)里有按 7.131.x 读法交不出去的成员(值不是串的成员 ⇒ 整键,`__proto__` 名的成员)⇒ 本部署兑现请求 MCP 时**拒续跑** `409 resume_blocked_by_policy`(点名成员坐标,见 9.5c-bis 同段;7.146.x 及以前剔掉照挂、一行 `mcp_transport_members_dropped`);条目不是对象 ⇒ 这一条不进(`malformed_entry`);超过 32 条 ⇒ 第 33 条起只记 `over_cap`;整只不是数组 ⇒ 整只忽略 + 运维 warn `mcp_injection_ignored`。
3236
3307
  - `POST /v1/capabilities/mcp/probe` 问的是新提交那**同一只判官**:上面每一形同码同句(含 `unknownKeys`,与 url 带凭据那一形的 `field` / `reason`),拒之前零拨号;判官受理的申报,探测口拨的就是一条任务提交会交给引擎的那一份(判官的合法归一照收)。它多出的两形:本口的体就是这份列表,`mcpServers` 缺席或为 `null` ⇒ `400 request.body_shape`(缺必需字段);声明了 `transport.assertionHeader` 的条目 ⇒ `400 request.field_invalid`(见 9.5d:本版承载、不发送,新提交照挂,探测不拨一份少了断言头的配置 —— 同一只谓词,后果按门分)。7.131.x 上本口另有一只判法(申报原文与读器产物逐字比),列表不是数组 / 条目不是对象答 `request.body_shape`。
3237
3308
  - **`transport.kind` 的词表可读(7.141.0 起)**:`GET /v1/capabilities` 的 `mcpTransportKinds`(`["stdio","http"]`,书写序,每种部署形都在场、不随部署形变)= 请求面读器与引擎今天能拨的传输种类。客户端在投影 `mcpServers` 条目之前据此判目标传输读不读得了(只拒那一台、其余照投),不必靠整次提交挨 400 去试。**位不是放行单**:位外的词(引擎的 `host`、别家的 `sse` / `sdk` / `websocket`)在新提交与探测口上照旧整次 400;本部署收不收自带的 `mcpServers` 读 `mcpInjection`,「端到端可达」没有任何位承诺。键缺席 = 老 server,读作 `["stdio","http"]`;位里出现不认识的词按「不支持该种类」处理。
3238
3309
  - **`toolFaces` 的判官是引擎的 `readToolFaces`(7.134.0 起;此前本服务自带一只只判 JSON 形的读器)**:它在门口就判声明本身能说的一切 —— 成员与类型、五张闭词表、上界、跨成员律 ——
@@ -3276,7 +3347,7 @@ WebSearch 工具的 `tool_result` 错误文本,回到模型的对话里,**不是
3276
3347
  | `settings.webSearch.endpoint` | 同段的 `apiKey`(brave / tavily 发成请求头;searxng 腿不发鉴权头,要凭据的实例在部署面声明 `WEB_SEARCH_ENDPOINT`) |
3277
3348
  | `images[i].url`(`{ url }` 臂;7.136.0 起) | 没有封签的头位 —— 图片字节改走内联 `{ data, mimeType }`(7.136.0 起) |
3278
3349
 
3279
- - **判据**:URL 带 userinfo(`https://user:pass@host/…`,只有用户名也算),或带名字在凭据名表里的 query / fragment 参数(`?api_key=` · `?key=` · `?token=` · `#access_token=` …;表 = 本服务日志脱敏器认作凭据名的那一张,全表见契约附录 A `request.field_invalid` 行;名字大小写不敏感,解一次百分号编码读,整名相等)。
3350
+ - **判据**:URL 带 userinfo(`https://user:pass@host/…`,只有用户名也算),或带名字在凭据名表里的 query / fragment 参数(`?api_key=` · `?key=` · `?token=` · `#access_token=` …;表 = 本服务日志脱敏器认作凭据名的那一张,全表见契约附录 A `request.field_invalid` 行;名字大小写不敏感,解一次百分号编码读;整名在表上,或(7.147.0 起)末尾收在强档词上的复合名 —— `db_password` · `PGPASSWORD` · `AWS_SECRET_ACCESS_KEY` 中,只看末尾,`password_hash` / `tokens` 不中)。
3280
3351
  - **答案**:新提交(`POST /v1/tasks` · `/v1/tasks/stream` · `/v1/runs`)`400 request.field_invalid`,体另带 `field`(上表的体内全坐标)与 `reason`(`url_userinfo` | `url_credential_param`);句子点名坐标、命中的那一段与表里的词,**不回显 URL 的值**,并指出路。MCP 座另有探测口(`POST /v1/capabilities/mcp/probe`,同码同句同机读位)。
3281
3352
  - **A2A 座**并进 `a2aPeers` 的条目判官表(7.136.0 起,与 `mcpServers` 同一副拒绝骨架,见下):`url` / `cardUrl` 先判是 http(s) 串、再判凭据;`a2aPeers` 超过 32 条(挂载上限)整份 `400 request.field_invalid`(`field:"a2aPeers"` · `reason:"over_cap"` · `count` · `cap`),
3282
3353
  判官要么看过每一条对端的 `url` / `cardUrl`,要么这份体不收。7.134.x 及以前超过 32 条是 200、第 33 条起丢弃(运维 warn 点名条数)。**搜索后端座**:userinfo 形此前已是 400(句子逐字不变,本版起体另带 `field` / `reason`),凭据名参数是本版新拒的。
@@ -3326,6 +3397,37 @@ WebSearch 工具的 `tool_result` 错误文本,回到模型的对话里,**不是
3326
3397
  仍会被座拒(`credential_unavailable` + 一行 `peer_seat_refused`,`reason: handle_not_held`)的窄形:被取消的父腿里一次工具调用起的会话级子代,在那条腿已放之后才取得持有(取消的孤儿窗);
3327
3398
  一条永不落定的准备腿永不放(条目随之常驻,`peer_registry_holders` 高于基线不回落即此形)。
3328
3399
 
3400
+ ### 9.5i-bis 部署声明的 A2A 对端:`config.d/a2a.json`(7.147.0 起)
3401
+
3402
+ 本地配置源(`CONFIG_PROVIDER=local`、`run-local`)从 `config.d/a2a.json` 读这台部署常驻的出站 A2A 对端,键与中心发布的 `a2a` 域同形(契约包 settings-schema 18.0.0 起 `a2a` 是便携域):两条腿过同一只读口、读成同一个声明,同一只解析器拨号。7.146.x 及以前本地腿不读这份文件,只有中心 `/effective.a2a` 能声明部署级对端。
3403
+
3404
+ ```json
3405
+ {
3406
+ "peers": [
3407
+ {
3408
+ "name": "planner",
3409
+ "url": "https://planner.example.com/a2a",
3410
+ "cardUrl": "https://planner.example.com/.well-known/agent-card.json",
3411
+ "headerRefs": { "Authorization": "PLANNER_A2A_TOKEN" },
3412
+ "allowSkills": ["plan", "review"]
3413
+ }
3414
+ ]
3415
+ }
3416
+ ```
3417
+
3418
+ - `name`(必填):对端的身份,线上工具名 `a2a__<name>__<skill>` 的命名空间段;非空、≤ 128 字符、首尾无空白,不截断、不 trim。两条对端重名 ⇒ 读口把整个 `a2a` 域判坏并点名(与其它域整域读坏同一处置)。
3419
+ - `url`(必填):对端服务地址,卡的 well-known 路径从它解析。带 userinfo(`https://u:p@peer/`)、带 fragment、或 query 值像密钥 ⇒ 这一条不进(读口点名,兄弟照拨);凭据走 `headerRefs`。
3420
+ - `cardUrl`(可缺席):对端卡不在 well-known 路径时写;写了就只用它。
3421
+ - `headerRefs`(可缺席):「头名 → 本服务环境变量名」。文件里只写变量名,值在起服时从本服务的环境读(与 `mcp.json` 的 `headerRefs` 同一种读法)。任一引用的变量缺席 ⇒ 这一条整条不拨、一行 warn `web_admin_a2a_env_missing`,兄弟照拨;记录里任一成员写坏(头名不是合法头名、值不是串)⇒ 这一条整条不进(读口点名),不会只带一半凭据拨号。
3422
+ - `principalHeader`(可缺席):把任务的终端用户主体注入到哪个头名(值由运行器填,模型读不到也改不了)。
3423
+ - `allowSkills`(可缺席):只挂对端卡上这几只技能。缺席与空表同读 = 挂卡上全部技能;不想拨这个对端,写 `enabled: false` 或删掉这一条。
3424
+ - `scenarios`(缺省 `[]` = 所有场景)· `enabled`(缺省 `true`)。
3425
+ - 不收 `toolAxes`:远端技能的风险轴只能由调用方在请求里声明(9.5h),部署面不替调用方担保;写了是词表外的键,读口点名。
3426
+ - **生效**:重启生效(与 `mcp.json` 同一条线)。文件改了 ⇒ `/health` 报 `restartRequired`、`restart.reasons` 含 `a2a`。重启信号只看真会改变拨号的写法:没写文件、`peers` 为空、只剩 `enabled: false` 的对端是同一个状态(这台部署没有部署级对端,不签重启);`cardUrl` / `principalHeader` 写空串、`allowSkills` / `headerRefs` 写空,与不写同读。
3427
+ - **从中心腿迁来**:中心发的 `a2a` 域照旧生效,现在也过同一只读口 —— 上面「这一条不进」的几种形,7.146.x 原样透传给引擎,7.147.0 起在读口点名丢掉;条目没写 `enabled` / `scenarios` 时读口补缺省,对拨号同义。
3428
+ - **已知限制:对端拨号面今天没有主机底线**:启用的对端按 `url` / `cardUrl` 取卡、按卡里写的服务端点发送,**不过任何部署主机底线** —— 本服务今天不给引擎接它(`RunnerDeps.hostFloor` 没有写点)。`url` / `cardUrl` 是运维写的,可指向任意主机(含内网与回环);服务端点是对端的卡写的:**不带头的对端**(这次发送不带任何声明的头:没写 `headerRefs`,也不注入主体头)的服务端点可指向任意主机,遇重定向照跟(引擎设跳数上限,没有主机底线);带头的对端另受引擎的端点绑定 —— 服务端点必须落在声明的 `url` / `cardUrl` 所在的源上,且不跟重定向。取卡发生在对端被用到时:每次任务准备、会话 A2A 面板(`GET /v1/sessions/:id/a2a`)缓存缺失 / 过期(5 秒)时;起服本身不取卡。**多租户部署同样挂**:多租户闸只关请求体自带的 `a2aPeers`(9.5h),部署基线照挂。
3429
+ **补偿**:只写你信任的对端 —— 不带头的对端,它的卡能让引擎去敲它写下的任何地址;起服前自审 `config.d/a2a.json`;多租户 / 公共部署不要接不受信对端。出路 = 部署主机底线接线(尚未到货),到货后本条改写。
3430
+
3329
3431
  ### 9.5j 请求的 `settings.env` 不收凭据(7.146.0 起,新提交 400)
3330
3432
 
3331
3433
  **律**:凭据进引擎只经引擎进程启动时的环境,从不经请求。`settings.env` 来自一份模型读得到的设置文件,而本服务把它交给会话的 shell(单用户 host / device 车道原样叠进模型 shell 的环境 ——
@@ -3372,7 +3474,9 @@ WebSearch 工具的 `tool_result` 错误文本,回到模型的对话里,**不是
3372
3474
  ② 名字的左界止于前一枚已认出的凭据(或可读的结构块)的终点:强锚名紧贴在前一枚凭据的尾巴上(`-----END PRIVATE KEY-----password=…`;PGP 收尾栅栏、PuTTY 的 `Private-MAC:` 行、AWS 键 id 同形,中间无分隔)时**首遍即遮**,再过一遍读数不变 ——所以各出口同读,不看出口过几遍脱敏:流式 `done` 帧、同步体、运维日志行、活流 `text_delta` / `reasoning_delta` 逐块帧与 `text_end` / `reasoning_end`(活流与 `done` 帧同一只脱敏器、同一遍,逐块拼接 = 段末帧,契约 §4a-nov-bis)、`GET /v1/runs/:id` 的 run 行、`GET /v1/runs/:id/events` 回放里的 `text` / `reasoning` / `done` / `tool_end` 行。
3373
3475
  ③ 不带引号的值里面的赋值照问:`api_key=abc,password="my secret value"` 里两个值都遮(此前外层不带引号的值把里面的赋值藏住,里面那一只不认)。
3374
3476
  **升级前已落库的行**:回放读面对 run 行与 `text` / `reasoning` / `done` 行再过一遍脱敏 ⇒ 按本读法遮;`tool_end` 行回放读面不再过(只剥媒体字节)⇒ 升级前写下的 ① ② 两形照旧明文,升级后新写的行照遮。
3375
- **仍不遮的一形**:名字整个落在前一枚已认出凭据的值里 —— 以字母数字收尾的令牌后面紧贴强锚名(`ghp_…password=<值>`,中间无分隔;令牌的形状把名字吞了进去)—— 令牌照遮、`<值>` 明文,各出口同读;中间有分隔(空白 / `,` / `;` / 换行)照遮。名字按整名认:前面与字母数字以 `_` / `-` 连写的(`DB_PASSWORD=` / `foo-api_key=`)是另一个名字、不算锚;只有收尾栅栏、没有起始栅栏的 `…KEY-----password=` 同理(孤立的收尾栅栏不是凭据,不立名界)。要靠脱敏挡住的凭据别以这一形写出(MIGRATION「自由文本脱敏的名字面」)。
3477
+ **名字判定读契约**(7.147.0 起):「是不是凭据名」整段交契约包的名字判官 —— 整名在凭据名表上,或末段 / 尾串收在强档词上:以 `password` / `passwd` / `pwd` / `secret` / `apikey` 收尾(整段或与前缀粘连)的复合名(`DB_PASSWORD=` / `PGPASSWORD=` / `dbPassword=` / `--db-password=` / compose 的 `POSTGRES_PASSWORD: <值>`)与三条尾串(`SECRET_KEY` / `AWS_SECRET_ACCESS_KEY` 读作 `secret`;`x-api-key` / `OPENAI_API_KEY` 读作 `api_key`;`github_access_token` 读作 `access_token`)都是凭据名,锚强度按它读作的那个表词;只看末尾(`password_hash=` / `secretName:` / `pwd_dir=` / `api_key_count=` / `token_count=` 不是)。名字长度不封顶。四个读者同读:自由文本(属性路径只把最后一段交判官:`spring.datasource.password=`)、JSON 键与 URL 参数名(整段交判官;URL 参数名按收方的解析读:`+` 是空格、百分号解一次 —— `?db.password=` / `?x+password=` 同遮)、诊断面。受理口的 URL 凭据判官读同一只判官,两边对同一个名字同答。所以此前「名字整个落在前一枚已认出凭据的值里」那一形(`ghp_…password=<值>`,令牌的形状把名字吞了进去)也遮:名字是整段,收在强档词上。代价:以强档词收尾、值不是凭据的名字(`show_password=1` / `is_secret=true`)的值也换成记号;单行出门的运维日志里,行尾凭据值之后被剥掉换行而拼上来的下一行首词随值一起遮。**仍不遮的一形**:引号里以 `$` 起头的值(`DB_PASSWORD='$ecret12'`)读作插值,不是字面量 —— 要靠脱敏挡住的凭据别以这一形写出。
3478
+
3479
+ **错误文本的出口契约**(归一化视图 / 遮蔽记号闭形)写在源码仓内的助手 wire 契约 `ASSISTANT-WIRE-CONTRACT.md` 里(不随 npm 包出)。
3376
3480
 
3377
3481
  ### 9.7 bake-runner(构建宿主守护进程)的 env(7.103.0 起成文)
3378
3482