@sema-agent/server 7.141.0 → 7.142.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 (171) hide show
  1. package/MIGRATION.md +215 -0
  2. package/USAGE.md +67 -12
  3. package/dist/approval-ask-audit-store.d.ts +54 -38
  4. package/dist/approval-ask-audit-store.js +58 -39
  5. package/dist/approval-card.d.ts +8 -8
  6. package/dist/approval-crash-convergence.d.ts +17 -7
  7. package/dist/approval-crash-convergence.js +94 -22
  8. package/dist/approval-deny-reasons.d.ts +34 -30
  9. package/dist/approval-deny-reasons.js +21 -6
  10. package/dist/approval-policy-settlement.d.ts +1 -1
  11. package/dist/approval-reconciler.d.ts +33 -1
  12. package/dist/approval-reconciler.js +43 -40
  13. package/dist/auth-bridge.d.ts +9 -6
  14. package/dist/auth-bridge.js +9 -3
  15. package/dist/boot/adoption.js +2 -1
  16. package/dist/boot/budget-tracing.d.ts +16 -0
  17. package/dist/boot/budget-tracing.js +10 -6
  18. package/dist/boot/device-lane.d.ts +1 -1
  19. package/dist/boot/execution-env.d.ts +24 -4
  20. package/dist/boot/execution-env.js +26 -23
  21. package/dist/boot/execution-lane.js +2 -1
  22. package/dist/boot/exit-boundary.js +5 -4
  23. package/dist/boot/memory-consolidation.js +2 -1
  24. package/dist/boot/parked-revive-gate.js +2 -1
  25. package/dist/boot/permission-rules-audit.d.ts +0 -28
  26. package/dist/boot/permission-rules-audit.js +2 -1
  27. package/dist/boot/read-deny-anchor-audit.js +2 -1
  28. package/dist/boot/reapers.js +4 -3
  29. package/dist/boot/resolve-spec.js +11 -2
  30. package/dist/boot/retention-lane.js +5 -3
  31. package/dist/boot/runner-deps.js +16 -13
  32. package/dist/boot/runtime-caps.js +3 -2
  33. package/dist/boot/session-faces.js +4 -3
  34. package/dist/boot/shutdown.js +3 -2
  35. package/dist/boot/stage-04-stores.js +3 -2
  36. package/dist/boot/stage-05-execution-env.d.ts +1 -1
  37. package/dist/boot/stage-05-execution-env.js +8 -6
  38. package/dist/boot/stage-06-runners.d.ts +1 -1
  39. package/dist/boot/stage-06-runners.js +2 -1
  40. package/dist/boot/stage-07-capability-layer.d.ts +1 -1
  41. package/dist/boot/stage-07-capability-layer.js +2 -1
  42. package/dist/boot/stage-08-reapers.d.ts +1 -1
  43. package/dist/boot/stage-08-reapers.js +3 -2
  44. package/dist/boot/stage-09-leader.d.ts +1 -1
  45. package/dist/boot/stage-10-http-server.d.ts +1 -1
  46. package/dist/boot/stage-10-http-server.js +5 -4
  47. package/dist/boot/stores.js +3 -2
  48. package/dist/boot/web-admin.js +17 -17
  49. package/dist/boot/webfetch-summarize-lane.js +2 -1
  50. package/dist/boot/workflow-orchestration.js +3 -2
  51. package/dist/capabilities/builtin-tools.d.ts +2 -1
  52. package/dist/capabilities/builtin-tools.js +2 -4
  53. package/dist/capabilities/composite-members.d.ts +2 -1
  54. package/dist/capabilities/composite-members.js +2 -1
  55. package/dist/capability-facts.d.ts +24 -0
  56. package/dist/capability-facts.js +23 -0
  57. package/dist/config-catalog.js +1 -0
  58. package/dist/config-types.d.ts +15 -4
  59. package/dist/config.d.ts +6 -0
  60. package/dist/config.js +17 -3
  61. package/dist/execution-lane-caps.d.ts +2 -1
  62. package/dist/fleet/fleet-bus.d.ts +6 -3
  63. package/dist/hooks/hook-runner.d.ts +6 -0
  64. package/dist/hooks/hook-runner.js +30 -1
  65. package/dist/http/dispatch.js +4 -2
  66. package/dist/http/execution-context.d.ts +2 -2
  67. package/dist/http/idempotency.d.ts +1 -1
  68. package/dist/http/principal-gate.d.ts +3 -1
  69. package/dist/http/principal-gate.js +17 -8
  70. package/dist/http/resume-legs.js +5 -9
  71. package/dist/http/route-table.js +2 -0
  72. package/dist/http/routes/attachments.js +18 -0
  73. package/dist/http/routes/capabilities.js +5 -2
  74. package/dist/http/routes/memory-policy.js +26 -6
  75. package/dist/http/routes/runs.js +3 -3
  76. package/dist/http/routes/session-memory-sync.d.ts +63 -0
  77. package/dist/http/routes/session-memory-sync.js +157 -0
  78. package/dist/http/routes/sessions-list.d.ts +2 -1
  79. package/dist/http/routes/tasks.js +7 -5
  80. package/dist/http/run-read-face.d.ts +7 -0
  81. package/dist/http/run-read-face.js +9 -3
  82. package/dist/http/send.d.ts +5 -0
  83. package/dist/http/send.js +9 -2
  84. package/dist/http/server.d.ts +14 -8
  85. package/dist/http/server.js +2 -4
  86. package/dist/http/submit-replay.d.ts +4 -3
  87. package/dist/http/submit-replay.js +2 -2
  88. package/dist/http/sync-submit-body.d.ts +21 -4
  89. package/dist/http/sync-submit-body.js +13 -0
  90. package/dist/identity-source.d.ts +21 -2
  91. package/dist/identity-source.js +6 -0
  92. package/dist/memory-incident-sink.d.ts +19 -6
  93. package/dist/memory-incident-sink.js +8 -4
  94. package/dist/memory-sync-client.d.ts +21 -21
  95. package/dist/memory-sync-client.js +50 -42
  96. package/dist/memory-sync-identity.d.ts +24 -0
  97. package/dist/memory-sync-identity.js +57 -0
  98. package/dist/memory-sync.d.ts +7 -1
  99. package/dist/memory-sync.js +6 -3
  100. package/dist/observability/fail-open.d.ts +27 -10
  101. package/dist/observability/fail-open.js +20 -7
  102. package/dist/observability/run-terminal.d.ts +92 -34
  103. package/dist/observability/run-terminal.js +167 -162
  104. package/dist/plugins/approval-ask-store-file.d.ts +14 -4
  105. package/dist/plugins/approval-ask-store-file.js +48 -7
  106. package/dist/plugins/approval-ask-store-memory.d.ts +2 -1
  107. package/dist/plugins/approval-ask-store-memory.js +9 -8
  108. package/dist/plugins/approval-ask-store-sql.d.ts +42 -10
  109. package/dist/plugins/approval-ask-store-sql.js +49 -19
  110. package/dist/plugins/file-run-store.d.ts +6 -1
  111. package/dist/plugins/file-run-store.js +11 -1
  112. package/dist/plugins/local-session-store.d.ts +36 -7
  113. package/dist/plugins/local-session-store.js +76 -32
  114. package/dist/plugins/local-task-attachment-store.d.ts +12 -2
  115. package/dist/plugins/local-task-attachment-store.js +67 -43
  116. package/dist/plugins/memory-key-guards.d.ts +3 -3
  117. package/dist/plugins/memory-run-store.d.ts +6 -1
  118. package/dist/plugins/memory-run-store.js +11 -1
  119. package/dist/plugins/one-time-migrations.d.ts +27 -1
  120. package/dist/plugins/one-time-migrations.js +57 -3
  121. package/dist/plugins/remote-env-e2b.d.ts +23 -4
  122. package/dist/plugins/remote-env-e2b.js +111 -28
  123. package/dist/plugins/remote-env-k8s.d.ts +31 -5
  124. package/dist/plugins/remote-env-k8s.js +86 -19
  125. package/dist/plugins/remote-env-local-docker.d.ts +15 -3
  126. package/dist/plugins/remote-env-local-docker.js +40 -12
  127. package/dist/plugins/run-store-sql.d.ts +9 -1
  128. package/dist/plugins/run-store-sql.js +9 -1
  129. package/dist/plugins/store-backend.d.ts +10 -7
  130. package/dist/plugins/store-backend.js +1 -0
  131. package/dist/plugins/store-contracts.d.ts +26 -1
  132. package/dist/plugins/store-contracts.js +3 -0
  133. package/dist/plugins/task-attachment-store.d.ts +112 -14
  134. package/dist/plugins/task-attachment-store.js +221 -28
  135. package/dist/plugins/workspace-fill.d.ts +60 -0
  136. package/dist/plugins/workspace-fill.js +106 -0
  137. package/dist/project-bundle.d.ts +38 -0
  138. package/dist/project-bundle.js +609 -0
  139. package/dist/run-local.js +2 -8
  140. package/dist/runs.js +1 -2
  141. package/dist/sandbox-egress-policy.d.ts +4 -1
  142. package/dist/sandbox-egress-policy.js +3 -1
  143. package/dist/sandbox-git-plane.d.ts +31 -12
  144. package/dist/sandbox-git-plane.js +24 -4
  145. package/dist/security.d.ts +9 -0
  146. package/dist/security.js +8 -0
  147. package/dist/session-error-wire.d.ts +24 -0
  148. package/dist/session-error-wire.js +7 -0
  149. package/dist/task-settings.d.ts +10 -2
  150. package/dist/task-settings.js +15 -2
  151. package/dist/terminal.d.ts +39 -19
  152. package/dist/terminal.js +65 -22
  153. package/dist/tool-approval.d.ts +4 -3
  154. package/dist/tool-approval.js +12 -14
  155. package/dist/trace/core-keyset-guard.d.ts +123 -6
  156. package/dist/trace/engine-notice-wire.d.ts +8 -2
  157. package/dist/trace/engine-notice-wire.js +4 -2
  158. package/dist/trace/exit-seat.d.ts +2 -1
  159. package/dist/trace/ledger-sink.d.ts +8 -4
  160. package/dist/trace/ledger-sink.js +2 -2
  161. package/dist/trace/project.d.ts +18 -1
  162. package/dist/trace/project.js +16 -0
  163. package/dist/trace/self-minted-wire.d.ts +251 -2
  164. package/dist/trace/self-minted-wire.js +82 -0
  165. package/dist/trace/wire-projection-faces.d.ts +1 -1
  166. package/dist/trace/wire-projection-faces.js +1 -1
  167. package/dist/web-admin-client/apply-effective.js +21 -1
  168. package/dist/web-admin-client/effective-keys.js +2 -0
  169. package/dist/web-admin-client/types.d.ts +4 -0
  170. package/package.json +1 -1
  171. package/wire-changes/server@7.142.0.json +639 -0
package/MIGRATION.md CHANGED
@@ -7,6 +7,221 @@
7
7
  > ⚠️ 完整清单在仓库根 `CHANGELOG.md`——它**不随 npm tarball 出包**(本文件随包)。看完整迁移窗的
8
8
  > 权威姿势是源码 tag diff:`git diff v<旧>..v<新>`(每版都推 `v<版本>` tag);npm 包页也镜像 CHANGELOG。
9
9
 
10
+ ## 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
+
12
+ 1. **`onError` 汇点的日志行一律带引擎错自带的机器码 `code`(运维面;日志形加性,记忆事故行与起服面三只引擎座的无码占位除外;部署零动作)**:
13
+ 引擎把「运维该按码告警」的事交给本服务时,码挂在错对象上(`err.code`),句子里不一定有那串码 —— 例:MCP 服务器没申报 `tools` 能力而部署声明的 `allowTools` 非空,引擎铸 `mcp.tools_capability_absent`,句子只说 `declared no "tools" capability`。
14
+ 此前只有 `runner_config_advisory` 一条行把码写成结构化字段;`mcp_server_unavailable` / `a2a_peer_unavailable` / `task_degraded` / `prompt_composition_warning` / `prompt_cache_break` / 兜底 `runner_error` 把码折进 `info` / `err` 散文里。
15
+ 现在这七条行同一条律:错带非空串 `code`、且它**是码**(不超过 64 字符的短 token:字母、数字与 `_ . : -`,并且秘密脱敏器原样放过它 —— `sk-…` / `AKIA…` / `ghp_…` 这类长得像码的凭据不是码)⇒ 行里多结构化字段 `code`(逐字);读不出(没有 / 不是串 / 空串 / 读取时抛)或不是码 ⇒ 该键缺席,不补占位。
16
+ 「是不是码」只问一只判官(`codeFieldOf` 里的读口 + 判官;折码运维线索与执行车道占位读的故障词读的是同一只):`code` 是别人写的串,一句话或一把凭据都能挂在上面;wire 上同一个值在 message 尾注里被脱敏器遮掉,日志行与指标标签若照印就是日志露、wire 遮。`rewind_snapshot_failed` 早已带码,形不动。记忆引擎事故行(`memory_engine_incident` 与计数 `memory_engine_incident_total`)读同一只判官:此前它自写一份读口(码是什么都照写,无码写 `code:null` / 标签 `code="none"`),现在是码才带,无码或不是码 ⇒ `code` 键与 `code` 标签缺席,不再写 `null` / `none`。起服面三只 operator 面引擎座(`memory_compliance_incident` / `memory_origin_incident` / `memory_consolidation_incident`)同判官:此前各写一份 `code: err.code ?? null`,现在三座共用一只落点工厂、`code` 位读同一只判官,无码或不是码 ⇒ 键缺席;事件名与 `detail` 句不变。子代额外工具故障行 `subagent_extra_tools_error` 的 `code`(引擎的 `config.extra_tool_names`,或部署工厂自己抛的错)同判官:此前是串就原样进行,现在不是码 ⇒ 键缺席。`info` / `err` 句的级别与计数器一字未动;句文本已按本节第 5 条改走运维文本座(凭据形遮掉、控制字符剥掉、界 200 码元),不再是抛出物原文。
17
+ 读面收紧(`runner_config_advisory` 行与它的指标标签 `code`):码是空串、不是码(超长 / 含空白 / 凭据形)、或抛出物不是 `Error` 实例时,此前写成 `code:""` / 原样的串 / 读抛出物上的串码,现在键缺席(与其余六条同一读口、同一判官)。已读到的引擎告警码(`config.*` / `mcp.*`)都是 `Error` 实例上的短 token 非空串,不受影响。
18
+ **谁需要**:按码(而不是按句子)路由引擎运维告警的日志管道。**谁受伤**:无(加性字段);只有按 `code:""`、按「非 `Error` 抛出物上的 `code`」、按「不是码的串」(超长 / 含空白 / 凭据形),按记忆事故行的 `code:null` / 计数标签 `code="none"`,按起服面三只引擎座的 `code:null`,或按 `subagent_extra_tools_error` 里不是码的 `code` 串匹配的规则会失配。**出路**:告警规则改按 `code` 字段匹配;无需动作(要找「没有码的记忆事故」或「没有码的起服面引擎座事件」就按 `code` 键 / 标签缺席匹配)。**回滚**:回到上一版即回到旧行形。
19
+
20
+ 2. **一次性兜底 warn(每进程每 tag 一次)由 stderr 裸文本行改为一行 JSON 日志(运维面;日志形变更;部署零动作)**:
21
+ 此前 `[sema] fail-open: <tag>` 作为**裸文本**直写 stderr,而日志器写 stderr 的日志都是 JSON 行 —— 把 stderr 当 JSON 行流收的日志管道,在每个 tag 第一次走兜底时撞上一行解析不了的文本。
22
+ 现在同一句话(逐字)做一行 JSON 日志的 `msg`:`{"ts":…,"level":"warn","msg":"[sema] fail-open: <tag>","tag":"<tag>","count":<n>}`,字段闭集 `{ tag, count }`(`count` = 此刻为止该 tag 的累计命中数)。这一行不随 `LOG_LEVEL`(级别钉在 warn)、
23
+ 不依赖日志器装配(boot 之前的兜底同样出),仍是每进程每 tag 一次;这一行不再是裸文本(stderr 上别处的非 JSON 文本,如引擎的 `process.emitWarning`,不在本条范围)。结构化 `fail_open` 日志行、`fail_open_total{tag,cls}` 指标、`SEMA_FAILOPEN_PROBE` 探针文件三件一字未动。
24
+ **谁需要**:把 stderr 当 JSON 行流收的日志管道;按文字子串找这一行的黑盒断言(文字没变,照读)。**谁受伤**:锚住整行 / 行首的匹配(`^\[sema\] fail-open:`)会失配。**出路**:改按子串 `[sema] fail-open: <tag>` 或 JSON 的 `msg` 字段匹配;要计数读 `fail_open_total{tag}`。**回滚**:回到上一版即回到裸文本行。
25
+
26
+ 3. **停驻赎回在认领之前判卡的主体的车道裁定:不被许可本部署车道的主体,对停驻卡的 approve / deny 答 `409 resume_blocked_by_policy`(7.139.0 起的行为,当时未披露;本条是文字订正,零行为变更)**:
27
+ `POST /v1/approvals/:sessionId/decide` 复活停驻的后台子代之前,走与续跑家族同一只执行上下文解析器:根会话行那一格 → 车道 → **卡的主体**(停驻那一刻 checkpoint 上的主体,不是发决定的人)的车道裁定。
28
+ 中心对这位主体的裁定 `required: true` 而 `allowedLanes` 不含本部署的车道 ⇒ `409 resume_blocked_by_policy`,体 `blockedBy: "execution_lane_not_allowed"` · `allowedLanes` · `lane` · `retriable: true`;
29
+ 能复活的停驻行上 approve 与 deny 同答(deny 也要复活子代、让它在这条车道上读到拒绝再往下跑);停驻行不被认领、卡仍待决、被门的工具零执行。7.138.x 及以前这一形照常复活。
30
+ 复活不了的停驻行(行上没有根会话锚)在这一判之前分流、不判车道:approve 照旧 `409 decide.row_unrevivable`,deny 照旧作废这张卡(200)。
31
+ 续跑家族的任务级入口(`/answer` · `/plan_review` · `/wake` · 任务级 `/decide`)早已同判同折,码与体不变(7.139.0 节第 16 条的「这些拒」包括这一只)。
32
+ **谁受伤**:中心在任务停驻之后收窄了某位主体车道的部署 —— 那位主体停驻的卡决不动;SLA 拒扫不替停驻卡代决;拒句尾点名的 `POST /v1/runs/:id/cancel` 只结算 run 行,停驻的后台子代没有 run 行。
33
+ **出路**:在中心把本部署的车道放回这位主体的 `allowedLanes` 再决(卡一直待决,放回之后照常决);不打算放回 ⇒ 会话属主或 operator 发 `POST /v1/sessions/:id/children/retire`,
34
+ 结清以该会话为根的全部后台子代(停驻的作废卡、在跑的停掉;是整棵树,不是单张卡)。
35
+ **升级前做**:无。**回滚**:无需(只改文字)。
36
+
37
+ 4. **折码的运维线索(订正 7.141.0 节第 30 条读面那一半):读面上的再读不再出 `[terminal] outward_failure_code_folded` 行,改计数 + 每进程一次 warn;长得像码的秘密报 `<not-a-code>`(运维面;日志形变更;安全;部署零动作)**:
38
+ 7.141.0 节第 30 条说,读面上的再读(`at=run.result` / `at=ev.done.result`)同一读面同一原码每进程只一行(表有界,1024 条)。本版起这两种行不再出现 ——
39
+ 读面上的再读按**被折的码座**计数:`fail_open_total{tag="server.terminal.stored-result-code-folded",cls="F"}` 每折一座加一(一次读里,因由码 `terminal.code` 一座、cascade 每一级的 `attempts[].errorCode` 各一座 —— 折了三座就加三,不是加一);每进程一次结构化 warn `fail_open`(`tag` 同上,`detail` = 第一次被折的那一座的字段句
40
+ `original=<码 | <not-a-code>> outward=internal.store_error messageBytes=<n> at=<run.result | ev.done.result>`)与一次性兜底 warn 那一行(`msg` = `[sema] fail-open: server.terminal.stored-result-code-folded`,行形见第 2 条);
41
+ 配了 `SEMA_FAILOPEN_PROBE` 的部署,探针文件每折一座一行。`at=exit` 那一行(结果第一次离开引擎,每折一座一行)不变。
42
+ 另:码形之内、却被秘密脱敏器认作凭据形的码(`sk-…` / `AKIA…` 之类)在 `original=` 处报 `<not-a-code>`,不再报原值(写口那一行与读面的 `detail` 同判)。
43
+ **谁受伤**:按 `[terminal] outward_failure_code_folded` 且 `at=run.result` / `at=ev.done.result` 写的告警 / 看板(那两种行不再出现);按 `original=` 原值聚合、而那个值本就是秘密的规则(改报 `<not-a-code>`)。
44
+ **出路**:读面上的再读改看计数器 `fail_open_total{tag="server.terminal.stored-result-code-folded"}`(或 warn `fail_open` 的 `tag`);要逐座明细就开探针文件。**回滚**:回到 7.141.0 即回到那两种行(仍不带失败句原文)。
45
+
46
+ 5. **起服面运维行里抛出物的句子经出口座:凭据形遮掉、控制字符剥掉、界 200 码元(运维面;日志形变更;安全;部署零动作)**:
47
+ 起服面(装配、起服自检、关停收割、配置拉取与应用、保留期清扫、交给各店 / 车道的失败回调)运维日志里装异常句的字段(`err` / `error` / `detail` / `info` / `lastError` / `why`,事件名不变),
48
+ 旧形是抛出物原文(`String(e)` 或 `e.message`,多行照原样);新形与 `tool_trace` / hook 失败句同一行规则:认出的凭据形换成 `«redacted…»` 记号,控制与双向字符剥掉(多行折成一行),
49
+ 超过 200 码元截断并以 `…` 收尾(记号计入界)。k8s 车道起服自检三行在内:`sandbox_secret_seat_unverified.detail`、`sandbox_egress_policy_check_crashed.error`、读不到时的 `sandbox_egress_policy_gap.detail`;
50
+ 异常句先转一手再进行的同改:`counter_flush_degraded.error`、`memory_consolidation_default_off_no_cheap_model.detail` 里嵌的引擎拒句那一段、`config_loaded_lkg.why`(起服拉配置失败回落 LKG;
51
+ 旧形只剥控制字符、截 120 码元,不认凭据形)。致命退出那一行不变(整句多行、只过凭据形脱敏)。
52
+ **谁受伤**:按异常原文全文排障的人(长句被截、多行的折成一行);按某个字段整句精确匹配的告警(凭据形处换成记号)。**出路**:原因句的前 200 码元照旧在(错误类与首句,凭据形之外逐字);
53
+ 规则改按事件名与字段前缀匹配。**回滚**:回到上一版即回到原文。
54
+
55
+ 6. **会话列表不再漏掉存档读不出的会话:行带加性键 `unreadable: { errorCode }`(行为面:列表不再静默漏行;wire 加性;部署零动作)**:
56
+ 文件后端(`DB_BACKEND=local`)上,会话头坏了、整份截在会话头半截、或存档读不出来(权限不对、I/O 故障)的会话,此前从 `GET /v1/sessions` 里消失(引擎的会话仓列表跳过它),
57
+ 而同一只会话的 `GET /v1/sessions/:id` 与 `…/head` 答 500。本版起它照样是列表里的一行:行上的格出自会话行(`owner` / `title` / `pinned` / `lastActivityAt` / `firstActivityAt` / `rowTag` /
58
+ run 四位 / `tempo`,与好行同读),`objectivePreview: null`,另带 `unreadable: { errorCode }` —— present-iff 这只会话的头的叶子读不出,键闭集只有 `errorCode`,值 = 读口打开这只会话时答的码
59
+ (`corrupt_session`:会话头坏 / 截在头里;`internal.error`:I/O 级读不出)。同一张码表也是全局兜底答码的那一张,两处不会分叉;各读口的答复逐字不变。
60
+ 边档里没有格、存档头又读不出的会话(7.104.0 之前写下、此后从没被读过)没有可读的属主,仍不列(运维面照旧点名它)。SQL 后端的行从不带这只键(叶子是会话行上一列,一行坏事件照旧在列表里)。
61
+ 订正契约 §17.2 的旧句「整份读不出的会话不出现在列表里」—— 那是本版之前文件后端的行为。
62
+ **谁受伤**:把列表每一行都当「点开就能读」的端(这些行点开得 500,就是读口一向答的码);不认识新键的端会把它画成一行没有预览的普通会话;按列表行数计数的面多出这些行;
63
+ 跨会话对端目录在文件后端上多出这些会话(与 SQL 后端坏事件行今天的读法相同);`?q=` 搜索不命中它们(没有预览)。
64
+ **出路**:按 `unreadable` 在场分支(灰显 / 提示读不出 / 只给删除);`DELETE /v1/sessions/:id` 对属主照常删掉它;要修就把它的存档文件(数据根 `sessions/` 目录下)从留存副本恢复、或修好文件权限 / 磁盘,行上的标记随之消失。**回滚**:回到上一版,这些会话重新从列表里消失(数据不动)。
65
+ 7. **🔴 记忆同步中心必须声明自己的同步身份 `MEMORY_SYNC_SERVE_IDENTITY`;中心侧同步口只服务点名这一身份的轮(BREAKING 部署面:中心升级要加一行 env;行为收紧;wire 加性)**:
66
+ **同步身份** = 客户端到达中心同步口用的基址的规范形:小写协议与主机、无缺省端口、路径去尾斜杠,不带 userinfo / 查询串 / 片段 / 凭据形的段,≤ 190 字符。客户端腿发的 `peer` 自 7.139.0 起就是它自己 `MEMORY_SYNC_URL` 的规范形(本版不变)。
67
+ ① DB 记忆平面的中心(`MEMORY_ENGINE_BACKEND=pg|mysql`)用新旋钮 `MEMORY_SYNC_SERVE_IDENTITY` 声明它;没声明 ⇒ `POST /v1/memory/sync/:scope` 答 501 `feature.memory_sync_serve_disabled`(鉴权之后),每个客户端的每一轮都失败(客户端那一层出 `memory_sync_layer_failed`,下一轮重试)。
68
+ 声明不是规范形(大写主机、`:443`、尾斜杠……)/ 带凭据 / 查询 / 片段 / 非 http(s) / 超宽 ⇒ **拒启**,拒句给出规范形(不静默规范化);引擎关着也读、也拒。
69
+ ② 入站 `peer` 不是同步身份的规范形 ⇒ 400 `request.field_invalid`(`field: "peer"`,`reason` ∈ `not-http-url` / `userinfo` / `query` / `fragment` / `credential` / `too-long` / `not-canonical`;不回显值)。7.139.0 之前的客户端发的标签 `"central"` 落这一格(`not-http-url`)。
70
+ ③ `peer` 是规范形但 ≠ 声明 ⇒ 409 `config.memory_sync_target_mismatch`,整轮零读零写(此前按 `(scope, peer)` 新开一只游标 —— 别名客户端在中心上是第二条同步线;那些已经写下的旧游标行本版不动、不读)。拒句只点名本中心声明的身份。
71
+ ④ 同步口与 `GET /v1/memory/export` 的 501 `capability.memory_store_required` 改读能力事实(执行位 / 导出读面在不在),不再读 `MEMORY_ENGINE_BACKEND` 的词;能力面加位 `memorySyncServe`(`false` | `{ identity }`)与 `memoryExport`(boolean)。
72
+ **谁需要**:多台客户端经不同地址连同一台中心的部署(别名不再悄悄分叉);要读中心身份的会话记忆同步触发口(本节第 8 条)。**谁受伤**:没声明身份就升级的中心(同步全停,直到声明);7.139.0 之前的客户端(400);经别名到达的客户端(409);
73
+ 嵌入方装了同步执行位、后端名写 `file` 的部署(两口从 501 变成受理)。**出路**:**升级序 = 先客户端、后中心** —— 先把所有客户端升到 ≥ 7.139.0,再升中心并在同一次发布里加 `MEMORY_SYNC_SERVE_IDENTITY=<客户端 MEMORY_SYNC_URL 的规范形>`(写错了拒启句会给出规范形);
74
+ 别名客户端把 `MEMORY_SYNC_URL` 改成声明的那一个(409 的拒句点名它)—— 改了地址的客户端在它那一侧游标照旧绑旧身份,会停驻在对端轴上,按 7.141.0 的处置删游标。**回滚**:回到上一版即回到旧收法(声明被忽略)。
75
+
76
+ 8. **会话记忆同步触发口 `POST /v1/sessions/:id/memory/sync`;同步一轮从不整轮失败(wire 加性;运维面日志形变更;嵌入方类型面 BREAKING;部署零动作)**:
77
+ ① 新口:本机形(file 记忆引擎 + `MEMORY_SYNC_URL` + 持久 run 账面)上壳在 fork 之前调它,体恰好 `{ "target": <会话要去的那台引擎 GET /v1/capabilities 上的 memorySyncServe.identity,逐字> }`;答 200 + 引擎的同步报告(`layers` 逐层结局,或整轮 `refused`:
78
+ `config.memory_sync_target_mismatch` = 目标不是本机同步腿到达的那台 / `session_active` = 会话有在飞 run,或读完它的记忆观测之后又受理了 run / 会话记录前进了)。读集 = 会话最近一条已结 run 的记忆观测;取不到 ⇒ 409 `conflict.session_memory_unobserved`。读集里被同步腿覆盖的项目层经 `syncOnce(…, { reading })` **永久**加入本进程的后台同步集(此后每一轮后台同步都带上它、直到进程退出:多一份游标、每轮多几次往返)。能力位 `sessionMemorySync`;位为假时 501(`feature.memory_sync_disabled` /
79
+ `capability.run_store_required` / `capability.session_ownership_required`)。
80
+ ② 同步一轮里一层失败只结束那一层,别的层照跑、轮照样落定(此前在轮末汇总抛错,同一轮成功层的结果随之丢掉);客户端侧后台轮的轮级日志行 `memory_sync_failed` **退役**(中心侧同步口答 500 `internal.error` 的那一臂照旧发同名 warn 行 `memory_sync_failed`,不在本条),逐层失败各一行 `memory_sync_layer_failed` / `memory_sync_layer_parked`(键不变);这两行与 `memory_sync_cursor_write_failed` 的 `err` 值改由不抛的读口出 —— 错误的消息原文,不再带 `Error: ` 前缀,打印不出的抛出物记 `<unprintable>`(此前 `String(err)`:那种抛出物会让轮在失败行里再抛)。
81
+ ③ 同步层名(`MEMORY_SYNC_SCOPE`,缺省 `MEMORY_SCOPE`)带凭据形的段 ⇒ **起服拒**(这个名字随每一份报告逐字上 wire);`MEMORY_SYNC_URL` 的起服拒句改由同步身份的同一只铸点出(判据不变,句子变)。
82
+ ④ 嵌入方类型面:自建 `ServiceDeps` 带 `memorySyncLeg` 的,它多要 `peer` 与 `syncOnce`(tsc 红即通告);`MemorySyncRunner.syncOnce` 的兑现值由 `{ layers }` 改为 `{ calls }`(每层每次调用如它落定的那样),不再 reject。
83
+ **谁需要**:把本机会话推上云的壳;运维(部分层失败不再抹掉成功层)。**谁受伤**:按 `memory_sync_failed` 告警的规则;按旧拒句子串匹配 `MEMORY_SYNC_URL` 起服失败的规则;同步层名带令牌形的本机部署(起不来);上面 ④ 的嵌入方。
84
+ **出路**:客户端侧告警改认 `memory_sync_layer_failed` / `memory_sync_layer_parked`(中心侧 500 那一臂照旧认 `memory_sync_failed`);起服失败按 env 键名匹配;同步层改名;嵌入方按 `createMemorySyncRunner` 装配即可。**回滚**:回到上一版(新口随之消失,能力位缺席)。
85
+
86
+ 9. **隔离车道(e2b / k8s / local-docker)的附件改由恢复点放进工作区(行为修复;部署零动作)**:
87
+ 此前附件物化在引擎恢复相之前碰执行环境:e2b 上会话带附件的续跑腿跑在一只为写附件新建的沙箱上(停驻的那只没被恢复,停驻前的工作区不在,不报错);
88
+ 无附件的 e2b / local-docker 续跑腿在引擎读收据句柄那一步抛 `connect_failed`;k8s 续跑腿多起一只 pod(先前那只靠 pod 收割兜),续跑前新绑的附件不在恢复出来的 pod 里。
89
+ 现在隔离车道的附件只在恢复点放(沙箱铸出或恢复之后、交给任务之前);其余车道照旧在执行环境建立时放。k8s 续跑腿若在恢复之前已有一只 pod,先删掉它并写一行运维 warn `sandbox_pod_released_before_restore`。
90
+ **谁受伤**:无;e2b 的附件写失败从「执行环境建立失败」变成「这一次连接失败」(同码 `connect_failed`、同样带写失败句)。**出路**:无需动作。**回滚**:回到上一版即回到旧形(上面三种病随之回来)。
91
+ **已知限制(7.141.0 既有;本版不改)**:空间开的隔离车道会话里,若 run 在首次触到执行环境之前就停驻(pod 还没起、没有快照对象),续跑走停驻车道、不恢复 `latest`,run 结束提交的快照会覆盖此前 run 的改动,且不报警。修法候选:没有对象的停驻快照,续跑改作一次新连接、走 establish。
92
+
93
+ 10. **项目包:没有 git 的项目以 `application/vnd.sema.project-bundle` 上传、绑进隔离车道的会话作为工作区的种子(wire 加性);附件一经绑定不再改绑(行为收窄);嵌入方附件店类型面 BREAKING**:
94
+ ① **行为面(收窄)**:提交体 `attachmentIds` 里的附件已绑在别的会话上 ⇒ 新提交 `409 attachment.bound_elsewhere`(体 `id`),此前同属主下后绑会把它从前一个会话移过来。
95
+ 同会话重绑(首交重试、续跑腿重放)照旧受理、不改绑定时刻;续跑腿重放存量体时这只拒不 4xx,写一行运维 warn `attachment_refused_on_resume`、该 id 跳过。
96
+ **谁受伤**:把同一只已上传的附件交给多个会话的调用方。**出路**:每个会话各上传一次(SQL 后端按 sha256 去重,不多占对象存储;local 后端多一份字节)。
97
+ ② **wire 面(加性)**:`POST /v1/attachments` 新受理 mime 值 `application/vnd.sema.project-bundle`(gzip 的 POSIX tar,顶层即工作根);体读不出项目包 ⇒ `400 attachment.bundle_invalid`,体 `reason`(七词闭集,见契约附录 A.1)与 `path`(读得出、且不是放不下的超长路径才在)。
98
+ 提交体带项目包而这条腿的车道不隔离 ⇒ `409 attachment.bundle_lane_not_isolated`(体 `id` · `lane`);会话已有种子或同一提交体带第二只 ⇒ `409 attachment.bundle_seed_fixed`(体 `id`);
99
+ 「会话已有种子」由附件店在绑定那一步原子地判(两次并发的提交各带一只新项目包到同一会话 ⇒ 恰一只受理);店拒了提交体那只新项目包时,体里其余附件一只都不绑(提交体上读得到的拒 —— 已绑别的会话、车道不隔离、第二只新项目包 —— 同样在任何绑定之前到)。读与绑之间才被别的会话抢走的普通附件不在此列:请求答 409,同一次提交里已先绑上的种子与同组未被抢走的附件不回滚。
100
+ 嵌入方类型面(自建 `ServiceDeps.taskAttachmentStore`):`bind` 的兑现值由「此刻绑在本会话上的 id 数组」改为 `{ bound, refused }`,两条绑定律(不改绑 · 一只会话一只种子)由店判(BREAKING;tsc 红即通告)。PG 店 `PgTaskAttachmentStore` 不从包根导出(包的 `exports` 表也不放深路径);它的构造参数由查询函数改为池,只对直接 import 本仓源码路径的嵌入方可见。
101
+ 种子只在隔离车道的恢复点铸出新工作区时解到工作根本身(恢复出来的工作区从不播种、fork 不搬、不进目标里的附件告知);运维 info 行 `workspace_seeded{sessionId, bundleId, entries}`。
102
+ ③ **部署面**:配了 `ATTACHMENT_MIME_ALLOWLIST` 的部署,要用项目包就把 `application/vnd.sema.project-bundle` 加进白名单(否则上传 415 `attachment.mime_not_allowed`);
103
+ 隔离车道的沙箱镜像要带支持 gzip 的 `tar`(k8s 工作区快照早已靠它;e2b 自定义模板与 local-docker 镜像自查),缺 ⇒ 带种子的会话在执行环境建立时被拒、句子点名 `tar`;
104
+ 镜像若在工作目录预置了内容,与项目顶层同名 ⇒ 被拒、句子点名路径(不覆写、不合并)。
105
+ **谁需要**:用壳把没有 git 的本机项目交到云端隔离车道继续的用户。**谁受伤**:见 ①;顶层恰有 `attachments` 目录的项目(包被拒,改名);
106
+ e2b / local-docker / `WORKSPACE_PERSIST=off` 的 k8s 上,每个新回合都从上传态开始(车道本就不留工作区;壳读能力位 `workspaceSync.persist === "space"` 判跨回合持久)。
107
+ **升级前做**:配了 mime 白名单的部署按需加一项;无 DDL、无数据迁移。**回滚**:回到上一版 ⇒ 项目包 mime 的附件当普通附件放进 `attachments/`(不解包),改绑回到「移过去」。
108
+ **已知限制(SQL 两方言;本版不改)**:同一会话已有种子时并发提交,若撞锁序死锁、重试(4 次)耗尽 ⇒ 店错(不是 `409 attachment.bundle_seed_fixed`);TiDB 另有:另一笔未绑的项目包被长锁占住、超过锁等待上限时答 1205(同样是店错,不是 409)。
109
+ 11. **local 车道有序关停截断的审批卡:`crashConverged[].cause` 与决断口同一个词;前世审计判孤儿的 `PARKING` 问起服即 `VOID`(行为面;wire 值变化;零新键、零新词;部署零动作)**:
110
+ 此前有序关停(SIGTERM / SIGINT / SIGHUP)截断、停车行在关停宽限窗内没提交的**后台 / 委派子代**卡,重启后三个读面三个词:问账行记 `shutdown_before_park`(关停那一拍),`GET /v1/approvals` 的
111
+ `crashConverged[].cause` 却是 `crashed_before_park`(审计账的关停记号按腿落,子代卡的腿不在协调器追踪视野里),决断口先答 `425`,对账器走到它再收成 `DENIED` / `routing_failure_fail_closed`。
112
+ 现在只有一个词:有序关停是进程的事实 —— 审计账每一世在审批收尾等待结束那一拍落一行 `interrupted_by_shutdown`,这一世此刻没结清的卡一律 `shutdown_before_park`;起服收敛把审计判为孤儿的前世
113
+ `PARKING` 行与 `STREAM_PENDING` 同一拍处置(停车行其实已提交 ⇒ `PARKED`,运维日志一行 `approval_crash_convergence_park_committed`,同批仍在停驻途中的兄弟随之 `VOID` / `batch_bound_elsewhere`;
114
+ 否则 `VOID`,成因与 `crashConverged` 同词)。「停车行提交了没有」读后端的只读口,与这一世开没开 `DURABLE_APPROVAL` 无关(前世已提交的停驻不因这一世关着 durable 被收掉)。
115
+ **wire 值的变化(local 车道;键与闭集不变)**:① `crashConverged[].cause`:有序关停截断、收尾结束时仍未结清的后台 / 委派子代卡 `crashed_before_park` → `shutdown_before_park`(`orphanState` / `resumeSafe` 不变);
116
+ ② 前世 `PARKING` 孤儿的决断口 `POST /v1/tasks/:taskId/asks/:askId/decision`:`425` →(对账后)`410 gone.ask {state:"DENIED", cause:"routing_failure_fail_closed"}` 改为起服即 `410 gone.ask {state:"VOID", cause:<crashConverged 同词>}`;
117
+ 晚答 `404 tool_approval.not_pending` 体的 `cause` 与按问读口 `GET /v1/tool-approvals/:approvalId`(`parked` → `void` + `cause`)同步变化。SQL 车道(`DB_BACKEND=mysql|pg`)零变化。
118
+ **审计账本形**(`<数据根>/approval-ask-audit/ledger.jsonl`,留盘给取证):关停记录 `{kind:"interrupted_by_shutdown", atMs}` 不再带 `taskId`;上一版写的带 `taskId` 的旧行照读(零迁移)。
119
+ **升级后的历史读数**:上一版已收敛过的组照新规则重投影 —— 那一世走完了有序收尾、而旧规则因腿证据缺席判成 `crashed_before_park` 的组,升级后 `crashConverged[].cause` 读 `shutdown_before_park`;
120
+ 它们的问账行早已是终态(`VOID` / `DENIED`),终态行不改写,所以这一批历史问上两处的词可能不同,随审批账本留存窗整问删除而消失。
121
+ **起服拒(行为收紧)**:起服收敛写不进审计收敛行(盘满 / 只读;那一行是这一组的幂等锚与成因上界)⇒ 拒启,问账一字不动;此前写丢被吞、照常起服。出路:修好数据根再起,照收、同一个词。
122
+ **谁需要**:有序重启后读 `crashConverged`、或对旧卡作答的壳(子代卡此前被说成「崩了」,作答先撞 425 再拿到「路由失败」)。**谁受伤**:按 `cause === "crashed_before_park"` 判「崩溃」的消费方少数到这类卡(两词处置相同);
123
+ 依赖「起服后 `PARKING` 孤儿先 425、之后 `DENIED`」时序的断言;审计账本盘坏了的 local 部署(由照常起改为拒启)。**出路**:按闭集两词读;作答见 410 即消卡;按拒启句修盘。**回滚**:回到上一版时新形关停记录(缺 `taskId`)被计坏行、经 F 类座披露(`server.approval-ask-audit.corrupt-record-skipped`),那几组回到上一版的答法;已收成 `VOID` / `PARKED` 的问账行保持终态。
124
+
125
+ 12. **嵌入方(自建 `StoreBackend`、不走 `createStoreBackend` 的进程):`StoreBackend.checkpointReadView()` 的返回型多一口 `findCheckpointCandidatesForAsk()`(BREAKING,类型面;零别名)**:
126
+ 这只只读口原先只交 `listPendingSessionIds()`(审批账本留存读它);local 车道的起服收敛现在也经它跑对账器判据 ① 的候选读口(第 6 条:前世已提交的停驻不因这一世关着 `DURABLE_APPROVAL` 被收掉),
127
+ 返回型按 `ReturnType<StoreBackend["checkpointReadView"]>` 读(两口:`listPendingSessionIds()` 与 `findCheckpointCandidatesForAsk(scope, sessionId, toolCallId, sinceMs)`)。本服务自带的三个后端零动作(SQL 孪生与 local 交的本就是整只 checkpoint 店)。
128
+ **谁受伤**:自建后端的嵌入方(tsc 红即通知)。**出路**:有 checkpoint 店就交它本身;没有就交恒空读口 `{ listPendingSessionIds: async () => [], findCheckpointCandidatesForAsk: async () => [] }`(没有店 ⇒ 没有停驻)。
129
+ **回滚**:回到上一版,多出的成员无人读。
130
+ 13. **结果上组合腿的自由文本过脱敏:`verification.findings[]` / `verification.evidence` / `attempts[].diagnostics` / `attempts[].gateError`(行为面;安全;wire 值可能变;部署零动作)**:
131
+ `verify:true` 任务结果的 `verification.findings[]` / `verification.evidence`、`cascade:true` 任务结果的 `attempts[].diagnostics` 与 `attempts[].gateError` 此前原样落 run 行 / 账本 `done` 行、
132
+ 原样上同步 200 体 / `GET /v1/runs/:id` / `GET /v1/runs/:id/events`。本版起它们经本服务的结果脱敏器(前三片与 `result` 同一只,`gateError` 与 `terminal.message` 同一只):
133
+ 认出的凭据片段换成 `«redacted…»` 记号,键集、数组条数与顺序不变;判词 / 轮数 / 验证器花费、每级的序号 / 模型 / 状态 / 是否通过逐字;升级前落盘的存量行在读面上同样脱。
134
+ 每级的失败码 `attempts[].errorCode` 与 `terminal.code` 同一只出口封口折:出了本服务码形的(驱动原生码、长得像凭据的串)读作 `internal.store_error`,原码只进运维线索 —— 写口(结果第一次离开引擎)每折一座一行 `outward_failure_code_folded`,读面上的再读改记 fail-open 计数(按被折的码座计)与每进程一次 warn(第 4 条)(此前级码原样)。
135
+ completed 因由的 `terminal.detail.finishReasonUnknown` 过服务商回声的形状闸(可见 ASCII、1–128 字符):越界 ⇒ 该键缺席;completed 臂上不认识的成员按自由文本脱。
136
+ **谁受伤**:按判词 / 门异常原文逐字比对或存档的消费方;把 `finishReasonUnknown` 当任意串读的端;按 `attempts[].errorCode` 读驱动原生码的端(此后与 `terminal.code` 同读 `internal.store_error`)。**出路**:按条数与判词分支照旧;比对改按记号之外的文本;
137
+ `finishReasonUnknown` 缺席读作「服务商的词不在回声形内」。**回滚**:回到上一版即回到原文(本版落库时已脱的字节不会复原)。
138
+
139
+ 14. **`GET /v1/runs/:id/events` 的停驻尾帧一律是状态帧:挂起前尚未落账的 `model_usage` 行写在 `suspended` / `needs_review` 之前,`verify:true` 的停驻与不带 `verify` 的停驻同序(行为面;wire 事件序;订正 7.141.0 节第 32 条 ①;部署零动作)**:
140
+ 7.141.0 节第 32 条 ① 写的是「`verify:true` 任务停驻期间回放的最后一帧是一帧 `model_usage`,排在状态帧之后;不带 `verify` 的停驻以状态帧收尾;两形同序的修正不在本版」。本版起两形同序:停驻帧是这一腿主线写下的最后一帧(晚到的子代转发帧、完成后追加的 `suggestions` 是旁路写,本来就可能排在状态帧之后,不在此列)。
141
+ 成因与改法:停驻行此前先写、挂起前的用量行紧随其后;不带 `verify` 的停驻在最后一个 `turn_end` 就把用量结清了,所以尾帧恰好是 `suspended`;`verify:true` 的任务是只回结果的组合腿、没有 `turn_end`,挂起前的用量只有停驻这一拍才落账。
142
+ (不带 `verify` 的形上,最后一个 `turn_end` 之后、停驻之前还有未结清的用量时,同样的尾巴也在。)现在停驻行只经一只写口:先把这一片未落账的用量结清(`model_usage`;没有用量就不写),再写 `suspended` / `needs_review`。
143
+ 后台腿、同步 detach 腿、续跑腿的再停与重开臂、续跑 verify 臂同序。用量行落不下 ⇒ 停驻帧照落、随后把失败抛给调用方 —— 失败语义与此前逐字同(后台腿按「停驻没落定」收场:行 `failed`、手里那张卡作废;detach 腿的兜底停驻与续跑腿的重开臂照旧吞掉它)。
144
+ 终局帧的序不变(用量行本来就在 `done` 之前);`model_usage` 行的内容、逐行求和与 `stats.modelUsage` 回声不变,变的只是用量行在流里的位置。
145
+ **谁需要**:按「主线最后一帧 = 当前状态」读事件流的客户端(此前 verify 停驻上要改读最后一只状态帧)。**谁受伤**:按「`model_usage` 紧跟在 `suspended` / `needs_review` 之后」配对计费行的解析器 —— 用量行现在在停驻帧之前,按位置配对的会错位。
146
+ **出路**:按 `model_usage` 行逐条求和(与位置无关),或读 `GET /v1/runs/:id` 的 `stats.modelUsage`;读「当前状态」可以读事件流的最后一帧,也可以读 `GET /v1/runs/:id` 的 `status`。**升级前做**:无。**回滚**:回到 7.141.0 即回到停驻帧在前的序。
147
+
148
+ 15. **同键重放的同步体改成首答的信封 + `replay: true`,不再是 run 行读面(wire 形;BREAKING 对按重放形解析的消费方;订正 7.141.0 节第 32 条 ②)**:
149
+ 同一把 `Idempotency-Key` 再投 `POST /v1/tasks` / `POST /v1/tasks/stream`,命中既有行(另一个进程 / 副本 / 重启之后)时,此前的体是 `GET /v1/runs/:id` 的读面加 `replay: true`(`result` 是对象,外加 `status` / `errorCode` / `error` / `heldBy` / `cancelRequested` 等读面键),
150
+ 与首答(引擎结果,`result` 是串)的信封对不上 —— 7.141.0 节第 32 条 ② 写的「外层形不同;重放回首答同一形的修正不在本版」。本版起重放与首答同一个信封,同一个铸点(`buildSyncSubmitBody`):
151
+ — 命中的行**已终局** ⇒ 体 = 引擎结果本身(`terminal` / `stats` / `taskId` / `sessionId` / `result`(串)……在顶层,终局体不带 `status`),加 `replay: true`;
152
+ — 命中的行**停在门上**(`suspended` / `needs_review`)⇒ 体 = 引擎结果 + 回执三键 `{ taskId, sessionId, status }`(与同步停驻的首答同形),加 `replay: true`;
153
+ — 命中的行**还在跑**(首投在别的进程上没落定),或行上没有结果可交 ⇒ 首答在这一刻不存在:体只有回执三键 `{ taskId, sessionId, status }` 加 `replay: true`(`status` 是那一行此刻的词:在跑 ⇒ `running`;结果已被清空的终局行 ⇒ 它的终局词;失联的行与读面同判折成 `failed`),去 `GET /v1/runs/:taskId` 轮询 / 读行。
154
+ 流式腿:`done.result` 是同一份体,帧上的 `replay: true` 照旧(已知差:停驻体上的回执三键,同步腿的首答带、流式腿的首答 `done.result` 不带,流式重放照同步腿带)。`POST /v1/runs` 与 A2A 的重放回执(202 `{ taskId, sessionId, status }`)不变。体上没有 `wiringManifest`、`notice`、`warnings`(照旧:它们说的是首帧的活捕获 / 本次请求)。
155
+ **键差**(重放体与上一版相比):没有了 `status`(终局)/ `errorCode` / `error` / `supervisorCost` / `suggestions` / `jobId` / `source` / `heldBy` / `cancelRequested` / `msSinceLastActivity` / `crossSliceUsage`;`result` 由对象变成串;引擎结果的其余顶层键(`terminal` / `stats` 等)现在在顶层。
156
+ **谁需要**:按首答形解析同步体的调用方(SDK / CLI / client-core 的同步路径)—— 重放体不用另写一支解析。**谁受伤**:按 `replay === true` 分流、再读 `body.result`(对象)/ `body.status` / `body.errorCode` 的重放分支。
157
+ **出路**:读顶层的引擎结果(判终局 / 停驻读 `terminal.kind`,与首答同);要读面键就读 `GET /v1/runs/:taskId`(`taskId` 在体里)。**升级前做**:无(部署零动作);**回滚**:回到 7.141.0 即回到读面形。
158
+
159
+ 16. **http hook 名单在拨出点执行:请求 `settings.allowedHttpHookUrls` 在场而不放行 ⇒ 这条 http hook 一个字节都不发;`settings.httpHookAllowedEnvVars` 与条目 `allowedEnvVars` 取交(安全;行为面:名单在场时 http hook 可能被拒;wire 加性;部署零动作)**:
160
+ ① **wire 面(加性)**:`settings` 受理键多两只 `allowedHttpHookUrls` / `httpHookAllowedEnvVars`(上一版新提交答 `400 request.body_shape`);能力位 `taskSettings.httpHookAllowlists`(与 `taskSettings.hooks` 同谓词,单用户车道 true)—— 端见位才发;
161
+ `hook_notice` 两族的 `reason` 各多一词 `url_not_allowed`(`hook_non_blocking_failure` / `hook_managed_unrun`;按开集读未知词的消费端零动作)。
162
+ ② **行为面**:两张名单是请求 `settings` 这一层的(设置源 `flagSettings`),读法与判决只在契约包;http 条目**拨出之前**问判官 —— URL 名单在场而这条的 `url` 不命中、名单是空的 `[]`、或名单不是串数组 ⇒ 不发出(零字节),
163
+ 非托管组是非阻断故障(故障帧 + 运维 warn `hook_http_withheld`),托管组失效关闭(`PreToolUse` 在没有否决时折 `ask`);帧的句子点名 `settings.hooks.<event>`、规则词(`url` / `empty` / `malformed`)与出路。
164
+ 变量名单在场 ⇒ header 只插值条目 `allowedEnvVars` 与它的交集,名单外的名字置空(既有置空行,只点名不带值)。两键都缺席 ⇒ 与上一版逐字同。提交面不判名单的值(坏形照收,在拨出点由判官拦)。
165
+ **谁需要**:单用户部署上带着组织或用户 http hook 名单的端(hook 在本服务上执行,名单只有在拨出处才执行得了)。**谁受伤**:带了名单而 hook 地址不在名单里的请求(那条 hook 不再发出;托管组上 `PreToolUse` 改问人);
166
+ 上一版两键新提交即 400,停驻任务的存量体里不会有它们 ⇒ 已在跑的任务零变化。**出路**:把 hook 地址的模式加进 `settings.allowedHttpHookUrls`(模式文法见契约包;例 `https://hooks.example.com/*`),变量名加进 `settings.httpHookAllowedEnvVars`;
167
+ 或不发这两张名单(不限)。**回滚**:回到上一版 ⇒ 两键新提交重新 400,不带名单的请求行为不变。
168
+ 17. **🔴 问账行的成因拆成两列:SQL 后端 `approval_ask.denied_reason` 改名 `ending_cause`、加一列 `park_trigger`;local 后端问账日志整本重写一次(本服务起服时自己改;升级前备份、先停光旧副本再起本版;回滚 = 恢复备份,或下面三句手工改回;嵌入方类型面 BREAKING;wire 零变化)**:
169
+ 旧形:一列 `denied_reason VARCHAR(64)`,`PARKING` / `PARKED` 行上是转 park 的触发成因,`VOID` / `DENIED` 行上是终态成因;对账器收 `PARKING` 行时终态成因覆盖触发成因。
170
+ 新形:`ending_cause VARCHAR(64)`(终态成因;到 `VOID` / `DENIED` 的转移带着它写)+ `park_trigger VARCHAR(64)`(触发成因;只由转 park 的那一次写,此后不变)。新建的库直接建成新形。
171
+ 本条**不用手跑 `ALTER`**:起服时在一次性迁移锁内依次 —— ① `denied_reason` 在 ⇒ 改名 `ending_cause`(MySQL 协议 `CHANGE COLUMN`、PostgreSQL `RENAME COLUMN`);② `park_trigger` 不在 ⇒ 加列;
172
+ ③ `PARKING` / `PARKED` 行上那一格搬进 `park_trigger`,再把这两态上的 `ending_cause` 置空。中途被打断,下次起服从断点续;第二次起服零命中。库账号要有 DDL 权限(起服建表本就要求)。
173
+ 终态行上早已被覆盖掉的触发词没有出处可补 —— 如实缺席(决断口 `410` / 晚答 `404` / 按问读口读的终态成因逐字不变)。
174
+ `DB_BACKEND=local`:问账日志 `approval-asks/asks.jsonl` 的记录信封号 1 → 2;窗内信封 1 的记录按旧律译读(行快照按行的状态、转移补丁按目标态),起服一次压实把整本重写成信封 2。
175
+ 起服行 `one_time_migrations_applied` 的三个读数键:`askCauseColumnsChanged`(表形改动 0–2 处;SQL)· `askParkTriggersMoved`(搬了几行触发词;SQL)· `askJournalRecordsUpgraded`(重写前读到的信封 1 记录数;local)。
176
+ **升级步**:① 备份整个库 / 整个数据根(回滚的前提)。② 推荐先停光全部旧副本再起本版:改名之后仍在跑的旧副本对 `approval_ask` 的每一条读写都响亮失败(不会写错列),重叠期间它们上的审批卡读写报错。
177
+ ③ 窗口:本条迁移自引入它的版本发布日起 30 天内每次起服都跑,窗关之后的第一个小版本删码 —— **跳版升级要在窗内先起一次带本条的版本**:删码之后,SQL 库上仍带 `denied_reason` ⇒ 起服拒
178
+ (退役 schema 探针,拒句点名出路);local 数据根里仍有信封 1 的记录 ⇒ 问账店拒启(`unknown envelope version`)。
179
+ **回滚**:旧版对改名后的表读写失败、对信封 2 的日志拒启。支持形 = 恢复升级前的备份;SQL 也可手工改回(终态行上本版记下的触发成因随之丢):
180
+ MySQL 协议 `UPDATE approval_ask SET ending_cause = park_trigger WHERE state IN ('PARKING', 'PARKED') AND park_trigger IS NOT NULL; ALTER TABLE approval_ask DROP COLUMN park_trigger; ALTER TABLE approval_ask CHANGE COLUMN ending_cause denied_reason VARCHAR(64) NULL;`;
181
+ PostgreSQL 前两句同,第三句 `ALTER TABLE approval_ask RENAME COLUMN ending_cause TO denied_reason;`。local:恢复数据根备份(或移开 `approval-asks/asks.jsonl` —— 其上的每张审批卡作废)。
182
+ **嵌入方类型面(BREAKING,零别名)**:`StoreBackend.approvalAsk()` 的行型 `AskRow.deniedReason` → `endingCause` + `parkTrigger`;转移补丁 `AskTransitionPatch.deniedReason` → `endingCause`(值域 = 结局成因闭集;补丁没有触发成因那一键);
183
+ 终局 claim 的败方读数 `current.cause`(`{ kind, cause }`)→ `current.endingCause` / `current.parkTrigger`(各是词本身,读不出即缺席);按状态分派的读口 `causeOfAskRow` 与型 `AskRowCause` 删除,换 `askRowEndingCause` / `askRowParkTrigger`;
184
+ `StoreBackend.runOneTimeMigrations()` 的报告多三键(上列)。自建问店的嵌入方:到 `VOID` / `DENIED` 的转移写终态成因格,赢下 `STREAM_PENDING → PARKING` 的 `claimTerminal(expire, { cause })` 写触发成因格,其后不再写它。
185
+ — **谁需要**:排查「这张卡为什么进了投递面、后来又为什么被收掉」的运维与取证。— **谁受伤**:滚动升级重叠期的旧副本(审批读写报错)、回滚(只剩恢复备份或手工三句)、自建问店 / 读行型的嵌入方(tsc 红)。
186
+ — **出路**:如上升级步;嵌入方按上列改名。
187
+ 18. **沙箱 git 平面接上有效配置块 `sandboxGit`:中心投影的块热生效,坏块读作没有任何 origin 并一行 warn `web_admin_sandbox_git_refused`;本版平面读数不变;嵌入方 `ServiceDeps.sandboxGitPlane` 由值改为读函数(部署面零动作;运维面多两行日志;嵌入方类型面 BREAKING)**:
188
+ ① **部署面**:块的唯一来源是中心有效配置 —— 控制台按部署登记册投影,只在本 worker 自己的拉取上(配了 `SEMA_WEB_ADMIN_WORKER`)出现;没有 env 旋钮,本地配置源(`CONFIG_PROVIDER=local`)没有这一块。
189
+ 块随有效配置的每次提交热换代(刷新拍 / 迟到起服 / LKG 起服都一样),不签重启;中心凭证换人降档时随整份中心配置撤下。块判不过契约(重复 origin / 超过 16 项 / 非规范 origin / `connectUrl` 不是 https / 不是对象)⇒
190
+ 当作没有任何 origin,一行 warn `web_admin_sandbox_git_refused`(`version` / `issues` 逐条坐标 + 契约拒句 / `effect`,不回显值),同一份候选的其余配置照常落地;块换代一行 info `web_admin_sandbox_git { version, origins }`。
191
+ 已经建起的沙箱 pod 的路由表在建 pod 时定下:删 / 加 origin 对新 pod 生效。
192
+ ② **本版平面读数不变**:取值口的 worker 凭据仍没接线,`GET /v1/capabilities` 的 `credentialPlane.sandboxGit` 停在 `no_caller_credential`(或更前的词),没有 pod 挂代理。
193
+ 这个值现在每次读时求值(起服读数 + 当前生效配置的块),不再是起服时算一次的读数。
194
+ ③ **嵌入方类型面**:自建 `ServiceDeps` 带 `sandboxGitPlane` 的,它由读数(`SandboxGitPlane`)改为读函数(`() => SandboxGitPlane`;能力位每次请求调它)—— tsc 红即通知;组合根交的是平面座的 `now`。
195
+ ④ **同版(部署零动作)**:run 行记下登录令牌的化身号(`sub_inc` ⇒ `task_run.identity_principal_inc`,零 DDL);超过 64 字符的声明不记、一行 warn `run_identity_claim_unrecorded { claim, chars, max }`(scope 超过 190 字符铸不出元组的旧行为同一行告警)。
196
+ 嵌入方自建的鉴权桥验证器交出在场而不是非空串的 `principalInc` ⇒ 身份不装(计数 `auth_bridge_verify_total{outcome="malformed"}`),声明缺席时不铸这一键即可。
197
+ **谁需要**:多租户 k8s 部署里要让成员用自己的 Gitea 凭据 clone / push 的团队、在控制台增删 origin 要不重启就生效的运维。**谁受伤**:坏块的部署(平面读 `no_origins`);按 `sandboxGitPlane` 读数装配的嵌入方。
198
+ **出路**:按 warn 行点名的坐标修控制台上的块;嵌入方交读函数。**升级前做**:无。**回滚**:回到上一版 ⇒ 块被忽略(平面读 `no_origins` 那一侧不变),run 行化身号一格回到恒空。
199
+ 19. **自由文本脱敏的两处已知漏形(安全披露;零行为变更;部署零动作)**:
200
+ 这两形在本版与此前各版都**不遮**,不是本版引入;写在这里,是让按「凭据不会以明文出现在日志 / 转录里」配告警、做审计的部署知道它们在射程外:
201
+ — **复合凭据名在自由文本里的 `名=值`**:`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…` 这类)时才遮,否则在结果、账本回放、流式与同步体、运维日志行上都原样 ——
202
+ 与旁边有没有私钥栅栏无关(私钥块本身照遮)。同一批名字在 JSON 字段(`{"api_key": "…"}`)与 URL 参数(`?api_key=…`)位上照遮;`password` / `passwd` / `pwd` / `secret` / `client_secret` 在赋值符两侧零空白的 `名=值` 形上、值是以字母数字起头的字面量时照遮(`$VAR` / `%s` / `<占位>` / 表达式 / `None` 一类不算值)。
203
+ — **强锚名紧贴在前一枚凭据的尾巴上**(`-----END PRIVATE KEY-----password=…`;PGP 收尾栅栏、PuTTY 的 `Private-MAC:` 行、AWS 键 id 同形;中间没有空白、换行、`?` / `&` 或引号):
204
+ 流式 done 帧、同步体、运维日志行上原样;run 行与账本回放上被遮(读面多过一遍脱敏)。
205
+ **谁受伤**:无(零行为变更)。**读法 / 出路**:要靠脱敏挡住的凭据,别以这两种形写进 prompt、工具输出或错误句;放进 JSON 字段 / URL 参数 / `Authorization` 头这类结构化位置照遮;
206
+ 已有的日志与转录可按这两种形自查。修好的那一版,这些值改成 `«redacted»`(那一版的本文件另写一条)。**升级前做**:无;**回滚**:无关。
207
+ 20. **用户面通告多一键 `eventId`(每次投递的身份),live 帧自此带 `id:` 行;有账本口的腿上,断流后通告与文件卡的死直写不再记「丢帧」,文件卡账本孪生的写失败改记计数(wire 加性;运维面计数口径变化;部署零动作)**:
208
+ `engine_notice` 帧的载荷由五键(`code` · `message` · `detail` · `sessionId` · `ts`)变六键:多 `eventId`(非空串,uuidv7 形)。server 在分流器扇出时给每一条通告铸一枚,同腿的 live 帧与 durable 账本行是同一只行,所以同值;
209
+ live 帧因此发 `id:` 行(值逐字 = 载荷 `eventId`;此前通告帧没有 `id:` 行,契约 §0.6「无锚不发行」名单里不再有它);`GET /v1/runs/:id/events` 的 `id:` 仍是行 `seq`(两个 id 域不可互推)。
210
+ 同一内容的两次投递是两枚。本版之前写下的账本行没有这一键,重放不补。读者从 live 换到 events 车道只能从 0 重放,重放里会再看到 live 上已见过的通告 —— 按载荷 `eventId` 认同一件事(契约附录 D.1「投递律」)。
211
+ 无新帧、无新端点、无新能力位、无新拒码;账本行 `data` 是 JSON,多一键不动 SQL。
212
+ 遥测(运维面):`frame=engine_notice` 与 `frame=file_link` 的 `server.stream.frame-dropped-stream-closed`(断流丢帧)自此**只在本腿没有账本口的部署上记**(无 run 店 ⇒ 无 durable run 行);有账本口的腿上,客户端已断时这一帧在账本里,账本孪生即送达,不再另记 ——
213
+ 丢没丢只由账本那一笔判:通告写失败记 `server.engine-notice.durable-append-failed`(旧有),文件卡账本孪生写失败改记 `server.ledger.forward-append-failed`(`type=file_link`;修前静默吞,工具面仍不 fault;流已断时账本那一笔失败 = 这张文件卡整张没送达)。
214
+ 所以有 run 店的部署上这个丢帧计数会掉:不是壳修好了,是有账本口的那部分不再被当成丢。live 可以多于账本:账本那一笔写失败时,用户在线上看见了这一条、重放里没有。
215
+ 本版保证到哪:在该腿把终局帧入链之前发出的通告,账本里先于终局帧(按停的中断说明即是);终局帧之后才发出的通告(引擎收尾阶段的通告、非 detach 断连之后引擎中止尾巴上的通告、无在册腿时发出的通告)不在保证内(契约附录 D.1)。
216
+ **谁需要**:从 live 换到 events 车道的客户端(无报告 id 的通告码,如中断说明,此前跨车道会渲两次);读遥测查「通告丢没丢」的运维(此前计数把没丢的也算成丢)。
217
+ **谁受伤**:① 严格按「五键」校验通告载荷的消费端(多一键);② 把 live 流最后一枚 `id:` 当续流游标的消费端:游标现在可能落在通告帧上(值仍是 uuidv7,与其它 live 帧同域);
218
+ ③ 按 `frame-dropped-stream-closed` 做看板 / 告警的运维:有 run 店的部署上计数会掉;④ 本版之前写下的账本行没有 `eventId`,对它们跨车道仍去不了重。
219
+ **出路**:消费端接受第六键、按 `eventId` 去重(键缺席回到流内序);看板改按 `server.engine-notice.durable-append-failed` 与 `server.ledger.forward-append-failed` 读「没落账」,按 `frame-dropped-stream-closed` 读「无账本腿上真丢」。
220
+ **回滚**:回到上一版,通告帧回到五键、无 `id:` 行,计数口径回到旧样;已写下的账本行多着一键,旧版读口逐字透传,不受影响。
221
+
222
+
223
+ 21. **更正(7.138.0 节第 19 条 ②,文书;零行为变化)**:待决工具审批行被拒的拒体是**顶层** `reason: "tool_identity_missing"`(`src/http/refusal-body.ts:37`,与 `blockedBy` / `field` 同一层),不是 `detail.reason` —— 本仓拒体没有 `detail` 对象。读那一条的消费方按顶层 `reason` 读;7.138.0 节原文不改(已发节字节不动)。
224
+
10
225
  ## 7.141.0 —— 起服自有凭据值被引擎拒收改发结构化 warn `own_credential_value_not_withheld`(运维面;日志形变更) · 两条用户面文案订正(起服行 `tool_policy_single_user_default` 的 `posture`、`SENSITIVE_WRITE_PATTERNS` 说明;文字,零行为变更) · `/decide` 续跑腿批准后被取消:终局文案改「已取消、不可再续」(行为面;文字;码不变) · 会话同步导入的 `anchors[]` 必须指向这次导入声明的条目(新提交 400;出口过滤存量悬空锚) · 流式口每帧带 `event:` 行(BREAKING)· 回退锚写入腿内落定(有界) · device 车道 `DEVICE_RELAY_URL` 必声明(URL 或 `none`;缺席拒起) · 显式 `DB_BACKEND=mysql|pg` 而该引擎的坐标缺席 ⇒ 起服拒,与 `SESSION_BACKEND` 怎么写无关(行为收紧,fail-closed;运维面) · 起服多一次只读普查:存量停驻行里带「无 `kind` 的 batch 成员」的行数(运维面;零动作;不改任何一行) · 🔴 local 后端数据根一次性迁移:会话的建档时刻与 fork 谱系搬进会话元数据边档、带属主戳的会话存档改写成匿名(本服务起服时自己改;升级前备份数据根、先停光旧进程;回滚只有恢复备份)· 仍带戳的存档 fork / 改日志推送答 409 `conflict`(行为收紧)· 先清后导之后建档时刻不变(行为修复) · 本机形(`DB_BACKEND=local`)后台子代的转录活过重启、随记录同亡;接线清单 `fleet.subagentTranscripts` 在 local 上改答 `full`、`rows` 档不再由本服务产出(行为变更;wire 值变化) · 记忆同步停驻:游标用不了的层只点名删游标一条出路,`GET /v1/sessions/:id/memory-status` 加性键 `memorySync`、停驻层的运维行改名 `memory_sync_layer_parked`(wire 加性;运维面日志形变更;订正 7.139.0 / 7.140.0 两节的处置句) · 在飞 run 的权限模式切档口 `POST /v1/runs/:id/permission-mode`(wire 加性;能力位 `permissionModeWrite` 按部署翻真) · `settings.permissions.deny` / `ask` 的条目可带出处层 `{ rule, layer }`:user 层的 `/` 形路径规则读在执行环境的配置根(wire 条目型放宽 + 能力位 `settings.permissions.layers`;端见位才发) · `GET /v1/runs/:id` 的 `heldBy` 终态行恒 `null`(一个条件:`null` = claim 已释放;wire 值域变化) · 运维队列行 / `/stream` `pending` 帧恰带授予绑定一臂,升级前停驻的卡投 `boundGrantAbsence: "unreadable"`(wire 值域变化) · 沙箱三车道读拒表字节地板的接线与新旋钮 `SANDBOX_READ_FLOOR`(keep | off,缺省 keep;本版地板模块不服务任何车道 ⇒ 三车道照旧不守,零行为变化;起服行多一个字段) · `REMOTE_EXEC` 是有序车道表,表里不止一条 ⇒ 起服拒(部署 env 形变 BREAKING;单词零变化) · `MEMORY_ENGINE_BACKEND=tidb` 改名 `mysql`(旧词拒启;`memoryEngine.backend` 值变化,BREAKING) · `config.d/remote-exec.json` 非空串字段只含空白 ⇒ 起服拒(行为收紧) · 契约包 settings-schema 16.0.0 的其余读点(设备入口拒句取契约 · 本地配置一次读、过深文档成被拒文档 `too_deep`) · SQL 后端(`DB_BACKEND=mysql|pg`)的主 runner 接线清单 `session.store` 由 `process_local` 改答 `declared_durable`(会话店补上顶层耐久声明;wire 值变化;只改读数) · `workflow_complete` 补投帧多可选键 `diagnostics`(引擎的诊断教学文本;过脱敏、有界;wire 加性;零 DDL;崩溃恢复补投不带) · `GET /v1/capabilities` 加能力位 `mcpTransportKinds`(请求面读器与引擎今天能拨的 MCP 传输种类;wire 加性;提交面不放宽)· `GET /health` 加性键 `engineLease: { lingerMs, tickMs }`(引擎附着租约窗上 wire;运维面)· 名单型 env 旋钮点到自有凭据名的起服拒句一句只用一套序号(文字;运维面)· 订正 7.139.0 节第 23 条 ⑤:服务进程被 `SIGKILL` 时暂存目录留在目标旁(文字订正) · 折码的运维 warn 行 `outward_failure_code_folded` 不再带失败句原文、读面同一原码只一行(运维面;日志形变更;安全) · 验证器自己停驻:恢复凭据不再外出、那张卡结算时作废、读面上不合本服务码形的存量失败码读作 `internal.store_error`(行为面;安全 / 修复) · 订正 7.140.0 节第 7 / 10 / 12 / 13 条的四处说法、第 8 条补一句(文字订正) · 契约包 settings-schema 16.0.0 · 引擎 7.60.0
11
226
 
12
227
  1. **起服自有凭据值被引擎拒收改发结构化 warn(运维面;日志形变更;部署零动作)**:本进程自有凭据变量(配置目录 secret 名与模型条目 `apiKeyEnv`)的值被引擎判「不是凭据值」(空白 / 占位词 / 一个字符重复 / 不足 8 字符)时,
package/USAGE.md CHANGED
@@ -449,7 +449,7 @@ MODEL_ID=<your-model-name> MODEL_CHEAP_ID=<your-cheap-model-name>
449
449
  MODEL_CODE_ROLES=default,subagent # 不设=全中立;仅这些角色在「任务自己没说角色基底」时跑编码基底
450
450
  ```
451
451
  - 经 `RoleSpec.promptRole: "code"` 挂在**角色**上(7.137.0 起 / 引擎 7.56.0;此前是把同一段目录文本粘进 `RoleSpec.systemPrompt`),非开发角色保持中立。角色基底只有一条来源链:任务 → 请求的模型角色的预设 → 引擎委派子代的人设,**第一个说出基底的来源整个说了算** —— 任务自带 `systemPrompt`(或客户端注入)、或场景自己写了角色词(cowork 场景写 `cowork`)时任务优先;这些 run 的宿主角色是 `code`(按 `when.host.role` 条件的 overlay 自此对它们生效),基底字节与此前相同。
452
- - **验证门**(core 1.44,opt-in):请求体带 `verify:true`(可选 `verifyRounds`,夹到 [1,5]、默认 2)→ 任务跑完后由**独立只读 verifier**(`verifier` 角色,默认=主模型)证据强制地"试图 break 它",FAIL 则把 findings 注回同 session 续跑修复→重验,循环到 PASS 或轮数上限。结果带 `verification:{verdict,rounds,findings,evidence}`(`verdict` 看质量,`result`/`status` 仍是实现的)。**仅 `/v1/tasks`(同步)与 `/v1/runs`(异步)**——`/v1/tasks/stream` 不支持(多轮非单流,请求 verify 会 400)。verifier 工具默认 = 实现任务工具滤掉 `effect:"write"`(只读边界)。`/metrics` 加 `verifications_total{verdict}`。⚠️ 同步 `/v1/tasks` 形**没有事件通道**(过程帧无处回放,只回终局 JSON)——带 `verify`/`cascade` 的 200 体自 7.23.0 起附可选 `notice` 键指路 `POST /v1/runs`(要过程可见性用异步形)。
452
+ - **验证门**(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
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 天)。
454
454
  - **记忆(文件记忆引擎,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
455
  **`MEMORY_PERSISTENCE_CAPABLE`(三态,7.14.0 起)**=**部署自述**「本机能不能把用户的『记住 X』落到持久处」,与请求键 `memoryWrite` 两轴正交(后者收紧本次运行,前者是 operator 声明;任一取否即只读方向)。缺省不设=引擎按 roster 自行推断;`true`=本部署有推断看不见的持久通道(自定义 writer / 记忆 MCP / 远程执行车道与记忆根共享挂载);`false`=强制只读披露并关掉文件工具往记忆根的写通道。不认得的词拒启。
@@ -497,6 +497,16 @@ MODEL_CODE_ROLES=default,subagent # 不设=全中立;仅这些角色在「
497
497
  - **没配 `MEMORY_EMBEDDER_*` 时门整个不装**(元表不动,旧指纹保留),启动日志 `memory_embedder_identity_absent` 说明:缺席期间**被改写过**的行,其 embedding 本来就被写成 NULL,所以「配置回归同一 identity 后向量续用」只对缺席期间**未被触碰**的行成立。
498
498
  - 残余:同名模型在不同供应商若其实是不同实现(自托管 finetune 撞名),指纹辨不出——明知异实现时改 `MEMORY_EMBEDDER_MODEL` 名,或手动跑上面那条清列 SQL。`MEMORY_ENGINE_BACKEND=mysql` 无向量面(v1 词面档),没有这张元表也没有这道门。
499
499
  📋 **档位对运维可见**:启动日志 `memory_engine_enabled` 行带 `vectorMode` 字段(取自后端实例的真值,不是配置推断)——`lexical` 在跑就必须看得见;配了 embedder 时同行还带 `embedderModel`/`embedderDim`,换模型这件事在日志里留痕。
500
+ - **记忆同步(本机 file 引擎腿 ↔ DB 中心)**:本机腿由 `MEMORY_SYNC_URL`(中心基址;令牌只放 `MEMORY_SYNC_TOKEN`)开起,在起服时与每次任务写进记忆之后后台同步;
501
+ 它的同步身份 = `MEMORY_SYNC_URL` 的规范形(小写协议与主机、无缺省端口、路径去尾斜杠)。中心用 `MEMORY_SYNC_SERVE_IDENTITY` 声明同一个值(能力位 `memorySyncServe.identity` 逐字是它),
502
+ 只服务点名这一身份的轮(不是规范形 ⇒ 400 `request.field_invalid`;别的身份 ⇒ 409 `config.memory_sync_target_mismatch`,零读零写;没声明 ⇒ 501 `feature.memory_sync_serve_disabled`)。
503
+ 壳在把会话推上云之前调 `POST /v1/sessions/:id/memory/sync`(体 `{ target }` = 目标引擎的 `memorySyncServe.identity`;能力位 `sessionMemorySync`),拿到引擎的同步报告:
504
+ 这个会话读的每一层一个结局(`synced` / `incomplete` / `refused` / `failed`),或整轮 `refused`(目标不是本机腿到达的那台 / 会话有在飞 run,或读完它的记忆观测之后又受理了 run / 会话记录前进了)。
505
+
506
+ | env | 缺省 | 说明 |
507
+ |---|---|---|
508
+ | `MEMORY_SYNC_SERVE_IDENTITY` | 缺省(= 本中心不服务同步) | **中心侧**:客户端到达本中心同步口用的基址,必须已是规范形(不是 ⇒ 拒启,拒句给出规范形;带凭据 / 查询 / 片段 / 非 http(s) / 超过 190 字符 ⇒ 拒启)。引擎关着也读。只在 DB 记忆平面上服务同步;file 平面上读了不用 |
509
+
500
510
  - **org 记忆准入(7.0.0 起 BREAKING)**:`org:*` 记忆 scope 分**两个来源**——部署自证
501
511
  (env `MEMORY_SCOPE` 的 org 形 + **单用户部署**的 `projects[].defaultScopes` org 键)直通;**多租户**
502
512
  部署里由调用方 `projectId` 选中的登记簿 org 键算 **request 来源**,必须拿到授权目录的逐 principal
@@ -915,6 +925,7 @@ FLEET_ADVERTISE_ADDRESS=http://<本机可达IP>:8090 # 可选:设了才启公
915
925
  生效与否的观测口=`/health` 世代账键(`configTargetVersion`/`configAppliedVersion`+两 ordinal+`configApplyStaleMs`,见 API 表 `/health` 行)——target≠applied 持续=「改了没生效」的机读信号。
916
926
  - **`run-local`(一次性本机 CLI)上三张审批名单同样执法**(7.101.0 起,TTY 问 / 无 TTY 拒):`APPROVAL_DENY` 当场拒;`APPROVAL_REQUIRE` / `APPROVAL_NEVER_AUTO` 命中 ⇒ stdin 是 TTY 就 `y/N` 问人,否则当场拒并在 stderr 说出路(7.116.0 起按**这一问的类**说,不再对每一问打同一句全量旋钮清单:没人表态的服务器工具调用 ⇒ 指 `APPROVAL_GRANT`;名单产的问 ⇒ 点名三张名单键;部署治理盖了出身的问 ⇒ 点治理旋钮;引擎自己的问 ⇒ 不点旋钮、只说去终端答;引擎没有说明这一问的类 ⇒ 说明这一点、只给终端出路);`APPROVAL_AUTO_BUDGET` 照常生效(never-auto 不吃预算);TTY 问句带上这次调用的参数,人答 N 而停下的 run 退 1(停因 `haltedOnUserRejection`)。**无 TTY 的拒记在部署政策名下**(7.110.0 起;此前记成人拒):该次调用 `tool_end.gate.settlement` = `{kind:"policy_refused", who:{party:"policy"}}`(7.113.0 起;7.110.x–7.112.x 为 `party:"none"`),模型读到的是引擎的策略句(「部署政策拒了、此处没有交互审批,别重试,先做不需要审批的部分」)加一句拒因,而不是「用户不想继续,停下来等他」—— 不停批,模型接着做别的部分;那只要人批的动作没执行 ⇒ run 照样退 1,非 `--json` 形 stderr 停因行是 `task incomplete (approval unanswered): …`。参数展示不出而不问就拒的那一形(TTY 在场也一样)同此。这条腿没有 checkpoint 店也**不**拒启 —— 它有一只当场作答的席,名单的 ask 不需要 park(上表「无 checkpoint 店 ⇒ 整组拒」只说服务腿)。此前三张名单在这条腿上无声不生效。
917
927
  - **仅 `/v1/tasks`(同步)+ `/v1/runs`(异步)**——`/v1/tasks/stream` 不支持(多次尝试非单流,400)。与 `verify` **互斥**(同时给 → 400)。
928
+ - 结果带 `attempts[]`(每级一行:`index` / `model` / `passed` / `status` 逐字;`errorCode` 与 `terminal.code` 同一条对外词法 —— 出了本服务码形的读作 `internal.store_error`);门抛出的 `gateError` 与门附的 `diagnostics` 是自由文本,7.142.0 起过同一只结果脱敏器(认出的凭据片段换成 `«redacted…»`)。
918
929
  - 成本上界 = 任务的 `maxCostUsd`(防冷重跑税)。**三条 core 警示**:① 每档**冷重跑**重付输入成本(便宜档常过才划算);② 门收到**未脱敏**输出(自定义门转发外部 verifier 要脱敏);③ **写工具会跑 N 次**——**只用于只读/幂等任务**(每次升级整任务重跑)。开放式任务(找全 bug/文笔)没有可判定 oracle、级联会空转,那种让模型用 Agent 工具委派几路子代再综合(广度交叉),而非级联(深度阶梯)。
919
930
 
920
931
  **可选 — AskUserQuestion 的裁量窗(问活人的三个帽子)**
@@ -1821,7 +1832,7 @@ PostgreSQL 硬上限 ⇒ 只有缩小),运维日志一行 `submit_ctx_row_too_la
1821
1832
  | `objective` | ✅ | 要做什么(自然语言)。**唯一必填。** |
1822
1833
  | `sessionId` | — | 续聊:带上次返回的 `sessionId`,服务端自动 wake 历史。**只收 canonical uuid**(任意版本,小写;与会话口同一只形判):自选名 / 业务键 / 空串 ⇒ `400 request.id_invalid`,缺省请**省略这个键**(服务端现铸 uuid)。想要好记的名字,自己记 name → uuid 的映射 |
1823
1834
  | `images` | — | 图文输入 `[{data,mimeType}|{url}]`(模型需支持 vision)。**随提交的这条目标一次送达**:续跑 / 投递腿(审批 / 资源停车 / wake / plan_review 续跑、在宿主会话上新起的一跑)重建配置时不再带它(与 `userAttachments` 同律;图在首跑已是会话里的消息) |
1824
- | `attachmentIds` | — | D-1 通用文件上传(1.289+):先 `POST /v1/attachments?name=…`(raw body,content-type=mime)拿句柄,提交时引用 ≤16 个;文件物化到执行环境工作目录 `attachments/` 下,objective 尾部自动追加文件清单(内容不进会话流)。单文件缺省 ≤32 MiB(`ATTACHMENT_MAX_BYTES`);可配 mime 白名单(`ATTACHMENT_MIME_ALLOWLIST` CSV,缺省不限);上传后未引用的按 `ATTACHMENT_UNBOUND_TTL_MS`(缺省 24h)回收。**云形态(tidb/pg)字节本体存对象存储——MinIO 必配**(`MINIO_ENDPOINT/MINIO_ACCESS_KEY/MINIO_SECRET_KEY`,与快照 lane 同一组变量),未配则附件面 501;local 形走本地文件店。 |
1835
+ | `attachmentIds` | — | D-1 通用文件上传(1.289+):先 `POST /v1/attachments?name=…`(raw body,content-type=mime)拿句柄,提交时引用 ≤16 个;文件物化到执行环境工作目录 `attachments/` 下(沙箱车道在恢复点放:沙箱铸出或恢复之后、交给任务之前),objective 尾部自动追加文件清单(内容不进会话流)。一只上传一经绑定就属于那个会话,交给别的会话 ⇒ `409 attachment.bound_elsewhere`(7.142.0 起;每个会话各传一次)。mime 为 `application/vnd.sema.project-bundle` 的是**项目包**(工作区的种子,只进沙箱车道,见 §9.3j)。单文件缺省 ≤32 MiB(`ATTACHMENT_MAX_BYTES`);可配 mime 白名单(`ATTACHMENT_MIME_ALLOWLIST` CSV,缺省不限);上传后未引用的按 `ATTACHMENT_UNBOUND_TTL_MS`(缺省 24h)回收。**云形态(tidb/pg)字节本体存对象存储——MinIO 必配**(`MINIO_ENDPOINT/MINIO_ACCESS_KEY/MINIO_SECRET_KEY`,与快照 lane 同一组变量),未配则附件面 501;local 形走本地文件店。 |
1825
1836
  | `userAttachments` | — | 请求层附件(≤20 条),原样交给引擎、随**这条目标**送到模型:`{ path, lines? }` 读这次 run 的工作区 / `{ name, text, startLine? }` 宿主给的文本 / `{ name, image: { data, mimeType } }` 宿主给的图片(base64)。形判属主是引擎:坏形、带行号超 Read 上限(100 000 字符)的 text、过不了准入的图 ⇒ `400 config.user_attachment_invalid`(`error` 以字段路径起);`path` 只在有手场景(这次 run 有工作区)受理 —— 无手场景(`scan` / `code-review`、无手叠层、`provider: "in-process"` 的部署)上整条 `path` 条目同码 400,改发 text / image 条目;能力位 `supplies` 是席位能承载的供给,不按场景裁 —— **这个场景在本部署上受理哪些**读 `GET /v1/capabilities/scenarios/<name>` 的 `attachmentSupplies`(7.133.0 起;席位 `supplies` 的子集,不含 `path` 的场景发 path 条目必同码 400,发之前读它,不必撞 400);读不到的文件不拒,它的消息里说明没附上,结局见流上目标那一帧 `human_input.attachments`。只随目标走:steer / interrupt 带它 400,续跑不重发。带附件的提交在第一次回复出来之前被切断(halt / interrupt)时,这次 run 不落回退锚 —— 拿它的 taskId 去 `resumeAt` ⇒ `404 resume_at.unknown_event`,请显式给 resumeAt。与 `attachmentIds` 分工:那条把文件放进工作区让模型按需去读(内容不进上下文),这条把内容直接送进上下文。能力位 `userAttachments` |
1826
1837
  | `scenario` | — | 省略时取本部署的 `DEFAULT_SCENARIO`(工厂缺省场景名 = **`code`**——CC 编码 persona 蒸馏版,default 全量工具面,终验旋钮独立;`default` 场景**已非出厂缺省**,要用它须显式传 `scenario:"default"`,或部署方显式设 `DEFAULT_SCENARIO=default`)。可选内建名:`default`(通用/助理场景,域中性 persona,CC agent loop 全量工具面)/ **`cowork`**(办公向场景,**7.133.0+**:`default` 的工具面去掉编码类工具 —— 不带 `Bash` / `Edit` / `Write` / `NotebookEdit` / `RepoMap` / `LSP` / `EnterWorktree` / `ExitWorktree` / `Monitor`,留 `Read` / `Glob` / `Grep` 文件读、浏览(WebFetch / WebSearch)与连接器挂载;role 层是 core 的 Cowork 角色基底,调用方的 `systemPrompt` 照样整替它;仍挂手,所以附件 `path` 在有执行环境的部署上照常受理;你自己请求体里的 `excludeTools` 与它取并,只会多卸)/ `code-review`(见 §5)/ `scan`(同 §5 的 repo 只读工具但**中性无框架提示词**——objective+中心下发 skill 全权主导输出,OA 扫描类用;与 `code-review` 同属 clone-free 无执行环境场景,见 §5 末)/ **配置控制面可声明任意新场景**(`{name, toolset: none\|repo-readonly, prompt?}`,组合即配置、能力写死在部署;restart-to-apply;center 可覆盖内建名,boot 日志 `config_center_scenarios.shadowsBuiltin` 可审计)。**缺省与指派都必须是本部署在册的场景名,悬空不回落 `default`**:`DEFAULT_SCENARIO` 不在册 ⇒ **起服拒**(拒句点名那个名字与在册词表);中心按主体指派的场景名本部署没有 ⇒ 该主体的 run 答 **503 `state.scenario_assignment_unknown`**(体 `detail: { scenario, source: "ruling" }`,不带 `Retry-After`;修法 = 中心改指派,或把那个场景加回本部署,然后原样重试);请求体显式给的未知名仍是 400 `scenario_unknown` |
1827
1838
  | `repo` | — | `code-review`/`scan` 必填,见 §5(7.126.0 起 `council` / `debate` / `lenses` / `rounds` / `team` / `businessContext` 是未知键 ⇒ 400:它们喂的评审团腿与 `discuss` 场景随引擎 7.49.0 删掉团队运行时一并删除) |
@@ -2376,7 +2387,7 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
2376
2387
  | HTTP `429` + `errorCode:"usage.window_exhausted"` | 部署级治理窗耗尽(`USAGE_WINDOWS`,token 或 $ 天花板先满者) | 看响应体 `retryAfterSec` 退避;窗滑动/桶到期后放行 |
2377
2388
  | `terminal:{kind:"failed", code:"usage.window_exhausted"}` | 已受理的 run 在 **turn 边界**撞上治理窗且无法 durable 挂起 | 同上退避后重投;配好 checkpoint 基建则改为 `suspended` 等窗自动续跑 |
2378
2389
  | HTTP `501`(`/v1/runs`) | 内存模式不支持异步 | 配持久存储(`DB_BACKEND=mysql` / `pg` + 坐标;7.106.0 起给了坐标必须点名 `DB_BACKEND`) |
2379
- | HTTP `500` + `errorCode:"corrupt_session"`(7.104.0 起;流式口在应答头之后则是 `done` 帧的 `result.terminal.code`,后台 run 是 run 行的 `errorCode`) | 这只会话的**存档读不出**、唤不醒(文件后端:会话日志没有会话头;SQL 后端:一行事件读不出)。这一轮没跑,一个 token 没烧。直接读这只会话的口(头探针 / 事件订阅 / 会话视图 / 改名 / 导出 / 分叉 / 2c 同步)同答(契约 §17;文件后端上事件订阅与改名 7.136.0 起同答,此前一个 200 起流却零帧、一个 404) | **别重试**(确定性失败)。**文件后端**:运维看 `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`)。要继续工作就换一个新会话 |
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`)。要继续工作就换一个新会话 |
2380
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` 重发会重新受理(这类零工作的拒不占键) |
2381
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` 收不到通告帧(只有运维面的计数与探针) |
2382
2393
  | 流里一帧 `engine_notice` `code:"route.finish_reason_unknown"`,`detail:{ finishReason, sessionId, runId }`(7.126.0 起) | 提供方用一个引擎读不懂的结束原因结束了这一轮:回答按模型留下的样子保留,但可能被提供方截短了 | 告诉用户「这一段回答可能不完整」;同一个词同一 run 只说一次。运维按 `finishReason` 查是哪家网关 / 模型发的 |
@@ -2391,13 +2402,15 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
2391
2402
 
2392
2403
  **进程警告面(日志采集方注意)**:引擎(core ≥5.16)在持久 agent 行的 org 判决写回被丢弃等场合走
2393
2404
  Node `process.emitWarning`,警告文案前缀固定 `sema durable-agents:`——采集规则按该前缀匹配,别当噪音过滤掉。
2394
- server 自身的兜底留痕走 stderr 契约行 `[sema] fail-open: <tag>`(逐 tag 一次)+ `fail_open_total` 指标,
2405
+ server 自身的兜底留痕走 stderr 上的一行 JSON 日志(`msg` 逐字 `[sema] fail-open: <tag>`,字段 `{ tag, count }`,逐 tag 一次,不随 `LOG_LEVEL`)+ `fail_open_total` 指标,
2395
2406
  tag 词表与每条的最坏后果见 `docs/FAIL-OPEN-CENSUS.md`。引擎装配期忠告(core `onError(phase:"config")`)落
2396
2407
  `runner_config_advisory` 日志行,带结构化 `code`(如 `config.toolpolicy.unmatched_names`)与 `classification`
2397
2408
  (core 给宿主定级的判别词;词表外原样透传并标 `classificationKnown:false`),指标 `runner_config_advisory_total`
2398
2409
  按 `code` / `classification` / `level` 打标;其中 `shell-gate-off` 在本 session 显式声明 `permissionMode:
2399
2410
  "bypassPermissions"` 时是调用方要的姿态,降为 **info** 并标 `expected`——只有没表态落到引擎默认 off 的无壳直连
2400
- 形才 warn。子代 `agents[].denyTools` / `allowTools` 的**名字**归引擎开跑前一只判(7.126.0 起与根 run 的 `allowTools` / `excludeTools` 是同一只工具选择器):
2411
+ 形才 warn。引擎交来的错自带机器码(`err.code` 是非空串、且**是码**:不超过 64 字符的短 token,不含空白 / `=` / 换行,也不是 `sk-…` / `AKIA…` / `ghp_…` 这类凭据形)时,`onError` 汇点上的日志行一律把它写成结构化 `code` 字段 ——
2412
+ `mcp_server_unavailable` / `a2a_peer_unavailable` / `task_degraded` / `prompt_composition_warning` / `prompt_cache_break` / `runner_config_advisory` / `runner_error` 同一条律(如
2413
+ `mcp.tools_capability_absent`:MCP 服务器没申报 `tools` 能力而 `allowTools` 非空)。告警规则按这个字段匹配,别去句子里找(句子不一定带那串码);错不带码(没有 / 不是串 / 空串)或带的不是码时该键缺席(`runner_config_advisory_total` 的 `code` 标签同判);记忆引擎事故行 `memory_engine_incident` 与计数 `memory_engine_incident_total` 的 `code` 位、起服面三只 operator 面引擎座(`memory_compliance_incident` / `memory_origin_incident` / `memory_consolidation_incident`)的 `code` 位、子代额外工具故障行 `subagent_extra_tools_error` 的 `code` 同一条律(无码 / 不是码 ⇒ `code` 键与标签缺席,不写 `null` / `none` 占位)。子代 `agents[].denyTools` / `allowTools` 的**名字**归引擎开跑前一只判(7.126.0 起与根 run 的 `allowTools` / `excludeTools` 是同一只工具选择器):
2401
2414
  7.129.0 起(引擎 7.51.0)没有任何工具答应、也不是某只已挂载工具另一种拼法的名字(**真未知名**)**引擎不再拒跑**:条目原样保留(今天什么都不拒 / 不准,之后挂上同名工具即生效),运维行 `runner_config_advisory` 每条告知一次(句子带该写的拼法;委派工具声明了逐次注入工具的名册,引擎列得出子代工具池);
2402
2415
  引擎开跑前拒(`config.agent.unknown_tool`)的是任何工具名都不可能有的拼法(空白、括号不配、空名)、非字符串条目,以及(引擎 7.52.0 起,两侧同判)写成**已挂载工具另一种拼法**的条目:大小写孪生(`BASH`)与有后继的退役名(`Task` ⇒ `Agent`、`bash` ⇒ `Bash`)。**7.131.0 起本服务把其中写错现名的在受理口前置拒:限制侧四类全拒、放行侧只拒近似名** —— `denyTools:["BASH"]` / `["bash"]` 以为禁了 `Bash`、子代照用(静默放行),`allowTools:["BASH"]` 想放 `Bash` 却什么都没放(静默收窄);
2403
2416
  提交 400 `config.agent.unknown_tool`(根 run 的清单是 `config.tool_face_control`),句子点名条目、原因词与现名;放行侧的**内容形**(`Agent(Explore)`)与没有后继的退役名(`MultiEdit`)仍照 7.130.0 受理、交引擎既有通告,选择器清单(根 / 子代 `allowTools`)里**有后继的退役名**(`Task` · `bash`)7.132.0 起由引擎开跑前拒,规则表里的退役名照旧受理、引擎开跑时告运维;文法读不出的放行条目授不出去 = 收窄而不是放行:规则表(`permissions.allow` / 技能 `allowedTools`)上受理口不拒、引擎开跑时告运维;选择器清单(根 / 子代 `allowTools`)上受理口不拒、引擎开跑前自己拒(子代 ⇒ `config.agent.unknown_tool`、根 ⇒ `config.tool_face_control`,同步腿都是 `400`〔7.132.0 起;此前子代这一形答 HTTP 200 + failed 终局体〕;异步 / 活流腿 HTTP 2xx、同码落在终态 run 行);存量体续跑见 MIGRATION 7.131.0 第 8 条。判官仍是引擎的条目读法。**真未知名**(没有近似也没有退役对应:懒挂载的合法形)照旧只告运维、不拒。名字按工具名原拼写 `Bash`。这两张表管子代可挂载的**全部**工具 —— 委派池、内建 hands(`Read` / `Edit` / `Write` / `NotebookEdit` / `Bash` / `Grep` /
@@ -2558,6 +2571,7 @@ k8s 车道每条 run 一只新 pod(pod 寿命 = 泄漏护栏,不跨 run 复用)
2558
2571
  它恰是服务为 env Secret 真发的调用;不要给 `get`(服务从不读回,给了就读得到命名空间里每一只 Secret)与 `delete`(Secret 随属主 pod 由集群回收)。租约 Secret 的 `patch` 只在 §9.3h 的平面打开时要。
2559
2572
  名单非空时起服各问一次 SelfSubjectAccessReview:答了「不许」⇒ **拒启**,拒句带 `[sandbox-secret-rbac]`、点名缺的动词与这条规则;问不到(API 连不上 / 审查本身被拒)⇒ 照常起服,
2560
2573
  一行 `sandbox_secret_seat_unverified` + fail-open 计数 `server.sandbox-secret.seat-unverified` —— 那时若 Role 真缺,第一个要转发的 run 在连接时被拒、它的 pod 被删。
2574
+ 这一行的 `detail` 是哪只动词没问到、为什么(请求失败的原因句);原因句与起服面别的运维行里的异常句同一读法:认出的凭据形换成记号、剥控制字符、界 200 码元。
2561
2575
  - **放弃的 pod 删不掉(运维面,7.137.0 起)**:上面这类拒绝(env Secret 建不成 · 成员凭据代理的租约 Secret 建不成 · 代理镜像拉不下)要删那只刚建的 pod;这一删自己失败(请求抛 / API 回 403 或 5xx,比如 Role 缺 pods `delete`)时,
2562
2576
  连接的拒答不变,服务多记一行 error `sandbox_pod_delete_failed`(`pod` / `namespace` / `why` / `cause`,不带值、不带 API 文本)+ fail-open 计数 `server.sandbox-pod.delete-failed`;pod 与它名下的 Secret 留给回收器(§9.3g)。
2563
2577
  计数持续非零就按 `cause` 核 Role 的 pods `delete` 与 API 可达性。
@@ -2633,7 +2647,8 @@ k8s 车道每条 run 一只新 pod(pod 寿命 = 泄漏护栏,不跨 run 复用)
2633
2647
 
2634
2648
  **起服自检**(`REMOTE_EXEC=k8s`,起服一次、不挡起服):服务用自己的 k8s 账号读节点表(`nodes` get / list,集群级)与沙箱 ns 的 NetworkPolicy(`networkpolicies` get / list),
2635
2649
  按「选中沙箱 pod 的 Egress 策略的放行规则并集」判每个节点的每个 InternalIP / ExternalIP。读出的缺口每个一行 warn `sandbox_egress_policy_gap`(带 `effect` 后果句)并计数
2636
- `server.sandbox-egress.policy-gap`(fail-open 登记表第 135 行,记债);零缺口一行 info `sandbox_egress_policy_checked`。`kind` 闭集:
2650
+ `server.sandbox-egress.policy-gap`(fail-open 登记表第 135 行,记债);零缺口一行 info `sandbox_egress_policy_checked`;自检本身半途抛了一行 error `sandbox_egress_policy_check_crashed`。
2651
+ 读不到时 `detail` 与 `error` 里的原因句(客户端异常原文)认出的凭据形换成记号、剥控制字符、界 200 码元。`kind` 闭集:
2637
2652
 
2638
2653
  | `kind` | 什么时候 | 去哪改 |
2639
2654
  |---|---|---|
@@ -2710,11 +2725,15 @@ ssh / adb / local-docker 三条车道的流式调用同读这一条律:没有车
2710
2725
  租约卷只读、0400、只挂代理容器;`shareProcessNamespace:false`;连接要等代理容器就绪(exec 探针)才算成。部署的 pod 补丁在代理在场时只许 `volumes` / `volumeMounts` / `containerSecurityContext` / `labels`
2711
2726
  (容器级只落 agent 容器),**`podSecurityContext` 整只不许** —— 用了它的部署,平面关(`pod_patch_reaches_broker`)而不拒启;把 pod 级加固挪进 `containerSecurityContext`。
2712
2727
  - **平面开没开,看一处**:`GET /v1/capabilities` 的 `credentialPlane.sandboxGit`(`on` + 逐 origin `delivers`,或 `off` + 第一个不成立的前提词,九词闭集与判序见契约附录 B)。
2713
- 起服时若是 k8s 多租户部署,日志一行 `credential_plane_sandbox_git`(`state` / `reason` / `why`)。
2728
+ 读数每次读时求值(起服读数 —— 秘密座答复、补丁判官 —— 加当前生效配置的 `sandboxGit` 块),不是起服时存下的一份。起服时若是 k8s 多租户部署,日志一行 `credential_plane_sandbox_git`(`state` / `reason` / `why`,记的是那一刻的读数)。
2729
+ - **代理哪些 origin**:有效配置的 `sandboxGit` 块 —— 控制台按部署登记册投影,只在本 worker 自己的拉取上出现(配了 `SEMA_WEB_ADMIN_WORKER`);没有 env 旋钮,本地配置源(`CONFIG_PROVIDER=local`)没有这一块。
2730
+ 块随有效配置的每次提交热换代、不签重启(刷新拍 / 迟到起服 / 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 }`。
2714
2733
  - **旋钮**:`K8S_SANDBOX_BROKER_IMAGE`(代理容器的镜像;建议填本引擎自身镜像 —— 代理是本包里固定路径的程序;不设 ⇒ `broker_image_unset`)。Role 在 §9.3c 的 `create` 之外还要 `patch`(改写租约;租约 Secret 只用这两个;本版平面恒关,不用先给)。
2715
- - ⚠️ **7.137.0 平面恒 `off`**:两处读口还没接线 —— 取值口的 worker 凭据、有效配置的 `sandboxGit` 块;读数停在 `no_caller_credential`(或更前的词),没有 pod 会挂上代理。
2716
- run 行的身份来源不是接线点:读的是受理时记在 run 行上的那一份身份。本节写的是组件的行为与边界;
2717
- 打开平面的那一版会在本节补上取值与续期。
2734
+ - ⚠️ **平面仍恒 `off`**:一处读口还没接线 —— 取值口的 worker 凭据;有效配置的 `sandboxGit` 块已接(上一条)。读数停在 `no_caller_credential`(或更前的词),没有 pod 会挂上代理。
2735
+ run 行的身份来源不是接线点:读的是受理时记在 run 行上的那一份身份(连同登录令牌的 scope 与化身号 `sub_inc` 一起记下;化身号超过 64 字符不记,一行 `run_identity_claim_unrecorded`)。
2736
+ 本节写的是组件的行为与边界;本版没有取值与续期(它们随 worker 凭据接线一起落),所以任何凭据都不进 pod。
2718
2737
 
2719
2738
  ### 9.3i 沙箱三车道的读拒表字节地板:`SANDBOX_READ_FLOOR`(7.141.0 起;本版模块不服务任何车道)
2720
2739
 
@@ -2728,6 +2747,26 @@ ssh / adb / local-docker 三条车道的流式调用同读这一条律:没有车
2728
2747
  ② 一次不带地板的引擎起动(流式执行面,以及本服务里几处替引擎干活的内部调用方今天都不带)—— 句子点名车道与 `SANDBOX_READ_FLOOR=off`。
2729
2748
  - **不想守**:`SANDBOX_READ_FLOOR=off`。块如实答 `false`;读拒表非空的 run 照旧每 run 一条运维通告 `config.read_deny_floor_unkept`,部署设了 `READ_DENY_FLOOR_REQUIRED=true` 时准备期拒(同码)。
2730
2749
 
2750
+ ### 9.3j 沙箱三车道:项目包 —— 没有 git 的项目作为工作区的种子(7.142.0 起)
2751
+
2752
+ 没有 git 的本机项目经附件链上云:上传一只**项目包**,在会话的首次提交里绑定,沙箱车道(e2b / k8s / local-docker)在为这只会话**铸出新工作区**时把它解到工作根本身。
2753
+
2754
+ - **形**:gzip 压缩的 POSIX tar(`tar czf` 的形),顶层就是工作根(不要包装目录);只放普通文件、目录、相对符号链接(落点按包自己的树解 —— 经过包里别的链接 —— 仍在包内);不放顶层 `.git`(有 git 的项目走 git 引用)与顶层 `attachments`(附件的落点);
2755
+ 不经符号链接写子路径;每条路径 ≤ 4095 字节、每段名字 ≤ 255 字节;pax 扩展头只收 `path` / `linkpath` / `size` 与元数据(时间、属主、扩展属性、ACL —— 解出来不带),稀疏文件等改变内容读法的扩展拒(`entry_type`);
2756
+ 展开 ≤ 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`(被拒条目的落点;放不下的超长路径不带)。
2758
+ 配了 `ATTACHMENT_MIME_ALLOWLIST` 的部署要把这个 mime 加进去,否则 415。
2759
+ - **绑定**:提交体 `attachmentIds: [id]`(首次提交;重试带同一 id 幂等)。车道不隔离 ⇒ `409 attachment.bundle_lane_not_isolated`(先读能力位 `executionLane.isolated`);会话已有种子 ⇒ `409 attachment.bundle_seed_fixed`;
2760
+ 这只上传已绑在别的会话上 ⇒ `409 attachment.bound_elsewhere`(一只上传只给一个会话 —— 这条对所有附件都成立)。
2761
+ - **什么时候解**:只在车道铸出新工作区、且没有可恢复的树时(k8s:空间没有 `latest` 或 `latest` 的归档已没;e2b / local-docker:每只新沙箱),解完再放附件。
2762
+ 恢复出来的工作区(k8s 空间的 `latest`、停驻快照的续跑)从不解种子,所以模型改过 / 删过的文件不会被种子还原。fork 出来的会话不带种子。委派子代不解。
2763
+ **跨回合是否保留改动看车道**:读能力位 `workspaceSync.persist === "space"`(k8s 工作区空间)才保留;e2b / local-docker / `WORKSPACE_PERSIST=off` 的 k8s 每个新回合都从上传态开始。
2764
+ k8s 工作区快照剔除名单(`WORKSPACE_SNAPSHOT_EXCLUDE`)里的目录:首轮收尾快照不含它,下一轮恢复时它不在,种子也不补。
2765
+ - **镜像契约**:沙箱镜像要带支持 gzip 的 `tar`(缺 ⇒ 这一次执行环境拒,句子点名 `tar`);镜像若在工作目录预置了与项目顶层同名的内容 ⇒ 拒,句子点名路径(不覆写、不合并);
2766
+ 镜像别在工作根的祖先目录放 `.git`(引擎按向上找到的那只仓判项目身份)。
2767
+ - **运维**:每次播种一行 info `workspace_seeded{sessionId, bundleId, entries}`;删会话时种子随附件一并删。
2768
+ - **不删包**:首交成功之后不要 `DELETE /v1/attachments/:id` —— 无持久的车道每个新回合都要再解它。
2769
+
2731
2770
  ### 9.4 auto 模式(`permissionMode:"auto"`):武装三态、本地 org deny、自查(与 core 的意图武装式合修)
2732
2771
 
2733
2772
  **背景**:此前引擎只在「组织授予」(web-admin 解出的 per-principal `runtimeCaps.autoMode === true`)时武装
@@ -2966,6 +3005,11 @@ WebSearch 工具的 `tool_result` 错误文本,回到模型的对话里,**不是
2966
3005
  - 每个 header 值剥 CR / LF / NUL;header 名不展开。
2967
3006
  - 置空(名单外 / 凭据名)时日志一行 `hook_http_header_env_blanked`(名字与原因,不带值)。
2968
3007
  例:`"headers": { "Authorization": "Bearer ${HOOK_TOKEN}" }`、`"allowedEnvVars": ["HOOK_TOKEN"]`,并在请求 `settings.env` 里给 `HOOK_TOKEN`。7.125.x 及更早的读法不同(升级见 MIGRATION)。
3008
+ **两张名单 `settings.allowedHttpHookUrls` / `settings.httpHookAllowedEnvVars`(7.142.0 起;CC settings 同名键)**:读法与判决都在契约包 settings-schema,本服务只在 http 条目**拨出之前**问一次(能力位 `taskSettings.httpHookAllowlists`,单用户车道为真):
3009
+ - `allowedHttpHookUrls`(URL 模式的串数组,例 `["https://hooks.example.com/*"]`)在场 ⇒ 条目 `url` 必须命中其中一条才发出;不命中 / 名单是 `[]` / 名单不是串数组 ⇒ **一个字节都不发**。非托管组:这次 hook 算非阻断故障(fleet 流 `hook_notice` `url_not_allowed`,运维 warn `hook_http_withheld`);
3010
+ 托管组(`source: managedPolicy` / `managedPlugin:*`):失效关闭,`PreToolUse` 在没有否决时改问人。句子点名 `settings.hooks.<事件>` 与规则(`url` / `empty` / `malformed`)。出路:把地址的模式加进名单,或改 hook 的 `url`。
3011
+ - `httpHookAllowedEnvVars`(变量名的串数组)在场 ⇒ header 能展开的变量 = 条目 `allowedEnvVars` 与它的交集,交集外的引用置空(上面那行 `hook_http_header_env_blanked`,`not-allowed`)。
3012
+ - 两张都不写 ⇒ 不限(与 7.141.x 同)。提交时不判名单的值;续跑重放同读。多租户车道 `settings.hooks` 整只被忽略,两键无作用。
2969
3013
  **`url` 不得带凭据(7.135.0 起)**:`http` 条目的 `url` 带 userinfo 或凭据名表里的 query / fragment 参数 ⇒ 新提交 `400 request.field_invalid`,体带 `field: "settings.hooks.<事件>[g].hooks[h].url"` 与 `reason`;凭据走同一条目的 `headers`(上面的 `$NAME` + `allowedEnvVars`)。整条规则与出路见 9.5g。
2970
3014
 
2971
3015
  ### 9.5c-bis `settings.hooks` 的执行模型与时限(7.132.0 起)
@@ -3126,6 +3170,10 @@ WebSearch 工具的 `tool_result` 错误文本,回到模型的对话里,**不是
3126
3170
 
3127
3171
  **日志不是原形面**:运维结构化日志里 `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` 里(日志行与会话流行同)认不出,机读位的规则文本是在原串上认的、不受此限。
3128
3172
 
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「自由文本脱敏的两处已知漏形」)。
3176
+
3129
3177
  ### 9.7 bake-runner(构建宿主守护进程)的 env(7.103.0 起成文)
3130
3178
 
3131
3179
  `bake-runner`(`npm run start:bake-runner` = `node dist/bake-runner/main.js`)是跑在构建宿主上的**独立进程**:它向 image-api
@@ -3382,12 +3430,19 @@ web-admin 拉取 / fleet 公告 / 凭证续签、WebSearch、对象存储、OTLP
3382
3430
  - **按车分库是硬规则**(7.105.0 起):点了名的每一条都必须显式给出非空的 `GATE_<名>` —— **在场但为空**
3383
3431
  (`GATE_INNODB_TEST_URL=""` 想关掉那条腿)⇒ `REMOTE_GATE_DSN_EMPTY` 红停;**缺席** ⇒ `REMOTE_GATE_DSN_ABSENT` 红停。脚本不再有共享库缺省
3384
3432
  (旧形把空串 / 缺席回落到共享的 `sema_server_test` 等库,车QC 的锁形套在共享库上整表 DELETE 了别车的行)。要关一条腿:别在 `--dsn` 里点它;
3385
- 要用:给按车库(`eval "$(node scripts/db-lane.mjs <车>)"` 建 `aiagent_test_<车>` / `sema_server_test_<车>`,再显式 export 三条
3386
- `GATE_*_TEST_URL` 指 k3s8 上那几只库);发车链由 `scripts/chain-launch.sh` 显式传共享库那三条(唯一合法的「用共享库」)。
3433
+ 要用:给按车库(`eval "$(node scripts/db-lane.mjs <车>)"` 建 `aiagent_test_<车>` / `sema_server_test_<车>`;起它时 shell 里已有的
3434
+ `GATE_*_TEST_URL` 跟着换成同一只车道库,没有就自己显式 export 三条指 k3s8 上那几只库);发车链由 `scripts/chain-launch.sh` 显式传三台引擎的坐标,
3435
+ 门 2 的真库组进组时各自换成自己的车道库(下一条)。
3387
3436
  值**不进任何 argv**:经 ssh 的 stdin 送到远端 shell,再以 `docker run -e <名>`(只有名字)交给容器 —— 本机与 k3s8 的 `ps` 都看不见口令,
3388
3437
  值里带 `&` / 空格也不会切断远端命令;值里有换行 ⇒ 红停。远端先 unset 点了名的每一条、只收点了名的名,核每一条都收到且解得开才起容器
3389
3438
  (没收到 ⇒ `REMOTE_GATE_DSN_LOST`,解不开 ⇒ `REMOTE_GATE_DSN_BAD`),核完打一行机读 `REMOTE_GATE_DSN handed=<名,…|-> skipped=<n>`
3390
3439
  (skipped = 闭集里这一跑没交给容器的引擎数);发车链把它抄上 `GATE2 …` 读数行的 `skipped=`(本机臂按换门交出的名数;远端红 ⇒ `unknown`)。
3440
+ - **门 2 的真库组各跑在自己的车道库上**(7.142.0 起):一次套件跑不与任何别的进程共用一只库。门 2 的组只要「门 × 凭据」表把引擎 DSN(本机腿或远端孪生 `GATE_*`)交给它,
3441
+ `gate2_group` 进组时就经 `scripts/db-lane.mjs <车道>` 建库并暖库(生产 boot 同款全部 ensure 腿;实测约 14 s),车道 DSN 只在组的命令的子 shell 里导出,出组即删(组红也删)。
3442
+ 车道名 = 本次 `release.sh` 的前缀 `r<pid>_<epoch>` + 组名(`.` 换 `_`),链日志每组一行「车道库 <车道> 已建」。建不成 ⇒ 那一组红停(不回落共享库);删不掉 ⇒ 响亮一句,
3443
+ 手收:`node scripts/db-lane.mjs <车道> --drop`。起链者传的六条 DSN 只是引擎坐标:链里没有一组读写共享测试库的表,手跑的钉 / 别的车 / 别的仓在共享库上留下的瞬时行进不了门 2,
3444
+ 链里的破坏性钉也不落在别人用的库上。`db-lane.mjs` 三条腿同一坐标读法(手里那条 DSN;InnoDB 缺席才起夹具),坐标在手的腿建不成 ⇒ 整只车道失败,`--drop` 则三条腿逐只都试、没删掉的逐只点名并 rc 1;暖库的 DSN 只从环境读
3445
+ (`TIDB_TEST_URL=… PG_TEST_URL=… npx tsx scripts/db-lane-warm.ts`)。
3391
3446
  - **起链 = `scripts/chain-launch.sh <version>`**(7.105.0 起;取代 sema-internal 的 chain-helper):
3392
3447
  ```bash
3393
3448
  nohup bash scripts/chain-launch.sh 7.105.0 > scratchpad/chain-7.105.0.log 2>&1 & disown # 在 node_modules 是真目录的树(主树)里