/**
 * Per-tool contract for `get_tool_info` — first hop of the lazy-load pair
 * with `execute_tool`.
 *
 * Side effect: marks the tool as fetched on the shared `SessionToolInfoState`
 * so `execute_tool`'s gate can pass. Both contracts must share the same instance.
 */
import { Type } from '@sinclair/typebox';
import { buildRendererArtifact } from '../../../contracts/host-tool.js';
import { CREATE_WEBSITE_TOOL_SUMMARY } from '../create-website/index.js';
import { SANDBOX_LLM_TOOL_SUMMARY } from '../sandbox-llm/index.js';
import { GET_TOOL_INFO } from '../tool-names.js';
import { WEB_CRAWL_TOOL_SUMMARY } from '../web-crawl/index.js';
import { WEB_SEARCH_TOOL_SUMMARY } from '../web-search/index.js';
import { EXECUTABLE_TOOL_NAMES as SHARED_EXECUTABLE_TOOL_NAMES, executableToolNameLiteralUnion } from './shared.js';
export const GET_TOOL_INFO_KIND = 'getToolInfo';
/** The 4 executable tool names `get_tool_info` / `execute_tool` gate. */
export const EXECUTABLE_TOOL_NAMES = SHARED_EXECUTABLE_TOOL_NAMES;
/** Listing summaries shown in the `get_tool_info` description. */
export const EXECUTABLE_TOOL_SUMMARIES = {
    web_search: WEB_SEARCH_TOOL_SUMMARY,
    web_crawl: WEB_CRAWL_TOOL_SUMMARY,
    sandbox_llm: SANDBOX_LLM_TOOL_SUMMARY,
    create_website: CREATE_WEBSITE_TOOL_SUMMARY
};
// Strip runtime-injected fields the LLM should not supply.
const INJECTED_PARAM_NAMES = new Set(['tool_call_id', 'config', 'state']);
function stripInjectedProperties(properties, required) {
    const filteredProperties = {};
    for (const [name, spec] of Object.entries(properties)) {
        if (!INJECTED_PARAM_NAMES.has(name))
            filteredProperties[name] = spec;
    }
    const filteredRequired = required.filter((name) => !INJECTED_PARAM_NAMES.has(name));
    return { properties: filteredProperties, required: filteredRequired };
}
// ---------------------------------------------------------------------------
// LLM-facing description + schema
// ---------------------------------------------------------------------------
const INTRO = 'Get detailed parameter information for a specific tool in JSON schema format.\n\n' +
    'Use this to look up for schemas/guidelines, then execute_tool to call them. You must always call this first before running execute_tool.';
const TRAILER = "Returns a JSON schema with the tool's name, description, and parameters object containing:\n" +
    '- properties: Object describing each parameter (type, description, etc.)\n' +
    '- required: Array of required parameter names\n\n' +
    'Use this to understand the tool API before calling execute_tool.';
/** Build the LLM-visible description; tool order is sorted for stability. */
export function buildGetToolInfoDescription(summaries = EXECUTABLE_TOOL_SUMMARIES, toolNames = EXECUTABLE_TOOL_NAMES) {
    const lines = [...toolNames]
        .slice()
        .sort()
        .map((name) => `- ${name}: ${summaries[name]}`)
        .join('\n');
    return `${INTRO}\n\nAvailable:\n${lines}\n\n${TRAILER}`;
}
export const GET_TOOL_INFO_TOOL_DESCRIPTION = buildGetToolInfoDescription();
export const GET_TOOL_INFO_TOOL_NAME_DESCRIPTION = "The name of the tool to get parameter info about (e.g., 'web_search', 'web_crawl')";
function buildGetToolInfoLlmSchema(toolNames) {
    return Type.Object({
        tool_name: executableToolNameLiteralUnion(toolNames, {
            description: GET_TOOL_INFO_TOOL_NAME_DESCRIPTION,
            errorPrefix: 'get_tool_info'
        })
    });
}
// ---------------------------------------------------------------------------
// Validation + normalization
// ---------------------------------------------------------------------------
export function isExecutableToolName(value) {
    return typeof value === 'string' && EXECUTABLE_TOOL_NAMES.includes(value);
}
function validateParams(value) {
    if (!value || typeof value !== 'object')
        return false;
    const candidate = value;
    return isExecutableToolName(candidate.tool_name);
}
function validateResultDetails(value) {
    if (value === undefined)
        return true;
    if (!value || typeof value !== 'object')
        return false;
    const candidate = value;
    return ((candidate.hidden === undefined || typeof candidate.hidden === 'boolean') &&
        (candidate.tool_name === undefined || isExecutableToolName(candidate.tool_name)));
}
function normalizeResultDetails(value) {
    return value === undefined ? undefined : { ...value };
}
function getResultObservabilityFields(details) {
    if (!validateResultDetails(details) || details === undefined)
        return undefined;
    const metadata = {
        ...(details.hidden !== undefined ? { hidden: details.hidden } : {}),
        ...(details.tool_name !== undefined ? { tool_name: details.tool_name } : {})
    };
    return Object.keys(metadata).length > 0 ? metadata : undefined;
}
function evaluateApproval() {
    return { required: false };
}
function createLlmTool(options) {
    const availableToolNames = options.contractsByName === undefined
        ? [...EXECUTABLE_TOOL_NAMES]
        : EXECUTABLE_TOOL_NAMES.filter((name) => options.contractsByName?.[name] !== undefined);
    return {
        name: GET_TOOL_INFO,
        label: 'Get tool info',
        isReadOnly: true,
        description: buildGetToolInfoDescription(EXECUTABLE_TOOL_SUMMARIES, availableToolNames),
        parameters: buildGetToolInfoLlmSchema(availableToolNames),
        getResultObservabilityFields,
        async execute(_toolCallId, llmArgs) {
            const args = llmArgs;
            const toolName = args.tool_name;
            const targetContract = options.contractsByName?.[toolName];
            if (!targetContract) {
                // Defensive — schema enum should prevent this.
                const errorBody = {
                    error: `Tool '${toolName}' not found`,
                    available_tools: availableToolNames
                };
                return {
                    content: [{ type: 'text', text: JSON.stringify(errorBody, null, 2) }],
                    details: buildRendererArtifact({ hidden: true, tool_name: toolName }, errorBody.error),
                    isError: false
                };
            }
            // Throwaway definition built only to read its `.description` and `.parameters`.
            const def = targetContract.createLlmTool({
                pendingHostToolRequests: options.pendingHostToolRequests,
                sessionPolicy: options.sessionPolicy
            });
            const rawSchema = def.parameters;
            const { properties, required } = stripInjectedProperties(rawSchema.properties ?? {}, rawSchema.required ?? []);
            const schema = {
                name: def.name,
                description: def.description,
                parameters: { type: 'object', properties, required }
            };
            options.sessionState.markFetched(toolName);
            return {
                content: [{ type: 'text', text: JSON.stringify(schema, null, 2) }],
                // Visible in the transcript (not hidden) so the user sees what the agent looked up.
                details: { tool_name: toolName },
                isError: false
            };
        }
    };
}
export const getToolInfoContract = {
    kind: GET_TOOL_INFO_KIND,
    createLlmTool,
    evaluateApproval,
    getResultObservabilityFields,
    validateParams,
    validateResultDetails,
    normalizeResultDetails
};
//# sourceMappingURL=contract.js.map