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.
- package/LICENSE +21 -0
- package/README.md +1638 -0
- package/bin/cowork.js +42 -0
- package/build/entitlements.mac.plist +16 -0
- package/build/icon.icns +0 -0
- package/build/icon.png +0 -0
- package/dist/electron/electron/activity/ActivityRepository.js +190 -0
- package/dist/electron/electron/agent/browser/browser-service.js +639 -0
- package/dist/electron/electron/agent/context-manager.js +225 -0
- package/dist/electron/electron/agent/custom-skill-loader.js +566 -0
- package/dist/electron/electron/agent/daemon.js +975 -0
- package/dist/electron/electron/agent/executor.js +3561 -0
- package/dist/electron/electron/agent/llm/anthropic-provider.js +155 -0
- package/dist/electron/electron/agent/llm/bedrock-provider.js +202 -0
- package/dist/electron/electron/agent/llm/gemini-provider.js +375 -0
- package/dist/electron/electron/agent/llm/index.js +34 -0
- package/dist/electron/electron/agent/llm/ollama-provider.js +263 -0
- package/dist/electron/electron/agent/llm/openai-oauth.js +101 -0
- package/dist/electron/electron/agent/llm/openai-provider.js +657 -0
- package/dist/electron/electron/agent/llm/openrouter-provider.js +232 -0
- package/dist/electron/electron/agent/llm/pricing.js +160 -0
- package/dist/electron/electron/agent/llm/provider-factory.js +880 -0
- package/dist/electron/electron/agent/llm/types.js +178 -0
- package/dist/electron/electron/agent/queue-manager.js +378 -0
- package/dist/electron/electron/agent/sandbox/docker-sandbox.js +402 -0
- package/dist/electron/electron/agent/sandbox/macos-sandbox.js +407 -0
- package/dist/electron/electron/agent/sandbox/runner.js +410 -0
- package/dist/electron/electron/agent/sandbox/sandbox-factory.js +228 -0
- package/dist/electron/electron/agent/sandbox/security-utils.js +258 -0
- package/dist/electron/electron/agent/search/brave-provider.js +119 -0
- package/dist/electron/electron/agent/search/google-provider.js +100 -0
- package/dist/electron/electron/agent/search/index.js +28 -0
- package/dist/electron/electron/agent/search/provider-factory.js +395 -0
- package/dist/electron/electron/agent/search/serpapi-provider.js +112 -0
- package/dist/electron/electron/agent/search/tavily-provider.js +90 -0
- package/dist/electron/electron/agent/search/types.js +40 -0
- package/dist/electron/electron/agent/security/index.js +12 -0
- package/dist/electron/electron/agent/security/input-sanitizer.js +303 -0
- package/dist/electron/electron/agent/security/output-filter.js +217 -0
- package/dist/electron/electron/agent/skill-eligibility.js +281 -0
- package/dist/electron/electron/agent/skill-registry.js +396 -0
- package/dist/electron/electron/agent/skills/document.js +878 -0
- package/dist/electron/electron/agent/skills/image-generator.js +225 -0
- package/dist/electron/electron/agent/skills/organizer.js +141 -0
- package/dist/electron/electron/agent/skills/presentation.js +367 -0
- package/dist/electron/electron/agent/skills/spreadsheet.js +165 -0
- package/dist/electron/electron/agent/tools/browser-tools.js +523 -0
- package/dist/electron/electron/agent/tools/builtin-settings.js +384 -0
- package/dist/electron/electron/agent/tools/canvas-tools.js +530 -0
- package/dist/electron/electron/agent/tools/cron-tools.js +577 -0
- package/dist/electron/electron/agent/tools/edit-tools.js +194 -0
- package/dist/electron/electron/agent/tools/file-tools.js +719 -0
- package/dist/electron/electron/agent/tools/glob-tools.js +283 -0
- package/dist/electron/electron/agent/tools/grep-tools.js +387 -0
- package/dist/electron/electron/agent/tools/image-tools.js +111 -0
- package/dist/electron/electron/agent/tools/mention-tools.js +282 -0
- package/dist/electron/electron/agent/tools/node-tools.js +476 -0
- package/dist/electron/electron/agent/tools/registry.js +2719 -0
- package/dist/electron/electron/agent/tools/search-tools.js +91 -0
- package/dist/electron/electron/agent/tools/shell-tools.js +574 -0
- package/dist/electron/electron/agent/tools/skill-tools.js +274 -0
- package/dist/electron/electron/agent/tools/system-tools.js +578 -0
- package/dist/electron/electron/agent/tools/web-fetch-tools.js +444 -0
- package/dist/electron/electron/agent/tools/x-tools.js +264 -0
- package/dist/electron/electron/agents/AgentRoleRepository.js +420 -0
- package/dist/electron/electron/agents/HeartbeatService.js +356 -0
- package/dist/electron/electron/agents/MentionRepository.js +197 -0
- package/dist/electron/electron/agents/TaskSubscriptionRepository.js +168 -0
- package/dist/electron/electron/agents/WorkingStateRepository.js +229 -0
- package/dist/electron/electron/canvas/canvas-manager.js +714 -0
- package/dist/electron/electron/canvas/canvas-preload.js +53 -0
- package/dist/electron/electron/canvas/canvas-protocol.js +195 -0
- package/dist/electron/electron/canvas/canvas-store.js +174 -0
- package/dist/electron/electron/canvas/index.js +13 -0
- package/dist/electron/electron/control-plane/client.js +364 -0
- package/dist/electron/electron/control-plane/handlers.js +572 -0
- package/dist/electron/electron/control-plane/index.js +41 -0
- package/dist/electron/electron/control-plane/node-manager.js +264 -0
- package/dist/electron/electron/control-plane/protocol.js +194 -0
- package/dist/electron/electron/control-plane/remote-client.js +437 -0
- package/dist/electron/electron/control-plane/server.js +640 -0
- package/dist/electron/electron/control-plane/settings.js +369 -0
- package/dist/electron/electron/control-plane/ssh-tunnel.js +549 -0
- package/dist/electron/electron/cron/index.js +30 -0
- package/dist/electron/electron/cron/schedule.js +190 -0
- package/dist/electron/electron/cron/service.js +614 -0
- package/dist/electron/electron/cron/store.js +155 -0
- package/dist/electron/electron/cron/types.js +82 -0
- package/dist/electron/electron/cron/webhook.js +258 -0
- package/dist/electron/electron/database/SecureSettingsRepository.js +444 -0
- package/dist/electron/electron/database/TaskLabelRepository.js +120 -0
- package/dist/electron/electron/database/repositories.js +1781 -0
- package/dist/electron/electron/database/schema.js +978 -0
- package/dist/electron/electron/extensions/index.js +33 -0
- package/dist/electron/electron/extensions/loader.js +313 -0
- package/dist/electron/electron/extensions/registry.js +485 -0
- package/dist/electron/electron/extensions/types.js +11 -0
- package/dist/electron/electron/gateway/channel-registry.js +1102 -0
- package/dist/electron/electron/gateway/channels/bluebubbles-client.js +479 -0
- package/dist/electron/electron/gateway/channels/bluebubbles.js +432 -0
- package/dist/electron/electron/gateway/channels/discord.js +975 -0
- package/dist/electron/electron/gateway/channels/email-client.js +593 -0
- package/dist/electron/electron/gateway/channels/email.js +443 -0
- package/dist/electron/electron/gateway/channels/google-chat.js +631 -0
- package/dist/electron/electron/gateway/channels/imessage-client.js +363 -0
- package/dist/electron/electron/gateway/channels/imessage.js +465 -0
- package/dist/electron/electron/gateway/channels/index.js +36 -0
- package/dist/electron/electron/gateway/channels/line-client.js +470 -0
- package/dist/electron/electron/gateway/channels/line.js +479 -0
- package/dist/electron/electron/gateway/channels/matrix-client.js +432 -0
- package/dist/electron/electron/gateway/channels/matrix.js +592 -0
- package/dist/electron/electron/gateway/channels/mattermost-client.js +394 -0
- package/dist/electron/electron/gateway/channels/mattermost.js +496 -0
- package/dist/electron/electron/gateway/channels/signal-client.js +500 -0
- package/dist/electron/electron/gateway/channels/signal.js +582 -0
- package/dist/electron/electron/gateway/channels/slack.js +415 -0
- package/dist/electron/electron/gateway/channels/teams.js +596 -0
- package/dist/electron/electron/gateway/channels/telegram.js +1390 -0
- package/dist/electron/electron/gateway/channels/twitch-client.js +502 -0
- package/dist/electron/electron/gateway/channels/twitch.js +396 -0
- package/dist/electron/electron/gateway/channels/types.js +8 -0
- package/dist/electron/electron/gateway/channels/whatsapp.js +953 -0
- package/dist/electron/electron/gateway/context-policy.js +268 -0
- package/dist/electron/electron/gateway/index.js +1063 -0
- package/dist/electron/electron/gateway/infrastructure.js +496 -0
- package/dist/electron/electron/gateway/router.js +2700 -0
- package/dist/electron/electron/gateway/security.js +375 -0
- package/dist/electron/electron/gateway/session.js +115 -0
- package/dist/electron/electron/gateway/tunnel.js +503 -0
- package/dist/electron/electron/guardrails/guardrail-manager.js +348 -0
- package/dist/electron/electron/hooks/gmail-watcher.js +300 -0
- package/dist/electron/electron/hooks/index.js +46 -0
- package/dist/electron/electron/hooks/mappings.js +381 -0
- package/dist/electron/electron/hooks/server.js +480 -0
- package/dist/electron/electron/hooks/settings.js +447 -0
- package/dist/electron/electron/hooks/types.js +41 -0
- package/dist/electron/electron/ipc/canvas-handlers.js +158 -0
- package/dist/electron/electron/ipc/handlers.js +3138 -0
- package/dist/electron/electron/ipc/mission-control-handlers.js +141 -0
- package/dist/electron/electron/main.js +448 -0
- package/dist/electron/electron/mcp/client/MCPClientManager.js +330 -0
- package/dist/electron/electron/mcp/client/MCPServerConnection.js +437 -0
- package/dist/electron/electron/mcp/client/transports/SSETransport.js +304 -0
- package/dist/electron/electron/mcp/client/transports/StdioTransport.js +307 -0
- package/dist/electron/electron/mcp/client/transports/WebSocketTransport.js +329 -0
- package/dist/electron/electron/mcp/host/MCPHostServer.js +354 -0
- package/dist/electron/electron/mcp/host/ToolAdapter.js +100 -0
- package/dist/electron/electron/mcp/registry/MCPRegistryManager.js +497 -0
- package/dist/electron/electron/mcp/settings.js +446 -0
- package/dist/electron/electron/mcp/types.js +59 -0
- package/dist/electron/electron/memory/MemoryService.js +435 -0
- package/dist/electron/electron/notifications/index.js +17 -0
- package/dist/electron/electron/notifications/service.js +118 -0
- package/dist/electron/electron/notifications/store.js +144 -0
- package/dist/electron/electron/preload.js +842 -0
- package/dist/electron/electron/reports/StandupReportService.js +272 -0
- package/dist/electron/electron/security/concurrency.js +293 -0
- package/dist/electron/electron/security/index.js +15 -0
- package/dist/electron/electron/security/policy-manager.js +435 -0
- package/dist/electron/electron/settings/appearance-manager.js +193 -0
- package/dist/electron/electron/settings/personality-manager.js +724 -0
- package/dist/electron/electron/settings/x-manager.js +58 -0
- package/dist/electron/electron/tailscale/exposure.js +188 -0
- package/dist/electron/electron/tailscale/index.js +28 -0
- package/dist/electron/electron/tailscale/settings.js +205 -0
- package/dist/electron/electron/tailscale/tailscale.js +355 -0
- package/dist/electron/electron/tray/QuickInputWindow.js +568 -0
- package/dist/electron/electron/tray/TrayManager.js +895 -0
- package/dist/electron/electron/tray/index.js +9 -0
- package/dist/electron/electron/updater/index.js +6 -0
- package/dist/electron/electron/updater/update-manager.js +418 -0
- package/dist/electron/electron/utils/env-migration.js +209 -0
- package/dist/electron/electron/utils/process.js +102 -0
- package/dist/electron/electron/utils/rate-limiter.js +104 -0
- package/dist/electron/electron/utils/validation.js +419 -0
- package/dist/electron/electron/utils/x-cli.js +177 -0
- package/dist/electron/electron/voice/VoiceService.js +507 -0
- package/dist/electron/electron/voice/index.js +14 -0
- package/dist/electron/electron/voice/voice-settings-manager.js +359 -0
- package/dist/electron/shared/channelMessages.js +170 -0
- package/dist/electron/shared/types.js +1185 -0
- package/package.json +159 -0
- package/resources/skills/1password.json +10 -0
- package/resources/skills/add-documentation.json +31 -0
- package/resources/skills/analyze-csv.json +17 -0
- package/resources/skills/apple-notes.json +10 -0
- package/resources/skills/apple-reminders.json +10 -0
- package/resources/skills/auto-commenter.json +10 -0
- package/resources/skills/bear-notes.json +10 -0
- package/resources/skills/bird.json +35 -0
- package/resources/skills/blogwatcher.json +10 -0
- package/resources/skills/blucli.json +10 -0
- package/resources/skills/bluebubbles.json +10 -0
- package/resources/skills/camsnap.json +10 -0
- package/resources/skills/clean-imports.json +18 -0
- package/resources/skills/code-review.json +18 -0
- package/resources/skills/coding-agent.json +10 -0
- package/resources/skills/compare-files.json +23 -0
- package/resources/skills/convert-code.json +34 -0
- package/resources/skills/create-changelog.json +24 -0
- package/resources/skills/debug-error.json +17 -0
- package/resources/skills/dependency-check.json +10 -0
- package/resources/skills/discord.json +10 -0
- package/resources/skills/eightctl.json +10 -0
- package/resources/skills/explain-code.json +29 -0
- package/resources/skills/extract-todos.json +18 -0
- package/resources/skills/food-order.json +10 -0
- package/resources/skills/gemini.json +10 -0
- package/resources/skills/generate-readme.json +10 -0
- package/resources/skills/gifgrep.json +10 -0
- package/resources/skills/git-commit.json +10 -0
- package/resources/skills/github.json +10 -0
- package/resources/skills/gog.json +10 -0
- package/resources/skills/goplaces.json +10 -0
- package/resources/skills/himalaya.json +10 -0
- package/resources/skills/imsg.json +10 -0
- package/resources/skills/karpathy-guidelines.json +12 -0
- package/resources/skills/last30days.json +26 -0
- package/resources/skills/local-places.json +10 -0
- package/resources/skills/mcporter.json +10 -0
- package/resources/skills/model-usage.json +10 -0
- package/resources/skills/nano-banana-pro.json +10 -0
- package/resources/skills/nano-pdf.json +10 -0
- package/resources/skills/notion.json +10 -0
- package/resources/skills/obsidian.json +10 -0
- package/resources/skills/openai-image-gen.json +10 -0
- package/resources/skills/openai-whisper-api.json +10 -0
- package/resources/skills/openai-whisper.json +10 -0
- package/resources/skills/openhue.json +10 -0
- package/resources/skills/oracle.json +10 -0
- package/resources/skills/ordercli.json +10 -0
- package/resources/skills/peekaboo.json +10 -0
- package/resources/skills/project-structure.json +10 -0
- package/resources/skills/proofread.json +17 -0
- package/resources/skills/refactor-code.json +31 -0
- package/resources/skills/rename-symbol.json +23 -0
- package/resources/skills/sag.json +10 -0
- package/resources/skills/security-audit.json +18 -0
- package/resources/skills/session-logs.json +10 -0
- package/resources/skills/sherpa-onnx-tts.json +10 -0
- package/resources/skills/skill-creator.json +15 -0
- package/resources/skills/skill-hub.json +29 -0
- package/resources/skills/slack.json +10 -0
- package/resources/skills/songsee.json +10 -0
- package/resources/skills/sonoscli.json +10 -0
- package/resources/skills/spotify-player.json +10 -0
- package/resources/skills/startup-cfo.json +55 -0
- package/resources/skills/summarize-folder.json +18 -0
- package/resources/skills/summarize.json +10 -0
- package/resources/skills/things-mac.json +10 -0
- package/resources/skills/tmux.json +10 -0
- package/resources/skills/translate.json +36 -0
- package/resources/skills/trello.json +10 -0
- package/resources/skills/video-frames.json +10 -0
- package/resources/skills/voice-call.json +10 -0
- package/resources/skills/wacli.json +10 -0
- package/resources/skills/weather.json +10 -0
- package/resources/skills/write-tests.json +31 -0
- package/src/electron/activity/ActivityRepository.ts +238 -0
- package/src/electron/agent/browser/browser-service.ts +721 -0
- package/src/electron/agent/context-manager.ts +257 -0
- package/src/electron/agent/custom-skill-loader.ts +634 -0
- package/src/electron/agent/daemon.ts +1097 -0
- package/src/electron/agent/executor.ts +4017 -0
- package/src/electron/agent/llm/anthropic-provider.ts +175 -0
- package/src/electron/agent/llm/bedrock-provider.ts +236 -0
- package/src/electron/agent/llm/gemini-provider.ts +422 -0
- package/src/electron/agent/llm/index.ts +9 -0
- package/src/electron/agent/llm/ollama-provider.ts +347 -0
- package/src/electron/agent/llm/openai-oauth.ts +127 -0
- package/src/electron/agent/llm/openai-provider.ts +686 -0
- package/src/electron/agent/llm/openrouter-provider.ts +273 -0
- package/src/electron/agent/llm/pricing.ts +180 -0
- package/src/electron/agent/llm/provider-factory.ts +971 -0
- package/src/electron/agent/llm/types.ts +291 -0
- package/src/electron/agent/queue-manager.ts +408 -0
- package/src/electron/agent/sandbox/docker-sandbox.ts +453 -0
- package/src/electron/agent/sandbox/macos-sandbox.ts +426 -0
- package/src/electron/agent/sandbox/runner.ts +453 -0
- package/src/electron/agent/sandbox/sandbox-factory.ts +337 -0
- package/src/electron/agent/sandbox/security-utils.ts +251 -0
- package/src/electron/agent/search/brave-provider.ts +141 -0
- package/src/electron/agent/search/google-provider.ts +131 -0
- package/src/electron/agent/search/index.ts +6 -0
- package/src/electron/agent/search/provider-factory.ts +450 -0
- package/src/electron/agent/search/serpapi-provider.ts +138 -0
- package/src/electron/agent/search/tavily-provider.ts +108 -0
- package/src/electron/agent/search/types.ts +118 -0
- package/src/electron/agent/security/index.ts +20 -0
- package/src/electron/agent/security/input-sanitizer.ts +380 -0
- package/src/electron/agent/security/output-filter.ts +259 -0
- package/src/electron/agent/skill-eligibility.ts +334 -0
- package/src/electron/agent/skill-registry.ts +457 -0
- package/src/electron/agent/skills/document.ts +1070 -0
- package/src/electron/agent/skills/image-generator.ts +272 -0
- package/src/electron/agent/skills/organizer.ts +131 -0
- package/src/electron/agent/skills/presentation.ts +418 -0
- package/src/electron/agent/skills/spreadsheet.ts +166 -0
- package/src/electron/agent/tools/browser-tools.ts +546 -0
- package/src/electron/agent/tools/builtin-settings.ts +422 -0
- package/src/electron/agent/tools/canvas-tools.ts +572 -0
- package/src/electron/agent/tools/cron-tools.ts +723 -0
- package/src/electron/agent/tools/edit-tools.ts +196 -0
- package/src/electron/agent/tools/file-tools.ts +811 -0
- package/src/electron/agent/tools/glob-tools.ts +303 -0
- package/src/electron/agent/tools/grep-tools.ts +432 -0
- package/src/electron/agent/tools/image-tools.ts +126 -0
- package/src/electron/agent/tools/mention-tools.ts +371 -0
- package/src/electron/agent/tools/node-tools.ts +550 -0
- package/src/electron/agent/tools/registry.ts +3052 -0
- package/src/electron/agent/tools/search-tools.ts +111 -0
- package/src/electron/agent/tools/shell-tools.ts +651 -0
- package/src/electron/agent/tools/skill-tools.ts +340 -0
- package/src/electron/agent/tools/system-tools.ts +665 -0
- package/src/electron/agent/tools/web-fetch-tools.ts +528 -0
- package/src/electron/agent/tools/x-tools.ts +267 -0
- package/src/electron/agents/AgentRoleRepository.ts +557 -0
- package/src/electron/agents/HeartbeatService.ts +469 -0
- package/src/electron/agents/MentionRepository.ts +242 -0
- package/src/electron/agents/TaskSubscriptionRepository.ts +231 -0
- package/src/electron/agents/WorkingStateRepository.ts +278 -0
- package/src/electron/canvas/canvas-manager.ts +818 -0
- package/src/electron/canvas/canvas-preload.ts +102 -0
- package/src/electron/canvas/canvas-protocol.ts +174 -0
- package/src/electron/canvas/canvas-store.ts +200 -0
- package/src/electron/canvas/index.ts +8 -0
- package/src/electron/control-plane/client.ts +527 -0
- package/src/electron/control-plane/handlers.ts +723 -0
- package/src/electron/control-plane/index.ts +51 -0
- package/src/electron/control-plane/node-manager.ts +322 -0
- package/src/electron/control-plane/protocol.ts +269 -0
- package/src/electron/control-plane/remote-client.ts +517 -0
- package/src/electron/control-plane/server.ts +853 -0
- package/src/electron/control-plane/settings.ts +401 -0
- package/src/electron/control-plane/ssh-tunnel.ts +624 -0
- package/src/electron/cron/index.ts +9 -0
- package/src/electron/cron/schedule.ts +217 -0
- package/src/electron/cron/service.ts +743 -0
- package/src/electron/cron/store.ts +165 -0
- package/src/electron/cron/types.ts +291 -0
- package/src/electron/cron/webhook.ts +303 -0
- package/src/electron/database/SecureSettingsRepository.ts +514 -0
- package/src/electron/database/TaskLabelRepository.ts +148 -0
- package/src/electron/database/repositories.ts +2397 -0
- package/src/electron/database/schema.ts +1017 -0
- package/src/electron/extensions/index.ts +18 -0
- package/src/electron/extensions/loader.ts +336 -0
- package/src/electron/extensions/registry.ts +546 -0
- package/src/electron/extensions/types.ts +372 -0
- package/src/electron/gateway/channel-registry.ts +1267 -0
- package/src/electron/gateway/channels/bluebubbles-client.ts +641 -0
- package/src/electron/gateway/channels/bluebubbles.ts +509 -0
- package/src/electron/gateway/channels/discord.ts +1150 -0
- package/src/electron/gateway/channels/email-client.ts +708 -0
- package/src/electron/gateway/channels/email.ts +516 -0
- package/src/electron/gateway/channels/google-chat.ts +760 -0
- package/src/electron/gateway/channels/imessage-client.ts +473 -0
- package/src/electron/gateway/channels/imessage.ts +520 -0
- package/src/electron/gateway/channels/index.ts +21 -0
- package/src/electron/gateway/channels/line-client.ts +598 -0
- package/src/electron/gateway/channels/line.ts +559 -0
- package/src/electron/gateway/channels/matrix-client.ts +632 -0
- package/src/electron/gateway/channels/matrix.ts +655 -0
- package/src/electron/gateway/channels/mattermost-client.ts +526 -0
- package/src/electron/gateway/channels/mattermost.ts +550 -0
- package/src/electron/gateway/channels/signal-client.ts +722 -0
- package/src/electron/gateway/channels/signal.ts +666 -0
- package/src/electron/gateway/channels/slack.ts +458 -0
- package/src/electron/gateway/channels/teams.ts +681 -0
- package/src/electron/gateway/channels/telegram.ts +1727 -0
- package/src/electron/gateway/channels/twitch-client.ts +665 -0
- package/src/electron/gateway/channels/twitch.ts +468 -0
- package/src/electron/gateway/channels/types.ts +1002 -0
- package/src/electron/gateway/channels/whatsapp.ts +1101 -0
- package/src/electron/gateway/context-policy.ts +382 -0
- package/src/electron/gateway/index.ts +1274 -0
- package/src/electron/gateway/infrastructure.ts +645 -0
- package/src/electron/gateway/router.ts +3206 -0
- package/src/electron/gateway/security.ts +422 -0
- package/src/electron/gateway/session.ts +144 -0
- package/src/electron/gateway/tunnel.ts +626 -0
- package/src/electron/guardrails/guardrail-manager.ts +380 -0
- package/src/electron/hooks/gmail-watcher.ts +355 -0
- package/src/electron/hooks/index.ts +30 -0
- package/src/electron/hooks/mappings.ts +404 -0
- package/src/electron/hooks/server.ts +574 -0
- package/src/electron/hooks/settings.ts +466 -0
- package/src/electron/hooks/types.ts +245 -0
- package/src/electron/ipc/canvas-handlers.ts +223 -0
- package/src/electron/ipc/handlers.ts +3661 -0
- package/src/electron/ipc/mission-control-handlers.ts +182 -0
- package/src/electron/main.ts +496 -0
- package/src/electron/mcp/client/MCPClientManager.ts +406 -0
- package/src/electron/mcp/client/MCPServerConnection.ts +514 -0
- package/src/electron/mcp/client/transports/SSETransport.ts +360 -0
- package/src/electron/mcp/client/transports/StdioTransport.ts +355 -0
- package/src/electron/mcp/client/transports/WebSocketTransport.ts +384 -0
- package/src/electron/mcp/host/MCPHostServer.ts +388 -0
- package/src/electron/mcp/host/ToolAdapter.ts +140 -0
- package/src/electron/mcp/registry/MCPRegistryManager.ts +565 -0
- package/src/electron/mcp/settings.ts +468 -0
- package/src/electron/mcp/types.ts +371 -0
- package/src/electron/memory/MemoryService.ts +523 -0
- package/src/electron/notifications/index.ts +16 -0
- package/src/electron/notifications/service.ts +161 -0
- package/src/electron/notifications/store.ts +163 -0
- package/src/electron/preload.ts +2845 -0
- package/src/electron/reports/StandupReportService.ts +356 -0
- package/src/electron/security/concurrency.ts +333 -0
- package/src/electron/security/index.ts +17 -0
- package/src/electron/security/policy-manager.ts +539 -0
- package/src/electron/settings/appearance-manager.ts +182 -0
- package/src/electron/settings/personality-manager.ts +800 -0
- package/src/electron/settings/x-manager.ts +62 -0
- package/src/electron/tailscale/exposure.ts +262 -0
- package/src/electron/tailscale/index.ts +34 -0
- package/src/electron/tailscale/settings.ts +218 -0
- package/src/electron/tailscale/tailscale.ts +379 -0
- package/src/electron/tray/QuickInputWindow.ts +609 -0
- package/src/electron/tray/TrayManager.ts +1005 -0
- package/src/electron/tray/index.ts +6 -0
- package/src/electron/updater/index.ts +1 -0
- package/src/electron/updater/update-manager.ts +447 -0
- package/src/electron/utils/env-migration.ts +203 -0
- package/src/electron/utils/process.ts +124 -0
- package/src/electron/utils/rate-limiter.ts +130 -0
- package/src/electron/utils/validation.ts +493 -0
- package/src/electron/utils/x-cli.ts +198 -0
- package/src/electron/voice/VoiceService.ts +583 -0
- package/src/electron/voice/index.ts +9 -0
- package/src/electron/voice/voice-settings-manager.ts +403 -0
- package/src/renderer/App.tsx +775 -0
- package/src/renderer/components/ActivityFeed.tsx +407 -0
- package/src/renderer/components/ActivityFeedItem.tsx +285 -0
- package/src/renderer/components/AgentRoleCard.tsx +343 -0
- package/src/renderer/components/AgentRoleEditor.tsx +805 -0
- package/src/renderer/components/AgentSquadSettings.tsx +295 -0
- package/src/renderer/components/AgentWorkingStatePanel.tsx +411 -0
- package/src/renderer/components/AppearanceSettings.tsx +122 -0
- package/src/renderer/components/ApprovalDialog.tsx +100 -0
- package/src/renderer/components/BlueBubblesSettings.tsx +505 -0
- package/src/renderer/components/BuiltinToolsSettings.tsx +307 -0
- package/src/renderer/components/CanvasPreview.tsx +1189 -0
- package/src/renderer/components/CommandOutput.tsx +202 -0
- package/src/renderer/components/ContextPolicySettings.tsx +523 -0
- package/src/renderer/components/ControlPlaneSettings.tsx +1134 -0
- package/src/renderer/components/DisclaimerModal.tsx +124 -0
- package/src/renderer/components/DiscordSettings.tsx +436 -0
- package/src/renderer/components/EmailSettings.tsx +606 -0
- package/src/renderer/components/ExtensionsSettings.tsx +542 -0
- package/src/renderer/components/FileViewer.tsx +224 -0
- package/src/renderer/components/GoogleChatSettings.tsx +535 -0
- package/src/renderer/components/GuardrailSettings.tsx +487 -0
- package/src/renderer/components/HooksSettings.tsx +581 -0
- package/src/renderer/components/ImessageSettings.tsx +484 -0
- package/src/renderer/components/LineSettings.tsx +483 -0
- package/src/renderer/components/MCPRegistryBrowser.tsx +386 -0
- package/src/renderer/components/MCPSettings.tsx +943 -0
- package/src/renderer/components/MainContent.tsx +2433 -0
- package/src/renderer/components/MatrixSettings.tsx +510 -0
- package/src/renderer/components/MattermostSettings.tsx +473 -0
- package/src/renderer/components/MemorySettings.tsx +247 -0
- package/src/renderer/components/MentionBadge.tsx +87 -0
- package/src/renderer/components/MentionInput.tsx +409 -0
- package/src/renderer/components/MentionList.tsx +476 -0
- package/src/renderer/components/MissionControlPanel.tsx +1995 -0
- package/src/renderer/components/NodesSettings.tsx +316 -0
- package/src/renderer/components/NotificationPanel.tsx +481 -0
- package/src/renderer/components/Onboarding/AwakeningOrb.tsx +44 -0
- package/src/renderer/components/Onboarding/Onboarding.tsx +443 -0
- package/src/renderer/components/Onboarding/TypewriterText.tsx +102 -0
- package/src/renderer/components/Onboarding/index.ts +3 -0
- package/src/renderer/components/OnboardingModal.tsx +698 -0
- package/src/renderer/components/PairingCodeDisplay.tsx +324 -0
- package/src/renderer/components/PersonalitySettings.tsx +597 -0
- package/src/renderer/components/QueueSettings.tsx +119 -0
- package/src/renderer/components/QuickTaskFAB.tsx +71 -0
- package/src/renderer/components/RightPanel.tsx +413 -0
- package/src/renderer/components/ScheduledTasksSettings.tsx +1328 -0
- package/src/renderer/components/SearchSettings.tsx +328 -0
- package/src/renderer/components/Settings.tsx +1504 -0
- package/src/renderer/components/Sidebar.tsx +344 -0
- package/src/renderer/components/SignalSettings.tsx +673 -0
- package/src/renderer/components/SkillHubBrowser.tsx +458 -0
- package/src/renderer/components/SkillParameterModal.tsx +185 -0
- package/src/renderer/components/SkillsSettings.tsx +451 -0
- package/src/renderer/components/SlackSettings.tsx +442 -0
- package/src/renderer/components/StandupReportViewer.tsx +614 -0
- package/src/renderer/components/TaskBoard.tsx +498 -0
- package/src/renderer/components/TaskBoardCard.tsx +357 -0
- package/src/renderer/components/TaskBoardColumn.tsx +211 -0
- package/src/renderer/components/TaskLabelManager.tsx +472 -0
- package/src/renderer/components/TaskQueuePanel.tsx +144 -0
- package/src/renderer/components/TaskQuickActions.tsx +492 -0
- package/src/renderer/components/TaskTimeline.tsx +216 -0
- package/src/renderer/components/TaskView.tsx +162 -0
- package/src/renderer/components/TeamsSettings.tsx +518 -0
- package/src/renderer/components/TelegramSettings.tsx +421 -0
- package/src/renderer/components/Toast.tsx +76 -0
- package/src/renderer/components/TraySettings.tsx +189 -0
- package/src/renderer/components/TwitchSettings.tsx +511 -0
- package/src/renderer/components/UpdateSettings.tsx +295 -0
- package/src/renderer/components/VoiceIndicator.tsx +270 -0
- package/src/renderer/components/VoiceSettings.tsx +867 -0
- package/src/renderer/components/WhatsAppSettings.tsx +721 -0
- package/src/renderer/components/WorkingStateEditor.tsx +309 -0
- package/src/renderer/components/WorkingStateHistory.tsx +481 -0
- package/src/renderer/components/WorkspaceSelector.tsx +150 -0
- package/src/renderer/components/XSettings.tsx +311 -0
- package/src/renderer/global.d.ts +9 -0
- package/src/renderer/hooks/useAgentContext.ts +153 -0
- package/src/renderer/hooks/useOnboardingFlow.ts +548 -0
- package/src/renderer/hooks/useVoiceInput.ts +268 -0
- package/src/renderer/index.html +12 -0
- package/src/renderer/main.tsx +10 -0
- package/src/renderer/public/cowork-os-logo.png +0 -0
- package/src/renderer/quick-input.html +164 -0
- package/src/renderer/styles/index.css +14504 -0
- package/src/renderer/utils/agentMessages.ts +749 -0
- package/src/renderer/utils/voice-directives.ts +169 -0
- package/src/shared/channelMessages.ts +213 -0
- package/src/shared/types.ts +3608 -0
- package/tsconfig.electron.json +26 -0
- package/tsconfig.json +26 -0
- package/tsconfig.node.json +10 -0
- package/vite.config.ts +23 -0
|
@@ -0,0 +1,975 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.AgentDaemon = void 0;
|
|
37
|
+
const events_1 = require("events");
|
|
38
|
+
const electron_1 = require("electron");
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const crypto = __importStar(require("crypto"));
|
|
41
|
+
const repositories_1 = require("../database/repositories");
|
|
42
|
+
const ActivityRepository_1 = require("../activity/ActivityRepository");
|
|
43
|
+
const types_1 = require("../../shared/types");
|
|
44
|
+
const executor_1 = require("./executor");
|
|
45
|
+
const queue_manager_1 = require("./queue-manager");
|
|
46
|
+
const concurrency_1 = require("../security/concurrency");
|
|
47
|
+
const MemoryService_1 = require("../memory/MemoryService");
|
|
48
|
+
// Memory management constants
|
|
49
|
+
const MAX_CACHED_EXECUTORS = 10; // Maximum number of completed task executors to keep in memory
|
|
50
|
+
const EXECUTOR_CACHE_TTL_MS = 30 * 60 * 1000; // 30 minutes - time before completed executors are cleaned up
|
|
51
|
+
/**
|
|
52
|
+
* AgentDaemon is the core orchestrator that manages task execution
|
|
53
|
+
* It coordinates between the database, task executors, and UI
|
|
54
|
+
*/
|
|
55
|
+
class AgentDaemon extends events_1.EventEmitter {
|
|
56
|
+
constructor(dbManager) {
|
|
57
|
+
super();
|
|
58
|
+
this.dbManager = dbManager;
|
|
59
|
+
this.activeTasks = new Map();
|
|
60
|
+
this.pendingApprovals = new Map();
|
|
61
|
+
const db = dbManager.getDatabase();
|
|
62
|
+
this.taskRepo = new repositories_1.TaskRepository(db);
|
|
63
|
+
this.eventRepo = new repositories_1.TaskEventRepository(db);
|
|
64
|
+
this.workspaceRepo = new repositories_1.WorkspaceRepository(db);
|
|
65
|
+
this.approvalRepo = new repositories_1.ApprovalRepository(db);
|
|
66
|
+
this.artifactRepo = new repositories_1.ArtifactRepository(db);
|
|
67
|
+
this.activityRepo = new ActivityRepository_1.ActivityRepository(db);
|
|
68
|
+
// Initialize queue manager with callbacks
|
|
69
|
+
this.queueManager = new queue_manager_1.TaskQueueManager({
|
|
70
|
+
startTaskImmediate: (task) => this.startTaskImmediate(task),
|
|
71
|
+
emitQueueUpdate: (status) => this.emitQueueUpdate(status),
|
|
72
|
+
getTaskById: (taskId) => this.taskRepo.findById(taskId),
|
|
73
|
+
updateTaskStatus: (taskId, status) => this.taskRepo.update(taskId, { status }),
|
|
74
|
+
onTaskTimeout: (taskId) => this.handleTaskTimeout(taskId),
|
|
75
|
+
});
|
|
76
|
+
// Start periodic cleanup of old executors
|
|
77
|
+
this.cleanupIntervalHandle = setInterval(() => this.cleanupOldExecutors(), 5 * 60 * 1000); // Run every 5 minutes
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Initialize the daemon - call after construction to set up queue
|
|
81
|
+
*/
|
|
82
|
+
async initialize() {
|
|
83
|
+
// Find queued tasks from database for queue recovery
|
|
84
|
+
const queuedTasks = this.taskRepo.findByStatus('queued');
|
|
85
|
+
// Find "running" tasks from previous session - these are orphaned since we lost their executors
|
|
86
|
+
const orphanedTasks = this.taskRepo.findByStatus(['planning', 'executing']);
|
|
87
|
+
// Mark orphaned tasks as failed - they can't be resumed since we lost their state
|
|
88
|
+
if (orphanedTasks.length > 0) {
|
|
89
|
+
console.log(`[AgentDaemon] Found ${orphanedTasks.length} orphaned tasks from previous session, marking as failed`);
|
|
90
|
+
for (const task of orphanedTasks) {
|
|
91
|
+
this.taskRepo.update(task.id, {
|
|
92
|
+
status: 'failed',
|
|
93
|
+
error: 'Task interrupted - application was restarted while task was running',
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Only initialize queue with queued tasks (not orphaned "running" tasks)
|
|
98
|
+
await this.queueManager.initialize(queuedTasks, []);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Clean up old completed task executors to prevent memory leaks
|
|
102
|
+
*/
|
|
103
|
+
cleanupOldExecutors() {
|
|
104
|
+
const now = Date.now();
|
|
105
|
+
const toDelete = [];
|
|
106
|
+
let completedCount = 0;
|
|
107
|
+
// Find executors to clean up
|
|
108
|
+
this.activeTasks.forEach((cached, taskId) => {
|
|
109
|
+
if (cached.status === 'completed') {
|
|
110
|
+
completedCount++;
|
|
111
|
+
// Remove if older than TTL
|
|
112
|
+
if (now - cached.lastAccessed > EXECUTOR_CACHE_TTL_MS) {
|
|
113
|
+
toDelete.push(taskId);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
// Also remove oldest completed executors if we have too many
|
|
118
|
+
if (completedCount > MAX_CACHED_EXECUTORS) {
|
|
119
|
+
const completedTasks = Array.from(this.activeTasks.entries())
|
|
120
|
+
.filter(([_, cached]) => cached.status === 'completed')
|
|
121
|
+
.sort((a, b) => a[1].lastAccessed - b[1].lastAccessed);
|
|
122
|
+
const excessCount = completedCount - MAX_CACHED_EXECUTORS;
|
|
123
|
+
for (let i = 0; i < excessCount; i++) {
|
|
124
|
+
const [taskId] = completedTasks[i];
|
|
125
|
+
if (!toDelete.includes(taskId)) {
|
|
126
|
+
toDelete.push(taskId);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// Delete the marked executors
|
|
131
|
+
for (const taskId of toDelete) {
|
|
132
|
+
console.log(`[AgentDaemon] Cleaning up cached executor for task ${taskId}`);
|
|
133
|
+
this.activeTasks.delete(taskId);
|
|
134
|
+
}
|
|
135
|
+
if (toDelete.length > 0) {
|
|
136
|
+
console.log(`[AgentDaemon] Cleaned up ${toDelete.length} old executor(s). Active: ${this.activeTasks.size}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Queue a task for execution
|
|
141
|
+
* The task will either start immediately or be queued based on concurrency limits
|
|
142
|
+
*/
|
|
143
|
+
async startTask(task) {
|
|
144
|
+
await this.queueManager.enqueue(task);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Start executing a task immediately (internal - called by queue manager)
|
|
148
|
+
*/
|
|
149
|
+
async startTaskImmediate(task) {
|
|
150
|
+
console.log(`[AgentDaemon] Starting task ${task.id}: ${task.title}`);
|
|
151
|
+
// Get workspace details
|
|
152
|
+
const workspace = this.workspaceRepo.findById(task.workspaceId);
|
|
153
|
+
if (!workspace) {
|
|
154
|
+
throw new Error(`Workspace ${task.workspaceId} not found`);
|
|
155
|
+
}
|
|
156
|
+
console.log(`[AgentDaemon] Workspace found: ${workspace.name}`);
|
|
157
|
+
// Create task executor - wrapped in try-catch to handle provider initialization errors
|
|
158
|
+
let executor;
|
|
159
|
+
try {
|
|
160
|
+
console.log(`[AgentDaemon] Creating TaskExecutor...`);
|
|
161
|
+
executor = new executor_1.TaskExecutor(task, workspace, this);
|
|
162
|
+
console.log(`[AgentDaemon] TaskExecutor created successfully`);
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
console.error(`[AgentDaemon] Task ${task.id} failed to initialize:`, error);
|
|
166
|
+
this.taskRepo.update(task.id, {
|
|
167
|
+
status: 'failed',
|
|
168
|
+
error: error.message || 'Failed to initialize task executor',
|
|
169
|
+
completedAt: Date.now(),
|
|
170
|
+
});
|
|
171
|
+
this.logEvent(task.id, 'error', { error: error.message });
|
|
172
|
+
// Notify queue manager so it can start next task
|
|
173
|
+
this.queueManager.onTaskFinished(task.id);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
this.activeTasks.set(task.id, {
|
|
177
|
+
executor,
|
|
178
|
+
lastAccessed: Date.now(),
|
|
179
|
+
status: 'active',
|
|
180
|
+
});
|
|
181
|
+
// Update task status
|
|
182
|
+
this.taskRepo.update(task.id, { status: 'planning' });
|
|
183
|
+
this.logEvent(task.id, 'task_created', { task });
|
|
184
|
+
console.log(`[AgentDaemon] Task status updated to 'planning', starting execution...`);
|
|
185
|
+
// Start execution (non-blocking)
|
|
186
|
+
executor.execute().catch(error => {
|
|
187
|
+
console.error(`[AgentDaemon] Task ${task.id} execution failed:`, error);
|
|
188
|
+
this.taskRepo.update(task.id, {
|
|
189
|
+
status: 'failed',
|
|
190
|
+
error: error.message,
|
|
191
|
+
completedAt: Date.now(),
|
|
192
|
+
});
|
|
193
|
+
this.logEvent(task.id, 'error', { error: error.message });
|
|
194
|
+
this.activeTasks.delete(task.id);
|
|
195
|
+
// Notify queue manager so it can start next task
|
|
196
|
+
this.queueManager.onTaskFinished(task.id);
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Create a new task in the database and start it
|
|
201
|
+
* This is a convenience method used by the cron service
|
|
202
|
+
*/
|
|
203
|
+
async createTask(params) {
|
|
204
|
+
const task = this.taskRepo.create({
|
|
205
|
+
title: params.title,
|
|
206
|
+
prompt: params.prompt,
|
|
207
|
+
status: 'pending',
|
|
208
|
+
workspaceId: params.workspaceId,
|
|
209
|
+
budgetTokens: params.budgetTokens,
|
|
210
|
+
budgetCost: params.budgetCost,
|
|
211
|
+
});
|
|
212
|
+
// Start the task (will be queued if necessary)
|
|
213
|
+
await this.startTask(task);
|
|
214
|
+
return task;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Get a task by its ID
|
|
218
|
+
*/
|
|
219
|
+
async getTaskById(taskId) {
|
|
220
|
+
return this.taskRepo.findById(taskId);
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Get all child tasks for a given parent task
|
|
224
|
+
*/
|
|
225
|
+
async getChildTasks(parentTaskId) {
|
|
226
|
+
return this.taskRepo.findByParent(parentTaskId);
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Create a child task (sub-agent or parallel agent)
|
|
230
|
+
*/
|
|
231
|
+
async createChildTask(params) {
|
|
232
|
+
const task = this.taskRepo.create({
|
|
233
|
+
title: params.title,
|
|
234
|
+
prompt: params.prompt,
|
|
235
|
+
status: 'pending',
|
|
236
|
+
workspaceId: params.workspaceId,
|
|
237
|
+
parentTaskId: params.parentTaskId,
|
|
238
|
+
agentType: params.agentType,
|
|
239
|
+
agentConfig: params.agentConfig,
|
|
240
|
+
depth: params.depth ?? 0,
|
|
241
|
+
budgetTokens: params.budgetTokens,
|
|
242
|
+
budgetCost: params.budgetCost,
|
|
243
|
+
});
|
|
244
|
+
// Start the task (will be queued if necessary)
|
|
245
|
+
await this.startTask(task);
|
|
246
|
+
return task;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Cancel a running or queued task
|
|
250
|
+
*/
|
|
251
|
+
async cancelTask(taskId) {
|
|
252
|
+
// Check if task is queued (not yet started)
|
|
253
|
+
if (this.queueManager.cancelQueuedTask(taskId)) {
|
|
254
|
+
this.taskRepo.update(taskId, { status: 'cancelled', completedAt: Date.now() });
|
|
255
|
+
this.logEvent(taskId, 'task_cancelled', {
|
|
256
|
+
message: 'Task removed from queue',
|
|
257
|
+
});
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
// Task is running - cancel it
|
|
261
|
+
const cached = this.activeTasks.get(taskId);
|
|
262
|
+
if (cached) {
|
|
263
|
+
await cached.executor.cancel();
|
|
264
|
+
this.activeTasks.delete(taskId);
|
|
265
|
+
}
|
|
266
|
+
// Always notify queue manager to remove from running set
|
|
267
|
+
// (handles orphaned tasks that are in runningTaskIds but have no executor)
|
|
268
|
+
this.queueManager.onTaskFinished(taskId);
|
|
269
|
+
// Always emit cancelled event so UI updates
|
|
270
|
+
this.logEvent(taskId, 'task_cancelled', {
|
|
271
|
+
message: 'Task was stopped by user',
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Pause a running task
|
|
276
|
+
*/
|
|
277
|
+
async pauseTask(taskId) {
|
|
278
|
+
const cached = this.activeTasks.get(taskId);
|
|
279
|
+
if (cached) {
|
|
280
|
+
cached.lastAccessed = Date.now();
|
|
281
|
+
await cached.executor.pause();
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Resume a paused task
|
|
286
|
+
*/
|
|
287
|
+
async resumeTask(taskId) {
|
|
288
|
+
const cached = this.activeTasks.get(taskId);
|
|
289
|
+
if (cached) {
|
|
290
|
+
cached.lastAccessed = Date.now();
|
|
291
|
+
cached.status = 'active';
|
|
292
|
+
this.updateTaskStatus(taskId, 'executing');
|
|
293
|
+
this.logEvent(taskId, 'task_resumed', { message: 'Task resumed' });
|
|
294
|
+
await cached.executor.resume();
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Send stdin input to a running command in a task
|
|
299
|
+
*/
|
|
300
|
+
sendStdinToTask(taskId, input) {
|
|
301
|
+
const cached = this.activeTasks.get(taskId);
|
|
302
|
+
if (!cached) {
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
return cached.executor.sendStdin(input);
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Kill the running command in a task (send SIGINT like Ctrl+C)
|
|
309
|
+
* @param taskId - The task ID
|
|
310
|
+
* @param force - If true, send SIGKILL immediately instead of graceful escalation
|
|
311
|
+
*/
|
|
312
|
+
killCommandInTask(taskId, force) {
|
|
313
|
+
const cached = this.activeTasks.get(taskId);
|
|
314
|
+
if (!cached) {
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
return cached.executor.killShellProcess(force);
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Request approval from user for an action
|
|
321
|
+
*/
|
|
322
|
+
async requestApproval(taskId, type, description, details) {
|
|
323
|
+
const approval = this.approvalRepo.create({
|
|
324
|
+
taskId,
|
|
325
|
+
type: type,
|
|
326
|
+
description,
|
|
327
|
+
details,
|
|
328
|
+
status: 'pending',
|
|
329
|
+
requestedAt: Date.now(),
|
|
330
|
+
});
|
|
331
|
+
// Emit event to UI
|
|
332
|
+
this.logEvent(taskId, 'approval_requested', { approval });
|
|
333
|
+
// Wait for user response
|
|
334
|
+
return new Promise((resolve, reject) => {
|
|
335
|
+
// Timeout after 5 minutes
|
|
336
|
+
const timeoutHandle = setTimeout(() => {
|
|
337
|
+
const pending = this.pendingApprovals.get(approval.id);
|
|
338
|
+
if (pending && !pending.resolved) {
|
|
339
|
+
pending.resolved = true;
|
|
340
|
+
this.pendingApprovals.delete(approval.id);
|
|
341
|
+
this.approvalRepo.update(approval.id, 'denied');
|
|
342
|
+
this.logEvent(taskId, 'approval_denied', { approvalId: approval.id, reason: 'timeout' });
|
|
343
|
+
reject(new Error('Approval request timed out'));
|
|
344
|
+
}
|
|
345
|
+
}, 5 * 60 * 1000);
|
|
346
|
+
this.pendingApprovals.set(approval.id, { taskId, resolve, reject, resolved: false, timeoutHandle });
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Respond to an approval request
|
|
351
|
+
* Uses idempotency to prevent double-approval race conditions
|
|
352
|
+
* Implements C6: Approval Gate Enforcement
|
|
353
|
+
*/
|
|
354
|
+
async respondToApproval(approvalId, approved) {
|
|
355
|
+
// Generate idempotency key for this approval response
|
|
356
|
+
const idempotencyKey = concurrency_1.IdempotencyManager.generateKey('approval:respond', approvalId, approved ? 'approve' : 'deny');
|
|
357
|
+
// Check if this exact response was already processed
|
|
358
|
+
const existing = concurrency_1.approvalIdempotency.check(idempotencyKey);
|
|
359
|
+
if (existing.exists) {
|
|
360
|
+
console.log(`[AgentDaemon] Duplicate approval response ignored: ${approvalId}`);
|
|
361
|
+
return; // Silently ignore duplicate
|
|
362
|
+
}
|
|
363
|
+
// Start tracking this operation
|
|
364
|
+
if (!concurrency_1.approvalIdempotency.start(idempotencyKey)) {
|
|
365
|
+
console.log(`[AgentDaemon] Concurrent approval response in progress: ${approvalId}`);
|
|
366
|
+
return; // Another response is being processed
|
|
367
|
+
}
|
|
368
|
+
try {
|
|
369
|
+
const pending = this.pendingApprovals.get(approvalId);
|
|
370
|
+
if (pending && !pending.resolved) {
|
|
371
|
+
// Mark as resolved first to prevent race condition with timeout
|
|
372
|
+
pending.resolved = true;
|
|
373
|
+
// Clear the timeout
|
|
374
|
+
clearTimeout(pending.timeoutHandle);
|
|
375
|
+
this.pendingApprovals.delete(approvalId);
|
|
376
|
+
this.approvalRepo.update(approvalId, approved ? 'approved' : 'denied');
|
|
377
|
+
// Emit event so UI knows the approval has been handled
|
|
378
|
+
const eventType = approved ? 'approval_granted' : 'approval_denied';
|
|
379
|
+
this.logEvent(pending.taskId, eventType, { approvalId });
|
|
380
|
+
if (approved) {
|
|
381
|
+
pending.resolve(true);
|
|
382
|
+
}
|
|
383
|
+
else {
|
|
384
|
+
pending.reject(new Error('User denied approval'));
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
concurrency_1.approvalIdempotency.complete(idempotencyKey, { success: true });
|
|
388
|
+
}
|
|
389
|
+
catch (error) {
|
|
390
|
+
concurrency_1.approvalIdempotency.fail(idempotencyKey, error);
|
|
391
|
+
throw error;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Log an event for a task
|
|
396
|
+
*/
|
|
397
|
+
logEvent(taskId, type, payload) {
|
|
398
|
+
this.eventRepo.create({
|
|
399
|
+
taskId,
|
|
400
|
+
timestamp: Date.now(),
|
|
401
|
+
type: type,
|
|
402
|
+
payload,
|
|
403
|
+
});
|
|
404
|
+
this.logActivityForEvent(taskId, type, payload);
|
|
405
|
+
this.emitTaskEvent(taskId, type, payload);
|
|
406
|
+
// Capture to memory system (async, don't block)
|
|
407
|
+
this.captureToMemory(taskId, type, payload).catch((error) => {
|
|
408
|
+
// Silently log - memory capture is optional enhancement
|
|
409
|
+
console.debug('[AgentDaemon] Memory capture failed:', error);
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Capture task event to memory system for cross-session context
|
|
414
|
+
*/
|
|
415
|
+
async captureToMemory(taskId, type, payload) {
|
|
416
|
+
// Map event types to memory types
|
|
417
|
+
const memoryTypeMap = {
|
|
418
|
+
tool_call: 'observation',
|
|
419
|
+
tool_result: 'observation',
|
|
420
|
+
tool_error: 'error',
|
|
421
|
+
step_started: 'observation',
|
|
422
|
+
step_completed: 'observation',
|
|
423
|
+
step_failed: 'error',
|
|
424
|
+
assistant_message: 'observation',
|
|
425
|
+
plan_created: 'decision',
|
|
426
|
+
plan_revised: 'decision',
|
|
427
|
+
error: 'error',
|
|
428
|
+
verification_passed: 'insight',
|
|
429
|
+
verification_failed: 'error',
|
|
430
|
+
file_created: 'observation',
|
|
431
|
+
file_modified: 'observation',
|
|
432
|
+
};
|
|
433
|
+
const memoryType = memoryTypeMap[type];
|
|
434
|
+
if (!memoryType)
|
|
435
|
+
return;
|
|
436
|
+
const task = this.taskRepo.findById(taskId);
|
|
437
|
+
if (!task)
|
|
438
|
+
return;
|
|
439
|
+
// Build content string based on event type
|
|
440
|
+
let content = '';
|
|
441
|
+
if (type === 'tool_call') {
|
|
442
|
+
content = `Tool called: ${payload.tool || payload.name}\nInput: ${JSON.stringify(payload.input, null, 2)}`;
|
|
443
|
+
}
|
|
444
|
+
else if (type === 'tool_result') {
|
|
445
|
+
const result = typeof payload.result === 'string' ? payload.result : JSON.stringify(payload.result);
|
|
446
|
+
content = `Tool result for ${payload.tool || payload.name}:\n${result}`;
|
|
447
|
+
}
|
|
448
|
+
else if (type === 'tool_error') {
|
|
449
|
+
content = `Tool error for ${payload.tool || payload.name}: ${payload.error}`;
|
|
450
|
+
}
|
|
451
|
+
else if (type === 'assistant_message') {
|
|
452
|
+
content = payload.content || payload.message || JSON.stringify(payload);
|
|
453
|
+
}
|
|
454
|
+
else if (type === 'plan_created' || type === 'plan_revised') {
|
|
455
|
+
content = `Plan ${type === 'plan_revised' ? 'revised' : 'created'}:\n${JSON.stringify(payload.plan || payload, null, 2)}`;
|
|
456
|
+
}
|
|
457
|
+
else if (type === 'step_completed') {
|
|
458
|
+
content = `Step completed: ${payload.step?.description || JSON.stringify(payload)}`;
|
|
459
|
+
}
|
|
460
|
+
else if (type === 'step_failed') {
|
|
461
|
+
content = `Step failed: ${payload.step?.description || ''}\nError: ${payload.error || 'Unknown error'}`;
|
|
462
|
+
}
|
|
463
|
+
else if (type === 'file_created' || type === 'file_modified') {
|
|
464
|
+
content = `File ${type === 'file_created' ? 'created' : 'modified'}: ${payload.path}`;
|
|
465
|
+
}
|
|
466
|
+
else if (type === 'verification_passed') {
|
|
467
|
+
content = `Verification passed: ${payload.message || 'Task completed successfully'}`;
|
|
468
|
+
}
|
|
469
|
+
else if (type === 'verification_failed') {
|
|
470
|
+
content = `Verification failed: ${payload.message || payload.error || 'Unknown failure'}`;
|
|
471
|
+
}
|
|
472
|
+
else {
|
|
473
|
+
content = JSON.stringify(payload);
|
|
474
|
+
}
|
|
475
|
+
// Truncate very long content
|
|
476
|
+
if (content.length > 5000) {
|
|
477
|
+
content = content.slice(0, 5000) + '\n[... truncated]';
|
|
478
|
+
}
|
|
479
|
+
await MemoryService_1.MemoryService.capture(task.workspaceId, taskId, memoryType, content);
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Log notable task events to the Activity feed
|
|
483
|
+
*/
|
|
484
|
+
logActivityForEvent(taskId, type, payload) {
|
|
485
|
+
const task = this.taskRepo.findById(taskId);
|
|
486
|
+
if (!task)
|
|
487
|
+
return;
|
|
488
|
+
const activity = this.buildActivityFromEvent(task, type, payload);
|
|
489
|
+
if (!activity)
|
|
490
|
+
return;
|
|
491
|
+
const created = this.activityRepo.create(activity);
|
|
492
|
+
this.emitActivityEvent(created);
|
|
493
|
+
}
|
|
494
|
+
buildActivityFromEvent(task, type, payload) {
|
|
495
|
+
const actorType = task.assignedAgentRoleId ? 'agent' : 'system';
|
|
496
|
+
const agentRoleId = task.assignedAgentRoleId;
|
|
497
|
+
const activityType = type;
|
|
498
|
+
switch (type) {
|
|
499
|
+
case 'task_created':
|
|
500
|
+
return {
|
|
501
|
+
workspaceId: task.workspaceId,
|
|
502
|
+
taskId: task.id,
|
|
503
|
+
agentRoleId,
|
|
504
|
+
actorType,
|
|
505
|
+
activityType,
|
|
506
|
+
title: 'Task created',
|
|
507
|
+
description: task.title,
|
|
508
|
+
};
|
|
509
|
+
case 'task_completed':
|
|
510
|
+
return {
|
|
511
|
+
workspaceId: task.workspaceId,
|
|
512
|
+
taskId: task.id,
|
|
513
|
+
agentRoleId,
|
|
514
|
+
actorType,
|
|
515
|
+
activityType,
|
|
516
|
+
title: 'Task completed',
|
|
517
|
+
description: task.title,
|
|
518
|
+
};
|
|
519
|
+
case 'executing':
|
|
520
|
+
return {
|
|
521
|
+
workspaceId: task.workspaceId,
|
|
522
|
+
taskId: task.id,
|
|
523
|
+
agentRoleId,
|
|
524
|
+
actorType,
|
|
525
|
+
activityType: 'task_started',
|
|
526
|
+
title: 'Task started',
|
|
527
|
+
description: task.title,
|
|
528
|
+
};
|
|
529
|
+
case 'task_cancelled':
|
|
530
|
+
return {
|
|
531
|
+
workspaceId: task.workspaceId,
|
|
532
|
+
taskId: task.id,
|
|
533
|
+
agentRoleId,
|
|
534
|
+
actorType,
|
|
535
|
+
activityType: 'info',
|
|
536
|
+
title: 'Task cancelled',
|
|
537
|
+
description: task.title,
|
|
538
|
+
};
|
|
539
|
+
case 'task_paused':
|
|
540
|
+
return {
|
|
541
|
+
workspaceId: task.workspaceId,
|
|
542
|
+
taskId: task.id,
|
|
543
|
+
agentRoleId,
|
|
544
|
+
actorType,
|
|
545
|
+
activityType,
|
|
546
|
+
title: 'Task paused',
|
|
547
|
+
description: task.title,
|
|
548
|
+
};
|
|
549
|
+
case 'task_resumed':
|
|
550
|
+
return {
|
|
551
|
+
workspaceId: task.workspaceId,
|
|
552
|
+
taskId: task.id,
|
|
553
|
+
agentRoleId,
|
|
554
|
+
actorType,
|
|
555
|
+
activityType,
|
|
556
|
+
title: 'Task resumed',
|
|
557
|
+
description: task.title,
|
|
558
|
+
};
|
|
559
|
+
case 'approval_requested':
|
|
560
|
+
return {
|
|
561
|
+
workspaceId: task.workspaceId,
|
|
562
|
+
taskId: task.id,
|
|
563
|
+
agentRoleId,
|
|
564
|
+
actorType,
|
|
565
|
+
activityType: 'info',
|
|
566
|
+
title: 'Approval requested',
|
|
567
|
+
description: payload?.approval?.description || task.title,
|
|
568
|
+
};
|
|
569
|
+
case 'approval_granted':
|
|
570
|
+
return {
|
|
571
|
+
workspaceId: task.workspaceId,
|
|
572
|
+
taskId: task.id,
|
|
573
|
+
agentRoleId,
|
|
574
|
+
actorType,
|
|
575
|
+
activityType: 'info',
|
|
576
|
+
title: 'Approval granted',
|
|
577
|
+
description: task.title,
|
|
578
|
+
};
|
|
579
|
+
case 'approval_denied':
|
|
580
|
+
return {
|
|
581
|
+
workspaceId: task.workspaceId,
|
|
582
|
+
taskId: task.id,
|
|
583
|
+
agentRoleId,
|
|
584
|
+
actorType,
|
|
585
|
+
activityType: 'info',
|
|
586
|
+
title: 'Approval denied',
|
|
587
|
+
description: payload?.reason || task.title,
|
|
588
|
+
};
|
|
589
|
+
case 'error':
|
|
590
|
+
case 'step_failed':
|
|
591
|
+
case 'verification_failed':
|
|
592
|
+
return {
|
|
593
|
+
workspaceId: task.workspaceId,
|
|
594
|
+
taskId: task.id,
|
|
595
|
+
agentRoleId,
|
|
596
|
+
actorType,
|
|
597
|
+
activityType: 'error',
|
|
598
|
+
title: type === 'error' ? 'Task error' : 'Execution issue',
|
|
599
|
+
description: payload?.error || payload?.message || payload?.step?.description || task.title,
|
|
600
|
+
};
|
|
601
|
+
case 'verification_passed':
|
|
602
|
+
return {
|
|
603
|
+
workspaceId: task.workspaceId,
|
|
604
|
+
taskId: task.id,
|
|
605
|
+
agentRoleId,
|
|
606
|
+
actorType,
|
|
607
|
+
activityType: 'info',
|
|
608
|
+
title: 'Verification passed',
|
|
609
|
+
description: payload?.message || task.title,
|
|
610
|
+
};
|
|
611
|
+
case 'file_created':
|
|
612
|
+
return {
|
|
613
|
+
workspaceId: task.workspaceId,
|
|
614
|
+
taskId: task.id,
|
|
615
|
+
agentRoleId,
|
|
616
|
+
actorType,
|
|
617
|
+
activityType,
|
|
618
|
+
title: 'File created',
|
|
619
|
+
description: payload?.path || task.title,
|
|
620
|
+
};
|
|
621
|
+
case 'file_modified':
|
|
622
|
+
return {
|
|
623
|
+
workspaceId: task.workspaceId,
|
|
624
|
+
taskId: task.id,
|
|
625
|
+
agentRoleId,
|
|
626
|
+
actorType,
|
|
627
|
+
activityType,
|
|
628
|
+
title: 'File modified',
|
|
629
|
+
description: payload?.path || task.title,
|
|
630
|
+
};
|
|
631
|
+
case 'file_deleted':
|
|
632
|
+
return {
|
|
633
|
+
workspaceId: task.workspaceId,
|
|
634
|
+
taskId: task.id,
|
|
635
|
+
agentRoleId,
|
|
636
|
+
actorType,
|
|
637
|
+
activityType,
|
|
638
|
+
title: 'File deleted',
|
|
639
|
+
description: payload?.path || task.title,
|
|
640
|
+
};
|
|
641
|
+
case 'tool_call':
|
|
642
|
+
return {
|
|
643
|
+
workspaceId: task.workspaceId,
|
|
644
|
+
taskId: task.id,
|
|
645
|
+
agentRoleId,
|
|
646
|
+
actorType,
|
|
647
|
+
activityType: 'tool_used',
|
|
648
|
+
title: 'Tool used',
|
|
649
|
+
description: payload?.tool || payload?.name || task.title,
|
|
650
|
+
};
|
|
651
|
+
default:
|
|
652
|
+
return undefined;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
emitActivityEvent(activity) {
|
|
656
|
+
const windows = electron_1.BrowserWindow.getAllWindows();
|
|
657
|
+
windows.forEach(window => {
|
|
658
|
+
try {
|
|
659
|
+
if (!window.isDestroyed() && window.webContents && !window.webContents.isDestroyed()) {
|
|
660
|
+
window.webContents.send(types_1.IPC_CHANNELS.ACTIVITY_EVENT, { type: 'created', activity });
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
catch (error) {
|
|
664
|
+
console.error('[AgentDaemon] Error sending activity IPC:', error);
|
|
665
|
+
}
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
/**
|
|
669
|
+
* Register an artifact (file created during task execution)
|
|
670
|
+
* This allows files like screenshots to be sent back to the user
|
|
671
|
+
*/
|
|
672
|
+
registerArtifact(taskId, filePath, mimeType) {
|
|
673
|
+
try {
|
|
674
|
+
if (!fs.existsSync(filePath)) {
|
|
675
|
+
console.error(`[AgentDaemon] Artifact file not found: ${filePath}`);
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
const stats = fs.statSync(filePath);
|
|
679
|
+
const fileBuffer = fs.readFileSync(filePath);
|
|
680
|
+
const sha256 = crypto.createHash('sha256').update(fileBuffer).digest('hex');
|
|
681
|
+
this.artifactRepo.create({
|
|
682
|
+
taskId,
|
|
683
|
+
path: filePath,
|
|
684
|
+
mimeType,
|
|
685
|
+
sha256,
|
|
686
|
+
size: stats.size,
|
|
687
|
+
createdAt: Date.now(),
|
|
688
|
+
});
|
|
689
|
+
console.log(`[AgentDaemon] Registered artifact: ${filePath}`);
|
|
690
|
+
}
|
|
691
|
+
catch (error) {
|
|
692
|
+
console.error(`[AgentDaemon] Failed to register artifact:`, error);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Emit event to renderer process and local listeners
|
|
697
|
+
*/
|
|
698
|
+
emitTaskEvent(taskId, type, payload) {
|
|
699
|
+
// Emit to local EventEmitter listeners (for gateway integration)
|
|
700
|
+
try {
|
|
701
|
+
this.emit(type, { taskId, ...payload });
|
|
702
|
+
}
|
|
703
|
+
catch (error) {
|
|
704
|
+
console.error(`[AgentDaemon] Error emitting event ${type}:`, error);
|
|
705
|
+
}
|
|
706
|
+
// Emit to renderer process via IPC
|
|
707
|
+
const windows = electron_1.BrowserWindow.getAllWindows();
|
|
708
|
+
windows.forEach(window => {
|
|
709
|
+
// Check if window is still valid before sending
|
|
710
|
+
try {
|
|
711
|
+
if (!window.isDestroyed() && window.webContents && !window.webContents.isDestroyed()) {
|
|
712
|
+
window.webContents.send(types_1.IPC_CHANNELS.TASK_EVENT, {
|
|
713
|
+
taskId,
|
|
714
|
+
type,
|
|
715
|
+
payload,
|
|
716
|
+
timestamp: Date.now(),
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
catch (error) {
|
|
721
|
+
// Window might have been destroyed between check and send
|
|
722
|
+
console.error(`[AgentDaemon] Error sending IPC to window:`, error);
|
|
723
|
+
}
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Update task status
|
|
728
|
+
*/
|
|
729
|
+
updateTaskStatus(taskId, status) {
|
|
730
|
+
this.taskRepo.update(taskId, { status });
|
|
731
|
+
}
|
|
732
|
+
/**
|
|
733
|
+
* Get task by ID
|
|
734
|
+
*/
|
|
735
|
+
getTask(taskId) {
|
|
736
|
+
return this.taskRepo.findById(taskId);
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* Update task workspace ID in database
|
|
740
|
+
*/
|
|
741
|
+
updateTaskWorkspace(taskId, workspaceId) {
|
|
742
|
+
this.taskRepo.update(taskId, { workspaceId });
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Get workspace by ID
|
|
746
|
+
*/
|
|
747
|
+
getWorkspaceById(id) {
|
|
748
|
+
return this.workspaceRepo.findById(id);
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Get workspace by path
|
|
752
|
+
*/
|
|
753
|
+
getWorkspaceByPath(path) {
|
|
754
|
+
return this.workspaceRepo.findByPath(path);
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Create a new workspace with default permissions
|
|
758
|
+
*/
|
|
759
|
+
createWorkspace(name, path) {
|
|
760
|
+
const defaultPermissions = {
|
|
761
|
+
read: true,
|
|
762
|
+
write: true,
|
|
763
|
+
delete: false,
|
|
764
|
+
network: true,
|
|
765
|
+
shell: false,
|
|
766
|
+
};
|
|
767
|
+
return this.workspaceRepo.create(name, path, defaultPermissions);
|
|
768
|
+
}
|
|
769
|
+
/**
|
|
770
|
+
* Update task fields (for Goal Mode attempt tracking, etc.)
|
|
771
|
+
*/
|
|
772
|
+
updateTask(taskId, updates) {
|
|
773
|
+
this.taskRepo.update(taskId, updates);
|
|
774
|
+
}
|
|
775
|
+
/**
|
|
776
|
+
* Mark task as completed
|
|
777
|
+
* Note: We keep the executor in memory for follow-up messages (with TTL-based cleanup)
|
|
778
|
+
*/
|
|
779
|
+
completeTask(taskId) {
|
|
780
|
+
this.taskRepo.update(taskId, {
|
|
781
|
+
status: 'completed',
|
|
782
|
+
completedAt: Date.now(),
|
|
783
|
+
});
|
|
784
|
+
// Mark executor as completed for TTL-based cleanup
|
|
785
|
+
const cached = this.activeTasks.get(taskId);
|
|
786
|
+
if (cached) {
|
|
787
|
+
cached.status = 'completed';
|
|
788
|
+
cached.lastAccessed = Date.now();
|
|
789
|
+
}
|
|
790
|
+
this.logEvent(taskId, 'task_completed', { message: 'Task completed successfully' });
|
|
791
|
+
// Notify queue manager so it can start next task
|
|
792
|
+
this.queueManager.onTaskFinished(taskId);
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* Send a follow-up message to a task
|
|
796
|
+
*/
|
|
797
|
+
async sendMessage(taskId, message) {
|
|
798
|
+
let cached = this.activeTasks.get(taskId);
|
|
799
|
+
let executor;
|
|
800
|
+
// Always get fresh task and workspace from DB to pick up permission changes
|
|
801
|
+
const task = this.taskRepo.findById(taskId);
|
|
802
|
+
if (!task) {
|
|
803
|
+
throw new Error(`Task ${taskId} not found`);
|
|
804
|
+
}
|
|
805
|
+
const workspace = this.workspaceRepo.findById(task.workspaceId);
|
|
806
|
+
if (!workspace) {
|
|
807
|
+
throw new Error(`Workspace ${task.workspaceId} not found`);
|
|
808
|
+
}
|
|
809
|
+
if (!cached) {
|
|
810
|
+
// Task executor not in memory - need to recreate it
|
|
811
|
+
// Create new executor
|
|
812
|
+
executor = new executor_1.TaskExecutor(task, workspace, this);
|
|
813
|
+
// Rebuild conversation history from saved events
|
|
814
|
+
const events = this.eventRepo.findByTaskId(taskId);
|
|
815
|
+
if (events.length > 0) {
|
|
816
|
+
executor.rebuildConversationFromEvents(events);
|
|
817
|
+
}
|
|
818
|
+
this.activeTasks.set(taskId, {
|
|
819
|
+
executor,
|
|
820
|
+
lastAccessed: Date.now(),
|
|
821
|
+
status: 'active',
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
else {
|
|
825
|
+
executor = cached.executor;
|
|
826
|
+
// Update workspace to pick up permission changes (e.g., shell enabled)
|
|
827
|
+
executor.updateWorkspace(workspace);
|
|
828
|
+
cached.lastAccessed = Date.now();
|
|
829
|
+
cached.status = 'active';
|
|
830
|
+
}
|
|
831
|
+
// Send the message
|
|
832
|
+
await executor.sendMessage(message);
|
|
833
|
+
}
|
|
834
|
+
// ===== Queue Management Methods =====
|
|
835
|
+
/**
|
|
836
|
+
* Get current queue status
|
|
837
|
+
*/
|
|
838
|
+
getQueueStatus() {
|
|
839
|
+
return this.queueManager.getStatus();
|
|
840
|
+
}
|
|
841
|
+
/**
|
|
842
|
+
* Get queue settings
|
|
843
|
+
*/
|
|
844
|
+
getQueueSettings() {
|
|
845
|
+
return this.queueManager.getSettings();
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* Save queue settings
|
|
849
|
+
*/
|
|
850
|
+
saveQueueSettings(settings) {
|
|
851
|
+
this.queueManager.saveSettings(settings);
|
|
852
|
+
}
|
|
853
|
+
/**
|
|
854
|
+
* Clear stuck tasks from the queue
|
|
855
|
+
* Used to recover from stuck state when tasks fail to clean up
|
|
856
|
+
* Also properly cancels running tasks to clean up resources (browser sessions, etc.)
|
|
857
|
+
*/
|
|
858
|
+
async clearStuckTasks() {
|
|
859
|
+
// Get running task IDs before clearing
|
|
860
|
+
const status = this.queueManager.getStatus();
|
|
861
|
+
const runningTaskIds = [...status.runningTaskIds];
|
|
862
|
+
const queuedTaskIds = [...status.queuedTaskIds];
|
|
863
|
+
console.log(`[AgentDaemon] Clearing ${runningTaskIds.length} running tasks and ${queuedTaskIds.length} queued tasks`);
|
|
864
|
+
// Cancel all running tasks properly (this cleans up browser sessions, etc.)
|
|
865
|
+
for (const taskId of runningTaskIds) {
|
|
866
|
+
const cached = this.activeTasks.get(taskId);
|
|
867
|
+
if (cached) {
|
|
868
|
+
try {
|
|
869
|
+
console.log(`[AgentDaemon] Cancelling running task: ${taskId}`);
|
|
870
|
+
await cached.executor.cancel();
|
|
871
|
+
this.activeTasks.delete(taskId);
|
|
872
|
+
}
|
|
873
|
+
catch (error) {
|
|
874
|
+
console.error(`[AgentDaemon] Error cancelling task ${taskId}:`, error);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
// Now clear the queue state
|
|
879
|
+
return this.queueManager.clearStuckTasks();
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* Handle a task that has timed out
|
|
883
|
+
* Called by queue manager when a task exceeds the configured timeout
|
|
884
|
+
*/
|
|
885
|
+
async handleTaskTimeout(taskId) {
|
|
886
|
+
console.log(`[AgentDaemon] Task ${taskId} has timed out, cancelling...`);
|
|
887
|
+
const cached = this.activeTasks.get(taskId);
|
|
888
|
+
if (cached) {
|
|
889
|
+
try {
|
|
890
|
+
// Cancel the task (this cleans up browser sessions, etc.)
|
|
891
|
+
await cached.executor.cancel();
|
|
892
|
+
this.activeTasks.delete(taskId);
|
|
893
|
+
}
|
|
894
|
+
catch (error) {
|
|
895
|
+
console.error(`[AgentDaemon] Error cancelling timed out task ${taskId}:`, error);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
// Update task status to failed with timeout message
|
|
899
|
+
this.taskRepo.update(taskId, {
|
|
900
|
+
status: 'failed',
|
|
901
|
+
error: 'Task timed out - exceeded maximum allowed execution time',
|
|
902
|
+
});
|
|
903
|
+
// Emit timeout event
|
|
904
|
+
this.logEvent(taskId, 'step_timeout', {
|
|
905
|
+
message: 'Task exceeded maximum execution time and was automatically cancelled',
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* Emit queue update event to all windows
|
|
910
|
+
*/
|
|
911
|
+
emitQueueUpdate(status) {
|
|
912
|
+
const windows = electron_1.BrowserWindow.getAllWindows();
|
|
913
|
+
windows.forEach(window => {
|
|
914
|
+
try {
|
|
915
|
+
if (!window.isDestroyed() && window.webContents && !window.webContents.isDestroyed()) {
|
|
916
|
+
window.webContents.send(types_1.IPC_CHANNELS.QUEUE_UPDATE, status);
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
catch (error) {
|
|
920
|
+
console.error(`[AgentDaemon] Error sending queue update to window:`, error);
|
|
921
|
+
}
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Shutdown daemon
|
|
926
|
+
* Properly awaits all task cancellations and clears intervals
|
|
927
|
+
*/
|
|
928
|
+
async shutdown() {
|
|
929
|
+
console.log('Shutting down agent daemon...');
|
|
930
|
+
// Clear the cleanup interval
|
|
931
|
+
if (this.cleanupIntervalHandle) {
|
|
932
|
+
clearInterval(this.cleanupIntervalHandle);
|
|
933
|
+
this.cleanupIntervalHandle = undefined;
|
|
934
|
+
}
|
|
935
|
+
// Clear all pending approval timeouts and reject pending promises
|
|
936
|
+
this.pendingApprovals.forEach((pending, approvalId) => {
|
|
937
|
+
clearTimeout(pending.timeoutHandle);
|
|
938
|
+
if (!pending.resolved) {
|
|
939
|
+
pending.resolved = true;
|
|
940
|
+
pending.reject(new Error('Daemon shutting down'));
|
|
941
|
+
}
|
|
942
|
+
});
|
|
943
|
+
this.pendingApprovals.clear();
|
|
944
|
+
// Cancel all active tasks and wait for them to complete
|
|
945
|
+
const cancelPromises = [];
|
|
946
|
+
this.activeTasks.forEach((cached, taskId) => {
|
|
947
|
+
const promise = cached.executor.cancel().catch(err => {
|
|
948
|
+
console.error(`Error cancelling task ${taskId}:`, err);
|
|
949
|
+
});
|
|
950
|
+
cancelPromises.push(promise);
|
|
951
|
+
});
|
|
952
|
+
// Wait for all cancellations to complete (with timeout)
|
|
953
|
+
await Promise.race([
|
|
954
|
+
Promise.all(cancelPromises),
|
|
955
|
+
new Promise(resolve => setTimeout(resolve, 5000)), // 5 second timeout
|
|
956
|
+
]);
|
|
957
|
+
this.activeTasks.clear();
|
|
958
|
+
// Remove all EventEmitter listeners to prevent memory leaks
|
|
959
|
+
this.removeAllListeners();
|
|
960
|
+
console.log('Agent daemon shutdown complete');
|
|
961
|
+
}
|
|
962
|
+
/**
|
|
963
|
+
* Prune old conversation snapshots for a task, keeping only the most recent one.
|
|
964
|
+
* This prevents database bloat from accumulating snapshots.
|
|
965
|
+
*/
|
|
966
|
+
pruneOldSnapshots(taskId) {
|
|
967
|
+
try {
|
|
968
|
+
this.eventRepo.pruneOldSnapshots(taskId);
|
|
969
|
+
}
|
|
970
|
+
catch (error) {
|
|
971
|
+
console.debug('[AgentDaemon] Failed to prune old snapshots:', error);
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
exports.AgentDaemon = AgentDaemon;
|