import type { TaskEvent } from "../task-event.js";
import type { RunnerDeps } from "../runner-deps.js";
import { type SafeNotifier } from "../safe-notify.js";
/** One run's forwarding-failure bookkeeping: opaque to everything but this module. */
export interface ForwardSinkScope {
    readonly notifier: SafeNotifier;
}
/** The two seats this module reads off a run's trusted internals — spelled structurally so a SNAPSHOT
 *  of them (the delegation revive lane holds one) is as valid a holder as the live object. */
export interface ForwardSinkHolder {
    onForwardEvent?: (event: TaskEvent) => void;
    forwardScope?: ForwardSinkScope;
}
/**
 * Mint the forwarding scope for ONE run, bound to the deployment's notice seat. Called where the run's
 * internals object is composed (`Runner.runTaskStream`), so every copy of that object made later — the
 * prepare re-spread, a placement-root rebind, a child's snapshot — carries the same counters.
 */
export declare function createForwardSinkScope(onNotice: RunnerDeps["onNotice"]): ForwardSinkScope;
/**
 * Forward ONE event to the deployment's display sink, containing both failure shapes.
 *
 * @contract runtask.forward_sink_never_faults_the_run — a `RunInternals.onForwardEvent` that throws,
 * or that returns a promise which rejects, never reaches the run's control flow: the frame is dropped,
 * the failure is counted on the run's own `forwardScope`, and the FIRST one is announced once per run
 * as `config.forward_sink_failed` (later ones are counted only). Delivery is never awaited — describing
 * a run is not part of it, so a sink that never settles cannot hold a turn open. An absent sink is a
 * no-op, and a healthy sink sees the same call it always did, with the same receiver.
 */
export declare function forwardSafely(holder: ForwardSinkHolder | undefined, event: TaskEvent): void;
/** Contained forwarding failures on this scope (diagnostics; 0 on the healthy path). */
export declare function forwardSinkFailures(holder: ForwardSinkHolder | undefined): number;
