agent4discord 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 (78) hide show
  1. package/README.ko.md +134 -0
  2. package/README.md +170 -0
  3. package/dist/bot.d.ts +1 -0
  4. package/dist/bot.js +114 -0
  5. package/dist/bot.js.map +1 -0
  6. package/dist/cli.d.ts +2 -0
  7. package/dist/cli.js +27 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/commands/index.d.ts +9 -0
  10. package/dist/commands/index.js +44 -0
  11. package/dist/commands/index.js.map +1 -0
  12. package/dist/commands/init.d.ts +5 -0
  13. package/dist/commands/init.js +152 -0
  14. package/dist/commands/init.js.map +1 -0
  15. package/dist/commands/model.d.ts +5 -0
  16. package/dist/commands/model.js +65 -0
  17. package/dist/commands/model.js.map +1 -0
  18. package/dist/commands/resume.d.ts +6 -0
  19. package/dist/commands/resume.js +113 -0
  20. package/dist/commands/resume.js.map +1 -0
  21. package/dist/config.d.ts +12 -0
  22. package/dist/config.js +49 -0
  23. package/dist/config.js.map +1 -0
  24. package/dist/formatters/chunker.d.ts +5 -0
  25. package/dist/formatters/chunker.js +46 -0
  26. package/dist/formatters/chunker.js.map +1 -0
  27. package/dist/formatters/embedBuilder.d.ts +28 -0
  28. package/dist/formatters/embedBuilder.js +32 -0
  29. package/dist/formatters/embedBuilder.js.map +1 -0
  30. package/dist/formatters/toolFormatter.d.ts +4 -0
  31. package/dist/formatters/toolFormatter.js +90 -0
  32. package/dist/formatters/toolFormatter.js.map +1 -0
  33. package/dist/guild.d.ts +22 -0
  34. package/dist/guild.js +41 -0
  35. package/dist/guild.js.map +1 -0
  36. package/dist/interactions/directoryBrowser.d.ts +61 -0
  37. package/dist/interactions/directoryBrowser.js +611 -0
  38. package/dist/interactions/directoryBrowser.js.map +1 -0
  39. package/dist/interactions/index.d.ts +5 -0
  40. package/dist/interactions/index.js +92 -0
  41. package/dist/interactions/index.js.map +1 -0
  42. package/dist/interactions/permissionHandler.d.ts +11 -0
  43. package/dist/interactions/permissionHandler.js +107 -0
  44. package/dist/interactions/permissionHandler.js.map +1 -0
  45. package/dist/interactions/sessionControls.d.ts +9 -0
  46. package/dist/interactions/sessionControls.js +95 -0
  47. package/dist/interactions/sessionControls.js.map +1 -0
  48. package/dist/sessions/eventHandler.d.ts +3 -0
  49. package/dist/sessions/eventHandler.js +209 -0
  50. package/dist/sessions/eventHandler.js.map +1 -0
  51. package/dist/sessions/sessionManager.d.ts +29 -0
  52. package/dist/sessions/sessionManager.js +203 -0
  53. package/dist/sessions/sessionManager.js.map +1 -0
  54. package/dist/sessions/sessionStore.d.ts +4 -0
  55. package/dist/sessions/sessionStore.js +29 -0
  56. package/dist/sessions/sessionStore.js.map +1 -0
  57. package/dist/sessions/streamHandler.d.ts +18 -0
  58. package/dist/sessions/streamHandler.js +119 -0
  59. package/dist/sessions/streamHandler.js.map +1 -0
  60. package/dist/sessions/toolProgress.d.ts +14 -0
  61. package/dist/sessions/toolProgress.js +65 -0
  62. package/dist/sessions/toolProgress.js.map +1 -0
  63. package/dist/sessions/usageTracker.d.ts +12 -0
  64. package/dist/sessions/usageTracker.js +222 -0
  65. package/dist/sessions/usageTracker.js.map +1 -0
  66. package/dist/setup.d.ts +1 -0
  67. package/dist/setup.js +101 -0
  68. package/dist/setup.js.map +1 -0
  69. package/dist/utils/filesystem.d.ts +11 -0
  70. package/dist/utils/filesystem.js +26 -0
  71. package/dist/utils/filesystem.js.map +1 -0
  72. package/dist/utils/logger.d.ts +1 -0
  73. package/dist/utils/logger.js +3 -0
  74. package/dist/utils/logger.js.map +1 -0
  75. package/dist/utils/plugins.d.ts +6 -0
  76. package/dist/utils/plugins.js +66 -0
  77. package/dist/utils/plugins.js.map +1 -0
  78. package/package.json +45 -0
@@ -0,0 +1,92 @@
1
+ import { handleDirectoryBrowse, handleDirectoryCancel, handleDirectoryNext, handleDirectoryParent, handleDirectoryPrev, handleModelCancel, handleModelConfirm, handleModelSelect, handleResumeBack, handleResumeBrowse, handleResumeSession, handleResumeStart, handleSessionStart, } from './directoryBrowser.js';
2
+ import { handlePermission } from './permissionHandler.js';
3
+ import { handleSessionStop, handleSessionArchive } from './sessionControls.js';
4
+ /**
5
+ * Route component interactions (buttons, select menus) based on customId prefix.
6
+ */
7
+ export async function routeInteraction(interaction) {
8
+ const customId = interaction.customId;
9
+ if (customId.startsWith('a4d:dir:')) {
10
+ switch (customId) {
11
+ case 'a4d:dir:browse':
12
+ await handleDirectoryBrowse(interaction);
13
+ return;
14
+ case 'a4d:dir:parent':
15
+ await handleDirectoryParent(interaction);
16
+ return;
17
+ case 'a4d:dir:start':
18
+ await handleSessionStart(interaction);
19
+ return;
20
+ case 'a4d:dir:resume':
21
+ await handleResumeSession(interaction);
22
+ return;
23
+ case 'a4d:dir:cancel':
24
+ await handleDirectoryCancel(interaction);
25
+ return;
26
+ case 'a4d:dir:prev':
27
+ await handleDirectoryPrev(interaction);
28
+ return;
29
+ case 'a4d:dir:next':
30
+ await handleDirectoryNext(interaction);
31
+ return;
32
+ case 'a4d:dir:pageinfo':
33
+ await interaction.deferUpdate();
34
+ return;
35
+ default:
36
+ await interaction.reply({ content: 'Unknown directory browser action.', ephemeral: true });
37
+ return;
38
+ }
39
+ }
40
+ if (customId.startsWith('a4d:model:')) {
41
+ switch (customId) {
42
+ case 'a4d:model:select':
43
+ await handleModelSelect(interaction);
44
+ return;
45
+ case 'a4d:model:confirm':
46
+ await handleModelConfirm(interaction);
47
+ return;
48
+ case 'a4d:model:cancel':
49
+ await handleModelCancel(interaction);
50
+ return;
51
+ default:
52
+ await interaction.reply({ content: 'Unknown model selection action.', ephemeral: true });
53
+ return;
54
+ }
55
+ }
56
+ if (customId.startsWith('a4d:session:')) {
57
+ switch (customId) {
58
+ case 'a4d:session:stop':
59
+ await handleSessionStop(interaction);
60
+ return;
61
+ case 'a4d:session:archive':
62
+ await handleSessionArchive(interaction);
63
+ return;
64
+ default:
65
+ await interaction.reply({ content: 'Unknown session control action.', ephemeral: true });
66
+ return;
67
+ }
68
+ }
69
+ if (customId.startsWith('a4d:perm:')) {
70
+ await handlePermission(interaction);
71
+ return;
72
+ }
73
+ if (customId.startsWith('a4d:resume:')) {
74
+ switch (customId) {
75
+ case 'a4d:resume:browse':
76
+ await handleResumeBrowse(interaction);
77
+ return;
78
+ case 'a4d:resume:back':
79
+ await handleResumeBack(interaction);
80
+ return;
81
+ case 'a4d:resume:start':
82
+ await handleResumeStart(interaction);
83
+ return;
84
+ default:
85
+ await interaction.reply({ content: 'Unknown resume action.', ephemeral: true });
86
+ return;
87
+ }
88
+ }
89
+ console.warn(`[interactions] Unknown customId: ${customId}`);
90
+ await interaction.reply({ content: 'Unknown interaction.', ephemeral: true });
91
+ }
92
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/interactions/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { type ButtonInteraction, type TextChannel } from 'discord.js';
2
+ import type { PermissionResult } from '@anthropic-ai/claude-agent-sdk';
3
+ /**
4
+ * Request tool permission via Discord buttons.
5
+ * Returns a promise that resolves when the user clicks Allow/Deny or times out.
6
+ */
7
+ export declare function requestPermission(channel: TextChannel, userId: string, toolName: string, toolInput: Record<string, unknown>): Promise<PermissionResult>;
8
+ /**
9
+ * Handle permission button interactions.
10
+ */
11
+ export declare function handlePermission(interaction: ButtonInteraction): Promise<void>;
@@ -0,0 +1,107 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder, MessageFlags, } from 'discord.js';
3
+ import { COLORS } from '../formatters/embedBuilder.js';
4
+ import { formatToolInput, getToolEmoji } from '../formatters/toolFormatter.js';
5
+ // Auto-allow these safe tools
6
+ const AUTO_ALLOW_TOOLS = new Set(['Read', 'Glob', 'Grep', 'LSP']);
7
+ const pendingPermissions = new Map();
8
+ /**
9
+ * Request tool permission via Discord buttons.
10
+ * Returns a promise that resolves when the user clicks Allow/Deny or times out.
11
+ */
12
+ export async function requestPermission(channel, userId, toolName, toolInput) {
13
+ // Auto-allow safe tools
14
+ if (AUTO_ALLOW_TOOLS.has(toolName)) {
15
+ return { behavior: 'allow', updatedInput: {} };
16
+ }
17
+ const requestId = randomUUID().slice(0, 8); // short ID for customId limit
18
+ // Build permission embed
19
+ const emoji = getToolEmoji(toolName);
20
+ const inputPreview = formatToolInput(toolName, toolInput);
21
+ const embed = new EmbedBuilder()
22
+ .setTitle(`${emoji} Permission Request`)
23
+ .setDescription(`**Tool:** ${toolName}\n\n${inputPreview.slice(0, 3000)}`)
24
+ .setColor(COLORS.PERMISSION)
25
+ .setFooter({ text: `Expires in 60 seconds` });
26
+ // Build buttons
27
+ const row = new ActionRowBuilder().addComponents(new ButtonBuilder()
28
+ .setCustomId(`a4d:perm:${requestId}:allow`)
29
+ .setLabel('Allow')
30
+ .setStyle(ButtonStyle.Success), new ButtonBuilder()
31
+ .setCustomId(`a4d:perm:${requestId}:deny`)
32
+ .setLabel('Deny')
33
+ .setStyle(ButtonStyle.Danger), new ButtonBuilder()
34
+ .setCustomId(`a4d:perm:${requestId}:details`)
35
+ .setLabel('Details')
36
+ .setStyle(ButtonStyle.Secondary));
37
+ const msg = await channel.send({ embeds: [embed], components: [row] });
38
+ return new Promise((resolve) => {
39
+ const timeout = setTimeout(() => {
40
+ pendingPermissions.delete(requestId);
41
+ const timedOutEmbed = EmbedBuilder.from(embed)
42
+ .setTitle(`${emoji} Permission Request (Timed Out)`)
43
+ .setColor(COLORS.STOPPED);
44
+ const disabledRow = buildDisabledRow();
45
+ msg.edit({ embeds: [timedOutEmbed], components: [disabledRow] }).catch(() => { });
46
+ resolve({ behavior: 'deny', message: 'Permission request timed out' });
47
+ }, 60_000);
48
+ pendingPermissions.set(requestId, {
49
+ resolve: (result) => {
50
+ clearTimeout(timeout);
51
+ pendingPermissions.delete(requestId);
52
+ resolve(result);
53
+ },
54
+ channelId: channel.id,
55
+ messageId: msg.id,
56
+ userId,
57
+ toolInput,
58
+ });
59
+ });
60
+ }
61
+ /**
62
+ * Handle permission button interactions.
63
+ */
64
+ export async function handlePermission(interaction) {
65
+ const parts = interaction.customId.split(':');
66
+ // a4d:perm:{requestId}:{action}
67
+ const requestId = parts[2];
68
+ const action = parts[3];
69
+ const pending = pendingPermissions.get(requestId);
70
+ if (!pending) {
71
+ await interaction.reply({ content: 'This permission request has expired.', flags: MessageFlags.Ephemeral });
72
+ return;
73
+ }
74
+ // Only session owner can approve/deny
75
+ if (interaction.user.id !== pending.userId) {
76
+ await interaction.reply({ content: 'Only the session owner can approve or deny this.', flags: MessageFlags.Ephemeral });
77
+ return;
78
+ }
79
+ if (action === 'details') {
80
+ // Show full input as ephemeral (don't resolve/delete)
81
+ const fullInput = JSON.stringify(pending.toolInput, null, 2);
82
+ const truncated = fullInput.length > 1900 ? fullInput.slice(0, 1900) + '\n...' : fullInput;
83
+ await interaction.reply({
84
+ content: `\`\`\`json\n${truncated}\n\`\`\``,
85
+ flags: MessageFlags.Ephemeral,
86
+ });
87
+ return;
88
+ }
89
+ if (action === 'allow') {
90
+ const embed = EmbedBuilder.from(interaction.message.embeds[0])
91
+ .setTitle('Permission Granted')
92
+ .setColor(COLORS.IDLE);
93
+ await interaction.update({ embeds: [embed], components: [buildDisabledRow()] });
94
+ pending.resolve({ behavior: 'allow', updatedInput: {} });
95
+ }
96
+ else if (action === 'deny') {
97
+ const embed = EmbedBuilder.from(interaction.message.embeds[0])
98
+ .setTitle('Permission Denied')
99
+ .setColor(COLORS.STOPPED);
100
+ await interaction.update({ embeds: [embed], components: [buildDisabledRow()] });
101
+ pending.resolve({ behavior: 'deny', message: 'User denied via Discord' });
102
+ }
103
+ }
104
+ function buildDisabledRow() {
105
+ return new ActionRowBuilder().addComponents(new ButtonBuilder().setCustomId('a4d:perm:x:allow').setLabel('Allow').setStyle(ButtonStyle.Success).setDisabled(true), new ButtonBuilder().setCustomId('a4d:perm:x:deny').setLabel('Deny').setStyle(ButtonStyle.Danger).setDisabled(true), new ButtonBuilder().setCustomId('a4d:perm:x:details').setLabel('Details').setStyle(ButtonStyle.Secondary).setDisabled(true));
106
+ }
107
+ //# sourceMappingURL=permissionHandler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissionHandler.js","sourceRoot":"","sources":["../../src/interactions/permissionHandler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { type ButtonInteraction } from 'discord.js';
2
+ /**
3
+ * Handle the Stop button for a session channel.
4
+ */
5
+ export declare function handleSessionStop(interaction: ButtonInteraction): Promise<void>;
6
+ /**
7
+ * Handle the Archive button for a session channel.
8
+ */
9
+ export declare function handleSessionArchive(interaction: ButtonInteraction): Promise<void>;
@@ -0,0 +1,95 @@
1
+ // Session control handlers (Stop / Archive) -- AGE-018
2
+ import { ActionRowBuilder, ButtonBuilder, ButtonStyle, } from 'discord.js';
3
+ import { sessionManager } from '../sessions/sessionManager.js';
4
+ import { removeSessionFromGuild } from '../sessions/sessionStore.js';
5
+ import { buildStatusEmbed, COLORS } from '../formatters/embedBuilder.js';
6
+ /**
7
+ * Find the pinned status embed message in a channel.
8
+ */
9
+ async function findStatusMessage(channel) {
10
+ const pinned = await channel.messages.fetchPinned();
11
+ return pinned.find((m) => m.author.id === channel.client.user?.id && m.embeds.length > 0) ?? null;
12
+ }
13
+ /**
14
+ * Build disabled control row (used after stop/archive).
15
+ */
16
+ function buildDisabledControlRow() {
17
+ return new ActionRowBuilder().addComponents(new ButtonBuilder()
18
+ .setCustomId('a4d:session:stop')
19
+ .setLabel('Stop')
20
+ .setStyle(ButtonStyle.Danger)
21
+ .setDisabled(true), new ButtonBuilder()
22
+ .setCustomId('a4d:session:archive')
23
+ .setLabel('Archive')
24
+ .setStyle(ButtonStyle.Secondary)
25
+ .setDisabled(true));
26
+ }
27
+ /**
28
+ * Handle the Stop button for a session channel.
29
+ */
30
+ export async function handleSessionStop(interaction) {
31
+ const channelId = interaction.channelId;
32
+ const session = sessionManager.getSession(channelId);
33
+ if (!session) {
34
+ await interaction.reply({ content: 'No active session in this channel.', ephemeral: true });
35
+ return;
36
+ }
37
+ await interaction.deferUpdate();
38
+ sessionManager.stopSession(channelId);
39
+ removeSessionFromGuild(session.guildId, channelId);
40
+ // Update the pinned status embed
41
+ const textChannel = interaction.channel;
42
+ const statusMsg = await findStatusMessage(textChannel);
43
+ if (statusMsg) {
44
+ const updatedEmbed = buildStatusEmbed({
45
+ status: 'Session Stopped',
46
+ color: COLORS.STOPPED,
47
+ cwd: statusMsg.embeds[0]?.fields.find((f) => f.name === 'Directory')?.value ?? session.cwd,
48
+ model: statusMsg.embeds[0]?.fields.find((f) => f.name === 'Model')?.value ?? 'opus',
49
+ sessionId: session.sessionId || 'pending',
50
+ costUsd: session.totalCostUsd,
51
+ startedAt: session.createdAt,
52
+ });
53
+ await statusMsg.edit({ embeds: [updatedEmbed], components: [buildDisabledControlRow()] });
54
+ }
55
+ await textChannel.send('Session stopped by user.');
56
+ }
57
+ /**
58
+ * Handle the Archive button for a session channel.
59
+ */
60
+ export async function handleSessionArchive(interaction) {
61
+ const channelId = interaction.channelId;
62
+ const session = sessionManager.getSession(channelId);
63
+ await interaction.deferUpdate();
64
+ // Stop session if it is still running
65
+ if (session) {
66
+ if (session.state !== 'stopped' && session.state !== 'archived') {
67
+ sessionManager.stopSession(channelId);
68
+ }
69
+ removeSessionFromGuild(session.guildId, channelId);
70
+ }
71
+ const textChannel = interaction.channel;
72
+ // Update the pinned status embed
73
+ const statusMsg = await findStatusMessage(textChannel);
74
+ if (statusMsg) {
75
+ const updatedEmbed = buildStatusEmbed({
76
+ status: 'Session Archived',
77
+ color: COLORS.ARCHIVED,
78
+ cwd: statusMsg.embeds[0]?.fields.find((f) => f.name === 'Directory')?.value ?? session?.cwd ?? 'unknown',
79
+ model: statusMsg.embeds[0]?.fields.find((f) => f.name === 'Model')?.value ?? 'opus',
80
+ sessionId: session?.sessionId || statusMsg.embeds[0]?.fields.find((f) => f.name === 'Session ID')?.value || 'unknown',
81
+ costUsd: session?.totalCostUsd ?? 0,
82
+ startedAt: session?.createdAt ?? statusMsg.embeds[0]?.fields.find((f) => f.name === 'Started')?.value ?? 'unknown',
83
+ });
84
+ await statusMsg.edit({ embeds: [updatedEmbed], components: [buildDisabledControlRow()] });
85
+ }
86
+ // Set channel to read-only: deny SendMessages for @everyone
87
+ const guild = interaction.guild;
88
+ if (guild) {
89
+ await textChannel.permissionOverwrites.edit(guild.roles.everyone, {
90
+ SendMessages: false,
91
+ });
92
+ }
93
+ await textChannel.send('Session archived. This channel is now read-only.');
94
+ }
95
+ //# sourceMappingURL=sessionControls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionControls.js","sourceRoot":"","sources":["../../src/interactions/sessionControls.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Client } from 'discord.js';
2
+ export declare function getAndClearTurnThreads(channelId: string): string[];
3
+ export declare function setupEventHandlers(client: Client): void;
@@ -0,0 +1,209 @@
1
+ import { sessionManager } from './sessionManager.js';
2
+ import { chunkMessage } from '../formatters/chunker.js';
3
+ import { buildStatusEmbed, COLORS } from '../formatters/embedBuilder.js';
4
+ import { StreamHandler } from './streamHandler.js';
5
+ import { ToolProgressHandler } from './toolProgress.js';
6
+ import { formatThreadName, formatToolInput } from '../formatters/toolFormatter.js';
7
+ // Track active stream handlers per channel (keyed by "channelId:text" or "channelId:thinking")
8
+ const activeStreams = new Map();
9
+ // Track active tool progress handlers per channel
10
+ const activeToolProgress = new Map();
11
+ // Channels where StreamHandler.finalize() just sent the final text,
12
+ // so the 'assistant' handler should skip re-sending it.
13
+ const recentlyFinalized = new Set();
14
+ // Track tool-call threads per channel for the current turn
15
+ const turnThreads = new Map();
16
+ function trackThread(channelId, threadId) {
17
+ const threads = turnThreads.get(channelId) || [];
18
+ threads.push(threadId);
19
+ turnThreads.set(channelId, threads);
20
+ }
21
+ export function getAndClearTurnThreads(channelId) {
22
+ const threads = turnThreads.get(channelId) || [];
23
+ turnThreads.delete(channelId);
24
+ return threads;
25
+ }
26
+ /**
27
+ * Finalize and clean up all active stream handlers for a given channel.
28
+ */
29
+ async function finalizeStreamsForChannel(channelId) {
30
+ // Collect and remove from map FIRST to prevent re-entrant double-finalize
31
+ const toFinalize = [];
32
+ for (const [key, handler] of activeStreams) {
33
+ if (key.startsWith(channelId + ':')) {
34
+ toFinalize.push({ key, handler });
35
+ }
36
+ }
37
+ for (const { key } of toFinalize) {
38
+ activeStreams.delete(key);
39
+ }
40
+ let hadTextStream = false;
41
+ for (const { key, handler } of toFinalize) {
42
+ if (key.endsWith(':text'))
43
+ hadTextStream = true;
44
+ await handler.finalize();
45
+ }
46
+ if (hadTextStream) {
47
+ recentlyFinalized.add(channelId);
48
+ setTimeout(() => recentlyFinalized.delete(channelId), 5000);
49
+ }
50
+ return hadTextStream;
51
+ }
52
+ let handlersRegistered = false;
53
+ export function setupEventHandlers(client) {
54
+ if (handlersRegistered)
55
+ return;
56
+ handlersRegistered = true;
57
+ // --- Stream events (text / thinking deltas) ---
58
+ sessionManager.on('stream_event', async (channelId, msg) => {
59
+ const channel = client.channels.cache.get(channelId);
60
+ if (!channel?.isTextBased())
61
+ return;
62
+ const textChannel = channel;
63
+ // msg may wrap the event in different shapes depending on SDK version
64
+ const event = msg.event || msg;
65
+ if (event.type === 'content_block_start') {
66
+ const blockType = event.content_block?.type;
67
+ if (blockType === 'text' || blockType === 'thinking') {
68
+ const handler = new StreamHandler(textChannel, blockType);
69
+ activeStreams.set(`${channelId}:${blockType}`, handler);
70
+ }
71
+ }
72
+ if (event.type === 'content_block_delta') {
73
+ if (event.delta?.type === 'text_delta') {
74
+ const handler = activeStreams.get(`${channelId}:text`);
75
+ handler?.push(event.delta.text);
76
+ }
77
+ if (event.delta?.type === 'thinking_delta') {
78
+ const handler = activeStreams.get(`${channelId}:thinking`);
79
+ handler?.push(event.delta.thinking);
80
+ }
81
+ }
82
+ if (event.type === 'content_block_stop') {
83
+ // Finalize all handlers for this channel whose block just stopped
84
+ await finalizeStreamsForChannel(channelId);
85
+ }
86
+ });
87
+ // --- Tool progress events ---
88
+ sessionManager.on('tool_progress', async (channelId, msg) => {
89
+ const channel = client.channels.cache.get(channelId);
90
+ if (!channel?.isTextBased())
91
+ return;
92
+ const textChannel = channel;
93
+ const toolName = msg.tool_name || msg.name || 'tool';
94
+ if (!activeToolProgress.has(channelId)) {
95
+ const handler = new ToolProgressHandler(textChannel, toolName);
96
+ activeToolProgress.set(channelId, handler);
97
+ }
98
+ activeToolProgress.get(channelId).update();
99
+ });
100
+ // --- Assistant messages ---
101
+ sessionManager.on('assistant', async (channelId, msg) => {
102
+ const channel = client.channels.cache.get(channelId);
103
+ if (!channel?.isTextBased())
104
+ return;
105
+ const textChannel = channel;
106
+ if (msg.message?.content) {
107
+ const contentBlocks = msg.message.content;
108
+ // Text blocks are handled by StreamHandler via stream_event.
109
+ // Only send text here as a fallback if NO stream handler was active.
110
+ const hadStream = recentlyFinalized.has(channelId) ||
111
+ activeStreams.has(`${channelId}:text`);
112
+ if (!hadStream) {
113
+ for (const block of contentBlocks) {
114
+ if (block.type === 'text' && block.text) {
115
+ const chunks = chunkMessage(block.text);
116
+ for (const chunk of chunks) {
117
+ await textChannel.send(chunk);
118
+ }
119
+ }
120
+ }
121
+ }
122
+ // Handle tool_use blocks -- create threads for each
123
+ const toolUseBlocks = contentBlocks.filter((b) => b.type === 'tool_use');
124
+ if (toolUseBlocks.length > 0) {
125
+ if (toolUseBlocks.length > 1) {
126
+ await textChannel.send(`\uD83D\uDD27 Executing ${toolUseBlocks.length} tool calls...`);
127
+ }
128
+ for (const block of toolUseBlocks) {
129
+ const threadName = formatThreadName(block.name || 'unknown', block.input || {});
130
+ const thread = await textChannel.threads.create({
131
+ name: threadName,
132
+ autoArchiveDuration: 60,
133
+ reason: `A4D tool call: ${block.name || 'unknown'}`,
134
+ });
135
+ const inputText = formatToolInput(block.name || 'unknown', block.input || {});
136
+ await thread.send(inputText);
137
+ trackThread(channelId, thread.id);
138
+ }
139
+ }
140
+ }
141
+ });
142
+ // --- Result events ---
143
+ sessionManager.on('result', async (channelId, _msg) => {
144
+ // Clean up any lingering stream handlers
145
+ await finalizeStreamsForChannel(channelId);
146
+ // Clean up any lingering tool progress handlers
147
+ const toolHandler = activeToolProgress.get(channelId);
148
+ if (toolHandler) {
149
+ await toolHandler.finalize();
150
+ activeToolProgress.delete(channelId);
151
+ }
152
+ // Remove hourglass, add checkmark on the last user message
153
+ const channel = client.channels.cache.get(channelId);
154
+ if (!channel?.isTextBased())
155
+ return;
156
+ const session = sessionManager.getSession(channelId);
157
+ if (!session)
158
+ return;
159
+ const textChannel = channel;
160
+ // Update the pinned status embed with new cost
161
+ try {
162
+ const pinned = await textChannel.messages.fetchPinned();
163
+ const statusMsg = pinned.find((m) => m.author.id === client.user?.id && m.embeds.length > 0);
164
+ if (statusMsg) {
165
+ const embed = statusMsg.embeds[0];
166
+ const updatedEmbed = buildStatusEmbed({
167
+ status: 'Session Active',
168
+ color: COLORS.IDLE,
169
+ cwd: embed?.fields.find((f) => f.name === 'Directory')?.value ?? session.cwd,
170
+ model: embed?.fields.find((f) => f.name === 'Model')?.value ?? 'opus',
171
+ sessionId: session.sessionId || 'pending',
172
+ costUsd: session.totalCostUsd,
173
+ startedAt: session.createdAt,
174
+ });
175
+ await statusMsg.edit({ embeds: [updatedEmbed] });
176
+ }
177
+ }
178
+ catch {
179
+ // Status embed update is best-effort
180
+ }
181
+ // Find the most recent user message to update reactions
182
+ try {
183
+ const messages = await textChannel.messages.fetch({ limit: 20 });
184
+ const userMsg = messages.find((m) => !m.author.bot && m.reactions.cache.has('\u23f3'));
185
+ if (userMsg) {
186
+ await userMsg.reactions.cache.get('\u23f3')?.users.remove(client.user.id).catch(() => { });
187
+ await userMsg.react('\u2705').catch(() => { });
188
+ }
189
+ }
190
+ catch {
191
+ // Reaction cleanup is best-effort
192
+ }
193
+ });
194
+ sessionManager.on('error', async (channelId, _err) => {
195
+ // Clean up any active handlers on error
196
+ await finalizeStreamsForChannel(channelId);
197
+ const toolHandler = activeToolProgress.get(channelId);
198
+ if (toolHandler) {
199
+ await toolHandler.finalize();
200
+ activeToolProgress.delete(channelId);
201
+ }
202
+ const channel = client.channels.cache.get(channelId);
203
+ if (!channel?.isTextBased())
204
+ return;
205
+ const textChannel = channel;
206
+ await textChannel.send('An error occurred in this session. The session may have stopped.');
207
+ });
208
+ }
209
+ //# sourceMappingURL=eventHandler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eventHandler.js","sourceRoot":"","sources":["../../src/sessions/eventHandler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import { type CanUseTool, type Query, type SDKUserMessage } from '@anthropic-ai/claude-agent-sdk';
3
+ export type SessionState = 'idle' | 'running' | 'stopped' | 'archived';
4
+ export interface ActiveSession {
5
+ query: Query;
6
+ channelId: string;
7
+ guildId: string;
8
+ userId: string;
9
+ sessionId: string;
10
+ cwd: string;
11
+ state: SessionState;
12
+ totalCostUsd: number;
13
+ createdAt: string;
14
+ resolveNext: ((msg: SDKUserMessage) => void) | null;
15
+ abortController: AbortController;
16
+ }
17
+ declare class SessionManager extends EventEmitter {
18
+ private sessions;
19
+ createSession(guildId: string, userId: string, channelId: string, cwd: string, model?: string, canUseTool?: CanUseTool): ActiveSession;
20
+ resumeSession(guildId: string, userId: string, channelId: string, sessionId: string, cwd: string, model?: string, canUseTool?: CanUseTool): ActiveSession;
21
+ sendMessage(channelId: string, content: string): void;
22
+ getSession(channelId: string): ActiveSession | null;
23
+ stopSession(channelId: string): void;
24
+ removeSession(channelId: string): void;
25
+ getAllSessions(): ActiveSession[];
26
+ private _processEvents;
27
+ }
28
+ export declare const sessionManager: SessionManager;
29
+ export {};