@sema-agent/server 7.131.0 → 7.132.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 (256) hide show
  1. package/MIGRATION.md +309 -0
  2. package/README.md +5 -1
  3. package/README.zh-CN.md +3 -1
  4. package/USAGE.md +163 -54
  5. package/dist/approval-card.d.ts +3 -1
  6. package/dist/approval-card.js +2 -3
  7. package/dist/audit.d.ts +7 -10
  8. package/dist/auth-bridge.d.ts +4 -0
  9. package/dist/auth-bridge.js +10 -11
  10. package/dist/boot/deployment-mods.d.ts +31 -0
  11. package/dist/boot/deployment-mods.js +26 -0
  12. package/dist/boot/execution-env.d.ts +1 -1
  13. package/dist/boot/execution-env.js +19 -6
  14. package/dist/boot/memory-boundary.d.ts +2 -2
  15. package/dist/boot/read-deny-anchor-audit.d.ts +17 -0
  16. package/dist/boot/read-deny-anchor-audit.js +36 -0
  17. package/dist/boot/resolve-spec.d.ts +1 -5
  18. package/dist/boot/resolve-spec.js +19 -47
  19. package/dist/boot/runner-deps.d.ts +19 -8
  20. package/dist/boot/runner-deps.js +14 -2
  21. package/dist/boot/session-faces.d.ts +1 -7
  22. package/dist/boot/stage-03-store-backend.d.ts +1 -1
  23. package/dist/boot/stage-03-store-backend.js +2 -2
  24. package/dist/boot/stage-04-stores.d.ts +1 -1
  25. package/dist/boot/stage-05-execution-env.d.ts +4 -4
  26. package/dist/boot/stage-05-execution-env.js +7 -10
  27. package/dist/boot/stage-06-runners.d.ts +2 -2
  28. package/dist/boot/stage-06-runners.js +5 -4
  29. package/dist/boot/stage-07-capability-layer.d.ts +2 -2
  30. package/dist/boot/stage-07-capability-layer.js +3 -3
  31. package/dist/boot/stage-08-reapers.d.ts +2 -2
  32. package/dist/boot/stage-09-leader.d.ts +3 -9
  33. package/dist/boot/stage-10-http-server.d.ts +3 -9
  34. package/dist/boot/stage-10-http-server.js +6 -7
  35. package/dist/boot/stores.d.ts +1 -1
  36. package/dist/boot/stores.js +5 -6
  37. package/dist/boot/web-admin.d.ts +0 -3
  38. package/dist/boot/web-admin.js +35 -35
  39. package/dist/brain.js +17 -22
  40. package/dist/budget.js +5 -4
  41. package/dist/caller-text-echo.d.ts +20 -0
  42. package/dist/caller-text-echo.js +24 -0
  43. package/dist/capabilities/brand-identity.d.ts +26 -0
  44. package/dist/capabilities/brand-identity.js +27 -0
  45. package/dist/capabilities/center-plugins.js +1 -1
  46. package/dist/capabilities/center-prompts.d.ts +32 -59
  47. package/dist/capabilities/center-prompts.js +18 -32
  48. package/dist/capabilities/engine-capabilities.js +2 -1
  49. package/dist/capabilities/hands-lane.d.ts +9 -6
  50. package/dist/capabilities/hands-lane.js +1 -1
  51. package/dist/capabilities/mcp-probe.d.ts +14 -1
  52. package/dist/capabilities/mcp-probe.js +22 -133
  53. package/dist/capabilities/scenarios.d.ts +34 -40
  54. package/dist/capabilities/scenarios.js +16 -33
  55. package/dist/capabilities/skills.d.ts +19 -4
  56. package/dist/capabilities/skills.js +30 -10
  57. package/dist/config-catalog.js +6 -6
  58. package/dist/config-lkg.d.ts +9 -9
  59. package/dist/config-lkg.js +20 -4
  60. package/dist/config-provider.d.ts +3 -1
  61. package/dist/config-provider.js +12 -4
  62. package/dist/config-types.d.ts +7 -9
  63. package/dist/config.js +17 -4
  64. package/dist/device-ws-protocol.d.ts +1 -1
  65. package/dist/device-ws-protocol.js +1 -0
  66. package/dist/env-name-allowlist-knobs.d.ts +12 -2
  67. package/dist/env-name-allowlist-knobs.js +5 -4
  68. package/dist/exec-bounds.d.ts +15 -3
  69. package/dist/exec-bounds.js +4 -0
  70. package/dist/execution-lane-caps.d.ts +9 -2
  71. package/dist/execution-lane-caps.js +11 -0
  72. package/dist/hooks/classic-stations.d.ts +86 -0
  73. package/dist/hooks/classic-stations.js +27 -0
  74. package/dist/hooks/hook-llm.d.ts +8 -5
  75. package/dist/hooks/hook-llm.js +78 -35
  76. package/dist/hooks/hook-runner.d.ts +232 -80
  77. package/dist/hooks/hook-runner.js +218 -312
  78. package/dist/http/admission.d.ts +8 -2
  79. package/dist/http/admission.js +101 -57
  80. package/dist/http/approval-sweeps.js +57 -16
  81. package/dist/http/checkpoint-ctx.d.ts +25 -8
  82. package/dist/http/checkpoint-ctx.js +11 -7
  83. package/dist/http/claimed-run.d.ts +13 -0
  84. package/dist/http/claimed-run.js +15 -0
  85. package/dist/http/device-placement.js +5 -10
  86. package/dist/http/dispatch.d.ts +0 -4
  87. package/dist/http/dispatch.js +5 -9
  88. package/dist/http/durable-run-leg.d.ts +2 -1
  89. package/dist/http/durable-run-leg.js +5 -3
  90. package/dist/http/health-self-report.d.ts +7 -5
  91. package/dist/http/health-self-report.js +4 -18
  92. package/dist/http/principal-gate.d.ts +23 -6
  93. package/dist/http/principal-gate.js +24 -5
  94. package/dist/http/refusal-body.d.ts +21 -0
  95. package/dist/http/refusal-body.js +4 -0
  96. package/dist/http/request-guards.d.ts +27 -0
  97. package/dist/http/request-guards.js +24 -3
  98. package/dist/http/resume-legs.d.ts +25 -0
  99. package/dist/http/resume-legs.js +43 -22
  100. package/dist/http/routes/capabilities.js +1 -1
  101. package/dist/http/routes/diagnostics.js +1 -1
  102. package/dist/http/routes/rules.js +2 -2
  103. package/dist/http/routes/runs.js +7 -7
  104. package/dist/http/routes/session-sync.js +4 -2
  105. package/dist/http/routes/sessions.js +1 -1
  106. package/dist/http/routes/shared-memory.js +2 -13
  107. package/dist/http/routes/side-query.js +2 -3
  108. package/dist/http/routes/tasks.js +19 -9
  109. package/dist/http/send.d.ts +9 -7
  110. package/dist/http/send.js +7 -2
  111. package/dist/http/server.d.ts +21 -11
  112. package/dist/http/server.js +28 -2
  113. package/dist/http/submit-ctx.d.ts +25 -0
  114. package/dist/http/submit-ctx.js +39 -0
  115. package/dist/http/wire-types.d.ts +1 -1
  116. package/dist/index.d.ts +1 -1
  117. package/dist/index.js +1 -1
  118. package/dist/lane-home-probe.d.ts +43 -0
  119. package/dist/lane-home-probe.js +35 -0
  120. package/dist/limit-refusal.d.ts +47 -0
  121. package/dist/limit-refusal.js +12 -0
  122. package/dist/main.js +1 -1
  123. package/dist/mcp-assertion-header.d.ts +1 -9
  124. package/dist/mcp-assertion-header.js +0 -6
  125. package/dist/mcp-tool-faces.d.ts +5 -0
  126. package/dist/mcp-tool-faces.js +5 -1
  127. package/dist/memory-scope.d.ts +1 -1
  128. package/dist/model-provider.d.ts +39 -12
  129. package/dist/model-provider.js +19 -2
  130. package/dist/model-route-endpoint.d.ts +1 -1
  131. package/dist/model-route-endpoint.js +5 -5
  132. package/dist/observability/cost-taxonomy.d.ts +7 -7
  133. package/dist/observability/fail-open.d.ts +42 -6
  134. package/dist/observability/fail-open.js +42 -6
  135. package/dist/observability/metrics.js +3 -1
  136. package/dist/observability/prompt-manifest.d.ts +47 -82
  137. package/dist/observability/prompt-manifest.js +119 -17
  138. package/dist/observability/run-terminal.js +14 -6
  139. package/dist/observability/secret-env-scrub.d.ts +13 -1
  140. package/dist/observability/secret-env-scrub.js +13 -1
  141. package/dist/observability/tool-trace.d.ts +16 -21
  142. package/dist/observability/tool-trace.js +51 -42
  143. package/dist/per-task-image.d.ts +7 -1
  144. package/dist/per-task-image.js +3 -3
  145. package/dist/plain-string-record.d.ts +25 -0
  146. package/dist/plain-string-record.js +32 -0
  147. package/dist/plugins/blob-backend.d.ts +68 -13
  148. package/dist/plugins/blob-backend.js +53 -10
  149. package/dist/plugins/checkpoint-store-sql.d.ts +14 -4
  150. package/dist/plugins/checkpoint-store-sql.js +39 -26
  151. package/dist/plugins/k8s-bg-scripts.d.ts +11 -0
  152. package/dist/plugins/k8s-bg-scripts.js +22 -0
  153. package/dist/plugins/local-checkpoint-store.js +5 -2
  154. package/dist/plugins/local-session-store.js +1 -1
  155. package/dist/plugins/one-time-migrations.d.ts +137 -90
  156. package/dist/plugins/one-time-migrations.js +356 -207
  157. package/dist/plugins/permission-rule-store-sql.d.ts +13 -7
  158. package/dist/plugins/permission-rule-store-sql.js +4 -18
  159. package/dist/plugins/pg-session-storage.d.ts +4 -5
  160. package/dist/plugins/pg-session-storage.js +7 -17
  161. package/dist/plugins/remote-env-adb.d.ts +6 -0
  162. package/dist/plugins/remote-env-adb.js +9 -4
  163. package/dist/plugins/remote-env-device.js +3 -0
  164. package/dist/plugins/remote-env-e2b.d.ts +1 -1
  165. package/dist/plugins/remote-env-e2b.js +21 -9
  166. package/dist/plugins/remote-env-host.d.ts +9 -9
  167. package/dist/plugins/remote-env-host.js +10 -32
  168. package/dist/plugins/remote-env-k8s.d.ts +95 -26
  169. package/dist/plugins/remote-env-k8s.js +265 -90
  170. package/dist/plugins/remote-env-local-docker.d.ts +20 -1
  171. package/dist/plugins/remote-env-local-docker.js +77 -9
  172. package/dist/plugins/remote-env-ssh.d.ts +15 -0
  173. package/dist/plugins/remote-env-ssh.js +109 -3
  174. package/dist/plugins/remote-shell.d.ts +0 -1
  175. package/dist/plugins/remote-shell.js +1 -2
  176. package/dist/plugins/retention-store-sql.js +6 -2
  177. package/dist/plugins/schema-consistency.d.ts +54 -0
  178. package/dist/plugins/schema-consistency.js +52 -0
  179. package/dist/plugins/session-epoch-anchor.d.ts +13 -0
  180. package/dist/plugins/session-epoch-anchor.js +23 -0
  181. package/dist/plugins/shared-memory-store-sql.d.ts +9 -3
  182. package/dist/plugins/shared-memory-store-sql.js +5 -16
  183. package/dist/plugins/sql-driver.d.ts +141 -0
  184. package/dist/plugins/sql-driver.js +115 -1
  185. package/dist/plugins/sql-errors.d.ts +41 -0
  186. package/dist/plugins/sql-errors.js +36 -0
  187. package/dist/plugins/store-backend.js +2 -1
  188. package/dist/plugins/tidb-session-storage.d.ts +7 -11
  189. package/dist/plugins/tidb-session-storage.js +6 -16
  190. package/dist/plugins/tidb-session-store.js +1 -1
  191. package/dist/prompts-domain-validate.d.ts +17 -61
  192. package/dist/prompts-domain-validate.js +9 -119
  193. package/dist/request-key-closure.d.ts +2 -2
  194. package/dist/request-key-closure.js +2 -4
  195. package/dist/rule-entry-list.d.ts +12 -9
  196. package/dist/rule-entry-list.js +37 -21
  197. package/dist/run-local.js +19 -19
  198. package/dist/runs.d.ts +6 -0
  199. package/dist/runs.js +16 -0
  200. package/dist/server-secret-env.d.ts +44 -18
  201. package/dist/server-secret-env.js +49 -8
  202. package/dist/session-incarnation.d.ts +15 -5
  203. package/dist/session-incarnation.js +16 -9
  204. package/dist/session-titler.js +12 -2
  205. package/dist/spec-fields.d.ts +28 -16
  206. package/dist/spec-fields.js +46 -53
  207. package/dist/sql-engine-posture.d.ts +29 -4
  208. package/dist/sql-engine-posture.js +36 -1
  209. package/dist/stored-request-tool-axes-m19.d.ts +19 -4
  210. package/dist/stored-request-tool-axes-m19.js +30 -2
  211. package/dist/task-a2a.d.ts +9 -1
  212. package/dist/task-a2a.js +73 -47
  213. package/dist/task-mcp.d.ts +46 -58
  214. package/dist/task-mcp.js +99 -65
  215. package/dist/task-settings.d.ts +7 -21
  216. package/dist/task-settings.js +7 -36
  217. package/dist/text-bidi.d.ts +3 -46
  218. package/dist/text-bidi.js +3 -3
  219. package/dist/tool-approval.d.ts +1 -1
  220. package/dist/trace/core-keyset-guard.d.ts +24 -4
  221. package/dist/trace/engine-notice-wire.d.ts +11 -3
  222. package/dist/trace/engine-notice-wire.js +8 -1
  223. package/dist/trace/ledger-events.d.ts +3 -1
  224. package/dist/trace/ledger-sink.js +4 -1
  225. package/dist/trace/model-segment.js +1 -1
  226. package/dist/trace/project.d.ts +19 -6
  227. package/dist/trace/project.js +57 -22
  228. package/dist/trace/wire-projection-faces.d.ts +79 -1
  229. package/dist/trace/wire-projection-faces.js +22 -0
  230. package/dist/web-admin-client/candidate-gate.d.ts +6 -6
  231. package/dist/web-admin-client/candidate-gate.js +3 -3
  232. package/dist/web-admin-client/http-client.js +0 -6
  233. package/dist/web-admin-client/read-warnings.d.ts +13 -0
  234. package/dist/web-admin-client/read-warnings.js +10 -0
  235. package/dist/web-admin-client/skills-mcp.d.ts +3 -0
  236. package/dist/web-admin-client/skills-mcp.js +9 -6
  237. package/dist/web-admin-client/types.d.ts +2 -3
  238. package/dist/workspace-cache.d.ts +35 -0
  239. package/dist/workspace-cache.js +36 -0
  240. package/dist/workspace-space.d.ts +3 -2
  241. package/dist/workspace-space.js +2 -0
  242. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +8 -1
  243. package/package.json +4 -4
  244. package/scripts/drain-check-local.mjs +34 -1
  245. package/scripts/drain-check.mysql.sql +30 -2
  246. package/scripts/drain-check.pg.sql +29 -2
  247. package/dist/capabilities/memory-notice.d.ts +0 -138
  248. package/dist/capabilities/memory-notice.js +0 -64
  249. package/dist/capabilities/prompt.d.ts +0 -17
  250. package/dist/capabilities/prompt.js +0 -6
  251. package/dist/mcp-content-origin.d.ts +0 -34
  252. package/dist/mcp-content-origin.js +0 -10
  253. package/dist/tool-axis-words.d.ts +0 -27
  254. package/dist/tool-axis-words.js +0 -9
  255. package/dist/wire-name-bound.d.ts +0 -14
  256. package/dist/wire-name-bound.js +0 -13
@@ -46,7 +46,7 @@ export const FAIL_OPEN_TAGS = {
46
46
  },
47
47
  "server.trace.projection-input-malformed": {
48
48
  cls: "F",
49
- note: "7.78.0(core 7.18.0 #741/#786/#789/#790,`trace/project.ts` 的五张新投影脸共用一条规则;之后逐版入族 —— NP-5 / 7.100.0 起含 `trace/task-notification-facets.ts` 的 S-616 委派终态 facet / 重供两键与 `compacted.summarizedCrossSessionMessages`,助手住叶模块 `trace/projection-drop.ts`,全表见普查表第 73 行):`tool_progress` / `tool_disclosure` 两帧的必需位读不出 ⇒ **整帧不发**;`wiring_manifest.hooks[]` / `wiring_manifest.lsp` / `context_usage.sections[]` 三段的容器形或成员形读不出 ⇒ 那一行/那一段被丢、全丢光即键缺席。规则一句话:**在场但读不出 ⇒ 计一次并丢弃;缺席从不计数**(缺席是这几张脸上的一等状态,契约把它定义成一个正面事实 —— `hooks` 缺席 = 「本条腿没有自己的钩子记录」、`sections` 缺席 = 「这条腿没装配出段 IR」、`lsp` 缺席 = 「本部署没接 LSP」)。放行的最坏后果=这条腿的消费端少一张观测/渲染面(壳退回上一份已知状态或通用卡片),**不影响任何裁决**——门在引擎里早已判完。反方向(半张脸照发)严格更坏:消费端会把读不出的行当成那个正面事实。计数是**唯一**能把「坏世代 / 第三方 producer 一直在发坏载荷」与那个正面事实分开的位。",
49
+ note: "7.78.0(core 7.18.0 #741/#786/#789/#790,`trace/project.ts` 的五张新投影脸共用一条规则;之后逐版入族 —— NP-5 / 7.100.0 起含 `trace/task-notification-facets.ts` 的 S-616 委派终态 facet / 重供两键与 `compacted.summarizedCrossSessionMessages`,助手住叶模块 `trace/projection-drop.ts`,全表见普查表第 73 行):`tool_progress` / `tool_disclosure` 两帧的必需位读不出 ⇒ **整帧不发**;`wiring_manifest.mods[]`(7.132.0 前为 `hooks[]`)/ `wiring_manifest.lsp` / `context_usage.sections[]` 三段的容器形或成员形读不出 ⇒ 那一行/那一段被丢、全丢光即键缺席;7.132.0 起含 `observability/prompt-manifest.ts` 的耐久 `prompt_assembled` 行(引擎清单 v3):`identity` / `selectors` / `toolDisclosure` / `totalChars` 读不出 ⇒ 该成员丢,`blocks` / `sections` / `superseded` / `tools` 一行一判、全丢光即键缺席。规则一句话:**在场但读不出 ⇒ 计一次并丢弃;缺席从不计数**(缺席是这几张脸上的一等状态,契约把它定义成一个正面事实 —— `mods` 缺席 = 「这条腿没有注册」、`sections` 缺席 = 「这条腿没装配出段 IR」、`lsp` 缺席 = 「本部署没接 LSP」)。放行的最坏后果=这条腿的消费端少一张观测/渲染面(壳退回上一份已知状态或通用卡片),**不影响任何裁决**——门在引擎里早已判完。反方向(半张脸照发)严格更坏:消费端会把读不出的行当成那个正面事实。计数是**唯一**能把「坏世代 / 第三方 producer 一直在发坏载荷」与那个正面事实分开的位。",
50
50
  },
51
51
  "server.task-mcp.source-label-dropped": {
52
52
  cls: "F",
@@ -98,12 +98,36 @@ export const FAIL_OPEN_TAGS = {
98
98
  },
99
99
  "server.session-titler.gateway-unreachable": {
100
100
  cls: "F",
101
- note: "S-178(cli [6662] ① clay 裁定转达,`session-titler.ts logFailure`):自动会话标题那一次廉价模型调用失败(gateway 不可达 / 凭据缺失 / 超时 / 非 2xx 应答;端点回 3xx 重定向被拒的那一族另记 `server.session-titler.redirect-refused`,不进本 tag)。标题是**纯装饰**——列表面回退 objectivePreview,零行为影响,门/裁决/账本一个字节不受它管。此前每个新会话首次同 cause 打一条 `warn`,于是一台没配模型 gateway 的单机部署在日志里被这条噪音刷屏,壳因此去关 `SESSION_AUTO_TITLE`(替上游压噪),而那是把一个部署级旋钮当消音器用。现在:逐次 `debug` + 本计数 + 一次性 warn(recorder 自己的),运维仍看得见「这台机器的 titler 一直在失败」,用户面零噪音。观测面另有 `session_title_total{outcome}` 逐次记账。",
101
+ note: "S-178(cli [6662] ① clay 裁定转达,`session-titler.ts logFailure`):自动会话标题那一次廉价模型调用失败 —— **只数真不可达**:连不上 / 网络层失败 / 超时(失败码 `unreachable`,没有应答)。其它成因族各有自己的 tag,不再进本 tag:端点回 3xx 被拒 `server.session-titler.redirect-refused`;端点答了但不是可用 completion `server.session-titler.bad-response`;空应答 `server.session-titler.no-content`;选不出模型 `server.session-titler.model-unresolved`;路由核验拒 `server.session-titler.route-refused`;凭据托管失败 `server.session-titler.credential-custody-failed`;失败码缺席 `server.session-titler.unclassified`。标题是**纯装饰**——列表面回退 objectivePreview,零行为影响,门/裁决/账本一个字节不受它管。此前每个新会话首次同 cause 打一条 `warn`,于是一台没配模型 gateway 的单机部署在日志里被这条噪音刷屏,壳因此去关 `SESSION_AUTO_TITLE`(替上游压噪),而那是把一个部署级旋钮当消音器用。现在:逐次 `debug` + 本计数 + 一次性 warn(recorder 自己的),运维仍看得见「这台机器的 titler 一直在失败」,用户面零噪音。观测面另有 `session_title_total{outcome}` 逐次记账。",
102
102
  },
103
103
  "server.session-titler.redirect-refused": {
104
104
  cls: "F",
105
105
  note: "`session-titler.ts logFailure`:自动会话标题那一次廉价模型调用被拒 —— 配置的模型端点回了 3xx 重定向。本服务发往配置端点的直连请求不跟随重定向(跟随会把请求体里的用户内容与凭据头送到 `Location` 指向的另一个 origin),所以这是**端点配置问题,不是网络问题**:出路 = 把该模型的网关 URL(`MODEL_GATEWAY_BASEURL` / 目录条目自己的 baseUrl / `ANTHROPIC_BASE_URL`)改成最终地址,查网络没有用。与 `server.session-titler.gateway-unreachable` 的分界 = 失败码(llm 面交回的 `redirect_refused`),不看文案。标题是**纯装饰**——列表面回退 objectivePreview,零行为影响,门/裁决/账本一个字节不受它管。放行的最坏后果 = 该部署的会话没有自动标题。逐次 `debug` + 本计数 + 一次性 warn(recorder 自己的);`session_title_total{outcome=llm_failed}` 照常逐次记账。",
106
106
  },
107
+ "server.session-titler.model-unresolved": {
108
+ cls: "F",
109
+ note: "`session-titler.ts logFailure`(失败码 `model_unresolved`,铸点 `hooks/hook-llm.ts` 的选模闸 `hookModelFor`):自动会话标题那一次廉价模型调用**选不出模型** —— 目录里没有这只模型 / 条目已发布但本部署不可用 / 网关腿解析不出端点(没有网关的部署上条目也不自带 baseUrl)。**配置问题,不是网络问题**:出路 = 改模型目录或网关配置(summarize 角色指向的模型名、`MODEL_CHEAP_ID`、`MODEL_GATEWAY_BASEURL`),查网络没有用;零请求出门。此前与「连不上」共用 `gateway-unreachable`。标题是**纯装饰**——列表面回退 objectivePreview,零行为影响,门/裁决/账本一个字节不受它管。放行的最坏后果 = 该部署的会话没有自动标题。逐次 `debug` + 本计数 + 一次性 warn(recorder 自己的);`session_title_total{outcome=llm_failed}` 照常逐次记账。",
110
+ },
111
+ "server.session-titler.route-refused": {
112
+ cls: "F",
113
+ note: "`session-titler.ts logFailure`(失败码 `route_refused`,铸点 `hooks/hook-llm.ts` 的 `judgeRoute` 与组装期的 URL 铸造):自动会话标题那一次廉价模型调用被路由核验拒发 —— key↔URL 不配对(core 配对法的 `route.credential_*`)/ foreign 端点无 per-model key(标题 prompt 内嵌用户内容,未认证地发往未声明的端点等于把数据交给该 URL 的运营方)/ 请求 URL 铸不出来(如根带 `#fragment`)。**配置问题,零请求出门**:出路 = 改路由或凭据配置(模型条目的 baseUrl 与它的 key),查网络没有用。此前与「连不上」共用 `gateway-unreachable`。标题是**纯装饰**——列表面回退 objectivePreview,零行为影响,门/裁决/账本一个字节不受它管。放行的最坏后果 = 该部署的会话没有自动标题。逐次 `debug` + 本计数 + 一次性 warn(recorder 自己的);`session_title_total{outcome=llm_failed}` 照常逐次记账。",
114
+ },
115
+ "server.session-titler.credential-custody-failed": {
116
+ cls: "F",
117
+ note: "`session-titler.ts logFailure`(失败码 `credential_custody_failed`,铸点 `hooks/hook-llm.ts` 的 per-model key 读取):自动会话标题那一次廉价模型调用的 per-model key 托管读不出(密封 key 中毒 / 解密失败)。**fail-closed 方向不变**:不回落共享路由凭据、零请求出门(回落会把共享凭据发给不该拿到它的端点)。出路 = 查密钥托管(该模型的 sealed key / `apiKeyEnv`),查网络没有用。此前与「连不上」共用 `gateway-unreachable`。标题是**纯装饰**——列表面回退 objectivePreview,零行为影响,门/裁决/账本一个字节不受它管。放行的最坏后果 = 该部署的会话没有自动标题。逐次 `debug` + 本计数 + 一次性 warn(recorder 自己的);`session_title_total{outcome=llm_failed}` 照常逐次记账。",
118
+ },
119
+ "server.session-titler.bad-response": {
120
+ cls: "F",
121
+ note: "`session-titler.ts logFailure`(失败码 `bad_response`,铸点 `hooks/hook-llm.ts` 的应答读取):自动会话标题那一次廉价模型调用打到了端点、端点**答了**,但答的不是可用的 completion —— 非 2xx 状态(401 / 403 凭据被端点拒 · 429 · 5xx 网关故障),或 2xx 而 body 不是 JSON(中间的代理塞了一页 HTML)。**不是不可达**:出路 = 看端点答了什么(网关日志 / 凭据是否被它认),不是查网络。此前与「连不上」共用 `gateway-unreachable`。标题是**纯装饰**——列表面回退 objectivePreview,零行为影响,门/裁决/账本一个字节不受它管。放行的最坏后果 = 该部署的会话没有自动标题。逐次 `debug` + 本计数 + 一次性 warn(recorder 自己的);`session_title_total{outcome=llm_failed}` 照常逐次记账。",
122
+ },
123
+ "server.session-titler.no-content": {
124
+ cls: "F",
125
+ note: "`session-titler.ts logFailure`(失败码 `no_content`,铸点 `hooks/hook-llm.ts`):自动会话标题那一次廉价模型调用得到合法 JSON 应答、但里面**没有文本**(例:推理档模型的思考占满了输出额度 / 网关侧内容过滤清空了 choices)。出路 = 看该模型对这类短输出的行为(换 summarize 角色的模型),不是查网络。此前与「连不上」共用 `gateway-unreachable`。标题是**纯装饰**——列表面回退 objectivePreview,零行为影响,门/裁决/账本一个字节不受它管。放行的最坏后果 = 该部署的会话没有自动标题。逐次 `debug` + 本计数 + 一次性 warn(recorder 自己的);`session_title_total{outcome=llm_failed}` 照常逐次记账。",
126
+ },
127
+ "server.session-titler.unclassified": {
128
+ cls: "F",
129
+ note: "`session-titler.ts logFailure` 的 `switch` 在**失败码缺席**时的明说臂:自动会话标题那一次调用失败了,但载体没有给码(失败臂漏铸码 / 异常路径:store 写失败、载体违约抛出)。它**不冒充任何一族**:此前这一臂记成 `gateway-unreachable`,运维去查网络。计数非零 = 有失败臂没带码,去补铸点(`hooks/hook-llm.ts`)或看 `session_title_failed` 的 debug 行,不是去查网络。标题是**纯装饰**——列表面回退 objectivePreview,零行为影响,门/裁决/账本一个字节不受它管。放行的最坏后果 = 该部署的会话没有自动标题。逐次 `debug` + 本计数 + 一次性 warn(recorder 自己的);`session_title_total{outcome=llm_failed}` 照常逐次记账。",
130
+ },
107
131
  "server.prompts.artifact-cache-write-failed": {
108
132
  cls: "F",
109
133
  note: "已验签的提示词工件回填本地缓存失败。工件本身已返回给调用方;代价是下次同 digest 还要再取一次网络。",
@@ -136,6 +160,10 @@ export const FAIL_OPEN_TAGS = {
136
160
  cls: "F",
137
161
  note: "S-700(`plugins/checkpoint-store-sql.ts` 的 `readParkRowFactsCell`):运维待批队列(`GET /v1/approvals` / `/stream`)的某一行,`checkpoint` 列里寄存行的 **`pendingAction` 子树**(NP-3 / 7.100.0 起引擎内只取这一棵、整只快照不出库;只为 park 行事实 `requiresRealApproval` / `ruleOffersAbsence` / `mandated`(S-663 二段,core #1079)/ `mandate`(S-721)/ `origin`(S-755)而读 —— 事实位的真源是 `approval-card.ts` 的 `ParkRowFacts`)**在场却不是一只对象**(JSON `null` 字面 / 串 / 数 —— 手改 / 迁移脚本改过)。子树**缺席**(SQL NULL:成员不在,或整只文档不是对象 —— 后者 `get()` 照旧拒;MariaDB 文本承载上的非 JSON 字节同样答 SQL NULL)⇒ 事实缺席、不计数。放行的最坏后果 = **这一行**的 park 行事实(`ParkRowFacts` 各位)缺席(展示/分诊材料;core 定性 display metadata,resume 的带子读 gate 自己的位),队列其余部分照常 —— 而这一行本来就 resume 不了(`get()` 对同一格拒)。不放行的代价是整只 `rows.map` 抛出 ⇒ 一行坏 blob 打掉整个租户的队列(列表 500、SSE 心跳照常而队列永远空),与同一 `rows.map` 里 `rule_suggestions` / `risk_descriptor` 两格同族同判。故 F 类;tag 与那两格**分开计**(哪一格在坏要读得出来)。计数非零 = 这一行的 decide 同样读不动(`get()` 对同一格拒),去按 session 查那一行。 **第二座 `member=gate`**(`readParkGateCell`:同一列的 **`gate` 子树**在场却不是一只对象 —— 队列行的卡词与到期两条腿的分法都读它):读不出 = 门上无卡,队列行不带卡词,**到期改道** —— core `facetOf` 读「无卡 = 不是一问」为 `A`,所以一张本该到期**作废**的问答行改走 SLA **拒扫腿**(模型拿到一次「拒」而不是整条 run 作废);改道后的续跑 `get()` 对同一份坏 blob 响亮拒(拒扫腿每拍一条失败),行留停驻、由 `terminal_at_ms` 兜底收割 —— 所以最坏后果**不是**「只少展示」,是一张问答行的到期去向变了、最长挂到绝对上限。放行仍对:不放行的代价与 pendingAction 座同(一行坏 blob 打掉整个租户的队列与到期读腿)。计数 detail = `member=gate` 时,去按 session 查那一行的 `checkpoint.gate`。",
138
162
  },
163
+ "server.approvals.queue-row-gate-kind-missing": {
164
+ cls: "F",
165
+ note: "S-1681(`plugins/checkpoint-store-sql.ts` 的 `queueRowGateKind`,7.132.0;SQL 与 local 两只孪生的 `listPending` 同调这一只判官):运维待批队列(`GET /v1/approvals` / `/stream`)读到一行 pending 的门型(SQL 孪生:`checkpoint.gate_kind` 列;local 孪生:快照上的门)是 NULL 或不是 core 登记的门型 ⇒ 这一行**不列**。按构造不可达:每次停驻都把门型反范式进这一列,一次性迁移 M21 把没有它的存量行从门子树补齐(读不出登记门型的置 expired)。放行(不列、队列其余照常)的最坏后果 = 一行谁也决不了的行不出现在队列上 —— 它本来就不可决:每一条决断口按门型绑定取行(`gate_kind IN (…)`),证不出门型就不当它是这道门;不放行(整只拒)的代价是一行坏行打掉整个租户的队列(列表 500、SSE 心跳照常而队列永远停在旧帧)。也不按缺键投出去:消费方按 `gateKind` 路由、缺键即 fail-closed,队列行的契约是 `gateKind` 恒在场。故 F 类;计数非零 = 写腿或手改出了错,`SELECT session_id FROM checkpoint WHERE status = 'pending' AND gate_kind IS NULL` 查那一行(置 expired 或删会话)。",
166
+ },
139
167
  "server.sql-json.checkpoint.pending-steer-queue-lossy": {
140
168
  cls: "F",
141
169
  note: "S-550(`plugins/checkpoint-store-sql.ts` 的 `get()`,core 5.14.0 #147 的耐久转向队列):一条挂起 run 的 `checkpoint.pending_steer_queue` 这一格**没能完整读出来**(不是合法 JSON 文本 / 不是数组 / 某一条目的四个必备键不合形)⇒ 读不出的那部分**丢掉**,run 照常 resume。放行的最坏后果 = 一条已经 park 的操作员转向指令**没有**在这次 resume 上注入(人以为说过的话没送到);不放行的代价是这条挂起的 run **永远不可 resume** —— resume 是它唯一的出路,为一格读不出的转向把整条 run 变成砖是错误的失败方向(写侧刻意相反:`setPendingSteer` 往一份读不全的队列上追加会把读不出的条目永久抹掉,那里 fail-loud)。故 F 类。留痕从一条 `logger.info` 升成登记 tag 的理由:日志行在**遥测轴**上是零信号,「这台机器的转向队列一直读不出来」与「这台机器本来就没人 steer」在指标上完全同形。计数非零 = 去按日志里的 token 指纹查那一行。",
@@ -222,7 +250,7 @@ export const FAIL_OPEN_TAGS = {
222
250
  },
223
251
  "server.hooks.matcher-unjudged": {
224
252
  cls: "F",
225
- note: "S-995(`hooks/hook-runner.ts` 的 `toolMatcherIssue`,fresh 受理面「工具事件 matcher 选中别名而选不中正名 ⇒ 400」判据的**未判**臂;detail = `event=… reason=… matcherChars=…`,不带 matcher 原字节):一只 matcher 没能被判 ⇒ 照旧受理。判据一条顺序、两档共用(7.114.0 修复轮):长度上限 → 形只问契约包的 `hookMatcherForm`(literal 形〔名字表 / 空 / `*`〕两档都直接问 `hookMatcherSelects`、不占预算;regex 形 regex 档把同一只契约函数交进 vm 带每次求值各一份的固定预算〔超时 = 灾难回溯 ⇒ 确定的 400 拒,不是未判、不登记〕、literal 档不适用;invalid 形 regex 档 = 确定的 400 拒〔S-1455 收尾:不是未判、不登记;运行期另记 `hook_matcher_invalid`〕、literal 档不适用)。现在只有一种 reason —— `oversize`(两档:超 8 192 字符不读;多租户车道上 hooks 不装配,这一笔只为让超长输入显形)。`invalid_regex` 臂自 S-1455 收尾(7.126.0)起退役:它是确定性的坏配置、不是「判不了」,与超预算同类 ⇒ 受理当场 400。**不记**的:literal 档(多租户车道)遇正则形 = 不适用 —— 该车道 hooks 整只不装配,没有被守的对象,不是未判。放行的最坏后果 = **那一只 matcher 没被这条判据守住**:它若真写了别名,照旧永不命中(7.112.0 对正则形一律不判,同一后果);**不是安全闸**(受理面的有效性判 = S-924 的「响亮」,决定不了任何放行 / 拒绝 / 权限;hook 的 deny 语义在运行期 hook-runner)。拒放集合相对 7.113.0 的一处放宽:超过上限的字面别名 matcher 7.113.0 拒、7.114.0 两档未判受理(已披露)。计数让「受理面一直判不了」(超长 matcher)显形。",
253
+ note: "S-995(`hooks/hook-runner.ts` 的 `hooksMatcherIssue`,fresh 受理面「工具事件 matcher 选中别名而选不中正名 ⇒ 400」判据的**未判**臂;detail = `event=… reason=… matcherChars=…`,不带 matcher 原字节):一只 matcher 没能被判 ⇒ 照旧受理。判据一条顺序、两档共用(7.114.0 修复轮):长度上限 → 形只问契约包的 `hookMatcherForm`(literal 形〔名字表 / 空 / `*`〕两档都直接问 `hookMatcherSelects`、不占预算;regex 形 regex 档把同一只契约函数交进 vm 带每次求值各一份的固定预算〔超时 = 灾难回溯 ⇒ 确定的 400 拒,不是未判、不登记;一次受理里所有已完成求值的耗时之和超过受理总额度同样是确定的 400 拒〕、literal 档不适用;invalid 形 regex 档 = 确定的 400 拒〔S-1455 收尾:不是未判、不登记;运行期另记 `hook_matcher_invalid`;形判对所有契约事件同一判,不止工具事件〕、literal 档不适用)。现在只有一种 reason —— `oversize`(两档:超 8 192 字符不读;多租户车道上 hooks 不装配,这一笔只为让超长输入显形)。`invalid_regex` 臂自 S-1455 收尾(7.126.0)起退役:它是确定性的坏配置、不是「判不了」,与超预算同类 ⇒ 受理当场 400。**不记**的:literal 档(多租户车道)遇正则形 = 不适用 —— 该车道 hooks 整只不装配,没有被守的对象,不是未判。放行的最坏后果 = **那一只 matcher 没被这条判据守住**:它若真写了别名,照旧永不命中(7.112.0 对正则形一律不判,同一后果);**不是安全闸**(受理面的有效性判 = S-924 的「响亮」,决定不了任何放行 / 拒绝 / 权限;hook 的 deny 语义在运行期 hook-runner)。拒放集合相对 7.113.0 的一处放宽:超过上限的字面别名 matcher 7.113.0 拒、7.114.0 两档未判受理(已披露)。计数让「受理面一直判不了」(超长 matcher)显形。",
226
254
  },
227
255
  "server.hooks.resume-reader-refused": {
228
256
  cls: "P-DEBT",
@@ -405,15 +433,15 @@ export const FAIL_OPEN_TAGS = {
405
433
  },
406
434
  "server.workspace-cache.unavailable": {
407
435
  cls: "F",
408
- note: "k8s 车道按工作区空间的构建缓存卷(`WORKSPACE_CACHE=volume`,`plugins/remote-env-k8s.ts` 连沙箱那一步):这条 run 拿不到它空间的缓存卷 —— 卷建不成(RBAC 少动词 / API 拒 / 网络错,`reason=claim_failed`)、卷在快照时限内没绑定(集群没有缺省 StorageClass / 供给器不在,`reason=claim_unbound`)、卷绑上了而挂着它的 pod 起不来(挂载失败 / 卷所在节点没了,`reason=claim_unmountable`)—— 后两种把等它的那只 pod 删掉,换一只不挂卷的 —— 、或本副本读不出会话住客没有空间键(`reason=incarnation_unknown`)⇒ run **照常起**:不挂卷、不注入工具缓存 env,工具按镜像自己的缓存目录工作(每轮重下依赖)。放行的最坏后果 = 这一轮构建慢(缓存定义上可再生),不是门、不是凭据、不碰工作区与快照;反方向(卷拿不到就拒这一轮)会让一个缓存把整条 run 打掉。同拍一行运维日志 `workspace_cache_unavailable`(`space` 前缀 / `reason` / `error`)。计数非零 = 这台部署开了缓存卷却拿不到,运维去看 StorageClass / Role / 配额。",
436
+ note: "k8s 车道按工作区空间的构建缓存卷(`WORKSPACE_CACHE=volume`,`plugins/remote-env-k8s.ts` 连沙箱那一步):这条 run 拿不到它空间的缓存卷 —— 卷建不成(RBAC 少动词 / API 拒 / 网络错,`reason=claim_failed`)、卷在快照时限内没绑定(集群没有缺省 StorageClass / 供给器不在,`reason=claim_unbound`)、卷绑上了而挂着它的 pod 起不来(挂载失败 / 卷所在节点没了,`reason=claim_unmountable`)—— 后两种把等它的那只 pod 删掉,换一只不挂卷的 —— 、或本副本读不出会话住客没有空间键(`reason=incarnation_unknown`)⇒ run **照常起**:不挂卷、不注入工具缓存 env,工具按镜像自己的缓存目录工作(每轮重下依赖)。放行的最坏后果 = 这一轮构建慢(缓存定义上可再生),不是门、不是凭据、不碰工作区与快照;反方向(卷拿不到就拒这一轮)会让一个缓存把整条 run 打掉。同拍一行运维日志 `workspace_cache_unavailable`(`space` 前缀 / `reason` / `error`)。`claim_unbound` / `claim_unmountable` 按空间记一个快照时限(部署的 per-run 绑定持有的进程内记忆):窗内这个空间的后续 run 直接走这条臂(不发 claim 请求、不起等卷的 pod),每条 run 仍计一次、出一行,`error` 点名「记着的」。计数非零 = 这台部署开了缓存卷却拿不到,运维去看 StorageClass / Role / 配额。",
409
437
  },
410
438
  "server.workspace-cache.reclaim-failed": {
411
439
  cls: "F",
412
- note: "k8s 车道按工作区空间的构建缓存卷的**回收**(`plugins/remote-env-k8s.ts` `createSpaceReclaimer`;两条腿同一只回收座:会话删除协调器 `boot/session-faces.ts` `purgeSession` 与托管留存 sweep `boot/retention-lane.ts` 删掉会话树之后):按标签 `sema.space=<空间摘要>` 删卷的请求失败(Role 少 `deletecollection` / API 拒 / 网络,`reason=delete_failed`)、回收座这次调用的绝对截止(= 快照时限)先到(请求没发或在途被切,`reason=budget`),或在线腿删除前读不出被删会话的住客(`reason=occupant_unreadable`;化身缺席的存量行 = 没有空间,不计)⇒ 卷留着(住客读不出时该空间的快照对象也没回收,归桶的生命周期规则),会话照删(不挡删除)。放行的最坏后果 = 节点盘上多留一只没人再用的卷(存储泄漏,不碰任何别的空间、不是门不是凭据);反方向(删卷失败就拒删会话)会让一个缓存挡住删除权。同拍一行 `workspace_cache_reclaim_failed`(回收座的两形:`space` 前缀 / `label` / `reason` / `error`;住客读不出那一形:`sessionId` / `reason` / `error`)。计数非零 = 有卷没回收掉,运维按行里的标签手清,并核 Role。",
440
+ note: "k8s 车道按工作区空间的构建缓存卷的**回收**(`plugins/remote-env-k8s.ts` `createSpaceReclaimer`;两条腿同一只回收座:会话删除协调器 `boot/session-faces.ts` `purgeSession` 与托管留存 sweep `boot/retention-lane.ts` 删掉会话树之后):按标签 `sema.space=<空间摘要>` 删卷的请求失败(Role 少 `deletecollection` / API 拒 / 网络,`reason=delete_failed`)、回收座这次调用的绝对截止(= 快照时限)先到(请求没发或在途被切,`reason=budget`),或在线腿删除前读不出被删会话的住客(`reason=occupant_unreadable`;化身缺席的存量行 = 没有空间,不计)⇒ 卷留着(住客读不出时该空间的快照对象也没回收,归桶的生命周期规则),会话照删(不挡删除)。放行的最坏后果 = 节点盘上多留一只没人再用的卷(存储泄漏,不碰任何别的空间、不是门不是凭据);反方向(删卷失败就拒删会话)会让一个缓存挡住删除权。同拍一行 `workspace_cache_reclaim_failed`(回收座的两形:`space` 前缀 / `label` / `reason` / `count` / `error` —— 一次调用里同 `reason` 的失败合成一行,`count` = 撞上它的空间数、其余键点名调用里排第一的那个,计数仍逐空间;住客读不出那一形:`sessionId` / `reason` / `error`)。计数非零 = 有卷没回收掉,运维按行里的标签手清,并核 Role。",
413
441
  },
414
442
  "server.workspace-space.reclaim-failed": {
415
443
  cls: "F",
416
- note: "k8s 车道工作区空间结束时对**快照对象**的回收(`plugins/remote-env-k8s.ts` `createSpaceReclaimer` 的对象部分;与上一条同一只回收座、两条腿:会话删除协调器与托管留存 sweep):按空间的 `<前缀>/<化身>/space/` 分区(快照 + `latest`)和该会话停驻车道里名字带被结束住客归属记号的 `<前缀>/<会话 id>/*.tar.gz` 分别 LIST、逐个预签 DELETE;LIST 失败、DELETE 失败(HTTP 非 2xx / 非 404、网络)LIST 超过 1000 个对象被截断,或回收座一次调用的绝对截止(= 快照时限;卷回收也在内)到点〔切断在途请求、停掉余下的;这次调用里还没轮到的空间零请求、各记一次;没删的归生命周期规则〕,各记一次(detail `lane=space|park stage=list|delete|budget`;会话删除口把回收座脱离应答路径,座若抛了——契约禁止——记 `stage=seat_threw`)⇒ 这些对象留在桶里,会话照删(不挡删除)。放行的最坏后果 = 一份已无人能读的工作区快照留在桶里(会话已删,经 API 不可达;内容是用户工作区,所以不是「只占盘」—— 留到桶的生命周期规则把它过期掉为止);不碰任何别的空间、不是门不是凭据;反方向(清不掉对象就拒删会话)会让一个对象存储抖动挡住用户的删除权。故 F 类,但必须留痕:静默吞掉之后「这个会话删了、它的工作区内容还在不在桶里」在遥测里没有任何痕迹。同拍一行 `workspace_space_reclaim_failed`(`space` 前缀 / `sessionId` / `lane` / `stage` / `error`)。计数非零 = 有对象没回收掉,运维按行里的空间前缀 / 会话 id 去桶里手清,并核对象存储的可达性与 DELETE 权限。回收与同一空间的尚在途的 run 末提交不是互斥的:会话删除时没有活 run(409 挡),但上一条 run 的提交若恰在删除之后才落对象,这批对象不在本次回收射程内(同样归桶的生命周期规则)。停驻分区按会话 id 分区,所以只删名字带被结束住客归属记号的对象(不读任何钟)—— 会话 id 删后被别人重新登记,新住客停驻出的快照带它自己的记号、不碰;不带记号的旧形与 `none` 留给桶的生命周期规则。",
444
+ note: "k8s 车道工作区空间结束时对**快照对象**的回收(`plugins/remote-env-k8s.ts` `createSpaceReclaimer` 的对象部分;与上一条同一只回收座、两条腿:会话删除协调器与托管留存 sweep):按空间的 `<前缀>/<化身>/space/` 分区(快照 + `latest`)和该会话停驻车道里名字带被结束住客归属记号的 `<前缀>/<会话 id>/*.tar.gz` 分别 LIST、逐个预签 DELETE;LIST 失败、DELETE 失败(HTTP 非 2xx / 非 404、网络)LIST 超过 1000 个对象被截断,或回收座一次调用的绝对截止(= 快照时限;卷回收也在内)到点〔切断在途请求、停掉余下的;这次调用里还没轮到的空间零请求、各记一次;没删的归生命周期规则〕,各记一次(detail `lane=space|park stage=list|delete|budget`;会话删除口把回收座脱离应答路径,座若抛了——契约禁止——记 `stage=seat_threw`)⇒ 这些对象留在桶里,会话照删(不挡删除)。放行的最坏后果 = 一份已无人能读的工作区快照留在桶里(会话已删,经 API 不可达;内容是用户工作区,所以不是「只占盘」—— 留到桶的生命周期规则把它过期掉为止);不碰任何别的空间、不是门不是凭据;反方向(清不掉对象就拒删会话)会让一个对象存储抖动挡住用户的删除权。故 F 类,但必须留痕:静默吞掉之后「这个会话删了、它的工作区内容还在不在桶里」在遥测里没有任何痕迹。同拍一行 `workspace_space_reclaim_failed`(`space` 前缀 / `sessionId` / `lane` / `stage` / `count` / `error`;一次调用里同类〔分区 × 阶段〕的失败合成一行,`count` = 撞上它的空间数,其余键点名调用里排第一的那个,计数仍逐空间;回收座同一时刻最多 4 个空间在收,sweep 与会话删除共用这一只门)。计数非零 = 有对象没回收掉,运维按行里的空间前缀 / 会话 id 去桶里手清,并核对象存储的可达性与 DELETE 权限。回收与同一空间的尚在途的 run 末提交不是互斥的:会话删除时没有活 run(409 挡),但上一条 run 的提交若恰在删除之后才落对象,这批对象不在本次回收射程内(同样归桶的生命周期规则)。停驻分区按会话 id 分区,所以只删名字带被结束住客归属记号的对象(不读任何钟)—— 会话 id 删后被别人重新登记,新住客停驻出的快照带它自己的记号、不碰;不带记号的旧形与 `none` 留给桶的生命周期规则。",
417
445
  },
418
446
  "server.sandbox-egress.policy-gap": {
419
447
  cls: "P-DEBT",
@@ -427,6 +455,14 @@ export const FAIL_OPEN_TAGS = {
427
455
  cls: "P-DEBT",
428
456
  note: "7.121.0(`fleet/serving-lease.ts` 的 `enterDenied`,中心以正面证据拒了本实例的服务租约那一拍;detail = `worker=<w> generation=<g>`):拒要落成数据根下一只文件(重启后起步即排空),而这一次**没落下** —— 没有数据根,或写失败(盘满 / 只读挂载 / 目录不在)。本进程照样被拒、照样当拍排空、到期自停;放行的是**重启之后**:同代际重启的实例不记得被拒,在第一次续租得到答复之前会接新工作(中心宣告了能力位时,第一次续租就再被拒一次 —— 中心的代际下限是持久的;中心那时不可达则一直接单到过期满 `FLEET_LEASE_OUTAGE_MAX_MS`)。归 P 类:这是围栏的记忆;归 DEBT:进程内无法把「记不住」变成 fail-closed(起步不接单直到第一次答复,是对租约不可达容忍规则的改动,另案)。denied 行的 `sticky` 同拍说 `process-only`,不声称跨重启记得。计数非零 = 这台机器的数据根写不进,或部署没有数据根。",
429
457
  },
458
+ "server.sql.tidb-row-cap-unread": {
459
+ cls: "F",
460
+ note: "TiDB 连接初始化读不出单行上限(`plugins/sql-driver.ts` `readTidbEntrySizeLimit`;detail = `assumed=<字节> <两次读各自的原因>`):会话变量 `@@tidb_txn_entry_size_limit` 为 0 / 不认识(TiDB 7.6 之前没有它),且 `SHOW CONFIG` 读 `performance.txn-entry-size-limit` 失败(要 `CONFIG` 权限)或读不到行 ⇒ 按 TiDB 自己的缺省 6291456 字节当本库的单行上限(`SqlEngineFacts.maxRowBytes`),提交口据它把装不进一行的请求体答 413。每条池连接初始化各记一次。放行的最坏后果:运维抬过上限而读不到 ⇒ 6 MiB 到真上限之间的体被多拒(413,出路 = 给账号 `CONFIG` 权限,或 TiDB 7.6+ 设 `tidb_txn_entry_size_limit`);运维调低过而读不到 ⇒ 门放进了装不下的体,写库那一刻被库拒:提交时写续跑输入那一笔(durable 审批部署)答带码的 413(店的 `StoreRowTooLargeError`),开跑后的写(会话日志的用户消息等)按各自的既有失败面。不是门、不是凭据、不改任何裁决;计数非零 = 去给库账号授权或设会话变量。",
461
+ },
462
+ "server.sql.packet-cap-unread": {
463
+ cls: "F",
464
+ note: "MySQL 协议连接初始化读不出语句级包墙(`plugins/sql-driver.ts` `readMaxAllowedPacket`,InnoDB 与 TiDB 同一处初始化;detail = `assumed=<字节> engine=<tidb|innodb> <读不出的原因>`):会话变量 `@@max_allowed_packet` 不认识 / 读它的查询失败 / 读到的不是正整数 ⇒ 按该服务器族的文档缺省当本库的包墙(`SqlEngineFacts.maxPacketBytes`;tidb 67108864,innodb 取 MariaDB 与 MySQL 8 两个缺省里小的 16777216),SQL 快照 blob 的缺省帽据它扣语句余量(1 KiB)再折半(文本协议 2× 膨胀)后与单行墙取小。每条池连接初始化各记一次。放行的最坏后果:库的真包墙比落点小(DBA 调小而没读到)⇒ 缺省帽放进了装不进一条语句的 blob,库在写的那一刻以 `ER_NET_PACKET_TOO_LARGE` 拒,快照 blob 写走各自既有的失败面(店的 never-throw 结果形折成 502);比落点大(调大而没读到)⇒ 缺省帽多拒(413 `blob_too_large_for_sql`,出路 = 显式旋钮 `SNAPSHOT_BLOB_SQL_MAX_BYTES` 或配对象存储)。不是门、不是凭据、不改任何裁决;计数非零 = 去看库账号能不能读 `@@max_allowed_packet`(普通账号都读得到,读不出多半是代理 / 中间件拦了这句)。",
465
+ },
430
466
  };
431
467
  export function failOpenTagForDroppedFrame(frameType) {
432
468
  switch (frameType) {
@@ -235,10 +235,12 @@ export function createMetrics() {
235
235
  m.counter("budget_exceeded_total", "Tasks failed by a budget/limit ceiling, by errorCode");
236
236
  m.counter("limit_ceiling_after_answer_total", "Runs where a walltime/turns ceiling fired AFTER the run had already answered (engine 7.13.0: these end completed with NO limits.* code). Dual of budget_exceeded_total, but NOT over the same population: this one is minted on the deps-level notice sink so it sees EVERY engine run (background children, delegated/nested legs, leader lanes); budget_exceeded_total is incremented only by the HTTP submit legs finalizeTaskResult, i.e. top-level HTTP-submitted runs. Sum them only within that narrower population; see the forwarder comment, by origin");
237
237
  m.counter("cost_quota_rejected_total", "Tasks refused because the principal is over its cumulative cost quota");
238
+ m.counter("deny_sweep_resume_blocked_total", "SLA deny-sweep resumes the resume leg refused (every attempt; the deny_sweep_resume_blocked warn is logged once per row and refusal)");
239
+ m.counter("submit_ctx_run_release_failed_total", "Submissions whose resume-input write failed AND whose just-claimed run could not be released, by step (terminal|idem_key); the session stays held until REAP_RUN_STALE_SEC (one submit_ctx_run_release_failed warn per failure)");
240
+ m.counter("deny_sweep_list_failed_total", "SLA deny-sweep ticks whose read of the expired approvals failed (that tick denies nothing; one deny_sweep_list_failed warn per failure)");
238
241
  m.counter("degraded_total", "Tasks degraded to a cheaper model (1.40), by reason (budget/rate_limit/breaker_open)");
239
242
  m.counter("task_model_not_allowlisted_resume_total", "Resumed legs whose model/compactionModel is outside the @-mention allowlist (allowed to keep running by design — the allowlist gates fresh picks only)");
240
243
  m.counter("task_model_unavailable_fallback_total", "Resumed legs whose explicit model pick names a catalog entry that is unavailable on this deployment (no brain mounted for its provider / entry fails its schema) — the leg runs on the default model; fresh submits get 400 request.model_unavailable");
241
- m.counter("prompt_constitution_total", "Prompt assembly constitution mode per task (core 1.243 提示词主权): steady state = core; provider-assembled/replaced/legacy warrant eyes");
242
244
  m.counter("rewind_snapshot_failed_total", "Per-turn rewind file snapshots that failed, by the engine's FileHistoryResult.error.code (snapshot_failed/aborted/…; `unknown` = the text carried no word of that closed set, e.g. the boundary-timeout arm). COARSE on purpose — whether a failure is permanent (the same file will fail every turn until the deployment changes) is counted by file_history_permanent_failure_total instead, from the typed error object rather than from prose");
243
245
  m.counter("verifications_total", "Tasks run through the verification gate (1.44), by final verdict");
244
246
  m.counter("cascade_total", "Tasks run through the model cascade (1.45), by outcome (passed/exhausted)");
@@ -1,85 +1,49 @@
1
- /** The whitelisted projection of core's `prompt.assembled` trace event ([ref]② web observability slice A:
2
- * the manifest reaches the durable run event log + turns/stream). Built FIELD-BY-FIELD at record time
3
- * (never a spread — the 1.72 task_progress lesson: a future core field must not ride the durable wire raw).
4
- * Carries ONLY ids/digests/counts by core's manifest contract (no prompt bodies; hashes are core-salted) —
5
- * so the projection legs have zero redact burden. */
6
- export interface PromptManifestRecord {
7
- constitution: string;
8
- blocks: Array<{
9
- id: string;
10
- chars: number;
11
- hash: string;
12
- }>;
13
- /** `contentHash`(core 1.315 [ref]①):operator 声明 section 的**非盐化**对账锚(core 已对 text 校验
14
- * 后透传)——盐化 `hash` 恒在旁;端到端对账链(center 发布→manifest)经本投影,必须透传不可吞。 */
15
- sections?: Array<{
16
- id: string;
17
- slot: string;
18
- carrier: string;
19
- cadence: string;
20
- cacheClass: string;
21
- chars: number;
22
- hash: string;
23
- contentHash?: string;
24
- }>;
25
- tools?: Array<{
26
- wireName: string;
27
- aliases: string[];
28
- contractId: string;
29
- implementationRevision: string;
30
- cardId: string;
31
- shapeDigest: string;
32
- wireSchemaDigest: string;
33
- }>;
34
- /** core 5.15.0([ref]):deferred 披露策略自证三键(诊断值,零内容)。缺席=core <5.15 或无 deferred。 */
35
- toolDisclosure?: {
36
- deferredTools: number;
37
- strategy: string;
38
- source: string;
39
- };
40
- totalChars: number;
41
- }
42
- /** Whitelist one `prompt.assembled` trace event into a {@link PromptManifestRecord} (v1 face required,
43
- * v2 `sections`/`tools` additive — absent stays absent). */
44
- export declare function promptManifestRecordOf(e: {
45
- constitution: string;
46
- blocks: Array<{
47
- id: string;
48
- chars: number;
49
- hash: string;
50
- }>;
51
- sections?: Array<{
52
- id: string;
53
- slot: string;
54
- carrier: string;
55
- cadence: string;
56
- cacheClass: string;
57
- chars: number;
58
- hash: string;
59
- contentHash?: string;
60
- }>;
61
- tools?: Array<{
62
- wireName: string;
63
- aliases: string[];
64
- contractId: string;
65
- implementationRevision: string;
66
- cardId: string;
67
- shapeDigest: string;
68
- wireSchemaDigest: string;
69
- }>;
70
- /** core 5.15.0([ref],[ref] 提货②):deferred 工具的披露策略自证——{deferredTools(数), strategy
71
- * ("static"|"swap"), source(四值:`spec` / `env` / `default` / `degraded_no_direct_lane`)}。
72
- * 诊断三键(数 + 两闭集词,零内容)。词表属主是 core(`core/trace.d.ts` 的 `source` 联合),本仓宽收
73
- * ⇒ 词错不会红,所以照抄时务必对表:`degraded_no_direct_lane` = 直呼车道联锁把 `static` 请求收窄成
74
- * `swap` 的那一格,正是运维最需要认出来的一格(本注上一版把它写成了不存在的 `api-default`,下游看板
75
- * 照抄就会把真实的 degraded 读成未知词)。 */
76
- toolDisclosure?: {
77
- deferredTools: number;
78
- strategy: string;
79
- source: string;
80
- };
81
- totalChars: number;
82
- }): PromptManifestRecord;
1
+ import type { TraceEvent } from "@sema-agent/core";
2
+ /**
3
+ * 引擎的清单帧 `prompt.assembled`(引擎 7.52.0 起 `version: 3`):这条腿的提示**是哪一份**(`identity` + 它的选择器值 `selectors`)、
4
+ * 由哪些块与段组成、每一段的文本从哪里来(`chain`,被取代的 overlay 另在 `superseded`)。只有 id / 尺寸 / 摘要,没有提示正文。
5
+ */
6
+ export type PromptAssembledEvent = Extract<TraceEvent, {
7
+ kind: "prompt.assembled";
8
+ }>;
9
+ /** 帧上**不上行**的键。唯一一簿在 `trace/wire-projection-faces.ts` 的 `prompt_manifest` 行(逐键理由在那里);这里只做类型查询。 */
10
+ type NotOnTheRow = (typeof import("../trace/wire-projection-faces.js").WIRE_PROJECTION_FACES)["prompt_manifest"]["excluded"][number];
11
+ /** 引擎的型,**词读成开集串**:投影判形,不判词表(档名、激活方式、来源类、渲染器……每一张词表的属主都是引擎,新词原样过境)。 */
12
+ type OpenWords<T> = T extends string ? string : T extends readonly (infer U)[] ? OpenWords<U>[] : T extends object ? {
13
+ [K in keyof T]: OpenWords<T[K]>;
14
+ } : T;
15
+ /**
16
+ * 耐久 `prompt_assembled` 行(turns 的 `prompt-assembled` 块与 trace SSE 的同名帧按它原样交出)= **引擎帧减不上行的键**,
17
+ * 由引擎的型派生,本仓不另写成员表。每个成员都可能缺席:引擎型里可选的成员缺席即缺席;在场却读不出的成员被丢并计数。
18
+ */
19
+ export type PromptManifestRecord = {
20
+ [K in Exclude<keyof PromptAssembledEvent, NotOnTheRow>]?: OpenWords<PromptAssembledEvent[K]>;
21
+ };
22
+ /** 投影器的入参:引擎帧的键,值一律当未知读 —— 逐层判形在投影器里做,第三方 producer / 坏世代的帧走同一道门。 */
23
+ export type PromptManifestInput = {
24
+ readonly [K in keyof PromptAssembledEvent]?: unknown;
25
+ };
26
+ /** 读一个值:读得出 ⇒ 值;读不出 ⇒ `undefined`;可选成员缺席 ⇒ {@link ABSENT}(不铸、不计)。 */
27
+ type Read<V> = (v: unknown) => V | undefined;
28
+ declare const ABSENT: unique symbol;
29
+ /** 可选格的记号:只有 {@link optional} 铸得出带它的读器 —— 一只裸读器的回值型是可选格回值型的子型,单靠回值型拦不住它。 */
30
+ declare const OPTIONAL_CELL: unique symbol;
31
+ type OptionalRead<V> = Read<V | typeof ABSENT> & {
32
+ readonly [OPTIONAL_CELL]: true;
33
+ };
34
+ /**
35
+ * 每张读表按引擎型的**每一个键**写一格(`-?`:可选键也必须有格,引擎给哪一层加成员 ⇒ 这里编译红);可选键的格必须经 {@link optional} —— 格型要求
36
+ * {@link OPTIONAL_CELL} 记号,裸读器写进可选键 ⇒ 编译红(否则缺席的成员被裸读器读成「读不出」,整句被丢)。
37
+ */
38
+ export type ReadTable<T> = {
39
+ [K in keyof T]-?: undefined extends T[K] ? OptionalRead<Exclude<T[K], undefined>> : Read<T[K]>;
40
+ };
41
+ /**
42
+ * 把一帧 `prompt.assembled` 读成耐久行(记录时白名单:tracer 同步、无 IO,未来的引擎键不随帧上行)。
43
+ * 规则一句:**只挑引擎型里有的键,逐层判形;在场却读不出 ⇒ 计一次 `server.trace.projection-input-malformed` 并丢;缺席从不计数**。
44
+ * 词原样过境(不判词表);`identity` 是这条腿的提示身份的唯一读法 —— 本仓不另铸第二种(v2 的 `constitution` 随引擎退役)。
45
+ */
46
+ export declare function promptManifestRecordOf(e: PromptManifestInput): PromptManifestRecord;
83
47
  /**
84
48
  * [ref]② per-task prompt-manifest accumulator — the `ModelUsageTracker`(budget.ts)pattern: the
85
49
  * `prompt.assembled` tracer hook is SYNC/no-IO, so it cannot persist; it RECORDS here (registered
@@ -126,4 +90,5 @@ export declare class PromptManifestTracker {
126
90
  * a stale leg's teardown after a fast re-register is a no-op (the new leg owns it now). */
127
91
  clear(taskId: string, lease: number): void;
128
92
  }
93
+ export {};
129
94
  //# sourceMappingURL=prompt-manifest.d.ts.map
@@ -1,22 +1,124 @@
1
- export function promptManifestRecordOf(e) {
2
- return {
3
- constitution: String(e.constitution),
4
- blocks: (Array.isArray(e.blocks) ? e.blocks : []).map((b) => ({ id: String(b.id), chars: b.chars, hash: String(b.hash) })),
5
- ...(Array.isArray(e.sections)
6
- ? { sections: e.sections.map((s) => ({ id: String(s.id), slot: String(s.slot), carrier: String(s.carrier), cadence: String(s.cadence), cacheClass: String(s.cacheClass), chars: s.chars, hash: String(s.hash), ...(typeof s.contentHash === "string" ? { contentHash: s.contentHash } : {}) })) }
7
- : {}),
8
- ...(Array.isArray(e.tools)
9
- ? { tools: e.tools.map((t) => ({ wireName: String(t.wireName), aliases: (Array.isArray(t.aliases) ? t.aliases : []).map(String), contractId: String(t.contractId), implementationRevision: String(t.implementationRevision), cardId: String(t.cardId), shapeDigest: String(t.shapeDigest), wireSchemaDigest: String(t.wireSchemaDigest) })) }
10
- : {}),
11
- ...(typeof e.toolDisclosure === "object" && e.toolDisclosure !== null &&
12
- typeof e.toolDisclosure.deferredTools === "number" && Number.isFinite(e.toolDisclosure.deferredTools) &&
13
- typeof e.toolDisclosure.strategy === "string" && e.toolDisclosure.strategy.length > 0 &&
14
- typeof e.toolDisclosure.source === "string" && e.toolDisclosure.source.length > 0
15
- ? { toolDisclosure: { deferredTools: e.toolDisclosure.deferredTools, strategy: e.toolDisclosure.strategy, source: e.toolDisclosure.source } }
16
- : {}),
17
- totalChars: e.totalChars,
1
+ import { dropProjectedFace } from "../trace/projection-drop.js";
2
+ const ABSENT = Symbol("absent");
3
+ const OPTIONAL_CELL = Symbol("optional-cell");
4
+ const text = (v) => (typeof v === "string" ? v : undefined);
5
+ const finite = (v) => (typeof v === "number" && Number.isFinite(v) ? v : undefined);
6
+ const flag = (v) => (typeof v === "boolean" ? v : undefined);
7
+ const onlyTrue = (v) => (v === true ? true : undefined);
8
+ const plainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v) ? v : undefined;
9
+ function optional(read) {
10
+ return Object.assign((v) => (v === undefined ? ABSENT : read(v)), { [OPTIONAL_CELL]: true });
11
+ }
12
+ function objectOf(table) {
13
+ return (v) => {
14
+ const o = plainObject(v);
15
+ if (o === undefined)
16
+ return undefined;
17
+ const out = {};
18
+ for (const [key, read] of Object.entries(table)) {
19
+ const r = read(o[key]);
20
+ if (r === undefined)
21
+ return undefined;
22
+ if (r !== ABSENT)
23
+ out[key] = r;
24
+ }
25
+ return out;
26
+ };
27
+ }
28
+ function listOf(read) {
29
+ return (v) => {
30
+ if (!Array.isArray(v))
31
+ return undefined;
32
+ const out = [];
33
+ for (const x of v) {
34
+ const r = read(x);
35
+ if (r === undefined)
36
+ return undefined;
37
+ out.push(r);
38
+ }
39
+ return out;
40
+ };
41
+ }
42
+ function entriesOf(read) {
43
+ return (v) => {
44
+ const o = plainObject(v);
45
+ if (o === undefined)
46
+ return undefined;
47
+ const out = [];
48
+ for (const [k, x] of Object.entries(o)) {
49
+ const r = read(x);
50
+ if (r === undefined)
51
+ return undefined;
52
+ out.push([k, r]);
53
+ }
54
+ return Object.fromEntries(out);
55
+ };
56
+ }
57
+ function drop(member, why) {
58
+ return dropProjectedFace("prompt_assembled." + member + " " + why);
59
+ }
60
+ function rowsOf(member, read) {
61
+ return (v) => {
62
+ if (!Array.isArray(v))
63
+ return undefined;
64
+ const out = [];
65
+ for (const x of v) {
66
+ const r = read(x);
67
+ if (r === undefined)
68
+ drop(`${member}[]`, "element does not read as the engine's type");
69
+ else
70
+ out.push(r);
71
+ }
72
+ return v.length > 0 && out.length === 0 ? ABSENT : out;
18
73
  };
19
74
  }
75
+ const readIdentity = objectOf({
76
+ artifact: text,
77
+ layers: objectOf({ catalog: text, renderers: entriesOf(finite), center: optional(text), deploy: optional(text), task: optional(text), binding: text }),
78
+ epoch: finite,
79
+ activatedBy: text,
80
+ });
81
+ const readSelectors = objectOf({
82
+ binding: optional(text),
83
+ traits: listOf(text),
84
+ host: objectOf({ role: text, form: text }),
85
+ facts: entriesOf(flag),
86
+ });
87
+ const readSection = objectOf({
88
+ id: text,
89
+ band: text,
90
+ rank: finite,
91
+ variant: optional(text),
92
+ renderer: optional(objectOf({ id: text, version: finite })),
93
+ chars: finite,
94
+ locale: text,
95
+ contentHash: text,
96
+ chain: listOf(objectOf({ source: text, overlay: optional(text), op: optional(text), superseded: optional(onlyTrue) })),
97
+ });
98
+ const ROW = {
99
+ identity: readIdentity,
100
+ selectors: readSelectors,
101
+ blocks: rowsOf("blocks", objectOf({ id: text, chars: finite, hash: text })),
102
+ sections: rowsOf("sections", readSection),
103
+ superseded: rowsOf("superseded", objectOf({ id: text, source: text, by: text, target: text, supersededBy: text })),
104
+ tools: rowsOf("tools", objectOf({ wireName: text, aliases: listOf(text), contractId: text, implementationRevision: text, cardId: text, shapeDigest: text, wireSchemaDigest: text })),
105
+ toolDisclosure: objectOf({ deferredTools: finite, strategy: text, source: text }),
106
+ totalChars: finite,
107
+ };
108
+ export function promptManifestRecordOf(e) {
109
+ const out = {};
110
+ for (const [member, read] of Object.entries(ROW)) {
111
+ const v = e[member];
112
+ if (v === undefined)
113
+ continue;
114
+ const r = read(v);
115
+ if (r === undefined)
116
+ drop(member, "does not read as the engine's type");
117
+ else if (r !== ABSENT)
118
+ out[member] = r;
119
+ }
120
+ return out;
121
+ }
20
122
  export function configAssembledRecordOf(e) {
21
123
  const rec = {
22
124
  catalogVersion: e.catalogVersion,
@@ -206,19 +206,20 @@ function redactStringLeaves(node, depth = 0) {
206
206
  }
207
207
  return out ?? node;
208
208
  }
209
+ const OFF_ARM_MEMBER_TREATMENT = "free-text-tree";
210
+ const redactDetailMember = (v) => redactStringLeaves(v, 1);
209
211
  function shapeTerminalFailureDetail(detail) {
210
212
  if (detail === null || typeof detail !== "object" || Array.isArray(detail))
211
213
  return detail;
212
214
  const d = detail;
213
215
  const kind = d.kind;
214
- const table = typeof kind === "string" && Object.hasOwn(TERMINAL_FAILURE_DETAIL_TREATMENT, kind) ? TERMINAL_FAILURE_DETAIL_TREATMENT[kind] : undefined;
215
- if (table === undefined)
216
- return redactStringLeaves(d);
216
+ const arm = typeof kind === "string" && Object.hasOwn(TERMINAL_FAILURE_DETAIL_TREATMENT, kind) ? TERMINAL_FAILURE_DETAIL_TREATMENT[kind] : {};
217
217
  let out;
218
- for (const [key, treatment] of Object.entries(table)) {
218
+ for (const key of Object.keys(d)) {
219
219
  const v = d[key];
220
220
  if (v === undefined)
221
221
  continue;
222
+ const treatment = Object.hasOwn(arm, key) ? arm[key] : OFF_ARM_MEMBER_TREATMENT;
222
223
  switch (treatment) {
223
224
  case "verbatim":
224
225
  case "provider-join-key":
@@ -228,7 +229,7 @@ function shapeTerminalFailureDetail(detail) {
228
229
  delete (out ??= { ...d })[key];
229
230
  break;
230
231
  case "free-text-tree": {
231
- const nv = redactStringLeaves(v);
232
+ const nv = redactDetailMember(v);
232
233
  if (nv !== v)
233
234
  (out ??= { ...d })[key] = nv;
234
235
  break;
@@ -251,10 +252,11 @@ function shapeTerminalBlockedDetail(detail) {
251
252
  return detail;
252
253
  const d = detail;
253
254
  let out;
254
- for (const [key, treatment] of Object.entries(TERMINAL_BLOCKED_DETAIL_TREATMENT)) {
255
+ for (const key of Object.keys(d)) {
255
256
  const v = d[key];
256
257
  if (v === undefined)
257
258
  continue;
259
+ const treatment = Object.hasOwn(TERMINAL_BLOCKED_DETAIL_TREATMENT, key) ? TERMINAL_BLOCKED_DETAIL_TREATMENT[key] : OFF_ARM_MEMBER_TREATMENT;
258
260
  switch (treatment) {
259
261
  case "verbatim":
260
262
  break;
@@ -265,6 +267,12 @@ function shapeTerminalBlockedDetail(detail) {
265
267
  (out ??= { ...d })[key] = next;
266
268
  }
267
269
  break;
270
+ case "free-text-tree": {
271
+ const nv = redactDetailMember(v);
272
+ if (nv !== v)
273
+ (out ??= { ...d })[key] = nv;
274
+ break;
275
+ }
268
276
  default: {
269
277
  const unhandled = treatment;
270
278
  throw new Error(`unhandled blocked-detail treatment: ${String(unhandled)}`);
@@ -49,10 +49,22 @@ export interface SecretEnvScrubSiteSpec {
49
49
  }
50
50
  /** 登记过的剥除现场。闭集:未登记的 site 传不进 {@link recordSecretEnvScrub}(编译期拒,同 FAIL_OPEN_TAGS 纪律)。 */
51
51
  export declare const SECRET_ENV_SCRUB_SITES: {
52
- readonly "server.remote-env-host.inherit-scrub": {
52
+ readonly "server.remote-env-host.spawn-env": {
53
53
  readonly note: "模型可驱动的 host shell 继承 env(`inheritEnv` 三词任一,缺省 `\"scrub\"`;`\"all\"` 词下扣的只有本服务自己的凭据名)——这条腿上的一次泄漏等于把编排器的脑钥/仓库令牌交给模型选的命令";
54
54
  readonly remedy: null;
55
55
  };
56
+ readonly "server.remote-env-local-docker.spawn-env": {
57
+ readonly note: "local-docker 车道交给宿主 `docker` CLI 的 env(它拉起的凭据助手 / CLI 插件同读)—— 继承词 `\"all\"`,扣的只有本服务自己的凭据;一次泄漏等于把本服务凭据交给宿主上的第三方助手";
58
+ readonly remedy: null;
59
+ };
60
+ readonly "server.remote-env-adb.spawn-env": {
61
+ readonly note: "adb 车道交给宿主 `adb` CLI 的 env(首次调用起的常驻 adb server 继承同一份并一直活着)—— 继承词 `\"all\"`,扣的只有本服务自己的凭据;一次泄漏等于把本服务凭据留在一只常驻进程的环境里";
62
+ readonly remedy: null;
63
+ };
64
+ readonly "server.hooks.spawn-env": {
65
+ readonly note: "settings hooks 的命令腿(请求带来的命令跑在宿主上)的 env —— 严格名单(`PATH` / `HOME` / 终端 / locale 等十二名)经引擎组合,名单外一律不交;请求显式给的 `settings.env` 另叠在上面";
66
+ readonly remedy: null;
67
+ };
56
68
  readonly "server.project-memory.git-subprocess": {
57
69
  readonly note: "project-memory 读仓叙事时的 git 子进程 env —— 仓内的钩子/凭证助手会以本进程身份运行";
58
70
  readonly remedy: null;
@@ -1,10 +1,22 @@
1
1
  import { summarizeRedactions } from "@sema-agent/core";
2
2
  import { describeAllowlistPostures } from "../env-name-allowlist-knobs.js";
3
3
  export const SECRET_ENV_SCRUB_SITES = {
4
- "server.remote-env-host.inherit-scrub": {
4
+ "server.remote-env-host.spawn-env": {
5
5
  note: "模型可驱动的 host shell 继承 env(`inheritEnv` 三词任一,缺省 `\"scrub\"`;`\"all\"` 词下扣的只有本服务自己的凭据名)——这条腿上的一次泄漏等于把编排器的脑钥/仓库令牌交给模型选的命令",
6
6
  remedy: null,
7
7
  },
8
+ "server.remote-env-local-docker.spawn-env": {
9
+ note: "local-docker 车道交给宿主 `docker` CLI 的 env(它拉起的凭据助手 / CLI 插件同读)—— 继承词 `\"all\"`,扣的只有本服务自己的凭据;一次泄漏等于把本服务凭据交给宿主上的第三方助手",
10
+ remedy: null,
11
+ },
12
+ "server.remote-env-adb.spawn-env": {
13
+ note: "adb 车道交给宿主 `adb` CLI 的 env(首次调用起的常驻 adb server 继承同一份并一直活着)—— 继承词 `\"all\"`,扣的只有本服务自己的凭据;一次泄漏等于把本服务凭据留在一只常驻进程的环境里",
14
+ remedy: null,
15
+ },
16
+ "server.hooks.spawn-env": {
17
+ note: "settings hooks 的命令腿(请求带来的命令跑在宿主上)的 env —— 严格名单(`PATH` / `HOME` / 终端 / locale 等十二名)经引擎组合,名单外一律不交;请求显式给的 `settings.env` 另叠在上面",
18
+ remedy: null,
19
+ },
8
20
  "server.project-memory.git-subprocess": {
9
21
  note: "project-memory 读仓叙事时的 git 子进程 env —— 仓内的钩子/凭证助手会以本进程身份运行",
10
22
  remedy: null,