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
|
@@ -3,6 +3,7 @@ import * as path from 'path';
|
|
|
3
3
|
import * as fs from 'fs/promises';
|
|
4
4
|
import * as fsSync from 'fs';
|
|
5
5
|
import mammoth from 'mammoth';
|
|
6
|
+
import mime from 'mime-types';
|
|
6
7
|
|
|
7
8
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
8
9
|
const pdfParseModule = require('pdf-parse');
|
|
@@ -28,10 +29,23 @@ import { MentionRepository } from '../agents/MentionRepository';
|
|
|
28
29
|
import { TaskLabelRepository } from '../database/TaskLabelRepository';
|
|
29
30
|
import { WorkingStateRepository } from '../agents/WorkingStateRepository';
|
|
30
31
|
import { ContextPolicyManager } from '../gateway/context-policy';
|
|
31
|
-
import { IPC_CHANNELS, LLMSettingsData, AddChannelRequest, UpdateChannelRequest, SecurityMode, UpdateInfo, TEMP_WORKSPACE_ID, TEMP_WORKSPACE_NAME, Workspace, AgentRole, Task, BoardColumn, XSettingsData } from '../../shared/types';
|
|
32
|
+
import { IPC_CHANNELS, LLMSettingsData, AddChannelRequest, UpdateChannelRequest, SecurityMode, UpdateInfo, TEMP_WORKSPACE_ID, TEMP_WORKSPACE_NAME, Workspace, AgentRole, Task, BoardColumn, XSettingsData, NotionSettingsData, BoxSettingsData, OneDriveSettingsData, GoogleWorkspaceSettingsData, DropboxSettingsData, SharePointSettingsData } from '../../shared/types';
|
|
33
|
+
import { CUSTOM_PROVIDER_MAP, CUSTOM_PROVIDER_IDS } from '../../shared/llm-provider-catalog';
|
|
32
34
|
import * as os from 'os';
|
|
33
35
|
import { AgentDaemon } from '../agent/daemon';
|
|
34
|
-
import {
|
|
36
|
+
import {
|
|
37
|
+
LLMProviderFactory,
|
|
38
|
+
LLMProviderConfig,
|
|
39
|
+
ModelKey,
|
|
40
|
+
MODELS,
|
|
41
|
+
GEMINI_MODELS,
|
|
42
|
+
OPENROUTER_MODELS,
|
|
43
|
+
OLLAMA_MODELS,
|
|
44
|
+
GROQ_MODELS,
|
|
45
|
+
XAI_MODELS,
|
|
46
|
+
KIMI_MODELS,
|
|
47
|
+
OpenAIOAuth,
|
|
48
|
+
} from '../agent/llm';
|
|
35
49
|
import { SearchProviderFactory, SearchSettings, SearchProviderType } from '../agent/search';
|
|
36
50
|
import { ChannelGateway } from '../gateway';
|
|
37
51
|
import { updateManager } from '../updater';
|
|
@@ -42,10 +56,18 @@ import {
|
|
|
42
56
|
TaskCreateSchema,
|
|
43
57
|
TaskRenameSchema,
|
|
44
58
|
TaskMessageSchema,
|
|
59
|
+
FileImportSchema,
|
|
60
|
+
FileImportDataSchema,
|
|
45
61
|
ApprovalResponseSchema,
|
|
46
62
|
LLMSettingsSchema,
|
|
47
63
|
SearchSettingsSchema,
|
|
48
64
|
XSettingsSchema,
|
|
65
|
+
NotionSettingsSchema,
|
|
66
|
+
BoxSettingsSchema,
|
|
67
|
+
OneDriveSettingsSchema,
|
|
68
|
+
GoogleWorkspaceSettingsSchema,
|
|
69
|
+
DropboxSettingsSchema,
|
|
70
|
+
SharePointSettingsSchema,
|
|
49
71
|
AddChannelSchema,
|
|
50
72
|
UpdateChannelSchema,
|
|
51
73
|
GrantAccessSchema,
|
|
@@ -54,10 +76,25 @@ import {
|
|
|
54
76
|
GuardrailSettingsSchema,
|
|
55
77
|
UUIDSchema,
|
|
56
78
|
StringIdSchema,
|
|
79
|
+
MCPConnectorOAuthSchema,
|
|
57
80
|
} from '../utils/validation';
|
|
58
81
|
import { GuardrailManager } from '../guardrails/guardrail-manager';
|
|
59
82
|
import { AppearanceManager } from '../settings/appearance-manager';
|
|
60
83
|
import { PersonalityManager } from '../settings/personality-manager';
|
|
84
|
+
import { NotionSettingsManager } from '../settings/notion-manager';
|
|
85
|
+
import { testNotionConnection } from '../utils/notion-api';
|
|
86
|
+
import { BoxSettingsManager } from '../settings/box-manager';
|
|
87
|
+
import { OneDriveSettingsManager } from '../settings/onedrive-manager';
|
|
88
|
+
import { GoogleWorkspaceSettingsManager } from '../settings/google-workspace-manager';
|
|
89
|
+
import { DropboxSettingsManager } from '../settings/dropbox-manager';
|
|
90
|
+
import { SharePointSettingsManager } from '../settings/sharepoint-manager';
|
|
91
|
+
import { testBoxConnection } from '../utils/box-api';
|
|
92
|
+
import { testOneDriveConnection } from '../utils/onedrive-api';
|
|
93
|
+
import { testGoogleWorkspaceConnection } from '../utils/google-workspace-api';
|
|
94
|
+
import { testDropboxConnection } from '../utils/dropbox-api';
|
|
95
|
+
import { testSharePointConnection } from '../utils/sharepoint-api';
|
|
96
|
+
import { startConnectorOAuth } from '../mcp/oauth/connector-oauth';
|
|
97
|
+
import { startGoogleWorkspaceOAuth } from '../utils/google-workspace-oauth';
|
|
61
98
|
|
|
62
99
|
const normalizeMentionToken = (value: string): string =>
|
|
63
100
|
value.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
@@ -124,7 +161,9 @@ const scoreAgentForTask = (role: AgentRole, text: string) => {
|
|
|
124
161
|
return score;
|
|
125
162
|
};
|
|
126
163
|
|
|
127
|
-
const
|
|
164
|
+
const MAX_AUTO_AGENTS = 4;
|
|
165
|
+
|
|
166
|
+
const selectBestAgentsForTask = (text: string, roles: AgentRole[], maxAgents = MAX_AUTO_AGENTS) => {
|
|
128
167
|
if (roles.length === 0) return roles;
|
|
129
168
|
const scored = roles
|
|
130
169
|
.map((role) => ({ role, score: scoreAgentForTask(role, text) }))
|
|
@@ -139,19 +178,19 @@ const selectBestAgentsForTask = (text: string, roles: AgentRole[]) => {
|
|
|
139
178
|
const threshold = Math.max(1, maxScore - 2);
|
|
140
179
|
const selected = withScore
|
|
141
180
|
.filter((entry) => entry.score >= threshold)
|
|
142
|
-
.slice(0,
|
|
181
|
+
.slice(0, maxAgents)
|
|
143
182
|
.map((entry) => entry.role);
|
|
144
|
-
return selected.length > 0 ? selected : withScore.slice(0,
|
|
183
|
+
return selected.length > 0 ? selected : withScore.slice(0, maxAgents).map((entry) => entry.role);
|
|
145
184
|
}
|
|
146
185
|
|
|
147
186
|
const leads = roles
|
|
148
187
|
.filter((role) => role.autonomyLevel === 'lead')
|
|
149
188
|
.sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0));
|
|
150
189
|
if (leads.length > 0) {
|
|
151
|
-
return leads.slice(0,
|
|
190
|
+
return leads.slice(0, maxAgents);
|
|
152
191
|
}
|
|
153
192
|
|
|
154
|
-
return roles.slice(0, Math.min(
|
|
193
|
+
return roles.slice(0, Math.min(maxAgents, roles.length));
|
|
155
194
|
};
|
|
156
195
|
|
|
157
196
|
const extractMentionedRoles = (
|
|
@@ -159,10 +198,9 @@ const extractMentionedRoles = (
|
|
|
159
198
|
roles: AgentRole[]
|
|
160
199
|
) => {
|
|
161
200
|
const normalizedText = text.toLowerCase();
|
|
162
|
-
const useSmartSelection = /\B@everybody\b/.test(normalizedText)
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}
|
|
201
|
+
const useSmartSelection = /\B@everybody\b/.test(normalizedText) ||
|
|
202
|
+
/\B@all\b/.test(normalizedText) ||
|
|
203
|
+
/\B@everyone\b/.test(normalizedText);
|
|
166
204
|
|
|
167
205
|
const index = buildAgentMentionIndex(roles);
|
|
168
206
|
const matches = new Map<string, AgentRole>();
|
|
@@ -180,11 +218,15 @@ const extractMentionedRoles = (
|
|
|
180
218
|
|
|
181
219
|
if (matches.size > 0) {
|
|
182
220
|
if (useSmartSelection) {
|
|
183
|
-
const selected = selectBestAgentsForTask(text, roles);
|
|
184
221
|
const merged = new Map<string, AgentRole>();
|
|
185
|
-
selected.forEach((role) => merged.set(role.id, role));
|
|
186
222
|
matches.forEach((role) => merged.set(role.id, role));
|
|
187
|
-
|
|
223
|
+
const selected = selectBestAgentsForTask(text, roles, MAX_AUTO_AGENTS);
|
|
224
|
+
selected.forEach((role) => {
|
|
225
|
+
if (merged.size < MAX_AUTO_AGENTS) {
|
|
226
|
+
merged.set(role.id, role);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
return Array.from(merged.values()).slice(0, MAX_AUTO_AGENTS);
|
|
188
230
|
}
|
|
189
231
|
return Array.from(matches.values());
|
|
190
232
|
}
|
|
@@ -200,73 +242,12 @@ const extractMentionedRoles = (
|
|
|
200
242
|
});
|
|
201
243
|
|
|
202
244
|
if (useSmartSelection) {
|
|
203
|
-
return selectBestAgentsForTask(text, roles);
|
|
245
|
+
return selectBestAgentsForTask(text, roles, MAX_AUTO_AGENTS);
|
|
204
246
|
}
|
|
205
247
|
|
|
206
248
|
return Array.from(matches.values());
|
|
207
249
|
};
|
|
208
250
|
|
|
209
|
-
const buildSoulSummary = (soul?: string): string | null => {
|
|
210
|
-
if (!soul) return null;
|
|
211
|
-
try {
|
|
212
|
-
const parsed = JSON.parse(soul) as Record<string, unknown>;
|
|
213
|
-
const parts: string[] = [];
|
|
214
|
-
if (typeof parsed.name === 'string') parts.push(`Name: ${parsed.name}`);
|
|
215
|
-
if (typeof parsed.role === 'string') parts.push(`Role: ${parsed.role}`);
|
|
216
|
-
if (typeof parsed.personality === 'string') parts.push(`Personality: ${parsed.personality}`);
|
|
217
|
-
if (typeof parsed.communicationStyle === 'string') parts.push(`Style: ${parsed.communicationStyle}`);
|
|
218
|
-
if (Array.isArray(parsed.focusAreas)) parts.push(`Focus: ${parsed.focusAreas.join(', ')}`);
|
|
219
|
-
if (Array.isArray(parsed.strengths)) parts.push(`Strengths: ${parsed.strengths.join(', ')}`);
|
|
220
|
-
if (parts.length === 0) {
|
|
221
|
-
return null;
|
|
222
|
-
}
|
|
223
|
-
return parts.join('\n');
|
|
224
|
-
} catch {
|
|
225
|
-
return soul;
|
|
226
|
-
}
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
const buildAgentDispatchPrompt = (
|
|
230
|
-
role: {
|
|
231
|
-
displayName: string;
|
|
232
|
-
description?: string | null;
|
|
233
|
-
capabilities?: string[];
|
|
234
|
-
systemPrompt?: string | null;
|
|
235
|
-
soul?: string | null;
|
|
236
|
-
},
|
|
237
|
-
parentTask: { title: string; prompt: string }
|
|
238
|
-
) => {
|
|
239
|
-
const lines: string[] = [
|
|
240
|
-
`You are ${role.displayName}${role.description ? ` — ${role.description}` : ''}.`,
|
|
241
|
-
];
|
|
242
|
-
|
|
243
|
-
if (role.capabilities && role.capabilities.length > 0) {
|
|
244
|
-
lines.push(`Capabilities: ${role.capabilities.join(', ')}`);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
if (role.systemPrompt) {
|
|
248
|
-
lines.push('System guidance:');
|
|
249
|
-
lines.push(role.systemPrompt);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
const soulSummary = buildSoulSummary(role.soul || undefined);
|
|
253
|
-
if (soulSummary) {
|
|
254
|
-
lines.push('Role notes:');
|
|
255
|
-
lines.push(soulSummary);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
lines.push('');
|
|
259
|
-
lines.push(`Parent task: ${parentTask.title}`);
|
|
260
|
-
lines.push('Request:');
|
|
261
|
-
lines.push(parentTask.prompt);
|
|
262
|
-
lines.push('');
|
|
263
|
-
lines.push('Deliverables:');
|
|
264
|
-
lines.push('- Provide a concise summary of your findings.');
|
|
265
|
-
lines.push('- Call out risks or open questions.');
|
|
266
|
-
lines.push('- Recommend next steps.');
|
|
267
|
-
|
|
268
|
-
return lines.join('\n');
|
|
269
|
-
};
|
|
270
251
|
import { XSettingsManager } from '../settings/x-manager';
|
|
271
252
|
import { testXConnection, checkBirdInstalled } from '../utils/x-cli';
|
|
272
253
|
import { getCustomSkillLoader } from '../agent/custom-skill-loader';
|
|
@@ -303,6 +284,8 @@ import { getVoiceService } from '../voice/VoiceService';
|
|
|
303
284
|
|
|
304
285
|
// Global notification service instance
|
|
305
286
|
let notificationService: NotificationService | null = null;
|
|
287
|
+
const resolveCustomProviderId = (providerType: string) =>
|
|
288
|
+
providerType === 'kimi-coding' ? 'kimi-code' : providerType;
|
|
306
289
|
|
|
307
290
|
/**
|
|
308
291
|
* Get the notification service instance
|
|
@@ -329,6 +312,9 @@ rateLimiter.configure(IPC_CHANNELS.LLM_GET_OLLAMA_MODELS, RATE_LIMIT_CONFIGS.sta
|
|
|
329
312
|
rateLimiter.configure(IPC_CHANNELS.LLM_GET_GEMINI_MODELS, RATE_LIMIT_CONFIGS.standard);
|
|
330
313
|
rateLimiter.configure(IPC_CHANNELS.LLM_GET_OPENROUTER_MODELS, RATE_LIMIT_CONFIGS.standard);
|
|
331
314
|
rateLimiter.configure(IPC_CHANNELS.LLM_GET_BEDROCK_MODELS, RATE_LIMIT_CONFIGS.standard);
|
|
315
|
+
rateLimiter.configure(IPC_CHANNELS.LLM_GET_GROQ_MODELS, RATE_LIMIT_CONFIGS.standard);
|
|
316
|
+
rateLimiter.configure(IPC_CHANNELS.LLM_GET_XAI_MODELS, RATE_LIMIT_CONFIGS.standard);
|
|
317
|
+
rateLimiter.configure(IPC_CHANNELS.LLM_GET_KIMI_MODELS, RATE_LIMIT_CONFIGS.standard);
|
|
332
318
|
rateLimiter.configure(IPC_CHANNELS.SEARCH_SAVE_SETTINGS, RATE_LIMIT_CONFIGS.limited);
|
|
333
319
|
rateLimiter.configure(IPC_CHANNELS.SEARCH_TEST_PROVIDER, RATE_LIMIT_CONFIGS.expensive);
|
|
334
320
|
rateLimiter.configure(IPC_CHANNELS.GATEWAY_ADD_CHANNEL, RATE_LIMIT_CONFIGS.limited);
|
|
@@ -624,6 +610,159 @@ export async function setupIpcHandlers(
|
|
|
624
610
|
}
|
|
625
611
|
});
|
|
626
612
|
|
|
613
|
+
// File import handler - copy selected files into the workspace for attachment use
|
|
614
|
+
ipcMain.handle('file:importToWorkspace', async (_, data: { workspaceId: string; files: string[] }) => {
|
|
615
|
+
const validated = validateInput(FileImportSchema, data, 'file import');
|
|
616
|
+
const workspace = workspaceRepo.findById(validated.workspaceId);
|
|
617
|
+
|
|
618
|
+
if (!workspace) {
|
|
619
|
+
throw new Error(`Workspace not found: ${validated.workspaceId}`);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
if (!workspace.permissions.write) {
|
|
623
|
+
throw new Error('Write permission not granted for workspace');
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
const sanitizeFileName = (fileName: string): string => {
|
|
627
|
+
const sanitized = fileName.replace(/[<>:"/\\|?*\x00-\x1F]/g, '_').trim();
|
|
628
|
+
return sanitized.length > 0 ? sanitized : 'file';
|
|
629
|
+
};
|
|
630
|
+
|
|
631
|
+
const ensureUniqueName = (dir: string, baseName: string, usedNames: Set<string>): string => {
|
|
632
|
+
const ext = path.extname(baseName);
|
|
633
|
+
const stem = path.basename(baseName, ext);
|
|
634
|
+
let candidate = baseName;
|
|
635
|
+
let counter = 1;
|
|
636
|
+
while (usedNames.has(candidate) || fsSync.existsSync(path.join(dir, candidate))) {
|
|
637
|
+
candidate = `${stem}-${counter}${ext}`;
|
|
638
|
+
counter += 1;
|
|
639
|
+
}
|
|
640
|
+
usedNames.add(candidate);
|
|
641
|
+
return candidate;
|
|
642
|
+
};
|
|
643
|
+
|
|
644
|
+
let uploadRoot: string | null = null;
|
|
645
|
+
const usedNames = new Set<string>();
|
|
646
|
+
|
|
647
|
+
const ensureUploadRoot = async (): Promise<string> => {
|
|
648
|
+
if (uploadRoot) return uploadRoot;
|
|
649
|
+
uploadRoot = path.join(workspace.path, '.cowork', 'uploads', `${Date.now()}`);
|
|
650
|
+
await fs.mkdir(uploadRoot, { recursive: true });
|
|
651
|
+
return uploadRoot;
|
|
652
|
+
};
|
|
653
|
+
|
|
654
|
+
const results: Array<{ relativePath: string; fileName: string; size: number; mimeType?: string }> = [];
|
|
655
|
+
|
|
656
|
+
for (const filePath of validated.files) {
|
|
657
|
+
const absolutePath = path.isAbsolute(filePath) ? filePath : path.resolve(filePath);
|
|
658
|
+
const stats = await fs.stat(absolutePath);
|
|
659
|
+
|
|
660
|
+
if (!stats.isFile()) {
|
|
661
|
+
throw new Error(`Not a file: ${filePath}`);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
const sizeCheck = GuardrailManager.isFileSizeExceeded(stats.size);
|
|
665
|
+
if (sizeCheck.exceeded) {
|
|
666
|
+
throw new Error(`File "${path.basename(filePath)}" is ${sizeCheck.sizeMB.toFixed(1)}MB and exceeds the ${sizeCheck.limitMB}MB limit.`);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
const mimeType = (mime.lookup(absolutePath) || undefined) as string | undefined;
|
|
670
|
+
|
|
671
|
+
if (isPathWithinWorkspace(absolutePath, workspace.path)) {
|
|
672
|
+
results.push({
|
|
673
|
+
relativePath: path.relative(workspace.path, absolutePath),
|
|
674
|
+
fileName: path.basename(absolutePath),
|
|
675
|
+
size: stats.size,
|
|
676
|
+
mimeType,
|
|
677
|
+
});
|
|
678
|
+
continue;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
const safeName = sanitizeFileName(path.basename(absolutePath));
|
|
682
|
+
const targetRoot = await ensureUploadRoot();
|
|
683
|
+
const uniqueName = ensureUniqueName(targetRoot, safeName, usedNames);
|
|
684
|
+
const destination = path.join(targetRoot, uniqueName);
|
|
685
|
+
|
|
686
|
+
await fs.copyFile(absolutePath, destination);
|
|
687
|
+
|
|
688
|
+
results.push({
|
|
689
|
+
relativePath: path.relative(workspace.path, destination),
|
|
690
|
+
fileName: uniqueName,
|
|
691
|
+
size: stats.size,
|
|
692
|
+
mimeType,
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
return results;
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
// File import handler - save provided file data into the workspace (clipboard / drag data)
|
|
700
|
+
ipcMain.handle('file:importDataToWorkspace', async (_, data: { workspaceId: string; files: Array<{ name: string; data: string; mimeType?: string }> }) => {
|
|
701
|
+
const validated = validateInput(FileImportDataSchema, data, 'file import data');
|
|
702
|
+
const workspace = workspaceRepo.findById(validated.workspaceId);
|
|
703
|
+
|
|
704
|
+
if (!workspace) {
|
|
705
|
+
throw new Error(`Workspace not found: ${validated.workspaceId}`);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
if (!workspace.permissions.write) {
|
|
709
|
+
throw new Error('Write permission not granted for workspace');
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
const sanitizeFileName = (fileName: string): string => {
|
|
713
|
+
const sanitized = fileName.replace(/[<>:"/\\|?*\x00-\x1F]/g, '_').trim();
|
|
714
|
+
return sanitized.length > 0 ? sanitized : 'file';
|
|
715
|
+
};
|
|
716
|
+
|
|
717
|
+
const ensureExtension = (fileName: string, mimeType?: string): string => {
|
|
718
|
+
if (path.extname(fileName) || !mimeType) return fileName;
|
|
719
|
+
const ext = mime.extension(mimeType);
|
|
720
|
+
return ext ? `${fileName}.${ext}` : fileName;
|
|
721
|
+
};
|
|
722
|
+
|
|
723
|
+
const ensureUniqueName = (dir: string, baseName: string, usedNames: Set<string>): string => {
|
|
724
|
+
const ext = path.extname(baseName);
|
|
725
|
+
const stem = path.basename(baseName, ext);
|
|
726
|
+
let candidate = baseName;
|
|
727
|
+
let counter = 1;
|
|
728
|
+
while (usedNames.has(candidate) || fsSync.existsSync(path.join(dir, candidate))) {
|
|
729
|
+
candidate = `${stem}-${counter}${ext}`;
|
|
730
|
+
counter += 1;
|
|
731
|
+
}
|
|
732
|
+
usedNames.add(candidate);
|
|
733
|
+
return candidate;
|
|
734
|
+
};
|
|
735
|
+
|
|
736
|
+
const uploadRoot = path.join(workspace.path, '.cowork', 'uploads', `${Date.now()}`);
|
|
737
|
+
await fs.mkdir(uploadRoot, { recursive: true });
|
|
738
|
+
const usedNames = new Set<string>();
|
|
739
|
+
|
|
740
|
+
const results: Array<{ relativePath: string; fileName: string; size: number; mimeType?: string }> = [];
|
|
741
|
+
|
|
742
|
+
for (const file of validated.files) {
|
|
743
|
+
const rawName = ensureExtension(sanitizeFileName(file.name), file.mimeType);
|
|
744
|
+
const uniqueName = ensureUniqueName(uploadRoot, rawName, usedNames);
|
|
745
|
+
const destination = path.join(uploadRoot, uniqueName);
|
|
746
|
+
const buffer = Buffer.from(file.data, 'base64');
|
|
747
|
+
|
|
748
|
+
const sizeCheck = GuardrailManager.isFileSizeExceeded(buffer.length);
|
|
749
|
+
if (sizeCheck.exceeded) {
|
|
750
|
+
throw new Error(`File "${rawName}" is ${sizeCheck.sizeMB.toFixed(1)}MB and exceeds the ${sizeCheck.limitMB}MB limit.`);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
await fs.writeFile(destination, buffer);
|
|
754
|
+
|
|
755
|
+
results.push({
|
|
756
|
+
relativePath: path.relative(workspace.path, destination),
|
|
757
|
+
fileName: uniqueName,
|
|
758
|
+
size: buffer.length,
|
|
759
|
+
mimeType: file.mimeType,
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
return results;
|
|
764
|
+
});
|
|
765
|
+
|
|
627
766
|
// Workspace handlers
|
|
628
767
|
ipcMain.handle(IPC_CHANNELS.WORKSPACE_CREATE, async (_, data) => {
|
|
629
768
|
const validated = validateInput(WorkspaceCreateSchema, data, 'workspace');
|
|
@@ -659,7 +798,15 @@ export async function setupIpcHandlers(
|
|
|
659
798
|
});
|
|
660
799
|
|
|
661
800
|
ipcMain.handle(IPC_CHANNELS.WORKSPACE_SELECT, async (_, id: string) => {
|
|
662
|
-
|
|
801
|
+
const workspace = workspaceRepo.findById(id);
|
|
802
|
+
if (workspace && workspace.id !== TEMP_WORKSPACE_ID) {
|
|
803
|
+
try {
|
|
804
|
+
workspaceRepo.updateLastUsedAt(workspace.id);
|
|
805
|
+
} catch (error) {
|
|
806
|
+
console.warn('Failed to update workspace last used time:', error);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
return workspace;
|
|
663
810
|
});
|
|
664
811
|
|
|
665
812
|
ipcMain.handle(IPC_CHANNELS.WORKSPACE_UPDATE_PERMISSIONS, async (_, id: string, permissions: { shell?: boolean; network?: boolean; read?: boolean; write?: boolean; delete?: boolean }) => {
|
|
@@ -672,6 +819,15 @@ export async function setupIpcHandlers(
|
|
|
672
819
|
return workspaceRepo.findById(id);
|
|
673
820
|
});
|
|
674
821
|
|
|
822
|
+
ipcMain.handle(IPC_CHANNELS.WORKSPACE_TOUCH, async (_, id: string) => {
|
|
823
|
+
const workspace = workspaceRepo.findById(id);
|
|
824
|
+
if (!workspace) {
|
|
825
|
+
throw new Error(`Workspace not found: ${id}`);
|
|
826
|
+
}
|
|
827
|
+
workspaceRepo.updateLastUsedAt(id);
|
|
828
|
+
return workspaceRepo.findById(id);
|
|
829
|
+
});
|
|
830
|
+
|
|
675
831
|
// Task handlers
|
|
676
832
|
ipcMain.handle(IPC_CHANNELS.TASK_CREATE, async (_, data) => {
|
|
677
833
|
checkRateLimit(IPC_CHANNELS.TASK_CREATE);
|
|
@@ -686,104 +842,49 @@ export async function setupIpcHandlers(
|
|
|
686
842
|
budgetCost,
|
|
687
843
|
});
|
|
688
844
|
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
status: 'failed',
|
|
696
|
-
error: error.message || 'Failed to start task',
|
|
697
|
-
});
|
|
698
|
-
throw new Error(error.message || 'Failed to start task. Please check your LLM provider settings.');
|
|
845
|
+
if (workspaceId !== TEMP_WORKSPACE_ID) {
|
|
846
|
+
try {
|
|
847
|
+
workspaceRepo.updateLastUsedAt(workspaceId);
|
|
848
|
+
} catch (error) {
|
|
849
|
+
console.warn('Failed to update workspace last used time:', error);
|
|
850
|
+
}
|
|
699
851
|
}
|
|
700
852
|
|
|
701
|
-
//
|
|
853
|
+
// Capture mentioned agent roles for deferred dispatch (after main plan is created)
|
|
702
854
|
try {
|
|
703
855
|
const activeRoles = agentRoleRepo.findAll(false).filter((role) => role.isActive);
|
|
704
856
|
const mentionedRoles = extractMentionedRoles(`${title}\n${prompt}`, activeRoles);
|
|
705
|
-
const
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
const taskUpdate: Partial<Task> = {
|
|
709
|
-
mentionedAgentRoleIds: dispatchRoles.map((role) => role.id),
|
|
710
|
-
};
|
|
711
|
-
taskRepo.update(task.id, taskUpdate);
|
|
712
|
-
|
|
713
|
-
// Parallelize child task creation for better performance
|
|
714
|
-
const dispatchPromises = dispatchRoles.map(async (role) => {
|
|
715
|
-
const childPrompt = buildAgentDispatchPrompt(role, task);
|
|
716
|
-
const childTask = await agentDaemon.createChildTask({
|
|
717
|
-
title: `@${role.displayName}: ${task.title}`,
|
|
718
|
-
prompt: childPrompt,
|
|
719
|
-
workspaceId: task.workspaceId,
|
|
720
|
-
parentTaskId: task.id,
|
|
721
|
-
agentType: 'sub',
|
|
722
|
-
agentConfig: {
|
|
723
|
-
...(role.modelKey ? { modelKey: role.modelKey } : {}),
|
|
724
|
-
...(role.personalityId ? { personalityId: role.personalityId } : {}),
|
|
725
|
-
retainMemory: false,
|
|
726
|
-
},
|
|
727
|
-
});
|
|
728
|
-
|
|
729
|
-
const childUpdate: Partial<Task> = {
|
|
730
|
-
assignedAgentRoleId: role.id,
|
|
731
|
-
boardColumn: 'todo' as BoardColumn,
|
|
732
|
-
};
|
|
733
|
-
taskRepo.update(childTask.id, childUpdate);
|
|
734
|
-
|
|
735
|
-
const dispatchActivity = activityRepo.create({
|
|
736
|
-
workspaceId: task.workspaceId,
|
|
737
|
-
taskId: task.id,
|
|
738
|
-
agentRoleId: role.id,
|
|
739
|
-
actorType: 'system',
|
|
740
|
-
activityType: 'agent_assigned',
|
|
741
|
-
title: `Dispatched to ${role.displayName}`,
|
|
742
|
-
description: childTask.title,
|
|
743
|
-
});
|
|
744
|
-
getMainWindow()?.webContents.send(IPC_CHANNELS.ACTIVITY_EVENT, { type: 'created', activity: dispatchActivity });
|
|
745
|
-
|
|
746
|
-
const mention = mentionRepo.create({
|
|
747
|
-
workspaceId: task.workspaceId,
|
|
748
|
-
taskId: task.id,
|
|
749
|
-
toAgentRoleId: role.id,
|
|
750
|
-
mentionType: 'request',
|
|
751
|
-
context: `New task: ${task.title}`,
|
|
752
|
-
});
|
|
753
|
-
getMainWindow()?.webContents.send(IPC_CHANNELS.MENTION_EVENT, { type: 'created', mention });
|
|
754
|
-
|
|
755
|
-
const mentionActivity = activityRepo.create({
|
|
756
|
-
workspaceId: task.workspaceId,
|
|
757
|
-
taskId: task.id,
|
|
758
|
-
agentRoleId: role.id,
|
|
759
|
-
actorType: 'user',
|
|
760
|
-
activityType: 'mention',
|
|
761
|
-
title: `@${role.displayName} mentioned`,
|
|
762
|
-
description: mention.context,
|
|
763
|
-
metadata: { mentionId: mention.id, mentionType: mention.mentionType },
|
|
764
|
-
});
|
|
765
|
-
getMainWindow()?.webContents.send(IPC_CHANNELS.ACTIVITY_EVENT, { type: 'created', activity: mentionActivity });
|
|
766
|
-
|
|
767
|
-
return { role, childTask };
|
|
768
|
-
});
|
|
769
|
-
|
|
770
|
-
await Promise.all(dispatchPromises);
|
|
857
|
+
const mentionedAgentRoleIds = mentionedRoles.map((role) => role.id);
|
|
858
|
+
if (mentionedAgentRoleIds.length > 0) {
|
|
859
|
+
taskRepo.update(task.id, { mentionedAgentRoleIds });
|
|
771
860
|
}
|
|
772
861
|
} catch (error: unknown) {
|
|
773
862
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
774
|
-
console.error('Failed to
|
|
863
|
+
console.error('Failed to record mentioned agents:', error);
|
|
775
864
|
// Notify user of dispatch failure via activity feed
|
|
776
865
|
const errorActivity = activityRepo.create({
|
|
777
866
|
workspaceId: task.workspaceId,
|
|
778
867
|
taskId: task.id,
|
|
779
868
|
actorType: 'system',
|
|
780
869
|
activityType: 'error',
|
|
781
|
-
title: 'Agent
|
|
782
|
-
description: `Failed to
|
|
870
|
+
title: 'Agent mention capture failed',
|
|
871
|
+
description: `Failed to record mentioned agents for deferred dispatch: ${errorMessage}`,
|
|
783
872
|
});
|
|
784
873
|
getMainWindow()?.webContents.send(IPC_CHANNELS.ACTIVITY_EVENT, { type: 'created', activity: errorActivity });
|
|
785
874
|
}
|
|
786
875
|
|
|
876
|
+
// Start task execution in agent daemon
|
|
877
|
+
try {
|
|
878
|
+
await agentDaemon.startTask(task);
|
|
879
|
+
} catch (error: any) {
|
|
880
|
+
// Update task status to failed if we can't start it
|
|
881
|
+
taskRepo.update(task.id, {
|
|
882
|
+
status: 'failed',
|
|
883
|
+
error: error.message || 'Failed to start task',
|
|
884
|
+
});
|
|
885
|
+
throw new Error(error.message || 'Failed to start task. Please check your LLM provider settings.');
|
|
886
|
+
}
|
|
887
|
+
|
|
787
888
|
return task;
|
|
788
889
|
});
|
|
789
890
|
|
|
@@ -1031,6 +1132,29 @@ export async function setupIpcHandlers(
|
|
|
1031
1132
|
};
|
|
1032
1133
|
}
|
|
1033
1134
|
|
|
1135
|
+
const normalizeAzureSettings = (
|
|
1136
|
+
incoming?: LLMSettingsData['azure'],
|
|
1137
|
+
existing?: LLMSettingsData['azure']
|
|
1138
|
+
): LLMSettingsData['azure'] | undefined => {
|
|
1139
|
+
if (!incoming && !existing) return undefined;
|
|
1140
|
+
const mergedDeployments = [
|
|
1141
|
+
...(incoming?.deployments || []),
|
|
1142
|
+
...(existing?.deployments || []),
|
|
1143
|
+
]
|
|
1144
|
+
.map((entry) => entry.trim())
|
|
1145
|
+
.filter(Boolean);
|
|
1146
|
+
const deployment = (incoming?.deployment || existing?.deployment || mergedDeployments[0] || '').trim();
|
|
1147
|
+
if (deployment && !mergedDeployments.includes(deployment)) {
|
|
1148
|
+
mergedDeployments.unshift(deployment);
|
|
1149
|
+
}
|
|
1150
|
+
return {
|
|
1151
|
+
...(existing || {}),
|
|
1152
|
+
...(incoming || {}),
|
|
1153
|
+
deployment: deployment || undefined,
|
|
1154
|
+
deployments: mergedDeployments.length > 0 ? Array.from(new Set(mergedDeployments)) : undefined,
|
|
1155
|
+
};
|
|
1156
|
+
};
|
|
1157
|
+
|
|
1034
1158
|
LLMProviderFactory.saveSettings({
|
|
1035
1159
|
providerType: validated.providerType,
|
|
1036
1160
|
modelKey: validated.modelKey as ModelKey,
|
|
@@ -1040,12 +1164,20 @@ export async function setupIpcHandlers(
|
|
|
1040
1164
|
gemini: validated.gemini,
|
|
1041
1165
|
openrouter: validated.openrouter,
|
|
1042
1166
|
openai: openaiSettings,
|
|
1167
|
+
azure: normalizeAzureSettings(validated.azure, existingSettings.azure),
|
|
1168
|
+
groq: validated.groq,
|
|
1169
|
+
xai: validated.xai,
|
|
1170
|
+
kimi: validated.kimi,
|
|
1171
|
+
customProviders: validated.customProviders ?? existingSettings.customProviders,
|
|
1043
1172
|
// Preserve cached models from existing settings
|
|
1044
1173
|
cachedGeminiModels: existingSettings.cachedGeminiModels,
|
|
1045
1174
|
cachedOpenRouterModels: existingSettings.cachedOpenRouterModels,
|
|
1046
1175
|
cachedOllamaModels: existingSettings.cachedOllamaModels,
|
|
1047
1176
|
cachedBedrockModels: existingSettings.cachedBedrockModels,
|
|
1048
1177
|
cachedOpenAIModels: existingSettings.cachedOpenAIModels,
|
|
1178
|
+
cachedGroqModels: existingSettings.cachedGroqModels,
|
|
1179
|
+
cachedXaiModels: existingSettings.cachedXaiModels,
|
|
1180
|
+
cachedKimiModels: existingSettings.cachedKimiModels,
|
|
1049
1181
|
});
|
|
1050
1182
|
// Clear cache so next task uses new settings
|
|
1051
1183
|
LLMProviderFactory.clearCache();
|
|
@@ -1062,6 +1194,9 @@ export async function setupIpcHandlers(
|
|
|
1062
1194
|
openaiAccessToken = settings.openai?.accessToken;
|
|
1063
1195
|
openaiRefreshToken = settings.openai?.refreshToken;
|
|
1064
1196
|
}
|
|
1197
|
+
const resolvedProviderType = resolveCustomProviderId(config.providerType);
|
|
1198
|
+
const customProviderConfig = config.customProviders?.[resolvedProviderType] || config.customProviders?.[config.providerType];
|
|
1199
|
+
const azureDeployment = config.azure?.deployment || config.azure?.deployments?.[0];
|
|
1065
1200
|
const providerConfig: LLMProviderConfig = {
|
|
1066
1201
|
type: config.providerType,
|
|
1067
1202
|
model: LLMProviderFactory.getModelId(
|
|
@@ -1070,7 +1205,12 @@ export async function setupIpcHandlers(
|
|
|
1070
1205
|
config.ollama?.model,
|
|
1071
1206
|
config.gemini?.model,
|
|
1072
1207
|
config.openrouter?.model,
|
|
1073
|
-
config.openai?.model
|
|
1208
|
+
config.openai?.model,
|
|
1209
|
+
azureDeployment,
|
|
1210
|
+
config.groq?.model,
|
|
1211
|
+
config.xai?.model,
|
|
1212
|
+
config.kimi?.model,
|
|
1213
|
+
config.customProviders
|
|
1074
1214
|
),
|
|
1075
1215
|
anthropicApiKey: config.anthropic?.apiKey,
|
|
1076
1216
|
awsRegion: config.bedrock?.region,
|
|
@@ -1082,9 +1222,22 @@ export async function setupIpcHandlers(
|
|
|
1082
1222
|
ollamaApiKey: config.ollama?.apiKey,
|
|
1083
1223
|
geminiApiKey: config.gemini?.apiKey,
|
|
1084
1224
|
openrouterApiKey: config.openrouter?.apiKey,
|
|
1225
|
+
openrouterBaseUrl: config.openrouter?.baseUrl,
|
|
1085
1226
|
openaiApiKey: config.openai?.apiKey,
|
|
1086
1227
|
openaiAccessToken: openaiAccessToken,
|
|
1087
1228
|
openaiRefreshToken: openaiRefreshToken,
|
|
1229
|
+
azureApiKey: config.azure?.apiKey,
|
|
1230
|
+
azureEndpoint: config.azure?.endpoint,
|
|
1231
|
+
azureDeployment: azureDeployment,
|
|
1232
|
+
azureApiVersion: config.azure?.apiVersion,
|
|
1233
|
+
groqApiKey: config.groq?.apiKey,
|
|
1234
|
+
groqBaseUrl: config.groq?.baseUrl,
|
|
1235
|
+
xaiApiKey: config.xai?.apiKey,
|
|
1236
|
+
xaiBaseUrl: config.xai?.baseUrl,
|
|
1237
|
+
kimiApiKey: config.kimi?.apiKey,
|
|
1238
|
+
kimiBaseUrl: config.kimi?.baseUrl,
|
|
1239
|
+
providerApiKey: customProviderConfig?.apiKey,
|
|
1240
|
+
providerBaseUrl: customProviderConfig?.baseUrl,
|
|
1088
1241
|
};
|
|
1089
1242
|
return LLMProviderFactory.testProvider(providerConfig);
|
|
1090
1243
|
});
|
|
@@ -1106,134 +1259,232 @@ export async function setupIpcHandlers(
|
|
|
1106
1259
|
// Get models based on the current provider type
|
|
1107
1260
|
let models: Array<{ key: string; displayName: string; description: string }> = [];
|
|
1108
1261
|
let currentModel = settings.modelKey;
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
models = cachedGemini;
|
|
1130
|
-
} else {
|
|
1131
|
-
// Fall back to static models
|
|
1132
|
-
models = Object.values(GEMINI_MODELS).map((value) => ({
|
|
1133
|
-
key: value.id,
|
|
1262
|
+
const resolvedProviderType = resolveCustomProviderId(settings.providerType);
|
|
1263
|
+
const customEntry = CUSTOM_PROVIDER_MAP.get(resolvedProviderType as any);
|
|
1264
|
+
|
|
1265
|
+
if (customEntry) {
|
|
1266
|
+
const customConfig = settings.customProviders?.[resolvedProviderType] || settings.customProviders?.[settings.providerType];
|
|
1267
|
+
currentModel = customConfig?.model || customEntry.defaultModel;
|
|
1268
|
+
models = [
|
|
1269
|
+
{
|
|
1270
|
+
key: currentModel,
|
|
1271
|
+
displayName: currentModel,
|
|
1272
|
+
description: customEntry.description || `${customEntry.name} model`,
|
|
1273
|
+
},
|
|
1274
|
+
];
|
|
1275
|
+
} else {
|
|
1276
|
+
switch (settings.providerType) {
|
|
1277
|
+
case 'anthropic':
|
|
1278
|
+
case 'bedrock':
|
|
1279
|
+
// Use Anthropic/Bedrock models from MODELS
|
|
1280
|
+
models = Object.entries(MODELS).map(([key, value]) => ({
|
|
1281
|
+
key,
|
|
1134
1282
|
displayName: value.displayName,
|
|
1135
|
-
description:
|
|
1283
|
+
description: key.includes('opus') ? 'Most capable for complex work' :
|
|
1284
|
+
key.includes('sonnet') ? 'Balanced performance and speed' :
|
|
1285
|
+
'Fast and efficient',
|
|
1136
1286
|
}));
|
|
1287
|
+
break;
|
|
1288
|
+
|
|
1289
|
+
case 'gemini': {
|
|
1290
|
+
// For Gemini, use the specific model from settings (full model ID)
|
|
1291
|
+
currentModel = settings.gemini?.model || 'gemini-2.0-flash';
|
|
1292
|
+
// Use cached models if available, otherwise fall back to static list
|
|
1293
|
+
const cachedGemini = LLMProviderFactory.getCachedModels('gemini');
|
|
1294
|
+
if (cachedGemini && cachedGemini.length > 0) {
|
|
1295
|
+
models = cachedGemini;
|
|
1296
|
+
} else {
|
|
1297
|
+
// Fall back to static models
|
|
1298
|
+
models = Object.values(GEMINI_MODELS).map((value) => ({
|
|
1299
|
+
key: value.id,
|
|
1300
|
+
displayName: value.displayName,
|
|
1301
|
+
description: value.description,
|
|
1302
|
+
}));
|
|
1303
|
+
}
|
|
1304
|
+
// Ensure the currently selected model is in the list
|
|
1305
|
+
if (currentModel && !models.some(m => m.key === currentModel)) {
|
|
1306
|
+
models.unshift({
|
|
1307
|
+
key: currentModel,
|
|
1308
|
+
displayName: currentModel,
|
|
1309
|
+
description: 'Selected model',
|
|
1310
|
+
});
|
|
1311
|
+
}
|
|
1312
|
+
break;
|
|
1137
1313
|
}
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1314
|
+
|
|
1315
|
+
case 'openrouter': {
|
|
1316
|
+
// For OpenRouter, use the specific model from settings (full model ID)
|
|
1317
|
+
currentModel = settings.openrouter?.model || 'anthropic/claude-3.5-sonnet';
|
|
1318
|
+
// Use cached models if available, otherwise fall back to static list
|
|
1319
|
+
const cachedOpenRouter = LLMProviderFactory.getCachedModels('openrouter');
|
|
1320
|
+
if (cachedOpenRouter && cachedOpenRouter.length > 0) {
|
|
1321
|
+
models = cachedOpenRouter;
|
|
1322
|
+
} else {
|
|
1323
|
+
// Fall back to static models
|
|
1324
|
+
models = Object.values(OPENROUTER_MODELS).map((value) => ({
|
|
1325
|
+
key: value.id,
|
|
1326
|
+
displayName: value.displayName,
|
|
1327
|
+
description: value.description,
|
|
1328
|
+
}));
|
|
1329
|
+
}
|
|
1330
|
+
// Ensure the currently selected model is in the list
|
|
1331
|
+
if (currentModel && !models.some(m => m.key === currentModel)) {
|
|
1332
|
+
models.unshift({
|
|
1333
|
+
key: currentModel,
|
|
1334
|
+
displayName: currentModel,
|
|
1335
|
+
description: 'Selected model',
|
|
1336
|
+
});
|
|
1337
|
+
}
|
|
1338
|
+
break;
|
|
1145
1339
|
}
|
|
1146
|
-
break;
|
|
1147
|
-
}
|
|
1148
1340
|
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1341
|
+
case 'ollama': {
|
|
1342
|
+
// For Ollama, use the specific model from settings
|
|
1343
|
+
currentModel = settings.ollama?.model || 'llama3.2';
|
|
1344
|
+
// Use cached models if available, otherwise fall back to static list
|
|
1345
|
+
const cachedOllama = LLMProviderFactory.getCachedModels('ollama');
|
|
1346
|
+
if (cachedOllama && cachedOllama.length > 0) {
|
|
1347
|
+
models = cachedOllama;
|
|
1348
|
+
} else {
|
|
1349
|
+
// Fall back to static models
|
|
1350
|
+
models = Object.entries(OLLAMA_MODELS).map(([key, value]) => ({
|
|
1351
|
+
key,
|
|
1352
|
+
displayName: value.displayName,
|
|
1353
|
+
description: `${value.size} parameter model`,
|
|
1354
|
+
}));
|
|
1355
|
+
}
|
|
1356
|
+
// Ensure the currently selected model is in the list
|
|
1357
|
+
if (currentModel && !models.some(m => m.key === currentModel)) {
|
|
1358
|
+
models.unshift({
|
|
1359
|
+
key: currentModel,
|
|
1360
|
+
displayName: currentModel,
|
|
1361
|
+
description: 'Selected model',
|
|
1362
|
+
});
|
|
1363
|
+
}
|
|
1364
|
+
break;
|
|
1163
1365
|
}
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1366
|
+
|
|
1367
|
+
case 'openai': {
|
|
1368
|
+
// For OpenAI, use the specific model from settings
|
|
1369
|
+
currentModel = settings.openai?.model || 'gpt-4o-mini';
|
|
1370
|
+
// Use cached models if available, otherwise fall back to static list
|
|
1371
|
+
const cachedOpenAI = LLMProviderFactory.getCachedModels('openai');
|
|
1372
|
+
if (cachedOpenAI && cachedOpenAI.length > 0) {
|
|
1373
|
+
models = cachedOpenAI;
|
|
1374
|
+
} else {
|
|
1375
|
+
// Fall back to static models
|
|
1376
|
+
models = [
|
|
1377
|
+
{ key: 'gpt-4o', displayName: 'GPT-4o', description: 'Most capable model for complex tasks' },
|
|
1378
|
+
{ key: 'gpt-4o-mini', displayName: 'GPT-4o Mini', description: 'Fast and affordable for most tasks' },
|
|
1379
|
+
{ key: 'gpt-4-turbo', displayName: 'GPT-4 Turbo', description: 'Previous generation flagship' },
|
|
1380
|
+
{ key: 'gpt-3.5-turbo', displayName: 'GPT-3.5 Turbo', description: 'Fast and cost-effective' },
|
|
1381
|
+
{ key: 'o1', displayName: 'o1', description: 'Advanced reasoning model' },
|
|
1382
|
+
{ key: 'o1-mini', displayName: 'o1 Mini', description: 'Fast reasoning model' },
|
|
1383
|
+
];
|
|
1384
|
+
}
|
|
1385
|
+
// Ensure the currently selected model is in the list
|
|
1386
|
+
if (currentModel && !models.some(m => m.key === currentModel)) {
|
|
1387
|
+
models.unshift({
|
|
1388
|
+
key: currentModel,
|
|
1389
|
+
displayName: currentModel,
|
|
1390
|
+
description: 'Selected model',
|
|
1391
|
+
});
|
|
1392
|
+
}
|
|
1393
|
+
break;
|
|
1171
1394
|
}
|
|
1172
|
-
break;
|
|
1173
|
-
}
|
|
1174
1395
|
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
} else {
|
|
1183
|
-
// Fall back to static models
|
|
1184
|
-
models = Object.entries(OLLAMA_MODELS).map(([key, value]) => ({
|
|
1185
|
-
key,
|
|
1186
|
-
displayName: value.displayName,
|
|
1187
|
-
description: `${value.size} parameter model`,
|
|
1396
|
+
case 'azure': {
|
|
1397
|
+
const deployments = (settings.azure?.deployments || []).filter(Boolean);
|
|
1398
|
+
currentModel = settings.azure?.deployment || deployments[0] || 'deployment-name';
|
|
1399
|
+
models = deployments.map((deployment) => ({
|
|
1400
|
+
key: deployment,
|
|
1401
|
+
displayName: deployment,
|
|
1402
|
+
description: 'Azure OpenAI deployment',
|
|
1188
1403
|
}));
|
|
1404
|
+
if (currentModel && !models.some(m => m.key === currentModel)) {
|
|
1405
|
+
models.unshift({
|
|
1406
|
+
key: currentModel,
|
|
1407
|
+
displayName: currentModel,
|
|
1408
|
+
description: 'Selected model',
|
|
1409
|
+
});
|
|
1410
|
+
}
|
|
1411
|
+
break;
|
|
1189
1412
|
}
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
}
|
|
1413
|
+
|
|
1414
|
+
case 'groq': {
|
|
1415
|
+
currentModel = settings.groq?.model || 'llama-3.1-8b-instant';
|
|
1416
|
+
const cachedGroq = LLMProviderFactory.getCachedModels('groq');
|
|
1417
|
+
if (cachedGroq && cachedGroq.length > 0) {
|
|
1418
|
+
models = cachedGroq;
|
|
1419
|
+
} else {
|
|
1420
|
+
models = Object.values(GROQ_MODELS).map((value) => ({
|
|
1421
|
+
key: value.id,
|
|
1422
|
+
displayName: value.displayName,
|
|
1423
|
+
description: value.description,
|
|
1424
|
+
}));
|
|
1425
|
+
}
|
|
1426
|
+
if (currentModel && !models.some(m => m.key === currentModel)) {
|
|
1427
|
+
models.unshift({
|
|
1428
|
+
key: currentModel,
|
|
1429
|
+
displayName: currentModel,
|
|
1430
|
+
description: 'Selected model',
|
|
1431
|
+
});
|
|
1432
|
+
}
|
|
1433
|
+
break;
|
|
1197
1434
|
}
|
|
1198
|
-
break;
|
|
1199
|
-
}
|
|
1200
1435
|
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
{
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1436
|
+
case 'xai': {
|
|
1437
|
+
currentModel = settings.xai?.model || 'grok-4-fast-non-reasoning';
|
|
1438
|
+
const cachedXai = LLMProviderFactory.getCachedModels('xai');
|
|
1439
|
+
if (cachedXai && cachedXai.length > 0) {
|
|
1440
|
+
models = cachedXai;
|
|
1441
|
+
} else {
|
|
1442
|
+
models = Object.values(XAI_MODELS).map((value) => ({
|
|
1443
|
+
key: value.id,
|
|
1444
|
+
displayName: value.displayName,
|
|
1445
|
+
description: value.description,
|
|
1446
|
+
}));
|
|
1447
|
+
}
|
|
1448
|
+
if (currentModel && !models.some(m => m.key === currentModel)) {
|
|
1449
|
+
models.unshift({
|
|
1450
|
+
key: currentModel,
|
|
1451
|
+
displayName: currentModel,
|
|
1452
|
+
description: 'Selected model',
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1455
|
+
break;
|
|
1218
1456
|
}
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
}
|
|
1457
|
+
|
|
1458
|
+
case 'kimi': {
|
|
1459
|
+
currentModel = settings.kimi?.model || 'kimi-k2.5';
|
|
1460
|
+
const cachedKimi = LLMProviderFactory.getCachedModels('kimi');
|
|
1461
|
+
if (cachedKimi && cachedKimi.length > 0) {
|
|
1462
|
+
models = cachedKimi;
|
|
1463
|
+
} else {
|
|
1464
|
+
models = Object.values(KIMI_MODELS).map((value) => ({
|
|
1465
|
+
key: value.id,
|
|
1466
|
+
displayName: value.displayName,
|
|
1467
|
+
description: value.description,
|
|
1468
|
+
}));
|
|
1469
|
+
}
|
|
1470
|
+
if (currentModel && !models.some(m => m.key === currentModel)) {
|
|
1471
|
+
models.unshift({
|
|
1472
|
+
key: currentModel,
|
|
1473
|
+
displayName: currentModel,
|
|
1474
|
+
description: 'Selected model',
|
|
1475
|
+
});
|
|
1476
|
+
}
|
|
1477
|
+
break;
|
|
1226
1478
|
}
|
|
1227
|
-
break;
|
|
1228
|
-
}
|
|
1229
1479
|
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1480
|
+
default:
|
|
1481
|
+
// Fallback to Anthropic models
|
|
1482
|
+
models = Object.entries(MODELS).map(([key, value]) => ({
|
|
1483
|
+
key,
|
|
1484
|
+
displayName: value.displayName,
|
|
1485
|
+
description: 'Claude model',
|
|
1486
|
+
}));
|
|
1487
|
+
}
|
|
1237
1488
|
}
|
|
1238
1489
|
|
|
1239
1490
|
return {
|
|
@@ -1247,27 +1498,61 @@ export async function setupIpcHandlers(
|
|
|
1247
1498
|
// Set the current model (persists selection across sessions)
|
|
1248
1499
|
ipcMain.handle(IPC_CHANNELS.LLM_SET_MODEL, async (_, modelKey: string) => {
|
|
1249
1500
|
const settings = LLMProviderFactory.loadSettings();
|
|
1501
|
+
const resolvedProviderType = resolveCustomProviderId(settings.providerType);
|
|
1250
1502
|
|
|
1251
1503
|
// Update the model key based on the current provider
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1504
|
+
if (CUSTOM_PROVIDER_IDS.has(resolvedProviderType as any)) {
|
|
1505
|
+
const existing = settings.customProviders?.[resolvedProviderType] || {};
|
|
1506
|
+
settings.customProviders = {
|
|
1507
|
+
...(settings.customProviders || {}),
|
|
1508
|
+
[resolvedProviderType]: {
|
|
1509
|
+
...existing,
|
|
1510
|
+
model: modelKey,
|
|
1511
|
+
},
|
|
1512
|
+
};
|
|
1513
|
+
} else {
|
|
1514
|
+
switch (settings.providerType) {
|
|
1515
|
+
case 'gemini':
|
|
1516
|
+
settings.gemini = { ...settings.gemini, model: modelKey };
|
|
1517
|
+
break;
|
|
1518
|
+
case 'openrouter':
|
|
1519
|
+
settings.openrouter = { ...settings.openrouter, model: modelKey };
|
|
1520
|
+
break;
|
|
1521
|
+
case 'ollama':
|
|
1522
|
+
settings.ollama = { ...settings.ollama, model: modelKey };
|
|
1523
|
+
break;
|
|
1524
|
+
case 'openai':
|
|
1525
|
+
settings.openai = { ...settings.openai, model: modelKey };
|
|
1526
|
+
break;
|
|
1527
|
+
case 'azure':
|
|
1528
|
+
{
|
|
1529
|
+
const existingDeployments = (settings.azure?.deployments || []).filter(Boolean);
|
|
1530
|
+
const nextDeployments = existingDeployments.includes(modelKey)
|
|
1531
|
+
? existingDeployments
|
|
1532
|
+
: [modelKey, ...existingDeployments];
|
|
1533
|
+
settings.azure = {
|
|
1534
|
+
...settings.azure,
|
|
1535
|
+
deployment: modelKey,
|
|
1536
|
+
deployments: nextDeployments.length > 0 ? nextDeployments : undefined,
|
|
1537
|
+
};
|
|
1538
|
+
}
|
|
1539
|
+
break;
|
|
1540
|
+
case 'groq':
|
|
1541
|
+
settings.groq = { ...settings.groq, model: modelKey };
|
|
1542
|
+
break;
|
|
1543
|
+
case 'xai':
|
|
1544
|
+
settings.xai = { ...settings.xai, model: modelKey };
|
|
1545
|
+
break;
|
|
1546
|
+
case 'kimi':
|
|
1547
|
+
settings.kimi = { ...settings.kimi, model: modelKey };
|
|
1548
|
+
break;
|
|
1549
|
+
case 'anthropic':
|
|
1550
|
+
case 'bedrock':
|
|
1551
|
+
default:
|
|
1552
|
+
// For Anthropic/Bedrock, use the modelKey field
|
|
1553
|
+
settings.modelKey = modelKey as ModelKey;
|
|
1554
|
+
break;
|
|
1555
|
+
}
|
|
1271
1556
|
}
|
|
1272
1557
|
|
|
1273
1558
|
LLMProviderFactory.saveSettings(settings);
|
|
@@ -1302,9 +1587,9 @@ export async function setupIpcHandlers(
|
|
|
1302
1587
|
return models;
|
|
1303
1588
|
});
|
|
1304
1589
|
|
|
1305
|
-
ipcMain.handle(IPC_CHANNELS.LLM_GET_OPENROUTER_MODELS, async (_, apiKey?: string) => {
|
|
1590
|
+
ipcMain.handle(IPC_CHANNELS.LLM_GET_OPENROUTER_MODELS, async (_, apiKey?: string, baseUrl?: string) => {
|
|
1306
1591
|
checkRateLimit(IPC_CHANNELS.LLM_GET_OPENROUTER_MODELS);
|
|
1307
|
-
const models = await LLMProviderFactory.getOpenRouterModels(apiKey);
|
|
1592
|
+
const models = await LLMProviderFactory.getOpenRouterModels(apiKey, baseUrl);
|
|
1308
1593
|
// Cache the models for use in config status
|
|
1309
1594
|
const cachedModels = models.map(m => ({
|
|
1310
1595
|
key: m.id,
|
|
@@ -1329,6 +1614,42 @@ export async function setupIpcHandlers(
|
|
|
1329
1614
|
return models;
|
|
1330
1615
|
});
|
|
1331
1616
|
|
|
1617
|
+
ipcMain.handle(IPC_CHANNELS.LLM_GET_GROQ_MODELS, async (_, apiKey?: string, baseUrl?: string) => {
|
|
1618
|
+
checkRateLimit(IPC_CHANNELS.LLM_GET_GROQ_MODELS);
|
|
1619
|
+
const models = await LLMProviderFactory.getGroqModels(apiKey, baseUrl);
|
|
1620
|
+
const cachedModels = models.map(m => ({
|
|
1621
|
+
key: m.id,
|
|
1622
|
+
displayName: m.name,
|
|
1623
|
+
description: 'Groq model',
|
|
1624
|
+
}));
|
|
1625
|
+
LLMProviderFactory.saveCachedModels('groq', cachedModels);
|
|
1626
|
+
return models;
|
|
1627
|
+
});
|
|
1628
|
+
|
|
1629
|
+
ipcMain.handle(IPC_CHANNELS.LLM_GET_XAI_MODELS, async (_, apiKey?: string, baseUrl?: string) => {
|
|
1630
|
+
checkRateLimit(IPC_CHANNELS.LLM_GET_XAI_MODELS);
|
|
1631
|
+
const models = await LLMProviderFactory.getXAIModels(apiKey, baseUrl);
|
|
1632
|
+
const cachedModels = models.map(m => ({
|
|
1633
|
+
key: m.id,
|
|
1634
|
+
displayName: m.name,
|
|
1635
|
+
description: 'xAI model',
|
|
1636
|
+
}));
|
|
1637
|
+
LLMProviderFactory.saveCachedModels('xai', cachedModels);
|
|
1638
|
+
return models;
|
|
1639
|
+
});
|
|
1640
|
+
|
|
1641
|
+
ipcMain.handle(IPC_CHANNELS.LLM_GET_KIMI_MODELS, async (_, apiKey?: string, baseUrl?: string) => {
|
|
1642
|
+
checkRateLimit(IPC_CHANNELS.LLM_GET_KIMI_MODELS);
|
|
1643
|
+
const models = await LLMProviderFactory.getKimiModels(apiKey, baseUrl);
|
|
1644
|
+
const cachedModels = models.map(m => ({
|
|
1645
|
+
key: m.id,
|
|
1646
|
+
displayName: m.name,
|
|
1647
|
+
description: 'Kimi model',
|
|
1648
|
+
}));
|
|
1649
|
+
LLMProviderFactory.saveCachedModels('kimi', cachedModels);
|
|
1650
|
+
return models;
|
|
1651
|
+
});
|
|
1652
|
+
|
|
1332
1653
|
// OpenAI OAuth handlers
|
|
1333
1654
|
ipcMain.handle(IPC_CHANNELS.LLM_OPENAI_OAUTH_START, async () => {
|
|
1334
1655
|
checkRateLimit(IPC_CHANNELS.LLM_OPENAI_OAUTH_START);
|
|
@@ -1464,6 +1785,233 @@ export async function setupIpcHandlers(
|
|
|
1464
1785
|
};
|
|
1465
1786
|
});
|
|
1466
1787
|
|
|
1788
|
+
// Notion Settings handlers
|
|
1789
|
+
ipcMain.handle(IPC_CHANNELS.NOTION_GET_SETTINGS, async () => {
|
|
1790
|
+
return NotionSettingsManager.loadSettings();
|
|
1791
|
+
});
|
|
1792
|
+
|
|
1793
|
+
ipcMain.handle(IPC_CHANNELS.NOTION_SAVE_SETTINGS, async (_, settings) => {
|
|
1794
|
+
checkRateLimit(IPC_CHANNELS.NOTION_SAVE_SETTINGS);
|
|
1795
|
+
const validated = validateInput(NotionSettingsSchema, settings, 'notion settings') as NotionSettingsData;
|
|
1796
|
+
NotionSettingsManager.saveSettings(validated);
|
|
1797
|
+
NotionSettingsManager.clearCache();
|
|
1798
|
+
return { success: true };
|
|
1799
|
+
});
|
|
1800
|
+
|
|
1801
|
+
ipcMain.handle(IPC_CHANNELS.NOTION_TEST_CONNECTION, async () => {
|
|
1802
|
+
checkRateLimit(IPC_CHANNELS.NOTION_TEST_CONNECTION);
|
|
1803
|
+
const settings = NotionSettingsManager.loadSettings();
|
|
1804
|
+
return testNotionConnection(settings);
|
|
1805
|
+
});
|
|
1806
|
+
|
|
1807
|
+
ipcMain.handle(IPC_CHANNELS.NOTION_GET_STATUS, async () => {
|
|
1808
|
+
checkRateLimit(IPC_CHANNELS.NOTION_GET_STATUS);
|
|
1809
|
+
const settings = NotionSettingsManager.loadSettings();
|
|
1810
|
+
if (!settings.apiKey) {
|
|
1811
|
+
return { configured: false, connected: false };
|
|
1812
|
+
}
|
|
1813
|
+
if (!settings.enabled) {
|
|
1814
|
+
return { configured: true, connected: false };
|
|
1815
|
+
}
|
|
1816
|
+
const result = await testNotionConnection(settings);
|
|
1817
|
+
return {
|
|
1818
|
+
configured: true,
|
|
1819
|
+
connected: result.success,
|
|
1820
|
+
name: result.name,
|
|
1821
|
+
error: result.success ? undefined : result.error,
|
|
1822
|
+
};
|
|
1823
|
+
});
|
|
1824
|
+
|
|
1825
|
+
// Box Settings handlers
|
|
1826
|
+
ipcMain.handle(IPC_CHANNELS.BOX_GET_SETTINGS, async () => {
|
|
1827
|
+
return BoxSettingsManager.loadSettings();
|
|
1828
|
+
});
|
|
1829
|
+
|
|
1830
|
+
ipcMain.handle(IPC_CHANNELS.BOX_SAVE_SETTINGS, async (_, settings) => {
|
|
1831
|
+
checkRateLimit(IPC_CHANNELS.BOX_SAVE_SETTINGS);
|
|
1832
|
+
const validated = validateInput(BoxSettingsSchema, settings, 'box settings') as BoxSettingsData;
|
|
1833
|
+
BoxSettingsManager.saveSettings(validated);
|
|
1834
|
+
BoxSettingsManager.clearCache();
|
|
1835
|
+
return { success: true };
|
|
1836
|
+
});
|
|
1837
|
+
|
|
1838
|
+
ipcMain.handle(IPC_CHANNELS.BOX_TEST_CONNECTION, async () => {
|
|
1839
|
+
checkRateLimit(IPC_CHANNELS.BOX_TEST_CONNECTION);
|
|
1840
|
+
const settings = BoxSettingsManager.loadSettings();
|
|
1841
|
+
return testBoxConnection(settings);
|
|
1842
|
+
});
|
|
1843
|
+
|
|
1844
|
+
ipcMain.handle(IPC_CHANNELS.BOX_GET_STATUS, async () => {
|
|
1845
|
+
checkRateLimit(IPC_CHANNELS.BOX_GET_STATUS);
|
|
1846
|
+
const settings = BoxSettingsManager.loadSettings();
|
|
1847
|
+
if (!settings.accessToken) {
|
|
1848
|
+
return { configured: false, connected: false };
|
|
1849
|
+
}
|
|
1850
|
+
if (!settings.enabled) {
|
|
1851
|
+
return { configured: true, connected: false };
|
|
1852
|
+
}
|
|
1853
|
+
const result = await testBoxConnection(settings);
|
|
1854
|
+
return {
|
|
1855
|
+
configured: true,
|
|
1856
|
+
connected: result.success,
|
|
1857
|
+
name: result.name,
|
|
1858
|
+
error: result.success ? undefined : result.error,
|
|
1859
|
+
};
|
|
1860
|
+
});
|
|
1861
|
+
|
|
1862
|
+
// OneDrive Settings handlers
|
|
1863
|
+
ipcMain.handle(IPC_CHANNELS.ONEDRIVE_GET_SETTINGS, async () => {
|
|
1864
|
+
return OneDriveSettingsManager.loadSettings();
|
|
1865
|
+
});
|
|
1866
|
+
|
|
1867
|
+
ipcMain.handle(IPC_CHANNELS.ONEDRIVE_SAVE_SETTINGS, async (_, settings) => {
|
|
1868
|
+
checkRateLimit(IPC_CHANNELS.ONEDRIVE_SAVE_SETTINGS);
|
|
1869
|
+
const validated = validateInput(OneDriveSettingsSchema, settings, 'onedrive settings') as OneDriveSettingsData;
|
|
1870
|
+
OneDriveSettingsManager.saveSettings(validated);
|
|
1871
|
+
OneDriveSettingsManager.clearCache();
|
|
1872
|
+
return { success: true };
|
|
1873
|
+
});
|
|
1874
|
+
|
|
1875
|
+
ipcMain.handle(IPC_CHANNELS.ONEDRIVE_TEST_CONNECTION, async () => {
|
|
1876
|
+
checkRateLimit(IPC_CHANNELS.ONEDRIVE_TEST_CONNECTION);
|
|
1877
|
+
const settings = OneDriveSettingsManager.loadSettings();
|
|
1878
|
+
return testOneDriveConnection(settings);
|
|
1879
|
+
});
|
|
1880
|
+
|
|
1881
|
+
ipcMain.handle(IPC_CHANNELS.ONEDRIVE_GET_STATUS, async () => {
|
|
1882
|
+
checkRateLimit(IPC_CHANNELS.ONEDRIVE_GET_STATUS);
|
|
1883
|
+
const settings = OneDriveSettingsManager.loadSettings();
|
|
1884
|
+
if (!settings.accessToken) {
|
|
1885
|
+
return { configured: false, connected: false };
|
|
1886
|
+
}
|
|
1887
|
+
if (!settings.enabled) {
|
|
1888
|
+
return { configured: true, connected: false };
|
|
1889
|
+
}
|
|
1890
|
+
const result = await testOneDriveConnection(settings);
|
|
1891
|
+
return {
|
|
1892
|
+
configured: true,
|
|
1893
|
+
connected: result.success,
|
|
1894
|
+
name: result.name,
|
|
1895
|
+
error: result.success ? undefined : result.error,
|
|
1896
|
+
};
|
|
1897
|
+
});
|
|
1898
|
+
|
|
1899
|
+
// Google Workspace Settings handlers
|
|
1900
|
+
ipcMain.handle(IPC_CHANNELS.GOOGLE_WORKSPACE_GET_SETTINGS, async () => {
|
|
1901
|
+
return GoogleWorkspaceSettingsManager.loadSettings();
|
|
1902
|
+
});
|
|
1903
|
+
|
|
1904
|
+
ipcMain.handle(IPC_CHANNELS.GOOGLE_WORKSPACE_SAVE_SETTINGS, async (_, settings) => {
|
|
1905
|
+
checkRateLimit(IPC_CHANNELS.GOOGLE_WORKSPACE_SAVE_SETTINGS);
|
|
1906
|
+
const validated = validateInput(GoogleWorkspaceSettingsSchema, settings, 'google workspace settings') as GoogleWorkspaceSettingsData;
|
|
1907
|
+
GoogleWorkspaceSettingsManager.saveSettings(validated);
|
|
1908
|
+
GoogleWorkspaceSettingsManager.clearCache();
|
|
1909
|
+
return { success: true };
|
|
1910
|
+
});
|
|
1911
|
+
|
|
1912
|
+
ipcMain.handle(IPC_CHANNELS.GOOGLE_WORKSPACE_TEST_CONNECTION, async () => {
|
|
1913
|
+
checkRateLimit(IPC_CHANNELS.GOOGLE_WORKSPACE_TEST_CONNECTION);
|
|
1914
|
+
const settings = GoogleWorkspaceSettingsManager.loadSettings();
|
|
1915
|
+
return testGoogleWorkspaceConnection(settings);
|
|
1916
|
+
});
|
|
1917
|
+
|
|
1918
|
+
ipcMain.handle(IPC_CHANNELS.GOOGLE_WORKSPACE_GET_STATUS, async () => {
|
|
1919
|
+
checkRateLimit(IPC_CHANNELS.GOOGLE_WORKSPACE_GET_STATUS);
|
|
1920
|
+
const settings = GoogleWorkspaceSettingsManager.loadSettings();
|
|
1921
|
+
if (!settings.accessToken && !settings.refreshToken) {
|
|
1922
|
+
return { configured: false, connected: false };
|
|
1923
|
+
}
|
|
1924
|
+
if (!settings.enabled) {
|
|
1925
|
+
return { configured: true, connected: false };
|
|
1926
|
+
}
|
|
1927
|
+
const result = await testGoogleWorkspaceConnection(settings);
|
|
1928
|
+
return {
|
|
1929
|
+
configured: true,
|
|
1930
|
+
connected: result.success,
|
|
1931
|
+
name: result.name,
|
|
1932
|
+
error: result.success ? undefined : result.error,
|
|
1933
|
+
};
|
|
1934
|
+
});
|
|
1935
|
+
|
|
1936
|
+
ipcMain.handle(IPC_CHANNELS.GOOGLE_WORKSPACE_OAUTH_START, async (_, payload) => {
|
|
1937
|
+
checkRateLimit(IPC_CHANNELS.GOOGLE_WORKSPACE_OAUTH_START);
|
|
1938
|
+
return startGoogleWorkspaceOAuth(payload);
|
|
1939
|
+
});
|
|
1940
|
+
|
|
1941
|
+
// Dropbox Settings handlers
|
|
1942
|
+
ipcMain.handle(IPC_CHANNELS.DROPBOX_GET_SETTINGS, async () => {
|
|
1943
|
+
return DropboxSettingsManager.loadSettings();
|
|
1944
|
+
});
|
|
1945
|
+
|
|
1946
|
+
ipcMain.handle(IPC_CHANNELS.DROPBOX_SAVE_SETTINGS, async (_, settings) => {
|
|
1947
|
+
checkRateLimit(IPC_CHANNELS.DROPBOX_SAVE_SETTINGS);
|
|
1948
|
+
const validated = validateInput(DropboxSettingsSchema, settings, 'dropbox settings') as DropboxSettingsData;
|
|
1949
|
+
DropboxSettingsManager.saveSettings(validated);
|
|
1950
|
+
DropboxSettingsManager.clearCache();
|
|
1951
|
+
return { success: true };
|
|
1952
|
+
});
|
|
1953
|
+
|
|
1954
|
+
ipcMain.handle(IPC_CHANNELS.DROPBOX_TEST_CONNECTION, async () => {
|
|
1955
|
+
checkRateLimit(IPC_CHANNELS.DROPBOX_TEST_CONNECTION);
|
|
1956
|
+
const settings = DropboxSettingsManager.loadSettings();
|
|
1957
|
+
return testDropboxConnection(settings);
|
|
1958
|
+
});
|
|
1959
|
+
|
|
1960
|
+
ipcMain.handle(IPC_CHANNELS.DROPBOX_GET_STATUS, async () => {
|
|
1961
|
+
checkRateLimit(IPC_CHANNELS.DROPBOX_GET_STATUS);
|
|
1962
|
+
const settings = DropboxSettingsManager.loadSettings();
|
|
1963
|
+
if (!settings.accessToken) {
|
|
1964
|
+
return { configured: false, connected: false };
|
|
1965
|
+
}
|
|
1966
|
+
if (!settings.enabled) {
|
|
1967
|
+
return { configured: true, connected: false };
|
|
1968
|
+
}
|
|
1969
|
+
const result = await testDropboxConnection(settings);
|
|
1970
|
+
return {
|
|
1971
|
+
configured: true,
|
|
1972
|
+
connected: result.success,
|
|
1973
|
+
name: result.name,
|
|
1974
|
+
error: result.success ? undefined : result.error,
|
|
1975
|
+
};
|
|
1976
|
+
});
|
|
1977
|
+
|
|
1978
|
+
// SharePoint Settings handlers
|
|
1979
|
+
ipcMain.handle(IPC_CHANNELS.SHAREPOINT_GET_SETTINGS, async () => {
|
|
1980
|
+
return SharePointSettingsManager.loadSettings();
|
|
1981
|
+
});
|
|
1982
|
+
|
|
1983
|
+
ipcMain.handle(IPC_CHANNELS.SHAREPOINT_SAVE_SETTINGS, async (_, settings) => {
|
|
1984
|
+
checkRateLimit(IPC_CHANNELS.SHAREPOINT_SAVE_SETTINGS);
|
|
1985
|
+
const validated = validateInput(SharePointSettingsSchema, settings, 'sharepoint settings') as SharePointSettingsData;
|
|
1986
|
+
SharePointSettingsManager.saveSettings(validated);
|
|
1987
|
+
SharePointSettingsManager.clearCache();
|
|
1988
|
+
return { success: true };
|
|
1989
|
+
});
|
|
1990
|
+
|
|
1991
|
+
ipcMain.handle(IPC_CHANNELS.SHAREPOINT_TEST_CONNECTION, async () => {
|
|
1992
|
+
checkRateLimit(IPC_CHANNELS.SHAREPOINT_TEST_CONNECTION);
|
|
1993
|
+
const settings = SharePointSettingsManager.loadSettings();
|
|
1994
|
+
return testSharePointConnection(settings);
|
|
1995
|
+
});
|
|
1996
|
+
|
|
1997
|
+
ipcMain.handle(IPC_CHANNELS.SHAREPOINT_GET_STATUS, async () => {
|
|
1998
|
+
checkRateLimit(IPC_CHANNELS.SHAREPOINT_GET_STATUS);
|
|
1999
|
+
const settings = SharePointSettingsManager.loadSettings();
|
|
2000
|
+
if (!settings.accessToken) {
|
|
2001
|
+
return { configured: false, connected: false };
|
|
2002
|
+
}
|
|
2003
|
+
if (!settings.enabled) {
|
|
2004
|
+
return { configured: true, connected: false };
|
|
2005
|
+
}
|
|
2006
|
+
const result = await testSharePointConnection(settings);
|
|
2007
|
+
return {
|
|
2008
|
+
configured: true,
|
|
2009
|
+
connected: result.success,
|
|
2010
|
+
name: result.name,
|
|
2011
|
+
error: result.success ? undefined : result.error,
|
|
2012
|
+
};
|
|
2013
|
+
});
|
|
2014
|
+
|
|
1467
2015
|
// Gateway / Channel handlers
|
|
1468
2016
|
ipcMain.handle(IPC_CHANNELS.GATEWAY_GET_CHANNELS, async () => {
|
|
1469
2017
|
if (!gateway) return [];
|
|
@@ -2426,6 +2974,7 @@ function setupMCPHandlers(): void {
|
|
|
2426
2974
|
rateLimiter.configure(IPC_CHANNELS.MCP_CONNECT_SERVER, RATE_LIMIT_CONFIGS.expensive);
|
|
2427
2975
|
rateLimiter.configure(IPC_CHANNELS.MCP_TEST_SERVER, RATE_LIMIT_CONFIGS.expensive);
|
|
2428
2976
|
rateLimiter.configure(IPC_CHANNELS.MCP_REGISTRY_INSTALL, RATE_LIMIT_CONFIGS.expensive);
|
|
2977
|
+
rateLimiter.configure(IPC_CHANNELS.MCP_CONNECTOR_OAUTH_START, RATE_LIMIT_CONFIGS.expensive);
|
|
2429
2978
|
|
|
2430
2979
|
// Initialize MCP settings manager
|
|
2431
2980
|
MCPSettingsManager.initialize();
|
|
@@ -2553,6 +3102,13 @@ function setupMCPHandlers(): void {
|
|
|
2553
3102
|
return MCPRegistryManager.updateServer(validatedId);
|
|
2554
3103
|
});
|
|
2555
3104
|
|
|
3105
|
+
// MCP Connector OAuth (Salesforce/Jira)
|
|
3106
|
+
ipcMain.handle(IPC_CHANNELS.MCP_CONNECTOR_OAUTH_START, async (_, payload) => {
|
|
3107
|
+
checkRateLimit(IPC_CHANNELS.MCP_CONNECTOR_OAUTH_START);
|
|
3108
|
+
const validated = validateInput(MCPConnectorOAuthSchema, payload, 'connector oauth');
|
|
3109
|
+
return startConnectorOAuth(validated);
|
|
3110
|
+
});
|
|
3111
|
+
|
|
2556
3112
|
// MCP Host handlers
|
|
2557
3113
|
ipcMain.handle(IPC_CHANNELS.MCP_HOST_START, async () => {
|
|
2558
3114
|
const hostServer = MCPHostServer.getInstance();
|