cowork-os 0.3.21 → 0.3.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +372 -10
- package/connectors/README.md +20 -0
- package/connectors/asana-mcp/README.md +24 -0
- package/connectors/asana-mcp/dist/index.js +427 -0
- package/connectors/asana-mcp/package.json +15 -0
- package/connectors/asana-mcp/src/index.ts +553 -0
- package/connectors/asana-mcp/tsconfig.json +13 -0
- package/connectors/hubspot-mcp/README.md +35 -0
- package/connectors/hubspot-mcp/dist/index.js +454 -0
- package/connectors/hubspot-mcp/package.json +15 -0
- package/connectors/hubspot-mcp/src/index.ts +562 -0
- package/connectors/hubspot-mcp/tsconfig.json +13 -0
- package/connectors/jira-mcp/README.md +49 -0
- package/connectors/jira-mcp/dist/index.js +588 -0
- package/connectors/jira-mcp/package.json +15 -0
- package/connectors/jira-mcp/src/index.ts +711 -0
- package/connectors/jira-mcp/tsconfig.json +13 -0
- package/connectors/linear-mcp/README.md +22 -0
- package/connectors/linear-mcp/dist/index.js +402 -0
- package/connectors/linear-mcp/package.json +15 -0
- package/connectors/linear-mcp/src/index.ts +522 -0
- package/connectors/linear-mcp/tsconfig.json +13 -0
- package/connectors/okta-mcp/README.md +24 -0
- package/connectors/okta-mcp/dist/index.js +411 -0
- package/connectors/okta-mcp/package.json +15 -0
- package/connectors/okta-mcp/src/index.ts +520 -0
- package/connectors/okta-mcp/tsconfig.json +13 -0
- package/connectors/salesforce-mcp/README.md +47 -0
- package/connectors/salesforce-mcp/dist/index.js +584 -0
- package/connectors/salesforce-mcp/package.json +15 -0
- package/connectors/salesforce-mcp/src/index.ts +722 -0
- package/connectors/salesforce-mcp/tsconfig.json +13 -0
- package/connectors/servicenow-mcp/README.md +26 -0
- package/connectors/servicenow-mcp/dist/index.js +400 -0
- package/connectors/servicenow-mcp/package.json +15 -0
- package/connectors/servicenow-mcp/src/index.ts +500 -0
- package/connectors/servicenow-mcp/tsconfig.json +13 -0
- package/connectors/templates/mcp-connector/README.md +31 -0
- package/connectors/templates/mcp-connector/package.json +15 -0
- package/connectors/templates/mcp-connector/src/index.ts +330 -0
- package/connectors/templates/mcp-connector/tsconfig.json +13 -0
- package/connectors/zendesk-mcp/README.md +40 -0
- package/connectors/zendesk-mcp/dist/index.js +431 -0
- package/connectors/zendesk-mcp/package.json +15 -0
- package/connectors/zendesk-mcp/src/index.ts +543 -0
- package/connectors/zendesk-mcp/tsconfig.json +13 -0
- package/dist/electron/electron/agent/custom-skill-loader.js +31 -1
- package/dist/electron/electron/agent/daemon.js +189 -13
- package/dist/electron/electron/agent/executor.js +895 -78
- package/dist/electron/electron/agent/llm/anthropic-compatible-provider.js +177 -0
- package/dist/electron/electron/agent/llm/azure-openai-provider.js +328 -0
- package/dist/electron/electron/agent/llm/bedrock-provider.js +49 -9
- package/dist/electron/electron/agent/llm/github-copilot-provider.js +97 -0
- package/dist/electron/electron/agent/llm/groq-provider.js +33 -0
- package/dist/electron/electron/agent/llm/index.js +13 -1
- package/dist/electron/electron/agent/llm/kimi-provider.js +33 -0
- package/dist/electron/electron/agent/llm/openai-compatible-provider.js +116 -0
- package/dist/electron/electron/agent/llm/openai-compatible.js +111 -0
- package/dist/electron/electron/agent/llm/openai-oauth.js +2 -1
- package/dist/electron/electron/agent/llm/openrouter-provider.js +1 -1
- package/dist/electron/electron/agent/llm/provider-factory.js +350 -4
- package/dist/electron/electron/agent/llm/types.js +66 -1
- package/dist/electron/electron/agent/llm/xai-provider.js +33 -0
- package/dist/electron/electron/agent/search/provider-factory.js +38 -2
- package/dist/electron/electron/agent/tools/box-tools.js +231 -0
- package/dist/electron/electron/agent/tools/builtin-settings.js +28 -0
- package/dist/electron/electron/agent/tools/dropbox-tools.js +237 -0
- package/dist/electron/electron/agent/tools/file-tools.js +66 -3
- package/dist/electron/electron/agent/tools/google-drive-tools.js +227 -0
- package/dist/electron/electron/agent/tools/grep-tools.js +90 -10
- package/dist/electron/electron/agent/tools/image-tools.js +11 -1
- package/dist/electron/electron/agent/tools/notion-tools.js +312 -0
- package/dist/electron/electron/agent/tools/onedrive-tools.js +217 -0
- package/dist/electron/electron/agent/tools/registry.js +548 -10
- package/dist/electron/electron/agent/tools/search-tools.js +28 -10
- package/dist/electron/electron/agent/tools/sharepoint-tools.js +243 -0
- package/dist/electron/electron/agent/tools/shell-tools.js +12 -3
- package/dist/electron/electron/agent/tools/x-tools.js +1 -1
- package/dist/electron/electron/agents/agent-dispatch.js +63 -0
- package/dist/electron/electron/database/repositories.js +19 -5
- package/dist/electron/electron/database/schema.js +8 -0
- package/dist/electron/electron/gateway/channels/whatsapp.js +55 -0
- package/dist/electron/electron/gateway/index.js +75 -1
- package/dist/electron/electron/gateway/router.js +209 -154
- package/dist/electron/electron/ipc/canvas-handlers.js +5 -0
- package/dist/electron/electron/ipc/handlers.js +763 -267
- package/dist/electron/electron/main.js +63 -0
- package/dist/electron/electron/mcp/oauth/connector-oauth.js +333 -0
- package/dist/electron/electron/mcp/registry/MCPRegistryManager.js +503 -154
- package/dist/electron/electron/memory/MemoryService.js +2 -1
- package/dist/electron/electron/preload.js +78 -1
- package/dist/electron/electron/settings/appearance-manager.js +18 -1
- package/dist/electron/electron/settings/box-manager.js +54 -0
- package/dist/electron/electron/settings/dropbox-manager.js +54 -0
- package/dist/electron/electron/settings/google-drive-manager.js +54 -0
- package/dist/electron/electron/settings/notion-manager.js +56 -0
- package/dist/electron/electron/settings/onedrive-manager.js +54 -0
- package/dist/electron/electron/settings/sharepoint-manager.js +54 -0
- package/dist/electron/electron/utils/box-api.js +153 -0
- package/dist/electron/electron/utils/dropbox-api.js +144 -0
- package/dist/electron/electron/utils/env-migration.js +19 -0
- package/dist/electron/electron/utils/google-drive-api.js +152 -0
- package/dist/electron/electron/utils/notion-api.js +103 -0
- package/dist/electron/electron/utils/onedrive-api.js +113 -0
- package/dist/electron/electron/utils/sharepoint-api.js +109 -0
- package/dist/electron/electron/utils/validation.js +98 -3
- package/dist/electron/electron/utils/x-cli.js +1 -1
- package/dist/electron/shared/channelMessages.js +284 -3
- package/dist/electron/shared/llm-provider-catalog.js +198 -0
- package/dist/electron/shared/types.js +90 -1
- package/package.json +14 -3
- package/resources/skills/nano-banana-pro.json +4 -4
- package/resources/skills/openai-image-gen.json +3 -3
- package/resources/skills/scripts/gen.py +163 -0
- package/resources/skills/scripts/generate_image.py +91 -0
- package/src/electron/agent/custom-skill-loader.ts +34 -1
- package/src/electron/agent/daemon.ts +210 -14
- package/src/electron/agent/executor.ts +1124 -85
- package/src/electron/agent/llm/anthropic-compatible-provider.ts +214 -0
- package/src/electron/agent/llm/azure-openai-provider.ts +388 -0
- package/src/electron/agent/llm/bedrock-provider.ts +62 -9
- package/src/electron/agent/llm/github-copilot-provider.ts +117 -0
- package/src/electron/agent/llm/groq-provider.ts +39 -0
- package/src/electron/agent/llm/index.ts +6 -0
- package/src/electron/agent/llm/kimi-provider.ts +39 -0
- package/src/electron/agent/llm/openai-compatible-provider.ts +153 -0
- package/src/electron/agent/llm/openai-compatible.ts +133 -0
- package/src/electron/agent/llm/openai-oauth.ts +2 -1
- package/src/electron/agent/llm/openrouter-provider.ts +2 -1
- package/src/electron/agent/llm/provider-factory.ts +459 -6
- package/src/electron/agent/llm/types.ts +95 -1
- package/src/electron/agent/llm/xai-provider.ts +39 -0
- package/src/electron/agent/search/provider-factory.ts +43 -2
- package/src/electron/agent/tools/box-tools.ts +239 -0
- package/src/electron/agent/tools/builtin-settings.ts +36 -0
- package/src/electron/agent/tools/dropbox-tools.ts +237 -0
- package/src/electron/agent/tools/file-tools.ts +66 -3
- package/src/electron/agent/tools/gmail-tools.ts +240 -0
- package/src/electron/agent/tools/google-calendar-tools.ts +258 -0
- package/src/electron/agent/tools/google-drive-tools.ts +228 -0
- package/src/electron/agent/tools/grep-tools.ts +97 -12
- package/src/electron/agent/tools/image-tools.ts +11 -1
- package/src/electron/agent/tools/notion-tools.ts +330 -0
- package/src/electron/agent/tools/onedrive-tools.ts +217 -0
- package/src/electron/agent/tools/registry.ts +794 -10
- package/src/electron/agent/tools/search-tools.ts +29 -11
- package/src/electron/agent/tools/sharepoint-tools.ts +247 -0
- package/src/electron/agent/tools/shell-tools.ts +11 -3
- package/src/electron/agent/tools/x-tools.ts +1 -1
- package/src/electron/agents/agent-dispatch.ts +79 -0
- package/src/electron/database/SecureSettingsRepository.ts +7 -1
- package/src/electron/database/repositories.ts +58 -6
- package/src/electron/database/schema.ts +8 -0
- package/src/electron/gateway/channels/discord.ts +4 -0
- package/src/electron/gateway/channels/google-chat.ts +3 -0
- package/src/electron/gateway/channels/line.ts +3 -0
- package/src/electron/gateway/channels/matrix-client.ts +15 -0
- package/src/electron/gateway/channels/matrix.ts +31 -0
- package/src/electron/gateway/channels/mattermost.ts +3 -0
- package/src/electron/gateway/channels/signal.ts +3 -0
- package/src/electron/gateway/channels/slack.ts +9 -4
- package/src/electron/gateway/channels/teams.ts +4 -0
- package/src/electron/gateway/channels/telegram.ts +2 -0
- package/src/electron/gateway/channels/twitch.ts +2 -0
- package/src/electron/gateway/channels/types.ts +8 -0
- package/src/electron/gateway/channels/whatsapp.ts +66 -0
- package/src/electron/gateway/index.ts +95 -2
- package/src/electron/gateway/router.ts +231 -161
- package/src/electron/gateway/security.ts +21 -9
- package/src/electron/ipc/canvas-handlers.ts +10 -0
- package/src/electron/ipc/handlers.ts +848 -292
- package/src/electron/main.ts +35 -0
- package/src/electron/mcp/oauth/connector-oauth.ts +448 -0
- package/src/electron/mcp/registry/MCPRegistryManager.ts +343 -12
- package/src/electron/memory/MemoryService.ts +7 -1
- package/src/electron/preload.ts +200 -5
- package/src/electron/settings/appearance-manager.ts +20 -2
- package/src/electron/settings/box-manager.ts +58 -0
- package/src/electron/settings/dropbox-manager.ts +58 -0
- package/src/electron/settings/google-workspace-manager.ts +59 -0
- package/src/electron/settings/notion-manager.ts +60 -0
- package/src/electron/settings/onedrive-manager.ts +58 -0
- package/src/electron/settings/sharepoint-manager.ts +58 -0
- package/src/electron/utils/box-api.ts +184 -0
- package/src/electron/utils/dropbox-api.ts +171 -0
- package/src/electron/utils/env-migration.ts +22 -0
- package/src/electron/utils/gmail-api.ts +121 -0
- package/src/electron/utils/google-calendar-api.ts +115 -0
- package/src/electron/utils/google-workspace-api.ts +228 -0
- package/src/electron/utils/google-workspace-auth.ts +109 -0
- package/src/electron/utils/google-workspace-oauth.ts +232 -0
- package/src/electron/utils/notion-api.ts +126 -0
- package/src/electron/utils/onedrive-api.ts +137 -0
- package/src/electron/utils/sharepoint-api.ts +132 -0
- package/src/electron/utils/validation.ts +128 -1
- package/src/electron/utils/x-cli.ts +1 -1
- package/src/renderer/App.tsx +119 -8
- package/src/renderer/components/ActivityFeedItem.tsx +34 -17
- package/src/renderer/components/AgentWorkingStatePanel.tsx +7 -5
- package/src/renderer/components/AppearanceSettings.tsx +37 -2
- package/src/renderer/components/BlueBubblesSettings.tsx +18 -7
- package/src/renderer/components/BoxSettings.tsx +203 -0
- package/src/renderer/components/BrowserView.tsx +101 -0
- package/src/renderer/components/BuiltinToolsSettings.tsx +105 -0
- package/src/renderer/components/CanvasPreview.tsx +68 -1
- package/src/renderer/components/ConnectorEnvModal.tsx +116 -0
- package/src/renderer/components/ConnectorSetupModal.tsx +566 -0
- package/src/renderer/components/ConnectorsSettings.tsx +397 -0
- package/src/renderer/components/ControlPlaneSettings.tsx +2 -0
- package/src/renderer/components/DiscordSettings.tsx +18 -7
- package/src/renderer/components/DropboxSettings.tsx +202 -0
- package/src/renderer/components/EmailSettings.tsx +18 -7
- package/src/renderer/components/FileViewer.tsx +21 -13
- package/src/renderer/components/GoogleChatSettings.tsx +17 -7
- package/src/renderer/components/GoogleWorkspaceSettings.tsx +332 -0
- package/src/renderer/components/ImessageSettings.tsx +22 -11
- package/src/renderer/components/LineIcons.tsx +376 -0
- package/src/renderer/components/LineSettings.tsx +18 -7
- package/src/renderer/components/MCPSettings.tsx +56 -0
- package/src/renderer/components/MainContent.tsx +740 -76
- package/src/renderer/components/MatrixSettings.tsx +18 -7
- package/src/renderer/components/MattermostSettings.tsx +18 -7
- package/src/renderer/components/NodesSettings.tsx +58 -99
- package/src/renderer/components/NotificationPanel.tsx +25 -11
- package/src/renderer/components/NotionSettings.tsx +231 -0
- package/src/renderer/components/Onboarding/Onboarding.tsx +13 -1
- package/src/renderer/components/OnboardingModal.tsx +70 -1
- package/src/renderer/components/OneDriveSettings.tsx +212 -0
- package/src/renderer/components/RightPanel.tsx +141 -28
- package/src/renderer/components/ScheduledTasksSettings.tsx +10 -62
- package/src/renderer/components/SearchSettings.tsx +118 -114
- package/src/renderer/components/Settings.tsx +1425 -651
- package/src/renderer/components/SharePointSettings.tsx +224 -0
- package/src/renderer/components/Sidebar.tsx +94 -19
- package/src/renderer/components/SignalSettings.tsx +18 -7
- package/src/renderer/components/SkillHubBrowser.tsx +144 -185
- package/src/renderer/components/SlackSettings.tsx +18 -7
- package/src/renderer/components/TaskQuickActions.tsx +11 -6
- package/src/renderer/components/TaskTimeline.tsx +58 -26
- package/src/renderer/components/TeamsSettings.tsx +18 -7
- package/src/renderer/components/TelegramSettings.tsx +18 -7
- package/src/renderer/components/ThemeIcon.tsx +16 -0
- package/src/renderer/components/TwitchSettings.tsx +18 -7
- package/src/renderer/components/VoiceSettings.tsx +30 -74
- package/src/renderer/components/WhatsAppSettings.tsx +48 -37
- package/src/renderer/components/WorkingStateHistory.tsx +7 -5
- package/src/renderer/components/WorkspaceSelector.tsx +42 -13
- package/src/renderer/hooks/useOnboardingFlow.ts +21 -0
- package/src/renderer/styles/index.css +2333 -209
- package/src/shared/channelMessages.ts +367 -4
- package/src/shared/llm-provider-catalog.ts +217 -0
- package/src/shared/types.ts +251 -2
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Simple image generator wrapper (OpenAI Images API, stdlib only)."""
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import base64
|
|
6
|
+
import datetime
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
import sys
|
|
10
|
+
import urllib.error
|
|
11
|
+
import urllib.request
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
OPENAI_URL = "https://api.openai.com/v1/images/generations"
|
|
15
|
+
|
|
16
|
+
SIZE_MAP = {
|
|
17
|
+
"1K": "1024x1024",
|
|
18
|
+
"2K": "1536x1024",
|
|
19
|
+
"4K": "1536x1024",
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _require_api_key() -> str:
|
|
24
|
+
api_key = os.environ.get("OPENAI_API_KEY")
|
|
25
|
+
if not api_key:
|
|
26
|
+
print("ERROR: OPENAI_API_KEY is not set", file=sys.stderr)
|
|
27
|
+
sys.exit(2)
|
|
28
|
+
return api_key
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _post_json(payload: dict) -> dict:
|
|
32
|
+
api_key = _require_api_key()
|
|
33
|
+
data = json.dumps(payload).encode("utf-8")
|
|
34
|
+
req = urllib.request.Request(
|
|
35
|
+
OPENAI_URL,
|
|
36
|
+
data=data,
|
|
37
|
+
headers={
|
|
38
|
+
"Authorization": f"Bearer {api_key}",
|
|
39
|
+
"Content-Type": "application/json",
|
|
40
|
+
},
|
|
41
|
+
method="POST",
|
|
42
|
+
)
|
|
43
|
+
try:
|
|
44
|
+
with urllib.request.urlopen(req) as resp:
|
|
45
|
+
return json.load(resp)
|
|
46
|
+
except urllib.error.HTTPError as err:
|
|
47
|
+
body = err.read().decode("utf-8", "ignore")
|
|
48
|
+
print(f"OpenAI API error: {err.code} {err.reason}\n{body}", file=sys.stderr)
|
|
49
|
+
sys.exit(3)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def main() -> None:
|
|
53
|
+
parser = argparse.ArgumentParser(description="Generate a single image")
|
|
54
|
+
parser.add_argument("--prompt", required=True)
|
|
55
|
+
parser.add_argument("--filename", required=True)
|
|
56
|
+
parser.add_argument("--resolution", default="1K")
|
|
57
|
+
parser.add_argument("-i", dest="inputs", action="append", default=[])
|
|
58
|
+
args = parser.parse_args()
|
|
59
|
+
|
|
60
|
+
if args.inputs:
|
|
61
|
+
print("WARNING: edit/composition inputs are not supported in this build; ignoring -i", file=sys.stderr)
|
|
62
|
+
|
|
63
|
+
size = SIZE_MAP.get(args.resolution.upper(), "1024x1024")
|
|
64
|
+
|
|
65
|
+
payload = {
|
|
66
|
+
"model": "gpt-image-1",
|
|
67
|
+
"prompt": args.prompt,
|
|
68
|
+
"n": 1,
|
|
69
|
+
"size": size,
|
|
70
|
+
"response_format": "b64_json",
|
|
71
|
+
"quality": "high",
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
result = _post_json(payload)
|
|
75
|
+
data = result.get("data", [])
|
|
76
|
+
if not data or not data[0].get("b64_json"):
|
|
77
|
+
print("ERROR: No image data returned", file=sys.stderr)
|
|
78
|
+
sys.exit(4)
|
|
79
|
+
|
|
80
|
+
out_path = Path(args.filename).expanduser()
|
|
81
|
+
out_path.parent.mkdir(parents=True, exist_ok=True)
|
|
82
|
+
|
|
83
|
+
with open(out_path, "wb") as f:
|
|
84
|
+
f.write(base64.b64decode(data[0]["b64_json"]))
|
|
85
|
+
|
|
86
|
+
print(f"MEDIA: {out_path}")
|
|
87
|
+
print(f"Image saved as: {out_path}")
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
if __name__ == "__main__":
|
|
91
|
+
main()
|
|
@@ -390,7 +390,7 @@ export class CustomSkillLoader {
|
|
|
390
390
|
skill: CustomSkill,
|
|
391
391
|
parameterValues: Record<string, string | number | boolean>
|
|
392
392
|
): string {
|
|
393
|
-
let prompt = skill.prompt;
|
|
393
|
+
let prompt = this.expandBaseDir(skill.prompt, skill);
|
|
394
394
|
|
|
395
395
|
// Replace {{param}} placeholders with values
|
|
396
396
|
if (skill.parameters) {
|
|
@@ -407,6 +407,39 @@ export class CustomSkillLoader {
|
|
|
407
407
|
return prompt.trim();
|
|
408
408
|
}
|
|
409
409
|
|
|
410
|
+
/**
|
|
411
|
+
* Expand {baseDir} placeholders to the resolved skill base directory.
|
|
412
|
+
*/
|
|
413
|
+
expandBaseDir(prompt: string, skill: CustomSkill): string {
|
|
414
|
+
if (!prompt.includes('{baseDir}')) {
|
|
415
|
+
return prompt;
|
|
416
|
+
}
|
|
417
|
+
const baseDir = this.resolveBaseDir(skill);
|
|
418
|
+
return prompt.replace(/\{baseDir\}/g, baseDir);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
private resolveBaseDir(skill: CustomSkill): string {
|
|
422
|
+
const fileDir = skill.filePath ? path.dirname(skill.filePath) : this.bundledSkillsDir;
|
|
423
|
+
const candidates = [
|
|
424
|
+
fileDir,
|
|
425
|
+
this.bundledSkillsDir,
|
|
426
|
+
this.managedSkillsDir,
|
|
427
|
+
this.workspaceSkillsDir || '',
|
|
428
|
+
].filter(Boolean) as string[];
|
|
429
|
+
|
|
430
|
+
for (const dir of candidates) {
|
|
431
|
+
try {
|
|
432
|
+
if (fs.existsSync(path.join(dir, 'scripts'))) {
|
|
433
|
+
return dir;
|
|
434
|
+
}
|
|
435
|
+
} catch {
|
|
436
|
+
// ignore and continue
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
return fileDir;
|
|
441
|
+
}
|
|
442
|
+
|
|
410
443
|
/**
|
|
411
444
|
* Get eligible skills (those that meet all requirements)
|
|
412
445
|
*/
|
|
@@ -12,7 +12,10 @@ import {
|
|
|
12
12
|
MemoryType,
|
|
13
13
|
} from '../database/repositories';
|
|
14
14
|
import { ActivityRepository } from '../activity/ActivityRepository';
|
|
15
|
-
import {
|
|
15
|
+
import { AgentRoleRepository } from '../agents/AgentRoleRepository';
|
|
16
|
+
import { MentionRepository } from '../agents/MentionRepository';
|
|
17
|
+
import { buildAgentDispatchPrompt } from '../agents/agent-dispatch';
|
|
18
|
+
import { Task, TaskStatus, IPC_CHANNELS, QueueSettings, QueueStatus, Workspace, WorkspacePermissions, AgentConfig, AgentType, ActivityActorType, ActivityType, CreateActivityRequest, Plan, BoardColumn, Activity, AgentMention } from '../../shared/types';
|
|
16
19
|
import { TaskExecutor } from './executor';
|
|
17
20
|
import { TaskQueueManager } from './queue-manager';
|
|
18
21
|
import { approvalIdempotency, taskIdempotency, IdempotencyManager } from '../security/concurrency';
|
|
@@ -43,12 +46,18 @@ export class AgentDaemon extends EventEmitter {
|
|
|
43
46
|
private approvalRepo: ApprovalRepository;
|
|
44
47
|
private artifactRepo: ArtifactRepository;
|
|
45
48
|
private activityRepo: ActivityRepository;
|
|
49
|
+
private agentRoleRepo: AgentRoleRepository;
|
|
50
|
+
private mentionRepo: MentionRepository;
|
|
46
51
|
private activeTasks: Map<string, CachedExecutor> = new Map();
|
|
47
52
|
private pendingApprovals: Map<string, { taskId: string; resolve: (value: boolean) => void; reject: (reason?: unknown) => void; resolved: boolean; timeoutHandle: ReturnType<typeof setTimeout> }> = new Map();
|
|
48
53
|
private cleanupIntervalHandle?: ReturnType<typeof setInterval>;
|
|
49
54
|
private queueManager: TaskQueueManager;
|
|
50
55
|
// Activity throttle: Map<taskId:eventType, lastTimestamp>
|
|
51
56
|
private activityThrottle: Map<string, number> = new Map();
|
|
57
|
+
private pendingRetries: Map<string, ReturnType<typeof setTimeout>> = new Map();
|
|
58
|
+
private retryCounts: Map<string, number> = new Map();
|
|
59
|
+
private readonly maxTaskRetries = 2;
|
|
60
|
+
private readonly retryDelayMs = 30 * 1000;
|
|
52
61
|
|
|
53
62
|
constructor(private dbManager: DatabaseManager) {
|
|
54
63
|
super();
|
|
@@ -59,6 +68,8 @@ export class AgentDaemon extends EventEmitter {
|
|
|
59
68
|
this.approvalRepo = new ApprovalRepository(db);
|
|
60
69
|
this.artifactRepo = new ArtifactRepository(db);
|
|
61
70
|
this.activityRepo = new ActivityRepository(db);
|
|
71
|
+
this.agentRoleRepo = new AgentRoleRepository(db);
|
|
72
|
+
this.mentionRepo = new MentionRepository(db);
|
|
62
73
|
|
|
63
74
|
// Initialize queue manager with callbacks
|
|
64
75
|
this.queueManager = new TaskQueueManager({
|
|
@@ -177,6 +188,7 @@ export class AgentDaemon extends EventEmitter {
|
|
|
177
188
|
error: error.message || 'Failed to initialize task executor',
|
|
178
189
|
completedAt: Date.now(),
|
|
179
190
|
});
|
|
191
|
+
this.clearRetryState(task.id);
|
|
180
192
|
this.logEvent(task.id, 'error', { error: error.message });
|
|
181
193
|
// Notify queue manager so it can start next task
|
|
182
194
|
this.queueManager.onTaskFinished(task.id);
|
|
@@ -202,6 +214,7 @@ export class AgentDaemon extends EventEmitter {
|
|
|
202
214
|
error: error.message,
|
|
203
215
|
completedAt: Date.now(),
|
|
204
216
|
});
|
|
217
|
+
this.clearRetryState(task.id);
|
|
205
218
|
this.logEvent(task.id, 'error', { error: error.message });
|
|
206
219
|
this.activeTasks.delete(task.id);
|
|
207
220
|
// Notify queue manager so it can start next task
|
|
@@ -282,6 +295,136 @@ export class AgentDaemon extends EventEmitter {
|
|
|
282
295
|
return task;
|
|
283
296
|
}
|
|
284
297
|
|
|
298
|
+
private buildPlanSummary(plan?: Plan): string | undefined {
|
|
299
|
+
if (!plan) return undefined;
|
|
300
|
+
const lines: string[] = [];
|
|
301
|
+
if (plan.description) {
|
|
302
|
+
lines.push(`Plan: ${plan.description}`);
|
|
303
|
+
}
|
|
304
|
+
if (plan.steps && plan.steps.length > 0) {
|
|
305
|
+
lines.push('Steps:');
|
|
306
|
+
const stepLines = plan.steps
|
|
307
|
+
.slice(0, 7)
|
|
308
|
+
.map((step) => `- ${step.description}`);
|
|
309
|
+
lines.push(...stepLines);
|
|
310
|
+
if (plan.steps.length > 7) {
|
|
311
|
+
lines.push(`- …and ${plan.steps.length - 7} more steps`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return lines.length > 0 ? lines.join('\n') : undefined;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
private emitActivityEvent(activity: Activity): void {
|
|
318
|
+
const windows = BrowserWindow.getAllWindows();
|
|
319
|
+
windows.forEach(window => {
|
|
320
|
+
try {
|
|
321
|
+
if (!window.isDestroyed() && window.webContents && !window.webContents.isDestroyed()) {
|
|
322
|
+
window.webContents.send(IPC_CHANNELS.ACTIVITY_EVENT, { type: 'created', activity });
|
|
323
|
+
}
|
|
324
|
+
} catch (error) {
|
|
325
|
+
console.error('[AgentDaemon] Error sending activity IPC:', error);
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
private emitMentionEvent(mention: AgentMention): void {
|
|
331
|
+
const windows = BrowserWindow.getAllWindows();
|
|
332
|
+
windows.forEach(window => {
|
|
333
|
+
try {
|
|
334
|
+
if (!window.isDestroyed() && window.webContents && !window.webContents.isDestroyed()) {
|
|
335
|
+
window.webContents.send(IPC_CHANNELS.MENTION_EVENT, { type: 'created', mention });
|
|
336
|
+
}
|
|
337
|
+
} catch (error) {
|
|
338
|
+
console.error('[AgentDaemon] Error sending mention IPC:', error);
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Dispatch mentioned agent roles after the main plan is created.
|
|
345
|
+
* This avoids starting sub-agents before the task is clearly defined.
|
|
346
|
+
*/
|
|
347
|
+
async dispatchMentionedAgents(taskId: string, plan?: Plan): Promise<void> {
|
|
348
|
+
const task = this.taskRepo.findById(taskId);
|
|
349
|
+
if (!task || task.parentTaskId) return;
|
|
350
|
+
|
|
351
|
+
const mentionedRoleIds = (task.mentionedAgentRoleIds || []).filter(Boolean);
|
|
352
|
+
if (mentionedRoleIds.length === 0) return;
|
|
353
|
+
|
|
354
|
+
const activeRoles = this.agentRoleRepo.findAll(false).filter(role => role.isActive);
|
|
355
|
+
const mentionedRoles = activeRoles.filter(role => mentionedRoleIds.includes(role.id));
|
|
356
|
+
if (mentionedRoles.length === 0) return;
|
|
357
|
+
|
|
358
|
+
const existingChildren = this.taskRepo.findByParent(taskId);
|
|
359
|
+
const assignedRoleIds = new Set(
|
|
360
|
+
existingChildren
|
|
361
|
+
.map(child => child.assignedAgentRoleId)
|
|
362
|
+
.filter((id): id is string => typeof id === 'string' && id.length > 0)
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
const rolesToDispatch = mentionedRoles.filter(role => !assignedRoleIds.has(role.id));
|
|
366
|
+
if (rolesToDispatch.length === 0) return;
|
|
367
|
+
|
|
368
|
+
const planSummary = this.buildPlanSummary(plan);
|
|
369
|
+
|
|
370
|
+
for (const role of rolesToDispatch) {
|
|
371
|
+
const childPrompt = buildAgentDispatchPrompt(
|
|
372
|
+
role,
|
|
373
|
+
{ title: task.title, prompt: task.prompt },
|
|
374
|
+
planSummary ? { planSummary } : undefined
|
|
375
|
+
);
|
|
376
|
+
const childTask = await this.createChildTask({
|
|
377
|
+
title: `@${role.displayName}: ${task.title}`,
|
|
378
|
+
prompt: childPrompt,
|
|
379
|
+
workspaceId: task.workspaceId,
|
|
380
|
+
parentTaskId: task.id,
|
|
381
|
+
agentType: 'sub',
|
|
382
|
+
agentConfig: {
|
|
383
|
+
...(role.modelKey ? { modelKey: role.modelKey } : {}),
|
|
384
|
+
...(role.personalityId ? { personalityId: role.personalityId } : {}),
|
|
385
|
+
retainMemory: false,
|
|
386
|
+
},
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
this.taskRepo.update(childTask.id, {
|
|
390
|
+
assignedAgentRoleId: role.id,
|
|
391
|
+
boardColumn: 'todo' as BoardColumn,
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
const dispatchActivity = this.activityRepo.create({
|
|
395
|
+
workspaceId: task.workspaceId,
|
|
396
|
+
taskId: task.id,
|
|
397
|
+
agentRoleId: role.id,
|
|
398
|
+
actorType: 'system',
|
|
399
|
+
activityType: 'agent_assigned',
|
|
400
|
+
title: `Dispatched to ${role.displayName}`,
|
|
401
|
+
description: childTask.title,
|
|
402
|
+
});
|
|
403
|
+
this.emitActivityEvent(dispatchActivity);
|
|
404
|
+
|
|
405
|
+
const mention = this.mentionRepo.create({
|
|
406
|
+
workspaceId: task.workspaceId,
|
|
407
|
+
taskId: task.id,
|
|
408
|
+
toAgentRoleId: role.id,
|
|
409
|
+
mentionType: 'request',
|
|
410
|
+
context: `New task: ${task.title}`,
|
|
411
|
+
});
|
|
412
|
+
this.emitMentionEvent(mention);
|
|
413
|
+
|
|
414
|
+
const mentionActivity = this.activityRepo.create({
|
|
415
|
+
workspaceId: task.workspaceId,
|
|
416
|
+
taskId: task.id,
|
|
417
|
+
agentRoleId: role.id,
|
|
418
|
+
actorType: 'user',
|
|
419
|
+
activityType: 'mention',
|
|
420
|
+
title: `@${role.displayName} mentioned`,
|
|
421
|
+
description: mention.context,
|
|
422
|
+
metadata: { mentionId: mention.id, mentionType: mention.mentionType },
|
|
423
|
+
});
|
|
424
|
+
this.emitActivityEvent(mentionActivity);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
285
428
|
/**
|
|
286
429
|
* Cancel a running or queued task
|
|
287
430
|
*/
|
|
@@ -289,6 +432,7 @@ export class AgentDaemon extends EventEmitter {
|
|
|
289
432
|
// Check if task is queued (not yet started)
|
|
290
433
|
if (this.queueManager.cancelQueuedTask(taskId)) {
|
|
291
434
|
this.taskRepo.update(taskId, { status: 'cancelled', completedAt: Date.now() });
|
|
435
|
+
this.clearRetryState(taskId);
|
|
292
436
|
this.logEvent(taskId, 'task_cancelled', {
|
|
293
437
|
message: 'Task removed from queue',
|
|
294
438
|
});
|
|
@@ -307,11 +451,62 @@ export class AgentDaemon extends EventEmitter {
|
|
|
307
451
|
this.queueManager.onTaskFinished(taskId);
|
|
308
452
|
|
|
309
453
|
// Always emit cancelled event so UI updates
|
|
454
|
+
this.clearRetryState(taskId);
|
|
310
455
|
this.logEvent(taskId, 'task_cancelled', {
|
|
311
456
|
message: 'Task was stopped by user',
|
|
312
457
|
});
|
|
313
458
|
}
|
|
314
459
|
|
|
460
|
+
/**
|
|
461
|
+
* Handle transient provider errors by scheduling a retry instead of failing.
|
|
462
|
+
* Returns true if a retry was scheduled, false if retries are exhausted.
|
|
463
|
+
*/
|
|
464
|
+
handleTransientTaskFailure(taskId: string, reason: string, delayMs: number = this.retryDelayMs): boolean {
|
|
465
|
+
const currentCount = this.retryCounts.get(taskId) ?? 0;
|
|
466
|
+
const nextCount = currentCount + 1;
|
|
467
|
+
if (nextCount > this.maxTaskRetries) {
|
|
468
|
+
return false;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
this.retryCounts.set(taskId, nextCount);
|
|
472
|
+
|
|
473
|
+
if (this.pendingRetries.has(taskId)) {
|
|
474
|
+
return true;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// Mark as queued with a helpful message
|
|
478
|
+
this.taskRepo.update(taskId, {
|
|
479
|
+
status: 'queued',
|
|
480
|
+
error: `Transient provider error. Retry ${nextCount}/${this.maxTaskRetries} in ${Math.ceil(delayMs / 1000)}s.`,
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
this.logEvent(taskId, 'log', {
|
|
484
|
+
message: `Transient provider error detected. Scheduling retry ${nextCount}/${this.maxTaskRetries} in ${Math.ceil(delayMs / 1000)}s.`,
|
|
485
|
+
reason,
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
// Clear executor and free queue slot
|
|
489
|
+
this.activeTasks.delete(taskId);
|
|
490
|
+
this.queueManager.onTaskFinished(taskId);
|
|
491
|
+
|
|
492
|
+
const handle = setTimeout(async () => {
|
|
493
|
+
this.pendingRetries.delete(taskId);
|
|
494
|
+
const task = this.taskRepo.findById(taskId);
|
|
495
|
+
if (!task) {
|
|
496
|
+
this.retryCounts.delete(taskId);
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
if (task.status !== 'queued') return;
|
|
500
|
+
if (this.activeTasks.has(taskId) || this.queueManager.isRunning(taskId) || this.queueManager.isQueued(taskId)) {
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
await this.startTask(task);
|
|
504
|
+
}, delayMs);
|
|
505
|
+
|
|
506
|
+
this.pendingRetries.set(taskId, handle);
|
|
507
|
+
return true;
|
|
508
|
+
}
|
|
509
|
+
|
|
315
510
|
/**
|
|
316
511
|
* Pause a running task
|
|
317
512
|
*/
|
|
@@ -741,19 +936,6 @@ export class AgentDaemon extends EventEmitter {
|
|
|
741
936
|
}
|
|
742
937
|
}
|
|
743
938
|
|
|
744
|
-
private emitActivityEvent(activity: any): void {
|
|
745
|
-
const windows = BrowserWindow.getAllWindows();
|
|
746
|
-
windows.forEach(window => {
|
|
747
|
-
try {
|
|
748
|
-
if (!window.isDestroyed() && window.webContents && !window.webContents.isDestroyed()) {
|
|
749
|
-
window.webContents.send(IPC_CHANNELS.ACTIVITY_EVENT, { type: 'created', activity });
|
|
750
|
-
}
|
|
751
|
-
} catch (error) {
|
|
752
|
-
console.error('[AgentDaemon] Error sending activity IPC:', error);
|
|
753
|
-
}
|
|
754
|
-
});
|
|
755
|
-
}
|
|
756
|
-
|
|
757
939
|
/**
|
|
758
940
|
* Register an artifact (file created during task execution)
|
|
759
941
|
* This allows files like screenshots to be sent back to the user
|
|
@@ -820,6 +1002,9 @@ export class AgentDaemon extends EventEmitter {
|
|
|
820
1002
|
*/
|
|
821
1003
|
updateTaskStatus(taskId: string, status: Task['status']): void {
|
|
822
1004
|
this.taskRepo.update(taskId, { status });
|
|
1005
|
+
if (status === 'completed' || status === 'failed' || status === 'cancelled') {
|
|
1006
|
+
this.clearRetryState(taskId);
|
|
1007
|
+
}
|
|
823
1008
|
}
|
|
824
1009
|
|
|
825
1010
|
/**
|
|
@@ -871,6 +1056,15 @@ export class AgentDaemon extends EventEmitter {
|
|
|
871
1056
|
this.taskRepo.update(taskId, updates);
|
|
872
1057
|
}
|
|
873
1058
|
|
|
1059
|
+
private clearRetryState(taskId: string): void {
|
|
1060
|
+
const pending = this.pendingRetries.get(taskId);
|
|
1061
|
+
if (pending) {
|
|
1062
|
+
clearTimeout(pending);
|
|
1063
|
+
this.pendingRetries.delete(taskId);
|
|
1064
|
+
}
|
|
1065
|
+
this.retryCounts.delete(taskId);
|
|
1066
|
+
}
|
|
1067
|
+
|
|
874
1068
|
/**
|
|
875
1069
|
* Mark task as completed
|
|
876
1070
|
* Note: We keep the executor in memory for follow-up messages (with TTL-based cleanup)
|
|
@@ -880,6 +1074,7 @@ export class AgentDaemon extends EventEmitter {
|
|
|
880
1074
|
status: 'completed',
|
|
881
1075
|
completedAt: Date.now(),
|
|
882
1076
|
});
|
|
1077
|
+
this.clearRetryState(taskId);
|
|
883
1078
|
// Mark executor as completed for TTL-based cleanup
|
|
884
1079
|
const cached = this.activeTasks.get(taskId);
|
|
885
1080
|
if (cached) {
|
|
@@ -1014,6 +1209,7 @@ export class AgentDaemon extends EventEmitter {
|
|
|
1014
1209
|
status: 'failed',
|
|
1015
1210
|
error: 'Task timed out - exceeded maximum allowed execution time',
|
|
1016
1211
|
});
|
|
1212
|
+
this.clearRetryState(taskId);
|
|
1017
1213
|
|
|
1018
1214
|
// Emit timeout event
|
|
1019
1215
|
this.logEvent(taskId, 'step_timeout', {
|