@sema-agent/server 7.142.0 → 7.144.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 (301) hide show
  1. package/MIGRATION.md +608 -0
  2. package/USAGE.md +63 -36
  3. package/dist/adoption/plan.js +22 -0
  4. package/dist/approval-ask-audit-store.d.ts +7 -5
  5. package/dist/approval-ask-audit-store.js +12 -6
  6. package/dist/approval-ask-machine.d.ts +15 -7
  7. package/dist/approval-ask-machine.js +10 -4
  8. package/dist/approval-card.d.ts +155 -39
  9. package/dist/approval-card.js +111 -31
  10. package/dist/approval-queue-kind.d.ts +27 -0
  11. package/dist/approval-queue-kind.js +6 -0
  12. package/dist/approval-reconciler.d.ts +8 -8
  13. package/dist/approval-reconciler.js +3 -3
  14. package/dist/audit.js +4 -2
  15. package/dist/boot/budget-tracing.d.ts +1 -1
  16. package/dist/boot/execution-env.js +1 -1
  17. package/dist/boot/governance-seams.d.ts +26 -41
  18. package/dist/boot/governance-seams.js +61 -28
  19. package/dist/boot/late-binds.d.ts +7 -4
  20. package/dist/boot/late-binds.js +3 -1
  21. package/dist/boot/leader.js +2 -4
  22. package/dist/boot/memory-consolidation.d.ts +0 -8
  23. package/dist/boot/memory-consolidation.js +3 -3
  24. package/dist/boot/reapers.js +5 -4
  25. package/dist/boot/resolve-spec.d.ts +1 -1
  26. package/dist/boot/resolve-spec.js +8 -8
  27. package/dist/boot/runner-deps.d.ts +14 -6
  28. package/dist/boot/runner-deps.js +6 -17
  29. package/dist/boot/shutdown.d.ts +9 -0
  30. package/dist/boot/shutdown.js +18 -2
  31. package/dist/boot/stage-01-config.js +2 -0
  32. package/dist/boot/stage-02-web-admin-pull.js +3 -0
  33. package/dist/boot/stage-03-store-backend.d.ts +4 -1
  34. package/dist/boot/stage-03-store-backend.js +1 -2
  35. package/dist/boot/stage-04-stores.d.ts +5 -2
  36. package/dist/boot/stage-04-stores.js +3 -5
  37. package/dist/boot/stage-05-execution-env.d.ts +5 -2
  38. package/dist/boot/stage-06-runners.d.ts +5 -2
  39. package/dist/boot/stage-06-runners.js +1 -3
  40. package/dist/boot/stage-07-capability-layer.d.ts +5 -2
  41. package/dist/boot/stage-07-capability-layer.js +6 -2
  42. package/dist/boot/stage-08-reapers.d.ts +5 -2
  43. package/dist/boot/stage-09-leader.d.ts +5 -2
  44. package/dist/boot/stage-10-http-server.d.ts +5 -2
  45. package/dist/boot/stage-10-http-server.js +4 -1
  46. package/dist/boot/stores.d.ts +5 -2
  47. package/dist/boot/stores.js +7 -7
  48. package/dist/boot/web-admin.d.ts +5 -1
  49. package/dist/boot/web-admin.js +31 -19
  50. package/dist/capabilities/hands-lane.d.ts +9 -39
  51. package/dist/capabilities/hands-lane.js +8 -13
  52. package/dist/capabilities/mcp-probe.d.ts +3 -2
  53. package/dist/capabilities/scenarios.d.ts +23 -17
  54. package/dist/capabilities/scenarios.js +2 -2
  55. package/dist/capabilities/skills.d.ts +1 -17
  56. package/dist/capabilities/skills.js +0 -8
  57. package/dist/capability-facts.d.ts +8 -8
  58. package/dist/capability-facts.js +4 -4
  59. package/dist/card-void.d.ts +6 -9
  60. package/dist/config-catalog.js +1 -1
  61. package/dist/config-d-migration.d.ts +2 -0
  62. package/dist/config-dir-trust.js +58 -29
  63. package/dist/config-invariants.d.ts +7 -1
  64. package/dist/config-invariants.js +7 -0
  65. package/dist/config-provider.d.ts +10 -0
  66. package/dist/config-provider.js +15 -4
  67. package/dist/config-root-migration.d.ts +13 -0
  68. package/dist/config-root-migration.js +99 -0
  69. package/dist/config-types.d.ts +5 -5
  70. package/dist/config.d.ts +4 -3
  71. package/dist/config.js +1 -1
  72. package/dist/delegated-session-owner.d.ts +35 -151
  73. package/dist/delegated-session-owner.js +1 -83
  74. package/dist/device-ws-protocol.d.ts +1 -0
  75. package/dist/device-ws-protocol.js +2 -1
  76. package/dist/entry-mode-clamp-notice.d.ts +1 -1
  77. package/dist/env-model-table-gate.d.ts +53 -0
  78. package/dist/env-model-table-gate.js +53 -0
  79. package/dist/execution-placement.d.ts +7 -4
  80. package/dist/fleet/fleet-bus.d.ts +28 -52
  81. package/dist/fleet/fleet-bus.js +35 -100
  82. package/dist/fleet/fleet-reconciler.d.ts +4 -4
  83. package/dist/fleet/fleet-reconciler.js +1 -1
  84. package/dist/fleet/subagent-tail-bus.d.ts +14 -11
  85. package/dist/fleet/subagent-tail-bus.js +15 -28
  86. package/dist/hooks/classic-stations.d.ts +3 -1
  87. package/dist/hooks/classic-stations.js +2 -0
  88. package/dist/hooks/hook-runner.js +28 -7
  89. package/dist/host-decision.d.ts +54 -20
  90. package/dist/host-decision.js +42 -14
  91. package/dist/http/admission.d.ts +1 -1
  92. package/dist/http/admission.js +7 -3
  93. package/dist/http/approval-sweeps.js +11 -8
  94. package/dist/http/caller-trust.d.ts +1 -1
  95. package/dist/http/durable-run-leg.js +2 -2
  96. package/dist/http/engine-refusal.d.ts +16 -0
  97. package/dist/http/engine-refusal.js +17 -0
  98. package/dist/http/memory-compliance-failure-reply.d.ts +1 -3
  99. package/dist/http/memory-compliance-failure-reply.js +4 -6
  100. package/dist/http/owner-access.d.ts +11 -0
  101. package/dist/http/owner-access.js +13 -0
  102. package/dist/http/principal-gate.d.ts +1 -1
  103. package/dist/http/refusal-body.d.ts +7 -3
  104. package/dist/http/refusal-body.js +1 -0
  105. package/dist/http/resume-legs.d.ts +21 -7
  106. package/dist/http/resume-legs.js +82 -53
  107. package/dist/http/resume-restatement.d.ts +39 -0
  108. package/dist/http/resume-restatement.js +25 -0
  109. package/dist/http/route-ctx.d.ts +35 -10
  110. package/dist/http/route-ctx.js +1 -1
  111. package/dist/http/routes/approvals-assistant.d.ts +9 -1
  112. package/dist/http/routes/approvals-assistant.js +143 -31
  113. package/dist/http/routes/capabilities.js +11 -2
  114. package/dist/http/routes/memory-policy.d.ts +2 -2
  115. package/dist/http/routes/memory-policy.js +1 -1
  116. package/dist/http/routes/notify-wake.js +23 -25
  117. package/dist/http/routes/rules.js +48 -13
  118. package/dist/http/routes/runs.js +9 -8
  119. package/dist/http/routes/session-memory-sync.d.ts +34 -6
  120. package/dist/http/routes/session-memory-sync.js +27 -10
  121. package/dist/http/routes/session-sync.js +13 -20
  122. package/dist/http/routes/sessions.d.ts +2 -2
  123. package/dist/http/routes/sessions.js +22 -23
  124. package/dist/http/routes/tasks.js +27 -49
  125. package/dist/http/server.d.ts +13 -7
  126. package/dist/http/server.js +2 -2
  127. package/dist/http/stored-body-replay.js +15 -4
  128. package/dist/http/wire-types.d.ts +17 -7
  129. package/dist/index.d.ts +1 -0
  130. package/dist/index.js +1 -0
  131. package/dist/lane-home-probe.d.ts +15 -0
  132. package/dist/lane-home-probe.js +4 -0
  133. package/dist/leader/leader.js +5 -2
  134. package/dist/leader/wire.d.ts +7 -1
  135. package/dist/leader/wire.js +6 -5
  136. package/dist/leg-facts.d.ts +37 -0
  137. package/dist/leg-facts.js +15 -0
  138. package/dist/memory-bundle-engine.d.ts +1 -4
  139. package/dist/memory-bundle-engine.js +2 -2
  140. package/dist/memory-operator-faces.d.ts +30 -66
  141. package/dist/memory-operator-faces.js +17 -88
  142. package/dist/memory-scope-stores.d.ts +38 -40
  143. package/dist/memory-scope-stores.js +51 -36
  144. package/dist/memory-scope.d.ts +0 -9
  145. package/dist/memory-scope.js +0 -9
  146. package/dist/memory-sync-client.js +14 -3
  147. package/dist/memory-sync.d.ts +20 -4
  148. package/dist/memory-sync.js +23 -4
  149. package/dist/model-plane-face.d.ts +13 -7
  150. package/dist/model-plane-face.js +2 -1
  151. package/dist/observability/corrupt-read-seat.d.ts +5 -9
  152. package/dist/observability/corrupt-read-seat.js +1 -2
  153. package/dist/observability/fail-open.d.ts +30 -10
  154. package/dist/observability/fail-open.js +27 -10
  155. package/dist/observability/run-terminal.d.ts +1 -1
  156. package/dist/observability/run-terminal.js +6 -2
  157. package/dist/orchestration/workflow-completion-inbox.d.ts +5 -19
  158. package/dist/orchestration/workflow-completion-inbox.js +0 -12
  159. package/dist/orchestration/workflow-notify-journal.d.ts +3 -2
  160. package/dist/orchestration/workflow-park-decision.d.ts +8 -3
  161. package/dist/orchestration/workflow-park-decision.js +5 -3
  162. package/dist/org-delivery-gate.d.ts +11 -0
  163. package/dist/org-delivery-gate.js +19 -1
  164. package/dist/parked-decide.d.ts +13 -9
  165. package/dist/parked-decide.js +7 -7
  166. package/dist/parked-revive-surface.d.ts +6 -7
  167. package/dist/parked-revive-surface.js +1 -1
  168. package/dist/peer-inbound-face.d.ts +122 -0
  169. package/dist/peer-inbound-face.js +123 -0
  170. package/dist/permission-rule-list.d.ts +13 -9
  171. package/dist/permission-rule-list.js +7 -3
  172. package/dist/plugins/approval-ask-store-file.js +18 -6
  173. package/dist/plugins/approval-ask-store-memory.js +8 -6
  174. package/dist/plugins/approval-ask-store-sql.d.ts +28 -13
  175. package/dist/plugins/approval-ask-store-sql.js +30 -11
  176. package/dist/plugins/caching-session-store.d.ts +0 -3
  177. package/dist/plugins/caching-session-store.js +0 -3
  178. package/dist/plugins/checkpoint-store-sql.d.ts +45 -38
  179. package/dist/plugins/checkpoint-store-sql.js +108 -45
  180. package/dist/plugins/file-history-store-sql.d.ts +24 -18
  181. package/dist/plugins/file-history-store-sql.js +24 -19
  182. package/dist/plugins/file-run-store.d.ts +29 -0
  183. package/dist/plugins/file-run-store.js +97 -8
  184. package/dist/plugins/fork-routing-session-store.d.ts +8 -1
  185. package/dist/plugins/fork-routing-session-store.js +6 -0
  186. package/dist/plugins/imported-layer-root-retirement.d.ts +82 -0
  187. package/dist/plugins/imported-layer-root-retirement.js +96 -0
  188. package/dist/plugins/local-checkpoint-store.d.ts +6 -8
  189. package/dist/plugins/local-checkpoint-store.js +5 -4
  190. package/dist/plugins/local-session-store.d.ts +69 -30
  191. package/dist/plugins/local-session-store.js +133 -57
  192. package/dist/plugins/local-session-window.d.ts +4 -2
  193. package/dist/plugins/mailbox-recipient-lifecycle.d.ts +9 -31
  194. package/dist/plugins/mailbox-recipient-lifecycle.js +4 -0
  195. package/dist/plugins/mailbox-store-sql.d.ts +12 -8
  196. package/dist/plugins/mailbox-store-sql.js +76 -7
  197. package/dist/plugins/memory-sync-store-pg.d.ts +15 -0
  198. package/dist/plugins/memory-sync-store-pg.js +21 -0
  199. package/dist/plugins/memory-sync-store-tidb.d.ts +2 -0
  200. package/dist/plugins/memory-sync-store-tidb.js +13 -1
  201. package/dist/plugins/one-time-migrations.d.ts +44 -14
  202. package/dist/plugins/one-time-migrations.js +118 -15
  203. package/dist/plugins/org-delivery-quota-store.d.ts +1 -1
  204. package/dist/plugins/org-delivery-quota-store.js +2 -12
  205. package/dist/plugins/park-commit-watch.d.ts +1 -1
  206. package/dist/plugins/permission-rule-store-file.d.ts +10 -1
  207. package/dist/plugins/permission-rule-store-file.js +15 -2
  208. package/dist/plugins/permission-rule-store-sql.d.ts +31 -1
  209. package/dist/plugins/permission-rule-store-sql.js +35 -2
  210. package/dist/plugins/pg-session-storage.d.ts +1 -9
  211. package/dist/plugins/pg-session-storage.js +2 -19
  212. package/dist/plugins/remote-env-device.d.ts +8 -11
  213. package/dist/plugins/remote-env-device.js +6 -3
  214. package/dist/plugins/remote-env-host.d.ts +8 -4
  215. package/dist/plugins/remote-env-host.js +7 -9
  216. package/dist/plugins/remote-env-k8s.d.ts +37 -24
  217. package/dist/plugins/remote-env-k8s.js +50 -17
  218. package/dist/plugins/remote-shell.js +12 -2
  219. package/dist/plugins/retention-store-sql.d.ts +2 -2
  220. package/dist/plugins/sandbox-read-floor.d.ts +5 -7
  221. package/dist/plugins/sandbox-read-floor.js +2 -7
  222. package/dist/plugins/schema-consistency.d.ts +5 -1
  223. package/dist/plugins/session-fork.d.ts +1 -1
  224. package/dist/plugins/session-ownership.d.ts +9 -0
  225. package/dist/plugins/session-ownership.js +6 -0
  226. package/dist/plugins/session-store.d.ts +13 -16
  227. package/dist/plugins/session-store.js +9 -10
  228. package/dist/plugins/sql-errors.d.ts +10 -1
  229. package/dist/plugins/sql-errors.js +13 -0
  230. package/dist/plugins/sql-json-column.d.ts +24 -2
  231. package/dist/plugins/sql-json-column.js +38 -3
  232. package/dist/plugins/store-backend.d.ts +23 -28
  233. package/dist/plugins/store-backend.js +12 -3
  234. package/dist/plugins/store-contracts.d.ts +29 -5
  235. package/dist/plugins/store-contracts.js +4 -1
  236. package/dist/plugins/task-attachment-store.d.ts +2 -2
  237. package/dist/plugins/task-attachment-store.js +37 -52
  238. package/dist/plugins/tidb-session-store.d.ts +2 -14
  239. package/dist/plugins/tidb-session-store.js +2 -20
  240. package/dist/plugins/tool-result-store-sql.d.ts +9 -8
  241. package/dist/plugins/tool-result-store-sql.js +23 -2
  242. package/dist/plugins/wiring-section-codec.d.ts +32 -0
  243. package/dist/plugins/wiring-section-codec.js +55 -0
  244. package/dist/plugins/workspace-fill.d.ts +1 -1
  245. package/dist/rule-entry-list.d.ts +3 -2
  246. package/dist/rule-entry-list.js +5 -1
  247. package/dist/rules-consent.d.ts +28 -5
  248. package/dist/rules-consent.js +40 -28
  249. package/dist/run-local.js +11 -6
  250. package/dist/runs.d.ts +12 -8
  251. package/dist/runs.js +10 -7
  252. package/dist/runtime-governance.d.ts +1 -1
  253. package/dist/security.d.ts +0 -4
  254. package/dist/server-secret-env.d.ts +4 -0
  255. package/dist/session-error-wire.d.ts +34 -23
  256. package/dist/session-error-wire.js +8 -3
  257. package/dist/session-incarnation.d.ts +3 -3
  258. package/dist/session-notify-door.d.ts +106 -0
  259. package/dist/session-notify-door.js +97 -0
  260. package/dist/session-sync.d.ts +9 -14
  261. package/dist/session-sync.js +5 -5
  262. package/dist/session-workspace.d.ts +3 -13
  263. package/dist/session-workspace.js +0 -9
  264. package/dist/standing-answer-grant.d.ts +5 -0
  265. package/dist/standing-answer-grant.js +10 -0
  266. package/dist/stores-sql/session-policy-store-sql.d.ts +4 -2
  267. package/dist/stores-sql/session-policy-store-sql.js +3 -13
  268. package/dist/task-mcp.d.ts +1 -1
  269. package/dist/task-settings.d.ts +11 -8
  270. package/dist/task-settings.js +31 -11
  271. package/dist/tool-approval.d.ts +55 -35
  272. package/dist/tool-approval.js +112 -74
  273. package/dist/trace/core-keyset-guard.d.ts +12 -9
  274. package/dist/trace/engine-notice-wire.d.ts +1 -1
  275. package/dist/trace/engine-notice-wire.js +4 -2
  276. package/dist/trace/exit-seat.d.ts +7 -1
  277. package/dist/trace/exit-seat.js +1 -0
  278. package/dist/trace/ledger-sink.d.ts +2 -2
  279. package/dist/trace/ledger-sink.js +12 -4
  280. package/dist/trace/model-segment.d.ts +74 -25
  281. package/dist/trace/model-segment.js +141 -32
  282. package/dist/trace/project.d.ts +15 -6
  283. package/dist/trace/project.js +12 -4
  284. package/dist/trace/redact.d.ts +72 -9
  285. package/dist/trace/redact.js +1509 -471
  286. package/dist/trace/self-minted-wire.d.ts +598 -47
  287. package/dist/trace/self-minted-wire.js +190 -20
  288. package/dist/trace/wire-projection-faces.d.ts +1 -1
  289. package/dist/trace/wire-projection-faces.js +1 -1
  290. package/dist/verifier-pause-card.d.ts +33 -5
  291. package/dist/verifier-pause-card.js +15 -10
  292. package/dist/web-admin-client/apply-effective.d.ts +12 -16
  293. package/dist/web-admin-client/apply-effective.js +90 -10
  294. package/dist/workspace-space.d.ts +19 -3
  295. package/dist/workspace-space.js +8 -4
  296. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -1
  297. package/package.json +3 -2
  298. package/wire-changes/server@7.143.0.json +1167 -0
  299. package/wire-changes/server@7.144.0.json +291 -0
  300. package/dist/session-records-skipped-notice.d.ts +0 -21
  301. package/dist/session-records-skipped-notice.js +0 -27
package/USAGE.md CHANGED
@@ -90,6 +90,12 @@ docker compose up --build --scale service=4 # 4 个无状态 worker,nginx
90
90
  > 起服不过 ⇒ 拒启(fatal 标题 `[config.dir_untrusted]`;`run-local` 退 2);起服之后才变坏 ⇒ 那一拍 `web_admin_refresh_failed` 带同一句、新字节不读、已应用的配置不动。拒句点名那一个路径、实测值与改法
91
91
  > (`chmod go-w` / `chown` / `CONFIG_LOCAL_OWNER_UID=<uid>`)。Windows 上属主与模式位不可判:只判结构,另记一次 fail-open 告警。容器形(rootless / userns-remap / ConfigMap / fsGroup)见源码仓文档 DEPLOY-PREREQS 的 7.133.0 节(不随包)。
92
92
 
93
+ > 🔴 **本地目录顶替 env 模型表要配置根上的一份确认(7.143.0 起)**:`config.d/models.json` 只要有启用条目就管模型面 —— 采纳之后服务表整张换成目录表。设了 `MODEL_ID`(env 模型表已声明)时,这一换只在配置根(放 `config.d/` 的那个目录)上的
94
+ > `config.env-models.json` 读作 `{"envModels":"replaced"}` 时发生;缺席 / `{"envModels":"kept"}` / 读不懂 ⇒ 这份目录的模型面被拒(码 `config.catalog_replaces_env_models`,`GET /v1/models` 的 `modelPlane.planeRefusalCode` 与 `/health.modelRefusals.plane` 报它),
95
+ > env 模型继续服务,同一份目录的其余配置照落;warn `web_admin_model_plane_refused` 点名被顶替的 env 模型、决定文件的路径与两条出路(写 `replaced`,或撤掉 `MODEL_ID` / `MODEL_CHEAP_ID`)。写好决定,下一个刷新拍生效,不用重启。
96
+ > 升级的本地根不用动手:起服时的一次性迁移在目录已经管模型面的根上代写 `replaced`(warn `config_env_models_replaced_by_migration` 点名从此被顶替的 env 模型),并在根上留一份运行记录 `config.migration.SS-M11.json`;那以后决定缺席就是 `kept`。
97
+ > 撤销 = 删掉决定文件或写 `kept` 再重启(运行中的进程只对目录判组拒、留住在服的那一代,env 模型在下一次起服回来)。这两份根级文档归上一条的目录信任判官管(判据同 `config.d` 下的文档)。中心配置源不守卫(中心发布的目录照旧整表顶替 env 表)。严格名册布防(`CONFIG_REQUIRE_ROSTER=true`)时 env 表是占位,不算已声明。
98
+
93
99
  > **会话 scratchpad 与 `SANDBOX_ENV_FACTS`(7.104.0 起)**:`SANDBOX_ENV_FACTS=false` 只关沙箱事实(profile / capabilities / pkgSource / egress / resume);会话 scratchpad 的宣告不归它管 —— 宣告是 Write / Edit 能写进 scratchpad 的唯一来源(引擎据它开根围栏的洞,并用同一次裁决在提示词里写出 `# Scratchpad Directory` 一节)。缺省部署此前每轮一句 `scratchpad advertised writable directory skipped` 的噪音随之消失。
94
100
 
95
101
  > 拓扑:LB(入口)→ N 个一样的 worker → 共享 SQL 库(数据中心;MySQL 协议或 PG)+ 模型网关。无中央调度器,跨 worker 靠 DB 协调。
@@ -436,7 +442,7 @@ MODEL_ID=<your-model-name> MODEL_CHEAP_ID=<your-cheap-model-name>
436
442
  存量中心配置里的 `roles.team` / `roles.synthesize` 由 settings-schema 的一次性迁移搬走,side query 带 `modelRole` 表外词(含 `advisor`)⇒ **400**
437
443
  `request.field_invalid`(不再回落主模型;带了 `model` 也一样拒)。要给委派子代配模型就绑 `subagent`,主任务绑 `default`。
438
444
  - 调用方可在 `objective` 里 `@<模型名>` 选模型,**只认已配置的名单**(`MODEL_ID` / `MODEL_CHEAP_ID`,或目录条目)——注入不了 `baseUrl`/`apiKey`;无 `@` → 默认模型;`@` 点中一条已发布但本部署不可用的目录条目 ⇒ 400 `request.model_unavailable`。
439
- - **`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。
445
+ - **`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.143.0 起 `modelPlane` 段末多一个条件键 `planeRefusalCode` —— 最新到达的那份目录候选被模型面组拒时在场、值 = 那一拒的码(`catalog:"refused"` 而它缺席 ⇔ 最新到达的候选被候选判官整拒,看 `candidateRejected`);`catalog` 的 `refused` 读同一份事实:更新的候选到了、没走到模型面那一判(被整拒,或过了判官却在换装时被拒)时不再挂着旧候选的拒。契约 §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。
440
446
  - 不设 `MODEL_CHEAP_ID` → 所有角色 = 主模型(行为不变)。
441
447
  - `MODEL_CHEAP_ID` 写成与 `MODEL_ID` **同一个 id**(7.104.0 起)→ 那就是主模型本身:不铸 cheap 槽,所有角色 = 主模型,起服一行
442
448
  `config_env_entry_cheap_same_as_main`(同时设的其余 `MODEL_CHEAP_*` 键一并不生效)。要给同一只上游 id 第二种形(另一个价 / 思考 /
@@ -450,7 +456,7 @@ MODEL_CODE_ROLES=default,subagent # 不设=全中立;仅这些角色在「
450
456
  ```
451
457
  - 经 `RoleSpec.promptRole: "code"` 挂在**角色**上(7.137.0 起 / 引擎 7.56.0;此前是把同一段目录文本粘进 `RoleSpec.systemPrompt`),非开发角色保持中立。角色基底只有一条来源链:任务 → 请求的模型角色的预设 → 引擎委派子代的人设,**第一个说出基底的来源整个说了算** —— 任务自带 `systemPrompt`(或客户端注入)、或场景自己写了角色词(cowork 场景写 `cowork`)时任务优先;这些 run 的宿主角色是 `code`(按 `when.host.role` 条件的 overlay 自此对它们生效),基底字节与此前相同。
452
458
  - **验证门**(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` 仍是实现的)。`findings` / `evidence` 是验证器模型写的自由文本(常引命令与输出),7.142.0 起与 `result` 同一只脱敏器脱:认出的凭据片段换成 `«redacted…»` 记号,条数与顺序不变,判词 / 轮数逐字(同步体、`GET /v1/runs/:id`、`/events` 回放与落库同读)。**仅 `/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`(要过程可见性用异步形)。
453
- - **实现腿撞资源限(`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 天)。
459
+ - **实现腿撞资源限(`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_TOKENS` / `_TOTAL_BUDGET_USD`)封住**整个任务**的实现侧:每一轮修复拿的是总额减去前面各腿自报的花费(不开 `RESOURCE_SUSPEND` 时同一条规则作用在任务的 `limits` 上);某条轴花光 ⇒ 不再修复,判词停在那次 FAIL,结果带 `verification.allowanceExhausted`;前面有一腿没报价而配了美元总额 ⇒ 读作没有余额,同样不修复。`_MAX_SLICES` 仍按一条腿的续跑链数。没开 `RESOURCE_SUSPEND` 的部署行为不变(撞限 `failed` + `limits.*` 码)。停驻期间同会话再提交 / 删会话 / 换绑设备 / 会话同步都 409,先续跑或取消;被弃置的停驻由 `RESOURCE_SUSPEND_TTL_SEC` 到期收回(缺省 30 天)。
454
460
  - **记忆(文件记忆引擎,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 不提交)。
455
461
  **`MEMORY_PERSISTENCE_CAPABLE`(三态,7.14.0 起)**=**部署自述**「本机能不能把用户的『记住 X』落到持久处」,与请求键 `memoryWrite` 两轴正交(后者收紧本次运行,前者是 operator 声明;任一取否即只读方向)。缺省不设=引擎按 roster 自行推断;`true`=本部署有推断看不见的持久通道(自定义 writer / 记忆 MCP / 远程执行车道与记忆根共享挂载);`false`=强制只读披露并关掉文件工具往记忆根的写通道。不认得的词拒启。
456
462
  🔴 **`false` 自 core 5.27.0(7.14.0 提货)起还多一层——但只在 file 记忆引擎上**(`MEMORY_ENGINE_BACKEND` 未设 = 默认单机形):该会话是**受限会话**——materialize/搜索/harvest 一律按**已提交账**供给,记忆根下无事务背书的磁盘分歧既不收编也不供给,而是留盘 + 响亮点名(`restricted_divergence`,本服务把它计进 `memory_harvest_rejections_total{code}` 并 warn 一条),等下一次**非受限**会话走正常门收编。合法流程不受影响:用户手改、`git pull` 落下的删除照旧(延后,不销毁),并发的可写会话提交的变更算有事务背书。
@@ -500,6 +506,8 @@ MODEL_CODE_ROLES=default,subagent # 不设=全中立;仅这些角色在「
500
506
  - **记忆同步(本机 file 引擎腿 ↔ DB 中心)**:本机腿由 `MEMORY_SYNC_URL`(中心基址;令牌只放 `MEMORY_SYNC_TOKEN`)开起,在起服时与每次任务写进记忆之后后台同步;
501
507
  它的同步身份 = `MEMORY_SYNC_URL` 的规范形(小写协议与主机、无缺省端口、路径去尾斜杠)。中心用 `MEMORY_SYNC_SERVE_IDENTITY` 声明同一个值(能力位 `memorySyncServe.identity` 逐字是它),
502
508
  只服务点名这一身份的轮(不是规范形 ⇒ 400 `request.field_invalid`;别的身份 ⇒ 409 `config.memory_sync_target_mismatch`,零读零写;没声明 ⇒ 501 `feature.memory_sync_serve_disabled`)。
509
+ 中心侧同步口是 `POST /v2/memory/sync/:scope`;游标绑中心的**化身**(每个层分区一只,分区第一次被同步读到时出生、存在中心库):中心被清空重建、或游标是升级前写下的 ⇒
510
+ 中心在写之前拒这一轮,本机这一层停驻(同其余停驻,出路 = 删这一层的游标文件)。中心从备份恢复之后,删掉 `agent_memory_engine_sync_partition` 的行再起服(轮换化身,见 DEPLOY-PREREQS)。
503
511
  壳在把会话推上云之前调 `POST /v1/sessions/:id/memory/sync`(体 `{ target }` = 目标引擎的 `memorySyncServe.identity`;能力位 `sessionMemorySync`),拿到引擎的同步报告:
504
512
  这个会话读的每一层一个结局(`synced` / `incomplete` / `refused` / `failed`),或整轮 `refused`(目标不是本机腿到达的那台 / 会话有在飞 run,或读完它的记忆观测之后又受理了 run / 会话记录前进了)。
505
513
 
@@ -513,18 +521,18 @@ MODEL_CODE_ROLES=default,subagent # 不设=全中立;仅这些角色在「
513
521
  授予才准入,拿不到一律 fail-closed 拒(纯读外泄面:projectId 只过形状门不过授权)。目录源**三态单选,
514
522
  授权面不双源合并**:web-admin 远程腿(per-principal `orgMemory` 段)> `MEMORY_ORG_DIRECTORY_JSON`
515
523
  静态表 > 缺席(request 来源的 org scope 恒拒)。同一个目录也是 `GET /v1/memory/export` /
516
- `POST /v1/memory/sync/:scope` 的 `org:` 属主门真源(写面另需条目 `write:true`);拒绝形 = 终局
524
+ `POST /v2/memory/sync/:scope` 的 `org:` 属主门真源(写面另需条目 `write:true`);拒绝形 = 终局
517
525
  `memory.admission_denied`(403)/ 瞬时 `memory.admission_required`(503,带 `retryAfterSec`)。
518
526
 
519
527
  | env | 缺省 | 说明 |
520
528
  |---|---|---|
521
- | `MEMORY_ORG_ADMISSION_MODE` | `enforce` | `enforce`=判决即结果;`audit`=**运维诊断位**——准入面判决照算、拒绝降为日志+metric,零行为变化(不整拒、不窄化写面);`/v1/memory/export`、`/v1/memory/sync/:scope` 的 `org:` 属主门在 audit 下**逐字保持收编前的 operator-only**(未验证的目录不得开数据面)。两模式都要求目录 client 在场 |
529
+ | `MEMORY_ORG_ADMISSION_MODE` | `enforce` | `enforce`=判决即结果;`audit`=**运维诊断位**——准入面判决照算、拒绝降为日志+metric,零行为变化(不整拒、不窄化写面);`/v1/memory/export`、`/v2/memory/sync/:scope` 的 `org:` 属主门在 audit 下**逐字保持收编前的 operator-only**(未验证的目录不得开数据面)。两模式都要求目录 client 在场 |
522
530
  | `MEMORY_ORG_DIRECTORY_JSON` | 缺省 | 单机形静态授权表 `{"<principal>":{"org:acme":{"write":true}}}`。**启动期整表校验,坏表拒启动**。有 web-admin 时被忽略(center 胜 + warn) |
523
531
  | `MEMORY_ORG_GRANT_TTL_MS` | `60000`(`[1000, 3600000]`) | 授予/负结果的缓存 TTL = 「有界 LKG」:**新任务/新 resume 腿**的准入判决滞后 ≤ 此值;**在跑任务不受吊销影响**(判决点在 prepare 期) |
524
532
  | `MEMORY_ORG_UNAVAILABLE_BACKOFF_MS` | `10000`(`[500, 600000]`) | 目录取不到之后的退避窗(只用于取数失败臂,不用于负结果) |
525
533
  | `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`=本部署未设,引擎自缺省) |
526
534
  | `MEMORY_PROVENANCE` | 缺省(=引擎自缺省 `carry`) | **记忆 provenance 总开关**(core 5.46.0 起)。一个会话被判为**已暴露**之后,它的记忆写怎么处置。`carry`=引擎缺省:**普通**记忆写照常提交,但带上引擎铸的 `origin` 标记(随条目走后端/同步/导出包);指令形文件(feedback / pinned / triggers / applies-when)仍被扣下隔离;派生索引的会话散文照回滚;内容扫描门原样跑(**标记不是豁免**)。`off`=此前的行为:不铸 origin 标记,已暴露会话的 harvest **一条都不收**(整体隔离候人审)。⚠️ 已提交的标记在编辑时照样带下去 —— `off` 停的是**铸**,从不抹掉已记的事实。与 `MEMORY_DELEGATION_EVIDENCE` **正交**(四种组合全合法):那一键决定**什么时候**判会话为已暴露,本键决定判了之后**对写做什么**。坏值**启动期拒**(`OFF`/`false`/`none` 这类「看起来像关掉」的写法一律不折成缺省 —— 静默回默认 = 一台自认为「暴露会话零收录」的部署其实在照常收录)。**部署席 only**(TaskSpec 无同名键,任务/受治脚本无法据此改姿态);**不冻进 checkpoint**(resume 腿跟当前部署配置走)。运维读面 = `GET /v1/diagnostics/wiring` 的 `memoryPosture.provenance`(`null`=本部署未设,引擎自缺省) |
527
- | `MEMORY_CAPTURE_POLICY` | 缺省(=引擎自缺省 `open`) | **记忆采集 opt-out 的部署姿态**(core 7.0.0 起)。任务可带 `memory.capture:"off"` 声明「本会话不进长期记忆」(一次性,resume 恒不采集,无反向拼写),本键决定怎么裁那条声明:`open`=按面值生效,**只有**该 principal 的显式 `false` verdict 拒(403 `memory.capture_optout_denied`),resolver 故障**放行**+披露(隐私轴 fail-safe:错拒=记了用户明说不要记的会话,不可逆);`governed`=verdict **必答**(混合车队:部分用户强制留存),故障/缺席**拒跑**(fail-closed);`capture-required`=全体声明拒(一行配置)。verdict 源**两条**(析取):①SQL 店后端上的授权表 `memory_optout_grant`(三层折叠:per-principal 行 → 部署缺省行 → 代码缺省 allow;管理面 `GET|PUT /v1/admin/memory-optout`(PUT=部署缺省行)· `PUT|DELETE …/:principal`,operator-only,能力位 `memoryOptOutGrant`,零缓存写后即生效);②非 dry-run 的 web-admin 在该 principal 的 entitlement caps 里发 `allowMemoryOptOut`。🔴 **`governed` 而两源皆无 ⇒ 启动期拒启**(无源 = 每个声明都被拒且没人知道为什么;接 `DB_BACKEND=mysql|pg`、接非 dry-run center,或改姿态)。⚠️ **center-only 源的语义**(只配了 center、没有 SQL 授权表):`allowMemoryOptOut` 在 entitlement schema 里是**可选键** —— center 不发它 = 该 principal 的 verdict **缺席** = `governed` 下每次 opt-out 恒拒(403 `memory.capture_optout_denied`),而 worker **照常起动**;这一形部署级证不死(per-principal 授权只有运行期才知道),所以 server 两处响亮:启动期一行 `memory_capture_governed_center_only_source`、运行期首次真缺席一行 `memory_capture_governed_verdict_absent`(每进程一次,纯披露不改判)。要确定性 verdict 就接 SQL 授权表(它三层折叠后恒答显式 boolean)。坏值**启动期拒**(`Governed`/`required`/`off` 一律不折成缺省:两种姿态的故障极性**相反**)。**部署席 only**;**不冻进 checkpoint**。运维读面 = `GET /v1/diagnostics/wiring` 的 `memoryPosture.capturePolicy`(`null`=本部署未设)。记录载体(core 7.0.2 起):SQL 后端(`DB_BACKEND=mysql\|pg`)上记录落 `session_capture_optout` 表(跨副本存活,随中央 schema 自建零旋钮)——**远端车道(e2b/k8s)的 `capture:"off"` 声明自此真持久**;`DB_BACKEND=local` 自 7.71.0 起在记忆引擎接线时供 core 的控制面文件三腿(钉在本部署记忆引擎那只控制面目录上,按记忆平面取用)⇒ 单机默认部署(`CONFIG_PROVIDER=local`、`REMOTE_EXEC` 未设)上声明与中途翻转 verb 从 409 转为真受理(此前该 409 在这一形上是结构性的:core 判「远端」看的是执行环境的接口形,host 车道同被判 remote);记忆面暗的部署本席位缺席而 core 也不走采集腿。今天仍答 409 `config.memory_capture_unsupported` 的只剩把记忆引擎接了却没给任何店的自建装配形。用户面四口:提交键 `memoryCapture:"off"`(声明形)· `POST /v1/runs/:id/memory/capture-optout`(live 中途翻转,owner 门)· `POST /v1/sessions/:id/memory/erase`(所有者自助抹除,select 硬限本会话)· `GET /v1/sessions/:id/memory-status`(状态读面,owner 门,五可选键诚实缺席)。契约全文 `docs/ASSISTANT-WIRE-CONTRACT.md` §12 |
535
+ | `MEMORY_CAPTURE_POLICY` | 缺省(=引擎自缺省 `open`) | **记忆采集 opt-out 的部署姿态**(core 7.0.0 起)。任务可带 `memory.capture:"off"` 声明「本会话不进长期记忆」(一次性,resume 恒不采集,无反向拼写),本键决定怎么裁那条声明:`open`=按面值生效,**只有**该 principal 的显式 `false` verdict 拒(403 `memory.capture_optout_denied`),resolver 故障**放行**+披露〔授权表读故障 `memory_optout_grant_resolve_failed`、中心拉取故障 `runtime_caps_resolve_failed` 各一行 warn;center-only 源上中心健康却省略 `allowMemoryOptOut` 同样放行,但**零运维行**(有本地授权表时 verdict 由它显式答,不缺席);授权表故障那一行之后回落到中心那一层的结果〕(隐私轴 fail-safe:错拒=记了用户明说不要记的会话,不可逆);`governed`=verdict **必答**(混合车队:部分用户强制留存),故障/缺席**拒跑**(fail-closed);`capture-required`=全体声明拒(一行配置)。verdict 源**两条**(析取):①SQL 店后端上的授权表 `memory_optout_grant`(三层折叠:per-principal 行 → 部署缺省行 → 代码缺省 allow;管理面 `GET|PUT /v1/admin/memory-optout`(PUT=部署缺省行)· `PUT|DELETE …/:principal`,operator-only,能力位 `memoryOptOutGrant`,零缓存写后即生效);②非 dry-run 的 web-admin 在该 principal 的 entitlement caps 里发 `allowMemoryOptOut`。🔴 **`governed` 而两源皆无 ⇒ 启动期拒启**(无源 = 每个声明都被拒且没人知道为什么;接 `DB_BACKEND=mysql|pg`、接非 dry-run center,或改姿态)。⚠️ **center-only 源的语义**(只配了 center、没有 SQL 授权表):`allowMemoryOptOut` 在 entitlement schema 里是**可选键** —— center 不发它 = 该 principal 的 verdict **缺席** = `governed` 下每次 opt-out 恒拒(403 `memory.capture_optout_denied`),而 worker **照常起动**;这一形部署级证不死(per-principal 授权只有运行期才知道),所以 server 两处响亮:启动期一行 `memory_capture_governed_center_only_source`、运行期首次真缺席一行 `memory_capture_governed_verdict_absent`(只在 `governed` 挂;每进程每成因一次,成因两员:无主体的匿名 / 系统 run `anonymous_lane`、具名主体而 center 没发该键 `center_verdict_absent`;纯披露不改判)。要确定性 verdict 就接 SQL 授权表(它三层折叠后恒答显式 boolean)。坏值**启动期拒**(`Governed`/`required`/`off` 一律不折成缺省:两种姿态的故障极性**相反**)。**部署席 only**;**不冻进 checkpoint**。运维读面 = `GET /v1/diagnostics/wiring` 的 `memoryPosture.capturePolicy`(`null`=本部署未设)。记录载体(core 7.0.2 起):SQL 后端(`DB_BACKEND=mysql\|pg`)上记录落 `session_capture_optout` 表(跨副本存活,随中央 schema 自建零旋钮)——**远端车道(e2b/k8s)的 `capture:"off"` 声明自此真持久**;`DB_BACKEND=local` 自 7.71.0 起在记忆引擎接线时供 core 的控制面文件三腿(钉在本部署记忆引擎那只控制面目录上,按记忆平面取用)⇒ 单机默认部署(`CONFIG_PROVIDER=local`、`REMOTE_EXEC` 未设)上声明与中途翻转 verb 从 409 转为真受理(此前该 409 在这一形上是结构性的:core 判「远端」看的是执行环境的接口形,host 车道同被判 remote);记忆面暗的部署本席位缺席而 core 也不走采集腿。今天仍答 409 `config.memory_capture_unsupported` 的只剩把记忆引擎接了却没给任何店的自建装配形。用户面四口:提交键 `memoryCapture:"off"`(声明形)· `POST /v1/runs/:id/memory/capture-optout`(live 中途翻转,owner 门)· `POST /v1/sessions/:id/memory/erase`(所有者自助抹除,select 硬限本会话)· `GET /v1/sessions/:id/memory-status`(状态读面,owner 门,五可选键诚实缺席)。契约全文 `docs/ASSISTANT-WIRE-CONTRACT.md` §12 |
528
536
 
529
537
  ⚠️ **拒启动**:多租户 + 记忆面点亮 + `projects[].defaultScopes` 里有 org 键 + 目录源缺席 ⇒ 启动报错
530
538
  并点名 projectId(该部署的每个此类请求都会在 prepare 期整拒,响亮拒启动比静默全拒服务诚实)。
@@ -616,8 +624,7 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
616
624
  下一轮不必从店重新唤醒整只会话。每次命中先读店里这只会话的当前叶(SQL 后端一条主键 SELECT):与暖份的叶相同 ⇒ 照用;
617
625
  不同(这只会话在别的副本上写过)或会话已不在 ⇒ 丢掉暖份、从店重读再用。所以 chart 缺省 Service、轮询负载均衡这类**任意路由**
618
626
  下,同一会话轮流落到不同副本不会撞 `conflict`;会话亲和只提高命中率(少重读),不是正确性前提。代价:每次命中多一次读头
619
- —— SQL 后端一条主键 SELECT;文件后端(`DB_BACKEND=local`)是把这只会话的存档文件再读一遍(只读头,不铸 `session.records_skipped`
620
- 帧)。命中却已陈旧的次数计在 `session_cache_stale_hit_total`(高 = 同一会话在 TTL 内常换副本,加亲和可省掉这些重读)。
627
+ —— SQL 后端一条主键 SELECT;文件后端(`DB_BACKEND=local`)是把这只会话的存档文件再读一遍(只读头,不回放日志)。命中却已陈旧的次数计在 `session_cache_stale_hit_total`(高 = 同一会话在 TTL 内常换副本,加亲和可省掉这些重读)。
621
628
  `0` = 每一轮都从店唤醒,不复用。⚠️ 核的是叶:另一只副本用会话同步**整段替换**(含 `overwrite-dst`)把已有条目的内容改了、
622
629
  末叶却没变的那一形,别的副本上的暖份在 TTL 内仍是旧内容(与改前同;写不会因此失败)。依赖这一形即时可见的部署设 `0`。
623
630
  - 数据驻留提示:`DB_BACKEND=local` 下显式 `SESSION_BACKEND=memory` 会被收编为 **durable(local)**
@@ -645,7 +652,10 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
645
652
  行与编号高水位一并抹掉(同 anchors/附件/快照的级联)——会话 id 可被重新认领,残留就等于把上一位
646
653
  主人的任务标题与编号进度交给下一个化身。
647
654
  - **会话的行事实只住在数据根的 `session-incarnations.json`**(每只会话一格:属主 / 化身 / 执行放置 / 建档时刻 / fork 谱系);`sessions/*.jsonl` 是匿名的对话内容。
648
- 备份 / 迁数据根时两样一起搬;更早版本写下的带属主戳的存档由起服一次性迁移改写(改不了的点名,fork 与改日志推送答 409),快照目录 `session-archive-m33/` 非空且消化不了 ⇒ 拒启(MIGRATION 写了出路)。
655
+ 备份 / 迁数据根时两样一起搬;更早版本写下的带属主戳的存档由起服一次性迁移改写(改不了的点名,fork 与改日志推送答 409 `conflict.session_archive_stamped`),快照目录 `session-archive-m33/` 非空且消化不了 ⇒ 拒启(MIGRATION 写了出路)。
656
+ `session-incarnations.json` 本构建读不出(更新的引擎写过、格里多出不认得的键;或文件坏了 / 读不了)⇒ 起服拒 `config.schema_mismatch`(与 SQL 后端列册对账同一个码,拒句点名文件与键;
657
+ 出路 = 部署写下它的那一版,或恢复升级前的数据根备份;读不了 ⇒ 修好权限 / 盘再起);起服之后才读不出(跳过了 `ensureSchema()` 的嵌入方;或违规共享的数据根上另一只引擎写了它)⇒ 同码拒,
658
+ 形读不出则那只边档的每一读每一写拒到重启,盘上字节不被盖掉。
649
659
  - 多实例边界:同一数据根同时只允许一个实例(BootLock 独占,第二个进程拒启;死主自愈含 SIGKILL)。
650
660
  core 5.54.0 起拒启错误带**可分派 code**(`FileStoreLockError`,message 点名占用方 pid),运维按码
651
661
  三动作:`store.dir_in_use`=另一个活进程持有该目录 → 停掉它(或换数据根/迁 SQL 后端);
@@ -710,10 +720,10 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
710
720
  ⚠️ 这条拒启**穿过**「裸 boot 的 local 存不了盘就退回内存」那条既有降级臂:开了这根旋钮就是要
711
721
  fail-closed,被降级成内存等于把它翻译成 fail-open(而且丢的正是持久 run 账本)。
712
722
  射程 = boot 期的**全部盘面**:`runs/`+`runs/active/` 的建目录、两次目录列举,加上逐个
713
- `run.json` / `events.jsonl` / claim 文件的读取。目录里本来就没有的东西(`.DS_Store` 一类杂物、
723
+ `run.json` / `events.jsonl` / claim 文件与每会话块账本 `run-blobs/<会话>.jsonl` 的读取。目录里本来就没有的东西(`.DS_Store` 一类杂物、
714
724
  尚未落盘的目录)照旧跳过 —— 拒的是「读不出来」,不是「没有」。
715
725
  ⚠️ 射程**不含**「文件读得出、里面有一条坏记录」:那一形在两档下都是跳过 + 披露 + 计数(下一条)。
716
- - **local 车道 JSONL 店的坏记录(7.104.0 起)**:`DB_BACKEND=local` 的八本 JSONL 账(run 店三面、续跑锚、
726
+ - **local 车道 JSONL 店的坏记录(7.104.0 起)**:`DB_BACKEND=local` 的八本 JSONL 账(run 店四面、续跑锚、
717
727
  发文件账本、流内审批 ask 账本、直连门 nonce 账、「本会话不再询问」豁免账、规则审批记录 / 导入票两本)重放时,
718
728
  **中间**一条记录读不出(一行 `42`、缺本店必填键的对象、半截 JSON)不再静默:每条打一行 warn(事件名
719
729
  `<店>_corrupt_read`,带 `path` / `line` / `form` 与本次重放的 `kept` / `dropped` / `total`),方向按店分两类 ——
@@ -761,9 +771,12 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
761
771
  - **服务进程被 `SIGKILL` 时,暂存目录会留在目标旁(可直接删)**:上一条的清理是服务进程自己做的 —— `kill -9`、内存杀手、容器被强停这类**没有机会跑清理**的退出,正在传输的那一次写留下 `.<名>.stage-<12 位十六进制>/`,里面是在传的新内容(上传途中被杀只有前半截,传完才被杀则是整份);**目标逐字是旧内容**,整文件替换的保证不受影响,多出来的只是这只目录。ssh 车道实测(被强杀的写都留下了);其余四条远端车道走同一段暂存脚本,同形。取消(Ctrl-C 中断那一次写)不留 —— 取消路径上清理那一往返照跑。**本版不自动清**:下一次对同一目标的写不会去扫陈旧的暂存目录,它会一直留着,确认没有另一个进程正在写同一个目标之后 `rm -rf` 即可(名字里的十六进制每次写都不同,在写的那一只和陈旧的那一只光看名字分不出)。host 车道的暂存是引擎的,同形的残留名是 `<目标>.tmp.<进程号>.<十六进制>`,同样可以删。
762
772
  - 暂存自创建起不比目标更可读:远端五车道的暂存只在私有目录里(别的用户进不去;替换 0600 的文件时新内容在传输期间只有写入方与 root 读得到),device 的暂存件以 0600 打开;
763
773
  host 车道的这一保证在引擎原语里:引擎 7.60.0 起目标存在时暂存件以 0600 打开。新建的文件是写入方身份的 umask 形(local-docker = 容器内镜像用户的)。
764
- 私有目录建出后要读回核实是 0700 且属写入方;**核不过(`chmod` 被拒、文件系统不认权限位、缺省 ACL 的掩码收不下来、目标机没有 `stat`)⇒ 不暂存,退回就地写目标**
774
+ 私有目录建出后要读回核实是 0700 且属写入方、且目录上没有 ACL 条目:macOS 的原生 ACL 与 FreeBSD / illumos ZFS 的 NFSv4 ACL 不经模式位 —— 工作区父目录带可继承的允许条目时,新建的私有目录一建出来就带着它,读回 `700` 也挡不住,
775
+ 所以读到条目就去掉(macOS `chmod -N`、FreeBSD `setfacl -b`、illumos `chmod A-`)、去完再读;**核不过(`chmod` 被拒、文件系统不认权限位、缺省 ACL 的掩码收不下来、继承来的 ACL 条目去不掉或读不出、目标机的系统不在 Linux / macOS / FreeBSD / illumos 之内、目标机没有 `stat`)⇒ 不暂存,退回就地写目标**
765
776
  (两只文件面、五条腿都有;非原子;ssh / local-docker / k8s 写进原 inode、权限位不变,e2b 的 `files.write` 与 adb 的 `adb push` 整份写目标、inode / 权限位由对端定;
766
- 遥测计 `server.remote-write.stage-seal-fallback`);adb 的追加 = 7.138.x 的形:读旧 + 拼 + 整份 push(push 没有追加写)。Android 共享存储(`/sdcard` 一类)上 adb 的写按 sdcardfs / MediaProvider FUSE 的权限语义**推断**每一次都走这一形(未上真机,真机读数候 test)—— adb 的工作区放在 `/data/local/tmp` 一类认权限位的目录即照常暂存。
777
+ 遥测计 `server.remote-write.stage-seal-fallback`);读 ACL 的臂按**目标机**的 `uname -s` 选(Linux:POSIX ACL 由模式位读回的掩码覆盖,不另读 · Darwin · FreeBSD · SunOS);FreeBSD / illumos 两臂按系统文档写、未上真机,读 ACL 的工具报错 / 输出形不对 / `uname` 退出状态不成都按读不出处理(读 ACL 的工具自己静默吞掉的读取错误看不出);
778
+ Linux 上经 NFS 挂载、由服务端自己执行的 NFSv4 ACL 在客户端没有可读的条目,不在覆盖内;其它系统(OpenBSD / NetBSD / AIX / Cygwin 一类)的目标机每一次写都走就地写并计数。
779
+ host 车道的暂存是引擎的,device 车道的暂存是设备侧执行器的 —— 这一道不管它们;adb 的追加 = 7.138.x 的形:读旧 + 拼 + 整份 push(push 没有追加写)。Android 共享存储(`/sdcard` 一类)上 adb 的写按 sdcardfs / MediaProvider FUSE 的权限语义**推断**每一次都走这一形(未上真机,真机读数候 test)—— adb 的工作区放在 `/data/local/tmp` 一类认权限位的目录即照常暂存。
767
780
  - 一个身份:准备目录、上传、提交、清理都以同一个身份做。local-docker 的上传是容器内以镜像的用户经 stdin 收字节(不是守护进程以 root 的 `docker cp`)——
768
781
  写文件与代理的 shell 同一套权限,镜像用户写不了的路径答 `permission_denied` —— 镜像里没预建的工作区目录由容器启动以 root 建出,镜像用户 ≠ root 时要在镜像里预建并归镜像用户。
769
782
  - 回落(host 与五条远端车道,与引擎同一套;device 由设备侧执行器自定,非常规文件拒写):单跳符号链接 ⇒ 替换它指的那只常规文件(链接仍是链接;远端车道先在那只文件的目录里重新暂存,rename 只在一个目录之内);链接链 / FIFO / 设备文件 / rename 被拒(如单文件绑定挂载)⇒ 由已落定的暂存件就地拷入(非原子);
@@ -1449,6 +1462,8 @@ TASK_WRITE_FACE_ESCALATION=allow # 词表 allow|refuse;坏词启动期响亮
1449
1462
  只有 `__proto__` 名的成员坏 ⇒ 剔掉它、其余照交。server 照挂,运维 warn `mcp_transport_members_dropped`(`server` / `lost` / `lostTotal?` / `note`)点名没交出去的键 / 成员与整键没交的
1450
1463
  `transport.args` / `transport.principalHeader`。A2A 对端 `headers` 同读:值不是串 ⇒ 对端照旧不挂;只有 `__proto__` 成员坏 ⇒ 续跑照挂、剔掉它,一行 `a2a_peer_headers_members_dropped`
1451
1464
  (`peer` / `lost` / `note`);新提交上两形都整条不挂。
1465
+ - **续跑时换表(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`;续跑请求体带上同名键(与提交体同形、同一只判官)就以它为准,
1466
+ 并写回成之后续跑的表 —— 用户移除过一台 MCP 服务器之后,壳送此刻的整张表,续跑就不再重连那一台(停在那一台调用上的任务由引擎判:引擎 7.61.0 起三码各答各形,以 core 为准:那只工具不在了 ⇒ `resume.tool_unavailable`(引擎重开检查点:卡仍 pending、可再决);同名而已不是批准时那一只 ⇒ `resume.tool_contract_mismatch`(同样重开);还是那一只而它的面或声明变了 ⇒ `resume.target_moved`(这次调用不执行、批准用掉,须重新发起),本服务逐字过境);直连门部署的两道回决门拒收这两键(契约 §4e-bis)。
1452
1467
 
1453
1468
  **可选 — MCP 入站表单(elicitation,E23):默认关**
1454
1469
 
@@ -1711,7 +1726,7 @@ invalid_using_default` 警告 + 退回缺省值」这条臂随词表放宽一并
1711
1726
  (中心热应用新增 / 改名条目时在模型目录换代前重声明,名单只随落地的世代增长 —— 改名后旧名照扣到重启,没落地的候选不留痕;条目的 `apiKeyEnv` 是引擎扣不住的名,或它点到的名正被某只名单型旋钮交给沙箱 / 语言服务器(见下文沙箱名单段)⇒ 该候选整拒),`"all"` = 整份环境**减**它们,任何继承词都不把它们
1712
1727
  交给模型 shell(能力位 `inherit_env.own_credentials_withheld` = 最近一次声明 ⊇ 当前生效配置应声明的集合;真要让模型 shell 持某枚令牌 ⇒ 另放一个不在配置目录、也不是任何模型条目 `apiKeyEnv` 的名,或经 `settings.env` 显式授予);这些变量此刻的**值**也交给引擎按值扣(同值换名的副本同样进不了模型 shell),引擎判某个值「不是凭据值」(空白 / 占位词 / 一个字符重复 / 不足 8 字符)⇒
1713
1728
  那一个值不按值扣(名照扣、不拒启,能力位 `inherit_env.own_credential_values_withheld` 答 false),起服与模型目录换代时每个这样的变量出**一条** warn 结构化行 `own_credential_value_not_withheld { name, reason }`
1714
- (`reason` 是引擎的拒句原文,从不回显值;起服第一步 logger 还没建时,这一行当拍以同一行形写 stderr,不缓冲)—— 按事件名告警,给该变量一个真值即可;`"scrub"` / 缺席 = 上面的缺省剥除。🔴 **`"all"` 只在单用户部署兑现**:`REQUIRE_PRINCIPAL=true`
1729
+ (`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`
1715
1730
  ⇒ 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` 认、不带这两位。部署方
1716
1731
  **没有**旋钮能替调用方打开它,也没有旋钮能在单用户部署上关掉它(单用户部署的用户把他自己的环境交给他自己驱动的 shell);起服一行 info
1717
1732
  `task_inherit_env_honored {honored}` 告诉你这台机器认不认。上面「部署级名单不给模型驱动 shell」那条不变 —— 两件事是两条轴(部署名单 vs 调用方 × 单用户射程)。
@@ -1977,14 +1992,14 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
1977
1992
  ## 7. 其它接口(按需)
1978
1993
  | 端点 | 用途 |
1979
1994
  |---|---|
1980
- | `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 重新登记 ⇒ 其下的子代再次停驻被否决(读过根之后才换的,写进去的仍是读根那一刻的属主) |
1995
+ | `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(`DB_BACKEND=local` 到 7.140.x 也是)/ 会话店不存元组(`DB_BACKEND=memory`;嵌入方自建、不声明委派分区的店),读作「没存」、回落 fleet 行 / 名册上的 id。子代会话的属主是引擎判的委派树属主(**7.144.0+**:没人派生的 run 读自己那一行的属主、按化身绑一次,原样带给每一层子代;三只持久会话店〔MySQL 协议 / PostgreSQL / local〕建行那一笔原样记引擎交来的属主,`null` = 根匿名;停驻挂点只比对宿主行属主与引擎交来的属主,不认领、不推断。**7.133.0–7.143.x** 是本服务在建行那一笔按根 / 父推断:根会话的行在 ⇒ 它的属主,根行已不在 ⇒ 它记录的父会话的,都不在 ⇒ 哨兵 `_orphan:<id>`;7.132.x 及更早的 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 后端两店每次起服由一次性迁移 M23 在它的窗内回填升级前写下的无主 placed 行(按 7.133.0 起的读法:根的属主,根行不在取记录的父的,都不在写 `_orphan:<id>`;起服读数行 `placedSessionOwnersRemaining` 非零 = 还有没定下的,再起直到为 0),local 后端没有回填臂。属主 `_orphan:<id>` 由 7.133.0–7.143.x 的建行推断与 M23 回填写下(7.144.0 起本服务不再推哨兵,建行原样记引擎交来的属主 —— 已有的哨兵属主随引擎的树属主原样带到它下面新建的子代行上):任何主体都不匹配,只有 fleet-wide / operator 读得到,前缀永久保留。停驻时宿主行属主与引擎交来的不等 ⇒ 停驻否决(那张待批卡作废、那一只子代以失败收尾) |
1981
1996
  | `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=` 跨看也是原文) |
1982
1997
  | `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+**) |
1983
1998
  | `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) |
1984
1999
  | `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 |
1985
2000
  | `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) |
1986
2001
  | `GET /v1/capabilities/scenarios/<name>` | 单个场景的只读详情:工具面、提示词概览、**本部署现在跑不跑得动**、附件供给、**技能清单**(见下) |
1987
- | `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` |
2002
+ | `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` |
1988
2003
  | `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` 已退役。 |
1989
2004
  | `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 原文。 |
1990
2005
  | `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。 |
@@ -2041,6 +2056,7 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
2041
2056
  > 治理拍),锁上会让**每一条**任务在引擎门口被拒,而那把锁本就无物可守(策略已是部署所有、请求放松不了);
2042
2057
  > ②`mcp` + 部署自己配了 MCP 服务器——部署的服务器走同一个字段,锁上同样每条腿必拒。两者都在**启动期**点名拒,
2043
2058
  > 不留给运维在每条任务上猜。
2059
+ > 同一条判据也管 A2A:`LOCKED_CONFIG_KEYS` 含 `a2a`,或合规档位禁 `a2a_peers`,而部署自己配了 A2A 对端(中心 `a2a` 域)⇒ 同样在启动期点名拒。
2044
2060
  > - `RETENTION_MAX_AGE_DAYS=<非负数>`:托管留存**视界**(天;`0` 合法=立即可删)。坏值(非数/负数/设了但为空)
2045
2061
  > 一律拒启——删数据的旋钮不许 NaN 流通。**它只是"保留多久"这句声明**;真正按它删数据的是下面两根。
2046
2062
  > - `RETENTION_SWEEP_INTERVAL_SEC=<非负整数>`(默认 `0` = **关**;上限 `2147483` 秒 ≈ 24.8 天):
@@ -2248,9 +2264,10 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
2248
2264
  > 没有家目录读数,座缺席,起服响一行同一只 warn。`~/` 形规则的下单门只认声明(提交那一刻执行环境还没开,读不到实测值):要写 `~/` 形规则就声明 `REMOTE_EXEC_HOME_DIR`。
2249
2265
  > **声明与出厂答案都会被对证**(本版起六条车道同一只判官):k8s、local-docker、ssh、adb、e2b 在每只执行环境开起来那一拍问一次 `$HOME`(与命令同一条 shell、同一份 env;
2250
2266
  > adb 经设备 shell、e2b 是建 / 恢复沙箱时的一次 `commands.run`),host 读它自己组合给子进程的那份 env 里的 `HOME`(会话 `settings.env` 改了 `HOME` 也算);
2251
- > 与主张(`REMOTE_EXEC_HOME_DIR`,或 host 的本进程家目录 / e2b 的 `/home/user`)不一致(去掉尾斜杠后逐字比)或问不出 ⇒ 那只执行环境不开,工具结果里的拒句点名两个值与改法
2252
- > (把 `REMOTE_EXEC_HOME_DIR` 设成实测值、删掉错的声明,或让环境的 `HOME` 读主张那个值);环境答了的不一致记在那只环境上(此后每次回同一句,不再起容器 / pod / 沙箱、不再问),
2253
- > 问不出的那一次不记、下一次重问。e2b 恢复出来的沙箱对不上时重新挂起(快照留着),不杀。
2267
+ > 与主张(`REMOTE_EXEC_HOME_DIR`,或 host 的本进程家目录 / e2b 的 `/home/user`)不一致(去掉尾斜杠后逐字比)或问不出 ⇒ 那只执行环境不开,拒句点名两个值与改法
2268
+ > (把 `REMOTE_EXEC_HOME_DIR` 设成实测值、删掉错的声明,或让环境的 `HOME` 读主张那个值)。host 车道的读数在铸环境时就到手 ⇒ 环境不铸,run 在开跑前失败
2269
+ > (码 `env.home_unconfirmed`,同步腿 409;一条命令没跑、模型一次没调,命令带不带路径都是这一句;7.144.0 起,此前拒句落在工具结果里);远端五车道的读数要环境开起来才有 ⇒
2270
+ > 拒句落在工具结果里,环境答了的不一致记在那只环境上(此后每次回同一句,不再起容器 / pod / 沙箱、不再问),问不出的那一次不记、下一次重问。e2b 恢复出来的沙箱对不上时重新挂起(快照留着),不杀。
2254
2271
  > e2b 的出厂答案 `/home/user` 已于 2026-10-29 在真沙箱上实测一致(缺省模板:用户 `user`、`$HOME` = `/home/user`);自定义模板换了用户的,`$HOME` 若不是它,没声明 `REMOTE_EXEC_HOME_DIR` 的 e2b 部署的每只沙箱建起来就拒,把声明设成沙箱里 `echo $HOME` 的结果(声明同样被对证)。
2255
2272
  > device 车道读不了家目录:声明即座,起服响一行 `execution_lane_home_unmeasured`(点名声明值与「未实测」)。
2256
2273
  >
@@ -2370,6 +2387,7 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
2370
2387
  | HTTP `409`(`/v1/runs`) | 同 session 已有活跃 run | 等它完成 / 用返回的 `activeTaskId` |
2371
2388
  | HTTP `409` + `errorCode:"conflict.idempotency_payload_mismatch"`(7.126.0 起;`POST /v1/tasks` · `/v1/tasks/stream` · `/v1/runs`) | `Idempotency-Key` 已绑定在一条你读得到的 run 上,而绑定它的那次请求的体与这次不同(规范化:按键排序、去掉 `taskId`;契约 §0.7「同一请求 = 同一份体」)—— 不回放、不新建、那一条不动,体不回显键与那一行;流式腿在开流之后才撞上时,是终帧 `done.result` 带同一 `errorCode` 的 `failed`。A2A 不会遇到 | 重试就重发同一份体;不同的请求换一把新键。与 `conflict.run_exists` 不同:后者是那一行你读不到、或带的 `taskId` 不是那一行 |
2372
2389
  | HTTP `429` | 限流 | 看 `Retry-After` 退避 |
2390
+ | HTTP `409` + `errorCode:"conflict.session_archive_stamped"`(7.144.0 起;`DB_BACKEND=local`;`POST /v1/sessions/:id/fork` 与会话同步导入的提交 `POST …/sync/import/:stagingId/entries`;7.141.0–7.143.0 答的码是 `conflict`) | 这只会话的存档仍带更早版本写下的属主戳(一次性迁移改写不了的,或跳过带迁移的版本升上来的数据根;起服 warn `local_archive_kept_stamped` 点名):不在它上面造新存档、不原地换它的日志(干净追加 / 载荷分叉 / fork / stale 各形的推送同答),什么都没写;读 / 续跑 / 拉取照常,「id 与内容都相同」的重推照常落定 | 按体里的固定句办:删掉这只会话(`DELETE /v1/sessions/:id`)再推。与暂存提交时属主变了的 `errorCode:"conflict"`(出路相反:从第一阶段重来)按码分开 |
2373
2391
  | HTTP `429` + `errorCode:"limit.cost_quota_exceeded"` | per-principal 累计成本配额越顶(`MAX_PRINCIPAL_COST_USD`;**进场门**,不打断在跑的 run) | 看 `Retry-After` / 体 `retryAfterSec` 退避(体 `windowResetsAt` = 当代窗滚过的时刻);窗滚过或调高上限后放行 |
2374
2392
  | 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 …`),只作展示、别锚文案 |
2375
2393
  | 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` 分支,句子只作展示 |
@@ -2386,10 +2404,10 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
2386
2404
  | HTTP `422`(`request settings cannot be applied as written: …`)+ `detail.problems[].reason:"home_unresolvable"`,句子说 `user-settings path rule rooted at the configuration directory`(7.141.0 起) | 请求 `settings.permissions.deny` / `ask` 里一条 `layer:"userSettings"` 的 `/` 形路径规则,而这条沙箱车道既没把 `SEMA_CONFIG_DIR` 转进沙箱、也没有可知的 home(k8s / local-docker / ssh / adb / device 且未声明 `REMOTE_EXEC_HOME_DIR`)—— 配置根读不出,不猜目录(续跑同体 409 同句) | 往沙箱 env 名单里加 `SEMA_CONFIG_DIR`,或声明 `REMOTE_EXEC_HOME_DIR`,或把规则写成 `//` 绝对形 / 放进项目层(读在任务根);见 9.4d |
2387
2405
  | HTTP `429` + `errorCode:"usage.window_exhausted"` | 部署级治理窗耗尽(`USAGE_WINDOWS`,token 或 $ 天花板先满者) | 看响应体 `retryAfterSec` 退避;窗滑动/桶到期后放行 |
2388
2406
  | `terminal:{kind:"failed", code:"usage.window_exhausted"}` | 已受理的 run 在 **turn 边界**撞上治理窗且无法 durable 挂起 | 同上退避后重投;配好 checkpoint 基建则改为 `suspended` 等窗自动续跑 |
2407
+ | HTTP `409` + `errorCode:"env.home_unconfirmed"`(同步 `POST /v1/tasks`;流式 / 后台腿是 `terminal:{kind:"failed", code:"env.home_unconfirmed"}`;7.144.0 起) | 执行环境的家目录主张证不了:部署给环境的家目录(`REMOTE_EXEC_HOME_DIR`,没设时 host 车道 = 服务进程的家目录)与环境里命令看到的 `HOME`(含会话 `settings.env` 里写的 `HOME`)对不上 / 读不出 / 不是绝对路径 ⇒ 环境在开跑前就不铸:一条命令没跑、模型一次没调、一个 token 没烧,不管模型要跑的命令带不带路径都是这一句(句子点名两个家目录与改法)。今天只有 host 车道(含 `run-local`)这样答;远端五车道照旧在开环境那一拍拒、同一句落在工具结果里(读边界要先读环境才判得了的命令 —— 递归读,adb 上连带路径的普通读 —— 在那里先被读边界拦下,句子不点名家目录) | 把 `REMOTE_EXEC_HOME_DIR` 设成环境里 `echo $HOME` 的结果(或删掉它),或让环境的 `HOME` 等于声明,或去掉会话 `settings.env` 里的 `HOME`;原样重试不改判 |
2389
2408
  | HTTP `501`(`/v1/runs`) | 内存模式不支持异步 | 配持久存储(`DB_BACKEND=mysql` / `pg` + 坐标;7.106.0 起给了坐标必须点名 `DB_BACKEND`) |
2390
- | HTTP `500` + `errorCode:"corrupt_session"`(7.104.0 起;流式口在应答头之后则是 `done` 帧的 `result.terminal.code`,后台 run 是 run 行的 `errorCode`) | 这只会话的**存档读不出**、唤不醒(文件后端:会话日志没有会话头;SQL 后端:一行事件读不出)。这一轮没跑,一个 token 没烧。直接读这只会话的口(头探针 / 事件订阅 / 会话视图 / 改名 / 导出 / 分叉 / 2c 同步)同答(契约 §17;文件后端上事件订阅与改名 7.136.0 起同答,此前一个 200 起流却零帧、一个 404)。会话列表 `GET /v1/sessions` 里它仍是一行,行带 `unreadable: { errorCode }`,码就是这些口对它答的那一个(文件后端,7.142.0 起;此前它从列表里消失;存档文件本身读不出 —— 权限 / I/O —— 时码是 `internal.error`) | **别重试**(确定性失败)。**文件后端**:运维看 `fail_open_total{tag="server.session-repo.corrupt-record-skipped"}` 的计数、stderr 契约行 `[sema] fail-open: server.session-repo.corrupt-record-skipped` 与随带的一条 `fail_open` warn(后两者每进程每 tag 只一次,warn 带首条明细);逐条明细(文件路径、会话 id、行号、成因)每命中一行进探针文件 —— 起服前设 `SEMA_FAILOPEN_PROBE=<文件路径>`(会话头那一行本身读不出时,它就是其中一条);修或移开那份存档。**SQL 后端**不经这只座:坏的那一行由错误句子点名(`session_event.payload` + 会话 id + 序号 `seq`)。要继续工作就换一个新会话 |
2391
- | HTTP `500` + `errorCode:"invalid_session"`(7.137.0 起;流式口在应答头之后则是 `done` 帧的 `result.terminal.code`,后台 run 是 run 行的 `errorCode`) | 这只会话**存着的日志**读得出、结构走不通:某条目的父不在日志里(悬空)、条目是自己的祖先(自指 / 成环)、叶子不在日志里。这一轮没跑、一个 token 没烧(同步提交答 500、不计费)。读整条分支的口(会话视图 `GET /v1/sessions/:id` / `…/memory-status`)同答;事件订阅与头探针只读会话头的叶子,对这一形照旧 200 / 起流(契约 §17.7)。体的 `error` 是引擎原句(点名条目 id,不含盘上路径与会话 id),没有 `reason` 机读位。会话同步**导入**时调用方交来的日志没过校验是另一件事:`422 invalid_entries` | **别重试**(确定性失败)。换一个新会话;或从断点之上的条目分叉;或由运维修好 / 移走那份日志。同一 `Idempotency-Key` 重发会重新受理(这类零工作的拒不占键) |
2392
- | 流里一帧 `engine_notice` `code:"session.records_skipped"`,`detail:{ dropped, entriesKept, sessionId }`(7.104.0 起,只在文件后端) | 这只会话的存档打开时有 `dropped` 条记录读不出、被跳过了;这一轮**照跑**,但模型读到的历史只剩 `entriesKept` 条。存档没修好之前,每次真正打开这只会话都会再说一次(会话缓存命中的轮次不打开、不说;`SESSION_CACHE_TTL_SEC=0` 时每一轮都说) | 告诉用户「这段对话的一部分历史丢了」;运维按 `fail_open_total{tag="server.session-repo.corrupt-record-skipped"}` 的计数与探针文件(`SEMA_FAILOPEN_PROBE=<文件路径>`,每命中一行)里的逐条明细(文件路径、会话 id、行号、成因)查那份文件。非流式 `POST /v1/tasks` 收不到通告帧(只有运维面的计数与探针) |
2409
+ | HTTP `500` + `errorCode:"corrupt_session"`(7.104.0 起;流式口在应答头之后则是 `done` 帧的 `result.terminal.code`,后台 run 是 run 行的 `errorCode`) | 这只会话的**存档读不出**、唤不醒(文件后端:会话日志没有会话头;SQL 后端:一行事件读不出);或(7.144.0 起)存着的日志读得出、**走不成一棵树**:某条目的父不在日志里而回放没丢记录(悬空)、条目是自己的祖先(自指 / 成环)、叶子不在日志里(7.137.0–7.143.0 这一形答 `invalid_session`;句子点名条目 id,读整条分支的口 —— 会话视图 `GET /v1/sessions/:id` / `…/memory-status` —— 同答,事件订阅与头探针只读会话头的叶子,照旧 200 / 起流)。这一轮没跑,一个 token 没烧。直接读这只会话的口(头探针 / 事件订阅 / 会话视图 / 改名 / 导出 / 分叉 / 2c 同步)同答(契约 §17;文件后端上事件订阅与改名 7.136.0 起同答,此前一个 200 起流却零帧、一个 404)。会话列表 `GET /v1/sessions` 里它仍是一行,行带 `unreadable: { errorCode }`,码就是这些口对它答的那一个(文件后端,7.142.0 起;此前它从列表里消失;存档文件本身读不出 —— 权限 / I/O —— 时码是 `internal.error`) | **别重试**(确定性失败)。**文件后端**:运维看 `fail_open_total{tag="server.session-repo.corrupt-record-skipped"}` 的计数、stderr 契约行 `[sema] fail-open: server.session-repo.corrupt-record-skipped` 与随带的一条 `fail_open` warn(后两者每进程每 tag 只一次,warn 带首条明细);逐条明细(文件路径、会话 id、行号、成因)每命中一行进探针文件 —— 起服前设 `SEMA_FAILOPEN_PROBE=<文件路径>`(会话头那一行本身读不出时,它就是其中一条);修或移开那份存档。**SQL 后端**不经这只座:坏的那一行由错误句子点名(`session_event.payload` + 会话 id + 序号 `seq`)。要继续工作就换一个新会话 |
2410
+ | 流里一帧 `engine_notice` `code:"session.lineage_rerooted"`,`detail:{ break, recordsDropped, recordsTotal, leaf, entriesOnPath, sessionId, runId }`(7.144.0 起,只在文件后端;7.104.0–7.143.0 这件事由 `session.records_skipped` 说,已退役) | 这只会话的日志回放时有 `recordsDropped` 条记录读不出、被跳过了;这一轮**照跑**,模型读到的历史是 `entriesOnPath` 条。`break: true` = 被跳过的是分支上的祖先(`missingParentId`),这一轮从点名它作父的那一条(`rootedAt`)续上、`entriesCut` 条更早的条目不在上下文里(7.143.0 及以前这一形整轮失败);`break: false` = 分支完整,从 `leaf` 续上(`leaf: null` = 一条都没剩,从空对话起跑)。日志没修好之前每一轮都会再说一次(会话缓存命中的轮次照样说);进程已持有这只会话时盘上才坏的行,这一轮读的是内存里那份完整历史、不说,重启后的第一轮才说 | 告诉用户「这段对话的一部分历史丢了」;运维按 `fail_open_total{tag="server.session-repo.corrupt-record-skipped"}` 的计数与探针文件(`SEMA_FAILOPEN_PROBE=<文件路径>`,每命中一行)里的逐条明细(文件路径、会话 id、行号、成因)查那份文件。非流式 `POST /v1/tasks` 收不到通告帧(只有运维面的计数与探针) |
2393
2411
  | 流里一帧 `engine_notice` `code:"route.finish_reason_unknown"`,`detail:{ finishReason, sessionId, runId }`(7.126.0 起) | 提供方用一个引擎读不懂的结束原因结束了这一轮:回答按模型留下的样子保留,但可能被提供方截短了 | 告诉用户「这一段回答可能不完整」;同一个词同一 run 只说一次。运维按 `finishReason` 查是哪家网关 / 模型发的 |
2394
2412
  | 流里一帧 `engine_notice` `code:"route.stream_end_ignored"`,`detail:{ statement, word, frames, sessionId, runId }`(7.126.0 起) | 提供方已经结束了这一轮,它的流却又发来了东西(又一个结束 / 一个错误 / 内容);第一个结束算数,之后的没进这一轮 | 一般无需处置;频繁出现 ⇒ 运维查那条网关的流式实现(`statement` / `word` 说的是迟到的第一帧) |
2395
2413
 
@@ -2511,7 +2529,7 @@ k8s 车道每条 run 一只新 pod(pod 寿命 = 泄漏护栏,不跨 run 复用)
2511
2529
  - **空间 = 会话的这一位住客**(会话化身:会话建行时铸的不透明随机串,删会话随行消失)。同一会话的各条 run 共享一个空间;不同会话互不可见;会话删掉后同一个 id 被重新登记(不论谁)= 新住客,从空目录开始,前一位住客的对象不再被读。前缀 `WORKSPACE_SNAPSHOT_PREFIX` 缺省 `kata`,可以是**多段路径**(`libA/ws`:一只桶按属主分区;7.136.0 起,单段旧值照常合法),桶 `WORKSPACE_SNAPSHOT_BUCKET` 缺省 `workspaces`(**server 不建桶**:起服时 server 核这只桶在不在,缺 ⇒ 拒启,见下方失败形)。
2512
2530
  - **两条车道,两种分区**:
2513
2531
  - **跨轮保留**(本节):归档 `<前缀>/<化身>/space/<snapshotId>.tar.gz`,指针 `<前缀>/<化身>/space/latest`;只有这条车道被列举、被删旧,且只在 `space/` 里。按住客分区的理由:读它的是「这只会话的下一条 run」,分区就决定了谁读得到 —— 按会话 id 分,删后重登记的另一位就会恢复前一位的文件。
2514
- - **停驻挂起 / 续跑**(引擎驱动的 durable suspend):归档 `<前缀>/<会话 id>/<snapshotId>.tar.gz`(key 形与 7.125.0 之前同形,存量停驻快照照常可读,零迁移;7.126.0 起新写的 `snapshotId` 形为 `kata-<归属记号>-<时间>-<随机>`,归属记号 = 住客化身的不可逆摘要头 16 位,没有已知住客的 run 记 `none`);从不被列举、从不被删旧 —— 一条停驻中的 run 的快照不会被同会话后来的 run 挤掉。按会话 id 分区的理由:读它的只有那条停驻 run 自己的检查点(snapshotId 记在检查点里,删会话连检查点一起删),分区只做命名空间、不决定谁读。
2532
+ - **停驻挂起 / 续跑**(引擎驱动的 durable suspend):归档 `<前缀>/<会话 id>/<snapshotId>.tar.gz`(key 形与 7.125.0 之前同形,存量停驻快照照常可读,零迁移;7.126.0 起新写的 `snapshotId` 形为 `kata-<归属记号>-<时间>-<随机>`,归属记号 = 住客化身的不可逆摘要头 16 位,没有已知住客的 run 记 `none`);从不被列举、从不被删旧 —— 一条停驻中的 run 的快照不会被同会话后来的 run 挤掉。按会话 id 分区的理由:读它的只有那条停驻 run 自己的检查点(snapshotId 记在检查点里,删会话连检查点一起删),分区只做命名空间、不决定谁读。**`snapshotId` 自己说打包没打包**(7.143.0 起):`kata-…` = 打包过(此前写下的 id 都读作这一族),续跑取这份归档 —— 归档在 ⇒ 恢复;归档不在(被生命周期规则 / 手工清走)⇒ 与 `latest` 悬空同一条规则(见下方失败形,`lane: "park"`);`nopack-<归属记号>-<时间>-<随机>` = 停驻时沙箱从未起过(run 在首次碰沙箱之前就停驻)、什么都没打包,桶里从不放对象 —— 续跑就是一次新连接:空间开着 ⇒ 等上一条提交、按 `latest` 恢复(不在 ⇒ 空目录),空间关 ⇒ 空目录;这种 id 下不发取包请求。
2515
2533
  - **没有这只会话**(会话店答「没有」:删了 / 从未登记)⇒ 没有空间:这条 run 的工作区既不恢复也不保存,一行 `workspace_space_unavailable`(`reason: incarnation_unknown` / `sessionId`),**不会**改按会话 id 找空间。**读住客失败**(会话店报错,或答出的不是住客令牌)⇒ 读不到空间不等于没有空间:这条 run 连沙箱失败(见下方失败形),不在空目录里照跑。住客在建这条 run 的执行环境时从会话店读出;没有存储后端的部署(`DB_BACKEND=memory`,`/health.sessionBackend` 报 `memory`)与 `space`(含缺省)不能同开:起服拒(见下方失败形)。日志里点名空间只打化身的前 8 位(字段 `space`),完整化身不进日志。
2516
2534
  - **委派子代 / 后台代理不开空间**:它们与父 run 并发,共用一个空间就是两个写者 ⇒ 子代的工作区每轮新建、不恢复也不保存,不出任何行;父 run 的空间不受影响(停驻挂起照常)。
2517
2535
  - **指针**:`space/latest`,体 = 最近一份 snapshotId。run 结束时先把 `/workspace` 打包上传(与挂起同一条车道:沙箱只拿到限时预签 URL,凭据留在 server),成功后**整体覆写** `latest`,再删 pod。下一条 run 第一次用到沙箱时读 `latest`:在 ⇒ 新 pod 上恢复那一份;不在(空间第一条 run)⇒ 空目录。
@@ -2537,7 +2555,7 @@ k8s 车道每条 run 一只新 pod(pod 寿命 = 泄漏护栏,不跨 run 复用)
2537
2555
  - **没有存储后端**(`DB_BACKEND=memory`;配了 SQL 的 `DB_BACKEND` 却显式 `SESSION_BACKEND=memory` 时库不开,同一形)× `space`(含缺省,且配了快照存储)⇒ **拒启**,拒句一行点名 `WORKSPACE_PERSIST=space`、缺的存储后端、会话店种类(与 `/health.sessionBackend` 同一读法)与可用的后端。出路:`DB_BACKEND=local`,或 SQL(`DB_BACKEND=mysql|pg`,`SESSION_BACKEND` 不设 `memory`),或设 `WORKSPACE_PERSIST=off`。`SESSION_BACKEND=memory` 配 `DB_BACKEND=local` 不在此列:被收编成 local 店,照起。拒启而不是降级:否则能力位报 `space`、每条 run 却一份都不存。
2538
2556
  - **快照桶不存在**(`WORKSPACE_SNAPSHOT_BUCKET` 指的桶没建):**起服拒启**(k8s 车道配了快照存储时,server 在打开任何店之前核这只桶;拒句点名桶名、`MINIO_ENDPOINT` 的显示安全形与出路 —— 建桶,或把 `WORKSPACE_SNAPSHOT_BUCKET` 指向已有的桶;建桶晚于 server 起是编排里的常态,所以缺桶在 `STORE_LOCK_WAIT_MS` 窗内重试、到窗才拒);连不上 / 403 / 5xx 这些证明不了桶缺的答复不拒启,走 fail-open 兜底口 `server.snapshot-bucket.presence-unproven`。起服之后桶才没的(被删),或起服核证明不了的部署:读 `latest` 得 `404 NoSuchBucket` ⇒ 这条 run **连沙箱失败**(`connect_failed`,句子带 `snapshot_bucket_missing`、桶名与出路),一只 pod 都不建,另有一行 `workspace_snapshot_bucket_missing`(`space` / `bucket` / `code` / `effect`),每只执行环境至多一次;**不再**读成「空间第一条 run」而给一个空目录、每轮只留下一条不点名桶的存失败。无 `NoSuchBucket` 的 404(桶在、键不在;代理的 404)仍是「空间第一条 run」。
2539
2557
  - **读 `latest` 失败**(非 404 的 HTTP 错、网络错)、**读或弃提交围栏那一行失败**或**读会话住客失败**(会话店报错、答出的不是住客令牌,或没在 `rpcTimeoutMs`〔30 秒,与读围栏行、读 `latest` 同一只墙〕内答 —— 挂住的会话店不再让 run 的起步永挂)⇒ 这条 run 连沙箱失败(`connect_failed`,工具调用报沙箱不可用;停驻 run 续跑时的恢复同样失败),**不会**悄悄给一个空目录、也不猜「没有提交在飞」—— 「以为恢复了其实没有」比失败更坏。读住客失败时一只 pod 都不建,另有一行 `workspace_space_unreadable_occupant`(`sessionId` / `code`:会话店错误的码,不带正文;读超时的码是 `incarnation_read_timeout`),每条 run 至多一次。修好存储 / 数据库后重跑,或设 `WORKSPACE_PERSIST=off` 即恢复。
2540
- - **`latest` 指向的快照对象不在了**(被删 / 被只盖 `.tar.gz` 的生命周期规则清掉)⇒ 沙箱**照常起**:一行 `workspace_latest_dangling`(`space` / `key` / `deleted`),删掉悬空的 `latest`,这条 run 从空目录开始,它的 run 末快照成为新的 `latest`(数据已被运维规则清走:响亮、可继续)。指针删不掉 ⇒ 同一行 `deleted: false` + `error`,下一条 run 再说一次。
2558
+ - **`latest` 指向的快照对象不在了**(被删 / 被只盖 `.tar.gz` 的生命周期规则清掉)⇒ 沙箱**照常起**:一行 `workspace_latest_dangling`(`lane: "space"` / `space` / `key` / `deleted`),删掉悬空的 `latest`,这条 run 从空目录开始,它的 run 末快照成为新的 `latest`(数据已被运维规则清走:响亮、可继续)。指针删不掉 ⇒ 同一行 `deleted: false` + `error`,下一条 run 再说一次。**打包过的停驻快照对象不在了**(`kata-…` 形,同样被清走)⇒ 同一条规则、同一行名:续跑**照常起**,一行 `workspace_latest_dangling`(`lane: "park"` / `key` / `sessionId`),这条腿从空目录开始(停驻车道没有指针可删;空间开着时它的 run 末快照成为新的 `latest`)。7.142.x 及以前这一形不出任何行。
2541
2559
  - **`latest` 指向的快照在、却拿不回来**(桶答了却不是 200 / 解压失败 / 超过快照时限 / 传输中断)⇒ 这条 run **仍然连沙箱失败**(`connect_failed`)、**不**退到空目录 —— 用户的改动还在桶里,静默空目录 = 让人以为丢了。**一条 run 只恢复一次**(同一只执行环境上并发的连沙箱 —— 显式 `connect()` 与首个工具调用的懒连接 —— 共用同一次建连:一只 pod、一次恢复、一个终局,被拒也是同一个拒;恢复还没完成时才到的工具调用和 `reconnect` 也等这一次建连 —— pod 先于它的工作区存在,不在半恢复的 `/workspace` 上先跑):瞬时类(`timeout` 超过快照时限 / `transport` 传输中断:执行通道断了,或 pod 取包的 GET 没拿到完整应答 —— 连不上、被重置、传到一半被截)在恢复这一步里换一只新 pod 再试一次(约 1 秒后),再试成了 ⇒ 照常起、不出行;`store_busy`(桶答 `408` / `429` / `500` / `502` / `503` / `504` —— 「现在不行」,同一请求再发可能换答案)同属瞬时类;非瞬时类(`download` 桶答了、答的是其余非 200 —— 如 `401` / `403` / `501`;`untar` 包完整下来了却解不开)不重试。每次没成的尝试,新起的 pod 当场删掉(残缺目录既不给工具用,也不会在 run 末被快照回去覆盖 `latest`)。恢复终局是拒 ⇒ **恰一行** `workspace_restore_failed`(`space` / `snapshotId` / `class` = `store_busy` \| `download` \| `untar` \| `timeout` \| `transport` / `attempts` = 这次恢复的尝试次数(`1` 或 `2`)/ `boundMs` = 每次尝试的时限 / `error`,预签 URL 已脱敏),这条 run 之后的每次沙箱操作都答**同一句**拒(不再建 pod、不再读 `latest`、不再出行);建 pod 本身失败不是恢复的结局(不出这行,下一次操作重来)。拒句写明类别与尝试次数,点名两条出路:**调大 `WORKSPACE_SNAPSHOT_TIMEOUT_MS`**(大工作区撞上界),或 **`WORKSPACE_PERSIST=off` 跑一轮**(空目录起,不删任何快照;改回 `space` 后下一条 run 照常恢复那一份)。没有「删指针」的接口:丢弃这份快照 = 丢弃用户数据,要做就由运维在对象存储侧有意识地删 `<前缀>/<化身>/space/latest`(下一条 run 从空目录起)。与上一条「悬空」分开:那是对象**不在**(没东西可恢复 ⇒ 空目录 + 响亮),这是对象**在**而拿不回来(⇒ 拒)。
2542
2560
  - **run 末提交围栏失守、上传失败或 `latest` 写失败** ⇒ 一行 `workspace_snapshot_failed`(`space` / `stage` = `fence` \| `upload` \| `pointer` / `code` / `error`,预签 URL 已脱敏),这条 run 在 `GET /v1/fleet/stream` 上的终态 task 帧带 `workspaceSnapshot: "failed"`;pod 照删;`latest` 不动 ⇒ 下一条 run 恢复的是**上一份**。`latest` 只指向真写出的对象:run 末保存走到上传那一步时沙箱 pod 已不在(它在提交围栏认领期间消失)也按上传失败处理(`stage: upload`、`code: connect_failed`),不铸一个没有对象的 snapshotId 去改指针。
2543
2561
  - **run 末快照保存前一律先停 pod 里的进程并确认**(与那一刻有没有命令在飞无关)。命令「结束」(自己退出、被取消、超时)不等于它起的进程结束(断开 exec 套接字不会让 pod 里的进程停),在飞登记里已经没有命令也不能证明 pod 静了,所以保存前都先杀掉 pod 里除 init 与容器保活进程外的全部进程并确认;没连过沙箱的 run 没有可存的工作区,不走这一步。**run 结束时还有命令在跑**(取消 / 超时结束的 run 的命令没来得及停)⇒ 杀之前**先等**它们结束:最长一只 `rpcTimeoutMs` 墙(缺省 30 秒;等待算在这次提交的快照时限里,上传拿到的是剩下的);等不到的还要断开仍开着的 exec 套接字(那条命令的调用方读到 `aborted`),**照样保存**,并出一行 `workspace_snapshot_commands_cut`(`space` / `inFlight` / `waitedMs` / `killed`)。**没能确认进程停下**(杀进程那一步失败,或几次复查后仍有活进程;有没有命令在飞、等到与否都一样)⇒ **不保存**:一行 `workspace_snapshot_failed`(`stage: upload`、`code: command_in_flight`),`latest` 不动 —— 可能还在变的工作区不当 `latest`。**确认不了的四种形**(工作区不存,下一轮恢复的是上一份 —— 本轮的工作在下一轮看不到;7.131.x 在「run 结束时没有命令在飞」时照存):镜像里经 `sudo` / setuid 程序起的、以别的 uid 跑的进程(`kill -9` 没有权限,例如 `sudo -u postgres` 起的库进程)· 不可中断睡眠(D 态)的进程 · `podSpecPatch` 打开了 `shareProcessNamespace`(同 pod 别的容器的进程也在 `/proc` 里)· 镜像里没有 `tr`、而容器保活进程不是 1 号进程(pod 起动先跑包源钩子的那一形:认不出保活进程、把它杀掉,容器随之结束)。**出路**:k8s 车道镜像以一个非 root 用户跑、不经 sudo / setuid 起常驻进程,带 `tr`,不开 `shareProcessNamespace`;按 `workspace_snapshot_failed{stage: upload, code: command_in_flight}` 告警;不需要跨轮保留的部署设 `WORKSPACE_PERSIST=off`。杀进程脚本只在「该杀的都不活了」时退出 0(僵尸进程算死:容器保活进程不收尸)。从 destroy 开始起这只执行环境不再收新命令(工具调用报沙箱已销毁)。`command_in_flight` 的拒只留给停驻挂起(run 还活着,撕裂的归档才是真风险)。此前 run 结束时只要还有命令在飞,快照就整份被拒(`workspace_snapshot_failed`,`stage: upload`),`latest` 不动,下一轮恢复上一份 —— 取消 = 丢掉这一轮的工作。
@@ -2728,8 +2746,10 @@ ssh / adb / local-docker 三条车道的流式调用同读这一条律:没有车
2728
2746
  读数每次读时求值(起服读数 —— 秘密座答复、补丁判官 —— 加当前生效配置的 `sandboxGit` 块),不是起服时存下的一份。起服时若是 k8s 多租户部署,日志一行 `credential_plane_sandbox_git`(`state` / `reason` / `why`,记的是那一刻的读数)。
2729
2747
  - **代理哪些 origin**:有效配置的 `sandboxGit` 块 —— 控制台按部署登记册投影,只在本 worker 自己的拉取上出现(配了 `SEMA_WEB_ADMIN_WORKER`);没有 env 旋钮,本地配置源(`CONFIG_PROVIDER=local`)没有这一块。
2730
2748
  块随有效配置的每次提交热换代、不签重启(刷新拍 / 迟到起服 / LKG 起服都一样;中心凭证换人降档时随整份中心配置撤下):能力位下一次读就是新代;k8s 每只 pod 在建起时读一次平面,
2731
- 已在跑的 pod 的路由表不变 —— 删 / 加 origin 对新 pod 生效。块判不过契约(重复 / 超过 16 项 / 非规范 origin / `connectUrl` 不是 https / 不是对象)⇒ 当作没有任何 origin(`no_origins`;`@sema-agent/settings-schema` 消费契约 §12.8 的读法),
2732
- 一行 warn `web_admin_sandbox_git_refused`(`issues` 逐条坐标 + 契约拒句,不回显值),同一份候选的其余配置照常落地;块换代一行 info `web_admin_sandbox_git { version, origins }`。
2749
+ 已在跑的 pod 的路由表不变 —— 删 / 加 origin 对新 pod 生效。块判不过契约(重复 / 超过 16 项 / 非规范 origin / `connectUrl` 不是 https / 不是对象)⇒ 当作没有任何 origin(平面的第九条前提 `no_origins` 不成立;`@sema-agent/settings-schema` 消费契约 §12.8 的读法),
2750
+ 一行 warn `web_admin_sandbox_git_refused`(`issues` 逐条坐标 + 契约拒句,不回显值;`effect` 句里「the sandbox git plane reads no_origins」说的就是这第九条前提),同一份候选的其余配置照常落地;块换代一行 info `web_admin_sandbox_git { version, origins }`。
2751
+ **能力位上读到哪个词是另一回事**:`credentialPlane.sandboxGit.reason` 取判序里**第一个**不成立的前提(九词闭集与判序见契约附录 B);本版第三条前提(取值口的 worker 凭据,见下「平面仍恒 `off`」)恒不成立,
2752
+ 所以坏块、撤块、没有块的部署上 `GET /v1/capabilities` 读到的都是 `no_caller_credential`(或更前的词)—— `no_origins` 要前八条前提都成立才会成为读数。
2733
2753
  - **旋钮**:`K8S_SANDBOX_BROKER_IMAGE`(代理容器的镜像;建议填本引擎自身镜像 —— 代理是本包里固定路径的程序;不设 ⇒ `broker_image_unset`)。Role 在 §9.3c 的 `create` 之外还要 `patch`(改写租约;租约 Secret 只用这两个;本版平面恒关,不用先给)。
2734
2754
  - ⚠️ **平面仍恒 `off`**:一处读口还没接线 —— 取值口的 worker 凭据;有效配置的 `sandboxGit` 块已接(上一条)。读数停在 `no_caller_credential`(或更前的词),没有 pod 会挂上代理。
2735
2755
  run 行的身份来源不是接线点:读的是受理时记在 run 行上的那一份身份(连同登录令牌的 scope 与化身号 `sub_inc` 一起记下;化身号超过 64 字符不记,一行 `run_identity_claim_unrecorded`)。
@@ -2742,9 +2762,9 @@ ssh / adb / local-docker 三条车道的流式调用同读这一条律:没有车
2742
2762
  - **判据只有一条**:沙箱车道(e2b / k8s / local-docker)守地板 ⇔ 读地板模块服务这条车道 ∧ `SANDBOX_READ_FLOOR` 不是 `off`。主机 / ssh / adb / device 恒不守(这些环境约束不了进程)。
2743
2763
  **本版模块不服务任何车道** ⇒ 三条沙箱车道的 `keepsReadFloor` 仍是 `false`,沙箱里的起动与上一版逐字同。
2744
2764
  - **看一处**:起服行 `remote_exec_enabled` 的 `keepsReadFloor`,沙箱车道上再加 `sandboxReadFloor`:`unserved`(模块还没服务这条车道)/ `off`(部署关了)/ `kept`(守)。
2745
- - **守地板的车道上会多出的两种拒起**(都是 `spawn_error`,命令一个进程都不起):① 这只沙箱守不住整张表 —— 首句 `read floor unavailable on this sandbox (<原因词>)`,原因词九选一
2746
- (`arch` / `user_notif` / `addfd_send` / `uid_zero` / `writable_system_path` / `binary_digest` / `relay_silent` / `relay_identity` / `provenance`),句中点名表首行与 `SANDBOX_READ_FLOOR=off`;
2747
- ② 一次不带地板的引擎起动(流式执行面,以及本服务里几处替引擎干活的内部调用方今天都不带)—— 句子点名车道与 `SANDBOX_READ_FLOOR=off`。
2765
+ - **守地板的车道上会多出的一种拒起**(`spawn_error`,命令一个进程都不起):这只沙箱守不住整张表 —— 首句 `read floor unavailable on this sandbox (<原因词>)`,原因词九选一
2766
+ (`arch` / `user_notif` / `addfd_send` / `uid_zero` / `writable_system_path` / `binary_digest` / `relay_silent` / `relay_identity` / `provenance`),句中点名表首行与 `SANDBOX_READ_FLOOR=off`。
2767
+ 一次起动不带地板 = 那张表一行都没有(引擎的读法:run 的每次起动都带它那一刻的表;不带的是表无行的 run,或不属于哪条 run 的起动 —— 引擎的执行门、语言服务器、本服务的 diff 拉取)⇒ 照常经监督者起,没有要拒的路径。
2748
2768
  - **不想守**:`SANDBOX_READ_FLOOR=off`。块如实答 `false`;读拒表非空的 run 照旧每 run 一条运维通告 `config.read_deny_floor_unkept`,部署设了 `READ_DENY_FLOOR_REQUIRED=true` 时准备期拒(同码)。
2749
2769
 
2750
2770
  ### 9.3j 沙箱三车道:项目包 —— 没有 git 的项目作为工作区的种子(7.142.0 起)
@@ -2754,7 +2774,7 @@ ssh / adb / local-docker 三条车道的流式调用同读这一条律:没有车
2754
2774
  - **形**:gzip 压缩的 POSIX tar(`tar czf` 的形),顶层就是工作根(不要包装目录);只放普通文件、目录、相对符号链接(落点按包自己的树解 —— 经过包里别的链接 —— 仍在包内);不放顶层 `.git`(有 git 的项目走 git 引用)与顶层 `attachments`(附件的落点);
2755
2775
  不经符号链接写子路径;每条路径 ≤ 4095 字节、每段名字 ≤ 255 字节;pax 扩展头只收 `path` / `linkpath` / `size` 与元数据(时间、属主、扩展属性、ACL —— 解出来不带),稀疏文件等改变内容读法的扩展拒(`entry_type`);
2756
2776
  展开 ≤ 512 MiB、≤ 200,000 个对象(条目 + 路径隐含的目录);压缩体照附件上限 `ATTACHMENT_MAX_BYTES`(缺省 32 MiB)。项目身份照旧:包顶的 `.sema/project` 记号由引擎读,服务不读不写。
2757
- - **上传**:`POST /v1/attachments?name=<名>`,`Content-Type: application/vnd.sema.project-bundle` → 201 `{ id, sha256, sizeBytes }`。读不出项目包 ⇒ `400 attachment.bundle_invalid`,体 `reason`(`not_archive` / `unsafe_path` / `entry_type` / `reserved_path` / `duplicate_path` / `expanded_too_large` / `too_many_entries`)与 `path`(被拒条目的落点;放不下的超长路径不带)。
2777
+ - **上传**:`POST /v1/attachments?name=<名>`,`Content-Type: application/vnd.sema.project-bundle` → **201** `{ id, name, mime, sha256, sizeBytes }`(与普通附件同一个状态码、同一个体形:`POST /v1/attachments` 受理一律 201)。读不出项目包 ⇒ `400 attachment.bundle_invalid`,体 `reason`(`not_archive` / `unsafe_path` / `entry_type` / `reserved_path` / `duplicate_path` / `expanded_too_large` / `too_many_entries`)与 `path`(被拒条目的落点;放不下的超长路径不带)。
2758
2778
  配了 `ATTACHMENT_MIME_ALLOWLIST` 的部署要把这个 mime 加进去,否则 415。
2759
2779
  - **绑定**:提交体 `attachmentIds: [id]`(首次提交;重试带同一 id 幂等)。车道不隔离 ⇒ `409 attachment.bundle_lane_not_isolated`(先读能力位 `executionLane.isolated`);会话已有种子 ⇒ `409 attachment.bundle_seed_fixed`;
2760
2780
  这只上传已绑在别的会话上 ⇒ `409 attachment.bound_elsewhere`(一只上传只给一个会话 —— 这条对所有附件都成立)。
@@ -2866,19 +2886,24 @@ plan 起步的任务把计划交给人之后停在 `plan_review` 门上。`POST
2866
2886
  - **`SENSITIVE_WRITE_PATTERNS` 拒的是文件工具,shell 写同一路径不被它拒绝,走普通审批问**:守卫集对 Write / Edit / NotebookEdit 按路径做 deny;shell 命令写同一条路径(重定向、`tee`、`cp` …)是一条普通审批问,和这次运行里别的 shell 写同一条出路(人答;`auto` 下交分类器),不会因为命中这个集而被拒。
2867
2887
  要让某类 shell 写在你的部署里被问或被拒,管 shell 本身:`commandPolicy` / `AUTONOMY` / `APPROVAL_REQUIRE`。
2868
2888
 
2869
- ### 9.4d 设置层的路径规则读在它那一层的根:`settings.permissions.deny` / `ask` 的带层条目(7.141.0 起)
2889
+ ### 9.4d 设置层的路径规则读在它那一层的根:`settings.permissions` 三桶的带层条目(deny / ask 7.141.0 起,allow 7.143.0 起)
2870
2890
 
2871
2891
  - **一句规则**:一条路径规则的 `/` 读在它所在那一层的根上;`~/`、`//`、相对形在每一层读法相同。端从设置文件读规则时可以把出处层一起发:
2872
2892
  `"deny": ["Read(/secret/**)", { "rule": "Read(/secret/**)", "layer": "userSettings" }]` —— 纯串是请求自己写的规则(读在任务根,读法不变);带层条目的 `layer` 是设置来源词
2873
- (`userSettings` · `projectSettings` · `localSettings` · `flagSettings` · `policySettings` · `foreignProjectSettings`)。能力位 `settings.permissions.layers` 为真才发。
2893
+ (`userSettings` · `projectSettings` · `localSettings` · `flagSettings` · `policySettings` · `foreignProjectSettings`)。deny / ask 见能力位 `settings.permissions.layers` 为真才发,allow 见 `settings.permissions.layeredAllow`。
2874
2894
  - **只有 user 层的 `/` 形随层变**:`userSettings` 里的 `Read(/secret/**)` 守的是**执行环境的配置根**下的 `secret`(host 车道 = 本服务的 `SEMA_CONFIG_DIR`,缺省 `~/.sema`),
2875
2895
  不再守任务根下的 `secret` —— 与参照实现读 user 层 `/` 的位置相同。别的来源与纯串读在任务根。要守每个项目自己的目录:把规则放进项目层,或写相对形 `secret/**`(任务根下任意深度);固定位置写 `//` 绝对形。
2876
2896
  - **沙箱车道的配置根**:运维把 `SEMA_CONFIG_DIR` 列进 `E2B_SANDBOX_ENV` / `K8S_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV` 转进沙箱 ⇒ 读那个值;没转 ⇒ 执行环境 home 下的 `.sema`
2877
2897
  (e2b = `/home/user/.sema`;k8s / local-docker / ssh / adb / device 要 `REMOTE_EXEC_HOME_DIR` 声明 home)。两样都没有 ⇒ 这样的条目 422,`detail.problems[].reason:"home_unresolvable"`(与 `~/` 缺 home 同一个词);
2878
- 出路:转 `SEMA_CONFIG_DIR`、声明 `REMOTE_EXEC_HOME_DIR`,或把规则写成 `//` 绝对形。转进去的值不是绝对路径 ⇒ 422 `unsupported.path_base`。
2898
+ 出路:转 `SEMA_CONFIG_DIR`、声明 `REMOTE_EXEC_HOME_DIR`,或把规则写成 `//` 绝对形。转进去的值不是绝对路径 ⇒ deny / ask 422 `unsupported.path_base`(allow 桶见下面「allow 桶同读」)。
2879
2899
  - **层不改准入**:多租部署上的准入档只按这次请求验过的凭据推(9 节速查 `below_trust_floor` 一行);条目上的层只决定规则读在哪。
2880
2900
  - **回滚 / 混版副本**:本版之前的服务对带层条目是桶门 `bad_entry`(新提交 400,续跑 409),不会静默读丢;端见位才发,所以没升级的服务上它照旧只收到纯串。
2881
- - **已知限制**:`allow` 桶只收纯串(带层条目 400 `bad_entry`);user 层的 `/` 形 allow 今天仍按任务根放行。
2901
+ - **allow 桶同读**:`"allow": [{ "rule": "Read(/secret/**)", "layer": "userSettings" }]` 放的是配置根下 `secret` 的读,不是任务根下的;别的层与纯串照旧读在任务根。
2902
+ **配置根下的写不归 allow 放**:配置根在每条车道上都在引擎缺省写保护表的 `.sema` 行上(声明的 `SEMA_CONFIG_DIR` 及其以下全命中,转进沙箱的是同名 env 的原值、同一只目录;缺省 `~/.sema` 与沙箱车道缺省的 `<home>/.sema` 带 `.sema` 段),
2903
+ 那里的写照问(卡上 `mandate: "write_protection"`;规则答不了这一类问),带层条目、纯串、`//` 绝对形的 allow 都清不掉 —— user 层 `Edit(/secret/**)` 放不掉配置根下 `secret` 的写。
2904
+ 两处不归这一行:引擎数据根(壳布局 `~/.sema/engine-data`)坐在配置根下时,根里的写按根内的相对路径判;整表替换写保护表(`WRITE_PROTECTED_TABLE_REPLACE`)去掉这一行时随之不在。
2905
+ 一条一员按位置交给引擎(命中的 `positions` = settings 表的下标);配置根读不出时与 deny / ask 同一个 422;转进沙箱的值不是绝对路径时受理口不拒、引擎开跑时拒这条 run
2906
+ (`config.invalid_permission_rules`,issue `unsupported.path_base`)。任何一层的 deny / ask 照旧压过它。
2882
2907
 
2883
2908
  ### 9.5 Web search:两车道优先级、per-request `settings.webSearch`、错误形
2884
2909
 
@@ -3170,9 +3195,11 @@ WebSearch 工具的 `tool_result` 错误文本,回到模型的对话里,**不是
3170
3195
 
3171
3196
  **日志不是原形面**:运维结构化日志里 `engine_notice` 行的 `message` 与会话流上同一条通知的 `message` 过同一只脱敏(认出凭据形才换成 `«redacted…»`,没有凭据形的句子逐字节原样;会话流行另有 1000 字上限,日志行不截),`detail` 里的规则文本(`permission.allowed_tool_not_granted` 的 `entries[].entry`、`permission.rule_row_dropped` 的 `rows[].rule`)是条目的脱敏原值、`detail` 其余字段逐字 —— 同一条规则文本在 `message` 与 `detail` 里的脱敏是同一只脱敏器;已知边界:引擎把条目里的零宽字符写成 `\u200b` 转义,夹零宽字符的凭据在 `message` 里(日志行与会话流行同)认不出,机读位的规则文本是在原串上认的、不受此限。
3172
3197
 
3173
- **自由文本的已知漏形**(两形;结果、账本回放、流式与同步体、运维日志行同读):① 复合凭据名(`api_key` / `apikey` / `api-key` / `access_token` / `refresh_token` / `id_token` / `private_key` / `x-amz-signature` / `x-amz-security-token` / `x-amz-credential`)在自由文本 `名=值` 形上没有名字锚,值只有自身形状被别的臂认出(`ghp_…` / `sk-…` / `AKIA…` 这类)时才遮 ——
3174
- 同一批名字在 JSON 字段与 URL 参数位上照遮,`password` / `passwd` / `pwd` / `secret` / `client_secret` 在赋值符两侧零空白的 `名=值` 形上、值是以字母数字起头的字面量时照遮(`$VAR` / `%s` / `<占位>` / 表达式 / `None` 一类不算值);② 强锚名紧贴在前一枚凭据的尾巴上(`-----END PRIVATE KEY-----password=…`;PGP 收尾栅栏、PuTTY 的 `Private-MAC:` 行、AWS 键 id 同形;中间无分隔)时首遍不遮
3175
- (流式 done 帧、同步体、日志行明文;run 行与账本回放读面再过一遍,被遮)。两形都与旁边的私钥块无关,块本身照遮;要靠脱敏挡住的凭据别以这两形写出(MIGRATION「自由文本脱敏的两处已知漏形」)。
3198
+ **自由文本的强锚读法**(7.143.0 起;此前下面 ① ② 两形漏遮,见 MIGRATION「自由文本脱敏的名字面」):① 复合凭据名(`api_key` / `apikey` / `api-key` / `access_token` / `refresh_token` / `id_token` / `private_key` / `x-amz-signature` / `x-amz-security-token` / `x-amz-credential`)与 `password` / `secret` / `client_secret` 同档 ——在自由文本 `名=值` / `名: 值` 的**声明形**(名字带引号、值带引号,或赋值符两侧零空白)上是名字锚,值是以字母数字起头的字面量时遮(`$VAR` / `%s` / `<占位>` / 表达式 / `None` 一类不算值;`passwd` / `pwd` 任何赋值形都是锚);赋值符两侧带空白的不带引号形(`api_key = abc`)不是声明形、不算锚。`token` / `key` / `auth` / `sig` / `signature` / `sas` 在自由文本里是常用词,只在 JSON 字段与 URL 参数位上是名字锚,自由文本里只有值自身形状被别的臂认出(`ghp_…` / `sk-…` / `AKIA…` 这类)时才遮。代价:关键字参数透传(`f(api_key=api_key)`)的变量名、源码里对象字面量的值(`apiKey: "…"`,名字大小写不敏感)也换成记号;这些名字后面本来由值自身形状认出的值(`API_KEY=sk-…`)记号是强锚值的 `«redacted»`,不再是 `«redacted:<种类>»`(与 `password=` 同读)。
3199
+ ② 名字的左界止于前一枚已认出的凭据(或可读的结构块)的终点:强锚名紧贴在前一枚凭据的尾巴上(`-----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` 行。
3200
+ ③ 不带引号的值里面的赋值照问:`api_key=abc,password="my secret value"` 里两个值都遮(此前外层不带引号的值把里面的赋值藏住,里面那一只不认)。
3201
+ **升级前已落库的行**:回放读面对 run 行与 `text` / `reasoning` / `done` 行再过一遍脱敏 ⇒ 按本读法遮;`tool_end` 行回放读面不再过(只剥媒体字节)⇒ 升级前写下的 ① ② 两形照旧明文,升级后新写的行照遮。
3202
+ **仍不遮的一形**:名字整个落在前一枚已认出凭据的值里 —— 以字母数字收尾的令牌后面紧贴强锚名(`ghp_…password=<值>`,中间无分隔;令牌的形状把名字吞了进去)—— 令牌照遮、`<值>` 明文,各出口同读;中间有分隔(空白 / `,` / `;` / 换行)照遮。名字按整名认:前面与字母数字以 `_` / `-` 连写的(`DB_PASSWORD=` / `foo-api_key=`)是另一个名字、不算锚;只有收尾栅栏、没有起始栅栏的 `…KEY-----password=` 同理(孤立的收尾栅栏不是凭据,不立名界)。要靠脱敏挡住的凭据别以这一形写出(MIGRATION「自由文本脱敏的名字面」)。
3176
3203
 
3177
3204
  ### 9.7 bake-runner(构建宿主守护进程)的 env(7.103.0 起成文)
3178
3205
 
@@ -279,6 +279,17 @@ export const REBIND_LEGS = [
279
279
  residualKey: ["handle", "seq"],
280
280
  why: "未投递通知 = 183 [3265] A 类资产(消息体随信箱同迁;本表无 `scope` 可读列)",
281
281
  },
282
+ {
283
+ leg: "mailbox_consumed#scope_key",
284
+ table: "mailbox_consumed",
285
+ kind: "bulk-rebind",
286
+ action: "bucket-rebind",
287
+ columns: ["scope_key"],
288
+ matchColumn: "scope_key",
289
+ encoding: "verbatim",
290
+ residualKey: ["handle", "seq"],
291
+ why: "信箱的已收窗(core 7.61.0):盒事实,**必须与 `mailbox` 同迁** —— 漏迁 ⇒ 收编后新身份下的盒忘了已收过的记录,同一记录的重放被收成第二条;本表无 `scope` 可读列(与 mailbox_message 同形)",
292
+ },
282
293
  {
283
294
  leg: "background_agent#scope",
284
295
  table: "background_agent",
@@ -356,6 +367,17 @@ export const REBIND_LEGS = [
356
367
  residualKey: ["peer"],
357
368
  why: "记忆同步的 per-peer 游标",
358
369
  },
370
+ {
371
+ leg: "agent_memory_engine_sync_partition#scope",
372
+ table: "agent_memory_engine_sync_partition",
373
+ kind: "bulk-rebind",
374
+ action: "bucket-rebind",
375
+ columns: ["scope"],
376
+ matchColumn: "scope",
377
+ encoding: "memory-scope",
378
+ residualKey: [],
379
+ why: "记忆同步中心的层分区化身(随它那一层的条目走:条目改了键而化身留在旧键上 = 新键的分区下一轮另生一只化身,已同步的客户端全被拒)",
380
+ },
359
381
  {
360
382
  leg: "agent_memory_engine_push_queue#scope",
361
383
  table: "agent_memory_engine_push_queue",
@@ -196,7 +196,9 @@ export interface AuditAskInput {
196
196
  approvalId: string;
197
197
  /** 出处 run 的 taskId(`origin.taskId`,不是投递连接的)。 */
198
198
  taskId: string;
199
- sourceTaskId?: string;
199
+ /** 发起这一问的会话(引擎 `AskRequest.sourceSessionId`,同名同值)。落盘键是账本既有的 `sourceTaskId`(持久面不随引擎改名),只在账本边界映射:
200
+ * 写盘 {@link ledgerRecordToDisk},读盘 {@link readLedgerLine} 的 `ask` 臂。 */
201
+ sourceSessionId?: string;
200
202
  sessionId?: string;
201
203
  owner: string | null;
202
204
  toolName: string;
@@ -237,7 +239,7 @@ export type AuditSettleInput = (AuditSettleBase & {
237
239
  */
238
240
  export interface AuditLateSettleInput {
239
241
  /** checkpoint 锚①:被决议那条 durable 行的 sessionId(`/v1/approvals/:sessionId/decide` 的那把;
240
- * parked 子代形下 = **子会话 id**,与 ask 行的 `sourceTaskId` 同值)。 */
242
+ * parked 子代形下 = **子会话 id**,与 ask 行的 `sourceSessionId` 同值)。 */
241
243
  sessionId: string;
242
244
  /** checkpoint 锚②:`pendingAction.toolCallId`(tool_approval 门恒在场;别的审批门型可缺席)。 */
243
245
  toolCallId?: string;
@@ -402,8 +404,8 @@ export declare class FileApprovalAskAuditStore implements ApprovalAskAuditSink,
402
404
  private static callAnchorKey;
403
405
  /**
404
406
  * [ref]:把一条铸造行放进反查索引。
405
- * 会话轴登记 `sessionId` 与 `sourceTaskId` **两把**:parked 后台子代的 checkpoint 上
406
- * `cp.sessionId` 是**子会话 id**(= ask 行的 `sourceTaskId`),而 ask 行的 `sessionId` 是**宿主**
407
+ * 会话轴登记 `sessionId` 与 `sourceSessionId` **两把**:parked 后台子代的 checkpoint 上
408
+ * `cp.sessionId` 是**子会话 id**(= ask 行的 `sourceSessionId`),而 ask 行的 `sessionId` 是**宿主**
407
409
  * 会话(铸造点取的是 `origin.sessionId`,继承链闭包携宿主身份)—— 只登记一把会让委派子代那条腿
408
410
  * 的迟到决议恒反查不到。顶层 run 上两者同值,登记去重后是同一把。
409
411
  */
@@ -475,7 +477,7 @@ export declare class FileApprovalAskAuditStore implements ApprovalAskAuditSink,
475
477
  * **留存轮转**(审批账本留存律的审计账半场;律一处成文在 `approval-ask-retention.ts`,判据见头注「体量与保留」)。维护 tick 是唯一调用方。
476
478
  *
477
479
  * 读的是**文件**(前世 + 本进程两段账,行序即语义序),不是构造时冻结的前世快照:本进程铸的问要按它此刻在盘上的样子判「结了没有」。
478
- * 判「结了」= 它挂靠的格(铸造行的 `taskId` / `sessionId` / `sourceTaskId`)都不在 `keepAnchors`(还能动的东西的挂靠集,由清扫从 run 账与
480
+ * 判「结了」= 它挂靠的格(铸造行的 `taskId` / `sessionId` / `sourceSessionId`)都不在 `keepAnchors`(还能动的东西的挂靠集,由清扫从 run 账与
479
481
  * 后台子代账列出)∧ {@link judgeOrphan} 此刻不欠它答案 —— 停驻 run / 能动子代的问(窗到期转 durable、迟到结算还要这组前置事实)、活卡
480
482
  * (铸了没决)与批了腿没闭的问多老都留。删的单位是**整问**:
481
483
  * · 一问的最后动静 = 它所有记录(铸造 / 决议 / 收敛 / 迟到结算)里最晚的时刻;早于 cutoff ∧ 不欠答案 ⇒ 这一问的全部行删;