@sema-agent/server 1.322.0 → 2.0.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 (539) hide show
  1. package/dist/approval-hmac.d.ts +40 -0
  2. package/dist/approval-hmac.js +67 -0
  3. package/dist/approval.d.ts +31 -0
  4. package/dist/approval.js +72 -4
  5. package/dist/audit.d.ts +26 -0
  6. package/dist/audit.js +40 -1
  7. package/dist/auth-bridge.d.ts +10 -0
  8. package/dist/auth-bridge.js +47 -4
  9. package/dist/auth-keys.d.ts +19 -0
  10. package/dist/auth-keys.js +13 -0
  11. package/dist/bake-runner/main.d.ts +5 -0
  12. package/dist/bake-runner/main.js +43 -4
  13. package/dist/bake-runner/protocol.d.ts +57 -0
  14. package/dist/bake-runner/protocol.js +58 -3
  15. package/dist/bake-runner/runner.d.ts +54 -0
  16. package/dist/bake-runner/runner.js +87 -5
  17. package/dist/bench/l8/artifact.d.ts +63 -0
  18. package/dist/bench/l8/artifact.js +61 -2
  19. package/dist/bench/l8/escape.d.ts +36 -0
  20. package/dist/bench/l8/escape.js +25 -0
  21. package/dist/bench/l8/index.d.ts +14 -0
  22. package/dist/bench/l8/index.js +14 -0
  23. package/dist/bench/l8/probes.d.ts +74 -0
  24. package/dist/bench/l8/probes.js +60 -0
  25. package/dist/bench/l8/run-probes.d.ts +43 -0
  26. package/dist/bench/l8/run-probes.js +45 -2
  27. package/dist/bench/s1/arms.d.ts +126 -0
  28. package/dist/bench/s1/arms.js +188 -13
  29. package/dist/bench/s1/live-deps.d.ts +66 -0
  30. package/dist/bench/s1/live-deps.js +249 -11
  31. package/dist/bench/s1/oracle.d.ts +49 -0
  32. package/dist/bench/s1/oracle.js +36 -1
  33. package/dist/bench/s1/repair-oracle-adapter.d.ts +33 -0
  34. package/dist/bench/s1/repair-oracle-adapter.js +30 -0
  35. package/dist/bench/s1/reviewer.d.ts +38 -0
  36. package/dist/bench/s1/reviewer.js +34 -0
  37. package/dist/bench/s1/row.d.ts +77 -0
  38. package/dist/bench/s1/row.js +62 -0
  39. package/dist/bench/s1/run-firm.d.ts +47 -0
  40. package/dist/bench/s1/run-firm.js +70 -4
  41. package/dist/bench/s1/runner-ctx.d.ts +71 -0
  42. package/dist/bench/s1/runner-ctx.js +36 -0
  43. package/dist/bench/s1/tasks.d.ts +108 -0
  44. package/dist/bench/s1/tasks.js +115 -0
  45. package/dist/boot-reclaim.d.ts +19 -0
  46. package/dist/boot-reclaim.js +20 -0
  47. package/dist/brain.d.ts +25 -0
  48. package/dist/brain.js +61 -1
  49. package/dist/budget.d.ts +66 -0
  50. package/dist/budget.js +128 -4
  51. package/dist/capabilities/builtin-tools.d.ts +3 -0
  52. package/dist/capabilities/builtin-tools.js +3 -0
  53. package/dist/capabilities/center-plugins.d.ts +17 -1
  54. package/dist/capabilities/center-plugins.js +45 -4
  55. package/dist/capabilities/center-prompts.d.ts +46 -0
  56. package/dist/capabilities/center-prompts.js +57 -3
  57. package/dist/capabilities/code-review-council.d.ts +28 -0
  58. package/dist/capabilities/code-review-council.js +40 -7
  59. package/dist/capabilities/collab-wire.d.ts +2 -0
  60. package/dist/capabilities/collab-workflows.d.ts +7 -0
  61. package/dist/capabilities/collab-workflows.js +44 -1
  62. package/dist/capabilities/oa-tools.d.ts +6 -0
  63. package/dist/capabilities/oa-tools.js +10 -0
  64. package/dist/capabilities/prompt.d.ts +14 -0
  65. package/dist/capabilities/prompt.js +14 -0
  66. package/dist/capabilities/prompts/code-review.d.ts +14 -0
  67. package/dist/capabilities/prompts/code-review.js +14 -0
  68. package/dist/capabilities/prompts/identity.d.ts +6 -0
  69. package/dist/capabilities/prompts/identity.js +6 -0
  70. package/dist/capabilities/prompts/team.d.ts +4 -0
  71. package/dist/capabilities/prompts/team.js +4 -0
  72. package/dist/capabilities/repo-tools.d.ts +11 -0
  73. package/dist/capabilities/repo-tools.js +8 -0
  74. package/dist/capabilities/sandbox-file-send.d.ts +95 -0
  75. package/dist/capabilities/sandbox-file-send.js +91 -3
  76. package/dist/capabilities/scenarios.d.ts +130 -0
  77. package/dist/capabilities/scenarios.js +180 -7
  78. package/dist/capabilities/select-environment-tool.d.ts +7 -0
  79. package/dist/capabilities/select-environment-tool.js +30 -0
  80. package/dist/capabilities/send-user-file-tool.d.ts +33 -0
  81. package/dist/capabilities/send-user-file-tool.js +65 -1
  82. package/dist/capabilities/skills.d.ts +29 -0
  83. package/dist/capabilities/skills.js +13 -2
  84. package/dist/capabilities/team.d.ts +22 -0
  85. package/dist/capabilities/team.js +22 -2
  86. package/dist/capabilities/tool-defer.d.ts +26 -0
  87. package/dist/capabilities/tool-defer.js +3 -0
  88. package/dist/config-center/apply-effective.d.ts +65 -0
  89. package/dist/config-center/apply-effective.js +204 -12
  90. package/dist/config-center/http-client.d.ts +34 -0
  91. package/dist/config-center/http-client.js +68 -0
  92. package/dist/config-center/restart-signal.d.ts +22 -0
  93. package/dist/config-center/restart-signal.js +39 -0
  94. package/dist/config-center/skills-mcp.d.ts +20 -1
  95. package/dist/config-center/skills-mcp.js +42 -7
  96. package/dist/config-center/types.d.ts +92 -1
  97. package/dist/config-lkg.d.ts +12 -0
  98. package/dist/config-lkg.js +41 -0
  99. package/dist/config-provider.d.ts +78 -0
  100. package/dist/config-provider.js +165 -2
  101. package/dist/config-types.d.ts +546 -0
  102. package/dist/config.d.ts +64 -0
  103. package/dist/config.js +455 -60
  104. package/dist/degenerate-instrument.d.ts +38 -0
  105. package/dist/degenerate-instrument.js +55 -3
  106. package/dist/elicitation.d.ts +39 -0
  107. package/dist/elicitation.js +87 -3
  108. package/dist/env-facts.d.ts +61 -0
  109. package/dist/env-facts.js +73 -7
  110. package/dist/fleet/fleet-bus.d.ts +185 -0
  111. package/dist/fleet/fleet-bus.js +333 -27
  112. package/dist/fleet/subagent-tail-bus.d.ts +11 -0
  113. package/dist/fleet/subagent-tail-bus.js +33 -2
  114. package/dist/fleet-client.d.ts +38 -0
  115. package/dist/fleet-client.js +44 -9
  116. package/dist/fleet-lease.d.ts +46 -0
  117. package/dist/fleet-lease.js +73 -5
  118. package/dist/hooks/branch-transcript.d.ts +8 -0
  119. package/dist/hooks/branch-transcript.js +33 -0
  120. package/dist/hooks/cc-stop-prompt.d.ts +42 -0
  121. package/dist/hooks/cc-stop-prompt.js +43 -1
  122. package/dist/hooks/hook-llm.d.ts +15 -0
  123. package/dist/hooks/hook-llm.js +47 -0
  124. package/dist/hooks/hook-runner.d.ts +89 -0
  125. package/dist/hooks/hook-runner.js +327 -18
  126. package/dist/http/idempotency.d.ts +31 -0
  127. package/dist/http/idempotency.js +34 -0
  128. package/dist/http/principal-gate.d.ts +17 -0
  129. package/dist/http/principal-gate.js +35 -3
  130. package/dist/http/route-ctx.d.ts +36 -0
  131. package/dist/http/routes/approvals-assistant.d.ts +25 -0
  132. package/dist/http/routes/approvals-assistant.js +253 -23
  133. package/dist/http/routes/attachments.js +22 -4
  134. package/dist/http/routes/capabilities.d.ts +7 -0
  135. package/dist/http/routes/capabilities.js +202 -1
  136. package/dist/http/routes/fleet.d.ts +6 -0
  137. package/dist/http/routes/fleet.js +94 -5
  138. package/dist/http/routes/images.d.ts +17 -0
  139. package/dist/http/routes/images.js +222 -14
  140. package/dist/http/routes/leader.d.ts +7 -0
  141. package/dist/http/routes/leader.js +10 -0
  142. package/dist/http/routes/memory-policy.d.ts +6 -0
  143. package/dist/http/routes/memory-policy.js +38 -6
  144. package/dist/http/routes/notify-wake.d.ts +10 -0
  145. package/dist/http/routes/notify-wake.js +19 -3
  146. package/dist/http/routes/observability.d.ts +6 -0
  147. package/dist/http/routes/observability.js +19 -2
  148. package/dist/http/routes/runs.d.ts +15 -0
  149. package/dist/http/routes/runs.js +464 -41
  150. package/dist/http/routes/session-sync.d.ts +15 -0
  151. package/dist/http/routes/session-sync.js +229 -19
  152. package/dist/http/routes/sessions-list.d.ts +6 -0
  153. package/dist/http/routes/sessions-list.js +35 -4
  154. package/dist/http/routes/sessions.d.ts +14 -0
  155. package/dist/http/routes/sessions.js +176 -31
  156. package/dist/http/routes/side-query.d.ts +8 -0
  157. package/dist/http/routes/side-query.js +26 -1
  158. package/dist/http/routes/tasks.d.ts +18 -0
  159. package/dist/http/routes/tasks.js +501 -38
  160. package/dist/http/routes/trace-usage.d.ts +10 -0
  161. package/dist/http/routes/trace-usage.js +66 -14
  162. package/dist/http/routes/workflows.d.ts +12 -0
  163. package/dist/http/routes/workflows.js +180 -19
  164. package/dist/http/run-meta.d.ts +11 -0
  165. package/dist/http/run-meta.js +6 -0
  166. package/dist/http/send.d.ts +19 -0
  167. package/dist/http/send.js +11 -0
  168. package/dist/http/server.d.ts +255 -0
  169. package/dist/http/server.js +921 -75
  170. package/dist/http/sse-log.d.ts +15 -0
  171. package/dist/http/sse-log.js +16 -0
  172. package/dist/http/tar.d.ts +9 -0
  173. package/dist/http/tar.js +21 -5
  174. package/dist/http/wire-gate.d.ts +9 -0
  175. package/dist/http/wire-gate.js +9 -0
  176. package/dist/http/wire-types.d.ts +187 -0
  177. package/dist/http/workspace-content.d.ts +8 -0
  178. package/dist/http/workspace-content.js +10 -0
  179. package/dist/images/bake-validate.d.ts +63 -0
  180. package/dist/images/bake-validate.js +71 -1
  181. package/dist/images/manifest.d.ts +19 -0
  182. package/dist/images/manifest.js +6 -1
  183. package/dist/index.d.ts +1 -5
  184. package/dist/index.js +10 -5
  185. package/dist/key-resolver.d.ts +23 -0
  186. package/dist/key-resolver.js +27 -2
  187. package/dist/leader/diffout.d.ts +23 -0
  188. package/dist/leader/diffout.js +8 -1
  189. package/dist/leader/diffup.d.ts +32 -0
  190. package/dist/leader/diffup.js +43 -0
  191. package/dist/leader/endpoint.d.ts +20 -0
  192. package/dist/leader/endpoint.js +25 -2
  193. package/dist/leader/fanout.d.ts +60 -0
  194. package/dist/leader/fanout.js +29 -3
  195. package/dist/leader/grader-env-factory.d.ts +74 -0
  196. package/dist/leader/grader-env-factory.js +62 -3
  197. package/dist/leader/leader.d.ts +97 -0
  198. package/dist/leader/leader.js +151 -4
  199. package/dist/leader/merge.d.ts +94 -0
  200. package/dist/leader/merge.js +112 -9
  201. package/dist/leader/planner.d.ts +52 -0
  202. package/dist/leader/planner.js +47 -3
  203. package/dist/leader/repair-oracle.d.ts +61 -0
  204. package/dist/leader/repair-oracle.js +59 -1
  205. package/dist/leader/repair-wire.d.ts +78 -0
  206. package/dist/leader/repair-wire.js +79 -2
  207. package/dist/leader/wire.d.ts +79 -0
  208. package/dist/leader/wire.js +255 -8
  209. package/dist/lsp/e2b-bridge.d.ts +16 -0
  210. package/dist/lsp/e2b-bridge.js +32 -4
  211. package/dist/lsp/e2b-manager.d.ts +10 -0
  212. package/dist/lsp/e2b-manager.js +18 -2
  213. package/dist/lsp/lsp-frames.d.ts +12 -0
  214. package/dist/lsp/lsp-frames.js +12 -0
  215. package/dist/lsp/manager.d.ts +12 -0
  216. package/dist/lsp/manager.js +35 -2
  217. package/dist/lsp/types.d.ts +10 -0
  218. package/dist/lsp/ws-transport.d.ts +12 -0
  219. package/dist/lsp/ws-transport.js +53 -5
  220. package/dist/lsp-evict.d.ts +14 -0
  221. package/dist/lsp-evict.js +15 -1
  222. package/dist/main.js +2132 -96
  223. package/dist/memory-export.d.ts +1 -0
  224. package/dist/memory-export.js +4 -0
  225. package/dist/memory-scope.d.ts +49 -0
  226. package/dist/memory-scope.js +87 -4
  227. package/dist/memory-sync-client.d.ts +24 -0
  228. package/dist/memory-sync-client.js +41 -2
  229. package/dist/memory-sync.d.ts +23 -0
  230. package/dist/memory-sync.js +70 -1
  231. package/dist/model-select.d.ts +39 -0
  232. package/dist/model-select.js +42 -1
  233. package/dist/observability/cost-quota.d.ts +20 -0
  234. package/dist/observability/cost-quota.js +4 -0
  235. package/dist/observability/cost-taxonomy.d.ts +45 -0
  236. package/dist/observability/cost-taxonomy.js +34 -0
  237. package/dist/observability/logger.d.ts +1 -0
  238. package/dist/observability/logger.js +6 -0
  239. package/dist/observability/metrics.d.ts +13 -0
  240. package/dist/observability/metrics.js +63 -0
  241. package/dist/observability/otel-exporter.d.ts +9 -0
  242. package/dist/observability/otel-exporter.js +11 -3
  243. package/dist/observability/principal-context.d.ts +9 -0
  244. package/dist/observability/principal-context.js +9 -0
  245. package/dist/observability/prompt-manifest.d.ts +37 -0
  246. package/dist/observability/prompt-manifest.js +35 -1
  247. package/dist/observability/rate-limit.d.ts +10 -0
  248. package/dist/observability/rate-limit.js +1 -0
  249. package/dist/observability/tool-trace.d.ts +24 -0
  250. package/dist/observability/tool-trace.js +41 -1
  251. package/dist/orchestration/hardened-vm-runner.d.ts +107 -0
  252. package/dist/orchestration/hardened-vm-runner.js +119 -4
  253. package/dist/orchestration/hardened-vm-worker-runner.d.ts +14 -0
  254. package/dist/orchestration/hardened-vm-worker-runner.js +26 -1
  255. package/dist/orchestration/hardened-vm-worker.js +27 -0
  256. package/dist/orchestration/subagent-steer.d.ts +44 -0
  257. package/dist/orchestration/subagent-steer.js +45 -1
  258. package/dist/orchestration/workflow-agent-steer.d.ts +54 -0
  259. package/dist/orchestration/workflow-agent-steer.js +80 -1
  260. package/dist/orchestration/workflow-completion-inbox.d.ts +189 -2
  261. package/dist/orchestration/workflow-completion-inbox.js +283 -32
  262. package/dist/orchestration/workflow-notify-journal.d.ts +132 -1
  263. package/dist/orchestration/workflow-notify-journal.js +187 -13
  264. package/dist/parked-decide.d.ts +61 -0
  265. package/dist/parked-decide.js +58 -1
  266. package/dist/per-task-image.d.ts +54 -0
  267. package/dist/per-task-image.js +41 -0
  268. package/dist/plan-cache-probe.d.ts +35 -0
  269. package/dist/plan-cache-probe.js +18 -3
  270. package/dist/plugins/approval-exemption-store.d.ts +7 -0
  271. package/dist/plugins/approval-exemption-store.js +28 -2
  272. package/dist/plugins/approval-store-sql.d.ts +64 -0
  273. package/dist/plugins/approval-store-sql.js +32 -0
  274. package/dist/plugins/background-agent-store-sql.d.ts +55 -0
  275. package/dist/plugins/background-agent-store-sql.js +91 -4
  276. package/dist/plugins/background-shell-support.d.ts +98 -0
  277. package/dist/plugins/background-shell-support.js +117 -13
  278. package/dist/plugins/blob-backend.d.ts +104 -0
  279. package/dist/plugins/blob-backend.js +163 -6
  280. package/dist/plugins/breaker-state-sql.d.ts +65 -1
  281. package/dist/plugins/breaker-state-sql.js +46 -8
  282. package/dist/plugins/caching-session-store.d.ts +42 -0
  283. package/dist/plugins/caching-session-store.js +63 -3
  284. package/dist/plugins/checkpoint-store-sql.d.ts +179 -0
  285. package/dist/plugins/checkpoint-store-sql.js +239 -11
  286. package/dist/plugins/file-outcome-sink.d.ts +4 -0
  287. package/dist/plugins/file-outcome-sink.js +9 -0
  288. package/dist/plugins/file-resume-anchor-store.d.ts +20 -0
  289. package/dist/plugins/file-resume-anchor-store.js +43 -4
  290. package/dist/plugins/file-run-store.d.ts +90 -0
  291. package/dist/plugins/file-run-store.js +145 -13
  292. package/dist/plugins/file-snapshot-store-sql.d.ts +133 -1
  293. package/dist/plugins/file-snapshot-store-sql.js +178 -10
  294. package/dist/plugins/file-workflow-journal-store.d.ts +10 -0
  295. package/dist/plugins/file-workflow-journal-store.js +10 -0
  296. package/dist/plugins/fork-routing-session-store.d.ts +47 -0
  297. package/dist/plugins/fork-routing-session-store.js +51 -3
  298. package/dist/plugins/host-platform.d.ts +69 -0
  299. package/dist/plugins/host-platform.js +81 -2
  300. package/dist/plugins/image-bake-store-sql.d.ts +136 -1
  301. package/dist/plugins/image-bake-store-sql.js +241 -7
  302. package/dist/plugins/image-index-sql.d.ts +46 -0
  303. package/dist/plugins/image-index-sql.js +113 -4
  304. package/dist/plugins/k8s-bg-scripts.d.ts +34 -0
  305. package/dist/plugins/k8s-bg-scripts.js +88 -4
  306. package/dist/plugins/k8s-exec-protocol.d.ts +33 -0
  307. package/dist/plugins/k8s-exec-protocol.js +41 -0
  308. package/dist/plugins/local-checkpoint-store.d.ts +40 -0
  309. package/dist/plugins/local-checkpoint-store.js +86 -7
  310. package/dist/plugins/local-session-store.d.ts +157 -0
  311. package/dist/plugins/local-session-store.js +289 -34
  312. package/dist/plugins/local-task-attachment-store.js +14 -2
  313. package/dist/plugins/mailbox-store-sql.d.ts +38 -0
  314. package/dist/plugins/mailbox-store-sql.js +39 -6
  315. package/dist/plugins/memory-engine-pg.d.ts +30 -0
  316. package/dist/plugins/memory-engine-pg.js +122 -4
  317. package/dist/plugins/memory-engine-tidb.d.ts +26 -0
  318. package/dist/plugins/memory-engine-tidb.js +115 -5
  319. package/dist/plugins/memory-engine-vector-util.d.ts +5 -0
  320. package/dist/plugins/memory-engine-vector-util.js +5 -0
  321. package/dist/plugins/memory-resume-anchor-store.d.ts +8 -0
  322. package/dist/plugins/memory-resume-anchor-store.js +17 -0
  323. package/dist/plugins/memory-run-store.d.ts +63 -0
  324. package/dist/plugins/memory-run-store.js +73 -8
  325. package/dist/plugins/memory-session-policy-store.d.ts +17 -0
  326. package/dist/plugins/memory-session-policy-store.js +17 -0
  327. package/dist/plugins/memory-sync-store-pg.d.ts +42 -0
  328. package/dist/plugins/memory-sync-store-pg.js +45 -2
  329. package/dist/plugins/memory-sync-store-tidb.d.ts +9 -0
  330. package/dist/plugins/memory-sync-store-tidb.js +31 -1
  331. package/dist/plugins/outcome-ledger-sql.d.ts +63 -0
  332. package/dist/plugins/outcome-ledger-sql.js +97 -3
  333. package/dist/plugins/pg-approval-store.d.ts +7 -0
  334. package/dist/plugins/pg-approval-store.js +7 -0
  335. package/dist/plugins/pg-breaker-state.d.ts +6 -0
  336. package/dist/plugins/pg-breaker-state.js +6 -0
  337. package/dist/plugins/pg-checkpoint-store.d.ts +8 -0
  338. package/dist/plugins/pg-checkpoint-store.js +8 -0
  339. package/dist/plugins/pg-cost-quota.d.ts +17 -0
  340. package/dist/plugins/pg-cost-quota.js +6 -0
  341. package/dist/plugins/pg-file-snapshot-store.d.ts +6 -0
  342. package/dist/plugins/pg-file-snapshot-store.js +6 -0
  343. package/dist/plugins/pg-image-bake.d.ts +9 -0
  344. package/dist/plugins/pg-image-bake.js +9 -0
  345. package/dist/plugins/pg-image-index.d.ts +9 -0
  346. package/dist/plugins/pg-image-index.js +9 -0
  347. package/dist/plugins/pg-outcome-ledger.d.ts +9 -0
  348. package/dist/plugins/pg-outcome-ledger.js +9 -0
  349. package/dist/plugins/pg-pool.d.ts +50 -0
  350. package/dist/plugins/pg-pool.js +78 -0
  351. package/dist/plugins/pg-query.d.ts +11 -0
  352. package/dist/plugins/pg-rate-limiter.d.ts +23 -0
  353. package/dist/plugins/pg-rate-limiter.js +12 -2
  354. package/dist/plugins/pg-resume-anchor-store.d.ts +5 -0
  355. package/dist/plugins/pg-resume-anchor-store.js +5 -0
  356. package/dist/plugins/pg-run-store.d.ts +7 -0
  357. package/dist/plugins/pg-run-store.js +7 -0
  358. package/dist/plugins/pg-safe-json.d.ts +13 -0
  359. package/dist/plugins/pg-safe-json.js +40 -4
  360. package/dist/plugins/pg-session-policy-store.d.ts +5 -0
  361. package/dist/plugins/pg-session-policy-store.js +5 -0
  362. package/dist/plugins/pg-session-storage.d.ts +72 -0
  363. package/dist/plugins/pg-session-storage.js +152 -20
  364. package/dist/plugins/pg-session-store.d.ts +10 -0
  365. package/dist/plugins/pg-session-store.js +10 -0
  366. package/dist/plugins/pg-tool-result-store.d.ts +7 -0
  367. package/dist/plugins/pg-tool-result-store.js +7 -0
  368. package/dist/plugins/pg-workflow-journal-store.d.ts +7 -0
  369. package/dist/plugins/pg-workflow-journal-store.js +7 -0
  370. package/dist/plugins/pg-workflow-run-store.d.ts +7 -0
  371. package/dist/plugins/pg-workflow-run-store.js +7 -0
  372. package/dist/plugins/posix-shell-fs.d.ts +3 -0
  373. package/dist/plugins/posix-shell-fs.js +31 -1
  374. package/dist/plugins/remote-env-adb.d.ts +25 -0
  375. package/dist/plugins/remote-env-adb.js +95 -12
  376. package/dist/plugins/remote-env-e2b.d.ts +146 -0
  377. package/dist/plugins/remote-env-e2b.js +371 -40
  378. package/dist/plugins/remote-env-file-error.d.ts +34 -0
  379. package/dist/plugins/remote-env-file-error.js +37 -0
  380. package/dist/plugins/remote-env-host.d.ts +151 -0
  381. package/dist/plugins/remote-env-host.js +468 -70
  382. package/dist/plugins/remote-env-k8s.d.ts +135 -0
  383. package/dist/plugins/remote-env-k8s.js +318 -32
  384. package/dist/plugins/remote-env-local-docker.d.ts +65 -0
  385. package/dist/plugins/remote-env-local-docker.js +149 -24
  386. package/dist/plugins/remote-env-ssh.d.ts +35 -0
  387. package/dist/plugins/remote-env-ssh.js +117 -20
  388. package/dist/plugins/remote-scratchpad.d.ts +38 -0
  389. package/dist/plugins/remote-scratchpad.js +32 -2
  390. package/dist/plugins/remote-shell.d.ts +22 -0
  391. package/dist/plugins/remote-shell.js +27 -1
  392. package/dist/plugins/resume-anchor-store-sql.d.ts +28 -0
  393. package/dist/plugins/resume-anchor-store-sql.js +11 -0
  394. package/dist/plugins/roster-store-sql.d.ts +27 -0
  395. package/dist/plugins/roster-store-sql.js +54 -0
  396. package/dist/plugins/run-store-sql.d.ts +194 -0
  397. package/dist/plugins/run-store-sql.js +210 -9
  398. package/dist/plugins/s3-presign.d.ts +20 -0
  399. package/dist/plugins/s3-presign.js +49 -2
  400. package/dist/plugins/scheduler-support.d.ts +12 -0
  401. package/dist/plugins/scheduler-support.js +75 -3
  402. package/dist/plugins/send-file-ledger.d.ts +15 -0
  403. package/dist/plugins/send-file-ledger.js +53 -4
  404. package/dist/plugins/send-user-file.d.ts +77 -0
  405. package/dist/plugins/send-user-file.js +92 -4
  406. package/dist/plugins/session-policy-store-sql.d.ts +15 -0
  407. package/dist/plugins/session-policy-store-sql.js +75 -3
  408. package/dist/plugins/session-store.d.ts +45 -0
  409. package/dist/plugins/session-store.js +51 -0
  410. package/dist/plugins/sql-driver.d.ts +77 -0
  411. package/dist/plugins/sql-driver.js +4 -0
  412. package/dist/plugins/sql-escape.d.ts +8 -0
  413. package/dist/plugins/sql-escape.js +8 -0
  414. package/dist/plugins/sql-row-helpers.d.ts +25 -0
  415. package/dist/plugins/sql-row-helpers.js +25 -0
  416. package/dist/plugins/store-backend.d.ts +103 -0
  417. package/dist/plugins/store-backend.js +120 -27
  418. package/dist/plugins/store-contracts.d.ts +62 -0
  419. package/dist/plugins/store-contracts.js +3 -0
  420. package/dist/plugins/task-attachment-store.d.ts +60 -0
  421. package/dist/plugins/task-attachment-store.js +49 -3
  422. package/dist/plugins/task-list-store-sql.d.ts +25 -0
  423. package/dist/plugins/task-list-store-sql.js +21 -0
  424. package/dist/plugins/tidb-approval-store.d.ts +6 -0
  425. package/dist/plugins/tidb-approval-store.js +6 -0
  426. package/dist/plugins/tidb-breaker-state.d.ts +5 -0
  427. package/dist/plugins/tidb-breaker-state.js +5 -0
  428. package/dist/plugins/tidb-checkpoint-store.d.ts +7 -0
  429. package/dist/plugins/tidb-checkpoint-store.js +7 -0
  430. package/dist/plugins/tidb-cost-quota.d.ts +6 -0
  431. package/dist/plugins/tidb-cost-quota.js +1 -0
  432. package/dist/plugins/tidb-file-snapshot-store.d.ts +6 -0
  433. package/dist/plugins/tidb-file-snapshot-store.js +6 -0
  434. package/dist/plugins/tidb-image-bake.d.ts +8 -0
  435. package/dist/plugins/tidb-image-bake.js +8 -0
  436. package/dist/plugins/tidb-image-index.d.ts +8 -0
  437. package/dist/plugins/tidb-image-index.js +8 -0
  438. package/dist/plugins/tidb-outcome-ledger.d.ts +10 -0
  439. package/dist/plugins/tidb-outcome-ledger.js +10 -0
  440. package/dist/plugins/tidb-pool.d.ts +56 -0
  441. package/dist/plugins/tidb-pool.js +181 -1
  442. package/dist/plugins/tidb-rate-limiter.d.ts +11 -0
  443. package/dist/plugins/tidb-rate-limiter.js +5 -2
  444. package/dist/plugins/tidb-resume-anchor-store.d.ts +5 -0
  445. package/dist/plugins/tidb-resume-anchor-store.js +5 -0
  446. package/dist/plugins/tidb-run-store.d.ts +7 -0
  447. package/dist/plugins/tidb-run-store.js +7 -0
  448. package/dist/plugins/tidb-session-policy-store.d.ts +5 -0
  449. package/dist/plugins/tidb-session-policy-store.js +5 -0
  450. package/dist/plugins/tidb-session-storage.d.ts +43 -0
  451. package/dist/plugins/tidb-session-storage.js +62 -3
  452. package/dist/plugins/tidb-session-store.d.ts +142 -0
  453. package/dist/plugins/tidb-session-store.js +264 -16
  454. package/dist/plugins/tidb-tool-result-store.d.ts +5 -0
  455. package/dist/plugins/tidb-tool-result-store.js +7 -0
  456. package/dist/plugins/tidb-workflow-journal-store.d.ts +7 -0
  457. package/dist/plugins/tidb-workflow-journal-store.js +7 -0
  458. package/dist/plugins/tidb-workflow-run-store.d.ts +8 -0
  459. package/dist/plugins/tidb-workflow-run-store.js +8 -0
  460. package/dist/plugins/tool-result-store-sql.d.ts +58 -0
  461. package/dist/plugins/tool-result-store-sql.js +38 -2
  462. package/dist/plugins/web-search.d.ts +35 -0
  463. package/dist/plugins/web-search.js +45 -9
  464. package/dist/plugins/workflow-journal-limits.d.ts +10 -0
  465. package/dist/plugins/workflow-journal-limits.js +10 -0
  466. package/dist/plugins/workflow-journal-store-sql.d.ts +78 -0
  467. package/dist/plugins/workflow-journal-store-sql.js +59 -3
  468. package/dist/plugins/workflow-run-store-sql.d.ts +84 -0
  469. package/dist/plugins/workflow-run-store-sql.js +84 -6
  470. package/dist/plugins/worktree-isolation.d.ts +45 -0
  471. package/dist/plugins/worktree-isolation.js +105 -6
  472. package/dist/plugins/write-behind-counter.d.ts +56 -1
  473. package/dist/plugins/write-behind-counter.js +53 -14
  474. package/dist/principal-jwt.d.ts +24 -0
  475. package/dist/principal-jwt.js +60 -5
  476. package/dist/project-memory.d.ts +22 -0
  477. package/dist/project-memory.js +144 -15
  478. package/dist/prompts-domain-validate.d.ts +28 -0
  479. package/dist/prompts-domain-validate.js +42 -1
  480. package/dist/question.d.ts +33 -0
  481. package/dist/question.js +69 -2
  482. package/dist/resource-suspend.d.ts +21 -0
  483. package/dist/resource-suspend.js +16 -0
  484. package/dist/router/route-orchestration.d.ts +78 -0
  485. package/dist/router/route-orchestration.js +77 -0
  486. package/dist/run-local.d.ts +32 -0
  487. package/dist/run-local.js +172 -8
  488. package/dist/runs.d.ts +189 -4
  489. package/dist/runs.js +465 -24
  490. package/dist/runtime-caps-resolver.d.ts +106 -0
  491. package/dist/runtime-caps-resolver.js +89 -4
  492. package/dist/runtime-governance.d.ts +84 -0
  493. package/dist/runtime-governance.js +127 -1
  494. package/dist/sandbox-pkg-source.d.ts +23 -0
  495. package/dist/sandbox-pkg-source.js +37 -0
  496. package/dist/sealed-key.d.ts +54 -0
  497. package/dist/sealed-key.js +68 -3
  498. package/dist/security.d.ts +108 -0
  499. package/dist/security.js +145 -6
  500. package/dist/sema-registry.d.ts +21 -0
  501. package/dist/sema-registry.js +21 -0
  502. package/dist/session-leaf-bus.d.ts +31 -0
  503. package/dist/session-leaf-bus.js +32 -0
  504. package/dist/session-sync-content.d.ts +46 -0
  505. package/dist/session-sync-content.js +47 -1
  506. package/dist/session-sync-kernel.d.ts +89 -0
  507. package/dist/session-sync-kernel.js +48 -3
  508. package/dist/session-sync.d.ts +125 -0
  509. package/dist/session-sync.js +181 -8
  510. package/dist/session-titler.d.ts +26 -0
  511. package/dist/session-titler.js +41 -7
  512. package/dist/session-watch.d.ts +53 -1
  513. package/dist/session-watch.js +73 -9
  514. package/dist/sighup-idle.d.ts +30 -0
  515. package/dist/sighup-idle.js +12 -1
  516. package/dist/spec-fields.d.ts +91 -3
  517. package/dist/spec-fields.js +116 -4
  518. package/dist/task-cwd.d.ts +50 -0
  519. package/dist/task-cwd.js +68 -1
  520. package/dist/task-mcp.d.ts +47 -0
  521. package/dist/task-mcp.js +29 -0
  522. package/dist/task-settings.d.ts +175 -0
  523. package/dist/task-settings.js +226 -11
  524. package/dist/task-workflow.d.ts +67 -0
  525. package/dist/task-workflow.js +75 -8
  526. package/dist/tool-approval.d.ts +91 -0
  527. package/dist/tool-approval.js +215 -0
  528. package/dist/trace/artifacts.d.ts +9 -0
  529. package/dist/trace/artifacts.js +30 -5
  530. package/dist/trace/core-keyset-guard.d.ts +18 -0
  531. package/dist/trace/ledger-sink.d.ts +43 -0
  532. package/dist/trace/ledger-sink.js +64 -5
  533. package/dist/trace/project.d.ts +133 -0
  534. package/dist/trace/project.js +189 -2
  535. package/dist/trace/redact.d.ts +19 -0
  536. package/dist/trace/redact.js +66 -9
  537. package/dist/usage-analytics.d.ts +26 -0
  538. package/dist/usage-analytics.js +26 -3
  539. package/package.json +1 -1
@@ -7,7 +7,17 @@ const DEFAULTS = {
7
7
  concurrency: 12,
8
8
  maxHeapMb: 128,
9
9
  };
10
+ /** Own keys that carry a prototype-tampering gadget across the JSON membrane. */
10
11
  const PROTO_TAMPER_KEYS = ["__proto__", "constructor", "prototype"];
12
+ /**
13
+ * Recursively delete own `__proto__` / `constructor` / `prototype` keys from JSON-parsed data. Closes the
14
+ * tampering gadget a script passes across the membrane: `Reflect.defineProperty(o, "__proto__",
15
+ * {enumerable:true, value:{...}})` (Reflect is reachable; `codeGeneration:{strings:false}` does NOT block it)
16
+ * makes `__proto__` an OWN ENUMERABLE key, which in-context `JSON.stringify` emits and host `JSON.parse`
17
+ * re-materializes as a real own data property — feeding a host fn (or the returned result) a crafted
18
+ * prototype a downstream deep-merge / key-assignment sink splats onto the HOST realm's `Object.prototype`
19
+ * (process-wide, cross-tenant). Caught by the s8-sandbox-robustness marshal-bridge lens (core's battery + the
20
+ * result-path probes both miss it). Apply ONLY to JSON.parse output (plain, acyclic, getter-free). */
11
21
  function scrubProtoDeep(value) {
12
22
  if (value === null || typeof value !== "object")
13
23
  return;
@@ -24,8 +34,22 @@ function scrubProtoDeep(value) {
24
34
  scrubProtoDeep(value[key]);
25
35
  }
26
36
  }
37
+ /**
38
+ * [1818]§二邻位 — script-realm rejection guard. The vm context shares the HOST isolate's unhandledRejection
39
+ * machinery, and Node's default posture is CRASH: an untrusted script's un-awaited rejection (a bare
40
+ * `(async () => { throw ... })()`, an un-awaited `agent()`/`phase()` whose body fails) would take the whole
41
+ * server process down — a resource/availability hole, not a data-boundary one (clay's live incident hit the
42
+ * sibling shape that HANGS instead of rejecting, which is the only reason it didn't crash). There is no
43
+ * per-context hook in Node, so the guard is a process listener that tells realms apart by intrinsic:
44
+ * a promise minted INSIDE the context fails `instanceof` against the HOST `Promise`. Context-realm rejections
45
+ * are contained (logged, never fatal); host-realm rejections keep Node's fail-loud default (rethrown as an
46
+ * uncaughtException) UNLESS the deployment installed its own handler — then we defer to it entirely.
47
+ * Installed lazily on the first hardened run: only processes that actually execute untrusted scripts change
48
+ * their global posture, and only then is the hazard live. Idempotent.
49
+ */
27
50
  let rejectionGuardInstalled = false;
28
51
  let onScriptRealmRejection;
52
+ /** Optional observability hook for contained script-realm rejections (e.g. wire to the deployment logger). */
29
53
  export function setScriptRealmRejectionObserver(observer) {
30
54
  onScriptRealmRejection = observer;
31
55
  }
@@ -35,24 +59,32 @@ export function installScriptRealmRejectionGuard() {
35
59
  rejectionGuardInstalled = true;
36
60
  process.on("unhandledRejection", (reason, promise) => {
37
61
  if (!(promise instanceof Promise)) {
62
+ // cross-realm ⇒ minted in a vm context ⇒ an un-awaited rejection inside an untrusted script. Contain.
38
63
  const summary = reason instanceof Error ? `${reason.name}: ${reason.message}` : String(reason);
39
64
  try {
40
65
  (onScriptRealmRejection ?? ((s) => console.warn(`hardened-vm: un-awaited script rejection contained: ${s}`)))(summary);
41
66
  }
42
67
  catch {
68
+ /* the guard must never become a crash source itself */
43
69
  }
44
70
  return;
45
71
  }
72
+ // Host-realm: preserve fail-loud unless another handler exists (a deployment that installed one chose
73
+ // its own posture — a rethrow here would override that choice).
46
74
  if (process.listenerCount("unhandledRejection") > 1)
47
75
  return;
48
76
  throw reason;
49
77
  });
50
78
  }
79
+ /** Host-side parse that strips the tampering gadget from data crossing INTO a host fn. */
51
80
  function sterilizingParse(json) {
52
81
  const parsed = JSON.parse(json);
53
82
  scrubProtoDeep(parsed);
54
83
  return parsed;
55
84
  }
85
+ /** Marshal a (possibly live cross-realm) script value to PLAIN, sterilized host data: JSON round-trips it
86
+ * (firing in-context getters/toJSON harmlessly in-context → plain data; drops functions; no live context
87
+ * object with traps reaches the host) then strips the tampering gadget. Throws on a non-serializable result. */
56
88
  function marshalOut(value) {
57
89
  let json;
58
90
  try {
@@ -63,14 +95,21 @@ function marshalOut(value) {
63
95
  }
64
96
  return sterilizingParse(json === undefined ? "null" : json);
65
97
  }
98
+ /**
99
+ * The shared hardened-vm core. Builds the membrane, lets `buildGlobals` construct the context-local globals,
100
+ * runs the untrusted `body` as an async function under the caps, and returns the MARSHALLED (plain, sterilized)
101
+ * result. Both public surfaces delegate here.
102
+ */
66
103
  async function runHardened(opts) {
67
104
  const cfg = { ...DEFAULTS, ...opts.limits };
68
105
  const { signal } = opts;
69
106
  if (signal?.aborted)
70
107
  throw new WorkflowScriptError("hardened-vm aborted before start");
71
108
  installScriptRealmRejectionGuard();
109
+ // 1. hardened context
72
110
  const sandbox = { __proto__: null };
73
111
  const ctx = vm.createContext(sandbox, { codeGeneration: { strings: false, wasm: false } });
112
+ // 2. determinism guard (resume-safety; matches CC): nondeterministic clocks/RNG throw in-context.
74
113
  vm.runInContext(`(() => {
75
114
  const block = (l) => { throw new Error(l + " is unavailable in workflow scripts (nondeterministic — breaks resume). Pass timestamps via args; vary by index for randomness."); };
76
115
  Date.now = () => block("Date.now()");
@@ -87,17 +126,30 @@ async function runHardened(opts) {
87
126
  globalThis[n] = () => noTimer(n + "()");
88
127
  }
89
128
  })()`, ctx);
129
+ // 3. membrane helpers (factory bodies COMPILED IN-CONTEXT → context-rooted prototypes)
90
130
  const ctxJsonParse = vm.runInContext("(s => JSON.parse(s))", ctx);
91
131
  const dataIn = (v) => {
132
+ // sterilize the gadget host-side BEFORE it re-materializes in-context (a __proto__ own key would otherwise
133
+ // pollute the CONTEXT's Object.prototype — sandboxed, but cheap to close).
92
134
  const clean = JSON.parse(JSON.stringify(v === undefined ? null : v));
93
135
  scrubProtoDeep(clean);
94
136
  return ctxJsonParse(JSON.stringify(clean));
95
137
  };
138
+ // a {ok,v} | {ok:false,err,name} JSON round-trip; the IN-CONTEXT wrapper throws a CONTEXT-LOCAL Error on
139
+ // failure so a host error never reaches the script (beyond-battery fix).
96
140
  const mkBridge = vm.runInContext(`(hostCall => async function (...a) {
97
141
  const r = JSON.parse(await hostCall(JSON.stringify(a)));
98
142
  if (r.ok) return r.v;
99
143
  const e = new Error(r.err); e.name = r.name || "Error"; throw e;
100
144
  })`, ctx);
145
+ // [1818]§二 — quiescence deadlock detector. Inside the membrane the ONLY way a suspended script can ever be
146
+ // resumed is a bridged host call settling: timers are blocked, there is no I/O, and every other await is a
147
+ // microtask chain that drains on its own. So "zero bridged calls in flight + the microtask AND macrotask
148
+ // queues have both drained + scriptPromise still pending" is not a heuristic — it PROVES the script can never
149
+ // progress again (clay's live shape: an un-awaited `const r = phase(...)` whose body returns `r` → a promise
150
+ // cycle that neither rejects nor resolves; pre-fix it burned the full totalTimeoutMs blind and then blamed
151
+ // the timeout). setImmediate (a macrotask) is the drain fence: any continuation the settling host call woke
152
+ // runs first, and a continuation that starts a new bridged call increments the counter synchronously.
101
153
  let inFlightHostCalls = 0;
102
154
  let scriptSettled = false;
103
155
  let fireDeadlock;
@@ -113,6 +165,8 @@ async function runHardened(opts) {
113
165
  const bridge = (hostFn) => mkBridge(async (argsJson) => {
114
166
  inFlightHostCalls++;
115
167
  try {
168
+ // sterilizingParse (not bare JSON.parse): strip a slipped-in __proto__/constructor/prototype gadget
169
+ // BEFORE the args reach the host fn — the dangerous (script→host) direction.
116
170
  const out = await hostFn(...sterilizingParse(argsJson));
117
171
  return JSON.stringify({ ok: true, v: out ?? null });
118
172
  }
@@ -129,6 +183,7 @@ async function runHardened(opts) {
129
183
  }
130
184
  });
131
185
  const membrane = { bridge, dataIn, ctxEval: (src) => vm.runInContext(src, ctx) };
186
+ // 4. expose the surface's context-local globals + run the body under the sync vm timeout
132
187
  for (const [name, value] of Object.entries(opts.buildGlobals(membrane)))
133
188
  sandbox[name] = value;
134
189
  let invocation;
@@ -138,9 +193,14 @@ async function runHardened(opts) {
138
193
  catch (err) {
139
194
  throw new WorkflowScriptError(`script failed to compile: ${err instanceof Error ? err.message : String(err)}`);
140
195
  }
196
+ // `timeout` bounds the SYNCHRONOUS run up to the first await (a sync infinite loop is killed here); an async
197
+ // loop is bounded by the wall-clock race below.
141
198
  const scriptPromise = invocation.runInContext(ctx, { timeout: cfg.syncTimeoutMs });
142
199
  scriptPromise.then(() => (scriptSettled = true), () => (scriptSettled = true));
200
+ // Initial probe: a script that suspends without EVER touching the bridge (`await new Promise(() => {})`)
201
+ // never decrements the counter, so the settle-side probe alone would miss it.
143
202
  probeQuiescence();
203
+ // 5. wall-clock + external-abort + quiescence-deadlock race
144
204
  let timer;
145
205
  let onAbort;
146
206
  try {
@@ -153,14 +213,24 @@ async function runHardened(opts) {
153
213
  onAbort = () => reject(new WorkflowScriptError("hardened-vm aborted"));
154
214
  signal.addEventListener("abort", onAbort, { once: true });
155
215
  }
216
+ // Diagnosis-first message: it reaches the model via failureSummary (bounded), so the self-correction
217
+ // hint has to be up front.
156
218
  fireDeadlock = () => reject(new WorkflowScriptError("script deadlocked: no host call (agent/phase) in flight and nothing can resume the script — usually an un-awaited promise cycle, e.g. `const r = phase(...)` without await whose body reads `r`."));
157
219
  }),
158
220
  ]);
221
+ // hand the host PLAIN sterilized data, never a live cross-realm object (getter/toJSON/then traps) and
222
+ // never a slipped-in __proto__ (return-path tampering).
159
223
  return marshalOut(result);
160
224
  }
161
225
  catch (err) {
226
+ // Live dogfood catch 2026-07-03 ( `setTimeout` blew up as a bare "workflow failed"): a RUNTIME script-realm
227
+ // rejection (ReferenceError, an uncaught script throw, a bridged agent() error the script didn't catch) used
228
+ // to propagate UN-typed — `failureSummary` only surfaces the message for `workflow.*`-coded errors, so the model/
229
+ // operator got zero diagnosis. Wrap it as `workflow.script_error`; core's failureSummary already bounds (200
230
+ // chars) + `redactHostLeaks`-redacts the message before it reaches the notification, so a bridged host error
231
+ // message is safe to carry here.
162
232
  if (err instanceof WorkflowScriptError)
163
- throw err;
233
+ throw err; // compile/timeout/abort/marshal — already typed
164
234
  throw new WorkflowScriptError(`script failed at runtime: ${err instanceof Error ? err.message : String(err)}`);
165
235
  }
166
236
  finally {
@@ -170,6 +240,23 @@ async function runHardened(opts) {
170
240
  signal.removeEventListener("abort", onAbort);
171
241
  }
172
242
  }
243
+ /**
244
+ * phases=[] ROOT CAUSE + fix — the SCOPED-PHASE GATE. The old in-context `phase` was pure sugar
245
+ * (log a "◆ phase" line, run the body) that NEVER called the host `primitives.phase`, so core recorded ZERO
246
+ * phase state: terminal rows had `phases: []` and agents carried no `phase` key (exactly the storage-row
247
+ * autopsy — agents fine, phase accounting gone). The membrane's JSON bridge cannot carry the script's BODY
248
+ * function across, so the bridge is split into three JSON-safe host calls, and the body stays IN-CONTEXT:
249
+ * - `mark(title)` → `primitives.phase(title, undefined)` (core's bare CC-marker path);
250
+ * - `enter(title)` → opens core's scoped phase with a DEFERRED body (`await gate`), returns a numeric token.
251
+ * core pushes the phase record + sets `currentPhase` SYNCHRONOUSLY before its first await, so by the time
252
+ * `enter` returns, `agent()` calls made inside the VM body land with the right phase label;
253
+ * - `exit(token, ok, err?)` → releases the gate (ok → core closes the phase `completed`; !ok → the deferred
254
+ * body throws and core closes it `failed`), then awaits core's settle so the close is persisted before the
255
+ * VM continues.
256
+ * A finalized-run `phase()` (core throws) surfaces on `enter` via the settled-race; an unknown/double `exit`
257
+ * is idempotent. The VM wrapper rethrows the body's own error — the gate's synthetic error never reaches the
258
+ * script.
259
+ */
173
260
  export function scopedPhaseGate(phase) {
174
261
  let seq = 0;
175
262
  const open = new Map();
@@ -182,7 +269,9 @@ export function scopedPhaseGate(phase) {
182
269
  if (!r.ok)
183
270
  throw new Error(r.err ?? "phase body failed");
184
271
  }));
185
- settled.catch(() => undefined);
272
+ settled.catch(() => undefined); // the VM path rethrows the body's own error — never an unhandled rejection here
273
+ // Surface an IMMEDIATE rejection (core's finalized guard rejects before its first await settles) while a
274
+ // healthy still-pending phase resolves the race via the resolved sentinel.
186
275
  await Promise.race([settled, Promise.resolve()]);
187
276
  const token = ++seq;
188
277
  open.set(token, { release, settled });
@@ -191,13 +280,19 @@ export function scopedPhaseGate(phase) {
191
280
  async exit(token, ok, err) {
192
281
  const o = open.get(token);
193
282
  if (!o)
194
- return;
283
+ return; // unknown/double exit — idempotent
195
284
  open.delete(token);
196
285
  o.release({ ok, ...(err !== undefined ? { err } : {}) });
197
- await o.settled.catch(() => undefined);
286
+ await o.settled.catch(() => undefined); // core closed + persisted the phase before the VM continues
198
287
  },
199
288
  };
200
289
  }
290
+ /** Build the orchestration context-local globals (agent/parallel/pipeline/phase/log/budget/args) from the
291
+ * host {@link WorkflowPrimitives}. agent/log/phase are host-bridged (phase via the three-verb
292
+ * {@link scopedPhaseGate} — the body function itself never crosses the membrane); parallel/pipeline are
293
+ * in-context sugar; budget is a frozen null-proto object over host accessors.
294
+ * Exported so the worker-isolated runner (hardened-vm-worker.ts) reuses the EXACT same surface with
295
+ * IPC-backed primitives — one definition of the orchestration globals, no drift. */
201
296
  export function orchestrationGlobals(primitives, scriptArgs, limits = {}) {
202
297
  const cfg = { ...DEFAULTS, ...limits };
203
298
  return (m) => {
@@ -222,6 +317,8 @@ export function orchestrationGlobals(primitives, scriptArgs, limits = {}) {
222
317
  await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
223
318
  return out;
224
319
  })`)(cfg.concurrency);
320
+ // Phase-gate fix: REAL phase bridging via the three-verb gate (see scopedPhaseGate) — the old in-context sugar
321
+ // never told core a phase existed (phases:[] / label-less agents in every hardened-runner deployment).
225
322
  const gate = scopedPhaseGate((t, b) => primitives.phase(t, b));
226
323
  const phaseMark = m.bridge(async (title) => void (await primitives.phase(String(title), undefined)));
227
324
  const phaseEnter = m.bridge(async (title) => gate.enter(String(title)));
@@ -235,6 +332,9 @@ export function orchestrationGlobals(primitives, scriptArgs, limits = {}) {
235
332
  return { agent, parallel, pipeline, phase, log, budget, args: m.dataIn(scriptArgs) };
236
333
  };
237
334
  }
335
+ /** Build the hardened-vm S8 `WorkflowScriptRunner` (TOC+TOB workflow orchestration). `safeForUntrustedScripts`
336
+ * is `true` — it has passed the conformance battery + robustness suite; the S8 gate mounts `run_workflow` only for
337
+ * a runner that asserts this. */
238
338
  export function createHardenedVmRunner(limits = {}) {
239
339
  const cfg = { ...DEFAULTS, ...limits };
240
340
  return {
@@ -247,10 +347,25 @@ export function createHardenedVmRunner(limits = {}) {
247
347
  ...(signal ? { signal } : {}),
248
348
  buildGlobals: orchestrationGlobals(primitives, scriptArgs, cfg),
249
349
  });
350
+ // `meta` is core-parsed (AST, not eval) but its values are LLM-controlled — sterilize it too.
250
351
  return { result, meta: marshalOut(meta) };
251
352
  },
252
353
  };
253
354
  }
355
+ /**
356
+ * General untrusted-code exec on the SAME hardened membrane — the TOC ephemeral execution sandbox. Runs
357
+ * `code` (an async function body that may `return`) with ONLY the safe built-ins plus whatever host
358
+ * capabilities the caller bridges via `bindings` (each MUST go through `m.bridge` / `m.dataIn` — never a raw
359
+ * host reference). Returns the marshalled (plain, sterilized) result. With no `bindings`, the code is pure
360
+ * computation (no host reach at all).
361
+ *
362
+ * @example
363
+ * const out = await runInHardenedVm({
364
+ * code: "return await fetchJson(args.url)",
365
+ * bindings: (m) => ({ fetchJson: m.bridge((u) => safeFetch(String(u))), args: m.dataIn({ url }) }),
366
+ * limits: { totalTimeoutMs: 5000 },
367
+ * });
368
+ */
254
369
  export async function runInHardenedVm(opts) {
255
370
  return runHardened({
256
371
  body: opts.code,
@@ -1,4 +1,18 @@
1
1
  import { type WorkflowScriptRunner } from "@sema-agent/core";
2
2
  import { type HardenedVmLimits } from "./hardened-vm-runner.js";
3
+ /**
4
+ * Worker-isolated S8 hard runner (design/98 v2). Runs the hardened-vm membrane inside a `worker_thread`, adding
5
+ * RESOURCE isolation on top of the in-process {@link import("./hardened-vm-runner.js").createHardenedVmRunner}'s
6
+ * BOUNDARY isolation:
7
+ * - HEAP cap (`resourceLimits.maxOldGenerationSizeMb`): a runaway-allocation script OOMs the WORKER (an `error`
8
+ * event), never the shared main process — closes the in-process resource-exhaustion gap.
9
+ * - `terminate()` on wall-clock timeout / abort: kills a MICROTASK-starvation loop (`while(true){await null}`)
10
+ * the in-process wall-clock race cannot pre-empt (it starves its own event loop).
11
+ * - credential isolation: the worker gets only the script + args; `agent`/`log` IPC to the main thread (which
12
+ * holds the real Runner/DB/keys); a (validated-impossible) boundary crossing lands in a credential-free isolate.
13
+ *
14
+ * `safeForUntrustedScripts: true` — the membrane it runs is the conformance-validated + robustness-tested one. Use
15
+ * this (over the in-process runner) for genuinely-untrusted multi-tenant scripts where resource isolation matters.
16
+ */
3
17
  export declare function createWorkerHardenedVmRunner(limits?: HardenedVmLimits): WorkflowScriptRunner;
4
18
  //# sourceMappingURL=hardened-vm-worker-runner.d.ts.map
@@ -2,9 +2,26 @@ import { Worker } from "node:worker_threads";
2
2
  import { WorkflowScriptError } from "@sema-agent/core";
3
3
  import { scopedPhaseGate } from "./hardened-vm-runner.js";
4
4
  const DEFAULTS = { totalTimeoutMs: 600_000, syncTimeoutMs: 5_000, maxAgents: 1000, concurrency: 12, maxHeapMb: 128 };
5
+ // Resolve the worker bootstrap next to this module. In a tsc build it's the sibling `.js`; under vitest/tsx
6
+ // (this module's URL ends in `.ts`) point at the `.ts` and register tsx as the worker's loader so the worker
7
+ // can import TypeScript. Prod (node dist/) never needs tsx.
5
8
  const IS_TS = import.meta.url.endsWith(".ts");
6
9
  const WORKER_URL = new URL(IS_TS ? "./hardened-vm-worker.ts" : "./hardened-vm-worker.js", import.meta.url);
7
10
  const WORKER_EXEC_ARGV = IS_TS ? ["--import", "tsx/esm"] : undefined;
11
+ /**
12
+ * Worker-isolated S8 hard runner (design/98 v2). Runs the hardened-vm membrane inside a `worker_thread`, adding
13
+ * RESOURCE isolation on top of the in-process {@link import("./hardened-vm-runner.js").createHardenedVmRunner}'s
14
+ * BOUNDARY isolation:
15
+ * - HEAP cap (`resourceLimits.maxOldGenerationSizeMb`): a runaway-allocation script OOMs the WORKER (an `error`
16
+ * event), never the shared main process — closes the in-process resource-exhaustion gap.
17
+ * - `terminate()` on wall-clock timeout / abort: kills a MICROTASK-starvation loop (`while(true){await null}`)
18
+ * the in-process wall-clock race cannot pre-empt (it starves its own event loop).
19
+ * - credential isolation: the worker gets only the script + args; `agent`/`log` IPC to the main thread (which
20
+ * holds the real Runner/DB/keys); a (validated-impossible) boundary crossing lands in a credential-free isolate.
21
+ *
22
+ * `safeForUntrustedScripts: true` — the membrane it runs is the conformance-validated + robustness-tested one. Use
23
+ * this (over the in-process runner) for genuinely-untrusted multi-tenant scripts where resource isolation matters.
24
+ */
8
25
  export function createWorkerHardenedVmRunner(limits = {}) {
9
26
  const cfg = { ...DEFAULTS, ...limits };
10
27
  return {
@@ -14,6 +31,8 @@ export function createWorkerHardenedVmRunner(limits = {}) {
14
31
  return Promise.reject(new WorkflowScriptError("worker hardened-vm aborted before start"));
15
32
  return new Promise((resolve, reject) => {
16
33
  const worker = new Worker(WORKER_URL, {
34
+ // budgetSpent seeds the worker's cache with the CURRENT spend (core 1.238 wiring battery caught the
35
+ // cold-start gap: a cache seeded 0 made remaining() read total until the first agent reply refreshed it).
17
36
  workerData: { scriptSource, scriptArgs, limits: cfg, budgetTotal: primitives.budget.total, budgetSpent: primitives.budget.spent() },
18
37
  resourceLimits: { maxOldGenerationSizeMb: cfg.maxHeapMb },
19
38
  ...(WORKER_EXEC_ARGV ? { execArgv: WORKER_EXEC_ARGV } : {}),
@@ -26,7 +45,7 @@ export function createWorkerHardenedVmRunner(limits = {}) {
26
45
  clearTimeout(timer);
27
46
  if (signal)
28
47
  signal.removeEventListener("abort", onAbort);
29
- void worker.terminate();
48
+ void worker.terminate(); // hard-stops a starved/runaway worker; no-op if it already exited
30
49
  };
31
50
  const finish = (settle) => {
32
51
  if (settled)
@@ -35,10 +54,15 @@ export function createWorkerHardenedVmRunner(limits = {}) {
35
54
  cleanup();
36
55
  settle();
37
56
  };
57
+ // Wall-clock backstop (terminate). Grace over the in-worker totalTimeoutMs so the worker's CLEAN error
58
+ // (stuck host primitive) wins normally; terminate is the backstop for microtask-starvation.
38
59
  timer = setTimeout(() => finish(() => reject(new WorkflowScriptError(`worker hardened-vm exceeded totalTimeoutMs (${cfg.totalTimeoutMs})`))), cfg.totalTimeoutMs + 1500);
39
60
  timer.unref?.();
40
61
  if (signal)
41
62
  signal.addEventListener("abort", onAbort, { once: true });
63
+ // Phase-gate fix: the worker's phase IPC verbs land on the REAL primitives.phase via the same three-verb gate
64
+ // the in-process runner uses (the body function stays in the worker; only mark/enter/exit cross). Replies
65
+ // reuse the worker's id-keyed "agentResult" resolver (same shape — no second pending map in the worker).
42
66
  const phaseGate = scopedPhaseGate((t, b) => primitives.phase(t, b));
43
67
  const reply = (id, p) => void p.then((value) => worker.postMessage({ type: "agentResult", id, ok: true, value: value ?? null, budgetSpent: primitives.budget.spent() }), (e) => worker.postMessage({ type: "agentResult", id, ok: false, error: e instanceof Error ? e.message : String(e), name: e instanceof Error ? e.name : "Error", budgetSpent: primitives.budget.spent() }));
44
68
  worker.on("message", (msg) => {
@@ -67,6 +91,7 @@ export function createWorkerHardenedVmRunner(limits = {}) {
67
91
  finish(() => reject(new WorkflowScriptError(msg.message ?? "worker hardened-vm script failed")));
68
92
  }
69
93
  });
94
+ // An `error` event = an UNCAUGHT worker exception, most often the heap-cap OOM ("Reached heap limit").
70
95
  worker.on("error", (err) => finish(() => reject(new WorkflowScriptError(`worker hardened-vm crashed (likely heap/OOM): ${err.message}`))));
71
96
  worker.on("exit", (code) => {
72
97
  if (!settled)
@@ -1,13 +1,30 @@
1
+ /**
2
+ * S8 hardened-vm WORKER bootstrap (design/98 v2 resource isolation). Runs inside a `worker_thread` spawned by
3
+ * {@link import("./hardened-vm-worker-runner.js").createWorkerHardenedVmRunner}. The SAME hardened-vm membrane
4
+ * ({@link runInHardenedVm} + {@link orchestrationGlobals}) executes the untrusted script HERE — so on top of
5
+ * the membrane's BOUNDARY isolation we get the worker's RESOURCE isolation: a separate V8 isolate + heap (capped
6
+ * by `resourceLimits.maxOldGenerationSizeMb` → a memory bomb OOMs the WORKER, not the shared main process) and
7
+ * `terminate()` (the main thread kills a microtask-starvation / runaway loop the in-process wall-clock cannot).
8
+ *
9
+ * The worker holds NO credentials: `agent`/`log` are STUBS that IPC to the main thread (which runs the real
10
+ * primitives with the real Runner/DB/model keys); `budget` reads a worker-local cache the main thread refreshes
11
+ * on each agent reply. So even a (validated-impossible) membrane boundary crossing lands in a credential-free isolate.
12
+ */
1
13
  import { parentPort, workerData } from "node:worker_threads";
2
14
  import { splitWorkflowMeta } from "@sema-agent/core";
15
+ // Resolve the sibling membrane module with an EXPLICIT extension so it loads under both a tsc build (`.js`) and
16
+ // tsx (`.ts`) — tsx does not map a `.js` specifier to `.ts` for a worker's static import. The template literal
17
+ // keeps tsc from rejecting a literal `.ts` specifier; the cast restores the types.
3
18
  const ext = import.meta.url.endsWith(".ts") ? "ts" : "js";
4
19
  const { runInHardenedVm, orchestrationGlobals } = (await import(`./hardened-vm-runner.${ext}`));
5
20
  const port = parentPort;
6
21
  if (!port)
7
22
  throw new Error("hardened-vm-worker must run inside a worker_thread");
8
23
  const input = workerData;
24
+ // agent: async IPC (request/response keyed by id) to the main thread's real primitive.
9
25
  let nextId = 0;
10
26
  const pending = new Map();
27
+ // budget: worker-local cache the main thread refreshes (piggybacked on every agentResult).
11
28
  const budgetCache = { total: input.budgetTotal, spent: input.budgetSpent ?? 0 };
12
29
  port.on("message", (msg) => {
13
30
  if (msg.type !== "agentResult" || msg.id === undefined)
@@ -26,6 +43,8 @@ port.on("message", (msg) => {
26
43
  p.rej(e);
27
44
  }
28
45
  });
46
+ // IPC-backed WorkflowPrimitives handed to orchestrationGlobals — agent/log/phase cross to the main thread;
47
+ // budget is the live cache; parallel/pipeline are built in-context by orchestrationGlobals.
29
48
  const ipcCall = (type, extra) => new Promise((res, rej) => {
30
49
  const id = nextId++;
31
50
  pending.set(id, { res, rej });
@@ -39,8 +58,14 @@ const ipcPrimitives = {
39
58
  spent: () => budgetCache.spent,
40
59
  remaining: () => (budgetCache.total === null ? Infinity : budgetCache.total - budgetCache.spent),
41
60
  },
61
+ // never invoked (orchestrationGlobals builds parallel/pipeline in-context), but required by the type.
42
62
  parallel: async () => [],
43
63
  pipeline: async () => [],
64
+ // Phase-gate fix: phase accounting must reach the MAIN thread's real primitives (the old stub ran the body and told
65
+ // core nothing — phases:[] on every worker-isolated run). The body function cannot cross the IPC boundary, so
66
+ // the same three-verb protocol as the in-process gate: mark (bare) / enter→token / exit(token, ok, err).
67
+ // orchestrationGlobals' own gate wraps THIS phase — its deferred body resolves when the in-VM body settles,
68
+ // which chains 1:1 onto these IPC verbs (enter here ⇒ the main-side gate's enter; exit ⇒ its exit).
44
69
  phase: async (t, b) => {
45
70
  if (b === undefined) {
46
71
  await ipcCall("phaseMark", { title: String(t) });
@@ -57,6 +82,7 @@ const ipcPrimitives = {
57
82
  await ipcCall("phaseExit", { token, ok: false, err: e instanceof Error ? e.message : String(e) });
58
83
  }
59
84
  catch {
85
+ /* exit is best-effort — the body's own error wins */
60
86
  }
61
87
  throw e;
62
88
  }
@@ -70,6 +96,7 @@ void (async () => {
70
96
  bindings: orchestrationGlobals(ipcPrimitives, input.scriptArgs, input.limits),
71
97
  limits: input.limits,
72
98
  });
99
+ // meta is core-parsed; JSON round-trip strips any slipped-in gadget before it crosses back via structuredClone.
73
100
  const safeMeta = JSON.parse(JSON.stringify(meta));
74
101
  port.postMessage({ type: "done", result, meta: safeMeta });
75
102
  }
@@ -1,12 +1,56 @@
1
1
  import type { SubagentSteerHandle } from "@sema-agent/core";
2
+ /**
3
+ * C2 (core 1.219) + design/122 (core 1.225) — the SERVICE-side registry for
4
+ * STEERING and RESUMING a Task SUB-AGENT (a sync delegation spawned by the `Agent` tool), the sibling of
5
+ * {@link import("./workflow-agent-steer.js").WorkflowAgentRegistry} for workflow agents.
6
+ *
7
+ * core emits a {@link SubagentSteerHandle} to the deployment's `RunInternals.onSubagentSpawn` sink the
8
+ * moment a SYNC child spawns (the handle NEVER reaches the model). The service registers it under the
9
+ * PARENT RUN's taskId so `POST /v1/runs/:runId/subagents/:target/{steer,resume}` can route into it.
10
+ * Delivery is replica-local (the handle is an in-memory bridge, like `steerableRuns`); a cross-replica
11
+ * steer/resume is an honest 409, never a silent drop.
12
+ *
13
+ * 🔴 LIFECYCLE (design/122 item ③ — the "不改必踩" change): handles are NO LONGER evicted on child settle.
14
+ * `resume` is only legal AFTER the child settled (core rejects `steering.still_running` before), so a
15
+ * settle-evicted registry would make resume structurally unreachable. Instead:
16
+ * - one handle PER parentToolCallId, NEWEST WINS (core D2: each resume re-emits a FRESH handle under the
17
+ * same id whose `settled` tracks the revived run — `Map.set` replace is exactly that contract);
18
+ * - handles live until the OWNING LEG ends (each run leg collects its `register()` unregister thunks and
19
+ * runs them in its finally — identity-guarded, so a suspend→fast-resume sibling leg's fresh handles are
20
+ * never clobbered by the old leg's teardown, mirroring the `steerableRuns` identity guard);
21
+ * - a per-run cap (oldest-evicted) bounds the map for a delegation-heavy parent (handles now outlive
22
+ * settle, so an unbounded list would grow for the whole run).
23
+ * After the parent run ends core disposes the retain ledger anyway (abort in-flight resumes + release all),
24
+ * so a post-teardown resume would only ever 409 — the eviction just keeps the map honest.
25
+ *
26
+ * Addressing: `target` matches `parentToolCallId` FIRST (spawn-time identity, unique per delegation), then
27
+ * falls back to `agentName` (display name; two children of the same agent type collide → the route 409s
28
+ * rather than steering/resuming an arbitrary sibling). Steer-in redaction is shared with the workflow route
29
+ * (`redactSteerIn`) — one owner of the trust gate. 🔐 `handle.childSessionId` is a continuation
30
+ * CAPABILITY: control-plane only — no route response may include it.
31
+ *
32
+ * SCOPE (core r1-M3): only SYNC delegations emit a handle — `run_in_background` children have neither steer
33
+ * nor resume (poll/stop via TaskOutput/TaskStop; durable bg revival is a separate core item).
34
+ */
35
+ /** Per-run handle cap — handles now live until the leg ends (not settle), so bound the map. Oldest-evicted;
36
+ * 256 mirrors the ToolDetachHub bound (a run with >256 live-or-resumable delegations is pathological). */
2
37
  export declare const SUBAGENT_HANDLES_PER_RUN_CAP = 256;
3
38
  export declare class SubagentSteerRegistry {
39
+ /** parent run taskId → (parentToolCallId → NEWEST handle). Map iteration order = insertion = age. */
4
40
  private readonly byRun;
41
+ /** Register a child handle under its parent run. SAME parentToolCallId replaces (core D2: a resume
42
+ * re-emits a fresh handle for the revived run — newest wins). Returns an IDENTITY-GUARDED unregister
43
+ * thunk: it removes the entry only while it still holds THIS handle (a fresh sibling-leg registration
44
+ * under the same id survives the old leg's teardown). Call it at LEG END, not on settle (design/122 ③). */
5
45
  register(runId: string, handle: SubagentSteerHandle): () => void;
46
+ /** Resolve `runId` + `target` to its handle on this replica. Exact `parentToolCallId` match wins (unique —
47
+ * newest-per-id by construction → count is 0|1); otherwise match by `agentName` (count may be >1 →
48
+ * ambiguous, `handle` undefined → the route 409s). count=0 → no such child here. */
6
49
  resolve(runId: string, target: string): {
7
50
  handle?: SubagentSteerHandle;
8
51
  count: number;
9
52
  };
53
+ /** Total registered handles (test/observability). */
10
54
  get size(): number;
11
55
  }
12
56
  //# sourceMappingURL=subagent-steer.d.ts.map
@@ -1,13 +1,53 @@
1
+ /**
2
+ * C2 (core 1.219) + design/122 (core 1.225) — the SERVICE-side registry for
3
+ * STEERING and RESUMING a Task SUB-AGENT (a sync delegation spawned by the `Agent` tool), the sibling of
4
+ * {@link import("./workflow-agent-steer.js").WorkflowAgentRegistry} for workflow agents.
5
+ *
6
+ * core emits a {@link SubagentSteerHandle} to the deployment's `RunInternals.onSubagentSpawn` sink the
7
+ * moment a SYNC child spawns (the handle NEVER reaches the model). The service registers it under the
8
+ * PARENT RUN's taskId so `POST /v1/runs/:runId/subagents/:target/{steer,resume}` can route into it.
9
+ * Delivery is replica-local (the handle is an in-memory bridge, like `steerableRuns`); a cross-replica
10
+ * steer/resume is an honest 409, never a silent drop.
11
+ *
12
+ * 🔴 LIFECYCLE (design/122 item ③ — the "不改必踩" change): handles are NO LONGER evicted on child settle.
13
+ * `resume` is only legal AFTER the child settled (core rejects `steering.still_running` before), so a
14
+ * settle-evicted registry would make resume structurally unreachable. Instead:
15
+ * - one handle PER parentToolCallId, NEWEST WINS (core D2: each resume re-emits a FRESH handle under the
16
+ * same id whose `settled` tracks the revived run — `Map.set` replace is exactly that contract);
17
+ * - handles live until the OWNING LEG ends (each run leg collects its `register()` unregister thunks and
18
+ * runs them in its finally — identity-guarded, so a suspend→fast-resume sibling leg's fresh handles are
19
+ * never clobbered by the old leg's teardown, mirroring the `steerableRuns` identity guard);
20
+ * - a per-run cap (oldest-evicted) bounds the map for a delegation-heavy parent (handles now outlive
21
+ * settle, so an unbounded list would grow for the whole run).
22
+ * After the parent run ends core disposes the retain ledger anyway (abort in-flight resumes + release all),
23
+ * so a post-teardown resume would only ever 409 — the eviction just keeps the map honest.
24
+ *
25
+ * Addressing: `target` matches `parentToolCallId` FIRST (spawn-time identity, unique per delegation), then
26
+ * falls back to `agentName` (display name; two children of the same agent type collide → the route 409s
27
+ * rather than steering/resuming an arbitrary sibling). Steer-in redaction is shared with the workflow route
28
+ * (`redactSteerIn`) — one owner of the trust gate. 🔐 `handle.childSessionId` is a continuation
29
+ * CAPABILITY: control-plane only — no route response may include it.
30
+ *
31
+ * SCOPE (core r1-M3): only SYNC delegations emit a handle — `run_in_background` children have neither steer
32
+ * nor resume (poll/stop via TaskOutput/TaskStop; durable bg revival is a separate core item).
33
+ */
34
+ /** Per-run handle cap — handles now live until the leg ends (not settle), so bound the map. Oldest-evicted;
35
+ * 256 mirrors the ToolDetachHub bound (a run with >256 live-or-resumable delegations is pathological). */
1
36
  export const SUBAGENT_HANDLES_PER_RUN_CAP = 256;
2
37
  export class SubagentSteerRegistry {
38
+ /** parent run taskId → (parentToolCallId → NEWEST handle). Map iteration order = insertion = age. */
3
39
  byRun = new Map();
40
+ /** Register a child handle under its parent run. SAME parentToolCallId replaces (core D2: a resume
41
+ * re-emits a fresh handle for the revived run — newest wins). Returns an IDENTITY-GUARDED unregister
42
+ * thunk: it removes the entry only while it still holds THIS handle (a fresh sibling-leg registration
43
+ * under the same id survives the old leg's teardown). Call it at LEG END, not on settle (design/122 ③). */
4
44
  register(runId, handle) {
5
45
  let handles = this.byRun.get(runId);
6
46
  if (!handles) {
7
47
  handles = new Map();
8
48
  this.byRun.set(runId, handles);
9
49
  }
10
- handles.delete(handle.parentToolCallId);
50
+ handles.delete(handle.parentToolCallId); // re-insert → moves to newest position (age = last registration)
11
51
  handles.set(handle.parentToolCallId, handle);
12
52
  if (handles.size > SUBAGENT_HANDLES_PER_RUN_CAP) {
13
53
  const oldest = handles.keys().next().value;
@@ -25,6 +65,9 @@ export class SubagentSteerRegistry {
25
65
  }
26
66
  };
27
67
  }
68
+ /** Resolve `runId` + `target` to its handle on this replica. Exact `parentToolCallId` match wins (unique —
69
+ * newest-per-id by construction → count is 0|1); otherwise match by `agentName` (count may be >1 →
70
+ * ambiguous, `handle` undefined → the route 409s). count=0 → no such child here. */
28
71
  resolve(runId, target) {
29
72
  const handles = this.byRun.get(runId);
30
73
  if (!handles || handles.size === 0)
@@ -35,6 +78,7 @@ export class SubagentSteerRegistry {
35
78
  const byName = [...handles.values()].filter((h) => h.agentName === target);
36
79
  return byName.length === 1 ? { handle: byName[0], count: 1 } : { count: byName.length };
37
80
  }
81
+ /** Total registered handles (test/observability). */
38
82
  get size() {
39
83
  let n = 0;
40
84
  for (const handles of this.byRun.values())
@@ -1,15 +1,69 @@
1
1
  import { type WorkflowAgentHandle } from "@sema-agent/core";
2
+ /** The result of resolving a steer target. core does NOT enforce label uniqueness within a run (a script may
3
+ * spawn two steerable agents with the same explicit `label`), so a (runId,label) can address MORE THAN ONE live
4
+ * agent — steering then has no well-defined target and must be refused, not sent to an arbitrary one. */
2
5
  export interface SteerResolution {
6
+ /** The live handle iff EXACTLY ONE matches; undefined when none (not here) or when ambiguous (>1). */
3
7
  handle?: WorkflowAgentHandle;
8
+ /** Live handles matching (0 = none on this replica, 1 = unique target, >1 = ambiguous → the route 409s). */
4
9
  count: number;
5
10
  }
11
+ /** A process-local registry of the steerable workflow-agent handles live on THIS replica. Mirrors the
12
+ * `steerableRuns` / `preemptableRuns` pattern (a live in-memory handle is replica-local — a cross-replica steer
13
+ * is a structured 409, never a silent drop). Stores a LIST per (runId,label): core does not enforce label
14
+ * uniqueness, so a duplicate-label collision must NOT silently last-writer-wins (that would steer the WRONG
15
+ * agent + strand the sibling) — {@link resolve} reports the count so the route can 409-on-ambiguous instead. */
6
16
  export declare class WorkflowAgentRegistry {
7
17
  private readonly handles;
18
+ /** Register a handle when its `agentStream` starts on this replica. Returns an unregister thunk to call when
19
+ * the agent settles (keeps the map from leaking). APPENDS (does not overwrite) so a duplicate-label sibling is
20
+ * also tracked → {@link resolve} sees count>1 and the route refuses rather than steering an arbitrary one. */
8
21
  register(handle: WorkflowAgentHandle): () => void;
22
+ /** Resolve `runId`+`label` to its UNIQUE live handle on this replica. count=0 → not here (terminal /
23
+ * cross-replica); count=1 → `handle` set; count>1 → ambiguous (`handle` undefined → the route 409s rather
24
+ * than steering an arbitrary one of the colliding same-label agents). */
9
25
  resolve(runId: string, label: string): SteerResolution;
26
+ /** Total live handles (test/observability). */
10
27
  get size(): number;
11
28
  }
29
+ /** Max steer-in length fed to a worker (chars). A steer is a short redirection, not a payload; core's
30
+ * `untrustedEgressForHuman` bounds + truncates beyond this.
31
+ *
32
+ * ⚠️ 纠正(2026-07-26,core 1.414 + 亲测):这里原本写的是「truncates with an **honest marker**」,现在只有
33
+ * 一半成立 —— 实测(`test/svc5-steer-live.test.ts` 里已钉):
34
+ * · **「被截断过」仍有信号**:超界时正文段尾出现 `…`(未超界时不出现)⇒ 消费方能判断"这不是全文";
35
+ * · **「截了多少」不再披露**:`[+N chars]` 在**所有**档位都没有了(实测 +1 / +10 / +100 / +5000 / +50000
36
+ * 全部无披露,输出长度恒 4103)。所以一个超出 5 万字符的输入与只超 1 个字符的输入,**外观完全相同**。
37
+ * ⚠️ core [1707]⑤ 的措辞是「**刚过界**的输入现在是裸 `…` 结尾」,而实测是**全档位**都没有披露 —— 范围比
38
+ * 它描述的宽。已带实测数据上黑板问 core 这是否是有意的(可能只是描述省略,也可能是它没注意到)。
39
+ * 在得到答复前**不改行为**:core 的截断器是它那侧的安全件,server 不该自己在外面补一层"+N chars"去顶
40
+ * ——那会变成两处各说一套。 */
12
41
  export declare const STEER_IN_MAX_CHARS = 4000;
42
+ /**
43
+ * **入参**上限(与 {@link STEER_IN_MAX_CHARS} 是两件事:那个限的是**输出**,这个限的是**请求带进来的**)。
44
+ *
45
+ * ── 为什么需要它 ───────────────────────────────────────────────────────────────────────────────
46
+ * 脱敏门是**同步**函数,烧的是**事件循环** ⇒ 一次大 steer 卡住的是**整个副本**,不只是那个请求。
47
+ * 此前这里没有上限:`body.content` 只校验类型与非空,而 JSON 请求体上限是 8 MiB。
48
+ *
49
+ * ── 数是怎么定的(实测,不是拍的)────────────────────────────────────────────────────────────────
50
+ * core 1.421.0 起该函数对输入是**线性**的(此前对「最长不间断词字符段」二次,已修)。
51
+ * 本机实测最坏形状(纯词字符段):64 KiB ≈ 17ms ⇒ **256 KiB ≈ 68ms**、8 MiB ≈ 2.2s。
52
+ * ⇒ 取 **256 KiB**:最坏一拍 ~70ms(可接受的事件循环占用),而它是输出上限的 **64 倍** ——
53
+ * 任何正当的粘贴都装得下,而超出部分**本来就会被丢弃**(只保留 4000 字符 + 一个"丢了多少"的披露)。
54
+ *
55
+ * ── 为什么是**拒**而不是**服务端先截** ──────────────────────────────────────────────────────────
56
+ * 先截会让 core 的 `[+N chars]` 披露**低报**(N 会变成相对截断后输入的),
57
+ * 而"诚实标记不得低报"是本仓另一条钉守着的性质。**为一个已经不紧迫的成本去改一条正确性性质,不划算。**
58
+ * ⇒ 拒 + 在错误里说清上限,调用方自己决定发什么。
59
+ */
13
60
  export declare const STEER_IN_MAX_REQUEST_CHARS: number;
61
+ /**
62
+ * The steer-in REDACTION gate (SVC-5 信任门). Treat the human/leader steer content as UNTRUSTED before it
63
+ * reaches the worker: redact host-internal leaks (URLs / tokens / named secrets / absolute paths), size-bound,
64
+ * and FENCE it so it can't pose as authority / forge a `</system-reminder>`. `label` tags the fenced block for
65
+ * the worker's reader. This is the service's half of the depth — `handle.steer` fences again inside its trusted
66
+ * marker framing.
67
+ */
14
68
  export declare function redactSteerIn(content: string, label: string): string;
15
69
  //# sourceMappingURL=workflow-agent-steer.d.ts.map