/**
 * localCodeRunner — run code in a child process, on this machine.
 *
 * Pattern: Adapter (GoF) over `node:child_process`.
 * Role:    the dev default behind the {@link CodeRunner} port. `agentCoreCodeRunner`
 *          is the production swap; the tool code does not change.
 * Emits:   nothing. The tool that owns the session emits.
 *
 * ── ISOLATION, NOT A SANDBOX. Read this before deploying it. ────────────────
 * The name is `localCodeRunner`, never `sandboxedCodeRunner`, and the
 * difference is not modesty — it is the whole security posture.
 *
 * What a child process DOES give you:
 *   • a separate process and heap — a crash or an OOM takes the child, not you;
 *   • kill on timeout, so runaway code has a ceiling — on POSIX the child
 *     runs in a process group of its own, and at the timeout, on an abort or
 *     at the output ceiling the runner stops that OWNED group (SIGKILL). That
 *     is cleanup, not containment: a descendant that starts its own session
 *     or group escapes it, and a host killed before its exit handlers run
 *     stops nothing. On Windows only the child itself can be stopped, and the
 *     timeout error says so;
 *   • no inherited stdin, so nothing can block waiting for a terminal;
 *   • an environment ALLOWLIST — `process.env` is not inherited (only `PATH`,
 *     so the interpreter can be found), so an `AWS_SECRET_ACCESS_KEY` in your
 *     shell is not in the model's reach;
 *   • a working directory, by convention.
 *
 * What it does NOT give you, at all:
 *   • a filesystem jail — the code can read and write anywhere this process can;
 *   • a network jail — it can call out;
 *   • CPU or memory limits beyond the timeout;
 *   • any protection from code that deletes something outside `cwd`.
 *
 * ── It stages inputs; it does NOT report outputs (a decision, not a gap) ────
 * `stageInputs` puts declared artifact payloads INTO the session, and the code
 * reads them from the manifest. Nothing comes back the same way: this runner
 * leaves `CodeResult.artifacts` ABSENT — never `[]`, which would claim the code
 * produced nothing — because it has no output location to collect from. The
 * child's working directory is the CALLER'S OWN cwd, so "files the code wrote"
 * is not a set this adapter can identify without guessing which of a developer's
 * files were meant, and a dev-loop runner that quietly uploaded whatever
 * appeared beside your source would be the worse failure. Producing outputs
 * needs a declared output directory the model is told about and a size policy
 * for what is too big to carry in-band; `readFile` below is the bounded
 * read-back, for a caller that names the file. A runner that has all three
 * fills the field, and `codeRunnerTool` mints every data-carrying entry with
 * no further wiring.
 *
 * ── Files and commands ─────────────────────────────────────────────────────
 * `readFile(path, { maxBytes })` reads ONE file back from the working
 * directory — the path checked by the port's one rule (`sandboxPath`), then
 * by its REAL location, so a symbolic link cannot lead it outside; a file over
 * `maxBytes` is refused, never cut; a file on disk is bytes, so bytes come
 * back and no encoding is guessed. `runCommand(command)` runs ONE `sh -c`
 * command with the same environment allowlist as the code, and exists only on
 * a runner built with `commands: true`. Neither is a network or filesystem
 * jail: a command reaches whatever this machine reaches.
 *
 * So: a development loop, a trusted-input pipeline, a machine you would be
 * relaxed about a shell script running on. NOT arbitrary model-written code
 * from an untrusted user, on a host with anything on it. For that, run a real
 * sandbox behind the same port — `agentCoreCodeRunner`, a gVisor container, a
 * Firecracker VM — and keep the tool identical.
 *
 * **In-process `eval` / `node:vm` is refused outright.** Node's own
 * documentation says `vm` is not a security mechanism ("do not use it to run
 * untrusted code"), so shipping it as one would be theater: the same code
 * reaching the same globals, wearing a word that makes a reader stop checking.
 * A subprocess is genuinely a boundary; it is just a smaller one than the word
 * "sandbox" implies. The library teaches refusals for things that are WRONG and
 * honest names for things that are merely LIMITED — running local code in a dev
 * loop is not wrong, and calling it a sandbox is.
 *
 * @example
 *   const agent = Agent.create({ provider })
 *     .tool(codeRunnerTool({ runner: localCodeRunner() }))
 *     .build();
 */
import type { CodeRunner } from '../../provider-contract/types.js';
export interface LocalCodeRunnerOptions {
    /**
     * How to run a snippet, as `[command, ...args]` — the code is appended as the
     * final argument. Defaults by language: `['node', '-e']` for javascript,
     * `['python3', '-c']` for python.
     */
    readonly command?: readonly string[];
    /** Working directory for the child. Defaults to `process.cwd()`. */
    readonly cwd?: string;
    /**
     * The child's environment. An ALLOWLIST, never a merge: `process.env` is NOT
     * inherited, so a credential sitting in this process's environment is not
     * handed to model-written code by default. Pass exactly what the code needs.
     *
     * ONE exception, stated because an unstated one is a hole: `PATH` is passed
     * through, because without it the operating system cannot find the
     * interpreter and nothing runs at all. `PATH` names directories, not secrets.
     * Set `env: { PATH: '/usr/bin' }` to narrow it, or `env: { PATH: '' }` to
     * refuse even that — anything you supply here WINS over the inherited value.
     */
    readonly env?: Readonly<Record<string, string>>;
    /** Per-execution ceiling; the child is killed past it. Default 30s. */
    readonly timeoutMs?: number;
    /**
     * Per-stream output ceiling, in characters. Default 8000.
     *
     * Cutting is allowed; cutting SILENTLY is not — anything cut is reported on
     * `CodeResult.truncated`, and `codeRunnerTool` renders that as a visible
     * marker in the tool result. The model has to know the table it is about to
     * reason over is a fragment.
     */
    readonly maxOutputChars?: number;
    /**
     * Give each session `runCommand` — one `sh -c` command in the working
     * directory, with the same environment allowlist as the code. Default
     * `false`, and then the member is ABSENT (`canRunCodeCommands` says no):
     * it runs arbitrary shell, so a runner offers it only when built to. Build
     * one runner per agent that should have it.
     *
     * Not a jail: the command reaches this machine's filesystem and network, as
     * the code already does.
     */
    readonly commands?: boolean;
    /** Stable id (default `'local-code-runner'`). Rides the session events. */
    readonly id?: string;
    /** @internal Test seam — the `node:child_process` module. */
    readonly _childProcess?: ChildProcessModuleLike;
    /** @internal Test seam — the `node:fs` slice staging and reading use. */
    readonly _fs?: FsModuleLike;
    /** @internal Test seam — where staging directories are created. Defaults to
     *  the OS temp directory. */
    readonly _stagingRoot?: string;
}
/** The slice of `node:fs` staging and reading touch. Loaded lazily, and ONLY
 *  when a caller actually stages or reads something — a session that does
 *  neither never asks this runtime for a filesystem. */
export interface FsModuleLike {
    readonly mkdtempSync?: (prefix: string) => string;
    readonly writeFileSync?: (path: string, data: string | Uint8Array) => void;
    readonly rmSync?: (path: string, options?: {
        recursive?: boolean;
        force?: boolean;
    }) => void;
    readonly realpathSync?: (path: string) => string;
    readonly openSync?: (path: string, flags: string | number) => number;
    /** The open flags a read uses where the platform has them. */
    readonly constants?: {
        readonly O_RDONLY?: number;
        readonly O_NONBLOCK?: number;
        readonly O_NOFOLLOW?: number;
    };
    readonly fstatSync?: (fd: number) => {
        isFile(): boolean;
        readonly size: number;
    };
    readonly readSync?: (fd: number, buffer: Uint8Array, offset: number, length: number, position: number | null) => number;
    readonly closeSync?: (fd: number) => void;
}
/** The slice of `node:child_process` this adapter touches. */
export interface ChildProcessModuleLike {
    /** Used when present (the real module always has it): only `spawn` can give
     *  a child a process group of its own. */
    readonly spawn?: SpawnLike;
    readonly execFile?: (file: string, args: readonly string[], options: {
        cwd?: string;
        env?: Record<string, string>;
        maxBuffer?: number;
        killSignal?: string;
        detached?: boolean;
    }, callback: (error: (Error & {
        code?: number | string;
        killed?: boolean;
        signal?: string | null;
    }) | null, stdout: string, stderr: string) => void) => unknown;
}
/** The part of the child `execFile` / `spawn` hands back that ending it needs. */
interface ChildLike {
    readonly pid?: number;
    kill?(signal?: string): boolean;
    readonly stdout?: {
        destroy?(): void;
    } | null;
    readonly stderr?: {
        destroy?(): void;
    } | null;
}
/** A readable end of a spawned child's pipe, as far as collecting it goes. */
interface PipeLike {
    on(event: 'data', listener: (chunk: Uint8Array) => void): unknown;
    destroy?(): void;
}
/** The child `spawn` hands back, as far as this adapter uses it. */
interface SpawnedLike extends ChildLike {
    readonly stdout?: PipeLike | null;
    readonly stderr?: PipeLike | null;
    on(event: 'error', listener: (error: Error & {
        code?: string;
    }) => void): unknown;
    on(event: 'close', listener: (code: number | null, signal: string | null) => void): unknown;
}
/** The slice of `node:child_process` · `spawn` the runner uses — preferred
 *  over `execFile`, which does not pass `detached` on to the child. */
type SpawnLike = (file: string, args: readonly string[], options: {
    cwd?: string;
    env?: Record<string, string>;
    detached?: boolean;
    stdio?: readonly ['ignore', 'pipe', 'pipe'];
}) => SpawnedLike;
export declare function localCodeRunner(options?: LocalCodeRunnerOptions): CodeRunner;
export {};
//# sourceMappingURL=local.d.ts.map