@saccolabs/tars 1.7.7 → 1.8.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.
@@ -1,210 +0,0 @@
1
- import { Client, GatewayIntentBits, ChannelType, Partials } from 'discord.js';
2
- import logger from '../utils/logger.js';
3
- import { MessageFormatter } from './message-formatter.js';
4
- import { AttachmentProcessor } from '../utils/attachment-processor.js';
5
- /**
6
- * Discord bot wrapper for Tars
7
- */
8
- export class DiscordBot {
9
- config;
10
- client;
11
- supervisor;
12
- processor;
13
- lastChannelId = null;
14
- constructor(supervisor, config) {
15
- this.config = config;
16
- this.supervisor = supervisor;
17
- this.processor = new AttachmentProcessor(config);
18
- this.client = new Client({
19
- intents: [
20
- GatewayIntentBits.Guilds,
21
- GatewayIntentBits.GuildMessages,
22
- GatewayIntentBits.MessageContent,
23
- GatewayIntentBits.DirectMessages
24
- ],
25
- partials: [Partials.Channel, Partials.Message]
26
- });
27
- this.setupEventHandlers();
28
- }
29
- /**
30
- * Start the Discord bot
31
- */
32
- async start() {
33
- await this.client.login(this.config.discordToken);
34
- }
35
- /**
36
- * Stop the Discord bot
37
- */
38
- async stop() {
39
- this.client.destroy();
40
- }
41
- /**
42
- * Send a proactive notification to the primary contact
43
- */
44
- async notify(content) {
45
- if (!this.config.discordOwnerId || !content.trim())
46
- return;
47
- try {
48
- const user = await this.client.users.fetch(this.config.discordOwnerId);
49
- if (user) {
50
- const formatted = MessageFormatter.format(content);
51
- if (formatted.length > 8000) {
52
- const filePath = this.processor.saveResponse(content, 'md');
53
- await user.send({
54
- content: `🔔 **Task Notification** (Response too long, see attached):`,
55
- files: [filePath]
56
- });
57
- }
58
- else {
59
- const chunks = MessageFormatter.split(formatted);
60
- for (let i = 0; i < chunks.length; i++) {
61
- const prefix = i === 0 ? `🔔 **Task Notification:**\n` : ``;
62
- await user.send(prefix + chunks[i]);
63
- }
64
- }
65
- }
66
- }
67
- catch (e) {
68
- logger.error(`Failed to send proactive notification: ${e.message}`);
69
- }
70
- }
71
- /**
72
- * Setup event handlers
73
- */
74
- setupEventHandlers() {
75
- this.client.once('clientReady', (c) => {
76
- logger.info(`🚀 ${this.config.assistantName} online as ${c.user.tag}`);
77
- logger.info(`🧠 Gemini Model: ${this.config.geminiModel}`);
78
- if (this.config.discordOwnerId) {
79
- logger.info(`👤 Primary Contact ID: ${this.config.discordOwnerId}`);
80
- }
81
- else {
82
- logger.warn(`⚠️ No Primary Contact ID set. Will bind to the first user who sends a message.`);
83
- }
84
- });
85
- this.client.on('messageCreate', this.handleMessage.bind(this));
86
- }
87
- /**
88
- * Handle incoming messages
89
- */
90
- async handleMessage(message) {
91
- if (message.author.bot)
92
- return;
93
- const userPrompt = this.extractPrompt(message);
94
- // If null, message wasn't for us. If empty string, check for attachments.
95
- if (userPrompt === null)
96
- return;
97
- // Auto-Bind on first interaction if not set
98
- if (!this.config.discordOwnerId) {
99
- this.config.discordOwnerId = message.author.id;
100
- this.config.saveSettings();
101
- logger.info(`🔒 Automatically bound Primary Contact to user: ${message.author.id}`);
102
- await message.reply(`🔒 **System Alert:** I have permanently bound my background notification channel to your account. I will send proactive alerts here.`);
103
- }
104
- if (!userPrompt && message.attachments.size === 0)
105
- return;
106
- logger.info(`Received request from ${message.author.tag}: "${userPrompt || '[Attachment Only]'}"`);
107
- // Handle Attachments
108
- const attachments = [];
109
- let attachmentContextText = '';
110
- if (message.attachments.size > 0) {
111
- for (const [id, attachment] of message.attachments) {
112
- try {
113
- const filePath = await this.processor.download(attachment);
114
- if (attachment.contentType) {
115
- attachments.push({
116
- path: filePath,
117
- mimeType: attachment.contentType
118
- });
119
- }
120
- attachmentContextText += `\n[User attached file (${attachment.contentType}): ${filePath}]`;
121
- }
122
- catch (err) {
123
- logger.error(`Failed to download attachment: ${err.message}`);
124
- await message.reply(`⚠️ Failed to download ${attachment.name}: ${err.message}`);
125
- }
126
- }
127
- }
128
- const fullPrompt = `${userPrompt}${attachmentContextText}`.trim();
129
- if (!fullPrompt && attachments.length === 0)
130
- return;
131
- let typingInterval = null;
132
- // Start typing indicator loop (Discord typing status lasts 10s)
133
- if ('sendTyping' in message.channel) {
134
- // Initial typing
135
- await message.channel.sendTyping().catch(() => { });
136
- // Loop every 9s to keep it active
137
- typingInterval = setInterval(() => {
138
- if ('sendTyping' in message.channel) {
139
- message.channel.sendTyping().catch(() => { });
140
- }
141
- }, 9000);
142
- }
143
- try {
144
- let fullResponse = '';
145
- await this.supervisor.run(fullPrompt, async (event) => {
146
- if ((event.type === 'text' || event.type === 'message') &&
147
- event.content &&
148
- event.role !== 'user') {
149
- fullResponse += event.content;
150
- }
151
- else if (event.type === 'error') {
152
- await message.reply(`❌ **Error:** ${event.error}`);
153
- }
154
- else if (event.type === 'done') {
155
- if (fullResponse.trim()) {
156
- const formatted = MessageFormatter.format(fullResponse);
157
- if (formatted.length > 8000) {
158
- const filePath = this.processor.saveResponse(fullResponse, 'md');
159
- await message.reply({
160
- content: `📄 **Response too long** (${formatted.length} chars). See attached file:`,
161
- files: [filePath]
162
- });
163
- }
164
- else {
165
- const chunks = MessageFormatter.split(formatted);
166
- for (const chunk of chunks) {
167
- await message.reply(chunk);
168
- }
169
- }
170
- }
171
- else {
172
- logger.warn('Gemini returned an empty response.');
173
- }
174
- }
175
- }, undefined, attachments);
176
- }
177
- catch (error) {
178
- logger.error(`Discord handling error: ${error.message}`);
179
- await message.reply(`❌ **Supervisor Error:** ${error.message}`);
180
- }
181
- finally {
182
- if (typingInterval)
183
- clearInterval(typingInterval);
184
- }
185
- }
186
- /**
187
- * Extract prompt and handle prefix
188
- */
189
- extractPrompt(message) {
190
- const isDM = message.channel.type === ChannelType.DM;
191
- const isMentioned = this.client.user && message.mentions.has(this.client.user);
192
- const customPrefix = `!${this.config.assistantName.toLowerCase()}`;
193
- const hasCustomCommand = message.content.toLowerCase().startsWith(customPrefix);
194
- const hasLegacyCommand = message.content.toLowerCase().startsWith('!tars');
195
- if (!isDM && !isMentioned && !hasCustomCommand && !hasLegacyCommand)
196
- return null;
197
- let prompt = message.content;
198
- if (hasCustomCommand) {
199
- prompt = prompt.substring(customPrefix.length);
200
- }
201
- else if (hasLegacyCommand) {
202
- prompt = prompt.substring(6); // length of '!tars'
203
- }
204
- if (isMentioned && this.client.user) {
205
- prompt = prompt.replace(new RegExp(`<@!?${this.client.user.id}>`, 'g'), '');
206
- }
207
- return prompt.trim();
208
- }
209
- }
210
- //# sourceMappingURL=discord-bot.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"discord-bot.js","sourceRoot":"","sources":["../../src/discord/discord-bot.ts"],"names":[],"mappings":"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"}
@@ -1,95 +0,0 @@
1
- /**
2
- * Discord Message Formatter
3
- *
4
- * Transforms Gemini CLI output (GitHub Flavored Markdown)
5
- * into Discord-compatible formatting.
6
- *
7
- * Discord supports:
8
- * - Bold: **text**
9
- * - Italic: *text* or _text_
10
- * - Underline: __text__
11
- * - Strikethrough: ~~text~~
12
- * - Code inline: `text`
13
- * - Code block: ```lang\ncode\n```
14
- * - Blockquotes: > text
15
- * - Headers: # (only #, ##, ###)
16
- *
17
- * Discord does NOT support:
18
- * - Markdown tables (we instruct the LLM to avoid these)
19
- * - #### or deeper headers
20
- * - Small text (-#)
21
- */
22
- export declare class MessageFormatter {
23
- private static readonly MAX_MESSAGE_LENGTH;
24
- /**
25
- * Format text for Discord
26
- */
27
- static format(text: string): string;
28
- /**
29
- * Fix critical spacing issues from LLM output
30
- * These are the most common formatting bugs that break Discord rendering
31
- */
32
- private static fixSpacing;
33
- /**
34
- * Fix broken asterisks patterns
35
- * Only fixes obviously broken patterns, avoids aggressive matching
36
- */
37
- private static fixAsterisks;
38
- /**
39
- * Normalize bullet points for Discord
40
- */
41
- private static normalizeBullets;
42
- /**
43
- * Normalize markdown headers to Discord-friendly format
44
- * Discord supports #, ##, ### natively now.
45
- */
46
- private static normalizeHeaders;
47
- /**
48
- * Fix blockquote formatting
49
- */
50
- private static fixBlockquotes;
51
- /**
52
- * Detect and wrap JSON-like content in code blocks
53
- */
54
- private static formatJsonBlocks;
55
- /**
56
- * Strip markdown tables - they don't render well on mobile Discord
57
- * This is a fallback; the LLM should be instructed not to generate tables
58
- */
59
- private static stripTables;
60
- /**
61
- * Fix small text markers (-#) that Discord doesn't support
62
- */
63
- private static fixSmallText;
64
- /**
65
- * Split long messages into Discord-safe chunks intelligently
66
- * Respects semantic boundaries: headers, code blocks, paragraphs
67
- */
68
- static split(text: string, maxLength?: number): string[];
69
- /**
70
- * Find the optimal split point respecting semantic boundaries
71
- * Priority: Header > Code block boundary > Paragraph > Sentence > Hard cut
72
- */
73
- private static findSemanticSplitPoint;
74
- /**
75
- * Format and split in one operation
76
- * Ensures summary line (first line with actionable emoji) stays at the top
77
- */
78
- static formatAndSplit(text: string): string[];
79
- /**
80
- * Extract the summary line from the beginning of formatted text
81
- * Summary lines start with key actionable emojis: 🎯 ⚖️ 📊 ⚠️ ✅ ❓
82
- */
83
- private static extractSummaryLine;
84
- /**
85
- * Parse markdown into sections based on headers (##)
86
- */
87
- static parseSections(text: string): {
88
- title: string;
89
- content: string;
90
- }[];
91
- /**
92
- * Format a data object as a clean Discord-friendly list
93
- */
94
- static formatDataAsEmbed(title: string, data: Record<string, unknown>): string;
95
- }