@skaile/workspaces 5.1.2 → 5.2.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 (341) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/dist/app-sdk/index.js +1 -1
  3. package/dist/{asset-feeds-EVS3WPWS.js → asset-feeds-WDIB3E6H.js} +11 -11
  4. package/dist/{asset-feeds-EVS3WPWS.js.map → asset-feeds-WDIB3E6H.js.map} +1 -1
  5. package/dist/asset-manager/catalog-deployer.js +8 -8
  6. package/dist/asset-manager/index.js +12 -12
  7. package/dist/asset-manager/installer.js +8 -8
  8. package/dist/asset-manager/src/index.d.ts +27 -1
  9. package/dist/asset-manager/src/index.d.ts.map +1 -1
  10. package/dist/bridge/drivers/claude-sdk.js +110 -23
  11. package/dist/bridge/drivers/claude-sdk.js.map +1 -1
  12. package/dist/bridge/drivers/codex.js +11 -10
  13. package/dist/bridge/drivers/codex.js.map +1 -1
  14. package/dist/bridge/drivers/continue.js +10 -10
  15. package/dist/bridge/drivers/echo.js +3 -3
  16. package/dist/bridge/drivers/gemini.js +5 -5
  17. package/dist/bridge/drivers/goose.js +10 -10
  18. package/dist/bridge/drivers/omp.js +6 -5
  19. package/dist/bridge/drivers/omp.js.map +1 -1
  20. package/dist/bridge/drivers/opencode.js +10 -10
  21. package/dist/bridge/drivers/qwen.js +5 -5
  22. package/dist/bridge/index.js +13 -13
  23. package/dist/bridge/src/driver-capabilities.d.ts +66 -0
  24. package/dist/bridge/src/driver-capabilities.d.ts.map +1 -0
  25. package/dist/bridge/src/drivers/claude-sdk.d.ts +16 -0
  26. package/dist/bridge/src/drivers/claude-sdk.d.ts.map +1 -1
  27. package/dist/bridge/src/drivers/codex.d.ts.map +1 -1
  28. package/dist/bridge/src/drivers/omp.d.ts.map +1 -1
  29. package/dist/bridge/src/index.d.ts +1 -0
  30. package/dist/bridge/src/index.d.ts.map +1 -1
  31. package/dist/bridge/src/registry.d.ts.map +1 -1
  32. package/dist/{chunk-CJB5HUY6.js → chunk-2MEMXBLC.js} +8 -7
  33. package/dist/chunk-2MEMXBLC.js.map +1 -0
  34. package/dist/{chunk-TVOODIZE.js → chunk-2QZG5VJQ.js} +3 -3
  35. package/dist/{chunk-TVOODIZE.js.map → chunk-2QZG5VJQ.js.map} +1 -1
  36. package/dist/{chunk-7SAWDPUT.js → chunk-37GBJLF5.js} +5 -55
  37. package/dist/chunk-37GBJLF5.js.map +1 -0
  38. package/dist/{chunk-RDTHJSGY.js → chunk-3E2TIPBZ.js} +56 -7
  39. package/dist/chunk-3E2TIPBZ.js.map +1 -0
  40. package/dist/{chunk-H4EGUQ7M.js → chunk-4YE7OYTB.js} +5 -5
  41. package/dist/chunk-4YE7OYTB.js.map +1 -0
  42. package/dist/{chunk-K5GBV4SA.js → chunk-5BEFNLOR.js} +6 -5
  43. package/dist/chunk-5BEFNLOR.js.map +1 -0
  44. package/dist/{chunk-UO5CWEHZ.js → chunk-6OWIEFZ2.js} +5 -5
  45. package/dist/{chunk-UO5CWEHZ.js.map → chunk-6OWIEFZ2.js.map} +1 -1
  46. package/dist/{chunk-EPHQWHOH.js → chunk-6ZCVROWW.js} +6 -6
  47. package/dist/{chunk-EPHQWHOH.js.map → chunk-6ZCVROWW.js.map} +1 -1
  48. package/dist/{chunk-TB6GDL6Q.js → chunk-77GKEZ2R.js} +21 -4
  49. package/dist/chunk-77GKEZ2R.js.map +1 -0
  50. package/dist/{chunk-TYUJB336.js → chunk-7Y43UXKA.js} +3 -3
  51. package/dist/{chunk-TYUJB336.js.map → chunk-7Y43UXKA.js.map} +1 -1
  52. package/dist/{chunk-YV4J7V53.js → chunk-ANWHAGM7.js} +3 -3
  53. package/dist/{chunk-YV4J7V53.js.map → chunk-ANWHAGM7.js.map} +1 -1
  54. package/dist/{chunk-SP47UJKE.js → chunk-B4J4RGNY.js} +231 -114
  55. package/dist/chunk-B4J4RGNY.js.map +1 -0
  56. package/dist/{chunk-IWCCZL7C.js → chunk-BIMEKYOE.js} +91 -25
  57. package/dist/chunk-BIMEKYOE.js.map +1 -0
  58. package/dist/{chunk-WCQMXBO5.js → chunk-BJQUSAGB.js} +4 -4
  59. package/dist/{chunk-WCQMXBO5.js.map → chunk-BJQUSAGB.js.map} +1 -1
  60. package/dist/{chunk-RDYI4MML.js → chunk-BNUZEYC3.js} +3 -3
  61. package/dist/{chunk-RDYI4MML.js.map → chunk-BNUZEYC3.js.map} +1 -1
  62. package/dist/{chunk-P5UQ56RA.js → chunk-BUNBKRJZ.js} +3 -3
  63. package/dist/{chunk-P5UQ56RA.js.map → chunk-BUNBKRJZ.js.map} +1 -1
  64. package/dist/{chunk-KDH74UWB.js → chunk-BWOLP6KQ.js} +3 -3
  65. package/dist/{chunk-KDH74UWB.js.map → chunk-BWOLP6KQ.js.map} +1 -1
  66. package/dist/chunk-BZXR6SW5.js +15 -0
  67. package/dist/{chunk-XUQBVK4R.js.map → chunk-BZXR6SW5.js.map} +1 -1
  68. package/dist/{chunk-ZMMFBID4.js → chunk-DD3OZLRD.js} +3 -3
  69. package/dist/{chunk-ZMMFBID4.js.map → chunk-DD3OZLRD.js.map} +1 -1
  70. package/dist/{chunk-56IWSMSD.js → chunk-EOGNW74K.js} +9 -8
  71. package/dist/chunk-EOGNW74K.js.map +1 -0
  72. package/dist/{chunk-RNNUYFFD.js → chunk-FJTC5YFO.js} +3 -3
  73. package/dist/{chunk-RNNUYFFD.js.map → chunk-FJTC5YFO.js.map} +1 -1
  74. package/dist/{chunk-THPQ6J3U.js → chunk-IOUIC2ZD.js} +8 -8
  75. package/dist/{chunk-THPQ6J3U.js.map → chunk-IOUIC2ZD.js.map} +1 -1
  76. package/dist/{chunk-YMOZC2D3.js → chunk-JPR7EPIM.js} +4 -4
  77. package/dist/{chunk-YMOZC2D3.js.map → chunk-JPR7EPIM.js.map} +1 -1
  78. package/dist/{chunk-IDEA6CYI.js → chunk-K2K7OF4P.js} +95 -3
  79. package/dist/chunk-K2K7OF4P.js.map +1 -0
  80. package/dist/{chunk-AGWYBS2E.js → chunk-KGGT6MUM.js} +4 -4
  81. package/dist/{chunk-AGWYBS2E.js.map → chunk-KGGT6MUM.js.map} +1 -1
  82. package/dist/{chunk-YJACHPI7.js → chunk-L257BENF.js} +73 -54
  83. package/dist/chunk-L257BENF.js.map +1 -0
  84. package/dist/{chunk-UVOJSWD2.js → chunk-LZ5RH6LD.js} +24 -13
  85. package/dist/chunk-LZ5RH6LD.js.map +1 -0
  86. package/dist/{chunk-N2X7DK2S.js → chunk-MGHUSFYQ.js} +2472 -2265
  87. package/dist/chunk-MGHUSFYQ.js.map +1 -0
  88. package/dist/{chunk-43ZBF3WH.js → chunk-OABIGSUY.js} +16 -3
  89. package/dist/chunk-OABIGSUY.js.map +1 -0
  90. package/dist/{chunk-YIVB7GIP.js → chunk-OFNXRI4L.js} +4 -4
  91. package/dist/{chunk-YIVB7GIP.js.map → chunk-OFNXRI4L.js.map} +1 -1
  92. package/dist/{chunk-MXXKTQXJ.js → chunk-P2EM6FNQ.js} +5 -5
  93. package/dist/{chunk-MXXKTQXJ.js.map → chunk-P2EM6FNQ.js.map} +1 -1
  94. package/dist/{chunk-KAYH6AXN.js → chunk-PHKFFJ4I.js} +3 -3
  95. package/dist/{chunk-KAYH6AXN.js.map → chunk-PHKFFJ4I.js.map} +1 -1
  96. package/dist/{chunk-YDJZRLGN.js → chunk-PU5VY7KF.js} +4 -4
  97. package/dist/{chunk-YDJZRLGN.js.map → chunk-PU5VY7KF.js.map} +1 -1
  98. package/dist/{chunk-KC3OF6B2.js → chunk-QMWMSZQ4.js} +3 -3
  99. package/dist/{chunk-KC3OF6B2.js.map → chunk-QMWMSZQ4.js.map} +1 -1
  100. package/dist/{chunk-52PFJLBJ.js → chunk-R3QW7CA5.js} +5 -5
  101. package/dist/{chunk-52PFJLBJ.js.map → chunk-R3QW7CA5.js.map} +1 -1
  102. package/dist/{chunk-UMSWQKCY.js → chunk-RXPC4KBW.js} +3 -3
  103. package/dist/{chunk-UMSWQKCY.js.map → chunk-RXPC4KBW.js.map} +1 -1
  104. package/dist/{chunk-X6IELFVB.js → chunk-S6KNDUAU.js} +4 -4
  105. package/dist/{chunk-X6IELFVB.js.map → chunk-S6KNDUAU.js.map} +1 -1
  106. package/dist/{chunk-6CSSFLGC.js → chunk-TVJBJLXR.js} +14 -15
  107. package/dist/chunk-TVJBJLXR.js.map +1 -0
  108. package/dist/{chunk-WKIOWDLQ.js → chunk-UDBN3LZU.js} +4 -4
  109. package/dist/{chunk-WKIOWDLQ.js.map → chunk-UDBN3LZU.js.map} +1 -1
  110. package/dist/{chunk-E67J56FJ.js → chunk-WMEPS6S4.js} +3 -3
  111. package/dist/{chunk-E67J56FJ.js.map → chunk-WMEPS6S4.js.map} +1 -1
  112. package/dist/{chunk-CXGHL75R.js → chunk-XIYXBHLN.js} +5 -4
  113. package/dist/chunk-XIYXBHLN.js.map +1 -0
  114. package/dist/{chunk-VDJKWKH2.js → chunk-XOTJVCJB.js} +3 -3
  115. package/dist/{chunk-VDJKWKH2.js.map → chunk-XOTJVCJB.js.map} +1 -1
  116. package/dist/{chunk-DXUQCMVT.js → chunk-YANDJSMN.js} +3 -3
  117. package/dist/{chunk-DXUQCMVT.js.map → chunk-YANDJSMN.js.map} +1 -1
  118. package/dist/{chunk-AG44ZJ7W.js → chunk-YRF5CYHR.js} +5 -14
  119. package/dist/chunk-YRF5CYHR.js.map +1 -0
  120. package/dist/{chunk-VFAJZE45.js → chunk-ZSFTCMZL.js} +907 -785
  121. package/dist/chunk-ZSFTCMZL.js.map +1 -0
  122. package/dist/cli/index.js +506 -434
  123. package/dist/cli/index.js.map +1 -1
  124. package/dist/cli/src/commands/asset-cmd.d.ts.map +1 -1
  125. package/dist/cli/src/commands/catalog.d.ts.map +1 -1
  126. package/dist/cli/src/commands/config.d.ts.map +1 -1
  127. package/dist/cli/src/commands/library-cmd.d.ts.map +1 -1
  128. package/dist/cli/src/commands/library-publish.d.ts.map +1 -1
  129. package/dist/cli/src/commands/project.d.ts.map +1 -1
  130. package/dist/cli/src/commands/run.d.ts.map +1 -1
  131. package/dist/cli/src/commands/skailify-lib.d.ts +0 -45
  132. package/dist/cli/src/commands/skailify-lib.d.ts.map +1 -1
  133. package/dist/cli/src/commands/source.d.ts.map +1 -1
  134. package/dist/cli/src/commands/validate.d.ts.map +1 -1
  135. package/dist/cli/src/flow-discovery.d.ts +9 -0
  136. package/dist/cli/src/flow-discovery.d.ts.map +1 -1
  137. package/dist/cli/src/helpers.d.ts +13 -0
  138. package/dist/cli/src/helpers.d.ts.map +1 -1
  139. package/dist/cli/src/setup.d.ts.map +1 -1
  140. package/dist/connectors/config.js +5 -5
  141. package/dist/connectors/index.js +17 -17
  142. package/dist/connectors/src/connector-manager.d.ts +8 -27
  143. package/dist/connectors/src/connector-manager.d.ts.map +1 -1
  144. package/dist/connectors/src/index.d.ts +1 -1
  145. package/dist/connectors/src/index.d.ts.map +1 -1
  146. package/dist/connectors/src/mount-ledger.d.ts +61 -0
  147. package/dist/connectors/src/mount-ledger.d.ts.map +1 -0
  148. package/dist/{connectors-KSVPVYCA.js → connectors-GUN4MRWR.js} +20 -20
  149. package/dist/{connectors-KSVPVYCA.js.map → connectors-GUN4MRWR.js.map} +1 -1
  150. package/dist/connectors-shared/index.js +2 -2
  151. package/dist/core/framework.js +1 -1
  152. package/dist/core/index.js +7 -7
  153. package/dist/core/manifest.js +2 -2
  154. package/dist/core/models.js +1 -1
  155. package/dist/core/publish-manifest.js +2 -2
  156. package/dist/core/ref.js +1 -1
  157. package/dist/core/runtime-assets.js +5 -5
  158. package/dist/core/src/framework.d.ts +7 -0
  159. package/dist/core/src/framework.d.ts.map +1 -1
  160. package/dist/core/src/index.d.ts +4 -4
  161. package/dist/core/src/index.d.ts.map +1 -1
  162. package/dist/core/src/lock.d.ts +12 -0
  163. package/dist/core/src/lock.d.ts.map +1 -1
  164. package/dist/core/src/manifest-yaml-editor.d.ts +14 -6
  165. package/dist/core/src/manifest-yaml-editor.d.ts.map +1 -1
  166. package/dist/core/src/models.d.ts +15 -0
  167. package/dist/core/src/models.d.ts.map +1 -1
  168. package/dist/core/src/publish-manifest.d.ts +4 -6
  169. package/dist/core/src/publish-manifest.d.ts.map +1 -1
  170. package/dist/core/src/settings.d.ts.map +1 -1
  171. package/dist/core/workspace-config.js +4 -4
  172. package/dist/deploy/index.js +7 -7
  173. package/dist/discovery/index.js +4 -4
  174. package/dist/discovery/src/discover-manifest.d.ts +5 -0
  175. package/dist/discovery/src/discover-manifest.d.ts.map +1 -1
  176. package/dist/discovery/src/index.d.ts +1 -1
  177. package/dist/discovery/src/index.d.ts.map +1 -1
  178. package/dist/{ensure-sources-KVV72WRP.js → ensure-sources-VCPA762L.js} +15 -15
  179. package/dist/{ensure-sources-KVV72WRP.js.map → ensure-sources-VCPA762L.js.map} +1 -1
  180. package/dist/factory-assets/connectors/box.js +17 -17
  181. package/dist/factory-assets/connectors/deploy.js +17 -17
  182. package/dist/factory-assets/connectors/devserver.js +17 -17
  183. package/dist/factory-assets/connectors/flow/adapter.d.ts +28 -4
  184. package/dist/factory-assets/connectors/flow/adapter.d.ts.map +1 -1
  185. package/dist/factory-assets/connectors/flow/adapter.js +17 -17
  186. package/dist/factory-assets/connectors/flow/engine.js +8 -8
  187. package/dist/factory-assets/connectors/flow/index.d.ts +1 -1
  188. package/dist/factory-assets/connectors/flow/index.d.ts.map +1 -1
  189. package/dist/factory-assets/connectors/flow/run-flow.js +19 -19
  190. package/dist/factory-assets/connectors/flow.js +17 -17
  191. package/dist/factory-assets/connectors/git/checkout.d.ts +161 -0
  192. package/dist/factory-assets/connectors/git/checkout.d.ts.map +1 -0
  193. package/dist/factory-assets/connectors/git/config.d.ts +78 -0
  194. package/dist/factory-assets/connectors/git/config.d.ts.map +1 -0
  195. package/dist/factory-assets/connectors/git/credentials.d.ts +93 -0
  196. package/dist/factory-assets/connectors/git/credentials.d.ts.map +1 -0
  197. package/dist/factory-assets/connectors/git/driver.d.ts +39 -598
  198. package/dist/factory-assets/connectors/git/driver.d.ts.map +1 -1
  199. package/dist/factory-assets/connectors/git/exec.d.ts +177 -0
  200. package/dist/factory-assets/connectors/git/exec.d.ts.map +1 -0
  201. package/dist/factory-assets/connectors/git/inspect.d.ts +120 -0
  202. package/dist/factory-assets/connectors/git/inspect.d.ts.map +1 -0
  203. package/dist/factory-assets/connectors/git/lifecycle.d.ts +107 -0
  204. package/dist/factory-assets/connectors/git/lifecycle.d.ts.map +1 -0
  205. package/dist/factory-assets/connectors/git/repo.d.ts +496 -0
  206. package/dist/factory-assets/connectors/git/repo.d.ts.map +1 -0
  207. package/dist/factory-assets/connectors/git.js +17 -17
  208. package/dist/factory-assets/connectors/gmail.js +17 -17
  209. package/dist/factory-assets/connectors/googledrive.js +17 -17
  210. package/dist/factory-assets/connectors/local.js +17 -17
  211. package/dist/factory-assets/connectors/mattermost.js +17 -17
  212. package/dist/factory-assets/connectors/memory.js +17 -17
  213. package/dist/factory-assets/connectors/minio.js +17 -17
  214. package/dist/factory-assets/connectors/postgres.js +17 -17
  215. package/dist/factory-assets/connectors/s3.js +17 -17
  216. package/dist/factory-assets/connectors/sharepoint.js +17 -17
  217. package/dist/factory-assets/connectors/sqlite.js +17 -17
  218. package/dist/factory-assets/connectors/static-server.js +17 -17
  219. package/dist/factory-assets/connectors/tunnel.js +17 -17
  220. package/dist/factory-assets/connectors/webdav.js +17 -17
  221. package/dist/factory-assets/connectors/xstate-store.js +17 -17
  222. package/dist/factory-assets/connectors/xstate.js +17 -17
  223. package/dist/{flows-SVCCADGE.js → flows-BOB5YA5R.js} +12 -12
  224. package/dist/{flows-SVCCADGE.js.map → flows-BOB5YA5R.js.map} +1 -1
  225. package/dist/{helpers-4NT7UX6I.js → helpers-ZGK4R5MZ.js} +15 -15
  226. package/dist/{helpers-4NT7UX6I.js.map → helpers-ZGK4R5MZ.js.map} +1 -1
  227. package/dist/library/index.js +10 -10
  228. package/dist/library/install.js +2 -2
  229. package/dist/library/src/index.d.ts +6 -3
  230. package/dist/library/src/index.d.ts.map +1 -1
  231. package/dist/library/src/install/install-from-manifest.d.ts +21 -0
  232. package/dist/library/src/install/install-from-manifest.d.ts.map +1 -1
  233. package/dist/library/src/local/install-library-asset.d.ts +64 -0
  234. package/dist/library/src/local/install-library-asset.d.ts.map +1 -0
  235. package/dist/library/src/local/local-catalog-source.d.ts +30 -0
  236. package/dist/library/src/local/local-catalog-source.d.ts.map +1 -1
  237. package/dist/library/src/local/store-paths.d.ts +6 -0
  238. package/dist/library/src/local/store-paths.d.ts.map +1 -1
  239. package/dist/library/src/manifest-writer.d.ts +73 -0
  240. package/dist/library/src/manifest-writer.d.ts.map +1 -0
  241. package/dist/library/src/sync/manifest-writeback.d.ts +2 -17
  242. package/dist/library/src/sync/manifest-writeback.d.ts.map +1 -1
  243. package/dist/{library-publish-GHIWWEEY.js → library-publish-RIP6NBYA.js} +20 -21
  244. package/dist/library-publish-RIP6NBYA.js.map +1 -0
  245. package/dist/{open-library-XKWTUYU2.js → open-library-4IJTBVTR.js} +18 -18
  246. package/dist/{open-library-XKWTUYU2.js.map → open-library-4IJTBVTR.js.map} +1 -1
  247. package/dist/{open-registry-CHCWKV5W.js → open-registry-G5DI4RPU.js} +14 -14
  248. package/dist/{open-registry-CHCWKV5W.js.map → open-registry-G5DI4RPU.js.map} +1 -1
  249. package/dist/paths-L5XLUC3D.js +22 -0
  250. package/dist/{paths-MKSI52PF.js.map → paths-L5XLUC3D.js.map} +1 -1
  251. package/dist/plugin-registry/src/targets.d.ts +3 -1
  252. package/dist/plugin-registry/src/targets.d.ts.map +1 -1
  253. package/dist/{plugin-store-SVYV7L4C.js → plugin-store-QENDYZ63.js} +9 -9
  254. package/dist/{plugin-store-SVYV7L4C.js.map → plugin-store-QENDYZ63.js.map} +1 -1
  255. package/dist/{project-P2QS3GNG.js → project-4INCIIZI.js} +16 -16
  256. package/dist/{project-P2QS3GNG.js.map → project-4INCIIZI.js.map} +1 -1
  257. package/dist/resolver/index.js +8 -8
  258. package/dist/runner/index.js +27 -27
  259. package/dist/runner/src/agent.d.ts +1 -1
  260. package/dist/runner/src/agent.d.ts.map +1 -1
  261. package/dist/runner/src/ai-provider-switch.d.ts +10 -0
  262. package/dist/runner/src/ai-provider-switch.d.ts.map +1 -1
  263. package/dist/runner/src/codex-init.d.ts.map +1 -1
  264. package/dist/runner/src/compaction-gate.d.ts +9 -33
  265. package/dist/runner/src/compaction-gate.d.ts.map +1 -1
  266. package/dist/runner/src/managed-codex-bootstrap.d.ts.map +1 -1
  267. package/dist/runner/src/pending-turns.d.ts +7 -2
  268. package/dist/runner/src/pending-turns.d.ts.map +1 -1
  269. package/dist/runner/src/resources.d.ts +2 -2
  270. package/dist/runner/src/resources.d.ts.map +1 -1
  271. package/dist/runner/src/serve-credentials.d.ts +2 -1
  272. package/dist/runner/src/serve-credentials.d.ts.map +1 -1
  273. package/dist/runner/src/serve.d.ts +19 -35
  274. package/dist/runner/src/serve.d.ts.map +1 -1
  275. package/dist/runner/src/session-builder.d.ts.map +1 -1
  276. package/dist/runner/src/turn-scheduler.d.ts +158 -0
  277. package/dist/runner/src/turn-scheduler.d.ts.map +1 -0
  278. package/dist/sdk/asset-manager.js +12 -12
  279. package/dist/sdk/bridge.js +13 -13
  280. package/dist/sdk/core.js +7 -7
  281. package/dist/sdk/flow.js +8 -8
  282. package/dist/sdk/index.js +27 -27
  283. package/dist/sdk/resolver.js +8 -8
  284. package/dist/sdk/runner.js +27 -27
  285. package/dist/sdk/session.js +2 -2
  286. package/dist/sdk/types.js +1 -1
  287. package/dist/session/index.js +2 -2
  288. package/dist/session/src/dispatcher.d.ts.map +1 -1
  289. package/dist/{setup-RVP74D37.js → setup-RG4PJJUW.js} +23 -23
  290. package/dist/setup-RG4PJJUW.js.map +1 -0
  291. package/dist/{skills-7K2YO3KN.js → skills-CL45F7AG.js} +13 -13
  292. package/dist/{skills-7K2YO3KN.js.map → skills-CL45F7AG.js.map} +1 -1
  293. package/dist/{source-VBROLEFB.js → source-LJOI5AXU.js} +19 -19
  294. package/dist/{source-VBROLEFB.js.map → source-LJOI5AXU.js.map} +1 -1
  295. package/dist/{source-manifest-publish-7XDLJRLX.js → source-manifest-publish-QEJML4VS.js} +21 -21
  296. package/dist/{source-manifest-publish-7XDLJRLX.js.map → source-manifest-publish-QEJML4VS.js.map} +1 -1
  297. package/dist/tui/index.js +28 -28
  298. package/dist/tui/index.js.map +1 -1
  299. package/dist/types/index.js +1 -1
  300. package/dist/types/src/delivery.d.ts +26 -0
  301. package/dist/types/src/delivery.d.ts.map +1 -0
  302. package/dist/types/src/index.d.ts +3 -1
  303. package/dist/types/src/index.d.ts.map +1 -1
  304. package/dist/types/src/runtime.d.ts +51 -0
  305. package/dist/types/src/runtime.d.ts.map +1 -1
  306. package/dist/workspace-plugin/adapters/mcp.js +24 -24
  307. package/dist/workspace-plugin/adapters/omp.js +18 -17
  308. package/dist/workspace-plugin/adapters/omp.js.map +1 -1
  309. package/dist/workspace-plugin/index.js +2 -1
  310. package/dist/workspace-plugin/src/adapters/mcp.d.ts.map +1 -1
  311. package/dist/workspace-plugin/src/adapters/omp.d.ts.map +1 -1
  312. package/dist/workspace-plugin/src/plugin.d.ts.map +1 -1
  313. package/package.json +1 -1
  314. package/dist/chunk-43ZBF3WH.js.map +0 -1
  315. package/dist/chunk-56IWSMSD.js.map +0 -1
  316. package/dist/chunk-6CSSFLGC.js.map +0 -1
  317. package/dist/chunk-7SAWDPUT.js.map +0 -1
  318. package/dist/chunk-AG44ZJ7W.js.map +0 -1
  319. package/dist/chunk-CJB5HUY6.js.map +0 -1
  320. package/dist/chunk-CXGHL75R.js.map +0 -1
  321. package/dist/chunk-F7JHP7JV.js +0 -122
  322. package/dist/chunk-F7JHP7JV.js.map +0 -1
  323. package/dist/chunk-H4EGUQ7M.js.map +0 -1
  324. package/dist/chunk-IDEA6CYI.js.map +0 -1
  325. package/dist/chunk-IWCCZL7C.js.map +0 -1
  326. package/dist/chunk-K5GBV4SA.js.map +0 -1
  327. package/dist/chunk-N2X7DK2S.js.map +0 -1
  328. package/dist/chunk-RDTHJSGY.js.map +0 -1
  329. package/dist/chunk-SP47UJKE.js.map +0 -1
  330. package/dist/chunk-TB6GDL6Q.js.map +0 -1
  331. package/dist/chunk-UVOJSWD2.js.map +0 -1
  332. package/dist/chunk-VFAJZE45.js.map +0 -1
  333. package/dist/chunk-XUQBVK4R.js +0 -15
  334. package/dist/chunk-YJACHPI7.js.map +0 -1
  335. package/dist/library-publish-GHIWWEEY.js.map +0 -1
  336. package/dist/paths-MKSI52PF.js +0 -22
  337. package/dist/runner/src/flow-turn-dispatch.d.ts +0 -41
  338. package/dist/runner/src/flow-turn-dispatch.d.ts.map +0 -1
  339. package/dist/runner/src/reply-routing.d.ts +0 -54
  340. package/dist/runner/src/reply-routing.d.ts.map +0 -1
  341. package/dist/setup-RVP74D37.js.map +0 -1
@@ -1,32 +1,32 @@
1
- import { WorkspacePlugin } from './chunk-CXGHL75R.js';
2
- import { buildWorkspaceToolDefs } from './chunk-CJB5HUY6.js';
3
- import { findAiAssetsRoot } from './chunk-TYUJB336.js';
1
+ import { WorkspacePlugin } from './chunk-XIYXBHLN.js';
2
+ import { buildWorkspaceToolDefs } from './chunk-2MEMXBLC.js';
3
+ import { findAiAssetsRoot } from './chunk-7Y43UXKA.js';
4
4
  import { WebSocketServerTransport } from './chunk-5LKOAX7I.js';
5
5
  import { assembleSystemPrompt, buildCapabilitiesPromptSection } from './chunk-W3UDISS2.js';
6
6
  import { prepareNativePolicy, codexInstanceEnvironment, parseCodexCredential, parseCodexRefreshInput, parseCodexRefreshResult } from './chunk-HCYS7GFP.js';
7
- import { resolveFlowPath } from './chunk-THPQ6J3U.js';
8
- import { findAiAssetsRoot as findAiAssetsRoot$1, deployCatalogEntry, undeployCatalogEntry } from './chunk-VDJKWKH2.js';
9
- import { ConnectFailureCarryOver, registerBuiltinConnectors, buildConnectorPromptSection, findMissingPackages, installNpmPackages, ConnectorManager, buildSdkConnectorTools, buildLazySdkFlowTools, describeFlowOperations, buildConnectorToolDefs, buildFlowToolDefs, SessionNodeExecutor, SessionSubpromptRunner, ClassifierSubpromptRunner, SessionInlineSubFlowRunner, driveOrchestratorTurn, buildSdkFlowTools } from './chunk-N2X7DK2S.js';
10
- import { loadYamlConnectorDeclarations, withMaterializedConnectorDeclarations, SecretProviderChain, ConnectorStartupError, isKnownSecretRef, EnvSecretProvider, PreMintedSecretProvider, InMemorySecretProvider, resolvedConfigToDeclarations, deriveSingleMaterializedConnectorDeclaration } from './chunk-WKIOWDLQ.js';
11
- import { EventNormalizer } from './chunk-YIVB7GIP.js';
12
- import { probeResumeTranscript, transcriptStoreForRuntime } from './chunk-TVOODIZE.js';
7
+ import { resolveFlowPath } from './chunk-IOUIC2ZD.js';
8
+ import { findAiAssetsRoot as findAiAssetsRoot$1, deployCatalogEntry, undeployCatalogEntry } from './chunk-XOTJVCJB.js';
9
+ import { ConnectFailureCarryOver, registerBuiltinConnectors, buildConnectorPromptSection, findMissingPackages, installNpmPackages, ConnectorManager, buildSdkConnectorTools, buildLazySdkFlowTools, describeFlowOperations, buildConnectorToolDefs, buildFlowToolDefs, SessionNodeExecutor, SessionSubpromptRunner, ClassifierSubpromptRunner, SessionInlineSubFlowRunner, driveOrchestratorTurn, buildSdkFlowTools } from './chunk-MGHUSFYQ.js';
10
+ import { loadYamlConnectorDeclarations, withMaterializedConnectorDeclarations, SecretProviderChain, ConnectorStartupError, isKnownSecretRef, EnvSecretProvider, PreMintedSecretProvider, InMemorySecretProvider, resolvedConfigToDeclarations, deriveSingleMaterializedConnectorDeclaration } from './chunk-UDBN3LZU.js';
11
+ import { EventNormalizer } from './chunk-OFNXRI4L.js';
12
+ import { probeResumeTranscript, transcriptStoreForRuntime } from './chunk-2QZG5VJQ.js';
13
13
  import { scopeReconfigureModel } from './chunk-EINLFFP6.js';
14
14
  import { CodexNativeStateUnavailable } from './chunk-LOO5CB5M.js';
15
- import { dispatchCapability } from './chunk-RDYI4MML.js';
15
+ import { dispatchCapability } from './chunk-BNUZEYC3.js';
16
16
  import { classifyClaudeSdkError } from './chunk-XBSQ4KMC.js';
17
17
  import { agentExecRequested } from './chunk-YROR332M.js';
18
- import { toAgentEffort, createDriver, catalogLayer, providerCredentialEnvName, toMergedRegistry, registryCredentialEnv, AI_PROVIDER_PRESETS, AgentDriver } from './chunk-RDTHJSGY.js';
19
- import { loadFlow } from './chunk-UMSWQKCY.js';
18
+ import { driverCapabilities, toAgentEffort, createDriver, catalogLayer, providerCredentialEnvName, toMergedRegistry, registryCredentialEnv, AI_PROVIDER_PRESETS, supportsLiveToolUpdates, AgentDriver } from './chunk-3E2TIPBZ.js';
19
+ import { loadFlow } from './chunk-RXPC4KBW.js';
20
20
  import { parseFlowDefinition } from './chunk-QZLWJETK.js';
21
21
  import { renderStimulusPrompt } from './chunk-XUUYAS3Z.js';
22
- import { withActivityLabelJsonSchema, TOOL_ACTIVITY_PROMPT_SECTION, SESSION_ROOT_CONNECTOR_ID, PROTOCOL_VERSION } from './chunk-IDEA6CYI.js';
23
- import { resolveLibraryDir, resolveSettings, loadProviderRegistry } from './chunk-IWCCZL7C.js';
24
- import { resolveRuntimeAssets } from './chunk-YDJZRLGN.js';
25
- import { resolveDriverPaths } from './chunk-K5GBV4SA.js';
26
- import { resolveSkWorkspaceConfig, resolveAgentDir, loadMcpServerDeclarations, COMPACTION_DEFAULTS, resolveCompactionThresholdPercent, validateAssetRecipeAttr, deriveSingleMaterializedMcpDeclaration, stageMaterializedSkills, readSkillAllowList, isStagedSkillMarkerFor, unstageSkill, STAGED_SKILL_MARKER } from './chunk-MXXKTQXJ.js';
22
+ import { withActivityLabelJsonSchema, TOOL_ACTIVITY_PROMPT_SECTION, SESSION_ROOT_CONNECTOR_ID, PROTOCOL_VERSION, deliveryClassOf } from './chunk-K2K7OF4P.js';
23
+ import { resolveLibraryDir, resolveSettings, loadProviderRegistry } from './chunk-BIMEKYOE.js';
24
+ import { resolveRuntimeAssets } from './chunk-PU5VY7KF.js';
25
+ import { DEFAULT_DRIVER_ID, resolveDriverPaths } from './chunk-5BEFNLOR.js';
26
+ import { resolveSkWorkspaceConfig, resolveAgentDir, loadMcpServerDeclarations, COMPACTION_DEFAULTS, resolveCompactionThresholdPercent, validateAssetRecipeAttr, deriveSingleMaterializedMcpDeclaration, stageMaterializedSkills, readSkillAllowList, isStagedSkillMarkerFor, unstageSkill, STAGED_SKILL_MARKER } from './chunk-P2EM6FNQ.js';
27
27
  import { globalSkaileDir } from './chunk-TLKM4M47.js';
28
- import { withFsDeadline, FsDeadlineError, fsDeadlineMs, parseFrontmatter, tryWithFsDeadline } from './chunk-DXUQCMVT.js';
29
- import { parseAssetRef } from './chunk-43ZBF3WH.js';
28
+ import { withFsDeadline, FsDeadlineError, fsDeadlineMs, parseFrontmatter, tryWithFsDeadline } from './chunk-YANDJSMN.js';
29
+ import { parseAssetRef } from './chunk-OABIGSUY.js';
30
30
  import { resolveLogStoreConfig, createLogStoreFromConfig, OnLogBridgeSink, WsLogSink, resolveLogsRetention, enforceLogsRetention } from './chunk-JMKB7IB4.js';
31
31
  import { pickRegistryEntries, registryEntries, resolveEntryBaseUrl, parseModelRef, isEntryEnabled, parseCredentialRef } from './chunk-MJHSLODD.js';
32
32
  import { getLogStore, registerLogStore, createLogger, resetLogStore, isUnwritableStreamError, markStreamBroken, isBrokenPipeError, redactCredentials } from './chunk-BOFL7GIF.js';
@@ -2332,6 +2332,7 @@ function parsePresignResult(raw, op) {
2332
2332
  // runner/src/resources.ts
2333
2333
  async function buildAgentResources(projectDir, driverType, _onLog, watch, secretProvider, sessionMeta, tokenMediator, preMintedSecrets, deferFilesystemMounts, capabilityRegistry, fallbackConnectorDeclarations, extraMcpDeclarations, s3Presigner) {
2334
2334
  const managedProjectMcp = sessionMeta?.managedCodex ? await resolveExternalMcpDeclarations(projectDir, sessionMeta.sessionId, true) : void 0;
2335
+ const inProcessMcp = driverCapabilities(driverType).inProcessMcpServers;
2335
2336
  const resLog = createLogger({
2336
2337
  kind: "runner",
2337
2338
  subkind: "resources",
@@ -2415,7 +2416,7 @@ async function buildAgentResources(projectDir, driverType, _onLog, watch, secret
2415
2416
  }
2416
2417
  return externalMcpManager;
2417
2418
  };
2418
- if (driverType === "claude-sdk") {
2419
+ if (inProcessMcp) {
2419
2420
  const inProcess = await buildInProcessMcpServers(
2420
2421
  resourceManager,
2421
2422
  projectDir,
@@ -2442,7 +2443,7 @@ async function buildAgentResources(projectDir, driverType, _onLog, watch, secret
2442
2443
  mcpSecretChain,
2443
2444
  sessionMeta
2444
2445
  );
2445
- } else if (driverType === "claude-sdk") {
2446
+ } else if (inProcessMcp) {
2446
2447
  externalMcpDeclarations = await resolveExternalMcpDeclarations(
2447
2448
  projectDir,
2448
2449
  sessionMeta?.sessionId
@@ -2454,7 +2455,7 @@ async function buildAgentResources(projectDir, driverType, _onLog, watch, secret
2454
2455
  sessionMeta
2455
2456
  );
2456
2457
  }
2457
- if (!resourceManager && driverType !== "claude-sdk" && !externalMcpManager?.hasServers() && !capabilityRegistry)
2458
+ if (!resourceManager && !inProcessMcp && !externalMcpManager?.hasServers() && !capabilityRegistry)
2458
2459
  return noopResult;
2459
2460
  let coalescer = null;
2460
2461
  const startWatching = () => {
@@ -2724,7 +2725,7 @@ function toSdkMcpServerConfig(decl, secrets) {
2724
2725
  // runner/src/session-builder.ts
2725
2726
  async function createAgentSession(config) {
2726
2727
  const managed = config.managedCodex;
2727
- if (managed && (config.driver !== "codex" || !config.model || !config.aiProviderConfigId || !config.capabilityRegistry || !config.capabilities))
2728
+ if (managed && (!driverCapabilities(config.driver).managedSession || !config.model || !config.aiProviderConfigId || !config.capabilityRegistry || !config.capabilities))
2728
2729
  throw new Error("AI_PROFILE_INVALID");
2729
2730
  const {
2730
2731
  projectDir,
@@ -2763,7 +2764,7 @@ async function createAgentSession(config) {
2763
2764
  driver: driverOverride,
2764
2765
  model: modelOverride
2765
2766
  });
2766
- const driverType = managed ? "codex" : settings.driver ?? "omp";
2767
+ const driverType = (managed ? config.driver : settings.driver) ?? DEFAULT_DRIVER_ID;
2767
2768
  const model = managed ? config.model : settings.model;
2768
2769
  const registry = loadSessionRegistry(projectDir, config.registry, config.providers, sessionLog);
2769
2770
  const modelRef = config.modelRef?.trim() || model?.trim() || void 0;
@@ -2867,7 +2868,7 @@ async function createAgentSession(config) {
2867
2868
  const maxTurns = maxTurnsOverride ?? manifest?.runtime?.max_turns;
2868
2869
  let driver;
2869
2870
  try {
2870
- const extraMcpServers = driverType === "claude-sdk" ? await config.extraInProcessMcpServers?.() : void 0;
2871
+ const extraMcpServers = driverCapabilities(driverType).inProcessMcpServers ? await config.extraInProcessMcpServers?.() : void 0;
2871
2872
  const driverMcpServers = extraMcpServers ? { ...mcpServers, ...extraMcpServers } : mcpServers;
2872
2873
  driver = await createSessionDriver({
2873
2874
  config,
@@ -4502,7 +4503,7 @@ function validateCodexInit(cmd, supported, previous) {
4502
4503
  if (requirements !== void 0 && (!Array.isArray(requirements) || requirements.some((item) => typeof item !== "string" || !supported.includes(item))))
4503
4504
  throw new Error("Unsupported runtime: required features are unavailable");
4504
4505
  const profile = cmd.resolvedConfig.agentConfig;
4505
- const managed = profile?.driver === "codex" && (profile.authMode !== void 0 || requirements?.includes("codex-app-server-v1"));
4506
+ const managed = profile !== void 0 && driverCapabilities(profile.driver).managedSession && (profile.authMode !== void 0 || requirements?.includes("codex-app-server-v1"));
4506
4507
  if ((managed || cmd.credentials.ai !== void 0) && !supported.includes("codex-app-server-v1"))
4507
4508
  throw new Error("Unsupported runtime: codex-app-server-v1 is unavailable");
4508
4509
  if (!managed && cmd.credentials.ai === void 0) return;
@@ -4605,7 +4606,7 @@ function loadManagedCodexHostConfig(file, projectDir) {
4605
4606
  function bindManagedCodexInit(host, cmd, refreshCredential) {
4606
4607
  const profile = cmd.resolvedConfig.agentConfig;
4607
4608
  const credential = parseCodexCredential(cmd.credentials.ai);
4608
- if (!profile?.authMode || !["subscription", "api-key"].includes(profile.authMode) || cmd.identity.sessionId !== host.sessionId || profile?.driver !== "codex" || !profile.model || !host.subpromptModels.available.includes(profile.model) || credential.configId !== cmd.resolvedConfig.aiProviderConfigId || profile.authMode === "subscription" !== (credential.kind === "codex-subscription"))
4609
+ if (!profile?.authMode || !["subscription", "api-key"].includes(profile.authMode) || cmd.identity.sessionId !== host.sessionId || !driverCapabilities(profile.driver).managedSession || !profile.model || !host.subpromptModels.available.includes(profile.model) || credential.configId !== cmd.resolvedConfig.aiProviderConfigId || profile.authMode === "subscription" !== (credential.kind === "codex-subscription"))
4609
4610
  throw refused();
4610
4611
  const reasoningEffort = profile.effort ?? host.reasoningEffort;
4611
4612
  if (!["minimal", "low", "medium", "high", "xhigh"].includes(reasoningEffort)) throw refused();
@@ -4870,6 +4871,10 @@ function createAiProviderConfigSwitcher(deps) {
4870
4871
  }
4871
4872
  };
4872
4873
  }
4874
+ function movedOffBlockedSeat(blockedConfigId, outcome) {
4875
+ if (!blockedConfigId || !outcome.mint.ok || !outcome.configId) return false;
4876
+ return outcome.configId !== blockedConfigId;
4877
+ }
4873
4878
 
4874
4879
  // runner/src/provider-quota-forwarder.ts
4875
4880
  function createProviderQuotaForwarder(deps) {
@@ -5008,167 +5013,470 @@ function planRuntimeSessionEmit(input) {
5008
5013
  return { type: "runtime_session", driverSessionId, model, capabilitySignature };
5009
5014
  }
5010
5015
 
5011
- // runner/src/compaction-gate.ts
5012
- var CompactionGate = class {
5013
- /**
5014
- * Every compaction currently holding the driver.
5015
- *
5016
- * A set rather than a boolean because holds can overlap: a driver swap
5017
- * rebuilds the orchestrator, and the new instance carries its own
5018
- * `_isCompacting`, so it can start a compaction while the previous
5019
- * instance's call is still unwinding. Keyed by handle so the older one
5020
- * settling cannot open the gate under the newer one — the same identity
5021
- * check `DriverSwapGate.beginSwap` makes for swaps.
5022
- */
5023
- held = /* @__PURE__ */ new Set();
5024
- /** Resolves when the last hold is released. Null whenever the driver is free. */
5025
- opened = null;
5026
- release = null;
5027
- /**
5028
- * Mark a compaction as occupying the driver. The returned handle is the only
5029
- * thing that can release it.
5030
- */
5031
- begin() {
5032
- const hold = /* @__PURE__ */ Symbol("compaction");
5033
- if (this.held.size === 0) {
5034
- this.opened = new Promise((resolve4) => {
5035
- this.release = resolve4;
5036
- });
5037
- }
5038
- this.held.add(hold);
5039
- return hold;
5040
- }
5041
- /**
5042
- * Release one hold, opening the gate once none remain. Unknown and null
5043
- * handles are ignored, so a settle signal that fires without a matching
5044
- * `begin()` is harmless.
5045
- *
5046
- * Call this from a signal the orchestrator emits unconditionally. A `begin()`
5047
- * whose `settle()` can be skipped holds user turns forever, which is a worse
5048
- * failure than the discarded reply this gate exists to prevent.
5049
- */
5050
- settle(hold) {
5051
- if (hold == null || !this.held.delete(hold)) return;
5052
- if (this.held.size > 0) return;
5053
- const release2 = this.release;
5054
- this.opened = null;
5055
- this.release = null;
5056
- release2?.();
5057
- }
5058
- /** Whether no compaction holds the driver right now. */
5059
- isIdle() {
5060
- return this.held.size === 0;
5061
- }
5062
- /**
5063
- * Wait for the driver to be free.
5064
- *
5065
- * Returns `idle` when nothing was in flight, `waited` when every compaction
5066
- * settled within the bound, and `timeout` when they did not. A caller that
5067
- * gets `timeout` must surface it rather than dispatch: a compaction still
5068
- * holding the driver past this bound is stuck, and dispatching into it
5069
- * reproduces the silent loss.
5070
- *
5071
- * Re-checks in a loop, as `DriverSwapGate.awaitSwap` does, so a compaction
5072
- * armed while this turn was already waiting is also awaited. `timeoutMs`
5073
- * bounds the whole wait, not each iteration.
5074
- */
5075
- async awaitIdle(timeoutMs) {
5076
- if (this.held.size === 0) return "idle";
5077
- const deadline = Date.now() + timeoutMs;
5078
- let waited = false;
5079
- while (this.held.size > 0) {
5080
- const opened = this.opened;
5081
- if (!opened) break;
5082
- const remaining = deadline - Date.now();
5083
- if (remaining <= 0) return "timeout";
5084
- if (await raceDeadline(opened, remaining) === "timeout") return "timeout";
5085
- waited = true;
5016
+ // runner/src/init-build-hold.ts
5017
+ var DEFAULT_CONNECTOR_MUTATE_INIT_HOLD_MS = 1e4;
5018
+ function resolveConnectorMutateInitHoldMs(env = process.env) {
5019
+ const parsed = Number.parseInt(env.SKAILE_CONNECTOR_MUTATE_INIT_HOLD_MS ?? "", 10);
5020
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_CONNECTOR_MUTATE_INIT_HOLD_MS;
5021
+ }
5022
+ function createInitBuildHold(now = Date.now) {
5023
+ let current = null;
5024
+ let tail = Promise.resolve();
5025
+ let held = 0;
5026
+ const building = () => current !== null && !current.done;
5027
+ async function awaitNewestBuild() {
5028
+ for (; ; ) {
5029
+ const build = current;
5030
+ if (!build) return null;
5031
+ if (!build.done) await build.settled;
5032
+ if (current === build) return build.error;
5086
5033
  }
5087
- return waited ? "waited" : "idle";
5088
- }
5089
- };
5090
- async function raceDeadline(opened, ms) {
5091
- let timer;
5092
- const expired = new Promise((resolve4) => {
5093
- timer = setTimeout(() => resolve4("timeout"), ms);
5094
- timer.unref?.();
5095
- });
5096
- try {
5097
- return await Promise.race([opened.then(() => "opened"), expired]);
5098
- } finally {
5099
- if (timer) clearTimeout(timer);
5100
5034
  }
5035
+ return {
5036
+ beginBuild() {
5037
+ let resolve4;
5038
+ const build = {
5039
+ done: false,
5040
+ error: null,
5041
+ settled: new Promise((r) => {
5042
+ resolve4 = r;
5043
+ })
5044
+ };
5045
+ current = build;
5046
+ const settle = (error) => {
5047
+ if (build.done) return;
5048
+ build.done = true;
5049
+ build.error = error;
5050
+ resolve4();
5051
+ };
5052
+ return { succeed: () => settle(null), fail: (error) => settle(error) };
5053
+ },
5054
+ run(task, deadlineMs) {
5055
+ if (held === 0 && !building()) {
5056
+ if (current?.error) return task({ kind: "failed", error: current.error });
5057
+ return task({ kind: "ready", remainingMs: void 0 });
5058
+ }
5059
+ held++;
5060
+ const arrivedAt = now();
5061
+ const previous = tail;
5062
+ const turn = previous.then(awaitNewestBuild);
5063
+ let timer;
5064
+ const deadline = deadlineMs === void 0 ? null : new Promise((resolve4) => {
5065
+ timer = setTimeout(() => resolve4("timeout"), deadlineMs);
5066
+ timer.unref?.();
5067
+ });
5068
+ let markStarted;
5069
+ const started = new Promise((resolve4) => {
5070
+ markStarted = resolve4;
5071
+ });
5072
+ const result = (async () => {
5073
+ const first = deadline ? await Promise.race([turn, deadline]) : await turn;
5074
+ clearTimeout(timer);
5075
+ if (first === "timeout") {
5076
+ void turn.then(markStarted);
5077
+ return task({ kind: "timeout", waitedMs: now() - arrivedAt });
5078
+ }
5079
+ markStarted();
5080
+ if (first) return task({ kind: "failed", error: first });
5081
+ return task({
5082
+ kind: "ready",
5083
+ remainingMs: deadlineMs === void 0 ? void 0 : Math.max(0, deadlineMs - (now() - arrivedAt))
5084
+ });
5085
+ })();
5086
+ tail = started;
5087
+ void Promise.allSettled([turn, result]).then(() => {
5088
+ held--;
5089
+ });
5090
+ return result;
5091
+ },
5092
+ held: () => held
5093
+ };
5101
5094
  }
5102
- var DEFAULT_COMPACTION_GATE_TIMEOUT_MS = 60 * 6e4;
5103
- function compactionGateTimeoutMs() {
5104
- const raw = Number(process.env.SKAILE_COMPACTION_GATE_TIMEOUT_MS);
5105
- return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_COMPACTION_GATE_TIMEOUT_MS;
5106
- }
5107
- var DEFAULT_COMPACTION_TURN_WAIT_MS = 10 * 6e4;
5108
- function compactionTurnWaitMs() {
5109
- const raw = Number(process.env.SKAILE_COMPACTION_TURN_WAIT_MS);
5110
- return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_COMPACTION_TURN_WAIT_MS;
5111
- }
5112
- var PLATFORM_COMPACTION_TURN_WAIT_MS = 3e4;
5113
- function compactionTurnWaitFor(trigger) {
5114
- return trigger === "threshold" ? compactionTurnWaitMs() : PLATFORM_COMPACTION_TURN_WAIT_MS;
5095
+
5096
+ // runner/src/serial-lock.ts
5097
+ function createSerialLock() {
5098
+ let tail = Promise.resolve();
5099
+ return (fn) => {
5100
+ const run = tail.then(fn, fn);
5101
+ tail = run.catch(() => void 0);
5102
+ return run;
5103
+ };
5115
5104
  }
5116
- async function claimDriverForCompaction(deps) {
5117
- const hold = deps.gate.begin();
5118
- let released = false;
5119
- const release2 = () => {
5120
- if (released) return;
5121
- released = true;
5122
- deps.gate.settle(hold);
5105
+ function createCoalescedTask(lock, fn) {
5106
+ let waiting = null;
5107
+ return (...args) => {
5108
+ if (waiting) return waiting;
5109
+ const run = lock(() => {
5110
+ waiting = null;
5111
+ return fn(...args);
5112
+ });
5113
+ waiting = run;
5114
+ return run;
5123
5115
  };
5124
- const timeoutMs = deps.timeoutMs ?? compactionTurnWaitMs();
5125
- if (await raceDeadline(deps.awaitClaimedTurns(), timeoutMs) !== "opened") {
5126
- release2();
5127
- deps.log(
5128
- `[serve] compaction skipped: a turn still held the driver after ${Math.round(timeoutMs / 1e3)}s`
5116
+ }
5117
+ async function recordStagedSkills(staged, projectDir, skillsDir, names, timeoutMs) {
5118
+ for (const name of names) {
5119
+ const path6 = join(projectDir, skillsDir, name);
5120
+ const st = await tryWithFsDeadline(`lstat ${path6}`, () => lstat(path6), null, timeoutMs);
5121
+ staged.set(
5122
+ name,
5123
+ st?.isDirectory() && st.birthtimeMs > 0 ? { path: path6, dev: st.dev, ino: st.ino, birthtimeMs: st.birthtimeMs } : null
5129
5124
  );
5130
- return null;
5131
- }
5132
- if (deps.isSwapping?.()) {
5133
- release2();
5134
- deps.log("[serve] compaction skipped: a driver swap is pending");
5135
- return null;
5136
5125
  }
5137
- const endTurn = deps.claimTurnSlot?.();
5138
- return () => {
5139
- endTurn?.();
5140
- release2();
5141
- };
5142
5126
  }
5143
- var TURN_COMPACTION_YIELD_ATTEMPTS = 3;
5144
- async function beginTurnClearOfCompaction(deps, maxAttempts = TURN_COMPACTION_YIELD_ATTEMPTS) {
5145
- for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
5146
- if (!await deps.awaitIdle()) return null;
5147
- const endTurn = await deps.claimTurn();
5148
- if (deps.gate.isIdle()) return endTurn;
5149
- endTurn();
5150
- }
5151
- throw new Error(`turn overtaken by a compaction ${maxAttempts} times running`);
5127
+ async function isStillOurs(record2, path6, timeoutMs) {
5128
+ if (!record2 || record2.path !== path6) return false;
5129
+ const st = await tryWithFsDeadline(`lstat ${path6}`, () => lstat(path6), null, timeoutMs);
5130
+ return st?.isDirectory() === true && st.dev === record2.dev && st.ino === record2.ino && st.birthtimeMs === record2.birthtimeMs;
5152
5131
  }
5153
- var COMPACTION_GATE_TIMEOUT_MESSAGE = "The session is still compacting its history and could not take this message. Send it again in a moment.";
5154
- async function guardTurnAgainstCompaction(deps) {
5155
- const outcome = await deps.gate.awaitIdle(deps.timeoutMs ?? compactionGateTimeoutMs());
5156
- if (outcome === "waited") deps.log(`[serve] ${deps.what} released after compaction settled`);
5157
- if (outcome !== "timeout") return true;
5158
- deps.log(`[serve] ${deps.what} abandoned: a compaction has held the driver past its bound`);
5159
- deps.sendEvent({ type: "error", message: COMPACTION_GATE_TIMEOUT_MESSAGE, fatal: false });
5160
- return false;
5132
+ async function recordLoadedSkillDigests(loaded, projectDir, skillsDir, names, opts) {
5133
+ for (const name of names) {
5134
+ if (opts.driverUp && opts.previouslyStaged.has(name)) continue;
5135
+ const path6 = join(projectDir, skillsDir, name);
5136
+ loaded.set(name, { path: path6, digest: await stagedSkillDigest(path6) });
5137
+ }
5161
5138
  }
5162
-
5163
- // runner/src/driver-swap-gate.ts
5164
- var TURN_BIND_ATTEMPTS = 3;
5165
- var DriverSwapGate = class {
5166
- /** Resolves when the in-flight driver recreate settles (resolve or reject). */
5167
- activeDriverSwap = null;
5168
- /** FIFO chain; resolves once every started turn has settled. */
5169
- turnTail = Promise.resolve();
5170
- /** FIFO chain of swaps; never rejects, resolves once every queued swap settled. */
5171
- swapTail = Promise.resolve();
5139
+ var DIGEST_DEADLINE_FACTOR = 4;
5140
+ var DIGEST_MAX_BYTES = 64 * 1024 * 1024;
5141
+ var DIGEST_CHUNK_BYTES = 64 * 1024;
5142
+ var DIGEST_OPEN_FLAGS = constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK;
5143
+ async function stagedSkillDigest(dir, timeoutMs) {
5144
+ return tryWithFsDeadline(
5145
+ `digest ${dir}`,
5146
+ async () => {
5147
+ const hash = createHash("sha256");
5148
+ let budget = DIGEST_MAX_BYTES;
5149
+ const hashFile = async (path6) => {
5150
+ const fh = await open(path6, DIGEST_OPEN_FLAGS);
5151
+ try {
5152
+ const st = await fh.stat();
5153
+ if (!st.isFile()) throw new Error(`not a regular file: ${path6}`);
5154
+ if (st.size > budget) throw new Error("skill too large to digest");
5155
+ const buf = Buffer.alloc(DIGEST_CHUNK_BYTES);
5156
+ let total = 0;
5157
+ for (; ; ) {
5158
+ const { bytesRead } = await fh.read(buf, 0, buf.length, null);
5159
+ if (bytesRead === 0) break;
5160
+ total += bytesRead;
5161
+ if (total > budget) throw new Error("skill too large to digest");
5162
+ hash.update(buf.subarray(0, bytesRead));
5163
+ }
5164
+ budget -= total;
5165
+ hash.update(`\0${total}\0`);
5166
+ } finally {
5167
+ await fh.close();
5168
+ }
5169
+ };
5170
+ const walk = async (rel) => {
5171
+ const here = rel ? join(dir, rel) : dir;
5172
+ if (rel && !(await lstat(here)).isDirectory()) throw new Error(`not a dir: ${here}`);
5173
+ const entries = await readdir(here, { withFileTypes: true });
5174
+ entries.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
5175
+ for (const entry of entries) {
5176
+ if (!rel && entry.name === STAGED_SKILL_MARKER) continue;
5177
+ const path6 = rel ? `${rel}/${entry.name}` : entry.name;
5178
+ if (entry.isDirectory()) {
5179
+ hash.update(`d\0${path6}\0`);
5180
+ await walk(path6);
5181
+ } else if (entry.isSymbolicLink()) {
5182
+ hash.update(`l\0${path6}\0${await readlink(join(dir, path6))}\0`);
5183
+ } else if (entry.isFile()) {
5184
+ hash.update(`f\0${path6}\0`);
5185
+ await hashFile(join(dir, path6));
5186
+ } else {
5187
+ hash.update(`o\0${path6}\0`);
5188
+ }
5189
+ }
5190
+ };
5191
+ await walk("");
5192
+ return hash.digest("hex");
5193
+ },
5194
+ null,
5195
+ fsDeadlineMs() * DIGEST_DEADLINE_FACTOR
5196
+ );
5197
+ }
5198
+ async function attachStagedSkill(deps, name) {
5199
+ try {
5200
+ const skillsDir = deps.skillsDir();
5201
+ const dirName = name.includes("/") ? name.split("/").pop() : name;
5202
+ const { staged, unchanged } = await deps.withSkillFsLock(async () => {
5203
+ const dest = join(deps.projectDir, skillsDir, dirName);
5204
+ const loaded = deps.stagedSkillDirs.has(dirName) ? deps.loadedSkillDigests.get(dirName) : void 0;
5205
+ const names = await stageMaterializedSkills(deps.projectDir, skillsDir, { declared: [name] });
5206
+ let same = false;
5207
+ if (names.includes(dirName)) {
5208
+ const digest = await stagedSkillDigest(dest);
5209
+ same = digest !== null && loaded?.path === dest && loaded.digest === digest;
5210
+ if (!same) deps.loadedSkillDigests.set(dirName, { path: dest, digest });
5211
+ }
5212
+ await recordStagedSkills(deps.stagedSkillDirs, deps.projectDir, skillsDir, names);
5213
+ return { staged: names, unchanged: same ? dirName : null };
5214
+ });
5215
+ if (staged.length === 0) {
5216
+ return {
5217
+ ok: false,
5218
+ code: "skill_not_materialized",
5219
+ message: `no materialized skill matching "${name}" under .skaile/assets/skill`
5220
+ };
5221
+ }
5222
+ const live = deps.driverStarted();
5223
+ const toRestart = staged.filter((s) => s !== unchanged);
5224
+ if (toRestart.length === 0) {
5225
+ deps.log(
5226
+ `[serve] runner.attach_instance: skill ${staged.join(", ")} already staged and unchanged \u2014 no driver restart`
5227
+ );
5228
+ return { ok: true, id: name, live, toolCount: 0 };
5229
+ }
5230
+ deps.log(`[serve] runner.attach_instance staged skill(s): ${toRestart.join(", ")}`);
5231
+ deps.scheduleSkillRestart(toRestart.map((s) => ({ name: s, action: "add" })));
5232
+ return { ok: true, id: name, live, toolCount: 0 };
5233
+ } catch (err) {
5234
+ return {
5235
+ ok: false,
5236
+ code: "skill_attach_failed",
5237
+ message: err instanceof Error ? err.message : String(err)
5238
+ };
5239
+ }
5240
+ }
5241
+ async function detachStagedSkill(deps, name) {
5242
+ try {
5243
+ const skillsDir = deps.skillsDir();
5244
+ const dirName = name.split("/").pop() ?? name;
5245
+ const result = await deps.withSkillFsLock(async () => {
5246
+ const knownStaged = await isStillOurs(
5247
+ deps.stagedSkillDirs.get(dirName),
5248
+ join(deps.projectDir, skillsDir, dirName)
5249
+ );
5250
+ return unstageSkill(deps.projectDir, skillsDir, name, { knownStaged });
5251
+ });
5252
+ const live = deps.driverStarted();
5253
+ if ("reason" in result) {
5254
+ deps.log(
5255
+ `[serve] runner.detach_instance: "${result.name}" exists but was not staged by the runner \u2014 left in place`
5256
+ );
5257
+ return { ok: true, id: result.name, removed: false, live, reason: result.reason };
5258
+ }
5259
+ const wasTracked = deps.stagedSkillDirs.delete(result.name);
5260
+ if (!result.removedStaged && !wasTracked) {
5261
+ deps.log(`[serve] runner.detach_instance: skill "${result.name}" was not staged`);
5262
+ return { ok: true, id: result.name, removed: result.removedAsset, live };
5263
+ }
5264
+ deps.log(`[serve] runner.detach_instance unstaged skill: ${result.name}`);
5265
+ deps.scheduleSkillRestart([{ name: result.name, action: "remove" }]);
5266
+ return { ok: true, id: result.name, removed: true, live };
5267
+ } catch (err) {
5268
+ return {
5269
+ ok: false,
5270
+ code: "skill_detach_failed",
5271
+ message: err instanceof Error ? err.message : String(err)
5272
+ };
5273
+ }
5274
+ }
5275
+ async function pathState(path6) {
5276
+ try {
5277
+ await withFsDeadline(`lstat ${path6}`, () => lstat(path6));
5278
+ return "present";
5279
+ } catch (err) {
5280
+ return err?.code === "ENOENT" ? "absent" : "unknown";
5281
+ }
5282
+ }
5283
+ async function removeUnlistedStagedSkills(deps, skillsDir) {
5284
+ const allowed = await readSkillAllowList(deps.projectDir);
5285
+ if (!allowed) return [];
5286
+ const root = join(deps.projectDir, skillsDir);
5287
+ const entries = await tryWithFsDeadline(
5288
+ `readdir ${root}`,
5289
+ () => readdir(root, { withFileTypes: true }),
5290
+ []
5291
+ );
5292
+ const removed = [];
5293
+ for (const entry of entries) {
5294
+ if (!entry.isDirectory() || allowed.has(entry.name)) continue;
5295
+ try {
5296
+ const dir = join(root, entry.name);
5297
+ if (await pathState(join(deps.projectDir, ".skaile/assets/skill", entry.name)) !== "absent") {
5298
+ continue;
5299
+ }
5300
+ const knownStaged = await isStillOurs(deps.stagedSkillDirs.get(entry.name), dir);
5301
+ if (!knownStaged && !await isStagedSkillMarkerFor(dir, entry.name).catch(() => false))
5302
+ continue;
5303
+ const result = await unstageSkill(deps.projectDir, skillsDir, entry.name, {
5304
+ knownStaged,
5305
+ keepAsset: true
5306
+ });
5307
+ if ("reason" in result || !result.removedStaged) continue;
5308
+ deps.stagedSkillDirs.delete(result.name);
5309
+ removed.push(result.name);
5310
+ } catch (err) {
5311
+ deps.log(
5312
+ `[serve] could not remove unlisted skill "${entry.name}" (left in place): ${err instanceof Error ? err.message : String(err)}`
5313
+ );
5314
+ }
5315
+ }
5316
+ return removed;
5317
+ }
5318
+
5319
+ // runner/src/compaction-gate.ts
5320
+ var CompactionGate = class {
5321
+ /**
5322
+ * Every compaction currently holding the driver.
5323
+ *
5324
+ * A set rather than a boolean because holds can overlap: a driver swap
5325
+ * rebuilds the orchestrator, and the new instance carries its own
5326
+ * `_isCompacting`, so it can start a compaction while the previous
5327
+ * instance's call is still unwinding. Keyed by handle so the older one
5328
+ * settling cannot open the gate under the newer one — the same identity
5329
+ * check `DriverSwapGate.beginSwap` makes for swaps.
5330
+ */
5331
+ held = /* @__PURE__ */ new Set();
5332
+ /** Resolves when the last hold is released. Null whenever the driver is free. */
5333
+ opened = null;
5334
+ release = null;
5335
+ /**
5336
+ * Mark a compaction as occupying the driver. The returned handle is the only
5337
+ * thing that can release it.
5338
+ */
5339
+ begin() {
5340
+ const hold = /* @__PURE__ */ Symbol("compaction");
5341
+ if (this.held.size === 0) {
5342
+ this.opened = new Promise((resolve4) => {
5343
+ this.release = resolve4;
5344
+ });
5345
+ }
5346
+ this.held.add(hold);
5347
+ return hold;
5348
+ }
5349
+ /**
5350
+ * Release one hold, opening the gate once none remain. Unknown and null
5351
+ * handles are ignored, so a settle signal that fires without a matching
5352
+ * `begin()` is harmless.
5353
+ *
5354
+ * Call this from a signal the orchestrator emits unconditionally. A `begin()`
5355
+ * whose `settle()` can be skipped holds user turns forever, which is a worse
5356
+ * failure than the discarded reply this gate exists to prevent.
5357
+ */
5358
+ settle(hold) {
5359
+ if (hold == null || !this.held.delete(hold)) return;
5360
+ if (this.held.size > 0) return;
5361
+ const release2 = this.release;
5362
+ this.opened = null;
5363
+ this.release = null;
5364
+ release2?.();
5365
+ }
5366
+ /** Whether no compaction holds the driver right now. */
5367
+ isIdle() {
5368
+ return this.held.size === 0;
5369
+ }
5370
+ /**
5371
+ * Wait for the driver to be free.
5372
+ *
5373
+ * Returns `idle` when nothing was in flight, `waited` when every compaction
5374
+ * settled within the bound, and `timeout` when they did not. A caller that
5375
+ * gets `timeout` must surface it rather than dispatch: a compaction still
5376
+ * holding the driver past this bound is stuck, and dispatching into it
5377
+ * reproduces the silent loss.
5378
+ *
5379
+ * Re-checks in a loop, as `DriverSwapGate.awaitSwap` does, so a compaction
5380
+ * armed while this turn was already waiting is also awaited. `timeoutMs`
5381
+ * bounds the whole wait, not each iteration.
5382
+ */
5383
+ async awaitIdle(timeoutMs) {
5384
+ if (this.held.size === 0) return "idle";
5385
+ const deadline = Date.now() + timeoutMs;
5386
+ let waited = false;
5387
+ while (this.held.size > 0) {
5388
+ const opened = this.opened;
5389
+ if (!opened) break;
5390
+ const remaining = deadline - Date.now();
5391
+ if (remaining <= 0) return "timeout";
5392
+ if (await raceDeadline(opened, remaining) === "timeout") return "timeout";
5393
+ waited = true;
5394
+ }
5395
+ return waited ? "waited" : "idle";
5396
+ }
5397
+ };
5398
+ async function raceDeadline(opened, ms) {
5399
+ let timer;
5400
+ const expired = new Promise((resolve4) => {
5401
+ timer = setTimeout(() => resolve4("timeout"), ms);
5402
+ timer.unref?.();
5403
+ });
5404
+ try {
5405
+ return await Promise.race([opened.then(() => "opened"), expired]);
5406
+ } finally {
5407
+ if (timer) clearTimeout(timer);
5408
+ }
5409
+ }
5410
+ var DEFAULT_COMPACTION_GATE_TIMEOUT_MS = 60 * 6e4;
5411
+ function compactionGateTimeoutMs() {
5412
+ const raw = Number(process.env.SKAILE_COMPACTION_GATE_TIMEOUT_MS);
5413
+ return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_COMPACTION_GATE_TIMEOUT_MS;
5414
+ }
5415
+ var DEFAULT_COMPACTION_TURN_WAIT_MS = 10 * 6e4;
5416
+ function compactionTurnWaitMs() {
5417
+ const raw = Number(process.env.SKAILE_COMPACTION_TURN_WAIT_MS);
5418
+ return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_COMPACTION_TURN_WAIT_MS;
5419
+ }
5420
+ var PLATFORM_COMPACTION_TURN_WAIT_MS = 3e4;
5421
+ function compactionTurnWaitFor(trigger) {
5422
+ return trigger === "threshold" ? compactionTurnWaitMs() : PLATFORM_COMPACTION_TURN_WAIT_MS;
5423
+ }
5424
+ async function claimDriverForCompaction(deps) {
5425
+ const hold = deps.gate.begin();
5426
+ let released = false;
5427
+ const release2 = () => {
5428
+ if (released) return;
5429
+ released = true;
5430
+ deps.gate.settle(hold);
5431
+ };
5432
+ const { timeoutMs } = deps;
5433
+ if (await raceDeadline(deps.awaitClaimedTurns(), timeoutMs) !== "opened") {
5434
+ release2();
5435
+ deps.log(
5436
+ `[serve] compaction skipped: a turn still held the driver after ${Math.round(timeoutMs / 1e3)}s`
5437
+ );
5438
+ return null;
5439
+ }
5440
+ if (deps.isDriverReplaced()) {
5441
+ release2();
5442
+ deps.log("[serve] compaction skipped: its driver is being or has been replaced");
5443
+ return null;
5444
+ }
5445
+ const endTurn = deps.claimTurnSlot();
5446
+ return () => {
5447
+ endTurn();
5448
+ release2();
5449
+ };
5450
+ }
5451
+ var TURN_COMPACTION_YIELD_ATTEMPTS = 3;
5452
+ async function beginTurnClearOfCompaction(deps) {
5453
+ for (let attempt = 0; attempt < TURN_COMPACTION_YIELD_ATTEMPTS; attempt += 1) {
5454
+ if (!await deps.awaitIdle()) return null;
5455
+ const endTurn = await deps.claimTurn();
5456
+ if (deps.gate.isIdle()) return endTurn;
5457
+ endTurn();
5458
+ }
5459
+ throw new Error(`turn overtaken by a compaction ${TURN_COMPACTION_YIELD_ATTEMPTS} times running`);
5460
+ }
5461
+ var COMPACTION_GATE_TIMEOUT_MESSAGE = "The session is still compacting its history and could not take this message. Send it again in a moment.";
5462
+ async function guardTurnAgainstCompaction(deps) {
5463
+ const outcome = await deps.gate.awaitIdle(compactionGateTimeoutMs());
5464
+ if (outcome === "waited") deps.log(`[serve] ${deps.what} released after compaction settled`);
5465
+ if (outcome !== "timeout") return true;
5466
+ deps.log(`[serve] ${deps.what} abandoned: a compaction has held the driver past its bound`);
5467
+ deps.sendEvent({ type: "error", message: COMPACTION_GATE_TIMEOUT_MESSAGE, fatal: false });
5468
+ return false;
5469
+ }
5470
+
5471
+ // runner/src/driver-swap-gate.ts
5472
+ var TURN_BIND_ATTEMPTS = 3;
5473
+ var DriverSwapGate = class {
5474
+ /** Resolves when the in-flight driver recreate settles (resolve or reject). */
5475
+ activeDriverSwap = null;
5476
+ /** FIFO chain; resolves once every started turn has settled. */
5477
+ turnTail = Promise.resolve();
5478
+ /** FIFO chain of swaps; never rejects, resolves once every queued swap settled. */
5479
+ swapTail = Promise.resolve();
5172
5480
  /**
5173
5481
  * Mark a turn as in flight. Call at the real `driver.prompt()` dispatch — not
5174
5482
  * earlier — so a same-handler restart (e.g. restaging skills before the turn)
@@ -5273,103 +5581,23 @@ var DriverSwapGate = class {
5273
5581
  beginSwap(recreate) {
5274
5582
  this.activeDriverSwap = recreate;
5275
5583
  const clear = () => {
5276
- if (this.activeDriverSwap === recreate) this.activeDriverSwap = null;
5277
- };
5278
- recreate.then(clear, clear);
5279
- }
5280
- /**
5281
- * Await the in-flight swap before reading the live driver. Re-checks in a loop
5282
- * so a second swap starting during the wait is also awaited.
5283
- */
5284
- async awaitSwap() {
5285
- while (this.activeDriverSwap) {
5286
- try {
5287
- await this.activeDriverSwap;
5288
- } catch {
5289
- }
5290
- }
5291
- }
5292
- };
5293
-
5294
- // runner/src/init-build-hold.ts
5295
- var DEFAULT_CONNECTOR_MUTATE_INIT_HOLD_MS = 1e4;
5296
- function resolveConnectorMutateInitHoldMs(env = process.env) {
5297
- const parsed = Number.parseInt(env.SKAILE_CONNECTOR_MUTATE_INIT_HOLD_MS ?? "", 10);
5298
- return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_CONNECTOR_MUTATE_INIT_HOLD_MS;
5299
- }
5300
- function createInitBuildHold(now = Date.now) {
5301
- let current = null;
5302
- let tail = Promise.resolve();
5303
- let held = 0;
5304
- const building = () => current !== null && !current.done;
5305
- async function awaitNewestBuild() {
5306
- for (; ; ) {
5307
- const build = current;
5308
- if (!build) return null;
5309
- if (!build.done) await build.settled;
5310
- if (current === build) return build.error;
5311
- }
5584
+ if (this.activeDriverSwap === recreate) this.activeDriverSwap = null;
5585
+ };
5586
+ recreate.then(clear, clear);
5312
5587
  }
5313
- return {
5314
- beginBuild() {
5315
- let resolve4;
5316
- const build = {
5317
- done: false,
5318
- error: null,
5319
- settled: new Promise((r) => {
5320
- resolve4 = r;
5321
- })
5322
- };
5323
- current = build;
5324
- const settle = (error) => {
5325
- if (build.done) return;
5326
- build.done = true;
5327
- build.error = error;
5328
- resolve4();
5329
- };
5330
- return { succeed: () => settle(null), fail: (error) => settle(error) };
5331
- },
5332
- run(task, deadlineMs) {
5333
- if (held === 0 && !building()) {
5334
- if (current?.error) return task({ kind: "failed", error: current.error });
5335
- return task({ kind: "ready", remainingMs: void 0 });
5588
+ /**
5589
+ * Await the in-flight swap before reading the live driver. Re-checks in a loop
5590
+ * so a second swap starting during the wait is also awaited.
5591
+ */
5592
+ async awaitSwap() {
5593
+ while (this.activeDriverSwap) {
5594
+ try {
5595
+ await this.activeDriverSwap;
5596
+ } catch {
5336
5597
  }
5337
- held++;
5338
- const arrivedAt = now();
5339
- const previous = tail;
5340
- const turn = previous.then(awaitNewestBuild);
5341
- let timer;
5342
- const deadline = deadlineMs === void 0 ? null : new Promise((resolve4) => {
5343
- timer = setTimeout(() => resolve4("timeout"), deadlineMs);
5344
- timer.unref?.();
5345
- });
5346
- let markStarted;
5347
- const started = new Promise((resolve4) => {
5348
- markStarted = resolve4;
5349
- });
5350
- const result = (async () => {
5351
- const first = deadline ? await Promise.race([turn, deadline]) : await turn;
5352
- clearTimeout(timer);
5353
- if (first === "timeout") {
5354
- void turn.then(markStarted);
5355
- return task({ kind: "timeout", waitedMs: now() - arrivedAt });
5356
- }
5357
- markStarted();
5358
- if (first) return task({ kind: "failed", error: first });
5359
- return task({
5360
- kind: "ready",
5361
- remainingMs: deadlineMs === void 0 ? void 0 : Math.max(0, deadlineMs - (now() - arrivedAt))
5362
- });
5363
- })();
5364
- tail = started;
5365
- void Promise.allSettled([turn, result]).then(() => {
5366
- held--;
5367
- });
5368
- return result;
5369
- },
5370
- held: () => held
5371
- };
5372
- }
5598
+ }
5599
+ }
5600
+ };
5373
5601
 
5374
5602
  // runner/src/pending-turns.ts
5375
5603
  var PendingTurns = class {
@@ -5393,9 +5621,15 @@ var PendingTurns = class {
5393
5621
  }
5394
5622
  };
5395
5623
  }
5396
- /** Call at a turn's end, while that turn is still counted: whether to report idle now. */
5397
- shouldReportIdle() {
5398
- if (this.count > 1) {
5624
+ /**
5625
+ * Whether to report idle now, at the end of something that ran on the driver.
5626
+ * A `"turn"` end belongs to a counted turn, which is still counted when it
5627
+ * fires. A `"compaction"` end (the compaction turn, or the snapshot turn after
5628
+ * it) is no counted turn, so every counted turn is waiting behind it.
5629
+ */
5630
+ shouldReportIdle(endedBy) {
5631
+ const own = endedBy === "turn" ? 1 : 0;
5632
+ if (this.count > own) {
5399
5633
  this.idleOwed = true;
5400
5634
  return false;
5401
5635
  }
@@ -5463,248 +5697,298 @@ var PromptTurnQueue = class {
5463
5697
  }
5464
5698
  };
5465
5699
 
5466
- // runner/src/serial-lock.ts
5467
- function createSerialLock() {
5468
- let tail = Promise.resolve();
5469
- return (fn) => {
5470
- const run = tail.then(fn, fn);
5471
- tail = run.catch(() => void 0);
5472
- return run;
5473
- };
5474
- }
5475
- function createCoalescedTask(lock, fn) {
5476
- let waiting = null;
5477
- return (...args) => {
5478
- if (waiting) return waiting;
5479
- const run = lock(() => {
5480
- waiting = null;
5481
- return fn(...args);
5700
+ // runner/src/turn-scheduler.ts
5701
+ var FLOW_TURN_COMPACTION_TIMEOUT = "Flow turn abandoned: a compaction held the driver";
5702
+ var TurnScheduler = class {
5703
+ constructor(deps) {
5704
+ this.deps = deps;
5705
+ }
5706
+ deps;
5707
+ swaps = new DriverSwapGate();
5708
+ compactions = new CompactionGate();
5709
+ pending = new PendingTurns(() => this.sendIdle());
5710
+ queue = new PromptTurnQueue();
5711
+ /** The compaction that armed the event suppression; null while none runs. */
5712
+ suppressedBy = null;
5713
+ /** Compactions past their end callback and not yet settled: a snapshot turn may still run. */
5714
+ snapshotTurns = /* @__PURE__ */ new Set();
5715
+ /** Counts {@link compactionHooks} calls; only the latest hooks may start a compaction. */
5716
+ hooksGeneration = 0;
5717
+ /** Run a chat prompt: queue it, wait out swaps and compactions, claim the driver, dispatch. */
5718
+ prompt(turn) {
5719
+ return this.counted(async () => {
5720
+ const place = this.queue.join(turn.owner);
5721
+ try {
5722
+ if (place.queued) {
5723
+ turn.onQueued?.();
5724
+ const queuedAt = Date.now();
5725
+ await place.ready;
5726
+ this.deps.logPhase("prompt_queue_wait", queuedAt, { dropped: place.dropped });
5727
+ if (place.dropped) return;
5728
+ }
5729
+ await this.runPrompt(turn, place.leave);
5730
+ } finally {
5731
+ place.leave();
5732
+ }
5482
5733
  });
5483
- waiting = run;
5484
- return run;
5485
- };
5486
- }
5487
- async function recordStagedSkills(staged, projectDir, skillsDir, names, timeoutMs) {
5488
- for (const name of names) {
5489
- const path6 = join(projectDir, skillsDir, name);
5490
- const st = await tryWithFsDeadline(`lstat ${path6}`, () => lstat(path6), null, timeoutMs);
5491
- staged.set(
5492
- name,
5493
- st?.isDirectory() && st.birthtimeMs > 0 ? { path: path6, dev: st.dev, ino: st.ino, birthtimeMs: st.birthtimeMs } : null
5494
- );
5495
5734
  }
5496
- }
5497
- async function isStillOurs(record2, path6, timeoutMs) {
5498
- if (!record2 || record2.path !== path6) return false;
5499
- const st = await tryWithFsDeadline(`lstat ${path6}`, () => lstat(path6), null, timeoutMs);
5500
- return st?.isDirectory() === true && st.dev === record2.dev && st.ino === record2.ino && st.birthtimeMs === record2.birthtimeMs;
5501
- }
5502
- async function recordLoadedSkillDigests(loaded, projectDir, skillsDir, names, opts) {
5503
- for (const name of names) {
5504
- if (opts.driverUp && opts.previouslyStaged.has(name)) continue;
5505
- const path6 = join(projectDir, skillsDir, name);
5506
- loaded.set(name, { path: path6, digest: await stagedSkillDigest(path6) });
5735
+ /**
5736
+ * Run a question reply. A paused question takes the answer at once, ahead of
5737
+ * every wait. A recreate waits for the paused turn, and that turn waits for
5738
+ * this answer, so gating the answer on the swap wedges the session for good.
5739
+ * Anything else is a fresh turn and queues like a prompt.
5740
+ */
5741
+ reply(turn) {
5742
+ return this.counted(async () => {
5743
+ if (!await turn.admit()) return;
5744
+ const paused = this.deps.getDriver();
5745
+ if (paused.hasPendingQuestion()) {
5746
+ if (paused.answerQuestion(turn.answer, turn.question, turn.requestId)) {
5747
+ turn.onAnswered?.();
5748
+ return;
5749
+ }
5750
+ } else {
5751
+ await this.swaps.awaitSwap();
5752
+ }
5753
+ if (!this.deps.getDriver().acceptsReplyAsPrompt) {
5754
+ if (!this.deps.getDriver().hasPendingQuestion()) {
5755
+ this.deps.log("[serve] reply: driver takes no reply as a prompt; answer not delivered");
5756
+ this.deps.sendEvent({
5757
+ type: "error",
5758
+ message: "This question is no longer open, so the agent did not get your answer. Send it as a new message.",
5759
+ fatal: false
5760
+ });
5761
+ }
5762
+ return;
5763
+ }
5764
+ let endTurn;
5765
+ try {
5766
+ endTurn = await this.claim("reply", () => this.deps.ensureDriverStarted());
5767
+ } catch (err) {
5768
+ this.deps.sendEvent(replyFailed(err));
5769
+ return;
5770
+ }
5771
+ if (!endTurn) return;
5772
+ try {
5773
+ await this.deps.getDriver().prompt(freshTurnReplyPrompt(turn.answer, turn.question));
5774
+ } catch (err) {
5775
+ this.deps.sendEvent(replyFailed(err));
5776
+ } finally {
5777
+ endTurn();
5778
+ }
5779
+ });
5507
5780
  }
5508
- }
5509
- var DIGEST_DEADLINE_FACTOR = 4;
5510
- var DIGEST_MAX_BYTES = 64 * 1024 * 1024;
5511
- var DIGEST_CHUNK_BYTES = 64 * 1024;
5512
- var DIGEST_OPEN_FLAGS = constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK;
5513
- async function stagedSkillDigest(dir, timeoutMs) {
5514
- return tryWithFsDeadline(
5515
- `digest ${dir}`,
5516
- async () => {
5517
- const hash = createHash("sha256");
5518
- let budget = DIGEST_MAX_BYTES;
5519
- const hashFile = async (path6) => {
5520
- const fh = await open(path6, DIGEST_OPEN_FLAGS);
5781
+ /**
5782
+ * Run one flow turn and resolve on its `agent_end`.
5783
+ *
5784
+ * Waits out a pending swap, any compaction, and every turn already in flight.
5785
+ * The in-flight wait is what makes the `agent_end` listener safe: a flow
5786
+ * started from inside a chat turn would otherwise resolve on that turn's end.
5787
+ * The slot is held for the whole turn, never across a wait, which would stall
5788
+ * a swap behind a compaction.
5789
+ */
5790
+ flowTurn(prompt, timeoutMs) {
5791
+ return this.counted(async () => {
5792
+ for (; ; ) {
5793
+ await this.swaps.awaitSwap();
5794
+ if (!await this.awaitCompactionIdle("flow turn"))
5795
+ throw new Error(FLOW_TURN_COMPACTION_TIMEOUT);
5796
+ const endTurn = await this.swaps.beginQueuedTurn();
5797
+ if (this.swaps.isSwapping() || !this.compactions.isIdle()) {
5798
+ endTurn();
5799
+ continue;
5800
+ }
5521
5801
  try {
5522
- const st = await fh.stat();
5523
- if (!st.isFile()) throw new Error(`not a regular file: ${path6}`);
5524
- if (st.size > budget) throw new Error("skill too large to digest");
5525
- const buf = Buffer.alloc(DIGEST_CHUNK_BYTES);
5526
- let total = 0;
5527
- for (; ; ) {
5528
- const { bytesRead } = await fh.read(buf, 0, buf.length, null);
5529
- if (bytesRead === 0) break;
5530
- total += bytesRead;
5531
- if (total > budget) throw new Error("skill too large to digest");
5532
- hash.update(buf.subarray(0, bytesRead));
5533
- }
5534
- budget -= total;
5535
- hash.update(`\0${total}\0`);
5802
+ await this.deps.ensureDriverStarted();
5803
+ await awaitAgentEnd(this.deps.getDriver(), prompt, timeoutMs);
5804
+ return;
5536
5805
  } finally {
5537
- await fh.close();
5538
- }
5539
- };
5540
- const walk = async (rel) => {
5541
- const here = rel ? join(dir, rel) : dir;
5542
- if (rel && !(await lstat(here)).isDirectory()) throw new Error(`not a dir: ${here}`);
5543
- const entries = await readdir(here, { withFileTypes: true });
5544
- entries.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
5545
- for (const entry of entries) {
5546
- if (!rel && entry.name === STAGED_SKILL_MARKER) continue;
5547
- const path6 = rel ? `${rel}/${entry.name}` : entry.name;
5548
- if (entry.isDirectory()) {
5549
- hash.update(`d\0${path6}\0`);
5550
- await walk(path6);
5551
- } else if (entry.isSymbolicLink()) {
5552
- hash.update(`l\0${path6}\0${await readlink(join(dir, path6))}\0`);
5553
- } else if (entry.isFile()) {
5554
- hash.update(`f\0${path6}\0`);
5555
- await hashFile(join(dir, path6));
5556
- } else {
5557
- hash.update(`o\0${path6}\0`);
5558
- }
5806
+ endTurn();
5559
5807
  }
5560
- };
5561
- await walk("");
5562
- return hash.digest("hex");
5563
- },
5564
- null,
5565
- fsDeadlineMs() * DIGEST_DEADLINE_FACTOR
5566
- );
5567
- }
5568
- async function attachStagedSkill(deps, name) {
5569
- try {
5570
- const skillsDir = deps.skillsDir();
5571
- const dirName = name.includes("/") ? name.split("/").pop() : name;
5572
- const { staged, unchanged } = await deps.withSkillFsLock(async () => {
5573
- const dest = join(deps.projectDir, skillsDir, dirName);
5574
- const loaded = deps.stagedSkillDirs.has(dirName) ? deps.loadedSkillDigests.get(dirName) : void 0;
5575
- const names = await stageMaterializedSkills(deps.projectDir, skillsDir, { declared: [name] });
5576
- let same = false;
5577
- if (names.includes(dirName)) {
5578
- const digest = await stagedSkillDigest(dest);
5579
- same = digest !== null && loaded?.path === dest && loaded.digest === digest;
5580
- if (!same) deps.loadedSkillDigests.set(dirName, { path: dest, digest });
5581
5808
  }
5582
- await recordStagedSkills(deps.stagedSkillDirs, deps.projectDir, skillsDir, names);
5583
- return { staged: names, unchanged: same ? dirName : null };
5584
5809
  });
5585
- if (staged.length === 0) {
5586
- return {
5587
- ok: false,
5588
- code: "skill_not_materialized",
5589
- message: `no materialized skill matching "${name}" under .skaile/assets/skill`
5590
- };
5591
- }
5592
- const live = deps.driverStarted();
5593
- const toRestart = staged.filter((s) => s !== unchanged);
5594
- if (toRestart.length === 0) {
5595
- deps.log(
5596
- `[serve] runner.attach_instance: skill ${staged.join(", ")} already staged and unchanged \u2014 no driver restart`
5597
- );
5598
- return { ok: true, id: name, live, toolCount: 0 };
5599
- }
5600
- deps.log(`[serve] runner.attach_instance staged skill(s): ${toRestart.join(", ")}`);
5601
- deps.scheduleSkillRestart(toRestart.map((s) => ({ name: s, action: "add" })));
5602
- return { ok: true, id: name, live, toolCount: 0 };
5603
- } catch (err) {
5604
- return {
5605
- ok: false,
5606
- code: "skill_attach_failed",
5607
- message: err instanceof Error ? err.message : String(err)
5608
- };
5609
5810
  }
5610
- }
5611
- async function detachStagedSkill(deps, name) {
5612
- try {
5613
- const skillsDir = deps.skillsDir();
5614
- const dirName = name.split("/").pop() ?? name;
5615
- const result = await deps.withSkillFsLock(async () => {
5616
- const knownStaged = await isStillOurs(
5617
- deps.stagedSkillDirs.get(dirName),
5618
- join(deps.projectDir, skillsDir, dirName)
5619
- );
5620
- return unstageSkill(deps.projectDir, skillsDir, name, { knownStaged });
5621
- });
5622
- const live = deps.driverStarted();
5623
- if ("reason" in result) {
5624
- deps.log(
5625
- `[serve] runner.detach_instance: "${result.name}" exists but was not staged by the runner \u2014 left in place`
5626
- );
5627
- return { ok: true, id: result.name, removed: false, live, reason: result.reason };
5628
- }
5629
- const wasTracked = deps.stagedSkillDirs.delete(result.name);
5630
- if (!result.removedStaged && !wasTracked) {
5631
- deps.log(`[serve] runner.detach_instance: skill "${result.name}" was not staged`);
5632
- return { ok: true, id: result.name, removed: result.removedAsset, live };
5633
- }
5634
- deps.log(`[serve] runner.detach_instance unstaged skill: ${result.name}`);
5635
- deps.scheduleSkillRestart([{ name: result.name, action: "remove" }]);
5636
- return { ok: true, id: result.name, removed: true, live };
5637
- } catch (err) {
5811
+ /**
5812
+ * Run a driver swap (recreate): queued behind every earlier swap, then behind
5813
+ * every turn holding a slot. Prompts defer to it from the moment it is
5814
+ * registered. Never call from inside a swap: it would wait on itself.
5815
+ */
5816
+ runSwap(swap) {
5817
+ return this.swaps.runSwap(swap);
5818
+ }
5819
+ /**
5820
+ * The driver ended a turn. Reports idle unless a compaction suppresses it, or
5821
+ * more turns are queued behind this one, in which case it is owed and goes
5822
+ * out when the last of them settles.
5823
+ */
5824
+ reportTurnEnd() {
5825
+ this.reportIdleIfDrained(this.snapshotTurns.size > 0 ? "compaction" : "turn");
5826
+ }
5827
+ /**
5828
+ * Drop every prompt `senderId` has waiting, in queue order. The prompt whose
5829
+ * turn is running is not waiting, and a prompt with no sender is nobody's to drop.
5830
+ */
5831
+ dropQueuedPromptsFrom(senderId) {
5832
+ return this.queue.dropQueuedFrom(senderId);
5833
+ }
5834
+ /** Turns taken on and not yet settled, waiting ones included. */
5835
+ get pendingTurns() {
5836
+ return this.pending.size;
5837
+ }
5838
+ /**
5839
+ * Whether a compaction is running, from its start until its end callback: the
5840
+ * host must not see the compaction turn's events, `status: idle` included.
5841
+ */
5842
+ get isCompacting() {
5843
+ return this.suppressedBy !== null;
5844
+ }
5845
+ /**
5846
+ * The hooks one compaction orchestrator wires to take part in scheduling: it
5847
+ * claims the driver before it starts, holds user turns off from its start
5848
+ * until it has settled, and suppresses turn events from its start until its
5849
+ * end. Call once per orchestrator; a newer call retires the hooks before it,
5850
+ * so a stale orchestrator (its driver replaced by a swap) can no longer
5851
+ * start a compaction.
5852
+ */
5853
+ compactionHooks() {
5854
+ const generation = ++this.hooksGeneration;
5855
+ const superseded = () => generation !== this.hooksGeneration;
5856
+ let hold = null;
5638
5857
  return {
5639
- ok: false,
5640
- code: "skill_detach_failed",
5641
- message: err instanceof Error ? err.message : String(err)
5858
+ // A user turn that already holds a slot keeps the driver; the compaction
5859
+ // waits for it rather than prompting the driver under it. A platform call
5860
+ // waits briefly: a turn paused on a question can hold its slot for hours.
5861
+ claimDriver: async (trigger) => {
5862
+ if (superseded()) {
5863
+ this.deps.log("[serve] compaction skipped: its driver has been replaced");
5864
+ return null;
5865
+ }
5866
+ return claimDriverForCompaction({
5867
+ gate: this.compactions,
5868
+ awaitClaimedTurns: () => this.swaps.awaitActiveTurn(),
5869
+ isDriverReplaced: () => this.swaps.isSwapping() || superseded(),
5870
+ claimTurnSlot: () => this.swaps.beginTurn(),
5871
+ log: this.deps.log,
5872
+ timeoutMs: compactionTurnWaitFor(trigger)
5873
+ });
5874
+ },
5875
+ onCompactionStart: (trigger) => {
5876
+ hold = this.compactions.begin();
5877
+ this.suppressedBy = hold;
5878
+ this.deps.sendEvent({ type: "status", phase: "compacting" });
5879
+ this.deps.log(`[serve] compaction started (trigger=${trigger})`);
5880
+ },
5881
+ onCompactionEnd: () => {
5882
+ if (hold === null || this.suppressedBy !== hold) return;
5883
+ this.suppressedBy = null;
5884
+ this.snapshotTurns.add(hold);
5885
+ this.reportIdleIfDrained("compaction");
5886
+ },
5887
+ onCompactionSettled: () => {
5888
+ if (hold !== null && this.suppressedBy === hold) this.suppressedBy = null;
5889
+ if (hold !== null) this.snapshotTurns.delete(hold);
5890
+ this.compactions.settle(hold);
5891
+ hold = null;
5892
+ }
5642
5893
  };
5643
5894
  }
5644
- }
5645
- async function pathState(path6) {
5646
- try {
5647
- await withFsDeadline(`lstat ${path6}`, () => lstat(path6));
5648
- return "present";
5649
- } catch (err) {
5650
- return err?.code === "ENOENT" ? "absent" : "unknown";
5651
- }
5652
- }
5653
- async function removeUnlistedStagedSkills(deps, skillsDir) {
5654
- const allowed = await readSkillAllowList(deps.projectDir);
5655
- if (!allowed) return [];
5656
- const root = join(deps.projectDir, skillsDir);
5657
- const entries = await tryWithFsDeadline(
5658
- `readdir ${root}`,
5659
- () => readdir(root, { withFileTypes: true }),
5660
- []
5661
- );
5662
- const removed = [];
5663
- for (const entry of entries) {
5664
- if (!entry.isDirectory() || allowed.has(entry.name)) continue;
5665
- try {
5666
- const dir = join(root, entry.name);
5667
- if (await pathState(join(deps.projectDir, ".skaile/assets/skill", entry.name)) !== "absent") {
5668
- continue;
5895
+ async runPrompt(turn, leaveQueue) {
5896
+ if (!await turn.admit()) return;
5897
+ await this.swaps.awaitSwap();
5898
+ if (!await this.awaitCompactionIdle("prompt")) return;
5899
+ const handOff = await turn.route();
5900
+ if (handOff === false) return;
5901
+ if (handOff) {
5902
+ leaveQueue();
5903
+ try {
5904
+ await handOff();
5905
+ } finally {
5906
+ this.reportIdleIfDrained("turn");
5669
5907
  }
5670
- const knownStaged = await isStillOurs(deps.stagedSkillDirs.get(entry.name), dir);
5671
- if (!knownStaged && !await isStagedSkillMarkerFor(dir, entry.name).catch(() => false))
5672
- continue;
5673
- const result = await unstageSkill(deps.projectDir, skillsDir, entry.name, {
5674
- knownStaged,
5675
- keepAsset: true
5908
+ return;
5909
+ }
5910
+ const startedAt = Date.now();
5911
+ let ok = false;
5912
+ let attempts = 0;
5913
+ let endTurn;
5914
+ try {
5915
+ endTurn = await this.claim("prompt", () => {
5916
+ attempts += 1;
5917
+ return this.deps.ensureDriverStarted();
5676
5918
  });
5677
- if ("reason" in result || !result.removedStaged) continue;
5678
- deps.stagedSkillDirs.delete(result.name);
5679
- removed.push(result.name);
5919
+ ok = endTurn !== null;
5680
5920
  } catch (err) {
5681
- deps.log(
5682
- `[serve] could not remove unlisted skill "${entry.name}" (left in place): ${err instanceof Error ? err.message : String(err)}`
5683
- );
5921
+ const message = errorMessage2(err);
5922
+ this.deps.log(`[serve] driver start failed: ${message}`);
5923
+ this.deps.sendEvent({
5924
+ type: "error",
5925
+ message: `Driver start failed: ${message}`,
5926
+ fatal: true
5927
+ });
5928
+ return;
5929
+ } finally {
5930
+ this.deps.logPhase("driver_start", startedAt, { ok, attempts });
5684
5931
  }
5685
- }
5686
- return removed;
5687
- }
5688
-
5689
- // runner/src/flow-turn-dispatch.ts
5690
- var FLOW_TURN_COMPACTION_TIMEOUT = "Flow turn abandoned: a compaction held the driver";
5691
- async function dispatchFlowTurn(deps, prompt, timeoutMs) {
5692
- for (; ; ) {
5693
- await deps.gate.awaitSwap();
5694
- if (!await deps.awaitCompactionIdle()) throw new Error(FLOW_TURN_COMPACTION_TIMEOUT);
5695
- const endTurn = await deps.gate.beginQueuedTurn();
5696
- if (deps.gate.isSwapping() || deps.isCompacting()) {
5932
+ if (!endTurn) return;
5933
+ try {
5934
+ await turn.dispatch();
5935
+ } catch (err) {
5936
+ const message = errorMessage2(err);
5937
+ this.deps.log(`[serve] prompt failed: ${message}`);
5938
+ this.deps.sendEvent({ type: "error", message: `Prompt failed: ${message}`, fatal: true });
5939
+ } finally {
5697
5940
  endTurn();
5698
- continue;
5699
5941
  }
5942
+ }
5943
+ /** Claim a turn slot on a live driver no swap or compaction occupies. `null`: turn abandoned. */
5944
+ claim(what, prepare) {
5945
+ return beginTurnClearOfCompaction({
5946
+ gate: this.compactions,
5947
+ awaitIdle: () => this.awaitCompactionIdle(what),
5948
+ claimTurn: () => this.swaps.beginTurnOnLiveDriver(prepare)
5949
+ });
5950
+ }
5951
+ /** `false` when a compaction held the driver past its bound; the abandon is already reported. */
5952
+ awaitCompactionIdle(what) {
5953
+ return guardTurnAgainstCompaction({
5954
+ gate: this.compactions,
5955
+ what,
5956
+ log: this.deps.log,
5957
+ sendEvent: this.deps.sendEvent
5958
+ });
5959
+ }
5960
+ /** Count a turn from receipt until its handler settles, so waiting turns count as queued. */
5961
+ async counted(run) {
5962
+ const end = this.pending.begin();
5700
5963
  try {
5701
- await deps.ensureDriverStarted();
5702
- await awaitAgentEnd(deps.getDriver(), prompt, timeoutMs);
5703
- return;
5964
+ return await run();
5704
5965
  } finally {
5705
- endTurn();
5966
+ end();
5706
5967
  }
5707
5968
  }
5969
+ /** Idle unless a compaction suppresses events or more turns are queued, in which case it is owed. */
5970
+ reportIdleIfDrained(endedBy) {
5971
+ if (this.suppressedBy) return;
5972
+ if (this.pending.shouldReportIdle(endedBy)) this.sendIdle();
5973
+ }
5974
+ /** The only `status: idle` emit site. Silent while a compaction suppresses turn events. */
5975
+ sendIdle() {
5976
+ if (this.suppressedBy) return;
5977
+ this.deps.sendEvent({ type: "status", phase: "idle" });
5978
+ }
5979
+ };
5980
+ function errorMessage2(err) {
5981
+ return err instanceof Error ? err.message : String(err);
5982
+ }
5983
+ function replyFailed(err) {
5984
+ return { type: "error", message: `Reply failed: ${errorMessage2(err)}`, fatal: false };
5985
+ }
5986
+ function freshTurnReplyPrompt(answer, question) {
5987
+ const asked = question?.replace(/\s+/g, " ").trim();
5988
+ if (!asked) return answer;
5989
+ return `${answer}
5990
+
5991
+ (The answer above is to your question "${asked}")`;
5708
5992
  }
5709
5993
  function awaitAgentEnd(driver, prompt, timeoutMs) {
5710
5994
  return new Promise((resolve4, reject) => {
@@ -5733,25 +6017,6 @@ function awaitAgentEnd(driver, prompt, timeoutMs) {
5733
6017
  });
5734
6018
  }
5735
6019
 
5736
- // runner/src/reply-routing.ts
5737
- async function routeReply(deps) {
5738
- const current = deps.getDriver();
5739
- if (current.hasPendingQuestion()) {
5740
- if (!current.answerQuestion(deps.answer, deps.question, deps.requestId)) return "fresh-turn";
5741
- deps.onAnswered?.();
5742
- return "answered";
5743
- }
5744
- await deps.awaitSwap();
5745
- return "fresh-turn";
5746
- }
5747
- function freshTurnReplyPrompt(answer, question) {
5748
- const asked = question?.replace(/\s+/g, " ").trim();
5749
- if (!asked) return answer;
5750
- return `${answer}
5751
-
5752
- (The answer above is to your question "${asked}")`;
5753
- }
5754
-
5755
6020
  // runner/src/event-gap-buffer.ts
5756
6021
  var EVENT_GAP_BUFFER_MAX_EVENTS = 1e4;
5757
6022
  var EVENT_GAP_BUFFER_MAX_BYTES = 16 * 1024 * 1024;
@@ -6734,7 +6999,8 @@ function selectClaudeCodeCredentialsSecret(secrets, selection) {
6734
6999
  const legacy = secrets[CLAUDE_CODE_CREDENTIALS_KEY];
6735
7000
  if (legacy) return { key: CLAUDE_CODE_CREDENTIALS_KEY, value: legacy };
6736
7001
  if (!selection?.registry) return void 0;
6737
- if (selection.driver && selection.driver !== "claude-sdk") return void 0;
7002
+ if (selection.driver && !driverCapabilities(selection.driver).claudeCodeCredentials)
7003
+ return void 0;
6738
7004
  const entries = registryEntries(selection.registry).map(
6739
7005
  ([id, entry2]) => [id, { ...entry2, kind: entryKind(entry2) }]
6740
7006
  );
@@ -8476,9 +8742,6 @@ function pickRepointedManager(next, current, disposed) {
8476
8742
  if (current !== null && current === disposed) return null;
8477
8743
  return current;
8478
8744
  }
8479
- function supportsLiveToolUpdates(driverType, managedCodex = false) {
8480
- return driverType !== "claude-sdk" && !(driverType === "codex" && managedCodex);
8481
- }
8482
8745
  function buildResourcesAvailablePayload(manager, mcpManager, driverLive, bakedServerIds) {
8483
8746
  const { mounts, connectors } = splitConnectorsForWire(manager);
8484
8747
  const mcp_servers = (mcpManager?.listServers() ?? []).map((s) => ({
@@ -8487,19 +8750,16 @@ function buildResourcesAvailablePayload(manager, mcpManager, driverLive, bakedSe
8487
8750
  }));
8488
8751
  return { type: "resources_available", mounts, connectors, mcp_servers, sessionRoot: true };
8489
8752
  }
8490
- function isTerminalFlowStatus(status) {
8491
- return status === "complete" || status === "cancelled";
8492
- }
8493
8753
  function hasNonTerminalFlow(flows) {
8494
8754
  for (const f of flows) {
8495
- if (!isTerminalFlowStatus(f.adapter.getExecution(f.handle).status)) return true;
8755
+ if (!f.adapter.turnRule(f.handle).terminal) return true;
8496
8756
  }
8497
8757
  return false;
8498
8758
  }
8499
8759
  function registerActiveFlow(flows, runId, entry) {
8500
8760
  const removed = [];
8501
8761
  for (const [key, existing] of flows) {
8502
- if (isTerminalFlowStatus(existing.adapter.getExecution(existing.handle).status)) {
8762
+ if (existing.adapter.turnRule(existing.handle).terminal) {
8503
8763
  flows.delete(key);
8504
8764
  removed.push(key);
8505
8765
  }
@@ -8511,21 +8771,18 @@ function pickFlowToolTarget(flows, manager) {
8511
8771
  let latest;
8512
8772
  for (const entry of flows) latest = entry;
8513
8773
  if (!latest || !manager) return null;
8514
- if (isTerminalFlowStatus(latest.adapter.getExecution(latest.handle).status)) return null;
8774
+ if (latest.adapter.turnRule(latest.handle).terminal) return null;
8515
8775
  return { manager, connectorId: latest.connectorId };
8516
8776
  }
8517
- function flowRunDrivesTurns(execution) {
8518
- return !(execution.done || execution.status === "failed" || isTerminalFlowStatus(execution.status));
8519
- }
8520
8777
  function pickChatFlowTarget(flows) {
8521
8778
  let latest;
8522
8779
  for (const entry of flows) latest = entry;
8523
8780
  if (!latest) return null;
8524
- if (!flowRunDrivesTurns(latest.adapter.getExecution(latest.handle))) return null;
8781
+ if (!latest.adapter.turnRule(latest.handle).acceptsTurn) return null;
8525
8782
  return latest;
8526
8783
  }
8527
8784
  function flowStartNeedsDriverRestart(state) {
8528
- return state.driverStarted && state.driverType === "claude-sdk" && !state.flowToolsBaked;
8785
+ return state.driverStarted && driverCapabilities(state.driverType).inProcessMcpServers && !state.flowToolsBaked;
8529
8786
  }
8530
8787
  async function setUpFlowConnector(deps) {
8531
8788
  const recreated = await deps.recreateForFlowTools();
@@ -8536,8 +8793,8 @@ async function setUpFlowConnector(deps) {
8536
8793
  async function recreateKeepingLiveFlows(flows, deps) {
8537
8794
  const live = [];
8538
8795
  for (const { runId, connectorId, flowDef, adapter, handle } of flows) {
8796
+ if (adapter.turnRule(handle).terminal) continue;
8539
8797
  const execution = adapter.getExecution(handle);
8540
- if (isTerminalFlowStatus(execution.status)) continue;
8541
8798
  live.push({ runId, connectorId, flowDef, execution: structuredClone(execution) });
8542
8799
  }
8543
8800
  await deps.recreate();
@@ -8558,7 +8815,7 @@ async function recreateKeepingLiveFlows(flows, deps) {
8558
8815
  return reconnected;
8559
8816
  }
8560
8817
  function reconnectedRunNeedsAgentTurn(state) {
8561
- return state.cutDriverWork && state.wantsAgentTurn && !state.busHasTurn && !state.promptQueuedForRun;
8818
+ return state.cutDriverWork && state.restoreKick === "agent_work" && !state.busHasTurn && !state.promptQueuedForRun;
8562
8819
  }
8563
8820
  var DRIVER_ACTIVITY_EVENTS = /* @__PURE__ */ new Set([
8564
8821
  "message_start",
@@ -8961,11 +9218,11 @@ function toActiveFlowTarget(entry) {
8961
9218
  runId: entry.runId,
8962
9219
  connectorId: entry.connectorId,
8963
9220
  hydratedStateChanged: entry.hydratedStateChanged,
8964
- hasRuntimeWork: entry.adapter.hasRuntimeWork(entry.handle)
9221
+ restoreKick: entry.adapter.turnRule(entry.handle).restoreKick
8965
9222
  };
8966
9223
  }
8967
9224
  function restoredRunHasWorkToResume(target) {
8968
- return target.hydratedStateChanged === true || target.hasRuntimeWork === true;
9225
+ return target.hydratedStateChanged === true || target.restoreKick === "runtime_work";
8969
9226
  }
8970
9227
  async function executeConnectorMutate(cmd, deps) {
8971
9228
  let acked = false;
@@ -9154,7 +9411,7 @@ async function applyConnectorMutate(cmd, deps, respond, connectorWaitMs) {
9154
9411
  } else if (cmd.op === "hydrate") {
9155
9412
  const active = deps.flowState().activeFlow;
9156
9413
  const changed = typeof result === "string" && JSON.stringify(payload.state) !== result;
9157
- if (active && (changed || active.hasRuntimeWork === true)) {
9414
+ if (active && (changed || active.restoreKick === "runtime_work")) {
9158
9415
  deps.signalStimulus(active, { kind: "resumed_after_hibernation" });
9159
9416
  }
9160
9417
  }
@@ -9438,7 +9695,7 @@ async function startAgentServer(opts) {
9438
9695
  reason: "retry-401",
9439
9696
  rejection: "usage-limit"
9440
9697
  });
9441
- if (outcome.switched) {
9698
+ if (outcome.switched || movedOffBlockedSeat(ctx.configId, outcome)) {
9442
9699
  return { switched: true, ...outcome.configId ? { configId: outcome.configId } : {} };
9443
9700
  }
9444
9701
  return {
@@ -9573,7 +9830,7 @@ async function startAgentServer(opts) {
9573
9830
  return {
9574
9831
  projectDir: opts.projectDir,
9575
9832
  agentDir,
9576
- driver: managedCodex ? "codex" : opts.driver ?? wireAgentConfig?.driver,
9833
+ driver: managedCodex ? wireAgentConfig?.driver : opts.driver ?? wireAgentConfig?.driver,
9577
9834
  model: managedCodex ? wireAgentConfig?.model : opts.model ?? wireAgentConfig?.model,
9578
9835
  // The control-plane registry layer, normalized to the bare `id → entry`
9579
9836
  // map the session builder layers `providers.yml` under.
@@ -9889,18 +10146,17 @@ async function startAgentServer(opts) {
9889
10146
  }
9890
10147
  const normalizer = new EventNormalizer();
9891
10148
  function forwardNormalizedEvent(event) {
9892
- if (compacting) return;
10149
+ if (turnScheduler.isCompacting) return;
9893
10150
  contextUsage.observeEvent(event);
9894
- const isTransient = event.type === "status" || event.type === "sync_status" || event.type === "resources_available" || event.type === "resource_response" || event.type === "commands_available";
9895
- if (!isTransient) {
10151
+ if (deliveryClassOf(event.type) === "persisted") {
9896
10152
  eventSeq++;
9897
10153
  compactionOrchestrator?.trackMessage(eventSeq);
9898
10154
  }
9899
10155
  if (event.type === "finished") {
9900
10156
  scheduleContextUsageEmit("turn-end");
9901
- if (!compacting && compactionOrchestrator?.shouldCompact()) {
10157
+ if (!turnScheduler.isCompacting && compactionOrchestrator?.shouldCompact()) {
9902
10158
  const compactionTimer = setTimeout(() => {
9903
- if (compacting || !compactionOrchestrator?.shouldCompact()) return;
10159
+ if (turnScheduler.isCompacting || !compactionOrchestrator?.shouldCompact()) return;
9904
10160
  compactionOrchestrator.compact("threshold").catch((err) => {
9905
10161
  log(
9906
10162
  `[serve] auto-compaction failed: ${err instanceof Error ? err.message : String(err)}`
@@ -9923,12 +10179,13 @@ async function startAgentServer(opts) {
9923
10179
  }
9924
10180
  let driverStarted = false;
9925
10181
  let driverStartInFlight = null;
9926
- const driverSwapGate = new DriverSwapGate();
9927
- const compactionGate = new CompactionGate();
9928
- const pendingTurns = new PendingTurns(() => {
9929
- if (!compacting) sendEvent({ type: "status", phase: "idle" });
10182
+ const turnScheduler = new TurnScheduler({
10183
+ ensureDriverStarted,
10184
+ getDriver: () => driver,
10185
+ sendEvent,
10186
+ log,
10187
+ logPhase
9930
10188
  });
9931
- const promptTurns = new PromptTurnQueue();
9932
10189
  let driverRestartChain = Promise.resolve();
9933
10190
  let liveDriverActiveAfterTurn = false;
9934
10191
  let driver = secretsMode === "env" ? agentSession.driver : void 0;
@@ -9971,8 +10228,6 @@ async function startAgentServer(opts) {
9971
10228
  };
9972
10229
  sendEvent(runtimeSessionEvent);
9973
10230
  }
9974
- let compacting = false;
9975
- let suppressedBy = null;
9976
10231
  let compactionOrchestrator = null;
9977
10232
  const compactionConfig = wsConfig.compaction;
9978
10233
  function scheduleContextUsageEmit(trigger) {
@@ -9987,18 +10242,13 @@ async function startAgentServer(opts) {
9987
10242
  }
9988
10243
  function rebuildCompactionOrchestrator() {
9989
10244
  if (!driver) return;
9990
- let gateHold = null;
9991
10245
  compactionOrchestrator = new CompactionOrchestrator({
9992
10246
  driver,
9993
10247
  config: compactionConfig,
9994
10248
  contextWindows,
9995
- onCompactionStart: (trigger) => {
9996
- compacting = true;
9997
- gateHold = compactionGate.begin();
9998
- suppressedBy = gateHold;
9999
- sendEvent({ type: "status", phase: "compacting" });
10000
- log(`[serve] compaction started (trigger=${trigger})`);
10001
- },
10249
+ // Start, end, settled and the driver claim: the scheduler owns the holds,
10250
+ // the event suppression and the `status` phases around a compaction.
10251
+ ...turnScheduler.compactionHooks(),
10002
10252
  onSnapshot: (snapshot) => {
10003
10253
  sendEvent(snapshot);
10004
10254
  contextUsage.onCompaction(snapshot.summary);
@@ -10007,27 +10257,6 @@ async function startAgentServer(opts) {
10007
10257
  onCompactionAttempt: (event) => {
10008
10258
  sendEvent(event);
10009
10259
  },
10010
- onCompactionEnd: () => {
10011
- if (suppressedBy !== gateHold) return;
10012
- compacting = false;
10013
- suppressedBy = null;
10014
- sendEvent({ type: "status", phase: "idle" });
10015
- },
10016
- onCompactionSettled: () => {
10017
- compactionGate.settle(gateHold);
10018
- gateHold = null;
10019
- },
10020
- // A user turn that already holds a slot keeps the driver; the compaction
10021
- // waits for it rather than prompting the driver under it. A platform call
10022
- // waits briefly: a turn paused on a question can hold its slot for hours.
10023
- claimDriver: (trigger) => claimDriverForCompaction({
10024
- gate: compactionGate,
10025
- awaitClaimedTurns: () => driverSwapGate.awaitActiveTurn(),
10026
- isSwapping: () => driverSwapGate.isSwapping(),
10027
- claimTurnSlot: () => driverSwapGate.beginTurn(),
10028
- log,
10029
- timeoutMs: compactionTurnWaitFor(trigger)
10030
- }),
10031
10260
  onLog: (line) => log(`[serve] ${line}`),
10032
10261
  // Phase 3: stamp every CompactionAttemptEvent with the registry's
10033
10262
  // current signature so wake-time tier-1 selection has the input it
@@ -10074,7 +10303,8 @@ async function startAgentServer(opts) {
10074
10303
  const normalized = normalizer.normalize(event);
10075
10304
  const turnEnded = event.type === "agent_end";
10076
10305
  for (const e of normalized) {
10077
- if (turnEnded && !compacting && e.type === "status" && e.phase === "idle" && !pendingTurns.shouldReportIdle()) {
10306
+ if (turnEnded && e.type === "status" && e.phase === "idle") {
10307
+ turnScheduler.reportTurnEnd();
10078
10308
  continue;
10079
10309
  }
10080
10310
  forwardNormalizedEvent(e);
@@ -10114,7 +10344,6 @@ async function startAgentServer(opts) {
10114
10344
  const stimulus = stimulusFromMetas(metas);
10115
10345
  await entry.adapter.drainRuntimeNodes(entry.handle);
10116
10346
  const execution = entry.adapter.getExecution(entry.handle);
10117
- if (!flowRunDrivesTurns(execution)) return;
10118
10347
  if (!entry.adapter.shouldDriveAgentTurn(entry.handle, stimulus)) return;
10119
10348
  await ensureFlowDriverStarted();
10120
10349
  await driveOrchestratorTurn({
@@ -10122,42 +10351,13 @@ async function startAgentServer(opts) {
10122
10351
  execution,
10123
10352
  stimulus,
10124
10353
  emit: sendEvent,
10125
- deliver: (prompt) => awaitFlowAgentEnd(prompt, FLOW_TURN_TIMEOUT_MS)
10354
+ deliver: (prompt) => turnScheduler.flowTurn(prompt, FLOW_TURN_TIMEOUT_MS)
10126
10355
  });
10127
10356
  },
10128
10357
  log: serverLog
10129
10358
  });
10130
10359
  return stimulusBus;
10131
10360
  }
10132
- function awaitFlowAgentEnd(prompt, timeoutMs) {
10133
- return countPendingTurn(
10134
- () => dispatchFlowTurn(
10135
- {
10136
- gate: driverSwapGate,
10137
- awaitCompactionIdle: () => awaitCompactionIdle("flow turn"),
10138
- isCompacting: () => !compactionGate.isIdle(),
10139
- ensureDriverStarted,
10140
- getDriver: () => driver
10141
- },
10142
- prompt,
10143
- timeoutMs
10144
- )
10145
- );
10146
- }
10147
- function activeFlowWantsAgentTurn(runId) {
10148
- const entry = activeFlows.get(runId);
10149
- if (!entry) return false;
10150
- if (!flowRunDrivesTurns(entry.adapter.getExecution(entry.handle))) return false;
10151
- return entry.adapter.shouldDriveAgentTurn(entry.handle, { kind: "resumed_after_hibernation" });
10152
- }
10153
- async function countPendingTurn(run) {
10154
- const end = pendingTurns.begin();
10155
- try {
10156
- return await run();
10157
- } finally {
10158
- end();
10159
- }
10160
- }
10161
10361
  function ensureResourceManagerForFlow() {
10162
10362
  if (!resourceManager) {
10163
10363
  registerBuiltinConnectors();
@@ -10255,7 +10455,7 @@ async function startAgentServer(opts) {
10255
10455
  execution,
10256
10456
  stimulus,
10257
10457
  emit: sendEvent,
10258
- deliver: (prompt) => awaitFlowAgentEnd(prompt, FLOW_TURN_TIMEOUT_MS)
10458
+ deliver: (prompt) => turnScheduler.flowTurn(prompt, FLOW_TURN_TIMEOUT_MS)
10259
10459
  });
10260
10460
  }
10261
10461
  });
@@ -10301,7 +10501,7 @@ async function startAgentServer(opts) {
10301
10501
  execute: (op, args) => adapter.executeOp(handle, op, args),
10302
10502
  mandatoryGates: () => adapter.getMandatoryAgentApprovalNodeIds(handle)
10303
10503
  };
10304
- if (agentSession?.driverType === "claude-sdk") {
10504
+ if (driverCapabilities(agentSession?.driverType).inProcessMcpServers) {
10305
10505
  try {
10306
10506
  const server = await buildSdkConnectorTools(manager, { z: z });
10307
10507
  const flowServer = flowToolsBaked ? null : await buildSdkFlowTools(manager, connectorId, { z: z });
@@ -10323,7 +10523,7 @@ async function startAgentServer(opts) {
10323
10523
  log(`[serve] failed to rebuild MCP tools for flow: ${errMsg}`);
10324
10524
  }
10325
10525
  }
10326
- if (agentSession?.driverType === "codex" && !driver.acceptsReplyAsPrompt) {
10526
+ if (driverCapabilities(agentSession?.driverType).managedSession && !driver.acceptsReplyAsPrompt) {
10327
10527
  removeCodexFlowTools?.();
10328
10528
  removeCodexFlowTools = registerCodexFlowTools(capabilityRegistry, manager, connectorId);
10329
10529
  }
@@ -10588,7 +10788,7 @@ async function startAgentServer(opts) {
10588
10788
  return { needsWireFallbackRecreate, needsWireAgentConfigRecreate };
10589
10789
  }
10590
10790
  async function recreateAgentSession(overrides) {
10591
- return driverSwapGate.runSwap(async () => {
10791
+ return turnScheduler.runSwap(async () => {
10592
10792
  const resolved = typeof overrides === "function" ? overrides() : overrides;
10593
10793
  if (!resolved) return false;
10594
10794
  await recreateAgentSessionInner(resolved);
@@ -10612,10 +10812,10 @@ async function startAgentServer(opts) {
10612
10812
  needsAgentTurn: (run) => {
10613
10813
  const needed = reconnectedRunNeedsAgentTurn({
10614
10814
  cutDriverWork,
10615
- wantsAgentTurn: activeFlowWantsAgentTurn(run.runId),
10815
+ restoreKick: run.restoreKick ?? null,
10616
10816
  busHasTurn: stimulusBus?.hasTurn(run.connectorId) ?? false,
10617
10817
  // Approximate: a waiting reply counts too. A prompt reaches one run only.
10618
- promptQueuedForRun: pendingTurns.size > 0 && pickChatFlowTarget(activeFlows.values())?.runId === run.runId
10818
+ promptQueuedForRun: turnScheduler.pendingTurns > 0 && pickChatFlowTarget(activeFlows.values())?.runId === run.runId
10619
10819
  });
10620
10820
  if (needed) redriven.push(run.runId);
10621
10821
  return needed;
@@ -10818,7 +11018,7 @@ async function startAgentServer(opts) {
10818
11018
  }
10819
11019
  }
10820
11020
  async function rebuildMcpToolsForNewStores(storesRegistered) {
10821
- if (!(storesRegistered > 0 && !driverStarted && agentSession?.driverType === "claude-sdk")) {
11021
+ if (!(storesRegistered > 0 && !driverStarted && driverCapabilities(agentSession?.driverType).inProcessMcpServers)) {
10822
11022
  return;
10823
11023
  }
10824
11024
  try {
@@ -10970,13 +11170,15 @@ async function startAgentServer(opts) {
10970
11170
  }
10971
11171
  try {
10972
11172
  validateCodexInit(cmd, supportedRuntimeFeatures, runtimeIdentity ?? void 0);
10973
- const wantsManaged = cmd.resolvedConfig.agentConfig?.driver === "codex";
11173
+ const wantsManaged = driverCapabilities(
11174
+ cmd.resolvedConfig.agentConfig?.driver
11175
+ ).managedSession;
10974
11176
  if (managedHost && !wantsManaged) throw new Error("AI_PROFILE_INVALID");
10975
11177
  if (managedHost && wantsManaged) {
10976
11178
  if (managedBootstrapClosed || agentSession && !managedInitFingerprint || cmd.secrets && CLAUDE_CODE_CREDENTIALS_KEY in cmd.secrets)
10977
11179
  throw new Error("AI_PROFILE_INVALID");
10978
11180
  const profile2 = cmd.resolvedConfig.agentConfig;
10979
- if (opts.driver && opts.driver !== "codex" || opts.provider && opts.provider !== "openai" || opts.model && opts.model !== profile2.model)
11181
+ if (opts.driver && opts.driver !== profile2.driver || opts.provider && opts.provider !== "openai" || opts.model && opts.model !== profile2.model)
10980
11182
  throw new Error("AI_PROFILE_INVALID");
10981
11183
  const initialCredential = parseCodexCredential(cmd.credentials.ai);
10982
11184
  const binding = bindManagedCodexInit(managedHost, cmd, async (input, signal) => {
@@ -11085,7 +11287,7 @@ async function startAgentServer(opts) {
11085
11287
  settings = { driver: driver2 ?? profile?.driver, model: model ?? profile?.model };
11086
11288
  }
11087
11289
  credentialsSelection = {
11088
- driver: settings.driver ?? "omp",
11290
+ driver: settings.driver ?? DEFAULT_DRIVER_ID,
11089
11291
  registry: wireAgentConfig?.registry,
11090
11292
  modelRef: wireAgentConfig?.modelRef?.trim() || settings.model?.trim() || void 0
11091
11293
  };
@@ -11134,7 +11336,7 @@ async function startAgentServer(opts) {
11134
11336
  let pendingResumeSessionId;
11135
11337
  const cfgResumeSessionId = cfg.resumeSessionId;
11136
11338
  const cfgExpectedSig = cfg.expectedCapabilitySignature;
11137
- const resumeTargetDriver = managedCodex ? "codex" : opts.driver ?? wireAgentConfig?.driver ?? agentSession?.driverType;
11339
+ const resumeTargetDriver = (managedCodex ? void 0 : opts.driver) ?? wireAgentConfig?.driver ?? agentSession?.driverType;
11138
11340
  if (cfgResumeSessionId) {
11139
11341
  const currentSig = computeCapabilitySignature(capabilityRegistry.list());
11140
11342
  if (cfgExpectedSig && cfgExpectedSig !== currentSig) {
@@ -11149,7 +11351,7 @@ async function startAgentServer(opts) {
11149
11351
  resumeSessionId: cfgResumeSessionId,
11150
11352
  reason: "signature_mismatch"
11151
11353
  });
11152
- } else if (resumeProbe === "jsonl_lost" && resumeTargetDriver === "claude-sdk") {
11354
+ } else if (resumeProbe === "jsonl_lost" && driverCapabilities(resumeTargetDriver).resumeNeedsTranscript) {
11153
11355
  log(`[serve] tier-1 resume skipped: no transcript for ${cfgResumeSessionId}`);
11154
11356
  sendEvent({
11155
11357
  type: "resume_failed",
@@ -11246,129 +11448,88 @@ async function startAgentServer(opts) {
11246
11448
  }
11247
11449
  });
11248
11450
  }
11249
- const awaitCompactionIdle = (what) => guardTurnAgainstCompaction({ gate: compactionGate, what, log, sendEvent });
11250
11451
  async function handlePrompt(cmd) {
11251
11452
  if (cmd.messageId) sendEvent({ type: "prompt_ack", messageId: cmd.messageId });
11252
- const place = promptTurns.join({ senderId: cmd.senderId, messageId: cmd.messageId });
11253
- try {
11254
- if (place.queued) {
11255
- driver?.supersedePendingQuestion?.();
11256
- const queuedAt = Date.now();
11257
- await place.ready;
11258
- logPhase("prompt_queue_wait", queuedAt, { dropped: place.dropped });
11259
- if (place.dropped) return;
11260
- }
11261
- await runPromptTurn(cmd, place.leave);
11262
- } finally {
11263
- place.leave();
11264
- }
11265
- }
11266
- async function runPromptTurn(cmd, leaveQueue) {
11267
- const promptReceivedAt = Date.now();
11268
- const sessionIsReady = await awaitSessionReadyOrFail();
11269
- logPhase("prompt_gate_wait", promptReceivedAt, { ready: sessionIsReady });
11270
- if (!sessionIsReady) return;
11271
- if (!secretsProvisioned) {
11272
- log("[serve] prompt received before secrets provisioned \u2014 rejecting");
11273
- sendEvent({
11274
- type: "error",
11275
- message: "Secrets not yet provisioned \u2014 send provision_secrets first",
11276
- fatal: false
11277
- });
11278
- return;
11279
- }
11280
- log(`[serve] prompt: ${cmd.prompt.slice(0, 80)}...`);
11281
- contextUsage.observeUserPrompt(cmd.prompt);
11282
- await driverSwapGate.awaitSwap();
11283
- if (!await awaitCompactionIdle("prompt")) return;
11284
- await restageMaterializedSkills(true);
11285
- try {
11286
- await rebuildForLateMcpDeclarations({
11287
- driverStarted: () => driverStarted,
11288
- unloadedMcpDeclarationIds: () => agentSession.unloadedMcpDeclarationIds(),
11289
- resumeSessionId: () => agentSession.resumeSessionId,
11290
- recreate: (overrides) => recreateAgentSession(overrides),
11291
- log,
11292
- warn: (line) => serverLog.warn(line)
11293
- });
11294
- } catch (err) {
11295
- const errMsg = err instanceof Error ? err.message : String(err);
11296
- serverLog.warn(`[serve] session rebuild for late MCP servers failed: ${errMsg}`);
11297
- sendEvent({ type: "error", message: `Session rebuild failed: ${errMsg}`, fatal: true });
11298
- return;
11299
- }
11300
- const activeFlow = pickChatFlowTarget(activeFlows.values());
11301
- if (activeFlow) {
11302
- leaveQueue();
11303
- sendEvent({ type: "status", phase: "thinking" });
11304
- try {
11305
- const bus = getOrCreateStimulusBus();
11306
- const userStimulus = {
11307
- kind: "user_message",
11308
- text: cmd.prompt,
11309
- senderId: "user"
11310
- };
11311
- await bus.signal(activeFlow.connectorId, {
11312
- promptFragment: renderStimulusPrompt(userStimulus),
11313
- meta: {
11314
- kind: "user_message",
11315
- runId: activeFlow.runId,
11316
- text: cmd.prompt,
11317
- senderId: "user"
11453
+ await turnScheduler.prompt({
11454
+ owner: { senderId: cmd.senderId, messageId: cmd.messageId },
11455
+ // The turn ahead may be paused on a question it can only leave through
11456
+ // this driver's rule for a new prompt; apply it now, not after that turn.
11457
+ onQueued: () => driver?.supersedePendingQuestion?.(),
11458
+ admit: async () => {
11459
+ const promptReceivedAt = Date.now();
11460
+ const sessionIsReady = await awaitSessionReadyOrFail();
11461
+ logPhase("prompt_gate_wait", promptReceivedAt, { ready: sessionIsReady });
11462
+ if (!sessionIsReady) return false;
11463
+ if (!secretsProvisioned) {
11464
+ log("[serve] prompt received before secrets provisioned \u2014 rejecting");
11465
+ sendEvent({
11466
+ type: "error",
11467
+ message: "Secrets not yet provisioned \u2014 send provision_secrets first",
11468
+ fatal: false
11469
+ });
11470
+ return false;
11471
+ }
11472
+ log(`[serve] prompt: ${cmd.prompt.slice(0, 80)}...`);
11473
+ contextUsage.observeUserPrompt(cmd.prompt);
11474
+ return true;
11475
+ },
11476
+ route: async () => {
11477
+ await restageMaterializedSkills(true);
11478
+ try {
11479
+ await rebuildForLateMcpDeclarations({
11480
+ driverStarted: () => driverStarted,
11481
+ unloadedMcpDeclarationIds: () => agentSession.unloadedMcpDeclarationIds(),
11482
+ resumeSessionId: () => agentSession.resumeSessionId,
11483
+ recreate: (overrides) => recreateAgentSession(overrides),
11484
+ log,
11485
+ warn: (line) => serverLog.warn(line)
11486
+ });
11487
+ } catch (err) {
11488
+ const errMsg = err instanceof Error ? err.message : String(err);
11489
+ serverLog.warn(`[serve] session rebuild for late MCP servers failed: ${errMsg}`);
11490
+ sendEvent({ type: "error", message: `Session rebuild failed: ${errMsg}`, fatal: true });
11491
+ return false;
11492
+ }
11493
+ const activeFlow = pickChatFlowTarget(activeFlows.values());
11494
+ if (!activeFlow) return null;
11495
+ return async () => {
11496
+ sendEvent({ type: "status", phase: "thinking" });
11497
+ try {
11498
+ const bus = getOrCreateStimulusBus();
11499
+ const userStimulus = {
11500
+ kind: "user_message",
11501
+ text: cmd.prompt,
11502
+ senderId: "user"
11503
+ };
11504
+ await bus.signal(activeFlow.connectorId, {
11505
+ promptFragment: renderStimulusPrompt(userStimulus),
11506
+ meta: {
11507
+ kind: "user_message",
11508
+ runId: activeFlow.runId,
11509
+ text: cmd.prompt,
11510
+ senderId: "user"
11511
+ }
11512
+ });
11513
+ } catch (err) {
11514
+ const errMsg = err instanceof Error ? err.message : String(err);
11515
+ log(`[serve] flow user-message turn failed: ${errMsg}`);
11516
+ sendEvent({ type: "error", message: `Flow turn failed: ${errMsg}`, fatal: false });
11318
11517
  }
11319
- });
11320
- } catch (err) {
11321
- const errMsg = err instanceof Error ? err.message : String(err);
11322
- log(`[serve] flow user-message turn failed: ${errMsg}`);
11323
- sendEvent({ type: "error", message: `Flow turn failed: ${errMsg}`, fatal: false });
11324
- } finally {
11325
- if (pendingTurns.shouldReportIdle()) sendEvent({ type: "status", phase: "idle" });
11518
+ };
11519
+ },
11520
+ dispatch: async () => {
11521
+ sendEvent({ type: "status", phase: "thinking" });
11522
+ await driver.promptInput({ text: cmd.prompt, attachments: cmd.attachments });
11326
11523
  }
11327
- return;
11328
- }
11329
- const driverStartStartedAt = Date.now();
11330
- let driverStartOk = false;
11331
- let driverStartAttempts = 0;
11332
- let endTurn;
11333
- try {
11334
- endTurn = await beginTurnClearOfCompaction({
11335
- gate: compactionGate,
11336
- awaitIdle: () => awaitCompactionIdle("prompt"),
11337
- claimTurn: () => driverSwapGate.beginTurnOnLiveDriver(() => {
11338
- driverStartAttempts += 1;
11339
- return ensureDriverStarted();
11340
- })
11341
- });
11342
- driverStartOk = endTurn !== null;
11343
- } catch (err) {
11344
- const errMsg = err instanceof Error ? err.message : String(err);
11345
- log(`[serve] driver start failed: ${errMsg}`);
11346
- sendEvent({ type: "error", message: `Driver start failed: ${errMsg}`, fatal: true });
11347
- return;
11348
- } finally {
11349
- logPhase("driver_start", driverStartStartedAt, {
11350
- ok: driverStartOk,
11351
- attempts: driverStartAttempts
11352
- });
11353
- }
11354
- if (!endTurn) return;
11355
- sendEvent({ type: "status", phase: "thinking" });
11356
- try {
11357
- await driver.promptInput({ text: cmd.prompt, attachments: cmd.attachments });
11358
- } catch (err) {
11359
- const errMsg = err instanceof Error ? err.message : String(err);
11360
- log(`[serve] prompt failed: ${errMsg}`);
11361
- sendEvent({ type: "error", message: `Prompt failed: ${errMsg}`, fatal: true });
11362
- } finally {
11363
- endTurn();
11364
- }
11524
+ });
11365
11525
  }
11366
11526
  async function handleReply(cmd) {
11367
- if (!await awaitSessionReadyOrFail()) return;
11368
- log(`[serve] reply: ${cmd.answer.slice(0, 80)}...`);
11369
- const routed = await routeReply({
11370
- getDriver: () => driver,
11371
- awaitSwap: () => driverSwapGate.awaitSwap(),
11527
+ await turnScheduler.reply({
11528
+ admit: async () => {
11529
+ if (!await awaitSessionReadyOrFail()) return false;
11530
+ log(`[serve] reply: ${cmd.answer.slice(0, 80)}...`);
11531
+ return true;
11532
+ },
11372
11533
  answer: cmd.answer,
11373
11534
  question: cmd.question,
11374
11535
  requestId: cmd.requestId,
@@ -11381,49 +11542,10 @@ async function startAgentServer(opts) {
11381
11542
  ...cmd.requestId !== void 0 ? { requestId: cmd.requestId } : {}
11382
11543
  })
11383
11544
  });
11384
- if (routed === "answered") return;
11385
- if (!driver.acceptsReplyAsPrompt) {
11386
- if (!driver.hasPendingQuestion()) {
11387
- log("[serve] reply: driver takes no reply as a prompt; answer not delivered");
11388
- sendEvent({
11389
- type: "error",
11390
- message: "This question is no longer open, so the agent did not get your answer. Send it as a new message.",
11391
- fatal: false
11392
- });
11393
- }
11394
- return;
11395
- }
11396
- let endTurn;
11397
- try {
11398
- endTurn = await beginTurnClearOfCompaction({
11399
- gate: compactionGate,
11400
- awaitIdle: () => awaitCompactionIdle("reply"),
11401
- claimTurn: () => driverSwapGate.beginTurnOnLiveDriver(ensureDriverStarted)
11402
- });
11403
- } catch (err) {
11404
- sendEvent({
11405
- type: "error",
11406
- message: `Reply failed: ${err instanceof Error ? err.message : String(err)}`,
11407
- fatal: false
11408
- });
11409
- return;
11410
- }
11411
- if (!endTurn) return;
11412
- try {
11413
- await driver.prompt(freshTurnReplyPrompt(cmd.answer, cmd.question));
11414
- } catch (err) {
11415
- sendEvent({
11416
- type: "error",
11417
- message: `Reply failed: ${err instanceof Error ? err.message : String(err)}`,
11418
- fatal: false
11419
- });
11420
- } finally {
11421
- endTurn();
11422
- }
11423
11545
  }
11424
11546
  async function handleCancel(cmd) {
11425
11547
  if (cmd.senderId) {
11426
- const dropped = promptTurns.dropQueuedFrom(cmd.senderId);
11548
+ const dropped = turnScheduler.dropQueuedPromptsFrom(cmd.senderId);
11427
11549
  if (dropped.length > 0) {
11428
11550
  log(`[serve] cancel dropped ${dropped.length} queued prompt(s) of the presser`);
11429
11551
  sendEvent({
@@ -11699,10 +11821,10 @@ async function startAgentServer(opts) {
11699
11821
  await handleSessionInit(cmd);
11700
11822
  break;
11701
11823
  case "prompt":
11702
- await countPendingTurn(() => handlePrompt(cmd));
11824
+ await handlePrompt(cmd);
11703
11825
  break;
11704
11826
  case "reply":
11705
- await countPendingTurn(() => handleReply(cmd));
11827
+ await handleReply(cmd);
11706
11828
  break;
11707
11829
  case "cancel":
11708
11830
  await handleCancel(cmd);
@@ -11811,7 +11933,7 @@ async function startAgentServer(opts) {
11811
11933
  }
11812
11934
  function emitAvailability() {
11813
11935
  if (resourceManager || agentSession?.getExternalMcpManager()) {
11814
- const driverLive = agentSession?.driverType !== "claude-sdk";
11936
+ const driverLive = driverCapabilities(agentSession?.driverType).liveToolUpdates;
11815
11937
  const payload = buildResourcesAvailablePayload(
11816
11938
  resourceManager,
11817
11939
  agentSession?.getExternalMcpManager() ?? null,
@@ -12011,6 +12133,6 @@ function touchSession(state) {
12011
12133
  return { ...state, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
12012
12134
  }
12013
12135
 
12014
- export { CLAUDE_CODE_CREDENTIALS_KEY, COMPILE_MANIFEST_FILENAME, CONNECTOR_MUTATE_NOT_READY, CapabilityRegistry, DEFAULT_CAPABILITY_CALL_TIMEOUT_MS, DEFAULT_COALESCE_MS, DEFAULT_FLOW_MUTATE_CONNECTOR_WAIT_MS, MOUNT_LIST_MAX_DEPTH, MOUNT_LIST_MAX_ENTRIES, MOUNT_READ_DEADLINE_FACTOR, MOUNT_READ_MAX_CHUNK_BYTES, MOUNT_STAT_BUDGET_MS, MOUNT_STAT_MAX_ENTRIES, MOUNT_TREE_OP_DEADLINE_FACTOR, MOUNT_WRITE_MAX_CHUNK_BYTES, MarkdownStreamer, PreInitRingSink, ResourceOperationError, SESSION_ROOT_WRITABLE_DIRS, SESSION_ROOT_WRITABLE_FILES, WRITE_RANGE_PARTIAL_TTL_MS, WS_AUTH_TOKEN_ENV, WS_AUTH_TOKEN_FILE_ENV, agentDefinitionExists, bootstrapCapabilityRegistry, bootstrapRunnerLogStore, buildAgentResources, buildClientCapabilityHandler, buildConnectorTokenMediator, buildContextSection, buildEnvironmentSection, buildResourcesAvailablePayload, buildStoreSnapshotReplay, builtinCapabilities, capabilityLogInstance, capabilitySignatureInputs, clearPreInitRingSink, clearSession, compileComposition, computeCapabilitySignature, computeDirectoryTotals, connectorRefreshKind, createAgentSession, createFlowConnectorReadiness, createManagedCodexSubpromptDriver, createSessionStimulusBus, defineCapability, deleteSession, dispatchResourceRequest, dispatchRunnerCapabilityInvocation, driverActiveAfterTurnEnd, emitSystemPromptComposed, ensureClaudeSettingsDisablesConnectors, ensureGitConfigInclude, executeConnectorMutate, extractClaudeAiOauthExpiresAt, flowRunDrivesTurns, flowStartNeedsDriverRestart, getPreInitRingSink, handleMountResourceRequest, handleResourceRequest, handleSessionRootResourceRequest, hasNonTerminalFlow, hostOpResumeStimulus, installPreInitRingSink, isSessionRootPathAllowed, isTerminalFlowStatus, listSessions, loadAgentManifest, loadCompileManifest, loadCompileManifestFromDir, loadManagedCodexHostConfig, loadSession, loadSessionById, mcpAuthSecretKey, newSession, nulTailPaddingLength, pickChatFlowTarget, pickFlowToolTarget, pickRepointedManager, planFlowMutate, prepareCodexTools, reconcilePendingDecisions, reconnectedRunNeedsAgentTurn, recreateKeepingLiveFlows, registerActiveFlow, registerCompositionCapabilities, rejectCapabilityOnApprovalDeny, rejectPendingCapabilityCallsOnDisconnect, resetRunnerLogStore, resolveAgentComposition, resolveAgentMixins, resolveBinding, resolveCapabilityCallTimeoutMs, resolveCapabilityResult, resolveComposition, resolveFlowMutateConnectorWaitMs, resolveMixin, restoredRunHasWorkToResume, runAgentChat, saveSession, setCurrentSession, setUpFlowConnector, startAgentServer, stimulusFromMetas, supportsLiveToolUpdates, takeRunnerWsAuthToken, toActiveFlowTarget, touchSession, validateInputResponse, writeClaudeCodeCredentialsFile, writeRangeChunk, writeRangePartialPath };
12015
- //# sourceMappingURL=chunk-VFAJZE45.js.map
12016
- //# sourceMappingURL=chunk-VFAJZE45.js.map
12136
+ export { CLAUDE_CODE_CREDENTIALS_KEY, COMPILE_MANIFEST_FILENAME, CONNECTOR_MUTATE_NOT_READY, CapabilityRegistry, DEFAULT_CAPABILITY_CALL_TIMEOUT_MS, DEFAULT_COALESCE_MS, DEFAULT_FLOW_MUTATE_CONNECTOR_WAIT_MS, MOUNT_LIST_MAX_DEPTH, MOUNT_LIST_MAX_ENTRIES, MOUNT_READ_DEADLINE_FACTOR, MOUNT_READ_MAX_CHUNK_BYTES, MOUNT_STAT_BUDGET_MS, MOUNT_STAT_MAX_ENTRIES, MOUNT_TREE_OP_DEADLINE_FACTOR, MOUNT_WRITE_MAX_CHUNK_BYTES, MarkdownStreamer, PreInitRingSink, ResourceOperationError, SESSION_ROOT_WRITABLE_DIRS, SESSION_ROOT_WRITABLE_FILES, WRITE_RANGE_PARTIAL_TTL_MS, WS_AUTH_TOKEN_ENV, WS_AUTH_TOKEN_FILE_ENV, agentDefinitionExists, bootstrapCapabilityRegistry, bootstrapRunnerLogStore, buildAgentResources, buildClientCapabilityHandler, buildConnectorTokenMediator, buildContextSection, buildEnvironmentSection, buildResourcesAvailablePayload, buildStoreSnapshotReplay, builtinCapabilities, capabilityLogInstance, capabilitySignatureInputs, clearPreInitRingSink, clearSession, compileComposition, computeCapabilitySignature, computeDirectoryTotals, connectorRefreshKind, createAgentSession, createFlowConnectorReadiness, createManagedCodexSubpromptDriver, createSessionStimulusBus, defineCapability, deleteSession, dispatchResourceRequest, dispatchRunnerCapabilityInvocation, driverActiveAfterTurnEnd, emitSystemPromptComposed, ensureClaudeSettingsDisablesConnectors, ensureGitConfigInclude, executeConnectorMutate, extractClaudeAiOauthExpiresAt, flowStartNeedsDriverRestart, getPreInitRingSink, handleMountResourceRequest, handleResourceRequest, handleSessionRootResourceRequest, hasNonTerminalFlow, hostOpResumeStimulus, installPreInitRingSink, isSessionRootPathAllowed, listSessions, loadAgentManifest, loadCompileManifest, loadCompileManifestFromDir, loadManagedCodexHostConfig, loadSession, loadSessionById, mcpAuthSecretKey, newSession, nulTailPaddingLength, pickChatFlowTarget, pickFlowToolTarget, pickRepointedManager, planFlowMutate, prepareCodexTools, reconcilePendingDecisions, reconnectedRunNeedsAgentTurn, recreateKeepingLiveFlows, registerActiveFlow, registerCompositionCapabilities, rejectCapabilityOnApprovalDeny, rejectPendingCapabilityCallsOnDisconnect, resetRunnerLogStore, resolveAgentComposition, resolveAgentMixins, resolveBinding, resolveCapabilityCallTimeoutMs, resolveCapabilityResult, resolveComposition, resolveFlowMutateConnectorWaitMs, resolveMixin, restoredRunHasWorkToResume, runAgentChat, saveSession, setCurrentSession, setUpFlowConnector, startAgentServer, stimulusFromMetas, takeRunnerWsAuthToken, toActiveFlowTarget, touchSession, validateInputResponse, writeClaudeCodeCredentialsFile, writeRangeChunk, writeRangePartialPath };
12137
+ //# sourceMappingURL=chunk-ZSFTCMZL.js.map
12138
+ //# sourceMappingURL=chunk-ZSFTCMZL.js.map