/**
 * remote/approval — RemoteApprovalChannel: HITL routed to a remote client.
 *
 * Implements the SDK's `ApprovalChannel` port. When the agent loop needs
 * approval for a dangerous tool, this channel:
 *   1. emits a `hitl.request` SessionEvent — the remote client (phone) sees it;
 *   2. parks the request until the client calls `resolve()` (wired by a
 *      transport to e.g. `POST /remote/hitl/:id/approve`);
 *   3. on `timeoutMs` with no verdict, applies the fail-closed timeout policy.
 *
 * It emits `hitl.resolved` on every verdict so the remote client's view stays
 * consistent. This is the HITL half of remote-control — the founder wants to
 * "approve commands" from the phone.
 *
 * @public @since 2.11.0 — preste remote-control T1
 */
import { randomUUID } from "node:crypto";
import { makeEvent } from "./events.js";
/** Create a RemoteApprovalChannel bound to a SessionEventSink. */
export function createRemoteApprovalChannel(opts) {
    const defaultTimeout = opts.timeoutMs ?? 120_000;
    const onTimeout = opts.onTimeout ?? "reject";
    const queue = new Map();
    function settle(id, approved, by, note) {
        const e = queue.get(id);
        if (!e || e.status !== "pending")
            return false;
        if (e.timer)
            clearTimeout(e.timer);
        e.status = "resolved";
        e.verdict = { approved, by, rationale: note, at: new Date().toISOString() };
        opts.sink.emit(makeEvent("hitl.resolved", { requestId: id, approved, by }));
        return true;
    }
    return {
        async send(req) {
            const id = randomUUID();
            const timeoutMs = req.timeoutMs > 0 ? req.timeoutMs : defaultTimeout;
            const entry = { id, req, status: "pending" };
            entry.timer = setTimeout(() => {
                settle(id, onTimeout === "approve", "timeout-policy", `auto-${onTimeout} after ${timeoutMs}ms`);
            }, timeoutMs);
            entry.timer.unref?.();
            queue.set(id, entry);
            opts.sink.emit(makeEvent("hitl.request", {
                requestId: id,
                action: req.action,
                context: req.context,
                expiresAt: new Date(Date.now() + timeoutMs).toISOString(),
            }));
            return id;
        },
        async poll(id) {
            const e = queue.get(id);
            return e && e.status === "resolved" && e.verdict ? e.verdict : null;
        },
        async cancel(id) {
            const e = queue.get(id);
            if (e?.timer)
                clearTimeout(e.timer);
            if (e && e.status === "pending") {
                e.status = "resolved";
                e.verdict = {
                    approved: false,
                    by: "cancelled",
                    rationale: "request cancelled",
                    at: new Date().toISOString(),
                };
                opts.sink.emit(makeEvent("hitl.resolved", {
                    requestId: id,
                    approved: false,
                    by: "cancelled",
                }));
            }
        },
        resolve(id, approved, by, note = "") {
            return settle(id, approved, by, note);
        },
        pending() {
            return [...queue.values()]
                .filter((e) => e.status === "pending")
                .map((e) => ({
                id: e.id,
                action: e.req.action,
                context: e.req.context,
            }));
        },
        history() {
            const out = [];
            for (const e of queue.values()) {
                if (e.status === "resolved" && e.verdict) {
                    out.push({
                        action: e.req.action,
                        approved: e.verdict.approved,
                        by: e.verdict.by,
                        at: e.verdict.at,
                    });
                }
            }
            return out;
        },
        dispose() {
            for (const e of queue.values()) {
                if (e.timer)
                    clearTimeout(e.timer);
            }
        },
    };
}
//# sourceMappingURL=approval.js.map