/**
 * Per-tool contract for `execute_bash_command`. Bash runs in a sandboxed
 * Modal workspace, so approval is never required.
 */
import { Type } from '@sinclair/typebox';
import { HostToolResultStatus, buildRendererArtifact } from '../../../contracts/host-tool.js';
import { withAbort } from '../../../utils/abort.js';
import { truncateOutput } from '../../../utils/truncate.js';
import { BASH_COMMAND } from '../tool-names.js';
export const EXECUTE_BASH_COMMAND_KIND = 'executeBashCommand';
export const EXECUTE_BASH_COMMAND_TOOL_DESCRIPTION = `
Full Linux sandbox with bash, Python 3.11, and filesystem (/workspace shared with execute_code via GCS cloud storage).
You are allowed to use this tool to execute any computer-related task that cannot be satisfactorily executed by your other specialized tools.
Common use cases: data processing, file conversion, web scraping, text processing.
When parsing LLM-generated JSON in Python, use strict=False to handle unescaped control characters.

**System tools:** poppler-utils (pdftotext, pdfinfo), qpdf, ghostscript (gs), curl, wget, jq, zip/unzip, imagemagick, libreoffice.

**Python packages (pre-installed):**
- Data: numpy, scipy, pandas, openpyxl, xlrd, xlsxwriter, odfpy, lxml, beautifulsoup4, html5lib, chardet, pyyaml, orjson
- Finance: numpy-financial, yfinance, sec-edgar-downloader, python-dateutil
- Documents: python-docx, python-pptx, striprtf, pylatexenc
- PDF: camelot, pymupdf (fitz), pdfplumber (slow), pdftotext, weasyprint, fpdf2, reportlab, tabulate
- Web/HTTP: httpx, requests, playwright (with Chrome)
- Other: pillow, matplotlib, seaborn, rapidfuzz, phonenumbers, email-validator, pycountry

**Permissions**
- You are allowed to pip / apt install packages and remove files / folders upon user request (you cannot rm -rf but you can iteratively remove)

**GCS storage tips:**
- No append operations (>>, tee -a). Read the file, modify in memory, then overwrite entirely.
- Some download tools perform unsupported filesystem operations on cloud mounts and may fail silently.
- For file-intensive operations (extracting archives, downloading datasets, transformations), work in /tmp first then copy results back.
- For Office files, ZIP-based files, archives, PDFs, generated binaries, downloads, conversions, and anything needing seek/random-write semantics, always create in /tmp, then copy the final artifact to /workspace or /global.

**External services:** Use the external-services skill before making authenticated API calls or raw TCP connections. Use \`safe\` for authenticated HTTP and \`shortcut-tcp-forward\` for approved non-HTTP \`host:port\` protocols.

**Conventions:**
- Auxiliary files based on an uploaded file must be created within the stem folder of the uploaded filename (e.g. "report.pdf" -> "report/" folder).
- When creating files, tell users the relative path (e.g. "report/tables.md")
- Stop retrying after 2 attempts for unexplained infrastructure failures.
`.trim();
export const EXECUTE_BASH_COMMAND_HUMAN_DESCRIPTION = "A short one-sentence description of what the command does, starting with a gerund (e.g., 'Parsing...', 'Downloading...', 'Converting...').";
export const EXECUTE_BASH_COMMAND_COMMAND_DESCRIPTION = 'The bash command to execute.';
const executeBashCommandLlmSchema = Type.Object({
    description: Type.String({
        description: EXECUTE_BASH_COMMAND_HUMAN_DESCRIPTION
    }),
    command: Type.String({
        description: EXECUTE_BASH_COMMAND_COMMAND_DESCRIPTION
    }),
    timeoutSeconds: Type.Optional(Type.Number({
        description: 'Optional command timeout in seconds.'
    }))
});
function validateParams(value) {
    if (!value || typeof value !== 'object')
        return false;
    const candidate = value;
    return (typeof candidate.command === 'string' &&
        typeof candidate.description === 'string' &&
        (candidate.timeoutSeconds === undefined || typeof candidate.timeoutSeconds === 'number'));
}
function validateResultDetails(value) {
    if (value === undefined)
        return true;
    if (!value || typeof value !== 'object')
        return false;
    const candidate = value;
    return ((candidate.exit_code === undefined || typeof candidate.exit_code === 'number') &&
        (candidate.sandbox_id === undefined || typeof candidate.sandbox_id === 'string') &&
        (candidate.stderr === undefined || typeof candidate.stderr === 'string') &&
        (candidate.stdout === undefined || typeof candidate.stdout === 'string'));
}
function normalizeResultDetails(value) {
    return value === undefined ? undefined : { ...value };
}
function getResultObservabilityFields(details) {
    if (!validateResultDetails(details) || details === undefined)
        return undefined;
    const metadata = {
        ...(details.exit_code !== undefined ? { exit_code: details.exit_code } : {}),
        ...(details.sandbox_id !== undefined ? { sandbox_id: details.sandbox_id } : {})
    };
    return Object.keys(metadata).length > 0 ? metadata : undefined;
}
function evaluateApproval() {
    return { required: false };
}
function formatResult(details, isError, userMessage) {
    const stdout = details?.stdout?.trim() ?? '';
    const stderr = details?.stderr?.trim() ?? '';
    const exitCode = details?.exit_code;
    const cleanError = userMessage?.trim() ?? '';
    const parts = [cleanError];
    if (stdout)
        parts.push(stdout);
    if (stderr)
        parts.push(`stderr:\n${stderr}`);
    if (exitCode != null)
        parts.push(`Exit code: ${exitCode}`);
    const text = truncateOutput(parts.filter((part) => part.length > 0).join('\n\n'));
    return {
        content: [{ type: 'text', text }],
        details: buildRendererArtifact(details, userMessage),
        isError
    };
}
function createLlmTool(options) {
    return {
        name: BASH_COMMAND,
        label: BASH_COMMAND,
        isReadOnly: false,
        description: EXECUTE_BASH_COMMAND_TOOL_DESCRIPTION,
        parameters: executeBashCommandLlmSchema,
        async execute(toolCallId, llmArgs, signal) {
            const args = llmArgs;
            const params = {
                command: args.command,
                description: args.description,
                timeoutSeconds: args.timeoutSeconds
            };
            void options.sessionPolicy;
            const request = {
                approval: evaluateApproval(),
                heartbeat: {
                    label: 'Running command...',
                    toolName: BASH_COMMAND
                },
                kind: EXECUTE_BASH_COMMAND_KIND,
                params,
                requestId: toolCallId
            };
            const result = await withAbort(signal, async () => options.pendingHostToolRequests.request(request));
            const details = result.details;
            const isError = result.status === HostToolResultStatus.Failed ||
                result.status === HostToolResultStatus.Cancelled;
            return formatResult(details, isError, result.userMessage);
        }
    };
}
export const executeBashCommandContract = {
    kind: EXECUTE_BASH_COMMAND_KIND,
    createLlmTool,
    evaluateApproval,
    getResultObservabilityFields,
    validateParams,
    validateResultDetails,
    normalizeResultDetails
};
//# sourceMappingURL=contract.js.map