/**
 * Wire shapes for streamed model requests/events that cross the worker
 * boundary, plus structural guards. See shortcutXL/docs/migration/spec.md.
 */
export const ShortcutXLModelStreamEffortLevel = {
    Low: 'low',
    Medium: 'medium',
    High: 'high',
    Max: 'max'
};
export const ShortcutXLModelStreamThinkingType = {
    Adaptive: 'adaptive',
    Off: 'off'
};
/**
 * Runtime operation labels forwarded as `X-LLM-Operation`.
 *
 * These are observability/billing categories, not endpoint names. Keep aligned
 * with shared `LLM_OPERATIONS` and backend `ALLOWED_LLM_OPERATIONS`.
 */
export const ShortcutXLLlmOperations = [
    'chat',
    'compaction',
    'suggestions',
    'summarize',
    'file_naming',
    'formula_trace',
    'code_preview',
    'sentiment_analysis'
];
export const ShortcutXLModelStreamMessageRole = {
    Ai: 'ai',
    Human: 'human',
    System: 'system',
    Tool: 'tool'
};
export const ShortcutXLAgentAssistantContentKind = {
    Text: 'text',
    Thinking: 'thinking',
    ToolCall: 'toolCall'
};
export const ShortcutXLModelStreamStopReason = {
    Aborted: 'aborted',
    Error: 'error',
    Length: 'length',
    Stop: 'stop',
    ToolUse: 'toolUse'
};
export const ShortcutXLModelStreamEventKind = {
    Done: 'done',
    Error: 'error',
    Start: 'start',
    TextDelta: 'textDelta',
    TextEnd: 'textEnd',
    TextStart: 'textStart',
    ThinkingDelta: 'thinkingDelta',
    ThinkingEnd: 'thinkingEnd',
    ThinkingRedacted: 'thinkingRedacted',
    ThinkingStart: 'thinkingStart',
    ToolCallDelta: 'toolCallDelta',
    ToolCallEnd: 'toolCallEnd',
    ToolCallStart: 'toolCallStart'
};
/**
 * Stage of a stream-fetch failure surfaced by the runtime's `onFetchTrace`
 * hook. Distinguishes failure modes that aren't visible in the generic
 * recovery telemetry (which collapses everything into `server_error`).
 */
export const ShortcutXLModelStreamFetchStage = {
    /**
     * `fetch()` rejected before any `Response` was received — DNS failure,
     * CORS preflight failure, network unreachable, captive portal, WebView
     * cookie-isolation block. The opaque "Failed to fetch" / "Load failed".
     */
    BeforeResponse: 'before_response',
    /** Got a `Response` but `status` was outside 2xx. */
    NonOkResponse: 'non_ok_response',
    /** Response started but the SSE read failed or idle-timed out mid-stream. */
    ResponseBody: 'response_body'
};
function isRecord(value) {
    return value != null && typeof value === 'object' && !Array.isArray(value);
}
function isNumber(value) {
    return typeof value === 'number' && Number.isFinite(value);
}
function isShortcutXLAgentUsage(value) {
    if (!isRecord(value) || !isRecord(value.cost)) {
        return false;
    }
    return (isNumber(value.input) &&
        isNumber(value.output) &&
        isNumber(value.cacheRead) &&
        isNumber(value.cacheWrite) &&
        isNumber(value.totalTokens) &&
        isNumber(value.cost.input) &&
        isNumber(value.cost.output) &&
        isNumber(value.cost.cacheRead) &&
        isNumber(value.cost.cacheWrite) &&
        isNumber(value.cost.total));
}
function isShortcutXLAgentAssistantContent(value) {
    if (!isRecord(value) || typeof value.type !== 'string') {
        return false;
    }
    switch (value.type) {
        case ShortcutXLAgentAssistantContentKind.Text:
            return typeof value.text === 'string';
        case ShortcutXLAgentAssistantContentKind.Thinking:
            return (typeof value.thinking === 'string' &&
                typeof value.thinkingSignature === 'string' &&
                typeof value.redacted === 'boolean');
        case ShortcutXLAgentAssistantContentKind.ToolCall:
            return (typeof value.id === 'string' && typeof value.name === 'string' && isRecord(value.arguments));
        default:
            return false;
    }
}
function isShortcutXLAgentToolCallContent(value) {
    return (isRecord(value) &&
        value.type === ShortcutXLAgentAssistantContentKind.ToolCall &&
        typeof value.id === 'string' &&
        typeof value.name === 'string' &&
        isRecord(value.arguments));
}
const STREAM_ERROR_PROVIDER_TYPES = new Set([
    'overloaded',
    'rate_limit',
    'unauthorized',
    'invalid_request',
    'internal',
    'network'
]);
export function isShortcutXLModelStreamErrorDetails(value) {
    if (!isRecord(value))
        return false;
    if (value.providerErrorType !== undefined &&
        !(typeof value.providerErrorType === 'string' &&
            STREAM_ERROR_PROVIDER_TYPES.has(value.providerErrorType))) {
        return false;
    }
    if (value.retryable !== undefined && typeof value.retryable !== 'boolean')
        return false;
    if (value.retryAfterMs !== undefined && typeof value.retryAfterMs !== 'number')
        return false;
    if (value.status !== undefined && typeof value.status !== 'number')
        return false;
    if (value.transportFailureStage !== undefined &&
        !Object.values(ShortcutXLModelStreamFetchStage).includes(value.transportFailureStage)) {
        return false;
    }
    return true;
}
/**
 * Default `ShortcutXLModelStreamErrorDetails` for a transport-level network
 * failure (fetch threw, connection dropped before HTTP response). Single
 * source of truth so callers don't hand-roll `{ providerErrorType: 'network',
 * retryable: true }` literals.
 */
export function networkErrorDetails(transportFailureStage) {
    return {
        providerErrorType: 'network',
        retryable: true,
        ...(transportFailureStage !== undefined ? { transportFailureStage } : {})
    };
}
/**
 * Drop an unrecognized `providerErrorType` and keep the rest. Used when the
 * backend ships a typo or a value this client doesn't yet understand — we
 * preserve `status`/`retryable`/`retryAfterMs` rather than discarding the
 * whole envelope.
 */
function validateOrStripProviderErrorType(details) {
    if (isShortcutXLModelStreamErrorDetails(details))
        return details;
    const rest = { ...details };
    delete rest.providerErrorType;
    return rest;
}
/**
 * Parse a snake_case SSE/HTTP error payload into the camelCase
 * `ShortcutXLModelStreamErrorDetails`. Returns `undefined` only when every
 * input field is absent; otherwise returns a (possibly partial) envelope.
 * `fallbackStatus` is stamped when the payload omits `status`.
 */
export function parseStreamErrorPayload(payload, fallbackStatus) {
    const details = {};
    if (typeof payload?.provider_error_type === 'string') {
        details.providerErrorType = payload.provider_error_type;
    }
    if (typeof payload?.retryable === 'boolean')
        details.retryable = payload.retryable;
    if (typeof payload?.retry_after_ms === 'number')
        details.retryAfterMs = payload.retry_after_ms;
    if (typeof payload?.status === 'number')
        details.status = payload.status;
    if (fallbackStatus !== undefined && details.status === undefined)
        details.status = fallbackStatus;
    if (Object.keys(details).length === 0)
        return undefined;
    return validateOrStripProviderErrorType(details);
}
/**
 * Extract the structured error payload from an HTTP error body. The backend
 * wraps it as either `{detail: {error: {...}}}` (FastAPI HTTPException) or
 * `{error: {...}}` — both shapes are surfaced as the inner `error` record.
 */
export function extractStructuredHttpErrorPayload(value) {
    if (!isRecord(value))
        return undefined;
    const detail = value.detail;
    if (isRecord(detail) && isRecord(detail.error))
        return detail.error;
    if (isRecord(value.error))
        return value.error;
    return undefined;
}
export function isShortcutXLAgentAssistantMessage(value) {
    return (isRecord(value) &&
        value.role === 'assistant' &&
        typeof value.api === 'string' &&
        typeof value.provider === 'string' &&
        typeof value.model === 'string' &&
        typeof value.timestamp === 'number' &&
        typeof value.stopReason === 'string' &&
        Object.values(ShortcutXLModelStreamStopReason).includes(value.stopReason) &&
        Array.isArray(value.content) &&
        value.content.every((item) => isShortcutXLAgentAssistantContent(item)) &&
        (value.errorDetails === undefined || isShortcutXLModelStreamErrorDetails(value.errorDetails)) &&
        (value.errorMessage === undefined || typeof value.errorMessage === 'string') &&
        isShortcutXLAgentUsage(value.usage));
}
function isShortcutXLModelStreamToolCall(value) {
    return (isRecord(value) &&
        typeof value.id === 'string' &&
        typeof value.name === 'string' &&
        typeof value.arguments === 'string');
}
function isShortcutXLModelStreamMessage(value) {
    return (isRecord(value) &&
        typeof value.role === 'string' &&
        Object.values(ShortcutXLModelStreamMessageRole).includes(value.role) &&
        typeof value.content === 'string' &&
        (value.toolCallId === undefined || typeof value.toolCallId === 'string') &&
        (value.thinking === undefined || typeof value.thinking === 'string') &&
        (value.thinkingSignature === undefined || typeof value.thinkingSignature === 'string') &&
        (value.toolCalls === undefined ||
            (Array.isArray(value.toolCalls) &&
                value.toolCalls.every((toolCall) => isShortcutXLModelStreamToolCall(toolCall)))));
}
function isShortcutXLModelStreamTool(value) {
    return (isRecord(value) &&
        typeof value.name === 'string' &&
        typeof value.description === 'string' &&
        isRecord(value.parameters));
}
export function isShortcutXLModelStreamRequest(value) {
    return (isRecord(value) &&
        typeof value.requestId === 'string' &&
        typeof value.model === 'string' &&
        Array.isArray(value.messages) &&
        value.messages.every((message) => isShortcutXLModelStreamMessage(message)) &&
        (value.tools === undefined ||
            (Array.isArray(value.tools) &&
                value.tools.every((tool) => isShortcutXLModelStreamTool(tool)))) &&
        (value.agentMode === undefined || typeof value.agentMode === 'string') &&
        (value.fileAttachmentIds === undefined ||
            (Array.isArray(value.fileAttachmentIds) &&
                value.fileAttachmentIds.every((fileId) => typeof fileId === 'string'))) &&
        (value.initialFileId === undefined || typeof value.initialFileId === 'string') &&
        (value.interactionMode === undefined || typeof value.interactionMode === 'string') &&
        (value.llmOperation === undefined ||
            ShortcutXLLlmOperations.includes(value.llmOperation)) &&
        (value.maxTokens === undefined || isNumber(value.maxTokens)) &&
        (value.userMessageId === undefined || typeof value.userMessageId === 'string') &&
        (value.effortLevel === undefined ||
            Object.values(ShortcutXLModelStreamEffortLevel).includes(value.effortLevel)) &&
        (value.thinkingType === undefined ||
            Object.values(ShortcutXLModelStreamThinkingType).includes(value.thinkingType)) &&
        (value.speed === undefined || value.speed === 'fast' || value.speed === 'standard'));
}
export function isShortcutXLModelStreamEvent(value) {
    if (!isRecord(value) || typeof value.kind !== 'string') {
        return false;
    }
    const hasPartial = isShortcutXLAgentAssistantMessage(value.partial);
    if (!hasPartial) {
        return false;
    }
    switch (value.kind) {
        case ShortcutXLModelStreamEventKind.Start:
            return true;
        case ShortcutXLModelStreamEventKind.TextStart:
        case ShortcutXLModelStreamEventKind.TextEnd:
        case ShortcutXLModelStreamEventKind.ThinkingStart:
        case ShortcutXLModelStreamEventKind.ThinkingRedacted:
        case ShortcutXLModelStreamEventKind.ToolCallStart:
            return isNumber(value.contentIndex);
        case ShortcutXLModelStreamEventKind.TextDelta:
        case ShortcutXLModelStreamEventKind.ThinkingDelta:
        case ShortcutXLModelStreamEventKind.ToolCallDelta:
            return isNumber(value.contentIndex) && typeof value.delta === 'string';
        case ShortcutXLModelStreamEventKind.ThinkingEnd:
            return isNumber(value.contentIndex) && typeof value.content === 'string';
        case ShortcutXLModelStreamEventKind.ToolCallEnd:
            return isNumber(value.contentIndex) && isShortcutXLAgentToolCallContent(value.toolCall);
        case ShortcutXLModelStreamEventKind.Done:
            return (isShortcutXLAgentAssistantMessage(value.message) &&
                (value.reason === ShortcutXLModelStreamStopReason.Stop ||
                    value.reason === ShortcutXLModelStreamStopReason.ToolUse ||
                    value.reason === ShortcutXLModelStreamStopReason.Length));
        case ShortcutXLModelStreamEventKind.Error:
            return (isShortcutXLAgentAssistantMessage(value.error) &&
                (value.reason === ShortcutXLModelStreamStopReason.Aborted ||
                    value.reason === ShortcutXLModelStreamStopReason.Error));
        default:
            return false;
    }
}
//# sourceMappingURL=model-stream.js.map