@sema-agent/server 7.149.0 → 7.151.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 (441) hide show
  1. package/MIGRATION.md +307 -9
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/USAGE.md +92 -40
  5. package/deploy/sema-up/README.md +7 -1
  6. package/deploy/sema-up/chart/Chart.yaml +1 -1
  7. package/deploy/sema-up/chart/templates/NOTES.txt +5 -0
  8. package/deploy/sema-up/chart/templates/_helpers.tpl +15 -16
  9. package/deploy/sema-up/chart/templates/server-v4pro.yaml +5 -3
  10. package/deploy/sema-up/chart/templates/server.yaml +5 -3
  11. package/deploy/sema-up/chart/values-full-single.yaml +3 -1
  12. package/deploy/sema-up/chart/values.yaml +8 -1
  13. package/deploy/sema-up/cluster-up.sh +1 -0
  14. package/deploy/sema-up/kube-up.sh +1 -1
  15. package/deploy/sema-up/lib/flagshape.sh +1 -1
  16. package/deploy/sema-up/lib/helm-values-cluster.py +2 -0
  17. package/deploy/sema-up/lib/helm-values-kube.py +2 -0
  18. package/dist/adoption/import.js +3 -3
  19. package/dist/approval-ask-machine.d.ts +1 -1
  20. package/dist/approval-card.d.ts +3 -3
  21. package/dist/approval-gate-currency.d.ts +2 -2
  22. package/dist/audit.d.ts +1 -1
  23. package/dist/audit.js +2 -2
  24. package/dist/auth-bridge.d.ts +20 -0
  25. package/dist/auth-bridge.js +9 -0
  26. package/dist/background-session-reap.d.ts +2 -2
  27. package/dist/bake-runner/main.d.ts +4 -2
  28. package/dist/bind-host.d.ts +2 -3
  29. package/dist/bind-host.js +2 -4
  30. package/dist/boot/budget-tracing.js +1 -1
  31. package/dist/boot/deferred-sandbox-path-env.d.ts +5 -3
  32. package/dist/boot/deferred-sandbox-path-env.js +1 -2
  33. package/dist/boot/engine-lease.d.ts +5 -0
  34. package/dist/boot/engine-lease.js +1 -0
  35. package/dist/boot/execution-env.d.ts +8 -14
  36. package/dist/boot/execution-env.js +54 -17
  37. package/dist/boot/execution-lane.js +32 -5
  38. package/dist/boot/late-binds.d.ts +8 -4
  39. package/dist/boot/late-binds.js +7 -1
  40. package/dist/boot/memory-consolidation.d.ts +3 -2
  41. package/dist/boot/memory-consolidation.js +6 -6
  42. package/dist/boot/parked-revive-gate.d.ts +5 -5
  43. package/dist/boot/parked-revive-gate.js +2 -3
  44. package/dist/boot/reapers.d.ts +5 -0
  45. package/dist/boot/reapers.js +12 -1
  46. package/dist/boot/resolve-spec.d.ts +7 -8
  47. package/dist/boot/resolve-spec.js +80 -35
  48. package/dist/boot/retention-lane.d.ts +1 -1
  49. package/dist/boot/runner-deps.d.ts +9 -2
  50. package/dist/boot/runner-deps.js +3 -3
  51. package/dist/boot/session-age-out.d.ts +36 -0
  52. package/dist/boot/session-age-out.js +101 -0
  53. package/dist/boot/session-faces.d.ts +5 -14
  54. package/dist/boot/session-faces.js +28 -24
  55. package/dist/boot/stage-01-config.js +2 -0
  56. package/dist/boot/stage-03-store-backend.js +2 -2
  57. package/dist/boot/stage-05-execution-env.d.ts +5 -3
  58. package/dist/boot/stage-05-execution-env.js +2 -2
  59. package/dist/boot/stage-06-runners.d.ts +7 -4
  60. package/dist/boot/stage-06-runners.js +16 -8
  61. package/dist/boot/stage-07-capability-layer.d.ts +7 -4
  62. package/dist/boot/stage-07-capability-layer.js +3 -2
  63. package/dist/boot/stage-08-reapers.d.ts +7 -4
  64. package/dist/boot/stage-08-reapers.js +4 -2
  65. package/dist/boot/stage-09-leader.d.ts +11 -5
  66. package/dist/boot/stage-09-leader.js +12 -3
  67. package/dist/boot/stage-10-http-server.d.ts +44 -6
  68. package/dist/boot/stage-10-http-server.js +12 -10
  69. package/dist/boot/web-admin.d.ts +1 -1
  70. package/dist/boot/web-admin.js +3 -1
  71. package/dist/boot/workflow-orchestration.d.ts +7 -2
  72. package/dist/boot/workflow-orchestration.js +24 -7
  73. package/dist/bounded-rotation.d.ts +29 -0
  74. package/dist/bounded-rotation.js +24 -0
  75. package/dist/capability-facts.d.ts +84 -66
  76. package/dist/capability-facts.js +58 -41
  77. package/dist/center-credential.d.ts +25 -6
  78. package/dist/center-credential.js +25 -5
  79. package/dist/config-catalog.d.ts +26 -7
  80. package/dist/config-catalog.js +15 -12
  81. package/dist/config-invariants.d.ts +2 -1
  82. package/dist/config-invariants.js +4 -4
  83. package/dist/config-types.d.ts +22 -15
  84. package/dist/config.d.ts +51 -2
  85. package/dist/config.js +35 -24
  86. package/dist/deployment-governance.d.ts +7 -28
  87. package/dist/deployment-governance.js +1 -14
  88. package/dist/deployment-shape.d.ts +9 -1
  89. package/dist/deployment-shape.js +2 -1
  90. package/dist/device-protocol.d.ts +5 -4
  91. package/dist/device-protocol.js +1 -1
  92. package/dist/device-ws-hub.d.ts +1 -0
  93. package/dist/device-ws-hub.js +66 -29
  94. package/dist/device-ws-protocol.d.ts +79 -12
  95. package/dist/device-ws-protocol.js +64 -26
  96. package/dist/engine-identity.d.ts +23 -5
  97. package/dist/engine-identity.js +13 -0
  98. package/dist/entry-mode-clamp-notice.d.ts +2 -1
  99. package/dist/execution-env-refusal.d.ts +23 -0
  100. package/dist/execution-env-refusal.js +5 -0
  101. package/dist/execution-lane-caps.d.ts +3 -1
  102. package/dist/field-conflict.d.ts +3 -3
  103. package/dist/file-history-capture.d.ts +11 -4
  104. package/dist/file-history-capture.js +9 -6
  105. package/dist/file-history-disclosure.d.ts +7 -5
  106. package/dist/file-history-disclosure.js +7 -5
  107. package/dist/fleet/drain-entry.d.ts +4 -0
  108. package/dist/fleet/engine-signal.d.ts +14 -1
  109. package/dist/fleet/engine-signal.js +5 -1
  110. package/dist/fleet/fleet-reconciler.d.ts +1 -1
  111. package/dist/fleet/serving-lease.d.ts +18 -3
  112. package/dist/fleet/serving-lease.js +81 -35
  113. package/dist/fleet-client.d.ts +13 -11
  114. package/dist/fleet-client.js +53 -33
  115. package/dist/history-anchor.js +1 -1
  116. package/dist/hitl-delivery.d.ts +1 -3
  117. package/dist/hitl-delivery.js +1 -1
  118. package/dist/hooks/hook-runner.js +2 -2
  119. package/dist/host-lsp-manager.d.ts +2 -2
  120. package/dist/http/active-run-conflict.d.ts +3 -7
  121. package/dist/http/active-run-conflict.js +2 -2
  122. package/dist/http/admission.js +19 -4
  123. package/dist/http/approval-sweeps.js +7 -6
  124. package/dist/http/caller-trust.d.ts +2 -2
  125. package/dist/http/caller-trust.js +3 -3
  126. package/dist/http/checkpoint-ctx.d.ts +20 -63
  127. package/dist/http/checkpoint-ctx.js +2 -24
  128. package/dist/http/claim-heartbeat.d.ts +4 -4
  129. package/dist/http/claim-heartbeat.js +3 -3
  130. package/dist/http/dispatch.d.ts +2 -1
  131. package/dist/http/dispatch.js +36 -29
  132. package/dist/http/durable-run-leg.js +17 -17
  133. package/dist/http/health-self-report.d.ts +9 -1
  134. package/dist/http/health-self-report.js +2 -0
  135. package/dist/http/owner-access.d.ts +264 -20
  136. package/dist/http/owner-access.js +215 -5
  137. package/dist/http/readiness.d.ts +4 -2
  138. package/dist/http/resume-ctx-read.js +38 -1
  139. package/dist/http/resume-legs.d.ts +2 -1
  140. package/dist/http/resume-legs.js +273 -89
  141. package/dist/http/route-ctx.d.ts +41 -19
  142. package/dist/http/route-table.d.ts +1 -1
  143. package/dist/http/routes/a2a-serve.js +15 -14
  144. package/dist/http/routes/admin-memory-optout.d.ts +1 -1
  145. package/dist/http/routes/agents-roster.js +2 -3
  146. package/dist/http/routes/approvals-assistant.d.ts +3 -2
  147. package/dist/http/routes/approvals-assistant.js +12 -12
  148. package/dist/http/routes/background.d.ts +3 -3
  149. package/dist/http/routes/capabilities.js +10 -6
  150. package/dist/http/routes/config-catalog.d.ts +1 -1
  151. package/dist/http/routes/fleet.js +10 -8
  152. package/dist/http/routes/mcp-reconnect.js +2 -2
  153. package/dist/http/routes/memory-origin.d.ts +1 -1
  154. package/dist/http/routes/notify-wake.js +2 -3
  155. package/dist/http/routes/projects.d.ts +4 -2
  156. package/dist/http/routes/projects.js +7 -6
  157. package/dist/http/routes/retention-ops.d.ts +1 -1
  158. package/dist/http/routes/run-permission-mode.d.ts +1 -1
  159. package/dist/http/routes/run-permission-mode.js +2 -2
  160. package/dist/http/routes/runs.d.ts +1 -1
  161. package/dist/http/routes/runs.js +125 -66
  162. package/dist/http/routes/session-memory-sync.js +4 -5
  163. package/dist/http/routes/session-sync.d.ts +4 -3
  164. package/dist/http/routes/session-sync.js +174 -83
  165. package/dist/http/routes/sessions-list.js +13 -7
  166. package/dist/http/routes/sessions.d.ts +1 -1
  167. package/dist/http/routes/sessions.js +26 -48
  168. package/dist/http/routes/tasks.js +38 -23
  169. package/dist/http/routes/trace-usage.js +14 -14
  170. package/dist/http/routes/workflows.d.ts +1 -0
  171. package/dist/http/routes/workflows.js +20 -38
  172. package/dist/http/server.d.ts +28 -30
  173. package/dist/http/server.js +7 -3
  174. package/dist/http/stored-body-replay.d.ts +3 -24
  175. package/dist/http/stored-body-replay.js +2 -12
  176. package/dist/http/submit-ctx.d.ts +1 -2
  177. package/dist/http/submit-ctx.js +8 -6
  178. package/dist/http/submit-replay.d.ts +15 -2
  179. package/dist/http/submit-replay.js +10 -4
  180. package/dist/http/wire-types.d.ts +19 -6
  181. package/dist/identity-source.d.ts +20 -3
  182. package/dist/identity-source.js +14 -0
  183. package/dist/index.d.ts +4 -0
  184. package/dist/lane-home-probe.d.ts +3 -7
  185. package/dist/lane-home-probe.js +0 -1
  186. package/dist/leader/endpoint.js +9 -8
  187. package/dist/leader/leader.d.ts +2 -1
  188. package/dist/leg-facts.d.ts +19 -5
  189. package/dist/leg-facts.js +3 -1
  190. package/dist/mcp-authorization-face.d.ts +18 -14
  191. package/dist/mcp-authorization-face.js +3 -2
  192. package/dist/memory-scope.d.ts +5 -4
  193. package/dist/memory-scope.js +4 -4
  194. package/dist/memory-store-refusal.d.ts +1 -1
  195. package/dist/memory-sync-client.d.ts +30 -2
  196. package/dist/memory-sync-client.js +16 -9
  197. package/dist/observability/corrupt-read-seat.d.ts +1 -1
  198. package/dist/observability/err-text.d.ts +7 -0
  199. package/dist/observability/err-text.js +5 -0
  200. package/dist/observability/fail-open.d.ts +12 -8
  201. package/dist/observability/fail-open.js +12 -8
  202. package/dist/observability/logger.d.ts +7 -0
  203. package/dist/observability/logger.js +3 -1
  204. package/dist/observability/metrics.js +1 -0
  205. package/dist/observability/run-terminal.d.ts +1 -1
  206. package/dist/orchestration/workflow-notify-journal.d.ts +18 -1
  207. package/dist/orchestration/workflow-notify-journal.js +12 -1
  208. package/dist/orchestration/workflow-park-decision.d.ts +70 -18
  209. package/dist/orchestration/workflow-park-decision.js +49 -10
  210. package/dist/orchestration/workflow-park-holder.d.ts +65 -0
  211. package/dist/orchestration/workflow-park-holder.js +98 -0
  212. package/dist/orchestration/workflow-record-fence.d.ts +36 -0
  213. package/dist/orchestration/workflow-record-fence.js +42 -0
  214. package/dist/owner-faces.d.ts +28 -4
  215. package/dist/owner-faces.js +2 -0
  216. package/dist/parked-decide.d.ts +104 -53
  217. package/dist/parked-decide.js +153 -117
  218. package/dist/peer-directory.d.ts +2 -2
  219. package/dist/peer-inbound-face.d.ts +2 -6
  220. package/dist/peer-inbound-face.js +0 -3
  221. package/dist/peer-seat.d.ts +1 -1
  222. package/dist/peer-seat.js +52 -59
  223. package/dist/permission-mode-admission.d.ts +41 -5
  224. package/dist/permission-mode-admission.js +29 -3
  225. package/dist/plugins/adoption-log-sql.d.ts +3 -1
  226. package/dist/plugins/adoption-log-sql.js +7 -1
  227. package/dist/plugins/approval-ask-store-file.d.ts +10 -2
  228. package/dist/plugins/approval-ask-store-file.js +8 -0
  229. package/dist/plugins/approval-ask-store-memory.d.ts +6 -1
  230. package/dist/plugins/approval-ask-store-memory.js +28 -9
  231. package/dist/plugins/approval-ask-store-sql.d.ts +43 -2
  232. package/dist/plugins/approval-ask-store-sql.js +34 -5
  233. package/dist/plugins/approval-exemption-store.js +3 -0
  234. package/dist/plugins/approval-nonce-store.js +2 -0
  235. package/dist/plugins/breaker-state-sql.js +2 -0
  236. package/dist/plugins/caching-session-store.d.ts +9 -15
  237. package/dist/plugins/caching-session-store.js +6 -0
  238. package/dist/plugins/device-store-sql.js +6 -0
  239. package/dist/plugins/error-origin.d.ts +18 -0
  240. package/dist/plugins/error-origin.js +10 -0
  241. package/dist/plugins/exec-path-law.d.ts +34 -0
  242. package/dist/plugins/exec-path-law.js +13 -0
  243. package/dist/plugins/execution-placement-sql.js +6 -6
  244. package/dist/plugins/file-run-store.js +1 -1
  245. package/dist/plugins/image-bake-store-sql.js +6 -0
  246. package/dist/plugins/image-index-sql.js +4 -0
  247. package/dist/plugins/landed-attendants.d.ts +212 -0
  248. package/dist/plugins/landed-attendants.js +161 -0
  249. package/dist/plugins/leader-run-store-sql.js +4 -0
  250. package/dist/plugins/local-checkpoint-store.d.ts +4 -1
  251. package/dist/plugins/local-checkpoint-store.js +3 -0
  252. package/dist/plugins/local-session-live-reference.d.ts +40 -0
  253. package/dist/plugins/local-session-live-reference.js +39 -0
  254. package/dist/plugins/local-session-store.d.ts +71 -33
  255. package/dist/plugins/local-session-store.js +129 -27
  256. package/dist/plugins/memory-engine-pg.js +4 -0
  257. package/dist/plugins/memory-engine-sql-apply.js +5 -2
  258. package/dist/plugins/memory-engine-tidb.js +4 -0
  259. package/dist/plugins/memory-optout-grant-store-sql.js +4 -0
  260. package/dist/plugins/memory-run-store.js +1 -1
  261. package/dist/plugins/memory-sync-store-pg.js +5 -0
  262. package/dist/plugins/memory-sync-store-tidb.js +5 -0
  263. package/dist/plugins/one-time-migrations.d.ts +80 -61
  264. package/dist/plugins/one-time-migrations.js +241 -104
  265. package/dist/plugins/org-delivery-quota-store.js +4 -1
  266. package/dist/plugins/outcome-ledger-sql.js +2 -0
  267. package/dist/plugins/permission-rule-store-sql.js +11 -4
  268. package/dist/plugins/pg-cost-quota.js +4 -0
  269. package/dist/plugins/pg-pool.js +28 -1
  270. package/dist/plugins/pg-rate-limiter.js +2 -0
  271. package/dist/plugins/posix-shell-fs.d.ts +6 -1
  272. package/dist/plugins/posix-shell-fs.js +8 -3
  273. package/dist/plugins/remote-env-adb.d.ts +19 -15
  274. package/dist/plugins/remote-env-adb.js +44 -17
  275. package/dist/plugins/remote-env-connect-seat.d.ts +105 -0
  276. package/dist/plugins/remote-env-connect-seat.js +72 -0
  277. package/dist/plugins/remote-env-device.d.ts +22 -5
  278. package/dist/plugins/remote-env-device.js +114 -14
  279. package/dist/plugins/remote-env-e2b.d.ts +18 -7
  280. package/dist/plugins/remote-env-e2b.js +53 -40
  281. package/dist/plugins/remote-env-file-error.js +6 -2
  282. package/dist/plugins/remote-env-host.d.ts +18 -1
  283. package/dist/plugins/remote-env-host.js +48 -47
  284. package/dist/plugins/remote-env-k8s.d.ts +41 -39
  285. package/dist/plugins/remote-env-k8s.js +56 -32
  286. package/dist/plugins/remote-env-local-docker.d.ts +13 -8
  287. package/dist/plugins/remote-env-local-docker.js +37 -43
  288. package/dist/plugins/remote-env-ssh.d.ts +18 -12
  289. package/dist/plugins/remote-env-ssh.js +64 -62
  290. package/dist/plugins/remote-shell.d.ts +101 -2
  291. package/dist/plugins/remote-shell.js +221 -3
  292. package/dist/plugins/replica-registration.d.ts +35 -0
  293. package/dist/plugins/replica-registration.js +48 -0
  294. package/dist/plugins/resume-anchor-store-sql.d.ts +11 -2
  295. package/dist/plugins/resume-anchor-store-sql.js +12 -5
  296. package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
  297. package/dist/plugins/retention-lane-store-sql.js +4 -0
  298. package/dist/plugins/retention-sql.d.ts +20 -2
  299. package/dist/plugins/retention-sql.js +11 -0
  300. package/dist/plugins/run-store-sql.d.ts +22 -3
  301. package/dist/plugins/run-store-sql.js +33 -9
  302. package/dist/plugins/scenario-prefix-switch-m42.d.ts +6 -12
  303. package/dist/plugins/scenario-prefix-switch-m42.js +7 -5
  304. package/dist/plugins/schema-consistency.d.ts +9 -5
  305. package/dist/plugins/schema-consistency.js +48 -18
  306. package/dist/plugins/schema-drift-sentence.js +1 -1
  307. package/dist/plugins/send-file-ledger.js +3 -0
  308. package/dist/plugins/serving-requirement.d.ts +68 -0
  309. package/dist/plugins/serving-requirement.js +60 -0
  310. package/dist/plugins/session-birth.d.ts +6 -2
  311. package/dist/plugins/session-capture-record-store-sql.js +2 -0
  312. package/dist/plugins/session-ext-table.d.ts +14 -1
  313. package/dist/plugins/session-ext-table.js +5 -1
  314. package/dist/plugins/session-row-sql.d.ts +49 -2
  315. package/dist/plugins/session-row-sql.js +56 -13
  316. package/dist/plugins/session-store-sql.d.ts +34 -18
  317. package/dist/plugins/session-store-sql.js +98 -17
  318. package/dist/plugins/shared-memory-store-sql.d.ts +1 -0
  319. package/dist/plugins/shared-memory-store-sql.js +6 -2
  320. package/dist/plugins/sql-driver.d.ts +2 -1
  321. package/dist/plugins/sql-driver.js +18 -3
  322. package/dist/plugins/sql-errors.d.ts +11 -6
  323. package/dist/plugins/sql-errors.js +5 -6
  324. package/dist/plugins/stop-then-start-sentence.d.ts +48 -3
  325. package/dist/plugins/stop-then-start-sentence.js +48 -0
  326. package/dist/plugins/stop-then-start.d.ts +109 -0
  327. package/dist/plugins/stop-then-start.js +104 -0
  328. package/dist/plugins/store-backend.d.ts +40 -13
  329. package/dist/plugins/store-backend.js +169 -27
  330. package/dist/plugins/store-boundary.d.ts +57 -0
  331. package/dist/plugins/store-boundary.js +139 -0
  332. package/dist/plugins/store-contracts.d.ts +29 -2
  333. package/dist/plugins/store-contracts.js +3 -0
  334. package/dist/plugins/store-fault-sentence-m53.d.ts +7 -0
  335. package/dist/plugins/store-fault-sentence-m53.js +25 -0
  336. package/dist/plugins/store-fault.d.ts +26 -7
  337. package/dist/plugins/store-fault.js +18 -2
  338. package/dist/plugins/task-attachment-store.js +9 -0
  339. package/dist/plugins/task-list-store-sql.d.ts +2 -2
  340. package/dist/plugins/task-list-store-sql.js +10 -8
  341. package/dist/plugins/tidb-cost-quota.js +4 -0
  342. package/dist/plugins/tidb-pool.js +6 -1
  343. package/dist/plugins/tidb-rate-limiter.js +4 -0
  344. package/dist/plugins/user-input-ledger-sql.js +4 -0
  345. package/dist/plugins/workflow-run-store-sql.d.ts +28 -10
  346. package/dist/plugins/workflow-run-store-sql.js +64 -4
  347. package/dist/plugins/workspace-fill.d.ts +4 -7
  348. package/dist/plugins/workspace-fill.js +2 -1
  349. package/dist/plugins/write-behind-counter.js +5 -0
  350. package/dist/process-instance.d.ts +3 -0
  351. package/dist/process-instance.js +7 -0
  352. package/dist/project-context.d.ts +64 -0
  353. package/dist/project-context.js +66 -0
  354. package/dist/project-registry.d.ts +46 -0
  355. package/dist/project-registry.js +44 -0
  356. package/dist/question.d.ts +9 -21
  357. package/dist/question.js +28 -19
  358. package/dist/request-key-closure.js +1 -1
  359. package/dist/request-value-closure.d.ts +4 -0
  360. package/dist/request-value-closure.js +10 -0
  361. package/dist/run-acceptance.d.ts +3 -2
  362. package/dist/run-acceptance.js +4 -6
  363. package/dist/run-cancel-card.d.ts +1 -1
  364. package/dist/run-cancel-card.js +2 -1
  365. package/dist/run-local.js +1 -0
  366. package/dist/runs.d.ts +37 -8
  367. package/dist/runs.js +53 -12
  368. package/dist/runtime-governance.d.ts +5 -4
  369. package/dist/security.d.ts +31 -33
  370. package/dist/security.js +41 -7
  371. package/dist/server-secret-env.d.ts +12 -10
  372. package/dist/server-secret-env.js +24 -15
  373. package/dist/session-agent.d.ts +1 -10
  374. package/dist/session-agent.js +0 -6
  375. package/dist/session-bindings.d.ts +82 -0
  376. package/dist/session-bindings.js +18 -0
  377. package/dist/session-children-retire.d.ts +2 -2
  378. package/dist/session-children-retire.js +2 -6
  379. package/dist/session-deletion.d.ts +16 -1
  380. package/dist/session-error-wire.d.ts +3 -3
  381. package/dist/session-error-wire.js +3 -3
  382. package/dist/session-incarnation.d.ts +28 -4
  383. package/dist/session-incarnation.js +14 -2
  384. package/dist/session-kind.d.ts +57 -0
  385. package/dist/session-kind.js +22 -0
  386. package/dist/session-notify-door.d.ts +4 -4
  387. package/dist/session-notify-door.js +5 -6
  388. package/dist/session-row.d.ts +21 -1
  389. package/dist/session-row.js +7 -1
  390. package/dist/session-rules-seat.d.ts +4 -3
  391. package/dist/session-rules-seat.js +3 -11
  392. package/dist/session-sync-kernel.d.ts +24 -14
  393. package/dist/session-sync-kernel.js +3 -6
  394. package/dist/session-sync.d.ts +4 -98
  395. package/dist/session-sync.js +4 -74
  396. package/dist/session-titler.d.ts +10 -3
  397. package/dist/session-titler.js +19 -6
  398. package/dist/ssh-host-key.d.ts +5 -0
  399. package/dist/ssh-host-key.js +3 -0
  400. package/dist/stores-sql/checkpoint-store-sql.d.ts +21 -2
  401. package/dist/stores-sql/checkpoint-store-sql.js +106 -18
  402. package/dist/stores-sql/file-history-store-sql.d.ts +52 -2
  403. package/dist/stores-sql/file-history-store-sql.js +108 -70
  404. package/dist/stores-sql/pg-session-storage.d.ts +34 -7
  405. package/dist/stores-sql/pg-session-storage.js +37 -13
  406. package/dist/stores-sql/retention-store-sql.d.ts +2 -10
  407. package/dist/stores-sql/retention-store-sql.js +2 -10
  408. package/dist/stores-sql/session-policy-store-sql.d.ts +30 -7
  409. package/dist/stores-sql/session-policy-store-sql.js +61 -44
  410. package/dist/stores-sql/session-write-hooks.d.ts +11 -0
  411. package/dist/stores-sql/tidb-session-store.d.ts +58 -7
  412. package/dist/stores-sql/tidb-session-store.js +37 -13
  413. package/dist/submission-exit.d.ts +3 -2
  414. package/dist/submission-exit.js +9 -1
  415. package/dist/task-cwd.d.ts +6 -5
  416. package/dist/task-settings.d.ts +19 -5
  417. package/dist/task-settings.js +13 -7
  418. package/dist/terminal.d.ts +33 -2
  419. package/dist/terminal.js +139 -9
  420. package/dist/tool-approval.d.ts +147 -12
  421. package/dist/tool-approval.js +206 -109
  422. package/dist/trace/engine-notice-wire.d.ts +14 -0
  423. package/dist/trace/engine-notice-wire.js +18 -1
  424. package/dist/trace/project.d.ts +4 -3
  425. package/dist/trace/project.js +3 -2
  426. package/dist/trace/projection-table.d.ts +33 -2
  427. package/dist/trace/self-minted-wire.d.ts +422 -26
  428. package/dist/trace/self-minted-wire.js +149 -23
  429. package/dist/trace/tool-card.d.ts +3 -0
  430. package/dist/trace/tool-card.js +24 -0
  431. package/dist/trace/tool-output-media.d.ts +2 -1
  432. package/dist/unattended-permission-mode.d.ts +1 -1
  433. package/dist/web-admin-client/apply-effective.d.ts +1 -1
  434. package/dist/web-admin-client/effective-keys.d.ts +1 -1
  435. package/dist/web-admin-client/skills-mcp.d.ts +1 -1
  436. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +120 -41
  437. package/package.json +3 -3
  438. package/wire-changes/server@7.150.0.json +335 -0
  439. package/wire-changes/server@7.151.0.json +762 -0
  440. package/dist/http/session-inherit-env.d.ts +0 -36
  441. package/dist/http/session-inherit-env.js +0 -6
@@ -0,0 +1,72 @@
1
+ import { RemoteExecutionError, isRetryableRemoteErrorCode } from "@sema-agent/core";
2
+ export const CONNECT_REFUSAL_CODE = {
3
+ home_unconfirmed: "env.home_unconfirmed",
4
+ snapshot_bucket_missing: "env.snapshot_bucket_missing",
5
+ occupant_missing: "env.occupant_missing",
6
+ pod_admission_refused: "env.sandbox_refused",
7
+ credential_broker_refused: "env.sandbox_refused",
8
+ archive_unrecoverable: "env.sandbox_refused",
9
+ workspace_fill_refused: "env.sandbox_refused",
10
+ host_key_rejected: "env.host_key_rejected",
11
+ pod_quota_exceeded: "connect_failed",
12
+ occupant_unreadable: "connect_failed",
13
+ target_settling: "connect_failed",
14
+ };
15
+ const ENGINE_FIELD_STILL_CLOSED = true;
16
+ void ENGINE_FIELD_STILL_CLOSED;
17
+ function mintRefusalError(code, message, cause) {
18
+ return new RemoteExecutionError(code, message, cause);
19
+ }
20
+ function standsFor(code) {
21
+ return !isRetryableRemoteErrorCode(code);
22
+ }
23
+ export function connectRefusal(fact, message, cause) {
24
+ const code = CONNECT_REFUSAL_CODE[fact];
25
+ const error = mintRefusalError(code, message, cause);
26
+ return standsFor(code) ? { ok: false, error, standing: true } : { ok: false, error, standing: false };
27
+ }
28
+ export function withdrawOnAbort(walk, signal) {
29
+ if (signal === undefined)
30
+ return walk;
31
+ if (signal.aborted)
32
+ return Promise.resolve({ ok: false, error: new RemoteExecutionError("aborted", "connect aborted") });
33
+ return new Promise((resolve, reject) => {
34
+ const onAbort = () => resolve({ ok: false, error: new RemoteExecutionError("aborted", "connect aborted while waiting") });
35
+ signal.addEventListener("abort", onAbort, { once: true });
36
+ walk.then((r) => {
37
+ signal.removeEventListener("abort", onAbort);
38
+ resolve(r);
39
+ }, (e) => {
40
+ signal.removeEventListener("abort", onAbort);
41
+ reject(e);
42
+ });
43
+ });
44
+ }
45
+ export function createConnectSeat(lane) {
46
+ let held;
47
+ return {
48
+ get held() {
49
+ return held;
50
+ },
51
+ answer(refusal) {
52
+ if (refusal.standing && held === undefined)
53
+ held = refusal;
54
+ return refusal;
55
+ },
56
+ connect(config) {
57
+ if (held !== undefined)
58
+ return Promise.resolve(held);
59
+ const signal = config?.abortSignal;
60
+ const walking = lane.inFlight();
61
+ if (walking !== undefined)
62
+ return withdrawOnAbort(walking, signal);
63
+ const handle = lane.live();
64
+ if (handle !== undefined)
65
+ return Promise.resolve({ ok: true, value: handle });
66
+ if (signal?.aborted)
67
+ return Promise.resolve({ ok: false, error: new RemoteExecutionError("aborted", "connect aborted") });
68
+ return lane.establish(config);
69
+ },
70
+ };
71
+ }
72
+ //# sourceMappingURL=remote-env-connect-seat.js.map
@@ -1,4 +1,4 @@
1
- import { ExecutionError, FileError, RemoteExecutionError, type ExecutionEnv, type ExecutionEnvFactory, type ExecStreamOptions, type FileInfo, type OutputChunk, type RemoteConnectConfig, type RemoteExecutionEnv, type Result, type SessionToken, type SnapshotId, type VmLifecycleOptions, type WorkspaceHandle, type WriteExpectation, type WriteReceipt } from "@sema-agent/core";
1
+ import { ExecutionError, FileError, RemoteExecutionError, type ExecutionEnv, type ExecutionEnvFactory, type ExecStreamOptions, type FileInfo, type OutputChunk, type RemoteConnectConfig, type RemoteExecutionEnv, type Result, type SessionToken, type SnapshotId, type VmLifecycleOptions, type WorkspaceHandle, type WriteExpectation, type WriteReceipt, type BoundFileObject } from "@sema-agent/core";
2
2
  import { type DeviceOwner, type DeviceRejectCode, type DeviceRow, type DeviceStore } from "../device-store.js";
3
3
  import type { DeviceEnrollment } from "../device-enrollment.js";
4
4
  import { type ExecutionPlacementSeat } from "../execution-placement.js";
@@ -48,6 +48,13 @@ type RejectCode = DeviceRejectCode;
48
48
  */
49
49
  export declare const DEVICE_REJECT_TO_EXEC_ERROR: Readonly<Record<RejectCode, ExecutionError["code"]>>;
50
50
  export declare const DEVICE_REJECT_TO_FILE_ERROR: Readonly<Record<RejectCode, FileError["code"]>>;
51
+ /**
52
+ * 生命周期面(connect/reconnect)的 typed 拒 → `RemoteExecutionErrorCode`。
53
+ *
54
+ * `connect_failed` = 本次到不了(**可重试**,core `RETRYABLE_REMOTE_ERROR_CODES` 含它);
55
+ * `auth_failed` = 永久拒(NEVER retry —— 换绑/吊销/不是你的设备,重试一万次也是同一答)。
56
+ */
57
+ export declare const DEVICE_REJECT_TO_REMOTE_ERROR: Readonly<Record<RejectCode, RemoteExecutionError["code"]>>;
51
58
  export interface DeviceEnvLogger {
52
59
  warn(msg: string, fields?: Record<string, unknown>): void;
53
60
  info?(msg: string, fields?: Record<string, unknown>): void;
@@ -102,9 +109,15 @@ export declare class RemoteDeviceExecutionEnv implements RemoteExecutionEnv {
102
109
  readLink?: (path: string, abortSignal?: AbortSignal) => Promise<Result<string, FileError>>;
103
110
  writeFileExclusive?: (path: string, content: string | Uint8Array, abortSignal?: AbortSignal) => Promise<Result<void, FileError>>;
104
111
  writeFileGuarded?: (path: string, content: string | Uint8Array, expect: WriteExpectation, abortSignal?: AbortSignal) => Promise<Result<WriteReceipt, FileError>>;
112
+ openBound?: (path: string, abortSignal?: AbortSignal) => Promise<Result<BoundFileObject, FileError>>;
105
113
  private connected;
106
114
  /** 在飞的那一次准入(单飞记忆)——见 {@link admitShared}。settle 即清,**不缓存败诺**。 */
107
115
  private connectPromise?;
116
+ /**
117
+ * 建连座(`remote-env-connect-seat.ts`):`connect` 的唯一次序 —— 准入在飞 ⇒ 等那一次(调用方的取消只退它自己的等待)· 已连 ⇒ 当下的把手、不再准入 · 否则准入一次。
118
+ * 本车道不记拒:准入每次都是对绑定 / 吊销 / 在线的一次新读(拒码按生命周期表拼:{@link DEVICE_REJECT_TO_REMOTE_ERROR})。
119
+ */
120
+ private readonly seat;
108
121
  /** 拆除是**终态**(六条兄弟腿同纪律:destroy 之后 connect/exec/读面一律 fail-closed,不复活)。 */
109
122
  private destroyed;
110
123
  constructor(cfg: DeviceEnvConfig);
@@ -170,10 +183,8 @@ export declare class RemoteDeviceExecutionEnv implements RemoteExecutionEnv {
170
183
  /**
171
184
  * 懒连接:**首次调用任一面**(exec / execStream / 文件面)时自动走一次准入。
172
185
  *
173
- * 🔴 为什么这条必须在 adapter 里(而不是「谁用谁先 connect」):task/run 车道**没有任何人**会替
174
- * adapter 调 `connect()` —— 工厂铸出 env 就直接用它的 exec/文件面(本仓亲核:`src/` 里对执行环境的
175
- * `.connect()` 只有 bench 与生命周期面自己)。所以一条「设了未连闸、又不自连」的腿,它的每一次工具
176
- * 调用都被自家的闸挡回,而设备侧一条指令帧都收不到。e2b(首用建 VM)/ k8s(`connectPromise ??=`)/
186
+ * 🔴 为什么这条必须在 adapter 里(而不是「谁用谁先 connect」):面可能在任何人连它之前就被调到(工厂交出之前的附件物化层、
187
+ * 委派子代的放置检查)。所以一条「设了未连闸、又不自连」的腿,它的每一次工具调用都被自家的闸挡回,而设备侧一条指令帧都收不到。e2b(首用建 VM)/ k8s(`connectPromise ??=`)/
177
188
  * ssh(single-flight join)/ local-docker 四腿早就是懒连接;本腿从此同形,`connect()` 的语义
178
189
  * (准入 + 取活连接 + 保护面对拍)一个字不动,只是触发时机与兄弟一致。
179
190
  *
@@ -262,6 +273,12 @@ export declare class RemoteDeviceExecutionEnv implements RemoteExecutionEnv {
262
273
  canonicalPath(p: string, abortSignal?: AbortSignal): Promise<Result<string, FileError>>;
263
274
  fileInfo(p: string, abortSignal?: AbortSignal): Promise<Result<FileInfo, FileError>>;
264
275
  private doReadLink;
276
+ private doOpenBound;
277
+ /**
278
+ * 逐答重绑的答者:每一答一条 `openBound` 指令,结局 kind **按 op 精确对拍**(`open` / `size` / `list` ⇒ `stat`,`readBytes` ⇒ `read`;答错 = 协议违约,不猜)。
279
+ * 开答之后的三答带开答时的 `identity` + `kind`,执行器在本机核对这一次打开的对象与它们都相等;每一答前照文件面同一道门(已拆 ⇒ 拒;首用准入)。
280
+ */
281
+ private boundAnswers;
265
282
  readBinaryFile(p: string, abortSignal?: AbortSignal): Promise<Result<Uint8Array, FileError>>;
266
283
  readTextFile(p: string, abortSignal?: AbortSignal): Promise<Result<string, FileError>>;
267
284
  readTextLines(p: string, options?: {
@@ -1,9 +1,12 @@
1
1
  import { posix } from "node:path";
2
+ import { anchoredPath, anchorUnder, joinedPath } from "./exec-path-law.js";
2
3
  import { ExecutionError, FileError, RemoteExecutionError, } from "@sema-agent/core";
4
+ import { openRebinding } from "./remote-shell.js";
3
5
  import { capabilitiesFor } from "../execution-lane-caps.js";
4
6
  import { DEVICE_ERROR_CODES, isDeviceAbsolutePath, } from "../device-store.js";
5
7
  import { boundDeviceOf } from "../execution-placement.js";
6
8
  import { createCutError, createExecFailure } from "../exec-cut.js";
9
+ import { createConnectSeat, withdrawOnAbort } from "./remote-env-connect-seat.js";
7
10
  export const DEVICE_PROVIDER = "device";
8
11
  const ok = (value) => ({ ok: true, value });
9
12
  const STREAM_PENDING_MAX_BYTES = 2 * 1024 * 1024;
@@ -88,12 +91,12 @@ export const DEVICE_REJECT_TO_FILE_ERROR = {
88
91
  "limit.rate_exceeded": "target_unavailable",
89
92
  "draining": "target_unavailable",
90
93
  };
91
- const DEVICE_REJECT_TO_REMOTE_ERROR = {
94
+ export const DEVICE_REJECT_TO_REMOTE_ERROR = {
92
95
  "device.not_bound": "connect_failed",
93
96
  "device.offline": "connect_failed",
94
97
  "device.busy": "connect_failed",
95
98
  "device.attached_elsewhere": "connect_failed",
96
- "device.claim_conflict": "auth_failed",
99
+ "device.claim_conflict": "connect_failed",
97
100
  "device.provider_mismatch": "auth_failed",
98
101
  "device.identity_mismatch": "auth_failed",
99
102
  "device.revoked": "auth_failed",
@@ -109,6 +112,9 @@ function rejectText(reject) {
109
112
  }
110
113
  const ENV_TERMINAL = { kind: "env_terminal" };
111
114
  const ENV_TERMINAL_MESSAGE = "execution env already destroyed";
115
+ function connectAnswerOf(admitted) {
116
+ return admitted.ok ? { ok: true, value: admitted.value } : { ok: false, error: new RemoteExecutionError(admitted.failure.remote, admitted.failure.message) };
117
+ }
112
118
  function failureFor(code, message) {
113
119
  return { remote: DEVICE_REJECT_TO_REMOTE_ERROR[code], exec: DEVICE_REJECT_TO_EXEC_ERROR[code], file: DEVICE_REJECT_TO_FILE_ERROR[code], message };
114
120
  }
@@ -136,8 +142,14 @@ export class RemoteDeviceExecutionEnv {
136
142
  readLink;
137
143
  writeFileExclusive;
138
144
  writeFileGuarded;
145
+ openBound;
139
146
  connected = false;
140
147
  connectPromise;
148
+ seat = createConnectSeat({
149
+ live: () => (this.connected && !this.destroyed ? this.workspaceHandle() : undefined),
150
+ inFlight: () => this.connectPromise?.then(connectAnswerOf),
151
+ establish: (config) => withdrawOnAbort(this.admitShared().then(connectAnswerOf), config?.abortSignal),
152
+ });
141
153
  destroyed = false;
142
154
  constructor(cfg) {
143
155
  this.cfg = cfg;
@@ -149,6 +161,8 @@ export class RemoteDeviceExecutionEnv {
149
161
  this.writeFileExclusive = (p, c, s) => this.doWrite("writeFileExclusive", p, c, undefined, s);
150
162
  if (cfg.supports?.has("writeFileGuarded") === true)
151
163
  this.writeFileGuarded = (p, c, e, s) => this.doGuardedWrite(p, c, e, s);
164
+ if (cfg.supports?.has("openBound") === true)
165
+ this.openBound = (p, s) => this.doOpenBound(p, s);
152
166
  }
153
167
  guardFaceSnapshot() {
154
168
  const out = new Set();
@@ -174,7 +188,7 @@ export class RemoteDeviceExecutionEnv {
174
188
  "performing a NON-atomic write here is exactly the silent downgrade guarded writes exist to prevent (#157). Refusing; the per-task factory mints a correctly-shaped env on the next task.", absPath);
175
189
  }
176
190
  resolve(p) {
177
- return isDeviceAbsolutePath(p) ? posix.normalize(p) : posix.normalize(posix.join(this.cwd, p));
191
+ return anchoredPath(DEVICE_PATH_FAMILY, this.cwd, p);
178
192
  }
179
193
  instructionCwd(requested) {
180
194
  return requested === undefined ? this.cwd : this.resolve(requested);
@@ -183,7 +197,7 @@ export class RemoteDeviceExecutionEnv {
183
197
  return ok(this.resolve(p));
184
198
  }
185
199
  async joinPath(parts) {
186
- return ok(posix.join(...parts));
200
+ return ok(joinedPath(DEVICE_PATH_FAMILY, parts));
187
201
  }
188
202
  workspaceHandle() {
189
203
  return {
@@ -195,12 +209,7 @@ export class RemoteDeviceExecutionEnv {
195
209
  };
196
210
  }
197
211
  async connect(config) {
198
- if (config?.abortSignal?.aborted === true)
199
- return { ok: false, error: new RemoteExecutionError("aborted", "connect aborted") };
200
- const admitted = await this.admitShared();
201
- if (admitted.ok)
202
- return ok(admitted.value);
203
- return { ok: false, error: new RemoteExecutionError(admitted.failure.remote, admitted.failure.message) };
212
+ return this.seat.connect(config);
204
213
  }
205
214
  async admitShared() {
206
215
  const inflight = this.connectPromise;
@@ -557,7 +566,7 @@ export class RemoteDeviceExecutionEnv {
557
566
  return this.wrongOutcome("stat", o.kind, abs);
558
567
  if (!o.info)
559
568
  return { ok: false, error: new FileError("unknown", "device returned a stat outcome without info — protocol violation", abs) };
560
- return ok(toFileInfo(o.info));
569
+ return ok(toFileInfo(o.info, abs));
561
570
  });
562
571
  }
563
572
  async doReadLink(p, abortSignal) {
@@ -576,6 +585,96 @@ export class RemoteDeviceExecutionEnv {
576
585
  return ok(o.linkTarget);
577
586
  });
578
587
  }
588
+ async doOpenBound(p, abortSignal) {
589
+ const gated = await this.fsGate(p);
590
+ if (gated)
591
+ return gated;
592
+ const abs = this.resolve(p);
593
+ return openRebinding(abs, this.boundAnswers(abs), abortSignal);
594
+ }
595
+ boundAnswers(abs) {
596
+ const send = async (args, signal, onChunk) => {
597
+ const gated = await this.fsGate(abs);
598
+ if (gated)
599
+ return gated;
600
+ return this.dispatch("openBound", { path: abs, ...args }, { timeoutMs: this.cfg.execTimeoutMs, ...(signal === undefined ? {} : { signal }), ...(onChunk === undefined ? {} : { onChunk }) });
601
+ };
602
+ const violation = (what) => ({ ok: false, error: new FileError("unknown", `device returned an openBound outcome ${what} — protocol violation`, abs) });
603
+ return {
604
+ open: async (signal) => {
605
+ const res = await send({ op: "open" }, signal);
606
+ if ("ok" in res)
607
+ return res;
608
+ return this.fileOutcome(res, abs, (o) => {
609
+ if (o.kind !== "stat")
610
+ return this.wrongOutcome("stat", o.kind, abs);
611
+ if (typeof o.identity !== "string" || o.identity === "" || o.info === undefined)
612
+ return violation("without identity and info for the open answer");
613
+ if (o.info.kind !== "file" && o.info.kind !== "dir")
614
+ return violation(`naming a ${o.info.kind} (an executor refuses anything but a regular file or a directory with invalid)`);
615
+ return ok({ identity: o.identity, kind: o.info.kind === "dir" ? "directory" : "file" });
616
+ });
617
+ },
618
+ size: async (bound) => {
619
+ const res = await send({ op: "size", identity: bound.identity, kind: bound.kind }, undefined);
620
+ if ("ok" in res)
621
+ return res;
622
+ return this.fileOutcome(res, abs, (o) => {
623
+ if (o.kind !== "stat")
624
+ return this.wrongOutcome("stat", o.kind, abs);
625
+ if (o.info === undefined)
626
+ return violation("without info for the size answer");
627
+ return ok(o.info.size);
628
+ });
629
+ },
630
+ readBytes: async (bound, max, signal) => {
631
+ const parts = [];
632
+ let got = 0;
633
+ let stray;
634
+ const res = await send({ op: "readBytes", identity: bound.identity, kind: bound.kind, max }, signal, (c) => {
635
+ if (c.kind !== "data") {
636
+ stray ??= c.kind;
637
+ return;
638
+ }
639
+ if (c.data === undefined || c.data.length === 0)
640
+ return;
641
+ got += c.data.length;
642
+ if (got <= max)
643
+ parts.push(c.data);
644
+ });
645
+ if ("ok" in res)
646
+ return res;
647
+ return this.fileOutcome(res, abs, (o) => {
648
+ if (o.kind !== "read")
649
+ return this.wrongOutcome("read", o.kind, abs);
650
+ if (stray !== undefined)
651
+ return violation(`carrying a ${stray} chunk for a readBytes answer (only data chunks belong to it)`);
652
+ if (got > max)
653
+ return { ok: false, error: new FileError("invalid", `The held file is over the ${max}-byte read cap (${got} bytes or more arrived); it was not read`, abs) };
654
+ return ok(new Uint8Array(Buffer.concat(parts)));
655
+ });
656
+ },
657
+ list: async (bound, signal) => {
658
+ const res = await send({ op: "list", identity: bound.identity, kind: bound.kind }, signal);
659
+ if ("ok" in res)
660
+ return res;
661
+ return this.fileOutcome(res, abs, (o) => {
662
+ if (o.kind !== "stat")
663
+ return this.wrongOutcome("stat", o.kind, abs);
664
+ if (o.entries === undefined)
665
+ return violation("without entries for the list answer");
666
+ const entries = [];
667
+ for (const w of o.entries) {
668
+ const kind = w.kind === "dir" ? "directory" : w.kind === "symlink" ? "symlink" : w.kind === "file" ? "file" : undefined;
669
+ if (kind === undefined)
670
+ continue;
671
+ entries.push({ name: posix.basename(w.path), kind, size: w.size, mtimeMs: w.mtimeMs });
672
+ }
673
+ return ok(entries);
674
+ });
675
+ },
676
+ };
677
+ }
579
678
  async readBinaryFile(p, abortSignal) {
580
679
  const gated = await this.fsGate(p);
581
680
  if (gated)
@@ -642,7 +741,7 @@ export class RemoteDeviceExecutionEnv {
642
741
  return this.wrongOutcome("stat", o.kind, abs);
643
742
  if (!o.entries)
644
743
  return { ok: false, error: new FileError("unknown", "device returned a stat outcome without entries — protocol violation", abs) };
645
- return ok(o.entries.map(toFileInfo));
744
+ return ok(o.entries.map((w) => toFileInfo(w, anchorUnder(DEVICE_PATH_FAMILY, abs, posix.basename(w.path)))));
646
745
  });
647
746
  }
648
747
  async writeFile(p, content, abortSignal) {
@@ -738,8 +837,9 @@ function mergedEnv(taskEnv, commandEnv) {
738
837
  function markTruncated(text, truncated) {
739
838
  return truncated === true ? `${text}\n[output truncated by the device executor]` : text;
740
839
  }
741
- function toFileInfo(w) {
742
- return { name: posix.basename(w.path), path: w.path, kind: w.kind === "dir" ? "directory" : w.kind === "symlink" ? "symlink" : "file", size: w.size, mtimeMs: w.mtimeMs };
840
+ const DEVICE_PATH_FAMILY = { sep: posix.sep, isAbsolute: isDeviceAbsolutePath };
841
+ function toFileInfo(w, at) {
842
+ return { name: posix.basename(at), path: at, kind: w.kind === "dir" ? "directory" : w.kind === "symlink" ? "symlink" : "file", size: w.size, mtimeMs: w.mtimeMs };
743
843
  }
744
844
  function toWireExpectation(e) {
745
845
  return {
@@ -20,7 +20,7 @@
20
20
  * iterator MAY throw `RemoteExecutionError` mid-stream before an `exit` chunk (seam contract, OutputChunk).
21
21
  */
22
22
  import { Sandbox, type SandboxOpts, type SandboxConnectOpts } from "e2b";
23
- import { FileError, ExecutionError, RemoteExecutionError, type ExecutionEnv, type ExecResult, type RemoteExecutionEnv, type WorkspaceHandle, type FileInfo, type Result, type SymlinkChain, type OutputChunk, type ExecStreamOptions, type RemoteConnectConfig, type SnapshotId, type SessionToken, type ExecutionEnvFactory, type VmLifecycleOptions, type BackgroundShellCapability, type BackgroundShellId, type BackgroundShellError, type BackgroundSpawnOptions, type BackgroundPoll, type ExecutionEnvCapabilities } from "@sema-agent/core";
23
+ import { FileError, ExecutionError, RemoteExecutionError, type ExecutionEnv, type ExecResult, type RemoteExecutionEnv, type WorkspaceHandle, type FileInfo, type Result, type SymlinkChain, type BoundFileObject, type OutputChunk, type ExecStreamOptions, type RemoteConnectConfig, type SnapshotId, type SessionToken, type ExecutionEnvFactory, type VmLifecycleOptions, type BackgroundShellCapability, type BackgroundShellId, type BackgroundShellError, type BackgroundSpawnOptions, type BackgroundPoll, type ExecutionEnvCapabilities } from "@sema-agent/core";
24
24
  import { type SandboxReadFloorKnob } from "../execution-lane-caps.js";
25
25
  import { type SandboxReadFloor } from "./sandbox-read-floor.js";
26
26
  import { type LaneHomeConfig } from "../lane-home-probe.js";
@@ -45,7 +45,7 @@ export interface RemoteContainerEnvConfig extends RemoteEnvObservers, LaneHomeCo
45
45
  template?: string;
46
46
  /** Sandbox lifetime in ms (E2B billing window). Default {@link DEFAULT_TIMEOUT_MS}. */
47
47
  timeoutMs?: number;
48
- /** Workspace root inside the VM; remote paths normalize against it. Default {@link DEFAULT_MOUNT_PATH}. */
48
+ /** Workspace root inside the VM; a relative path is anchored at it (`exec-path-law.ts`: anchored, never folded). Default {@link DEFAULT_MOUNT_PATH}. */
49
49
  mountPath?: string;
50
50
  /** Outbound network for the sandbox (clay: service needs external-network capability). Default `true`. */
51
51
  allowInternetAccess?: boolean;
@@ -127,9 +127,12 @@ export declare class RemoteContainerExecutionEnv implements RemoteExecutionEnv,
127
127
  private destroyed;
128
128
  /** 家目录的座(主张在场 ⇒ 每只沙箱对证 · 否则建沙箱那一拍实测 · 实测缺席 ⇒ 缺席 + 一行 warn)。 */
129
129
  private readonly homeSeat;
130
- /** 这只环境的拒 —— 记一次、此后每次连接 / 恢复原样回它(同一模板同一个答案:不再建沙箱、不再问)。两个写者:家目录(沙箱答了而与主张不一致,或答的不是绝对路径)
131
- * 与恢复点的填充(换一台沙箱也是同一个答的拒:种子不是项目包、摘要不符、镜像没有 `tar`、镜像预置撞名 —— `FillOutcome.standing`)。 */
132
- private connectRefusal?;
130
+ /**
131
+ * 建连座(`remote-env-connect-seat.ts`):`connect` 的唯一次序(记住的拒 ⇒ 原样再答 · 一走在飞 ⇒ 等它 · 已连 ⇒ 当下的把手 · 否则建一只沙箱),并持有这只环境记住的拒 ——
132
+ * 记一次、此后每次连接 / 恢复原样回它(同一模板同一个答案:不再建沙箱、不再问)。两件立定的事实写它:家目录(沙箱答了而与主张不一致,或答的不是绝对路径;`env.home_unconfirmed`)
133
+ * 与恢复点的填充(换一台沙箱也是同一个答的拒:种子不是项目包、摘要不符、镜像没有 `tar`、镜像预置撞名 —— `FillOutcome.standing`;`env.sandbox_refused`)。
134
+ */
135
+ private readonly seat;
133
136
  /** In-flight connect/resume, memoized so concurrent lazy first-use OR resume provisions exactly one VM (no double-bill). `target` = the snapshot a resume
134
137
  * restores (`undefined` = a fresh sandbox): {@link resumeVM} shares only a resume of the SAME snapshot — a connect for anything else is not the
135
138
  * workspace it is asked to restore (it waits that one out, then releases the sandbox it left). */
@@ -223,8 +226,9 @@ export declare class RemoteContainerExecutionEnv implements RemoteExecutionEnv,
223
226
  }>;
224
227
  /**
225
228
  * 家目录:座要问({@link LaneHomeSeat.asks}:主张在场 ⇒ 对证;没主张 ⇒ 量到绝对路径为止)⇒ 在这只沙箱里问一次 `$HOME`(前台 `commands.run`,与命令同一个
226
- * 模板缺省用户、同一份沙箱 env),读数交座。返回这只沙箱的拒(`connect_failed`,拒句 = 同一只判官的句子),或 `undefined`(照用)。
227
- * 沙箱答了的拒记在环境上(同一模板同一个答案);没答上(非零退出 / RPC 失败 / 输出里没有标记行)⇒ 这一次不开、不记,下一次重问;调用方中止 ⇒ `aborted`、不交座。
229
+ * 模板缺省用户、同一份沙箱 env),读数交座。返回这只沙箱的拒(拒句 = 同一只判官的句子),或 `undefined`(照用)。
230
+ * 沙箱答了的拒是立定的拒(`env.home_unconfirmed`,建连座记住它:同一模板同一个答案);没答上(非零退出 / RPC 失败 / 输出里没有标记行)⇒ 这一次不开、不记(`connect_failed`),
231
+ * 下一次重问;调用方中止 ⇒ `aborted`、不交座。
228
232
  * 上界 = 控制面 RPC 的墙钟(`rpcTimeoutMs`:问一个环境变量,不是跑用户命令)。
229
233
  */
230
234
  private homeVerdict;
@@ -295,6 +299,11 @@ export declare class RemoteContainerExecutionEnv implements RemoteExecutionEnv,
295
299
  * 🔴 exit≠0 归 `unknown` 而不是 `not_found`(与本腿 `readLink` 那一格刻意分家,同 `posix-shell-fs` 的理由):
296
300
  * 「叶不是软链」与「叶不存在」在这条命令上都是 exit 0 + 空 stdout,所以 exit≠0 一定不是「文件不在」。 */
297
301
  canonicalChain(p: string, abortSignal?: AbortSignal): Promise<Result<SymlinkChain, FileError>>;
302
+ /** 绑定读(逐答重绑,Linux 读法)。本腿不并进 `posix-shell-fs` 基座(原生 SDK 腿),但脚本、读法与工厂与四条 shell 腿**是同一只**
303
+ * (`remote-shell.ts` `openBoundOverShell`),这里只多一条传输:{@link execInternal}(适配器自己的固定命令那一起动,与链脚本同一形)、数据界。
304
+ * 🔴 字节只经 stdout 的 base64 回来 —— `files.read` 按路径读,不能用;也不「拷进暂存件再按路径读」(同身份进程可换暂存件或它的祖先目录)。 */
305
+ openBound(p: string, abortSignal?: AbortSignal): Promise<Result<BoundFileObject, FileError>>;
306
+ private get boundLane();
298
307
  canonicalPath(p: string, abortSignal?: AbortSignal): Promise<Result<string, FileError>>;
299
308
  exists(p: string, abortSignal?: AbortSignal): Promise<Result<boolean, FileError>>;
300
309
  createDir(p: string, options?: {
@@ -365,6 +374,8 @@ export declare class RemoteContainerExecutionEnv implements RemoteExecutionEnv,
365
374
  private ensureConnected;
366
375
  private fsReady;
367
376
  private tempResult;
377
+ /** 提供方的条目 → `FileInfo`。`path` 取本环境的拼法(`at`:被问的那只路径 / 列目录时锚在它之下的条目),不取提供方回报的那一份
378
+ * —— 提供方可能已把 `..` 折掉,而执行缝路径律(`exec-path-law.ts`)要求条目名在交来的路径之下、能原样再交回来。 */
368
379
  private toFileInfo;
369
380
  private fileError;
370
381
  /** A thrown provider error → this exec's error. The two cuts (aborted, the provider's own command deadline) are a command that RAN: they carry what it had
@@ -1,4 +1,5 @@
1
1
  import path from "node:path";
2
+ import { anchoredPath, anchorUnder, joinedPath } from "./exec-path-law.js";
2
3
  import { Sandbox, CommandExitError, FileType } from "e2b";
3
4
  import { FileError, ExecutionError, RemoteExecutionError, RollingTailBuffer, markTruncated, } from "@sema-agent/core";
4
5
  import { capabilitiesFor } from "../execution-lane-caps.js";
@@ -6,7 +7,8 @@ import { DEPLOYMENT_SHAPE_PREDICATES } from "../deployment-shape.js";
6
7
  import { ADAPTER_START, BOOTSTRAP_START, createSandboxReadFloor, e2bFloorCommand, engineStart, } from "./sandbox-read-floor.js";
7
8
  import { LANE_HOME_PROBE_COMMAND, LaneHomeSeat } from "../lane-home-probe.js";
8
9
  import { fileErrorFromExec } from "./remote-env-file-error.js";
9
- import { appendFileStaged, readyUpload, rewriteAppend, numEnvOr, runSymlinkChain, shellQuote, writeFileStaged } from "./remote-shell.js";
10
+ import { connectRefusal, createConnectSeat } from "./remote-env-connect-seat.js";
11
+ import { appendFileStaged, openBoundOverShell, readyUpload, rewriteAppend, numEnvOr, runSymlinkChain, shellQuote, writeFileStaged } from "./remote-shell.js";
10
12
  import { buildLivenessCut, commandBoundsOf, firstBoundOf, laneLivenessOf, LIVENESS_MS_DEFAULT } from "../exec-bounds.js";
11
13
  import { createCutError } from "../exec-cut.js";
12
14
  import { BackgroundShellManager } from "./background-shell-support.js";
@@ -36,7 +38,22 @@ export class RemoteContainerExecutionEnv {
36
38
  handle;
37
39
  destroyed = false;
38
40
  homeSeat;
39
- connectRefusal;
41
+ seat = createConnectSeat({
42
+ live: () => (this.sbx !== undefined && !this.paused ? this.workspaceHandle() : undefined),
43
+ inFlight: () => this.connecting?.promise,
44
+ establish: (config) => {
45
+ if (this.paused)
46
+ return Promise.resolve({ ok: false, error: new RemoteExecutionError("connect_failed", "execution env is suspended (paused) — resumeVM(snapshotId) before use") });
47
+ const mine = { target: undefined, promise: this.doConnect(config) };
48
+ this.connecting = mine;
49
+ const settled = () => {
50
+ if (this.connecting === mine)
51
+ this.connecting = undefined;
52
+ };
53
+ void mine.promise.then(settled, settled);
54
+ return mine.promise;
55
+ },
56
+ });
40
57
  connecting;
41
58
  activeCommands = 0;
42
59
  paused = false;
@@ -155,20 +172,7 @@ export class RemoteContainerExecutionEnv {
155
172
  }
156
173
  if (this.destroyed)
157
174
  return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env already destroyed") };
158
- if (this.connectRefusal)
159
- return { ok: false, error: this.connectRefusal };
160
- if (this.paused)
161
- return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env is suspended (paused) — resumeVM(snapshotId) before use") };
162
- if (this.sbx)
163
- return ok(this.workspaceHandle());
164
- if (this.connecting)
165
- return this.connecting.promise;
166
- const mine = { target: undefined, promise: this.doConnect(config) };
167
- this.connecting = mine;
168
- const r = await mine.promise;
169
- if (!r.ok && this.connecting === mine)
170
- this.connecting = undefined;
171
- return r;
175
+ return this.seat.connect(config);
172
176
  }
173
177
  async doConnect(config) {
174
178
  try {
@@ -199,9 +203,7 @@ export class RemoteContainerExecutionEnv {
199
203
  if (!filled.ok || this.destroyed) {
200
204
  await sbx.kill().catch(() => { });
201
205
  this.cfg.metrics?.inc("remote_env_connect_total", { provider: PROVIDER, result: "error" });
202
- if (!filled.ok && filled.standing)
203
- this.connectRefusal = filled.error;
204
- return { ok: false, error: filled.ok ? new RemoteExecutionError("connect_failed", "execution env destroyed during connect") : filled.error };
206
+ return filled.ok ? { ok: false, error: new RemoteExecutionError("connect_failed", "execution env destroyed during connect") } : this.seat.answer(filled);
205
207
  }
206
208
  this.sbx = sbx;
207
209
  this.paused = false;
@@ -255,8 +257,9 @@ export class RemoteContainerExecutionEnv {
255
257
  resumeVM(snapshotId, options) {
256
258
  if (this.destroyed)
257
259
  return Promise.resolve({ ok: false, error: new RemoteExecutionError("connect_failed", "env already destroyed") });
258
- if (this.connectRefusal)
259
- return Promise.resolve({ ok: false, error: this.connectRefusal });
260
+ const held = this.seat.held;
261
+ if (held)
262
+ return Promise.resolve(held);
260
263
  if (this.sbx && !this.paused && this.sbx.sandboxId === snapshotId)
261
264
  return Promise.resolve(ok(this.workspaceHandle()));
262
265
  const inFlight = this.connecting;
@@ -268,10 +271,7 @@ export class RemoteContainerExecutionEnv {
268
271
  if (this.connecting === mine)
269
272
  this.connecting = undefined;
270
273
  };
271
- mine.promise.then((r) => {
272
- if (!r.ok)
273
- forget();
274
- }, forget);
274
+ mine.promise.then(forget, forget);
275
275
  return mine.promise;
276
276
  }
277
277
  async resumeWalk(inFlight, snapshotId, options) {
@@ -279,6 +279,9 @@ export class RemoteContainerExecutionEnv {
279
279
  await inFlight.promise;
280
280
  if (this.destroyed)
281
281
  return { ok: false, error: new RemoteExecutionError("connect_failed", "env already destroyed") };
282
+ const held = this.seat.held;
283
+ if (held)
284
+ return held;
282
285
  if (this.sbx && !this.paused && this.sbx.sandboxId === snapshotId)
283
286
  return ok(this.workspaceHandle());
284
287
  }
@@ -328,9 +331,7 @@ export class RemoteContainerExecutionEnv {
328
331
  if (!filled.ok) {
329
332
  await this.rpc("sandbox.pause", (signal) => sbx.pause({ signal }), undefined, this.cfg.dataTimeoutMs).catch((e) => this.cfg.logger?.warn?.("remote_env_fill_refused_repause_failed", { sandboxId: sbx.sandboxId, error: errMsg(e) }));
330
333
  this.cfg.metrics?.inc("remote_env_resume_total", { provider: PROVIDER, result: "error" });
331
- if (filled.standing)
332
- this.connectRefusal = filled.error;
333
- return { ok: false, error: filled.error };
334
+ return this.seat.answer(filled);
334
335
  }
335
336
  this.sbx = sbx;
336
337
  this.paused = false;
@@ -382,10 +383,7 @@ export class RemoteContainerExecutionEnv {
382
383
  const verdict = this.homeSeat.settle(probe);
383
384
  if (verdict === undefined)
384
385
  return undefined;
385
- const error = new RemoteExecutionError("connect_failed", verdict.refusal);
386
- if (verdict.answered)
387
- this.connectRefusal = error;
388
- return error;
386
+ return verdict.answered ? this.seat.answer(connectRefusal("home_unconfirmed", verdict.refusal)).error : new RemoteExecutionError("connect_failed", verdict.refusal);
389
387
  }
390
388
  fillAt(sbx, root, origin) {
391
389
  return this.cfg.workspaceFill === undefined ? Promise.resolve({ ok: true }) : this.cfg.workspaceFill.fill(this.fillChannel(sbx), root, origin);
@@ -862,7 +860,7 @@ export class RemoteContainerExecutionEnv {
862
860
  return ok(this.resolve(p));
863
861
  }
864
862
  async joinPath(parts) {
865
- return ok(path.posix.join(...parts));
863
+ return ok(joinedPath(path.posix, parts));
866
864
  }
867
865
  async readTextFile(p, abortSignal) {
868
866
  const sbx = await this.fsReady(p);
@@ -934,8 +932,9 @@ export class RemoteContainerExecutionEnv {
934
932
  if (!sbx.ok)
935
933
  return sbx;
936
934
  try {
937
- const info = await this.rpc("files.getInfo", (signal) => sbx.value.files.getInfo(this.resolve(p), { signal }), abortSignal);
938
- return ok(this.toFileInfo(info));
935
+ const abs = this.resolve(p);
936
+ const info = await this.rpc("files.getInfo", (signal) => sbx.value.files.getInfo(abs, { signal }), abortSignal);
937
+ return ok(this.toFileInfo(info, abs));
939
938
  }
940
939
  catch (e) {
941
940
  return { ok: false, error: this.fileError(e, this.resolve(p), abortSignal) };
@@ -946,8 +945,9 @@ export class RemoteContainerExecutionEnv {
946
945
  if (!sbx.ok)
947
946
  return sbx;
948
947
  try {
949
- const entries = await this.rpc("files.list", (signal) => sbx.value.files.list(this.resolve(p), { signal }), abortSignal);
950
- return ok(entries.map((e) => this.toFileInfo(e)));
948
+ const abs = this.resolve(p);
949
+ const entries = await this.rpc("files.list", (signal) => sbx.value.files.list(abs, { signal }), abortSignal);
950
+ return ok(entries.map((e) => this.toFileInfo(e, anchorUnder(path.posix, abs, e.name))));
951
951
  }
952
952
  catch (e) {
953
953
  return { ok: false, error: this.fileError(e, this.resolve(p), abortSignal) };
@@ -965,6 +965,19 @@ export class RemoteContainerExecutionEnv {
965
965
  async canonicalChain(p, abortSignal) {
966
966
  return runSymlinkChain(this.resolve(p), (cmd) => this.execInternal(cmd, { abortSignal }));
967
967
  }
968
+ async openBound(p, abortSignal) {
969
+ return openBoundOverShell(this.resolve(p), this.boundLane, abortSignal);
970
+ }
971
+ get boundLane() {
972
+ return {
973
+ run: async (cmd, _cap, abortSignal) => {
974
+ const r = await this.execInternal(cmd, { cwd: "/", timeout: this.cfg.dataTimeoutMs / 1000, ...(abortSignal !== undefined ? { abortSignal } : {}) });
975
+ return r.ok ? { ok: true, value: { stdout: Buffer.from(r.value.stdout, "utf8"), stderr: r.value.stderr, exitCode: r.value.exitCode } } : r;
976
+ },
977
+ payload: "base64",
978
+ boundSec: this.cfg.dataTimeoutMs / 1000,
979
+ };
980
+ }
968
981
  async canonicalPath(p, abortSignal) {
969
982
  const abs = this.resolve(p);
970
983
  const r = await this.execInternal(`realpath -- ${shellQuote(abs)}`, { abortSignal });
@@ -1044,7 +1057,7 @@ export class RemoteContainerExecutionEnv {
1044
1057
  return this.tempResult(r, "mktemp");
1045
1058
  }
1046
1059
  resolve(p) {
1047
- return path.posix.isAbsolute(p) ? path.posix.normalize(p) : path.posix.normalize(path.posix.join(this.cwd, p));
1060
+ return anchoredPath(path.posix, this.cwd, p);
1048
1061
  }
1049
1062
  async rpc(label, op, userSignal, timeoutMs = this.cfg.rpcTimeoutMs) {
1050
1063
  const deadline = new AbortController();
@@ -1163,11 +1176,11 @@ export class RemoteContainerExecutionEnv {
1163
1176
  return { ok: false, error: new FileError("unknown", r.value.stderr.trim() || `${op} failed`) };
1164
1177
  return ok(r.value.stdout.trim());
1165
1178
  }
1166
- toFileInfo(e) {
1179
+ toFileInfo(e, at) {
1167
1180
  const kind = e.symlinkTarget ? "symlink" : e.type === FileType.DIR ? "directory" : "file";
1168
1181
  return {
1169
1182
  name: e.name,
1170
- path: e.path,
1183
+ path: at,
1171
1184
  kind,
1172
1185
  size: e.size ?? 0,
1173
1186
  mtimeMs: e.modifiedTime ? e.modifiedTime.getTime() : 0,
@@ -1,9 +1,13 @@
1
1
  import { FileError, RemoteExecutionError } from "@sema-agent/core";
2
+ import { isExecutionEnvRefusalCode } from "../execution-env-refusal.js";
3
+ function sayEnvNotOpened(code) {
4
+ return code === "connect_failed" || isExecutionEnvRefusalCode(code);
5
+ }
2
6
  function neverOpened(e) {
3
- if (e.code === "connect_failed")
7
+ if (sayEnvNotOpened(e.code))
4
8
  return true;
5
9
  const cause = e.cause;
6
- return cause instanceof RemoteExecutionError && cause.code === "connect_failed";
10
+ return cause instanceof RemoteExecutionError && sayEnvNotOpened(cause.code);
7
11
  }
8
12
  export function fileErrorFromExec(e, path) {
9
13
  const code = e.code === "aborted" ? "aborted" : neverOpened(e) ? "target_unavailable" : "unknown";