Initial gori agent gateway
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
import type { GatewayPolicy } from "../config.js";
|
||||
import { AgentRegistry } from "../agents/agent-registry.js";
|
||||
import type { PlatformAdapter } from "./adapter.js";
|
||||
import { CommandRouter } from "./command-router.js";
|
||||
import { SessionStore, sessionIdFor } from "./session-store.js";
|
||||
import type { IncomingMessage } from "./types.js";
|
||||
|
||||
export interface GatewayResult {
|
||||
ok: boolean;
|
||||
reply?: string;
|
||||
ignored?: boolean;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export class Gateway {
|
||||
private readonly locks = new Map<string, Promise<void>>();
|
||||
readonly commandRouter: CommandRouter;
|
||||
|
||||
constructor(
|
||||
private readonly policy: GatewayPolicy,
|
||||
private readonly agents: AgentRegistry,
|
||||
private readonly sessions: SessionStore
|
||||
) {
|
||||
this.commandRouter = new CommandRouter(agents, sessions);
|
||||
}
|
||||
|
||||
async receive(message: IncomingMessage, adapter: PlatformAdapter, options: { synchronous?: boolean } = {}): Promise<GatewayResult> {
|
||||
const policyError = this.checkPolicy(message);
|
||||
if (policyError) return { ok: true, ignored: true, error: policyError };
|
||||
|
||||
const sessionId = sessionIdFor(message.platform, message.chatId);
|
||||
return this.withChatLock(sessionId, async () => {
|
||||
const command = this.commandRouter.route(message, sessionId);
|
||||
if (command.handled) {
|
||||
const reply = command.text || "";
|
||||
await adapter.sendMessage({
|
||||
target: { platform: message.platform, chatId: message.chatId, userId: message.userId, raw: message.raw },
|
||||
text: reply,
|
||||
replyTo: message.messageId
|
||||
});
|
||||
return { ok: true, reply };
|
||||
}
|
||||
|
||||
const session = this.sessions.getSession(sessionId);
|
||||
const agent = this.agents.get(session.selectedAgent);
|
||||
try {
|
||||
const response = await agent.run({
|
||||
input: message.text,
|
||||
sessionId,
|
||||
platform: message.platform,
|
||||
chatId: message.chatId,
|
||||
userId: message.userId,
|
||||
messageId: message.messageId
|
||||
});
|
||||
await adapter.sendMessage({
|
||||
target: { platform: message.platform, chatId: message.chatId, userId: message.userId, raw: message.raw },
|
||||
text: response.text,
|
||||
replyTo: message.messageId
|
||||
});
|
||||
return { ok: true, reply: response.text };
|
||||
} catch (error) {
|
||||
const errorText = error instanceof Error ? error.message : String(error);
|
||||
const reply = `Agent error: ${errorText}`;
|
||||
if (!options.synchronous) {
|
||||
await adapter.sendMessage({
|
||||
target: { platform: message.platform, chatId: message.chatId, userId: message.userId, raw: message.raw },
|
||||
text: reply,
|
||||
replyTo: message.messageId
|
||||
});
|
||||
}
|
||||
return { ok: false, error: errorText, reply };
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
stats(): { sessions: number; lockedChats: number } {
|
||||
return { ...this.sessions.stats(), lockedChats: this.locks.size };
|
||||
}
|
||||
|
||||
private checkPolicy(message: IncomingMessage): string | undefined {
|
||||
if (this.policy.allowedUsers.length > 0 && !this.policy.allowedUsers.includes(message.userId)) {
|
||||
return `User not allowed: ${message.userId}`;
|
||||
}
|
||||
if (this.policy.allowedChats.length > 0 && !this.policy.allowedChats.includes(message.chatId)) {
|
||||
return `Chat not allowed: ${message.chatId}`;
|
||||
}
|
||||
if (this.policy.requireMentionInGroup && message.isGroup && !message.mentionsBot) {
|
||||
return "Mention required in group chat";
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private async withChatLock<T>(key: string, fn: () => Promise<T>): Promise<T> {
|
||||
const previous = this.locks.get(key) || Promise.resolve();
|
||||
let release!: () => void;
|
||||
const current = new Promise<void>((resolve) => {
|
||||
release = resolve;
|
||||
});
|
||||
const queued = previous.then(() => current);
|
||||
this.locks.set(key, queued);
|
||||
|
||||
await previous;
|
||||
try {
|
||||
return await fn();
|
||||
} finally {
|
||||
release();
|
||||
if (this.locks.get(key) === queued) {
|
||||
this.locks.delete(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user