@geminixiang/mama 0.2.0-beta.2 → 0.2.0-beta.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. package/README.md +156 -392
  2. package/dist/adapter.d.ts +31 -7
  3. package/dist/adapter.d.ts.map +1 -1
  4. package/dist/adapter.js.map +1 -1
  5. package/dist/adapters/discord/bot.d.ts +10 -5
  6. package/dist/adapters/discord/bot.d.ts.map +1 -1
  7. package/dist/adapters/discord/bot.js +347 -115
  8. package/dist/adapters/discord/bot.js.map +1 -1
  9. package/dist/adapters/discord/context.d.ts +1 -1
  10. package/dist/adapters/discord/context.d.ts.map +1 -1
  11. package/dist/adapters/discord/context.js +118 -25
  12. package/dist/adapters/discord/context.js.map +1 -1
  13. package/dist/adapters/shared.d.ts +91 -0
  14. package/dist/adapters/shared.d.ts.map +1 -0
  15. package/dist/adapters/shared.js +191 -0
  16. package/dist/adapters/shared.js.map +1 -0
  17. package/dist/adapters/slack/bot.d.ts +21 -22
  18. package/dist/adapters/slack/bot.d.ts.map +1 -1
  19. package/dist/adapters/slack/bot.js +530 -221
  20. package/dist/adapters/slack/bot.js.map +1 -1
  21. package/dist/adapters/slack/branch-manager.d.ts +28 -0
  22. package/dist/adapters/slack/branch-manager.d.ts.map +1 -0
  23. package/dist/adapters/slack/branch-manager.js +107 -0
  24. package/dist/adapters/slack/branch-manager.js.map +1 -0
  25. package/dist/adapters/slack/context.d.ts +4 -1
  26. package/dist/adapters/slack/context.d.ts.map +1 -1
  27. package/dist/adapters/slack/context.js +193 -75
  28. package/dist/adapters/slack/context.js.map +1 -1
  29. package/dist/adapters/slack/session.d.ts +38 -0
  30. package/dist/adapters/slack/session.d.ts.map +1 -0
  31. package/dist/adapters/slack/session.js +66 -0
  32. package/dist/adapters/slack/session.js.map +1 -0
  33. package/dist/adapters/slack/tools/attach.d.ts +1 -1
  34. package/dist/adapters/slack/tools/attach.d.ts.map +1 -1
  35. package/dist/adapters/slack/tools/attach.js.map +1 -1
  36. package/dist/adapters/telegram/bot.d.ts.map +1 -1
  37. package/dist/adapters/telegram/bot.js +140 -153
  38. package/dist/adapters/telegram/bot.js.map +1 -1
  39. package/dist/adapters/telegram/context.d.ts +1 -1
  40. package/dist/adapters/telegram/context.d.ts.map +1 -1
  41. package/dist/adapters/telegram/context.js +74 -20
  42. package/dist/adapters/telegram/context.js.map +1 -1
  43. package/dist/agent.d.ts +13 -3
  44. package/dist/agent.d.ts.map +1 -1
  45. package/dist/agent.js +677 -552
  46. package/dist/agent.js.map +1 -1
  47. package/dist/commands/auto-reply.d.ts +16 -0
  48. package/dist/commands/auto-reply.d.ts.map +1 -0
  49. package/dist/commands/auto-reply.js +72 -0
  50. package/dist/commands/auto-reply.js.map +1 -0
  51. package/dist/commands/index.d.ts +5 -0
  52. package/dist/commands/index.d.ts.map +1 -0
  53. package/dist/commands/index.js +18 -0
  54. package/dist/commands/index.js.map +1 -0
  55. package/dist/commands/login.d.ts +5 -0
  56. package/dist/commands/login.d.ts.map +1 -0
  57. package/dist/commands/login.js +91 -0
  58. package/dist/commands/login.js.map +1 -0
  59. package/dist/commands/model.d.ts +14 -0
  60. package/dist/commands/model.d.ts.map +1 -0
  61. package/dist/commands/model.js +112 -0
  62. package/dist/commands/model.js.map +1 -0
  63. package/dist/commands/new.d.ts +9 -0
  64. package/dist/commands/new.d.ts.map +1 -0
  65. package/dist/commands/new.js +28 -0
  66. package/dist/commands/new.js.map +1 -0
  67. package/dist/commands/registry.d.ts +4 -0
  68. package/dist/commands/registry.d.ts.map +1 -0
  69. package/dist/commands/registry.js +9 -0
  70. package/dist/commands/registry.js.map +1 -0
  71. package/dist/commands/sandbox.d.ts +10 -0
  72. package/dist/commands/sandbox.d.ts.map +1 -0
  73. package/dist/commands/sandbox.js +88 -0
  74. package/dist/commands/sandbox.js.map +1 -0
  75. package/dist/commands/session-view.d.ts +5 -0
  76. package/dist/commands/session-view.d.ts.map +1 -0
  77. package/dist/commands/session-view.js +62 -0
  78. package/dist/commands/session-view.js.map +1 -0
  79. package/dist/commands/types.d.ts +41 -0
  80. package/dist/commands/types.d.ts.map +1 -0
  81. package/dist/commands/types.js +2 -0
  82. package/dist/commands/types.js.map +1 -0
  83. package/dist/commands/utils.d.ts +8 -0
  84. package/dist/commands/utils.d.ts.map +1 -0
  85. package/dist/commands/utils.js +14 -0
  86. package/dist/commands/utils.js.map +1 -0
  87. package/dist/config.d.ts +45 -8
  88. package/dist/config.d.ts.map +1 -1
  89. package/dist/config.js +299 -67
  90. package/dist/config.js.map +1 -1
  91. package/dist/context.d.ts +10 -42
  92. package/dist/context.d.ts.map +1 -1
  93. package/dist/context.js +14 -127
  94. package/dist/context.js.map +1 -1
  95. package/dist/events.d.ts +2 -0
  96. package/dist/events.d.ts.map +1 -1
  97. package/dist/events.js +148 -67
  98. package/dist/events.js.map +1 -1
  99. package/dist/execution-resolver.d.ts +10 -6
  100. package/dist/execution-resolver.d.ts.map +1 -1
  101. package/dist/execution-resolver.js +121 -21
  102. package/dist/execution-resolver.js.map +1 -1
  103. package/dist/file-guards.d.ts +9 -0
  104. package/dist/file-guards.d.ts.map +1 -0
  105. package/dist/file-guards.js +56 -0
  106. package/dist/file-guards.js.map +1 -0
  107. package/dist/fs-atomic.d.ts +10 -0
  108. package/dist/fs-atomic.d.ts.map +1 -0
  109. package/dist/fs-atomic.js +45 -0
  110. package/dist/fs-atomic.js.map +1 -0
  111. package/dist/index.d.ts +7 -0
  112. package/dist/index.d.ts.map +1 -0
  113. package/dist/index.js +4 -0
  114. package/dist/index.js.map +1 -0
  115. package/dist/instrument.d.ts.map +1 -1
  116. package/dist/instrument.js +2 -3
  117. package/dist/instrument.js.map +1 -1
  118. package/dist/log.d.ts +1 -12
  119. package/dist/log.d.ts.map +1 -1
  120. package/dist/log.js +12 -143
  121. package/dist/log.js.map +1 -1
  122. package/dist/{login.d.ts → login/index.d.ts} +16 -3
  123. package/dist/login/index.d.ts.map +1 -0
  124. package/dist/{login.js → login/index.js} +94 -17
  125. package/dist/login/index.js.map +1 -0
  126. package/dist/{link-server.d.ts → login/portal.d.ts} +6 -4
  127. package/dist/login/portal.d.ts.map +1 -0
  128. package/dist/login/portal.js +1544 -0
  129. package/dist/login/portal.js.map +1 -0
  130. package/dist/login/session.d.ts +26 -0
  131. package/dist/login/session.d.ts.map +1 -0
  132. package/dist/{link-token.js → login/session.js} +10 -22
  133. package/dist/login/session.js.map +1 -0
  134. package/dist/main.d.ts.map +1 -1
  135. package/dist/main.js +138 -352
  136. package/dist/main.js.map +1 -1
  137. package/dist/provisioner.d.ts +42 -11
  138. package/dist/provisioner.d.ts.map +1 -1
  139. package/dist/provisioner.js +273 -64
  140. package/dist/provisioner.js.map +1 -1
  141. package/dist/runtime/conversation-orchestrator.d.ts +40 -0
  142. package/dist/runtime/conversation-orchestrator.d.ts.map +1 -0
  143. package/dist/runtime/conversation-orchestrator.js +183 -0
  144. package/dist/runtime/conversation-orchestrator.js.map +1 -0
  145. package/dist/runtime/index.d.ts +2 -0
  146. package/dist/runtime/index.d.ts.map +1 -0
  147. package/dist/runtime/index.js +2 -0
  148. package/dist/runtime/index.js.map +1 -0
  149. package/dist/runtime/session-runtime.d.ts +26 -0
  150. package/dist/runtime/session-runtime.d.ts.map +1 -0
  151. package/dist/runtime/session-runtime.js +221 -0
  152. package/dist/runtime/session-runtime.js.map +1 -0
  153. package/dist/sandbox/cloudflare.d.ts +15 -0
  154. package/dist/sandbox/cloudflare.d.ts.map +1 -0
  155. package/dist/sandbox/cloudflare.js +137 -0
  156. package/dist/sandbox/cloudflare.js.map +1 -0
  157. package/dist/sandbox/container.d.ts +2 -1
  158. package/dist/sandbox/container.d.ts.map +1 -1
  159. package/dist/sandbox/container.js +18 -2
  160. package/dist/sandbox/container.js.map +1 -1
  161. package/dist/sandbox/firecracker.d.ts +2 -1
  162. package/dist/sandbox/firecracker.d.ts.map +1 -1
  163. package/dist/sandbox/firecracker.js +6 -0
  164. package/dist/sandbox/firecracker.js.map +1 -1
  165. package/dist/sandbox/host.d.ts +2 -1
  166. package/dist/sandbox/host.d.ts.map +1 -1
  167. package/dist/sandbox/host.js +4 -0
  168. package/dist/sandbox/host.js.map +1 -1
  169. package/dist/sandbox/index.d.ts +6 -4
  170. package/dist/sandbox/index.d.ts.map +1 -1
  171. package/dist/sandbox/index.js +9 -6
  172. package/dist/sandbox/index.js.map +1 -1
  173. package/dist/sandbox/path-context.d.ts +4 -0
  174. package/dist/sandbox/path-context.d.ts.map +1 -0
  175. package/dist/sandbox/path-context.js +20 -0
  176. package/dist/sandbox/path-context.js.map +1 -0
  177. package/dist/sandbox/types.d.ts +17 -1
  178. package/dist/sandbox/types.d.ts.map +1 -1
  179. package/dist/sandbox/types.js.map +1 -1
  180. package/dist/sentry.d.ts +20 -1
  181. package/dist/sentry.d.ts.map +1 -1
  182. package/dist/sentry.js +58 -8
  183. package/dist/sentry.js.map +1 -1
  184. package/dist/session-policy.d.ts +13 -0
  185. package/dist/session-policy.d.ts.map +1 -0
  186. package/dist/session-policy.js +23 -0
  187. package/dist/session-policy.js.map +1 -0
  188. package/dist/session-store.d.ts +33 -2
  189. package/dist/session-store.d.ts.map +1 -1
  190. package/dist/session-store.js +179 -13
  191. package/dist/session-store.js.map +1 -1
  192. package/dist/session-view/command.d.ts +5 -0
  193. package/dist/session-view/command.d.ts.map +1 -0
  194. package/dist/session-view/command.js +11 -0
  195. package/dist/session-view/command.js.map +1 -0
  196. package/dist/session-view/portal.d.ts +16 -0
  197. package/dist/session-view/portal.d.ts.map +1 -0
  198. package/dist/session-view/portal.js +1822 -0
  199. package/dist/session-view/portal.js.map +1 -0
  200. package/dist/session-view/service.d.ts +34 -0
  201. package/dist/session-view/service.d.ts.map +1 -0
  202. package/dist/session-view/service.js +427 -0
  203. package/dist/session-view/service.js.map +1 -0
  204. package/dist/session-view/store.d.ts +18 -0
  205. package/dist/session-view/store.d.ts.map +1 -0
  206. package/dist/session-view/store.js +36 -0
  207. package/dist/session-view/store.js.map +1 -0
  208. package/dist/store.d.ts +3 -6
  209. package/dist/store.d.ts.map +1 -1
  210. package/dist/store.js +22 -48
  211. package/dist/store.js.map +1 -1
  212. package/dist/tool-diagnostics.d.ts +2 -0
  213. package/dist/tool-diagnostics.d.ts.map +1 -0
  214. package/dist/tool-diagnostics.js +7 -0
  215. package/dist/tool-diagnostics.js.map +1 -0
  216. package/dist/tools/bash.d.ts +2 -2
  217. package/dist/tools/bash.d.ts.map +1 -1
  218. package/dist/tools/bash.js.map +1 -1
  219. package/dist/tools/edit.d.ts +2 -2
  220. package/dist/tools/edit.d.ts.map +1 -1
  221. package/dist/tools/edit.js.map +1 -1
  222. package/dist/tools/event.d.ts +42 -2
  223. package/dist/tools/event.d.ts.map +1 -1
  224. package/dist/tools/event.js +43 -9
  225. package/dist/tools/event.js.map +1 -1
  226. package/dist/tools/index.d.ts +2 -2
  227. package/dist/tools/index.d.ts.map +1 -1
  228. package/dist/tools/index.js +2 -2
  229. package/dist/tools/index.js.map +1 -1
  230. package/dist/tools/read.d.ts +2 -2
  231. package/dist/tools/read.d.ts.map +1 -1
  232. package/dist/tools/read.js.map +1 -1
  233. package/dist/tools/write.d.ts +2 -2
  234. package/dist/tools/write.d.ts.map +1 -1
  235. package/dist/tools/write.js.map +1 -1
  236. package/dist/trigger.d.ts +31 -0
  237. package/dist/trigger.d.ts.map +1 -0
  238. package/dist/trigger.js +98 -0
  239. package/dist/trigger.js.map +1 -0
  240. package/dist/vault-routing.d.ts +2 -7
  241. package/dist/vault-routing.d.ts.map +1 -1
  242. package/dist/vault-routing.js +6 -42
  243. package/dist/vault-routing.js.map +1 -1
  244. package/dist/vault.d.ts +22 -56
  245. package/dist/vault.d.ts.map +1 -1
  246. package/dist/vault.js +155 -263
  247. package/dist/vault.js.map +1 -1
  248. package/package.json +11 -11
  249. package/dist/bindings.d.ts +0 -44
  250. package/dist/bindings.d.ts.map +0 -1
  251. package/dist/bindings.js +0 -74
  252. package/dist/bindings.js.map +0 -1
  253. package/dist/link-server.d.ts.map +0 -1
  254. package/dist/link-server.js +0 -899
  255. package/dist/link-server.js.map +0 -1
  256. package/dist/link-token.d.ts +0 -32
  257. package/dist/link-token.d.ts.map +0 -1
  258. package/dist/link-token.js.map +0 -1
  259. package/dist/login.d.ts.map +0 -1
  260. package/dist/login.js.map +0 -1
  261. package/dist/sandbox.d.ts +0 -2
  262. package/dist/sandbox.d.ts.map +0 -1
  263. package/dist/sandbox.js +0 -2
  264. package/dist/sandbox.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"bot.js","sourceRoot":"","sources":["../../../src/adapters/discord/bot.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Client,\n Events,\n GatewayIntentBits,\n Partials,\n type Collection,\n type Message,\n type Attachment,\n type TextChannel,\n type DMChannel,\n type NewsChannel,\n type ThreadChannel,\n} from \"discord.js\";\nimport { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { basename, join } from \"path\";\n\nimport type { Bot, BotEvent, BotHandler, PlatformInfo } from \"../../adapter.js\";\nimport * as log from \"../../log.js\";\nimport { formatAlreadyWorking, formatNothingRunning } from \"../../ui-copy.js\";\nimport { createDiscordAdapters } from \"./context.js\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface DiscordEvent extends BotEvent {\n type: \"mention\" | \"dm\";\n userName?: string;\n}\n\n// ============================================================================\n// Per-channel queue for sequential processing\n// ============================================================================\n\ntype QueuedWork = () => Promise<void>;\n\nclass ChannelQueue {\n private queue: QueuedWork[] = [];\n private processing = false;\n\n enqueue(work: QueuedWork): void {\n this.queue.push(work);\n this.processNext();\n }\n\n size(): number {\n return this.queue.length;\n }\n\n private async processNext(): Promise<void> {\n if (this.processing || this.queue.length === 0) return;\n this.processing = true;\n const work = this.queue.shift()!;\n try {\n await work();\n } catch (err) {\n log.logWarning(\"Discord queue error\", err instanceof Error ? err.message : String(err));\n }\n this.processing = false;\n this.processNext();\n }\n}\n\n// ============================================================================\n// DiscordBot\n// ============================================================================\n\nexport class DiscordBot implements Bot {\n private client: Client;\n private handler: BotHandler;\n private workingDir: string;\n private botUserId: string | null = null;\n private queues = new Map<string, ChannelQueue>();\n private startupTime: number = 0;\n private channels = new Map<string, { id: string; name: string }>();\n private users = new Map<string, { id: string; userName: string; displayName: string }>();\n\n constructor(handler: BotHandler, config: { token: string; workingDir: string }) {\n this.handler = handler;\n this.workingDir = config.workingDir;\n this.client = new Client({\n intents: [\n GatewayIntentBits.Guilds,\n GatewayIntentBits.GuildMessages,\n GatewayIntentBits.MessageContent,\n GatewayIntentBits.DirectMessages,\n ],\n partials: [Partials.Channel, Partials.Message],\n });\n }\n\n // ==========================================================================\n // Public API (implements Bot)\n // ==========================================================================\n\n async start(): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n this.client.once(Events.ClientReady, (readyClient) => {\n this.botUserId = readyClient.user.id;\n this.startupTime = Date.now();\n log.logConnected();\n log.logInfo(`Discord bot started as ${readyClient.user.tag}`);\n this.loadCachedGuildData();\n this.setupEventHandlers();\n resolve();\n });\n this.client.once(Events.Error, reject);\n this.client.login(process.env.MOM_DISCORD_BOT_TOKEN!).catch(reject);\n });\n }\n\n async postMessage(channel: string, text: string): Promise<string> {\n const ch = await this.fetchTextChannel(channel);\n const msg = await ch.send(text);\n return msg.id;\n }\n\n async updateMessage(channel: string, ts: string, text: string): Promise<void> {\n await this.updateMessageRaw(channel, ts, text);\n }\n\n enqueueEvent(event: BotEvent): boolean {\n const conversationId = event.conversationId;\n const queue = this.getQueue(conversationId);\n if (queue.size() >= 5) {\n log.logWarning(\n `Event queue full for ${conversationId}, discarding: ${event.text.substring(0, 50)}`,\n );\n return false;\n }\n log.logInfo(`Enqueueing event for ${conversationId}: ${event.text.substring(0, 50)}`);\n queue.enqueue(() => {\n const adapters = createDiscordAdapters(event as DiscordEvent, this, true);\n return this.handler.handleEvent(event, this, adapters, true);\n });\n return true;\n }\n\n getPlatformInfo(): PlatformInfo {\n return {\n name: \"discord\",\n formattingGuide:\n \"## Discord Formatting (Markdown)\\nBold: **text**, Italic: *text*, Code: `code`, Block: ```language\\ncode```\\nLinks: [text](url)\",\n channels: this.getAllChannels(),\n users: this.getAllUsers(),\n };\n }\n\n // ==========================================================================\n // Internal helpers (used by context.ts)\n // ==========================================================================\n\n async updateMessageRaw(channelId: string, messageId: string, text: string): Promise<void> {\n const ch = await this.fetchTextChannel(channelId);\n const msg = await ch.messages.fetch(messageId);\n await msg.edit(text);\n }\n\n async postReply(channelId: string, replyToId: string, text: string): Promise<string> {\n const ch = await this.fetchTextChannel(channelId);\n const replyTarget = await ch.messages.fetch(replyToId);\n const sent = await replyTarget.reply(text);\n return sent.id;\n }\n\n async postInThread(channelId: string, threadOrMessageId: string, text: string): Promise<string> {\n // Try as a thread channel first, then fall back to posting in the channel\n try {\n const thread = await this.client.channels.fetch(threadOrMessageId);\n if (thread && (thread.isThread() || thread.isTextBased())) {\n const msg = await (thread as ThreadChannel).send(text);\n return msg.id;\n }\n } catch {\n // Not a thread channel, treat as message ID for reply\n }\n return this.postReply(channelId, threadOrMessageId, text);\n }\n\n async deleteMessageRaw(channelId: string, messageId: string): Promise<void> {\n try {\n const ch = await this.fetchTextChannel(channelId);\n const msg = await ch.messages.fetch(messageId);\n await msg.delete();\n } catch {\n // Ignore if already deleted\n }\n }\n\n async sendTyping(channelId: string): Promise<void> {\n try {\n const ch = await this.fetchTextChannel(channelId);\n await ch.sendTyping();\n } catch {\n // Non-fatal\n }\n }\n\n async uploadFile(channelId: string, filePath: string, title?: string): Promise<void> {\n const ch = await this.fetchTextChannel(channelId);\n const fileName = title ?? basename(filePath);\n const fileContent = readFileSync(filePath);\n await ch.send({ files: [{ attachment: fileContent, name: fileName }] });\n }\n\n getAllChannels(): { id: string; name: string }[] {\n return Array.from(this.channels.values());\n }\n\n getAllUsers(): { id: string; userName: string; displayName: string }[] {\n return Array.from(this.users.values());\n }\n\n logToFile(channelId: string, entry: object): void {\n const dir = join(this.workingDir, channelId);\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n appendFileSync(join(dir, \"log.jsonl\"), `${JSON.stringify(entry)}\\n`);\n }\n\n logBotResponse(channelId: string, text: string, ts: string): void {\n this.logToFile(channelId, {\n date: new Date().toISOString(),\n ts,\n user: \"bot\",\n text,\n attachments: [],\n isBot: true,\n });\n }\n\n /**\n * Process attachments from a Discord message\n * Downloads files in background and returns metadata\n * Returns format compatible with ChatMessage: { name: string, localPath: string }[]\n */\n processAttachments(\n channelId: string,\n attachments: Collection<string, Attachment>,\n _messageId: string,\n ): { name: string; localPath: string }[] {\n const result: { name: string; localPath: string }[] = [];\n\n // Discord attachments Collection - iterate over values\n for (const attachment of attachments.values()) {\n if (!attachment.name) {\n log.logWarning(\"Discord attachment missing name, skipping\", attachment.url);\n continue;\n }\n\n // Generate local filename\n const ts = Date.now();\n const sanitizedName = attachment.name.replace(/[^a-zA-Z0-9._-]/g, \"_\");\n const filename = `${ts}_${sanitizedName}`;\n const localPath = `${channelId}/attachments/${filename}`;\n const fullDir = join(this.workingDir, channelId, \"attachments\");\n\n result.push({\n name: attachment.name,\n localPath: localPath,\n });\n\n // Download in background (fire and forget)\n this.downloadAttachment(fullDir, filename, attachment.url).catch((err) => {\n log.logWarning(`Failed to download Discord attachment`, `${filename}: ${err}`);\n });\n }\n\n return result;\n }\n\n /**\n * Download an attachment from URL to local file\n */\n private async downloadAttachment(dir: string, filename: string, url: string): Promise<void> {\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n\n try {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n const buffer = await response.arrayBuffer();\n writeFileSync(join(dir, filename), Buffer.from(buffer));\n } catch (err) {\n throw new Error(`Download failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n // ==========================================================================\n // Private - Event Handlers\n // ==========================================================================\n\n private getQueue(channelId: string): ChannelQueue {\n let queue = this.queues.get(channelId);\n if (!queue) {\n queue = new ChannelQueue();\n this.queues.set(channelId, queue);\n }\n return queue;\n }\n\n private loadCachedGuildData(): void {\n for (const guild of this.client.guilds.cache.values()) {\n for (const channel of guild.channels.cache.values()) {\n if (channel.isTextBased() && \"name\" in channel) {\n this.channels.set(channel.id, { id: channel.id, name: channel.name ?? channel.id });\n }\n }\n for (const member of guild.members.cache.values()) {\n this.users.set(member.id, {\n id: member.id,\n userName: member.user.username,\n displayName: member.displayName,\n });\n }\n }\n }\n\n private stripBotMention(text: string): string {\n if (!this.botUserId) return text;\n return text.replace(new RegExp(`<@!?${this.botUserId}>`, \"g\"), \"\").trim();\n }\n\n private setupEventHandlers(): void {\n this.client.on(Events.MessageCreate, async (msg: Message) => {\n // Skip messages from before startup\n if (msg.createdTimestamp < this.startupTime) return;\n // Skip bot messages\n if (msg.author.bot) return;\n // Skip if bot isn't mentioned and it's not a DM\n const isDM = msg.channel.type === 1; // ChannelType.DM = 1\n const isMentioned = msg.mentions.users.has(this.botUserId ?? \"\");\n if (!isDM && !isMentioned) return;\n\n const channelId = msg.channelId;\n const userId = msg.author.id;\n const userName = msg.author.username;\n const msgId = msg.id;\n\n // Track user\n this.users.set(userId, {\n id: userId,\n userName,\n displayName: msg.member?.displayName ?? userName,\n });\n\n // Track channel\n if (!this.channels.has(channelId) && \"name\" in msg.channel) {\n const ch = msg.channel as TextChannel | NewsChannel;\n this.channels.set(channelId, { id: channelId, name: ch.name });\n }\n\n // Thread: if this message is in a thread (has parentId) or is a reply\n const isInThread = msg.channel.isThread();\n const referencedMsgId = msg.reference?.messageId;\n const threadTs = isInThread ? msg.channelId : referencedMsgId;\n const sessionKey = `${channelId}:${threadTs ?? msgId}`;\n\n const cleanedText = this.stripBotMention(msg.content);\n\n // Process attachments (download in background)\n const processedAttachments = this.processAttachments(channelId, msg.attachments, msgId);\n\n const event: DiscordEvent = {\n type: isDM ? \"dm\" : \"mention\",\n conversationId: channelId,\n conversationKind: isDM ? \"direct\" : \"shared\",\n ts: msgId,\n thread_ts: threadTs,\n user: userId,\n userName,\n text: cleanedText,\n attachments: processedAttachments,\n };\n\n // Log message\n this.logToFile(channelId, {\n date: msg.createdAt.toISOString(),\n ts: msgId,\n user: userId,\n userName,\n text: cleanedText,\n attachments: processedAttachments,\n isBot: false,\n });\n\n // Handle stop command\n if (cleanedText.toLowerCase() === \"stop\" || cleanedText.toLowerCase() === \"/stop\") {\n if (this.handler.isRunning(sessionKey)) {\n this.handler.handleStop(sessionKey, channelId, this);\n } else {\n await this.postMessage(channelId, formatNothingRunning(\"discord\"));\n }\n return;\n }\n\n if (this.handler.isRunning(sessionKey)) {\n await this.postMessage(channelId, formatAlreadyWorking(\"discord\", \"stop\"));\n } else {\n this.getQueue(sessionKey).enqueue(() => {\n const adapters = createDiscordAdapters(event, this, false);\n return this.handler.handleEvent(event, this, adapters, false);\n });\n }\n });\n }\n\n private async fetchTextChannel(\n channelId: string,\n ): Promise<TextChannel | DMChannel | NewsChannel | ThreadChannel> {\n const ch = await this.client.channels.fetch(channelId);\n if (!ch || !ch.isTextBased()) {\n throw new Error(`Channel ${channelId} is not a text channel`);\n }\n return ch as TextChannel | DMChannel | NewsChannel | ThreadChannel;\n }\n}\n"]}
1
+ {"version":3,"file":"bot.js","sourceRoot":"","sources":["../../../src/adapters/discord/bot.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n ApplicationCommandOptionType,\n Client,\n Events,\n GatewayIntentBits,\n Partials,\n type ChatInputCommandInteraction,\n type Collection,\n type Message,\n type Attachment,\n type TextChannel,\n type DMChannel,\n type NewsChannel,\n type ThreadChannel,\n} from \"discord.js\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { basename, join } from \"path\";\n\nimport type {\n Bot,\n BotAdapters,\n BotEvent,\n BotHandler,\n ChatMessage,\n ChatResponseContext,\n ChatToolResult,\n PlatformInfo,\n} from \"../../adapter.js\";\nimport * as log from \"../../log.js\";\nimport { resolveChatSessionKey } from \"../../session-policy.js\";\nimport { evaluateAutoReplyPolicy } from \"../../trigger.js\";\nimport { formatNothingRunning } from \"../../ui-copy.js\";\nimport {\n appendBotResponseLog,\n appendChannelLog,\n ChannelQueue,\n resolveOnlyScopedStopTarget,\n resolveStopTarget,\n withRetry,\n} from \"../shared.js\";\nimport { createDiscordAdapters } from \"./context.js\";\n\n// discord.js: DiscordAPIError exposes `.status` (HTTP status) and a `.code`.\n// RateLimitError fires when the internal queue gives up. Both should retry.\nfunction discordIsRateLimited(err: Error): boolean {\n if ((err as { status?: number }).status === 429) return true;\n if ((err as { httpStatus?: number }).httpStatus === 429) return true;\n if (err.name === \"RateLimitError\") return true;\n return false;\n}\n\nconst discordRetry = <T>(fn: () => Promise<T>): Promise<T> =>\n withRetry(fn, { isRateLimited: discordIsRateLimited });\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface DiscordEvent extends BotEvent {\n type: \"mention\" | \"dm\";\n userName?: string;\n}\n\n// ============================================================================\n// DiscordBot\n// ============================================================================\n\nexport class DiscordBot implements Bot {\n private client: Client;\n private handler: BotHandler;\n private token: string;\n private workingDir: string;\n private botUserId: string | null = null;\n private queues = new Map<string, ChannelQueue>();\n private startupTime: number = 0;\n private channels = new Map<string, { id: string; name: string }>();\n private users = new Map<string, { id: string; userName: string; displayName: string }>();\n\n constructor(handler: BotHandler, config: { token: string; workingDir: string }) {\n this.handler = handler;\n this.token = config.token;\n this.workingDir = config.workingDir;\n this.client = new Client({\n intents: [\n GatewayIntentBits.Guilds,\n GatewayIntentBits.GuildMessages,\n GatewayIntentBits.MessageContent,\n GatewayIntentBits.DirectMessages,\n ],\n partials: [Partials.Channel, Partials.Message],\n });\n }\n\n // ==========================================================================\n // Public API (implements Bot)\n // ==========================================================================\n\n async start(): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n this.client.once(Events.ClientReady, async (readyClient) => {\n this.botUserId = readyClient.user.id;\n this.startupTime = Date.now();\n log.logConnected(\"Discord\");\n log.logInfo(`Discord bot started as ${readyClient.user.tag}`);\n this.loadCachedGuildData();\n this.setupEventHandlers();\n try {\n await readyClient.application.commands.set([\n {\n name: \"login\",\n description: \"Store credentials in your private vault\",\n },\n {\n name: \"session\",\n description: \"Open the current session in the web viewer\",\n },\n {\n name: \"new\",\n description: \"Reset conversation history and start fresh\",\n },\n {\n name: \"stop\",\n description: \"Stop the current conversation\",\n },\n {\n name: \"model\",\n description: \"Switch this conversation's LLM model\",\n options: [\n {\n name: \"model\",\n description: \"provider/model[:thinking], e.g. anthropic/claude-sonnet-4-6:off\",\n type: ApplicationCommandOptionType.String,\n required: false,\n },\n ],\n },\n {\n name: \"sandbox\",\n description: \"Show or temporarily boost this conversation's sandbox limits\",\n options: [\n {\n name: \"action\",\n description: \"Use 'boost' to temporarily apply the configured boost limits\",\n type: ApplicationCommandOptionType.String,\n required: false,\n },\n ],\n },\n ]);\n } catch (err) {\n log.logWarning(\n \"Failed to register Discord slash commands\",\n err instanceof Error ? err.message : String(err),\n );\n }\n resolve();\n });\n this.client.once(Events.Error, reject);\n this.client.login(this.token).catch(reject);\n });\n }\n\n async postMessage(channel: string, text: string): Promise<string> {\n return discordRetry(async () => {\n const ch = await this.fetchTextChannel(channel);\n const msg = await ch.send(text);\n return msg.id;\n });\n }\n\n async updateMessage(channel: string, ts: string, text: string): Promise<void> {\n await this.updateMessageRaw(channel, ts, text);\n }\n\n enqueueEvent(event: BotEvent): boolean {\n const conversationId = event.conversationId;\n const queue = this.getQueue(conversationId);\n if (queue.size() >= 5) {\n log.logWarning(\n `Event queue full for ${conversationId}, discarding: ${event.text.substring(0, 50)}`,\n );\n return false;\n }\n log.logInfo(`Enqueueing event for ${conversationId}: ${event.text.substring(0, 50)}`);\n queue.enqueue(() => {\n const adapters = createDiscordAdapters(event as DiscordEvent, this);\n return this.handler.handleEvent(event, this, adapters);\n });\n return true;\n }\n\n getPlatformInfo(): PlatformInfo {\n return {\n name: \"discord\",\n formattingGuide:\n \"## Discord Formatting (Markdown)\\nBold: **text**, Italic: *text*, Code: `code`, Block: ```language\\ncode```\\nLinks: [text](url)\",\n channels: this.getAllChannels(),\n users: this.getAllUsers(),\n diagnostics: {\n showUsageSummary: false,\n },\n };\n }\n\n // ==========================================================================\n // Internal helpers (used by context.ts)\n // ==========================================================================\n\n async updateMessageRaw(channelId: string, messageId: string, text: string): Promise<void> {\n return discordRetry(async () => {\n const ch = await this.fetchTextChannel(channelId);\n const msg = await ch.messages.fetch(messageId);\n await msg.edit(text);\n });\n }\n\n async postReply(channelId: string, replyToId: string, text: string): Promise<string> {\n return discordRetry(async () => {\n const ch = await this.fetchTextChannel(channelId);\n const replyTarget = await ch.messages.fetch(replyToId);\n const sent = await replyTarget.reply(text);\n return sent.id;\n });\n }\n\n async postInThread(channelId: string, threadOrMessageId: string, text: string): Promise<string> {\n // Try as a thread channel first, then fall back to posting in the channel\n try {\n const thread = await this.client.channels.fetch(threadOrMessageId);\n if (thread && (thread.isThread() || thread.isTextBased())) {\n return discordRetry(async () => {\n const msg = await (thread as ThreadChannel).send(text);\n return msg.id;\n });\n }\n } catch {\n // Not a thread channel, treat as message ID for reply\n }\n return this.postReply(channelId, threadOrMessageId, text);\n }\n\n async deleteMessageRaw(channelId: string, messageId: string): Promise<void> {\n try {\n const ch = await this.fetchTextChannel(channelId);\n const msg = await ch.messages.fetch(messageId);\n await msg.delete();\n } catch {\n // Ignore if already deleted\n }\n }\n\n async sendTyping(channelId: string): Promise<void> {\n try {\n const ch = await this.fetchTextChannel(channelId);\n await ch.sendTyping();\n } catch {\n // Non-fatal\n }\n }\n\n async uploadFile(channelId: string, filePath: string, title?: string): Promise<void> {\n return discordRetry(async () => {\n const ch = await this.fetchTextChannel(channelId);\n const fileName = title ?? basename(filePath);\n const fileContent = readFileSync(filePath);\n await ch.send({ files: [{ attachment: fileContent, name: fileName }] });\n });\n }\n\n async sendDirectMessage(userId: string, text: string): Promise<string> {\n return discordRetry(async () => {\n const user = await this.client.users.fetch(userId);\n const msg = await user.send(text);\n return msg.id;\n });\n }\n\n async postPrivate(_conversationId: string, userId: string, text: string): Promise<void> {\n await this.sendDirectMessage(userId, text);\n }\n\n getAllChannels(): { id: string; name: string }[] {\n return Array.from(this.channels.values());\n }\n\n getAllUsers(): { id: string; userName: string; displayName: string }[] {\n return Array.from(this.users.values());\n }\n\n logToFile(channelId: string, entry: object): void {\n appendChannelLog(this.workingDir, channelId, entry);\n }\n\n logBotResponse(channelId: string, text: string, ts: string): void {\n appendBotResponseLog(this.workingDir, channelId, text, ts);\n }\n\n /**\n * Process attachments from a Discord message.\n * Downloads files before returning so the agent can read them immediately.\n */\n async processAttachments(\n channelId: string,\n attachments: Collection<string, Attachment>,\n _messageId: string,\n ): Promise<{ name: string; localPath: string }[]> {\n const downloads: Array<Promise<{ name: string; localPath: string } | null>> = [];\n\n // Discord attachments Collection - iterate over values\n for (const attachment of attachments.values()) {\n if (!attachment.name) {\n log.logWarning(\"Discord attachment missing name, skipping\", attachment.url);\n continue;\n }\n\n const ts = Date.now();\n const sanitizedName = attachment.name.replace(/[^a-zA-Z0-9._-]/g, \"_\");\n const filename = `${ts}_${sanitizedName}`;\n const localPath = `${channelId}/attachments/${filename}`;\n const fullDir = join(this.workingDir, channelId, \"attachments\");\n const result = {\n name: attachment.name,\n localPath,\n };\n\n downloads.push(\n this.downloadAttachment(fullDir, filename, attachment.url)\n .then(() => result)\n .catch((err) => {\n log.logWarning(`Failed to download Discord attachment`, `${filename}: ${err}`);\n return null;\n }),\n );\n }\n\n const results = await Promise.all(downloads);\n return results.filter(\n (attachment): attachment is { name: string; localPath: string } => attachment !== null,\n );\n }\n\n /**\n * Download an attachment from URL to local file\n */\n private async downloadAttachment(dir: string, filename: string, url: string): Promise<void> {\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n\n try {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n const buffer = await response.arrayBuffer();\n writeFileSync(join(dir, filename), Buffer.from(buffer));\n } catch (err) {\n throw new Error(`Download failed: ${err instanceof Error ? err.message : String(err)}`, {\n cause: err,\n });\n }\n }\n\n // ==========================================================================\n // Private - Event Handlers\n // ==========================================================================\n\n private getQueue(channelId: string): ChannelQueue {\n let queue = this.queues.get(channelId);\n if (!queue) {\n queue = new ChannelQueue(\"Discord\");\n this.queues.set(channelId, queue);\n }\n return queue;\n }\n\n private resolveStopTarget(channelId: string, sessionKey: string): string | null {\n const directTarget = resolveStopTarget({\n handler: this.handler,\n conversationId: channelId,\n sessionKey,\n });\n if (directTarget) return directTarget;\n if (sessionKey !== channelId) return null;\n return resolveOnlyScopedStopTarget(this.handler, channelId);\n }\n\n private loadCachedGuildData(): void {\n for (const guild of this.client.guilds.cache.values()) {\n for (const channel of guild.channels.cache.values()) {\n if (channel.isTextBased() && \"name\" in channel) {\n this.channels.set(channel.id, { id: channel.id, name: channel.name ?? channel.id });\n }\n }\n for (const member of guild.members.cache.values()) {\n this.users.set(member.id, {\n id: member.id,\n userName: member.user.username,\n displayName: member.displayName,\n });\n }\n }\n }\n\n private stripBotMention(text: string): string {\n if (!this.botUserId) return text;\n return text.replace(new RegExp(`<@!?${this.botUserId}>`, \"g\"), \"\").trim();\n }\n\n private resolveConversationContext(input: {\n channelId: string;\n inGuild: boolean;\n isThread: boolean;\n parentChannelId?: string | null;\n referencedMsgId?: string;\n }): { conversationId: string; threadTs?: string } {\n if (!input.inGuild) {\n return {\n conversationId: input.channelId,\n threadTs: input.referencedMsgId,\n };\n }\n\n if (input.isThread) {\n return {\n conversationId: input.parentChannelId ?? input.channelId,\n threadTs: input.channelId,\n };\n }\n\n return {\n conversationId: input.channelId,\n threadTs: input.referencedMsgId,\n };\n }\n\n private createSlashCommandAdapters(\n interaction: ChatInputCommandInteraction,\n commandText: string,\n sessionKey: string,\n conversationId: string,\n ): BotAdapters {\n const isDM = !interaction.inGuild();\n const userId = interaction.user.id;\n const userName = interaction.user.username;\n const platform = this.getPlatformInfo();\n const shouldUseEphemeral = !isDM;\n\n const message: ChatMessage = {\n id: interaction.id,\n sessionKey,\n conversationKind: isDM ? \"direct\" : \"shared\",\n userId,\n userName,\n text: commandText,\n attachments: [],\n };\n\n const respondPrivately = async (text: string, replace = false): Promise<void> => {\n if (interaction.replied || interaction.deferred) {\n if (replace) {\n await interaction.editReply({ content: text });\n } else {\n await interaction.followUp({ content: text, ephemeral: shouldUseEphemeral });\n }\n return;\n }\n\n await interaction.reply({ content: text, ephemeral: shouldUseEphemeral });\n };\n\n const responseCtx: ChatResponseContext = {\n respond: async (text: string) => {\n await respondPrivately(text);\n },\n replaceResponse: async (text: string) => {\n await respondPrivately(text, true);\n },\n respondDiagnostic: async (text: string) => {\n await respondPrivately(text);\n },\n respondToolResult: async (result: ChatToolResult) => {\n const duration = (result.durationMs / 1000).toFixed(1);\n const formatted = `${result.isError ? \"Error\" : \"Done\"} ${result.toolName} (${duration}s)\\n${result.result}`;\n await respondPrivately(formatted);\n },\n setTyping: async () => {},\n setWorking: async () => {},\n uploadFile: async (filePath: string, title?: string) => {\n await this.uploadFile(conversationId, filePath, title);\n },\n deleteResponse: async () => {},\n };\n\n return { message, responseCtx, platform };\n }\n\n private setupEventHandlers(): void {\n this.client.on(Events.InteractionCreate, async (interaction) => {\n if (!interaction.isChatInputCommand()) return;\n if (\n interaction.commandName !== \"login\" &&\n interaction.commandName !== \"session\" &&\n interaction.commandName !== \"new\" &&\n interaction.commandName !== \"stop\" &&\n interaction.commandName !== \"model\" &&\n interaction.commandName !== \"sandbox\"\n ) {\n return;\n }\n\n const isDM = !interaction.inGuild();\n const { conversationId, threadTs } = this.resolveConversationContext({\n channelId: interaction.channelId,\n inGuild: interaction.inGuild(),\n isThread: interaction.channel?.isThread() ?? false,\n parentChannelId:\n interaction.channel && \"parentId\" in interaction.channel\n ? interaction.channel.parentId\n : null,\n });\n const sessionKey = resolveChatSessionKey({\n conversationId,\n conversationKind: isDM ? \"direct\" : \"shared\",\n messageId: interaction.id,\n persistentTopLevel: true,\n threadTs,\n });\n const modelOption =\n interaction.commandName === \"model\"\n ? interaction.options.getString(\"model\")?.trim()\n : undefined;\n const sandboxAction =\n interaction.commandName === \"sandbox\"\n ? interaction.options.getString(\"action\")?.trim()\n : undefined;\n const commandArg = modelOption ?? sandboxAction;\n const commandText = commandArg\n ? `/${interaction.commandName} ${commandArg}`\n : `/${interaction.commandName}`;\n\n this.logToFile(conversationId, {\n date: new Date(interaction.createdTimestamp).toISOString(),\n ts: interaction.id,\n ...(threadTs ? { threadTs } : {}),\n user: interaction.user.id,\n userName: interaction.user.username,\n text: commandText,\n attachments: [],\n isBot: false,\n });\n\n const adapters = this.createSlashCommandAdapters(\n interaction,\n commandText,\n sessionKey,\n conversationId,\n );\n try {\n if (interaction.commandName === \"new\") {\n await this.handler.handleNewCommand(sessionKey, conversationId, this);\n return;\n }\n\n if (interaction.commandName === \"stop\") {\n const stopTarget = this.resolveStopTarget(conversationId, sessionKey);\n if (stopTarget) {\n await this.handler.handleStop(stopTarget, conversationId, this);\n } else {\n await adapters.responseCtx.respond(formatNothingRunning(\"discord\"));\n }\n return;\n }\n\n const event: BotEvent = {\n type: \"dm\",\n conversationId,\n conversationKind: isDM ? \"direct\" : \"shared\",\n ts: interaction.id,\n thread_ts: threadTs,\n sessionKey,\n user: interaction.user.id,\n text: commandText,\n attachments: [],\n };\n\n await this.handler.handleEvent(event, this, adapters);\n } catch (err) {\n log.logWarning(\n \"Discord slash command error\",\n err instanceof Error ? err.message : String(err),\n );\n if (!interaction.replied && !interaction.deferred) {\n await interaction.reply({\n content: `${interaction.commandName} command failed. Please try again later.`,\n ephemeral: !isDM,\n });\n }\n }\n });\n\n this.client.on(Events.MessageCreate, async (msg: Message) => {\n // Skip messages from before startup\n if (msg.createdTimestamp < this.startupTime) return;\n // Skip bot messages\n if (msg.author.bot) return;\n const isDM = msg.channel.type === 1; // ChannelType.DM = 1\n const isInThread = msg.channel.isThread();\n const referencedMsgId = msg.reference?.messageId;\n const isThreadReply = isInThread || !!referencedMsgId;\n const isMentioned = msg.mentions.users.has(this.botUserId ?? \"\");\n const isAutoReplyCandidate = !isDM && !isMentioned && !isThreadReply;\n\n const { conversationId, threadTs } = this.resolveConversationContext({\n channelId: msg.channelId,\n inGuild: !isDM,\n isThread: isInThread,\n parentChannelId: \"parentId\" in msg.channel ? msg.channel.parentId : null,\n referencedMsgId,\n });\n const userId = msg.author.id;\n const userName = msg.author.username;\n const msgId = msg.id;\n\n // Track user\n this.users.set(userId, {\n id: userId,\n userName,\n displayName: msg.member?.displayName ?? userName,\n });\n\n // Track channel\n if (!this.channels.has(conversationId) && \"name\" in msg.channel) {\n const ch = msg.channel as TextChannel | NewsChannel;\n this.channels.set(conversationId, { id: conversationId, name: ch.name });\n }\n\n const conversationKind = isDM ? \"direct\" : \"shared\";\n const sessionKey = resolveChatSessionKey({\n conversationId,\n conversationKind,\n messageId: msgId,\n persistentTopLevel: true,\n threadTs,\n });\n\n const cleanedText = this.stripBotMention(msg.content);\n\n const eventBase: DiscordEvent = {\n type: isDM ? \"dm\" : \"mention\",\n conversationId,\n conversationKind,\n ts: msgId,\n thread_ts: threadTs,\n sessionKey,\n user: userId,\n userName,\n text: cleanedText,\n };\n\n // Handle stop before trigger gate — \"stop\" should never be auto-reply judged.\n if (cleanedText.toLowerCase() === \"stop\" || cleanedText.toLowerCase() === \"/stop\") {\n const stopTarget = this.resolveStopTarget(conversationId, sessionKey);\n if (stopTarget) {\n this.handler.handleStop(stopTarget, conversationId, this);\n } else if (!isAutoReplyCandidate) {\n await this.postMessage(conversationId, formatNothingRunning(\"discord\"));\n }\n return;\n }\n\n const triggerResult = isAutoReplyCandidate\n ? await evaluateAutoReplyPolicy({ event: eventBase, workingDir: this.workingDir })\n : ({ trigger: true, reason: \"addressed\" } as const);\n\n const logEntryBase = {\n date: msg.createdAt.toISOString(),\n ts: msgId,\n ...(!isDM && threadTs ? { threadTs } : {}),\n user: userId,\n userName,\n text: cleanedText,\n isBot: false,\n };\n\n if (!triggerResult.trigger) {\n this.logToFile(conversationId, { ...logEntryBase, attachments: [] });\n return;\n }\n\n const processedAttachments = await this.processAttachments(\n conversationId,\n msg.attachments,\n msgId,\n );\n const event: DiscordEvent = { ...eventBase, attachments: processedAttachments };\n\n this.logToFile(conversationId, { ...logEntryBase, attachments: processedAttachments });\n\n this.getQueue(sessionKey).enqueue(() => {\n const adapters = createDiscordAdapters(event, this);\n return this.handler.handleEvent(event, this, adapters);\n });\n });\n }\n\n private async fetchTextChannel(\n channelId: string,\n ): Promise<TextChannel | DMChannel | NewsChannel | ThreadChannel> {\n const ch = await this.client.channels.fetch(channelId);\n if (!ch || !ch.isTextBased()) {\n throw new Error(`Channel ${channelId} is not a text channel`);\n }\n return ch as TextChannel | DMChannel | NewsChannel | ThreadChannel;\n }\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  import type { ChatMessage, ChatResponseContext, PlatformInfo } from "../../adapter.js";
2
2
  import type { DiscordBot, DiscordEvent } from "./bot.js";
3
3
  export declare const DISCORD_FORMATTING_GUIDE = "## Discord Formatting (Markdown)\nBold: **text**, Italic: *text*, Code: `code`, Block: ```language\ncode```\nLinks: [text](url), Spoiler: ||text||\nKeep messages under 2000 characters. Use code blocks for code.";
4
- export declare function createDiscordAdapters(event: DiscordEvent, bot: DiscordBot, isEvent?: boolean): {
4
+ export declare function createDiscordAdapters(event: DiscordEvent, bot: DiscordBot): {
5
5
  message: ChatMessage;
6
6
  responseCtx: ChatResponseContext;
7
7
  platform: PlatformInfo;
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/adapters/discord/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEvF,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAEzD,eAAO,MAAM,wBAAwB,uNAG0B,CAAC;AAEhE,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,YAAY,EACnB,GAAG,EAAE,UAAU,EACf,OAAO,CAAC,EAAE,OAAO,GAChB;IACD,OAAO,EAAE,WAAW,CAAC;IACrB,WAAW,EAAE,mBAAmB,CAAC;IACjC,QAAQ,EAAE,YAAY,CAAC;CACxB,CAkKA","sourcesContent":["import type { ChatMessage, ChatResponseContext, PlatformInfo } from \"../../adapter.js\";\nimport * as log from \"../../log.js\";\nimport type { DiscordBot, DiscordEvent } from \"./bot.js\";\n\nexport const DISCORD_FORMATTING_GUIDE = `## Discord Formatting (Markdown)\nBold: **text**, Italic: *text*, Code: \\`code\\`, Block: \\`\\`\\`language\\ncode\\`\\`\\`\nLinks: [text](url), Spoiler: ||text||\nKeep messages under 2000 characters. Use code blocks for code.`;\n\nexport function createDiscordAdapters(\n event: DiscordEvent,\n bot: DiscordBot,\n isEvent?: boolean,\n): {\n message: ChatMessage;\n responseCtx: ChatResponseContext;\n platform: PlatformInfo;\n} {\n let messageId: string | null = null;\n let accumulatedText = \"\";\n let isWorking = true;\n const workingIndicator = \" ...\";\n let updatePromise = Promise.resolve();\n let typingInterval: ReturnType<typeof setInterval> | null = null;\n\n function stopTyping(): void {\n if (typingInterval !== null) {\n clearInterval(typingInterval);\n typingInterval = null;\n }\n }\n\n const conversationId = event.conversationId;\n const channelId = conversationId;\n const _eventFilename = isEvent ? event.text.match(/^\\[EVENT:([^:]+):/)?.[1] : undefined;\n const isThreaded = !!event.thread_ts;\n\n const message: ChatMessage = {\n id: event.ts,\n sessionKey: event.sessionKey ?? `${conversationId}:${event.thread_ts ?? event.ts}`,\n conversationKind: event.conversationKind,\n userId: event.user,\n userName: event.userName,\n text: event.text,\n attachments: event.attachments,\n threadTs: event.thread_ts,\n };\n\n const platform: PlatformInfo = {\n name: \"discord\",\n formattingGuide: DISCORD_FORMATTING_GUIDE,\n channels: bot.getAllChannels(),\n users: bot.getAllUsers(),\n };\n\n // Discord message limit is 2000 chars; use 1900 for safety\n const MAX_LENGTH = 1900;\n const truncationNote = \"\\n\\n*(message truncated, ask me to elaborate on specific parts)*\";\n\n function truncate(text: string, limit: number, note: string): string {\n if (text.length > limit) {\n return text.substring(0, limit - note.length) + note;\n }\n return text;\n }\n\n const responseCtx: ChatResponseContext = {\n respond: async (text: string) => {\n updatePromise = updatePromise.then(async () => {\n try {\n accumulatedText = accumulatedText ? `${accumulatedText}\\n${text}` : text;\n const displayText = truncate(\n isWorking ? accumulatedText + workingIndicator : accumulatedText,\n MAX_LENGTH,\n truncationNote,\n );\n\n if (messageId !== null) {\n await bot.updateMessageRaw(channelId, messageId, displayText);\n } else {\n stopTyping();\n if (isThreaded && event.thread_ts) {\n messageId = await bot.postInThread(channelId, event.thread_ts, displayText);\n } else {\n messageId = await bot.postReply(channelId, event.ts, displayText);\n }\n }\n\n if (messageId !== null) {\n bot.logBotResponse(channelId, text, messageId);\n }\n } catch (err) {\n log.logWarning(\"Discord respond error\", err instanceof Error ? err.message : String(err));\n }\n });\n await updatePromise;\n },\n\n replaceResponse: async (text: string) => {\n updatePromise = updatePromise.then(async () => {\n try {\n accumulatedText = truncate(text, MAX_LENGTH, truncationNote);\n const displayText = isWorking ? accumulatedText + workingIndicator : accumulatedText;\n\n if (messageId !== null) {\n await bot.updateMessageRaw(channelId, messageId, displayText);\n } else {\n stopTyping();\n if (isThreaded && event.thread_ts) {\n messageId = await bot.postInThread(channelId, event.thread_ts, displayText);\n } else {\n messageId = await bot.postReply(channelId, event.ts, displayText);\n }\n }\n } catch (err) {\n log.logWarning(\n \"Discord replaceResponse error\",\n err instanceof Error ? err.message : String(err),\n );\n }\n });\n await updatePromise;\n },\n\n // Discord threads not used here — discard thread-only messages (e.g. usage summary)\n respondInThread: async (_text: string) => {},\n\n setTyping: async (isTyping: boolean) => {\n if (isTyping && typingInterval === null) {\n // Send immediately and repeat every 8s (Discord clears indicator after ~10s)\n bot.sendTyping(channelId).catch(() => {});\n typingInterval = setInterval(() => {\n bot.sendTyping(channelId).catch(() => {});\n }, 8000);\n } else if (!isTyping) {\n stopTyping();\n }\n },\n\n setWorking: async (working: boolean) => {\n updatePromise = updatePromise.then(async () => {\n try {\n isWorking = working;\n if (!working) stopTyping();\n if (messageId !== null) {\n const displayText = isWorking ? accumulatedText + workingIndicator : accumulatedText;\n await bot.updateMessageRaw(channelId, messageId, displayText);\n }\n } catch (err) {\n log.logWarning(\n \"Discord setWorking error\",\n err instanceof Error ? err.message : String(err),\n );\n }\n });\n await updatePromise;\n },\n\n uploadFile: async (filePath: string, title?: string) => {\n await bot.uploadFile(channelId, filePath, title);\n },\n\n deleteResponse: async () => {\n updatePromise = updatePromise.then(async () => {\n stopTyping();\n if (messageId !== null) {\n try {\n await bot.deleteMessageRaw(channelId, messageId);\n } catch {\n // Ignore errors\n }\n messageId = null;\n }\n });\n await updatePromise;\n },\n };\n\n return { message, responseCtx, platform };\n}\n"]}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/adapters/discord/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,mBAAmB,EAEnB,YAAY,EACb,MAAM,kBAAkB,CAAC;AAI1B,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAEzD,eAAO,MAAM,wBAAwB,uNAG0B,CAAC;AAsBhE,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,YAAY,EACnB,GAAG,EAAE,UAAU,GACd;IACD,OAAO,EAAE,WAAW,CAAC;IACrB,WAAW,EAAE,mBAAmB,CAAC;IACjC,QAAQ,EAAE,YAAY,CAAC;CACxB,CAuPA","sourcesContent":["import type {\n ChatMessage,\n ChatResponseContext,\n ChatToolResult,\n PlatformInfo,\n} from \"../../adapter.js\";\nimport * as log from \"../../log.js\";\nimport { resolveChatSessionKey } from \"../../session-policy.js\";\nimport { createChatResponseErrorReporter, formatToolArgs, splitText } from \"../shared.js\";\nimport type { DiscordBot, DiscordEvent } from \"./bot.js\";\n\nexport const DISCORD_FORMATTING_GUIDE = `## Discord Formatting (Markdown)\nBold: **text**, Italic: *text*, Code: \\`code\\`, Block: \\`\\`\\`language\\ncode\\`\\`\\`\nLinks: [text](url), Spoiler: ||text||\nKeep messages under 2000 characters. Use code blocks for code.`;\n\n// Discord hard limit is 2000 chars; 1900 leaves headroom for working indicator.\nconst MAX_LENGTH = 1900;\n\nconst formatDiscordContinuation = (partNum: number): string => `*(continued ${partNum})*`;\n\nfunction isDiscordMessageReference(id: string | undefined): id is string {\n return typeof id === \"string\" && id !== \"\" && !id.startsWith(\"event:\");\n}\n\nfunction formatToolResult(result: ChatToolResult): string {\n const argsFormatted = formatToolArgs(result.args);\n const duration = (result.durationMs / 1000).toFixed(1);\n let text = `**${result.isError ? \"Error\" : \"Done\"} ${result.toolName}**`;\n if (result.label) text += `: ${result.label}`;\n text += ` (${duration}s)\\n`;\n if (argsFormatted) text += `\\`\\`\\`\\n${argsFormatted}\\n\\`\\`\\`\\n`;\n text += `**Result:**\\n\\`\\`\\`\\n${result.result}\\n\\`\\`\\``;\n return text;\n}\n\nexport function createDiscordAdapters(\n event: DiscordEvent,\n bot: DiscordBot,\n): {\n message: ChatMessage;\n responseCtx: ChatResponseContext;\n platform: PlatformInfo;\n} {\n let messageId: string | null = null;\n let accumulatedText = \"\";\n let isWorking = true;\n const workingIndicator = \" ...\";\n let updatePromise = Promise.resolve();\n let typingInterval: ReturnType<typeof setInterval> | null = null;\n let typingFailureWarned = false;\n\n function stopTyping(): void {\n if (typingInterval !== null) {\n clearInterval(typingInterval);\n typingInterval = null;\n }\n }\n\n const conversationId = event.conversationId;\n const channelId = conversationId;\n const threadTargetId = isDiscordMessageReference(event.thread_ts) ? event.thread_ts : undefined;\n const replyTargetId = isDiscordMessageReference(event.ts) ? event.ts : undefined;\n\n const message: ChatMessage = {\n id: event.ts,\n sessionKey:\n event.sessionKey ??\n resolveChatSessionKey({\n conversationId,\n conversationKind: event.conversationKind,\n messageId: event.ts,\n persistentTopLevel: true,\n threadTs: event.thread_ts,\n }),\n conversationKind: event.conversationKind,\n userId: event.user,\n userName: event.userName,\n text: event.text,\n attachments: event.attachments,\n threadTs: event.thread_ts,\n };\n\n const platform: PlatformInfo = {\n name: \"discord\",\n formattingGuide: DISCORD_FORMATTING_GUIDE,\n channels: bot.getAllChannels(),\n users: bot.getAllUsers(),\n diagnostics: {\n showUsageSummary: false,\n },\n };\n\n async function postDiagnosticMessage(text: string): Promise<string> {\n stopTyping();\n if (threadTargetId) {\n return bot.postInThread(channelId, threadTargetId, text);\n }\n if (replyTargetId) {\n return bot.postReply(channelId, replyTargetId, text);\n }\n if (messageId !== null) {\n return bot.postReply(channelId, messageId, text);\n }\n return bot.postMessage(channelId, text);\n }\n\n const reportResponseError = createChatResponseErrorReporter(() => ({\n platform: \"discord\",\n conversationId,\n channelId,\n messageId: message.id,\n sessionKey: message.sessionKey,\n responseMessageId: messageId,\n threadTs: threadTargetId,\n replyTargetId,\n conversationKind: message.conversationKind,\n }));\n\n const responseCtx: ChatResponseContext = {\n respond: async (text: string) => {\n updatePromise = updatePromise.then(async () => {\n try {\n accumulatedText = accumulatedText ? `${accumulatedText}\\n${text}` : text;\n const [displayText, ...extraParts] = splitText(\n isWorking ? accumulatedText + workingIndicator : accumulatedText,\n MAX_LENGTH,\n formatDiscordContinuation,\n );\n\n if (messageId !== null) {\n await bot.updateMessageRaw(channelId, messageId, displayText);\n } else {\n stopTyping();\n if (threadTargetId) {\n messageId = await bot.postInThread(channelId, threadTargetId, displayText);\n } else if (replyTargetId) {\n messageId = await bot.postReply(channelId, replyTargetId, displayText);\n } else {\n messageId = await bot.postMessage(channelId, displayText);\n }\n }\n for (const part of extraParts) {\n await postDiagnosticMessage(part);\n }\n\n if (messageId !== null) {\n bot.logBotResponse(channelId, text, messageId);\n }\n } catch (err) {\n log.logWarning(\"Discord respond error\", err instanceof Error ? err.message : String(err));\n reportResponseError(err, \"respond\", {\n phase: messageId ? \"update\" : \"initial_post\",\n textLength: text.length,\n accumulatedLength: accumulatedText.length,\n });\n }\n });\n await updatePromise;\n },\n\n replaceResponse: async (text: string) => {\n updatePromise = updatePromise.then(async () => {\n try {\n accumulatedText = text;\n const [displayText, ...extraParts] = splitText(\n accumulatedText,\n MAX_LENGTH,\n formatDiscordContinuation,\n );\n\n if (messageId !== null) {\n await bot.updateMessageRaw(channelId, messageId, displayText);\n } else {\n stopTyping();\n if (threadTargetId) {\n messageId = await bot.postInThread(channelId, threadTargetId, displayText);\n } else if (replyTargetId) {\n messageId = await bot.postReply(channelId, replyTargetId, displayText);\n } else {\n messageId = await bot.postMessage(channelId, displayText);\n }\n }\n for (const part of extraParts) {\n await postDiagnosticMessage(part);\n }\n } catch (err) {\n log.logWarning(\n \"Discord replaceResponse error\",\n err instanceof Error ? err.message : String(err),\n );\n reportResponseError(err, \"replace_response\", {\n textLength: text.length,\n hadExistingResponse: Boolean(messageId),\n });\n }\n });\n await updatePromise;\n },\n\n respondDiagnostic: async (text: string, options?: { style?: \"muted\" | \"error\" }) => {\n updatePromise = updatePromise.then(async () => {\n try {\n const prefix = options?.style === \"error\" ? \"*Error:* \" : \"\";\n for (const part of splitText(`${prefix}${text}`, MAX_LENGTH, formatDiscordContinuation)) {\n await postDiagnosticMessage(part);\n }\n } catch (err) {\n log.logWarning(\n \"Discord respondDiagnostic error\",\n err instanceof Error ? err.message : String(err),\n );\n reportResponseError(err, \"respond_diagnostic\", {\n textLength: text.length,\n style: options?.style,\n });\n }\n });\n await updatePromise;\n },\n\n respondToolResult: async (result: ChatToolResult) => {\n await responseCtx.respondDiagnostic(formatToolResult(result));\n },\n\n setTyping: async (isTyping: boolean) => {\n const onTypingError = (err: unknown): void => {\n if (typingFailureWarned) return;\n typingFailureWarned = true;\n log.logWarning(\n \"Discord sendTyping failed (further occurrences suppressed for this session)\",\n err instanceof Error ? err.message : String(err),\n );\n };\n if (isTyping && typingInterval === null) {\n // Send immediately and repeat every 8s (Discord clears indicator after ~10s)\n bot.sendTyping(channelId).catch(onTypingError);\n typingInterval = setInterval(() => {\n bot.sendTyping(channelId).catch(onTypingError);\n }, 8000);\n } else if (!isTyping) {\n stopTyping();\n }\n },\n\n setWorking: async (working: boolean) => {\n updatePromise = updatePromise.then(async () => {\n try {\n isWorking = working;\n if (!working) stopTyping();\n if (messageId !== null) {\n const [displayText] = splitText(\n isWorking ? accumulatedText + workingIndicator : accumulatedText,\n MAX_LENGTH,\n formatDiscordContinuation,\n );\n await bot.updateMessageRaw(channelId, messageId, displayText);\n }\n } catch (err) {\n log.logWarning(\n \"Discord setWorking error\",\n err instanceof Error ? err.message : String(err),\n );\n reportResponseError(err, \"set_working\", { working });\n }\n });\n await updatePromise;\n },\n\n uploadFile: async (filePath: string, title?: string) => {\n await bot.uploadFile(channelId, filePath, title);\n },\n\n deleteResponse: async () => {\n updatePromise = updatePromise.then(async () => {\n stopTyping();\n if (messageId !== null) {\n try {\n await bot.deleteMessageRaw(channelId, messageId);\n } catch {\n // Ignore errors\n }\n messageId = null;\n }\n });\n await updatePromise;\n },\n };\n\n return { message, responseCtx, platform };\n}\n"]}
@@ -1,15 +1,36 @@
1
1
  import * as log from "../../log.js";
2
+ import { resolveChatSessionKey } from "../../session-policy.js";
3
+ import { createChatResponseErrorReporter, formatToolArgs, splitText } from "../shared.js";
2
4
  export const DISCORD_FORMATTING_GUIDE = `## Discord Formatting (Markdown)
3
5
  Bold: **text**, Italic: *text*, Code: \`code\`, Block: \`\`\`language\ncode\`\`\`
4
6
  Links: [text](url), Spoiler: ||text||
5
7
  Keep messages under 2000 characters. Use code blocks for code.`;
6
- export function createDiscordAdapters(event, bot, isEvent) {
8
+ // Discord hard limit is 2000 chars; 1900 leaves headroom for working indicator.
9
+ const MAX_LENGTH = 1900;
10
+ const formatDiscordContinuation = (partNum) => `*(continued ${partNum})*`;
11
+ function isDiscordMessageReference(id) {
12
+ return typeof id === "string" && id !== "" && !id.startsWith("event:");
13
+ }
14
+ function formatToolResult(result) {
15
+ const argsFormatted = formatToolArgs(result.args);
16
+ const duration = (result.durationMs / 1000).toFixed(1);
17
+ let text = `**${result.isError ? "Error" : "Done"} ${result.toolName}**`;
18
+ if (result.label)
19
+ text += `: ${result.label}`;
20
+ text += ` (${duration}s)\n`;
21
+ if (argsFormatted)
22
+ text += `\`\`\`\n${argsFormatted}\n\`\`\`\n`;
23
+ text += `**Result:**\n\`\`\`\n${result.result}\n\`\`\``;
24
+ return text;
25
+ }
26
+ export function createDiscordAdapters(event, bot) {
7
27
  let messageId = null;
8
28
  let accumulatedText = "";
9
29
  let isWorking = true;
10
30
  const workingIndicator = " ...";
11
31
  let updatePromise = Promise.resolve();
12
32
  let typingInterval = null;
33
+ let typingFailureWarned = false;
13
34
  function stopTyping() {
14
35
  if (typingInterval !== null) {
15
36
  clearInterval(typingInterval);
@@ -18,11 +39,18 @@ export function createDiscordAdapters(event, bot, isEvent) {
18
39
  }
19
40
  const conversationId = event.conversationId;
20
41
  const channelId = conversationId;
21
- const _eventFilename = isEvent ? event.text.match(/^\[EVENT:([^:]+):/)?.[1] : undefined;
22
- const isThreaded = !!event.thread_ts;
42
+ const threadTargetId = isDiscordMessageReference(event.thread_ts) ? event.thread_ts : undefined;
43
+ const replyTargetId = isDiscordMessageReference(event.ts) ? event.ts : undefined;
23
44
  const message = {
24
45
  id: event.ts,
25
- sessionKey: event.sessionKey ?? `${conversationId}:${event.thread_ts ?? event.ts}`,
46
+ sessionKey: event.sessionKey ??
47
+ resolveChatSessionKey({
48
+ conversationId,
49
+ conversationKind: event.conversationKind,
50
+ messageId: event.ts,
51
+ persistentTopLevel: true,
52
+ threadTs: event.thread_ts,
53
+ }),
26
54
  conversationKind: event.conversationKind,
27
55
  userId: event.user,
28
56
  userName: event.userName,
@@ -35,40 +63,69 @@ export function createDiscordAdapters(event, bot, isEvent) {
35
63
  formattingGuide: DISCORD_FORMATTING_GUIDE,
36
64
  channels: bot.getAllChannels(),
37
65
  users: bot.getAllUsers(),
66
+ diagnostics: {
67
+ showUsageSummary: false,
68
+ },
38
69
  };
39
- // Discord message limit is 2000 chars; use 1900 for safety
40
- const MAX_LENGTH = 1900;
41
- const truncationNote = "\n\n*(message truncated, ask me to elaborate on specific parts)*";
42
- function truncate(text, limit, note) {
43
- if (text.length > limit) {
44
- return text.substring(0, limit - note.length) + note;
70
+ async function postDiagnosticMessage(text) {
71
+ stopTyping();
72
+ if (threadTargetId) {
73
+ return bot.postInThread(channelId, threadTargetId, text);
74
+ }
75
+ if (replyTargetId) {
76
+ return bot.postReply(channelId, replyTargetId, text);
77
+ }
78
+ if (messageId !== null) {
79
+ return bot.postReply(channelId, messageId, text);
45
80
  }
46
- return text;
81
+ return bot.postMessage(channelId, text);
47
82
  }
83
+ const reportResponseError = createChatResponseErrorReporter(() => ({
84
+ platform: "discord",
85
+ conversationId,
86
+ channelId,
87
+ messageId: message.id,
88
+ sessionKey: message.sessionKey,
89
+ responseMessageId: messageId,
90
+ threadTs: threadTargetId,
91
+ replyTargetId,
92
+ conversationKind: message.conversationKind,
93
+ }));
48
94
  const responseCtx = {
49
95
  respond: async (text) => {
50
96
  updatePromise = updatePromise.then(async () => {
51
97
  try {
52
98
  accumulatedText = accumulatedText ? `${accumulatedText}\n${text}` : text;
53
- const displayText = truncate(isWorking ? accumulatedText + workingIndicator : accumulatedText, MAX_LENGTH, truncationNote);
99
+ const [displayText, ...extraParts] = splitText(isWorking ? accumulatedText + workingIndicator : accumulatedText, MAX_LENGTH, formatDiscordContinuation);
54
100
  if (messageId !== null) {
55
101
  await bot.updateMessageRaw(channelId, messageId, displayText);
56
102
  }
57
103
  else {
58
104
  stopTyping();
59
- if (isThreaded && event.thread_ts) {
60
- messageId = await bot.postInThread(channelId, event.thread_ts, displayText);
105
+ if (threadTargetId) {
106
+ messageId = await bot.postInThread(channelId, threadTargetId, displayText);
107
+ }
108
+ else if (replyTargetId) {
109
+ messageId = await bot.postReply(channelId, replyTargetId, displayText);
61
110
  }
62
111
  else {
63
- messageId = await bot.postReply(channelId, event.ts, displayText);
112
+ messageId = await bot.postMessage(channelId, displayText);
64
113
  }
65
114
  }
115
+ for (const part of extraParts) {
116
+ await postDiagnosticMessage(part);
117
+ }
66
118
  if (messageId !== null) {
67
119
  bot.logBotResponse(channelId, text, messageId);
68
120
  }
69
121
  }
70
122
  catch (err) {
71
123
  log.logWarning("Discord respond error", err instanceof Error ? err.message : String(err));
124
+ reportResponseError(err, "respond", {
125
+ phase: messageId ? "update" : "initial_post",
126
+ textLength: text.length,
127
+ accumulatedLength: accumulatedText.length,
128
+ });
72
129
  }
73
130
  });
74
131
  await updatePromise;
@@ -76,35 +133,70 @@ export function createDiscordAdapters(event, bot, isEvent) {
76
133
  replaceResponse: async (text) => {
77
134
  updatePromise = updatePromise.then(async () => {
78
135
  try {
79
- accumulatedText = truncate(text, MAX_LENGTH, truncationNote);
80
- const displayText = isWorking ? accumulatedText + workingIndicator : accumulatedText;
136
+ accumulatedText = text;
137
+ const [displayText, ...extraParts] = splitText(accumulatedText, MAX_LENGTH, formatDiscordContinuation);
81
138
  if (messageId !== null) {
82
139
  await bot.updateMessageRaw(channelId, messageId, displayText);
83
140
  }
84
141
  else {
85
142
  stopTyping();
86
- if (isThreaded && event.thread_ts) {
87
- messageId = await bot.postInThread(channelId, event.thread_ts, displayText);
143
+ if (threadTargetId) {
144
+ messageId = await bot.postInThread(channelId, threadTargetId, displayText);
145
+ }
146
+ else if (replyTargetId) {
147
+ messageId = await bot.postReply(channelId, replyTargetId, displayText);
88
148
  }
89
149
  else {
90
- messageId = await bot.postReply(channelId, event.ts, displayText);
150
+ messageId = await bot.postMessage(channelId, displayText);
91
151
  }
92
152
  }
153
+ for (const part of extraParts) {
154
+ await postDiagnosticMessage(part);
155
+ }
93
156
  }
94
157
  catch (err) {
95
158
  log.logWarning("Discord replaceResponse error", err instanceof Error ? err.message : String(err));
159
+ reportResponseError(err, "replace_response", {
160
+ textLength: text.length,
161
+ hadExistingResponse: Boolean(messageId),
162
+ });
96
163
  }
97
164
  });
98
165
  await updatePromise;
99
166
  },
100
- // Discord threads not used here — discard thread-only messages (e.g. usage summary)
101
- respondInThread: async (_text) => { },
167
+ respondDiagnostic: async (text, options) => {
168
+ updatePromise = updatePromise.then(async () => {
169
+ try {
170
+ const prefix = options?.style === "error" ? "*Error:* " : "";
171
+ for (const part of splitText(`${prefix}${text}`, MAX_LENGTH, formatDiscordContinuation)) {
172
+ await postDiagnosticMessage(part);
173
+ }
174
+ }
175
+ catch (err) {
176
+ log.logWarning("Discord respondDiagnostic error", err instanceof Error ? err.message : String(err));
177
+ reportResponseError(err, "respond_diagnostic", {
178
+ textLength: text.length,
179
+ style: options?.style,
180
+ });
181
+ }
182
+ });
183
+ await updatePromise;
184
+ },
185
+ respondToolResult: async (result) => {
186
+ await responseCtx.respondDiagnostic(formatToolResult(result));
187
+ },
102
188
  setTyping: async (isTyping) => {
189
+ const onTypingError = (err) => {
190
+ if (typingFailureWarned)
191
+ return;
192
+ typingFailureWarned = true;
193
+ log.logWarning("Discord sendTyping failed (further occurrences suppressed for this session)", err instanceof Error ? err.message : String(err));
194
+ };
103
195
  if (isTyping && typingInterval === null) {
104
196
  // Send immediately and repeat every 8s (Discord clears indicator after ~10s)
105
- bot.sendTyping(channelId).catch(() => { });
197
+ bot.sendTyping(channelId).catch(onTypingError);
106
198
  typingInterval = setInterval(() => {
107
- bot.sendTyping(channelId).catch(() => { });
199
+ bot.sendTyping(channelId).catch(onTypingError);
108
200
  }, 8000);
109
201
  }
110
202
  else if (!isTyping) {
@@ -118,12 +210,13 @@ export function createDiscordAdapters(event, bot, isEvent) {
118
210
  if (!working)
119
211
  stopTyping();
120
212
  if (messageId !== null) {
121
- const displayText = isWorking ? accumulatedText + workingIndicator : accumulatedText;
213
+ const [displayText] = splitText(isWorking ? accumulatedText + workingIndicator : accumulatedText, MAX_LENGTH, formatDiscordContinuation);
122
214
  await bot.updateMessageRaw(channelId, messageId, displayText);
123
215
  }
124
216
  }
125
217
  catch (err) {
126
218
  log.logWarning("Discord setWorking error", err instanceof Error ? err.message : String(err));
219
+ reportResponseError(err, "set_working", { working });
127
220
  }
128
221
  });
129
222
  await updatePromise;
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/adapters/discord/context.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ChatMessage, ChatResponseContext, PlatformInfo } from \"../../adapter.js\";\nimport * as log from \"../../log.js\";\nimport type { DiscordBot, DiscordEvent } from \"./bot.js\";\n\nexport const DISCORD_FORMATTING_GUIDE = `## Discord Formatting (Markdown)\nBold: **text**, Italic: *text*, Code: \\`code\\`, Block: \\`\\`\\`language\\ncode\\`\\`\\`\nLinks: [text](url), Spoiler: ||text||\nKeep messages under 2000 characters. Use code blocks for code.`;\n\nexport function createDiscordAdapters(\n event: DiscordEvent,\n bot: DiscordBot,\n isEvent?: boolean,\n): {\n message: ChatMessage;\n responseCtx: ChatResponseContext;\n platform: PlatformInfo;\n} {\n let messageId: string | null = null;\n let accumulatedText = \"\";\n let isWorking = true;\n const workingIndicator = \" ...\";\n let updatePromise = Promise.resolve();\n let typingInterval: ReturnType<typeof setInterval> | null = null;\n\n function stopTyping(): void {\n if (typingInterval !== null) {\n clearInterval(typingInterval);\n typingInterval = null;\n }\n }\n\n const conversationId = event.conversationId;\n const channelId = conversationId;\n const _eventFilename = isEvent ? event.text.match(/^\\[EVENT:([^:]+):/)?.[1] : undefined;\n const isThreaded = !!event.thread_ts;\n\n const message: ChatMessage = {\n id: event.ts,\n sessionKey: event.sessionKey ?? `${conversationId}:${event.thread_ts ?? event.ts}`,\n conversationKind: event.conversationKind,\n userId: event.user,\n userName: event.userName,\n text: event.text,\n attachments: event.attachments,\n threadTs: event.thread_ts,\n };\n\n const platform: PlatformInfo = {\n name: \"discord\",\n formattingGuide: DISCORD_FORMATTING_GUIDE,\n channels: bot.getAllChannels(),\n users: bot.getAllUsers(),\n };\n\n // Discord message limit is 2000 chars; use 1900 for safety\n const MAX_LENGTH = 1900;\n const truncationNote = \"\\n\\n*(message truncated, ask me to elaborate on specific parts)*\";\n\n function truncate(text: string, limit: number, note: string): string {\n if (text.length > limit) {\n return text.substring(0, limit - note.length) + note;\n }\n return text;\n }\n\n const responseCtx: ChatResponseContext = {\n respond: async (text: string) => {\n updatePromise = updatePromise.then(async () => {\n try {\n accumulatedText = accumulatedText ? `${accumulatedText}\\n${text}` : text;\n const displayText = truncate(\n isWorking ? accumulatedText + workingIndicator : accumulatedText,\n MAX_LENGTH,\n truncationNote,\n );\n\n if (messageId !== null) {\n await bot.updateMessageRaw(channelId, messageId, displayText);\n } else {\n stopTyping();\n if (isThreaded && event.thread_ts) {\n messageId = await bot.postInThread(channelId, event.thread_ts, displayText);\n } else {\n messageId = await bot.postReply(channelId, event.ts, displayText);\n }\n }\n\n if (messageId !== null) {\n bot.logBotResponse(channelId, text, messageId);\n }\n } catch (err) {\n log.logWarning(\"Discord respond error\", err instanceof Error ? err.message : String(err));\n }\n });\n await updatePromise;\n },\n\n replaceResponse: async (text: string) => {\n updatePromise = updatePromise.then(async () => {\n try {\n accumulatedText = truncate(text, MAX_LENGTH, truncationNote);\n const displayText = isWorking ? accumulatedText + workingIndicator : accumulatedText;\n\n if (messageId !== null) {\n await bot.updateMessageRaw(channelId, messageId, displayText);\n } else {\n stopTyping();\n if (isThreaded && event.thread_ts) {\n messageId = await bot.postInThread(channelId, event.thread_ts, displayText);\n } else {\n messageId = await bot.postReply(channelId, event.ts, displayText);\n }\n }\n } catch (err) {\n log.logWarning(\n \"Discord replaceResponse error\",\n err instanceof Error ? err.message : String(err),\n );\n }\n });\n await updatePromise;\n },\n\n // Discord threads not used here — discard thread-only messages (e.g. usage summary)\n respondInThread: async (_text: string) => {},\n\n setTyping: async (isTyping: boolean) => {\n if (isTyping && typingInterval === null) {\n // Send immediately and repeat every 8s (Discord clears indicator after ~10s)\n bot.sendTyping(channelId).catch(() => {});\n typingInterval = setInterval(() => {\n bot.sendTyping(channelId).catch(() => {});\n }, 8000);\n } else if (!isTyping) {\n stopTyping();\n }\n },\n\n setWorking: async (working: boolean) => {\n updatePromise = updatePromise.then(async () => {\n try {\n isWorking = working;\n if (!working) stopTyping();\n if (messageId !== null) {\n const displayText = isWorking ? accumulatedText + workingIndicator : accumulatedText;\n await bot.updateMessageRaw(channelId, messageId, displayText);\n }\n } catch (err) {\n log.logWarning(\n \"Discord setWorking error\",\n err instanceof Error ? err.message : String(err),\n );\n }\n });\n await updatePromise;\n },\n\n uploadFile: async (filePath: string, title?: string) => {\n await bot.uploadFile(channelId, filePath, title);\n },\n\n deleteResponse: async () => {\n updatePromise = updatePromise.then(async () => {\n stopTyping();\n if (messageId !== null) {\n try {\n await bot.deleteMessageRaw(channelId, messageId);\n } catch {\n // Ignore errors\n }\n messageId = null;\n }\n });\n await updatePromise;\n },\n };\n\n return { message, responseCtx, platform };\n}\n"]}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/adapters/discord/context.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ChatMessage,\n ChatResponseContext,\n ChatToolResult,\n PlatformInfo,\n} from \"../../adapter.js\";\nimport * as log from \"../../log.js\";\nimport { resolveChatSessionKey } from \"../../session-policy.js\";\nimport { createChatResponseErrorReporter, formatToolArgs, splitText } from \"../shared.js\";\nimport type { DiscordBot, DiscordEvent } from \"./bot.js\";\n\nexport const DISCORD_FORMATTING_GUIDE = `## Discord Formatting (Markdown)\nBold: **text**, Italic: *text*, Code: \\`code\\`, Block: \\`\\`\\`language\\ncode\\`\\`\\`\nLinks: [text](url), Spoiler: ||text||\nKeep messages under 2000 characters. Use code blocks for code.`;\n\n// Discord hard limit is 2000 chars; 1900 leaves headroom for working indicator.\nconst MAX_LENGTH = 1900;\n\nconst formatDiscordContinuation = (partNum: number): string => `*(continued ${partNum})*`;\n\nfunction isDiscordMessageReference(id: string | undefined): id is string {\n return typeof id === \"string\" && id !== \"\" && !id.startsWith(\"event:\");\n}\n\nfunction formatToolResult(result: ChatToolResult): string {\n const argsFormatted = formatToolArgs(result.args);\n const duration = (result.durationMs / 1000).toFixed(1);\n let text = `**${result.isError ? \"Error\" : \"Done\"} ${result.toolName}**`;\n if (result.label) text += `: ${result.label}`;\n text += ` (${duration}s)\\n`;\n if (argsFormatted) text += `\\`\\`\\`\\n${argsFormatted}\\n\\`\\`\\`\\n`;\n text += `**Result:**\\n\\`\\`\\`\\n${result.result}\\n\\`\\`\\``;\n return text;\n}\n\nexport function createDiscordAdapters(\n event: DiscordEvent,\n bot: DiscordBot,\n): {\n message: ChatMessage;\n responseCtx: ChatResponseContext;\n platform: PlatformInfo;\n} {\n let messageId: string | null = null;\n let accumulatedText = \"\";\n let isWorking = true;\n const workingIndicator = \" ...\";\n let updatePromise = Promise.resolve();\n let typingInterval: ReturnType<typeof setInterval> | null = null;\n let typingFailureWarned = false;\n\n function stopTyping(): void {\n if (typingInterval !== null) {\n clearInterval(typingInterval);\n typingInterval = null;\n }\n }\n\n const conversationId = event.conversationId;\n const channelId = conversationId;\n const threadTargetId = isDiscordMessageReference(event.thread_ts) ? event.thread_ts : undefined;\n const replyTargetId = isDiscordMessageReference(event.ts) ? event.ts : undefined;\n\n const message: ChatMessage = {\n id: event.ts,\n sessionKey:\n event.sessionKey ??\n resolveChatSessionKey({\n conversationId,\n conversationKind: event.conversationKind,\n messageId: event.ts,\n persistentTopLevel: true,\n threadTs: event.thread_ts,\n }),\n conversationKind: event.conversationKind,\n userId: event.user,\n userName: event.userName,\n text: event.text,\n attachments: event.attachments,\n threadTs: event.thread_ts,\n };\n\n const platform: PlatformInfo = {\n name: \"discord\",\n formattingGuide: DISCORD_FORMATTING_GUIDE,\n channels: bot.getAllChannels(),\n users: bot.getAllUsers(),\n diagnostics: {\n showUsageSummary: false,\n },\n };\n\n async function postDiagnosticMessage(text: string): Promise<string> {\n stopTyping();\n if (threadTargetId) {\n return bot.postInThread(channelId, threadTargetId, text);\n }\n if (replyTargetId) {\n return bot.postReply(channelId, replyTargetId, text);\n }\n if (messageId !== null) {\n return bot.postReply(channelId, messageId, text);\n }\n return bot.postMessage(channelId, text);\n }\n\n const reportResponseError = createChatResponseErrorReporter(() => ({\n platform: \"discord\",\n conversationId,\n channelId,\n messageId: message.id,\n sessionKey: message.sessionKey,\n responseMessageId: messageId,\n threadTs: threadTargetId,\n replyTargetId,\n conversationKind: message.conversationKind,\n }));\n\n const responseCtx: ChatResponseContext = {\n respond: async (text: string) => {\n updatePromise = updatePromise.then(async () => {\n try {\n accumulatedText = accumulatedText ? `${accumulatedText}\\n${text}` : text;\n const [displayText, ...extraParts] = splitText(\n isWorking ? accumulatedText + workingIndicator : accumulatedText,\n MAX_LENGTH,\n formatDiscordContinuation,\n );\n\n if (messageId !== null) {\n await bot.updateMessageRaw(channelId, messageId, displayText);\n } else {\n stopTyping();\n if (threadTargetId) {\n messageId = await bot.postInThread(channelId, threadTargetId, displayText);\n } else if (replyTargetId) {\n messageId = await bot.postReply(channelId, replyTargetId, displayText);\n } else {\n messageId = await bot.postMessage(channelId, displayText);\n }\n }\n for (const part of extraParts) {\n await postDiagnosticMessage(part);\n }\n\n if (messageId !== null) {\n bot.logBotResponse(channelId, text, messageId);\n }\n } catch (err) {\n log.logWarning(\"Discord respond error\", err instanceof Error ? err.message : String(err));\n reportResponseError(err, \"respond\", {\n phase: messageId ? \"update\" : \"initial_post\",\n textLength: text.length,\n accumulatedLength: accumulatedText.length,\n });\n }\n });\n await updatePromise;\n },\n\n replaceResponse: async (text: string) => {\n updatePromise = updatePromise.then(async () => {\n try {\n accumulatedText = text;\n const [displayText, ...extraParts] = splitText(\n accumulatedText,\n MAX_LENGTH,\n formatDiscordContinuation,\n );\n\n if (messageId !== null) {\n await bot.updateMessageRaw(channelId, messageId, displayText);\n } else {\n stopTyping();\n if (threadTargetId) {\n messageId = await bot.postInThread(channelId, threadTargetId, displayText);\n } else if (replyTargetId) {\n messageId = await bot.postReply(channelId, replyTargetId, displayText);\n } else {\n messageId = await bot.postMessage(channelId, displayText);\n }\n }\n for (const part of extraParts) {\n await postDiagnosticMessage(part);\n }\n } catch (err) {\n log.logWarning(\n \"Discord replaceResponse error\",\n err instanceof Error ? err.message : String(err),\n );\n reportResponseError(err, \"replace_response\", {\n textLength: text.length,\n hadExistingResponse: Boolean(messageId),\n });\n }\n });\n await updatePromise;\n },\n\n respondDiagnostic: async (text: string, options?: { style?: \"muted\" | \"error\" }) => {\n updatePromise = updatePromise.then(async () => {\n try {\n const prefix = options?.style === \"error\" ? \"*Error:* \" : \"\";\n for (const part of splitText(`${prefix}${text}`, MAX_LENGTH, formatDiscordContinuation)) {\n await postDiagnosticMessage(part);\n }\n } catch (err) {\n log.logWarning(\n \"Discord respondDiagnostic error\",\n err instanceof Error ? err.message : String(err),\n );\n reportResponseError(err, \"respond_diagnostic\", {\n textLength: text.length,\n style: options?.style,\n });\n }\n });\n await updatePromise;\n },\n\n respondToolResult: async (result: ChatToolResult) => {\n await responseCtx.respondDiagnostic(formatToolResult(result));\n },\n\n setTyping: async (isTyping: boolean) => {\n const onTypingError = (err: unknown): void => {\n if (typingFailureWarned) return;\n typingFailureWarned = true;\n log.logWarning(\n \"Discord sendTyping failed (further occurrences suppressed for this session)\",\n err instanceof Error ? err.message : String(err),\n );\n };\n if (isTyping && typingInterval === null) {\n // Send immediately and repeat every 8s (Discord clears indicator after ~10s)\n bot.sendTyping(channelId).catch(onTypingError);\n typingInterval = setInterval(() => {\n bot.sendTyping(channelId).catch(onTypingError);\n }, 8000);\n } else if (!isTyping) {\n stopTyping();\n }\n },\n\n setWorking: async (working: boolean) => {\n updatePromise = updatePromise.then(async () => {\n try {\n isWorking = working;\n if (!working) stopTyping();\n if (messageId !== null) {\n const [displayText] = splitText(\n isWorking ? accumulatedText + workingIndicator : accumulatedText,\n MAX_LENGTH,\n formatDiscordContinuation,\n );\n await bot.updateMessageRaw(channelId, messageId, displayText);\n }\n } catch (err) {\n log.logWarning(\n \"Discord setWorking error\",\n err instanceof Error ? err.message : String(err),\n );\n reportResponseError(err, \"set_working\", { working });\n }\n });\n await updatePromise;\n },\n\n uploadFile: async (filePath: string, title?: string) => {\n await bot.uploadFile(channelId, filePath, title);\n },\n\n deleteResponse: async () => {\n updatePromise = updatePromise.then(async () => {\n stopTyping();\n if (messageId !== null) {\n try {\n await bot.deleteMessageRaw(channelId, messageId);\n } catch {\n // Ignore errors\n }\n messageId = null;\n }\n });\n await updatePromise;\n },\n };\n\n return { message, responseCtx, platform };\n}\n"]}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Helpers shared across platform adapters.
3
+ *
4
+ * The agent runner is platform-agnostic: it hands strings and structured tool
5
+ * results to each adapter, which decides how to split, format, and route them.
6
+ * The split/normalize logic itself doesn't differ across platforms — only the
7
+ * markup wrappers — so it lives here once.
8
+ */
9
+ import type { BotHandler } from "../adapter.js";
10
+ export type ChatResponseErrorOperation = "respond" | "replace_response" | "respond_diagnostic" | "set_working";
11
+ export interface ChatResponseErrorContext {
12
+ platform: string;
13
+ conversationId: string;
14
+ messageId: string;
15
+ sessionKey: string;
16
+ conversationKind: string;
17
+ operation: ChatResponseErrorOperation;
18
+ channelId?: string;
19
+ chatId?: number;
20
+ responseMessageId?: string | number | null;
21
+ threadTs?: string;
22
+ replyTargetId?: string;
23
+ replyToId?: number | null;
24
+ isThreaded?: boolean;
25
+ extra?: Record<string, unknown>;
26
+ }
27
+ export type ChatResponseErrorReporter = (err: unknown, operation: ChatResponseErrorOperation, extra?: Record<string, unknown>) => void;
28
+ export declare function createChatResponseErrorReporter(resolve: () => Omit<ChatResponseErrorContext, "operation" | "extra">): ChatResponseErrorReporter;
29
+ export declare function reportChatResponseError(err: unknown, context: ChatResponseErrorContext): void;
30
+ export declare class ChannelQueue {
31
+ private readonly name;
32
+ private queue;
33
+ private processing;
34
+ constructor(name?: string);
35
+ enqueue(work: () => Promise<void>): void;
36
+ size(): number;
37
+ private processNext;
38
+ }
39
+ export interface RetryOptions {
40
+ /** Predicate that returns true when an error indicates a platform-side rate limit. */
41
+ isRateLimited: (err: Error) => boolean;
42
+ maxRetries?: number;
43
+ baseDelayMs?: number;
44
+ }
45
+ /**
46
+ * Run `fn` and retry with exponential backoff when its error matches
47
+ * `isRateLimited`. Other errors propagate immediately. Each platform supplies
48
+ * its own predicate so we don't have to know every SDK's error shape here.
49
+ */
50
+ export declare function withRetry<T>(fn: () => Promise<T>, opts: RetryOptions): Promise<T>;
51
+ /**
52
+ * Split `text` into chunks no larger than `limit`, appending a continuation
53
+ * marker (e.g. `_(continued 1)_`) at the end of every part except the last.
54
+ *
55
+ * Each adapter passes its own `formatContinuation` so the marker uses the
56
+ * platform's italic / emphasis convention.
57
+ */
58
+ export declare function splitText(text: string, limit: number, formatContinuation: (partNum: number) => string): string[];
59
+ /**
60
+ * Append a JSON-serializable entry to `${workingDir}/${channel}/log.jsonl`,
61
+ * creating the directory on first use. This is the single write path every
62
+ * adapter uses for human-readable message history.
63
+ */
64
+ export declare function appendChannelLog(workingDir: string, channel: string, entry: object): void;
65
+ /** Convenience for appending the bot's own outbound message. */
66
+ export declare function appendBotResponseLog(workingDir: string, channel: string, text: string, ts: string, threadTs?: string): void;
67
+ export interface ResolveStopTargetInput {
68
+ handler: BotHandler;
69
+ conversationId: string;
70
+ /** Session key derived from the current message; checked first when present. */
71
+ sessionKey?: string;
72
+ }
73
+ /**
74
+ * Pick which session key a `/stop` should target without applying any
75
+ * platform-specific fallback policy. Order:
76
+ * 1. The provided sessionKey, if running.
77
+ * 2. The bare conversationId, if running.
78
+ */
79
+ export declare function resolveStopTarget(input: ResolveStopTargetInput): string | null;
80
+ /**
81
+ * Return the single running scoped session for this conversation, or null when
82
+ * there are zero or multiple matches.
83
+ */
84
+ export declare function resolveOnlyScopedStopTarget(handler: BotHandler, conversationId: string): string | null;
85
+ /**
86
+ * Render tool-call args for human display. Drops `label` (already in the
87
+ * heading) and folds `path` + `offset`/`limit` into a single `path:start-end`
88
+ * line. Pure data normalization with no platform-specific markup.
89
+ */
90
+ export declare function formatToolArgs(args: Record<string, unknown> | undefined): string;
91
+ //# sourceMappingURL=shared.d.ts.map