import type { Event, HookType } from "./event-queue.js"; export interface HookConfig { startup?: string; agent_begin?: string; agent_stop?: string; shutdown?: string; } export interface AgentRuntimeLike { processEvent(event: Event): Promise; } type EnqueueFn = (event: Omit) => Event | null; export class HookManager { private config: HookConfig = {}; parseConfig(content: string): HookConfig { const config: HookConfig = {}; const lines = content.split("\n"); let inHooksSection = false; let currentHookType: HookType | null = null; for (const line of lines) { const h2Match = line.match(/^##\s+(.+)$/); if (h2Match) { if (inHooksSection) { // Another ## heading ends the Hooks section break; } if (h2Match[1].trim() === "Hooks") { inHooksSection = true; } continue; } if (!inHooksSection) continue; const h3Match = line.match(/^###\s+(.+)$/); if (h3Match) { const name = h3Match[1].trim(); if (name === "startup" || name === "agent_begin" || name === "agent_stop" || name === "shutdown") { currentHookType = name; } else { currentHookType = null; } continue; } const instructionMatch = line.match(/^Instruction:\s*(.+)$/); if (instructionMatch && currentHookType !== null) { config[currentHookType] = instructionMatch[1].trim(); continue; } } this.config = config; return config; } fire(hookType: HookType, enqueue: EnqueueFn): void { const instruction = this.config[hookType]; enqueue({ type: "hook", payload: { hookType, ...(instruction !== undefined ? { instruction } : {}), }, source: "hook-manager", }); } async fireInline(hookType: HookType, runtime: AgentRuntimeLike): Promise { const instruction = this.config[hookType]; const event: Event = { id: 0, type: "hook", payload: { hookType, ...(instruction !== undefined ? { instruction } : {}), }, timestamp: new Date(), source: "hook-manager-inline", }; await runtime.processEvent(event); } }