@skaile/workspaces 3.14.1 → 3.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 (207) hide show
  1. package/CHANGELOG.md +183 -0
  2. package/dist/{asset-feeds-STHKKTW3.js → asset-feeds-6UC7CUTI.js} +12 -12
  3. package/dist/{asset-feeds-STHKKTW3.js.map → asset-feeds-6UC7CUTI.js.map} +1 -1
  4. package/dist/asset-manager/catalog-deployer.js +7 -7
  5. package/dist/asset-manager/index.js +12 -12
  6. package/dist/asset-manager/installer.js +9 -9
  7. package/dist/asset-manager/renderers.js +2 -2
  8. package/dist/bridge/drivers/claude-sdk.js +15 -12
  9. package/dist/bridge/drivers/claude-sdk.js.map +1 -1
  10. package/dist/bridge/drivers/codex.js +4 -4
  11. package/dist/bridge/drivers/continue.js +4 -4
  12. package/dist/bridge/drivers/echo.js +3 -3
  13. package/dist/bridge/drivers/gemini.js +6 -6
  14. package/dist/bridge/drivers/goose.js +4 -4
  15. package/dist/bridge/drivers/omp.js +147 -8
  16. package/dist/bridge/drivers/omp.js.map +1 -1
  17. package/dist/bridge/drivers/opencode.js +4 -4
  18. package/dist/bridge/drivers/qwen.js +6 -6
  19. package/dist/bridge/index.js +10 -10
  20. package/dist/bridge/src/drivers/claude-sdk.d.ts.map +1 -1
  21. package/dist/bridge/src/drivers/omp.d.ts +62 -0
  22. package/dist/bridge/src/drivers/omp.d.ts.map +1 -1
  23. package/dist/bridge/src/index.d.ts +1 -0
  24. package/dist/bridge/src/index.d.ts.map +1 -1
  25. package/dist/bridge/src/models.d.ts +27 -2
  26. package/dist/bridge/src/models.d.ts.map +1 -1
  27. package/dist/bridge/src/provider-endpoints.d.ts +39 -0
  28. package/dist/bridge/src/provider-endpoints.d.ts.map +1 -1
  29. package/dist/bridge/src/validation.d.ts +6 -1
  30. package/dist/bridge/src/validation.d.ts.map +1 -1
  31. package/dist/{chunk-RUFTB7QN.js → chunk-3NCGSSCU.js} +3 -3
  32. package/dist/{chunk-RUFTB7QN.js.map → chunk-3NCGSSCU.js.map} +1 -1
  33. package/dist/{chunk-4VTBZ2MF.js → chunk-3XXQPHGL.js} +4 -4
  34. package/dist/{chunk-4VTBZ2MF.js.map → chunk-3XXQPHGL.js.map} +1 -1
  35. package/dist/{chunk-3UU3D2Q3.js → chunk-5KSIVLOX.js} +5 -5
  36. package/dist/{chunk-3UU3D2Q3.js.map → chunk-5KSIVLOX.js.map} +1 -1
  37. package/dist/{chunk-HYFJPR7W.js → chunk-6HTQNNOX.js} +7 -7
  38. package/dist/{chunk-HYFJPR7W.js.map → chunk-6HTQNNOX.js.map} +1 -1
  39. package/dist/{chunk-PGUTER7Z.js → chunk-6KPCHQP5.js} +3 -3
  40. package/dist/{chunk-PGUTER7Z.js.map → chunk-6KPCHQP5.js.map} +1 -1
  41. package/dist/{chunk-GHWJHOYX.js → chunk-7BUZQVRR.js} +4 -4
  42. package/dist/{chunk-GHWJHOYX.js.map → chunk-7BUZQVRR.js.map} +1 -1
  43. package/dist/{chunk-JAHHMZWG.js → chunk-7IRJ4K4F.js} +3 -3
  44. package/dist/{chunk-JAHHMZWG.js.map → chunk-7IRJ4K4F.js.map} +1 -1
  45. package/dist/{chunk-IRORTHGA.js → chunk-AN6NAFRT.js} +21 -7
  46. package/dist/chunk-AN6NAFRT.js.map +1 -0
  47. package/dist/{chunk-OYZIURPH.js → chunk-BQMRUH7J.js} +3 -3
  48. package/dist/{chunk-OYZIURPH.js.map → chunk-BQMRUH7J.js.map} +1 -1
  49. package/dist/{chunk-GRMQBBN4.js → chunk-BUKPS4YL.js} +3 -3
  50. package/dist/{chunk-GRMQBBN4.js.map → chunk-BUKPS4YL.js.map} +1 -1
  51. package/dist/{chunk-OCLCDB7G.js → chunk-C4AP3XZB.js} +6 -6
  52. package/dist/{chunk-OCLCDB7G.js.map → chunk-C4AP3XZB.js.map} +1 -1
  53. package/dist/{chunk-AZPXCHDK.js → chunk-CIPK5JMJ.js} +5 -6
  54. package/dist/chunk-CIPK5JMJ.js.map +1 -0
  55. package/dist/{chunk-KKMRT6GU.js → chunk-CL4TYERP.js} +10 -10
  56. package/dist/{chunk-KKMRT6GU.js.map → chunk-CL4TYERP.js.map} +1 -1
  57. package/dist/{chunk-IEX5YV4R.js → chunk-DVSASEGO.js} +3 -3
  58. package/dist/{chunk-IEX5YV4R.js.map → chunk-DVSASEGO.js.map} +1 -1
  59. package/dist/{chunk-PW4SJE3X.js → chunk-DZ4UZOFB.js} +4 -4
  60. package/dist/{chunk-PW4SJE3X.js.map → chunk-DZ4UZOFB.js.map} +1 -1
  61. package/dist/{chunk-5F33LGZL.js → chunk-ER4UOIUS.js} +3 -3
  62. package/dist/{chunk-5F33LGZL.js.map → chunk-ER4UOIUS.js.map} +1 -1
  63. package/dist/{chunk-G2CXWUO5.js → chunk-FAI2SMFE.js} +4 -4
  64. package/dist/{chunk-G2CXWUO5.js.map → chunk-FAI2SMFE.js.map} +1 -1
  65. package/dist/{chunk-5Q7HA4JP.js → chunk-FTG7CRFD.js} +3 -3
  66. package/dist/{chunk-5Q7HA4JP.js.map → chunk-FTG7CRFD.js.map} +1 -1
  67. package/dist/{chunk-V34ZEXSE.js → chunk-GURXUF2J.js} +32 -7
  68. package/dist/chunk-GURXUF2J.js.map +1 -0
  69. package/dist/{chunk-Z7W4B7P2.js → chunk-GWQZ2ECO.js} +6 -6
  70. package/dist/{chunk-Z7W4B7P2.js.map → chunk-GWQZ2ECO.js.map} +1 -1
  71. package/dist/{chunk-ZPCHTTD7.js → chunk-IVWJHELM.js} +116 -29
  72. package/dist/chunk-IVWJHELM.js.map +1 -0
  73. package/dist/{chunk-2A7BV6WT.js → chunk-JNCORFRK.js} +9 -6
  74. package/dist/chunk-JNCORFRK.js.map +1 -0
  75. package/dist/{chunk-XICDQVOG.js → chunk-KLDWNSDN.js} +4 -4
  76. package/dist/{chunk-XICDQVOG.js.map → chunk-KLDWNSDN.js.map} +1 -1
  77. package/dist/{chunk-URB2QZ4W.js → chunk-MFWIWPJP.js} +8 -8
  78. package/dist/{chunk-URB2QZ4W.js.map → chunk-MFWIWPJP.js.map} +1 -1
  79. package/dist/{chunk-VQ4P7UYE.js → chunk-MKZOK4JU.js} +3 -3
  80. package/dist/{chunk-VQ4P7UYE.js.map → chunk-MKZOK4JU.js.map} +1 -1
  81. package/dist/{chunk-AONVXRAE.js → chunk-NIUG3MIH.js} +3 -3
  82. package/dist/{chunk-AONVXRAE.js.map → chunk-NIUG3MIH.js.map} +1 -1
  83. package/dist/{chunk-ZSZSDBBW.js → chunk-OGTS7QJX.js} +5 -5
  84. package/dist/{chunk-ZSZSDBBW.js.map → chunk-OGTS7QJX.js.map} +1 -1
  85. package/dist/{chunk-SH572OBN.js → chunk-OIDFK7SG.js} +4 -4
  86. package/dist/{chunk-SH572OBN.js.map → chunk-OIDFK7SG.js.map} +1 -1
  87. package/dist/{chunk-IP3GXHOB.js → chunk-OZ7UNMTK.js} +3 -3
  88. package/dist/{chunk-IP3GXHOB.js.map → chunk-OZ7UNMTK.js.map} +1 -1
  89. package/dist/{chunk-UTW6YKAC.js → chunk-PMYZRX6M.js} +3 -3
  90. package/dist/{chunk-UTW6YKAC.js.map → chunk-PMYZRX6M.js.map} +1 -1
  91. package/dist/{chunk-O7WKKINT.js → chunk-S4UA244H.js} +5 -5
  92. package/dist/{chunk-O7WKKINT.js.map → chunk-S4UA244H.js.map} +1 -1
  93. package/dist/{chunk-WAFHQDLK.js → chunk-SUPIVTLX.js} +3 -3
  94. package/dist/{chunk-WAFHQDLK.js.map → chunk-SUPIVTLX.js.map} +1 -1
  95. package/dist/{chunk-Y6JJHOLR.js → chunk-SZ7VJQK3.js} +10 -10
  96. package/dist/{chunk-Y6JJHOLR.js.map → chunk-SZ7VJQK3.js.map} +1 -1
  97. package/dist/{chunk-3A5IKBHQ.js → chunk-W4IPN3DD.js} +61 -8
  98. package/dist/chunk-W4IPN3DD.js.map +1 -0
  99. package/dist/{chunk-VW6RUT7X.js → chunk-X2665LP2.js} +8 -8
  100. package/dist/{chunk-VW6RUT7X.js.map → chunk-X2665LP2.js.map} +1 -1
  101. package/dist/{chunk-M5D2QNTY.js → chunk-ZNCZ7RUO.js} +3 -3
  102. package/dist/{chunk-M5D2QNTY.js.map → chunk-ZNCZ7RUO.js.map} +1 -1
  103. package/dist/cli/index.js +60 -56
  104. package/dist/cli/index.js.map +1 -1
  105. package/dist/cli/src/commands/serve.d.ts.map +1 -1
  106. package/dist/connectors/config.js +7 -7
  107. package/dist/connectors/index.js +13 -13
  108. package/dist/core/index.js +6 -6
  109. package/dist/core/logging.js +2 -2
  110. package/dist/core/manifest.js +2 -2
  111. package/dist/core/runtime-assets.js +4 -4
  112. package/dist/core/src/logging/index.d.ts +2 -0
  113. package/dist/core/src/logging/index.d.ts.map +1 -1
  114. package/dist/core/src/logging/log-store.d.ts.map +1 -1
  115. package/dist/core/src/logging/sinks/stdout-sink.d.ts +6 -1
  116. package/dist/core/src/logging/sinks/stdout-sink.d.ts.map +1 -1
  117. package/dist/core/src/logging/stream-health.d.ts +46 -0
  118. package/dist/core/src/logging/stream-health.d.ts.map +1 -0
  119. package/dist/core/src/settings.d.ts +12 -1
  120. package/dist/core/src/settings.d.ts.map +1 -1
  121. package/dist/core/workspace-config.js +3 -3
  122. package/dist/deploy/index.js +8 -7
  123. package/dist/deploy/index.js.map +1 -1
  124. package/dist/deploy/src/targets/process-handle.d.ts.map +1 -1
  125. package/dist/discovery/index.js +4 -4
  126. package/dist/{ensure-sources-BFVKV773.js → ensure-sources-VJZUHSFZ.js} +15 -15
  127. package/dist/{ensure-sources-BFVKV773.js.map → ensure-sources-VJZUHSFZ.js.map} +1 -1
  128. package/dist/factory-assets/connectors/box.js +13 -13
  129. package/dist/factory-assets/connectors/deploy.js +13 -13
  130. package/dist/factory-assets/connectors/devserver.js +13 -13
  131. package/dist/factory-assets/connectors/flow/adapter.js +13 -13
  132. package/dist/factory-assets/connectors/flow/engine.js +7 -7
  133. package/dist/factory-assets/connectors/flow/run-flow.js +15 -15
  134. package/dist/factory-assets/connectors/flow.js +13 -13
  135. package/dist/factory-assets/connectors/git.js +13 -13
  136. package/dist/factory-assets/connectors/gmail.js +13 -13
  137. package/dist/factory-assets/connectors/googledrive.js +13 -13
  138. package/dist/factory-assets/connectors/local.js +13 -13
  139. package/dist/factory-assets/connectors/mattermost.js +13 -13
  140. package/dist/factory-assets/connectors/memory.js +13 -13
  141. package/dist/factory-assets/connectors/minio.js +13 -13
  142. package/dist/factory-assets/connectors/postgres.js +13 -13
  143. package/dist/factory-assets/connectors/s3.js +13 -13
  144. package/dist/factory-assets/connectors/sharepoint.js +13 -13
  145. package/dist/factory-assets/connectors/sqlite.js +13 -13
  146. package/dist/factory-assets/connectors/static-server.js +13 -13
  147. package/dist/factory-assets/connectors/tunnel.js +13 -13
  148. package/dist/factory-assets/connectors/webdav.js +13 -13
  149. package/dist/factory-assets/connectors/xstate-store.js +13 -13
  150. package/dist/factory-assets/connectors/xstate.js +13 -13
  151. package/dist/flows-2SUFOVVO.js +20 -0
  152. package/dist/{flows-4R4VJ5VM.js.map → flows-2SUFOVVO.js.map} +1 -1
  153. package/dist/{helpers-CQV2V2SL.js → helpers-EEYMGGLO.js} +15 -15
  154. package/dist/{helpers-CQV2V2SL.js.map → helpers-EEYMGGLO.js.map} +1 -1
  155. package/dist/library/index.js +9 -9
  156. package/dist/library/install.js +6 -6
  157. package/dist/{library-publish-6FZCXPYU.js → library-publish-37UBVMC3.js} +19 -19
  158. package/dist/{library-publish-6FZCXPYU.js.map → library-publish-37UBVMC3.js.map} +1 -1
  159. package/dist/open-library-JYVUN4CY.js +32 -0
  160. package/dist/{open-library-4EOFZ4QD.js.map → open-library-JYVUN4CY.js.map} +1 -1
  161. package/dist/paths-4APA2PKT.js +16 -0
  162. package/dist/{paths-S7UESJKT.js.map → paths-4APA2PKT.js.map} +1 -1
  163. package/dist/{plugin-store-5SJX2PRR.js → plugin-store-JQAIKR3R.js} +8 -8
  164. package/dist/{plugin-store-5SJX2PRR.js.map → plugin-store-JQAIKR3R.js.map} +1 -1
  165. package/dist/{project-J7RSKBAU.js → project-DKQACM3N.js} +16 -16
  166. package/dist/{project-J7RSKBAU.js.map → project-DKQACM3N.js.map} +1 -1
  167. package/dist/resolver/index.js +7 -7
  168. package/dist/runner/index.js +18 -18
  169. package/dist/runner/src/process-safety-net.d.ts +61 -0
  170. package/dist/runner/src/process-safety-net.d.ts.map +1 -0
  171. package/dist/runner/src/serve.d.ts +10 -0
  172. package/dist/runner/src/serve.d.ts.map +1 -1
  173. package/dist/sdk/asset-manager.js +12 -12
  174. package/dist/sdk/bridge.js +10 -10
  175. package/dist/sdk/core.js +6 -6
  176. package/dist/sdk/flow.js +7 -7
  177. package/dist/sdk/index.js +18 -18
  178. package/dist/sdk/resolver.js +7 -7
  179. package/dist/sdk/runner.js +18 -18
  180. package/dist/{setup-OQT5YK2Q.js → setup-7FJNMKFA.js} +12 -12
  181. package/dist/{setup-OQT5YK2Q.js.map → setup-7FJNMKFA.js.map} +1 -1
  182. package/dist/skills-Q7PJVB3W.js +20 -0
  183. package/dist/{skills-OBJX3VYG.js.map → skills-Q7PJVB3W.js.map} +1 -1
  184. package/dist/{source-AJ27AS6D.js → source-CT6RH3CJ.js} +18 -18
  185. package/dist/{source-AJ27AS6D.js.map → source-CT6RH3CJ.js.map} +1 -1
  186. package/dist/{source-manifest-publish-UCA2O5UA.js → source-manifest-publish-VBENANBT.js} +20 -20
  187. package/dist/{source-manifest-publish-UCA2O5UA.js.map → source-manifest-publish-VBENANBT.js.map} +1 -1
  188. package/dist/tui/index.js +18 -18
  189. package/dist/types/manifests.js +1 -1
  190. package/dist/types/src/manifests/agent.d.ts +47 -7
  191. package/dist/types/src/manifests/agent.d.ts.map +1 -1
  192. package/dist/types/src/manifests/index.d.ts +2 -2
  193. package/dist/types/src/manifests/index.d.ts.map +1 -1
  194. package/dist/workspace-plugin/adapters/mcp.js +11 -11
  195. package/dist/workspace-plugin/adapters/omp.js +7 -7
  196. package/dist/workspace-plugin/index.js +3 -3
  197. package/package.json +2 -1
  198. package/dist/chunk-2A7BV6WT.js.map +0 -1
  199. package/dist/chunk-3A5IKBHQ.js.map +0 -1
  200. package/dist/chunk-AZPXCHDK.js.map +0 -1
  201. package/dist/chunk-IRORTHGA.js.map +0 -1
  202. package/dist/chunk-V34ZEXSE.js.map +0 -1
  203. package/dist/chunk-ZPCHTTD7.js.map +0 -1
  204. package/dist/flows-4R4VJ5VM.js +0 -20
  205. package/dist/open-library-4EOFZ4QD.js +0 -32
  206. package/dist/paths-S7UESJKT.js +0 -16
  207. package/dist/skills-OBJX3VYG.js +0 -20
package/CHANGELOG.md CHANGED
@@ -1,5 +1,188 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.16.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#628](https://github.com/skaile-ai/workspaces/pull/628) [`f2069be`](https://github.com/skaile-ai/workspaces/commit/f2069be99b7e1d42020a92510a8aaa0d369a4fe2) Thanks [@mortegro](https://github.com/mortegro)! - fix(serve): stop the unbounded EPIPE log loop on a broken stdout pipe ([#627](https://github.com/skaile-ai/workspaces/issues/627))
8
+
9
+ `serve`'s `uncaughtException` handler logged through the same stdout sink whose
10
+ failure raised it. When a parent closed the child's stdout pipe, each log line
11
+ raised `EPIPE`, the handler logged that, and the log line raised `EPIPE` again.
12
+ Marking the exception "(non-fatal)" also removed the exit an unhandled `EPIPE`
13
+ would normally have caused, so nothing terminated the cycle: 237,752 copies of
14
+ one stack trace and 1.0 GB of `logs.db` in under four hours.
15
+
16
+ The existing `try/catch` in `LogStore.flushSync` looked like it covered this but
17
+ could not: writes to a pipe are asynchronous, so `EPIPE` surfaces on the
18
+ stream's `error` event after `writeBatch()` has already returned.
19
+
20
+ Guards at each place the cycle passes through:
21
+
22
+ - `StdoutSink` goes quiet once its stream is destroyed, ended, or marked broken.
23
+ It deliberately does _not_ claim the stream's `error` event: doing so
24
+ suppresses Node's fatal-EPIPE exit, which is right for a long-lived server and
25
+ wrong for a CLI piped into `head`. Ownership of that event sits with
26
+ `watchOutputStream`, which only `serve` calls.
27
+ - A broken output pipe silences the sinks in every mode. The exit is separate:
28
+ new `ServeOptions.exitOnBrokenPipe`, off by default so a library caller's
29
+ process is never killed, opted into by `skaile serve` and opted back out by
30
+ `SKAILE_SERVE_EXIT_ON_BROKEN_STDOUT=0` (set by the `local` / `nix` deploy
31
+ targets, whose children outlive the CLI by design).
32
+ - `watchOutputStream` is the sole owner of stdout's `error` event, and is
33
+ installed as soon as the LogStore exists so the guarantee covers boot too.
34
+ It is deliberately separate from `installProcessSafetyNet`, which downgrades
35
+ uncaught errors to logged and must wait until startup has succeeded — during
36
+ boot a failure before the transport listens still has to be fatal.
37
+ Claiming that event is what keeps the async failure out of `uncaughtException`
38
+ and so out of the sink that raised it.
39
+ - `installProcessSafetyNet` still logs the errors it downgrades, through the
40
+ sinks that remain writable (SQLite, WebSocket), instead of dropping them when
41
+ stdout is gone. It refuses to re-enter itself, so a failure raised while
42
+ logging cannot be logged in turn.
43
+ - `LogStore`'s last-resort `process.stderr` fallback no longer throws when
44
+ stderr is dead too, which is how the periodic flush timer joined the loop.
45
+
46
+ ## 3.15.0
47
+
48
+ ### Minor Changes
49
+
50
+ - [#635](https://github.com/skaile-ai/workspaces/pull/635) [`22ffa9c`](https://github.com/skaile-ai/workspaces/commit/22ffa9cd74ad9f9e9d233e0f3c7b2243f819ba15) Thanks [@mortegro](https://github.com/mortegro)! - Honour the configured Anthropic base URL in model discovery
51
+
52
+ Anthropic model discovery hardcoded `api.anthropic.com`. Behind a gateway the
53
+ credential in play is the gateway's, so that request 401s, discovery returns an
54
+ empty catalog, and any caller that reads "empty" as "unknown" degrades into
55
+ "accept any model" — which is how a bad model ID reached a live session with no
56
+ validation error. Consumers were working around it with their own gateway-aware
57
+ fetch.
58
+
59
+ `fetchProviderModels`, `getModels`, and `validateApiKey` take an optional
60
+ `{ baseUrl, env }` argument and resolve the endpoint through the new
61
+ `resolveProviderModelsUrl`, whose precedence is explicit `baseUrl` → the
62
+ provider's own base-URL env var (`ANTHROPIC_BASE_URL`, `OPENAI_BASE_URL`) →
63
+ the provider's public API. Joining is tolerant of a trailing slash and of a
64
+ base that already ends in `/v1`.
65
+
66
+ `ClaudeSdkDriver.listModels()` now reports a catalog under `cloud: gateway`,
67
+ resolving the base URL from `cloudConfig.baseUrl` → config env → ambient env and
68
+ the credential from `cloudSecrets` (auth token first, mirroring
69
+ `buildClaudeCloudEnv`). Bedrock, Vertex, and Azure still report nothing — those
70
+ address models through their own control planes, where an Anthropic
71
+ `/v1/models` call is the wrong endpoint and a key leak.
72
+
73
+ Behaviour with no base URL configured is unchanged. New exports:
74
+ `resolveProviderModelsUrl`, `FetchModelsOptions`, and the `provider-endpoints`
75
+ module from the bridge index.
76
+
77
+ - [#635](https://github.com/skaile-ai/workspaces/pull/635) [`22ffa9c`](https://github.com/skaile-ai/workspaces/commit/22ffa9cd74ad9f9e9d233e0f3c7b2243f819ba15) Thanks [@mortegro](https://github.com/mortegro)! - Accept the current Anthropic model lineup in agent manifests
78
+
79
+ `ModelIdSchema` demanded a version with at least two numeric components
80
+ (`claude-sonnet-4-5`), so every current single-digit-major ID — `claude-opus-5`,
81
+ `claude-sonnet-5`, `claude-fable-5` — failed validation outright and a manifest
82
+ could not name a model that demonstrably exists. `fable` was also missing from
83
+ the family vocabulary entirely. Authors were pushed onto family aliases or
84
+ stale IDs, and `normalizeAnthropicModel` silently downgraded a perfectly good
85
+ current ID to `sonnet`.
86
+
87
+ The version segment now accepts one or more numeric components, and the
88
+ family/version core is shared by the full, Bedrock, and Vertex patterns instead
89
+ of being spelled out three times. New exports: `ANTHROPIC_MODEL_FAMILIES` and
90
+ `AnthropicModelFamily`.
91
+
92
+ Consequence worth knowing: `claude-opus-4` is structurally indistinguishable
93
+ from `claude-opus-5`, so it now validates too and
94
+ `normalizeAnthropicModel("claude-opus-4")` returns it unchanged rather than
95
+ coercing to `"opus"`. This validator is structural, not a catalog check — an ID
96
+ no provider serves is caught at dispatch.
97
+
98
+ `fable` is deliberately _not_ added to `ANTHROPIC_MODEL_ALIASES`: it is a real
99
+ ID family but has no Claude Code rolling alias, so a malformed `claude-fable-*`
100
+ falls through to the caller's fallback rather than to an alias the runtime
101
+ would reject.
102
+
103
+ - [#635](https://github.com/skaile-ai/workspaces/pull/635) [`22ffa9c`](https://github.com/skaile-ai/workspaces/commit/22ffa9cd74ad9f9e9d233e0f3c7b2243f819ba15) Thanks [@mortegro](https://github.com/mortegro)! - Widen the omp child-env allowlist and surface failed omp requests
104
+
105
+ The omp child process was started with an env allowlist carrying exactly one
106
+ provider credential — `ANTHROPIC_API_KEY`. omp is model-agnostic and selects a
107
+ provider from whichever credential it finds, so a workspace configured for any
108
+ other provider had nothing in the child to authenticate with and silently fell
109
+ back. `ANTHROPIC_BASE_URL` was dropped too, so an Anthropic-compatible gateway
110
+ was bypassed and traffic went to Anthropic's own API instead. Downstream this
111
+ was carried as a local `patch-package` patch, i.e. lost on any reinstall or
112
+ version bump.
113
+
114
+ The allowlist now mirrors the "Environment Variables" section of `omp --help` —
115
+ the authoritative statement of what the child actually reads — across Anthropic
116
+ (direct, gateway, Foundry/mTLS), the other first-party providers, the
117
+ aggregators, the cloud model planes, and the search backends. It remains an
118
+ allowlist: names outside it, including anything a platform provisions under its
119
+ own naming, still do not reach the child. Exported as
120
+ `OMP_PROVIDER_ENV_KEYS`, alongside `buildOmpChildEnv`.
121
+
122
+ Separately, omp reports a failed request as a normal `message_end` carrying
123
+ `stopReason: "error"`, not as an `error` event. The driver ignored that, so a
124
+ failed turn completed silently with no assistant text and no failure — it read
125
+ as a hung or empty agent. It now emits a classified `error` agent-event on the
126
+ same channel as every other driver error. `message_start` also seeds the
127
+ delta tracker from its own content instead of clearing it, so a start frame
128
+ that already carries text no longer causes the first chunk to be emitted twice.
129
+
130
+ - [#635](https://github.com/skaile-ai/workspaces/pull/635) [`22ffa9c`](https://github.com/skaile-ai/workspaces/commit/22ffa9cd74ad9f9e9d233e0f3c7b2243f819ba15) Thanks [@mortegro](https://github.com/mortegro)! - Declare `tools` on the agent manifest and forward it to the omp driver
131
+
132
+ An agent definition's `tools:` block was never declared on
133
+ `AgentManifestSchema`; it survived only because that schema is a loose object.
134
+ So it was untyped at every read site, unvalidated, and — because the omp driver
135
+ never passed it on — enforced on the Claude SDK path while doing nothing at all
136
+ on omp. A consuming app could not trim omp's toolset, which matters because
137
+ omp's default toolset dominates the fixed token prefix of every request.
138
+
139
+ `tools` is now a declared field backed by `AgentToolsSchema` (exported,
140
+ alongside the `AgentTools` type), with `allowed` and `denied` typed as string
141
+ arrays.
142
+
143
+ The omp driver translates the manifest's canonical Claude Code tool names into
144
+ omp's built-in names and passes `--tools a,b,c`. omp has no deny flag, so a
145
+ deny list is applied by subtracting from omp's default toolset; a restriction
146
+ that leaves nothing enabled becomes `--no-tools`. Names omp has no built-in for
147
+ (`WebFetch`, `mcp__*`) are dropped, because `--tools` governs built-ins only. A
148
+ manifest with no `tools` block passes no flag and keeps omp's full default
149
+ toolset exactly as before. Exported as `buildOmpToolArgs`, `OMP_BUILTIN_TOOLS`,
150
+ and `OMP_DEFAULT_TOOLS`.
151
+
152
+ Note that a malformed `tools:` block now fails manifest validation where it
153
+ previously slipped through untyped.
154
+
155
+ - [#635](https://github.com/skaile-ai/workspaces/pull/635) [`22ffa9c`](https://github.com/skaile-ai/workspaces/commit/22ffa9cd74ad9f9e9d233e0f3c7b2243f819ba15) Thanks [@mortegro](https://github.com/mortegro)! - Stop pinning a default model in the built-in settings and in `skaile init`
156
+
157
+ `SETTINGS_DEFAULTS.model` was `"claude-sonnet-4-6"`, and both `loadSettings` and
158
+ `resolveSettings` spread the built-in defaults _under_ every other layer. That made
159
+ the pin a floor rather than a default: deleting `model` from `.skaile/settings.json`
160
+ did not unset it, the built-in came back. A consumer could therefore never express
161
+ "no model configured", and could not route an unconfigured session to its own
162
+ per-backend default — it silently inherited whatever id was compiled in here.
163
+
164
+ The failure mode is not hypothetical. A host app removed the model from every
165
+ settings source it owned, created a workspace, and got `claude-sonnet-4-6` back in
166
+ its project metadata; its own per-backend default resolution was unreachable because
167
+ the field was never falsy by the time it ran. When the pinned id is one the account
168
+ cannot serve — retired, or rate-limited below the request's fixed token prefix —
169
+ every session starts on it and every retry fails the same way.
170
+
171
+ `skaile init --backend claude-code` carried the same id in `BACKEND_DEFAULTS` and
172
+ wrote it into the `agent-config` block of each generated skaile.yaml, pinning every
173
+ new workspace at creation. `omp` and `codex` already omitted a model there;
174
+ `claude-code` is now consistent with them.
175
+
176
+ Both defaults are now unset, so the driver applies its own current default: omp
177
+ omits `--model` and lets omp choose, `claude-sdk` falls back to the `sonnet` rolling
178
+ alias. Nothing else in the resolution chain changes — an explicit `model` in
179
+ settings, skaile.yaml, `PREFERRED_MODEL`, or a CLI override behaves exactly as
180
+ before.
181
+
182
+ `PROVIDER_MODEL_TIERS.anthropic.default` is deliberately untouched. It is a
183
+ tier-selection table with a test-enforced correspondence to `STATIC_MODELS`, not a
184
+ settings floor, and choosing the Anthropic default tier is a separate decision.
185
+
3
186
  ## 3.14.1
4
187
 
5
188
  ### Patch Changes
@@ -1,18 +1,18 @@
1
- import { assignDomain, buildDomainIndex, DOMAIN_MD_FILENAME, parseDomainMd } from './chunk-PW4SJE3X.js';
1
+ import { assignDomain, buildDomainIndex, DOMAIN_MD_FILENAME, parseDomainMd } from './chunk-DZ4UZOFB.js';
2
2
  import './chunk-JXT46QFC.js';
3
- import './chunk-AZPXCHDK.js';
3
+ import './chunk-CIPK5JMJ.js';
4
4
  import './chunk-F3HNW354.js';
5
5
  import './chunk-K5GBV4SA.js';
6
6
  import './chunk-QVZA2MDT.js';
7
- import { factoryAssetRoots } from './chunk-SH572OBN.js';
8
- import { scanRepo } from './chunk-IP3GXHOB.js';
7
+ import { factoryAssetRoots } from './chunk-OIDFK7SG.js';
8
+ import { scanRepo } from './chunk-OZ7UNMTK.js';
9
9
  import './chunk-MENCWIHW.js';
10
10
  import './chunk-P4VHQA4N.js';
11
- import { walkDir, parseFrontmatter } from './chunk-VQ4P7UYE.js';
11
+ import { walkDir, parseFrontmatter } from './chunk-MKZOK4JU.js';
12
12
  import './chunk-GEEW6SY2.js';
13
- import './chunk-5Q7HA4JP.js';
14
- import './chunk-3A5IKBHQ.js';
15
- import './chunk-IRORTHGA.js';
13
+ import './chunk-FTG7CRFD.js';
14
+ import './chunk-W4IPN3DD.js';
15
+ import './chunk-AN6NAFRT.js';
16
16
  import './chunk-NSBPE2FW.js';
17
17
  import { existsSync, readFileSync } from 'fs';
18
18
  import * as path from 'path';
@@ -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-4EOFZ4QD.js'),
68
+ import('./open-library-JYVUN4CY.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-4EOFZ4QD.js');
101
+ const { resolveCatalogSource } = await import('./open-library-JYVUN4CY.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-STHKKTW3.js.map
206
- //# sourceMappingURL=asset-feeds-STHKKTW3.js.map
205
+ //# sourceMappingURL=asset-feeds-6UC7CUTI.js.map
206
+ //# sourceMappingURL=asset-feeds-6UC7CUTI.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../cli/src/asset-feeds.ts"],"names":[],"mappings":"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","file":"asset-feeds-STHKKTW3.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-6UC7CUTI.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-GHWJHOYX.js';
2
- import '../chunk-AZPXCHDK.js';
1
+ export { WORKSPACE_MOUNT_ENV_VAR, deployAll, deployCatalogEntry, findAiAssetsRoot, isUnderWorkspaceMount, resolveFlowSkills, undeployCatalogEntry, workspaceMountDirs } from '../chunk-7BUZQVRR.js';
2
+ import '../chunk-CIPK5JMJ.js';
3
3
  import '../chunk-F3HNW354.js';
4
4
  import '../chunk-K5GBV4SA.js';
5
5
  import '../chunk-QVZA2MDT.js';
6
- import '../chunk-SH572OBN.js';
7
- import '../chunk-IP3GXHOB.js';
6
+ import '../chunk-OIDFK7SG.js';
7
+ import '../chunk-OZ7UNMTK.js';
8
8
  import '../chunk-MENCWIHW.js';
9
9
  import '../chunk-P4VHQA4N.js';
10
- import '../chunk-VQ4P7UYE.js';
10
+ import '../chunk-MKZOK4JU.js';
11
11
  import '../chunk-GEEW6SY2.js';
12
- import '../chunk-5Q7HA4JP.js';
13
- import '../chunk-3A5IKBHQ.js';
12
+ import '../chunk-FTG7CRFD.js';
13
+ import '../chunk-W4IPN3DD.js';
14
14
  import '../chunk-NSBPE2FW.js';
15
15
  //# sourceMappingURL=catalog-deployer.js.map
16
16
  //# sourceMappingURL=catalog-deployer.js.map
@@ -1,27 +1,27 @@
1
- export { AssetManager, FACTORY_SOURCE_URL, MissingManifestError, NON_DEPLOYABLE_KINDS, normalizeRepoUrl, parseCanonicalLockKey, parseRefTolerant, renderMcpServerConfig, softDepName } from '../chunk-URB2QZ4W.js';
2
- import '../chunk-OCLCDB7G.js';
3
- import '../chunk-PW4SJE3X.js';
1
+ export { AssetManager, FACTORY_SOURCE_URL, MissingManifestError, NON_DEPLOYABLE_KINDS, normalizeRepoUrl, parseCanonicalLockKey, parseRefTolerant, renderMcpServerConfig, softDepName } from '../chunk-MFWIWPJP.js';
2
+ import '../chunk-C4AP3XZB.js';
3
+ import '../chunk-DZ4UZOFB.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-O7WKKINT.js';
7
+ export { createScaffold, deployAll, installAgent, removeAsset } from '../chunk-S4UA244H.js';
8
8
  export { BUILT_IN_FRAGMENTS, loadPromptExtensions, resolveFragments } from '../chunk-CGYEHQOX.js';
9
- import '../chunk-AZPXCHDK.js';
9
+ import '../chunk-CIPK5JMJ.js';
10
10
  import '../chunk-F3HNW354.js';
11
11
  import '../chunk-K5GBV4SA.js';
12
12
  import '../chunk-QVZA2MDT.js';
13
- import '../chunk-SH572OBN.js';
14
- import '../chunk-IP3GXHOB.js';
13
+ import '../chunk-OIDFK7SG.js';
14
+ import '../chunk-OZ7UNMTK.js';
15
15
  import '../chunk-MENCWIHW.js';
16
16
  import '../chunk-P4VHQA4N.js';
17
- import '../chunk-VQ4P7UYE.js';
17
+ import '../chunk-MKZOK4JU.js';
18
18
  import '../chunk-GEEW6SY2.js';
19
- import '../chunk-5Q7HA4JP.js';
20
- import '../chunk-3A5IKBHQ.js';
19
+ import '../chunk-FTG7CRFD.js';
20
+ import '../chunk-W4IPN3DD.js';
21
21
  export { appendHistory, clearHistory, getRecentHistory, loadHistory } from '../chunk-KTBKW2FI.js';
22
22
  import '../chunk-UQ6LFBPZ.js';
23
- export { AGENT_RENDERERS, claudeCodeRenderer, codexRenderer, driverTargetSupportsAgents, ompRenderer, renderAgentToFramework } from '../chunk-IEX5YV4R.js';
24
- import '../chunk-IRORTHGA.js';
23
+ export { AGENT_RENDERERS, claudeCodeRenderer, codexRenderer, driverTargetSupportsAgents, ompRenderer, renderAgentToFramework } from '../chunk-DVSASEGO.js';
24
+ import '../chunk-AN6NAFRT.js';
25
25
  import '../chunk-NSBPE2FW.js';
26
26
  //# sourceMappingURL=index.js.map
27
27
  //# sourceMappingURL=index.js.map
@@ -1,19 +1,19 @@
1
- export { createScaffold, deployAll, deployAsset, removeAsset } from '../chunk-O7WKKINT.js';
1
+ export { createScaffold, deployAll, deployAsset, removeAsset } from '../chunk-S4UA244H.js';
2
2
  import '../chunk-CGYEHQOX.js';
3
- import '../chunk-AZPXCHDK.js';
3
+ import '../chunk-CIPK5JMJ.js';
4
4
  import '../chunk-F3HNW354.js';
5
5
  import '../chunk-K5GBV4SA.js';
6
6
  import '../chunk-QVZA2MDT.js';
7
- import '../chunk-SH572OBN.js';
8
- import '../chunk-IP3GXHOB.js';
7
+ import '../chunk-OIDFK7SG.js';
8
+ import '../chunk-OZ7UNMTK.js';
9
9
  import '../chunk-MENCWIHW.js';
10
10
  import '../chunk-P4VHQA4N.js';
11
- import '../chunk-VQ4P7UYE.js';
11
+ import '../chunk-MKZOK4JU.js';
12
12
  import '../chunk-GEEW6SY2.js';
13
- import '../chunk-5Q7HA4JP.js';
14
- import '../chunk-3A5IKBHQ.js';
15
- import '../chunk-IEX5YV4R.js';
16
- import '../chunk-IRORTHGA.js';
13
+ import '../chunk-FTG7CRFD.js';
14
+ import '../chunk-W4IPN3DD.js';
15
+ import '../chunk-DVSASEGO.js';
16
+ import '../chunk-AN6NAFRT.js';
17
17
  import '../chunk-NSBPE2FW.js';
18
18
  //# sourceMappingURL=installer.js.map
19
19
  //# sourceMappingURL=installer.js.map
@@ -1,5 +1,5 @@
1
- export { AGENT_RENDERERS, claudeCodeRenderer, codexRenderer, driverTargetSupportsAgents, ompRenderer, renderAgentToFramework } from '../chunk-IEX5YV4R.js';
2
- import '../chunk-IRORTHGA.js';
1
+ export { AGENT_RENDERERS, claudeCodeRenderer, codexRenderer, driverTargetSupportsAgents, ompRenderer, renderAgentToFramework } from '../chunk-DVSASEGO.js';
2
+ import '../chunk-AN6NAFRT.js';
3
3
  import '../chunk-NSBPE2FW.js';
4
4
  //# sourceMappingURL=renderers.js.map
5
5
  //# sourceMappingURL=renderers.js.map
@@ -1,21 +1,21 @@
1
- import { scrubPoisonedTranscript, resolveClaudeConfigDir, SUPPORTED_CLOUDS, buildClaudeCloudEnv } from '../../chunk-RUFTB7QN.js';
1
+ import { scrubPoisonedTranscript, resolveClaudeConfigDir, SUPPORTED_CLOUDS, buildClaudeCloudEnv } from '../../chunk-3NCGSSCU.js';
2
2
  import { dispatchCapability } from '../../chunk-RRVQAE5D.js';
3
- import { fetchProviderModels } from '../../chunk-V34ZEXSE.js';
3
+ import { fetchProviderModels } from '../../chunk-GURXUF2J.js';
4
4
  import { AuthError, classifyClaudeSdkError, reclassifyAuthAsAccountUnavailable, AccountUnavailableError } from '../../chunk-GAH4YRCI.js';
5
- import { BUILTIN_DRIVER_CATALOG, AgentDriver, getBridgeLogger } from '../../chunk-AONVXRAE.js';
5
+ import { BUILTIN_DRIVER_CATALOG, AgentDriver, getBridgeLogger } from '../../chunk-NIUG3MIH.js';
6
6
  import '../../chunk-6E6PKKAD.js';
7
- import '../../chunk-AZPXCHDK.js';
7
+ import '../../chunk-CIPK5JMJ.js';
8
8
  import '../../chunk-F3HNW354.js';
9
9
  import '../../chunk-K5GBV4SA.js';
10
10
  import '../../chunk-QVZA2MDT.js';
11
- import '../../chunk-SH572OBN.js';
12
- import '../../chunk-IP3GXHOB.js';
11
+ import '../../chunk-OIDFK7SG.js';
12
+ import '../../chunk-OZ7UNMTK.js';
13
13
  import '../../chunk-MENCWIHW.js';
14
14
  import '../../chunk-P4VHQA4N.js';
15
- import '../../chunk-VQ4P7UYE.js';
15
+ import '../../chunk-MKZOK4JU.js';
16
16
  import '../../chunk-GEEW6SY2.js';
17
- import '../../chunk-5Q7HA4JP.js';
18
- import '../../chunk-3A5IKBHQ.js';
17
+ import '../../chunk-FTG7CRFD.js';
18
+ import '../../chunk-W4IPN3DD.js';
19
19
  import '../../chunk-NSBPE2FW.js';
20
20
  import { spawnSync, spawn } from 'child_process';
21
21
  import { readFileSync, existsSync } from 'fs';
@@ -2144,10 +2144,13 @@ var ClaudeSdkDriver = class extends AgentDriver {
2144
2144
  this.hasSession = false;
2145
2145
  }
2146
2146
  async listModels() {
2147
- if (this.resolveCloud() !== "default") return [];
2148
- const apiKey = this.config.apiKeys?.anthropic || this.config.env?.ANTHROPIC_API_KEY || process.env.ANTHROPIC_API_KEY;
2147
+ const cloud = this.resolveCloud();
2148
+ if (cloud !== "default" && cloud !== "gateway") return [];
2149
+ const baseUrl = cloud === "gateway" ? this.config.cloudConfig?.baseUrl || this.config.env?.ANTHROPIC_BASE_URL || process.env.ANTHROPIC_BASE_URL : void 0;
2150
+ const apiKey = (cloud === "gateway" ? this.config.cloudSecrets?.ANTHROPIC_AUTH_TOKEN || this.config.cloudSecrets?.ANTHROPIC_API_KEY : void 0) || this.config.apiKeys?.anthropic || this.config.env?.ANTHROPIC_API_KEY || process.env.ANTHROPIC_API_KEY;
2149
2151
  if (!apiKey) return [];
2150
- const result = await fetchProviderModels("anthropic", apiKey);
2152
+ if (cloud === "gateway" && !baseUrl) return [];
2153
+ const result = await fetchProviderModels("anthropic", apiKey, { baseUrl });
2151
2154
  return result.ok ? result.models : [];
2152
2155
  }
2153
2156
  getTokenUsage() {