/**
 * Per-tool contract for `request_oauth`.
 *
 * Bridge-mediated user-gesture tool. The agent emits a tool call, the
 * contract dispatches a `HostToolRequest`, the adapter posts a pending entry
 * into `pendingOAuthRequestsByThreadId`, `OAuthDropdown` opens the consent
 * popup via `platform.openOAuthFlow(...)`; tokens are stored by the SFAE
 * HTTP API after redirect; the resolution lands as
 * `{status: 'success', domain}` or `{status: 'cancelled'}`.
 *
 * The success prefix `"OAuth credentials stored"` is a wire contract: the
 * renderer pattern-matches on it, so this contract preserves the exact string.
 * Cancel and failure also return plain content-only ToolMessages.
 *
 * Approval is always `{ required: false }`. The user gesture surfaces in
 * the dropdown, not the host-tool approval gate.
 */
import { Type } from '@sinclair/typebox';
import { HostToolResultStatus, buildRendererArtifact } from '../../../contracts/host-tool.js';
import { withAbort } from '../../../utils/abort.js';
import { REQUEST_OAUTH } from '../tool-names.js';
export const REQUEST_OAUTH_KIND = 'requestOauth';
// ---------------------------------------------------------------------------
// Tool description (what the LLM sees) — verbatim port of backend.
// ---------------------------------------------------------------------------
export const REQUEST_OAUTH_TOOL_DESCRIPTION = 'Initiate an OAuth 2.0 authorization flow with the user. ' +
    'Use this when a service requires OAuth consent (e.g. Google APIs) ' +
    'instead of a static API key or access token. ' +
    "The user will be redirected to the provider's consent page in a popup. " +
    'After authorization, access and refresh tokens are stored securely. ' +
    'You will never see the actual tokens — they are resolved by safe at ' +
    'request time via the -ACCESS_TOKEN- placeholder.';
export const REQUEST_OAUTH_DOMAIN_DESCRIPTION = "The API domain requiring OAuth (e.g. 'googleapis.com')";
export const REQUEST_OAUTH_SCOPE_DESCRIPTION = "Space-separated OAuth scopes (e.g. 'https://www.googleapis.com/auth/gmail.readonly')";
export const REQUEST_OAUTH_MESSAGE_DESCRIPTION = 'Explanation shown to the user about why OAuth access is needed';
const requestOauthLlmSchema = Type.Object({
    domain: Type.String({
        description: REQUEST_OAUTH_DOMAIN_DESCRIPTION,
        minLength: 1
    }),
    scope: Type.String({
        description: REQUEST_OAUTH_SCOPE_DESCRIPTION,
        minLength: 1
    }),
    message: Type.Optional(Type.String({ description: REQUEST_OAUTH_MESSAGE_DESCRIPTION }))
});
// ---------------------------------------------------------------------------
// Validation
// ---------------------------------------------------------------------------
function isRequestOauthStatus(value) {
    return value === 'success' || value === 'cancelled';
}
function validateParams(value) {
    if (!value || typeof value !== 'object')
        return false;
    const candidate = value;
    return (typeof candidate.domain === 'string' &&
        typeof candidate.scope === 'string' &&
        (candidate.message === undefined || typeof candidate.message === 'string'));
}
function validateResultDetails(value) {
    if (value === undefined)
        return true;
    if (!value || typeof value !== 'object')
        return false;
    const candidate = value;
    return (isRequestOauthStatus(candidate.status) &&
        (candidate.domain === undefined || typeof candidate.domain === 'string'));
}
function normalizeResultDetails(value) {
    return value === undefined ? undefined : { ...value };
}
function getResultObservabilityFields(details) {
    if (!validateResultDetails(details) || details === undefined)
        return undefined;
    return { status: details.status };
}
function evaluateApproval() {
    return { required: false };
}
// ---------------------------------------------------------------------------
// Content formatters. Keep these exact strings stable for renderers and agent retries.
//
// Prefix "OAuth credentials stored" is load-bearing: the renderer
// pattern-matches on it. Do not rename.
// ---------------------------------------------------------------------------
/** Verbatim prefix the renderer looks for — keep in sync with backend. */
export const OAUTH_SUCCESS_CONTENT_PREFIX = 'OAuth credentials stored';
function successContent(domain) {
    return `${OAUTH_SUCCESS_CONTENT_PREFIX}: ${domain}`;
}
const CANCELLED_CONTENT = 'User cancelled the OAuth request.';
function failedContent(error) {
    return `OAuth request failed: ${error}`;
}
function formatResult(details, isError, userMessage, params) {
    // Failed/Cancelled: surface as error with the canonical userMessage, but
    // preserve the actual status in details so renderers can distinguish a
    // bridge cancel from a user gesture rather than collapsing both to
    // 'cancelled'.
    if (isError) {
        const text = details?.status === 'success' || details?.status === 'cancelled' || details === undefined
            ? (userMessage ?? 'OAuth request was cancelled.')
            : failedContent(userMessage ?? 'unknown');
        return {
            content: [{ type: 'text', text }],
            details: buildRendererArtifact({ status: (details?.status ?? 'cancelled') }, userMessage),
            isError: true
        };
    }
    if (details?.status === 'success') {
        const domain = details.domain ?? params.domain;
        return {
            content: [{ type: 'text', text: successContent(domain) }],
            details: { status: 'success', domain },
            isError: false
        };
    }
    return {
        content: [{ type: 'text', text: CANCELLED_CONTENT }],
        details: { status: 'cancelled' },
        isError: false
    };
}
// ---------------------------------------------------------------------------
// LLM tool factory
// ---------------------------------------------------------------------------
function createLlmTool(options) {
    void options.sessionPolicy;
    return {
        name: REQUEST_OAUTH,
        label: 'Request OAuth',
        isReadOnly: false,
        description: REQUEST_OAUTH_TOOL_DESCRIPTION,
        parameters: requestOauthLlmSchema,
        async execute(toolCallId, llmArgs, signal) {
            const args = llmArgs;
            const params = {
                domain: args.domain,
                scope: args.scope,
                ...(args.message !== undefined ? { message: args.message } : {})
            };
            const pendingHostToolRequests = options.pendingHostToolRequests;
            const request = {
                approval: evaluateApproval(),
                heartbeat: {
                    label: 'Requesting OAuth access...',
                    toolName: REQUEST_OAUTH
                },
                kind: REQUEST_OAUTH_KIND,
                params,
                requestId: toolCallId
            };
            const result = await withAbort(signal, async () => pendingHostToolRequests.request(request));
            const details = result.details;
            const isError = result.status === HostToolResultStatus.Failed ||
                result.status === HostToolResultStatus.Cancelled;
            return formatResult(details, isError, result.userMessage, params);
        }
    };
}
export const requestOauthContract = {
    kind: REQUEST_OAUTH_KIND,
    createLlmTool,
    evaluateApproval,
    getResultObservabilityFields,
    validateParams,
    validateResultDetails,
    normalizeResultDetails
};
//# sourceMappingURL=contract.js.map