@namzu/sdk 31.1.0 → 33.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 (355) hide show
  1. package/CHANGELOG.md +188 -0
  2. package/dist/agents/ReactiveAgent.d.ts.map +1 -1
  3. package/dist/agents/ReactiveAgent.js +4 -0
  4. package/dist/agents/ReactiveAgent.js.map +1 -1
  5. package/dist/agents/SupervisorAgent.d.ts.map +1 -1
  6. package/dist/agents/SupervisorAgent.js +4 -0
  7. package/dist/agents/SupervisorAgent.js.map +1 -1
  8. package/dist/agents/runAgent.d.ts +4 -0
  9. package/dist/agents/runAgent.d.ts.map +1 -1
  10. package/dist/agents/runAgent.js +4 -0
  11. package/dist/agents/runAgent.js.map +1 -1
  12. package/dist/authorization/gate.d.ts +1 -1
  13. package/dist/authorization/gate.js +1 -1
  14. package/dist/connector/index.d.ts +1 -1
  15. package/dist/connector/index.d.ts.map +1 -1
  16. package/dist/connector/index.js +1 -1
  17. package/dist/connector/index.js.map +1 -1
  18. package/dist/connector/mcp/adapter.d.ts.map +1 -1
  19. package/dist/connector/mcp/adapter.js +71 -16
  20. package/dist/connector/mcp/adapter.js.map +1 -1
  21. package/dist/connector/mcp/client.d.ts.map +1 -1
  22. package/dist/connector/mcp/client.js +3 -2
  23. package/dist/connector/mcp/client.js.map +1 -1
  24. package/dist/connector/mcp/http-redirect.d.ts +17 -0
  25. package/dist/connector/mcp/http-redirect.d.ts.map +1 -0
  26. package/dist/connector/mcp/http-redirect.js +31 -0
  27. package/dist/connector/mcp/http-redirect.js.map +1 -0
  28. package/dist/connector/mcp/http-sse.d.ts.map +1 -1
  29. package/dist/connector/mcp/http-sse.js +5 -0
  30. package/dist/connector/mcp/http-sse.js.map +1 -1
  31. package/dist/connector/mcp/image-admission.d.ts +16 -0
  32. package/dist/connector/mcp/image-admission.d.ts.map +1 -0
  33. package/dist/connector/mcp/image-admission.js +288 -0
  34. package/dist/connector/mcp/image-admission.js.map +1 -0
  35. package/dist/connector/mcp/stdio.d.ts +13 -0
  36. package/dist/connector/mcp/stdio.d.ts.map +1 -1
  37. package/dist/connector/mcp/stdio.js +66 -23
  38. package/dist/connector/mcp/stdio.js.map +1 -1
  39. package/dist/connector/mcp/streamable-http.d.ts.map +1 -1
  40. package/dist/connector/mcp/streamable-http.js +3 -0
  41. package/dist/connector/mcp/streamable-http.js.map +1 -1
  42. package/dist/constants/tools/index.js +1 -1
  43. package/dist/constants/tools/index.js.map +1 -1
  44. package/dist/directory/derive-supervisor.d.ts.map +1 -1
  45. package/dist/directory/derive-supervisor.js +5 -0
  46. package/dist/directory/derive-supervisor.js.map +1 -1
  47. package/dist/directory/derive.d.ts.map +1 -1
  48. package/dist/directory/derive.js +5 -0
  49. package/dist/directory/derive.js.map +1 -1
  50. package/dist/directory/load.d.ts.map +1 -1
  51. package/dist/directory/load.js +1 -0
  52. package/dist/directory/load.js.map +1 -1
  53. package/dist/directory/types.d.ts +2 -0
  54. package/dist/directory/types.d.ts.map +1 -1
  55. package/dist/execution/base.d.ts +13 -0
  56. package/dist/execution/base.d.ts.map +1 -1
  57. package/dist/execution/base.js +103 -8
  58. package/dist/execution/base.js.map +1 -1
  59. package/dist/execution/code-runtime/types.d.ts +11 -1
  60. package/dist/execution/code-runtime/types.d.ts.map +1 -1
  61. package/dist/execution/code-runtime/types.js.map +1 -1
  62. package/dist/execution/code-runtime/worker.d.ts.map +1 -1
  63. package/dist/execution/code-runtime/worker.js +67 -9
  64. package/dist/execution/code-runtime/worker.js.map +1 -1
  65. package/dist/execution/errors.d.ts +12 -0
  66. package/dist/execution/errors.d.ts.map +1 -0
  67. package/dist/execution/errors.js +21 -0
  68. package/dist/execution/errors.js.map +1 -0
  69. package/dist/execution/factory.d.ts.map +1 -1
  70. package/dist/execution/factory.js +1 -0
  71. package/dist/execution/factory.js.map +1 -1
  72. package/dist/execution/hybrid.d.ts +5 -0
  73. package/dist/execution/hybrid.d.ts.map +1 -1
  74. package/dist/execution/hybrid.js +36 -16
  75. package/dist/execution/hybrid.js.map +1 -1
  76. package/dist/execution/index.d.ts +1 -0
  77. package/dist/execution/index.d.ts.map +1 -1
  78. package/dist/execution/index.js +1 -0
  79. package/dist/execution/index.js.map +1 -1
  80. package/dist/execution/local.d.ts +13 -0
  81. package/dist/execution/local.d.ts.map +1 -1
  82. package/dist/execution/local.js +145 -19
  83. package/dist/execution/local.js.map +1 -1
  84. package/dist/execution/output.d.ts +27 -0
  85. package/dist/execution/output.d.ts.map +1 -0
  86. package/dist/execution/output.js +105 -0
  87. package/dist/execution/output.js.map +1 -0
  88. package/dist/execution/remote.d.ts +26 -2
  89. package/dist/execution/remote.d.ts.map +1 -1
  90. package/dist/execution/remote.js +86 -15
  91. package/dist/execution/remote.js.map +1 -1
  92. package/dist/manager/agent/lifecycle.d.ts.map +1 -1
  93. package/dist/manager/agent/lifecycle.js +1 -0
  94. package/dist/manager/agent/lifecycle.js.map +1 -1
  95. package/dist/plugin/lifecycle.d.ts.map +1 -1
  96. package/dist/plugin/lifecycle.js +9 -3
  97. package/dist/plugin/lifecycle.js.map +1 -1
  98. package/dist/process/kill-tree.d.ts +11 -13
  99. package/dist/process/kill-tree.d.ts.map +1 -1
  100. package/dist/process/kill-tree.js +11 -13
  101. package/dist/process/kill-tree.js.map +1 -1
  102. package/dist/provider/errors.d.ts +1 -0
  103. package/dist/provider/errors.d.ts.map +1 -1
  104. package/dist/provider/errors.js +39 -1
  105. package/dist/provider/errors.js.map +1 -1
  106. package/dist/provider/fallback.d.ts.map +1 -1
  107. package/dist/provider/fallback.js +31 -0
  108. package/dist/provider/fallback.js.map +1 -1
  109. package/dist/provider/idle-timeout.d.ts.map +1 -1
  110. package/dist/provider/idle-timeout.js +5 -0
  111. package/dist/provider/idle-timeout.js.map +1 -1
  112. package/dist/provider/retry.d.ts.map +1 -1
  113. package/dist/provider/retry.js +5 -0
  114. package/dist/provider/retry.js.map +1 -1
  115. package/dist/public-runtime.d.ts +7 -5
  116. package/dist/public-runtime.d.ts.map +1 -1
  117. package/dist/public-runtime.js +9 -4
  118. package/dist/public-runtime.js.map +1 -1
  119. package/dist/public-types.d.ts +2 -1
  120. package/dist/public-types.d.ts.map +1 -1
  121. package/dist/registry/tool/execute.d.ts +7 -1
  122. package/dist/registry/tool/execute.d.ts.map +1 -1
  123. package/dist/registry/tool/execute.js +201 -54
  124. package/dist/registry/tool/execute.js.map +1 -1
  125. package/dist/run/command-gate.d.ts.map +1 -1
  126. package/dist/run/command-gate.js +11 -0
  127. package/dist/run/command-gate.js.map +1 -1
  128. package/dist/run/reporter.d.ts.map +1 -1
  129. package/dist/run/reporter.js +6 -1
  130. package/dist/run/reporter.js.map +1 -1
  131. package/dist/run/workspace-fingerprint.d.ts +4 -4
  132. package/dist/run/workspace-fingerprint.d.ts.map +1 -1
  133. package/dist/run/workspace-fingerprint.js +6 -4
  134. package/dist/run/workspace-fingerprint.js.map +1 -1
  135. package/dist/runtime/bidi/index.d.ts +1 -1
  136. package/dist/runtime/bidi/index.d.ts.map +1 -1
  137. package/dist/runtime/bidi/index.js +1 -1
  138. package/dist/runtime/bidi/index.js.map +1 -1
  139. package/dist/runtime/bidi/session.d.ts +19 -0
  140. package/dist/runtime/bidi/session.d.ts.map +1 -1
  141. package/dist/runtime/bidi/session.js +150 -26
  142. package/dist/runtime/bidi/session.js.map +1 -1
  143. package/dist/runtime/query/executor.d.ts +43 -1
  144. package/dist/runtime/query/executor.d.ts.map +1 -1
  145. package/dist/runtime/query/executor.js +824 -174
  146. package/dist/runtime/query/executor.js.map +1 -1
  147. package/dist/runtime/query/index.d.ts +5 -0
  148. package/dist/runtime/query/index.d.ts.map +1 -1
  149. package/dist/runtime/query/index.js +47 -4
  150. package/dist/runtime/query/index.js.map +1 -1
  151. package/dist/runtime/query/iteration/index.d.ts +1 -0
  152. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  153. package/dist/runtime/query/iteration/index.js +34 -14
  154. package/dist/runtime/query/iteration/index.js.map +1 -1
  155. package/dist/runtime/query/iteration/phases/advisory.js +2 -2
  156. package/dist/runtime/query/iteration/phases/advisory.js.map +1 -1
  157. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  158. package/dist/runtime/query/iteration/phases/tool-review.js +84 -22
  159. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  160. package/dist/runtime/query/iteration/provider-rejected-image.d.ts +13 -0
  161. package/dist/runtime/query/iteration/provider-rejected-image.d.ts.map +1 -0
  162. package/dist/runtime/query/iteration/provider-rejected-image.js +91 -0
  163. package/dist/runtime/query/iteration/provider-rejected-image.js.map +1 -0
  164. package/dist/runtime/query/iteration/stream-turn.d.ts +4 -1
  165. package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -1
  166. package/dist/runtime/query/iteration/stream-turn.js +7 -3
  167. package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
  168. package/dist/runtime/query/request-rich-content.d.ts +13 -0
  169. package/dist/runtime/query/request-rich-content.d.ts.map +1 -1
  170. package/dist/runtime/query/request-rich-content.js +173 -17
  171. package/dist/runtime/query/request-rich-content.js.map +1 -1
  172. package/dist/runtime/query/result.d.ts.map +1 -1
  173. package/dist/runtime/query/result.js +1 -0
  174. package/dist/runtime/query/result.js.map +1 -1
  175. package/dist/runtime/query/resume-pending.d.ts +5 -1
  176. package/dist/runtime/query/resume-pending.d.ts.map +1 -1
  177. package/dist/runtime/query/resume-pending.js +66 -1
  178. package/dist/runtime/query/resume-pending.js.map +1 -1
  179. package/dist/runtime/query/tool-output-budget.d.ts.map +1 -1
  180. package/dist/runtime/query/tool-output-budget.js +45 -16
  181. package/dist/runtime/query/tool-output-budget.js.map +1 -1
  182. package/dist/runtime/query/tooling.d.ts +6 -0
  183. package/dist/runtime/query/tooling.d.ts.map +1 -1
  184. package/dist/runtime/query/tooling.js +4 -0
  185. package/dist/runtime/query/tooling.js.map +1 -1
  186. package/dist/sandbox/provider/local.d.ts.map +1 -1
  187. package/dist/sandbox/provider/local.js +193 -41
  188. package/dist/sandbox/provider/local.js.map +1 -1
  189. package/dist/scheduler/delegating.d.ts +2 -1
  190. package/dist/scheduler/delegating.d.ts.map +1 -1
  191. package/dist/scheduler/delegating.js +6 -5
  192. package/dist/scheduler/delegating.js.map +1 -1
  193. package/dist/scheduler/local.d.ts +2 -1
  194. package/dist/scheduler/local.d.ts.map +1 -1
  195. package/dist/scheduler/local.js +2 -2
  196. package/dist/scheduler/local.js.map +1 -1
  197. package/dist/session/workspace/driver.d.ts +12 -4
  198. package/dist/session/workspace/driver.d.ts.map +1 -1
  199. package/dist/session/workspace/git-worktree.d.ts +17 -3
  200. package/dist/session/workspace/git-worktree.d.ts.map +1 -1
  201. package/dist/session/workspace/git-worktree.js +153 -34
  202. package/dist/session/workspace/git-worktree.js.map +1 -1
  203. package/dist/skills/registry.d.ts +15 -0
  204. package/dist/skills/registry.d.ts.map +1 -1
  205. package/dist/skills/registry.js +33 -0
  206. package/dist/skills/registry.js.map +1 -1
  207. package/dist/store/attachment/index.d.ts +24 -2
  208. package/dist/store/attachment/index.d.ts.map +1 -1
  209. package/dist/store/attachment/index.js +52 -5
  210. package/dist/store/attachment/index.js.map +1 -1
  211. package/dist/tools/builtins/computer-use.d.ts.map +1 -1
  212. package/dist/tools/builtins/computer-use.js +39 -2
  213. package/dist/tools/builtins/computer-use.js.map +1 -1
  214. package/dist/tools/builtins/lsp.d.ts +1 -1
  215. package/dist/tools/builtins/run-code.d.ts.map +1 -1
  216. package/dist/tools/builtins/run-code.js +24 -16
  217. package/dist/tools/builtins/run-code.js.map +1 -1
  218. package/dist/tools/builtins/skill.d.ts +2 -1
  219. package/dist/tools/builtins/skill.d.ts.map +1 -1
  220. package/dist/tools/builtins/skill.js +250 -6
  221. package/dist/tools/builtins/skill.js.map +1 -1
  222. package/dist/tools/coordinator/agent.d.ts.map +1 -1
  223. package/dist/tools/coordinator/agent.js +100 -30
  224. package/dist/tools/coordinator/agent.js.map +1 -1
  225. package/dist/types/agent/base.d.ts +4 -0
  226. package/dist/types/agent/base.d.ts.map +1 -1
  227. package/dist/types/agent/factory.d.ts +1 -0
  228. package/dist/types/agent/factory.d.ts.map +1 -1
  229. package/dist/types/agent/manager.d.ts +1 -1
  230. package/dist/types/agent/manager.d.ts.map +1 -1
  231. package/dist/types/agent/scheduler.d.ts +10 -1
  232. package/dist/types/agent/scheduler.d.ts.map +1 -1
  233. package/dist/types/authorization/index.d.ts +12 -12
  234. package/dist/types/bidi/index.d.ts +19 -2
  235. package/dist/types/bidi/index.d.ts.map +1 -1
  236. package/dist/types/computer-use/index.d.ts +18 -0
  237. package/dist/types/computer-use/index.d.ts.map +1 -1
  238. package/dist/types/connector/execution-context.d.ts +3 -1
  239. package/dist/types/connector/execution-context.d.ts.map +1 -1
  240. package/dist/types/execution/index.d.ts +43 -1
  241. package/dist/types/execution/index.d.ts.map +1 -1
  242. package/dist/types/hitl/index.d.ts +11 -0
  243. package/dist/types/hitl/index.d.ts.map +1 -1
  244. package/dist/types/hitl/index.js.map +1 -1
  245. package/dist/types/message/index.d.ts +31 -1
  246. package/dist/types/message/index.d.ts.map +1 -1
  247. package/dist/types/message/index.js +43 -0
  248. package/dist/types/message/index.js.map +1 -1
  249. package/dist/types/plugin/index.d.ts +14 -1
  250. package/dist/types/plugin/index.d.ts.map +1 -1
  251. package/dist/types/plugin/index.js +1 -0
  252. package/dist/types/plugin/index.js.map +1 -1
  253. package/dist/types/provider/error.d.ts +4 -1
  254. package/dist/types/provider/error.d.ts.map +1 -1
  255. package/dist/types/provider/interface.d.ts +14 -0
  256. package/dist/types/provider/interface.d.ts.map +1 -1
  257. package/dist/types/run/events.d.ts +10 -2
  258. package/dist/types/run/events.d.ts.map +1 -1
  259. package/dist/types/run/events.js.map +1 -1
  260. package/dist/types/sandbox/index.d.ts +15 -10
  261. package/dist/types/sandbox/index.d.ts.map +1 -1
  262. package/dist/types/sandbox/index.js.map +1 -1
  263. package/dist/types/tool/index.d.ts +129 -12
  264. package/dist/types/tool/index.d.ts.map +1 -1
  265. package/dist/types/tool/index.js.map +1 -1
  266. package/dist/types/workspace/ref.d.ts +5 -0
  267. package/dist/types/workspace/ref.d.ts.map +1 -1
  268. package/dist/utils/abort.d.ts +8 -0
  269. package/dist/utils/abort.d.ts.map +1 -1
  270. package/dist/utils/abort.js +16 -0
  271. package/dist/utils/abort.js.map +1 -1
  272. package/package.json +4 -4
  273. package/src/agents/ReactiveAgent.ts +4 -0
  274. package/src/agents/SupervisorAgent.ts +4 -0
  275. package/src/agents/runAgent.ts +8 -0
  276. package/src/authorization/gate.ts +1 -1
  277. package/src/connector/index.ts +2 -0
  278. package/src/connector/mcp/adapter.ts +82 -21
  279. package/src/connector/mcp/client.ts +2 -1
  280. package/src/connector/mcp/http-redirect.ts +32 -0
  281. package/src/connector/mcp/http-sse.ts +5 -0
  282. package/src/connector/mcp/image-admission.ts +272 -0
  283. package/src/connector/mcp/stdio.ts +63 -21
  284. package/src/connector/mcp/streamable-http.ts +3 -0
  285. package/src/constants/tools/index.ts +1 -1
  286. package/src/directory/derive-supervisor.ts +5 -0
  287. package/src/directory/derive.ts +5 -0
  288. package/src/directory/load.ts +1 -0
  289. package/src/directory/types.ts +2 -0
  290. package/src/execution/base.ts +124 -7
  291. package/src/execution/code-runtime/types.ts +15 -1
  292. package/src/execution/code-runtime/worker.ts +72 -9
  293. package/src/execution/errors.ts +24 -0
  294. package/src/execution/factory.ts +1 -0
  295. package/src/execution/hybrid.ts +47 -16
  296. package/src/execution/index.ts +1 -0
  297. package/src/execution/local.ts +164 -19
  298. package/src/execution/output.ts +104 -0
  299. package/src/execution/remote.ts +122 -17
  300. package/src/manager/agent/lifecycle.ts +1 -0
  301. package/src/plugin/lifecycle.ts +9 -3
  302. package/src/process/kill-tree.ts +11 -13
  303. package/src/provider/errors.ts +40 -1
  304. package/src/provider/fallback.ts +39 -0
  305. package/src/provider/idle-timeout.ts +8 -0
  306. package/src/provider/retry.ts +8 -0
  307. package/src/public-runtime.ts +23 -2
  308. package/src/public-types.ts +11 -0
  309. package/src/registry/tool/execute.ts +230 -64
  310. package/src/run/command-gate.ts +12 -0
  311. package/src/run/reporter.ts +7 -1
  312. package/src/run/workspace-fingerprint.ts +5 -4
  313. package/src/runtime/bidi/index.ts +1 -1
  314. package/src/runtime/bidi/session.ts +177 -28
  315. package/src/runtime/query/executor.ts +987 -172
  316. package/src/runtime/query/index.ts +59 -6
  317. package/src/runtime/query/iteration/index.ts +69 -29
  318. package/src/runtime/query/iteration/phases/advisory.ts +2 -2
  319. package/src/runtime/query/iteration/phases/tool-review.ts +87 -23
  320. package/src/runtime/query/iteration/provider-rejected-image.ts +106 -0
  321. package/src/runtime/query/iteration/stream-turn.ts +12 -2
  322. package/src/runtime/query/request-rich-content.ts +213 -15
  323. package/src/runtime/query/result.ts +1 -0
  324. package/src/runtime/query/resume-pending.ts +83 -1
  325. package/src/runtime/query/tool-output-budget.ts +52 -17
  326. package/src/runtime/query/tooling.ts +10 -0
  327. package/src/sandbox/provider/local.ts +213 -45
  328. package/src/scheduler/delegating.ts +6 -5
  329. package/src/scheduler/local.ts +3 -2
  330. package/src/session/workspace/driver.ts +12 -4
  331. package/src/session/workspace/git-worktree.ts +173 -34
  332. package/src/skills/registry.ts +47 -0
  333. package/src/store/attachment/index.ts +100 -15
  334. package/src/tools/builtins/computer-use.ts +45 -2
  335. package/src/tools/builtins/run-code.ts +24 -16
  336. package/src/tools/builtins/skill.ts +325 -7
  337. package/src/tools/coordinator/agent.ts +108 -33
  338. package/src/types/agent/base.ts +4 -0
  339. package/src/types/agent/factory.ts +1 -0
  340. package/src/types/agent/manager.ts +1 -1
  341. package/src/types/agent/scheduler.ts +10 -1
  342. package/src/types/bidi/index.ts +19 -2
  343. package/src/types/computer-use/index.ts +19 -0
  344. package/src/types/connector/execution-context.ts +3 -0
  345. package/src/types/execution/index.ts +46 -1
  346. package/src/types/hitl/index.ts +11 -0
  347. package/src/types/message/index.ts +78 -1
  348. package/src/types/plugin/index.ts +15 -0
  349. package/src/types/provider/error.ts +4 -1
  350. package/src/types/provider/interface.ts +18 -0
  351. package/src/types/run/events.ts +18 -4
  352. package/src/types/sandbox/index.ts +15 -10
  353. package/src/types/tool/index.ts +159 -15
  354. package/src/types/workspace/ref.ts +5 -0
  355. package/src/utils/abort.ts +18 -0
@@ -12,6 +12,21 @@ export interface BaseExecutionEvent {
12
12
 
13
13
  export type ExecutionEventListener = (event: BaseExecutionEvent) => void
14
14
 
15
+ type ExecutionLifecycleState =
16
+ | 'idle'
17
+ | 'initializing'
18
+ | 'ready'
19
+ | 'tearing-down'
20
+ | 'teardown-failed'
21
+ | 'torn-down'
22
+
23
+ class InitializationSupersededError extends Error {
24
+ constructor(contextId: string) {
25
+ super(`Execution context "${contextId}" initialization was superseded by teardown`)
26
+ this.name = 'InitializationSupersededError'
27
+ }
28
+ }
29
+
15
30
  export abstract class BaseExecutionContext implements ExecutionContextLifecycle {
16
31
  abstract readonly id: string
17
32
  abstract readonly environment: ExecutionEnvironment
@@ -19,6 +34,9 @@ export abstract class BaseExecutionContext implements ExecutionContextLifecycle
19
34
  protected log: Logger
20
35
  protected ready = false
21
36
  private listeners: ExecutionEventListener[] = []
37
+ private lifecycleState: ExecutionLifecycleState = 'idle'
38
+ private initializationOperation: Promise<void> | undefined
39
+ private teardownOperation: Promise<void> | undefined
22
40
 
23
41
  constructor(log?: Logger) {
24
42
  this.log = resolveLogger(log).child({
@@ -27,17 +45,62 @@ export abstract class BaseExecutionContext implements ExecutionContextLifecycle
27
45
  })
28
46
  }
29
47
 
30
- async initialize(): Promise<void> {
48
+ initialize(): Promise<void> {
49
+ if (this.lifecycleState === 'ready') return Promise.resolve()
50
+ if (this.lifecycleState === 'initializing' && this.initializationOperation) {
51
+ return this.initializationOperation
52
+ }
53
+ if (this.lifecycleState === 'tearing-down') {
54
+ return Promise.reject(
55
+ new Error(`Execution context "${this.id}" cannot initialize while teardown is in progress`),
56
+ )
57
+ }
58
+ if (this.lifecycleState === 'teardown-failed') {
59
+ return Promise.reject(
60
+ new Error(
61
+ `Execution context "${this.id}" cannot initialize until its failed teardown is retried`,
62
+ ),
63
+ )
64
+ }
65
+
66
+ this.lifecycleState = 'initializing'
67
+ const operation = this.performInitialize()
68
+ this.initializationOperation = operation
69
+ void operation.then(
70
+ () => this.releaseInitialization(operation),
71
+ () => this.releaseInitialization(operation),
72
+ )
73
+ return operation
74
+ }
75
+
76
+ private async performInitialize(): Promise<void> {
31
77
  try {
78
+ this.onInitializationStarted()
32
79
  await this.doInitialize()
80
+ if (this.lifecycleState !== 'initializing') {
81
+ throw new InitializationSupersededError(this.id)
82
+ }
83
+ this.onInitializationCommitted()
84
+ if (this.lifecycleState !== 'initializing') {
85
+ throw new InitializationSupersededError(this.id)
86
+ }
33
87
  this.ready = true
34
- this.emit({ type: 'context_initialized', contextId: this.id, environment: this.environment })
35
- this.emit({ type: 'context_ready', contextId: this.id })
88
+ this.lifecycleState = 'ready'
36
89
  this.log.info('Execution context initialized', {
37
90
  'namzu.execution.context_id': this.id,
38
91
  'namzu.execution.environment': this.environment,
39
92
  })
93
+ this.emit({
94
+ type: 'context_initialized',
95
+ contextId: this.id,
96
+ environment: this.environment,
97
+ })
98
+ if (this.lifecycleState !== 'ready') return
99
+ this.emit({ type: 'context_ready', contextId: this.id })
40
100
  } catch (err) {
101
+ if (err instanceof InitializationSupersededError) throw err
102
+ this.ready = false
103
+ if (this.lifecycleState === 'initializing') this.lifecycleState = 'idle'
41
104
  const message = toErrorMessage(err)
42
105
  this.emit({ type: 'context_error', contextId: this.id, error: message })
43
106
  this.log.error('Execution context initialization failed', {
@@ -48,20 +111,65 @@ export abstract class BaseExecutionContext implements ExecutionContextLifecycle
48
111
  }
49
112
  }
50
113
 
114
+ private releaseInitialization(operation: Promise<void>): void {
115
+ if (this.initializationOperation === operation) this.initializationOperation = undefined
116
+ }
117
+
51
118
  isReady(): boolean {
52
119
  return this.ready
53
120
  }
54
121
 
55
- async teardown(): Promise<void> {
122
+ teardown(): Promise<void> {
123
+ if (this.lifecycleState === 'tearing-down' && this.teardownOperation) {
124
+ return this.teardownOperation
125
+ }
126
+ if (this.lifecycleState === 'torn-down') return Promise.resolve()
127
+
128
+ this.ready = false
129
+ this.lifecycleState = 'tearing-down'
130
+ const operation = this.performTeardown(this.initializationOperation)
131
+ this.teardownOperation = operation
132
+ void operation.then(
133
+ () => this.releaseTeardown(operation),
134
+ () => this.releaseTeardown(operation),
135
+ )
136
+ return operation
137
+ }
138
+
139
+ private async performTeardown(pendingInitialization: Promise<void> | undefined): Promise<void> {
56
140
  try {
141
+ // Runs before the first await. Subclasses close public admission here so
142
+ // work cannot slip behind an initialization this teardown must await.
143
+ this.onTeardownRequested()
144
+ if (pendingInitialization) {
145
+ try {
146
+ await pendingInitialization
147
+ } catch {
148
+ // Initialization errors are reported by their owner. Cleanup still runs.
149
+ }
150
+ }
57
151
  await this.doTeardown()
58
- } finally {
59
- this.ready = false
152
+ this.lifecycleState = 'torn-down'
153
+ this.log.info('Execution context torn down', {
154
+ 'namzu.execution.context_id': this.id,
155
+ })
60
156
  this.emit({ type: 'context_teardown', contextId: this.id })
61
- this.log.info('Execution context torn down', { 'namzu.execution.context_id': this.id })
157
+ } catch (err) {
158
+ this.lifecycleState = 'teardown-failed'
159
+ const message = toErrorMessage(err)
160
+ this.emit({ type: 'context_error', contextId: this.id, error: message })
161
+ this.log.error('Execution context teardown failed', {
162
+ 'namzu.execution.context_id': this.id,
163
+ 'exception.message': message,
164
+ })
165
+ throw err
62
166
  }
63
167
  }
64
168
 
169
+ private releaseTeardown(operation: Promise<void>): void {
170
+ if (this.teardownOperation === operation) this.teardownOperation = undefined
171
+ }
172
+
65
173
  on(listener: ExecutionEventListener): void {
66
174
  this.listeners.push(listener)
67
175
  }
@@ -85,4 +193,13 @@ export abstract class BaseExecutionContext implements ExecutionContextLifecycle
85
193
 
86
194
  protected abstract doInitialize(): Promise<void>
87
195
  protected abstract doTeardown(): Promise<void>
196
+
197
+ /** Fence subclass-owned public admission before initialization begins. */
198
+ protected onInitializationStarted(): void {}
199
+
200
+ /** Reopen admission only after Base has proved initialization was not superseded. */
201
+ protected onInitializationCommitted(): void {}
202
+
203
+ /** Fence subclass-owned public admission synchronously when teardown is requested. */
204
+ protected onTeardownRequested(): void {}
88
205
  }
@@ -42,6 +42,17 @@ export interface HostCallResult {
42
42
  readonly error?: string
43
43
  }
44
44
 
45
+ /** Authority and provenance for one call from a running program. */
46
+ export interface HostCallContext {
47
+ /** Unique within this program execution. */
48
+ readonly runtimeToolCallId: string
49
+ /**
50
+ * Revoked when the caller cancels or the program's own wall clock expires.
51
+ * A conforming host threads this into the operation it starts.
52
+ */
53
+ readonly signal: AbortSignal
54
+ }
55
+
45
56
  /**
46
57
  * What the host will answer.
47
58
  *
@@ -50,7 +61,10 @@ export interface HostCallResult {
50
61
  * exactly the point: the program's reach is the run's reach, resolved at
51
62
  * the moment of the call rather than frozen when the program started.
52
63
  */
53
- export type HostCallHandler = (request: HostCallRequest) => Promise<HostCallResult>
64
+ export type HostCallHandler = (
65
+ request: HostCallRequest,
66
+ context: HostCallContext,
67
+ ) => Promise<HostCallResult>
54
68
 
55
69
  export interface RunCodeOptions {
56
70
  /** The program. Untrusted text, from the model. */
@@ -105,6 +105,17 @@ export class WorkerCodeRuntime implements CodeRuntime {
105
105
  let output = ''
106
106
  let truncated = false
107
107
 
108
+ // A withdrawn caller owns admission. Do not start an isolate merely to
109
+ // discover the signal was already aborted after construction.
110
+ if (options.signal?.aborted) {
111
+ return {
112
+ outcome: { status: 'cancelled' },
113
+ output,
114
+ outputTruncated: truncated,
115
+ calls,
116
+ }
117
+ }
118
+
108
119
  const appendOutput = (lines: readonly string[]): void => {
109
120
  for (const line of lines) {
110
121
  if (truncated) return
@@ -143,6 +154,10 @@ export class WorkerCodeRuntime implements CodeRuntime {
143
154
 
144
155
  return await new Promise<CodeRunResult>((resolve) => {
145
156
  let settled = false
157
+ let terminal: CodeRunResult | undefined
158
+ const inFlight = new Set<Promise<void>>()
159
+ const operation = new AbortController()
160
+
146
161
  const finish = (result: CodeRunResult): void => {
147
162
  // The Promise already ignores a second `resolve`, so removing
148
163
  // this guard changes no result — a mutation removing it
@@ -153,18 +168,42 @@ export class WorkerCodeRuntime implements CodeRuntime {
153
168
  // then `exit` is the ordinary sequence, not an edge case.
154
169
  if (settled) return
155
170
  settled = true
171
+ // Terminating the worker only stops the program. Host work already
172
+ // admitted through onHostCall lives outside it, so revoke the signal
173
+ // that was handed to every such call before reporting settlement.
174
+ if (!operation.signal.aborted) {
175
+ operation.abort(new Error(`Code runtime settled with status ${result.outcome.status}`))
176
+ }
156
177
  clearTimeout(deadline)
157
178
  options.signal?.removeEventListener('abort', onAbort)
158
179
  void worker.terminate()
159
- resolve(result)
180
+ // A late, non-cooperative host call must not mutate a result the
181
+ // caller already received.
182
+ resolve({ ...result, calls: [...calls] })
183
+ }
184
+
185
+ const finishTerminalWhenHostCallsSettle = (): void => {
186
+ if (terminal && inFlight.size === 0) finish(terminal)
160
187
  }
161
188
 
162
189
  const deadline = setTimeout(() => {
163
- finish({ outcome: { status: 'timed-out' }, output, outputTruncated: truncated, calls })
190
+ operation.abort(new Error(`Code runtime exceeded ${options.timeoutMs}ms`))
191
+ finish({
192
+ outcome: { status: 'timed-out' },
193
+ output,
194
+ outputTruncated: truncated,
195
+ calls,
196
+ })
164
197
  }, options.timeoutMs)
165
198
 
166
199
  const onAbort = (): void => {
167
- finish({ outcome: { status: 'cancelled' }, output, outputTruncated: truncated, calls })
200
+ operation.abort(options.signal?.reason)
201
+ finish({
202
+ outcome: { status: 'cancelled' },
203
+ output,
204
+ outputTruncated: truncated,
205
+ calls,
206
+ })
168
207
  }
169
208
  options.signal?.addEventListener('abort', onAbort, { once: true })
170
209
  if (options.signal?.aborted) {
@@ -185,13 +224,23 @@ export class WorkerCodeRuntime implements CodeRuntime {
185
224
  kind: 'call-result',
186
225
  id,
187
226
  ok: false,
188
- error: new HostCallDeniedError({ name, allowed: options.allowedCalls }).message,
227
+ error: new HostCallDeniedError({
228
+ name,
229
+ allowed: options.allowedCalls,
230
+ }).message,
189
231
  })
190
232
  return
191
233
  }
192
- void options
193
- .onHostCall({ name, input: message.input })
234
+ const runtimeToolCallId = String(id)
235
+ const hostCall = Promise.resolve()
236
+ .then(() =>
237
+ options.onHostCall(
238
+ { name, input: message.input },
239
+ { runtimeToolCallId, signal: operation.signal },
240
+ ),
241
+ )
194
242
  .then((result) => {
243
+ if (settled) return
195
244
  calls.push({ name, ok: result.ok })
196
245
  worker.postMessage({
197
246
  kind: 'call-result',
@@ -202,6 +251,7 @@ export class WorkerCodeRuntime implements CodeRuntime {
202
251
  })
203
252
  })
204
253
  .catch((err: unknown) => {
254
+ if (settled) return
205
255
  calls.push({ name, ok: false })
206
256
  worker.postMessage({
207
257
  kind: 'call-result',
@@ -210,6 +260,11 @@ export class WorkerCodeRuntime implements CodeRuntime {
210
260
  error: err instanceof Error ? err.message : String(err),
211
261
  })
212
262
  })
263
+ .finally(() => {
264
+ inFlight.delete(hostCall)
265
+ finishTerminalWhenHostCallsSettle()
266
+ })
267
+ inFlight.add(hostCall)
213
268
  return
214
269
  }
215
270
 
@@ -219,12 +274,17 @@ export class WorkerCodeRuntime implements CodeRuntime {
219
274
  }
220
275
 
221
276
  if (message.kind === 'done') {
222
- finish({
277
+ // A program may start a host call without awaiting it. The worker
278
+ // declaring its JavaScript body complete is not evidence that the
279
+ // effect it started is complete, so keep the runtime open until the
280
+ // already-admitted host calls settle (or the deadline revokes them).
281
+ terminal = {
223
282
  outcome: { status: 'completed', result: message.result },
224
283
  output,
225
284
  outputTruncated: truncated,
226
285
  calls,
227
- })
286
+ }
287
+ finishTerminalWhenHostCallsSettle()
228
288
  return
229
289
  }
230
290
 
@@ -255,7 +315,10 @@ export class WorkerCodeRuntime implements CodeRuntime {
255
315
  // `error` message — a hard crash, or a program that called
256
316
  // `process.exit` through something we failed to withhold.
257
317
  finish({
258
- outcome: { status: 'failed', error: `The program exited with code ${code}.` },
318
+ outcome: {
319
+ status: 'failed',
320
+ error: `The program exited with code ${code}.`,
321
+ },
259
322
  output,
260
323
  outputTruncated: truncated,
261
324
  calls,
@@ -0,0 +1,24 @@
1
+ export class CommandCancellationUnsupportedError extends Error {
2
+ readonly code = 'command_cancellation_unsupported' as const
3
+
4
+ constructor(readonly contextId: string) {
5
+ super(
6
+ `Remote execution context "${contextId}" cannot accept command cancellation because its generic executor has no reservation and terminal-acknowledgement protocol. No remote command was started. Use the Sandbox.exec() contract for cancellable remote command execution.`,
7
+ )
8
+ this.name = 'CommandCancellationUnsupportedError'
9
+ }
10
+ }
11
+
12
+ export class RemoteExecutionBusyError extends Error {
13
+ readonly code = 'remote_execution_busy' as const
14
+
15
+ constructor(
16
+ readonly contextId: string,
17
+ readonly activeCommandCount: number,
18
+ ) {
19
+ super(
20
+ `Remote execution context "${contextId}" still owns ${activeCommandCount} active command${activeCommandCount === 1 ? '' : 's'}. Disconnect or teardown was refused; wait for the command${activeCommandCount === 1 ? '' : 's'} to settle, then retry.`,
21
+ )
22
+ this.name = 'RemoteExecutionBusyError'
23
+ }
24
+ }
@@ -15,6 +15,7 @@ export class ExecutionContextFactory {
15
15
  envVars: config.envVars,
16
16
  capabilities: config.capabilities,
17
17
  shell: config.shell,
18
+ maxOutputBytes: config.maxOutputBytes,
18
19
  })
19
20
 
20
21
  case 'remote':
@@ -21,6 +21,7 @@ export interface HybridExecutionContextOptions {
21
21
  envVars?: Record<string, string>
22
22
  capabilities?: ExecutionCapability[]
23
23
  shell?: string
24
+ maxOutputBytes?: number
24
25
  }
25
26
  remotes: RemoteTarget[]
26
27
  routingStrategy?: ExecutionRoutingStrategy
@@ -36,6 +37,7 @@ export class HybridExecutionContext extends BaseExecutionContext implements Comm
36
37
  private remoteTargets: RemoteTarget[]
37
38
  private routingStrategy: ExecutionRoutingStrategy
38
39
  private roundRobinIndex = 0
40
+ private admissionsOpen = true
39
41
 
40
42
  constructor(options: HybridExecutionContextOptions) {
41
43
  super(options.log)
@@ -50,6 +52,7 @@ export class HybridExecutionContext extends BaseExecutionContext implements Comm
50
52
  envVars: options.local.envVars,
51
53
  capabilities: options.local.capabilities,
52
54
  shell: options.local.shell,
55
+ maxOutputBytes: options.local.maxOutputBytes,
53
56
  log: options.log,
54
57
  })
55
58
 
@@ -83,13 +86,29 @@ export class HybridExecutionContext extends BaseExecutionContext implements Comm
83
86
  })
84
87
  }
85
88
 
89
+ protected override onInitializationStarted(): void {
90
+ this.admissionsOpen = false
91
+ }
92
+
93
+ protected override onInitializationCommitted(): void {
94
+ this.admissionsOpen = true
95
+ }
96
+
97
+ protected override onTeardownRequested(): void {
98
+ this.admissionsOpen = false
99
+ }
100
+
86
101
  protected async doTeardown(): Promise<void> {
87
- const teardownPromises: Promise<void>[] = []
88
- for (const remote of this.remoteCtxs.values()) {
89
- teardownPromises.push(remote.teardown())
102
+ const contexts: BaseExecutionContext[] = [this.localCtx, ...this.remoteCtxs.values()]
103
+ const results = await Promise.allSettled(contexts.map((context) => context.teardown()))
104
+ const failures = results.flatMap((result) =>
105
+ result.status === 'rejected' ? [result.reason] : [],
106
+ )
107
+
108
+ if (failures.length === 1) throw failures[0]
109
+ if (failures.length > 1) {
110
+ throw new AggregateError(failures, 'Multiple hybrid execution contexts failed to tear down')
90
111
  }
91
- await Promise.allSettled(teardownPromises)
92
- await this.localCtx.teardown()
93
112
  }
94
113
 
95
114
  getLocal(): LocalExecutionContext {
@@ -122,11 +141,16 @@ export class HybridExecutionContext extends BaseExecutionContext implements Comm
122
141
  }
123
142
 
124
143
  async disconnectAllRemotes(): Promise<void> {
125
- const promises: Promise<void>[] = []
126
- for (const remote of this.remoteCtxs.values()) {
127
- promises.push(remote.disconnect())
144
+ const results = await Promise.allSettled(
145
+ Array.from(this.remoteCtxs.values(), (remote) => remote.disconnect()),
146
+ )
147
+ const failures = results.flatMap((result) =>
148
+ result.status === 'rejected' ? [result.reason] : [],
149
+ )
150
+ if (failures.length === 1) throw failures[0]
151
+ if (failures.length > 1) {
152
+ throw new AggregateError(failures, 'Multiple remote execution contexts failed to disconnect')
128
153
  }
129
- await Promise.allSettled(promises)
130
154
  }
131
155
 
132
156
  async executeCommand(
@@ -134,6 +158,11 @@ export class HybridExecutionContext extends BaseExecutionContext implements Comm
134
158
  args: string[] = [],
135
159
  options?: CommandOptions,
136
160
  ): Promise<CommandResult> {
161
+ if (!this.admissionsOpen) {
162
+ throw new Error(
163
+ `Hybrid execution context "${this.id}" is initializing, tearing down, or torn down. Wait for initialization to commit before executing another command.`,
164
+ )
165
+ }
137
166
  switch (this.routingStrategy) {
138
167
  case 'local-first':
139
168
  return this.localCtx.executeCommand(command, args, options)
@@ -141,8 +170,7 @@ export class HybridExecutionContext extends BaseExecutionContext implements Comm
141
170
  case 'remote-first': {
142
171
  const connectedRemote = this.getFirstConnectedRemote()
143
172
  if (connectedRemote) {
144
- const fullCommand = args.length > 0 ? `${command} ${args.join(' ')}` : command
145
- return connectedRemote.executeRemote(fullCommand, options)
173
+ return connectedRemote.executeCommand(command, args, options)
146
174
  }
147
175
  return this.localCtx.executeCommand(command, args, options)
148
176
  }
@@ -162,8 +190,7 @@ export class HybridExecutionContext extends BaseExecutionContext implements Comm
162
190
  if (target === this.localCtx) {
163
191
  return this.localCtx.executeCommand(command, args, options)
164
192
  }
165
- const fullCommand = args.length > 0 ? `${command} ${args.join(' ')}` : command
166
- return (target as RemoteExecutionContext).executeRemote(fullCommand, options)
193
+ return (target as RemoteExecutionContext).executeCommand(command, args, options)
167
194
  }
168
195
 
169
196
  default: {
@@ -174,13 +201,17 @@ export class HybridExecutionContext extends BaseExecutionContext implements Comm
174
201
  }
175
202
 
176
203
  toConfig(): HybridExecutionContextConfig {
204
+ const local = this.localCtx.toConfig()
177
205
  return {
178
206
  id: this.id,
179
207
  environment: 'hybrid',
180
208
  local: {
181
- cwd: this.localCtx.getCwd(),
182
- fsAccess: this.localCtx.hasFsAccess(),
183
- envVars: this.localCtx.getEnvVars(),
209
+ cwd: local.cwd,
210
+ fsAccess: local.fsAccess,
211
+ envVars: local.envVars,
212
+ capabilities: local.capabilities,
213
+ shell: local.shell,
214
+ maxOutputBytes: local.maxOutputBytes,
184
215
  },
185
216
  remotes: this.remoteTargets.map((t) => ({ ...t })),
186
217
  routingStrategy: this.routingStrategy,
@@ -18,6 +18,7 @@
18
18
  */
19
19
 
20
20
  export { BaseExecutionContext } from './base.js'
21
+ export { CommandCancellationUnsupportedError, RemoteExecutionBusyError } from './errors.js'
21
22
  export { ExecutionContextFactory } from './factory.js'
22
23
  export { HybridExecutionContext } from './hybrid.js'
23
24
  export type { HybridExecutionContextOptions } from './hybrid.js'