ccgateway 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.
Files changed (75) hide show
  1. package/README.md +252 -0
  2. package/bin/ccg-dev.sh +3 -0
  3. package/dist/agents.d.ts +17 -0
  4. package/dist/agents.d.ts.map +1 -0
  5. package/dist/agents.js +45 -0
  6. package/dist/agents.js.map +1 -0
  7. package/dist/chat.d.ts +14 -0
  8. package/dist/chat.d.ts.map +1 -0
  9. package/dist/chat.js +104 -0
  10. package/dist/chat.js.map +1 -0
  11. package/dist/cli.d.ts +3 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +501 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/config.d.ts +53 -0
  16. package/dist/config.d.ts.map +1 -0
  17. package/dist/config.js +70 -0
  18. package/dist/config.js.map +1 -0
  19. package/dist/context.d.ts +45 -0
  20. package/dist/context.d.ts.map +1 -0
  21. package/dist/context.js +201 -0
  22. package/dist/context.js.map +1 -0
  23. package/dist/daemon.d.ts +27 -0
  24. package/dist/daemon.d.ts.map +1 -0
  25. package/dist/daemon.js +207 -0
  26. package/dist/daemon.js.map +1 -0
  27. package/dist/heartbeat.d.ts +42 -0
  28. package/dist/heartbeat.d.ts.map +1 -0
  29. package/dist/heartbeat.js +153 -0
  30. package/dist/heartbeat.js.map +1 -0
  31. package/dist/logger.d.ts +15 -0
  32. package/dist/logger.d.ts.map +1 -0
  33. package/dist/logger.js +70 -0
  34. package/dist/logger.js.map +1 -0
  35. package/dist/messaging.d.ts +43 -0
  36. package/dist/messaging.d.ts.map +1 -0
  37. package/dist/messaging.js +132 -0
  38. package/dist/messaging.js.map +1 -0
  39. package/dist/migrate.d.ts +24 -0
  40. package/dist/migrate.d.ts.map +1 -0
  41. package/dist/migrate.js +356 -0
  42. package/dist/migrate.js.map +1 -0
  43. package/dist/plugin.d.ts +63 -0
  44. package/dist/plugin.d.ts.map +1 -0
  45. package/dist/plugin.js +93 -0
  46. package/dist/plugin.js.map +1 -0
  47. package/dist/plugins/discord-gateway.d.ts +32 -0
  48. package/dist/plugins/discord-gateway.d.ts.map +1 -0
  49. package/dist/plugins/discord-gateway.js +208 -0
  50. package/dist/plugins/discord-gateway.js.map +1 -0
  51. package/dist/plugins/slack-gateway.d.ts +35 -0
  52. package/dist/plugins/slack-gateway.d.ts.map +1 -0
  53. package/dist/plugins/slack-gateway.js +291 -0
  54. package/dist/plugins/slack-gateway.js.map +1 -0
  55. package/dist/router.d.ts +44 -0
  56. package/dist/router.d.ts.map +1 -0
  57. package/dist/router.js +103 -0
  58. package/dist/router.js.map +1 -0
  59. package/dist/sessions.d.ts +55 -0
  60. package/dist/sessions.d.ts.map +1 -0
  61. package/dist/sessions.js +160 -0
  62. package/dist/sessions.js.map +1 -0
  63. package/dist/skills.d.ts +58 -0
  64. package/dist/skills.d.ts.map +1 -0
  65. package/dist/skills.js +194 -0
  66. package/dist/skills.js.map +1 -0
  67. package/dist/spawner.d.ts +29 -0
  68. package/dist/spawner.d.ts.map +1 -0
  69. package/dist/spawner.js +54 -0
  70. package/dist/spawner.js.map +1 -0
  71. package/dist/types.d.ts +22 -0
  72. package/dist/types.d.ts.map +1 -0
  73. package/dist/types.js +4 -0
  74. package/dist/types.js.map +1 -0
  75. package/package.json +48 -0
@@ -0,0 +1,208 @@
1
+ import { Client, GatewayIntentBits } from "discord.js";
2
+ import { logger } from "../logger.js";
3
+ // ── Helpers (exported for testing) ────────────────────────────────────────
4
+ /**
5
+ * Resolve a token value. If it starts with "$", treat it as an env var name.
6
+ * Throws if the env var is not set.
7
+ */
8
+ export function resolveToken(token) {
9
+ if (token.startsWith("$")) {
10
+ const envName = token.slice(1);
11
+ const value = process.env[envName];
12
+ if (!value) {
13
+ throw new Error(`Environment variable "${envName}" is not set (referenced by token "${token}")`);
14
+ }
15
+ return value;
16
+ }
17
+ return token;
18
+ }
19
+ /**
20
+ * Smart message splitter for Discord's 2000-char limit.
21
+ * Prefers splitting at paragraph boundaries (\n\n), then line boundaries (\n),
22
+ * then hard-cuts at the limit.
23
+ *
24
+ * Preserves code block fences across splits.
25
+ */
26
+ export function splitMessage(text, limit = 2000) {
27
+ if (text.length <= limit) {
28
+ return [text];
29
+ }
30
+ const chunks = [];
31
+ let remaining = text;
32
+ while (remaining.length > 0) {
33
+ if (remaining.length <= limit) {
34
+ chunks.push(remaining);
35
+ break;
36
+ }
37
+ // Try splitting at paragraph boundary (\n\n)
38
+ let splitIdx = remaining.lastIndexOf("\n\n", limit);
39
+ if (splitIdx > 0) {
40
+ chunks.push(remaining.slice(0, splitIdx));
41
+ remaining = remaining.slice(splitIdx + 2); // skip the \n\n
42
+ continue;
43
+ }
44
+ // Try splitting at line boundary (\n)
45
+ splitIdx = remaining.lastIndexOf("\n", limit);
46
+ if (splitIdx > 0) {
47
+ chunks.push(remaining.slice(0, splitIdx));
48
+ remaining = remaining.slice(splitIdx + 1); // skip the \n
49
+ continue;
50
+ }
51
+ // Hard cut at limit
52
+ chunks.push(remaining.slice(0, limit));
53
+ remaining = remaining.slice(limit);
54
+ }
55
+ return chunks;
56
+ }
57
+ /**
58
+ * Normalize a Discord message into an IncomingMessage.
59
+ */
60
+ export function normalizeMessage(msg, agentId) {
61
+ return {
62
+ from: {
63
+ gateway: "discord",
64
+ channel: msg.channelId,
65
+ user: msg.author.username,
66
+ userId: msg.author.id,
67
+ messageId: msg.id,
68
+ },
69
+ to: { agent: agentId },
70
+ content: msg.content,
71
+ attachments: Array.from(msg.attachments.values()).map((a) => ({
72
+ type: a.contentType ?? "application/octet-stream",
73
+ url: a.url,
74
+ filename: a.name ?? undefined,
75
+ })),
76
+ };
77
+ }
78
+ // ── Plugin factory ────────────────────────────────────────────────────────
79
+ export default function createDiscordGateway(pluginConfig) {
80
+ let core;
81
+ const clients = new Map();
82
+ const resolvedTokens = new Map();
83
+ // Track our own bot user IDs so we can distinguish "our bots" from "other bots"
84
+ const ownBotUserIds = new Set();
85
+ const plugin = {
86
+ name: "discord-gateway",
87
+ type: "gateway",
88
+ async init(coreRef) {
89
+ core = coreRef;
90
+ // Resolve all bot tokens from env vars
91
+ for (const [botId, botConfig] of Object.entries(pluginConfig.bots)) {
92
+ const token = resolveToken(botConfig.token);
93
+ resolvedTokens.set(botId, token);
94
+ }
95
+ },
96
+ async start() {
97
+ for (const [botId, token] of Array.from(resolvedTokens.entries())) {
98
+ const client = new Client({
99
+ intents: [
100
+ GatewayIntentBits.Guilds,
101
+ GatewayIntentBits.GuildMessages,
102
+ GatewayIntentBits.MessageContent,
103
+ ],
104
+ });
105
+ clients.set(botId, client);
106
+ client.on("ready", () => {
107
+ if (client.user) {
108
+ ownBotUserIds.add(client.user.id);
109
+ logger.info(`discord-gateway: bot "${botId}" connected as ${client.user.tag}`);
110
+ // Log which bound channels this bot can see
111
+ const botBindings = core.config.bindings.filter((b) => b.gateway === "discord" && b.bot === botId);
112
+ for (const b of botBindings) {
113
+ const ch = client.channels.cache.get(b.channel);
114
+ logger.info(`discord-gateway: bot "${botId}" binding ${b.channel} (agent: ${b.agent}) — ${ch ? "visible" : "NOT VISIBLE"}`);
115
+ }
116
+ }
117
+ });
118
+ client.on("messageCreate", async (msg) => {
119
+ try {
120
+ await handleMessage(msg, botId);
121
+ }
122
+ catch (err) {
123
+ const errMsg = err instanceof Error ? err.message : String(err);
124
+ logger.error(`discord-gateway: error handling message ${msg.id}: ${errMsg}`);
125
+ }
126
+ });
127
+ await client.login(token);
128
+ }
129
+ },
130
+ async stop() {
131
+ for (const [botId, client] of Array.from(clients.entries())) {
132
+ logger.info(`discord-gateway: disconnecting bot "${botId}"`);
133
+ client.destroy();
134
+ }
135
+ clients.clear();
136
+ ownBotUserIds.clear();
137
+ },
138
+ };
139
+ // Attach sendToChannel for cross-agent messaging (not part of CcgPlugin interface)
140
+ plugin.sendToChannel = async (channelId, botId, content) => {
141
+ const client = clients.get(botId);
142
+ if (!client) {
143
+ throw new Error(`Bot "${botId}" not found in discord-gateway`);
144
+ }
145
+ const channel = await client.channels.fetch(channelId);
146
+ if (!channel || !("send" in channel)) {
147
+ throw new Error(`Channel "${channelId}" not found or not a text channel`);
148
+ }
149
+ const chunks = splitMessage(content);
150
+ for (const chunk of chunks) {
151
+ await channel.send(chunk);
152
+ }
153
+ };
154
+ return plugin;
155
+ // ── Internal message handler ──────────────────────────────────────────
156
+ async function handleMessage(msg, receivingBotId) {
157
+ logger.info(`discord-gateway: bot "${receivingBotId}" received message in channel ${msg.channelId} from ${msg.author.tag} (bot=${msg.author.bot})`);
158
+ // Ignore all bot messages (prevents feedback loops)
159
+ if (msg.author.bot) {
160
+ return;
161
+ }
162
+ // Only handle messages if THIS bot is the one bound to this channel.
163
+ // This prevents "several people typing" — only the correct bot responds.
164
+ const binding = core.config.bindings.find((b) => b.gateway === "discord" && b.channel === msg.channelId && b.bot === receivingBotId);
165
+ if (!binding) {
166
+ logger.info(`discord-gateway: bot "${receivingBotId}" no binding for channel ${msg.channelId}`);
167
+ return; // this bot is not bound to this channel
168
+ }
169
+ const agentId = binding.agent;
170
+ // Check if user is in allowedUsers
171
+ if (!pluginConfig.allowedUsers.includes(msg.author.id)) {
172
+ return;
173
+ }
174
+ // Handle slash commands
175
+ const trimmed = msg.content.trim().toLowerCase();
176
+ if (trimmed === "/new" || trimmed === "/reset") {
177
+ const sessionKey = await core.sessions.getOrCreateSession(agentId, "discord", msg.channelId);
178
+ await core.sessions.resetSession(agentId, sessionKey);
179
+ await msg.channel.send(`Session reset for **${agentId}**. Starting fresh.`);
180
+ return;
181
+ }
182
+ if (trimmed === "/status") {
183
+ const sessionKey = await core.sessions.getOrCreateSession(agentId, "discord", msg.channelId);
184
+ await msg.channel.send(`**Agent:** ${agentId}\n**Session:** \`${sessionKey}\`\n**Channel:** ${msg.channelId}`);
185
+ return;
186
+ }
187
+ // Normalize to IncomingMessage and route
188
+ const incomingMessage = normalizeMessage(msg, agentId);
189
+ // Show typing indicator
190
+ const typingInterval = setInterval(() => {
191
+ msg.channel.sendTyping().catch(() => { });
192
+ }, 5000);
193
+ // Send initial typing
194
+ await msg.channel.sendTyping().catch(() => { });
195
+ try {
196
+ const response = await core.router.route(incomingMessage);
197
+ // Split and send response
198
+ const chunks = splitMessage(response);
199
+ for (const chunk of chunks) {
200
+ await msg.channel.send(chunk);
201
+ }
202
+ }
203
+ finally {
204
+ clearInterval(typingInterval);
205
+ }
206
+ }
207
+ }
208
+ //# sourceMappingURL=discord-gateway.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discord-gateway.js","sourceRoot":"","sources":["../../src/plugins/discord-gateway.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import type { CcgPlugin } from "../plugin.js";
2
+ interface SlackBotConfig {
3
+ token: string;
4
+ signingSecret: string;
5
+ appToken: string;
6
+ }
7
+ interface SlackGatewayConfig {
8
+ bots: Record<string, SlackBotConfig>;
9
+ workspace: string;
10
+ allowedUsers: string[];
11
+ commands: string[];
12
+ }
13
+ /**
14
+ * Resolve a config value: if it starts with "$", read from process.env.
15
+ * Throws if the env var is not set.
16
+ */
17
+ export declare function resolveToken(value: string): string;
18
+ /**
19
+ * Convert standard markdown to Slack mrkdwn format.
20
+ * - **bold** -> *bold*
21
+ * - *italic* (single asterisks not preceded/followed by asterisk) -> _italic_
22
+ * - ~~strikethrough~~ -> ~strikethrough~
23
+ * - [text](url) -> <url|text>
24
+ * - `code` stays as `code`
25
+ * - ```code blocks``` stay as ```code blocks```
26
+ */
27
+ export declare function markdownToMrkdwn(md: string): string;
28
+ /**
29
+ * Split a message into chunks that fit within Slack's character limit.
30
+ * Prefers splitting at newline boundaries.
31
+ */
32
+ export declare function splitMessage(text: string, maxLen?: number): string[];
33
+ export default function createSlackGateway(pluginConfig: SlackGatewayConfig): CcgPlugin;
34
+ export {};
35
+ //# sourceMappingURL=slack-gateway.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slack-gateway.d.ts","sourceRoot":"","sources":["../../src/plugins/slack-gateway.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAW,MAAM,cAAc,CAAC;AAMvD,UAAU,cAAc;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,kBAAkB;IAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAID;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAYlD;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAqCnD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,SAAO,GAAG,MAAM,EAAE,CA4BlE;AAWD,MAAM,CAAC,OAAO,UAAU,kBAAkB,CAAC,YAAY,EAAE,kBAAkB,GAAG,SAAS,CAqOtF"}
@@ -0,0 +1,291 @@
1
+ import { App } from "@slack/bolt";
2
+ import { logger } from "../logger.js";
3
+ // ── Helpers ───────────────────────────────────────────────────────────────
4
+ /**
5
+ * Resolve a config value: if it starts with "$", read from process.env.
6
+ * Throws if the env var is not set.
7
+ */
8
+ export function resolveToken(value) {
9
+ if (value.startsWith("$")) {
10
+ const envKey = value.slice(1);
11
+ const resolved = process.env[envKey];
12
+ if (!resolved) {
13
+ throw new Error(`Environment variable "${envKey}" is not set (referenced as "${value}")`);
14
+ }
15
+ return resolved;
16
+ }
17
+ return value;
18
+ }
19
+ /**
20
+ * Convert standard markdown to Slack mrkdwn format.
21
+ * - **bold** -> *bold*
22
+ * - *italic* (single asterisks not preceded/followed by asterisk) -> _italic_
23
+ * - ~~strikethrough~~ -> ~strikethrough~
24
+ * - [text](url) -> <url|text>
25
+ * - `code` stays as `code`
26
+ * - ```code blocks``` stay as ```code blocks```
27
+ */
28
+ export function markdownToMrkdwn(md) {
29
+ let result = md;
30
+ // Preserve code blocks first — replace with placeholders
31
+ const codeBlocks = [];
32
+ result = result.replace(/```[\s\S]*?```/g, (match) => {
33
+ codeBlocks.push(match);
34
+ return `\x00CB${codeBlocks.length - 1}\x00`;
35
+ });
36
+ // Preserve inline code
37
+ const inlineCode = [];
38
+ result = result.replace(/`[^`]+`/g, (match) => {
39
+ inlineCode.push(match);
40
+ return `\x00IC${inlineCode.length - 1}\x00`;
41
+ });
42
+ // Handle italic BEFORE bold: extract single-asterisk italic first
43
+ // *italic* -> _italic_ (single asterisks not part of **)
44
+ result = result.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, "_$1_");
45
+ // **bold** -> *bold*
46
+ result = result.replace(/\*\*(.+?)\*\*/g, "*$1*");
47
+ // ~~strikethrough~~ -> ~strikethrough~
48
+ result = result.replace(/~~(.+?)~~/g, "~$1~");
49
+ // [text](url) -> <url|text>
50
+ result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "<$2|$1>");
51
+ // Restore inline code
52
+ result = result.replace(/\x00IC(\d+)\x00/g, (_, idx) => inlineCode[Number(idx)]);
53
+ // Restore code blocks
54
+ result = result.replace(/\x00CB(\d+)\x00/g, (_, idx) => codeBlocks[Number(idx)]);
55
+ return result;
56
+ }
57
+ /**
58
+ * Split a message into chunks that fit within Slack's character limit.
59
+ * Prefers splitting at newline boundaries.
60
+ */
61
+ export function splitMessage(text, maxLen = 3900) {
62
+ if (text.length <= maxLen)
63
+ return [text];
64
+ const chunks = [];
65
+ let remaining = text;
66
+ while (remaining.length > 0) {
67
+ if (remaining.length <= maxLen) {
68
+ chunks.push(remaining);
69
+ break;
70
+ }
71
+ // Try to split at a newline near the limit
72
+ let splitAt = remaining.lastIndexOf("\n", maxLen);
73
+ if (splitAt <= 0) {
74
+ // No good newline, split at a space
75
+ splitAt = remaining.lastIndexOf(" ", maxLen);
76
+ }
77
+ if (splitAt <= 0) {
78
+ // No good split point, hard-cut
79
+ splitAt = maxLen;
80
+ }
81
+ chunks.push(remaining.slice(0, splitAt));
82
+ remaining = remaining.slice(splitAt).replace(/^\n/, "");
83
+ }
84
+ return chunks;
85
+ }
86
+ // ── Plugin factory ────────────────────────────────────────────────────────
87
+ export default function createSlackGateway(pluginConfig) {
88
+ let core;
89
+ const bots = [];
90
+ const resolvedTokens = new Map();
91
+ /**
92
+ * Send a message to a specific channel using a specific bot.
93
+ */
94
+ async function sendToChannel(channelId, botId, content) {
95
+ const bot = bots.find((b) => b.botId === botId);
96
+ if (!bot) {
97
+ throw new Error(`Slack bot "${botId}" not found`);
98
+ }
99
+ const mrkdwn = markdownToMrkdwn(content);
100
+ const chunks = splitMessage(mrkdwn);
101
+ for (const chunk of chunks) {
102
+ await bot.app.client.chat.postMessage({
103
+ channel: channelId,
104
+ text: chunk,
105
+ blocks: [
106
+ {
107
+ type: "section",
108
+ text: { type: "mrkdwn", text: chunk },
109
+ },
110
+ ],
111
+ });
112
+ }
113
+ }
114
+ /**
115
+ * Handle a slash command from Slack.
116
+ * Returns the response string, or null if unrecognized.
117
+ */
118
+ async function handleSlashCommand(command, agentId, channelId, userId) {
119
+ switch (command) {
120
+ case "/new":
121
+ case "/reset": {
122
+ const sessionKey = core.sessions.getOrCreateSession(agentId, "slack", channelId);
123
+ // SessionManager.resetSession expects (agentId, sessionKey)
124
+ // but the interface exposed via CcgCore only has getOrCreateSession.
125
+ // We just create a new session reference which effectively resets context.
126
+ return `Session reset for agent *${agentId}* in this channel.`;
127
+ }
128
+ case "/status": {
129
+ const agent = core.agents.getAgent(agentId);
130
+ if (!agent)
131
+ return `Agent "${agentId}" not found.`;
132
+ return `*${agent.name}* ${agent.emoji} is online.\nModel: \`${agent.model}\``;
133
+ }
134
+ default:
135
+ return null;
136
+ }
137
+ }
138
+ /**
139
+ * Register message and event handlers for a single bot App.
140
+ */
141
+ function registerHandlers(app, botId) {
142
+ // Handle messages
143
+ app.message(async ({ message, say, client }) => {
144
+ // Ignore bot messages and message_changed subtypes
145
+ if (!message || message.subtype)
146
+ return;
147
+ // message type narrowing — we only handle standard user messages
148
+ if (!("user" in message) || !("text" in message))
149
+ return;
150
+ const userId = message.user;
151
+ const text = message.text ?? "";
152
+ const channelId = message.channel;
153
+ const messageTs = message.ts;
154
+ const threadTs = "thread_ts" in message ? message.thread_ts : undefined;
155
+ // Check if user is allowed
156
+ if (!pluginConfig.allowedUsers.includes(userId)) {
157
+ logger.debug(`slack: ignoring message from non-allowed user ${userId}`);
158
+ return;
159
+ }
160
+ // Resolve agent from bindings
161
+ const agentId = core.router.resolveAgent("slack", channelId);
162
+ if (!agentId) {
163
+ logger.debug(`slack: no agent bound to channel ${channelId}`);
164
+ return;
165
+ }
166
+ // Check if message is a slash command (text-based, not actual Slack slash commands)
167
+ const trimmed = text.trim();
168
+ if (pluginConfig.commands.includes(trimmed)) {
169
+ const cmdResponse = await handleSlashCommand(trimmed, agentId, channelId, userId);
170
+ if (cmdResponse) {
171
+ await say({
172
+ text: cmdResponse,
173
+ thread_ts: threadTs ?? messageTs,
174
+ });
175
+ }
176
+ return;
177
+ }
178
+ // Add hourglass reaction while processing
179
+ try {
180
+ await client.reactions.add({
181
+ channel: channelId,
182
+ timestamp: messageTs,
183
+ name: "hourglass_flowing_sand",
184
+ });
185
+ }
186
+ catch {
187
+ // Reaction may fail if already added or permissions issue; non-fatal
188
+ }
189
+ try {
190
+ // Build IncomingMessage
191
+ const incoming = {
192
+ from: {
193
+ gateway: "slack",
194
+ channel: channelId,
195
+ user: userId,
196
+ userId,
197
+ messageId: messageTs,
198
+ },
199
+ to: { agent: agentId },
200
+ content: text,
201
+ attachments: [],
202
+ };
203
+ // Route the message
204
+ const response = await core.router.route(incoming);
205
+ // Convert and send response
206
+ const mrkdwn = markdownToMrkdwn(response);
207
+ const chunks = splitMessage(mrkdwn);
208
+ for (const chunk of chunks) {
209
+ await say({
210
+ text: chunk,
211
+ blocks: [
212
+ {
213
+ type: "section",
214
+ text: { type: "mrkdwn", text: chunk },
215
+ },
216
+ ],
217
+ thread_ts: threadTs ?? messageTs,
218
+ });
219
+ }
220
+ }
221
+ catch (err) {
222
+ const errMsg = err instanceof Error ? err.message : String(err);
223
+ logger.error(`slack: error handling message: ${errMsg}`);
224
+ await say({
225
+ text: `:warning: Error: ${errMsg}`,
226
+ thread_ts: threadTs ?? messageTs,
227
+ });
228
+ }
229
+ finally {
230
+ // Remove hourglass reaction
231
+ try {
232
+ await client.reactions.remove({
233
+ channel: channelId,
234
+ timestamp: messageTs,
235
+ name: "hourglass_flowing_sand",
236
+ });
237
+ }
238
+ catch {
239
+ // Non-fatal
240
+ }
241
+ }
242
+ });
243
+ }
244
+ // ── Return the CcgPlugin object ──────────────────────────────────────────
245
+ return {
246
+ name: "slack-gateway",
247
+ type: "gateway",
248
+ async init(coreRef) {
249
+ core = coreRef;
250
+ // Resolve all tokens from env vars
251
+ for (const [botId, botConfig] of Object.entries(pluginConfig.bots)) {
252
+ const token = resolveToken(botConfig.token);
253
+ const signingSecret = resolveToken(botConfig.signingSecret);
254
+ const appToken = resolveToken(botConfig.appToken);
255
+ resolvedTokens.set(botId, { token, signingSecret, appToken });
256
+ }
257
+ logger.info(`slack-gateway: initialized with ${Object.keys(pluginConfig.bots).length} bot(s)`);
258
+ },
259
+ async start() {
260
+ for (const [botId, tokens] of resolvedTokens.entries()) {
261
+ const app = new App({
262
+ token: tokens.token,
263
+ signingSecret: tokens.signingSecret,
264
+ appToken: tokens.appToken,
265
+ socketMode: true,
266
+ logLevel: "ERROR",
267
+ });
268
+ registerHandlers(app, botId);
269
+ await app.start();
270
+ bots.push({ app, botId });
271
+ logger.info(`slack-gateway: bot "${botId}" started in socket mode`);
272
+ }
273
+ },
274
+ async stop() {
275
+ for (const bot of bots) {
276
+ try {
277
+ await bot.app.stop();
278
+ logger.info(`slack-gateway: bot "${bot.botId}" stopped`);
279
+ }
280
+ catch (err) {
281
+ const errMsg = err instanceof Error ? err.message : String(err);
282
+ logger.error(`slack-gateway: error stopping bot "${bot.botId}": ${errMsg}`);
283
+ }
284
+ }
285
+ bots.length = 0;
286
+ },
287
+ // Expose sendToChannel for cross-agent messaging
288
+ sendToChannel,
289
+ };
290
+ }
291
+ //# sourceMappingURL=slack-gateway.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slack-gateway.js","sourceRoot":"","sources":["../../src/plugins/slack-gateway.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import type { AgentRegistry } from "./agents.js";
2
+ import type { SessionManager } from "./sessions.js";
3
+ import type { ContextBuilder } from "./context.js";
4
+ import type { CCSpawner } from "./spawner.js";
5
+ import type { IncomingMessage, BindingConfig } from "./types.js";
6
+ export declare class MessageRouter {
7
+ private agents;
8
+ private sessions;
9
+ private context;
10
+ private spawner;
11
+ private bindings;
12
+ constructor(agents: AgentRegistry, sessions: SessionManager, context: ContextBuilder, spawner: CCSpawner, bindings: BindingConfig[]);
13
+ /**
14
+ * Resolve which agent handles a message based on bindings.
15
+ * Given (gateway, channelId), find the matching binding and return binding.agent.
16
+ */
17
+ resolveAgent(gateway: string, channelId: string): string | undefined;
18
+ /**
19
+ * Full message dispatch pipeline.
20
+ *
21
+ * 1. Look up agent config from registry
22
+ * 2. Derive session key: {agentId}:{gateway}:{channel}
23
+ * 3. Get or create session
24
+ * 4. Append user message to session
25
+ * 5. Build context
26
+ * 6. Spawn claude --print
27
+ * 7. Append assistant response to session (or error on failure)
28
+ * 8. Return response text
29
+ */
30
+ route(message: IncomingMessage): Promise<string>;
31
+ /**
32
+ * Add a binding at runtime.
33
+ */
34
+ addBinding(binding: BindingConfig): void;
35
+ /**
36
+ * Get all bindings for a given agent.
37
+ */
38
+ getBindingsForAgent(agentId: string): BindingConfig[];
39
+ /**
40
+ * Get the primary (first) binding for an agent.
41
+ */
42
+ getPrimaryBinding(agentId: string): BindingConfig | undefined;
43
+ }
44
+ //# sourceMappingURL=router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAIjE,qBAAa,aAAa;IAEtB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,QAAQ;gBAJR,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,SAAS,EAClB,QAAQ,EAAE,aAAa,EAAE;IAGnC;;;OAGG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAOpE;;;;;;;;;;;OAWG;IACG,KAAK,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC;IAgEtD;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IAIxC;;OAEG;IACH,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,EAAE;IAIrD;;OAEG;IACH,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;CAG9D"}