Spaces:
Paused
Paused
| import fs from 'fs'; | |
| import path from 'path'; | |
| import { EventEmitter } from 'events'; | |
| // DEFINITION: Path to the Single Source of Truth | |
| // Assuming running from apps/backend/src or similar structure in Docker | |
| const HANDOVER_LOG_PATH = path.join(process.cwd(), '..', '..', 'docs', 'HANDOVER_LOG.md'); | |
| export class HandoverWatchdog extends EventEmitter { | |
| private static instance: HandoverWatchdog; | |
| private isWatching: boolean = false; | |
| private lastProcessedTime: number = 0; | |
| private constructor() { | |
| super(); | |
| } | |
| public static getInstance(): HandoverWatchdog { | |
| if (!HandoverWatchdog.instance) { | |
| HandoverWatchdog.instance = new HandoverWatchdog(); | |
| } | |
| return HandoverWatchdog.instance; | |
| } | |
| /** | |
| * Starts the surveillance of the Blackboard Protocol. | |
| */ | |
| public startWatch(): void { | |
| if (this.isWatching) { | |
| console.log('ποΈ [WATCHDOG] Already active.'); | |
| return; | |
| } | |
| console.log(`ποΈ [WATCHDOG] Initializing Blackboard Protocol surveillance...`); | |
| console.log(`π [WATCHDOG] Target: ${HANDOVER_LOG_PATH}`); | |
| // Verify existence of the Truth Document | |
| if (!fs.existsSync(HANDOVER_LOG_PATH)) { | |
| console.warn(`β οΈ [WATCHDOG] Critical Failure: Handover log not found at ${HANDOVER_LOG_PATH}`); | |
| // Retry logic could be added here, but for now we warn | |
| return; | |
| } | |
| try { | |
| // We use watchFile for better Docker volume compatibility than fs.watch | |
| fs.watchFile(HANDOVER_LOG_PATH, { interval: 2000 }, (curr, prev) => { | |
| // Only trigger if modified time changed and it's newer than last process | |
| if (curr.mtimeMs !== prev.mtimeMs) { | |
| console.log('β‘ [WATCHDOG] Detect: Blackboard updated. Analyzing...'); | |
| this.analyzeBlackboard(); | |
| } | |
| }); | |
| this.isWatching = true; | |
| console.log('π’ [WATCHDOG] Active and Waiting for Signals.'); | |
| } catch (error) { | |
| console.error('β [WATCHDOG] Failed to initialize:', error); | |
| } | |
| } | |
| /** | |
| * Reads the Blackboard and looks for "READY FOR [AGENT]" signals. | |
| */ | |
| private analyzeBlackboard(): void { | |
| try { | |
| const content = fs.readFileSync(HANDOVER_LOG_PATH, 'utf-8'); | |
| // REGEX LOGIC: | |
| // We look for: **Status:** [BOLD/EMOJI?] READY FOR [AGENT_NAME] | |
| // Captures the agent name. Case insensitive. | |
| const statusRegex = /\*\*Status:\*\*\s*(?:β |π΄|π’|\*\*)?\s*READY FOR\s*\[?(.*?)\]?/i; | |
| // We only scan the last 2000 characters to be efficient | |
| const recentContent = content.slice(-2000); | |
| const match = statusRegex.exec(recentContent); | |
| if (match && match[1]) { | |
| const targetAgent = match[1].trim().replace(/\*|\]|\[/g, ''); // Cleanup cleanup | |
| console.log(`π [WATCHDOG] SIGNAL VERIFIED: Handover to Agent [${targetAgent}]`); | |
| // Emit the signal for the Orchestrator to pick up | |
| this.emit('handover_signal', { | |
| target: targetAgent, | |
| timestamp: new Date(), | |
| source: 'Blackboard' | |
| }); | |
| } else { | |
| // Debug log to confirm scan happened (can be removed later for silence) | |
| // console.log('π [WATCHDOG] Scanned. No active handover signal found.'); | |
| } | |
| } catch (error) { | |
| console.error('β [WATCHDOG] Read error:', error); | |
| } | |
| } | |
| } | |
| export const handoverWatchdog = HandoverWatchdog.getInstance(); | |