cowork-os 0.3.21 → 0.3.25

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 (252) hide show
  1. package/README.md +372 -10
  2. package/connectors/README.md +20 -0
  3. package/connectors/asana-mcp/README.md +24 -0
  4. package/connectors/asana-mcp/dist/index.js +427 -0
  5. package/connectors/asana-mcp/package.json +15 -0
  6. package/connectors/asana-mcp/src/index.ts +553 -0
  7. package/connectors/asana-mcp/tsconfig.json +13 -0
  8. package/connectors/hubspot-mcp/README.md +35 -0
  9. package/connectors/hubspot-mcp/dist/index.js +454 -0
  10. package/connectors/hubspot-mcp/package.json +15 -0
  11. package/connectors/hubspot-mcp/src/index.ts +562 -0
  12. package/connectors/hubspot-mcp/tsconfig.json +13 -0
  13. package/connectors/jira-mcp/README.md +49 -0
  14. package/connectors/jira-mcp/dist/index.js +588 -0
  15. package/connectors/jira-mcp/package.json +15 -0
  16. package/connectors/jira-mcp/src/index.ts +711 -0
  17. package/connectors/jira-mcp/tsconfig.json +13 -0
  18. package/connectors/linear-mcp/README.md +22 -0
  19. package/connectors/linear-mcp/dist/index.js +402 -0
  20. package/connectors/linear-mcp/package.json +15 -0
  21. package/connectors/linear-mcp/src/index.ts +522 -0
  22. package/connectors/linear-mcp/tsconfig.json +13 -0
  23. package/connectors/okta-mcp/README.md +24 -0
  24. package/connectors/okta-mcp/dist/index.js +411 -0
  25. package/connectors/okta-mcp/package.json +15 -0
  26. package/connectors/okta-mcp/src/index.ts +520 -0
  27. package/connectors/okta-mcp/tsconfig.json +13 -0
  28. package/connectors/salesforce-mcp/README.md +47 -0
  29. package/connectors/salesforce-mcp/dist/index.js +584 -0
  30. package/connectors/salesforce-mcp/package.json +15 -0
  31. package/connectors/salesforce-mcp/src/index.ts +722 -0
  32. package/connectors/salesforce-mcp/tsconfig.json +13 -0
  33. package/connectors/servicenow-mcp/README.md +26 -0
  34. package/connectors/servicenow-mcp/dist/index.js +400 -0
  35. package/connectors/servicenow-mcp/package.json +15 -0
  36. package/connectors/servicenow-mcp/src/index.ts +500 -0
  37. package/connectors/servicenow-mcp/tsconfig.json +13 -0
  38. package/connectors/templates/mcp-connector/README.md +31 -0
  39. package/connectors/templates/mcp-connector/package.json +15 -0
  40. package/connectors/templates/mcp-connector/src/index.ts +330 -0
  41. package/connectors/templates/mcp-connector/tsconfig.json +13 -0
  42. package/connectors/zendesk-mcp/README.md +40 -0
  43. package/connectors/zendesk-mcp/dist/index.js +431 -0
  44. package/connectors/zendesk-mcp/package.json +15 -0
  45. package/connectors/zendesk-mcp/src/index.ts +543 -0
  46. package/connectors/zendesk-mcp/tsconfig.json +13 -0
  47. package/dist/electron/electron/agent/custom-skill-loader.js +31 -1
  48. package/dist/electron/electron/agent/daemon.js +189 -13
  49. package/dist/electron/electron/agent/executor.js +895 -78
  50. package/dist/electron/electron/agent/llm/anthropic-compatible-provider.js +177 -0
  51. package/dist/electron/electron/agent/llm/azure-openai-provider.js +328 -0
  52. package/dist/electron/electron/agent/llm/bedrock-provider.js +49 -9
  53. package/dist/electron/electron/agent/llm/github-copilot-provider.js +97 -0
  54. package/dist/electron/electron/agent/llm/groq-provider.js +33 -0
  55. package/dist/electron/electron/agent/llm/index.js +13 -1
  56. package/dist/electron/electron/agent/llm/kimi-provider.js +33 -0
  57. package/dist/electron/electron/agent/llm/openai-compatible-provider.js +116 -0
  58. package/dist/electron/electron/agent/llm/openai-compatible.js +111 -0
  59. package/dist/electron/electron/agent/llm/openai-oauth.js +2 -1
  60. package/dist/electron/electron/agent/llm/openrouter-provider.js +1 -1
  61. package/dist/electron/electron/agent/llm/provider-factory.js +350 -4
  62. package/dist/electron/electron/agent/llm/types.js +66 -1
  63. package/dist/electron/electron/agent/llm/xai-provider.js +33 -0
  64. package/dist/electron/electron/agent/search/provider-factory.js +38 -2
  65. package/dist/electron/electron/agent/tools/box-tools.js +231 -0
  66. package/dist/electron/electron/agent/tools/builtin-settings.js +28 -0
  67. package/dist/electron/electron/agent/tools/dropbox-tools.js +237 -0
  68. package/dist/electron/electron/agent/tools/file-tools.js +66 -3
  69. package/dist/electron/electron/agent/tools/google-drive-tools.js +227 -0
  70. package/dist/electron/electron/agent/tools/grep-tools.js +90 -10
  71. package/dist/electron/electron/agent/tools/image-tools.js +11 -1
  72. package/dist/electron/electron/agent/tools/notion-tools.js +312 -0
  73. package/dist/electron/electron/agent/tools/onedrive-tools.js +217 -0
  74. package/dist/electron/electron/agent/tools/registry.js +548 -10
  75. package/dist/electron/electron/agent/tools/search-tools.js +28 -10
  76. package/dist/electron/electron/agent/tools/sharepoint-tools.js +243 -0
  77. package/dist/electron/electron/agent/tools/shell-tools.js +12 -3
  78. package/dist/electron/electron/agent/tools/x-tools.js +1 -1
  79. package/dist/electron/electron/agents/agent-dispatch.js +63 -0
  80. package/dist/electron/electron/database/repositories.js +19 -5
  81. package/dist/electron/electron/database/schema.js +8 -0
  82. package/dist/electron/electron/gateway/channels/whatsapp.js +55 -0
  83. package/dist/electron/electron/gateway/index.js +75 -1
  84. package/dist/electron/electron/gateway/router.js +209 -154
  85. package/dist/electron/electron/ipc/canvas-handlers.js +5 -0
  86. package/dist/electron/electron/ipc/handlers.js +763 -267
  87. package/dist/electron/electron/main.js +63 -0
  88. package/dist/electron/electron/mcp/oauth/connector-oauth.js +333 -0
  89. package/dist/electron/electron/mcp/registry/MCPRegistryManager.js +503 -154
  90. package/dist/electron/electron/memory/MemoryService.js +2 -1
  91. package/dist/electron/electron/preload.js +78 -1
  92. package/dist/electron/electron/settings/appearance-manager.js +18 -1
  93. package/dist/electron/electron/settings/box-manager.js +54 -0
  94. package/dist/electron/electron/settings/dropbox-manager.js +54 -0
  95. package/dist/electron/electron/settings/google-drive-manager.js +54 -0
  96. package/dist/electron/electron/settings/notion-manager.js +56 -0
  97. package/dist/electron/electron/settings/onedrive-manager.js +54 -0
  98. package/dist/electron/electron/settings/sharepoint-manager.js +54 -0
  99. package/dist/electron/electron/utils/box-api.js +153 -0
  100. package/dist/electron/electron/utils/dropbox-api.js +144 -0
  101. package/dist/electron/electron/utils/env-migration.js +19 -0
  102. package/dist/electron/electron/utils/google-drive-api.js +152 -0
  103. package/dist/electron/electron/utils/notion-api.js +103 -0
  104. package/dist/electron/electron/utils/onedrive-api.js +113 -0
  105. package/dist/electron/electron/utils/sharepoint-api.js +109 -0
  106. package/dist/electron/electron/utils/validation.js +98 -3
  107. package/dist/electron/electron/utils/x-cli.js +1 -1
  108. package/dist/electron/shared/channelMessages.js +284 -3
  109. package/dist/electron/shared/llm-provider-catalog.js +198 -0
  110. package/dist/electron/shared/types.js +90 -1
  111. package/package.json +14 -3
  112. package/resources/skills/nano-banana-pro.json +4 -4
  113. package/resources/skills/openai-image-gen.json +3 -3
  114. package/resources/skills/scripts/gen.py +163 -0
  115. package/resources/skills/scripts/generate_image.py +91 -0
  116. package/src/electron/agent/custom-skill-loader.ts +34 -1
  117. package/src/electron/agent/daemon.ts +210 -14
  118. package/src/electron/agent/executor.ts +1124 -85
  119. package/src/electron/agent/llm/anthropic-compatible-provider.ts +214 -0
  120. package/src/electron/agent/llm/azure-openai-provider.ts +388 -0
  121. package/src/electron/agent/llm/bedrock-provider.ts +62 -9
  122. package/src/electron/agent/llm/github-copilot-provider.ts +117 -0
  123. package/src/electron/agent/llm/groq-provider.ts +39 -0
  124. package/src/electron/agent/llm/index.ts +6 -0
  125. package/src/electron/agent/llm/kimi-provider.ts +39 -0
  126. package/src/electron/agent/llm/openai-compatible-provider.ts +153 -0
  127. package/src/electron/agent/llm/openai-compatible.ts +133 -0
  128. package/src/electron/agent/llm/openai-oauth.ts +2 -1
  129. package/src/electron/agent/llm/openrouter-provider.ts +2 -1
  130. package/src/electron/agent/llm/provider-factory.ts +459 -6
  131. package/src/electron/agent/llm/types.ts +95 -1
  132. package/src/electron/agent/llm/xai-provider.ts +39 -0
  133. package/src/electron/agent/search/provider-factory.ts +43 -2
  134. package/src/electron/agent/tools/box-tools.ts +239 -0
  135. package/src/electron/agent/tools/builtin-settings.ts +36 -0
  136. package/src/electron/agent/tools/dropbox-tools.ts +237 -0
  137. package/src/electron/agent/tools/file-tools.ts +66 -3
  138. package/src/electron/agent/tools/gmail-tools.ts +240 -0
  139. package/src/electron/agent/tools/google-calendar-tools.ts +258 -0
  140. package/src/electron/agent/tools/google-drive-tools.ts +228 -0
  141. package/src/electron/agent/tools/grep-tools.ts +97 -12
  142. package/src/electron/agent/tools/image-tools.ts +11 -1
  143. package/src/electron/agent/tools/notion-tools.ts +330 -0
  144. package/src/electron/agent/tools/onedrive-tools.ts +217 -0
  145. package/src/electron/agent/tools/registry.ts +794 -10
  146. package/src/electron/agent/tools/search-tools.ts +29 -11
  147. package/src/electron/agent/tools/sharepoint-tools.ts +247 -0
  148. package/src/electron/agent/tools/shell-tools.ts +11 -3
  149. package/src/electron/agent/tools/x-tools.ts +1 -1
  150. package/src/electron/agents/agent-dispatch.ts +79 -0
  151. package/src/electron/database/SecureSettingsRepository.ts +7 -1
  152. package/src/electron/database/repositories.ts +58 -6
  153. package/src/electron/database/schema.ts +8 -0
  154. package/src/electron/gateway/channels/discord.ts +4 -0
  155. package/src/electron/gateway/channels/google-chat.ts +3 -0
  156. package/src/electron/gateway/channels/line.ts +3 -0
  157. package/src/electron/gateway/channels/matrix-client.ts +15 -0
  158. package/src/electron/gateway/channels/matrix.ts +31 -0
  159. package/src/electron/gateway/channels/mattermost.ts +3 -0
  160. package/src/electron/gateway/channels/signal.ts +3 -0
  161. package/src/electron/gateway/channels/slack.ts +9 -4
  162. package/src/electron/gateway/channels/teams.ts +4 -0
  163. package/src/electron/gateway/channels/telegram.ts +2 -0
  164. package/src/electron/gateway/channels/twitch.ts +2 -0
  165. package/src/electron/gateway/channels/types.ts +8 -0
  166. package/src/electron/gateway/channels/whatsapp.ts +66 -0
  167. package/src/electron/gateway/index.ts +95 -2
  168. package/src/electron/gateway/router.ts +231 -161
  169. package/src/electron/gateway/security.ts +21 -9
  170. package/src/electron/ipc/canvas-handlers.ts +10 -0
  171. package/src/electron/ipc/handlers.ts +848 -292
  172. package/src/electron/main.ts +35 -0
  173. package/src/electron/mcp/oauth/connector-oauth.ts +448 -0
  174. package/src/electron/mcp/registry/MCPRegistryManager.ts +343 -12
  175. package/src/electron/memory/MemoryService.ts +7 -1
  176. package/src/electron/preload.ts +200 -5
  177. package/src/electron/settings/appearance-manager.ts +20 -2
  178. package/src/electron/settings/box-manager.ts +58 -0
  179. package/src/electron/settings/dropbox-manager.ts +58 -0
  180. package/src/electron/settings/google-workspace-manager.ts +59 -0
  181. package/src/electron/settings/notion-manager.ts +60 -0
  182. package/src/electron/settings/onedrive-manager.ts +58 -0
  183. package/src/electron/settings/sharepoint-manager.ts +58 -0
  184. package/src/electron/utils/box-api.ts +184 -0
  185. package/src/electron/utils/dropbox-api.ts +171 -0
  186. package/src/electron/utils/env-migration.ts +22 -0
  187. package/src/electron/utils/gmail-api.ts +121 -0
  188. package/src/electron/utils/google-calendar-api.ts +115 -0
  189. package/src/electron/utils/google-workspace-api.ts +228 -0
  190. package/src/electron/utils/google-workspace-auth.ts +109 -0
  191. package/src/electron/utils/google-workspace-oauth.ts +232 -0
  192. package/src/electron/utils/notion-api.ts +126 -0
  193. package/src/electron/utils/onedrive-api.ts +137 -0
  194. package/src/electron/utils/sharepoint-api.ts +132 -0
  195. package/src/electron/utils/validation.ts +128 -1
  196. package/src/electron/utils/x-cli.ts +1 -1
  197. package/src/renderer/App.tsx +119 -8
  198. package/src/renderer/components/ActivityFeedItem.tsx +34 -17
  199. package/src/renderer/components/AgentWorkingStatePanel.tsx +7 -5
  200. package/src/renderer/components/AppearanceSettings.tsx +37 -2
  201. package/src/renderer/components/BlueBubblesSettings.tsx +18 -7
  202. package/src/renderer/components/BoxSettings.tsx +203 -0
  203. package/src/renderer/components/BrowserView.tsx +101 -0
  204. package/src/renderer/components/BuiltinToolsSettings.tsx +105 -0
  205. package/src/renderer/components/CanvasPreview.tsx +68 -1
  206. package/src/renderer/components/ConnectorEnvModal.tsx +116 -0
  207. package/src/renderer/components/ConnectorSetupModal.tsx +566 -0
  208. package/src/renderer/components/ConnectorsSettings.tsx +397 -0
  209. package/src/renderer/components/ControlPlaneSettings.tsx +2 -0
  210. package/src/renderer/components/DiscordSettings.tsx +18 -7
  211. package/src/renderer/components/DropboxSettings.tsx +202 -0
  212. package/src/renderer/components/EmailSettings.tsx +18 -7
  213. package/src/renderer/components/FileViewer.tsx +21 -13
  214. package/src/renderer/components/GoogleChatSettings.tsx +17 -7
  215. package/src/renderer/components/GoogleWorkspaceSettings.tsx +332 -0
  216. package/src/renderer/components/ImessageSettings.tsx +22 -11
  217. package/src/renderer/components/LineIcons.tsx +376 -0
  218. package/src/renderer/components/LineSettings.tsx +18 -7
  219. package/src/renderer/components/MCPSettings.tsx +56 -0
  220. package/src/renderer/components/MainContent.tsx +740 -76
  221. package/src/renderer/components/MatrixSettings.tsx +18 -7
  222. package/src/renderer/components/MattermostSettings.tsx +18 -7
  223. package/src/renderer/components/NodesSettings.tsx +58 -99
  224. package/src/renderer/components/NotificationPanel.tsx +25 -11
  225. package/src/renderer/components/NotionSettings.tsx +231 -0
  226. package/src/renderer/components/Onboarding/Onboarding.tsx +13 -1
  227. package/src/renderer/components/OnboardingModal.tsx +70 -1
  228. package/src/renderer/components/OneDriveSettings.tsx +212 -0
  229. package/src/renderer/components/RightPanel.tsx +141 -28
  230. package/src/renderer/components/ScheduledTasksSettings.tsx +10 -62
  231. package/src/renderer/components/SearchSettings.tsx +118 -114
  232. package/src/renderer/components/Settings.tsx +1425 -651
  233. package/src/renderer/components/SharePointSettings.tsx +224 -0
  234. package/src/renderer/components/Sidebar.tsx +94 -19
  235. package/src/renderer/components/SignalSettings.tsx +18 -7
  236. package/src/renderer/components/SkillHubBrowser.tsx +144 -185
  237. package/src/renderer/components/SlackSettings.tsx +18 -7
  238. package/src/renderer/components/TaskQuickActions.tsx +11 -6
  239. package/src/renderer/components/TaskTimeline.tsx +58 -26
  240. package/src/renderer/components/TeamsSettings.tsx +18 -7
  241. package/src/renderer/components/TelegramSettings.tsx +18 -7
  242. package/src/renderer/components/ThemeIcon.tsx +16 -0
  243. package/src/renderer/components/TwitchSettings.tsx +18 -7
  244. package/src/renderer/components/VoiceSettings.tsx +30 -74
  245. package/src/renderer/components/WhatsAppSettings.tsx +48 -37
  246. package/src/renderer/components/WorkingStateHistory.tsx +7 -5
  247. package/src/renderer/components/WorkspaceSelector.tsx +42 -13
  248. package/src/renderer/hooks/useOnboardingFlow.ts +21 -0
  249. package/src/renderer/styles/index.css +2333 -209
  250. package/src/shared/channelMessages.ts +367 -4
  251. package/src/shared/llm-provider-catalog.ts +217 -0
  252. package/src/shared/types.ts +251 -2
@@ -16,11 +16,115 @@ import { OllamaProvider } from './ollama-provider';
16
16
  import { GeminiProvider } from './gemini-provider';
17
17
  import { OpenRouterProvider } from './openrouter-provider';
18
18
  import { OpenAIProvider } from './openai-provider';
19
+ import { AzureOpenAIProvider } from './azure-openai-provider';
20
+ import { GroqProvider } from './groq-provider';
21
+ import { XAIProvider } from './xai-provider';
22
+ import { KimiProvider } from './kimi-provider';
23
+ import { AnthropicCompatibleProvider } from './anthropic-compatible-provider';
24
+ import { OpenAICompatibleProvider } from './openai-compatible-provider';
25
+ import { GitHubCopilotProvider } from './github-copilot-provider';
19
26
  import { SecureSettingsRepository } from '../../database/SecureSettingsRepository';
27
+ import {
28
+ CUSTOM_PROVIDER_CATALOG,
29
+ CUSTOM_PROVIDER_MAP,
30
+ CUSTOM_PROVIDER_IDS,
31
+ type ProviderCatalogEntry,
32
+ } from '../../../shared/llm-provider-catalog';
33
+ import type { CustomProviderConfig } from '../../../shared/types';
20
34
 
21
35
  const LEGACY_SETTINGS_FILE = 'llm-settings.json';
22
36
  const MASKED_VALUE = '***configured***';
23
37
  const ENCRYPTED_PREFIX = 'encrypted:';
38
+ const CUSTOM_PROVIDER_ALIASES: Partial<Record<LLMProviderType, LLMProviderType>> = {
39
+ 'kimi-coding': 'kimi-code',
40
+ };
41
+
42
+ function resolveCustomProviderId(providerType: LLMProviderType): LLMProviderType {
43
+ return CUSTOM_PROVIDER_ALIASES[providerType] || providerType;
44
+ }
45
+
46
+ function getCustomProviderEntry(providerType: LLMProviderType): ProviderCatalogEntry | undefined {
47
+ return CUSTOM_PROVIDER_MAP.get(resolveCustomProviderId(providerType));
48
+ }
49
+
50
+ function getCustomProviderConfig(
51
+ customProviders: Record<string, CustomProviderConfig> | undefined,
52
+ providerType: LLMProviderType
53
+ ): CustomProviderConfig | undefined {
54
+ if (!customProviders) return undefined;
55
+ const resolved = resolveCustomProviderId(providerType);
56
+ const resolvedConfig = customProviders[resolved];
57
+ if (resolvedConfig) {
58
+ return resolvedConfig;
59
+ }
60
+ const fallbackConfig = customProviders[providerType];
61
+ if (fallbackConfig && resolved !== providerType) {
62
+ console.log(`[LLMProviderFactory] Custom provider config not found for "${resolved}", falling back to "${providerType}".`);
63
+ }
64
+ return fallbackConfig;
65
+ }
66
+
67
+ function isCustomProviderConfigured(
68
+ entry: ProviderCatalogEntry,
69
+ config?: CustomProviderConfig
70
+ ): boolean {
71
+ if (!config) return false;
72
+ const hasApiKey = !!config.apiKey?.trim();
73
+ const hasBaseUrl = !!config.baseUrl?.trim() || !!entry.baseUrl;
74
+ const hasUserConfig = hasApiKey || !!config.baseUrl?.trim() || !!config.model?.trim();
75
+
76
+ if (!hasUserConfig) return false;
77
+
78
+ if (entry.apiKeyOptional) {
79
+ return entry.requiresBaseUrl ? hasBaseUrl : hasApiKey || hasBaseUrl;
80
+ }
81
+
82
+ return entry.requiresBaseUrl ? hasApiKey && hasBaseUrl : hasApiKey;
83
+ }
84
+
85
+ function createCustomProvider(
86
+ config: LLMProviderConfig,
87
+ entry: ProviderCatalogEntry,
88
+ resolvedType: LLMProviderType
89
+ ): LLMProvider {
90
+ if (resolvedType === 'github-copilot') {
91
+ return new GitHubCopilotProvider(config);
92
+ }
93
+
94
+ const apiKey = config.providerApiKey || '';
95
+ const baseUrl = config.providerBaseUrl || entry.baseUrl || '';
96
+
97
+ if (entry.requiresBaseUrl && !baseUrl) {
98
+ throw new Error(`${entry.name} base URL is required. Configure it in Settings.`);
99
+ }
100
+
101
+ if (!apiKey && !entry.apiKeyOptional) {
102
+ throw new Error(`${entry.name} API key is required. Configure it in Settings.`);
103
+ }
104
+
105
+ const model = config.model || entry.defaultModel;
106
+ if (!model) {
107
+ throw new Error(`${entry.name} model is required. Configure it in Settings.`);
108
+ }
109
+
110
+ if (entry.compatibility === 'openai') {
111
+ return new OpenAICompatibleProvider({
112
+ type: resolvedType,
113
+ providerName: entry.name,
114
+ apiKey,
115
+ baseUrl,
116
+ defaultModel: model,
117
+ });
118
+ }
119
+
120
+ return new AnthropicCompatibleProvider({
121
+ type: resolvedType,
122
+ providerName: entry.name,
123
+ apiKey,
124
+ baseUrl,
125
+ defaultModel: model,
126
+ });
127
+ }
24
128
 
25
129
  // ============ Legacy Encryption Functions (for migration only) ============
26
130
  // These functions are only used to decrypt settings from legacy JSON files
@@ -159,6 +263,45 @@ function sanitizeSettings(settings: LLMSettings): LLMSettings {
159
263
  };
160
264
  }
161
265
 
266
+ if (sanitized.azure) {
267
+ sanitized.azure = {
268
+ ...sanitized.azure,
269
+ apiKey: decryptSecret(sanitized.azure.apiKey),
270
+ };
271
+ }
272
+
273
+ if (sanitized.groq) {
274
+ sanitized.groq = {
275
+ ...sanitized.groq,
276
+ apiKey: decryptSecret(sanitized.groq.apiKey),
277
+ };
278
+ }
279
+
280
+ if (sanitized.xai) {
281
+ sanitized.xai = {
282
+ ...sanitized.xai,
283
+ apiKey: decryptSecret(sanitized.xai.apiKey),
284
+ };
285
+ }
286
+
287
+ if (sanitized.kimi) {
288
+ sanitized.kimi = {
289
+ ...sanitized.kimi,
290
+ apiKey: decryptSecret(sanitized.kimi.apiKey),
291
+ };
292
+ }
293
+
294
+ if (sanitized.customProviders) {
295
+ const normalized: Record<string, CustomProviderConfig> = {};
296
+ for (const [key, value] of Object.entries(sanitized.customProviders)) {
297
+ normalized[key] = {
298
+ ...value,
299
+ apiKey: decryptSecret(value.apiKey),
300
+ };
301
+ }
302
+ sanitized.customProviders = normalized;
303
+ }
304
+
162
305
  return sanitized;
163
306
  }
164
307
 
@@ -204,6 +347,7 @@ export interface LLMSettings {
204
347
  openrouter?: {
205
348
  apiKey?: string;
206
349
  model?: string;
350
+ baseUrl?: string;
207
351
  };
208
352
  openai?: {
209
353
  apiKey?: string;
@@ -214,12 +358,38 @@ export interface LLMSettings {
214
358
  tokenExpiresAt?: number;
215
359
  authMethod?: 'api_key' | 'oauth';
216
360
  };
361
+ azure?: {
362
+ apiKey?: string;
363
+ endpoint?: string;
364
+ deployment?: string;
365
+ deployments?: string[];
366
+ apiVersion?: string;
367
+ };
368
+ groq?: {
369
+ apiKey?: string;
370
+ model?: string;
371
+ baseUrl?: string;
372
+ };
373
+ xai?: {
374
+ apiKey?: string;
375
+ model?: string;
376
+ baseUrl?: string;
377
+ };
378
+ kimi?: {
379
+ apiKey?: string;
380
+ model?: string;
381
+ baseUrl?: string;
382
+ };
383
+ customProviders?: Record<string, CustomProviderConfig>;
217
384
  // Cached models from API (populated when user refreshes)
218
385
  cachedGeminiModels?: CachedModelInfo[];
219
386
  cachedOpenRouterModels?: CachedModelInfo[];
220
387
  cachedOllamaModels?: CachedModelInfo[];
221
388
  cachedBedrockModels?: CachedModelInfo[];
222
389
  cachedOpenAIModels?: CachedModelInfo[];
390
+ cachedGroqModels?: CachedModelInfo[];
391
+ cachedXaiModels?: CachedModelInfo[];
392
+ cachedKimiModels?: CachedModelInfo[];
223
393
  }
224
394
 
225
395
  const DEFAULT_SETTINGS: LLMSettings = {
@@ -235,6 +405,22 @@ export class LLMProviderFactory {
235
405
  private static cachedSettings: LLMSettings | null = null;
236
406
  private static migrationCompleted = false;
237
407
 
408
+ private static normalizeCustomProviders(settings: LLMSettings): void {
409
+ if (!settings.customProviders) return;
410
+
411
+ const legacyKey = settings.customProviders['kimi-coding'];
412
+ if (legacyKey && !settings.customProviders['kimi-code']) {
413
+ settings.customProviders['kimi-code'] = legacyKey;
414
+ }
415
+ if (settings.customProviders['kimi-coding']) {
416
+ delete settings.customProviders['kimi-coding'];
417
+ }
418
+
419
+ if (settings.providerType === 'kimi-coding') {
420
+ settings.providerType = 'kimi-code';
421
+ }
422
+ }
423
+
238
424
  /**
239
425
  * Initialize the factory
240
426
  */
@@ -330,6 +516,7 @@ export class LLMProviderFactory {
330
516
  const stored = repository.load<LLMSettings>('llm');
331
517
  if (stored) {
332
518
  settings = { ...DEFAULT_SETTINGS, ...stored };
519
+ this.normalizeCustomProviders(settings);
333
520
  settingsExist = true;
334
521
  }
335
522
  }
@@ -369,6 +556,19 @@ export class LLMProviderFactory {
369
556
  if (settings.openai?.apiKey || settings.openai?.accessToken) {
370
557
  return 'openai';
371
558
  }
559
+ const azureDeployment = settings.azure?.deployment || settings.azure?.deployments?.[0];
560
+ if (settings.azure?.apiKey && settings.azure?.endpoint && azureDeployment) {
561
+ return 'azure';
562
+ }
563
+ if (settings.groq?.apiKey) {
564
+ return 'groq';
565
+ }
566
+ if (settings.xai?.apiKey) {
567
+ return 'xai';
568
+ }
569
+ if (settings.kimi?.apiKey) {
570
+ return 'kimi';
571
+ }
372
572
  if (settings.bedrock?.accessKeyId || settings.bedrock?.profile) {
373
573
  return 'bedrock';
374
574
  }
@@ -376,6 +576,15 @@ export class LLMProviderFactory {
376
576
  return 'ollama';
377
577
  }
378
578
 
579
+ if (settings.customProviders) {
580
+ for (const entry of CUSTOM_PROVIDER_CATALOG) {
581
+ const config = getCustomProviderConfig(settings.customProviders, entry.id);
582
+ if (isCustomProviderConfigured(entry, config)) {
583
+ return entry.id;
584
+ }
585
+ }
586
+ }
587
+
379
588
  // No valid credentials detected - user needs to configure via Settings
380
589
  return null;
381
590
  }
@@ -418,10 +627,26 @@ export class LLMProviderFactory {
418
627
  static createProvider(overrideConfig?: Partial<LLMProviderConfig>): LLMProvider {
419
628
  const settings = this.loadSettings();
420
629
  const providerType = overrideConfig?.type || settings.providerType;
630
+ const customConfig = getCustomProviderConfig(settings.customProviders, providerType);
631
+ const azureDeployment = overrideConfig?.azureDeployment
632
+ || settings.azure?.deployment
633
+ || settings.azure?.deployments?.[0];
421
634
 
422
635
  const config: LLMProviderConfig = {
423
636
  type: providerType,
424
- model: this.getModelId(settings.modelKey, providerType, settings.ollama?.model, settings.gemini?.model, settings.openrouter?.model, settings.openai?.model),
637
+ model: this.getModelId(
638
+ settings.modelKey,
639
+ providerType,
640
+ settings.ollama?.model,
641
+ settings.gemini?.model,
642
+ settings.openrouter?.model,
643
+ settings.openai?.model,
644
+ azureDeployment,
645
+ settings.groq?.model,
646
+ settings.xai?.model,
647
+ settings.kimi?.model,
648
+ settings.customProviders
649
+ ),
425
650
  // Anthropic config - from settings only
426
651
  anthropicApiKey: normalizeSecret(overrideConfig?.anthropicApiKey) || settings.anthropic?.apiKey,
427
652
  // Bedrock config - from settings only
@@ -437,11 +662,29 @@ export class LLMProviderFactory {
437
662
  geminiApiKey: normalizeSecret(overrideConfig?.geminiApiKey) || settings.gemini?.apiKey,
438
663
  // OpenRouter config - from settings only
439
664
  openrouterApiKey: normalizeSecret(overrideConfig?.openrouterApiKey) || settings.openrouter?.apiKey,
665
+ openrouterBaseUrl: overrideConfig?.openrouterBaseUrl || settings.openrouter?.baseUrl,
440
666
  // OpenAI config - from settings only
441
667
  openaiApiKey: normalizeSecret(overrideConfig?.openaiApiKey) || settings.openai?.apiKey,
442
668
  openaiAccessToken: normalizeSecret(overrideConfig?.openaiAccessToken) || settings.openai?.accessToken,
443
669
  openaiRefreshToken: settings.openai?.refreshToken,
444
670
  openaiTokenExpiresAt: settings.openai?.tokenExpiresAt,
671
+ // Azure OpenAI config - from settings only
672
+ azureApiKey: normalizeSecret(overrideConfig?.azureApiKey) || settings.azure?.apiKey,
673
+ azureEndpoint: overrideConfig?.azureEndpoint || settings.azure?.endpoint,
674
+ azureDeployment,
675
+ azureApiVersion: overrideConfig?.azureApiVersion || settings.azure?.apiVersion,
676
+ // Groq config - from settings only
677
+ groqApiKey: normalizeSecret(overrideConfig?.groqApiKey) || settings.groq?.apiKey,
678
+ groqBaseUrl: overrideConfig?.groqBaseUrl || settings.groq?.baseUrl,
679
+ // xAI config - from settings only
680
+ xaiApiKey: normalizeSecret(overrideConfig?.xaiApiKey) || settings.xai?.apiKey,
681
+ xaiBaseUrl: overrideConfig?.xaiBaseUrl || settings.xai?.baseUrl,
682
+ // Kimi config - from settings only
683
+ kimiApiKey: normalizeSecret(overrideConfig?.kimiApiKey) || settings.kimi?.apiKey,
684
+ kimiBaseUrl: overrideConfig?.kimiBaseUrl || settings.kimi?.baseUrl,
685
+ // Custom provider config
686
+ providerApiKey: normalizeSecret(overrideConfig?.providerApiKey) || customConfig?.apiKey,
687
+ providerBaseUrl: overrideConfig?.providerBaseUrl || customConfig?.baseUrl,
445
688
  };
446
689
 
447
690
  return this.createProviderFromConfig(config);
@@ -451,6 +694,12 @@ export class LLMProviderFactory {
451
694
  * Create a provider from explicit config
452
695
  */
453
696
  static createProviderFromConfig(config: LLMProviderConfig): LLMProvider {
697
+ const customEntry = getCustomProviderEntry(config.type);
698
+ if (customEntry) {
699
+ const resolvedType = resolveCustomProviderId(config.type);
700
+ return createCustomProvider(config, customEntry, resolvedType);
701
+ }
702
+
454
703
  switch (config.type) {
455
704
  case 'anthropic':
456
705
  return new AnthropicProvider(config);
@@ -464,6 +713,14 @@ export class LLMProviderFactory {
464
713
  return new OpenRouterProvider(config);
465
714
  case 'openai':
466
715
  return new OpenAIProvider(config);
716
+ case 'azure':
717
+ return new AzureOpenAIProvider(config);
718
+ case 'groq':
719
+ return new GroqProvider(config);
720
+ case 'xai':
721
+ return new XAIProvider(config);
722
+ case 'kimi':
723
+ return new KimiProvider(config);
467
724
  default:
468
725
  throw new Error(`Unknown provider type: ${config.type}`);
469
726
  }
@@ -472,7 +729,25 @@ export class LLMProviderFactory {
472
729
  /**
473
730
  * Get the model ID for a provider
474
731
  */
475
- static getModelId(modelKey: ModelKey | string, providerType: LLMProviderType, ollamaModel?: string, geminiModel?: string, openrouterModel?: string, openaiModel?: string): string {
732
+ static getModelId(
733
+ modelKey: ModelKey | string,
734
+ providerType: LLMProviderType,
735
+ ollamaModel?: string,
736
+ geminiModel?: string,
737
+ openrouterModel?: string,
738
+ openaiModel?: string,
739
+ azureDeployment?: string,
740
+ groqModel?: string,
741
+ xaiModel?: string,
742
+ kimiModel?: string,
743
+ customProviders?: Record<string, CustomProviderConfig>
744
+ ): string {
745
+ const customEntry = getCustomProviderEntry(providerType);
746
+ if (customEntry) {
747
+ const customConfig = getCustomProviderConfig(customProviders, providerType);
748
+ return customConfig?.model || customEntry.defaultModel;
749
+ }
750
+
476
751
  // For Ollama, use the specific Ollama model if provided
477
752
  if (providerType === 'ollama') {
478
753
  return ollamaModel || 'gpt-oss:20b';
@@ -493,6 +768,26 @@ export class LLMProviderFactory {
493
768
  return openaiModel || 'gpt-4o-mini';
494
769
  }
495
770
 
771
+ // For Azure OpenAI, use the deployment name
772
+ if (providerType === 'azure') {
773
+ return azureDeployment || '';
774
+ }
775
+
776
+ // For Groq, use the specific model if provided or default
777
+ if (providerType === 'groq') {
778
+ return groqModel || 'llama-3.1-8b-instant';
779
+ }
780
+
781
+ // For xAI, use the specific model if provided or default
782
+ if (providerType === 'xai') {
783
+ return xaiModel || 'grok-4-fast-non-reasoning';
784
+ }
785
+
786
+ // For Kimi, use the specific model if provided or default
787
+ if (providerType === 'kimi') {
788
+ return kimiModel || 'kimi-k2.5';
789
+ }
790
+
496
791
  // For other providers, look up in MODELS
497
792
  const model = MODELS[modelKey as ModelKey];
498
793
  if (!model) {
@@ -529,7 +824,7 @@ export class LLMProviderFactory {
529
824
  }> {
530
825
  const settings = this.loadSettings();
531
826
 
532
- return [
827
+ const builtIns = [
533
828
  {
534
829
  type: 'anthropic' as LLMProviderType,
535
830
  name: 'Anthropic API',
@@ -550,6 +845,30 @@ export class LLMProviderFactory {
550
845
  name: 'OpenAI',
551
846
  configured: !!(settings.openai?.apiKey || settings.openai?.accessToken),
552
847
  },
848
+ {
849
+ type: 'azure' as LLMProviderType,
850
+ name: 'Azure OpenAI',
851
+ configured: !!(
852
+ settings.azure?.apiKey
853
+ && settings.azure?.endpoint
854
+ && (settings.azure?.deployment || settings.azure?.deployments?.length)
855
+ ),
856
+ },
857
+ {
858
+ type: 'groq' as LLMProviderType,
859
+ name: 'Groq',
860
+ configured: !!settings.groq?.apiKey,
861
+ },
862
+ {
863
+ type: 'xai' as LLMProviderType,
864
+ name: 'xAI (Grok)',
865
+ configured: !!settings.xai?.apiKey,
866
+ },
867
+ {
868
+ type: 'kimi' as LLMProviderType,
869
+ name: 'Kimi',
870
+ configured: !!settings.kimi?.apiKey,
871
+ },
553
872
  {
554
873
  type: 'bedrock' as LLMProviderType,
555
874
  name: 'AWS Bedrock',
@@ -561,6 +880,17 @@ export class LLMProviderFactory {
561
880
  configured: !!(settings.ollama?.baseUrl || settings.ollama?.model),
562
881
  },
563
882
  ];
883
+
884
+ const customProviders = CUSTOM_PROVIDER_CATALOG.map((entry: ProviderCatalogEntry) => {
885
+ const config = getCustomProviderConfig(settings.customProviders, entry.id);
886
+ return {
887
+ type: entry.id,
888
+ name: entry.name,
889
+ configured: isCustomProviderConfigured(entry, config),
890
+ };
891
+ });
892
+
893
+ return [...builtIns, ...customProviders];
564
894
  }
565
895
 
566
896
  /**
@@ -755,11 +1085,13 @@ export class LLMProviderFactory {
755
1085
  /**
756
1086
  * Fetch available OpenRouter models from the API
757
1087
  */
758
- static async getOpenRouterModels(apiKey?: string): Promise<Array<{ id: string; name: string; context_length: number }>> {
1088
+ static async getOpenRouterModels(apiKey?: string, baseUrl?: string): Promise<Array<{ id: string; name: string; context_length: number }>> {
759
1089
  const settings = this.loadSettings();
760
1090
  // Normalize empty strings to undefined
761
1091
  const normalizedApiKey = apiKey?.trim() || undefined;
762
1092
  const key = normalizedApiKey || settings.openrouter?.apiKey;
1093
+ const normalizedBaseUrl = baseUrl?.trim() || undefined;
1094
+ const resolvedBaseUrl = normalizedBaseUrl || settings.openrouter?.baseUrl;
763
1095
 
764
1096
  const defaultModels = [
765
1097
  { id: 'anthropic/claude-3.5-sonnet', name: 'Claude 3.5 Sonnet', context_length: 200000 },
@@ -780,6 +1112,7 @@ export class LLMProviderFactory {
780
1112
  type: 'openrouter',
781
1113
  model: '',
782
1114
  openrouterApiKey: key,
1115
+ openrouterBaseUrl: resolvedBaseUrl,
783
1116
  });
784
1117
  return await provider.getAvailableModels();
785
1118
  } catch (error: any) {
@@ -871,6 +1204,109 @@ export class LLMProviderFactory {
871
1204
  }
872
1205
  }
873
1206
 
1207
+ /**
1208
+ * Fetch available Groq models from the API
1209
+ */
1210
+ static async getGroqModels(apiKey?: string, baseUrl?: string): Promise<Array<{ id: string; name: string }>> {
1211
+ const settings = this.loadSettings();
1212
+ const normalizedApiKey = apiKey?.trim() || undefined;
1213
+ const key = normalizedApiKey || settings.groq?.apiKey;
1214
+ const normalizedBaseUrl = baseUrl?.trim() || undefined;
1215
+ const resolvedBaseUrl = normalizedBaseUrl || settings.groq?.baseUrl;
1216
+
1217
+ const defaultModels = [
1218
+ { id: 'llama-3.1-8b-instant', name: 'Llama 3.1 8B Instant' },
1219
+ { id: 'llama-3.3-70b-versatile', name: 'Llama 3.3 70B Versatile' },
1220
+ ];
1221
+
1222
+ if (!key) {
1223
+ return defaultModels;
1224
+ }
1225
+
1226
+ try {
1227
+ const provider = new GroqProvider({
1228
+ type: 'groq',
1229
+ model: '',
1230
+ groqApiKey: key,
1231
+ groqBaseUrl: resolvedBaseUrl,
1232
+ });
1233
+ return await provider.getAvailableModels();
1234
+ } catch (error: any) {
1235
+ console.error('Failed to fetch Groq models:', error);
1236
+ return defaultModels;
1237
+ }
1238
+ }
1239
+
1240
+ /**
1241
+ * Fetch available xAI models from the API
1242
+ */
1243
+ static async getXAIModels(apiKey?: string, baseUrl?: string): Promise<Array<{ id: string; name: string }>> {
1244
+ const settings = this.loadSettings();
1245
+ const normalizedApiKey = apiKey?.trim() || undefined;
1246
+ const key = normalizedApiKey || settings.xai?.apiKey;
1247
+ const normalizedBaseUrl = baseUrl?.trim() || undefined;
1248
+ const resolvedBaseUrl = normalizedBaseUrl || settings.xai?.baseUrl;
1249
+
1250
+ const defaultModels = [
1251
+ { id: 'grok-4', name: 'Grok 4' },
1252
+ { id: 'grok-4-fast-non-reasoning', name: 'Grok 4 Fast (Non-Reasoning)' },
1253
+ { id: 'grok-4-fast-reasoning', name: 'Grok 4 Fast (Reasoning)' },
1254
+ ];
1255
+
1256
+ if (!key) {
1257
+ return defaultModels;
1258
+ }
1259
+
1260
+ try {
1261
+ const provider = new XAIProvider({
1262
+ type: 'xai',
1263
+ model: '',
1264
+ xaiApiKey: key,
1265
+ xaiBaseUrl: resolvedBaseUrl,
1266
+ });
1267
+ return await provider.getAvailableModels();
1268
+ } catch (error: any) {
1269
+ console.error('Failed to fetch xAI models:', error);
1270
+ return defaultModels;
1271
+ }
1272
+ }
1273
+
1274
+ /**
1275
+ * Fetch available Kimi models from the API
1276
+ */
1277
+ static async getKimiModels(apiKey?: string, baseUrl?: string): Promise<Array<{ id: string; name: string }>> {
1278
+ const settings = this.loadSettings();
1279
+ const normalizedApiKey = apiKey?.trim() || undefined;
1280
+ const key = normalizedApiKey || settings.kimi?.apiKey;
1281
+ const normalizedBaseUrl = baseUrl?.trim() || undefined;
1282
+ const resolvedBaseUrl = normalizedBaseUrl || settings.kimi?.baseUrl;
1283
+
1284
+ const defaultModels = [
1285
+ { id: 'kimi-k2.5', name: 'Kimi K2.5' },
1286
+ { id: 'kimi-k2-0905-preview', name: 'Kimi K2.5 Preview' },
1287
+ { id: 'kimi-k2-turbo-preview', name: 'Kimi K2 Turbo (Preview)' },
1288
+ { id: 'kimi-k2-thinking', name: 'Kimi K2 Thinking' },
1289
+ { id: 'kimi-k2-thinking-turbo', name: 'Kimi K2 Thinking Turbo' },
1290
+ ];
1291
+
1292
+ if (!key) {
1293
+ return defaultModels;
1294
+ }
1295
+
1296
+ try {
1297
+ const provider = new KimiProvider({
1298
+ type: 'kimi',
1299
+ model: '',
1300
+ kimiApiKey: key,
1301
+ kimiBaseUrl: resolvedBaseUrl,
1302
+ });
1303
+ return await provider.getAvailableModels();
1304
+ } catch (error: any) {
1305
+ console.error('Failed to fetch Kimi models:', error);
1306
+ return defaultModels;
1307
+ }
1308
+ }
1309
+
874
1310
  /**
875
1311
  * Format OpenAI model ID to display name
876
1312
  */
@@ -921,7 +1357,7 @@ export class LLMProviderFactory {
921
1357
  * Save cached models for a provider
922
1358
  */
923
1359
  static saveCachedModels(
924
- providerType: 'gemini' | 'openrouter' | 'ollama' | 'bedrock' | 'openai',
1360
+ providerType: 'gemini' | 'openrouter' | 'ollama' | 'bedrock' | 'openai' | 'groq' | 'xai' | 'kimi',
925
1361
  models: CachedModelInfo[]
926
1362
  ): void {
927
1363
  const settings = this.loadSettings();
@@ -942,6 +1378,15 @@ export class LLMProviderFactory {
942
1378
  case 'openai':
943
1379
  settings.cachedOpenAIModels = models;
944
1380
  break;
1381
+ case 'groq':
1382
+ settings.cachedGroqModels = models;
1383
+ break;
1384
+ case 'xai':
1385
+ settings.cachedXaiModels = models;
1386
+ break;
1387
+ case 'kimi':
1388
+ settings.cachedKimiModels = models;
1389
+ break;
945
1390
  }
946
1391
 
947
1392
  this.saveSettings(settings);
@@ -950,7 +1395,9 @@ export class LLMProviderFactory {
950
1395
  /**
951
1396
  * Get cached models for a provider
952
1397
  */
953
- static getCachedModels(providerType: 'gemini' | 'openrouter' | 'ollama' | 'bedrock' | 'openai'): CachedModelInfo[] | undefined {
1398
+ static getCachedModels(
1399
+ providerType: 'gemini' | 'openrouter' | 'ollama' | 'bedrock' | 'openai' | 'groq' | 'xai' | 'kimi'
1400
+ ): CachedModelInfo[] | undefined {
954
1401
  const settings = this.loadSettings();
955
1402
 
956
1403
  switch (providerType) {
@@ -964,6 +1411,12 @@ export class LLMProviderFactory {
964
1411
  return settings.cachedBedrockModels;
965
1412
  case 'openai':
966
1413
  return settings.cachedOpenAIModels;
1414
+ case 'groq':
1415
+ return settings.cachedGroqModels;
1416
+ case 'xai':
1417
+ return settings.cachedXaiModels;
1418
+ case 'kimi':
1419
+ return settings.cachedKimiModels;
967
1420
  default:
968
1421
  return undefined;
969
1422
  }