@sema-agent/server 7.137.0 → 7.139.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 (328) hide show
  1. package/MIGRATION.md +600 -142
  2. package/README.md +7 -3
  3. package/README.zh-CN.md +6 -3
  4. package/USAGE.md +132 -41
  5. package/deploy/sema-up/README.md +1 -1
  6. package/deploy/sema-up/lib/lanes.json +3 -2
  7. package/dist/adoption/wire.d.ts +3 -3
  8. package/dist/approval-card.d.ts +23 -16
  9. package/dist/approval-card.js +1 -0
  10. package/dist/audit.js +1 -0
  11. package/dist/background-agent-name.d.ts +5 -0
  12. package/dist/background-agent-name.js +5 -0
  13. package/dist/bench/s1/arms.d.ts +1 -1
  14. package/dist/bench/s1/arms.js +1 -3
  15. package/dist/bench/s1/run-firm.js +2 -2
  16. package/dist/boot/budget-tracing.d.ts +1 -12
  17. package/dist/boot/deployment-mods.d.ts +46 -16
  18. package/dist/boot/deployment-mods.js +82 -6
  19. package/dist/boot/device-lane.d.ts +8 -3
  20. package/dist/boot/device-lane.js +7 -2
  21. package/dist/boot/execution-env.d.ts +4 -2
  22. package/dist/boot/execution-env.js +16 -24
  23. package/dist/boot/execution-lane.d.ts +1 -1
  24. package/dist/boot/governance-seams.d.ts +12 -18
  25. package/dist/boot/governance-seams.js +10 -24
  26. package/dist/boot/leader.d.ts +3 -0
  27. package/dist/boot/leader.js +1 -0
  28. package/dist/boot/memory-consolidation.js +2 -2
  29. package/dist/boot/org-delivery.js +15 -2
  30. package/dist/boot/resolve-spec.d.ts +4 -1
  31. package/dist/boot/resolve-spec.js +29 -19
  32. package/dist/boot/retention-lane.d.ts +14 -8
  33. package/dist/boot/retention-lane.js +2 -8
  34. package/dist/boot/runner-deps.d.ts +10 -4
  35. package/dist/boot/runner-deps.js +2 -0
  36. package/dist/boot/session-faces.d.ts +0 -3
  37. package/dist/boot/session-faces.js +15 -8
  38. package/dist/boot/stage-02-web-admin-pull.js +2 -2
  39. package/dist/boot/stage-04-stores.d.ts +1 -12
  40. package/dist/boot/stage-05-execution-env.d.ts +3 -12
  41. package/dist/boot/stage-05-execution-env.js +8 -5
  42. package/dist/boot/stage-06-runners.d.ts +3 -12
  43. package/dist/boot/stage-06-runners.js +5 -3
  44. package/dist/boot/stage-07-capability-layer.d.ts +3 -12
  45. package/dist/boot/stage-07-capability-layer.js +2 -2
  46. package/dist/boot/stage-08-reapers.d.ts +3 -12
  47. package/dist/boot/stage-09-leader.d.ts +3 -12
  48. package/dist/boot/stage-09-leader.js +2 -3
  49. package/dist/boot/stage-10-http-server.d.ts +3 -12
  50. package/dist/boot/stage-10-http-server.js +5 -3
  51. package/dist/boot/stores.js +4 -4
  52. package/dist/boot/web-admin.d.ts +5 -2
  53. package/dist/boot/web-admin.js +5 -5
  54. package/dist/brain.js +5 -5
  55. package/dist/budget.d.ts +17 -15
  56. package/dist/budget.js +2 -2
  57. package/dist/capabilities/mcp-probe.d.ts +36 -11
  58. package/dist/capabilities/mcp-probe.js +44 -5
  59. package/dist/capabilities/scenarios.d.ts +1 -1
  60. package/dist/capability-facts.d.ts +21 -7
  61. package/dist/capability-facts.js +13 -3
  62. package/dist/checkpoint-identity.d.ts +13 -6
  63. package/dist/config-catalog.js +14 -8
  64. package/dist/config-invariants.d.ts +23 -3
  65. package/dist/config-invariants.js +42 -0
  66. package/dist/config-provider.d.ts +19 -24
  67. package/dist/config-provider.js +27 -12
  68. package/dist/config-types.d.ts +25 -12
  69. package/dist/config.d.ts +33 -0
  70. package/dist/config.js +52 -20
  71. package/dist/delegated-session-owner.d.ts +2 -2
  72. package/dist/deployment-shape.d.ts +7 -7
  73. package/dist/device-enrollment.d.ts +41 -2
  74. package/dist/device-enrollment.js +27 -12
  75. package/dist/device-store.d.ts +58 -85
  76. package/dist/device-store.js +19 -49
  77. package/dist/device-ws-hub.js +2 -1
  78. package/dist/env-name-allowlist-knobs.d.ts +5 -4
  79. package/dist/exec-cut.d.ts +60 -0
  80. package/dist/exec-cut.js +36 -0
  81. package/dist/execution-lane-caps.d.ts +19 -0
  82. package/dist/execution-lane-caps.js +10 -0
  83. package/dist/execution-placement.d.ts +259 -0
  84. package/dist/execution-placement.js +199 -0
  85. package/dist/field-conflict.d.ts +3 -2
  86. package/dist/field-conflict.js +1 -0
  87. package/dist/gate-settlement.d.ts +71 -0
  88. package/dist/gate-settlement.js +40 -0
  89. package/dist/hooks/classic-stations.d.ts +1 -0
  90. package/dist/hooks/classic-stations.js +1 -0
  91. package/dist/hooks/command-spawn.d.ts +2 -2
  92. package/dist/hooks/hook-runner.js +14 -14
  93. package/dist/hosted-posture.d.ts +2 -2
  94. package/dist/http/admission.js +10 -19
  95. package/dist/http/checkpoint-ctx.d.ts +21 -12
  96. package/dist/http/checkpoint-ctx.js +4 -2
  97. package/dist/http/claimed-run.d.ts +1 -1
  98. package/dist/http/dispatch.d.ts +4 -0
  99. package/dist/http/dispatch.js +3 -0
  100. package/dist/http/durable-run-leg.js +12 -6
  101. package/dist/http/execution-context.d.ts +218 -0
  102. package/dist/http/execution-context.js +330 -0
  103. package/dist/http/memory-compliance-failure-reply.js +3 -0
  104. package/dist/http/resume-legs.d.ts +5 -11
  105. package/dist/http/resume-legs.js +179 -104
  106. package/dist/http/route-ctx.d.ts +44 -59
  107. package/dist/http/route-ctx.js +2 -5
  108. package/dist/http/routes/a2a-serve.js +10 -4
  109. package/dist/http/routes/agents-roster.d.ts +1 -1
  110. package/dist/http/routes/agents-roster.js +5 -1
  111. package/dist/http/routes/approvals-assistant.js +6 -6
  112. package/dist/http/routes/background.d.ts +29 -1
  113. package/dist/http/routes/background.js +29 -2
  114. package/dist/http/routes/capabilities.js +12 -10
  115. package/dist/http/routes/devices.d.ts +13 -19
  116. package/dist/http/routes/devices.js +21 -26
  117. package/dist/http/routes/mcp-reconnect.d.ts +3 -2
  118. package/dist/http/routes/mcp-reconnect.js +7 -5
  119. package/dist/http/routes/memory-consolidation.d.ts +1 -1
  120. package/dist/http/routes/memory-origin.d.ts +1 -1
  121. package/dist/http/routes/rules.js +3 -3
  122. package/dist/http/routes/runs.js +11 -7
  123. package/dist/http/routes/session-sync.js +110 -15
  124. package/dist/http/routes/sessions.d.ts +6 -0
  125. package/dist/http/routes/sessions.js +119 -13
  126. package/dist/http/routes/side-query.d.ts +12 -4
  127. package/dist/http/routes/side-query.js +6 -8
  128. package/dist/http/routes/tasks.js +800 -782
  129. package/dist/http/server.d.ts +61 -39
  130. package/dist/http/server.js +11 -0
  131. package/dist/http/session-inherit-env.d.ts +3 -4
  132. package/dist/http/session-inherit-env.js +1 -1
  133. package/dist/http/wire-types.d.ts +22 -18
  134. package/dist/identity-source.d.ts +1 -1
  135. package/dist/index.d.ts +3 -0
  136. package/dist/index.js +1 -0
  137. package/dist/leader/wire.d.ts +7 -0
  138. package/dist/leader/wire.js +5 -4
  139. package/dist/mcp-assertion-header.d.ts +51 -40
  140. package/dist/mcp-assertion-header.js +10 -11
  141. package/dist/mcp-authorization-face.d.ts +27 -0
  142. package/dist/mcp-authorization-face.js +15 -0
  143. package/dist/memory-operator-faces.d.ts +13 -4
  144. package/dist/memory-operator-faces.js +1 -17
  145. package/dist/memory-sync-client.d.ts +53 -14
  146. package/dist/memory-sync-client.js +187 -92
  147. package/dist/memory-sync.d.ts +1 -1
  148. package/dist/memory-sync.js +3 -2
  149. package/dist/model-route-gap.d.ts +6 -22
  150. package/dist/model-route-gap.js +7 -5
  151. package/dist/mods-declaration.d.ts +110 -0
  152. package/dist/mods-declaration.js +109 -0
  153. package/dist/observability/cost-taxonomy.d.ts +2 -2
  154. package/dist/observability/err-text.d.ts +5 -0
  155. package/dist/observability/err-text.js +9 -0
  156. package/dist/observability/fail-open.d.ts +17 -1
  157. package/dist/observability/fail-open.js +17 -1
  158. package/dist/observability/run-terminal.js +0 -1
  159. package/dist/observability/secret-env-scrub.d.ts +1 -1
  160. package/dist/observability/secret-env-scrub.js +1 -1
  161. package/dist/orchestration/workflow-notify-journal.js +4 -4
  162. package/dist/org-memory-admission.d.ts +4 -3
  163. package/dist/org-memory-admission.js +6 -6
  164. package/dist/parked-decide.d.ts +49 -11
  165. package/dist/parked-decide.js +25 -26
  166. package/dist/parked-revive-surface.d.ts +12 -15
  167. package/dist/parked-revive-surface.js +3 -6
  168. package/dist/peer-seat.d.ts +156 -0
  169. package/dist/peer-seat.js +273 -0
  170. package/dist/permission-rule-vocab.d.ts +7 -7
  171. package/dist/permission-rule-vocab.js +3 -3
  172. package/dist/plain-string-record.d.ts +38 -0
  173. package/dist/plain-string-record.js +38 -0
  174. package/dist/plugins/approval-ask-store-file.d.ts +7 -0
  175. package/dist/plugins/approval-ask-store-file.js +11 -0
  176. package/dist/plugins/approval-ask-store-memory.d.ts +6 -0
  177. package/dist/plugins/approval-ask-store-memory.js +12 -0
  178. package/dist/plugins/approval-ask-store-sql.d.ts +22 -5
  179. package/dist/plugins/approval-ask-store-sql.js +30 -12
  180. package/dist/plugins/background-agent-store-sql.js +6 -3
  181. package/dist/plugins/caching-session-store.d.ts +11 -2
  182. package/dist/plugins/caching-session-store.js +15 -3
  183. package/dist/plugins/checkpoint-store-sql.d.ts +21 -25
  184. package/dist/plugins/checkpoint-store-sql.js +13 -15
  185. package/dist/plugins/device-store-sql.d.ts +25 -51
  186. package/dist/plugins/device-store-sql.js +49 -103
  187. package/dist/plugins/execution-placement-sql.d.ts +151 -0
  188. package/dist/plugins/execution-placement-sql.js +223 -0
  189. package/dist/plugins/file-history-store-sql.d.ts +7 -6
  190. package/dist/plugins/file-history-store-sql.js +8 -7
  191. package/dist/plugins/file-run-store.d.ts +6 -1
  192. package/dist/plugins/file-run-store.js +4 -0
  193. package/dist/plugins/fork-routing-session-store.d.ts +6 -0
  194. package/dist/plugins/fork-routing-session-store.js +16 -0
  195. package/dist/plugins/k8s-pod-reaper.d.ts +2 -8
  196. package/dist/plugins/k8s-pod-reaper.js +3 -3
  197. package/dist/plugins/local-checkpoint-store.d.ts +1 -11
  198. package/dist/plugins/local-checkpoint-store.js +0 -1
  199. package/dist/plugins/local-session-store.d.ts +183 -45
  200. package/dist/plugins/local-session-store.js +412 -105
  201. package/dist/plugins/mailbox-store-sql.d.ts +11 -3
  202. package/dist/plugins/mailbox-store-sql.js +103 -15
  203. package/dist/plugins/memory-key-guards.d.ts +4 -0
  204. package/dist/plugins/memory-key-guards.js +1 -0
  205. package/dist/plugins/memory-run-store.d.ts +1 -1
  206. package/dist/plugins/memory-sync-store-pg.d.ts +16 -5
  207. package/dist/plugins/memory-sync-store-tidb.d.ts +3 -3
  208. package/dist/plugins/one-time-migrations.d.ts +117 -6
  209. package/dist/plugins/one-time-migrations.js +383 -14
  210. package/dist/plugins/outcome-ledger-sql.d.ts +2 -2
  211. package/dist/plugins/permission-rule-store-sql.d.ts +13 -15
  212. package/dist/plugins/permission-rule-store-sql.js +34 -29
  213. package/dist/plugins/pg-pool.d.ts +7 -3
  214. package/dist/plugins/pg-session-storage.d.ts +41 -22
  215. package/dist/plugins/pg-session-storage.js +136 -55
  216. package/dist/plugins/remote-env-adb.d.ts +22 -4
  217. package/dist/plugins/remote-env-adb.js +70 -62
  218. package/dist/plugins/remote-env-device.d.ts +32 -12
  219. package/dist/plugins/remote-env-device.js +18 -17
  220. package/dist/plugins/remote-env-e2b.d.ts +19 -3
  221. package/dist/plugins/remote-env-e2b.js +41 -42
  222. package/dist/plugins/remote-env-file-error.d.ts +2 -0
  223. package/dist/plugins/remote-env-file-error.js +1 -1
  224. package/dist/plugins/remote-env-host.d.ts +23 -8
  225. package/dist/plugins/remote-env-host.js +66 -70
  226. package/dist/plugins/remote-env-k8s.d.ts +13 -16
  227. package/dist/plugins/remote-env-k8s.js +61 -54
  228. package/dist/plugins/remote-env-local-docker.d.ts +30 -5
  229. package/dist/plugins/remote-env-local-docker.js +46 -59
  230. package/dist/plugins/remote-env-ssh.d.ts +18 -4
  231. package/dist/plugins/remote-env-ssh.js +72 -56
  232. package/dist/plugins/remote-shell.d.ts +100 -3
  233. package/dist/plugins/remote-shell.js +166 -2
  234. package/dist/plugins/retention-store-sql.d.ts +23 -8
  235. package/dist/plugins/retention-store-sql.js +13 -1
  236. package/dist/plugins/run-store-sql.d.ts +13 -3
  237. package/dist/plugins/run-store-sql.js +5 -0
  238. package/dist/plugins/schema-consistency.d.ts +15 -5
  239. package/dist/plugins/schema-consistency.js +14 -8
  240. package/dist/plugins/schema-drift-sentence.d.ts +27 -0
  241. package/dist/plugins/schema-drift-sentence.js +38 -0
  242. package/dist/plugins/session-fork.d.ts +63 -12
  243. package/dist/plugins/session-fork.js +38 -21
  244. package/dist/plugins/session-ownership.d.ts +7 -0
  245. package/dist/plugins/session-ownership.js +5 -0
  246. package/dist/plugins/session-row-sql.d.ts +22 -0
  247. package/dist/plugins/session-row-sql.js +81 -0
  248. package/dist/plugins/session-store.d.ts +1 -1
  249. package/dist/plugins/session-workspace-cell.d.ts +17 -0
  250. package/dist/plugins/session-workspace-cell.js +38 -0
  251. package/dist/plugins/store-backend.d.ts +1 -1
  252. package/dist/plugins/store-backend.js +26 -14
  253. package/dist/plugins/store-contracts.d.ts +25 -5
  254. package/dist/plugins/tidb-pool.d.ts +12 -7
  255. package/dist/plugins/tidb-pool.js +15 -3
  256. package/dist/plugins/tidb-session-store.d.ts +74 -28
  257. package/dist/plugins/tidb-session-store.js +132 -53
  258. package/dist/plugins/tool-result-store-sql.d.ts +16 -8
  259. package/dist/plugins/tool-result-store-sql.js +26 -17
  260. package/dist/plugins/worktree-isolation.d.ts +3 -2
  261. package/dist/plugins/worktree-isolation.js +1 -7
  262. package/dist/project-identity.d.ts +1 -14
  263. package/dist/project-identity.js +0 -4
  264. package/dist/project-memory.d.ts +9 -15
  265. package/dist/project-memory.js +5 -4
  266. package/dist/request-key-closure.js +1 -1
  267. package/dist/request-value-closure.d.ts +6 -7
  268. package/dist/request-value-closure.js +1 -3
  269. package/dist/rules-consent.js +6 -4
  270. package/dist/run-local.d.ts +6 -0
  271. package/dist/run-local.js +36 -26
  272. package/dist/runs.d.ts +9 -2
  273. package/dist/runs.js +9 -2
  274. package/dist/sandbox-git-plane.d.ts +2 -2
  275. package/dist/security.d.ts +38 -12
  276. package/dist/security.js +8 -2
  277. package/dist/server-secret-env.d.ts +15 -8
  278. package/dist/server-secret-env.js +4 -2
  279. package/dist/session-children-retire.d.ts +27 -9
  280. package/dist/session-children-retire.js +9 -5
  281. package/dist/session-incarnation.d.ts +19 -10
  282. package/dist/session-incarnation.js +5 -3
  283. package/dist/session-row.d.ts +114 -5
  284. package/dist/session-row.js +55 -5
  285. package/dist/session-titler.d.ts +2 -2
  286. package/dist/session-workspace.d.ts +130 -0
  287. package/dist/session-workspace.js +83 -0
  288. package/dist/spec-fields.d.ts +1 -1
  289. package/dist/standing-answer-grant.d.ts +2 -13
  290. package/dist/standing-answer-grant.js +0 -7
  291. package/dist/task-a2a.d.ts +44 -14
  292. package/dist/task-a2a.js +46 -20
  293. package/dist/task-cwd.d.ts +6 -1
  294. package/dist/task-mcp.d.ts +33 -20
  295. package/dist/task-mcp.js +46 -22
  296. package/dist/tool-approval.d.ts +38 -8
  297. package/dist/tool-approval.js +57 -13
  298. package/dist/trace/core-keyset-guard.d.ts +33 -18
  299. package/dist/trace/engine-notice-wire.d.ts +1 -1
  300. package/dist/trace/engine-notice-wire.js +2 -0
  301. package/dist/trace/exit-seat.d.ts +20 -1
  302. package/dist/trace/exit-seat.js +3 -0
  303. package/dist/trace/extension-call-face.d.ts +33 -0
  304. package/dist/trace/extension-call-face.js +39 -0
  305. package/dist/trace/project.d.ts +26 -7
  306. package/dist/trace/project.js +30 -3
  307. package/dist/trace/redact.js +42 -13
  308. package/dist/trace/self-minted-wire.d.ts +240 -12
  309. package/dist/trace/self-minted-wire.js +100 -4
  310. package/dist/trace/wire-projection-faces.d.ts +6 -6
  311. package/dist/trace/wire-projection-faces.js +6 -5
  312. package/dist/url-refusal.d.ts +1 -1
  313. package/dist/user-authored-text.d.ts +2 -2
  314. package/dist/user-authored-text.js +16 -1
  315. package/dist/web-admin-client/apply-effective.d.ts +17 -3
  316. package/dist/web-admin-client/apply-effective.js +20 -10
  317. package/dist/web-admin-client/center-request.d.ts +3 -2
  318. package/dist/web-admin-client/center-request.js +2 -3
  319. package/dist/web-admin-client/skills-mcp.d.ts +8 -4
  320. package/dist/web-admin-client/skills-mcp.js +32 -18
  321. package/dist/web-admin-client/types.d.ts +11 -0
  322. package/package.json +3 -3
  323. package/wire-changes/server@7.138.0.json +446 -0
  324. package/wire-changes/server@7.139.0.json +348 -0
  325. package/dist/http/device-placement.d.ts +0 -175
  326. package/dist/http/device-placement.js +0 -145
  327. package/dist/http/mcp-remote-text.d.ts +0 -5
  328. package/dist/http/mcp-remote-text.js +0 -7
@@ -11,12 +11,12 @@
11
11
  * 给那个会话的 run 组 `spec.tools` 的同一只工厂、同一个会话分区(任务清单店 `taskListStoreFor(root)` 与领队那一张是同一张;
12
12
  * 多租户的 WebFetch 过滤同样生效 ⇒ 子代同样拿不到)。座 = `(t) => tools.includes(t)`(身份判:同名副本不算)。
13
13
  * · **禁**用 boot 期单例填座:那只 bundle 没有会话,它的任务清单是一张私有表 —— 填了座,每个会话 / 租户的复活队友都会共用它。
14
- * · **根会话现在还是这张卡的那一位住客**才填座(codex r1 [high] + [ref] 加严):任务清单店只按会话 id 分区,而会话 id 由调用方自选、
15
- * 删后可被别人(或同一个人)重领 —— 根会话删了(或删了重建)之后赎回一张旧卡,按 id 重解出来的是**下一位住客**的清单。判据两条都要:
16
- * ① 根会话的属主与卡的权威 principal 逐字相等;② 赎回腿 claim 前 / 后两次读到的是**同一位住客**(化身逐字相等,
17
- * core 的 `compareOccupants` 答 `same`;`unknown` 不算)—— 只比属主比不出「同一个人删了重建」。任一条不成立 / 读数缺席 ⇒ 不交座
18
- * (fail-closed:子代不继承,继承工具上的 allow 回 422、审批待决,deny 照结)。赎回腿自己在两次读不一致时整条拒(`409 decide.not_parked`),
19
- * 这里的第 ② 条是同一条事实在交座这一处的判据(规则只写在这里,腿只负责读两次)。根删除时把子孙的 parked 卡一并作废 = [ref] 件 B
14
+ * · **根会话属于这张卡的租户**才填座(codex r1 [high]):任务清单店只按会话 id 分区,而会话 id 由调用方自选、删后可被别人(或同一个人)重领 ——
15
+ * 根会话删了(或删了重建)之后赎回一张旧卡,按 id 重解出来的是**下一位住客**的清单。两件事,两处判,各判一次:
16
+ * ① **住客判**(根会话现在还是这次决定判过的那一位:化身 ∧ 属主)只在赎回腿认领之后的唯一写口(执行放置,按解析器那一读的住客定址)——
17
+ * 不是那一位 ⇒ 赎回腿整条拒(`409 decide.not_parked`),走不到这里;只比属主比不出「同一个人删了重建」,所以住客判比化身;
18
+ * ② **租户判**在这里:写口放行的那一位(`rootOccupant`)的属主与卡的权威 principal 逐字相等(匿名会话 `null` ≡ 无 principal)。
19
+ * 不成立 ⇒ 不交座(fail-closed:子代不继承,继承工具上的 allow 回 422、审批待决,deny 照结)。根删除时把子孙的 parked 卡一并作废 = [ref] 件 B
20
20
  * (`boot/session-faces.ts` 的删除协调器)。
21
21
  * · 只对**一层**行填座(`rootSessionId` 在场且 = `parentSessionId`:派它的 run 就是根会话的 run,列表正是那个 run 的挂载)。更深的行
22
22
  * (孙代:派它的是一个子代,它的挂载是继承裁剪后的子集,行上读不出来)不填 —— 无座 = core 的 fail-closed 臂;
@@ -25,9 +25,9 @@
25
25
  * 座只判 bundle 实例的成员性,继承面是 `[...web, ...scaffolds]`,那两项差异不改变谁在面内。
26
26
  *
27
27
  * **属主来源([ref] / [ref])与交座是两个独立判据**:赎回工具的停驻挂点(复活的子代再要审批时停驻,`plugins/session-store.ts`)按委派继承律定宿主行属主,
28
- * 律要的是**委派树根的见证**:根 id(行上本来就带 `rootSessionId`)+ 判断那一刻读到的根行住客(赎回腿 claim 前后两读的那一位 —— 两读不是同一位时赎回腿整条拒,
29
- * 走不到这里;读数缺席 ⇒ 见证不带住客,挂点那一刻根行在 ⇒ 停驻否决)。所以**每一行**都交出它({@link ScenarioContext} 的 `delegationRoot`):交座的一层行以
30
- * `{ sessionId: root, delegationRoot }` 重解,不交座的行(更深的行、根住客已变的一层行、读数缺席)以 `{ delegationRoot }` 重解 —— 只喂停驻挂点,不挂任何会话级设施、
28
+ * 律要的是**委派树根的见证**:根 id(行上本来就带 `rootSessionId`)+ 判断那一刻的根行住客 = 写口放行的那一位(赎回腿解析器那一读;不是那一位时
29
+ * 写口整条拒,走不到这里)—— 见证恒带住客。所以**每一行**都交出它({@link ScenarioContext} 的 `delegationRoot`):交座的一层行以
30
+ * `{ sessionId: root, delegationRoot }` 重解,不交座的行(更深的行、根属主不是卡的租户的一层行)以 `{ delegationRoot }` 重解 —— 只喂停驻挂点,不挂任何会话级设施、
31
31
  * 不带 principal,交座判据(上面三条)一个字不动。行上没有根 ⇒ boot 期那只裸 Agent(没有根可交 ⇒ 律给哨兵)。挂点那一刻根行已不在 ⇒ 律给哨兵;在而不是见证的那一位
32
32
  * (批准之后删了、同 id 被重新登记)⇒ 停驻否决 —— 两件都在挂点那一侧判,与这里无关。
33
33
  */
@@ -41,15 +41,12 @@ export interface ParkedReviveSurface {
41
41
  readonly mountsCallerTool?: (tool: ToolSpec) => boolean;
42
42
  }
43
43
  /**
44
- * 按行解析赎回面的入参身份:`principal` = 权威 principal(取 checkpoint 的 scope 列,不取行上有损的 `row.scope`);`root` = 赎回腿对行的
45
- * 根会话读到的两次会话面(claim 之前 / 之后;属主 `null` = 匿名会话)。缺席 = 没有读数 ⇒ 不交座。
44
+ * 按行解析赎回面的入参身份:`principal` = 权威 principal(取 checkpoint 的 scope 列,不取行上有损的 `row.scope`);`rootOccupant` = 写口放行的那一位
45
+ * 根会话住客(赎回腿认领之前解析器那一次行读;属主 `null` = 匿名会话)—— 型上恒在:接赎回腿的部署必须带执行席。
46
46
  */
47
47
  export interface ParkedReviveSurfaceIdentity {
48
48
  readonly principal: string | undefined;
49
- readonly root?: {
50
- readonly before: SessionFace;
51
- readonly after: SessionFace;
52
- };
49
+ readonly rootOccupant: SessionFace;
53
50
  }
54
51
  /** 按行解析赎回面。 */
55
52
  export type ParkedReviveSurfaceFactory = (row: BackgroundAgentRecord, identity: ParkedReviveSurfaceIdentity) => ParkedReviveSurface;
@@ -1,4 +1,4 @@
1
- import { DEFAULT_SUBAGENT_TOOL_NAME, compareOccupants } from "@sema-agent/core";
1
+ import { DEFAULT_SUBAGENT_TOOL_NAME } from "@sema-agent/core";
2
2
  const agentOf = (tools) => tools.find((t) => t.name === DEFAULT_SUBAGENT_TOOL_NAME);
3
3
  export function createParkedReviveSurface(defaultScenario) {
4
4
  const bootTool = agentOf(defaultScenario({}).tools);
@@ -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.root?.after.incarnation ?? null };
12
+ const delegationRoot = { sessionId: root, 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)
@@ -18,9 +18,6 @@ export function createParkedReviveSurface(defaultScenario) {
18
18
  };
19
19
  }
20
20
  function holdsSeat(oneLevel, identity) {
21
- const face = identity.root;
22
- if (!oneLevel || face === undefined || compareOccupants(face.before, face.after) !== "same")
23
- return false;
24
- return (face.before.owner ?? undefined) === identity.principal && (face.after.owner ?? undefined) === identity.principal;
21
+ return oneLevel && (identity.rootOccupant.owner ?? undefined) === identity.principal;
25
22
  }
26
23
  //# sourceMappingURL=parked-revive-surface.js.map
@@ -0,0 +1,156 @@
1
+ import type { A2aServerSpec, McpServerSpec, PeerAnswer, PeerQuestion, PeerSeat, PeerSeatTarget } from "@sema-agent/core";
2
+ import { type Logger } from "./observability/logger.js";
3
+ import { type SafeText } from "./trace/exit-seat.js";
4
+ type StdioArm = Extract<McpServerSpec["transport"], {
5
+ kind: "stdio";
6
+ }>;
7
+ type HttpArm = Extract<McpServerSpec["transport"], {
8
+ kind: "http";
9
+ }>;
10
+ /** The pieces a declaration's sends carry, as written: header name → value (http MCP, A2A) or variable → value (stdio MCP). */
11
+ export type PeerPieces = Readonly<Record<string, string>>;
12
+ /**
13
+ * An MCP server declaration before the desk has seen it: the engine's declaration without its credential reference, beside the
14
+ * pieces. `credentials?: never` sits on each transport arm (where the engine keeps the reference) so that a declaration that already
15
+ * carries one cannot be passed back in as a draft: `Omit` alone would not refuse it, because excess members are judged only on fresh
16
+ * object literals. Without `exactOptionalPropertyTypes` an explicit `credentials: undefined` still passes; it carries no reference.
17
+ */
18
+ export type McpDraftSpec = Omit<McpServerSpec, "transport"> & {
19
+ transport: (Omit<StdioArm, "credentials"> & {
20
+ credentials?: never;
21
+ }) | (Omit<HttpArm, "credentials"> & {
22
+ credentials?: never;
23
+ });
24
+ };
25
+ /** An MCP draft: the declaration and the pieces its sends carry (absent or empty ⇒ nothing to register). */
26
+ export interface McpDraft {
27
+ readonly spec: McpDraftSpec;
28
+ readonly pieces?: PeerPieces;
29
+ }
30
+ /** An A2A peer declaration before the desk has seen it; the engine keeps an A2A peer's reference at the top level. */
31
+ export type A2aDraftSpec = Omit<A2aServerSpec, "credentials"> & {
32
+ credentials?: never;
33
+ };
34
+ /** An A2A draft: the declaration and the headers its sends carry (absent or empty ⇒ nothing to register). */
35
+ export interface A2aDraft {
36
+ readonly spec: A2aDraftSpec;
37
+ readonly pieces?: PeerPieces;
38
+ }
39
+ export type PeerDraft = McpDraft | A2aDraft;
40
+ /** The reference {@link PeerDesk.declare} mints (the `handle` kind of the reference the engine asks with): opaque, the same for the same
41
+ * content within one process. */
42
+ export type PeerHandle = Extract<NonNullable<PeerSeatTarget["credentials"]>, {
43
+ kind: "handle";
44
+ }>;
45
+ /**
46
+ * Who a send goes to, as the seat binds an answer to it: the engine's question target without the reference itself and without an
47
+ * A2A send's `destination` (the engine owns which declared origin a send may reach). An http MCP server = name, url, mount
48
+ * fingerprint; a stdio one = name, the whole start command (absent `args` ≡ `[]`), fingerprint; an A2A peer = name, url, `cardUrl`.
49
+ */
50
+ export type PeerBinding = {
51
+ readonly protocol: "mcp";
52
+ readonly transport: "http";
53
+ readonly server: string;
54
+ readonly url: string;
55
+ readonly fingerprint?: string;
56
+ } | {
57
+ readonly protocol: "mcp";
58
+ readonly transport: "stdio";
59
+ readonly server: string;
60
+ readonly command: string;
61
+ readonly args: readonly string[];
62
+ readonly fingerprint?: string;
63
+ } | {
64
+ readonly protocol: "a2a";
65
+ readonly peer: string;
66
+ readonly url: string;
67
+ readonly cardUrl?: string;
68
+ };
69
+ /** Why the seat refused a question. Closed: a new word is a new arm of `answer`'s switch. */
70
+ export type PeerSeatRefusalReason = "handle_not_held" | "binding_mismatch" | "kind_env" | "kind_vault" | "kind_assertion" | "kind_unknown";
71
+ /**
72
+ * The desk's own failures, each loud:
73
+ * · `peer_lease_released` — a released lease was used to declare or to extend (a scope released before its last use);
74
+ * · `peer_lease_foreign` — a lease another desk opened was used here (its release would never reach this desk's entries);
75
+ * · `peer_lease_scope_unknown` — a lease was asked for with a scope word outside {@link PEER_LEASE_SCOPES};
76
+ * · `peer_binding_conflict` — two different contents were minted one id (only a digest collision does that); the entry stands;
77
+ * · `peer_seat_refused` — the seat refused a question (the engine reads any throw as the seat not furnishing that send).
78
+ * No message names a value or an id.
79
+ */
80
+ export type PeerDeskErrorCode = "peer_lease_released" | "peer_lease_foreign" | "peer_lease_scope_unknown" | "peer_binding_conflict" | "peer_seat_refused";
81
+ export declare class PeerDeskError extends Error {
82
+ readonly code: PeerDeskErrorCode;
83
+ constructor(code: PeerDeskErrorCode, message: string);
84
+ }
85
+ /**
86
+ * Which host scope a lease is opened for: the request funnel of a fresh submission, the funnel of a resumed leg, a probe walk, the
87
+ * roster (held for the process's life). A closed set, so the desk's own error text can name a scope without echoing caller text.
88
+ */
89
+ export declare const PEER_LEASE_SCOPES: readonly ["fresh", "resume", "probe", "roster"];
90
+ export type PeerLeaseScope = (typeof PEER_LEASE_SCOPES)[number];
91
+ /** A host holder. `release` is idempotent; `extend` opens a further lease that holds every entry this one holds now. */
92
+ export interface PeerLease {
93
+ /** A further lease holding what this one holds at this moment; this one released ⇒ throws `peer_lease_released`. */
94
+ extend(): PeerLease;
95
+ /** Stop holding. Entries no other holder holds are dropped now. A second call does nothing. */
96
+ release(): void;
97
+ }
98
+ /** Live entries and their holders — host leases and engine holds — summed across entries: the registry's whole observable state. */
99
+ export interface PeerDeskSize {
100
+ readonly entries: number;
101
+ readonly holders: number;
102
+ }
103
+ /**
104
+ * The engine's seat as the desk is it (`RunnerDeps.peerSeat`, the probe's and the panels' `peerSeat`): `answer` the pieces for one send, or a
105
+ * throw (refused: logged as one `peer_seat_refused` row naming protocol, peer and reason only) — it reads no signal (the desk does no IO);
106
+ * `hold` / `release` one engine holder of a reference this desk minted. Every hold the desk cannot count throws — a reference this desk did not
107
+ * mint (any kind but `handle`), a handle whose entry is gone — and so does a release with no counted hold: the engine reads a hold that returns
108
+ * as held, so a quiet return would be a hold nobody keeps (security axis, fail-closed). The engine reports each throw once as `peer-seat-hold`;
109
+ * a refused hold holds nothing and gets no release, a refused release changes nothing.
110
+ */
111
+ export interface PeerDeskSeat extends PeerSeat {
112
+ answer(question: PeerQuestion, signal?: AbortSignal): Promise<PeerAnswer>;
113
+ }
114
+ export interface PeerDesk {
115
+ readonly seat: PeerDeskSeat;
116
+ /**
117
+ * Register a draft's pieces under `lease` and mint the reference the declaration carries to the engine. No pieces ⇒ nothing is
118
+ * registered and nothing is minted (`undefined`: the declaration goes without a reference). The same content (binding, lane,
119
+ * pieces by name) mints the same id in one process, so two legs declaring one body share one entry and each holds it.
120
+ */
121
+ declare(draft: PeerDraft, lease: PeerLease): PeerHandle | undefined;
122
+ /** Open a host lease for `scope` (named in the desk's own error text; a word outside the set ⇒ throws `peer_lease_scope_unknown`). */
123
+ lease(scope: PeerLeaseScope): PeerLease;
124
+ size(): PeerDeskSize;
125
+ }
126
+ export interface PeerDeskOptions {
127
+ /** Where refusal rows go (default: a JSON-line logger). */
128
+ readonly log?: Logger;
129
+ }
130
+ /** The binding of a draft, or of the target the engine asks about; both read the same three rows. */
131
+ export declare function bindingOf(peer: PeerDraft | PeerSeatTarget): PeerBinding;
132
+ export declare function createPeerDesk(opts?: PeerDeskOptions): PeerDesk;
133
+ /**
134
+ * **The one minting point** every entrance calls — the roster (under the roster lease), the request surface (under the leg's host lease) and
135
+ * the probe walk: register `draft`'s pieces on `desk` under `lease` and hand back the declaration the engine gets, carrying the minted
136
+ * reference where the engine keeps it (an MCP server's transport arm, an A2A peer's top level) and no piece value anywhere. A draft with no
137
+ * pieces registers nothing and goes as drafted (no reference member). Where the reference sits is written here and nowhere else.
138
+ */
139
+ export declare function mintDeclaration(desk: Pick<PeerDesk, "declare">, draft: McpDraft, lease: PeerLease): McpServerSpec;
140
+ export declare function mintDeclaration(desk: Pick<PeerDesk, "declare">, draft: A2aDraft, lease: PeerLease): A2aServerSpec;
141
+ /**
142
+ * **The one exit** for an engine judge's sentence about a peer declaration (an MCP server's or an A2A peer's `toolAxes` / `toolFaces` /
143
+ * `sessionMetaKey` / start bound …) on its way into this server's refusal text or an operator line: the engine words what the declaring side
144
+ * wrote (a tool name, a key — which a mistake can make a credential), so the sentence leaves through the exit seat's `peer-declaration-sentence`
145
+ * row (credential shapes become marks, the sentence stays the engine's). The request surface's judges, the replay reread lines and the roster's
146
+ * skip lines all use this one.
147
+ */
148
+ export declare function peerSentence(problem: string): SafeText;
149
+ /**
150
+ * Hold `lease`'s entries for as long as `drive` runs: a further lease is opened before `drive` is called (synchronously, so before
151
+ * the caller's own scope can release) and released when `drive` settles — resolved, rejected or thrown synchronously. A released
152
+ * lease rejects with `peer_lease_released` and `drive` is not called.
153
+ */
154
+ export declare function heldFor<T>(lease: PeerLease, drive: () => Promise<T>): Promise<T>;
155
+ export {};
156
+ //# sourceMappingURL=peer-seat.d.ts.map
@@ -0,0 +1,273 @@
1
+ import { createHmac, randomBytes } from "node:crypto";
2
+ import { createLogger } from "./observability/logger.js";
3
+ import { seatText } from "./trace/exit-seat.js";
4
+ export class PeerDeskError extends Error {
5
+ code;
6
+ constructor(code, message) {
7
+ super(message);
8
+ this.code = code;
9
+ this.name = "PeerDeskError";
10
+ }
11
+ }
12
+ export const PEER_LEASE_SCOPES = ["fresh", "resume", "probe", "roster"];
13
+ export function bindingOf(peer) {
14
+ if ("spec" in peer) {
15
+ const spec = peer.spec;
16
+ if (!("transport" in spec))
17
+ return a2aBinding(spec.name, spec.url, spec.cardUrl);
18
+ const t = spec.transport;
19
+ switch (t.kind) {
20
+ case "http":
21
+ return httpBinding(spec.name, t.url, spec.fingerprint);
22
+ case "stdio":
23
+ return stdioBinding(spec.name, t.command, t.args ?? [], spec.fingerprint);
24
+ default: {
25
+ const unknownArm = t;
26
+ throw new Error(`peer desk: an MCP draft with a transport this desk does not know (${discriminantsOf(unknownArm)})`);
27
+ }
28
+ }
29
+ }
30
+ switch (peer.protocol) {
31
+ case "mcp":
32
+ switch (peer.transport) {
33
+ case "http":
34
+ return httpBinding(peer.server, peer.url, peer.fingerprint);
35
+ case "stdio":
36
+ return stdioBinding(peer.server, peer.command, peer.args, peer.fingerprint);
37
+ default: {
38
+ const unknownArm = peer;
39
+ throw new Error(`peer desk: a question about an MCP transport this desk does not know (${discriminantsOf(unknownArm)})`);
40
+ }
41
+ }
42
+ case "a2a":
43
+ return a2aBinding(peer.peer, peer.url, peer.cardUrl);
44
+ default: {
45
+ const unknownArm = peer;
46
+ throw new Error(`peer desk: a question about a protocol this desk does not know (${discriminantsOf(unknownArm)})`);
47
+ }
48
+ }
49
+ }
50
+ function httpBinding(server, url, fingerprint) {
51
+ return Object.freeze({ protocol: "mcp", transport: "http", server, url, ...(fingerprint !== undefined ? { fingerprint } : {}) });
52
+ }
53
+ function stdioBinding(server, command, args, fingerprint) {
54
+ return Object.freeze({ protocol: "mcp", transport: "stdio", server, command, args: Object.freeze([...args]), ...(fingerprint !== undefined ? { fingerprint } : {}) });
55
+ }
56
+ function a2aBinding(peer, url, cardUrl) {
57
+ return Object.freeze({ protocol: "a2a", peer, url, ...(cardUrl !== undefined ? { cardUrl } : {}) });
58
+ }
59
+ function discriminantsOf(value) {
60
+ if (typeof value !== "object" || value === null)
61
+ return typeof value;
62
+ const at = (key) => {
63
+ const v = Reflect.get(value, key);
64
+ return typeof v === "string" ? JSON.stringify(v.slice(0, 40)) : typeof v;
65
+ };
66
+ return `protocol ${at("protocol")}, transport ${at("transport")}, kind ${at("kind")}`;
67
+ }
68
+ function laneOf(binding) {
69
+ return binding.protocol === "a2a" ? "a2a" : binding.transport;
70
+ }
71
+ function rowsOf(record) {
72
+ return Object.entries(record).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
73
+ }
74
+ function canonicalOf(binding, lane, pieces) {
75
+ return JSON.stringify([rowsOf(binding), lane, rowsOf(pieces)]);
76
+ }
77
+ function sameBinding(a, b) {
78
+ return JSON.stringify(rowsOf(a)) === JSON.stringify(rowsOf(b));
79
+ }
80
+ const ID_KEY = randomBytes(32);
81
+ function mintId(canonical) {
82
+ return `h1.${createHmac("sha256", ID_KEY).update(canonical).digest("base64url")}`;
83
+ }
84
+ function copyPieces(given) {
85
+ if (given === undefined)
86
+ return undefined;
87
+ const names = Object.keys(given);
88
+ if (names.length === 0)
89
+ return undefined;
90
+ const copy = Object.create(null);
91
+ for (const name of names)
92
+ copy[name] = given[name];
93
+ return Object.freeze(copy);
94
+ }
95
+ export function createPeerDesk(opts = {}) {
96
+ const log = opts.log ?? createLogger();
97
+ const table = new Map();
98
+ const leases = new WeakMap();
99
+ let lastLeaseId = 0;
100
+ const entryHeld = (id) => {
101
+ const entry = table.get(id);
102
+ if (entry === undefined)
103
+ throw new Error("peer desk: a live lease holds an entry the table no longer has");
104
+ return entry;
105
+ };
106
+ const dropIfUnheld = (id, entry) => {
107
+ if (entry.hosts.size === 0 && entry.engineHolds === 0)
108
+ table.delete(id);
109
+ };
110
+ const liveState = (lease) => {
111
+ const state = leases.get(lease);
112
+ if (state === undefined)
113
+ throw new PeerDeskError("peer_lease_foreign", "peer desk: a lease another desk opened was used here");
114
+ const held = state.held;
115
+ if (held === undefined)
116
+ throw new PeerDeskError("peer_lease_released", `peer desk: a "${state.scope}" lease was used after it was released`);
117
+ return { id: state.id, scope: state.scope, held };
118
+ };
119
+ const openLease = (scope, holds) => {
120
+ const state = { id: ++lastLeaseId, scope, held: new Set(holds) };
121
+ for (const id of holds)
122
+ entryHeld(id).hosts.add(state.id);
123
+ const lease = Object.freeze({
124
+ extend: () => openLease(state.scope, liveState(lease).held),
125
+ release: () => {
126
+ const held = state.held;
127
+ if (held === undefined)
128
+ return;
129
+ state.held = undefined;
130
+ for (const id of held) {
131
+ const entry = entryHeld(id);
132
+ entry.hosts.delete(state.id);
133
+ dropIfUnheld(id, entry);
134
+ }
135
+ },
136
+ });
137
+ leases.set(lease, state);
138
+ return lease;
139
+ };
140
+ const refuse = (target, reason) => {
141
+ const peer = target.protocol === "a2a" ? target.peer : target.server;
142
+ log.warn("peer_seat_refused", { protocol: target.protocol, peer, reason });
143
+ throw new PeerDeskError("peer_seat_refused", `peer desk: the seat refused this send (${reason})`);
144
+ };
145
+ const carry = (entry) => {
146
+ switch (entry.lane) {
147
+ case "http":
148
+ case "a2a":
149
+ return { kind: "carry", headers: entry.pieces };
150
+ case "stdio":
151
+ return { kind: "carry", env: entry.pieces };
152
+ }
153
+ };
154
+ const handleIdOf = (ref) => {
155
+ switch (ref.kind) {
156
+ case "handle":
157
+ return ref.id;
158
+ case "env":
159
+ case "vault":
160
+ case "assertion":
161
+ return undefined;
162
+ default: {
163
+ const unknownKind = ref;
164
+ void unknownKind;
165
+ return undefined;
166
+ }
167
+ }
168
+ };
169
+ const seat = Object.freeze({
170
+ hold: (ref) => {
171
+ const id = handleIdOf(ref);
172
+ if (id === undefined)
173
+ throw new Error("peer desk: hold of a reference this desk did not mint — not held");
174
+ const entry = table.get(id);
175
+ if (entry === undefined)
176
+ throw new Error("peer desk: hold of a handle whose entry is gone — not held (a hold never revives an entry)");
177
+ entry.engineHolds += 1;
178
+ },
179
+ release: (ref) => {
180
+ const id = handleIdOf(ref);
181
+ if (id === undefined)
182
+ throw new Error("peer desk: release of a reference this desk did not mint — nothing released");
183
+ const entry = table.get(id);
184
+ if (entry === undefined || entry.engineHolds === 0)
185
+ throw new Error("peer desk: release of a handle with no counted engine hold — nothing released");
186
+ entry.engineHolds -= 1;
187
+ dropIfUnheld(id, entry);
188
+ },
189
+ answer: async (question) => {
190
+ const target = question.peer;
191
+ const ref = target.credentials;
192
+ if (ref === undefined)
193
+ return { kind: "carry" };
194
+ switch (ref.kind) {
195
+ case "handle": {
196
+ const entry = table.get(ref.id);
197
+ if (entry === undefined)
198
+ return refuse(target, "handle_not_held");
199
+ if (!sameBinding(entry.binding, bindingOf(target)))
200
+ return refuse(target, "binding_mismatch");
201
+ return carry(entry);
202
+ }
203
+ case "env":
204
+ return refuse(target, "kind_env");
205
+ case "vault":
206
+ return refuse(target, "kind_vault");
207
+ case "assertion":
208
+ return refuse(target, "kind_assertion");
209
+ default: {
210
+ const unknownKind = ref;
211
+ void unknownKind;
212
+ return refuse(target, "kind_unknown");
213
+ }
214
+ }
215
+ },
216
+ });
217
+ return Object.freeze({
218
+ seat,
219
+ declare: (draft, lease) => {
220
+ const pieces = copyPieces(draft.pieces);
221
+ const binding = pieces === undefined ? undefined : bindingOf(draft);
222
+ const holder = liveState(lease);
223
+ if (pieces === undefined || binding === undefined)
224
+ return undefined;
225
+ const lane = laneOf(binding);
226
+ const canonical = canonicalOf(binding, lane, pieces);
227
+ const id = mintId(canonical);
228
+ const known = table.get(id);
229
+ if (known === undefined) {
230
+ table.set(id, { binding, lane, pieces, hosts: new Set([holder.id]), engineHolds: 0 });
231
+ }
232
+ else if (canonicalOf(known.binding, known.lane, known.pieces) !== canonical) {
233
+ throw new PeerDeskError("peer_binding_conflict", "peer desk: two different declarations were minted one id; the registered one stands");
234
+ }
235
+ else {
236
+ known.hosts.add(holder.id);
237
+ }
238
+ holder.held.add(id);
239
+ return Object.freeze({ kind: "handle", id });
240
+ },
241
+ lease: (scope) => {
242
+ if (!PEER_LEASE_SCOPES.includes(scope))
243
+ throw new PeerDeskError("peer_lease_scope_unknown", "peer desk: a lease was asked for with a scope outside the closed set");
244
+ return openLease(scope, new Set());
245
+ },
246
+ size: () => {
247
+ let holders = 0;
248
+ for (const entry of table.values())
249
+ holders += entry.hosts.size + entry.engineHolds;
250
+ return { entries: table.size, holders };
251
+ },
252
+ });
253
+ }
254
+ export function mintDeclaration(desk, draft, lease) {
255
+ const credentials = desk.declare(draft, lease);
256
+ const spec = draft.spec;
257
+ if ("transport" in spec)
258
+ return credentials === undefined ? spec : { ...spec, transport: { ...spec.transport, credentials } };
259
+ return credentials === undefined ? spec : { ...spec, credentials };
260
+ }
261
+ export function peerSentence(problem) {
262
+ return seatText("peer-declaration-sentence", problem);
263
+ }
264
+ export async function heldFor(lease, drive) {
265
+ const own = lease.extend();
266
+ try {
267
+ return await drive();
268
+ }
269
+ finally {
270
+ own.release();
271
+ }
272
+ }
273
+ //# sourceMappingURL=peer-seat.js.map
@@ -1,14 +1,14 @@
1
1
  import type { RuleBehavior } from "@sema-agent/core";
2
2
  /**
3
- * `PersistedRuleTool` **不再是闭词表**(core 7.9.0 / [ref]):工具名自 core 的**目录**派生
4
- * (`pathTarget` 声明 ⇒ 路径文法;`Bash` ⇒ 命令文法),型是 `string`。于是本文件对它的处置从
5
- * 「转录一份名单」换成「转 core 的**谓词**」——名单在 core 的目录里,抄一份的那天目录加一个带
6
- * `pathTarget` 的工具,本仓会静默拒收一整类合法行(正是本文件顶注说的那个病)。
3
+ * 一张审批卡的**一键整工具同意**能落在这只工具名上吗 —— 仅当没有任何规则词替这个名字说话:它自己没有规则文法(`ruleToolGrammarOf` 答 undefined),
4
+ * 也不在 shell 面上(`COARSE_SHELL_TOOLS`:`Bash` 与 `Monitor`,它们的命令按 `Bash(…)` 规则判)。引擎为卡铸整工具候选恰在这个条件下(没有规则文法、
5
+ * 也不回答任何规则词的调用);一张 shell 卡上的一键整工具同意 = 对每一个程序的常驻许可,路径 / 名字 / 主机工具的卡各有自己的那一形。
7
6
  *
8
- * 谓词方向仍是 fail-CLOSED:`ruleToolGrammarOf` 认不出的名字**不是**一条可持久化规则的工具位
9
- * (既非命令文法也非路径文法 ⇒ 这条行永远匹配不上任何调用),两只店与票口一律拒。
7
+ * 读者:卡素材铸点的派生事实一致性(`tool-approval.ts`:整工具候选的工具过不了 ⇒ 丢整只候选,卡上不渲一格按不动的「不再询问」)。
8
+ * 卡编辑面不读它:引擎的编辑门对任何整工具规则一律拒(编辑只改命令规则),编辑面的形闭集(`rules-consent.ts`)跟着恒拒 —— 那张闭集里「shell 卡上一键整只 shell」
9
+ * 的理由就是本谓词的理由,不另写一份判据。方向 fail-closed:答不出 ⇒ 不是。
10
10
  */
11
- export declare function isPersistedRuleTool(name: string): boolean;
11
+ export declare function cardTakesWholeToolConsent(tool: string): boolean;
12
12
  /**
13
13
  * [ref] —— `RuleBehavior` 每一员在**无同意仪式的直接写口**(`POST /v1/rules`)上的逐员表态。
14
14
  *
@@ -1,6 +1,6 @@
1
- import { ruleToolGrammarOf } from "@sema-agent/core";
2
- export function isPersistedRuleTool(name) {
3
- return ruleToolGrammarOf(name) !== undefined;
1
+ import { COARSE_SHELL_TOOLS, ruleToolGrammarOf } from "@sema-agent/core";
2
+ export function cardTakesWholeToolConsent(tool) {
3
+ return ruleToolGrammarOf(tool) === undefined && !COARSE_SHELL_TOOLS.includes(tool);
4
4
  }
5
5
  export const DIRECT_RULE_WRITE_ADMITS = {
6
6
  allow: false,
@@ -22,4 +22,42 @@ export type PlainStringRecordRead = {
22
22
  * 数据属性取值,访问器调一次。先判每个成员的值(`__proto__` 名的成员也判:它的值不是串 ⇒ 整只不交,与 7.131.x 同),再剔 `__proto__` 名。调用方的对象不被交出去、也不被改。
23
23
  */
24
24
  export declare function readPlainStringRecord(v: unknown): PlainStringRecordRead;
25
+ /**
26
+ * 请求面一份声明里每个键的**去处**(闭集;{@link splitDeclaration} 对它穷尽 `switch`):
27
+ * · `engine` —— 进交给引擎的声明;
28
+ * · `seat` —— 进草稿的件:由部署的对端座在发送时按原字节作答,交给引擎的声明里不带(只有值是「名 → 串」记录的键能标它);
29
+ * · `withheld` —— 承载、不发送:声明与件两边都不进(由声明这一格的属主另说一行)。
30
+ */
31
+ export type DeclarationKeyDisposition = "engine" | "seat" | "withheld";
32
+ /**
33
+ * 请求形 `R` 的**处置表**:`R` 的每个键(可选键也算)恰一格 —— 写成 `as const satisfies DeclarationDispositionTable<R>` 的对象字面量,于是请求形多一个键而表没写 ⇒ 缺属性编译红,
34
+ * 表里写一个请求形上没有的词 ⇒ 多余属性编译红(三道编译格的第一道,两向)。只有值是「名 → 串」记录的键可以标 `seat`(别的键标它 ⇒ 编译红)。
35
+ * 表的书写序就是键闭集的序({@link declarationKeys}):受理门按它判序。
36
+ */
37
+ export type DeclarationDispositionTable<R> = {
38
+ readonly [K in keyof Required<R>]: NonNullable<R[K]> extends Readonly<Record<string, string>> ? DeclarationKeyDisposition : Exclude<DeclarationKeyDisposition, "seat">;
39
+ };
40
+ /** 处置表里标 `engine` 的键。 */
41
+ export type EngineKeysOf<T> = {
42
+ [K in keyof T]: T[K] extends "engine" ? K : never;
43
+ }[keyof T];
44
+ /**
45
+ * 三道编译格的后两道(引擎一侧)的**缺口**,一个型名、两向;用处写成 `AssertAllKeysHandled<EngineSideGap<…>>`(缺口非空 ⇒ 编译红,点名那个键):
46
+ * ② 引擎形上没标 `engine` 的键恰是 `Unbound`(引擎形长出一个键而表没跟 ⇒ 缺口里有它);
47
+ * ③ 标 `engine` 的键与 `Unbound` 都仍在引擎形上(引擎退役一个键、或退役了 `Unbound` 那一位 ⇒ 缺口里有它)。`Unbound` = 引擎形上请求面不写、由宿主铸的位(凭据引用)。
48
+ * 只证键集,不证值型与运行期转换完备(值型由 {@link splitDeclaration} 的返回型交给声明型去判)。
49
+ */
50
+ export type EngineSideGap<T, EngineArm, Unbound extends PropertyKey> = Exclude<keyof EngineArm, EngineKeysOf<T> | Unbound> | Exclude<EngineKeysOf<T> | Unbound, keyof EngineArm>;
51
+ /** 一张处置表的键,按表的书写序(冻结;受理门的键闭集与判序读它,不另写第二份清单)。 */
52
+ export declare function declarationKeys<T extends object>(table: T): ReadonlyArray<keyof T & string>;
53
+ /**
54
+ * 按处置表拆一份请求面声明:标 `engine` 的成员进 `engine`(交给引擎的声明那一部分),标 `seat` 的那一张记录进 `pieces`(空记录 = 没有件,`pieces` 缺席),
55
+ * 标 `withheld` 的成员两边都不进;值为 `undefined` 的成员按缺席读。
56
+ * 请求面声明的唯一拆法 —— MCP 两臂(`toMcpDraft`)与 A2A 对端(`toA2aDraft`)同调这一只,所以「哪个键去哪」只有表一处说,代码里没有第二份键名。
57
+ * 运行期漏臂(响亮):成员不在表里(表的型挡住了写得出的形,只有绕过型的调用方走得到)/ 一份声明有两张标 `seat` 的记录 ⇒ 抛;两句都不带值。
58
+ */
59
+ export declare function splitDeclaration<R extends object, T extends DeclarationDispositionTable<R>>(declaration: R, table: T): {
60
+ readonly engine: Pick<R, EngineKeysOf<T> & keyof R>;
61
+ readonly pieces?: Readonly<Record<string, string>>;
62
+ };
25
63
  //# sourceMappingURL=plain-string-record.d.ts.map
@@ -29,4 +29,42 @@ export function readPlainStringRecord(v) {
29
29
  }
30
30
  return { ok: true, record, stripped };
31
31
  }
32
+ export function declarationKeys(table) {
33
+ return Object.freeze(Object.keys(table));
34
+ }
35
+ export function splitDeclaration(declaration, table) {
36
+ const dispositions = table;
37
+ const engine = {};
38
+ let pieces;
39
+ let seatSeen = false;
40
+ for (const [key, value] of Object.entries(declaration)) {
41
+ if (value === undefined)
42
+ continue;
43
+ const disposition = Object.hasOwn(dispositions, key) ? dispositions[key] : undefined;
44
+ switch (disposition) {
45
+ case "engine":
46
+ engine[key] = value;
47
+ break;
48
+ case "seat": {
49
+ if (seatSeen)
50
+ throw new Error("request declaration: two members are marked as pieces in one disposition table");
51
+ seatSeen = true;
52
+ const record = value;
53
+ if (Object.keys(record).length > 0)
54
+ pieces = record;
55
+ break;
56
+ }
57
+ case "withheld":
58
+ break;
59
+ case undefined:
60
+ throw new Error(`request declaration: a member the disposition table does not classify (${JSON.stringify(key.slice(0, 40))})`);
61
+ default: {
62
+ const unknownDisposition = disposition;
63
+ void unknownDisposition;
64
+ throw new Error("request declaration: a disposition outside the closed set");
65
+ }
66
+ }
67
+ }
68
+ return { engine: engine, ...(pieces === undefined ? {} : { pieces }) };
69
+ }
32
70
  //# sourceMappingURL=plain-string-record.js.map
@@ -1,3 +1,4 @@
1
+ import { type ExecutionLane } from "../execution-lane-caps.js";
1
2
  import type { AskState } from "../approval-ask-machine.js";
2
3
  import type { SettleCause } from "../approval-deny-reasons.js";
3
4
  import type { AskCallCoordinate, AskRow, AskTerminalClaimIntent, AskTerminalClaimOpts, AskTerminalClaimOutcome, AskTransitionPatch, ApprovalAskStore, BatchRow, BindGateInput, BindResult, DecideAskInput, DecideResult, EnsureAskResult, ExpireResult, NewAskRow } from "./approval-ask-store-sql.js";
@@ -209,6 +210,12 @@ export declare class FileApprovalAskStore implements ApprovalAskStore {
209
210
  };
210
211
  /** 释放追加 fd(`LocalBackend.close` → 优雅重启重开)。不包 try/catch 的理由同
211
212
  * {@link FileApprovalNonceStore.dispose}:core 的 `AppendLog.close()` 自己就是全函数。 */
213
+ /**
214
+ * 执行放置切换 ④b 的 local 腿(一次性迁移登记表 M32;`LocalBackend.runOneTimeMigrations` 在任何读者之前调):升级前写下的问(日志 `ensureAsk` 记录行 /
215
+ * 压实快照行没有 `executionLane`)记成本部署车道表的唯一项,然后**一次压实**把整本账重写成带车道的快照(压实 = 既有整本原子重写)。
216
+ * 没有缺车道的行 ⇒ 零写。车道表不是一元而还有缺车道的行 ⇒ 抛与 SQL 同一只拒(`backfill_ambiguous`,由调用方铸),一个字节没写。
217
+ */
218
+ backfillExecutionLanes(lanes: readonly ExecutionLane[], refuse: (pending: number) => never): number;
212
219
  dispose(): void;
213
220
  }
214
221
  export {};