import { v7 as uuidv7 } from "uuid"; import { Agent } from "./runner.js"; import { Channel } from "./channel.js"; import type { AgentOptions, Message } from "./types.js"; const devNull = { write: () => true } as NodeJS.WritableStream; export class AsyncAgent { private readonly agent: Agent; private readonly channel = new Channel(); private _closed = false; private queue: Promise = Promise.resolve(); readonly sessionId: string; constructor(options?: AgentOptions) { this.agent = new Agent({ ...options, logger: { stdout: devNull, stderr: devNull }, }); this.sessionId = this.agent.sessionId; } get closed(): boolean { return this._closed; } /** Write message to agent (non-blocking, serialized queue) */ write(content: string): void { if (this._closed) throw new Error("Agent is closed"); this.queue = this.queue .then(async () => { if (this._closed) return; const result = await this.agent.run(content); if (result.text) { this.channel.send({ id: uuidv7(), content: result.text }); } if (result.error) { this.channel.send({ id: uuidv7(), content: `[error] ${result.error}` }); } }) .catch((err) => { const message = err instanceof Error ? err.message : String(err); this.channel.send({ id: uuidv7(), content: `[error] ${message}` }); }); } /** Continuously read message stream */ read(): AsyncIterable { return this.channel; } /** Close agent, stop all reads */ close(): void { if (this._closed) return; this._closed = true; this.channel.close(); } }