cowork-os 0.3.21

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 (526) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1638 -0
  3. package/bin/cowork.js +42 -0
  4. package/build/entitlements.mac.plist +16 -0
  5. package/build/icon.icns +0 -0
  6. package/build/icon.png +0 -0
  7. package/dist/electron/electron/activity/ActivityRepository.js +190 -0
  8. package/dist/electron/electron/agent/browser/browser-service.js +639 -0
  9. package/dist/electron/electron/agent/context-manager.js +225 -0
  10. package/dist/electron/electron/agent/custom-skill-loader.js +566 -0
  11. package/dist/electron/electron/agent/daemon.js +975 -0
  12. package/dist/electron/electron/agent/executor.js +3561 -0
  13. package/dist/electron/electron/agent/llm/anthropic-provider.js +155 -0
  14. package/dist/electron/electron/agent/llm/bedrock-provider.js +202 -0
  15. package/dist/electron/electron/agent/llm/gemini-provider.js +375 -0
  16. package/dist/electron/electron/agent/llm/index.js +34 -0
  17. package/dist/electron/electron/agent/llm/ollama-provider.js +263 -0
  18. package/dist/electron/electron/agent/llm/openai-oauth.js +101 -0
  19. package/dist/electron/electron/agent/llm/openai-provider.js +657 -0
  20. package/dist/electron/electron/agent/llm/openrouter-provider.js +232 -0
  21. package/dist/electron/electron/agent/llm/pricing.js +160 -0
  22. package/dist/electron/electron/agent/llm/provider-factory.js +880 -0
  23. package/dist/electron/electron/agent/llm/types.js +178 -0
  24. package/dist/electron/electron/agent/queue-manager.js +378 -0
  25. package/dist/electron/electron/agent/sandbox/docker-sandbox.js +402 -0
  26. package/dist/electron/electron/agent/sandbox/macos-sandbox.js +407 -0
  27. package/dist/electron/electron/agent/sandbox/runner.js +410 -0
  28. package/dist/electron/electron/agent/sandbox/sandbox-factory.js +228 -0
  29. package/dist/electron/electron/agent/sandbox/security-utils.js +258 -0
  30. package/dist/electron/electron/agent/search/brave-provider.js +119 -0
  31. package/dist/electron/electron/agent/search/google-provider.js +100 -0
  32. package/dist/electron/electron/agent/search/index.js +28 -0
  33. package/dist/electron/electron/agent/search/provider-factory.js +395 -0
  34. package/dist/electron/electron/agent/search/serpapi-provider.js +112 -0
  35. package/dist/electron/electron/agent/search/tavily-provider.js +90 -0
  36. package/dist/electron/electron/agent/search/types.js +40 -0
  37. package/dist/electron/electron/agent/security/index.js +12 -0
  38. package/dist/electron/electron/agent/security/input-sanitizer.js +303 -0
  39. package/dist/electron/electron/agent/security/output-filter.js +217 -0
  40. package/dist/electron/electron/agent/skill-eligibility.js +281 -0
  41. package/dist/electron/electron/agent/skill-registry.js +396 -0
  42. package/dist/electron/electron/agent/skills/document.js +878 -0
  43. package/dist/electron/electron/agent/skills/image-generator.js +225 -0
  44. package/dist/electron/electron/agent/skills/organizer.js +141 -0
  45. package/dist/electron/electron/agent/skills/presentation.js +367 -0
  46. package/dist/electron/electron/agent/skills/spreadsheet.js +165 -0
  47. package/dist/electron/electron/agent/tools/browser-tools.js +523 -0
  48. package/dist/electron/electron/agent/tools/builtin-settings.js +384 -0
  49. package/dist/electron/electron/agent/tools/canvas-tools.js +530 -0
  50. package/dist/electron/electron/agent/tools/cron-tools.js +577 -0
  51. package/dist/electron/electron/agent/tools/edit-tools.js +194 -0
  52. package/dist/electron/electron/agent/tools/file-tools.js +719 -0
  53. package/dist/electron/electron/agent/tools/glob-tools.js +283 -0
  54. package/dist/electron/electron/agent/tools/grep-tools.js +387 -0
  55. package/dist/electron/electron/agent/tools/image-tools.js +111 -0
  56. package/dist/electron/electron/agent/tools/mention-tools.js +282 -0
  57. package/dist/electron/electron/agent/tools/node-tools.js +476 -0
  58. package/dist/electron/electron/agent/tools/registry.js +2719 -0
  59. package/dist/electron/electron/agent/tools/search-tools.js +91 -0
  60. package/dist/electron/electron/agent/tools/shell-tools.js +574 -0
  61. package/dist/electron/electron/agent/tools/skill-tools.js +274 -0
  62. package/dist/electron/electron/agent/tools/system-tools.js +578 -0
  63. package/dist/electron/electron/agent/tools/web-fetch-tools.js +444 -0
  64. package/dist/electron/electron/agent/tools/x-tools.js +264 -0
  65. package/dist/electron/electron/agents/AgentRoleRepository.js +420 -0
  66. package/dist/electron/electron/agents/HeartbeatService.js +356 -0
  67. package/dist/electron/electron/agents/MentionRepository.js +197 -0
  68. package/dist/electron/electron/agents/TaskSubscriptionRepository.js +168 -0
  69. package/dist/electron/electron/agents/WorkingStateRepository.js +229 -0
  70. package/dist/electron/electron/canvas/canvas-manager.js +714 -0
  71. package/dist/electron/electron/canvas/canvas-preload.js +53 -0
  72. package/dist/electron/electron/canvas/canvas-protocol.js +195 -0
  73. package/dist/electron/electron/canvas/canvas-store.js +174 -0
  74. package/dist/electron/electron/canvas/index.js +13 -0
  75. package/dist/electron/electron/control-plane/client.js +364 -0
  76. package/dist/electron/electron/control-plane/handlers.js +572 -0
  77. package/dist/electron/electron/control-plane/index.js +41 -0
  78. package/dist/electron/electron/control-plane/node-manager.js +264 -0
  79. package/dist/electron/electron/control-plane/protocol.js +194 -0
  80. package/dist/electron/electron/control-plane/remote-client.js +437 -0
  81. package/dist/electron/electron/control-plane/server.js +640 -0
  82. package/dist/electron/electron/control-plane/settings.js +369 -0
  83. package/dist/electron/electron/control-plane/ssh-tunnel.js +549 -0
  84. package/dist/electron/electron/cron/index.js +30 -0
  85. package/dist/electron/electron/cron/schedule.js +190 -0
  86. package/dist/electron/electron/cron/service.js +614 -0
  87. package/dist/electron/electron/cron/store.js +155 -0
  88. package/dist/electron/electron/cron/types.js +82 -0
  89. package/dist/electron/electron/cron/webhook.js +258 -0
  90. package/dist/electron/electron/database/SecureSettingsRepository.js +444 -0
  91. package/dist/electron/electron/database/TaskLabelRepository.js +120 -0
  92. package/dist/electron/electron/database/repositories.js +1781 -0
  93. package/dist/electron/electron/database/schema.js +978 -0
  94. package/dist/electron/electron/extensions/index.js +33 -0
  95. package/dist/electron/electron/extensions/loader.js +313 -0
  96. package/dist/electron/electron/extensions/registry.js +485 -0
  97. package/dist/electron/electron/extensions/types.js +11 -0
  98. package/dist/electron/electron/gateway/channel-registry.js +1102 -0
  99. package/dist/electron/electron/gateway/channels/bluebubbles-client.js +479 -0
  100. package/dist/electron/electron/gateway/channels/bluebubbles.js +432 -0
  101. package/dist/electron/electron/gateway/channels/discord.js +975 -0
  102. package/dist/electron/electron/gateway/channels/email-client.js +593 -0
  103. package/dist/electron/electron/gateway/channels/email.js +443 -0
  104. package/dist/electron/electron/gateway/channels/google-chat.js +631 -0
  105. package/dist/electron/electron/gateway/channels/imessage-client.js +363 -0
  106. package/dist/electron/electron/gateway/channels/imessage.js +465 -0
  107. package/dist/electron/electron/gateway/channels/index.js +36 -0
  108. package/dist/electron/electron/gateway/channels/line-client.js +470 -0
  109. package/dist/electron/electron/gateway/channels/line.js +479 -0
  110. package/dist/electron/electron/gateway/channels/matrix-client.js +432 -0
  111. package/dist/electron/electron/gateway/channels/matrix.js +592 -0
  112. package/dist/electron/electron/gateway/channels/mattermost-client.js +394 -0
  113. package/dist/electron/electron/gateway/channels/mattermost.js +496 -0
  114. package/dist/electron/electron/gateway/channels/signal-client.js +500 -0
  115. package/dist/electron/electron/gateway/channels/signal.js +582 -0
  116. package/dist/electron/electron/gateway/channels/slack.js +415 -0
  117. package/dist/electron/electron/gateway/channels/teams.js +596 -0
  118. package/dist/electron/electron/gateway/channels/telegram.js +1390 -0
  119. package/dist/electron/electron/gateway/channels/twitch-client.js +502 -0
  120. package/dist/electron/electron/gateway/channels/twitch.js +396 -0
  121. package/dist/electron/electron/gateway/channels/types.js +8 -0
  122. package/dist/electron/electron/gateway/channels/whatsapp.js +953 -0
  123. package/dist/electron/electron/gateway/context-policy.js +268 -0
  124. package/dist/electron/electron/gateway/index.js +1063 -0
  125. package/dist/electron/electron/gateway/infrastructure.js +496 -0
  126. package/dist/electron/electron/gateway/router.js +2700 -0
  127. package/dist/electron/electron/gateway/security.js +375 -0
  128. package/dist/electron/electron/gateway/session.js +115 -0
  129. package/dist/electron/electron/gateway/tunnel.js +503 -0
  130. package/dist/electron/electron/guardrails/guardrail-manager.js +348 -0
  131. package/dist/electron/electron/hooks/gmail-watcher.js +300 -0
  132. package/dist/electron/electron/hooks/index.js +46 -0
  133. package/dist/electron/electron/hooks/mappings.js +381 -0
  134. package/dist/electron/electron/hooks/server.js +480 -0
  135. package/dist/electron/electron/hooks/settings.js +447 -0
  136. package/dist/electron/electron/hooks/types.js +41 -0
  137. package/dist/electron/electron/ipc/canvas-handlers.js +158 -0
  138. package/dist/electron/electron/ipc/handlers.js +3138 -0
  139. package/dist/electron/electron/ipc/mission-control-handlers.js +141 -0
  140. package/dist/electron/electron/main.js +448 -0
  141. package/dist/electron/electron/mcp/client/MCPClientManager.js +330 -0
  142. package/dist/electron/electron/mcp/client/MCPServerConnection.js +437 -0
  143. package/dist/electron/electron/mcp/client/transports/SSETransport.js +304 -0
  144. package/dist/electron/electron/mcp/client/transports/StdioTransport.js +307 -0
  145. package/dist/electron/electron/mcp/client/transports/WebSocketTransport.js +329 -0
  146. package/dist/electron/electron/mcp/host/MCPHostServer.js +354 -0
  147. package/dist/electron/electron/mcp/host/ToolAdapter.js +100 -0
  148. package/dist/electron/electron/mcp/registry/MCPRegistryManager.js +497 -0
  149. package/dist/electron/electron/mcp/settings.js +446 -0
  150. package/dist/electron/electron/mcp/types.js +59 -0
  151. package/dist/electron/electron/memory/MemoryService.js +435 -0
  152. package/dist/electron/electron/notifications/index.js +17 -0
  153. package/dist/electron/electron/notifications/service.js +118 -0
  154. package/dist/electron/electron/notifications/store.js +144 -0
  155. package/dist/electron/electron/preload.js +842 -0
  156. package/dist/electron/electron/reports/StandupReportService.js +272 -0
  157. package/dist/electron/electron/security/concurrency.js +293 -0
  158. package/dist/electron/electron/security/index.js +15 -0
  159. package/dist/electron/electron/security/policy-manager.js +435 -0
  160. package/dist/electron/electron/settings/appearance-manager.js +193 -0
  161. package/dist/electron/electron/settings/personality-manager.js +724 -0
  162. package/dist/electron/electron/settings/x-manager.js +58 -0
  163. package/dist/electron/electron/tailscale/exposure.js +188 -0
  164. package/dist/electron/electron/tailscale/index.js +28 -0
  165. package/dist/electron/electron/tailscale/settings.js +205 -0
  166. package/dist/electron/electron/tailscale/tailscale.js +355 -0
  167. package/dist/electron/electron/tray/QuickInputWindow.js +568 -0
  168. package/dist/electron/electron/tray/TrayManager.js +895 -0
  169. package/dist/electron/electron/tray/index.js +9 -0
  170. package/dist/electron/electron/updater/index.js +6 -0
  171. package/dist/electron/electron/updater/update-manager.js +418 -0
  172. package/dist/electron/electron/utils/env-migration.js +209 -0
  173. package/dist/electron/electron/utils/process.js +102 -0
  174. package/dist/electron/electron/utils/rate-limiter.js +104 -0
  175. package/dist/electron/electron/utils/validation.js +419 -0
  176. package/dist/electron/electron/utils/x-cli.js +177 -0
  177. package/dist/electron/electron/voice/VoiceService.js +507 -0
  178. package/dist/electron/electron/voice/index.js +14 -0
  179. package/dist/electron/electron/voice/voice-settings-manager.js +359 -0
  180. package/dist/electron/shared/channelMessages.js +170 -0
  181. package/dist/electron/shared/types.js +1185 -0
  182. package/package.json +159 -0
  183. package/resources/skills/1password.json +10 -0
  184. package/resources/skills/add-documentation.json +31 -0
  185. package/resources/skills/analyze-csv.json +17 -0
  186. package/resources/skills/apple-notes.json +10 -0
  187. package/resources/skills/apple-reminders.json +10 -0
  188. package/resources/skills/auto-commenter.json +10 -0
  189. package/resources/skills/bear-notes.json +10 -0
  190. package/resources/skills/bird.json +35 -0
  191. package/resources/skills/blogwatcher.json +10 -0
  192. package/resources/skills/blucli.json +10 -0
  193. package/resources/skills/bluebubbles.json +10 -0
  194. package/resources/skills/camsnap.json +10 -0
  195. package/resources/skills/clean-imports.json +18 -0
  196. package/resources/skills/code-review.json +18 -0
  197. package/resources/skills/coding-agent.json +10 -0
  198. package/resources/skills/compare-files.json +23 -0
  199. package/resources/skills/convert-code.json +34 -0
  200. package/resources/skills/create-changelog.json +24 -0
  201. package/resources/skills/debug-error.json +17 -0
  202. package/resources/skills/dependency-check.json +10 -0
  203. package/resources/skills/discord.json +10 -0
  204. package/resources/skills/eightctl.json +10 -0
  205. package/resources/skills/explain-code.json +29 -0
  206. package/resources/skills/extract-todos.json +18 -0
  207. package/resources/skills/food-order.json +10 -0
  208. package/resources/skills/gemini.json +10 -0
  209. package/resources/skills/generate-readme.json +10 -0
  210. package/resources/skills/gifgrep.json +10 -0
  211. package/resources/skills/git-commit.json +10 -0
  212. package/resources/skills/github.json +10 -0
  213. package/resources/skills/gog.json +10 -0
  214. package/resources/skills/goplaces.json +10 -0
  215. package/resources/skills/himalaya.json +10 -0
  216. package/resources/skills/imsg.json +10 -0
  217. package/resources/skills/karpathy-guidelines.json +12 -0
  218. package/resources/skills/last30days.json +26 -0
  219. package/resources/skills/local-places.json +10 -0
  220. package/resources/skills/mcporter.json +10 -0
  221. package/resources/skills/model-usage.json +10 -0
  222. package/resources/skills/nano-banana-pro.json +10 -0
  223. package/resources/skills/nano-pdf.json +10 -0
  224. package/resources/skills/notion.json +10 -0
  225. package/resources/skills/obsidian.json +10 -0
  226. package/resources/skills/openai-image-gen.json +10 -0
  227. package/resources/skills/openai-whisper-api.json +10 -0
  228. package/resources/skills/openai-whisper.json +10 -0
  229. package/resources/skills/openhue.json +10 -0
  230. package/resources/skills/oracle.json +10 -0
  231. package/resources/skills/ordercli.json +10 -0
  232. package/resources/skills/peekaboo.json +10 -0
  233. package/resources/skills/project-structure.json +10 -0
  234. package/resources/skills/proofread.json +17 -0
  235. package/resources/skills/refactor-code.json +31 -0
  236. package/resources/skills/rename-symbol.json +23 -0
  237. package/resources/skills/sag.json +10 -0
  238. package/resources/skills/security-audit.json +18 -0
  239. package/resources/skills/session-logs.json +10 -0
  240. package/resources/skills/sherpa-onnx-tts.json +10 -0
  241. package/resources/skills/skill-creator.json +15 -0
  242. package/resources/skills/skill-hub.json +29 -0
  243. package/resources/skills/slack.json +10 -0
  244. package/resources/skills/songsee.json +10 -0
  245. package/resources/skills/sonoscli.json +10 -0
  246. package/resources/skills/spotify-player.json +10 -0
  247. package/resources/skills/startup-cfo.json +55 -0
  248. package/resources/skills/summarize-folder.json +18 -0
  249. package/resources/skills/summarize.json +10 -0
  250. package/resources/skills/things-mac.json +10 -0
  251. package/resources/skills/tmux.json +10 -0
  252. package/resources/skills/translate.json +36 -0
  253. package/resources/skills/trello.json +10 -0
  254. package/resources/skills/video-frames.json +10 -0
  255. package/resources/skills/voice-call.json +10 -0
  256. package/resources/skills/wacli.json +10 -0
  257. package/resources/skills/weather.json +10 -0
  258. package/resources/skills/write-tests.json +31 -0
  259. package/src/electron/activity/ActivityRepository.ts +238 -0
  260. package/src/electron/agent/browser/browser-service.ts +721 -0
  261. package/src/electron/agent/context-manager.ts +257 -0
  262. package/src/electron/agent/custom-skill-loader.ts +634 -0
  263. package/src/electron/agent/daemon.ts +1097 -0
  264. package/src/electron/agent/executor.ts +4017 -0
  265. package/src/electron/agent/llm/anthropic-provider.ts +175 -0
  266. package/src/electron/agent/llm/bedrock-provider.ts +236 -0
  267. package/src/electron/agent/llm/gemini-provider.ts +422 -0
  268. package/src/electron/agent/llm/index.ts +9 -0
  269. package/src/electron/agent/llm/ollama-provider.ts +347 -0
  270. package/src/electron/agent/llm/openai-oauth.ts +127 -0
  271. package/src/electron/agent/llm/openai-provider.ts +686 -0
  272. package/src/electron/agent/llm/openrouter-provider.ts +273 -0
  273. package/src/electron/agent/llm/pricing.ts +180 -0
  274. package/src/electron/agent/llm/provider-factory.ts +971 -0
  275. package/src/electron/agent/llm/types.ts +291 -0
  276. package/src/electron/agent/queue-manager.ts +408 -0
  277. package/src/electron/agent/sandbox/docker-sandbox.ts +453 -0
  278. package/src/electron/agent/sandbox/macos-sandbox.ts +426 -0
  279. package/src/electron/agent/sandbox/runner.ts +453 -0
  280. package/src/electron/agent/sandbox/sandbox-factory.ts +337 -0
  281. package/src/electron/agent/sandbox/security-utils.ts +251 -0
  282. package/src/electron/agent/search/brave-provider.ts +141 -0
  283. package/src/electron/agent/search/google-provider.ts +131 -0
  284. package/src/electron/agent/search/index.ts +6 -0
  285. package/src/electron/agent/search/provider-factory.ts +450 -0
  286. package/src/electron/agent/search/serpapi-provider.ts +138 -0
  287. package/src/electron/agent/search/tavily-provider.ts +108 -0
  288. package/src/electron/agent/search/types.ts +118 -0
  289. package/src/electron/agent/security/index.ts +20 -0
  290. package/src/electron/agent/security/input-sanitizer.ts +380 -0
  291. package/src/electron/agent/security/output-filter.ts +259 -0
  292. package/src/electron/agent/skill-eligibility.ts +334 -0
  293. package/src/electron/agent/skill-registry.ts +457 -0
  294. package/src/electron/agent/skills/document.ts +1070 -0
  295. package/src/electron/agent/skills/image-generator.ts +272 -0
  296. package/src/electron/agent/skills/organizer.ts +131 -0
  297. package/src/electron/agent/skills/presentation.ts +418 -0
  298. package/src/electron/agent/skills/spreadsheet.ts +166 -0
  299. package/src/electron/agent/tools/browser-tools.ts +546 -0
  300. package/src/electron/agent/tools/builtin-settings.ts +422 -0
  301. package/src/electron/agent/tools/canvas-tools.ts +572 -0
  302. package/src/electron/agent/tools/cron-tools.ts +723 -0
  303. package/src/electron/agent/tools/edit-tools.ts +196 -0
  304. package/src/electron/agent/tools/file-tools.ts +811 -0
  305. package/src/electron/agent/tools/glob-tools.ts +303 -0
  306. package/src/electron/agent/tools/grep-tools.ts +432 -0
  307. package/src/electron/agent/tools/image-tools.ts +126 -0
  308. package/src/electron/agent/tools/mention-tools.ts +371 -0
  309. package/src/electron/agent/tools/node-tools.ts +550 -0
  310. package/src/electron/agent/tools/registry.ts +3052 -0
  311. package/src/electron/agent/tools/search-tools.ts +111 -0
  312. package/src/electron/agent/tools/shell-tools.ts +651 -0
  313. package/src/electron/agent/tools/skill-tools.ts +340 -0
  314. package/src/electron/agent/tools/system-tools.ts +665 -0
  315. package/src/electron/agent/tools/web-fetch-tools.ts +528 -0
  316. package/src/electron/agent/tools/x-tools.ts +267 -0
  317. package/src/electron/agents/AgentRoleRepository.ts +557 -0
  318. package/src/electron/agents/HeartbeatService.ts +469 -0
  319. package/src/electron/agents/MentionRepository.ts +242 -0
  320. package/src/electron/agents/TaskSubscriptionRepository.ts +231 -0
  321. package/src/electron/agents/WorkingStateRepository.ts +278 -0
  322. package/src/electron/canvas/canvas-manager.ts +818 -0
  323. package/src/electron/canvas/canvas-preload.ts +102 -0
  324. package/src/electron/canvas/canvas-protocol.ts +174 -0
  325. package/src/electron/canvas/canvas-store.ts +200 -0
  326. package/src/electron/canvas/index.ts +8 -0
  327. package/src/electron/control-plane/client.ts +527 -0
  328. package/src/electron/control-plane/handlers.ts +723 -0
  329. package/src/electron/control-plane/index.ts +51 -0
  330. package/src/electron/control-plane/node-manager.ts +322 -0
  331. package/src/electron/control-plane/protocol.ts +269 -0
  332. package/src/electron/control-plane/remote-client.ts +517 -0
  333. package/src/electron/control-plane/server.ts +853 -0
  334. package/src/electron/control-plane/settings.ts +401 -0
  335. package/src/electron/control-plane/ssh-tunnel.ts +624 -0
  336. package/src/electron/cron/index.ts +9 -0
  337. package/src/electron/cron/schedule.ts +217 -0
  338. package/src/electron/cron/service.ts +743 -0
  339. package/src/electron/cron/store.ts +165 -0
  340. package/src/electron/cron/types.ts +291 -0
  341. package/src/electron/cron/webhook.ts +303 -0
  342. package/src/electron/database/SecureSettingsRepository.ts +514 -0
  343. package/src/electron/database/TaskLabelRepository.ts +148 -0
  344. package/src/electron/database/repositories.ts +2397 -0
  345. package/src/electron/database/schema.ts +1017 -0
  346. package/src/electron/extensions/index.ts +18 -0
  347. package/src/electron/extensions/loader.ts +336 -0
  348. package/src/electron/extensions/registry.ts +546 -0
  349. package/src/electron/extensions/types.ts +372 -0
  350. package/src/electron/gateway/channel-registry.ts +1267 -0
  351. package/src/electron/gateway/channels/bluebubbles-client.ts +641 -0
  352. package/src/electron/gateway/channels/bluebubbles.ts +509 -0
  353. package/src/electron/gateway/channels/discord.ts +1150 -0
  354. package/src/electron/gateway/channels/email-client.ts +708 -0
  355. package/src/electron/gateway/channels/email.ts +516 -0
  356. package/src/electron/gateway/channels/google-chat.ts +760 -0
  357. package/src/electron/gateway/channels/imessage-client.ts +473 -0
  358. package/src/electron/gateway/channels/imessage.ts +520 -0
  359. package/src/electron/gateway/channels/index.ts +21 -0
  360. package/src/electron/gateway/channels/line-client.ts +598 -0
  361. package/src/electron/gateway/channels/line.ts +559 -0
  362. package/src/electron/gateway/channels/matrix-client.ts +632 -0
  363. package/src/electron/gateway/channels/matrix.ts +655 -0
  364. package/src/electron/gateway/channels/mattermost-client.ts +526 -0
  365. package/src/electron/gateway/channels/mattermost.ts +550 -0
  366. package/src/electron/gateway/channels/signal-client.ts +722 -0
  367. package/src/electron/gateway/channels/signal.ts +666 -0
  368. package/src/electron/gateway/channels/slack.ts +458 -0
  369. package/src/electron/gateway/channels/teams.ts +681 -0
  370. package/src/electron/gateway/channels/telegram.ts +1727 -0
  371. package/src/electron/gateway/channels/twitch-client.ts +665 -0
  372. package/src/electron/gateway/channels/twitch.ts +468 -0
  373. package/src/electron/gateway/channels/types.ts +1002 -0
  374. package/src/electron/gateway/channels/whatsapp.ts +1101 -0
  375. package/src/electron/gateway/context-policy.ts +382 -0
  376. package/src/electron/gateway/index.ts +1274 -0
  377. package/src/electron/gateway/infrastructure.ts +645 -0
  378. package/src/electron/gateway/router.ts +3206 -0
  379. package/src/electron/gateway/security.ts +422 -0
  380. package/src/electron/gateway/session.ts +144 -0
  381. package/src/electron/gateway/tunnel.ts +626 -0
  382. package/src/electron/guardrails/guardrail-manager.ts +380 -0
  383. package/src/electron/hooks/gmail-watcher.ts +355 -0
  384. package/src/electron/hooks/index.ts +30 -0
  385. package/src/electron/hooks/mappings.ts +404 -0
  386. package/src/electron/hooks/server.ts +574 -0
  387. package/src/electron/hooks/settings.ts +466 -0
  388. package/src/electron/hooks/types.ts +245 -0
  389. package/src/electron/ipc/canvas-handlers.ts +223 -0
  390. package/src/electron/ipc/handlers.ts +3661 -0
  391. package/src/electron/ipc/mission-control-handlers.ts +182 -0
  392. package/src/electron/main.ts +496 -0
  393. package/src/electron/mcp/client/MCPClientManager.ts +406 -0
  394. package/src/electron/mcp/client/MCPServerConnection.ts +514 -0
  395. package/src/electron/mcp/client/transports/SSETransport.ts +360 -0
  396. package/src/electron/mcp/client/transports/StdioTransport.ts +355 -0
  397. package/src/electron/mcp/client/transports/WebSocketTransport.ts +384 -0
  398. package/src/electron/mcp/host/MCPHostServer.ts +388 -0
  399. package/src/electron/mcp/host/ToolAdapter.ts +140 -0
  400. package/src/electron/mcp/registry/MCPRegistryManager.ts +565 -0
  401. package/src/electron/mcp/settings.ts +468 -0
  402. package/src/electron/mcp/types.ts +371 -0
  403. package/src/electron/memory/MemoryService.ts +523 -0
  404. package/src/electron/notifications/index.ts +16 -0
  405. package/src/electron/notifications/service.ts +161 -0
  406. package/src/electron/notifications/store.ts +163 -0
  407. package/src/electron/preload.ts +2845 -0
  408. package/src/electron/reports/StandupReportService.ts +356 -0
  409. package/src/electron/security/concurrency.ts +333 -0
  410. package/src/electron/security/index.ts +17 -0
  411. package/src/electron/security/policy-manager.ts +539 -0
  412. package/src/electron/settings/appearance-manager.ts +182 -0
  413. package/src/electron/settings/personality-manager.ts +800 -0
  414. package/src/electron/settings/x-manager.ts +62 -0
  415. package/src/electron/tailscale/exposure.ts +262 -0
  416. package/src/electron/tailscale/index.ts +34 -0
  417. package/src/electron/tailscale/settings.ts +218 -0
  418. package/src/electron/tailscale/tailscale.ts +379 -0
  419. package/src/electron/tray/QuickInputWindow.ts +609 -0
  420. package/src/electron/tray/TrayManager.ts +1005 -0
  421. package/src/electron/tray/index.ts +6 -0
  422. package/src/electron/updater/index.ts +1 -0
  423. package/src/electron/updater/update-manager.ts +447 -0
  424. package/src/electron/utils/env-migration.ts +203 -0
  425. package/src/electron/utils/process.ts +124 -0
  426. package/src/electron/utils/rate-limiter.ts +130 -0
  427. package/src/electron/utils/validation.ts +493 -0
  428. package/src/electron/utils/x-cli.ts +198 -0
  429. package/src/electron/voice/VoiceService.ts +583 -0
  430. package/src/electron/voice/index.ts +9 -0
  431. package/src/electron/voice/voice-settings-manager.ts +403 -0
  432. package/src/renderer/App.tsx +775 -0
  433. package/src/renderer/components/ActivityFeed.tsx +407 -0
  434. package/src/renderer/components/ActivityFeedItem.tsx +285 -0
  435. package/src/renderer/components/AgentRoleCard.tsx +343 -0
  436. package/src/renderer/components/AgentRoleEditor.tsx +805 -0
  437. package/src/renderer/components/AgentSquadSettings.tsx +295 -0
  438. package/src/renderer/components/AgentWorkingStatePanel.tsx +411 -0
  439. package/src/renderer/components/AppearanceSettings.tsx +122 -0
  440. package/src/renderer/components/ApprovalDialog.tsx +100 -0
  441. package/src/renderer/components/BlueBubblesSettings.tsx +505 -0
  442. package/src/renderer/components/BuiltinToolsSettings.tsx +307 -0
  443. package/src/renderer/components/CanvasPreview.tsx +1189 -0
  444. package/src/renderer/components/CommandOutput.tsx +202 -0
  445. package/src/renderer/components/ContextPolicySettings.tsx +523 -0
  446. package/src/renderer/components/ControlPlaneSettings.tsx +1134 -0
  447. package/src/renderer/components/DisclaimerModal.tsx +124 -0
  448. package/src/renderer/components/DiscordSettings.tsx +436 -0
  449. package/src/renderer/components/EmailSettings.tsx +606 -0
  450. package/src/renderer/components/ExtensionsSettings.tsx +542 -0
  451. package/src/renderer/components/FileViewer.tsx +224 -0
  452. package/src/renderer/components/GoogleChatSettings.tsx +535 -0
  453. package/src/renderer/components/GuardrailSettings.tsx +487 -0
  454. package/src/renderer/components/HooksSettings.tsx +581 -0
  455. package/src/renderer/components/ImessageSettings.tsx +484 -0
  456. package/src/renderer/components/LineSettings.tsx +483 -0
  457. package/src/renderer/components/MCPRegistryBrowser.tsx +386 -0
  458. package/src/renderer/components/MCPSettings.tsx +943 -0
  459. package/src/renderer/components/MainContent.tsx +2433 -0
  460. package/src/renderer/components/MatrixSettings.tsx +510 -0
  461. package/src/renderer/components/MattermostSettings.tsx +473 -0
  462. package/src/renderer/components/MemorySettings.tsx +247 -0
  463. package/src/renderer/components/MentionBadge.tsx +87 -0
  464. package/src/renderer/components/MentionInput.tsx +409 -0
  465. package/src/renderer/components/MentionList.tsx +476 -0
  466. package/src/renderer/components/MissionControlPanel.tsx +1995 -0
  467. package/src/renderer/components/NodesSettings.tsx +316 -0
  468. package/src/renderer/components/NotificationPanel.tsx +481 -0
  469. package/src/renderer/components/Onboarding/AwakeningOrb.tsx +44 -0
  470. package/src/renderer/components/Onboarding/Onboarding.tsx +443 -0
  471. package/src/renderer/components/Onboarding/TypewriterText.tsx +102 -0
  472. package/src/renderer/components/Onboarding/index.ts +3 -0
  473. package/src/renderer/components/OnboardingModal.tsx +698 -0
  474. package/src/renderer/components/PairingCodeDisplay.tsx +324 -0
  475. package/src/renderer/components/PersonalitySettings.tsx +597 -0
  476. package/src/renderer/components/QueueSettings.tsx +119 -0
  477. package/src/renderer/components/QuickTaskFAB.tsx +71 -0
  478. package/src/renderer/components/RightPanel.tsx +413 -0
  479. package/src/renderer/components/ScheduledTasksSettings.tsx +1328 -0
  480. package/src/renderer/components/SearchSettings.tsx +328 -0
  481. package/src/renderer/components/Settings.tsx +1504 -0
  482. package/src/renderer/components/Sidebar.tsx +344 -0
  483. package/src/renderer/components/SignalSettings.tsx +673 -0
  484. package/src/renderer/components/SkillHubBrowser.tsx +458 -0
  485. package/src/renderer/components/SkillParameterModal.tsx +185 -0
  486. package/src/renderer/components/SkillsSettings.tsx +451 -0
  487. package/src/renderer/components/SlackSettings.tsx +442 -0
  488. package/src/renderer/components/StandupReportViewer.tsx +614 -0
  489. package/src/renderer/components/TaskBoard.tsx +498 -0
  490. package/src/renderer/components/TaskBoardCard.tsx +357 -0
  491. package/src/renderer/components/TaskBoardColumn.tsx +211 -0
  492. package/src/renderer/components/TaskLabelManager.tsx +472 -0
  493. package/src/renderer/components/TaskQueuePanel.tsx +144 -0
  494. package/src/renderer/components/TaskQuickActions.tsx +492 -0
  495. package/src/renderer/components/TaskTimeline.tsx +216 -0
  496. package/src/renderer/components/TaskView.tsx +162 -0
  497. package/src/renderer/components/TeamsSettings.tsx +518 -0
  498. package/src/renderer/components/TelegramSettings.tsx +421 -0
  499. package/src/renderer/components/Toast.tsx +76 -0
  500. package/src/renderer/components/TraySettings.tsx +189 -0
  501. package/src/renderer/components/TwitchSettings.tsx +511 -0
  502. package/src/renderer/components/UpdateSettings.tsx +295 -0
  503. package/src/renderer/components/VoiceIndicator.tsx +270 -0
  504. package/src/renderer/components/VoiceSettings.tsx +867 -0
  505. package/src/renderer/components/WhatsAppSettings.tsx +721 -0
  506. package/src/renderer/components/WorkingStateEditor.tsx +309 -0
  507. package/src/renderer/components/WorkingStateHistory.tsx +481 -0
  508. package/src/renderer/components/WorkspaceSelector.tsx +150 -0
  509. package/src/renderer/components/XSettings.tsx +311 -0
  510. package/src/renderer/global.d.ts +9 -0
  511. package/src/renderer/hooks/useAgentContext.ts +153 -0
  512. package/src/renderer/hooks/useOnboardingFlow.ts +548 -0
  513. package/src/renderer/hooks/useVoiceInput.ts +268 -0
  514. package/src/renderer/index.html +12 -0
  515. package/src/renderer/main.tsx +10 -0
  516. package/src/renderer/public/cowork-os-logo.png +0 -0
  517. package/src/renderer/quick-input.html +164 -0
  518. package/src/renderer/styles/index.css +14504 -0
  519. package/src/renderer/utils/agentMessages.ts +749 -0
  520. package/src/renderer/utils/voice-directives.ts +169 -0
  521. package/src/shared/channelMessages.ts +213 -0
  522. package/src/shared/types.ts +3608 -0
  523. package/tsconfig.electron.json +26 -0
  524. package/tsconfig.json +26 -0
  525. package/tsconfig.node.json +10 -0
  526. package/vite.config.ts +23 -0
@@ -0,0 +1,775 @@
1
+ import { useState, useEffect, useRef } from 'react';
2
+ import { Sidebar } from './components/Sidebar';
3
+ import { MainContent } from './components/MainContent';
4
+ import { RightPanel } from './components/RightPanel';
5
+ import { Settings } from './components/Settings';
6
+ import { DisclaimerModal } from './components/DisclaimerModal';
7
+ import { Onboarding } from './components/Onboarding';
8
+ // TaskQueuePanel moved to RightPanel
9
+ import { ToastContainer } from './components/Toast';
10
+ import { QuickTaskFAB } from './components/QuickTaskFAB';
11
+ import { NotificationPanel } from './components/NotificationPanel';
12
+ import { Task, Workspace, TaskEvent, LLMModelInfo, LLMProviderInfo, SuccessCriteria, UpdateInfo, ThemeMode, AccentColor, QueueStatus, ToastNotification } from '../shared/types';
13
+
14
+
15
+ // Helper to get effective theme based on system preference
16
+ function getEffectiveTheme(themeMode: ThemeMode): 'light' | 'dark' {
17
+ if (themeMode === 'system') {
18
+ return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
19
+ }
20
+ return themeMode;
21
+ }
22
+
23
+ type AppView = 'main' | 'settings';
24
+
25
+ export function App() {
26
+ const [currentWorkspace, setCurrentWorkspace] = useState<Workspace | null>(null);
27
+ const [tasks, setTasks] = useState<Task[]>([]);
28
+ const [selectedTaskId, setSelectedTaskId] = useState<string | null>(null);
29
+ const [currentView, setCurrentView] = useState<AppView>('main');
30
+ const [settingsTab, setSettingsTab] = useState<'appearance' | 'llm' | 'search' | 'telegram' | 'slack' | 'whatsapp' | 'teams' | 'morechannels' | 'updates' | 'guardrails' | 'queue' | 'skills' | 'scheduled' | 'voice'>('appearance');
31
+ const [events, setEvents] = useState<TaskEvent[]>([]);
32
+
33
+ // Model selection state
34
+ const [selectedModel, setSelectedModel] = useState<string>('opus-4-5');
35
+ const [availableModels, setAvailableModels] = useState<LLMModelInfo[]>([]);
36
+ const [_availableProviders, setAvailableProviders] = useState<LLMProviderInfo[]>([]);
37
+
38
+ // Update notification state
39
+ const [updateInfo, setUpdateInfo] = useState<UpdateInfo | null>(null);
40
+ const [updateDismissed, setUpdateDismissed] = useState(false);
41
+
42
+ // Theme state (loaded from main process on mount)
43
+ const [themeMode, setThemeMode] = useState<ThemeMode>('dark');
44
+ const [accentColor, setAccentColor] = useState<AccentColor>('cyan');
45
+
46
+ // Queue state
47
+ const [queueStatus, setQueueStatus] = useState<QueueStatus | null>(null);
48
+ const [toasts, setToasts] = useState<ToastNotification[]>([]);
49
+
50
+ // Sidebar collapse state
51
+ const [leftSidebarCollapsed, setLeftSidebarCollapsed] = useState(false);
52
+ const [rightSidebarCollapsed, setRightSidebarCollapsed] = useState(false);
53
+
54
+ // Ref to track current tasks for use in event handlers (avoids stale closure)
55
+ const tasksRef = useRef<Task[]>([]);
56
+
57
+ // Disclaimer state (null = loading)
58
+ const [disclaimerAccepted, setDisclaimerAccepted] = useState<boolean | null>(null);
59
+ // Onboarding state (null = loading)
60
+ const [onboardingCompleted, setOnboardingCompleted] = useState<boolean | null>(null);
61
+ // Timestamp of when onboarding was completed
62
+ const [onboardingCompletedAt, setOnboardingCompletedAt] = useState<string | undefined>(undefined);
63
+
64
+ const handleDisclaimerAccept = (dontShowAgain: boolean) => {
65
+ // Save to main process for persistence
66
+ window.electronAPI.saveAppearanceSettings({ disclaimerAccepted: dontShowAgain });
67
+ setDisclaimerAccepted(true);
68
+ };
69
+
70
+ const handleOnboardingComplete = (dontShowAgain: boolean) => {
71
+ const timestamp = new Date().toISOString();
72
+ // Save to main process for persistence
73
+ // If dontShowAgain is true, mark as completed with timestamp
74
+ // If false, just save the timestamp but don't mark as completed (user can see it again next time)
75
+ window.electronAPI.saveAppearanceSettings({
76
+ onboardingCompleted: dontShowAgain,
77
+ onboardingCompletedAt: timestamp,
78
+ });
79
+ setOnboardingCompleted(true); // Always allow proceeding to main app
80
+ setOnboardingCompletedAt(timestamp);
81
+ // Refresh LLM config after onboarding (user may have configured a provider)
82
+ loadLLMConfig();
83
+ };
84
+
85
+ const handleShowOnboarding = () => {
86
+ // Reset onboarding state to show the wizard again
87
+ setOnboardingCompleted(false);
88
+ // Close settings view if open
89
+ setCurrentView('main');
90
+ };
91
+
92
+ // Load LLM config status
93
+ const loadLLMConfig = async () => {
94
+ try {
95
+ const config = await window.electronAPI.getLLMConfigStatus();
96
+ setSelectedModel(config.currentModel);
97
+ setAvailableModels(config.models);
98
+ setAvailableProviders(config.providers);
99
+ } catch (error) {
100
+ console.error('Failed to load LLM config:', error);
101
+ }
102
+ };
103
+
104
+ // Load LLM config on mount
105
+ useEffect(() => {
106
+ loadLLMConfig();
107
+ }, []);
108
+
109
+ // Load appearance settings on mount
110
+ useEffect(() => {
111
+ const loadAppearanceSettings = async () => {
112
+ try {
113
+ const settings = await window.electronAPI.getAppearanceSettings();
114
+ setThemeMode(settings.themeMode);
115
+ setAccentColor(settings.accentColor);
116
+ setDisclaimerAccepted(settings.disclaimerAccepted ?? false);
117
+ setOnboardingCompleted(settings.onboardingCompleted ?? false);
118
+ setOnboardingCompletedAt(settings.onboardingCompletedAt);
119
+ } catch (error) {
120
+ console.error('Failed to load appearance settings:', error);
121
+ setDisclaimerAccepted(false);
122
+ setOnboardingCompleted(false);
123
+ setOnboardingCompletedAt(undefined);
124
+ }
125
+ };
126
+ loadAppearanceSettings();
127
+ }, []);
128
+
129
+ // Check for migration status and show one-time notification if needed
130
+ // This handles the case where the app was renamed from cowork-oss to cowork-os
131
+ // and encrypted credentials (API keys) need to be re-entered
132
+ const migrationCheckDone = useRef(false);
133
+ useEffect(() => {
134
+ // Prevent double execution in React StrictMode
135
+ if (migrationCheckDone.current) return;
136
+ migrationCheckDone.current = true;
137
+
138
+ const checkMigrationStatus = async () => {
139
+ try {
140
+ const status = await window.electronAPI.getMigrationStatus();
141
+
142
+ // If migration happened but notification hasn't been dismissed, show info toast
143
+ if (status.migrated && !status.notificationDismissed) {
144
+ const id = `migration-notice-${Date.now()}`;
145
+ const toast: ToastNotification = {
146
+ id,
147
+ type: 'info',
148
+ title: 'Welcome to CoWork OS',
149
+ message: 'Your data was migrated successfully. Due to macOS security, API keys need to be re-entered.',
150
+ action: {
151
+ label: 'Open Settings',
152
+ callback: () => {
153
+ setCurrentView('settings');
154
+ setSettingsTab('llm');
155
+ },
156
+ },
157
+ };
158
+ setToasts(prev => [...prev, toast]);
159
+
160
+ // Longer auto-dismiss for this important notification (30 seconds)
161
+ setTimeout(() => {
162
+ setToasts(prev => prev.filter(t => t.id !== id));
163
+ }, 30000);
164
+
165
+ // Mark notification as dismissed so it only shows once
166
+ await window.electronAPI.dismissMigrationNotification();
167
+ }
168
+ } catch (error) {
169
+ console.error('Failed to check migration status:', error);
170
+ }
171
+ };
172
+ checkMigrationStatus();
173
+ }, []);
174
+
175
+ // Load queue status and subscribe to updates
176
+ useEffect(() => {
177
+ const loadQueueStatus = async () => {
178
+ try {
179
+ const status = await window.electronAPI.getQueueStatus();
180
+ setQueueStatus(status);
181
+ } catch (error) {
182
+ console.error('Failed to load queue status:', error);
183
+ }
184
+ };
185
+
186
+ loadQueueStatus();
187
+
188
+ const unsubscribe = window.electronAPI.onQueueUpdate((status) => {
189
+ setQueueStatus(status);
190
+ });
191
+
192
+ return unsubscribe;
193
+ }, []);
194
+
195
+ // Check for updates on mount
196
+ useEffect(() => {
197
+ const checkUpdates = async () => {
198
+ try {
199
+ const info = await window.electronAPI.checkForUpdates();
200
+ if (info.available) {
201
+ setUpdateInfo(info);
202
+ }
203
+ } catch (error) {
204
+ // Silently ignore update check failures
205
+ console.log('Update check skipped:', error);
206
+ }
207
+ };
208
+ // Delay check to not block app startup
209
+ const timeoutId = setTimeout(checkUpdates, 3000);
210
+ return () => clearTimeout(timeoutId);
211
+ }, []);
212
+
213
+ // Apply theme classes to root element
214
+ useEffect(() => {
215
+ const root = document.documentElement;
216
+ const effectiveTheme = getEffectiveTheme(themeMode);
217
+
218
+ // Remove existing theme classes
219
+ root.classList.remove('theme-light', 'theme-dark');
220
+
221
+ // Apply theme class (only light needs explicit class, dark is default)
222
+ if (effectiveTheme === 'light') {
223
+ root.classList.add('theme-light');
224
+ }
225
+
226
+ // Remove existing accent classes
227
+ root.classList.remove('accent-cyan', 'accent-blue', 'accent-purple', 'accent-pink', 'accent-rose', 'accent-orange', 'accent-green', 'accent-teal');
228
+
229
+ // Apply accent class
230
+ root.classList.add(`accent-${accentColor}`);
231
+ }, [themeMode, accentColor]);
232
+
233
+ // Listen for system theme changes when in 'system' mode
234
+ useEffect(() => {
235
+ if (themeMode !== 'system') return;
236
+
237
+ const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
238
+ const handleChange = () => {
239
+ const root = document.documentElement;
240
+ root.classList.remove('theme-light', 'theme-dark');
241
+ if (!mediaQuery.matches) {
242
+ root.classList.add('theme-light');
243
+ }
244
+ };
245
+
246
+ mediaQuery.addEventListener('change', handleChange);
247
+ return () => mediaQuery.removeEventListener('change', handleChange);
248
+ }, [themeMode]);
249
+
250
+ useEffect(() => {
251
+ console.log('App mounted');
252
+ console.log('window.electronAPI available:', !!window.electronAPI);
253
+ if (window.electronAPI) {
254
+ console.log('electronAPI methods:', Object.keys(window.electronAPI));
255
+ }
256
+ }, []);
257
+
258
+ // Auto-load temp workspace on mount if no workspace is selected
259
+ useEffect(() => {
260
+ const initWorkspace = async () => {
261
+ if (!currentWorkspace) {
262
+ try {
263
+ const tempWorkspace = await window.electronAPI.getTempWorkspace();
264
+ setCurrentWorkspace(tempWorkspace);
265
+ } catch (error) {
266
+ console.error('Failed to initialize temp workspace:', error);
267
+ }
268
+ }
269
+ };
270
+ initWorkspace();
271
+ }, []);
272
+
273
+ // Load tasks when workspace is set
274
+ useEffect(() => {
275
+ if (currentWorkspace) {
276
+ loadTasks();
277
+ }
278
+ }, [currentWorkspace]);
279
+
280
+ // Toast helper functions
281
+ const addToast = (toast: Omit<ToastNotification, 'id'>) => {
282
+ const id = `toast-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
283
+ const newToast: ToastNotification = { ...toast, id };
284
+ setToasts(prev => [...prev, newToast]);
285
+ // Auto-dismiss after 5 seconds
286
+ setTimeout(() => dismissToast(id), 5000);
287
+ };
288
+
289
+ const dismissToast = (id: string) => {
290
+ setToasts(prev => prev.filter(t => t.id !== id));
291
+ };
292
+
293
+ // Keep tasksRef in sync with tasks state
294
+ useEffect(() => {
295
+ tasksRef.current = tasks;
296
+ }, [tasks]);
297
+
298
+ // Subscribe to all task events to update task status
299
+ useEffect(() => {
300
+ const unsubscribe = window.electronAPI.onTaskEvent((event: TaskEvent) => {
301
+ // Update task status based on event type
302
+ const statusMap: Record<string, Task['status']> = {
303
+ 'task_created': 'pending',
304
+ 'task_queued': 'queued',
305
+ 'task_dequeued': 'planning',
306
+ 'executing': 'executing',
307
+ 'step_started': 'executing',
308
+ 'step_completed': 'executing',
309
+ 'task_completed': 'completed',
310
+ 'task_paused': 'paused',
311
+ 'approval_requested': 'blocked',
312
+ 'approval_granted': 'executing',
313
+ 'approval_denied': 'failed',
314
+ 'error': 'failed',
315
+ 'task_cancelled': 'cancelled',
316
+ };
317
+
318
+ // Check if this is a new task we don't know about (e.g., sub-agent created)
319
+ const isNewTask = !tasksRef.current.some(t => t.id === event.taskId);
320
+ if (isNewTask && event.type === 'task_created') {
321
+ // Refresh task list to include the new sub-agent task
322
+ loadTasks();
323
+ return;
324
+ }
325
+
326
+ const newStatus = event.type === 'task_status' ? event.payload?.status : statusMap[event.type];
327
+ if (newStatus) {
328
+ setTasks(prev => prev.map(t =>
329
+ t.id === event.taskId ? { ...t, status: newStatus } : t
330
+ ));
331
+ }
332
+
333
+ if (event.type === 'approval_granted') {
334
+ void window.electronAPI.resumeTask(event.taskId);
335
+ }
336
+
337
+ // Show toast notifications for task completion/failure
338
+ if (event.type === 'task_completed') {
339
+ const task = tasksRef.current.find(t => t.id === event.taskId);
340
+ addToast({
341
+ type: 'success',
342
+ title: '✅ Task Done!',
343
+ message: task?.title || 'Task finished successfully',
344
+ taskId: event.taskId,
345
+ });
346
+ } else if (event.type === 'error') {
347
+ const task = tasksRef.current.find(t => t.id === event.taskId);
348
+ addToast({
349
+ type: 'error',
350
+ title: 'Task Failed',
351
+ message: task?.title || 'Task encountered an error',
352
+ taskId: event.taskId,
353
+ });
354
+ }
355
+
356
+ // Add event to events list if it's for the selected task
357
+ if (event.taskId === selectedTaskId) {
358
+ setEvents(prev => [...prev, event]);
359
+ }
360
+ });
361
+
362
+ return unsubscribe;
363
+ }, [selectedTaskId]);
364
+
365
+ // Load historical events when task is selected
366
+ useEffect(() => {
367
+ if (!selectedTaskId) {
368
+ setEvents([]);
369
+ return;
370
+ }
371
+
372
+ // Load historical events from database
373
+ const loadHistoricalEvents = async () => {
374
+ try {
375
+ const historicalEvents = await window.electronAPI.getTaskEvents(selectedTaskId);
376
+ setEvents(historicalEvents);
377
+ } catch (error) {
378
+ console.error('Failed to load historical events:', error);
379
+ setEvents([]);
380
+ }
381
+ };
382
+
383
+ loadHistoricalEvents();
384
+ }, [selectedTaskId]);
385
+
386
+ const loadTasks = async () => {
387
+ try {
388
+ const loadedTasks = await window.electronAPI.listTasks();
389
+ setTasks(loadedTasks);
390
+ } catch (error) {
391
+ console.error('Failed to load tasks:', error);
392
+ }
393
+ };
394
+
395
+ // Handle workspace change - opens folder selection dialog directly
396
+ const handleChangeWorkspace = async () => {
397
+ try {
398
+ // Get list of existing workspaces for reference
399
+ const existingWorkspaces = await window.electronAPI.listWorkspaces();
400
+
401
+ // Open folder selection dialog
402
+ const folderPath = await window.electronAPI.selectFolder();
403
+ if (!folderPath) return; // User cancelled
404
+
405
+ // Check if this folder is already a workspace
406
+ const existingWorkspace = existingWorkspaces.find((w: Workspace) => w.path === folderPath);
407
+ if (existingWorkspace) {
408
+ setCurrentWorkspace(existingWorkspace);
409
+ return;
410
+ }
411
+
412
+ // Create a new workspace for this folder
413
+ const folderName = folderPath.split('/').pop() || 'Workspace';
414
+ const workspace = await window.electronAPI.createWorkspace({
415
+ name: folderName,
416
+ path: folderPath,
417
+ permissions: {
418
+ read: true,
419
+ write: true,
420
+ delete: true,
421
+ network: true,
422
+ shell: false,
423
+ },
424
+ });
425
+
426
+ setCurrentWorkspace(workspace);
427
+ } catch (error) {
428
+ console.error('Failed to change workspace:', error);
429
+ }
430
+ };
431
+
432
+ const handleCreateTask = async (title: string, prompt: string, options?: { successCriteria?: SuccessCriteria; maxAttempts?: number }) => {
433
+ if (!currentWorkspace) return;
434
+
435
+ try {
436
+ const task = await window.electronAPI.createTask({
437
+ title,
438
+ prompt,
439
+ workspaceId: currentWorkspace.id,
440
+ ...(options?.successCriteria && { successCriteria: options.successCriteria }),
441
+ ...(options?.maxAttempts && { maxAttempts: options.maxAttempts }),
442
+ });
443
+
444
+ setTasks(prev => [task, ...prev]);
445
+ setSelectedTaskId(task.id);
446
+ } catch (error: unknown) {
447
+ console.error('Failed to create task:', error);
448
+ // Check if it's an API key error and prompt user to configure settings
449
+ const errorMessage = error instanceof Error ? error.message : 'Failed to create task';
450
+ if (errorMessage.includes('API key') || errorMessage.includes('credentials')) {
451
+ const openSettings = window.confirm(
452
+ `${errorMessage}\n\nWould you like to open Settings to configure your LLM provider?`
453
+ );
454
+ if (openSettings) {
455
+ setCurrentView('settings');
456
+ }
457
+ } else {
458
+ alert(`Error: ${errorMessage}`);
459
+ }
460
+ }
461
+ };
462
+
463
+ const selectedTask = tasks.find(t => t.id === selectedTaskId);
464
+
465
+ const handleSendMessage = async (message: string) => {
466
+ if (!selectedTaskId) return;
467
+
468
+ try {
469
+ await window.electronAPI.sendMessage(selectedTaskId, message);
470
+ } catch (error: unknown) {
471
+ console.error('Failed to send message:', error);
472
+ const errorMessage = error instanceof Error ? error.message : 'Failed to send message';
473
+ alert(`Error: ${errorMessage}`);
474
+ }
475
+ };
476
+
477
+ const handleCancelTask = async () => {
478
+ if (!selectedTaskId) return;
479
+
480
+ try {
481
+ await window.electronAPI.cancelTask(selectedTaskId);
482
+ } catch (error: unknown) {
483
+ console.error('Failed to cancel task:', error);
484
+ const errorMessage = error instanceof Error ? error.message : 'Failed to cancel task';
485
+ alert(`Error: ${errorMessage}`);
486
+ }
487
+ };
488
+
489
+ const handleCancelTaskById = async (taskId: string) => {
490
+ try {
491
+ await window.electronAPI.cancelTask(taskId);
492
+ } catch (error: unknown) {
493
+ console.error('Failed to cancel task:', error);
494
+ }
495
+ };
496
+
497
+ const handleQuickTask = async (prompt: string) => {
498
+ if (!currentWorkspace) return;
499
+
500
+ const title = prompt.slice(0, 50) + (prompt.length > 50 ? '...' : '');
501
+ await handleCreateTask(title, prompt);
502
+ };
503
+
504
+ const handleModelChange = (modelKey: string) => {
505
+ setSelectedModel(modelKey);
506
+ // Persist to main process
507
+ window.electronAPI.setLLMModel(modelKey);
508
+ // When model changes during a task, clear the current task to start fresh
509
+ if (selectedTaskId) {
510
+ setSelectedTaskId(null);
511
+ setEvents([]);
512
+ }
513
+ };
514
+
515
+ const handleThemeChange = (theme: ThemeMode) => {
516
+ setThemeMode(theme);
517
+ // Persist to main process
518
+ window.electronAPI.saveAppearanceSettings({ themeMode: theme, accentColor });
519
+ };
520
+
521
+ const handleAccentChange = (accent: AccentColor) => {
522
+ setAccentColor(accent);
523
+ // Persist to main process
524
+ window.electronAPI.saveAppearanceSettings({ themeMode, accentColor: accent });
525
+ };
526
+
527
+ // Show loading state while checking disclaimer/onboarding status
528
+ if (disclaimerAccepted === null || onboardingCompleted === null) {
529
+ return (
530
+ <div className="app">
531
+ <div className="title-bar" />
532
+ </div>
533
+ );
534
+ }
535
+
536
+ // Show disclaimer modal on first launch
537
+ if (!disclaimerAccepted) {
538
+ return (
539
+ <div className="app">
540
+ <div className="title-bar" />
541
+ <DisclaimerModal onAccept={handleDisclaimerAccept} />
542
+ </div>
543
+ );
544
+ }
545
+
546
+ // Show cinematic onboarding after disclaimer is accepted but before main app
547
+ if (!onboardingCompleted) {
548
+ return (
549
+ <div className="app">
550
+ <Onboarding onComplete={handleOnboardingComplete} />
551
+ </div>
552
+ );
553
+ }
554
+
555
+ return (
556
+ <div className="app">
557
+ <div className="title-bar">
558
+ <div className="title-bar-left">
559
+ <button
560
+ type="button"
561
+ style={{
562
+ display: 'flex',
563
+ alignItems: 'center',
564
+ justifyContent: 'center',
565
+ width: '32px',
566
+ height: '32px',
567
+ borderRadius: '6px',
568
+ backgroundColor: 'transparent',
569
+ border: 'none',
570
+ cursor: 'pointer',
571
+ position: 'relative',
572
+ zIndex: 1,
573
+ // @ts-expect-error - webkit property for Electron
574
+ WebkitAppRegion: 'no-drag',
575
+ }}
576
+ onClick={() => setLeftSidebarCollapsed(!leftSidebarCollapsed)}
577
+ title={leftSidebarCollapsed ? 'Show sidebar' : 'Hide sidebar'}
578
+ >
579
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#6b7280" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{ display: 'block', flexShrink: 0 }}>
580
+ <rect x="3" y="3" width="18" height="18" rx="2" ry="2" />
581
+ <line x1="9" y1="3" x2="9" y2="21" />
582
+ </svg>
583
+ </button>
584
+ </div>
585
+ <div className="title-bar-actions">
586
+ <button
587
+ type="button"
588
+ style={{
589
+ display: 'flex',
590
+ alignItems: 'center',
591
+ justifyContent: 'center',
592
+ width: '32px',
593
+ height: '32px',
594
+ borderRadius: '6px',
595
+ backgroundColor: 'transparent',
596
+ border: 'none',
597
+ cursor: 'pointer',
598
+ position: 'relative',
599
+ zIndex: 1,
600
+ // @ts-expect-error - webkit property for Electron
601
+ WebkitAppRegion: 'no-drag',
602
+ }}
603
+ onClick={() => {
604
+ const effectiveTheme = getEffectiveTheme(themeMode);
605
+ handleThemeChange(effectiveTheme === 'dark' ? 'light' : 'dark');
606
+ }}
607
+ title={`Switch to ${getEffectiveTheme(themeMode) === 'dark' ? 'light' : 'dark'} mode`}
608
+ >
609
+ {getEffectiveTheme(themeMode) === 'dark' ? (
610
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#6b7280" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{ display: 'block', flexShrink: 0 }}>
611
+ <circle cx="12" cy="12" r="5" />
612
+ <line x1="12" y1="1" x2="12" y2="3" />
613
+ <line x1="12" y1="21" x2="12" y2="23" />
614
+ <line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
615
+ <line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
616
+ <line x1="1" y1="12" x2="3" y2="12" />
617
+ <line x1="21" y1="12" x2="23" y2="12" />
618
+ <line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
619
+ <line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
620
+ </svg>
621
+ ) : (
622
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#6b7280" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{ display: 'block', flexShrink: 0 }}>
623
+ <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
624
+ </svg>
625
+ )}
626
+ </button>
627
+ <NotificationPanel
628
+ onNotificationClick={(notification) => {
629
+ // Prioritize taskId to show the completed task result
630
+ if (notification.taskId) {
631
+ const task = tasks.find(t => t.id === notification.taskId);
632
+ if (task) {
633
+ setSelectedTaskId(task.id);
634
+ setCurrentView('main');
635
+ return;
636
+ }
637
+ }
638
+ // Fall back to scheduled tasks settings if only cronJobId
639
+ if (notification.cronJobId) {
640
+ setSettingsTab('scheduled');
641
+ setCurrentView('settings');
642
+ }
643
+ }}
644
+ />
645
+ <button
646
+ type="button"
647
+ style={{
648
+ display: 'flex',
649
+ alignItems: 'center',
650
+ justifyContent: 'center',
651
+ width: '32px',
652
+ height: '32px',
653
+ borderRadius: '6px',
654
+ backgroundColor: 'transparent',
655
+ border: 'none',
656
+ cursor: 'pointer',
657
+ position: 'relative',
658
+ zIndex: 1,
659
+ // @ts-expect-error - webkit property for Electron
660
+ WebkitAppRegion: 'no-drag',
661
+ }}
662
+ onClick={() => setRightSidebarCollapsed(!rightSidebarCollapsed)}
663
+ title={rightSidebarCollapsed ? 'Show panel' : 'Hide panel'}
664
+ >
665
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#6b7280" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{ display: 'block', flexShrink: 0 }}>
666
+ <rect x="3" y="3" width="18" height="18" rx="2" ry="2" />
667
+ <line x1="15" y1="3" x2="15" y2="21" />
668
+ </svg>
669
+ </button>
670
+ </div>
671
+ </div>
672
+ {/* Update notification banner */}
673
+ {updateInfo?.available && !updateDismissed && (
674
+ <div className="update-banner">
675
+ <div className="update-banner-content">
676
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
677
+ <path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3" />
678
+ </svg>
679
+ <span>
680
+ New version <strong>v{updateInfo.latestVersion}</strong> is available!
681
+ </span>
682
+ <button
683
+ className="update-banner-link"
684
+ onClick={() => {
685
+ setSettingsTab('updates');
686
+ setCurrentView('settings');
687
+ }}
688
+ >
689
+ View Release
690
+ </button>
691
+ </div>
692
+ <button
693
+ className="update-banner-dismiss"
694
+ onClick={() => setUpdateDismissed(true)}
695
+ aria-label="Dismiss update notification"
696
+ >
697
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
698
+ <path d="M18 6L6 18M6 6l12 12" />
699
+ </svg>
700
+ </button>
701
+ </div>
702
+ )}
703
+ {currentView === 'main' && (
704
+ <>
705
+ <div className={`app-layout ${leftSidebarCollapsed ? 'left-collapsed' : ''} ${rightSidebarCollapsed ? 'right-collapsed' : ''}`}>
706
+ {!leftSidebarCollapsed && (
707
+ <Sidebar
708
+ workspace={currentWorkspace}
709
+ tasks={tasks}
710
+ selectedTaskId={selectedTaskId}
711
+ onSelectTask={setSelectedTaskId}
712
+ onOpenSettings={() => setCurrentView('settings')}
713
+ onTasksChanged={loadTasks}
714
+ />
715
+ )}
716
+ <MainContent
717
+ task={selectedTask}
718
+ selectedTaskId={selectedTaskId}
719
+ workspace={currentWorkspace}
720
+ events={events}
721
+ onSendMessage={handleSendMessage}
722
+ onCreateTask={handleCreateTask}
723
+ onChangeWorkspace={handleChangeWorkspace}
724
+ onSelectWorkspace={(workspace) => setCurrentWorkspace(workspace)}
725
+ onOpenSettings={(tab) => {
726
+ setSettingsTab(tab || 'appearance');
727
+ setCurrentView('settings');
728
+ }}
729
+ onStopTask={handleCancelTask}
730
+ selectedModel={selectedModel}
731
+ availableModels={availableModels}
732
+ onModelChange={handleModelChange}
733
+ />
734
+ {!rightSidebarCollapsed && (
735
+ <RightPanel
736
+ task={selectedTask}
737
+ workspace={currentWorkspace}
738
+ events={events}
739
+ tasks={tasks}
740
+ queueStatus={queueStatus}
741
+ onSelectTask={setSelectedTaskId}
742
+ onCancelTask={handleCancelTaskById}
743
+ />
744
+ )}
745
+ </div>
746
+
747
+ {/* Quick Task FAB */}
748
+ {currentWorkspace && (
749
+ <QuickTaskFAB onCreateTask={handleQuickTask} />
750
+ )}
751
+
752
+ {/* Toast Notifications */}
753
+ <ToastContainer
754
+ toasts={toasts}
755
+ onDismiss={dismissToast}
756
+ onTaskClick={setSelectedTaskId}
757
+ />
758
+ </>
759
+ )}
760
+ {currentView === 'settings' && (
761
+ <Settings
762
+ onBack={() => setCurrentView('main')}
763
+ onSettingsChanged={loadLLMConfig}
764
+ themeMode={themeMode}
765
+ accentColor={accentColor}
766
+ onThemeChange={handleThemeChange}
767
+ onAccentChange={handleAccentChange}
768
+ initialTab={settingsTab}
769
+ onShowOnboarding={handleShowOnboarding}
770
+ onboardingCompletedAt={onboardingCompletedAt}
771
+ />
772
+ )}
773
+ </div>
774
+ );
775
+ }