@sema-agent/server 7.141.0 → 7.143.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (341) hide show
  1. package/MIGRATION.md +657 -0
  2. package/USAGE.md +102 -30
  3. package/dist/adoption/plan.js +22 -0
  4. package/dist/approval-ask-audit-store.d.ts +61 -43
  5. package/dist/approval-ask-audit-store.js +69 -44
  6. package/dist/approval-ask-machine.d.ts +15 -7
  7. package/dist/approval-ask-machine.js +10 -4
  8. package/dist/approval-card.d.ts +126 -27
  9. package/dist/approval-card.js +103 -23
  10. package/dist/approval-crash-convergence.d.ts +17 -7
  11. package/dist/approval-crash-convergence.js +94 -22
  12. package/dist/approval-deny-reasons.d.ts +34 -30
  13. package/dist/approval-deny-reasons.js +21 -6
  14. package/dist/approval-policy-settlement.d.ts +1 -1
  15. package/dist/approval-queue-kind.d.ts +27 -0
  16. package/dist/approval-queue-kind.js +6 -0
  17. package/dist/approval-reconciler.d.ts +41 -9
  18. package/dist/approval-reconciler.js +45 -42
  19. package/dist/audit.js +4 -2
  20. package/dist/auth-bridge.d.ts +9 -6
  21. package/dist/auth-bridge.js +9 -3
  22. package/dist/boot/adoption.js +2 -1
  23. package/dist/boot/budget-tracing.d.ts +17 -1
  24. package/dist/boot/budget-tracing.js +10 -6
  25. package/dist/boot/device-lane.d.ts +1 -1
  26. package/dist/boot/execution-env.d.ts +24 -4
  27. package/dist/boot/execution-env.js +26 -23
  28. package/dist/boot/execution-lane.js +2 -1
  29. package/dist/boot/exit-boundary.js +5 -4
  30. package/dist/boot/governance-seams.d.ts +26 -41
  31. package/dist/boot/governance-seams.js +61 -28
  32. package/dist/boot/late-binds.d.ts +7 -4
  33. package/dist/boot/late-binds.js +3 -1
  34. package/dist/boot/leader.js +2 -4
  35. package/dist/boot/memory-consolidation.d.ts +0 -8
  36. package/dist/boot/memory-consolidation.js +5 -4
  37. package/dist/boot/parked-revive-gate.js +2 -1
  38. package/dist/boot/permission-rules-audit.d.ts +0 -28
  39. package/dist/boot/permission-rules-audit.js +2 -1
  40. package/dist/boot/read-deny-anchor-audit.js +2 -1
  41. package/dist/boot/reapers.js +9 -7
  42. package/dist/boot/resolve-spec.d.ts +1 -1
  43. package/dist/boot/resolve-spec.js +19 -10
  44. package/dist/boot/retention-lane.js +5 -3
  45. package/dist/boot/runner-deps.d.ts +10 -7
  46. package/dist/boot/runner-deps.js +20 -29
  47. package/dist/boot/runtime-caps.js +3 -2
  48. package/dist/boot/session-faces.js +4 -3
  49. package/dist/boot/shutdown.d.ts +9 -0
  50. package/dist/boot/shutdown.js +19 -4
  51. package/dist/boot/stage-02-web-admin-pull.js +3 -0
  52. package/dist/boot/stage-03-store-backend.d.ts +4 -1
  53. package/dist/boot/stage-03-store-backend.js +1 -2
  54. package/dist/boot/stage-04-stores.d.ts +5 -2
  55. package/dist/boot/stage-04-stores.js +6 -7
  56. package/dist/boot/stage-05-execution-env.d.ts +6 -3
  57. package/dist/boot/stage-05-execution-env.js +8 -6
  58. package/dist/boot/stage-06-runners.d.ts +6 -3
  59. package/dist/boot/stage-06-runners.js +3 -4
  60. package/dist/boot/stage-07-capability-layer.d.ts +6 -3
  61. package/dist/boot/stage-07-capability-layer.js +7 -2
  62. package/dist/boot/stage-08-reapers.d.ts +6 -3
  63. package/dist/boot/stage-08-reapers.js +3 -2
  64. package/dist/boot/stage-09-leader.d.ts +6 -3
  65. package/dist/boot/stage-10-http-server.d.ts +6 -3
  66. package/dist/boot/stage-10-http-server.js +14 -5
  67. package/dist/boot/stores.d.ts +5 -2
  68. package/dist/boot/stores.js +10 -9
  69. package/dist/boot/web-admin.d.ts +5 -1
  70. package/dist/boot/web-admin.js +48 -36
  71. package/dist/boot/webfetch-summarize-lane.js +2 -1
  72. package/dist/boot/workflow-orchestration.js +3 -2
  73. package/dist/capabilities/builtin-tools.d.ts +2 -1
  74. package/dist/capabilities/builtin-tools.js +2 -4
  75. package/dist/capabilities/composite-members.d.ts +2 -1
  76. package/dist/capabilities/composite-members.js +2 -1
  77. package/dist/capabilities/hands-lane.d.ts +9 -39
  78. package/dist/capabilities/hands-lane.js +8 -13
  79. package/dist/capabilities/mcp-probe.d.ts +3 -2
  80. package/dist/capabilities/scenarios.d.ts +11 -1
  81. package/dist/capabilities/scenarios.js +1 -0
  82. package/dist/capabilities/skills.d.ts +1 -17
  83. package/dist/capabilities/skills.js +0 -8
  84. package/dist/capability-facts.d.ts +31 -7
  85. package/dist/capability-facts.js +27 -4
  86. package/dist/card-void.d.ts +6 -9
  87. package/dist/config-catalog.js +2 -1
  88. package/dist/config-dir-trust.js +58 -29
  89. package/dist/config-invariants.d.ts +7 -1
  90. package/dist/config-invariants.js +7 -0
  91. package/dist/config-provider.d.ts +10 -0
  92. package/dist/config-provider.js +15 -4
  93. package/dist/config-root-migration.d.ts +13 -0
  94. package/dist/config-root-migration.js +99 -0
  95. package/dist/config-types.d.ts +19 -8
  96. package/dist/config.d.ts +10 -3
  97. package/dist/config.js +18 -4
  98. package/dist/delegated-session-owner.d.ts +5 -3
  99. package/dist/delegated-session-owner.js +1 -1
  100. package/dist/device-ws-protocol.d.ts +1 -0
  101. package/dist/device-ws-protocol.js +2 -1
  102. package/dist/env-model-table-gate.d.ts +53 -0
  103. package/dist/env-model-table-gate.js +53 -0
  104. package/dist/execution-lane-caps.d.ts +2 -1
  105. package/dist/fleet/fleet-bus.d.ts +34 -55
  106. package/dist/fleet/fleet-bus.js +35 -100
  107. package/dist/fleet/fleet-reconciler.d.ts +4 -4
  108. package/dist/fleet/fleet-reconciler.js +1 -1
  109. package/dist/fleet/subagent-tail-bus.d.ts +14 -11
  110. package/dist/fleet/subagent-tail-bus.js +15 -28
  111. package/dist/hooks/hook-runner.d.ts +6 -0
  112. package/dist/hooks/hook-runner.js +58 -8
  113. package/dist/host-decision.d.ts +54 -20
  114. package/dist/host-decision.js +42 -14
  115. package/dist/http/admission.d.ts +1 -1
  116. package/dist/http/admission.js +7 -3
  117. package/dist/http/approval-sweeps.js +11 -8
  118. package/dist/http/caller-trust.d.ts +1 -1
  119. package/dist/http/dispatch.js +4 -2
  120. package/dist/http/durable-run-leg.js +2 -2
  121. package/dist/http/engine-refusal.d.ts +16 -0
  122. package/dist/http/engine-refusal.js +17 -0
  123. package/dist/http/execution-context.d.ts +2 -2
  124. package/dist/http/idempotency.d.ts +1 -1
  125. package/dist/http/memory-compliance-failure-reply.d.ts +1 -3
  126. package/dist/http/memory-compliance-failure-reply.js +4 -6
  127. package/dist/http/owner-access.d.ts +11 -0
  128. package/dist/http/owner-access.js +13 -0
  129. package/dist/http/principal-gate.d.ts +3 -1
  130. package/dist/http/principal-gate.js +17 -8
  131. package/dist/http/resume-legs.d.ts +21 -7
  132. package/dist/http/resume-legs.js +76 -58
  133. package/dist/http/resume-restatement.d.ts +39 -0
  134. package/dist/http/resume-restatement.js +25 -0
  135. package/dist/http/route-ctx.d.ts +35 -10
  136. package/dist/http/route-ctx.js +1 -1
  137. package/dist/http/route-table.js +2 -0
  138. package/dist/http/routes/approvals-assistant.d.ts +9 -1
  139. package/dist/http/routes/approvals-assistant.js +142 -31
  140. package/dist/http/routes/attachments.js +18 -0
  141. package/dist/http/routes/capabilities.js +16 -4
  142. package/dist/http/routes/memory-policy.d.ts +2 -2
  143. package/dist/http/routes/memory-policy.js +27 -7
  144. package/dist/http/routes/notify-wake.js +23 -25
  145. package/dist/http/routes/rules.js +48 -13
  146. package/dist/http/routes/runs.js +12 -11
  147. package/dist/http/routes/session-memory-sync.d.ts +91 -0
  148. package/dist/http/routes/session-memory-sync.js +174 -0
  149. package/dist/http/routes/session-sync.js +13 -20
  150. package/dist/http/routes/sessions-list.d.ts +2 -1
  151. package/dist/http/routes/sessions.d.ts +2 -2
  152. package/dist/http/routes/sessions.js +22 -23
  153. package/dist/http/routes/tasks.js +34 -54
  154. package/dist/http/run-read-face.d.ts +7 -0
  155. package/dist/http/run-read-face.js +9 -3
  156. package/dist/http/send.d.ts +5 -0
  157. package/dist/http/send.js +9 -2
  158. package/dist/http/server.d.ts +27 -15
  159. package/dist/http/server.js +4 -6
  160. package/dist/http/stored-body-replay.js +15 -4
  161. package/dist/http/submit-replay.d.ts +4 -3
  162. package/dist/http/submit-replay.js +2 -2
  163. package/dist/http/sync-submit-body.d.ts +21 -4
  164. package/dist/http/sync-submit-body.js +13 -0
  165. package/dist/http/wire-types.d.ts +17 -7
  166. package/dist/identity-source.d.ts +21 -2
  167. package/dist/identity-source.js +6 -0
  168. package/dist/leader/leader.js +5 -2
  169. package/dist/leader/wire.d.ts +7 -1
  170. package/dist/leader/wire.js +6 -5
  171. package/dist/leg-facts.d.ts +37 -0
  172. package/dist/leg-facts.js +15 -0
  173. package/dist/memory-bundle-engine.d.ts +1 -4
  174. package/dist/memory-bundle-engine.js +2 -2
  175. package/dist/memory-incident-sink.d.ts +19 -6
  176. package/dist/memory-incident-sink.js +8 -4
  177. package/dist/memory-operator-faces.d.ts +30 -66
  178. package/dist/memory-operator-faces.js +17 -88
  179. package/dist/memory-scope-stores.d.ts +38 -40
  180. package/dist/memory-scope-stores.js +51 -36
  181. package/dist/memory-scope.d.ts +0 -9
  182. package/dist/memory-scope.js +0 -9
  183. package/dist/memory-sync-client.d.ts +21 -21
  184. package/dist/memory-sync-client.js +62 -43
  185. package/dist/memory-sync-identity.d.ts +24 -0
  186. package/dist/memory-sync-identity.js +57 -0
  187. package/dist/memory-sync.d.ts +27 -5
  188. package/dist/memory-sync.js +29 -7
  189. package/dist/model-plane-face.d.ts +13 -7
  190. package/dist/model-plane-face.js +2 -1
  191. package/dist/observability/fail-open.d.ts +47 -14
  192. package/dist/observability/fail-open.js +37 -11
  193. package/dist/observability/run-terminal.d.ts +92 -34
  194. package/dist/observability/run-terminal.js +171 -162
  195. package/dist/orchestration/workflow-completion-inbox.d.ts +5 -19
  196. package/dist/orchestration/workflow-completion-inbox.js +0 -12
  197. package/dist/orchestration/workflow-notify-journal.d.ts +3 -2
  198. package/dist/orchestration/workflow-park-decision.d.ts +8 -3
  199. package/dist/orchestration/workflow-park-decision.js +5 -3
  200. package/dist/org-delivery-gate.d.ts +11 -0
  201. package/dist/org-delivery-gate.js +19 -1
  202. package/dist/parked-decide.d.ts +13 -9
  203. package/dist/parked-decide.js +7 -7
  204. package/dist/parked-revive-surface.js +1 -1
  205. package/dist/peer-inbound-face.d.ts +127 -0
  206. package/dist/peer-inbound-face.js +158 -0
  207. package/dist/permission-rule-list.d.ts +13 -9
  208. package/dist/permission-rule-list.js +7 -3
  209. package/dist/plugins/approval-ask-store-file.d.ts +14 -4
  210. package/dist/plugins/approval-ask-store-file.js +64 -11
  211. package/dist/plugins/approval-ask-store-memory.d.ts +2 -1
  212. package/dist/plugins/approval-ask-store-memory.js +17 -14
  213. package/dist/plugins/approval-ask-store-sql.d.ts +70 -23
  214. package/dist/plugins/approval-ask-store-sql.js +79 -30
  215. package/dist/plugins/checkpoint-store-sql.d.ts +4 -4
  216. package/dist/plugins/checkpoint-store-sql.js +9 -7
  217. package/dist/plugins/file-history-store-sql.d.ts +24 -18
  218. package/dist/plugins/file-history-store-sql.js +24 -19
  219. package/dist/plugins/file-run-store.d.ts +35 -1
  220. package/dist/plugins/file-run-store.js +108 -9
  221. package/dist/plugins/imported-layer-root-retirement.d.ts +82 -0
  222. package/dist/plugins/imported-layer-root-retirement.js +96 -0
  223. package/dist/plugins/local-checkpoint-store.d.ts +1 -1
  224. package/dist/plugins/local-checkpoint-store.js +2 -2
  225. package/dist/plugins/local-session-store.d.ts +42 -9
  226. package/dist/plugins/local-session-store.js +83 -33
  227. package/dist/plugins/local-task-attachment-store.d.ts +12 -2
  228. package/dist/plugins/local-task-attachment-store.js +67 -43
  229. package/dist/plugins/mailbox-recipient-lifecycle.d.ts +9 -31
  230. package/dist/plugins/mailbox-recipient-lifecycle.js +4 -0
  231. package/dist/plugins/mailbox-store-sql.d.ts +12 -8
  232. package/dist/plugins/mailbox-store-sql.js +76 -7
  233. package/dist/plugins/memory-key-guards.d.ts +3 -3
  234. package/dist/plugins/memory-run-store.d.ts +6 -1
  235. package/dist/plugins/memory-run-store.js +11 -1
  236. package/dist/plugins/memory-sync-store-pg.d.ts +15 -0
  237. package/dist/plugins/memory-sync-store-pg.js +21 -0
  238. package/dist/plugins/memory-sync-store-tidb.d.ts +2 -0
  239. package/dist/plugins/memory-sync-store-tidb.js +13 -1
  240. package/dist/plugins/one-time-migrations.d.ts +58 -2
  241. package/dist/plugins/one-time-migrations.js +161 -4
  242. package/dist/plugins/org-delivery-quota-store.d.ts +1 -1
  243. package/dist/plugins/org-delivery-quota-store.js +2 -12
  244. package/dist/plugins/park-commit-watch.d.ts +1 -1
  245. package/dist/plugins/permission-rule-store-file.d.ts +10 -1
  246. package/dist/plugins/permission-rule-store-file.js +15 -2
  247. package/dist/plugins/permission-rule-store-sql.d.ts +31 -1
  248. package/dist/plugins/permission-rule-store-sql.js +35 -2
  249. package/dist/plugins/remote-env-device.d.ts +8 -11
  250. package/dist/plugins/remote-env-device.js +6 -3
  251. package/dist/plugins/remote-env-e2b.d.ts +23 -4
  252. package/dist/plugins/remote-env-e2b.js +111 -28
  253. package/dist/plugins/remote-env-k8s.d.ts +61 -22
  254. package/dist/plugins/remote-env-k8s.js +133 -33
  255. package/dist/plugins/remote-env-local-docker.d.ts +15 -3
  256. package/dist/plugins/remote-env-local-docker.js +40 -12
  257. package/dist/plugins/retention-store-sql.d.ts +2 -2
  258. package/dist/plugins/run-store-sql.d.ts +9 -1
  259. package/dist/plugins/run-store-sql.js +9 -1
  260. package/dist/plugins/sandbox-read-floor.d.ts +5 -7
  261. package/dist/plugins/sandbox-read-floor.js +2 -7
  262. package/dist/plugins/sql-errors.d.ts +10 -1
  263. package/dist/plugins/sql-errors.js +13 -0
  264. package/dist/plugins/store-backend.d.ts +33 -35
  265. package/dist/plugins/store-backend.js +10 -1
  266. package/dist/plugins/store-contracts.d.ts +50 -1
  267. package/dist/plugins/store-contracts.js +6 -0
  268. package/dist/plugins/task-attachment-store.d.ts +112 -14
  269. package/dist/plugins/task-attachment-store.js +207 -29
  270. package/dist/plugins/tool-result-store-sql.d.ts +9 -8
  271. package/dist/plugins/tool-result-store-sql.js +23 -2
  272. package/dist/plugins/wiring-section-codec.d.ts +32 -0
  273. package/dist/plugins/wiring-section-codec.js +55 -0
  274. package/dist/plugins/workspace-fill.d.ts +60 -0
  275. package/dist/plugins/workspace-fill.js +106 -0
  276. package/dist/project-bundle.d.ts +38 -0
  277. package/dist/project-bundle.js +609 -0
  278. package/dist/rule-entry-list.d.ts +3 -2
  279. package/dist/rule-entry-list.js +5 -1
  280. package/dist/rules-consent.d.ts +28 -5
  281. package/dist/rules-consent.js +40 -28
  282. package/dist/run-local.js +11 -13
  283. package/dist/runs.d.ts +12 -8
  284. package/dist/runs.js +6 -8
  285. package/dist/runtime-governance.d.ts +1 -1
  286. package/dist/sandbox-egress-policy.d.ts +4 -1
  287. package/dist/sandbox-egress-policy.js +3 -1
  288. package/dist/sandbox-git-plane.d.ts +31 -12
  289. package/dist/sandbox-git-plane.js +24 -4
  290. package/dist/security.d.ts +9 -0
  291. package/dist/security.js +8 -0
  292. package/dist/server-secret-env.d.ts +4 -0
  293. package/dist/session-error-wire.d.ts +36 -0
  294. package/dist/session-error-wire.js +13 -0
  295. package/dist/session-incarnation.d.ts +1 -1
  296. package/dist/session-notify-door.d.ts +106 -0
  297. package/dist/session-notify-door.js +93 -0
  298. package/dist/session-sync.d.ts +9 -14
  299. package/dist/session-sync.js +5 -5
  300. package/dist/session-workspace.d.ts +3 -13
  301. package/dist/session-workspace.js +0 -9
  302. package/dist/standing-answer-grant.d.ts +5 -0
  303. package/dist/standing-answer-grant.js +10 -0
  304. package/dist/stores-sql/session-policy-store-sql.d.ts +4 -2
  305. package/dist/stores-sql/session-policy-store-sql.js +3 -13
  306. package/dist/task-mcp.d.ts +1 -1
  307. package/dist/task-settings.d.ts +21 -10
  308. package/dist/task-settings.js +46 -13
  309. package/dist/terminal.d.ts +39 -19
  310. package/dist/terminal.js +65 -22
  311. package/dist/tool-approval.d.ts +56 -37
  312. package/dist/tool-approval.js +78 -71
  313. package/dist/trace/core-keyset-guard.d.ts +134 -14
  314. package/dist/trace/engine-notice-wire.d.ts +9 -3
  315. package/dist/trace/engine-notice-wire.js +7 -3
  316. package/dist/trace/exit-seat.d.ts +9 -2
  317. package/dist/trace/exit-seat.js +1 -0
  318. package/dist/trace/ledger-sink.d.ts +10 -6
  319. package/dist/trace/ledger-sink.js +2 -2
  320. package/dist/trace/model-segment.d.ts +55 -25
  321. package/dist/trace/model-segment.js +101 -33
  322. package/dist/trace/project.d.ts +25 -5
  323. package/dist/trace/project.js +22 -0
  324. package/dist/trace/redact.d.ts +61 -9
  325. package/dist/trace/redact.js +1490 -468
  326. package/dist/trace/self-minted-wire.d.ts +745 -24
  327. package/dist/trace/self-minted-wire.js +239 -9
  328. package/dist/trace/wire-projection-faces.d.ts +2 -2
  329. package/dist/trace/wire-projection-faces.js +2 -2
  330. package/dist/verifier-pause-card.d.ts +33 -5
  331. package/dist/verifier-pause-card.js +15 -10
  332. package/dist/web-admin-client/apply-effective.d.ts +12 -16
  333. package/dist/web-admin-client/apply-effective.js +109 -9
  334. package/dist/web-admin-client/effective-keys.js +2 -0
  335. package/dist/web-admin-client/types.d.ts +4 -0
  336. package/dist/workspace-space.d.ts +19 -3
  337. package/dist/workspace-space.js +8 -4
  338. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -1
  339. package/package.json +3 -2
  340. package/wire-changes/server@7.142.0.json +639 -0
  341. package/wire-changes/server@7.143.0.json +1167 -0
@@ -126,6 +126,10 @@ export declare class FileApprovalAskStore implements ApprovalAskStore {
126
126
  private replayRejectedCount;
127
127
  /** 重放期被丢弃的坏行数(撕尾不计:那是本格式存在的理由,由 {@link corruptQuarantinePath} 单独说)。 */
128
128
  private corruptLineCount;
129
+ /** 本次启动重放到的 v1 信封记录数(随 M34;{@link upgradeJournalEnvelope} 的读数)。 */
130
+ private journalV1Records;
131
+ /** 盘上还有 v1 信封记录(随 M34):重放到 v1 记录时置位,任何一次压实(整本重写成 v2)清位 —— 已被压实过的账本不再为升级重写第二次。 */
132
+ private journalV1OnDisk;
129
133
  /** 本次启动隔离出去的原件路径(无坏字节 ⇒ `undefined`)。 */
130
134
  readonly corruptQuarantinePath: string | undefined;
131
135
  /**
@@ -179,8 +183,8 @@ export declare class FileApprovalAskStore implements ApprovalAskStore {
179
183
  * 🔴 [ref] —— **出域的一次决议不落账本**。作用域的属主维(`owner`,判据 {@link askRowCarriesOwner})不在记录上:行此刻不带着门判过的那位属主
180
184
  * ⇒ 内核上这一笔必输、零写(读钟也在判定之后,不开账本窗)⇒ 没有要重放的东西,直调内核拿它的输者答案(与留存两动词「空的一次不落」同一条先例)。
181
185
  * 判定、落账、内核调用都在同一个同步前缀里,其间行不可能换属主。于是**落了账的每一条决议都在域内**,重放按行自己的属主喂属主维
182
- * ({@link FileApprovalAskStore.replayOp})—— 记录形一个字节不变:升级前写下的账本、升级后写下的账本、降级回老版本时老读者读新账本,三者同读,
183
- * 零迁移、零双读。
186
+ * ({@link FileApprovalAskStore.replayOp})—— 决议记录的形一个字节不变(没有属主格):升级前写下的决议记录与升级后写下的同读,零迁移、零双读
187
+ * (整本账的信封号另算 —— 见顶注「信封版本」)。
184
188
  */
185
189
  decideAsk(askId: string, batchId: string, owner: string | null, decision: DecideAskInput): Promise<DecideResult>;
186
190
  claimTerminal(askId: string, batchId: string, intent: AskTerminalClaimIntent, opts?: AskTerminalClaimOpts): Promise<AskTerminalClaimOutcome>;
@@ -208,14 +212,20 @@ export declare class FileApprovalAskStore implements ApprovalAskStore {
208
212
  corruptLines: number;
209
213
  records: number;
210
214
  };
211
- /** 释放追加 fd(`LocalBackend.close` → 优雅重启重开)。不包 try/catch 的理由同
212
- * {@link FileApprovalNonceStore.dispose}:core 的 `AppendLog.close()` 自己就是全函数。 */
215
+ /**
216
+ * 问行成因两列的 local 腿(一次性迁移 M34,窗口见 `plugins/one-time-migrations.ts` 登记表;`LocalBackend.runOneTimeMigrations` 调):本次启动重放到的 v1 信封记录
217
+ * 已由读侧半场按意义译进内存({@link translateJournalV1Record}),这里**一次压实**把整本账重写成 v2 快照(压实 = 既有整本原子重写)。返回 v1 记录数;没有 ⇒ 零写、0。
218
+ * 本次启动里账本已被别的压实整本重写过(撕尾 / 坏行收拢、M32 的车道回填)⇒ 盘上已无 v1,不再重写,读数照报。
219
+ */
220
+ upgradeJournalEnvelope(): number;
213
221
  /**
214
222
  * 执行放置切换 ④b 的 local 腿(一次性迁移登记表 M32;`LocalBackend.runOneTimeMigrations` 在任何读者之前调):升级前写下的问(日志 `ensureAsk` 记录行 /
215
223
  * 压实快照行没有 `executionLane`)记成本部署车道表的唯一项,然后**一次压实**把整本账重写成带车道的快照(压实 = 既有整本原子重写)。
216
224
  * 没有缺车道的行 ⇒ 零写。车道表不是一元而还有缺车道的行 ⇒ 抛与 SQL 同一只拒(`backfill_ambiguous`,由调用方铸),一个字节没写。
217
225
  */
218
226
  backfillExecutionLanes(lanes: readonly ExecutionLane[], refuse: (pending: number) => never): number;
227
+ /** 释放追加 fd(`LocalBackend.close` → 优雅重启重开)。不包 try/catch 的理由同
228
+ * {@link FileApprovalNonceStore.dispose}:core 的 `AppendLog.close()` 自己就是全函数。 */
219
229
  dispose(): void;
220
230
  }
221
231
  export {};
@@ -30,6 +30,41 @@ const JOURNAL_OP_WORDS = new Set(JOURNAL_OPS);
30
30
  export const JOURNAL_OP_TABLE_GATE = true;
31
31
  const JOURNAL_V1_CLAIM_INTENTS = ["expire", "cancel"];
32
32
  const JOURNAL_V1_CLAIM_INTENT_WORDS = new Set(JOURNAL_V1_CLAIM_INTENTS);
33
+ const JOURNAL_ENVELOPE = 2;
34
+ const JOURNAL_V1_ENVELOPE = 1;
35
+ function askRowToDisk(row) {
36
+ const { sourceSessionId, ...rest } = row;
37
+ return { ...rest, sourceTaskId: sourceSessionId };
38
+ }
39
+ function askRowFromDisk(row, refuse) {
40
+ const sourceTaskId = row !== null && typeof row === "object" ? row.sourceTaskId : undefined;
41
+ if (typeof sourceTaskId !== "string")
42
+ return refuse("ask row without a string sourceTaskId (the on-disk name of the ask's source session)");
43
+ const { sourceTaskId: _onDisk, ...rest } = row;
44
+ return { ...rest, sourceSessionId: sourceTaskId };
45
+ }
46
+ const callToDisk = (call) => (call.op === "ensureAsk" ? { ...call, row: askRowToDisk(call.row) } : call);
47
+ const V1_ENDING_STATES = new Set(["VOID", "DENIED"]);
48
+ const V1_TRIGGER_STATES = new Set(["PARKING", "PARKED"]);
49
+ function translateJournalV1Record(raw, refuse) {
50
+ const rec = raw;
51
+ if (rec.k === "ask" && rec.row !== null && typeof rec.row === "object") {
52
+ const { deniedReason, ...row } = rec.row;
53
+ const cause = typeof deniedReason === "string" ? deniedReason : null;
54
+ if (cause !== null && row.state === "STREAM_PENDING") {
55
+ refuse(`an envelope-1 snapshot row in STREAM_PENDING carries the single cause field ${JSON.stringify(cause)} — no released server wrote that shape and it has no two-column reading`);
56
+ }
57
+ return { ...raw, row: { ...row, endingCause: V1_ENDING_STATES.has(row.state) ? cause : null, parkTrigger: V1_TRIGGER_STATES.has(row.state) ? cause : null } };
58
+ }
59
+ if (rec.k === "op" && (rec.op === "transitionAsk" || rec.op === "resolveProvisional") && rec.patch !== null && typeof rec.patch === "object" && "deniedReason" in rec.patch) {
60
+ const { deniedReason, ...patch } = rec.patch;
61
+ if (!V1_ENDING_STATES.has(rec.to) || (typeof deniedReason !== "string" && deniedReason !== null)) {
62
+ refuse(`an envelope-1 ${String(rec.op)} record sets the single cause field on a transition to ${JSON.stringify(rec.to)} with ${JSON.stringify(deniedReason)} — no released server wrote that shape and it has no two-column reading`);
63
+ }
64
+ return { ...raw, patch: { ...patch, endingCause: deniedReason } };
65
+ }
66
+ return raw;
67
+ }
33
68
  const ASK_JOURNAL_SHAPE = {
34
69
  what: "approval-ask journal record",
35
70
  read: (record) => record,
@@ -57,6 +92,8 @@ export class FileApprovalAskStore {
57
92
  pinnedNow = null;
58
93
  replayRejectedCount = 0;
59
94
  corruptLineCount = 0;
95
+ journalV1Records = 0;
96
+ journalV1OnDisk = false;
60
97
  corruptQuarantinePath;
61
98
  constructor(root) {
62
99
  const dir = join(root, APPROVAL_ASK_DIR);
@@ -95,9 +132,9 @@ export class FileApprovalAskStore {
95
132
  }
96
133
  write(call, run) {
97
134
  const t = Date.now();
98
- const record = { v: 1, k: "op", t, ...call };
99
- const encoded = encodeStoreJsonPayload(`${call.op} call`, record);
100
- this.logFor().append(encoded, true);
135
+ const encoded = encodeStoreJsonPayload(`${call.op} call`, { v: JOURNAL_ENVELOPE, k: "op", t, ...call });
136
+ const onDisk = { v: JOURNAL_ENVELOPE, k: "op", t, ...callToDisk(encoded) };
137
+ this.logFor().append(onDisk, true);
101
138
  this.records += 1;
102
139
  this.pinnedNow = t;
103
140
  let out;
@@ -125,22 +162,29 @@ export class FileApprovalAskStore {
125
162
  }
126
163
  compact(snapshot = this.core.exportRows()) {
127
164
  const lines = [
128
- ...snapshot.batches.map((row) => `${JSON.stringify({ v: 1, k: "batch", row })}\n`),
129
- ...snapshot.asks.map((row) => `${JSON.stringify({ v: 1, k: "ask", row })}\n`),
165
+ ...snapshot.batches.map((row) => `${JSON.stringify({ v: JOURNAL_ENVELOPE, k: "batch", row })}\n`),
166
+ ...snapshot.asks.map((row) => `${JSON.stringify({ v: JOURNAL_ENVELOPE, k: "ask", row: askRowToDisk(row) })}\n`),
130
167
  ];
131
168
  this.log?.closeForSwap();
132
169
  atomicWriteFile(this.tmpDir, this.path, lines.join(""));
170
+ this.journalV1OnDisk = false;
133
171
  this.records = lines.length;
134
172
  }
135
- replayRecord(raw) {
136
- const probe = raw;
173
+ replayRecord(disk) {
174
+ const head = disk;
137
175
  const refuse = (why) => {
138
176
  throw new StoreBootRefusalError(`FileApprovalAskStore: approval ask journal ${this.path} carries a record this build cannot replay — ${why} ` +
139
- `(v=${JSON.stringify(probe.v)} k=${JSON.stringify(probe.k)} op=${JSON.stringify(probe.op)}). ` +
177
+ `(v=${JSON.stringify(head.v)} k=${JSON.stringify(head.k)} op=${JSON.stringify(head.op)}). ` +
140
178
  "Replaying only the records it understands would resurrect decided asks or drop a human's approval. " +
141
179
  "Run the server version that wrote this journal, or move the file aside (every approval on it is abandoned) to continue.");
142
180
  };
143
- if (probe.v !== 1)
181
+ if (head.v === JOURNAL_V1_ENVELOPE) {
182
+ this.journalV1Records += 1;
183
+ this.journalV1OnDisk = true;
184
+ }
185
+ const raw = head.v === JOURNAL_V1_ENVELOPE ? translateJournalV1Record(disk, refuse) : disk;
186
+ const probe = raw;
187
+ if (probe.v !== JOURNAL_ENVELOPE && probe.v !== JOURNAL_V1_ENVELOPE)
144
188
  refuse("unknown envelope version");
145
189
  if (probe.k === "ask" || probe.k === "batch") {
146
190
  const row = probe.row;
@@ -150,7 +194,7 @@ export class FileApprovalAskStore {
150
194
  if (typeof key !== "string" || key.length === 0)
151
195
  refuse(`snapshot record without a ${probe.k === "ask" ? "askId" : "batchId"}`);
152
196
  if (probe.k === "ask")
153
- this.core.importRows({ asks: [row] });
197
+ this.core.importRows({ asks: [askRowFromDisk(row, refuse)] });
154
198
  else
155
199
  this.core.importRows({ batches: [row] });
156
200
  return;
@@ -168,7 +212,7 @@ export class FileApprovalAskStore {
168
212
  if (!Array.isArray(anchors) || !anchors.every((a) => typeof a === "string"))
169
213
  refuse("reapSettledAsks record without a string-array keepAnchors");
170
214
  }
171
- const rec = raw;
215
+ const rec = (probe.op === "ensureAsk" ? { ...raw, row: askRowFromDisk(probe.row, refuse) } : raw);
172
216
  this.pinnedNow = rec.t;
173
217
  try {
174
218
  void this.replayOp(rec).catch((e) => {
@@ -296,6 +340,15 @@ export class FileApprovalAskStore {
296
340
  replayStats() {
297
341
  return { rejected: this.replayRejectedCount, corruptLines: this.corruptLineCount, records: this.records };
298
342
  }
343
+ upgradeJournalEnvelope() {
344
+ const upgraded = this.journalV1Records;
345
+ if (upgraded === 0)
346
+ return 0;
347
+ if (this.journalV1OnDisk)
348
+ this.compact();
349
+ this.journalV1Records = 0;
350
+ return upgraded;
351
+ }
299
352
  backfillExecutionLanes(lanes, refuse) {
300
353
  const pending = this.core.exportRows().asks.filter((r) => !isExecutionLane(String(r.executionLane ?? ""))).length;
301
354
  if (pending === 0)
@@ -101,7 +101,8 @@ export declare class InMemoryApprovalAskStore implements ApprovalAskStore {
101
101
  * 单调性论证)。
102
102
  */
103
103
  claimTerminal(askId: string, batchId: string, intent: AskTerminalClaimIntent, opts?: AskTerminalClaimOpts): Promise<AskTerminalClaimOutcome>;
104
- /** `cause`(可选)= 转 park 的触发成因,赢下 PARKING 的同一步落进 `deniedReason`(SQL twin 同一条 UPDATE 的镜像;接口上的 `expireAsk` 不带它,只有 `claimTerminal(expire, { cause })` 带)。 */
104
+ /** `cause`(可选)= 转 park 的触发成因,赢下 PARKING 的同一步落进 `parkTrigger`(SQL twin 同一条 UPDATE 的镜像;接口上的 `expireAsk` 不带它,只有 `claimTerminal(expire, { cause })` 带)——
105
+ * `parkTrigger` 的唯一写点:此后的转移(补丁里没有这一键)不再碰它。 */
105
106
  expireAsk(askId: string, batchId: string, cause?: AskTerminalClaimOpts["cause"]): Promise<ExpireResult>;
106
107
  bindBatch(batchId: string, askId: string, gate: BindGateInput): Promise<BindResult>;
107
108
  deferReconcile(askId: string, expectedState: AskState, expectedRev: number, nowMs: number): Promise<boolean>;
@@ -1,7 +1,7 @@
1
1
  import { ASK_TERMINAL, canAskTransition } from "../approval-ask-machine.js";
2
2
  import { isExecutionLane } from "../execution-lane-caps.js";
3
3
  import { APPROVAL_ENDING_CAUSES } from "../approval-deny-reasons.js";
4
- import { askRowCarriesOwner, askRowOnCall, assertDecideSettledBy, assertIdempotencyKeyShape, assertIntegralEpochMs, assertReapBounds, ApprovalAskReadAbortedError, claimTargetState, POLICY_REFUSAL_DECISION, projectAskTerminalClaimTruth, projectDecideReading } from "./approval-ask-store-sql.js";
4
+ import { askRowCarriesOwner, askRowOnCall, assertDecideSource, assertIdempotencyKeyShape, assertIntegralEpochMs, assertReapBounds, ApprovalAskReadAbortedError, claimTargetState, POLICY_REFUSAL_DECISION, projectAskTerminalClaimTruth, projectDecideReading } from "./approval-ask-store-sql.js";
5
5
  function applyPatch(row, patch) {
6
6
  if (patch.decision !== undefined)
7
7
  row.decision = patch.decision;
@@ -11,8 +11,8 @@ function applyPatch(row, patch) {
11
11
  row.decisionNote = patch.decisionNote;
12
12
  if (patch.decidedAtMs !== undefined)
13
13
  row.decidedAtMs = patch.decidedAtMs;
14
- if (patch.deniedReason !== undefined)
15
- row.deniedReason = patch.deniedReason;
14
+ if (patch.endingCause !== undefined)
15
+ row.endingCause = patch.endingCause;
16
16
  if (patch.gateToken !== undefined)
17
17
  row.gateToken = patch.gateToken;
18
18
  if (patch.gateBoundCallId !== undefined)
@@ -58,7 +58,7 @@ export class InMemoryApprovalAskStore {
58
58
  }
59
59
  importRows(snapshot) {
60
60
  for (const r of snapshot.asks ?? [])
61
- this.asks.set(r.askId, { ...r, settledBy: r.settledBy ?? null });
61
+ this.asks.set(r.askId, { ...r, settledBy: r.settledBy ?? null, decidedVia: r.decidedVia ?? null });
62
62
  for (const b of snapshot.batches ?? [])
63
63
  this.batches.set(b.batchId, { ...b });
64
64
  }
@@ -82,7 +82,7 @@ export class InMemoryApprovalAskStore {
82
82
  const created = {
83
83
  askId: row.askId,
84
84
  taskId: row.taskId,
85
- sourceTaskId: row.sourceTaskId,
85
+ sourceSessionId: row.sourceSessionId,
86
86
  sessionId: row.sessionId,
87
87
  owner: row.owner ?? null,
88
88
  executionLane: row.executionLane,
@@ -98,8 +98,10 @@ export class InMemoryApprovalAskStore {
98
98
  decisionActor: null,
99
99
  decisionNote: null,
100
100
  settledBy: null,
101
+ decidedVia: null,
101
102
  decidedAtMs: null,
102
- deniedReason: null,
103
+ endingCause: null,
104
+ parkTrigger: null,
103
105
  gateToken: null,
104
106
  gateBoundCallId: null,
105
107
  gateBoundInputHash: null,
@@ -131,7 +133,7 @@ export class InMemoryApprovalAskStore {
131
133
  async decideAsk(askId, batchId, owner, decision) {
132
134
  if (decision.idempotencyKey != null)
133
135
  assertIdempotencyKeyShape(decision.idempotencyKey);
134
- assertDecideSettledBy(decision);
136
+ assertDecideSource(decision);
135
137
  const reading = (row) => projectDecideReading(row ? { ...row } : null, owner);
136
138
  const batch = this.batches.get(batchId);
137
139
  if (!batch || batch.state !== "OPEN") {
@@ -159,6 +161,7 @@ export class InMemoryApprovalAskStore {
159
161
  ask.decisionActor = decision.decisionActor ?? null;
160
162
  ask.decisionNote = decision.decisionNote ?? null;
161
163
  ask.settledBy = decision.settledBy ?? null;
164
+ ask.decidedVia = decision.decidedVia ?? null;
162
165
  if (decision.updatedInput === undefined)
163
166
  delete ask.updatedInput;
164
167
  else
@@ -181,13 +184,13 @@ export class InMemoryApprovalAskStore {
181
184
  const scoped = this.asks.get(askId);
182
185
  if (scoped !== undefined && scoped.batchId !== batchId)
183
186
  return { claimed: false, current: { state: "absent" } };
184
- const won = await this.transitionAsk(askId, "STREAM_PENDING", "VOID", { updatedAtMs: this.now(), ...(opts?.cause !== undefined ? { deniedReason: opts.cause } : {}) });
187
+ const won = await this.transitionAsk(askId, "STREAM_PENDING", "VOID", { updatedAtMs: this.now(), ...(opts?.cause !== undefined ? { endingCause: opts.cause } : {}) });
185
188
  if (won)
186
189
  return { claimed: true, voidedSiblings: [] };
187
190
  break;
188
191
  }
189
192
  case "refuse": {
190
- assertDecideSettledBy(POLICY_REFUSAL_DECISION);
193
+ assertDecideSource(POLICY_REFUSAL_DECISION);
191
194
  const to = claimTargetState(intent);
192
195
  if (!canAskTransition("STREAM_PENDING", to))
193
196
  throw new Error(`InMemoryApprovalAskStore.claimTerminal: illegal terminal claim STREAM_PENDING -> ${to} (askId=${askId})`);
@@ -222,7 +225,7 @@ export class InMemoryApprovalAskStore {
222
225
  return { won: false, voidedSiblings: [] };
223
226
  ask.state = "PARKING";
224
227
  if (cause !== undefined)
225
- ask.deniedReason = cause;
228
+ ask.parkTrigger = cause;
226
229
  ask.rev += 1;
227
230
  ask.updatedAtMs = now;
228
231
  if (batch.state === "OPEN") {
@@ -234,7 +237,7 @@ export class InMemoryApprovalAskStore {
234
237
  for (const [id, r] of this.asks) {
235
238
  if (r.batchId === batchId && r.state === "STREAM_PENDING" && id !== askId) {
236
239
  r.state = "VOID";
237
- r.deniedReason = APPROVAL_ENDING_CAUSES.BATCH_SIBLING_WITHDRAWN;
240
+ r.endingCause = APPROVAL_ENDING_CAUSES.BATCH_SIBLING_WITHDRAWN;
238
241
  r.rev += 1;
239
242
  r.updatedAtMs = now;
240
243
  voided.push(id);
@@ -267,7 +270,7 @@ export class InMemoryApprovalAskStore {
267
270
  for (const [id, r] of this.asks) {
268
271
  if (r.batchId === batchId && r.state === "PARKING" && id !== askId) {
269
272
  r.state = "VOID";
270
- r.deniedReason = APPROVAL_ENDING_CAUSES.BATCH_BOUND_ELSEWHERE;
273
+ r.endingCause = APPROVAL_ENDING_CAUSES.BATCH_BOUND_ELSEWHERE;
271
274
  r.rev += 1;
272
275
  r.updatedAtMs = now;
273
276
  voidedSiblings.push(id);
@@ -295,7 +298,7 @@ export class InMemoryApprovalAskStore {
295
298
  for (const [id, r] of this.asks) {
296
299
  if (r.batchId === batchId && r.state === "STREAM_PENDING") {
297
300
  r.state = "VOID";
298
- r.deniedReason = APPROVAL_ENDING_CAUSES.BATCH_ABORTED;
301
+ r.endingCause = APPROVAL_ENDING_CAUSES.BATCH_ABORTED;
299
302
  r.rev += 1;
300
303
  r.updatedAtMs = now;
301
304
  voided.push(id);
@@ -389,7 +392,7 @@ export class InMemoryApprovalAskStore {
389
392
  }
390
393
  planReapSettledAsks(cutoffMs, limit, keepAnchors) {
391
394
  assertReapBounds(cutoffMs, limit);
392
- const anchored = (r) => keepAnchors.has(r.taskId) || keepAnchors.has(r.sessionId) || keepAnchors.has(r.sourceTaskId);
395
+ const anchored = (r) => keepAnchors.has(r.taskId) || keepAnchors.has(r.sessionId) || keepAnchors.has(r.sourceSessionId);
393
396
  return [...this.asks.values()]
394
397
  .filter((r) => ASK_TERMINAL.has(r.state) && !r.provisional && r.updatedAtMs < cutoffMs && !anchored(r))
395
398
  .sort((a, b) => a.updatedAtMs - b.updatedAtMs || (a.askId < b.askId ? -1 : a.askId > b.askId ? 1 : 0))
@@ -42,14 +42,15 @@ import type { PgQueryFn } from "./pg-query.js";
42
42
  import { type SqlDriver, type SqlDialect } from "./sql-driver.js";
43
43
  import { type AskState, type BatchState } from "../approval-ask-machine.js";
44
44
  import type { HostSettledBy } from "../host-decision.js";
45
- import { type AskRowCause, type SettleCause } from "../approval-deny-reasons.js";
45
+ import { type DecidedVia } from "@sema-agent/core";
46
+ import { type AskRowEndingCause, type SettleCause, type ParkTriggerCause } from "../approval-deny-reasons.js";
46
47
  import { type IndexSpec } from "./ensure-index.js";
47
48
  import { type ExecutionLane } from "../execution-lane-caps.js";
48
49
  export type AskDecision = "approve" | "deny";
49
50
  export interface AskRow {
50
51
  askId: string;
51
52
  taskId: string;
52
- sourceTaskId: string;
53
+ sourceSessionId: string;
53
54
  sessionId: string;
54
55
  /** 租户 scope——哨兵归一在消费层(车2+),店里存原值(设计定稿 §3)。 */
55
56
  owner: string | null;
@@ -69,7 +70,7 @@ export interface AskRow {
69
70
  * 与下面 PARKED 坐标里的 `gateBoundInputHash` 是**两件不同的东西**,别混:
70
71
  * - `boundInputHash`(本列)= ask **铸行时**的入参摘要,一次写定永不改;对账收敛器判据 1 用它跟
71
72
  * checkpoint 行的同名列做**硬相等**——它是**身份三元组之外的第二道等式**,身份本身是
72
- * (`sourceTaskId`, `toolCallId`, 因果下界)三维([ref] 件1 换轴,判据属主 = `approval-reconciler.ts`
73
+ * (`sourceSessionId`, `toolCallId`, 因果下界)三维([ref] 件1 换轴,判据属主 = `approval-reconciler.ts`
73
74
  * 的 `classifyGateMatch`;本注上一版写的「identity 四元组」是换轴前的旧口径,`sessionId` 从来不是
74
75
  * 内存判据维)。任一侧缺席 ⇒ 判据 1 **不命中**;归因看走到哪一层:身份先判(候选集非空却没有同身份
75
76
  * 的一条 ⇒ `identity_miss`),身份这层还够得着时缺席才记 `single_mint`(禁「能取到时才比」的可选谓词
@@ -90,7 +91,7 @@ export interface AskRow {
90
91
  /**
91
92
  * 🔴 [ref]([ref] 的持久半场)—— 这条决议的**结算来源**:`null` = 一个人答的(= 缺席律,core 对
92
93
  * 不带这一格的 outcome 逐字按 `"human"` 读),非 null = 宿主自报的那个词([ref] 起写进来的只有决定体出处位
93
- * `decidedBy:"policy"` 经 `liveSettledBy` 映射来的 `"policy"`,allow 与 deny 两向都写;词表属主恒是 core —— 见 {@link HostSettledBy})。
94
+ * `decidedBy:"policy"` 经 `liveHostSource` 映射来的 `"policy"`,allow 与 deny 两向都写;词表属主恒是 core —— 见 {@link HostSettledBy})。
94
95
  *
95
96
  * **为什么必须是行上的一格**(病灶,7.93.3 合并树 codex ① 验真):`settledBy` 此前只到达**当次**回决的
96
97
  * 即时回调,行上不留痕 ⇒ 重放 / 再入 / 同 askId 的兄弟等待者 / 歧义提交恢复读回的是裸 `false`,
@@ -113,6 +114,14 @@ export interface AskRow {
113
114
  * 三态收成两态是刻意的:`"human"` 与缺席在 core 那边逐字同义,故店只存「非人」的那些词,`null` 即人。
114
115
  */
115
116
  settledBy: HostSettledBy | null;
117
+ /**
118
+ * core 7.61.0([ref])—— `settledBy = "policy"` 旁的**机器座位**:哪一个机器座位替这台部署答的(core `DECIDED_VIA`:`"policy"` 部署自己的策略 /
119
+ * `"permission_request_hook"` 一只 PermissionRequest 钩子)。`null` = 没陈述(旧行同读)= 部署自己的策略。它决定模型读哪一句拒句
120
+ * (「这一跑没有交互审批通道」vs「一只 PermissionRequest 钩子拒了」),所以与 {@link settledBy} 同一条理由住在行上、与决议同一条 UPDATE 落列:
121
+ * 活卡腿的结算输入只从行读,不在行上 = 重放 / 兄弟 / 再入 / 歧义恢复 / 迟到成功 / 跨副本广播六条路把钩子的拒读回成部署策略的拒。
122
+ * 只在 `settledBy = "policy"` 旁(core 同一条律;店门 {@link assertDecideSource} 守)。
123
+ */
124
+ decidedVia: DecidedVia | null;
116
125
  /**
117
126
  * [ref]([ref] / [ref] 收口):人批准时带的 **ctrl+g 编辑实参**(wire `updatedInput`),与决议**同一条
118
127
  * UPDATE** 原子落列 `updated_input`。语义三态,读法**严格**:
@@ -132,7 +141,16 @@ export interface AskRow {
132
141
  */
133
142
  updatedInput?: unknown;
134
143
  decidedAtMs: number | null;
135
- deniedReason: string | null;
144
+ /**
145
+ * `ending_cause` 列原值 —— **终态成因**:这一行为什么没被人答就结束了(到 `VOID` / `DENIED` 的转移带着它写:取消 claim、店内撤兄弟 / bind-once 落选 / 批中止、对账器、起服收敛;没带的转移 ⇒ 保持 `null`)。
146
+ * 只经 {@link askRowEndingCause} 读(词表 + 行不变式在那里);`null` = 没写归因。
147
+ */
148
+ endingCause: string | null;
149
+ /**
150
+ * `park_trigger` 列原值 —— **转 park 的触发成因**:这一行为什么被转进投递面。**唯一写者** = 赢下 `STREAM_PENDING → PARKING` 的那一次 `claimTerminal(expire, { cause })`,
151
+ * 此后不再被写(行走到 `PARKED` / `DENIED` / `VOID` 之后它仍在;{@link AskTransitionPatch} 没有这一键)。只经 {@link askRowParkTrigger} 读;`null` = 没写(不带 cause 的转 park)。
152
+ */
153
+ parkTrigger: string | null;
136
154
  /** PARKED 坐标三件。 */
137
155
  gateToken: string | null;
138
156
  gateBoundCallId: string | null;
@@ -177,7 +195,7 @@ export interface AskRow {
177
195
  export interface NewAskRow {
178
196
  askId: string;
179
197
  taskId: string;
180
- sourceTaskId: string;
198
+ sourceSessionId: string;
181
199
  sessionId: string;
182
200
  owner?: string | null;
183
201
  /** 出处的执行车道(必填;语义见 {@link AskRow.executionLane})。 */
@@ -222,7 +240,9 @@ export interface AskTransitionPatch {
222
240
  decisionActor?: unknown | null;
223
241
  decisionNote?: string | null;
224
242
  decidedAtMs?: number | null;
225
- deniedReason?: string | null;
243
+ /** 终态成因(值域与终局 claim 的 `cause` 同一型 {@link SettleCause};读口按终态成因词表读,且只在 `VOID` / `DENIED` 上作数 —— 别的转移上写了也读成缺席)。
244
+ * 补丁**没有**触发成因那一键:它只由 `claimTerminal(expire, { cause })` 写一次。 */
245
+ endingCause?: SettleCause;
226
246
  gateToken?: string | null;
227
247
  gateBoundCallId?: string | null;
228
248
  gateBoundInputHash?: string | null;
@@ -239,10 +259,12 @@ export interface DecideAskInput {
239
259
  * 🔴 [ref]:本次回决的**结算来源**,与决议同一条 UPDATE 落列 `settled_by`(语义/两态读法/为什么是
240
260
  * 自己一列,全在 {@link AskRow.settledBy})。缺席 ⇒ 列 NULL ⇒ 读回是「一个人答的」。
241
261
  *
242
- * 店**不判词**(词表属主是 core):写什么存什么。店只判**形** —— `assertDecideSettledBy` 的那一条
262
+ * 店**不判词**(词表属主是 core):写什么存什么。店只判**形** —— `assertDecideSource` 的那一条
243
263
  * (长度 ≤ 列宽;[ref] 起 approve 行也可以带来源位),理由见那只门的顶注;三 twin 同判。
244
264
  */
245
265
  settledBy?: HostSettledBy;
266
+ /** core 7.61.0([ref]):`settledBy:"policy"` 旁的机器座位,与决议同一条 UPDATE 落列 `decided_via`(语义见 {@link AskRow.decidedVia});缺席 ⇒ 列 NULL。 */
267
+ decidedVia?: DecidedVia;
246
268
  /** [ref]:ctrl+g 编辑实参,与决议同一条 UPDATE 落列(语义三态见 {@link AskRow.updatedInput})。
247
269
  * 缺席 / `undefined` ⇒ 列 NULL(没带编辑);**`null` 是值**(编辑成 null),照存 JSON `"null"`。
248
270
  * 调用方纪律:deny 不传(店不替调用方判决议方向,写什么存什么)。 */
@@ -282,7 +304,7 @@ export declare function assertReapBounds(cutoffMs: number, limit: number): void;
282
304
  export declare function assertIdempotencyKeyShape(key: string): void;
283
305
  /** `settled_by` 列宽 = 这一格的唯一尺(两方言 DDL 与下面的形门读同一个常量,不手抄第二份)。 */
284
306
  export declare const MAX_SETTLED_BY_CHARS = 16;
285
- /** `settled_by` 这一列装得下这个词吗(1..{@link MAX_SETTLED_BY_CHARS})—— 店门口形门({@link assertDecideSettledBy})的**谓词本身**。
307
+ /** `settled_by` 这一列装得下这个词吗(1..{@link MAX_SETTLED_BY_CHARS})—— 店门口形门({@link assertDecideSource})的**谓词本身**。
286
308
  * [ref] 起决定口不再读它:wire 上的出处位是闭集(`host-decision.ts` 的 `APPROVAL_DECIDED_BY`),映射到这一列的词恒在尺内。 */
287
309
  export declare function settledByFitsColumn(v: string): boolean;
288
310
  /**
@@ -296,9 +318,11 @@ export declare function settledByFitsColumn(v: string): boolean;
296
318
  * 🔴 [ref]:此前的第二条「只与 `deny` 同行」**删了** —— 它的前提(「只有人能批准一个要执行的动作,core 对 `{allow:true,settledBy}`
297
319
  * 一律 fail-closed 拒」)自 core 7.39.0 起不成立:策略的放行铸 `policy_allowed`,决定体的出处位 `decidedBy:"policy"` 在 allow 与 deny
298
320
  * 上都落这一列,approve 行带来源位是一条合法的行(读回铸点 `outcomeOfDecidedRow` 两向都读它)。今天写这一列的只有两条路:决定口经
299
- * `liveSettledBy` 映射来的 `"policy"`,与 `claimTerminal(refuse)` 的 {@link POLICY_REFUSAL_DECISION} —— 都是 core 在两向上都收的词。
321
+ * `liveHostSource` 映射来的 `"policy"`,与 `claimTerminal(refuse)` 的 {@link POLICY_REFUSAL_DECISION} —— 都是 core 在两向上都收的词。
300
322
  */
301
- export declare function assertDecideSettledBy(decision: Pick<DecideAskInput, "settledBy">): void;
323
+ export declare function assertDecideSource(decision: Pick<DecideAskInput, "settledBy" | "decidedVia">): void;
324
+ /** `decided_via` 列宽 = 这一格的唯一尺(两方言 DDL 读同一个常量)。core `DECIDED_VIA` 今天最长 23 字符(`permission_request_hook`)。 */
325
+ export declare const MAX_DECIDED_VIA_CHARS = 32;
302
326
  /**
303
327
  * [ref] 车3 刀 3b(§14.2 遗留记账):两条**重放读口**被调用方的 deadline 掐断时抛的具名错误。
304
328
  *
@@ -330,9 +354,10 @@ export interface ExpireResult {
330
354
  */
331
355
  export type AskTerminalClaimIntent = "expire" | "cancel" | "refuse";
332
356
  /**
333
- * 终局 claim 的可选实参:`cause` —— **赢下 `cancel`(VOID)/ `expire`(PARKING)时写进这一行 `denied_reason` 列的成因词**(决断口 410 / 晚答 404 / 跨副本的败方读数读同一列,
334
- * 所以这一行自己记着「为什么」,读面不靠任何进程内的东西)。`cancel` 写终态成因({@link SettleCause} 里的 `run_cancelled` / `run_exited`),
335
- * `expire` 写转 park 的触发成因({@link ParkTriggerCause}:`ttl_expired` / `broadcast_lost` / `stream_disconnected` / `undeliverable` / `shutdown_before_park`);缺席 ⇒ 列保持 NULL(读面缺席即缺席)。
357
+ * 终局 claim 的可选实参:`cause` —— **赢下终局的同一步写进这一行的成因词**,列由意图定({@link claimCauseColumn}):`cancel`(VOID)写 `ending_cause` = 终态成因
358
+ * ({@link SettleCause} 里的 `run_cancelled` / `run_exited`;决断口 410 / 晚答 404 / 跨副本的败方读数读这一列),`expire`(PARKING)写 `park_trigger` = 转 park 的触发成因
359
+ * ({@link ParkTriggerCause}:`ttl_expired` / `broadcast_lost` / `stream_disconnected` / `undeliverable` / `shutdown_before_park`;跨副本的悬挂腿与败方读数读这一列)。
360
+ * 所以这一行自己记着「为什么」,读面不靠任何进程内的东西;缺席 ⇒ 列保持 NULL(读面缺席即缺席)。
336
361
  * `refuse` 不读它(落的是一条决议,来源位 `policy`,不是结局成因);`expire` 撤兄弟时店自己写 `batch_sibling_withdrawn`(那是这条事务本身的事实)。
337
362
  */
338
363
  export interface AskTerminalClaimOpts {
@@ -341,13 +366,18 @@ export interface AskTerminalClaimOpts {
341
366
  /**
342
367
  * `refuse` 意图落行的决议(NP-2)—— **部署政策拒**:deny + 结算来源 `policy`。来源词的型取 core 的 `HostSettledBy`
343
368
  * (core 改词 / 退词 ⇒ 这里 tsc 红);协调器交给 core 的那一只(`tool-approval.ts` 的 `UnattendedPolicyRefusal`)读的是
344
- * **同一个**常量,于是「行上记的来源」与「首判交出去的来源」在构造上是一个词。三 twin 落行前都过 {@link assertDecideSettledBy}
369
+ * **同一个**常量,于是「行上记的来源」与「首判交出去的来源」在构造上是一个词。三 twin 落行前都过 {@link assertDecideSource}
345
370
  * (settled_by 的唯一形门:长度 ≤ 列宽)。
346
371
  */
347
372
  export declare const POLICY_REFUSAL_DECISION: {
348
373
  readonly decision: "deny";
349
374
  readonly settledBy: Extract<HostSettledBy, "policy">;
350
375
  };
376
+ /**
377
+ * 带成因的终局 claim 把成因写进哪一列(穷举 switch:意图闭集加一个词 ⇒ 这里编译期先红)。一列一义:`cancel` 赢下的是终态(`ending_cause`),
378
+ * `expire` 赢下的是转 park(`park_trigger`);`refuse` 落的是一条决议,不带成因(不进本表)。SQL 两孪生读这一只;内存孪生的两臂各自落同一格(`transitionAsk` 的补丁 / `expireAsk`)。
379
+ */
380
+ export declare function claimCauseColumn(intent: Exclude<AskTerminalClaimIntent, "refuse">): "ending_cause" | "park_trigger";
351
381
  /** 终局 claim 意图 → 本次 CAS 的目标态(穷举 switch:意图闭集加一个词 ⇒ 这里编译期先红)。 */
352
382
  export declare function claimTargetState(intent: AskTerminalClaimIntent): AskState;
353
383
  /**
@@ -374,10 +404,13 @@ export type AskTerminalClaimCurrent = {
374
404
  /** [ref]:赢家决议的**结算来源**(语义与两态读法见 {@link AskRow.settledBy})。**必填**(不是可选)——
375
405
  * 可选会让投影漏挑这一列时静默编译过,而漏挑的后果恰是本批要修的病:CAS 输家把赢家的策略拒读成人拒。 */
376
406
  settledBy: HostSettledBy | null;
377
- /** 行上记着的成因,经问账行成因的唯一读口 `causeOfAskRow` 读出(`kind` 说它在这一行上的意思:VOID / DENIED = 终态成因,PARKING / PARKED = 转 park 的触发成因);
378
- * **只在读得出时在场**(没写 / 与 state 不配 ⇒ 缺席;DECIDED 的败方读数逐字不变)。CAS 输家据它按**行上那个成因**结算(而不是把别人撤的卡读成一次拒绝);
379
- * 投影的唯一函数是 {@link projectAskTerminalClaimTruth},三 twin 不各自挑列,原始列值不出店层。 */
380
- cause?: AskRowCause;
407
+ /** core 7.61.0([ref]):赢家决议的机器座位(语义见 {@link AskRow.decidedVia});必填,理由与 `settledBy` 同。 */
408
+ decidedVia: DecidedVia | null;
409
+ /** 行上记着的终态成因(`ending_cause` 列,经唯一读口 `askRowEndingCause` 读出)。**只在读得出时在场**(没写 / 与 state 不配 ⇒ 缺席;DECIDED 的败方读数逐字不变)。
410
+ * CAS 输家据它按**行上那个成因**结算(而不是把别人撤的卡读成一次拒绝);投影的唯一函数是 {@link projectAskTerminalClaimTruth},三 twin 不各自挑列,原始列值不出店层。 */
411
+ endingCause?: AskRowEndingCause;
412
+ /** 行上记着的转 park 触发成因(`park_trigger` 列,经唯一读口 `askRowParkTrigger` 读出;行走过 PARKING 才可能在场)。在场律同 {@link endingCause}。 */
413
+ parkTrigger?: ParkTriggerCause;
381
414
  };
382
415
  /**
383
416
  * 单次原子往返的答案(语义律的唯一规范陈述点 = core `checkpoint-store.ts` `claimTerminal` 契约 JSDoc,
@@ -433,7 +466,7 @@ export interface BatchRow {
433
466
  }
434
467
  /** design 定稿 §4 的持久层接口。协调器接线(车2)、恢复扫描消费(车5)不在本车范围——本车只落这些方法。 */
435
468
  /** 一次调用在一条腿上的坐标({@link ApprovalAskStore.listByCall} 的四列;型从 {@link AskRow} 派生,不手抄)。 */
436
- export type AskCallCoordinate = Pick<AskRow, "taskId" | "legKey" | "toolCallId" | "sourceTaskId">;
469
+ export type AskCallCoordinate = Pick<AskRow, "taskId" | "legKey" | "toolCallId" | "sourceSessionId">;
437
470
  /** 「这一行在这次调用上」的唯一判据(四列逐字相等):InMemory twin 的筛子与 SQL twin 的出口对齐读同一只。 */
438
471
  export declare function askRowOnCall(row: AskRow, call: AskCallCoordinate): boolean;
439
472
  /**
@@ -517,12 +550,12 @@ export interface ApprovalAskStore {
517
550
  * 后续件。 */
518
551
  listPendingBySession(sessionId: string, owner: string | null, signal?: AbortSignal): Promise<AskRow[]>;
519
552
  /**
520
- * 🔴 [ref] / SN-C1:**这次调用在这条腿上的全部行** —— 任意状态、任意 ask 身份(同一调用不同类集 = 不同 askId 的几只问、升级前按旧派生
553
+ * 🔴 [ref] / SN-C1:**这次调用在这条腿上的全部行** —— 任意状态、任意 ask 身份(同一调用不同类集 / 不同展示动作 = 不同 askId 的几只问、升级前按旧派生
521
554
  * 写的行,都在其中)。坐标 = {@link AskCallCoordinate} 四列**精确**匹配;序 = `created_at_ms ASC, ask_id ASC`(确定性)。
522
555
  *
523
556
  * 唯一消费者 = 会话授权短路的争议判据(`tool-approval.ts` `sessionGrantUncontestedByRow`):「这次调用在腿内有行 ⇒ 一揽子放行不得代答」。
524
557
  * 为什么按调用而不是按 askId 点读:争议的真源是**调用**(人对这次调用说过什么 / 这次调用还有一张卡挂着),ask 身份只是其中一问的主键;
525
- * 身份可以加轴([ref] 加了类集),争议判据不得依赖身份 —— 按身份点读在身份加轴的那一刻就把同调用的别的问、旧派生的行读成「不存在」。
558
+ * 身份可以加轴([ref] 加了类集,7.143.0 加了展示的动作),争议判据不得依赖身份 —— 按身份点读在身份加轴的那一刻就把同调用的别的问、旧派生的行读成「不存在」。
526
559
  *
527
560
  * 三 twin 同答:SQL twin 读后按四列逐字再滤一遍 —— MySQL 协议的 VARCHAR 等值带 PAD SPACE(`'tc'` = `'tc '`),PG(`COLLATE "C"`)与
528
561
  * InMemory 严格比较;不在入口拒(调用 id 是引擎铸的原值,首尾空白不归本店判),在出口对齐。索引:走 `idx_ask_task_state`
@@ -550,7 +583,7 @@ export interface ApprovalAskStore {
550
583
  /**
551
584
  * **审批账本留存律的问账半场之一:问行**(律、节律与「还能动的 run」一处成文在 `approval-ask-retention.ts`;维护 tick 是唯一调用方)。
552
585
  * 删至多 `limit` 只**结了**的问行 —— `state ∈ ASK_TERMINAL` ∧ `provisional = false`(provisional 行还能被 {@link resolveProvisional} 改判,没结)
553
- * ∧ `updatedAtMs < cutoffMs` ∧ 行挂靠的三格(`taskId` / `sessionId` / `sourceTaskId`)都不在 `keepAnchors`(还能动的东西的挂靠集:
586
+ * ∧ `updatedAtMs < cutoffMs` ∧ 行挂靠的三格(`taskId` / `sessionId` / `sourceSessionId`)都不在 `keepAnchors`(还能动的东西的挂靠集:
554
587
  * 它还会回读这些行 —— 会话授权的争议判据 {@link listByCall} 把「这次调用在腿内没有行」读成可以代答)。非终态行多老都不删(它们归对账器
555
588
  * 与起服收敛)。只删不改。
556
589
  * 一条语句:要么删了并交回删掉的行数,要么一行没删就抛。三形同答(InMemory = 内核;File 形把这次调用照常落成一条账本动词,
@@ -578,6 +611,20 @@ export declare const APPROVAL_ASK_EXECUTION_LANE_COLUMN: {
578
611
  readonly mysql: "execution_lane VARCHAR(32) NOT NULL DEFAULT ''";
579
612
  readonly pg: "execution_lane VARCHAR(32) COLLATE \"C\" NOT NULL DEFAULT ''";
580
613
  };
614
+ /**
615
+ * 问行两列成因的列定义(两方言;唯一属主 —— 建表 DDL 内联它们,存量库的改名 / 加列只由一次性迁移做,那一步也读这里:窗口见 `plugins/one-time-migrations.ts` 登记表 M34)。
616
+ * 宽度 64 = 成因闭集里最长的词有余(钉在 `test/approval-reconciler.test.ts`)。语义见 {@link AskRow.endingCause} / {@link AskRow.parkTrigger}。
617
+ */
618
+ export declare const APPROVAL_ASK_ENDING_CAUSE_COLUMN: {
619
+ readonly name: "ending_cause";
620
+ readonly mysql: "ending_cause VARCHAR(64) NULL";
621
+ readonly pg: "ending_cause VARCHAR(64) COLLATE \"C\"";
622
+ };
623
+ export declare const APPROVAL_ASK_PARK_TRIGGER_COLUMN: {
624
+ readonly name: "park_trigger";
625
+ readonly mysql: "park_trigger VARCHAR(64) NULL";
626
+ readonly pg: "park_trigger VARCHAR(64) COLLATE \"C\"";
627
+ };
581
628
  export declare const APPROVAL_BATCH_TABLE = "approval_batch";
582
629
  /**
583
630
  * TiDB/MySQL-protocol 侧的两条建表语句,**抽成导出数组**(车3 刀 3a):`tidb-pool.ts` 的