/**
 * Budget + permission ExecutionGate — the engine half of plan D15 ("无人值守触发须过 budget+权限闸门")
 * and the M2 acceptance "触发式运行受 budget+权限闸门拦截".
 *
 * A workflow run is either ATTENDED (trigger "manual": a human is present and authorizes
 * interactively) or UNATTENDED (trigger "schedule" / "im-message" / "webhook": fired by the
 * trigger layer with nobody watching). This gate enforces two independent guarantees on every
 * node, before it executes:
 *
 *   1. Permission (security, HARD fail-loud) — an unattended run may only execute side-effecting
 *      ("sensitive") node kinds that were EXPLICITLY authorized via `allowUnattendedKinds`. A
 *      sensitive kind that was not pre-authorized throws `WorkflowPermissionDenied` and halts the
 *      run (status "failed"). Pure / control-flow kinds (set/if/loop/merge/passthrough/trigger)
 *      are always allowed. Manual runs skip permission entirely (the user is the authorization).
 *   2. Budget (resumable pause) — every node must pass the per-run budget (D15 "每轮过 budget"):
 *      a hard cap on node executions and an optional wall-clock cap. Exceeding either throws
 *      `WorkflowBudgetExceeded`, which extends the runtime's pause signal, so the run SUSPENDS
 *      with its cache intact (raise the budget, then resume) rather than failing — mirroring the
 *      existing ProductBudgetTracker "exceeded → pause" semantics.
 *
 * Pure and deterministic: no I/O, an injectable clock. fail-loud construction: a non-positive
 * `maxNodeExecutions` (or non-positive `maxDurationMs`) throws — a budget with no ceiling is a
 * silent way to disable the gate, which D15 forbids.
 */
import { WorkflowPauseSignal, type ExecutionGate } from "./workflow-runtime.js";
/** The trigger kinds that can start a run. Single source of the union (consumed by the controller). */
export type WorkflowTriggerKind = "manual" | "schedule" | "im-message" | "webhook" | "intent" | "form";
/**
 * Node kinds that reach the outside world (network calls, channel sends, agent/tool/mcp side
 * effects). These require explicit authorization to run unattended. Control / data-shaping kinds
 * (set / if / loop / merge / passthrough / trigger) are NOT here — they cannot escape the engine.
 */
export declare const SENSITIVE_KINDS: ReadonlySet<string>;
/** Hard, non-resumable denial: an unauthorized side-effecting kind under an unattended trigger. */
export declare class WorkflowPermissionDenied extends Error {
    readonly nodeId: string;
    readonly kind: string;
    readonly trigger: WorkflowTriggerKind;
    constructor(nodeId: string, kind: string, trigger: WorkflowTriggerKind);
}
/**
 * Budget ceiling reached. Extends WorkflowPauseSignal so the runtime treats it as a graceful,
 * resumable suspension (cache retained) rather than a failure — the caller raises the budget and
 * resumes, and already-completed nodes hit cache.
 */
export declare class WorkflowBudgetExceeded extends WorkflowPauseSignal {
    readonly reason: "executions" | "duration";
    readonly limit: number;
    constructor(atNodeId: string, reason: "executions" | "duration", limit: number);
}
export interface BudgetPermissionGateOptions {
    /** Trigger kind of THIS run. "manual" = attended (permission skipped); others = unattended. */
    trigger: WorkflowTriggerKind;
    /** Hard cap on node executions per run (D15 loop hard-cap is separate, in the scheduler). */
    maxNodeExecutions: number;
    /** Optional wall-clock cap in ms; the run pauses once elapsed reaches it. */
    maxDurationMs?: number;
    /** Sensitive kinds an UNATTENDED run is allowed to execute. Ignored for manual runs. */
    allowUnattendedKinds?: readonly string[];
    /** Injectable clock (ms epoch) for deterministic tests. */
    now?: () => number;
}
export declare class BudgetPermissionGate implements ExecutionGate {
    private readonly trigger;
    private maxNodeExecutions;
    private maxDurationMs?;
    private readonly allow;
    private readonly now;
    private executions;
    private startedAt;
    constructor(opts: BudgetPermissionGateOptions);
    private get unattended();
    /** Invoked by the runtime before each node executes. Throws to deny (hard) or pause (budget). */
    check(nodeId: string, kind: string): void;
    /**
     * Raise the remaining allowance so a budget-paused run can resume (D15 "抬预算后 resume").
     * Deltas are ADDED to the current ceilings. fail-loud: a non-positive delta is rejected, and
     * extending duration is only valid when a duration cap exists (extending an absent cap is a
     * silent no-op the caller likely did not intend).
     */
    extend(delta: {
        executions?: number;
        durationMs?: number;
    }): void;
}
