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
|
@@ -94,18 +94,36 @@ export class SearchTools {
|
|
|
94
94
|
});
|
|
95
95
|
|
|
96
96
|
// Use searchWithFallback for automatic fallback support
|
|
97
|
-
|
|
97
|
+
try {
|
|
98
|
+
const response = await SearchProviderFactory.searchWithFallback(searchQuery);
|
|
98
99
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
100
|
+
this.daemon.logEvent(this.taskId, 'tool_result', {
|
|
101
|
+
tool: 'web_search',
|
|
102
|
+
result: {
|
|
103
|
+
query: input.query,
|
|
104
|
+
searchType: searchQuery.searchType,
|
|
105
|
+
resultCount: response.results.length,
|
|
106
|
+
provider: response.provider,
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
return response;
|
|
111
|
+
} catch (error: any) {
|
|
112
|
+
const message = error?.message || 'Web search failed';
|
|
113
|
+
this.daemon.logEvent(this.taskId, 'tool_result', {
|
|
114
|
+
tool: 'web_search',
|
|
115
|
+
error: message,
|
|
116
|
+
});
|
|
108
117
|
|
|
109
|
-
|
|
118
|
+
return {
|
|
119
|
+
query: input.query,
|
|
120
|
+
searchType: input.searchType || 'web',
|
|
121
|
+
results: [],
|
|
122
|
+
provider: (input.provider || settings.primaryProvider || 'none') as SearchProviderType | 'none',
|
|
123
|
+
metadata: {
|
|
124
|
+
error: message,
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
110
128
|
}
|
|
111
129
|
}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { Workspace } from '../../../shared/types';
|
|
4
|
+
import { AgentDaemon } from '../daemon';
|
|
5
|
+
import { SharePointSettingsManager } from '../../settings/sharepoint-manager';
|
|
6
|
+
import { sharepointRequest } from '../../utils/sharepoint-api';
|
|
7
|
+
|
|
8
|
+
type SharePointAction =
|
|
9
|
+
| 'get_current_user'
|
|
10
|
+
| 'search_sites'
|
|
11
|
+
| 'get_site'
|
|
12
|
+
| 'list_site_drives'
|
|
13
|
+
| 'list_drive_items'
|
|
14
|
+
| 'get_item'
|
|
15
|
+
| 'create_folder'
|
|
16
|
+
| 'upload_file'
|
|
17
|
+
| 'delete_item';
|
|
18
|
+
|
|
19
|
+
interface SharePointActionInput {
|
|
20
|
+
action: SharePointAction;
|
|
21
|
+
site_id?: string;
|
|
22
|
+
drive_id?: string;
|
|
23
|
+
item_id?: string;
|
|
24
|
+
query?: string;
|
|
25
|
+
parent_id?: string;
|
|
26
|
+
name?: string;
|
|
27
|
+
conflict_behavior?: 'rename' | 'fail' | 'replace';
|
|
28
|
+
file_path?: string;
|
|
29
|
+
remote_path?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class SharePointTools {
|
|
33
|
+
constructor(
|
|
34
|
+
private workspace: Workspace,
|
|
35
|
+
private daemon: AgentDaemon,
|
|
36
|
+
private taskId: string
|
|
37
|
+
) {}
|
|
38
|
+
|
|
39
|
+
setWorkspace(workspace: Workspace): void {
|
|
40
|
+
this.workspace = workspace;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static isEnabled(): boolean {
|
|
44
|
+
return SharePointSettingsManager.loadSettings().enabled;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private async requireApproval(summary: string, details: Record<string, unknown>): Promise<void> {
|
|
48
|
+
const approved = await this.daemon.requestApproval(
|
|
49
|
+
this.taskId,
|
|
50
|
+
'external_service',
|
|
51
|
+
summary,
|
|
52
|
+
details
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
if (!approved) {
|
|
56
|
+
throw new Error('User denied SharePoint action');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private resolveFilePath(inputPath: string): string {
|
|
61
|
+
if (!this.workspace.permissions.read) {
|
|
62
|
+
throw new Error('Read permission not granted for uploads');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const workspaceRoot = path.resolve(this.workspace.path);
|
|
66
|
+
const allowedPaths = this.workspace.permissions.allowedPaths || [];
|
|
67
|
+
const canReadOutside = this.workspace.isTemp || this.workspace.permissions.unrestrictedFileAccess;
|
|
68
|
+
|
|
69
|
+
const isPathAllowed = (absolutePath: string): boolean => {
|
|
70
|
+
if (allowedPaths.length === 0) return false;
|
|
71
|
+
const normalizedPath = path.normalize(absolutePath);
|
|
72
|
+
return allowedPaths.some((allowed) => {
|
|
73
|
+
const normalizedAllowed = path.normalize(allowed);
|
|
74
|
+
return normalizedPath === normalizedAllowed || normalizedPath.startsWith(normalizedAllowed + path.sep);
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const candidate = path.isAbsolute(inputPath)
|
|
79
|
+
? path.normalize(inputPath)
|
|
80
|
+
: path.resolve(workspaceRoot, inputPath);
|
|
81
|
+
|
|
82
|
+
const relative = path.relative(workspaceRoot, candidate);
|
|
83
|
+
const isInsideWorkspace = !(relative.startsWith('..') || path.isAbsolute(relative));
|
|
84
|
+
if (!isInsideWorkspace && !canReadOutside && !isPathAllowed(candidate)) {
|
|
85
|
+
throw new Error('File path must be inside the workspace or in Allowed Paths');
|
|
86
|
+
}
|
|
87
|
+
if (!fs.existsSync(candidate)) {
|
|
88
|
+
throw new Error(`File not found: ${inputPath}`);
|
|
89
|
+
}
|
|
90
|
+
const stats = fs.statSync(candidate);
|
|
91
|
+
if (!stats.isFile()) {
|
|
92
|
+
throw new Error(`Path is not a file: ${inputPath}`);
|
|
93
|
+
}
|
|
94
|
+
return candidate;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private getSiteId(inputSiteId?: string): string {
|
|
98
|
+
const settings = SharePointSettingsManager.loadSettings();
|
|
99
|
+
const siteId = inputSiteId || settings.siteId;
|
|
100
|
+
if (!siteId) {
|
|
101
|
+
throw new Error('Missing site_id. Provide it in settings or the tool input.');
|
|
102
|
+
}
|
|
103
|
+
return siteId;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private getDriveId(inputDriveId?: string): string {
|
|
107
|
+
const settings = SharePointSettingsManager.loadSettings();
|
|
108
|
+
const driveId = inputDriveId || settings.driveId;
|
|
109
|
+
if (!driveId) {
|
|
110
|
+
throw new Error('Missing drive_id. Provide it in settings or the tool input.');
|
|
111
|
+
}
|
|
112
|
+
return driveId;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async executeAction(input: SharePointActionInput): Promise<any> {
|
|
116
|
+
const settings = SharePointSettingsManager.loadSettings();
|
|
117
|
+
if (!settings.enabled) {
|
|
118
|
+
throw new Error('SharePoint integration is disabled. Enable it in Settings > Integrations > SharePoint.');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const action = input.action;
|
|
122
|
+
if (!action) {
|
|
123
|
+
throw new Error('Missing required "action" parameter');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
let result;
|
|
127
|
+
|
|
128
|
+
switch (action) {
|
|
129
|
+
case 'get_current_user': {
|
|
130
|
+
result = await sharepointRequest(settings, { method: 'GET', path: '/me' });
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
case 'search_sites': {
|
|
134
|
+
if (!input.query) throw new Error('Missing query for search_sites');
|
|
135
|
+
result = await sharepointRequest(settings, {
|
|
136
|
+
method: 'GET',
|
|
137
|
+
path: '/sites',
|
|
138
|
+
query: { search: input.query },
|
|
139
|
+
});
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
case 'get_site': {
|
|
143
|
+
const siteId = this.getSiteId(input.site_id);
|
|
144
|
+
result = await sharepointRequest(settings, { method: 'GET', path: `/sites/${siteId}` });
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
case 'list_site_drives': {
|
|
148
|
+
const siteId = this.getSiteId(input.site_id);
|
|
149
|
+
result = await sharepointRequest(settings, { method: 'GET', path: `/sites/${siteId}/drives` });
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
case 'list_drive_items': {
|
|
153
|
+
const driveId = this.getDriveId(input.drive_id);
|
|
154
|
+
const pathSuffix = input.item_id ? `/items/${input.item_id}/children` : '/root/children';
|
|
155
|
+
result = await sharepointRequest(settings, { method: 'GET', path: `/drives/${driveId}${pathSuffix}` });
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
case 'get_item': {
|
|
159
|
+
if (!input.item_id) throw new Error('Missing item_id for get_item');
|
|
160
|
+
const driveId = this.getDriveId(input.drive_id);
|
|
161
|
+
result = await sharepointRequest(settings, { method: 'GET', path: `/drives/${driveId}/items/${input.item_id}` });
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
case 'create_folder': {
|
|
165
|
+
if (!input.name) throw new Error('Missing name for create_folder');
|
|
166
|
+
const driveId = this.getDriveId(input.drive_id);
|
|
167
|
+
const parentPath = input.parent_id
|
|
168
|
+
? `/items/${input.parent_id}/children`
|
|
169
|
+
: '/root/children';
|
|
170
|
+
await this.requireApproval('Create a SharePoint folder', {
|
|
171
|
+
action: 'create_folder',
|
|
172
|
+
parent_id: input.parent_id || 'root',
|
|
173
|
+
name: input.name,
|
|
174
|
+
});
|
|
175
|
+
result = await sharepointRequest(settings, {
|
|
176
|
+
method: 'POST',
|
|
177
|
+
path: `/drives/${driveId}${parentPath}`,
|
|
178
|
+
body: {
|
|
179
|
+
name: input.name,
|
|
180
|
+
folder: {},
|
|
181
|
+
'@microsoft.graph.conflictBehavior': input.conflict_behavior || 'rename',
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
case 'upload_file': {
|
|
187
|
+
if (!input.file_path) throw new Error('Missing file_path for upload_file');
|
|
188
|
+
const driveId = this.getDriveId(input.drive_id);
|
|
189
|
+
const resolved = this.resolveFilePath(input.file_path);
|
|
190
|
+
const data = fs.readFileSync(resolved);
|
|
191
|
+
const fileName = input.name || path.basename(resolved);
|
|
192
|
+
let uploadPath: string;
|
|
193
|
+
if (input.remote_path) {
|
|
194
|
+
const cleaned = input.remote_path.replace(/^\/+/, '');
|
|
195
|
+
const encoded = cleaned
|
|
196
|
+
.split('/')
|
|
197
|
+
.map(segment => encodeURIComponent(segment))
|
|
198
|
+
.join('/');
|
|
199
|
+
uploadPath = `/drives/${driveId}/root:/${encoded}:/content`;
|
|
200
|
+
} else if (input.parent_id) {
|
|
201
|
+
uploadPath = `/drives/${driveId}/items/${input.parent_id}:/${encodeURIComponent(fileName)}:/content`;
|
|
202
|
+
} else {
|
|
203
|
+
uploadPath = `/drives/${driveId}/root:/${encodeURIComponent(fileName)}:/content`;
|
|
204
|
+
}
|
|
205
|
+
await this.requireApproval(`Upload file to SharePoint: ${fileName}`, {
|
|
206
|
+
action: 'upload_file',
|
|
207
|
+
destination: input.remote_path || input.parent_id || 'root',
|
|
208
|
+
file: fileName,
|
|
209
|
+
});
|
|
210
|
+
result = await sharepointRequest(settings, {
|
|
211
|
+
method: 'PUT',
|
|
212
|
+
path: uploadPath,
|
|
213
|
+
body: data,
|
|
214
|
+
headers: { 'Content-Type': 'application/octet-stream' },
|
|
215
|
+
});
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
case 'delete_item': {
|
|
219
|
+
if (!input.item_id) throw new Error('Missing item_id for delete_item');
|
|
220
|
+
const driveId = this.getDriveId(input.drive_id);
|
|
221
|
+
await this.requireApproval('Delete a SharePoint item', {
|
|
222
|
+
action: 'delete_item',
|
|
223
|
+
item_id: input.item_id,
|
|
224
|
+
});
|
|
225
|
+
result = await sharepointRequest(settings, { method: 'DELETE', path: `/drives/${driveId}/items/${input.item_id}` });
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
default:
|
|
229
|
+
throw new Error(`Unsupported action: ${action}`);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
this.daemon.logEvent(this.taskId, 'tool_result', {
|
|
233
|
+
tool: 'sharepoint_action',
|
|
234
|
+
action,
|
|
235
|
+
status: result?.status,
|
|
236
|
+
hasData: result?.data ? true : false,
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
success: true,
|
|
241
|
+
action,
|
|
242
|
+
status: result?.status,
|
|
243
|
+
data: result?.data,
|
|
244
|
+
raw: result?.raw,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
}
|
|
@@ -2,6 +2,7 @@ import { spawn, ChildProcess, execSync } from 'child_process';
|
|
|
2
2
|
import { Workspace, CommandTerminationReason } from '../../../shared/types';
|
|
3
3
|
import { AgentDaemon } from '../daemon';
|
|
4
4
|
import { GuardrailManager } from '../../guardrails/guardrail-manager';
|
|
5
|
+
import { BuiltinToolsSettingsManager } from './builtin-settings';
|
|
5
6
|
|
|
6
7
|
// Limits to prevent runaway commands
|
|
7
8
|
const MAX_TIMEOUT = 5 * 60 * 1000; // 5 minutes max
|
|
@@ -345,9 +346,9 @@ export class ShellTools {
|
|
|
345
346
|
}
|
|
346
347
|
|
|
347
348
|
/**
|
|
348
|
-
* Execute a shell command (requires user approval)
|
|
349
|
+
* Execute a shell command (requires user approval unless auto-approve is enabled)
|
|
349
350
|
* Note: We don't check workspace.permissions.shell here because
|
|
350
|
-
* shell commands
|
|
351
|
+
* shell commands are gated by approval flow (or auto-approve/trust settings)
|
|
351
352
|
*/
|
|
352
353
|
async runCommand(
|
|
353
354
|
command: string,
|
|
@@ -376,9 +377,16 @@ export class ShellTools {
|
|
|
376
377
|
|
|
377
378
|
// Check if command is trusted (auto-approve without user confirmation)
|
|
378
379
|
const trustCheck = GuardrailManager.isCommandTrusted(command);
|
|
380
|
+
const autoApproveEnabled = BuiltinToolsSettingsManager.getToolAutoApprove('run_command');
|
|
379
381
|
let approved = false;
|
|
380
382
|
|
|
381
|
-
if (
|
|
383
|
+
if (autoApproveEnabled && this.isAutoApprovalSafe(command)) {
|
|
384
|
+
approved = true;
|
|
385
|
+
this.daemon.logEvent(this.taskId, 'log', {
|
|
386
|
+
message: 'Auto-approved command (user setting enabled)',
|
|
387
|
+
command,
|
|
388
|
+
});
|
|
389
|
+
} else if (trustCheck.trusted) {
|
|
382
390
|
// Auto-approve trusted commands
|
|
383
391
|
approved = true;
|
|
384
392
|
this.daemon.logEvent(this.taskId, 'log', {
|
|
@@ -119,7 +119,7 @@ export class XTools {
|
|
|
119
119
|
async executeAction(input: XActionInput): Promise<any> {
|
|
120
120
|
const settings = XSettingsManager.loadSettings();
|
|
121
121
|
if (!settings.enabled) {
|
|
122
|
-
throw new Error('X integration is disabled. Enable it in Settings >
|
|
122
|
+
throw new Error('X integration is disabled. Enable it in Settings > X (Twitter).');
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
const action = input.action;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export type DispatchRole = {
|
|
2
|
+
displayName: string;
|
|
3
|
+
description?: string | null;
|
|
4
|
+
capabilities?: string[];
|
|
5
|
+
systemPrompt?: string | null;
|
|
6
|
+
soul?: string | null;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export type DispatchParentTask = {
|
|
10
|
+
title: string;
|
|
11
|
+
prompt: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type DispatchPromptOptions = {
|
|
15
|
+
planSummary?: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const buildSoulSummary = (soul?: string): string | null => {
|
|
19
|
+
if (!soul) return null;
|
|
20
|
+
try {
|
|
21
|
+
const parsed = JSON.parse(soul) as Record<string, unknown>;
|
|
22
|
+
const parts: string[] = [];
|
|
23
|
+
if (typeof parsed.name === 'string') parts.push(`Name: ${parsed.name}`);
|
|
24
|
+
if (typeof parsed.role === 'string') parts.push(`Role: ${parsed.role}`);
|
|
25
|
+
if (typeof parsed.personality === 'string') parts.push(`Personality: ${parsed.personality}`);
|
|
26
|
+
if (typeof parsed.communicationStyle === 'string') parts.push(`Style: ${parsed.communicationStyle}`);
|
|
27
|
+
if (Array.isArray(parsed.focusAreas)) parts.push(`Focus: ${parsed.focusAreas.join(', ')}`);
|
|
28
|
+
if (Array.isArray(parsed.strengths)) parts.push(`Strengths: ${parsed.strengths.join(', ')}`);
|
|
29
|
+
if (parts.length === 0) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
return parts.join('\n');
|
|
33
|
+
} catch {
|
|
34
|
+
return soul;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const buildAgentDispatchPrompt = (
|
|
39
|
+
role: DispatchRole,
|
|
40
|
+
parentTask: DispatchParentTask,
|
|
41
|
+
options?: DispatchPromptOptions
|
|
42
|
+
): string => {
|
|
43
|
+
const lines: string[] = [
|
|
44
|
+
`You are ${role.displayName}${role.description ? ` — ${role.description}` : ''}.`,
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
if (role.capabilities && role.capabilities.length > 0) {
|
|
48
|
+
lines.push(`Capabilities: ${role.capabilities.join(', ')}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (role.systemPrompt) {
|
|
52
|
+
lines.push('System guidance:');
|
|
53
|
+
lines.push(role.systemPrompt);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const soulSummary = buildSoulSummary(role.soul || undefined);
|
|
57
|
+
if (soulSummary) {
|
|
58
|
+
lines.push('Role notes:');
|
|
59
|
+
lines.push(soulSummary);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (options?.planSummary) {
|
|
63
|
+
lines.push('');
|
|
64
|
+
lines.push('Main agent plan summary (context only):');
|
|
65
|
+
lines.push(options.planSummary);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
lines.push('');
|
|
69
|
+
lines.push(`Parent task: ${parentTask.title}`);
|
|
70
|
+
lines.push('Request:');
|
|
71
|
+
lines.push(parentTask.prompt);
|
|
72
|
+
lines.push('');
|
|
73
|
+
lines.push('Deliverables:');
|
|
74
|
+
lines.push('- Provide a concise summary of your findings.');
|
|
75
|
+
lines.push('- Call out risks or open questions.');
|
|
76
|
+
lines.push('- Recommend next steps.');
|
|
77
|
+
|
|
78
|
+
return lines.join('\n');
|
|
79
|
+
};
|
|
@@ -27,17 +27,19 @@ export class WorkspaceRepository {
|
|
|
27
27
|
constructor(private db: Database.Database) {}
|
|
28
28
|
|
|
29
29
|
create(name: string, path: string, permissions: WorkspacePermissions): Workspace {
|
|
30
|
+
const now = Date.now();
|
|
30
31
|
const workspace: Workspace = {
|
|
31
32
|
id: uuidv4(),
|
|
32
33
|
name,
|
|
33
34
|
path,
|
|
34
|
-
createdAt:
|
|
35
|
+
createdAt: now,
|
|
36
|
+
lastUsedAt: now,
|
|
35
37
|
permissions,
|
|
36
38
|
};
|
|
37
39
|
|
|
38
40
|
const stmt = this.db.prepare(`
|
|
39
|
-
INSERT INTO workspaces (id, name, path, created_at, permissions)
|
|
40
|
-
VALUES (?, ?, ?, ?, ?)
|
|
41
|
+
INSERT INTO workspaces (id, name, path, created_at, last_used_at, permissions)
|
|
42
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
41
43
|
`);
|
|
42
44
|
|
|
43
45
|
stmt.run(
|
|
@@ -45,6 +47,7 @@ export class WorkspaceRepository {
|
|
|
45
47
|
workspace.name,
|
|
46
48
|
workspace.path,
|
|
47
49
|
workspace.createdAt,
|
|
50
|
+
workspace.lastUsedAt,
|
|
48
51
|
JSON.stringify(workspace.permissions)
|
|
49
52
|
);
|
|
50
53
|
|
|
@@ -58,7 +61,11 @@ export class WorkspaceRepository {
|
|
|
58
61
|
}
|
|
59
62
|
|
|
60
63
|
findAll(): Workspace[] {
|
|
61
|
-
const stmt = this.db.prepare(
|
|
64
|
+
const stmt = this.db.prepare(`
|
|
65
|
+
SELECT *
|
|
66
|
+
FROM workspaces
|
|
67
|
+
ORDER BY COALESCE(last_used_at, created_at) DESC
|
|
68
|
+
`);
|
|
62
69
|
const rows = stmt.all() as any[];
|
|
63
70
|
return rows.map(row => this.mapRowToWorkspace(row));
|
|
64
71
|
}
|
|
@@ -89,6 +96,14 @@ export class WorkspaceRepository {
|
|
|
89
96
|
stmt.run(JSON.stringify(permissions), id);
|
|
90
97
|
}
|
|
91
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Update last used timestamp for recency ordering
|
|
101
|
+
*/
|
|
102
|
+
updateLastUsedAt(id: string, lastUsedAt: number = Date.now()): void {
|
|
103
|
+
const stmt = this.db.prepare('UPDATE workspaces SET last_used_at = ? WHERE id = ?');
|
|
104
|
+
stmt.run(lastUsedAt, id);
|
|
105
|
+
}
|
|
106
|
+
|
|
92
107
|
/**
|
|
93
108
|
* Delete a workspace by ID
|
|
94
109
|
*/
|
|
@@ -120,6 +135,7 @@ export class WorkspaceRepository {
|
|
|
120
135
|
name: row.name,
|
|
121
136
|
path: row.path,
|
|
122
137
|
createdAt: row.created_at,
|
|
138
|
+
lastUsedAt: row.last_used_at ?? undefined,
|
|
123
139
|
permissions: mergedPermissions,
|
|
124
140
|
};
|
|
125
141
|
}
|
|
@@ -1054,13 +1070,49 @@ export class ChannelUserRepository {
|
|
|
1054
1070
|
WHERE channel_id = ?
|
|
1055
1071
|
AND allowed = 0
|
|
1056
1072
|
AND channel_user_id LIKE 'pending_%'
|
|
1057
|
-
AND
|
|
1058
|
-
|
|
1073
|
+
AND (
|
|
1074
|
+
pairing_expires_at IS NULL
|
|
1075
|
+
OR pairing_code IS NULL
|
|
1076
|
+
OR pairing_expires_at < ?
|
|
1077
|
+
)
|
|
1059
1078
|
`);
|
|
1060
1079
|
const result = stmt.run(channelId, now);
|
|
1061
1080
|
return result.changes;
|
|
1062
1081
|
}
|
|
1063
1082
|
|
|
1083
|
+
/**
|
|
1084
|
+
* Delete all pending pairing entries for a channel (valid or expired).
|
|
1085
|
+
*/
|
|
1086
|
+
deletePendingByChannel(channelId: string): number {
|
|
1087
|
+
const stmt = this.db.prepare(`
|
|
1088
|
+
DELETE FROM channel_users
|
|
1089
|
+
WHERE channel_id = ?
|
|
1090
|
+
AND allowed = 0
|
|
1091
|
+
AND channel_user_id LIKE 'pending_%'
|
|
1092
|
+
`);
|
|
1093
|
+
const result = stmt.run(channelId);
|
|
1094
|
+
return result.changes;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
/**
|
|
1098
|
+
* Delete expired or empty pending pairing entries across all channels.
|
|
1099
|
+
*/
|
|
1100
|
+
deleteExpiredPendingAll(): number {
|
|
1101
|
+
const now = Date.now();
|
|
1102
|
+
const stmt = this.db.prepare(`
|
|
1103
|
+
DELETE FROM channel_users
|
|
1104
|
+
WHERE allowed = 0
|
|
1105
|
+
AND channel_user_id LIKE 'pending_%'
|
|
1106
|
+
AND (
|
|
1107
|
+
pairing_expires_at IS NULL
|
|
1108
|
+
OR pairing_code IS NULL
|
|
1109
|
+
OR pairing_expires_at < ?
|
|
1110
|
+
)
|
|
1111
|
+
`);
|
|
1112
|
+
const result = stmt.run(now);
|
|
1113
|
+
return result.changes;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1064
1116
|
findByPairingCode(channelId: string, pairingCode: string): ChannelUser | undefined {
|
|
1065
1117
|
const stmt = this.db.prepare('SELECT * FROM channel_users WHERE channel_id = ? AND UPPER(pairing_code) = UPPER(?)');
|
|
1066
1118
|
const row = stmt.get(channelId, pairingCode) as Record<string, unknown> | undefined;
|
|
@@ -234,6 +234,7 @@ export class DatabaseManager {
|
|
|
234
234
|
name TEXT NOT NULL,
|
|
235
235
|
path TEXT NOT NULL UNIQUE,
|
|
236
236
|
created_at INTEGER NOT NULL,
|
|
237
|
+
last_used_at INTEGER,
|
|
237
238
|
permissions TEXT NOT NULL
|
|
238
239
|
);
|
|
239
240
|
|
|
@@ -592,6 +593,13 @@ export class DatabaseManager {
|
|
|
592
593
|
}
|
|
593
594
|
}
|
|
594
595
|
|
|
596
|
+
// Migration: Add last_used_at to workspaces for recency ordering
|
|
597
|
+
try {
|
|
598
|
+
this.db.exec('ALTER TABLE workspaces ADD COLUMN last_used_at INTEGER');
|
|
599
|
+
} catch {
|
|
600
|
+
// Column already exists, ignore
|
|
601
|
+
}
|
|
602
|
+
|
|
595
603
|
// Migration: Add Sub-Agent / Parallel Agent columns to tasks table
|
|
596
604
|
const subAgentColumns = [
|
|
597
605
|
'ALTER TABLE tasks ADD COLUMN parent_task_id TEXT REFERENCES tasks(id)',
|
|
@@ -1014,6 +1014,7 @@ export class DiscordAdapter implements ChannelAdapter {
|
|
|
1014
1014
|
// Check for thread context
|
|
1015
1015
|
const isThread = message.channel.isThread();
|
|
1016
1016
|
const threadId = isThread ? message.channelId : undefined;
|
|
1017
|
+
const isGroup = message.channel.type !== DiscordChannelType.DM;
|
|
1017
1018
|
|
|
1018
1019
|
return {
|
|
1019
1020
|
messageId: message.id,
|
|
@@ -1021,6 +1022,7 @@ export class DiscordAdapter implements ChannelAdapter {
|
|
|
1021
1022
|
userId: message.author.id,
|
|
1022
1023
|
userName: message.author.displayName || message.author.username,
|
|
1023
1024
|
chatId: isThread ? (message.channel as ThreadChannel).parentId! : message.channelId,
|
|
1025
|
+
isGroup,
|
|
1024
1026
|
text: commandText || text,
|
|
1025
1027
|
timestamp: message.createdAt,
|
|
1026
1028
|
replyTo: message.reference?.messageId,
|
|
@@ -1073,6 +1075,7 @@ export class DiscordAdapter implements ChannelAdapter {
|
|
|
1073
1075
|
|
|
1074
1076
|
// Check for thread context
|
|
1075
1077
|
const isThread = interaction.channel?.isThread() ?? false;
|
|
1078
|
+
const isGroup = Boolean(interaction.guildId);
|
|
1076
1079
|
|
|
1077
1080
|
return {
|
|
1078
1081
|
messageId: interaction.id,
|
|
@@ -1080,6 +1083,7 @@ export class DiscordAdapter implements ChannelAdapter {
|
|
|
1080
1083
|
userId: interaction.user.id,
|
|
1081
1084
|
userName: interaction.user.displayName || interaction.user.username,
|
|
1082
1085
|
chatId: interaction.channelId!,
|
|
1086
|
+
isGroup,
|
|
1083
1087
|
text,
|
|
1084
1088
|
timestamp: new Date(interaction.createdTimestamp),
|
|
1085
1089
|
threadId: isThread ? interaction.channelId! : undefined,
|
|
@@ -464,6 +464,8 @@ export class GoogleChatAdapter implements ChannelAdapter {
|
|
|
464
464
|
const userName = message.sender?.displayName || 'Unknown User';
|
|
465
465
|
const userId = message.sender?.name || '';
|
|
466
466
|
|
|
467
|
+
const isGroup = message.space.type !== 'DM';
|
|
468
|
+
|
|
467
469
|
// Map to IncomingMessage format
|
|
468
470
|
const incomingMessage: IncomingMessage = {
|
|
469
471
|
messageId: messageId,
|
|
@@ -471,6 +473,7 @@ export class GoogleChatAdapter implements ChannelAdapter {
|
|
|
471
473
|
userId: userId,
|
|
472
474
|
userName: userName,
|
|
473
475
|
chatId: spaceId,
|
|
476
|
+
isGroup,
|
|
474
477
|
text: text,
|
|
475
478
|
timestamp: message.createTime ? new Date(message.createTime) : new Date(),
|
|
476
479
|
threadId: message.thread?.name,
|
|
@@ -451,6 +451,8 @@ export class LineAdapter implements ChannelAdapter {
|
|
|
451
451
|
});
|
|
452
452
|
}
|
|
453
453
|
|
|
454
|
+
const isGroup = lineMessage.source.type !== 'user';
|
|
455
|
+
|
|
454
456
|
// Convert to IncomingMessage
|
|
455
457
|
const message: IncomingMessage = {
|
|
456
458
|
messageId: lineMessage.id,
|
|
@@ -458,6 +460,7 @@ export class LineAdapter implements ChannelAdapter {
|
|
|
458
460
|
userId: lineMessage.source.userId || '',
|
|
459
461
|
userName,
|
|
460
462
|
chatId,
|
|
463
|
+
isGroup,
|
|
461
464
|
text: lineMessage.text,
|
|
462
465
|
timestamp: lineMessage.timestamp,
|
|
463
466
|
raw: lineMessage,
|
|
@@ -191,6 +191,21 @@ export class MatrixClient extends EventEmitter {
|
|
|
191
191
|
return response.joined_rooms;
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
+
/**
|
|
195
|
+
* Get direct (1:1) room IDs from account data
|
|
196
|
+
*/
|
|
197
|
+
async getDirectRooms(): Promise<string[]> {
|
|
198
|
+
try {
|
|
199
|
+
const response = await this.apiRequest<Record<string, string[]>>(
|
|
200
|
+
'GET',
|
|
201
|
+
`/_matrix/client/v3/user/${encodeURIComponent(this.options.userId)}/account_data/m.direct`
|
|
202
|
+
);
|
|
203
|
+
return Object.values(response || {}).flat().filter(Boolean);
|
|
204
|
+
} catch {
|
|
205
|
+
return [];
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
194
209
|
/**
|
|
195
210
|
* Start syncing (receiving messages)
|
|
196
211
|
*/
|