@sema-agent/server 7.142.0 → 7.143.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 (258) hide show
  1. package/MIGRATION.md +442 -0
  2. package/USAGE.md +41 -24
  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 +124 -25
  9. package/dist/approval-card.js +103 -23
  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/governance-seams.d.ts +26 -41
  17. package/dist/boot/governance-seams.js +61 -28
  18. package/dist/boot/late-binds.d.ts +7 -4
  19. package/dist/boot/late-binds.js +3 -1
  20. package/dist/boot/leader.js +2 -4
  21. package/dist/boot/memory-consolidation.d.ts +0 -8
  22. package/dist/boot/memory-consolidation.js +3 -3
  23. package/dist/boot/reapers.js +5 -4
  24. package/dist/boot/resolve-spec.d.ts +1 -1
  25. package/dist/boot/resolve-spec.js +8 -8
  26. package/dist/boot/runner-deps.d.ts +10 -7
  27. package/dist/boot/runner-deps.js +5 -17
  28. package/dist/boot/shutdown.d.ts +9 -0
  29. package/dist/boot/shutdown.js +16 -2
  30. package/dist/boot/stage-02-web-admin-pull.js +3 -0
  31. package/dist/boot/stage-03-store-backend.d.ts +4 -1
  32. package/dist/boot/stage-03-store-backend.js +1 -2
  33. package/dist/boot/stage-04-stores.d.ts +5 -2
  34. package/dist/boot/stage-04-stores.js +3 -5
  35. package/dist/boot/stage-05-execution-env.d.ts +5 -2
  36. package/dist/boot/stage-06-runners.d.ts +5 -2
  37. package/dist/boot/stage-06-runners.js +1 -3
  38. package/dist/boot/stage-07-capability-layer.d.ts +5 -2
  39. package/dist/boot/stage-07-capability-layer.js +5 -1
  40. package/dist/boot/stage-08-reapers.d.ts +5 -2
  41. package/dist/boot/stage-09-leader.d.ts +5 -2
  42. package/dist/boot/stage-10-http-server.d.ts +5 -2
  43. package/dist/boot/stage-10-http-server.js +9 -1
  44. package/dist/boot/stores.d.ts +5 -2
  45. package/dist/boot/stores.js +7 -7
  46. package/dist/boot/web-admin.d.ts +5 -1
  47. package/dist/boot/web-admin.js +31 -19
  48. package/dist/capabilities/hands-lane.d.ts +9 -39
  49. package/dist/capabilities/hands-lane.js +8 -13
  50. package/dist/capabilities/mcp-probe.d.ts +3 -2
  51. package/dist/capabilities/scenarios.d.ts +11 -1
  52. package/dist/capabilities/scenarios.js +1 -0
  53. package/dist/capabilities/skills.d.ts +1 -17
  54. package/dist/capabilities/skills.js +0 -8
  55. package/dist/capability-facts.d.ts +8 -8
  56. package/dist/capability-facts.js +4 -4
  57. package/dist/card-void.d.ts +6 -9
  58. package/dist/config-catalog.js +1 -1
  59. package/dist/config-dir-trust.js +58 -29
  60. package/dist/config-invariants.d.ts +7 -1
  61. package/dist/config-invariants.js +7 -0
  62. package/dist/config-provider.d.ts +10 -0
  63. package/dist/config-provider.js +15 -4
  64. package/dist/config-root-migration.d.ts +13 -0
  65. package/dist/config-root-migration.js +99 -0
  66. package/dist/config-types.d.ts +5 -5
  67. package/dist/config.d.ts +4 -3
  68. package/dist/config.js +1 -1
  69. package/dist/delegated-session-owner.d.ts +5 -3
  70. package/dist/delegated-session-owner.js +1 -1
  71. package/dist/device-ws-protocol.d.ts +1 -0
  72. package/dist/device-ws-protocol.js +2 -1
  73. package/dist/env-model-table-gate.d.ts +53 -0
  74. package/dist/env-model-table-gate.js +53 -0
  75. package/dist/fleet/fleet-bus.d.ts +28 -52
  76. package/dist/fleet/fleet-bus.js +35 -100
  77. package/dist/fleet/fleet-reconciler.d.ts +4 -4
  78. package/dist/fleet/fleet-reconciler.js +1 -1
  79. package/dist/fleet/subagent-tail-bus.d.ts +14 -11
  80. package/dist/fleet/subagent-tail-bus.js +15 -28
  81. package/dist/hooks/hook-runner.js +28 -7
  82. package/dist/host-decision.d.ts +54 -20
  83. package/dist/host-decision.js +42 -14
  84. package/dist/http/admission.d.ts +1 -1
  85. package/dist/http/admission.js +7 -3
  86. package/dist/http/approval-sweeps.js +11 -8
  87. package/dist/http/caller-trust.d.ts +1 -1
  88. package/dist/http/durable-run-leg.js +2 -2
  89. package/dist/http/engine-refusal.d.ts +16 -0
  90. package/dist/http/engine-refusal.js +17 -0
  91. package/dist/http/memory-compliance-failure-reply.d.ts +1 -3
  92. package/dist/http/memory-compliance-failure-reply.js +4 -6
  93. package/dist/http/owner-access.d.ts +11 -0
  94. package/dist/http/owner-access.js +13 -0
  95. package/dist/http/resume-legs.d.ts +21 -7
  96. package/dist/http/resume-legs.js +71 -49
  97. package/dist/http/resume-restatement.d.ts +39 -0
  98. package/dist/http/resume-restatement.js +25 -0
  99. package/dist/http/route-ctx.d.ts +35 -10
  100. package/dist/http/route-ctx.js +1 -1
  101. package/dist/http/routes/approvals-assistant.d.ts +9 -1
  102. package/dist/http/routes/approvals-assistant.js +142 -31
  103. package/dist/http/routes/capabilities.js +11 -2
  104. package/dist/http/routes/memory-policy.d.ts +2 -2
  105. package/dist/http/routes/memory-policy.js +1 -1
  106. package/dist/http/routes/notify-wake.js +23 -25
  107. package/dist/http/routes/rules.js +48 -13
  108. package/dist/http/routes/runs.js +9 -8
  109. package/dist/http/routes/session-memory-sync.d.ts +34 -6
  110. package/dist/http/routes/session-memory-sync.js +27 -10
  111. package/dist/http/routes/session-sync.js +13 -20
  112. package/dist/http/routes/sessions.d.ts +2 -2
  113. package/dist/http/routes/sessions.js +22 -23
  114. package/dist/http/routes/tasks.js +27 -49
  115. package/dist/http/server.d.ts +13 -7
  116. package/dist/http/server.js +2 -2
  117. package/dist/http/stored-body-replay.js +15 -4
  118. package/dist/http/wire-types.d.ts +17 -7
  119. package/dist/leader/leader.js +5 -2
  120. package/dist/leader/wire.d.ts +7 -1
  121. package/dist/leader/wire.js +6 -5
  122. package/dist/leg-facts.d.ts +37 -0
  123. package/dist/leg-facts.js +15 -0
  124. package/dist/memory-bundle-engine.d.ts +1 -4
  125. package/dist/memory-bundle-engine.js +2 -2
  126. package/dist/memory-operator-faces.d.ts +30 -66
  127. package/dist/memory-operator-faces.js +17 -88
  128. package/dist/memory-scope-stores.d.ts +38 -40
  129. package/dist/memory-scope-stores.js +51 -36
  130. package/dist/memory-scope.d.ts +0 -9
  131. package/dist/memory-scope.js +0 -9
  132. package/dist/memory-sync-client.js +14 -3
  133. package/dist/memory-sync.d.ts +20 -4
  134. package/dist/memory-sync.js +23 -4
  135. package/dist/model-plane-face.d.ts +13 -7
  136. package/dist/model-plane-face.js +2 -1
  137. package/dist/observability/fail-open.d.ts +20 -4
  138. package/dist/observability/fail-open.js +17 -4
  139. package/dist/observability/run-terminal.js +4 -0
  140. package/dist/orchestration/workflow-completion-inbox.d.ts +5 -19
  141. package/dist/orchestration/workflow-completion-inbox.js +0 -12
  142. package/dist/orchestration/workflow-notify-journal.d.ts +3 -2
  143. package/dist/orchestration/workflow-park-decision.d.ts +8 -3
  144. package/dist/orchestration/workflow-park-decision.js +5 -3
  145. package/dist/org-delivery-gate.d.ts +11 -0
  146. package/dist/org-delivery-gate.js +19 -1
  147. package/dist/parked-decide.d.ts +13 -9
  148. package/dist/parked-decide.js +7 -7
  149. package/dist/parked-revive-surface.js +1 -1
  150. package/dist/peer-inbound-face.d.ts +127 -0
  151. package/dist/peer-inbound-face.js +158 -0
  152. package/dist/permission-rule-list.d.ts +13 -9
  153. package/dist/permission-rule-list.js +7 -3
  154. package/dist/plugins/approval-ask-store-file.js +18 -6
  155. package/dist/plugins/approval-ask-store-memory.js +8 -6
  156. package/dist/plugins/approval-ask-store-sql.d.ts +28 -13
  157. package/dist/plugins/approval-ask-store-sql.js +30 -11
  158. package/dist/plugins/checkpoint-store-sql.d.ts +4 -4
  159. package/dist/plugins/checkpoint-store-sql.js +9 -7
  160. package/dist/plugins/file-history-store-sql.d.ts +24 -18
  161. package/dist/plugins/file-history-store-sql.js +24 -19
  162. package/dist/plugins/file-run-store.d.ts +29 -0
  163. package/dist/plugins/file-run-store.js +97 -8
  164. package/dist/plugins/imported-layer-root-retirement.d.ts +82 -0
  165. package/dist/plugins/imported-layer-root-retirement.js +96 -0
  166. package/dist/plugins/local-checkpoint-store.d.ts +1 -1
  167. package/dist/plugins/local-checkpoint-store.js +2 -2
  168. package/dist/plugins/local-session-store.d.ts +6 -2
  169. package/dist/plugins/local-session-store.js +8 -2
  170. package/dist/plugins/mailbox-recipient-lifecycle.d.ts +9 -31
  171. package/dist/plugins/mailbox-recipient-lifecycle.js +4 -0
  172. package/dist/plugins/mailbox-store-sql.d.ts +12 -8
  173. package/dist/plugins/mailbox-store-sql.js +76 -7
  174. package/dist/plugins/memory-sync-store-pg.d.ts +15 -0
  175. package/dist/plugins/memory-sync-store-pg.js +21 -0
  176. package/dist/plugins/memory-sync-store-tidb.d.ts +2 -0
  177. package/dist/plugins/memory-sync-store-tidb.js +13 -1
  178. package/dist/plugins/one-time-migrations.d.ts +32 -2
  179. package/dist/plugins/one-time-migrations.js +106 -3
  180. package/dist/plugins/org-delivery-quota-store.d.ts +1 -1
  181. package/dist/plugins/org-delivery-quota-store.js +2 -12
  182. package/dist/plugins/park-commit-watch.d.ts +1 -1
  183. package/dist/plugins/permission-rule-store-file.d.ts +10 -1
  184. package/dist/plugins/permission-rule-store-file.js +15 -2
  185. package/dist/plugins/permission-rule-store-sql.d.ts +31 -1
  186. package/dist/plugins/permission-rule-store-sql.js +35 -2
  187. package/dist/plugins/remote-env-device.d.ts +8 -11
  188. package/dist/plugins/remote-env-device.js +6 -3
  189. package/dist/plugins/remote-env-k8s.d.ts +37 -24
  190. package/dist/plugins/remote-env-k8s.js +50 -17
  191. package/dist/plugins/retention-store-sql.d.ts +2 -2
  192. package/dist/plugins/sandbox-read-floor.d.ts +5 -7
  193. package/dist/plugins/sandbox-read-floor.js +2 -7
  194. package/dist/plugins/sql-errors.d.ts +10 -1
  195. package/dist/plugins/sql-errors.js +13 -0
  196. package/dist/plugins/store-backend.d.ts +23 -28
  197. package/dist/plugins/store-backend.js +9 -1
  198. package/dist/plugins/store-contracts.d.ts +29 -5
  199. package/dist/plugins/store-contracts.js +4 -1
  200. package/dist/plugins/task-attachment-store.d.ts +2 -2
  201. package/dist/plugins/task-attachment-store.js +37 -52
  202. package/dist/plugins/tool-result-store-sql.d.ts +9 -8
  203. package/dist/plugins/tool-result-store-sql.js +23 -2
  204. package/dist/plugins/wiring-section-codec.d.ts +32 -0
  205. package/dist/plugins/wiring-section-codec.js +55 -0
  206. package/dist/plugins/workspace-fill.d.ts +1 -1
  207. package/dist/rule-entry-list.d.ts +3 -2
  208. package/dist/rule-entry-list.js +5 -1
  209. package/dist/rules-consent.d.ts +28 -5
  210. package/dist/rules-consent.js +40 -28
  211. package/dist/run-local.js +9 -5
  212. package/dist/runs.d.ts +12 -8
  213. package/dist/runs.js +6 -7
  214. package/dist/runtime-governance.d.ts +1 -1
  215. package/dist/server-secret-env.d.ts +4 -0
  216. package/dist/session-error-wire.d.ts +12 -0
  217. package/dist/session-error-wire.js +6 -0
  218. package/dist/session-incarnation.d.ts +1 -1
  219. package/dist/session-notify-door.d.ts +106 -0
  220. package/dist/session-notify-door.js +93 -0
  221. package/dist/session-sync.d.ts +9 -14
  222. package/dist/session-sync.js +5 -5
  223. package/dist/session-workspace.d.ts +3 -13
  224. package/dist/session-workspace.js +0 -9
  225. package/dist/standing-answer-grant.d.ts +5 -0
  226. package/dist/standing-answer-grant.js +10 -0
  227. package/dist/stores-sql/session-policy-store-sql.d.ts +4 -2
  228. package/dist/stores-sql/session-policy-store-sql.js +3 -13
  229. package/dist/task-mcp.d.ts +1 -1
  230. package/dist/task-settings.d.ts +11 -8
  231. package/dist/task-settings.js +31 -11
  232. package/dist/tool-approval.d.ts +52 -34
  233. package/dist/tool-approval.js +66 -57
  234. package/dist/trace/core-keyset-guard.d.ts +11 -8
  235. package/dist/trace/engine-notice-wire.d.ts +1 -1
  236. package/dist/trace/engine-notice-wire.js +3 -1
  237. package/dist/trace/exit-seat.d.ts +7 -1
  238. package/dist/trace/exit-seat.js +1 -0
  239. package/dist/trace/ledger-sink.d.ts +2 -2
  240. package/dist/trace/model-segment.d.ts +55 -25
  241. package/dist/trace/model-segment.js +101 -33
  242. package/dist/trace/project.d.ts +7 -4
  243. package/dist/trace/project.js +6 -0
  244. package/dist/trace/redact.d.ts +61 -9
  245. package/dist/trace/redact.js +1490 -468
  246. package/dist/trace/self-minted-wire.d.ts +510 -38
  247. package/dist/trace/self-minted-wire.js +165 -17
  248. package/dist/trace/wire-projection-faces.d.ts +1 -1
  249. package/dist/trace/wire-projection-faces.js +1 -1
  250. package/dist/verifier-pause-card.d.ts +33 -5
  251. package/dist/verifier-pause-card.js +15 -10
  252. package/dist/web-admin-client/apply-effective.d.ts +12 -16
  253. package/dist/web-admin-client/apply-effective.js +90 -10
  254. package/dist/workspace-space.d.ts +19 -3
  255. package/dist/workspace-space.js +8 -4
  256. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -1
  257. package/package.json +3 -2
  258. package/wire-changes/server@7.143.0.json +1167 -0
package/MIGRATION.md CHANGED
@@ -7,6 +7,448 @@
7
7
  > ⚠️ 完整清单在仓库根 `CHANGELOG.md`——它**不随 npm tarball 出包**(本文件随包)。看完整迁移窗的
8
8
  > 权威姿势是源码 tag diff:`git diff v<旧>..v<新>`(每版都推 `v<版本>` tag);npm 包页也镜像 CHANGELOG。
9
9
 
10
+ ## 7.143.0 —— `wiring_manifest` 帧的容器段在 run 店 File 孪生按会话块账本去重(持久面;体量;回滚地板 = 本版) · env 两槽 × 目录:本地根首份管模型面目录的首次写入决定文档(K0)、`planeRefusalCode` 只答最新候选、leader 路由模型经服务表解、env cheap 槽悬空一次性 warn(配置面;行为收紧) · 🔴 部署治理的装配相容性门改在部署 MCP 服务器 / A2A 对端物化之后判:`mcp` / `a2a` 锁、合规否决 `mcp_servers` / `a2a_peers` 撞上部署自带的服务器 / 对端 ⇒ 起服拒(行为收紧,fail-closed;部署面) · 运维审批队列行带族名 `kind`、被扣的跨会话来信作 `peer_inbound` 行进审批席(wire 加性;🔴 消费方先按 `kind` 分支) · k8s 车道:停驻时沙箱从未起过的停驻,续跑改作一次新连接(空间开 ⇒ 恢复 `latest`);打包过的停驻快照对象不在 ⇒ 一行 `workspace_latest_dangling`(`lane: "park"`)+ 空目录;更正 7.142.0 节第 9 条尾的已知限制(行为修复;运维面日志加性) · 订正 7.141.0 节第 1 条的「谁受伤」:结构化 warn 只覆盖本服务写的那一行,网关密钥被拒收时引擎另说一行裸 `console.warn`(引擎已知限制),logger 接上后的那一行受 `LOG_LEVEL` 约束(文字订正;零行为变更) · 🔴 SQL 后端 `task_attachment` 加一列生成列 `seed_session_id` + 唯一索引 `uq_att_seed`:一只会话一只种子改由唯一键判,已有种子会话上的并发提交恒答 409(行为修复;DDL 升级前先加列;wire 零变化) · 续跑请求可重述对端表:四道续跑门收提交体同键的 `mcpServers` / `a2aPeers`,缺席 = 冻结读法(wire 加性;行为面;壳宜每次续跑送当前表) · 🔴 活流模型文本过同一只脱敏器:`text_delta` / `reasoning_delta` 逐块帧只放已判定的字节,拼接 = 段末帧正文 = `done.result`,段末余下的字节一帧补齐(wire 值语义;安全) · 会话读面(视图 · 记忆状态 · 记忆抹除 · 头探针 · 事件流 · 执行放置)对「文件后端上行事实没有落下、存档又读不出」的会话,从 500 `corrupt_session` 改答 404 `not_found.session`(与对非属主、对不存在的会话同一只;原故障进运维日志 `session_owner_unreadable`;行为面;应答格状态变化) · 中心 MCP 声明撞同一工具命名空间前缀:同步提交腿从 `200` + `failed` 体改 `409 config.mcp_server_name_collision`,`GET /v1/capabilities/mcp` 从无码 `500` 改同码同状态同句(wire 码与状态变;按 HTTP 状态分支的提交方看一眼) · 🔴 持久 ask 店车道上 askId 的身份含这一问展示给人的动作(工具名 + 入参摘要):同一 run 同一腿里复用调用 id、换了入参或换了工具的那一问出自己的卡(安全轴;行为收紧;不透明 `askId` 值变;零 DDL) · 🔴 `POST /v1/approvals/:sessionId/decide` 的 `boundCallId` 与 `boundInputHash` 两位必带,缺哪位 400 `decide_requires_binding` 点名哪位;不再拿当前停驻行补;`remember_requires_binding` 退役(BREAKING 行为面;安全轴;决断方回显队列行上的那一对) · 订正 7.142.0 节第 18 / 19 条的两处读数说法:坏块 / 撤块时能力位读 `no_caller_credential`(`no_origins` 是平面的第九条前提);强锚名紧贴前一枚凭据尾巴那一形在 7.142.0 上于 `GET /v1/runs/:id/events` 回放的 `tool_end` 行与活流逐块帧明文(文字订正;零行为变更;这一形本版起已修,见第 41 条) · 会话记忆同步触发口的 409 `conflict.session_memory_unobserved` 体恒带 `reason`:最近一条已结 run 的记录里没有记忆观测 ⇒ 点名它的记录里有什么、句子给出路;🔴 它配了记忆而没挂上(`memoryless`)⇒ 由 200 空报告改 409 `memory_not_mounted`;读集仍只读最近一条已结 run(wire 加性;行为面;三句拒句换形 / 新句;零 DDL;部署零动作) · 🔴 引擎 7.61.0:升级前结清在途审批、排空旧 worker,mod 包按契约 7 重建(部署面;BREAKING) · 🔴 审批卡的委派子代出处键 `sourceTaskId` 改名 `sourceSessionId`(wire BREAKING;零别名) · 通告码 `context.image_removed` 改名 `context.media_removed`、TaskOutput 退役(wire 码改名;策略名单) · 设备车道放置围栏 + 协议修订 4 `symlink_loop`(行为面;wire 加性) · SQL 后端新表 `mailbox_consumed`(起服自建) · `DB_BACKEND=local` 工具结果按 TTL 回收(行为变化) · blob 上传哈希不符一律 400、嵌入方两只店换新组形(wire 值变化;类型面 BREAKING) · 记忆同步:别的 scheme 里的 URL userinfo 也判凭据(行为收紧) · 设置导入:用户层 `/` 形路径规则留在文件里(行为收窄)、导入预览的用户层不收 `root`(wire BREAKING) · 🔴 SQL 后端 `approval_ask` 加一列 `decided_via`、决定体加性键 `decidedVia`、能力位 `approvalDecidedVia`(wire 加性;DDL 升级前先加列) · `verify:true` 任务的上限封住实现腿与各轮修复的合计,花光 ⇒ 不再修复(行为收窄;wire 加性) · 订正 7.139.0 节第 24 条 ③:没配包装的 worktree 子代仍由本服务拒(文字订正;零行为变更) · 嵌入方包 `resolveSpec` 的包装须返回同一只对象(嵌入方行为面 BREAKING) · 验证器自己停驻的那张卡改由引擎的作废动词结算、会话钉随之放掉,编排(leader)路径同样作废(行为面;修复;部署零动作;订正 7.141.0 节第 31 条的两条已知限制) · `settings.permissions.allow` 的条目也可带出处层,user 层的 `/` 形 allow 读在执行环境的配置根(wire 条目型放宽 + 能力位;端见位才发;收掉 7.141.0 节第 15 条「已知限制」的 allow 那一半) · 后台子代一个 id:面板 / 完成通知 / 孙代嵌套 / 审批归属都读注册表行 id;升级与回滚前各让停驻的后台子代结清(行为面;部署面) · Runner 之外复活的后台子代(停驻赎回 / 操作员续跑)的完成走委派树根会话的会话通知门(行为修复;wire 零新键) · 🔴 记忆同步:中心每个层分区一只化身、同步口改到 `/v2/memory/sync/:scope`、本机游标原样携带化身(引擎三处硬破坏;中心库新表起服自建;部署:两端同版 + 停驻层删游标) · 🔴 抹除与会话记忆状态答整个部署(`409 memory.erasure_store_unjudged` 与会话自助抹除体的 `scope` 退役;嵌入方类型面 BREAKING) · 🔴 规则店:引擎 7.61.0 之前从用户层设置导入的、从 `/` 写起的全局放行起服即退役(按 add;deny / ask 不动;一次性迁移 M36,每次起服幂等;持久数据面;行为收紧;部署零动作) · 本地导入载体的 deny / ask 在 prepare 当场落店、预览成员带 `outcome`,放行仍在 redeem 落;收紧写不进 ⇒ 整批 503、放行扣下(行为面;wire 加性) · 自由文本脱敏的取证读法按出处读:一枚凭据不拿自己上一轮的读数当证据、私钥收尾栅栏落在不完整读数里两读取并、SSH2 块之后紧跟的孤立收尾之前读作无头体、取证级数界 32 到顶整段遮(行为面;安全;脱敏产物可能变;运维面多一个 fail-open 标签) · 🔴 审批卡的断连重放帧与活铸帧同事实:问行卡信封加一座 `liveFacts`,断连重放的 `approval_request` 带活铸帧顶层的同一组卡外事实;本版之前铸的行重放帧带 `liveFactsUnknown: true`(安全轴;wire 加性;存形加性,零 DDL;🔴 多副本先停光旧副本再让本版接流量) · 🔴 自由文本脱敏的名字面:复合凭据名(`api_key=` 一族)在自由文本的声明形里是名字锚、名字的左界止于前一枚已认出凭据的终点(紧贴形首遍即遮、各出口同读)、不带引号的值里面的赋值照问(行为面;安全;脱敏产物变;带这些赋值的命令不再给常驻放行;部署零动作) · 运行时依赖 `@modelcontextprotocol/server` 2.4.0(npm 安装多一只包;部署零动作)
11
+
12
+ 1. **`DB_BACKEND=local`:run 账本里每条 run 的装配清单(`wiring_manifest` 行)按会话去重存 —— 容器段存进每会话一只块账本 `<数据根>/run-blobs/<会话>.jsonl`,行里只留引用(持久数据面:存形加性,存量不迁移;wire 与读面零变化;回滚地板 = 本版;部署零动作)**:
13
+ 旧形:`runs/<taskId>/events.jsonl` 里每条 run 的 `wiring_manifest` 行带一整份清单(名册、MCP、技能……;实测每行约 37–44 KB,同会话每条 run 一份,占 run 账本约八成)。
14
+ 新形:这一行里值为对象 / 数组的段换成 `{ "$blob": "sha256:<64 hex>" }`,段体按会话在 `run-blobs/<会话>.jsonl`(一行一只 `{ digest, body }`;文件名 = 会话 id 的可读前缀 + `-` + 会话 id 的 sha256)里只存一份 —— 没变的段跨 run 共用一块,变了的段多一块;
15
+ 标量段与 `eventId` 照旧在行里。`GET /v1/runs/:id/events`、run 事件流、会话 MCP 面板与其余经 run 店的读口读到的仍是整帧,逐字段与以前相等(同一段的嵌套键序取首次存下的那一份)。
16
+ 升级前写下的整帧行不改写、照常读;从本版起新写的行是引用形 —— 账本只向前缩,旧会话随删会话回收。删会话先删这只块账本、再删各 run 目录;
17
+ 块账本删不掉(权限 / 挂载)⇒ 这次删会话整次答错误、一样都不删,修好之后重试即可。
18
+ 块账本缺行 / 坏行(断电丢尾、手工改动)⇒ 引用它的那一帧读不出:读口不交出这一帧(其余帧照交;不交出缺段的清单),计数 `fail_open_total{tag="server.run-store.corrupt-record-skipped"}` 每帧每进程一次;
19
+ 块账本整份读不出(权限 / 挂载)与 `events.jsonl` 读不出同一条路:`RUN_STORE_STRICT_HYDRATE=on` 拒启并点名路径,否则起服原样抛。
20
+ 直读 `events.jsonl` 的离线工具(取证脚本、按行统计)从本版起读到引用形:要整帧就经 `GET /v1/runs/:id/events` 读,或按 `run-blobs/<会话>.jsonl` 的 `{ digest, body }` 自行换回。
21
+ **谁需要**:长会话的 local 车道(run 账本的盘面、起服堆与读盘随 run 数线性长,其中约八成是重复的清单)。**谁受伤**:直读 `events.jsonl` 的离线工具(见上);回滚到上一版(见下)。
22
+ **补偿**:直读账本的离线工具改经 `GET /v1/runs/:id/events` 读整帧,或按 `run-blobs/<会话>.jsonl` 的 `{ digest, body }` 自行换回;回滚只伤回放面,再升回本版即复原。**升级前做**:无。**回滚地板 = 本版**:本版写过的数据根回到上一版,旧二进制读得出账本但不认引用 —— 回放面(`GET /v1/runs/:id/events`、事件流重连)上本版写下的 run 的清单段显示成 `{ "$blob": … }`,
23
+ 会话 MCP 面板对这些 run 读成缺席;活 run 不受影响(旧二进制整帧写)。再升回本版即复原(块账本还在)。旧二进制删会话不删 `run-blobs/` 里的块账本 —— 留下的只占空间。
24
+
25
+ 2. **🔴 本地配置源:一份管模型面的 `config.d/models.json`,在设了 `MODEL_ID` 时只有配置根的 `config.env-models.json` 读作 `replaced` 才顶替 env 模型表;升级的本地根由起服时的一次性迁移铸这份确认(行为收紧;升级之后才写下目录的根要写一份确认;中心配置源零变化)**:
26
+ — **谁受影响**:`CONFIG_PROVIDER=local`(含 `run-local` / `sema` CLI)、设了 `MODEL_ID`(env 模型表已声明;严格名册布防 `CONFIG_REQUIRE_ROSTER=true` 时 env 表是占位,不算)、`config.d/models.json` 有启用条目的配置根。中心配置源不守卫:中心发布的目录照旧整表顶替 env 表。
27
+ — **配置根**就是放 `config.d/` 的那个目录(`CONFIG_LOCAL_DIR` → `AGENT_DATA_DIR` → `~/.ai-agent`)。决定文件 `<配置根>/config.env-models.json`,两种写法 `{"envModels":"replaced"}`(目录顶替 env 表)/ `{"envModels":"kept"}`(env 表保留);
28
+ 缺席读作 `kept`;读不懂(坏 JSON / 坏形 / 读不出)从不读成确认(每份内容每进程一行 warn `config_env_models_unreadable`)。
29
+ — **升级时自动做的**:本版第一次起服(读本地配置之前、目录信任判官之后)在每个本地配置根上跑一次契约包的迁移:独占创建运行记录 `<配置根>/config.migration.SS-M11.json`;决定缺席且 `config.d/models.json` 此刻管模型面 ⇒
30
+ 铸 `{"envModels":"replaced"}` —— 升级前「目录顶替 env 表」的读法原样保住:info `config_dir_migrated` 记一条收据,env 模型已声明时再一行 warn `config_env_models_replaced_by_migration` 点名从此被顶替的 env 模型。
31
+ 根不在 ⇒ 以 `0700` 建出(只放运行记录,不建 `config.d`)。运行记录在场之后迁移不再跑,决定缺席就是 `kept`。
32
+ — **升级后要动手的几形**:① 升级时还没有管模型面的目录、之后才写下第一份的根(含新装):这份目录的模型面被拒 ——
33
+ `/health.modelRefusals.plane` = `config.catalog_replaces_env_models`,`GET /v1/models.modelPlane` = `{ "servedTable": "env", "catalog": "refused", "planeRefusalCode": "config.catalog_replaces_env_models" }`,warn `web_admin_model_plane_refused` 点名路径与出路;
34
+ env 模型继续服务,同一份目录的其余配置照落。要让目录顶替 env 表:写 `config.env-models.json` = `{"envModels":"replaced"}`(下一个刷新拍或 `POST /v1/admin/config/refresh` 生效,不用重启),或撤掉 `MODEL_ID` / `MODEL_CHEAP_ID`。
35
+ ② 升级时 `models.json` 读不出(权限 / I/O)或确认写不成:warn `config_env_models_mint_failed` 带手工恢复句,之后同 ① 处置。③ 只读配置根:运行记录建不成 ⇒ 不铸,每次起服重跑、每进程一行 warn `config_dir_migration_failed`;手写的决定照常生效。
36
+ ④ 运行记录读不懂 ⇒ warn `config_env_models_marker_unreadable`,按已跑处置。
37
+ — **撤销**:要回到 env 模型,删掉决定文件或写 `{"envModels":"kept"}`,再**重启**:运行中的进程在下一个刷新拍对目录判组拒(`planeRefusalCode` 在场),但组拒不降档 —— 在服的那一代目录照旧服务,env 模型在下一次起服才回来。删掉运行记录再起服 ⇒ 迁移重跑,决定缺席即重铸。
38
+ — **目录信任**:根上这两份文档与 `config.d` 下的文档同一套判据(属主只能是本服务的用户 / root / `CONFIG_LOCAL_OWNER_UID`,不得组 / 他人可写,常规文件,≤ 16 MiB),不过 ⇒ 拒启 `[config.dir_untrusted]`;`config.d` 缺席时也判到根。手写决定文件后核一下模式位(`chmod go-w`)。
39
+ — **跳版**:迁移随契约包下一个大版本删除;在那之后才第一次升到本系列的根没有迁移替它铸确认,管模型面的目录被拒,按 ① 处置。
40
+ — **回滚**:回到上一版 ⇒ 决定文件与运行记录不被读(目录照旧整表顶替 env 表);两份文件留在盘上,再升回本版照读。
41
+ **谁需要**:同一个本地配置根上既声明 env 模型、又放目录的部署(目录顶不顶替 env 模型由盘上一份文件说)。**谁受伤**:如上 ① – ③。**出路**:如上。
42
+
43
+ 3. **`GET /v1/models.modelPlane` 加性键 `planeRefusalCode`;`catalog: "refused"` 的模型面那一半改读最新到达的候选(wire 加性;wire 值语义收窄;部署零动作)**:
44
+ `planeRefusalCode` 在场 ⇔ 最新到达的目录候选被模型面组拒,值 = 那一拒的码(契约包 `MODEL_PLANE_REFUSAL_CODES` 之一);`catalog: "refused"` 而本键缺席 ⇔ 最新到达的候选被候选判官整拒(看同一应答的 `candidateRejected`)。
45
+ 此前 `catalog` 的模型面那一半读 `/health.modelRefusals.plane`:更新的候选到了而没走到模型面那一判(被候选判官整拒,或过了判官、却在换装时被拒)时,旧候选的组拒仍让 `catalog` 答 `refused`;
46
+ 本版起这一形改按在服的表答(`adopted` / `absent`),那一份换装失败只在 `/health` 的世代账(target ≠ applied)里看得见(与「装配期被引擎整份拒收」同一形,本版仍没有座)。`/health.modelRefusals.plane` 的读法不变(最近一份被判过的模型面的组拒判决,不随更新的到达撤)。
47
+ 同一只路由判官的码可能同时出现在 `planeRefusalCode` 与 `refusalCode`:前者指最新到达的候选目录,后者指此刻在服的那张表。
48
+ **谁需要**:读 `modelPlane` 判「刚写 / 刚发布的目录为什么没落」的壳与控制台。**谁受伤**:把 `catalog: "refused"` 读成「上一份被拒的目录还挂着」的消费方。
49
+ **出路**:用 `planeRefusalCode` 与 `candidateRejected` 分两种拒;要问「最新候选落没落」读世代账。**升级前做**:无;**回滚**:键消失,`catalog` 回到旧读法。
50
+
51
+ 4. **leader 路由腿:没有 `MODEL_ROUTER_ID` 时在服务表里解 `classifier` 角色(classifier → summarize → default),不再钉 `MODEL_CHEAP_ID` 的名;目录采纳后悬空的 env 角色绑定每名一行 warn(行为面;运维面日志加性;部署零动作或目录里一行绑定)**:
52
+ 纯 env 部署:路由腿仍跑在 `MODEL_CHEAP_ID` 的模型上(env cheap 槽绑 `summarize`),没有 cheap 槽仍跑主模型 —— 零变化。采纳了目录的 leader 部署:路由腿跑目录自己的 `classifier` 绑定,没有就 `summarize`,都没有就目录缺省(此前钉的 env cheap 名不在目录表里)。
53
+ 要路由跑便宜模型:在目录 `roles` 里把 `classifier`(或 `summarize`)绑到一条便宜条目、激活 `flash` 档,或设 `MODEL_ROUTER_ID=<目录条目名>`(按名在服务表里解,与此前同)。
54
+ leader 的每一只 Runner(修复 / 路由 / worker 腿)从本版起带档位表,角色在档位展开的视图里解,与主 runner 同答:激活了档位的部署上,worker 腿里按档位缺省解的角色(委派子代 / 压缩)从此解到档位绑定的条目(此前落到缺省模型)。
55
+ 同版:目录的模型面落地时,env 角色底里目录没覆写的角色(`MODEL_CHEAP_ID` 绑的 `subagent` / `summarize`)绑的名不在这份目录(档位展开后)里 ⇒ 每个名每进程一行 warn `config_env_role_binding_dangling { model, env, roles, engineNotice: "model.role_defaulted" }`;
56
+ 选模不变(引擎读作未设、沿角色链回落,每个任务一条 `model.role_defaulted`)。出路同上:在目录里绑这些角色,或让目录里有一条叫这个名的条目。**回滚**:路由腿回到 `MODEL_ROUTER_ID || MODEL_CHEAP_ID`,warn 行消失。
57
+ **谁需要**:采纳了目录的 leader 部署 —— 路由腿跑在这份目录里真有的条目上(此前钉的 env cheap 名不在目录表里);运维在目录落地那一刻读到哪一条 env 角色绑定悬空,而不是只在每个任务的 `model.role_defaulted` 里零散读到。
58
+ **谁受伤**:采纳了目录、目录里既没绑 `classifier` 也没绑 `summarize`、又没激活 `flash` 档、却指望路由腿跑 `MODEL_CHEAP_ID` 那只便宜模型的 leader 部署 —— 路由腿改跑目录缺省模型;激活了档位的 leader 部署 —— worker 腿里按档位缺省解的角色改解到档位绑定的条目。
59
+ **补偿**:在目录 `roles` 里绑 `classifier`(或 `summarize`),或设 `MODEL_ROUTER_ID=<目录条目名>`;warn 行点名悬空的名与它绑的角色。
60
+
61
+ 5. **🔴 部署治理的装配相容性门改在部署 MCP 服务器 / A2A 对端物化之后判:`LOCKED_CONFIG_KEYS` 的 `mcp` / `a2a` 锁、合规否决 `mcp_servers` / `a2a_peers`,撞上部署自带的服务器 / 对端(`config.d/mcp.json` 或中心下发)⇒ 起服拒(行为收紧,fail-closed;部署面;落在这四形里的部署要动手)**:
62
+ — **此前**:USAGE「部署治理三声明」写明的两形启动期拒 —— `LOCKED_CONFIG_KEYS=mcp` + 部署自己配了 MCP 服务器、合规档位禁 `mcp_servers` + 部署自己配了 MCP 服务器 —— 在 HTTP 服务上从未发生:
63
+ 判据在部署 MCP 基线读进来之前就跑完了,于是这两形照常起服,而每条任务在引擎门口被拒 —— 锁形答 `400 config.locked_key`(不带 `mcpServers` 的任务也一样),否决形落 `200` 失败体(`config.compliance_denied`),部署自带的服务器零拨号。
64
+ 同族的 `LOCKED_CONFIG_KEYS=a2a` / 否决 `a2a_peers` + 中心下发的 A2A 对端同样只在任务腿上撞拒(判据此前不看这两项)。`run-local` / `sema` 的 MCP 两形早已在启动时拒(零变化)。
65
+ — **本版**:四形都在起服时拒(进程退出码 1;句子点名那把锁 / 那条否决与两条出路),与 `toolPolicy` 锁、单用户形禁 `web_fetch` 同一只判据、同一个出口。判据的时点挪到部署基线物化之后:`toolPolicy` 锁与单用户禁 `web_fetch` 两形照旧拒启,
66
+ 只是拒的那一刻在起服序列里稍晚(仍早于场景装配、reaper 与 HTTP 监听)。`enabled: false` 的条目、引用的 env 名在本服务上缺席而被跳过的条目不算「配了」。
67
+ — **升级前做**:落在四形里的部署二选一 —— 去掉锁 / 否决(`LOCKED_CONFIG_KEYS` 里删 `mcp` / `a2a`,`COMPLIANCE_ADDITIONAL_DENIES` 里删 `mcp_servers` / `a2a_peers`),或去掉部署自带的服务器 / 对端(`config.d/mcp.json` 的条目、中心 `mcp` / `a2a` 域的条目)。
68
+ 中心侧之后再加服务器 / 对端仍是 restart-to-apply:落进这四形的那一次变更让滚动重启的新副本起服拒(旧副本照跑),出路同上。
69
+ **谁需要**:配了锁或合规档位的部署 —— 配错的组合在起服时点名,而不是起得来、任务全失败。**谁受伤**:落在四形里、服务器 / 对端只挂在某几个场景上、其余场景的任务今天照常服务的部署 —— 升级后整台起不来。
70
+ **出路**:同「升级前做」;要锁住请求自带的 MCP / A2A 而保留部署自带的,本服务没有这一形(引擎的锁守的是同一个字段)。**回滚**:HTTP 服务回到上一版 ⇒ 四形照旧起服、任务腿照旧撞拒(`run-local` / `sema` 上 MCP 两形在上一版就拒启,回滚不改)。
71
+
72
+ 6. **🔴 运维审批队列的行带族名 `kind`(闭集 `checkpoint` | `peer_inbound`);被扣下、等收信方审的跨会话来信作 `peer_inbound` 行进 `GET /v1/approvals` 的 `pending[]` 与 `/v1/approvals/stream`,经同一决断口 `POST /v1/approvals/:id/decide` 批 / 拒(wire 加性;🔴 按「每一行都是停驻门」写死的消费方要先按 `kind` 分支;部署零动作)**:
73
+ `pending[]` 的每一行与流上的 `pending` / `resolved` 帧从本版起带 `kind`:停驻门的行 `kind: "checkpoint"`,其余键与上一版逐字同;新族 `peer_inbound` = 收信会话的权限档对等判扣下的跨会话来信
74
+ (发信方的权限档与收信方对不上,成因 `mode-mismatch` / `no-mode-asserted` / `invalid-mode-attestation`),行键 `id` / `recipientSessionId` / `lane` / `cause` / `content` / `sentAt` / `heldAt` / `fromSession?` / `fromName?` / `fromScope?`,**没有** `sessionId` / `toolCallId` / `riskDescriptor` / `deadline`。
75
+ 决断口 `:id` = 行的 `id`(`peer_inbound:` 前缀,不透明),体只收 `{ decision: "approve" | "deny" }`(其余键 400 点名拒),答 `200 { kind, id, outcome }` / `404 not_found.approval` / `503 state.peer_decision_unrecorded`(新码,可重试)。
76
+ `resolved` 帧按族:`checkpoint` 族带 `sessionId` + `toolCallId`(同上一版,另加 `kind`),`peer_inbound` 族带 `id`。`synced.count` 数 `pending` 车道全部行(两族同计)。
77
+ 部署策略扣下的信(`CROSS_SESSION_INBOUND=hold`)**不进**审批席(组织策略不由收信方逐封越过);只有收信方本人列得出、决得了(operator 不多看)。行是**本副本**进程内投影(跨副本看不到,同 `livePending`);有序关停把仍被扣的信以 `expired` 结清并回执发信方。
78
+ **谁需要**:用跨会话通信、收发两方权限档会不一致的部署 —— 此前被扣的信只能等到期,收信方没有任何面能放行或拒绝。
79
+ **谁受伤**:按 `pending[]` 每一行都是停驻门写死的消费方(读 `row.sessionId` 去 `/decide`、读 `riskDescriptor`、按 `(sessionId, toolCallId)` 消 `resolved`)—— 一行 `peer_inbound` 到来时读到缺席。
80
+ **出路**:先按 `kind` 分支;不认识的 `kind` 跳过该行、不要拿它去 `/decide`(契约 §0.11)。**升级前做**:无;**回滚**:`kind` 键与 `peer_inbound` 行消失,被扣的信回到只能等到期。
81
+
82
+ 7. **k8s 车道:停驻时沙箱从未起过(什么都没打包)的停驻,续跑改作一次新连接 —— 空间开着就等上一条提交、按 `latest` 恢复;打包过的停驻快照对象不在 ⇒ 一行 `workspace_latest_dangling`(`lane: "park"`)+ 空目录;更正 7.142.0 节第 9 条尾的「已知限制」(行为修复;运维面日志加性;部署零动作)**:
83
+ 此前:run 在首次碰到沙箱之前就停驻(pod 还没起)⇒ 停驻快照 id 下没有对象;续跑走停驻车道、取包得 404,读作「空快照」⇒ 新 pod 上的空目录,不读空间的 `latest`。
84
+ 空间开着(`WORKSPACE_PERSIST=space`,缺省)时这条腿的 run 末提交随后覆写 `latest` ⇒ 此前各条 run 的改动从下一条 run 起不在了,且不报警。
85
+ 同一个 404 也是「打包过的停驻快照被桶的生命周期规则 / 手工删掉」的读数 —— 两件事一样静默。
86
+ 现在:停驻快照 id 自己说清打包没打包 —— `kata-<记号>-…` = 打包过(此前铸下的 id 都是这一族);`nopack-<记号>-…` = 停驻时没有 pod、什么都没打包,桶里从不放对象。
87
+ — `nopack-…` 的续跑 = 一次新连接(与 run 首次碰沙箱同一条走法),这种 id 下不发任何取包请求:空间开 ⇒ 等上一条提交落定、读 `latest`,在 ⇒ 恢复(不播种),不在 ⇒ 空目录(播种);
88
+ 空间关 ⇒ 空目录(播种,同此前);没有这只会话 ⇒ 空目录 + 一行 `workspace_space_unavailable`(此前续跑不出这行);读住客 / 提交围栏 / `latest` 失败 ⇒ 续跑 `connect_failed`(与连接同句,引擎按续跑重试)。
89
+ — `kata-…` 的续跑照旧取包;对象不在 ⇒ 一行 `workspace_latest_dangling`(`lane: "park"` / `key` / `sessionId` / `effect`),与空间 `latest` 悬空同一行名、同一条规则(响亮、可继续),这条腿从空目录开始(播种);
90
+ 空间开着时它的 run 末提交成为新的 `latest`。空间那一臂的同一行多一个字段 `lane: "space"`(其余字段不变)。
91
+ **升级窗**:升级之前铸下的停驻 id 都是 `kata-…` 形(那时还不区分):升级时仍停驻着、而停驻前 pod 还没起的 run,续跑读到的是「打包过、对象不在」⇒ 一行 `workspace_latest_dangling`(`lane: "park"`)+ 空目录 ——
92
+ 工作区与此前同,多一行;空间开着时它的 run 末提交仍覆写 `latest`(只这一窗;升级之后的停驻不再如此)。
93
+ **谁需要**:k8s 车道上空间开着、用耐久审批(停驻 → 续跑)的会话 —— 在首次碰沙箱之前就停驻的 run(首个工具就要审批、计划审阅)续跑后看得到此前各条 run 的文件,run 末提交不再把它们冲掉;想看见停驻快照被清走的运维。
94
+ **谁受伤**:按 `workspace_latest_dangling` 告警的运维:停驻快照的悬空也报这一行;升级窗里停驻着的那几条 run 续跑时各报一行(见上)。
95
+ **出路**:告警按 `lane` 分两类;升级窗那几行按 `key` 里的会话 id 对账即可。**回滚**:回到上一版 ⇒ `nopack-…` 的停驻在旧二进制上续跑取包得 404 ⇒ 旧形(空目录、不报警);`kata-…` 不受影响。
96
+ **更正(7.142.0 节第 9 条尾,文书)**:那一条的「已知限制」—— 空间开着时停驻在先、续跑不恢复 `latest`、run 末提交覆写此前改动且不报警 —— 自本版起不再成立(本条即其修法);7.142.0 节原文不改(已发节字节不动)。
97
+
98
+ 8. **更正(7.141.0 节第 1 条「谁受伤」,文书;零行为变化)**:该条说起服自有凭据值被引擎拒收「此前是一行裸 stderr 文本,现在是日志器的一行 `warn` `own_credential_value_not_withheld { name, reason }`」—— 说的是**本服务自己**写的那一行,这一半不改。没写全的是另两件事(亲读:真主进程起服,7.141.0 标签与本版读数同形):
99
+ ① **网关密钥**(`MODEL_API_KEY` / `ANTHROPIC_API_KEY`,起服交给模型适配器的那一个)被引擎判「不是凭据值」(占位词 / 不足 8 字符 / 一个字符重复)时,进程输出里除这一行结构化 `warn`,**还有一行引擎写的裸文本**:
100
+ `a credential the engine was handed is … — it is used as given, but a variable holding the same string is not withheld from a spawned child (said once per value; the value is not repeated)`。
101
+ 引擎在适配器构造口收下网关密钥的那一刻对同一个值「每进程每值说一次」,而这个收值口没有通告席(适配器的配置没有通告位),引擎落它自己的 `console.warn` —— 引擎文档把这一形列为已知限制(无席的收值口共四个:适配器构造 / 请求路由读 / 对端拨号 / 直建蒸馏座)。
102
+ 本服务不是这一行的写者、也无席可接,不在引擎外面吞 `console`;引擎不收的自有凭据变量(例如 `SERVICE_AUTH_TOKEN`,本版真主进程读数)被拒时只有结构化那一行。两行说的是同一个值同一件事,都不带值;本服务这一行点名变量,引擎那一行只说哪一类。
103
+ ② 结构化行走日志器:起服第一步(日志器未建)的那一行当拍写 stderr,不受 `LOG_LEVEL` 约束;日志器接上之后(模型目录换代并进的条目的值被拒)受 `LOG_LEVEL` 约束 —— `error` 级读不到,而此前的裸 `console.warn` 无条件出。
104
+ **谁需要**:读这件事告警的运维。**谁受伤**:① 以为 7.141.0 之后这件事在 stderr 里只剩 JSON 行的日志管线 / 告警规则(把「不是 JSON 的行」当异常、或按行解析 stderr 的)—— 网关密钥是占位词时仍会读到引擎那一行(7.141.0 与本版同形,不是新回归);② `LOG_LEVEL=error` 的部署:换代并进条目的值被拒那一行读不到。
105
+ **补偿**:给网关密钥一个真值(至少 8 字符、不是占位词)—— 引擎与本服务的两行一起消失;按事件名 `own_credential_value_not_withheld` 与字段 `name` 筛本服务的行;机读事实读 `GET /v1/capabilities` 的 `inherit_env.own_credential_values_withheld`(一个值没按值扣 ⇒ false,不随日志级走);`LOG_LEVEL` 至少 `warn`。
106
+ 引擎一侧那一行等引擎给收值口一席、或让声明口拒收的值记作已说,本服务再接;在此之前它是引擎的,本版不改。7.141.0 节原文不改(已发节字节不动)。
107
+
108
+ 9. **🔴 SQL 后端:`task_attachment` 加一列生成列 `seed_session_id` 与唯一索引 `uq_att_seed` ——「一只会话一只种子」改由表上的唯一键判;已有种子的会话上的并发提交恒答 `409 attachment.bundle_seed_fixed`,不再撞死锁 / 锁等待成店错(行为修复;DDL:升级前先加列,先升库再升副本;wire 零变化;订正 7.142.0 节第 10 条的「已知限制」)**:
109
+ — **此前**:SQL 两方言判「这只会话有没有种子」用的是一只事务里的锚读:按 id 序加锁读全部未绑的项目包与本会话的项目包,再加锁读这只包自己的行。两只并发事务的取锁序可以反向,引擎挑一只输家回滚,
110
+ 店最多重来 4 次,被反复挑中的那只把店错抛给调用方(PG `40P01`);任何一只未绑的项目包(不论哪个会话、哪个属主)被别的事务长时间锁住时,项目包绑定在锁等待上限处答店错(MySQL 协议 `1205`、PG `55P03`)。
111
+ — **本版**:每一行的绑定都是同一条带谓词的 `UPDATE`;「一只会话一只种子」是表上的唯一键 —— 生成列 `seed_session_id` = 项目包行的 `session_id`(其余行 NULL;库从 `mime` 与 `session_id` 推导,本服务不写它),
112
+ 唯一索引 `uq_att_seed` 在它上面,第二只项目包的绑定被库以唯一键冲突拒,答 `409 attachment.bundle_seed_fixed`。一次绑定只碰要绑的行与这只会话在唯一键上的那一个值:已有种子的会话上的提交不等别的会话 / 别的属主的行即答 409(只在这只会话自己的种子行正被别的事务写着时排在那只写者之后);
113
+ 无种子会话的两次并发首绑恰一只绑上(与 7.142.0 的契约同)。local 后端零变化。
114
+ — **升级前做(`DB_BACKEND=mysql|pg`)**:先在库上加列 —— 没加就起服 ⇒ 列册对账拒启 `config.schema_mismatch`,拒句逐字打印同一句:
115
+ MySQL 协议(TiDB / MySQL)`ALTER TABLE task_attachment ADD COLUMN seed_session_id VARCHAR(190) GENERATED ALWAYS AS (CASE WHEN mime = 'application/vnd.sema.project-bundle' THEN session_id END) VIRTUAL;`;PostgreSQL `ALTER TABLE task_attachment ADD COLUMN seed_session_id VARCHAR(190) COLLATE "C" GENERATED ALWAYS AS (CASE WHEN mime = 'application/vnd.sema.project-bundle' THEN session_id END) STORED;`
116
+ 唯一索引 `uq_att_seed` 由本版起服时自己补建(MySQL 协议在线 `ADD UNIQUE INDEX`;PG `CREATE UNIQUE INDEX CONCURRENTLY`)。存量行不迁移:列由库从已有两列算出;7.142.0 的店已保证一只会话至多一只项目包,存量行不撞这只唯一键。
117
+ 先升库、再升副本:加了列的库上,重启的旧副本被列册对账判成「库比代码新」而拒启;还在跑的旧副本照常服务(它的写同样受唯一键约束 —— 与新副本并发绑第二只项目包的那一窄形,旧副本答店错而不是 409)。
118
+ **谁需要**:SQL 后端上用项目包的会话 —— 并发提交(壳重试、多端同时续)不再偶发店错。**谁受伤**:SQL 后端的部署者:升级前多一句 DDL(PG 的 `STORED` 生成列加列时重写整表;MySQL 协议的 `VIRTUAL` 不重写)。
119
+ **出路**:同「升级前做」。**回滚**:先停光本版的全部副本(唯一键是本版判「一只会话一只种子」的唯一判官:删索引时还在服务的本版副本会让同一会话并发绑上两只项目包),再删索引、删列(否则旧版起服拒启:库比代码新),再起 7.142.x —— MySQL 协议 `ALTER TABLE task_attachment DROP INDEX uq_att_seed;` 再 `ALTER TABLE task_attachment DROP COLUMN seed_session_id;`;
120
+ PG `DROP INDEX IF EXISTS uq_att_seed;` 再 `ALTER TABLE task_attachment DROP COLUMN seed_session_id;`。删列不动任何一行的绑定。
121
+
122
+ 10. **续跑请求可重述对端表:`/decide` · `plan_review` · `/resume` · `/wake` 的请求体收提交体同键的 `mcpServers` / `a2aPeers`(wire 加性;行为面;部署零动作)**:
123
+ — **本版**:键在场(不是 `null`)⇒ 这张表取代停驻任务存下的提交体里的同名表(本次续跑读它,续跑受理后随续跑输入写回,之后不送的续跑重放的也是它);值形与 `POST /v1/tasks` 同一只判官(坏形同码同句 400)。
124
+ 键缺席或 `null` ⇒ 冻结读法:重放提交时存下的表(与上一版逐字同)。两键各自独立;空数组是一张空表,不是缺席。
125
+ — **任务停在一只 MCP 调用上、目标台不在送去的表里**:由引擎判,本服务不判 —— 引擎 7.61.0 起三码各答各形,以 core 为准:那只工具不在了 ⇒ `resume.tool_unavailable`(引擎重开检查点:卡仍 pending、可再决);同名而已不是批准时那一只 ⇒ `resume.tool_contract_mismatch`(同样重开);还是那一只而它的面或声明变了 ⇒ `resume.target_moved`(这次调用不执行、批准用掉,须重新发起),本服务逐字过境。出路:把那一台加回表再决,或取消这条 run。
126
+ — **`/resume` 现在读体**:无体照旧;体不是 JSON ⇒ `400 request.invalid_json`(上一版不读体)。**直连门部署**:`/decide` / `plan_review` 送这两键 ⇒ `400 restatement_not_in_proof`(不论值),不送即可。
127
+ — **不读重述的路**:SLA 到期拒扫、live 卡答复口的停驻赎回(没有调用方送来的表)。`/decide` 命中停驻后台子代的卡 ⇒ `400 decide.parked_restatement_unsupported`
128
+ (子代复活的是它停驻时的那一份,表无处可盖;卡仍 pending)—— 去掉这两键再决。workflow 出身的卡:投递那一跑还活着时同一决定换一张表重投 ⇒ `409 conflict.session_active_run`(不再答首投回执)。
129
+ **谁需要**:任务停着期间移除过自己 MCP 服务器 / A2A 对端的用户(例如 `sema mcp remove` 之后续跑,不让它被重连)和替他们续跑的壳。
130
+ **谁受伤**:不送当前表的壳 —— 续跑仍按提交时的表重连(含已删的台),本服务不替它判、不预拒(本条如实披露);每次都送表的壳在停驻后台子代的卡上得 400,要去掉两键再决(见上)。
131
+ **补偿**:壳在**每次**续跑送它此刻的整张表(MCP 与 A2A 各一张);写回失败时(运维面 warn + `resume_ctx_not_persisted_total`)这一跑照用送来的表,之后一次不送的续跑回到上一张 —— 每次都送就没有这一形。
132
+ **升级前做**:无。**回滚**:回到上一版 ⇒ `/decide` / `plan_review` 送这两键得 `400`,`/resume` / `/wake` 忽略它们;已经写回的表留在续跑输入里,照常重放。
133
+
134
+ 11. **🔴 活流模型文本过同一只脱敏器:`text_delta` / `reasoning_delta` 逐块帧只放脱敏器已判定的字节,一段的拼接 = 段末帧正文;段末余下的字节在段末帧之前一帧补齐(wire 值语义;安全;部署零动作)**:
135
+ — **本版**:`POST /v1/tasks/stream`(顶层与转发子代腿)与 `GET /v1/runs/:id/subagents/:handle/stream` 上,模型文本的逐块帧经与 `done` / 账本 / `text_end` / `reasoning_end` **同一只**自由文本脱敏器放行:
136
+ 服务端按流身份把每段喂进脱敏器的流式口,只放它已判定的字节 —— 末尾那一截(脱敏器的未决尾:本版至多 202 个读法单元,外加一个重扫步长)等更多字节到了再判。含凭据形的段,活流拼接出来的字节与该段的 `text_end.content` / `reasoning_end.content` 相同(凭据是记号,不再是明文);
137
+ 文字段的单位是引擎的文字块(与 `text_end` 同),账本 `text` 行按 turn 拼接后再判 —— 一条凭据的锚与值分落在同一 turn 的两个文字块里时,账本行遮、`text_end` 与活流不遮(此前 `text_end` 上就是这样);
138
+ 不含凭据形的段逐字节不变,只是分帧变了。
139
+ — **帧怎么变**:core 的每一块仍恰出一帧、同一枚 `eventId`、原序,但 `delta` 可以是 `""`(这一块的字节还在窗里);段末还有没放出的字节 ⇒ 紧贴段末帧之前多一帧 `text_delta` / `reasoning_delta`,
140
+ **不带 `eventId`**(因而不发 `id:` 行),身份三键(`parentToolCallId` / `sourceTaskId` / `bgAgentId`)随行。转发子代腿与 tail 面的逐块帧此后也带上 core 给的 `sourceTaskId` / `bgAgentId`(加性)。
141
+ — **延迟**:一个字节在它之后又到了未决尾上界(本版 202 个读法单元,由脱敏器的臂表读出:各臂判定距离的最大值)之后的下一次重扫时出门(重扫步长 = max(64, 段长 / 32) 个码元);
142
+ 落在还没判定完的凭据形(PEM 栅栏、没收尾的 JSON 串值 / URL userinfo、带引号的值)里的那一截等判定或段末。不到上界的短段在段末一次出。
143
+ — **tail 面半路订阅**:订阅者在一段中途开始听,从此刻起收逐块帧,判定仍按整段上下文;它收到的 `reasoning_end` 是整段(此前只覆盖它开始听之后的那一截)。
144
+ **谁需要**:读活流的 API 消费方与渲染端 —— 此前一条凭据在 `done` 上是记号、在活流上是明文。
145
+ **谁受伤**:① 拿活流逐块原文做 diff / 校验和、或拿它当「模型原文」存档的消费方(含凭据形的段字节变了);② 假设每个 `text_delta` 都带 `eventId`、或每帧 `delta` 非空的消费方;③ 对首字延迟敏感的渲染端。
146
+ **补偿**:① 要的是一段的定稿 ⇒ 读 `text_end` / `reasoning_end` / `done`(与活流拼接同一份字节),或经 `GET /v1/runs/:id/events` 回放;② 空 `delta` 照常拼接,余下那一帧按身份三键归并,续流游标沿用上一枚 `id:`;
147
+ ③ 延迟上界是脱敏器的未决尾上界(本版 202 单元)加段长的 1/32,散文在头 202 单元之后按步长连续流出。
148
+ **升级前做**:无。**回滚**:回到上一版 ⇒ `text_delta` 逐块原样、`reasoning_delta` 逐块各自脱敏(跨块的凭据形在活流上露出);落盘数据不受影响。
149
+
150
+ 12. **会话读面对「属主读不出」的会话答 404,不再是 500 `corrupt_session`(行为面:一个应答格的状态变了;运维面日志加性;零新码;部署零动作)**:
151
+ — **哪一形**:文件后端(`DB_BACKEND=local`)上,会话的行事实(属主 / 化身)只住在会话元数据边档的那一格;7.104.0 之前写下、此后从没被读过的会话没有这一格,要从存档补铸它,存档又读不出(会话头坏 / 截在会话头里 / 日志结构走不通)
152
+ ⇒ 这只会话没有可读的属主。SQL 后端的会话行就是 `session_meta` 那一行,没有这一形。
153
+ — **本版**:`GET /v1/sessions/:id` · `GET …/memory-status` · `POST …/memory/erase` · `GET …/head` · `GET …/events` · `GET …/execution-placement` 对这一形答 404 `not_found.session`,体与「没有这只会话」逐字同,带着身份的谁来读都一样(此前 500 + 原故障的码 `corrupt_session` / `invalid_session`,
154
+ 读者拿得到「这个 id 在盘上、且坏了」)。原故障(码与句)进运维日志一行 warn `session_owner_unreadable`,字段 `{ face, sessionId, errorCode, err }`(`errorCode` = 此前这一读答的码)。
155
+ — **没动**:边档有格而存档读不出的会话(属主可判)—— 非属主 404、属主照旧 500(原故障的码;执行放置口读的是行格、不读存档内容,属主 200);I/O 级读故障(说不出在不在)与别的读故障照旧 500;改名 · 分叉 · 删除 · 规则 · 后台任务 · 通知 · MCP 重连 ·
156
+ 会话同步各口 · 提交三口对这一形仍答原故障的 500;没有身份边界的部署上同一形同样 404。
157
+ **谁需要**:带身份边界的文件后端部署的 API 消费方与运维 —— 读面对别人的会话、对属主读不出的会话、对不存在的会话是同一句话。
158
+ **谁受伤**:① 把读面的 500 当「服务错」告警、或拿读面的 `corrupt_session` 判「这只会话存档坏了」的运维 / 监控 / 壳(这一形现在与「没有这只会话」不可区分);② 本来就依赖「读面 500 ⇒ 去查盘」的手工流程。
159
+ **补偿**:按运维日志行 `session_owner_unreadable` 告警与定位(它带会话 id 与原码);这一形本来就不在会话列表里;要修就把它的存档文件(数据根 `sessions/` 目录下)从留存副本恢复 —— 恢复后第一次读会补铸行事实,读面随之恢复。
160
+ **升级前做**:无。**回滚**:回到上一版 ⇒ 读面对这一形重新答 500 + 原故障的码(`corrupt_session` / `invalid_session`);落盘数据不受影响。
161
+
162
+ 13. **中心 MCP 声明撞同一工具命名空间前缀:同步提交腿从 `200` + `failed` 体改 `409 config.mcp_server_name_collision`;`GET /v1/capabilities/mcp` 从无码 `500 internal.error` 改同码、同状态、同句(wire 码与状态变;部署零动作)**:
163
+ — **本版**:部署声明的 MCP 服务器里,两台的名字归一后(引擎铸工具名只留 `[a-zA-Z0-9_-]`,其余换成 `_`)同挂一个工具命名空间前缀 —— `my.srv` / `my_srv` 同挂 `mcp__my_srv__` —— 引擎在任何传输被触碰之前把整份声明集拒掉(码 `config.mcp_server_name_collision`,句子点名两个原拼写、指路改名)。
164
+ 同一份声明集上,两个面现在读**同一张**「引擎拒码 → 状态」表:`POST /v1/tasks`(同步腿)答 `409`,体 `{ error, errorCode }`;`GET /v1/capabilities/mcp` 答 `409`,同体、同码、同句(句子里若有凭据形的拼写,两面都盖住、不外露,只是记号写法不同)。
165
+ 此前:同步腿 `200` + `terminal.failed`(码与整句在终局体里);读面 `500 internal.error` 无码。
166
+ — **不变**:异步 `POST /v1/runs` 与流式腿 HTTP 2xx,同码落终态 run 行;`POST /v1/capabilities/mcp/probe` 自带申报那一口,同一份坏申报仍是 `400 request.field_invalid`(因由不回显);逐字同名的中心声明被配置包的 `mcp` 域在候选判官那一拍整份拒(`config_candidate_rejected`;整份候选不采纳,读面继续答在服的那一份中心声明,没有更早的在服配置就是 `servers: []`),不经引擎。
167
+ **谁需要**:中心(或 `config.d/mcp.json`)里配了 MCP 声明、其中有归一后同前缀的部署:运维与壳的 `mcp list` 在读面上读到原因,而不是无码 `500`;按 HTTP 状态分支的提交方不再把挂不起来的声明集读成一次成功的 `200`。声明集的另一个来源 —— 单用户部署上请求自带的 `mcpServers` —— 里归一后同前缀的两条,同步腿同样答这个码这个状态(一个码一个状态,不分来源)。
168
+ **谁受伤**:① 在这类部署上(或自带了撞前缀的 `mcpServers`)调同步 `POST /v1/tasks`、按 `200` 走成功路径再读 `terminal` 的调用方:现在拿到 `409` 与 `{ error, errorCode }`,没有 `terminal`(该部署每一次提交本来就失败,只是状态换了);② 把读面 `500` 当「服务坏了」做重试或告警的探活:现在 `409`,重试不改判。
169
+ **补偿**:一律按 `errorCode` 分支(两个面同码);出路在运维手里 —— 改中心声明里的名字,让归一后各不相同(拒句点名两个原拼写)。配置坏的部署读面另落一条 warn `mcp_status_face_declaration_refused { code, error }`。
170
+ **升级前做**:无(声明集里没有归一后同前缀的两台的部署零变化)。**回滚**:回到上一版 ⇒ 同步腿 `200` + `failed` 体、读面无码 `500`;落盘数据不受影响。
171
+
172
+ 14. **🔴 持久 ask 店车道:askId 的身份含这一问展示给人的动作(工具名 + 入参摘要)—— 同一 run 同一腿里,模型在后一次回复中复用了前一次的调用 id、换了入参或换了工具,那一问是新问、出自己的卡(安全轴;行为收紧;不透明 `askId` 值变;零 DDL;部署零动作)**:
173
+ — **本版**:流内审批协议上场的部署(持久 ask 店在场)上,ask 身份 = (会话, run, 调用 id, 腿, 委派父调用 id, 类集, **工具名, 入参摘要**);入参摘要是引擎在派发这一问时铸的、与停驻卡上 `boundInputHash` 同一只值。
174
+ 引擎的入参摘要只覆盖入参、不覆盖工具名,所以工具名单独进身份。
175
+ 此前身份不含这两样:引擎只在一次回复之内拒重复的调用 id,跨回复复用同一 id、换了入参(或换了工具、入参逐字同)的第二问与第一问同身份 ⇒ 幂等命中第一问已决的行,
176
+ 第一问的「是」被回放给第二问 —— 那次调用不出卡、不经人就执行。同一调用同工具同入参的重试照旧幂等(同一身份、回各自的答)。
177
+ — **wire 值变更**:不透明 `askId`(`approval_request` 呈卡帧、`POST /v1/tasks/:taskId/asks/:askId/decision` 的路径段)的**每一只值**都与上一版不同 ——
178
+ 身份元组多了两元,没有入参摘要的问那一元是空串,同样改值(形不变:64 位十六进制串)。消费方只把它当不透明 id 用 ⇒ 零动作。
179
+ — **升级窗**:升级前写下的问行按上一版的派生存着,本服务不重算、不迁移。升级时还挂着的一张卡照旧按它行上的 `askId` 可答;同一调用在升级之后再被问一次时得一只新 id、出一张新卡(人多答一次)。
180
+ 没有入参摘要的问(绕过引擎派发点的嵌入方自铸的问)只按工具名分问,不按入参分;对账器的 `unmatchableNoHash` 计数照旧报这类行。
181
+ **谁需要**:流内审批协议上场的部署 —— 一次批准只放行人看到的那一个动作(那一个工具、那一份入参)。
182
+ **谁受伤**:无(收紧:换了入参或换了工具的那一问回到人面前)。升级窗里同一调用再问一次的那张卡多出一次。
183
+ **补偿**:n/a(答那张卡)。**升级前做**:无。**回滚**:回到上一版 ⇒ 身份回到不含动作的形,本版写下的问行按它们存下的 `askId` 照旧可答。
184
+
185
+ 15. **🔴 `POST /v1/approvals/:sessionId/decide`:`boundCallId` 与 `boundInputHash` 两位必带(决断方回显它在 `GET /v1/approvals` 那一行上看到的那一对),缺哪位 ⇒ `400 decide_requires_binding`,体带 `missingKeys` 点名缺的那几位;本服务不再拿当前停驻行补;`remember_requires_binding` 退役(BREAKING 行为面;安全轴;wire:错误码集少一码、拒体加性键 `missingKeys`)**:
186
+ — **本版**:决定落在决断方点名的那一个动作上。两位都是非空串才受理;缺席或空串 ⇒ 400(`null` 等非字符串值照旧先在值形门答 `400 request.field_invalid`)。判在门型守卫之后
187
+ (错门型照旧先拿 `409 gate_not_tool_approval` + 指路),续跑一步都不驱动、卡仍 pending。同一判对这道门的每一个决定者成立:本口、活卡回决停驻成的那张卡(停驻时绑定的那一对)、SLA 到期拒扫(它读下的那张卡的那一对)。
188
+ — **此前**:只要求 `checkpointToken` 或 `boundCallId` 之一,缺的 `boundInputHash` 由**此刻**停在门上的那一行补。同一调用 id、换了入参的新停驻顶替了决断方看到的那一张时,对旧动作的批准落在新动作上 ——
189
+ 只回显 `boundCallId` 的合规形正中这一形。
190
+ — **跟着收的三处**:① `remember: "session"` 不再另有一道门:不带那一对 ⇒ 与一切决定同答 `400 decide_requires_binding`(此前 `400 remember_requires_binding`,本码退役);
191
+ ② 幂等回放(首决已结清后同一份体重发)要求 `boundInputHash` 与行逐字相等 —— 不带 hash 的重发不回放、答 404(此前跳过这一判、回放成功);一份按本版首决成功过的体必然带着它;
192
+ ③ 体形拒句把那一对写成必带键。
193
+ — **决不了的卡**:队列行上没有 `boundInputHash` 的工具审批卡 —— 只有引擎铸入参摘要之前停驻的老行(绑定纪元之前的行)—— 本口决不了,SLA 拒扫也决不了;
194
+ 用 `POST /v1/runs/:id/cancel` 收场,或候它的终局时刻收割。
195
+ **谁需要**:每一个决断方 —— 一次批准只落在人看到的那一个动作上。
196
+ **谁受伤**:只回显 `boundCallId`(或只回显 `checkpointToken`)的决断方 —— 升级后决定得 400;拿 `remember_requires_binding` 分支的消费方(码不再出现)。
197
+ **补偿**:从队列行原样回显那一对:`GET /v1/approvals` 的 `pending[]` 行与 `/v1/approvals/stream` 的 `pending` 帧都带 `boundCallId` / `boundInputHash`;按 `missingKeys` 补齐再决。
198
+ **升级前做**:决断方(SDK / 壳 / BFF)先升到回显两位的版本,再升本服务。**回滚**:回到上一版 ⇒ 缺 `boundInputHash` 的决定重新被当前停驻行补(本条修的那一形回来);带全那一对的决定两版同答。
199
+
200
+ 16. **更正(7.142.0 节第 18 条与第 19 条的两处读数说法,文书;零行为变化)**:
201
+ ① **第 18 条**「谁受伤」写「坏块的部署(平面读 `no_origins`)」,回滚句写「平面读 `no_origins` 那一侧不变」;7.142.0 节 CHANGELOG 同说,`web_admin_sandbox_git_refused` 行的 `effect` 句也说「the sandbox git plane reads no_origins」。这些说的是平面的**第九条前提**(`sandboxGit.origins` 非空)不成立。
202
+ `GET /v1/capabilities` 的 `credentialPlane.sandboxGit.reason` 取判序里**第一个**不成立的前提(九词闭集与判序见契约附录 B):第三条前提(取值口的 worker 凭据)恒不成立,所以坏块、撤块、没有块的部署线上读到的都是 `no_caller_credential`(或更前的词),`no_origins` 要前八条都成立才会成为读数。第 18 条 ② 那一句(读数停在 `no_caller_credential`)本来就这样写,以它为准。
203
+ ② **第 19 条**对第二形(强锚名紧贴在前一枚凭据的尾巴上)写「run 行与账本回放上被遮(读面多过一遍脱敏)」。在 7.142.0 上该形首遍不遮、第二遍才遮,读数看这个出口过几遍:`GET /v1/runs/:id` 的 run 行(写口一遍、读口一遍;经同步 / 流式提交落库的结果写前还多过出口那一遍)与 `GET /v1/runs/:id/events` 回放里的 `text` / `reasoning` / `done` 行(账本汇点写口一遍、回放读面一遍,至少两遍)被遮;
204
+ **回放里的 `tool_end` 行**(工具自己的输出,例如 Bash 回显)只过工具输出投影那一遍、回放读面不再过 —— 这一形在那里**明文**。流式 `done` 帧、同步体、运维日志行不变(一遍,明文);活流逐块帧 `text_delta` / `reasoning_delta` 与 `text_end` / `reasoning_end`(7.143.0 起与 `done` 同一只脱敏器、同一遍)同样明文。
205
+ 第一形(复合凭据名)在每个出口都是明文,第 19 条原句不变。**本版起两形都遮**(第 41 条):复合凭据名在自由文本的声明形里是名字锚;名字的左界止于前一枚已认出的凭据的终点,第二形首遍即遮、各出口同读,上面这段出口遍数读数只是 7.142.0 的读数。
206
+ **谁需要**:按「凭据不会以明文出现在日志 / 转录里」配告警、做审计的部署,以及把 `GET /v1/runs/:id/events` 当作已脱敏副本交给下游的消费方;读 `credentialPlane.sandboxGit.reason` 并按 `no_origins` 分支的运维 / 嵌入方。
207
+ **谁受伤**:无新增(零行为变更);照第 19 条原句认定「账本回放不带这一形」的部署 —— 7.142.0 写下的回放里的 `tool_end` 行会带。
208
+ **补偿**:升级前已落库的 `tool_end` 行回放读面不再过脱敏(第 41 条「存量行」)⇒ 已有的回放可按这两形自查;`reason` 按判序第一个不成立的前提读。7.142.0 节原文不改(已发节字节不动)。**升级前做**:无。**回滚**:无关。
209
+
210
+ 17. **🔴 引擎 7.61.0:升级前结清在途审批、排空旧 worker;部署的 mod 包按契约 7 重建(部署面;BREAKING)**:
211
+ — **此前**:引擎 7.60.0 的检查点把发起会话记在 `sourceTaskId` 下;mod 内核契约号是 6。
212
+ — **本版**:引擎只认新键 `sourceSessionId`(零别名)—— 升级前停驻、升级后才决断的卡,读回答 `requirements_damaged`,不能再决断;SQL 检查点店读到旧键的行同样判不可读,不静默读成空。
213
+ 引擎只加载声明 `"contract": 7` 的 mod 包:按契约 6 写的包在起服被拒 `config.mods_invalid`,拒句点名两个契约号。
214
+ **谁需要**:所有升级到本版的部署。**谁受伤**:升级时还停着审批卡的部署(那几张卡作废,要重新提交任务);同一只库上混跑旧 worker 的滚动窗;装了 mod 包的部署。
215
+ **补偿**:升级前在旧版上把在途审批批完 / 拒完(`GET /v1/approvals` 为空),排空并停光旧 worker 再起本版;mod 包把 `hooks.json` 的 `"contract"` 改成 7 并按新契约重建(新站点与 `$` 调用见引擎文档)。回滚到 7.142.x:同样先结清本版的在途审批再起旧版。
216
+
217
+ 18. **🔴 审批卡的委派子代出处键 `sourceTaskId` 改名 `sourceSessionId`(wire BREAKING;零别名;值不变)**:
218
+ — **此前**:活的 `tool_approval` / `approval_request` 帧、`GET /v1/approvals` 的 `livePending` 与停驻行、卡片 JSON 上,委派子代的卡带 `sourceTaskId`(值是子代的会话 id)。
219
+ — **本版**:同一格叫 `sourceSessionId`,同值;`sourceTaskId` 不再出现。唤醒腿写进检查点的这一格改填会话 id(此前误填了 run 的 task id)。
220
+ 本版之前铸的卡信封(SQL `approval_ask.card_json` / local 问账里同一份信封)上那一格还叫 `sourceTaskId`:读口改名读回(一次性迁移 M37,只有读侧,窗口见登记表;
221
+ 每改一次计一次 fail-open `server.approval-card.retired-card-key-read`)—— 断连重放、回决口、幂等重入照读,wire 上只出 `sourceSessionId`(不是别名)。
222
+ **谁需要**:引擎同一个会话身份只有一个名字。**谁受伤**:按 `sourceTaskId` 在场判「这是委派子代的卡」、或拿它关联子代行的壳 / SDK / 控制台。**补偿**:改读 `sourceSessionId`;`fromSubagent` 判别位与 `originTaskId` 不变。
223
+
224
+ 19. **通告码 `context.image_removed` 改名 `context.media_removed`;TaskOutput 工具退役(wire 码改名;任务柄读腿文字;策略名单)**:
225
+ — **本版**:媒体离开模型请求的通告只铸 `context.media_removed`(`detail.reason` 说原因、`documents` 计文档),旧码不再出现;模型名册里没有 `TaskOutput`(后台命令的输出读输出文件,后台 agent / 工作流的结果在完成通告里);
226
+ HTTP 任务柄读腿的正文围栏标签由 `TaskOutput <id>` 改为 `task <id>`;策略的 deny / ask 名单点名 `TaskOutput`(或 `TeamCreate` / `TeamDelete`)⇒ 跑前拒 `config.legacy_tool_name`,allow 名单只得一行提示。
227
+ **谁需要**:读媒体移出通告的消费方 —— 图片与文档离开模型请求是同一只码(`detail.reason` 说原因、`documents` 计文档);在 deny / ask 名单里写了已退役工具名的部署 —— 跑前被点名,而不是一条名册里已经没有的工具名在名单里匹配不到任何调用。
228
+ **谁受伤**:按旧通告码写分支的消费方;按旧围栏标签匹配任务柄正文的 UI;策略名单里写了 `TaskOutput` 的部署。**补偿**:改读新码;按新标签匹配;从 deny / ask 名单里删掉这三个名字。
229
+
230
+ 20. **设备车道:放置围栏 + 设备执行器协议修订 4(行为面;wire 加性;部署零动作)**:
231
+ — **本版**:被放在某台设备上的续跑 / 委派 / 唤醒腿,根会话此刻绑的不是那台设备(中途换绑过)⇒ `device.identity_mismatch`,在连接之前拒,不改道到新设备;引擎对报告与围栏不符的新腿答 `config.placement_not_honored`。
232
+ 协议修订号 3 → 4:文件操作的结局码闭集加 `symlink_loop`,执行器声明修订 4 才发;`deviceExecutor.protocolVersion` 报 4。
233
+ **谁需要**:用设备车道、任务停着期间可能给根会话换绑设备的部署 —— 续跑 / 委派 / 唤醒腿在连接之前被拒,不跑到一台没有那份工作区的设备上;执行器在文件操作上撞到符号链接环时有一个专门的结局码可报。
234
+ **谁受伤**:在任务停着时给根会话换绑设备、又想在新设备上续跑的用户(新设备上没有那份工作区)。**补偿**:结束或取消在途任务,在新设备上重新开始;执行器升到修订 4 再发新词。
235
+
236
+ 21. **SQL 后端:新表 `mailbox_consumed`(信箱已收窗;本服务起服自建;部署零动作)**:
237
+ — **本版**:`ack` 删掉的带身份记录各留一行(每盒留最新的引擎窗那么多行,同段修剪),追加时与持有行一起交引擎判重 —— 已收过的记录重投答它原来的 `seq`,不再当新信。表由起服的建表语句自建(`CREATE TABLE IF NOT EXISTS`),存量信箱行不迁移。
238
+ **谁需要**:SQL 后端上会重投带身份记录的发信方(重试、断线重连)—— 已收过的那一条答它原来的 `seq`,收信方不把它当一封新信再收一次。
239
+ **谁受伤**:SQL 部署多一张表(每盒的行数以引擎窗为界、同段修剪);回滚到 7.142.x 的那段时间里,重投已收过的记录照旧当新信收(旧版不读这张表)。
240
+ **补偿**:部署零动作(表起服自建);升级之前已被 `ack` 删掉的记录没有窗行(存量不迁移),升级后重投它们仍当新信 —— 窗从升级那一刻起积累。
241
+ **回滚**到 7.142.x:旧版不读这张表,表留着即可(再升回本版时接着用)。
242
+
243
+ 22. **`DB_BACKEND=local`:落盘的工具结果按 `TOOL_RESULT_TTL_SEC` 老化回收(行为变化;运维面日志 tag 改名)**:
244
+ — **此前**:本机后端的工具结果目录没有任何清理路径,只增不减;SQL 后端按 TTL 回收。
245
+ — **本版**:本机与 SQL 同一只回收腿,按 `TOOL_RESULT_TTL_SEC`(缺省 86400 秒)收走更老的工具结果;回收失败的节流日志 tag 由 `tool_result_reap_older_than` 改为 `tool_result_reap`。
246
+ **谁需要**:长期运行的本机部署 —— 工具结果目录此前只增不减,盘面无界。
247
+ **谁受伤**:本机部署里依赖比 TTL 更老的工具结果引用的会话(引用读不回)。**补偿**:把 `TOOL_RESULT_TTL_SEC` 调大;告警规则按新 tag 匹配。
248
+
249
+ 23. **会话同步 blob 上传:内容与哈希不符一律 400;嵌入方的文件历史店 / 工具结果店换新组形(wire 值变化;嵌入方类型面 BREAKING)**:
250
+ — **本版**:`PUT /v1/sessions/:id/sync/blobs/:hash` 内容地址对不上 ⇒ 400 `request.hash_mismatch`(此前一形答 502 `unsupported_format`)。
251
+ 嵌入方自建的文件历史店:`syncBlobFaces` / `putBlob` / `getBlob` / `hasBlobs` 退役,改交引擎的 `blobExchange` 组(`put` / `get` / `has`,整组或没有;内容地址在 `put` 判);自建工具结果店:`reapOlderThan` 退役,改 `reap(now, { maxAgeMs })`。
252
+ **谁需要**:会话同步客户端 —— 内容与哈希不符读到一个确定的 400(同一份字节重传不会好),而不是一个看似可重试的 502;嵌入方 —— 两只店的 blob 面收成引擎的一组,内容地址只在 `put` 一处判。
253
+ **谁受伤**:把 502 当可重试的同步客户端;自建这两只店的嵌入方(编译即红)。**补偿**:哈希不符按 400 处理(重传正确内容);按新组形实现。
254
+
255
+ 24. **记忆同步:套在别的 scheme 下的 URL userinfo 也判作凭据(行为收紧;部署面)**:
256
+ — **本版**:`MEMORY_SYNC_URL` / `MEMORY_SYNC_SERVE_IDENTITY` / 同步层名里出现 `proj:https://user@host/…` 这一形 ⇒ 起服拒;同步口请求的 `peer` / `target` 带这一形 ⇒ 400(不回显值)。
257
+ **谁需要**:不让凭据落进会被日志记下、存进本机游标、经能力位 `memorySyncServe.identity` 逐字公开的那些值的部署 —— 此前 `proj:https://user@host/…` 这种外面再套一层 scheme 的写法漏判。
258
+ **谁受伤**:把凭据写进这些值的部署(此前放行)。**补偿**:凭据从这些值里拿掉,走凭据配置。
259
+
260
+ 25. **设置导入:用户层里以 `/` 起的路径规则留在设置文件里(行为收窄);🔴 导入预览请求体的 `userSettings` 层不再收 `root`(wire BREAKING;零别名)**:
261
+ — **本版**:导入预览把这类规则列进 `preview.skipped`(原因前缀 `unsupported.layer_root:`),不再存成每个项目根都读的全局规则;先前准备好的导入记录在兑现时拒这类候选。
262
+ `POST /v1/rules/cc-import/prepare` 的每一层按 `layer` 分形:`{ layer: "userSettings", path, content }`(**没有** `root`)| `{ layer: "projectSettings" | "localSettings", path, root, content }`。
263
+ 用户层带 `root` ⇒ 400 `request.body_shape`,拒句 `body must be {layers:[{layer:"userSettings",path,content} | {layer:"projectSettings"|"localSettings",path,root,content}]}`(此前每一层都必带 `root`,用户层那一格从不被读)。
264
+ **谁需要**:从 CC 导入用户层设置的用户 —— 用户层的 `/x` 规则此前存成每个项目根都读的全局规则(第 37 条起服退役的就是这批存量);导入客户端 —— 用户层不再要一个从不被读的 `root`。
265
+ **谁受伤**:靠导入把用户层 `/x` 规则带进来的用户;在用户层上照旧带 `root` 的导入客户端 —— 每次带用户层的预览都 400,不只是带 `/x` 规则的那几次。
266
+ **补偿**:在项目层写同一条规则(项目层的 `/` 形读在那个项目的根);导入客户端在 `userSettings` 层上去掉 `root`(项目层 / 本地层照旧带)。
267
+
268
+ 26. **🔴 SQL 后端:`approval_ask` 加一列 `decided_via`;决定体加性键 `decidedVia`;能力位 `approvalDecidedVia`(wire 加性;DDL:升级前先加列,先升库再升副本;零一次性迁移)**:
269
+ — **本版**:四条决定口(`POST /v1/tool-approvals/:id/respond`、`POST /v1/tasks/:taskId/asks/:askId/decision`、`POST /v1/approvals/:sessionId/decide`、`plan_review`)受理 `decidedVia`(取引擎的座位闭集,只许在 `decidedBy: "policy"` 旁),
270
+ `hostDecision.decidedVia` 原样交引擎;问账行与决定同一条 UPDATE 落列 `decided_via`。行上 NULL(含加列前的旧行)读作缺席 = 部署自己的策略;请求体上的 `decidedVia: null` 不是缺席,答 400。
271
+ — **DDL**(升级前先在库上执行;没加就起服 ⇒ 拒启 `config.schema_mismatch`,拒句逐字打印同一句):
272
+ MySQL 协议 `ALTER TABLE approval_ask ADD COLUMN decided_via VARCHAR(32) NULL;`;PostgreSQL `ALTER TABLE approval_ask ADD COLUMN decided_via VARCHAR(32) COLLATE "C";`
273
+ 存量行不改写(加列即 NULL)。加了列的库上,重启的 7.142.x 副本拒启(库比代码新)。
274
+ **谁需要**:按 PermissionRequest 钩子代答卡片的壳与宿主(模型读到「一只 PermissionRequest 钩子拒了这次调用」,不是部署策略句)。**谁受伤**:已有问账表的 SQL 部署(`DB_BACKEND=mysql|pg`):升级前多一句 DDL。
275
+ **补偿**:能力位 `approvalDecidedVia`(恒 `true`)见位才发;**回滚**到 7.142.x:先停光本版全部副本,再 `ALTER TABLE approval_ask DROP COLUMN decided_via;`,再起旧版。
276
+
277
+ 27. **`verify:true` 任务的上限封住整个任务的实现侧:每轮修复只拿余额,一条轴花光 ⇒ 不再修复(行为收窄;wire 加性 `verification.allowanceExhausted`;部署零动作)**:
278
+ — **本版**:任务的 `limits`(请求的 `maxCostUsd` / `maxTokens` / `limits.maxTurns`,或 `MAX_TASK_COST_USD` / `MAX_TASK_TOKENS` 落成的值;开着 `RESOURCE_SUSPEND` 时是跨片总额 `RESOURCE_SUSPEND_TOTAL_TOKENS` / `_TOTAL_BUDGET_USD`,每一片仍按 `limits` 的窗走)
279
+ 管实现腿与各轮修复的合计:每轮修复拿的是上限减去前面各腿自报的花费。修复腿越过余额 ⇒ 照常 `limits.max_cost_exceeded` / `max_tokens_exceeded` / `max_turns_exceeded`;某条轴已花光 ⇒ 不派修复,判词停在那次 FAIL,
280
+ 结果带 `verification.allowanceExhausted: { axis: "cost" | "tokens" | "turns", remaining?: 0 }`。前面有一腿没报价(模型无价目,结果里没有 `costMicroUsd`)而任务带美元上限 ⇒ 美元轴余额算不出,读作没有余额(`axis: "cost"`,不带 `remaining`)。
281
+ 此前每轮修复是新开的一次运行、整份重拿上限,跨片总额也逐腿重给 —— 本文件 7.140.0 节第 10 条「补偿」里「verify 任务没有跨腿的总额」一句自本版起不再成立(已发节字节不动)。
282
+ **谁需要**:给 verify 任务设了花费上限、要它真封住整个任务的部署。**谁受伤**:按「每轮修复各有一份上限」配额的调用方(修复轮数可能变少,结果多一键);配了 `MAX_TASK_COST_USD` 却跑无价目模型的部署 —— verify 任务第一次 FAIL 之后不再修复(起服那行 `cost_ceiling_without_pricing` 说的就是这台)。
283
+ **补偿**:按整个任务配上限(各轮可能花的加起来);给模型配价目(`MODEL_COST_*` / 名册行的 `cost`);轮数仍由 `verifyRounds` 管。**回滚**:回到 7.142.x 即回到逐腿整份(无数据面)。
284
+
285
+ 28. **更正(7.139.0 节第 24 条 ③,文书;零行为变化)**:本机宿主车道上没配 worktree 包装、要 `isolation: "worktree"` 的 workflow 子代,拒它的仍是本服务的 host 工厂(码 `isolation.unavailable`,句子点名 `WORKTREE_ISOLATION_ENABLED` / `WORKTREE_REPO_ROOT`),
286
+ 不随引擎 7.61.0 改由引擎拒:worktree 的核验在引擎契约里是执行环境工厂一方的义务(必须铸 worktree 或抛),引擎没有回执可核。部署零动作;7.139.0 节原文不改(已发节字节不动)。
287
+
288
+ 29. **嵌入方:包一层 `ServiceDeps.resolveSpec` 的包装必须返回装配器交回的同一只对象(嵌入方行为面 BREAKING;部署零动作)**:
289
+ — **此前**:本服务的部署事实(执行车道、run 技能名册、受理见证)以 symbol 键挂在装配器交回的 spec 上,`{ ...spec, model }` 这类拷贝把它们一起带过去。
290
+ — **本版**:事实登记在以装配器交回的那一只对象为键的载体里,不在 spec 上;拷贝读不到 ⇒ 选路答 500(`HandsLaneUnstampedError`,日志留原句),技能名册与会话工作区见证缺席。
291
+ **谁需要**:每一条 run 与续跑 —— 引擎的 `TaskSpec` 一读门按 `Reflect.ownKeys` 判自有成员,不是它声明的键(含 symbol 键)一律拒(新 run `config.spec_unknown_key`、续跑 `checkpoint.config_refused`),挂在 spec 上的部署事实会让任务跑不起来。
292
+ **谁受伤**:在 `createResolveSpec` 外面再包一层、返回拷贝的嵌入方。**补偿**:在交回的对象上改并返回它(`return Object.assign(spec, { model: … })`);只用本服务装配器的部署零动作。
293
+
294
+ 30. **验证器自己停驻的那张卡改由引擎的作废动词结算,会话钉随之放掉;编排(leader)路径同样作废(行为面;修复;部署零动作;订正 7.141.0 节第 31 条的两条已知限制)**:
295
+ 7.141.0 第 31 条写的两条已知限制 ——「作废只结清卡,引擎为它钉住的验证器会话不在这里释放,留在副本内存里直到进程重启」「编排(leader)路径上的验证器卡本版不作废」—— 本版两条都收:
296
+ 后台 run、同步提交、续跑 verify 臂与 leader 的 worker 结算一律交跑过那次验证门的 Runner 的作废动词(引擎读卡上记的组合腿,只收验证器腿铸的卡;卡 `expired`、验证器会话放钉),本服务不再读卡、比会话、店级作废。
297
+ **谁需要**:长寿副本、看待决面的人与壳。**谁受伤**:按 `server.verifier-pause.card-void-failed` 探针行的旧原因词(`reason=store_error|card_mismatch|no_checkpoint_store`)筛的告警 —— 探针行改带引擎的码(`code=<码 | thrown>`)。
298
+ **补偿**:告警改按 `code=` 筛(`thrown` = checkpoint 店在出错;`checkpoint.void_unpin_failed` = 卡已作废、会话钉没放掉)。7.141.0 节原文不改(已发节字节不动)。
299
+
300
+ 31. **`settings.permissions.allow` 的条目也可带出处层 `{ rule, layer }`;user 层的 `/` 形 allow 读在执行环境的配置根(wire 条目型放宽 + 能力位 `settings.permissions.layeredAllow`;端见位才发;部署零动作,沙箱车道配置根读不出的部署见补偿)**:
301
+ 7.141.0 节第 15 条为 deny / ask 立的读法 —— 一条路径规则的 `/` 读在它那一层的根上 —— 本版扩到 allow 桶(收掉那一条「已知限制」里 allow 桶只收纯串的那一半):`userSettings` 里要根的 allow 规则免问的是配置根下的路径、不再是任务根下的;
302
+ 别的层与纯串照旧读在任务根。条目一条一员按位置交给引擎(命中报的位置 = settings 表的下标)。
303
+ **谁需要**:user 层写了 `/` 形 allow 的端用户。**谁受伤**:① 依赖「user 层 `/x` 放每个项目的 `x`」的人 —— 端升级发带层条目后任务根下 `x` 的写照问;② 沙箱车道配置根读不出的部署(home 未声明且没转 `SEMA_CONFIG_DIR`):这样的 allow 422 `home_unresolvable`;
304
+ ③ 回滚 / 滚动发布中的旧副本读到带层 allow 的体 ⇒ 新提交 400、续跑 409。
305
+ **补偿**:① 规则挪进项目层,或写相对形 `x/**` / `//` 绝对形;② 沙箱 env 名单加 `SEMA_CONFIG_DIR`,或声明 `REMOTE_EXEC_HOME_DIR`;转进去的值写绝对路径(相对值由引擎在开跑时拒这条 run);③ 端读能力位 `settings.permissions.layeredAllow`、见位才发。7.141.0 节原文不改(已发节字节不动)。
306
+
307
+ 32. **后台子代一个 id(引擎换代):面板、完成通知、孙代嵌套、审批归属都读注册表行 id `a…`;升级前停驻的后台子代先结清,回滚前同样(行为面;部署面:升级 / 回滚前各一步;wire 键零变化)**:
308
+ 引擎这一版起后台子代的 tick、结果、完成通知、孙代的父链接与问上的 `agentId` 都是它的注册表行 id;本服务按这一个 id 读,不把它的会话 id 当别名读(升级前的 id 不被读成新 id 的别名)。
309
+ **谁需要**:看面板的端(一只后台子代一行)。**谁受伤**:① 升级时还停驻着的后台子代 —— 它升级前登记的那些行(它自己的后台任务、它派出的子代,属主是它当时的会话 id)在它以行 id 复活之后够不着;
310
+ ② 回滚:本版停驻的后台子代的卡记着行 id(要求词 `row_task_id`),上一版读不懂这个词,续跑拒 `version_newer`。
311
+ **补偿**:升级前与回滚前各让停驻的后台子代结清(答掉它的审批、或停掉它);没结清的卡升回本版即可续跑。**升级前做**:让停驻的后台子代结清(答掉它的审批、或停掉它)。**回滚前做**:同样让本版停驻的后台子代结清;没结清的卡在回滚后的副本上续跑不了,升回本版即可续跑。
312
+ 运维日志 `task_notification_observed` 删 `hadFleetRow` 键(后台子代的行不经那条腿)。
313
+
314
+ 33. **Runner 之外复活的后台子代,完成通告走委派树根会话的会话通知门(行为修复;wire 零新键;部署零动作)**:
315
+ — **此前**:引擎 7.61.0 下,停驻的后台子代经 `POST /v1/approvals/:sessionId/decide` 批准恒答 409 `decide.revive_rejected`(引擎 `revive.no_background`:Runner 之外的续跑解不出完成 sink 就不起),子代永不再跑;
316
+ 7.61.0 之前续跑起得来、完成谁都收不到。操作员续跑(`POST /v1/runs/:id/subagents/:target/resume`)起的续跑同样起得来、完成不交给任何人。
317
+ — **本版**:这两种续跑跑完,完成交给委派树根会话的会话通知门(与 `POST /v1/sessions/:id/notify` 同一只门):根会话在本副本有活流 ⇒ 注入那条 run(`<task-notification type="external">`,最早边界);
318
+ 空闲 ⇒ 进它的收件箱,下次开流排空成一帧 `task_notification`。帧 = notify 门的 external 形:`task_type: "external"`、`task_id` = 子代句柄、`status`、`summary`、`result?`、`seq`(停驻周期号),不带 `completionId`。
319
+ 投递那一刻根会话不在、或住客已换(删了、同 id 被重新登记、属主变了)⇒ 不交,一行 warn `background_completion_undelivered`。更深的行(孙代)报给委派树的根。
320
+ 门在投递动作之前紧邻地复核住客(属主与化身两样),`POST /v1/sessions/:id/notify` 同此:属主门读过之后、投递之前这只会话被删、同 id 换了住客或换了属主 ⇒ 答 404 `not_found.session`(此前照投给下一位住客);
321
+ 收件箱条目的属主照旧取属主门那一读(无主会话盖请求主体)。会话店不铸住客令牌(嵌入方自建的店;头读没有 `incarnation`)⇒ 复核只比属主:属主没变 ⇒ 照投(与此前同形),变了 ⇒ 404;
322
+ 本服务自带的店每次建行都铸令牌。
323
+ **谁需要**:停驻后台子代、再由人决断的部署;用操作员续跑动词的壳。**谁受伤**:把复活的完成帧当噪声的消费方(根会话多一帧 `task_notification`);按 `task_type: "background_agent"` 或 `completionId` 关联子代完成的消费方
324
+ (这一形上两键都不在);根会话空闲时排空的是展示帧,下一次 run 的模型上下文里没有它。**补偿**:帧形是 notify 门既有的 external 形,按 `task_id`(子代句柄)关联;同一子代两次复活的完成 `seq` 不同。
325
+
326
+ 34. **会话记忆同步触发口 `POST /v1/sessions/:id/memory/sync`:409 `conflict.session_memory_unobserved` 体恒带 `reason`(闭集五词),句子由 `reason` 定;最近一条已结 run 的记录里没有记忆观测的那一形有了自己的一句出路;🔴 它配了记忆而没挂上(`memoryless`)的那一形由 200 空报告改 409(wire 加性;行为面;三句拒句换形 / 新句;零 DDL;部署零动作)**:
327
+ — **本版**:读集照旧只读会话**最近一条已结 run** 的记忆观测,不往更早的 run 找。读集取不到时 409 体多一个键 `reason`,判序 = 读的先后:
328
+ `no_settled_run`(会话还没有已结 run)· `run_result_missing`(最近一条已结 run 的记录里没有结果:实例丢了被收割、停驻过期被收割 —— 收割写空结果,停驻时那份观测随之不在 —— 、在引擎答出之前抛)·
329
+ `run_result_unobserved`(记录里有结果、结果里没有记忆观测:引擎在准备阶段拒了 / 失败了它,引擎答出之前被取消,或更早版本的引擎写下的结果)·
330
+ `memory_not_mounted`(观测在,说那一条配了记忆而没挂上:引擎 `memoryless`,`mount-failed` / `no-backend`)· `no_leaf`(观测在、说得出读集,会话还没有叶子)。
331
+ 停因本身读那条 run 的终局(`GET /v1/runs/:id` 的 `status` / `errorCode`;没挂上的因由读它的 `result.effectiveMemoryScopes.reason`)。有在飞 run 时照旧另带 `activeTaskId`。
332
+ — **行为变化(🔴)**:最近一条已结 run 的观测是 `memoryless` 时,上一版把读集读作 `[]`、答 200 空报告(`scopes: []`、`layers: []`);本版答 409 `memory_not_mounted` —— 挂载失败时那一腿本该读哪几层不可知,`[]` 只留给本会话没配记忆(`none`)。
333
+ — **句子**:`no_settled_run` 不变;两只空观测因由同一句(「the latest settled run in this session has no memory observation on record, and an earlier run is never read in its place — run another task in this session (if that run was refused, fix what refused it first), then sync」);
334
+ `memory_not_mounted` 一句(「memory was configured for the latest settled run in this session but did not mount, so which memory this session reads is unknown, and an earlier run is never read in its place — run another task in this session (if its memory fails to mount again, fix that first), then sync」,新臂);
335
+ `no_leaf` 一句(「this session has no transcript yet, so there is no revision to sync from — run a task in it (or let the run in flight finish), then sync」)。此前三臂同一句「this session has no settled run with a memory observation yet …」。
336
+ — **不变**:码、状态 409、门序、零请求零写;观测是 `mounted` / `none` 的 run 读集与 200 报告逐字同上一版;持久数据零变化(空观测由读口从行上已有事实算出,不落盘)。
337
+ **谁需要**:壳与控制台 —— 按 `reason` 告诉用户同步为什么不可用、怎么解开(会话里早先成功过的 run 不再让人以为同步坏了;记忆没挂上的会话不再被当成「同步完了」推上云)。
338
+ **谁受伤**:按 409 体逐字节比对、或按句子匹配这一码的消费方:体多一个键,空观测两臂与 `no_leaf` 臂换了句子;最近一条 run 记忆没挂上的会话 —— 上一版 200 空报告、本版 409,同步按钮灰到再跑一条挂上记忆的任务为止。
339
+ **补偿**:按 `errorCode` + `reason` 分支,不比句子;`reason` 是闭集,读不懂的词按「读集取不到」处置(在会话里跑一条任务、等在飞的 run 结束,再触发);`memory_not_mounted` 反复出现 ⇒ 读那条 run 的 `result.effectiveMemoryScopes.reason`,查记忆引擎的挂载故障。
340
+ **升级前做**:无。**回滚**:回到上一版 ⇒ 409 体不再带 `reason`,三臂回到同一句,`memoryless` 回到 200 空报告;落盘数据不受影响。
341
+
342
+ 35. **🔴 记忆同步:中心每个层分区一只化身,写之前判请求的主张;同步口改到 `POST /v2/memory/sync/:scope`(`/v1/…` 答 404);本机游标原样携带化身(引擎 7.61.0 三处硬破坏;持久数据面:中心库新表 `agent_memory_engine_sync_partition`,起服自建、零 ALTER;部署:两端同版 + 停驻层删游标)**:
343
+ 此前:游标只绑层、对端地址与本机店。中心库被清空重建或从更早的备份恢复,同一地址读作同一个对端 —— 下一轮按旧基线对账,中心没有的基线条目答成「中心已删」、旧副本答成「中心改过」,本机照落,这一轮还报 `ok`。
344
+ 现在:① 中心每个层分区一只化身(分区第一次被同步读到时出生、存在中心库),结果体 `cursor.incarnation` 恒在场;请求的 `peerIncarnation` 在任何读写之前比 —— 不是本分区的、或带非空基线却没有主张 ⇒ 200 `{ refused: "baseline_incarnation", incarnation }`,零读零写;
345
+ 一轮写完之后化身已变 ⇒ 500(不答算在旧状态上的结果)。② 同步口路径 `/v2/memory/sync/:scope`;`/v1/memory/sync/…` 无人认领 ⇒ 404。③ 本机执行器把落盘游标的 `peerIncarnation` 原样交引擎;中心在化身轴上的拒 = 停驻(同另三轴,一条出路:删游标;运维行 `axis: "incarnation"`)。
346
+ **谁需要**:DB 记忆平面的中心与连它的本机同步腿 —— 中心库重建之后设备上的条目不再被一轮「成功」的同步删掉或回滚。
347
+ **谁受伤**:① 升级期:新旧两端互答 404(新客户端只打 `/v2`、旧中心只认 `/v1`;新中心对旧客户端同样 404),两侧都什么都没写,**同步暂停,两端同版后恢复**;
348
+ ② 两端同版后的第一轮:每一层带基线的游标(升级前写下的,没有化身)被中心拒 `absent`,本机那一层停驻(`GET /v1/sessions/:id/memory-status` 的 `memorySync.parked[]`、运维行 `memory_sync_layer_parked { axis: "incarnation", found: "absent" }`),停驻层每一轮仍发一次请求、被中心在写之前拒掉;
349
+ ③ 中心从备份恢复:恢复回来的化身就是备份时那一只,读作同一份状态 —— 备份之后设备上的新条目照旧会被删掉或回滚(引擎的已知限制)。
350
+ **补偿**:① 两端都升到本版,先后不限(升级期只是不同步,不丢数据);② 删掉停驻层的游标文件(运维行 `cursorPath` 逐字;`<记忆引擎根>/sync-cursors/<scope 的 sha256 前 32 位>.json`),下一轮从空基线重收敛:
351
+ 两边都改过的条目按冲突报,上一轮之后一侧删掉的条目会从另一侧回来;③ 中心从备份恢复之后、起服之前删掉 `agent_memory_engine_sync_partition` 的全部行(轮换化身):每个客户端下一轮被拒 `other`、按 ② 处置(DEPLOY-PREREQS)。
352
+ **升级前做**:无(新表起服时 `CREATE TABLE IF NOT EXISTS` 自建;库账号已有建表权限)。**回滚**:旧中心不认 `/v2` ⇒ 同步再停;本版写下的游标带 `peerIncarnation`,旧二进制按名挑员时丢掉它 —— 回到旧版即回到旧语义;新表留着无读者。
353
+ **嵌入方**(自建 `ServiceDeps`):`memorySync` 的答型改为 `MemorySyncAnswer`(结果或拒答);自建 `MemorySyncStore` 多一口 `partitionIncarnation(scope)`(TypeScript 编译红即通知)。
354
+
355
+ 36. **🔴 抹除与会话记忆状态答整个部署:两只抹除口走引擎的一只部署对象,会话记忆状态不带层座 ⇒ 引擎在每只店上折成的那一份,本部署有哪些店读盘上的(行为面;409 `memory.erasure_store_unjudged` 退役、会话自助抹除体的 `scope` 退役;嵌入方类型面 BREAKING;部署零动作)**:
356
+ 此前(7.140.0 起):目录钉住的 file 部署上,落到个人挂载那只店的抹除一律 `409 memory.erasure_store_unjudged`、什么都没擦;会话记忆状态由本服务折;本部署的店集由配置点名的层推。
357
+ 现在:① 抹除(`POST /v1/memory/erase` 与 `POST /v1/sessions/:id/memory/erase`)交引擎的部署对象 —— 一个 `requestId` 在整个部署里只名一次请求:执行之前读遍每只店的锚,同一个 id 头一回换了选择子或执行在另一只店 ⇒ `409 memory.erasure_selector_mismatch`,
358
+ 换了授权集 ⇒ `400 config.memory_erasure_scope`,什么都没删;个人层的抹除照常执行;`select.ids` / `select.sessionId` 不带 `expectedScopes` ⇒ 每只店各擦各的那一份、收执合成一份;授权集的层分住两只店 ⇒ `400 config.memory_erasure_scope`。
359
+ ② 会话记忆状态不带层座 ⇒ 引擎的部署对象折好的一份;带层座 ⇒ 那只店上的引擎答案。③ 本部署的店集 = 盘上的(个人挂载上有店就算,配置不再点名个人层也算)。
360
+ **谁需要**:目录钉住的 file 部署上要擦个人层记忆的 operator 与会话所有者(擦得掉、重放收敛);换过 `MEMORY_SCOPE` 的部署(个人挂载上留下的那一份照样被状态数到、被按会话抹除擦掉)。
361
+ **谁受伤**:① 按 `409 memory.erasure_store_unjudged` 分支的客户端 —— 码退役,这些请求现在照常执行;② 会话自助抹除体带 `scope` ⇒ `400 request.body_shape`(7.140.0 起的层座,本版退役:抹除交整个部署,没有「落哪只店」可点);
362
+ ③ 个人挂载上有店的部署,不带层座的出处 / 清标 / 清标账请求 ⇒ `400 request.memory_layer`(此前配置不点名个人层时答在部署的店上);④ 抹除那一条只走 incident 座的码(`memory.erasure_index_residue`)的运维行名由 `memory_compliance_incident` 变为 `memory_engine_incident`;
363
+ ⑤ 嵌入方(自建 `ServiceDeps` / 直调记忆作用域店):`SessionMemoryStatusFace` 改为 `readingAt(at?) → { key, status(sessionId) }`、`MemoryComplianceFaces.erase(input)` 去掉层座参、`createMemoryScopeStores` 去掉 `namedLayers`(多一个可选 `captureRecords`)、
364
+ `foldPlaneStatuses` / `memoryStoresOfDeployment` / `memoryLayersNamedBy` / `MemoryErasureStoreUnjudged` 删除、`backendOwnsControlPlane` 改从 `memory-scope-stores.ts` 导入(TypeScript 编译红即通知;零别名)。
365
+ **补偿**:① ② 去掉对退役码 / 键的分支;③ 在请求里点名层(GET 口 `?scope=<层键>`,POST 口体 `"scope"`);④ 告警按码匹配(两种行名都带 `code`);⑤ 照新形改。记忆引擎根下多一个目录 `.memory-erasure-judge`(抹除判官锁;根本就要可写)。
366
+ **升级前做**:无。**回滚**:回到上一版即回到上一版的答法(本版个人店上执行过的抹除,其锚留在那只店里:上一版对那只店的抹除照答 409,不会重复执行)。
367
+
368
+ 37. **🔴 规则店:引擎 7.61.0 之前从用户层设置导入、从 `/` 写起的全局放行,起服即退役(一次性迁移 M36;持久数据面;行为收紧;部署零动作)**:
369
+ 旧形:引擎 7.61.0 之前的设置导入(`/v1/rules/cc-import/*`、`/v1/rules/local-import/*`)把用户层(用户配置目录下那份 settings)里 `Edit(/notes/**)` 这样从 `/` 写起的路径规则存成**全局**行。
370
+ 全局行在每个任务的**项目根**读 `/x`,用户层却在它自己的配置目录读 —— 一条本意是「配置目录下的 notes」的放行,在每个项目里放行 `notes/` 的编辑、不问。
371
+ 引擎 7.61.0 起导入不再这样存(本版第 25 条);已存的行引擎不迁、也不在读侧丢,判据交给店的属主跑。
372
+ 新形:每次起服的一次性迁移读规则桶(SQL:规则表里的每一只;local:导入记录点名过的属主的那几只),按引擎那一句判据退役:态是 `allow` ∧ add 的出处是设置导入(`imported-cc`)∧ 行的 scope 是全局 ∧ 规则在项目根读 `/x`。
373
+ **按 add 退役、不按行**:同一条规则另有人亲手同意过的那一份(`user` 出处),规则照旧生效,只少了导入那一份;deny / ask 不动(它们在项目根只收紧);`//abs`、`~/`、相对路径、非路径规则与项目 scope 的行不动。
374
+ 第二次起服零写。SQL 两库与 local(File)同一只函数、同一处写法:墓碑经这只桶自己的写面落(与撤销口同一形),删除点由本副本铸。
375
+ 读数:起服报告两键 `importedLayerRootAllowsRetired`(逐行:桶键 / 工具名 / 退役的 add 数 / 规则是否还生效;不带规则正文)与 `importedLayerRootBucketsSkipped`(闭集四词 `bucket_unreadable` / `vanished` / `not_writable` / `contended`;`vanished` = 写它的那一步里这只桶被另一副本删掉了,无可退役、不拒启);
376
+ warn `imported_layer_root_allow_retired` / `imported_layer_root_bucket_skipped`,一行一桶 / 一行一规则。
377
+ **谁需要**:用过设置导入的部署 —— 那几条放行从此不在每个项目里替人答应。
378
+ **谁受伤**:① 依赖那条错位放行的人:升级后在项目里编辑 / 读那几个目录会被问(本意本来就不是它);② 回滚到引擎 7.61.0 之前的版本后再导入:旧导入门会把同形的行再铸出来;
379
+ ③ local 车道审批记录日志损坏、或某只规则桶文件 / 规则目录的收编标记读不出(含一次 I/O 失败)的数据根:迁移读不全 ⇒ 起服拒(此前起服照常、只在规则面上拒)—— 不跳过(跳过就是让那几行照旧放行而不说);
380
+ ④ SQL 后端:窗内每次起服读一遍规则表里的每一只桶,桶多的库起服变慢;起服那一刻库读失败 ⇒ 起服拒(SQL 店的判官判为损坏的桶不在此列 —— 指纹 / 文法戳 / 行形不合,或某只 JSON 列的体读不出:点名跳过 `bucket_unreadable`、一个字节不动、接着读下一只桶,它本来就什么都不放行;一只与导入无关的坏桶不挡起服);
381
+ ⑤ local 车道:引擎的本地数据根收编把一只桶改名到没有任何导入记录点名的身份之后,这只桶迁移叫不出、不退役(已知缺口)。
382
+ **补偿**:① 真要在每个项目里放行的,以项目 scope 或 `//abs` 绝对路径重新同意一次;本意是用户配置目录的,写 `~/…`;② 再升回本版,起服把再铸的那几只 add 再退役一次(每次起服都跑);③ 按拒句点名的文件与行修好再起;④ 读失败照常重启(迁移幂等);⑤ 那只桶里导入的全局 `/x` 放行经撤销面撤掉(`DELETE /v1/rules`)。
383
+ **回滚**:墓碑是上一版撤销口写的同一形,上一版照常读;回滚不动库,被退役的那几条不回来(要回来就重新同意)。迁移码在不再支持回滚到引擎 7.61.0 之前的版本时删(窗口见登记表)。
384
+
385
+ 38. **本地导入载体(`POST /v1/rules/local-import/{prepare,redeem}`)的 deny / ask 在 prepare 当场落店 —— 收紧不等同意(行为面;wire 加性;部署零动作)**:
386
+ 旧形:三态都合成进导入层、经导入记录与票在 redeem 落;引擎 7.61.0 起导入门把用户层里从 `/` 写起的路径规则留在设置文件里,全局 `/x` 的 deny / ask 因此成员级 `not_importable`(云端不拒)。
387
+ 新形:收下的 deny / ask 按提交序经直写接缝当场落(与 `POST /v1/rules` 同一只;出处 `user`),预览成员 `{key, admitted:true, outcome:"persisted"|"no-op"}`;allow 照旧经记录与票在 redeem 落,
388
+ 本版铸的票的回执只有 allow;只有 deny / ask 的一批没有票。整工具的 deny / ask 同样当场落。一条 deny / ask 写不进店 ⇒ `503 state.rule_write_failed`(带 `committed`),这一批的 allow 全部扣下、没有票;同一批重发幂等。
389
+ **谁需要**:用载体迁移本机规则的人 —— 本机生效的收紧在云端同样生效。**谁受伤**:按回执数收紧的客户端;把 prepare 当无副作用预览的客户端;按出处 `imported-cc` 筛收紧的治理面。
390
+ **补偿**:收紧的结局读预览成员的 `outcome`;撤销走 `DELETE /v1/rules`;出处词与直写口同义(放行仍是 `imported-cc`)。滚动窗里旧版本铸的票照旧在 redeem 落它记录里的 deny / ask。
391
+
392
+ 39. **自由文本脱敏的取证读法按出处读(行为面;安全;脱敏产物可能变;运维面多一个 fail-open 标签 `server.redact.forensic-level-cap`;部署零动作)**:
393
+ — **本版**:一枚凭据不再拿自己上一轮的读数当证据,嵌套或相邻的另一枚凭据照算;一段由字符类自然停下的值盖住私钥栅栏或 URL 的 `@` 时两种读法都算、取覆盖多的那一份;
394
+ SSH2 私钥块收好之后的孤立收尾栅栏之前读作丢了起头的体(体从体长上界内最早的那一枚已收口块的终点起,体与孤立收尾那一行照遮;此前一枚记号盖到孤立收尾之后,现在各块与体各一枚);
395
+ 私钥收尾本身落在一段值里的块只在一读里收好 ⇒ 不藏、也不越过它体里的起始栅栏(那一枚照读截断律,收尾之后的体形串遮掉);证据链深过 32 环的输入从那一处遮到末尾,并记一次 `server.redact.forensic-level-cap`。
396
+ **谁需要**:日志 / 转录 / 结果里出现「赋值形凭据紧贴私钥栅栏或 URL」、URL 密码里带 `@`、或嵌套 / 相邻私钥块的部署 —— 此前这些形下私钥体或 URL 凭据的一段原样出门。
397
+ **谁受伤**:按字节比对脱敏产物的消费方(这些形遮得更多;两枚起始栅栏共用一枚收尾、其后不远另有收尾时,遮到后一枚收尾;私钥收尾被一段值盖住、其后只剩别的块自己的收尾时,遮到最后一枚;
398
+ 只看窗内的读法里私钥块的收尾只有两读的(例如落在没写完的 JSON 秘密字段里)⇒ 遮到窗尾;SSH2 块与其后的孤立收尾之间从一枚记号变成几枚,覆盖不变;
399
+ SSH2 / PEM 起始栅栏成千上万枚、共用其后同一枚收尾的输入整段遮,级数随枚数涨、32 级封顶,耗时为上一版的数倍到百倍)。
400
+ **补偿**:无开关(凭据轴只收紧);done 帧与事件回放同一份产物可作比对基准;到顶的输入看 `server.redact.forensic-level-cap` 计数(每次脱敏至多一次)。
401
+ **升级前做**:无。**回滚**:回到上一版 ⇒ 读法回到旧形,上述形下的那一段重新原样出门;无持久数据。
402
+
403
+ 40. **🔴 审批卡的断连重放帧与活铸帧同事实:问行卡信封(SQL `approval_ask.card_json` / File 问账的同一份信封)加一座 `liveFacts`,断连重放的 `approval_request` 帧带活铸帧顶层的同一组卡外事实;本版之前铸的行重放帧带 `liveFactsUnknown: true`(安全轴;wire 加性;持久数据面:存形加性,零 DDL;🔴 多副本先停光旧副本再让本版接流量)**:
404
+ — **本版**:铸卡时活铸帧顶层的十一位卡外事实(`mandated` / `mandate` / `origin` / `boundGrantDigest` / `boundGrants` / `classifierNoRuling` / `boundGrantAbsence` / `approvalCard` / `readRootCandidate` / `probeReason` / `requestedBy`)
405
+ 与卡同一条写落进卡信封的 `liveFacts` 座(在场才拷;一位都不在场时是空座;这组事实里有 NUL / 孤代理 ⇒ 不落座,行读作「不知道」—— PostgreSQL 问店存不下逐字的那一份)。
406
+ 断流续读、开流 preamble、起服接管行与按问读口内嵌的帧只读这一座 ⇒ 行上记着的每一位与活铸帧逐字同值;起服接管行顶层的 `mandated` / `mandate` / `origin` 读内嵌帧,
407
+ 行上没有座 ⇒ 行顶层同带 `liveFactsUnknown: true`。同一只问被另一腿再问(重启续跑、另一只副本)时,重入那一腿的活铸帧与列表行采信行上这一座;
408
+ 「本会话放行」的判据只收窄(强制位取两边之并、授予绑定两边一致才认)(行上没有座 ⇒ 这一腿的读数照旧)。
409
+ 行上没有这一座(本版之前铸的行)或它读不出 ⇒ 重放帧带 `liveFactsUnknown: true`、十一位一位不带;这时的缺席不是断言,按强制 / 未知渲。活铸帧恒不带它。
410
+ — **不回填**:存量行不改写、不迁移;待决的问被答掉或到期即离开重放集,存量随 ask 期限收敛。零 DDL(卡信封是不透明列)。
411
+ — **滚动升级窗**:上一版的卡信封读者是 `.strict()` 的,读不动带这一座的行。上一版副本碰到本版写下的行:断连重放跳过那只卡(一次 warn)、幂等重入走 park、回决口按「要人」读(拒策略放行)、
412
+ 持久规则兑付答素材缺席(规则不落)。都是收紧方向,不会错放;本版副本读上一版写下的行 ⇒ 存量行形(`liveFactsUnknown`)。
413
+ **谁需要**:断线重连的壳与控制台 —— 重连之后的卡与断开前同一张;只经 preamble 到达的后台子代的问第一次带全开卡事实。
414
+ **谁受伤**:滚动升级窗里由上一版副本服务的问(上一句的四形);把重放帧上的缺席读成「不是 X」的消费方,在存量行上读到 `liveFactsUnknown`。
415
+ **补偿**:读 `liveFactsUnknown` 分支(在场 ⇒ 按强制 / 未知渲);上一版 server 既不带事实也不带标记,按服务版本分支。
416
+ **升级前做**:先停光上一版副本再让本版接流量(k8s 多副本用 `Recreate`,或先把旧副本摘出负载均衡)。只排空待决的问不够 —— 本版副本新写的问,还在服务的旧副本照样读不动。
417
+ 不这样做也可以:窗里旧副本上的四形都是收紧方向、不会错放,代价是那些卡在旧副本上重连看不到、走 park。
418
+ **回滚**:回到上一版 ⇒ 本版写下的、仍待决的问在旧副本上落滚动升级窗那四形(走 park / 人答);local 车道起服收敛读不出这些行的卡信封,成因词落 `crashed_before_park`(一次兜底计数);
419
+ 旧副本新铸的行回到无座形,再升回本版即读作存量行。
420
+
421
+ 41. **🔴 自由文本脱敏的名字面:复合凭据名在自由文本的声明形里是名字锚;名字的左界止于前一枚已认出的凭据的终点;不带引号的值里面的赋值照问(行为面;安全;脱敏产物变;带这些赋值的命令不再给常驻放行;部署零动作)**:
422
+ — **本版**:① `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` 同档:
423
+ 在自由文本 `名=值` / `名: 值` 的声明形(名字带引号、值带引号,或赋值符两侧零空白)上,值是以字母数字起头的字面量时遮(`$VAR` / `%s` / `<占位>` / 表达式 / `None` 不算值)。
424
+ ② 名字的左界止于前一枚已认出的凭据(或可读的结构块)的终点:强锚名紧贴在前一枚凭据的尾巴上(`-----END PRIVATE KEY-----password=…`;PGP 收尾栅栏、PuTTY 的 `Private-MAC:` 行、AWS 键 id 同形,中间无分隔)时首遍即遮、再过一遍读数不变 ⇒
425
+ 流式 `done` 帧、同步体、运维日志行、活流逐块帧、`GET /v1/runs/:id` 的 run 行与 `GET /v1/runs/:id/events` 回放里的 `text` / `reasoning` / `done` / `tool_end` 行同读(此前单遍口明文、多遍口被遮,见第 16 条)。
426
+ ③ 每个赋值符恰问一次,值怎么读不取决于它在不在另一只值里:不带引号的值里面的赋值照问 —— `api_key=abc,password="my secret value"` 里两个值都遮,强锚的不带引号值里夹着的 `…?Sig=password="n "<尾段>` 尾段也遮(此前外层不带引号的值把里面的赋值藏住,里面那一只不认)。
427
+ 常驻放行的命令素材闸读同一只脱敏器:命令里带 ① 这类赋值(`curl … -d api_key=<字面量>`)⇒ 判为带凭据,不给常驻放行。
428
+ — **不变**:JSON 字段与 URL 参数位的读法(表里每个名字照旧都是锚);`token` / `key` / `auth` / `sig` / `signature` / `sas` 在自由文本里仍只是常用词(只在结构位置上是锚);
429
+ 赋值符两侧带空白的不带引号形(`api_key = abc`)不是声明形;名字按整名认(`DB_PASSWORD` / `foo-api_key` 是另一个名字)。
430
+ — **存量行**:回放读面对 run 行与 `text` / `reasoning` / `done` 行再过一遍脱敏 ⇒ 升级前落下的这些行按本版读法遮;`tool_end` 行回放读面不再过(只剥媒体字节)⇒ 升级前写下的这几形照旧明文,升级后新写的行照遮。
431
+ — **仍不遮**:名字整个落在前一枚已认出凭据的值里(以字母数字收尾的令牌后面紧贴强锚名,`ghp_…password=<值>`)时令牌照遮、`<值>` 明文,各出口同读;中间有分隔照遮。
432
+ **谁需要**:prompt / 工具输出 / 日志 / 结果里出现这些赋值形的部署 —— 此前 `api_key=<字面量>` 一族在自由文本里原样出门,紧贴形在单遍口上原样出门。
433
+ **谁受伤**:① 关键字参数透传(`f(api_key=api_key)`)的变量名换成记号;② 带这些赋值的命令不再给常驻放行(每次问人);③ 脱敏计数上升;④ 不带引号的值里面的赋值现在也遮(上一版跳过),外层值与里面那一只相交时两枚记号相邻、里面那一只的名字随外层记号盖住;⑤ 源码与配置片段里的对象字面量(`apiKey: "…"` / `access_token: "…"`;名字大小写不敏感,值带引号即声明形)的值换成记号,测试夹具与示例代码里的假值同样;⑥ 赋值符密集的长输入脱敏变慢,仍与长度同阶(每几个字符一个赋值符的 40 万字符输入约为上一版的 2–8 倍,约 0.1 s;散文与长值不变);⑦ 这些名字后面、本来由值自身形状认出的值(`api_key=ghp_…` / `access_token=AKIA…` / `API_KEY=sk-…`)记号从 `«redacted:<种类>»` 变成强锚值的 `«redacted»`(与 `password=` 同读),`redactions_applied_total` 的 `pattern` 随之由种类名移到 `redacted`;
434
+ 按字节比对脱敏产物的消费方在这几形上读到差。
435
+ **补偿**:赋值符两侧带空白(`api_key = x`)不是声明形、不作锚(既有判据);新遮的值记在 `redactions_applied_total{pattern="redacted",arm="full"}`(与 `password=` 一族同一格),按 `arm` 维与弱证据臂分开读;
436
+ 无开关(凭据轴只收紧);done 帧与事件回放同一份产物可作比对基准。
437
+ **升级前做**:无。**回滚**:回到上一版 ⇒ 上述几形回到上一版读法(复合凭据名在自由文本里只靠值自身形状、紧贴形在单遍口上明文、不带引号的值里面的赋值不问);无持久数据。
438
+
439
+ 42. **运行时依赖 `@modelcontextprotocol/server` 2.4.0(npm 消费方安装时一并装上;部署零动作;镜像里的回声 MCP 可作探针)**:
440
+ — **此前**:镜像里的回声 MCP 脚本(`/app/scripts/mcp-echo.mjs`)借引擎带进安装树的 MCP SDK v1 跑;本包 `dependencies` 里没有 MCP 服务端包。
441
+ — **本版**:引擎 7.61.0 不再把 SDK v1 带进安装树,回声脚本改依赖本包自己声明的 `@modelcontextprotocol/server` 2.4.0(`package.json` 的 `dependencies`);
442
+ 镜像照旧把脚本拷到 `/app/scripts/mcp-echo.mjs`。`npm install @sema-agent/server` 一并装上这只包;服务进程本身不加载它(只有回声类脚本用:镜像回声 + `scripts/drill/mcp-principal-echo.mjs`)。
443
+ **谁需要**:用镜像里的回声 MCP(stdio,`bun /app/scripts/mcp-echo.mjs`;工具 `echo` / `env_check`)探 MCP 接线与 `envRefs` 注入的部署。
444
+ **谁受伤**:只装 npm 包的消费方多装一只依赖 —— 这只包 7 023 377 字节、117 个文件(其中源图 4 305 531 字节);它的两只依赖 `@modelcontextprotocol/core` 2.4.0 与 `zod`(^4.2.0)
445
+ 已随引擎 / 契约包在安装树里(去重),净增只有这一只包。npm 包不带回声脚本(`files` 不含 `scripts/mcp-echo.mjs`),这只依赖在 npm 安装里没有读者。
446
+ **补偿**:无(本版不拆成可选依赖)。**升级前做**:无。**回滚**:回到上一版 ⇒ 依赖表回到上一版(上一版的回声脚本读引擎带进来的 SDK v1);无持久数据。
447
+
448
+ 43. **更正(7.141.0 节「谁受伤 ①」,文书;零行为变化)**:`DB_BACKEND=local` 上,回滚到上一版后上一版读本版写过的 `session-incarnations.json` 不是「严格拒启」—— 严格读在第一次用到那只边档时才拒(`ensureLoaded`),
449
+ 所以读不出的那一形(≤ 7.140.0 的副本附着 ≥ 7.141.0 写下的数据根 —— 7.141.0 起每格多 `createdAt` / `forkedFrom` 两键;本版边档形零改,7.141.0 / 7.142.0 读得了本版写的根)用户面是每个碰到它的请求答 500 `internal.error`(引擎日志:边档 malformed),服务进程本身起得来。规则不变:**不许降级附着;回滚 = 恢复升级前的数据根备份**。
450
+ 「数据根比代码新 ⇒ 起服拒 + 机器码」(与 SQL 后端 `config.schema_mismatch` 同一条律)另票、不在本版。无三问(更正条)。
451
+
10
452
  ## 7.142.0 —— `onError` 汇点的日志行一律带引擎错自带的机器码 `code`(码须合码形,否则省略;记忆事故行与起服面三只引擎座的无码占位改为键缺席;`mcp_server_unavailable` / `a2a_peer_unavailable` / `task_degraded` / `prompt_composition_warning` / `prompt_cache_break` / `runner_error` 多结构化字段;运维面;日志形加性) · 一次性兜底 warn 由 stderr 裸文本行改为一行 JSON 日志(`msg` 逐字 `[sema] fail-open: <tag>`,字段 `{ tag, count }`;运维面;日志形变更) · 订正 7.139.0 节第 16 条:停驻赎回在认领之前判卡的主体的车道裁定,不被许可本部署车道的主体对停驻卡的 approve / deny 答 409 `resume_blocked_by_policy`(文字订正;零行为变更) · 折码的运维线索:读面上的再读改走 fail-open 的计数与一次 warn、长得像码的秘密报 `<not-a-code>`(订正 7.141.0 节第 30 条读面那一半;运维面;日志形变更;安全) · 起服面运维行里的异常句改经出口座:凭据形遮掉、控制字符剥掉、界 200 码元(运维面;日志形变更;安全) · 会话列表不再漏掉存档读不出的会话:行带加性键 `unreadable: { errorCode }`(行为面;wire 加性) · 🔴 记忆同步中心必须声明同步身份 `MEMORY_SYNC_SERVE_IDENTITY`(没声明 ⇒ 同步口 501);中心侧同步口拒不是同步身份的 `peer`(400)与不是本中心的 `peer`(409)(BREAKING 部署面;升级序先客户端后中心;wire 加性) · 会话记忆同步触发口 `POST /v1/sessions/:id/memory/sync`(wire 加性);同步一轮从不整轮失败、客户端侧轮级日志行 `memory_sync_failed` 退役(运维面;中心同步口答 500 的那一臂照旧发同名行);同步层名带凭据形拒启(行为收紧);`ServiceDeps.memorySyncLeg` 多要两成员(嵌入方类型面 BREAKING) · 隔离车道的附件改由恢复点放进工作区(行为修复) · 项目包:`application/vnd.sema.project-bundle` 上传、绑进隔离车道的会话作为工作区的种子(wire 加性:一个受理的 mime 值 + 四个新拒码;嵌入方 `ServiceDeps.taskAttachmentStore` 类型面 BREAKING) · 附件一经绑定不再改绑(行为收窄;`409 attachment.bound_elsewhere`) · local 车道有序关停截断的审批卡:`crashConverged[].cause` 与决断口同一个词,前世 `PARKING` 孤儿起服即 `VOID`(行为面;wire 值变化;零新键、零新词;审计收敛行写不进起服拒,行为收紧) · 嵌入方(自建 `StoreBackend`):`StoreBackend.checkpointReadView()` 多交一口 `findCheckpointCandidatesForAsk()`(BREAKING,类型面;零别名) · 结果上组合腿的自由文本过脱敏(`verification.findings[]` / `evidence`、`attempts[].diagnostics` / `gateError`;completed 因由的 `finishReasonUnknown` 过回声形状闸;行为面;安全;wire 值可能变) · `GET /v1/runs/:id/events` 的停驻尾帧一律是状态帧:挂起前的 `model_usage` 行写在 `suspended` / `needs_review` 之前,`verify:true` 的停驻与不带 `verify` 的停驻同序(行为面;wire 事件序;订正 7.141.0 节第 32 条 ①) · 同键重放的同步体改成首答的信封 + `replay: true`(BREAKING 对按重放形解析的消费方;wire 形;订正 7.141.0 节第 32 条 ②) · http hook 名单在拨出点执行:`settings.allowedHttpHookUrls` 不放行 ⇒ http hook 不发出、`settings.httpHookAllowedEnvVars` 与条目 `allowedEnvVars` 取交(安全;行为面;wire 加性) · 🔴 问账行成因拆成两列:SQL 后端 `approval_ask.denied_reason` 改名 `ending_cause`、加列 `park_trigger`,local 问账日志整本重写一次(本服务起服时自己改;升级前备份、先停光旧副本;回滚 = 恢复备份或手工三句;嵌入方类型面 BREAKING;wire 零变化) · 沙箱 git 平面接上有效配置块 `sandboxGit`(中心投影、热生效;坏块读作没有 origin + 一行 warn `web_admin_sandbox_git_refused`;本版平面读数不变;运维面) · run 行记下令牌化身号(零 DDL;部署零动作) · `ServiceDeps.sandboxGitPlane` 由值改为读函数(嵌入方类型面 BREAKING) · 自由文本脱敏的两处已知漏形写明:复合凭据名(`api_key=` 一族)的值、紧贴前一枚凭据尾巴的强锚值(安全披露;零行为变更) · 用户面通告多一键 `eventId`、live 帧带 `id:` 行;有账本口的腿断流后通告与文件卡的死直写不再记丢帧,文件卡账本孪生写失败改记计数(wire 加性;运维面计数口径变化)
11
453
 
12
454
  1. **`onError` 汇点的日志行一律带引擎错自带的机器码 `code`(运维面;日志形加性,记忆事故行与起服面三只引擎座的无码占位除外;部署零动作)**: