Spaces:
Build error
Build error
| import fs from 'fs'; | |
| import path from 'path'; | |
| import { fileURLToPath } from 'url'; | |
| import { dirname } from 'path'; | |
| import { handleMessage, pushNotification } from './agent.js'; | |
| import { bot } from './bot.js'; | |
| import { env } from './config.js'; | |
| const __filename = fileURLToPath(import.meta.url); | |
| const __dirname = dirname(__filename); | |
| // Interval in milliseconds (Default: 30 minutes) | |
| const HEARTBEAT_INTERVAL_MS = 30 * 60 * 1000; | |
| let heartbeatInterval: NodeJS.Timeout | null = null; | |
| const HEARTBEAT_FILE_PATH = path.join(__dirname, '../skills/HEARTBEAT.md'); | |
| export function startHeartbeat() { | |
| if (heartbeatInterval) { | |
| console.log('Heartbeat is already running.'); | |
| return; | |
| } | |
| console.log(`⏱️ Starting system heartbeat (Interval: ${HEARTBEAT_INTERVAL_MS / 1000 / 60} minutes)`); | |
| heartbeatInterval = setInterval(async () => { | |
| try { | |
| await triggerHeartbeat(); | |
| } catch (error) { | |
| console.error('Error during heartbeat execution:', error); | |
| } | |
| }, HEARTBEAT_INTERVAL_MS); | |
| } | |
| export function stopHeartbeat() { | |
| if (heartbeatInterval) { | |
| clearInterval(heartbeatInterval); | |
| heartbeatInterval = null; | |
| console.log('⏱️ System heartbeat stopped.'); | |
| } | |
| } | |
| export async function triggerHeartbeat(isManualTrigger = false) { | |
| if (isManualTrigger) { | |
| console.log('⏱️ Manual heartbeat triggered.'); | |
| } else { | |
| console.log('⏱️ Executing scheduled heartbeat...'); | |
| } | |
| let heartbeatContent = ''; | |
| // 1. Read the HEARTBEAT.md file | |
| try { | |
| if (fs.existsSync(HEARTBEAT_FILE_PATH)) { | |
| heartbeatContent = fs.readFileSync(HEARTBEAT_FILE_PATH, 'utf-8'); | |
| } else { | |
| console.log('No HEARTBEAT.md found in skills directory. Skipping heartbeat.'); | |
| return; | |
| } | |
| } catch (e) { | |
| console.error('Failed to read HEARTBEAT.md:', e); | |
| return; | |
| } | |
| // 2. Construct the invisible prompt | |
| const hiddenPrompt = `[SYSTEM HEARTBEAT WAKEUP]\nThis is an automated background heartbeat. Please review your instructions and act accordingly:\n\n${heartbeatContent}`; | |
| // 3. Invoke the agent loop directly (without triggering Telegram 'typing' directly from here) | |
| const response = await handleMessage(hiddenPrompt, undefined, undefined, true); // true = isBackground | |
| // 4. Evaluate response | |
| const cleanResponse = response.trim(); | |
| const strippedResponse = cleanResponse.replace(/\\/g, ''); | |
| if (strippedResponse.includes('HEARTBEAT_OK')) { | |
| console.log('⏱️ Heartbeat complete: No action required (HEARTBEAT_OK detected).'); | |
| // Do nothing, remain silent. | |
| } else { | |
| console.log('⏱️ Heartbeat generated active response. Notifying user.'); | |
| // The agent decided to say something or take an action. Forward it to Telegram AND the app! | |
| try { | |
| await bot.api.sendMessage(env.TELEGRAM_USER_ID, `*(Auto-Heartbeat)*\n\n${cleanResponse}`); | |
| pushNotification('heartbeat', cleanResponse); | |
| } catch (botErr) { | |
| console.error('Failed to send heartbeat message to Telegram:', botErr); | |
| } | |
| } | |
| } | |