/**
 * WorkItem Dispatch Subscriber
 *
 * Pushes a `[CREWLY-DISPATCH]` system message to the **target session** every
 * time a WorkItem is added to the pool with a specific target. Closes the
 * "queued → claimed" gap that {@link AgentAutoClaimService} cannot cover:
 * AutoClaim is *pull* (reacts to `agent:idle` / `task:done`); this subscriber
 * is *push* (reacts to `workitem:queued`). The two compose:
 *
 *   - Fresh queued WI lands → this fires → target sees the prompt, runs
 *     `poll-tasks`, claims the WI.
 *   - Agent later goes idle → AutoClaim fires → it picks the next WI.
 *
 * **Why both are needed.** AutoClaim's startup recovery
 * ({@link AgentAutoClaimService.recoverPendingTasks}) skips WIs whose
 * target agent is already `active` on the assumption that "an active agent
 * will claim it on its own". Empirically (2026-05-06 dogfood) that's false:
 * an agent in the middle of a long thought stream never emits the
 * `agent:idle` event AutoClaim listens to, so targeted WIs sit in the pool
 * untouched. This subscriber covers exactly that case.
 *
 * **Integration with AgentAutoClaim.** The recovery path's `active` branch
 * delegates here via {@link WorkItemDispatchSubscriber.dispatchTo}, so both
 * paths emit the identical message. No double-fire because the dedup Set
 * is shared across both call-sites.
 *
 * **Dedup contract.** Each WI is dispatched at most once per process
 * lifetime (in-memory `Set<workItemId>`). Backend restart resets the set —
 * which is the desired behavior since startup backfill needs to re-fire for
 * the queued WIs that survived the restart.
 *
 * @module services/v3/workitem-dispatch.subscriber
 */
import type { WorkItem } from '../../types/v2/work-item.types.js';
/**
 * SLA tracker WIs use a deterministic id pattern `request:<rid>:respond_to_user`
 * (see `request-sla.subscriber.ts`). They're internal bookkeeping items, not
 * dispatchable work — the orc never "claims and executes" one; the WI is
 * passively tracked and resolved by the SLA path when orc actually replies
 * via the reply-slack skill.
 *
 * Exported so {@link AgentAutoClaimService} can apply the same filter when
 * scoring claimable WIs. Before that fix (2026-05-12), AutoClaim would
 * happily claim a `respond_to_user` WI for crewly-orc, then call
 * `dispatchTo` which short-circuits here on the same regex — leaving the
 * WI stuck in `running` with no PTY delivery. 5/10-min SLA breach fired,
 * claim revoked, WI cycled back to `queued`, AutoClaim re-claimed it,
 * loop repeated indefinitely. From the user's perspective: "Request never
 * progresses, only heartbeats and SLA-breach DMs."
 */
export declare const SLA_TRACKER_ID_PATTERN: RegExp;
/**
 * Singleton subscriber that pushes dispatch prompts to target sessions.
 */
export declare class WorkItemDispatchSubscriber {
    private static instance;
    private readonly logger;
    private eventBusService;
    /**
     * Dispatch dedup keyed by `${workItemId}::${target}`. Reset on restart —
     * which is intentional, the startup backfill re-reads the pool.
     *
     * 2026-05-15 Steve dogfood: previously keyed by `workItemId` alone, which
     * silently dropped re-dispatches after a WI was reassigned to a different
     * target. Concrete repro from crewly-2026-05-15.log:
     *   - 14:55:26 WI 20a778bc dispatched to strategy-ethan-c36c18bd
     *   - ethan never started work, claim expired, WI requeued
     *   - 15:36:55 AgentAutoClaim reassigned to crewly-orc
     *   - dispatchTo short-circuited (dispatched.has('20a778bc') === true)
     *   - orc never saw [CREWLY-DISPATCH], claim expired 13min later
     *   - loop repeated
     * The composite key fixes this: a re-target gets a fresh dispatch.
     * Within a (workItemId, target) pair the dedup still holds, so the
     * `workitem:queued` event and the AutoClaim post-claim hand-off don't
     * double-fire on the same target.
     */
    private readonly dispatched;
    /** Composite dedup key — workItem id alone is not enough (see above). */
    private dispatchKey;
    private constructor();
    static getInstance(): WorkItemDispatchSubscriber;
    static resetInstance(): void;
    /**
     * Wire the EventBus. Must be called before {@link start}.
     *
     * @param eventBusService - The shared EventBusService instance
     */
    initialize(eventBusService: {
        on: (event: string, handler: (...args: unknown[]) => void) => void;
    }): void;
    /**
     * Begin listening for `workitem:queued` events and schedule the one-shot
     * backfill that catches WIs that survived a backend restart.
     */
    start(): void;
    /**
     * Push a dispatch message to the WI's target session. Idempotent within
     * the process: a second call with the same WI id is a no-op.
     *
     * Returns true if a message was actually written, false if skipped.
     *
     * @param workItem - WorkItem to dispatch
     * @returns Whether the dispatch was actually performed
     */
    dispatchTo(workItem: WorkItem): Promise<boolean>;
    /**
     * Force-redeliver a WorkItem brief to its target, bypassing the in-process
     * dedup cache. Used by Hybrid Wake's `redeliver` strategy (2026-05-20):
     * when a queued WI lingers past threshold but its target is `active` and
     * `activeWorkItemCount === 0`, the original `dispatchTo` write likely
     * landed during claude-code's startup banner and was silently dropped.
     * Resetting the dedup key and reposting the brief gives the now-idle
     * agent a second chance to see it.
     *
     * Returns true if the redelivered write succeeded.
     *
     * @param workItem - WorkItem whose brief should be re-pushed
     * @returns Whether the redelivery write succeeded
     */
    redispatch(workItem: WorkItem): Promise<boolean>;
    /**
     * Fetch the WI by id and dispatch it. Called from the event listener.
     *
     * @param workItemId - The id from the workitem:queued event payload
     */
    private handleQueuedEvent;
    /**
     * Scan the pool once for queued+targeted WIs that lost their dispatch
     * push to the previous backend instance, and re-fire each. Runs ~25s
     * after start so AgentAutoClaim's recovery (which wakes offline agents)
     * has a chance to settle first.
     */
    private runStartupBackfill;
    /**
     * Compose the prompt the worker sees in their terminal. Format choices:
     *
     *   - Tagged prefix `[CREWLY-DISPATCH]` so the line is grep-able and
     *     visually distinguishable from natural-language ORC messages.
     *   - Includes WI id + title + type so the worker can sanity-check before
     *     running the claim command.
     *   - Closes with a runnable `poll-tasks` command — the worker can
     *     copy-paste or the harness can auto-execute. We deliberately do NOT
     *     pass the WI id to poll-tasks; the skill is role-driven and will
     *     pick whatever matches first, which preserves AutoClaim's scoring
     *     contract.
     *
     * @param workItem - WI being dispatched
     * @returns Multi-line message suitable for terminal write
     */
    private buildDispatchMessage;
}
//# sourceMappingURL=workitem-dispatch.subscriber.d.ts.map