@xopcai/xopc 0.0.166 → 0.0.168

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 (151) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/feishu/src/outbound/media-load.js +1 -1
  3. package/dist/extensions/telegram/xopc.extension.json +1 -1
  4. package/dist/gateway/static/root/assets/{activity-store-DkALQFJT.js → activity-store-DjEDu38T.js} +1 -1
  5. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-Tr2hgn4j.js → agent-avatar-dicebear-DVuLzaJi.js} +1 -1
  6. package/dist/gateway/static/root/assets/agents-tF_QTWxn.js +2 -0
  7. package/dist/gateway/static/root/assets/{api-DPIXEcIq.js → api-DwVCuPOb.js} +1 -1
  8. package/dist/gateway/static/root/assets/{app-management-settings-panel-D7Nrbhez.js → app-management-settings-panel-spzVBvAu.js} +1 -1
  9. package/dist/gateway/static/root/assets/{appearance-settings-DNNbABFj.js → appearance-settings-CetimI47.js} +1 -1
  10. package/dist/gateway/static/root/assets/{apps-page-0zXIeywq.js → apps-page-HDyN6Fjx.js} +1 -1
  11. package/dist/gateway/static/root/assets/{archive-plugin-CizqFy4G.js → archive-plugin-TOmQ2XKc.js} +1 -1
  12. package/dist/gateway/static/root/assets/{attachment-load-DU8KjTua.js → attachment-load-NFNEWVLJ.js} +1 -1
  13. package/dist/gateway/static/root/assets/{automation-suggestion-card-3pqyIveW.js → automation-suggestion-card-B0FnLpSl.js} +1 -1
  14. package/dist/gateway/static/root/assets/{automations-page-DSz3xDsi.js → automations-page-DVevVD6A.js} +1 -1
  15. package/dist/gateway/static/root/assets/{binary-plugins-CipUnsOr.js → binary-plugins-Csc4xkR9.js} +1 -1
  16. package/dist/gateway/static/root/assets/{block-editor-XoEq6qiv.js → block-editor-C6Xgrbse.js} +1 -1
  17. package/dist/gateway/static/root/assets/{browser-settings-page-COYvq3y5.js → browser-settings-page-CyDDEKxB.js} +1 -1
  18. package/dist/gateway/static/root/assets/{browser-workflow-inputs-DRH995XI.js → browser-workflow-inputs-DaxyAai1.js} +1 -1
  19. package/dist/gateway/static/root/assets/{browser-workflows-page-65edjhpz.js → browser-workflows-page-CaX6iIJA.js} +1 -1
  20. package/dist/gateway/static/root/assets/capabilities-settings-panel-EXZv96j1.js +7 -0
  21. package/dist/gateway/static/root/assets/capability-presets-api-DdCHSzcA.js +1 -0
  22. package/dist/gateway/static/root/assets/capability-presets-settings-panel-o08SXqM4.js +2 -0
  23. package/dist/gateway/static/root/assets/{channel-recipient-api-B5rLegwk.js → channel-recipient-api-BOkLI89e.js} +1 -1
  24. package/dist/gateway/static/root/assets/{channels-settings-D8nH0mve.js → channels-settings-aCN05KdJ.js} +1 -1
  25. package/dist/gateway/static/root/assets/channels-status-swr-ZrqadU60.js +1 -0
  26. package/dist/gateway/static/root/assets/{connector-logo-n4Hg41iD.js → connector-logo-Mp40HWsb.js} +1 -1
  27. package/dist/gateway/static/root/assets/{connectors-page-jvRWZn3Y.js → connectors-page-i0cu5rg5.js} +1 -1
  28. package/dist/gateway/static/root/assets/{desktop-pet-D4OzBOwm.js → desktop-pet-BtzUJ0d7.js} +1 -1
  29. package/dist/gateway/static/root/assets/{desktop-pet-settings-CDGezbaf.js → desktop-pet-settings-Dg0Duj8B.js} +1 -1
  30. package/dist/gateway/static/root/assets/{directory-picker-path-field-DmL4AUXQ.js → directory-picker-path-field-9f0k1wZq.js} +1 -1
  31. package/dist/gateway/static/root/assets/{dreaming-settings-ClUcBKnd.js → dreaming-settings-rdW7dsGP.js} +1 -1
  32. package/dist/gateway/static/root/assets/{extension-debug-page-DsV_KUDM.js → extension-debug-page-C9zUnn-z.js} +1 -1
  33. package/dist/gateway/static/root/assets/{extension-page-DJv_G1jY.js → extension-page-Cy5rfU0h.js} +1 -1
  34. package/dist/gateway/static/root/assets/{extension-settings-page-DUAlJ4CU.js → extension-settings-page-BjIquAw6.js} +1 -1
  35. package/dist/gateway/static/root/assets/{gateway-config-api-SVYgzqv7.js → gateway-config-api-AQQmI6SW.js} +1 -1
  36. package/dist/gateway/static/root/assets/{gateway-settings-YL6f2C_F.js → gateway-settings-CYNmbqHj.js} +1 -1
  37. package/dist/gateway/static/root/assets/{gateway-startup-retry-ulwTJovP.js → gateway-startup-retry-qnEmj-vL.js} +1 -1
  38. package/dist/gateway/static/root/assets/{goal-create-dialog-CJJe-4Co.js → goal-create-dialog-CkXaDfN3.js} +1 -1
  39. package/dist/gateway/static/root/assets/{goal-detail-page-Da9VMWRd.js → goal-detail-page-FqDa5bfO.js} +1 -1
  40. package/dist/gateway/static/root/assets/{goals-config-api-st_Itxa9.js → goals-config-api-CaVGtM6n.js} +1 -1
  41. package/dist/gateway/static/root/assets/{goals-page-B6pC7PYZ.js → goals-page-B-uBRGN6.js} +1 -1
  42. package/dist/gateway/static/root/assets/{goals-settings-BEZePZ5m.js → goals-settings-C9J3MX0F.js} +1 -1
  43. package/dist/gateway/static/root/assets/{heartbeat-settings-DpeD9GQ9.js → heartbeat-settings-C0qFLyew.js} +1 -1
  44. package/dist/gateway/static/root/assets/{index-BhQMnKH1.js → index-DchbD-qM.js} +9 -9
  45. package/dist/gateway/static/root/assets/index-DfE2j0g8.css +1 -0
  46. package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-BAZlRZMd.js +1 -0
  47. package/dist/gateway/static/root/assets/{local-app-workbench-page-Ctia6lSG.js → local-app-workbench-page-DkJUQRyC.js} +1 -1
  48. package/dist/gateway/static/root/assets/{local-apps-page-DWF9rpWQ.js → local-apps-page-PcQABi7F.js} +1 -1
  49. package/dist/gateway/static/root/assets/locale-store-tnRLWdx2.js +1 -0
  50. package/dist/gateway/static/root/assets/{logs-page-D3gL6vHX.js → logs-page-B-nXM29t.js} +1 -1
  51. package/dist/gateway/static/root/assets/{markdown-split-BuduJ0Ev.js → markdown-split-B6W1H0Fj.js} +1 -1
  52. package/dist/gateway/static/root/assets/{media-plugins-CLASZjhT.js → media-plugins-B-r9Tj74.js} +1 -1
  53. package/dist/gateway/static/root/assets/{messages-C96Zjb0V.js → messages-B1EG4gdu.js} +2 -4
  54. package/dist/gateway/static/root/assets/{notes-page-ve3XiBDF.js → notes-page-BNECY6Oy.js} +1 -1
  55. package/dist/gateway/static/root/assets/{notes-workbench-oHU532lp.js → notes-workbench-Be97S0Im.js} +2 -2
  56. package/dist/gateway/static/root/assets/{page-tabs-DU85jn98.js → page-tabs-BmUWVWO9.js} +1 -1
  57. package/dist/gateway/static/root/assets/{preview-open-alternatives-CtchTuRQ.js → preview-open-alternatives-a76A8p5R.js} +1 -1
  58. package/dist/gateway/static/root/assets/{product-open-page-DFT6Fx6y.js → product-open-page-BWhYrt-U.js} +1 -1
  59. package/dist/gateway/static/root/assets/{project-detail-page-Ceu0NoC1.js → project-detail-page-md0ANZ-f.js} +1 -1
  60. package/dist/gateway/static/root/assets/{projects-page-CdpqOmO8.js → projects-page-D1MNrLcy.js} +1 -1
  61. package/dist/gateway/static/root/assets/{remote-access-hub-D1NKLyiR.js → remote-access-hub-GTuiqlJO.js} +1 -1
  62. package/dist/gateway/static/root/assets/{save-bar-controls-CmcrHpMc.js → save-bar-controls-_ZMLDSUw.js} +1 -1
  63. package/dist/gateway/static/root/assets/{schedule-field-DmIkP6JV.js → schedule-field-D4ErObRc.js} +1 -1
  64. package/dist/gateway/static/root/assets/{schema-form-Dkk9blN0.js → schema-form-PqNSGlcH.js} +1 -1
  65. package/dist/gateway/static/root/assets/{sessions-page-CMj_JaOk.js → sessions-page-Ct0MVjNv.js} +1 -1
  66. package/dist/gateway/static/root/assets/settings-advanced-gate-QOUph2Oq.js +1 -0
  67. package/dist/gateway/static/root/assets/{settings-form-section-CuQzWvg5.js → settings-form-section-BCKaoLhJ.js} +1 -1
  68. package/dist/gateway/static/root/assets/{settings-loading-skeleton-C5FNbyej.js → settings-loading-skeleton-Dn95fked.js} +1 -1
  69. package/dist/gateway/static/root/assets/{settings-page-a-A2yTGE.js → settings-page-C8MPUl2Q.js} +1 -1
  70. package/dist/gateway/static/root/assets/{settings-page-layout-MuXpamWD.js → settings-page-layout-Bigc8-GR.js} +1 -1
  71. package/dist/gateway/static/root/assets/{setup-status-panel-BUdh8FKY.js → setup-status-panel-DFTcAHsd.js} +1 -1
  72. package/dist/gateway/static/root/assets/{share-preview-page-BfzzmTgg.js → share-preview-page-xAjqaV4y.js} +1 -1
  73. package/dist/gateway/static/root/assets/{shares-settings-8a9qnLF8.js → shares-settings-D2uvC_aM.js} +1 -1
  74. package/dist/gateway/static/root/assets/skill-reload-api-DYtfQXB-.js +1 -0
  75. package/dist/gateway/static/root/assets/{skills-page-CmHj-CKY.js → skills-page-6dr3RRVW.js} +1 -1
  76. package/dist/gateway/static/root/assets/{system-settings-panel-CyhRkWMi.js → system-settings-panel-BJJQulg9.js} +1 -1
  77. package/dist/gateway/static/root/assets/{theme-store-D37XLD_C.js → theme-store-H9T49Iar.js} +1 -1
  78. package/dist/gateway/static/root/assets/{time-picker-WEPp3HrB.js → time-picker-DsuPUbRE.js} +1 -1
  79. package/dist/gateway/static/root/assets/{typed-models-lib-Bi1uoxFR.js → typed-models-lib-Djk3F3EL.js} +1 -1
  80. package/dist/gateway/static/root/assets/{use-channel-catalog-DBh0dOC9.js → use-channel-catalog-UACJTyH0.js} +1 -1
  81. package/dist/gateway/static/root/assets/{user-context-page-BObEyiim.js → user-context-page-BC585kHK.js} +1 -1
  82. package/dist/gateway/static/root/assets/{voice-api-key-field-Gvgodhec.js → voice-api-key-field-CvocOFat.js} +1 -1
  83. package/dist/gateway/static/root/assets/{work-discovery-page-BHnjvGH1.js → work-discovery-page-DiMn6nrT.js} +1 -1
  84. package/dist/gateway/static/root/assets/{work-item-detail-page-CQ8ybkxx.js → work-item-detail-page-CFBlQbPy.js} +1 -1
  85. package/dist/gateway/static/root/assets/{work-page-D_U_En23.js → work-page-CLJLKqsf.js} +1 -1
  86. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-B-Sk6gFn.js → workflow-run-setup-panel-DbQwWPjv.js} +1 -1
  87. package/dist/gateway/static/root/assets/{workflows-page-DT0pzfkz.js → workflows-page-BJOhlABF.js} +1 -1
  88. package/dist/gateway/static/root/index.html +8 -8
  89. package/dist/package.js +1 -1
  90. package/dist/src/agent/agent-manager.js +12 -5
  91. package/dist/src/agent/image/generation/custom-openai-images-provider.d.ts +7 -0
  92. package/dist/src/agent/image/generation/custom-openai-images-provider.js +81 -0
  93. package/dist/src/agent/image/generation/{openai-compatible-image-provider.d.ts → openai-images-provider.d.ts} +10 -31
  94. package/dist/src/agent/image/generation/{openai-compatible-image-provider.js → openai-images-provider.js} +36 -23
  95. package/dist/src/agent/image/generation/provider-registry.d.ts +2 -0
  96. package/dist/src/agent/image/generation/provider-registry.js +50 -4
  97. package/dist/src/agent/image/generation/providers/openai.js +2 -2
  98. package/dist/src/agent/image/generation/runtime.js +5 -1
  99. package/dist/src/agent/image/generation/types.d.ts +11 -0
  100. package/dist/src/agent/models/manager.d.ts +5 -0
  101. package/dist/src/agent/models/manager.js +8 -0
  102. package/dist/src/agent/prompt/sections/messaging-runtime.js +1 -1
  103. package/dist/src/agent/prompt/sections/tooling.js +1 -1
  104. package/dist/src/agent/tools/communication.js +25 -7
  105. package/dist/src/channels/plugin-registry.js +6 -1
  106. package/dist/src/cli/commands/channels.js +1 -1
  107. package/dist/src/cli/commands/connectors.js +1 -1
  108. package/dist/src/cli/commands/doctor/checks/image-providers.d.ts +2 -0
  109. package/dist/src/cli/commands/doctor/checks/image-providers.js +68 -0
  110. package/dist/src/cli/commands/doctor/flow.js +2 -0
  111. package/dist/src/cli/commands/extension-marketplace.js +1 -1
  112. package/dist/src/cli/commands/models.js +1 -1
  113. package/dist/src/cli/commands/onboard.js +1 -1
  114. package/dist/src/config/index.js +2 -2
  115. package/dist/src/config/models-json.d.ts +325 -0
  116. package/dist/src/config/models-json.js +139 -4
  117. package/dist/src/extensions/sdk/index.js +1 -1
  118. package/dist/src/gateway/custom-image-providers.d.ts +89 -0
  119. package/dist/src/gateway/custom-image-providers.js +59 -0
  120. package/dist/src/gateway/hono/routes/models.js +182 -1
  121. package/dist/src/gateway/hono/routes/workflows.js +1 -1
  122. package/dist/src/gateway/image-generation-setup.js +7 -1
  123. package/dist/src/gateway/service/config-coordinator.js +2 -0
  124. package/dist/src/gateway/service.js +2 -2
  125. package/dist/src/media-shared/http/index.d.ts +1 -1
  126. package/dist/src/media-shared/http/index.js +2 -2
  127. package/dist/src/media-shared/http/provider-http.d.ts +2 -0
  128. package/dist/src/media-shared/http/provider-http.js +43 -4
  129. package/dist/src/providers/model-reference-auditor.d.ts +1 -0
  130. package/dist/src/providers/model-reference-auditor.js +23 -13
  131. package/dist/src/session/patch-metadata.d.ts +2 -0
  132. package/dist/src/session/patch-metadata.js +2 -0
  133. package/dist/src/tui/backends/embedded-backend.d.ts +2 -0
  134. package/dist/src/tui/backends/embedded-backend.js +46 -9
  135. package/dist/src/tui/backends/gateway-sse-backend.js +10 -3
  136. package/dist/src/tui/tui-backend.d.ts +2 -0
  137. package/dist/src/tui/tui-empty-session-cleanup.d.ts +11 -0
  138. package/dist/src/tui/tui-empty-session-cleanup.js +37 -0
  139. package/dist/src/tui/tui.js +43 -16
  140. package/dist/src/workflows/service/workflow-run-service.js +1 -1
  141. package/package.json +2 -2
  142. package/dist/gateway/static/root/assets/agents-DZ69tC4K.js +0 -2
  143. package/dist/gateway/static/root/assets/capabilities-settings-panel-LOQyfdsM.js +0 -2
  144. package/dist/gateway/static/root/assets/capability-presets-api-XL4h7dCK.js +0 -1
  145. package/dist/gateway/static/root/assets/capability-presets-settings-panel-KPJFkt1h.js +0 -2
  146. package/dist/gateway/static/root/assets/channels-status-swr-DZZq0upi.js +0 -1
  147. package/dist/gateway/static/root/assets/index-BXtJvaQl.css +0 -1
  148. package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-fhssr2-J.js +0 -1
  149. package/dist/gateway/static/root/assets/locale-store-C_ax27uc.js +0 -1
  150. package/dist/gateway/static/root/assets/settings-advanced-gate-DR9Q2ZyF.js +0 -1
  151. package/dist/gateway/static/root/assets/skill-reload-api-da5gLCer.js +0 -1
@@ -48,6 +48,55 @@ function validateModelsConfig(config) {
48
48
  const hasModels = providerConfig.models && providerConfig.models.length > 0;
49
49
  const hasModelOverrides = providerConfig.modelOverrides && Object.keys(providerConfig.modelOverrides).length > 0;
50
50
  const hasBaseUrl = !!providerConfig.baseUrl;
51
+ const imageGeneration = providerConfig.imageGeneration;
52
+ if (imageGeneration) {
53
+ if (RESERVED_PROVIDER_IDS.has(providerName)) errors.push({
54
+ path: `providers.${providerName}.imageGeneration`,
55
+ message: "Custom image generation cannot override a built-in provider ID",
56
+ severity: "error"
57
+ });
58
+ if (!hasBaseUrl) errors.push({
59
+ path: `providers.${providerName}.baseUrl`,
60
+ message: "baseUrl is required when defining image generation models",
61
+ severity: "error"
62
+ });
63
+ const credentialHeaderNames = new Set([
64
+ "authorization",
65
+ "proxy-authorization",
66
+ "cookie",
67
+ ...imageGeneration.auth.type === "header" ? [imageGeneration.auth.headerName.toLowerCase()] : []
68
+ ]);
69
+ for (const headerName of Object.keys(providerConfig.headers ?? {})) if (credentialHeaderNames.has(headerName.toLowerCase())) errors.push({
70
+ path: `providers.${providerName}.headers.${headerName}`,
71
+ message: "Credential headers must be stored in the credential store, not models.json",
72
+ severity: "error"
73
+ });
74
+ for (const [headerName, headerValue] of Object.entries(providerConfig.headers ?? {})) if (!isHttpHeaderValue(headerValue)) errors.push({
75
+ path: `providers.${providerName}.headers.${headerName}`,
76
+ message: "Header values must contain only HTTP ByteString characters and no control characters",
77
+ severity: "error"
78
+ });
79
+ const imageModelIds = /* @__PURE__ */ new Set();
80
+ for (let i = 0; i < imageGeneration.models.length; i++) {
81
+ const model = imageGeneration.models[i];
82
+ if (model.id.includes("/")) errors.push({
83
+ path: `providers.${providerName}.imageGeneration.models[${i}].id`,
84
+ message: "Image model ID cannot contain \"/\" character",
85
+ severity: "error"
86
+ });
87
+ if (imageModelIds.has(model.id)) errors.push({
88
+ path: `providers.${providerName}.imageGeneration.models[${i}].id`,
89
+ message: `Duplicate image model ID "${model.id}"`,
90
+ severity: "error"
91
+ });
92
+ imageModelIds.add(model.id);
93
+ }
94
+ if (!imageModelIds.has(imageGeneration.defaultModel)) errors.push({
95
+ path: `providers.${providerName}.imageGeneration.defaultModel`,
96
+ message: "defaultModel must reference a configured image generation model",
97
+ severity: "error"
98
+ });
99
+ }
51
100
  if (hasModels) {
52
101
  if (!hasBaseUrl) errors.push({
53
102
  path: `providers.${providerName}.baseUrl`,
@@ -55,7 +104,7 @@ function validateModelsConfig(config) {
55
104
  severity: "error"
56
105
  });
57
106
  }
58
- if (!hasModels && !hasBaseUrl && !hasModelOverrides && !providerConfig.apiKey) errors.push({
107
+ if (!hasModels && !hasBaseUrl && !hasModelOverrides && !providerConfig.apiKey && !imageGeneration) errors.push({
59
108
  path: `providers.${providerName}`,
60
109
  message: "Must specify baseUrl, modelOverrides, models, or apiKey",
61
110
  severity: "error"
@@ -99,6 +148,13 @@ function validateModelsConfig(config) {
99
148
  errors
100
149
  };
101
150
  }
151
+ function isHttpHeaderValue(value) {
152
+ for (let index = 0; index < value.length; index++) {
153
+ const code = value.charCodeAt(index);
154
+ if (code > 255 || code < 32 && code !== 9 || code === 127) return false;
155
+ }
156
+ return true;
157
+ }
102
158
  function getDefaultModelValues() {
103
159
  return {
104
160
  input: ["text"],
@@ -158,7 +214,7 @@ function saveModelsJson(path, config) {
158
214
  function modelsJsonExists(path = resolveModelsJsonPath()) {
159
215
  return existsSync(path);
160
216
  }
161
- var OpenRouterRoutingSchema, VercelGatewayRoutingSchema, OpenAICompletionsCompatSchema, OpenAIResponsesCompatSchema, OpenAICompatSchema, CustomModelSchema, ModelOverrideSchema, ProviderConfigSchema, ModelsJsonSchema, PROVIDER_ID_REGEX, RESERVED_PROVIDER_IDS;
217
+ var OpenRouterRoutingSchema, VercelGatewayRoutingSchema, OpenAICompletionsCompatSchema, OpenAIResponsesCompatSchema, OpenAICompatSchema, ImageGenerationResolutionSchema, ImageGenerationQualitySchema, ImageGenerationOutputFormatSchema, ImageGenerationBackgroundSchema, ImageGenerationCapabilitiesSchema, ImageGenerationModelSchema, ImageGenerationProviderSchema, CustomModelSchema, ModelOverrideSchema, ProviderConfigSchema, ModelsJsonSchema, PROVIDER_ID_REGEX, RESERVED_PROVIDER_IDS;
162
218
  var init_models_json = __esmMin((() => {
163
219
  init_write_file_atomic();
164
220
  init_paths();
@@ -191,6 +247,82 @@ var init_models_json = __esmMin((() => {
191
247
  });
192
248
  OpenAIResponsesCompatSchema = z.object({});
193
249
  OpenAICompatSchema = z.union([OpenAICompletionsCompatSchema, OpenAIResponsesCompatSchema]);
250
+ ImageGenerationResolutionSchema = z.enum([
251
+ "1K",
252
+ "2K",
253
+ "4K"
254
+ ]);
255
+ ImageGenerationQualitySchema = z.enum([
256
+ "low",
257
+ "medium",
258
+ "high",
259
+ "auto"
260
+ ]);
261
+ ImageGenerationOutputFormatSchema = z.enum([
262
+ "png",
263
+ "jpeg",
264
+ "webp"
265
+ ]);
266
+ ImageGenerationBackgroundSchema = z.enum([
267
+ "transparent",
268
+ "opaque",
269
+ "auto"
270
+ ]);
271
+ ImageGenerationCapabilitiesSchema = z.object({
272
+ generate: z.object({
273
+ maxCount: z.number().int().positive().optional(),
274
+ supportsSize: z.boolean().optional(),
275
+ supportsAspectRatio: z.boolean().optional(),
276
+ supportsResolution: z.boolean().optional()
277
+ }).strict().optional(),
278
+ edit: z.object({
279
+ enabled: z.boolean(),
280
+ maxInputImages: z.number().int().positive().optional(),
281
+ supportsSize: z.boolean().optional(),
282
+ supportsAspectRatio: z.boolean().optional()
283
+ }).strict().optional(),
284
+ geometry: z.object({
285
+ sizes: z.array(z.string().min(1)).optional(),
286
+ aspectRatios: z.array(z.string().min(1)).optional(),
287
+ resolutions: z.array(ImageGenerationResolutionSchema).optional()
288
+ }).strict().optional(),
289
+ output: z.object({
290
+ qualities: z.array(ImageGenerationQualitySchema).optional(),
291
+ formats: z.array(ImageGenerationOutputFormatSchema).optional(),
292
+ backgrounds: z.array(ImageGenerationBackgroundSchema).optional()
293
+ }).strict().optional()
294
+ }).strict();
295
+ ImageGenerationModelSchema = z.object({
296
+ id: z.string().min(1),
297
+ name: z.string().min(1).optional(),
298
+ capabilities: ImageGenerationCapabilitiesSchema,
299
+ defaults: z.object({
300
+ count: z.number().int().positive().optional(),
301
+ size: z.string().min(1).optional(),
302
+ outputFormat: ImageGenerationOutputFormatSchema.optional()
303
+ }).strict().optional()
304
+ }).strict();
305
+ ImageGenerationProviderSchema = z.object({
306
+ api: z.literal("openai-images"),
307
+ name: z.string().min(1),
308
+ documentationUrl: z.string().url().optional(),
309
+ apiKeyUrl: z.string().url().optional(),
310
+ defaultModel: z.string().min(1),
311
+ auth: z.discriminatedUnion("type", [
312
+ z.object({ type: z.literal("bearer") }).strict(),
313
+ z.object({
314
+ type: z.literal("header"),
315
+ headerName: z.string().regex(/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/)
316
+ }).strict(),
317
+ z.object({ type: z.literal("none") }).strict()
318
+ ]),
319
+ paths: z.object({
320
+ generations: z.string().startsWith("/").optional(),
321
+ edits: z.string().startsWith("/").optional()
322
+ }).strict().optional(),
323
+ network: z.object({ allowedHosts: z.array(z.string().min(1).refine((host) => host === host.trim() && !host.includes("://") && !/[/?#]/.test(host), "Use an exact hostname or IP without scheme, port, or path")).min(1) }).strict().optional(),
324
+ models: z.array(ImageGenerationModelSchema).min(1)
325
+ }).strict();
194
326
  CustomModelSchema = z.object({
195
327
  id: z.string().min(1),
196
328
  name: z.string().min(1).optional(),
@@ -251,7 +383,8 @@ var init_models_json = __esmMin((() => {
251
383
  authHeader: z.boolean().optional(),
252
384
  models: z.array(CustomModelSchema).optional(),
253
385
  modelOverrides: z.record(z.string(), ModelOverrideSchema).optional(),
254
- modelDiscovery: z.object({ enabled: z.boolean() }).strict().optional()
386
+ modelDiscovery: z.object({ enabled: z.boolean() }).strict().optional(),
387
+ imageGeneration: ImageGenerationProviderSchema.optional()
255
388
  });
256
389
  ModelsJsonSchema = z.object({ providers: z.record(z.string(), ProviderConfigSchema) });
257
390
  PROVIDER_ID_REGEX = /^[a-z0-9]([a-z0-9-_]*[a-z0-9])?$/;
@@ -262,7 +395,9 @@ var init_models_json = __esmMin((() => {
262
395
  "cerebras",
263
396
  "cloudflare-ai-gateway",
264
397
  "cloudflare-workers-ai",
398
+ "dashscope",
265
399
  "deepseek",
400
+ "fal",
266
401
  "fireworks",
267
402
  "github-copilot",
268
403
  "google",
@@ -294,4 +429,4 @@ var init_models_json = __esmMin((() => {
294
429
  }));
295
430
  //#endregion
296
431
  init_models_json();
297
- export { CustomModelSchema, ModelOverrideSchema, ModelsJsonSchema, OpenAICompatSchema, OpenAICompletionsCompatSchema, OpenAIResponsesCompatSchema, OpenRouterRoutingSchema, ProviderConfigSchema, VercelGatewayRoutingSchema, getDefaultModelValues, resolveModelsJsonPath as getModelsJsonPath, init_models_json, loadModelsJson, modelsJsonExists, saveModelsJson, validateModelsConfig };
432
+ export { CustomModelSchema, ImageGenerationCapabilitiesSchema, ImageGenerationModelSchema, ImageGenerationProviderSchema, ModelOverrideSchema, ModelsJsonSchema, OpenAICompatSchema, OpenAICompletionsCompatSchema, OpenAIResponsesCompatSchema, OpenRouterRoutingSchema, ProviderConfigSchema, VercelGatewayRoutingSchema, getDefaultModelValues, resolveModelsJsonPath as getModelsJsonPath, init_models_json, loadModelsJson, modelsJsonExists, saveModelsJson, validateModelsConfig };
@@ -7,8 +7,8 @@ import { SlotRegistry, getSlotRegistry, registerSlotType } from "../slots.js";
7
7
  import { ExtensionDiagnostics, ExtensionLoaderCacheImpl, getExtensionCache, getExtensionDiagnostics } from "../diagnostics.js";
8
8
  import "../loader.js";
9
9
  import { ExtensionHookRunner, createHookContext, isHookEvent } from "../hooks.js";
10
- import { registerExtensionCliProgram } from "./channel-helpers.js";
11
10
  import { defineChannelPluginEntry } from "./channel-entry.js";
11
+ import { registerExtensionCliProgram } from "./channel-helpers.js";
12
12
  import { lazyFunction, lazyModule } from "./lazy.js";
13
13
  import { createMockExtensionApi, createTestGateway } from "./testing.js";
14
14
  //#region src/extensions/sdk/index.ts
@@ -0,0 +1,89 @@
1
+ import { z } from 'zod';
2
+ import { type ModelsJsonConfig } from '../config/models-json.js';
3
+ export declare const CustomImageProviderInputSchema: z.ZodObject<{
4
+ baseUrl: z.ZodString;
5
+ headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
6
+ imageGeneration: z.ZodObject<{
7
+ api: z.ZodLiteral<"openai-images">;
8
+ name: z.ZodString;
9
+ documentationUrl: z.ZodOptional<z.ZodString>;
10
+ apiKeyUrl: z.ZodOptional<z.ZodString>;
11
+ defaultModel: z.ZodString;
12
+ auth: z.ZodDiscriminatedUnion<[z.ZodObject<{
13
+ type: z.ZodLiteral<"bearer">;
14
+ }, z.core.$strict>, z.ZodObject<{
15
+ type: z.ZodLiteral<"header">;
16
+ headerName: z.ZodString;
17
+ }, z.core.$strict>, z.ZodObject<{
18
+ type: z.ZodLiteral<"none">;
19
+ }, z.core.$strict>], "type">;
20
+ paths: z.ZodOptional<z.ZodObject<{
21
+ generations: z.ZodOptional<z.ZodString>;
22
+ edits: z.ZodOptional<z.ZodString>;
23
+ }, z.core.$strict>>;
24
+ network: z.ZodOptional<z.ZodObject<{
25
+ allowedHosts: z.ZodArray<z.ZodString>;
26
+ }, z.core.$strict>>;
27
+ models: z.ZodArray<z.ZodObject<{
28
+ id: z.ZodString;
29
+ name: z.ZodOptional<z.ZodString>;
30
+ capabilities: z.ZodObject<{
31
+ generate: z.ZodOptional<z.ZodObject<{
32
+ maxCount: z.ZodOptional<z.ZodNumber>;
33
+ supportsSize: z.ZodOptional<z.ZodBoolean>;
34
+ supportsAspectRatio: z.ZodOptional<z.ZodBoolean>;
35
+ supportsResolution: z.ZodOptional<z.ZodBoolean>;
36
+ }, z.core.$strict>>;
37
+ edit: z.ZodOptional<z.ZodObject<{
38
+ enabled: z.ZodBoolean;
39
+ maxInputImages: z.ZodOptional<z.ZodNumber>;
40
+ supportsSize: z.ZodOptional<z.ZodBoolean>;
41
+ supportsAspectRatio: z.ZodOptional<z.ZodBoolean>;
42
+ }, z.core.$strict>>;
43
+ geometry: z.ZodOptional<z.ZodObject<{
44
+ sizes: z.ZodOptional<z.ZodArray<z.ZodString>>;
45
+ aspectRatios: z.ZodOptional<z.ZodArray<z.ZodString>>;
46
+ resolutions: z.ZodOptional<z.ZodArray<z.ZodEnum<{
47
+ "1K": "1K";
48
+ "2K": "2K";
49
+ "4K": "4K";
50
+ }>>>;
51
+ }, z.core.$strict>>;
52
+ output: z.ZodOptional<z.ZodObject<{
53
+ qualities: z.ZodOptional<z.ZodArray<z.ZodEnum<{
54
+ low: "low";
55
+ high: "high";
56
+ auto: "auto";
57
+ medium: "medium";
58
+ }>>>;
59
+ formats: z.ZodOptional<z.ZodArray<z.ZodEnum<{
60
+ png: "png";
61
+ jpeg: "jpeg";
62
+ webp: "webp";
63
+ }>>>;
64
+ backgrounds: z.ZodOptional<z.ZodArray<z.ZodEnum<{
65
+ auto: "auto";
66
+ transparent: "transparent";
67
+ opaque: "opaque";
68
+ }>>>;
69
+ }, z.core.$strict>>;
70
+ }, z.core.$strict>;
71
+ defaults: z.ZodOptional<z.ZodObject<{
72
+ count: z.ZodOptional<z.ZodNumber>;
73
+ size: z.ZodOptional<z.ZodString>;
74
+ outputFormat: z.ZodOptional<z.ZodEnum<{
75
+ png: "png";
76
+ jpeg: "jpeg";
77
+ webp: "webp";
78
+ }>>;
79
+ }, z.core.$strict>>;
80
+ }, z.core.$strict>>;
81
+ }, z.core.$strict>;
82
+ }, z.core.$strict>;
83
+ export type CustomImageProviderInput = z.infer<typeof CustomImageProviderInputSchema>;
84
+ export interface CustomImageProviderRecord extends CustomImageProviderInput {
85
+ providerId: string;
86
+ }
87
+ export declare function listCustomImageProviders(config: ModelsJsonConfig): CustomImageProviderRecord[];
88
+ export declare function upsertCustomImageProvider(config: ModelsJsonConfig, providerIdRaw: string, input: unknown): ModelsJsonConfig;
89
+ export declare function deleteCustomImageProvider(config: ModelsJsonConfig, providerIdRaw: string): ModelsJsonConfig;
@@ -0,0 +1,59 @@
1
+ import { ImageGenerationProviderSchema, init_models_json, validateModelsConfig } from "../config/models-json.js";
2
+ import { z } from "zod";
3
+ //#region src/gateway/custom-image-providers.ts
4
+ init_models_json();
5
+ const PROVIDER_ID_REGEX = /^[a-z0-9]([a-z0-9-_]*[a-z0-9])?$/;
6
+ const CustomImageProviderInputSchema = z.object({
7
+ baseUrl: z.string().url(),
8
+ headers: z.record(z.string(), z.string()).optional(),
9
+ imageGeneration: ImageGenerationProviderSchema
10
+ }).strict();
11
+ function listCustomImageProviders(config) {
12
+ return Object.entries(config.providers).flatMap(([providerId, provider]) => {
13
+ if (!provider.imageGeneration) return [];
14
+ return [{
15
+ providerId,
16
+ baseUrl: provider.baseUrl,
17
+ ...provider.headers ? { headers: { ...provider.headers } } : {},
18
+ imageGeneration: structuredClone(provider.imageGeneration)
19
+ }];
20
+ });
21
+ }
22
+ function upsertCustomImageProvider(config, providerIdRaw, input) {
23
+ const providerId = normalizeProviderId(providerIdRaw);
24
+ const parsed = CustomImageProviderInputSchema.safeParse(input);
25
+ if (!parsed.success) throw new Error(parsed.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; "));
26
+ const { headers: _currentHeaders, ...currentWithoutHeaders } = config.providers[providerId] ?? {};
27
+ const next = structuredClone(config);
28
+ next.providers[providerId] = {
29
+ ...currentWithoutHeaders,
30
+ baseUrl: parsed.data.baseUrl,
31
+ ...parsed.data.headers ? { headers: parsed.data.headers } : {},
32
+ imageGeneration: parsed.data.imageGeneration
33
+ };
34
+ assertValid(next);
35
+ return next;
36
+ }
37
+ function deleteCustomImageProvider(config, providerIdRaw) {
38
+ const providerId = normalizeProviderId(providerIdRaw);
39
+ const current = config.providers[providerId];
40
+ if (!current?.imageGeneration) throw new Error(`Custom image provider not found: ${providerId}`);
41
+ const next = structuredClone(config);
42
+ if (Boolean(current.api || current.apiKey || current.authHeader !== void 0 || current.models?.length || current.modelOverrides && Object.keys(current.modelOverrides).length > 0 || current.modelDiscovery)) {
43
+ const { imageGeneration: _removed, ...remaining } = current;
44
+ next.providers[providerId] = remaining;
45
+ } else delete next.providers[providerId];
46
+ assertValid(next);
47
+ return next;
48
+ }
49
+ function normalizeProviderId(value) {
50
+ const providerId = value.trim();
51
+ if (!PROVIDER_ID_REGEX.test(providerId)) throw new Error("Provider ID must start/end with alphanumeric and contain only lowercase letters, numbers, hyphens, and underscores");
52
+ return providerId;
53
+ }
54
+ function assertValid(config) {
55
+ const validation = validateModelsConfig(config);
56
+ if (!validation.valid) throw new Error(validation.errors.filter((entry) => entry.severity === "error").map((entry) => `${entry.path}: ${entry.message}`).join("; "));
57
+ }
58
+ //#endregion
59
+ export { CustomImageProviderInputSchema, deleteCustomImageProvider, listCustomImageProviders, upsertCustomImageProvider };
@@ -7,7 +7,9 @@ import { CredentialResolver, init_credentials } from "../../../auth/credentials.
7
7
  import "../../../providers/constants.js";
8
8
  import { getProviderRegistry, init_plugin_registry } from "../../../providers/plugin-registry.js";
9
9
  import { PROVIDER_META, getAllModels, getAllProviders, getAvailableModels, getProviderAuthState, init_providers, isProviderConfigured } from "../../../providers/index.js";
10
+ import { getImageGenerationProvider, reloadImageGenerationProviders } from "../../../agent/image/generation/provider-registry.js";
10
11
  import { SessionConfigStore } from "../../../session/config-store.js";
12
+ import { deleteCustomImageProvider, listCustomImageProviders, upsertCustomImageProvider } from "../../custom-image-providers.js";
11
13
  import { getDomesticProviderPreset, getDomesticProviderPresetIds } from "../../../providers/domestic-presets.js";
12
14
  import { getOnboardingFeaturedProviders, getProviderHint, getRecommendedModelsForProvider, isRecommendedModel, sortModelsForPicker, sortProvidersForPicker } from "../../../providers/presentation.js";
13
15
  import { getAgentImageGenerationConfig, getImageGenerationCatalog, prepareImageGenerationSetup, verifyImageGenerationCredential } from "../../image-generation-setup.js";
@@ -119,6 +121,7 @@ function registerModelsRoutes(authenticated, deps) {
119
121
  }, 400);
120
122
  const registry = getModelRegistry();
121
123
  registry.refresh();
124
+ reloadImageGenerationProviders(config);
122
125
  service.emit("models-json.updated", { modelCount: registry.getAll().length });
123
126
  return c.json({
124
127
  ok: true,
@@ -131,6 +134,7 @@ function registerModelsRoutes(authenticated, deps) {
131
134
  authenticated.post("/api/models-json/reload", async (c) => {
132
135
  const registry = getModelRegistry();
133
136
  registry.refresh();
137
+ reloadImageGenerationProviders();
134
138
  const error = registry.getError();
135
139
  const models = registry.getAll();
136
140
  service.emit("models-json.reloaded", {
@@ -239,6 +243,182 @@ function registerModelsRoutes(authenticated, deps) {
239
243
  payload: { providers: getImageGenerationCatalog(service.currentConfig) }
240
244
  });
241
245
  });
246
+ authenticated.get("/api/image-generation/custom-providers", (c) => {
247
+ const loaded = loadModelsJson(resolveModelsJsonPath());
248
+ if (loaded.error) return c.json({
249
+ ok: false,
250
+ error: { message: loaded.error }
251
+ }, 409);
252
+ return c.json({
253
+ ok: true,
254
+ payload: { providers: listCustomImageProviders(loaded.config) }
255
+ });
256
+ });
257
+ authenticated.put("/api/image-generation/custom-providers/:providerId", strictRateLimitMiddleware, async (c) => {
258
+ const loaded = loadModelsJson(resolveModelsJsonPath());
259
+ if (loaded.error) return c.json({
260
+ ok: false,
261
+ error: { message: loaded.error }
262
+ }, 409);
263
+ try {
264
+ const body = await c.req.json();
265
+ const next = upsertCustomImageProvider(loaded.config, c.req.param("providerId"), body);
266
+ const saved = saveModelsJson(resolveModelsJsonPath(), next);
267
+ if (!saved.success) return c.json({
268
+ ok: false,
269
+ error: { message: saved.error }
270
+ }, 400);
271
+ getModelRegistry().refresh();
272
+ reloadImageGenerationProviders(next);
273
+ service.emit("models-json.updated", { modelCount: getModelRegistry().getAll().length });
274
+ return c.json({
275
+ ok: true,
276
+ payload: {
277
+ provider: listCustomImageProviders(next).find((entry) => entry.providerId === c.req.param("providerId")),
278
+ providers: getImageGenerationCatalog(service.currentConfig)
279
+ }
280
+ });
281
+ } catch (error) {
282
+ return c.json({
283
+ ok: false,
284
+ error: { message: error instanceof Error ? error.message : String(error) }
285
+ }, 400);
286
+ }
287
+ });
288
+ authenticated.delete("/api/image-generation/custom-providers/:providerId", strictRateLimitMiddleware, (c) => {
289
+ const loaded = loadModelsJson(resolveModelsJsonPath());
290
+ if (loaded.error) return c.json({
291
+ ok: false,
292
+ error: { message: loaded.error }
293
+ }, 409);
294
+ try {
295
+ const next = deleteCustomImageProvider(loaded.config, c.req.param("providerId"));
296
+ const saved = saveModelsJson(resolveModelsJsonPath(), next);
297
+ if (!saved.success) return c.json({
298
+ ok: false,
299
+ error: { message: saved.error }
300
+ }, 400);
301
+ getModelRegistry().refresh();
302
+ reloadImageGenerationProviders(next);
303
+ service.emit("models-json.updated", { modelCount: getModelRegistry().getAll().length });
304
+ return c.json({
305
+ ok: true,
306
+ payload: {
307
+ deleted: c.req.param("providerId"),
308
+ providers: getImageGenerationCatalog(service.currentConfig)
309
+ }
310
+ });
311
+ } catch (error) {
312
+ return c.json({
313
+ ok: false,
314
+ error: { message: error instanceof Error ? error.message : String(error) }
315
+ }, 400);
316
+ }
317
+ });
318
+ authenticated.put("/api/image-generation/providers/:providerId/credential", strictRateLimitMiddleware, async (c) => {
319
+ const providerId = c.req.param("providerId").trim();
320
+ const provider = getImageGenerationProvider(providerId);
321
+ if (!provider) return c.json({
322
+ ok: false,
323
+ error: { message: `Unknown image provider: ${providerId}` }
324
+ }, 404);
325
+ if (provider.credentialMode === "none") return c.json({
326
+ ok: false,
327
+ error: { message: "This provider does not use an API key" }
328
+ }, 400);
329
+ const body = await c.req.json().catch(() => null);
330
+ const apiKey = typeof body?.apiKey === "string" ? body.apiKey.trim() : "";
331
+ if (!apiKey) return c.json({
332
+ ok: false,
333
+ error: { message: "API key is required" }
334
+ }, 400);
335
+ await new CredentialResolver().saveApiKey(providerId, apiKey, { profileName: "default" });
336
+ return c.json({
337
+ ok: true,
338
+ payload: {
339
+ providerId,
340
+ saved: true
341
+ }
342
+ });
343
+ });
344
+ authenticated.post("/api/image-generation/providers/:providerId/reveal-api-key", strictRateLimitMiddleware, async (c) => {
345
+ const providerId = c.req.param("providerId").trim();
346
+ if (!getImageGenerationProvider(providerId)) return c.json({
347
+ ok: false,
348
+ error: { message: `Unknown image provider: ${providerId}` }
349
+ }, 404);
350
+ const apiKey = await new CredentialResolver().revealGatewayStoredApiKey(providerId);
351
+ return c.json({
352
+ ok: true,
353
+ payload: {
354
+ providerId,
355
+ apiKey: apiKey ?? null,
356
+ source: apiKey ? "credential" : "none"
357
+ }
358
+ });
359
+ });
360
+ authenticated.delete("/api/image-generation/providers/:providerId/credential", strictRateLimitMiddleware, async (c) => {
361
+ const providerId = c.req.param("providerId").trim();
362
+ if (!getImageGenerationProvider(providerId)) return c.json({
363
+ ok: false,
364
+ error: { message: `Unknown image provider: ${providerId}` }
365
+ }, 404);
366
+ await new CredentialResolver().deleteProviderCredential(providerId);
367
+ return c.json({
368
+ ok: true,
369
+ payload: {
370
+ providerId,
371
+ deleted: true
372
+ }
373
+ });
374
+ });
375
+ authenticated.post("/api/image-generation/providers/:providerId/test", strictRateLimitMiddleware, async (c) => {
376
+ const providerId = c.req.param("providerId").trim();
377
+ const provider = getImageGenerationProvider(providerId);
378
+ if (!provider) return c.json({
379
+ ok: false,
380
+ error: { message: `Unknown image provider: ${providerId}` }
381
+ }, 404);
382
+ if (!provider.isConfigured({ cfg: service.currentConfig })) return c.json({
383
+ ok: false,
384
+ error: { message: "Provider credential is not configured" }
385
+ }, 400);
386
+ const body = await c.req.json().catch(() => ({}));
387
+ const modelId = typeof body.modelId === "string" && body.modelId.trim() ? body.modelId.trim() : provider.defaultModel;
388
+ if (!provider.models.includes(modelId)) return c.json({
389
+ ok: false,
390
+ error: { message: `Unknown ${providerId} image model: ${modelId}` }
391
+ }, 400);
392
+ const prompt = typeof body.prompt === "string" && body.prompt.trim() ? body.prompt.trim() : "A simple blue circle centered on a white background.";
393
+ try {
394
+ const result = await provider.generateImage({
395
+ provider: providerId,
396
+ model: modelId,
397
+ prompt,
398
+ count: 1,
399
+ cfg: service.currentConfig,
400
+ timeoutMs: 12e4
401
+ });
402
+ return c.json({
403
+ ok: true,
404
+ payload: {
405
+ providerId,
406
+ modelId: result.model ?? modelId,
407
+ images: result.images.map((image) => ({
408
+ mimeType: image.mimeType,
409
+ fileName: image.fileName,
410
+ dataUrl: `data:${image.mimeType};base64,${image.buffer.toString("base64")}`,
411
+ ...image.revisedPrompt ? { revisedPrompt: image.revisedPrompt } : {}
412
+ }))
413
+ }
414
+ });
415
+ } catch (error) {
416
+ return c.json({
417
+ ok: false,
418
+ error: { message: error instanceof Error ? error.message : String(error) }
419
+ }, 502);
420
+ }
421
+ });
242
422
  authenticated.get("/api/agents/:agentId/image-generation", (c) => {
243
423
  try {
244
424
  return c.json({
@@ -271,7 +451,8 @@ function registerModelsRoutes(authenticated, deps) {
271
451
  const resolver = new CredentialResolver();
272
452
  const previousKey = await resolver.revealGatewayStoredApiKey(prepared.providerId);
273
453
  const suppliedKey = typeof body.apiKey === "string" ? body.apiKey.trim() : "";
274
- if (!(suppliedKey || await resolver.resolveApiKey(prepared.providerId))) return c.json({
454
+ const effectiveKey = suppliedKey || await resolver.resolveApiKey(prepared.providerId);
455
+ if (getImageGenerationProvider(prepared.providerId).credentialMode !== "none" && !effectiveKey) return c.json({
275
456
  ok: false,
276
457
  error: { message: "API key is required" }
277
458
  }, 400);
@@ -7,9 +7,9 @@ import { messagesToClientHistory } from "../../../session/client-history.js";
7
7
  import "../../../workflows/domain/index.js";
8
8
  import { resolveWorkflowRunArtifactsDir } from "../../../workflows/store/paths.js";
9
9
  import { WorkflowDraftService } from "../../../workflows/draft/workflow-draft-service.js";
10
- import "../../../workflows/draft/index.js";
11
10
  import { WorkflowDraftConflictError, WorkflowDraftStore } from "../../../workflows/authoring/workflow-draft-store.js";
12
11
  import "../../../workflows/authoring/index.js";
12
+ import "../../../workflows/draft/index.js";
13
13
  import { basename, join } from "node:path";
14
14
  import { readFile, stat } from "node:fs/promises";
15
15
  //#region src/gateway/hono/routes/workflows.ts
@@ -64,7 +64,13 @@ function prepareImageGenerationSetup(config, agentIdRaw, input) {
64
64
  }
65
65
  };
66
66
  const entry = next.agents.list[index];
67
- entry.models.imageGenerationModel = { primary: `${providerId}/${modelId}` };
67
+ entry.models = {
68
+ ...entry.models ?? {
69
+ defaultRole: resolveEffectiveAgentManifestForAgent(config, agentId).models.defaultRole,
70
+ roles: {}
71
+ },
72
+ imageGenerationModel: { primary: `${providerId}/${modelId}` }
73
+ };
68
74
  const parsed = ConfigSchema.safeParse(next);
69
75
  if (!parsed.success) return {
70
76
  ok: false,
@@ -3,6 +3,7 @@ import { init_logger } from "../../utils/logger.js";
3
3
  import { loadConfig, saveConfig } from "../../config/loader.js";
4
4
  import { getModelRegistry } from "../../providers/model-registry.js";
5
5
  import { init_providers } from "../../providers/index.js";
6
+ import { reloadImageGenerationProviders } from "../../agent/image/generation/provider-registry.js";
6
7
  import { disposeAllSessionMcpRuntimes } from "../../agent/mcp/bundle-mcp-runtime.js";
7
8
  import "../../agent/mcp/bundle-mcp-tools.js";
8
9
  import { ConfigHotReloader } from "../../config/reload.js";
@@ -214,6 +215,7 @@ var GatewayConfigCoordinator = class {
214
215
  log.debug("Reloading models config...");
215
216
  this.opts.setConfig(newConfig);
216
217
  getModelRegistry().refresh();
218
+ reloadImageGenerationProviders();
217
219
  this.opts.emit("config.reload", { section: "models" });
218
220
  log.debug("Models config reloaded");
219
221
  }
@@ -37,11 +37,11 @@ import { AgentService } from "../agent/service.js";
37
37
  import { ensureStarterAgentsInitialized } from "../agent/starter-agents.js";
38
38
  import { AutomationService } from "../automations/service/automation-service.js";
39
39
  import "../automations/index.js";
40
- import { getWorkspacePath } from "../config/workspace-path-helpers.js";
41
- import "../config/index.js";
42
40
  import { setPairingBroadcastSink } from "../channels/pairing/pairing-events.js";
43
41
  import { registerClarifyBridge } from "./clarify-runtime.js";
44
42
  import { ChannelManager } from "../channels/manager.js";
43
+ import { getWorkspacePath } from "../config/workspace-path-helpers.js";
44
+ import "../config/index.js";
45
45
  import { startConnectedKnowledgeCoordinator } from "../knowledge/coordinator.js";
46
46
  import "../knowledge/index.js";
47
47
  import { startConnectorMemorySyncCoordinator } from "../connectors/memory-sync-coordinator.js";
@@ -7,5 +7,5 @@ export { TimeoutAbortError, isTimeoutAbortError, pickEffectiveTimeoutMs, pickTim
7
7
  export { BlockedPrivateNetworkError, assertNotPrivateNetwork, classifyHost, defaultPolicy as defaultPrivateNetworkPolicy, privateNetworkPolicyToSsrfGuardOptions, type HostClass, type PrivateNetworkPolicy, } from './private-network.js';
8
8
  export { resolveProviderHttpRequestConfig, type ResolvedProviderHttpDefaults, type ResolveProviderHttpRequestConfigOptions, } from './resolve-provider-http-request-config.js';
9
9
  export { ProviderHttpError, assertOk, assertOkOrThrowHttpError, assertOkOrThrowProviderError, createProviderHttpError, extractProviderErrorDetail, extractProviderRequestId, extractVendorErrorFields, formatProviderErrorPayload, formatProviderHttpErrorMessage, readResponseTextLimited, truncateErrorDetail, type ProviderHttpErrorParts, } from './provider-http-errors.js';
10
- export { createProviderOperationDeadline, fetchWithTimeoutGuarded, getJsonRequest, normalizeBaseUrl, postJsonRequest, postMultipartRequest, waitProviderOperationPollInterval, type FetchWithGuardOptions, type GetJsonRequestOptions, type PostJsonRequestOptions, type PostMultipartRequestOptions, type ProviderHttpRequestConfig, type ProviderOperationDeadline, } from './provider-http.js';
10
+ export { createProviderOperationDeadline, fetchWithTimeoutGuarded, getJsonRequest, normalizeBaseUrl, postJsonRequest, postMultipartRequest, readJsonResponseLimited, waitProviderOperationPollInterval, type FetchWithGuardOptions, type GetJsonRequestOptions, type PostJsonRequestOptions, type PostMultipartRequestOptions, type ProviderHttpRequestConfig, type ProviderOperationDeadline, } from './provider-http.js';
11
11
  export { SsrfBlockedError, assertSafeUrl, isPrivateIpAddress, isPrivateIpv4, isPrivateIpv6, type ResolvedSsrfTarget, type SsrfGuardOptions, } from './ssrf-guard.js';
@@ -4,5 +4,5 @@ import { BlockedPrivateNetworkError, assertNotPrivateNetwork, classifyHost, defa
4
4
  import { resolveProviderHttpRequestConfig } from "./resolve-provider-http-request-config.js";
5
5
  import { ProviderHttpError, assertOk, assertOkOrThrowHttpError, assertOkOrThrowProviderError, createProviderHttpError, extractProviderErrorDetail, extractProviderRequestId, extractVendorErrorFields, formatProviderErrorPayload, formatProviderHttpErrorMessage, readResponseTextLimited, truncateErrorDetail } from "./provider-http-errors.js";
6
6
  import { SsrfBlockedError, assertSafeUrl, isPrivateIpAddress, isPrivateIpv4, isPrivateIpv6 } from "./ssrf-guard.js";
7
- import { createProviderOperationDeadline, fetchWithTimeoutGuarded, getJsonRequest, normalizeBaseUrl, postJsonRequest, postMultipartRequest, waitProviderOperationPollInterval } from "./provider-http.js";
8
- export { BlockedPrivateNetworkError, ProviderHttpError, SsrfBlockedError, TimeoutAbortError, assertNotPrivateNetwork, assertOk, assertOkOrThrowHttpError, assertOkOrThrowProviderError, assertSafeUrl, classifyHost, collectProviderApiKeysForExecution, createProviderHttpError, createProviderOperationDeadline, defaultPolicy as defaultPrivateNetworkPolicy, executeWithApiKeyRotation, extractProviderErrorDetail, extractProviderRequestId, extractVendorErrorFields, fetchWithTimeoutGuarded, formatProviderErrorPayload, formatProviderHttpErrorMessage, getJsonRequest, isPrivateIpAddress, isPrivateIpv4, isPrivateIpv6, isRotatableAuthFailure, isTimeoutAbortError, normalizeBaseUrl, pickEffectiveTimeoutMs, pickTimeoutMsOrFallback, postJsonRequest, postMultipartRequest, privateNetworkPolicyToSsrfGuardOptions, readResponseTextLimited, resolveDeadline, resolveProviderHttpRequestConfig, truncateErrorDetail, waitProviderOperationPollInterval };
7
+ import { createProviderOperationDeadline, fetchWithTimeoutGuarded, getJsonRequest, normalizeBaseUrl, postJsonRequest, postMultipartRequest, readJsonResponseLimited, waitProviderOperationPollInterval } from "./provider-http.js";
8
+ export { BlockedPrivateNetworkError, ProviderHttpError, SsrfBlockedError, TimeoutAbortError, assertNotPrivateNetwork, assertOk, assertOkOrThrowHttpError, assertOkOrThrowProviderError, assertSafeUrl, classifyHost, collectProviderApiKeysForExecution, createProviderHttpError, createProviderOperationDeadline, defaultPolicy as defaultPrivateNetworkPolicy, executeWithApiKeyRotation, extractProviderErrorDetail, extractProviderRequestId, extractVendorErrorFields, fetchWithTimeoutGuarded, formatProviderErrorPayload, formatProviderHttpErrorMessage, getJsonRequest, isPrivateIpAddress, isPrivateIpv4, isPrivateIpv6, isRotatableAuthFailure, isTimeoutAbortError, normalizeBaseUrl, pickEffectiveTimeoutMs, pickTimeoutMsOrFallback, postJsonRequest, postMultipartRequest, privateNetworkPolicyToSsrfGuardOptions, readJsonResponseLimited, readResponseTextLimited, resolveDeadline, resolveProviderHttpRequestConfig, truncateErrorDetail, waitProviderOperationPollInterval };
@@ -39,6 +39,8 @@ export interface GetJsonRequestOptions extends ProviderHttpRequestConfig {
39
39
  headers?: Record<string, string> | Headers;
40
40
  }
41
41
  export declare function getJsonRequest(url: string | URL, options: GetJsonRequestOptions): Promise<Response>;
42
+ /** Read and parse a JSON response without allowing an unbounded body allocation. */
43
+ export declare function readJsonResponseLimited(response: Response, maxBytes: number): Promise<unknown>;
42
44
  /**
43
45
  * Normalize a base URL: trim trailing slash, force http(s) scheme.
44
46
  */