@skaile/workspaces 6.14.0 → 6.16.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 (447) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/dist/agent-file-refresh-76NNFB4W.js +89 -0
  3. package/dist/agent-file-refresh-76NNFB4W.js.map +1 -0
  4. package/dist/ai-sdk.js +8 -7
  5. package/dist/ai-sdk.js.map +1 -1
  6. package/dist/app-sdk/index.js +1 -1
  7. package/dist/{asset-feeds-DOG5FK7T.js → asset-feeds-ON7FYHHF.js} +25 -23
  8. package/dist/asset-feeds-ON7FYHHF.js.map +1 -0
  9. package/dist/asset-manager/catalog-deployer.js +8 -7
  10. package/dist/asset-manager/history.js +7 -6
  11. package/dist/asset-manager/index.js +17 -15
  12. package/dist/asset-manager/installer.js +16 -9
  13. package/dist/asset-manager/renderers.js +8 -8
  14. package/dist/asset-manager/src/add-remove/add.d.ts +134 -0
  15. package/dist/asset-manager/src/add-remove/add.d.ts.map +1 -0
  16. package/dist/asset-manager/src/add-remove/remove.d.ts +329 -0
  17. package/dist/asset-manager/src/add-remove/remove.d.ts.map +1 -0
  18. package/dist/asset-manager/src/agent-render.d.ts +116 -0
  19. package/dist/asset-manager/src/agent-render.d.ts.map +1 -0
  20. package/dist/asset-manager/src/composed-blocks.d.ts +40 -0
  21. package/dist/asset-manager/src/composed-blocks.d.ts.map +1 -0
  22. package/dist/asset-manager/src/config/core.d.ts +297 -0
  23. package/dist/asset-manager/src/config/core.d.ts.map +1 -0
  24. package/dist/asset-manager/src/config/edit.d.ts +161 -0
  25. package/dist/asset-manager/src/config/edit.d.ts.map +1 -0
  26. package/dist/asset-manager/src/index.d.ts +31 -2961
  27. package/dist/asset-manager/src/index.d.ts.map +1 -1
  28. package/dist/asset-manager/src/install/clone-commit-fetcher.d.ts +11 -0
  29. package/dist/asset-manager/src/install/clone-commit-fetcher.d.ts.map +1 -0
  30. package/dist/asset-manager/src/install/install.d.ts +151 -0
  31. package/dist/asset-manager/src/install/install.d.ts.map +1 -0
  32. package/dist/asset-manager/src/install/restore.d.ts +76 -0
  33. package/dist/asset-manager/src/install/restore.d.ts.map +1 -0
  34. package/dist/asset-manager/src/install-agent.d.ts +52 -4
  35. package/dist/asset-manager/src/install-agent.d.ts.map +1 -1
  36. package/dist/asset-manager/src/install-instances.d.ts +70 -2
  37. package/dist/asset-manager/src/install-instances.d.ts.map +1 -1
  38. package/dist/asset-manager/src/installer.d.ts +54 -4
  39. package/dist/asset-manager/src/installer.d.ts.map +1 -1
  40. package/dist/asset-manager/src/lock/copies.d.ts +156 -0
  41. package/dist/asset-manager/src/lock/copies.d.ts.map +1 -0
  42. package/dist/asset-manager/src/lock/library.d.ts +261 -0
  43. package/dist/asset-manager/src/lock/library.d.ts.map +1 -0
  44. package/dist/asset-manager/src/mcp-render.d.ts +86 -0
  45. package/dist/asset-manager/src/mcp-render.d.ts.map +1 -0
  46. package/dist/asset-manager/src/omp-sweep.d.ts +25 -0
  47. package/dist/asset-manager/src/omp-sweep.d.ts.map +1 -0
  48. package/dist/asset-manager/src/read/diff.d.ts +109 -0
  49. package/dist/asset-manager/src/read/diff.d.ts.map +1 -0
  50. package/dist/asset-manager/src/read/doctor.d.ts +116 -0
  51. package/dist/asset-manager/src/read/doctor.d.ts.map +1 -0
  52. package/dist/asset-manager/src/read/gaps.d.ts +120 -0
  53. package/dist/asset-manager/src/read/gaps.d.ts.map +1 -0
  54. package/dist/asset-manager/src/read/graph.d.ts +69 -0
  55. package/dist/asset-manager/src/read/graph.d.ts.map +1 -0
  56. package/dist/asset-manager/src/read/outdated.d.ts +152 -0
  57. package/dist/asset-manager/src/read/outdated.d.ts.map +1 -0
  58. package/dist/asset-manager/src/read/state.d.ts +265 -0
  59. package/dist/asset-manager/src/read/state.d.ts.map +1 -0
  60. package/dist/asset-manager/src/renderers.d.ts +11 -1
  61. package/dist/asset-manager/src/renderers.d.ts.map +1 -1
  62. package/dist/asset-manager/src/resolve/guards.d.ts +167 -0
  63. package/dist/asset-manager/src/resolve/guards.d.ts.map +1 -0
  64. package/dist/asset-manager/src/resolve/scan.d.ts +222 -0
  65. package/dist/asset-manager/src/resolve/scan.d.ts.map +1 -0
  66. package/dist/asset-manager/src/resolve/stores.d.ts +176 -0
  67. package/dist/asset-manager/src/resolve/stores.d.ts.map +1 -0
  68. package/dist/asset-manager/src/shared/files.d.ts +42 -0
  69. package/dist/asset-manager/src/shared/files.d.ts.map +1 -0
  70. package/dist/asset-manager/src/shared/library-hooks.d.ts +16 -0
  71. package/dist/asset-manager/src/shared/library-hooks.d.ts.map +1 -0
  72. package/dist/asset-manager/src/shared/library.d.ts +96 -0
  73. package/dist/asset-manager/src/shared/library.d.ts.map +1 -0
  74. package/dist/asset-manager/src/shared/refs.d.ts +101 -0
  75. package/dist/asset-manager/src/shared/refs.d.ts.map +1 -0
  76. package/dist/asset-manager/src/shared/types.d.ts +399 -0
  77. package/dist/asset-manager/src/shared/types.d.ts.map +1 -0
  78. package/dist/bridge/harnesses/claude-sdk.js +13 -19
  79. package/dist/bridge/harnesses/claude-sdk.js.map +1 -1
  80. package/dist/bridge/harnesses/codex.js +10 -10
  81. package/dist/bridge/harnesses/continue.js +12 -16
  82. package/dist/bridge/harnesses/continue.js.map +1 -1
  83. package/dist/bridge/harnesses/echo.js +5 -10
  84. package/dist/bridge/harnesses/echo.js.map +1 -1
  85. package/dist/bridge/harnesses/gemini.js +5 -5
  86. package/dist/bridge/harnesses/goose.js +13 -17
  87. package/dist/bridge/harnesses/goose.js.map +1 -1
  88. package/dist/bridge/harnesses/omp.js +7 -13
  89. package/dist/bridge/harnesses/omp.js.map +1 -1
  90. package/dist/bridge/harnesses/opencode.js +13 -17
  91. package/dist/bridge/harnesses/opencode.js.map +1 -1
  92. package/dist/bridge/harnesses/qwen.js +6 -11
  93. package/dist/bridge/harnesses/qwen.js.map +1 -1
  94. package/dist/bridge/index.js +18 -18
  95. package/dist/bridge/src/codex/managed-harness.d.ts.map +1 -1
  96. package/dist/bridge/src/driver-contract.d.ts +72 -0
  97. package/dist/bridge/src/driver-contract.d.ts.map +1 -0
  98. package/dist/bridge/src/harness-model-check.d.ts +1 -1
  99. package/dist/bridge/src/harness-model-check.d.ts.map +1 -1
  100. package/dist/bridge/src/harnesses/_subprocess-cli.d.ts +8 -0
  101. package/dist/bridge/src/harnesses/_subprocess-cli.d.ts.map +1 -1
  102. package/dist/bridge/src/harnesses/claude-sdk.d.ts.map +1 -1
  103. package/dist/bridge/src/harnesses/codex.d.ts +2 -0
  104. package/dist/bridge/src/harnesses/codex.d.ts.map +1 -1
  105. package/dist/bridge/src/harnesses/continue.d.ts.map +1 -1
  106. package/dist/bridge/src/harnesses/echo.d.ts.map +1 -1
  107. package/dist/bridge/src/harnesses/gemini.d.ts.map +1 -1
  108. package/dist/bridge/src/harnesses/goose.d.ts.map +1 -1
  109. package/dist/bridge/src/harnesses/omp.d.ts.map +1 -1
  110. package/dist/bridge/src/harnesses/opencode.d.ts.map +1 -1
  111. package/dist/bridge/src/harnesses/qwen.d.ts.map +1 -1
  112. package/dist/bridge/src/index.d.ts +2 -0
  113. package/dist/bridge/src/index.d.ts.map +1 -1
  114. package/dist/bridge/src/preflight.d.ts +114 -0
  115. package/dist/bridge/src/preflight.d.ts.map +1 -0
  116. package/dist/bridge/src/registry.d.ts +12 -0
  117. package/dist/bridge/src/registry.d.ts.map +1 -1
  118. package/dist/{chunk-7UBCUKJO.js → chunk-2JUCWMIS.js} +8808 -8337
  119. package/dist/chunk-2JUCWMIS.js.map +1 -0
  120. package/dist/{chunk-VS6XC5JE.js → chunk-2MTRRK37.js} +3 -3
  121. package/dist/{chunk-VS6XC5JE.js.map → chunk-2MTRRK37.js.map} +1 -1
  122. package/dist/chunk-2PJYJPDE.js +3 -0
  123. package/dist/chunk-2PJYJPDE.js.map +1 -0
  124. package/dist/{chunk-ZQL6HGTW.js → chunk-2SSG5B24.js} +4 -4
  125. package/dist/{chunk-ZQL6HGTW.js.map → chunk-2SSG5B24.js.map} +1 -1
  126. package/dist/{chunk-WDG5IVEA.js → chunk-2ZDZJ7XQ.js} +3 -3
  127. package/dist/{chunk-WDG5IVEA.js.map → chunk-2ZDZJ7XQ.js.map} +1 -1
  128. package/dist/{chunk-V2VDTQ7A.js → chunk-3A7JUOY2.js} +3 -3
  129. package/dist/{chunk-V2VDTQ7A.js.map → chunk-3A7JUOY2.js.map} +1 -1
  130. package/dist/chunk-3LAJJURV.js +26 -0
  131. package/dist/chunk-3LAJJURV.js.map +1 -0
  132. package/dist/{chunk-X66KSHZF.js → chunk-3UYKT62L.js} +5 -5
  133. package/dist/{chunk-X66KSHZF.js.map → chunk-3UYKT62L.js.map} +1 -1
  134. package/dist/{chunk-J3FJTH3V.js → chunk-4X5WXQV7.js} +4 -4
  135. package/dist/{chunk-J3FJTH3V.js.map → chunk-4X5WXQV7.js.map} +1 -1
  136. package/dist/chunk-4ZNKEDYH.js +887 -0
  137. package/dist/chunk-4ZNKEDYH.js.map +1 -0
  138. package/dist/{chunk-DYH3VSPU.js → chunk-65SOXO7I.js} +20 -14
  139. package/dist/chunk-65SOXO7I.js.map +1 -0
  140. package/dist/{chunk-MVUXBWNW.js → chunk-6HTN2UIJ.js} +3 -3
  141. package/dist/{chunk-MVUXBWNW.js.map → chunk-6HTN2UIJ.js.map} +1 -1
  142. package/dist/{chunk-4SSTREBO.js → chunk-6KV34G6A.js} +8 -8
  143. package/dist/{chunk-4SSTREBO.js.map → chunk-6KV34G6A.js.map} +1 -1
  144. package/dist/{chunk-YNQRVTXH.js → chunk-6M44OQKH.js} +40 -44
  145. package/dist/chunk-6M44OQKH.js.map +1 -0
  146. package/dist/{chunk-B6W5DCH5.js → chunk-72DYHOO2.js} +24 -25
  147. package/dist/chunk-72DYHOO2.js.map +1 -0
  148. package/dist/{chunk-RRAXV5LE.js → chunk-7IW5E6LB.js} +43 -48
  149. package/dist/chunk-7IW5E6LB.js.map +1 -0
  150. package/dist/{chunk-BNLXZXZN.js → chunk-BRAOB37W.js} +97 -47
  151. package/dist/chunk-BRAOB37W.js.map +1 -0
  152. package/dist/{chunk-ZLDAPZIX.js → chunk-BTMSHGN3.js} +4 -4
  153. package/dist/{chunk-ZLDAPZIX.js.map → chunk-BTMSHGN3.js.map} +1 -1
  154. package/dist/{chunk-7K73MCI6.js → chunk-CU5WY6HN.js} +3 -3
  155. package/dist/{chunk-7K73MCI6.js.map → chunk-CU5WY6HN.js.map} +1 -1
  156. package/dist/{chunk-G2ZWIYZR.js → chunk-DFFJKGCW.js} +3 -3
  157. package/dist/{chunk-G2ZWIYZR.js.map → chunk-DFFJKGCW.js.map} +1 -1
  158. package/dist/{chunk-CG63XGVV.js → chunk-EGKCKWWE.js} +3 -3
  159. package/dist/{chunk-CG63XGVV.js.map → chunk-EGKCKWWE.js.map} +1 -1
  160. package/dist/{chunk-CJO3XKO7.js → chunk-ETGC2MEM.js} +4 -4
  161. package/dist/{chunk-CJO3XKO7.js.map → chunk-ETGC2MEM.js.map} +1 -1
  162. package/dist/{chunk-CFRHYD4C.js → chunk-FPG36VC4.js} +8 -13
  163. package/dist/chunk-FPG36VC4.js.map +1 -0
  164. package/dist/{chunk-SBAUZ7XH.js → chunk-H3PY5DFL.js} +4 -4
  165. package/dist/{chunk-SBAUZ7XH.js.map → chunk-H3PY5DFL.js.map} +1 -1
  166. package/dist/{chunk-7HRZACI4.js → chunk-HW4BDJPC.js} +17 -6
  167. package/dist/chunk-HW4BDJPC.js.map +1 -0
  168. package/dist/{chunk-GJT2Q5XD.js → chunk-HXST2IYJ.js} +3 -3
  169. package/dist/{chunk-GJT2Q5XD.js.map → chunk-HXST2IYJ.js.map} +1 -1
  170. package/dist/{chunk-7VBD5XUM.js → chunk-IG4YQ7ER.js} +32 -90
  171. package/dist/chunk-IG4YQ7ER.js.map +1 -0
  172. package/dist/{chunk-DWPQBMCY.js → chunk-JPV4WE5L.js} +2 -2
  173. package/dist/{chunk-DWPQBMCY.js.map → chunk-JPV4WE5L.js.map} +1 -1
  174. package/dist/{chunk-OC3AIMZH.js → chunk-KDRA6FYE.js} +120 -211
  175. package/dist/chunk-KDRA6FYE.js.map +1 -0
  176. package/dist/{chunk-KOI7QFDH.js → chunk-M7XIYHWB.js} +3 -3
  177. package/dist/{chunk-KOI7QFDH.js.map → chunk-M7XIYHWB.js.map} +1 -1
  178. package/dist/{chunk-7LJUK5JK.js → chunk-MG6QD7GE.js} +201 -43
  179. package/dist/chunk-MG6QD7GE.js.map +1 -0
  180. package/dist/{chunk-XHLRMUMO.js → chunk-NAQXDTF3.js} +3 -3
  181. package/dist/{chunk-XHLRMUMO.js.map → chunk-NAQXDTF3.js.map} +1 -1
  182. package/dist/{chunk-YJAKSCCN.js → chunk-NJ6UCY2B.js} +3 -3
  183. package/dist/{chunk-YJAKSCCN.js.map → chunk-NJ6UCY2B.js.map} +1 -1
  184. package/dist/{chunk-OKPITFE3.js → chunk-NNJQQYHY.js} +2 -2
  185. package/dist/{chunk-OKPITFE3.js.map → chunk-NNJQQYHY.js.map} +1 -1
  186. package/dist/{chunk-QNHW26AT.js → chunk-OD4N5O7V.js} +4 -4
  187. package/dist/{chunk-QNHW26AT.js.map → chunk-OD4N5O7V.js.map} +1 -1
  188. package/dist/{chunk-F7WMHDTV.js → chunk-P6FJDT35.js} +27 -7
  189. package/dist/chunk-P6FJDT35.js.map +1 -0
  190. package/dist/{chunk-IOXFT5D7.js → chunk-PD42JZWI.js} +3 -3
  191. package/dist/{chunk-IOXFT5D7.js.map → chunk-PD42JZWI.js.map} +1 -1
  192. package/dist/{chunk-MS2IXLTC.js → chunk-PMXTBWFD.js} +27 -7
  193. package/dist/chunk-PMXTBWFD.js.map +1 -0
  194. package/dist/{chunk-NAAFRZTA.js → chunk-PPSSDUN6.js} +3 -3
  195. package/dist/{chunk-NAAFRZTA.js.map → chunk-PPSSDUN6.js.map} +1 -1
  196. package/dist/{chunk-LMUNQYJQ.js → chunk-PRB67LCK.js} +127 -18
  197. package/dist/chunk-PRB67LCK.js.map +1 -0
  198. package/dist/{chunk-RLV3FF4I.js → chunk-QUIK7L3Z.js} +11 -11
  199. package/dist/{chunk-RLV3FF4I.js.map → chunk-QUIK7L3Z.js.map} +1 -1
  200. package/dist/{chunk-DRUAQ7IY.js → chunk-QUPZKTF3.js} +9 -8
  201. package/dist/chunk-QUPZKTF3.js.map +1 -0
  202. package/dist/{chunk-7FXOOUOC.js → chunk-RPRMD7XV.js} +27 -11
  203. package/dist/chunk-RPRMD7XV.js.map +1 -0
  204. package/dist/{chunk-3BRBTQ6M.js → chunk-S3UOZBGE.js} +20 -3
  205. package/dist/chunk-S3UOZBGE.js.map +1 -0
  206. package/dist/{chunk-LQWOU7S2.js → chunk-SFBLZOV5.js} +3 -3
  207. package/dist/{chunk-LQWOU7S2.js.map → chunk-SFBLZOV5.js.map} +1 -1
  208. package/dist/{chunk-EB2LKPI6.js → chunk-SMFZ5YGN.js} +4 -4
  209. package/dist/{chunk-EB2LKPI6.js.map → chunk-SMFZ5YGN.js.map} +1 -1
  210. package/dist/{chunk-243Z3BNB.js → chunk-SX54OQQT.js} +4 -4
  211. package/dist/{chunk-243Z3BNB.js.map → chunk-SX54OQQT.js.map} +1 -1
  212. package/dist/{chunk-2PVYDAFN.js → chunk-T65N4CIZ.js} +3 -3
  213. package/dist/{chunk-2PVYDAFN.js.map → chunk-T65N4CIZ.js.map} +1 -1
  214. package/dist/{chunk-QBLS7X3Z.js → chunk-TVRL3IPZ.js} +3 -3
  215. package/dist/{chunk-QBLS7X3Z.js.map → chunk-TVRL3IPZ.js.map} +1 -1
  216. package/dist/{chunk-2URJ7W5X.js → chunk-TW5AP6IF.js} +5 -5
  217. package/dist/{chunk-2URJ7W5X.js.map → chunk-TW5AP6IF.js.map} +1 -1
  218. package/dist/{chunk-F6LDWA2N.js → chunk-TYK752MU.js} +3 -3
  219. package/dist/{chunk-F6LDWA2N.js.map → chunk-TYK752MU.js.map} +1 -1
  220. package/dist/{chunk-HPQ2WDYC.js → chunk-U2WPJHRA.js} +5 -5
  221. package/dist/{chunk-HPQ2WDYC.js.map → chunk-U2WPJHRA.js.map} +1 -1
  222. package/dist/{chunk-MEU2HHGV.js → chunk-U7P22GEB.js} +386 -46
  223. package/dist/chunk-U7P22GEB.js.map +1 -0
  224. package/dist/{chunk-6ISJMEDX.js → chunk-VFEZUSB6.js} +4 -4
  225. package/dist/{chunk-6ISJMEDX.js.map → chunk-VFEZUSB6.js.map} +1 -1
  226. package/dist/{chunk-5FO4U3OL.js → chunk-WW22FS74.js} +75 -67
  227. package/dist/chunk-WW22FS74.js.map +1 -0
  228. package/dist/{chunk-WCUT3EQR.js → chunk-Z23I2JA6.js} +7 -7
  229. package/dist/{chunk-WCUT3EQR.js.map → chunk-Z23I2JA6.js.map} +1 -1
  230. package/dist/{chunk-VKGNMXGZ.js → chunk-Z7HLX4V2.js} +4 -4
  231. package/dist/{chunk-VKGNMXGZ.js.map → chunk-Z7HLX4V2.js.map} +1 -1
  232. package/dist/{chunk-LZCX2TCZ.js → chunk-ZII54C3E.js} +355 -1075
  233. package/dist/chunk-ZII54C3E.js.map +1 -0
  234. package/dist/{chunk-CVUMZTNO.js → chunk-ZQPBYLCS.js} +4 -4
  235. package/dist/{chunk-CVUMZTNO.js.map → chunk-ZQPBYLCS.js.map} +1 -1
  236. package/dist/{claude-code-LFAOJ3UQ.js → claude-code-VL6UM7BZ.js} +4 -4
  237. package/dist/{claude-code-LFAOJ3UQ.js.map → claude-code-VL6UM7BZ.js.map} +1 -1
  238. package/dist/cli/index.js +494 -301
  239. package/dist/cli/index.js.map +1 -1
  240. package/dist/cli/src/agent-file-refresh.d.ts +25 -0
  241. package/dist/cli/src/agent-file-refresh.d.ts.map +1 -0
  242. package/dist/cli/src/commands/asset-cmd.d.ts.map +1 -1
  243. package/dist/cli/src/commands/catalog.d.ts +9 -2
  244. package/dist/cli/src/commands/catalog.d.ts.map +1 -1
  245. package/dist/cli/src/commands/config.d.ts.map +1 -1
  246. package/dist/cli/src/commands/harness.d.ts +1 -1
  247. package/dist/cli/src/commands/harness.d.ts.map +1 -1
  248. package/dist/cli/src/commands/library-cmd.d.ts.map +1 -1
  249. package/dist/cli/src/commands/library-index.d.ts +5 -7
  250. package/dist/cli/src/commands/library-index.d.ts.map +1 -1
  251. package/dist/cli/src/commands/library-publish.d.ts.map +1 -1
  252. package/dist/cli/src/commands/preset-export.d.ts.map +1 -1
  253. package/dist/cli/src/commands/project.d.ts.map +1 -1
  254. package/dist/cli/src/commands/run.d.ts.map +1 -1
  255. package/dist/cli/src/commands/serve-preflight.d.ts +27 -48
  256. package/dist/cli/src/commands/serve-preflight.d.ts.map +1 -1
  257. package/dist/cli/src/commands/serve.d.ts.map +1 -1
  258. package/dist/cli/src/commands/source.d.ts +6 -9
  259. package/dist/cli/src/commands/source.d.ts.map +1 -1
  260. package/dist/cli/src/commands/validate.d.ts +6 -0
  261. package/dist/cli/src/commands/validate.d.ts.map +1 -1
  262. package/dist/cli/src/config-checks.d.ts.map +1 -1
  263. package/dist/cli/src/harness-error.d.ts +11 -9
  264. package/dist/cli/src/harness-error.d.ts.map +1 -1
  265. package/dist/cli/src/skill-checks.d.ts +48 -74
  266. package/dist/cli/src/skill-checks.d.ts.map +1 -1
  267. package/dist/connectors/config.js +5 -5
  268. package/dist/connectors/index.js +22 -22
  269. package/dist/{connectors-KPF4UUDS.js → connectors-7EPJRVLJ.js} +25 -25
  270. package/dist/{connectors-KPF4UUDS.js.map → connectors-7EPJRVLJ.js.map} +1 -1
  271. package/dist/connectors-shared/index.js +2 -2
  272. package/dist/core/index.js +7 -6
  273. package/dist/core/manifest.js +2 -2
  274. package/dist/core/models.js +1 -1
  275. package/dist/core/publish-manifest.js +3 -3
  276. package/dist/core/ref.js +1 -1
  277. package/dist/core/runtime-assets.js +5 -5
  278. package/dist/core/src/agent-prompt-sections.d.ts +96 -4
  279. package/dist/core/src/agent-prompt-sections.d.ts.map +1 -1
  280. package/dist/core/src/check-asset.d.ts +115 -0
  281. package/dist/core/src/check-asset.d.ts.map +1 -0
  282. package/dist/core/src/harness-model.d.ts +20 -0
  283. package/dist/core/src/harness-model.d.ts.map +1 -0
  284. package/dist/core/src/index.d.ts +10 -6
  285. package/dist/core/src/index.d.ts.map +1 -1
  286. package/dist/core/src/lock.d.ts +12 -10
  287. package/dist/core/src/lock.d.ts.map +1 -1
  288. package/dist/core/src/models.d.ts +42 -11
  289. package/dist/core/src/models.d.ts.map +1 -1
  290. package/dist/core/src/publish-manifest.d.ts +17 -0
  291. package/dist/core/src/publish-manifest.d.ts.map +1 -1
  292. package/dist/core/src/repo-identity.d.ts +41 -0
  293. package/dist/core/src/repo-identity.d.ts.map +1 -0
  294. package/dist/core/src/repo-manager.d.ts +3 -9
  295. package/dist/core/src/repo-manager.d.ts.map +1 -1
  296. package/dist/core/src/walker.d.ts.map +1 -1
  297. package/dist/core/src/workspace-config.d.ts.map +1 -1
  298. package/dist/core/workspace-config.js +4 -4
  299. package/dist/deploy/index.js +7 -6
  300. package/dist/deploy/index.js.map +1 -1
  301. package/dist/discovery/index.js +5 -5
  302. package/dist/discovery/src/source-config.d.ts.map +1 -1
  303. package/dist/{ensure-sources-FOMEGCQ5.js → ensure-sources-CI3JQJST.js} +32 -30
  304. package/dist/ensure-sources-CI3JQJST.js.map +1 -0
  305. package/dist/factory-assets/connectors/box.js +22 -22
  306. package/dist/factory-assets/connectors/deploy.js +22 -22
  307. package/dist/factory-assets/connectors/devserver.js +22 -22
  308. package/dist/factory-assets/connectors/flow/adapter.js +22 -22
  309. package/dist/factory-assets/connectors/flow/engine.js +8 -7
  310. package/dist/factory-assets/connectors/flow/run-flow.js +24 -24
  311. package/dist/factory-assets/connectors/flow.js +22 -22
  312. package/dist/factory-assets/connectors/git.js +22 -22
  313. package/dist/factory-assets/connectors/gmail.js +22 -22
  314. package/dist/factory-assets/connectors/googledrive.js +22 -22
  315. package/dist/factory-assets/connectors/local.js +22 -22
  316. package/dist/factory-assets/connectors/mattermost.js +22 -22
  317. package/dist/factory-assets/connectors/memory.js +22 -22
  318. package/dist/factory-assets/connectors/minio.js +22 -22
  319. package/dist/factory-assets/connectors/postgres.js +22 -22
  320. package/dist/factory-assets/connectors/s3.js +22 -22
  321. package/dist/factory-assets/connectors/sharepoint.js +22 -22
  322. package/dist/factory-assets/connectors/sqlite.js +22 -22
  323. package/dist/factory-assets/connectors/static-server.js +22 -22
  324. package/dist/factory-assets/connectors/tunnel.js +22 -22
  325. package/dist/factory-assets/connectors/webdav.js +22 -22
  326. package/dist/factory-assets/connectors/xstate-store.js +22 -22
  327. package/dist/factory-assets/connectors/xstate.js +22 -22
  328. package/dist/{flows-J64L5MGJ.js → flows-6XQAYQS4.js} +12 -11
  329. package/dist/{flows-J64L5MGJ.js.map → flows-6XQAYQS4.js.map} +1 -1
  330. package/dist/{helpers-ZJ67FO3Z.js → helpers-26MR5QPO.js} +32 -30
  331. package/dist/{helpers-ZJ67FO3Z.js.map → helpers-26MR5QPO.js.map} +1 -1
  332. package/dist/library/index.js +10 -10
  333. package/dist/library/install.js +4 -4
  334. package/dist/library/src/index.d.ts +1 -1
  335. package/dist/library/src/index.d.ts.map +1 -1
  336. package/dist/library/src/local/db.d.ts.map +1 -1
  337. package/dist/library/src/local/library-manager.d.ts.map +1 -1
  338. package/dist/library/src/local/library.d.ts.map +1 -1
  339. package/dist/library/src/local/schema.d.ts +19 -0
  340. package/dist/library/src/local/schema.d.ts.map +1 -1
  341. package/dist/library/src/user-library.d.ts +18 -0
  342. package/dist/library/src/user-library.d.ts.map +1 -1
  343. package/dist/{library-publish-RGVXGTY4.js → library-publish-F3AKF66O.js} +74 -68
  344. package/dist/library-publish-F3AKF66O.js.map +1 -0
  345. package/dist/model-endpoint/index.js +10 -9
  346. package/dist/{open-library-I5JHL37T.js → open-library-FVV6GG4I.js} +35 -33
  347. package/dist/{open-library-I5JHL37T.js.map → open-library-FVV6GG4I.js.map} +1 -1
  348. package/dist/{open-registry-4HNCWOCN.js → open-registry-VX2BVMS5.js} +16 -16
  349. package/dist/{open-registry-4HNCWOCN.js.map → open-registry-VX2BVMS5.js.map} +1 -1
  350. package/dist/{paths-PXBWY532.js → paths-PIWSSUUG.js} +10 -9
  351. package/dist/{paths-PXBWY532.js.map → paths-PIWSSUUG.js.map} +1 -1
  352. package/dist/{plugin-store-ZC3EGZ3D.js → plugin-store-MVTW6G7J.js} +10 -9
  353. package/dist/{plugin-store-ZC3EGZ3D.js.map → plugin-store-MVTW6G7J.js.map} +1 -1
  354. package/dist/{project-K5JWPIJ6.js → project-GFQP4TW6.js} +50 -48
  355. package/dist/{project-K5JWPIJ6.js.map → project-GFQP4TW6.js.map} +1 -1
  356. package/dist/resolver/index.js +8 -7
  357. package/dist/runner/composition-content.d.ts +2 -0
  358. package/dist/runner/composition-content.js +29 -0
  359. package/dist/runner/composition-content.js.map +1 -0
  360. package/dist/runner/index.js +40 -39
  361. package/dist/runner/src/compaction/prompt.d.ts.map +1 -1
  362. package/dist/runner/src/composition/content-entry.d.ts +15 -0
  363. package/dist/runner/src/composition/content-entry.d.ts.map +1 -0
  364. package/dist/runner/src/composition/content-source.d.ts +7 -0
  365. package/dist/runner/src/composition/content-source.d.ts.map +1 -1
  366. package/dist/runner/src/composition/dry-run.d.ts +5 -1
  367. package/dist/runner/src/composition/dry-run.d.ts.map +1 -1
  368. package/dist/runner/src/composition/index.d.ts +1 -1
  369. package/dist/runner/src/composition/index.d.ts.map +1 -1
  370. package/dist/runner/src/composition/prompt-sections.d.ts +47 -20
  371. package/dist/runner/src/composition/prompt-sections.d.ts.map +1 -1
  372. package/dist/runner/src/context-usage/compose.d.ts.map +1 -1
  373. package/dist/runner/src/session-builder.d.ts +3 -8
  374. package/dist/runner/src/session-builder.d.ts.map +1 -1
  375. package/dist/runner/src/work-practices.d.ts +9 -0
  376. package/dist/runner/src/work-practices.d.ts.map +1 -0
  377. package/dist/sdk/asset-manager.js +17 -15
  378. package/dist/sdk/bridge.js +18 -18
  379. package/dist/sdk/core.js +7 -6
  380. package/dist/sdk/flow.js +8 -7
  381. package/dist/sdk/index.js +40 -39
  382. package/dist/sdk/index.js.map +1 -1
  383. package/dist/sdk/resolver.js +8 -7
  384. package/dist/sdk/runner.js +40 -39
  385. package/dist/sdk/session.js +2 -2
  386. package/dist/sdk/transport/ws/server.js +8 -7
  387. package/dist/sdk/transport/ws.js +8 -7
  388. package/dist/sdk/transport.js +8 -7
  389. package/dist/sdk/types.js +1 -1
  390. package/dist/serve-preflight-XJS5OZML.js +138 -0
  391. package/dist/serve-preflight-XJS5OZML.js.map +1 -0
  392. package/dist/session/index.js +2 -2
  393. package/dist/{setup-RXTNGML5.js → setup-BFEBJLYF.js} +33 -31
  394. package/dist/setup-BFEBJLYF.js.map +1 -0
  395. package/dist/{skills-5D4KDVPJ.js → skills-OCG7B5CL.js} +13 -12
  396. package/dist/{skills-5D4KDVPJ.js.map → skills-OCG7B5CL.js.map} +1 -1
  397. package/dist/source-P6JPZYWP.js +93 -0
  398. package/dist/{source-2LP5W3ST.js.map → source-P6JPZYWP.js.map} +1 -1
  399. package/dist/{source-manifest-publish-EZNCTSQF.js → source-manifest-publish-XBFHEURV.js} +54 -52
  400. package/dist/source-manifest-publish-XBFHEURV.js.map +1 -0
  401. package/dist/transport/index.js +8 -7
  402. package/dist/transport/ws/server.js +8 -7
  403. package/dist/transport/ws.js +8 -7
  404. package/dist/tui/index.js +40 -39
  405. package/dist/tui/index.js.map +1 -1
  406. package/dist/types/index.js +1 -1
  407. package/dist/types/src/context-usage.d.ts +1 -1
  408. package/dist/types/src/events.d.ts +3 -2
  409. package/dist/types/src/events.d.ts.map +1 -1
  410. package/dist/types/src/runtime.d.ts +6 -0
  411. package/dist/types/src/runtime.d.ts.map +1 -1
  412. package/dist/workspace-plugin/adapters/mcp.js +29 -29
  413. package/dist/workspace-plugin/adapters/omp.js +14 -14
  414. package/dist/workspace-plugin/index.js +1 -1
  415. package/package.json +7 -1
  416. package/dist/asset-feeds-DOG5FK7T.js.map +0 -1
  417. package/dist/chunk-3BRBTQ6M.js.map +0 -1
  418. package/dist/chunk-5FO4U3OL.js.map +0 -1
  419. package/dist/chunk-7FXOOUOC.js.map +0 -1
  420. package/dist/chunk-7HRZACI4.js.map +0 -1
  421. package/dist/chunk-7LJUK5JK.js.map +0 -1
  422. package/dist/chunk-7UBCUKJO.js.map +0 -1
  423. package/dist/chunk-7VBD5XUM.js.map +0 -1
  424. package/dist/chunk-A7TM4PZZ.js +0 -110
  425. package/dist/chunk-A7TM4PZZ.js.map +0 -1
  426. package/dist/chunk-B6W5DCH5.js.map +0 -1
  427. package/dist/chunk-BNLXZXZN.js.map +0 -1
  428. package/dist/chunk-CFRHYD4C.js.map +0 -1
  429. package/dist/chunk-DRUAQ7IY.js.map +0 -1
  430. package/dist/chunk-DYH3VSPU.js.map +0 -1
  431. package/dist/chunk-F7WMHDTV.js.map +0 -1
  432. package/dist/chunk-LMUNQYJQ.js.map +0 -1
  433. package/dist/chunk-LZCX2TCZ.js.map +0 -1
  434. package/dist/chunk-MEU2HHGV.js.map +0 -1
  435. package/dist/chunk-MS2IXLTC.js.map +0 -1
  436. package/dist/chunk-OC3AIMZH.js.map +0 -1
  437. package/dist/chunk-RRAXV5LE.js.map +0 -1
  438. package/dist/chunk-YNQRVTXH.js.map +0 -1
  439. package/dist/cli/src/commands/harness-preflight.d.ts +0 -40
  440. package/dist/cli/src/commands/harness-preflight.d.ts.map +0 -1
  441. package/dist/ensure-sources-FOMEGCQ5.js.map +0 -1
  442. package/dist/library-publish-RGVXGTY4.js.map +0 -1
  443. package/dist/serve-preflight-L6VVCDM5.js +0 -61
  444. package/dist/serve-preflight-L6VVCDM5.js.map +0 -1
  445. package/dist/setup-RXTNGML5.js.map +0 -1
  446. package/dist/source-2LP5W3ST.js +0 -91
  447. package/dist/source-manifest-publish-EZNCTSQF.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../bridge/src/logger.ts","../bridge/src/provider-registry-derive.ts","../bridge/src/types.ts"],"names":[],"mappings":"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","file":"chunk-XHLRMUMO.js","sourcesContent":["/**\n * Bridge-layer logger factory.\n *\n * Each harness creates its own Logger via `getBridgeLogger(subkind)` at\n * construction time. Falls back to an off-mode Logger (stdout only) when\n * no LogStore is registered — see @skaile/workspaces/core/logging for details.\n */\n\nimport { createLogger } from \"@skaile/workspaces/core/logging\";\nimport type { Logger } from \"@skaile/workspaces/types\";\n\n/**\n * Construct a bridge-scoped Logger for a given harness subkind.\n *\n * Creates a `Logger` with `kind: \"bridge\"` and the specified `subkind`. Falls back to\n * an off-mode Logger (stdout only) when no `LogStore` is registered — see\n * `@skaile/workspaces/core/logging` for details. Each harness calls this once at construction\n * time; never re-construct per call.\n *\n * @param subkind - Harness id, e.g. `\"claude-sdk\"`, `\"omp\"`, `\"echo\"`, `\"codex\"`.\n * @returns A `Logger` scoped to `bridge:<subkind>`.\n * @docLink packages/bridge/api-reference#logger\n */\nexport function getBridgeLogger(subkind: string): Logger {\n return createLogger({ kind: \"bridge\", subkind });\n}\n","/**\n * The two shaping helpers every registry consumer needs.\n *\n * The legacy provider axis — `provider` / `cloud` / `cloud_config` /\n * `cloudSecrets` — is gone from the runtime, and so is the user-local\n * `settings.json` `apiKeys{}` map: a session carries an AI provider registry\n * and a model selector, nothing else. Credentials are named by a\n * `credential.ref` and resolved into the child environment; no literal secret\n * ever enters an entry.\n */\n\nimport {\n type AIProviderLayerId,\n type AIProviderRegistry,\n type MergedAIProviderRegistry,\n mergeRegistries,\n} from \"@skaile/workspaces/core/provider-registry\";\nimport {\n type RegistryCredentialOptions,\n registryCredentialEnv,\n} from \"./provider-materialize/credential-env.js\";\n\n/** Turn any registry shape into the merged view, without re-merging one. */\nexport function toMergedRegistry(\n registry: AIProviderRegistry | MergedAIProviderRegistry,\n layer: AIProviderLayerId = \"session\",\n): MergedAIProviderRegistry {\n if (Array.isArray((registry as MergedAIProviderRegistry).order)) {\n return registry as MergedAIProviderRegistry;\n }\n return mergeRegistries([{ layer, providers: registry as AIProviderRegistry }]);\n}\n\n/**\n * The `AgentConfig` slice a `HarnessTarget.listModels` closure builds its\n * throwaway harness from: the registry, the selector when the caller named one\n * entry, and the credentials as a child-environment fragment.\n *\n * The credentials have to ride `env`, not a side channel: a `platform:` or\n * `cmd:` reference resolves where the *caller* stands, and this process's own\n * environment cannot see it. `registryCredentialEnv` writes each one under\n * `SKAILE_AIP_<ID>_KEY`, which is the carrier every harness already consults.\n */\nexport function registryHarnessConfig(\n registry: AIProviderRegistry | MergedAIProviderRegistry,\n options: RegistryCredentialOptions = {},\n): { registry: MergedAIProviderRegistry; env: Record<string, string>; modelRef?: string } {\n const merged = toMergedRegistry(registry, \"runtime\");\n return {\n registry: merged,\n env: registryCredentialEnv(merged, options),\n ...(options.providerId ? { modelRef: options.providerId } : {}),\n };\n}\n","import { EventEmitter } from \"node:events\";\nimport type { Span, TelemetryProvider, Trace } from \"@skaile/workspaces/telemetry\";\nimport type {\n AgentPromptInput,\n Capability,\n CredentialMint,\n CodexCredentialDelivery,\n ContextUsageDetail,\n HarnessContextReport,\n HarnessInfo,\n ErrorRetryReason,\n CodexSubscriptionRefreshInput,\n CodexSubscriptionRefreshResult,\n FlowExecution,\n ModelTokenUsage,\n RenderInvokedEvent,\n TokenUsage,\n} from \"@skaile/workspaces/types\";\nimport type { CodexServerRequestHandlers } from \"./codex-app-server.js\";\nexport type { AgentPromptInput } from \"@skaile/workspaces/types\";\nimport type { MergedAIProviderRegistry } from \"@skaile/workspaces/core/provider-registry\";\nimport type { ModelEntry } from \"./models.js\";\n\n/**\n * Test-only hook injected by the runner so a no-LLM harness (the echo harness's\n * scripted-flow mode) can read flow state and drive flow-adapter mutations\n * without an SDK/MCP round-trip. The runner wires this in `createActiveFlow`\n * from the active FlowAdapter + handle.\n *\n * This is an internal runner↔harness interface — NOT part of any wire\n * protocol or flow contract. Production harnesses ignore it.\n *\n * @see packages/bridge/harnesses/echo — scripted-flow mode\n * @since 3.6.0\n */\nexport interface FlowTestHarnessHook {\n /** Current flow execution snapshot, or `null` when no flow is active. */\n getState(): FlowExecution | null;\n /**\n * Execute a flow-adapter operation (`start_node`, `request_approval`,\n * `complete_node`, `request_input`, `skip_node`, `fail_node`, …). Returns\n * the adapter's JSON string result (or rejects on an illegal transition).\n */\n execute(op: string, args: Record<string, unknown>): Promise<string>;\n /**\n * Node IDs marked as mandatory approval gates. The scripted policy resolves\n * `auto` to `request_approval` for these even in autonomous mode, so hosts\n * can drive a mandatory gate deterministically. Optional — absent hooks\n * behave as if no node is mandatory.\n */\n mandatoryGates?(): string[];\n}\n\n/**\n * Minimal LLM tool descriptor used by the capability dispatch path. Mirrors\n * the runner's `LLMTool` shape so harnesses can consume registry output without\n * importing `@skaile/workspaces/runner` (which would create a circular dep).\n *\n * @category Capabilities\n * @since 2.0.0\n * @docLink packages/bridge/concepts#bridge-capability-tool\n */\nexport interface BridgeCapabilityTool {\n /** Capability name; used as the LLM-visible tool name. */\n name: string;\n /** Human/LLM-readable description. */\n description: string;\n /** JSON Schema for the tool's input parameters. */\n parameters: Record<string, unknown>;\n}\n\n/**\n * Render-cap event emission callback used by the bridge when the LLM invokes\n * a capability that carries a `render` spec. The runner provides the actual\n * sink (transport.send / sendEvent); the bridge only emits.\n *\n * @category Capabilities\n * @since 2.0.0\n * @docLink packages/bridge/concepts#bridge-render-emit\n */\nexport type BridgeRenderEmit = (event: RenderInvokedEvent) => void;\n\n/**\n * Text-event emission callback for render-capability fallbacks. When a\n * `RenderCapability` carries `render.fallback` and the LLM invokes it, the\n * bridge substitutes `{{prop}}` placeholders (top-level keys of `props`,\n * optional dotted-path traversal `{{user.name}}`) and pushes the rendered\n * string through this callback so clients without a render layer still see\n * a textual representation. No-op when `fallback` is absent.\n *\n * @category Capabilities\n * @since 2.0.0\n * @docLink packages/bridge/concepts#bridge-text-emit\n */\nexport type BridgeTextEmit = (text: string) => void;\n\n/**\n * Bundle of capability dispatch hooks. Registered into the harness via\n * `AgentConfig.capabilities`; the harness routes registered LLM tool calls\n * back into `invoke()` instead of using its native dispatch path.\n *\n * The runner constructs this from a `CapabilityRegistry` instance and threads\n * it through `createAgentSession()`. Harnesses that don't yet implement\n * capability dispatch ignore the field — legacy v1 paths stay intact.\n *\n * @category Capabilities\n * @since 2.0.0\n * @docLink packages/bridge/concepts#bridge-capability-hooks\n */\nexport interface BridgeCapabilityHooks {\n /** Build the LLM tool descriptor list from the registry. Called per turn so registration changes take effect. */\n composeTools(): BridgeCapabilityTool[];\n /** Resolve a wire-format Capability descriptor by name. Used by the bridge to inspect `fireAndForget` / `render`. */\n resolve(name: string): Capability | null;\n /**\n * Validate input + dispatch through the registry. Logging is wired by the\n * registry.\n *\n * `callId` is the per-dispatch id minted by `dispatchCapability`. It MUST be\n * unique per process (never a harness/provider tool-call id, which only the\n * model backend keeps unique): it is the `render_invoked.callId`, the\n * registry's log correlation id, the handler's `ctx.callId` and — for client\n * capabilities — the `capability_invoked.callId` keying the runner's pending\n * map, so a reused id would let a late `capability_result` for a timed-out\n * call resolve a later call. Callers without one omit it and the registry\n * generates a fresh id.\n */\n invoke(name: string, input: unknown, signal?: AbortSignal, callId?: string): Promise<unknown>;\n /** Subscribe to registry changes; callback carries no credentials or tool payloads. */\n onToolsChanged?(listener: () => void): () => void;\n /** Emit a render-invoked event. The runner forwards via the WebSocket transport. */\n emitRender?: BridgeRenderEmit;\n /** Emit a text fallback for render capabilities with `render.fallback`. */\n emitText?: BridgeTextEmit;\n /** Session id passed through to the runner's per-handler logger. */\n sessionId: string;\n}\n\n/**\n * Static metadata for a registered agent harness (the coding-agent product it drives).\n *\n * Declared once in `@skaile/workspaces/types` — it is protocol surface (the\n * platform reads `surfaces` to decide which AI provider registry entries a\n * harness can serve) and was previously duplicated here field-for-field.\n *\n * Returned by {@link listHarnesses} and exposed on every {@link AgentHarness}\n * instance via `harnessInfo`.\n *\n * @docLink packages/bridge/concepts#harness-info\n */\nexport type { HarnessInfo };\n\n/** Closed, passive managed-MCP observations. Counts saturate at 512. */\nexport interface CodexNativeMcpDiagnostics {\n startupBeforeTurn: \"unknown\" | \"starting\" | \"ready\" | \"failed\" | \"cancelled\";\n startupAtEnd: CodexNativeMcpDiagnostics[\"startupBeforeTurn\"];\n toolsListBeforeTurn: number;\n toolsListTotal: number;\n toolsCallTotal: number;\n nativeMcpToolItemCount: number;\n}\n\n/** Trusted session-local tool transport; fulfilled only after required tools are ready. */\nexport interface CodexToolTransport {\n mcpServers: Record<string, Record<string, unknown>>;\n /** Trusted runner server eligible for human native consent; must name its configured entry. */\n approvalServerName?: string;\n onServerRequest?: CodexServerRequestHandlers;\n /** Revalidate the required live inventory before starting each turn. */\n validate?(): Promise<void>;\n /** Rebuild the native generation at idle when its advertised inventory is stale. */\n needsReload?(): boolean;\n instructions?: string;\n /** Restrict model calls to the active turn and abort outstanding calls when it ends. */\n beginTurn?(signal: AbortSignal): void;\n /** Trusted private server binding and passive request-handler counters; never performs I/O. */\n diagnostics?: {\n serverName: string;\n requestCounts(): { toolsListTotal: number; toolsCallTotal: number };\n };\n dispose(): Promise<void>;\n}\n\n/** Explicit managed instance inputs; none are inferred from ambient developer state. */\nexport interface CodexManagedRuntimeOptions {\n executable: string;\n /** Optional launcher arguments, for the caller-owned executable wrapper. */\n executableArgs?: string[];\n /** Private local filesystem state; the host must fence old native processes before reuse. */\n privateDirectory: string;\n /** Host-owned private mount containing every parent/child instance; denied to native tools. */\n privateRoot: string;\n environment: Record<string, string>;\n credential: CodexCredentialDelivery;\n instructionFiles?: string[];\n skillsDirectory?: string;\n prepareTools(signal: AbortSignal): Promise<CodexToolTransport>;\n /**\n * Passive snapshots before/after turns, plus final local counts after successful transport closure.\n * No final snapshot is emitted on uncertain cleanup or a failed final counter read. Earlier snapshots\n * are not proof of completed cleanup. Sink failures never affect native execution or cleanup.\n */\n onNativeMcpDiagnostics?(value: Readonly<CodexNativeMcpDiagnostics>): void | Promise<void>;\n refreshCredential?: (\n input: CodexSubscriptionRefreshInput,\n signal: AbortSignal,\n ) => Promise<CodexSubscriptionRefreshResult>;\n requestTimeoutMs?: number;\n turnTimeoutMs?: number;\n /** Maximum native turns admitted by this instance; reserved for disposable one-turn children. */\n nativeTurnBudget?: 1;\n /** Human interaction deadline, bounded separately from ordinary RPCs. */\n interactionTimeoutMs?: number;\n}\n\n/**\n * Codex-specific tuning options passed through `HarnessOptions.codex`.\n *\n * @remarks These are forwarded verbatim to the Codex harness and have no effect on other harnesses.\n * @docLink packages/bridge/concepts#codex-harness-options\n */\nexport interface CodexHarnessOptions {\n /** Omission retains the standalone SDK path; managed mode always requires App Server inputs. */\n authSource?: \"local\" | \"managed\";\n managedRuntime?: CodexManagedRuntimeOptions;\n /** Controls whether Codex may auto-apply edits without user approval. */\n approvalPolicy?: \"never\" | \"on-request\" | \"on-failure\" | \"untrusted\";\n /** Filesystem sandbox level for the Codex process. */\n sandboxMode?: \"read-only\" | \"workspace-write\" | \"danger-full-access\";\n /** Reasoning budget passed to the model. */\n reasoningEffort?: \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\";\n /** When `true`, the Codex sandbox is allowed to make outbound network requests. */\n networkAccessEnabled?: boolean;\n /** Extra directories made accessible to the Codex sandbox in addition to `cwd`. */\n additionalDirectories?: string[];\n}\n\n/**\n * omp-specific options passed through `HarnessOptions.omp`.\n *\n * They govern the session-private agent directory the omp adapter renders the\n * provider registry into (`omp-models-yml.ts`); every one of them has a\n * working default, so a caller that sets none still gets a correct session.\n *\n * @remarks Forwarded verbatim to the omp harness; no effect on other harnesses.\n * @see PROVIDER-CONFIG-PLAN.md §2.3\n */\nexport interface OmpHarnessOptions {\n /**\n * Explicit session-private agent directory for `PI_CODING_AGENT_DIR`.\n * Defaults to `<cwd>/.omp/agent-home/<sessionId>`.\n */\n agentHome?: string;\n /**\n * Inherit the developer's own omp state (auth store, blobs, extensions) by\n * symlinking {@link OmpHarnessOptions.userAgentDir} into the session\n * directory. Defaults to *on when that directory exists*, which is exactly\n * the standalone case: a platform container has none, so its session starts\n * empty. Set `false` to force an empty session directory regardless.\n */\n seedUserAgentDir?: boolean;\n /** Seed source. Defaults to `~/.omp/agent`. */\n userAgentDir?: string;\n}\n\n/**\n * Harness-specific configuration bag.\n *\n * Fields are keyed by harness ID so that callers can pass harness-specific options\n * through the common {@link AgentConfig} without breaking other harnesses.\n *\n * @docLink packages/bridge/concepts#harness-options\n */\nexport interface HarnessOptions {\n /** Codex-specific options. Ignored by all other harnesses. */\n codex?: CodexHarnessOptions;\n /** omp-specific options. Ignored by all other harnesses. */\n omp?: OmpHarnessOptions;\n}\n\n/**\n * What the harness tells the host about a turn blocked by an upstream usage\n * limit. Everything but `configId` is best-effort: the SDK reports the limit\n * window only on its structured telemetry, not on the error result text.\n *\n * @see AgentConfig.onLimitBlocked\n * @category Configuration\n * @since 3.9.0\n */\nexport interface LimitBlockedContext {\n /** The AI provider config the blocked turn ran on (`\"\"` when unmediated). */\n configId: string;\n /** Upstream limit window (`five_hour`, `seven_day`, …) when reported. */\n limitType?: string;\n /** Epoch seconds at which the limit window resets, when reported. */\n resetsAt?: number;\n /** Fraction of the limit window consumed (0–1) when reported. */\n utilization?: number;\n}\n\n/**\n * The host's verdict on a limit-blocked turn. `switched: true` is a promise\n * that a different seat's credential is already on disk — the harness acts on it\n * by respawning the CLI and replaying the prompt, so returning it without\n * having rewritten the credential file would just replay onto the dead seat.\n *\n * @see AgentConfig.onLimitBlocked\n * @category Configuration\n * @since 3.9.0\n */\nexport interface LimitFailoverResult {\n /** Whether the session was repointed at a different AI provider config. */\n switched: boolean;\n /** The config id now in effect. Informational — used for logs. */\n configId?: string;\n /**\n * Why no switch happened: `same-config`, `standalone`,\n * `platform-omitted-config-id` (the mint succeeded but named no seat, so a\n * re-resolution is indistinguishable from none), or a `CredentialMint`\n * failure code. Diagnostic only — the harness branches on `switched`.\n */\n reason?: string;\n}\n\n/**\n * One observed AI-provider rejection, reported to the host for per-seat health\n * attribution. Fire-and-forget — see {@link AgentConfig.onProviderResponse}.\n *\n * @category Configuration\n * @since 3.9.0\n */\nexport interface ProviderResponseObservation {\n /** 401 — the credential was rejected. 429 — an upstream rate/session limit. */\n status: 401 | 429;\n /** The AI provider config the observation is attributed to, when known. */\n configId?: string;\n /** Upstream limit window (`five_hour`, `seven_day`, …) when reported. */\n limitType?: string;\n /** Fraction of the limit window consumed (0–1) when reported. */\n utilization?: number;\n /** Epoch seconds at which the limit window resets, when reported. */\n resetsAt?: number;\n /**\n * What the 429 was, when the harness recognised something narrower than a\n * usage limit. `long-context-entitlement`: the account may not use a long\n * (1M) context window; the seat is not out of quota. Diagnostic only — the\n * harness still treats the rejection as before.\n */\n cause?: ProviderResponseCause;\n}\n\n/**\n * Why a 429 {@link ProviderResponseObservation} was rejected, when the harness\n * recognised it. Consumers must tolerate values they do not know.\n *\n * @category Configuration\n * @since 4.11.0\n */\nexport type ProviderResponseCause = \"long-context-entitlement\";\n\n/**\n * One upstream quota-window reading, reported to the host for every SDK\n * `rate_limit_event` — allowed, warning or rejected alike. Fire-and-forget —\n * see {@link AgentConfig.onProviderQuota}.\n *\n * @category Configuration\n * @since 4.1.0\n */\nexport interface ProviderQuotaObservation {\n /** The AI provider config the reading is attributed to, when known. */\n configId?: string;\n /** Upstream window, verbatim (`five_hour`, `seven_day`, or a future value). */\n window: string;\n /** Upstream status, verbatim (`allowed`, `allowed_warning`, `rejected`, …). */\n status: string;\n /** Fraction of the window consumed (0–1); the SDK reports it on warnings and rejections only. */\n utilization?: number;\n /** Epoch seconds at which the window resets, when reported. */\n resetsAt?: number;\n}\n\n/**\n * Reasoning effort levels a host may set. The platform sends all five, the\n * Agent SDK accepts all five, omp has a same-named thinking level for each, and\n * managed codex passes the value to its app server.\n *\n * @since 4.22.0\n */\nexport const AGENT_EFFORT_LEVELS = [\"low\", \"medium\", \"high\", \"xhigh\", \"max\"] as const;\nexport type AgentEffort = (typeof AGENT_EFFORT_LEVELS)[number];\n\n/**\n * Narrow a free-form effort (the wire `agentConfig.effort`, a `skaile.yaml`\n * default) to an {@link AgentEffort}; anything else gives `undefined`, so an\n * unknown value never reaches a harness.\n */\nexport function toAgentEffort(value: unknown): AgentEffort | undefined {\n return isAgentEffort(value) ? value : undefined;\n}\n\nfunction isAgentEffort(value: unknown): value is AgentEffort {\n return (AGENT_EFFORT_LEVELS as readonly unknown[]).includes(value);\n}\n\n/**\n * Configuration passed to a harness when it is created via {@link createHarness}.\n *\n * All fields are shared across harnesses; harness-specific behaviour is documented\n * per field. Fields that are silently ignored by a harness are marked accordingly.\n *\n * @docLink packages/bridge/concepts#agent-config\n */\nexport interface AgentConfig {\n /** Absolute path to the working directory the agent operates in. */\n cwd: string;\n /**\n * LLM model identifier (e.g. `\"claude-sonnet-4-5\"`, `\"gpt-4o\"`).\n *\n * The unscoped fallback: {@link AgentConfig.modelRef} is what a session\n * normally carries, and this is what a harness falls back to when no\n * registry entry resolved one.\n */\n model?: string;\n /**\n * The AI provider registry for this session — every endpoint the harness may\n * address, already merged across the catalog / user / workspace / runtime /\n * control-plane layers and in registry order.\n *\n * This is the *only* provider surface a harness reads. Entries reference\n * their credential (`credential.ref`) and never carry the secret itself;\n * the value reaches the child under `SKAILE_AIP_<ID>_KEY`\n * (`provider-materialize/credential-env.ts`), which the session boundary\n * puts in {@link AgentConfig.env}.\n *\n * Absent (or empty) means no endpoint was configured at all — the harness\n * then falls back to whatever native auth it can discover on its own (a\n * `claude` OAuth login, `codex login`, an ambient vendor key).\n */\n registry?: MergedAIProviderRegistry;\n /**\n * Model selector resolved against {@link AgentConfig.registry}:\n * `\"<providerId>/<modelId>\"`, a bare `\"<modelId>\"` (first registry entry\n * that maps it wins), or `\"@role\"`. Absent when no model was configured at\n * all — the harness then applies its own default, exactly as it does for an\n * absent `model`.\n */\n modelRef?: string;\n /**\n * The `skaile.yaml` `roles` table — role name → model selector\n * (`{default: \"anthropic/opus\", smol: \"groq/llama-3.1-8b\"}`, §9.8). It is\n * what `\"@role\"` in {@link AgentConfig.modelRef} expands through, and what\n * a harness that supports per-role models materializes natively: omp renders\n * it as `modelRoles` in its session-private `config.yml`, so `@smol` inside\n * the session reaches a different provider than `@default`.\n *\n * Resolution is lenient: a role naming an entry this registry does not have\n * is a warning, never a failure.\n */\n roles?: Record<string, string>;\n /**\n * Additional environment variables merged into the child process\n * environment. The session boundary delivers resolved registry credentials\n * here as `SKAILE_AIP_<ID>_KEY` entries; anything else the caller sets is\n * merged over the harness's own allowlist, never instead of it.\n */\n env?: Record<string, string>;\n /** Inline system prompt — written to .omp/system.md and passed via --append-system-prompt (omp harness) */\n systemPrompt?: string;\n /** Path to project .omp/ directory (PI_CODING_AGENT_DIR for omp harness) */\n agentDir?: string;\n /** SSH key path — injected as GIT_SSH_COMMAND */\n sshKeyPath?: string;\n /** Pre-assign a UUID as the session ID for a new session (claude-sdk: passed as sessionId option) */\n sessionId?: string;\n /** Resume this specific past session by UUID instead of starting fresh (claude-sdk: passed as resume option) */\n resumeSessionId?: string;\n /** Maximum agentic turns per query (claude-sdk: passed as maxTurns option) */\n maxTurns?: number;\n /**\n * Whether the agent may act on its own (`auto`, the default) or has to be approved per tool\n * call (`interactive`); the `agents.<id>.permissions.permission_mode` of the instance.\n *\n * - claude-sdk: `auto` runs under the SDK's `bypassPermissions`; `interactive` runs under\n * `default`, and a call the SDK does not auto-allow (a shell command, a file edit, an MCP\n * tool) pauses the turn on a `question` event with an `approval` card (`Approve once` /\n * `Deny`) answered through `answerQuestion`. With {@link AgentConfig.unattended} the call is\n * refused instead.\n * - codex (standalone SDK path): `interactive` sets `approvalPolicy: \"on-request\"` instead of\n * `never`, unless `harnessOptions.codex.approvalPolicy` is set. The standalone SDK path has no\n * way to show an approval card, so codex refuses a command that needs to leave its sandbox\n * instead of asking; nothing is ever approved. A managed codex session keeps the host's own\n * policy.\n * - omp, echo and the subprocess CLI harnesses (gemini, qwen, opencode, goose, continue): ignored.\n */\n permissionMode?: \"auto\" | \"interactive\";\n /**\n * The session has no one to answer a question (a nested `agent.run` role session, a\n * single-shot `skaile run` without a terminal). With `permissionMode: \"interactive\"` a tool\n * call that needs approval is then refused (claude-sdk) instead of waiting for a reply.\n */\n unattended?: boolean;\n /** Agent name — selects a deployed sub-agent definition as the main agent identity.\n * claude-sdk: passed as `agent` option to query() → reads .claude/agents/<name>.md natively.\n * omp: ignored (omp uses agentDir/PI_CODING_AGENT_DIR instead). */\n agentName?: string;\n /** In-process SDK MCP servers for custom tool injection (Claude SDK only, ignored by other harnesses) */\n mcpServers?: Record<string, unknown>;\n /** Tool restrictions from agent.yaml — applied to the main agent session */\n tools?: {\n /** Tool names that the agent is allowed to invoke. An empty array means no restriction. */\n allowed?: string[];\n /** Tool names that the agent is explicitly forbidden from invoking. */\n denied?: string[];\n /**\n * Host tools (Protocol v2 capabilities) the session may see and call, as\n * anchored patterns where `*` is the only wildcard. Independent of\n * `allowed`/`denied`, which cover native tools only. `undefined` leaves host\n * tools unrestricted; an empty array forbids all of them. Enforced by omp.\n */\n host_allowed?: string[];\n };\n /** Thinking mode for Claude models: adaptive (Claude decides), enabled (always think), disabled (no thinking). */\n thinking?: \"adaptive\" | \"enabled\" | \"disabled\";\n /** Reasoning effort level; each harness maps it to its own scale (see {@link AgentEffort}). */\n effort?: AgentEffort;\n /** Harness-specific configuration bag. */\n harnessOptions?: HarnessOptions;\n /**\n * Protocol v2 capability dispatch hooks. When present, the harness uses the\n * registry as the source of LLM tool definitions and routes invocations\n * through `capabilities.invoke()`. Absent → legacy v1 path (existing\n * mcpServers / native dispatch).\n *\n * @since 2.0.0\n */\n capabilities?: BridgeCapabilityHooks;\n /**\n * The platform's `AIProviderConfig.id` for the AI credential currently\n * provisioned into this harness. The runner reads this off `AgentConfig`\n * during 401 mediation and passes it as `configId` in\n * `request_access_token { kind: 'ai-credentials' }`. The harness itself\n * does not consume this field — only the runner uses it.\n *\n * Set by the platform agent-gateway in `ConfigureCommandV2` for mediated\n * sessions. Standalone runners (CLI / forge / Claude plugin) leave it\n * undefined; the runner falls back to surfacing the auth error to the\n * user without mediating.\n *\n * Spec: `_devlog/specs/2026-05-07-unified-credential-mediation.md`\n * § \"Wire `aiProviderConfigId` end-to-end\".\n *\n * @since 3.3.0\n */\n aiProviderConfigId?: string;\n /**\n * Optional callback invoked by the harness when the underlying agent\n * surfaces an `authentication_error`. In Protocol v3 the runner mediates\n * the refresh via the `host.refresh_credential` capability and returns a\n * typed {@link CredentialMint}. The harness inspects the discriminator:\n *\n * - `mint.ok === true`: a fresh credential is now provisioned. The\n * harness re-attempts the in-flight prompt once.\n * - `mint.ok === false`: refresh failed (`code` carries a stable reason\n * such as `revoked`, `not-configured`, `provider-error`,\n * `backend-error`). The harness surfaces the original `AuthError` to\n * the caller.\n *\n * `args.configId` carries the platform `AIProviderConfig.id` the harness\n * was provisioned with (mirrors {@link AgentConfig.aiProviderConfigId}).\n * The runner uses it to scope the refresh to the correct AI credential.\n *\n * When omitted, the harness throws `AuthError` immediately as before\n * (standalone CLI / forge mode — there is no platform mediator to ask).\n *\n * Centralising auth-retry inside the harness means every consumer of\n * `harness.prompt(...)` (serve handler, compaction orchestrator, flow\n * orchestrator, …) gets self-healing for free — there is no per-call-site\n * wrapping.\n *\n * Spec: `_devlog/specs/2026-05-10-deterministic-session-bootstrap.md`\n * § \"host.refresh_credential capability\".\n *\n * @since 3.4.0\n */\n onAuthError?: (args: { configId: string }) => Promise<CredentialMint>;\n /**\n * Optional callback invoked by the harness when a turn is blocked by an\n * upstream **usage limit** rather than a bad credential — the subscription\n * five-hour / seven-day ceiling, or any `rate_limit` the classifier\n * recognises. Called at most once per logical turn, and only after the\n * blocked attempt has already failed, so it never runs mid-turn.\n *\n * The runner implements it by asking the platform to re-resolve the seat:\n *\n * - `switched: true` — the session was repointed at a DIFFERENT AI\n * provider config and `.credentials.json` was rewritten. The harness\n * drops its query (forcing a fresh CLI spawn, which is what clears a\n * resident CLI's cached \"blocked until <reset>\" state — a hot file\n * rewrite alone does not) and replays the prompt once.\n * - `switched: false` — no alternative seat was available. The harness\n * surfaces the original limit error unchanged.\n *\n * Left unset by standalone runners (CLI / forge / Claude plugin): there is\n * no platform mediator to re-resolve a seat, so the limit error surfaces\n * exactly as it does today.\n *\n * Companion to {@link onAuthError}: that one covers 401s, this one covers\n * the rejection path, which previously reached no failover at all\n * (skaile-ai/workspaces#567, skaile-ai/platform#2976).\n *\n * @since 3.9.0\n */\n onLimitBlocked?: (ctx: LimitBlockedContext) => Promise<LimitFailoverResult>;\n /**\n * Optional fire-and-forget hook invoked when the harness observes a 401 or a\n * 429 / subscription-limit rejection from the AI provider. The runner\n * forwards it to the platform as a `provider_response_seen` event so the\n * owning `AIProviderConfig`'s live health counters can be bumped — the cron\n * health probe only samples auth, never capacity\n * (skaile-ai/workspaces#368, skaile-ai/platform#3539).\n *\n * Never awaited and never retried; the harness swallows a throwing\n * implementation so telemetry can never fail a turn.\n *\n * @since 3.9.0\n */\n onProviderResponse?: (observation: ProviderResponseObservation) => void;\n /**\n * Optional fire-and-forget hook invoked for every upstream quota-window\n * reading (claude-sdk `rate_limit_event`), not only rejections. The runner\n * forwards it as `provider_quota_observed`, which is the only quota signal\n * the platform has for a seat whose token cannot read the OAuth usage\n * endpoint (a `claude setup-token` seat).\n *\n * Never awaited and never retried; the harness swallows a throwing\n * implementation so telemetry can never fail a turn. Unlike\n * {@link onProviderResponse} it fires on every event, so dedupe at the host.\n *\n * @since 4.1.0\n */\n onProviderQuota?: (observation: ProviderQuotaObservation) => void;\n /**\n * Test-only flow harness hook. When present, the echo harness's scripted-flow\n * mode uses it to advance flow nodes deterministically (no LLM). The runner\n * injects it in `createActiveFlow`; every other harness ignores it.\n *\n * @see FlowTestHarnessHook\n * @since 3.6.0\n */\n flowTestHarness?: FlowTestHarnessHook;\n}\n\n/**\n * A single message in an agent conversation.\n *\n * Emitted as part of {@link AgentEvent} variants (`message_start`, `message_update`, `message_end`).\n *\n * @docLink packages/bridge/concepts#agent-message\n */\nexport interface AgentMessage {\n /** Originator of the message. */\n role: \"user\" | \"assistant\" | \"tool\";\n /** Message body — either a plain string or a list of typed content blocks. */\n content: string | ContentBlock[];\n /** Tool invocations made by the assistant in this message. */\n toolCalls?: ToolCall[];\n /** For `role === \"tool\"`: the name of the tool whose result this message carries. */\n toolName?: string;\n /** For `role === \"tool\"`: `true` when the tool returned an error. */\n isError?: boolean;\n /** Optional structured payload for tool results. */\n data?: unknown;\n /**\n * Set when a subagent produced this message: the id of the tool use that\n * runs the subagent. Absent on main-thread messages.\n */\n parentToolUseId?: string;\n}\n\n/**\n * A typed block within an {@link AgentMessage}'s content array.\n *\n * Mirrors the Anthropic content block schema but is harness-agnostic.\n *\n * @docLink packages/bridge/concepts#content-block\n */\nexport interface ContentBlock {\n /** Block discriminant. */\n type: \"text\" | \"tool_use\" | \"tool_result\" | \"thinking\";\n /** Present for `text` and `thinking` blocks. */\n text?: string;\n /** Tool use / tool result correlation ID. */\n id?: string;\n /** Tool name (present on `tool_use` blocks). */\n name?: string;\n /** Tool input arguments (present on `tool_use` blocks). */\n input?: any;\n /** Serialised tool result content (present on `tool_result` blocks). */\n content?: string;\n}\n\n/**\n * A single tool invocation made by the agent within a message.\n *\n * @docLink packages/bridge/concepts#tool-call\n */\nexport interface ToolCall {\n /** Correlation ID used to match the call with its result. */\n id: string;\n /** Name of the tool being invoked. */\n name: string;\n /** Arguments passed to the tool. */\n input: any;\n}\n\n/**\n * Error classification for agent failures.\n *\n * Used to determine whether a failure is retryable and to surface actionable\n * hints to the user. See `bridge/CLAUDE.md` for the full category-to-behaviour matrix.\n *\n * @see AgentError\n * @docLink packages/bridge/concepts#error-category\n */\nexport type ErrorCategory =\n | \"auth\"\n | \"account\"\n | \"rate_limit\"\n | \"model\"\n | \"network\"\n | \"config\"\n | \"process\"\n | \"validation\"\n | \"unknown\";\n\n/**\n * Structured error payload emitted inside the `error` {@link AgentEvent}.\n *\n * Consumers should inspect `retryable` before deciding to surface a retry button,\n * and `hint` to provide an actionable message to the user.\n *\n * @docLink packages/bridge/concepts#agent-error\n */\nexport interface AgentError {\n /** Human-readable error description. */\n message: string;\n /** Coarse failure classification used for retry logic and telemetry. */\n category: ErrorCategory;\n /** HTTP status code if the error originated from an API response. */\n statusCode?: number;\n /** `true` when the caller may safely retry the same operation. */\n retryable: boolean;\n /** Short actionable advice suitable for display in the UI. */\n hint?: string;\n}\n\n/**\n * Metadata for a slash command exposed by the active agent runtime.\n *\n * @docLink packages/bridge/concepts#slash-command-info\n */\nexport interface SlashCommandInfo {\n /** Command name without the leading slash (e.g. `\"compact\"`). */\n name: string;\n /** One-line description shown in command pickers. */\n description: string;\n /** Placeholder text describing what argument the command expects. */\n argumentHint?: string;\n}\n\n/**\n * Discriminated union of all events emitted by a harness on the `'agent-event'` channel.\n *\n * @remarks\n * Consumers listen via `harness.on('agent-event', (event: AgentEvent) => ...)`.\n * The `[k: string]: any` index signature on most variants allows harnesses to attach\n * harness-specific fields (e.g. `_textDelta`) without breaking the union.\n *\n * @docLink packages/bridge/concepts#agent-event\n */\nexport type AgentEvent =\n | { type: \"message_start\"; message: AgentMessage; [k: string]: any }\n | { type: \"message_update\"; message: AgentMessage; [k: string]: any }\n | { type: \"message_end\"; message: AgentMessage; [k: string]: any }\n /**\n * Takes back `text`, the unfinished tail of streamed `_textDelta`s, before the\n * harness replays an abandoned attempt. Always a suffix of what was streamed\n * since the last completed assistant message; see `TextRetractEvent` in the types package.\n */\n | { type: \"message_retract\"; text: string; [k: string]: any }\n | { type: \"turn_end\"; toolResults?: AgentMessage[]; [k: string]: any }\n | { type: \"agent_end\"; [k: string]: any }\n /** A background task started in the running turn; see `BackgroundTaskStartedEvent`. */\n | { type: \"background_task_started\"; taskId: string; [k: string]: any }\n /** A turn the harness was not prompted for began; see `SelfTurnStartedEvent`. */\n | { type: \"self_turn_started\"; taskIds: string[]; [k: string]: any }\n /** A background task settled. Raw only: the runner's turn-visibility stamp reads it, no client does. */\n | { type: \"background_task_settled\"; taskId: string; [k: string]: any }\n /** The CLI process is gone, and every background task with it. Raw only, like the above. */\n | { type: \"background_tasks_reset\"; [k: string]: any }\n | {\n type: \"result\";\n subtype: string;\n summary?: string;\n costUsd?: number;\n /** Per-turn token usage when the harness tracks it. Added in 3.1.0. */\n tokens?: TokenUsage;\n /** Per-turn usage keyed by model, subagent calls included. Added in 3.11.0. */\n modelUsage?: Record<string, ModelTokenUsage>;\n /** Prompt size of the turn's last main-thread API call. Added in 3.11.0. */\n contextTokens?: number;\n /** Context window of the main model, in tokens. Added in 3.11.0. */\n contextWindow?: number;\n errors?: string[];\n [k: string]: any;\n }\n | {\n type: \"error\";\n error: string;\n detail?: AgentError;\n fatal?: boolean;\n /** Set (with `fatal: false`) when the harness replays the prompt in place; see `ErrorEvent.retrying`. */\n retrying?: boolean;\n retryReason?: ErrorRetryReason;\n /** Usage of a failed provider turn, when reported. */\n tokens?: TokenUsage;\n /** Failed-turn usage keyed by the actual model. */\n modelUsage?: Record<string, ModelTokenUsage>;\n contextTokens?: number;\n contextWindow?: number;\n [k: string]: any;\n }\n | {\n type: \"tool_call\";\n name?: string;\n tool?: { name: string };\n /** Set when a subagent made the call. */\n parentToolUseId?: string;\n [k: string]: any;\n }\n | { type: \"tool_execution_end\"; toolName?: string; parentToolUseId?: string; [k: string]: any }\n | { type: \"commands_available\"; commands: SlashCommandInfo[] }\n | { type: \"session_info\"; harnessSessionId: string; sessionFile?: string }\n | { type: \"ui_render\"; [k: string]: any }\n | { type: \"ui_render_update\"; [k: string]: any }\n | { type: \"ui_clear\"; [k: string]: any }\n // Phase 3 (resume cascade) — harness-emitted resume outcome.\n | {\n type: \"resume_failed\";\n resumeSessionId: string;\n reason:\n | \"signature_mismatch\"\n | \"model_mismatch\"\n | \"jsonl_lost\"\n | \"jsonl_poisoned\"\n | \"native_state_unavailable\";\n [k: string]: any;\n };\n\n/** The settings a live reconfigure may change. */\nexport type ReconfigurePatch = Partial<Pick<AgentConfig, \"model\" | \"thinking\" | \"effort\">>;\n\n/**\n * What a harness now holds for the fields of an accepted reconfigure. `modelRef`\n * is the registry selector the model was scoped to; it is absent when the\n * session has none. A host remembers exactly this, never the raw patch.\n */\nexport interface ReconfigureApplied {\n model?: string;\n modelRef?: string;\n thinking?: AgentConfig[\"thinking\"];\n effort?: AgentConfig[\"effort\"];\n}\n\n/**\n * Result of `AgentHarness.reconfigure()`. `ok: true` means effective from the\n * next turn (live switch, query recycle or respawn); a harness that cannot\n * promise that answers `ok: false` instead.\n *\n * - `not_supported`: the harness cannot change settings live.\n * - `model_not_on_provider`: the session's AI provider entry does not list the\n * model (`providerId` names the entry).\n * - `rejected`: the running process refused the change; the harness config is\n * unchanged.\n *\n * @category Bridge\n */\nexport type ReconfigureOutcome =\n | { ok: true; applied: ReconfigureApplied }\n | {\n ok: false;\n code: \"not_supported\" | \"model_not_on_provider\" | \"rejected\";\n message: string;\n providerId?: string;\n };\n\n/**\n * A reconfigure the harness answered `ok` (it landed mid-turn) and then refused\n * at the next turn boundary. The harness already rolled its config back;\n * `requested` is what was asked, `restored` what it holds again. Emitted as the\n * harness event `'reconfigure-rollback'`.\n */\nexport interface ReconfigureRollback {\n message: string;\n requested: ReconfigureApplied;\n restored: ReconfigureApplied;\n}\n\n/** Rejection message of a harness asked to run a turn under a tool allowlist it cannot enforce. */\nexport const TOOL_ALLOWLIST_UNSUPPORTED = \"This agent harness cannot enforce a tool allowlist\";\n\n/**\n * Abstract base class for agent harnesses.\n *\n * Wraps an LLM coding agent behind a single `prompt()` interface.\n * All harnesses emit `'agent-event'` with {@link AgentEvent} payloads.\n *\n * @remarks\n * Subclasses must implement `start`, `prompt`, `abort`, `kill`, and `isRunning`.\n * Implementations should never emit an event name other than `'agent-event'` as\n * the public streaming channel — internal events (`'ready'`, `'exit'`, etc.) are\n * harness-private. The one other public event is `'reconfigure-rollback'` (a\n * {@link ReconfigureRollback}): a live reconfigure the harness had accepted and\n * then refused at the turn boundary, already rolled back in the harness.\n *\n * @example\n * ```ts\n * const harness = createHarness('omp', config);\n * harness.on('agent-event', (event) => console.log(event.type));\n * await harness.start();\n * await harness.prompt('Refactor the auth module');\n * harness.kill();\n * ```\n *\n * @docLink packages/bridge/concepts#agent-harness\n */\nexport abstract class AgentHarness extends EventEmitter {\n /** Static metadata describing this harness's capabilities. */\n abstract readonly harnessInfo: HarnessInfo;\n\n /**\n * Initialises the harness — spawns the child process (omp) or loads\n * the SDK module (claude-sdk). Resolves when the harness is ready to accept\n * prompts. Idempotent: calling `start()` on an already-running harness is a no-op.\n *\n * @throws {Error} When the harness binary is missing or the SDK cannot be loaded.\n */\n abstract start(): Promise<void>;\n\n /**\n * Sends a user message to the agent and resolves when the agent's turn completes\n * (i.e. after the `agent_end` event has been emitted).\n *\n * @param message - Plain-text user prompt to send to the agent.\n * @throws {Error} When the harness is not running or the underlying coding agent reports a fatal error.\n */\n abstract prompt(message: string): Promise<void>;\n\n /**\n * Preserve text-only callers; harnesses must explicitly support attachments.\n * A `toolAllowlist` is refused here: only a harness that overrides\n * {@link enforcesToolAllowlist} may run a restricted turn.\n */\n promptInput(input: AgentPromptInput): Promise<void> {\n if (input.toolAllowlist !== undefined)\n return Promise.reject(new Error(TOOL_ALLOWLIST_UNSUPPORTED));\n if (input.attachments?.length) return Promise.reject(new Error(\"Structured input unsupported\"));\n return this.prompt(input.text);\n }\n\n /**\n * Whether {@link promptInput} enforces `AgentPromptInput.toolAllowlist` for\n * the whole turn, subagents included. A harness that answers `false` must\n * reject an input carrying one; the runner refuses such a prompt before it\n * reaches the harness.\n */\n get enforcesToolAllowlist(): boolean {\n return false;\n }\n\n /**\n * The names in `allowlist` this harness does not recognize as a tool it could\n * offer. The runner refuses a prompt whose allowlist names any, rather than\n * guess. A harness that does not enforce allowlists recognizes none.\n */\n unknownAllowlistedTools(allowlist: readonly string[]): string[] {\n return [...allowlist];\n }\n\n /** Managed replies never fall back to a new prompt after a request has settled. */\n get acceptsReplyAsPrompt(): boolean {\n return true;\n }\n\n /**\n * Sends an in-band abort signal to the agent, requesting it to stop the current\n * turn without terminating the process. The harness remains usable after `abort()`.\n *\n * @remarks Only meaningful when `harnessInfo.supportsInBandAbort` is `true`.\n * For other harnesses, prefer `kill()` followed by creating a new harness instance.\n */\n abstract abort(): Promise<void>;\n\n /**\n * Terminates the agent harness immediately (SIGTERM for subprocess harnesses,\n * `close()` for in-process harnesses). The harness instance must not be reused\n * after `kill()`.\n */\n abstract kill(): void;\n\n /** Stop the harness and await owned process/state cleanup when the harness supports it. */\n async dispose(): Promise<void> {\n this.kill();\n }\n\n /** `true` when the harness is alive and able to accept new prompts. */\n abstract get isRunning(): boolean;\n\n /** Optional provider-native session/thread identifier for resume support. */\n get runtimeSessionId(): string | undefined {\n return undefined;\n }\n\n /** Slash commands discovered from the agent runtime. Override in harnesses that support introspection. */\n getSlashCommands(): SlashCommandInfo[] {\n return [];\n }\n\n // ── Interactive questions (AskUserQuestion) ──────────────────────────────\n\n /**\n * `true` while the harness is paused on an interactive question and waiting\n * for the user's answer rather than a new prompt. The runner routes a\n * `reply` command to {@link answerQuestion} instead of {@link prompt} when\n * this returns `true`. Harnesses without interactive-question support return\n * `false` (the default), so a `reply` falls back to `prompt`.\n */\n hasPendingQuestion(): boolean {\n return false;\n }\n\n /**\n * Deliver the user's answer to the question the harness is currently paused\n * on. This resumes the in-flight turn — it does NOT start a new one. When a\n * single tool call asks several questions, the harness resolves the underlying\n * call only once every sub-question has an answer.\n *\n * @param answer - The user's reply text (an option label, free text, or for\n * multi-select a comma-joined list).\n * @param question - The sub-question this answers, verbatim from the\n * `question` event that asked it. Supply it whenever the host lets the user\n * answer out of order — without it the answer fills the next unanswered\n * slot in order, which silently mismatches a non-sequential reply.\n * @returns `true` when the answer was consumed by a pending question; `false`\n * when no question was pending (caller should treat the reply as a prompt).\n */\n answerQuestion(answer: string, question?: string, requestId?: string): boolean {\n void requestId;\n void answer;\n void question;\n return false;\n }\n\n /**\n * Apply this harness's rule for a new prompt that meets a pending question,\n * ahead of the prompt itself. The runner queues a prompt behind the turn in\n * flight, so a turn paused on a question would otherwise hold that prompt\n * until the user answered. Harnesses whose `prompt()` cancels the question\n * (claude-sdk, omp) do the same here; the default keeps the question.\n */\n supersedePendingQuestion(): void {\n // No-op: a harness without this rule keeps the question pending.\n }\n\n // ── Live reconfiguration ────────────────────────────────────────────────\n\n /**\n * Live-update harness configuration. `ok: true` means effective from the next\n * turn: the next turn runs on the new settings, by live switch, query recycle\n * or respawn, and the host needs no restart. A turn in progress finishes on\n * the old settings. Only model/thinking/effort are reconfigurable mid-session.\n *\n * Harnesses that can honour that promise build a `ReconfigureLedger` and\n * delegate to it; it owns the model scope rule, ordering, drift and rollback.\n * The base implementation answers `not_supported`.\n */\n reconfigure(patch: ReconfigurePatch): Promise<ReconfigureOutcome> {\n const fields = Object.keys(patch).join(\", \");\n return Promise.resolve({\n ok: false,\n code: \"not_supported\",\n message: `The ${this.harnessInfo.id} harness cannot change ${fields} live; the change applies when the session next starts.`,\n });\n }\n\n /**\n * Returns the model identifier currently configured on this harness.\n * Subclasses that store config locally should override this.\n */\n getModel(): string | undefined {\n return undefined;\n }\n\n // ── Compaction support ──────────────────────────────────────────────────\n\n /**\n * `Date.now()` of the harness's most recent sign of life, counting messages\n * that emit no `agent-event` (thinking deltas, rate-limit telemetry, API\n * retries). A stall check reads it alongside the events it sees. `null` when\n * the harness does not track it.\n */\n lastActivityAt(): number | null {\n return null;\n }\n\n /**\n * Returns the token usage from the most recent completed turn.\n * Returns `null` if usage data is unavailable (e.g. harness doesn't track it).\n *\n * Widened in 3.1.0 to return the full `TokenUsage` shape (input / output /\n * cache-read / cache-creation / reasoning). All fields are optional so\n * harnesses can populate just what their provider reports. Existing callers\n * that read only `inputTokens` / `outputTokens` keep working — those fields\n * are still present and carry the same semantics as before.\n */\n getTokenUsage(): TokenUsage | null {\n return null;\n }\n\n /**\n * Prompt tokens occupying the context window as of the most recent API\n * request — i.e. how full the context actually is right now.\n *\n * Deliberately NOT the same number as {@link getTokenUsage}, whose usage is\n * cumulative over every API call in the turn and therefore overstates the\n * context by 10-40x on any multi-tool turn. Callers measuring context fill\n * (compaction thresholds) must prefer this; callers billing a turn\n * (usage ledgers, the `finished` event) must keep using `getTokenUsage()`.\n *\n * Returns `null` both when the harness cannot report a per-call figure at all\n * and when it can but has no reading yet. Falling back to `getTokenUsage()`\n * is safe in either case only because a harness clears the two in lockstep: no\n * reading means no API call landed, so there is no cumulative usage to\n * overstate. A harness implementing this must preserve that invariant.\n */\n getContextTokens(): number | null {\n return null;\n }\n\n /**\n * Returns the model's context window size in tokens.\n * Returns `null` if unknown.\n */\n getContextWindow(): number | null {\n return null;\n }\n\n /**\n * What the harness can measure natively about its context window, in the\n * neutral {@link HarnessContextReport} shape, or `null` when it measures\n * nothing beyond {@link getContextTokens} / {@link getContextWindow}.\n *\n * `detail: \"full\"` (the default) may be expensive — claude-sdk issues\n * `count_tokens` requests — so the runner asks for it only when a host\n * requests a measured breakdown; `\"summary\"` must make no provider call and\n * is what the per-turn snapshot uses. Implementations must not throw for\n * \"unsupported\"; a rejection means the measurement itself failed and the\n * runner falls back to estimates.\n */\n async getContextUsageReport(_opts?: {\n detail?: ContextUsageDetail;\n }): Promise<HarnessContextReport | null> {\n return null;\n }\n\n /**\n * Returns the list of models available to this harness without starting a session.\n * Harnesses that can enumerate their own models override this method.\n * Returns an empty array by default.\n */\n async listModels(): Promise<ModelEntry[]> {\n return [];\n }\n\n /**\n * Closes the current conversation session and prepares for a fresh start.\n * The next `prompt()` call will begin a new session without prior history.\n * The harness remains usable after `resetSession()`.\n */\n abstract resetSession(): Promise<void>;\n\n // ── Telemetry ─────────────────────────────────────────────────────────────\n\n protected _telemetry?: TelemetryProvider;\n protected _activeTrace?: Trace;\n protected _activeTurnSpan?: Span;\n private _turnStartTime?: number;\n\n /**\n * Attach a telemetry provider and active trace to this harness.\n * Called by the runner after creating the harness but before start().\n */\n setTelemetry(provider: TelemetryProvider, trace: Trace): void {\n this._telemetry = provider;\n this._activeTrace = trace;\n }\n\n /**\n * Begin a turn span. Called by the runner before prompt().\n * Pass a parent span (e.g. the turn span from the orchestrator) for nesting.\n * Returns the span so the runner can annotate it further.\n */\n beginTurnSpan(\n parent?: Trace | Span,\n attrs?: Record<string, string | number | boolean>,\n ): Span | undefined {\n if (!this._telemetry || !this._activeTrace) return undefined;\n this._turnStartTime = Date.now();\n this._activeTurnSpan = this._telemetry.startSpan(parent ?? this._activeTrace, {\n name: \"llm_generation\",\n kind: \"llm_generation\",\n attributes: attrs,\n });\n return this._activeTurnSpan;\n }\n\n /**\n * End the current turn span with generation data.\n * Called by the runner after agent_end. Model and provider are passed\n * explicitly — the harness base class does not access config directly.\n */\n endTurnSpan(result?: {\n model?: string;\n provider?: string;\n inputTokens?: number;\n outputTokens?: number;\n stopReason?: string;\n error?: string;\n }): void {\n if (!this._telemetry || !this._activeTurnSpan) return;\n const durationMs = this._turnStartTime ? Date.now() - this._turnStartTime : 0;\n\n if (result?.inputTokens !== undefined || result?.outputTokens !== undefined) {\n this._telemetry.logGeneration(this._activeTurnSpan, {\n model: result?.model ?? \"unknown\",\n provider: result?.provider ?? \"unknown\",\n inputTokens: result.inputTokens,\n outputTokens: result.outputTokens,\n durationMs,\n stopReason: result.stopReason,\n });\n }\n\n this._telemetry.endSpan(this._activeTurnSpan, {\n status: result?.error ? \"error\" : \"ok\",\n error: result?.error,\n attributes: { \"skaile.turn.duration_ms\": durationMs },\n });\n\n this._activeTurnSpan = undefined;\n this._turnStartTime = undefined;\n }\n}\n\n/**\n * Factory function signature registered in the harness registry.\n *\n * @param config - Configuration for the new harness instance.\n * @returns A freshly constructed (not yet started) {@link AgentHarness}.\n * @docLink packages/bridge/concepts#harness-factory\n */\nexport type HarnessFactory = (config: AgentConfig) => AgentHarness;\n"]}
1
+ {"version":3,"sources":["../bridge/src/logger.ts","../bridge/src/provider-registry-derive.ts","../bridge/src/types.ts"],"names":[],"mappings":"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","file":"chunk-NAQXDTF3.js","sourcesContent":["/**\n * Bridge-layer logger factory.\n *\n * Each harness creates its own Logger via `getBridgeLogger(subkind)` at\n * construction time. Falls back to an off-mode Logger (stdout only) when\n * no LogStore is registered — see @skaile/workspaces/core/logging for details.\n */\n\nimport { createLogger } from \"@skaile/workspaces/core/logging\";\nimport type { Logger } from \"@skaile/workspaces/types\";\n\n/**\n * Construct a bridge-scoped Logger for a given harness subkind.\n *\n * Creates a `Logger` with `kind: \"bridge\"` and the specified `subkind`. Falls back to\n * an off-mode Logger (stdout only) when no `LogStore` is registered — see\n * `@skaile/workspaces/core/logging` for details. Each harness calls this once at construction\n * time; never re-construct per call.\n *\n * @param subkind - Harness id, e.g. `\"claude-sdk\"`, `\"omp\"`, `\"echo\"`, `\"codex\"`.\n * @returns A `Logger` scoped to `bridge:<subkind>`.\n * @docLink packages/bridge/api-reference#logger\n */\nexport function getBridgeLogger(subkind: string): Logger {\n return createLogger({ kind: \"bridge\", subkind });\n}\n","/**\n * The two shaping helpers every registry consumer needs.\n *\n * The legacy provider axis — `provider` / `cloud` / `cloud_config` /\n * `cloudSecrets` — is gone from the runtime, and so is the user-local\n * `settings.json` `apiKeys{}` map: a session carries an AI provider registry\n * and a model selector, nothing else. Credentials are named by a\n * `credential.ref` and resolved into the child environment; no literal secret\n * ever enters an entry.\n */\n\nimport {\n type AIProviderLayerId,\n type AIProviderRegistry,\n type MergedAIProviderRegistry,\n mergeRegistries,\n} from \"@skaile/workspaces/core/provider-registry\";\nimport {\n type RegistryCredentialOptions,\n registryCredentialEnv,\n} from \"./provider-materialize/credential-env.js\";\n\n/** Turn any registry shape into the merged view, without re-merging one. */\nexport function toMergedRegistry(\n registry: AIProviderRegistry | MergedAIProviderRegistry,\n layer: AIProviderLayerId = \"session\",\n): MergedAIProviderRegistry {\n if (Array.isArray((registry as MergedAIProviderRegistry).order)) {\n return registry as MergedAIProviderRegistry;\n }\n return mergeRegistries([{ layer, providers: registry as AIProviderRegistry }]);\n}\n\n/**\n * The `AgentConfig` slice a `HarnessTarget.listModels` closure builds its\n * throwaway harness from: the registry, the selector when the caller named one\n * entry, and the credentials as a child-environment fragment.\n *\n * The credentials have to ride `env`, not a side channel: a `platform:` or\n * `cmd:` reference resolves where the *caller* stands, and this process's own\n * environment cannot see it. `registryCredentialEnv` writes each one under\n * `SKAILE_AIP_<ID>_KEY`, which is the carrier every harness already consults.\n */\nexport function registryHarnessConfig(\n registry: AIProviderRegistry | MergedAIProviderRegistry,\n options: RegistryCredentialOptions = {},\n): { registry: MergedAIProviderRegistry; env: Record<string, string>; modelRef?: string } {\n const merged = toMergedRegistry(registry, \"runtime\");\n return {\n registry: merged,\n env: registryCredentialEnv(merged, options),\n ...(options.providerId ? { modelRef: options.providerId } : {}),\n };\n}\n","import { EventEmitter } from \"node:events\";\nimport type { Span, TelemetryProvider, Trace } from \"@skaile/workspaces/telemetry\";\nimport type {\n AgentPromptInput,\n Capability,\n CredentialMint,\n CodexCredentialDelivery,\n ContextUsageDetail,\n HarnessContextReport,\n HarnessInfo,\n ErrorRetryReason,\n CodexSubscriptionRefreshInput,\n CodexSubscriptionRefreshResult,\n FlowExecution,\n ModelTokenUsage,\n RenderInvokedEvent,\n TokenUsage,\n} from \"@skaile/workspaces/types\";\nimport type { CodexServerRequestHandlers } from \"./codex-app-server.js\";\nexport type { AgentPromptInput } from \"@skaile/workspaces/types\";\nimport type { MergedAIProviderRegistry } from \"@skaile/workspaces/core/provider-registry\";\nimport type { ModelEntry } from \"./models.js\";\n\n/**\n * Test-only hook injected by the runner so a no-LLM harness (the echo harness's\n * scripted-flow mode) can read flow state and drive flow-adapter mutations\n * without an SDK/MCP round-trip. The runner wires this in `createActiveFlow`\n * from the active FlowAdapter + handle.\n *\n * This is an internal runner↔harness interface — NOT part of any wire\n * protocol or flow contract. Production harnesses ignore it.\n *\n * @see packages/bridge/harnesses/echo — scripted-flow mode\n * @since 3.6.0\n */\nexport interface FlowTestHarnessHook {\n /** Current flow execution snapshot, or `null` when no flow is active. */\n getState(): FlowExecution | null;\n /**\n * Execute a flow-adapter operation (`start_node`, `request_approval`,\n * `complete_node`, `request_input`, `skip_node`, `fail_node`, …). Returns\n * the adapter's JSON string result (or rejects on an illegal transition).\n */\n execute(op: string, args: Record<string, unknown>): Promise<string>;\n /**\n * Node IDs marked as mandatory approval gates. The scripted policy resolves\n * `auto` to `request_approval` for these even in autonomous mode, so hosts\n * can drive a mandatory gate deterministically. Optional — absent hooks\n * behave as if no node is mandatory.\n */\n mandatoryGates?(): string[];\n}\n\n/**\n * Minimal LLM tool descriptor used by the capability dispatch path. Mirrors\n * the runner's `LLMTool` shape so harnesses can consume registry output without\n * importing `@skaile/workspaces/runner` (which would create a circular dep).\n *\n * @category Capabilities\n * @since 2.0.0\n * @docLink packages/bridge/concepts#bridge-capability-tool\n */\nexport interface BridgeCapabilityTool {\n /** Capability name; used as the LLM-visible tool name. */\n name: string;\n /** Human/LLM-readable description. */\n description: string;\n /** JSON Schema for the tool's input parameters. */\n parameters: Record<string, unknown>;\n}\n\n/**\n * Render-cap event emission callback used by the bridge when the LLM invokes\n * a capability that carries a `render` spec. The runner provides the actual\n * sink (transport.send / sendEvent); the bridge only emits.\n *\n * @category Capabilities\n * @since 2.0.0\n * @docLink packages/bridge/concepts#bridge-render-emit\n */\nexport type BridgeRenderEmit = (event: RenderInvokedEvent) => void;\n\n/**\n * Text-event emission callback for render-capability fallbacks. When a\n * `RenderCapability` carries `render.fallback` and the LLM invokes it, the\n * bridge substitutes `{{prop}}` placeholders (top-level keys of `props`,\n * optional dotted-path traversal `{{user.name}}`) and pushes the rendered\n * string through this callback so clients without a render layer still see\n * a textual representation. No-op when `fallback` is absent.\n *\n * @category Capabilities\n * @since 2.0.0\n * @docLink packages/bridge/concepts#bridge-text-emit\n */\nexport type BridgeTextEmit = (text: string) => void;\n\n/**\n * Bundle of capability dispatch hooks. Registered into the harness via\n * `AgentConfig.capabilities`; the harness routes registered LLM tool calls\n * back into `invoke()` instead of using its native dispatch path.\n *\n * The runner constructs this from a `CapabilityRegistry` instance and threads\n * it through `createAgentSession()`. Harnesses that don't yet implement\n * capability dispatch ignore the field — legacy v1 paths stay intact.\n *\n * @category Capabilities\n * @since 2.0.0\n * @docLink packages/bridge/concepts#bridge-capability-hooks\n */\nexport interface BridgeCapabilityHooks {\n /** Build the LLM tool descriptor list from the registry. Called per turn so registration changes take effect. */\n composeTools(): BridgeCapabilityTool[];\n /** Resolve a wire-format Capability descriptor by name. Used by the bridge to inspect `fireAndForget` / `render`. */\n resolve(name: string): Capability | null;\n /**\n * Validate input + dispatch through the registry. Logging is wired by the\n * registry.\n *\n * `callId` is the per-dispatch id minted by `dispatchCapability`. It MUST be\n * unique per process (never a harness/provider tool-call id, which only the\n * model backend keeps unique): it is the `render_invoked.callId`, the\n * registry's log correlation id, the handler's `ctx.callId` and — for client\n * capabilities — the `capability_invoked.callId` keying the runner's pending\n * map, so a reused id would let a late `capability_result` for a timed-out\n * call resolve a later call. Callers without one omit it and the registry\n * generates a fresh id.\n */\n invoke(name: string, input: unknown, signal?: AbortSignal, callId?: string): Promise<unknown>;\n /** Subscribe to registry changes; callback carries no credentials or tool payloads. */\n onToolsChanged?(listener: () => void): () => void;\n /** Emit a render-invoked event. The runner forwards via the WebSocket transport. */\n emitRender?: BridgeRenderEmit;\n /** Emit a text fallback for render capabilities with `render.fallback`. */\n emitText?: BridgeTextEmit;\n /** Session id passed through to the runner's per-handler logger. */\n sessionId: string;\n}\n\n/**\n * Static metadata for a registered agent harness (the coding-agent product it drives).\n *\n * Declared once in `@skaile/workspaces/types` — it is protocol surface (the\n * platform reads `surfaces` to decide which AI provider registry entries a\n * harness can serve) and was previously duplicated here field-for-field.\n *\n * Returned by {@link listHarnesses} and exposed on every {@link AgentHarness}\n * instance via `harnessInfo`.\n *\n * @docLink packages/bridge/concepts#harness-info\n */\nexport type { HarnessInfo };\n\n/** Closed, passive managed-MCP observations. Counts saturate at 512. */\nexport interface CodexNativeMcpDiagnostics {\n startupBeforeTurn: \"unknown\" | \"starting\" | \"ready\" | \"failed\" | \"cancelled\";\n startupAtEnd: CodexNativeMcpDiagnostics[\"startupBeforeTurn\"];\n toolsListBeforeTurn: number;\n toolsListTotal: number;\n toolsCallTotal: number;\n nativeMcpToolItemCount: number;\n}\n\n/** Trusted session-local tool transport; fulfilled only after required tools are ready. */\nexport interface CodexToolTransport {\n mcpServers: Record<string, Record<string, unknown>>;\n /** Trusted runner server eligible for human native consent; must name its configured entry. */\n approvalServerName?: string;\n onServerRequest?: CodexServerRequestHandlers;\n /** Revalidate the required live inventory before starting each turn. */\n validate?(): Promise<void>;\n /** Rebuild the native generation at idle when its advertised inventory is stale. */\n needsReload?(): boolean;\n instructions?: string;\n /** Restrict model calls to the active turn and abort outstanding calls when it ends. */\n beginTurn?(signal: AbortSignal): void;\n /** Trusted private server binding and passive request-handler counters; never performs I/O. */\n diagnostics?: {\n serverName: string;\n requestCounts(): { toolsListTotal: number; toolsCallTotal: number };\n };\n dispose(): Promise<void>;\n}\n\n/** Explicit managed instance inputs; none are inferred from ambient developer state. */\nexport interface CodexManagedRuntimeOptions {\n executable: string;\n /** Optional launcher arguments, for the caller-owned executable wrapper. */\n executableArgs?: string[];\n /** Private local filesystem state; the host must fence old native processes before reuse. */\n privateDirectory: string;\n /** Host-owned private mount containing every parent/child instance; denied to native tools. */\n privateRoot: string;\n environment: Record<string, string>;\n credential: CodexCredentialDelivery;\n instructionFiles?: string[];\n skillsDirectory?: string;\n prepareTools(signal: AbortSignal): Promise<CodexToolTransport>;\n /**\n * Passive snapshots before/after turns, plus final local counts after successful transport closure.\n * No final snapshot is emitted on uncertain cleanup or a failed final counter read. Earlier snapshots\n * are not proof of completed cleanup. Sink failures never affect native execution or cleanup.\n */\n onNativeMcpDiagnostics?(value: Readonly<CodexNativeMcpDiagnostics>): void | Promise<void>;\n refreshCredential?: (\n input: CodexSubscriptionRefreshInput,\n signal: AbortSignal,\n ) => Promise<CodexSubscriptionRefreshResult>;\n requestTimeoutMs?: number;\n turnTimeoutMs?: number;\n /** Maximum native turns admitted by this instance; reserved for disposable one-turn children. */\n nativeTurnBudget?: 1;\n /** Human interaction deadline, bounded separately from ordinary RPCs. */\n interactionTimeoutMs?: number;\n}\n\n/**\n * Codex-specific tuning options passed through `HarnessOptions.codex`.\n *\n * @remarks These are forwarded verbatim to the Codex harness and have no effect on other harnesses.\n * @docLink packages/bridge/concepts#codex-harness-options\n */\nexport interface CodexHarnessOptions {\n /** Omission retains the standalone SDK path; managed mode always requires App Server inputs. */\n authSource?: \"local\" | \"managed\";\n managedRuntime?: CodexManagedRuntimeOptions;\n /** Controls whether Codex may auto-apply edits without user approval. */\n approvalPolicy?: \"never\" | \"on-request\" | \"on-failure\" | \"untrusted\";\n /** Filesystem sandbox level for the Codex process. */\n sandboxMode?: \"read-only\" | \"workspace-write\" | \"danger-full-access\";\n /** Reasoning budget passed to the model. */\n reasoningEffort?: \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\";\n /** When `true`, the Codex sandbox is allowed to make outbound network requests. */\n networkAccessEnabled?: boolean;\n /** Extra directories made accessible to the Codex sandbox in addition to `cwd`. */\n additionalDirectories?: string[];\n}\n\n/**\n * omp-specific options passed through `HarnessOptions.omp`.\n *\n * They govern the session-private agent directory the omp adapter renders the\n * provider registry into (`omp-models-yml.ts`); every one of them has a\n * working default, so a caller that sets none still gets a correct session.\n *\n * @remarks Forwarded verbatim to the omp harness; no effect on other harnesses.\n * @see PROVIDER-CONFIG-PLAN.md §2.3\n */\nexport interface OmpHarnessOptions {\n /**\n * Explicit session-private agent directory for `PI_CODING_AGENT_DIR`.\n * Defaults to `<cwd>/.omp/agent-home/<sessionId>`.\n */\n agentHome?: string;\n /**\n * Inherit the developer's own omp state (auth store, blobs, extensions) by\n * symlinking {@link OmpHarnessOptions.userAgentDir} into the session\n * directory. Defaults to *on when that directory exists*, which is exactly\n * the standalone case: a platform container has none, so its session starts\n * empty. Set `false` to force an empty session directory regardless.\n */\n seedUserAgentDir?: boolean;\n /** Seed source. Defaults to `~/.omp/agent`. */\n userAgentDir?: string;\n}\n\n/**\n * Harness-specific configuration bag.\n *\n * Fields are keyed by harness ID so that callers can pass harness-specific options\n * through the common {@link AgentConfig} without breaking other harnesses.\n *\n * @docLink packages/bridge/concepts#harness-options\n */\nexport interface HarnessOptions {\n /** Codex-specific options. Ignored by all other harnesses. */\n codex?: CodexHarnessOptions;\n /** omp-specific options. Ignored by all other harnesses. */\n omp?: OmpHarnessOptions;\n}\n\n/**\n * What the harness tells the host about a turn blocked by an upstream usage\n * limit. Everything but `configId` is best-effort: the SDK reports the limit\n * window only on its structured telemetry, not on the error result text.\n *\n * @see AgentConfig.onLimitBlocked\n * @category Configuration\n * @since 3.9.0\n */\nexport interface LimitBlockedContext {\n /** The AI provider config the blocked turn ran on (`\"\"` when unmediated). */\n configId: string;\n /** Upstream limit window (`five_hour`, `seven_day`, …) when reported. */\n limitType?: string;\n /** Epoch seconds at which the limit window resets, when reported. */\n resetsAt?: number;\n /** Fraction of the limit window consumed (0–1) when reported. */\n utilization?: number;\n}\n\n/**\n * The host's verdict on a limit-blocked turn. `switched: true` is a promise\n * that a different seat's credential is already on disk — the harness acts on it\n * by respawning the CLI and replaying the prompt, so returning it without\n * having rewritten the credential file would just replay onto the dead seat.\n *\n * @see AgentConfig.onLimitBlocked\n * @category Configuration\n * @since 3.9.0\n */\nexport interface LimitFailoverResult {\n /** Whether the session was repointed at a different AI provider config. */\n switched: boolean;\n /** The config id now in effect. Informational — used for logs. */\n configId?: string;\n /**\n * Why no switch happened: `same-config`, `standalone`,\n * `platform-omitted-config-id` (the mint succeeded but named no seat, so a\n * re-resolution is indistinguishable from none), or a `CredentialMint`\n * failure code. Diagnostic only — the harness branches on `switched`.\n */\n reason?: string;\n}\n\n/**\n * One observed AI-provider rejection, reported to the host for per-seat health\n * attribution. Fire-and-forget — see {@link AgentConfig.onProviderResponse}.\n *\n * @category Configuration\n * @since 3.9.0\n */\nexport interface ProviderResponseObservation {\n /** 401 — the credential was rejected. 429 — an upstream rate/session limit. */\n status: 401 | 429;\n /** The AI provider config the observation is attributed to, when known. */\n configId?: string;\n /** Upstream limit window (`five_hour`, `seven_day`, …) when reported. */\n limitType?: string;\n /** Fraction of the limit window consumed (0–1) when reported. */\n utilization?: number;\n /** Epoch seconds at which the limit window resets, when reported. */\n resetsAt?: number;\n /**\n * What the 429 was, when the harness recognised something narrower than a\n * usage limit. `long-context-entitlement`: the account may not use a long\n * (1M) context window; the seat is not out of quota. Diagnostic only — the\n * harness still treats the rejection as before.\n */\n cause?: ProviderResponseCause;\n}\n\n/**\n * Why a 429 {@link ProviderResponseObservation} was rejected, when the harness\n * recognised it. Consumers must tolerate values they do not know.\n *\n * @category Configuration\n * @since 4.11.0\n */\nexport type ProviderResponseCause = \"long-context-entitlement\";\n\n/**\n * One upstream quota-window reading, reported to the host for every SDK\n * `rate_limit_event` — allowed, warning or rejected alike. Fire-and-forget —\n * see {@link AgentConfig.onProviderQuota}.\n *\n * @category Configuration\n * @since 4.1.0\n */\nexport interface ProviderQuotaObservation {\n /** The AI provider config the reading is attributed to, when known. */\n configId?: string;\n /** Upstream window, verbatim (`five_hour`, `seven_day`, or a future value). */\n window: string;\n /** Upstream status, verbatim (`allowed`, `allowed_warning`, `rejected`, …). */\n status: string;\n /** Fraction of the window consumed (0–1); the SDK reports it on warnings and rejections only. */\n utilization?: number;\n /** Epoch seconds at which the window resets, when reported. */\n resetsAt?: number;\n}\n\n/**\n * Reasoning effort levels a host may set. The platform sends all five, the\n * Agent SDK accepts all five, omp has a same-named thinking level for each, and\n * managed codex passes the value to its app server.\n *\n * @since 4.22.0\n */\nexport const AGENT_EFFORT_LEVELS = [\"low\", \"medium\", \"high\", \"xhigh\", \"max\"] as const;\nexport type AgentEffort = (typeof AGENT_EFFORT_LEVELS)[number];\n\n/**\n * Narrow a free-form effort (the wire `agentConfig.effort`, a `skaile.yaml`\n * default) to an {@link AgentEffort}; anything else gives `undefined`, so an\n * unknown value never reaches a harness.\n */\nexport function toAgentEffort(value: unknown): AgentEffort | undefined {\n return isAgentEffort(value) ? value : undefined;\n}\n\nfunction isAgentEffort(value: unknown): value is AgentEffort {\n return (AGENT_EFFORT_LEVELS as readonly unknown[]).includes(value);\n}\n\n/**\n * Configuration passed to a harness when it is created via {@link createHarness}.\n *\n * All fields are shared across harnesses; harness-specific behaviour is documented\n * per field. Fields that are silently ignored by a harness are marked accordingly.\n *\n * @docLink packages/bridge/concepts#agent-config\n */\nexport interface AgentConfig {\n /** Absolute path to the working directory the agent operates in. */\n cwd: string;\n /**\n * LLM model identifier (e.g. `\"claude-sonnet-4-5\"`, `\"gpt-4o\"`).\n *\n * The unscoped fallback: {@link AgentConfig.modelRef} is what a session\n * normally carries, and this is what a harness falls back to when no\n * registry entry resolved one.\n */\n model?: string;\n /**\n * The AI provider registry for this session — every endpoint the harness may\n * address, already merged across the catalog / user / workspace / runtime /\n * control-plane layers and in registry order.\n *\n * This is the *only* provider surface a harness reads. Entries reference\n * their credential (`credential.ref`) and never carry the secret itself;\n * the value reaches the child under `SKAILE_AIP_<ID>_KEY`\n * (`provider-materialize/credential-env.ts`), which the session boundary\n * puts in {@link AgentConfig.env}.\n *\n * Absent (or empty) means no endpoint was configured at all — the harness\n * then falls back to whatever native auth it can discover on its own (a\n * `claude` OAuth login, `codex login`, an ambient vendor key).\n */\n registry?: MergedAIProviderRegistry;\n /**\n * Model selector resolved against {@link AgentConfig.registry}:\n * `\"<providerId>/<modelId>\"`, a bare `\"<modelId>\"` (first registry entry\n * that maps it wins), or `\"@role\"`. Absent when no model was configured at\n * all — the harness then applies its own default, exactly as it does for an\n * absent `model`.\n */\n modelRef?: string;\n /**\n * The `skaile.yaml` `roles` table — role name → model selector\n * (`{default: \"anthropic/opus\", smol: \"groq/llama-3.1-8b\"}`, §9.8). It is\n * what `\"@role\"` in {@link AgentConfig.modelRef} expands through, and what\n * a harness that supports per-role models materializes natively: omp renders\n * it as `modelRoles` in its session-private `config.yml`, so `@smol` inside\n * the session reaches a different provider than `@default`.\n *\n * Resolution is lenient: a role naming an entry this registry does not have\n * is a warning, never a failure.\n */\n roles?: Record<string, string>;\n /**\n * Additional environment variables merged into the child process\n * environment. The session boundary delivers resolved registry credentials\n * here as `SKAILE_AIP_<ID>_KEY` entries; anything else the caller sets is\n * merged over the harness's own allowlist, never instead of it.\n */\n env?: Record<string, string>;\n /** Inline system prompt — written to .omp/system.md and passed via --append-system-prompt (omp harness) */\n systemPrompt?: string;\n /** Path to project .omp/ directory (PI_CODING_AGENT_DIR for omp harness) */\n agentDir?: string;\n /** SSH key path — injected as GIT_SSH_COMMAND */\n sshKeyPath?: string;\n /** Pre-assign a UUID as the session ID for a new session (claude-sdk: passed as sessionId option) */\n sessionId?: string;\n /** Resume this specific past session by UUID instead of starting fresh (claude-sdk: passed as resume option) */\n resumeSessionId?: string;\n /** Maximum agentic turns per query (claude-sdk: passed as maxTurns option) */\n maxTurns?: number;\n /**\n * Whether the agent may act on its own (`auto`, the default) or has to be approved per tool\n * call (`interactive`); the `agents.<id>.permissions.permission_mode` of the instance.\n *\n * - claude-sdk: `auto` runs under the SDK's `bypassPermissions`; `interactive` runs under\n * `default`, and a call the SDK does not auto-allow (a shell command, a file edit, an MCP\n * tool) pauses the turn on a `question` event with an `approval` card (`Approve once` /\n * `Deny`) answered through `answerQuestion`. With {@link AgentConfig.unattended} the call is\n * refused instead.\n * - codex (standalone SDK path): `interactive` sets `approvalPolicy: \"on-request\"` instead of\n * `never`, unless `harnessOptions.codex.approvalPolicy` is set. The standalone SDK path has no\n * way to show an approval card, so codex refuses a command that needs to leave its sandbox\n * instead of asking; nothing is ever approved. A managed codex session keeps the host's own\n * policy.\n * - omp, echo and the subprocess CLI harnesses (gemini, qwen, opencode, goose, continue): ignored.\n */\n permissionMode?: \"auto\" | \"interactive\";\n /**\n * The session has no one to answer a question (a nested `agent.run` role session, a\n * single-shot `skaile run` without a terminal). With `permissionMode: \"interactive\"` a tool\n * call that needs approval is then refused (claude-sdk) instead of waiting for a reply.\n */\n unattended?: boolean;\n /** Agent name — selects a deployed sub-agent definition as the main agent identity.\n * claude-sdk: passed as `agent` option to query() → reads .claude/agents/<name>.md natively.\n * omp: ignored (omp uses agentDir/PI_CODING_AGENT_DIR instead). */\n agentName?: string;\n /** In-process SDK MCP servers for custom tool injection (Claude SDK only, ignored by other harnesses) */\n mcpServers?: Record<string, unknown>;\n /** Tool restrictions from agent.yaml — applied to the main agent session */\n tools?: {\n /** Tool names that the agent is allowed to invoke. An empty array means no restriction. */\n allowed?: string[];\n /** Tool names that the agent is explicitly forbidden from invoking. */\n denied?: string[];\n /**\n * Host tools (Protocol v2 capabilities) the session may see and call, as\n * anchored patterns where `*` is the only wildcard. Independent of\n * `allowed`/`denied`, which cover native tools only. `undefined` leaves host\n * tools unrestricted; an empty array forbids all of them. Enforced by omp.\n */\n host_allowed?: string[];\n };\n /** Thinking mode for Claude models: adaptive (Claude decides), enabled (always think), disabled (no thinking). */\n thinking?: \"adaptive\" | \"enabled\" | \"disabled\";\n /** Reasoning effort level; each harness maps it to its own scale (see {@link AgentEffort}). */\n effort?: AgentEffort;\n /** Harness-specific configuration bag. */\n harnessOptions?: HarnessOptions;\n /**\n * Protocol v2 capability dispatch hooks. When present, the harness uses the\n * registry as the source of LLM tool definitions and routes invocations\n * through `capabilities.invoke()`. Absent → legacy v1 path (existing\n * mcpServers / native dispatch).\n *\n * @since 2.0.0\n */\n capabilities?: BridgeCapabilityHooks;\n /**\n * The platform's `AIProviderConfig.id` for the AI credential currently\n * provisioned into this harness. The runner reads this off `AgentConfig`\n * during 401 mediation and passes it as `configId` in\n * `request_access_token { kind: 'ai-credentials' }`. The harness itself\n * does not consume this field — only the runner uses it.\n *\n * Set by the platform agent-gateway in `ConfigureCommandV2` for mediated\n * sessions. Standalone runners (CLI / forge / Claude plugin) leave it\n * undefined; the runner falls back to surfacing the auth error to the\n * user without mediating.\n *\n * Spec: `_devlog/specs/2026-05-07-unified-credential-mediation.md`\n * § \"Wire `aiProviderConfigId` end-to-end\".\n *\n * @since 3.3.0\n */\n aiProviderConfigId?: string;\n /**\n * Optional callback invoked by the harness when the underlying agent\n * surfaces an `authentication_error`. In Protocol v3 the runner mediates\n * the refresh via the `host.refresh_credential` capability and returns a\n * typed {@link CredentialMint}. The harness inspects the discriminator:\n *\n * - `mint.ok === true`: a fresh credential is now provisioned. The\n * harness re-attempts the in-flight prompt once.\n * - `mint.ok === false`: refresh failed (`code` carries a stable reason\n * such as `revoked`, `not-configured`, `provider-error`,\n * `backend-error`). The harness surfaces the original `AuthError` to\n * the caller.\n *\n * `args.configId` carries the platform `AIProviderConfig.id` the harness\n * was provisioned with (mirrors {@link AgentConfig.aiProviderConfigId}).\n * The runner uses it to scope the refresh to the correct AI credential.\n *\n * When omitted, the harness throws `AuthError` immediately as before\n * (standalone CLI / forge mode — there is no platform mediator to ask).\n *\n * Centralising auth-retry inside the harness means every consumer of\n * `harness.prompt(...)` (serve handler, compaction orchestrator, flow\n * orchestrator, …) gets self-healing for free — there is no per-call-site\n * wrapping.\n *\n * Spec: `_devlog/specs/2026-05-10-deterministic-session-bootstrap.md`\n * § \"host.refresh_credential capability\".\n *\n * @since 3.4.0\n */\n onAuthError?: (args: { configId: string }) => Promise<CredentialMint>;\n /**\n * Optional callback invoked by the harness when a turn is blocked by an\n * upstream **usage limit** rather than a bad credential — the subscription\n * five-hour / seven-day ceiling, or any `rate_limit` the classifier\n * recognises. Called at most once per logical turn, and only after the\n * blocked attempt has already failed, so it never runs mid-turn.\n *\n * The runner implements it by asking the platform to re-resolve the seat:\n *\n * - `switched: true` — the session was repointed at a DIFFERENT AI\n * provider config and `.credentials.json` was rewritten. The harness\n * drops its query (forcing a fresh CLI spawn, which is what clears a\n * resident CLI's cached \"blocked until <reset>\" state — a hot file\n * rewrite alone does not) and replays the prompt once.\n * - `switched: false` — no alternative seat was available. The harness\n * surfaces the original limit error unchanged.\n *\n * Left unset by standalone runners (CLI / forge / Claude plugin): there is\n * no platform mediator to re-resolve a seat, so the limit error surfaces\n * exactly as it does today.\n *\n * Companion to {@link onAuthError}: that one covers 401s, this one covers\n * the rejection path, which previously reached no failover at all\n * (skaile-ai/workspaces#567, skaile-ai/platform#2976).\n *\n * @since 3.9.0\n */\n onLimitBlocked?: (ctx: LimitBlockedContext) => Promise<LimitFailoverResult>;\n /**\n * Optional fire-and-forget hook invoked when the harness observes a 401 or a\n * 429 / subscription-limit rejection from the AI provider. The runner\n * forwards it to the platform as a `provider_response_seen` event so the\n * owning `AIProviderConfig`'s live health counters can be bumped — the cron\n * health probe only samples auth, never capacity\n * (skaile-ai/workspaces#368, skaile-ai/platform#3539).\n *\n * Never awaited and never retried; the harness swallows a throwing\n * implementation so telemetry can never fail a turn.\n *\n * @since 3.9.0\n */\n onProviderResponse?: (observation: ProviderResponseObservation) => void;\n /**\n * Optional fire-and-forget hook invoked for every upstream quota-window\n * reading (claude-sdk `rate_limit_event`), not only rejections. The runner\n * forwards it as `provider_quota_observed`, which is the only quota signal\n * the platform has for a seat whose token cannot read the OAuth usage\n * endpoint (a `claude setup-token` seat).\n *\n * Never awaited and never retried; the harness swallows a throwing\n * implementation so telemetry can never fail a turn. Unlike\n * {@link onProviderResponse} it fires on every event, so dedupe at the host.\n *\n * @since 4.1.0\n */\n onProviderQuota?: (observation: ProviderQuotaObservation) => void;\n /**\n * Test-only flow harness hook. When present, the echo harness's scripted-flow\n * mode uses it to advance flow nodes deterministically (no LLM). The runner\n * injects it in `createActiveFlow`; every other harness ignores it.\n *\n * @see FlowTestHarnessHook\n * @since 3.6.0\n */\n flowTestHarness?: FlowTestHarnessHook;\n}\n\n/**\n * A single message in an agent conversation.\n *\n * Emitted as part of {@link AgentEvent} variants (`message_start`, `message_update`, `message_end`).\n *\n * @docLink packages/bridge/concepts#agent-message\n */\nexport interface AgentMessage {\n /** Originator of the message. */\n role: \"user\" | \"assistant\" | \"tool\";\n /** Message body — either a plain string or a list of typed content blocks. */\n content: string | ContentBlock[];\n /** Tool invocations made by the assistant in this message. */\n toolCalls?: ToolCall[];\n /** For `role === \"tool\"`: the name of the tool whose result this message carries. */\n toolName?: string;\n /** For `role === \"tool\"`: `true` when the tool returned an error. */\n isError?: boolean;\n /** Optional structured payload for tool results. */\n data?: unknown;\n /**\n * Set when a subagent produced this message: the id of the tool use that\n * runs the subagent. Absent on main-thread messages.\n */\n parentToolUseId?: string;\n}\n\n/**\n * A typed block within an {@link AgentMessage}'s content array.\n *\n * Mirrors the Anthropic content block schema but is harness-agnostic.\n *\n * @docLink packages/bridge/concepts#content-block\n */\nexport interface ContentBlock {\n /** Block discriminant. */\n type: \"text\" | \"tool_use\" | \"tool_result\" | \"thinking\";\n /** Present for `text` and `thinking` blocks. */\n text?: string;\n /** Tool use / tool result correlation ID. */\n id?: string;\n /** Tool name (present on `tool_use` blocks). */\n name?: string;\n /** Tool input arguments (present on `tool_use` blocks). */\n input?: any;\n /** Serialised tool result content (present on `tool_result` blocks). */\n content?: string;\n}\n\n/**\n * A single tool invocation made by the agent within a message.\n *\n * @docLink packages/bridge/concepts#tool-call\n */\nexport interface ToolCall {\n /** Correlation ID used to match the call with its result. */\n id: string;\n /** Name of the tool being invoked. */\n name: string;\n /** Arguments passed to the tool. */\n input: any;\n}\n\n/**\n * Error classification for agent failures.\n *\n * Used to determine whether a failure is retryable and to surface actionable\n * hints to the user. See `bridge/CLAUDE.md` for the full category-to-behaviour matrix.\n *\n * @see AgentError\n * @docLink packages/bridge/concepts#error-category\n */\nexport type ErrorCategory =\n | \"auth\"\n | \"account\"\n | \"rate_limit\"\n | \"model\"\n | \"network\"\n | \"config\"\n | \"process\"\n | \"validation\"\n | \"unknown\";\n\n/**\n * Structured error payload emitted inside the `error` {@link AgentEvent}.\n *\n * Consumers should inspect `retryable` before deciding to surface a retry button,\n * and `hint` to provide an actionable message to the user.\n *\n * @docLink packages/bridge/concepts#agent-error\n */\nexport interface AgentError {\n /** Human-readable error description. */\n message: string;\n /** Coarse failure classification used for retry logic and telemetry. */\n category: ErrorCategory;\n /** HTTP status code if the error originated from an API response. */\n statusCode?: number;\n /** `true` when the caller may safely retry the same operation. */\n retryable: boolean;\n /** Short actionable advice suitable for display in the UI. */\n hint?: string;\n}\n\n/**\n * Metadata for a slash command exposed by the active agent runtime.\n *\n * @docLink packages/bridge/concepts#slash-command-info\n */\nexport interface SlashCommandInfo {\n /** Command name without the leading slash (e.g. `\"compact\"`). */\n name: string;\n /** One-line description shown in command pickers. */\n description: string;\n /** Placeholder text describing what argument the command expects. */\n argumentHint?: string;\n}\n\n/**\n * Discriminated union of all events emitted by a harness on the `'agent-event'` channel.\n *\n * @remarks\n * Consumers listen via `harness.on('agent-event', (event: AgentEvent) => ...)`.\n * The `[k: string]: any` index signature on most variants allows harnesses to attach\n * harness-specific fields (e.g. `_textDelta`) without breaking the union.\n *\n * @docLink packages/bridge/concepts#agent-event\n */\nexport type AgentEvent =\n | { type: \"message_start\"; message: AgentMessage; [k: string]: any }\n | { type: \"message_update\"; message: AgentMessage; [k: string]: any }\n | { type: \"message_end\"; message: AgentMessage; [k: string]: any }\n /**\n * Takes back `text`, the unfinished tail of streamed `_textDelta`s, before the\n * harness replays an abandoned attempt. Always a suffix of what was streamed\n * since the last completed assistant message; see `TextRetractEvent` in the types package.\n */\n | { type: \"message_retract\"; text: string; [k: string]: any }\n | { type: \"turn_end\"; toolResults?: AgentMessage[]; [k: string]: any }\n | { type: \"agent_end\"; [k: string]: any }\n /** A background task started in the running turn; see `BackgroundTaskStartedEvent`. */\n | { type: \"background_task_started\"; taskId: string; [k: string]: any }\n /** A turn the harness was not prompted for began; see `SelfTurnStartedEvent`. */\n | { type: \"self_turn_started\"; taskIds: string[]; [k: string]: any }\n /** A background task settled. Raw only: the runner's turn-visibility stamp reads it, no client does. */\n | { type: \"background_task_settled\"; taskId: string; [k: string]: any }\n /** The CLI process is gone, and every background task with it. Raw only, like the above. */\n | { type: \"background_tasks_reset\"; [k: string]: any }\n | {\n type: \"result\";\n subtype: string;\n summary?: string;\n costUsd?: number;\n /** Per-turn token usage when the harness tracks it. Added in 3.1.0. */\n tokens?: TokenUsage;\n /** Per-turn usage keyed by model, subagent calls included. Added in 3.11.0. */\n modelUsage?: Record<string, ModelTokenUsage>;\n /** Prompt size of the turn's last main-thread API call. Added in 3.11.0. */\n contextTokens?: number;\n /** Context window of the main model, in tokens. Added in 3.11.0. */\n contextWindow?: number;\n errors?: string[];\n [k: string]: any;\n }\n | {\n type: \"error\";\n error: string;\n detail?: AgentError;\n fatal?: boolean;\n /** Set (with `fatal: false`) when the harness replays the prompt in place; see `ErrorEvent.retrying`. */\n retrying?: boolean;\n retryReason?: ErrorRetryReason;\n /** Usage of a failed provider turn, when reported. */\n tokens?: TokenUsage;\n /** Failed-turn usage keyed by the actual model. */\n modelUsage?: Record<string, ModelTokenUsage>;\n contextTokens?: number;\n contextWindow?: number;\n [k: string]: any;\n }\n | {\n type: \"tool_call\";\n name?: string;\n tool?: { name: string };\n /** Set when a subagent made the call. */\n parentToolUseId?: string;\n [k: string]: any;\n }\n | { type: \"tool_execution_end\"; toolName?: string; parentToolUseId?: string; [k: string]: any }\n | { type: \"commands_available\"; commands: SlashCommandInfo[] }\n | { type: \"session_info\"; harnessSessionId: string; sessionFile?: string }\n | { type: \"ui_render\"; [k: string]: any }\n | { type: \"ui_render_update\"; [k: string]: any }\n | { type: \"ui_clear\"; [k: string]: any }\n // Phase 3 (resume cascade) — harness-emitted resume outcome.\n | {\n type: \"resume_failed\";\n resumeSessionId: string;\n reason:\n | \"signature_mismatch\"\n | \"model_mismatch\"\n | \"jsonl_lost\"\n | \"jsonl_poisoned\"\n | \"native_state_unavailable\";\n [k: string]: any;\n };\n\n/** The settings a live reconfigure may change. */\nexport type ReconfigurePatch = Partial<Pick<AgentConfig, \"model\" | \"thinking\" | \"effort\">>;\n\n/**\n * What a harness now holds for the fields of an accepted reconfigure. `modelRef`\n * is the registry selector the model was scoped to; it is absent when the\n * session has none. A host remembers exactly this, never the raw patch.\n */\nexport interface ReconfigureApplied {\n model?: string;\n modelRef?: string;\n thinking?: AgentConfig[\"thinking\"];\n effort?: AgentConfig[\"effort\"];\n}\n\n/**\n * Result of `AgentHarness.reconfigure()`. `ok: true` means effective from the\n * next turn (live switch, query recycle or respawn); a harness that cannot\n * promise that answers `ok: false` instead.\n *\n * - `not_supported`: the harness cannot change settings live.\n * - `model_not_on_provider`: the session's AI provider entry does not list the\n * model (`providerId` names the entry).\n * - `rejected`: the running process refused the change; the harness config is\n * unchanged.\n *\n * @category Bridge\n */\nexport type ReconfigureOutcome =\n | { ok: true; applied: ReconfigureApplied }\n | {\n ok: false;\n code: \"not_supported\" | \"model_not_on_provider\" | \"rejected\";\n message: string;\n providerId?: string;\n };\n\n/**\n * A reconfigure the harness answered `ok` (it landed mid-turn) and then refused\n * at the next turn boundary. The harness already rolled its config back;\n * `requested` is what was asked, `restored` what it holds again. Emitted as the\n * harness event `'reconfigure-rollback'`.\n */\nexport interface ReconfigureRollback {\n message: string;\n requested: ReconfigureApplied;\n restored: ReconfigureApplied;\n}\n\n/** Rejection message of a harness asked to run a turn under a tool allowlist it cannot enforce. */\nexport const TOOL_ALLOWLIST_UNSUPPORTED = \"This agent harness cannot enforce a tool allowlist\";\n\n/**\n * Abstract base class for agent harnesses.\n *\n * Wraps an LLM coding agent behind a single `prompt()` interface.\n * All harnesses emit `'agent-event'` with {@link AgentEvent} payloads.\n *\n * @remarks\n * Subclasses must implement `start`, `prompt`, `abort`, `kill`, and `isRunning`.\n * Implementations should never emit an event name other than `'agent-event'` as\n * the public streaming channel — internal events (`'ready'`, `'exit'`, etc.) are\n * harness-private. The one other public event is `'reconfigure-rollback'` (a\n * {@link ReconfigureRollback}): a live reconfigure the harness had accepted and\n * then refused at the turn boundary, already rolled back in the harness.\n *\n * @example\n * ```ts\n * const harness = createHarness('omp', config);\n * harness.on('agent-event', (event) => console.log(event.type));\n * await harness.start();\n * await harness.prompt('Refactor the auth module');\n * harness.kill();\n * ```\n *\n * @docLink packages/bridge/concepts#agent-harness\n */\nexport abstract class AgentHarness extends EventEmitter {\n /** Static metadata describing this harness's capabilities. */\n abstract readonly harnessInfo: HarnessInfo;\n\n /**\n * Initialises the harness — spawns the child process (omp) or loads\n * the SDK module (claude-sdk). Resolves when the harness is ready to accept\n * prompts. Idempotent: calling `start()` on an already-running harness is a no-op.\n *\n * @throws {Error} When the harness binary is missing or the SDK cannot be loaded.\n */\n abstract start(): Promise<void>;\n\n /**\n * Sends a user message to the agent and resolves when the agent's turn completes\n * (i.e. after the `agent_end` event has been emitted).\n *\n * @param message - Plain-text user prompt to send to the agent.\n * @throws {Error} When the harness is not running or the underlying coding agent reports a fatal error.\n */\n abstract prompt(message: string): Promise<void>;\n\n /**\n * Preserve text-only callers; harnesses must explicitly support attachments.\n * A `toolAllowlist` is refused here: only a harness that overrides\n * {@link enforcesToolAllowlist} may run a restricted turn.\n */\n promptInput(input: AgentPromptInput): Promise<void> {\n if (input.toolAllowlist !== undefined)\n return Promise.reject(new Error(TOOL_ALLOWLIST_UNSUPPORTED));\n if (input.attachments?.length) return Promise.reject(new Error(\"Structured input unsupported\"));\n return this.prompt(input.text);\n }\n\n /**\n * Whether {@link promptInput} enforces `AgentPromptInput.toolAllowlist` for\n * the whole turn, subagents included. A harness that answers `false` must\n * reject an input carrying one; the runner refuses such a prompt before it\n * reaches the harness.\n */\n get enforcesToolAllowlist(): boolean {\n return false;\n }\n\n /**\n * The names in `allowlist` this harness does not recognize as a tool it could\n * offer. The runner refuses a prompt whose allowlist names any, rather than\n * guess. A harness that does not enforce allowlists recognizes none.\n */\n unknownAllowlistedTools(allowlist: readonly string[]): string[] {\n return [...allowlist];\n }\n\n /** Managed replies never fall back to a new prompt after a request has settled. */\n get acceptsReplyAsPrompt(): boolean {\n return true;\n }\n\n /**\n * Sends an in-band abort signal to the agent, requesting it to stop the current\n * turn without terminating the process. The harness remains usable after `abort()`.\n *\n * @remarks Only meaningful when `harnessInfo.supportsInBandAbort` is `true`.\n * For other harnesses, prefer `kill()` followed by creating a new harness instance.\n */\n abstract abort(): Promise<void>;\n\n /**\n * Terminates the agent harness immediately (SIGTERM for subprocess harnesses,\n * `close()` for in-process harnesses). The harness instance must not be reused\n * after `kill()`.\n */\n abstract kill(): void;\n\n /** Stop the harness and await owned process/state cleanup when the harness supports it. */\n async dispose(): Promise<void> {\n this.kill();\n }\n\n /** `true` when the harness is alive and able to accept new prompts. */\n abstract get isRunning(): boolean;\n\n /** Optional provider-native session/thread identifier for resume support. */\n get runtimeSessionId(): string | undefined {\n return undefined;\n }\n\n /** Slash commands discovered from the agent runtime. Override in harnesses that support introspection. */\n getSlashCommands(): SlashCommandInfo[] {\n return [];\n }\n\n // ── Interactive questions (AskUserQuestion) ──────────────────────────────\n\n /**\n * `true` while the harness is paused on an interactive question and waiting\n * for the user's answer rather than a new prompt. The runner routes a\n * `reply` command to {@link answerQuestion} instead of {@link prompt} when\n * this returns `true`. Harnesses without interactive-question support return\n * `false` (the default), so a `reply` falls back to `prompt`.\n */\n hasPendingQuestion(): boolean {\n return false;\n }\n\n /**\n * Deliver the user's answer to the question the harness is currently paused\n * on. This resumes the in-flight turn — it does NOT start a new one. When a\n * single tool call asks several questions, the harness resolves the underlying\n * call only once every sub-question has an answer.\n *\n * @param answer - The user's reply text (an option label, free text, or for\n * multi-select a comma-joined list).\n * @param question - The sub-question this answers, verbatim from the\n * `question` event that asked it. Supply it whenever the host lets the user\n * answer out of order — without it the answer fills the next unanswered\n * slot in order, which silently mismatches a non-sequential reply.\n * @returns `true` when the answer was consumed by a pending question; `false`\n * when no question was pending (caller should treat the reply as a prompt).\n */\n answerQuestion(answer: string, question?: string, requestId?: string): boolean {\n void requestId;\n void answer;\n void question;\n return false;\n }\n\n /**\n * Apply this harness's rule for a new prompt that meets a pending question,\n * ahead of the prompt itself. The runner queues a prompt behind the turn in\n * flight, so a turn paused on a question would otherwise hold that prompt\n * until the user answered. Harnesses whose `prompt()` cancels the question\n * (claude-sdk, omp) do the same here; the default keeps the question.\n */\n supersedePendingQuestion(): void {\n // No-op: a harness without this rule keeps the question pending.\n }\n\n // ── Live reconfiguration ────────────────────────────────────────────────\n\n /**\n * Live-update harness configuration. `ok: true` means effective from the next\n * turn: the next turn runs on the new settings, by live switch, query recycle\n * or respawn, and the host needs no restart. A turn in progress finishes on\n * the old settings. Only model/thinking/effort are reconfigurable mid-session.\n *\n * Harnesses that can honour that promise build a `ReconfigureLedger` and\n * delegate to it; it owns the model scope rule, ordering, drift and rollback.\n * The base implementation answers `not_supported`.\n */\n reconfigure(patch: ReconfigurePatch): Promise<ReconfigureOutcome> {\n const fields = Object.keys(patch).join(\", \");\n return Promise.resolve({\n ok: false,\n code: \"not_supported\",\n message: `The ${this.harnessInfo.id} harness cannot change ${fields} live; the change applies when the session next starts.`,\n });\n }\n\n /**\n * Returns the model identifier currently configured on this harness.\n * Subclasses that store config locally should override this.\n */\n getModel(): string | undefined {\n return undefined;\n }\n\n // ── Compaction support ──────────────────────────────────────────────────\n\n /**\n * `Date.now()` of the harness's most recent sign of life, counting messages\n * that emit no `agent-event` (thinking deltas, rate-limit telemetry, API\n * retries). A stall check reads it alongside the events it sees. `null` when\n * the harness does not track it.\n */\n lastActivityAt(): number | null {\n return null;\n }\n\n /**\n * Returns the token usage from the most recent completed turn.\n * Returns `null` if usage data is unavailable (e.g. harness doesn't track it).\n *\n * Widened in 3.1.0 to return the full `TokenUsage` shape (input / output /\n * cache-read / cache-creation / reasoning). All fields are optional so\n * harnesses can populate just what their provider reports. Existing callers\n * that read only `inputTokens` / `outputTokens` keep working — those fields\n * are still present and carry the same semantics as before.\n */\n getTokenUsage(): TokenUsage | null {\n return null;\n }\n\n /**\n * Prompt tokens occupying the context window as of the most recent API\n * request — i.e. how full the context actually is right now.\n *\n * Deliberately NOT the same number as {@link getTokenUsage}, whose usage is\n * cumulative over every API call in the turn and therefore overstates the\n * context by 10-40x on any multi-tool turn. Callers measuring context fill\n * (compaction thresholds) must prefer this; callers billing a turn\n * (usage ledgers, the `finished` event) must keep using `getTokenUsage()`.\n *\n * Returns `null` both when the harness cannot report a per-call figure at all\n * and when it can but has no reading yet. Falling back to `getTokenUsage()`\n * is safe in either case only because a harness clears the two in lockstep: no\n * reading means no API call landed, so there is no cumulative usage to\n * overstate. A harness implementing this must preserve that invariant.\n */\n getContextTokens(): number | null {\n return null;\n }\n\n /**\n * Returns the model's context window size in tokens.\n * Returns `null` if unknown.\n */\n getContextWindow(): number | null {\n return null;\n }\n\n /**\n * What the harness can measure natively about its context window, in the\n * neutral {@link HarnessContextReport} shape, or `null` when it measures\n * nothing beyond {@link getContextTokens} / {@link getContextWindow}.\n *\n * `detail: \"full\"` (the default) may be expensive — claude-sdk issues\n * `count_tokens` requests — so the runner asks for it only when a host\n * requests a measured breakdown; `\"summary\"` must make no provider call and\n * is what the per-turn snapshot uses. Implementations must not throw for\n * \"unsupported\"; a rejection means the measurement itself failed and the\n * runner falls back to estimates.\n */\n async getContextUsageReport(_opts?: {\n detail?: ContextUsageDetail;\n }): Promise<HarnessContextReport | null> {\n return null;\n }\n\n /**\n * Returns the list of models available to this harness without starting a session.\n * Harnesses that can enumerate their own models override this method.\n * Returns an empty array by default.\n */\n async listModels(): Promise<ModelEntry[]> {\n return [];\n }\n\n /**\n * Closes the current conversation session and prepares for a fresh start.\n * The next `prompt()` call will begin a new session without prior history.\n * The harness remains usable after `resetSession()`.\n */\n abstract resetSession(): Promise<void>;\n\n // ── Telemetry ─────────────────────────────────────────────────────────────\n\n protected _telemetry?: TelemetryProvider;\n protected _activeTrace?: Trace;\n protected _activeTurnSpan?: Span;\n private _turnStartTime?: number;\n\n /**\n * Attach a telemetry provider and active trace to this harness.\n * Called by the runner after creating the harness but before start().\n */\n setTelemetry(provider: TelemetryProvider, trace: Trace): void {\n this._telemetry = provider;\n this._activeTrace = trace;\n }\n\n /**\n * Begin a turn span. Called by the runner before prompt().\n * Pass a parent span (e.g. the turn span from the orchestrator) for nesting.\n * Returns the span so the runner can annotate it further.\n */\n beginTurnSpan(\n parent?: Trace | Span,\n attrs?: Record<string, string | number | boolean>,\n ): Span | undefined {\n if (!this._telemetry || !this._activeTrace) return undefined;\n this._turnStartTime = Date.now();\n this._activeTurnSpan = this._telemetry.startSpan(parent ?? this._activeTrace, {\n name: \"llm_generation\",\n kind: \"llm_generation\",\n attributes: attrs,\n });\n return this._activeTurnSpan;\n }\n\n /**\n * End the current turn span with generation data.\n * Called by the runner after agent_end. Model and provider are passed\n * explicitly — the harness base class does not access config directly.\n */\n endTurnSpan(result?: {\n model?: string;\n provider?: string;\n inputTokens?: number;\n outputTokens?: number;\n stopReason?: string;\n error?: string;\n }): void {\n if (!this._telemetry || !this._activeTurnSpan) return;\n const durationMs = this._turnStartTime ? Date.now() - this._turnStartTime : 0;\n\n if (result?.inputTokens !== undefined || result?.outputTokens !== undefined) {\n this._telemetry.logGeneration(this._activeTurnSpan, {\n model: result?.model ?? \"unknown\",\n provider: result?.provider ?? \"unknown\",\n inputTokens: result.inputTokens,\n outputTokens: result.outputTokens,\n durationMs,\n stopReason: result.stopReason,\n });\n }\n\n this._telemetry.endSpan(this._activeTurnSpan, {\n status: result?.error ? \"error\" : \"ok\",\n error: result?.error,\n attributes: { \"skaile.turn.duration_ms\": durationMs },\n });\n\n this._activeTurnSpan = undefined;\n this._turnStartTime = undefined;\n }\n}\n\n/**\n * Factory function signature registered in the harness registry.\n *\n * @param config - Configuration for the new harness instance.\n * @returns A freshly constructed (not yet started) {@link AgentHarness}.\n * @docLink packages/bridge/concepts#harness-factory\n */\nexport type HarnessFactory = (config: AgentConfig) => AgentHarness;\n"]}
@@ -1,4 +1,4 @@
1
- import { buildClaudePluginFiles } from './chunk-OKPITFE3.js';
1
+ import { buildClaudePluginFiles } from './chunk-NNJQQYHY.js';
2
2
  import { createHash } from 'crypto';
3
3
  import { existsSync } from 'fs';
4
4
  import fs from 'fs/promises';
@@ -281,5 +281,5 @@ async function status(projectDir) {
281
281
  }
282
282
 
283
283
  export { disable, enable, install, refreshPluginSkills, status, uninstall };
284
- //# sourceMappingURL=chunk-YJAKSCCN.js.map
285
- //# sourceMappingURL=chunk-YJAKSCCN.js.map
284
+ //# sourceMappingURL=chunk-NJ6UCY2B.js.map
285
+ //# sourceMappingURL=chunk-NJ6UCY2B.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../cli/src/commands/plugin/claude-code.ts"],"names":["disabled","next","updated","list"],"mappings":"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","file":"chunk-YJAKSCCN.js","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { existsSync } from \"node:fs\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { buildClaudePluginFiles } from \"@skaile/workspaces/workspace-plugin\";\nimport type { HarnessStatus, InstallResult, ToggleResult, UninstallResult } from \"./types.ts\";\n\nconst SKAILE_MCP_NAME = \"skaile-workspace\";\n\nconst MCP_SERVER_ENTRY = {\n command: \"skaile\",\n args: [\"mcp-server\"],\n env: {} as Record<string, string>,\n};\n\ninterface ClaudeSettings {\n plugins?: string[];\n enabledPlugins?: string[];\n disabledPlugins?: string[];\n disabledMcpjsonServers?: string[];\n [k: string]: unknown;\n}\n\ninterface ClaudeMcpFile {\n mcpServers?: Record<string, unknown>;\n [k: string]: unknown;\n}\n\n/**\n * Project-scope MCP file. Claude Code reads `<project>/.mcp.json`; the\n * `.claude/mcp.json` that earlier versions wrote is never read (`claude mcp\n * list` reported \"No MCP servers configured\").\n */\nfunction mcpFilePath(projectDir: string): string {\n return path.join(projectDir, \".mcp.json\");\n}\n\n/** The dead `.claude/mcp.json` of earlier versions; entries in it are removed. */\nfunction legacyMcpFilePath(projectDir: string): string {\n return path.join(projectDir, \".claude\", \"mcp.json\");\n}\n\n/**\n * Drop the skaile entry from an MCP file, deleting the file when nothing is\n * left. Returns whether the file changed.\n */\nasync function removeSkaileMcpEntry(mcpPath: string): Promise<boolean> {\n const mcp = await readJsonStrict<ClaudeMcpFile>(mcpPath);\n if (!mcp?.mcpServers || !(SKAILE_MCP_NAME in mcp.mcpServers)) return false;\n const { [SKAILE_MCP_NAME]: _removed, ...rest } = mcp.mcpServers;\n const updated: ClaudeMcpFile = { ...mcp, mcpServers: rest };\n if (Object.keys(rest).length === 0) delete updated.mcpServers;\n if (Object.keys(updated).length === 0) await fs.unlink(mcpPath);\n else await writeJson(mcpPath, updated);\n return true;\n}\n\n/** Absolute path to the generated Claude Code plugin directory. */\nfunction pluginDir(projectDir: string): string {\n return path.join(projectDir, \".claude\", \"plugins\", \"skaile\");\n}\n\nasync function readJsonStrict<T>(p: string): Promise<T | null> {\n let raw: string;\n try {\n raw = await fs.readFile(p, \"utf-8\");\n } catch {\n return null;\n }\n try {\n return JSON.parse(raw) as T;\n } catch (e: unknown) {\n throw new Error(\n `cannot parse ${p}: ${e instanceof Error ? e.message : String(e)}; please fix or remove`,\n );\n }\n}\n\nasync function writeJson(p: string, value: unknown): Promise<void> {\n await fs.mkdir(path.dirname(p), { recursive: true });\n await fs.writeFile(p, `${JSON.stringify(value, null, 2)}\\n`);\n}\n\n/** Skill folders that are never copied into the plugin: bytecode and dependency trees. */\nconst SKIPPED_IN_SKILL = new Set([\"__pycache__\", \"node_modules\", \".DS_Store\"]);\n\n/**\n * The project's installed skills, name to source folder: the folders with a `SKILL.md` that\n * `skaile add` deployed to `.claude/skills/`, and any under `.skaile/skills/` (the folder an\n * earlier release read; it wins over a deployed copy of the same name).\n */\nasync function installedSkillSources(projectDir: string): Promise<Map<string, string>> {\n const sources = new Map<string, string>();\n for (const root of [\n path.join(projectDir, \".claude\", \"skills\"),\n path.join(projectDir, \".skaile\", \"skills\"),\n ]) {\n for (const name of await fs.readdir(root).catch(() => [] as string[])) {\n const from = path.join(root, name);\n if (existsSync(path.join(from, \"SKILL.md\"))) sources.set(name, from);\n }\n }\n return sources;\n}\n\n/**\n * Fill the plugin's `skills/` with the project's installed skills (p1190). A skill the project\n * no longer has is removed from the plugin, so a repeated install matches the project. A linked\n * skill is copied as files.\n */\nasync function copyInstalledSkills(projectDir: string, skillsDir: string): Promise<void> {\n for (const entry of await fs.readdir(skillsDir).catch(() => [] as string[])) {\n if (entry !== \".gitkeep\")\n await fs.rm(path.join(skillsDir, entry), { recursive: true, force: true });\n }\n for (const [name, from] of await installedSkillSources(projectDir)) {\n await fs.cp(from, path.join(skillsDir, name), {\n recursive: true,\n dereference: true,\n filter: (src) => !SKIPPED_IN_SKILL.has(path.basename(src)),\n });\n }\n}\n\n/** Relative file path to content hash of everything the plugin copy would hold for `dir`. */\nasync function skillDigest(dir: string, rel = \"\"): Promise<string[]> {\n const out: string[] = [];\n for (const entry of (await fs.readdir(path.join(dir, rel)).catch(() => [] as string[])).sort()) {\n if (SKIPPED_IN_SKILL.has(entry)) continue;\n const relPath = path.join(rel, entry);\n const stat = await fs.stat(path.join(dir, relPath)).catch(() => undefined);\n if (!stat) continue;\n if (stat.isDirectory()) out.push(...(await skillDigest(dir, relPath)));\n else {\n const hash = createHash(\"sha256\")\n .update(await fs.readFile(path.join(dir, relPath)))\n .digest(\"hex\");\n out.push(`${relPath}:${hash}`);\n }\n }\n return out;\n}\n\n/**\n * True when the `--full` plugin's `skills/` copy differs from what `copyInstalledSkills` would\n * write now: a skill added, removed or changed since the copy was made (p1350).\n */\nasync function pluginSkillsStale(projectDir: string): Promise<boolean> {\n const skillsDir = path.join(pluginDir(projectDir), \"skills\");\n const expected = await installedSkillSources(projectDir);\n const actual = (await fs.readdir(skillsDir).catch(() => [] as string[])).filter(\n (n) => n !== \".gitkeep\",\n );\n if (actual.length !== expected.size || actual.some((n) => !expected.has(n))) return true;\n for (const [name, from] of expected) {\n const [want, have] = await Promise.all([\n skillDigest(from),\n skillDigest(path.join(skillsDir, name)),\n ]);\n if (want.join(\"\\n\") !== have.join(\"\\n\")) return true;\n }\n return false;\n}\n\n/**\n * Bring the `--full` plugin's skills copy up to date after `add`, `remove`, `install` or\n * `update` changed the project's skills (p1350). Does nothing when the plugin is not installed\n * (the MCP method has no copy) or the copy is current. Returns whether it rewrote the copy.\n */\nexport async function refreshPluginSkills(projectDir: string): Promise<boolean> {\n if ((await detectInstall(projectDir))?.method !== \"plugin\") return false;\n if (!(await pluginSkillsStale(projectDir))) return false;\n await copyInstalledSkills(projectDir, path.join(pluginDir(projectDir), \"skills\"));\n return true;\n}\n\n// ── install ────────────────────────────────────────────────────────────────\n\n/**\n * Install the skaile integration for Claude Code.\n *\n * Two modes:\n * - **mcp** (default) — write project-root `.mcp.json` pointing at `skaile mcp-server`.\n * - **full** (`opts.full`) — generate the `.claude/plugins/skaile/` directory\n * (MCP + lifecycle hooks) and register it in `.claude/settings.json`.\n */\nexport async function install(\n projectDir: string,\n opts?: { full?: boolean },\n): Promise<InstallResult> {\n return opts?.full ? installFullPlugin(projectDir) : installMcp(projectDir);\n}\n\nasync function installMcp(projectDir: string): Promise<InstallResult> {\n const mcpPath = mcpFilePath(projectDir);\n const existing = (await readJsonStrict<ClaudeMcpFile>(mcpPath)) ?? {};\n const mcpServers = { ...(existing.mcpServers ?? {}) };\n const previous = mcpServers[SKAILE_MCP_NAME];\n const matches = previous && JSON.stringify(previous) === JSON.stringify(MCP_SERVER_ENTRY);\n let warning: string | undefined;\n if (previous && !matches) {\n warning = `overwrote existing ${SKAILE_MCP_NAME} entry in ${path.relative(projectDir, mcpPath)}`;\n }\n mcpServers[SKAILE_MCP_NAME] = MCP_SERVER_ENTRY;\n await writeJson(mcpPath, { ...existing, mcpServers });\n const migrated = await removeSkaileMcpEntry(legacyMcpFilePath(projectDir));\n return { changed: !matches || migrated, method: \"mcp\", warning };\n}\n\n/**\n * Unlink `p` when it is a symlink (never what it points at), so the install below writes into\n * a real folder of its own instead of through the link into the user's files (`skills/ ->\n * ../../skills` would otherwise have its target cleared).\n */\nasync function dropSymlink(p: string): Promise<void> {\n const stat = await fs.lstat(p).catch(() => undefined);\n if (stat?.isSymbolicLink()) await fs.unlink(p);\n}\n\nasync function installFullPlugin(projectDir: string): Promise<InstallResult> {\n const dir = pluginDir(projectDir);\n await dropSymlink(dir);\n await dropSymlink(path.join(dir, \"skills\"));\n\n // Generate the plugin directory from the in-memory file map.\n for (const [rel, content] of Object.entries(buildClaudePluginFiles())) {\n const dest = path.join(dir, rel);\n await fs.mkdir(path.dirname(dest), { recursive: true });\n await fs.writeFile(dest, content);\n }\n\n await copyInstalledSkills(projectDir, path.join(dir, \"skills\"));\n\n // Register the generated dir in .claude/settings.json#plugins[].\n const settingsPath = path.join(projectDir, \".claude\", \"settings.json\");\n const settings = (await readJsonStrict<ClaudeSettings>(settingsPath)) ?? {};\n const plugins = settings.plugins ?? [];\n if (!plugins.includes(dir)) plugins.push(dir);\n await writeJson(settingsPath, { ...settings, plugins });\n\n return { changed: true, method: \"plugin\" };\n}\n\n// ── uninstall ──────────────────────────────────────────────────────────────\n\nexport async function uninstall(projectDir: string): Promise<UninstallResult> {\n const settingsPath = path.join(projectDir, \".claude\", \"settings.json\");\n const mcpPath = mcpFilePath(projectDir);\n const dir = pluginDir(projectDir);\n const rel = (p: string): string => path.relative(projectDir, p);\n\n /** Project-relative paths the uninstall changed or deleted, in the order it touched them. */\n const files: string[] = [];\n\n // Remove the generated plugin dir from plugins[] if registered.\n const settings = await readJsonStrict<ClaudeSettings>(settingsPath);\n if (settings && Array.isArray(settings.plugins)) {\n const next = settings.plugins.filter((p) => p !== dir);\n if (next.length !== settings.plugins.length) {\n files.push(rel(settingsPath));\n const updated: ClaudeSettings = { ...settings, plugins: next };\n if (next.length === 0) delete updated.plugins;\n // A settings file that held only the skaile plugin is left empty: delete it.\n if (Object.keys(updated).length === 0) await fs.unlink(settingsPath);\n else await writeJson(settingsPath, updated);\n }\n }\n\n // Delete the generated plugin directory.\n if (existsSync(dir)) {\n await fs.rm(dir, { recursive: true, force: true });\n files.push(rel(dir));\n }\n\n // Remove from .mcp.json — and from the legacy .claude/mcp.json — if present.\n if (await removeSkaileMcpEntry(mcpPath)) files.push(rel(mcpPath));\n const legacyPath = legacyMcpFilePath(projectDir);\n if (await removeSkaileMcpEntry(legacyPath)) files.push(rel(legacyPath));\n\n // Leave no empty `.claude/` or `.claude/plugins/` behind.\n for (const emptyDir of [path.dirname(dir), path.join(projectDir, \".claude\")]) {\n await fs.rmdir(emptyDir).catch(() => undefined);\n }\n\n return { changed: files.length > 0, files };\n}\n\n// ── enable / disable ───────────────────────────────────────────────────────\n\nasync function detectInstall(\n projectDir: string,\n): Promise<{ method: \"plugin\" | \"mcp\"; pluginPath?: string } | null> {\n const dir = pluginDir(projectDir);\n const settings = await readJsonStrict<ClaudeSettings>(\n path.join(projectDir, \".claude\", \"settings.json\"),\n );\n if (\n existsSync(path.join(dir, \".claude-plugin\", \"plugin.json\")) &&\n settings?.plugins?.includes(dir)\n ) {\n return { method: \"plugin\", pluginPath: dir };\n }\n\n const mcp = await readJsonStrict<ClaudeMcpFile>(mcpFilePath(projectDir));\n if (mcp?.mcpServers && SKAILE_MCP_NAME in mcp.mcpServers) {\n return { method: \"mcp\" };\n }\n return null;\n}\n\nexport async function enable(projectDir: string): Promise<ToggleResult> {\n const detected = await detectInstall(projectDir);\n if (!detected) throw new Error(\"plugin not installed for claude-code\");\n\n const settingsPath = path.join(projectDir, \".claude\", \"settings.json\");\n const settings = (await readJsonStrict<ClaudeSettings>(settingsPath)) ?? {};\n\n if (detected.method === \"plugin\") {\n const disabled = settings.disabledPlugins ?? [];\n const next = disabled.filter((p) => p !== detected.pluginPath);\n if (next.length === disabled.length) return { changed: false };\n const updated: ClaudeSettings = { ...settings, disabledPlugins: next };\n if (next.length === 0) delete updated.disabledPlugins;\n await writeJson(settingsPath, updated);\n return { changed: true };\n }\n\n const disabled = settings.disabledMcpjsonServers ?? [];\n const next = disabled.filter((n) => n !== SKAILE_MCP_NAME);\n if (next.length === disabled.length) return { changed: false };\n const updated: ClaudeSettings = { ...settings, disabledMcpjsonServers: next };\n if (next.length === 0) delete updated.disabledMcpjsonServers;\n await writeJson(settingsPath, updated);\n return { changed: true };\n}\n\nexport async function disable(projectDir: string): Promise<ToggleResult> {\n const detected = await detectInstall(projectDir);\n if (!detected) throw new Error(\"plugin not installed for claude-code\");\n\n const settingsPath = path.join(projectDir, \".claude\", \"settings.json\");\n const settings = (await readJsonStrict<ClaudeSettings>(settingsPath)) ?? {};\n\n if (detected.method === \"plugin\") {\n const list = settings.disabledPlugins ?? [];\n if (list.includes(detected.pluginPath as string)) return { changed: false };\n await writeJson(settingsPath, {\n ...settings,\n disabledPlugins: [...list, detected.pluginPath as string],\n });\n return { changed: true };\n }\n\n const list = settings.disabledMcpjsonServers ?? [];\n if (list.includes(SKAILE_MCP_NAME)) return { changed: false };\n await writeJson(settingsPath, {\n ...settings,\n disabledMcpjsonServers: [...list, SKAILE_MCP_NAME],\n });\n return { changed: true };\n}\n\n// ── status ─────────────────────────────────────────────────────────────────\n\nexport async function status(projectDir: string): Promise<HarnessStatus> {\n const settingsPath = path.join(projectDir, \".claude\", \"settings.json\");\n const mcpPath = mcpFilePath(projectDir);\n const dir = pluginDir(projectDir);\n const settings = await readJsonStrict<ClaudeSettings>(settingsPath);\n const mcp = await readJsonStrict<ClaudeMcpFile>(mcpPath);\n\n const pluginInstalled = !!(\n existsSync(path.join(dir, \".claude-plugin\", \"plugin.json\")) && settings?.plugins?.includes(dir)\n );\n const mcpInstalled = !!(mcp?.mcpServers && SKAILE_MCP_NAME in mcp.mcpServers);\n\n if (!pluginInstalled && !mcpInstalled) {\n return {\n harness: \"claude-code\",\n installed: \"no\",\n enabled: \"n/a\",\n method: \"—\",\n location: \"—\",\n };\n }\n\n if (pluginInstalled) {\n const disabled = settings?.disabledPlugins?.includes(dir) ?? false;\n // The plugin's skills are a copy; `add`, `remove`, `install` and `update` refresh it, but a\n // skill changed by hand since is not in it (p1350).\n const stale = await pluginSkillsStale(projectDir);\n return {\n harness: \"claude-code\",\n installed: \"yes\",\n enabled: disabled ? \"no\" : \"yes\",\n method: \"plugin\",\n location: path.relative(projectDir, dir),\n ...(stale\n ? {\n detail:\n \"the skills copy is stale: run skaile integration install --harness claude-code --full\",\n }\n : {}),\n };\n }\n\n const disabled = settings?.disabledMcpjsonServers?.includes(SKAILE_MCP_NAME) ?? false;\n return {\n harness: \"claude-code\",\n installed: \"yes\",\n enabled: disabled ? \"no\" : \"yes\",\n method: \"mcp\",\n location: path.relative(projectDir, mcpPath),\n };\n}\n"]}
1
+ {"version":3,"sources":["../cli/src/commands/plugin/claude-code.ts"],"names":["disabled","next","updated","list"],"mappings":"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","file":"chunk-NJ6UCY2B.js","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { existsSync } from \"node:fs\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { buildClaudePluginFiles } from \"@skaile/workspaces/workspace-plugin\";\nimport type { HarnessStatus, InstallResult, ToggleResult, UninstallResult } from \"./types.ts\";\n\nconst SKAILE_MCP_NAME = \"skaile-workspace\";\n\nconst MCP_SERVER_ENTRY = {\n command: \"skaile\",\n args: [\"mcp-server\"],\n env: {} as Record<string, string>,\n};\n\ninterface ClaudeSettings {\n plugins?: string[];\n enabledPlugins?: string[];\n disabledPlugins?: string[];\n disabledMcpjsonServers?: string[];\n [k: string]: unknown;\n}\n\ninterface ClaudeMcpFile {\n mcpServers?: Record<string, unknown>;\n [k: string]: unknown;\n}\n\n/**\n * Project-scope MCP file. Claude Code reads `<project>/.mcp.json`; the\n * `.claude/mcp.json` that earlier versions wrote is never read (`claude mcp\n * list` reported \"No MCP servers configured\").\n */\nfunction mcpFilePath(projectDir: string): string {\n return path.join(projectDir, \".mcp.json\");\n}\n\n/** The dead `.claude/mcp.json` of earlier versions; entries in it are removed. */\nfunction legacyMcpFilePath(projectDir: string): string {\n return path.join(projectDir, \".claude\", \"mcp.json\");\n}\n\n/**\n * Drop the skaile entry from an MCP file, deleting the file when nothing is\n * left. Returns whether the file changed.\n */\nasync function removeSkaileMcpEntry(mcpPath: string): Promise<boolean> {\n const mcp = await readJsonStrict<ClaudeMcpFile>(mcpPath);\n if (!mcp?.mcpServers || !(SKAILE_MCP_NAME in mcp.mcpServers)) return false;\n const { [SKAILE_MCP_NAME]: _removed, ...rest } = mcp.mcpServers;\n const updated: ClaudeMcpFile = { ...mcp, mcpServers: rest };\n if (Object.keys(rest).length === 0) delete updated.mcpServers;\n if (Object.keys(updated).length === 0) await fs.unlink(mcpPath);\n else await writeJson(mcpPath, updated);\n return true;\n}\n\n/** Absolute path to the generated Claude Code plugin directory. */\nfunction pluginDir(projectDir: string): string {\n return path.join(projectDir, \".claude\", \"plugins\", \"skaile\");\n}\n\nasync function readJsonStrict<T>(p: string): Promise<T | null> {\n let raw: string;\n try {\n raw = await fs.readFile(p, \"utf-8\");\n } catch {\n return null;\n }\n try {\n return JSON.parse(raw) as T;\n } catch (e: unknown) {\n throw new Error(\n `cannot parse ${p}: ${e instanceof Error ? e.message : String(e)}; please fix or remove`,\n );\n }\n}\n\nasync function writeJson(p: string, value: unknown): Promise<void> {\n await fs.mkdir(path.dirname(p), { recursive: true });\n await fs.writeFile(p, `${JSON.stringify(value, null, 2)}\\n`);\n}\n\n/** Skill folders that are never copied into the plugin: bytecode and dependency trees. */\nconst SKIPPED_IN_SKILL = new Set([\"__pycache__\", \"node_modules\", \".DS_Store\"]);\n\n/**\n * The project's installed skills, name to source folder: the folders with a `SKILL.md` that\n * `skaile add` deployed to `.claude/skills/`, and any under `.skaile/skills/` (the folder an\n * earlier release read; it wins over a deployed copy of the same name).\n */\nasync function installedSkillSources(projectDir: string): Promise<Map<string, string>> {\n const sources = new Map<string, string>();\n for (const root of [\n path.join(projectDir, \".claude\", \"skills\"),\n path.join(projectDir, \".skaile\", \"skills\"),\n ]) {\n for (const name of await fs.readdir(root).catch(() => [] as string[])) {\n const from = path.join(root, name);\n if (existsSync(path.join(from, \"SKILL.md\"))) sources.set(name, from);\n }\n }\n return sources;\n}\n\n/**\n * Fill the plugin's `skills/` with the project's installed skills (p1190). A skill the project\n * no longer has is removed from the plugin, so a repeated install matches the project. A linked\n * skill is copied as files.\n */\nasync function copyInstalledSkills(projectDir: string, skillsDir: string): Promise<void> {\n for (const entry of await fs.readdir(skillsDir).catch(() => [] as string[])) {\n if (entry !== \".gitkeep\")\n await fs.rm(path.join(skillsDir, entry), { recursive: true, force: true });\n }\n for (const [name, from] of await installedSkillSources(projectDir)) {\n await fs.cp(from, path.join(skillsDir, name), {\n recursive: true,\n dereference: true,\n filter: (src) => !SKIPPED_IN_SKILL.has(path.basename(src)),\n });\n }\n}\n\n/** Relative file path to content hash of everything the plugin copy would hold for `dir`. */\nasync function skillDigest(dir: string, rel = \"\"): Promise<string[]> {\n const out: string[] = [];\n for (const entry of (await fs.readdir(path.join(dir, rel)).catch(() => [] as string[])).sort()) {\n if (SKIPPED_IN_SKILL.has(entry)) continue;\n const relPath = path.join(rel, entry);\n const stat = await fs.stat(path.join(dir, relPath)).catch(() => undefined);\n if (!stat) continue;\n if (stat.isDirectory()) out.push(...(await skillDigest(dir, relPath)));\n else {\n const hash = createHash(\"sha256\")\n .update(await fs.readFile(path.join(dir, relPath)))\n .digest(\"hex\");\n out.push(`${relPath}:${hash}`);\n }\n }\n return out;\n}\n\n/**\n * True when the `--full` plugin's `skills/` copy differs from what `copyInstalledSkills` would\n * write now: a skill added, removed or changed since the copy was made (p1350).\n */\nasync function pluginSkillsStale(projectDir: string): Promise<boolean> {\n const skillsDir = path.join(pluginDir(projectDir), \"skills\");\n const expected = await installedSkillSources(projectDir);\n const actual = (await fs.readdir(skillsDir).catch(() => [] as string[])).filter(\n (n) => n !== \".gitkeep\",\n );\n if (actual.length !== expected.size || actual.some((n) => !expected.has(n))) return true;\n for (const [name, from] of expected) {\n const [want, have] = await Promise.all([\n skillDigest(from),\n skillDigest(path.join(skillsDir, name)),\n ]);\n if (want.join(\"\\n\") !== have.join(\"\\n\")) return true;\n }\n return false;\n}\n\n/**\n * Bring the `--full` plugin's skills copy up to date after `add`, `remove`, `install` or\n * `update` changed the project's skills (p1350). Does nothing when the plugin is not installed\n * (the MCP method has no copy) or the copy is current. Returns whether it rewrote the copy.\n */\nexport async function refreshPluginSkills(projectDir: string): Promise<boolean> {\n if ((await detectInstall(projectDir))?.method !== \"plugin\") return false;\n if (!(await pluginSkillsStale(projectDir))) return false;\n await copyInstalledSkills(projectDir, path.join(pluginDir(projectDir), \"skills\"));\n return true;\n}\n\n// ── install ────────────────────────────────────────────────────────────────\n\n/**\n * Install the skaile integration for Claude Code.\n *\n * Two modes:\n * - **mcp** (default) — write project-root `.mcp.json` pointing at `skaile mcp-server`.\n * - **full** (`opts.full`) — generate the `.claude/plugins/skaile/` directory\n * (MCP + lifecycle hooks) and register it in `.claude/settings.json`.\n */\nexport async function install(\n projectDir: string,\n opts?: { full?: boolean },\n): Promise<InstallResult> {\n return opts?.full ? installFullPlugin(projectDir) : installMcp(projectDir);\n}\n\nasync function installMcp(projectDir: string): Promise<InstallResult> {\n const mcpPath = mcpFilePath(projectDir);\n const existing = (await readJsonStrict<ClaudeMcpFile>(mcpPath)) ?? {};\n const mcpServers = { ...(existing.mcpServers ?? {}) };\n const previous = mcpServers[SKAILE_MCP_NAME];\n const matches = previous && JSON.stringify(previous) === JSON.stringify(MCP_SERVER_ENTRY);\n let warning: string | undefined;\n if (previous && !matches) {\n warning = `overwrote existing ${SKAILE_MCP_NAME} entry in ${path.relative(projectDir, mcpPath)}`;\n }\n mcpServers[SKAILE_MCP_NAME] = MCP_SERVER_ENTRY;\n await writeJson(mcpPath, { ...existing, mcpServers });\n const migrated = await removeSkaileMcpEntry(legacyMcpFilePath(projectDir));\n return { changed: !matches || migrated, method: \"mcp\", warning };\n}\n\n/**\n * Unlink `p` when it is a symlink (never what it points at), so the install below writes into\n * a real folder of its own instead of through the link into the user's files (`skills/ ->\n * ../../skills` would otherwise have its target cleared).\n */\nasync function dropSymlink(p: string): Promise<void> {\n const stat = await fs.lstat(p).catch(() => undefined);\n if (stat?.isSymbolicLink()) await fs.unlink(p);\n}\n\nasync function installFullPlugin(projectDir: string): Promise<InstallResult> {\n const dir = pluginDir(projectDir);\n await dropSymlink(dir);\n await dropSymlink(path.join(dir, \"skills\"));\n\n // Generate the plugin directory from the in-memory file map.\n for (const [rel, content] of Object.entries(buildClaudePluginFiles())) {\n const dest = path.join(dir, rel);\n await fs.mkdir(path.dirname(dest), { recursive: true });\n await fs.writeFile(dest, content);\n }\n\n await copyInstalledSkills(projectDir, path.join(dir, \"skills\"));\n\n // Register the generated dir in .claude/settings.json#plugins[].\n const settingsPath = path.join(projectDir, \".claude\", \"settings.json\");\n const settings = (await readJsonStrict<ClaudeSettings>(settingsPath)) ?? {};\n const plugins = settings.plugins ?? [];\n if (!plugins.includes(dir)) plugins.push(dir);\n await writeJson(settingsPath, { ...settings, plugins });\n\n return { changed: true, method: \"plugin\" };\n}\n\n// ── uninstall ──────────────────────────────────────────────────────────────\n\nexport async function uninstall(projectDir: string): Promise<UninstallResult> {\n const settingsPath = path.join(projectDir, \".claude\", \"settings.json\");\n const mcpPath = mcpFilePath(projectDir);\n const dir = pluginDir(projectDir);\n const rel = (p: string): string => path.relative(projectDir, p);\n\n /** Project-relative paths the uninstall changed or deleted, in the order it touched them. */\n const files: string[] = [];\n\n // Remove the generated plugin dir from plugins[] if registered.\n const settings = await readJsonStrict<ClaudeSettings>(settingsPath);\n if (settings && Array.isArray(settings.plugins)) {\n const next = settings.plugins.filter((p) => p !== dir);\n if (next.length !== settings.plugins.length) {\n files.push(rel(settingsPath));\n const updated: ClaudeSettings = { ...settings, plugins: next };\n if (next.length === 0) delete updated.plugins;\n // A settings file that held only the skaile plugin is left empty: delete it.\n if (Object.keys(updated).length === 0) await fs.unlink(settingsPath);\n else await writeJson(settingsPath, updated);\n }\n }\n\n // Delete the generated plugin directory.\n if (existsSync(dir)) {\n await fs.rm(dir, { recursive: true, force: true });\n files.push(rel(dir));\n }\n\n // Remove from .mcp.json — and from the legacy .claude/mcp.json — if present.\n if (await removeSkaileMcpEntry(mcpPath)) files.push(rel(mcpPath));\n const legacyPath = legacyMcpFilePath(projectDir);\n if (await removeSkaileMcpEntry(legacyPath)) files.push(rel(legacyPath));\n\n // Leave no empty `.claude/` or `.claude/plugins/` behind.\n for (const emptyDir of [path.dirname(dir), path.join(projectDir, \".claude\")]) {\n await fs.rmdir(emptyDir).catch(() => undefined);\n }\n\n return { changed: files.length > 0, files };\n}\n\n// ── enable / disable ───────────────────────────────────────────────────────\n\nasync function detectInstall(\n projectDir: string,\n): Promise<{ method: \"plugin\" | \"mcp\"; pluginPath?: string } | null> {\n const dir = pluginDir(projectDir);\n const settings = await readJsonStrict<ClaudeSettings>(\n path.join(projectDir, \".claude\", \"settings.json\"),\n );\n if (\n existsSync(path.join(dir, \".claude-plugin\", \"plugin.json\")) &&\n settings?.plugins?.includes(dir)\n ) {\n return { method: \"plugin\", pluginPath: dir };\n }\n\n const mcp = await readJsonStrict<ClaudeMcpFile>(mcpFilePath(projectDir));\n if (mcp?.mcpServers && SKAILE_MCP_NAME in mcp.mcpServers) {\n return { method: \"mcp\" };\n }\n return null;\n}\n\nexport async function enable(projectDir: string): Promise<ToggleResult> {\n const detected = await detectInstall(projectDir);\n if (!detected) throw new Error(\"plugin not installed for claude-code\");\n\n const settingsPath = path.join(projectDir, \".claude\", \"settings.json\");\n const settings = (await readJsonStrict<ClaudeSettings>(settingsPath)) ?? {};\n\n if (detected.method === \"plugin\") {\n const disabled = settings.disabledPlugins ?? [];\n const next = disabled.filter((p) => p !== detected.pluginPath);\n if (next.length === disabled.length) return { changed: false };\n const updated: ClaudeSettings = { ...settings, disabledPlugins: next };\n if (next.length === 0) delete updated.disabledPlugins;\n await writeJson(settingsPath, updated);\n return { changed: true };\n }\n\n const disabled = settings.disabledMcpjsonServers ?? [];\n const next = disabled.filter((n) => n !== SKAILE_MCP_NAME);\n if (next.length === disabled.length) return { changed: false };\n const updated: ClaudeSettings = { ...settings, disabledMcpjsonServers: next };\n if (next.length === 0) delete updated.disabledMcpjsonServers;\n await writeJson(settingsPath, updated);\n return { changed: true };\n}\n\nexport async function disable(projectDir: string): Promise<ToggleResult> {\n const detected = await detectInstall(projectDir);\n if (!detected) throw new Error(\"plugin not installed for claude-code\");\n\n const settingsPath = path.join(projectDir, \".claude\", \"settings.json\");\n const settings = (await readJsonStrict<ClaudeSettings>(settingsPath)) ?? {};\n\n if (detected.method === \"plugin\") {\n const list = settings.disabledPlugins ?? [];\n if (list.includes(detected.pluginPath as string)) return { changed: false };\n await writeJson(settingsPath, {\n ...settings,\n disabledPlugins: [...list, detected.pluginPath as string],\n });\n return { changed: true };\n }\n\n const list = settings.disabledMcpjsonServers ?? [];\n if (list.includes(SKAILE_MCP_NAME)) return { changed: false };\n await writeJson(settingsPath, {\n ...settings,\n disabledMcpjsonServers: [...list, SKAILE_MCP_NAME],\n });\n return { changed: true };\n}\n\n// ── status ─────────────────────────────────────────────────────────────────\n\nexport async function status(projectDir: string): Promise<HarnessStatus> {\n const settingsPath = path.join(projectDir, \".claude\", \"settings.json\");\n const mcpPath = mcpFilePath(projectDir);\n const dir = pluginDir(projectDir);\n const settings = await readJsonStrict<ClaudeSettings>(settingsPath);\n const mcp = await readJsonStrict<ClaudeMcpFile>(mcpPath);\n\n const pluginInstalled = !!(\n existsSync(path.join(dir, \".claude-plugin\", \"plugin.json\")) && settings?.plugins?.includes(dir)\n );\n const mcpInstalled = !!(mcp?.mcpServers && SKAILE_MCP_NAME in mcp.mcpServers);\n\n if (!pluginInstalled && !mcpInstalled) {\n return {\n harness: \"claude-code\",\n installed: \"no\",\n enabled: \"n/a\",\n method: \"—\",\n location: \"—\",\n };\n }\n\n if (pluginInstalled) {\n const disabled = settings?.disabledPlugins?.includes(dir) ?? false;\n // The plugin's skills are a copy; `add`, `remove`, `install` and `update` refresh it, but a\n // skill changed by hand since is not in it (p1350).\n const stale = await pluginSkillsStale(projectDir);\n return {\n harness: \"claude-code\",\n installed: \"yes\",\n enabled: disabled ? \"no\" : \"yes\",\n method: \"plugin\",\n location: path.relative(projectDir, dir),\n ...(stale\n ? {\n detail:\n \"the skills copy is stale: run skaile integration install --harness claude-code --full\",\n }\n : {}),\n };\n }\n\n const disabled = settings?.disabledMcpjsonServers?.includes(SKAILE_MCP_NAME) ?? false;\n return {\n harness: \"claude-code\",\n installed: \"yes\",\n enabled: disabled ? \"no\" : \"yes\",\n method: \"mcp\",\n location: path.relative(projectDir, mcpPath),\n };\n}\n"]}
@@ -305,5 +305,5 @@ function resolveProjectDir(additionalEnvVars) {
305
305
  }
306
306
 
307
307
  export { PluginStore, WorkspacePlugin, buildClaudePluginFiles, resolveProjectDir };
308
- //# sourceMappingURL=chunk-OKPITFE3.js.map
309
- //# sourceMappingURL=chunk-OKPITFE3.js.map
308
+ //# sourceMappingURL=chunk-NNJQQYHY.js.map
309
+ //# sourceMappingURL=chunk-NNJQQYHY.js.map