@sema-agent/server 7.43.0 → 7.44.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 (345) hide show
  1. package/USAGE.md +18 -0
  2. package/dist/adoption/plan.js +31 -192
  3. package/dist/adoption/quiesce.js +3 -86
  4. package/dist/adoption/runner.js +3 -140
  5. package/dist/adoption/sql.js +0 -74
  6. package/dist/adoption/wire.js +0 -80
  7. package/dist/approval-ask-machine.js +0 -75
  8. package/dist/approval-card.js +0 -323
  9. package/dist/approval-deny-reasons.js +0 -42
  10. package/dist/approval-hmac.js +0 -35
  11. package/dist/approval-reconciler.js +8 -166
  12. package/dist/approval.js +4 -67
  13. package/dist/audit.js +1 -44
  14. package/dist/auth-bridge.js +4 -47
  15. package/dist/auth-keys.js +0 -23
  16. package/dist/bake-runner/main.js +4 -65
  17. package/dist/bake-runner/protocol.js +3 -58
  18. package/dist/bake-runner/runner.js +5 -91
  19. package/dist/bench/l8/artifact.js +2 -61
  20. package/dist/bench/l8/escape.js +0 -25
  21. package/dist/bench/l8/index.js +0 -14
  22. package/dist/bench/l8/probes.js +0 -60
  23. package/dist/bench/l8/run-probes.js +2 -45
  24. package/dist/bench/s1/arms.js +14 -189
  25. package/dist/bench/s1/live-deps.js +11 -253
  26. package/dist/bench/s1/oracle.js +1 -36
  27. package/dist/bench/s1/repair-oracle-adapter.js +0 -30
  28. package/dist/bench/s1/reviewer.js +0 -34
  29. package/dist/bench/s1/row.js +0 -62
  30. package/dist/bench/s1/run-firm.js +4 -70
  31. package/dist/bench/s1/runner-ctx.js +0 -40
  32. package/dist/bench/s1/tasks.js +0 -115
  33. package/dist/boot/adoption.js +0 -20
  34. package/dist/boot/budget-tracing.js +3 -55
  35. package/dist/boot/config-center.js +43 -551
  36. package/dist/boot/coordinators.js +1 -69
  37. package/dist/boot/crash-last.js +0 -18
  38. package/dist/boot/deferred-sandbox-path-env.js +3 -119
  39. package/dist/boot/execution-env.js +5 -171
  40. package/dist/boot/governance-seams.js +0 -92
  41. package/dist/boot/leader.js +0 -69
  42. package/dist/boot/limit-sync.js +0 -2
  43. package/dist/boot/memory-boundary.js +3 -91
  44. package/dist/boot/org-memory.js +1 -25
  45. package/dist/boot/parked-revive-gate.js +0 -150
  46. package/dist/boot/permission-rules-audit.js +1 -56
  47. package/dist/boot/reapers.js +15 -273
  48. package/dist/boot/resolve-spec.js +9 -768
  49. package/dist/boot/retention-lane.js +4 -87
  50. package/dist/boot/runner-deps.js +3 -297
  51. package/dist/boot/runtime-caps.js +0 -40
  52. package/dist/boot/session-faces.js +2 -133
  53. package/dist/boot/shutdown.js +9 -83
  54. package/dist/boot/side-query-lane.js +2 -137
  55. package/dist/boot/stores.js +15 -215
  56. package/dist/boot/task-list-lane.js +0 -18
  57. package/dist/boot/webfetch-summarize-lane.js +0 -47
  58. package/dist/boot/workflow-orchestration.js +3 -99
  59. package/dist/boot-reclaim.js +0 -20
  60. package/dist/bounded-session-map.js +0 -19
  61. package/dist/brain.js +2 -139
  62. package/dist/budget.js +9 -244
  63. package/dist/capabilities/builtin-tools.js +0 -3
  64. package/dist/capabilities/center-plugins.js +6 -51
  65. package/dist/capabilities/center-prompts.js +6 -71
  66. package/dist/capabilities/code-review-council.js +7 -40
  67. package/dist/capabilities/collab-workflows.js +1 -44
  68. package/dist/capabilities/hands-lane.js +0 -65
  69. package/dist/capabilities/memory-notice.js +0 -70
  70. package/dist/capabilities/prompt.js +0 -14
  71. package/dist/capabilities/prompts/code-review.js +0 -14
  72. package/dist/capabilities/prompts/identity.js +0 -6
  73. package/dist/capabilities/prompts/team.js +0 -4
  74. package/dist/capabilities/repo-tools.js +0 -34
  75. package/dist/capabilities/sandbox-file-send.js +3 -92
  76. package/dist/capabilities/scenarios.d.ts +0 -1
  77. package/dist/capabilities/scenarios.js +18 -279
  78. package/dist/capabilities/select-environment-tool.js +0 -32
  79. package/dist/capabilities/send-user-file-tool.js +2 -66
  80. package/dist/capabilities/skills.d.ts +1 -2
  81. package/dist/capabilities/skills.js +3 -21
  82. package/dist/capabilities/team.d.ts +3 -11
  83. package/dist/capabilities/team.js +3 -51
  84. package/dist/capabilities/tool-defer.js +0 -3
  85. package/dist/config-center/apply-effective.js +15 -330
  86. package/dist/config-center/apply-ledger.js +5 -38
  87. package/dist/config-center/facade.js +0 -41
  88. package/dist/config-center/hot-keys-registry.js +0 -20
  89. package/dist/config-center/http-client.js +2 -125
  90. package/dist/config-center/mcp-revocation.js +2 -34
  91. package/dist/config-center/read-face.js +0 -59
  92. package/dist/config-center/restart-signal.js +1 -85
  93. package/dist/config-center/skills-mcp.d.ts +1 -1
  94. package/dist/config-center/skills-mcp.js +9 -76
  95. package/dist/config-center/stage-limits.js +8 -35
  96. package/dist/config-invariants.js +0 -16
  97. package/dist/config-lkg.js +0 -42
  98. package/dist/config-provider.js +3 -186
  99. package/dist/config-types.js +0 -5
  100. package/dist/config.js +107 -1145
  101. package/dist/degenerate-instrument.js +3 -67
  102. package/dist/deployment-governance.js +0 -124
  103. package/dist/digest-form.js +0 -12
  104. package/dist/elicitation.js +3 -86
  105. package/dist/env-facts.js +7 -75
  106. package/dist/fleet/fleet-bus.js +34 -507
  107. package/dist/fleet/fleet-reconciler.js +9 -149
  108. package/dist/fleet/fleet-terminal-window.js +10 -178
  109. package/dist/fleet/subagent-tail-bus.js +3 -72
  110. package/dist/fleet-client.js +10 -70
  111. package/dist/fleet-lease.js +5 -79
  112. package/dist/git-api-kind.js +0 -3
  113. package/dist/governance-ask-marks.js +2 -78
  114. package/dist/hooks/branch-transcript.js +0 -74
  115. package/dist/hooks/cc-agent-hook-prompt.js +0 -29
  116. package/dist/hooks/cc-stop-prompt.js +1 -46
  117. package/dist/hooks/hook-llm.js +1 -53
  118. package/dist/hooks/hook-runner.js +20 -414
  119. package/dist/http/active-run-conflict.js +4 -129
  120. package/dist/http/cursor-fingerprint.d.ts +5 -0
  121. package/dist/http/cursor-fingerprint.js +5 -0
  122. package/dist/http/idempotency.js +0 -37
  123. package/dist/http/principal-gate.js +3 -40
  124. package/dist/http/route-ctx.js +0 -9
  125. package/dist/http/routes/a2a-serve.js +7 -319
  126. package/dist/http/routes/admin-config-refresh.js +0 -4
  127. package/dist/http/routes/admin-drain.js +0 -10
  128. package/dist/http/routes/adoption.js +1 -29
  129. package/dist/http/routes/agents-roster.js +1 -47
  130. package/dist/http/routes/approvals-assistant.js +32 -395
  131. package/dist/http/routes/attachments.js +4 -22
  132. package/dist/http/routes/capabilities.js +3 -420
  133. package/dist/http/routes/diagnostics.js +1 -81
  134. package/dist/http/routes/fleet.js +9 -185
  135. package/dist/http/routes/images.js +14 -239
  136. package/dist/http/routes/leader.js +0 -13
  137. package/dist/http/routes/memory-bundle.js +1 -61
  138. package/dist/http/routes/memory-policy.js +9 -99
  139. package/dist/http/routes/notify-wake.js +3 -37
  140. package/dist/http/routes/observability.js +2 -19
  141. package/dist/http/routes/retention-ops.js +2 -34
  142. package/dist/http/routes/rules.js +1 -93
  143. package/dist/http/routes/runs.js +64 -873
  144. package/dist/http/routes/session-sync.js +19 -258
  145. package/dist/http/routes/sessions-list.js +17 -43
  146. package/dist/http/routes/sessions.js +34 -226
  147. package/dist/http/routes/shared-memory.js +5 -36
  148. package/dist/http/routes/side-query.js +1 -87
  149. package/dist/http/routes/tasks.js +52 -722
  150. package/dist/http/routes/trace-usage.js +32 -195
  151. package/dist/http/routes/workflows.js +19 -195
  152. package/dist/http/run-meta.js +0 -6
  153. package/dist/http/send.js +0 -32
  154. package/dist/http/server.js +92 -1597
  155. package/dist/http/sse-lifecycle.js +2 -13
  156. package/dist/http/sse-log.js +3 -48
  157. package/dist/http/tar.js +5 -21
  158. package/dist/http/verify-rounds.js +0 -5
  159. package/dist/http/wire-gate.js +0 -9
  160. package/dist/http/workspace-content.js +0 -10
  161. package/dist/images/bake-validate.js +1 -70
  162. package/dist/images/manifest.js +1 -6
  163. package/dist/index.js +0 -21
  164. package/dist/key-resolver.js +2 -17
  165. package/dist/leader/diffout.js +1 -20
  166. package/dist/leader/diffup.js +0 -47
  167. package/dist/leader/endpoint.js +2 -57
  168. package/dist/leader/fanout.js +3 -45
  169. package/dist/leader/grader-env-factory.js +3 -72
  170. package/dist/leader/leader.js +5 -156
  171. package/dist/leader/merge.js +8 -115
  172. package/dist/leader/planner.js +3 -54
  173. package/dist/leader/repair-oracle.js +1 -60
  174. package/dist/leader/repair-wire.js +2 -79
  175. package/dist/leader/wire.js +8 -307
  176. package/dist/lsp/e2b-bridge.js +4 -64
  177. package/dist/lsp/e2b-manager.js +6 -94
  178. package/dist/lsp/lsp-frames.js +0 -12
  179. package/dist/lsp/manager.js +4 -96
  180. package/dist/lsp/ws-transport.js +5 -55
  181. package/dist/lsp-evict.js +1 -15
  182. package/dist/main.js +57 -765
  183. package/dist/memory-bundle-engine.js +0 -55
  184. package/dist/memory-export.js +0 -4
  185. package/dist/memory-posture.js +1 -15
  186. package/dist/memory-scope.js +11 -148
  187. package/dist/memory-sync-client.js +2 -44
  188. package/dist/memory-sync.js +1 -80
  189. package/dist/model-select.js +3 -80
  190. package/dist/observability/cost-quota.js +1 -17
  191. package/dist/observability/cost-taxonomy.js +0 -34
  192. package/dist/observability/fail-open.js +7 -86
  193. package/dist/observability/logger.js +0 -6
  194. package/dist/observability/metrics.js +0 -94
  195. package/dist/observability/otel-exporter.js +3 -13
  196. package/dist/observability/principal-context.js +0 -9
  197. package/dist/observability/prompt-manifest.js +1 -37
  198. package/dist/observability/rate-limit.js +0 -4
  199. package/dist/observability/secret-env-scrub.js +2 -56
  200. package/dist/observability/tool-trace.js +1 -70
  201. package/dist/orchestration/hardened-vm-runner.js +4 -118
  202. package/dist/orchestration/hardened-vm-worker-runner.js +1 -26
  203. package/dist/orchestration/hardened-vm-worker.js +0 -27
  204. package/dist/orchestration/subagent-steer.js +1 -45
  205. package/dist/orchestration/workflow-agent-steer.js +1 -80
  206. package/dist/orchestration/workflow-completion-inbox.js +32 -285
  207. package/dist/orchestration/workflow-notify-journal.js +16 -259
  208. package/dist/org-memory-admission.js +3 -47
  209. package/dist/parent-watch.js +2 -48
  210. package/dist/parked-decide.js +1 -109
  211. package/dist/per-task-image.js +0 -57
  212. package/dist/plan-cache-probe.js +3 -27
  213. package/dist/plugins/adoption-log-sql.js +2 -119
  214. package/dist/plugins/approval-ask-store-memory.js +3 -38
  215. package/dist/plugins/approval-ask-store-sql.js +6 -188
  216. package/dist/plugins/approval-exemption-store.js +2 -28
  217. package/dist/plugins/background-agent-store-sql.js +4 -105
  218. package/dist/plugins/background-shell-support.js +14 -122
  219. package/dist/plugins/blob-backend.js +6 -169
  220. package/dist/plugins/breaker-state-sql.js +8 -46
  221. package/dist/plugins/caching-session-store.js +4 -106
  222. package/dist/plugins/checkpoint-store-sql.js +18 -505
  223. package/dist/plugins/e2b-orphan-reclaim.js +0 -45
  224. package/dist/plugins/file-outcome-sink.js +0 -9
  225. package/dist/plugins/file-resume-anchor-store.js +4 -43
  226. package/dist/plugins/file-run-store.js +26 -364
  227. package/dist/plugins/file-snapshot-store-sql.js +10 -181
  228. package/dist/plugins/fork-routing-session-store.js +8 -111
  229. package/dist/plugins/host-platform.js +2 -91
  230. package/dist/plugins/image-bake-store-sql.js +7 -250
  231. package/dist/plugins/image-index-sql.js +4 -123
  232. package/dist/plugins/k8s-bg-scripts.js +4 -88
  233. package/dist/plugins/k8s-exec-protocol.js +0 -41
  234. package/dist/plugins/leader-run-store-sql.js +0 -101
  235. package/dist/plugins/local-checkpoint-store.js +8 -128
  236. package/dist/plugins/local-session-store.js +34 -296
  237. package/dist/plugins/local-task-attachment-store.js +2 -16
  238. package/dist/plugins/mailbox-store-sql.js +8 -65
  239. package/dist/plugins/memory-embedder-fingerprint.js +5 -166
  240. package/dist/plugins/memory-embedder.js +4 -70
  241. package/dist/plugins/memory-engine-pg.js +6 -170
  242. package/dist/plugins/memory-engine-tidb.js +7 -155
  243. package/dist/plugins/memory-engine-vector-util.js +0 -10
  244. package/dist/plugins/memory-key-guards.js +0 -34
  245. package/dist/plugins/memory-origin-law.js +0 -187
  246. package/dist/plugins/memory-resume-anchor-store.js +0 -17
  247. package/dist/plugins/memory-run-store.js +12 -89
  248. package/dist/plugins/memory-session-policy-store.js +0 -17
  249. package/dist/plugins/memory-sync-store-pg.js +4 -49
  250. package/dist/plugins/memory-sync-store-tidb.js +3 -35
  251. package/dist/plugins/outcome-ledger-sql.js +3 -97
  252. package/dist/plugins/permission-rule-store-file.js +5 -133
  253. package/dist/plugins/permission-rule-store-sql.d.ts +3 -0
  254. package/dist/plugins/permission-rule-store-sql.js +50 -307
  255. package/dist/plugins/pg-cost-quota.js +0 -7
  256. package/dist/plugins/pg-pool.js +0 -92
  257. package/dist/plugins/pg-rate-limiter.js +2 -13
  258. package/dist/plugins/pg-safe-json.js +4 -40
  259. package/dist/plugins/pg-session-storage.js +25 -189
  260. package/dist/plugins/posix-shell-fs.js +1 -31
  261. package/dist/plugins/remote-env-adb.js +12 -101
  262. package/dist/plugins/remote-env-e2b.js +40 -371
  263. package/dist/plugins/remote-env-file-error.js +0 -37
  264. package/dist/plugins/remote-env-host.js +63 -483
  265. package/dist/plugins/remote-env-k8s.js +32 -326
  266. package/dist/plugins/remote-env-local-docker.js +24 -157
  267. package/dist/plugins/remote-env-ssh.js +20 -128
  268. package/dist/plugins/remote-scratchpad.js +2 -32
  269. package/dist/plugins/remote-shell.js +1 -32
  270. package/dist/plugins/resume-anchor-store-sql.js +0 -11
  271. package/dist/plugins/retention-lane-store-sql.js +0 -108
  272. package/dist/plugins/retention-store-sql.js +4 -383
  273. package/dist/plugins/roster-store-sql.js +0 -55
  274. package/dist/plugins/run-store-sql.js +14 -251
  275. package/dist/plugins/s3-presign.js +2 -49
  276. package/dist/plugins/scheduler-support.js +3 -80
  277. package/dist/plugins/send-file-ledger.js +4 -53
  278. package/dist/plugins/send-user-file.js +4 -94
  279. package/dist/plugins/session-placement.js +1 -89
  280. package/dist/plugins/session-policy-store-sql.js +3 -81
  281. package/dist/plugins/session-store.js +0 -57
  282. package/dist/plugins/shared-memory-store-sql.js +4 -167
  283. package/dist/plugins/sql-driver.js +0 -17
  284. package/dist/plugins/sql-errors.js +0 -7
  285. package/dist/plugins/sql-escape.js +0 -8
  286. package/dist/plugins/sql-row-helpers.js +0 -25
  287. package/dist/plugins/store-backend.js +42 -222
  288. package/dist/plugins/store-contracts.js +2 -46
  289. package/dist/plugins/task-attachment-store.js +3 -49
  290. package/dist/plugins/task-list-store-sql.js +0 -79
  291. package/dist/plugins/tidb-cost-quota.js +1 -4
  292. package/dist/plugins/tidb-pool.js +1 -207
  293. package/dist/plugins/tidb-rate-limiter.js +3 -9
  294. package/dist/plugins/tidb-session-storage.js +4 -70
  295. package/dist/plugins/tidb-session-store.js +19 -341
  296. package/dist/plugins/tool-result-store-sql.js +4 -179
  297. package/dist/plugins/usage-window-store-sql.js +0 -8
  298. package/dist/plugins/web-search.js +10 -132
  299. package/dist/plugins/workflow-journal-store-sql.js +2 -58
  300. package/dist/plugins/workflow-run-store-sql.js +6 -91
  301. package/dist/plugins/worktree-isolation.js +6 -126
  302. package/dist/plugins/write-behind-counter.js +16 -75
  303. package/dist/principal-jwt.js +5 -60
  304. package/dist/project-memory.js +15 -146
  305. package/dist/prompts-domain-validate.js +1 -51
  306. package/dist/question.js +2 -111
  307. package/dist/resource-suspend.js +0 -18
  308. package/dist/router/route-orchestration.js +0 -77
  309. package/dist/rules-consent.d.ts +44 -5
  310. package/dist/rules-consent.js +75 -215
  311. package/dist/run-local.js +14 -381
  312. package/dist/runs.js +29 -617
  313. package/dist/runtime-caps-resolver.js +6 -132
  314. package/dist/runtime-governance.js +1 -232
  315. package/dist/sandbox-pkg-source.js +0 -37
  316. package/dist/sealed-key.js +3 -68
  317. package/dist/security.js +6 -291
  318. package/dist/session-leaf-bus.js +0 -32
  319. package/dist/session-sync-content.js +1 -69
  320. package/dist/session-sync-kernel.js +3 -60
  321. package/dist/session-sync.js +3 -66
  322. package/dist/session-titler.js +7 -41
  323. package/dist/session-watch.js +9 -73
  324. package/dist/shared-memory-scope-authorizer.js +0 -18
  325. package/dist/sighup-idle.js +1 -12
  326. package/dist/spec-fields.js +4 -143
  327. package/dist/store-live-probe.js +3 -49
  328. package/dist/task-a2a.js +1 -126
  329. package/dist/task-cwd.js +1 -103
  330. package/dist/task-mcp.js +1 -89
  331. package/dist/task-settings.js +10 -300
  332. package/dist/task-workflow.js +8 -75
  333. package/dist/tool-approval.d.ts +28 -1
  334. package/dist/tool-approval.js +70 -1430
  335. package/dist/trace/artifacts.js +5 -20
  336. package/dist/trace/engine-notice-wire.js +6 -138
  337. package/dist/trace/ledger-sink.js +7 -96
  338. package/dist/trace/project.js +4 -448
  339. package/dist/trace/redact.js +9 -77
  340. package/dist/turn-activity.js +1 -27
  341. package/dist/usage-analytics.js +4 -34
  342. package/dist/wall-clock-jump-guard.js +1 -68
  343. package/package.json +3 -3
  344. package/dist/capabilities/scenario-alias.d.ts +0 -27
  345. package/dist/capabilities/scenario-alias.js +0 -61
@@ -1,122 +1,23 @@
1
- /**
2
- * #151 车3(design/172 流内审批协议)—— **审批卡的 schema 属主 + 重放腿的纯函数**。
3
- *
4
- * 本模块只出两样东西,**都不带 IO、不带定时器、不发帧**:
5
- * 1. `ApprovalCardSchema` / `ApprovalCardEnvelopeSchema` —— 落库 `approval_ask.card_json` 与一切读面
6
- * (重放腿=刀 3c、回决端点=车4、对账扫描=车5)共用的**同一份** schema。写侧(刀 3b 的 `ensureAsk`)
7
- * 与读侧走同一个符号,于是「存的形」与「读的形」不可能各自漂。
8
- * 2. `buildReplayFrame` / `buildApprovalPreamble` —— 行 → 帧的**纯投影**(设计稿 §5.2)。
9
- *
10
- * 🔴 为什么读面必须 `safeParse` 而不是裸 as-cast:`card_json` 是 JSON 列,驱动回读的是 `unknown`。
11
- * 宪法 [2704](边界必 schema / 禁裸 as-cast / schema 单一属主)在这里是硬约束——一条形状漂了的历史行
12
- * 若被 as-cast 成 `ApprovalCard`,它会带着 `undefined` 字段一路走到 wire 上,消费端拿到的是「结构上
13
- * 合法、语义上空」的卡。`safeParse` 让这种行**当场落地为「跳过 + 一次 warn」**(§5.3)。
14
- *
15
- * schema 属主裁定(设计稿 §12-2,属主 §14 裁 (a) 变体):v1 的 schema 属主 = server 本仓,server 加
16
- * **zod 直依赖**(此前 zod 只经 `@sema-agent/registry-core` 传递到场)。抽进 `@sema-agent/registry-core`
17
- * 的时机 = 出现第二个**运行期**消费者(cli 呈卡校验排期时)——届时是「属主迁移」而不是「复制形状」,
18
- * 不违单一属主宪法;此刻抽包 = 为不存在的消费者发一轮 registry-core 版本 + floor bump,零收益。
19
- *
20
- * 命名(CLAUDE.md 工厂命名律):本文件全部是 `build*` —— 返回值是纯数据(帧对象 / 帧数组 / 计数),
21
- * 没有方法、没有捕获的行为。
22
- */
23
1
  import { z } from "zod";
24
2
  import { ASK_EVIDENCE_ABSENCE_VALUES, MAX_RULE_TEXT_CHARS } from "@sema-agent/core";
25
3
  import { redactSecrets } from "./trace/redact.js";
26
- /** 模型自由文本(`sourceAgentName` / `delegation.agentName`)的限长(设计稿 §6.2)。设计 §3.1 把
27
- * 「限长 + 脱敏」写成 **server 新增责任**(引擎无此层):spawning model 挑的名字是自由文本,
28
- * 不能指望壳去截——一条 100KB 的 agentName 在 server 侧就该被拒,而不是变成一张撑爆呈卡面的卡。 */
29
4
  export const MAX_AGENT_NAME = 200;
30
- /** 中性投影里的自由文本/标识符统一限长(工具名、toolCallId、sourceTaskId)。 */
31
5
  const MAX_IDENT = 200;
32
- /**
33
- * **一条**规则候选的形 —— 同步腿(`ApprovalCardSchema.ruleSuggestions` / `card_json`)与耐久腿
34
- * (durable park 行的 `PendingCheckpoint.ruleSuggestions`,由 `plugins/checkpoint-store-sql.ts` 窄读)
35
- * **共用这一份**。
36
- *
37
- * 🔴 单一属主(宪法 [2704]「schema 单一属主禁复制」):两条腿投的是 core 的**同一个** `RuleSuggestion`
38
- * (同步腿 = `AskRequest.ruleSuggestions`,耐久腿 = `PendingAction.ruleSuggestions`,core
39
- * `checkpoint-store.d.ts` 明写「CONTRACT (same as the synchronous `AskRequest.ruleSuggestions`)」)。
40
- * 各写一份 zod 必然漂——闭词表 `match` 加词、上限改口径,只改一处就出两种形。
41
- */
42
6
  export const RuleSuggestionSchema = z
43
7
  .object({ rule: z.string().max(MAX_RULE_TEXT_CHARS), match: z.enum(["exact", "prefix"]), command: z.string().max(MAX_RULE_TEXT_CHARS) })
44
8
  .strict();
45
- /**
46
- * {@link RuleSuggestionSchema} 的**不限长**孪生 —— 只判**结构**(两个字符串 + 闭词表 `match`),长度由
47
- * 调用方在**脱敏之后**自己截。
48
- *
49
- * 🔴 为什么必须有它(codex 对抗复审 [medium],验真后修):`redactSecrets` **会变长**(一条
50
- * `password=…` 换成更长的遮蔽标记)。拿带 `.max()` 的形去校验**脱敏前**的原文,再脱敏,产出的就可能是
51
- * 一条超限的文本;而任何**回读**路径若再跑一遍同一个函数,那一条会在第二次校验时被整条丢掉 —— 于是
52
- * 「落库时在、读回时不在」,并且**只发生在有回读的那条腿上**(SQL 有列回读、LOCAL 直接从 blob 投),
53
- * 两个后端对同一份素材给出不同的卡面。同族先例逐字在 `tool-approval.ts` 的 `persistedRuleShadowed`
54
- * 发帧点(「先脱敏再截,一次算定」)。
55
- *
56
- * ⇒ 正确顺序:**结构判(本 schema)→ 脱敏 → 截长 → 结果按 {@link RuleSuggestionSchema} 必然合形**。
57
- * 这样函数对自己的输出**幂等**,两条腿同形。
58
- *
59
- * 🔴 **未知键 `strip`(合并码重扫放宽,重扫二轮改准形):窄读只判三键结构,多余键剥掉、且不拒收**。
60
- * base 带 `.strict()`,而 zod 4 的 `.extend()` **保留** strict —— core 给 `RuleSuggestion` 加一个 additive
61
- * 字段(它对这条面一贯的做法),逐条 `safeParse` 会**条条失败**,于是耐久腿的整只候选静默清零、列落
62
- * NULL、读口省键,把「有供给」谎报成「无供给」,正是本键存在理由的反面(`checkpoint-store-sql.ts` 顶注与
63
- * wire 契约都写死「缺席=真的没有候选」)。同步腿的 `buildApprovalCard` 是显式三键投影、对加字段天然免疫
64
- * ⇒ 不放宽这一份,两条腿会在「上游长一格」这条轴上分家。
65
- *
66
- * ⚠️ **写的是 `.strip()` 不是 `.loose()`**(重扫二轮,实测更正):zod 4 的 `loose` = **passthrough** ——
67
- * 未知键**原样留在** `parsed.data` 里,与本注和契约文写的「多余键剥掉」相反。放宽要的是「不拒收」,
68
- * 剥键是另一件事,`.loose()` 只做到了前者。旧形下真正在剥键的是 {@link boundedRuleSuggestions} 里那三行
69
- * 显式投影,于是本 schema 对「剥键」这条判据是**空跑**;而它是**导出符号**,output 类型带 index signature,
70
- * 任何新消费者拿 `parsed.data` 直投就把上游未知键(其上**没有** `redactSecrets` 覆盖)带上跨租户可见的
71
- * `GET /v1/approvals`。⇒ 改默认 strip 形:additive 加键照收、未知键当场剥掉,两个意图各自成立。
72
- * 落库/读回的 {@link ApprovalCardSchema} 仍 `.strict()`(投影只写三键,多余键根本到不了那一层)。
73
- */
74
9
  export const RuleSuggestionRawSchema = RuleSuggestionSchema.extend({ rule: z.string(), command: z.string() }).strip();
75
- /**
76
- * 候选**基数**的 server 执法上限。两条腿同用。
77
- *
78
- * 🔴 **两个数,别混**(合并码重扫,档实对齐):core 的契约是 **≤2**(`exact` 恒 index 0,至多再跟一条
79
- * reviewed `prefix` 形 —— core `checkpoint-store.d.ts` 与 `permission-rule-model.d.ts` 逐字),那是**今天
80
- * 真实的**上限;本常量是 server 侧的**容忍帽**,刻意留在 4:一个被改坏/未来放宽的上游不该让运维队列
81
- * 的一行被整只拒收(同步腿这一格进的是 `.strict()` 的卡 schema,收紧到 2 会把「多给一条展示用候选」
82
- * 变成铸卡失败——在一条纯展示轴上换来一次 park,方向反了)。⇒ 消费端按 **≤4** 布局,契约文同口径成文。
83
- */
84
10
  export const MAX_RULE_SUGGESTIONS = 4;
85
- /** 已 redact 的 ask 文案上限(`AskRequest.message` 的投影)。 */
86
11
  const MAX_MESSAGE = 8192;
87
- /**
88
- * #253 件 G1(core 5.33.0 backlog #239)—— `probeCause` 的**防御性上限**三兄弟。
89
- *
90
- * 🔴 **它们不是第二个真源**:core 自己在 `normalizeProbeCause` 里已经 cap 过(亲读装树
91
- * `dist/core/checkpoint-store.js`:`PROBE_REASON_MAX = 200` 管 code、`PROBE_CAUSE_PATH_MAX = 200`
92
- * 管每一条 `shown`、`PROBE_CAUSE_MAX_SHOWN = 8` 管条数),而那三个常量**没有从包根导出**(亲验
93
- * `index.d.ts` 零命中)⇒ 抄不了值,只能各写一份。所以这里的姿势是**信封而不是政策**:取与 core 同值
94
- * (条数取 8),并由 `test/approval-probe-cause-wire.test.ts` 的「至少带到 8」一格看守 —— core 哪天放宽
95
- * 条数,那一格仍绿(我们少带几条,`total` 逐字保真 ⇒ 消费端算得出「还有 N 条没点名」,不是谎报)。
96
- *
97
- * 为什么 `code` 超限是**整只丢**而不是截:`code` 是**机器可读**的因由 id,截出来的是**另一个**码 ——
98
- * 消费端会把它映射到错误的文案,比没有更坏。`shown` 相反:少点名几条是诚实的省略(`total` 还在)。
99
- */
100
12
  const MAX_PROBE_CAUSE_CODE = 200;
101
13
  const MAX_PROBE_CAUSE_PATH = 200;
102
14
  const MAX_PROBE_CAUSE_SHOWN = 8;
103
- /**
104
- * #253 件 G1 —— 一族**操作数**(`roots` / `further`)的形。`shown` 是可点名的条目,`total` 是这一族的
105
- * 真实基数。**不变量 `shown.length ≤ total`**:少报条目是诚实的省略(消费端渲「另有 N 条」),
106
- * 谎报 `total` 是错的。上限见 {@link MAX_PROBE_CAUSE_SHOWN} 顶注(信封,不是政策)。
107
- */
108
15
  const ProbeCauseOperandsSchema = z
109
16
  .object({
110
17
  shown: z.array(z.string().max(MAX_PROBE_CAUSE_PATH)).max(MAX_PROBE_CAUSE_SHOWN),
111
18
  total: z.number().int().nonnegative(),
112
19
  })
113
20
  .strict();
114
- /**
115
- * #253 件 G1 —— `probeCause` 的形(落库/读面共用这一份;窄读入口是 {@link readProbeCause})。
116
- *
117
- * `.strict()` 与卡的其余部分同调:落进 `card_json` 的东西是**本仓投出来的**三键,不可能有未知键
118
- * (未知键在窄读那一层就被 zod 默认 strip 掉了)。
119
- */
120
21
  export const ProbeCauseSchema = z
121
22
  .object({
122
23
  code: z.string().min(1).max(MAX_PROBE_CAUSE_CODE),
@@ -124,8 +25,6 @@ export const ProbeCauseSchema = z
124
25
  further: ProbeCauseOperandsSchema.optional(),
125
26
  })
126
27
  .strict();
127
- /** 窄读的入参形:只判**结构**,长度由 {@link readProbeCause} 在判完之后自己截(与
128
- * {@link RuleSuggestionRawSchema} 同款分工:先判形、再截,函数对自己的输出幂等)。 */
129
28
  const ProbeCauseRawOperandsSchema = z.object({ shown: z.array(z.string()), total: z.number().int().nonnegative() });
130
29
  const ProbeCauseEnvelopeSchema = z.object({
131
30
  probeCause: z
@@ -136,17 +35,6 @@ const ProbeCauseEnvelopeSchema = z.object({
136
35
  })
137
36
  .optional(),
138
37
  });
139
- /**
140
- * `AskRequest.probeCause`(以及耐久路同值的 `RiskDescriptor.probeCause`)的**边界窄读** —— 活卡帧与
141
- * `card_json` 的**唯一**铸造点,两面因此结构性同值(不是两处各挑一次键的巧合)。
142
- *
143
- * 🔴 姿势与 `RiskAxesEnvelopeSchema` 一致:`safeParse` 而不是裸 `as`(宪法 [2704] 边界必 schema)。
144
- * 形不合 ⇒ **按缺席处置**(绝不半解出一个残缺结构上卡面);未知键被 zod 默认 strip(core additive
145
- * 加键不该让整只 ask 的卡面塌掉)。
146
- *
147
- * 🔴 截长的两条不同判据(理由全文见 {@link MAX_PROBE_CAUSE_CODE} 顶注):`code` 超限 ⇒ **整只丢**
148
- * (截出来的是另一个机器码);`shown` 超限 ⇒ **截条目**,`total` 逐字保真。
149
- */
150
38
  export function readProbeCause(req) {
151
39
  const parsed = ProbeCauseEnvelopeSchema.safeParse(req);
152
40
  const raw = parsed.success ? parsed.data.probeCause : undefined;
@@ -156,8 +44,6 @@ export function readProbeCause(req) {
156
44
  return undefined;
157
45
  const family = (f) => ({
158
46
  shown: f.shown.slice(0, MAX_PROBE_CAUSE_SHOWN).map((p) => (p.length > MAX_PROBE_CAUSE_PATH ? p.slice(0, MAX_PROBE_CAUSE_PATH) : p)),
159
- // 不变量兜底:上游若少报 total(`total < shown.length`),取二者较大者 —— 与 core 的
160
- // `Math.max(total, shown.length)` 同向,永不让消费端算出负数的「另有 N 条」。
161
47
  total: Math.max(f.total, Math.min(f.shown.length, MAX_PROBE_CAUSE_SHOWN)),
162
48
  });
163
49
  return {
@@ -166,27 +52,10 @@ export function readProbeCause(req) {
166
52
  ...(raw.further !== undefined ? { further: family(raw.further) } : {}),
167
53
  };
168
54
  }
169
- /**
170
- * #263 件 G2(core 5.35.0 design/252 G-2)—— `ruleEvidence` 的**防御性上限**两兄弟(信封非政策,
171
- * 与 {@link MAX_PROBE_CAUSE_CODE} 家族同姿势:core 侧值来自引擎自己的治理层,没有导出上限可抄)。
172
- * `dot.actor` 是身份串(规则属主 id),256 取 core `APPROVER_ATTRIBUTION_MAX_CHARS` 的同族语义;
173
- * dots 条数的真实域 = 同一条规则文本的并发批准数(个位数),16 是纯防御臂。
174
- * 🔴 dots 超限 / actor 超限 ⇒ **整只丢**而不是截:core 契约明写「The set is the identity」——截过的
175
- * 集合、截过的 actor 名的是**另一个**身份,比缺席更坏(与 probeCause `code` 超限同判据)。
176
- */
177
55
  const MAX_EVIDENCE_DOT_ACTOR = 256;
178
56
  const MAX_EVIDENCE_DOTS = 16;
179
- /** 五词缺席词表——单一属主 = core 的 `ASK_EVIDENCE_ABSENCE_VALUES`(闭集常量,不在本仓抄词;
180
- * 上游加词时这里自动跟随)。`z.custom` 而不是 `z.enum`:后者要非空元组字面量型,从 readonly 数组
181
- * 常量过去只能 `as unknown as`(类型卫生门禁),而运行期判据本来就是「在 core 词表里」这一件事。 */
182
57
  const AskEvidenceAbsenceSchema = z.custom((v) => typeof v === "string" && ASK_EVIDENCE_ABSENCE_VALUES.includes(v));
183
- /** 一颗规则 add dot(core `RuleDot`:add 的持久身份 `{actor, counter}`)。 */
184
58
  const RuleDotSchema = z.object({ actor: z.string().min(1).max(MAX_EVIDENCE_DOT_ACTOR), counter: z.number().int().nonnegative() }).strict();
185
- /**
186
- * #263 件 G2 —— `AskRequest.ruleEvidence` 的形(帧 / `card_json` / 读面共用;窄读入口是
187
- * {@link readRuleEvidence})。三对成员各 = 值**或**五词命名缺席(`AskEvidenceAbsence`),core 引擎
188
- * 盖章的证据恰一在场;server 只判形转录,**永不**自铸缺席词(server 铸词 = 谎报「某一层没报」)。
189
- */
190
59
  export const RuleEvidenceSchema = z
191
60
  .object({
192
61
  orgRevision: z.number().finite().optional(),
@@ -197,8 +66,6 @@ export const RuleEvidenceSchema = z
197
66
  personalRuleDotsAbsent: AskEvidenceAbsenceSchema.optional(),
198
67
  })
199
68
  .strict();
200
- /** 窄读的入参形(不限长孪生,分工同 {@link ProbeCauseEnvelopeSchema}):只判结构,长度/脱敏由
201
- * {@link readRuleEvidence} 判完之后一次算定。 */
202
69
  const RuleEvidenceEnvelopeSchema = z.object({
203
70
  ruleEvidence: z
204
71
  .object({
@@ -211,18 +78,6 @@ const RuleEvidenceEnvelopeSchema = z.object({
211
78
  })
212
79
  .optional(),
213
80
  });
214
- /**
215
- * `AskRequest.ruleEvidence`(core 5.35.0 design/252 G-2)的**边界窄读**——活卡帧(`ToolApprovalFrame.
216
- * ruleEvidence`)与 `card_json`(`buildApprovalCard`)的**唯一**铸造点,两面结构性同值(与
217
- * {@link readProbeCause} 同款分工)。姿势逐条:
218
- * · `safeParse`,形不合 ⇒ 按缺席处置(宪法 [2704]);未知键 strip(core additive 加键不塌卡)。
219
- * · **恰一不变量**:一对成员(值 / 缺席词)**双在场** ⇒ 整只丢——那是矛盾证据(引擎盖章的恰一,
220
- * 双在场只可能是第三方 producer 的坏行),半解上卡比缺席更坏。双缺席**容忍**(老 producer 行)。
221
- * · `orgRule` 内容族(管理员写的规则文本)⇒ `redactSecrets` 后截 `MAX_RULE_TEXT_CHARS`,**先脱敏
222
- * 再截一次算定**(`persistedRuleShadowed` R2-[medium] 教训:redact 会变长,两面各截会漂);空串丢整只。
223
- * · `dots.actor` 身份串 ⇒ `redactSecrets`(对正常 id 恒等);超限**整只丢**(见上限顶注,身份不可截)。
224
- * · `orgRevision` / 缺席词 verbatim(数值 / 闭集词,非内容)。
225
- */
226
81
  export function readRuleEvidence(req) {
227
82
  const parsed = RuleEvidenceEnvelopeSchema.safeParse(req);
228
83
  const raw = parsed.success ? parsed.data.ruleEvidence : undefined;
@@ -257,122 +112,25 @@ export function readRuleEvidence(req) {
257
112
  ...(raw.personalRuleDotsAbsent !== undefined ? { personalRuleDotsAbsent: raw.personalRuleDotsAbsent } : {}),
258
113
  };
259
114
  }
260
- /**
261
- * design/172 §3.1 的**中性投影**——呈卡面看到的全部内容,与 wire 面的既有 `ToolApprovalFrame` 解耦。
262
- *
263
- * `risk` 的三态形(设计稿 §14.1,core [2794] 回帖后定):`AskRequest.riskAxes?.{irreversible,egress}`
264
- * 是 **additive optional**,**缺席 = 引擎未判,不是「安全」**。所以两轴在这里是 `optional()`:
265
- * `true` / `false` / **缺席(未标注)** 三态各自可分,投影层**禁把缺席折算成 false** —— 那等于替引擎
266
- * 打包票。`requiresRealApproval` 是**粗粒度**安全类标记,两侧的在场契约**不同、别混**:core 侧是
267
- * `AskRequest.requiresRealApproval?: boolean`(**可选、只在为真时带**,缺席 = 这不是一次安全类 ask,是
268
- * 正常的否定形而不是坏形);本卡面这一格是**必填 boolean**,由车2 的入参归一化(`=== true`)而来。
269
- * 它与两轴是两件事而不是新旧替代——原注写的「core 今天唯一在场的标记」是 `riskAxes` 上树之前的现势话,
270
- * 自 core **5.14.0**(其 CHANGELOG 的 `AskRequest.riskAxes` additive 条)起两者并存。
271
- */
272
115
  export const ApprovalCardSchema = z
273
116
  .object({
274
117
  toolName: z.string().max(MAX_IDENT),
275
- /** 已 redact(车2 `redactDeep`)。 */
276
118
  message: z.string().max(MAX_MESSAGE),
277
- /** 已 redactDeep + **字节**帽(设计稿 §6.5:`Buffer.byteLength`,不是 `.length` 的 UTF-16 单元)。 */
278
119
  args: z.unknown().optional(),
279
120
  argsOmitted: z.literal(true).optional(),
280
121
  toolCallId: z.string().max(MAX_IDENT).optional(),
281
122
  risk: z
282
123
  .object({
283
- /** 缺席 = 未标注(禁折算 false)。 */
284
124
  irreversible: z.boolean().optional(),
285
- /** 缺席 = 未标注(禁折算 false)。 */
286
125
  egress: z.boolean().optional(),
287
126
  requiresRealApproval: z.boolean(),
288
127
  })
289
128
  .strict(),
290
- /**
291
- * [2942]/[2943] **ADDITIVE**:`true` ⇔ 这只 ask 的门来自运维治理层(语义、判定缝与「缺席 ≠ false」
292
- * 的硬条款逐字见 `tool-approval.ts` 的 `ToolApprovalFrame.governanceForced` 与
293
- * `governance-ask-marks.ts` 顶注)。放在**卡的顶层**而不是 `risk` 里:`risk` 讲的是引擎对这次操作的
294
- * 风险判定(不可逆/出网/安全类),本键讲的是**门是谁下的**,两件事。
295
- *
296
- * ⚠️ **回滚窗的行为(codex 交叉复审 round1 [high],验真后按「真实但内生」收下)**。形版本闩
297
- * (`schemaVersion`)**不动**:additive optional 键在 `.strict()` 下对**旧行**无碍(缺席合法),代价
298
- * 全在**回滚方向** —— 一个降级回旧二进制的副本读到带本键的新行,`safeParse` 会失败,两条读面各自:
299
- * · 重放腿(`buildReplayFrame`)⇒ 该行**跳过** + 一次 warn(§5.3),不炸流;
300
- * · `askBroadcast` 幂等命中该行 ⇒ 走「持久行不可用」臂 ⇒ `"unavailable"` = **park 路由**
301
- * (tool-approval.ts 的 `persisted-row-unusable`)。park 是本协议的 fail-safe 出口:人仍可经
302
- * durable gate 补批 —— 不是「批准凭空消失」,更不是把它折算成 deny。
303
- * 这个代价是**任何** additive 键在一个 `.strict()` schema 下的内生代价,不是本键独有:bump 到
304
- * `schemaVersion: 2` 只会更糟(旧读面 `z.literal(1)` 直接全量拒收**所有**新行,连缺席本键的行都收不了)。
305
- * ⇒ 处置 = 不改形版本;**发车条款**写进 CHANGELOG:整队滚前(不留长期混版窗),回滚窗内新卡降级为
306
- * park(fail-safe),回滚窗结束即自愈。
307
- */
308
129
  governanceForced: z.literal(true).optional(),
309
- /**
310
- * #144(core 5.25.0 [3438]/[3443],**ADDITIVE**):这次 ask **命中了**一条用户的持久 allow 规则,
311
- * 但规则**清不掉**它 —— 值 = 那条被越级的规则**原文**(语义、消音边界与「缺席 ≠ 没有规则」的
312
- * 硬条款逐字见 `tool-approval.ts` 的 `ToolApprovalFrame.persistedRuleShadowed`)。
313
- *
314
- * 放在**卡的顶层**、与 `governanceForced` 并列而不是塞进 `risk`:`risk` 是引擎对这次**操作**的风险
315
- * 判定,这一格讲的是「你那条规则怎么了」——两件事。与 `governanceForced` 也**刻意分列**:那个说
316
- * 「门是运维下的」,这个说「有一条你的规则在场但被越级」,两者可以同时在场也可以各自单独在场
317
- * (governance 是不可消音的三个来源之一,doctrine `always` 与工具自带 egress/irreversible mark 是另两个)。
318
- *
319
- * 值域 = **用户内容族**(规则原文是人写的文本,core 侧已过 `inlineUntrusted` 200 cap):
320
- * 写侧在**上游**(`tool-approval.ts` 发帧点)过 `redactSecrets` 再交进来,本层只截长(`clip`)。
321
- * UNTRUSTED-for-display,同 `message`/`args` 待遇。
322
- *
323
- * 回滚窗代价与 `governanceForced` **逐字同族**(见上一段那条 ⚠️):`.strict()` 下旧二进制读不动带本键的
324
- * 新行 ⇒ 重放跳过 + 幂等重入走 park(fail-safe),`schemaVersion` 不动。
325
- */
326
130
  persistedRuleShadowed: z.string().max(MAX_RULE_TEXT_CHARS).optional(),
327
- /**
328
- * #154 车二(core 5.18.0 design/179):引擎为这次 ask 铸的**规则候选**(`Bash(git status)` 精确 /
329
- * `Bash(git status:*)` 词界前缀)—— 呈卡面据它渲「不再询问」。
330
- *
331
- * 🔴 **在场性即承诺**:这一格只在**规则店真装配**(`RunnerDeps.permissionRuleStore` 在场 ⇔ manifest
332
- * 的 `permissionRules.storeWired: true`)时投影。店缺席仍投 = 给客户端渲一格它按下去无处可兑的
333
- * 「不再询问」,那是 wire 谎言,比缺席更坏(判据逐字见 `core-keyset-guard.ts` ④ 面本键那一段)。
334
- * 缺席 ⇒ 这只 ask 没有可兑的候选(命令不可匹配,或本部署没接店),两种都读作「本卡无此选项」。
335
- * 值**逐字**取自 core(闭词表 `match` + 引擎铸的文本),server 不重铸、不重排。
336
- *
337
- * ⚠️ **兑付面的射程,如实说清**(codex 交叉复审 round1 [medium],验真后按「真实但已知」登记):
338
- * 本键随卡进 `card_json`,于是重放帧也带它;而它的兑付通道**只有一条** —— 重放帧自己携带的
339
- * `approvalId` 所指的那条 live 回决口(`POST /v1/tool-approvals/:id/respond` 的 `persistRule`)。
340
- * 那条口是**进程内**的(pending map 随副本走),所以:同副本重连 ⇒ 兑得动;换副本/重启之后 ⇒ 那个
341
- * `approvalId` 已经 404,人只能走 durable 回决口(`POST /v1/tasks/:id/asks/:askId/decision`),
342
- * 而**那条口 v1 不带规则位**(它的 zod 体是 `.strict()`,多给一个 `persistRule` 是响亮 400,不是静默丢)。
343
- * 这不是本键独有的射程:`approvalId` 本身就是同一个可用性信封(卡还能不能被这条通道决,取决于它)。
344
- * ⇒ 处置 = 不缩键(缩了,同副本重连这条真实可用的路也一起没了),把「durable 回决腿的规则位」
345
- * 登记为后续件(车4 域)。
346
- */
347
131
  ruleSuggestions: z.array(RuleSuggestionSchema).max(MAX_RULE_SUGGESTIONS).optional(),
348
- /**
349
- * #253 件 G1(core 5.33.0 backlog #239,判据帖 [3930] G1;**ADDITIVE**)——这次 ask **为什么**被
350
- * 收紧的**结构化**因由:`{ code, roots:{shown,total}, further?:{shown,total} }`。
351
- *
352
- * 🔴 **结构就是契约**(core d.ts 逐字:"render the entries as data, never re-derive structure by
353
- * splitting or joining them"):`code` 是机器可读的因由 id(`<domain>.<snake_case>`),两个操作数族
354
- * 各自带**真实基数** `total` —— `shown` 被省略时 `shown.length < total`,消费端据此渲「另有 N 条」。
355
- * 引擎交出的是 code + 数组,**永不**是写好的句子;句子归呈卡端按本地语言渲。所以本层**逐字透传结构**,
356
- * 不拍扁成文本、不重排、不合并两族。
357
- *
358
- * 🔴 **缺席 ≠「没有原因」**:只有「maybe 档收紧 ∧ 探针真给了 cause」的 ask 才有它。绝大多数 ask
359
- * 压根不经这条路(缺席),而**判得出可回滚**的调用根本不会变成 ask。
360
- *
361
- * 🔴 与 `message` **刻意分列、不合并**(判据帖 G1 的负控):`message` 是分类器/模型看得见的输入面,
362
- * 把一份随路径变化的操作数清单挤进去会污染判定输入;本键是**展示面**的结构化数据。
363
- *
364
- * 回滚窗代价与 `governanceForced` / `persistedRuleShadowed` **逐字同族**(见那两条的 ⚠️):
365
- * `.strict()` 下旧二进制读不动带本键的新行 ⇒ 重放跳过 + 幂等重入走 park(fail-safe),`schemaVersion` 不动。
366
- */
367
132
  probeCause: ProbeCauseSchema.optional(),
368
- /**
369
- * #263 件 G2(core 5.35.0 design/252 G-2):这只 ask 背后的**规则出处证据**——org 快照 revision /
370
- * 命中的 org 规则原文 / 被越级的个人规则 add dots,每员 = 值或五词命名缺席。展示/对账元数据,
371
- * **永不**是裁决输入(core 契约逐字:removing it would leave every decision byte-identical)。
372
- * 回滚窗代价与 `probeCause` 逐字同族(`.strict()` 下旧二进制读不动新行 ⇒ 重放跳过 + park fail-safe)。
373
- */
374
133
  ruleEvidence: RuleEvidenceSchema.optional(),
375
- /** 委派出处(子代 ask 才在场;判别键 = `fromSubagent`,core RB-39②)。 */
376
134
  fromSubagent: z.literal(true).optional(),
377
135
  sourceTaskId: z.string().max(MAX_IDENT).optional(),
378
136
  sourceAgentName: z.string().max(MAX_AGENT_NAME).optional(),
@@ -385,12 +143,6 @@ export const ApprovalCardSchema = z
385
143
  .optional(),
386
144
  })
387
145
  .strict();
388
- /**
389
- * `approval_ask.card_json` 里真正存的东西(设计稿 §6.2)。存**信封**而不是裸卡的理由:重放帧要回填
390
- * 车2 铸的 `approvalId`(wire 面的回决通道桥 + 消费端与并行 `tool_approval` 帧的去重键),而它不属于
391
- * 「卡的内容」——放进卡里会让 schema 的语义边界糊掉。`schemaVersion` 是形的版本闩(与行上的
392
- * `schema_version` 列同值,列供 SQL 侧过滤,信封里这份供读面在解出内容**之前**判形)。
393
- */
394
146
  export const ApprovalCardEnvelopeSchema = z
395
147
  .object({
396
148
  schemaVersion: z.literal(1),
@@ -398,26 +150,7 @@ export const ApprovalCardEnvelopeSchema = z
398
150
  card: ApprovalCardSchema,
399
151
  })
400
152
  .strict();
401
- /** 本车铸的信封形版本(写侧=刀 3b,读侧=本文件)。 */
402
153
  export const APPROVAL_CARD_SCHEMA_VERSION = 1;
403
- /**
404
- * core 的风险轴(`AskRequest.riskAxes`,[2829]/§14.1)的**边界窄读**。
405
- *
406
- * ✅ **已点亮**:`riskAxes` 现在既在类型面也在真码面(树上 core 5.16.0,`core/tool-policy.d.ts` 的
407
- * `readonly riskAxes?: {…}`;package.json 的 floor 已抬到 `^5.16.0`)。窄读当初写下时的现势前提是
408
- * 「树上 core d.ts(5.13.0)还没有这个键、字段形只由 core 认领」——该键自 core **5.14.0** 起就在
409
- * (其 CHANGELOG 的 `AskRequest.riskAxes` additive 条),那句现势话早已过期,**别再据它判断「core 还没
410
- * 供值 ⇒ 卡面 risk 恒缺席」**。判在不在场以**装树的 d.ts 为准**,不要读注里的版本号
411
- * (同族销账见 `tool-approval.ts` 的 `readBoundInputHash` 头注,#164)。
412
- *
413
- * 🔴 保留 `safeParse` 而不改成直读 `req.riskAxes` 的理由不变(它从来不只是等字段):窄读让**编译与行为
414
- * 解耦**,缺席=未标注;裸 `as` 转型会在两个方向上都出错——既绕过宪法 [2704] 的「边界必 schema」,也会在
415
- * core 发出一个形状不同的键(或加轴)时静默把垃圾投上卡面。
416
- *
417
- * 未知键被 zod 默认 strip(此处**故意不 `.strict()`**:core additive 加轴时不该让整只 ask 的卡面塌掉);
418
- * 形不合(如 `irreversible: "yes"`)⇒ 整个 safeParse 失败 ⇒ 按**缺席**处置 = 「未标注」,
419
- * 绝不折算成 `false`(那等于替引擎打包票,§14.1 明令)。
420
- */
421
154
  const RiskAxesEnvelopeSchema = z.object({
422
155
  riskAxes: z
423
156
  .object({
@@ -426,31 +159,15 @@ const RiskAxesEnvelopeSchema = z.object({
426
159
  })
427
160
  .optional(),
428
161
  });
429
- /** 自由文本的**截断**(不是拒绝):卡必须还是要送到人手上——`toolName`/`message` 少几个字仍可决策,
430
- * 而整只 ask 因为一个 200KB 的 agentName 落不了库/上不了 wire 才是真事故(设计稿 §6.2 的 `.max()`
431
- * 在 server 侧的落实形,E-5 钉)。`undefined` 透传(缺席 ≠ 空串)。 */
432
162
  function clip(s, max) {
433
163
  if (s === undefined)
434
164
  return undefined;
435
165
  return s.length > max ? s.slice(0, max) : s;
436
166
  }
437
- /**
438
- * design/172 §3.1 的**中性投影**(设计稿 §6.2)—— 写侧的唯一铸造点。
439
- *
440
- * `risk` 三态(§14.1):两轴 `optional`,`true`/`false`/**缺席(未标注)** 各自可分。`req` 是 core 交来的
441
- * `AskRequest`(可能带、也可能不带 `riskAxes`),按 `unknown` 窄读;`requiresRealApproval` 走**独立入参**
442
- * (车2 已归一化的 boolean),不从 `req` 里读。core 侧它是可选、只在为真时带,缺席 = 这不是一次安全类
443
- * ask(core 的铸造点语义,不是我们的折算);卡面这一格恒在,`false` 就是那个否定形的如实投影。
444
- * (原注写的「core 今天唯一在场的粗粒度标记」自 core 5.14.0 的 `riskAxes` 起过期,见 `ApprovalCardSchema`
445
- * 头注。)
446
- */
447
167
  export function buildApprovalCard(source, req, requiresRealApproval) {
448
168
  const axes = RiskAxesEnvelopeSchema.safeParse(req);
449
169
  const riskAxes = axes.success ? axes.data.riskAxes : undefined;
450
- // #253 件 G1:`probeCause` 与两轴同款,从 `req`(core 的 AskRequest)窄读 —— 发帧点用**同一个**函数
451
- // 铸帧上那一份,于是「帧与卡同值」是结构事实而不是两处各挑一次键的巧合(§6.5 同款判据)。
452
170
  const probeCause = readProbeCause(req);
453
- // #263 件 G2:`ruleEvidence` 同款(readRuleEvidence 是帧/卡唯一铸造点,redact+截长在函数内一次算定)。
454
171
  const ruleEvidence = readRuleEvidence(req);
455
172
  const toolCallId = clip(source.toolCallId, MAX_IDENT);
456
173
  const sourceTaskId = clip(source.sourceTaskId, MAX_IDENT);
@@ -466,18 +183,10 @@ export function buildApprovalCard(source, req, requiresRealApproval) {
466
183
  ...(riskAxes?.egress !== undefined ? { egress: riskAxes.egress } : {}),
467
184
  requiresRealApproval,
468
185
  },
469
- // [2942]/[2943]:只在为真时投影(`=== true` 严判:非布尔真值不得把一张普通卡染成治理卡)。
470
186
  ...(source.governanceForced === true ? { governanceForced: true } : {}),
471
- // #144:被越级的规则原文。**空串不投**(core 的四个 mint 点只在真有命中规则时带这个键 ⇒ 空串不是
472
- // 「有一条空规则」而是坏值;投一个空串会让壳渲一行「你的规则 ⟨⟩ 仍在」)。截长同 `message` 待遇。
473
187
  ...(shadowedRule !== undefined && shadowedRule !== "" ? { persistedRuleShadowed: shadowedRule } : {}),
474
- // #253 件 G1:结构化探针因由 —— 与活卡帧**同一个**窄读函数(`readProbeCause`),两面结构性同值;
475
- // 形不合/缺席 ⇒ 不铸键(语义见 `ApprovalCardSchema.probeCause` 顶注)。
476
188
  ...(probeCause !== undefined ? { probeCause } : {}),
477
- // #263 件 G2:规则出处证据 —— 同款同源(`readRuleEvidence`);形不合/缺席 ⇒ 不铸键。
478
189
  ...(ruleEvidence !== undefined ? { ruleEvidence } : {}),
479
- // #154 车二:候选**逐字**投影(空数组 ⇒ 不投键 —— 「没有候选」与「本部署不供候选」在卡面上都读作
480
- // 「本卡无此选项」,铸一个空数组只会让消费端多一条无意义的分支)。拷贝一份:卡不与调用方共享可变引用。
481
190
  ...(source.ruleSuggestions !== undefined && source.ruleSuggestions.length > 0
482
191
  ? { ruleSuggestions: source.ruleSuggestions.map((s) => ({ rule: s.rule, match: s.match, command: s.command })) }
483
192
  : {}),
@@ -495,18 +204,9 @@ export function buildApprovalCard(source, req, requiresRealApproval) {
495
204
  : {}),
496
205
  };
497
206
  }
498
- /** 落库信封的纯构造(写侧;读侧 = `ApprovalCardEnvelopeSchema.safeParse`,**同一个 schema**)。 */
499
207
  export function buildApprovalCardEnvelope(approvalId, card) {
500
208
  return { schemaVersion: APPROVAL_CARD_SCHEMA_VERSION, approvalId, card };
501
209
  }
502
- /**
503
- * design/172 §3.1 的呈卡帧(设计稿 §4.1)—— **live 铸造**形(重放形见 {@link buildReplayFrame},
504
- * 两者投的是同一种帧,区别只在 `card` 的来源:这里是刚投影出来的,那里是从行上回读的)。
505
- *
506
- * `expiresAtMs` **入参**而不是在这里现算:同一只 ask 的窗**只铸一次**(§3.1「永不赋新 deadline」),
507
- * 调用点已经算好了它(既是定时器时长的来源,也是落库 `expires_at_ms` 的值)——在这里重算会铸出第二个
508
- * 真源,而两个真源必然漂。
509
- */
510
210
  export function buildApprovalRequestFrame(input) {
511
211
  return {
512
212
  type: "approval_request",
@@ -521,22 +221,9 @@ export function buildApprovalRequestFrame(input) {
521
221
  approvalId: input.approvalId,
522
222
  };
523
223
  }
524
- /** 撤卡帧的纯构造(`build*`:返回纯数据,无方法无捕获行为)。`askIds` 拷贝一份 —— 调用方传进来的多是
525
- * store 返回的数组,帧不该与它共享可变引用。 */
526
224
  export function buildRevokeFrame(batchId, askIds, reason, serverNowMs) {
527
225
  return { type: "approval_revoke", schemaVersion: 1, batchId, askIds: [...askIds], reason, serverNowMs };
528
226
  }
529
- /**
530
- * 持久行 → 重放帧(设计稿 §5.2 逐字)。
531
- *
532
- * 三条不变量都在这几行里:
533
- * - **坏行不炸开流**:`safeParse` 失败 ⇒ 返回 `undefined`(调用方跳过 + 记一次 warn,§5.3)。
534
- * - **不续窗**:`expiresAtMs` 逐字回读,**永不重铸**;`expiresInMs` 由它与 `nowMs` 现算 ⇒ 同一张卡
535
- * 连续两次重放必然严格单调减(机器判据 = §10 钉 B-2)。
536
- * - **零写**:本函数不碰 store —— 重放腿只读,不 `transitionAsk`、不改 `expires_at_ms`、不重挂定时器。
537
- * 过窗未收敛的行(`expiresInMs === 0`)**仍然重放**,不在这里代打 `expireAsk` CAS(那会制造第五个
538
- * 竞争者;收敛是车2 的窗到期竞争者与车5 恢复扫描的职责)。
539
- */
540
227
  export function buildReplayFrame(row, nowMs) {
541
228
  const parsed = ApprovalCardEnvelopeSchema.safeParse(row.cardJson);
542
229
  if (!parsed.success)
@@ -554,15 +241,6 @@ export function buildReplayFrame(row, nowMs) {
554
241
  approvalId: parsed.data.approvalId,
555
242
  };
556
243
  }
557
- /**
558
- * 开流 preamble 的行集 → 帧集(设计稿 §5.3 的读面帽 + 坏行跳过)。
559
- *
560
- * 帽的方向:超限时**只投最新的 N 张**——未决卡的价值随时间倒序递减(最老的那些多半已经在别处过窗/
561
- * 被收敛),而壳的呈卡面容量有限。丢弃是**读面**行为,不写库、不改状态;写侧的准入门(设计稿 §0 X-2)
562
- * 是另一件事,在刀 3b。
563
- *
564
- * `replayMax <= 0` 视作「不投」(运维显式关掉重放),不当成「无帽」。
565
- */
566
244
  export function buildApprovalPreamble(rows, nowMs, replayMax) {
567
245
  const frames = [];
568
246
  let skipped = 0;
@@ -577,7 +255,6 @@ export function buildApprovalPreamble(rows, nowMs, replayMax) {
577
255
  const dropped = Math.max(0, frames.length - cap);
578
256
  return { frames: dropped > 0 ? frames.slice(frames.length - cap) : frames, skipped, dropped };
579
257
  }
580
- /** preamble 帧的 SSE 投递形(具名事件,**不带 `id:`** —— 它不是账本行,不参与 Last-Event-ID 游标)。 */
581
258
  export function buildApprovalPreambleSseFrames(frames) {
582
259
  return frames.map((f) => ({ event: f.type, data: f }));
583
260
  }
@@ -1,54 +1,12 @@
1
- /**
2
- * 流内审批协议(design/172 §3.0 对账三约束)的**终态归因词表** —— #151 车5。
3
- *
4
- * 两组常量、**两个不相交的值域**(车5 §8 C-1 裁定,与车4 F34 同向):
5
- * - {@link DENY_REASONS} —— `DENIED` 终态的归因。v1 是**单员闭集**:`routing_failure_fail_closed`。
6
- * - {@link VOID_REASONS} —— `VOID` 终态的归因。
7
- *
8
- * 🔴 为什么必须分两组、禁混值域:§3.0 的对账约束②是「**超时永不产生 denial**」,而审计面唯一能证明这条
9
- * 的东西就是终态 + 归因的组合。人拒恒为 `DECIDED(decision=deny)`(车4 §8),路由失败恒为
10
- * `DENIED(routing_failure_fail_closed)`,撤卡/取消/遗孤恒为 `VOID(<VOID_REASONS 之一>)`——三者在
11
- * wire 与审计上全程可分。若两组共用一个值域(或某个值同时可落两种终态),「这条 deny 是人做的、还是
12
- * 路由失败、还是超时兜底」就再也无法从行上读出来,归因诚实性(§6.1)当场失效。
13
- *
14
- * 落列面:两组值都写进 `approval_ask.denied_reason`(VARCHAR(64))——列名沿用车1 的 DDL(本车不改列),
15
- * 但它承载的是**终态归因**而不只是「拒绝理由」。读者靠「值属于哪一组」判别是哪条臂写的,这正是两组
16
- * 值域不相交这条纪律的用处(`ALL_TERMINAL_REASONS` 的机器判据钉住不相交性)。
17
- *
18
- * 写点纪律(grep 钉):全仓 `DENIED` 只有**一个**写点 = 收敛器判据 2 的非取消臂(`approval-reconciler.ts`)。
19
- */
20
- /** `DENIED` 终态的归因闭集(v1 单员)。 */
21
1
  export const DENY_REASONS = {
22
- /**
23
- * 收敛器判据 2:出处 run 已到**非 suspended 的终局**(completed / blocked / 非取消的 failed)而始终
24
- * 没有出现与本 ask 匹配的 checkpoint ⇒ 这只 ask 的投递面永远不会再出现,fail-closed 收 `DENIED`。
25
- * 🔴 证据是**行的终局态**(journal 的肯定证据),不是「等够久了」——约束②因此成立。
26
- */
27
2
  ROUTING_FAILURE: "routing_failure_fail_closed",
28
3
  };
29
- /** `VOID` 终态的归因闭集。 */
30
4
  export const VOID_REASONS = {
31
- /** 判据 2 的取消分臂(§9 C3):run `failed ∧ errorCode==="cancelled"` ⇒ 取消不是路由失败,收 VOID。 */
32
5
  RUN_CANCELLED: "run_cancelled",
33
- /** 判据 2 的取消分臂之二:批行已 `ABORTED`(取消腿打过 `abortBatch`)⇒ 同 abort 语义。 */
34
6
  BATCH_ABORTED: "batch_aborted",
35
- /**
36
- * bind-once 的**落选者**(codex 交叉复审 round2 R2-3,2026-08-06 验真):批已 `ROUTING_BOUND` 且
37
- * `bound_ask_id` 是**别人**——本行永远赢不了 `bindBatch`(谓词要求 `state='ROUTING_UNBOUND'`),
38
- * 却又不是路由失败(投递面好好的,只是这个决策点选了兄弟)。172 §3.0 的 bind-once 字面就是「非中选
39
- * 兄弟收敛 VOID」;`bindBatch` 的原子事务只连坐它当时看得见的 PARKING 兄弟,**之后**才进入 PARKING 的
40
- * 迟到行(late `ensureAsk` + `expireAsk` 不查批态,设计如此)由收敛器补判——就是这条臂。
41
- * 不这么判的后果:该行滞留到 run 终局,然后被判据 2 误报成 `routing_failure_fail_closed`(归因失真)。
42
- */
43
7
  BATCH_BOUND_ELSEWHERE: "batch_bound_elsewhere",
44
- /** 判据 4:adhoc 腿(`sessionId === taskId` ∧ `getRun` 无行)窗过 + 宽限 —— 该腿形**结构上**没有
45
- * durable 对账域(一次性 taskId 不进 runStore,park 目的地不可寻址),流死即 abort 语义。
46
- * 🔴 落 VOID 不落 DENIED:VOID 不是 denial,约束②不被触碰。 */
47
8
  ADHOC_LEG_NO_DURABLE_DOMAIN: "adhoc_leg_no_durable_domain",
48
- /** 判据 5:任何在 `STREAM_APPROVAL_ORPHAN_TTL_MS` 内仍未收敛的 `PARKING` 行(量 immutable 的
49
- * `createdAtMs`,§9 C5)—— 约束①(遗孤最终可判)的最后兜底,带一条 warn。 */
50
9
  ORPHAN_TTL_EXCEEDED: "orphan_ttl_exceeded",
51
10
  };
52
- /** 两组的并集(机器判据用:值域不相交 + 列宽 ≤ 64 的钉子在 test/approval-reconciler.test.ts)。 */
53
11
  export const ALL_TERMINAL_REASONS = [...Object.values(DENY_REASONS), ...Object.values(VOID_REASONS)];
54
12
  //# sourceMappingURL=approval-deny-reasons.js.map
@@ -1,43 +1,8 @@
1
- /**
2
- * design/80 D-G: approval-envelope HMAC (INTEGRITY, not identity). Split out of `security.ts` (design/158
3
- * A13; consumers import from HERE — [2354] cleared the security.ts compat re-exports; verifyDirectDoorProof in
4
- * ./principal-jwt.ts imports {@link verifyApprovalHmac} from here for the combined proof).
5
- *
6
- * ✅ WIRED into the resolve path (was "🔴 SCAFFOLD — NOT YET WIRED" — that claim went STALE; a 2026-07-25 doc-rot
7
- * sweep traced the real call chain: {@link verifyApprovalHmac} ← `verifyDirectDoorProof` ← * routes at `http/server.ts`(resumeCheckpoint 链;A9 拆分后行级坐标弃用,文件级指路+grep verifyDirectDoorProof 取证). So "not wired" was wrong; "not turned on" is right, and turning it
8
- * on is a DEPLOYMENT + KEY-PROVISIONING task (sema-registry mints/rotates the set), not a coding task.
9
- *
10
- * FULL activation chain (an independent 2026-07-25 cross-check caught the first draft of this note naming only the
11
- * leaf conditions — record it completely so a deployment engineer isn't surprised): the crypto branch lives ONLY
12
- * under the durable-checkpoint /decide path, whose route block is gated on `deps.checkpointStore`
13
- * (A9 拆分后行级坐标弃用:grep `deps.checkpointStore` in `http/server.ts` resumeCheckpoint 链), which needs `backend?.checkpoint && config.durableApproval` (A10 后住 `src/boot/stores.ts`/装配表:grep `durableApproval` in main.ts+boot/). That is NOT
14
- * an extra thing to remember, because `directDoorActive` ALREADY requires `DURABLE_APPROVAL=true` by construction
15
- * (config.ts approval 域,grep `directDoorActive` — and see the 🔴 note above it: without it, checkpointStore is undefined, this branch is
16
- * skipped, and the LEGACY trusted-header /decide becomes live = a full crypto bypass). So `directDoorActive` really
17
- * is the single load-bearing switch — it just transitively implies the durable store, rather than being independent
18
- * of it. A half-configured door refuses to boot (config.ts 同域 boot 不变量,grep `half-configured`/`门 D-G`), so "looks on, verifies nothing" is impossible.
19
- * This is the crypto FOUNDATION for D-G (the direct-connect approval door): it proves the decision envelope
20
- * `{checkpointToken, boundCallId, boundInputHash, decision}` was not tampered in transit. It is INTEGRITY, NOT
21
- * identity — `isOperator(principal)` stays the identity gate (a valid MAC says "the bytes are intact", never
22
- * "this is the operator").
23
- * The KEY-SET shape here is PROVISIONAL — sema-registry owns the final secret format; only parseApprovalHmacKeys
24
- * changes if it differs. Replay protection is the checkpointToken's SINGLE-USE nature (a replayed MAC names an
25
- * already-resolved token → the resolve CAS fails already_resolved), so no separate replay cache is needed.
26
- */
27
1
  import { createHmac, timingSafeEqual } from "node:crypto";
28
2
  export function approvalHmacMessage(env) {
29
3
  return JSON.stringify([env.sessionId, env.boundCallId ?? null, env.boundInputHash ?? null, env.decision, env.reason ?? null]);
30
4
  }
31
- /** 签名载荷里 `reason` 的字符上限。**拒**而不是截 —— 截断在这条路径上**结构上不可行**:服务端截过的字节
32
- * 与签名方签的字节必然不同,MAC 恒不匹配,"截断"只会把一个可诊断的 413 变成一个费解的 401。
33
- * 取 4096 与本仓既有的人写文本上限同档(`MAX_ELICIT_MESSAGE_CHARS` / `MAX_AGENT_TEXT_CHARS`)。 */
34
5
  export const MAX_APPROVAL_REASON_CHARS = 4_096;
35
- /**
36
- * design/80 D-G: verify the INTEGRITY of an approval-decision envelope. Returns true iff `mac` (hex) equals
37
- * HMAC-SHA256(canonical-envelope) under ANY key in the set — the `kid`-hinted key first, then the rest (so a MAC
38
- * signed with a key being rotated out still verifies during the overlap). `timingSafeEqual` avoids a timing
39
- * oracle. An empty key-set ⇒ false (caller decides whether absence = skip-because-inactive or fail-closed).
40
- */
41
6
  export function verifyApprovalHmac(env, mac, kid, keys) {
42
7
  if (keys.length === 0)
43
8
  return false;