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
@@ -0,0 +1,177 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AnthropicCompatibleProvider = void 0;
4
+ const ANTHROPIC_VERSION = '2023-06-01';
5
+ function joinUrl(baseUrl, path) {
6
+ const trimmedBase = baseUrl.replace(/\/+$/, '');
7
+ const trimmedPath = path.startsWith('/') ? path : `/${path}`;
8
+ return `${trimmedBase}${trimmedPath}`;
9
+ }
10
+ class AnthropicCompatibleProvider {
11
+ constructor(options) {
12
+ this.type = options.type;
13
+ this.apiKey = options.apiKey;
14
+ this.baseUrl = options.baseUrl;
15
+ this.defaultModel = options.defaultModel;
16
+ this.providerName = options.providerName;
17
+ }
18
+ async createMessage(request) {
19
+ const messages = this.convertMessages(request.messages);
20
+ const tools = request.tools ? this.convertTools(request.tools) : undefined;
21
+ const model = request.model || this.defaultModel;
22
+ try {
23
+ console.log(`[${this.providerName}] Calling API with model: ${model}`);
24
+ const response = await fetch(joinUrl(this.baseUrl, '/messages'), {
25
+ method: 'POST',
26
+ headers: {
27
+ 'Content-Type': 'application/json',
28
+ 'x-api-key': this.apiKey,
29
+ 'anthropic-version': ANTHROPIC_VERSION,
30
+ },
31
+ body: JSON.stringify({
32
+ model,
33
+ max_tokens: request.maxTokens,
34
+ system: request.system,
35
+ messages,
36
+ ...(tools && { tools }),
37
+ }),
38
+ signal: request.signal,
39
+ });
40
+ if (!response.ok) {
41
+ const errorData = await response.json().catch(() => ({}));
42
+ throw new Error(`${this.providerName} API error: ${response.status} ${response.statusText}` +
43
+ (errorData.error?.message ? ` - ${errorData.error.message}` : ''));
44
+ }
45
+ const data = await response.json();
46
+ return this.convertResponse(data);
47
+ }
48
+ catch (error) {
49
+ if (error.name === 'AbortError' || error.message?.includes('aborted')) {
50
+ console.log(`[${this.providerName}] Request aborted`);
51
+ throw new Error('Request cancelled');
52
+ }
53
+ console.error(`[${this.providerName}] API error:`, {
54
+ message: error.message,
55
+ status: error.status,
56
+ });
57
+ throw error;
58
+ }
59
+ }
60
+ async testConnection() {
61
+ try {
62
+ const response = await fetch(joinUrl(this.baseUrl, '/messages'), {
63
+ method: 'POST',
64
+ headers: {
65
+ 'Content-Type': 'application/json',
66
+ 'x-api-key': this.apiKey,
67
+ 'anthropic-version': ANTHROPIC_VERSION,
68
+ },
69
+ body: JSON.stringify({
70
+ model: this.defaultModel,
71
+ max_tokens: 10,
72
+ messages: [{ role: 'user', content: 'Hi' }],
73
+ }),
74
+ });
75
+ if (!response.ok) {
76
+ const errorData = await response.json().catch(() => ({}));
77
+ return {
78
+ success: false,
79
+ error: errorData.error?.message || `HTTP ${response.status}: ${response.statusText}`,
80
+ };
81
+ }
82
+ return { success: true };
83
+ }
84
+ catch (error) {
85
+ return {
86
+ success: false,
87
+ error: error.message || `Failed to connect to ${this.providerName} API`,
88
+ };
89
+ }
90
+ }
91
+ convertMessages(messages) {
92
+ return messages.map((msg) => {
93
+ if (typeof msg.content === 'string') {
94
+ return {
95
+ role: msg.role,
96
+ content: msg.content,
97
+ };
98
+ }
99
+ const content = msg.content.map((item) => {
100
+ if (item.type === 'tool_result') {
101
+ return {
102
+ type: 'tool_result',
103
+ tool_use_id: item.tool_use_id,
104
+ content: item.content,
105
+ ...(item.is_error && { is_error: true }),
106
+ };
107
+ }
108
+ if (item.type === 'tool_use') {
109
+ return {
110
+ type: 'tool_use',
111
+ id: item.id,
112
+ name: item.name,
113
+ input: item.input,
114
+ };
115
+ }
116
+ return {
117
+ type: 'text',
118
+ text: item.text,
119
+ };
120
+ });
121
+ return {
122
+ role: msg.role,
123
+ content,
124
+ };
125
+ });
126
+ }
127
+ convertTools(tools) {
128
+ return tools.map((tool) => ({
129
+ name: tool.name,
130
+ description: tool.description,
131
+ input_schema: tool.input_schema,
132
+ }));
133
+ }
134
+ convertResponse(response) {
135
+ const content = (response.content || [])
136
+ .filter((block) => block.type === 'text' || block.type === 'tool_use')
137
+ .map((block) => {
138
+ if (block.type === 'tool_use') {
139
+ return {
140
+ type: 'tool_use',
141
+ id: block.id,
142
+ name: block.name,
143
+ input: block.input,
144
+ };
145
+ }
146
+ return {
147
+ type: 'text',
148
+ text: block.text || '',
149
+ };
150
+ });
151
+ return {
152
+ content: content.length > 0 ? content : [{ type: 'text', text: '' }],
153
+ stopReason: this.mapStopReason(response.stop_reason),
154
+ usage: response.usage
155
+ ? {
156
+ inputTokens: response.usage.input_tokens || 0,
157
+ outputTokens: response.usage.output_tokens || 0,
158
+ }
159
+ : undefined,
160
+ };
161
+ }
162
+ mapStopReason(reason) {
163
+ switch (reason) {
164
+ case 'end_turn':
165
+ return 'end_turn';
166
+ case 'tool_use':
167
+ return 'tool_use';
168
+ case 'max_tokens':
169
+ return 'max_tokens';
170
+ case 'stop_sequence':
171
+ return 'stop_sequence';
172
+ default:
173
+ return 'end_turn';
174
+ }
175
+ }
176
+ }
177
+ exports.AnthropicCompatibleProvider = AnthropicCompatibleProvider;
@@ -0,0 +1,328 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AzureOpenAIProvider = void 0;
4
+ const openai_compatible_1 = require("./openai-compatible");
5
+ const DEFAULT_AZURE_API_VERSION = '2024-02-15-preview';
6
+ const isToolResult = (item) => item?.type === 'tool_result';
7
+ const isToolUse = (item) => item?.type === 'tool_use';
8
+ const isTextContent = (item) => item?.type === 'text';
9
+ class AzureOpenAIProvider {
10
+ constructor(config) {
11
+ this.type = 'azure';
12
+ const apiKey = config.azureApiKey?.trim();
13
+ const endpoint = config.azureEndpoint?.trim();
14
+ const deployment = config.azureDeployment?.trim();
15
+ if (!apiKey) {
16
+ throw new Error('Azure OpenAI API key is required. Configure it in Settings.');
17
+ }
18
+ if (!endpoint) {
19
+ throw new Error('Azure OpenAI endpoint is required. Configure it in Settings.');
20
+ }
21
+ if (!deployment) {
22
+ throw new Error('Azure OpenAI deployment name is required. Configure it in Settings.');
23
+ }
24
+ this.apiKey = apiKey;
25
+ this.endpoint = endpoint.replace(/\/+$/, '');
26
+ this.deployment = deployment;
27
+ this.apiVersion = config.azureApiVersion?.trim() || DEFAULT_AZURE_API_VERSION;
28
+ }
29
+ getChatCompletionsUrl() {
30
+ const deployment = encodeURIComponent(this.deployment);
31
+ const apiVersion = encodeURIComponent(this.apiVersion);
32
+ return `${this.endpoint}/openai/deployments/${deployment}/chat/completions?api-version=${apiVersion}`;
33
+ }
34
+ getResponsesUrl() {
35
+ return `${this.endpoint}/openai/v1/responses`;
36
+ }
37
+ isMaxTokensUnsupported(errorData) {
38
+ const message = errorData?.error?.message || '';
39
+ return /max_tokens/i.test(message) && /max_completion_tokens/i.test(message);
40
+ }
41
+ isChatCompletionUnsupported(errorData) {
42
+ const message = errorData?.error?.message || '';
43
+ return /chatcompletion/i.test(message) && /(does not work|not supported|unsupported)/i.test(message);
44
+ }
45
+ buildChatCompletionsBody(request, useMaxCompletionTokens) {
46
+ const messages = (0, openai_compatible_1.toOpenAICompatibleMessages)(request.messages, request.system);
47
+ const tools = request.tools ? (0, openai_compatible_1.toOpenAICompatibleTools)(request.tools) : undefined;
48
+ const tokenField = useMaxCompletionTokens ? 'max_completion_tokens' : 'max_tokens';
49
+ return {
50
+ model: request.model || this.deployment,
51
+ messages,
52
+ [tokenField]: request.maxTokens,
53
+ ...(tools && tools.length > 0 && { tools, tool_choice: 'auto' }),
54
+ };
55
+ }
56
+ buildResponsesInput(messages) {
57
+ const input = [];
58
+ for (const msg of messages) {
59
+ if (typeof msg.content === 'string') {
60
+ input.push({
61
+ type: 'message',
62
+ role: msg.role,
63
+ content: [
64
+ {
65
+ type: msg.role === 'assistant' ? 'output_text' : 'input_text',
66
+ text: msg.content,
67
+ },
68
+ ],
69
+ });
70
+ continue;
71
+ }
72
+ if (!Array.isArray(msg.content)) {
73
+ continue;
74
+ }
75
+ for (const item of msg.content) {
76
+ if (isToolResult(item)) {
77
+ input.push({
78
+ type: 'function_call_output',
79
+ call_id: item.tool_use_id,
80
+ output: typeof item.content === 'string' ? item.content : JSON.stringify(item.content ?? ''),
81
+ });
82
+ }
83
+ }
84
+ const textBlocks = msg.content.filter(isTextContent);
85
+ if (textBlocks.length > 0) {
86
+ input.push({
87
+ type: 'message',
88
+ role: msg.role,
89
+ content: textBlocks.map((block) => ({
90
+ type: msg.role === 'assistant' ? 'output_text' : 'input_text',
91
+ text: block.text,
92
+ })),
93
+ });
94
+ }
95
+ if (msg.role === 'assistant') {
96
+ const toolUses = msg.content.filter(isToolUse);
97
+ for (const toolUse of toolUses) {
98
+ input.push({
99
+ type: 'function_call',
100
+ call_id: toolUse.id,
101
+ name: toolUse.name,
102
+ arguments: JSON.stringify(toolUse.input ?? {}),
103
+ });
104
+ }
105
+ }
106
+ }
107
+ return input;
108
+ }
109
+ toResponsesTools(tools) {
110
+ return tools.map((tool) => ({
111
+ type: 'function',
112
+ name: tool.name,
113
+ description: tool.description,
114
+ parameters: this.sanitizeSchemaForResponses(tool.input_schema),
115
+ }));
116
+ }
117
+ sanitizeSchemaForResponses(schema) {
118
+ if (!schema || typeof schema !== 'object') {
119
+ return schema;
120
+ }
121
+ const result = Array.isArray(schema) ? [...schema] : { ...schema };
122
+ if (result.properties && typeof result.properties === 'object') {
123
+ const sanitizedProperties = {};
124
+ for (const [key, value] of Object.entries(result.properties)) {
125
+ sanitizedProperties[key] = this.sanitizeSchemaForResponses(value);
126
+ }
127
+ result.properties = sanitizedProperties;
128
+ }
129
+ if (result.items) {
130
+ result.items = this.sanitizeSchemaForResponses(result.items);
131
+ }
132
+ if (result.type === 'array' && !result.items) {
133
+ result.items = { type: 'string' };
134
+ }
135
+ return result;
136
+ }
137
+ buildResponsesBody(request) {
138
+ const input = this.buildResponsesInput(request.messages);
139
+ const tools = request.tools ? this.toResponsesTools(request.tools) : undefined;
140
+ return {
141
+ model: request.model || this.deployment,
142
+ input,
143
+ ...(request.system ? { instructions: request.system } : {}),
144
+ max_output_tokens: request.maxTokens,
145
+ ...(tools && tools.length > 0 && { tools, tool_choice: 'auto' }),
146
+ };
147
+ }
148
+ async sendRequest(url, body, signal) {
149
+ return fetch(url, {
150
+ method: 'POST',
151
+ headers: {
152
+ 'Content-Type': 'application/json',
153
+ 'api-key': this.apiKey,
154
+ },
155
+ body: JSON.stringify(body),
156
+ signal,
157
+ });
158
+ }
159
+ parseFunctionCallArguments(value) {
160
+ if (!value)
161
+ return {};
162
+ if (typeof value === 'object')
163
+ return value;
164
+ if (typeof value !== 'string')
165
+ return {};
166
+ try {
167
+ return JSON.parse(value);
168
+ }
169
+ catch {
170
+ return {};
171
+ }
172
+ }
173
+ fromResponsesApiResponse(response) {
174
+ const content = [];
175
+ let sawToolCall = false;
176
+ if (Array.isArray(response?.output)) {
177
+ response.output.forEach((item, index) => {
178
+ if (item.type === 'message') {
179
+ const blocks = Array.isArray(item.content) ? item.content : [];
180
+ for (const block of blocks) {
181
+ if (block.type === 'output_text' && typeof block.text === 'string') {
182
+ content.push({ type: 'text', text: block.text });
183
+ }
184
+ }
185
+ }
186
+ else if (item.type === 'function_call') {
187
+ sawToolCall = true;
188
+ const id = item.call_id || item.id || `call_${index}`;
189
+ content.push({
190
+ type: 'tool_use',
191
+ id,
192
+ name: item.name,
193
+ input: this.parseFunctionCallArguments(item.arguments),
194
+ });
195
+ }
196
+ });
197
+ }
198
+ if (content.length === 0 && typeof response?.output_text === 'string') {
199
+ content.push({ type: 'text', text: response.output_text });
200
+ }
201
+ if (content.length === 0) {
202
+ content.push({ type: 'text', text: '' });
203
+ }
204
+ return {
205
+ content,
206
+ stopReason: sawToolCall ? 'tool_use' : 'end_turn',
207
+ usage: response?.usage
208
+ ? {
209
+ inputTokens: response.usage.input_tokens ?? 0,
210
+ outputTokens: response.usage.output_tokens ?? 0,
211
+ }
212
+ : undefined,
213
+ };
214
+ }
215
+ async createMessage(request) {
216
+ try {
217
+ const chatUrl = this.getChatCompletionsUrl();
218
+ const responsesUrl = this.getResponsesUrl();
219
+ const runResponses = async () => {
220
+ const response = await this.sendRequest(responsesUrl, this.buildResponsesBody(request), request.signal);
221
+ if (!response.ok) {
222
+ const errorData = await response.json().catch(() => ({}));
223
+ throw new Error(`Azure OpenAI API error: ${response.status} ${response.statusText}` +
224
+ (errorData.error?.message ? ` - ${errorData.error.message}` : ''));
225
+ }
226
+ const data = await response.json();
227
+ return this.fromResponsesApiResponse(data);
228
+ };
229
+ let response = await this.sendRequest(chatUrl, this.buildChatCompletionsBody(request, false), request.signal);
230
+ if (!response.ok) {
231
+ let errorData = await response.json().catch(() => ({}));
232
+ if (this.isChatCompletionUnsupported(errorData)) {
233
+ return await runResponses();
234
+ }
235
+ if (this.isMaxTokensUnsupported(errorData)) {
236
+ response = await this.sendRequest(chatUrl, this.buildChatCompletionsBody(request, true), request.signal);
237
+ if (response.ok) {
238
+ const data = await response.json();
239
+ return (0, openai_compatible_1.fromOpenAICompatibleResponse)(data);
240
+ }
241
+ errorData = await response.json().catch(() => ({}));
242
+ if (this.isChatCompletionUnsupported(errorData)) {
243
+ return await runResponses();
244
+ }
245
+ }
246
+ throw new Error(`Azure OpenAI API error: ${response.status} ${response.statusText}` +
247
+ (errorData.error?.message ? ` - ${errorData.error.message}` : ''));
248
+ }
249
+ const data = await response.json();
250
+ return (0, openai_compatible_1.fromOpenAICompatibleResponse)(data);
251
+ }
252
+ catch (error) {
253
+ if (error.name === 'AbortError' || error.message?.includes('aborted')) {
254
+ console.log('[Azure OpenAI] Request aborted');
255
+ throw new Error('Request cancelled');
256
+ }
257
+ console.error('[Azure OpenAI] API error:', {
258
+ message: error.message,
259
+ });
260
+ throw error;
261
+ }
262
+ }
263
+ async testConnection() {
264
+ const testMaxTokens = 16;
265
+ try {
266
+ const chatUrl = this.getChatCompletionsUrl();
267
+ const responsesUrl = this.getResponsesUrl();
268
+ const runResponses = async () => {
269
+ const response = await this.sendRequest(responsesUrl, {
270
+ model: this.deployment,
271
+ input: [
272
+ {
273
+ type: 'message',
274
+ role: 'user',
275
+ content: [{ type: 'input_text', text: 'Hi' }],
276
+ },
277
+ ],
278
+ max_output_tokens: testMaxTokens,
279
+ });
280
+ if (!response.ok) {
281
+ const errorData = await response.json().catch(() => ({}));
282
+ return {
283
+ success: false,
284
+ error: errorData.error?.message || `HTTP ${response.status}: ${response.statusText}`,
285
+ };
286
+ }
287
+ return { success: true };
288
+ };
289
+ let response = await this.sendRequest(chatUrl, {
290
+ model: this.deployment,
291
+ messages: [{ role: 'user', content: 'Hi' }],
292
+ max_tokens: testMaxTokens,
293
+ });
294
+ if (!response.ok) {
295
+ let errorData = await response.json().catch(() => ({}));
296
+ if (this.isChatCompletionUnsupported(errorData)) {
297
+ return await runResponses();
298
+ }
299
+ if (this.isMaxTokensUnsupported(errorData)) {
300
+ response = await this.sendRequest(chatUrl, {
301
+ model: this.deployment,
302
+ messages: [{ role: 'user', content: 'Hi' }],
303
+ max_completion_tokens: testMaxTokens,
304
+ });
305
+ if (response.ok) {
306
+ return { success: true };
307
+ }
308
+ errorData = await response.json().catch(() => ({}));
309
+ if (this.isChatCompletionUnsupported(errorData)) {
310
+ return await runResponses();
311
+ }
312
+ }
313
+ return {
314
+ success: false,
315
+ error: errorData.error?.message || `HTTP ${response.status}: ${response.statusText}`,
316
+ };
317
+ }
318
+ return { success: true };
319
+ }
320
+ catch (error) {
321
+ return {
322
+ success: false,
323
+ error: error.message || 'Failed to connect to Azure OpenAI',
324
+ };
325
+ }
326
+ }
327
+ }
328
+ exports.AzureOpenAIProvider = AzureOpenAIProvider;
@@ -33,9 +33,10 @@ class BedrockProvider {
33
33
  this.client = new client_bedrock_runtime_1.BedrockRuntimeClient(clientConfig);
34
34
  }
35
35
  async createMessage(request) {
36
- const messages = this.convertMessages(request.messages);
36
+ const toolNameMap = request.tools ? this.buildToolNameMap(request.tools) : undefined;
37
+ const messages = this.convertMessages(request.messages, toolNameMap);
37
38
  const system = this.convertSystem(request.system);
38
- const toolConfig = request.tools ? this.convertTools(request.tools) : undefined;
39
+ const toolConfig = request.tools ? this.convertTools(request.tools, toolNameMap) : undefined;
39
40
  const command = new client_bedrock_runtime_1.ConverseCommand({
40
41
  modelId: request.model,
41
42
  messages,
@@ -50,7 +51,7 @@ class BedrockProvider {
50
51
  const response = await this.client.send(command,
51
52
  // Pass abort signal to allow cancellation
52
53
  request.signal ? { abortSignal: request.signal } : undefined);
53
- return this.convertResponse(response);
54
+ return this.convertResponse(response, toolNameMap);
54
55
  }
55
56
  catch (error) {
56
57
  // Handle abort errors gracefully
@@ -103,7 +104,7 @@ class BedrockProvider {
103
104
  convertSystem(system) {
104
105
  return [{ text: system }];
105
106
  }
106
- convertMessages(messages) {
107
+ convertMessages(messages, toolNameMap) {
107
108
  return messages.map((msg) => {
108
109
  const content = [];
109
110
  if (typeof msg.content === 'string') {
@@ -115,10 +116,11 @@ class BedrockProvider {
115
116
  content.push({ text: item.text });
116
117
  }
117
118
  else if (item.type === 'tool_use') {
119
+ const mappedName = toolNameMap?.toProvider.get(item.name) || item.name;
118
120
  content.push({
119
121
  toolUse: {
120
122
  toolUseId: item.id,
121
- name: item.name,
123
+ name: mappedName,
122
124
  input: item.input,
123
125
  },
124
126
  });
@@ -140,11 +142,11 @@ class BedrockProvider {
140
142
  };
141
143
  });
142
144
  }
143
- convertTools(tools) {
145
+ convertTools(tools, toolNameMap) {
144
146
  return {
145
147
  tools: tools.map((tool) => ({
146
148
  toolSpec: {
147
- name: tool.name,
149
+ name: toolNameMap?.toProvider.get(tool.name) || tool.name,
148
150
  description: tool.description,
149
151
  inputSchema: {
150
152
  json: tool.input_schema,
@@ -153,7 +155,7 @@ class BedrockProvider {
153
155
  })),
154
156
  };
155
157
  }
156
- convertResponse(response) {
158
+ convertResponse(response, toolNameMap) {
157
159
  const content = [];
158
160
  if (response.output?.message?.content) {
159
161
  for (const block of response.output.message.content) {
@@ -164,10 +166,11 @@ class BedrockProvider {
164
166
  });
165
167
  }
166
168
  else if (block.toolUse) {
169
+ const mappedName = toolNameMap?.fromProvider.get(block.toolUse.name) || block.toolUse.name;
167
170
  content.push({
168
171
  type: 'tool_use',
169
172
  id: block.toolUse.toolUseId,
170
- name: block.toolUse.name,
173
+ name: mappedName,
171
174
  input: block.toolUse.input,
172
175
  });
173
176
  }
@@ -184,6 +187,42 @@ class BedrockProvider {
184
187
  : undefined,
185
188
  };
186
189
  }
190
+ buildToolNameMap(tools) {
191
+ const toProvider = new Map();
192
+ const fromProvider = new Map();
193
+ const used = new Set();
194
+ for (const tool of tools) {
195
+ let base = this.normalizeToolName(tool.name);
196
+ if (!base) {
197
+ base = `tool_${this.shortHash(tool.name)}`;
198
+ }
199
+ let candidate = base;
200
+ if (used.has(candidate)) {
201
+ const hashed = `${base}_${this.shortHash(tool.name)}`;
202
+ candidate = hashed;
203
+ let counter = 1;
204
+ while (used.has(candidate)) {
205
+ candidate = `${hashed}_${counter++}`;
206
+ }
207
+ }
208
+ used.add(candidate);
209
+ toProvider.set(tool.name, candidate);
210
+ fromProvider.set(candidate, tool.name);
211
+ }
212
+ return { toProvider, fromProvider };
213
+ }
214
+ normalizeToolName(name) {
215
+ const sanitized = name.replace(/[^a-zA-Z0-9_-]/g, '_');
216
+ return BedrockProvider.toolNameRegex.test(sanitized) ? sanitized : '';
217
+ }
218
+ shortHash(input) {
219
+ let hash = 2166136261;
220
+ for (let i = 0; i < input.length; i++) {
221
+ hash ^= input.charCodeAt(i);
222
+ hash = Math.imul(hash, 16777619);
223
+ }
224
+ return (hash >>> 0).toString(36);
225
+ }
187
226
  mapStopReason(reason) {
188
227
  switch (reason) {
189
228
  case 'end_turn':
@@ -200,3 +239,4 @@ class BedrockProvider {
200
239
  }
201
240
  }
202
241
  exports.BedrockProvider = BedrockProvider;
242
+ BedrockProvider.toolNameRegex = /^[a-zA-Z0-9_-]+$/;