@skaile/workspaces 3.2.1 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/CHANGELOG.md +168 -0
  2. package/dist/{asset-feeds-SEYSNNEM.js → asset-feeds-CBY723RG.js} +11 -11
  3. package/dist/{asset-feeds-SEYSNNEM.js.map → asset-feeds-CBY723RG.js.map} +1 -1
  4. package/dist/asset-manager/catalog-deployer.js +7 -7
  5. package/dist/asset-manager/index.js +10 -10
  6. package/dist/asset-manager/installer.js +7 -7
  7. package/dist/bridge/drivers/claude-sdk.js +8 -8
  8. package/dist/bridge/drivers/codex.js +3 -3
  9. package/dist/bridge/drivers/continue.js +4 -4
  10. package/dist/bridge/drivers/echo.js +3 -3
  11. package/dist/bridge/drivers/gemini.js +5 -5
  12. package/dist/bridge/drivers/goose.js +4 -4
  13. package/dist/bridge/drivers/omp.js +3 -3
  14. package/dist/bridge/drivers/opencode.js +4 -4
  15. package/dist/bridge/drivers/qwen.js +5 -5
  16. package/dist/bridge/index.js +9 -9
  17. package/dist/{chunk-57RFYEVM.js → chunk-2Y2AWWWM.js} +4 -10
  18. package/dist/chunk-2Y2AWWWM.js.map +1 -0
  19. package/dist/{chunk-O6B3ZZ5S.js → chunk-3A3ETJFD.js} +4 -4
  20. package/dist/{chunk-O6B3ZZ5S.js.map → chunk-3A3ETJFD.js.map} +1 -1
  21. package/dist/{chunk-7GOCNYUV.js → chunk-3A5IKBHQ.js} +47 -47
  22. package/dist/{chunk-7GOCNYUV.js.map → chunk-3A5IKBHQ.js.map} +1 -1
  23. package/dist/{chunk-I7S6DFRU.js → chunk-3Q7JDLLH.js} +46 -6
  24. package/dist/chunk-3Q7JDLLH.js.map +1 -0
  25. package/dist/{chunk-PTAUN5CY.js → chunk-3UGP7I2Y.js} +3 -3
  26. package/dist/{chunk-PTAUN5CY.js.map → chunk-3UGP7I2Y.js.map} +1 -1
  27. package/dist/{chunk-CZZRDYT4.js → chunk-4VTBZ2MF.js} +4 -4
  28. package/dist/{chunk-CZZRDYT4.js.map → chunk-4VTBZ2MF.js.map} +1 -1
  29. package/dist/{chunk-U5POE4BA.js → chunk-5Q7HA4JP.js} +3 -3
  30. package/dist/{chunk-U5POE4BA.js.map → chunk-5Q7HA4JP.js.map} +1 -1
  31. package/dist/{chunk-P43HBWKK.js → chunk-6BV24JPZ.js} +36 -7
  32. package/dist/chunk-6BV24JPZ.js.map +1 -0
  33. package/dist/{chunk-AM5LFRXJ.js → chunk-A7XP4C7C.js} +3 -3
  34. package/dist/{chunk-AM5LFRXJ.js.map → chunk-A7XP4C7C.js.map} +1 -1
  35. package/dist/{chunk-ZNIHTT22.js → chunk-AXFOJ2UG.js} +5 -5
  36. package/dist/{chunk-ZNIHTT22.js.map → chunk-AXFOJ2UG.js.map} +1 -1
  37. package/dist/{chunk-PKEAYYEJ.js → chunk-BCXI27BH.js} +219 -4
  38. package/dist/chunk-BCXI27BH.js.map +1 -0
  39. package/dist/{chunk-3HVASHBF.js → chunk-BJ7QIUBM.js} +6 -15
  40. package/dist/chunk-BJ7QIUBM.js.map +1 -0
  41. package/dist/{chunk-CH3UTJBT.js → chunk-BYPQHEI4.js} +6 -6
  42. package/dist/{chunk-CH3UTJBT.js.map → chunk-BYPQHEI4.js.map} +1 -1
  43. package/dist/{chunk-NWFZUQPF.js → chunk-DFZ6QENJ.js} +18 -18
  44. package/dist/{chunk-NWFZUQPF.js.map → chunk-DFZ6QENJ.js.map} +1 -1
  45. package/dist/{chunk-F5VREBV2.js → chunk-ELPR5XBG.js} +40 -3
  46. package/dist/chunk-ELPR5XBG.js.map +1 -0
  47. package/dist/{chunk-MWFYXUFK.js → chunk-G2CXWUO5.js} +4 -4
  48. package/dist/{chunk-MWFYXUFK.js.map → chunk-G2CXWUO5.js.map} +1 -1
  49. package/dist/{chunk-BEAIJRVF.js → chunk-GHWJHOYX.js} +4 -4
  50. package/dist/{chunk-BEAIJRVF.js.map → chunk-GHWJHOYX.js.map} +1 -1
  51. package/dist/{chunk-FLBDLF3I.js → chunk-HGFKNJA5.js} +6 -4
  52. package/dist/chunk-HGFKNJA5.js.map +1 -0
  53. package/dist/{chunk-BK3WL3QK.js → chunk-JAHHMZWG.js} +3 -3
  54. package/dist/{chunk-BK3WL3QK.js.map → chunk-JAHHMZWG.js.map} +1 -1
  55. package/dist/{chunk-UFY2HFLO.js → chunk-KBXYT5HD.js} +4 -4
  56. package/dist/{chunk-UFY2HFLO.js.map → chunk-KBXYT5HD.js.map} +1 -1
  57. package/dist/{chunk-DNYE3SSL.js → chunk-MR3LQTGA.js} +4 -4
  58. package/dist/{chunk-DNYE3SSL.js.map → chunk-MR3LQTGA.js.map} +1 -1
  59. package/dist/{chunk-HCNVX34E.js → chunk-OI36ITDV.js} +8 -8
  60. package/dist/{chunk-HCNVX34E.js.map → chunk-OI36ITDV.js.map} +1 -1
  61. package/dist/{chunk-BEA7RXI3.js → chunk-P6V5GORE.js} +230 -41
  62. package/dist/chunk-P6V5GORE.js.map +1 -0
  63. package/dist/{chunk-2OCYS5EP.js → chunk-PEGIQ67F.js} +4 -4
  64. package/dist/{chunk-2OCYS5EP.js.map → chunk-PEGIQ67F.js.map} +1 -1
  65. package/dist/{chunk-I7MKEZ6J.js → chunk-PHJ2DU6D.js} +3 -3
  66. package/dist/{chunk-I7MKEZ6J.js.map → chunk-PHJ2DU6D.js.map} +1 -1
  67. package/dist/{chunk-NLZYBWVH.js → chunk-POYF2P7E.js} +10 -10
  68. package/dist/{chunk-NLZYBWVH.js.map → chunk-POYF2P7E.js.map} +1 -1
  69. package/dist/{chunk-NU4E7C5I.js → chunk-PW4SJE3X.js} +3 -3
  70. package/dist/{chunk-NU4E7C5I.js.map → chunk-PW4SJE3X.js.map} +1 -1
  71. package/dist/{chunk-Q26C5GLO.js → chunk-RD6MMBHQ.js} +3 -3
  72. package/dist/{chunk-Q26C5GLO.js.map → chunk-RD6MMBHQ.js.map} +1 -1
  73. package/dist/{chunk-46LE3GNY.js → chunk-RUFTB7QN.js} +3 -3
  74. package/dist/{chunk-46LE3GNY.js.map → chunk-RUFTB7QN.js.map} +1 -1
  75. package/dist/{chunk-BKUANJB7.js → chunk-S5N33NGZ.js} +50 -64
  76. package/dist/chunk-S5N33NGZ.js.map +1 -0
  77. package/dist/{chunk-IAIHZRG7.js → chunk-SJQ7544V.js} +7 -7
  78. package/dist/{chunk-IAIHZRG7.js.map → chunk-SJQ7544V.js.map} +1 -1
  79. package/dist/{chunk-VT6HDD2L.js → chunk-SUGDZOYZ.js} +3 -3
  80. package/dist/{chunk-VT6HDD2L.js.map → chunk-SUGDZOYZ.js.map} +1 -1
  81. package/dist/{chunk-XVIFPGYH.js → chunk-U2KIY532.js} +3 -3
  82. package/dist/{chunk-XVIFPGYH.js.map → chunk-U2KIY532.js.map} +1 -1
  83. package/dist/{chunk-HWEW3VII.js → chunk-VFL4STB7.js} +3 -3
  84. package/dist/{chunk-HWEW3VII.js.map → chunk-VFL4STB7.js.map} +1 -1
  85. package/dist/{chunk-DYMWJAL4.js → chunk-VQ4P7UYE.js} +3 -3
  86. package/dist/{chunk-DYMWJAL4.js.map → chunk-VQ4P7UYE.js.map} +1 -1
  87. package/dist/{chunk-KPZZI2KG.js → chunk-ZCIK44M7.js} +3 -3
  88. package/dist/{chunk-KPZZI2KG.js.map → chunk-ZCIK44M7.js.map} +1 -1
  89. package/dist/cli/index.js +396 -409
  90. package/dist/cli/index.js.map +1 -1
  91. package/dist/cli/src/commands/complete-resolvers.d.ts +4 -0
  92. package/dist/cli/src/commands/complete-resolvers.d.ts.map +1 -1
  93. package/dist/cli/src/commands/flow.d.ts +8 -4
  94. package/dist/cli/src/commands/flow.d.ts.map +1 -1
  95. package/dist/cli/src/commands/run.d.ts.map +1 -1
  96. package/dist/cli/src/commands/validate.d.ts.map +1 -1
  97. package/dist/cli/src/flow-discovery.d.ts +31 -15
  98. package/dist/cli/src/flow-discovery.d.ts.map +1 -1
  99. package/dist/cli/src/paths.d.ts +9 -19
  100. package/dist/cli/src/paths.d.ts.map +1 -1
  101. package/dist/connectors/config.js +7 -7
  102. package/dist/connectors/index.js +14 -14
  103. package/dist/core/index.js +6 -6
  104. package/dist/core/logging.js +2 -2
  105. package/dist/core/manifest.js +2 -2
  106. package/dist/core/runtime-assets.js +4 -4
  107. package/dist/core/src/asset-roots.d.ts +74 -0
  108. package/dist/core/src/asset-roots.d.ts.map +1 -0
  109. package/dist/core/src/index.d.ts +1 -0
  110. package/dist/core/src/index.d.ts.map +1 -1
  111. package/dist/core/workspace-config.js +3 -3
  112. package/dist/deploy/index.js +6 -6
  113. package/dist/discovery/index.js +3 -3
  114. package/dist/{ensure-sources-IM3JMW2L.js → ensure-sources-O6AWQXAY.js} +13 -13
  115. package/dist/{ensure-sources-IM3JMW2L.js.map → ensure-sources-O6AWQXAY.js.map} +1 -1
  116. package/dist/factory-assets/connectors/box.js +14 -14
  117. package/dist/factory-assets/connectors/deploy.js +14 -14
  118. package/dist/factory-assets/connectors/devserver.js +14 -14
  119. package/dist/factory-assets/connectors/flow/CONNECTOR.md +42 -9
  120. package/dist/factory-assets/connectors/flow/adapter.d.ts +1 -1
  121. package/dist/factory-assets/connectors/flow/adapter.d.ts.map +1 -1
  122. package/dist/factory-assets/connectors/flow/adapter.js +14 -14
  123. package/dist/factory-assets/connectors/flow/engine/index.d.ts +2 -0
  124. package/dist/factory-assets/connectors/flow/engine/index.d.ts.map +1 -1
  125. package/dist/factory-assets/connectors/flow/engine/locate.d.ts +75 -0
  126. package/dist/factory-assets/connectors/flow/engine/locate.d.ts.map +1 -0
  127. package/dist/factory-assets/connectors/flow/engine/node-shape.d.ts +56 -0
  128. package/dist/factory-assets/connectors/flow/engine/node-shape.d.ts.map +1 -1
  129. package/dist/factory-assets/connectors/flow/engine.js +8 -8
  130. package/dist/factory-assets/connectors/flow/index.d.ts +1 -1
  131. package/dist/factory-assets/connectors/flow/index.d.ts.map +1 -1
  132. package/dist/factory-assets/connectors/flow/node-executor.d.ts +23 -3
  133. package/dist/factory-assets/connectors/flow/node-executor.d.ts.map +1 -1
  134. package/dist/factory-assets/connectors/flow/prompt-fragments.d.ts +4 -0
  135. package/dist/factory-assets/connectors/flow/prompt-fragments.d.ts.map +1 -1
  136. package/dist/factory-assets/connectors/flow/prompt-fragments.js +2 -2
  137. package/dist/factory-assets/connectors/flow/run-flow.d.ts +9 -1
  138. package/dist/factory-assets/connectors/flow/run-flow.d.ts.map +1 -1
  139. package/dist/factory-assets/connectors/flow/run-flow.js +16 -16
  140. package/dist/factory-assets/connectors/flow.js +14 -14
  141. package/dist/factory-assets/connectors/git.js +14 -14
  142. package/dist/factory-assets/connectors/gmail.js +14 -14
  143. package/dist/factory-assets/connectors/googledrive.js +14 -14
  144. package/dist/factory-assets/connectors/local.js +14 -14
  145. package/dist/factory-assets/connectors/mattermost.js +14 -14
  146. package/dist/factory-assets/connectors/memory.js +14 -14
  147. package/dist/factory-assets/connectors/minio.js +14 -14
  148. package/dist/factory-assets/connectors/postgres.js +14 -14
  149. package/dist/factory-assets/connectors/s3.js +14 -14
  150. package/dist/factory-assets/connectors/sharepoint.js +14 -14
  151. package/dist/factory-assets/connectors/sqlite.js +14 -14
  152. package/dist/factory-assets/connectors/static-server.js +14 -14
  153. package/dist/factory-assets/connectors/tunnel.js +14 -14
  154. package/dist/factory-assets/connectors/webdav.js +14 -14
  155. package/dist/factory-assets/connectors/xstate-store.js +14 -14
  156. package/dist/factory-assets/connectors/xstate.js +14 -14
  157. package/dist/flows-66NOT5N6.js +20 -0
  158. package/dist/{flows-PBNW6BEH.js.map → flows-66NOT5N6.js.map} +1 -1
  159. package/dist/{helpers-LJFOH4RB.js → helpers-QUGRH2MW.js} +13 -13
  160. package/dist/{helpers-LJFOH4RB.js.map → helpers-QUGRH2MW.js.map} +1 -1
  161. package/dist/library/index.js +8 -8
  162. package/dist/library/install.js +6 -6
  163. package/dist/library/src/config.d.ts +6 -6
  164. package/dist/library/src/config.d.ts.map +1 -1
  165. package/dist/{library-publish-BEFA7OCS.js → library-publish-TCT652H4.js} +18 -18
  166. package/dist/{library-publish-BEFA7OCS.js.map → library-publish-TCT652H4.js.map} +1 -1
  167. package/dist/open-library-6OSZX2O6.js +32 -0
  168. package/dist/{open-library-WJ7P7B4N.js.map → open-library-6OSZX2O6.js.map} +1 -1
  169. package/dist/paths-ESJHWGBR.js +16 -0
  170. package/dist/{paths-2ZZMB7HU.js.map → paths-ESJHWGBR.js.map} +1 -1
  171. package/dist/{plugin-store-EOP3S7OU.js → plugin-store-3BYOBKYT.js} +8 -8
  172. package/dist/{plugin-store-EOP3S7OU.js.map → plugin-store-3BYOBKYT.js.map} +1 -1
  173. package/dist/{project-6RXDBSRV.js → project-5RGSHTZ7.js} +14 -14
  174. package/dist/{project-6RXDBSRV.js.map → project-5RGSHTZ7.js.map} +1 -1
  175. package/dist/resolver/index.js +7 -7
  176. package/dist/runner/index.js +19 -19
  177. package/dist/sdk/asset-manager.js +10 -10
  178. package/dist/sdk/bridge.js +9 -9
  179. package/dist/sdk/core.js +6 -6
  180. package/dist/sdk/flow.js +8 -8
  181. package/dist/sdk/index.js +19 -19
  182. package/dist/sdk/resolver.js +7 -7
  183. package/dist/sdk/runner.js +19 -19
  184. package/dist/sdk/session.js +1 -1
  185. package/dist/session/index.js +1 -1
  186. package/dist/session/src/dispatcher.d.ts +15 -0
  187. package/dist/session/src/dispatcher.d.ts.map +1 -1
  188. package/dist/session/src/workspace-change-digest.d.ts +34 -0
  189. package/dist/session/src/workspace-change-digest.d.ts.map +1 -0
  190. package/dist/{setup-XI7TCIGF.js → setup-X2T3NW7P.js} +11 -11
  191. package/dist/{setup-XI7TCIGF.js.map → setup-X2T3NW7P.js.map} +1 -1
  192. package/dist/skills-TYMTW4V4.js +20 -0
  193. package/dist/{skills-5PJ6YVM7.js.map → skills-TYMTW4V4.js.map} +1 -1
  194. package/dist/{source-TJ457ZRQ.js → source-JS3G5P6Z.js} +17 -17
  195. package/dist/{source-TJ457ZRQ.js.map → source-JS3G5P6Z.js.map} +1 -1
  196. package/dist/{source-manifest-publish-R3Q4MT5W.js → source-manifest-publish-Y7BOFFO4.js} +19 -19
  197. package/dist/{source-manifest-publish-R3Q4MT5W.js.map → source-manifest-publish-Y7BOFFO4.js.map} +1 -1
  198. package/dist/tui/index.js +19 -19
  199. package/dist/workspace-plugin/adapters/mcp.js +11 -11
  200. package/dist/workspace-plugin/adapters/omp.js +7 -7
  201. package/dist/workspace-plugin/index.js +3 -3
  202. package/package.json +1 -1
  203. package/dist/chunk-3HVASHBF.js.map +0 -1
  204. package/dist/chunk-57RFYEVM.js.map +0 -1
  205. package/dist/chunk-BEA7RXI3.js.map +0 -1
  206. package/dist/chunk-BKUANJB7.js.map +0 -1
  207. package/dist/chunk-F5VREBV2.js.map +0 -1
  208. package/dist/chunk-FLBDLF3I.js.map +0 -1
  209. package/dist/chunk-I7S6DFRU.js.map +0 -1
  210. package/dist/chunk-P43HBWKK.js.map +0 -1
  211. package/dist/chunk-PKEAYYEJ.js.map +0 -1
  212. package/dist/flows-PBNW6BEH.js +0 -20
  213. package/dist/open-library-WJ7P7B4N.js +0 -32
  214. package/dist/paths-2ZZMB7HU.js +0 -22
  215. package/dist/skills-5PJ6YVM7.js +0 -20
package/CHANGELOG.md CHANGED
@@ -1,5 +1,173 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#540](https://github.com/skaile-ai/workspaces/pull/540) [`876318e`](https://github.com/skaile-ai/workspaces/commit/876318e3cf0e69249b61f95725509e8f7725f507) Thanks [@peteralbert](https://github.com/peteralbert)! - Abort a `function`/`check` node's spawned process when its `control.timeoutSec`
8
+ fires. `NodeExecutor.execute` gains an optional second parameter carrying an
9
+ `AbortSignal`; `SessionNodeExecutor` honors it by killing the spawned process
10
+ _group_ with `SIGTERM`, escalating to `SIGKILL` after a 2s grace, and settling
11
+ only once the child has exited. The drain then gates the next deterministic
12
+ spawn on that settlement, because `abort()` only _starts_ a kill — without the
13
+ gate a retry is spawned during the escalation grace and, for a command that
14
+ traps `SIGTERM`, all `N + 1` attempts run at once. Previously the timeout only
15
+ stopped the adapter _waiting_: with `control.retries: N` up to N+1 orphaned
16
+ processes could run at once, all producing results guaranteed to be discarded.
17
+
18
+ The seam change is additive. An executor that declares only `(request)` still
19
+ satisfies the interface and keeps today's behavior — the node still fails on
20
+ timeout and a late settlement is still rejected; only the abandoned work goes
21
+ unreaped. One behavioral note for such an executor: because the retry gate waits
22
+ for the aborted attempt to settle, an executor that never honors the abort now
23
+ delays its own retry by that bounded budget. A timed-out node with no retry left
24
+ is never waited on, so a hung executor still cannot stall the drain's return.
25
+
26
+ Deterministic nodes now spawn `detached`, which is what makes the kill reach the
27
+ command's own children; the executor group-kills anything still live at process
28
+ exit to compensate for the child no longer inheriting the runtime's group
29
+ signals. Timeout computation, retry budgeting, and late-settlement rejection are
30
+ unchanged, and the `subprompt` seam still has no cancellation.
31
+
32
+ ### Patch Changes
33
+
34
+ - [#545](https://github.com/skaile-ai/workspaces/pull/545) [`3ab5535`](https://github.com/skaile-ai/workspaces/commit/3ab553531f2c0300e3a32df22e703936a86ad5b2) Thanks [@peteralbert](https://github.com/peteralbert)! - Deliver watched workspace changes that arrive while an agent is idle as one
35
+ bounded, escaped digest on the next agent-visible message. The digest is never
36
+ persisted as user-authored chat, active-turn file events are suppressed, and a
37
+ failed client send restores pending changes for the next eligible delivery.
38
+
39
+ - [#543](https://github.com/skaile-ai/workspaces/pull/543) [`12c03ca`](https://github.com/skaile-ai/workspaces/commit/12c03ca69f632a704afe8efcc9378c085a4e9fa4) Thanks [@peteralbert](https://github.com/peteralbert)! - Make `resolveFlowPath` accept every flow file discovery accepts, so a flow
40
+ `skaile flow list` shows is one `skaile resume`, a sub-flow `run.flow` target and
41
+ `skaile serve` can actually find. `resolveFlowPath` had its own hard-coded
42
+ candidate list — `.flow.yaml` plus a legacy bare `.json` under `ai-assets/` — and
43
+ its own root walk, while discovery had grown to `.flow.yaml` / `.flow.yml` /
44
+ `.flow.json` / `.json` across flat and per-flow-directory layouts and every
45
+ content root. Nobody widened the second list when the first grew, so
46
+ `.skaile/flows/<id>/<id>.flow.json`, `.skaile/flows/<id>.flow.json` and any
47
+ `.flow.yml` reported `Flow not found: <id>` for a file that was present, parsed,
48
+ and had been listed one command earlier.
49
+
50
+ Both surfaces now go through one definition: `flowLookupRoots` (root policy) and
51
+ `findFlowFileInRoots` (filename policy), exported from the flow engine. The root
52
+ policy itself moved down into `@skaile/workspaces/core` as `aiResourceRoots` /
53
+ `skaileHomeDir` / `resolveLibraryDir`, because both layers that need it already
54
+ depend on `core` while a `factory-assets → cli` import would be a module cycle.
55
+ `@skaile/workspaces/library` and the CLI's `paths` module re-export the same
56
+ symbols, so no public API changed.
57
+
58
+ Two behaviour changes worth knowing:
59
+
60
+ 1. **Resolution is by declared id, not filename.** `resolveFlowPath` used to
61
+ accept `<id>.flow.yaml` on its name alone and let `loadFlow` throw afterwards.
62
+ It now matches the declared `id`, like `run` and `flow list` already did. A
63
+ file whose declared id disagrees with its name is reported by path and
64
+ declared id instead of resolving — and `Flow not found` now names the roots
65
+ searched, the accepted filenames, and any such near miss.
66
+ 2. **`resume` / sub-flow targets / `serve` reach the same roots `run` does** —
67
+ factory assets, user libraries and the global `~/.skaile/` install root, in
68
+ addition to the project's `.skaile/` and `ai-assets/`. That is the point of
69
+ the fix (resume must find what run ran), not an escalation: those are the
70
+ roots `skaile run` already executes from.
71
+ 3. **Root precedence follows `run` too.** `resolveFlowPath` used to probe
72
+ `<project>/.skaile/flows/` before walking up to `ai-assets/`; it now searches
73
+ the authored `ai-assets/` tree first, which is the order `skaile run` has
74
+ always used so a workspace can shadow a built-in flow. For a flow id present
75
+ in _both_, a resumed session therefore executes the authored source rather
76
+ than the deployed, `patches:`-applied copy under `.skaile/` — the same bytes
77
+ `run` would have executed for that id.
78
+
79
+ The `isValidAssetName` identity guard and the "Flow path escapes its lookup root"
80
+ containment guard are unchanged; widening the accepted extensions does not widen
81
+ which paths are reachable.
82
+
83
+ ## 3.3.0
84
+
85
+ ### Minor Changes
86
+
87
+ - [#528](https://github.com/skaile-ai/workspaces/pull/528) [`864fdd4`](https://github.com/skaile-ai/workspaces/commit/864fdd49cf91815a9fd5331594212ef1b469f389) Thanks [@peteralbert](https://github.com/peteralbert)! - Render a predecessor's declared output fields in `build_handoff`, so a downstream agent reading it as context sees the typed answer and not only the prose.
88
+
89
+ `build_handoff`'s `## Upstream handoff` block rendered each completed predecessor as `summary` + `artifacts` + `concerns` and never its `output.fields`. A node's structured output was therefore reachable only by a consumer that bound it through an expression (`nodes.<id>.output.fields.<name>`); an agent node that simply read its upstream handoff saw the producer's prose and never its typed answer. This affected every producer kind — a `function` node capturing stdout into `fields` behaved the same way — and Flow v2 `subprompt` nodes made it far likelier to hit.
90
+
91
+ Each completed predecessor's block now carries a `Fields:` list between the summary and the artifacts. It is **declared-only**: just the top-level `properties` of the producer's `contract.output.fields`, in declaration order, which is already the only surface a consumer may bind — undeclared keys an agent happened to send stay scratch rather than becoming a second, unvalidated contract. It is **bounded** by `HANDOFF_FIELD_VALUE_MAX_CHARS` (500 characters per value, per field rather than one shared budget so a single huge field cannot erase the rest) and `HANDOFF_FIELD_MAX_COUNT` (20 fields per predecessor). And it is **de-duplicated**: a field is dropped only when the consumer's own bindings both read it and resolved to that exact value, so a binding that merely derives from a field (`${… > 5}`) or reads one leaf of it still gets the field itself rendered rather than silently dropped. Every truncation, cap overflow, and suppression states itself in the block; when nothing renders, no `Fields:` line appears at all.
92
+
93
+ The block is gated on `status === "complete"`, matching the binding layer — a skip spreads the prior execution, so a failed-then-skipped optional node can still carry captured output that expressions already refuse to read (`producer node X was skipped and produced no output`).
94
+
95
+ - [#531](https://github.com/skaile-ai/workspaces/pull/531) [`08b8674`](https://github.com/skaile-ai/workspaces/commit/08b8674f2d5a82e3cbcb26f48680498735dd7855) Thanks [@peteralbert](https://github.com/peteralbert)! - Discover flows that `skaile install` / `skaile add` actually installed.
96
+
97
+ `aiResourceRoots()` covered only the `factory-assets` layers and
98
+ `~/.skaile/libraries/`, never the install target the installer writes to
99
+ (`<project>/.skaile/flows/`, `~/.skaile/flows/`). An install reported success and
100
+ produced a flow that `skaile flow list` did not show and `skaile run` could not
101
+ start — it fell through to free-text prompt mode.
102
+
103
+ The install roots are now content roots, derived through the same `deployedBase`
104
+ the installer uses so discovery cannot drift from where the bytes land. Order:
105
+ project-local install (the deployed copy carries `patches:`, so it shadows the
106
+ factory original) → factory assets → user libraries → global install. The
107
+ relative order of the two pre-existing layers is unchanged. `skaile flow list`
108
+ and `skaile flow show` gained `--project-dir`, and `skaile run` now passes its
109
+ own `--project-dir` to discovery, so what a project lists is what it can run.
110
+
111
+ Every other asset kind (`skill`, `agent`, `prompt`, `contract`) installs into the
112
+ driver's own directory — `.claude/`, `.omp/`, `.codex/` — which the driver reads
113
+ natively and skaile deliberately does not enumerate. `flow` was the one kind
114
+ skaile owns and executes itself; that boundary is now asserted by a test rather
115
+ than left to a comment.
116
+
117
+ - [#535](https://github.com/skaile-ai/workspaces/pull/535) [`e2668e0`](https://github.com/skaile-ai/workspaces/commit/e2668e0d214b8d2d28cecc7b5329c2ae0bd8b056) Thanks [@peteralbert](https://github.com/peteralbert)! - Fix `skaile validate versions` silently skipping skill-version pins on v2 flows.
118
+
119
+ Both `validate versions` and `flow show` filtered nodes on the legacy `node.type === "skill"` /
120
+ `node.data` shape, which strict v2 flows do not have — v2 pins ride in `run.assets` as
121
+ `skill:<identity>#<range>`. `validate versions` therefore found zero pinned nodes in every v2 flow
122
+ and never ran the check, so a flow pinning an unsatisfiable range validated clean; `flow show`
123
+ printed `Nodes (0 skills)`.
124
+
125
+ Both call sites now read through shared, contract-typed node accessors, newly exported from
126
+ `@skaile/workspaces/factory-assets/connectors/flow/engine`: `nodeRunKind`, `nodeIsInert`,
127
+ `nodeSkillAsset`, `nodeSkillVersionPins`, and `flowSkillVersionPins` (alongside the existing
128
+ `node-shape` accessors, which that subpath now re-exports). `validate versions` loads each flow
129
+ through the normalizing `loadFlow`, and a flow it cannot read is reported as a warning instead of
130
+ being skipped in silence. `flow show` now lists every node with its run kind and any pinned skill,
131
+ not only skill nodes.
132
+
133
+ Two consequences for legacy v1 flows, both of which bring the CLI into line with what `skaile run`
134
+ already does:
135
+
136
+ - A legacy node that pinned `data.version` **without naming a `data.skill`** no longer registers a
137
+ pin. Normalization can only rebuild a `skill:` ref when the node names a skill, so the runner has
138
+ never enforced that pin; `validate versions` used to report it against the node id and now agrees
139
+ with execution instead.
140
+ - Legacy nodes the v2 runtime cannot execute — canvas `group` containers, authored `gate`s, unknown
141
+ kinds — normalize into permanently-blocked placeholders. `flow show` renders those as `inert`
142
+ rather than showing the `router` kind and `optional` flag normalization gave them, neither of
143
+ which the author wrote.
144
+
145
+ ### Patch Changes
146
+
147
+ - [#529](https://github.com/skaile-ai/workspaces/pull/529) [`ab74d98`](https://github.com/skaile-ai/workspaces/commit/ab74d9840869e735a59ee2145e8c5a9d32299f75) Thanks [@peteralbert](https://github.com/peteralbert)! - Parse a narrated subprompt reply instead of failing the node, refusing on any structural ambiguity.
148
+
149
+ `extractSubpromptJsonPayload` stripped a markdown fence only when the fence wrapped the entire
150
+ reply, so `Here's the classification:\n{"topic":"billing"}` went to `JSON.parse` as-is and failed.
151
+ A subprompt has no conversation to reprompt in, so that parse failure is an ordinary node failure:
152
+ it consumes `control.retries` and then fails the node, or skips it when optional. The cost of the
153
+ tail case was the whole node, not a degraded result.
154
+
155
+ When — and only when — the payload used today does not parse, the runtime now scans the reply for a
156
+ single balanced top-level JSON structure and uses that. Every reply that parses today yields a
157
+ byte-identical payload, so nothing that works changes. The scan is **fail-closed** and never repairs
158
+ JSON: a stray or mismatched bracket, a structure still open at end of input, more than one candidate
159
+ value, a candidate the sentence continues past (a value being talked about rather than answered
160
+ with), or a sole candidate that does not itself parse all keep today's loud failure. The error names
161
+ the refusal, so a reply that visibly contained JSON is not an unattributable failure.
162
+
163
+ The guarantee is structural, not semantic. A refusal whose shape is one JSON value with prose around
164
+ it — a model that answers "I need more information" and prints the schema on its own line — is still
165
+ accepted, and its keys land in `fields`. Telling that from a real answer needs the prose read, which
166
+ this deliberately does not do; a declared contract's validation is the layer that catches it.
167
+
168
+ `renderSubpromptPrompt` also restates the object-only directive _after_ the serialized schema, which
169
+ is otherwise the last thing the model reads.
170
+
3
171
  ## 3.2.1
4
172
 
5
173
  ### Patch Changes
@@ -1,17 +1,17 @@
1
- import { assignDomain, buildDomainIndex, DOMAIN_MD_FILENAME, parseDomainMd } from './chunk-NU4E7C5I.js';
1
+ import { assignDomain, buildDomainIndex, DOMAIN_MD_FILENAME, parseDomainMd } from './chunk-PW4SJE3X.js';
2
2
  import './chunk-JXT46QFC.js';
3
- import './chunk-P43HBWKK.js';
3
+ import './chunk-6BV24JPZ.js';
4
4
  import './chunk-F3HNW354.js';
5
5
  import './chunk-K5GBV4SA.js';
6
6
  import './chunk-QVZA2MDT.js';
7
- import { factoryAssetRoots } from './chunk-UFY2HFLO.js';
8
- import { scanRepo } from './chunk-Q26C5GLO.js';
7
+ import { factoryAssetRoots } from './chunk-KBXYT5HD.js';
8
+ import { scanRepo } from './chunk-RD6MMBHQ.js';
9
9
  import './chunk-MENCWIHW.js';
10
10
  import './chunk-P4VHQA4N.js';
11
- import { walkDir, parseFrontmatter } from './chunk-DYMWJAL4.js';
11
+ import { walkDir, parseFrontmatter } from './chunk-VQ4P7UYE.js';
12
12
  import './chunk-GEEW6SY2.js';
13
- import './chunk-U5POE4BA.js';
14
- import './chunk-7GOCNYUV.js';
13
+ import './chunk-5Q7HA4JP.js';
14
+ import './chunk-3A5IKBHQ.js';
15
15
  import './chunk-IRORTHGA.js';
16
16
  import './chunk-NSBPE2FW.js';
17
17
  import { existsSync, readFileSync } from 'fs';
@@ -65,7 +65,7 @@ async function libraryAssets() {
65
65
  const domains = [];
66
66
  try {
67
67
  const [{ openLibraryManager }, { skaileHomeDir }, { getGlobalCacheDir }] = await Promise.all([
68
- import('./open-library-WJ7P7B4N.js'),
68
+ import('./open-library-6OSZX2O6.js'),
69
69
  import('./library/index.js'),
70
70
  import('./core/index.js')
71
71
  ]);
@@ -98,7 +98,7 @@ async function libraryAssets() {
98
98
  async function storeAssets(projectDir) {
99
99
  const notes = [];
100
100
  try {
101
- const { resolveCatalogSource } = await import('./open-library-WJ7P7B4N.js');
101
+ const { resolveCatalogSource } = await import('./open-library-6OSZX2O6.js');
102
102
  const { source, close } = await resolveCatalogSource({ projectDir });
103
103
  try {
104
104
  const assets = await source.listAssets();
@@ -202,5 +202,5 @@ function dedupeBySlug(domains) {
202
202
  }
203
203
 
204
204
  export { gatherAssetFeeds };
205
- //# sourceMappingURL=asset-feeds-SEYSNNEM.js.map
206
- //# sourceMappingURL=asset-feeds-SEYSNNEM.js.map
205
+ //# sourceMappingURL=asset-feeds-CBY723RG.js.map
206
+ //# sourceMappingURL=asset-feeds-CBY723RG.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../cli/src/asset-feeds.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AAqFA,SAAS,QAAQ,CAAA,EAAmB;AAClC,EAAA,OAAO,CAAA,CAAE,KAAA,CAAW,IAAA,CAAA,GAAG,CAAA,CAAE,KAAK,GAAG,CAAA;AACnC;AAGA,eAAe,cAAc,IAAA,EAAmC;AAC9D,EAAA,MAAM,MAAkB,EAAC;AACzB,EAAA,WAAA,MAAiB,EAAE,IAAA,EAAM,QAAA,EAAU,MAAK,IAAK,OAAA,CAAQ,IAAI,CAAA,EAAG;AAC1D,IAAA,IAAI,SAAS,kBAAA,EAAoB;AACjC,IAAA,IAAI;AACF,MAAA,MAAM,EAAE,IAAA,EAAK,GAAI,iBAAiB,YAAA,CAAa,QAAA,EAAU,MAAM,CAAC,CAAA;AAChE,MAAA,GAAA,CAAI,IAAA,CAAK,cAAc,OAAA,CAAa,IAAA,CAAA,QAAA,CAAS,MAAM,QAAQ,CAAC,CAAA,EAAG,IAAI,CAAC,CAAA;AAAA,IACtE,CAAA,CAAA,MAAQ;AAAA,IAAC;AAAA,EACX;AACA,EAAA,OAAO,GAAA;AACT;AAcA,eAAe,YAAA,CACb,IAAA,EACA,YAAA,EACA,OAAA,EACA,qBAAqB,IAAA,EACK;AAC1B,EAAA,MAAM,SAAA,GAAY,MAAM,aAAA,CAAc,IAAI,CAAA;AAC1C,EAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,GAAA,CAAI,CAAC,KAAA,MAAW;AAAA,IAC1C,KAAA;AAAA,IACA,KAAK,CAAA,EAAG,KAAA,CAAM,IAAI,CAAA,CAAA,EAAI,MAAM,IAAI,CAAA,CAAA;AAAA,IAChC,cAAc,OAAA,CAAa,IAAA,CAAA,QAAA,CAAS,IAAA,EAAM,KAAA,CAAM,MAAM,CAAC;AAAA,GACzD,CAAE,CAAA;AAEF,EAAA,IAAI,YAAA,GAAe,KAAA;AACnB,EAAA,KAAA,MAAW,KAAK,WAAA,EAAa;AAC3B,IAAA,MAAM,IAAA,GAAO,YAAA,CAAa,CAAA,CAAE,YAAA,EAAc,SAAS,CAAA;AACnD,IAAA,IAAI,IAAA,EAAM,CAAA,CAAE,KAAA,CAAM,UAAA,GAAa,IAAA;AAAA,SAAA,IACtB,kBAAA,EAAoB;AAC3B,MAAA,CAAA,CAAE,MAAM,UAAA,GAAa,YAAA;AACrB,MAAA,YAAA,GAAe,IAAA;AAAA,IACjB,CAAA,MAAO,CAAA,CAAE,KAAA,CAAM,UAAA,GAAa,EAAA;AAAA,EAC9B;AAEA,EAAA,MAAM,EAAE,KAAA,EAAM,GAAI,gBAAA,CAAiB,aAAa,SAAS,CAAA;AACzD,EAAA,MAAM,UAA2B,MAAA,CAAO,MAAA,CAAO,KAAK,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,IAChE,MAAM,CAAA,CAAE,IAAA;AAAA,IACR,OAAO,CAAA,CAAE,KAAA;AAAA,IACT,GAAI,EAAE,WAAA,GAAc,EAAE,aAAa,CAAA,CAAE,WAAA,KAAgB,EAAC;AAAA,IACtD,GAAI,EAAE,QAAA,GAAW,EAAE,UAAU,CAAA,CAAE,QAAA,KAAa,EAAC;AAAA,IAC7C,GAAI,EAAE,KAAA,KAAU,MAAA,GAAY,EAAE,KAAA,EAAO,CAAA,CAAE,KAAA,EAAM,GAAI,EAAC;AAAA,IAClD,GAAI,CAAA,CAAE,WAAA,CAAY,CAAC,CAAA,GAAI,EAAE,cAAA,EAAgB,CAAA,CAAE,WAAA,CAAY,CAAC,CAAA,EAAE,GAAI;AAAC,GACjE,CAAE,CAAA;AACF,EAAA,IAAI,YAAA,UAAsB,IAAA,CAAK,EAAE,MAAM,YAAA,EAAc,KAAA,EAAO,cAAc,CAAA;AAC1E,EAAA,OAAO,OAAA;AACT;AAUA,eAAe,aAAA,GAIZ;AACD,EAAA,MAAM,QAAoB,EAAC;AAC3B,EAAA,MAAM,UAA2B,EAAC;AAClC,EAAA,IAAI;AACF,IAAA,MAAM,CAAC,EAAE,kBAAA,EAAmB,EAAG,EAAE,aAAA,EAAc,EAAG,EAAE,iBAAA,EAAmB,CAAA,GAAI,MAAM,QAAQ,GAAA,CAAI;AAAA,MAC3F,OAAO,4BAAmB,CAAA;AAAA,MAC1B,OAAO,oBAA4B,CAAA;AAAA,MACnC,OAAO,iBAAyB;AAAA,KACjC,CAAA;AACD,IAAA,MAAM,EAAE,OAAA,EAAS,KAAA,EAAM,GAAI,MAAM,kBAAA,EAAmB;AACpD,IAAA,IAAI;AAIF,MAAA,MAAM,UAAU,iBAAA,EAAkB;AAClC,MAAA,MAAM,UAAA,GAAkB,IAAA,CAAA,IAAA,CAAK,aAAA,EAAc,EAAG,SAAS,CAAA;AACvD,MAAA,MAAM,IAAA,GAAA,CAAQ,MAAM,OAAA,CAAQ,aAAA,EAAc,EAAG,MAAA;AAAA,QAC3C,CAAC,CAAA,KAAM,CAAC,CAAA,CAAE,IAAA,CAAK,UAAA,CAAW,OAAO,CAAA,IAAK,CAAC,CAAA,CAAE,IAAA,CAAK,UAAA,CAAW,UAAU;AAAA,OACrE;AACA,MAAA,MAAM,UAAyB,EAAC;AAChC,MAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,QAAA,IAAI,CAAC,UAAA,CAAW,GAAA,CAAI,IAAI,CAAA,EAAG;AAI3B,QAAA,MAAM,UAAA,GAAa,CAAC,GAAI,MAAM,SAAS,GAAA,CAAI,IAAA,EAAM,GAAA,CAAI,IAAI,CAAE,CAAA;AAC3D,QAAA,OAAA,CAAQ,IAAA,CAAK,GAAI,MAAM,YAAA,CAAa,GAAA,CAAI,MAAM,GAAA,CAAI,IAAA,EAAM,UAAA,EAAY,KAAK,CAAE,CAAA;AAC3E,QAAA,KAAA,MAAW,KAAA,IAAS,UAAA,EAAY,OAAA,CAAQ,IAAA,CAAK,EAAE,MAAA,EAAQ,SAAA,EAAW,KAAA,EAAO,KAAA,EAAO,GAAA,CAAI,IAAA,EAAM,CAAA;AAAA,MAC5F;AACA,MAAA,OAAO,EAAE,OAAA,EAAS,OAAA,EAAS,KAAA,EAAM;AAAA,IACnC,CAAA,SAAE;AACA,MAAA,KAAA,EAAM;AAAA,IACR;AAAA,EACF,SAAS,GAAA,EAAK;AACZ,IAAA,KAAA,CAAM,IAAA,CAAK;AAAA,MACT,IAAA,EAAM,WAAA;AAAA,MACN,SAAS,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,OAAO,GAAG;AAAA,KACzD,CAAA;AACD,IAAA,OAAO,EAAE,OAAA,EAAS,EAAC,EAAG,SAAS,KAAA,EAAM;AAAA,EACvC;AACF;AAYA,eAAe,YAAY,UAAA,EAIxB;AACD,EAAA,MAAM,QAAoB,EAAC;AAC3B,EAAA,IAAI;AACF,IAAA,MAAM,EAAE,oBAAA,EAAqB,GAAI,MAAM,OAAO,4BAAmB,CAAA;AACjE,IAAA,MAAM,EAAE,QAAQ,KAAA,EAAM,GAAI,MAAM,oBAAA,CAAqB,EAAE,YAAY,CAAA;AACnE,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,UAAA,EAAW;AACvC,MAAA,MAAM,OAAA,GAA0B,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,QACjD,MAAM,CAAA,CAAE,IAAA;AAAA,QACR,IAAA,EAAO,EAAE,IAAA,IAAsB,OAAA;AAAA,QAC/B,WAAA,EAAa,EAAE,WAAA,IAAe,EAAA;AAAA,QAC9B,MAAA,EAAQ,CAAA,QAAA,EAAW,CAAA,CAAE,EAAE,CAAA,CAAA;AAAA,QACvB,WAAW,CAAA,CAAE,SAAA;AAAA,QACb,MAAM,CAAA,CAAE,IAAA;AAAA,QACR,YAAY,CAAA,CAAE,UAAA;AAAA,QACd,UAAU,CAAA,CAAE,QAAA;AAAA,QACZ,SAAS,CAAA,CAAE,OAAA;AAAA,QACX,UAAU,EAAC;AAAA,QACX,cAAc;AAAC,OACjB,CAAE,CAAA;AAIF,MAAA,MAAM,OAAA,GAAU,MAAM,WAAA,CAAY,MAAM,CAAA;AACxC,MAAA,OAAO,EAAE,OAAA,EAAS,OAAA,EAAS,KAAA,EAAM;AAAA,IACnC,CAAA,SAAE;AACA,MAAA,KAAA,EAAM;AAAA,IACR;AAAA,EACF,SAAS,GAAA,EAAK;AACZ,IAAA,MAAM,MAAM,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,OAAO,GAAG,CAAA;AAG3D,IAAA,IAAI,CAAC,QAAA,CAAS,IAAA,CAAK,GAAG,CAAA,EAAG;AACvB,MAAA,KAAA,CAAM,KAAK,EAAE,IAAA,EAAM,OAAA,EAAS,OAAA,EAAS,KAAK,CAAA;AAAA,IAC5C;AACA,IAAA,OAAO,EAAE,OAAA,EAAS,IAAI,OAAA,EAAS,IAAI,KAAA,EAAM;AAAA,EAC3C;AACF;AAGA,eAAe,YAAY,MAAA,EAA2C;AACpE,EAAA,MAAM,KAAM,MAAA,CAA4D,WAAA;AACxE,EAAA,IAAI,OAAO,EAAA,KAAO,UAAA,EAAY,OAAO,EAAC;AACtC,EAAA,MAAM,OAAA,GAAU,MAAM,EAAA,CAAG,IAAA,CAAK,MAAM,CAAA;AACpC,EAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,GAAI,UAAU,EAAC;AAC7C;AAcA,eAAe,aACb,EAAA,EAC+D;AAI/D,EAAA,MAAM,eAAA,GAAkB,iBAAA,EAAkB,CAAE,GAAA,CAAI,CAAC,CAAA,KAAO,CAAA,CAAE,QAAA,CAAc,IAAA,CAAA,GAAG,CAAA,GAAI,CAAA,GAAI,CAAA,GAAS,IAAA,CAAA,GAAI,CAAA;AAChG,EAAA,MAAM,gBAAgB,CAAC,CAAA,KACrB,eAAA,CAAgB,IAAA,CAAK,CAAC,EAAA,KAAO,CAAA,KAAM,EAAA,CAAG,KAAA,CAAM,GAAG,EAAE,CAAA,IAAK,CAAA,CAAE,UAAA,CAAW,EAAE,CAAC,CAAA;AAExE,EAAA,MAAM,GAAA,GAAA,CAAO,MAAM,EAAA,CAAG,MAAA,EAAO,EAAG,MAAA,CAAO,CAAC,CAAA,KAAM,CAAC,aAAA,CAAc,CAAA,CAAE,MAAM,CAAC,CAAA;AACtE,EAAA,MAAM,UAAyB,EAAC;AAChC,EAAA,MAAM,UAA2B,EAAC;AAMlC,EAAA,KAAA,MAAW,EAAE,QAAA,EAAU,IAAA,EAAK,IAAK,EAAA,CAAG,iBAAgB,EAAG;AACrD,IAAA,IAAI,aAAA,CAAc,IAAI,CAAA,EAAG;AACzB,IAAA,MAAM,SAAS,IAAA,CAAK,QAAA,CAAc,IAAA,CAAA,GAAG,CAAA,GAAI,OAAO,IAAA,GAAY,IAAA,CAAA,GAAA;AAC5D,IAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,MAAA,KAAW,IAAA,IAAQ,CAAA,CAAE,MAAA,CAAO,UAAA,CAAW,MAAM,CAAC,CAAA;AAChF,IAAA,IAAI,KAAA,CAAM,WAAW,CAAA,EAAG;AACxB,IAAA,MAAM,aAAa,QAAA,CAAS,KAAA,CAAM,GAAG,CAAA,CAAE,KAAI,IAAK,QAAA;AAGhD,IAAA,OAAA,CAAQ,IAAA,CAAK,GAAI,MAAM,YAAA,CAAa,MAAM,UAAA,EAAY,KAAA,EAAO,KAAK,CAAE,CAAA;AACpE,IAAA,KAAA,MAAW,KAAA,IAAS,KAAA,EAAO,OAAA,CAAQ,IAAA,CAAK,EAAE,QAAQ,QAAA,EAAU,KAAA,EAAO,KAAA,EAAO,UAAA,EAAY,CAAA;AAAA,EACxF;AACA,EAAA,OAAO,EAAE,SAAS,OAAA,EAAQ;AAC5B;AAUA,eAAe,aAAA,GAIZ;AACD,EAAA,MAAM,QAAoB,EAAC;AAC3B,EAAA,MAAM,UAA2B,EAAC;AAClC,EAAA,IAAI;AACF,IAAA,MAAM,UAA0B,EAAC;AACjC,IAAA,KAAA,MAAW,IAAA,IAAQ,mBAAkB,EAAG;AACtC,MAAA,MAAM,cAAc,CAAC,GAAI,MAAM,QAAA,CAAS,IAAA,EAAM,gBAAgB,CAAE,CAAA;AAChE,MAAA,OAAA,CAAQ,KAAK,GAAI,MAAM,aAAa,IAAA,EAAM,gBAAA,EAAkB,WAAW,CAAE,CAAA;AACzE,MAAA,OAAA,CAAQ,IAAA,CAAK,GAAG,WAAW,CAAA;AAAA,IAC7B;AACA,IAAA,OAAO,EAAE,OAAA,EAAS,OAAA,EAAS,KAAA,EAAM;AAAA,EACnC,SAAS,GAAA,EAAK;AACZ,IAAA,KAAA,CAAM,IAAA,CAAK;AAAA,MACT,IAAA,EAAM,SAAA;AAAA,MACN,SAAS,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,OAAO,GAAG;AAAA,KACzD,CAAA;AACD,IAAA,OAAO,EAAE,OAAA,EAAS,EAAC,EAAG,SAAS,KAAA,EAAM;AAAA,EACvC;AACF;AAGA,SAAS,GAAA,CAAI,QAAqB,OAAA,EAAwC;AACxE,EAAA,OAAO,QAAQ,GAAA,CAAI,CAAC,WAAW,EAAE,MAAA,EAAQ,OAAM,CAAE,CAAA;AACnD;AASA,eAAsB,gBAAA,CACpB,IACA,UAAA,EAC0B;AAC1B,EAAA,MAAM,CAAC,IAAA,EAAM,KAAK,CAAA,GAAI,MAAM,OAAA,CAAQ,GAAA,CAAI,CAAC,aAAA,EAAc,EAAG,WAAA,CAAY,UAAU,CAAC,CAAC,CAAA;AAClF,EAAA,MAAM,OAAA,GAAU,MAAM,aAAA,EAAc;AACpC,EAAA,MAAM,GAAA,GAAM,MAAM,YAAA,CAAa,EAAE,CAAA;AACjC,EAAA,OAAO;AAAA,IACL,OAAA,EAAS;AAAA,MACP,GAAG,GAAA,CAAI,OAAA;AAAA,MACP,GAAG,IAAA,CAAK,OAAA;AAAA,MACR,GAAG,GAAA,CAAI,SAAA,EAAW,OAAA,CAAQ,OAAO,CAAA;AAAA,MACjC,GAAG,GAAA,CAAI,OAAA,EAAS,KAAA,CAAM,OAAO;AAAA,KAC/B;AAAA;AAAA;AAAA,IAGA,OAAA,EAAS,YAAA,CAAa,CAAC,GAAG,MAAM,OAAA,EAAS,GAAG,GAAA,CAAI,OAAA,EAAS,GAAG,IAAA,CAAK,OAAA,EAAS,GAAG,OAAA,CAAQ,OAAO,CAAC,CAAA;AAAA,IAC7F,KAAA,EAAO,CAAC,GAAG,IAAA,CAAK,KAAA,EAAO,GAAG,KAAA,CAAM,KAAA,EAAO,GAAG,OAAA,CAAQ,KAAK;AAAA,GACzD;AACF;AAGA,SAAS,aAAa,OAAA,EAA2C;AAC/D,EAAA,MAAM,IAAA,uBAAW,GAAA,EAAY;AAC7B,EAAA,MAAM,MAAuB,EAAC;AAC9B,EAAA,KAAA,MAAW,KAAK,OAAA,EAAS;AACvB,IAAA,IAAI,IAAA,CAAK,GAAA,CAAI,CAAA,CAAE,IAAI,CAAA,EAAG;AACtB,IAAA,IAAA,CAAK,GAAA,CAAI,EAAE,IAAI,CAAA;AACf,IAAA,GAAA,CAAI,KAAK,CAAC,CAAA;AAAA,EACZ;AACA,EAAA,OAAO,GAAA;AACT","file":"asset-feeds-SEYSNNEM.js","sourcesContent":["/**\n * asset-feeds.ts — gather installable assets from every feed the manage TUI\n * surfaces.\n *\n * Four feeds, normalized into `CatalogEntry`:\n * - **Project sources** — `skaile.yaml` `sources:` entries (scanned via\n * `AssetManager.search()`; the new `sources:` field is merged into the\n * legacy repositories map in `AssetManager.loadConfig`).\n * - **Local libraries** — user authoring places from `LocalIndex` (filesystem\n * scan of each library's `path`).\n * - **Factory assets** — the bundled first-party `factory-assets` tree\n * (`factoryAssetRoots()`), scanned from disk. Always present; no network.\n * - **Remote store** — the configured catalog source when `catalog.url` is\n * not `local`. Failures are non-fatal — they surface as notes.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport * as path from \"node:path\";\nimport {\n type AssetKind,\n type CatalogDomain,\n type CatalogEntry,\n factoryAssetRoots,\n parseFrontmatter,\n scanRepo,\n walkDir,\n} from \"@skaile/workspaces/core\";\nimport {\n assignDomain,\n buildDomainIndex,\n DOMAIN_MD_FILENAME,\n type DomainMd,\n parseDomainMd,\n} from \"@skaile/workspaces/discovery\";\n\n/** One non-fatal note returned to the caller for surfacing in the UI. */\nexport interface FeedNote {\n feed: \"sources\" | \"libraries\" | \"store\" | \"factory\";\n message: string;\n}\n\n/**\n * Which feed an asset came from. This is the top-level grouping axis in the\n * manage TUI (\"where is this skill coming from?\") and is intentionally distinct\n * from `publisher` (asset-intrinsic identity): the same publisher can legitimately\n * appear under more than one origin (e.g. a github repo you added as a `source`\n * that is also listed in the remote `store`).\n */\nexport type AssetOrigin = \"source\" | \"library\" | \"factory\" | \"store\";\n\n/** A catalog entry tagged with the feed it was gathered from. */\nexport interface OriginEntry {\n origin: AssetOrigin;\n entry: CatalogEntry;\n /**\n * Second-level grouping label for the manage tree. Defaults (when unset) to the\n * entry's `publisher`; the source feed sets it to the source's short repo name\n * so the Sources band groups by source — \"which repo did this come from\" — not\n * by the asset-intrinsic publisher.\n */\n group?: string;\n}\n\nexport interface AssetFeedResult {\n entries: OriginEntry[];\n /**\n * Display-only domain metadata, keyed (by the consumer) on `slug`. Populated\n * from the store's `catalog.listDomains` when the connected catalog source\n * exposes it; empty otherwise. The manage TUI joins `entry.domainSlug` → a\n * domain for grouping headers, descriptions, and relation badges.\n */\n domains: CatalogDomain[];\n notes: FeedNote[];\n}\n\n// ── Domain stamping (on-disk feeds) ──────────────────────────────────────────\n//\n// `scanRepo` (the source / library / factory scan path) does not read the\n// `DOMAIN.md` sidecars, so its entries arrive with no `domainSlug` and render\n// flat under their publisher band. We re-derive the grouping here from the\n// root's `DOMAIN.md` files (the same sidecars the remote store computes\n// server-side), and fall back to the repo/source name for any entry no domain\n// claims so it still groups instead of landing in the synthetic \"no domain\" pile.\n\n/** Repo-relative POSIX path (DOMAIN.md slugs are matched whole-segment, `/`-joined). */\nfunction toPosix(p: string): string {\n return p.split(path.sep).join(\"/\");\n}\n\n/** Parse every `DOMAIN.md` sidecar under `root` into a {@link DomainMd}. */\nasync function loadDomainMds(root: string): Promise<DomainMd[]> {\n const out: DomainMd[] = [];\n for await (const { path: filePath, name } of walkDir(root)) {\n if (name !== DOMAIN_MD_FILENAME) continue;\n try {\n const { data } = parseFrontmatter(readFileSync(filePath, \"utf8\"));\n out.push(parseDomainMd(toPosix(path.relative(root, filePath)), data));\n } catch {}\n }\n return out;\n}\n\n/**\n * Stamp `domainSlug` onto every entry scanned from `root` using the root's\n * `DOMAIN.md` sidecars. Returns the `CatalogDomain` registry for the real\n * domains, plus a synthetic fallback domain when any entry used the fallback.\n * Mutates `entries` in place (sets `entry.domainSlug`).\n *\n * `synthesizeFallback` (default true): entries no domain claims fall back to\n * `fallbackSlug` (the repo/source name). Pass `false` when the caller already\n * groups these entries one level up (e.g. the source feed groups by source\n * node) — unmatched entries then get no domain (`\"\"`) and hang directly under\n * that parent, instead of nesting under a same-named fallback domain.\n */\nasync function stampDomains(\n root: string,\n fallbackSlug: string,\n entries: CatalogEntry[],\n synthesizeFallback = true,\n): Promise<CatalogDomain[]> {\n const domainMds = await loadDomainMds(root);\n const assignables = entries.map((entry) => ({\n entry,\n ref: `${entry.kind}:${entry.name}`,\n relativePath: toPosix(path.relative(root, entry.source)),\n }));\n\n let usedFallback = false;\n for (const a of assignables) {\n const slug = assignDomain(a.relativePath, domainMds);\n if (slug) a.entry.domainSlug = slug;\n else if (synthesizeFallback) {\n a.entry.domainSlug = fallbackSlug;\n usedFallback = true;\n } else a.entry.domainSlug = \"\";\n }\n\n const { index } = buildDomainIndex(assignables, domainMds);\n const catalog: CatalogDomain[] = Object.values(index).map((d) => ({\n slug: d.slug,\n title: d.title,\n ...(d.description ? { description: d.description } : {}),\n ...(d.relation ? { relation: d.relation } : {}),\n ...(d.order !== undefined ? { order: d.order } : {}),\n ...(d.recommended[0] ? { recommendedRef: d.recommended[0] } : {}),\n }));\n if (usedFallback) catalog.push({ slug: fallbackSlug, title: fallbackSlug });\n return catalog;\n}\n\n/**\n * Pull assets from local libraries (the authoring places under\n * `~/.skaile/libraries/<name>/`, not the `~/.skaile/cache/sources/` cache). Each\n * library's `path` is scanned with `scanRepo` and grouped under the bare library\n * name (`group: <name>`) so the manage TUI shows a clean `<name>` band — no\n * `library:` prefix and no redundant same-named fallback domain (mirrors the\n * source feed).\n */\nasync function libraryAssets(): Promise<{\n entries: OriginEntry[];\n domains: CatalogDomain[];\n notes: FeedNote[];\n}> {\n const notes: FeedNote[] = [];\n const domains: CatalogDomain[] = [];\n try {\n const [{ openLibraryManager }, { skaileHomeDir }, { getGlobalCacheDir }] = await Promise.all([\n import(\"./open-library.ts\"),\n import(\"@skaile/workspaces/library\"),\n import(\"@skaile/workspaces/core\"),\n ]);\n const { manager, close } = await openLibraryManager();\n try {\n // Authoring libraries are the rows that are NOT source clones. index.db\n // rows predating the cache migration still carry the legacy\n // ~/.skaile/sources path, so exclude both roots.\n const newRoot = getGlobalCacheDir();\n const legacyRoot = path.join(skaileHomeDir(), \"sources\");\n const libs = (await manager.listLibraries()).filter(\n (l) => !l.path.startsWith(newRoot) && !l.path.startsWith(legacyRoot),\n );\n const entries: OriginEntry[] = [];\n for (const lib of libs) {\n if (!existsSync(lib.path)) continue;\n // Scan + group under the bare library name. `synthesizeFallback: false`\n // (as the source feed does) keeps domainless assets directly under the\n // library band instead of a redundant same-named fallback domain.\n const libEntries = [...(await scanRepo(lib.path, lib.name))];\n domains.push(...(await stampDomains(lib.path, lib.name, libEntries, false)));\n for (const entry of libEntries) entries.push({ origin: \"library\", entry, group: lib.name });\n }\n return { entries, domains, notes };\n } finally {\n close();\n }\n } catch (err) {\n notes.push({\n feed: \"libraries\",\n message: err instanceof Error ? err.message : String(err),\n });\n return { entries: [], domains, notes };\n }\n}\n\n/**\n * Pull a (best-effort) snapshot of the connected remote store catalog.\n *\n * - Returns no entries when the resolved catalog is local-only or the host\n * is unreachable (the failure becomes a note, not an exception).\n * - Maps `CatalogAsset` → `CatalogEntry`, carrying `publisher`, `repo`, and\n * `category` straight through and using `source = \"store://<id>\"`; per-file\n * `source` paths are not available for remote assets so the canonical ref is\n * a stable placeholder.\n */\nasync function storeAssets(projectDir: string): Promise<{\n entries: CatalogEntry[];\n domains: CatalogDomain[];\n notes: FeedNote[];\n}> {\n const notes: FeedNote[] = [];\n try {\n const { resolveCatalogSource } = await import(\"./open-library.ts\");\n const { source, close } = await resolveCatalogSource({ projectDir });\n try {\n const assets = await source.listAssets();\n const entries: CatalogEntry[] = assets.map((a) => ({\n name: a.name,\n kind: (a.kind as AssetKind) ?? \"skill\",\n description: a.description ?? \"\",\n source: `store://${a.id}`,\n publisher: a.publisher,\n repo: a.repo,\n domainSlug: a.domainSlug,\n category: a.category,\n version: a.version,\n requires: [],\n dependencies: [],\n }));\n // Domains are optional on the catalog source — only the store backend\n // (post Part 4 `catalog.listDomains`) exposes them. Duck-type the call so\n // older sources and the local catalog degrade to no domains, not an error.\n const domains = await loadDomains(source);\n return { entries, domains, notes };\n } finally {\n close();\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n // Local-only catalog throws a recognizable message — silence it; absence\n // is the user's choice, not an error.\n if (!/local/i.test(msg)) {\n notes.push({ feed: \"store\", message: msg });\n }\n return { entries: [], domains: [], notes };\n }\n}\n\n/** Optional `listDomains` capability — returns `[]` when the source lacks it. */\nasync function loadDomains(source: unknown): Promise<CatalogDomain[]> {\n const fn = (source as { listDomains?: () => Promise<CatalogDomain[]> }).listDomains;\n if (typeof fn !== \"function\") return [];\n const domains = await fn.call(source);\n return Array.isArray(domains) ? domains : [];\n}\n\n/** The slice of `AssetManager` the source feed needs (kept narrow for tests). */\ninterface AssetManagerLike {\n search: () => Promise<CatalogEntry[]>;\n searchRepoRoots: () => { repoName: string; root: string }[];\n}\n\n/**\n * Project-source feed: leans on `AssetManager.search()`, which now merges\n * `skaile.yaml`'s `sources:` field into the repositories map, then stamps\n * `DOMAIN.md`-derived domain grouping onto the scanned entries via\n * `searchRepoRoots()`.\n */\nasync function sourceAssets(\n am: AssetManagerLike,\n): Promise<{ entries: OriginEntry[]; domains: CatalogDomain[] }> {\n // The bundled factory tree is injected into the repositories map as an implicit\n // source, so `am.search()` returns it too. It has its own `factory` origin band,\n // so drop it here — otherwise the same first-party skills list twice under Sources.\n const factoryPrefixes = factoryAssetRoots().map((r) => (r.endsWith(path.sep) ? r : r + path.sep));\n const isFactoryPath = (p: string) =>\n factoryPrefixes.some((fp) => p === fp.slice(0, -1) || p.startsWith(fp));\n\n const all = (await am.search()).filter((e) => !isFactoryPath(e.source));\n const entries: OriginEntry[] = [];\n const domains: CatalogDomain[] = [];\n // Group each source's entries under the source node (its short repo name), not\n // the publisher: under Sources the navigational question is \"which repo did\n // this come from\". Each entry's `source` (absolute manifest path) lives under\n // exactly one declared root, so group + stamp by prefix. The cache slug is\n // URL-keyed `github.com/<org>/<repo>` — take the trailing segment.\n for (const { repoName, root } of am.searchRepoRoots()) {\n if (isFactoryPath(root)) continue;\n const prefix = root.endsWith(path.sep) ? root : root + path.sep;\n const owned = all.filter((e) => e.source === root || e.source.startsWith(prefix));\n if (owned.length === 0) continue;\n const sourceName = repoName.split(\"/\").pop() || repoName;\n // No synthetic fallback domain — the source node already groups these, so a\n // same-named fallback would just nest the source under itself.\n domains.push(...(await stampDomains(root, sourceName, owned, false)));\n for (const entry of owned) entries.push({ origin: \"source\", entry, group: sourceName });\n }\n return { entries, domains };\n}\n\n/**\n * Factory-asset feed: scan the bundled first-party `factory-assets` tree(s)\n * resolved by `factoryAssetRoots()` (the `SKAILE_FACTORY_ASSETS_DIR` override\n * layer ahead of the bundled tree). Local disk only, no network — the entries are\n * the built-in skills / connectors / bundles that ship with `@skaile/workspaces`.\n * `factoryAssetRoots()` already dedupes, so the env override pointing at the\n * bundled dir collapses to one root.\n */\nasync function factoryAssets(): Promise<{\n entries: CatalogEntry[];\n domains: CatalogDomain[];\n notes: FeedNote[];\n}> {\n const notes: FeedNote[] = [];\n const domains: CatalogDomain[] = [];\n try {\n const entries: CatalogEntry[] = [];\n for (const root of factoryAssetRoots()) {\n const rootEntries = [...(await scanRepo(root, \"skaile-factory\"))];\n domains.push(...(await stampDomains(root, \"skaile-factory\", rootEntries)));\n entries.push(...rootEntries);\n }\n return { entries, domains, notes };\n } catch (err) {\n notes.push({\n feed: \"factory\",\n message: err instanceof Error ? err.message : String(err),\n });\n return { entries: [], domains, notes };\n }\n}\n\n/** Tag a flat entry list with a single origin. */\nfunction tag(origin: AssetOrigin, entries: CatalogEntry[]): OriginEntry[] {\n return entries.map((entry) => ({ origin, entry }));\n}\n\n/**\n * Run all feeds and return a single normalized result.\n *\n * Feeds run in parallel where possible — the store fetch is the slow one\n * (one HTTP call) and is fired alongside the local library walk. The factory\n * scan is local disk only, so it runs inline.\n */\nexport async function gatherAssetFeeds(\n am: AssetManagerLike,\n projectDir: string,\n): Promise<AssetFeedResult> {\n const [libs, store] = await Promise.all([libraryAssets(), storeAssets(projectDir)]);\n const factory = await factoryAssets();\n const src = await sourceAssets(am);\n return {\n entries: [\n ...src.entries,\n ...libs.entries,\n ...tag(\"factory\", factory.entries),\n ...tag(\"store\", store.entries),\n ],\n // Store domains first (server-curated), then the on-disk feeds; dedupe by\n // slug, first-wins, so a store-served domain shadows a same-slug local one.\n domains: dedupeBySlug([...store.domains, ...src.domains, ...libs.domains, ...factory.domains]),\n notes: [...libs.notes, ...store.notes, ...factory.notes],\n };\n}\n\n/** First-wins dedupe of a domain list by `slug`. */\nfunction dedupeBySlug(domains: CatalogDomain[]): CatalogDomain[] {\n const seen = new Set<string>();\n const out: CatalogDomain[] = [];\n for (const d of domains) {\n if (seen.has(d.slug)) continue;\n seen.add(d.slug);\n out.push(d);\n }\n return out;\n}\n"]}
1
+ {"version":3,"sources":["../cli/src/asset-feeds.ts"],"names":[],"mappings":"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","file":"asset-feeds-CBY723RG.js","sourcesContent":["/**\n * asset-feeds.ts — gather installable assets from every feed the manage TUI\n * surfaces.\n *\n * Four feeds, normalized into `CatalogEntry`:\n * - **Project sources** — `skaile.yaml` `sources:` entries (scanned via\n * `AssetManager.search()`; the new `sources:` field is merged into the\n * legacy repositories map in `AssetManager.loadConfig`).\n * - **Local libraries** — user authoring places from `LocalIndex` (filesystem\n * scan of each library's `path`).\n * - **Factory assets** — the bundled first-party `factory-assets` tree\n * (`factoryAssetRoots()`), scanned from disk. Always present; no network.\n * - **Remote store** — the configured catalog source when `catalog.url` is\n * not `local`. Failures are non-fatal — they surface as notes.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport * as path from \"node:path\";\nimport {\n type AssetKind,\n type CatalogDomain,\n type CatalogEntry,\n factoryAssetRoots,\n parseFrontmatter,\n scanRepo,\n walkDir,\n} from \"@skaile/workspaces/core\";\nimport {\n assignDomain,\n buildDomainIndex,\n DOMAIN_MD_FILENAME,\n type DomainMd,\n parseDomainMd,\n} from \"@skaile/workspaces/discovery\";\n\n/** One non-fatal note returned to the caller for surfacing in the UI. */\nexport interface FeedNote {\n feed: \"sources\" | \"libraries\" | \"store\" | \"factory\";\n message: string;\n}\n\n/**\n * Which feed an asset came from. This is the top-level grouping axis in the\n * manage TUI (\"where is this skill coming from?\") and is intentionally distinct\n * from `publisher` (asset-intrinsic identity): the same publisher can legitimately\n * appear under more than one origin (e.g. a github repo you added as a `source`\n * that is also listed in the remote `store`).\n */\nexport type AssetOrigin = \"source\" | \"library\" | \"factory\" | \"store\";\n\n/** A catalog entry tagged with the feed it was gathered from. */\nexport interface OriginEntry {\n origin: AssetOrigin;\n entry: CatalogEntry;\n /**\n * Second-level grouping label for the manage tree. Defaults (when unset) to the\n * entry's `publisher`; the source feed sets it to the source's short repo name\n * so the Sources band groups by source — \"which repo did this come from\" — not\n * by the asset-intrinsic publisher.\n */\n group?: string;\n}\n\nexport interface AssetFeedResult {\n entries: OriginEntry[];\n /**\n * Display-only domain metadata, keyed (by the consumer) on `slug`. Populated\n * from the store's `catalog.listDomains` when the connected catalog source\n * exposes it; empty otherwise. The manage TUI joins `entry.domainSlug` → a\n * domain for grouping headers, descriptions, and relation badges.\n */\n domains: CatalogDomain[];\n notes: FeedNote[];\n}\n\n// ── Domain stamping (on-disk feeds) ──────────────────────────────────────────\n//\n// `scanRepo` (the source / library / factory scan path) does not read the\n// `DOMAIN.md` sidecars, so its entries arrive with no `domainSlug` and render\n// flat under their publisher band. We re-derive the grouping here from the\n// root's `DOMAIN.md` files (the same sidecars the remote store computes\n// server-side), and fall back to the repo/source name for any entry no domain\n// claims so it still groups instead of landing in the synthetic \"no domain\" pile.\n\n/** Repo-relative POSIX path (DOMAIN.md slugs are matched whole-segment, `/`-joined). */\nfunction toPosix(p: string): string {\n return p.split(path.sep).join(\"/\");\n}\n\n/** Parse every `DOMAIN.md` sidecar under `root` into a {@link DomainMd}. */\nasync function loadDomainMds(root: string): Promise<DomainMd[]> {\n const out: DomainMd[] = [];\n for await (const { path: filePath, name } of walkDir(root)) {\n if (name !== DOMAIN_MD_FILENAME) continue;\n try {\n const { data } = parseFrontmatter(readFileSync(filePath, \"utf8\"));\n out.push(parseDomainMd(toPosix(path.relative(root, filePath)), data));\n } catch {}\n }\n return out;\n}\n\n/**\n * Stamp `domainSlug` onto every entry scanned from `root` using the root's\n * `DOMAIN.md` sidecars. Returns the `CatalogDomain` registry for the real\n * domains, plus a synthetic fallback domain when any entry used the fallback.\n * Mutates `entries` in place (sets `entry.domainSlug`).\n *\n * `synthesizeFallback` (default true): entries no domain claims fall back to\n * `fallbackSlug` (the repo/source name). Pass `false` when the caller already\n * groups these entries one level up (e.g. the source feed groups by source\n * node) — unmatched entries then get no domain (`\"\"`) and hang directly under\n * that parent, instead of nesting under a same-named fallback domain.\n */\nasync function stampDomains(\n root: string,\n fallbackSlug: string,\n entries: CatalogEntry[],\n synthesizeFallback = true,\n): Promise<CatalogDomain[]> {\n const domainMds = await loadDomainMds(root);\n const assignables = entries.map((entry) => ({\n entry,\n ref: `${entry.kind}:${entry.name}`,\n relativePath: toPosix(path.relative(root, entry.source)),\n }));\n\n let usedFallback = false;\n for (const a of assignables) {\n const slug = assignDomain(a.relativePath, domainMds);\n if (slug) a.entry.domainSlug = slug;\n else if (synthesizeFallback) {\n a.entry.domainSlug = fallbackSlug;\n usedFallback = true;\n } else a.entry.domainSlug = \"\";\n }\n\n const { index } = buildDomainIndex(assignables, domainMds);\n const catalog: CatalogDomain[] = Object.values(index).map((d) => ({\n slug: d.slug,\n title: d.title,\n ...(d.description ? { description: d.description } : {}),\n ...(d.relation ? { relation: d.relation } : {}),\n ...(d.order !== undefined ? { order: d.order } : {}),\n ...(d.recommended[0] ? { recommendedRef: d.recommended[0] } : {}),\n }));\n if (usedFallback) catalog.push({ slug: fallbackSlug, title: fallbackSlug });\n return catalog;\n}\n\n/**\n * Pull assets from local libraries (the authoring places under\n * `~/.skaile/libraries/<name>/`, not the `~/.skaile/cache/sources/` cache). Each\n * library's `path` is scanned with `scanRepo` and grouped under the bare library\n * name (`group: <name>`) so the manage TUI shows a clean `<name>` band — no\n * `library:` prefix and no redundant same-named fallback domain (mirrors the\n * source feed).\n */\nasync function libraryAssets(): Promise<{\n entries: OriginEntry[];\n domains: CatalogDomain[];\n notes: FeedNote[];\n}> {\n const notes: FeedNote[] = [];\n const domains: CatalogDomain[] = [];\n try {\n const [{ openLibraryManager }, { skaileHomeDir }, { getGlobalCacheDir }] = await Promise.all([\n import(\"./open-library.ts\"),\n import(\"@skaile/workspaces/library\"),\n import(\"@skaile/workspaces/core\"),\n ]);\n const { manager, close } = await openLibraryManager();\n try {\n // Authoring libraries are the rows that are NOT source clones. index.db\n // rows predating the cache migration still carry the legacy\n // ~/.skaile/sources path, so exclude both roots.\n const newRoot = getGlobalCacheDir();\n const legacyRoot = path.join(skaileHomeDir(), \"sources\");\n const libs = (await manager.listLibraries()).filter(\n (l) => !l.path.startsWith(newRoot) && !l.path.startsWith(legacyRoot),\n );\n const entries: OriginEntry[] = [];\n for (const lib of libs) {\n if (!existsSync(lib.path)) continue;\n // Scan + group under the bare library name. `synthesizeFallback: false`\n // (as the source feed does) keeps domainless assets directly under the\n // library band instead of a redundant same-named fallback domain.\n const libEntries = [...(await scanRepo(lib.path, lib.name))];\n domains.push(...(await stampDomains(lib.path, lib.name, libEntries, false)));\n for (const entry of libEntries) entries.push({ origin: \"library\", entry, group: lib.name });\n }\n return { entries, domains, notes };\n } finally {\n close();\n }\n } catch (err) {\n notes.push({\n feed: \"libraries\",\n message: err instanceof Error ? err.message : String(err),\n });\n return { entries: [], domains, notes };\n }\n}\n\n/**\n * Pull a (best-effort) snapshot of the connected remote store catalog.\n *\n * - Returns no entries when the resolved catalog is local-only or the host\n * is unreachable (the failure becomes a note, not an exception).\n * - Maps `CatalogAsset` → `CatalogEntry`, carrying `publisher`, `repo`, and\n * `category` straight through and using `source = \"store://<id>\"`; per-file\n * `source` paths are not available for remote assets so the canonical ref is\n * a stable placeholder.\n */\nasync function storeAssets(projectDir: string): Promise<{\n entries: CatalogEntry[];\n domains: CatalogDomain[];\n notes: FeedNote[];\n}> {\n const notes: FeedNote[] = [];\n try {\n const { resolveCatalogSource } = await import(\"./open-library.ts\");\n const { source, close } = await resolveCatalogSource({ projectDir });\n try {\n const assets = await source.listAssets();\n const entries: CatalogEntry[] = assets.map((a) => ({\n name: a.name,\n kind: (a.kind as AssetKind) ?? \"skill\",\n description: a.description ?? \"\",\n source: `store://${a.id}`,\n publisher: a.publisher,\n repo: a.repo,\n domainSlug: a.domainSlug,\n category: a.category,\n version: a.version,\n requires: [],\n dependencies: [],\n }));\n // Domains are optional on the catalog source — only the store backend\n // (post Part 4 `catalog.listDomains`) exposes them. Duck-type the call so\n // older sources and the local catalog degrade to no domains, not an error.\n const domains = await loadDomains(source);\n return { entries, domains, notes };\n } finally {\n close();\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n // Local-only catalog throws a recognizable message — silence it; absence\n // is the user's choice, not an error.\n if (!/local/i.test(msg)) {\n notes.push({ feed: \"store\", message: msg });\n }\n return { entries: [], domains: [], notes };\n }\n}\n\n/** Optional `listDomains` capability — returns `[]` when the source lacks it. */\nasync function loadDomains(source: unknown): Promise<CatalogDomain[]> {\n const fn = (source as { listDomains?: () => Promise<CatalogDomain[]> }).listDomains;\n if (typeof fn !== \"function\") return [];\n const domains = await fn.call(source);\n return Array.isArray(domains) ? domains : [];\n}\n\n/** The slice of `AssetManager` the source feed needs (kept narrow for tests). */\ninterface AssetManagerLike {\n search: () => Promise<CatalogEntry[]>;\n searchRepoRoots: () => { repoName: string; root: string }[];\n}\n\n/**\n * Project-source feed: leans on `AssetManager.search()`, which now merges\n * `skaile.yaml`'s `sources:` field into the repositories map, then stamps\n * `DOMAIN.md`-derived domain grouping onto the scanned entries via\n * `searchRepoRoots()`.\n */\nasync function sourceAssets(\n am: AssetManagerLike,\n): Promise<{ entries: OriginEntry[]; domains: CatalogDomain[] }> {\n // The bundled factory tree is injected into the repositories map as an implicit\n // source, so `am.search()` returns it too. It has its own `factory` origin band,\n // so drop it here — otherwise the same first-party skills list twice under Sources.\n const factoryPrefixes = factoryAssetRoots().map((r) => (r.endsWith(path.sep) ? r : r + path.sep));\n const isFactoryPath = (p: string) =>\n factoryPrefixes.some((fp) => p === fp.slice(0, -1) || p.startsWith(fp));\n\n const all = (await am.search()).filter((e) => !isFactoryPath(e.source));\n const entries: OriginEntry[] = [];\n const domains: CatalogDomain[] = [];\n // Group each source's entries under the source node (its short repo name), not\n // the publisher: under Sources the navigational question is \"which repo did\n // this come from\". Each entry's `source` (absolute manifest path) lives under\n // exactly one declared root, so group + stamp by prefix. The cache slug is\n // URL-keyed `github.com/<org>/<repo>` — take the trailing segment.\n for (const { repoName, root } of am.searchRepoRoots()) {\n if (isFactoryPath(root)) continue;\n const prefix = root.endsWith(path.sep) ? root : root + path.sep;\n const owned = all.filter((e) => e.source === root || e.source.startsWith(prefix));\n if (owned.length === 0) continue;\n const sourceName = repoName.split(\"/\").pop() || repoName;\n // No synthetic fallback domain — the source node already groups these, so a\n // same-named fallback would just nest the source under itself.\n domains.push(...(await stampDomains(root, sourceName, owned, false)));\n for (const entry of owned) entries.push({ origin: \"source\", entry, group: sourceName });\n }\n return { entries, domains };\n}\n\n/**\n * Factory-asset feed: scan the bundled first-party `factory-assets` tree(s)\n * resolved by `factoryAssetRoots()` (the `SKAILE_FACTORY_ASSETS_DIR` override\n * layer ahead of the bundled tree). Local disk only, no network — the entries are\n * the built-in skills / connectors / bundles that ship with `@skaile/workspaces`.\n * `factoryAssetRoots()` already dedupes, so the env override pointing at the\n * bundled dir collapses to one root.\n */\nasync function factoryAssets(): Promise<{\n entries: CatalogEntry[];\n domains: CatalogDomain[];\n notes: FeedNote[];\n}> {\n const notes: FeedNote[] = [];\n const domains: CatalogDomain[] = [];\n try {\n const entries: CatalogEntry[] = [];\n for (const root of factoryAssetRoots()) {\n const rootEntries = [...(await scanRepo(root, \"skaile-factory\"))];\n domains.push(...(await stampDomains(root, \"skaile-factory\", rootEntries)));\n entries.push(...rootEntries);\n }\n return { entries, domains, notes };\n } catch (err) {\n notes.push({\n feed: \"factory\",\n message: err instanceof Error ? err.message : String(err),\n });\n return { entries: [], domains, notes };\n }\n}\n\n/** Tag a flat entry list with a single origin. */\nfunction tag(origin: AssetOrigin, entries: CatalogEntry[]): OriginEntry[] {\n return entries.map((entry) => ({ origin, entry }));\n}\n\n/**\n * Run all feeds and return a single normalized result.\n *\n * Feeds run in parallel where possible — the store fetch is the slow one\n * (one HTTP call) and is fired alongside the local library walk. The factory\n * scan is local disk only, so it runs inline.\n */\nexport async function gatherAssetFeeds(\n am: AssetManagerLike,\n projectDir: string,\n): Promise<AssetFeedResult> {\n const [libs, store] = await Promise.all([libraryAssets(), storeAssets(projectDir)]);\n const factory = await factoryAssets();\n const src = await sourceAssets(am);\n return {\n entries: [\n ...src.entries,\n ...libs.entries,\n ...tag(\"factory\", factory.entries),\n ...tag(\"store\", store.entries),\n ],\n // Store domains first (server-curated), then the on-disk feeds; dedupe by\n // slug, first-wins, so a store-served domain shadows a same-slug local one.\n domains: dedupeBySlug([...store.domains, ...src.domains, ...libs.domains, ...factory.domains]),\n notes: [...libs.notes, ...store.notes, ...factory.notes],\n };\n}\n\n/** First-wins dedupe of a domain list by `slug`. */\nfunction dedupeBySlug(domains: CatalogDomain[]): CatalogDomain[] {\n const seen = new Set<string>();\n const out: CatalogDomain[] = [];\n for (const d of domains) {\n if (seen.has(d.slug)) continue;\n seen.add(d.slug);\n out.push(d);\n }\n return out;\n}\n"]}
@@ -1,16 +1,16 @@
1
- export { WORKSPACE_MOUNT_ENV_VAR, deployAll, deployCatalogEntry, findAiAssetsRoot, isUnderWorkspaceMount, resolveFlowSkills, undeployCatalogEntry, workspaceMountDirs } from '../chunk-BEAIJRVF.js';
2
- import '../chunk-P43HBWKK.js';
1
+ export { WORKSPACE_MOUNT_ENV_VAR, deployAll, deployCatalogEntry, findAiAssetsRoot, isUnderWorkspaceMount, resolveFlowSkills, undeployCatalogEntry, workspaceMountDirs } from '../chunk-GHWJHOYX.js';
2
+ import '../chunk-6BV24JPZ.js';
3
3
  import '../chunk-F3HNW354.js';
4
4
  import '../chunk-K5GBV4SA.js';
5
5
  import '../chunk-QVZA2MDT.js';
6
- import '../chunk-UFY2HFLO.js';
7
- import '../chunk-Q26C5GLO.js';
6
+ import '../chunk-KBXYT5HD.js';
7
+ import '../chunk-RD6MMBHQ.js';
8
8
  import '../chunk-MENCWIHW.js';
9
9
  import '../chunk-P4VHQA4N.js';
10
- import '../chunk-DYMWJAL4.js';
10
+ import '../chunk-VQ4P7UYE.js';
11
11
  import '../chunk-GEEW6SY2.js';
12
- import '../chunk-U5POE4BA.js';
13
- import '../chunk-7GOCNYUV.js';
12
+ import '../chunk-5Q7HA4JP.js';
13
+ import '../chunk-3A5IKBHQ.js';
14
14
  import '../chunk-NSBPE2FW.js';
15
15
  //# sourceMappingURL=catalog-deployer.js.map
16
16
  //# sourceMappingURL=catalog-deployer.js.map
@@ -1,23 +1,23 @@
1
- export { AssetManager, FACTORY_SOURCE_URL, MissingManifestError, NON_DEPLOYABLE_KINDS, normalizeRepoUrl, parseCanonicalLockKey, parseRefTolerant, renderMcpServerConfig, softDepName } from '../chunk-HCNVX34E.js';
2
- import '../chunk-3HVASHBF.js';
3
- import '../chunk-NU4E7C5I.js';
1
+ export { AssetManager, FACTORY_SOURCE_URL, MissingManifestError, NON_DEPLOYABLE_KINDS, normalizeRepoUrl, parseCanonicalLockKey, parseRefTolerant, renderMcpServerConfig, softDepName } from '../chunk-OI36ITDV.js';
2
+ import '../chunk-BJ7QIUBM.js';
3
+ import '../chunk-PW4SJE3X.js';
4
4
  import '../chunk-UKJMQZV6.js';
5
5
  import '../chunk-R7FOF242.js';
6
6
  import '../chunk-JXT46QFC.js';
7
- export { createScaffold, deployAll, installAgent, removeAsset } from '../chunk-2OCYS5EP.js';
7
+ export { createScaffold, deployAll, installAgent, removeAsset } from '../chunk-PEGIQ67F.js';
8
8
  export { BUILT_IN_FRAGMENTS, loadPromptExtensions, resolveFragments } from '../chunk-CGYEHQOX.js';
9
- import '../chunk-P43HBWKK.js';
9
+ import '../chunk-6BV24JPZ.js';
10
10
  import '../chunk-F3HNW354.js';
11
11
  import '../chunk-K5GBV4SA.js';
12
12
  import '../chunk-QVZA2MDT.js';
13
- import '../chunk-UFY2HFLO.js';
14
- import '../chunk-Q26C5GLO.js';
13
+ import '../chunk-KBXYT5HD.js';
14
+ import '../chunk-RD6MMBHQ.js';
15
15
  import '../chunk-MENCWIHW.js';
16
16
  import '../chunk-P4VHQA4N.js';
17
- import '../chunk-DYMWJAL4.js';
17
+ import '../chunk-VQ4P7UYE.js';
18
18
  import '../chunk-GEEW6SY2.js';
19
- import '../chunk-U5POE4BA.js';
20
- import '../chunk-7GOCNYUV.js';
19
+ import '../chunk-5Q7HA4JP.js';
20
+ import '../chunk-3A5IKBHQ.js';
21
21
  export { appendHistory, clearHistory, getRecentHistory, loadHistory } from '../chunk-KTBKW2FI.js';
22
22
  import '../chunk-UQ6LFBPZ.js';
23
23
  export { AGENT_RENDERERS, claudeCodeRenderer, codexRenderer, driverTargetSupportsAgents, ompRenderer, renderAgentToFramework } from '../chunk-IEX5YV4R.js';
@@ -1,17 +1,17 @@
1
- export { createScaffold, deployAll, deployAsset, removeAsset } from '../chunk-2OCYS5EP.js';
1
+ export { createScaffold, deployAll, deployAsset, removeAsset } from '../chunk-PEGIQ67F.js';
2
2
  import '../chunk-CGYEHQOX.js';
3
- import '../chunk-P43HBWKK.js';
3
+ import '../chunk-6BV24JPZ.js';
4
4
  import '../chunk-F3HNW354.js';
5
5
  import '../chunk-K5GBV4SA.js';
6
6
  import '../chunk-QVZA2MDT.js';
7
- import '../chunk-UFY2HFLO.js';
8
- import '../chunk-Q26C5GLO.js';
7
+ import '../chunk-KBXYT5HD.js';
8
+ import '../chunk-RD6MMBHQ.js';
9
9
  import '../chunk-MENCWIHW.js';
10
10
  import '../chunk-P4VHQA4N.js';
11
- import '../chunk-DYMWJAL4.js';
11
+ import '../chunk-VQ4P7UYE.js';
12
12
  import '../chunk-GEEW6SY2.js';
13
- import '../chunk-U5POE4BA.js';
14
- import '../chunk-7GOCNYUV.js';
13
+ import '../chunk-5Q7HA4JP.js';
14
+ import '../chunk-3A5IKBHQ.js';
15
15
  import '../chunk-IEX5YV4R.js';
16
16
  import '../chunk-IRORTHGA.js';
17
17
  import '../chunk-NSBPE2FW.js';
@@ -1,21 +1,21 @@
1
- import { scrubPoisonedTranscript, resolveClaudeConfigDir, SUPPORTED_CLOUDS, buildClaudeCloudEnv } from '../../chunk-46LE3GNY.js';
1
+ import { scrubPoisonedTranscript, resolveClaudeConfigDir, SUPPORTED_CLOUDS, buildClaudeCloudEnv } from '../../chunk-RUFTB7QN.js';
2
2
  import { dispatchCapability } from '../../chunk-RRVQAE5D.js';
3
3
  import { fetchProviderModels } from '../../chunk-V34ZEXSE.js';
4
4
  import { AuthError, classifyClaudeSdkError, reclassifyAuthAsAccountUnavailable, AccountUnavailableError } from '../../chunk-GAH4YRCI.js';
5
- import { BUILTIN_DRIVER_CATALOG, AgentDriver, getBridgeLogger } from '../../chunk-VT6HDD2L.js';
5
+ import { BUILTIN_DRIVER_CATALOG, AgentDriver, getBridgeLogger } from '../../chunk-SUGDZOYZ.js';
6
6
  import '../../chunk-6E6PKKAD.js';
7
- import '../../chunk-P43HBWKK.js';
7
+ import '../../chunk-6BV24JPZ.js';
8
8
  import '../../chunk-F3HNW354.js';
9
9
  import '../../chunk-K5GBV4SA.js';
10
10
  import '../../chunk-QVZA2MDT.js';
11
- import '../../chunk-UFY2HFLO.js';
12
- import '../../chunk-Q26C5GLO.js';
11
+ import '../../chunk-KBXYT5HD.js';
12
+ import '../../chunk-RD6MMBHQ.js';
13
13
  import '../../chunk-MENCWIHW.js';
14
14
  import '../../chunk-P4VHQA4N.js';
15
- import '../../chunk-DYMWJAL4.js';
15
+ import '../../chunk-VQ4P7UYE.js';
16
16
  import '../../chunk-GEEW6SY2.js';
17
- import '../../chunk-U5POE4BA.js';
18
- import '../../chunk-7GOCNYUV.js';
17
+ import '../../chunk-5Q7HA4JP.js';
18
+ import '../../chunk-3A5IKBHQ.js';
19
19
  import '../../chunk-NSBPE2FW.js';
20
20
  import { spawnSync, spawn } from 'child_process';
21
21
  import { readFileSync, existsSync } from 'fs';
@@ -1,9 +1,9 @@
1
1
  import { fetchProviderModels } from '../../chunk-V34ZEXSE.js';
2
2
  import { classifyGenericErrorMessage } from '../../chunk-GAH4YRCI.js';
3
- import { BUILTIN_DRIVER_CATALOG, AgentDriver, getBridgeLogger } from '../../chunk-VT6HDD2L.js';
3
+ import { BUILTIN_DRIVER_CATALOG, AgentDriver, getBridgeLogger } from '../../chunk-SUGDZOYZ.js';
4
4
  import '../../chunk-6E6PKKAD.js';
5
- import '../../chunk-U5POE4BA.js';
6
- import '../../chunk-7GOCNYUV.js';
5
+ import '../../chunk-5Q7HA4JP.js';
6
+ import '../../chunk-3A5IKBHQ.js';
7
7
  import '../../chunk-NSBPE2FW.js';
8
8
  import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'fs';
9
9
  import { join, dirname } from 'path';
@@ -1,9 +1,9 @@
1
- import { SubprocessCliDriver, mapApiKeysToEnv, extractText, extractCost, extractTokens } from '../../chunk-I7MKEZ6J.js';
1
+ import { SubprocessCliDriver, mapApiKeysToEnv, extractText, extractCost, extractTokens } from '../../chunk-PHJ2DU6D.js';
2
2
  import '../../chunk-GAH4YRCI.js';
3
- import { BUILTIN_DRIVER_CATALOG, getBridgeLogger } from '../../chunk-VT6HDD2L.js';
3
+ import { BUILTIN_DRIVER_CATALOG, getBridgeLogger } from '../../chunk-SUGDZOYZ.js';
4
4
  import '../../chunk-6E6PKKAD.js';
5
- import '../../chunk-U5POE4BA.js';
6
- import '../../chunk-7GOCNYUV.js';
5
+ import '../../chunk-5Q7HA4JP.js';
6
+ import '../../chunk-3A5IKBHQ.js';
7
7
  import '../../chunk-NSBPE2FW.js';
8
8
  import * as z from 'zod';
9
9
 
@@ -1,8 +1,8 @@
1
1
  import { dispatchCapability } from '../../chunk-RRVQAE5D.js';
2
- import { BUILTIN_DRIVER_CATALOG, AgentDriver, getBridgeLogger } from '../../chunk-VT6HDD2L.js';
2
+ import { BUILTIN_DRIVER_CATALOG, AgentDriver, getBridgeLogger } from '../../chunk-SUGDZOYZ.js';
3
3
  import '../../chunk-6E6PKKAD.js';
4
- import '../../chunk-U5POE4BA.js';
5
- import '../../chunk-7GOCNYUV.js';
4
+ import '../../chunk-5Q7HA4JP.js';
5
+ import '../../chunk-3A5IKBHQ.js';
6
6
  import '../../chunk-NSBPE2FW.js';
7
7
  import * as z from 'zod';
8
8
 
@@ -1,11 +1,11 @@
1
- export { GeminiDriver, GeminiFamilyDriver, geminiDriverTarget } from '../../chunk-O6B3ZZ5S.js';
2
- import '../../chunk-I7MKEZ6J.js';
1
+ export { GeminiDriver, GeminiFamilyDriver, geminiDriverTarget } from '../../chunk-3A3ETJFD.js';
2
+ import '../../chunk-PHJ2DU6D.js';
3
3
  import '../../chunk-V34ZEXSE.js';
4
4
  import '../../chunk-GAH4YRCI.js';
5
- import '../../chunk-VT6HDD2L.js';
5
+ import '../../chunk-SUGDZOYZ.js';
6
6
  import '../../chunk-6E6PKKAD.js';
7
- import '../../chunk-U5POE4BA.js';
8
- import '../../chunk-7GOCNYUV.js';
7
+ import '../../chunk-5Q7HA4JP.js';
8
+ import '../../chunk-3A5IKBHQ.js';
9
9
  import '../../chunk-NSBPE2FW.js';
10
10
  //# sourceMappingURL=gemini.js.map
11
11
  //# sourceMappingURL=gemini.js.map
@@ -1,9 +1,9 @@
1
- import { SubprocessCliDriver, mapApiKeysToEnv, extractText, extractCost, extractTokens } from '../../chunk-I7MKEZ6J.js';
1
+ import { SubprocessCliDriver, mapApiKeysToEnv, extractText, extractCost, extractTokens } from '../../chunk-PHJ2DU6D.js';
2
2
  import '../../chunk-GAH4YRCI.js';
3
- import { BUILTIN_DRIVER_CATALOG, getBridgeLogger } from '../../chunk-VT6HDD2L.js';
3
+ import { BUILTIN_DRIVER_CATALOG, getBridgeLogger } from '../../chunk-SUGDZOYZ.js';
4
4
  import '../../chunk-6E6PKKAD.js';
5
- import '../../chunk-U5POE4BA.js';
6
- import '../../chunk-7GOCNYUV.js';
5
+ import '../../chunk-5Q7HA4JP.js';
6
+ import '../../chunk-3A5IKBHQ.js';
7
7
  import '../../chunk-NSBPE2FW.js';
8
8
  import * as z from 'zod';
9
9
 
@@ -1,8 +1,8 @@
1
1
  import { classifyGenericErrorMessage } from '../../chunk-GAH4YRCI.js';
2
- import { BUILTIN_DRIVER_CATALOG, AgentDriver, getBridgeLogger } from '../../chunk-VT6HDD2L.js';
2
+ import { BUILTIN_DRIVER_CATALOG, AgentDriver, getBridgeLogger } from '../../chunk-SUGDZOYZ.js';
3
3
  import '../../chunk-6E6PKKAD.js';
4
- import '../../chunk-U5POE4BA.js';
5
- import '../../chunk-7GOCNYUV.js';
4
+ import '../../chunk-5Q7HA4JP.js';
5
+ import '../../chunk-3A5IKBHQ.js';
6
6
  import '../../chunk-NSBPE2FW.js';
7
7
  import { spawn, execFile } from 'child_process';
8
8
  import fs from 'fs/promises';
@@ -1,9 +1,9 @@
1
- import { SubprocessCliDriver, mapApiKeysToEnv, extractText, extractCost, extractTokens } from '../../chunk-I7MKEZ6J.js';
1
+ import { SubprocessCliDriver, mapApiKeysToEnv, extractText, extractCost, extractTokens } from '../../chunk-PHJ2DU6D.js';
2
2
  import '../../chunk-GAH4YRCI.js';
3
- import { BUILTIN_DRIVER_CATALOG, getBridgeLogger } from '../../chunk-VT6HDD2L.js';
3
+ import { BUILTIN_DRIVER_CATALOG, getBridgeLogger } from '../../chunk-SUGDZOYZ.js';
4
4
  import '../../chunk-6E6PKKAD.js';
5
- import '../../chunk-U5POE4BA.js';
6
- import '../../chunk-7GOCNYUV.js';
5
+ import '../../chunk-5Q7HA4JP.js';
6
+ import '../../chunk-3A5IKBHQ.js';
7
7
  import '../../chunk-NSBPE2FW.js';
8
8
  import * as z from 'zod';
9
9
 
@@ -1,11 +1,11 @@
1
- import { GeminiFamilyDriver } from '../../chunk-O6B3ZZ5S.js';
2
- import '../../chunk-I7MKEZ6J.js';
1
+ import { GeminiFamilyDriver } from '../../chunk-3A3ETJFD.js';
2
+ import '../../chunk-PHJ2DU6D.js';
3
3
  import { fetchProviderModels } from '../../chunk-V34ZEXSE.js';
4
4
  import '../../chunk-GAH4YRCI.js';
5
- import { BUILTIN_DRIVER_CATALOG, getBridgeLogger } from '../../chunk-VT6HDD2L.js';
5
+ import { BUILTIN_DRIVER_CATALOG, getBridgeLogger } from '../../chunk-SUGDZOYZ.js';
6
6
  import '../../chunk-6E6PKKAD.js';
7
- import '../../chunk-U5POE4BA.js';
8
- import '../../chunk-7GOCNYUV.js';
7
+ import '../../chunk-5Q7HA4JP.js';
8
+ import '../../chunk-3A5IKBHQ.js';
9
9
  import '../../chunk-NSBPE2FW.js';
10
10
  import * as z from 'zod';
11
11