@skaile/workspaces 6.3.0 → 6.4.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 (268) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/ai-sdk.js +28 -16
  3. package/dist/ai-sdk.js.map +1 -1
  4. package/dist/app-sdk/index.js +1 -1
  5. package/dist/{asset-feeds-3YO2352Y.js → asset-feeds-UJSYGKCX.js} +19 -19
  6. package/dist/{asset-feeds-3YO2352Y.js.map → asset-feeds-UJSYGKCX.js.map} +1 -1
  7. package/dist/asset-manager/catalog-deployer.js +6 -6
  8. package/dist/asset-manager/index.js +11 -11
  9. package/dist/asset-manager/installer.js +7 -7
  10. package/dist/asset-manager/renderers.js +6 -6
  11. package/dist/asset-manager/src/index.d.ts +42 -15
  12. package/dist/asset-manager/src/index.d.ts.map +1 -1
  13. package/dist/asset-manager/src/install-instances.d.ts +2 -0
  14. package/dist/asset-manager/src/install-instances.d.ts.map +1 -1
  15. package/dist/asset-manager/src/installer.d.ts.map +1 -1
  16. package/dist/bridge/harnesses/claude-sdk.js +10 -10
  17. package/dist/bridge/harnesses/codex.js +8 -8
  18. package/dist/bridge/harnesses/continue.js +9 -9
  19. package/dist/bridge/harnesses/echo.js +4 -4
  20. package/dist/bridge/harnesses/gemini.js +5 -5
  21. package/dist/bridge/harnesses/goose.js +9 -9
  22. package/dist/bridge/harnesses/omp.js +6 -6
  23. package/dist/bridge/harnesses/opencode.js +9 -9
  24. package/dist/bridge/harnesses/qwen.js +5 -5
  25. package/dist/bridge/index.js +15 -15
  26. package/dist/bridge/src/ai-sdk.d.ts +5 -0
  27. package/dist/bridge/src/ai-sdk.d.ts.map +1 -1
  28. package/dist/bridge/src/harness-model-check.d.ts +11 -7
  29. package/dist/bridge/src/harness-model-check.d.ts.map +1 -1
  30. package/dist/{chunk-72NT2TJU.js → chunk-24D7IW5O.js} +6 -6
  31. package/dist/{chunk-72NT2TJU.js.map → chunk-24D7IW5O.js.map} +1 -1
  32. package/dist/{chunk-HH2H2ZV7.js → chunk-2WOA3TKK.js} +12 -12
  33. package/dist/chunk-2WOA3TKK.js.map +1 -0
  34. package/dist/{chunk-6WH2WE4B.js → chunk-3ENE6ZOF.js} +4 -4
  35. package/dist/{chunk-6WH2WE4B.js.map → chunk-3ENE6ZOF.js.map} +1 -1
  36. package/dist/{chunk-32FYVSSW.js → chunk-45KLCFQB.js} +5 -5
  37. package/dist/{chunk-32FYVSSW.js.map → chunk-45KLCFQB.js.map} +1 -1
  38. package/dist/{chunk-BPTWWKGF.js → chunk-5NHBLKKU.js} +3 -3
  39. package/dist/{chunk-BPTWWKGF.js.map → chunk-5NHBLKKU.js.map} +1 -1
  40. package/dist/{chunk-7MV3NHPT.js → chunk-5T3DTK7A.js} +41 -23
  41. package/dist/chunk-5T3DTK7A.js.map +1 -0
  42. package/dist/{chunk-XBQTXGY4.js → chunk-6WQGJF65.js} +18 -6
  43. package/dist/chunk-6WQGJF65.js.map +1 -0
  44. package/dist/{chunk-H622YULS.js → chunk-6X3GDPCB.js} +3 -3
  45. package/dist/{chunk-H622YULS.js.map → chunk-6X3GDPCB.js.map} +1 -1
  46. package/dist/{chunk-7QG2WRSN.js → chunk-76WSWPOB.js} +15 -7
  47. package/dist/{chunk-7QG2WRSN.js.map → chunk-76WSWPOB.js.map} +1 -1
  48. package/dist/{chunk-J5JRGBKK.js → chunk-ABVWUM2E.js} +27 -17
  49. package/dist/chunk-ABVWUM2E.js.map +1 -0
  50. package/dist/{chunk-V5BPDB4J.js → chunk-ACBL7X62.js} +3 -3
  51. package/dist/{chunk-V5BPDB4J.js.map → chunk-ACBL7X62.js.map} +1 -1
  52. package/dist/{chunk-VUJXZDCY.js → chunk-BOS7P2Y4.js} +3 -3
  53. package/dist/{chunk-VUJXZDCY.js.map → chunk-BOS7P2Y4.js.map} +1 -1
  54. package/dist/{chunk-PNUM7GUY.js → chunk-BUU2DLPC.js} +3 -3
  55. package/dist/{chunk-PNUM7GUY.js.map → chunk-BUU2DLPC.js.map} +1 -1
  56. package/dist/{chunk-PL52CI4U.js → chunk-BX7PSWJA.js} +4 -4
  57. package/dist/{chunk-PL52CI4U.js.map → chunk-BX7PSWJA.js.map} +1 -1
  58. package/dist/{chunk-URCJIV7T.js → chunk-C7TQARGC.js} +3 -3
  59. package/dist/{chunk-URCJIV7T.js.map → chunk-C7TQARGC.js.map} +1 -1
  60. package/dist/{chunk-E5NQRHEB.js → chunk-CK3CJSRY.js} +3 -3
  61. package/dist/{chunk-E5NQRHEB.js.map → chunk-CK3CJSRY.js.map} +1 -1
  62. package/dist/{chunk-4QRJBY2P.js → chunk-DCJ4BION.js} +2 -2
  63. package/dist/{chunk-4QRJBY2P.js.map → chunk-DCJ4BION.js.map} +1 -1
  64. package/dist/{chunk-HGOJEVHY.js → chunk-DF5WJC3Q.js} +4 -4
  65. package/dist/{chunk-HGOJEVHY.js.map → chunk-DF5WJC3Q.js.map} +1 -1
  66. package/dist/{chunk-QK7AG5AV.js → chunk-E237FGGX.js} +4 -4
  67. package/dist/{chunk-QK7AG5AV.js.map → chunk-E237FGGX.js.map} +1 -1
  68. package/dist/{chunk-ZEH4WFEG.js → chunk-EXRQKOY6.js} +11 -11
  69. package/dist/{chunk-ZEH4WFEG.js.map → chunk-EXRQKOY6.js.map} +1 -1
  70. package/dist/{chunk-QFFMFZIR.js → chunk-G63KOHFS.js} +3 -3
  71. package/dist/{chunk-QFFMFZIR.js.map → chunk-G63KOHFS.js.map} +1 -1
  72. package/dist/{chunk-Y3I4KGLX.js → chunk-GBXQFJ4G.js} +8 -8
  73. package/dist/{chunk-Y3I4KGLX.js.map → chunk-GBXQFJ4G.js.map} +1 -1
  74. package/dist/{chunk-6GWSDPBP.js → chunk-GTMANMBZ.js} +3 -3
  75. package/dist/{chunk-6GWSDPBP.js.map → chunk-GTMANMBZ.js.map} +1 -1
  76. package/dist/{chunk-YW4ASEUP.js → chunk-HSF665NV.js} +5 -5
  77. package/dist/{chunk-YW4ASEUP.js.map → chunk-HSF665NV.js.map} +1 -1
  78. package/dist/{chunk-LEJQAX7G.js → chunk-HXZ3FQQN.js} +14 -14
  79. package/dist/{chunk-LEJQAX7G.js.map → chunk-HXZ3FQQN.js.map} +1 -1
  80. package/dist/{chunk-ROVZCGTN.js → chunk-IYZAJ2J2.js} +16 -13
  81. package/dist/chunk-IYZAJ2J2.js.map +1 -0
  82. package/dist/{chunk-PHAMYCZH.js → chunk-IZV3HDVG.js} +5 -5
  83. package/dist/{chunk-PHAMYCZH.js.map → chunk-IZV3HDVG.js.map} +1 -1
  84. package/dist/{chunk-ELSGFI3I.js → chunk-JSWMRYLM.js} +3 -3
  85. package/dist/{chunk-ELSGFI3I.js.map → chunk-JSWMRYLM.js.map} +1 -1
  86. package/dist/{chunk-TSTRH4SM.js → chunk-L45IHRDH.js} +3 -3
  87. package/dist/{chunk-TSTRH4SM.js.map → chunk-L45IHRDH.js.map} +1 -1
  88. package/dist/{chunk-Q7CU5GQO.js → chunk-L4OYPFLB.js} +3 -3
  89. package/dist/{chunk-Q7CU5GQO.js.map → chunk-L4OYPFLB.js.map} +1 -1
  90. package/dist/{chunk-7TSNR5H5.js → chunk-O7KAQQAI.js} +4 -4
  91. package/dist/{chunk-7TSNR5H5.js.map → chunk-O7KAQQAI.js.map} +1 -1
  92. package/dist/{chunk-EQNIBBB6.js → chunk-P7EL7QRD.js} +14 -14
  93. package/dist/{chunk-EQNIBBB6.js.map → chunk-P7EL7QRD.js.map} +1 -1
  94. package/dist/{chunk-2TFKXSB5.js → chunk-PTBX55QF.js} +4 -4
  95. package/dist/{chunk-2TFKXSB5.js.map → chunk-PTBX55QF.js.map} +1 -1
  96. package/dist/{chunk-DU26AVK2.js → chunk-Q4XWCVFA.js} +7 -7
  97. package/dist/{chunk-DU26AVK2.js.map → chunk-Q4XWCVFA.js.map} +1 -1
  98. package/dist/{chunk-S6KZHPR4.js → chunk-QJQQNBVT.js} +2 -2
  99. package/dist/{chunk-S6KZHPR4.js.map → chunk-QJQQNBVT.js.map} +1 -1
  100. package/dist/{chunk-HDVLBEEX.js → chunk-QSIFEHSN.js} +3 -3
  101. package/dist/{chunk-HDVLBEEX.js.map → chunk-QSIFEHSN.js.map} +1 -1
  102. package/dist/{chunk-VUWRGMT6.js → chunk-RKI35EAN.js} +3 -3
  103. package/dist/{chunk-VUWRGMT6.js.map → chunk-RKI35EAN.js.map} +1 -1
  104. package/dist/{chunk-7RH6E332.js → chunk-SISUT7VR.js} +7 -7
  105. package/dist/{chunk-7RH6E332.js.map → chunk-SISUT7VR.js.map} +1 -1
  106. package/dist/{chunk-TGV2EWGL.js → chunk-TDKZL5ZC.js} +3 -3
  107. package/dist/{chunk-TGV2EWGL.js.map → chunk-TDKZL5ZC.js.map} +1 -1
  108. package/dist/{chunk-KIPCBIFJ.js → chunk-TF5CWJQZ.js} +5 -5
  109. package/dist/{chunk-KIPCBIFJ.js.map → chunk-TF5CWJQZ.js.map} +1 -1
  110. package/dist/{chunk-KGHYXXXV.js → chunk-TGB4E5Z6.js} +188 -52
  111. package/dist/chunk-TGB4E5Z6.js.map +1 -0
  112. package/dist/{chunk-AMJ6KEV7.js → chunk-TPQKFX6X.js} +4 -4
  113. package/dist/{chunk-AMJ6KEV7.js.map → chunk-TPQKFX6X.js.map} +1 -1
  114. package/dist/{chunk-KPKGWXUJ.js → chunk-U6S5CXKB.js} +3 -3
  115. package/dist/{chunk-KPKGWXUJ.js.map → chunk-U6S5CXKB.js.map} +1 -1
  116. package/dist/{chunk-JGH7NB47.js → chunk-UAOHR6AL.js} +4 -4
  117. package/dist/{chunk-JGH7NB47.js.map → chunk-UAOHR6AL.js.map} +1 -1
  118. package/dist/{chunk-COUJOTAJ.js → chunk-UBLZALKP.js} +3 -3
  119. package/dist/{chunk-COUJOTAJ.js.map → chunk-UBLZALKP.js.map} +1 -1
  120. package/dist/{chunk-BIRW3HOG.js → chunk-UUKCPPTQ.js} +3 -3
  121. package/dist/{chunk-BIRW3HOG.js.map → chunk-UUKCPPTQ.js.map} +1 -1
  122. package/dist/{chunk-TO5SSMRI.js → chunk-UVTN4O7N.js} +42 -17
  123. package/dist/chunk-UVTN4O7N.js.map +1 -0
  124. package/dist/{chunk-LD6O6QKU.js → chunk-V4OXHEWY.js} +3 -3
  125. package/dist/{chunk-LD6O6QKU.js.map → chunk-V4OXHEWY.js.map} +1 -1
  126. package/dist/{chunk-QTLUZUF2.js → chunk-VSCLUPGZ.js} +28 -26
  127. package/dist/chunk-VSCLUPGZ.js.map +1 -0
  128. package/dist/{chunk-JMIKGAUA.js → chunk-VV2DJT4I.js} +15 -5
  129. package/dist/chunk-VV2DJT4I.js.map +1 -0
  130. package/dist/{chunk-JCPD4QRM.js → chunk-VZU67OKK.js} +2 -2
  131. package/dist/{chunk-JCPD4QRM.js.map → chunk-VZU67OKK.js.map} +1 -1
  132. package/dist/{chunk-XEVFSDIF.js → chunk-WENTJ5KP.js} +4 -4
  133. package/dist/{chunk-XEVFSDIF.js.map → chunk-WENTJ5KP.js.map} +1 -1
  134. package/dist/{chunk-MD3FUZS3.js → chunk-YFGXYQW4.js} +3 -3
  135. package/dist/{chunk-MD3FUZS3.js.map → chunk-YFGXYQW4.js.map} +1 -1
  136. package/dist/{chunk-MYXFR27L.js → chunk-YJNE4V2M.js} +16 -9
  137. package/dist/chunk-YJNE4V2M.js.map +1 -0
  138. package/dist/{chunk-RFQONXET.js → chunk-ZWCGJYR7.js} +3 -3
  139. package/dist/{chunk-RFQONXET.js.map → chunk-ZWCGJYR7.js.map} +1 -1
  140. package/dist/{claude-code-EUKDGOOO.js → claude-code-UBT4ZME7.js} +4 -4
  141. package/dist/{claude-code-EUKDGOOO.js.map → claude-code-UBT4ZME7.js.map} +1 -1
  142. package/dist/cli/index.js +103 -89
  143. package/dist/cli/index.js.map +1 -1
  144. package/dist/cli/src/commands/library-cmd.d.ts.map +1 -1
  145. package/dist/cli/src/commands/library-index.d.ts.map +1 -1
  146. package/dist/cli/src/commands/plugin-registry-cmd.d.ts.map +1 -1
  147. package/dist/cli/src/commands/preset-cmd.d.ts.map +1 -1
  148. package/dist/cli/src/helpers.d.ts +10 -2
  149. package/dist/cli/src/helpers.d.ts.map +1 -1
  150. package/dist/cli/src/plugin-store/index.d.ts +1 -1
  151. package/dist/cli/src/plugin-store/index.d.ts.map +1 -1
  152. package/dist/cli/src/plugin-store/reconcile.d.ts.map +1 -1
  153. package/dist/cli/src/plugin-store/spec.d.ts +19 -14
  154. package/dist/cli/src/plugin-store/spec.d.ts.map +1 -1
  155. package/dist/cli/src/setup.d.ts.map +1 -1
  156. package/dist/connectors/config.js +4 -4
  157. package/dist/connectors/index.js +19 -19
  158. package/dist/{connectors-XYPE2DCQ.js → connectors-4WH32NK2.js} +22 -22
  159. package/dist/{connectors-XYPE2DCQ.js.map → connectors-4WH32NK2.js.map} +1 -1
  160. package/dist/connectors-shared/index.js +2 -2
  161. package/dist/core/index.js +5 -5
  162. package/dist/core/manifest.js +1 -1
  163. package/dist/core/publish-manifest.js +2 -2
  164. package/dist/core/runtime-assets.js +4 -4
  165. package/dist/core/src/index.d.ts +1 -1
  166. package/dist/core/src/index.d.ts.map +1 -1
  167. package/dist/core/src/lock.d.ts.map +1 -1
  168. package/dist/core/src/manifest.d.ts +9 -3
  169. package/dist/core/src/manifest.d.ts.map +1 -1
  170. package/dist/core/src/workspace-config.d.ts +15 -0
  171. package/dist/core/src/workspace-config.d.ts.map +1 -1
  172. package/dist/core/src/workspace-yaml-editor.d.ts.map +1 -1
  173. package/dist/core/workspace-config.js +3 -3
  174. package/dist/deploy/index.js +5 -5
  175. package/dist/discovery/index.js +3 -3
  176. package/dist/{ensure-sources-IN3VKHUA.js → ensure-sources-UC2RBYMP.js} +24 -24
  177. package/dist/{ensure-sources-IN3VKHUA.js.map → ensure-sources-UC2RBYMP.js.map} +1 -1
  178. package/dist/factory-assets/connectors/box.js +19 -19
  179. package/dist/factory-assets/connectors/deploy.js +19 -19
  180. package/dist/factory-assets/connectors/devserver.js +19 -19
  181. package/dist/factory-assets/connectors/flow/adapter.js +19 -19
  182. package/dist/factory-assets/connectors/flow/engine.js +6 -6
  183. package/dist/factory-assets/connectors/flow/run-flow.js +21 -21
  184. package/dist/factory-assets/connectors/flow.js +19 -19
  185. package/dist/factory-assets/connectors/git.js +19 -19
  186. package/dist/factory-assets/connectors/gmail.js +19 -19
  187. package/dist/factory-assets/connectors/googledrive.js +19 -19
  188. package/dist/factory-assets/connectors/local.js +19 -19
  189. package/dist/factory-assets/connectors/mattermost.js +19 -19
  190. package/dist/factory-assets/connectors/memory.js +19 -19
  191. package/dist/factory-assets/connectors/minio.js +19 -19
  192. package/dist/factory-assets/connectors/postgres.js +19 -19
  193. package/dist/factory-assets/connectors/s3.js +19 -19
  194. package/dist/factory-assets/connectors/sharepoint.js +19 -19
  195. package/dist/factory-assets/connectors/sqlite.js +19 -19
  196. package/dist/factory-assets/connectors/static-server.js +19 -19
  197. package/dist/factory-assets/connectors/tunnel.js +19 -19
  198. package/dist/factory-assets/connectors/webdav.js +19 -19
  199. package/dist/factory-assets/connectors/xstate-store.js +19 -19
  200. package/dist/factory-assets/connectors/xstate.js +19 -19
  201. package/dist/{flows-QAO63TDV.js → flows-LYTKNGKE.js} +10 -10
  202. package/dist/{flows-QAO63TDV.js.map → flows-LYTKNGKE.js.map} +1 -1
  203. package/dist/{helpers-HDVGCF3M.js → helpers-SSSKT5XK.js} +24 -24
  204. package/dist/{helpers-HDVGCF3M.js.map → helpers-SSSKT5XK.js.map} +1 -1
  205. package/dist/library/index.js +8 -8
  206. package/dist/library/install.js +3 -3
  207. package/dist/{library-publish-62OWCNB5.js → library-publish-GQMNTAKK.js} +48 -48
  208. package/dist/{library-publish-62OWCNB5.js.map → library-publish-GQMNTAKK.js.map} +1 -1
  209. package/dist/model-endpoint/index.js +8 -8
  210. package/dist/{open-library-TZ7OSDEJ.js → open-library-XGUCUJYE.js} +27 -27
  211. package/dist/{open-library-TZ7OSDEJ.js.map → open-library-XGUCUJYE.js.map} +1 -1
  212. package/dist/{open-registry-RLZKTJG3.js → open-registry-DIFEPDF6.js} +12 -12
  213. package/dist/{open-registry-RLZKTJG3.js.map → open-registry-DIFEPDF6.js.map} +1 -1
  214. package/dist/{paths-D6LBQETQ.js → paths-G4GFJLE2.js} +8 -8
  215. package/dist/{paths-D6LBQETQ.js.map → paths-G4GFJLE2.js.map} +1 -1
  216. package/dist/{plugin-store-A5J4TLCH.js → plugin-store-BBLDFD3L.js} +8 -8
  217. package/dist/{plugin-store-A5J4TLCH.js.map → plugin-store-BBLDFD3L.js.map} +1 -1
  218. package/dist/{project-4AVRES3M.js → project-LB745WVZ.js} +44 -44
  219. package/dist/{project-4AVRES3M.js.map → project-LB745WVZ.js.map} +1 -1
  220. package/dist/resolver/index.js +6 -6
  221. package/dist/runner/index.js +34 -34
  222. package/dist/runner/src/capability-roundtrip.d.ts +4 -1
  223. package/dist/runner/src/capability-roundtrip.d.ts.map +1 -1
  224. package/dist/sdk/asset-manager.js +11 -11
  225. package/dist/sdk/bridge.js +15 -15
  226. package/dist/sdk/core.js +5 -5
  227. package/dist/sdk/flow.js +6 -6
  228. package/dist/sdk/index.js +34 -34
  229. package/dist/sdk/resolver.js +6 -6
  230. package/dist/sdk/runner.js +34 -34
  231. package/dist/sdk/session.js +2 -2
  232. package/dist/sdk/transport/ws/server.js +6 -6
  233. package/dist/sdk/transport/ws.js +6 -6
  234. package/dist/sdk/transport.js +6 -6
  235. package/dist/sdk/types.js +1 -1
  236. package/dist/{serve-preflight-H2AYMGJ6.js → serve-preflight-NPWI4MV6.js} +25 -25
  237. package/dist/{serve-preflight-H2AYMGJ6.js.map → serve-preflight-NPWI4MV6.js.map} +1 -1
  238. package/dist/session/index.js +2 -2
  239. package/dist/{setup-NSPQD4V4.js → setup-X6ZYH2MW.js} +30 -27
  240. package/dist/setup-X6ZYH2MW.js.map +1 -0
  241. package/dist/{skills-B533SRVZ.js → skills-62PXDBPJ.js} +11 -11
  242. package/dist/{skills-B533SRVZ.js.map → skills-62PXDBPJ.js.map} +1 -1
  243. package/dist/{source-VTECABUO.js → source-LAYCTRHP.js} +44 -44
  244. package/dist/{source-VTECABUO.js.map → source-LAYCTRHP.js.map} +1 -1
  245. package/dist/{source-manifest-publish-YORI42MN.js → source-manifest-publish-3S6PJ25W.js} +46 -46
  246. package/dist/{source-manifest-publish-YORI42MN.js.map → source-manifest-publish-3S6PJ25W.js.map} +1 -1
  247. package/dist/transport/index.js +6 -6
  248. package/dist/transport/ws/server.js +6 -6
  249. package/dist/transport/ws.js +6 -6
  250. package/dist/tui/index.js +34 -34
  251. package/dist/types/index.js +1 -1
  252. package/dist/types/src/capabilities.d.ts +9 -1
  253. package/dist/types/src/capabilities.d.ts.map +1 -1
  254. package/dist/workspace-plugin/adapters/mcp.js +26 -26
  255. package/dist/workspace-plugin/adapters/omp.js +14 -14
  256. package/dist/workspace-plugin/index.js +1 -1
  257. package/package.json +1 -1
  258. package/dist/chunk-7MV3NHPT.js.map +0 -1
  259. package/dist/chunk-HH2H2ZV7.js.map +0 -1
  260. package/dist/chunk-J5JRGBKK.js.map +0 -1
  261. package/dist/chunk-JMIKGAUA.js.map +0 -1
  262. package/dist/chunk-KGHYXXXV.js.map +0 -1
  263. package/dist/chunk-MYXFR27L.js.map +0 -1
  264. package/dist/chunk-QTLUZUF2.js.map +0 -1
  265. package/dist/chunk-ROVZCGTN.js.map +0 -1
  266. package/dist/chunk-TO5SSMRI.js.map +0 -1
  267. package/dist/chunk-XBQTXGY4.js.map +0 -1
  268. package/dist/setup-NSPQD4V4.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../bridge/src/harnesses/omp-models-yml.ts"],"names":[],"mappings":"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","file":"chunk-JGH7NB47.js","sourcesContent":["/**\n * The omp adapter: an AI provider registry serialized into omp's own\n * configuration files, inside a session-private agent directory.\n *\n * omp already implements the target schema — `~/.omp/agent/models.yml` is a\n * `providers:` map with `baseUrl`, `api`, `apiKey`, `headers`, `authHeader`,\n * `auth`, `discovery` and `models[]` (`omp://models.md`) — so this module is a\n * *serializer*, not a translator. Our `surface` enum is omp's `api` enum\n * verbatim (§1.2); {@link OMP_API_VALUES} encodes that assertion so a future\n * surface that omp does not know is reported instead of silently written into\n * a file omp will then reject wholesale.\n *\n * Two files are produced:\n *\n * - `models.yml` — one entry per enabled `llm`/`embed` registry entry. `apiKey`\n * holds the **name** of the per-entry environment variable\n * (`SKAILE_AIP_<ID>_KEY`, {@link providerCredentialEnvName}), never a value:\n * omp resolves a `models.yml` `apiKey` as an env-var name first and only\n * falls back to treating it as a literal token. The values travel in the\n * child environment.\n * - `config.yml` — omp's settings file, carrying `modelRoles` rendered from the\n * `skaile.yaml` `roles` table. `models.yml`'s root accepts only `providers:`;\n * unknown root keys fail its schema, so roles cannot live there.\n *\n * `PI_CODING_AGENT_DIR` relocates omp's whole agent base — `config.yml`,\n * `models.yml` **and** the auth store `agent.db` (`omp://settings.md`). Pointing\n * it at a session-private directory is what makes N providers per session\n * possible without ever writing the user's `~/.omp/agent`. The cost is that a\n * standalone developer session would also lose the OAuth logins stored in that\n * auth store, so {@link materializeOmpAgentDir} seeds the session directory\n * with symlinks to everything in the user's agent directory *except* the two\n * files this module owns. Verified against omp 18.2.6: a ChatGPT/Codex OAuth\n * account stored in `~/.omp/agent/agent.db` still resolves (`omp token\n * openai-codex`) from a relocated directory that only symlinks `agent.db*`.\n * Under the platform the user directory does not exist, the seed is a no-op,\n * and the session starts empty — which is the correct behaviour there.\n *\n * @see PROVIDER-CONFIG-PLAN.md §2.3 (omp row), §5, §9\n * @see omp://models.md, omp://settings.md\n */\n\nimport { createHash } from \"node:crypto\";\nimport type { Stats } from \"node:fs\";\nimport fs from \"node:fs/promises\";\nimport os from \"node:os\";\nimport net from \"node:net\";\nimport path from \"node:path\";\nimport {\n type AIProviderLlmEntry,\n type AIProviderRegistry,\n AI_PROVIDER_SURFACES,\n isEntryEnabled,\n type MergedAIProviderRegistry,\n normalizeEntryBaseUrl,\n parseModelRef,\n registryEntries,\n resolveEntryBaseUrl,\n} from \"@skaile/workspaces/core/provider-registry\";\nimport { parse, stringify } from \"yaml\";\nimport type { ModelEntry } from \"../models.js\";\nimport {\n entryCredentialValue,\n providerCredentialEnvName,\n type RegistryCredentialEnvOptions,\n unresolvedProviderCredentials,\n} from \"../provider-materialize/credential-env.js\";\nimport { entrySurface } from \"../provider-materialize/env-harnesses.js\";\nimport type { AgentConfig, OmpHarnessOptions } from \"../types.js\";\n\n/**\n * omp's allowed provider/model `api` values (`omp://models.md`, \"Allowed\n * provider/model `api` values\").\n *\n * Every {@link AI_PROVIDER_SURFACES} member is in this list — that equality is\n * the premise the whole adapter rests on (§1.2). `google-gemini-cli` is omp's\n * one extra value, which the registry has no surface for.\n */\nexport const OMP_API_VALUES: readonly string[] = [...AI_PROVIDER_SURFACES, \"google-gemini-cli\"];\n\n/**\n * `discovery.type` values omp accepts. The registry's own enum is wider on one\n * side (`anthropic-models-list`, `google-models-list`, `none`) and narrower on\n * the other, so a discovery block is emitted only when omp can act on it.\n */\nconst OMP_DISCOVERY_TYPES: readonly string[] = [\n \"ollama\",\n \"llama.cpp\",\n \"lm-studio\",\n \"openai-models-list\",\n \"proxy\",\n \"litellm\",\n];\n\n/** One `providers.<id>` block of `models.yml`. */\ninterface OmpProviderBlock {\n baseUrl?: string;\n api?: string;\n /** The *name* of the env var holding the credential — never a value. */\n apiKey?: string;\n auth?: \"apiKey\" | \"none\" | \"oauth\";\n authHeader?: boolean;\n headers?: Record<string, string>;\n discovery?: { type: string };\n models?: OmpModelBlock[];\n}\n\n/** One entry of a provider's `models[]`. */\ninterface OmpModelBlock {\n id: string;\n name?: string;\n input?: string[];\n contextWindow?: number;\n supportsTools?: boolean;\n}\n\n/** The two files this module owns inside the agent directory. */\nexport const OMP_MODELS_FILENAME = \"models.yml\";\n\n/** omp's settings file; `modelRoles` lives here, not in `models.yml`. */\nexport const OMP_CONFIG_FILENAME = \"config.yml\";\n\n/**\n * Names {@link materializeOmpAgentDir} never links in from the user's agent\n * directory: the two files it writes itself, their alternate spellings, and\n * the legacy `settings.json` omp would migrate into `config.yml`.\n */\nconst OWNED_AGENT_DIR_FILES: Record<string, true> = {\n \"models.yml\": true,\n \"models.yaml\": true,\n \"models.json\": true,\n \"config.yml\": true,\n \"config.yaml\": true,\n \"config.yml.lock\": true,\n \"config.yaml.lock\": true,\n \"settings.json\": true,\n};\n\n/**\n * omp's own runtime state inside its home. An agent directory that an older release used as\n * omp's home still holds these; they are never agent files, so the identity link skips them\n * and the user's own login (`agent.db`) linked in from their agent directory stays in place.\n */\nconst OMP_RUNTIME_ENTRIES: Record<string, true> = {\n \"agent.db\": true,\n \"agent.db-shm\": true,\n \"agent.db-wal\": true,\n \"models.db\": true,\n \"models.db-shm\": true,\n \"models.db-wal\": true,\n \"skill-descriptions.db\": true,\n \"skill-descriptions.db-shm\": true,\n \"skill-descriptions.db-wal\": true,\n \"custom-session-files\": true,\n intercom: true,\n sessions: true,\n};\n\n/**\n * The only entries of the user's own omp agent directory a session's private home links in:\n * omp's credential store, so the login the developer already has keeps working. Sessions,\n * history, `mcp.json`, extensions and blobs stay out: a run must not write into the user's\n * session store or load their MCP servers and extensions. `config.yml` is copied, not linked.\n */\nconst OMP_LOGIN_FILES = [\"agent.db\", \"agent.db-shm\", \"agent.db-wal\"] as const;\n\n/** Options for {@link renderOmpRegistry}. */\nexport interface RenderOmpRegistryOptions extends RegistryCredentialEnvOptions {\n /** `skaile.yaml` `roles`: role name → model selector. Rendered as `modelRoles`. */\n roles?: Record<string, string>;\n /**\n * The session's own selector. When `roles` names no `default`, the resolved\n * model becomes `modelRoles.default` so `@default` (and omp's `*` alias)\n * resolve to the entry the session actually runs on.\n */\n modelRef?: string;\n /**\n * Settings to render underneath `modelRoles` — the user's own `config.yml`\n * when a standalone session seeds one. `modelRoles` is replaced wholesale,\n * not deep-merged: a seeded role pointing at a provider this session does\n * not define would otherwise survive as a dangling selector.\n */\n baseSettings?: Record<string, unknown>;\n}\n\n/** What {@link renderOmpRegistry} produced. */\nexport interface OmpRegistryRender {\n /** `models.yml` contents. */\n modelsYml: string;\n /** `config.yml` contents. */\n configYml: string;\n /**\n * `SKAILE_AIP_<ID>_KEY` → secret, for the child environment. The only place\n * a credential value appears; both rendered files hold names alone.\n */\n credentialEnv: Record<string, string>;\n /** Entry ids written to `models.yml`, in registry order. */\n providerIds: string[];\n /** `modelRoles` as written, role name → `<providerId>/<providerModelId>`. */\n modelRoles: Record<string, string>;\n /** Entries and roles that could not be rendered, and why. */\n warnings: string[];\n}\n\n/** The provider-side model ids an entry declares, keyed by logical id. */\nfunction entryModelIds(entry: AIProviderLlmEntry): Array<[string, string]> {\n const logical = new Set<string>([\n ...Object.keys(entry.model_map ?? {}),\n ...Object.keys(entry.capabilities ?? {}),\n ]);\n const seen = new Set<string>();\n const out: Array<[string, string]> = [];\n for (const id of logical) {\n const providerSide = entry.model_map?.[id] ?? id;\n if (seen.has(providerSide)) continue;\n seen.add(providerSide);\n out.push([id, providerSide]);\n }\n return out;\n}\n\n/**\n * Serialize one entry into an omp provider block, or report why it cannot be.\n *\n * omp requires `baseUrl` on any provider that declares `models[]`, and\n * provider-level `api` whenever `discovery` is set (except `discovery.type:\n * proxy`, where the wire is auto-detected per model). A file that violates its\n * schema is rejected *wholesale* — the registry keeps operating on built-in\n * models and the error only surfaces through `ModelRegistry.getError()` — so an\n * entry that cannot satisfy those rules is dropped here, loudly, rather than\n * taking every other provider down with it.\n */\nfunction renderProvider(\n id: string,\n entry: AIProviderLlmEntry,\n warnings: string[],\n): OmpProviderBlock | undefined {\n // The Anthropic SDK inside omp appends `/v1/messages`: a stored `…/v1` must\n // not reach the file as written. Not `entryBaseUrl`: omp gets no preset\n // literal, so an entry without an endpoint of its own is reported below.\n const baseUrl = normalizeEntryBaseUrl(resolveEntryBaseUrl(entry), entrySurface(entry));\n if (!baseUrl) {\n warnings.push(\n `provider '${id}' has no resolvable base_url (missing transport params?) — omitted from models.yml`,\n );\n return undefined;\n }\n\n // Only the entry's own surface becomes `api`; an inherited one stays omp's\n // to infer, as before. The preset's surface above only picks the URL rule.\n const surface = entry.surface;\n if (surface && !OMP_API_VALUES.includes(surface)) {\n warnings.push(\n `provider '${id}' has surface '${surface}', which omp's api enum does not accept`,\n );\n return undefined;\n }\n\n const block: OmpProviderBlock = { baseUrl };\n if (surface) block.api = surface;\n\n const kind = entry.credential?.kind ?? \"none\";\n if (kind === \"none\") {\n block.auth = \"none\";\n } else if (kind === \"sigv4\" || kind === \"google_adc\" || kind === \"azure_entra\") {\n // Ambient signing chains: the AWS/Google/Azure SDK inside omp signs the\n // request from its own environment. There is no token to hand over, and\n // omp must not treat the provider as unavailable for the lack of one.\n block.auth = \"none\";\n } else {\n block.apiKey = providerCredentialEnvName(id);\n // `bearer` is the only kind that needs the header injected; the Anthropic\n // and Azure surfaces place an `api_key_header` credential themselves.\n if (kind === \"bearer\" || kind === \"oauth\") block.authHeader = true;\n }\n\n const headers = { ...entry.headers };\n if (entry.query_params && Object.keys(entry.query_params).length > 0) {\n if (surface === \"bedrock-converse-stream\") {\n // omp keeps a query string on a Bedrock `baseUrl` and appends it to the\n // final URL (and to SigV4's canonical request) — the documented carrier\n // for gateways that authenticate by query parameter.\n const search = new URLSearchParams(entry.query_params).toString();\n block.baseUrl = `${baseUrl}${baseUrl.includes(\"?\") ? \"&\" : \"?\"}${search}`;\n } else {\n warnings.push(\n `provider '${id}' declares query_params, which omp's models.yml has no slot for on surface ` +\n `'${surface ?? \"unset\"}' — they were dropped`,\n );\n }\n }\n if (Object.keys(headers).length > 0) block.headers = headers;\n\n const discoveryType = entry.discovery?.type;\n if (discoveryType) {\n if (!OMP_DISCOVERY_TYPES.includes(discoveryType)) {\n warnings.push(\n `provider '${id}' declares discovery '${discoveryType}', which omp does not implement — ` +\n `models come from model_map only`,\n );\n } else if (!surface && discoveryType !== \"proxy\") {\n warnings.push(\n `provider '${id}' declares discovery '${discoveryType}' but no surface — omp requires ` +\n `provider-level api for every discovery type but 'proxy'`,\n );\n } else {\n block.discovery = { type: discoveryType };\n }\n }\n\n const models: OmpModelBlock[] = [];\n for (const [logical, providerSide] of entryModelIds(entry)) {\n const model: OmpModelBlock = { id: providerSide };\n if (logical !== providerSide) model.name = logical;\n const caps = entry.capabilities?.[logical];\n if (caps?.context_window !== undefined) model.contextWindow = caps.context_window;\n if (caps?.images !== undefined) model.input = caps.images ? [\"text\", \"image\"] : [\"text\"];\n if (caps?.tools !== undefined) model.supportsTools = caps.tools;\n models.push(model);\n }\n if (models.length > 0) {\n if (!surface) {\n warnings.push(\n `provider '${id}' declares models but no surface — omp requires api at provider or model level`,\n );\n return undefined;\n }\n block.models = models;\n } else if (!block.discovery) {\n warnings.push(\n `provider '${id}' declares neither model_map/capabilities nor a discovery omp implements — ` +\n `it will only override an omp built-in provider of the same id`,\n );\n }\n\n return block;\n}\n\n/**\n * Resolve the `roles` table into omp `modelRoles` selectors.\n *\n * Roles do not chain, so each value is resolved as a plain selector rather than\n * through `@role` expansion. A trailing omp thinking suffix (`:high`) survives\n * untouched: it is not part of any `model_map` key, so it falls through\n * `parseModelRef`'s literal path and is re-emitted on the provider-side id.\n */\nfunction renderModelRoles(\n registry: AIProviderRegistry | MergedAIProviderRegistry,\n roles: Record<string, string> | undefined,\n modelRef: string | undefined,\n warnings: string[],\n): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [role, selector] of Object.entries(roles ?? {})) {\n const trimmed = selector.trim();\n if (!trimmed) continue;\n const ref = parseModelRef(trimmed, registry);\n if (!ref?.providerModelId) {\n warnings.push(\n `role '${role}' ('${selector}') resolves to no model in this registry — dropped`,\n );\n continue;\n }\n out[role] = `${ref.providerId}/${ref.providerModelId}`;\n }\n if (out.default === undefined && modelRef) {\n const ref = parseModelRef(modelRef, registry, roles);\n if (ref?.providerModelId) out.default = `${ref.providerId}/${ref.providerModelId}`;\n }\n return out;\n}\n\n/**\n * The warning for an entry left out of `models.yml` because its credential does not resolve.\n * `skaile run` and `skaile repl` print the same fact as one `! provider ... is not used` line\n * (runner `credential-notice.ts`), so the harness keeps this warning off the console.\n */\nfunction credentialOmissionWarning(id: string, reason: string): string {\n return `provider '${id}' omitted from models.yml: ${reason}`;\n}\n\n/** True for a warning built by {@link credentialOmissionWarning}. */\nexport function isCredentialOmissionWarning(warning: string): boolean {\n return /^provider '[^']+' omitted from models\\.yml: /.test(warning);\n}\n\n/**\n * Serialize a registry into the two omp files plus the credential environment\n * that carries their secrets.\n *\n * Only enabled `llm`/`embed` entries are rendered: `stt`/`tts` entries have no\n * wire surface and omp has no slot for them. The credential environment is\n * scoped to the same set — the omp child has no use for a Deepgram key, and a\n * secret that reaches a process which cannot spend it is pure exposure.\n *\n * An entry whose credential does not resolve here still gets its\n * `SKAILE_AIP_<ID>_KEY` name written into `models.yml`: a `platform:` ref is\n * resolved by the session boundary, which materializes the value into\n * `AgentConfig.env` under that same programme-standard name.\n */\nexport function renderOmpRegistry(\n registry: AIProviderRegistry | MergedAIProviderRegistry,\n options: RenderOmpRegistryOptions = {},\n): OmpRegistryRender {\n const warnings: string[] = [];\n const providers: Record<string, OmpProviderBlock> = {};\n const providerIds: string[] = [];\n const credentialEnv: Record<string, string> = {};\n\n // An entry whose credential cannot be resolved here is not handed to omp: it\n // would send an empty key (`invalid x-api-key`) and shadow omp's own login for\n // the same provider.\n const unresolved = new Map(\n unresolvedProviderCredentials(registry, options).map((u) => [u.providerId, u.reason]),\n );\n for (const [id, entry] of registryEntries(registry)) {\n if (entry.kind !== \"llm\" && entry.kind !== \"embed\") continue;\n if (!isEntryEnabled(entry)) continue;\n const reason = unresolved.get(id);\n if (reason) {\n warnings.push(credentialOmissionWarning(id, reason));\n continue;\n }\n const block = renderProvider(id, entry as AIProviderLlmEntry, warnings);\n if (!block) continue;\n providers[id] = block;\n providerIds.push(id);\n // Only the canonical name, never a vendor-conventional alias: every\n // endpoint omp may address is named in models.yml with its own variable,\n // and a stray `ANTHROPIC_API_KEY` would silently make omp's *built-in*\n // Anthropic provider look authenticated with some gateway entry's key.\n const secret = entryCredentialValue(id, entry, options);\n if (secret) credentialEnv[providerCredentialEnvName(id)] = secret;\n }\n\n const modelRoles = renderModelRoles(registry, options.roles, options.modelRef, warnings);\n\n const settings: Record<string, unknown> = { ...options.baseSettings };\n if (Object.keys(modelRoles).length > 0) settings.modelRoles = modelRoles;\n\n return {\n modelsYml: stringify({ providers }),\n configYml: stringify(settings),\n credentialEnv,\n providerIds,\n modelRoles,\n warnings,\n };\n}\n\n/**\n * The models a registry makes selectable, as the harness-neutral catalog entry.\n *\n * ids are `<providerId>/<providerModelId>` — the exact selector omp's `--model`\n * and `modelRoles` take, and unambiguous when two entries serve the same model\n * id, which is the normal case once a session carries N providers.\n */\nexport function registryModelEntries(\n registry: AIProviderRegistry | MergedAIProviderRegistry | undefined,\n): ModelEntry[] {\n if (!registry) return [];\n const out: ModelEntry[] = [];\n for (const [id, entry] of registryEntries(registry)) {\n if (entry.kind !== \"llm\" && entry.kind !== \"embed\") continue;\n if (!isEntryEnabled(entry)) continue;\n const label = entry.label ?? id;\n for (const [logical, providerSide] of entryModelIds(entry as AIProviderLlmEntry)) {\n out.push({ id: `${id}/${providerSide}`, name: `${label} ${logical}` });\n }\n }\n return out;\n}\n\n/**\n * Why omp cannot serve `<providerId>/<model>` from this registry, or `undefined` when it can (or\n * when this check cannot tell). omp knows only the models a registry entry declares\n * (`model_map`, `capabilities`) or its `discovery` lists: for a provider id omp has no login for\n * and an entry that declares neither, `omp --model fake/fake-1` exits with `Model \"fake/fake-1\"\n * not found` on the first prompt (p1334). `nativeProviders` are the ids omp ships a login for;\n * their entry only overrides the built-in provider, so the model may still be omp's own.\n */\nexport function ompRegistryModelRefusal(\n registry: AIProviderRegistry | MergedAIProviderRegistry | undefined,\n selector: string,\n nativeProviders: ReadonlySet<string>,\n): string | undefined {\n if (!registry) return undefined;\n const slash = selector.indexOf(\"/\");\n if (slash <= 0) return undefined;\n const head = selector.slice(0, slash);\n // A trailing omp thinking suffix (`:high`) is not part of the model id.\n const model = selector.slice(slash + 1).replace(/:(off|minimal|low|medium|high|xhigh)$/i, \"\");\n if (!model || nativeProviders.has(head)) return undefined;\n const found = registryEntries(registry).find(([id]) => id === head);\n if (!found) return undefined;\n const [, entry] = found;\n if ((entry.kind !== \"llm\" && entry.kind !== \"embed\") || !isEntryEnabled(entry)) return undefined;\n const llm = entry as AIProviderLlmEntry;\n if (llm.discovery?.type && OMP_DISCOVERY_TYPES.includes(llm.discovery.type)) return undefined;\n const known = entryModelIds(llm).flatMap(([logical, providerSide]) => [logical, providerSide]);\n if (known.includes(model)) return undefined;\n return known.length === 0\n ? `omp cannot serve model '${selector}': registry entry '${head}' declares no models (add model_map or capabilities to it in providers.yml), and omp has no login for '${head}'. Pick a model omp lists.`\n : `omp cannot serve model '${selector}': registry entry '${head}' declares ${known.join(\", \")}, not '${model}'. Pick one of those, or add '${model}' to the entry's model_map.`;\n}\n\n/** Options for {@link materializeOmpAgentDir}. */\nexport interface MaterializeOmpAgentDirOptions extends RenderOmpRegistryOptions {\n /**\n * The user's own omp agent directory to seed from — its auth store\n * (`agent.db`) is linked in so a standalone session keeps the login the\n * developer already has, and its `config.yml` is read as base settings.\n * Sessions, history, `mcp.json` and extensions are not linked. Absent (the\n * platform case) seeds nothing. A missing directory is not an error: it is the platform case.\n */\n seedFrom?: string;\n /**\n * GitAgent identity directory (`AgentConfig.agentDir`) whose contents omp\n * reads out of the same base: `agent.yaml`, `SOUL.md`, `RULES.md`,\n * `knowledge/`. Linked in after the seed, so identity wins a name clash.\n */\n identityDir?: string;\n}\n\n/** What {@link materializeOmpAgentDir} wrote. */\nexport interface OmpAgentDirMaterialization extends OmpRegistryRender {\n /** The directory `PI_CODING_AGENT_DIR` must point at. */\n agentDir: string;\n /** The directory is the harness's temporary home: remove it when the run ends. */\n ephemeral?: boolean;\n /** Names linked in from {@link MaterializeOmpAgentDirOptions.seedFrom}. */\n seeded: string[];\n /** Names linked in from {@link MaterializeOmpAgentDirOptions.identityDir}. */\n identity: string[];\n}\n\n/** Inputs {@link planOmpAgentDir} derives the session directory layout from. */\nexport interface OmpAgentDirPlanInput {\n /** Session working directory. Distinguishes two default-named sessions. */\n cwd: string;\n /** Session id — the directory key, so concurrent sessions never collide. */\n sessionId?: string;\n /** `AgentConfig.harnessOptions.omp`. */\n options?: OmpHarnessOptions;\n /** Home the default seed source resolves against. Defaults to `os.homedir()`. */\n homeDir?: string;\n /** Root the private directory is created under. Defaults to `os.tmpdir()`. */\n tmpDir?: string;\n}\n\n/** Where a session's omp agent directory lives, and what it inherits. */\nexport interface OmpAgentDirPlan {\n /** Session-private directory `PI_CODING_AGENT_DIR` points at. */\n agentDir: string;\n /** User agent directory whose login is linked in, when there is one to inherit. */\n seedFrom?: string;\n /**\n * The directory is the harness's own temporary home, to be removed when the run ends. False\n * when the session named its own `harnessOptions.omp.agentHome`, which belongs to the user.\n */\n ephemeral: boolean;\n}\n\n/**\n * Decide the session-private agent directory and whether it inherits the\n * developer's omp state.\n *\n * The directory is deliberately **outside `cwd`**. It holds omp's credential\n * store — a symlink to the developer's `agent.db` on a standalone machine —\n * and `cwd` is the one tree the agent has write access to by design; managed\n * codex draws the same line and rejects a private directory nested in the\n * workspace (`codex/instance-state.ts`). Sessions are keyed by id, falling\n * back to a digest of the workspace path. The temporary directory is `<key>-<random>`, created per\n * call, so no two runs share one (and one run's cleanup never removes another's).\n *\n * Seeding keys on the user directory *existing*, which is exactly the\n * platform/standalone distinction the plan draws: a platform container has no\n * `~/.omp/agent`, so the session correctly starts empty; a developer machine\n * has one, so the session keeps the logins already stored there.\n * `harnessOptions.omp.seedUserAgentDir: false` forces the empty case anyway.\n */\nexport async function planOmpAgentDir(input: OmpAgentDirPlanInput): Promise<OmpAgentDirPlan> {\n const options = input.options;\n const key =\n input.sessionId?.trim() ||\n createHash(\"sha256\").update(path.resolve(input.cwd)).digest(\"hex\").slice(0, 16);\n const ephemeral = options?.agentHome === undefined;\n // The temporary home is unique per spawn: callers key sessions loosely (`skaile chat` passes\n // none, `serve` without --session-id uses one shared id, sub-prompt harnesses share a hash), and\n // the harness deletes its home when its run ends, which must never reach a sibling's home.\n let agentDir = options?.agentHome ?? \"\";\n if (ephemeral) {\n const root = path.join(input.tmpDir ?? os.tmpdir(), \"skaile-omp-agent\");\n await fs.mkdir(root, { recursive: true });\n await sweepStaleOmpHomes(root);\n agentDir = await fs.mkdtemp(path.join(root, `${key.replace(/[^A-Za-z0-9._-]/g, \"_\")}-`));\n await fs.writeFile(path.join(agentDir, OWNER_FILE), String(process.pid));\n }\n if (options?.seedUserAgentDir === false) return { agentDir, ephemeral };\n\n const seedFrom =\n options?.userAgentDir ?? path.join(input.homeDir ?? os.homedir(), \".omp\", \"agent\");\n if (path.resolve(seedFrom) === path.resolve(agentDir)) return { agentDir, ephemeral };\n try {\n if (!(await fs.stat(seedFrom)).isDirectory()) return { agentDir, ephemeral };\n } catch {\n return { agentDir, ephemeral };\n }\n return { agentDir, seedFrom, ephemeral };\n}\n\n/** Names the process that created a temporary home; the sweep keeps a home whose owner lives. */\nconst OWNER_FILE = \".skaile-owner-pid\";\n/** A temporary home older than this whose owner is gone is removed at the next start. */\nconst STALE_HOME_MS = 24 * 60 * 60 * 1000;\n\n/**\n * The names skaile gave a temporary home: `<key>-<six random characters>` (`mkdtemp`), and\n * `<key>-<12 hex>` from releases before 5.16 (p1151).\n */\nconst SKAILE_HOME_NAME = /^[A-Za-z0-9._-]+-(?:[A-Za-z0-9]{6}|[0-9a-f]{12})$/;\n\nfunction processAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n return (err as NodeJS.ErrnoException).code === \"EPERM\";\n }\n}\n\n/**\n * Whether nothing listens on the unix socket `file`: a connect is refused (the process that\n * bound it is gone) or finds no socket. A socket somebody still serves, or one that does not\n * answer within a second, counts as in use.\n */\nfunction socketIsDead(file: string): Promise<boolean> {\n return new Promise<boolean>((resolve) => {\n const conn = net.connect(file);\n const done = (dead: boolean) => {\n conn.destroy();\n resolve(dead);\n };\n conn.setTimeout(1000, () => done(false));\n conn.once(\"connect\", () => done(false));\n conn.once(\"error\", (err: NodeJS.ErrnoException) =>\n done(err.code === \"ECONNREFUSED\" || err.code === \"ENOENT\"),\n );\n });\n}\n\n/**\n * Remove temporary homes older than a day whose creating process is gone: a run that was killed\n * never reached the harness's own cleanup, and every one leaves symlinks and databases behind\n * (p811). A home of a live process is kept whatever its age. A unix socket of that age that\n * nobody listens on is removed too (omp binds one beside the homes and a killed run leaves it,\n * p1316). Best effort: a failure is ignored.\n */\nasync function sweepStaleOmpHomes(root: string, now = Date.now()): Promise<void> {\n let names: string[];\n try {\n names = await fs.readdir(root);\n } catch {\n return;\n }\n await Promise.all(\n names.map(async (name) => {\n const home = path.join(root, name);\n try {\n const stat = await fs.lstat(home);\n if (now - stat.mtimeMs < STALE_HOME_MS) return;\n if (stat.isSocket()) {\n if (await socketIsDead(home)) await fs.rm(home, { force: true });\n return;\n }\n if (!stat.isDirectory()) return;\n const owner = Number.parseInt(\n await fs.readFile(path.join(home, OWNER_FILE), \"utf8\").catch(() => \"\"),\n 10,\n );\n if (Number.isInteger(owner) && processAlive(owner)) return;\n // A home with no owner file is one an older version made: remove it only when its\n // name has the `<key>-<six random characters>` shape `mkdtemp` gives (never a folder\n // somebody else put in this directory).\n if (!Number.isInteger(owner) && !SKAILE_HOME_NAME.test(name)) return;\n await fs.rm(home, { recursive: true, force: true });\n } catch {\n /* another process may be sweeping the same home */\n }\n }),\n );\n}\n\n/**\n * Run the steps that fill a planned home; when one throws, remove a temporary home before\n * rethrowing. The harness only learns the home (and deletes it at cleanup) once these steps\n * return, so a failure here would otherwise leave a new directory per spawn behind.\n */\nasync function removeHomeOnThrow<T>(plan: OmpAgentDirPlan, fill: () => Promise<T>): Promise<T> {\n try {\n return await fill();\n } catch (err) {\n if (plan.ephemeral) await fs.rm(plan.agentDir, { recursive: true, force: true });\n throw err;\n }\n}\n\n/** Remove a path whatever it is, without following a symlink into its target. */\nasync function removeEntry(target: string): Promise<void> {\n let stat: Stats;\n try {\n stat = await fs.lstat(target);\n } catch {\n return;\n }\n if (stat.isSymbolicLink() || stat.isFile()) {\n await fs.unlink(target);\n return;\n }\n await fs.rm(target, { recursive: true, force: true });\n}\n\n/** Symlink every entry of `sourceDir` into `dir`, skipping the owned names. */\nasync function linkInto(\n dir: string,\n sourceDir: string,\n skip: Record<string, true>,\n): Promise<string[]> {\n let names: string[];\n try {\n names = await fs.readdir(sourceDir);\n } catch {\n return [];\n }\n const linked: string[] = [];\n for (const name of names) {\n if (skip[name]) continue;\n const target = path.join(dir, name);\n await removeEntry(target);\n await fs.symlink(path.join(sourceDir, name), target);\n linked.push(name);\n }\n return linked;\n}\n\n/**\n * Remove the symlinks into the user's agent directory that an older release (or a home the\n * session named itself) left in `dir`: only the login files may point there (p811).\n */\nasync function dropStaleSeedLinks(dir: string, sourceDir: string): Promise<void> {\n let names: string[];\n try {\n names = await fs.readdir(dir);\n } catch {\n return;\n }\n const root = path.resolve(sourceDir);\n for (const name of names) {\n if ((OMP_LOGIN_FILES as readonly string[]).includes(name)) continue;\n const entry = path.join(dir, name);\n try {\n if (!(await fs.lstat(entry)).isSymbolicLink()) continue;\n const to = path.resolve(dir, await fs.readlink(entry));\n if (to === root || to.startsWith(root + path.sep)) await fs.unlink(entry);\n } catch {\n // Gone or unreadable: nothing to drop.\n }\n }\n}\n\n/** Symlink the login files (see {@link OMP_LOGIN_FILES}) of the user's agent directory into `dir`. */\nasync function linkLoginFiles(dir: string, sourceDir: string): Promise<string[]> {\n await dropStaleSeedLinks(dir, sourceDir);\n const linked: string[] = [];\n for (const name of OMP_LOGIN_FILES) {\n const from = path.join(sourceDir, name);\n try {\n if (!(await fs.stat(from)).isFile()) continue;\n } catch {\n continue;\n }\n const target = path.join(dir, name);\n await removeEntry(target);\n await fs.symlink(from, target);\n linked.push(name);\n }\n return linked;\n}\n\n/** Read a seeded `config.yml` as the base settings, or `{}` when unusable. */\nasync function readSeededSettings(seedFrom: string): Promise<Record<string, unknown>> {\n for (const name of [\"config.yml\", \"config.yaml\"]) {\n let raw: string;\n try {\n raw = await fs.readFile(path.join(seedFrom, name), \"utf-8\");\n } catch {\n continue;\n }\n const parsed = parse(raw) as unknown;\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n }\n return {};\n}\n\n/**\n * The model selector of the user's own omp default (`modelRoles.default` in their `config.yml`)\n * when it can only be served by their own `models.yml`, else `undefined` (p1172).\n *\n * The session-private home never copies the user's `models.yml`: it holds their credentials.\n * A default such as `anthropic/claude-opus-5-5` that their `models.yml` defines (a gateway, a\n * local server) then names a provider the private home does not have, and omp quietly picks\n * another model. A default on a built-in omp provider keeps working there (its login is linked\n * in), and a provider the registry renders is there too, so neither is reported.\n */\nasync function ownDefaultNeedingOwnModels(\n seedFrom: string,\n rendered: readonly string[],\n): Promise<string | undefined> {\n const selector = (await readSeededSettings(seedFrom)).modelRoles;\n const own =\n selector && typeof selector === \"object\" && !Array.isArray(selector)\n ? (selector as Record<string, unknown>).default\n : undefined;\n if (typeof own !== \"string\" || !own.includes(\"/\")) return undefined;\n const providerId = own.slice(0, own.indexOf(\"/\"));\n if (rendered.includes(providerId)) return undefined;\n let raw: string;\n try {\n raw = await fs.readFile(path.join(seedFrom, OMP_MODELS_FILENAME), \"utf-8\");\n } catch {\n return undefined;\n }\n const providers = (parse(raw) as { providers?: unknown } | null)?.providers;\n if (!providers || typeof providers !== \"object\" || Array.isArray(providers)) return undefined;\n return providerId in providers ? own : undefined;\n}\n\n/**\n * Build the session-private agent directory and write omp's two files into it.\n *\n * The user's `~/.omp/agent/models.yml` and `config.yml` are never opened for\n * writing: `config.yml` is *copied* into the session (so a token refresh or a\n * settings write inside the session cannot reach back through a symlink),\n * `models.yml` is not seeded at all, and everything else — `agent.db*` above\n * all — is symlinked so the session shares the developer's logins and caches.\n */\nexport async function materializeOmpAgentDir(\n agentDir: string,\n registry: AIProviderRegistry | MergedAIProviderRegistry,\n options: MaterializeOmpAgentDirOptions = {},\n): Promise<OmpAgentDirMaterialization> {\n await fs.mkdir(agentDir, { recursive: true });\n\n const seeded = options.seedFrom ? await linkLoginFiles(agentDir, options.seedFrom) : [];\n const identity = options.identityDir\n ? await linkInto(agentDir, options.identityDir, OWNED_AGENT_DIR_FILES)\n : [];\n\n const baseSettings =\n options.baseSettings ??\n (options.seedFrom ? await readSeededSettings(options.seedFrom) : undefined);\n\n const render = renderOmpRegistry(registry, { ...options, baseSettings });\n\n await fs.writeFile(path.join(agentDir, OMP_MODELS_FILENAME), render.modelsYml, \"utf-8\");\n await fs.writeFile(path.join(agentDir, OMP_CONFIG_FILENAME), render.configYml, \"utf-8\");\n\n return { ...render, agentDir, seeded, identity };\n}\n\n/** What the omp harness gets back from {@link materializeOmpProviders}. */\nexport interface OmpProviderMaterialization extends OmpAgentDirMaterialization {\n /**\n * The `--model` argument for this session: `<providerId>/<providerModelId>`,\n * the one selector form omp treats as unambiguous when several providers\n * carry the same model id. Absent when the session named no model.\n */\n modelArg?: string;\n}\n\n/**\n * The whole omp adapter for one session: decide the private agent directory,\n * inherit the developer's omp state where there is any, write `models.yml` and\n * `config.yml`, and report the credential environment and `--model` selector\n * the child needs.\n *\n * Returns `undefined` when the session carries no registry with a selectable\n * entry — the harness then behaves exactly as it did before the registry\n * existed, which is what keeps this an expand-and-contract change.\n *\n * Also returns `undefined` when the session names no model (no `--model`, no\n * `default` role) and the user's own omp default is served by the user's own\n * `models.yml`, which a private home does not carry (p1172): the registry would\n * only swap that default for whatever model omp finds first.\n *\n * Also returns `undefined` when no entry can be handed to omp (every\n * credential unresolved) or when the entry the session's model selects was\n * left out: a session-private agent directory would then start omp without the\n * user's own settings, and a model that only the user's own omp login can\n * serve would fail with `No API key found` although native omp answers. The\n * harness then starts omp on the user's agent directory, so \"omp falls back to\n * its own login\" is true.\n *\n * `env:` references resolve against the runner's environment *overlaid with*\n * `config.env`, so a credential the session boundary already resolved (a\n * `platform:` secret it turned into an env var, for instance) is found here\n * under the name the registry entry points at.\n */\nexport async function materializeOmpProviders(\n config: AgentConfig,\n): Promise<OmpProviderMaterialization | undefined> {\n const registry = config.registry;\n if (!registry) return undefined;\n const selectable = registryEntries(registry).some(\n ([, entry]) => (entry.kind === \"llm\" || entry.kind === \"embed\") && isEntryEnabled(entry),\n );\n if (!selectable) return undefined;\n\n const env = { ...process.env, ...config.env };\n const selected = config.modelRef\n ? parseModelRef(config.modelRef, registry, config.roles)\n : undefined;\n const usable = renderOmpRegistry(registry, {\n env,\n ...(config.roles ? { roles: config.roles } : {}),\n ...(config.modelRef ? { modelRef: config.modelRef } : {}),\n }).providerIds;\n if (usable.length === 0) return undefined;\n if (selected && !usable.includes(selected.providerId)) return undefined;\n\n const plan = await planOmpAgentDir({\n cwd: config.cwd,\n ...(config.sessionId ? { sessionId: config.sessionId } : {}),\n ...(config.harnessOptions?.omp ? { options: config.harnessOptions.omp } : {}),\n });\n\n if (!selected && !config.model && plan.seedFrom) {\n const named = renderOmpRegistry(registry, {\n env,\n ...(config.roles ? { roles: config.roles } : {}),\n }).modelRoles;\n if (named.default === undefined && (await ownDefaultNeedingOwnModels(plan.seedFrom, usable))) {\n if (plan.ephemeral) await fs.rm(plan.agentDir, { recursive: true, force: true });\n return undefined;\n }\n }\n\n const rendered = await removeHomeOnThrow(plan, () =>\n materializeOmpAgentDir(plan.agentDir, registry, {\n env,\n ...(plan.seedFrom ? { seedFrom: plan.seedFrom } : {}),\n ...(config.agentDir ? { identityDir: config.agentDir } : {}),\n ...(config.roles ? { roles: config.roles } : {}),\n ...(config.modelRef ? { modelRef: config.modelRef } : {}),\n }),\n );\n\n return {\n ...rendered,\n ephemeral: plan.ephemeral,\n ...(selected?.providerModelId\n ? { modelArg: `${selected.providerId}/${selected.providerModelId}` }\n : {}),\n };\n}\n\n/** What {@link materializeOmpIdentityDir} wrote. */\nexport interface OmpIdentityDirMaterialization {\n /** The directory `PI_CODING_AGENT_DIR` must point at. */\n agentDir: string;\n /** The directory is the harness's temporary home: remove it when the run ends. */\n ephemeral: boolean;\n /** Names linked in from the user's own omp agent directory. */\n seeded: string[];\n /** Names linked in from `AgentConfig.agentDir`. */\n identity: string[];\n}\n\n/**\n * The omp home of a session that has an agent directory but no provider registry.\n *\n * omp keeps its runtime state (`agent.db`, `models.db`, `skill-descriptions.db`, session\n * scratch, `intercom/`) in `PI_CODING_AGENT_DIR`. Pointing that at the agent directory itself\n * put those files beside `SOUL.md` in a pinned, shared source (`.skaile/cache/agents/<name>/`)\n * and made omp name a path inside the cache when it lacks a credential. So the home is a\n * private directory outside `cwd` (see {@link planOmpAgentDir}): the developer's own omp state\n * is linked in (their login keeps working), their `config.yml` / `models.yml` are copied (a\n * settings write cannot reach back), and the agent directory's entries are linked in last,\n * so the agent files stay where they are and omp can still read them.\n *\n * Returns `undefined` when the session has no `agentDir`.\n */\nexport async function materializeOmpIdentityDir(\n config: AgentConfig,\n): Promise<OmpIdentityDirMaterialization | undefined> {\n const identityDir = config.agentDir;\n if (!identityDir) return undefined;\n // Keyed by the agent and the workspace too: two role children of one session, each with\n // its own agent directory, must not link their entries into one home.\n const agentKey = createHash(\"sha256\")\n .update(`${path.resolve(identityDir)}\\0${path.resolve(config.cwd)}`)\n .digest(\"hex\")\n .slice(0, 12);\n const plan = await planOmpAgentDir({\n cwd: config.cwd,\n sessionId: `${config.sessionId?.trim() || \"agent\"}-${agentKey}`,\n ...(config.harnessOptions?.omp ? { options: config.harnessOptions.omp } : {}),\n });\n return removeHomeOnThrow(plan, async () => {\n await fs.mkdir(plan.agentDir, { recursive: true });\n const seeded = plan.seedFrom ? await linkLoginFiles(plan.agentDir, plan.seedFrom) : [];\n if (plan.seedFrom) {\n for (const name of Object.keys(OWNED_AGENT_DIR_FILES)) {\n const from = path.join(plan.seedFrom, name);\n try {\n if (!(await fs.stat(from)).isFile()) continue;\n } catch {\n continue;\n }\n const to = path.join(plan.agentDir, name);\n await removeEntry(to);\n await fs.copyFile(from, to);\n }\n }\n const identity = await linkInto(plan.agentDir, identityDir, {\n ...OWNED_AGENT_DIR_FILES,\n ...OMP_RUNTIME_ENTRIES,\n });\n return { agentDir: plan.agentDir, ephemeral: plan.ephemeral, seeded, identity };\n });\n}\n"]}
1
+ {"version":3,"sources":["../bridge/src/harnesses/omp-models-yml.ts"],"names":[],"mappings":"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","file":"chunk-UAOHR6AL.js","sourcesContent":["/**\n * The omp adapter: an AI provider registry serialized into omp's own\n * configuration files, inside a session-private agent directory.\n *\n * omp already implements the target schema — `~/.omp/agent/models.yml` is a\n * `providers:` map with `baseUrl`, `api`, `apiKey`, `headers`, `authHeader`,\n * `auth`, `discovery` and `models[]` (`omp://models.md`) — so this module is a\n * *serializer*, not a translator. Our `surface` enum is omp's `api` enum\n * verbatim (§1.2); {@link OMP_API_VALUES} encodes that assertion so a future\n * surface that omp does not know is reported instead of silently written into\n * a file omp will then reject wholesale.\n *\n * Two files are produced:\n *\n * - `models.yml` — one entry per enabled `llm`/`embed` registry entry. `apiKey`\n * holds the **name** of the per-entry environment variable\n * (`SKAILE_AIP_<ID>_KEY`, {@link providerCredentialEnvName}), never a value:\n * omp resolves a `models.yml` `apiKey` as an env-var name first and only\n * falls back to treating it as a literal token. The values travel in the\n * child environment.\n * - `config.yml` — omp's settings file, carrying `modelRoles` rendered from the\n * `skaile.yaml` `roles` table. `models.yml`'s root accepts only `providers:`;\n * unknown root keys fail its schema, so roles cannot live there.\n *\n * `PI_CODING_AGENT_DIR` relocates omp's whole agent base — `config.yml`,\n * `models.yml` **and** the auth store `agent.db` (`omp://settings.md`). Pointing\n * it at a session-private directory is what makes N providers per session\n * possible without ever writing the user's `~/.omp/agent`. The cost is that a\n * standalone developer session would also lose the OAuth logins stored in that\n * auth store, so {@link materializeOmpAgentDir} seeds the session directory\n * with symlinks to everything in the user's agent directory *except* the two\n * files this module owns. Verified against omp 18.2.6: a ChatGPT/Codex OAuth\n * account stored in `~/.omp/agent/agent.db` still resolves (`omp token\n * openai-codex`) from a relocated directory that only symlinks `agent.db*`.\n * Under the platform the user directory does not exist, the seed is a no-op,\n * and the session starts empty — which is the correct behaviour there.\n *\n * @see PROVIDER-CONFIG-PLAN.md §2.3 (omp row), §5, §9\n * @see omp://models.md, omp://settings.md\n */\n\nimport { createHash } from \"node:crypto\";\nimport type { Stats } from \"node:fs\";\nimport fs from \"node:fs/promises\";\nimport os from \"node:os\";\nimport net from \"node:net\";\nimport path from \"node:path\";\nimport {\n type AIProviderLlmEntry,\n type AIProviderRegistry,\n AI_PROVIDER_SURFACES,\n isEntryEnabled,\n type MergedAIProviderRegistry,\n normalizeEntryBaseUrl,\n parseModelRef,\n registryEntries,\n resolveEntryBaseUrl,\n} from \"@skaile/workspaces/core/provider-registry\";\nimport { parse, stringify } from \"yaml\";\nimport type { ModelEntry } from \"../models.js\";\nimport {\n entryCredentialValue,\n providerCredentialEnvName,\n type RegistryCredentialEnvOptions,\n unresolvedProviderCredentials,\n} from \"../provider-materialize/credential-env.js\";\nimport { entrySurface } from \"../provider-materialize/env-harnesses.js\";\nimport type { AgentConfig, OmpHarnessOptions } from \"../types.js\";\n\n/**\n * omp's allowed provider/model `api` values (`omp://models.md`, \"Allowed\n * provider/model `api` values\").\n *\n * Every {@link AI_PROVIDER_SURFACES} member is in this list — that equality is\n * the premise the whole adapter rests on (§1.2). `google-gemini-cli` is omp's\n * one extra value, which the registry has no surface for.\n */\nexport const OMP_API_VALUES: readonly string[] = [...AI_PROVIDER_SURFACES, \"google-gemini-cli\"];\n\n/**\n * `discovery.type` values omp accepts. The registry's own enum is wider on one\n * side (`anthropic-models-list`, `google-models-list`, `none`) and narrower on\n * the other, so a discovery block is emitted only when omp can act on it.\n */\nconst OMP_DISCOVERY_TYPES: readonly string[] = [\n \"ollama\",\n \"llama.cpp\",\n \"lm-studio\",\n \"openai-models-list\",\n \"proxy\",\n \"litellm\",\n];\n\n/** One `providers.<id>` block of `models.yml`. */\ninterface OmpProviderBlock {\n baseUrl?: string;\n api?: string;\n /** The *name* of the env var holding the credential — never a value. */\n apiKey?: string;\n auth?: \"apiKey\" | \"none\" | \"oauth\";\n authHeader?: boolean;\n headers?: Record<string, string>;\n discovery?: { type: string };\n models?: OmpModelBlock[];\n}\n\n/** One entry of a provider's `models[]`. */\ninterface OmpModelBlock {\n id: string;\n name?: string;\n input?: string[];\n contextWindow?: number;\n supportsTools?: boolean;\n}\n\n/** The two files this module owns inside the agent directory. */\nexport const OMP_MODELS_FILENAME = \"models.yml\";\n\n/** omp's settings file; `modelRoles` lives here, not in `models.yml`. */\nexport const OMP_CONFIG_FILENAME = \"config.yml\";\n\n/**\n * Names {@link materializeOmpAgentDir} never links in from the user's agent\n * directory: the two files it writes itself, their alternate spellings, and\n * the legacy `settings.json` omp would migrate into `config.yml`.\n */\nconst OWNED_AGENT_DIR_FILES: Record<string, true> = {\n \"models.yml\": true,\n \"models.yaml\": true,\n \"models.json\": true,\n \"config.yml\": true,\n \"config.yaml\": true,\n \"config.yml.lock\": true,\n \"config.yaml.lock\": true,\n \"settings.json\": true,\n};\n\n/**\n * omp's own runtime state inside its home. An agent directory that an older release used as\n * omp's home still holds these; they are never agent files, so the identity link skips them\n * and the user's own login (`agent.db`) linked in from their agent directory stays in place.\n */\nconst OMP_RUNTIME_ENTRIES: Record<string, true> = {\n \"agent.db\": true,\n \"agent.db-shm\": true,\n \"agent.db-wal\": true,\n \"models.db\": true,\n \"models.db-shm\": true,\n \"models.db-wal\": true,\n \"skill-descriptions.db\": true,\n \"skill-descriptions.db-shm\": true,\n \"skill-descriptions.db-wal\": true,\n \"custom-session-files\": true,\n intercom: true,\n sessions: true,\n};\n\n/**\n * The only entries of the user's own omp agent directory a session's private home links in:\n * omp's credential store, so the login the developer already has keeps working. Sessions,\n * history, `mcp.json`, extensions and blobs stay out: a run must not write into the user's\n * session store or load their MCP servers and extensions. `config.yml` is copied, not linked.\n */\nconst OMP_LOGIN_FILES = [\"agent.db\", \"agent.db-shm\", \"agent.db-wal\"] as const;\n\n/** Options for {@link renderOmpRegistry}. */\nexport interface RenderOmpRegistryOptions extends RegistryCredentialEnvOptions {\n /** `skaile.yaml` `roles`: role name → model selector. Rendered as `modelRoles`. */\n roles?: Record<string, string>;\n /**\n * The session's own selector. When `roles` names no `default`, the resolved\n * model becomes `modelRoles.default` so `@default` (and omp's `*` alias)\n * resolve to the entry the session actually runs on.\n */\n modelRef?: string;\n /**\n * Settings to render underneath `modelRoles` — the user's own `config.yml`\n * when a standalone session seeds one. `modelRoles` is replaced wholesale,\n * not deep-merged: a seeded role pointing at a provider this session does\n * not define would otherwise survive as a dangling selector.\n */\n baseSettings?: Record<string, unknown>;\n}\n\n/** What {@link renderOmpRegistry} produced. */\nexport interface OmpRegistryRender {\n /** `models.yml` contents. */\n modelsYml: string;\n /** `config.yml` contents. */\n configYml: string;\n /**\n * `SKAILE_AIP_<ID>_KEY` → secret, for the child environment. The only place\n * a credential value appears; both rendered files hold names alone.\n */\n credentialEnv: Record<string, string>;\n /** Entry ids written to `models.yml`, in registry order. */\n providerIds: string[];\n /** `modelRoles` as written, role name → `<providerId>/<providerModelId>`. */\n modelRoles: Record<string, string>;\n /** Entries and roles that could not be rendered, and why. */\n warnings: string[];\n}\n\n/** The provider-side model ids an entry declares, keyed by logical id. */\nfunction entryModelIds(entry: AIProviderLlmEntry): Array<[string, string]> {\n const logical = new Set<string>([\n ...Object.keys(entry.model_map ?? {}),\n ...Object.keys(entry.capabilities ?? {}),\n ]);\n const seen = new Set<string>();\n const out: Array<[string, string]> = [];\n for (const id of logical) {\n const providerSide = entry.model_map?.[id] ?? id;\n if (seen.has(providerSide)) continue;\n seen.add(providerSide);\n out.push([id, providerSide]);\n }\n return out;\n}\n\n/**\n * Serialize one entry into an omp provider block, or report why it cannot be.\n *\n * omp requires `baseUrl` on any provider that declares `models[]`, and\n * provider-level `api` whenever `discovery` is set (except `discovery.type:\n * proxy`, where the wire is auto-detected per model). A file that violates its\n * schema is rejected *wholesale* — the registry keeps operating on built-in\n * models and the error only surfaces through `ModelRegistry.getError()` — so an\n * entry that cannot satisfy those rules is dropped here, loudly, rather than\n * taking every other provider down with it.\n */\nfunction renderProvider(\n id: string,\n entry: AIProviderLlmEntry,\n warnings: string[],\n): OmpProviderBlock | undefined {\n // The Anthropic SDK inside omp appends `/v1/messages`: a stored `…/v1` must\n // not reach the file as written. Not `entryBaseUrl`: omp gets no preset\n // literal, so an entry without an endpoint of its own is reported below.\n const baseUrl = normalizeEntryBaseUrl(resolveEntryBaseUrl(entry), entrySurface(entry));\n if (!baseUrl) {\n warnings.push(\n `provider '${id}' has no resolvable base_url (missing transport params?) — omitted from models.yml`,\n );\n return undefined;\n }\n\n // Only the entry's own surface becomes `api`; an inherited one stays omp's\n // to infer, as before. The preset's surface above only picks the URL rule.\n const surface = entry.surface;\n if (surface && !OMP_API_VALUES.includes(surface)) {\n warnings.push(\n `provider '${id}' has surface '${surface}', which omp's api enum does not accept`,\n );\n return undefined;\n }\n\n const block: OmpProviderBlock = { baseUrl };\n if (surface) block.api = surface;\n\n const kind = entry.credential?.kind ?? \"none\";\n if (kind === \"none\") {\n block.auth = \"none\";\n } else if (kind === \"sigv4\" || kind === \"google_adc\" || kind === \"azure_entra\") {\n // Ambient signing chains: the AWS/Google/Azure SDK inside omp signs the\n // request from its own environment. There is no token to hand over, and\n // omp must not treat the provider as unavailable for the lack of one.\n block.auth = \"none\";\n } else {\n block.apiKey = providerCredentialEnvName(id);\n // `bearer` is the only kind that needs the header injected; the Anthropic\n // and Azure surfaces place an `api_key_header` credential themselves.\n if (kind === \"bearer\" || kind === \"oauth\") block.authHeader = true;\n }\n\n const headers = { ...entry.headers };\n if (entry.query_params && Object.keys(entry.query_params).length > 0) {\n if (surface === \"bedrock-converse-stream\") {\n // omp keeps a query string on a Bedrock `baseUrl` and appends it to the\n // final URL (and to SigV4's canonical request) — the documented carrier\n // for gateways that authenticate by query parameter.\n const search = new URLSearchParams(entry.query_params).toString();\n block.baseUrl = `${baseUrl}${baseUrl.includes(\"?\") ? \"&\" : \"?\"}${search}`;\n } else {\n warnings.push(\n `provider '${id}' declares query_params, which omp's models.yml has no slot for on surface ` +\n `'${surface ?? \"unset\"}' — they were dropped`,\n );\n }\n }\n if (Object.keys(headers).length > 0) block.headers = headers;\n\n const discoveryType = entry.discovery?.type;\n if (discoveryType) {\n if (!OMP_DISCOVERY_TYPES.includes(discoveryType)) {\n warnings.push(\n `provider '${id}' declares discovery '${discoveryType}', which omp does not implement — ` +\n `models come from model_map only`,\n );\n } else if (!surface && discoveryType !== \"proxy\") {\n warnings.push(\n `provider '${id}' declares discovery '${discoveryType}' but no surface — omp requires ` +\n `provider-level api for every discovery type but 'proxy'`,\n );\n } else {\n block.discovery = { type: discoveryType };\n }\n }\n\n const models: OmpModelBlock[] = [];\n for (const [logical, providerSide] of entryModelIds(entry)) {\n const model: OmpModelBlock = { id: providerSide };\n if (logical !== providerSide) model.name = logical;\n const caps = entry.capabilities?.[logical];\n if (caps?.context_window !== undefined) model.contextWindow = caps.context_window;\n if (caps?.images !== undefined) model.input = caps.images ? [\"text\", \"image\"] : [\"text\"];\n if (caps?.tools !== undefined) model.supportsTools = caps.tools;\n models.push(model);\n }\n if (models.length > 0) {\n if (!surface) {\n warnings.push(\n `provider '${id}' declares models but no surface — omp requires api at provider or model level`,\n );\n return undefined;\n }\n block.models = models;\n } else if (!block.discovery) {\n warnings.push(\n `provider '${id}' declares neither model_map/capabilities nor a discovery omp implements — ` +\n `it will only override an omp built-in provider of the same id`,\n );\n }\n\n return block;\n}\n\n/**\n * Resolve the `roles` table into omp `modelRoles` selectors.\n *\n * Roles do not chain, so each value is resolved as a plain selector rather than\n * through `@role` expansion. A trailing omp thinking suffix (`:high`) survives\n * untouched: it is not part of any `model_map` key, so it falls through\n * `parseModelRef`'s literal path and is re-emitted on the provider-side id.\n */\nfunction renderModelRoles(\n registry: AIProviderRegistry | MergedAIProviderRegistry,\n roles: Record<string, string> | undefined,\n modelRef: string | undefined,\n warnings: string[],\n): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [role, selector] of Object.entries(roles ?? {})) {\n const trimmed = selector.trim();\n if (!trimmed) continue;\n const ref = parseModelRef(trimmed, registry);\n if (!ref?.providerModelId) {\n warnings.push(\n `role '${role}' ('${selector}') resolves to no model in this registry — dropped`,\n );\n continue;\n }\n out[role] = `${ref.providerId}/${ref.providerModelId}`;\n }\n if (out.default === undefined && modelRef) {\n const ref = parseModelRef(modelRef, registry, roles);\n if (ref?.providerModelId) out.default = `${ref.providerId}/${ref.providerModelId}`;\n }\n return out;\n}\n\n/**\n * The warning for an entry left out of `models.yml` because its credential does not resolve.\n * `skaile run` and `skaile repl` print the same fact as one `! provider ... is not used` line\n * (runner `credential-notice.ts`), so the harness keeps this warning off the console.\n */\nfunction credentialOmissionWarning(id: string, reason: string): string {\n return `provider '${id}' omitted from models.yml: ${reason}`;\n}\n\n/** True for a warning built by {@link credentialOmissionWarning}. */\nexport function isCredentialOmissionWarning(warning: string): boolean {\n return /^provider '[^']+' omitted from models\\.yml: /.test(warning);\n}\n\n/**\n * Serialize a registry into the two omp files plus the credential environment\n * that carries their secrets.\n *\n * Only enabled `llm`/`embed` entries are rendered: `stt`/`tts` entries have no\n * wire surface and omp has no slot for them. The credential environment is\n * scoped to the same set — the omp child has no use for a Deepgram key, and a\n * secret that reaches a process which cannot spend it is pure exposure.\n *\n * An entry whose credential does not resolve here still gets its\n * `SKAILE_AIP_<ID>_KEY` name written into `models.yml`: a `platform:` ref is\n * resolved by the session boundary, which materializes the value into\n * `AgentConfig.env` under that same programme-standard name.\n */\nexport function renderOmpRegistry(\n registry: AIProviderRegistry | MergedAIProviderRegistry,\n options: RenderOmpRegistryOptions = {},\n): OmpRegistryRender {\n const warnings: string[] = [];\n const providers: Record<string, OmpProviderBlock> = {};\n const providerIds: string[] = [];\n const credentialEnv: Record<string, string> = {};\n\n // An entry whose credential cannot be resolved here is not handed to omp: it\n // would send an empty key (`invalid x-api-key`) and shadow omp's own login for\n // the same provider.\n const unresolved = new Map(\n unresolvedProviderCredentials(registry, options).map((u) => [u.providerId, u.reason]),\n );\n for (const [id, entry] of registryEntries(registry)) {\n if (entry.kind !== \"llm\" && entry.kind !== \"embed\") continue;\n if (!isEntryEnabled(entry)) continue;\n const reason = unresolved.get(id);\n if (reason) {\n warnings.push(credentialOmissionWarning(id, reason));\n continue;\n }\n const block = renderProvider(id, entry as AIProviderLlmEntry, warnings);\n if (!block) continue;\n providers[id] = block;\n providerIds.push(id);\n // Only the canonical name, never a vendor-conventional alias: every\n // endpoint omp may address is named in models.yml with its own variable,\n // and a stray `ANTHROPIC_API_KEY` would silently make omp's *built-in*\n // Anthropic provider look authenticated with some gateway entry's key.\n const secret = entryCredentialValue(id, entry, options);\n if (secret) credentialEnv[providerCredentialEnvName(id)] = secret;\n }\n\n const modelRoles = renderModelRoles(registry, options.roles, options.modelRef, warnings);\n\n const settings: Record<string, unknown> = { ...options.baseSettings };\n if (Object.keys(modelRoles).length > 0) settings.modelRoles = modelRoles;\n\n return {\n modelsYml: stringify({ providers }),\n configYml: stringify(settings),\n credentialEnv,\n providerIds,\n modelRoles,\n warnings,\n };\n}\n\n/**\n * The models a registry makes selectable, as the harness-neutral catalog entry.\n *\n * ids are `<providerId>/<providerModelId>` — the exact selector omp's `--model`\n * and `modelRoles` take, and unambiguous when two entries serve the same model\n * id, which is the normal case once a session carries N providers.\n */\nexport function registryModelEntries(\n registry: AIProviderRegistry | MergedAIProviderRegistry | undefined,\n): ModelEntry[] {\n if (!registry) return [];\n const out: ModelEntry[] = [];\n for (const [id, entry] of registryEntries(registry)) {\n if (entry.kind !== \"llm\" && entry.kind !== \"embed\") continue;\n if (!isEntryEnabled(entry)) continue;\n const label = entry.label ?? id;\n for (const [logical, providerSide] of entryModelIds(entry as AIProviderLlmEntry)) {\n out.push({ id: `${id}/${providerSide}`, name: `${label} ${logical}` });\n }\n }\n return out;\n}\n\n/**\n * Why omp cannot serve `<providerId>/<model>` from this registry, or `undefined` when it can (or\n * when this check cannot tell). omp knows only the models a registry entry declares\n * (`model_map`, `capabilities`) or its `discovery` lists: for a provider id omp has no login for\n * and an entry that declares neither, `omp --model fake/fake-1` exits with `Model \"fake/fake-1\"\n * not found` on the first prompt (p1334). `nativeProviders` are the ids omp ships a login for;\n * their entry only overrides the built-in provider, so the model may still be omp's own.\n */\nexport function ompRegistryModelRefusal(\n registry: AIProviderRegistry | MergedAIProviderRegistry | undefined,\n selector: string,\n nativeProviders: ReadonlySet<string>,\n): string | undefined {\n if (!registry) return undefined;\n const slash = selector.indexOf(\"/\");\n if (slash <= 0) return undefined;\n const head = selector.slice(0, slash);\n // A trailing omp thinking suffix (`:high`) is not part of the model id.\n const model = selector.slice(slash + 1).replace(/:(off|minimal|low|medium|high|xhigh)$/i, \"\");\n if (!model || nativeProviders.has(head)) return undefined;\n const found = registryEntries(registry).find(([id]) => id === head);\n if (!found) return undefined;\n const [, entry] = found;\n if ((entry.kind !== \"llm\" && entry.kind !== \"embed\") || !isEntryEnabled(entry)) return undefined;\n const llm = entry as AIProviderLlmEntry;\n if (llm.discovery?.type && OMP_DISCOVERY_TYPES.includes(llm.discovery.type)) return undefined;\n const known = entryModelIds(llm).flatMap(([logical, providerSide]) => [logical, providerSide]);\n if (known.includes(model)) return undefined;\n return known.length === 0\n ? `omp cannot serve model '${selector}': registry entry '${head}' declares no models (add model_map or capabilities to it in providers.yml), and omp has no login for '${head}'. Pick a model omp lists.`\n : `omp cannot serve model '${selector}': registry entry '${head}' declares ${known.join(\", \")}, not '${model}'. Pick one of those, or add '${model}' to the entry's model_map.`;\n}\n\n/** Options for {@link materializeOmpAgentDir}. */\nexport interface MaterializeOmpAgentDirOptions extends RenderOmpRegistryOptions {\n /**\n * The user's own omp agent directory to seed from — its auth store\n * (`agent.db`) is linked in so a standalone session keeps the login the\n * developer already has, and its `config.yml` is read as base settings.\n * Sessions, history, `mcp.json` and extensions are not linked. Absent (the\n * platform case) seeds nothing. A missing directory is not an error: it is the platform case.\n */\n seedFrom?: string;\n /**\n * GitAgent identity directory (`AgentConfig.agentDir`) whose contents omp\n * reads out of the same base: `agent.yaml`, `SOUL.md`, `RULES.md`,\n * `knowledge/`. Linked in after the seed, so identity wins a name clash.\n */\n identityDir?: string;\n}\n\n/** What {@link materializeOmpAgentDir} wrote. */\nexport interface OmpAgentDirMaterialization extends OmpRegistryRender {\n /** The directory `PI_CODING_AGENT_DIR` must point at. */\n agentDir: string;\n /** The directory is the harness's temporary home: remove it when the run ends. */\n ephemeral?: boolean;\n /** Names linked in from {@link MaterializeOmpAgentDirOptions.seedFrom}. */\n seeded: string[];\n /** Names linked in from {@link MaterializeOmpAgentDirOptions.identityDir}. */\n identity: string[];\n}\n\n/** Inputs {@link planOmpAgentDir} derives the session directory layout from. */\nexport interface OmpAgentDirPlanInput {\n /** Session working directory. Distinguishes two default-named sessions. */\n cwd: string;\n /** Session id — the directory key, so concurrent sessions never collide. */\n sessionId?: string;\n /** `AgentConfig.harnessOptions.omp`. */\n options?: OmpHarnessOptions;\n /** Home the default seed source resolves against. Defaults to `os.homedir()`. */\n homeDir?: string;\n /** Root the private directory is created under. Defaults to `os.tmpdir()`. */\n tmpDir?: string;\n}\n\n/** Where a session's omp agent directory lives, and what it inherits. */\nexport interface OmpAgentDirPlan {\n /** Session-private directory `PI_CODING_AGENT_DIR` points at. */\n agentDir: string;\n /** User agent directory whose login is linked in, when there is one to inherit. */\n seedFrom?: string;\n /**\n * The directory is the harness's own temporary home, to be removed when the run ends. False\n * when the session named its own `harnessOptions.omp.agentHome`, which belongs to the user.\n */\n ephemeral: boolean;\n}\n\n/**\n * Decide the session-private agent directory and whether it inherits the\n * developer's omp state.\n *\n * The directory is deliberately **outside `cwd`**. It holds omp's credential\n * store — a symlink to the developer's `agent.db` on a standalone machine —\n * and `cwd` is the one tree the agent has write access to by design; managed\n * codex draws the same line and rejects a private directory nested in the\n * workspace (`codex/instance-state.ts`). Sessions are keyed by id, falling\n * back to a digest of the workspace path. The temporary directory is `<key>-<random>`, created per\n * call, so no two runs share one (and one run's cleanup never removes another's).\n *\n * Seeding keys on the user directory *existing*, which is exactly the\n * platform/standalone distinction the plan draws: a platform container has no\n * `~/.omp/agent`, so the session correctly starts empty; a developer machine\n * has one, so the session keeps the logins already stored there.\n * `harnessOptions.omp.seedUserAgentDir: false` forces the empty case anyway.\n */\nexport async function planOmpAgentDir(input: OmpAgentDirPlanInput): Promise<OmpAgentDirPlan> {\n const options = input.options;\n const key =\n input.sessionId?.trim() ||\n createHash(\"sha256\").update(path.resolve(input.cwd)).digest(\"hex\").slice(0, 16);\n const ephemeral = options?.agentHome === undefined;\n // The temporary home is unique per spawn: callers key sessions loosely (`skaile chat` passes\n // none, `serve` without --session-id uses one shared id, sub-prompt harnesses share a hash), and\n // the harness deletes its home when its run ends, which must never reach a sibling's home.\n let agentDir = options?.agentHome ?? \"\";\n if (ephemeral) {\n const root = path.join(input.tmpDir ?? os.tmpdir(), \"skaile-omp-agent\");\n await fs.mkdir(root, { recursive: true });\n await sweepStaleOmpHomes(root);\n agentDir = await fs.mkdtemp(path.join(root, `${key.replace(/[^A-Za-z0-9._-]/g, \"_\")}-`));\n await fs.writeFile(path.join(agentDir, OWNER_FILE), String(process.pid));\n }\n if (options?.seedUserAgentDir === false) return { agentDir, ephemeral };\n\n const seedFrom =\n options?.userAgentDir ?? path.join(input.homeDir ?? os.homedir(), \".omp\", \"agent\");\n if (path.resolve(seedFrom) === path.resolve(agentDir)) return { agentDir, ephemeral };\n try {\n if (!(await fs.stat(seedFrom)).isDirectory()) return { agentDir, ephemeral };\n } catch {\n return { agentDir, ephemeral };\n }\n return { agentDir, seedFrom, ephemeral };\n}\n\n/** Names the process that created a temporary home; the sweep keeps a home whose owner lives. */\nconst OWNER_FILE = \".skaile-owner-pid\";\n/** A temporary home older than this whose owner is gone is removed at the next start. */\nconst STALE_HOME_MS = 24 * 60 * 60 * 1000;\n\n/**\n * The names skaile gave a temporary home: `<key>-<six random characters>` (`mkdtemp`), and\n * `<key>-<12 hex>` from releases before 5.16 (p1151).\n */\nconst SKAILE_HOME_NAME = /^[A-Za-z0-9._-]+-(?:[A-Za-z0-9]{6}|[0-9a-f]{12})$/;\n\nfunction processAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n return (err as NodeJS.ErrnoException).code === \"EPERM\";\n }\n}\n\n/**\n * Whether nothing listens on the unix socket `file`: a connect is refused (the process that\n * bound it is gone) or finds no socket. A socket somebody still serves, or one that does not\n * answer within a second, counts as in use.\n */\nfunction socketIsDead(file: string): Promise<boolean> {\n return new Promise<boolean>((resolve) => {\n const conn = net.connect(file);\n const done = (dead: boolean) => {\n conn.destroy();\n resolve(dead);\n };\n conn.setTimeout(1000, () => done(false));\n conn.once(\"connect\", () => done(false));\n conn.once(\"error\", (err: NodeJS.ErrnoException) =>\n done(err.code === \"ECONNREFUSED\" || err.code === \"ENOENT\"),\n );\n });\n}\n\n/**\n * Remove temporary homes older than a day whose creating process is gone: a run that was killed\n * never reached the harness's own cleanup, and every one leaves symlinks and databases behind\n * (p811). A home of a live process is kept whatever its age. A unix socket of that age that\n * nobody listens on is removed too (omp binds one beside the homes and a killed run leaves it,\n * p1316). Best effort: a failure is ignored.\n */\nasync function sweepStaleOmpHomes(root: string, now = Date.now()): Promise<void> {\n let names: string[];\n try {\n names = await fs.readdir(root);\n } catch {\n return;\n }\n await Promise.all(\n names.map(async (name) => {\n const home = path.join(root, name);\n try {\n const stat = await fs.lstat(home);\n if (now - stat.mtimeMs < STALE_HOME_MS) return;\n if (stat.isSocket()) {\n if (await socketIsDead(home)) await fs.rm(home, { force: true });\n return;\n }\n if (!stat.isDirectory()) return;\n const owner = Number.parseInt(\n await fs.readFile(path.join(home, OWNER_FILE), \"utf8\").catch(() => \"\"),\n 10,\n );\n if (Number.isInteger(owner) && processAlive(owner)) return;\n // A home with no owner file is one an older version made: remove it only when its\n // name has the `<key>-<six random characters>` shape `mkdtemp` gives (never a folder\n // somebody else put in this directory).\n if (!Number.isInteger(owner) && !SKAILE_HOME_NAME.test(name)) return;\n await fs.rm(home, { recursive: true, force: true });\n } catch {\n /* another process may be sweeping the same home */\n }\n }),\n );\n}\n\n/**\n * Run the steps that fill a planned home; when one throws, remove a temporary home before\n * rethrowing. The harness only learns the home (and deletes it at cleanup) once these steps\n * return, so a failure here would otherwise leave a new directory per spawn behind.\n */\nasync function removeHomeOnThrow<T>(plan: OmpAgentDirPlan, fill: () => Promise<T>): Promise<T> {\n try {\n return await fill();\n } catch (err) {\n if (plan.ephemeral) await fs.rm(plan.agentDir, { recursive: true, force: true });\n throw err;\n }\n}\n\n/** Remove a path whatever it is, without following a symlink into its target. */\nasync function removeEntry(target: string): Promise<void> {\n let stat: Stats;\n try {\n stat = await fs.lstat(target);\n } catch {\n return;\n }\n if (stat.isSymbolicLink() || stat.isFile()) {\n await fs.unlink(target);\n return;\n }\n await fs.rm(target, { recursive: true, force: true });\n}\n\n/** Symlink every entry of `sourceDir` into `dir`, skipping the owned names. */\nasync function linkInto(\n dir: string,\n sourceDir: string,\n skip: Record<string, true>,\n): Promise<string[]> {\n let names: string[];\n try {\n names = await fs.readdir(sourceDir);\n } catch {\n return [];\n }\n const linked: string[] = [];\n for (const name of names) {\n if (skip[name]) continue;\n const target = path.join(dir, name);\n await removeEntry(target);\n await fs.symlink(path.join(sourceDir, name), target);\n linked.push(name);\n }\n return linked;\n}\n\n/**\n * Remove the symlinks into the user's agent directory that an older release (or a home the\n * session named itself) left in `dir`: only the login files may point there (p811).\n */\nasync function dropStaleSeedLinks(dir: string, sourceDir: string): Promise<void> {\n let names: string[];\n try {\n names = await fs.readdir(dir);\n } catch {\n return;\n }\n const root = path.resolve(sourceDir);\n for (const name of names) {\n if ((OMP_LOGIN_FILES as readonly string[]).includes(name)) continue;\n const entry = path.join(dir, name);\n try {\n if (!(await fs.lstat(entry)).isSymbolicLink()) continue;\n const to = path.resolve(dir, await fs.readlink(entry));\n if (to === root || to.startsWith(root + path.sep)) await fs.unlink(entry);\n } catch {\n // Gone or unreadable: nothing to drop.\n }\n }\n}\n\n/** Symlink the login files (see {@link OMP_LOGIN_FILES}) of the user's agent directory into `dir`. */\nasync function linkLoginFiles(dir: string, sourceDir: string): Promise<string[]> {\n await dropStaleSeedLinks(dir, sourceDir);\n const linked: string[] = [];\n for (const name of OMP_LOGIN_FILES) {\n const from = path.join(sourceDir, name);\n try {\n if (!(await fs.stat(from)).isFile()) continue;\n } catch {\n continue;\n }\n const target = path.join(dir, name);\n await removeEntry(target);\n await fs.symlink(from, target);\n linked.push(name);\n }\n return linked;\n}\n\n/** Read a seeded `config.yml` as the base settings, or `{}` when unusable. */\nasync function readSeededSettings(seedFrom: string): Promise<Record<string, unknown>> {\n for (const name of [\"config.yml\", \"config.yaml\"]) {\n let raw: string;\n try {\n raw = await fs.readFile(path.join(seedFrom, name), \"utf-8\");\n } catch {\n continue;\n }\n const parsed = parse(raw) as unknown;\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n }\n return {};\n}\n\n/**\n * The model selector of the user's own omp default (`modelRoles.default` in their `config.yml`)\n * when it can only be served by their own `models.yml`, else `undefined` (p1172).\n *\n * The session-private home never copies the user's `models.yml`: it holds their credentials.\n * A default such as `anthropic/claude-opus-5-5` that their `models.yml` defines (a gateway, a\n * local server) then names a provider the private home does not have, and omp quietly picks\n * another model. A default on a built-in omp provider keeps working there (its login is linked\n * in), and a provider the registry renders is there too, so neither is reported.\n */\nasync function ownDefaultNeedingOwnModels(\n seedFrom: string,\n rendered: readonly string[],\n): Promise<string | undefined> {\n const selector = (await readSeededSettings(seedFrom)).modelRoles;\n const own =\n selector && typeof selector === \"object\" && !Array.isArray(selector)\n ? (selector as Record<string, unknown>).default\n : undefined;\n if (typeof own !== \"string\" || !own.includes(\"/\")) return undefined;\n const providerId = own.slice(0, own.indexOf(\"/\"));\n if (rendered.includes(providerId)) return undefined;\n let raw: string;\n try {\n raw = await fs.readFile(path.join(seedFrom, OMP_MODELS_FILENAME), \"utf-8\");\n } catch {\n return undefined;\n }\n const providers = (parse(raw) as { providers?: unknown } | null)?.providers;\n if (!providers || typeof providers !== \"object\" || Array.isArray(providers)) return undefined;\n return providerId in providers ? own : undefined;\n}\n\n/**\n * Build the session-private agent directory and write omp's two files into it.\n *\n * The user's `~/.omp/agent/models.yml` and `config.yml` are never opened for\n * writing: `config.yml` is *copied* into the session (so a token refresh or a\n * settings write inside the session cannot reach back through a symlink),\n * `models.yml` is not seeded at all, and everything else — `agent.db*` above\n * all — is symlinked so the session shares the developer's logins and caches.\n */\nexport async function materializeOmpAgentDir(\n agentDir: string,\n registry: AIProviderRegistry | MergedAIProviderRegistry,\n options: MaterializeOmpAgentDirOptions = {},\n): Promise<OmpAgentDirMaterialization> {\n await fs.mkdir(agentDir, { recursive: true });\n\n const seeded = options.seedFrom ? await linkLoginFiles(agentDir, options.seedFrom) : [];\n const identity = options.identityDir\n ? await linkInto(agentDir, options.identityDir, OWNED_AGENT_DIR_FILES)\n : [];\n\n const baseSettings =\n options.baseSettings ??\n (options.seedFrom ? await readSeededSettings(options.seedFrom) : undefined);\n\n const render = renderOmpRegistry(registry, { ...options, baseSettings });\n\n await fs.writeFile(path.join(agentDir, OMP_MODELS_FILENAME), render.modelsYml, \"utf-8\");\n await fs.writeFile(path.join(agentDir, OMP_CONFIG_FILENAME), render.configYml, \"utf-8\");\n\n return { ...render, agentDir, seeded, identity };\n}\n\n/** What the omp harness gets back from {@link materializeOmpProviders}. */\nexport interface OmpProviderMaterialization extends OmpAgentDirMaterialization {\n /**\n * The `--model` argument for this session: `<providerId>/<providerModelId>`,\n * the one selector form omp treats as unambiguous when several providers\n * carry the same model id. Absent when the session named no model.\n */\n modelArg?: string;\n}\n\n/**\n * The whole omp adapter for one session: decide the private agent directory,\n * inherit the developer's omp state where there is any, write `models.yml` and\n * `config.yml`, and report the credential environment and `--model` selector\n * the child needs.\n *\n * Returns `undefined` when the session carries no registry with a selectable\n * entry — the harness then behaves exactly as it did before the registry\n * existed, which is what keeps this an expand-and-contract change.\n *\n * Also returns `undefined` when the session names no model (no `--model`, no\n * `default` role) and the user's own omp default is served by the user's own\n * `models.yml`, which a private home does not carry (p1172): the registry would\n * only swap that default for whatever model omp finds first.\n *\n * Also returns `undefined` when no entry can be handed to omp (every\n * credential unresolved) or when the entry the session's model selects was\n * left out: a session-private agent directory would then start omp without the\n * user's own settings, and a model that only the user's own omp login can\n * serve would fail with `No API key found` although native omp answers. The\n * harness then starts omp on the user's agent directory, so \"omp falls back to\n * its own login\" is true.\n *\n * `env:` references resolve against the runner's environment *overlaid with*\n * `config.env`, so a credential the session boundary already resolved (a\n * `platform:` secret it turned into an env var, for instance) is found here\n * under the name the registry entry points at.\n */\nexport async function materializeOmpProviders(\n config: AgentConfig,\n): Promise<OmpProviderMaterialization | undefined> {\n const registry = config.registry;\n if (!registry) return undefined;\n const selectable = registryEntries(registry).some(\n ([, entry]) => (entry.kind === \"llm\" || entry.kind === \"embed\") && isEntryEnabled(entry),\n );\n if (!selectable) return undefined;\n\n const env = { ...process.env, ...config.env };\n const selected = config.modelRef\n ? parseModelRef(config.modelRef, registry, config.roles)\n : undefined;\n const usable = renderOmpRegistry(registry, {\n env,\n ...(config.roles ? { roles: config.roles } : {}),\n ...(config.modelRef ? { modelRef: config.modelRef } : {}),\n }).providerIds;\n if (usable.length === 0) return undefined;\n if (selected && !usable.includes(selected.providerId)) return undefined;\n\n const plan = await planOmpAgentDir({\n cwd: config.cwd,\n ...(config.sessionId ? { sessionId: config.sessionId } : {}),\n ...(config.harnessOptions?.omp ? { options: config.harnessOptions.omp } : {}),\n });\n\n if (!selected && !config.model && plan.seedFrom) {\n const named = renderOmpRegistry(registry, {\n env,\n ...(config.roles ? { roles: config.roles } : {}),\n }).modelRoles;\n if (named.default === undefined && (await ownDefaultNeedingOwnModels(plan.seedFrom, usable))) {\n if (plan.ephemeral) await fs.rm(plan.agentDir, { recursive: true, force: true });\n return undefined;\n }\n }\n\n const rendered = await removeHomeOnThrow(plan, () =>\n materializeOmpAgentDir(plan.agentDir, registry, {\n env,\n ...(plan.seedFrom ? { seedFrom: plan.seedFrom } : {}),\n ...(config.agentDir ? { identityDir: config.agentDir } : {}),\n ...(config.roles ? { roles: config.roles } : {}),\n ...(config.modelRef ? { modelRef: config.modelRef } : {}),\n }),\n );\n\n return {\n ...rendered,\n ephemeral: plan.ephemeral,\n ...(selected?.providerModelId\n ? { modelArg: `${selected.providerId}/${selected.providerModelId}` }\n : {}),\n };\n}\n\n/** What {@link materializeOmpIdentityDir} wrote. */\nexport interface OmpIdentityDirMaterialization {\n /** The directory `PI_CODING_AGENT_DIR` must point at. */\n agentDir: string;\n /** The directory is the harness's temporary home: remove it when the run ends. */\n ephemeral: boolean;\n /** Names linked in from the user's own omp agent directory. */\n seeded: string[];\n /** Names linked in from `AgentConfig.agentDir`. */\n identity: string[];\n}\n\n/**\n * The omp home of a session that has an agent directory but no provider registry.\n *\n * omp keeps its runtime state (`agent.db`, `models.db`, `skill-descriptions.db`, session\n * scratch, `intercom/`) in `PI_CODING_AGENT_DIR`. Pointing that at the agent directory itself\n * put those files beside `SOUL.md` in a pinned, shared source (`.skaile/cache/agents/<name>/`)\n * and made omp name a path inside the cache when it lacks a credential. So the home is a\n * private directory outside `cwd` (see {@link planOmpAgentDir}): the developer's own omp state\n * is linked in (their login keeps working), their `config.yml` / `models.yml` are copied (a\n * settings write cannot reach back), and the agent directory's entries are linked in last,\n * so the agent files stay where they are and omp can still read them.\n *\n * Returns `undefined` when the session has no `agentDir`.\n */\nexport async function materializeOmpIdentityDir(\n config: AgentConfig,\n): Promise<OmpIdentityDirMaterialization | undefined> {\n const identityDir = config.agentDir;\n if (!identityDir) return undefined;\n // Keyed by the agent and the workspace too: two role children of one session, each with\n // its own agent directory, must not link their entries into one home.\n const agentKey = createHash(\"sha256\")\n .update(`${path.resolve(identityDir)}\\0${path.resolve(config.cwd)}`)\n .digest(\"hex\")\n .slice(0, 12);\n const plan = await planOmpAgentDir({\n cwd: config.cwd,\n sessionId: `${config.sessionId?.trim() || \"agent\"}-${agentKey}`,\n ...(config.harnessOptions?.omp ? { options: config.harnessOptions.omp } : {}),\n });\n return removeHomeOnThrow(plan, async () => {\n await fs.mkdir(plan.agentDir, { recursive: true });\n const seeded = plan.seedFrom ? await linkLoginFiles(plan.agentDir, plan.seedFrom) : [];\n if (plan.seedFrom) {\n for (const name of Object.keys(OWNED_AGENT_DIR_FILES)) {\n const from = path.join(plan.seedFrom, name);\n try {\n if (!(await fs.stat(from)).isFile()) continue;\n } catch {\n continue;\n }\n const to = path.join(plan.agentDir, name);\n await removeEntry(to);\n await fs.copyFile(from, to);\n }\n }\n const identity = await linkInto(plan.agentDir, identityDir, {\n ...OWNED_AGENT_DIR_FILES,\n ...OMP_RUNTIME_ENTRIES,\n });\n return { agentDir: plan.agentDir, ephemeral: plan.ephemeral, seeded, identity };\n });\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { decodeBearerFromHeader, tokensEqual } from './chunk-32NA4TVC.js';
2
- import { listenWithProbe } from './chunk-J5JRGBKK.js';
2
+ import { listenWithProbe } from './chunk-ABVWUM2E.js';
3
3
  import { createServer } from 'http';
4
4
  import { WebSocketServer } from 'ws';
5
5
 
@@ -256,5 +256,5 @@ var WebSocketServerTransport = class {
256
256
  };
257
257
 
258
258
  export { WebSocketServerTransport };
259
- //# sourceMappingURL=chunk-COUJOTAJ.js.map
260
- //# sourceMappingURL=chunk-COUJOTAJ.js.map
259
+ //# sourceMappingURL=chunk-UBLZALKP.js.map
260
+ //# sourceMappingURL=chunk-UBLZALKP.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../transport/src/ws/server.ts"],"names":[],"mappings":"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","file":"chunk-COUJOTAJ.js","sourcesContent":["/**\n * WebSocketServerTransport — `ws`-based WebSocket server.\n *\n * Accepts one client at a time. Implements ServerTransport from @skaile/workspaces/types.\n * Built on the `ws` package + `node:http`, both of which run under Node and Bun.\n */\n\nimport { createServer, type Server as HttpServer } from \"node:http\";\nimport { listenWithProbe } from \"@skaile/workspaces/core\";\nimport type { AgentCommand, AgentEvent, ServerTransport } from \"@skaile/workspaces/types\";\nimport { type WebSocket, WebSocketServer } from \"ws\";\nimport { decodeBearerFromHeader, tokensEqual } from \"./auth.js\";\n\n/** @deprecated Moved to `@skaile/workspaces/core`, where `portIsHeld` replaces it. */\nexport { needsWildcardProbe } from \"@skaile/workspaces/core\";\n\n/**\n * Options for {@link WebSocketServerTransport}.\n *\n * @param port - TCP port to bind to; `0` picks a free port (read it back via `boundPort`).\n * @param onLog - Optional callback for internal log lines (client connect/disconnect events).\n * @param host - Address to bind. Default: every interface (Node's unspecified address).\n * `127.0.0.1` accepts local clients only.\n * @param authToken - When set, every upgrade must present this token via the\n * `skaile-bearer.<base64url>` subprotocol or the handshake is rejected with\n * HTTP 401. When unset, no auth is enforced (back-compatible default).\n * @param onListening - Called once the socket is bound, with the address a client\n * connects to (`url`) and whether it needs a token. For a plain-text ready line\n * (the `onLog` record carries the port only inside a log sentence).\n * @param heartbeatMs - Interval in milliseconds between pings to the active\n * client. Default: 30 000.\n * @param maxMissedPongs - Heartbeat ticks the client may go without a pong (or\n * any inbound frame) before its socket is terminated, so a client lost behind a\n * dead path (no FIN) fires `onDisconnect` instead of swallowing every event\n * sent to it. Default: 2, i.e. detection within 3 x `heartbeatMs`. `0` turns\n * the heartbeat off. Every WebSocket client answers pings on its own.\n *\n * @docLink packages/transport/dev-guide#websocket-server-transport\n */\nexport interface WebSocketServerOptions {\n port: number;\n host?: string;\n onLog?: (line: string) => void;\n authToken?: string;\n onListening?: (info: { url: string; port: number; tokenRequired: boolean }) => void;\n heartbeatMs?: number;\n maxMissedPongs?: number;\n}\n\nconst DEFAULT_HEARTBEAT_MS = 30_000;\nconst DEFAULT_MAX_MISSED_PONGS = 2;\n\n/**\n * Shape-validate a `session_init` frame at ingress before forwarding to the\n * runner. Protocol v3 requires the deterministic bootstrap envelope to carry\n * an identity tuple, a semver-shaped `protocolVersion`, a resolved config\n * object, and a `credentials` map keyed by `mounts` / `connectors`. Frames\n * that fail the guard are rejected on the wire so the runner never sees a\n * malformed boot envelope.\n *\n * Returns `null` when the frame is well-formed, or a human-readable reason\n * when it should be rejected.\n *\n * Spec: `_devlog/specs/2026-05-10-deterministic-session-bootstrap.md`\n * § \"SessionInitCommand schema\".\n *\n * @internal\n */\nfunction validateSessionInitShape(cmd: Record<string, unknown>): string | null {\n if (cmd.type !== \"session_init\") return \"type must be 'session_init'\";\n\n const identity = cmd.identity as Record<string, unknown> | undefined;\n if (!identity || typeof identity !== \"object\" || Array.isArray(identity)) {\n return \"identity must be an object\";\n }\n if (typeof identity.containerId !== \"string\") return \"identity.containerId must be a string\";\n if (typeof identity.sessionId !== \"string\") return \"identity.sessionId must be a string\";\n if (typeof identity.projectId !== \"string\") return \"identity.projectId must be a string\";\n\n const protocolVersion = cmd.protocolVersion as Record<string, unknown> | undefined;\n if (!protocolVersion || typeof protocolVersion !== \"object\" || Array.isArray(protocolVersion)) {\n return \"protocolVersion must be an object\";\n }\n if (typeof protocolVersion.major !== \"number\") return \"protocolVersion.major must be a number\";\n if (typeof protocolVersion.minor !== \"number\") return \"protocolVersion.minor must be a number\";\n if (typeof protocolVersion.patch !== \"number\") return \"protocolVersion.patch must be a number\";\n\n if (\n !cmd.resolvedConfig ||\n typeof cmd.resolvedConfig !== \"object\" ||\n Array.isArray(cmd.resolvedConfig)\n ) {\n return \"resolvedConfig must be an object\";\n }\n\n if (!cmd.credentials || typeof cmd.credentials !== \"object\" || Array.isArray(cmd.credentials)) {\n return \"credentials must be an object\";\n }\n\n return null;\n}\n\n/**\n * `ServerTransport` implementation backed by the `ws` package on top of a\n * `node:http` server.\n *\n * Enforces a single-client policy: if a new connection arrives while a client is\n * already connected, the stale connection is closed with code 4001 and replaced.\n * Incoming frames are parsed as JSON `AgentCommand` messages and fanned out to\n * registered `onCommand` handlers; outgoing `AgentEvent` objects are JSON-serialised\n * before sending. Call `listen()` to start accepting connections and `close()` to\n * shut down cleanly — `close()` terminates the active socket immediately rather\n * than keeping the event loop alive.\n *\n * @example\n * ```ts\n * const server = new WebSocketServerTransport({ port: 8080 });\n * await server.listen();\n * server.onCommand((cmd) => console.log(cmd));\n * server.send({ type: 'agent_end' });\n * await server.close();\n * ```\n *\n * @docLink packages/transport/dev-guide#websocket-server-transport\n */\nexport class WebSocketServerTransport implements ServerTransport {\n private httpServer: HttpServer | null = null;\n private wss: WebSocketServer | null = null;\n private activeClient: WebSocket | null = null;\n private readonly port: number;\n private readonly log: (line: string) => void;\n private readonly host?: string;\n private readonly authToken?: string;\n private readonly heartbeatMs: number;\n private readonly maxMissedPongs: number;\n\n private readonly onListening: WebSocketServerOptions[\"onListening\"];\n private readonly commandHandlers = new Set<(command: AgentCommand) => void>();\n private readonly connectHandlers = new Set<() => void>();\n private readonly disconnectHandlers = new Set<() => void>();\n\n constructor(options: WebSocketServerOptions) {\n this.port = options.port;\n this.log = options.onLog ?? console.log;\n this.host = options.host;\n this.authToken = options.authToken;\n this.onListening = options.onListening;\n this.heartbeatMs = options.heartbeatMs ?? DEFAULT_HEARTBEAT_MS;\n this.maxMissedPongs = options.maxMissedPongs ?? DEFAULT_MAX_MISSED_PONGS;\n }\n\n /** Start the server and accept connections. */\n async listen(): Promise<void> {\n const httpServer = createServer((_req, res) => {\n res.writeHead(200, { \"content-type\": \"text/plain\" });\n res.end(\"skaile agent server\");\n });\n // When an auth token is configured, reject the upgrade at handshake time\n // (HTTP 401) unless the client presents the matching bearer subprotocol.\n // Rejecting here — before `connection` — keeps unauthenticated sockets from\n // ever reaching the command handlers.\n const expected = this.authToken;\n const wss = new WebSocketServer({\n server: httpServer,\n verifyClient: expected\n ? (info, cb) => {\n const presented = decodeBearerFromHeader(info.req.headers[\"sec-websocket-protocol\"]);\n if (presented !== null && tokensEqual(presented, expected)) {\n cb(true);\n } else {\n this.log(\"[ws-server] rejecting unauthenticated upgrade\");\n cb(false, 401, \"Unauthorized\");\n }\n }\n : undefined,\n });\n wss.on(\"connection\", (ws) => this.handleConnection(ws));\n // `ws` re-emits the http server's errors on `wss`; unhandled during the bind,\n // that throws before the reject below runs and `listen()` hangs. Scoped to the\n // bind so a post-startup server error still surfaces loudly.\n const swallowBindError = () => {};\n wss.on(\"error\", swallowBindError);\n this.httpServer = httpServer;\n this.wss = wss;\n\n try {\n await listenWithProbe(httpServer, this.port, this.host);\n } catch (err) {\n wss.close();\n this.wss = null;\n this.httpServer = null;\n if (err instanceof Error && \"code\" in err && err.code === \"EADDRINUSE\") {\n const where = this.host ? `${this.host}:${this.port}` : `port ${this.port}`;\n throw Object.assign(\n new Error(\n `${where} is already in use: another process listens there. Pick another port.`,\n ),\n { code: \"EADDRINUSE\" },\n );\n }\n throw err;\n } finally {\n wss.off(\"error\", swallowBindError);\n }\n\n const addr = httpServer.address();\n const boundPort = typeof addr === \"object\" && addr ? addr.port : this.port;\n const bind = this.host ?? \"all interfaces\";\n this.log(\n `[ws-server] listening on port ${boundPort} (${bind}; ${this.authToken ? \"token required\" : \"no authentication\"})`,\n );\n // A wildcard bind is reachable on loopback; that is the URL worth printing.\n const urlHost =\n !this.host || this.host === \"0.0.0.0\" || this.host === \"::\"\n ? \"127.0.0.1\"\n : this.host.includes(\":\") && !this.host.startsWith(\"[\")\n ? `[${this.host}]`\n : this.host;\n this.onListening?.({\n url: `ws://${urlHost}:${boundPort}`,\n port: boundPort,\n tokenRequired: this.authToken !== undefined,\n });\n }\n\n private handleConnection(ws: WebSocket): void {\n if (this.activeClient) {\n // Close the stale connection instead of rejecting the new one.\n // This handles the race where waitForWsReady's probe hasn't\n // fully disconnected before the real gateway client connects.\n this.log(\"[ws-server] replacing previous connection\");\n try {\n this.activeClient.close(4001, \"Replaced by new connection\");\n } catch {\n // already closed\n }\n }\n this.log(\"[ws-server] client connected\");\n this.activeClient = ws;\n for (const handler of this.connectHandlers) {\n handler();\n }\n\n const stopHeartbeat = this.startHeartbeat(ws);\n\n ws.on(\"message\", (data) => {\n try {\n const parsed = JSON.parse(String(data)) as Record<string, unknown>;\n // Protocol v3: shape-validate `session_init` at ingress so the\n // runner never receives a malformed boot envelope. Other command\n // shapes pass through unchanged — invalid `AgentCommand`s surface\n // through the existing per-command handlers in serve.ts.\n if (parsed && typeof parsed === \"object\" && parsed.type === \"session_init\") {\n const reason = validateSessionInitShape(parsed);\n if (reason !== null) {\n this.log(`[ws-server] rejecting invalid session_init: ${reason}`);\n this.sendRaw({\n type: \"error\",\n message: `Invalid session_init: ${reason}`,\n fatal: false,\n });\n return;\n }\n }\n const cmd = parsed as unknown as AgentCommand;\n for (const handler of this.commandHandlers) {\n handler(cmd);\n }\n } catch (err) {\n this.sendRaw({\n type: \"error\",\n message: `Invalid message: ${err instanceof Error ? err.message : String(err)}`,\n fatal: false,\n });\n }\n });\n\n ws.on(\"close\", () => {\n stopHeartbeat();\n this.log(\"[ws-server] client disconnected\");\n if (this.activeClient === ws) {\n this.activeClient = null;\n for (const handler of this.disconnectHandlers) {\n handler();\n }\n }\n });\n }\n\n /**\n * Ping `ws` every {@link heartbeatMs}; terminate it after\n * {@link maxMissedPongs} unanswered ticks. `terminate()` makes `ws` emit\n * `close`, which runs the normal disconnect path. Returns the stop function.\n */\n private startHeartbeat(ws: WebSocket): () => void {\n if (this.maxMissedPongs <= 0) return () => {};\n let missed = 0;\n const markAlive = () => {\n missed = 0;\n };\n ws.on(\"pong\", markAlive);\n ws.on(\"message\", markAlive);\n const timer = setInterval(() => {\n if (ws.readyState !== ws.OPEN) return;\n if (missed >= this.maxMissedPongs) {\n this.log(\n `[ws-server] no pong for ${missed} heartbeats of ${this.heartbeatMs}ms; terminating client`,\n );\n clearInterval(timer);\n ws.terminate();\n return;\n }\n missed++;\n try {\n ws.ping();\n } catch {\n // socket closing underneath us; the close event cleans up\n }\n }, this.heartbeatMs);\n timer.unref?.();\n return () => clearInterval(timer);\n }\n\n /**\n * Stop the server.\n *\n * Terminates the active socket immediately — otherwise a still-connected\n * client would keep the event loop alive and block process shutdown.\n */\n async close(): Promise<void> {\n try {\n this.activeClient?.terminate();\n } catch {\n // already closed\n }\n this.activeClient = null;\n this.wss?.close();\n this.wss = null;\n const httpServer = this.httpServer;\n this.httpServer = null;\n if (httpServer) {\n httpServer.closeAllConnections();\n await new Promise<void>((resolve) => httpServer.close(() => resolve()));\n }\n }\n\n send(event: AgentEvent): void {\n this.sendRaw(event);\n }\n\n onCommand(handler: (command: AgentCommand) => void): () => void {\n this.commandHandlers.add(handler);\n return () => {\n this.commandHandlers.delete(handler);\n };\n }\n\n onConnect(handler: () => void): () => void {\n this.connectHandlers.add(handler);\n return () => {\n this.connectHandlers.delete(handler);\n };\n }\n\n onDisconnect(handler: () => void): () => void {\n this.disconnectHandlers.add(handler);\n return () => {\n this.disconnectHandlers.delete(handler);\n };\n }\n\n get connected(): boolean {\n return this.activeClient !== null;\n }\n\n /** The port actually bound by `listen()` (resolves `port: 0`); `undefined` when not listening. */\n get boundPort(): number | undefined {\n const addr = this.httpServer?.address();\n return typeof addr === \"object\" && addr ? addr.port : undefined;\n }\n\n private sendRaw(data: unknown): void {\n try {\n const client = this.activeClient;\n if (client && client.readyState === 1) {\n client.send(JSON.stringify(data));\n }\n } catch {\n // Silently ignore send failures\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../transport/src/ws/server.ts"],"names":[],"mappings":"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","file":"chunk-UBLZALKP.js","sourcesContent":["/**\n * WebSocketServerTransport — `ws`-based WebSocket server.\n *\n * Accepts one client at a time. Implements ServerTransport from @skaile/workspaces/types.\n * Built on the `ws` package + `node:http`, both of which run under Node and Bun.\n */\n\nimport { createServer, type Server as HttpServer } from \"node:http\";\nimport { listenWithProbe } from \"@skaile/workspaces/core\";\nimport type { AgentCommand, AgentEvent, ServerTransport } from \"@skaile/workspaces/types\";\nimport { type WebSocket, WebSocketServer } from \"ws\";\nimport { decodeBearerFromHeader, tokensEqual } from \"./auth.js\";\n\n/** @deprecated Moved to `@skaile/workspaces/core`, where `portIsHeld` replaces it. */\nexport { needsWildcardProbe } from \"@skaile/workspaces/core\";\n\n/**\n * Options for {@link WebSocketServerTransport}.\n *\n * @param port - TCP port to bind to; `0` picks a free port (read it back via `boundPort`).\n * @param onLog - Optional callback for internal log lines (client connect/disconnect events).\n * @param host - Address to bind. Default: every interface (Node's unspecified address).\n * `127.0.0.1` accepts local clients only.\n * @param authToken - When set, every upgrade must present this token via the\n * `skaile-bearer.<base64url>` subprotocol or the handshake is rejected with\n * HTTP 401. When unset, no auth is enforced (back-compatible default).\n * @param onListening - Called once the socket is bound, with the address a client\n * connects to (`url`) and whether it needs a token. For a plain-text ready line\n * (the `onLog` record carries the port only inside a log sentence).\n * @param heartbeatMs - Interval in milliseconds between pings to the active\n * client. Default: 30 000.\n * @param maxMissedPongs - Heartbeat ticks the client may go without a pong (or\n * any inbound frame) before its socket is terminated, so a client lost behind a\n * dead path (no FIN) fires `onDisconnect` instead of swallowing every event\n * sent to it. Default: 2, i.e. detection within 3 x `heartbeatMs`. `0` turns\n * the heartbeat off. Every WebSocket client answers pings on its own.\n *\n * @docLink packages/transport/dev-guide#websocket-server-transport\n */\nexport interface WebSocketServerOptions {\n port: number;\n host?: string;\n onLog?: (line: string) => void;\n authToken?: string;\n onListening?: (info: { url: string; port: number; tokenRequired: boolean }) => void;\n heartbeatMs?: number;\n maxMissedPongs?: number;\n}\n\nconst DEFAULT_HEARTBEAT_MS = 30_000;\nconst DEFAULT_MAX_MISSED_PONGS = 2;\n\n/**\n * Shape-validate a `session_init` frame at ingress before forwarding to the\n * runner. Protocol v3 requires the deterministic bootstrap envelope to carry\n * an identity tuple, a semver-shaped `protocolVersion`, a resolved config\n * object, and a `credentials` map keyed by `mounts` / `connectors`. Frames\n * that fail the guard are rejected on the wire so the runner never sees a\n * malformed boot envelope.\n *\n * Returns `null` when the frame is well-formed, or a human-readable reason\n * when it should be rejected.\n *\n * Spec: `_devlog/specs/2026-05-10-deterministic-session-bootstrap.md`\n * § \"SessionInitCommand schema\".\n *\n * @internal\n */\nfunction validateSessionInitShape(cmd: Record<string, unknown>): string | null {\n if (cmd.type !== \"session_init\") return \"type must be 'session_init'\";\n\n const identity = cmd.identity as Record<string, unknown> | undefined;\n if (!identity || typeof identity !== \"object\" || Array.isArray(identity)) {\n return \"identity must be an object\";\n }\n if (typeof identity.containerId !== \"string\") return \"identity.containerId must be a string\";\n if (typeof identity.sessionId !== \"string\") return \"identity.sessionId must be a string\";\n if (typeof identity.projectId !== \"string\") return \"identity.projectId must be a string\";\n\n const protocolVersion = cmd.protocolVersion as Record<string, unknown> | undefined;\n if (!protocolVersion || typeof protocolVersion !== \"object\" || Array.isArray(protocolVersion)) {\n return \"protocolVersion must be an object\";\n }\n if (typeof protocolVersion.major !== \"number\") return \"protocolVersion.major must be a number\";\n if (typeof protocolVersion.minor !== \"number\") return \"protocolVersion.minor must be a number\";\n if (typeof protocolVersion.patch !== \"number\") return \"protocolVersion.patch must be a number\";\n\n if (\n !cmd.resolvedConfig ||\n typeof cmd.resolvedConfig !== \"object\" ||\n Array.isArray(cmd.resolvedConfig)\n ) {\n return \"resolvedConfig must be an object\";\n }\n\n if (!cmd.credentials || typeof cmd.credentials !== \"object\" || Array.isArray(cmd.credentials)) {\n return \"credentials must be an object\";\n }\n\n return null;\n}\n\n/**\n * `ServerTransport` implementation backed by the `ws` package on top of a\n * `node:http` server.\n *\n * Enforces a single-client policy: if a new connection arrives while a client is\n * already connected, the stale connection is closed with code 4001 and replaced.\n * Incoming frames are parsed as JSON `AgentCommand` messages and fanned out to\n * registered `onCommand` handlers; outgoing `AgentEvent` objects are JSON-serialised\n * before sending. Call `listen()` to start accepting connections and `close()` to\n * shut down cleanly — `close()` terminates the active socket immediately rather\n * than keeping the event loop alive.\n *\n * @example\n * ```ts\n * const server = new WebSocketServerTransport({ port: 8080 });\n * await server.listen();\n * server.onCommand((cmd) => console.log(cmd));\n * server.send({ type: 'agent_end' });\n * await server.close();\n * ```\n *\n * @docLink packages/transport/dev-guide#websocket-server-transport\n */\nexport class WebSocketServerTransport implements ServerTransport {\n private httpServer: HttpServer | null = null;\n private wss: WebSocketServer | null = null;\n private activeClient: WebSocket | null = null;\n private readonly port: number;\n private readonly log: (line: string) => void;\n private readonly host?: string;\n private readonly authToken?: string;\n private readonly heartbeatMs: number;\n private readonly maxMissedPongs: number;\n\n private readonly onListening: WebSocketServerOptions[\"onListening\"];\n private readonly commandHandlers = new Set<(command: AgentCommand) => void>();\n private readonly connectHandlers = new Set<() => void>();\n private readonly disconnectHandlers = new Set<() => void>();\n\n constructor(options: WebSocketServerOptions) {\n this.port = options.port;\n this.log = options.onLog ?? console.log;\n this.host = options.host;\n this.authToken = options.authToken;\n this.onListening = options.onListening;\n this.heartbeatMs = options.heartbeatMs ?? DEFAULT_HEARTBEAT_MS;\n this.maxMissedPongs = options.maxMissedPongs ?? DEFAULT_MAX_MISSED_PONGS;\n }\n\n /** Start the server and accept connections. */\n async listen(): Promise<void> {\n const httpServer = createServer((_req, res) => {\n res.writeHead(200, { \"content-type\": \"text/plain\" });\n res.end(\"skaile agent server\");\n });\n // When an auth token is configured, reject the upgrade at handshake time\n // (HTTP 401) unless the client presents the matching bearer subprotocol.\n // Rejecting here — before `connection` — keeps unauthenticated sockets from\n // ever reaching the command handlers.\n const expected = this.authToken;\n const wss = new WebSocketServer({\n server: httpServer,\n verifyClient: expected\n ? (info, cb) => {\n const presented = decodeBearerFromHeader(info.req.headers[\"sec-websocket-protocol\"]);\n if (presented !== null && tokensEqual(presented, expected)) {\n cb(true);\n } else {\n this.log(\"[ws-server] rejecting unauthenticated upgrade\");\n cb(false, 401, \"Unauthorized\");\n }\n }\n : undefined,\n });\n wss.on(\"connection\", (ws) => this.handleConnection(ws));\n // `ws` re-emits the http server's errors on `wss`; unhandled during the bind,\n // that throws before the reject below runs and `listen()` hangs. Scoped to the\n // bind so a post-startup server error still surfaces loudly.\n const swallowBindError = () => {};\n wss.on(\"error\", swallowBindError);\n this.httpServer = httpServer;\n this.wss = wss;\n\n try {\n await listenWithProbe(httpServer, this.port, this.host);\n } catch (err) {\n wss.close();\n this.wss = null;\n this.httpServer = null;\n if (err instanceof Error && \"code\" in err && err.code === \"EADDRINUSE\") {\n const where = this.host ? `${this.host}:${this.port}` : `port ${this.port}`;\n throw Object.assign(\n new Error(\n `${where} is already in use: another process listens there. Pick another port.`,\n ),\n { code: \"EADDRINUSE\" },\n );\n }\n throw err;\n } finally {\n wss.off(\"error\", swallowBindError);\n }\n\n const addr = httpServer.address();\n const boundPort = typeof addr === \"object\" && addr ? addr.port : this.port;\n const bind = this.host ?? \"all interfaces\";\n this.log(\n `[ws-server] listening on port ${boundPort} (${bind}; ${this.authToken ? \"token required\" : \"no authentication\"})`,\n );\n // A wildcard bind is reachable on loopback; that is the URL worth printing.\n const urlHost =\n !this.host || this.host === \"0.0.0.0\" || this.host === \"::\"\n ? \"127.0.0.1\"\n : this.host.includes(\":\") && !this.host.startsWith(\"[\")\n ? `[${this.host}]`\n : this.host;\n this.onListening?.({\n url: `ws://${urlHost}:${boundPort}`,\n port: boundPort,\n tokenRequired: this.authToken !== undefined,\n });\n }\n\n private handleConnection(ws: WebSocket): void {\n if (this.activeClient) {\n // Close the stale connection instead of rejecting the new one.\n // This handles the race where waitForWsReady's probe hasn't\n // fully disconnected before the real gateway client connects.\n this.log(\"[ws-server] replacing previous connection\");\n try {\n this.activeClient.close(4001, \"Replaced by new connection\");\n } catch {\n // already closed\n }\n }\n this.log(\"[ws-server] client connected\");\n this.activeClient = ws;\n for (const handler of this.connectHandlers) {\n handler();\n }\n\n const stopHeartbeat = this.startHeartbeat(ws);\n\n ws.on(\"message\", (data) => {\n try {\n const parsed = JSON.parse(String(data)) as Record<string, unknown>;\n // Protocol v3: shape-validate `session_init` at ingress so the\n // runner never receives a malformed boot envelope. Other command\n // shapes pass through unchanged — invalid `AgentCommand`s surface\n // through the existing per-command handlers in serve.ts.\n if (parsed && typeof parsed === \"object\" && parsed.type === \"session_init\") {\n const reason = validateSessionInitShape(parsed);\n if (reason !== null) {\n this.log(`[ws-server] rejecting invalid session_init: ${reason}`);\n this.sendRaw({\n type: \"error\",\n message: `Invalid session_init: ${reason}`,\n fatal: false,\n });\n return;\n }\n }\n const cmd = parsed as unknown as AgentCommand;\n for (const handler of this.commandHandlers) {\n handler(cmd);\n }\n } catch (err) {\n this.sendRaw({\n type: \"error\",\n message: `Invalid message: ${err instanceof Error ? err.message : String(err)}`,\n fatal: false,\n });\n }\n });\n\n ws.on(\"close\", () => {\n stopHeartbeat();\n this.log(\"[ws-server] client disconnected\");\n if (this.activeClient === ws) {\n this.activeClient = null;\n for (const handler of this.disconnectHandlers) {\n handler();\n }\n }\n });\n }\n\n /**\n * Ping `ws` every {@link heartbeatMs}; terminate it after\n * {@link maxMissedPongs} unanswered ticks. `terminate()` makes `ws` emit\n * `close`, which runs the normal disconnect path. Returns the stop function.\n */\n private startHeartbeat(ws: WebSocket): () => void {\n if (this.maxMissedPongs <= 0) return () => {};\n let missed = 0;\n const markAlive = () => {\n missed = 0;\n };\n ws.on(\"pong\", markAlive);\n ws.on(\"message\", markAlive);\n const timer = setInterval(() => {\n if (ws.readyState !== ws.OPEN) return;\n if (missed >= this.maxMissedPongs) {\n this.log(\n `[ws-server] no pong for ${missed} heartbeats of ${this.heartbeatMs}ms; terminating client`,\n );\n clearInterval(timer);\n ws.terminate();\n return;\n }\n missed++;\n try {\n ws.ping();\n } catch {\n // socket closing underneath us; the close event cleans up\n }\n }, this.heartbeatMs);\n timer.unref?.();\n return () => clearInterval(timer);\n }\n\n /**\n * Stop the server.\n *\n * Terminates the active socket immediately — otherwise a still-connected\n * client would keep the event loop alive and block process shutdown.\n */\n async close(): Promise<void> {\n try {\n this.activeClient?.terminate();\n } catch {\n // already closed\n }\n this.activeClient = null;\n this.wss?.close();\n this.wss = null;\n const httpServer = this.httpServer;\n this.httpServer = null;\n if (httpServer) {\n httpServer.closeAllConnections();\n await new Promise<void>((resolve) => httpServer.close(() => resolve()));\n }\n }\n\n send(event: AgentEvent): void {\n this.sendRaw(event);\n }\n\n onCommand(handler: (command: AgentCommand) => void): () => void {\n this.commandHandlers.add(handler);\n return () => {\n this.commandHandlers.delete(handler);\n };\n }\n\n onConnect(handler: () => void): () => void {\n this.connectHandlers.add(handler);\n return () => {\n this.connectHandlers.delete(handler);\n };\n }\n\n onDisconnect(handler: () => void): () => void {\n this.disconnectHandlers.add(handler);\n return () => {\n this.disconnectHandlers.delete(handler);\n };\n }\n\n get connected(): boolean {\n return this.activeClient !== null;\n }\n\n /** The port actually bound by `listen()` (resolves `port: 0`); `undefined` when not listening. */\n get boundPort(): number | undefined {\n const addr = this.httpServer?.address();\n return typeof addr === \"object\" && addr ? addr.port : undefined;\n }\n\n private sendRaw(data: unknown): void {\n try {\n const client = this.activeClient;\n if (client && client.readyState === 1) {\n client.send(JSON.stringify(data));\n }\n } catch {\n // Silently ignore send failures\n }\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { declaresActivityLabel, stripActivityLabel } from './chunk-JCPD4QRM.js';
1
+ import { declaresActivityLabel, stripActivityLabel } from './chunk-VZU67OKK.js';
2
2
  import { randomUUID } from 'crypto';
3
3
 
4
4
  async function dispatchCapability(hooks, name, input, onBackgroundError = () => {
@@ -52,5 +52,5 @@ function filterRenderCapabilities(caps) {
52
52
  }
53
53
 
54
54
  export { dispatchCapability, filterRenderCapabilities, renderFallback };
55
- //# sourceMappingURL=chunk-BIRW3HOG.js.map
56
- //# sourceMappingURL=chunk-BIRW3HOG.js.map
55
+ //# sourceMappingURL=chunk-UUKCPPTQ.js.map
56
+ //# sourceMappingURL=chunk-UUKCPPTQ.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../bridge/src/capability-dispatch.ts"],"names":[],"mappings":";;;AAgFA,eAAsB,kBAAA,CACpB,KAAA,EACA,IAAA,EACA,KAAA,EACA,oBAA4D,MAAM;AAAC,CAAA,EACnE,MAAA,EACmC;AACnC,EAAA,MAAA,EAAQ,cAAA,EAAe;AACvB,EAAA,MAAM,SAAS,UAAA,EAAW;AAC1B,EAAA,MAAM,GAAA,GAAM,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA;AAI9B,EAAA,IAAI,CAAC,qBAAA,CAAsB,GAAA,EAAK,WAAW,CAAA,EAAG,KAAA,GAAQ,mBAAmB,KAAK,CAAA;AAK9E,EAAA,IAAI,GAAA,EAAK,MAAA,IAAU,KAAA,CAAM,UAAA,EAAY;AACnC,IAAA,KAAA,CAAM,UAAA,CAAW;AAAA,MACf,IAAA,EAAM,gBAAA;AAAA,MACN,cAAA,EAAgB,IAAA;AAAA,MAChB,KAAA,EAAO,KAAA;AAAA,MACP;AAAA,KACD,CAAA;AACD,IAAA,IAAI,GAAA,CAAI,MAAA,CAAO,QAAA,IAAY,KAAA,CAAM,QAAA,EAAU;AACzC,MAAA,MAAM,QAAA,GAAW,cAAA,CAAe,GAAA,CAAI,MAAA,CAAO,UAAU,KAAK,CAAA;AAC1D,MAAA,KAAA,CAAM,SAAS,QAAQ,CAAA;AAAA,IACzB;AAAA,EACF;AAEA,EAAA,IAAI,KAAK,aAAA,EAAe;AAEtB,IAAA,KAAK,KAAA,CAAM,MAAA,CAAO,IAAA,EAAM,KAAA,EAAO,MAAA,EAAQ,MAAM,CAAA,CAAE,KAAA,CAAM,CAAC,GAAA,KAAQ,iBAAA,CAAkB,GAAA,EAAK,MAAM,CAAC,CAAA;AAC5F,IAAA,OAAO,EAAE,MAAA,EAAQ,EAAC,EAAG,UAAA,EAAY,MAAM,MAAA,EAAO;AAAA,EAChD;AAEA,EAAA,MAAM,SAAS,MAAM,KAAA,CAAM,OAAO,IAAA,EAAM,KAAA,EAAO,QAAQ,MAAM,CAAA;AAC7D,EAAA,OAAO,EAAE,MAAA,EAAQ,UAAA,EAAY,KAAA,EAAO,MAAA,EAAO;AAC7C;AAqBO,SAAS,cAAA,CAAe,UAAkB,KAAA,EAAwB;AACvE,EAAA,IAAI,CAAC,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,EAAU;AACvC,IAAA,OAAO,QAAA,CAAS,OAAA,CAAQ,wBAAA,EAA0B,EAAE,CAAA;AAAA,EACtD;AACA,EAAA,MAAM,KAAA,GAAQ,KAAA;AACd,EAAA,OAAO,QAAA,CAAS,OAAA,CAAQ,0BAAA,EAA4B,CAAC,QAAQ,IAAA,KAAiB;AAC5E,IAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,KAAA,EAAO,IAAI,CAAA;AAClC,IAAA,OAAO,UAAU,MAAA,IAAa,KAAA,KAAU,IAAA,GAAO,EAAA,GAAK,OAAO,KAAK,CAAA;AAAA,EAClE,CAAC,CAAA;AACH;AAEA,SAAS,QAAA,CAAS,KAA8B,IAAA,EAAuB;AACrE,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AAC5B,EAAA,IAAI,OAAA,GAAmB,GAAA;AACvB,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,IAAI,YAAY,IAAA,IAAQ,OAAA,KAAY,MAAA,IAAa,OAAO,YAAY,QAAA,EAAU;AAC5E,MAAA,OAAO,MAAA;AAAA,IACT;AACA,IAAA,OAAA,GAAW,QAAoC,IAAI,CAAA;AAAA,EACrD;AACA,EAAA,OAAO,OAAA;AACT;AAYO,SAAS,yBAAyB,IAAA,EAAkC;AACzE,EAAA,OAAO,KAAK,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,WAAW,MAAS,CAAA;AAClD","file":"chunk-BIRW3HOG.js","sourcesContent":["/**\n * Capability dispatch helpers for bridge harnesses.\n *\n * The runner builds a {@link BridgeCapabilityHooks} bundle from its\n * `CapabilityRegistry` and threads it through `AgentConfig.capabilities`.\n * Harnesses consume the bundle via these helpers without taking a runtime\n * dependency on `@skaile/workspaces/runner` (which would create a circular dep).\n *\n * Three behaviours are layered into a single dispatch path:\n *\n * 1. **Standard tool call** — the LLM invokes a capability; the bridge calls\n * `hooks.invoke()`, awaits the result, and returns it to the LLM.\n * 2. **Fire-and-forget** — when the resolved Capability has `fireAndForget`\n * set, the bridge resolves to `{}` immediately and runs the handler in\n * the background. Background errors are logged but never surfaced to the\n * LLM since the call is already settled.\n * 3. **Render capability** — when the resolved Capability has a non-undefined\n * `render`, the bridge emits `RenderInvokedEvent` to the runner via\n * `hooks.emitRender` and (when `render.fallback` is set) a parallel text\n * event via `hooks.emitText` with simple `{{prop}}` substitution. The\n * placeholder convention follows the v2 spec — top-level keys of `props`\n * only, with optional dotted-path traversal (`{{user.name}}`). The legacy\n * AAP `{{props.x}}` form was retired in Phase 6.\n *\n * @category Capabilities\n * @since 2.0.0\n */\nimport { randomUUID } from \"node:crypto\";\nimport {\n type Capability,\n declaresActivityLabel,\n stripActivityLabel,\n} from \"@skaile/workspaces/types\";\nimport type { BridgeCapabilityHooks } from \"./types.js\";\n\n/**\n * Result of a capability dispatch returned by {@link dispatchCapability}.\n *\n * The bridge feeds `{ result }` back to the LLM tool-use response. For\n * fire-and-forget capabilities `result` is always `{}` and `background` is `true`.\n *\n * @docLink packages/bridge/concepts#capability-dispatch-result\n */\nexport interface CapabilityDispatchResult {\n /** Value returned to the LLM. `{}` for fire-and-forget capabilities. */\n result: unknown;\n /** True when the handler is still running in the background (fire-and-forget). */\n background: boolean;\n /** The per-dispatch call id minted for this invocation (see {@link dispatchCapability}). */\n callId: string;\n}\n\n/**\n * Run a capability invocation through the runner-provided hooks, applying\n * render-event emission and fire-and-forget short-circuit.\n *\n * The result is always shaped for direct return to the LLM. For\n * fire-and-forget capabilities the background promise is fired and any error\n * is logged through `onBackgroundError` (defaulting to a no-op). For all\n * other capabilities the handler is awaited; errors propagate as a rejection\n * so the harness can surface them as tool errors.\n *\n * **Dispatch call id.** Every dispatch mints a fresh `randomUUID()` and uses it\n * for `render_invoked.callId`, `hooks.invoke`'s `callId` and — for client\n * capabilities — the outbound `capability_invoked.callId`. It is deliberately\n * NOT the harness's tool-call id: those are only as unique as the model\n * provider makes them (some harnesses emit `call_0` on every turn), and the\n * runner's pending-call map is keyed by this id, so a late `capability_result`\n * for a timed-out call must never be able to resolve a later call that reused\n * the provider's id. Harnesses map results back to their tool calls through the\n * returned value, never through this id.\n *\n * @param hooks - Capability dispatch bundle from `AgentConfig.capabilities`.\n * @param name - Registered capability name.\n * @param input - Raw tool input from the LLM.\n * @param onBackgroundError - Optional error handler for fire-and-forget failures; receives the dispatch call id.\n * @param signal - Optional abort signal forwarded to the handler.\n * @returns Resolved dispatch result with the LLM-facing value, background flag and dispatch call id.\n * @docLink packages/bridge/concepts#dispatch-capability\n */\nexport async function dispatchCapability(\n hooks: BridgeCapabilityHooks,\n name: string,\n input: unknown,\n onBackgroundError: (err: unknown, callId: string) => void = () => {},\n signal?: AbortSignal,\n): Promise<CapabilityDispatchResult> {\n signal?.throwIfAborted();\n const callId = randomUUID();\n const cap = hooks.resolve(name);\n // The advertised `activity_label` is for the UI only. Strip it before render\n // props and the handler: strict inputs would reject it and external MCP\n // passthrough would forward it. Kept when the capability declares it itself.\n if (!declaresActivityLabel(cap?.inputSchema)) input = stripActivityLabel(input);\n\n // Render emission happens before invocation so clients see the props the\n // LLM passed in even if the handler errors. The runner's gateway forwards\n // the event over the transport.\n if (cap?.render && hooks.emitRender) {\n hooks.emitRender({\n type: \"render_invoked\",\n capabilityName: name,\n props: input,\n callId,\n });\n if (cap.render.fallback && hooks.emitText) {\n const fallback = renderFallback(cap.render.fallback, input);\n hooks.emitText(fallback);\n }\n }\n\n if (cap?.fireAndForget) {\n // Resolve immediately; run handler in background.\n void hooks.invoke(name, input, signal, callId).catch((err) => onBackgroundError(err, callId));\n return { result: {}, background: true, callId };\n }\n\n const result = await hooks.invoke(name, input, signal, callId);\n return { result, background: false, callId };\n}\n\n/**\n * Substitute `{{prop}}` placeholders inside a fallback template using the\n * input record. Top-level keys of `props` resolve directly\n * (`{{name}}` → `props.name`); dotted paths traverse nested records\n * (`{{user.name}}` → `props.user.name`). Missing keys collapse to an empty\n * string; non-record inputs collapse all placeholders.\n *\n * The convention is fixed by the v2 spec (§1, `RenderSpec.fallback`). The\n * legacy AAP form `{{props.x}}` is no longer accepted — author fallbacks\n * with bare keys.\n *\n * Intentionally minimal — full template engines belong in the platform's\n * render layer, not the bridge.\n *\n * @param template - Fallback string from `RenderSpec.fallback` with `{{key}}` placeholders.\n * @param input - LLM tool input object; non-object values collapse all placeholders.\n * @returns Template with all resolvable placeholders replaced.\n * @docLink packages/bridge/concepts#render-fallback\n */\nexport function renderFallback(template: string, input: unknown): string {\n if (!input || typeof input !== \"object\") {\n return template.replace(/\\{\\{\\s*[\\w$.]+\\s*\\}\\}/g, \"\");\n }\n const props = input as Record<string, unknown>;\n return template.replace(/\\{\\{\\s*([\\w$.]+)\\s*\\}\\}/g, (_match, path: string) => {\n const value = readPath(props, path);\n return value === undefined || value === null ? \"\" : String(value);\n });\n}\n\nfunction readPath(obj: Record<string, unknown>, path: string): unknown {\n const parts = path.split(\".\");\n let current: unknown = obj;\n for (const part of parts) {\n if (current === null || current === undefined || typeof current !== \"object\") {\n return undefined;\n }\n current = (current as Record<string, unknown>)[part];\n }\n return current;\n}\n\n/**\n * Filter a capability list to those marked as render-capable (having a `render` spec).\n *\n * Used by harnesses that emit a separate render manifest at registration time.\n * Currently unused in production harnesses; reserved for future phases.\n *\n * @param caps - Full capability list to filter.\n * @returns Subset of capabilities with a non-undefined `render` property.\n * @docLink packages/bridge/concepts#filter-render-capabilities\n */\nexport function filterRenderCapabilities(caps: Capability[]): Capability[] {\n return caps.filter((c) => c.render !== undefined);\n}\n"]}
1
+ {"version":3,"sources":["../bridge/src/capability-dispatch.ts"],"names":[],"mappings":";;;AAgFA,eAAsB,kBAAA,CACpB,KAAA,EACA,IAAA,EACA,KAAA,EACA,oBAA4D,MAAM;AAAC,CAAA,EACnE,MAAA,EACmC;AACnC,EAAA,MAAA,EAAQ,cAAA,EAAe;AACvB,EAAA,MAAM,SAAS,UAAA,EAAW;AAC1B,EAAA,MAAM,GAAA,GAAM,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA;AAI9B,EAAA,IAAI,CAAC,qBAAA,CAAsB,GAAA,EAAK,WAAW,CAAA,EAAG,KAAA,GAAQ,mBAAmB,KAAK,CAAA;AAK9E,EAAA,IAAI,GAAA,EAAK,MAAA,IAAU,KAAA,CAAM,UAAA,EAAY;AACnC,IAAA,KAAA,CAAM,UAAA,CAAW;AAAA,MACf,IAAA,EAAM,gBAAA;AAAA,MACN,cAAA,EAAgB,IAAA;AAAA,MAChB,KAAA,EAAO,KAAA;AAAA,MACP;AAAA,KACD,CAAA;AACD,IAAA,IAAI,GAAA,CAAI,MAAA,CAAO,QAAA,IAAY,KAAA,CAAM,QAAA,EAAU;AACzC,MAAA,MAAM,QAAA,GAAW,cAAA,CAAe,GAAA,CAAI,MAAA,CAAO,UAAU,KAAK,CAAA;AAC1D,MAAA,KAAA,CAAM,SAAS,QAAQ,CAAA;AAAA,IACzB;AAAA,EACF;AAEA,EAAA,IAAI,KAAK,aAAA,EAAe;AAEtB,IAAA,KAAK,KAAA,CAAM,MAAA,CAAO,IAAA,EAAM,KAAA,EAAO,MAAA,EAAQ,MAAM,CAAA,CAAE,KAAA,CAAM,CAAC,GAAA,KAAQ,iBAAA,CAAkB,GAAA,EAAK,MAAM,CAAC,CAAA;AAC5F,IAAA,OAAO,EAAE,MAAA,EAAQ,EAAC,EAAG,UAAA,EAAY,MAAM,MAAA,EAAO;AAAA,EAChD;AAEA,EAAA,MAAM,SAAS,MAAM,KAAA,CAAM,OAAO,IAAA,EAAM,KAAA,EAAO,QAAQ,MAAM,CAAA;AAC7D,EAAA,OAAO,EAAE,MAAA,EAAQ,UAAA,EAAY,KAAA,EAAO,MAAA,EAAO;AAC7C;AAqBO,SAAS,cAAA,CAAe,UAAkB,KAAA,EAAwB;AACvE,EAAA,IAAI,CAAC,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,EAAU;AACvC,IAAA,OAAO,QAAA,CAAS,OAAA,CAAQ,wBAAA,EAA0B,EAAE,CAAA;AAAA,EACtD;AACA,EAAA,MAAM,KAAA,GAAQ,KAAA;AACd,EAAA,OAAO,QAAA,CAAS,OAAA,CAAQ,0BAAA,EAA4B,CAAC,QAAQ,IAAA,KAAiB;AAC5E,IAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,KAAA,EAAO,IAAI,CAAA;AAClC,IAAA,OAAO,UAAU,MAAA,IAAa,KAAA,KAAU,IAAA,GAAO,EAAA,GAAK,OAAO,KAAK,CAAA;AAAA,EAClE,CAAC,CAAA;AACH;AAEA,SAAS,QAAA,CAAS,KAA8B,IAAA,EAAuB;AACrE,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AAC5B,EAAA,IAAI,OAAA,GAAmB,GAAA;AACvB,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,IAAI,YAAY,IAAA,IAAQ,OAAA,KAAY,MAAA,IAAa,OAAO,YAAY,QAAA,EAAU;AAC5E,MAAA,OAAO,MAAA;AAAA,IACT;AACA,IAAA,OAAA,GAAW,QAAoC,IAAI,CAAA;AAAA,EACrD;AACA,EAAA,OAAO,OAAA;AACT;AAYO,SAAS,yBAAyB,IAAA,EAAkC;AACzE,EAAA,OAAO,KAAK,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,WAAW,MAAS,CAAA;AAClD","file":"chunk-UUKCPPTQ.js","sourcesContent":["/**\n * Capability dispatch helpers for bridge harnesses.\n *\n * The runner builds a {@link BridgeCapabilityHooks} bundle from its\n * `CapabilityRegistry` and threads it through `AgentConfig.capabilities`.\n * Harnesses consume the bundle via these helpers without taking a runtime\n * dependency on `@skaile/workspaces/runner` (which would create a circular dep).\n *\n * Three behaviours are layered into a single dispatch path:\n *\n * 1. **Standard tool call** — the LLM invokes a capability; the bridge calls\n * `hooks.invoke()`, awaits the result, and returns it to the LLM.\n * 2. **Fire-and-forget** — when the resolved Capability has `fireAndForget`\n * set, the bridge resolves to `{}` immediately and runs the handler in\n * the background. Background errors are logged but never surfaced to the\n * LLM since the call is already settled.\n * 3. **Render capability** — when the resolved Capability has a non-undefined\n * `render`, the bridge emits `RenderInvokedEvent` to the runner via\n * `hooks.emitRender` and (when `render.fallback` is set) a parallel text\n * event via `hooks.emitText` with simple `{{prop}}` substitution. The\n * placeholder convention follows the v2 spec — top-level keys of `props`\n * only, with optional dotted-path traversal (`{{user.name}}`). The legacy\n * AAP `{{props.x}}` form was retired in Phase 6.\n *\n * @category Capabilities\n * @since 2.0.0\n */\nimport { randomUUID } from \"node:crypto\";\nimport {\n type Capability,\n declaresActivityLabel,\n stripActivityLabel,\n} from \"@skaile/workspaces/types\";\nimport type { BridgeCapabilityHooks } from \"./types.js\";\n\n/**\n * Result of a capability dispatch returned by {@link dispatchCapability}.\n *\n * The bridge feeds `{ result }` back to the LLM tool-use response. For\n * fire-and-forget capabilities `result` is always `{}` and `background` is `true`.\n *\n * @docLink packages/bridge/concepts#capability-dispatch-result\n */\nexport interface CapabilityDispatchResult {\n /** Value returned to the LLM. `{}` for fire-and-forget capabilities. */\n result: unknown;\n /** True when the handler is still running in the background (fire-and-forget). */\n background: boolean;\n /** The per-dispatch call id minted for this invocation (see {@link dispatchCapability}). */\n callId: string;\n}\n\n/**\n * Run a capability invocation through the runner-provided hooks, applying\n * render-event emission and fire-and-forget short-circuit.\n *\n * The result is always shaped for direct return to the LLM. For\n * fire-and-forget capabilities the background promise is fired and any error\n * is logged through `onBackgroundError` (defaulting to a no-op). For all\n * other capabilities the handler is awaited; errors propagate as a rejection\n * so the harness can surface them as tool errors.\n *\n * **Dispatch call id.** Every dispatch mints a fresh `randomUUID()` and uses it\n * for `render_invoked.callId`, `hooks.invoke`'s `callId` and — for client\n * capabilities — the outbound `capability_invoked.callId`. It is deliberately\n * NOT the harness's tool-call id: those are only as unique as the model\n * provider makes them (some harnesses emit `call_0` on every turn), and the\n * runner's pending-call map is keyed by this id, so a late `capability_result`\n * for a timed-out call must never be able to resolve a later call that reused\n * the provider's id. Harnesses map results back to their tool calls through the\n * returned value, never through this id.\n *\n * @param hooks - Capability dispatch bundle from `AgentConfig.capabilities`.\n * @param name - Registered capability name.\n * @param input - Raw tool input from the LLM.\n * @param onBackgroundError - Optional error handler for fire-and-forget failures; receives the dispatch call id.\n * @param signal - Optional abort signal forwarded to the handler.\n * @returns Resolved dispatch result with the LLM-facing value, background flag and dispatch call id.\n * @docLink packages/bridge/concepts#dispatch-capability\n */\nexport async function dispatchCapability(\n hooks: BridgeCapabilityHooks,\n name: string,\n input: unknown,\n onBackgroundError: (err: unknown, callId: string) => void = () => {},\n signal?: AbortSignal,\n): Promise<CapabilityDispatchResult> {\n signal?.throwIfAborted();\n const callId = randomUUID();\n const cap = hooks.resolve(name);\n // The advertised `activity_label` is for the UI only. Strip it before render\n // props and the handler: strict inputs would reject it and external MCP\n // passthrough would forward it. Kept when the capability declares it itself.\n if (!declaresActivityLabel(cap?.inputSchema)) input = stripActivityLabel(input);\n\n // Render emission happens before invocation so clients see the props the\n // LLM passed in even if the handler errors. The runner's gateway forwards\n // the event over the transport.\n if (cap?.render && hooks.emitRender) {\n hooks.emitRender({\n type: \"render_invoked\",\n capabilityName: name,\n props: input,\n callId,\n });\n if (cap.render.fallback && hooks.emitText) {\n const fallback = renderFallback(cap.render.fallback, input);\n hooks.emitText(fallback);\n }\n }\n\n if (cap?.fireAndForget) {\n // Resolve immediately; run handler in background.\n void hooks.invoke(name, input, signal, callId).catch((err) => onBackgroundError(err, callId));\n return { result: {}, background: true, callId };\n }\n\n const result = await hooks.invoke(name, input, signal, callId);\n return { result, background: false, callId };\n}\n\n/**\n * Substitute `{{prop}}` placeholders inside a fallback template using the\n * input record. Top-level keys of `props` resolve directly\n * (`{{name}}` → `props.name`); dotted paths traverse nested records\n * (`{{user.name}}` → `props.user.name`). Missing keys collapse to an empty\n * string; non-record inputs collapse all placeholders.\n *\n * The convention is fixed by the v2 spec (§1, `RenderSpec.fallback`). The\n * legacy AAP form `{{props.x}}` is no longer accepted — author fallbacks\n * with bare keys.\n *\n * Intentionally minimal — full template engines belong in the platform's\n * render layer, not the bridge.\n *\n * @param template - Fallback string from `RenderSpec.fallback` with `{{key}}` placeholders.\n * @param input - LLM tool input object; non-object values collapse all placeholders.\n * @returns Template with all resolvable placeholders replaced.\n * @docLink packages/bridge/concepts#render-fallback\n */\nexport function renderFallback(template: string, input: unknown): string {\n if (!input || typeof input !== \"object\") {\n return template.replace(/\\{\\{\\s*[\\w$.]+\\s*\\}\\}/g, \"\");\n }\n const props = input as Record<string, unknown>;\n return template.replace(/\\{\\{\\s*([\\w$.]+)\\s*\\}\\}/g, (_match, path: string) => {\n const value = readPath(props, path);\n return value === undefined || value === null ? \"\" : String(value);\n });\n}\n\nfunction readPath(obj: Record<string, unknown>, path: string): unknown {\n const parts = path.split(\".\");\n let current: unknown = obj;\n for (const part of parts) {\n if (current === null || current === undefined || typeof current !== \"object\") {\n return undefined;\n }\n current = (current as Record<string, unknown>)[part];\n }\n return current;\n}\n\n/**\n * Filter a capability list to those marked as render-capable (having a `render` spec).\n *\n * Used by harnesses that emit a separate render manifest at registration time.\n * Currently unused in production harnesses; reserved for future phases.\n *\n * @param caps - Full capability list to filter.\n * @returns Subset of capabilities with a non-undefined `render` property.\n * @docLink packages/bridge/concepts#filter-render-capabilities\n */\nexport function filterRenderCapabilities(caps: Capability[]): Capability[] {\n return caps.filter((c) => c.render !== undefined);\n}\n"]}