@sema-agent/server 7.138.0 → 7.140.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 (332) hide show
  1. package/MIGRATION.md +457 -7
  2. package/README.md +7 -4
  3. package/README.zh-CN.md +6 -4
  4. package/USAGE.md +91 -41
  5. package/deploy/sema-up/README.md +1 -1
  6. package/deploy/sema-up/lib/lanes.json +3 -2
  7. package/dist/adoption/runner.js +1 -1
  8. package/dist/adoption/sql.js +1 -1
  9. package/dist/adoption/wire.d.ts +3 -3
  10. package/dist/approval-card.d.ts +26 -26
  11. package/dist/bench/s1/arms.d.ts +1 -1
  12. package/dist/bench/s1/arms.js +1 -3
  13. package/dist/bench/s1/live-deps.js +3 -2
  14. package/dist/bench/s1/row.d.ts +5 -2
  15. package/dist/bench/s1/row.js +2 -22
  16. package/dist/bench/s1/run-firm.js +2 -2
  17. package/dist/boot/adoption.js +1 -1
  18. package/dist/boot/device-lane.d.ts +8 -3
  19. package/dist/boot/device-lane.js +7 -2
  20. package/dist/boot/execution-env.d.ts +4 -2
  21. package/dist/boot/execution-env.js +28 -34
  22. package/dist/boot/execution-lane.d.ts +9 -1
  23. package/dist/boot/execution-lane.js +6 -1
  24. package/dist/boot/governance-seams.d.ts +3 -2
  25. package/dist/boot/governance-seams.js +9 -11
  26. package/dist/boot/memory-boundary.d.ts +14 -51
  27. package/dist/boot/memory-boundary.js +10 -20
  28. package/dist/boot/memory-consolidation.d.ts +15 -11
  29. package/dist/boot/memory-consolidation.js +25 -14
  30. package/dist/boot/resolve-spec.d.ts +4 -6
  31. package/dist/boot/resolve-spec.js +10 -13
  32. package/dist/boot/retention-lane.d.ts +5 -5
  33. package/dist/boot/retention-lane.js +0 -6
  34. package/dist/boot/runner-deps.d.ts +6 -4
  35. package/dist/boot/runner-deps.js +3 -1
  36. package/dist/boot/session-faces.d.ts +0 -3
  37. package/dist/boot/session-faces.js +15 -8
  38. package/dist/boot/stage-02-web-admin-pull.js +2 -2
  39. package/dist/boot/stage-03-store-backend.d.ts +2 -6
  40. package/dist/boot/stage-03-store-backend.js +2 -3
  41. package/dist/boot/stage-04-stores.d.ts +2 -6
  42. package/dist/boot/stage-04-stores.js +5 -5
  43. package/dist/boot/stage-05-execution-env.d.ts +2 -6
  44. package/dist/boot/stage-05-execution-env.js +2 -2
  45. package/dist/boot/stage-06-runners.d.ts +2 -6
  46. package/dist/boot/stage-06-runners.js +2 -3
  47. package/dist/boot/stage-07-capability-layer.d.ts +2 -6
  48. package/dist/boot/stage-08-reapers.d.ts +2 -6
  49. package/dist/boot/stage-09-leader.d.ts +2 -6
  50. package/dist/boot/stage-09-leader.js +1 -2
  51. package/dist/boot/stage-10-http-server.d.ts +2 -6
  52. package/dist/boot/stage-10-http-server.js +7 -6
  53. package/dist/boot/stores.d.ts +3 -6
  54. package/dist/boot/stores.js +27 -40
  55. package/dist/boot/task-list-lane.d.ts +1 -1
  56. package/dist/boot/task-list-lane.js +1 -1
  57. package/dist/boot/web-admin.js +37 -11
  58. package/dist/capabilities/execution-lane.d.ts +2 -2
  59. package/dist/capabilities/mcp-probe.js +1 -1
  60. package/dist/capabilities/scenarios.d.ts +1 -1
  61. package/dist/capability-facts.d.ts +15 -7
  62. package/dist/capability-facts.js +8 -3
  63. package/dist/config-catalog.js +9 -8
  64. package/dist/config-invariants.d.ts +23 -3
  65. package/dist/config-invariants.js +42 -0
  66. package/dist/config-provider.d.ts +33 -37
  67. package/dist/config-provider.js +51 -30
  68. package/dist/config-types.d.ts +21 -7
  69. package/dist/config.d.ts +33 -0
  70. package/dist/config.js +46 -16
  71. package/dist/delegated-session-owner.d.ts +4 -3
  72. package/dist/delegated-session-owner.js +1 -1
  73. package/dist/deployment-shape.d.ts +10 -9
  74. package/dist/deployment-shape.js +4 -4
  75. package/dist/device-enrollment.d.ts +41 -2
  76. package/dist/device-enrollment.js +27 -12
  77. package/dist/device-store.d.ts +58 -85
  78. package/dist/device-store.js +19 -49
  79. package/dist/device-ws-hub.js +2 -1
  80. package/dist/env-name-allowlist-knobs.d.ts +5 -4
  81. package/dist/exec-cut.d.ts +60 -0
  82. package/dist/exec-cut.js +36 -0
  83. package/dist/execution-lane-caps.d.ts +115 -105
  84. package/dist/execution-lane-caps.js +76 -82
  85. package/dist/execution-placement.d.ts +259 -0
  86. package/dist/execution-placement.js +199 -0
  87. package/dist/field-conflict.d.ts +3 -2
  88. package/dist/field-conflict.js +1 -0
  89. package/dist/hosted-posture.d.ts +2 -2
  90. package/dist/http/admission.js +8 -19
  91. package/dist/http/checkpoint-ctx.d.ts +21 -12
  92. package/dist/http/checkpoint-ctx.js +4 -2
  93. package/dist/http/claimed-run.d.ts +1 -1
  94. package/dist/http/durable-run-leg.js +9 -5
  95. package/dist/http/execution-context.d.ts +218 -0
  96. package/dist/http/execution-context.js +330 -0
  97. package/dist/http/memory-compliance-failure-reply.d.ts +3 -1
  98. package/dist/http/memory-compliance-failure-reply.js +5 -0
  99. package/dist/http/memory-layer-reply.d.ts +30 -0
  100. package/dist/http/memory-layer-reply.js +24 -0
  101. package/dist/http/resume-legs.js +63 -36
  102. package/dist/http/retired-service-deps.d.ts +1 -1
  103. package/dist/http/retired-service-deps.js +1 -1
  104. package/dist/http/route-ctx.d.ts +14 -8
  105. package/dist/http/routes/adoption.js +1 -1
  106. package/dist/http/routes/background.d.ts +29 -1
  107. package/dist/http/routes/background.js +29 -2
  108. package/dist/http/routes/devices.d.ts +13 -19
  109. package/dist/http/routes/devices.js +21 -26
  110. package/dist/http/routes/memory-bundle.js +3 -0
  111. package/dist/http/routes/memory-compliance.js +8 -1
  112. package/dist/http/routes/memory-consolidation.js +12 -1
  113. package/dist/http/routes/memory-origin.js +14 -2
  114. package/dist/http/routes/memory-policy.js +5 -0
  115. package/dist/http/routes/session-sync.js +110 -15
  116. package/dist/http/routes/sessions.d.ts +6 -0
  117. package/dist/http/routes/sessions.js +143 -22
  118. package/dist/http/routes/side-query.d.ts +12 -4
  119. package/dist/http/routes/side-query.js +4 -5
  120. package/dist/http/routes/tasks.js +41 -33
  121. package/dist/http/server.d.ts +40 -44
  122. package/dist/http/server.js +13 -2
  123. package/dist/http/session-inherit-env.d.ts +3 -4
  124. package/dist/http/session-inherit-env.js +1 -1
  125. package/dist/http/wire-types.d.ts +8 -12
  126. package/dist/identity-source.d.ts +1 -1
  127. package/dist/identity-source.js +1 -1
  128. package/dist/index.d.ts +1 -0
  129. package/dist/lane-home-probe.d.ts +68 -42
  130. package/dist/lane-home-probe.js +53 -30
  131. package/dist/leader/wire.js +5 -4
  132. package/dist/memory-bundle-engine.d.ts +16 -9
  133. package/dist/memory-bundle-engine.js +29 -6
  134. package/dist/memory-operator-faces.d.ts +85 -30
  135. package/dist/memory-operator-faces.js +136 -30
  136. package/dist/memory-project-layer.d.ts +3 -21
  137. package/dist/memory-project-layer.js +11 -8
  138. package/dist/memory-scope-stores.d.ts +73 -0
  139. package/dist/memory-scope-stores.js +86 -0
  140. package/dist/memory-scope.d.ts +29 -5
  141. package/dist/memory-scope.js +16 -4
  142. package/dist/memory-sync-client.d.ts +70 -18
  143. package/dist/memory-sync-client.js +215 -117
  144. package/dist/memory-sync.d.ts +1 -1
  145. package/dist/memory-sync.js +3 -2
  146. package/dist/observability/cost-taxonomy.d.ts +2 -2
  147. package/dist/observability/fail-open.d.ts +12 -0
  148. package/dist/observability/fail-open.js +12 -0
  149. package/dist/observability/run-terminal.js +0 -1
  150. package/dist/orchestration/workflow-notify-journal.js +4 -4
  151. package/dist/org-memory-admission.d.ts +4 -3
  152. package/dist/org-memory-admission.js +6 -6
  153. package/dist/parked-decide.d.ts +34 -9
  154. package/dist/parked-decide.js +12 -19
  155. package/dist/parked-revive-surface.d.ts +12 -15
  156. package/dist/parked-revive-surface.js +3 -6
  157. package/dist/peer-seat.d.ts +13 -7
  158. package/dist/peer-seat.js +37 -3
  159. package/dist/plugins/adoption-log-sql.js +4 -4
  160. package/dist/plugins/approval-ask-store-file.d.ts +7 -0
  161. package/dist/plugins/approval-ask-store-file.js +11 -0
  162. package/dist/plugins/approval-ask-store-memory.d.ts +6 -0
  163. package/dist/plugins/approval-ask-store-memory.js +12 -0
  164. package/dist/plugins/approval-ask-store-sql.d.ts +22 -5
  165. package/dist/plugins/approval-ask-store-sql.js +44 -26
  166. package/dist/plugins/approval-nonce-store.js +4 -4
  167. package/dist/plugins/blob-backend.d.ts +4 -4
  168. package/dist/plugins/blob-backend.js +7 -7
  169. package/dist/plugins/breaker-state-sql.js +2 -2
  170. package/dist/plugins/caching-session-store.d.ts +6 -2
  171. package/dist/plugins/caching-session-store.js +9 -3
  172. package/dist/plugins/checkpoint-index-columns.js +2 -2
  173. package/dist/plugins/checkpoint-store-sql.d.ts +6 -5
  174. package/dist/plugins/checkpoint-store-sql.js +21 -23
  175. package/dist/plugins/device-store-sql.d.ts +25 -51
  176. package/dist/plugins/device-store-sql.js +68 -122
  177. package/dist/plugins/ensure-index.d.ts +1 -2
  178. package/dist/plugins/execution-placement-sql.d.ts +151 -0
  179. package/dist/plugins/execution-placement-sql.js +223 -0
  180. package/dist/plugins/file-history-store-sql.d.ts +9 -8
  181. package/dist/plugins/file-history-store-sql.js +9 -9
  182. package/dist/plugins/file-run-store.d.ts +6 -1
  183. package/dist/plugins/file-run-store.js +4 -0
  184. package/dist/plugins/fork-routing-session-store.d.ts +6 -0
  185. package/dist/plugins/fork-routing-session-store.js +16 -0
  186. package/dist/plugins/image-bake-store-sql.js +9 -9
  187. package/dist/plugins/image-index-sql.js +11 -11
  188. package/dist/plugins/leader-run-store-sql.js +4 -4
  189. package/dist/plugins/local-session-store.d.ts +149 -50
  190. package/dist/plugins/local-session-store.js +382 -107
  191. package/dist/plugins/mailbox-store-sql.d.ts +2 -1
  192. package/dist/plugins/mailbox-store-sql.js +4 -4
  193. package/dist/plugins/memory-engine-tidb.js +5 -5
  194. package/dist/plugins/memory-key-guards.d.ts +4 -0
  195. package/dist/plugins/memory-key-guards.js +1 -0
  196. package/dist/plugins/memory-optout-grant-store-sql.js +1 -1
  197. package/dist/plugins/memory-run-store.d.ts +1 -1
  198. package/dist/plugins/memory-sync-store-tidb.js +3 -3
  199. package/dist/plugins/one-time-migrations.d.ts +117 -8
  200. package/dist/plugins/one-time-migrations.js +408 -38
  201. package/dist/plugins/org-delivery-quota-store.js +2 -2
  202. package/dist/plugins/outcome-ledger-sql.d.ts +2 -2
  203. package/dist/plugins/outcome-ledger-sql.js +2 -2
  204. package/dist/plugins/permission-rule-store-sql.d.ts +17 -19
  205. package/dist/plugins/permission-rule-store-sql.js +29 -32
  206. package/dist/plugins/pg-pool.d.ts +7 -3
  207. package/dist/plugins/pg-session-storage.d.ts +31 -20
  208. package/dist/plugins/pg-session-storage.js +120 -52
  209. package/dist/plugins/remote-env-adb.d.ts +18 -19
  210. package/dist/plugins/remote-env-adb.js +91 -66
  211. package/dist/plugins/remote-env-device.d.ts +23 -31
  212. package/dist/plugins/remote-env-device.js +19 -17
  213. package/dist/plugins/remote-env-e2b.d.ts +27 -21
  214. package/dist/plugins/remote-env-e2b.js +91 -45
  215. package/dist/plugins/remote-env-file-error.d.ts +3 -1
  216. package/dist/plugins/remote-env-file-error.js +9 -3
  217. package/dist/plugins/remote-env-host.d.ts +31 -34
  218. package/dist/plugins/remote-env-host.js +91 -72
  219. package/dist/plugins/remote-env-k8s.d.ts +13 -29
  220. package/dist/plugins/remote-env-k8s.js +63 -55
  221. package/dist/plugins/remote-env-local-docker.d.ts +24 -12
  222. package/dist/plugins/remote-env-local-docker.js +49 -61
  223. package/dist/plugins/remote-env-ssh.d.ts +11 -19
  224. package/dist/plugins/remote-env-ssh.js +74 -57
  225. package/dist/plugins/remote-shell.d.ts +100 -3
  226. package/dist/plugins/remote-shell.js +166 -2
  227. package/dist/plugins/resume-anchor-store-sql.js +2 -2
  228. package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
  229. package/dist/plugins/retention-lane-store-sql.js +2 -2
  230. package/dist/plugins/retention-store-sql.d.ts +10 -7
  231. package/dist/plugins/retention-store-sql.js +13 -7
  232. package/dist/plugins/run-store-sql.d.ts +13 -3
  233. package/dist/plugins/run-store-sql.js +20 -15
  234. package/dist/plugins/schema-consistency.d.ts +21 -11
  235. package/dist/plugins/schema-consistency.js +15 -9
  236. package/dist/plugins/schema-drift-sentence.d.ts +27 -0
  237. package/dist/plugins/schema-drift-sentence.js +38 -0
  238. package/dist/plugins/schema-ensure-legs.d.ts +2 -3
  239. package/dist/plugins/schema-ensure-legs.js +2 -2
  240. package/dist/plugins/schema-live-columns.d.ts +1 -1
  241. package/dist/plugins/session-capture-record-store-sql.js +1 -1
  242. package/dist/plugins/session-fork.d.ts +54 -0
  243. package/dist/plugins/session-fork.js +38 -1
  244. package/dist/plugins/session-row-sql.d.ts +22 -0
  245. package/dist/plugins/session-row-sql.js +81 -0
  246. package/dist/plugins/session-rules-column.d.ts +1 -1
  247. package/dist/plugins/session-rules-column.js +1 -1
  248. package/dist/plugins/session-store.d.ts +1 -1
  249. package/dist/plugins/session-workspace-cell.d.ts +1 -1
  250. package/dist/plugins/session-workspace-cell.js +1 -1
  251. package/dist/plugins/shared-memory-store-sql.js +2 -2
  252. package/dist/plugins/sql-driver.d.ts +10 -5
  253. package/dist/plugins/sql-driver.js +6 -6
  254. package/dist/plugins/sql-errors.js +1 -1
  255. package/dist/plugins/store-backend.d.ts +5 -7
  256. package/dist/plugins/store-backend.js +23 -11
  257. package/dist/plugins/store-contracts.d.ts +25 -5
  258. package/dist/plugins/task-list-store-sql.js +11 -11
  259. package/dist/plugins/tidb-pool.d.ts +12 -7
  260. package/dist/plugins/tidb-pool.js +13 -7
  261. package/dist/plugins/tidb-session-storage.js +4 -4
  262. package/dist/plugins/tidb-session-store.d.ts +57 -26
  263. package/dist/plugins/tidb-session-store.js +127 -59
  264. package/dist/plugins/tool-result-store-sql.d.ts +5 -4
  265. package/dist/plugins/tool-result-store-sql.js +21 -13
  266. package/dist/plugins/user-input-ledger-sql.js +2 -2
  267. package/dist/plugins/workflow-journal-store-sql.js +5 -5
  268. package/dist/plugins/workflow-run-store-sql.js +19 -19
  269. package/dist/plugins/write-behind-counter.d.ts +2 -3
  270. package/dist/plugins/write-behind-counter.js +5 -5
  271. package/dist/project-memory.d.ts +9 -15
  272. package/dist/project-memory.js +5 -4
  273. package/dist/request-key-closure.js +1 -1
  274. package/dist/request-value-closure.d.ts +6 -7
  275. package/dist/request-value-closure.js +1 -3
  276. package/dist/resource-suspend.d.ts +14 -12
  277. package/dist/resource-suspend.js +1 -1
  278. package/dist/resource-window.d.ts +4 -3
  279. package/dist/run-local.d.ts +6 -0
  280. package/dist/run-local.js +34 -35
  281. package/dist/runs.d.ts +10 -5
  282. package/dist/runs.js +52 -66
  283. package/dist/security.d.ts +38 -12
  284. package/dist/security.js +8 -2
  285. package/dist/server-secret-env.d.ts +9 -5
  286. package/dist/session-children-retire.d.ts +27 -9
  287. package/dist/session-children-retire.js +9 -5
  288. package/dist/session-incarnation.d.ts +20 -10
  289. package/dist/session-incarnation.js +5 -3
  290. package/dist/session-policy-wire.d.ts +1 -1
  291. package/dist/session-row.d.ts +114 -5
  292. package/dist/session-row.js +55 -5
  293. package/dist/session-titler.d.ts +2 -2
  294. package/dist/sql-engine-posture.d.ts +1 -1
  295. package/dist/sql-engine-posture.js +1 -1
  296. package/dist/{plugins → stores-sql}/background-agent-store-sql.d.ts +2 -2
  297. package/dist/{plugins → stores-sql}/background-agent-store-sql.js +8 -8
  298. package/dist/{plugins → stores-sql}/session-policy-store-sql.d.ts +7 -5
  299. package/dist/{plugins → stores-sql}/session-policy-store-sql.js +12 -12
  300. package/dist/{plugins → stores-sql}/usage-window-store-sql.d.ts +1 -1
  301. package/dist/{plugins → stores-sql}/usage-window-store-sql.js +3 -3
  302. package/dist/task-a2a.d.ts +1 -1
  303. package/dist/task-a2a.js +5 -6
  304. package/dist/task-cwd.d.ts +6 -1
  305. package/dist/task-mcp.d.ts +22 -9
  306. package/dist/task-mcp.js +17 -9
  307. package/dist/terminal.d.ts +27 -0
  308. package/dist/terminal.js +6 -0
  309. package/dist/tool-approval.d.ts +6 -0
  310. package/dist/tool-approval.js +8 -1
  311. package/dist/trace/project.d.ts +1 -0
  312. package/dist/trace/project.js +1 -0
  313. package/dist/trace/redact.d.ts +3 -1
  314. package/dist/trace/redact.js +80 -28
  315. package/dist/trace/self-minted-wire.d.ts +169 -2
  316. package/dist/trace/self-minted-wire.js +54 -1
  317. package/dist/url-refusal.d.ts +1 -1
  318. package/dist/web-admin-client/apply-effective.d.ts +17 -3
  319. package/dist/web-admin-client/apply-effective.js +20 -10
  320. package/dist/web-admin-client/candidate-gate.d.ts +11 -7
  321. package/dist/web-admin-client/candidate-gate.js +1 -1
  322. package/dist/web-admin-client/http-client.d.ts +5 -4
  323. package/dist/web-admin-client/http-client.js +2 -2
  324. package/dist/web-admin-client/read-warnings.d.ts +7 -17
  325. package/dist/web-admin-client/read-warnings.js +5 -9
  326. package/dist/web-admin-client/skills-mcp.js +3 -2
  327. package/dist/web-admin-client/types.d.ts +6 -0
  328. package/package.json +3 -3
  329. package/wire-changes/server@7.139.0.json +348 -0
  330. package/wire-changes/server@7.140.0.json +184 -0
  331. package/dist/http/device-placement.d.ts +0 -175
  332. package/dist/http/device-placement.js +0 -145
package/USAGE.md CHANGED
@@ -58,7 +58,7 @@ docker compose up --build --scale service=4 # 4 个无状态 worker,nginx
58
58
  > 🔴 **7.119.0 起 `SESSION_BACKEND=auto` 配了坐标而库连不上同样拒启,不再有「auto 内存起服」这条出路**:`auto` = 「配了该引擎的坐标就是那只库(boot 期窗内重试、到窗拒启),没配就是内存」,
59
59
  > 由配置决定、不由起库成败决定;内存部署写 `DB_BACKEND=memory` 或 `SESSION_BACKEND=memory`。`GET /health` 的 `sessionBackend` 如实说装好的后端(`durable(pg)` / `memory`)。持久化姿态读面:caps `store`(附录 B)。
60
60
 
61
- > 🔴 7.96.0 起根 `docker-compose.yml` 的 `REMOTE_EXEC`(执行车道)是**必填**:不设 ⇒ `docker compose config` 当场失败(此前静默起成一个没有手的 worker)。单人自用 `REMOTE_EXEC=host`(手落在 worker 容器里);多人 / 生产选隔离车道 `e2b`(配 `E2B_API_KEY`)或 `k8s`(配 `K8S_SANDBOX_IMAGE`)。
61
+ > 🔴 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`)。
62
62
 
63
63
  > 🔴 **7.104.0 起多租户姿态不许跑不分租户的执行车道**:`REQUIRE_PRINCIPAL=true` ∧ 最终生效的车道在执行车道能力表上不分租户 ⇒ **拒启**。按表不按词,今天覆盖 `host` / `ssh` / `adb`(所有任务落同一个共享目标:worker 宿主 / 一台 SSH 对端 / 一台 ADB 设备)。判的是合成后的车道(env 的 `REMOTE_EXEC`、`CONFIG_PROVIDER=local` 而 `REMOTE_EXEC` 未设的隐式 host、local 形 `config.d/remote-exec.json` 的覆写,在中心拉取与任何配置应用之前合成)。拒因(逐字,以 host 为例):`REQUIRE_PRINCIPAL=true (multi-tenant posture) with the "host" execution lane runs every tenant's Bash on one shared target — this lane does not separate tenants; set REMOTE_EXEC to e2b / k8s / local-docker / device, or unset REQUIRE_PRINCIPAL for a single-user box`。车道 × 租户形:
64
64
  >
@@ -73,6 +73,11 @@ docker compose up --build --scale service=4 # 4 个无状态 worker,nginx
73
73
 
74
74
  > 🔴 **7.104.0 起 `CONFIG_PROVIDER` 是闭集**:只认 `local` / `remote` / 不设(空串 = 不设),**逐字**比较 —— `Local`、` local`、`remote ` 这类写法**拒启**,拒因句点名旋钮并列出合法词。此前认不出的词照起,却被配置源当成 `local`、被隐式 host 车道当成「不是 local」,local 配置源的根还落回进程 cwd(与车道文件覆写读的本地根分家)。本地配置根只有一条链:`CONFIG_LOCAL_DIR` → `AGENT_DATA_DIR` → `~/.ai-agent`,配置源与 `config.d/remote-exec.json` 读同一个。
75
75
 
76
+ > 🔴 **`CONFIG_PROVIDER=remote` 要中心地址**:`remote` 说的是「这台 worker 的配置来自中心」,中心就是 `SEMA_WEB_ADMIN_URL` 那一只 —— 写了 `remote` 而没设 `SEMA_WEB_ADMIN_URL` ⇒ **拒启**,
77
+ > 拒因句点名两键与出路(设中心地址 / 改 `CONFIG_PROVIDER=local` 读本地 `config.d` / 不设这个词做纯 env worker)。配置源的词要么兑现、要么拒,不被读成另一种源:
78
+ > 此前这一形照起,HTTP 服务按纯 env 跑、`run-local` 读本地 `config.d`,两处都零句。不写词而设了 `SEMA_WEB_ADMIN_URL` ⇒ 中心源(缺省)。
79
+ > `run-local`(`sema` CLI)只装本地配置源:没写词时它自己写 `local`;写了 `CONFIG_PROVIDER=remote`(配没配中心地址都一样)⇒ 一行拒句、退 2 —— 中心拉取的那一套(凭据续签、托管模型钥解封、名册门、LKG)只在 HTTP 服务里装配。
80
+
76
81
  > 🔴 **本地配置源的目录信任(7.133.0 起)**:`config.d` 是可执行配置(`mcp.json` 的 stdio `command` 以本服务用户起子进程,`models.json` 的 `baseUrl` 决定 API key 送往哪里),也是部署者在盘上声明外置 MCP 工具的唯一入口 ——
77
82
  > 写在 `config.d/mcp.json`,键与中心发布的 `mcp` 域同形,没有另一个工具目录。读之前先判:从 `/` 走到 `config.d`、再到读者会读的每一份文档(符号链接照跟、跟到哪判到哪),
78
83
  > 每一级属主只能是本服务的用户、root 或 `CONFIG_LOCAL_OWNER_UID`(多信的那一个 uid;形错拒启),不得组 / 他人可写(属主可信的粘滞目录如 `/tmp` 除外,但要读的一项缺在这样的目录里也拒),
@@ -307,7 +312,7 @@ MODEL_DEGRADE_ON=rate_limit,breaker_open # 可选,反应式触发器子集;词
307
312
 
308
313
  | 旋钮 | 谁触发、什么时刻判 | 拒/停的形 | 能打断在跑的 run 吗 |
309
314
  |------|------------------|-----------|--------------------|
310
- | `MAX_TASK_COST_USD` | 引擎(core)按**这一片任务**的累计花费判:首个模型调用前的 pre-call 估算 + turn 结束回查;**未开** `RESOURCE_SUSPEND` 时另有流中途投影取消(开了 resource-suspend 这一臂自动关,只在边界判) | 未开/不合格 ⇒ 终态 `failed` + `errorCode: limits.max_cost_exceeded`(HTTP 仍 200 / SSE `done` 帧带码);**开了 resource-suspend 且合格 ⇒ `suspended` + `resource_limit` 卡**(可续跑,不是失败)——消费方两种终态都要认 | ✅ 能,但只封**单片**:一次 park→resume 会重开满窗(见下条「per-slice 窗 vs 跨片总额」) |
315
+ | `MAX_TASK_COST_USD` | 引擎(core)按**这一片任务**的累计花费判:首个模型调用前的 pre-call 估算 + turn 结束回查;**未开** `RESOURCE_SUSPEND` 时另有流中途投影取消(开了 resource-suspend 这一臂自动关,只在边界判) | 未开/不合格 ⇒ 终态 `failed` + `errorCode: limits.max_cost_exceeded`(HTTP 仍 200 / SSE `done` 帧带码);**开了 resource-suspend 且合格 ⇒ `suspended` + `resource_limit` 卡**(可续跑,不是失败;`verify:true` 任务的实现腿与修复轮同样合格,`cascade:true` 任务不合格)——消费方两种终态都要认 | ✅ 能,但只封**单片**:一次 park→resume 会重开满窗(见下条「per-slice 窗 vs 跨片总额」) |
311
316
  | `MAX_PRINCIPAL_COST_USD`(+ `COST_QUOTA_WINDOW_SEC`) | **HTTP 提交面**在受理请求**之前**读该 principal 桶内累计。挂门的是**任务/run 族**:`/v1/tasks`、`POST /v1/runs`、steer/resume/wake、workflows、leader、a2a,以及 park 后赎回时开的续跑腿(⚠️ 例外见下:operator-only 的记忆整合口不在此列) | `429` + `errorCode: limit.cost_quota_exceeded` + `Retry-After` 头 + 体 `{usedMicroUsd, limitMicroUsd, retryAfterSec, windowResetsAt}` | ❌ **不能**。跑起来之后不再回查:越顶那张单会**跑完**,天花板从**下一次提交**起生效 |
312
317
  | `USAGE_WINDOWS[].maxCostUsd`(§9.2) | 引擎在**每个 turn 边界**把累计花费记进跨任务账本再回查窗;提交面另有同店同键的快速门 | 基建合格 ⇒ `suspended` + `checkpointGate.reason=usage_window` + `resumeAfterMs`(等窗放行原样续跑);不合格 ⇒ `failed` + `errorCode: usage.window_exhausted` + `retryAfterMs`;提交面 ⇒ `429 usage.window_exhausted` | ✅ 能,且是唯一按**跨任务累计**花费中途停的一根(第一根按的是这一片自己的额度,管不到租户总账) |
313
318
 
@@ -426,7 +431,7 @@ MODEL_ID=<your-model-name> MODEL_CHEAP_ID=<your-cheap-model-name>
426
431
  存量中心配置里的 `roles.team` / `roles.synthesize` 由 settings-schema 的一次性迁移搬走,side query 带 `modelRole` 表外词(含 `advisor`)⇒ **400**
427
432
  `request.field_invalid`(不再回落主模型;带了 `model` 也一样拒)。要给委派子代配模型就绑 `subagent`,主任务绑 `default`。
428
433
  - 调用方可在 `objective` 里 `@<模型名>` 选模型,**只认已配置的名单**(`MODEL_ID` / `MODEL_CHEAP_ID`,或目录条目)——注入不了 `baseUrl`/`apiKey`;无 `@` → 默认模型;`@` 点中一条已发布但本部署不可用的目录条目 ⇒ 400 `request.model_unavailable`。
429
- - **`GET /v1/models`**(带 Bearer)→ `{models:[{name,id,provider,reasoning,vision,…}],default,defaultModel,droppedEntries}`,只含名字/能力、**不含密钥** —— 消费方(如 OA)拿它做 `@` 自动补全下拉的数据源;`reasoning` / `vision` 两列是**三态**:声明了给布尔,**未声明 = 键缺席**(7.131.0 起;env 部署不设 `MODEL_REASONING` / `MODEL_VISION` 就是未声明,没有 `true` 缺省 —— 需要思考的模型显式写 `MODEL_REASONING=true`,纯文本模型写 `MODEL_VISION=false`;缺席时引擎对图收、对思考档不发),契约 §A.1d;`droppedEntries`(7.123.0 起恒在场,零条 = `[]`)是本代目录里被读腿逐条丢掉的条目(下标 + 闭集原因词 + 可披露身份),契约 §A.1d。 7.126.0 起另有**可选**顶层键 `candidateRejected: { domains, at, source }`:最近一次配置候选被候选判官整拒时在场(`domains` = 判形失败的配置域名,`source` = `local` 本机 `config.d` / `remote` 中心,`at` = 判下它的时刻)—— 在场就说明这份目录是回落的那一份(整份候选没收,候选里的模型一个都不在),真因域在 `domains` 里;后来的候选被判 clean 即缺席;dry-run 姿势(web-admin 腿 compare-only)恒缺席(拒判只走日志)。**缺席不等于配置一定干净**(也可能是更早的 server)。只含域名 / 时刻 / 源形态,拒因句与文件路径只在引擎日志 `config_candidate_rejected` 里,契约 §A.1d。 7.133.0 起另有**恒在场**的顶层键 `modelPlane: { servedTable, catalog, refusalCode?, gap? }` —— 壳问 server,不猜:`servedTable`(`"env"` | `"catalog"`)= 此刻服务哪张模型表;`catalog`(`"absent"` | `"adopted"` | `"refused"`)= 最近一份目录候选在两道判官手里的结局(`refused` = 候选判官整拒,或模型面组拒;`adopted` 说的是「没有被判官整拒」,不是「最新候选已落地」,后者问 `/health` 的世代账;没有 LKG 时服务的是 env 表,有 LKG 时目录表仍在服,谁在服归 `servedTable`;逐条被拒的条目不算整拒);`refusalCode` / `gap` 是起服路由判官(`judgeServedModelRoute`)对当前 config 的判决,通过 ⇒ 两键缺席(进程起来了,正常缺席;自建装配 / 不拒启的入口可能在场),不带拒因句。只在起服后可答,没有起服前的预判口;新键追加在应答最末。契约 §A.1d。 7.135.0 起另有**恒在场**的顶层键 `aliases: [{ name, atMentionable? }]` —— `model` 被认、却不是 `models` 行名的每一个词:`default` 别名(服务表有缺省模型才在)、按部署档位绑定铸出的档位词 / CC 别名 / `best`、以及被认的上游 id 写法;成员资格由受理口的判官逐词定(服务端不写词表)。判受理一条:`model` 的值是某一行或某一别名的 `name`(大小写敏感),且那一项的 `atMentionable` 不是 `false` ⇒ 过模型门;其余 ⇒ 400(不要再拿行的 `id` 推)。`atMentionable` 与行同一只判定、同一条在场律(`atMention` 不是 `all` 才出键)。零条 = `[]`,缺席 = 更早的 server(档位词 / `default` 判「说不清」)。契约 §A.1d。
434
+ - **`GET /v1/models`**(带 Bearer)→ `{models:[{name,id,provider,reasoning,vision,…}],default,defaultModel,droppedEntries}`,只含名字/能力、**不含密钥** —— 消费方(如 OA)拿它做 `@` 自动补全下拉的数据源;`reasoning` / `vision` 两列是**三态**:声明了给布尔,**未声明 = 键缺席**(7.131.0 起;env 部署不设 `MODEL_REASONING` / `MODEL_VISION` 就是未声明,没有 `true` 缺省 —— 需要思考的模型显式写 `MODEL_REASONING=true`,纯文本模型写 `MODEL_VISION=false`;缺席时引擎对图收、对思考档不发),契约 §A.1d;`droppedEntries`(7.123.0 起恒在场,零条 = `[]`)是本代目录里被读腿逐条丢掉的条目(下标 + 闭集原因词 + 可披露身份),契约 §A.1d。 7.126.0 起另有**可选**顶层键 `candidateRejected: { domains, at, source }`:最近一次配置候选被候选判官整拒时在场(`domains` = 被判官整拒点名的配置域名 —— 判形失败的域、本机 `config.d` 里读不出的文档所在的域、带事实的退役域,`source` = `local` 本机 `config.d` / `remote` 中心,`at` = 判下它的时刻)—— 在场就说明这份目录是回落的那一份(整份候选没收,候选里的模型一个都不在),真因域在 `domains` 里;后来的候选被判 clean,或配置源回到已应用的那一代(`config.d` 原样改回 / 中心退回那一版,日志 `config_source_back_to_applied`)即缺席;dry-run 姿势(web-admin 腿 compare-only)恒缺席(拒判只走日志)。**缺席不等于配置一定干净**(也可能是更早的 server)。只含域名 / 时刻 / 源形态,拒因句与文件路径只在引擎日志 `config_candidate_rejected` 里,契约 §A.1d。 7.133.0 起另有**恒在场**的顶层键 `modelPlane: { servedTable, catalog, refusalCode?, gap? }` —— 壳问 server,不猜:`servedTable`(`"env"` | `"catalog"`)= 此刻服务哪张模型表;`catalog`(`"absent"` | `"adopted"` | `"refused"`)= 最近一份目录候选在两道判官手里的结局(`refused` = 候选判官整拒,或模型面组拒;`adopted` 说的是「没有被判官整拒」,不是「最新候选已落地」,后者问 `/health` 的世代账;没有 LKG 时服务的是 env 表,有 LKG 时目录表仍在服,谁在服归 `servedTable`;逐条被拒的条目不算整拒);`refusalCode` / `gap` 是起服路由判官(`judgeServedModelRoute`)对当前 config 的判决,通过 ⇒ 两键缺席(进程起来了,正常缺席;自建装配 / 不拒启的入口可能在场),不带拒因句。只在起服后可答,没有起服前的预判口;新键追加在应答最末。契约 §A.1d。 7.135.0 起另有**恒在场**的顶层键 `aliases: [{ name, atMentionable? }]` —— `model` 被认、却不是 `models` 行名的每一个词:`default` 别名(服务表有缺省模型才在)、按部署档位绑定铸出的档位词 / CC 别名 / `best`、以及被认的上游 id 写法;成员资格由受理口的判官逐词定(服务端不写词表)。判受理一条:`model` 的值是某一行或某一别名的 `name`(大小写敏感),且那一项的 `atMentionable` 不是 `false` ⇒ 过模型门;其余 ⇒ 400(不要再拿行的 `id` 推)。`atMentionable` 与行同一只判定、同一条在场律(`atMention` 不是 `all` 才出键)。零条 = `[]`,缺席 = 更早的 server(档位词 / `default` 判「说不清」)。契约 §A.1d。
430
435
  - 不设 `MODEL_CHEAP_ID` → 所有角色 = 主模型(行为不变)。
431
436
  - `MODEL_CHEAP_ID` 写成与 `MODEL_ID` **同一个 id**(7.104.0 起)→ 那就是主模型本身:不铸 cheap 槽,所有角色 = 主模型,起服一行
432
437
  `config_env_entry_cheap_same_as_main`(同时设的其余 `MODEL_CHEAP_*` 键一并不生效)。要给同一只上游 id 第二种形(另一个价 / 思考 /
@@ -440,6 +445,7 @@ MODEL_CODE_ROLES=default,subagent # 不设=全中立;仅这些角色在「
440
445
  ```
441
446
  - 经 `RoleSpec.promptRole: "code"` 挂在**角色**上(7.137.0 起 / 引擎 7.56.0;此前是把同一段目录文本粘进 `RoleSpec.systemPrompt`),非开发角色保持中立。角色基底只有一条来源链:任务 → 请求的模型角色的预设 → 引擎委派子代的人设,**第一个说出基底的来源整个说了算** —— 任务自带 `systemPrompt`(或客户端注入)、或场景自己写了角色词(cowork 场景写 `cowork`)时任务优先;这些 run 的宿主角色是 `code`(按 `when.host.role` 条件的 overlay 自此对它们生效),基底字节与此前相同。
442
447
  - **验证门**(core 1.44,opt-in):请求体带 `verify:true`(可选 `verifyRounds`,夹到 [1,5]、默认 2)→ 任务跑完后由**独立只读 verifier**(`verifier` 角色,默认=主模型)证据强制地"试图 break 它",FAIL 则把 findings 注回同 session 续跑修复→重验,循环到 PASS 或轮数上限。结果带 `verification:{verdict,rounds,findings,evidence}`(`verdict` 看质量,`result`/`status` 仍是实现的)。**仅 `/v1/tasks`(同步)与 `/v1/runs`(异步)**——`/v1/tasks/stream` 不支持(多轮非单流,请求 verify 会 400)。verifier 工具默认 = 实现任务工具滤掉 `effect:"write"`(只读边界)。`/metrics` 加 `verifications_total{verdict}`。⚠️ 同步 `/v1/tasks` 形**没有事件通道**(过程帧无处回放,只回终局 JSON)——带 `verify`/`cascade` 的 200 体自 7.23.0 起附可选 `notice` 键指路 `POST /v1/runs`(要过程可见性用异步形)。
448
+ - **实现腿撞资源限(`RESOURCE_SUSPEND` 开着)**:`verify:true` 任务的实现腿(或某一轮修复)撞预算 / turns / 墙钟限时**停驻**而不是 `failed`:`POST /v1/runs` 的行停在 `suspended`(门 `resource_limit`,无失败码)、会话保持占用;`POST /v1/tasks` 答停驻回执;用 `POST /v1/assistant/tasks/:id/resume` 续,续完照常过验证门。验证器自己那一腿由引擎另铸 spec、不带这个 opt-in,撞限照旧失败。跨片总额(`RESOURCE_SUSPEND_TOTAL_*` / `_MAX_SLICES`)按**一条腿的**续跑链记:验证门的每一轮修复是引擎另起的一次运行,账从零起,所以 verify 任务的花费上界是实现腿那条链与各轮修复各自那条链的上界之和,没有跨腿的总额。没开 `RESOURCE_SUSPEND` 的部署行为不变(撞限 `failed` + `limits.*` 码)。停驻期间同会话再提交 / 删会话 / 换绑设备 / 会话同步都 409,先续跑或取消;被弃置的停驻由 `RESOURCE_SUSPEND_TTL_SEC` 到期收回(缺省 30 天)。
443
449
  - **记忆(文件记忆引擎,2026-07-08 起唯一记忆面)**:core 注入式文件引擎——任务开始时 materialize 记忆目录(`MEMORY_ENGINE_DIR`,默认 `~/.ai-agent`),模型用**普通文件技能**读写记忆(CC `# Memory` 指令 + 派生索引;无 remember/recall 工具),任务边界 harvest 门(secret/cap 扫描)提交。单用户默认开,`MEMORY_ENGINE=off` 显式关;多租户恒关(文件基座无租户隔离,fail-closed)。旧 SQL 记忆面(`MEMORY_BACKEND`/`EMBEDDING_*`/`MEMORY_READ_LIMIT`/去重/向量检索、`GET/DELETE /v1/memory` 与 session memory 写 verb)已退役,数据不迁移——升级后对库跑一次 `scripts/drop-memory-tables.sql`。`body.memoryWrite:false` 仍是每请求只读开关(harvest 不提交)。
444
450
  **`MEMORY_PERSISTENCE_CAPABLE`(三态,7.14.0 起)**=**部署自述**「本机能不能把用户的『记住 X』落到持久处」,与请求键 `memoryWrite` 两轴正交(后者收紧本次运行,前者是 operator 声明;任一取否即只读方向)。缺省不设=引擎按 roster 自行推断;`true`=本部署有推断看不见的持久通道(自定义 writer / 记忆 MCP / 远程执行车道与记忆根共享挂载);`false`=强制只读披露并关掉文件工具往记忆根的写通道。不认得的词拒启。
445
451
  🔴 **`false` 自 core 5.27.0(7.14.0 提货)起还多一层——但只在 file 记忆引擎上**(`MEMORY_ENGINE_BACKEND` 未设 = 默认单机形):该会话是**受限会话**——materialize/搜索/harvest 一律按**已提交账**供给,记忆根下无事务背书的磁盘分歧既不收编也不供给,而是留盘 + 响亮点名(`restricted_divergence`,本服务把它计进 `memory_harvest_rejections_total{code}` 并 warn 一条),等下一次**非受限**会话走正常门收编。合法流程不受影响:用户手改、`git pull` 落下的删除照旧(延后,不销毁),并发的可写会话提交的变更算有事务背书。
@@ -660,7 +666,13 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
660
666
  (权限、挂载丢失、路径被换走)时**缺省行为是照常起动**,只留一行 `file_run_store_hydrate_unreadable`
661
667
  的 **error** 日志(带 runs 目录绝对路径 + 异常文案)与 `fail_open_total{tag="server.run-store.
662
668
  file-hydrate-unreadable"}` 计数 —— 但那之后**每一次 `GET /v1/runs/:id` 都会 miss**,而「miss」在 wire
663
- 上与「本来就没有这条 run」完全同形。把 run 账本当真账本用(对账、审计、外部系统按 taskId 回查)的
669
+ 上与「本来就没有这条 run」完全同形。同一旋钮管的另一个盘面是 `runs/active/`(单活索引)列不出而
670
+ `runs/` 可读:缺省同样照常起动,留一行 `file_run_store_active_claims_unreadable` 的 **error** 日志与
671
+ `fail_open_total{tag="server.run-store.file-active-claims-unreadable"}` 计数 —— 已有 run 行仍可读,
672
+ 但之后对这些会话的审批决定会在请求里同步跑完、却找不到要更新的 run 行:决定已落,而
673
+ `GET /v1/runs/<taskId>` 停在决前的 `suspended`(这条 run 行不跟着翻);同一期间读单活索引的各处
674
+ (例如会话同步推送前的「会话有在飞 run」检查)读到「没人占用」。新提交不读这张索引:claim 文件完好时
675
+ 仍按盘上的 claim 裁定,不受影响。修好目录权限 / 挂载后重启即恢复;下面的 `on` 同样把这一形变成拒启。把 run 账本当真账本用(对账、审计、外部系统按 taskId 回查)的
664
676
  部署可以设 `on`:此时那次失败**拒启**,遗言仍带绝对路径与异常文案,拒因给三条出路(修权限/挂载、
665
677
  把 `LOCAL_DATA_ROOT` 指到可读目录、或撤掉本旋钮按旧形起动)。
666
678
  值取 on/off 双族词表(`on|true|1|yes|enabled` / `off|false|0|no|disabled`,空串=未设);**两族都不
@@ -711,6 +723,28 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
711
723
  (装 Git for Windows,或把 `HOST_SHELL_PATH` 设到 bash.exe);`info host_shell_resolved { shell }` —— 解析到了(起服成功,或失败之后装好 / 改好被下一次解析拾起),`shell` 是解析到的路径。
712
724
  - 找不到时:宿主车道的其它工具照常;要起 shell 的调用到 spawn 时以同一句真因拒(`shell_unavailable` / hook 故障帧),装好后下一次调用自动拾起,不用重启。macOS / Linux 不打这两行。
713
725
 
726
+ **文件写入的中断语义(七条执行车道一条规则;7.139.0 起)**
727
+ - `writeFile`(Write / Edit / NotebookEdit 落盘的那一步)= **整文件替换:目标要么整份是旧内容、要么整份是新内容;取消(`aborted`)⇒ 目标未碰**。
728
+ host 车道 = 引擎的整文件替换(同目录暂存 + fsync + rename);ssh / adb / local-docker / k8s / e2b = 先在目标目录下建一只生来 0700 的私有目录
729
+ `.<名>.stage-<12 位十六进制>/`,整份传进去,再在目标机上一次 `mv -f` 换上去;device = 设备侧执行器在同一步里暂存再换上。
730
+ 传输中途断、取消、写到一半出错 ⇒ 目标逐字是旧内容,私有目录被删(删不掉 ⇒ 错误句点名残留的目录)。修前 host 与五条远端车道是就地截断写:中途中断留下新内容的前半截,原件找不回。
731
+ - 暂存自创建起不比目标更可读:远端五车道的暂存只在私有目录里(别的用户进不去;替换 0600 的文件时新内容在传输期间只有写入方与 root 读得到),device 的暂存件以 0600 打开;
732
+ host 车道的这一保证在引擎原语里:引擎 7.60.0 起目标存在时暂存件以 0600 打开。新建的文件是写入方身份的 umask 形(local-docker = 容器内镜像用户的)。
733
+ 私有目录建出后要读回核实是 0700 且属写入方;**核不过(`chmod` 被拒、文件系统不认权限位、缺省 ACL 的掩码收不下来、目标机没有 `stat`)⇒ 不暂存,退回就地写目标**
734
+ (两只文件面、五条腿都有;非原子;ssh / local-docker / k8s 写进原 inode、权限位不变,e2b 的 `files.write` 与 adb 的 `adb push` 整份写目标、inode / 权限位由对端定;
735
+ 遥测计 `server.remote-write.stage-seal-fallback`);adb 的追加 = 7.138.x 的形:读旧 + 拼 + 整份 push(push 没有追加写)。Android 共享存储(`/sdcard` 一类)上 adb 的写按 sdcardfs / MediaProvider FUSE 的权限语义**推断**每一次都走这一形(未上真机,真机读数候 test)—— adb 的工作区放在 `/data/local/tmp` 一类认权限位的目录即照常暂存。
736
+ - 一个身份:准备目录、上传、提交、清理都以同一个身份做。local-docker 的上传是容器内以镜像的用户经 stdin 收字节(不是守护进程以 root 的 `docker cp`)——
737
+ 写文件与代理的 shell 同一套权限,镜像用户写不了的路径答 `permission_denied` —— 镜像里没预建的工作区目录由容器启动以 root 建出,镜像用户 ≠ root 时要在镜像里预建并归镜像用户。
738
+ - 回落(host 与五条远端车道,与引擎同一套;device 由设备侧执行器自定,非常规文件拒写):单跳符号链接 ⇒ 替换它指的那只常规文件(链接仍是链接;远端车道先在那只文件的目录里重新暂存,rename 只在一个目录之内);链接链 / FIFO / 设备文件 / rename 被拒(如单文件绑定挂载)⇒ 由已落定的暂存件就地拷入(非原子);
739
+ 目录不收新名字(只读目录里的可写文件)⇒ 直接写目标一次(旧形,同样非原子;远端车道在这一形里写目标那一步因取消中断答 `unknown`「目标可能只写了一半」,不答 `aborted`;取消落在那一步开始之前(读旧也在这之前)照答 `aborted`、目标未碰;那一步不收取消(ssh 的 sftp)⇒ 照样写完、答成功;
740
+ host 车道的回落形由引擎就地写,中途取消目前仍答 `aborted` —— 已知缺口,修在引擎)。
741
+ 已有文件的权限位带过去;属主不带(替换后的文件属于写入方)。
742
+ - 已知语义变化(与引擎 / 参照实现同形):rename 换的是目录项 ⇒ **硬链接被断开**(另一条链接留旧内容);目录可写时**没有写权限位的文件也会被替换**(权限位保留);
743
+ host 车道上名字超过约 230 字节(随服务进程号的位数浮动)的文件写不进(引擎的暂存名超出 255 字节的名字上限 ⇒ 错误码 `unknown`,`ENAMETOOLONG`;远端五车道的暂存名会截短,不受影响)。
744
+ - `appendFile`(后台输出镜像等)= **追加**:暂存的只是要加的字节,提交是目标机上一次 `cat >>`(host = 本服务的一次 O_APPEND 写);inode 不换(`tail -f`、硬链接照旧)、已有字节从不重写;
745
+ 取消 / 传输中途断 ⇒ 一个字节不加;提交本身在目标机上失败(如磁盘满)⇒ 可能只加了一部分。提交那一次往返本身断了 ⇒ 错误码 `unknown`,
746
+ 句子写明「提交跑没跑不可知」—— 重试前先读一次目标。
747
+
714
748
  **workspace 浏览面 —— 已随整树快照纪元退役**
715
749
  - 旧四端点(list/tree/file/archive)恒 `501 capability.workspace_retired`,`capabilities.workspace`
716
750
  恒 `false`。理由:rewind 换代为 per-file 历史(只备份代理**编辑过**的文件,无树遍历、无体积门),
@@ -718,14 +752,14 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
718
752
  - `WORKSPACE_FILE_MAX_BYTES` 随面转 INERT(值仍收下并校验,center 推送兼容;不再有任何效果)。
719
753
  - rewind 的新姿势:提交带 `resumeAt` + `restoreFiles: true`(或 code-only 的 `rewindFilesTo`)⇒
720
754
  引擎把**本会话跟踪集**收敛到该消息时刻的边界;`acceptPartialRestore: true` 容忍部分恢复
721
- (缺省=响亮失败带逐文件账本)。旧键 `rewindFiles` 的 capture 义=容忍 no-op(首触跟踪恒开),
722
- restore 义(与 resumeAt 同发)=typed 拒并指路 `restoreFiles`(迁移显式化是 core 裁定)。
755
+ (缺省=响亮失败带逐文件账本)。旧键 `rewindFiles` 已不在提交面上:fresh 提交带它 ⇒ 400 `request.body_shape`
756
+ (`unknownKeys` 点名它;捕获不需要请求,还原写 `resumeAt` + `restoreFiles: true`);盘上旧任务体里的它零读者。
723
757
  - rewind 与设备车道(`REMOTE_EXEC=device`):带 `resumeAt` 的那一发是**根提交**(不是续跑一族),`deviceId` 照收、按根提交的规矩判 —— 已绑:缺席 = 沿用 / 同 = 幂等 /
724
758
  异 = 403 `device.identity_mismatch`(rewind 不换设备,换设备走显式换绑);未绑:携 = 首绑、缺席 = 409 `device.not_bound`;非 device 车道携它 = 400 `request.precondition_unmet`。
725
759
  `resumeAt` 自己的拒(值形 400、`resume_at.*`)先答,设备准入链后答。
726
760
  - **按 run 关掉捕获**(7.104.0):提交体带 `fileHistory: "disabled"`(唯一词;坏值 400
727
761
  `request.field_invalid`)⇒ 这条 run 的编辑照常落盘,但不做首次触碰备份、不铸回合边界,委派出去的子代继承;
728
- `wiring_manifest.fileHistory` 报 `"disabled"`。同一提交再带还原键(`restoreFiles` / `rewindFilesTo` / `rewindFiles`)⇒ 受理时
762
+ `wiring_manifest.fileHistory` 报 `"disabled"`。同一提交再带还原键(`restoreFiles` / `rewindFilesTo`)⇒ 受理时
729
763
  400 `request.field_conflict`(会话与文件都不动)。
730
764
  能力词 `fileHistoryCapture:"on-optional"` 表示本部署认这个词;要「本会话不捕获」就在每个提交上都带它。
731
765
 
@@ -744,7 +778,7 @@ SEND_USER_FILE_SANDBOX_PUT_ENDPOINT=… # 可选:沙箱直传 PUT 的端
744
778
  ```
745
779
  - **词表优先级**:内网端点 `MINIO_ENDPOINT` 优先,`S3_ENDPOINT` 是外接 S3 兼容位(内网/公网同址场景可只给它,同时充当公网面);公网端点 `S3_PUBLIC_ENDPOINT` 优先,缺席回落 `S3_ENDPOINT`。空串一律按未设处理。
746
780
  - **🔴 云形(`DB_BACKEND=mysql|pg`)快照 blob 强制对象存储(1.295+,clay 拍)**:缺 MinIO 三键 ⇒ **boot 拒启**——单行 SQL blob 写会撞 TiDB `txn-entry-size-limit`(默认 6MiB,真库实测矮墙)/ mysql 协议 `max_allowed_packet`,大字节归对象存储(与 D-1 附件面同裁定)。单机/测试台显式逃生:`SNAPSHOT_BLOB_ALLOW_SQL_BYTES=true`(SQL 店此时带 per-blob 帽,MySQL 协议(InnoDB 与 TiDB)默认 = **写入那条数据库连接**报的两堵已知墙取最小:单行上限减 1 KiB(blob 行自己的余量:哈希键 + 长度 + 时间戳;TiDB 才有)与包墙 `max_allowed_packet` 扣 1 KiB 语句余量后折半(⌊(包墙 − 1 KiB) ÷ 2⌋:文本协议把 blob 写成十六进制,语句至少是它的 2 倍);任一读数读不出时按该库族文档缺省并留一行运维日志 `fail_open`(tag `server.sql.packet-cap-unread` / `server.sql.tidb-row-cap-unread`),超限 PUT 413 `blob_too_large_for_sql`,句子按帽的出处分三形(运维旋钮 / 库报的墙 / 读不出而按文档缺省假定的墙),机读位 `wallSource` 同词 `operator` / `reported` / `assumed`。包墙是每条连接自己的会话值(7.133.0 起按连接判):DBA 调大 `max_allowed_packet` 之后,池里的旧连接在它第一次按包墙拒 blob 时自换(运维日志一行 `sql_connection_packet_wall_stale`,那一笔在新连接上重判),不必重启副本;`GET /v1/diagnostics/wiring` 的 `sqlEngine.maxRowBytes` / `sqlEngine.maxPacketBytes` 是最近一次初始化的那条连接的读数(见部署前置清单 DEPLOY-PREREQS(仓内)「数据库」条);帽可用 `SNAPSHOT_BLOB_SQL_MAX_BYTES` 按部署真实限值覆写,两方言生效;pg 默认无帽)。
747
- - **per-file rewind 历史的边界上限**(与 workspace 浏览面同一个整树快照纪元退役批留下的保留条款):`FILE_HISTORY_RETENTION_KEEP` —— 每个 scope(会话)存活的**最新** boundary 数,**每次 boundary 提交后自剪**(最旧者先亡,刚提交者恒存活;失引用的版本行级联删,blob 字节走异步 grace 窗 GC;v1 基线永存)。未设=core 缺省 **100**(由 core 的 `resolveFileHistoryRetention` 兑现,server 不复制那个数);`unbounded` 是**唯一**的「不剪」拼法(整店自剪关闭,GC 归部署自排 `reap`);`0`/负数/小数/非数 ⇒ **boot 拒启**(码 `config.retention_policy_invalid`,与 core 内置后端同码同判——**省略这个键不等于不剪**,它选的是缺省 100)。三条车道(TiDB / PG / local 文件店)同旋钮同判。⚠️ 升级即生效:存量超 100 boundary 的 scope 在下一次提交时被剪到 100;要保全量先写 `unbounded`。被剪 entry 的键**已花掉**(再次 snapshot 同 entry 典型拒,不复铸)。排序=DB 分配的 per-scope 发布序号 `file_history_boundary.publish_seq`(不是墙钟):**自 7.50.x 有存量 `file_history_boundary` 的部署升级本版会在 boot 期被 `assertFileHistorySchema` 拒启并指明 `DROP TABLE file_history_boundary`**(本仓无 ALTER;丢的是 per-file rewind 便利态,会话与其他持久面不动)。
781
+ - **per-file rewind 历史的边界上限**(与 workspace 浏览面同一个整树快照纪元退役批留下的保留条款):`FILE_HISTORY_RETENTION_KEEP` —— 每个 scope(会话)存活的**最新** boundary 数,**每次 boundary 提交后自剪**(最旧者先亡,刚提交者恒存活;失引用的版本行级联删,blob 字节走异步 grace 窗 GC;v1 基线永存)。未设=core 缺省 **100**(由 core 的 `resolveFileHistoryRetention` 兑现,server 不复制那个数);`unbounded` 是**唯一**的「不剪」拼法(整店自剪关闭,GC 归部署自排 `reap`);`0`/负数/小数/非数 ⇒ **boot 拒启**(码 `config.retention_policy_invalid`,与 core 内置后端同码同判——**省略这个键不等于不剪**,它选的是缺省 100)。三条车道(TiDB / PG / local 文件店)同旋钮同判。⚠️ 升级即生效:存量超 100 boundary 的 scope 在下一次提交时被剪到 100;要保全量先写 `unbounded`。被剪 entry 的键**已花掉**(再次 snapshot 同 entry 典型拒,不复铸)。排序=DB 分配的 per-scope 发布序号 `file_history_boundary.publish_seq`(不是墙钟):**自 7.50.x 有存量 `file_history_boundary` 的部署升级本版会在 boot 期被 `assertFileHistorySchema` 拒启,拒因打印这张表缺的列的 `ALTER TABLE … ADD COLUMN …`,由部署者自己执行**(列集对账与拒启句只打印语句、永不改库;per-file rewind 历史照留,会话与其他持久面不动)。⚠️ 打印的语句取自建表语句原文:`publish_seq` 是 `NOT NULL` 且无缺省的列,在已有行的 PostgreSQL 表上那句原文会被引擎拒(列含空值)—— 这种存量表要部署者自己按 per-scope 的发布顺序补值后再改写执行;这是起服对账拒启句同一生成器的既有性质。
748
782
  - **会话同步重推(同图)要读 blob**(7.136.0 起):`POST /v1/sessions/:id/sync/import` 的 identical 重推臂,对云端「已握着同一张文件历史图」的目标,也先把信封引用的每一枚 blob 经云端自己的店读一遍、核哈希(不回写),再答成功 —— 对象存储里的对象若在图发布之后丢了,这一腿答 `422 missing_blob`(`PUT` 缺的 blob 再推;图不动),不再答 `200 identical`。代价:无变化会话的频繁重推多付对象存储读流量(每枚引用的对象一次下载,正比于引用的 blob 字节);没有旋钮。
749
783
  - **文件历史捕获总开关**(7.97.0 立,7.104.0 改语义):`FILE_HISTORY_CAPTURE`(缺省 `true`;布尔词表 true/false/1/0/yes/no/on/off,未知词 ⇒ boot 拒启;TiDB / PG / local 三条车道同义)。`false` ⇒ **只关捕获**:历史店照接,引擎拿到部署捕获位 `RunnerDeps.fileHistoryCapture: "off"`,改动文件的首次快照**一个都不捕获**、不铸回合边界(`wiring_manifest.fileHistory` 报 `"disabled"`);**已存历史照常可用** —— `resumeAt`+`restoreFiles` / `rewindFilesTo` 照常还原、2c session-sync 照常导出 / 导入。`GET /v1/capabilities` 的 `fileHistoryCapture` 报 `"off"`,而 `rewindFiles` / `restoreFiles` / `rewindFilesTo` / `sessionSync` 仍为 `true`(缺省部署报 `"on-optional"` = 捕获中且请求可按 run 关掉,见上一段)。🔴 **7.97.0–7.103.x 的旧语义**:`false` = 不把店交给引擎,连已存历史也读不了(还原走 `rewind.store_unconfigured`、sync 501)—— 把本旋钮当成「整个文件历史功能关掉」来用的部署,升级后已存历史**重新可以被还原 / 同步**。要让已存历史也不可用,今天的出路只有**逐会话** `DELETE /v1/sessions/:id`(擦除面不受本旋钮管)与 SQL 后端的留存清扫(`RETENTION_MODE=enforce` + `RETENTION_MAX_AGE_DAYS` + `RETENTION_SWEEP_INTERVAL_SEC`,按会话年龄删,local 车道没有留存车道);**没有**「整库历史一键不可用」的旋钮。⚠️ **关掉 ≠ 删掉**:翻旋钮不批量删除存量历史;local 车道上删会话走 `reap` —— 边界删光、v1 基线按 core 契约留存,既有残余。关着时 `FILE_HISTORY_RETENTION_KEEP` 没有效果(不再有边界提交,自剪不会触发),值仍照常校验。没有存储后端的部署(env-only worker)本来就没有历史店,旋钮在那里无事可关、不下发捕获位(引擎对「off 但没店」拒 prepare)。
750
784
  - **🔴 公桶策略必须 GetObject-only**:`mc anonymous set download` 会**连带打开 ListBucket**——匿名 `GET /<bucket>/?list-type=2` 能枚举全部不可猜 key,能力链接设计即告失效。正确姿势=`mc anonymous set-json`,policy 只含 `Action:["s3:GetObject"]` on `arn:aws:s3:::<bucket>/*`(验证:对象 GET 200、桶 LIST 403)。
@@ -1275,7 +1309,7 @@ TASK_WRITE_FACE_ESCALATION=allow # 词表 allow|refuse;坏词启动期响亮
1275
1309
  | 未设 | **是**(`REQUIRE_PRINCIPAL=true`) | **422 拒**,拒因点名 `TASK_WRITE_FACE_ESCALATION=allow` 与判成托管的证据 |
1276
1310
 
1277
1311
  - 🔴 **`REMOTE_EXEC=device` 车道上本门不适用**(任何 `REQUIRE_PRINCIPAL` 值):那条车道的路径落在
1278
- **发起者自己的设备**上,归属由 `device_session` 绑定行 + 准入链的 owner 谓词验过 —— host 闸防的
1312
+ **发起者自己的设备**上,归属由根会话行上的设备绑定(执行放置那一格)+ 准入链的 owner 谓词验过 —— host 闸防的
1279
1313
  跨租户宿主穿越在那儿**结构性不存在**(成文裁定,与 `settings.env` / `cwd` 两键在该车道上的既有臂同源),
1280
1314
  也叠加「device 车道不额外加一道审批」这条既定纪律。⚠️ 运维的**显式** `refuse` 在 device 上照样恒赢(旋钮不挂车道派生腿)。
1281
1315
  - **「托管形」怎么判**(判据单点 `src/hosted-posture.ts`;7.99.0 起):
@@ -1639,7 +1673,7 @@ invalid_using_default` 警告 + 退回缺省值」这条臂随词表放宽一并
1639
1673
  **请求级 `inheritEnv`(7.106.0 起)—— 调用方声明,不是部署旋钮**:提交体顶层键 `inheritEnv:"all"` 让**这一条请求**的模型驱动 host shell
1640
1674
  继承整份用户环境(CC 交互形,用户自己的凭据形变量在内)—— 🔴 **但不含本服务自己的凭据**(7.129.0 起,引擎 7.51.1):配置目录全部 `type:"secret"` 名
1641
1675
  (`SERVICE_AUTH_TOKEN(S)` / `APPROVAL_HMAC_KEYS` / `MODEL_API_KEY` …)与名单型旋钮名起服已向引擎声明为本进程自有凭据,生效配置里每条模型条目的 `apiKeyEnv` 也在名单里
1642
- (中心热应用新增 / 改名条目时在模型目录换代前重声明,名单只随落地的世代增长 —— 改名后旧名照扣到重启,没落地的候选不留痕;条目的 `apiKeyEnv` 是引擎扣不住的名 ⇒ 该候选整拒),`"all"` = 整份环境**减**它们,任何继承词都不把它们
1676
+ (中心热应用新增 / 改名条目时在模型目录换代前重声明,名单只随落地的世代增长 —— 改名后旧名照扣到重启,没落地的候选不留痕;条目的 `apiKeyEnv` 是引擎扣不住的名,或它点到的名正被某只名单型旋钮交给沙箱 / 语言服务器(见下文沙箱名单段)⇒ 该候选整拒),`"all"` = 整份环境**减**它们,任何继承词都不把它们
1643
1677
  交给模型 shell(能力位 `inherit_env.own_credentials_withheld` = 最近一次声明 ⊇ 当前生效配置应声明的集合;真要让模型 shell 持某枚令牌 ⇒ 另放一个不在配置目录、也不是任何模型条目 `apiKeyEnv` 的名,或经 `settings.env` 显式授予);`"scrub"` / 缺席 = 上面的缺省剥除。🔴 **`"all"` 只在单用户部署兑现**:`REQUIRE_PRINCIPAL=true`
1644
1678
  ⇒ 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` 认、不带这两位。部署方
1645
1679
  **没有**旋钮能替调用方打开它,也没有旋钮能在单用户部署上关掉它(单用户部署的用户把他自己的环境交给他自己驱动的 shell);起服一行 info
@@ -1662,7 +1696,7 @@ bake-runner 要用 ⇒ `LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW` 点名放回。
1662
1696
  **「点名放行」只有一种意思**:本服务里凡是「一张名单点到哪些环境变量键」的地方(这里的 `LSP_ENV_ALLOW`、沙箱车道的
1663
1697
  `E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV` / `K8S_SANDBOX_ENV`(见下表),与**嵌入方**构造 host shell 时的 `inheritEnv:[names]` —— 后者不是部署旋钮,见上一段)用的是同一只谓词 ——
1664
1698
  点到的键从服务进程环境原样交出去(空串值照交),密钥形也照交;源里没有的名什么也不产生。四只名单型旋钮(`LSP_ENV_ALLOW` /
1665
- `E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV` / `K8S_SANDBOX_ENV`)另走同一只名单解析:形错拒启点名第几段、不回显原值,点名任一只名单型旋钮拒启,
1699
+ `E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV` / `K8S_SANDBOX_ENV`)另走同一只名单解析:形错拒启点名第几段、不回显原值,点名任一只名单型旋钮或本服务自有凭据名拒启,
1666
1700
  本进程环境里没有的名按序号一行 `config_env_entry_rejected`,旋钮原文按凭据类对待(配置目录只回在场位、不进任何子进程)。
1667
1701
  几处只在**没点到的键**上不同:语言服务器在缺省剥除的结果之上放回点名键(没点到的照剥,非密钥键照常在);host shell 的
1668
1702
  `inheritEnv:[names]` 与三只沙箱名单是 hermetic 的(没点到的一律不交,与引擎 `NodeExecutionEnv` 同名选项同义;7.129.0 起 host shell 的名单由引擎读 ——
@@ -1670,7 +1704,7 @@ bake-runner 要用 ⇒ `LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW` 点名放回。
1670
1704
  两处都不向对方看齐:host shell 改成「剥后放回」等于在模型可驱动的 shell 上放宽,语言服务器改成 hermetic 会让它当场找不到自己的工具链。
1671
1705
 
1672
1706
  **沙箱带外 env 名单(`E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV` / `K8S_SANDBOX_ENV`,前两只 7.101.0 起、`K8S_SANDBOX_ENV` 7.126.0 起,同上一段的解析与谓词)**:沙箱里的工具要用本服务
1673
- 进程环境里的凭据(如 git 走 `$GIT_TOKEN`),又不想让它进提示词、命令串或事件日志,就把**名字**写进名单,值只在服务进程环境里:
1707
+ 进程环境里的凭据(如 git 走 `$GIT_TOKEN`;只能是部署方给沙箱的那一把,不能是本服务自己的凭据,见表下),又不想让它进提示词、命令串或事件日志,就把**名字**写进名单,值只在服务进程环境里:
1674
1708
 
1675
1709
  | 键 | 车道 | 写法 | 行为 |
1676
1710
  |---|---|---|---|
@@ -1680,9 +1714,15 @@ bake-runner 要用 ⇒ `LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW` 点名放回。
1680
1714
 
1681
1715
  三只旋钮只在**自己的车道被选中时**才求值(别的车道上它们是无关配置,不校验、不告警)。未设 / 空串 = 不注入。**形错拒启**:空段
1682
1716
  (`A,,B`、尾逗号)、非变量名形(`1BAD`、带空格、`NAME=value` 整段贴进来)、点名名单型旋钮(`E2B_SANDBOX_ENV=E2B_SANDBOX_ENV`,
1683
- 或 `LSP_ENV_ALLOW` / 另一只沙箱名单)⇒ 起服失败,拒句点名键与第几段,**不回显原值**。形对但服务进程环境里**没有**这个名 ⇒ 不注入,
1717
+ 或 `LSP_ENV_ALLOW` / 另一只沙箱名单)⇒ 起服失败,拒句点名键与第几段,**不回显原值**。**点名本服务自有凭据名** —— 配置目录里的 secret 型变量(如
1718
+ `SERVICE_AUTH_TOKEN(S)` / `APPROVAL_HMAC_KEYS` / `MODEL_API_KEY` / `E2B_API_KEY` / `K8S_TOKEN` / `GIT_API_TOKEN`)—— 同样起服失败:名单会把这枚凭据原样装进模型读得到的沙箱;
1719
+ 与 `LSP_ENV_ALLOW` 同一条律、同一只判官(引擎拒白名单的那一只,按平台比名),拒句同一个开头 `this deployment's own credential — `,点名键与第几段,只回显那个声明过的名。
1720
+ 沙箱要凭据 ⇒ 给它铸一把自己的(名与值都不同于本服务的任何凭据),放进服务进程环境再点那个名;判的是名字,把同一个值换个名字再点,起服认不出、值照样进沙箱 ——
1721
+ 所以「值也不同」是出路的一半。**中心 / config.d 模型条目后来点到同一个名**:条目的 `apiKeyEnv` 在模型目录换代时才并进本服务自有凭据名,那一刻对本表三只与
1722
+ `LSP_ENV_ALLOW` 此刻交出去的名用同一只判官重判 —— 新并进的名正被某只名单交出去 ⇒ 那份候选整拒、上一代照常服务,拒句点名旋钮、名与条目;出路二选一:给沙箱另铸一把并改名单
1723
+ (名单起服读,改了要重启),或在中心把条目的 `apiKeyEnv` 改指别的变量(下一拍自愈)。形对但服务进程环境里**没有**这个名 ⇒ 不注入,
1684
1724
  起服一行 `config_env_entry_rejected` 只报第几段。点到的键值为空串 ⇒ 照样注入一个空串(7.101.0 前跳过,见 CHANGELOG)。
1685
- `REQUIRE_PRINCIPAL=true` 下照常注入(与 `LSP_ENV_ALLOW` 不同:沙箱 env 是部署方给隔离沙箱显式配的)。
1725
+ `REQUIRE_PRINCIPAL=true` 下照常注入(与 `LSP_ENV_ALLOW` 不同:沙箱 env 是部署方给隔离沙箱显式配的);上面两类拒启不随姿态变。
1686
1726
  **起服可见面(7.101.0)**:三条车道都在 `remote_exec_enabled` 行带 `sandboxEnvKeys` —— 名单点到、且服务进程环境里真有(因而真被转发)的键名,字典序,只键名不带值
1687
1727
  (名单没配或点到的名全不在 ⇒ 不带该字段;e2b / k8s 腿不含 `SANDBOX_PKG_SOURCE` 推导出的包源变量);`REQUIRE_PRINCIPAL=true` 下另有一行 warn
1688
1728
  `config_env_forwarded_multi_tenant`(`env` = 旋钮名、`entries` = 名单段数、`note` = 姿态句:这张 worker 级名单进每个租户的沙箱),
@@ -1781,7 +1821,7 @@ PostgreSQL 硬上限 ⇒ 只有缩小),运维日志一行 `submit_ctx_row_too_la
1781
1821
  | `terminal.kind` | `completed` / `blocked` / `failed` / `paused`(闭集;`timeout` 已于 core 5.8.0/server 6.0.0 退役——见 §9) |
1782
1822
  | `terminal.code` | `failed` 臂的程序化分支:如 `"conflict"`(乐观锁丢失,可重试) |
1783
1823
  | `terminal.reason` | `blocked` 臂:为什么做不了(缺信息/权限) |
1784
- | `stats.toolCalls` | 工具调用的**尝试**次数,不是「真执行了几次」:模型每发出一次工具调用请求就计一次,计在工具查找与入参校验**之前** —— 请求了没挂载的工具(例:执行车道未点名时的 `Bash`)照样计 `1`;同一条 `Bash` 请求在真执行车道(文件落盘)与未点名车道(没挂、文件不存在)两臂同读 `1`。所以**不能**拿它判「任务真跑了工具」:判「真跑了没有」看 `tool_end.isError`(逐调用,`false` = 这次调用执行成功)与 `wiring_manifest.hands`(逐腿「有没有手」),不看这个数。它是**本腿**计数(续跑腿只计本腿)。 |
1824
+ | `stats.toolCalls` | 工具调用的**尝试**次数,不是「真执行了几次」:模型每发出一次工具调用请求就计一次,计在工具查找与入参校验**之前** —— 请求了没挂载的工具(例:执行车道未点名时的 `Bash`)照样计 `1`;同一条 `Bash` 请求在真执行车道(文件落盘)与未点名车道(没挂、文件不存在)两臂同读 `1`。所以**不能**拿它判「任务真跑了工具」:判「真跑了没有」不能只看一个位:`tool_end.isError` 是逐调用的**工具结果错误标记**(工具信封报错才 `true`),`false` 只说结果没被标错 —— **不**说命令退出成功(Bash 跑完退出码非 0 照样 `false`,退出码看 `structured.exitCode`),`true` 也不证明工具体跑过;「没跑」的正证据是 `notExecuted`;逐腿「有没有手」看 `wiring_manifest.hands`,不看这个数。它是**本腿**计数(续跑腿只计本腿)。 |
1785
1825
 
1786
1826
  > ⚠️ **任务成败以 `terminal.kind`/`terminal.code` 为准,勿以 HTTP 状态码判**:同步腿 200=提交受理成功(任务可能
1787
1827
  > `terminal.kind:"failed"`+`code`);`POST /v1/runs` 202=已受理(执行期结局落在终态 run 记录/`done` 帧)。执行期
@@ -1902,16 +1942,16 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
1902
1942
  | `GET /v1/sessions/<id>` | 审计回溯:当前上下文 + 摘要(owner 校验);消息里模型 / 工具产出的文本与 live 帧、账本帧同一只脱敏(密钥形换占位,其余字节不变);Read 图像 / PDF 的媒体卡(工具结果消息的 `details`)的 `file.base64` 是字节编码,逐字。**委派子代的转录**也走这一口:`<id>` = 子代帧 / fleet 子代行上的 `transcriptId`(未压缩过的子代首条用户消息是委派 prompt;子代跑过的工具是 assistant 消息里的 `toolCall` 段 + 同 `toolCallId` 的 `toolResult` 行;整段逐字条目走 `…/sync/entries`)。体顶层加性键 `agent: { id?, parentSessionId?, rootSessionId? }`(**7.133.0+**)说这是哪只子代:只投影会话行上存下来的委派元组,`id` 与 fleet 行的 `id`、`GET /v1/agents/roster` 的 `handle` 同一个串;键缺席 = 普通会话 / 老 server / 会话店不存元组(local 后端),读作「没存」、回落 fleet 行 / 名册上的 id。子代会话的属主是铸行那一刻它所在委派树的根会话的属主(**7.133.0+**,写下那一行时按当时读到的写入;此前 SQL 后端上子代会话无主、谁都读得到)⇒ 用发起子代的那位主体读;别的主体与不存在的 id 同一个 `404 not_found.session`。属主为空的委派子代行(根会话本就无主,或升级前写下、还没回填)只有 fleet 调用方读得到(出示 service 凭据、不带主体头;dev-open 部署不钉人),任何具名主体、没有 service 凭据的无身份调用方同样 404(存量无主会话互认臂 —— 单用户形下 `default` 主对无主的普通会话放行,fork / 删除 / `/sync/*`(同步族自 7.134.0 起)同走这一只 —— 对这些 placed 行同样不放行,审批豁免、notify 同样不放行;fork / 删除 / `/sync/*` 的 fleet 调用方与此处同一个判据:出示 service 凭据、不带主体头,带主体头的 service 凭据请求是那位主体,7.134.0 起);SQL 后端每次起服回填这些行(起服读数行 `placedSessionOwnersRemaining` 非零 = 还有没定下的,再起直到为 0,或等它们下一次停驻)。推不出属主的子代写成属主 `_orphan:<id>`,只有 fleet-wide / operator 读得到:SQL 后端的子代行根会话已删时按它记录的父会话,父也删了才是 `_orphan:`;停驻时拷进宿主店的那一份(local 后端)只按根判,根会话已删 ⇒ `_orphan:`;子代被派出 / 批准之后、再次停驻读根之前根会话被删并由别人以同一 id 重新登记 ⇒ 其下的子代再次停驻被否决(读过根之后才换的,写进去的仍是读根那一刻的属主) |
1903
1943
  | `GET /v1/approvals?owner=user:42` | 高危写审批(durable,需 `DURABLE_APPROVAL=true`):**operator** 看待办队列(可按 owner 过滤);非 operator 只看自己的。队列里某一行的存储体读不出来、重试不会好时,答 `500` + `errorCode:"approvals.read_corrupt"`(**7.134.0+**;与订阅流同码、同一只判官;运维处置那一行后再读,别原样重试);此刻读不出队列(连接 / 超时 / 店抖动)⇒ `503` + `errorCode:"approvals.read_unavailable"`(**7.135.0+**,此前 500 `internal.error`;稍后重发,这一答不表示队列为空)。`GET /v1/assistant/inbox` / `GET /v1/assistant/tasks` 读同一个队列,两种读失败同码同状态(**7.135.0+**)。停驻的**技能卡**(对引擎 `Skill` 工具的调用)的行带 `boundGrantDigest` / `boundGrants`(批准它还授予的东西:技能名 + 两只工具清单;清单只在哈希等于摘要时才有,是部署文本,渲染要按上下文转义),分类器没给判词的卡带 `classifierNoRuling`(`error` / `timeout` / `parse_error`)—— **7.136.0+**,与 `GET /v1/assistant/inbox` 行逐字同值(此前只有收件箱行与活卡帧带);普通卡三位都缺席。`GET /v1/assistant/inbox` 行的 `objective` 是门所属 run 的**目标预览**(写入时已脱敏、截到约 120 字符,与 `GET /v1/assistant/tasks` 读同一列;委派子代的门读派它的那条 run;没有 run 账本 ⇒ `null`;**7.136.0+**,此前原样回显存着的任务目标,运维 `?owner=` 跨看也是原文) |
1904
1944
  | `GET /v1/approvals/stream` | 同一队列的 SSE 推送(范围同上):首拍快照 → `pending` / `resolved` 增量(开了工具审批时另有 `live_pending` / `live_resolved`),空闲时心跳,15 分钟上限(`STREAM_MAX_DURATION` 帧后重连)。**心跳只表示流活着,不表示这一拍读过队列**:队列读不出时每次读失败发一帧 `error`(`approvals.read_unavailable`,流留着、下一拍再读,恢复后同一条流继续);某一行的存储体读不出来、重试不会好时发 `approvals.read_corrupt` 并收流(运维处置那一行后再重连)。15 分钟上限对读失败的拍同样算数。`pending` 帧带的行与 `GET /v1/approvals` 同一只投影(含上面的技能卡三位,**7.136.0+**) |
1905
- | `POST /v1/approvals/<sessionId>/decide` `{"decision":"approve"|"deny","reason":"…"}` | 批/否并恢复挂起任务(CAS,重复决议 409);**仅 operator**(非 operator → 403)。5.0.0 起旧轮询腿 `POST /v1/approvals/<id>` 已退役。停驻的后台子代**复活不了**(行上没有根会话锚;未起名的后台子代不在此列 —— 引擎 7.57.0 起每只子代都有名字,升级前的无名行读作派生名,照常复活)⇒ `deny` 作废这张卡:`200 {taskId, status:"failed", decision:"deny"}`,卡离开待决队列、子代终局 `failed`;`approve` 答 `409 decide.row_unrevivable`、拒句指路 deny(**7.136.0+**;此前两个决定都 409,卡处理不掉) |
1945
+ | `POST /v1/approvals/<sessionId>/decide` `{"decision":"approve"|"deny","reason":"…"}` | 批/否并恢复挂起任务(CAS,重复决议 409);**仅 operator**(非 operator → 403)。5.0.0 起旧轮询腿 `POST /v1/approvals/<id>` 已退役。停驻的后台子代**复活不了**(行上没有根会话锚;未起名的后台子代不在此列 —— 引擎 7.57.0 起每只子代都有名字,升级前的无名行读作派生名,照常复活)⇒ `deny` 作废这张卡:`200 {taskId, status:"failed", decision:"deny"}`,卡离开待决队列、子代终局 `failed`;`approve` 答 `409 decide.row_unrevivable`、拒句指路 deny(**7.136.0+**;此前两个决定都 409,卡处理不掉)。有根会话锚的停驻子代卡上,`decide` 与删根会话 / 卡作废并发:写口放行之后卡已被作废 ⇒ `422 parked_resume.startup_failed`(卡已作废、什么都没复活;与 `409 decide.gate_moved` 并列为并发终局码,调用方须同时处理 409 / 422) |
1906
1946
  | `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 |
1907
1947
  | `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) |
1908
1948
  | `GET /v1/capabilities/scenarios/<name>` | 单个场景的只读详情:工具面、提示词概览、**本部署现在跑不跑得动**、附件供给、**技能清单**(见下) |
1909
1949
  | `GET /v1/capabilities/mcp[?scenario=<场景名>]` | **无需先跑 run 的逐台 MCP 状态**(现连、列工具、关掉;按调用方限速 + 30 秒复用)。答的是**某个场景**会挂哪些中心声明的 MCP 服务器:不带参数 = 这位主体 run 时的场景(**7.131.0+**:中心按主体指派的场景胜出,否则部署缺省场景;此前恒读部署缺省场景;指派悬空 ⇒ `503 state.scenario_assignment_unknown`,与提交任务时同一句;`GET /v1/sessions/:id/mcp` 与 `…/a2a` 两张会话面板同读这位主体 run 时的场景:a2a 面板悬空时同样整面 503,而 mcp 面板另有「这个会话上一次真挂上了什么」那一半(`lastLegMcp`),悬空时答 `200`、`lastLegMcp` 照答、`servers` 缺席、拒因在 `scenarioRefusal: { errorCode, error, detail }`(与状态面整面 503 的体是同一份成员,`detail` 是门拒的机读事实 `{ scenario, source }`;`servers` 缺席 = 场景这一半没答,`servers: []` = 该场景没配 MCP));`?scenario=<名>`(**7.130.0+**)= 点名场景,可见集与同场景任务 run 时是同一只解析、过同一只场景门 —— 不在册 `400 scenario_unknown`(附在册词表;空串 `?scenario=` 同此,与提交 `scenario: ""` 同答)、这位 principal 的场景白名单集外 `400 scenario_not_allowed`(附 allowlist),两者的体与提交任务时的拒逐字同形;重复给出参数 `400 request.field_invalid`(提交腿没有这一形)。场景门接缝 `scenarioGate` 是 `createHttpServer` 的必填件:自建 `ServiceDeps` 的嵌入方用包根导出的 `createScenarioGate` 铸它(`"peek"` 腿给读面),没递就是 tsc 红。老 server 忽略这个参数、回缺省场景那一份,按 `GET /v1/capabilities` 的 `version` 判。`REQUIRE_PRINCIPAL` 下无身份 `401`(先于场景门)。每一行(两条口同形)在 §G.7 的行形之外多两个可选键(**7.133.0+**):`toolNames`(这台会挂上的工具名,引擎命名空间形 `mcp__<server>__<tool>`,读数序)+ `toolNamesTruncated: true`(名单超过 200 只时只截表,`toolCount` 仍是真数;读数用 `toolCount`,别用 `toolNames.length`);该台读数里没有名单 ⇒ 两键缺席(不编 `[]`),没连上的台是在场的 `[]`;老 server 没有这两键,按缺席读。自带 `.mcp.json` 的探测口是 `POST /v1/capabilities/mcp/probe` |
1910
1950
  | `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` 已退役。 |
1911
1951
  | `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 原文。 |
1912
- | `POST /v1/admin/config/refresh` | **配置手动刷新**(operator-only,与上一行同门:没有运维名册〔`OPERATOR_PRINCIPALS` 为空且没有单用户声明 `OPERATOR_SINGLE_USER=true`〕⇒ 501;名单非空而调用方不在名单上 ⇒ 403)。改完中心配置 / `config.d` 之后不想等 60s 轮询就调它:触发**一次既有**的 refresh 拍(在飞则汇入那一拍,绝不并发双拍),返回时该拍已落地。200 体 `{triggered:true,targetVersion,appliedVersion}` —— 两个世代号与 `/health` 的 `configTargetVersion`/`configAppliedVersion` 同源。候选被判官整拒(坏域 / 坏 prompts / 带事实的退役域 —— 后者 7.126.0 起)是这一拍的结论,不是失败:照样 200,`targetVersion ≠ appliedVersion`,拒因在 `config_candidate_rejected` 日志行;拍真的失败(配置源不可达 / 读不了 / 应用抛错)⇒ `502 internal.config_refresh_failed`。本部署没有配置管道(纯 env worker:既没配 `CONFIG_CENTER`、也不是 `CONFIG_PROVIDER=local`)⇒ 409 `config.refresh_unavailable` 指路,不会回一个「触发了但什么都没发生」的 200。 |
1952
+ | `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。 |
1913
1953
  | `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 **持续**时长;收敛时键缺席)—— 编排器据此摘掉「配置持续落后」的副本 |
1914
- | `GET /v1/sessions/:id/background` · `POST /v1/sessions/:id/background/stop` `{"includeRetained":true\|false}` | **会话后台任务的读口与停止口**(**7.102.0+**;能力位 `background.listFace` / `background.exitFaces`,老 server 404)。读口列**本会话**、**本副本**上的后台任务行(`{ rows: [{ id, description?, type, status, createdAt, sessionScoped, retained? }] }`,`retained:true` = 声明了保留的活服务,正常退出不会停它);停止口「软 → 等一次 `HOST_BG_KILL_GRACE_MS` → 硬」收掉本会话的后台 shell(SIGTERM 处理器先有一次宽限跑完,之后组 SIGKILL,屏蔽 SIGTERM 的循环也活不过去;有后台行时应答慢一次宽限)并逐字返回受据 `{ receipts: [{ id, outcome }] }`:`killed` / `already_gone` / `retained_skipped`(`includeRetained:false` 留下的活服务)/ `no_pgid`(远端 / 沙箱车道没有本机进程组可发,已派普通 kill,「可能还在跑」;host 车道的 `already_gone` 只由观察得出 —— 引擎已收割组长或内核答组已空,7.103.0 起;darwin 上梯子那一枪先落地、引擎还没收割的那一拍如实答 `no_pgid`,判「收掉没有」读进程)。`includeRetained` 必填:`true` = 连活服务一起停(壳的「Exit and stop tasks」),`false` = 活服务照留(`sema kill`)。鉴权 = 会话属主或显式 operator(未知与他人同 404);停止口在无凭据写门内(没配 service 凭证且未开 `ALLOW_UNAUTHED_WRITES` ⇒ 503 `auth.service_token_required`)。`DELETE /v1/sessions/:id` 走同一只收敛(`includeRetained:false`);run 取消(Esc)**不**收后台 shell。契约 §16 |
1954
+ | `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 |
1915
1955
  | `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「探针」 |
1916
1956
  | `GET /metrics` | Prometheus 指标(有 token 时需带) |
1917
1957
 
@@ -1968,13 +2008,15 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
1968
2008
  > - `RETENTION_SWEEP_INTERVAL_SEC=<非负整数>`(默认 `0` = **关**;上限 `2147483` 秒 ≈ 24.8 天):
1969
2009
  > 托管留存 **sweep lane** 的节律。`>0` 才起这条腿。越上限拒启(`setInterval` 的 32 位 delay 帽:溢出会被
1970
2010
  > Node 静默重置成 1ms,把一次"偶尔扫一遍"变成近乎连续的破坏性事务流)。
1971
- > 🔴 **半配置一律拒启**(不看有没有上锁):`>0` 而 ①没配 `RETENTION_MAX_AGE_DAYS`(没有视界就算不出 cutoff)、
1972
- > 或 ②后端不是 SQL(`DB_BACKEND=mysql|pg`;local/文件后端诚实声明 `retention:"none"`,没有任何东西会删行)
1973
- > ⇒ 启动期点名拒。**多副本安全**:互斥靠库内单行 **sweep 租约**(fencing token 随每一条破坏性审计行走),
2011
+ > 🔴 **被要的车道必须齐**(不看有没有上锁):节律 `>0` 或 `RETENTION_MODE=enforce` 都是在要这条车道,要了就得有视界
2012
+ > `RETENTION_MAX_AGE_DAYS`(没有视界就算不出 cutoff)且节律 `>0`(车道真在跑)—— 缺一 ⇒ 起服拒(配置不变量,`loadConfig` 里判,不碰库),
2013
+ > 拒因句点名缺的键与出路(照做即起得来):enforce 补缺的键,或改回 `audit-only`(节律在而天数缺时连节律一起设 `0`);只给了节律则补天数或把节律设 `0`。车道开着而后端不是 SQL
2014
+ > (`DB_BACKEND=mysql|pg`;local/文件后端诚实声明 `retention:"none"`,没有任何东西会删行)⇒ 装配期点名拒。**多副本安全**:互斥靠库内单行 **sweep 租约**(fencing token 随每一条破坏性审计行走),
1974
2015
  > 与 `LEADER_ENABLED` **无关**——不必先开 leader 面。
1975
2016
  > - `RETENTION_MODE=audit-only|enforce`(默认 `audit-only`):灰度档。`audit-only` = lane 照跑照判、每个域记一条
1976
- > `audit_only` 审计行(三个候选计数),**一条破坏性方法都不调**;`enforce` = 真删。闭集,拼错的词拒启
1977
- > (静默落回默认档 = 运维以为在删而其实没删)。**回滚边界 = 翻回 `audit-only`**(已删不可逆),所以先在
2017
+ > `audit_only` 审计行(三个候选计数),**一条破坏性方法都不调**;`enforce` = 真删(要天数与 `>0` 的节律齐,缺一拒启 —— 见上一条)。闭集,拼错的词拒启
2018
+ > (静默落回默认档 = 运维以为在删而其实没删)。`audit-only` 下给了天数而节律缺 / 为 0(车道关着)照起,起服 warn `retention_policy_not_executed`
2019
+ > (字段 `maxAgeDays` / `mode` / `retentionSweepIntervalSec`)说出这一真因:策略只是声明,没有谁按它删。**回滚边界 = 翻回 `audit-only`**(已删不可逆),所以先在
1978
2020
  > `audit-only` 上读几天审计行确认命中集无误再翻。
1979
2021
  >
1980
2022
  > **配套的 operator 面**(全部 `operator-only`,见 `OPERATOR_PRINCIPALS`):
@@ -2166,10 +2208,13 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
2166
2208
  > (与命令同一条 shell、同一份 env;k8s 接在建工作区根的同一条命令后面,不多一次往返;adb 经设备 shell)填座,于是代码检索不再恒问人。实测缺席(没答上 / 答的不是绝对路径,含空串)⇒ 座缺席,
2167
2209
  > 引擎照旧失败关闭(上面那些递归读照旧问 / 拒),环境照开,并响一行 `execution_lane_home_undeclared`(`provider` + `note`,点名读到了什么与出路;同一部署同一句只响一次)。device 车道的设备协议
2168
2210
  > 没有家目录读数,座缺席,起服响一行同一只 warn。`~/` 形规则的下单门只认声明(提交那一刻执行环境还没开,读不到实测值):要写 `~/` 形规则就声明 `REMOTE_EXEC_HOME_DIR`。
2169
- > **声明会被对证**:k8s、local-docker 与 ssh 车道在每只执行环境开起来那一拍问一次 `$HOME`(与命令同一条 shell、同一份 env),与 `REMOTE_EXEC_HOME_DIR` 不一致(去掉尾斜杠后逐字比)
2170
- > 或问不出 ⇒ 那只执行环境不开,工具结果里的拒句点名两个值与改法(改声明,或让环境以声明的那个用户运行);环境答了的不一致记在那只环境上(此后每次回同一句,不再起容器 / pod / 不再问),
2171
- > 问不出的那一次不记、下一次重问。
2172
- > e2b / host / adb / device 车道今天不对证(声明照单全收)。
2211
+ > **声明与出厂答案都会被对证**(本版起六条车道同一只判官):k8s、local-docker、ssh、adb、e2b 在每只执行环境开起来那一拍问一次 `$HOME`(与命令同一条 shell、同一份 env;
2212
+ > adb 经设备 shell、e2b 是建 / 恢复沙箱时的一次 `commands.run`),host 读它自己组合给子进程的那份 env 里的 `HOME`(会话 `settings.env` 改了 `HOME` 也算);
2213
+ > 与主张(`REMOTE_EXEC_HOME_DIR`,或 host 的本进程家目录 / e2b 的 `/home/user`)不一致(去掉尾斜杠后逐字比)或问不出 ⇒ 那只执行环境不开,工具结果里的拒句点名两个值与改法
2214
+ > (把 `REMOTE_EXEC_HOME_DIR` 设成实测值、删掉错的声明,或让环境的 `HOME` 读主张那个值);环境答了的不一致记在那只环境上(此后每次回同一句,不再起容器 / pod / 沙箱、不再问),
2215
+ > 问不出的那一次不记、下一次重问。e2b 恢复出来的沙箱对不上时重新挂起(快照留着),不杀。
2216
+ > e2b 的出厂答案 `/home/user` 已于 2026-10-29 在真沙箱上实测一致(缺省模板:用户 `user`、`$HOME` = `/home/user`);自定义模板换了用户的,`$HOME` 若不是它,没声明 `REMOTE_EXEC_HOME_DIR` 的 e2b 部署的每只沙箱建起来就拒,把声明设成沙箱里 `echo $HOME` 的结果(声明同样被对证)。
2217
+ > device 车道读不了家目录:声明即座,起服响一行 `execution_lane_home_unmeasured`(点名声明值与「未实测」)。
2173
2218
  >
2174
2219
  > **同批顺带:场景旧名 `team` / 工具旧名 `run_team` 退役(BREAKING)。** 此前过渡窗的别名层
2175
2220
  > (`SCENARIO_ALIASES`)整体撤销:请求体里显式传 `scenario:"team"` 从「静默归一到 `discuss`」变成
@@ -2309,6 +2354,8 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
2309
2354
  | 流里一帧 `engine_notice` `code:"route.finish_reason_unknown"`,`detail:{ finishReason, sessionId, runId }`(7.126.0 起) | 提供方用一个引擎读不懂的结束原因结束了这一轮:回答按模型留下的样子保留,但可能被提供方截短了 | 告诉用户「这一段回答可能不完整」;同一个词同一 run 只说一次。运维按 `finishReason` 查是哪家网关 / 模型发的 |
2310
2355
  | 流里一帧 `engine_notice` `code:"route.stream_end_ignored"`,`detail:{ statement, word, frames, sessionId, runId }`(7.126.0 起) | 提供方已经结束了这一轮,它的流却又发来了东西(又一个结束 / 一个错误 / 内容);第一个结束算数,之后的没进这一轮 | 一般无需处置;频繁出现 ⇒ 运维查那条网关的流式实现(`statement` / `word` 说的是迟到的第一帧) |
2311
2356
 
2357
+ 会话流 `engine_notice` 帧的**全部**可投码 = 随包 `dist/trace/engine-notice-wire.d.ts` 里的 `ENGINE_NOTICE_WIRE_CODES`(码表本身,只列码名;每一帧自带 `message` 与 `detail`);逐版增删见 `wire-changes/server@<版本>.json` 的 `closedSetMembers` 里 `set` 为 `engineNotice` 的那一项(该版没有增删就没有这一项);本表只列运维最常问的几条。其中 `delegation.ask_unresolvable` = 一只调用被最终拒而**没有任何人裁决过它**(`detail.cause` / `parkLaneExisted` / `remedies` 分开读),委派子代上的这一条今天不上父会话流。
2358
+
2312
2359
  **机器码**:每个 4xx/5xx 响应体都带一个 `errorCode`(与人类文案 `error` 并列),这是**唯一**该拿来做
2313
2360
  程序分支的字段——**别锚 `error` 文案**。前缀族固定:`auth.` / `request.` / `not_found.` / `conflict.` /
2314
2361
  `limit.` / `usage.`(部署级治理窗)/ `capability.`(本部署没接这个面)/ `feature.`(开关没开)/
@@ -2616,7 +2663,7 @@ ssh / adb / local-docker 三条车道的流式调用同读这一条律:没有车
2616
2663
  **某一只删失败**(Role 缺 pods `delete` / API 抖)⇒ 不挡同轮其余:一行 error `sandbox_pod_delete_failed`(`pod` / `namespace` / `why` = `reaper_containers_ended` · `reaper_run_ended_past_hold` / `cause` = `http_<状态>` · `request_failed`,不带值、不带 API 文本)+ fail-open 计数 `server.sandbox-pod.delete-failed`(与连接清理删失败是同一个计数,按 `why` 分座),这一只下一轮再判再删 —— `why` 以 `reaper_` 起头的计数持续非零就核 Role 的 pods `delete`:兜底本身删不动,残留会随轮次累积。
2617
2664
  没有存储后端(查不了 run 行)的部署上本回收照跑:「容器已全部结束」那一条照删,带 run 标签的 pod 跳过(`run_store_absent`)。
2618
2665
  用的是 Role 里既有的 pods `list` / `delete`。**运维可见的差别**:已结束的沙箱 pod 不再留着供事后 `kubectl logs` / `kubectl describe` 看;run 自己的日志面(事件流、账本)不受影响。没有旋钮。
2619
- **两套部署共用一个沙箱命名空间**(缺省都是 `sandboxes`):回收器按身份标签 `app=sema-sandbox,managed-by=sema-server` 列 pod,标签里没有部署身份 ⇒ 一套部署会删另一套**容器已结束**的 pod
2666
+ **两套部署共用一个沙箱命名空间**(两套的 `K8S_NAMESPACE` 同值):回收器按身份标签 `app=sema-sandbox,managed-by=sema-server` 列 pod,标签里没有部署身份 ⇒ 一套部署会删另一套**容器已结束**的 pod
2620
2667
  (只有调用方把同一个 run id 交给两套部署时才会碰到对方的活 pod)。用 `K8S_NAMESPACE` 给每套部署各一个沙箱命名空间。
2621
2668
 
2622
2669
  ### 9.3h k8s 车道:成员 git 凭据代理(7.137.0 起落组件;本版平面恒关)
@@ -2896,7 +2943,7 @@ WebSearch 工具的 `tool_result` 错误文本,回到模型的对话里,**不是
2896
2943
  ### 9.5e 请求体 `mcpServers` 的列表与条目键:写坏,整份新提交 400(7.131.0 起;列表级与词表外的键 7.132.0 起;`startupTimeoutMs` 与 `toolFaces` 的引擎判官 7.134.0 起;`transport.url` 带凭据 7.135.0 起)
2897
2944
 
2898
2945
  条目的受理键 = `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 整条不进。
2899
- - **新提交**(`POST /v1/tasks` · `/v1/tasks/stream` · `/v1/runs`):键在场却判不出形 ⇒ `400 request.field_invalid`,拒句形如 `mcpServers[2].allowTools (server "crm") is not a usable declaration — must be an array of strings (omit the key to leave the server's tools unrestricted) — got a string of 4 characters. …`:点名 `mcpServers[i].<键路径>`、server 名与该键判官的原因句,**整份请求不收** —— 不挂一半、不丢声明、不默认。原因句不回显声明的值,有几处例外,回显只有一只口径(剥控制与双向字符、截到 64 字、超出加 `…`):server 名(含写坏的 `name` 值,另报送来的原长度)、`contentOrigin` 词表外的值;`toolAxes` / `toolFaces` 的原因句是引擎写的(7.134.0 起 `toolFaces` 的判官也是引擎包根导出的 `readToolFaces`):点名工具名(引擎截 80 字)与成员,`toolFaces` 还会点名闭词表拒的那个词,整句详情由引擎截到 240 字、本服务再剥控制与双向字符 —— 不回显显示文本、不回显合规的列表;`transport.url` 的值从不回显(带凭据的 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` = 没声明,照常受理。
2946
+ - **新提交**(`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` = 没声明,照常受理。
2900
2947
  - **`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。
2901
2948
  - **耐久 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`。
2902
2949
  - `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`。
@@ -2971,22 +3018,25 @@ WebSearch 工具的 `tool_result` 错误文本,回到模型的对话里,**不是
2971
3018
  **谁需要读这一节**:用 `.mcp.json` 带凭据头 / 子进程环境的单用户壳,带 `a2aPeers[].headers` 的调用方,名册里用 `envRefs` / `headerRefs` 的部署,自建 `createHttpServer` 的嵌入方(MIGRATION 7.138.0 节第 24 条)。
2972
3019
 
2973
3020
  - **件的形由引擎在作答处判,比此前严**。下列形读器拒 —— 那一台 `credential_unavailable`(提交照收、兄弟照挂、运行期才见):stdio 件名不是标识符形(含 `.` / `-`,如 `MY-VAR`)· 任一件值含 CR / LF / NUL(多行 PEM、Secret 文件的尾换行;http 头值首尾的换行此前被发送时的头规范化吞掉,现被拒)·
2974
- http / A2A 头名不是 HTTP token · 两个头名只差大小写 · MCP http 的件头名是传输自己写的头(`content-type` · `accept` · `mcp-session-id` · `mcp-protocol-version` · `last-event-id`;A2A 没有这张表)·
2975
- stdio 的秘密形件(变量名被引擎认作凭据名)的值恰等于一只每个子进程都会继承的变量(`PATH` / `HOME` 一类)的值。
3021
+ http / A2A 头名不是 HTTP token · 名册 `headerRefs` 里两个头名只差大小写、或 MCP http 的头名是传输自己写的头(`content-type` · `accept` · `mcp-session-id` · `mcp-protocol-version` · `last-event-id`;A2A 没有这张表;
3022
+ 请求体自带的这两形在受理口就拒,见下面「头属主」一条)· stdio 的秘密形件(变量名被引擎认作凭据名)的值恰等于一只每个子进程都会继承的变量(`PATH` / `HOME` 一类)的值。
2976
3023
  - **在哪看见**:MCP 的连接期 `wiring_manifest.mcp[]` 行(`status:"failed"`、`errorCode:"credential_unavailable"`、`delivered:"no"`)与调用期 `tool_end.errorCode`;`GET /v1/sessions/:id/mcp` 面板的那一台 `failed`。A2A 这一侧只有文本:会话 A2A 面板 `peers[].cardFetched:false` + `error`,run 内是工具结果的错误文本(没有机读码)。
2977
3024
  运维日志:本服务的座自己拒时(引用不在表里 / 绑定不符 / 引用种类座不答)一行 warn `peer_seat_refused { protocol, peer, reason }`(`reason` ∈ `handle_not_held` · `binding_mismatch` · `kind_env` · `kind_vault` · `kind_assertion` · `kind_unknown`),不带件值、不带引用 id;因件的形被引擎读器拒的那一台没有这一行,看上面的码 / 文本。
2978
- - **扣留次序**:引擎把座答里认作凭据的 http 头值与 stdio 秘密形件值记为自己持有的凭据值 —— 服务进程环境里同值的变量不再交给模型可驱动的 host shell(Bash)子进程(MCP stdio 子进程只继承 MCP SDK 的少数默认变量〔`HOME` · `LOGNAME` · `PATH` · `SHELL` · `TERM` · `USER`〕与座给的件,拿不到服务进程环境的其余变量)。座的答案先过读器、放行了才记:
2979
- 被读器拒的形(件值含 CR / LF / NUL、头名不是 HTTP token、两个头名只差大小写、变量名不是标识符形)的值**不进扣留表**,那一台失败而环境里同值的变量照旧交给 host shell 子进程(上一发布版先记后判);MCP 传输自己写的头名在读器之后的协议层才拒,值此时已在表里。名册 `headerRefs` 指向的变量值带尾换行时尤其要去掉那个换行。
2980
- - **`/metrics` 两只读数**:`peer_registry_entries`(登记表现有条目数)· `peer_registry_holders`(各条目的宿主租约数之和 —— 条目的持有者只有宿主租约;一只租约持有三个条目记三次)。没有过期、没有上限;在飞腿全落定后两只回到名册基线(名册里带件条目的条数)。基线之上持续不回落 = 有作用域没放。
2981
- - **`principalHeader` 的名**:MCP 条目 `transport.principalHeader` 取传输自己写的头名(上面五个,任何大小写)或引擎的断言头 `connector-assertion`,A2A 条目 `principalHeader` 取 `connector-assertion` ⇒ **新提交 `400 request.field_invalid`**
2982
- (点名 `mcpServers[i].transport.principalHeader` / `a2aPeers[i].principalHeader`,不回显写的串)。耐久 resume 重放存量体不 4xx:MCP 那一台由引擎在拨号时拒,A2A 那一条对端整条不进(warn `a2a_injection_dropped`)。
3025
+ - **扣留次序**:引擎把座答里认作凭据的 http 头值与 stdio 秘密形件值记为自己持有的凭据值 —— 服务进程环境里同值的变量不再交给模型可驱动的 host shell(Bash)子进程(MCP stdio 子进程只继承 MCP SDK 的少数默认变量〔`HOME` · `LOGNAME` · `PATH` · `SHELL` · `TERM` · `USER`〕与座给的件,拿不到服务进程环境的其余变量)。座的答案**先记后判**(引擎 7.60.0 起):
3026
+ 认作凭据的值在读器判形**之前**就记进扣留表 —— http 头按凭据词表选中的头,按所写的原值与传输会发出的值各读一份;stdio 件按 env 项的读法;空值不记;一个扣不住的值不挡别的值,整份答案随后被拒。所以读器随后拒的形(件值含 CR / LF / NUL、头名不是 HTTP token、两个头名只差大小写、变量名不是标识符形)只让那一台失败,其中认作凭据的值已在表里 —— 环境里同值的变量照样不交给 host shell 子进程;MCP 传输自己写的头名在读器之后的协议层才拒,值同样已在表里。名册 `headerRefs` 指向的变量值带尾换行时那一台仍失败,要去掉那个换行(去掉之前那只变量已被扣留)。
3027
+ - **`/metrics` 两只读数**:`peer_registry_entries`(登记表现有条目数)· `peer_registry_holders`(各条目的持有数之和 —— 持有者是宿主租约与引擎:引擎拿到一份带引用的声明时持有、不再能用它拨号时放,一条腿从调用到流落定、一只留存子代从登记到离账;一只租约持有三个条目记三次)。没有过期、没有上限;在飞腿与后台子代全落定后两只回到名册基线(名册里带件条目的条数)。基线之上持续不回落 = 有作用域或引擎持有没放。
3028
+ - **头属主(入口判)**:请求体自带的一条 MCP / A2A 声明,这次发送会带的头集合(件头 + 身份头名)受理时问引擎的属主判官一次 —— 件头名是 MCP 传输自己写的头(上面五个,任何大小写)· 同一头名两个拼写 · 件头名撞身份头名 · MCP 条目 `transport.principalHeader` 取传输自己写的头名或引擎的断言头 `connector-assertion` · A2A 条目 `principalHeader` 取 `connector-assertion` ⇒ **新提交 `400 request.field_invalid`**
3029
+ (点名 `mcpServers[i].transport.headers` / `mcpServers[i].transport.principalHeader` / `a2aPeers[i].headers` / `a2aPeers[i].principalHeader`;头名撞传输或引擎自有名时,拒句里的头名是引擎固定表里的规范化(小写)名,不回显调用方写的原串 / 大小写形(调用方写的恰是小写时,句里的名与所写相同);
3030
+ 同名两拼写与撞身份头名两形,句里是调用方所写名字的小写形(剥控制与双向字符);拒句从不带值;一份声明同时撞几处 ⇒ 点名判官报出的第一处,件头先于身份头名)。探测口同拒同句。件里名叫 `connector-assertion` 的头不撞(见下一条)。7.139.x 及以前件头的这三形新提交照收、那一台每次发送 `credential_unavailable`。
3031
+ 耐久 resume 重放存量体不 4xx:MCP 那一台由引擎在拨号 / 发送时拒,A2A 那一条对端整条不进(warn `a2a_injection_dropped`)。
2983
3032
  - **断言头不发送**:引擎的断言头是固定的名 `connector-assertion`、值由对端座作答;本部署没有断言来源,座对这一种恒拒(9.5d)。这说的是 `assertionHeader` 声明本身不产生断言头:调用方自己写在 `headers` 里、名字恰为 `connector-assertion` 的头仍按普通件处理(引擎只在它作 `principalHeader` 时保留这个名)。
2984
3033
  - **探测口与面板**:`POST /v1/capabilities/mcp/probe` 带 `headers` / `env` 的申报与任务提交走同一条路(件在这次走查的租约下登记,拨号落地即放;同样的件形判;缓存键含件);`GET /v1/capabilities/mcp` 与两张会话面板拨名册声明,递同一只座。
2985
3034
  - **补偿**:多行值改 base64 或改传文件路径,Secret 文件去掉尾换行;变量名改标识符形;件头名改非协议名;`principalHeader` 换成非协议、非引擎自有的名(例 `x-agent-principal`);秘密形变量别设成与 `PATH` / `HOME` 之类同值。
2986
3035
 
2987
- **已知缺口(本版披露不修):会话级子代在父腿作用域结束之后首拨 http / stdio、或再发 http 会被座拒。** 引擎持有引用的计数那一半本版不在,座只认宿主租约:一个会话级(后台)子代继承父的、请求体自带的 http / stdio MCP 声明,在父腿结束之后再问 ⇒ 该次发送 / 那一台的挂载 `credential_unavailable`
2988
- (响亮拒:一行不带值的 `peer_seat_refused`,`reason: handle_not_held`;http 在首次连接与每次发送时问座,stdio 在起子进程时问座 —— 父腿作用域结束之后才首拨的 stdio 子代同样被拒);父腿很快收尾时,子代连自己的挂载都会被座拒。同一腿内(含 cascade 各级、同步腿)、名册声明的服务器(名册件由进程寿命的名册租约持有)与父腿作用域之内已起的 stdio 子进程不受影响;A2A 没有这个形(委派子代不继承父的 A2A 对端)。
2989
- 补偿:请求体带件 http / stdio MCP 的任务别依赖「父腿先结束、子代后调用」,让父腿等子代收尾,或把这类服务器改成名册声明;缺口在引擎持有那一半到货后翻绿。
3036
+ **会话级子代活过父腿:引用由引擎持有。** 一个会话级(后台)子代继承父的、请求体自带的 http / stdio MCP 声明,父腿结束之后照常首拨 / 再发:引擎在拿到带引用的声明时告诉座「我持有它」、
3037
+ 不再能用它拨号时告诉座「放了」(一条腿从调用到流落定;一只留存子代从登记到离开账本),登记表的条目活到宿主租约与引擎都不再持有。
3038
+ 仍会被座拒(`credential_unavailable` + 一行 `peer_seat_refused`,`reason: handle_not_held`)的窄形:被取消的父腿里一次工具调用起的会话级子代,在那条腿已放之后才取得持有(取消的孤儿窗);
3039
+ 一条永不落定的准备腿永不放(条目随之常驻,`peer_registry_holders` 高于基线不回落即此形)。
2990
3040
 
2991
3041
  ### 9.6 trace 脱敏的射程:与出站请求体无关
2992
3042
 
@@ -3471,8 +3521,8 @@ UPDATE session_policy_rev_mark SET rev = 0, gen = gen + 1 WHERE policy_key = '<w
3471
3521
  下一次工具调用起生效;收不回 operator 写下的那一部分(两边都写过的条目也算):`403 loosen_forbidden`,句子 `a non-operator session-rule write may not loosen what an operator wrote; …`
3472
3522
  (7.134.x 及以前的句子是 `… may only tighten …`,按句子匹配的脚本改读码)。operator 写一次就把整行变成 operator 的。`operatorRules` 由引擎铸,客户端改规则时**不要**回传它。
3473
3523
 
3474
- **行自带文法戳。** 7.129.0 起每一行会话规则自带规则文法戳 `grammar`(写口判官盖引擎当前的文法:7.129.0–7.131.x 写下的行是 `1`,7.132.0–7.134.x 写下的行是 `2`,7.135.0–7.136.x 写下的行是 `4`,7.137.x 写下的行是 `5`,钉引擎 7.57.0 起写下的行是 `6`(裸工具名成为整工具规则);
3475
- 早先写下的行保留自己的戳、引擎按它的戳读;店整只存、读回原样交出;7.134.x 及更早的引擎读不了戳 3 / 4 的行、7.136.x 及更早的读不了戳 5 的行、7.137.x 及更早的读不了戳 6 的行 —— 整行拒、那条会话每一次调用都拒,升级序见 MIGRATION(先升读者、再升写者))—— `GET` / `PUT` 回包的 `rules` 与同步导出的
3524
+ **行自带文法戳。** 7.129.0 起每一行会话规则自带规则文法戳 `grammar`(写口判官盖引擎当前的文法:7.129.0–7.131.x 写下的行是 `1`,7.132.0–7.134.x 写下的行是 `2`,7.135.0–7.136.x 写下的行是 `4`,7.137.x 写下的行是 `5`,7.138.x 写下的行是 `6`(裸工具名成为整工具规则),钉引擎 7.60.0 起写下的行是 `7`(拒绝 / 询问桶里身上带 `__` 的 MCP 名按每一种读法读:`mcp__a__b` 同时拒 server `a` 的工具 `b` 与 server `a__b` 的每一只工具);
3525
+ 早先写下的行保留自己的戳、引擎按它的戳读(戳 6 及更早的 `toolDeny` 精确 MCP 名仍只拒那一只工具,每层通告一次 `config.session_rules_earlier_grammar`);店整只存、读回原样交出;7.134.x 及更早的引擎读不了戳 3 / 4 的行、7.136.x 及更早的读不了戳 5 的行、7.137.x 及更早的读不了戳 6 的行、7.138.x 及更早的读不了戳 7 的行 —— 整行拒、那条会话每一次调用都拒,升级序见 MIGRATION(先升读者、再升写者))—— `GET` / `PUT` 回包的 `rules` 与同步导出的
3476
3526
  记录里多这一个键。客户端改规则时只写三只桶,不要把读回的 `rev` / `gen` / `grammar` / `operatorRules` 一起回传:带着 `rev` 却没有 `grammar` 的值引擎按存量行的文法(0)读,括号形 allow 条目答
3477
3527
  `400 rules.inert_entry`(`reason: "earlier_grammar"`;出路在句子里);`grammar` 坏或比引擎新 ⇒ `400 rules.invalid_shape`(`field: "grammar"`)。
3478
3528
 
@@ -87,7 +87,7 @@ summary 会打印手动接法。
87
87
 
88
88
  - **`host`**(compose 路径) — 可选 `--users`:`single`;落盘 `REMOTE_EXEC=host`;运维自备的键:无;隔离:`unsupported`。server 容器本身就是沙箱:agent 的手落在这只容器里(worktree 隔离在、OS 级隔离不在)。只给单用户 —— 多租户下一个租户的输入会把手指向另一个租户的文件与进程。
89
89
  - **`e2b-cloud`**(compose 路径) — 可选 `--users`:`single` / `multi`;落盘 `REMOTE_EXEC=e2b`;运维自备的键:`E2B_API_KEY`;隔离:`supported`。E2B 云沙箱:每任务一只 microVM。E2B_API_KEY 由运维自备(sema-up 只登记键名,绝不写值)。
90
- - **kube 路径(不选词)** — 可选 `--users`:`single` / `multi`;落盘 `REMOTE_EXEC=k8s`;运维自备的键:`K8S_SANDBOX_IMAGE`;隔离:`supported`。kube 路径**不选词**:runtime 由 kube-up.sh 按 runtimeClass `kata-qemu` 在不在自动定(在 ⇒ kata VM,不在 ⇒ runc 容器),沙箱镜像由 chart/清单给。本行只为对表(能力表 / 必需键 / REQUIRE_EXECUTION_LANE 三面),不进 --sandbox 词表。
90
+ - **kube 路径(不选词)** — 可选 `--users`:`single` / `multi`;落盘 `REMOTE_EXEC=k8s`;运维自备的键:`K8S_SANDBOX_IMAGE`、`K8S_NAMESPACE`;隔离:`supported`。kube 路径**不选词**:runtime 由 kube-up.sh 按 runtimeClass `kata-qemu` 在不在自动定(在 ⇒ kata VM,不在 ⇒ runc 容器),沙箱镜像与沙箱命名空间由 chart/清单给。本行只为对表(能力表 / 必需键 / REQUIRE_EXECUTION_LANE 三面),不进 --sandbox 词表。
91
91
 
92
92
  已退役的词(收到即非零退出并指路):
93
93
 
@@ -30,10 +30,11 @@
30
30
  "env": {
31
31
  "REMOTE_EXEC": "k8s",
32
32
  "REQUIRE_EXECUTION_LANE": "true",
33
- "K8S_SANDBOX_IMAGE": null
33
+ "K8S_SANDBOX_IMAGE": null,
34
+ "K8S_NAMESPACE": null
34
35
  },
35
36
  "isolation": "supported",
36
- "note": "kube 路径**不选词**:runtime 由 kube-up.sh 按 runtimeClass `kata-qemu` 在不在自动定(在 ⇒ kata VM,不在 ⇒ runc 容器),沙箱镜像由 chart/清单给。本行只为对表(能力表 / 必需键 / REQUIRE_EXECUTION_LANE 三面),不进 --sandbox 词表。"
37
+ "note": "kube 路径**不选词**:runtime 由 kube-up.sh 按 runtimeClass `kata-qemu` 在不在自动定(在 ⇒ kata VM,不在 ⇒ runc 容器),沙箱镜像与沙箱命名空间由 chart/清单给。本行只为对表(能力表 / 必需键 / REQUIRE_EXECUTION_LANE 三面),不进 --sandbox 词表。"
37
38
  }
38
39
  ],
39
40
  "retired": [
@@ -1,6 +1,6 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { ADOPTION_PHASE, adoptionLockName, } from "../plugins/adoption-log-sql.js";
3
- import { policyKeyFor } from "../plugins/session-policy-store-sql.js";
3
+ import { policyKeyFor } from "../stores-sql/session-policy-store-sql.js";
4
4
  import { buildRuleOwnerKey } from "../plugins/permission-rule-store-sql.js";
5
5
  import { withAdoptionQuiesce } from "./quiesce.js";
6
6
  import { AFFECTED_CONFIG_TEMPLATES, MEMORY_PROJECT_SCOPE_PREFIXES, MEMORY_SCOPE_MAX_CHARS, NOT_MIGRATED_BY_DESIGN, REBIND_LEGS, buildVerbatimPair, rewriteMemoryScope, } from "./plan.js";
@@ -1,5 +1,5 @@
1
1
  export function ph(dialect, i) {
2
- return dialect === "tidb" ? "?" : `$${i}`;
2
+ return dialect === "mysql" ? "?" : `$${i}`;
3
3
  }
4
4
  export function buildConflictProbeSql(dialect, leg) {
5
5
  const on = (leg.residualKey ?? []).map((c) => ` AND a.${c} = b.${c}`).join("");
@@ -71,10 +71,10 @@ export declare const NOT_MIGRATED_FACES: readonly ["usage-window", "cost-quota",
71
71
  export type NotMigratedFace = (typeof NOT_MIGRATED_FACES)[number];
72
72
  export declare const AdoptionNotMigratedSchema: z.ZodObject<{
73
73
  face: z.ZodEnum<{
74
+ "org-delivery-quota": "org-delivery-quota";
74
75
  "usage-window": "usage-window";
75
76
  "cost-quota": "cost-quota";
76
77
  "rate-limit": "rate-limit";
77
- "org-delivery-quota": "org-delivery-quota";
78
78
  "approval-exemption-grantor": "approval-exemption-grantor";
79
79
  "approval-ask-decision-actor": "approval-ask-decision-actor";
80
80
  "image-bake-requested-by": "image-bake-requested-by";
@@ -120,10 +120,10 @@ export declare const AdoptionReportSchema: z.ZodObject<{
120
120
  }, z.core.$strict>>;
121
121
  notMigratedByDesign: z.ZodArray<z.ZodObject<{
122
122
  face: z.ZodEnum<{
123
+ "org-delivery-quota": "org-delivery-quota";
123
124
  "usage-window": "usage-window";
124
125
  "cost-quota": "cost-quota";
125
126
  "rate-limit": "rate-limit";
126
- "org-delivery-quota": "org-delivery-quota";
127
127
  "approval-exemption-grantor": "approval-exemption-grantor";
128
128
  "approval-ask-decision-actor": "approval-ask-decision-actor";
129
129
  "image-bake-requested-by": "image-bake-requested-by";
@@ -182,10 +182,10 @@ export declare const AdoptionReceiptSchema: z.ZodObject<{
182
182
  }, z.core.$strict>>;
183
183
  notMigratedByDesign: z.ZodArray<z.ZodObject<{
184
184
  face: z.ZodEnum<{
185
+ "org-delivery-quota": "org-delivery-quota";
185
186
  "usage-window": "usage-window";
186
187
  "cost-quota": "cost-quota";
187
188
  "rate-limit": "rate-limit";
188
- "org-delivery-quota": "org-delivery-quota";
189
189
  "approval-exemption-grantor": "approval-exemption-grantor";
190
190
  "approval-ask-decision-actor": "approval-ask-decision-actor";
191
191
  "image-bake-requested-by": "image-bake-requested-by";