/**
 * codeRunnerTool — the tool that turns a {@link CodeRunner} into something an
 * LLM can call, holding ONE session per isolation key.
 *
 * Pattern: Factory over `defineTool` + the 9.7.0 tool-session contract.
 * Role:    the first consumer of `ctx.onTeardown` — and the proof it works.
 * Emits:   nothing directly; the teardown tier reports
 *          `agentfootprint.tools.session_started` / `_reused` / `_closed` /
 *          `_close_failed`.
 *
 * ── The doctrine: summarize prose, compute data ─────────────────────────────
 * A tool that hands the model 40,000 rows has not given it data; it has spent
 * the window. The motivating failure is real and measured: a production request
 * of 879,073 tokens, almost all of it one tool result pasted into the prompt.
 * (Since 9.6.0 that shape at least fails by NAME —
 * `ContextWindowExceededError` — instead of as a vendor 400. This is the other
 * half: not failing better, but not needing to.)
 *
 * With a code runner the model writes an aggregation, the RUNNER holds the
 * rows, and what comes back is the number. Prose gets summarized; data gets
 * computed.
 *
 * ── Why the session must be keyed, and keyed WIDELY ENOUGH ──────────────────
 * The session is the whole value: a code interpreter costs seconds to start and
 * milliseconds to invoke. But a session holds a filesystem, an environment and
 * half-run state, so the thing it is keyed on IS the isolation boundary. A
 * standing agent serving many people from one process, holding one session in a
 * module map, gives person B person A's files.
 *
 * So the key comes from `toolSessionKey(ctx, scope)` — one exported
 * implementation, composing tenant + principal + (session | run). Never a bare
 * `sessionId`: that is caller data, and anyone who can reach the host can put
 * someone else's there.
 *
 * ── Degradation is REFUSED, never silent ────────────────────────────────────
 * Ask for `scope: 'session'` at a door with no session and this throws, naming
 * the door. It does not quietly fall back. Falling back to a WIDER key is the
 * cross-binding bug itself; falling back to a NARROWER one is a silent 30×
 * latency change nobody sees until the bill.
 *
 * @example a session per run, on a local dev machine
 *   const agent = Agent.create({ provider })
 *     .tool(codeRunnerTool({ runner: localCodeRunner() }))
 *     .build();
 *
 * @example a session per hosted conversation, on a real sandbox
 *   const runner = agentCoreCodeRunner({ region, identifier: 'aws.codeinterpreter.v1' });
 *   const agent = Agent.create({ provider })
 *     .tool(codeRunnerTool({ runner, scope: 'session', language: 'python' }))
 *     .build();
 *   // the composition root says when a session is over:
 *   conversation.onClose(() => void agent.closeToolSessions({ sessionId }));
 */
import type { CodeRunner, CodeSession } from '../provider-contract/types.js';
import type { ToolWants } from '../artifacts/wants.js';
import type { CredentialNeed } from '../identity/types.js';
import type { CheckInDemand } from './checkin.js';
import { type Tool } from '../tools/tools.js';
import { type TeardownScope } from '../tools/toolSessions.js';
/** The scopes a code session can be held under. `'shutdown'` is not one: it is
 *  when everything goes, not a thing to key a session on. */
export type CodeRunnerToolScope = Extract<TeardownScope, 'call' | 'run' | 'session'>;
export interface CodeRunnerToolOptions {
    /** The backend. `localCodeRunner()` for a dev loop, `agentCoreCodeRunner(...)`
     *  for a real sandbox — the tool is identical across the swap. */
    readonly runner: CodeRunner;
    /** Tool name the model sees. Default `'run_code'`. */
    readonly name?: string;
    /** Description the model sees. A sensible one is composed from `scope` +
     *  `language` when you do not pass one. */
    readonly description?: string;
    /**
     * How long one session lives. Default `'run'` — a turn's worth of work shares
     * one interpreter, and nothing outlives the turn.
     *
     * `'session'` keeps the interpreter across the turns of one hosted
     * conversation (variables persist, files persist) and REQUIRES a
     * session-bound run plus a composition root that calls
     * `agent.closeToolSessions({ sessionId })`.
     *
     * `'call'` starts and stops per invocation — the safest and the slowest.
     */
    readonly scope?: CodeRunnerToolScope;
    /** Default language for the code the model writes. Default `'python'`. */
    readonly language?: string;
    /** Per-stream ceiling for what reaches the model, in characters. Default 4000.
     *  Anything cut is STATED in the result, never dropped quietly. */
    readonly maxOutputChars?: number;
    /** Per-execution ceiling handed to the runner. */
    readonly timeoutMs?: number;
    /** Demand a human check-in before code runs — `'always'`, or a predicate over
     *  the code string. A pause here does NOT tear the session down. */
    readonly checkIn?: CheckInDemand<{
        code: string;
    }>;
    /** A credential this tool needs (declare-and-push). Resolved before execute.
     *  Do NOT cache it past the call: a session outliving a run outlives its token. */
    readonly needs?: CredentialNeed;
    /**
     * Artifact arguments, declared exactly as any other tool declares them
     * (9.26.0): `wants: { dataset: 'dataset/rows' }`.
     *
     * The model passes the `art_…` ref as the argument, the framework resolves
     * it before `execute` under the run's own scope — the same `wants` machinery,
     * with the same teaching refusals for a stale, unknown or wrong-kind ref —
     * and then this tool STAGES the resolved payload into the code session as a
     * file. The data reaches the interpreter without ever entering the context
     * window, which is the whole doctrine this tool exists for, now with an
     * inbound leg to match the outbound one.
     *
     * ── What the model's code reads ─────────────────────────────────────────
     * The staged files are named in the `AF_STAGED_INPUTS` environment variable,
     * a JSON object of `argument name → path`. The composed tool description
     * states it with a one-line example in the tool's language, so a model needs
     * nothing beyond the description to use it.
     *
     * ── Refused rather than degraded ────────────────────────────────────────
     * Declaring `wants` on a runner whose sessions cannot accept staged inputs
     * (`stageInputs` absent — a backend that cannot write into its own session)
     * refuses BY NAME at dispatch. `localCodeRunner` and `agentCoreCodeRunner`
     * both stage. Running the code without the data it declared would leave the
     * model reasoning about a file that is not there, which is the exact silent
     * failure this library refuses to ship.
     *
     * Omitted, nothing changes: no schema properties are added, no session is
     * ever asked to stage, and the description is the one earlier releases
     * composed.
     */
    readonly wants?: ToolWants;
}
/**
 * The per-tool session map, riding the `Tool` under a REGISTRY symbol.
 *
 * `Symbol.for`, not a unique symbol: this package ships CJS and ESM, and a tool
 * built through one entry point must be readable through the other. The same
 * move `INNER_RUN_RECORDS` makes for `flowchartAsTool({ keepRecord })` — and
 * deliberately a DIFFERENT symbol, so one tool can carry both (spreading a tool
 * preserves symbol keys, which is why `{...tool, [SYM]: store}` composes).
 *
 * Invisible to the LLM, invisible to `Tool`'s shape, reachable by a test and by
 * whatever inspector comes next.
 */
export declare const TOOL_SESSIONS: unique symbol;
/**
 * Where a finished code run leaves its facts, keyed by `toolCallId`.
 *
 * `Symbol.for` for the same CJS/ESM reason as {@link TOOL_SESSIONS}, and keyed
 * by the CALL rather than stored as "the last run" because two tool calls in one
 * iteration run concurrently — a single slot would report one call's facts under
 * the other's name. The dispatch loop takes the entry and deletes it.
 */
export declare const CODE_RUNS: unique symbol;
/**
 * Where a code run's facts ALSO go when the AGENT dispatched the call: a slot
 * the dispatch loop puts on that one call's execution context
 * (`core/agent/stages/toolCalls.ts`, both execute boundaries) and reads back
 * when `execute` returns. The published map above is filled exactly as
 * before; the slot is the dispatch's own copy, the one the script record
 * attests from and `agentfootprint.tools.code_run` carries.
 *
 * Why not the map alone: it is keyed by the provider's call id, and two runs —
 * two agents sharing one tool object, or one run whose provider reuses an id —
 * can hold the same id at once. A fact read back by id then reaches whichever
 * dispatch reads first: one run's exit code attested another run's script.
 * The slot is the dispatch's own object, so its facts can only reach it. A
 * wrapper tool that hands the same context (or a spread of it) to this one
 * keeps the slot. `Symbol.for`, for the CJS/ESM reason above.
 *
 * @internal Library plumbing between the dispatch loop and this tool.
 */
export declare const CODE_RUN_SLOT: unique symbol;
/** One dispatch's slot for the facts of the code it ran. @internal */
export interface CodeRunSlot {
    facts?: CodeRunFacts;
}
/** What one finished code run is worth reporting, minus the code itself. */
export interface CodeRunFacts {
    readonly tool: string;
    readonly language: string;
    readonly stagedInputs: number;
    readonly outputChars: number;
    readonly truncated: boolean;
    /** What the runner REPORTED (`CodeResult.ok`). ABSENT when it reported
     *  nothing — unknown, never recorded as success. A `false` is told apart by
     *  the three fields below. */
    readonly ok?: boolean;
    /** The program's exit status — exactly when the runner supplied one. */
    readonly exitCode?: number;
    /** `true`: the runner flagged an error with no exit status, so whether the
     *  code ran is not confirmed — not a failed program. */
    readonly serviceError?: true;
    /** The signal that ended the process before it exited, when the runner
     *  knows it (`'SIGKILL'`): no exit status exists, and none is recorded. */
    readonly terminatedBy?: string;
    readonly shapeHash: string;
}
/** A `Tool` that records what its code runs were shaped like. */
export interface RecordsCodeRuns {
    readonly [CODE_RUNS]: ReadonlyMap<string, CodeRunFacts>;
}
/**
 * A program reduced to its CALL SHAPE: which operations, in what order.
 *
 * Strings, numbers, comments and identifier names are what make two runs of the
 * same computation look different, and they are also the half that quotes the
 * data — so removing them is both what makes the hash group correctly and what
 * makes it safe to emit. `groupBy(rows, 'wwn')` and `groupBy(items, 'serial')`
 * reduce to one shape; a totals-then-threshold written eleven times this month
 * hashes to one value eleven times, which is the signal worth having.
 *
 * **The callee names are KEPT, and that is the point.** The operation IS the
 * signal. The first version of this erased them too, which made `groupBy` and
 * `sortBy` one shape and collapsed the whole backlog into a single meaningless
 * bucket — caught by a clean-room probe on the published package, and missed by
 * a test whose two examples happened to differ elsewhere as well. A function
 * name is code, not data; the data lives in the literals and the variable
 * names, and those are what go.
 *
 * Deliberately crude — a lexical reduction, not a parse. It has to work on
 * whatever language the runner was configured for, and a wrong parse would be a
 * worse answer than a coarse one.
 *
 * The code is model output, so every pass is linear in it. Block comments and
 * string literals are scanned rather than matched: the regexes that used to
 * find them re-read the rest of the code from every unclosed comment opener or
 * quote — quadratic on openers repeated with text between them, or on a quote
 * followed by a long run of escaped quotes (16,000 of them took about half a
 * second). The scans produce exactly what those regexes did
 * (`test/security/linear-scanners.test.ts`).
 */
export declare function codeShape(code: string): string;
/**
 * Every `/* … *\/` replaced by one space, each ending at the first `*\/` after
 * its `/*`. Once a `/*` has no `*\/` after it, no later one has either — the
 * scan stops there instead of re-trying each.
 */
export declare function stripBlockComments(code: string): string;
/**
 * Every string literal replaced by `S`, leftmost first: from a quote, the
 * shortest run of escapes (`\` + anything but a line break) and other
 * characters up to the same quote unescaped.
 *
 * A literal that never closes is the costly case — the walk ends at a `\`
 * before a line break, or at the end — and the regex re-tries it from every
 * later quote. But a later opening of the SAME quote, before where that walk
 * stopped, walks the same tail (whether a character is escaped depends only on
 * the backslashes just before it, and a quote is not one), so it fails the
 * same way: those are skipped. Each quote's failed walks therefore cover
 * disjoint stretches, and the literals that close are jumped over.
 */
export declare function replaceStringLiterals(code: string): string;
/** A `Tool` that holds live sessions, keyed by isolation key. */
export interface HoldsToolSessions {
    readonly [TOOL_SESSIONS]: ReadonlyMap<string, CodeSession>;
}
/** Read the code-run facts off a candidate, or `undefined` when it records none. */
export declare function codeRunsOf(candidate: unknown): Map<string, CodeRunFacts> | undefined;
/** Read the live-session map off a candidate, or `undefined` when it holds none. */
export declare function toolSessionsOf(candidate: unknown): ReadonlyMap<string, CodeSession> | undefined;
export declare function codeRunnerTool(options: CodeRunnerToolOptions): Tool<{
    code: string;
}, string> & HoldsToolSessions & RecordsCodeRuns;
