Initial commit: Discord-Claude Gateway with event-driven agent runtime

This commit is contained in:
2026-02-22 00:31:25 -05:00
commit 77d7c74909
58 changed files with 11772 additions and 0 deletions

116
src/event-queue.ts Normal file
View File

@@ -0,0 +1,116 @@
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<void>) | null = null;
private processing = false;
private drainResolvers: Array<() => void> = [];
constructor(maxDepth: number) {
this.maxDepth = maxDepth;
}
enqueue(event: Omit<Event, "id" | "timestamp">): 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>): void {
this.handler = handler;
this.processNext();
}
drain(): Promise<void> {
if (this.queue.length === 0 && !this.processing) {
return Promise.resolve();
}
return new Promise<void>((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();
}
});
}
}