@chaaskit/server 0.1.0
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/dist/api/admin.js +438 -0
- package/dist/api/admin.js.map +1 -0
- package/dist/api/agents.js +21 -0
- package/dist/api/agents.js.map +1 -0
- package/dist/api/api-keys.js +122 -0
- package/dist/api/api-keys.js.map +1 -0
- package/dist/api/auth.js +399 -0
- package/dist/api/auth.js.map +1 -0
- package/dist/api/chat.js +900 -0
- package/dist/api/chat.js.map +1 -0
- package/dist/api/config.js +91 -0
- package/dist/api/config.js.map +1 -0
- package/dist/api/documents.js +237 -0
- package/dist/api/documents.js.map +1 -0
- package/dist/api/export.js +107 -0
- package/dist/api/export.js.map +1 -0
- package/dist/api/health.js +25 -0
- package/dist/api/health.js.map +1 -0
- package/dist/api/mcp-server.js +84 -0
- package/dist/api/mcp-server.js.map +1 -0
- package/dist/api/mcp.js +400 -0
- package/dist/api/mcp.js.map +1 -0
- package/dist/api/mentions.js +94 -0
- package/dist/api/mentions.js.map +1 -0
- package/dist/api/oauth.js +366 -0
- package/dist/api/oauth.js.map +1 -0
- package/dist/api/payments.js +473 -0
- package/dist/api/payments.js.map +1 -0
- package/dist/api/projects.js +301 -0
- package/dist/api/projects.js.map +1 -0
- package/dist/api/scheduled-prompts.js +617 -0
- package/dist/api/scheduled-prompts.js.map +1 -0
- package/dist/api/search.js +85 -0
- package/dist/api/search.js.map +1 -0
- package/dist/api/share.js +188 -0
- package/dist/api/share.js.map +1 -0
- package/dist/api/slack.js +468 -0
- package/dist/api/slack.js.map +1 -0
- package/dist/api/teams.js +693 -0
- package/dist/api/teams.js.map +1 -0
- package/dist/api/templates.js +134 -0
- package/dist/api/templates.js.map +1 -0
- package/dist/api/threads.js +323 -0
- package/dist/api/threads.js.map +1 -0
- package/dist/api/upload.js +57 -0
- package/dist/api/upload.js.map +1 -0
- package/dist/api/user.js +111 -0
- package/dist/api/user.js.map +1 -0
- package/dist/api/v1/openai.js +245 -0
- package/dist/api/v1/openai.js.map +1 -0
- package/dist/app.js +168 -0
- package/dist/app.js.map +1 -0
- package/dist/bin/cli.js +57 -0
- package/dist/bin/cli.js.map +1 -0
- package/dist/commands/db-sync.js +108 -0
- package/dist/commands/db-sync.js.map +1 -0
- package/dist/config/loader.js +374 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/documents/extractors.js +136 -0
- package/dist/documents/extractors.js.map +1 -0
- package/dist/extensions/glob.js +53 -0
- package/dist/extensions/glob.js.map +1 -0
- package/dist/extensions/loader.js +72 -0
- package/dist/extensions/loader.js.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/loaders/index.js +75 -0
- package/dist/loaders/index.js.map +1 -0
- package/dist/mcp/client.js +551 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/server.js +335 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/middleware/apiKeyAuth.js +136 -0
- package/dist/middleware/apiKeyAuth.js.map +1 -0
- package/dist/middleware/auth.js +192 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/middleware/errorHandler.js +41 -0
- package/dist/middleware/errorHandler.js.map +1 -0
- package/dist/middleware/mcpServerAuth.js +164 -0
- package/dist/middleware/mcpServerAuth.js.map +1 -0
- package/dist/middleware/requestLogger.js +9 -0
- package/dist/middleware/requestLogger.js.map +1 -0
- package/dist/middleware/team.js +132 -0
- package/dist/middleware/team.js.map +1 -0
- package/dist/oauth/server.js +410 -0
- package/dist/oauth/server.js.map +1 -0
- package/dist/queue/cli.js +93 -0
- package/dist/queue/cli.js.map +1 -0
- package/dist/queue/handlers/index.js +91 -0
- package/dist/queue/handlers/index.js.map +1 -0
- package/dist/queue/handlers/scheduled-prompt.js +270 -0
- package/dist/queue/handlers/scheduled-prompt.js.map +1 -0
- package/dist/queue/index.js +91 -0
- package/dist/queue/index.js.map +1 -0
- package/dist/queue/providers/memory.js +296 -0
- package/dist/queue/providers/memory.js.map +1 -0
- package/dist/queue/providers/sqs.js +275 -0
- package/dist/queue/providers/sqs.js.map +1 -0
- package/dist/queue/scheduler.js +355 -0
- package/dist/queue/scheduler.js.map +1 -0
- package/dist/queue/types.js +5 -0
- package/dist/queue/types.js.map +1 -0
- package/dist/queue/worker.js +230 -0
- package/dist/queue/worker.js.map +1 -0
- package/dist/registry/index.js +40 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/server.js +207 -0
- package/dist/server.js.map +1 -0
- package/dist/services/agent.js +530 -0
- package/dist/services/agent.js.map +1 -0
- package/dist/services/agents.js +194 -0
- package/dist/services/agents.js.map +1 -0
- package/dist/services/documents.js +507 -0
- package/dist/services/documents.js.map +1 -0
- package/dist/services/email/index.js +91 -0
- package/dist/services/email/index.js.map +1 -0
- package/dist/services/email/providers/ses.js +97 -0
- package/dist/services/email/providers/ses.js.map +1 -0
- package/dist/services/email/templates.js +194 -0
- package/dist/services/email/templates.js.map +1 -0
- package/dist/services/email/types.js +5 -0
- package/dist/services/email/types.js.map +1 -0
- package/dist/services/encryption.js +69 -0
- package/dist/services/encryption.js.map +1 -0
- package/dist/services/oauth-discovery.js +226 -0
- package/dist/services/oauth-discovery.js.map +1 -0
- package/dist/services/pendingConfirmation.js +105 -0
- package/dist/services/pendingConfirmation.js.map +1 -0
- package/dist/services/scheduledPrompts.js +70 -0
- package/dist/services/scheduledPrompts.js.map +1 -0
- package/dist/services/slack/client.js +174 -0
- package/dist/services/slack/client.js.map +1 -0
- package/dist/services/slack/events.js +189 -0
- package/dist/services/slack/events.js.map +1 -0
- package/dist/services/slack/index.js +6 -0
- package/dist/services/slack/index.js.map +1 -0
- package/dist/services/slack/notifications.js +124 -0
- package/dist/services/slack/notifications.js.map +1 -0
- package/dist/services/slack/signature.js +74 -0
- package/dist/services/slack/signature.js.map +1 -0
- package/dist/services/slack/thread-context.js +191 -0
- package/dist/services/slack/thread-context.js.map +1 -0
- package/dist/services/toolConfirmation.js +55 -0
- package/dist/services/toolConfirmation.js.map +1 -0
- package/dist/services/usage.js +241 -0
- package/dist/services/usage.js.map +1 -0
- package/dist/ssr/build.js +90 -0
- package/dist/ssr/build.js.map +1 -0
- package/dist/ssr/components/SSRMessageList.js +120 -0
- package/dist/ssr/components/SSRMessageList.js.map +1 -0
- package/dist/ssr/entry.client.js +8 -0
- package/dist/ssr/entry.client.js.map +1 -0
- package/dist/ssr/entry.server.js +71 -0
- package/dist/ssr/entry.server.js.map +1 -0
- package/dist/ssr/handler.js +51 -0
- package/dist/ssr/handler.js.map +1 -0
- package/dist/ssr/root.js +184 -0
- package/dist/ssr/root.js.map +1 -0
- package/dist/ssr/routes/login.js +140 -0
- package/dist/ssr/routes/login.js.map +1 -0
- package/dist/ssr/routes/pricing.js +195 -0
- package/dist/ssr/routes/pricing.js.map +1 -0
- package/dist/ssr/routes/privacy.js +39 -0
- package/dist/ssr/routes/privacy.js.map +1 -0
- package/dist/ssr/routes/register.js +148 -0
- package/dist/ssr/routes/register.js.map +1 -0
- package/dist/ssr/routes/shared.$shareId.js +153 -0
- package/dist/ssr/routes/shared.$shareId.js.map +1 -0
- package/dist/ssr/routes/terms.js +39 -0
- package/dist/ssr/routes/terms.js.map +1 -0
- package/dist/storage/index.js +43 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/providers/database.js +38 -0
- package/dist/storage/providers/database.js.map +1 -0
- package/dist/storage/providers/filesystem.js +51 -0
- package/dist/storage/providers/filesystem.js.map +1 -0
- package/dist/storage/types.js +2 -0
- package/dist/storage/types.js.map +1 -0
- package/dist/tools/documents.js +336 -0
- package/dist/tools/documents.js.map +1 -0
- package/dist/tools/get-plan-usage.js +82 -0
- package/dist/tools/get-plan-usage.js.map +1 -0
- package/dist/tools/index.js +106 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/types.js +2 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/web-scrape.js +145 -0
- package/dist/tools/web-scrape.js.map +1 -0
- package/package.json +93 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { db } from '@chaaskit/db';
|
|
2
|
+
import { getConfig } from '../../config/loader.js';
|
|
3
|
+
import { SlackClient } from './client.js';
|
|
4
|
+
/**
|
|
5
|
+
* Check if a notification event is enabled in config.
|
|
6
|
+
*/
|
|
7
|
+
function isEventEnabled(event) {
|
|
8
|
+
const config = getConfig();
|
|
9
|
+
if (!config.slack?.notifications?.events) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
const eventConfig = config.slack.notifications.events.find(e => e.event === event);
|
|
13
|
+
return eventConfig?.enabled ?? false;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Format notification message based on event type.
|
|
17
|
+
*/
|
|
18
|
+
function formatNotificationMessage(payload) {
|
|
19
|
+
const { event, data } = payload;
|
|
20
|
+
switch (event) {
|
|
21
|
+
case 'thread_shared':
|
|
22
|
+
return [
|
|
23
|
+
':outbox_tray: *Thread Shared*',
|
|
24
|
+
`${data.userName || data.userEmail || 'Someone'} shared "${data.threadTitle || 'a thread'}"`,
|
|
25
|
+
data.shareUrl ? `View: ${data.shareUrl}` : '',
|
|
26
|
+
].filter(Boolean).join('\n');
|
|
27
|
+
case 'message_liked':
|
|
28
|
+
return [
|
|
29
|
+
':thumbsup: *Message Liked*',
|
|
30
|
+
`${data.userName || data.userEmail || 'Someone'} liked a response in "${data.threadTitle || 'a thread'}"`,
|
|
31
|
+
].join('\n');
|
|
32
|
+
case 'team_member_joined':
|
|
33
|
+
return [
|
|
34
|
+
':wave: *New Team Member*',
|
|
35
|
+
`${data.userName || data.userEmail || 'Someone'} joined ${data.teamName || 'the team'}`,
|
|
36
|
+
].join('\n');
|
|
37
|
+
default:
|
|
38
|
+
return `Notification: ${event}`;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Send a notification to a team's Slack workspace.
|
|
43
|
+
*/
|
|
44
|
+
export async function sendSlackNotification(payload) {
|
|
45
|
+
const config = getConfig();
|
|
46
|
+
// Check if Slack is enabled
|
|
47
|
+
if (!config.slack?.enabled) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
// Check if this event type is enabled
|
|
51
|
+
if (!isEventEnabled(payload.event)) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
// Get the team's Slack integration
|
|
55
|
+
const integration = await db.slackIntegration.findUnique({
|
|
56
|
+
where: { teamId: payload.teamId },
|
|
57
|
+
});
|
|
58
|
+
if (!integration || integration.status !== 'active') {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
// Must have a notification channel configured
|
|
62
|
+
if (!integration.notificationChannel) {
|
|
63
|
+
console.log('[Slack] No notification channel configured for team:', payload.teamId);
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
const client = SlackClient.fromEncrypted(integration.encryptedTokens);
|
|
68
|
+
const message = formatNotificationMessage(payload);
|
|
69
|
+
const result = await client.postMessage(integration.notificationChannel, message);
|
|
70
|
+
if (!result.ok) {
|
|
71
|
+
console.error('[Slack] Failed to send notification:', result.error);
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
console.error('[Slack] Error sending notification:', error);
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Send notification when a thread is shared.
|
|
83
|
+
*/
|
|
84
|
+
export async function notifyThreadShared(teamId, userName, userEmail, threadTitle, shareUrl) {
|
|
85
|
+
await sendSlackNotification({
|
|
86
|
+
event: 'thread_shared',
|
|
87
|
+
teamId,
|
|
88
|
+
data: {
|
|
89
|
+
userName: userName || undefined,
|
|
90
|
+
userEmail,
|
|
91
|
+
threadTitle,
|
|
92
|
+
shareUrl,
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Send notification when a message is liked.
|
|
98
|
+
*/
|
|
99
|
+
export async function notifyMessageLiked(teamId, userName, userEmail, threadTitle) {
|
|
100
|
+
await sendSlackNotification({
|
|
101
|
+
event: 'message_liked',
|
|
102
|
+
teamId,
|
|
103
|
+
data: {
|
|
104
|
+
userName: userName || undefined,
|
|
105
|
+
userEmail,
|
|
106
|
+
threadTitle,
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Send notification when a team member joins.
|
|
112
|
+
*/
|
|
113
|
+
export async function notifyTeamMemberJoined(teamId, userName, userEmail, teamName) {
|
|
114
|
+
await sendSlackNotification({
|
|
115
|
+
event: 'team_member_joined',
|
|
116
|
+
teamId,
|
|
117
|
+
data: {
|
|
118
|
+
userName: userName || undefined,
|
|
119
|
+
userEmail,
|
|
120
|
+
teamName,
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=notifications.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notifications.js","sourceRoot":"","sources":["../../../src/services/slack/notifications.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
import { getConfig } from '../../config/loader.js';
|
|
3
|
+
/**
|
|
4
|
+
* Verify Slack request signature using HMAC-SHA256.
|
|
5
|
+
* See: https://api.slack.com/authentication/verifying-requests-from-slack
|
|
6
|
+
*/
|
|
7
|
+
export function verifySlackSignature(signature, timestamp, rawBody) {
|
|
8
|
+
if (!signature || !timestamp) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
const config = getConfig();
|
|
12
|
+
const signingSecretEnvVar = config.slack?.signingSecretEnvVar || 'SLACK_SIGNING_SECRET';
|
|
13
|
+
const signingSecret = process.env[signingSecretEnvVar];
|
|
14
|
+
if (!signingSecret) {
|
|
15
|
+
console.error('[Slack] Missing signing secret:', signingSecretEnvVar);
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
// Verify timestamp to prevent replay attacks (allow 5 minutes)
|
|
19
|
+
const requestTimestamp = parseInt(timestamp, 10);
|
|
20
|
+
const now = Math.floor(Date.now() / 1000);
|
|
21
|
+
if (Math.abs(now - requestTimestamp) > 300) {
|
|
22
|
+
console.warn('[Slack] Request timestamp too old:', requestTimestamp);
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
// Construct the base string
|
|
26
|
+
const baseString = `v0:${timestamp}:${rawBody}`;
|
|
27
|
+
// Compute HMAC-SHA256
|
|
28
|
+
const hmac = crypto.createHmac('sha256', signingSecret);
|
|
29
|
+
hmac.update(baseString);
|
|
30
|
+
const expectedSignature = `v0=${hmac.digest('hex')}`;
|
|
31
|
+
// Timing-safe comparison
|
|
32
|
+
try {
|
|
33
|
+
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature));
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Buffers have different lengths
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Verify internal secret for self-calling endpoints.
|
|
42
|
+
*/
|
|
43
|
+
export function verifyInternalSecret(headerValue) {
|
|
44
|
+
const config = getConfig();
|
|
45
|
+
const internalSecretEnvVar = config.slack?.internalSecretEnvVar || 'SLACK_INTERNAL_SECRET';
|
|
46
|
+
const internalSecret = process.env[internalSecretEnvVar];
|
|
47
|
+
if (!internalSecret) {
|
|
48
|
+
console.error('[Slack] Missing internal secret:', internalSecretEnvVar);
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
if (!headerValue) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
// Timing-safe comparison
|
|
55
|
+
try {
|
|
56
|
+
return crypto.timingSafeEqual(Buffer.from(headerValue), Buffer.from(internalSecret));
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Get the internal secret for self-calling endpoints.
|
|
64
|
+
*/
|
|
65
|
+
export function getInternalSecret() {
|
|
66
|
+
const config = getConfig();
|
|
67
|
+
const internalSecretEnvVar = config.slack?.internalSecretEnvVar || 'SLACK_INTERNAL_SECRET';
|
|
68
|
+
const internalSecret = process.env[internalSecretEnvVar];
|
|
69
|
+
if (!internalSecret) {
|
|
70
|
+
throw new Error(`Missing required environment variable: ${internalSecretEnvVar}`);
|
|
71
|
+
}
|
|
72
|
+
return internalSecret;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=signature.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signature.js","sourceRoot":"","sources":["../../../src/services/slack/signature.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { db } from '@chaaskit/db';
|
|
2
|
+
/**
|
|
3
|
+
* Build thread context from Slack conversation history.
|
|
4
|
+
*/
|
|
5
|
+
export async function buildSlackThreadContext(client, channelId, threadTs, botUserId) {
|
|
6
|
+
// If not in a thread, no context to build
|
|
7
|
+
if (!threadTs) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
const history = await client.getConversationHistory(channelId, {
|
|
12
|
+
threadTs,
|
|
13
|
+
limit: 20, // Get last 20 messages in thread
|
|
14
|
+
});
|
|
15
|
+
if (!history.ok || !history.messages) {
|
|
16
|
+
console.error('[Slack] Failed to get thread history:', history.error);
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
// Convert Slack messages to context format
|
|
20
|
+
// Messages come newest-first, so reverse them
|
|
21
|
+
const messages = history.messages
|
|
22
|
+
.slice()
|
|
23
|
+
.reverse()
|
|
24
|
+
.filter(msg => msg.text && msg.ts !== threadTs) // Exclude parent message
|
|
25
|
+
.map(msg => ({
|
|
26
|
+
role: (msg.bot_id || msg.user === botUserId ? 'assistant' : 'user'),
|
|
27
|
+
content: cleanSlackMessage(msg.text || '', botUserId),
|
|
28
|
+
slackUserId: msg.user,
|
|
29
|
+
}));
|
|
30
|
+
return {
|
|
31
|
+
messages,
|
|
32
|
+
slackThreadTs: threadTs,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
console.error('[Slack] Error building thread context:', error);
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Find or create an internal thread for Slack context continuity.
|
|
42
|
+
*/
|
|
43
|
+
export async function findOrCreateInternalThread(teamId, slackChannelId, slackThreadTs) {
|
|
44
|
+
// If no thread timestamp, always create a new thread
|
|
45
|
+
if (!slackThreadTs) {
|
|
46
|
+
const thread = await db.thread.create({
|
|
47
|
+
data: {
|
|
48
|
+
title: 'Slack Chat',
|
|
49
|
+
teamId,
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
return thread.id;
|
|
53
|
+
}
|
|
54
|
+
// Look for existing event with same channel/thread that has an internal thread
|
|
55
|
+
const existingEvent = await db.slackMessageEvent.findFirst({
|
|
56
|
+
where: {
|
|
57
|
+
slackChannelId,
|
|
58
|
+
slackThreadTs,
|
|
59
|
+
internalThreadId: { not: null },
|
|
60
|
+
},
|
|
61
|
+
orderBy: { createdAt: 'desc' },
|
|
62
|
+
select: { internalThreadId: true },
|
|
63
|
+
});
|
|
64
|
+
if (existingEvent?.internalThreadId) {
|
|
65
|
+
// Verify thread still exists
|
|
66
|
+
const thread = await db.thread.findUnique({
|
|
67
|
+
where: { id: existingEvent.internalThreadId },
|
|
68
|
+
});
|
|
69
|
+
if (thread) {
|
|
70
|
+
return existingEvent.internalThreadId;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Create new internal thread
|
|
74
|
+
const thread = await db.thread.create({
|
|
75
|
+
data: {
|
|
76
|
+
title: 'Slack Thread',
|
|
77
|
+
teamId,
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
return thread.id;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get message history from internal thread.
|
|
84
|
+
*/
|
|
85
|
+
export async function getInternalThreadHistory(threadId) {
|
|
86
|
+
const messages = await db.message.findMany({
|
|
87
|
+
where: { threadId },
|
|
88
|
+
orderBy: { createdAt: 'asc' },
|
|
89
|
+
take: 20, // Last 20 messages
|
|
90
|
+
select: {
|
|
91
|
+
role: true,
|
|
92
|
+
content: true,
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
return messages
|
|
96
|
+
.filter(m => m.role === 'user' || m.role === 'assistant')
|
|
97
|
+
.map(m => ({
|
|
98
|
+
role: m.role,
|
|
99
|
+
content: m.content,
|
|
100
|
+
}));
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Save a message to internal thread.
|
|
104
|
+
*/
|
|
105
|
+
export async function saveToInternalThread(threadId, role, content) {
|
|
106
|
+
await db.message.create({
|
|
107
|
+
data: {
|
|
108
|
+
threadId,
|
|
109
|
+
role,
|
|
110
|
+
content,
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
// Update thread's updatedAt
|
|
114
|
+
await db.thread.update({
|
|
115
|
+
where: { id: threadId },
|
|
116
|
+
data: { updatedAt: new Date() },
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Clean Slack message text:
|
|
121
|
+
* - Remove @mentions of the bot
|
|
122
|
+
* - Convert user mentions to readable format
|
|
123
|
+
* - Handle formatting
|
|
124
|
+
*/
|
|
125
|
+
export function cleanSlackMessage(text, botUserId) {
|
|
126
|
+
// Remove bot mentions
|
|
127
|
+
const botMentionRegex = new RegExp(`<@${botUserId}>`, 'g');
|
|
128
|
+
let cleaned = text.replace(botMentionRegex, '').trim();
|
|
129
|
+
// Convert user mentions <@U123> to @user
|
|
130
|
+
cleaned = cleaned.replace(/<@([A-Z0-9]+)>/g, '@user');
|
|
131
|
+
// Convert links <url|text> to just text or url
|
|
132
|
+
cleaned = cleaned.replace(/<(https?:\/\/[^|>]+)\|([^>]+)>/g, '$2');
|
|
133
|
+
cleaned = cleaned.replace(/<(https?:\/\/[^>]+)>/g, '$1');
|
|
134
|
+
// Convert channel references <#C123|channel-name> to #channel-name
|
|
135
|
+
cleaned = cleaned.replace(/<#[A-Z0-9]+\|([^>]+)>/g, '#$1');
|
|
136
|
+
return cleaned.trim();
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Format response for Slack mrkdwn:
|
|
140
|
+
* - Convert standard markdown to Slack mrkdwn syntax
|
|
141
|
+
* - Handle bold, italic, strikethrough, links
|
|
142
|
+
*/
|
|
143
|
+
export function formatForSlack(text) {
|
|
144
|
+
let result = text;
|
|
145
|
+
// Preserve code blocks first (we don't want to process inside them)
|
|
146
|
+
const codeBlocks = [];
|
|
147
|
+
result = result.replace(/```[\s\S]*?```/g, match => {
|
|
148
|
+
codeBlocks.push(match);
|
|
149
|
+
return `__CODE_BLOCK_${codeBlocks.length - 1}__`;
|
|
150
|
+
});
|
|
151
|
+
// Preserve inline code
|
|
152
|
+
const inlineCode = [];
|
|
153
|
+
result = result.replace(/`[^`]+`/g, match => {
|
|
154
|
+
inlineCode.push(match);
|
|
155
|
+
return `__INLINE_CODE_${inlineCode.length - 1}__`;
|
|
156
|
+
});
|
|
157
|
+
// Convert markdown links [text](url) to Slack format <url|text>
|
|
158
|
+
result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<$2|$1>');
|
|
159
|
+
// Convert markdown bold **text** to Slack bold *text*
|
|
160
|
+
// Be careful not to convert already single asterisks
|
|
161
|
+
result = result.replace(/\*\*([^*]+)\*\*/g, '*$1*');
|
|
162
|
+
// Convert markdown strikethrough ~~text~~ to Slack ~text~
|
|
163
|
+
result = result.replace(/~~([^~]+)~~/g, '~$1~');
|
|
164
|
+
// Markdown italic with underscores _text_ already works in Slack
|
|
165
|
+
// Markdown italic with single asterisks *text* would conflict with Slack bold,
|
|
166
|
+
// but after converting **bold** to *bold*, remaining single asterisks should be fine
|
|
167
|
+
// Restore inline code
|
|
168
|
+
inlineCode.forEach((code, i) => {
|
|
169
|
+
result = result.replace(`__INLINE_CODE_${i}__`, code);
|
|
170
|
+
});
|
|
171
|
+
// Restore code blocks
|
|
172
|
+
codeBlocks.forEach((block, i) => {
|
|
173
|
+
result = result.replace(`__CODE_BLOCK_${i}__`, block);
|
|
174
|
+
});
|
|
175
|
+
return result;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* System prompt suffix for Slack responses.
|
|
179
|
+
* Instructs the AI to format responses using Slack's mrkdwn syntax.
|
|
180
|
+
*/
|
|
181
|
+
export const SLACK_FORMAT_INSTRUCTIONS = `
|
|
182
|
+
Format your responses using Slack's mrkdwn syntax:
|
|
183
|
+
- Bold: *bold text*
|
|
184
|
+
- Italic: _italic text_
|
|
185
|
+
- Strikethrough: ~strikethrough~
|
|
186
|
+
- Code: \`inline code\` or \`\`\`code block\`\`\`
|
|
187
|
+
- Links: <https://example.com|link text>
|
|
188
|
+
- Lists: Use - or • for bullets
|
|
189
|
+
- Quotes: >quoted text
|
|
190
|
+
Keep responses concise and well-formatted for chat.`.trim();
|
|
191
|
+
//# sourceMappingURL=thread-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thread-context.js","sourceRoot":"","sources":["../../../src/services/slack/thread-context.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool Confirmation Service
|
|
3
|
+
*
|
|
4
|
+
* Determines whether a tool call requires user confirmation based on:
|
|
5
|
+
* 1. Admin configuration (mode: none, all, whitelist, blacklist)
|
|
6
|
+
* 2. User's "always allow" settings
|
|
7
|
+
* 3. Thread-level allowances (tools allowed for this thread)
|
|
8
|
+
*/
|
|
9
|
+
import { getConfig } from '../config/loader.js';
|
|
10
|
+
/**
|
|
11
|
+
* Check if a tool call requires user confirmation
|
|
12
|
+
*/
|
|
13
|
+
export function checkToolConfirmationRequired(params) {
|
|
14
|
+
const { serverId, toolName, userSettings, threadAllowedTools } = params;
|
|
15
|
+
const config = getConfig().mcp?.toolConfirmation;
|
|
16
|
+
const toolId = `${serverId}:${toolName}`;
|
|
17
|
+
// 1. Check admin config mode
|
|
18
|
+
if (!config || config.mode === 'none') {
|
|
19
|
+
return { required: false, autoApproveReason: 'config_none' };
|
|
20
|
+
}
|
|
21
|
+
// 2. Check whitelist mode (listed tools DON'T require confirmation)
|
|
22
|
+
if (config.mode === 'whitelist') {
|
|
23
|
+
const tools = config.tools || [];
|
|
24
|
+
if (tools.includes(toolId) || tools.includes(toolName)) {
|
|
25
|
+
return { required: false, autoApproveReason: 'whitelist' };
|
|
26
|
+
}
|
|
27
|
+
// In whitelist mode, if tool is not in whitelist, check other allowances
|
|
28
|
+
}
|
|
29
|
+
// 3. Check blacklist mode (listed tools DO require confirmation)
|
|
30
|
+
if (config.mode === 'blacklist') {
|
|
31
|
+
const tools = config.tools || [];
|
|
32
|
+
// If tool is NOT in the blacklist, it's auto-approved
|
|
33
|
+
if (!tools.includes(toolId) && !tools.includes(toolName)) {
|
|
34
|
+
return { required: false, autoApproveReason: 'config_none' };
|
|
35
|
+
}
|
|
36
|
+
// Tool is in blacklist, check other allowances below
|
|
37
|
+
}
|
|
38
|
+
// 4. Check user's "always allow" settings
|
|
39
|
+
if (userSettings?.allowedTools?.includes(toolId)) {
|
|
40
|
+
return { required: false, autoApproveReason: 'user_always' };
|
|
41
|
+
}
|
|
42
|
+
// 5. Check thread-level allowance
|
|
43
|
+
if (threadAllowedTools?.includes(toolId)) {
|
|
44
|
+
return { required: false, autoApproveReason: 'thread_allowed' };
|
|
45
|
+
}
|
|
46
|
+
// 6. If we got here, confirmation is required
|
|
47
|
+
return { required: true };
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Format a tool result for denial
|
|
51
|
+
*/
|
|
52
|
+
export function createDenialToolResult(toolName) {
|
|
53
|
+
return `The user denied permission to execute the tool "${toolName}". Please respect their decision and continue the conversation without using this tool, or ask if they'd like to try a different approach.`;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=toolConfirmation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolConfirmation.js","sourceRoot":"","sources":["../../src/services/toolConfirmation.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAoBhD;;GAEG;AACH,MAAM,UAAU,6BAA6B,CAAC,MAA+B;IAC3E,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,kBAAkB,EAAE,GAAG,MAAM,CAAC;IACxE,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC,GAAG,EAAE,gBAAgB,CAAC;IACjD,MAAM,MAAM,GAAG,GAAG,QAAQ,IAAI,QAAQ,EAAE,CAAC;IAEzC,6BAA6B;IAC7B,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QACtC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,CAAC;IAC/D,CAAC;IAED,oEAAoE;IACpE,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QAChC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;QACjC,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YACvD,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,CAAC;QAC7D,CAAC;QACD,yEAAyE;IAC3E,CAAC;IAED,iEAAiE;IACjE,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QAChC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;QACjC,sDAAsD;QACtD,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzD,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,CAAC;QAC/D,CAAC;QACD,qDAAqD;IACvD,CAAC;IAED,0CAA0C;IAC1C,IAAI,YAAY,EAAE,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACjD,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,CAAC;IAC/D,CAAC;IAED,kCAAkC;IAClC,IAAI,kBAAkB,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACzC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,CAAC;IAClE,CAAC;IAED,8CAA8C;IAC9C,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AAC5B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB,CAAC,QAAgB;IACrD,OAAO,mDAAmD,QAAQ,4IAA4I,CAAC;AACjN,CAAC"}
|