@assemblyline-agents/runtime 2.0.0 → 3.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 (389) hide show
  1. package/LICENSE +1 -1
  2. package/dist/a2a-client.d.ts +22 -0
  3. package/dist/a2a-client.d.ts.map +1 -0
  4. package/dist/a2a-client.js +211 -0
  5. package/dist/a2a-client.js.map +1 -0
  6. package/dist/agent-hook-context.d.ts +6 -1
  7. package/dist/agent-hook-context.d.ts.map +1 -1
  8. package/dist/agent-hook-context.js +24 -11
  9. package/dist/agent-hook-context.js.map +1 -1
  10. package/dist/agent-hook-evaluation.d.ts +6 -1
  11. package/dist/agent-hook-evaluation.d.ts.map +1 -1
  12. package/dist/agent-hook-evaluation.js +16 -5
  13. package/dist/agent-hook-evaluation.js.map +1 -1
  14. package/dist/agent-hook-run-lifecycle.d.ts +5 -1
  15. package/dist/agent-hook-run-lifecycle.d.ts.map +1 -1
  16. package/dist/agent-hook-run-lifecycle.js +11 -13
  17. package/dist/agent-hook-run-lifecycle.js.map +1 -1
  18. package/dist/agent-surface.d.ts +32 -0
  19. package/dist/agent-surface.d.ts.map +1 -0
  20. package/dist/agent-surface.js +85 -0
  21. package/dist/agent-surface.js.map +1 -0
  22. package/dist/attachments.d.ts +5 -1
  23. package/dist/attachments.d.ts.map +1 -1
  24. package/dist/attachments.js +58 -32
  25. package/dist/attachments.js.map +1 -1
  26. package/dist/audience.d.ts +30 -0
  27. package/dist/audience.d.ts.map +1 -0
  28. package/dist/audience.js +68 -0
  29. package/dist/audience.js.map +1 -0
  30. package/dist/automation-event-match.d.ts +8 -0
  31. package/dist/automation-event-match.d.ts.map +1 -0
  32. package/dist/automation-event-match.js +25 -0
  33. package/dist/automation-event-match.js.map +1 -0
  34. package/dist/background-review-trigger.d.ts.map +1 -1
  35. package/dist/background-review-trigger.js +14 -16
  36. package/dist/background-review-trigger.js.map +1 -1
  37. package/dist/background-review-types.d.ts +9 -2
  38. package/dist/background-review-types.d.ts.map +1 -1
  39. package/dist/background-review-worker.js +2 -2
  40. package/dist/background-review-worker.js.map +1 -1
  41. package/dist/background-review.d.ts.map +1 -1
  42. package/dist/background-review.js +27 -10
  43. package/dist/background-review.js.map +1 -1
  44. package/dist/builtin-tool-executors.d.ts +47 -21
  45. package/dist/builtin-tool-executors.d.ts.map +1 -1
  46. package/dist/builtin-tool-executors.js +293 -121
  47. package/dist/builtin-tool-executors.js.map +1 -1
  48. package/dist/builtin-tools.d.ts +16 -8
  49. package/dist/builtin-tools.d.ts.map +1 -1
  50. package/dist/builtin-tools.js +309 -70
  51. package/dist/builtin-tools.js.map +1 -1
  52. package/dist/capability-catalog.d.ts +3 -2
  53. package/dist/capability-catalog.d.ts.map +1 -1
  54. package/dist/capability-catalog.js +10 -6
  55. package/dist/capability-catalog.js.map +1 -1
  56. package/dist/capability-inspection.d.ts +31 -0
  57. package/dist/capability-inspection.d.ts.map +1 -0
  58. package/dist/capability-inspection.js +58 -0
  59. package/dist/capability-inspection.js.map +1 -0
  60. package/dist/channel-http-util.d.ts.map +1 -1
  61. package/dist/channel-http-util.js +6 -2
  62. package/dist/channel-http-util.js.map +1 -1
  63. package/dist/channel-ingress.d.ts +16 -1
  64. package/dist/channel-ingress.d.ts.map +1 -1
  65. package/dist/channel-ingress.js +117 -7
  66. package/dist/channel-ingress.js.map +1 -1
  67. package/dist/channel.d.ts +58 -2
  68. package/dist/channel.d.ts.map +1 -1
  69. package/dist/checkpoints.d.ts +1 -1
  70. package/dist/checkpoints.d.ts.map +1 -1
  71. package/dist/checkpoints.js +6 -6
  72. package/dist/checkpoints.js.map +1 -1
  73. package/dist/child-process-env.d.ts +7 -0
  74. package/dist/child-process-env.d.ts.map +1 -0
  75. package/dist/child-process-env.js +41 -0
  76. package/dist/child-process-env.js.map +1 -0
  77. package/dist/computer-relay-protocol.d.ts.map +1 -1
  78. package/dist/computer-relay-protocol.js +4 -4
  79. package/dist/computer-relay-protocol.js.map +1 -1
  80. package/dist/concurrency.d.ts +1 -1
  81. package/dist/concurrency.js +1 -1
  82. package/dist/connection-auth-util.d.ts +1 -0
  83. package/dist/connection-auth-util.d.ts.map +1 -1
  84. package/dist/connection-auth-util.js +1 -1
  85. package/dist/connection-auth-util.js.map +1 -1
  86. package/dist/connection-clients.d.ts +11 -3
  87. package/dist/connection-clients.d.ts.map +1 -1
  88. package/dist/connection-clients.js +71 -8
  89. package/dist/connection-clients.js.map +1 -1
  90. package/dist/connection-event-store.d.ts +98 -0
  91. package/dist/connection-event-store.d.ts.map +1 -0
  92. package/dist/connection-event-store.js +162 -0
  93. package/dist/connection-event-store.js.map +1 -0
  94. package/dist/connection-events.d.ts +51 -0
  95. package/dist/connection-events.d.ts.map +1 -0
  96. package/dist/connection-events.js +343 -0
  97. package/dist/connection-events.js.map +1 -0
  98. package/dist/connection-stores.d.ts +3 -0
  99. package/dist/connection-stores.d.ts.map +1 -1
  100. package/dist/connection-stores.js +4 -1
  101. package/dist/connection-stores.js.map +1 -1
  102. package/dist/connection-util.d.ts +3 -2
  103. package/dist/connection-util.d.ts.map +1 -1
  104. package/dist/connection-util.js +18 -1
  105. package/dist/connection-util.js.map +1 -1
  106. package/dist/connections.d.ts +54 -5
  107. package/dist/connections.d.ts.map +1 -1
  108. package/dist/connections.js +342 -53
  109. package/dist/connections.js.map +1 -1
  110. package/dist/context-policy-util.d.ts.map +1 -1
  111. package/dist/context-policy-util.js +1 -3
  112. package/dist/context-policy-util.js.map +1 -1
  113. package/dist/conversation-context.d.ts +6 -2
  114. package/dist/conversation-context.d.ts.map +1 -1
  115. package/dist/conversation-context.js +45 -12
  116. package/dist/conversation-context.js.map +1 -1
  117. package/dist/conversation-transcript.d.ts +8 -5
  118. package/dist/conversation-transcript.d.ts.map +1 -1
  119. package/dist/conversation-transcript.js +24 -5
  120. package/dist/conversation-transcript.js.map +1 -1
  121. package/dist/conversation-turn-coalescing.d.ts +22 -0
  122. package/dist/conversation-turn-coalescing.d.ts.map +1 -0
  123. package/dist/conversation-turn-coalescing.js +121 -0
  124. package/dist/conversation-turn-coalescing.js.map +1 -0
  125. package/dist/conversation-turn-dispatch.d.ts +5 -2
  126. package/dist/conversation-turn-dispatch.d.ts.map +1 -1
  127. package/dist/conversation-turn-dispatch.js +59 -22
  128. package/dist/conversation-turn-dispatch.js.map +1 -1
  129. package/dist/default-outbound-route.d.ts +29 -0
  130. package/dist/default-outbound-route.d.ts.map +1 -0
  131. package/dist/default-outbound-route.js +105 -0
  132. package/dist/default-outbound-route.js.map +1 -0
  133. package/dist/delivery-artifacts.d.ts +2 -0
  134. package/dist/delivery-artifacts.d.ts.map +1 -0
  135. package/dist/delivery-artifacts.js +34 -0
  136. package/dist/delivery-artifacts.js.map +1 -0
  137. package/dist/delivery-files.d.ts +5 -0
  138. package/dist/delivery-files.d.ts.map +1 -0
  139. package/dist/delivery-files.js +33 -0
  140. package/dist/delivery-files.js.map +1 -0
  141. package/dist/delivery-queue.d.ts +21 -1
  142. package/dist/delivery-queue.d.ts.map +1 -1
  143. package/dist/delivery-queue.js +144 -1
  144. package/dist/delivery-queue.js.map +1 -1
  145. package/dist/delivery-send.d.ts +2 -2
  146. package/dist/delivery-send.d.ts.map +1 -1
  147. package/dist/delivery-send.js +256 -100
  148. package/dist/delivery-send.js.map +1 -1
  149. package/dist/durable-files.d.ts +37 -0
  150. package/dist/durable-files.d.ts.map +1 -0
  151. package/dist/durable-files.js +63 -0
  152. package/dist/durable-files.js.map +1 -0
  153. package/dist/harness-loop.d.ts +6 -1
  154. package/dist/harness-loop.d.ts.map +1 -1
  155. package/dist/harness-loop.js +110 -25
  156. package/dist/harness-loop.js.map +1 -1
  157. package/dist/heic-transcode-worker.d.ts +2 -0
  158. package/dist/heic-transcode-worker.d.ts.map +1 -0
  159. package/dist/heic-transcode-worker.js +59 -0
  160. package/dist/heic-transcode-worker.js.map +1 -0
  161. package/dist/heic-transcode.d.ts +18 -0
  162. package/dist/heic-transcode.d.ts.map +1 -0
  163. package/dist/heic-transcode.js +99 -0
  164. package/dist/heic-transcode.js.map +1 -0
  165. package/dist/human-input-routing.d.ts +22 -0
  166. package/dist/human-input-routing.d.ts.map +1 -0
  167. package/dist/human-input-routing.js +26 -0
  168. package/dist/human-input-routing.js.map +1 -0
  169. package/dist/in-memory-conversation-turn-store.d.ts +3 -1
  170. package/dist/in-memory-conversation-turn-store.d.ts.map +1 -1
  171. package/dist/in-memory-conversation-turn-store.js +41 -2
  172. package/dist/in-memory-conversation-turn-store.js.map +1 -1
  173. package/dist/in-memory-run-store.d.ts +8 -0
  174. package/dist/in-memory-run-store.d.ts.map +1 -1
  175. package/dist/in-memory-run-store.js +28 -5
  176. package/dist/in-memory-run-store.js.map +1 -1
  177. package/dist/in-memory-sandbox-session-store.d.ts +2 -1
  178. package/dist/in-memory-sandbox-session-store.d.ts.map +1 -1
  179. package/dist/in-memory-sandbox-session-store.js +65 -1
  180. package/dist/in-memory-sandbox-session-store.js.map +1 -1
  181. package/dist/in-memory-small-stores.d.ts +18 -1
  182. package/dist/in-memory-small-stores.d.ts.map +1 -1
  183. package/dist/in-memory-small-stores.js +107 -1
  184. package/dist/in-memory-small-stores.js.map +1 -1
  185. package/dist/in-memory-workspace-store.d.ts +40 -0
  186. package/dist/in-memory-workspace-store.d.ts.map +1 -0
  187. package/dist/in-memory-workspace-store.js +370 -0
  188. package/dist/in-memory-workspace-store.js.map +1 -0
  189. package/dist/index.d.ts +73 -36
  190. package/dist/index.d.ts.map +1 -1
  191. package/dist/index.js +390 -258
  192. package/dist/index.js.map +1 -1
  193. package/dist/json-util.d.ts +8 -0
  194. package/dist/json-util.d.ts.map +1 -1
  195. package/dist/json-util.js +8 -0
  196. package/dist/json-util.js.map +1 -1
  197. package/dist/local-sandbox.d.ts +3 -2
  198. package/dist/local-sandbox.d.ts.map +1 -1
  199. package/dist/local-sandbox.js +69 -25
  200. package/dist/local-sandbox.js.map +1 -1
  201. package/dist/mcp-client.d.ts.map +1 -1
  202. package/dist/mcp-client.js +3 -2
  203. package/dist/mcp-client.js.map +1 -1
  204. package/dist/media-processor.d.ts +42 -0
  205. package/dist/media-processor.d.ts.map +1 -0
  206. package/dist/media-processor.js +2 -0
  207. package/dist/media-processor.js.map +1 -0
  208. package/dist/model-media.d.ts +13 -1
  209. package/dist/model-media.d.ts.map +1 -1
  210. package/dist/model-media.js +81 -18
  211. package/dist/model-media.js.map +1 -1
  212. package/dist/model-visible-interrupts.d.ts +10 -0
  213. package/dist/model-visible-interrupts.d.ts.map +1 -0
  214. package/dist/model-visible-interrupts.js +41 -0
  215. package/dist/model-visible-interrupts.js.map +1 -0
  216. package/dist/operator-controls.d.ts.map +1 -1
  217. package/dist/operator-controls.js +16 -3
  218. package/dist/operator-controls.js.map +1 -1
  219. package/dist/outbound-http-policy.d.ts +7 -0
  220. package/dist/outbound-http-policy.d.ts.map +1 -0
  221. package/dist/outbound-http-policy.js +155 -0
  222. package/dist/outbound-http-policy.js.map +1 -0
  223. package/dist/public-api.d.ts +21 -0
  224. package/dist/public-api.d.ts.map +1 -0
  225. package/dist/public-api.js +19 -0
  226. package/dist/public-api.js.map +1 -0
  227. package/dist/replay-resume.d.ts +8 -0
  228. package/dist/replay-resume.d.ts.map +1 -1
  229. package/dist/replay-resume.js +141 -11
  230. package/dist/replay-resume.js.map +1 -1
  231. package/dist/resource-projection-util.d.ts +1 -1
  232. package/dist/resource-projection-util.d.ts.map +1 -1
  233. package/dist/resource-projection-util.js +21 -10
  234. package/dist/resource-projection-util.js.map +1 -1
  235. package/dist/resource-projection.d.ts +8 -1
  236. package/dist/resource-projection.d.ts.map +1 -1
  237. package/dist/resource-projection.js +229 -29
  238. package/dist/resource-projection.js.map +1 -1
  239. package/dist/run-heartbeat.d.ts +12 -2
  240. package/dist/run-heartbeat.d.ts.map +1 -1
  241. package/dist/run-heartbeat.js +31 -4
  242. package/dist/run-heartbeat.js.map +1 -1
  243. package/dist/run-outcome.d.ts +9 -5
  244. package/dist/run-outcome.d.ts.map +1 -1
  245. package/dist/run-outcome.js +14 -5
  246. package/dist/run-outcome.js.map +1 -1
  247. package/dist/run-recovery.d.ts +19 -3
  248. package/dist/run-recovery.d.ts.map +1 -1
  249. package/dist/run-recovery.js +205 -34
  250. package/dist/run-recovery.js.map +1 -1
  251. package/dist/run-replay.d.ts +35 -0
  252. package/dist/run-replay.d.ts.map +1 -0
  253. package/dist/run-replay.js +193 -0
  254. package/dist/run-replay.js.map +1 -0
  255. package/dist/run-result.d.ts +4 -0
  256. package/dist/run-result.d.ts.map +1 -0
  257. package/dist/run-result.js +18 -0
  258. package/dist/run-result.js.map +1 -0
  259. package/dist/run-timeline-query.d.ts +38 -0
  260. package/dist/run-timeline-query.d.ts.map +1 -0
  261. package/dist/run-timeline-query.js +99 -0
  262. package/dist/run-timeline-query.js.map +1 -0
  263. package/dist/runs-query.d.ts.map +1 -1
  264. package/dist/runs-query.js +11 -3
  265. package/dist/runs-query.js.map +1 -1
  266. package/dist/runtime-bundle.d.ts.map +1 -1
  267. package/dist/runtime-bundle.js +2 -0
  268. package/dist/runtime-bundle.js.map +1 -1
  269. package/dist/runtime-learning.d.ts +15 -13
  270. package/dist/runtime-learning.d.ts.map +1 -1
  271. package/dist/runtime-learning.js +79 -58
  272. package/dist/runtime-learning.js.map +1 -1
  273. package/dist/runtime-types.d.ts +68 -9
  274. package/dist/runtime-types.d.ts.map +1 -1
  275. package/dist/sandbox-fs.d.ts +8 -5
  276. package/dist/sandbox-fs.d.ts.map +1 -1
  277. package/dist/sandbox-fs.js +30 -31
  278. package/dist/sandbox-fs.js.map +1 -1
  279. package/dist/sandbox-handle.d.ts +25 -0
  280. package/dist/sandbox-handle.d.ts.map +1 -0
  281. package/dist/sandbox-handle.js +120 -0
  282. package/dist/sandbox-handle.js.map +1 -0
  283. package/dist/sandbox-session-identity.d.ts +14 -0
  284. package/dist/sandbox-session-identity.d.ts.map +1 -0
  285. package/dist/sandbox-session-identity.js +33 -0
  286. package/dist/sandbox-session-identity.js.map +1 -0
  287. package/dist/sandbox-session-key.d.ts.map +1 -1
  288. package/dist/sandbox-session-key.js +12 -9
  289. package/dist/sandbox-session-key.js.map +1 -1
  290. package/dist/sandbox-session-lifecycle.d.ts.map +1 -1
  291. package/dist/sandbox-session-lifecycle.js +187 -44
  292. package/dist/sandbox-session-lifecycle.js.map +1 -1
  293. package/dist/sandbox-snapshot-policy.js +3 -3
  294. package/dist/sandbox-snapshot-policy.js.map +1 -1
  295. package/dist/sandbox-state-sync.d.ts +4 -2
  296. package/dist/sandbox-state-sync.d.ts.map +1 -1
  297. package/dist/sandbox-state-sync.js +47 -15
  298. package/dist/sandbox-state-sync.js.map +1 -1
  299. package/dist/sandbox-sync-worker.d.ts +11 -2
  300. package/dist/sandbox-sync-worker.d.ts.map +1 -1
  301. package/dist/sandbox-sync-worker.js +143 -31
  302. package/dist/sandbox-sync-worker.js.map +1 -1
  303. package/dist/schedule-execution.d.ts.map +1 -1
  304. package/dist/schedule-execution.js +127 -23
  305. package/dist/schedule-execution.js.map +1 -1
  306. package/dist/sdk-api-client.d.ts +17 -0
  307. package/dist/sdk-api-client.d.ts.map +1 -0
  308. package/dist/sdk-api-client.js +55 -0
  309. package/dist/sdk-api-client.js.map +1 -0
  310. package/dist/security.js +2 -2
  311. package/dist/security.js.map +1 -1
  312. package/dist/self-improvement.d.ts +5 -1
  313. package/dist/self-improvement.d.ts.map +1 -1
  314. package/dist/self-improvement.js +28 -14
  315. package/dist/self-improvement.js.map +1 -1
  316. package/dist/skill-bundles.d.ts +55 -0
  317. package/dist/skill-bundles.d.ts.map +1 -0
  318. package/dist/skill-bundles.js +134 -0
  319. package/dist/skill-bundles.js.map +1 -0
  320. package/dist/source-loader.js +7 -7
  321. package/dist/source-loader.js.map +1 -1
  322. package/dist/state-store-helpers.d.ts +2 -0
  323. package/dist/state-store-helpers.d.ts.map +1 -1
  324. package/dist/state-store-helpers.js +11 -1
  325. package/dist/state-store-helpers.js.map +1 -1
  326. package/dist/state-stores.d.ts +1 -0
  327. package/dist/state-stores.d.ts.map +1 -1
  328. package/dist/state-stores.js +26 -7
  329. package/dist/state-stores.js.map +1 -1
  330. package/dist/state.d.ts +305 -5
  331. package/dist/state.d.ts.map +1 -1
  332. package/dist/state.js +41 -2
  333. package/dist/state.js.map +1 -1
  334. package/dist/storage.d.ts +47 -2
  335. package/dist/storage.d.ts.map +1 -1
  336. package/dist/storage.js +158 -6
  337. package/dist/storage.js.map +1 -1
  338. package/dist/subagent-execution.d.ts +1 -1
  339. package/dist/subagent-execution.d.ts.map +1 -1
  340. package/dist/subagent-execution.js +10 -12
  341. package/dist/subagent-execution.js.map +1 -1
  342. package/dist/target-instructions.js +1 -1
  343. package/dist/target-instructions.js.map +1 -1
  344. package/dist/tool-apis.d.ts.map +1 -1
  345. package/dist/tool-apis.js +33 -9
  346. package/dist/tool-apis.js.map +1 -1
  347. package/dist/tool-execution.d.ts +22 -12
  348. package/dist/tool-execution.d.ts.map +1 -1
  349. package/dist/tool-execution.js +368 -238
  350. package/dist/tool-execution.js.map +1 -1
  351. package/dist/turn-media.d.ts +29 -0
  352. package/dist/turn-media.d.ts.map +1 -0
  353. package/dist/turn-media.js +233 -0
  354. package/dist/turn-media.js.map +1 -0
  355. package/dist/usage-reconciliation.d.ts +1 -1
  356. package/dist/usage-reconciliation.js +1 -1
  357. package/dist/workspace-id.d.ts +10 -0
  358. package/dist/workspace-id.d.ts.map +1 -0
  359. package/dist/workspace-id.js +55 -0
  360. package/dist/workspace-id.js.map +1 -0
  361. package/dist/workspace-manifest.d.ts +24 -0
  362. package/dist/workspace-manifest.d.ts.map +1 -0
  363. package/dist/workspace-manifest.js +111 -0
  364. package/dist/workspace-manifest.js.map +1 -0
  365. package/dist/workspace-operation-limiter.d.ts +16 -0
  366. package/dist/workspace-operation-limiter.d.ts.map +1 -0
  367. package/dist/workspace-operation-limiter.js +31 -0
  368. package/dist/workspace-operation-limiter.js.map +1 -0
  369. package/dist/workspace-operations.d.ts +145 -0
  370. package/dist/workspace-operations.d.ts.map +1 -0
  371. package/dist/workspace-operations.js +420 -0
  372. package/dist/workspace-operations.js.map +1 -0
  373. package/dist/workspace-runtime.d.ts +110 -0
  374. package/dist/workspace-runtime.d.ts.map +1 -0
  375. package/dist/workspace-runtime.js +240 -0
  376. package/dist/workspace-runtime.js.map +1 -0
  377. package/dist/workspace-search.d.ts +55 -0
  378. package/dist/workspace-search.d.ts.map +1 -0
  379. package/dist/workspace-search.js +262 -0
  380. package/dist/workspace-search.js.map +1 -0
  381. package/dist/workspace-service.d.ts +173 -0
  382. package/dist/workspace-service.d.ts.map +1 -0
  383. package/dist/workspace-service.js +619 -0
  384. package/dist/workspace-service.js.map +1 -0
  385. package/dist/workspace.d.ts +7 -0
  386. package/dist/workspace.d.ts.map +1 -0
  387. package/dist/workspace.js +7 -0
  388. package/dist/workspace.js.map +1 -0
  389. package/package.json +7 -4
@@ -0,0 +1,619 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { shellQuote } from "@assemblyline-agents/core";
3
+ import { resolveWorkspaceForRun, workspaceScopeKey } from "./workspace-id.js";
4
+ import { createWorkspaceManifest, DEFAULT_WORKSPACE_LIMITS, encodeWorkspaceManifest, normalizeWorkspaceRelativePath, parseWorkspaceManifest, scanWorkspaceEntries, workspaceManifestSha256 } from "./workspace-manifest.js";
5
+ import { buildWorkspaceSearchChunks, DEFAULT_WORKSPACE_SEARCH_LIMITS, directWorkspaceSearch, embedWorkspaceSearchQuery, mergeWorkspaceSearchResults } from "./workspace-search.js";
6
+ import { WorkspaceOperations } from "./workspace-operations.js";
7
+ import { SlidingWindowWorkspaceOperationLimiter } from "./workspace-operation-limiter.js";
8
+ import { RESERVED_WORKSPACE_DIRS } from "./sandbox-fs.js";
9
+ export class WorkspaceService {
10
+ host;
11
+ limits;
12
+ searchLimits;
13
+ operationLimiter;
14
+ maxCheckpoints;
15
+ constructor(host) {
16
+ this.host = host;
17
+ this.limits = host.limits ?? DEFAULT_WORKSPACE_LIMITS;
18
+ this.searchLimits = host.searchLimits ?? DEFAULT_WORKSPACE_SEARCH_LIMITS;
19
+ this.operationLimiter = host.operationLimiter ?? new SlidingWindowWorkspaceOperationLimiter();
20
+ this.maxCheckpoints = Math.max(1, Math.floor(host.maxCheckpoints ?? 1_000));
21
+ }
22
+ async resolveForRun(run) {
23
+ return resolveWorkspaceForRun(this.host.state, this.host.agentScope, run);
24
+ }
25
+ async loadManifest(workspace, versionId = workspace.headVersionId) {
26
+ if (versionId === null) {
27
+ return { manifest: createWorkspaceManifest(workspace.id, [], this.host.now().toISOString()) };
28
+ }
29
+ if (!versionId)
30
+ versionId = workspace.headVersionId;
31
+ if (!versionId)
32
+ return { manifest: createWorkspaceManifest(workspace.id, [], this.host.now().toISOString()) };
33
+ const version = await this.host.state.getWorkspaceVersion(versionId);
34
+ if (!version || version.workspaceId !== workspace.id)
35
+ throw new Error(`Workspace version not found: ${versionId}`);
36
+ const bytes = await this.host.blob.get(version.manifestBlobKey);
37
+ if (!bytes)
38
+ throw new Error(`Workspace manifest blob is missing: ${version.manifestBlobKey}`);
39
+ const sha256 = workspaceManifestSha256(bytes);
40
+ if (sha256 !== version.manifestSha256)
41
+ throw new Error(`Workspace manifest hash mismatch: ${version.id}`);
42
+ const manifest = parseWorkspaceManifest(bytes);
43
+ if (manifest.workspaceId !== workspace.id && !await this.isInheritedForkManifest(workspace, version)) {
44
+ throw new Error(`Workspace manifest belongs to another workspace: ${version.id}`);
45
+ }
46
+ if (manifest.fileCount !== version.fileCount || manifest.totalBytes !== version.totalBytes) {
47
+ throw new Error(`Workspace version totals do not match its manifest: ${version.id}`);
48
+ }
49
+ return { version, manifest };
50
+ }
51
+ async hydrate(run, sandbox, sessionKey) {
52
+ const workspace = await this.resolveForRun(run);
53
+ this.operationLimiter.assertAllowed("hydrate", workspace.id, this.host.now());
54
+ const session = await this.host.state.getSandboxSession(sessionKey);
55
+ if (session?.dirty) {
56
+ const sessionWorkspaceId = stringValue(session.manifest.workspaceId);
57
+ if (sessionWorkspaceId !== workspace.id) {
58
+ throw new Error(`Dirty sandbox belongs to workspace ${sessionWorkspaceId ?? "unknown"}, not ${workspace.id}.`);
59
+ }
60
+ const current = await this.loadManifest(workspace, stringValue(session.manifest.baseVersionId) ?? workspace.headVersionId);
61
+ return { workspace, ...current, skippedDirty: true };
62
+ }
63
+ const current = await this.loadManifest(workspace);
64
+ this.assertVersionSize(current.manifest, "hydrate");
65
+ await clearWorkspaceTree(sandbox);
66
+ for (const entry of current.manifest.entries) {
67
+ const contents = await this.host.blob.get(entry.blobKey);
68
+ if (!contents)
69
+ throw new Error(`Workspace content blob is missing: ${entry.blobKey}`);
70
+ if (contents.byteLength !== entry.sizeBytes || workspaceManifestSha256(contents) !== entry.sha256) {
71
+ throw new Error(`Workspace content failed integrity verification: ${entry.path}`);
72
+ }
73
+ await sandbox.writeFile(`/workspace/${entry.path}`, contents);
74
+ if ((entry.mode & 0o111) !== 0) {
75
+ await sandbox.shell(`chmod 755 -- ${shellQuote(entry.path)}`, { cwd: "/workspace" });
76
+ }
77
+ }
78
+ const manifest = {
79
+ ...(session?.manifest ?? {}),
80
+ workspaceId: workspace.id,
81
+ baseVersionId: workspace.headVersionId ?? null,
82
+ workspaceManifestSha256: current.version?.manifestSha256 ?? null,
83
+ sandboxRoot: sandbox.root,
84
+ sessionKey
85
+ };
86
+ await this.host.state.updateSandboxSession(sessionKey, {
87
+ runId: run.id,
88
+ sandboxId: sandbox.id,
89
+ state: "clean",
90
+ dirty: false,
91
+ lastSeenAt: this.host.now().toISOString(),
92
+ manifest
93
+ });
94
+ await this.host.state.appendEvent(run.id, "workspace.hydrated", {
95
+ workspaceId: workspace.id,
96
+ versionId: workspace.headVersionId ?? null,
97
+ fileCount: current.manifest.fileCount,
98
+ totalBytes: current.manifest.totalBytes
99
+ });
100
+ return { workspace, ...current, skippedDirty: false };
101
+ }
102
+ async sync(input) {
103
+ const workspace = await this.host.state.getWorkspace(input.workspaceId);
104
+ if (!workspace)
105
+ throw new Error(`Workspace not found: ${input.workspaceId}`);
106
+ const selected = await this.resolveForRun(input.run);
107
+ if (selected.id !== workspace.id)
108
+ throw new Error(`Sandbox workspace does not match run workspace: ${workspace.id}`);
109
+ this.operationLimiter.assertAllowed("sync", workspace.id, this.host.now());
110
+ const base = await this.loadManifest(workspace, input.baseVersionId);
111
+ const rawEntries = await listWorkspaceFiles(input.sandbox);
112
+ const scanned = scanWorkspaceEntries(rawEntries, this.limits);
113
+ const baseByPath = new Map(base.manifest.entries.map((entry) => [entry.path, entry]));
114
+ const scannedPaths = new Set(scanned.map((entry) => entry.path));
115
+ const deletedPaths = base.manifest.entries.filter((entry) => !scannedPaths.has(entry.path)).map((entry) => entry.path);
116
+ const changedPaths = [];
117
+ const entries = [];
118
+ for (const file of scanned) {
119
+ const previous = baseByPath.get(file.path);
120
+ if (previous && previous.sha256 === file.sha256 && previous.mode === file.mode) {
121
+ entries.push(previous);
122
+ continue;
123
+ }
124
+ const blobKey = workspaceContentBlobKey(file.sha256);
125
+ const blob = await this.host.blob.put(blobKey, file.contents, { contentType: file.contentType, visibility: "private" });
126
+ if (blob.sha256 !== file.sha256 || blob.size !== file.sizeBytes)
127
+ throw new Error(`Workspace blob write failed integrity verification: ${file.path}`);
128
+ changedPaths.push(file.path);
129
+ entries.push({
130
+ path: file.path,
131
+ blobKey: blob.key,
132
+ sha256: file.sha256,
133
+ sizeBytes: file.sizeBytes,
134
+ contentType: file.contentType,
135
+ mode: file.mode
136
+ });
137
+ }
138
+ const manifest = createWorkspaceManifest(workspace.id, entries, this.host.now().toISOString());
139
+ const manifestBytes = encodeWorkspaceManifest(manifest);
140
+ const manifestSha256 = workspaceManifestSha256(manifestBytes);
141
+ const manifestBlob = await this.host.blob.put(workspaceManifestBlobKey(manifestSha256), manifestBytes, {
142
+ contentType: "application/json",
143
+ visibility: "private"
144
+ });
145
+ if (manifestBlob.sha256 !== manifestSha256)
146
+ throw new Error(`Workspace manifest write failed integrity verification: ${workspace.id}`);
147
+ const committed = await this.host.state.commitWorkspaceVersion({
148
+ workspaceId: workspace.id,
149
+ expectedHeadVersionId: input.baseVersionId,
150
+ manifestBlobKey: manifestBlob.key,
151
+ manifestSha256,
152
+ fileCount: manifest.fileCount,
153
+ totalBytes: manifest.totalBytes,
154
+ reason: "sync",
155
+ runId: input.run.id,
156
+ idempotencyKey: `workspace-sync:${input.syncJobId}`,
157
+ metadata: { changedPaths, deletedPaths }
158
+ });
159
+ const committedManifest = committed.created
160
+ ? manifest
161
+ : (await this.loadManifest(committed.workspace, committed.version.id)).manifest;
162
+ const committedChangedPaths = committed.created
163
+ ? changedPaths
164
+ : stringArray(committed.version.metadata.changedPaths);
165
+ const committedDeletedPaths = committed.created
166
+ ? deletedPaths
167
+ : stringArray(committed.version.metadata.deletedPaths);
168
+ await this.host.state.appendEvent(input.run.id, "workspace.version_created", {
169
+ workspaceId: workspace.id,
170
+ versionId: committed.version.id,
171
+ parentVersionId: committed.version.parentVersionId ?? null,
172
+ fileCount: committedManifest.fileCount,
173
+ totalBytes: committedManifest.totalBytes,
174
+ changedPaths: committedChangedPaths,
175
+ deletedPaths: committedDeletedPaths,
176
+ created: committed.created
177
+ });
178
+ await this.tryIndexVersion(workspace.id, committed.version.id, input.run.id);
179
+ return {
180
+ ...committed,
181
+ manifest: committedManifest,
182
+ changedPaths: committedChangedPaths,
183
+ deletedPaths: committedDeletedPaths
184
+ };
185
+ }
186
+ async status(workspaceId) {
187
+ const workspace = await this.requireWorkspace(workspaceId);
188
+ const versions = await this.host.state.listWorkspaceVersions(workspace.id, { limit: 10_000 });
189
+ const checkpoints = await this.host.state.listWorkspaceCheckpoints(workspace.id);
190
+ const status = { workspace, checkpoints, versionCount: versions.length };
191
+ if (workspace.headVersionId) {
192
+ const head = versions.find((version) => version.id === workspace.headVersionId)
193
+ ?? await this.host.state.getWorkspaceVersion(workspace.headVersionId);
194
+ if (!head)
195
+ throw new Error(`Workspace head version is missing: ${workspace.headVersionId}`);
196
+ status.head = head;
197
+ }
198
+ return status;
199
+ }
200
+ async statusForRun(run) {
201
+ return this.status((await this.resolveForRun(run)).id);
202
+ }
203
+ retention(workspaceId, input = {}) {
204
+ return this.operations().retention(workspaceId, input);
205
+ }
206
+ reachability() {
207
+ return this.operations().reachability();
208
+ }
209
+ garbageCollect(input = {}) {
210
+ return this.operations().garbageCollect(input);
211
+ }
212
+ usage() {
213
+ return this.operations().usage();
214
+ }
215
+ diagnostics() {
216
+ return this.operations().diagnostics();
217
+ }
218
+ verify(workspaceId) {
219
+ return this.operations().verify(workspaceId);
220
+ }
221
+ repairBlob(input) {
222
+ return this.operations().repairBlob(input);
223
+ }
224
+ repairHead(input) {
225
+ return this.operations().repairHead(input);
226
+ }
227
+ async readFile(workspaceId, path, versionId) {
228
+ const workspace = await this.requireWorkspace(workspaceId);
229
+ const current = await this.loadManifest(workspace, versionId);
230
+ if (!current.version)
231
+ throw new Error(`Workspace has no committed version: ${workspace.id}`);
232
+ const normalized = normalizeWorkspaceRelativePath(path);
233
+ const entry = current.manifest.entries.find((candidate) => candidate.path === normalized);
234
+ if (!entry)
235
+ throw new Error(`Workspace file not found: ${normalized}`);
236
+ const contents = await this.host.blob.get(entry.blobKey);
237
+ if (!contents)
238
+ throw new Error(`Workspace content blob is missing: ${entry.blobKey}`);
239
+ if (contents.byteLength !== entry.sizeBytes || workspaceManifestSha256(contents) !== entry.sha256) {
240
+ throw new Error(`Workspace content failed integrity verification: ${entry.path}`);
241
+ }
242
+ return { workspaceId: workspace.id, versionId: current.version.id, entry, contents };
243
+ }
244
+ async listFiles(workspaceId, input = {}) {
245
+ const workspace = await this.requireWorkspace(workspaceId);
246
+ const current = await this.loadManifest(workspace, input.versionId);
247
+ if (!current.version)
248
+ throw new Error(`Workspace has no committed version: ${workspace.id}`);
249
+ const prefix = input.prefix ? normalizeWorkspaceRelativePath(input.prefix).replace(/\/$/u, "") : undefined;
250
+ const entries = prefix
251
+ ? current.manifest.entries.filter((entry) => entry.path === prefix || entry.path.startsWith(`${prefix}/`))
252
+ : current.manifest.entries;
253
+ return { workspaceId: workspace.id, versionId: current.version.id, entries };
254
+ }
255
+ async grep(workspaceId, pattern, input = {}) {
256
+ if (!pattern || pattern.length > 1_000)
257
+ throw new Error("Workspace grep pattern must contain 1 to 1000 characters.");
258
+ const workspace = await this.requireWorkspace(workspaceId);
259
+ const current = await this.loadManifest(workspace, input.versionId);
260
+ if (!current.version)
261
+ throw new Error(`Workspace has no committed version: ${workspace.id}`);
262
+ const flags = input.caseSensitive === true ? "g" : "gi";
263
+ const source = input.literal === false ? pattern : escapeRegExp(pattern);
264
+ let matcher;
265
+ try {
266
+ matcher = new RegExp(source, flags);
267
+ }
268
+ catch (error) {
269
+ throw new Error(`Invalid workspace grep pattern: ${error instanceof Error ? error.message : String(error)}`);
270
+ }
271
+ const limit = boundedSearchLimit(input.limit, 100);
272
+ const results = [];
273
+ for (const entry of current.manifest.entries) {
274
+ if (!entry.contentType.startsWith("text/") && entry.contentType !== "application/json")
275
+ continue;
276
+ const file = await this.readFile(workspace.id, entry.path, current.version.id);
277
+ const lines = Buffer.from(file.contents).toString("utf8").split("\n");
278
+ for (let index = 0; index < lines.length; index += 1) {
279
+ const line = lines[index] ?? "";
280
+ matcher.lastIndex = 0;
281
+ const match = matcher.exec(line);
282
+ if (!match)
283
+ continue;
284
+ results.push({ path: entry.path, versionId: current.version.id, line: index + 1, column: match.index + 1, text: line });
285
+ if (results.length >= limit)
286
+ return { workspaceId: workspace.id, versionId: current.version.id, results };
287
+ }
288
+ }
289
+ return { workspaceId: workspace.id, versionId: current.version.id, results };
290
+ }
291
+ async indexVersion(workspaceId, versionId) {
292
+ const workspace = await this.requireWorkspace(workspaceId);
293
+ const current = await this.loadManifest(workspace, versionId);
294
+ if (!current.version)
295
+ throw new Error(`Workspace has no committed version: ${workspace.id}`);
296
+ const built = await buildWorkspaceSearchChunks({
297
+ workspaceId: workspace.id,
298
+ versionId: current.version.id,
299
+ manifest: current.manifest,
300
+ blob: this.host.blob,
301
+ now: this.host.now(),
302
+ limits: this.searchLimits,
303
+ ...(this.host.embeddingProvider ? { embeddingProvider: this.host.embeddingProvider } : {})
304
+ });
305
+ const index = await this.host.state.replaceWorkspaceSearchChunks({
306
+ workspaceId: workspace.id,
307
+ versionId: current.version.id,
308
+ chunks: built.chunks,
309
+ ...(built.embeddingModel ? { embeddingModel: built.embeddingModel } : {})
310
+ });
311
+ return { ...index, excluded: built.excluded };
312
+ }
313
+ async search(workspaceId, query, input = {}) {
314
+ if (!query.trim() || query.length > 2_000)
315
+ throw new Error("Workspace search query must contain 1 to 2000 characters.");
316
+ const workspace = await this.requireWorkspace(workspaceId);
317
+ const current = await this.loadManifest(workspace, input.versionId);
318
+ if (!current.version)
319
+ throw new Error(`Workspace has no committed version: ${workspace.id}`);
320
+ const index = await this.host.state.getWorkspaceSearchIndex(workspace.id);
321
+ const indexStale = index?.versionId !== current.version.id;
322
+ if (indexStale) {
323
+ return {
324
+ workspaceId: workspace.id,
325
+ versionId: current.version.id,
326
+ ...(index ? { indexedVersionId: index.versionId } : {}),
327
+ indexStale: true,
328
+ results: await directWorkspaceSearch({
329
+ workspaceId: workspace.id,
330
+ versionId: current.version.id,
331
+ manifest: current.manifest,
332
+ blob: this.host.blob,
333
+ query,
334
+ ...(input.limit !== undefined ? { limit: input.limit } : {}),
335
+ limits: this.searchLimits
336
+ })
337
+ };
338
+ }
339
+ const fullText = await this.host.state.searchWorkspaceChunks({
340
+ workspaceId: workspace.id,
341
+ versionId: current.version.id,
342
+ query,
343
+ ...(input.limit !== undefined ? { limit: input.limit } : {})
344
+ });
345
+ let results = fullText;
346
+ if (this.host.embeddingProvider && index.embeddingModel) {
347
+ try {
348
+ const embedded = await embedWorkspaceSearchQuery(this.host.embeddingProvider, query);
349
+ const semantic = await this.host.state.searchWorkspaceEmbeddings({
350
+ workspaceId: workspace.id,
351
+ versionId: current.version.id,
352
+ embedding: embedded.embedding,
353
+ model: embedded.model,
354
+ ...(input.limit !== undefined ? { limit: input.limit } : {})
355
+ });
356
+ results = mergeWorkspaceSearchResults(fullText, semantic, input.limit);
357
+ }
358
+ catch {
359
+ results = fullText;
360
+ }
361
+ }
362
+ return {
363
+ workspaceId: workspace.id,
364
+ versionId: current.version.id,
365
+ indexedVersionId: index.versionId,
366
+ indexStale: false,
367
+ results
368
+ };
369
+ }
370
+ async createCheckpoint(input) {
371
+ const workspace = await this.requireWorkspace(input.workspaceId);
372
+ this.operationLimiter.assertAllowed("checkpoint", workspace.id, this.host.now());
373
+ if (!workspace.headVersionId)
374
+ throw new Error(`Workspace has no version to checkpoint: ${workspace.id}`);
375
+ const name = normalizeCheckpointName(input.name);
376
+ const head = await this.host.state.getWorkspaceVersion(workspace.headVersionId);
377
+ if (!head)
378
+ throw new Error(`Workspace head version is missing: ${workspace.headVersionId}`);
379
+ const current = await this.loadManifest(workspace, head.id);
380
+ this.assertVersionSize(current.manifest, "checkpoint");
381
+ const existingCheckpoint = await this.host.state.getWorkspaceCheckpoint(workspace.id, name);
382
+ if (!existingCheckpoint && (await this.host.state.listWorkspaceCheckpoints(workspace.id)).length >= this.maxCheckpoints) {
383
+ throw new Error(`Workspace checkpoint limit exceeded. Maximum is ${this.maxCheckpoints}.`);
384
+ }
385
+ const checkpoint = await this.host.state.createWorkspaceCheckpoint({
386
+ workspaceId: workspace.id,
387
+ name,
388
+ versionId: workspace.headVersionId,
389
+ ...(input.replace !== undefined ? { replace: input.replace } : {})
390
+ });
391
+ if (input.runId) {
392
+ await this.host.state.appendEvent(input.runId, "workspace.checkpoint_created", {
393
+ workspaceId: workspace.id,
394
+ checkpointId: checkpoint.id,
395
+ name: checkpoint.name,
396
+ versionId: checkpoint.versionId,
397
+ replaced: input.replace === true
398
+ });
399
+ }
400
+ return checkpoint;
401
+ }
402
+ async listCheckpoints(workspaceId) {
403
+ await this.requireWorkspace(workspaceId);
404
+ return this.host.state.listWorkspaceCheckpoints(workspaceId);
405
+ }
406
+ async getCheckpoint(workspaceId, name) {
407
+ await this.requireWorkspace(workspaceId);
408
+ return this.host.state.getWorkspaceCheckpoint(workspaceId, normalizeCheckpointName(name));
409
+ }
410
+ async deleteCheckpoint(input) {
411
+ await this.requireWorkspace(input.workspaceId);
412
+ const name = normalizeCheckpointName(input.name);
413
+ const deleted = await this.host.state.deleteWorkspaceCheckpoint(input.workspaceId, name);
414
+ if (deleted && input.runId) {
415
+ await this.host.state.appendEvent(input.runId, "workspace.checkpoint_deleted", {
416
+ workspaceId: input.workspaceId,
417
+ name
418
+ });
419
+ }
420
+ return deleted;
421
+ }
422
+ async restoreCheckpoint(input) {
423
+ const workspace = await this.requireWorkspace(input.workspaceId);
424
+ this.operationLimiter.assertAllowed("restore", workspace.id, this.host.now());
425
+ const name = normalizeCheckpointName(input.name);
426
+ const checkpoint = await this.host.state.getWorkspaceCheckpoint(workspace.id, name);
427
+ if (!checkpoint)
428
+ throw new Error(`Workspace checkpoint not found: ${name}`);
429
+ const source = await this.host.state.getWorkspaceVersion(checkpoint.versionId);
430
+ if (!source || source.workspaceId !== workspace.id)
431
+ throw new Error(`Workspace checkpoint version is missing: ${checkpoint.versionId}`);
432
+ const current = await this.loadManifest(workspace, source.id);
433
+ this.assertVersionSize(current.manifest, "restore");
434
+ const restored = await this.host.state.commitWorkspaceVersion({
435
+ workspaceId: workspace.id,
436
+ expectedHeadVersionId: workspace.headVersionId ?? null,
437
+ manifestBlobKey: source.manifestBlobKey,
438
+ manifestSha256: source.manifestSha256,
439
+ fileCount: source.fileCount,
440
+ totalBytes: source.totalBytes,
441
+ reason: "restore",
442
+ ...(input.runId ? { runId: input.runId } : {}),
443
+ sourceVersionId: source.id,
444
+ sourceCheckpointId: checkpoint.id,
445
+ idempotencyKey: input.idempotencyKey ?? `workspace-restore:${workspace.id}:${checkpoint.id}:${workspace.headVersionId ?? "empty"}:${randomUUID()}`,
446
+ metadata: { checkpointName: checkpoint.name }
447
+ });
448
+ if (input.runId) {
449
+ await this.host.state.appendEvent(input.runId, "workspace.restored", {
450
+ workspaceId: workspace.id,
451
+ checkpointId: checkpoint.id,
452
+ checkpointName: checkpoint.name,
453
+ sourceVersionId: source.id,
454
+ previousHeadVersionId: workspace.headVersionId ?? null,
455
+ versionId: restored.version.id,
456
+ created: restored.created
457
+ });
458
+ }
459
+ await this.tryIndexVersion(workspace.id, restored.version.id, input.runId);
460
+ return restored;
461
+ }
462
+ async fork(input) {
463
+ const sourceWorkspace = await this.requireWorkspace(input.sourceWorkspaceId);
464
+ this.operationLimiter.assertAllowed("fork", sourceWorkspace.id, this.host.now());
465
+ if (sourceWorkspace.scope.tenantId !== input.tenantId) {
466
+ throw new Error(`Workspace is outside the tenant scope: ${sourceWorkspace.id}`);
467
+ }
468
+ let sourceVersionId = input.sourceVersionId ?? sourceWorkspace.headVersionId;
469
+ if (input.checkpointName) {
470
+ const checkpoint = await this.host.state.getWorkspaceCheckpoint(sourceWorkspace.id, normalizeCheckpointName(input.checkpointName));
471
+ if (!checkpoint)
472
+ throw new Error(`Workspace checkpoint not found: ${input.checkpointName}`);
473
+ sourceVersionId = checkpoint.versionId;
474
+ }
475
+ if (!sourceVersionId)
476
+ throw new Error(`Workspace has no version to fork: ${sourceWorkspace.id}`);
477
+ const sourceVersion = await this.host.state.getWorkspaceVersion(sourceVersionId);
478
+ if (!sourceVersion || sourceVersion.workspaceId !== sourceWorkspace.id) {
479
+ throw new Error(`Workspace version does not belong to source workspace: ${sourceVersionId}`);
480
+ }
481
+ const current = await this.loadManifest(sourceWorkspace, sourceVersion.id);
482
+ this.assertVersionSize(current.manifest, "fork");
483
+ const workspaceId = input.workspaceId ?? randomUUID();
484
+ const scope = {
485
+ agentScope: this.host.agentScope,
486
+ kind: "explicit",
487
+ scopeId: workspaceId,
488
+ ...(input.tenantId ? { tenantId: input.tenantId } : {})
489
+ };
490
+ const forked = await this.host.state.forkWorkspace({
491
+ id: workspaceId,
492
+ sourceWorkspaceId: sourceWorkspace.id,
493
+ sourceVersionId,
494
+ scope,
495
+ scopeKey: workspaceScopeKey(scope),
496
+ ...(input.name ? { name: input.name } : {})
497
+ });
498
+ if (forked.headVersionId)
499
+ await this.tryIndexVersion(forked.id, forked.headVersionId);
500
+ return forked;
501
+ }
502
+ async tryIndexVersion(workspaceId, versionId, runId) {
503
+ try {
504
+ const indexed = await this.indexVersion(workspaceId, versionId);
505
+ if (runId) {
506
+ await this.host.state.appendEvent(runId, "workspace.indexed", {
507
+ workspaceId,
508
+ versionId,
509
+ chunkCount: indexed.chunkCount,
510
+ excludedCount: indexed.excluded.length,
511
+ embeddingModel: indexed.embeddingModel ?? null
512
+ });
513
+ }
514
+ }
515
+ catch (error) {
516
+ if (runId) {
517
+ await this.host.state.appendEvent(runId, "workspace.index_failed", {
518
+ workspaceId,
519
+ versionId,
520
+ error: error instanceof Error ? error.message : String(error)
521
+ });
522
+ }
523
+ }
524
+ }
525
+ async requireWorkspace(workspaceId) {
526
+ const workspace = await this.host.state.getWorkspace(workspaceId);
527
+ if (!workspace)
528
+ throw new Error(`Workspace not found: ${workspaceId}`);
529
+ if (workspace.scope.agentScope !== this.host.agentScope) {
530
+ throw new Error(`Workspace is outside the agent scope: ${workspace.id}`);
531
+ }
532
+ return workspace;
533
+ }
534
+ async isInheritedForkManifest(workspace, version) {
535
+ if (version.reason !== "fork" || !version.sourceVersionId || !workspace.sourceWorkspaceId)
536
+ return false;
537
+ const source = await this.host.state.getWorkspaceVersion(version.sourceVersionId);
538
+ return Boolean(source &&
539
+ source.workspaceId === workspace.sourceWorkspaceId &&
540
+ source.manifestBlobKey === version.manifestBlobKey &&
541
+ source.manifestSha256 === version.manifestSha256);
542
+ }
543
+ operations() {
544
+ return new WorkspaceOperations({
545
+ state: this.host.state,
546
+ blob: this.host.blob,
547
+ agentScope: this.host.agentScope,
548
+ now: this.host.now,
549
+ requireWorkspace: (workspaceId) => this.requireWorkspace(workspaceId),
550
+ loadManifest: (workspace, versionId) => this.loadManifest(workspace, versionId)
551
+ });
552
+ }
553
+ assertVersionSize(manifest, operation) {
554
+ if (manifest.fileCount > this.limits.maxFiles || manifest.totalBytes > this.limits.maxTotalBytes ||
555
+ manifest.entries.some((entry) => entry.sizeBytes > this.limits.maxFileBytes)) {
556
+ throw new Error(`Workspace ${operation} exceeds the configured file or byte limit.`);
557
+ }
558
+ }
559
+ }
560
+ export function workspaceContentBlobKey(sha256) {
561
+ return `workspaces/content/${sha256.slice(0, 2)}/${sha256}`;
562
+ }
563
+ export function workspaceManifestBlobKey(sha256) {
564
+ return `workspaces/manifests/${sha256.slice(0, 2)}/${sha256}.json`;
565
+ }
566
+ async function clearWorkspaceTree(sandbox) {
567
+ const entries = await listWorkspaceFiles(sandbox);
568
+ if (entries.length === 0)
569
+ return;
570
+ if (!sandbox.deletePath)
571
+ throw new Error("Sandbox adapter must support deletePath for durable workspace hydration.");
572
+ for (const entry of entries)
573
+ await sandbox.deletePath(`/workspace/${entry.path}`, { recursive: false });
574
+ }
575
+ async function listWorkspaceFiles(sandbox) {
576
+ if (!sandbox.listFiles)
577
+ throw new Error("Sandbox adapter must support listFiles for durable workspaces.");
578
+ const listed = await sandbox.listFiles("/workspace", {
579
+ excludeTopLevel: RESERVED_WORKSPACE_DIRS,
580
+ includeContents: true
581
+ });
582
+ const entries = [];
583
+ for (const entry of listed) {
584
+ const relative = relativeWorkspacePath(entry.path);
585
+ if (!relative || relative === ".keep")
586
+ continue;
587
+ entries.push({ ...entry, path: relative });
588
+ }
589
+ return entries;
590
+ }
591
+ function relativeWorkspacePath(path) {
592
+ const normalized = path.replace(/\\/g, "/").replace(/\/+/g, "/");
593
+ const relative = normalized === "/workspace"
594
+ ? ""
595
+ : normalized.startsWith("/workspace/")
596
+ ? normalized.slice("/workspace/".length)
597
+ : normalized.replace(/^\/+/, "");
598
+ return relative ? normalizeWorkspaceRelativePath(relative) : "";
599
+ }
600
+ function stringValue(value) {
601
+ return typeof value === "string" && value.length > 0 ? value : undefined;
602
+ }
603
+ function stringArray(value) {
604
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
605
+ }
606
+ function normalizeCheckpointName(value) {
607
+ const name = value.trim();
608
+ if (!name || name.length > 128 || /[\u0000-\u001f\u007f]/u.test(name)) {
609
+ throw new Error("Workspace checkpoint names must contain 1 to 128 visible characters.");
610
+ }
611
+ return name;
612
+ }
613
+ function escapeRegExp(value) {
614
+ return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
615
+ }
616
+ function boundedSearchLimit(value, fallback) {
617
+ return Math.max(1, Math.min(1_000, Math.floor(value ?? fallback)));
618
+ }
619
+ //# sourceMappingURL=workspace-service.js.map