@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
@@ -8,8 +8,9 @@ import { type WorkflowAgentSessionIndex } from "./workflow-agent-session-index.j
8
8
  * `completionId` = 引擎的一次完成的相关 id(fill-once,终态转换上铸):逐字、不脱敏、本服务零自铸。
9
9
  * `diagnostics` = 引擎在完成通知上铸的诊断教学文本(未完成 agent 的摘要行、完整记录的读取手柄、journal 坐标、续跑句):补投路径是它在原腿已不在(断线重连 / 重启)时唯一的去处,
10
10
  * 所以搬运;它是自由文本,出门前在入箱处过出口座(见 `buildWorkflowCompleteInboxEntry`),载荷上仍是引擎值。**崩溃恢复臂不带**:`WorkflowRun` 行上没有这一位,本服务不编。
11
- * · `drop`:这条持久链只搬运一句受限摘要 + 诊断文本 + 路由键 —— `task_id` / `task_type` / `toolUseId` 是 live `task_notification` 帧的键(那条路径自带),`result` / `usage` 是完整结果 / 计量
12
- * (`WorkflowStatus` 读得到),不进补投帧。
11
+ * · `drop`:这条持久链只搬运一句受限摘要 + 诊断文本 + 路由键 —— `task_id` / `task_type` / `toolUseId` 是 live `task_notification` 帧的键(那条路径自带),`result` / `usage` 是完整结果 / 计量,
12
+ * 不进补投帧:完整结果在引擎存下的运行记录里,上面搬运的 `diagnostics` 点名它的读取手柄(`ReadToolResult`;引擎没存下时那句如实说)—— 自 core 7.61.0 起
13
+ * 这是模型读到它的唯一一条路(轮询工具已删),所以 `diagnostics` 必须 `carry`。
13
14
  * 键名直呼 core 的入参型(型文本里就有 core 名)⇒ 读表的 literals 段收得到这些键。
14
15
  */
15
16
  export declare const WORKFLOW_NOTIFY_KEY_DISPOSITION: {
@@ -1,4 +1,5 @@
1
1
  import { type CheckpointToken, type ResumeOutcome } from "@sema-agent/core";
2
+ import type { ResumeRestatement } from "../http/resume-restatement.js";
2
3
  import { type JsonColumnReader } from "../plugins/sql-json-column.js";
3
4
  import { type InProcessClaimFence } from "../plugins/store-contracts.js";
4
5
  /** 一条人对审批卡的决定(引擎 `ResumeOutcome` 的 `policy_ask` 臂 —— 子代停驻只会是这一门)。 */
@@ -52,11 +53,15 @@ export interface ParkDecisionLaneSeats {
52
53
  */
53
54
  export declare function parkDecisionStoreRefusal(seats: ParkDecisionLaneSeats): string | undefined;
54
55
  /**
55
- * 决定的同一性判据:判词 + 绑定两位 + 出处词(`decidedBy`)+ 载荷指纹(`updatedInput` / `answer` / `reason` / `feedback`,与 live 腿幂等回放的门⑦
56
- * 同一只 {@link approvalPayloadFingerprint})。通道自报的 `approver` 不进 —— 它是归属标签,不改引擎铸的结算词。
56
+ * 决定的同一性判据:判词 + 绑定两位 + 出处(`decidedBy`,带了机器座位 `decidedVia` 时是 `[decidedBy, decidedVia]` 一对)+ 载荷指纹
57
+ * (`updatedInput` / `answer` / `reason` / `feedback`,与 live 腿幂等回放的门⑦同一只 {@link approvalPayloadFingerprint})。通道自报的 `approver` 不进 ——
58
+ * 它是归属标签,不改引擎铸的结算词;`decidedVia` 进 —— 它定模型读到的那一句(部署策略拒 / PermissionRequest 钩子拒),换了座位是另一个决定。
59
+ * 出处位按在场取形(与下面的重述同一条成员律):不带座位 ⇒ 那一位仍是裸 `decidedBy`,字节不变;带了 ⇒ 一对(数组与串不可能相等,不与任何不带座位的决定撞)。
57
60
  * 载荷序列化不了 ⇒ 抛(两侧都源自 JSON,走到这里是结构异常;那只函数自己已经响亮 warn)。
61
+ * 决定体带的续跑请求重述键(`mcpServers` / `a2aPeers`)也是这次决定的一部分:同一决定带另一张表是另一个决定(投递跑还活着时不答首投回执,
62
+ * 走既有的会话占用拒),同一张表(键序无关,引擎的规范串)才是重投。按在场追加 —— 不带的决定指纹字节不变,落了库的存量指纹不换字节。
58
63
  */
59
- export declare function parkDecisionFingerprint(outcome: WorkflowParkOutcome): string;
64
+ export declare function parkDecisionFingerprint(outcome: WorkflowParkOutcome, restatement?: ResumeRestatement): string;
60
65
  /**
61
66
  * 存下的决定体 → 内域形的**唯一读者**(SQL 列与 local 账本行共用)。判到本仓自己读的那几格为止(门、判词、绑定两位、出处对象);
62
67
  * 其余键的属主是引擎,它在续跑入口 pre-CAS 再判一遍(与 checkpoint 店读 `outcome` 列同一条分工,见 `checkpoint-store-sql.ts` 的形读者注)。
@@ -1,6 +1,6 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { join } from "node:path";
3
- import { AppendLog, ensureDir } from "@sema-agent/core";
3
+ import { AppendLog, canonicalJsonStringify, ensureDir } from "@sema-agent/core";
4
4
  import { approvalPayloadFingerprint } from "../plugins/approval-payload-fingerprint.js";
5
5
  import { createObjectColumnReader, isJsonObject } from "../plugins/sql-json-column.js";
6
6
  import { replayJsonlGuarded } from "../observability/corrupt-read-seat.js";
@@ -31,7 +31,7 @@ export function parkDecisionStoreRefusal(seats) {
31
31
  "its writes are fenced by the run store it was built on, so a different run store means decisions recorded where the session claims are not checked; " +
32
32
  "take runStore and workflowParkDecisions from the same store backend (run() and workflowParkDecisions()); refusing to start rather than record decisions the session claim cannot fence");
33
33
  }
34
- export function parkDecisionFingerprint(outcome) {
34
+ export function parkDecisionFingerprint(outcome, restatement) {
35
35
  const payload = approvalPayloadFingerprint({
36
36
  ...(outcome.updatedInput !== undefined ? { updatedInput: outcome.updatedInput } : {}),
37
37
  ...(outcome.answer !== undefined ? { answer: outcome.answer } : {}),
@@ -40,7 +40,9 @@ export function parkDecisionFingerprint(outcome) {
40
40
  });
41
41
  if (payload === null)
42
42
  throw new Error("workflow park decision: the decision payload cannot be serialized — refusing to record a decision whose identity cannot be stated");
43
- const identity = JSON.stringify([outcome.decision, outcome.boundCallId, outcome.boundInputHash, outcome.hostDecision.decidedBy, payload]);
43
+ const { decidedBy, decidedVia } = outcome.hostDecision;
44
+ const provenance = decidedVia !== undefined ? [decidedBy, decidedVia] : decidedBy;
45
+ const identity = JSON.stringify([outcome.decision, outcome.boundCallId, outcome.boundInputHash, provenance, payload, ...(restatement !== undefined ? [canonicalJsonStringify(restatement)] : [])]);
44
46
  return createHash("sha256").update(identity).digest("hex");
45
47
  }
46
48
  const readOutcomeObject = createObjectColumnReader();
@@ -78,4 +78,15 @@ export interface OrgDeliveryGateDeps {
78
78
  * 活对象(闭包持有目录 / 两只店 / 消费方)⇒ `create*`(CLAUDE.md 工厂命名律)。
79
79
  */
80
80
  export declare function createOrgDeliveryGate(deps: OrgDeliveryGateDeps): OrgDeliveryGate;
81
+ /**
82
+ * 箱址的读侧映射也归门(单一属主:箱址常量只在本文件出现,`test/org-delivery-gate-single-path.test.ts` ①):
83
+ * 被扣的跨会话信在收信会话的哪条车道上 ⇄ core 队列键的 `box`。消费方 = `src/peer-inbound-face.ts`。
84
+ */
85
+ /** 一封被扣的信在收信会话的哪条车道上:会话自己的箱,或它代为排空的本 principal 收件箱(core 的 `PRINCIPAL_INBOX_HANDLE`)。闭集。 */
86
+ export declare const PEER_INBOUND_LANES: readonly ["session", "principal_inbox"];
87
+ export type PeerInboundLane = (typeof PEER_INBOUND_LANES)[number];
88
+ /** 车道 → core 队列键的第三位(`peerHeldQueueIfAny` 的 `box`;会话自己的箱 = 缺席)。 */
89
+ export declare function boxOfLane(lane: PeerInboundLane): string | undefined;
90
+ /** 通告里的箱句柄 → 车道(会话箱按 core 的句柄函数比对,不手拼前缀);两者都不是 ⇒ `undefined`。 */
91
+ export declare function laneOfBox(box: string, sessionId: string): PeerInboundLane | undefined;
81
92
  //# sourceMappingURL=org-delivery-gate.d.ts.map
@@ -1,4 +1,4 @@
1
- import { CrossSessionCodecError, MAILBOX_TOMBSTONED_RECIPIENT_CODE, PRINCIPAL_INBOX_HANDLE, mintCrossPrincipalRecord, uuidv7, } from "@sema-agent/core";
1
+ import { CrossSessionCodecError, MAILBOX_TOMBSTONED_RECIPIENT_CODE, PRINCIPAL_INBOX_HANDLE, peerSessionBoxHandle, mintCrossPrincipalRecord, uuidv7, } from "@sema-agent/core";
2
2
  import { principalShapeProblem } from "./security.js";
3
3
  import { mailboxKeyProblem } from "./plugins/mailbox-store-sql.js";
4
4
  export const ORG_DELIVERY_QUOTA = Object.freeze({ windowMs: 3_600_000, attemptsPerSender: 60, perPair: 10, perRecipient: 30 });
@@ -56,4 +56,22 @@ export function createOrgDeliveryGate(deps) {
56
56
  },
57
57
  };
58
58
  }
59
+ export const PEER_INBOUND_LANES = ["session", "principal_inbox"];
60
+ export function boxOfLane(lane) {
61
+ switch (lane) {
62
+ case "session":
63
+ return undefined;
64
+ case "principal_inbox":
65
+ return PRINCIPAL_INBOX_HANDLE;
66
+ default: {
67
+ const unreachable = lane;
68
+ throw new Error(`peer inbound lane outside its closed set: ${String(unreachable)}`);
69
+ }
70
+ }
71
+ }
72
+ export function laneOfBox(box, sessionId) {
73
+ if (box === PRINCIPAL_INBOX_HANDLE)
74
+ return "principal_inbox";
75
+ return box === peerSessionBoxHandle(sessionId) ? "session" : undefined;
76
+ }
59
77
  //# sourceMappingURL=org-delivery-gate.js.map
@@ -9,7 +9,8 @@
9
9
  * 行永 parked、生命周期分叉)。
10
10
  */
11
11
  import type { HostDecision } from "./host-decision.js";
12
- import type { DecideBinding } from "./http/wire-types.js";
12
+ import type { DecideBinding, DecidedAction } from "./http/wire-types.js";
13
+ import type { ResumeRestatement } from "./http/resume-restatement.js";
13
14
  import type { BackgroundAgentRecord, BackgroundAgentStore, CheckpointStore, CheckpointToken, QuestionAnswer, RunInternals, TaskRegistry, ToolExecuteContext, WorkflowRun, WorkflowRunStore } from "@sema-agent/core";
14
15
  import type { ParkedReviveSurfaceFactory } from "./parked-revive-surface.js";
15
16
  import type { RebuiltInheritedGate } from "./boot/parked-revive-gate.js";
@@ -133,7 +134,7 @@ export declare function heldWorkflowParkDecisions(sources: HeldWorkflowParkDecis
133
134
  /**
134
135
  * pending checkpoint → parked bg 行匹配。
135
136
  *
136
- * 键链(core 1.390 事实):park 时 cp 落 `sourceTaskId: sessionId`(子会话 id,**不是** bg handle),
137
+ * 键链(core 1.390 事实):park 时 cp 落 `sourceSessionId: sessionId`(子会话 id,**不是** bg handle),
137
138
  * 而 bg 行同样存该子会话 id(`row.sessionId`)与绑定 token(`row.parkedCheckpointToken`)。
138
139
  * `listByScope` 只回 summary(含 handle/sessionId、不含 token),故两步:summary.sessionId 初筛 →
139
140
  * `get(handle, scope)` 全行复核 token。
@@ -219,7 +220,7 @@ export interface ParkedDecideRequest {
219
220
  token: string;
220
221
  scope: string;
221
222
  sessionId: string;
222
- /** cp.pendingAction 原样(outcome 铸造的 persisted 回落源,与 legacy 腿 D-1 姿势逐字同形)。 */
223
+ /** cp.pendingAction 原样(只读两件行事实:引擎自己的问题工具之判 `isEngineQuestionRow`、AskUserQuestion 预检的工具名;从不作绑定的回落源)。 */
223
224
  pendingAction: unknown;
224
225
  decision: "approve" | "deny";
225
226
  /** [ref] 件6③ / [ref](core 7.6.0 S6-A):**这次结算的事实**,由调用方陈述(必填,理由与 legacy 腿
@@ -231,12 +232,12 @@ export interface ParkedDecideRequest {
231
232
  * deny-sweep 恰是 req 缺席的那条腿,它对 parked 行另有静默跳过臂)⇒ 恒 `decidedBy:"person"`。 */
232
233
  hostDecision: HostDecision;
233
234
  reason?: string;
234
- /** D-1 decision-binding echo(操作员在 GET 看到的 boundCallId/boundInputHash 原样透传;server
235
- * 绝不重算,core resume 侧做 fail-closed 等值校验——与 legacy 腿同姿势)。 */
236
- binding?: Omit<DecideBinding, "checkpointToken">;
235
+ /** D-1 决断绑定:决断方点名它看到的那一对(必在 —— 续跑腿的判官 `decidedActionOf` 判过才交下来)与决定体的其余两位;
236
+ * server 绝不重算、绝不从停驻行补,core resume 侧做 fail-closed 等值校验 —— 与 legacy 腿同姿势。 */
237
+ binding: Omit<DecideBinding, "checkpointToken" | "boundCallId" | "boundInputHash"> & DecidedAction;
237
238
  /** body 带了 `remember:"session"` 时路由传入的 grant 执行器([ref] 候裁② 的 server 侧修,经两轮
238
239
  * 行为探针实证):豁免必须锚在**行的 root/host 会话 id**——部署侧三个豁免探针(policy 层/fs-write
239
- * 门/workflow 门)的闭包统一捕获 host 会话键且签名不携身份(1.393 seam 亲读:`sourceTaskId` 只在
240
+ * 门/workflow 门)的闭包统一捕获 host 会话键且签名不携身份(1.393 seam 亲读:`sourceSessionId` 只在
240
241
  * onAsk 层的 AskRequest,durable 车道无 onAsk),grant 落子代会话则键永不相交(G-F4 原案);落
241
242
  * root 会话则宿主键闭包立即命中,复活子代的后续同工具 ask 被短路(探针 v2 实证),语义与同步车道/
242
243
  * 宿主任务的「本会话不再询问」一致(= host 会话树全域)。行无 root 锚(病态形)⇒ claim 前诚实 400。 */
@@ -246,6 +247,9 @@ export interface ParkedDecideRequest {
246
247
  * server 不预判)。core 拒绝形:同步腿 409 invalid_outcome field:"answer",parked 腿经 drive 失败
247
248
  * 面 422 parked_resume.startup_failed。 */
248
249
  answer?: QuestionAnswer;
250
+ /** 决定体带的续跑请求重述键(`mcpServers` / `a2aPeers`;路由已过提交腿同一只判官)。两条车道各自决定拿它怎么办:workflow 车道盖在宿主会话的续跑输入上、
251
+ * 并进决定的同一性({@link parkDecisionFingerprint});bg 赎回腿不从存量提交体重建任务规格,盖不上 ⇒ claim 前诚实 400(`decide.parked_restatement_unsupported`)。 */
252
+ restatement?: ResumeRestatement;
249
253
  /** claim CAS 赢了(≈ legacy 腿的 markResuming 赢)之后、execute 之前触发——remember 豁免 grant
250
254
  * 的挂点(调用方自吞错,绝不抛落赎回)。claim 败/判别 miss 恒不触发。 */
251
255
  onDecideCommitted?: () => Promise<void>;
@@ -272,8 +276,8 @@ export interface ParkedDecideResult {
272
276
  * 判据一字未改,只是从 bg 腿里提出来:两条腿结算的是**同一种门**(core gateMatch 的 `policy_ask`),
273
277
  * 各铸一份必然漂移,而漂的是「人到底批了什么」这条审计事实。
274
278
  *
275
- * · D-1 绑定:操作员在 GET 看到的 `boundCallId`/`boundInputHash` 原样透传,server **绝不重算**;
276
- * 缺席时回落到行上 persisted 的那两位(core resume 侧做 fail-closed 等值校验)。
279
+ * · D-1 绑定:决断方在 GET 看到的 `boundCallId`/`boundInputHash` 原样透传,server **绝不重算、绝不从行上补**
280
+ * (那一对必在,续跑腿的判官判过;core resume 侧做 fail-closed 等值校验)。
277
281
  * · `reason` 走**存在性**判定(`!== undefined`),不是真值判定 —— 本铸点不折叠,原样上送。
278
282
  * 🔴 但**别把这条读成「空串与缺席在结算上是两件事」**(7.19.0 / [ref] 之前那句话是真的,之后不是):
279
283
  * 上送域的属主是引擎的单铸点,它把空文本折成缺席 ⇒ 到了消费端,`reason:""`、不传 `reason`、
@@ -120,17 +120,14 @@ function mintReviveArgs(row, knownAgentTypes) {
120
120
  };
121
121
  }
122
122
  export function buildParkedResumeOutcome(req) {
123
- const pa = req.pendingAction;
124
- const persistedCallId = pa?.kind === "tool_approval" ? (pa.toolCallId ?? "") : "";
125
- const persistedHash = pa?.kind === "tool_approval" ? (pa.boundInputHash ?? "") : "";
126
123
  const engineQuestion = isEngineQuestionRow(req.pendingAction);
127
124
  return {
128
125
  gate: "policy_ask",
129
- boundCallId: req.binding?.boundCallId ?? persistedCallId,
130
- boundInputHash: req.binding?.boundInputHash ?? persistedHash,
126
+ boundCallId: req.binding.boundCallId,
127
+ boundInputHash: req.binding.boundInputHash,
131
128
  decision: req.decision === "approve" ? "allow" : "deny",
132
- ...(req.decision === "approve" && !engineQuestion && req.binding?.updatedInput !== undefined ? { updatedInput: req.binding.updatedInput } : {}),
133
- ...(req.binding?.feedback !== undefined ? { feedback: req.binding.feedback } : {}),
129
+ ...(req.decision === "approve" && !engineQuestion && req.binding.updatedInput !== undefined ? { updatedInput: req.binding.updatedInput } : {}),
130
+ ...(req.binding.feedback !== undefined ? { feedback: req.binding.feedback } : {}),
134
131
  ...(req.answer !== undefined ? { answer: req.answer } : {}),
135
132
  ...(req.reason !== undefined ? { reason: req.reason } : {}),
136
133
  hostDecision: req.hostDecision,
@@ -263,6 +260,9 @@ export async function decideParkedAgent(deps, req, match) {
263
260
  if (req.grantRemember !== undefined && rememberRoot === undefined) {
264
261
  return { status: 400, body: refusalBody({ errorCode: "decide.parked_remember_unsupported", message: "remember cannot be applied: this parked agent row carries no root/parent session anchor for the exemption", facts: { taskId: match.handle } }) };
265
262
  }
263
+ if (req.restatement !== undefined) {
264
+ return { status: 400, body: refusalBody({ errorCode: "decide.parked_restatement_unsupported", message: "mcpServers / a2aPeers cannot be applied: this approval belongs to a background agent that stopped to wait for it, and that agent resumes with the MCP / A2A tables it had when it stopped — re-issue the decide without them to resume it that way", facts: { taskId: match.handle } }) };
265
+ }
266
266
  const pendingTool = req.pendingAction?.toolName;
267
267
  if (pendingTool === ASK_USER_QUESTION_TOOL_NAME && req.decision === "approve" && req.answer === undefined) {
268
268
  return { status: 400, body: refusalBody({ errorCode: "decide.parked_answer_required", message: "pending action is AskUserQuestion — approve requires body.answer (the operator's answers[]); deny needs none", facts: { taskId: match.handle } }) };
@@ -9,7 +9,7 @@ export function createParkedReviveSurface(defaultScenario) {
9
9
  if (root === undefined)
10
10
  return { reviveTool: bootTool };
11
11
  const seated = holdsSeat(row.parentSessionId === root, identity);
12
- const delegationRoot = { sessionId: root, incarnation: identity.rootOccupant.incarnation };
12
+ const delegationRoot = { sessionId: root, owner: identity.rootOccupant.owner, incarnation: identity.rootOccupant.incarnation };
13
13
  const tools = (seated ? defaultScenario({}, identity.principal, { sessionId: root, delegationRoot }) : defaultScenario({}, undefined, { delegationRoot })).tools;
14
14
  const reviveTool = agentOf(tools);
15
15
  if (reviveTool === undefined)
@@ -0,0 +1,127 @@
1
+ /**
2
+ * **被扣住的跨会话来信的收信方决断面**([ref])—— core 进程级 held 队列在本服务的**投影 + 决断转发**,不存第二份事实。
3
+ *
4
+ * ## 病
5
+ * 一封跨会话来信被扣住时,发信方 A 收到 `held` 回执,收信方 B 只见一帧 `engine_notice peer.inbound_disposition`(而且只在 B 有活腿时),
6
+ * `GET /v1/approvals` 里什么都没有 ⇒ 一封**等人审**的信(权限档对等判扣下的)B 没有地方批,只能等 `dialogExpiry` 带 `expired` 回执过期。
7
+ * core 早就给了宿主的决断口(`peerHeldQueueIfAny(scope, sessionId, box?)` → `PeerHeldQueue.list()` / `settle(seq, "approve" | "deny")`),本仓零处接。
8
+ *
9
+ * ## 规则
10
+ * · **事实只在 core**:一封信被扣住的那一刻起,它的决定是信箱记录自己的字段(store CAS),卡是 core 那只进程级队列上的一张;
11
+ * receipt / 投递 / 到期计时都是 core 绑在队列上的 sink(引擎在 run 的 drain 里自己 `configure`,宿主**不**接 sink —— 接了就是第二个写者)。
12
+ * 本面只做两件事:把队列里等人审的卡(见下「只有人能答的卡」)投成 `pending[]` 的 `peer_inbound` 行;把决断口的 approve / deny 原样交给 `settle`。
13
+ * 没有行店、没有落账:一张卡在不在、谁赢了 CAS、结局是什么,全是 core 的读数。
14
+ * · **属主 = 收信方**:列与决都只在调用方**自己**的信箱 scope 里找队列(`peerHeldQueueIfAny(调用方 scope, …)`;scope 读法与 core 的
15
+ * `taskScope = spec.principal ?? "default"` 同一条,见 {@link peerBoxScope})—— 别人的卡结构上取不到,决断口答的 404 与「没有这张卡」同码同文。
16
+ * 显式 operator **没有**额外射程:一封信是收信人的,不是运维治理面(与停驻门「operator 可决任意行」刻意不同,决断口按族分臂)。
17
+ * · **只有人能答的卡才进审批席**(core `PEER_HELD_REVIEW_CAUSES`:`mode-mismatch` / `no-mode-asserted` / `invalid-mode-attestation` —— 权限档
18
+ * 对等判扣下的信;CC 只对这几个成因开审阅框与到期计时,cli309.js:36111–36116 的句子逐条:`managed-setting` / `repo-setting` / `explicit-setting`
19
+ * 是「组织 / 仓库 / 你自己的设置在扣」,`your own "accept" cannot override managed policy; ask your admin` —— 收信方**不能**逐封放行)。
20
+ * 🔴 这是安全轴:core 的 `settle(approve)` 本身不看成因(它信宿主只把可审的卡呈给人),一张被部署策略
21
+ * `CROSS_SESSION_INBOUND=hold` 扣下的信若进了审批席,收信方一次 approve 就越过了组织策略。所以设置类 / `cross-principal-default` /
22
+ * `mode-unknown` / `bypass-default` 的卡**不列、不收**(决断口 404,与查无此卡同答);它们按 core 的规则等设置改变(每拍重判)。
23
+ * 成因会在重判时移动,列 / 决每次都读卡上**当下**的成因。
24
+ * · **一个调用方、一张卡、一个答案**:决断口只认列表会列出的卡(`decision === "held"` ∧ 可审成因);已批待投(`approved`)/ 已交接(`delivered`)的卡不列、不收。
25
+ *
26
+ * ## 索引(🚧 待 core 的枚举口到货即退役)
27
+ * core 的队列注册表是模块私有的,只能按 (scope, 会话, 箱) 查 —— 宿主拿不到「这个进程里有哪些队列」。本面因此记一张**坐标表**:
28
+ * (会话 id, 车道) —— 只是「去哪儿查」,不是卡,不含任何卡的事实。喂它的是 core 自己的两条通告(经部署级通告转发席,
29
+ * `boot/runner-deps.ts` 的 `createEngineNoticeForwarder`):`peer.inbound_disposition{disposition:"held"}`(成因可审)进表,
30
+ * `peer.held_settled{heldCount:0}` 出表。读的时候每一行都回 core 的队列取当下的卡,表里多一行 = 多查一次空队列,少一行 = 那张卡不列
31
+ * (信仍按 core 的期限带回执过期 —— 安全默认)。退役条件:core 导出进程内 held 队列的枚举(连同各队列的 scope)。
32
+ * 🚧 **坐标不带 scope**(通告 detail 里没有):同一会话 id 在本进程里被两个主体各排空出一只队列时(无主会话行 / 别的主体跑了这只会话),
33
+ * 一只队列清空的那条 `heldCount:0` 把坐标带出表,另一只队列里的卡就不再列出,直到它的下一条 `held`(失败方向 = 上一句的安全默认)。
34
+ * 钉 = `test/peer-inbound-approvals.test.ts` 的 🚧 R2 特征化格;core 请托 = 两条通告的 detail 带队列 scope(或上面的枚举口)。
35
+ *
36
+ * ## 关停
37
+ * CC `HBe` 在宿主:core 不调 `PeerHeldQueue.shutdown()`(全库零生产调用点)。{@link PeerInboundFace.shutdown} 在有序关停的审批收尾那一拍
38
+ * (`boot/shutdown.ts`,关池之前)对坐标表里每只队列调它 —— 仍被扣的信按 core 的规则带 `expired` 回执结清(reason `shutdown`)。
39
+ * scope 读自会话行属主(`owner ?? "default"`,与 `boot/session-faces.ts` 信箱腿同读),调用方给读口。
40
+ * 🚧 core 的队列 scope 是排空那条 run 的主体(`spec.principal ?? "default"`):无主会话被具名主体跑过时两者不同,关停找不到那只队列
41
+ * (失败方向同崩溃:信仍被扣在箱里,下一位消费者重新领它)。钉 = 🚧 R4 特征化格;core 请托同上。
42
+ * 🚧 core 的结清回执是投出即返(排空汇点 `void routePeerDeliveryReceipt(…)`):`shutdown()` 兑现时回执可能还没落箱,关池在它之后 ⇒ 那一条回执可能丢
43
+ * (信本身已结清)。宿主没有可等的句柄;core 请托 = `shutdown()` 等回执落箱再兑现。
44
+ */
45
+ import { type PeerHeldEntry, type PeerHeldSettleOutcome } from "@sema-agent/core";
46
+ import { type PeerInboundLane } from "./org-delivery-gate.js";
47
+ /**
48
+ * 一个 principal(或会话行属主)的信箱 scope —— core 写侧 `taskScope = spec.principal ?? "default"` 的同一条读法(无主 ⇒ `"default"`)。
49
+ * 列 / 决用调用方的 principal,关停用会话行属主;两者在属主围栏下同值(`resolve-spec.ts` 的 `fenceSessionOwner`),
50
+ * 无主存量会话被具名壳续聊的混属主形是既有残余(`boot/session-faces.ts` 信箱腿同一条登记)。
51
+ */
52
+ export declare function peerBoxScope(principalOrOwner: string | null | undefined): string;
53
+ /** 决断口 id 的坐标(id 是它的唯一拼法,见 {@link peerInboundIdOf})。 */
54
+ export interface PeerInboundCoord {
55
+ readonly sessionId: string;
56
+ readonly lane: PeerInboundLane;
57
+ readonly seq: number;
58
+ }
59
+ /** 坐标 → 决断口 id:`peer_inbound:<收信会话 id>:<车道>:<箱内 seq>`(会话 id 的文法里没有 `:`,三段无歧义)。 */
60
+ export declare function peerInboundIdOf(c: PeerInboundCoord): string;
61
+ /** 决断口 id → 坐标;不成形(会话 id 不合 core 的地址文法 / 车道不在闭集 / seq 不是非负安全整数)⇒ `undefined`(决断口答 404,不判 400:不给 id 空间当谕示)。 */
62
+ export declare function parsePeerInboundId(id: string): PeerInboundCoord | undefined;
63
+ /**
64
+ * `pending[]` 的 `peer_inbound` 行(wire 形)。键全部是 core 那张卡的投影,本仓不派生任何一位:
65
+ * · `id` —— 决断口 `POST /v1/approvals/:id/decide` 的 `:id`;
66
+ * · `recipientSessionId` —— 收信会话(刻意**不**叫 `sessionId`:那个名字在本数组里是停驻门的 `:id`,不分族的消费方拿它去决断会决到
67
+ * 同会话的停驻卡上;换名之后它读到缺席,而不是错卡);
68
+ * · `cause` —— core 的扣信成因(`PeerHeldEntry.cause`,`CrossSessionHoldCause` 闭集);
69
+ * · `content` / `fromName` —— 信的存储正文(`[summary] message`,core 的规范存储形)与发信方的显示名,**过凭据脱敏**(显示面;与停驻行的工具入参同一条);
70
+ * · `fromSession` / `fromScope` —— 发信会话 / 跨 principal 投递的发信 scope(core 的 typed 归属锚),在场才带;
71
+ * · `sentAt` / `heldAt` —— epoch ms。
72
+ * 🚧 **没有 `deadline`**:到期计时在 core 队列里(在扣信、成因变动、换绑时各自起表),卡上不带它;本仓若按 `heldAt + dialogExpiry` 自算,
73
+ * 成因一动就是一个错的截止时刻(第二份读法)。core 给卡加上到期时刻即补上(请托见 [ref] 收车档)。
74
+ */
75
+ export interface PeerInboundPendingRow {
76
+ readonly kind: "peer_inbound";
77
+ readonly id: string;
78
+ readonly recipientSessionId: string;
79
+ readonly lane: PeerInboundLane;
80
+ readonly cause: PeerHeldEntry["cause"];
81
+ readonly content: string;
82
+ readonly sentAt: number;
83
+ readonly heldAt: number;
84
+ readonly fromSession?: string;
85
+ readonly fromName?: string;
86
+ readonly fromScope?: string;
87
+ }
88
+ /** 决断口给本族的决定词(停驻门的 `decision` 闭集原样;core 的 `cancel` 臂本仓没有门 —— 见收车档未闭环)。 */
89
+ export type PeerInboundDecision = "approve" | "deny";
90
+ /** 决断口 `peer_inbound` 臂的 200 体(键集属主;`outcome` = core `settle` 的答案原词,`gone` 不上 wire —— 它答 404)。 */
91
+ export interface PeerInboundDecideReply {
92
+ readonly kind: "peer_inbound";
93
+ readonly id: string;
94
+ readonly outcome: Exclude<PeerHeldSettleOutcome, "gone">;
95
+ }
96
+ /** 一次决断的结局:`not_found`(调用方 scope 里没有这张待决卡 —— 不存在 / 别人的 / 已不在 held)、`settled`(core 的答案原词)、`unrecorded`(店拒了这一步,core 说什么都没决;`error` = core 抛的那一只,交给调用方留痕)。 */
97
+ export type PeerInboundDecideResult = {
98
+ readonly kind: "not_found";
99
+ } | {
100
+ readonly kind: "settled";
101
+ readonly outcome: Exclude<PeerHeldSettleOutcome, "gone">;
102
+ } | {
103
+ readonly kind: "unrecorded";
104
+ readonly error: unknown;
105
+ };
106
+ /** 关停读数:结清了几只队列、几只会话的 scope 读不出(那几只的卡随进程离开 —— 信仍在箱里被扣着,下一位消费者重新领它)。 */
107
+ export interface PeerInboundShutdownReading {
108
+ readonly queues: number;
109
+ readonly scopeUnresolved: number;
110
+ }
111
+ export interface PeerInboundFace {
112
+ /** 部署级通告转发席的观察口:只读两条码的坐标,其余一概不看。永不抛(转发席不许把异常回抛给引擎)。 */
113
+ observe(notice: {
114
+ readonly code: string;
115
+ readonly detail?: Record<string, unknown>;
116
+ }): void;
117
+ /** `scope` 里此刻等人审的信({@link awaitsReview} 的卡),按 `heldAt` 升序(同刻按 id)。 */
118
+ list(scope: string): PeerInboundPendingRow[];
119
+ /** 决断转发:只认 `scope` 里此刻等人审的那张卡(与 {@link list} 同一只判据);`settle` 抛(店拒了 CAS)⇒ `unrecorded`。 */
120
+ decide(scope: string, coord: PeerInboundCoord, decision: PeerInboundDecision): Promise<PeerInboundDecideResult>;
121
+ /** 有序关停:坐标表里每只队列 `shutdown()`(core 的规则:仍 held 的卡带 `expired` 回执结清,reason `shutdown`)。 */
122
+ shutdown(scopeOfSession: (sessionId: string) => Promise<string | undefined>): Promise<PeerInboundShutdownReading>;
123
+ }
124
+ export declare function createPeerInboundFace(): PeerInboundFace;
125
+ /** 进程单例 —— 与 core 的 held 队列注册表同一个寿命(进程级);部署级通告转发席与审批路由读的是同一只。 */
126
+ export declare const defaultPeerInboundFace: PeerInboundFace;
127
+ //# sourceMappingURL=peer-inbound-face.d.ts.map
@@ -0,0 +1,158 @@
1
+ import { PEER_HELD_REVIEW_CAUSES, isPeerSessionId, peerHeldQueueIfAny, } from "@sema-agent/core";
2
+ import { PEER_INBOUND_LANES, boxOfLane, laneOfBox } from "./org-delivery-gate.js";
3
+ import { PEER_INBOUND_ID_PREFIX } from "./approval-queue-kind.js";
4
+ import { recordFailOpen } from "./observability/fail-open.js";
5
+ import { seatText } from "./trace/exit-seat.js";
6
+ export function peerBoxScope(principalOrOwner) {
7
+ return principalOrOwner ?? "default";
8
+ }
9
+ export function peerInboundIdOf(c) {
10
+ return `${PEER_INBOUND_ID_PREFIX}${c.sessionId}:${c.lane}:${c.seq}`;
11
+ }
12
+ export function parsePeerInboundId(id) {
13
+ if (!id.startsWith(PEER_INBOUND_ID_PREFIX))
14
+ return undefined;
15
+ const parts = id.slice(PEER_INBOUND_ID_PREFIX.length).split(":");
16
+ if (parts.length !== 3)
17
+ return undefined;
18
+ const [sessionId, lane, seqText] = parts;
19
+ if (!isPeerSessionId(sessionId))
20
+ return undefined;
21
+ if (!PEER_INBOUND_LANES.includes(lane))
22
+ return undefined;
23
+ if (!/^(0|[1-9][0-9]*)$/.test(seqText))
24
+ return undefined;
25
+ const seq = Number(seqText);
26
+ if (!Number.isSafeInteger(seq))
27
+ return undefined;
28
+ return { sessionId, lane: lane, seq };
29
+ }
30
+ function rowOf(sessionId, lane, card) {
31
+ const fromSession = card.peerMeta?.fromSession;
32
+ const fromScope = card.peerMeta?.fromScope;
33
+ return {
34
+ kind: "peer_inbound",
35
+ id: peerInboundIdOf({ sessionId, lane, seq: card.seq }),
36
+ recipientSessionId: sessionId,
37
+ lane,
38
+ cause: card.cause,
39
+ content: seatText("peer-inbound-card-text", card.content),
40
+ sentAt: card.sentAt,
41
+ heldAt: card.heldAt,
42
+ ...(fromSession !== undefined ? { fromSession } : {}),
43
+ ...(card.from !== undefined ? { fromName: seatText("peer-inbound-card-text", card.from) } : {}),
44
+ ...(fromScope !== undefined ? { fromScope } : {}),
45
+ };
46
+ }
47
+ function coordinatesOf(code, detail) {
48
+ const sessionId = detail?.sessionId;
49
+ const box = detail?.box;
50
+ if (typeof sessionId === "string" && typeof box === "string" && isPeerSessionId(sessionId)) {
51
+ const lane = laneOfBox(box, sessionId);
52
+ if (lane !== undefined)
53
+ return { sessionId, lane };
54
+ }
55
+ recordFailOpen("server.peer-inbound.notice-coordinates-unreadable", `code=${code}`);
56
+ return undefined;
57
+ }
58
+ const indexKey = (e) => JSON.stringify([e.lane, e.sessionId.toLowerCase()]);
59
+ function awaitsReview(card) {
60
+ return card.decision === "held" && isReviewCause(card.cause);
61
+ }
62
+ function isReviewCause(cause) {
63
+ return typeof cause === "string" && PEER_HELD_REVIEW_CAUSES.includes(cause);
64
+ }
65
+ function queueOf(scope, e) {
66
+ return peerHeldQueueIfAny(scope, e.sessionId, boxOfLane(e.lane));
67
+ }
68
+ export function createPeerInboundFace() {
69
+ const index = new Map();
70
+ const tails = new WeakMap();
71
+ const serially = (q, seq, step) => {
72
+ const perQueue = tails.get(q) ?? new Map();
73
+ tails.set(q, perQueue);
74
+ const prev = perQueue.get(seq);
75
+ const run = prev === undefined ? step() : prev.then(step);
76
+ perQueue.set(seq, run);
77
+ return run.finally(() => {
78
+ if (perQueue.get(seq) === run)
79
+ perQueue.delete(seq);
80
+ });
81
+ };
82
+ return {
83
+ observe(notice) {
84
+ switch (notice.code) {
85
+ case "peer.inbound_disposition": {
86
+ if (notice.detail?.disposition !== "held" || !isReviewCause(notice.detail.cause))
87
+ return;
88
+ const e = coordinatesOf(notice.code, notice.detail);
89
+ if (e !== undefined && !index.has(indexKey(e)))
90
+ index.set(indexKey(e), e);
91
+ return;
92
+ }
93
+ case "peer.held_settled": {
94
+ if (notice.detail?.heldCount !== 0)
95
+ return;
96
+ const e = coordinatesOf(notice.code, notice.detail);
97
+ if (e !== undefined)
98
+ index.delete(indexKey(e));
99
+ return;
100
+ }
101
+ default:
102
+ return;
103
+ }
104
+ },
105
+ list(scope) {
106
+ const rows = [];
107
+ for (const e of index.values()) {
108
+ const q = queueOf(scope, e);
109
+ if (q === undefined)
110
+ continue;
111
+ for (const card of q.list())
112
+ if (awaitsReview(card))
113
+ rows.push(rowOf(e.sessionId, e.lane, card));
114
+ }
115
+ return rows.sort((a, b) => a.heldAt - b.heldAt || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
116
+ },
117
+ async decide(scope, coord, decision) {
118
+ const q = queueOf(scope, coord);
119
+ if (q === undefined)
120
+ return { kind: "not_found" };
121
+ return serially(q, coord.seq, async () => {
122
+ if (!q.list().some((c) => c.seq === coord.seq && awaitsReview(c)))
123
+ return { kind: "not_found" };
124
+ let outcome;
125
+ try {
126
+ outcome = await q.settle(coord.seq, decision);
127
+ }
128
+ catch (error) {
129
+ return { kind: "unrecorded", error };
130
+ }
131
+ return outcome === "gone" ? { kind: "not_found" } : { kind: "settled", outcome };
132
+ });
133
+ },
134
+ async shutdown(scopeOfSession) {
135
+ let queues = 0;
136
+ let scopeUnresolved = 0;
137
+ const scopes = new Map();
138
+ for (const e of [...index.values()]) {
139
+ const sid = e.sessionId.toLowerCase();
140
+ if (!scopes.has(sid))
141
+ scopes.set(sid, await scopeOfSession(e.sessionId).catch(() => undefined));
142
+ const scope = scopes.get(sid);
143
+ if (scope === undefined) {
144
+ scopeUnresolved += 1;
145
+ continue;
146
+ }
147
+ const q = queueOf(scope, e);
148
+ if (q === undefined)
149
+ continue;
150
+ await q.shutdown();
151
+ queues += 1;
152
+ }
153
+ return { queues, scopeUnresolved };
154
+ },
155
+ };
156
+ }
157
+ export const defaultPeerInboundFace = createPeerInboundFace();
158
+ //# sourceMappingURL=peer-inbound-face.js.map
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * **规则表的唯一编译口** —— 一张工具名规则表(`ask` / `deny` 之一)编成一只 `ToolPolicy`。读者:请求腿的 `settings.permissions.{ask,deny}`
3
- * 两桶(`task-settings.ts` 的 `deriveSettingsPolicy`,每桶按根至多编两集,见文件尾 {@link ruleRootOf})。设置层条目的形与层 → 根表也住这里(文件尾一节)。
3
+ * 两桶(`task-settings.ts` 的 `deriveSettingsPolicy`,每桶按根至多编两集,见文件尾 {@link ruleRootOf})。设置层条目的形与层 → 根表也住这里(文件尾一节;三桶同一张表、同一只分集读法)。
4
4
  *
5
5
  * 🔴 core 7.42.0 起策略只说 deny / ask:allow 规则有且只有引擎 allow 层的两个席位 —— 请求的 `settings.permissions.allow` 整份投
6
6
  * `TaskSpec.allowedTools`(`task-settings.ts` 的 `applyTaskSettings`),运维的 `APPROVAL_GRANT` 投 `RunnerDeps.allowedTools`
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * 为什么住在一只叶模块里(不留在 `task-settings.ts`):本模块只 import core 与契约包 settings-schema(纯叶),读者引它不成环。
10
10
  */
11
- import { type PathRuleBases, type PermissionRule, type PermissionRulePolicyOptions, type ToolPolicy } from "@sema-agent/core";
11
+ import { type PathRuleBases, type PermissionRule, type PermissionRulePolicyOptions, type RuleBehavior, type ToolPolicy } from "@sema-agent/core";
12
12
  import { type SettingsSource } from "@sema-agent/settings-schema";
13
13
  /** 一张表里规则条数的上限 —— 请求腿 wire 门(`task-settings.ts` 的 deny / ask 两桶判形)读的也是这一个名字:core 的缺省帽是 256,
14
14
  * 不对齐的话第 257 条起编译当场抛,等于服务端自己收下的东西自己反悔。 */
@@ -80,15 +80,15 @@ export declare function buildRuleListJudgeOptions(pathBases: PathRuleBases | und
80
80
  * `table` = 这一集出自哪只桶(只进编不出时的兜底句;逐条坐标由先跑的受理判 `rulePolicyBucketsRefusal` 给)。 */
81
81
  export declare function createRuleListPolicy(rules: readonly SourcedRule[], behavior: PermissionRule["behavior"], table: string, pathBases: PathRuleBases | undefined): ToolPolicy;
82
82
  /**
83
- * deny / ask 桶的一个条目:**纯串** = 请求自己写的规则(今天的形,读在任务根);**带层条目** `{ rule, layer }` = 端从某个设置层读来的规则,`layer` 是
83
+ * 三桶(`allow` / `deny` / `ask`)的一个条目:**纯串** = 请求自己写的规则(今天的形,读在任务根);**带层条目** `{ rule, layer }` = 端从某个设置层读来的规则,`layer` 是
84
84
  * 契约包 `SETTINGS_SOURCES` 的来源词(= 参照实现的设置来源词)。层只决定这条规则的 `/` 读在哪个根({@link layerRuleRoot}),**不决定准入档**
85
- * (准入只由凭据推,`http/caller-trust.ts`):调用方自报任何层,挪动的只是它自己那条收紧读在哪,与它自己写 `//abs` 等价。
85
+ * (准入只由凭据推,`http/caller-trust.ts`):调用方自报任何层,挪动的只是它自己那条规则读在哪,与它自己写 `//abs` 等价(allow 照旧按放宽成员要它的档)。
86
86
  *
87
87
  * 为什么是「条目带层」而不是一只新键:本版之前的每一个读者(回滚、滚动发布中的旧副本)对一只没见过的键是**静默忽略**(续跑重放走宽容层),
88
88
  * 带层的收紧会整批消失;对一个不是串的条目则在桶门上 `bad_entry` —— 新提交 400、续跑 409,响亮拒。条数帽也因此仍是「一桶一张表」。
89
89
  */
90
90
  export type RuleListEntry = string | LayeredRule;
91
- /** 带层条目(恰两键;受理判 {@link ruleListEntryOk})。 */
91
+ /** 带层条目(恰两键;受理判 {@link layeredRuleOk})。 */
92
92
  export interface LayeredRule {
93
93
  readonly rule: string;
94
94
  readonly layer: SettingsSource;
@@ -98,8 +98,12 @@ export interface SourcedRule {
98
98
  readonly rule: string;
99
99
  readonly source: string;
100
100
  }
101
- /** 一个条目合不合 deny / ask 桶的条目律:非空串;或恰 `rule`(非空串)+ `layer`(`SETTINGS_SOURCES` 里的词)两键的纯对象。别的形一律不合(多键、空串、未知层、数组、原型对象)。 */
101
+ /** 带层条目的判据(三桶同一只):恰 `rule`(非空串)+ `layer`(`SETTINGS_SOURCES` 里的词)两键的纯对象。别的形一律不合(多键、空串、未知层、数组、原型对象)。 */
102
+ export declare function layeredRuleOk(x: unknown): x is LayeredRule;
103
+ /** deny / ask 桶的条目律:非空串(规则编译器读不出空串),或合形的带层条目。 */
102
104
  export declare function ruleListEntryOk(x: unknown): x is RuleListEntry;
105
+ /** allow 桶的条目律:任意串(空串交引擎的 allow 层,读成 `grammar` 并通告),或合形的带层条目。 */
106
+ export declare function allowListEntryOk(x: unknown): x is RuleListEntry;
103
107
  /** 条目的规则原文(纯串即它自己)。 */
104
108
  export declare function ruleTextOf(entry: RuleListEntry): string;
105
109
  /** 条目进编译口的出处串:纯串 = 桶坐标(今天的字面);带层 = 桶坐标 + 层(`settings.permissions.deny, layer userSettings`)。 */
@@ -114,7 +118,7 @@ export type RuleRoot = (typeof RULE_ROOTS)[number];
114
118
  */
115
119
  export declare function layerRuleRoot(layer: SettingsSource): RuleRoot;
116
120
  /**
117
- * 一个条目编进哪一集:**只有 `/` 随层变** —— 带层条目的层根是配置根 ∧ 这条规则要 `root`(core 的必需基分类 `ruleBasesNeeded`)⇒ `configuration`;
121
+ * 一个条目编进哪一集(deny / ask 编成哪只策略、allow 投成哪一员 —— 三桶同读):**只有 `/` 随层变** —— 带层条目的层根是配置根 ∧ 这条规则要 `root`(core 的必需基分类 `ruleBasesNeeded`)⇒ `configuration`;
118
122
  * 其余一律 `task`(纯串、别的层、`~/` / 相对形、`//abs` 与 subpath 这类分类为空的规则 —— 后者在 Windows 族上仍随声明的根选盘,必须留在今天那一集)。
119
123
  * 文法读不出的串 ⇒ `task`(受理判在前面就拒它,这里不另说一句)。
120
124
  *
@@ -122,7 +126,7 @@ export declare function layerRuleRoot(layer: SettingsSource): RuleRoot;
122
126
  * `policyAnchors`)—— 「一层一集」在任务根读不出的腿(未登记工作目录的 host 腿、每一条沙箱腿)上会把 user 层的相对规则读到配置根下。配置根那一集只装要
123
127
  * `root` 的规则,`cwd` 永远不被问。
124
128
  */
125
- export declare function ruleRootOf(entry: RuleListEntry, behavior: PermissionRule["behavior"]): RuleRoot;
129
+ export declare function ruleRootOf(entry: RuleListEntry, behavior: RuleBehavior): RuleRoot;
126
130
  /** 一桶的条目按根分集(受理判与编译读同一只:判的就是要编的那一集);集内保持原序。 */
127
- export declare function ruleSetsOf(entries: readonly RuleListEntry[], behavior: PermissionRule["behavior"]): Readonly<Record<RuleRoot, readonly RuleListEntry[]>>;
131
+ export declare function ruleSetsOf(entries: readonly RuleListEntry[], behavior: RuleBehavior): Readonly<Record<RuleRoot, readonly RuleListEntry[]>>;
128
132
  //# sourceMappingURL=permission-rule-list.d.ts.map
@@ -17,9 +17,7 @@ export function createRuleListPolicy(rules, behavior, table, pathBases) {
17
17
  }
18
18
  }
19
19
  const SETTINGS_SOURCE_SET = new Set(SETTINGS_SOURCES);
20
- export function ruleListEntryOk(x) {
21
- if (typeof x === "string")
22
- return x.length > 0;
20
+ export function layeredRuleOk(x) {
23
21
  if (x === null || typeof x !== "object" || Array.isArray(x) || Object.getPrototypeOf(x) !== Object.prototype)
24
22
  return false;
25
23
  const keys = Object.keys(x);
@@ -28,6 +26,12 @@ export function ruleListEntryOk(x) {
28
26
  const { rule, layer } = x;
29
27
  return typeof rule === "string" && rule.length > 0 && typeof layer === "string" && SETTINGS_SOURCE_SET.has(layer);
30
28
  }
29
+ export function ruleListEntryOk(x) {
30
+ return typeof x === "string" ? x.length > 0 : layeredRuleOk(x);
31
+ }
32
+ export function allowListEntryOk(x) {
33
+ return typeof x === "string" || layeredRuleOk(x);
34
+ }
31
35
  export function ruleTextOf(entry) {
32
36
  return typeof entry === "string" ? entry : entry.rule;
33
37
  }