export type EventType = "message" | "heartbeat" | "cron" | "hook" | "webhook"; export type HookType = "startup" | "agent_begin" | "agent_stop" | "shutdown"; export interface MessagePayload { prompt: { text: string; channelId: string; userId: string; guildId: string | null }; } export interface HeartbeatPayload { instruction: string; checkName: string; } export interface CronPayload { instruction: string; jobName: string; } export interface HookPayload { hookType: HookType; instruction?: string; } export type EventPayload = MessagePayload | HeartbeatPayload | CronPayload | HookPayload; export interface Event { id: number; type: EventType; payload: EventPayload; timestamp: Date; source: string; } export class EventQueue { private queue: Event[] = []; private nextId = 1; private maxDepth: number; private handler: ((event: Event) => Promise) | null = null; private processing = false; private drainResolvers: Array<() => void> = []; constructor(maxDepth: number) { this.maxDepth = maxDepth; } enqueue(event: Omit): Event | null { if (this.queue.length >= this.maxDepth) { return null; } const fullEvent: Event = { ...event, id: this.nextId++, timestamp: new Date(), }; this.queue.push(fullEvent); this.processNext(); return fullEvent; } dequeue(): Event | undefined { return this.queue.shift(); } size(): number { return this.queue.length; } onEvent(handler: (event: Event) => Promise): void { this.handler = handler; this.processNext(); } drain(): Promise { if (this.queue.length === 0 && !this.processing) { return Promise.resolve(); } return new Promise((resolve) => { this.drainResolvers.push(resolve); }); } private processNext(): void { if (this.processing || !this.handler || this.queue.length === 0) { return; } this.processing = true; const event = this.queue.shift()!; this.handler(event) .then(() => { this.processing = false; if (this.queue.length === 0) { const resolvers = this.drainResolvers.splice(0); for (const resolve of resolvers) { resolve(); } } else { this.processNext(); } }) .catch(() => { this.processing = false; if (this.queue.length === 0) { const resolvers = this.drainResolvers.splice(0); for (const resolve of resolvers) { resolve(); } } else { this.processNext(); } }); } }