@sema-agent/server 7.132.0 → 7.134.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 (473) hide show
  1. package/MIGRATION.md +353 -0
  2. package/README.md +8 -9
  3. package/README.zh-CN.md +7 -6
  4. package/USAGE.md +301 -64
  5. package/deploy/sema-up/README.md +35 -1
  6. package/deploy/sema-up/chart/Chart.yaml +1 -1
  7. package/deploy/sema-up/chart/templates/NOTES.txt +15 -0
  8. package/deploy/sema-up/chart/templates/_helpers.tpl +129 -0
  9. package/deploy/sema-up/chart/templates/sealed-key.yaml +24 -0
  10. package/deploy/sema-up/chart/templates/server-v4pro.yaml +19 -2
  11. package/deploy/sema-up/chart/templates/server.yaml +19 -2
  12. package/deploy/sema-up/chart/values-full-single.yaml +5 -1
  13. package/deploy/sema-up/chart/values.yaml +34 -0
  14. package/deploy/sema-up/stack/compose.yaml +3 -0
  15. package/deploy/sema-up/stack/sema-stack-cluster.yaml +4 -2
  16. package/dist/adoption/plan.d.ts +3 -2
  17. package/dist/adoption/plan.js +7 -5
  18. package/dist/adoption/runner.js +3 -10
  19. package/dist/adoption/sql.d.ts +3 -2
  20. package/dist/adoption/sql.js +3 -5
  21. package/dist/adoption/wire.d.ts +4 -1
  22. package/dist/adoption/wire.js +1 -0
  23. package/dist/approval-ask-audit-store.d.ts +32 -14
  24. package/dist/approval-ask-audit-store.js +272 -74
  25. package/dist/approval-ask-machine.d.ts +15 -0
  26. package/dist/approval-ask-machine.js +6 -0
  27. package/dist/approval-ask-retention.d.ts +136 -0
  28. package/dist/approval-ask-retention.js +102 -0
  29. package/dist/approval-card.d.ts +83 -23
  30. package/dist/approval-card.js +45 -15
  31. package/dist/approval-content-kind.d.ts +5 -2
  32. package/dist/approval-content-kind.js +16 -3
  33. package/dist/approval-deny-reasons.d.ts +1 -1
  34. package/dist/approval-gate-currency.d.ts +72 -0
  35. package/dist/approval-gate-currency.js +94 -0
  36. package/dist/approval.d.ts +15 -0
  37. package/dist/approval.js +55 -17
  38. package/dist/audit.d.ts +27 -10
  39. package/dist/audit.js +12 -16
  40. package/dist/bake-runner/main.js +3 -2
  41. package/dist/boot/coordinators.d.ts +6 -1
  42. package/dist/boot/coordinators.js +6 -3
  43. package/dist/boot/deferred-sandbox-path-env.js +1 -1
  44. package/dist/boot/execution-env.js +9 -7
  45. package/dist/boot/execution-lane.js +11 -0
  46. package/dist/boot/leader.d.ts +2 -0
  47. package/dist/boot/leader.js +4 -1
  48. package/dist/boot/memory-boundary.js +2 -2
  49. package/dist/boot/memory-consolidation.d.ts +5 -4
  50. package/dist/boot/memory-consolidation.js +7 -6
  51. package/dist/boot/org-delivery.d.ts +28 -0
  52. package/dist/boot/org-delivery.js +20 -0
  53. package/dist/boot/read-deny-anchor-audit.js +2 -2
  54. package/dist/boot/reapers.d.ts +3 -0
  55. package/dist/boot/reapers.js +21 -1
  56. package/dist/boot/resolve-spec.d.ts +3 -0
  57. package/dist/boot/resolve-spec.js +60 -81
  58. package/dist/boot/runner-deps.d.ts +18 -5
  59. package/dist/boot/runner-deps.js +10 -0
  60. package/dist/boot/session-faces.d.ts +1 -0
  61. package/dist/boot/session-faces.js +4 -2
  62. package/dist/boot/stage-02-web-admin-pull.js +4 -1
  63. package/dist/boot/stage-06-runners.d.ts +6 -3
  64. package/dist/boot/stage-06-runners.js +20 -4
  65. package/dist/boot/stage-07-capability-layer.d.ts +4 -1
  66. package/dist/boot/stage-07-capability-layer.js +24 -3
  67. package/dist/boot/stage-08-reapers.d.ts +4 -1
  68. package/dist/boot/stage-08-reapers.js +2 -2
  69. package/dist/boot/stage-09-leader.d.ts +5 -1
  70. package/dist/boot/stage-09-leader.js +2 -2
  71. package/dist/boot/stage-10-http-server.d.ts +5 -1
  72. package/dist/boot/stage-10-http-server.js +6 -6
  73. package/dist/boot/stores.d.ts +8 -0
  74. package/dist/boot/stores.js +10 -14
  75. package/dist/boot/web-admin.d.ts +10 -0
  76. package/dist/boot/web-admin.js +162 -65
  77. package/dist/boot/workflow-orchestration.d.ts +2 -0
  78. package/dist/boot/workflow-orchestration.js +2 -1
  79. package/dist/brain.js +8 -3
  80. package/dist/breaker-key.d.ts +6 -0
  81. package/dist/breaker-key.js +29 -0
  82. package/dist/budget.js +5 -1
  83. package/dist/caller-text-echo.d.ts +11 -6
  84. package/dist/caller-text-echo.js +10 -12
  85. package/dist/canonical-json.d.ts +2 -41
  86. package/dist/canonical-json.js +2 -13
  87. package/dist/capabilities/center-plugins.d.ts +7 -0
  88. package/dist/capabilities/center-plugins.js +1 -1
  89. package/dist/capabilities/composite-members.d.ts +1 -1
  90. package/dist/capabilities/composite-members.js +1 -1
  91. package/dist/capabilities/execution-lane.js +2 -2
  92. package/dist/capabilities/hands-lane.d.ts +21 -0
  93. package/dist/capabilities/hands-lane.js +10 -0
  94. package/dist/capabilities/mcp-probe.d.ts +36 -4
  95. package/dist/capabilities/mcp-probe.js +29 -6
  96. package/dist/capabilities/scenarios.d.ts +63 -7
  97. package/dist/capabilities/scenarios.js +39 -7
  98. package/dist/capability-facts.d.ts +36 -4
  99. package/dist/capability-facts.js +30 -4
  100. package/dist/center-config-tier.d.ts +69 -0
  101. package/dist/center-config-tier.js +42 -0
  102. package/dist/checkpoint-ctx-seal-m28.d.ts +42 -0
  103. package/dist/checkpoint-ctx-seal-m28.js +19 -0
  104. package/dist/checkpoint-identity.d.ts +16 -0
  105. package/dist/checkpoint-identity.js +19 -0
  106. package/dist/config-catalog.d.ts +17 -10
  107. package/dist/config-catalog.js +43 -28
  108. package/dist/config-d-migration.d.ts +3 -1
  109. package/dist/config-d-migration.js +2 -2
  110. package/dist/config-dir-trust.d.ts +114 -0
  111. package/dist/config-dir-trust.js +388 -0
  112. package/dist/config-invariants.d.ts +18 -5
  113. package/dist/config-invariants.js +35 -3
  114. package/dist/config-provider.d.ts +6 -1
  115. package/dist/config-provider.js +6 -2
  116. package/dist/config-types.d.ts +71 -14
  117. package/dist/config-types.js +8 -0
  118. package/dist/config.d.ts +13 -0
  119. package/dist/config.js +121 -59
  120. package/dist/delegated-session-owner.d.ts +185 -0
  121. package/dist/delegated-session-owner.js +119 -0
  122. package/dist/device-protocol.d.ts +5 -3
  123. package/dist/device-protocol.js +1 -1
  124. package/dist/device-ws-hub.js +21 -2
  125. package/dist/device-ws-protocol.d.ts +70 -6
  126. package/dist/device-ws-protocol.js +50 -23
  127. package/dist/elicitation.js +3 -2
  128. package/dist/entry-problems.d.ts +34 -0
  129. package/dist/entry-problems.js +14 -0
  130. package/dist/execution-lane-caps.d.ts +49 -10
  131. package/dist/execution-lane-caps.js +43 -4
  132. package/dist/field-conflict.d.ts +44 -0
  133. package/dist/field-conflict.js +21 -0
  134. package/dist/file-history-disclosure.d.ts +34 -5
  135. package/dist/file-history-disclosure.js +2 -2
  136. package/dist/fleet/fleet-bus.d.ts +84 -6
  137. package/dist/fleet/fleet-bus.js +42 -9
  138. package/dist/hooks/classic-stations.d.ts +10 -5
  139. package/dist/hooks/classic-stations.js +4 -4
  140. package/dist/hooks/command-spawn.d.ts +57 -0
  141. package/dist/hooks/command-spawn.js +139 -0
  142. package/dist/hooks/hook-llm.js +2 -2
  143. package/dist/hooks/hook-runner.d.ts +127 -25
  144. package/dist/hooks/hook-runner.js +668 -287
  145. package/dist/hooks/hook-source.d.ts +44 -0
  146. package/dist/hooks/hook-source.js +40 -0
  147. package/dist/http/active-run-conflict.d.ts +5 -13
  148. package/dist/http/active-run-conflict.js +17 -27
  149. package/dist/http/admission.js +111 -60
  150. package/dist/http/approval-sweeps.js +58 -19
  151. package/dist/http/caller-trust.d.ts +77 -0
  152. package/dist/http/caller-trust.js +143 -0
  153. package/dist/http/checkpoint-ctx.d.ts +173 -18
  154. package/dist/http/checkpoint-ctx.js +171 -12
  155. package/dist/http/claimed-run.d.ts +4 -3
  156. package/dist/http/device-placement.d.ts +2 -4
  157. package/dist/http/device-placement.js +2 -1
  158. package/dist/http/dispatch.js +13 -7
  159. package/dist/http/durable-run-leg.d.ts +4 -32
  160. package/dist/http/durable-run-leg.js +37 -12
  161. package/dist/http/idempotency.d.ts +2 -2
  162. package/dist/http/idempotency.js +2 -2
  163. package/dist/http/owner-access.d.ts +56 -0
  164. package/dist/http/owner-access.js +42 -0
  165. package/dist/http/principal-gate.d.ts +78 -27
  166. package/dist/http/principal-gate.js +64 -14
  167. package/dist/http/readiness.d.ts +11 -5
  168. package/dist/http/readiness.js +10 -3
  169. package/dist/http/refusal-body.d.ts +12 -0
  170. package/dist/http/refusal-body.js +11 -0
  171. package/dist/http/request-guards.d.ts +16 -32
  172. package/dist/http/request-guards.js +31 -30
  173. package/dist/http/resume-ctx-read.d.ts +50 -6
  174. package/dist/http/resume-ctx-read.js +33 -7
  175. package/dist/http/resume-legs.d.ts +5 -5
  176. package/dist/http/resume-legs.js +163 -91
  177. package/dist/http/retired-service-deps.d.ts +43 -0
  178. package/dist/http/retired-service-deps.js +18 -0
  179. package/dist/http/route-ctx.d.ts +44 -37
  180. package/dist/http/route-ctx.js +4 -0
  181. package/dist/http/route-table.js +2 -0
  182. package/dist/http/routes/a2a-serve.js +3 -4
  183. package/dist/http/routes/admin-config-refresh.js +1 -1
  184. package/dist/http/routes/admin-drain.js +1 -1
  185. package/dist/http/routes/admin-memory-optout.js +5 -1
  186. package/dist/http/routes/adoption.js +2 -9
  187. package/dist/http/routes/agents-roster.js +2 -3
  188. package/dist/http/routes/approvals-assistant.js +91 -56
  189. package/dist/http/routes/background.js +3 -2
  190. package/dist/http/routes/capabilities.js +20 -4
  191. package/dist/http/routes/config-catalog.d.ts +3 -3
  192. package/dist/http/routes/config-catalog.js +1 -1
  193. package/dist/http/routes/devices.js +3 -17
  194. package/dist/http/routes/diagnostics.d.ts +3 -3
  195. package/dist/http/routes/diagnostics.js +6 -2
  196. package/dist/http/routes/fleet.js +4 -4
  197. package/dist/http/routes/images.js +6 -7
  198. package/dist/http/routes/leader.js +1 -8
  199. package/dist/http/routes/mcp-reconnect.d.ts +1 -1
  200. package/dist/http/routes/mcp-reconnect.js +3 -2
  201. package/dist/http/routes/memory-bundle.js +1 -1
  202. package/dist/http/routes/memory-compliance.js +1 -1
  203. package/dist/http/routes/memory-consolidation.js +1 -1
  204. package/dist/http/routes/memory-origin.d.ts +1 -1
  205. package/dist/http/routes/memory-origin.js +1 -1
  206. package/dist/http/routes/memory-policy.d.ts +1 -1
  207. package/dist/http/routes/memory-policy.js +3 -2
  208. package/dist/http/routes/notify-wake.js +8 -21
  209. package/dist/http/routes/observability.js +2 -2
  210. package/dist/http/routes/principal-inbox.d.ts +39 -0
  211. package/dist/http/routes/principal-inbox.js +76 -0
  212. package/dist/http/routes/retention-ops.js +2 -9
  213. package/dist/http/routes/rules.js +9 -22
  214. package/dist/http/routes/runs.d.ts +5 -0
  215. package/dist/http/routes/runs.js +63 -115
  216. package/dist/http/routes/session-sync.d.ts +3 -2
  217. package/dist/http/routes/session-sync.js +53 -48
  218. package/dist/http/routes/sessions-list.d.ts +1 -1
  219. package/dist/http/routes/sessions-list.js +3 -4
  220. package/dist/http/routes/sessions.js +33 -43
  221. package/dist/http/routes/side-query.js +6 -4
  222. package/dist/http/routes/tasks.js +71 -30
  223. package/dist/http/routes/trace-usage.js +7 -9
  224. package/dist/http/routes/workflows.d.ts +351 -0
  225. package/dist/http/routes/workflows.js +153 -119
  226. package/dist/http/run-declared-families.d.ts +16 -0
  227. package/dist/http/run-declared-families.js +12 -0
  228. package/dist/http/send.d.ts +24 -3
  229. package/dist/http/send.js +14 -2
  230. package/dist/http/server.d.ts +23 -18
  231. package/dist/http/server.js +20 -15
  232. package/dist/http/stored-body-replay.js +17 -4
  233. package/dist/http/submit-ctx.d.ts +4 -3
  234. package/dist/http/submit-ctx.js +23 -6
  235. package/dist/http/submit-replay.d.ts +4 -3
  236. package/dist/http/submit-replay.js +5 -4
  237. package/dist/http/wire-types.d.ts +17 -10
  238. package/dist/index.d.ts +3 -1
  239. package/dist/index.js +1 -1
  240. package/dist/lane-home-probe.d.ts +78 -9
  241. package/dist/lane-home-probe.js +78 -12
  242. package/dist/leader/endpoint.d.ts +4 -1
  243. package/dist/leader/endpoint.js +11 -10
  244. package/dist/leader/wire.d.ts +4 -0
  245. package/dist/leader/wire.js +8 -7
  246. package/dist/limit-refusal.d.ts +17 -5
  247. package/dist/limit-refusal.js +4 -1
  248. package/dist/main.js +1 -1
  249. package/dist/mcp-assertion-header.d.ts +31 -10
  250. package/dist/mcp-assertion-header.js +10 -3
  251. package/dist/memory-origin-fence.js +2 -2
  252. package/dist/memory-project-layer.d.ts +37 -0
  253. package/dist/memory-project-layer.js +46 -0
  254. package/dist/memory-scope.d.ts +23 -16
  255. package/dist/memory-scope.js +24 -8
  256. package/dist/memory-sync-client.d.ts +27 -13
  257. package/dist/memory-sync-client.js +107 -18
  258. package/dist/model-compat.d.ts +1 -1
  259. package/dist/model-entry-refusal.d.ts +9 -1
  260. package/dist/model-entry-refusal.js +6 -1
  261. package/dist/model-plane-face.d.ts +51 -0
  262. package/dist/model-plane-face.js +24 -0
  263. package/dist/model-provider.d.ts +5 -0
  264. package/dist/model-provider.js +4 -2
  265. package/dist/model-route-endpoint.d.ts +8 -11
  266. package/dist/model-route-endpoint.js +4 -7
  267. package/dist/model-route-env.d.ts +22 -0
  268. package/dist/model-route-env.js +2 -0
  269. package/dist/model-route-gap.d.ts +60 -30
  270. package/dist/model-route-gap.js +21 -15
  271. package/dist/observability/corrupt-read-seat.d.ts +7 -0
  272. package/dist/observability/corrupt-read-seat.js +9 -0
  273. package/dist/observability/err-text.d.ts +6 -0
  274. package/dist/observability/err-text.js +18 -4
  275. package/dist/observability/fail-open.d.ts +35 -9
  276. package/dist/observability/fail-open.js +35 -9
  277. package/dist/observability/metrics.js +1 -0
  278. package/dist/observability/prompt-manifest.d.ts +22 -25
  279. package/dist/observability/prompt-manifest.js +54 -50
  280. package/dist/observability/tool-trace.js +9 -10
  281. package/dist/operator-ask.d.ts +10 -0
  282. package/dist/operator-ask.js +3 -0
  283. package/dist/orchestration/workflow-agent-session-index.d.ts +12 -12
  284. package/dist/orchestration/workflow-agent-session-index.js +9 -13
  285. package/dist/orchestration/workflow-completion-inbox.d.ts +42 -15
  286. package/dist/orchestration/workflow-completion-inbox.js +13 -0
  287. package/dist/orchestration/workflow-notify-journal.d.ts +13 -32
  288. package/dist/orchestration/workflow-notify-journal.js +27 -34
  289. package/dist/orchestration/workflow-park-decision.d.ts +89 -0
  290. package/dist/orchestration/workflow-park-decision.js +119 -0
  291. package/dist/org-delivery-gate.d.ts +81 -0
  292. package/dist/org-delivery-gate.js +59 -0
  293. package/dist/panel-tool-names.d.ts +23 -0
  294. package/dist/panel-tool-names.js +8 -0
  295. package/dist/parked-decide.d.ts +43 -11
  296. package/dist/parked-decide.js +100 -51
  297. package/dist/parked-revive-surface.d.ts +9 -2
  298. package/dist/parked-revive-surface.js +11 -8
  299. package/dist/permission-rule-list.d.ts +1 -1
  300. package/dist/permission-rule-list.js +2 -2
  301. package/dist/permission-rule-vocab.d.ts +1 -23
  302. package/dist/permission-rule-vocab.js +0 -13
  303. package/dist/plugins/approval-ask-store-file.d.ts +16 -1
  304. package/dist/plugins/approval-ask-store-file.js +21 -0
  305. package/dist/plugins/approval-ask-store-memory.d.ts +18 -1
  306. package/dist/plugins/approval-ask-store-memory.js +34 -2
  307. package/dist/plugins/approval-ask-store-sql.d.ts +25 -0
  308. package/dist/plugins/approval-ask-store-sql.js +37 -1
  309. package/dist/plugins/approval-payload-fingerprint.d.ts +14 -6
  310. package/dist/plugins/approval-payload-fingerprint.js +1 -1
  311. package/dist/plugins/blob-backend.d.ts +38 -25
  312. package/dist/plugins/blob-backend.js +29 -19
  313. package/dist/plugins/breaker-state-sql.d.ts +2 -2
  314. package/dist/plugins/breaker-state-sql.js +4 -4
  315. package/dist/plugins/caching-session-store.d.ts +4 -1
  316. package/dist/plugins/caching-session-store.js +7 -3
  317. package/dist/plugins/checkpoint-store-sql.d.ts +36 -1
  318. package/dist/plugins/checkpoint-store-sql.js +65 -3
  319. package/dist/plugins/file-history-store-sql.d.ts +5 -2
  320. package/dist/plugins/file-history-store-sql.js +3 -1
  321. package/dist/plugins/file-run-store.d.ts +5 -13
  322. package/dist/plugins/file-run-store.js +9 -0
  323. package/dist/plugins/fork-routing-session-store.d.ts +9 -1
  324. package/dist/plugins/fork-routing-session-store.js +12 -1
  325. package/dist/plugins/host-platform.d.ts +2 -0
  326. package/dist/plugins/host-platform.js +2 -1
  327. package/dist/plugins/local-checkpoint-store.d.ts +15 -2
  328. package/dist/plugins/local-checkpoint-store.js +32 -1
  329. package/dist/plugins/local-session-store.d.ts +13 -10
  330. package/dist/plugins/local-session-store.js +23 -14
  331. package/dist/plugins/local-session-window.d.ts +45 -0
  332. package/dist/plugins/local-session-window.js +38 -0
  333. package/dist/plugins/mailbox-recipient-lifecycle.d.ts +3 -2
  334. package/dist/plugins/mailbox-store-sql.d.ts +30 -48
  335. package/dist/plugins/mailbox-store-sql.js +77 -52
  336. package/dist/plugins/memory-embedder-fingerprint.js +2 -1
  337. package/dist/plugins/memory-run-store.d.ts +5 -13
  338. package/dist/plugins/memory-run-store.js +8 -0
  339. package/dist/plugins/one-time-migrations.d.ts +121 -5
  340. package/dist/plugins/one-time-migrations.js +332 -8
  341. package/dist/plugins/org-delivery-quota-store.d.ts +123 -0
  342. package/dist/plugins/org-delivery-quota-store.js +228 -0
  343. package/dist/plugins/permission-rule-store-file.js +10 -6
  344. package/dist/plugins/permission-rule-store-sql.js +8 -8
  345. package/dist/plugins/pg-pool.js +39 -7
  346. package/dist/plugins/pg-session-storage.d.ts +18 -5
  347. package/dist/plugins/pg-session-storage.js +68 -23
  348. package/dist/plugins/remote-env-adb.d.ts +6 -12
  349. package/dist/plugins/remote-env-adb.js +12 -2
  350. package/dist/plugins/remote-env-device.d.ts +14 -7
  351. package/dist/plugins/remote-env-device.js +23 -15
  352. package/dist/plugins/remote-env-k8s.d.ts +8 -12
  353. package/dist/plugins/remote-env-k8s.js +23 -5
  354. package/dist/plugins/remote-env-local-docker.d.ts +10 -15
  355. package/dist/plugins/remote-env-local-docker.js +12 -9
  356. package/dist/plugins/remote-env-ssh.d.ts +10 -16
  357. package/dist/plugins/remote-env-ssh.js +17 -11
  358. package/dist/plugins/run-store-sql.d.ts +16 -14
  359. package/dist/plugins/run-store-sql.js +22 -2
  360. package/dist/plugins/schema-indexes.js +2 -0
  361. package/dist/plugins/send-user-file.js +2 -2
  362. package/dist/plugins/session-epoch-anchor.d.ts +32 -6
  363. package/dist/plugins/session-epoch-anchor.js +2 -3
  364. package/dist/plugins/session-ownership.d.ts +11 -0
  365. package/dist/plugins/session-ownership.js +11 -0
  366. package/dist/plugins/session-placement.d.ts +12 -14
  367. package/dist/plugins/session-placement.js +2 -15
  368. package/dist/plugins/session-store.d.ts +18 -8
  369. package/dist/plugins/session-store.js +15 -4
  370. package/dist/plugins/sql-driver.d.ts +74 -11
  371. package/dist/plugins/sql-driver.js +210 -12
  372. package/dist/plugins/sql-errors.d.ts +23 -0
  373. package/dist/plugins/sql-errors.js +21 -0
  374. package/dist/plugins/store-backend.d.ts +36 -1
  375. package/dist/plugins/store-backend.js +40 -14
  376. package/dist/plugins/store-contracts.d.ts +42 -0
  377. package/dist/plugins/store-contracts.js +1 -0
  378. package/dist/plugins/tidb-pool.d.ts +6 -1
  379. package/dist/plugins/tidb-pool.js +44 -10
  380. package/dist/plugins/tidb-session-storage.d.ts +3 -2
  381. package/dist/plugins/tidb-session-storage.js +2 -2
  382. package/dist/plugins/tidb-session-store.d.ts +25 -5
  383. package/dist/plugins/tidb-session-store.js +67 -21
  384. package/dist/plugins/workflow-journal-store-sql.d.ts +73 -19
  385. package/dist/plugins/workflow-journal-store-sql.js +19 -6
  386. package/dist/plugins/workflow-run-store-sql.d.ts +30 -8
  387. package/dist/plugins/workflow-run-store-sql.js +50 -9
  388. package/dist/posture-source.d.ts +18 -10
  389. package/dist/posture-source.js +4 -1
  390. package/dist/project-identity.d.ts +45 -6
  391. package/dist/project-identity.js +162 -8
  392. package/dist/project-layer-key.d.ts +28 -0
  393. package/dist/project-layer-key.js +29 -0
  394. package/dist/question.js +3 -2
  395. package/dist/read-deny-entry.d.ts +25 -0
  396. package/dist/read-deny-entry.js +12 -0
  397. package/dist/read-face-posture.d.ts +37 -11
  398. package/dist/read-face-posture.js +32 -3
  399. package/dist/request-key-closure.d.ts +7 -0
  400. package/dist/request-key-closure.js +9 -1
  401. package/dist/request-tool-axes.d.ts +12 -0
  402. package/dist/request-tool-axes.js +20 -0
  403. package/dist/request-value-closure.d.ts +5 -0
  404. package/dist/request-value-closure.js +11 -3
  405. package/dist/rule-entry-list.d.ts +26 -2
  406. package/dist/rule-entry-list.js +72 -12
  407. package/dist/rules-consent.js +3 -4
  408. package/dist/run-acceptance.d.ts +19 -3
  409. package/dist/run-acceptance.js +13 -1
  410. package/dist/run-local.js +37 -6
  411. package/dist/runs.d.ts +12 -10
  412. package/dist/runs.js +25 -6
  413. package/dist/runtime-caps-resolver.d.ts +16 -6
  414. package/dist/runtime-caps-resolver.js +18 -5
  415. package/dist/sandbox-pkg-source.d.ts +19 -1
  416. package/dist/sandbox-pkg-source.js +29 -7
  417. package/dist/sealed-key.d.ts +32 -0
  418. package/dist/sealed-key.js +55 -1
  419. package/dist/security.d.ts +95 -15
  420. package/dist/security.js +52 -9
  421. package/dist/session-error-wire.d.ts +31 -7
  422. package/dist/session-error-wire.js +9 -2
  423. package/dist/session-sync-kernel.d.ts +37 -10
  424. package/dist/session-sync-kernel.js +10 -0
  425. package/dist/session-titled-frame.d.ts +33 -0
  426. package/dist/session-titled-frame.js +4 -0
  427. package/dist/session-titler.d.ts +9 -1
  428. package/dist/session-titler.js +15 -4
  429. package/dist/spec-fields.js +21 -2
  430. package/dist/sql-engine-posture.d.ts +3 -1
  431. package/dist/task-a2a.js +4 -3
  432. package/dist/task-cwd.d.ts +1 -1
  433. package/dist/task-mcp.d.ts +5 -5
  434. package/dist/task-mcp.js +27 -74
  435. package/dist/task-settings.d.ts +93 -88
  436. package/dist/task-settings.js +71 -63
  437. package/dist/text-bidi.d.ts +0 -4
  438. package/dist/text-bidi.js +1 -3
  439. package/dist/tighten-only-seats.d.ts +150 -0
  440. package/dist/tighten-only-seats.js +196 -0
  441. package/dist/tool-approval.d.ts +451 -136
  442. package/dist/tool-approval.js +733 -256
  443. package/dist/tool-rule-names.d.ts +6 -0
  444. package/dist/trace/core-keyset-guard.d.ts +108 -25
  445. package/dist/trace/exit-seat.d.ts +40 -0
  446. package/dist/trace/exit-seat.js +130 -0
  447. package/dist/trace/ledger-events.d.ts +6 -3
  448. package/dist/trace/project.d.ts +98 -13
  449. package/dist/trace/project.js +57 -32
  450. package/dist/trace/projection-table.d.ts +100 -0
  451. package/dist/trace/projection-table.js +130 -0
  452. package/dist/trace/redact.d.ts +57 -0
  453. package/dist/trace/redact.js +962 -168
  454. package/dist/trace/self-minted-wire.d.ts +349 -1
  455. package/dist/trace/self-minted-wire.js +118 -1
  456. package/dist/trace/wire-projection-faces.d.ts +304 -2
  457. package/dist/trace/wire-projection-faces.js +160 -13
  458. package/dist/url-refusal.js +2 -0
  459. package/dist/web-admin-client/apply-effective.d.ts +12 -0
  460. package/dist/web-admin-client/apply-effective.js +56 -5
  461. package/dist/web-admin-client/facade.d.ts +1 -1
  462. package/dist/web-admin-client/facade.js +1 -1
  463. package/dist/web-admin-client/read-face.js +5 -9
  464. package/dist/web-admin-client/skills-mcp.d.ts +1 -1
  465. package/dist/web-admin-client/skills-mcp.js +6 -8
  466. package/dist/web-admin-client/types.d.ts +1 -1
  467. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +57 -9
  468. package/package.json +8 -4
  469. package/scripts/drain-check-local.mjs +34 -2
  470. package/scripts/drain-check.mysql.sql +20 -2
  471. package/scripts/drain-check.pg.sql +20 -2
  472. package/dist/mcp-tool-faces.d.ts +0 -69
  473. package/dist/mcp-tool-faces.js +0 -106
@@ -14,7 +14,16 @@
14
14
  *
15
15
  * The marker `SEMA_PKG_SOURCE` always rides along: the image's pkg-source.sh hook (pod-start / profile.d)
16
16
  * materializes the file-bound pieces from it; images without the hook simply ignore it.
17
+ *
18
+ * ## One law: no URL that rides into the model-visible sandbox env carries userinfo
19
+ * Everything this module derives lands in the env of every sandbox process — including the ones the model drives
20
+ * (`env`, `printenv`) — so `https://user:pass@mirror/…` among the `custom` addresses would hand the deployment's
21
+ * package-source credential to the model. {@link customPkgSourceFromEnv} is the ONE reader of the six addresses and the
22
+ * one place that judges them: the shared URL-shape judge (`httpUrlFlaws`, the same one MEMORY_EMBEDDER_ENDPOINT /
23
+ * A2A_SERVE_URL answer to) says no ⇒ it throws, startup refuses. Never stripped, never rewritten, never forwarded
24
+ * quietly. The request face has no package-source address key at all (deployment face only).
17
25
  */
26
+ import { type HttpUrlFlaw } from "@sema-agent/settings-schema";
18
27
  /** Explicit URLs for `custom` (RFC B: 极端情况出口). Read from the service process env (operator-trusted). */
19
28
  export interface CustomPkgSource {
20
29
  aptMirror?: string;
@@ -29,6 +38,15 @@ export interface CustomPkgSource {
29
38
  * profile (unset = byte-identical sandbox — the B5 backward-compat contract).
30
39
  */
31
40
  export declare function derivePkgSourceEnv(profile: string | undefined, custom?: CustomPkgSource): Record<string, string>;
32
- /** Read the `custom` explicit-URL set from the service process env (operator-trusted deployment config). */
41
+ /**
42
+ * The refusal sentence for one address (pure data): the shared judge's rule words + the value's origin only
43
+ * ({@link buildHttpUrlRefusal}), then why it matters here and the two ways out. No byte of the credential can reach it.
44
+ */
45
+ export declare function buildCustomPkgSourceRefusal(envName: string, raw: string, flaws: readonly HttpUrlFlaw[]): string;
46
+ /**
47
+ * Read the `custom` explicit-URL set from the service process env (operator-trusted deployment config) — the ONE reader and
48
+ * the ONE judge of those six addresses (see the module header). An address the shared judge refuses (userinfo, or a shape
49
+ * that is not a parseable http(s) URL) throws {@link buildCustomPkgSourceRefusal}; empty / unset = not set.
50
+ */
33
51
  export declare function customPkgSourceFromEnv(env?: NodeJS.ProcessEnv): CustomPkgSource;
34
52
  //# sourceMappingURL=sandbox-pkg-source.d.ts.map
@@ -1,3 +1,5 @@
1
+ import { httpUrlFlaws } from "@sema-agent/settings-schema";
2
+ import { buildHttpUrlRefusal } from "./url-refusal.js";
1
3
  const GLOBAL_ENV = {
2
4
  PIP_INDEX_URL: "https://pypi.org/simple",
3
5
  UV_DEFAULT_INDEX: "https://pypi.org/simple",
@@ -43,14 +45,34 @@ export function derivePkgSourceEnv(profile, custom) {
43
45
  return { SEMA_PKG_SOURCE: profile };
44
46
  }
45
47
  }
48
+ export function buildCustomPkgSourceRefusal(envName, raw, flaws) {
49
+ return (`${buildHttpUrlRefusal(envName, raw, flaws)} — with SANDBOX_PKG_SOURCE=custom this address is written verbatim into the environment of every sandbox ` +
50
+ `(e2b VM / k8s pod), where any process the model can run reads it back with \`env\`; an address that carries userinfo, or whose shape cannot be judged ` +
51
+ `credential-free (not http(s)://host[:port][/path], or containing a backslash — the URL standard reads it as "/", package managers do not), would hand ` +
52
+ `the deployment's package-source credential to the model, so the server refuses to start rather than forward or rewrite it. ` +
53
+ `Way out: (1) set ${envName} to the credential-free address and give the package manager its credential through its own channel — a token variable ` +
54
+ `named in the lane's name list (E2B_SANDBOX_ENV / K8S_SANDBOX_ENV; naming it accepts that every process in the sandbox, the model included, can read it) ` +
55
+ `or a credential file baked into the sandbox image; (2) if the source needs no authentication, drop the userinfo.`);
56
+ }
46
57
  export function customPkgSourceFromEnv(env = process.env) {
47
- return {
48
- ...(env.SEMA_APT_MIRROR ? { aptMirror: env.SEMA_APT_MIRROR } : {}),
49
- ...(env.SEMA_PIP_INDEX ? { pipIndex: env.SEMA_PIP_INDEX } : {}),
50
- ...(env.SEMA_UV_INDEX ? { uvIndex: env.SEMA_UV_INDEX } : {}),
51
- ...(env.SEMA_UV_PY_MIRROR ? { uvPyMirror: env.SEMA_UV_PY_MIRROR } : {}),
52
- ...(env.SEMA_NPM_REGISTRY ? { npmRegistry: env.SEMA_NPM_REGISTRY } : {}),
53
- ...(env.SEMA_NODE_MIRROR ? { nodeMirror: env.SEMA_NODE_MIRROR } : {}),
58
+ const addresses = {
59
+ aptMirror: ["SEMA_APT_MIRROR", env.SEMA_APT_MIRROR],
60
+ pipIndex: ["SEMA_PIP_INDEX", env.SEMA_PIP_INDEX],
61
+ uvIndex: ["SEMA_UV_INDEX", env.SEMA_UV_INDEX],
62
+ uvPyMirror: ["SEMA_UV_PY_MIRROR", env.SEMA_UV_PY_MIRROR],
63
+ npmRegistry: ["SEMA_NPM_REGISTRY", env.SEMA_NPM_REGISTRY],
64
+ nodeMirror: ["SEMA_NODE_MIRROR", env.SEMA_NODE_MIRROR],
54
65
  };
66
+ const out = {};
67
+ for (const field of Object.keys(addresses)) {
68
+ const [name, raw] = addresses[field];
69
+ if (!raw)
70
+ continue;
71
+ const flaws = raw.includes("\\") ? ["not-http-url"] : httpUrlFlaws(raw, ["userinfo"]);
72
+ if (flaws.length > 0)
73
+ throw new Error(buildCustomPkgSourceRefusal(name, raw, flaws));
74
+ out[field] = raw;
75
+ }
76
+ return out;
55
77
  }
56
78
  //# sourceMappingURL=sandbox-pkg-source.js.map
@@ -1,4 +1,5 @@
1
1
  import type { Logger } from "./observability/logger.js";
2
+ import { type CheckpointCtxSealing } from "./http/checkpoint-ctx.js";
2
3
  /** Algorithm tag — MUST equal registry-core's `SEALED_BOX_ALG` (v1 sole value; a different alg on
3
4
  * the wire is a loud per-model rejection in applyEffective, never a guess). */
4
5
  export declare const SEALED_KEY_ALG = "libsodium-sealedbox-x25519";
@@ -61,6 +62,36 @@ export declare function deriveKeyId(publicKeyRaw: Uint8Array): string;
61
62
  * zero loadable keys AND generation failed).
62
63
  */
63
64
  export declare function ensureSealedKeyStore(dir: string, logger?: Logger): Promise<SealedKeyStore>;
65
+ declare const checkpointCtxKeyStoreBrand: unique symbol;
66
+ /**
67
+ * 停驻行封签那一域的保管店(品牌型):读口与 {@link SealedKeyStore} 同形,型不同 —— {@link createCheckpointCtxSealing} 只收它,模型钥匙那只店交进去是 tsc 错
68
+ * (「两域并成一对钥匙」那条路在型面上关掉,不靠注释)。唯一铸点 = {@link ensureCheckpointCtxSealing} 建店处(经 {@link mintCheckpointCtxKeyStore});
69
+ * 品牌断言的普查在 `test/owner-gate-single-owner.test.ts` ⑤b。
70
+ */
71
+ export type CheckpointCtxKeyStore = SealedKeyStore & {
72
+ readonly [checkpointCtxKeyStoreBrand]: true;
73
+ };
74
+ /**
75
+ * 停驻行封签器的**生产工厂**(起服 web-admin 运行时与嵌入方同调这一只):在 `sealedKeyDir`(`<数据根>/sealed-key`)之下的 {@link CHECKPOINT_CTX_KEY_SUBDIR}
76
+ * 建这一域自己的保管店(同一只 {@link ensureSealedKeyStore}:目录 0700、钥匙文件 0600、空目录生成一把、读不了的文件跳过不删),再交 {@link createCheckpointCtxSealing}。
77
+ * 钥匙只在这里读进内存(起服一次):进程运行中补进目录的钥匙文件要重启才认。
78
+ * 抛 = 这一域没有封签器(目录建不了 / 改不了权限,或钥匙文件公私不配对);调用方当「没有封签器」处置。
79
+ */
80
+ export declare function ensureCheckpointCtxSealing(sealedKeyDir: string, logger?: Logger): Promise<CheckpointCtxSealing>;
81
+ /**
82
+ * 停驻行凭据的**封签器**(S-1907;形与规则的属主 = `http/checkpoint-ctx.ts` 末段「凭据位密封」)—— 从**这一域自己的**保管店派生(生产 = {@link ensureCheckpointCtxSealing};
83
+ * 入参型 {@link CheckpointCtxKeyStore}:模型钥匙那只店进不来),信封是:封进密文的明文是 `JSON.stringify([CHECKPOINT_CTX_SEAL_ALG, sessionId, value])`。
84
+ * · **封**:对着保管店**最新**那把公钥 `crypto_box_seal`(只用公钥);封签记下那把钥匙的 id。
85
+ * · **开**:按封签上的 id **精确**选私钥(保管店的 `open`,不逐把试),再判信封:三元数组、首元是本信封的词、第二元是**这一行的会话 id**,交回第三元(封进去的 JSON 值,任何类型) ——
86
+ * 任何一条不成立 ⇒ 抛。域隔离由钥匙给(两域两对钥匙:模型钥匙的密文放进停驻行凭据位 ⇒ 这一域不持有那把钥匙;反过来停驻行的封签放进 `sealedApiKey` ⇒ 模型钥匙店不持有),
87
+ * 信封再给**行绑定**(别的会话那一行的封签搬过来开不开)与格式词 —— 持有本对象的那一层(HTTP 续跑腿)拿到的是「只开停驻行封签」的口;
88
+ * 私钥本身、保管目录、持有 id 列表照旧够不着(只多一个 `holds(id)`:开之前判「这把钥匙在不在本副本」,`key_not_held` 与 `unopenable` 分开报)。
89
+ * · 错误句不带明文:信封解析失败只报「不是停驻行封签」(`JSON.parse` 的报错会嵌入输入片段 —— 那里可能是一只模型钥匙),保管店的错误句只说哪把钥匙 / 哪种失败。
90
+ * 前置条件:`store` 来自 {@link ensureSealedKeyStore}(它已等过 libsodium 就绪);没就绪 ⇒ 当场响亮抛,不造一只封不了的对象。
91
+ * **自检**:交出之前对最新那把钥匙封一只探针、再开回来 —— 开不回来(保管档里公私钥不配对:长度都对,装载照收)⇒ 当场抛。封得进、开不出的封签器比没有更坏:
92
+ * 起服的一次性迁移 M28 会用它把还读得懂的明文行换成谁都开不开的密文。调用方(web-admin 运行时)把这一抛当「没有封签器」处置 ⇒ 耐久审批在场的部署起服拒。
93
+ */
94
+ export declare function createCheckpointCtxSealing(store: CheckpointCtxKeyStore): CheckpointCtxSealing;
64
95
  /**
65
96
  * Best-effort public-key registration with the web-admin (`POST /api/config/execution-keys`).
66
97
  *
@@ -86,4 +117,5 @@ export declare function reportExecutionPublicKey(opts: {
86
117
  /** Test seam. */
87
118
  fetchImpl?: typeof fetch;
88
119
  }): Promise<void>;
120
+ export {};
89
121
  //# sourceMappingURL=sealed-key.d.ts.map
@@ -6,6 +6,7 @@ import { buildDisplaySafeUrl } from "./trace/redact.js";
6
6
  import { createCenterCredentialSource } from "./center-credential.js";
7
7
  import { centerFetch } from "./web-admin-client/center-request.js";
8
8
  import { WEB_ADMIN_ENV } from "@sema-agent/settings-schema";
9
+ import { CHECKPOINT_CTX_SEAL_ALG } from "./http/checkpoint-ctx.js";
9
10
  export const SEALED_KEY_ALG = "libsodium-sealedbox-x25519";
10
11
  const SEAL_OVERHEAD_BYTES = 48;
11
12
  const BASE64_STRICT = /^[A-Za-z0-9+/]+={0,2}$/;
@@ -70,7 +71,7 @@ export async function ensureSealedKeyStore(dir, logger) {
70
71
  await writeFile(path, JSON.stringify(record, null, 2) + "\n", { mode: 0o600 });
71
72
  await chmod(path, 0o600);
72
73
  keys.set(record.publicKeyId, { publicKey: kp.publicKey, privateKey: kp.privateKey, publicKeyB64, createdAt: record.createdAt });
73
- logger?.info("sealed_key_generated", { publicKeyId: record.publicKeyId, publicKey: publicKeyB64, dir, note: "new X25519 sealed-box keypair — register the PUBLIC key with the registry (see sealed_key_public_key)" });
74
+ logger?.info("sealed_key_generated", { publicKeyId: record.publicKeyId, publicKey: publicKeyB64, dir, note: "new X25519 sealed-box keypair in this directory — the private key exists only here: keep the directory across restarts and identical on every replica that has to open what it sealed, and back it up as a private key" });
74
75
  }
75
76
  const latestEntry = [...keys.entries()].sort((a, b) => (a[1].createdAt < b[1].createdAt ? 1 : a[1].createdAt > b[1].createdAt ? -1 : a[0] < b[0] ? -1 : 1))[0];
76
77
  return {
@@ -88,6 +89,59 @@ export async function ensureSealedKeyStore(dir, logger) {
88
89
  },
89
90
  };
90
91
  }
92
+ const CHECKPOINT_CTX_KEY_SUBDIR = "checkpoint-ctx-keys";
93
+ function mintCheckpointCtxKeyStore(store) {
94
+ return store;
95
+ }
96
+ export async function ensureCheckpointCtxSealing(sealedKeyDir, logger) {
97
+ return createCheckpointCtxSealing(mintCheckpointCtxKeyStore(await ensureSealedKeyStore(join(sealedKeyDir, CHECKPOINT_CTX_KEY_SUBDIR), logger)));
98
+ }
99
+ export function createCheckpointCtxSealing(store) {
100
+ const sodium = _sodium;
101
+ if (typeof sodium.crypto_box_seal !== "function")
102
+ throw new Error("createCheckpointCtxSealing: libsodium is not initialized — build the store with ensureSealedKeyStore() first");
103
+ const { publicKeyId } = store.latest;
104
+ const publicKey = b64ToBytes(store.latest.publicKey, "publicKey");
105
+ const sealing = createUncheckedCheckpointCtxSealing(store, publicKeyId, publicKey);
106
+ const probeSession = "sealed-key-self-test";
107
+ let roundTrip;
108
+ try {
109
+ roundTrip = sealing.open(sealing.seal(probeSession, probeSession), probeSession);
110
+ }
111
+ catch {
112
+ roundTrip = undefined;
113
+ }
114
+ if (roundTrip !== probeSession) {
115
+ throw new Error(`createCheckpointCtxSealing: self-test failed — a value sealed against the newest public key (publicKeyId "${publicKeyId}") does not open with the private key held under that id; the key file's public and private halves do not belong together. Refusing to hand out a sealer whose seals nobody can open`);
116
+ }
117
+ return sealing;
118
+ }
119
+ function createUncheckedCheckpointCtxSealing(store, publicKeyId, publicKey) {
120
+ const sodium = _sodium;
121
+ return {
122
+ seal: (value, sessionId) => ({
123
+ alg: CHECKPOINT_CTX_SEAL_ALG,
124
+ publicKeyId,
125
+ sealed: Buffer.from(sodium.crypto_box_seal(sodium.from_string(JSON.stringify([CHECKPOINT_CTX_SEAL_ALG, sessionId, value])), publicKey)).toString("base64"),
126
+ }),
127
+ holds: (id) => store.has(id),
128
+ open: (sealed, sessionId) => {
129
+ const plaintext = store.open(sealed.sealed, sealed.publicKeyId);
130
+ let envelope;
131
+ try {
132
+ envelope = JSON.parse(plaintext);
133
+ }
134
+ catch {
135
+ throw new Error("sealed value is not a checkpoint-context seal");
136
+ }
137
+ if (!Array.isArray(envelope) || envelope.length !== 3 || envelope[0] !== CHECKPOINT_CTX_SEAL_ALG)
138
+ throw new Error("sealed value is not a checkpoint-context seal");
139
+ if (envelope[1] !== sessionId)
140
+ throw new Error("sealed value was sealed for a different session's resume context");
141
+ return envelope[2];
142
+ },
143
+ };
144
+ }
91
145
  export async function reportExecutionPublicKey(opts) {
92
146
  const { baseUrl, adminToken, key, notes, logger } = opts;
93
147
  const manual = (why) => {
@@ -1,10 +1,11 @@
1
1
  import type { IncomingMessage } from "node:http";
2
- import { type SessionStore, type SessionTreeEntry } from "@sema-agent/core";
2
+ import { type SessionPlacementRecord, type SessionStore, type SessionTreeEntry } from "@sema-agent/core";
3
3
  import type { ServiceConfig } from "./config-types.js";
4
4
  import type { TaskRequestBody } from "./http/wire-types.js";
5
5
  import type { StagingHandle } from "./session-sync-kernel.js";
6
6
  import type { SessionFace, SessionFaceReader } from "./session-incarnation.js";
7
7
  import type { SessionRowPatch } from "./session-row.js";
8
+ import { type OwnerlessClaim } from "./delegated-session-owner.js";
8
9
  /** One row of the session-list projection (CC /resume picker) — the durable session abstraction's enumeration
9
10
  * output (§0.5). Mirrors {@link RunStore}'s shipped `SessionSummary` shape so the wire response is unchanged. */
10
11
  export interface SessionListItem {
@@ -41,7 +42,7 @@ export interface SessionListItem {
41
42
  * 而 `DELETE /v1/sessions/:id`(删整条会话)不在其中 —— 同一风险形、爆炸半径**更大**,却没门。
42
43
  *
43
44
  * 风险形逐字同源(守卫自己的注释):「无 service token + 未开 `ALLOW_UNAUTHED_WRITES`」这一形下
44
- * `sessionOwnerScopeForWrite` 判 fleet-wide ⇒ 集群内**任意**调用方可对**任意** session 动手。
45
+ * fleet 写档(`http/principal-gate.ts` 的 `fleetViewOf`,dev-open 臂)判 fleet-wide ⇒ 集群内**任意**调用方可对**任意** session 动手。
45
46
  * policy 腿担心的是「forged principal 能 tighten/DoS 一个会话的工具」;DELETE 直接把会话删掉。
46
47
  *
47
48
  * ⇒ 枚举改**族判定**:新增一条破坏性会话路由时自动落进门内,不必记得回来加白名单。
@@ -51,6 +52,54 @@ export interface SessionListItem {
51
52
  * 不含读面,也不含 `POST /v1/tasks` 那条提交腿 —— 它有自己的同族门(别重复拦,双拦会让错误归因变糊)。
52
53
  */
53
54
  export declare function isDestructiveSessionWrite(method: string, url: string): boolean;
55
+ declare const principalBrand: unique symbol;
56
+ declare const durableOwnerKeyBrand: unique symbol;
57
+ /**
58
+ * 🔴 **两个 owner 不是同一个东西**(S-487 RH-3,类型事实):
59
+ * · {@link Principal} —— **这次请求**验过的调用方身份。铸点闭集:`http/principal-gate.ts` 的 `gatedPrincipal`(验身份的那一只),以及属主存取器
60
+ * (`http/owner-access.ts`)里一只「上游已验」的适配口(调用方身份经一只纯串结构转手过来的腿 —— 第二期随那只结构带品牌而删);
61
+ * · {@link DurableOwnerKey} —— **行上 / 存储键里**落下的属主(run 行、会话行的 `owner` 列)。铸点闭集:属主存取器读行处与 {@link sessionOwnerCellOf}。
62
+ * 两者互不可赋、裸 `string` 两者都不可赋;「这个调用方读得到这一行吗」的判官(`runOwnerReadable`)签名两参各带一个品牌,对调即编译红。
63
+ * 铸点按文件计数钉在 `test/owner-gate-single-owner.test.ts` 的品牌铸点普查格里:多一处铸点 = 门红。
64
+ */
65
+ export type Principal = string & {
66
+ readonly [principalBrand]: true;
67
+ };
68
+ export type DurableOwnerKey = string & {
69
+ readonly [durableOwnerKeyBrand]: true;
70
+ };
71
+ /** {@link Principal} 的铸造口(只在铸点闭集里调,见上)。 */
72
+ export declare function mintPrincipal(verified: string): Principal;
73
+ /** {@link DurableOwnerKey} 的铸造口(只在铸点闭集里调,见上)。 */
74
+ export declare function mintDurableOwnerKey(persisted: string): DurableOwnerKey;
75
+ /**
76
+ * 会话行属主门读的那一格(S-1770):有主的行只有属主;**无主的行**另带「它是不是委派子代的 placed 行」(店的 `placementOf` 答,{@link sessionOwnerCellOf} 读;
77
+ * 不实现 placement 的店没有 placed 行)。`placed` 只在无主时存在 —— 有主的行属主门不看它。
78
+ */
79
+ export type SessionOwnerCell = {
80
+ readonly owner: DurableOwnerKey;
81
+ } | {
82
+ readonly owner: null;
83
+ readonly placed: boolean;
84
+ };
85
+ /** 会话行属主门的调用方:验过的主体(`gatedPrincipal`)+ 它是不是 **fleet 调用方**(dev-open 部署上的任何调用方,或出示了这台部署的 service 凭据、
86
+ * 不报主体的调用方)。唯一铸点 `http/principal-gate.ts` 的 `sessionCallerOf`。 */
87
+ export interface SessionCaller {
88
+ readonly principal: Principal | undefined;
89
+ readonly fleet: boolean;
90
+ }
91
+ /**
92
+ * 🔴 **会话行属主门上「无主行放行」臂的唯一判据**(S-1770 读面律;会话面谓词 `sessionOwnerReadable`、fork / 删除 / 同步族的存量互认臂 {@link legacyUnownedSessionOk}、
93
+ * 审批豁免面、notify 面的无主臂都经它,不各写一份):
94
+ * · 无主的**普通**会话行 ⇒ 成立(各臂照它自己的其余条件);
95
+ * · 无主的 **placed** 行(委派子代:属主 = 委派树根的属主,`delegated-session-owner.ts`)⇒ 只对 **fleet 调用方**成立({@link SessionCaller}:dev-open 部署上的任何调用方,
96
+ * 或出示了 service 凭据、不报主体的调用方 —— 只配 token 的单用户部署、BFF 的系统调用)。任何具名主体(含经 BFF 代发的)与没有 service 凭据的无身份调用方都不成立:
97
+ * 这一行要么属于一棵无身份调用方的树,要么属主还没定下(升级前写下、回填还没判出 / 判断时来源还没有属主)—— 后者把它当公共行,就是把别人那棵树的子代转录交给拿到 id 的人。
98
+ * · 有主的行 ⇒ 不在本臂。
99
+ */
100
+ export declare function ownerlessRowOpen(cell: SessionOwnerCell, caller: SessionCaller): boolean;
101
+ /** 读一只会话行的 {@link SessionOwnerCell}:无主时问一次店的 `placementOf`(既有读口;店不交它 = 不实现 placement = 没有 placed 行)。`owner` = 调用点那一读。 */
102
+ export declare function sessionOwnerCellOf(store: Pick<OwnerAwareSessionStore, "placementOf"> | undefined, sessionId: string, owner: string | null): Promise<SessionOwnerCell>;
54
103
  /** [ref] 窄互认第二臂的租户键字面量 —— 单机形下 core 写侧 `spec.principal ?? "default"` 铸出的那一个,
55
104
  * 也是壳 [ref]G1 翻面后随请求带的那一个。**不是哨兵**([ref] 措辞):它是单机形的真租户键。 */
56
105
  export declare const LEGACY_UNOWNED_AMNESTY_TENANT = "default";
@@ -66,14 +115,19 @@ export declare const LEGACY_UNOWNED_AMNESTY_TENANT = "default";
66
115
  *
67
116
  * 🔒 **三条件缺一不放行**(与第一臂同源,不是兼容臂):
68
117
  * ① 单机形 `!requirePrincipal` —— 多租部署本臂**死路**(header 必到、键必具名);
69
- * ② 存量行恰为无主 `owner === null` —— 任何具名 owner 的行不受影响;
118
+ * ② 存量行恰为无主 `owner === null`、且不是委派子代的 placed 行(无主臂的唯一判据 {@link ownerlessRowOpen})—— 任何具名 owner 的行不受影响;
70
119
  * ③ 调用方恰为 `default` 主 —— 非 default 的具名主对无主行仍走原门(404 / deleted:false)。
71
120
  * 反向风险有界:①限定的单机形本就单用户,把无主行判给 default 主不产生跨租外泄。
72
121
  *
73
122
  * 🪦 墓碑注(与第一臂同源):这是两个表示法之间的**过渡承接**。存量随会话生命周期消亡后,本臂
74
123
  * 连同 {@link LEGACY_UNOWNED_AMNESTY_TENANT} 与几处调用点可**整删**,不留兼容层。
75
124
  *
76
- * ── 逐面判过、**刻意不放行**的三处(免得后人当遗漏再补一次)────────────────────────────────
125
+ * ── 同步族(`…/sync/*`:拉取 / 推送 / 开导入 / 导入第二阶段)──────────────────────────────────
126
+ * 同步族四口自 7.134.0 同走本臂(表态见 7.134.0 发车帖;三条件不变):属主门是 `http/routes/session-sync.ts` 的一只布尔 `mayTouch`,与 fork / 删除同一只谓词、同一对读法
127
+ * (`sessionCallerOf` + `sessionOwnerCellOf`),不另立判据。这一族带破坏性的 import 腿,但单用户形下 `default` 是唯一用户,与已走本臂的删除同级,没有新增跨租风险;
128
+ * 多租形、非 `default` 主体、无主的 placed 行仍与不存在的会话同码同体。暂存句柄的归属不因本臂放宽(非 fleet 调用方只推自己开的暂存,fleet 调用方照旧可推任何一张)。
129
+ *
130
+ * ── 逐面判过、**刻意不放行**的两处(免得后人当遗漏再补一次)────────────────────────────────
77
131
  * · `runOwnerOk` 三读面(head / audit 单读 / events):`owner === null` 那一支**本来就放行**
78
132
  * (判据是 `owner !== null && principal !== owner`),不需要本臂。[ref] 帖文里「单读 404」一句
79
133
  * 经亲读实现行**不成立**,已加现状钉(session-head-probe.test.ts)锁住这个事实。
@@ -82,22 +136,19 @@ export declare const LEGACY_UNOWNED_AMNESTY_TENANT = "default";
82
136
  * 这把键,替它读写规则只能**猜**一个 principal(猜错 = 200 说已生效,其实没有哪条 run 读它)。已经存下的规则不受影响:
83
137
  * 它们存在当初那个属主的键下,那个 principal 的 run 照读照生效(7.119.0 前 local 后端重启即把属主忘掉,于是那些规则
84
138
  * 在跑、却读不到也删不掉 —— 修在属主落盘,不在放宽本门)。放宽它等于制造一条 fail-open,不在本臂辖域。
85
- * · `…/sync/*`(PULL/PUSH/import):PULL 虽是只读、形同 workspace,但这一族**带一条破坏性 import 腿**
86
- * (`isDestructiveSessionWrite` 明列),而 [ref]/[ref] 裁的是会话**读面与生命周期**四门。放宽一个
87
- * 写面族超出裁定射程 —— 要做另立一次表态,不在本臂里顺手带过。
88
139
  *
89
140
  * @param requirePrincipal `config.requirePrincipal`(条件①)
90
- * @param gateOwner `sessionOwnerScope(ForWrite)` 判出的**已验证**主(条件③;fleet-wide 面不经本臂)
91
- * @param owner 存量行的 owner 列(条件②)
141
+ * @param caller 调用方(条件③ 读它验过的主体;fleet-wide 面不经本臂)
142
+ * @param cell 存量行那一格(条件②:无主,且经无主臂的唯一判据 {@link ownerlessRowOpen} —— 无主的委派子代 placed 行不在本臂,S-1770)
92
143
  */
93
- export declare function legacyUnownedSessionOk(requirePrincipal: boolean, gateOwner: string | null, owner: string | null): boolean;
144
+ export declare function legacyUnownedSessionOk(requirePrincipal: boolean, caller: SessionCaller, cell: SessionOwnerCell): boolean;
94
145
  /**
95
146
  * {@link legacyUnownedSessionOk} 的**条件①③**半支 —— 只判「这个部署形 × 这个调用方」是否落在窄臂辖域内。
96
147
  *
97
148
  * 🔴 单独存在的唯一理由是**列表面**:`GET /v1/sessions` 的第三条件(`owner === null`)不在 JS 里判,
98
149
  * 它是 store 侧 `owner = ? OR owner IS NULL` 这一句 SQL 谓词 —— 三条件仍然齐,只是第三条件由 SQL 承载
99
150
  * (`listSessions({ owner, includeUnowned })` 的 `includeUnowned` 位即本函数的返回值)。
100
- * 单实体读/写点(fork/delete/workspace)**一律**用三条件齐的 {@link legacyUnownedSessionOk},不要用本函数。
151
+ * 单实体读/写点(fork/delete/同步族/workspace)**一律**用三条件齐的 {@link legacyUnownedSessionOk},不要用本函数。
101
152
  */
102
153
  export declare function legacyUnownedAmnestyOpen(requirePrincipal: boolean, gateOwner: string | null): boolean;
103
154
  export type OwnerAwareSessionStore = SessionStore & {
@@ -106,6 +157,13 @@ export type OwnerAwareSessionStore = SessionStore & {
106
157
  * 是 `src/session-incarnation.ts`。只关心属主的消费点照旧读 `ownerOf`;判「这还是不是同一位住客」的读它。 */
107
158
  sessionFaceOf?: SessionFaceReader;
108
159
  register?: (sessionId: string, owner: string | null) => Promise<void>;
160
+ /** S-1770 委派继承律的**认领口** —— 只在记录委派树的店上(SQL 两店,只命中 placed 行;local 店没有):判断时读到的那一位住客仍是无主的 placed 行 ⇒ 写 `owner`,
161
+ * 返回是否写成(只 CAS 子行自己)。唯一调用方 = 停驻挂点的 `settleDelegatedOwner`(`delegated-session-owner.ts`;M23 走同一句 SQL);规则写在那个模块。 */
162
+ claimOwnerless?: OwnerlessClaim;
163
+ /** [ref] placed 分区的元组读口(店的既有读口,一行 PK 读;`undefined` = 普通会话或无此行)。店不交它 = 不实现 placement = 没有 placed 行(local 店)。两个读者:
164
+ * ① 会话面的属主门只在属主为 NULL 时读它一次(无主的 placed 行不是公共行,`http/principal-gate.ts` 的 `sessionOwnerCellOf`);
165
+ * ② 会话视图读它投影体顶层 `agent` 键(`audit.ts` 的 `sessionAgentOf`,只在视图读上付):店没有这一面 ⇒ 键缺席,读面不凭别处另铸子代身份。 */
166
+ placementOf?: (sessionId: string) => Promise<SessionPlacementRecord | undefined>;
109
167
  /** E16 — enumerate the caller's sessions (owner-filtered on the session store's OWN owner column).
110
168
  * `includeUnowned` = [ref] 窄互认第二臂的**第三条件**(`owner IS NULL`)的 SQL 承载位:置真时
111
169
  * 谓词放宽为 `owner = ? OR owner IS NULL`。只在 `owner` 在场时有意义(路由已判①③);`owner` 缺席
@@ -269,24 +327,39 @@ export declare function createAuthorizer(config: ServiceConfig, sessionStore: Ow
269
327
  * SendUserFile ledger scope + the /v1/sendfile-links fleet-wide default; the resource-suspend isolation
270
328
  * key), `"_leader"` merges with the leader durable-approval owner scope (a tenant so named could list/
271
329
  * decide internal leader approvals), `"_leader-…"` with the leader resource-suspend scope family, and
272
- * `"__none__"` with the missing-principal placeholder scope on the decide/pending gates.
330
+ * `"__none__"` with the missing-principal read placeholder of the approval queue faces (≤ 7.132.x replicas).
273
331
  * - length > 190 is REJECTED: every scope/owner column the principal lands in VERBATIM is VARCHAR(190)
274
332
  * (sendfile_link.scope, approval.owner/scope, memory-engine scope, rate-limit keys, …) — an over-long
275
333
  * principal would truncate/500 at the INSERT and split one identity into two.
276
334
  * Throws a typed 400 `HttpError` (the HTTP layer maps it); the message names the rule (a reserved identifier /
277
335
  * the length cap), never a secret and never the internal scope's semantics. */
278
336
  export declare const PRINCIPAL_MAX_LENGTH = 190;
337
+ /**
338
+ * 一个被受理的主体串最多占多少个 UTF-8 字节 —— 按**字节**计宽的身份列(TiDB `VARBINARY(n)`)的列宽**唯一来源**(派生,不手抄第二个数)。
339
+ * {@link PRINCIPAL_MAX_LENGTH} 数的是 UTF-16 码元(`string.length`),一个码元最坏落 3 个 UTF-8 字节:BMP 字符 3 字节;代理对 4 字节 / 2 码元;
340
+ * 孤代理按 U+FFFD 编,3 字节。按**字符**计宽的列(`VARCHAR(n)`:TiDB utf8mb4 / PG 都数码点)直接用 {@link PRINCIPAL_MAX_LENGTH} —— 码点数 ≤ 码元数。
341
+ */
342
+ export declare const PRINCIPAL_MAX_UTF8_BYTES: number;
279
343
  /** 修1 — the SINGLE registry of internal sentinel/scope identifiers a tenant principal must never equal.
280
344
  * 🔴 新增内部 sentinel scope 必须在此登记(精确值加 RESERVED_PRINCIPALS,前缀族加
281
345
  * RESERVED_PRINCIPAL_PREFIXES),否则 assertPrincipalShape 会放行同名租户 = 与内部 scope 行合流的超范围面。
282
346
  * Current entries (where each is minted): `"_"` = single-user/anonymous sentinel (checkpoint/sendfile/
283
- * resource-suspend scope family); `"__none__"` = missing-principal placeholder on the decide/pending gates
284
- * (http/server.ts `principal ?? "__none__"`); `"_leader"` = leader durable-approval scope
347
+ * resource-suspend scope family); `"__none__"` = the missing-principal read placeholder of the approval
348
+ * queue faces up to 7.132.x (7.133.0 reads the anonymous group `"_"` instead; still reserved because a 7.132.x replica in the same
349
+ * rolling upgrade lists that scope for anonymous callers, so a tenant by that name would merge with them there); `"_leader"` = leader durable-approval scope
285
350
  * (src/leader/wire.ts); prefix `"_leader-"` = the leader resource-suspend scope family
286
- * (`_leader-resource-<workerId>`, src/leader/wire.ts). NB the prefix REQUIRES the hyphen — a tenant named
351
+ * (`_leader-resource-<workerId>`, src/leader/wire.ts); prefix `"_orphan:"` = the owner written on a delegated session whose
352
+ * delegation ancestors are gone (`_orphan:<sessionId>`, src/delegated-session-owner.ts) — a caller allowed to authenticate as it
353
+ * would read that transcript. NB the `_leader-` prefix REQUIRES the hyphen — a tenant named
287
354
  * `_leaderX` collides with nothing internal and stays valid (boundary pinned by test). */
288
355
  export declare const RESERVED_PRINCIPALS: readonly string[];
289
356
  export declare const RESERVED_PRINCIPAL_PREFIXES: readonly string[];
357
+ /** The principal-shape rule as a VERDICT (the one judge; {@link assertPrincipalShape} is its throwing reader). `undefined` = a
358
+ * principal may carry this string; otherwise which rule it breaks. A reader that only needs "can this string be anyone?" (the
359
+ * organization delivery gate's recipient: a string that breaks the rule is nobody, refused with the same bytes as an unknown
360
+ * principal) reads the verdict instead of catching the throw. */
361
+ export type PrincipalShapeProblem = "reserved" | "too_long";
362
+ export declare function principalShapeProblem(principal: string): PrincipalShapeProblem | undefined;
290
363
  export declare function assertPrincipalShape(principal: string): void;
291
364
  /** The checkpoint `scope` column's single-user/anonymous sentinel — the same "_" documented above
292
365
  * ({@link RESERVED_PRINCIPALS}) as the checkpoint/sendfile/resource-suspend scope family's shared value.
@@ -321,6 +394,10 @@ export declare const encodeCheckpointScope: (principal: string | null | undefine
321
394
  /** Decode a checkpoint `scope` column value back to a principal — {@link CHECKPOINT_PUBLIC_SCOPE} maps to
322
395
  * `undefined` (no principal), everything else passes through verbatim. */
323
396
  export declare const decodeCheckpointScope: (scope: string) => string | undefined;
397
+ /** A checkpoint `scope` value read as the **owner column** of the rows it governs (run rows, live / crash asks): {@link CHECKPOINT_PUBLIC_SCOPE}
398
+ * ⇒ `null` (the null-owner rows: tasks submitted without a principal), everything else verbatim. The one translation from the approval faces'
399
+ * scope to an owner filter — stores filter on the owner column and never recognize `"_"` themselves. */
400
+ export declare const ownerOfCheckpointScope: (scope: string) => string | null;
324
401
  /** Read the authenticated principal from the trusted header. Never from the body. */
325
402
  export declare function principalFrom(req: IncomingMessage, config: ServiceConfig): string | undefined;
326
403
  /** Header carrying the crypto-verified principal JWT on a direct-door worker (the issuer signs it; the worker only
@@ -342,5 +419,8 @@ export declare function setSsoPrincipal(req: IncomingMessage, principal: string)
342
419
  export declare function ssoVerifiedPrincipal(req: IncomingMessage): string | undefined;
343
420
  export declare function setSsoScope(req: IncomingMessage, scope: string): void;
344
421
  export declare function ssoVerifiedScope(req: IncomingMessage): string | undefined;
422
+ export declare function setSsoRole(req: IncomingMessage, role: string): void;
423
+ export declare function ssoVerifiedRole(req: IncomingMessage): string | undefined;
345
424
  export declare function verifiedPrincipal(req: IncomingMessage, config: ServiceConfig): string | undefined;
425
+ export {};
346
426
  //# sourceMappingURL=security.d.ts.map
package/dist/security.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import { uuidv7, PROJECT_ID_REGEX } from "@sema-agent/core";
2
2
  import { memoryScopeFor } from "./memory-scope.js";
3
3
  import { verifyPrincipalJwt } from "./principal-jwt.js";
4
+ import { fieldConflictFacts } from "./field-conflict.js";
5
+ import { ORPHAN_SESSION_OWNER_PREFIX } from "./delegated-session-owner.js";
4
6
  export function isDestructiveSessionWrite(method, url) {
5
7
  if (method === "DELETE")
6
8
  return /^\/v1\/sessions\/[^/]+$/.test(url);
@@ -10,9 +12,26 @@ export function isDestructiveSessionWrite(method, url) {
10
12
  return /^\/v1\/sessions\/[^/]+\/(sync\/import|fork|memory\/erase)$/.test(url);
11
13
  return false;
12
14
  }
15
+ export function mintPrincipal(verified) {
16
+ return verified;
17
+ }
18
+ export function mintDurableOwnerKey(persisted) {
19
+ return persisted;
20
+ }
21
+ export function ownerlessRowOpen(cell, caller) {
22
+ if (cell.owner !== null)
23
+ return false;
24
+ return !cell.placed || caller.fleet;
25
+ }
26
+ export async function sessionOwnerCellOf(store, sessionId, owner) {
27
+ if (owner !== null)
28
+ return { owner: mintDurableOwnerKey(owner) };
29
+ const placementOf = store?.placementOf;
30
+ return { owner: null, placed: placementOf !== undefined && (await placementOf.call(store, sessionId)) !== undefined };
31
+ }
13
32
  export const LEGACY_UNOWNED_AMNESTY_TENANT = "default";
14
- export function legacyUnownedSessionOk(requirePrincipal, gateOwner, owner) {
15
- return owner === null && legacyUnownedAmnestyOpen(requirePrincipal, gateOwner);
33
+ export function legacyUnownedSessionOk(requirePrincipal, caller, cell) {
34
+ return legacyUnownedAmnestyOpen(requirePrincipal, caller.principal ?? null) && ownerlessRowOpen(cell, caller);
16
35
  }
17
36
  export function legacyUnownedAmnestyOpen(requirePrincipal, gateOwner) {
18
37
  return !requirePrincipal && gateOwner === LEGACY_UNOWNED_AMNESTY_TENANT;
@@ -98,7 +117,7 @@ export function createAuthorizer(config, sessionStore) {
98
117
  }
99
118
  else {
100
119
  if (requireExisting) {
101
- throw createCodedHttpError(422, "request.field_conflict", "requireExistingSession requires a sessionId — there is no existing session to resume");
120
+ throw createCodedHttpError(422, "request.field_conflict", "requireExistingSession requires a sessionId — there is no existing session to resume", fieldConflictFacts("existing_session", ["requireExistingSession", "sessionId"]));
102
121
  }
103
122
  sessionId = uuidv7();
104
123
  if (register)
@@ -108,14 +127,29 @@ export function createAuthorizer(config, sessionStore) {
108
127
  };
109
128
  }
110
129
  export const PRINCIPAL_MAX_LENGTH = 190;
130
+ export const PRINCIPAL_MAX_UTF8_BYTES = PRINCIPAL_MAX_LENGTH * 3;
111
131
  export const RESERVED_PRINCIPALS = ["_", "__none__", "_leader"];
112
- export const RESERVED_PRINCIPAL_PREFIXES = ["_leader-"];
132
+ export const RESERVED_PRINCIPAL_PREFIXES = ["_leader-", ORPHAN_SESSION_OWNER_PREFIX];
133
+ export function principalShapeProblem(principal) {
134
+ if (RESERVED_PRINCIPALS.includes(principal) || RESERVED_PRINCIPAL_PREFIXES.some((p) => principal.startsWith(p)))
135
+ return "reserved";
136
+ if (principal.length > PRINCIPAL_MAX_LENGTH)
137
+ return "too_long";
138
+ return undefined;
139
+ }
113
140
  export function assertPrincipalShape(principal) {
114
- if (RESERVED_PRINCIPALS.includes(principal) || RESERVED_PRINCIPAL_PREFIXES.some((p) => principal.startsWith(p))) {
115
- throw new HttpError(400, `principal ${JSON.stringify(principal)} is reserved (an internal scope sentinel) — authenticate with a real tenant identifier`, { code: "principal_reserved" });
116
- }
117
- if (principal.length > PRINCIPAL_MAX_LENGTH) {
118
- throw new HttpError(400, `principal exceeds ${PRINCIPAL_MAX_LENGTH} characters (the scope-column width every store pins, e.g. sendfile_link.scope VARCHAR(190)); got ${principal.length}`, { code: "principal_too_long" });
141
+ const problem = principalShapeProblem(principal);
142
+ switch (problem) {
143
+ case undefined:
144
+ return;
145
+ case "reserved":
146
+ throw new HttpError(400, `principal ${JSON.stringify(principal)} is reserved (an internal scope sentinel) — authenticate with a real tenant identifier`, { code: "principal_reserved" });
147
+ case "too_long":
148
+ throw new HttpError(400, `principal exceeds ${PRINCIPAL_MAX_LENGTH} characters (the scope-column width every store pins, e.g. sendfile_link.scope VARCHAR(190)); got ${principal.length}`, { code: "principal_too_long" });
149
+ default: {
150
+ const unknownProblem = problem;
151
+ throw new Error(`assertPrincipalShape: unknown problem ${String(unknownProblem)}`);
152
+ }
119
153
  }
120
154
  }
121
155
  export const CHECKPOINT_PUBLIC_SCOPE = "_";
@@ -125,6 +159,7 @@ export function backgroundScopesForCheckpointScope(scope) {
125
159
  }
126
160
  export const encodeCheckpointScope = (principal) => principal ?? CHECKPOINT_PUBLIC_SCOPE;
127
161
  export const decodeCheckpointScope = (scope) => (scope === CHECKPOINT_PUBLIC_SCOPE ? undefined : scope);
162
+ export const ownerOfCheckpointScope = (scope) => decodeCheckpointScope(scope) ?? null;
128
163
  export function principalFrom(req, config) {
129
164
  if (!config.principalHeader)
130
165
  return undefined;
@@ -153,6 +188,14 @@ export function ssoVerifiedScope(req) {
153
188
  const v = req[SSO_SCOPE];
154
189
  return typeof v === "string" && v.length > 0 ? v : undefined;
155
190
  }
191
+ const SSO_ROLES = new WeakMap();
192
+ export function setSsoRole(req, role) {
193
+ SSO_ROLES.set(req, role);
194
+ }
195
+ export function ssoVerifiedRole(req) {
196
+ const v = SSO_ROLES.get(req);
197
+ return v !== undefined && v.length > 0 ? v : undefined;
198
+ }
156
199
  export function verifiedPrincipal(req, config) {
157
200
  const sso = ssoVerifiedPrincipal(req);
158
201
  if (sso) {
@@ -27,10 +27,23 @@
27
27
  * `open` 面留白(#1077 形,见上)。
28
28
  * · `not_found` —— 留白:每一面各有自己的「不存在」答法(404 `not_found.session` / 同步口的 `resume.session_not_found` /
29
29
  * 列表里的缺席),码位各不相同,不由本表统一。
30
- * · `invalid_entry` / `invalid_fork_target` / `placement_refused` —— 留白:本仓今天没有一条 wire 口把它们作为自己的
31
- * 应答(它们在 core 的追加 / 分叉 / 放置面上抛,落进各站点既有的处置或通用 500)。给它们起名是另一件事、另一张票。
32
- * · `storage` / `conflict` / `unknown` —— 留白:`conflict` 是 run 的写撞上真并发写,落成那条 run 的 `failed` 终局
33
- * (`terminal.code: "conflict"`),提交腿不另作处置;`storage` / `unknown` 是故障类,通用 500 就是它们今天的应答。
30
+ * · `placement_refused` —— `import` 面 **409 `conflict.session_placed`**(机读位 `detail.placement_kind`,固定句);`open` 面留白。
31
+ * 同一个词在两面上说的不是同一件事:import 面只有「会话同步往一只**分区会话**(委派子代的转录)上写」一个来源
32
+ * (SQL 两店的整段换装 / 暂存提交门:目标 id 的 `placement_kind` 在场 ⇒ 不适用),调用方没做错格式、是**目标**不收这种写;
33
+ * open 面上同一个词还含「声明了分区的创建撞上了分区外 / 异组的行」两形(core `TtlSessionStore.assertPlacementAdmission` 的后两条),
34
+ * 那不是「目标是分区会话」。而 run 终局上它是引擎词汇(`terminal.code`),原样随 failed 体走 —— 若在这里分诊成 409,
35
+ * 体上的 `errorCode` 仍是引擎词 `placement_refused`,一个状态两个名字。所以 open 面留白,`invalid_session` 是同形先例。
36
+ * 409 的语义:目标会话**状态**不收这种写(重试同一个 id 同一结果;换一个会话 id 推)—— 与 `conflict.session_ownerless` / `conflict.session_children_alive` 同族。
37
+ * `detail.placement_kind` 是**常量**,不是从错误里读的:core 的 `SessionError` 只带 `code` 与句子,不带种类;今天分区种类闭集只有
38
+ * `"subagent"` 一个词,所以常量就是事实。core 给 `SessionPlacementRecord["kind"]` 加第二个词 ⇒ 下面的守卫编译红,由人裁定种类从哪儿读
39
+ * (请求 core 把种类放进错误,而不是在这里解析句子)。
40
+ * · `invalid_entry` / `invalid_fork_target` —— 留白:本仓今天没有一条 wire 口把它们作为自己的
41
+ * 应答(它们在 core 的追加 / 分叉面上抛,落进各站点既有的处置或通用 500)。给它们起名是另一件事、另一张票。
42
+ * · `conflict` —— `import` 面 **409 `conflict`**(固定句 {@link STAGED_IMPORT_OWNER_CHANGED_SENTENCE});`open` 面留白。import 面上这个词只有一个来源:
43
+ * 暂存提交的属主 CAS(`session-sync-kernel.ts` 的 `StagedCommitOwnerChangedError`,三家店在提交事务里比「此刻的属主格 = 这次导入的门判过的那一格」,
44
+ * 不等 ⇒ 回滚)—— 码位与同族的提交期同步冲突(`SyncConflictError` ⇒ 409 `conflict`「sync conflict」)同一个,出路也相同:从第一阶段重来(重新过门)。
45
+ * open 面上它是 run 的写撞上真并发写,落成那条 run 的 `failed` 终局(`terminal.code: "conflict"`),提交腿不另作处置。
46
+ * · `storage` / `unknown` —— 留白:故障类,通用 500 就是它们今天的应答。
34
47
  *
35
48
  * core 给闭集加一个词 ⇒ 下面的 `switch` 编译红,由人裁定它在两面上的答复,而不是让它静默落进某个恰好存在的臂
36
49
  * ([ref] 词表纪律;同形先例 `session-policy-wire.ts` 的 `sessionPolicyErrorHttp`)。
@@ -45,14 +58,25 @@ export interface SessionErrorWire {
45
58
  readonly status: number;
46
59
  readonly errorCode: string;
47
60
  /**
48
- * 固定句(缺席 = 发 core 的原句,过既有脱敏)。只有 import 面的 `invalid_session` 用它:core 导入门的原句点名的是
49
- * 调用方日志里的条目 id / 父子关系,修前这一口就只回一句固定话(冻结在 `test/api-error-text-freeze.test.ts` 的
50
- * 常量锚格),本批逐字不改。
61
+ * 固定句(缺席 = 发 core 的原句,过既有脱敏)。import 面的三行用它(`invalid_session` / `placement_refused` / `conflict`):店与引擎的原句
62
+ * 点名条目 id / 会话 id / 引擎内部词,wire 上只回一句固定话(各自冻结在 `test/api-error-text-freeze.test.ts` 的常量锚格)。
51
63
  */
52
64
  readonly sentence?: string;
65
+ /** 机读事实(体顶层的 `detail` 键;缺席 = 体里不带这个键)。只有 import 面的 `placement_refused` 用它:`{ placement_kind }`。 */
66
+ readonly detail?: Readonly<Record<string, string>>;
53
67
  }
54
68
  /** import 面 `invalid_session` 的固定句(修前 session-sync 暂存导入腿那一行的原文)。 */
55
69
  export declare const STAGED_IMPORT_INVALID_SENTENCE = "staged session log failed validation";
70
+ /**
71
+ * import 面 `placement_refused` 的固定句:wire 上不发引擎原句 —— 两家 SQL 店的原句不同(措辞各自铸),都点名会话 id 与引擎内部词
72
+ * (「placement partition」「delegation transcript」);固定句只说调用方能做什么(冻结在 `test/api-error-text-freeze.test.ts` 的常量锚格)。
73
+ */
74
+ export declare const PLACED_SESSION_SENTENCE = "this session holds a delegated agent's transcript, so session sync cannot overwrite it; sync into a different session id";
75
+ /**
76
+ * import 面 `conflict` 的固定句:暂存提交时会话的属主格已不是开暂存时门判过的那一格(另一副本在导入流式进行期间删建 / 认领 / 新建了它),提交事务回滚。
77
+ * 不发店的原句(它点名会话 id);固定句只说发生了什么与出路(冻结在 `test/api-error-text-freeze.test.ts` 的常量锚格)。
78
+ */
79
+ export declare const STAGED_IMPORT_OWNER_CHANGED_SENTENCE = "session owner changed while the import streamed; nothing was imported \u2014 start the sync again";
56
80
  export declare function sessionErrorHttp(code: SessionErrorCode, face: SessionErrorFace): SessionErrorWire | undefined;
57
81
  export declare function isSessionErrorCode(code: string): code is SessionErrorCode;
58
82
  //# sourceMappingURL=session-error-wire.d.ts.map
@@ -1,18 +1,25 @@
1
1
  export const STAGED_IMPORT_INVALID_SENTENCE = "staged session log failed validation";
2
+ export const PLACED_SESSION_SENTENCE = "this session holds a delegated agent's transcript, so session sync cannot overwrite it; sync into a different session id";
3
+ const PLACEMENT_KIND = "subagent";
4
+ export const STAGED_IMPORT_OWNER_CHANGED_SENTENCE = "session owner changed while the import streamed; nothing was imported — start the sync again";
2
5
  const CORRUPT_SESSION = { status: 500, errorCode: "corrupt_session" };
3
6
  const INVALID_ENTRIES = { status: 422, errorCode: "invalid_entries", sentence: STAGED_IMPORT_INVALID_SENTENCE };
7
+ const SESSION_PLACED = { status: 409, errorCode: "conflict.session_placed", sentence: PLACED_SESSION_SENTENCE, detail: { placement_kind: PLACEMENT_KIND } };
8
+ const STAGED_IMPORT_OWNER_CHANGED = { status: 409, errorCode: "conflict", sentence: STAGED_IMPORT_OWNER_CHANGED_SENTENCE };
4
9
  export function sessionErrorHttp(code, face) {
5
10
  switch (code) {
6
11
  case "corrupt_session":
7
12
  return CORRUPT_SESSION;
8
13
  case "invalid_session":
9
14
  return face === "import" ? INVALID_ENTRIES : undefined;
15
+ case "placement_refused":
16
+ return face === "import" ? SESSION_PLACED : undefined;
17
+ case "conflict":
18
+ return face === "import" ? STAGED_IMPORT_OWNER_CHANGED : undefined;
10
19
  case "not_found":
11
20
  case "invalid_entry":
12
21
  case "invalid_fork_target":
13
- case "placement_refused":
14
22
  case "storage":
15
- case "conflict":
16
23
  case "unknown":
17
24
  return undefined;
18
25
  default: {