From d901f85abbb7922cc7284fe1aea73db0022764ae Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 4 Jun 2026 00:38:21 -0400 Subject: [PATCH] docs: document sandbox backend contracts --- src/agents/sandbox/backend.types.ts | 12 ++++++++++++ src/agents/sandbox/fs-bridge.types.ts | 9 +++++++++ src/agents/sandbox/path-utils.ts | 8 ++++++++ 3 files changed, 29 insertions(+) diff --git a/src/agents/sandbox/backend.types.ts b/src/agents/sandbox/backend.types.ts index dd6ed445abda..ea800191e6c9 100644 --- a/src/agents/sandbox/backend.types.ts +++ b/src/agents/sandbox/backend.types.ts @@ -3,12 +3,20 @@ import type { SandboxBackendHandle } from "./backend-handle.types.js"; import type { SandboxRegistryEntry } from "./registry.js"; import type { SandboxConfig } from "./types.js"; +/** + * Shared sandbox backend registration contracts. + * + * Docker, SSH, and future backends expose a factory plus optional manager so + * runtime creation and lifecycle cleanup stay behind one backend boundary. + */ +/** Current runtime state reported by a sandbox backend manager. */ export type SandboxBackendRuntimeInfo = { running: boolean; actualConfigLabel?: string; configLabelMatch: boolean; }; +/** Optional lifecycle manager for an existing registered sandbox runtime. */ export type SandboxBackendManager = { describeRuntime(params: { entry: SandboxRegistryEntry; @@ -22,6 +30,7 @@ export type SandboxBackendManager = { }): Promise; }; +/** Inputs needed to create a sandbox backend handle for one session scope. */ export type CreateSandboxBackendParams = { sessionKey: string; scopeKey: string; @@ -30,10 +39,12 @@ export type CreateSandboxBackendParams = { cfg: SandboxConfig; }; +/** Factory that creates a backend handle for a sandbox session. */ export type SandboxBackendFactory = ( params: CreateSandboxBackendParams, ) => Promise; +/** Registry input accepted for sandbox backend registration. */ export type SandboxBackendRegistration = | SandboxBackendFactory | { @@ -41,6 +52,7 @@ export type SandboxBackendRegistration = manager?: SandboxBackendManager; }; +/** Normalized backend registration stored in the sandbox backend registry. */ export type RegisteredSandboxBackend = { factory: SandboxBackendFactory; manager?: SandboxBackendManager; diff --git a/src/agents/sandbox/fs-bridge.types.ts b/src/agents/sandbox/fs-bridge.types.ts index afb31306bee3..daf41616eba2 100644 --- a/src/agents/sandbox/fs-bridge.types.ts +++ b/src/agents/sandbox/fs-bridge.types.ts @@ -1,15 +1,24 @@ +/** + * Public sandbox filesystem bridge contracts. + * + * Tool and backend code use this interface to access files through the sandbox + * boundary instead of reaching directly into host paths. + */ +/** Resolved sandbox path with host, relative, and container views. */ export type SandboxResolvedPath = { hostPath?: string; relativePath: string; containerPath: string; }; +/** Minimal file stat shape returned by sandbox fs bridge implementations. */ export type SandboxFsStat = { type: "file" | "directory" | "other"; size: number; mtimeMs: number; }; +/** Filesystem operations exposed across the sandbox boundary. */ export type SandboxFsBridge = { resolvePath(params: { filePath: string; cwd?: string }): SandboxResolvedPath; readFile(params: { filePath: string; cwd?: string; signal?: AbortSignal }): Promise; diff --git a/src/agents/sandbox/path-utils.ts b/src/agents/sandbox/path-utils.ts index 2dc4f30da12d..aeb182c62add 100644 --- a/src/agents/sandbox/path-utils.ts +++ b/src/agents/sandbox/path-utils.ts @@ -1,10 +1,17 @@ import path from "node:path"; +/** + * POSIX path helpers for sandbox container paths. + * + * Container paths are always normalized as POSIX paths even when the host is not. + */ +/** Normalizes a container path and treats "." as the container root. */ export function normalizeContainerPath(value: string): string { const normalized = path.posix.normalize(value); return normalized === "." ? "/" : normalized; } +/** Returns whether target is lexically inside root after container-path normalization. */ export function isPathInsideContainerRoot(root: string, target: string): boolean { const normalizedRoot = normalizeContainerPath(root); const normalizedTarget = normalizeContainerPath(target); @@ -14,6 +21,7 @@ export function isPathInsideContainerRoot(root: string, target: string): boolean return normalizedTarget === normalizedRoot || normalizedTarget.startsWith(`${normalizedRoot}/`); } +/** Returns whether a relative path would escape its container root. */ export function relativePathEscapesContainerRoot(relativePath: string): boolean { return ( relativePath === ".." || relativePath.startsWith("../") || path.posix.isAbsolute(relativePath)