@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
@@ -3,7 +3,7 @@ import os from "node:os";
3
3
  import fs from "node:fs/promises";
4
4
  import { spawn } from "node:child_process";
5
5
  import { hostSubprocessEnv } from "../server-secret-env.js";
6
- import { LANE_HOME_PROBE_COMMAND, judgeLaneHome } from "../lane-home-probe.js";
6
+ import { LANE_HOME_PROBE_COMMAND, LaneHomeSeat } from "../lane-home-probe.js";
7
7
  import { randomBytes } from "node:crypto";
8
8
  import { shellQuote, armPipeDestroyGrace } from "./remote-shell.js";
9
9
  import { signalNumber } from "./host-platform.js";
@@ -23,7 +23,9 @@ const unsupported = (op) => ({
23
23
  export class RemoteLocalDockerExecutionEnv {
24
24
  capabilities = { isolation: true, suspendable: false, canonicalPathAuthoritative: true };
25
25
  cwd;
26
- homeDir;
26
+ get homeDir() {
27
+ return this.homeSeat.home;
28
+ }
27
29
  cdPath = "unknown";
28
30
  cfg;
29
31
  spawnFn;
@@ -33,6 +35,7 @@ export class RemoteLocalDockerExecutionEnv {
33
35
  destroyed = false;
34
36
  connecting;
35
37
  homeRefusal;
38
+ homeSeat;
36
39
  constructor(config, deps = {}) {
37
40
  if (!config.image || !config.image.trim()) {
38
41
  config = { ...config, image: config.image ?? "" };
@@ -46,7 +49,7 @@ export class RemoteLocalDockerExecutionEnv {
46
49
  controlTimeoutMs: config.controlTimeoutMs ?? DEFAULT_CONTROL_TIMEOUT_MS,
47
50
  };
48
51
  this.cwd = this.cfg.mountPath;
49
- this.homeDir = config.homeDir;
52
+ this.homeSeat = new LaneHomeSeat({ lane: PROVIDER, declared: config.homeDir, verifiesDeclared: true, onAbsent: config.onHomeAbsent });
50
53
  this.spawnFn = deps.spawn ?? spawn;
51
54
  const id = config.id ?? randomBytes(6).toString("hex");
52
55
  const suffix = randomBytes(4).toString("hex");
@@ -117,7 +120,7 @@ export class RemoteLocalDockerExecutionEnv {
117
120
  await this.forceRemove().catch(() => { });
118
121
  return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env destroyed during connect") };
119
122
  }
120
- if (this.homeDir !== undefined && !(await this.untilOrAborted(this.probeContainerShell(), config?.abortSignal))) {
123
+ if (this.homeSeat.asks && !(await this.untilOrAborted(this.probeContainerShell(), config?.abortSignal))) {
121
124
  await this.forceRemove().catch(() => { });
122
125
  return { ok: false, error: new RemoteExecutionError("aborted", "connect aborted while detecting the container's shell") };
123
126
  }
@@ -137,14 +140,14 @@ export class RemoteLocalDockerExecutionEnv {
137
140
  return ok(this.handle);
138
141
  }
139
142
  async homeVerdict(signal) {
140
- if (this.homeDir === undefined)
143
+ if (!this.homeSeat.asks)
141
144
  return undefined;
142
145
  const probe = await this.docker(this.execRaw(LANE_HOME_PROBE_COMMAND), { signal, timeoutMs: this.cfg.controlTimeoutMs });
143
- const verdict = judgeLaneHome(PROVIDER, this.homeDir, probe.ok && probe.value.exitCode === 0 ? { ok: true, stdout: probe.value.stdout } : { ok: false, reason: probe.ok ? `exit ${probe.value.exitCode}` : probe.error.message });
144
- if (verdict === undefined)
145
- return undefined;
146
146
  if (!probe.ok && probe.error.code === "aborted")
147
147
  return new RemoteExecutionError("aborted", "connect aborted while reading the environment's home");
148
+ const verdict = this.homeSeat.settle(probe.ok && probe.value.exitCode === 0 ? { ok: true, stdout: probe.value.stdout } : { ok: false, reason: probe.ok ? `exit ${probe.value.exitCode}` : probe.error.message });
149
+ if (verdict === undefined)
150
+ return undefined;
148
151
  const error = new RemoteExecutionError("connect_failed", verdict.refusal);
149
152
  if (verdict.answered)
150
153
  this.homeRefusal = error;
@@ -174,7 +177,7 @@ export class RemoteLocalDockerExecutionEnv {
174
177
  }
175
178
  const insp = await this.docker(["inspect", "-f", "{{.State.Running}}", this.containerName], { timeoutMs: this.cfg.controlTimeoutMs });
176
179
  if (insp.ok && insp.value.exitCode === 0 && insp.value.stdout.trim() === "true") {
177
- if (this.containerId === undefined && this.homeDir !== undefined)
180
+ if (this.containerId === undefined && this.homeSeat.asks)
178
181
  await this.probeContainerShell();
179
182
  const home = this.containerId === undefined ? await this.homeVerdict() : undefined;
180
183
  if (home !== undefined)
@@ -1,22 +1,14 @@
1
1
  import { type SshHostKeyPolicy } from "../ssh-host-key.js";
2
2
  import { FileError, ExecutionError, RemoteExecutionError, type ExecutionEnv, type RemoteExecutionEnv, type WorkspaceHandle, type FileInfo, type Result, type SymlinkChain, type OutputChunk, type ExecStreamOptions, type RemoteConnectConfig, type SnapshotId, type SessionToken, type VmLifecycleOptions, type ExecutionEnvFactory } from "@sema-agent/core";
3
+ import { type LaneHomeConfig } from "../lane-home-probe.js";
3
4
  type ExecOpts = Parameters<ExecutionEnv["exec"]>[1];
4
- export interface SshEnvConfig {
5
+ export interface SshEnvConfig extends LaneHomeConfig {
5
6
  host: string;
6
7
  port?: number;
7
8
  username: string;
8
9
  /** PEM private key — from the adapter-held secret env (NEVER the workspace). [ref] §5 #2. */
9
10
  privateKey: string;
10
11
  passphrase?: string;
11
- /**
12
- * S-213④ / core [ref] —— 本执行环境的 **home 目录**(`ExecutionEnv.homeDir` 座,绝对路径,本环境自己的
13
- * 命名空间里)。`~/…` 形权限规则(`Edit(~/.ssh/**)`)对**跑在本 env 里的调用**就以它为基。
14
- * 缺席 ⇒ 本 env 声明「我没有 home」,core 对 `~/` 规则判 `unreadable`(fail-closed 的 ask),
15
- * 绝不回落引擎进程自己的 home(那在远端腿上是另一个用户的另一个目录 —— 静默守错目录正是 [ref] 的病)。
16
- * 值由 `boot/execution-env.ts` 从**唯一属主** `execution-lane-caps.ts` 的 `executionLaneHomeDir` 递进来;
17
- * adapter 不自算(两处各算一份 = 422 门与引擎对同一条规则给相反答案)。
18
- */
19
- homeDir?: string;
20
12
  /** Workspace root; relative paths resolve against it. Default "." (the login dir). */
21
13
  mountPath?: string;
22
14
  /** Connect handshake bound (ms). */
@@ -92,9 +84,11 @@ export declare class RemoteSshExecutionEnv implements RemoteExecutionEnv {
92
84
  * =重连风暴);②共享尝试**不携带任何 caller 信号**(首创者的 pre-abort 不得毒化 joiner;单 caller
93
85
  * 取消只退它自己的等待,底层握手余命 ≤ readyTimeout 有界,彻底取消归 destroy 代际闸)。 */
94
86
  private inFlight?;
95
- /** core `ExecutionEnv.homeDir`([ref]):本沙箱/目标机上执行用户的 home。见配置同名字段。 */
96
- readonly homeDir?: string | undefined;
97
- /** 家目录声明 × 实测的判定,按连接记(同一条连接只问一次;重连换了连接再问 —— 登录用户不变时答案相同,代价一次往返)。 */
87
+ /** core `ExecutionEnv.homeDir`([ref]):目标机上登录用户的 home —— 声明胜,否则连上之后实测的 `$HOME`,见 {@link LaneHomeSeat}。读时才取(没声明时连上之前缺席)。 */
88
+ get homeDir(): string | undefined;
89
+ /** 家目录的座(声明胜 · 否则连上之后实测 · 实测缺席 ⇒ 缺席 + 一行 warn)。本车道对声明对证。 */
90
+ private readonly homeSeat;
91
+ /** 家目录声明 × 实测的判定,按连接记(同一条连接只问一次;重连换了连接再问 —— 登录用户不变时答案相同,代价一次往返;没声明且已量到绝对路径之后不再问)。 */
98
92
  private homeChecked?;
99
93
  /** 目标机答了而与声明不一致 ⇒ 这只环境的拒,记一次、此后每个面原样回它(同一目标机同一登录用户同一答案:不再问、不再发命令)。 */
100
94
  private homeRefusal?;
@@ -126,9 +120,9 @@ export declare class RemoteSshExecutionEnv implements RemoteExecutionEnv {
126
120
  /** 家目录判定的 per-caller 可中止等待(共享的判定承诺不动,只退本 caller)。 */
127
121
  private raceVerdictAbort;
128
122
  /**
129
- * 家目录:声明在场(`homeDir`,只来自 `REMOTE_EXEC_HOME_DIR` —— 本车道没有自己的缺省)⇒ 在这条连接上问一次 `$HOME`(登录用户的 shell 答的,
130
- * 与命令同一个用户、同一份登录 env),交唯一判官 `judgeLaneHome`。答了而不一致 / 不是绝对路径 ⇒ 拒记在环境上(每个面原样回它),命令一条都不发;
131
- * 没答上(通道开不出 / 非零退出 / 超时)⇒ 这一次同样不开、不记,下一次调用重问。
123
+ * 家目录:座要问({@link LaneHomeSeat.asks}:声明在场 ⇒ 对证;没声明 ⇒ 量到绝对路径为止)⇒ 在这条连接上问一次 `$HOME`(登录用户的 shell 答的,与命令同一个用户、同一份登录 env),读数交座。
124
+ * 声明在场:答了而不一致 / 不是绝对路径 ⇒ 拒记在环境上(每个面原样回它),命令一条都不发;没答上(通道开不出 / 非零退出 / 超时)⇒ 这一次同样不开、不记,下一次调用重问。
125
+ * 没声明:座收下绝对路径;答不上 / 不是绝对路径 ⇒ 座缺席 + 一行 warn,命令照跑(没声明就没有声明可拒)—— 判定对这条连接记下,换了连接再问。
132
126
  * 问的是裸命令(不经工作目录守卫:工作目录进不去不影响家目录的答案)。上界 = 握手的 `readyTimeoutMs`(问一个环境变量,不是跑用户命令)。
133
127
  */
134
128
  private homeVerdict;
@@ -7,7 +7,7 @@ import { buildLivenessCut, commandBoundsOf } from "../exec-bounds.js";
7
7
  import { createPosixShellFs } from "./posix-shell-fs.js";
8
8
  import { FileError, ExecutionError, RemoteExecutionError, withRetry, RollingTailBuffer, markTruncated, } from "@sema-agent/core";
9
9
  import { fileErrorFromExec } from "./remote-env-file-error.js";
10
- import { LANE_HOME_PROBE_COMMAND, judgeLaneHome } from "../lane-home-probe.js";
10
+ import { LANE_HOME_PROBE_COMMAND, LaneHomeSeat } from "../lane-home-probe.js";
11
11
  const PROVIDER = "ssh";
12
12
  const ok = (value) => ({ ok: true, value });
13
13
  const unsupported = (op) => ({
@@ -25,7 +25,10 @@ export class RemoteSshExecutionEnv {
25
25
  destroyed = false;
26
26
  connEpoch = 0;
27
27
  inFlight;
28
- homeDir;
28
+ get homeDir() {
29
+ return this.homeSeat.home;
30
+ }
31
+ homeSeat;
29
32
  homeChecked;
30
33
  homeRefusal;
31
34
  cdPath = "unknown";
@@ -47,7 +50,7 @@ export class RemoteSshExecutionEnv {
47
50
  hostKey: config.hostKey,
48
51
  };
49
52
  this.cwd = this.cfg.mountPath;
50
- this.homeDir = config.homeDir;
53
+ this.homeSeat = new LaneHomeSeat({ lane: PROVIDER, declared: config.homeDir, verifiesDeclared: true, onAbsent: config.onHomeAbsent });
51
54
  }
52
55
  resolve(p) {
53
56
  return path.posix.isAbsolute(p) ? path.posix.normalize(p) : path.posix.normalize(path.posix.join(this.cwd, p));
@@ -110,13 +113,13 @@ export class RemoteSshExecutionEnv {
110
113
  if (this.homeRefusal)
111
114
  return { ok: false, error: this.homeRefusal };
112
115
  const transport = await this.ensureTransport(signal);
113
- if (!transport.ok || this.homeDir === undefined)
116
+ if (!transport.ok || !this.homeSeat.asks)
114
117
  return transport;
115
118
  const conn = this.conn;
116
119
  if (!conn)
117
120
  return { ok: false, error: new ExecutionError("shell_unavailable", "SSH connection dropped right after connect") };
118
121
  if (this.homeChecked?.conn !== conn) {
119
- const checked = { conn, verdict: this.homeVerdict(conn, this.homeDir) };
122
+ const checked = { conn, verdict: this.homeVerdict(conn) };
120
123
  this.homeChecked = checked;
121
124
  void checked.verdict.then((r) => {
122
125
  if (!r.ok && this.homeRefusal === undefined && this.homeChecked === checked)
@@ -148,9 +151,10 @@ export class RemoteSshExecutionEnv {
148
151
  signal.removeEventListener("abort", onAbort);
149
152
  return raced;
150
153
  }
151
- homeVerdict(conn, declared) {
154
+ homeVerdict(conn) {
152
155
  return new Promise((resolve) => {
153
- let out = "";
156
+ const chunks = [];
157
+ let outBytes = 0;
154
158
  let settled = false;
155
159
  let channel;
156
160
  let channelClosed = false;
@@ -173,7 +177,7 @@ export class RemoteSshExecutionEnv {
173
177
  const closeTrouble = closeChannel(channel);
174
178
  if (closeTrouble !== undefined && !probe.ok)
175
179
  probe = { ok: false, reason: `${probe.reason}; ${closeTrouble}` };
176
- const verdict = judgeLaneHome(PROVIDER, declared, probe);
180
+ const verdict = this.homeSeat.settle(probe);
177
181
  if (verdict === undefined)
178
182
  return resolve(ok(undefined));
179
183
  const error = new ExecutionError("shell_unavailable", verdict.refusal);
@@ -191,12 +195,14 @@ export class RemoteSshExecutionEnv {
191
195
  channel = stream;
192
196
  stream
193
197
  .on("data", (d) => {
194
- if (out.length < 4096)
195
- out += d.toString("utf8");
198
+ if (outBytes < 4096) {
199
+ chunks.push(d);
200
+ outBytes += d.length;
201
+ }
196
202
  })
197
203
  .on("close", (code) => {
198
204
  channelClosed = true;
199
- settle(code === 0 ? { ok: true, stdout: out } : { ok: false, reason: code == null ? "the channel closed without an exit status" : `exit ${code}` });
205
+ settle(code === 0 ? { ok: true, stdout: Buffer.concat(chunks).toString("utf8") } : { ok: false, reason: code == null ? "the channel closed without an exit status" : `exit ${code}` });
200
206
  });
201
207
  stream.stderr.on("data", () => { });
202
208
  });
@@ -2,13 +2,14 @@ import type { TaskStatus } from "@sema-agent/core";
2
2
  import type { Pool as MySqlPool } from "mysql2/promise";
3
3
  import type { Pool as PgPool } from "pg";
4
4
  import { type UsageRow } from "../usage-analytics.js";
5
- import { type CreateRunOutcome, type NewRunMeta, type RunIdemBinding, type RunRecord, type SessionSummary, type RunEvent, type PersistedTaskResult, type LatestRunRef } from "./store-contracts.js";
6
- import { type SqlDriver } from "./sql-driver.js";
5
+ import { type ClaimFenced, type CreateRunOutcome, type NewRunMeta, type RunIdemBinding, type RunListQuery, type RunRecord, type SessionSummary, type RunEvent, type PersistedTaskResult, type LatestRunRef } from "./store-contracts.js";
6
+ import { type SqlDriver, type SqlTxConn } from "./sql-driver.js";
7
7
  import type { LedgerEventType } from "../trace/ledger-events.js";
8
8
  import { type SpaceCommitClaim, type SpaceCommitReading, type SpaceCommitState } from "../workspace-space-commit.js";
9
9
  export type { RunRecord, SessionSummary, RunEvent, LatestRunRef } from "./store-contracts.js";
10
10
  export declare class SqlRunStore {
11
- protected readonly db: SqlDriver;
11
+ readonly db: SqlDriver;
12
+ /** `db` 公开只读:会话认领住在这只池背后的库里,写在认领栅栏里的店(workflow 子代决定行)读写都走它 —— 那只店的池只有这一处来源。 */
12
13
  constructor(db: SqlDriver);
13
14
  /** Pick the dialect's SQL text. Both statements stay written out at the call site ON PURPOSE. */
14
15
  private q;
@@ -159,6 +160,17 @@ export declare class SqlRunStore {
159
160
  * (一个输掉的认领不许覆盖赢家的持有记录——同一条条件 UPDATE 天然保证)。
160
161
  */
161
162
  markResuming(taskId: string, instanceId: string): Promise<boolean>;
163
+ /**
164
+ * 认领当写栅栏用(进程内两孪生的契约 = `store-contracts.ts` 的 `InProcessClaimFence`;本孪生是它的事务形):一个事务 ——
165
+ * ① 锁读这条 run 行且它在跑(`task_run … FOR UPDATE`);② 锁读会话的认领行且它是这条 run 的(`task_active … FOR UPDATE`);
166
+ * ③ 刷新这条 run 的活性戳;④ `write` 在**同一个事务连接**上跑;提交即放锁。任一没读到 ⇒ 不持有,不刷新、`write` 不调。`write` 抛或提交失败 ⇒ 整只抛。
167
+ * 🔴 **取锁序 = task_run → task_active**,与 `setTerminal` / `reapStale` / `reapSuspended` 同序(它们先改 run 行、再删认领行):两行用两条语句按序锁,
168
+ * 不用连表锁读(那一形的先后由计划器定,与终局 / 收割相反时两只事务各攥一行成环)。`createRun` 只插认领行、`deleteBySession` 见认领行在就中止,
169
+ * 都不与本序成环。锁住之后:收割的改行 / 终局与收割的删认领 / 别人的建行都排在本事务之后,收割器提交后重判 `updated_at` 看到的是刚刷的。
170
+ * 锁读没命中时 InnoDB 可能取一把间隙锁;本事务此后不往这两张表插任何行(见 space-commit 那条锁形注)。
171
+ * 担保止于提交:判出「持有」的是这只事务;提交确认在路上迟到、这一跑随后失去认领,不在本栅栏的射程里(调用方的已知边界)。
172
+ */
173
+ whileHoldingClaim<T>(sessionId: string, taskId: string, write: (tx: SqlTxConn) => Promise<T>): Promise<ClaimFenced<T>>;
162
174
  /** The active run's taskId for a session (the `task_active` claim) — lets resume find the suspended run row. */
163
175
  getActiveTaskId(sessionId: string): Promise<string | undefined>;
164
176
  /** Shared row→record mapper — byte-identical between dialects (both drivers hand back the same column
@@ -194,17 +206,7 @@ export declare class SqlRunStore {
194
206
  * built with plain string pushes in call order. PG's `$n` needs a NUMBERED placeholder per param, so that arm
195
207
  * keeps its own positional-counter closure (`p()`) — collapsing the two into one shared counter would hide
196
208
  * the very placeholder-numbering divergence this file exists to keep visible. */
197
- listRuns(opts: {
198
- status?: string;
199
- jobId?: string;
200
- source?: string;
201
- owner?: string;
202
- cursor?: {
203
- createdAt: string;
204
- taskId: string;
205
- };
206
- limit: number;
207
- }): Promise<RunRecord[]>;
209
+ listRuns(opts: RunListQuery): Promise<RunRecord[]>;
208
210
  /**
209
211
  * S-297 —— 这个会话**最近一条腿**的窄指针(`/mcp` 面板的 `lastLegMcp` 与任何「上一条 run 是谁」的读面共用)。
210
212
  *
@@ -269,6 +269,21 @@ export class SqlRunStore {
269
269
  const { affected } = await this.db.query(this.q("UPDATE task_run SET status = 'running', cancel_requested = 0, preempt_requested = 0, result = NULL, instance_id = ?, updated_at = ? WHERE task_id = ? AND status IN ('suspended','needs_review')", "UPDATE task_run SET status = 'running', cancel_requested = 0, preempt_requested = 0, result = NULL, instance_id = $1, updated_at = $2 WHERE task_id = $3 AND status IN ('suspended','needs_review')"), [instanceId, new Date(), taskId]);
270
270
  return affected === 1;
271
271
  }
272
+ async whileHoldingClaim(sessionId, taskId, write) {
273
+ let fenced = { held: false };
274
+ await this.tx(async (conn) => {
275
+ const run = await conn.query(this.q("SELECT task_id FROM task_run WHERE task_id = ? AND status = 'running' FOR UPDATE", "SELECT task_id FROM task_run WHERE task_id = $1 AND status = 'running' FOR UPDATE"), [taskId]);
276
+ if (run.rows.length === 0)
277
+ return;
278
+ const claim = await conn.query(this.q("SELECT task_id FROM task_active WHERE session_id = ? FOR UPDATE", "SELECT task_id FROM task_active WHERE session_id = $1 FOR UPDATE"), [sessionId]);
279
+ const holder = claim.rows[0];
280
+ if (holder === undefined || String(holder.task_id) !== taskId)
281
+ return;
282
+ await conn.query(this.q("UPDATE task_run SET updated_at = ? WHERE task_id = ?", "UPDATE task_run SET updated_at = $1 WHERE task_id = $2"), [new Date(), taskId]);
283
+ fenced = { held: true, value: await write(conn) };
284
+ });
285
+ return fenced;
286
+ }
272
287
  async getActiveTaskId(sessionId) {
273
288
  const { rows } = await this.db.query(this.q("SELECT task_id FROM task_active WHERE session_id = ?", "SELECT task_id FROM task_active WHERE session_id = $1"), [sessionId]);
274
289
  return rows[0] ? String(rows[0].task_id) : undefined;
@@ -335,7 +350,10 @@ export class SqlRunStore {
335
350
  where.push("source = ?");
336
351
  params.push(opts.source);
337
352
  }
338
- if (opts.owner !== undefined) {
353
+ if (opts.owner === null) {
354
+ where.push("owner IS NULL");
355
+ }
356
+ else if (opts.owner !== undefined) {
339
357
  where.push("owner = ?");
340
358
  params.push(opts.owner);
341
359
  }
@@ -362,7 +380,9 @@ export class SqlRunStore {
362
380
  where.push(`job_id = ${p(opts.jobId)}`);
363
381
  if (opts.source !== undefined)
364
382
  where.push(`source = ${p(opts.source)}`);
365
- if (opts.owner !== undefined)
383
+ if (opts.owner === null)
384
+ where.push("owner IS NULL");
385
+ else if (opts.owner !== undefined)
366
386
  where.push(`owner = ${p(opts.owner)}`);
367
387
  if (opts.cursor) {
368
388
  const a = p(new Date(opts.cursor.createdAt));
@@ -21,6 +21,8 @@ export const CENTRAL_SCHEMA_INDEXES = [
21
21
  { table: "workflow_run", name: "idx_wfrun_scope_created", columns: ["scope", "created_at_ms"] },
22
22
  { table: "workflow_run", name: "idx_wfrun_scope_status", columns: ["scope", "status"] },
23
23
  { table: "workflow_agent_session", name: "idx_wfagentsess_run", columns: ["run_id"] },
24
+ { table: "workflow_park_decision", name: "idx_wfparkdec_host", columns: ["host_session_id"] },
25
+ { table: "workflow_park_decision", name: "idx_wfparkdec_run", columns: ["run_id"] },
24
26
  { table: "workflow_completion_inbox", name: "idx_wfinbox_session_seq", columns: ["session_id", "seq"] },
25
27
  { table: "workflow_inbox_fence", name: "idx_wffence_until", columns: ["until_ms"] },
26
28
  { table: "workflow_notify_journal", name: "idx_wfnotify_acked", columns: ["acked"] },
@@ -1,6 +1,6 @@
1
1
  import { createHmac } from "node:crypto";
2
2
  import { uuidv7 } from "@sema-agent/core";
3
- import { CONTROL_AND_BIDI_STRIP_RE } from "../text-bidi.js";
3
+ import { stripControlAndBidi } from "@sema-agent/settings-schema";
4
4
  import { presignS3CopyUrl, presignS3Url } from "./s3-presign.js";
5
5
  import { OBJECT_STORE_IO_TIMEOUT_MS } from "./blob-backend.js";
6
6
  export const PRESIGN_MAX_TTL_SEC = 604_800;
@@ -11,7 +11,7 @@ export function scopeSegment(secretKey, scope) {
11
11
  }
12
12
  export function sanitizeSendFileName(name) {
13
13
  const base = (name ?? "").split(/[/\\]/).pop() ?? "";
14
- let clean = base.replace(CONTROL_AND_BIDI_STRIP_RE, "").replace(/^\.+/, "").trim();
14
+ let clean = stripControlAndBidi(base).replace(/^\.+/, "").trim();
15
15
  if (clean.length > 128) {
16
16
  const dot = clean.lastIndexOf(".");
17
17
  const ext = dot > 0 && clean.length - dot <= 16 ? clean.slice(dot) : "";
@@ -1,13 +1,39 @@
1
- /** One row of the scan: its `seq` and the `payload` column projected as text (`jsonColumnText`). */
1
+ /**
2
+ * The prompt-epoch anchor below a bounded window's floor — `SessionStorage.getEpochAnchor()` for every session storage that
3
+ * loads a window instead of the whole tree: the two SQL session backends (`tidb-session-storage.ts` / `pg-session-storage.ts`,
4
+ * the run's wake and the session view alike) and the local leg's session view (`local-session-window.ts`). One reading, three
5
+ * dialects: each storage hands in only its own page and reads its own rows.
6
+ *
7
+ * ## The rule: the store carries the record, the engine reads it
8
+ * A bounded wake loads `[floor .. leaf]`; when the loaded branch holds no epoch carrier (a rewind cut inside the kept
9
+ * tail, or compactions written before the engine restated the epoch), the engine's `Session.getPromptEpoch()` asks the
10
+ * storage for the nearest carrier BELOW the floor and reads it itself. The storage returns that record **as stored**:
11
+ * · a `prompt_epoch` entry ⇒ the whole entry (the engine's branch walk reads the whole entry too);
12
+ * · a compaction whose `details.promptEpoch` is present ⇒ that restatement;
13
+ * · a compaction without a restatement carries nothing ⇒ the scan pages past it, to exhaustion (a session that lived
14
+ * across the release that introduced epochs can hold any number of them between the floor and its pin).
15
+ * The storage never judges the record's shape. The engine answers a current-shape record as a pin and a retired-shape
16
+ * record by name (`legacy`, with whether it pinned a center artifact) — a storage that normalized the record first would
17
+ * turn a session pinned under a retired shape into one with no pin, and the engine would then skip the center source's
18
+ * handling of that pin. A record that is neither is the engine's to read as well (it reads as no record).
19
+ *
20
+ * `floorSeq <= 0` (a full load: the walk already reaches the root) or no carrier below the floor ⇒ `undefined`
21
+ * (a session that predates epochs).
22
+ *
23
+ * What "below the floor, newest first" walks is the storage's: the SQL twins page their `session_event` rows by `seq` (their
24
+ * window is cut by `seq` too); the local window pages the branch path it was cut from.
25
+ */
26
+ import type { SessionTreeEntry } from "@sema-agent/core";
27
+ /** One row of a scan: its position (`seq`, the keyset cursor). What else the row holds is the storage's — it reads the entry
28
+ * out of the row itself ({@link scanEpochAnchor}'s `entryOf`), so a row is read only when the scan reaches it. */
2
29
  export interface EpochAnchorRow {
3
30
  readonly seq: string | number;
4
- readonly payload: unknown;
5
31
  }
6
32
  /**
7
- * Page the rows `seq < beforeSeq` whose type is `prompt_epoch` or `compaction`, newest first (the backend's own SQL;
8
- * an empty page = no older row).
33
+ * Page the rows `seq < beforeSeq` that may carry an epoch record, newest first (an empty page = no older row). A page may hold
34
+ * other rows too — they carry nothing and the scan passes them.
9
35
  */
10
- export type EpochAnchorPage = (beforeSeq: number) => Promise<readonly EpochAnchorRow[]>;
36
+ export type EpochAnchorPage<R extends EpochAnchorRow> = (beforeSeq: number) => Promise<readonly R[]>;
11
37
  /** The nearest epoch carrier below `floorSeq`, as stored; see the file header. */
12
- export declare function scanEpochAnchor(sessionId: string, floorSeq: number, page: EpochAnchorPage): Promise<unknown>;
38
+ export declare function scanEpochAnchor<R extends EpochAnchorRow>(floorSeq: number, page: EpochAnchorPage<R>, entryOf: (row: R) => SessionTreeEntry): Promise<unknown>;
13
39
  //# sourceMappingURL=session-epoch-anchor.d.ts.map
@@ -1,5 +1,4 @@
1
- import { decodeSessionEventPayload } from "./session-event-payload.js";
2
- export async function scanEpochAnchor(sessionId, floorSeq, page) {
1
+ export async function scanEpochAnchor(floorSeq, page, entryOf) {
3
2
  if (floorSeq <= 0)
4
3
  return undefined;
5
4
  let cursor = floorSeq;
@@ -8,7 +7,7 @@ export async function scanEpochAnchor(sessionId, floorSeq, page) {
8
7
  if (rows.length === 0)
9
8
  return undefined;
10
9
  for (const row of rows) {
11
- const entry = decodeSessionEventPayload(row.payload, sessionId, row.seq);
10
+ const entry = entryOf(row);
12
11
  if (entry.type === "prompt_epoch")
13
12
  return entry;
14
13
  if (entry.type === "compaction") {
@@ -0,0 +1,11 @@
1
+ /**
2
+ * fork 拷贝的属主 —— core `SessionStore.fork` 契约的唯一读法:
3
+ * 显式串 ⇒ 该串;无参 / 显式 `null` ⇒ 源会话记下的属主(`undefined` = 源会话没记,拷贝也不记)。
4
+ */
5
+ export declare function forkOwnerOf(requested: string | null | undefined, sourceOwner: string | null | undefined): string | null | undefined;
6
+ /**
7
+ * `register` 首写的 owner 守卫 —— core `SessionStore.register` 契约的唯一读法(三只店同调):首写的 owner 既不是串也不是 `null` ⇒ 响亮拒(不留行);
8
+ * 「首写」由调用方判(已记下属主的会话上的调用照旧静默不改,不经这里)。
9
+ */
10
+ export declare function assertFirstOwnerWritable(sessionId: string, owner: unknown): asserts owner is string | null;
11
+ //# sourceMappingURL=session-ownership.d.ts.map
@@ -0,0 +1,11 @@
1
+ import { SessionError } from "@sema-agent/core";
2
+ export function forkOwnerOf(requested, sourceOwner) {
3
+ return typeof requested === "string" ? requested : sourceOwner;
4
+ }
5
+ export function assertFirstOwnerWritable(sessionId, owner) {
6
+ if (typeof owner === "string" || owner === null)
7
+ return;
8
+ throw new SessionError("storage", `register(${sessionId}): a first ownership write must carry a principal (string) or null (anonymous), got ${owner === undefined ? "undefined" : typeof owner} — ` +
9
+ "a stored value that ownerOf reads back as 'no row' would leave the session claimable forever and ownable never; refusing, nothing written");
10
+ }
11
+ //# sourceMappingURL=session-ownership.js.map
@@ -33,7 +33,7 @@
33
33
  * 宿主可枚举、release 不真删的普通会话 ——「声明了 full tier 却在静默说谎」。⇒ 在**声明了分区**的
34
34
  * 店上,带 placement 的 acquire 必须回读到**已持久化**且同一元组身份的行,回读缺失/异组=响亮拒。
35
35
  */
36
- import { type PlacedSessionRow, type SessionPlacement, type SessionPlacementRecord } from "@sema-agent/core";
36
+ import { type PlacedSessionRow, type SessionPlacement, type SessionPlacementRecord, type SessionStore } from "@sema-agent/core";
37
37
  /**
38
38
  * 两支 SQL 孪生的 placement **声明**(core `SessionStore.placements`)。
39
39
  *
@@ -88,20 +88,18 @@ export interface PlacementAcquireOpts {
88
88
  placement?: SessionPlacement;
89
89
  }
90
90
  /**
91
- * 准入判据 —— **两个方向**(顶注 ①②;core `TtlSessionStore.assertPlacementAdmission` 的 SQL 镜像,
92
- * 逐条同义,文案自铸)。
93
- *
94
- * @param persisted 该 id 在库里**已持久化**的元组(缺席 = 普通会话 / 无此行)。
95
- *
96
- * · 方向①(claim 关门):`persisted` 在场 + 非 `requireExisting` + 调用方没带创建 placement ⇒ 拒。
97
- * `requireExisting`(读/复活形,义务 5)与「创建调用及其幂等重取」(带 placement 的可信内部链)
98
- * 是仅有的两个合法形。placement 不可变意味着带 placement 的参数**永远重写不了**任何东西——持久化
99
- * 的那一份恒赢,所以放行它不开任何口子。
100
- * · 方向②(镜像/squat):带 placement 的创建形回读到**缺席**或**异组**元组 ⇒ 拒。缺席 = 并发的
101
- * claim 形抢先建出了普通会话(或带外写把元组抹了);异组 = 同 id 上坐着另一条委派链的转录。
102
- * 两者都不许让「声明了 durable 分区」这句话在静默中变成谎。
91
+ * 准入判据 —— 只有引擎那一只:core 包根导出 `admitSessionPlacement(sessionId, persisted, request, capability)`(core 7.54.0 #1468 K2;
92
+ * `TtlSessionStore` 自己调的同一只函数,两个方向:claim 关门 / 镜像·squat,`placement_refused`)。`capability.subagentPlacement` 读的是**店自己的声明**
93
+ * (`placements.subagent` 在场 —— 与 core `TtlSessionStore` 同一只读法,{@link placementCapabilityOf}),不是另写一个「这只店声明了」的常量:声明只有一处。
94
+ * 修前本模块持一份 SQL 镜像(`assertPlacementAdmission`,逐条同义、文案自铸)—— 与引擎是两份判据;随 7.54.0 退役、零别名。
95
+ * 拒句随之换成引擎的句子(缺元组那一形的结尾词:「or the repo dropped the tuple」,此前本仓写「written away out of band」)——
96
+ * 按原句匹配告警的运维规则要跟着改(MIGRATION 7.134.0 节记了这一句)。
103
97
  */
104
- export declare function assertPlacementAdmission(sessionId: string, persisted: SessionPlacementRecord | undefined, opts?: PlacementAcquireOpts): void;
98
+ export declare function placementCapabilityOf(store: {
99
+ readonly placements?: SessionStore["placements"];
100
+ }): {
101
+ readonly subagentPlacement: boolean;
102
+ };
105
103
  /**
106
104
  * 一次 `listPlaced` 最多带回的行数。
107
105
  *
@@ -45,21 +45,8 @@ export function placementInsertValues(placement, placedAt) {
45
45
  placedAt,
46
46
  ];
47
47
  }
48
- export function assertPlacementAdmission(sessionId, persisted, opts) {
49
- if (opts?.requireExisting === true)
50
- return;
51
- if (persisted !== undefined && opts?.placement === undefined) {
52
- throw new SessionError("placement_refused", `session ${sessionId} belongs to the ${persisted.kind} placement partition — a claim-form acquire cannot attach to it (read it with requireExisting, or address the agent through its a* handle)`);
53
- }
54
- if (opts?.placement === undefined)
55
- return;
56
- const want = opts.placement;
57
- if (persisted === undefined) {
58
- throw new SessionError("placement_refused", `session ${sessionId} exists OUTSIDE the ${want.kind} placement partition it was declared into — refusing to run a subagent transcript on it (a concurrent claim-form create won this id, or the tuple was written away out of band)`);
59
- }
60
- if (persisted.kind !== want.kind || persisted.scope !== want.scope || persisted.handle !== want.handle) {
61
- throw new SessionError("placement_refused", `session ${sessionId}'s persisted placement does not match the declared one (persisted ${persisted.kind}/${persisted.scope ?? "-"}/${persisted.handle ?? "-"} vs declared ${want.kind}/${want.scope ?? "-"}/${want.handle ?? "-"}) — refusing a cross-tuple attach`);
62
- }
48
+ export function placementCapabilityOf(store) {
49
+ return { subagentPlacement: store.placements?.subagent !== undefined };
63
50
  }
64
51
  export const LIST_PLACED_BATCH = 1000;
65
52
  //# sourceMappingURL=session-placement.js.map
@@ -1,6 +1,8 @@
1
1
  import { type SessionStore } from "@sema-agent/core";
2
2
  import type { ServiceConfig } from "../config-types.js";
3
3
  import type { StoreBackend } from "./store-backend.js";
4
+ import { type DelegationRootWitness, type DelegationTree, type OrphanOwnerLog } from "../delegated-session-owner.js";
5
+ import type { OwnerAwareSessionStore } from "../security.js";
4
6
  /**
5
7
  * Session-store plugin seam.
6
8
  *
@@ -43,15 +45,23 @@ export declare function ensureSessionDurable(hostStore: SessionStore, sessionId:
43
45
  * [ref] 主动晋升版 ensureChildSessionDurable(cli TiDB 真机撞获:1.263 的只查不迁形在拆店生产形下
44
46
  * 恒否决 park——bg 子会话住 subRunner 私有 TTL 店、从不在 host 店,core 收 veto 即 expire checkpoint
45
47
  * (14ms~2s)+ settle failed,赎回窗口不存在):
46
- * 1. 快路:已在 host 店且有真内容 → 直接通过(幂等/重入)。
48
+ * 0. 属主(S-1770,`delegated-session-owner.ts`):按委派继承律判一次(唯一判据 `judgeDelegatedOwner`)—— 来源集 = 见证的根这一只(调用方交出的委派树根 + 调用方判断那一刻
49
+ * 读到的根行住客:请求腿 = 本请求经鉴权解析的会话与受理时那一读,`/decide` 赎回腿 = 行上的 `rootSessionId` 与 claim 前后两读的那一位;core 的挂点签名只给子代 id,
50
+ * 场景层把见证绑进闭包交下来)。此刻根行在而住客不是见证的那一位(删了、同 id 被重新登记)⇒ **拒**(`assertRootStillWitnessed`:不按新住客晋升 / 认领);
51
+ * 缺席(行上没有根)或根行此刻不在 ⇒ 孤行哨兵。
52
+ * 1. 快路:已在 host 店且有真内容 → 通过,**前提是那一行的属主按律定得下来**(`settleDelegatedOwner`:记着别的委派树 ⇒ 抛;记着这一棵 ⇒ 按它记录的根、父再判一次
53
+ * (与 M23 同读数),无主 ⇒ 认领(CAS 子行,写判断时的读数)、没落下 ⇒ 抛(只有记录委派树的 SQL 店;local 店的行不记委派树 ⇒ 不认领);此刻属主不是律给的那一只 ⇒ 那是别人的同 id 会话,
54
+ * 不是这只子代的转录 ⇒ 抛 = 停驻否决;与晋升建行前那一道同一只函数)。
47
55
  * 2. 晋升:routing.promoteToHost 把子会话整树从私店拷进 host 店(拷贝→私店 forget→路由记 host,
48
- * 三步见 ForkRoutingSessionStore.promoteToHost)。
49
- * 3. 复验:同一把 `ensureSessionDurable` 严检(leaf 非空)——晋升产物必须达到与既有校验完全相同的
50
- * durable 标准,任何一步失败=如实 reject=veto=pre-153 行为(不放大)。
56
+ * 三步见 ForkRoutingSessionStore.promoteToHost),宿主行在建行那一笔里带第 0 步的属主;写的是哨兵 ⇒ 写落下之后一行 warn。
57
+ * 3. 复验:同一把 `ensureSessionDurable` 严检(leaf 非空)+ 同一只属主定夺 —— 晋升产物必须达到与快路完全相同的
58
+ * 标准,任何一步失败=如实 reject=veto=pre-153 行为(不放大)。
59
+ * 宿主店必须读得出属主(`delegatedOwnerHostOf`:`ownerOf` + `sessionFaceOf`;记录委派树的店另交 `placementOf` + `claimOwnerless`,两只同在或同缺;生产的 SQL / local 店与缓存包装层都满足),
60
+ * 否则响亮拒。
51
61
  * 调用方纪律与 {@link ensureSessionDurable} 同款:hostStore 必须是 release 非破坏性的真 durable 店
52
- * (main.ts 的 `config.sessionBackend === "tidb"` 门不变)。
62
+ * (stage-07 的 `config.sessionBackend === "tidb"` 门不变 —— 该词在 local 后端上同样归一成 `tidb`,所以本腿在 local 宿主店上也跑)。
53
63
  */
54
- export declare function ensureChildSessionDurableWithPromotion(hostStore: SessionStore, routing: {
55
- promoteToHost(sessionId: string): Promise<void>;
56
- }, sessionId: string): Promise<void>;
64
+ export declare function ensureChildSessionDurableWithPromotion(hostStore: OwnerAwareSessionStore, routing: {
65
+ promoteToHost(sessionId: string, owner: string | null, tree: DelegationTree): Promise<void>;
66
+ }, sessionId: string, root: DelegationRootWitness | undefined, log: OrphanOwnerLog): Promise<void>;
57
67
  //# sourceMappingURL=session-store.d.ts.map
@@ -1,5 +1,6 @@
1
1
  import { TtlSessionStore } from "@sema-agent/core";
2
2
  import { CachingSessionStore } from "./caching-session-store.js";
3
+ import { assertRootStillWitnessed, delegatedOwnerHostOf, readDelegatedOwner, reportWrittenOwner, settleDelegatedOwner, sourceFacesVia, } from "../delegated-session-owner.js";
3
4
  export function createSessionStore(config, backend, metrics) {
4
5
  if (config.sessionBackend === "tidb") {
5
6
  if (!backend)
@@ -22,14 +23,24 @@ export async function ensureSessionDurable(hostStore, sessionId) {
22
23
  await hostStore.forget?.(acquired.sessionId);
23
24
  }
24
25
  }
25
- export async function ensureChildSessionDurableWithPromotion(hostStore, routing, sessionId) {
26
+ export async function ensureChildSessionDurableWithPromotion(hostStore, routing, sessionId, root, log) {
27
+ const host = delegatedOwnerHostOf(hostStore);
28
+ if (host === undefined) {
29
+ throw new Error(`session "${sessionId}" cannot be parked: the host session store cannot read a session's owner (ownerOf / sessionFaceOf), or records delegation trees without being able to claim under them (placementOf / claimOwnerless), so the delegated transcript's owner cannot be derived or checked`);
30
+ }
31
+ const judged = await readDelegatedOwner(sessionId, root === undefined ? {} : { rootSessionId: root.sessionId }, sourceFacesVia(host.sessionFaceOf));
32
+ assertRootStillWitnessed(sessionId, root, judged);
33
+ let promoted = false;
26
34
  try {
27
35
  await ensureSessionDurable(hostStore, sessionId);
28
- return;
29
36
  }
30
37
  catch {
38
+ await routing.promoteToHost(sessionId, judged.inherited.owner, judged.tree);
39
+ await ensureSessionDurable(hostStore, sessionId);
40
+ promoted = true;
31
41
  }
32
- await routing.promoteToHost(sessionId);
33
- await ensureSessionDurable(hostStore, sessionId);
42
+ const settled = await settleDelegatedOwner(host, sessionId, judged.inherited.owner, judged.tree);
43
+ if (promoted || settled.claimed)
44
+ reportWrittenOwner(log, "promotion", sessionId, settled.judged ?? judged);
34
45
  }
35
46
  //# sourceMappingURL=session-store.js.map