wierd one.
This commit is contained in:
parent
28d280d37a
commit
e91aeabdeb
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"WebFetch(domain:github.com)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
}
|
||||
}
|
||||
|
|
@ -254,6 +254,23 @@ class TelegramChannel extends NotificationChannel {
|
|||
}
|
||||
};
|
||||
|
||||
// Check if we need to use multi-message sending based on original response length
|
||||
const fullResponse = notification.metadata?.claudeResponse || '';
|
||||
const shouldUseMultiMessage = fullResponse.length > 1500; // Lower threshold for better experience
|
||||
|
||||
if (shouldUseMultiMessage) {
|
||||
console.log(`[DEBUG] Using multi-message sending (original response: ${fullResponse.length} chars)`);
|
||||
const success = await this._sendMultipleMessages(notification, sessionId, token, chatId, buttons);
|
||||
if (!success) {
|
||||
// Clean up failed session
|
||||
await this._removeSession(sessionId);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
// Use single message sending
|
||||
console.log(`[DEBUG] Using single message sending (${messageText.length} chars)`);
|
||||
|
||||
try {
|
||||
// Log the message details for debugging
|
||||
console.log(`[DEBUG] =====================================================`);
|
||||
|
|
@ -446,6 +463,229 @@ class TelegramChannel extends NotificationChannel {
|
|||
return `${emoji} Claude Task ${status}\nToken: ${token}\nUse: /cmd ${token} <command>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send multiple messages for long responses
|
||||
* @param {Object} notification - Notification data
|
||||
* @param {string} sessionId - Session ID
|
||||
* @param {string} token - Session token
|
||||
* @param {string} chatId - Telegram chat ID
|
||||
* @param {Array} buttons - Inline keyboard buttons
|
||||
* @returns {Promise<boolean>} - Success status
|
||||
*/
|
||||
async _sendMultipleMessages(notification, sessionId, token, chatId, buttons) {
|
||||
const type = notification.type;
|
||||
const emoji = type === 'completed' ? '✅' : '⏳';
|
||||
const status = type === 'completed' ? 'Completed' : 'Waiting for Input';
|
||||
|
||||
try {
|
||||
const fullResponse = notification.metadata?.claudeResponse || '';
|
||||
const userQuestion = notification.metadata?.userQuestion || '';
|
||||
|
||||
console.log(`[DEBUG] Multi-message sending: Response length ${fullResponse.length} chars`);
|
||||
|
||||
// Split the response content into optimal chunks
|
||||
const responseChunks = this._splitTextIntoChunks(fullResponse, 3200); // Slightly smaller for better formatting
|
||||
const totalParts = Math.max(1, responseChunks.length);
|
||||
|
||||
console.log(`[DEBUG] Split response into ${totalParts} parts`);
|
||||
|
||||
// Create the first message with basic info
|
||||
let firstMessage = `${emoji} *Claude Task ${status}* \\[1/${totalParts}\\]\n`;
|
||||
firstMessage += `*Project:* ${this._escapeMarkdown(notification.project || 'Unknown')}\n`;
|
||||
firstMessage += `*Token:* \`${token}\`\n\n`;
|
||||
|
||||
if (userQuestion) {
|
||||
const question = userQuestion.substring(0, 150);
|
||||
firstMessage += `📝 *Question:* ${this._escapeMarkdown(question)}${question.length < userQuestion.length ? '...' : ''}\n\n`;
|
||||
}
|
||||
|
||||
// Add response header and first part
|
||||
firstMessage += `🤖 *Claude Response:*\n`;
|
||||
|
||||
// Calculate space for first response part
|
||||
const commandInstructions = `\n\n💬 *Commands:* \`/cmd ${token} <your command>\``;
|
||||
const baseLength = firstMessage.length + commandInstructions.length;
|
||||
const maxFirstResponseLength = 3800 - baseLength;
|
||||
|
||||
// Add first part of response
|
||||
let firstResponsePart = '';
|
||||
if (responseChunks.length > 0) {
|
||||
firstResponsePart = responseChunks[0];
|
||||
if (firstResponsePart.length > maxFirstResponseLength) {
|
||||
// Further truncate first part if needed
|
||||
firstResponsePart = firstResponsePart.substring(0, maxFirstResponseLength - 50) + '...';
|
||||
}
|
||||
}
|
||||
|
||||
firstMessage += this._escapeMarkdown(firstResponsePart);
|
||||
|
||||
// Add continuation indicator
|
||||
if (totalParts > 1) {
|
||||
firstMessage += `\n\n*\\[Continued in next message\\]*`;
|
||||
}
|
||||
|
||||
firstMessage += commandInstructions;
|
||||
|
||||
// Send the first message
|
||||
console.log(`[DEBUG] Sending first message (${firstMessage.length} chars)`);
|
||||
const firstMessageResult = await this._sendSingleMessage(chatId, firstMessage, buttons, sessionId, 'Markdown');
|
||||
|
||||
if (!firstMessageResult.success) {
|
||||
console.log(`[DEBUG] Failed to send first message, trying fallback`);
|
||||
// Try plain text fallback for first message
|
||||
const plainFirstMessage = firstMessage
|
||||
.replace(/\*([^*]+)\*/g, '$1') // Remove bold
|
||||
.replace(/\`([^`]+)\`/g, '$1') // Remove code
|
||||
.replace(/\\(.)/g, '$1'); // Remove escapes
|
||||
|
||||
const fallbackResult = await this._sendSingleMessage(chatId, plainFirstMessage, buttons, sessionId, null);
|
||||
if (!fallbackResult.success) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const firstMessageId = firstMessageResult.messageId;
|
||||
let successfulParts = 1;
|
||||
|
||||
// Send remaining parts
|
||||
for (let i = 1; i < responseChunks.length; i++) {
|
||||
const partNumber = i + 1;
|
||||
|
||||
let continuationMessage = `*\\[Part ${partNumber}/${totalParts}\\]*\n\n`;
|
||||
continuationMessage += this._escapeMarkdown(responseChunks[i]);
|
||||
|
||||
console.log(`[DEBUG] Sending part ${partNumber}/${totalParts} (${continuationMessage.length} chars)`);
|
||||
|
||||
const success = await this._sendSingleMessage(
|
||||
chatId,
|
||||
continuationMessage,
|
||||
null, // No buttons for continuation messages
|
||||
sessionId,
|
||||
'Markdown',
|
||||
firstMessageId // Reply to first message to create thread
|
||||
);
|
||||
|
||||
if (success.success) {
|
||||
successfulParts++;
|
||||
} else {
|
||||
console.log(`[DEBUG] Failed to send part ${partNumber}/${totalParts}, trying plain text`);
|
||||
// Try plain text fallback
|
||||
const plainMessage = continuationMessage.replace(/\*([^*]+)\*/g, '$1').replace(/\\(.)/g, '$1');
|
||||
const fallbackSuccess = await this._sendSingleMessage(
|
||||
chatId, plainMessage, null, sessionId, null, firstMessageId
|
||||
);
|
||||
if (fallbackSuccess.success) {
|
||||
successfulParts++;
|
||||
}
|
||||
}
|
||||
|
||||
// Add delay to prevent rate limiting
|
||||
await new Promise(resolve => setTimeout(resolve, 800));
|
||||
}
|
||||
|
||||
console.log(`[DEBUG] Multi-message sending completed: ${successfulParts}/${totalParts} parts sent successfully`);
|
||||
|
||||
// Send a final summary if not all parts were sent
|
||||
if (successfulParts < totalParts) {
|
||||
const summaryMessage = `⚠️ *Message Status:* ${successfulParts}/${totalParts} parts sent successfully\n\nSome parts may have been skipped due to formatting issues\\.`;
|
||||
await this._sendSingleMessage(chatId, summaryMessage, null, sessionId, 'Markdown', firstMessageId);
|
||||
}
|
||||
|
||||
return successfulParts > 0; // Success if at least first part was sent
|
||||
|
||||
} catch (error) {
|
||||
console.log(`[DEBUG] Error in _sendMultipleMessages: ${error.message}`);
|
||||
console.log(`[DEBUG] Stack trace:`, error.stack);
|
||||
this.logger.error('Failed to send multiple messages:', error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a single message with enhanced error handling
|
||||
* @param {string} chatId - Telegram chat ID
|
||||
* @param {string} messageText - Message text
|
||||
* @param {Array} buttons - Inline keyboard buttons (optional)
|
||||
* @param {string} sessionId - Session ID for logging
|
||||
* @param {string} parseMode - Parse mode (optional)
|
||||
* @param {number} replyToMessageId - Reply to message ID (optional)
|
||||
* @returns {Promise<Object>} - {success: boolean, messageId: number}
|
||||
*/
|
||||
async _sendSingleMessage(chatId, messageText, buttons = null, sessionId = 'unknown', parseMode = null, replyToMessageId = null) {
|
||||
const requestData = {
|
||||
chat_id: chatId,
|
||||
text: messageText
|
||||
};
|
||||
|
||||
if (parseMode) {
|
||||
requestData.parse_mode = parseMode;
|
||||
}
|
||||
|
||||
if (buttons) {
|
||||
requestData.reply_markup = {
|
||||
inline_keyboard: buttons
|
||||
};
|
||||
}
|
||||
|
||||
if (replyToMessageId) {
|
||||
requestData.reply_to_message_id = replyToMessageId;
|
||||
}
|
||||
|
||||
try {
|
||||
console.log(`[DEBUG] Sending message (${messageText.length} chars) to chat ${chatId}`);
|
||||
|
||||
const response = await axios.post(
|
||||
`${this.apiBaseUrl}/bot${this.config.botToken}/sendMessage`,
|
||||
requestData,
|
||||
{
|
||||
...this._getNetworkOptions(),
|
||||
timeout: 15000
|
||||
}
|
||||
);
|
||||
|
||||
const messageId = response.data.result.message_id;
|
||||
console.log(`[DEBUG] ✅ Message sent successfully, ID: ${messageId}`);
|
||||
|
||||
return { success: true, messageId: messageId };
|
||||
|
||||
} catch (error) {
|
||||
const errorData = error.response?.data;
|
||||
const errorMessage = errorData?.description || error.message;
|
||||
const errorCode = errorData?.error_code;
|
||||
|
||||
console.log(`[DEBUG] ❌ Failed to send message: ${errorCode} - ${errorMessage}`);
|
||||
|
||||
// Try fallback without formatting
|
||||
if (parseMode && errorCode === 400) {
|
||||
console.log(`[DEBUG] Retrying without parse mode...`);
|
||||
try {
|
||||
const fallbackData = { ...requestData };
|
||||
delete fallbackData.parse_mode;
|
||||
fallbackData.text = this._createSafeText(messageText);
|
||||
|
||||
const fallbackResponse = await axios.post(
|
||||
`${this.apiBaseUrl}/bot${this.config.botToken}/sendMessage`,
|
||||
fallbackData,
|
||||
{
|
||||
...this._getNetworkOptions(),
|
||||
timeout: 15000
|
||||
}
|
||||
);
|
||||
|
||||
const fallbackMessageId = fallbackResponse.data.result.message_id;
|
||||
console.log(`[DEBUG] ✅ Fallback message sent successfully, ID: ${fallbackMessageId}`);
|
||||
|
||||
return { success: true, messageId: fallbackMessageId };
|
||||
|
||||
} catch (fallbackError) {
|
||||
console.log(`[DEBUG] ❌ Fallback also failed: ${fallbackError.response?.data?.description || fallbackError.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
return { success: false, messageId: null };
|
||||
}
|
||||
}
|
||||
|
||||
async _createSession(sessionId, notification, token) {
|
||||
const session = {
|
||||
id: sessionId,
|
||||
|
|
|
|||
|
|
@ -907,5 +907,95 @@
|
|||
"sessionId": "197cd53a-4a64-42a9-8262-8e3b429f08b8",
|
||||
"tmuxSession": "claude-session",
|
||||
"description": "completed - ecllipse"
|
||||
},
|
||||
"TJ5VIF92": {
|
||||
"type": "pty",
|
||||
"createdAt": 1756992584,
|
||||
"expiresAt": 1757078984,
|
||||
"cwd": "/home/lsamc/.local/src/Claude-Code-Remote",
|
||||
"sessionId": "019ab1c3-bde2-4d00-b21e-23167fe52c16",
|
||||
"tmuxSession": "claude-session",
|
||||
"description": "completed - Claude-Code-Remote"
|
||||
},
|
||||
"J56R0D84": {
|
||||
"type": "pty",
|
||||
"createdAt": 1757031172,
|
||||
"expiresAt": 1757117572,
|
||||
"cwd": "/home/lsamc/.local/src/Claude-Code-Remote",
|
||||
"sessionId": "cb99ffc3-ee1f-44be-9438-666e9b7fcd54",
|
||||
"tmuxSession": "claude-session",
|
||||
"description": "completed - Claude-Code-Remote"
|
||||
},
|
||||
"2A26K43N": {
|
||||
"type": "pty",
|
||||
"createdAt": 1757031544,
|
||||
"expiresAt": 1757117944,
|
||||
"cwd": "/home/lsamc/.local/src/Claude-Code-Remote",
|
||||
"sessionId": "f930e41f-eb86-49d0-85ab-2fcc06aa27e9",
|
||||
"tmuxSession": "claude-session",
|
||||
"description": "completed - Claude-Code-Remote"
|
||||
},
|
||||
"30W8P8Y1": {
|
||||
"type": "pty",
|
||||
"createdAt": 1757031609,
|
||||
"expiresAt": 1757118009,
|
||||
"cwd": "/home/lsamc/develop/ecllipse",
|
||||
"sessionId": "945a07d5-5403-433e-8f90-951e639e0c0c",
|
||||
"tmuxSession": "claude-session",
|
||||
"description": "completed - ecllipse"
|
||||
},
|
||||
"ZNE082NM": {
|
||||
"type": "pty",
|
||||
"createdAt": 1757031634,
|
||||
"expiresAt": 1757118034,
|
||||
"cwd": "/home/lsamc/develop/ecllipse",
|
||||
"sessionId": "c30b0f1f-1d4f-438f-a7c8-81fb9d8ddd7c",
|
||||
"tmuxSession": "claude-session",
|
||||
"description": "completed - ecllipse"
|
||||
},
|
||||
"G73CZLO4": {
|
||||
"type": "pty",
|
||||
"createdAt": 1757031693,
|
||||
"expiresAt": 1757118093,
|
||||
"cwd": "/home/lsamc/develop/ecllipse",
|
||||
"sessionId": "c4d9c060-83f4-4fc1-b83a-41e3045df66a",
|
||||
"tmuxSession": "claude-session",
|
||||
"description": "completed - ecllipse"
|
||||
},
|
||||
"6GAO026F": {
|
||||
"type": "pty",
|
||||
"createdAt": 1757031822,
|
||||
"expiresAt": 1757118222,
|
||||
"cwd": "/home/lsamc/develop/ecllipse",
|
||||
"sessionId": "6feee05d-cdf5-41ab-9eb7-3e2b39be97cd",
|
||||
"tmuxSession": "claude-session",
|
||||
"description": "completed - ecllipse"
|
||||
},
|
||||
"0ZDCUMDH": {
|
||||
"type": "pty",
|
||||
"createdAt": 1757031847,
|
||||
"expiresAt": 1757118247,
|
||||
"cwd": "/home/lsamc/.local/src/Claude-Code-Remote",
|
||||
"sessionId": "6b330566-d096-4862-9afe-aa32e038e9e9",
|
||||
"tmuxSession": "claude-session",
|
||||
"description": "completed - Claude-Code-Remote"
|
||||
},
|
||||
"RJMOLNH2": {
|
||||
"type": "pty",
|
||||
"createdAt": 1757032064,
|
||||
"expiresAt": 1757118464,
|
||||
"cwd": "/home/lsamc/.local/src/Claude-Code-Remote",
|
||||
"sessionId": "f394b1b0-9ea2-4106-b27a-75842319ed31",
|
||||
"tmuxSession": "claude-session",
|
||||
"description": "completed - Claude-Code-Remote"
|
||||
},
|
||||
"E1E1RJD2": {
|
||||
"type": "pty",
|
||||
"createdAt": 1757032743,
|
||||
"expiresAt": 1757119143,
|
||||
"cwd": "/home/lsamc/.local/src/Claude-Code-Remote",
|
||||
"sessionId": "f0acac1d-9b96-4baa-8622-cb030c300767",
|
||||
"tmuxSession": "claude-session",
|
||||
"description": "completed - Claude-Code-Remote"
|
||||
}
|
||||
}
|
||||
|
|
@ -613,6 +613,12 @@ class TmuxMonitor extends EventEmitter {
|
|||
console.log(`[DEBUG] Total lines in buffer: ${lines.length}`);
|
||||
console.log(`[DEBUG] Last 5 lines:`, lines.slice(-5));
|
||||
|
||||
// Save full buffer for debugging (first 50 and last 50 lines)
|
||||
if (lines.length > 10) {
|
||||
console.log(`[DEBUG] Buffer preview - First 10 lines:`, lines.slice(0, 10));
|
||||
console.log(`[DEBUG] Buffer preview - Last 10 lines:`, lines.slice(-10));
|
||||
}
|
||||
|
||||
let userQuestion = '';
|
||||
let claudeResponse = '';
|
||||
let responseLines = [];
|
||||
|
|
@ -636,13 +642,17 @@ class TmuxMonitor extends EventEmitter {
|
|||
for (let i = lastUserIndex + 1; i < lines.length; i++) {
|
||||
const line = lines[i].trim();
|
||||
|
||||
// Skip empty lines and system lines
|
||||
// Skip empty lines and system lines, but be more selective
|
||||
if (!line ||
|
||||
line.includes('? for shortcuts') ||
|
||||
line.match(/^[╭╰│─]+$/) ||
|
||||
line.startsWith('$') ||
|
||||
line.startsWith('#') ||
|
||||
line.startsWith('[')) {
|
||||
line.match(/^\[.*\]$/) || // System messages in brackets
|
||||
line.includes('(esc to interrupt)') || // Claude Code interrupt message
|
||||
line.match(/^\+\s*Cascading/) || // Cascading status message
|
||||
line.match(/^_{3,}$/)) { // Multiple underscores (display artifacts)
|
||||
console.log(`[DEBUG] Skipping system line: ${line}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -664,29 +674,87 @@ class TmuxMonitor extends EventEmitter {
|
|||
responseLines = [line.startsWith('⏺ ') ? line.substring(2).trim() : line];
|
||||
console.log(`[DEBUG] Found response start at line ${i}: ${line}`);
|
||||
} else if (foundResponseStart) {
|
||||
// Stop if we hit another user input or prompt
|
||||
if (line.startsWith('> ') || line.includes('│ > │') || line.startsWith('╭')) {
|
||||
console.log(`[DEBUG] Stopping response collection at line ${i}: ${line}`);
|
||||
// Only stop for clear prompt indicators, not for special characters or formatting
|
||||
if (line.startsWith('> ') && line.length > 2) { // New user input
|
||||
console.log(`[DEBUG] Stopping response collection at new user input: ${line}`);
|
||||
break;
|
||||
}
|
||||
// Stop at command prompt box start (new interaction)
|
||||
if (line.startsWith('╭') && line.includes('─')) {
|
||||
console.log(`[DEBUG] Stopping response collection at prompt box: ${line}`);
|
||||
break;
|
||||
}
|
||||
// Continue collecting response, ignoring formatting artifacts
|
||||
responseLines.push(line);
|
||||
console.log(`[DEBUG] Added response line ${responseLines.length}: ${line.substring(0, 50)}...`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: Look for any Claude response pattern in the last 20 lines
|
||||
// Fallback: Look for any Claude response pattern in a larger range
|
||||
if (responseLines.length === 0) {
|
||||
console.log(`[DEBUG] No response found after user input, trying fallback method`);
|
||||
const recentLines = lines.slice(-20);
|
||||
const recentLines = lines.slice(-40); // Increased range
|
||||
|
||||
let inFallbackResponse = false;
|
||||
for (let i = 0; i < recentLines.length; i++) {
|
||||
const line = recentLines[i].trim();
|
||||
|
||||
if (line.startsWith('⏺ ') ||
|
||||
(line.length > 10 && !line.startsWith('> ') && !line.includes('? for shortcuts'))) {
|
||||
responseLines.push(line.startsWith('⏺ ') ? line.substring(2).trim() : line);
|
||||
console.log(`[DEBUG] Fallback found response line: ${line}`);
|
||||
// Skip problematic lines even in fallback
|
||||
if (!line ||
|
||||
line.includes('(esc to interrupt)') ||
|
||||
line.match(/^\+\s*Cascading/) ||
|
||||
line.match(/^_{3,}$/) ||
|
||||
line.includes('? for shortcuts')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Look for Claude response indicators
|
||||
if (line.startsWith('⏺ ') ||
|
||||
(line.length > 5 &&
|
||||
!line.startsWith('> ') &&
|
||||
!line.startsWith('$') &&
|
||||
!line.startsWith('#') &&
|
||||
!line.match(/^\[.*\]$/))) {
|
||||
|
||||
const cleanLine = line.startsWith('⏺ ') ? line.substring(2).trim() : line;
|
||||
responseLines.push(cleanLine);
|
||||
inFallbackResponse = true;
|
||||
console.log(`[DEBUG] Fallback found response line: ${cleanLine.substring(0, 50)}...`);
|
||||
} else if (inFallbackResponse && line.length > 0) {
|
||||
// Continue collecting if we're in a response and line isn't a clear break
|
||||
responseLines.push(line);
|
||||
console.log(`[DEBUG] Fallback continued response: ${line.substring(0, 50)}...`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Super fallback: Get any meaningful content if still empty
|
||||
if (responseLines.length === 0) {
|
||||
console.log(`[DEBUG] Still no response, trying super fallback - scanning all meaningful lines`);
|
||||
const meaningfulLines = [];
|
||||
|
||||
for (let i = Math.max(0, lastUserIndex + 1); i < lines.length; i++) {
|
||||
const line = lines[i].trim();
|
||||
|
||||
// Collect any line that seems like content (not system/formatting)
|
||||
if (line.length > 5 &&
|
||||
!line.includes('? for shortcuts') &&
|
||||
!line.match(/^[╭╰│─\s]+$/) &&
|
||||
!line.includes('(esc to interrupt)') &&
|
||||
!line.match(/^\+\s*Cascading/) &&
|
||||
!line.match(/^_{3,}$/) &&
|
||||
!line.startsWith('> ') &&
|
||||
!line.startsWith('$') &&
|
||||
!line.startsWith('#')) {
|
||||
|
||||
meaningfulLines.push(line);
|
||||
}
|
||||
}
|
||||
|
||||
if (meaningfulLines.length > 0) {
|
||||
responseLines = meaningfulLines;
|
||||
console.log(`[DEBUG] Super fallback collected ${meaningfulLines.length} meaningful lines`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue