cowork-os 0.3.23 → 0.3.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/README.md +89 -14
  2. package/dist/electron/electron/agent/custom-skill-loader.js +31 -1
  3. package/dist/electron/electron/agent/daemon.js +164 -13
  4. package/dist/electron/electron/agent/executor.js +716 -54
  5. package/dist/electron/electron/agent/llm/azure-openai-provider.js +328 -0
  6. package/dist/electron/electron/agent/llm/bedrock-provider.js +49 -9
  7. package/dist/electron/electron/agent/llm/index.js +3 -1
  8. package/dist/electron/electron/agent/llm/provider-factory.js +34 -2
  9. package/dist/electron/electron/agent/search/provider-factory.js +38 -2
  10. package/dist/electron/electron/agent/tools/file-tools.js +66 -3
  11. package/dist/electron/electron/agent/tools/grep-tools.js +90 -10
  12. package/dist/electron/electron/agent/tools/image-tools.js +11 -1
  13. package/dist/electron/electron/agent/tools/registry.js +7 -10
  14. package/dist/electron/electron/agent/tools/search-tools.js +28 -10
  15. package/dist/electron/electron/agents/agent-dispatch.js +63 -0
  16. package/dist/electron/electron/database/repositories.js +19 -5
  17. package/dist/electron/electron/database/schema.js +8 -0
  18. package/dist/electron/electron/gateway/channels/whatsapp.js +55 -0
  19. package/dist/electron/electron/gateway/index.js +74 -1
  20. package/dist/electron/electron/gateway/router.js +86 -11
  21. package/dist/electron/electron/ipc/handlers.js +156 -129
  22. package/dist/electron/electron/memory/MemoryService.js +2 -1
  23. package/dist/electron/electron/preload.js +4 -0
  24. package/dist/electron/electron/settings/appearance-manager.js +18 -1
  25. package/dist/electron/electron/utils/validation.js +18 -2
  26. package/dist/electron/shared/types.js +2 -0
  27. package/package.json +3 -2
  28. package/resources/skills/nano-banana-pro.json +4 -4
  29. package/resources/skills/openai-image-gen.json +3 -3
  30. package/resources/skills/scripts/gen.py +163 -0
  31. package/resources/skills/scripts/generate_image.py +91 -0
  32. package/src/electron/agent/custom-skill-loader.ts +34 -1
  33. package/src/electron/agent/daemon.ts +210 -14
  34. package/src/electron/agent/executor.ts +919 -57
  35. package/src/electron/agent/llm/azure-openai-provider.ts +388 -0
  36. package/src/electron/agent/llm/bedrock-provider.ts +62 -9
  37. package/src/electron/agent/llm/index.ts +1 -0
  38. package/src/electron/agent/llm/provider-factory.ts +45 -0
  39. package/src/electron/agent/llm/types.ts +5 -0
  40. package/src/electron/agent/search/provider-factory.ts +43 -2
  41. package/src/electron/agent/tools/builtin-settings.ts +2 -0
  42. package/src/electron/agent/tools/file-tools.ts +66 -3
  43. package/src/electron/agent/tools/gmail-tools.ts +240 -0
  44. package/src/electron/agent/tools/google-calendar-tools.ts +258 -0
  45. package/src/electron/agent/tools/google-drive-tools.ts +5 -5
  46. package/src/electron/agent/tools/grep-tools.ts +97 -12
  47. package/src/electron/agent/tools/image-tools.ts +11 -1
  48. package/src/electron/agent/tools/registry.ts +229 -10
  49. package/src/electron/agent/tools/search-tools.ts +29 -11
  50. package/src/electron/agents/agent-dispatch.ts +79 -0
  51. package/src/electron/database/repositories.ts +58 -6
  52. package/src/electron/database/schema.ts +8 -0
  53. package/src/electron/gateway/channels/discord.ts +4 -0
  54. package/src/electron/gateway/channels/google-chat.ts +3 -0
  55. package/src/electron/gateway/channels/line.ts +3 -0
  56. package/src/electron/gateway/channels/matrix-client.ts +15 -0
  57. package/src/electron/gateway/channels/matrix.ts +31 -0
  58. package/src/electron/gateway/channels/mattermost.ts +3 -0
  59. package/src/electron/gateway/channels/signal.ts +3 -0
  60. package/src/electron/gateway/channels/slack.ts +9 -4
  61. package/src/electron/gateway/channels/teams.ts +4 -0
  62. package/src/electron/gateway/channels/telegram.ts +2 -0
  63. package/src/electron/gateway/channels/twitch.ts +2 -0
  64. package/src/electron/gateway/channels/types.ts +8 -0
  65. package/src/electron/gateway/channels/whatsapp.ts +66 -0
  66. package/src/electron/gateway/index.ts +94 -2
  67. package/src/electron/gateway/router.ts +97 -12
  68. package/src/electron/gateway/security.ts +21 -9
  69. package/src/electron/ipc/handlers.ts +199 -163
  70. package/src/electron/memory/MemoryService.ts +2 -0
  71. package/src/electron/preload.ts +48 -16
  72. package/src/electron/settings/appearance-manager.ts +20 -2
  73. package/src/electron/settings/google-workspace-manager.ts +59 -0
  74. package/src/electron/utils/gmail-api.ts +121 -0
  75. package/src/electron/utils/google-calendar-api.ts +115 -0
  76. package/src/electron/utils/google-workspace-api.ts +228 -0
  77. package/src/electron/utils/google-workspace-auth.ts +109 -0
  78. package/src/electron/utils/google-workspace-oauth.ts +232 -0
  79. package/src/electron/utils/validation.ts +28 -2
  80. package/src/renderer/App.tsx +99 -6
  81. package/src/renderer/components/ActivityFeedItem.tsx +34 -17
  82. package/src/renderer/components/AgentWorkingStatePanel.tsx +7 -5
  83. package/src/renderer/components/AppearanceSettings.tsx +37 -2
  84. package/src/renderer/components/BlueBubblesSettings.tsx +18 -7
  85. package/src/renderer/components/ControlPlaneSettings.tsx +2 -0
  86. package/src/renderer/components/DiscordSettings.tsx +18 -7
  87. package/src/renderer/components/EmailSettings.tsx +18 -7
  88. package/src/renderer/components/FileViewer.tsx +21 -13
  89. package/src/renderer/components/GoogleChatSettings.tsx +17 -7
  90. package/src/renderer/components/GoogleWorkspaceSettings.tsx +332 -0
  91. package/src/renderer/components/ImessageSettings.tsx +22 -11
  92. package/src/renderer/components/LineIcons.tsx +376 -0
  93. package/src/renderer/components/LineSettings.tsx +18 -7
  94. package/src/renderer/components/MainContent.tsx +522 -94
  95. package/src/renderer/components/MatrixSettings.tsx +18 -7
  96. package/src/renderer/components/MattermostSettings.tsx +18 -7
  97. package/src/renderer/components/NodesSettings.tsx +58 -99
  98. package/src/renderer/components/NotificationPanel.tsx +25 -11
  99. package/src/renderer/components/RightPanel.tsx +141 -28
  100. package/src/renderer/components/ScheduledTasksSettings.tsx +10 -62
  101. package/src/renderer/components/SearchSettings.tsx +118 -114
  102. package/src/renderer/components/Settings.tsx +1179 -1008
  103. package/src/renderer/components/Sidebar.tsx +78 -19
  104. package/src/renderer/components/SignalSettings.tsx +18 -7
  105. package/src/renderer/components/SkillHubBrowser.tsx +144 -185
  106. package/src/renderer/components/SlackSettings.tsx +18 -7
  107. package/src/renderer/components/TaskQuickActions.tsx +11 -6
  108. package/src/renderer/components/TaskTimeline.tsx +58 -26
  109. package/src/renderer/components/TeamsSettings.tsx +18 -7
  110. package/src/renderer/components/TelegramSettings.tsx +18 -7
  111. package/src/renderer/components/ThemeIcon.tsx +16 -0
  112. package/src/renderer/components/TwitchSettings.tsx +18 -7
  113. package/src/renderer/components/VoiceSettings.tsx +30 -74
  114. package/src/renderer/components/WhatsAppSettings.tsx +48 -37
  115. package/src/renderer/components/WorkingStateHistory.tsx +7 -5
  116. package/src/renderer/components/WorkspaceSelector.tsx +42 -13
  117. package/src/renderer/styles/index.css +1925 -214
  118. package/src/shared/types.ts +32 -8
  119. package/src/electron/settings/google-drive-manager.ts +0 -58
  120. package/src/electron/utils/google-drive-api.ts +0 -183
  121. package/src/renderer/components/GoogleDriveSettings.tsx +0 -201
package/README.md CHANGED
@@ -29,10 +29,35 @@ Your AI needs a secure home. CoWork OS provides the runtime, security layers, an
29
29
  | **20+ AI Providers** | Claude, OpenAI, Gemini, Bedrock, OpenRouter, Ollama (free/local), Groq, xAI, Kimi, Mistral, Cerebras, MiniMax, Qwen, Copilot, and more |
30
30
  | **14 Messaging Channels** | WhatsApp, Telegram, Discord, Slack, Teams, Google Chat, iMessage, Signal, Mattermost, Matrix, Twitch, LINE, BlueBubbles, Email |
31
31
  | **8 Enterprise Connectors** | Salesforce, Jira, HubSpot, Zendesk, ServiceNow, Linear, Asana, Okta |
32
- | **6 Cloud Storage** | Notion, Box, OneDrive, Google Drive, Dropbox, SharePoint |
33
- | **Security-First** | 1800+ unit tests, configurable guardrails, approval workflows |
32
+ | **6 Cloud Storage** | Notion, Box, OneDrive, Google Workspace (Drive/Gmail/Calendar), Dropbox, SharePoint |
33
+ | **Security-First** | 2350+ unit tests, configurable guardrails, approval workflows |
34
34
  | **Local-First** | Your data stays on your machine. BYOK (Bring Your Own Key) |
35
35
 
36
+ ### Feature Comparison (CoWork OS vs OpenClaw vs Claude Cowork)
37
+
38
+ | Feature | CoWork OS | OpenClaw | Claude Cowork |
39
+ |---|---|---|---|
40
+ | Model providers / BYOK | 20+ providers (Claude, OpenAI, Gemini, Bedrock, OpenRouter, Ollama, Groq, xAI, Kimi, Mistral, Cerebras, MiniMax, Qwen, Copilot, and more); BYOK | Any model supported; OAuth + API keys (Anthropic/OpenAI listed) | Claude plans (Pro/Max/Team/Enterprise); other providers not mentioned |
41
+ | Local-first data control | Data stays on your machine; BYOK | Run on your own devices; local-first gateway | Runs locally in an isolated VM; choose folders/connectors; local history for Team/Enterprise |
42
+ | Desktop platforms | macOS (cross-platform planned) | macOS, Linux, Windows (via WSL2) | macOS only (research preview) |
43
+ | Messaging channels | 14 channels: WhatsApp, Telegram, Discord, Slack, Teams, Google Chat, iMessage, Signal, Mattermost, Matrix, Twitch, LINE, BlueBubbles, Email | Multi-channel inbox: WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, BlueBubbles, iMessage, Teams, Matrix, Zalo, WebChat, macOS, iOS/Android | Not stated (desktop app + connectors) |
44
+ | Enterprise connectors | Salesforce, Jira, HubSpot, Zendesk, ServiceNow, Linear, Asana, Okta | Not stated | Connectors supported; examples include Slack, Notion, GitHub, Linear |
45
+ | Cloud storage connectors | Notion, Box, OneDrive, Google Workspace, Dropbox, SharePoint | Not stated | Not stated |
46
+ | Mobile companions | iOS + Android companion apps (local network) | iOS/Android nodes + macOS menu bar app | Not stated (macOS only) |
47
+ | Scheduling / cron | Recurring tasks via cron expressions | Cron + wakeups | Not stated |
48
+ | Remote access | Tailscale or SSH tunnels | Tailscale Serve/Funnel or SSH tunnels | Not stated |
49
+ | WebSocket API / control plane | WebSocket API for custom integrations | Gateway WebSocket control plane | Not stated |
50
+ | Browser automation | Playwright automation | Browser control (Chrome/Chromium) | Browser access via Chrome connector |
51
+ | Skills / plugins | 75+ bundled skills | Skills platform (bundled, managed, workspace skills) | Not stated |
52
+ | MCP support | Yes | Not stated | Not stated |
53
+ | Guardrails (token/cost/iteration limits) | Configurable guardrails | Not stated | Not stated |
54
+ | Approval workflows | Required for destructive operations | Not stated | Human approval before significant actions |
55
+ | Access control (pairing/allowlists) | Pairing codes + per-channel allowlists | DM pairing + allowlists | Folder/connector access controls |
56
+ | Sandbox isolation | macOS sandbox-exec or Docker | Not stated | Runs locally in an isolated VM |
57
+ | Encrypted credential storage | OS keychain + AES-256 fallback | Not stated | Not stated |
58
+
59
+ _Sources: [OpenClaw README](https://github.com/openclaw/openclaw), [OpenClaw docs](https://docs.openclaw.ai), and the [Claude Cowork product page](https://claude.com/product/cowork). “Not stated” means not documented in public materials._
60
+
36
61
  > **Status**: macOS desktop app (cross-platform support planned)
37
62
 
38
63
  ---
@@ -65,7 +90,7 @@ Your AI needs a secure home. CoWork OS provides the runtime, security layers, an
65
90
  - **Dangerous command blocking**: Built-in patterns + custom regex rules
66
91
  - **Approval workflows**: User consent required for destructive operations
67
92
  - **Pairing & allowlists**: Control who can access your AI via messaging channels
68
- - **1800+ tests**: Comprehensive test coverage for access control and policies
93
+ - **2350+ tests**: Comprehensive test coverage for access control and policies
69
94
 
70
95
  ### Your Data, Your Control
71
96
 
@@ -218,6 +243,18 @@ All channels support:
218
243
  - Session management
219
244
  - Rate limiting
220
245
 
246
+ ### Visual Theme System
247
+
248
+ Customize the app appearance with multiple theme options.
249
+
250
+ | Theme | Description |
251
+ |-------|-------------|
252
+ | **System** | Follows your macOS light/dark mode preference |
253
+ | **Light** | Clean light interface |
254
+ | **Dark** | Dark mode for reduced eye strain |
255
+
256
+ Configure in **Settings** > **Appearance**.
257
+
221
258
  ### Agent Capabilities
222
259
 
223
260
  - **Task-Based Workflow**: Multi-step execution with plan-execute-observe loops
@@ -226,6 +263,7 @@ All channels support:
226
263
  - **75+ Built-in Skills**: GitHub, Slack, Notion, Spotify, Apple Notes, and more
227
264
  - **Document Creation**: Excel, Word, PDF, PowerPoint with professional formatting
228
265
  - **Persistent Memory**: Cross-session context with privacy-aware observation capture
266
+ - **Workspace Recency**: Workspaces ordered by last used time for quick access
229
267
 
230
268
  ### Voice Mode (NEW)
231
269
 
@@ -1377,7 +1415,13 @@ Access CoWork OS from your iPhone, iPad, or Android device via the local network
1377
1415
 
1378
1416
  ## Web Search Integration
1379
1417
 
1380
- Multi-provider web search for research tasks.
1418
+ Multi-provider web search for research tasks with automatic retry and fallback.
1419
+
1420
+ ### Features
1421
+
1422
+ - **Automatic Retry**: Transient errors (rate limits, timeouts) trigger automatic retry with exponential backoff
1423
+ - **Provider Fallback**: If one provider fails, automatically tries the next configured provider
1424
+ - **Graceful Degradation**: Returns helpful error messages instead of failing silently
1381
1425
 
1382
1426
  ### Supported Providers
1383
1427
 
@@ -1410,6 +1454,8 @@ Claude Code-style tools for developers.
1410
1454
  → grep pattern="TODO:" glob="*.ts"
1411
1455
  ```
1412
1456
 
1457
+ **Smart Document Detection**: Automatically detects document-heavy workspaces (PDF/DOCX) and provides helpful guidance to use `read_file` instead, since grep only searches text files.
1458
+
1413
1459
  ### edit_file - Surgical Editing
1414
1460
 
1415
1461
  ```
@@ -1553,25 +1599,47 @@ onedrive_action({
1553
1599
 
1554
1600
  ---
1555
1601
 
1556
- ## Google Drive Integration
1602
+ ## Google Workspace Integration
1557
1603
 
1558
- Configure in **Settings > Integrations > Google Drive**. Use `google_drive_action` to search, read, and manage Google Drive files and folders. Write actions (create, upload, delete) require approval.
1604
+ Configure in **Settings > Integrations > Google Workspace**. Unified access to Gmail, Google Calendar, and Google Drive with shared OAuth authentication.
1559
1605
 
1560
- ### List files
1606
+ ### Available Tools
1607
+
1608
+ | Service | Tool | Actions |
1609
+ |---------|------|---------|
1610
+ | **Drive** | `google_drive_action` | list_files, search, upload_file, download_file, delete_file |
1611
+ | **Gmail** | `gmail_action` | list_messages, search, send_email, read_email, create_draft |
1612
+ | **Calendar** | `google_calendar_action` | list_events, create_event, update_event, delete_event |
1613
+
1614
+ ### Gmail - Send an email
1561
1615
 
1562
1616
  ```ts
1563
- google_drive_action({
1564
- action: "list_files",
1565
- page_size: 20
1617
+ gmail_action({
1618
+ action: "send_email",
1619
+ to: "recipient@example.com",
1620
+ subject: "Weekly Report",
1621
+ body: "Please find the attached report..."
1566
1622
  });
1567
1623
  ```
1568
1624
 
1569
- ### Upload a file
1625
+ ### Calendar - Create an event
1626
+
1627
+ ```ts
1628
+ google_calendar_action({
1629
+ action: "create_event",
1630
+ title: "Team Standup",
1631
+ start_time: "2025-02-10T09:00:00",
1632
+ end_time: "2025-02-10T09:30:00",
1633
+ attendees: ["team@example.com"]
1634
+ });
1635
+ ```
1636
+
1637
+ ### Drive - List files
1570
1638
 
1571
1639
  ```ts
1572
1640
  google_drive_action({
1573
- action: "upload_file",
1574
- file_path: "reports/summary.pdf"
1641
+ action: "list_files",
1642
+ page_size: 20
1575
1643
  });
1576
1644
  ```
1577
1645
 
@@ -1876,7 +1944,7 @@ Users must comply with their model provider's terms:
1876
1944
  - [x] Tailscale and SSH remote access
1877
1945
  - [x] Personality system
1878
1946
  - [x] 75+ bundled skills
1879
- - [x] 1800+ unit tests
1947
+ - [x] 2350+ unit tests
1880
1948
  - [x] Docker-based sandboxing (cross-platform)
1881
1949
  - [x] Per-context security policies (DM vs group)
1882
1950
  - [x] Enhanced pairing code UI with countdown
@@ -1885,6 +1953,13 @@ Users must comply with their model provider's terms:
1885
1953
  - [x] Voice Mode with ElevenLabs and OpenAI integration
1886
1954
  - [x] Enterprise MCP Connectors (Salesforce, Jira, HubSpot, Zendesk, ServiceNow, Linear, Asana, Okta)
1887
1955
  - [x] Cloud Storage Integrations (Notion, Box, OneDrive, Google Drive, Dropbox, SharePoint)
1956
+ - [x] Visual Theme System (Light/Dark/System modes)
1957
+ - [x] Workspace recency ordering
1958
+ - [x] Web search retry with exponential backoff
1959
+ - [x] Google Workspace Integration (Gmail, Calendar, Drive with shared OAuth)
1960
+ - [x] Gateway channel cleanup and enhanced security (Matrix direct rooms, Slack groups)
1961
+ - [x] Agent transient error retry logic for improved reliability
1962
+ - [x] Smart parameter inference for document creation tools
1888
1963
 
1889
1964
  ### Planned
1890
1965
 
@@ -359,7 +359,7 @@ class CustomSkillLoader {
359
359
  * Expand a skill's prompt template with parameter values
360
360
  */
361
361
  expandPrompt(skill, parameterValues) {
362
- let prompt = skill.prompt;
362
+ let prompt = this.expandBaseDir(skill.prompt, skill);
363
363
  // Replace {{param}} placeholders with values
364
364
  if (skill.parameters) {
365
365
  for (const param of skill.parameters) {
@@ -372,6 +372,36 @@ class CustomSkillLoader {
372
372
  prompt = prompt.replace(/\{\{[^}]+\}\}/g, '');
373
373
  return prompt.trim();
374
374
  }
375
+ /**
376
+ * Expand {baseDir} placeholders to the resolved skill base directory.
377
+ */
378
+ expandBaseDir(prompt, skill) {
379
+ if (!prompt.includes('{baseDir}')) {
380
+ return prompt;
381
+ }
382
+ const baseDir = this.resolveBaseDir(skill);
383
+ return prompt.replace(/\{baseDir\}/g, baseDir);
384
+ }
385
+ resolveBaseDir(skill) {
386
+ const fileDir = skill.filePath ? path.dirname(skill.filePath) : this.bundledSkillsDir;
387
+ const candidates = [
388
+ fileDir,
389
+ this.bundledSkillsDir,
390
+ this.managedSkillsDir,
391
+ this.workspaceSkillsDir || '',
392
+ ].filter(Boolean);
393
+ for (const dir of candidates) {
394
+ try {
395
+ if (fs.existsSync(path.join(dir, 'scripts'))) {
396
+ return dir;
397
+ }
398
+ }
399
+ catch {
400
+ // ignore and continue
401
+ }
402
+ }
403
+ return fileDir;
404
+ }
375
405
  /**
376
406
  * Get eligible skills (those that meet all requirements)
377
407
  */
@@ -40,6 +40,9 @@ const fs = __importStar(require("fs"));
40
40
  const crypto = __importStar(require("crypto"));
41
41
  const repositories_1 = require("../database/repositories");
42
42
  const ActivityRepository_1 = require("../activity/ActivityRepository");
43
+ const AgentRoleRepository_1 = require("../agents/AgentRoleRepository");
44
+ const MentionRepository_1 = require("../agents/MentionRepository");
45
+ const agent_dispatch_1 = require("../agents/agent-dispatch");
43
46
  const types_1 = require("../../shared/types");
44
47
  const executor_1 = require("./executor");
45
48
  const queue_manager_1 = require("./queue-manager");
@@ -63,6 +66,10 @@ class AgentDaemon extends events_1.EventEmitter {
63
66
  this.pendingApprovals = new Map();
64
67
  // Activity throttle: Map<taskId:eventType, lastTimestamp>
65
68
  this.activityThrottle = new Map();
69
+ this.pendingRetries = new Map();
70
+ this.retryCounts = new Map();
71
+ this.maxTaskRetries = 2;
72
+ this.retryDelayMs = 30 * 1000;
66
73
  const db = dbManager.getDatabase();
67
74
  this.taskRepo = new repositories_1.TaskRepository(db);
68
75
  this.eventRepo = new repositories_1.TaskEventRepository(db);
@@ -70,6 +77,8 @@ class AgentDaemon extends events_1.EventEmitter {
70
77
  this.approvalRepo = new repositories_1.ApprovalRepository(db);
71
78
  this.artifactRepo = new repositories_1.ArtifactRepository(db);
72
79
  this.activityRepo = new ActivityRepository_1.ActivityRepository(db);
80
+ this.agentRoleRepo = new AgentRoleRepository_1.AgentRoleRepository(db);
81
+ this.mentionRepo = new MentionRepository_1.MentionRepository(db);
73
82
  // Initialize queue manager with callbacks
74
83
  this.queueManager = new queue_manager_1.TaskQueueManager({
75
84
  startTaskImmediate: (task) => this.startTaskImmediate(task),
@@ -250,6 +259,123 @@ class AgentDaemon extends events_1.EventEmitter {
250
259
  await this.startTask(task);
251
260
  return task;
252
261
  }
262
+ buildPlanSummary(plan) {
263
+ if (!plan)
264
+ return undefined;
265
+ const lines = [];
266
+ if (plan.description) {
267
+ lines.push(`Plan: ${plan.description}`);
268
+ }
269
+ if (plan.steps && plan.steps.length > 0) {
270
+ lines.push('Steps:');
271
+ const stepLines = plan.steps
272
+ .slice(0, 7)
273
+ .map((step) => `- ${step.description}`);
274
+ lines.push(...stepLines);
275
+ if (plan.steps.length > 7) {
276
+ lines.push(`- …and ${plan.steps.length - 7} more steps`);
277
+ }
278
+ }
279
+ return lines.length > 0 ? lines.join('\n') : undefined;
280
+ }
281
+ emitActivityEvent(activity) {
282
+ const windows = electron_1.BrowserWindow.getAllWindows();
283
+ windows.forEach(window => {
284
+ try {
285
+ if (!window.isDestroyed() && window.webContents && !window.webContents.isDestroyed()) {
286
+ window.webContents.send(types_1.IPC_CHANNELS.ACTIVITY_EVENT, { type: 'created', activity });
287
+ }
288
+ }
289
+ catch (error) {
290
+ console.error('[AgentDaemon] Error sending activity IPC:', error);
291
+ }
292
+ });
293
+ }
294
+ emitMentionEvent(mention) {
295
+ const windows = electron_1.BrowserWindow.getAllWindows();
296
+ windows.forEach(window => {
297
+ try {
298
+ if (!window.isDestroyed() && window.webContents && !window.webContents.isDestroyed()) {
299
+ window.webContents.send(types_1.IPC_CHANNELS.MENTION_EVENT, { type: 'created', mention });
300
+ }
301
+ }
302
+ catch (error) {
303
+ console.error('[AgentDaemon] Error sending mention IPC:', error);
304
+ }
305
+ });
306
+ }
307
+ /**
308
+ * Dispatch mentioned agent roles after the main plan is created.
309
+ * This avoids starting sub-agents before the task is clearly defined.
310
+ */
311
+ async dispatchMentionedAgents(taskId, plan) {
312
+ const task = this.taskRepo.findById(taskId);
313
+ if (!task || task.parentTaskId)
314
+ return;
315
+ const mentionedRoleIds = (task.mentionedAgentRoleIds || []).filter(Boolean);
316
+ if (mentionedRoleIds.length === 0)
317
+ return;
318
+ const activeRoles = this.agentRoleRepo.findAll(false).filter(role => role.isActive);
319
+ const mentionedRoles = activeRoles.filter(role => mentionedRoleIds.includes(role.id));
320
+ if (mentionedRoles.length === 0)
321
+ return;
322
+ const existingChildren = this.taskRepo.findByParent(taskId);
323
+ const assignedRoleIds = new Set(existingChildren
324
+ .map(child => child.assignedAgentRoleId)
325
+ .filter((id) => typeof id === 'string' && id.length > 0));
326
+ const rolesToDispatch = mentionedRoles.filter(role => !assignedRoleIds.has(role.id));
327
+ if (rolesToDispatch.length === 0)
328
+ return;
329
+ const planSummary = this.buildPlanSummary(plan);
330
+ for (const role of rolesToDispatch) {
331
+ const childPrompt = (0, agent_dispatch_1.buildAgentDispatchPrompt)(role, { title: task.title, prompt: task.prompt }, planSummary ? { planSummary } : undefined);
332
+ const childTask = await this.createChildTask({
333
+ title: `@${role.displayName}: ${task.title}`,
334
+ prompt: childPrompt,
335
+ workspaceId: task.workspaceId,
336
+ parentTaskId: task.id,
337
+ agentType: 'sub',
338
+ agentConfig: {
339
+ ...(role.modelKey ? { modelKey: role.modelKey } : {}),
340
+ ...(role.personalityId ? { personalityId: role.personalityId } : {}),
341
+ retainMemory: false,
342
+ },
343
+ });
344
+ this.taskRepo.update(childTask.id, {
345
+ assignedAgentRoleId: role.id,
346
+ boardColumn: 'todo',
347
+ });
348
+ const dispatchActivity = this.activityRepo.create({
349
+ workspaceId: task.workspaceId,
350
+ taskId: task.id,
351
+ agentRoleId: role.id,
352
+ actorType: 'system',
353
+ activityType: 'agent_assigned',
354
+ title: `Dispatched to ${role.displayName}`,
355
+ description: childTask.title,
356
+ });
357
+ this.emitActivityEvent(dispatchActivity);
358
+ const mention = this.mentionRepo.create({
359
+ workspaceId: task.workspaceId,
360
+ taskId: task.id,
361
+ toAgentRoleId: role.id,
362
+ mentionType: 'request',
363
+ context: `New task: ${task.title}`,
364
+ });
365
+ this.emitMentionEvent(mention);
366
+ const mentionActivity = this.activityRepo.create({
367
+ workspaceId: task.workspaceId,
368
+ taskId: task.id,
369
+ agentRoleId: role.id,
370
+ actorType: 'user',
371
+ activityType: 'mention',
372
+ title: `@${role.displayName} mentioned`,
373
+ description: mention.context,
374
+ metadata: { mentionId: mention.id, mentionType: mention.mentionType },
375
+ });
376
+ this.emitActivityEvent(mentionActivity);
377
+ }
378
+ }
253
379
  /**
254
380
  * Cancel a running or queued task
255
381
  */
@@ -276,6 +402,44 @@ class AgentDaemon extends events_1.EventEmitter {
276
402
  message: 'Task was stopped by user',
277
403
  });
278
404
  }
405
+ /**
406
+ * Handle transient provider errors by scheduling a retry instead of failing.
407
+ * Returns true if a retry was scheduled, false if retries are exhausted.
408
+ */
409
+ handleTransientTaskFailure(taskId, reason, delayMs = this.retryDelayMs) {
410
+ const currentCount = this.retryCounts.get(taskId) ?? 0;
411
+ const nextCount = currentCount + 1;
412
+ if (nextCount > this.maxTaskRetries) {
413
+ return false;
414
+ }
415
+ this.retryCounts.set(taskId, nextCount);
416
+ if (this.pendingRetries.has(taskId)) {
417
+ return true;
418
+ }
419
+ // Mark as queued with a helpful message
420
+ this.taskRepo.update(taskId, {
421
+ status: 'queued',
422
+ error: `Transient provider error. Retry ${nextCount}/${this.maxTaskRetries} in ${Math.ceil(delayMs / 1000)}s.`,
423
+ });
424
+ this.logEvent(taskId, 'log', {
425
+ message: `Transient provider error detected. Scheduling retry ${nextCount}/${this.maxTaskRetries} in ${Math.ceil(delayMs / 1000)}s.`,
426
+ reason,
427
+ });
428
+ // Clear executor and free queue slot
429
+ this.activeTasks.delete(taskId);
430
+ this.queueManager.onTaskFinished(taskId);
431
+ const handle = setTimeout(async () => {
432
+ this.pendingRetries.delete(taskId);
433
+ const task = this.taskRepo.findById(taskId);
434
+ if (!task)
435
+ return;
436
+ if (task.status === 'cancelled' || task.status === 'completed')
437
+ return;
438
+ await this.startTask(task);
439
+ }, delayMs);
440
+ this.pendingRetries.set(taskId, handle);
441
+ return true;
442
+ }
279
443
  /**
280
444
  * Pause a running task
281
445
  */
@@ -677,19 +841,6 @@ class AgentDaemon extends events_1.EventEmitter {
677
841
  return undefined;
678
842
  }
679
843
  }
680
- emitActivityEvent(activity) {
681
- const windows = electron_1.BrowserWindow.getAllWindows();
682
- windows.forEach(window => {
683
- try {
684
- if (!window.isDestroyed() && window.webContents && !window.webContents.isDestroyed()) {
685
- window.webContents.send(types_1.IPC_CHANNELS.ACTIVITY_EVENT, { type: 'created', activity });
686
- }
687
- }
688
- catch (error) {
689
- console.error('[AgentDaemon] Error sending activity IPC:', error);
690
- }
691
- });
692
- }
693
844
  /**
694
845
  * Register an artifact (file created during task execution)
695
846
  * This allows files like screenshots to be sent back to the user