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,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SearchTools = void 0;
4
+ const search_1 = require("../search");
5
+ /**
6
+ * SearchTools implements web search operations for the agent
7
+ */
8
+ class SearchTools {
9
+ constructor(workspace, daemon, taskId) {
10
+ this.workspace = workspace;
11
+ this.daemon = daemon;
12
+ this.taskId = taskId;
13
+ }
14
+ /**
15
+ * Update the workspace for this tool
16
+ */
17
+ setWorkspace(workspace) {
18
+ this.workspace = workspace;
19
+ }
20
+ /**
21
+ * Perform a web search with automatic fallback support
22
+ */
23
+ async webSearch(input) {
24
+ // Check if any provider is configured
25
+ if (!search_1.SearchProviderFactory.isAnyProviderConfigured()) {
26
+ // Return a helpful response instead of throwing an error
27
+ // This allows the LLM to inform the user gracefully
28
+ this.daemon.logEvent(this.taskId, 'log', {
29
+ message: 'Web search is not available - no search provider configured',
30
+ });
31
+ return {
32
+ query: input.query,
33
+ searchType: input.searchType || 'web',
34
+ results: [],
35
+ provider: 'none',
36
+ metadata: {
37
+ error: 'Web search is not configured. To enable web search, please configure a search provider in Settings > Web Search. Supported providers: Tavily, Brave Search, SerpAPI, or Google Custom Search.',
38
+ notConfigured: true,
39
+ },
40
+ };
41
+ }
42
+ const settings = search_1.SearchProviderFactory.loadSettings();
43
+ if (!settings.primaryProvider && !input.provider) {
44
+ // This shouldn't happen after the loadSettings auto-detection fix,
45
+ // but keep as a safety net
46
+ this.daemon.logEvent(this.taskId, 'log', {
47
+ message: 'Web search provider not selected - auto-selecting...',
48
+ });
49
+ // Clear cache and reload to trigger auto-detection
50
+ search_1.SearchProviderFactory.clearCache();
51
+ const reloadedSettings = search_1.SearchProviderFactory.loadSettings();
52
+ if (!reloadedSettings.primaryProvider) {
53
+ return {
54
+ query: input.query,
55
+ searchType: input.searchType || 'web',
56
+ results: [],
57
+ provider: 'none',
58
+ metadata: {
59
+ error: 'No search provider is selected. Please configure one in Settings > Web Search.',
60
+ notConfigured: true,
61
+ },
62
+ };
63
+ }
64
+ }
65
+ const searchQuery = {
66
+ query: input.query,
67
+ searchType: input.searchType || 'web',
68
+ maxResults: Math.min(input.maxResults || 10, 20), // Cap at 20 results
69
+ dateRange: input.dateRange,
70
+ region: input.region,
71
+ provider: input.provider,
72
+ };
73
+ const providerName = input.provider || settings.primaryProvider || 'unknown';
74
+ this.daemon.logEvent(this.taskId, 'log', {
75
+ message: `Searching ${searchQuery.searchType}: "${input.query}" via ${providerName}`,
76
+ });
77
+ // Use searchWithFallback for automatic fallback support
78
+ const response = await search_1.SearchProviderFactory.searchWithFallback(searchQuery);
79
+ this.daemon.logEvent(this.taskId, 'tool_result', {
80
+ tool: 'web_search',
81
+ result: {
82
+ query: input.query,
83
+ searchType: searchQuery.searchType,
84
+ resultCount: response.results.length,
85
+ provider: response.provider,
86
+ },
87
+ });
88
+ return response;
89
+ }
90
+ }
91
+ exports.SearchTools = SearchTools;
@@ -0,0 +1,574 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports._testUtils = exports.ShellTools = void 0;
4
+ const child_process_1 = require("child_process");
5
+ const guardrail_manager_1 = require("../../guardrails/guardrail-manager");
6
+ // Limits to prevent runaway commands
7
+ const MAX_TIMEOUT = 5 * 60 * 1000; // 5 minutes max
8
+ const DEFAULT_TIMEOUT = 60 * 1000; // 1 minute default
9
+ const MAX_OUTPUT_SIZE = 100 * 1024; // 100KB max output
10
+ /**
11
+ * Validate that a PID is a safe positive integer
12
+ * Prevents command injection if PID is somehow not a number
13
+ */
14
+ function isValidPid(pid) {
15
+ return typeof pid === 'number' &&
16
+ Number.isInteger(pid) &&
17
+ pid > 0 &&
18
+ pid <= 4194304; // Max PID on Linux (can be configured higher, but this is safe default)
19
+ }
20
+ /**
21
+ * Check if a process with the given PID exists and is owned by the current user
22
+ * Returns false if the process doesn't exist or is owned by another user
23
+ */
24
+ function isProcessOwnedByCurrentUser(pid) {
25
+ if (!isValidPid(pid))
26
+ return false;
27
+ try {
28
+ // Use kill with signal 0 to check if process exists and we have permission to signal it
29
+ // This will throw EPERM if process exists but is owned by another user
30
+ // This will throw ESRCH if process doesn't exist
31
+ process.kill(pid, 0);
32
+ return true;
33
+ }
34
+ catch (error) {
35
+ // ESRCH = no such process (that's fine, process exited)
36
+ // EPERM = permission denied (process exists but owned by another user - DON'T KILL)
37
+ if (error.code === 'EPERM') {
38
+ console.warn(`[ShellTools] Process ${pid} exists but is owned by another user, skipping`);
39
+ return false;
40
+ }
41
+ // Process doesn't exist, that's fine
42
+ return false;
43
+ }
44
+ }
45
+ /**
46
+ * Validate username for safe use in shell commands
47
+ * Prevents command injection via USER environment variable
48
+ */
49
+ function isValidUsername(username) {
50
+ if (!username)
51
+ return false;
52
+ // Username must be alphanumeric, underscore, or dash (standard POSIX username chars)
53
+ // Max length 32 chars (common limit)
54
+ return /^[a-zA-Z0-9_-]{1,32}$/.test(username);
55
+ }
56
+ /**
57
+ * Get all descendant process IDs for a given parent PID
58
+ * Uses pgrep to find child processes recursively
59
+ * Only returns processes owned by the current user for security
60
+ */
61
+ function getDescendantPids(parentPid) {
62
+ if (!isValidPid(parentPid)) {
63
+ console.error(`[ShellTools] Invalid parent PID: ${parentPid}`);
64
+ return [];
65
+ }
66
+ const currentUser = process.env.USER;
67
+ // Validate username to prevent command injection
68
+ const safeUser = isValidUsername(currentUser) ? currentUser : undefined;
69
+ if (currentUser && !safeUser) {
70
+ console.warn(`[ShellTools] Invalid USER env var: ${currentUser}, skipping user filter`);
71
+ }
72
+ const descendants = [];
73
+ const toProcess = [parentPid];
74
+ const seen = new Set(); // Prevent infinite loops from circular references
75
+ while (toProcess.length > 0) {
76
+ const pid = toProcess.pop();
77
+ if (seen.has(pid))
78
+ continue;
79
+ seen.add(pid);
80
+ try {
81
+ // pgrep -P finds direct children of the given PID
82
+ // Add -U $USER to only find processes owned by current user (security)
83
+ const pgrepCmd = safeUser
84
+ ? `pgrep -P ${pid} -U ${safeUser}`
85
+ : `pgrep -P ${pid}`;
86
+ const output = (0, child_process_1.execSync)(pgrepCmd, {
87
+ encoding: 'utf-8',
88
+ timeout: 1000,
89
+ // Don't inherit env to avoid any injection via environment
90
+ env: { PATH: '/usr/bin:/bin' },
91
+ });
92
+ const childPids = output.trim().split('\n')
93
+ .filter(line => line.length > 0)
94
+ .map(line => parseInt(line, 10))
95
+ .filter(childPid => isValidPid(childPid) && !seen.has(childPid));
96
+ descendants.push(...childPids);
97
+ toProcess.push(...childPids);
98
+ }
99
+ catch {
100
+ // pgrep returns non-zero if no children found, which is fine
101
+ }
102
+ }
103
+ return descendants;
104
+ }
105
+ /**
106
+ * Kill a process and all its descendants
107
+ * Sends the signal to children first, then to the parent (bottom-up killing)
108
+ * Only kills processes owned by the current user for security
109
+ */
110
+ function killProcessTree(pid, signal) {
111
+ if (!isValidPid(pid)) {
112
+ console.error(`[ShellTools] Refusing to kill invalid PID: ${pid}`);
113
+ return;
114
+ }
115
+ const descendants = getDescendantPids(pid);
116
+ // Kill descendants first (in reverse order, deepest children first)
117
+ for (const descendantPid of descendants.reverse()) {
118
+ // Double-check ownership before killing each process
119
+ if (isProcessOwnedByCurrentUser(descendantPid)) {
120
+ try {
121
+ process.kill(descendantPid, signal);
122
+ }
123
+ catch {
124
+ // Process may have already exited
125
+ }
126
+ }
127
+ }
128
+ // Kill the parent process (also verify ownership)
129
+ if (isProcessOwnedByCurrentUser(pid)) {
130
+ try {
131
+ process.kill(pid, signal);
132
+ }
133
+ catch {
134
+ // Process may have already exited
135
+ }
136
+ }
137
+ }
138
+ /**
139
+ * ShellTools implements shell command execution with user approval
140
+ */
141
+ class ShellTools {
142
+ constructor(workspace, daemon, taskId) {
143
+ this.workspace = workspace;
144
+ this.daemon = daemon;
145
+ this.taskId = taskId;
146
+ this.recentApprovals = new Map();
147
+ this.approvalWindowMs = 2 * 60 * 1000;
148
+ // Track the currently running child process for stdin support
149
+ this.activeProcess = null;
150
+ // Track escalation timeouts so we can cancel them when process exits
151
+ this.escalationTimeouts = [];
152
+ // Prevent multiple concurrent kill attempts
153
+ this.killInProgress = false;
154
+ // Unique identifier for the current process session (prevents PID reuse issues)
155
+ this.processSessionId = 0;
156
+ // Track user-initiated kills to signal termination reason to agent
157
+ this.userKillRequested = false;
158
+ }
159
+ /**
160
+ * Update the workspace for this tool
161
+ */
162
+ setWorkspace(workspace) {
163
+ this.workspace = workspace;
164
+ }
165
+ /**
166
+ * Clear all pending escalation timeouts
167
+ * Called when process exits to prevent killing reused PIDs
168
+ */
169
+ clearEscalationTimeouts() {
170
+ for (const timeout of this.escalationTimeouts) {
171
+ clearTimeout(timeout);
172
+ }
173
+ this.escalationTimeouts = [];
174
+ this.killInProgress = false;
175
+ }
176
+ /**
177
+ * Send input to the currently running command's stdin
178
+ */
179
+ sendStdin(input) {
180
+ if (!this.activeProcess || !this.activeProcess.stdin || this.activeProcess.killed) {
181
+ return false;
182
+ }
183
+ try {
184
+ this.activeProcess.stdin.write(input);
185
+ // Echo the input to show it was sent
186
+ this.daemon.logEvent(this.taskId, 'command_output', {
187
+ type: 'stdin',
188
+ output: input,
189
+ });
190
+ return true;
191
+ }
192
+ catch (error) {
193
+ console.error('Failed to write to stdin:', error);
194
+ return false;
195
+ }
196
+ }
197
+ /**
198
+ * Check if a command is currently running
199
+ */
200
+ hasActiveProcess() {
201
+ return this.activeProcess !== null && !this.activeProcess.killed;
202
+ }
203
+ /**
204
+ * Kill the currently running command and all its child processes
205
+ * @param force - If true, send SIGKILL immediately. Otherwise, try SIGINT first, then SIGTERM, then SIGKILL.
206
+ */
207
+ killProcess(force = false) {
208
+ if (!this.activeProcess || this.activeProcess.killed) {
209
+ return false;
210
+ }
211
+ const pid = this.activeProcess.pid;
212
+ if (!isValidPid(pid)) {
213
+ console.error(`[ShellTools] Invalid PID for kill: ${pid}`);
214
+ return false;
215
+ }
216
+ // Prevent multiple concurrent kill chains (security: avoid race conditions)
217
+ if (this.killInProgress && !force) {
218
+ console.log(`[ShellTools] Kill already in progress, ignoring duplicate request`);
219
+ return true; // Return true since a kill is already underway
220
+ }
221
+ // Capture session ID to verify we're killing the right process in escalation timeouts
222
+ const currentSessionId = this.processSessionId;
223
+ // Mark this as a user-initiated kill so the close handler can signal the agent
224
+ this.userKillRequested = true;
225
+ if (force) {
226
+ // Force kill - immediate SIGKILL to entire process tree
227
+ // Clear any pending escalation timeouts first
228
+ this.clearEscalationTimeouts();
229
+ try {
230
+ killProcessTree(pid, 'SIGKILL');
231
+ this.daemon.logEvent(this.taskId, 'command_output', {
232
+ type: 'error',
233
+ output: '\n[Process tree force killed by user]\n',
234
+ });
235
+ return true;
236
+ }
237
+ catch (error) {
238
+ console.error('Failed to force kill process tree:', error);
239
+ return false;
240
+ }
241
+ }
242
+ // Mark kill as in progress to prevent duplicate escalation chains
243
+ this.killInProgress = true;
244
+ try {
245
+ // Send SIGINT (Ctrl+C) to gracefully interrupt the process tree
246
+ killProcessTree(pid, 'SIGINT');
247
+ this.daemon.logEvent(this.taskId, 'command_output', {
248
+ type: 'error',
249
+ output: '\n^C [Process tree interrupted by user]\n',
250
+ });
251
+ // Set up escalation: if still running after 2s, send SIGTERM to tree
252
+ // If still running after 4s, send SIGKILL to tree
253
+ // These timeouts are tracked so they can be cancelled if process exits
254
+ const childProcess = this.activeProcess;
255
+ const sigtermTimeout = setTimeout(() => {
256
+ // Verify this is still the same process session (prevents PID reuse attacks)
257
+ if (currentSessionId !== this.processSessionId) {
258
+ console.log(`[ShellTools] Session ID mismatch, skipping SIGTERM escalation`);
259
+ return;
260
+ }
261
+ if (childProcess && !childProcess.killed && childProcess.pid === pid) {
262
+ // Additional safety: verify we own this process before killing
263
+ if (!isProcessOwnedByCurrentUser(pid)) {
264
+ console.warn(`[ShellTools] Process ${pid} no longer owned by current user, skipping SIGTERM`);
265
+ return;
266
+ }
267
+ try {
268
+ killProcessTree(pid, 'SIGTERM');
269
+ this.daemon.logEvent(this.taskId, 'command_output', {
270
+ type: 'error',
271
+ output: '[Escalating to SIGTERM for process tree...]\n',
272
+ });
273
+ }
274
+ catch { /* Process may have exited */ }
275
+ }
276
+ }, 2000);
277
+ this.escalationTimeouts.push(sigtermTimeout);
278
+ const sigkillTimeout = setTimeout(() => {
279
+ // Verify this is still the same process session (prevents PID reuse attacks)
280
+ if (currentSessionId !== this.processSessionId) {
281
+ console.log(`[ShellTools] Session ID mismatch, skipping SIGKILL escalation`);
282
+ return;
283
+ }
284
+ if (childProcess && !childProcess.killed && childProcess.pid === pid) {
285
+ // Additional safety: verify we own this process before killing
286
+ if (!isProcessOwnedByCurrentUser(pid)) {
287
+ console.warn(`[ShellTools] Process ${pid} no longer owned by current user, skipping SIGKILL`);
288
+ return;
289
+ }
290
+ try {
291
+ killProcessTree(pid, 'SIGKILL');
292
+ this.daemon.logEvent(this.taskId, 'command_output', {
293
+ type: 'error',
294
+ output: '[Escalating to SIGKILL for process tree...]\n',
295
+ });
296
+ }
297
+ catch { /* Process may have exited */ }
298
+ }
299
+ }, 4000);
300
+ this.escalationTimeouts.push(sigkillTimeout);
301
+ return true;
302
+ }
303
+ catch (error) {
304
+ console.error('Failed to kill process tree:', error);
305
+ this.killInProgress = false;
306
+ // Try SIGTERM as fallback
307
+ try {
308
+ killProcessTree(pid, 'SIGTERM');
309
+ return true;
310
+ }
311
+ catch {
312
+ // Last resort: SIGKILL
313
+ try {
314
+ killProcessTree(pid, 'SIGKILL');
315
+ return true;
316
+ }
317
+ catch {
318
+ return false;
319
+ }
320
+ }
321
+ }
322
+ }
323
+ /**
324
+ * Execute a shell command (requires user approval)
325
+ * Note: We don't check workspace.permissions.shell here because
326
+ * shell commands always require explicit user approval via requestApproval()
327
+ */
328
+ async runCommand(command, options) {
329
+ // Check if command is blocked by guardrails BEFORE anything else
330
+ const blockCheck = guardrail_manager_1.GuardrailManager.isCommandBlocked(command);
331
+ if (blockCheck.blocked) {
332
+ throw new Error(`Command blocked by guardrails: "${command}"\n` +
333
+ `Matched pattern: ${blockCheck.pattern}\n` +
334
+ `This command has been blocked for safety. You can modify blocked patterns in Settings > Guardrails.`);
335
+ }
336
+ // Check if command is trusted (auto-approve without user confirmation)
337
+ const trustCheck = guardrail_manager_1.GuardrailManager.isCommandTrusted(command);
338
+ let approved = false;
339
+ if (trustCheck.trusted) {
340
+ // Auto-approve trusted commands
341
+ approved = true;
342
+ this.daemon.logEvent(this.taskId, 'log', {
343
+ message: `Auto-approved trusted command (matched: ${trustCheck.pattern})`,
344
+ command,
345
+ });
346
+ }
347
+ else {
348
+ const signature = this.getCommandSignature(command);
349
+ const previousApproval = signature ? this.recentApprovals.get(signature) : undefined;
350
+ const now = Date.now();
351
+ if (signature &&
352
+ previousApproval &&
353
+ now - previousApproval.approvedAt <= this.approvalWindowMs &&
354
+ this.isAutoApprovalSafe(command)) {
355
+ approved = true;
356
+ previousApproval.count += 1;
357
+ previousApproval.approvedAt = now;
358
+ this.recentApprovals.set(signature, previousApproval);
359
+ this.daemon.logEvent(this.taskId, 'log', {
360
+ message: `Auto-approved similar command (approved ${previousApproval.count}x in last ${Math.round(this.approvalWindowMs / 1000)}s)`,
361
+ command,
362
+ });
363
+ }
364
+ else {
365
+ // Request user approval before executing
366
+ approved = await this.daemon.requestApproval(this.taskId, 'run_command', `Run command: ${command}`, {
367
+ command,
368
+ cwd: options?.cwd || this.workspace.path,
369
+ timeout: options?.timeout || DEFAULT_TIMEOUT,
370
+ });
371
+ if (approved && signature) {
372
+ this.recentApprovals.set(signature, { approvedAt: now, count: 1 });
373
+ }
374
+ }
375
+ }
376
+ if (!approved) {
377
+ throw new Error('User denied command execution');
378
+ }
379
+ // Log the command execution attempt
380
+ this.daemon.logEvent(this.taskId, 'tool_call', {
381
+ tool: 'run_command',
382
+ command,
383
+ cwd: options?.cwd || this.workspace.path,
384
+ });
385
+ const timeout = Math.min(options?.timeout || DEFAULT_TIMEOUT, MAX_TIMEOUT);
386
+ // Create a minimal, safe environment (don't leak sensitive process.env vars like API keys)
387
+ const safeEnv = {
388
+ // Essential system variables only
389
+ PATH: process.env.PATH || '/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin',
390
+ HOME: process.env.HOME || '',
391
+ USER: process.env.USER || '',
392
+ SHELL: process.env.SHELL || '/bin/bash',
393
+ LANG: process.env.LANG || 'en_US.UTF-8',
394
+ TERM: process.env.TERM || 'xterm-256color',
395
+ TMPDIR: process.env.TMPDIR || '/tmp',
396
+ // Add any user-provided env vars (explicitly passed by caller)
397
+ ...options?.env,
398
+ };
399
+ const cwd = options?.cwd || this.workspace.path;
400
+ // Emit the command being executed
401
+ this.daemon.logEvent(this.taskId, 'command_output', {
402
+ command,
403
+ cwd,
404
+ type: 'start',
405
+ output: `$ ${command}\n`,
406
+ });
407
+ return new Promise((resolve) => {
408
+ let stdout = '';
409
+ let stderr = '';
410
+ let killed = false;
411
+ // Increment session ID to invalidate any pending escalation timeouts from previous commands
412
+ this.processSessionId++;
413
+ // Clear any leftover escalation timeouts from previous commands
414
+ this.clearEscalationTimeouts();
415
+ // Use shell to handle complex commands with pipes, redirects, etc.
416
+ const shell = process.env.SHELL || '/bin/bash';
417
+ const child = (0, child_process_1.spawn)(shell, ['-c', command], {
418
+ cwd,
419
+ env: safeEnv,
420
+ stdio: ['pipe', 'pipe', 'pipe'], // Enable stdin for interactive commands
421
+ });
422
+ // Store reference to active process for stdin support
423
+ this.activeProcess = child;
424
+ // Set timeout
425
+ const timeoutId = setTimeout(() => {
426
+ killed = true;
427
+ child.kill('SIGTERM');
428
+ this.daemon.logEvent(this.taskId, 'command_output', {
429
+ command,
430
+ type: 'error',
431
+ output: `\n[Command timed out after ${timeout / 1000}s]\n`,
432
+ });
433
+ }, timeout);
434
+ // Stream stdout
435
+ child.stdout.on('data', (data) => {
436
+ const chunk = data.toString('utf-8');
437
+ stdout += chunk;
438
+ // Emit live output
439
+ this.daemon.logEvent(this.taskId, 'command_output', {
440
+ command,
441
+ type: 'stdout',
442
+ output: chunk,
443
+ });
444
+ });
445
+ // Stream stderr
446
+ child.stderr.on('data', (data) => {
447
+ const chunk = data.toString('utf-8');
448
+ stderr += chunk;
449
+ // Emit live output
450
+ this.daemon.logEvent(this.taskId, 'command_output', {
451
+ command,
452
+ type: 'stderr',
453
+ output: chunk,
454
+ });
455
+ });
456
+ child.on('close', (code) => {
457
+ clearTimeout(timeoutId);
458
+ this.activeProcess = null; // Clear active process reference
459
+ // Clear any pending escalation timeouts to prevent killing reused PIDs
460
+ this.clearEscalationTimeouts();
461
+ // Determine termination reason to signal the agent
462
+ let terminationReason = 'normal';
463
+ if (this.userKillRequested) {
464
+ terminationReason = 'user_stopped';
465
+ }
466
+ else if (killed) {
467
+ terminationReason = 'timeout';
468
+ }
469
+ // Reset for next command
470
+ this.userKillRequested = false;
471
+ const success = terminationReason === 'normal' && code === 0;
472
+ const truncatedStdout = this.truncateOutput(stdout);
473
+ const truncatedStderr = this.truncateOutput(stderr);
474
+ const exitCodeLabel = code === null ? 'unknown' : String(code);
475
+ const errorMessage = terminationReason === 'timeout'
476
+ ? 'Command timed out'
477
+ : terminationReason === 'user_stopped'
478
+ ? 'Command stopped by user'
479
+ : !success
480
+ ? `Command exited with code ${exitCodeLabel}`
481
+ : undefined;
482
+ // Emit command completion with termination reason
483
+ this.daemon.logEvent(this.taskId, 'command_output', {
484
+ command,
485
+ type: 'end',
486
+ exitCode: code,
487
+ success,
488
+ terminationReason,
489
+ });
490
+ this.daemon.logEvent(this.taskId, 'tool_result', {
491
+ tool: 'run_command',
492
+ success,
493
+ exitCode: code,
494
+ terminationReason,
495
+ error: errorMessage,
496
+ });
497
+ resolve({
498
+ success,
499
+ stdout: truncatedStdout,
500
+ stderr: truncatedStderr,
501
+ exitCode: code,
502
+ truncated: stdout.length > MAX_OUTPUT_SIZE || stderr.length > MAX_OUTPUT_SIZE,
503
+ terminationReason,
504
+ });
505
+ });
506
+ child.on('error', (error) => {
507
+ clearTimeout(timeoutId);
508
+ this.activeProcess = null; // Clear active process reference
509
+ // Clear any pending escalation timeouts to prevent killing reused PIDs
510
+ this.clearEscalationTimeouts();
511
+ // Reset user kill flag
512
+ this.userKillRequested = false;
513
+ const terminationReason = 'error';
514
+ this.daemon.logEvent(this.taskId, 'command_output', {
515
+ command,
516
+ type: 'error',
517
+ output: `\n[Error: ${error.message}]\n`,
518
+ terminationReason,
519
+ });
520
+ this.daemon.logEvent(this.taskId, 'tool_result', {
521
+ tool: 'run_command',
522
+ success: false,
523
+ error: error.message,
524
+ terminationReason,
525
+ });
526
+ resolve({
527
+ success: false,
528
+ stdout: this.truncateOutput(stdout),
529
+ stderr: error.message,
530
+ exitCode: null,
531
+ terminationReason,
532
+ });
533
+ });
534
+ });
535
+ }
536
+ /**
537
+ * Generate a normalized signature for a command to detect similar repeats
538
+ */
539
+ getCommandSignature(command) {
540
+ if (!command)
541
+ return '';
542
+ let signature = command.trim();
543
+ signature = signature.replace(/\s+/g, ' ');
544
+ signature = signature.replace(/"(?:[^"\\]|\\.)*"/g, '"<arg>"');
545
+ signature = signature.replace(/'(?:[^'\\]|\\.)*'/g, "'<arg>'");
546
+ signature = signature.replace(/(?:\/Users\/[^\s]+|~\/[^\s]+|\/[^\s]+)/g, '<path>');
547
+ return signature;
548
+ }
549
+ /**
550
+ * Safety check for auto-approving similar commands
551
+ */
552
+ isAutoApprovalSafe(command) {
553
+ return !/(^|\s)(sudo|rm|dd|mkfs|diskutil|shutdown|reboot|killall)\b/i.test(command);
554
+ }
555
+ /**
556
+ * Truncate output to prevent context overflow
557
+ */
558
+ truncateOutput(output) {
559
+ if (output.length <= MAX_OUTPUT_SIZE) {
560
+ return output;
561
+ }
562
+ return (output.slice(0, MAX_OUTPUT_SIZE) +
563
+ `\n\n[... Output truncated. Showing first ${Math.round(MAX_OUTPUT_SIZE / 1024)}KB ...]`);
564
+ }
565
+ }
566
+ exports.ShellTools = ShellTools;
567
+ // Export validation functions for testing
568
+ exports._testUtils = {
569
+ isValidPid,
570
+ isValidUsername,
571
+ isProcessOwnedByCurrentUser,
572
+ getDescendantPids,
573
+ killProcessTree,
574
+ };