import type { OpenBlock } from './feed/feed.js';
export type OutcomeKind = 'ticket' | 'pr' | 'worktree' | 'unassigned';
/** Stable attribution of a block (or agent) to one deliverable. */
export interface OutcomeRef {
    /** Map/JSON key, e.g. `ticket:RUSH-1125`, `pr:#534`, `worktree:headless-secrets`, `unassigned`. */
    key: string;
    kind: OutcomeKind;
    /** Human header label, e.g. `RUSH-1125`, `PR#534`, `headless-secrets`, `Unassigned`. */
    label: string;
}
/**
 * Signals used to derive an outcome. All optional — callers pass whatever they
 * already know (block fields, session meta, free-text questions).
 */
export interface OutcomeSignals {
    ticket?: string | null;
    pr?: string | null;
    worktreeSlug?: string | null;
    /** Branch name — scanned for `rush-1125`-style ticket slugs. */
    branch?: string | null;
    /** Epic / team label when no ticket/PR/worktree is known. */
    epic?: string | null;
    /** Free text (question headers + bodies) scanned for ticket/PR refs. */
    text?: string | null;
}
/**
 * Normalize a PR ref.
 * - Full GitHub URL → `owner/repo#N` (repo identity included)
 * - Bare `#123` / `PR#123` → `#123` (no repo known)
 */
export declare function normalizePrRef(raw: string | null | undefined): string | undefined;
/** `https://github.com/owner/repo/pull/N` → `owner/repo`. */
export declare function repoFromPrUrl(url: string | null | undefined): string | undefined;
/** Canonical ticket id (uppercase team key). */
export declare function normalizeTicketRef(raw: string | null | undefined): string | undefined;
/**
 * Derive the single outcome a block/agent belongs to.
 * Exactly one outcome per call — never ambiguous, never empty.
 */
export declare function deriveOutcome(signals: OutcomeSignals): OutcomeRef;
/** Join question headers + bodies into one scan target for ticket/PR detection. */
export declare function blockScanText(block: Pick<OpenBlock, 'questions'>): string;
/** Outcome for a stored open block (uses stamped fields + question text). */
export declare function outcomeForBlock(block: OpenBlock): OutcomeRef;
/** Per-outcome rollup used by the default `agents feed` view. */
export interface OutcomeGroup {
    outcome: OutcomeRef;
    blocks: OpenBlock[];
    counts: {
        /** Distinct mailbox ids under this outcome. */
        agents: number;
        /** Still open: unanswered and not hard-parked. */
        open: number;
        answered: number;
        parked: number;
    };
}
/**
 * Collapse blocks under their outcome. Order: needs-you outcomes first
 * (open count desc), then by label. Unassigned always last among ties of 0 open.
 */
export declare function groupBlocksByOutcome(blocks: OpenBlock[]): OutcomeGroup[];
/**
 * Stamp each block with its derived outcome for JSON consumers.
 * Does not mutate the input records.
 */
export declare function stampBlockOutcomes(blocks: OpenBlock[]): Array<OpenBlock & {
    outcome: OutcomeRef;
}>;
/**
 * True when every still-open block under the outcome asks the same question
 * (same cluster of header+text). Fan-out answers are only safe when this holds
 * — otherwise the operator must pick a specific agent.
 */
export declare function isUnambiguousOutcomeAnswer(group: OutcomeGroup): boolean;
/** Still-open blocks under an outcome (candidates for a fan-out reply). */
export declare function openBlocksForOutcome(group: OutcomeGroup): OpenBlock[];
/**
 * Lightweight session signals used to fill missing ticket/PR/worktree on a
 * block at list time (the publish hook may not have had them yet).
 */
export interface SessionOutcomeHint {
    sessionId?: string | null;
    agentId?: string | null;
    mailboxId?: string | null;
    ticketId?: string | null;
    prNumber?: number | null;
    prUrl?: string | null;
    worktreeSlug?: string | null;
    branch?: string | null;
    project?: string | null;
    host?: string | null;
    origin?: 'cli' | 'routine' | null;
    routineName?: string | null;
}
/**
 * Overlay session meta onto a block when the block itself is missing ticket/PR/
 * worktree. Never overwrites a field the block already carries.
 */
export declare function enrichBlockFromSession(block: OpenBlock, hint: SessionOutcomeHint): OpenBlock;
/**
 * Build a mailboxId → session-hint index and enrich every block. Pure.
 * Matching order: mailboxId, then sessionId, then agentId.
 */
export declare function enrichBlocksFromSessions(blocks: OpenBlock[], sessions: SessionOutcomeHint[]): OpenBlock[];
