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
@@ -20,6 +20,9 @@ const PROVIDERS: {
20
20
  { id: 'gemini', name: 'Gemini', requiresKey: true },
21
21
  { id: 'ollama', name: 'Ollama', requiresKey: false },
22
22
  { id: 'openrouter', name: 'OpenRouter', requiresKey: true },
23
+ { id: 'groq', name: 'Groq', requiresKey: true },
24
+ { id: 'xai', name: 'Grok', requiresKey: true },
25
+ { id: 'kimi', name: 'Kimi', requiresKey: true },
23
26
  { id: 'bedrock', name: 'AWS Bedrock', requiresKey: false },
24
27
  ];
25
28
 
@@ -29,6 +32,9 @@ const PROVIDER_URLS: Record<string, string> = {
29
32
  openai: 'https://platform.openai.com/api-keys',
30
33
  gemini: 'https://aistudio.google.com/app/apikey',
31
34
  openrouter: 'https://openrouter.ai/keys',
35
+ groq: 'https://console.groq.com/keys',
36
+ xai: 'https://console.x.ai/',
37
+ kimi: 'https://platform.moonshot.ai/',
32
38
  };
33
39
 
34
40
  export function Onboarding({ onComplete }: OnboardingProps) {
@@ -252,7 +258,13 @@ export function Onboarding({ onComplete }: OnboardingProps) {
252
258
  ? 'OpenAI'
253
259
  : provider === 'gemini'
254
260
  ? 'Google AI Studio'
255
- : 'OpenRouter'}
261
+ : provider === 'openrouter'
262
+ ? 'OpenRouter'
263
+ : provider === 'groq'
264
+ ? 'Groq Console'
265
+ : provider === 'xai'
266
+ ? 'xAI Console'
267
+ : 'Moonshot Platform'}
256
268
  </a>
257
269
  </p>
258
270
  )}
@@ -12,7 +12,16 @@ interface OnboardingModalProps {
12
12
  type OnboardingStep = 'welcome' | 'llm' | 'channels';
13
13
 
14
14
  // LLM Provider types for the simplified setup
15
- type LLMProviderType = 'anthropic' | 'openai' | 'gemini' | 'ollama' | 'openrouter' | 'bedrock';
15
+ type LLMProviderType =
16
+ | 'anthropic'
17
+ | 'openai'
18
+ | 'gemini'
19
+ | 'ollama'
20
+ | 'openrouter'
21
+ | 'bedrock'
22
+ | 'groq'
23
+ | 'xai'
24
+ | 'kimi';
16
25
 
17
26
  interface ProviderOption {
18
27
  type: LLMProviderType;
@@ -105,6 +114,48 @@ const PROVIDER_OPTIONS: ProviderOption[] = [
105
114
  apiKeyPlaceholder: 'sk-or-...',
106
115
  apiKeyLink: 'https://openrouter.ai/keys',
107
116
  },
117
+ {
118
+ type: 'groq',
119
+ name: 'Groq',
120
+ description: 'Fast, low-latency models',
121
+ icon: (
122
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
123
+ <path d="M4 12h16" />
124
+ <path d="M12 4v16" />
125
+ <circle cx="12" cy="12" r="9" />
126
+ </svg>
127
+ ),
128
+ requiresApiKey: true,
129
+ apiKeyPlaceholder: 'gsk_...',
130
+ apiKeyLink: 'https://console.groq.com/keys',
131
+ },
132
+ {
133
+ type: 'xai',
134
+ name: 'xAI (Grok)',
135
+ description: 'Grok models from xAI',
136
+ icon: (
137
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
138
+ <path d="M4 4l16 16" />
139
+ <path d="M20 4L4 20" />
140
+ </svg>
141
+ ),
142
+ requiresApiKey: true,
143
+ apiKeyPlaceholder: 'xai-...',
144
+ apiKeyLink: 'https://console.x.ai/',
145
+ },
146
+ {
147
+ type: 'kimi',
148
+ name: 'Kimi',
149
+ description: 'Kimi models via Moonshot',
150
+ icon: (
151
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
152
+ <path d="M12 2l3 7 7 3-7 3-3 7-3-7-7-3 7-3 3-7z" />
153
+ </svg>
154
+ ),
155
+ requiresApiKey: true,
156
+ apiKeyPlaceholder: 'sk-...',
157
+ apiKeyLink: 'https://platform.moonshot.ai/',
158
+ },
108
159
  {
109
160
  type: 'bedrock',
110
161
  name: 'AWS Bedrock',
@@ -263,6 +314,12 @@ export function OnboardingModal({
263
314
  testConfig.openrouter = { apiKey };
264
315
  } else if (selectedProvider === 'ollama') {
265
316
  testConfig.ollama = { baseUrl: ollamaUrl };
317
+ } else if (selectedProvider === 'groq') {
318
+ testConfig.groq = { apiKey };
319
+ } else if (selectedProvider === 'xai') {
320
+ testConfig.xai = { apiKey };
321
+ } else if (selectedProvider === 'kimi') {
322
+ testConfig.kimi = { apiKey };
266
323
  }
267
324
 
268
325
  const result = await window.electronAPI.testLLMProvider(testConfig);
@@ -295,6 +352,12 @@ export function OnboardingModal({
295
352
  settings.ollama = { baseUrl: ollamaUrl, model: 'llama3.2' };
296
353
  } else if (selectedProvider === 'bedrock') {
297
354
  settings.bedrock = { region: 'us-east-1', useDefaultCredentials: true };
355
+ } else if (selectedProvider === 'groq') {
356
+ settings.groq = { apiKey, model: 'llama-3.1-8b-instant' };
357
+ } else if (selectedProvider === 'xai') {
358
+ settings.xai = { apiKey, model: 'grok-4-fast-non-reasoning' };
359
+ } else if (selectedProvider === 'kimi') {
360
+ settings.kimi = { apiKey, model: 'kimi-k2.5' };
298
361
  }
299
362
 
300
363
  await window.electronAPI.saveLLMSettings(settings);
@@ -339,6 +402,12 @@ export function OnboardingModal({
339
402
  return 'anthropic/claude-3.5-sonnet';
340
403
  case 'bedrock':
341
404
  return 'sonnet-4-5';
405
+ case 'groq':
406
+ return 'llama-3.1-8b-instant';
407
+ case 'xai':
408
+ return 'grok-4-fast-non-reasoning';
409
+ case 'kimi':
410
+ return 'kimi-k2.5';
342
411
  default:
343
412
  return 'sonnet-4';
344
413
  }
@@ -0,0 +1,212 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { OneDriveSettingsData } from '../../shared/types';
3
+
4
+ export function OneDriveSettings() {
5
+ const [settings, setSettings] = useState<OneDriveSettingsData | null>(null);
6
+ const [saving, setSaving] = useState(false);
7
+ const [testing, setTesting] = useState(false);
8
+ const [testResult, setTestResult] = useState<{ success: boolean; error?: string; name?: string; userId?: string; driveId?: string } | null>(null);
9
+ const [status, setStatus] = useState<{ configured: boolean; connected: boolean; name?: string; error?: string } | null>(null);
10
+ const [statusLoading, setStatusLoading] = useState(false);
11
+
12
+ useEffect(() => {
13
+ loadSettings();
14
+ refreshStatus();
15
+ }, []);
16
+
17
+ const loadSettings = async () => {
18
+ try {
19
+ const loaded = await window.electronAPI.getOneDriveSettings();
20
+ setSettings(loaded);
21
+ } catch (error) {
22
+ console.error('Failed to load OneDrive settings:', error);
23
+ }
24
+ };
25
+
26
+ const updateSettings = (updates: Partial<OneDriveSettingsData>) => {
27
+ if (!settings) return;
28
+ setSettings({ ...settings, ...updates });
29
+ };
30
+
31
+ const handleSave = async () => {
32
+ if (!settings) return;
33
+ setSaving(true);
34
+ setTestResult(null);
35
+ try {
36
+ const payload: OneDriveSettingsData = { ...settings };
37
+ await window.electronAPI.saveOneDriveSettings(payload);
38
+ setSettings(payload);
39
+ await refreshStatus();
40
+ } catch (error) {
41
+ console.error('Failed to save OneDrive settings:', error);
42
+ } finally {
43
+ setSaving(false);
44
+ }
45
+ };
46
+
47
+ const refreshStatus = async () => {
48
+ try {
49
+ setStatusLoading(true);
50
+ const result = await window.electronAPI.getOneDriveStatus();
51
+ setStatus(result);
52
+ } catch (error) {
53
+ console.error('Failed to load OneDrive status:', error);
54
+ } finally {
55
+ setStatusLoading(false);
56
+ }
57
+ };
58
+
59
+ const handleTestConnection = async () => {
60
+ setTesting(true);
61
+ setTestResult(null);
62
+ try {
63
+ const result = await window.electronAPI.testOneDriveConnection();
64
+ setTestResult(result);
65
+ await refreshStatus();
66
+ } catch (error: any) {
67
+ setTestResult({ success: false, error: error.message || 'Failed to test connection' });
68
+ } finally {
69
+ setTesting(false);
70
+ }
71
+ };
72
+
73
+ if (!settings) {
74
+ return <div className="settings-loading">Loading OneDrive settings...</div>;
75
+ }
76
+
77
+ const statusLabel = !status?.configured
78
+ ? 'Missing Token'
79
+ : status.connected
80
+ ? 'Connected'
81
+ : 'Configured';
82
+
83
+ const statusClass = !status?.configured
84
+ ? 'missing'
85
+ : status.connected
86
+ ? 'connected'
87
+ : 'configured';
88
+
89
+ return (
90
+ <div className="onedrive-settings">
91
+ <div className="settings-section">
92
+ <div className="settings-section-header">
93
+ <div className="settings-title-with-badge">
94
+ <h3>Connect OneDrive</h3>
95
+ {status && (
96
+ <span
97
+ className={`onedrive-status-badge ${statusClass}`}
98
+ title={!status.configured ? 'Access token not configured' : status.connected ? 'Connected to OneDrive' : 'Configured'}
99
+ >
100
+ {statusLabel}
101
+ </span>
102
+ )}
103
+ {statusLoading && !status && (
104
+ <span className="onedrive-status-badge configured">Checking…</span>
105
+ )}
106
+ </div>
107
+ <button className="btn-secondary btn-sm" onClick={refreshStatus} disabled={statusLoading}>
108
+ {statusLoading ? 'Checking...' : 'Refresh Status'}
109
+ </button>
110
+ </div>
111
+ <p className="settings-description">
112
+ Connect the agent to OneDrive using a Microsoft Graph access token, then use the built-in `onedrive_action`
113
+ tool to search and manage files.
114
+ </p>
115
+ {status?.error && (
116
+ <p className="settings-hint">Status check: {status.error}</p>
117
+ )}
118
+ <div className="settings-actions">
119
+ <button
120
+ className="btn-secondary btn-sm"
121
+ onClick={() => window.electronAPI.openExternal('https://portal.azure.com')}
122
+ >
123
+ Open Azure Portal
124
+ </button>
125
+ </div>
126
+ </div>
127
+
128
+ <div className="settings-section">
129
+ <div className="settings-field">
130
+ <label>Enable Integration</label>
131
+ <label className="settings-toggle">
132
+ <input
133
+ type="checkbox"
134
+ checked={settings.enabled}
135
+ onChange={(e) => updateSettings({ enabled: e.target.checked })}
136
+ />
137
+ <span className="toggle-slider" />
138
+ </label>
139
+ </div>
140
+
141
+ <div className="settings-field">
142
+ <label>Access Token</label>
143
+ <input
144
+ type="password"
145
+ className="settings-input"
146
+ placeholder="Microsoft Graph access token"
147
+ value={settings.accessToken || ''}
148
+ onChange={(e) => updateSettings({ accessToken: e.target.value || undefined })}
149
+ />
150
+ <p className="settings-hint">Use an access token with Files.ReadWrite or Files.Read scope.</p>
151
+ </div>
152
+
153
+ <div className="settings-field">
154
+ <label>Drive ID (optional)</label>
155
+ <input
156
+ type="text"
157
+ className="settings-input"
158
+ placeholder="Default: /me/drive"
159
+ value={settings.driveId || ''}
160
+ onChange={(e) => updateSettings({ driveId: e.target.value || undefined })}
161
+ />
162
+ <p className="settings-hint">Leave blank to use your default drive.</p>
163
+ </div>
164
+
165
+ <div className="settings-field">
166
+ <label>Timeout (ms)</label>
167
+ <input
168
+ type="number"
169
+ className="settings-input"
170
+ min={1000}
171
+ max={120000}
172
+ value={settings.timeoutMs ?? 20000}
173
+ onChange={(e) => updateSettings({ timeoutMs: Number(e.target.value) })}
174
+ />
175
+ </div>
176
+
177
+ <div className="settings-actions">
178
+ <button className="btn-secondary btn-sm" onClick={handleTestConnection} disabled={testing}>
179
+ {testing ? 'Testing...' : 'Test Connection'}
180
+ </button>
181
+ <button className="btn-primary btn-sm" onClick={handleSave} disabled={saving}>
182
+ {saving ? 'Saving...' : 'Save Settings'}
183
+ </button>
184
+ </div>
185
+
186
+ {testResult && (
187
+ <div className={`test-result ${testResult.success ? 'success' : 'error'}`}>
188
+ {testResult.success ? (
189
+ <span>Connected{testResult.name ? ` as ${testResult.name}` : ''}</span>
190
+ ) : (
191
+ <span>Connection failed: {testResult.error}</span>
192
+ )}
193
+ </div>
194
+ )}
195
+ </div>
196
+
197
+ <div className="settings-section">
198
+ <h4>Quick Usage</h4>
199
+ <pre className="settings-info-box">{`// List root items
200
+ onedrive_action({
201
+ action: "list_children"
202
+ });
203
+
204
+ // Upload a file to root
205
+ onedrive_action({
206
+ action: "upload_file",
207
+ file_path: "reports/summary.pdf"
208
+ });`}</pre>
209
+ </div>
210
+ </div>
211
+ );
212
+ }
@@ -207,13 +207,54 @@ export function RightPanel({ task, workspace, events, tasks = [], queueStatus, o
207
207
  return Array.from(files).slice(0, 10); // Limit to 10 most recent
208
208
  }, [events]);
209
209
 
210
- // Get status indicator for CLI style
210
+ // Get status indicator (terminal vs modern)
211
211
  const getStatusIndicator = (status: string) => {
212
212
  switch (status) {
213
- case 'completed': return '[✓]';
214
- case 'in_progress': return '[~]';
215
- case 'failed': return '[✗]';
216
- default: return '[ ]';
213
+ case 'completed':
214
+ return (
215
+ <>
216
+ <span className="terminal-only">[]</span>
217
+ <span className="modern-only">
218
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round">
219
+ <polyline points="20 6 9 17 4 12" />
220
+ </svg>
221
+ </span>
222
+ </>
223
+ );
224
+ case 'in_progress':
225
+ return (
226
+ <>
227
+ <span className="terminal-only">[~]</span>
228
+ <span className="modern-only">
229
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round">
230
+ <circle cx="12" cy="12" r="10" />
231
+ </svg>
232
+ </span>
233
+ </>
234
+ );
235
+ case 'failed':
236
+ return (
237
+ <>
238
+ <span className="terminal-only">[✗]</span>
239
+ <span className="modern-only">
240
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round">
241
+ <line x1="18" y1="6" x2="6" y2="18" />
242
+ <line x1="6" y1="6" x2="18" y2="18" />
243
+ </svg>
244
+ </span>
245
+ </>
246
+ );
247
+ default:
248
+ return (
249
+ <>
250
+ <span className="terminal-only">[ ]</span>
251
+ <span className="modern-only">
252
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round">
253
+ <circle cx="12" cy="12" r="10" opacity="0.3" />
254
+ </svg>
255
+ </span>
256
+ </>
257
+ );
217
258
  }
218
259
  };
219
260
 
@@ -232,8 +273,14 @@ export function RightPanel({ task, workspace, events, tasks = [], queueStatus, o
232
273
  <div className="right-panel-section cli-section">
233
274
  <div className="cli-section-header" onClick={() => toggleSection('progress')}>
234
275
  <span className="cli-section-prompt">&gt;</span>
235
- <span className="cli-section-title">{agentContext.getUiCopy('rightProgressTitle')}</span>
236
- <span className="cli-section-toggle">{expandedSections.progress ? '[-]' : '[+]'}</span>
276
+ <span className="cli-section-title">
277
+ <span className="terminal-only">{agentContext.getUiCopy('rightProgressTitle')}</span>
278
+ <span className="modern-only">Progress</span>
279
+ </span>
280
+ <span className="cli-section-toggle">
281
+ <span className="terminal-only">{expandedSections.progress ? '[-]' : '[+]'}</span>
282
+ <span className="modern-only">{expandedSections.progress ? '−' : '+'}</span>
283
+ </span>
237
284
  </div>
238
285
  {expandedSections.progress && (
239
286
  <div className="cli-section-content">
@@ -256,7 +303,10 @@ export function RightPanel({ task, workspace, events, tasks = [], queueStatus, o
256
303
  │ {task?.status === 'executing' ? '◉ WORKING...' : task?.status === 'paused' ? '⏸ PAUSED' : task?.status === 'blocked' ? '! BLOCKED' : task?.status === 'completed' ? '✓ ALL DONE' : '○ READY'}{' '}│
257
304
  └─────────────────────┘
258
305
  </div>
259
- <p className="cli-hint">{agentContext.getUiCopy('rightProgressEmptyHint')}</p>
306
+ <p className="cli-hint">
307
+ <span className="terminal-only">{agentContext.getUiCopy('rightProgressEmptyHint')}</span>
308
+ <span className="modern-only">Standing by when you are ready.</span>
309
+ </p>
260
310
  </div>
261
311
  )}
262
312
  </div>
@@ -268,18 +318,32 @@ export function RightPanel({ task, workspace, events, tasks = [], queueStatus, o
268
318
  <div className="right-panel-section cli-section">
269
319
  <div className="cli-section-header" onClick={() => toggleSection('queue')}>
270
320
  <span className="cli-section-prompt">&gt;</span>
271
- <span className="cli-section-title">{agentContext.getUiCopy('rightQueueTitle')}</span>
321
+ <span className="cli-section-title">
322
+ <span className="terminal-only">{agentContext.getUiCopy('rightQueueTitle')}</span>
323
+ <span className="modern-only">Queue</span>
324
+ </span>
272
325
  <span className="cli-queue-badge">{queueStatus?.runningCount}/{queueStatus?.maxConcurrent}{queueStatus && queueStatus.queuedCount > 0 && ` +${queueStatus.queuedCount}`}</span>
273
- <span className="cli-section-toggle">{expandedSections.queue ? '[-]' : '[+]'}</span>
326
+ <span className="cli-section-toggle">
327
+ <span className="terminal-only">{expandedSections.queue ? '[-]' : '[+]'}</span>
328
+ <span className="modern-only">{expandedSections.queue ? '−' : '+'}</span>
329
+ </span>
274
330
  </div>
275
331
  {expandedSections.queue && (
276
332
  <div className="cli-section-content">
277
333
  {runningTasks.length > 0 && (
278
334
  <div className="cli-queue-group">
279
- <div className="cli-context-label">{agentContext.getUiCopy('rightQueueActiveLabel')}</div>
335
+ <div className="cli-context-label">
336
+ <span className="terminal-only">{agentContext.getUiCopy('rightQueueActiveLabel')}</span>
337
+ <span className="modern-only">Active</span>
338
+ </div>
280
339
  {runningTasks.map(t => (
281
340
  <div key={t.id} className="cli-queue-item running">
282
- <span className="cli-queue-status">[~]</span>
341
+ <span className="cli-queue-status">
342
+ <span className="terminal-only">[~]</span>
343
+ <span className="modern-only">
344
+ <span className="queue-status-dot running" />
345
+ </span>
346
+ </span>
283
347
  <span className="cli-queue-title" onClick={() => onSelectTask?.(t.id)}>
284
348
  {(t.title || t.prompt).slice(0, 25)}{(t.title || t.prompt).length > 25 ? '...' : ''}
285
349
  </span>
@@ -290,10 +354,18 @@ export function RightPanel({ task, workspace, events, tasks = [], queueStatus, o
290
354
  )}
291
355
  {queuedTasks.length > 0 && (
292
356
  <div className="cli-queue-group">
293
- <div className="cli-context-label">{agentContext.getUiCopy('rightQueueNextLabel')}</div>
357
+ <div className="cli-context-label">
358
+ <span className="terminal-only">{agentContext.getUiCopy('rightQueueNextLabel')}</span>
359
+ <span className="modern-only">Up next</span>
360
+ </div>
294
361
  {queuedTasks.map((t, i) => (
295
362
  <div key={t.id} className="cli-queue-item queued">
296
- <span className="cli-queue-status">[{i + 1}]</span>
363
+ <span className="cli-queue-status">
364
+ <span className="terminal-only">[{i + 1}]</span>
365
+ <span className="modern-only">
366
+ <span className="queue-status-pill">{i + 1}</span>
367
+ </span>
368
+ </span>
297
369
  <span className="cli-queue-title" onClick={() => onSelectTask?.(t.id)}>
298
370
  {(t.title || t.prompt).slice(0, 25)}{(t.title || t.prompt).length > 25 ? '...' : ''}
299
371
  </span>
@@ -311,8 +383,14 @@ export function RightPanel({ task, workspace, events, tasks = [], queueStatus, o
311
383
  <div className="right-panel-section cli-section">
312
384
  <div className="cli-section-header" onClick={() => toggleSection('folder')}>
313
385
  <span className="cli-section-prompt">&gt;</span>
314
- <span className="cli-section-title">{agentContext.getUiCopy('rightFilesTitle')}</span>
315
- <span className="cli-section-toggle">{expandedSections.folder ? '[-]' : '[+]'}</span>
386
+ <span className="cli-section-title">
387
+ <span className="terminal-only">{agentContext.getUiCopy('rightFilesTitle')}</span>
388
+ <span className="modern-only">Files</span>
389
+ </span>
390
+ <span className="cli-section-toggle">
391
+ <span className="terminal-only">{expandedSections.folder ? '[-]' : '[+]'}</span>
392
+ <span className="modern-only">{expandedSections.folder ? '−' : '+'}</span>
393
+ </span>
316
394
  </div>
317
395
  {expandedSections.folder && (
318
396
  <div className="cli-section-content">
@@ -320,7 +398,12 @@ export function RightPanel({ task, workspace, events, tasks = [], queueStatus, o
320
398
  <div className="cli-file-list">
321
399
  {files.map((file, index) => (
322
400
  <div key={`${file.path}-${index}`} className={`cli-file-item ${file.action}`}>
323
- <span className={`cli-file-action ${file.action}`}>{getFileActionSymbol(file.action)}</span>
401
+ <span className={`cli-file-action ${file.action}`}>
402
+ <span className="terminal-only">{getFileActionSymbol(file.action)}</span>
403
+ <span className="modern-only">
404
+ <span className="file-action-dot" />
405
+ </span>
406
+ </span>
324
407
  <ClickableFilePath path={file.path} workspacePath={workspace?.path} className="cli-file-name" onOpenViewer={setViewerFilePath} />
325
408
  </div>
326
409
  ))}
@@ -331,12 +414,18 @@ export function RightPanel({ task, workspace, events, tasks = [], queueStatus, o
331
414
  {`├── (empty)
332
415
  └── ...`}
333
416
  </pre>
334
- <p className="cli-hint">{agentContext.getUiCopy('rightFilesEmptyHint')}</p>
417
+ <p className="cli-hint">
418
+ <span className="terminal-only">{agentContext.getUiCopy('rightFilesEmptyHint')}</span>
419
+ <span className="modern-only">No file changes yet.</span>
420
+ </p>
335
421
  </div>
336
422
  )}
337
423
  {workspace && (
338
424
  <div className="cli-workspace-path">
339
- <span className="cli-label">PWD:</span>
425
+ <span className="cli-label">
426
+ <span className="terminal-only">PWD:</span>
427
+ <span className="modern-only">Workspace</span>
428
+ </span>
340
429
  <span className="cli-path" title={workspace.path}>{workspace.name}/</span>
341
430
  </div>
342
431
  )}
@@ -348,8 +437,14 @@ export function RightPanel({ task, workspace, events, tasks = [], queueStatus, o
348
437
  <div className="right-panel-section cli-section">
349
438
  <div className="cli-section-header" onClick={() => toggleSection('context')}>
350
439
  <span className="cli-section-prompt">&gt;</span>
351
- <span className="cli-section-title">{agentContext.getUiCopy('rightContextTitle')}</span>
352
- <span className="cli-section-toggle">{expandedSections.context ? '[-]' : '[+]'}</span>
440
+ <span className="cli-section-title">
441
+ <span className="terminal-only">{agentContext.getUiCopy('rightContextTitle')}</span>
442
+ <span className="modern-only">Context</span>
443
+ </span>
444
+ <span className="cli-section-toggle">
445
+ <span className="terminal-only">{expandedSections.context ? '[-]' : '[+]'}</span>
446
+ <span className="modern-only">{expandedSections.context ? '−' : '+'}</span>
447
+ </span>
353
448
  </div>
354
449
  {expandedSections.context && (
355
450
  <div className="cli-section-content">
@@ -357,7 +452,10 @@ export function RightPanel({ task, workspace, events, tasks = [], queueStatus, o
357
452
  <div className="cli-context-list">
358
453
  {toolUsage.length > 0 && (
359
454
  <div className="cli-context-group">
360
- <div className="cli-context-label"># tools_used:</div>
455
+ <div className="cli-context-label">
456
+ <span className="terminal-only"># tools_used:</span>
457
+ <span className="modern-only">Tools used</span>
458
+ </div>
361
459
  {toolUsage.map((tool, index) => (
362
460
  <div key={`${tool.name}-${index}`} className="cli-context-item">
363
461
  <span className="cli-context-key">{tool.name}</span>
@@ -369,7 +467,10 @@ export function RightPanel({ task, workspace, events, tasks = [], queueStatus, o
369
467
  )}
370
468
  {referencedFiles.length > 0 && (
371
469
  <div className="cli-context-group">
372
- <div className="cli-context-label"># files_read:</div>
470
+ <div className="cli-context-label">
471
+ <span className="terminal-only"># files_read:</span>
472
+ <span className="modern-only">Files read</span>
473
+ </div>
373
474
  {referencedFiles.map((file, index) => (
374
475
  <div key={`${file}-${index}`} className="cli-context-item">
375
476
  <ClickableFilePath path={file} workspacePath={workspace?.path} className="cli-context-file" onOpenViewer={setViewerFilePath} />
@@ -381,10 +482,16 @@ export function RightPanel({ task, workspace, events, tasks = [], queueStatus, o
381
482
  ) : (
382
483
  <div className="cli-empty-state">
383
484
  <div className="cli-context-empty">
384
- tools: 0
385
- files: 0
485
+ <span className="terminal-only">
486
+ tools: 0
487
+ files: 0
488
+ </span>
489
+ <span className="modern-only">Nothing shared yet.</span>
386
490
  </div>
387
- <p className="cli-hint">{agentContext.getUiCopy('rightContextEmptyHint')}</p>
491
+ <p className="cli-hint">
492
+ <span className="terminal-only">{agentContext.getUiCopy('rightContextEmptyHint')}</span>
493
+ <span className="modern-only">Share tools or files to populate this panel.</span>
494
+ </p>
388
495
  </div>
389
496
  )}
390
497
  </div>
@@ -396,8 +503,14 @@ export function RightPanel({ task, workspace, events, tasks = [], queueStatus, o
396
503
 
397
504
  {/* Footer note */}
398
505
  <div className="cli-panel-footer">
399
- <span className="cli-footer-prompt">$</span>
400
- <span className="cli-footer-text">{agentContext.getUiCopy('rightFooterText')}</span>
506
+ <span className="cli-footer-prompt">
507
+ <span className="terminal-only">$</span>
508
+ <span className="modern-only">•</span>
509
+ </span>
510
+ <span className="cli-footer-text">
511
+ <span className="terminal-only">{agentContext.getUiCopy('rightFooterText')}</span>
512
+ <span className="modern-only">Local work only</span>
513
+ </span>
401
514
  </div>
402
515
 
403
516
  {/* File Viewer Modal */}