import type { Actor } from '../types.js';
import type { InboundMessage } from './types.js';
/**
 * How a channel's inbound messages are processed: one {@link ChannelJob} per message, in order per
 * conversation, each made of named phases.
 *
 * - {@link inlineExecutor}: in this process, after the `200`. A restart loses what was in flight.
 * - {@link durableExecutor}: as `@adonis-agora/durable` runs — persisted before the `200`, one at a
 *   time per conversation (a singleton key), each phase a checkpointed step (a dispatched one, when
 *   the engine has somewhere to serve it), and resumed by the engine's recovery after a crash.
 */
/** What a channel processes: a message, the rest of a run after its question, a question timing out. */
export type ChannelJob = {
    kind: 'message';
    channel: string;
    conversation: string;
    message: InboundMessage;
    /** When the webhook took it (epoch ms) — what a turn's queue wait is measured from. */
    receivedAt?: number;
    /**
     * Recorded in the conversation's burst log when it was taken (`coalesce` on): the job first
     * looks for a burst to join, or to lead.
     */
    coalesce?: boolean;
} | {
    /** Read a run's stream (again) and deliver what was not delivered yet. */
    kind: 'resume';
    channel: string;
    conversation: string;
    runId: string;
    threadId: string | null;
    actor: Actor;
} | {
    /** A question nobody answered in time: skip it, then resume the run. Runs outside the lock. */
    kind: 'timeout';
    channel: string;
    conversation: string;
    /** The run parked on the question (a delegate's, sometimes). */
    runId: string;
    toolCallId: string;
    /** The run whose stream the person reads. */
    streamRunId: string;
    threadId: string | null;
    actor: Actor;
};
/**
 * The phases a job is made of. Each one is rebuilt from its (JSON) input — the job and what the
 * phases before it returned — so it can run in any process that registered the channel.
 */
export type ChannelPhase = 'gather' | 'prepare' | 'act' | 'read' | 'timeout';
/** What one phase is handed: the job, and what the phases before it came to (all JSON). */
export interface ChannelPhaseInput {
    job: ChannelJob;
    /** `gather`: which look at the burst this is (0, 1, …). */
    round?: number;
    /** The messages the turn is made of, when it groups several (a burst). */
    batch?: InboundMessage[];
    /** What `prepare` came to. */
    prepared?: unknown;
    /** What `act` came to. */
    turn?: unknown;
}
/** Runs one phase: checkpointed (durable — in this process, or dispatched) or just called (inline). */
export type ChannelPhaseRunner = <T>(phase: ChannelPhase, input: ChannelPhaseInput) => Promise<T>;
/** How a job runs its phases, and how it waits for a burst to end. */
export interface ChannelJobIo {
    phase: ChannelPhaseRunner;
    /** Wait until `at` (epoch ms): the durable engine's sleep, else a timer. */
    sleepUntil(at: number): Promise<void>;
}
/** What a registered channel gives the executors: its job, and each of its phases on its own. */
export interface ChannelRunner {
    run(job: ChannelJob, io: ChannelJobIo): Promise<void>;
    phase(phase: ChannelPhase, input: ChannelPhaseInput): Promise<unknown>;
    /** How long a dispatched phase may go without an answer before the engine gives up on it. */
    stepTimeoutMs(phase: ChannelPhase): number;
    /** `false` → this channel's phases always run as `localStep`s (`durableSteps: false`). */
    dispatched?: boolean;
}
export interface ChannelExecutor {
    readonly durable: boolean;
    /**
     * Take a job: persisted (durable) or queued (inline) when this resolves — what the webhook waits
     * for before its `200`. Jobs of one conversation run one at a time, in the order they were taken.
     */
    enqueue(job: ChannelJob, id: string): Promise<void>;
    /** Run a job at `at` (epoch ms), outside the conversation's order. */
    later(job: ChannelJob, id: string, at: number): Promise<void>;
    /** Resolves once every job taken by this executor so far has finished. */
    drain(): Promise<void>;
}
/** Ids a store / engine takes whatever the provider's message id looks like. */
export declare function jobId(...parts: string[]): string;
/** Each phase called right here, a wait a timer: what the inline executor runs a job with. */
export declare function inlineIo(runner: Pick<ChannelRunner, 'phase'>): ChannelJobIo;
/** In this process: a promise chain per conversation. Nothing survives a restart. */
export declare function inlineExecutor(run: (job: ChannelJob) => Promise<void>): ChannelExecutor;
/** What a dispatched step handler is given besides its input — a durable `StepLogger`'s slice. */
export interface ChannelStepLog {
    /** Tell the engine the step is alive (rearms its timeout, where the transport carries beats). */
    heartbeat?(progress?: unknown): void;
}
/**
 * Where dispatched steps are served BY NAME — a durable `Transport` with `handle` (every built-in
 * one has it: in-memory, BullMQ, database). A handler registered here runs in whatever process the
 * transport hands the step to.
 */
export interface ChannelStepServer {
    handle(name: string, fn: (input: unknown, log?: ChannelStepLog) => Promise<unknown>): void;
}
/**
 * The slice of an `@adonis-agora/durable` `WorkflowEngine` the channels use — the engine itself
 * satisfies it (`channels.handle(adapter, { durable: engine })`).
 */
export interface ChannelWorkflowEngine {
    register(name: string, version: string, fn: (ctx: ChannelWorkflowCtx, input: unknown) => Promise<unknown>, opts?: {
        singleton?: {
            key: (input: unknown) => string;
            limit?: number;
        };
        /** Package attribution stamped on every run (`WorkflowRun.origin`). */
        origin?: string;
    }): void;
    start(workflow: string, input: unknown, runId: string, opts?: {
        startAt?: number | Date;
    }): Promise<unknown>;
    waitForRun?(runId: string, opts?: {
        timeoutMs?: number;
        terminal?: boolean;
    }): Promise<unknown>;
    /**
     * Serve a dispatched step by name — for an engine that serves its own steps. `@adonis-agora/durable`'s
     * engine serves them on its transport instead: the channels find it in the app's container (or take
     * it as `channels.handle(…, { durableSteps: transport })`). Neither → the phases run in the process
     * that runs the workflow (`ctx.localStep`).
     */
    handleStep?(name: string, fn: (input: unknown, log?: ChannelStepLog) => Promise<unknown>): void;
}
/** The slice of a durable `WorkflowCtx` a channel job uses. */
export interface ChannelWorkflowCtx {
    localStep<T>(name: string, fn: () => Promise<T>, options?: {
        retries?: number;
        backoff?: 'fixed' | 'exp';
        backoffMs?: number;
    }): Promise<T>;
    /**
     * A dispatched step, routed to the handler served under `name` in any process. Absent → the
     * phases run as `localStep`s.
     */
    step?<T>(name: string, input: unknown, options?: {
        retries?: number;
        timeoutMs?: number;
        pickupTimeoutMs?: number;
    }): Promise<T>;
    /** Suspend the run until `when` (durable). Absent → {@link sleep}, else a timer in this process. */
    sleepUntil?(when: number | Date): Promise<void>;
    /** Suspend the run for `duration` ms (durable). */
    sleep?(duration: number): Promise<void>;
}
/** The workflow a message (or a resume) runs as: one at a time per channel conversation. */
export declare const CHANNEL_JOB_WORKFLOW = "agora.channel.job";
/** A question's timeout: runs on its own, so it never waits behind the conversation. */
export declare const CHANNEL_TIMER_WORKFLOW = "agora.channel.timer";
/** The dispatched step a phase runs as: `agora.channel.prepare`, `agora.channel.read`, … */
export declare const channelPhaseStep: (phase: ChannelPhase) => string;
/** A control-flow signal a workflow body surfaces (a suspend for a sleep, say) — not a failure. */
export declare const isControlFlowSignal: (error: unknown) => boolean;
/**
 * Register the channel workflows on `engine`, once. Their body finds the job's channel with
 * `runnerFor` — the handler `channels.handle()` registered under that name IN THIS PROCESS. A
 * process that resumes channel jobs must have created the same handlers.
 *
 * - version `1`: every phase a `ctx.localStep`, in the process running the workflow — kept so runs
 *   started before an upgrade finish on the path they were recorded on;
 * - version `2` (new runs): each phase a DISPATCHED step (`agora.channel.<phase>`) when `server` (or
 *   `engine.handleStep`) serves them — any process that registered the channel runs it, any
 *   process continues the workflow — else a `localStep`; and a burst (`coalesce`) waited for with
 *   the engine's durable sleep. Which of the two a run uses is recorded in its first step.
 */
export declare function registerChannelWorkflows(engine: ChannelWorkflowEngine, runnerFor: (channel: string) => ChannelRunner | undefined, server?: ChannelStepServer): void;
/** As durable runs on `engine` — see {@link registerChannelWorkflows}. */
export declare function durableExecutor(engine: ChannelWorkflowEngine): ChannelExecutor;
export interface ChannelRetryOptions {
    /** Attempts per phase (the first included). Default 3. */
    attempts?: number;
    /** Delay before the second attempt; doubles each time (with jitter). Default 1 s. */
    backoffMs?: number;
    /** The longest delay between two attempts. Default 30 s. */
    maxBackoffMs?: number;
    /**
     * Retry after this error? Default: not a refusal that retrying cannot change — a definite 4xx
     * from the provider (`ChannelDeliveryError.definite`), an error carrying a 4xx `status`, a
     * `QuotaBlockedError` — everything else yes.
     */
    retryable?(error: unknown): boolean;
}
/** {@link ChannelRetryOptions.retryable}'s default. */
export declare function retryableByDefault(error: unknown): boolean;
/** Run `fn`, again after a growing delay while it fails with a retryable error. */
export declare function withRetries<T>(fn: () => Promise<T>, options?: ChannelRetryOptions): Promise<T>;
//# sourceMappingURL=executor.d.ts.map