import { MESH_PROTOCOL_VERSION_V2, type CoordinatorIdentity, type MeshEventScope } from './contracts.js';
export interface PendingMeshCoordinatorEvent {
    event: string;
    meshId: string;
    nodeLabel: string;
    nodeId?: string;
    workspace?: string;
    metadataEvent: Record<string, unknown>;
    coordinatorMessage?: string;
    queuedAt: number;
    /**
     * When set, this event is intended for a specific coordinator daemon.
     * Coordinators on other daemons should ignore it during drain.
     * Absent on legacy events — treated as broadcast to any coordinator.
     */
    targetCoordinatorDaemonId?: string;
    /**
     * When set, this event is intended for a specific coordinator SESSION on the
     * target daemon (the session that originally dispatched the work). PHASE 2 inject
     * strict-matches the live coordinator by this session id so a sibling coordinator
     * session on the same daemon does not receive another coordinator's completion.
     * Absent on legacy / version-skewed events → daemon-level broadcast (no regression).
     * Rides inside the event payload, so it survives the SQLite payload round-trip and
     * the JSONL file without a dedicated column; it is NOT a drain-scoping key.
     */
    targetCoordinatorSessionId?: string;
    /** '2.0' once stamped. Absent on v1 events. */
    protocolVersion?: typeof MESH_PROTOCOL_VERSION_V2;
    /** Idempotency key (UUID). The receiver's authoritative dedup key (B3). */
    eventId?: string;
    /** Routing scope. Defaulted from the event name unless the emit hint overrides. */
    scope?: MeshEventScope;
    /** Identity of the coordinator that dispatched the work this event reports. */
    dispatchedBy?: CoordinatorIdentity;
    /** Present only for unicast scope: the coordinator this event is addressed to. */
    intendedFor?: CoordinatorIdentity;
    /**
     * True when this event was stamped as a broadcast SOLELY because no owning
     * coordinator identity was resolvable at emit time (self-fallback: dispatchedBy
     * is THIS daemon's own machineId, not a real coordinator). Such a broadcast has
     * no owner, so the MAGI-REPLICA-COMPLETION-EVENT-LEAK guard — which only exists
     * to stop a NON-owner coordinator from consuming an OWNED terminal event — must
     * not apply: an ownerless terminal broadcast is a genuine "deliver to any
     * coordinator that drains on this machine" event and identity-matching its
     * self-id dispatchedBy against the drainer would wrongly route it away.
     * Absent (undefined/false) on a normally-owned event → the leak guard applies.
     */
    dispatchedBySelfFallback?: boolean;
}
/**
 * Optional emit-time hint passed to queuePendingMeshCoordinatorEvent so a call
 * site can override the name-defaulted scope or supply richer coordinator
 * identity (e.g. a coordinatorRunId the base event fields don't carry). Every
 * field is optional; when omitted the stamp is derived entirely from the
 * event's own targetCoordinatorDaemonId / targetCoordinatorSessionId. Kept
 * separate from the event so existing single-arg callers are untouched.
 */
export interface PendingEventEmitHint {
    scope?: MeshEventScope;
    /** Overrides the coordinator identity derived from the event's target fields. */
    dispatchedBy?: CoordinatorIdentity;
    /** Overrides the unicast target derived from the event's target fields. */
    intendedFor?: CoordinatorIdentity;
    /** coordinatorRunId to fold into the derived identity when the event lacks one. */
    coordinatorRunId?: string;
}
/**
 * T6 (B3c) enforce switch. When ON, the drain path stops passing an unversioned
 * (v1) or a validation-failing v2 event through to the coordinator — it QUARANTINES
 * it instead (excluded from the delivered batch + WARN + counter), and unicast
 * routing is the only delivery path (there is no v1 broadcast fallback). On by
 * default; set MESH_PROTOCOL_V2_ENFORCE=0/false/off/no to disable and restore the
 * accept-and-warn rollout behaviour exactly.
 *
 * Per the rollout plan (§1 decision 4): enforce is `MESH_PROTOCOL_V2_ENFORCE` (env).
 * Now that every node emits v2 (§배포 게이트 1 / risk §4), the code default is ON —
 * a manual env injection is no longer required to get enforce behaviour. Rollback to
 * accept mode is a pure-env step: set `MESH_PROTOCOL_V2_ENFORCE=0` (or `false`/`off`/
 * `no`) — no code change, no data migration (the schema is additive). Read at call
 * time so a test / operator can toggle it without a restart.
 *
 * Quarantine (not drop) keeps the loss-free invariant. The DESTRUCTIVE drain has
 * already consumed the event from its store by the time routing runs, so "held
 * back" here means: excluded from the delivered batch AND mirrored into the mesh
 * ledger as a recoverable `event_held` entry (the same recovery channel the
 * pending-trim path uses). It is observable via the counters + the ledger, so an
 * operator can requeue it after fixing the producer. The non-destructive PEEK path
 * (countMetrics=false) merely omits the event from the returned list — it never
 * consumed it and must not ledger-record on every status poll.
 */
export declare function isMeshProtocolV2EnforceEnabled(): boolean;
/** Observability counters for the v2 drain path. Read by tests and surfaced in
 *  mesh_status (B4/T6). Process-lifetime totals — never reset in production. */
declare const meshV2DrainCounters: {
    /** v2 events that passed validation and unicast/broadcast routing → delivered. */
    v2Delivered: number;
    /** v2 unicast events skipped because intendedFor addressed another coordinator. */
    v2RoutedAway: number;
    /** v2 events skipped because their eventId was already drained (idempotency). */
    v2DedupSkipped: number;
    /** v2 events that failed assertPendingMeshCoordinatorEventV2 but were PASSED
     *  THROUGH (accept mode). Non-zero here is the rollout signal that a producer
     *  emits a malformed envelope. */
    v2ValidationFailedAccepted: number;
    /** unicast events re-attributed to the drainer via daemon-core match (a
     *  coordinatorRunId change orphaned them). */
    v2ReattributedToDrainer: number;
    /** v1 (unversioned) events passed through as broadcast (rollout baseline). */
    v1BroadcastAccepted: number;
    /** T6 enforce: v2 events that FAILED validation and were QUARANTINED (held back
     *  from delivery, not dropped). Non-zero here means a producer is still emitting a
     *  malformed envelope after enforce was turned on. */
    v2ValidationFailedQuarantined: number;
    /** T6 enforce: v1 (unversioned) events QUARANTINED because no v2 envelope could be
     *  derived at emit time. Non-zero here means a producer path still emits v1 after
     *  enforce — it should reach 0 once every node is on a v2-stamping build. */
    v1UnversionedQuarantined: number;
};
/** Test/observability accessor for the v2 drain counters (snapshot copy). */
export declare function getMeshV2DrainCounters(): Readonly<typeof meshV2DrainCounters>;
/** Test helper: zero the v2 drain counters so a test starts from a clean slate. */
export declare function __resetMeshV2DrainCountersForTests(): void;
/** Test helper: clear the one-shot WARN dedup set. */
export declare function __resetMeshV2WarnDedupForTests(): void;
export declare function readRefineJobId(event: {
    metadataEvent?: Record<string, unknown>;
} | Record<string, unknown>): string;
export declare function buildPendingEventFingerprint(event: PendingMeshCoordinatorEvent): string;
export declare function hasPendingCoordinatorEventDuplicate(event: PendingMeshCoordinatorEvent): boolean;
/**
 * Retention sweep for the SQLite mesh_pending_events inbox. Deletes long-drained
 * rows (past the idempotency-useful window) and long-orphaned undrained rows (a
 * coordinator identity that never returned). Best-effort and idempotent: a store
 * failure or an empty table is a cheap no-op. Called from the periodic mesh-event
 * maintenance sweep. Returns the number of rows pruned (0 when nothing to do).
 */
export declare function prunePendingMeshCoordinatorEventsRetention(): number;
/**
 * Stamp the v2 protocol envelope onto a pending event at emit time (B2a).
 *
 * Non-breaking: returns a NEW event object with protocolVersion/eventId/scope/
 * dispatchedBy/intendedFor added when a coordinator identity can be derived,
 * otherwise returns the input unchanged (a v1 event, broadcast-treated during
 * rollout). Identity and the unicast target are derived from the event's own
 * targetCoordinatorDaemonId / targetCoordinatorSessionId (already carried by
 * every producer), so most call sites need no change; the optional `hint`
 * overrides scope/identity where a site knows better.
 *
 * The eventId is generated here (randomUUID) exactly once, so re-queues that
 * pass an already-stamped event keep their original eventId — the idempotency
 * key is stable across re-delivery. An already-stamped event is returned as-is.
 */
export declare function stampPendingEventV2(event: PendingMeshCoordinatorEvent, hint?: PendingEventEmitHint): PendingMeshCoordinatorEvent;
/**
 * Copy the v2 envelope fields that are present on `event` onto a flat wire
 * payload. Only sets a field when it is present, so a v1 event contributes
 * nothing (the payload stays v1-shaped and version-skew safe).
 */
export declare function serializeV2EnvelopeToWire(event: PendingMeshCoordinatorEvent): Record<string, unknown>;
/**
 * Restore the v2 envelope fields from a flat wire payload for a re-queue. Only
 * returns fields that survive validation; a payload missing/malforming a field
 * yields a partial (or empty) object so the re-queue path stays v1-safe. The
 * eventId is returned verbatim — its preservation is the idempotency guarantee.
 */
export declare function readV2EnvelopeFromWire(payload: Record<string, unknown>): Partial<Pick<PendingMeshCoordinatorEvent, 'protocolVersion' | 'eventId' | 'scope' | 'dispatchedBy' | 'intendedFor'>>;
export declare function queuePendingMeshCoordinatorEvent(rawEvent: PendingMeshCoordinatorEvent, hint?: PendingEventEmitHint): boolean;
/**
 * Drain and return pending coordinator events for meshId, removing the drained
 * ones from both the SQLite inbox and the JSONL legacy file.
 *
 * When `opts.onlyEvents` is supplied, ONLY events whose name is in that set are
 * drained; every other event stays queued (undrained in SQLite, rewritten back to
 * the JSONL file). The reconcile loop uses this to force-drain terminal/force-inject
 * events into a *generating* coordinator while leaving non-force progress events for
 * the coordinator's next idle transition. The atomic SQLite drained=1 marking and the
 * atomic JSONL rename keep force-drain and a concurrent full drain from double-consuming.
 */
export declare function drainPendingMeshCoordinatorEvents(meshId?: string, coordinatorDaemonId?: string | ReadonlyArray<string>, opts?: {
    onlyEvents?: ReadonlySet<string>;
    drainerIdentity?: CoordinatorIdentity;
}): PendingMeshCoordinatorEvent[];
/**
 * FALSE-BLOCKER-CLONE-QUEUE: retract any still-UNDELIVERED `mesh:dispatch_blocked`
 * actionable-skip event for a task whose blocker has since resolved (the task was
 * claimed, or its skip transitioned to a self-resolving transient reason). Without
 * this, a `target_node_id_unmatched` blocker paged during the brief clone/bootstrap
 * propagation window would linger in the coordinator's pending queue and surface as a
 * false "actionable blocker — will NOT clear on its own" even after the task dispatched.
 *
 * Only removes events that have NOT yet been drained/delivered to the coordinator — an
 * already-delivered message cannot be unsent, but de-dup re-arm (caller side) plus this
 * retraction guarantee no NEW stale blocker accumulates. Best-effort across both the
 * SQLite inbox and the JSONL legacy files (scoped + shared). Returns rows removed.
 */
export declare function retractPendingDispatchBlockedEvent(meshId: string | undefined, taskId: string | undefined, coordinatorDaemonId?: string): number;
/** Peek at pending coordinator events without draining (non-destructive). */
export declare function getPendingMeshCoordinatorEvents(meshId?: string, coordinatorDaemonId?: string | ReadonlyArray<string>, opts?: {
    drainerIdentity?: CoordinatorIdentity;
}): readonly PendingMeshCoordinatorEvent[];
/**
 * Test helper: purge all pending-event state for a mesh — SQLite rows
 * (including drained fingerprint history) and JSONL files.
 */
export declare function __clearMeshPendingEventsForTests(meshId: string): void;
/**
 * Test helper: persist a pending event VERBATIM, skipping the emit-time v2 stamp.
 * The local emit path (queuePendingMeshCoordinatorEvent → stampPendingEventV2) now
 * always mints a v2 envelope (self-daemon broadcast fallback when no coordinator
 * identity is present), so a genuinely-unversioned (v1) row can no longer be produced
 * through the normal queue. The drain-side v1 handling (accept-broadcast / enforce-
 * quarantine) still matters for durable v1 rows written by a pre-v2 daemon and for
 * version-skewed remote relays, so tests inject those rows directly through this.
 */
export declare function __persistUnstampedPendingEventForTests(event: PendingMeshCoordinatorEvent): boolean;
/** Narrowing filter for {@link requeueHeldMeshCoordinatorEvents}, scoped within one mesh. */
export interface MeshHeldEventRequeueFilter {
    /** Restore only held events whose worker task id matches (from the held event's metadata/taskId). */
    taskId?: string;
    /** Restore only held events originating from this node. */
    nodeId?: string;
    /** Restore only held events of this event name (e.g. 'session:completed'). */
    event?: string;
    /** Restore only held entries recorded at/after this ISO timestamp. */
    since?: string;
    /** Restore only held entries with this hold reason (e.g. 'pending_trim_dropped'). */
    reason?: string;
}
export interface MeshHeldEventRequeueResult {
    meshId: string;
    /** event_held entries considered after the filter. */
    matched: number;
    /** entries skipped because a prior requeue already recovered them. */
    alreadyRequeued: number;
    /** entries skipped because they carried no restorable original event / were not recoverable. */
    unrecoverable: number;
    /** entries handed to the pending queue (some may have been dedup-suppressed downstream). */
    requeued: number;
    /** of `requeued`, how many the pending-queue dedup collapsed onto a live event. */
    dedupSuppressed: number;
    entries: Array<{
        heldEntryId: string;
        event: string;
        nodeId?: string;
        taskId?: string;
        reason?: string;
        outcome: 'requeued' | 'already_requeued' | 'unrecoverable';
    }>;
}
/**
 * Restore recoverable `event_held` ledger entries back to the pending coordinator
 * queue for `meshId`. See the block comment above for the no-loss / no-double-requeue
 * invariants. Returns per-entry outcomes for the caller to surface.
 */
export declare function requeueHeldMeshCoordinatorEvents(meshId: string, filter?: MeshHeldEventRequeueFilter): MeshHeldEventRequeueResult;
/** Explicitly clear all pending coordinator events for a mesh (and coordinator if scoped). */
export declare function clearPendingMeshCoordinatorEvents(meshId?: string, coordinatorDaemonId?: string): void;
export {};
