acp-discord 0.9.0-beta.2 → 0.9.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/daemon.js CHANGED
@@ -213,24 +213,31 @@ var SessionManager = class {
213
213
  console.error("Failed to save session map:", err);
214
214
  }
215
215
  }
216
- async prompt(channelId, text, agentName, agentConfig, requestorId, mcpServers) {
217
- console.log(`[MCP] prompt: channel=${channelId} mcpServers=${mcpServers ? `[${mcpServers.length} server(s)]` : "undefined"}`);
216
+ async prompt(channelId, text, agentName, agentConfig, requestorId, mcpServers, images) {
217
+ console.log(`[MCP] prompt: channel=${channelId} mcpServers=${mcpServers ? `[${mcpServers.length} server(s)]` : "undefined"}${images?.length ? ` images=${images.length}` : ""}`);
218
218
  const session = await this.getOrCreate(channelId, agentName, agentConfig, requestorId, mcpServers);
219
219
  session.lastActivity = Date.now();
220
220
  this.resetIdleTimer(session, agentConfig.idle_timeout);
221
221
  if (session.prompting) {
222
- session.queue.push({ text, requestorId });
222
+ session.queue.push({ text, requestorId, images });
223
223
  return "queued";
224
224
  }
225
- return this.executePrompt(session, text, requestorId, agentConfig);
225
+ return this.executePrompt(session, text, requestorId, agentConfig, images);
226
226
  }
227
- async executePrompt(session, text, requestorId, agentConfig) {
227
+ async executePrompt(session, text, requestorId, agentConfig, images) {
228
228
  session.prompting = true;
229
229
  session.activePromptRequestorId = requestorId;
230
230
  try {
231
+ const contentBlocks = [];
232
+ if (text) contentBlocks.push({ type: "text", text });
233
+ if (images?.length) {
234
+ for (const img of images) {
235
+ contentBlocks.push({ type: "image", data: img.data, mimeType: img.mimeType });
236
+ }
237
+ }
231
238
  const result = await session.connection.prompt({
232
239
  sessionId: session.sessionId,
233
- prompt: [{ type: "text", text }]
240
+ prompt: contentBlocks
234
241
  });
235
242
  this.handlers.onPromptComplete(session.channelId, result.stopReason);
236
243
  return result.stopReason;
@@ -244,7 +251,7 @@ var SessionManager = class {
244
251
  if (this.sessions.has(session.channelId)) {
245
252
  const next = session.queue.shift();
246
253
  if (next) {
247
- this.executePrompt(session, next.text, next.requestorId, agentConfig).catch((err) => {
254
+ this.executePrompt(session, next.text, next.requestorId, agentConfig, next.images).catch((err) => {
248
255
  console.error(`Queued prompt failed for channel ${session.channelId}:`, err);
249
256
  });
250
257
  }
@@ -1663,9 +1670,44 @@ async function startDiscordBot(config, sessionsPath, configPath) {
1663
1670
  function getGuildId(message) {
1664
1671
  return message.guildId ?? null;
1665
1672
  }
1666
- async function promptWithMcp(channelId, text, agentName, guildId, agentConfig, requestorId) {
1673
+ async function promptWithMcp(channelId, text, agentName, guildId, agentConfig, requestorId, images) {
1667
1674
  const mcpServers = guildId ? buildMcpServers(channelId, agentName, guildId) : void 0;
1668
- await sessionManager.prompt(channelId, text, agentName, agentConfig, requestorId, mcpServers);
1675
+ await sessionManager.prompt(channelId, text, agentName, agentConfig, requestorId, mcpServers, images);
1676
+ }
1677
+ const MAX_IMAGE_BYTES = 10 * 1024 * 1024;
1678
+ async function fetchImage(url, mimeType, label) {
1679
+ try {
1680
+ const res = await fetch(url);
1681
+ if (!res.ok) {
1682
+ console.warn(`Failed to fetch image ${label}: ${res.status} ${res.statusText}`);
1683
+ return null;
1684
+ }
1685
+ const buf = Buffer.from(await res.arrayBuffer());
1686
+ if (buf.byteLength > MAX_IMAGE_BYTES) {
1687
+ console.warn(`Skipping image ${label}: ${buf.byteLength} bytes exceeds ${MAX_IMAGE_BYTES}`);
1688
+ return null;
1689
+ }
1690
+ return { data: buf.toString("base64"), mimeType };
1691
+ } catch (err) {
1692
+ console.warn(`Error fetching image ${label}:`, err);
1693
+ return null;
1694
+ }
1695
+ }
1696
+ async function extractMessageImages(message) {
1697
+ const out = [];
1698
+ for (const att of message.attachments.values()) {
1699
+ if (!att.contentType?.startsWith("image/")) {
1700
+ console.log(`Ignoring non-image attachment ${att.name} (contentType=${att.contentType})`);
1701
+ continue;
1702
+ }
1703
+ if (att.size > MAX_IMAGE_BYTES) {
1704
+ console.warn(`Skipping image attachment ${att.name}: ${att.size} bytes exceeds ${MAX_IMAGE_BYTES}`);
1705
+ continue;
1706
+ }
1707
+ const img = await fetchImage(att.url, att.contentType, att.name);
1708
+ if (img) out.push(img);
1709
+ }
1710
+ return out;
1669
1711
  }
1670
1712
  discordClient = new Client({
1671
1713
  intents: [
@@ -1690,6 +1732,8 @@ async function startDiscordBot(config, sessionsPath, configPath) {
1690
1732
  console.log(`Discord bot ready: ${c.user.tag}`);
1691
1733
  const askCommand = new SlashCommandBuilder().setName("ask").setDescription("Ask the coding agent a question").addStringOption(
1692
1734
  (opt) => opt.setName("message").setDescription("Your message").setRequired(true)
1735
+ ).addAttachmentOption(
1736
+ (opt) => opt.setName("image").setDescription("Optional image attachment").setRequired(false)
1693
1737
  );
1694
1738
  const clearCommand = new SlashCommandBuilder().setName("clear").setDescription("Clear the agent session and start fresh");
1695
1739
  const rest = new REST().setToken(config.discord.token);
@@ -1710,8 +1754,9 @@ async function startDiscordBot(config, sessionsPath, configPath) {
1710
1754
  const isMention = message.mentions.has(discordClient.user);
1711
1755
  if (!resolved.autoReply && !isMention) return;
1712
1756
  const text = message.content.replace(/<@!?\d+>/g, "").trim();
1713
- if (!text) {
1714
- await message.reply("Please provide a message.");
1757
+ const images = await extractMessageImages(message);
1758
+ if (!text && images.length === 0) {
1759
+ await message.reply("Please provide a message or an image.");
1715
1760
  return;
1716
1761
  }
1717
1762
  startTyping(channelId);
@@ -1719,7 +1764,7 @@ async function startDiscordBot(config, sessionsPath, configPath) {
1719
1764
  await message.reply("\u23F3 Agent is working. Your message has been queued.");
1720
1765
  }
1721
1766
  try {
1722
- await promptWithMcp(channelId, text, resolved.agentName, getGuildId(message), resolved.agent, message.author.id);
1767
+ await promptWithMcp(channelId, text, resolved.agentName, getGuildId(message), resolved.agent, message.author.id, images);
1723
1768
  } catch (err) {
1724
1769
  stopTyping(channelId);
1725
1770
  const errMsg = err instanceof Error ? err.message : String(err);
@@ -1771,7 +1816,19 @@ async function startDiscordBot(config, sessionsPath, configPath) {
1771
1816
  return;
1772
1817
  }
1773
1818
  const text = interaction.options.getString("message", true);
1819
+ const imageOpt = interaction.options.getAttachment("image");
1774
1820
  await interaction.deferReply();
1821
+ const images = [];
1822
+ if (imageOpt) {
1823
+ if (!imageOpt.contentType?.startsWith("image/")) {
1824
+ console.log(`/ask: ignoring non-image attachment ${imageOpt.name} (contentType=${imageOpt.contentType})`);
1825
+ } else if (imageOpt.size > MAX_IMAGE_BYTES) {
1826
+ console.warn(`/ask: skipping oversized image ${imageOpt.name}: ${imageOpt.size} bytes`);
1827
+ } else {
1828
+ const img = await fetchImage(imageOpt.url, imageOpt.contentType, imageOpt.name);
1829
+ if (img) images.push(img);
1830
+ }
1831
+ }
1775
1832
  startTyping(channelId);
1776
1833
  if (sessionManager.isPrompting(channelId)) {
1777
1834
  await interaction.editReply("\u23F3 Agent is working. Your message has been queued.");
@@ -1780,7 +1837,7 @@ async function startDiscordBot(config, sessionsPath, configPath) {
1780
1837
  }
1781
1838
  try {
1782
1839
  const guildId = interaction.guildId ?? null;
1783
- await promptWithMcp(channelId, text, resolved.agentName, guildId, resolved.agent, interaction.user.id);
1840
+ await promptWithMcp(channelId, text, resolved.agentName, guildId, resolved.agent, interaction.user.id, images);
1784
1841
  } catch (err) {
1785
1842
  stopTyping(channelId);
1786
1843
  const errMsg = err instanceof Error ? err.message : String(err);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/daemon/index.ts","../src/daemon/discord-bot.ts","../src/daemon/channel-router.ts","../src/daemon/session-manager.ts","../src/daemon/acp-client.ts","../src/daemon/permission-ui.ts","../src/daemon/message-bridge.ts","../src/daemon/ipc-server.ts","../src/daemon/task-scheduler.ts","../src/daemon/task-runner.ts"],"sourcesContent":["import { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { loadConfig } from \"../shared/config.js\";\nimport { writePid, removePid } from \"../cli/pid.js\";\nimport { startDiscordBot } from \"./discord-bot.js\";\n\nconst CONFIG_DIR = join(homedir(), \".acp-discord\");\nconst CONFIG_PATH = join(CONFIG_DIR, \"config.toml\");\nconst PID_PATH = join(CONFIG_DIR, \"daemon.pid\");\nconst SESSIONS_PATH = join(CONFIG_DIR, \"sessions.json\");\n\nexport async function runDaemon(): Promise<void> {\n // Global error handlers — graceful shutdown on fatal errors, continue on rejections\n process.on(\"uncaughtException\", (err) => {\n console.error(\"Uncaught exception:\", err);\n // Allow event loop to flush logs, then exit for restart by service manager\n setTimeout(() => process.exit(1), 1000);\n });\n\n process.on(\"unhandledRejection\", (reason) => {\n console.error(\"Unhandled rejection:\", reason);\n // Unhandled rejections are less severe — log but continue\n });\n\n // Load config first — if it fails, no stale PID file is left behind (#12)\n const config = loadConfig(CONFIG_PATH);\n\n writePid(PID_PATH, process.pid);\n process.on(\"exit\", () => removePid(PID_PATH));\n\n console.log(`acp-discord daemon started (PID: ${process.pid})`);\n console.log(`Loaded config: ${Object.keys(config.channels).length} channel(s)`);\n\n await startDiscordBot(config, SESSIONS_PATH, CONFIG_PATH);\n}\n\nif (process.env.ACP_DISCORD_DAEMON === \"1\") {\n runDaemon().catch((err) => {\n console.error(\"Daemon failed:\", err);\n process.exit(1);\n });\n}\n","import {\n Client,\n GatewayIntentBits,\n Events,\n REST,\n Routes,\n SlashCommandBuilder,\n ActionRowBuilder,\n ButtonBuilder,\n ButtonStyle,\n EmbedBuilder,\n type Message,\n type TextChannel,\n} from \"discord.js\";\nimport { resolve as resolvePath, dirname } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport type { AppConfig } from \"../shared/types.js\";\nimport { saveConfig, resolveChannelConfig } from \"../shared/config.js\";\nimport { ChannelRouter } from \"./channel-router.js\";\nimport { SessionManager, type McpServerConfig } from \"./session-manager.js\";\nimport { sendPermissionRequest } from \"./permission-ui.js\";\nimport { splitMessage, formatToolSummary, formatDiff, type ToolStatus } from \"./message-bridge.js\";\nimport type { AcpEventHandlers, DiffContent } from \"./acp-client.js\";\nimport { IpcServer, DEFAULT_IPC_SOCKET_PATH } from \"./ipc-server.js\";\nimport { TaskScheduler } from \"./task-scheduler.js\";\nimport { runTask } from \"./task-runner.js\";\n\nexport async function startDiscordBot(config: AppConfig, sessionsPath: string, configPath: string): Promise<void> {\n let currentConfig = config;\n const router = new ChannelRouter(currentConfig);\n\n // Per-channel state for display\n const toolStates = new Map<string, Map<string, { title: string; status: ToolStatus; rawInput?: Record<string, unknown> }>>();\n const toolSummaryMessages = new Map<string, Message>();\n const replyBuffers = new Map<string, string>();\n const replyMessages = new Map<string, Message>();\n const flushTimers = new Map<string, NodeJS.Timeout>();\n // channelId -> toolCallId -> DiffContent[]\n const pendingDiffs = new Map<string, Map<string, DiffContent[]>>();\n // channelId -> Set of toolCallIds whose diffs were already shown at permission-request time\n const permissionDiffShown = new Map<string, Set<string>>();\n\n // Typing indicator state: channelId -> interval timer\n const typingIntervals = new Map<string, NodeJS.Timeout>();\n\n function startTyping(channelId: string) {\n if (typingIntervals.has(channelId)) return;\n // Set a placeholder immediately to prevent concurrent calls from creating duplicate intervals\n const placeholder = setTimeout(() => {}, 0);\n typingIntervals.set(channelId, placeholder);\n clearTimeout(placeholder);\n\n fetchChannel(channelId).then((channel) => {\n if (!channel) { typingIntervals.delete(channelId); return; }\n // Re-check: stopTyping may have been called while we awaited\n if (!typingIntervals.has(channelId)) return;\n channel.sendTyping().catch(() => {});\n const interval = setInterval(() => {\n channel.sendTyping().catch(() => {});\n }, 8000);\n typingIntervals.set(channelId, interval);\n }).catch(() => {\n typingIntervals.delete(channelId);\n });\n }\n\n function stopTyping(channelId: string) {\n const interval = typingIntervals.get(channelId);\n if (interval) {\n clearInterval(interval);\n typingIntervals.delete(channelId);\n }\n }\n\n let discordClient: Client;\n\n // --- Confirmation UI for MCP tool actions ---\n\n // Pending confirmation requests from MCP servers (requestId -> { resolver, allowedUserId })\n const pendingConfirmations = new Map<string, { resolve: (approved: boolean) => void; allowedUserId: string | null }>();\n\n async function handleConfirmAction(sourceChannelId: string, description: string, details: string): Promise<boolean> {\n const channel = await fetchChannel(sourceChannelId);\n if (!channel) return false;\n\n // Only the user who triggered the current prompt can approve\n const allowedUserId = sessionManager.getActiveRequestorId(sourceChannelId);\n\n const requestId = `mcp_confirm_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n\n const embed = new EmbedBuilder()\n .setColor(0xffa500)\n .setTitle(`Channel Action: ${description}`)\n .setDescription(details || \"No additional details\")\n .setTimestamp();\n\n const row = new ActionRowBuilder<ButtonBuilder>().addComponents(\n new ButtonBuilder()\n .setCustomId(`mcp_approve_${requestId}`)\n .setLabel(\"\\u2705 Approve\")\n .setStyle(ButtonStyle.Success),\n new ButtonBuilder()\n .setCustomId(`mcp_reject_${requestId}`)\n .setLabel(\"\\u274C Reject\")\n .setStyle(ButtonStyle.Danger),\n );\n\n const msg = await channel.send({ embeds: [embed], components: [row] });\n\n return new Promise<boolean>((resolve) => {\n const timeout = setTimeout(() => {\n pendingConfirmations.delete(requestId);\n msg.delete().catch(() => msg.edit({ components: [] }).catch(() => {}));\n resolve(false);\n }, 5 * 60 * 1000); // 5 minute timeout\n\n pendingConfirmations.set(requestId, {\n resolve: (approved: boolean) => {\n clearTimeout(timeout);\n pendingConfirmations.delete(requestId);\n msg.delete().catch(() => msg.edit({ components: [] }).catch(() => {}));\n resolve(approved);\n },\n allowedUserId,\n });\n });\n }\n\n // --- IPC Server ---\n\n // --- Task Scheduler ---\n\n const taskScheduler = new TaskScheduler(async (task) => {\n const resolved = router.resolve(task.channel_id);\n if (!resolved) {\n console.error(`TaskScheduler: no resolved config for channel ${task.channel_id}`);\n taskScheduler.logRun(task.id, {\n startedAt: new Date(),\n completedAt: new Date(),\n durationMs: 0,\n status: \"error\",\n output: \"\",\n error: `No resolved config for channel ${task.channel_id}`,\n });\n return;\n }\n\n const channel = await fetchChannel(task.channel_id);\n const guildId = channel?.guild?.id ?? null;\n const mcpServers = guildId ? buildMcpServers(task.channel_id, task.agent_name, guildId) : [];\n\n const startedAt = new Date();\n const result = await runTask(resolved.agent, task.prompt, mcpServers);\n const completedAt = new Date();\n const durationMs = completedAt.getTime() - startedAt.getTime();\n\n taskScheduler.logRun(task.id, {\n startedAt,\n completedAt,\n durationMs,\n status: result.error ? \"error\" : \"success\",\n output: result.output,\n error: result.error,\n });\n\n const shouldNotify =\n task.notify === \"always\" ||\n (task.notify === \"on_error\" && result.error);\n\n if (shouldNotify && channel) {\n const text = result.error ?? result.output;\n const chunks = splitMessage(text || \"(no output)\");\n for (const chunk of chunks) {\n await channel.send(chunk);\n }\n }\n });\n\n const ipcServer = new IpcServer(\n {\n registerChannel(channelId, agentName, autoReply) {\n router.registerDynamic(channelId, agentName, autoReply);\n console.log(`IPC: registered dynamic channel ${channelId} -> agent ${agentName}`);\n },\n unregisterChannel(channelId) {\n router.unregisterDynamic(channelId);\n console.log(`IPC: unregistered dynamic channel ${channelId}`);\n },\n confirmAction: handleConfirmAction,\n\n async bindChannel(channelId, agentName, opts, guildId) {\n // Validate guild ownership if guildId provided\n if (guildId) {\n try {\n const channel = await discordClient.channels.fetch(channelId);\n const channelGuildId = channel && \"guildId\" in channel ? (channel as { guildId: string }).guildId : null;\n if (channelGuildId !== guildId) {\n return { success: false, error: `Channel ${channelId} does not belong to guild ${guildId}` };\n }\n } catch {\n return { success: false, error: `Cannot verify channel ${channelId}` };\n }\n }\n\n // Validate agent exists\n if (!currentConfig.agents[agentName]) {\n return { success: false, error: `Unknown agent \"${agentName}\"` };\n }\n\n // Update in-memory config\n currentConfig.channels[channelId] = {\n agent: agentName,\n cwd: opts.cwd,\n auto_reply: opts.autoReply ?? true,\n discord_tools: opts.discordTools,\n };\n\n // Register in router\n router.registerDynamic(channelId, agentName, opts.autoReply ?? true);\n\n // Persist to config.toml\n try {\n saveConfig(configPath, currentConfig);\n } catch (err) {\n // Rollback in-memory state on write failure\n delete currentConfig.channels[channelId];\n router.unregisterDynamic(channelId);\n const message = err instanceof Error ? err.message : String(err);\n return { success: false, error: `Failed to save config: ${message}` };\n }\n\n console.log(`IPC: bound channel ${channelId} -> agent ${agentName}`);\n return { success: true };\n },\n\n async unbindChannel(channelId, guildId) {\n // Validate guild ownership if guildId provided\n if (guildId) {\n try {\n const channel = await discordClient.channels.fetch(channelId);\n const channelGuildId = channel && \"guildId\" in channel ? (channel as { guildId: string }).guildId : null;\n if (channelGuildId !== guildId) {\n return { success: false, error: `Channel ${channelId} does not belong to guild ${guildId}` };\n }\n } catch {\n // Channel may have been deleted — allow unbinding if it exists in config\n }\n }\n\n if (!currentConfig.channels[channelId]) {\n return { success: false, error: `Channel ${channelId} is not bound` };\n }\n\n // Save for rollback\n const previous = currentConfig.channels[channelId];\n\n // Update in-memory\n delete currentConfig.channels[channelId];\n router.unregisterDynamic(channelId);\n\n // Persist\n try {\n saveConfig(configPath, currentConfig);\n } catch (err) {\n // Rollback\n currentConfig.channels[channelId] = previous;\n router.registerDynamic(channelId, previous.agent, previous.auto_reply ?? false);\n const message = err instanceof Error ? err.message : String(err);\n return { success: false, error: `Failed to save config: ${message}` };\n }\n\n console.log(`IPC: unbound channel ${channelId}`);\n return { success: true };\n },\n\n async listBindings(guildId) {\n const entries = Object.entries(currentConfig.channels);\n const bindings = [];\n for (const [channelId, ch] of entries) {\n // Filter by guild if specified\n if (guildId) {\n try {\n const channel = discordClient.channels.cache.get(channelId) ?? await discordClient.channels.fetch(channelId);\n const channelGuildId = channel && \"guildId\" in channel ? (channel as { guildId: string }).guildId : null;\n if (channelGuildId !== guildId) continue;\n } catch {\n continue;\n }\n }\n const resolved = resolveChannelConfig(currentConfig, channelId);\n bindings.push({\n channelId,\n agent: ch.agent,\n cwd: ch.cwd ?? currentConfig.agents[ch.agent]?.cwd,\n autoReply: ch.auto_reply ?? false,\n discordTools: resolved?.agent.discord_tools ?? false,\n });\n }\n return { success: true, bindings };\n },\n createTask(params) {\n try {\n const task = taskScheduler.createTask(params as Parameters<typeof taskScheduler.createTask>[0]);\n return { task };\n } catch (err) {\n return { error: err instanceof Error ? err.message : String(err) };\n }\n },\n listTasks(channelId) {\n return { tasks: taskScheduler.listTasks(channelId) };\n },\n updateTask(taskId, updates, channelId) {\n const task = taskScheduler.updateTask(taskId, updates, channelId);\n if (!task) return { error: `Task ${taskId} not found` };\n return { task };\n },\n deleteTask(taskId, channelId) {\n const deleted = taskScheduler.deleteTask(taskId, channelId);\n if (!deleted) return { error: `Task ${taskId} not found` };\n return { deleted: true };\n },\n getTaskLogs(taskId, channelId) {\n return { logs: taskScheduler.getTaskLogs(taskId, channelId) };\n },\n },\n DEFAULT_IPC_SOCKET_PATH,\n );\n\n const handlers: AcpEventHandlers = {\n onToolCall(channelId, toolCallId, title, _kind, status, diffs, rawInput) {\n startTyping(channelId);\n if (!toolStates.has(channelId)) toolStates.set(channelId, new Map());\n toolStates.get(channelId)!.set(toolCallId, { title, status: status as ToolStatus, rawInput });\n accumulateDiffs(channelId, toolCallId, diffs);\n updateToolSummaryMessage(channelId);\n if (status === \"completed\") sendDiffsForTool(channelId, toolCallId);\n },\n\n onToolCallUpdate(channelId, toolCallId, status, diffs, rawInput) {\n const tools = toolStates.get(channelId);\n const tool = tools?.get(toolCallId);\n if (tool) {\n tool.status = status as ToolStatus;\n if (rawInput && !tool.rawInput) tool.rawInput = rawInput;\n accumulateDiffs(channelId, toolCallId, diffs);\n updateToolSummaryMessage(channelId);\n if (status === \"completed\") sendDiffsForTool(channelId, toolCallId);\n }\n },\n\n onAgentMessageChunk(channelId, text) {\n startTyping(channelId);\n const current = replyBuffers.get(channelId) ?? \"\";\n replyBuffers.set(channelId, current + text);\n scheduleFlushReply(channelId);\n },\n\n async onPermissionRequest(channelId, requestorId, toolCall, options, diffs) {\n const channel = await fetchChannel(channelId);\n if (!channel) return { outcome: \"cancelled\" as const };\n const result = await sendPermissionRequest(channel, toolCall.title, toolCall.kind, options, requestorId, diffs);\n if (result.diffsSent) {\n if (!permissionDiffShown.has(channelId)) permissionDiffShown.set(channelId, new Set());\n permissionDiffShown.get(channelId)!.add(toolCall.toolCallId);\n }\n return result;\n },\n\n onPromptComplete(channelId, _stopReason) {\n stopTyping(channelId);\n // Final flush\n flushReply(channelId, true);\n // Remove stop button from tool summary\n removeStopButton(channelId);\n // Clear state for next turn\n toolStates.delete(channelId);\n toolSummaryMessages.delete(channelId);\n replyBuffers.delete(channelId);\n replyMessages.delete(channelId);\n pendingDiffs.delete(channelId);\n permissionDiffShown.delete(channelId);\n },\n };\n\n const sessionManager = new SessionManager(handlers, sessionsPath);\n\n // --- MCP server config builder ---\n\n function buildMcpServers(channelId: string, agentName: string, guildId: string): McpServerConfig[] {\n const resolved = router.resolve(channelId);\n const discordToolsEnabled = resolved?.agent.discord_tools ?? false;\n const scheduledTasksEnabled = resolved?.agent.scheduled_tasks ?? false;\n console.log(`[MCP] buildMcpServers: channel=${channelId} agent=${agentName} discord_tools=${discordToolsEnabled} scheduled_tasks=${scheduledTasksEnabled}`);\n\n const mcpConfig: McpServerConfig[] = [];\n\n // Resolve the built MCP server script path relative to this package\n // import.meta.dirname is available in Node 21.2+; fall back to fileURLToPath for Node 18\n const currentDir = import.meta.dirname ?? dirname(fileURLToPath(import.meta.url));\n\n if (discordToolsEnabled) {\n const mcpScriptPath = resolvePath(currentDir, \"mcp-discord-channels.js\");\n const scriptExists = existsSync(mcpScriptPath);\n\n console.log(`[MCP] buildMcpServers: mcpScriptPath=${mcpScriptPath} exists=${scriptExists}`);\n if (!scriptExists) {\n console.warn(`[MCP] WARNING: MCP script not found at ${mcpScriptPath} — Discord tools will not work`);\n }\n\n mcpConfig.push({\n name: \"discord-channels\",\n command: \"node\",\n args: [mcpScriptPath],\n env: [\n { name: \"DISCORD_TOKEN\", value: config.discord.token },\n { name: \"GUILD_ID\", value: guildId },\n { name: \"IPC_SOCKET_PATH\", value: DEFAULT_IPC_SOCKET_PATH },\n { name: \"AGENT_NAME\", value: agentName },\n { name: \"SOURCE_CHANNEL_ID\", value: channelId },\n ],\n });\n }\n\n if (scheduledTasksEnabled) {\n const tasksMcpPath = resolvePath(currentDir, \"mcp-scheduled-tasks.js\");\n const tasksScriptExists = existsSync(tasksMcpPath);\n\n console.log(`[MCP] buildMcpServers: tasksMcpPath=${tasksMcpPath} exists=${tasksScriptExists}`);\n if (!tasksScriptExists) {\n console.warn(`[MCP] WARNING: MCP script not found at ${tasksMcpPath} — Scheduled tasks tools will not work`);\n } else {\n mcpConfig.push({\n name: \"scheduled-tasks\",\n command: \"node\",\n args: [tasksMcpPath],\n env: [\n { name: \"IPC_SOCKET_PATH\", value: DEFAULT_IPC_SOCKET_PATH },\n { name: \"AGENT_NAME\", value: agentName },\n { name: \"SOURCE_CHANNEL_ID\", value: channelId },\n ],\n });\n }\n }\n\n console.log(`[MCP] buildMcpServers: returning ${mcpConfig.length} MCP server(s):`, JSON.stringify(mcpConfig.map(s => ({ name: s.name, command: s.command, args: s.args }))));\n return mcpConfig;\n }\n\n // --- Display helpers ---\n\n function accumulateDiffs(channelId: string, toolCallId: string, diffs: DiffContent[]) {\n if (diffs.length === 0) return;\n if (!pendingDiffs.has(channelId)) pendingDiffs.set(channelId, new Map());\n const channelDiffs = pendingDiffs.get(channelId)!;\n const existing = channelDiffs.get(toolCallId) ?? [];\n channelDiffs.set(toolCallId, existing.concat(diffs));\n }\n\n async function sendDiffsForTool(channelId: string, toolCallId: string) {\n // Skip if diffs were already shown at permission-request time\n const shownSet = permissionDiffShown.get(channelId);\n if (shownSet?.has(toolCallId)) {\n shownSet.delete(toolCallId);\n pendingDiffs.get(channelId)?.delete(toolCallId);\n return;\n }\n\n const channelDiffs = pendingDiffs.get(channelId);\n const diffs = channelDiffs?.get(toolCallId);\n if (!diffs || diffs.length === 0) return;\n\n const channel = await fetchChannel(channelId);\n if (!channel) return;\n\n const messages = formatDiff(diffs);\n for (const msg of messages) {\n await channel.send({ content: msg, allowedMentions: { parse: [] as const } });\n }\n\n channelDiffs!.delete(toolCallId);\n }\n\n async function fetchChannel(channelId: string): Promise<TextChannel | null> {\n const cached = discordClient.channels.cache.get(channelId) as TextChannel | undefined;\n if (cached) return cached;\n try {\n const fetched = await discordClient.channels.fetch(channelId);\n return fetched as TextChannel;\n } catch {\n return null;\n }\n }\n\n async function updateToolSummaryMessage(channelId: string) {\n const tools = toolStates.get(channelId);\n if (!tools) return;\n\n const content = formatToolSummary(tools);\n const channel = await fetchChannel(channelId);\n if (!channel) return;\n\n const stopButton = new ActionRowBuilder<ButtonBuilder>().addComponents(\n new ButtonBuilder()\n .setCustomId(`stop_${channelId}`)\n .setLabel(\"\\u23F9 Stop\")\n .setStyle(ButtonStyle.Secondary),\n );\n\n const noMentions = { parse: [] as const };\n const existing = toolSummaryMessages.get(channelId);\n if (existing) {\n await existing.edit({ content, components: [stopButton], allowedMentions: noMentions }).catch(() => {});\n } else {\n const msg = await channel.send({ content, components: [stopButton], allowedMentions: noMentions });\n toolSummaryMessages.set(channelId, msg);\n }\n }\n\n async function removeStopButton(channelId: string) {\n const msg = toolSummaryMessages.get(channelId);\n if (msg) {\n const tools = toolStates.get(channelId);\n const content = tools ? formatToolSummary(tools) : msg.content;\n await msg.edit({ content, components: [], allowedMentions: { parse: [] as const } }).catch(() => {});\n }\n }\n\n function scheduleFlushReply(channelId: string) {\n if (flushTimers.has(channelId)) return;\n flushTimers.set(\n channelId,\n setTimeout(() => {\n flushTimers.delete(channelId);\n flushReply(channelId, false);\n }, 500),\n );\n }\n\n async function flushReply(channelId: string, final: boolean) {\n const timer = flushTimers.get(channelId);\n if (timer) {\n clearTimeout(timer);\n flushTimers.delete(channelId);\n }\n\n const buffer = replyBuffers.get(channelId);\n if (!buffer) return;\n\n const channel = await fetchChannel(channelId);\n if (!channel) return;\n\n if (final) {\n // Send final reply as new message(s), delete streaming message\n const existing = replyMessages.get(channelId);\n if (existing) await existing.delete().catch(() => {});\n replyMessages.delete(channelId);\n\n const chunks = splitMessage(buffer);\n for (const chunk of chunks) {\n await channel.send(chunk);\n }\n replyBuffers.delete(channelId);\n } else {\n // Streaming update: edit existing message\n const truncated = buffer.length > 2000 ? buffer.slice(buffer.length - 1900) + \"...\" : buffer;\n const existing = replyMessages.get(channelId);\n if (existing) {\n await existing.edit(truncated).catch(() => {});\n } else {\n const msg = await channel.send(truncated);\n replyMessages.set(channelId, msg);\n }\n }\n }\n\n // --- Helper: resolve guild ID from a channel ---\n\n function getGuildId(message: Message): string | null {\n return message.guildId ?? null;\n }\n\n // --- Helper: prompt with MCP servers ---\n\n async function promptWithMcp(channelId: string, text: string, agentName: string, guildId: string | null, agentConfig: typeof config.agents[string], requestorId: string): Promise<void> {\n const mcpServers = guildId ? buildMcpServers(channelId, agentName, guildId) : undefined;\n await sessionManager.prompt(channelId, text, agentName, agentConfig, requestorId, mcpServers);\n }\n\n // --- Discord client setup ---\n\n discordClient = new Client({\n intents: [\n GatewayIntentBits.Guilds,\n GatewayIntentBits.GuildMessages,\n GatewayIntentBits.MessageContent,\n ],\n });\n\n discordClient.on(\"error\", (err) => {\n console.error(\"Discord client error:\", err);\n });\n\n discordClient.on(\"warn\", (msg) => {\n console.warn(\"Discord client warning:\", msg);\n });\n\n discordClient.on(\"shardDisconnect\", (event, shardId) => {\n console.warn(`Shard ${shardId} disconnected (code: ${event.code})`);\n });\n\n discordClient.on(\"shardReconnecting\", (shardId) => {\n console.log(`Shard ${shardId} reconnecting...`);\n });\n\n discordClient.on(Events.ClientReady, async (c) => {\n console.log(`Discord bot ready: ${c.user.tag}`);\n\n // Register slash commands\n const askCommand = new SlashCommandBuilder()\n .setName(\"ask\")\n .setDescription(\"Ask the coding agent a question\")\n .addStringOption((opt) =>\n opt.setName(\"message\").setDescription(\"Your message\").setRequired(true),\n );\n\n const clearCommand = new SlashCommandBuilder()\n .setName(\"clear\")\n .setDescription(\"Clear the agent session and start fresh\");\n\n const rest = new REST().setToken(config.discord.token);\n try {\n await rest.put(Routes.applicationCommands(c.application.id), {\n body: [askCommand.toJSON(), clearCommand.toJSON()],\n });\n console.log(\"Registered /ask and /clear commands\");\n } catch (err) {\n console.error(\"Failed to register commands:\", err);\n }\n });\n\n // Handle @mention messages in configured channels\n discordClient.on(Events.MessageCreate, async (message: Message) => {\n if (message.author.bot) return;\n\n const channelId = message.channelId;\n const resolved = router.resolve(channelId);\n if (!resolved) return;\n\n const isMention = message.mentions.has(discordClient.user!);\n if (!resolved.autoReply && !isMention) return;\n\n // Strip mention prefix if present\n const text = message.content.replace(/<@!?\\d+>/g, \"\").trim();\n\n if (!text) {\n await message.reply(\"Please provide a message.\");\n return;\n }\n\n startTyping(channelId);\n\n if (sessionManager.isPrompting(channelId)) {\n await message.reply(\"\\u23F3 Agent is working. Your message has been queued.\");\n }\n\n try {\n await promptWithMcp(channelId, text, resolved.agentName, getGuildId(message), resolved.agent, message.author.id);\n } catch (err) {\n stopTyping(channelId);\n const errMsg = err instanceof Error ? err.message : String(err);\n console.error(`Prompt failed for channel ${channelId}:`, err);\n if (errMsg.includes(\"sessions are busy\")) {\n await message.reply(\"\\u23F3 All agent sessions are busy. Please wait for the current task to finish and try again.\").catch(() => {});\n } else {\n await message.reply(\"An error occurred while processing your request.\").catch(() => {});\n }\n }\n });\n\n // Handle stop button clicks and MCP confirmation buttons\n discordClient.on(Events.InteractionCreate, async (interaction) => {\n if (!interaction.isButton()) return;\n\n if (interaction.customId.startsWith(\"stop_\")) {\n const channelId = interaction.customId.replace(\"stop_\", \"\");\n const activeRequestor = sessionManager.getActiveRequestorId(channelId);\n\n // Only the user who triggered the current prompt can stop it\n if (activeRequestor && interaction.user.id !== activeRequestor) {\n await interaction.reply({ content: \"Only the user who started this prompt can stop it.\", ephemeral: true });\n return;\n }\n\n sessionManager.cancel(channelId);\n await interaction.update({ components: [] });\n }\n\n // Handle MCP confirmation buttons\n if (interaction.customId.startsWith(\"mcp_approve_\") || interaction.customId.startsWith(\"mcp_reject_\")) {\n const approved = interaction.customId.startsWith(\"mcp_approve_\");\n const requestId = interaction.customId.replace(/^mcp_(approve|reject)_/, \"\");\n const pending = pendingConfirmations.get(requestId);\n if (pending) {\n // Only the user who triggered the prompt can approve/reject\n if (pending.allowedUserId && interaction.user.id !== pending.allowedUserId) {\n await interaction.reply({ content: \"Only the user who started this prompt can approve or reject.\", ephemeral: true });\n return;\n }\n await interaction.deferUpdate();\n pending.resolve(approved);\n } else {\n await interaction.reply({ content: \"This confirmation has expired.\", ephemeral: true });\n }\n }\n });\n\n // Handle /ask command\n discordClient.on(Events.InteractionCreate, async (interaction) => {\n if (!interaction.isChatInputCommand()) return;\n if (interaction.commandName !== \"ask\") return;\n\n const channelId = interaction.channelId;\n const resolved = router.resolve(channelId);\n if (!resolved) {\n await interaction.reply({ content: \"This channel is not configured for ACP.\", ephemeral: true });\n return;\n }\n\n const text = interaction.options.getString(\"message\", true);\n await interaction.deferReply();\n startTyping(channelId);\n\n if (sessionManager.isPrompting(channelId)) {\n await interaction.editReply(\"\\u23F3 Agent is working. Your message has been queued.\");\n } else {\n await interaction.editReply(`\\uD83D\\uDCAC Processing: ${text.slice(0, 100)}...`);\n }\n\n try {\n const guildId = interaction.guildId ?? null;\n await promptWithMcp(channelId, text, resolved.agentName, guildId, resolved.agent, interaction.user.id);\n } catch (err) {\n stopTyping(channelId);\n const errMsg = err instanceof Error ? err.message : String(err);\n console.error(`Prompt failed for channel ${channelId}:`, err);\n const reply = errMsg.includes(\"sessions are busy\")\n ? \"\\u23F3 All agent sessions are busy. Please wait for the current task to finish and try again.\"\n : \"An error occurred while processing your request.\";\n await interaction.followUp({ content: reply, ephemeral: true }).catch(() => {});\n }\n });\n\n // Handle /clear command\n discordClient.on(Events.InteractionCreate, async (interaction) => {\n if (!interaction.isChatInputCommand()) return;\n if (interaction.commandName !== \"clear\") return;\n\n const channelId = interaction.channelId;\n sessionManager.teardown(channelId);\n\n // Clean up display state\n stopTyping(channelId);\n toolStates.delete(channelId);\n toolSummaryMessages.delete(channelId);\n replyBuffers.delete(channelId);\n replyMessages.delete(channelId);\n pendingDiffs.delete(channelId);\n permissionDiffShown.delete(channelId);\n const timer = flushTimers.get(channelId);\n if (timer) clearTimeout(timer);\n flushTimers.delete(channelId);\n\n await interaction.reply(\"Session cleared. Next message will start a fresh agent.\");\n });\n\n // --- Start IPC server and task scheduler ---\n await ipcServer.start();\n taskScheduler.start();\n\n // Graceful shutdown\n process.on(\"SIGTERM\", () => {\n for (const channelId of typingIntervals.keys()) stopTyping(channelId);\n taskScheduler.stop();\n ipcServer.stop();\n sessionManager.teardownAll();\n discordClient.destroy();\n });\n\n process.on(\"SIGINT\", () => {\n for (const channelId of typingIntervals.keys()) stopTyping(channelId);\n taskScheduler.stop();\n ipcServer.stop();\n sessionManager.teardownAll();\n discordClient.destroy();\n });\n\n try {\n await discordClient.login(config.discord.token);\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err);\n if (message.includes(\"TOKEN_INVALID\") || message.includes(\"An invalid token was provided\")) {\n console.error(\"Error: Invalid Discord bot token. Check your config.toml.\");\n } else if (message.includes(\"ConnectTimeout\") || message.includes(\"ETIMEDOUT\") || message.includes(\"ECONNREFUSED\")) {\n console.error(\"Error: Cannot connect to Discord API. Check your network or proxy settings.\");\n console.error(\"Hint: Set HTTPS_PROXY=http://127.0.0.1:7890 if you need a proxy.\");\n } else {\n console.error(\"Error: Failed to connect to Discord:\", message);\n }\n ipcServer.stop();\n process.exit(1);\n }\n}\n","import type { AppConfig, ResolvedChannelConfig } from \"../shared/types.js\";\nimport { resolveChannelConfig } from \"../shared/config.js\";\n\nexport class ChannelRouter {\n private config: AppConfig;\n private dynamicChannels = new Map<string, { agentName: string; autoReply: boolean }>();\n\n constructor(config: AppConfig) {\n this.config = config;\n }\n\n resolve(channelId: string): ResolvedChannelConfig | null {\n return resolveChannelConfig(this.config, channelId);\n }\n\n isConfigured(channelId: string): boolean {\n return this.resolve(channelId) !== null;\n }\n\n registerDynamic(channelId: string, agentName: string, autoReply: boolean): void {\n if (!this.config.agents[agentName]) {\n console.error(`Cannot register dynamic channel: unknown agent \"${agentName}\"`);\n return;\n }\n this.dynamicChannels.set(channelId, { agentName, autoReply });\n // Merge into existing channel config to preserve cwd/discord_tools overrides\n const existing = this.config.channels[channelId];\n this.config.channels[channelId] = {\n ...existing,\n agent: agentName,\n auto_reply: autoReply,\n };\n }\n\n unregisterDynamic(channelId: string): void {\n this.dynamicChannels.delete(channelId);\n delete this.config.channels[channelId];\n }\n\n updateConfig(newConfig: AppConfig): void {\n this.config = newConfig;\n // Re-inject dynamic channels that aren't already in the new config\n for (const [channelId, { agentName, autoReply }] of this.dynamicChannels) {\n if (!this.config.channels[channelId] && this.config.agents[agentName]) {\n this.config.channels[channelId] = {\n agent: agentName,\n auto_reply: autoReply,\n };\n }\n }\n }\n\n getConfig(): AppConfig {\n return this.config;\n }\n}\n","import { spawn, type ChildProcess } from \"node:child_process\";\nimport { readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { Readable, Writable } from \"node:stream\";\nimport { ClientSideConnection, ndJsonStream, PROTOCOL_VERSION } from \"@agentclientprotocol/sdk\";\nimport type { AgentConfig } from \"../shared/types.js\";\nimport { createAcpClient, type AcpEventHandlers } from \"./acp-client.js\";\n\nexport interface McpServerConfig {\n name: string;\n command: string;\n args: string[];\n env: Array<{ name: string; value: string }>;\n}\n\ninterface ManagedSession {\n channelId: string;\n agentName: string;\n process: ChildProcess;\n connection: ClientSideConnection;\n sessionId: string;\n lastActivity: number;\n idleTimer: NodeJS.Timeout;\n prompting: boolean;\n queue: Array<{ text: string; requestorId: string }>;\n /** Set only when executePrompt begins — stable for the duration of the prompt */\n activePromptRequestorId: string;\n}\n\ninterface PersistedSession {\n sessionId: string;\n agentName: string;\n}\n\nexport class SessionManager {\n private sessions = new Map<string, ManagedSession>();\n private handlers: AcpEventHandlers;\n private sessionsPath: string;\n private pendingResumes = new Map<string, PersistedSession>();\n private maxConcurrentSessions: number;\n\n constructor(handlers: AcpEventHandlers, sessionsPath: string, maxConcurrentSessions = 2) {\n this.handlers = handlers;\n this.sessionsPath = sessionsPath;\n this.maxConcurrentSessions = maxConcurrentSessions;\n this.loadSessionMap();\n }\n\n private loadSessionMap(): void {\n try {\n const data = readFileSync(this.sessionsPath, \"utf-8\");\n const map = JSON.parse(data) as Record<string, PersistedSession>;\n for (const [channelId, entry] of Object.entries(map)) {\n this.pendingResumes.set(channelId, entry);\n }\n if (this.pendingResumes.size > 0) {\n console.log(`Loaded ${this.pendingResumes.size} session(s) for lazy resume`);\n }\n } catch (err: unknown) {\n // ENOENT is expected on first run; log other errors for diagnosability\n if (err instanceof Error && \"code\" in err && (err as NodeJS.ErrnoException).code === \"ENOENT\") return;\n if (err instanceof SyntaxError) {\n console.warn(\"Corrupt sessions.json, starting fresh:\", err.message);\n }\n }\n }\n\n saveSessionMap(): void {\n const map: Record<string, PersistedSession> = {};\n // Include unresumed pending sessions so they survive daemon restarts\n // where no messages were received for that channel\n for (const [channelId, entry] of this.pendingResumes) {\n map[channelId] = entry;\n }\n // Active sessions override any pending resume for the same channel\n for (const [channelId, session] of this.sessions) {\n map[channelId] = {\n sessionId: session.sessionId,\n agentName: session.agentName,\n };\n }\n try {\n mkdirSync(dirname(this.sessionsPath), { recursive: true });\n writeFileSync(this.sessionsPath, JSON.stringify(map, null, 2));\n } catch (err) {\n console.error(\"Failed to save session map:\", err);\n }\n }\n\n async prompt(channelId: string, text: string, agentName: string, agentConfig: AgentConfig, requestorId: string, mcpServers?: McpServerConfig[]): Promise<string> {\n console.log(`[MCP] prompt: channel=${channelId} mcpServers=${mcpServers ? `[${mcpServers.length} server(s)]` : \"undefined\"}`);\n const session = await this.getOrCreate(channelId, agentName, agentConfig, requestorId, mcpServers);\n session.lastActivity = Date.now();\n this.resetIdleTimer(session, agentConfig.idle_timeout);\n\n if (session.prompting) {\n session.queue.push({ text, requestorId });\n return \"queued\";\n }\n\n return this.executePrompt(session, text, requestorId, agentConfig);\n }\n\n private async executePrompt(session: ManagedSession, text: string, requestorId: string, agentConfig: AgentConfig): Promise<string> {\n session.prompting = true;\n session.activePromptRequestorId = requestorId;\n try {\n const result = await session.connection.prompt({\n sessionId: session.sessionId,\n prompt: [{ type: \"text\", text }],\n });\n this.handlers.onPromptComplete(session.channelId, result.stopReason);\n return result.stopReason;\n } catch (err) {\n // Connection broken or agent crashed — teardown to kill orphaned processes\n console.error(`Prompt error for channel ${session.channelId}, tearing down session:`, err);\n this.handlers.onPromptComplete(session.channelId, \"error\");\n this.teardown(session.channelId);\n throw err;\n } finally {\n session.prompting = false;\n // Process queue only if session is still alive (not torn down above)\n if (this.sessions.has(session.channelId)) {\n const next = session.queue.shift();\n if (next) {\n this.executePrompt(session, next.text, next.requestorId, agentConfig).catch((err) => {\n console.error(`Queued prompt failed for channel ${session.channelId}:`, err);\n });\n }\n }\n }\n }\n\n cancel(channelId: string): void {\n const session = this.sessions.get(channelId);\n if (session) {\n session.connection.cancel({ sessionId: session.sessionId });\n }\n }\n\n private async getOrCreate(channelId: string, agentName: string, agentConfig: AgentConfig, requestorId: string, mcpServers?: McpServerConfig[]): Promise<ManagedSession> {\n const existing = this.sessions.get(channelId);\n if (existing) {\n console.log(`[MCP] getOrCreate: reusing existing session for channel=${channelId} (mcpServers passed but ignored: ${mcpServers ? mcpServers.length : 0} server(s))`);\n return existing;\n }\n\n // Check for a pending resume from a previous daemon run\n const pending = this.pendingResumes.get(channelId);\n if (pending && pending.agentName === agentName) {\n this.pendingResumes.delete(channelId);\n try {\n return await this.resumeSession(channelId, agentName, agentConfig, requestorId, pending.sessionId, mcpServers);\n } catch (err) {\n console.warn(`Session resume failed for channel ${channelId}, creating new session:`, err);\n // Fall through to create a new session\n }\n } else if (pending) {\n // Agent name changed since last run — discard stale resume\n this.pendingResumes.delete(channelId);\n }\n\n // Evict oldest idle session(s) if at capacity\n this.evictIfNeeded();\n\n console.log(`[MCP] getOrCreate: creating new session for channel=${channelId} with ${mcpServers?.length ?? 0} MCP server(s)`);\n return this.createSession(channelId, agentName, agentConfig, requestorId, mcpServers);\n }\n\n private evictIfNeeded(): void {\n while (this.sessions.size >= this.maxConcurrentSessions) {\n // Find the least-recently-active non-prompting (idle) session\n let oldest: ManagedSession | null = null;\n for (const session of this.sessions.values()) {\n if (session.prompting) continue;\n if (!oldest || session.lastActivity < oldest.lastActivity) {\n oldest = session;\n }\n }\n if (!oldest) {\n // All sessions are actively prompting — refuse to evict\n throw new Error(\"All agent sessions are busy. Please wait for the current task to finish.\");\n }\n console.log(`Evicting idle session for channel ${oldest.channelId} (lastActivity=${new Date(oldest.lastActivity).toISOString()}) to make room`);\n this.teardown(oldest.channelId);\n }\n }\n\n private async createSession(channelId: string, agentName: string, config: AgentConfig, requestorId: string, mcpServers?: McpServerConfig[]): Promise<ManagedSession> {\n const proc = spawn(config.command, config.args, {\n stdio: [\"pipe\", \"pipe\", \"inherit\"],\n cwd: config.cwd,\n detached: true, // Create new process group so we can kill the entire tree\n });\n\n // Handle spawn errors (ENOENT, permission denied, etc.) (#4)\n proc.on(\"error\", (err) => {\n console.error(`Agent process error for channel ${channelId}:`, err);\n const session = this.sessions.get(channelId);\n if (session?.process === proc) {\n clearTimeout(session.idleTimer);\n this.sessions.delete(channelId);\n }\n });\n\n proc.on(\"exit\", (code) => {\n const session = this.sessions.get(channelId);\n if (session?.process === proc) {\n clearTimeout(session.idleTimer);\n this.sessions.delete(channelId);\n if (code !== 0 && code !== null) {\n console.warn(`Agent process for channel ${channelId} exited with code ${code}`);\n }\n }\n });\n\n // Wrap initialize/newSession in try/catch to clean up process on failure (#5)\n let connection: ClientSideConnection;\n let sessionId: string;\n try {\n const stream = ndJsonStream(\n Writable.toWeb(proc.stdin!) as WritableStream<Uint8Array>,\n Readable.toWeb(proc.stdout!) as ReadableStream<Uint8Array>,\n );\n\n const client = createAcpClient(channelId, this.handlers, () => {\n return this.sessions.get(channelId)?.activePromptRequestorId ?? requestorId;\n });\n connection = new ClientSideConnection((_agent) => client, stream);\n\n await connection.initialize({\n protocolVersion: PROTOCOL_VERSION,\n clientCapabilities: {\n fs: { readTextFile: true, writeTextFile: true },\n terminal: true,\n },\n clientInfo: {\n name: \"acp-discord\",\n title: \"ACP Discord Bot\",\n version: \"0.1.0\",\n },\n });\n\n const newSessionPayload = {\n cwd: config.cwd,\n mcpServers: mcpServers ?? [],\n };\n console.log(`[MCP] createSession: calling newSession for channel=${channelId}`, JSON.stringify({\n cwd: newSessionPayload.cwd,\n mcpServerCount: newSessionPayload.mcpServers.length,\n mcpServerNames: newSessionPayload.mcpServers.map(s => s.name),\n }));\n const result = await connection.newSession(newSessionPayload);\n sessionId = result.sessionId;\n console.log(`[MCP] createSession: newSession succeeded, sessionId=${sessionId}`);\n } catch (err) {\n this.killProcessTree(proc);\n throw err;\n }\n\n const managed: ManagedSession = {\n channelId,\n agentName,\n process: proc,\n connection,\n sessionId,\n lastActivity: Date.now(),\n idleTimer: this.startIdleTimer(channelId, config.idle_timeout),\n prompting: false,\n queue: [],\n activePromptRequestorId: requestorId,\n };\n\n this.sessions.set(channelId, managed);\n return managed;\n }\n\n private async resumeSession(channelId: string, agentName: string, config: AgentConfig, requestorId: string, previousSessionId: string, mcpServers?: McpServerConfig[]): Promise<ManagedSession> {\n const proc = spawn(config.command, config.args, {\n stdio: [\"pipe\", \"pipe\", \"inherit\"],\n cwd: config.cwd,\n detached: true, // Create new process group so we can kill the entire tree\n });\n\n proc.on(\"error\", (err) => {\n console.error(`Agent process error for channel ${channelId}:`, err);\n const session = this.sessions.get(channelId);\n if (session?.process === proc) {\n clearTimeout(session.idleTimer);\n this.sessions.delete(channelId);\n }\n });\n\n proc.on(\"exit\", (code) => {\n const session = this.sessions.get(channelId);\n if (session?.process === proc) {\n clearTimeout(session.idleTimer);\n this.sessions.delete(channelId);\n if (code !== 0 && code !== null) {\n console.warn(`Agent process for channel ${channelId} exited with code ${code}`);\n }\n }\n });\n\n let connection: ClientSideConnection;\n let sessionId: string;\n try {\n const stream = ndJsonStream(\n Writable.toWeb(proc.stdin!) as WritableStream<Uint8Array>,\n Readable.toWeb(proc.stdout!) as ReadableStream<Uint8Array>,\n );\n\n const client = createAcpClient(channelId, this.handlers, () => {\n return this.sessions.get(channelId)?.activePromptRequestorId ?? requestorId;\n });\n connection = new ClientSideConnection((_agent) => client, stream);\n\n const initResult = await connection.initialize({\n protocolVersion: PROTOCOL_VERSION,\n clientCapabilities: {\n fs: { readTextFile: true, writeTextFile: true },\n terminal: true,\n },\n clientInfo: {\n name: \"acp-discord\",\n title: \"ACP Discord Bot\",\n version: \"0.1.0\",\n },\n });\n\n // Check if agent supports session resume\n const supportsResume = !!initResult.agentCapabilities?.sessionCapabilities?.resume;\n if (!supportsResume) {\n this.killProcessTree(proc);\n throw new Error(\"Agent does not support session resume\");\n }\n\n await connection.unstable_resumeSession({\n sessionId: previousSessionId,\n cwd: config.cwd,\n mcpServers: mcpServers ?? [],\n });\n sessionId = previousSessionId;\n console.log(`Resumed session ${sessionId} for channel ${channelId}`);\n } catch (err) {\n this.killProcessTree(proc);\n throw err;\n }\n\n const managed: ManagedSession = {\n channelId,\n agentName,\n process: proc,\n connection,\n sessionId,\n lastActivity: Date.now(),\n idleTimer: this.startIdleTimer(channelId, config.idle_timeout),\n prompting: false,\n queue: [],\n activePromptRequestorId: requestorId,\n };\n\n this.sessions.set(channelId, managed);\n return managed;\n }\n\n private startIdleTimer(channelId: string, timeoutSec: number): NodeJS.Timeout {\n return setTimeout(() => this.teardown(channelId), timeoutSec * 1000);\n }\n\n private resetIdleTimer(session: ManagedSession, timeoutSec: number): void {\n clearTimeout(session.idleTimer);\n session.idleTimer = this.startIdleTimer(session.channelId, timeoutSec);\n }\n\n teardown(channelId: string): void {\n this.pendingResumes.delete(channelId);\n const session = this.sessions.get(channelId);\n if (!session) return;\n clearTimeout(session.idleTimer);\n // Kill process group to ensure MCP child processes are also terminated\n this.killProcessTree(session.process);\n this.sessions.delete(channelId);\n }\n\n private killProcessTree(proc: ChildProcess): void {\n if (!proc.pid) {\n proc.kill();\n return;\n }\n try {\n // Kill the entire process group (negative PID) to clean up MCP children\n process.kill(-proc.pid, \"SIGTERM\");\n } catch {\n // Process group kill failed (e.g. not a group leader) — fall back to direct kill\n try { proc.kill(\"SIGTERM\"); } catch { /* already dead */ }\n }\n // Force-kill after 5s if still alive\n setTimeout(() => {\n try { process.kill(-proc.pid!, \"SIGKILL\"); } catch { /* already dead */ }\n try { proc.kill(\"SIGKILL\"); } catch { /* already dead */ }\n }, 5000).unref();\n }\n\n teardownAll(): void {\n this.saveSessionMap();\n for (const channelId of this.sessions.keys()) {\n this.teardown(channelId);\n }\n }\n\n isPrompting(channelId: string): boolean {\n return this.sessions.get(channelId)?.prompting ?? false;\n }\n\n getActiveRequestorId(channelId: string): string | null {\n const session = this.sessions.get(channelId);\n if (!session?.prompting) return null;\n return session.activePromptRequestorId;\n }\n\n getActiveChannels(): string[] {\n return Array.from(this.sessions.keys());\n }\n}\n","import type {\n Client,\n RequestPermissionRequest,\n RequestPermissionResponse,\n SessionNotification,\n} from \"@agentclientprotocol/sdk\";\n\nexport interface DiffContent {\n path: string;\n oldText?: string | null;\n newText: string;\n}\n\nexport interface AcpEventHandlers {\n onToolCall(channelId: string, toolCallId: string, title: string, kind: string, status: string, diffs: DiffContent[], rawInput?: Record<string, unknown>): void;\n onToolCallUpdate(channelId: string, toolCallId: string, status: string, diffs: DiffContent[], rawInput?: Record<string, unknown>): void;\n onAgentMessageChunk(channelId: string, text: string): void;\n onPermissionRequest(\n channelId: string,\n requestorId: string,\n toolCall: { toolCallId: string; title: string; kind: string },\n options: Array<{ optionId: string; name: string; kind: string }>,\n diffs: DiffContent[],\n ): Promise<{ outcome: \"selected\"; optionId: string } | { outcome: \"cancelled\" }>;\n onPromptComplete(channelId: string, stopReason: string): void;\n}\n\nexport function createAcpClient(\n channelId: string,\n handlers: AcpEventHandlers,\n getRequestorId: () => string,\n): Client {\n return {\n async requestPermission(params: RequestPermissionRequest): Promise<RequestPermissionResponse> {\n const diffs = extractDiffs((params.toolCall as { content?: unknown }).content);\n const result = await handlers.onPermissionRequest(\n channelId,\n getRequestorId(),\n {\n toolCallId: params.toolCall.toolCallId,\n title: params.toolCall.title ?? \"Unknown\",\n kind: params.toolCall.kind ?? \"other\",\n },\n params.options.map((o: { optionId: string; name: string; kind: string }) => ({\n optionId: o.optionId,\n name: o.name,\n kind: o.kind,\n })),\n diffs,\n );\n\n if (result.outcome === \"selected\") {\n return { outcome: { outcome: \"selected\", optionId: result.optionId } };\n }\n return { outcome: { outcome: \"cancelled\" } };\n },\n\n async sessionUpdate(params: SessionNotification): Promise<void> {\n const update = params.update;\n switch (update.sessionUpdate) {\n case \"agent_message_chunk\": {\n if (update.content.type === \"text\") {\n handlers.onAgentMessageChunk(channelId, update.content.text);\n }\n break;\n }\n case \"tool_call\": {\n const rawKeys = (update as Record<string, unknown>).rawInput;\n console.log(`[DEBUG tool_call] toolCallId=${update.toolCallId} title=${update.title} rawInputKeys=${rawKeys && typeof rawKeys === \"object\" ? Object.keys(rawKeys).join(\",\") : String(rawKeys)} updateKeys=${Object.keys(update).join(\",\")}`);\n const toolCallDiffs = extractDiffs(update.content);\n const rawVal = (update as Record<string, unknown>).rawInput;\n const rawInput = typeof rawVal === \"object\" && rawVal !== null && !Array.isArray(rawVal)\n ? (rawVal as Record<string, unknown>)\n : undefined;\n handlers.onToolCall(\n channelId,\n update.toolCallId,\n update.title ?? \"Unknown\",\n update.kind ?? \"other\",\n update.status ?? \"pending\",\n toolCallDiffs,\n rawInput,\n );\n break;\n }\n case \"tool_call_update\": {\n const updateRawKeys = (update as Record<string, unknown>).rawInput;\n console.log(`[DEBUG tool_call_update] toolCallId=${update.toolCallId} rawInputKeys=${updateRawKeys && typeof updateRawKeys === \"object\" ? Object.keys(updateRawKeys).join(\",\") : String(updateRawKeys)} updateKeys=${Object.keys(update).join(\",\")}`);\n const updateDiffs = extractDiffs(update.content);\n const updateRawVal = (update as Record<string, unknown>).rawInput;\n const updateRawInput = typeof updateRawVal === \"object\" && updateRawVal !== null && !Array.isArray(updateRawVal)\n ? (updateRawVal as Record<string, unknown>)\n : undefined;\n handlers.onToolCallUpdate(\n channelId,\n update.toolCallId,\n update.status ?? \"in_progress\",\n updateDiffs,\n updateRawInput,\n );\n break;\n }\n }\n },\n };\n}\n\nfunction extractDiffs(content: unknown): DiffContent[] {\n if (!Array.isArray(content)) return [];\n const diffs: DiffContent[] = [];\n for (const item of content) {\n if (item && typeof item === \"object\" && \"type\" in item && item.type === \"diff\") {\n const { path, oldText, newText } = item as Record<string, unknown>;\n if (typeof path !== \"string\" || typeof newText !== \"string\") continue;\n if (oldText !== undefined && oldText !== null && typeof oldText !== \"string\") continue;\n diffs.push({ path, oldText: (oldText as string | null) ?? null, newText });\n }\n }\n return diffs;\n}\n","import {\n ActionRowBuilder,\n ButtonBuilder,\n ButtonStyle,\n EmbedBuilder,\n type Message,\n type TextChannel,\n} from \"discord.js\";\nimport type { DiffContent } from \"./acp-client.js\";\nimport { formatDiff } from \"./message-bridge.js\";\n\nconst KIND_LABELS: Record<string, string> = {\n allow_once: \"\\u2705 Allow\",\n allow_always: \"\\u2705 Always Allow\",\n reject_once: \"\\u274C Reject\",\n reject_always: \"\\u274C Never Allow\",\n};\n\nconst KIND_STYLES: Record<string, ButtonStyle> = {\n allow_once: ButtonStyle.Success,\n allow_always: ButtonStyle.Success,\n reject_once: ButtonStyle.Danger,\n reject_always: ButtonStyle.Danger,\n};\n\nexport interface PermissionOption {\n optionId: string;\n name: string;\n kind: string;\n}\n\nexport async function sendPermissionRequest(\n channel: TextChannel,\n toolTitle: string,\n toolKind: string,\n options: PermissionOption[],\n requestorId: string,\n diffs: DiffContent[] = [],\n timeoutMs = 14 * 60 * 1000,\n): Promise<{ outcome: \"selected\"; optionId: string; diffsSent?: boolean } | { outcome: \"cancelled\"; diffsSent?: boolean }> {\n if (options.length === 0) {\n return { outcome: \"cancelled\" };\n }\n\n // Send diffs before the permission embed so the user can review changes\n let diffsSent = false;\n const diffMsgs: Message[] = [];\n if (diffs.length > 0) {\n try {\n const diffMessages = formatDiff(diffs);\n for (const content of diffMessages) {\n diffMsgs.push(await channel.send(content));\n }\n diffsSent = true;\n } catch (err) {\n console.error(\"Failed to send permission diffs:\", err);\n }\n }\n\n const embed = new EmbedBuilder()\n .setColor(0xffa500)\n .setTitle(`Permission: ${toolTitle}`)\n .setDescription(`Tool type: \\`${toolKind}\\``)\n .setTimestamp();\n\n const buttons = options.map((opt) =>\n new ButtonBuilder()\n .setCustomId(`perm_${opt.optionId}`)\n .setLabel(KIND_LABELS[opt.kind] ?? opt.name)\n .setStyle(KIND_STYLES[opt.kind] ?? ButtonStyle.Secondary),\n );\n\n // Discord allows max 5 buttons per ActionRow\n const rows: ActionRowBuilder<ButtonBuilder>[] = [];\n for (let i = 0; i < buttons.length; i += 5) {\n rows.push(new ActionRowBuilder<ButtonBuilder>().addComponents(buttons.slice(i, i + 5)));\n }\n\n const msg = await channel.send({ embeds: [embed], components: rows });\n\n return new Promise((resolve) => {\n const collector = msg.createMessageComponentCollector({\n filter: (i) => i.user.id === requestorId,\n time: timeoutMs,\n });\n\n const cleanup = () => {\n for (const dm of diffMsgs) dm.delete().catch(() => {});\n msg.delete().catch(() => msg.edit({ components: [] }).catch(() => {}));\n };\n\n collector.on(\"collect\", async (interaction) => {\n const optionId = interaction.customId.replace(\"perm_\", \"\");\n await interaction.deferUpdate();\n cleanup();\n collector.stop(\"selected\");\n resolve({ outcome: \"selected\", optionId, diffsSent });\n });\n\n collector.on(\"end\", (_collected, reason) => {\n if (reason !== \"selected\") {\n cleanup();\n resolve({ outcome: \"cancelled\", diffsSent });\n }\n });\n });\n}\n","import { createTwoFilesPatch } from \"diff\";\nimport type { DiffContent } from \"./acp-client.js\";\n\nconst DISCORD_MAX_LENGTH = 2000;\nconst MAX_DIFF_LINES = 150;\n\nexport function splitMessage(text: string, maxLength = DISCORD_MAX_LENGTH): string[] {\n if (text.length <= maxLength) return [text];\n\n const chunks: string[] = [];\n let remaining = text;\n let inCodeBlock = false;\n let codeFence = \"\";\n\n while (remaining.length > 0) {\n if (remaining.length <= maxLength) {\n chunks.push(remaining);\n break;\n }\n\n // Find split point: prefer newline before maxLength\n let splitAt = maxLength;\n const lastNewline = remaining.lastIndexOf(\"\\n\", maxLength);\n if (lastNewline > maxLength * 0.5) {\n splitAt = lastNewline + 1;\n }\n\n let chunk = remaining.slice(0, splitAt);\n remaining = remaining.slice(splitAt);\n\n // Handle code blocks: count fences in this chunk\n const fenceMatches = chunk.match(/```\\w*/g) || [];\n for (const fence of fenceMatches) {\n if (!inCodeBlock) {\n inCodeBlock = true;\n codeFence = fence;\n } else {\n inCodeBlock = false;\n codeFence = \"\";\n }\n }\n\n // If we're inside a code block at the split, close and reopen\n if (inCodeBlock) {\n chunk += \"\\n```\";\n remaining = codeFence + \"\\n\" + remaining;\n inCodeBlock = false;\n codeFence = \"\";\n }\n\n chunks.push(chunk);\n }\n\n return chunks;\n}\n\nexport type ToolStatus = \"pending\" | \"in_progress\" | \"completed\" | \"failed\";\n\nconst STATUS_ICONS: Record<ToolStatus, string> = {\n pending: \"\\u23F3\", // ⏳\n in_progress: \"\\uD83D\\uDD04\", // 🔄\n completed: \"\\u2705\", // ✅\n failed: \"\\u274C\", // ❌\n};\n\nexport function formatToolSummary(\n tools: Map<string, { title: string; status: ToolStatus; rawInput?: Record<string, unknown> }>,\n): string {\n const lines: string[] = [];\n for (const [, tool] of tools) {\n const detail = extractToolDetail(tool.rawInput) ?? extractDetailFromTitle(tool.title);\n const suffix = detail ? ` · \\`${detail}\\`` : \"\";\n lines.push(`${STATUS_ICONS[tool.status]} ${tool.title}${suffix}`);\n }\n return lines.join(\"\\n\");\n}\n\nconst MAX_DETAIL_LENGTH = 80;\n\n// Only display values from known-safe fields to avoid leaking secrets\nconst SAFE_FIELDS = [\"command\", \"file_path\", \"pattern\", \"query\", \"path\", \"url\", \"description\"];\n\n// Substrings that mark a field as sensitive — matches \"secret_key\", \"api_key\", \"access_token\", etc.\nconst BLOCKED_SUBSTRINGS = [\"token\", \"secret\", \"password\", \"key\", \"content\", \"new_string\", \"old_string\", \"credential\", \"auth\"];\n\nfunction isBlockedField(name: string): boolean {\n const lower = name.toLowerCase();\n return BLOCKED_SUBSTRINGS.some((sub) => lower.includes(sub));\n}\n\nfunction extractToolDetail(rawInput?: Record<string, unknown>): string | null {\n if (!rawInput) return null;\n\n // Try known safe fields first\n for (const field of SAFE_FIELDS) {\n if (typeof rawInput[field] === \"string\" && rawInput[field]) {\n return truncate(sanitizeDetail(rawInput[field] as string), MAX_DETAIL_LENGTH);\n }\n }\n\n // Fallback: pick the first short string value from non-blocked fields\n for (const [fieldName, value] of Object.entries(rawInput)) {\n if (isBlockedField(fieldName)) continue;\n if (SAFE_FIELDS.includes(fieldName)) continue; // already checked\n if (typeof value === \"string\" && value.length > 0 && value.length < 100) {\n return truncate(sanitizeDetail(value), MAX_DETAIL_LENGTH);\n }\n }\n\n return null;\n}\n\n/**\n * Extract useful detail from the tool title when rawInput is unavailable.\n * Titles often look like \"Read /path/to/file\" or \"Bash: ls -la\" —\n * we extract the part after the first space or colon.\n */\nfunction extractDetailFromTitle(title: string): string | null {\n if (!title) return null;\n\n // Pattern: \"ToolName /path/or/arg\" or \"ToolName: something\"\n const colonMatch = title.match(/^[^:]+:\\s*(.+)/);\n if (colonMatch) {\n return truncate(sanitizeDetail(colonMatch[1].trim()), MAX_DETAIL_LENGTH);\n }\n\n // Pattern: \"ToolName /some/path\" — extract if second part looks like a path or command\n const spaceIdx = title.indexOf(\" \");\n if (spaceIdx > 0 && spaceIdx < title.length - 1) {\n const rest = title.slice(spaceIdx + 1).trim();\n // Only extract if it looks like a path, URL, or meaningful argument\n if (rest.startsWith(\"/\") || rest.startsWith(\"./\") || rest.startsWith(\"http\") || rest.includes(\".\")) {\n return truncate(sanitizeDetail(rest), MAX_DETAIL_LENGTH);\n }\n }\n\n return null;\n}\n\nfunction sanitizeDetail(text: string): string {\n return text.replace(/`/g, \"'\");\n}\n\nfunction truncate(text: string, max: number): string {\n // Use first line only for multiline values\n const firstLine = text.split(\"\\n\")[0];\n if (firstLine.length <= max) return firstLine;\n return firstLine.slice(0, max - 1) + \"\\u2026\";\n}\n\nexport function formatDiff(diffs: DiffContent[], maxLines = MAX_DIFF_LINES): string[] {\n if (diffs.length === 0) return [];\n\n const parts: string[] = [];\n\n for (const d of diffs) {\n const fileName = d.path.split(\"/\").pop() ?? d.path;\n const oldText = d.oldText ?? \"\";\n const patch = createTwoFilesPatch(\n d.oldText == null ? \"/dev/null\" : d.path,\n d.path,\n oldText,\n d.newText,\n undefined,\n undefined,\n { context: 3 },\n );\n\n // Remove the first two header lines (Index: and ===) if present, keep ---/+++ and hunks\n const patchLines = patch.split(\"\\n\");\n // Find the first --- line to start from\n const startIdx = patchLines.findIndex((l) => l.startsWith(\"---\"));\n const diffLines = startIdx >= 0 ? patchLines.slice(startIdx) : patchLines;\n\n let truncated = false;\n let displayLines = diffLines;\n if (diffLines.length > maxLines) {\n displayLines = diffLines.slice(0, maxLines);\n truncated = true;\n }\n\n let block = `**${fileName}**\\n\\`\\`\\`diff\\n${displayLines.join(\"\\n\")}\\n\\`\\`\\``;\n if (truncated) {\n block += `\\n*... ${diffLines.length - maxLines} more lines*`;\n }\n\n parts.push(block);\n }\n\n // Join all diff blocks and split for Discord's message limit\n const fullMessage = parts.join(\"\\n\\n\");\n return splitMessage(fullMessage);\n}\n","import { createServer, type Server, type Socket } from \"node:net\";\nimport { existsSync, unlinkSync, mkdirSync, chmodSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport const DEFAULT_IPC_SOCKET_PATH = join(homedir(), \".acp-discord\", \"ipc.sock\");\n\nexport interface BindChannelOpts {\n cwd?: string;\n autoReply?: boolean;\n discordTools?: boolean;\n}\n\nexport interface BindingInfo {\n channelId: string;\n agent: string;\n cwd?: string;\n autoReply: boolean;\n discordTools: boolean;\n}\n\nexport interface TaskCrudResult {\n task?: unknown;\n tasks?: unknown[];\n logs?: unknown[];\n error?: string;\n deleted?: boolean;\n}\n\nexport interface IpcHandler {\n registerChannel(channelId: string, agentName: string, autoReply: boolean): void;\n unregisterChannel(channelId: string): void;\n confirmAction(sourceChannelId: string, description: string, details: string): Promise<boolean>;\n bindChannel(channelId: string, agentName: string, opts: BindChannelOpts, guildId?: string): Promise<{ success: boolean; error?: string }>;\n unbindChannel(channelId: string, guildId?: string): Promise<{ success: boolean; error?: string }>;\n listBindings(guildId?: string): Promise<{ success: boolean; bindings: BindingInfo[] }>;\n createTask?(params: Record<string, unknown>): TaskCrudResult;\n listTasks?(channelId?: string): TaskCrudResult;\n updateTask?(taskId: string, updates: Record<string, unknown>, channelId?: string): TaskCrudResult;\n deleteTask?(taskId: string, channelId?: string): TaskCrudResult;\n getTaskLogs?(taskId?: string, channelId?: string): TaskCrudResult;\n}\n\ninterface IpcMessage {\n action: string;\n requestId?: string;\n channelId?: string;\n agentName?: string;\n autoReply?: boolean;\n sourceChannelId?: string;\n description?: string;\n details?: string;\n cwd?: string;\n discordTools?: boolean;\n guildId?: string;\n // Task-related fields\n taskId?: string;\n prompt?: string;\n scheduleType?: string;\n scheduleValue?: string;\n notify?: string;\n updates?: Record<string, unknown>;\n}\n\nexport class IpcServer {\n private server: Server | null = null;\n private socketPath: string;\n private handler: IpcHandler;\n private connections = new Set<Socket>();\n\n constructor(handler: IpcHandler, socketPath = DEFAULT_IPC_SOCKET_PATH) {\n this.handler = handler;\n this.socketPath = socketPath;\n }\n\n async start(): Promise<void> {\n // Clean up stale socket\n if (existsSync(this.socketPath)) {\n unlinkSync(this.socketPath);\n }\n mkdirSync(dirname(this.socketPath), { recursive: true });\n\n return new Promise((resolve, reject) => {\n this.server = createServer((socket) => this.handleConnection(socket));\n\n this.server.on(\"error\", (err) => {\n console.error(\"IPC server error:\", err);\n reject(err);\n });\n\n this.server.listen(this.socketPath, () => {\n // Restrict socket to owner-only access\n chmodSync(this.socketPath, 0o600);\n console.log(`IPC server listening on ${this.socketPath}`);\n resolve();\n });\n });\n }\n\n private handleConnection(socket: Socket): void {\n this.connections.add(socket);\n let buffer = \"\";\n\n socket.on(\"data\", (data) => {\n buffer += data.toString();\n // Process newline-delimited JSON messages\n let newlineIdx: number;\n while ((newlineIdx = buffer.indexOf(\"\\n\")) !== -1) {\n const line = buffer.slice(0, newlineIdx).trim();\n buffer = buffer.slice(newlineIdx + 1);\n if (line) {\n this.processMessage(socket, line).catch((err) => {\n console.error(\"IPC message processing error:\", err);\n });\n }\n }\n });\n\n socket.on(\"close\", () => {\n this.connections.delete(socket);\n });\n\n socket.on(\"error\", (err) => {\n console.error(\"IPC connection error:\", err);\n this.connections.delete(socket);\n });\n }\n\n private async processMessage(socket: Socket, raw: string): Promise<void> {\n let msg: IpcMessage;\n try {\n msg = JSON.parse(raw);\n } catch {\n console.error(\"IPC: invalid JSON:\", raw);\n return;\n }\n\n switch (msg.action) {\n case \"register_channel\":\n if (msg.channelId && msg.agentName) {\n this.handler.registerChannel(msg.channelId, msg.agentName, msg.autoReply ?? true);\n }\n break;\n\n case \"unregister_channel\":\n if (msg.channelId) {\n this.handler.unregisterChannel(msg.channelId);\n }\n break;\n\n case \"confirm_action\":\n if (msg.requestId && msg.sourceChannelId && msg.description) {\n const approved = await this.handler.confirmAction(\n msg.sourceChannelId,\n msg.description,\n msg.details ?? \"\",\n );\n const response = JSON.stringify({ requestId: msg.requestId, approved }) + \"\\n\";\n socket.write(response);\n }\n break;\n\n case \"bind_channel\": {\n if (msg.requestId && msg.channelId && msg.agentName) {\n const result = await this.handler.bindChannel(msg.channelId, msg.agentName, {\n cwd: msg.cwd,\n autoReply: msg.autoReply,\n discordTools: msg.discordTools,\n }, msg.guildId);\n socket.write(JSON.stringify({ requestId: msg.requestId, ...result }) + \"\\n\");\n }\n break;\n }\n\n case \"unbind_channel\": {\n if (msg.requestId && msg.channelId) {\n const result = await this.handler.unbindChannel(msg.channelId, msg.guildId);\n socket.write(JSON.stringify({ requestId: msg.requestId, ...result }) + \"\\n\");\n }\n break;\n }\n\n case \"list_bindings\": {\n if (msg.requestId) {\n const result = await this.handler.listBindings(msg.guildId);\n socket.write(JSON.stringify({ requestId: msg.requestId, ...result }) + \"\\n\");\n }\n break;\n }\n\n case \"create_task\": {\n if (!msg.requestId || !this.handler.createTask) break;\n try {\n const result = this.handler.createTask({\n channel_id: msg.channelId ?? msg.sourceChannelId ?? \"\",\n agent_name: msg.agentName ?? \"unknown\",\n prompt: msg.prompt ?? \"\",\n schedule_type: msg.scheduleType ?? \"once\",\n schedule_value: msg.scheduleValue ?? \"\",\n description: msg.description,\n notify: msg.notify,\n created_by: \"agent\",\n });\n socket.write(JSON.stringify({ requestId: msg.requestId, ...result }) + \"\\n\");\n } catch (err) {\n const errMsg = err instanceof Error ? err.message : String(err);\n socket.write(JSON.stringify({ requestId: msg.requestId, error: errMsg }) + \"\\n\");\n }\n break;\n }\n\n case \"list_tasks\": {\n if (!msg.requestId || !this.handler.listTasks) break;\n const listResult = this.handler.listTasks(msg.channelId);\n socket.write(JSON.stringify({ requestId: msg.requestId, ...listResult }) + \"\\n\");\n break;\n }\n\n case \"update_task\": {\n if (!msg.requestId || !msg.taskId || !this.handler.updateTask) break;\n try {\n const updateResult = this.handler.updateTask(msg.taskId, msg.updates ?? {}, msg.channelId);\n socket.write(JSON.stringify({ requestId: msg.requestId, ...updateResult }) + \"\\n\");\n } catch (err) {\n const errMsg = err instanceof Error ? err.message : String(err);\n socket.write(JSON.stringify({ requestId: msg.requestId, error: errMsg }) + \"\\n\");\n }\n break;\n }\n\n case \"delete_task\": {\n if (!msg.requestId || !msg.taskId || !this.handler.deleteTask) break;\n const deleteResult = this.handler.deleteTask(msg.taskId, msg.channelId);\n socket.write(JSON.stringify({ requestId: msg.requestId, ...deleteResult }) + \"\\n\");\n break;\n }\n\n case \"get_task_logs\": {\n if (!msg.requestId || !this.handler.getTaskLogs) break;\n const logsResult = this.handler.getTaskLogs(msg.taskId, msg.channelId);\n socket.write(JSON.stringify({ requestId: msg.requestId, ...logsResult }) + \"\\n\");\n break;\n }\n\n default:\n console.error(\"IPC: unknown action:\", msg.action);\n }\n }\n\n stop(): void {\n for (const conn of this.connections) {\n conn.destroy();\n }\n this.connections.clear();\n if (this.server) {\n this.server.close();\n this.server = null;\n }\n // Clean up socket file\n if (existsSync(this.socketPath)) {\n try {\n unlinkSync(this.socketPath);\n } catch {\n // ignore\n }\n }\n }\n}\n","import { readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { randomUUID } from \"node:crypto\";\nimport { CronExpressionParser } from \"cron-parser\";\n\nconst DEFAULT_DATA_DIR = join(homedir(), \".acp-discord\");\nconst MAX_LOGS_PER_TASK = 50;\n\nexport interface ScheduledTask {\n id: string;\n channel_id: string;\n agent_name: string;\n prompt: string;\n description: string;\n schedule_type: \"once\" | \"cron\" | \"interval\";\n schedule_value: string;\n status: \"active\" | \"paused\" | \"completed\";\n notify: \"always\" | \"on_error\" | \"never\";\n next_run: string | null;\n last_run: string | null;\n created_by: string;\n created_at: string;\n}\n\nexport interface TaskRunLog {\n id: string;\n task_id: string;\n task_description: string;\n started_at: string;\n completed_at: string;\n duration_ms: number;\n status: \"success\" | \"error\";\n output: string;\n error: string | null;\n}\n\nexport interface TaskRunInput {\n startedAt: Date;\n completedAt: Date;\n durationMs: number;\n status: \"success\" | \"error\";\n output: string;\n error: string | null;\n}\n\nexport interface CreateTaskParams {\n channel_id: string;\n agent_name: string;\n prompt: string;\n description?: string;\n schedule_type: \"once\" | \"cron\" | \"interval\";\n schedule_value: string;\n notify?: \"always\" | \"on_error\" | \"never\";\n created_by: string;\n}\n\nexport type OnTaskFire = (task: ScheduledTask) => Promise<void>;\n\nexport class TaskScheduler {\n private tasks: ScheduledTask[] = [];\n private logs: TaskRunLog[] = [];\n private interval: NodeJS.Timeout | null = null;\n private onTaskFire: OnTaskFire;\n private tasksPath: string;\n private logsPath: string;\n private dataDir: string;\n private inFlight = new Set<string>(); // task IDs currently running\n\n constructor(onTaskFire: OnTaskFire, dataDir?: string) {\n this.onTaskFire = onTaskFire;\n this.dataDir = dataDir ?? DEFAULT_DATA_DIR;\n this.tasksPath = join(this.dataDir, \"scheduled-tasks.json\");\n this.logsPath = join(this.dataDir, \"task-run-logs.json\");\n }\n\n start(): void {\n this.load();\n this.interval = setInterval(() => {\n this.poll().catch((err) => {\n console.error(\"TaskScheduler poll error:\", err);\n });\n }, 15_000);\n console.log(`TaskScheduler started with ${this.tasks.length} task(s)`);\n }\n\n stop(): void {\n if (this.interval) {\n clearInterval(this.interval);\n this.interval = null;\n }\n this.save();\n }\n\n createTask(params: CreateTaskParams): ScheduledTask {\n const now = new Date().toISOString();\n const task: ScheduledTask = {\n id: randomUUID(),\n channel_id: params.channel_id,\n agent_name: params.agent_name,\n prompt: params.prompt,\n description: params.description ?? params.prompt.slice(0, 80),\n schedule_type: params.schedule_type,\n schedule_value: params.schedule_value,\n status: \"active\",\n notify: params.notify ?? \"on_error\",\n next_run: null,\n last_run: null,\n created_by: params.created_by,\n created_at: now,\n };\n\n task.next_run = this.computeNextRun(task);\n if (!task.next_run) {\n throw new Error(`Invalid schedule: cannot compute next run for ${params.schedule_type} \"${params.schedule_value}\"`);\n }\n\n this.tasks.push(task);\n this.save();\n return task;\n }\n\n listTasks(channelId?: string): ScheduledTask[] {\n if (channelId) {\n return this.tasks.filter((t) => t.channel_id === channelId);\n }\n return [...this.tasks];\n }\n\n updateTask(\n id: string,\n updates: Partial<Pick<ScheduledTask, \"status\" | \"prompt\" | \"schedule_type\" | \"schedule_value\" | \"notify\" | \"description\">>,\n channelId?: string,\n ): ScheduledTask | null {\n const task = this.tasks.find((t) => t.id === id);\n if (!task) return null;\n if (channelId && task.channel_id !== channelId) return null;\n\n if (updates.status !== undefined) task.status = updates.status;\n if (updates.prompt !== undefined) task.prompt = updates.prompt;\n if (updates.description !== undefined) task.description = updates.description;\n if (updates.notify !== undefined) task.notify = updates.notify;\n\n if (updates.schedule_type !== undefined || updates.schedule_value !== undefined) {\n // Save originals for rollback on validation failure\n const origType = task.schedule_type;\n const origValue = task.schedule_value;\n const origNextRun = task.next_run;\n\n if (updates.schedule_type !== undefined) task.schedule_type = updates.schedule_type;\n if (updates.schedule_value !== undefined) task.schedule_value = updates.schedule_value;\n const nextRun = this.computeNextRun(task);\n if (!nextRun) {\n // Rollback\n task.schedule_type = origType;\n task.schedule_value = origValue;\n task.next_run = origNextRun;\n throw new Error(`Invalid schedule: cannot compute next run for ${task.schedule_type} \"${task.schedule_value}\"`);\n }\n task.next_run = nextRun;\n }\n\n this.save();\n return task;\n }\n\n deleteTask(id: string, channelId?: string): boolean {\n const idx = this.tasks.findIndex((t) => t.id === id);\n if (idx === -1) return false;\n if (channelId && this.tasks[idx].channel_id !== channelId) return false;\n this.tasks.splice(idx, 1);\n this.save();\n return true;\n }\n\n getTaskLogs(taskId?: string, channelId?: string): TaskRunLog[] {\n let logs = this.logs;\n if (taskId) {\n // Verify the task belongs to the channel if channelId specified\n if (channelId) {\n const task = this.tasks.find((t) => t.id === taskId);\n if (task && task.channel_id !== channelId) return [];\n }\n logs = logs.filter((l) => l.task_id === taskId);\n } else if (channelId) {\n // Only return logs for tasks belonging to this channel\n const channelTaskIds = new Set(\n this.tasks.filter((t) => t.channel_id === channelId).map((t) => t.id),\n );\n logs = logs.filter((l) => channelTaskIds.has(l.task_id));\n }\n return [...logs];\n }\n\n logRun(taskId: string, result: TaskRunInput): void {\n const task = this.tasks.find((t) => t.id === taskId);\n const log: TaskRunLog = {\n id: randomUUID(),\n task_id: taskId,\n task_description: task?.description ?? \"unknown\",\n started_at: result.startedAt.toISOString(),\n completed_at: result.completedAt.toISOString(),\n duration_ms: result.durationMs,\n status: result.status,\n output: result.output.slice(0, 4000),\n error: result.error,\n };\n this.logs.push(log);\n\n // Trim logs per task\n const taskLogs = this.logs.filter((l) => l.task_id === taskId);\n if (taskLogs.length > MAX_LOGS_PER_TASK) {\n const idsToRemove = new Set(\n taskLogs\n .slice(0, taskLogs.length - MAX_LOGS_PER_TASK)\n .map((l) => l.id),\n );\n this.logs = this.logs.filter((l) => !idsToRemove.has(l.id));\n }\n\n this.saveLogs();\n }\n\n computeNextRun(task: ScheduledTask): string | null {\n const now = new Date();\n\n switch (task.schedule_type) {\n case \"once\": {\n const date = new Date(task.schedule_value);\n if (isNaN(date.getTime())) return null;\n return date > now ? date.toISOString() : date.toISOString();\n }\n\n case \"cron\": {\n try {\n const expr = CronExpressionParser.parse(task.schedule_value, { currentDate: now });\n const next = expr.next();\n return next.toDate().toISOString();\n } catch {\n return null;\n }\n }\n\n case \"interval\": {\n const seconds = parseInt(task.schedule_value, 10);\n if (isNaN(seconds) || seconds <= 0) return null;\n // Anchor to last_run to prevent drift\n const anchor = task.last_run ? new Date(task.last_run) : now;\n const next = new Date(anchor.getTime() + seconds * 1000);\n // If next is in the past, advance forward\n if (next <= now) {\n const elapsed = now.getTime() - anchor.getTime();\n const intervals = Math.ceil(elapsed / (seconds * 1000));\n return new Date(anchor.getTime() + intervals * seconds * 1000).toISOString();\n }\n return next.toISOString();\n }\n\n default:\n return null;\n }\n }\n\n private async poll(): Promise<void> {\n const now = new Date();\n\n for (const task of this.tasks) {\n if (task.status !== \"active\") continue;\n if (!task.next_run) continue;\n if (this.inFlight.has(task.id)) continue; // skip if already running\n\n const nextRun = new Date(task.next_run);\n if (nextRun > now) continue;\n\n // Mark as firing\n task.last_run = now.toISOString();\n\n if (task.schedule_type === \"once\") {\n task.status = \"completed\";\n } else {\n task.next_run = this.computeNextRun(task);\n }\n this.save();\n\n // Fire asynchronously with in-flight guard\n this.inFlight.add(task.id);\n this.onTaskFire(task)\n .catch((err) => {\n console.error(`Task fire error for ${task.id}:`, err);\n })\n .finally(() => {\n this.inFlight.delete(task.id);\n });\n }\n }\n\n private load(): void {\n mkdirSync(this.dataDir, { recursive: true });\n try {\n const data = readFileSync(this.tasksPath, \"utf-8\");\n this.tasks = JSON.parse(data);\n } catch {\n this.tasks = [];\n }\n try {\n const data = readFileSync(this.logsPath, \"utf-8\");\n this.logs = JSON.parse(data);\n } catch {\n this.logs = [];\n }\n }\n\n private save(): void {\n mkdirSync(this.dataDir, { recursive: true });\n writeFileSync(this.tasksPath, JSON.stringify(this.tasks, null, 2));\n }\n\n private saveLogs(): void {\n mkdirSync(this.dataDir, { recursive: true });\n writeFileSync(this.logsPath, JSON.stringify(this.logs, null, 2));\n }\n}\n","import { spawn, type ChildProcess } from \"node:child_process\";\nimport { Readable, Writable } from \"node:stream\";\nimport { ClientSideConnection, ndJsonStream, PROTOCOL_VERSION } from \"@agentclientprotocol/sdk\";\nimport type { Client, RequestPermissionResponse, SessionNotification } from \"@agentclientprotocol/sdk\";\nimport type { AgentConfig } from \"../shared/types.js\";\nimport type { McpServerConfig } from \"./session-manager.js\";\n\nexport interface TaskRunResult {\n output: string;\n stopReason: string;\n error: string | null;\n}\n\nfunction killProcessTree(proc: ChildProcess): void {\n if (!proc.pid) { proc.kill(); return; }\n try {\n process.kill(-proc.pid, \"SIGTERM\");\n } catch {\n try { proc.kill(\"SIGTERM\"); } catch { /* already dead */ }\n }\n setTimeout(() => {\n try { process.kill(-proc.pid!, \"SIGKILL\"); } catch { /* already dead */ }\n try { proc.kill(\"SIGKILL\"); } catch { /* already dead */ }\n }, 5000).unref();\n}\n\nexport async function runTask(\n agentConfig: AgentConfig,\n prompt: string,\n mcpServers: McpServerConfig[],\n): Promise<TaskRunResult> {\n const proc = spawn(agentConfig.command, agentConfig.args, {\n stdio: [\"pipe\", \"pipe\", \"inherit\"],\n cwd: agentConfig.cwd,\n detached: true,\n });\n\n let output = \"\";\n let error: string | null = null;\n\n try {\n const stream = ndJsonStream(\n Writable.toWeb(proc.stdin!) as WritableStream<Uint8Array>,\n Readable.toWeb(proc.stdout!) as ReadableStream<Uint8Array>,\n );\n\n const client: Client = {\n async requestPermission(): Promise<RequestPermissionResponse> {\n // Auto-cancel permission requests in scheduled tasks\n return { outcome: { outcome: \"cancelled\" } };\n },\n async sessionUpdate(params: SessionNotification): Promise<void> {\n const update = params.update;\n if (update.sessionUpdate === \"agent_message_chunk\") {\n if (update.content.type === \"text\") {\n output += update.content.text;\n }\n }\n },\n };\n\n const connection = new ClientSideConnection((_agent) => client, stream);\n\n await connection.initialize({\n protocolVersion: PROTOCOL_VERSION,\n clientCapabilities: {\n fs: { readTextFile: true, writeTextFile: true },\n terminal: true,\n },\n clientInfo: {\n name: \"acp-discord-task-runner\",\n title: \"ACP Discord Task Runner\",\n version: \"0.1.0\",\n },\n });\n\n const { sessionId } = await connection.newSession({\n cwd: agentConfig.cwd,\n mcpServers,\n });\n\n const result = await connection.prompt({\n sessionId,\n prompt: [{ type: \"text\", text: prompt }],\n });\n\n killProcessTree(proc);\n return { output, stopReason: result.stopReason, error: null };\n } catch (err) {\n killProcessTree(proc);\n error = err instanceof Error ? err.message : String(err);\n return { output, stopReason: \"error\", error };\n }\n}\n"],"mappings":"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","names":["join","homedir","ActionRowBuilder","ButtonBuilder","ButtonStyle","EmbedBuilder","dirname","existsSync","mkdirSync","dirname","readFileSync","writeFileSync","mkdirSync","join","homedir","spawn","Readable","Writable","ClientSideConnection","ndJsonStream","PROTOCOL_VERSION","EmbedBuilder","ActionRowBuilder","ButtonBuilder","ButtonStyle","dirname","existsSync","join","homedir"]}
1
+ {"version":3,"sources":["../src/daemon/index.ts","../src/daemon/discord-bot.ts","../src/daemon/channel-router.ts","../src/daemon/session-manager.ts","../src/daemon/acp-client.ts","../src/daemon/permission-ui.ts","../src/daemon/message-bridge.ts","../src/daemon/ipc-server.ts","../src/daemon/task-scheduler.ts","../src/daemon/task-runner.ts"],"sourcesContent":["import { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { loadConfig } from \"../shared/config.js\";\nimport { writePid, removePid } from \"../cli/pid.js\";\nimport { startDiscordBot } from \"./discord-bot.js\";\n\nconst CONFIG_DIR = join(homedir(), \".acp-discord\");\nconst CONFIG_PATH = join(CONFIG_DIR, \"config.toml\");\nconst PID_PATH = join(CONFIG_DIR, \"daemon.pid\");\nconst SESSIONS_PATH = join(CONFIG_DIR, \"sessions.json\");\n\nexport async function runDaemon(): Promise<void> {\n // Global error handlers — graceful shutdown on fatal errors, continue on rejections\n process.on(\"uncaughtException\", (err) => {\n console.error(\"Uncaught exception:\", err);\n // Allow event loop to flush logs, then exit for restart by service manager\n setTimeout(() => process.exit(1), 1000);\n });\n\n process.on(\"unhandledRejection\", (reason) => {\n console.error(\"Unhandled rejection:\", reason);\n // Unhandled rejections are less severe — log but continue\n });\n\n // Load config first — if it fails, no stale PID file is left behind (#12)\n const config = loadConfig(CONFIG_PATH);\n\n writePid(PID_PATH, process.pid);\n process.on(\"exit\", () => removePid(PID_PATH));\n\n console.log(`acp-discord daemon started (PID: ${process.pid})`);\n console.log(`Loaded config: ${Object.keys(config.channels).length} channel(s)`);\n\n await startDiscordBot(config, SESSIONS_PATH, CONFIG_PATH);\n}\n\nif (process.env.ACP_DISCORD_DAEMON === \"1\") {\n runDaemon().catch((err) => {\n console.error(\"Daemon failed:\", err);\n process.exit(1);\n });\n}\n","import {\n Client,\n GatewayIntentBits,\n Events,\n REST,\n Routes,\n SlashCommandBuilder,\n ActionRowBuilder,\n ButtonBuilder,\n ButtonStyle,\n EmbedBuilder,\n type Message,\n type TextChannel,\n} from \"discord.js\";\nimport { resolve as resolvePath, dirname } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport type { AppConfig } from \"../shared/types.js\";\nimport { saveConfig, resolveChannelConfig } from \"../shared/config.js\";\nimport { ChannelRouter } from \"./channel-router.js\";\nimport { SessionManager, type McpServerConfig, type ImageAttachment } from \"./session-manager.js\";\nimport { sendPermissionRequest } from \"./permission-ui.js\";\nimport { splitMessage, formatToolSummary, formatDiff, type ToolStatus } from \"./message-bridge.js\";\nimport type { AcpEventHandlers, DiffContent } from \"./acp-client.js\";\nimport { IpcServer, DEFAULT_IPC_SOCKET_PATH } from \"./ipc-server.js\";\nimport { TaskScheduler } from \"./task-scheduler.js\";\nimport { runTask } from \"./task-runner.js\";\n\nexport async function startDiscordBot(config: AppConfig, sessionsPath: string, configPath: string): Promise<void> {\n let currentConfig = config;\n const router = new ChannelRouter(currentConfig);\n\n // Per-channel state for display\n const toolStates = new Map<string, Map<string, { title: string; status: ToolStatus; rawInput?: Record<string, unknown> }>>();\n const toolSummaryMessages = new Map<string, Message>();\n const replyBuffers = new Map<string, string>();\n const replyMessages = new Map<string, Message>();\n const flushTimers = new Map<string, NodeJS.Timeout>();\n // channelId -> toolCallId -> DiffContent[]\n const pendingDiffs = new Map<string, Map<string, DiffContent[]>>();\n // channelId -> Set of toolCallIds whose diffs were already shown at permission-request time\n const permissionDiffShown = new Map<string, Set<string>>();\n\n // Typing indicator state: channelId -> interval timer\n const typingIntervals = new Map<string, NodeJS.Timeout>();\n\n function startTyping(channelId: string) {\n if (typingIntervals.has(channelId)) return;\n // Set a placeholder immediately to prevent concurrent calls from creating duplicate intervals\n const placeholder = setTimeout(() => {}, 0);\n typingIntervals.set(channelId, placeholder);\n clearTimeout(placeholder);\n\n fetchChannel(channelId).then((channel) => {\n if (!channel) { typingIntervals.delete(channelId); return; }\n // Re-check: stopTyping may have been called while we awaited\n if (!typingIntervals.has(channelId)) return;\n channel.sendTyping().catch(() => {});\n const interval = setInterval(() => {\n channel.sendTyping().catch(() => {});\n }, 8000);\n typingIntervals.set(channelId, interval);\n }).catch(() => {\n typingIntervals.delete(channelId);\n });\n }\n\n function stopTyping(channelId: string) {\n const interval = typingIntervals.get(channelId);\n if (interval) {\n clearInterval(interval);\n typingIntervals.delete(channelId);\n }\n }\n\n let discordClient: Client;\n\n // --- Confirmation UI for MCP tool actions ---\n\n // Pending confirmation requests from MCP servers (requestId -> { resolver, allowedUserId })\n const pendingConfirmations = new Map<string, { resolve: (approved: boolean) => void; allowedUserId: string | null }>();\n\n async function handleConfirmAction(sourceChannelId: string, description: string, details: string): Promise<boolean> {\n const channel = await fetchChannel(sourceChannelId);\n if (!channel) return false;\n\n // Only the user who triggered the current prompt can approve\n const allowedUserId = sessionManager.getActiveRequestorId(sourceChannelId);\n\n const requestId = `mcp_confirm_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n\n const embed = new EmbedBuilder()\n .setColor(0xffa500)\n .setTitle(`Channel Action: ${description}`)\n .setDescription(details || \"No additional details\")\n .setTimestamp();\n\n const row = new ActionRowBuilder<ButtonBuilder>().addComponents(\n new ButtonBuilder()\n .setCustomId(`mcp_approve_${requestId}`)\n .setLabel(\"\\u2705 Approve\")\n .setStyle(ButtonStyle.Success),\n new ButtonBuilder()\n .setCustomId(`mcp_reject_${requestId}`)\n .setLabel(\"\\u274C Reject\")\n .setStyle(ButtonStyle.Danger),\n );\n\n const msg = await channel.send({ embeds: [embed], components: [row] });\n\n return new Promise<boolean>((resolve) => {\n const timeout = setTimeout(() => {\n pendingConfirmations.delete(requestId);\n msg.delete().catch(() => msg.edit({ components: [] }).catch(() => {}));\n resolve(false);\n }, 5 * 60 * 1000); // 5 minute timeout\n\n pendingConfirmations.set(requestId, {\n resolve: (approved: boolean) => {\n clearTimeout(timeout);\n pendingConfirmations.delete(requestId);\n msg.delete().catch(() => msg.edit({ components: [] }).catch(() => {}));\n resolve(approved);\n },\n allowedUserId,\n });\n });\n }\n\n // --- IPC Server ---\n\n // --- Task Scheduler ---\n\n const taskScheduler = new TaskScheduler(async (task) => {\n const resolved = router.resolve(task.channel_id);\n if (!resolved) {\n console.error(`TaskScheduler: no resolved config for channel ${task.channel_id}`);\n taskScheduler.logRun(task.id, {\n startedAt: new Date(),\n completedAt: new Date(),\n durationMs: 0,\n status: \"error\",\n output: \"\",\n error: `No resolved config for channel ${task.channel_id}`,\n });\n return;\n }\n\n const channel = await fetchChannel(task.channel_id);\n const guildId = channel?.guild?.id ?? null;\n const mcpServers = guildId ? buildMcpServers(task.channel_id, task.agent_name, guildId) : [];\n\n const startedAt = new Date();\n const result = await runTask(resolved.agent, task.prompt, mcpServers);\n const completedAt = new Date();\n const durationMs = completedAt.getTime() - startedAt.getTime();\n\n taskScheduler.logRun(task.id, {\n startedAt,\n completedAt,\n durationMs,\n status: result.error ? \"error\" : \"success\",\n output: result.output,\n error: result.error,\n });\n\n const shouldNotify =\n task.notify === \"always\" ||\n (task.notify === \"on_error\" && result.error);\n\n if (shouldNotify && channel) {\n const text = result.error ?? result.output;\n const chunks = splitMessage(text || \"(no output)\");\n for (const chunk of chunks) {\n await channel.send(chunk);\n }\n }\n });\n\n const ipcServer = new IpcServer(\n {\n registerChannel(channelId, agentName, autoReply) {\n router.registerDynamic(channelId, agentName, autoReply);\n console.log(`IPC: registered dynamic channel ${channelId} -> agent ${agentName}`);\n },\n unregisterChannel(channelId) {\n router.unregisterDynamic(channelId);\n console.log(`IPC: unregistered dynamic channel ${channelId}`);\n },\n confirmAction: handleConfirmAction,\n\n async bindChannel(channelId, agentName, opts, guildId) {\n // Validate guild ownership if guildId provided\n if (guildId) {\n try {\n const channel = await discordClient.channels.fetch(channelId);\n const channelGuildId = channel && \"guildId\" in channel ? (channel as { guildId: string }).guildId : null;\n if (channelGuildId !== guildId) {\n return { success: false, error: `Channel ${channelId} does not belong to guild ${guildId}` };\n }\n } catch {\n return { success: false, error: `Cannot verify channel ${channelId}` };\n }\n }\n\n // Validate agent exists\n if (!currentConfig.agents[agentName]) {\n return { success: false, error: `Unknown agent \"${agentName}\"` };\n }\n\n // Update in-memory config\n currentConfig.channels[channelId] = {\n agent: agentName,\n cwd: opts.cwd,\n auto_reply: opts.autoReply ?? true,\n discord_tools: opts.discordTools,\n };\n\n // Register in router\n router.registerDynamic(channelId, agentName, opts.autoReply ?? true);\n\n // Persist to config.toml\n try {\n saveConfig(configPath, currentConfig);\n } catch (err) {\n // Rollback in-memory state on write failure\n delete currentConfig.channels[channelId];\n router.unregisterDynamic(channelId);\n const message = err instanceof Error ? err.message : String(err);\n return { success: false, error: `Failed to save config: ${message}` };\n }\n\n console.log(`IPC: bound channel ${channelId} -> agent ${agentName}`);\n return { success: true };\n },\n\n async unbindChannel(channelId, guildId) {\n // Validate guild ownership if guildId provided\n if (guildId) {\n try {\n const channel = await discordClient.channels.fetch(channelId);\n const channelGuildId = channel && \"guildId\" in channel ? (channel as { guildId: string }).guildId : null;\n if (channelGuildId !== guildId) {\n return { success: false, error: `Channel ${channelId} does not belong to guild ${guildId}` };\n }\n } catch {\n // Channel may have been deleted — allow unbinding if it exists in config\n }\n }\n\n if (!currentConfig.channels[channelId]) {\n return { success: false, error: `Channel ${channelId} is not bound` };\n }\n\n // Save for rollback\n const previous = currentConfig.channels[channelId];\n\n // Update in-memory\n delete currentConfig.channels[channelId];\n router.unregisterDynamic(channelId);\n\n // Persist\n try {\n saveConfig(configPath, currentConfig);\n } catch (err) {\n // Rollback\n currentConfig.channels[channelId] = previous;\n router.registerDynamic(channelId, previous.agent, previous.auto_reply ?? false);\n const message = err instanceof Error ? err.message : String(err);\n return { success: false, error: `Failed to save config: ${message}` };\n }\n\n console.log(`IPC: unbound channel ${channelId}`);\n return { success: true };\n },\n\n async listBindings(guildId) {\n const entries = Object.entries(currentConfig.channels);\n const bindings = [];\n for (const [channelId, ch] of entries) {\n // Filter by guild if specified\n if (guildId) {\n try {\n const channel = discordClient.channels.cache.get(channelId) ?? await discordClient.channels.fetch(channelId);\n const channelGuildId = channel && \"guildId\" in channel ? (channel as { guildId: string }).guildId : null;\n if (channelGuildId !== guildId) continue;\n } catch {\n continue;\n }\n }\n const resolved = resolveChannelConfig(currentConfig, channelId);\n bindings.push({\n channelId,\n agent: ch.agent,\n cwd: ch.cwd ?? currentConfig.agents[ch.agent]?.cwd,\n autoReply: ch.auto_reply ?? false,\n discordTools: resolved?.agent.discord_tools ?? false,\n });\n }\n return { success: true, bindings };\n },\n createTask(params) {\n try {\n const task = taskScheduler.createTask(params as Parameters<typeof taskScheduler.createTask>[0]);\n return { task };\n } catch (err) {\n return { error: err instanceof Error ? err.message : String(err) };\n }\n },\n listTasks(channelId) {\n return { tasks: taskScheduler.listTasks(channelId) };\n },\n updateTask(taskId, updates, channelId) {\n const task = taskScheduler.updateTask(taskId, updates, channelId);\n if (!task) return { error: `Task ${taskId} not found` };\n return { task };\n },\n deleteTask(taskId, channelId) {\n const deleted = taskScheduler.deleteTask(taskId, channelId);\n if (!deleted) return { error: `Task ${taskId} not found` };\n return { deleted: true };\n },\n getTaskLogs(taskId, channelId) {\n return { logs: taskScheduler.getTaskLogs(taskId, channelId) };\n },\n },\n DEFAULT_IPC_SOCKET_PATH,\n );\n\n const handlers: AcpEventHandlers = {\n onToolCall(channelId, toolCallId, title, _kind, status, diffs, rawInput) {\n startTyping(channelId);\n if (!toolStates.has(channelId)) toolStates.set(channelId, new Map());\n toolStates.get(channelId)!.set(toolCallId, { title, status: status as ToolStatus, rawInput });\n accumulateDiffs(channelId, toolCallId, diffs);\n updateToolSummaryMessage(channelId);\n if (status === \"completed\") sendDiffsForTool(channelId, toolCallId);\n },\n\n onToolCallUpdate(channelId, toolCallId, status, diffs, rawInput) {\n const tools = toolStates.get(channelId);\n const tool = tools?.get(toolCallId);\n if (tool) {\n tool.status = status as ToolStatus;\n if (rawInput && !tool.rawInput) tool.rawInput = rawInput;\n accumulateDiffs(channelId, toolCallId, diffs);\n updateToolSummaryMessage(channelId);\n if (status === \"completed\") sendDiffsForTool(channelId, toolCallId);\n }\n },\n\n onAgentMessageChunk(channelId, text) {\n startTyping(channelId);\n const current = replyBuffers.get(channelId) ?? \"\";\n replyBuffers.set(channelId, current + text);\n scheduleFlushReply(channelId);\n },\n\n async onPermissionRequest(channelId, requestorId, toolCall, options, diffs) {\n const channel = await fetchChannel(channelId);\n if (!channel) return { outcome: \"cancelled\" as const };\n const result = await sendPermissionRequest(channel, toolCall.title, toolCall.kind, options, requestorId, diffs);\n if (result.diffsSent) {\n if (!permissionDiffShown.has(channelId)) permissionDiffShown.set(channelId, new Set());\n permissionDiffShown.get(channelId)!.add(toolCall.toolCallId);\n }\n return result;\n },\n\n onPromptComplete(channelId, _stopReason) {\n stopTyping(channelId);\n // Final flush\n flushReply(channelId, true);\n // Remove stop button from tool summary\n removeStopButton(channelId);\n // Clear state for next turn\n toolStates.delete(channelId);\n toolSummaryMessages.delete(channelId);\n replyBuffers.delete(channelId);\n replyMessages.delete(channelId);\n pendingDiffs.delete(channelId);\n permissionDiffShown.delete(channelId);\n },\n };\n\n const sessionManager = new SessionManager(handlers, sessionsPath);\n\n // --- MCP server config builder ---\n\n function buildMcpServers(channelId: string, agentName: string, guildId: string): McpServerConfig[] {\n const resolved = router.resolve(channelId);\n const discordToolsEnabled = resolved?.agent.discord_tools ?? false;\n const scheduledTasksEnabled = resolved?.agent.scheduled_tasks ?? false;\n console.log(`[MCP] buildMcpServers: channel=${channelId} agent=${agentName} discord_tools=${discordToolsEnabled} scheduled_tasks=${scheduledTasksEnabled}`);\n\n const mcpConfig: McpServerConfig[] = [];\n\n // Resolve the built MCP server script path relative to this package\n // import.meta.dirname is available in Node 21.2+; fall back to fileURLToPath for Node 18\n const currentDir = import.meta.dirname ?? dirname(fileURLToPath(import.meta.url));\n\n if (discordToolsEnabled) {\n const mcpScriptPath = resolvePath(currentDir, \"mcp-discord-channels.js\");\n const scriptExists = existsSync(mcpScriptPath);\n\n console.log(`[MCP] buildMcpServers: mcpScriptPath=${mcpScriptPath} exists=${scriptExists}`);\n if (!scriptExists) {\n console.warn(`[MCP] WARNING: MCP script not found at ${mcpScriptPath} — Discord tools will not work`);\n }\n\n mcpConfig.push({\n name: \"discord-channels\",\n command: \"node\",\n args: [mcpScriptPath],\n env: [\n { name: \"DISCORD_TOKEN\", value: config.discord.token },\n { name: \"GUILD_ID\", value: guildId },\n { name: \"IPC_SOCKET_PATH\", value: DEFAULT_IPC_SOCKET_PATH },\n { name: \"AGENT_NAME\", value: agentName },\n { name: \"SOURCE_CHANNEL_ID\", value: channelId },\n ],\n });\n }\n\n if (scheduledTasksEnabled) {\n const tasksMcpPath = resolvePath(currentDir, \"mcp-scheduled-tasks.js\");\n const tasksScriptExists = existsSync(tasksMcpPath);\n\n console.log(`[MCP] buildMcpServers: tasksMcpPath=${tasksMcpPath} exists=${tasksScriptExists}`);\n if (!tasksScriptExists) {\n console.warn(`[MCP] WARNING: MCP script not found at ${tasksMcpPath} — Scheduled tasks tools will not work`);\n } else {\n mcpConfig.push({\n name: \"scheduled-tasks\",\n command: \"node\",\n args: [tasksMcpPath],\n env: [\n { name: \"IPC_SOCKET_PATH\", value: DEFAULT_IPC_SOCKET_PATH },\n { name: \"AGENT_NAME\", value: agentName },\n { name: \"SOURCE_CHANNEL_ID\", value: channelId },\n ],\n });\n }\n }\n\n console.log(`[MCP] buildMcpServers: returning ${mcpConfig.length} MCP server(s):`, JSON.stringify(mcpConfig.map(s => ({ name: s.name, command: s.command, args: s.args }))));\n return mcpConfig;\n }\n\n // --- Display helpers ---\n\n function accumulateDiffs(channelId: string, toolCallId: string, diffs: DiffContent[]) {\n if (diffs.length === 0) return;\n if (!pendingDiffs.has(channelId)) pendingDiffs.set(channelId, new Map());\n const channelDiffs = pendingDiffs.get(channelId)!;\n const existing = channelDiffs.get(toolCallId) ?? [];\n channelDiffs.set(toolCallId, existing.concat(diffs));\n }\n\n async function sendDiffsForTool(channelId: string, toolCallId: string) {\n // Skip if diffs were already shown at permission-request time\n const shownSet = permissionDiffShown.get(channelId);\n if (shownSet?.has(toolCallId)) {\n shownSet.delete(toolCallId);\n pendingDiffs.get(channelId)?.delete(toolCallId);\n return;\n }\n\n const channelDiffs = pendingDiffs.get(channelId);\n const diffs = channelDiffs?.get(toolCallId);\n if (!diffs || diffs.length === 0) return;\n\n const channel = await fetchChannel(channelId);\n if (!channel) return;\n\n const messages = formatDiff(diffs);\n for (const msg of messages) {\n await channel.send({ content: msg, allowedMentions: { parse: [] as const } });\n }\n\n channelDiffs!.delete(toolCallId);\n }\n\n async function fetchChannel(channelId: string): Promise<TextChannel | null> {\n const cached = discordClient.channels.cache.get(channelId) as TextChannel | undefined;\n if (cached) return cached;\n try {\n const fetched = await discordClient.channels.fetch(channelId);\n return fetched as TextChannel;\n } catch {\n return null;\n }\n }\n\n async function updateToolSummaryMessage(channelId: string) {\n const tools = toolStates.get(channelId);\n if (!tools) return;\n\n const content = formatToolSummary(tools);\n const channel = await fetchChannel(channelId);\n if (!channel) return;\n\n const stopButton = new ActionRowBuilder<ButtonBuilder>().addComponents(\n new ButtonBuilder()\n .setCustomId(`stop_${channelId}`)\n .setLabel(\"\\u23F9 Stop\")\n .setStyle(ButtonStyle.Secondary),\n );\n\n const noMentions = { parse: [] as const };\n const existing = toolSummaryMessages.get(channelId);\n if (existing) {\n await existing.edit({ content, components: [stopButton], allowedMentions: noMentions }).catch(() => {});\n } else {\n const msg = await channel.send({ content, components: [stopButton], allowedMentions: noMentions });\n toolSummaryMessages.set(channelId, msg);\n }\n }\n\n async function removeStopButton(channelId: string) {\n const msg = toolSummaryMessages.get(channelId);\n if (msg) {\n const tools = toolStates.get(channelId);\n const content = tools ? formatToolSummary(tools) : msg.content;\n await msg.edit({ content, components: [], allowedMentions: { parse: [] as const } }).catch(() => {});\n }\n }\n\n function scheduleFlushReply(channelId: string) {\n if (flushTimers.has(channelId)) return;\n flushTimers.set(\n channelId,\n setTimeout(() => {\n flushTimers.delete(channelId);\n flushReply(channelId, false);\n }, 500),\n );\n }\n\n async function flushReply(channelId: string, final: boolean) {\n const timer = flushTimers.get(channelId);\n if (timer) {\n clearTimeout(timer);\n flushTimers.delete(channelId);\n }\n\n const buffer = replyBuffers.get(channelId);\n if (!buffer) return;\n\n const channel = await fetchChannel(channelId);\n if (!channel) return;\n\n if (final) {\n // Send final reply as new message(s), delete streaming message\n const existing = replyMessages.get(channelId);\n if (existing) await existing.delete().catch(() => {});\n replyMessages.delete(channelId);\n\n const chunks = splitMessage(buffer);\n for (const chunk of chunks) {\n await channel.send(chunk);\n }\n replyBuffers.delete(channelId);\n } else {\n // Streaming update: edit existing message\n const truncated = buffer.length > 2000 ? buffer.slice(buffer.length - 1900) + \"...\" : buffer;\n const existing = replyMessages.get(channelId);\n if (existing) {\n await existing.edit(truncated).catch(() => {});\n } else {\n const msg = await channel.send(truncated);\n replyMessages.set(channelId, msg);\n }\n }\n }\n\n // --- Helper: resolve guild ID from a channel ---\n\n function getGuildId(message: Message): string | null {\n return message.guildId ?? null;\n }\n\n // --- Helper: prompt with MCP servers ---\n\n async function promptWithMcp(channelId: string, text: string, agentName: string, guildId: string | null, agentConfig: typeof config.agents[string], requestorId: string, images?: ImageAttachment[]): Promise<void> {\n const mcpServers = guildId ? buildMcpServers(channelId, agentName, guildId) : undefined;\n await sessionManager.prompt(channelId, text, agentName, agentConfig, requestorId, mcpServers, images);\n }\n\n // Max size for forwarded image attachments (10 MB). Larger images are skipped\n // to avoid OOM and to keep agent token usage reasonable.\n const MAX_IMAGE_BYTES = 10 * 1024 * 1024;\n\n async function fetchImage(url: string, mimeType: string, label: string): Promise<ImageAttachment | null> {\n try {\n const res = await fetch(url);\n if (!res.ok) {\n console.warn(`Failed to fetch image ${label}: ${res.status} ${res.statusText}`);\n return null;\n }\n const buf = Buffer.from(await res.arrayBuffer());\n if (buf.byteLength > MAX_IMAGE_BYTES) {\n console.warn(`Skipping image ${label}: ${buf.byteLength} bytes exceeds ${MAX_IMAGE_BYTES}`);\n return null;\n }\n return { data: buf.toString(\"base64\"), mimeType };\n } catch (err) {\n console.warn(`Error fetching image ${label}:`, err);\n return null;\n }\n }\n\n async function extractMessageImages(message: Message): Promise<ImageAttachment[]> {\n const out: ImageAttachment[] = [];\n for (const att of message.attachments.values()) {\n if (!att.contentType?.startsWith(\"image/\")) {\n console.log(`Ignoring non-image attachment ${att.name} (contentType=${att.contentType})`);\n continue;\n }\n if (att.size > MAX_IMAGE_BYTES) {\n console.warn(`Skipping image attachment ${att.name}: ${att.size} bytes exceeds ${MAX_IMAGE_BYTES}`);\n continue;\n }\n const img = await fetchImage(att.url, att.contentType, att.name);\n if (img) out.push(img);\n }\n return out;\n }\n\n // --- Discord client setup ---\n\n discordClient = new Client({\n intents: [\n GatewayIntentBits.Guilds,\n GatewayIntentBits.GuildMessages,\n GatewayIntentBits.MessageContent,\n ],\n });\n\n discordClient.on(\"error\", (err) => {\n console.error(\"Discord client error:\", err);\n });\n\n discordClient.on(\"warn\", (msg) => {\n console.warn(\"Discord client warning:\", msg);\n });\n\n discordClient.on(\"shardDisconnect\", (event, shardId) => {\n console.warn(`Shard ${shardId} disconnected (code: ${event.code})`);\n });\n\n discordClient.on(\"shardReconnecting\", (shardId) => {\n console.log(`Shard ${shardId} reconnecting...`);\n });\n\n discordClient.on(Events.ClientReady, async (c) => {\n console.log(`Discord bot ready: ${c.user.tag}`);\n\n // Register slash commands\n const askCommand = new SlashCommandBuilder()\n .setName(\"ask\")\n .setDescription(\"Ask the coding agent a question\")\n .addStringOption((opt) =>\n opt.setName(\"message\").setDescription(\"Your message\").setRequired(true),\n )\n .addAttachmentOption((opt) =>\n opt.setName(\"image\").setDescription(\"Optional image attachment\").setRequired(false),\n );\n\n const clearCommand = new SlashCommandBuilder()\n .setName(\"clear\")\n .setDescription(\"Clear the agent session and start fresh\");\n\n const rest = new REST().setToken(config.discord.token);\n try {\n await rest.put(Routes.applicationCommands(c.application.id), {\n body: [askCommand.toJSON(), clearCommand.toJSON()],\n });\n console.log(\"Registered /ask and /clear commands\");\n } catch (err) {\n console.error(\"Failed to register commands:\", err);\n }\n });\n\n // Handle @mention messages in configured channels\n discordClient.on(Events.MessageCreate, async (message: Message) => {\n if (message.author.bot) return;\n\n const channelId = message.channelId;\n const resolved = router.resolve(channelId);\n if (!resolved) return;\n\n const isMention = message.mentions.has(discordClient.user!);\n if (!resolved.autoReply && !isMention) return;\n\n // Strip mention prefix if present\n const text = message.content.replace(/<@!?\\d+>/g, \"\").trim();\n const images = await extractMessageImages(message);\n\n if (!text && images.length === 0) {\n await message.reply(\"Please provide a message or an image.\");\n return;\n }\n\n startTyping(channelId);\n\n if (sessionManager.isPrompting(channelId)) {\n await message.reply(\"\\u23F3 Agent is working. Your message has been queued.\");\n }\n\n try {\n await promptWithMcp(channelId, text, resolved.agentName, getGuildId(message), resolved.agent, message.author.id, images);\n } catch (err) {\n stopTyping(channelId);\n const errMsg = err instanceof Error ? err.message : String(err);\n console.error(`Prompt failed for channel ${channelId}:`, err);\n if (errMsg.includes(\"sessions are busy\")) {\n await message.reply(\"\\u23F3 All agent sessions are busy. Please wait for the current task to finish and try again.\").catch(() => {});\n } else {\n await message.reply(\"An error occurred while processing your request.\").catch(() => {});\n }\n }\n });\n\n // Handle stop button clicks and MCP confirmation buttons\n discordClient.on(Events.InteractionCreate, async (interaction) => {\n if (!interaction.isButton()) return;\n\n if (interaction.customId.startsWith(\"stop_\")) {\n const channelId = interaction.customId.replace(\"stop_\", \"\");\n const activeRequestor = sessionManager.getActiveRequestorId(channelId);\n\n // Only the user who triggered the current prompt can stop it\n if (activeRequestor && interaction.user.id !== activeRequestor) {\n await interaction.reply({ content: \"Only the user who started this prompt can stop it.\", ephemeral: true });\n return;\n }\n\n sessionManager.cancel(channelId);\n await interaction.update({ components: [] });\n }\n\n // Handle MCP confirmation buttons\n if (interaction.customId.startsWith(\"mcp_approve_\") || interaction.customId.startsWith(\"mcp_reject_\")) {\n const approved = interaction.customId.startsWith(\"mcp_approve_\");\n const requestId = interaction.customId.replace(/^mcp_(approve|reject)_/, \"\");\n const pending = pendingConfirmations.get(requestId);\n if (pending) {\n // Only the user who triggered the prompt can approve/reject\n if (pending.allowedUserId && interaction.user.id !== pending.allowedUserId) {\n await interaction.reply({ content: \"Only the user who started this prompt can approve or reject.\", ephemeral: true });\n return;\n }\n await interaction.deferUpdate();\n pending.resolve(approved);\n } else {\n await interaction.reply({ content: \"This confirmation has expired.\", ephemeral: true });\n }\n }\n });\n\n // Handle /ask command\n discordClient.on(Events.InteractionCreate, async (interaction) => {\n if (!interaction.isChatInputCommand()) return;\n if (interaction.commandName !== \"ask\") return;\n\n const channelId = interaction.channelId;\n const resolved = router.resolve(channelId);\n if (!resolved) {\n await interaction.reply({ content: \"This channel is not configured for ACP.\", ephemeral: true });\n return;\n }\n\n const text = interaction.options.getString(\"message\", true);\n const imageOpt = interaction.options.getAttachment(\"image\");\n await interaction.deferReply();\n const images: ImageAttachment[] = [];\n if (imageOpt) {\n if (!imageOpt.contentType?.startsWith(\"image/\")) {\n console.log(`/ask: ignoring non-image attachment ${imageOpt.name} (contentType=${imageOpt.contentType})`);\n } else if (imageOpt.size > MAX_IMAGE_BYTES) {\n console.warn(`/ask: skipping oversized image ${imageOpt.name}: ${imageOpt.size} bytes`);\n } else {\n const img = await fetchImage(imageOpt.url, imageOpt.contentType, imageOpt.name);\n if (img) images.push(img);\n }\n }\n startTyping(channelId);\n\n if (sessionManager.isPrompting(channelId)) {\n await interaction.editReply(\"\\u23F3 Agent is working. Your message has been queued.\");\n } else {\n await interaction.editReply(`\\uD83D\\uDCAC Processing: ${text.slice(0, 100)}...`);\n }\n\n try {\n const guildId = interaction.guildId ?? null;\n await promptWithMcp(channelId, text, resolved.agentName, guildId, resolved.agent, interaction.user.id, images);\n } catch (err) {\n stopTyping(channelId);\n const errMsg = err instanceof Error ? err.message : String(err);\n console.error(`Prompt failed for channel ${channelId}:`, err);\n const reply = errMsg.includes(\"sessions are busy\")\n ? \"\\u23F3 All agent sessions are busy. Please wait for the current task to finish and try again.\"\n : \"An error occurred while processing your request.\";\n await interaction.followUp({ content: reply, ephemeral: true }).catch(() => {});\n }\n });\n\n // Handle /clear command\n discordClient.on(Events.InteractionCreate, async (interaction) => {\n if (!interaction.isChatInputCommand()) return;\n if (interaction.commandName !== \"clear\") return;\n\n const channelId = interaction.channelId;\n sessionManager.teardown(channelId);\n\n // Clean up display state\n stopTyping(channelId);\n toolStates.delete(channelId);\n toolSummaryMessages.delete(channelId);\n replyBuffers.delete(channelId);\n replyMessages.delete(channelId);\n pendingDiffs.delete(channelId);\n permissionDiffShown.delete(channelId);\n const timer = flushTimers.get(channelId);\n if (timer) clearTimeout(timer);\n flushTimers.delete(channelId);\n\n await interaction.reply(\"Session cleared. Next message will start a fresh agent.\");\n });\n\n // --- Start IPC server and task scheduler ---\n await ipcServer.start();\n taskScheduler.start();\n\n // Graceful shutdown\n process.on(\"SIGTERM\", () => {\n for (const channelId of typingIntervals.keys()) stopTyping(channelId);\n taskScheduler.stop();\n ipcServer.stop();\n sessionManager.teardownAll();\n discordClient.destroy();\n });\n\n process.on(\"SIGINT\", () => {\n for (const channelId of typingIntervals.keys()) stopTyping(channelId);\n taskScheduler.stop();\n ipcServer.stop();\n sessionManager.teardownAll();\n discordClient.destroy();\n });\n\n try {\n await discordClient.login(config.discord.token);\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err);\n if (message.includes(\"TOKEN_INVALID\") || message.includes(\"An invalid token was provided\")) {\n console.error(\"Error: Invalid Discord bot token. Check your config.toml.\");\n } else if (message.includes(\"ConnectTimeout\") || message.includes(\"ETIMEDOUT\") || message.includes(\"ECONNREFUSED\")) {\n console.error(\"Error: Cannot connect to Discord API. Check your network or proxy settings.\");\n console.error(\"Hint: Set HTTPS_PROXY=http://127.0.0.1:7890 if you need a proxy.\");\n } else {\n console.error(\"Error: Failed to connect to Discord:\", message);\n }\n ipcServer.stop();\n process.exit(1);\n }\n}\n","import type { AppConfig, ResolvedChannelConfig } from \"../shared/types.js\";\nimport { resolveChannelConfig } from \"../shared/config.js\";\n\nexport class ChannelRouter {\n private config: AppConfig;\n private dynamicChannels = new Map<string, { agentName: string; autoReply: boolean }>();\n\n constructor(config: AppConfig) {\n this.config = config;\n }\n\n resolve(channelId: string): ResolvedChannelConfig | null {\n return resolveChannelConfig(this.config, channelId);\n }\n\n isConfigured(channelId: string): boolean {\n return this.resolve(channelId) !== null;\n }\n\n registerDynamic(channelId: string, agentName: string, autoReply: boolean): void {\n if (!this.config.agents[agentName]) {\n console.error(`Cannot register dynamic channel: unknown agent \"${agentName}\"`);\n return;\n }\n this.dynamicChannels.set(channelId, { agentName, autoReply });\n // Merge into existing channel config to preserve cwd/discord_tools overrides\n const existing = this.config.channels[channelId];\n this.config.channels[channelId] = {\n ...existing,\n agent: agentName,\n auto_reply: autoReply,\n };\n }\n\n unregisterDynamic(channelId: string): void {\n this.dynamicChannels.delete(channelId);\n delete this.config.channels[channelId];\n }\n\n updateConfig(newConfig: AppConfig): void {\n this.config = newConfig;\n // Re-inject dynamic channels that aren't already in the new config\n for (const [channelId, { agentName, autoReply }] of this.dynamicChannels) {\n if (!this.config.channels[channelId] && this.config.agents[agentName]) {\n this.config.channels[channelId] = {\n agent: agentName,\n auto_reply: autoReply,\n };\n }\n }\n }\n\n getConfig(): AppConfig {\n return this.config;\n }\n}\n","import { spawn, type ChildProcess } from \"node:child_process\";\nimport { readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { Readable, Writable } from \"node:stream\";\nimport { ClientSideConnection, ndJsonStream, PROTOCOL_VERSION, type ContentBlock } from \"@agentclientprotocol/sdk\";\nimport type { AgentConfig } from \"../shared/types.js\";\nimport { createAcpClient, type AcpEventHandlers } from \"./acp-client.js\";\n\nexport interface ImageAttachment {\n /** base64-encoded image bytes */\n data: string;\n /** e.g. \"image/png\", \"image/jpeg\" */\n mimeType: string;\n}\n\nexport interface McpServerConfig {\n name: string;\n command: string;\n args: string[];\n env: Array<{ name: string; value: string }>;\n}\n\ninterface ManagedSession {\n channelId: string;\n agentName: string;\n process: ChildProcess;\n connection: ClientSideConnection;\n sessionId: string;\n lastActivity: number;\n idleTimer: NodeJS.Timeout;\n prompting: boolean;\n queue: Array<{ text: string; requestorId: string; images?: ImageAttachment[] }>;\n /** Set only when executePrompt begins — stable for the duration of the prompt */\n activePromptRequestorId: string;\n}\n\ninterface PersistedSession {\n sessionId: string;\n agentName: string;\n}\n\nexport class SessionManager {\n private sessions = new Map<string, ManagedSession>();\n private handlers: AcpEventHandlers;\n private sessionsPath: string;\n private pendingResumes = new Map<string, PersistedSession>();\n private maxConcurrentSessions: number;\n\n constructor(handlers: AcpEventHandlers, sessionsPath: string, maxConcurrentSessions = 2) {\n this.handlers = handlers;\n this.sessionsPath = sessionsPath;\n this.maxConcurrentSessions = maxConcurrentSessions;\n this.loadSessionMap();\n }\n\n private loadSessionMap(): void {\n try {\n const data = readFileSync(this.sessionsPath, \"utf-8\");\n const map = JSON.parse(data) as Record<string, PersistedSession>;\n for (const [channelId, entry] of Object.entries(map)) {\n this.pendingResumes.set(channelId, entry);\n }\n if (this.pendingResumes.size > 0) {\n console.log(`Loaded ${this.pendingResumes.size} session(s) for lazy resume`);\n }\n } catch (err: unknown) {\n // ENOENT is expected on first run; log other errors for diagnosability\n if (err instanceof Error && \"code\" in err && (err as NodeJS.ErrnoException).code === \"ENOENT\") return;\n if (err instanceof SyntaxError) {\n console.warn(\"Corrupt sessions.json, starting fresh:\", err.message);\n }\n }\n }\n\n saveSessionMap(): void {\n const map: Record<string, PersistedSession> = {};\n // Include unresumed pending sessions so they survive daemon restarts\n // where no messages were received for that channel\n for (const [channelId, entry] of this.pendingResumes) {\n map[channelId] = entry;\n }\n // Active sessions override any pending resume for the same channel\n for (const [channelId, session] of this.sessions) {\n map[channelId] = {\n sessionId: session.sessionId,\n agentName: session.agentName,\n };\n }\n try {\n mkdirSync(dirname(this.sessionsPath), { recursive: true });\n writeFileSync(this.sessionsPath, JSON.stringify(map, null, 2));\n } catch (err) {\n console.error(\"Failed to save session map:\", err);\n }\n }\n\n async prompt(channelId: string, text: string, agentName: string, agentConfig: AgentConfig, requestorId: string, mcpServers?: McpServerConfig[], images?: ImageAttachment[]): Promise<string> {\n console.log(`[MCP] prompt: channel=${channelId} mcpServers=${mcpServers ? `[${mcpServers.length} server(s)]` : \"undefined\"}${images?.length ? ` images=${images.length}` : \"\"}`);\n const session = await this.getOrCreate(channelId, agentName, agentConfig, requestorId, mcpServers);\n session.lastActivity = Date.now();\n this.resetIdleTimer(session, agentConfig.idle_timeout);\n\n if (session.prompting) {\n session.queue.push({ text, requestorId, images });\n return \"queued\";\n }\n\n return this.executePrompt(session, text, requestorId, agentConfig, images);\n }\n\n private async executePrompt(session: ManagedSession, text: string, requestorId: string, agentConfig: AgentConfig, images?: ImageAttachment[]): Promise<string> {\n session.prompting = true;\n session.activePromptRequestorId = requestorId;\n try {\n const contentBlocks: ContentBlock[] = [];\n if (text) contentBlocks.push({ type: \"text\", text });\n if (images?.length) {\n for (const img of images) {\n contentBlocks.push({ type: \"image\", data: img.data, mimeType: img.mimeType });\n }\n }\n const result = await session.connection.prompt({\n sessionId: session.sessionId,\n prompt: contentBlocks,\n });\n this.handlers.onPromptComplete(session.channelId, result.stopReason);\n return result.stopReason;\n } catch (err) {\n // Connection broken or agent crashed — teardown to kill orphaned processes\n console.error(`Prompt error for channel ${session.channelId}, tearing down session:`, err);\n this.handlers.onPromptComplete(session.channelId, \"error\");\n this.teardown(session.channelId);\n throw err;\n } finally {\n session.prompting = false;\n // Process queue only if session is still alive (not torn down above)\n if (this.sessions.has(session.channelId)) {\n const next = session.queue.shift();\n if (next) {\n this.executePrompt(session, next.text, next.requestorId, agentConfig, next.images).catch((err) => {\n console.error(`Queued prompt failed for channel ${session.channelId}:`, err);\n });\n }\n }\n }\n }\n\n cancel(channelId: string): void {\n const session = this.sessions.get(channelId);\n if (session) {\n session.connection.cancel({ sessionId: session.sessionId });\n }\n }\n\n private async getOrCreate(channelId: string, agentName: string, agentConfig: AgentConfig, requestorId: string, mcpServers?: McpServerConfig[]): Promise<ManagedSession> {\n const existing = this.sessions.get(channelId);\n if (existing) {\n console.log(`[MCP] getOrCreate: reusing existing session for channel=${channelId} (mcpServers passed but ignored: ${mcpServers ? mcpServers.length : 0} server(s))`);\n return existing;\n }\n\n // Check for a pending resume from a previous daemon run\n const pending = this.pendingResumes.get(channelId);\n if (pending && pending.agentName === agentName) {\n this.pendingResumes.delete(channelId);\n try {\n return await this.resumeSession(channelId, agentName, agentConfig, requestorId, pending.sessionId, mcpServers);\n } catch (err) {\n console.warn(`Session resume failed for channel ${channelId}, creating new session:`, err);\n // Fall through to create a new session\n }\n } else if (pending) {\n // Agent name changed since last run — discard stale resume\n this.pendingResumes.delete(channelId);\n }\n\n // Evict oldest idle session(s) if at capacity\n this.evictIfNeeded();\n\n console.log(`[MCP] getOrCreate: creating new session for channel=${channelId} with ${mcpServers?.length ?? 0} MCP server(s)`);\n return this.createSession(channelId, agentName, agentConfig, requestorId, mcpServers);\n }\n\n private evictIfNeeded(): void {\n while (this.sessions.size >= this.maxConcurrentSessions) {\n // Find the least-recently-active non-prompting (idle) session\n let oldest: ManagedSession | null = null;\n for (const session of this.sessions.values()) {\n if (session.prompting) continue;\n if (!oldest || session.lastActivity < oldest.lastActivity) {\n oldest = session;\n }\n }\n if (!oldest) {\n // All sessions are actively prompting — refuse to evict\n throw new Error(\"All agent sessions are busy. Please wait for the current task to finish.\");\n }\n console.log(`Evicting idle session for channel ${oldest.channelId} (lastActivity=${new Date(oldest.lastActivity).toISOString()}) to make room`);\n this.teardown(oldest.channelId);\n }\n }\n\n private async createSession(channelId: string, agentName: string, config: AgentConfig, requestorId: string, mcpServers?: McpServerConfig[]): Promise<ManagedSession> {\n const proc = spawn(config.command, config.args, {\n stdio: [\"pipe\", \"pipe\", \"inherit\"],\n cwd: config.cwd,\n detached: true, // Create new process group so we can kill the entire tree\n });\n\n // Handle spawn errors (ENOENT, permission denied, etc.) (#4)\n proc.on(\"error\", (err) => {\n console.error(`Agent process error for channel ${channelId}:`, err);\n const session = this.sessions.get(channelId);\n if (session?.process === proc) {\n clearTimeout(session.idleTimer);\n this.sessions.delete(channelId);\n }\n });\n\n proc.on(\"exit\", (code) => {\n const session = this.sessions.get(channelId);\n if (session?.process === proc) {\n clearTimeout(session.idleTimer);\n this.sessions.delete(channelId);\n if (code !== 0 && code !== null) {\n console.warn(`Agent process for channel ${channelId} exited with code ${code}`);\n }\n }\n });\n\n // Wrap initialize/newSession in try/catch to clean up process on failure (#5)\n let connection: ClientSideConnection;\n let sessionId: string;\n try {\n const stream = ndJsonStream(\n Writable.toWeb(proc.stdin!) as WritableStream<Uint8Array>,\n Readable.toWeb(proc.stdout!) as ReadableStream<Uint8Array>,\n );\n\n const client = createAcpClient(channelId, this.handlers, () => {\n return this.sessions.get(channelId)?.activePromptRequestorId ?? requestorId;\n });\n connection = new ClientSideConnection((_agent) => client, stream);\n\n await connection.initialize({\n protocolVersion: PROTOCOL_VERSION,\n clientCapabilities: {\n fs: { readTextFile: true, writeTextFile: true },\n terminal: true,\n },\n clientInfo: {\n name: \"acp-discord\",\n title: \"ACP Discord Bot\",\n version: \"0.1.0\",\n },\n });\n\n const newSessionPayload = {\n cwd: config.cwd,\n mcpServers: mcpServers ?? [],\n };\n console.log(`[MCP] createSession: calling newSession for channel=${channelId}`, JSON.stringify({\n cwd: newSessionPayload.cwd,\n mcpServerCount: newSessionPayload.mcpServers.length,\n mcpServerNames: newSessionPayload.mcpServers.map(s => s.name),\n }));\n const result = await connection.newSession(newSessionPayload);\n sessionId = result.sessionId;\n console.log(`[MCP] createSession: newSession succeeded, sessionId=${sessionId}`);\n } catch (err) {\n this.killProcessTree(proc);\n throw err;\n }\n\n const managed: ManagedSession = {\n channelId,\n agentName,\n process: proc,\n connection,\n sessionId,\n lastActivity: Date.now(),\n idleTimer: this.startIdleTimer(channelId, config.idle_timeout),\n prompting: false,\n queue: [],\n activePromptRequestorId: requestorId,\n };\n\n this.sessions.set(channelId, managed);\n return managed;\n }\n\n private async resumeSession(channelId: string, agentName: string, config: AgentConfig, requestorId: string, previousSessionId: string, mcpServers?: McpServerConfig[]): Promise<ManagedSession> {\n const proc = spawn(config.command, config.args, {\n stdio: [\"pipe\", \"pipe\", \"inherit\"],\n cwd: config.cwd,\n detached: true, // Create new process group so we can kill the entire tree\n });\n\n proc.on(\"error\", (err) => {\n console.error(`Agent process error for channel ${channelId}:`, err);\n const session = this.sessions.get(channelId);\n if (session?.process === proc) {\n clearTimeout(session.idleTimer);\n this.sessions.delete(channelId);\n }\n });\n\n proc.on(\"exit\", (code) => {\n const session = this.sessions.get(channelId);\n if (session?.process === proc) {\n clearTimeout(session.idleTimer);\n this.sessions.delete(channelId);\n if (code !== 0 && code !== null) {\n console.warn(`Agent process for channel ${channelId} exited with code ${code}`);\n }\n }\n });\n\n let connection: ClientSideConnection;\n let sessionId: string;\n try {\n const stream = ndJsonStream(\n Writable.toWeb(proc.stdin!) as WritableStream<Uint8Array>,\n Readable.toWeb(proc.stdout!) as ReadableStream<Uint8Array>,\n );\n\n const client = createAcpClient(channelId, this.handlers, () => {\n return this.sessions.get(channelId)?.activePromptRequestorId ?? requestorId;\n });\n connection = new ClientSideConnection((_agent) => client, stream);\n\n const initResult = await connection.initialize({\n protocolVersion: PROTOCOL_VERSION,\n clientCapabilities: {\n fs: { readTextFile: true, writeTextFile: true },\n terminal: true,\n },\n clientInfo: {\n name: \"acp-discord\",\n title: \"ACP Discord Bot\",\n version: \"0.1.0\",\n },\n });\n\n // Check if agent supports session resume\n const supportsResume = !!initResult.agentCapabilities?.sessionCapabilities?.resume;\n if (!supportsResume) {\n this.killProcessTree(proc);\n throw new Error(\"Agent does not support session resume\");\n }\n\n await connection.unstable_resumeSession({\n sessionId: previousSessionId,\n cwd: config.cwd,\n mcpServers: mcpServers ?? [],\n });\n sessionId = previousSessionId;\n console.log(`Resumed session ${sessionId} for channel ${channelId}`);\n } catch (err) {\n this.killProcessTree(proc);\n throw err;\n }\n\n const managed: ManagedSession = {\n channelId,\n agentName,\n process: proc,\n connection,\n sessionId,\n lastActivity: Date.now(),\n idleTimer: this.startIdleTimer(channelId, config.idle_timeout),\n prompting: false,\n queue: [],\n activePromptRequestorId: requestorId,\n };\n\n this.sessions.set(channelId, managed);\n return managed;\n }\n\n private startIdleTimer(channelId: string, timeoutSec: number): NodeJS.Timeout {\n return setTimeout(() => this.teardown(channelId), timeoutSec * 1000);\n }\n\n private resetIdleTimer(session: ManagedSession, timeoutSec: number): void {\n clearTimeout(session.idleTimer);\n session.idleTimer = this.startIdleTimer(session.channelId, timeoutSec);\n }\n\n teardown(channelId: string): void {\n this.pendingResumes.delete(channelId);\n const session = this.sessions.get(channelId);\n if (!session) return;\n clearTimeout(session.idleTimer);\n // Kill process group to ensure MCP child processes are also terminated\n this.killProcessTree(session.process);\n this.sessions.delete(channelId);\n }\n\n private killProcessTree(proc: ChildProcess): void {\n if (!proc.pid) {\n proc.kill();\n return;\n }\n try {\n // Kill the entire process group (negative PID) to clean up MCP children\n process.kill(-proc.pid, \"SIGTERM\");\n } catch {\n // Process group kill failed (e.g. not a group leader) — fall back to direct kill\n try { proc.kill(\"SIGTERM\"); } catch { /* already dead */ }\n }\n // Force-kill after 5s if still alive\n setTimeout(() => {\n try { process.kill(-proc.pid!, \"SIGKILL\"); } catch { /* already dead */ }\n try { proc.kill(\"SIGKILL\"); } catch { /* already dead */ }\n }, 5000).unref();\n }\n\n teardownAll(): void {\n this.saveSessionMap();\n for (const channelId of this.sessions.keys()) {\n this.teardown(channelId);\n }\n }\n\n isPrompting(channelId: string): boolean {\n return this.sessions.get(channelId)?.prompting ?? false;\n }\n\n getActiveRequestorId(channelId: string): string | null {\n const session = this.sessions.get(channelId);\n if (!session?.prompting) return null;\n return session.activePromptRequestorId;\n }\n\n getActiveChannels(): string[] {\n return Array.from(this.sessions.keys());\n }\n}\n","import type {\n Client,\n RequestPermissionRequest,\n RequestPermissionResponse,\n SessionNotification,\n} from \"@agentclientprotocol/sdk\";\n\nexport interface DiffContent {\n path: string;\n oldText?: string | null;\n newText: string;\n}\n\nexport interface AcpEventHandlers {\n onToolCall(channelId: string, toolCallId: string, title: string, kind: string, status: string, diffs: DiffContent[], rawInput?: Record<string, unknown>): void;\n onToolCallUpdate(channelId: string, toolCallId: string, status: string, diffs: DiffContent[], rawInput?: Record<string, unknown>): void;\n onAgentMessageChunk(channelId: string, text: string): void;\n onPermissionRequest(\n channelId: string,\n requestorId: string,\n toolCall: { toolCallId: string; title: string; kind: string },\n options: Array<{ optionId: string; name: string; kind: string }>,\n diffs: DiffContent[],\n ): Promise<{ outcome: \"selected\"; optionId: string } | { outcome: \"cancelled\" }>;\n onPromptComplete(channelId: string, stopReason: string): void;\n}\n\nexport function createAcpClient(\n channelId: string,\n handlers: AcpEventHandlers,\n getRequestorId: () => string,\n): Client {\n return {\n async requestPermission(params: RequestPermissionRequest): Promise<RequestPermissionResponse> {\n const diffs = extractDiffs((params.toolCall as { content?: unknown }).content);\n const result = await handlers.onPermissionRequest(\n channelId,\n getRequestorId(),\n {\n toolCallId: params.toolCall.toolCallId,\n title: params.toolCall.title ?? \"Unknown\",\n kind: params.toolCall.kind ?? \"other\",\n },\n params.options.map((o: { optionId: string; name: string; kind: string }) => ({\n optionId: o.optionId,\n name: o.name,\n kind: o.kind,\n })),\n diffs,\n );\n\n if (result.outcome === \"selected\") {\n return { outcome: { outcome: \"selected\", optionId: result.optionId } };\n }\n return { outcome: { outcome: \"cancelled\" } };\n },\n\n async sessionUpdate(params: SessionNotification): Promise<void> {\n const update = params.update;\n switch (update.sessionUpdate) {\n case \"agent_message_chunk\": {\n if (update.content.type === \"text\") {\n handlers.onAgentMessageChunk(channelId, update.content.text);\n }\n break;\n }\n case \"tool_call\": {\n const rawKeys = (update as Record<string, unknown>).rawInput;\n console.log(`[DEBUG tool_call] toolCallId=${update.toolCallId} title=${update.title} rawInputKeys=${rawKeys && typeof rawKeys === \"object\" ? Object.keys(rawKeys).join(\",\") : String(rawKeys)} updateKeys=${Object.keys(update).join(\",\")}`);\n const toolCallDiffs = extractDiffs(update.content);\n const rawVal = (update as Record<string, unknown>).rawInput;\n const rawInput = typeof rawVal === \"object\" && rawVal !== null && !Array.isArray(rawVal)\n ? (rawVal as Record<string, unknown>)\n : undefined;\n handlers.onToolCall(\n channelId,\n update.toolCallId,\n update.title ?? \"Unknown\",\n update.kind ?? \"other\",\n update.status ?? \"pending\",\n toolCallDiffs,\n rawInput,\n );\n break;\n }\n case \"tool_call_update\": {\n const updateRawKeys = (update as Record<string, unknown>).rawInput;\n console.log(`[DEBUG tool_call_update] toolCallId=${update.toolCallId} rawInputKeys=${updateRawKeys && typeof updateRawKeys === \"object\" ? Object.keys(updateRawKeys).join(\",\") : String(updateRawKeys)} updateKeys=${Object.keys(update).join(\",\")}`);\n const updateDiffs = extractDiffs(update.content);\n const updateRawVal = (update as Record<string, unknown>).rawInput;\n const updateRawInput = typeof updateRawVal === \"object\" && updateRawVal !== null && !Array.isArray(updateRawVal)\n ? (updateRawVal as Record<string, unknown>)\n : undefined;\n handlers.onToolCallUpdate(\n channelId,\n update.toolCallId,\n update.status ?? \"in_progress\",\n updateDiffs,\n updateRawInput,\n );\n break;\n }\n }\n },\n };\n}\n\nfunction extractDiffs(content: unknown): DiffContent[] {\n if (!Array.isArray(content)) return [];\n const diffs: DiffContent[] = [];\n for (const item of content) {\n if (item && typeof item === \"object\" && \"type\" in item && item.type === \"diff\") {\n const { path, oldText, newText } = item as Record<string, unknown>;\n if (typeof path !== \"string\" || typeof newText !== \"string\") continue;\n if (oldText !== undefined && oldText !== null && typeof oldText !== \"string\") continue;\n diffs.push({ path, oldText: (oldText as string | null) ?? null, newText });\n }\n }\n return diffs;\n}\n","import {\n ActionRowBuilder,\n ButtonBuilder,\n ButtonStyle,\n EmbedBuilder,\n type Message,\n type TextChannel,\n} from \"discord.js\";\nimport type { DiffContent } from \"./acp-client.js\";\nimport { formatDiff } from \"./message-bridge.js\";\n\nconst KIND_LABELS: Record<string, string> = {\n allow_once: \"\\u2705 Allow\",\n allow_always: \"\\u2705 Always Allow\",\n reject_once: \"\\u274C Reject\",\n reject_always: \"\\u274C Never Allow\",\n};\n\nconst KIND_STYLES: Record<string, ButtonStyle> = {\n allow_once: ButtonStyle.Success,\n allow_always: ButtonStyle.Success,\n reject_once: ButtonStyle.Danger,\n reject_always: ButtonStyle.Danger,\n};\n\nexport interface PermissionOption {\n optionId: string;\n name: string;\n kind: string;\n}\n\nexport async function sendPermissionRequest(\n channel: TextChannel,\n toolTitle: string,\n toolKind: string,\n options: PermissionOption[],\n requestorId: string,\n diffs: DiffContent[] = [],\n timeoutMs = 14 * 60 * 1000,\n): Promise<{ outcome: \"selected\"; optionId: string; diffsSent?: boolean } | { outcome: \"cancelled\"; diffsSent?: boolean }> {\n if (options.length === 0) {\n return { outcome: \"cancelled\" };\n }\n\n // Send diffs before the permission embed so the user can review changes\n let diffsSent = false;\n const diffMsgs: Message[] = [];\n if (diffs.length > 0) {\n try {\n const diffMessages = formatDiff(diffs);\n for (const content of diffMessages) {\n diffMsgs.push(await channel.send(content));\n }\n diffsSent = true;\n } catch (err) {\n console.error(\"Failed to send permission diffs:\", err);\n }\n }\n\n const embed = new EmbedBuilder()\n .setColor(0xffa500)\n .setTitle(`Permission: ${toolTitle}`)\n .setDescription(`Tool type: \\`${toolKind}\\``)\n .setTimestamp();\n\n const buttons = options.map((opt) =>\n new ButtonBuilder()\n .setCustomId(`perm_${opt.optionId}`)\n .setLabel(KIND_LABELS[opt.kind] ?? opt.name)\n .setStyle(KIND_STYLES[opt.kind] ?? ButtonStyle.Secondary),\n );\n\n // Discord allows max 5 buttons per ActionRow\n const rows: ActionRowBuilder<ButtonBuilder>[] = [];\n for (let i = 0; i < buttons.length; i += 5) {\n rows.push(new ActionRowBuilder<ButtonBuilder>().addComponents(buttons.slice(i, i + 5)));\n }\n\n const msg = await channel.send({ embeds: [embed], components: rows });\n\n return new Promise((resolve) => {\n const collector = msg.createMessageComponentCollector({\n filter: (i) => i.user.id === requestorId,\n time: timeoutMs,\n });\n\n const cleanup = () => {\n for (const dm of diffMsgs) dm.delete().catch(() => {});\n msg.delete().catch(() => msg.edit({ components: [] }).catch(() => {}));\n };\n\n collector.on(\"collect\", async (interaction) => {\n const optionId = interaction.customId.replace(\"perm_\", \"\");\n await interaction.deferUpdate();\n cleanup();\n collector.stop(\"selected\");\n resolve({ outcome: \"selected\", optionId, diffsSent });\n });\n\n collector.on(\"end\", (_collected, reason) => {\n if (reason !== \"selected\") {\n cleanup();\n resolve({ outcome: \"cancelled\", diffsSent });\n }\n });\n });\n}\n","import { createTwoFilesPatch } from \"diff\";\nimport type { DiffContent } from \"./acp-client.js\";\n\nconst DISCORD_MAX_LENGTH = 2000;\nconst MAX_DIFF_LINES = 150;\n\nexport function splitMessage(text: string, maxLength = DISCORD_MAX_LENGTH): string[] {\n if (text.length <= maxLength) return [text];\n\n const chunks: string[] = [];\n let remaining = text;\n let inCodeBlock = false;\n let codeFence = \"\";\n\n while (remaining.length > 0) {\n if (remaining.length <= maxLength) {\n chunks.push(remaining);\n break;\n }\n\n // Find split point: prefer newline before maxLength\n let splitAt = maxLength;\n const lastNewline = remaining.lastIndexOf(\"\\n\", maxLength);\n if (lastNewline > maxLength * 0.5) {\n splitAt = lastNewline + 1;\n }\n\n let chunk = remaining.slice(0, splitAt);\n remaining = remaining.slice(splitAt);\n\n // Handle code blocks: count fences in this chunk\n const fenceMatches = chunk.match(/```\\w*/g) || [];\n for (const fence of fenceMatches) {\n if (!inCodeBlock) {\n inCodeBlock = true;\n codeFence = fence;\n } else {\n inCodeBlock = false;\n codeFence = \"\";\n }\n }\n\n // If we're inside a code block at the split, close and reopen\n if (inCodeBlock) {\n chunk += \"\\n```\";\n remaining = codeFence + \"\\n\" + remaining;\n inCodeBlock = false;\n codeFence = \"\";\n }\n\n chunks.push(chunk);\n }\n\n return chunks;\n}\n\nexport type ToolStatus = \"pending\" | \"in_progress\" | \"completed\" | \"failed\";\n\nconst STATUS_ICONS: Record<ToolStatus, string> = {\n pending: \"\\u23F3\", // ⏳\n in_progress: \"\\uD83D\\uDD04\", // 🔄\n completed: \"\\u2705\", // ✅\n failed: \"\\u274C\", // ❌\n};\n\nexport function formatToolSummary(\n tools: Map<string, { title: string; status: ToolStatus; rawInput?: Record<string, unknown> }>,\n): string {\n const lines: string[] = [];\n for (const [, tool] of tools) {\n const detail = extractToolDetail(tool.rawInput) ?? extractDetailFromTitle(tool.title);\n const suffix = detail ? ` · \\`${detail}\\`` : \"\";\n lines.push(`${STATUS_ICONS[tool.status]} ${tool.title}${suffix}`);\n }\n return lines.join(\"\\n\");\n}\n\nconst MAX_DETAIL_LENGTH = 80;\n\n// Only display values from known-safe fields to avoid leaking secrets\nconst SAFE_FIELDS = [\"command\", \"file_path\", \"pattern\", \"query\", \"path\", \"url\", \"description\"];\n\n// Substrings that mark a field as sensitive — matches \"secret_key\", \"api_key\", \"access_token\", etc.\nconst BLOCKED_SUBSTRINGS = [\"token\", \"secret\", \"password\", \"key\", \"content\", \"new_string\", \"old_string\", \"credential\", \"auth\"];\n\nfunction isBlockedField(name: string): boolean {\n const lower = name.toLowerCase();\n return BLOCKED_SUBSTRINGS.some((sub) => lower.includes(sub));\n}\n\nfunction extractToolDetail(rawInput?: Record<string, unknown>): string | null {\n if (!rawInput) return null;\n\n // Try known safe fields first\n for (const field of SAFE_FIELDS) {\n if (typeof rawInput[field] === \"string\" && rawInput[field]) {\n return truncate(sanitizeDetail(rawInput[field] as string), MAX_DETAIL_LENGTH);\n }\n }\n\n // Fallback: pick the first short string value from non-blocked fields\n for (const [fieldName, value] of Object.entries(rawInput)) {\n if (isBlockedField(fieldName)) continue;\n if (SAFE_FIELDS.includes(fieldName)) continue; // already checked\n if (typeof value === \"string\" && value.length > 0 && value.length < 100) {\n return truncate(sanitizeDetail(value), MAX_DETAIL_LENGTH);\n }\n }\n\n return null;\n}\n\n/**\n * Extract useful detail from the tool title when rawInput is unavailable.\n * Titles often look like \"Read /path/to/file\" or \"Bash: ls -la\" —\n * we extract the part after the first space or colon.\n */\nfunction extractDetailFromTitle(title: string): string | null {\n if (!title) return null;\n\n // Pattern: \"ToolName /path/or/arg\" or \"ToolName: something\"\n const colonMatch = title.match(/^[^:]+:\\s*(.+)/);\n if (colonMatch) {\n return truncate(sanitizeDetail(colonMatch[1].trim()), MAX_DETAIL_LENGTH);\n }\n\n // Pattern: \"ToolName /some/path\" — extract if second part looks like a path or command\n const spaceIdx = title.indexOf(\" \");\n if (spaceIdx > 0 && spaceIdx < title.length - 1) {\n const rest = title.slice(spaceIdx + 1).trim();\n // Only extract if it looks like a path, URL, or meaningful argument\n if (rest.startsWith(\"/\") || rest.startsWith(\"./\") || rest.startsWith(\"http\") || rest.includes(\".\")) {\n return truncate(sanitizeDetail(rest), MAX_DETAIL_LENGTH);\n }\n }\n\n return null;\n}\n\nfunction sanitizeDetail(text: string): string {\n return text.replace(/`/g, \"'\");\n}\n\nfunction truncate(text: string, max: number): string {\n // Use first line only for multiline values\n const firstLine = text.split(\"\\n\")[0];\n if (firstLine.length <= max) return firstLine;\n return firstLine.slice(0, max - 1) + \"\\u2026\";\n}\n\nexport function formatDiff(diffs: DiffContent[], maxLines = MAX_DIFF_LINES): string[] {\n if (diffs.length === 0) return [];\n\n const parts: string[] = [];\n\n for (const d of diffs) {\n const fileName = d.path.split(\"/\").pop() ?? d.path;\n const oldText = d.oldText ?? \"\";\n const patch = createTwoFilesPatch(\n d.oldText == null ? \"/dev/null\" : d.path,\n d.path,\n oldText,\n d.newText,\n undefined,\n undefined,\n { context: 3 },\n );\n\n // Remove the first two header lines (Index: and ===) if present, keep ---/+++ and hunks\n const patchLines = patch.split(\"\\n\");\n // Find the first --- line to start from\n const startIdx = patchLines.findIndex((l) => l.startsWith(\"---\"));\n const diffLines = startIdx >= 0 ? patchLines.slice(startIdx) : patchLines;\n\n let truncated = false;\n let displayLines = diffLines;\n if (diffLines.length > maxLines) {\n displayLines = diffLines.slice(0, maxLines);\n truncated = true;\n }\n\n let block = `**${fileName}**\\n\\`\\`\\`diff\\n${displayLines.join(\"\\n\")}\\n\\`\\`\\``;\n if (truncated) {\n block += `\\n*... ${diffLines.length - maxLines} more lines*`;\n }\n\n parts.push(block);\n }\n\n // Join all diff blocks and split for Discord's message limit\n const fullMessage = parts.join(\"\\n\\n\");\n return splitMessage(fullMessage);\n}\n","import { createServer, type Server, type Socket } from \"node:net\";\nimport { existsSync, unlinkSync, mkdirSync, chmodSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport const DEFAULT_IPC_SOCKET_PATH = join(homedir(), \".acp-discord\", \"ipc.sock\");\n\nexport interface BindChannelOpts {\n cwd?: string;\n autoReply?: boolean;\n discordTools?: boolean;\n}\n\nexport interface BindingInfo {\n channelId: string;\n agent: string;\n cwd?: string;\n autoReply: boolean;\n discordTools: boolean;\n}\n\nexport interface TaskCrudResult {\n task?: unknown;\n tasks?: unknown[];\n logs?: unknown[];\n error?: string;\n deleted?: boolean;\n}\n\nexport interface IpcHandler {\n registerChannel(channelId: string, agentName: string, autoReply: boolean): void;\n unregisterChannel(channelId: string): void;\n confirmAction(sourceChannelId: string, description: string, details: string): Promise<boolean>;\n bindChannel(channelId: string, agentName: string, opts: BindChannelOpts, guildId?: string): Promise<{ success: boolean; error?: string }>;\n unbindChannel(channelId: string, guildId?: string): Promise<{ success: boolean; error?: string }>;\n listBindings(guildId?: string): Promise<{ success: boolean; bindings: BindingInfo[] }>;\n createTask?(params: Record<string, unknown>): TaskCrudResult;\n listTasks?(channelId?: string): TaskCrudResult;\n updateTask?(taskId: string, updates: Record<string, unknown>, channelId?: string): TaskCrudResult;\n deleteTask?(taskId: string, channelId?: string): TaskCrudResult;\n getTaskLogs?(taskId?: string, channelId?: string): TaskCrudResult;\n}\n\ninterface IpcMessage {\n action: string;\n requestId?: string;\n channelId?: string;\n agentName?: string;\n autoReply?: boolean;\n sourceChannelId?: string;\n description?: string;\n details?: string;\n cwd?: string;\n discordTools?: boolean;\n guildId?: string;\n // Task-related fields\n taskId?: string;\n prompt?: string;\n scheduleType?: string;\n scheduleValue?: string;\n notify?: string;\n updates?: Record<string, unknown>;\n}\n\nexport class IpcServer {\n private server: Server | null = null;\n private socketPath: string;\n private handler: IpcHandler;\n private connections = new Set<Socket>();\n\n constructor(handler: IpcHandler, socketPath = DEFAULT_IPC_SOCKET_PATH) {\n this.handler = handler;\n this.socketPath = socketPath;\n }\n\n async start(): Promise<void> {\n // Clean up stale socket\n if (existsSync(this.socketPath)) {\n unlinkSync(this.socketPath);\n }\n mkdirSync(dirname(this.socketPath), { recursive: true });\n\n return new Promise((resolve, reject) => {\n this.server = createServer((socket) => this.handleConnection(socket));\n\n this.server.on(\"error\", (err) => {\n console.error(\"IPC server error:\", err);\n reject(err);\n });\n\n this.server.listen(this.socketPath, () => {\n // Restrict socket to owner-only access\n chmodSync(this.socketPath, 0o600);\n console.log(`IPC server listening on ${this.socketPath}`);\n resolve();\n });\n });\n }\n\n private handleConnection(socket: Socket): void {\n this.connections.add(socket);\n let buffer = \"\";\n\n socket.on(\"data\", (data) => {\n buffer += data.toString();\n // Process newline-delimited JSON messages\n let newlineIdx: number;\n while ((newlineIdx = buffer.indexOf(\"\\n\")) !== -1) {\n const line = buffer.slice(0, newlineIdx).trim();\n buffer = buffer.slice(newlineIdx + 1);\n if (line) {\n this.processMessage(socket, line).catch((err) => {\n console.error(\"IPC message processing error:\", err);\n });\n }\n }\n });\n\n socket.on(\"close\", () => {\n this.connections.delete(socket);\n });\n\n socket.on(\"error\", (err) => {\n console.error(\"IPC connection error:\", err);\n this.connections.delete(socket);\n });\n }\n\n private async processMessage(socket: Socket, raw: string): Promise<void> {\n let msg: IpcMessage;\n try {\n msg = JSON.parse(raw);\n } catch {\n console.error(\"IPC: invalid JSON:\", raw);\n return;\n }\n\n switch (msg.action) {\n case \"register_channel\":\n if (msg.channelId && msg.agentName) {\n this.handler.registerChannel(msg.channelId, msg.agentName, msg.autoReply ?? true);\n }\n break;\n\n case \"unregister_channel\":\n if (msg.channelId) {\n this.handler.unregisterChannel(msg.channelId);\n }\n break;\n\n case \"confirm_action\":\n if (msg.requestId && msg.sourceChannelId && msg.description) {\n const approved = await this.handler.confirmAction(\n msg.sourceChannelId,\n msg.description,\n msg.details ?? \"\",\n );\n const response = JSON.stringify({ requestId: msg.requestId, approved }) + \"\\n\";\n socket.write(response);\n }\n break;\n\n case \"bind_channel\": {\n if (msg.requestId && msg.channelId && msg.agentName) {\n const result = await this.handler.bindChannel(msg.channelId, msg.agentName, {\n cwd: msg.cwd,\n autoReply: msg.autoReply,\n discordTools: msg.discordTools,\n }, msg.guildId);\n socket.write(JSON.stringify({ requestId: msg.requestId, ...result }) + \"\\n\");\n }\n break;\n }\n\n case \"unbind_channel\": {\n if (msg.requestId && msg.channelId) {\n const result = await this.handler.unbindChannel(msg.channelId, msg.guildId);\n socket.write(JSON.stringify({ requestId: msg.requestId, ...result }) + \"\\n\");\n }\n break;\n }\n\n case \"list_bindings\": {\n if (msg.requestId) {\n const result = await this.handler.listBindings(msg.guildId);\n socket.write(JSON.stringify({ requestId: msg.requestId, ...result }) + \"\\n\");\n }\n break;\n }\n\n case \"create_task\": {\n if (!msg.requestId || !this.handler.createTask) break;\n try {\n const result = this.handler.createTask({\n channel_id: msg.channelId ?? msg.sourceChannelId ?? \"\",\n agent_name: msg.agentName ?? \"unknown\",\n prompt: msg.prompt ?? \"\",\n schedule_type: msg.scheduleType ?? \"once\",\n schedule_value: msg.scheduleValue ?? \"\",\n description: msg.description,\n notify: msg.notify,\n created_by: \"agent\",\n });\n socket.write(JSON.stringify({ requestId: msg.requestId, ...result }) + \"\\n\");\n } catch (err) {\n const errMsg = err instanceof Error ? err.message : String(err);\n socket.write(JSON.stringify({ requestId: msg.requestId, error: errMsg }) + \"\\n\");\n }\n break;\n }\n\n case \"list_tasks\": {\n if (!msg.requestId || !this.handler.listTasks) break;\n const listResult = this.handler.listTasks(msg.channelId);\n socket.write(JSON.stringify({ requestId: msg.requestId, ...listResult }) + \"\\n\");\n break;\n }\n\n case \"update_task\": {\n if (!msg.requestId || !msg.taskId || !this.handler.updateTask) break;\n try {\n const updateResult = this.handler.updateTask(msg.taskId, msg.updates ?? {}, msg.channelId);\n socket.write(JSON.stringify({ requestId: msg.requestId, ...updateResult }) + \"\\n\");\n } catch (err) {\n const errMsg = err instanceof Error ? err.message : String(err);\n socket.write(JSON.stringify({ requestId: msg.requestId, error: errMsg }) + \"\\n\");\n }\n break;\n }\n\n case \"delete_task\": {\n if (!msg.requestId || !msg.taskId || !this.handler.deleteTask) break;\n const deleteResult = this.handler.deleteTask(msg.taskId, msg.channelId);\n socket.write(JSON.stringify({ requestId: msg.requestId, ...deleteResult }) + \"\\n\");\n break;\n }\n\n case \"get_task_logs\": {\n if (!msg.requestId || !this.handler.getTaskLogs) break;\n const logsResult = this.handler.getTaskLogs(msg.taskId, msg.channelId);\n socket.write(JSON.stringify({ requestId: msg.requestId, ...logsResult }) + \"\\n\");\n break;\n }\n\n default:\n console.error(\"IPC: unknown action:\", msg.action);\n }\n }\n\n stop(): void {\n for (const conn of this.connections) {\n conn.destroy();\n }\n this.connections.clear();\n if (this.server) {\n this.server.close();\n this.server = null;\n }\n // Clean up socket file\n if (existsSync(this.socketPath)) {\n try {\n unlinkSync(this.socketPath);\n } catch {\n // ignore\n }\n }\n }\n}\n","import { readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { randomUUID } from \"node:crypto\";\nimport { CronExpressionParser } from \"cron-parser\";\n\nconst DEFAULT_DATA_DIR = join(homedir(), \".acp-discord\");\nconst MAX_LOGS_PER_TASK = 50;\n\nexport interface ScheduledTask {\n id: string;\n channel_id: string;\n agent_name: string;\n prompt: string;\n description: string;\n schedule_type: \"once\" | \"cron\" | \"interval\";\n schedule_value: string;\n status: \"active\" | \"paused\" | \"completed\";\n notify: \"always\" | \"on_error\" | \"never\";\n next_run: string | null;\n last_run: string | null;\n created_by: string;\n created_at: string;\n}\n\nexport interface TaskRunLog {\n id: string;\n task_id: string;\n task_description: string;\n started_at: string;\n completed_at: string;\n duration_ms: number;\n status: \"success\" | \"error\";\n output: string;\n error: string | null;\n}\n\nexport interface TaskRunInput {\n startedAt: Date;\n completedAt: Date;\n durationMs: number;\n status: \"success\" | \"error\";\n output: string;\n error: string | null;\n}\n\nexport interface CreateTaskParams {\n channel_id: string;\n agent_name: string;\n prompt: string;\n description?: string;\n schedule_type: \"once\" | \"cron\" | \"interval\";\n schedule_value: string;\n notify?: \"always\" | \"on_error\" | \"never\";\n created_by: string;\n}\n\nexport type OnTaskFire = (task: ScheduledTask) => Promise<void>;\n\nexport class TaskScheduler {\n private tasks: ScheduledTask[] = [];\n private logs: TaskRunLog[] = [];\n private interval: NodeJS.Timeout | null = null;\n private onTaskFire: OnTaskFire;\n private tasksPath: string;\n private logsPath: string;\n private dataDir: string;\n private inFlight = new Set<string>(); // task IDs currently running\n\n constructor(onTaskFire: OnTaskFire, dataDir?: string) {\n this.onTaskFire = onTaskFire;\n this.dataDir = dataDir ?? DEFAULT_DATA_DIR;\n this.tasksPath = join(this.dataDir, \"scheduled-tasks.json\");\n this.logsPath = join(this.dataDir, \"task-run-logs.json\");\n }\n\n start(): void {\n this.load();\n this.interval = setInterval(() => {\n this.poll().catch((err) => {\n console.error(\"TaskScheduler poll error:\", err);\n });\n }, 15_000);\n console.log(`TaskScheduler started with ${this.tasks.length} task(s)`);\n }\n\n stop(): void {\n if (this.interval) {\n clearInterval(this.interval);\n this.interval = null;\n }\n this.save();\n }\n\n createTask(params: CreateTaskParams): ScheduledTask {\n const now = new Date().toISOString();\n const task: ScheduledTask = {\n id: randomUUID(),\n channel_id: params.channel_id,\n agent_name: params.agent_name,\n prompt: params.prompt,\n description: params.description ?? params.prompt.slice(0, 80),\n schedule_type: params.schedule_type,\n schedule_value: params.schedule_value,\n status: \"active\",\n notify: params.notify ?? \"on_error\",\n next_run: null,\n last_run: null,\n created_by: params.created_by,\n created_at: now,\n };\n\n task.next_run = this.computeNextRun(task);\n if (!task.next_run) {\n throw new Error(`Invalid schedule: cannot compute next run for ${params.schedule_type} \"${params.schedule_value}\"`);\n }\n\n this.tasks.push(task);\n this.save();\n return task;\n }\n\n listTasks(channelId?: string): ScheduledTask[] {\n if (channelId) {\n return this.tasks.filter((t) => t.channel_id === channelId);\n }\n return [...this.tasks];\n }\n\n updateTask(\n id: string,\n updates: Partial<Pick<ScheduledTask, \"status\" | \"prompt\" | \"schedule_type\" | \"schedule_value\" | \"notify\" | \"description\">>,\n channelId?: string,\n ): ScheduledTask | null {\n const task = this.tasks.find((t) => t.id === id);\n if (!task) return null;\n if (channelId && task.channel_id !== channelId) return null;\n\n if (updates.status !== undefined) task.status = updates.status;\n if (updates.prompt !== undefined) task.prompt = updates.prompt;\n if (updates.description !== undefined) task.description = updates.description;\n if (updates.notify !== undefined) task.notify = updates.notify;\n\n if (updates.schedule_type !== undefined || updates.schedule_value !== undefined) {\n // Save originals for rollback on validation failure\n const origType = task.schedule_type;\n const origValue = task.schedule_value;\n const origNextRun = task.next_run;\n\n if (updates.schedule_type !== undefined) task.schedule_type = updates.schedule_type;\n if (updates.schedule_value !== undefined) task.schedule_value = updates.schedule_value;\n const nextRun = this.computeNextRun(task);\n if (!nextRun) {\n // Rollback\n task.schedule_type = origType;\n task.schedule_value = origValue;\n task.next_run = origNextRun;\n throw new Error(`Invalid schedule: cannot compute next run for ${task.schedule_type} \"${task.schedule_value}\"`);\n }\n task.next_run = nextRun;\n }\n\n this.save();\n return task;\n }\n\n deleteTask(id: string, channelId?: string): boolean {\n const idx = this.tasks.findIndex((t) => t.id === id);\n if (idx === -1) return false;\n if (channelId && this.tasks[idx].channel_id !== channelId) return false;\n this.tasks.splice(idx, 1);\n this.save();\n return true;\n }\n\n getTaskLogs(taskId?: string, channelId?: string): TaskRunLog[] {\n let logs = this.logs;\n if (taskId) {\n // Verify the task belongs to the channel if channelId specified\n if (channelId) {\n const task = this.tasks.find((t) => t.id === taskId);\n if (task && task.channel_id !== channelId) return [];\n }\n logs = logs.filter((l) => l.task_id === taskId);\n } else if (channelId) {\n // Only return logs for tasks belonging to this channel\n const channelTaskIds = new Set(\n this.tasks.filter((t) => t.channel_id === channelId).map((t) => t.id),\n );\n logs = logs.filter((l) => channelTaskIds.has(l.task_id));\n }\n return [...logs];\n }\n\n logRun(taskId: string, result: TaskRunInput): void {\n const task = this.tasks.find((t) => t.id === taskId);\n const log: TaskRunLog = {\n id: randomUUID(),\n task_id: taskId,\n task_description: task?.description ?? \"unknown\",\n started_at: result.startedAt.toISOString(),\n completed_at: result.completedAt.toISOString(),\n duration_ms: result.durationMs,\n status: result.status,\n output: result.output.slice(0, 4000),\n error: result.error,\n };\n this.logs.push(log);\n\n // Trim logs per task\n const taskLogs = this.logs.filter((l) => l.task_id === taskId);\n if (taskLogs.length > MAX_LOGS_PER_TASK) {\n const idsToRemove = new Set(\n taskLogs\n .slice(0, taskLogs.length - MAX_LOGS_PER_TASK)\n .map((l) => l.id),\n );\n this.logs = this.logs.filter((l) => !idsToRemove.has(l.id));\n }\n\n this.saveLogs();\n }\n\n computeNextRun(task: ScheduledTask): string | null {\n const now = new Date();\n\n switch (task.schedule_type) {\n case \"once\": {\n const date = new Date(task.schedule_value);\n if (isNaN(date.getTime())) return null;\n return date > now ? date.toISOString() : date.toISOString();\n }\n\n case \"cron\": {\n try {\n const expr = CronExpressionParser.parse(task.schedule_value, { currentDate: now });\n const next = expr.next();\n return next.toDate().toISOString();\n } catch {\n return null;\n }\n }\n\n case \"interval\": {\n const seconds = parseInt(task.schedule_value, 10);\n if (isNaN(seconds) || seconds <= 0) return null;\n // Anchor to last_run to prevent drift\n const anchor = task.last_run ? new Date(task.last_run) : now;\n const next = new Date(anchor.getTime() + seconds * 1000);\n // If next is in the past, advance forward\n if (next <= now) {\n const elapsed = now.getTime() - anchor.getTime();\n const intervals = Math.ceil(elapsed / (seconds * 1000));\n return new Date(anchor.getTime() + intervals * seconds * 1000).toISOString();\n }\n return next.toISOString();\n }\n\n default:\n return null;\n }\n }\n\n private async poll(): Promise<void> {\n const now = new Date();\n\n for (const task of this.tasks) {\n if (task.status !== \"active\") continue;\n if (!task.next_run) continue;\n if (this.inFlight.has(task.id)) continue; // skip if already running\n\n const nextRun = new Date(task.next_run);\n if (nextRun > now) continue;\n\n // Mark as firing\n task.last_run = now.toISOString();\n\n if (task.schedule_type === \"once\") {\n task.status = \"completed\";\n } else {\n task.next_run = this.computeNextRun(task);\n }\n this.save();\n\n // Fire asynchronously with in-flight guard\n this.inFlight.add(task.id);\n this.onTaskFire(task)\n .catch((err) => {\n console.error(`Task fire error for ${task.id}:`, err);\n })\n .finally(() => {\n this.inFlight.delete(task.id);\n });\n }\n }\n\n private load(): void {\n mkdirSync(this.dataDir, { recursive: true });\n try {\n const data = readFileSync(this.tasksPath, \"utf-8\");\n this.tasks = JSON.parse(data);\n } catch {\n this.tasks = [];\n }\n try {\n const data = readFileSync(this.logsPath, \"utf-8\");\n this.logs = JSON.parse(data);\n } catch {\n this.logs = [];\n }\n }\n\n private save(): void {\n mkdirSync(this.dataDir, { recursive: true });\n writeFileSync(this.tasksPath, JSON.stringify(this.tasks, null, 2));\n }\n\n private saveLogs(): void {\n mkdirSync(this.dataDir, { recursive: true });\n writeFileSync(this.logsPath, JSON.stringify(this.logs, null, 2));\n }\n}\n","import { spawn, type ChildProcess } from \"node:child_process\";\nimport { Readable, Writable } from \"node:stream\";\nimport { ClientSideConnection, ndJsonStream, PROTOCOL_VERSION } from \"@agentclientprotocol/sdk\";\nimport type { Client, RequestPermissionResponse, SessionNotification } from \"@agentclientprotocol/sdk\";\nimport type { AgentConfig } from \"../shared/types.js\";\nimport type { McpServerConfig } from \"./session-manager.js\";\n\nexport interface TaskRunResult {\n output: string;\n stopReason: string;\n error: string | null;\n}\n\nfunction killProcessTree(proc: ChildProcess): void {\n if (!proc.pid) { proc.kill(); return; }\n try {\n process.kill(-proc.pid, \"SIGTERM\");\n } catch {\n try { proc.kill(\"SIGTERM\"); } catch { /* already dead */ }\n }\n setTimeout(() => {\n try { process.kill(-proc.pid!, \"SIGKILL\"); } catch { /* already dead */ }\n try { proc.kill(\"SIGKILL\"); } catch { /* already dead */ }\n }, 5000).unref();\n}\n\nexport async function runTask(\n agentConfig: AgentConfig,\n prompt: string,\n mcpServers: McpServerConfig[],\n): Promise<TaskRunResult> {\n const proc = spawn(agentConfig.command, agentConfig.args, {\n stdio: [\"pipe\", \"pipe\", \"inherit\"],\n cwd: agentConfig.cwd,\n detached: true,\n });\n\n let output = \"\";\n let error: string | null = null;\n\n try {\n const stream = ndJsonStream(\n Writable.toWeb(proc.stdin!) as WritableStream<Uint8Array>,\n Readable.toWeb(proc.stdout!) as ReadableStream<Uint8Array>,\n );\n\n const client: Client = {\n async requestPermission(): Promise<RequestPermissionResponse> {\n // Auto-cancel permission requests in scheduled tasks\n return { outcome: { outcome: \"cancelled\" } };\n },\n async sessionUpdate(params: SessionNotification): Promise<void> {\n const update = params.update;\n if (update.sessionUpdate === \"agent_message_chunk\") {\n if (update.content.type === \"text\") {\n output += update.content.text;\n }\n }\n },\n };\n\n const connection = new ClientSideConnection((_agent) => client, stream);\n\n await connection.initialize({\n protocolVersion: PROTOCOL_VERSION,\n clientCapabilities: {\n fs: { readTextFile: true, writeTextFile: true },\n terminal: true,\n },\n clientInfo: {\n name: \"acp-discord-task-runner\",\n title: \"ACP Discord Task Runner\",\n version: \"0.1.0\",\n },\n });\n\n const { sessionId } = await connection.newSession({\n cwd: agentConfig.cwd,\n mcpServers,\n });\n\n const result = await connection.prompt({\n sessionId,\n prompt: [{ type: \"text\", text: prompt }],\n });\n\n killProcessTree(proc);\n return { output, stopReason: result.stopReason, error: null };\n } catch (err) {\n killProcessTree(proc);\n error = err instanceof Error ? err.message : String(err);\n return { output, stopReason: \"error\", error };\n }\n}\n"],"mappings":"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","names":["join","homedir","ActionRowBuilder","ButtonBuilder","ButtonStyle","EmbedBuilder","dirname","existsSync","mkdirSync","dirname","readFileSync","writeFileSync","mkdirSync","join","homedir","spawn","Readable","Writable","ClientSideConnection","ndJsonStream","PROTOCOL_VERSION","EmbedBuilder","ActionRowBuilder","ButtonBuilder","ButtonStyle","dirname","existsSync","join","homedir"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "acp-discord",
3
- "version": "0.9.0-beta.2",
3
+ "version": "0.9.0",
4
4
  "description": "Discord bot that wraps ACP protocol for coding agents",
5
5
  "type": "module",
6
6
  "bin": {