/** * Agent Runtime — Sandboxed execution environment for AI agents * * Provides controlled access to shell commands, file system, and system * resources with policy enforcement, approval gates, and audit logging. * * Components: * - SandboxPolicy — defines what agents are allowed to do * - ShellExecutor — spawns child processes within policy constraints * - FileAccessor — scoped file read/write with traversal protection * - ApprovalGate — human-in-the-loop approval for sensitive operations * - AgentRuntime — unified facade combining all components * * @module AgentRuntime * @version 1.0.0 */ import { EventEmitter } from 'events'; import type { ExecutionReceipt } from '../security'; /** * Thrown when an agent attempts to access source code files or directories * while `sourceProtection` is enabled on the sandbox policy. */ export declare class SourceProtectionError extends Error { /** The path that was blocked */ readonly blockedPath: string; /** The agent that attempted access */ readonly agentId: string; constructor(blockedPath: string, agentId: string); } /** Result of a shell command execution */ export interface ShellResult { /** Exit code (0 = success) */ exitCode: number; /** Standard output */ stdout: string; /** Standard error output */ stderr: string; /** Execution time in milliseconds */ durationMs: number; /** Whether the command was terminated due to timeout */ timedOut: boolean; /** Whether the command was killed due to output limit */ truncated: boolean; /** Runtime-issued outcome-bound receipt (present when AgentRuntime executes via exec()) */ receipt?: ExecutionReceipt; } /** Options for shell execution */ export interface ShellOptions { /** Working directory (defaults to policy basePath) */ cwd?: string; /** Environment variables to merge with process.env */ env?: Record; /** Command timeout in milliseconds (default: 30000) */ timeoutMs?: number; /** Maximum output bytes (default: 1048576 = 1MB) */ maxOutputBytes?: number; /** Whether this command requires human approval (auto-detected from policy if not set) */ requiresApproval?: boolean; /** Agent ID requesting execution */ agentId?: string; } /** File operation result */ export interface FileResult { success: boolean; path: string; content?: string; entries?: string[]; error?: string; durationMs: number; /** Runtime-issued outcome-bound receipt (present on successful write operations) */ receipt?: ExecutionReceipt; } /** Sandbox policy configuration */ export interface SandboxPolicyConfig { /** Base directory agents are scoped to */ basePath: string; /** Allowed command patterns (globs). Empty = deny all. */ allowedCommands: string[]; /** Blocked command patterns (override allowed). Always enforced. */ blockedCommands: string[]; /** Allowed directory paths for file access (relative to basePath) */ allowedPaths: string[]; /** Blocked directory paths (override allowed) */ blockedPaths: string[]; /** Maximum concurrent processes (default: 5) */ maxConcurrentProcesses: number; /** Default command timeout in ms (default: 30000) */ defaultTimeoutMs: number; /** Default max output bytes (default: 1MB) */ defaultMaxOutputBytes: number; /** Commands that always require approval (patterns) */ approvalRequired: string[]; /** Whether read-only file operations auto-approve (default: true) */ autoApproveReads: boolean; /** * When true, agents cannot access source code files or directories * (lib/, adapters/, bin/, scripts/, *.ts, *.py, root *.json config files). * All access attempts outside `data//` are blocked and audited. * Default: false. */ sourceProtection?: boolean; /** * Active environment name. When set, file access is further restricted * to `data//` when sourceProtection is enabled. * Set this at construction time. Runtime changes to `NETWORK_AI_ENV` after * `SandboxPolicy` is created have no effect. */ env?: string; } /** Approval request passed to the approval callback */ export interface ApprovalRequest { /** Type of operation */ type: 'shell' | 'file_write' | 'file_read' | 'file_list'; /** The command or path being requested */ target: string; /** Agent ID making the request */ agentId: string; /** Why the agent needs this */ justification?: string; /** Risk assessment */ risk: 'low' | 'medium' | 'high'; /** Timestamp of request */ timestamp: number; } /** Approval decision from the human/callback */ export interface ApprovalDecision { approved: boolean; approvedBy?: string; reason?: string; } /** Callback function for approval decisions */ export type ApprovalCallback = (request: ApprovalRequest) => Promise; /** Audit entry for runtime operations */ export interface RuntimeAuditEntry { timestamp: string; action: 'shell_execute' | 'file_read' | 'file_write' | 'file_list' | 'approval_requested' | 'approval_granted' | 'approval_denied' | 'policy_violation'; agentId: string; target: string; result: 'success' | 'denied' | 'error' | 'timeout' | 'blocked'; details?: Record; durationMs?: number; } /** Events emitted by AgentRuntime */ export interface RuntimeEvents { 'approval:requested': (request: ApprovalRequest) => void; 'approval:decided': (request: ApprovalRequest, decision: ApprovalDecision) => void; 'command:start': (agentId: string, command: string) => void; 'command:complete': (agentId: string, command: string, result: ShellResult) => void; 'file:access': (agentId: string, path: string, mode: 'read' | 'write' | 'list') => void; 'policy:violation': (agentId: string, target: string, reason: string) => void; 'audit': (entry: RuntimeAuditEntry) => void; } /** Options for creating an AgentRuntime */ export interface AgentRuntimeOptions { policy: Partial & { basePath: string; }; onApproval?: ApprovalCallback; autoApproveAll?: boolean; } /** * Sandbox policy engine — determines what agents are allowed to do. * * @example * ```typescript * const policy = new SandboxPolicy({ * basePath: '/project', * allowedCommands: ['npm *', 'node *', 'git status'], * }); * policy.isCommandAllowed('npm test'); // true * policy.isCommandAllowed('rm -rf /'); // false * ``` */ export declare class SandboxPolicy { private readonly config; constructor(config: Partial & { basePath: string; }); /** Check if a command matches the policy's allowed list and isn't blocked */ isCommandAllowed(command: string): boolean; /** * Tokenize a command into an argv array suitable for `shell: false` * execution. Returns `null` for any command that contains unquoted shell * metacharacters or malformed quoting — i.e. exactly the inputs that * {@link isCommandAllowed} rejects. Quoted metacharacters are preserved as * literal data within their token. * * @param command - Raw command string to tokenize. * @returns The argv array, or `null` if the command is unsafe to execute. */ tokenizeCommand(command: string): string[] | null; /** * Canonicalize a command into the exact representation the executor will * run: quotes stripped, whitespace collapsed to single spaces, using the * same tokenizer {@link tokenizeCommand} uses. Returns `null` when the * command contains unquoted shell metacharacters, unterminated quoting, or * no tokens at all. * * All security-relevant matching (blocklist, allowlist, approval * requirement, risk assessment) MUST operate on this canonical form, never * on the raw string — quotes and irregular whitespace are literal * characters to the glob matcher but semantically invisible to the * tokenizing executor, letting a crafted command evade blocklist/approval * matching while running identically to its unquoted equivalent * (GHSA-9v4f-j8cv-fhxw). */ private canonicalize; /** Check if a command requires human approval */ requiresApproval(command: string): boolean; /** Assess risk level of a command */ assessRisk(command: string): 'low' | 'medium' | 'high'; /** Validate that a file path is within allowed scope */ isPathAllowed(filePath: string): boolean; /** Resolve and validate a path against basePath (returns null if traversal detected) */ resolvePath(filePath: string): string | null; /** Get a copy of the current policy config */ getConfig(): Readonly; /** Update allowed commands */ allowCommand(pattern: string): void; /** Remove an allowed command pattern */ disallowCommand(pattern: string): void; /** Update allowed paths */ allowPath(path: string): void; /** Block a path */ blockPath(path: string): void; get basePath(): string; get maxConcurrentProcesses(): number; get defaultTimeoutMs(): number; get defaultMaxOutputBytes(): number; get autoApproveReads(): boolean; /** Simple glob matching: supports * as wildcard */ private matchesAny; /** Match a simple glob pattern against a string */ private globMatch; } /** * Sandboxed shell command executor with timeout, output limits, and * concurrent process tracking. * * @example * ```typescript * const executor = new ShellExecutor(policy); * const result = await executor.execute('npm test', { agentId: 'tester' }); * console.log(result.exitCode, result.stdout); * ``` */ export declare class ShellExecutor { private readonly policy; private activeProcesses; constructor(policy: SandboxPolicy); /** Execute a shell command within policy constraints */ execute(command: string, opts?: ShellOptions): Promise; /** Get the number of currently running processes */ get running(): number; private spawnCommand; } /** * Policy-scoped file system accessor. All paths are validated against * the sandbox policy before any I/O. * * **Error contract:** All public methods (`read`, `write`, `list`) return a * `{success: boolean, ...}` result object — they never throw. All access-denied * paths (path traversal, out-of-scope under `sourceProtection`, policy-blocked * reads/writes, and `SourceProtectionError`) are caught at the method boundary * and converted to `{success: false, error: }`. The `error` field * contains a short description without leaking internal path details. * * @example * ```typescript * const files = new FileAccessor(policy); * const result = await files.read('src/index.ts', 'reader-agent'); * ``` */ export declare class FileAccessor { private readonly policy; constructor(policy: SandboxPolicy); /** * Checks whether the resolved absolute path is blocked by source protection. * Throws SourceProtectionError if access is denied. */ private checkSourceProtection; /** Read a file within the sandbox scope */ read(filePath: string, agentId: string): Promise; /** Write a file within the sandbox scope */ write(filePath: string, content: string, agentId: string): Promise; /** List directory contents within the sandbox scope */ list(dirPath: string, agentId: string): Promise; } /** * Human-in-the-loop approval gate. Queues requests and waits for * a decision from the configured callback. * * @example * ```typescript * const gate = new ApprovalGate(async (req) => { * return { approved: true, approvedBy: 'operator' }; * }); * const decision = await gate.request({ type: 'shell', target: 'npm publish', ... }); * ``` */ export declare class ApprovalGate extends EventEmitter { private readonly callback; private readonly autoApproveAll; private readonly history; constructor(callback?: ApprovalCallback, autoApproveAll?: boolean); /** Request approval for an operation */ request(req: ApprovalRequest): Promise; /** Get approval history */ getHistory(): ReadonlyArray<{ request: ApprovalRequest; decision: ApprovalDecision; }>; /** Get count of approvals/denials */ getStats(): { total: number; approved: number; denied: number; }; } /** * Unified agent execution runtime. Combines policy, shell, file access, * and approval into a single interface for agent consumption. * * @example * ```typescript * const runtime = new AgentRuntime({ * policy: { * basePath: '/project', * allowedCommands: ['npm *', 'node *', 'git status', 'git diff'], * }, * onApproval: async (req) => { * console.log(`Approve? ${req.type}: ${req.target}`); * return { approved: true, approvedBy: 'operator' }; * }, * }); * * const result = await runtime.exec('npm test', 'tester-agent'); * ``` */ export declare class AgentRuntime extends EventEmitter { readonly policy: SandboxPolicy; readonly shell: ShellExecutor; readonly files: FileAccessor; readonly gate: ApprovalGate; private auditLog; private readonly receiptManager; constructor(opts: AgentRuntimeOptions); /** * Execute a shell command with policy + approval checks. * Returns ShellResult on success, throws on policy violation. */ exec(command: string, agentId: string, opts?: ShellOptions): Promise; /** Read a file with policy + optional approval */ readFile(filePath: string, agentId: string): Promise; /** Write a file with policy + approval */ writeFile(filePath: string, content: string, agentId: string): Promise; /** List a directory with policy check */ listDir(dirPath: string, agentId: string): Promise; /** Get the internal audit log */ getAuditLog(): ReadonlyArray; /** Clear the internal audit log */ clearAuditLog(): void; private audit; } /** Thrown when an operation violates the sandbox policy */ export declare class RuntimePolicyError extends Error { readonly code = "POLICY_VIOLATION"; constructor(message: string); } /** Thrown when an operation is denied by the approval gate */ export declare class RuntimeApprovalError extends Error { readonly code = "APPROVAL_DENIED"; constructor(message: string); } /** Thrown when a command fails to spawn */ export declare class RuntimeExecutionError extends Error { readonly code = "EXECUTION_ERROR"; constructor(message: string); } //# sourceMappingURL=agent-runtime.d.ts.map