/**
 * Shared decoder for the `decision_resolved` leading frame the
 * `confirm-tool` route emits as the first chunk of its SSE response.
 *
 * Wire format (matches `use-embedded-chat.ts:270-394`):
 *   - Frames are JSON objects separated by `\0` (NUL).
 *   - `\x1E` (record separator) marks the transition from leading
 *     metadata frames into the text body (chat path).
 *   - For the tickets-UI path, the request body carries `messages: []`
 *     which makes phase-2 (text body) structurally unreachable —
 *     `confirm-tool/route.ts:569` skips the `runDocsChat` call. So the
 *     stream contains exactly ONE frame (`decision_resolved`) followed
 *     by EOF.
 *
 * This helper drains the stream defensively (reads all remaining bytes
 * after the leading frame so the server-side `composed.start()` doesn't
 * hang on a half-closed connection) and returns the first decoded
 * `decision_resolved` frame. Throws on:
 *   - Empty body
 *   - First frame is not `decision_resolved`
 *   - Malformed JSON before the first `\0`
 *
 * Migration note: `use-embedded-chat.ts` still inlines the full decoder
 * including phase-2 text + trailing usage parsing. A future refactor
 * could move the leading-frame loop here and have the chat shell
 * compose with that — out of scope for this PR.
 */
export interface DecisionResolvedFrame {
    kind: 'decision_resolved';
    ok: boolean;
    action: 'approved' | 'rejected';
    toolName?: string;
    willAutoContinue?: boolean;
    proposalId?: string;
    /** Approve-path result envelope. `mirror_synced=false` means the
     *  HubSpot REST call succeeded but the local mirror upsert lagged —
     *  callers should optimistic-render and schedule a delayed refetch. */
    result?: {
        ticket_id?: string;
        status?: string | null;
        mirror_synced?: boolean;
    } | null;
    card?: {
        type?: string;
        marker?: string;
        ref?: unknown;
    } | null;
    receiptText?: string;
}
/**
 * Read the leading `decision_resolved` frame from a confirm-tool SSE
 * response. Drains the stream to end-of-file before resolving so the
 * server doesn't sit on a half-closed socket.
 *
 * @throws {Error} when the body is empty, the first frame is not
 *   `decision_resolved`, or the leading JSON is malformed.
 */
export declare function readLeadingDecisionFrame(response: Response): Promise<DecisionResolvedFrame>;
//# sourceMappingURL=sse-decision-frame.d.ts.map