/**
 * Command/event surface of `EmbeddedAgentRuntime` — the contract clients
 * drive and observe. See shortcutXL/docs/migration/spec.md.
 */
import { isAgentSessionContext } from './agent-session-store.js';
import { isAgentSnapshot } from './agent-snapshot.js';
import { isAgentFileUpload } from './agent-uploads.js';
import { isFocusedRange, isRangeRestriction } from './user-turn-enrichments.js';
export const AgentCommandKind = {
    Abort: 'abort',
    Configure: 'configure',
    Dispose: 'dispose',
    SubmitPrompt: 'submitPrompt'
};
export const AgentEventKind = {
    ChildAgentEvent: 'childAgentEvent',
    HostToolRequest: 'hostToolRequest',
    HostToolResultApplied: 'hostToolResultApplied',
    ConfigurationUpdated: 'configurationUpdated',
    FatalError: 'fatalError',
    /** Fired when the post-turn suggestions runner starts. */
    FollowupSuggestionsStarted: 'followupSuggestionsStarted',
    FollowupSuggestionsReady: 'followupSuggestionsReady',
    /** §14 — fired when the post-turn suggestions runner errors. */
    FollowupSuggestionsFailed: 'followupSuggestionsFailed',
    MainMessageStream: 'mainMessageStream',
    RunStatsUpdated: 'runStatsUpdated',
    SessionContextUpdated: 'sessionContextUpdated',
    SnapshotUpdated: 'snapshotUpdated',
    Telemetry: 'telemetry'
};
export const AgentProtocolErrorKind = {
    HostToolRequestProtocol: 'hostToolRequestProtocol',
    ControllerProtocol: 'controllerProtocol',
    WorkerPortProtocol: 'workerPortProtocol'
};
export const AgentMainMessageStreamEventType = {
    End: 'end',
    Start: 'start',
    TextDelta: 'text_delta',
    ThinkingDelta: 'thinking_delta',
    ToolCallEnd: 'toolcall_end'
};
export const AGENT_TOOL_IN_FLIGHT_DISPLAY_DELAY_MS = 30_000;
export const AgentTelemetryEventType = {
    OperationFinished: 'operation_finished',
    OperationStarted: 'operation_started',
    RecoveryFailed: 'recovery_failed',
    RecoveryFallback: 'recovery_fallback',
    RecoveryStarted: 'recovery_started',
    RecoverySucceeded: 'recovery_succeeded',
    RunStarted: 'run_started'
};
export const AgentTelemetryTerminalPhase = {
    Cancelled: 'cancelled',
    Completed: 'completed',
    Failed: 'failed'
};
/** Type guard for a per-model run-stats usage record received cross-process. */
function isRunStatsModelUsage(value) {
    if (!value || typeof value !== 'object')
        return false;
    const u = value;
    return (typeof u.input_tokens === 'number' &&
        typeof u.output_tokens === 'number' &&
        typeof u.total_tokens === 'number' &&
        typeof u.cache_read_input_tokens === 'number' &&
        typeof u.cache_creation_input_tokens === 'number');
}
/** Type guard for a `model -> usage` map of run-stats token usage. */
function isRunStatsTokenUsageByModel(value) {
    if (!value || typeof value !== 'object')
        return false;
    for (const v of Object.values(value)) {
        if (!isRunStatsModelUsage(v))
            return false;
    }
    return true;
}
/** Type guard for a single completed-run RunStatsEntry payload. */
function isRunStatsEntry(value) {
    if (!value || typeof value !== 'object')
        return false;
    const e = value;
    return (typeof e.run_id === 'string' &&
        typeof e.run_number === 'number' &&
        typeof e.started_at === 'string' &&
        typeof e.ended_at === 'string' &&
        typeof e.duration_ms === 'number' &&
        isRunStatsTokenUsageByModel(e.usage) &&
        (e.last_message_id === undefined || typeof e.last_message_id === 'string') &&
        (e.model_name === undefined || typeof e.model_name === 'string') &&
        (e.effort_level === undefined || typeof e.effort_level === 'string') &&
        (e.speed === undefined || typeof e.speed === 'string') &&
        (e.context_size === undefined || typeof e.context_size === 'string'));
}
/**
 * Cross-process validator. The event ferries through `postMessage`, so the
 * receiver MUST verify the per-model usage shape before handing it to
 * `useRunStatsStore.updateFromBackend` — a malformed payload from a
 * compromised or stale worker would otherwise corrupt the store.
 */
export function isAgentRunStatsUpdatedEvent(value) {
    if (!value || typeof value !== 'object')
        return false;
    const ev = value;
    return (ev.kind === AgentEventKind.RunStatsUpdated &&
        isRunStatsTokenUsageByModel(ev.threadTokenUsage) &&
        isRunStatsEntry(ev.runEntry));
}
/** Project an AgentEvent into a structured-clone-safe form using the provided tool registry. */
export function toStructuredCloneSafeAgentEvent(event, registry) {
    switch (event.kind) {
        case AgentEventKind.HostToolResultApplied:
            return {
                ...event,
                result: registry.normalizeHostToolResult(event.result)
            };
        case AgentEventKind.ChildAgentEvent:
        case AgentEventKind.HostToolRequest:
        case AgentEventKind.ConfigurationUpdated:
        case AgentEventKind.FatalError:
        case AgentEventKind.FollowupSuggestionsStarted:
        case AgentEventKind.FollowupSuggestionsReady:
        case AgentEventKind.FollowupSuggestionsFailed:
        case AgentEventKind.MainMessageStream:
        case AgentEventKind.RunStatsUpdated:
        case AgentEventKind.SessionContextUpdated:
        case AgentEventKind.SnapshotUpdated:
        case AgentEventKind.Telemetry:
            return event;
    }
}
/** Type guard for the wire-level `AgentConfiguration` shape. */
export function isAgentConfiguration(value) {
    if (!value || typeof value !== 'object') {
        return false;
    }
    const candidate = value;
    return (typeof candidate.systemPrompt === 'string' &&
        Array.isArray(candidate.toolNames) &&
        candidate.toolNames.every((toolName) => typeof toolName === 'string') &&
        (candidate.agentMode === undefined || typeof candidate.agentMode === 'string'));
}
/** Type guard validating any AgentCommand variant received from the host. */
export function isAgentCommand(value) {
    if (!value || typeof value !== 'object')
        return false;
    const candidate = value;
    switch (candidate.kind) {
        case AgentCommandKind.Configure:
            return isAgentConfiguration(candidate.configuration);
        case AgentCommandKind.SubmitPrompt: {
            const submit = candidate;
            if (typeof submit.prompt !== 'string')
                return false;
            const userMessageId = submit.submitOptions?.userMessageId;
            if (userMessageId !== undefined && typeof userMessageId !== 'string')
                return false;
            const interactionMode = submit.submitOptions?.interactionMode;
            if (interactionMode !== undefined && typeof interactionMode !== 'string')
                return false;
            const uploads = submit.submitOptions?.fileUploads;
            if (uploads !== undefined) {
                if (!Array.isArray(uploads))
                    return false;
                if (!uploads.every(isAgentFileUpload))
                    return false;
            }
            const workbookSummary = submit.submitOptions?.workbookSummary;
            if (workbookSummary !== undefined && typeof workbookSummary !== 'string')
                return false;
            const focusedRange = submit.submitOptions?.focusedRange;
            if (focusedRange !== undefined && !isFocusedRange(focusedRange))
                return false;
            const rangeRestrictions = submit.submitOptions?.rangeRestrictions;
            if (rangeRestrictions !== undefined) {
                if (!Array.isArray(rangeRestrictions))
                    return false;
                if (!rangeRestrictions.every(isRangeRestriction))
                    return false;
            }
            const contextReset = submit.submitOptions?.contextReset;
            if (contextReset !== undefined && typeof contextReset !== 'boolean')
                return false;
            const skipTools = submit.submitOptions?.skipTools;
            if (skipTools !== undefined) {
                if (!Array.isArray(skipTools))
                    return false;
                if (!skipTools.every((toolName) => typeof toolName === 'string'))
                    return false;
            }
            return true;
        }
        case AgentCommandKind.Abort:
        case AgentCommandKind.Dispose:
            return true;
        default:
            return false;
    }
}
/** Type guard for a RuntimeErrorInfo payload carried on fatal-error events. */
export function isRuntimeErrorInfo(value) {
    if (!value || typeof value !== 'object')
        return false;
    const candidate = value;
    return (typeof candidate.kind === 'string' &&
        Object.values(AgentProtocolErrorKind).includes(candidate.kind) &&
        typeof candidate.message === 'string' &&
        (candidate.requestId === undefined || typeof candidate.requestId === 'string'));
}
/** Type guard for any AgentTelemetryPayload variant. */
function isAgentTelemetryPayload(value) {
    if (!value || typeof value !== 'object')
        return false;
    const candidate = value;
    switch (candidate.type) {
        case AgentTelemetryEventType.RunStarted:
            return (candidate.runId === undefined ||
                typeof candidate.runId === 'string');
        case AgentTelemetryEventType.OperationStarted: {
            const ev = candidate;
            return (typeof ev.opId === 'string' &&
                typeof ev.opKind === 'string' &&
                ['compaction', 'llm', 'tool', 'host_tool', 'subagent'].includes(ev.opKind) &&
                typeof ev.startedAt === 'number' &&
                (ev.runId === undefined || typeof ev.runId === 'string') &&
                (ev.toolCallId === undefined || typeof ev.toolCallId === 'string') &&
                (ev.toolName === undefined || typeof ev.toolName === 'string'));
        }
        case AgentTelemetryEventType.OperationFinished: {
            const ev = candidate;
            return (typeof ev.opId === 'string' &&
                typeof ev.opKind === 'string' &&
                ['compaction', 'llm', 'tool', 'host_tool', 'subagent'].includes(ev.opKind) &&
                typeof ev.phase === 'string' &&
                Object.values(AgentTelemetryTerminalPhase).includes(ev.phase) &&
                typeof ev.startedAt === 'number' &&
                typeof ev.endedAt === 'number' &&
                (ev.runId === undefined || typeof ev.runId === 'string') &&
                (ev.toolCallId === undefined || typeof ev.toolCallId === 'string') &&
                (ev.toolName === undefined || typeof ev.toolName === 'string') &&
                (ev.metadata === undefined || isRecord(ev.metadata)) &&
                (ev.errorMessage === undefined || typeof ev.errorMessage === 'string'));
        }
        case AgentTelemetryEventType.RecoveryStarted: {
            const ev = candidate;
            return (typeof ev.category === 'string' &&
                typeof ev.attempt === 'number' &&
                typeof ev.maxAttempts === 'number' &&
                typeof ev.delayMs === 'number' &&
                typeof ev.errorMessage === 'string' &&
                (ev.runId === undefined || typeof ev.runId === 'string'));
        }
        case AgentTelemetryEventType.RecoverySucceeded: {
            const ev = candidate;
            return (typeof ev.category === 'string' &&
                typeof ev.attempt === 'number' &&
                (ev.runId === undefined || typeof ev.runId === 'string'));
        }
        case AgentTelemetryEventType.RecoveryFailed: {
            const ev = candidate;
            return (typeof ev.category === 'string' &&
                typeof ev.attempt === 'number' &&
                (ev.finalError === undefined || typeof ev.finalError === 'string') &&
                (ev.runId === undefined || typeof ev.runId === 'string'));
        }
        case AgentTelemetryEventType.RecoveryFallback: {
            const ev = candidate;
            return (typeof ev.category === 'string' &&
                typeof ev.fallbackModel === 'string' &&
                (ev.runId === undefined || typeof ev.runId === 'string'));
        }
        default:
            return false;
    }
}
/** True if `value` is a non-array object record. */
function isRecord(value) {
    return typeof value === 'object' && value !== null && !Array.isArray(value);
}
/** True if the value's `runId` field is absent or a string. */
function hasOptionalRunId(value) {
    return value.runId === undefined || typeof value.runId === 'string';
}
/** Type guard for any AgentMainMessageStreamPayload variant. */
function isAgentMainMessageStreamPayload(value) {
    if (!isRecord(value))
        return false;
    if (typeof value.streamId !== 'string' || !hasOptionalRunId(value))
        return false;
    switch (value.type) {
        case AgentMainMessageStreamEventType.Start:
            return typeof value.messageId === 'string' && value.messageId.length > 0;
        case AgentMainMessageStreamEventType.TextDelta:
        case AgentMainMessageStreamEventType.ThinkingDelta:
            return typeof value.contentIndex === 'number' && typeof value.delta === 'string';
        case AgentMainMessageStreamEventType.ToolCallEnd:
            return (typeof value.contentIndex === 'number' &&
                typeof value.toolCallId === 'string' &&
                typeof value.toolName === 'string' &&
                isRecord(value.args));
        case AgentMainMessageStreamEventType.End:
            return true;
        default:
            return false;
    }
}
/** Type guard validating any AgentEvent variant arriving cross-process. */
export function isAgentEvent(value, registry) {
    if (!value || typeof value !== 'object')
        return false;
    const candidate = value;
    switch (candidate.kind) {
        case AgentEventKind.ChildAgentEvent: {
            const ev = candidate;
            return (typeof ev.toolCallId === 'string' &&
                typeof ev.subagentType === 'string' &&
                typeof ev.subagentName === 'string' &&
                (ev.subagentModel === undefined || typeof ev.subagentModel === 'string') &&
                (ev.subagentThinkingLevel === undefined || typeof ev.subagentThinkingLevel === 'string') &&
                ev.event !== undefined);
        }
        case AgentEventKind.HostToolRequest:
            return registry.isHostToolRequest(candidate.request);
        case AgentEventKind.HostToolResultApplied:
            return registry.isHostToolResult(candidate.result);
        case AgentEventKind.ConfigurationUpdated:
            return isAgentConfiguration(candidate.configuration);
        case AgentEventKind.FatalError:
            return isRuntimeErrorInfo(candidate.error);
        case AgentEventKind.SessionContextUpdated:
            return isAgentSessionContext(candidate.context);
        case AgentEventKind.SnapshotUpdated:
            return isAgentSnapshot(candidate.snapshot);
        case AgentEventKind.RunStatsUpdated:
            return isAgentRunStatsUpdatedEvent(candidate);
        case AgentEventKind.FollowupSuggestionsStarted:
            return true;
        case AgentEventKind.FollowupSuggestionsReady: {
            const ev = candidate;
            if (!Array.isArray(ev.suggestions))
                return false;
            if (ev.suggestions.length < 1 || ev.suggestions.length > 3)
                return false;
            if (!ev.suggestions.every((s) => typeof s === 'string'))
                return false;
            if (ev.skillProposal !== undefined && ev.skillProposal !== null) {
                if (typeof ev.skillProposal !== 'object')
                    return false;
                if (typeof ev.skillProposal.name !== 'string')
                    return false;
                if (typeof ev.skillProposal.description !== 'string')
                    return false;
                if (typeof ev.skillProposal.prompt !== 'string')
                    return false;
            }
            return true;
        }
        case AgentEventKind.FollowupSuggestionsFailed:
            return typeof candidate.message === 'string';
        case AgentEventKind.MainMessageStream:
            return isAgentMainMessageStreamPayload(candidate.stream);
        case AgentEventKind.Telemetry:
            return isAgentTelemetryPayload(candidate.telemetry);
        default:
            return false;
    }
}
//# sourceMappingURL=agent-api.js.map