import { NoChatContext } from "./agent-types.js";
/**
 * Per-payload-shape multiplexer for frontend callbacks.
 *
 * Each registered frontend (Telegram, center, future Web TUI, ...) hands the
 * Agent a callback. When the LLM fires e.g. `attachment.send`, ONE of those
 * callbacks must own the delivery — namely the frontend whose chat handler
 * is currently on the call stack (identified by an AsyncLocalStorage entry
 * inside the frontend's own code).
 *
 * The multiplexer iterates the registered callbacks in insertion order and:
 *   - returns on the first callback that resolves successfully;
 *   - skips callbacks that throw `NoChatContext` (= "not my chat scope");
 *   - propagates any other error (real failure inside the chosen frontend).
 *
 * When every registered callback throws NoChatContext, the multiplexer
 * rethrows `NoChatContext` so the caller (executor/tool handler) can present
 * a clear "no frontend has a chat scope" message to the LLM.
 *
 * When NO callbacks are registered, `dispatcher()` returns null — callers
 * still get the legacy null-sender code path.
 */
export class FrontendMultiplexer {
    entries = new Map();
    set(id, fn) {
        this.entries.set(id, fn);
    }
    delete(id) {
        return this.entries.delete(id);
    }
    get size() {
        return this.entries.size;
    }
    has(id) {
        return this.entries.has(id);
    }
    /** For tests / debugging. Order matches insertion order. */
    ids() {
        return Array.from(this.entries.keys());
    }
    /**
     * Returns a callable that does the multiplex dispatch, or null when no
     * frontends are registered (callers still need to handle the legacy
     * "frontend missing" case explicitly — e.g. attachment-executor returns
     * a friendly "no frontend registered" string to the LLM).
     */
    dispatcher() {
        if (this.entries.size === 0)
            return null;
        const snapshot = Array.from(this.entries.values());
        return async (payload) => {
            for (const fn of snapshot) {
                try {
                    await fn(payload);
                    return;
                }
                catch (err) {
                    if (err instanceof NoChatContext)
                        continue;
                    throw err;
                }
            }
            throw new NoChatContext("No registered frontend has a chat scope for this delivery");
        };
    }
}
//# sourceMappingURL=frontend-multiplexer.js.map