@animalabs/agent-framework 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +233 -0
  3. package/dist/src/agent.d.ts +130 -0
  4. package/dist/src/agent.d.ts.map +1 -0
  5. package/dist/src/agent.js +406 -0
  6. package/dist/src/agent.js.map +1 -0
  7. package/dist/src/api/mcp-server.d.ts +37 -0
  8. package/dist/src/api/mcp-server.d.ts.map +1 -0
  9. package/dist/src/api/mcp-server.js +598 -0
  10. package/dist/src/api/mcp-server.js.map +1 -0
  11. package/dist/src/api/server.d.ts +83 -0
  12. package/dist/src/api/server.d.ts.map +1 -0
  13. package/dist/src/api/server.js +785 -0
  14. package/dist/src/api/server.js.map +1 -0
  15. package/dist/src/api/types.d.ts +395 -0
  16. package/dist/src/api/types.d.ts.map +1 -0
  17. package/dist/src/api/types.js +5 -0
  18. package/dist/src/api/types.js.map +1 -0
  19. package/dist/src/framework.d.ts +309 -0
  20. package/dist/src/framework.d.ts.map +1 -0
  21. package/dist/src/framework.js +2309 -0
  22. package/dist/src/framework.js.map +1 -0
  23. package/dist/src/gate/event-gate.d.ts +75 -0
  24. package/dist/src/gate/event-gate.d.ts.map +1 -0
  25. package/dist/src/gate/event-gate.js +503 -0
  26. package/dist/src/gate/event-gate.js.map +1 -0
  27. package/dist/src/gate/index.d.ts +3 -0
  28. package/dist/src/gate/index.d.ts.map +1 -0
  29. package/dist/src/gate/index.js +2 -0
  30. package/dist/src/gate/index.js.map +1 -0
  31. package/dist/src/gate/types.d.ts +82 -0
  32. package/dist/src/gate/types.d.ts.map +1 -0
  33. package/dist/src/gate/types.js +9 -0
  34. package/dist/src/gate/types.js.map +1 -0
  35. package/dist/src/index.d.ts +17 -0
  36. package/dist/src/index.d.ts.map +1 -0
  37. package/dist/src/index.js +18 -0
  38. package/dist/src/index.js.map +1 -0
  39. package/dist/src/mcpl/channel-registry.d.ts +141 -0
  40. package/dist/src/mcpl/channel-registry.d.ts.map +1 -0
  41. package/dist/src/mcpl/channel-registry.js +647 -0
  42. package/dist/src/mcpl/channel-registry.js.map +1 -0
  43. package/dist/src/mcpl/checkpoint-manager.d.ts +79 -0
  44. package/dist/src/mcpl/checkpoint-manager.d.ts.map +1 -0
  45. package/dist/src/mcpl/checkpoint-manager.js +430 -0
  46. package/dist/src/mcpl/checkpoint-manager.js.map +1 -0
  47. package/dist/src/mcpl/errors.d.ts +52 -0
  48. package/dist/src/mcpl/errors.d.ts.map +1 -0
  49. package/dist/src/mcpl/errors.js +93 -0
  50. package/dist/src/mcpl/errors.js.map +1 -0
  51. package/dist/src/mcpl/feature-set-manager.d.ts +91 -0
  52. package/dist/src/mcpl/feature-set-manager.d.ts.map +1 -0
  53. package/dist/src/mcpl/feature-set-manager.js +250 -0
  54. package/dist/src/mcpl/feature-set-manager.js.map +1 -0
  55. package/dist/src/mcpl/hook-orchestrator.d.ts +51 -0
  56. package/dist/src/mcpl/hook-orchestrator.d.ts.map +1 -0
  57. package/dist/src/mcpl/hook-orchestrator.js +217 -0
  58. package/dist/src/mcpl/hook-orchestrator.js.map +1 -0
  59. package/dist/src/mcpl/index.d.ts +14 -0
  60. package/dist/src/mcpl/index.d.ts.map +1 -0
  61. package/dist/src/mcpl/index.js +21 -0
  62. package/dist/src/mcpl/index.js.map +1 -0
  63. package/dist/src/mcpl/inference-router.d.ts +67 -0
  64. package/dist/src/mcpl/inference-router.d.ts.map +1 -0
  65. package/dist/src/mcpl/inference-router.js +249 -0
  66. package/dist/src/mcpl/inference-router.js.map +1 -0
  67. package/dist/src/mcpl/push-handler.d.ts +60 -0
  68. package/dist/src/mcpl/push-handler.d.ts.map +1 -0
  69. package/dist/src/mcpl/push-handler.js +162 -0
  70. package/dist/src/mcpl/push-handler.js.map +1 -0
  71. package/dist/src/mcpl/scope-manager.d.ts +76 -0
  72. package/dist/src/mcpl/scope-manager.d.ts.map +1 -0
  73. package/dist/src/mcpl/scope-manager.js +213 -0
  74. package/dist/src/mcpl/scope-manager.js.map +1 -0
  75. package/dist/src/mcpl/server-connection.d.ts +174 -0
  76. package/dist/src/mcpl/server-connection.d.ts.map +1 -0
  77. package/dist/src/mcpl/server-connection.js +559 -0
  78. package/dist/src/mcpl/server-connection.js.map +1 -0
  79. package/dist/src/mcpl/server-registry.d.ts +63 -0
  80. package/dist/src/mcpl/server-registry.d.ts.map +1 -0
  81. package/dist/src/mcpl/server-registry.js +119 -0
  82. package/dist/src/mcpl/server-registry.js.map +1 -0
  83. package/dist/src/mcpl/types.d.ts +755 -0
  84. package/dist/src/mcpl/types.d.ts.map +1 -0
  85. package/dist/src/mcpl/types.js +48 -0
  86. package/dist/src/mcpl/types.js.map +1 -0
  87. package/dist/src/module-registry.d.ts +93 -0
  88. package/dist/src/module-registry.d.ts.map +1 -0
  89. package/dist/src/module-registry.js +386 -0
  90. package/dist/src/module-registry.js.map +1 -0
  91. package/dist/src/modules/api/index.d.ts +22 -0
  92. package/dist/src/modules/api/index.d.ts.map +1 -0
  93. package/dist/src/modules/api/index.js +63 -0
  94. package/dist/src/modules/api/index.js.map +1 -0
  95. package/dist/src/modules/discord/discord-js-client.d.ts +58 -0
  96. package/dist/src/modules/discord/discord-js-client.d.ts.map +1 -0
  97. package/dist/src/modules/discord/discord-js-client.js +273 -0
  98. package/dist/src/modules/discord/discord-js-client.js.map +1 -0
  99. package/dist/src/modules/discord/index.d.ts +82 -0
  100. package/dist/src/modules/discord/index.d.ts.map +1 -0
  101. package/dist/src/modules/discord/index.js +959 -0
  102. package/dist/src/modules/discord/index.js.map +1 -0
  103. package/dist/src/modules/discord/types.d.ts +327 -0
  104. package/dist/src/modules/discord/types.d.ts.map +1 -0
  105. package/dist/src/modules/discord/types.js +5 -0
  106. package/dist/src/modules/discord/types.js.map +1 -0
  107. package/dist/src/modules/index.d.ts +11 -0
  108. package/dist/src/modules/index.d.ts.map +1 -0
  109. package/dist/src/modules/index.js +7 -0
  110. package/dist/src/modules/index.js.map +1 -0
  111. package/dist/src/modules/workspace/index.d.ts +59 -0
  112. package/dist/src/modules/workspace/index.d.ts.map +1 -0
  113. package/dist/src/modules/workspace/index.js +831 -0
  114. package/dist/src/modules/workspace/index.js.map +1 -0
  115. package/dist/src/modules/workspace/sync.d.ts +48 -0
  116. package/dist/src/modules/workspace/sync.d.ts.map +1 -0
  117. package/dist/src/modules/workspace/sync.js +253 -0
  118. package/dist/src/modules/workspace/sync.js.map +1 -0
  119. package/dist/src/modules/workspace/types.d.ts +166 -0
  120. package/dist/src/modules/workspace/types.d.ts.map +1 -0
  121. package/dist/src/modules/workspace/types.js +5 -0
  122. package/dist/src/modules/workspace/types.js.map +1 -0
  123. package/dist/src/modules/workspace/watcher.d.ts +43 -0
  124. package/dist/src/modules/workspace/watcher.d.ts.map +1 -0
  125. package/dist/src/modules/workspace/watcher.js +120 -0
  126. package/dist/src/modules/workspace/watcher.js.map +1 -0
  127. package/dist/src/queue.d.ts +50 -0
  128. package/dist/src/queue.d.ts.map +1 -0
  129. package/dist/src/queue.js +95 -0
  130. package/dist/src/queue.js.map +1 -0
  131. package/dist/src/safe-slice.d.ts +8 -0
  132. package/dist/src/safe-slice.d.ts.map +1 -0
  133. package/dist/src/safe-slice.js +18 -0
  134. package/dist/src/safe-slice.js.map +1 -0
  135. package/dist/src/types/agent.d.ts +118 -0
  136. package/dist/src/types/agent.d.ts.map +1 -0
  137. package/dist/src/types/agent.js +2 -0
  138. package/dist/src/types/agent.js.map +1 -0
  139. package/dist/src/types/events.d.ts +220 -0
  140. package/dist/src/types/events.d.ts.map +1 -0
  141. package/dist/src/types/events.js +2 -0
  142. package/dist/src/types/events.js.map +1 -0
  143. package/dist/src/types/framework.d.ts +184 -0
  144. package/dist/src/types/framework.d.ts.map +1 -0
  145. package/dist/src/types/framework.js +2 -0
  146. package/dist/src/types/framework.js.map +1 -0
  147. package/dist/src/types/index.d.ts +7 -0
  148. package/dist/src/types/index.d.ts.map +1 -0
  149. package/dist/src/types/index.js +7 -0
  150. package/dist/src/types/index.js.map +1 -0
  151. package/dist/src/types/inference-log.d.ts +64 -0
  152. package/dist/src/types/inference-log.d.ts.map +1 -0
  153. package/dist/src/types/inference-log.js +5 -0
  154. package/dist/src/types/inference-log.js.map +1 -0
  155. package/dist/src/types/module.d.ts +302 -0
  156. package/dist/src/types/module.d.ts.map +1 -0
  157. package/dist/src/types/module.js +2 -0
  158. package/dist/src/types/module.js.map +1 -0
  159. package/dist/src/types/trace.d.ts +173 -0
  160. package/dist/src/types/trace.d.ts.map +1 -0
  161. package/dist/src/types/trace.js +2 -0
  162. package/dist/src/types/trace.js.map +1 -0
  163. package/dist/test/event-gate.test.d.ts +2 -0
  164. package/dist/test/event-gate.test.d.ts.map +1 -0
  165. package/dist/test/event-gate.test.js +535 -0
  166. package/dist/test/event-gate.test.js.map +1 -0
  167. package/dist/test/framework.test.d.ts +5 -0
  168. package/dist/test/framework.test.d.ts.map +1 -0
  169. package/dist/test/framework.test.js +738 -0
  170. package/dist/test/framework.test.js.map +1 -0
  171. package/dist/tsconfig.tsbuildinfo +1 -0
  172. package/package.json +58 -0
  173. package/src/agent.ts +512 -0
  174. package/src/api/mcp-server.ts +687 -0
  175. package/src/api/server.ts +1023 -0
  176. package/src/api/types.ts +542 -0
  177. package/src/framework.ts +2784 -0
  178. package/src/gate/event-gate.ts +623 -0
  179. package/src/gate/index.ts +12 -0
  180. package/src/gate/types.ts +112 -0
  181. package/src/index.ts +63 -0
  182. package/src/mcpl/channel-registry.ts +824 -0
  183. package/src/mcpl/checkpoint-manager.ts +545 -0
  184. package/src/mcpl/errors.ts +114 -0
  185. package/src/mcpl/feature-set-manager.ts +333 -0
  186. package/src/mcpl/hook-orchestrator.ts +281 -0
  187. package/src/mcpl/index.ts +134 -0
  188. package/src/mcpl/inference-router.ts +335 -0
  189. package/src/mcpl/push-handler.ts +232 -0
  190. package/src/mcpl/scope-manager.ts +262 -0
  191. package/src/mcpl/server-connection.ts +694 -0
  192. package/src/mcpl/server-registry.ts +144 -0
  193. package/src/mcpl/types.ts +991 -0
  194. package/src/module-registry.ts +547 -0
  195. package/src/modules/api/index.ts +94 -0
  196. package/src/modules/discord/discord-js-client.ts +353 -0
  197. package/src/modules/discord/index.ts +1146 -0
  198. package/src/modules/discord/types.ts +465 -0
  199. package/src/modules/index.ts +43 -0
  200. package/src/modules/workspace/index.ts +969 -0
  201. package/src/modules/workspace/sync.ts +314 -0
  202. package/src/modules/workspace/types.ts +202 -0
  203. package/src/modules/workspace/watcher.ts +142 -0
  204. package/src/queue.ts +106 -0
  205. package/src/safe-slice.ts +18 -0
  206. package/src/types/agent.ts +119 -0
  207. package/src/types/events.ts +252 -0
  208. package/src/types/framework.ts +211 -0
  209. package/src/types/index.ts +6 -0
  210. package/src/types/inference-log.ts +64 -0
  211. package/src/types/module.ts +367 -0
  212. package/src/types/trace.ts +196 -0
@@ -0,0 +1,353 @@
1
+ /**
2
+ * Discord.js client adapter implementing DiscordClientInterface
3
+ */
4
+
5
+ import {
6
+ Client,
7
+ GatewayIntentBits,
8
+ Partials,
9
+ TextChannel,
10
+ DMChannel,
11
+ type Message,
12
+ type Attachment,
13
+ type User,
14
+ type Guild,
15
+ type GuildBasedChannel,
16
+ } from 'discord.js';
17
+ import type {
18
+ DiscordClientInterface,
19
+ DiscordMessageData,
20
+ DiscordAttachment,
21
+ DiscordGuild,
22
+ DiscordChannel,
23
+ HistoryMessage,
24
+ } from './types.js';
25
+
26
+ export interface DiscordJsClientConfig {
27
+ token: string;
28
+ guildIds?: string[];
29
+ channelIds?: string[];
30
+ }
31
+
32
+ export class DiscordJsClient implements DiscordClientInterface {
33
+ private client: Client;
34
+ private token: string;
35
+ private guildIds?: string[];
36
+ private channelIds?: string[];
37
+
38
+ private messageHandler?: (message: DiscordMessageData) => void;
39
+ private editHandler?: (messageId: string, newContent: string) => void;
40
+ private deleteHandler?: (messageId: string) => void;
41
+ private readyHandler?: () => void;
42
+
43
+ constructor(config: DiscordJsClientConfig) {
44
+ this.token = config.token;
45
+ this.guildIds = config.guildIds;
46
+ this.channelIds = config.channelIds;
47
+
48
+ this.client = new Client({
49
+ intents: [
50
+ GatewayIntentBits.Guilds,
51
+ GatewayIntentBits.GuildMessages,
52
+ GatewayIntentBits.MessageContent,
53
+ GatewayIntentBits.DirectMessages,
54
+ GatewayIntentBits.GuildMessageReactions,
55
+ ],
56
+ partials: [Partials.Channel, Partials.Message],
57
+ });
58
+
59
+ this.setupEventHandlers();
60
+ }
61
+
62
+ private setupEventHandlers(): void {
63
+ this.client.on('messageCreate', (message: Message) => {
64
+ if (!this.shouldHandleMessage(message)) return;
65
+ if (this.messageHandler) {
66
+ this.messageHandler(this.convertMessage(message));
67
+ }
68
+ });
69
+
70
+ this.client.on('messageUpdate', (_oldMessage, newMessage) => {
71
+ if (!newMessage.content || !this.editHandler) return;
72
+ this.editHandler(newMessage.id, newMessage.content);
73
+ });
74
+
75
+ this.client.on('messageDelete', (message) => {
76
+ if (this.deleteHandler) {
77
+ this.deleteHandler(message.id);
78
+ }
79
+ });
80
+
81
+ this.client.on('ready', () => {
82
+ console.log(`[Discord] Logged in as ${this.client.user?.tag}`);
83
+ if (this.readyHandler) {
84
+ this.readyHandler();
85
+ }
86
+ });
87
+
88
+ this.client.on('error', (error: Error) => {
89
+ console.error('[Discord] Client error:', error);
90
+ });
91
+ }
92
+
93
+ private shouldHandleMessage(message: Message): boolean {
94
+ // Never process our own messages (prevents feedback loop)
95
+ if (message.author.id === this.client.user?.id) {
96
+ return false;
97
+ }
98
+
99
+ // Filter by guild
100
+ if (this.guildIds && this.guildIds.length > 0) {
101
+ if (!message.guildId || !this.guildIds.includes(message.guildId)) {
102
+ return false;
103
+ }
104
+ }
105
+
106
+ // Filter by channel
107
+ if (this.channelIds && this.channelIds.length > 0) {
108
+ if (!this.channelIds.includes(message.channelId)) {
109
+ return false;
110
+ }
111
+ }
112
+
113
+ return true;
114
+ }
115
+
116
+ private convertMessage(message: Message): DiscordMessageData {
117
+ const attachments: DiscordAttachment[] = message.attachments.map((a: Attachment) => ({
118
+ id: a.id,
119
+ filename: a.name ?? 'unknown',
120
+ url: a.url,
121
+ contentType: a.contentType ?? undefined,
122
+ size: a.size,
123
+ }));
124
+
125
+ // Extract mentioned user IDs
126
+ const mentionedUserIds = message.mentions.users.map((u: User) => u.id);
127
+
128
+ return {
129
+ id: message.id,
130
+ content: message.content,
131
+ authorId: message.author.id,
132
+ authorName: message.author.username,
133
+ isBot: message.author.bot,
134
+ channelId: message.channelId,
135
+ guildId: message.guildId ?? null,
136
+ isReply: message.reference !== null,
137
+ replyToId: message.reference?.messageId ?? undefined,
138
+ // Note: replyToAuthorId requires fetching the referenced message
139
+ // We'll leave it undefined for now - can be enhanced later if needed
140
+ replyToAuthorId: undefined,
141
+ mentionedUserIds,
142
+ attachments,
143
+ timestamp: message.createdAt,
144
+ };
145
+ }
146
+
147
+ async connect(): Promise<void> {
148
+ await this.client.login(this.token);
149
+ }
150
+
151
+ async disconnect(): Promise<void> {
152
+ this.client.destroy();
153
+ }
154
+
155
+ isConnected(): boolean {
156
+ return this.client.isReady();
157
+ }
158
+
159
+ async sendMessage(
160
+ channelId: string,
161
+ content: string,
162
+ options?: { replyTo?: string; createThread?: { name: string } }
163
+ ): Promise<{ messageId: string }> {
164
+ const channel = await this.client.channels.fetch(channelId);
165
+ if (!channel || !('send' in channel)) {
166
+ throw new Error(`Channel ${channelId} not found or not a text channel`);
167
+ }
168
+
169
+ const textChannel = channel as TextChannel | DMChannel;
170
+ const message = await textChannel.send({
171
+ content,
172
+ reply: options?.replyTo ? { messageReference: options.replyTo } : undefined,
173
+ });
174
+
175
+ if (options?.createThread && 'threads' in textChannel) {
176
+ await message.startThread({ name: options.createThread.name });
177
+ }
178
+
179
+ return { messageId: message.id };
180
+ }
181
+
182
+ async sendDM(userId: string, content: string): Promise<{ messageId: string }> {
183
+ const user = await this.client.users.fetch(userId);
184
+ const message = await user.send(content);
185
+ return { messageId: message.id };
186
+ }
187
+
188
+ async fetchMessage(channelId: string, messageId: string): Promise<string> {
189
+ const channel = await this.client.channels.fetch(channelId);
190
+ if (!channel || !('messages' in channel)) {
191
+ throw new Error(`Channel ${channelId} not found`);
192
+ }
193
+ const message = await (channel as TextChannel).messages.fetch(messageId);
194
+ return message.content;
195
+ }
196
+
197
+ async editMessage(channelId: string, messageId: string, content: string): Promise<void> {
198
+ const channel = await this.client.channels.fetch(channelId);
199
+ if (!channel || !('messages' in channel)) {
200
+ throw new Error(`Channel ${channelId} not found`);
201
+ }
202
+ const message = await (channel as TextChannel).messages.fetch(messageId);
203
+ await message.edit(content);
204
+ }
205
+
206
+ async deleteMessage(channelId: string, messageId: string): Promise<void> {
207
+ const channel = await this.client.channels.fetch(channelId);
208
+ if (!channel || !('messages' in channel)) {
209
+ throw new Error(`Channel ${channelId} not found`);
210
+ }
211
+ const message = await (channel as TextChannel).messages.fetch(messageId);
212
+ await message.delete();
213
+ }
214
+
215
+ async addReaction(channelId: string, messageId: string, emoji: string): Promise<void> {
216
+ const channel = await this.client.channels.fetch(channelId);
217
+ if (!channel || !('messages' in channel)) {
218
+ throw new Error(`Channel ${channelId} not found`);
219
+ }
220
+ const message = await (channel as TextChannel).messages.fetch(messageId);
221
+ await message.react(emoji);
222
+ }
223
+
224
+ async createThread(
225
+ channelId: string,
226
+ messageId: string,
227
+ name: string,
228
+ autoArchiveDuration?: number
229
+ ): Promise<{ threadId: string }> {
230
+ const channel = await this.client.channels.fetch(channelId);
231
+ if (!channel || !('messages' in channel)) {
232
+ throw new Error(`Channel ${channelId} not found`);
233
+ }
234
+ const message = await (channel as TextChannel).messages.fetch(messageId);
235
+ const thread = await message.startThread({
236
+ name,
237
+ autoArchiveDuration: (autoArchiveDuration as 60 | 1440 | 4320 | 10080) ?? 1440,
238
+ });
239
+ return { threadId: thread.id };
240
+ }
241
+
242
+ onMessage(handler: (message: DiscordMessageData) => void): void {
243
+ this.messageHandler = handler;
244
+ }
245
+
246
+ onMessageEdit(handler: (messageId: string, newContent: string) => void): void {
247
+ this.editHandler = handler;
248
+ }
249
+
250
+ onMessageDelete(handler: (messageId: string) => void): void {
251
+ this.deleteHandler = handler;
252
+ }
253
+
254
+ onReady(handler: () => void): void {
255
+ this.readyHandler = handler;
256
+ }
257
+
258
+ getBotUserId(): string | null {
259
+ return this.client.user?.id ?? null;
260
+ }
261
+
262
+ async sendTyping(channelId: string): Promise<void> {
263
+ const channel = await this.client.channels.fetch(channelId);
264
+ if (!channel || !('sendTyping' in channel)) {
265
+ throw new Error(`Channel ${channelId} not found or not a text channel`);
266
+ }
267
+ await (channel as TextChannel).sendTyping();
268
+ }
269
+
270
+ async listGuilds(): Promise<DiscordGuild[]> {
271
+ const guilds = this.client.guilds.cache;
272
+ return guilds.map((g: Guild) => ({
273
+ id: g.id,
274
+ name: g.name,
275
+ iconUrl: g.iconURL() ?? undefined,
276
+ memberCount: g.memberCount,
277
+ }));
278
+ }
279
+
280
+ async listChannels(guildId: string): Promise<DiscordChannel[]> {
281
+ const guild = await this.client.guilds.fetch(guildId);
282
+ if (!guild) {
283
+ throw new Error(`Guild ${guildId} not found`);
284
+ }
285
+
286
+ // guild.channels might be undefined if the guild was fetched without full data
287
+ if (!guild.channels) {
288
+ throw new Error(`Guild ${guildId} channels not available - may need to re-fetch with force`);
289
+ }
290
+
291
+ const channels = await guild.channels.fetch();
292
+ const result: DiscordChannel[] = [];
293
+
294
+ channels.forEach((c: GuildBasedChannel | null) => {
295
+ if (c) {
296
+ result.push({
297
+ id: c.id,
298
+ name: c.name,
299
+ type: this.mapChannelType(c.type),
300
+ parentId: c.parentId ?? undefined,
301
+ position: 'position' in c ? (c as { position?: number }).position : undefined,
302
+ });
303
+ }
304
+ });
305
+
306
+ return result;
307
+ }
308
+
309
+ private mapChannelType(type: number | undefined): DiscordChannel['type'] {
310
+ if (type === undefined) return 'unknown';
311
+ // Discord.js channel types: 0=text, 2=voice, 4=category, 11=thread, 15=forum
312
+ switch (type) {
313
+ case 0: return 'text';
314
+ case 2: return 'voice';
315
+ case 4: return 'category';
316
+ case 11:
317
+ case 12: return 'thread';
318
+ case 15: return 'forum';
319
+ default: return 'unknown';
320
+ }
321
+ }
322
+
323
+ async fetchHistory(
324
+ channelId: string,
325
+ options?: { limit?: number; before?: string }
326
+ ): Promise<HistoryMessage[]> {
327
+ const channel = await this.client.channels.fetch(channelId);
328
+ if (!channel || !('messages' in channel)) {
329
+ throw new Error(`Channel ${channelId} not found or not a text channel`);
330
+ }
331
+
332
+ const textChannel = channel as TextChannel;
333
+ const messages = await textChannel.messages.fetch({
334
+ limit: options?.limit ?? 50,
335
+ before: options?.before,
336
+ });
337
+
338
+ return messages.map((m: Message) => ({
339
+ id: m.id,
340
+ authorId: m.author.id,
341
+ authorName: m.author.username,
342
+ isBot: m.author.bot,
343
+ content: m.content,
344
+ timestamp: m.createdAt,
345
+ replyTo: m.reference
346
+ ? {
347
+ messageId: m.reference.messageId ?? '',
348
+ authorId: '', // Would need additional fetch to get this
349
+ }
350
+ : undefined,
351
+ }));
352
+ }
353
+ }