/**
 * remote/veto — the VetoChannel port for the T6a tool-intent veto window.
 *
 * Before every tool call, the AgentLoop emits `tool.intent` (with a
 * `vetoWindowMs` budget) and then waits on this channel for up to that many
 * milliseconds. If a veto signal arrives, the loop SKIPS the tool call and
 * records a `[vetoed by <by>]` result; otherwise the call proceeds normally.
 *
 * The channel is keyed by `callId` so several pending tool intents can be
 * vetoed independently. A signal submitted before any wait is buffered, so
 * the remote client cannot race the loop into executing a doomed call.
 *
 * @public @since 2.15.0 — preste remote-control T6a (veto window)
 */
/**
 * Process-local in-memory veto channel. A signal arriving BEFORE a wait is
 * buffered and consumed by the first matching `await`; a wait without a
 * pending signal resolves `null` when its window elapses.
 */
export class InMemoryVetoChannel {
    buffered = new Map();
    waiters = new Map();
    submit(signal) {
        const waiter = this.waiters.get(signal.callId);
        if (waiter) {
            this.waiters.delete(signal.callId);
            waiter(signal);
            return;
        }
        this.buffered.set(signal.callId, signal);
    }
    async await(callId, windowMs) {
        const pending = this.buffered.get(callId);
        if (pending) {
            this.buffered.delete(callId);
            return pending;
        }
        if (windowMs <= 0)
            return null;
        return new Promise((resolve) => {
            const timer = setTimeout(() => {
                if (this.waiters.get(callId)) {
                    this.waiters.delete(callId);
                    resolve(null);
                }
            }, windowMs);
            timer.unref?.();
            this.waiters.set(callId, (signal) => {
                clearTimeout(timer);
                resolve(signal);
            });
        });
    }
    pendingCount() {
        return this.buffered.size;
    }
}
//# sourceMappingURL=veto.js.map