/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import { isTool } from '../index.js';
import { SHELL_TOOL_NAMES } from './shell-utils.js';
import levenshtein from 'fast-levenshtein';
import { ApprovalMode } from '../policy/types.js';
import { CoreToolCallStatus, } from '../scheduler/types.js';
import { ASK_USER_DISPLAY_NAME, WRITE_FILE_DISPLAY_NAME, EDIT_DISPLAY_NAME, } from '../tools/tool-names.js';
/**
 * Validates if an object is a ToolCallResponseInfo.
 */
export function isToolCallResponseInfo(data) {
    return (typeof data === 'object' &&
        data !== null &&
        'callId' in data &&
        'responseParts' in data);
}
/**
 * Determines if a tool call should be hidden from the standard tool history UI.
 *
 * We hide tools in several cases:
 * 1. Tool calls that have a parent, as they are "internal" to another tool (e.g. subagent).
 * 2. Ask User tools that are in progress, displayed via specialized UI.
 * 3. Ask User tools that errored without result display, typically param
 *    validation errors that the agent automatically recovers from.
 * 4. WriteFile and Edit tools when in Plan Mode, redundant because the
 *    resulting plans are displayed separately upon exiting plan mode.
 */
export function shouldHideToolCall(params) {
    const { displayName, status, approvalMode, hasResultDisplay, parentCallId } = params;
    if (parentCallId) {
        return true;
    }
    switch (displayName) {
        case ASK_USER_DISPLAY_NAME:
            switch (status) {
                case CoreToolCallStatus.Scheduled:
                case CoreToolCallStatus.Validating:
                case CoreToolCallStatus.Executing:
                case CoreToolCallStatus.AwaitingApproval:
                    return true;
                case CoreToolCallStatus.Error:
                    return !hasResultDisplay;
                default:
                    return false;
            }
        case WRITE_FILE_DISPLAY_NAME:
        case EDIT_DISPLAY_NAME:
            return approvalMode === ApprovalMode.PLAN;
        default:
            return false;
    }
}
/**
 * Generates a suggestion string for a tool name that was not found in the registry.
 * It finds the closest matches based on Levenshtein distance.
 * @param unknownToolName The tool name that was not found.
 * @param allToolNames The list of all available tool names.
 * @param topN The number of suggestions to return. Defaults to 3.
 * @returns A suggestion string like " Did you mean 'tool'?" or " Did you mean one of: 'tool1', 'tool2'?", or an empty string if no suggestions are found.
 */
export function getToolSuggestion(unknownToolName, allToolNames, topN = 3) {
    const matches = allToolNames.map((toolName) => ({
        name: toolName,
        distance: levenshtein.get(unknownToolName, toolName),
    }));
    matches.sort((a, b) => a.distance - b.distance);
    const topNResults = matches.slice(0, topN);
    if (topNResults.length === 0) {
        return '';
    }
    const suggestedNames = topNResults
        .map((match) => `"${match.name}"`)
        .join(', ');
    if (topNResults.length > 1) {
        return ` Did you mean one of: ${suggestedNames}?`;
    }
    else {
        return ` Did you mean ${suggestedNames}?`;
    }
}
/**
 * Checks if a tool invocation matches any of a list of patterns.
 *
 * @param toolOrToolName The tool object or the name of the tool being invoked.
 * @param invocation The invocation object for the tool or the command invoked.
 * @param patterns A list of patterns to match against.
 *   Patterns can be:
 *   - A tool name (e.g., "ReadFileTool") to match any invocation of that tool.
 *   - A tool name with a prefix (e.g., "ShellTool(git status)") to match
 *     invocations where the arguments start with that prefix.
 * @returns True if the invocation matches any pattern, false otherwise.
 */
export function doesToolInvocationMatch(toolOrToolName, invocation, patterns) {
    let toolNames;
    if (isTool(toolOrToolName)) {
        toolNames = [toolOrToolName.name, toolOrToolName.constructor.name];
    }
    else {
        toolNames = [toolOrToolName];
    }
    if (toolNames.some((name) => SHELL_TOOL_NAMES.includes(name))) {
        toolNames = [...new Set([...toolNames, ...SHELL_TOOL_NAMES])];
    }
    for (const pattern of patterns) {
        const openParen = pattern.indexOf('(');
        if (openParen === -1) {
            // No arguments, just a tool name
            if (toolNames.includes(pattern)) {
                return true;
            }
            continue;
        }
        const patternToolName = pattern.substring(0, openParen);
        if (!toolNames.includes(patternToolName)) {
            continue;
        }
        if (!pattern.endsWith(')')) {
            continue;
        }
        const argPattern = pattern.substring(openParen + 1, pattern.length - 1);
        let command;
        if (typeof invocation === 'string') {
            command = invocation;
        }
        else {
            if (!('command' in invocation.params)) {
                // This invocation has no command - nothing to check.
                continue;
            }
            // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
            command = String(invocation.params.command);
        }
        if (toolNames.some((name) => SHELL_TOOL_NAMES.includes(name))) {
            if (command === argPattern || command.startsWith(argPattern + ' ')) {
                return true;
            }
        }
    }
    return false;
}
//# sourceMappingURL=tool-utils.js.map