@tangle-network/agent-app 0.44.56 → 0.44.57
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/sandbox/index.ts","../src/sandbox/outcome.ts","../src/sandbox/model.ts","../src/sandbox/binary-read.ts","../src/sandbox/terminal-proxy-token.ts","../src/sandbox/workspace-terminal.ts","../src/sandbox/prewarm.ts","../src/sandbox/prewarm-claim-d1.ts"],"sourcesContent":["import {\n Sandbox,\n type ExecResult,\n} from '@tangle-network/sandbox/core'\nimport type {\n EgressPolicy,\n MintScopedTokenOptions,\n SandboxConnection,\n SandboxInstance,\n ScopedTokenScope,\n StorageConfig,\n TurnDriveResult,\n ProvisionEvent,\n} from '@tangle-network/sandbox'\nimport type {\n AgentProfile,\n AgentProfileFileMount,\n AgentProfileMcpServer,\n ReasoningEffort,\n} from '@tangle-network/agent-interface'\nimport { createHash } from 'node:crypto'\nimport {\n buildAppToolMcpServer,\n type AppToolName,\n type AppToolContext,\n type ToolHeaderNames,\n} from '../tools/index'\nimport { assertHarnessModelCompatible, type Harness } from '../harness/index'\nimport {\n resolveTangleExecutionEnvironment,\n trimOrNull,\n type TangleExecutionEnvironment,\n} from '../runtime/model'\nimport { ok, fail, type Outcome } from './outcome'\nimport { fingerprintAgentProfile, type ProfileFingerprint } from '../profile/fingerprint'\nimport {\n assertProfilePromptWithinBudget,\n type ComposeProfileBudget,\n} from '../profile/budget'\nimport {\n resolveModel,\n resolveModelSelection,\n requireTransportableModel,\n SandboxModelResolutionError,\n type ProviderResolutionConfig,\n type ResolvedModel,\n type ModelSelection,\n type ModelSelectionFailure,\n type ModelSelectionError,\n type ModelSelectionSource,\n} from './model'\n\nexport type { Outcome } from './outcome'\nexport * from './binary-read'\nexport {\n resolveModel,\n resolveModelSelection,\n requireTransportableModel,\n SandboxModelResolutionError,\n}\nexport type {\n ProviderResolutionConfig,\n ResolvedModel,\n ModelSelection,\n ModelSelectionFailure,\n ModelSelectionError,\n ModelSelectionSource,\n}\n\n/** Define client credentials for accessing the sandbox environment with API key and base URL */\nexport interface SandboxClientCredentials {\n apiKey: string\n baseUrl: string\n}\n\n/**\n * Sandbox credential policy reuses the canonical execution-environment union\n * (development/test/staging/production) so env classification stays in one place\n * (see resolveTangleExecutionEnvironment in runtime/model).\n */\nexport type SandboxCredentialEnvironment = TangleExecutionEnvironment\n\n/** Resolve options for obtaining sandbox client credentials from environment variables and classification */\nexport interface ResolveSandboxClientCredentialsOptions {\n /**\n * Environment object to read from. Defaults to process.env when available.\n */\n env?: Record<string, string | undefined>\n /**\n * Explicit environment classification. Defaults to APP_ENV/NODE_ENV derived\n * behavior: local/development/test use direct env credentials; staging/prod\n * require the provision callback unless allowDirectEnvCredentials opts in.\n */\n environment?: SandboxCredentialEnvironment\n /**\n * Env names that may carry a sandbox-compatible bearer. The first non-empty\n * value wins when direct env credentials are allowed.\n */\n directKeyNames?: readonly string[]\n /**\n * Env names that may carry the sandbox gateway URL. The first non-empty value\n * wins, then defaultBaseUrl.\n */\n baseUrlNames?: readonly string[]\n /**\n * Base URL used when none of baseUrlNames are present.\n */\n defaultBaseUrl?: string\n /**\n * Whether direct env credentials are allowed for this environment. Defaults\n * to true in development/test and false in staging/production.\n */\n allowDirectEnvCredentials?: boolean | ((environment: SandboxCredentialEnvironment) => boolean)\n /**\n * Product-owned provision path, usually minting a per-user sandbox key from a\n * linked platform account. Called before direct env credentials in\n * staging/production and after direct env credentials in development/test.\n */\n provision?: (\n context: {\n environment: SandboxCredentialEnvironment\n env: Record<string, string | undefined>\n },\n ) => SandboxClientCredentials | null | undefined | Promise<SandboxClientCredentials | null | undefined>\n}\n\nconst DEFAULT_SANDBOX_DIRECT_KEY_NAMES = [\n 'TCLOUD_SANDBOX_API_KEY',\n 'SANDBOX_API_KEY',\n 'TANGLE_API_KEY',\n] as const\nconst DEFAULT_SANDBOX_BASE_URL_NAMES = ['SANDBOX_GATEWAY_URL', 'SANDBOX_API_URL'] as const\n\nfunction normalizeBaseUrl(value: string): string {\n return value.trim().replace(/\\/v1\\/?$/, '').replace(/\\/+$/, '')\n}\n\nfunction processEnv(): Record<string, string | undefined> {\n return typeof process === 'undefined' ? {} : process.env\n}\n\nfunction directEnvCredentialsAllowed(\n environment: SandboxCredentialEnvironment,\n allow: ResolveSandboxClientCredentialsOptions['allowDirectEnvCredentials'],\n): boolean {\n if (typeof allow === 'function') return allow(environment)\n if (typeof allow === 'boolean') return allow\n return environment === 'development' || environment === 'test'\n}\n\nfunction resolveSandboxBaseUrl(\n env: Record<string, string | undefined>,\n names: readonly string[],\n defaultBaseUrl: string | undefined,\n): string {\n for (const name of names) {\n const value = trimOrNull(env[name])\n if (value) return normalizeBaseUrl(value)\n }\n const value = trimOrNull(defaultBaseUrl)\n if (value) return normalizeBaseUrl(value)\n throw new Error(\n `Sandbox base URL is required (set one of ${names.join(', ')} or pass defaultBaseUrl).`,\n )\n}\n\nfunction resolveDirectSandboxCredentials(\n env: Record<string, string | undefined>,\n keyNames: readonly string[],\n baseUrlNames: readonly string[],\n defaultBaseUrl: string | undefined,\n): SandboxClientCredentials | null {\n for (const name of keyNames) {\n const apiKey = trimOrNull(env[name])\n if (!apiKey) continue\n return {\n apiKey,\n baseUrl: resolveSandboxBaseUrl(env, baseUrlNames, defaultBaseUrl),\n }\n }\n return null\n}\n\n/** Resolve sandbox client credentials based on environment and provided options asynchronously */\nexport async function resolveSandboxClientCredentials(\n options: ResolveSandboxClientCredentialsOptions = {},\n): Promise<SandboxClientCredentials> {\n const env = options.env ?? processEnv()\n const environment = options.environment ?? resolveTangleExecutionEnvironment(env)\n const keyNames = options.directKeyNames ?? DEFAULT_SANDBOX_DIRECT_KEY_NAMES\n const baseUrlNames = options.baseUrlNames ?? DEFAULT_SANDBOX_BASE_URL_NAMES\n const directAllowed = directEnvCredentialsAllowed(environment, options.allowDirectEnvCredentials)\n const direct = () =>\n directAllowed\n ? resolveDirectSandboxCredentials(env, keyNames, baseUrlNames, options.defaultBaseUrl)\n : null\n\n if (environment === 'development' || environment === 'test') {\n const credentials = direct()\n if (credentials) return credentials\n }\n\n const provisioned = await options.provision?.({ environment, env })\n if (provisioned) {\n return {\n apiKey: provisioned.apiKey,\n baseUrl: normalizeBaseUrl(provisioned.baseUrl),\n }\n }\n\n const credentials = direct()\n if (credentials) return credentials\n\n const directHint = directAllowed\n ? ` or set one of ${keyNames.join(', ')}`\n : ''\n throw new Error(\n `Sandbox credentials are required for ${environment} (provide a provision callback${directHint}).`,\n )\n}\n\n/** Define configuration parameters for sandbox resource allocation and lifecycle management */\nexport interface SandboxResourceConfig {\n image: string\n cpuCores: number\n memoryMB: number\n diskGB: number\n maxLifetimeSeconds: number\n idleTimeoutSeconds: number\n}\n\n/** Define the context for building a sandbox including workspace, integrations, and optional user ID */\nexport interface SandboxBuildContext {\n workspaceId: string\n connectedIntegrationIds: string[]\n userId?: string\n}\n\n// SDK-typed snapshot storage config (re-exported for product seam closures).\nexport type { StorageConfig }\n\n// Scope handed to per-identity seams. workspaceId is always present; userId is\n// present when the lifecycle op carries one. Workspace-keyed products ignore userId.\n/** Define a scope containing workspace and optional user identifiers for sandbox environments */\nexport interface SandboxScope {\n workspaceId: string\n userId?: string\n}\n\n// Snapshot RESTORE-on-create. Returned alongside storage; undefined => fresh box.\n/** Define the specification for restoring a sandbox from a snapshot or another sandbox ID */\nexport interface SandboxRestoreSpec {\n fromSnapshot: string\n fromSandboxId: string\n}\n\n/** Describe the failure details when resuming a stopped sandbox instance */\nexport interface StoppedSandboxResumeFailure {\n box: SandboxInstance\n error: Error\n scope: SandboxScope\n boxKey: string\n}\n\n/** Define the structure for resuming a stopped sandbox with replacement key and optional restore options */\nexport interface StoppedSandboxResumeRecovery {\n // Used for both the replacement sandbox name and idempotency key.\n replacementBoxKey: string\n // undefined preserves the configured restore seam; null creates without one.\n restore?: SandboxRestoreSpec | null\n}\n\n/**\n * Default ERE passed to `pgrep -f` when a liveness probe does not override the\n * harness-process matcher. It covers the platform process names used by the\n * fleet's shared OpenCode, Claude Code, and Codex terminal path; products with\n * another harness can override it.\n */\nexport const DEFAULT_SIDECAR_PROCESS_PATTERN = 'opencode|claude|codex'\n\n// Reuse health gate + sidecar liveness. The exec+timeout-race is generic; a\n// product with a custom harness can override the default process matcher.\n// Absent livenessProbe => no probe (reuse on metadata.harness match).\n/** Define configuration for liveness probes including sidecar process pattern and optional timeouts */\nexport interface LivenessProbeConfig {\n sidecarProcessPattern?: (harness: Harness) => string\n execTimeoutMs?: number\n psTimeoutMs?: number\n}\n\n/** Define options for composing a user profile including prompts, files, servers, and name */\nexport interface ProfileComposeOptions {\n systemPrompt?: string\n extraFiles?: AgentProfileFileMount[]\n extraMcp?: Record<string, AgentProfileMcpServer>\n name?: string\n /**\n * The harness this profile is being composed for.\n *\n * Where a resource lands is harness-specific — skills resolve to\n * `.opencode/skills/`, `.claude/skills/`, `.pi/skills/`, and so on\n * (`skillDirForHarness`), and some harnesses take no cwd skills at all.\n * Without this, an app composing a profile cannot place a harness-native\n * resource and is pushed into hardcoding one harness's path, which then\n * silently disagrees with the path its own prompt cites: the agent is told\n * to read a directory nothing was written to and its skills are invisible.\n * Prefer declaring `resources.skills` and letting the platform place them;\n * use this when composing paths directly.\n */\n harness: Harness\n}\n\n/** Define runtime configuration methods for sandbox environments including credentials, metadata, and permissions */\nexport interface SandboxRuntimeConfig {\n // Widened to accept an optional scope and be async so a per-user key can be\n // minted. The sync, no-arg form still satisfies the type (back-compat).\n credentials: (\n scope?: SandboxScope,\n ) => SandboxClientCredentials | null | Promise<SandboxClientCredentials | null>\n name: (workspaceId: string) => string\n metadata: (harness: Harness) => Record<string, unknown>\n connectedIntegrationIds: (workspaceId: string) => Promise<string[]>\n env: (ctx: SandboxBuildContext) => Promise<Record<string, string>>\n files: (ctx: SandboxBuildContext) => Promise<AgentProfileFileMount[]>\n secrets: (workspaceId: string) => Promise<string[]>\n profile: (options: ProfileComposeOptions) => AgentProfile\n permissionRole?: (workspaceRole: string) => SandboxPermissionLevel\n resources?: SandboxResourceConfig\n provider?: ProviderResolutionConfig\n\n /**\n * Product-declared outbound network policy. Applied when a sandbox is\n * created. A reused or resumed sandbox is returned only when its explicit\n * policy already matches; existing mismatches are rejected without updating\n * or deleting the sandbox.\n */\n egressPolicy?: EgressPolicy\n\n // BYOS3/R2 snapshot storage. Returns undefined => key omitted entirely\n // (fail-closed when creds absent). Product owns bucket/endpoint/credentials/prefix.\n storage?: (ctx: SandboxBuildContext) => StorageConfig | undefined\n // Snapshot RESTORE-on-create. undefined => fresh box.\n restore?: (ctx: SandboxBuildContext) => SandboxRestoreSpec | undefined\n // Per-identity box NAME. Defaults to name(scope.workspaceId) when absent.\n boxKey?: (scope: SandboxScope) => string\n // Per-workspace child-key mint: overrides the resolved model apiKey before create.\n // Applied only when a model is resolved and its provider is openai-compat.\n childKeyMint?: (scope: SandboxScope) => Promise<Outcome<string>>\n // One-shot post-running bootstrap, on BOTH create and reuse paths (idempotency\n // is the closure's job — it owns the marker check).\n bootstrap?: (box: SandboxInstance, scope: SandboxScope) => Promise<Outcome<void>>\n // Reuse health gate + sidecar liveness probe.\n livenessProbe?: LivenessProbeConfig\n // Enable browser terminal endpoints on newly-created sandboxes.\n webTerminalEnabled?: boolean\n // default true: try stopped-resume before create.\n resumeStopped?: boolean\n // Product-owned retention/recovery policy after a stopped box fails to resume.\n // Return ok(null) to preserve the original resume error. A replacement key is\n // required before this shell creates a new box, so it cannot resolve the\n // failed stopped box through the original idempotency identity.\n recoverStoppedSandbox?: (\n failure: StoppedSandboxResumeFailure,\n ) => Promise<Outcome<StoppedSandboxResumeRecovery | null>>\n // default false: bake resolveModel() into backend.model at create.\n backendModelAtCreate?: boolean\n // default false: write the profile's `resources.files` INTO the box after it\n // reaches running (via `box.exec`), instead of inlining them in the create\n // payload. The orchestrator caps the provision body at 256 KiB; a large\n // file corpus (skills, tool scripts) blows that cap. Deferring it keeps the\n // provision body small and uncapped in corpus size, and lands real files on\n // disk — which is also the only path that works for harnesses whose backend\n // does not materialize the provider-neutral `resources.files` channel.\n //\n // Only `kind: 'inline'` files are deferred; non-inline refs (e.g. github)\n // stay in the create payload so the orchestrator resolves them. Runs on the\n // create AND resume/reuse paths (idempotent overwrite). Inline files are\n // STRIPPED from `resources.files` before create when this is set.\n deferProfileFiles?: boolean\n // Byte budget on the system prompt of the profile this shell actually SENDS\n // (provision + every turn). Defaults to the same 40 KB cap\n // `composeAgentProfile` applies; set `warnOnly` + `overBudgetReason` to\n // downgrade it. Wired here because the composer is opt-in: a product may\n // hand-build its profile, or carry its real prompt only on the PER-TURN\n // backend, and then the compose-time gate never runs on the bytes the model\n // sees. An over-budget prompt degrades toward empty answers, which is\n // indistinguishable from a broken stream — so it fails loud instead.\n promptBudget?: ComposeProfileBudget\n}\n\n/** Define default resource limits and settings for sandbox environments */\nexport const DEFAULT_SANDBOX_RESOURCES: SandboxResourceConfig = {\n image: 'universal',\n cpuCores: 2,\n memoryMB: 4096,\n diskGB: 10,\n maxLifetimeSeconds: 86400,\n idleTimeoutSeconds: 3600,\n}\n\ninterface ClientCacheEntry {\n client: Sandbox\n fingerprint: string\n}\n\nlet _cached: ClientCacheEntry | null = null\n\nfunction getClientFromCreds(creds: SandboxClientCredentials): Sandbox {\n const fingerprint = `${creds.apiKey} ${creds.baseUrl}`\n if (_cached && _cached.fingerprint === fingerprint) return _cached.client\n\n const client = new Sandbox({ apiKey: creds.apiKey, baseUrl: creds.baseUrl })\n _cached = { client, fingerprint }\n return client\n}\n\n// Sync client for non-scoped callers (secretStoreFromClient etc.). Resolves\n// credentials with no scope; throws if the seam returns a Promise — scoped\n// products must use the async ensureWorkspaceSandbox path.\n/** Resolve a synchronous sandbox client from provided runtime configuration credentials */\nexport function getClient(shell: SandboxRuntimeConfig): Sandbox {\n const creds = shell.credentials()\n if (creds && typeof (creds as Promise<unknown>).then === 'function') {\n throw new Error('getClient: scoped (async) credentials require the async sandbox path')\n }\n if (!creds) throw new Error('sandbox credentials are required (apiKey/baseUrl)')\n return getClientFromCreds(creds as SandboxClientCredentials)\n}\n\n/** Reset the client cache to clear stored data and force fresh retrieval */\nexport function resetClientCache(): void {\n _cached = null\n}\n\n/** Describe an application tool with its name, unique key, and description */\nexport interface AppToolDescriptor {\n tool: AppToolName\n key: string\n description: string\n}\n\n/** Define options for building MCP server configurations in the app tool environment */\nexport interface BuildAppToolMcpServersOptions {\n tools: AppToolDescriptor[]\n baseUrl: string\n token: string\n ctx: AppToolContext\n headerNames?: ToolHeaderNames\n}\n\n/** Build a mapping of MCP server profiles keyed by tool identifiers from provided options */\nexport function buildAppToolMcpServers(\n options: BuildAppToolMcpServersOptions,\n): Record<string, AgentProfileMcpServer> {\n const entries: Record<string, AgentProfileMcpServer> = {}\n for (const { tool, key, description } of options.tools) {\n entries[key] = buildAppToolMcpServer({\n tool,\n baseUrl: options.baseUrl,\n token: options.token,\n ctx: options.ctx,\n description,\n headerNames: options.headerNames,\n }) as AgentProfileMcpServer\n }\n return entries\n}\n\n/** Define options for ensuring a workspace sandbox with provisioning and progress handling */\nexport interface EnsureWorkspaceSandboxOptions {\n workspaceId: string\n userId?: string\n harness: Harness\n // When set, both the running-reuse and stopped-resume short-circuits are\n // skipped and any name-matched box is deleted before create. This is the\n // ONLY way a liveness-failed box is discarded: a failed probe triggers a\n // state-preserving stop→resume recovery (or a SandboxRecoveryFailedError),\n // never a delete — delete deprovisions the persistent workspace (#299).\n forceNew?: boolean\n // Real-time provisioning progress from the SDK's SSE stream, forwarded from\n // the `waitFor('running')` calls on the resume and cold-create paths. Callers\n // surface it as live \"warming up\" status. Only fires while a box is actually\n // being provisioned; a reused running box emits nothing.\n onProgress?: (event: ProvisionEvent) => void\n // Billing owner for the created box, forwarded VERBATIM on the create\n // payload. The sandbox platform honors it only when the create-auth\n // principal is a trusted first-party service; the box's usage then bills\n // this platform user's wallet (the platform's per-user router-key mint)\n // instead of the service account that authenticated the create. Omitted =>\n // platform default (billing owner = create-auth principal) — unchanged.\n billingOwnerId?: string\n}\n\n// Single-quote a string for safe interpolation into a shell command.\nfunction shellSingleQuote(value: string): string {\n return `'${value.replace(/'/g, `'\\\\''`)}'`\n}\n\n/** Define the specification for a sandbox tool including its name, content, and optional executability */\nexport interface SandboxToolSpec {\n name: string\n content: string\n executable?: boolean\n}\n\n/** Define options for resolving sandbox tool paths including appName, baseDir, and binDir */\nexport interface SandboxToolPathOptions {\n appName: string\n baseDir?: string\n binDir?: string\n}\n\n/** Define options for building sandbox tool file mounts including tool specifications and paths */\nexport interface BuildSandboxToolFileMountsOptions extends SandboxToolPathOptions {\n tools: readonly SandboxToolSpec[]\n}\n\nconst DEFAULT_SANDBOX_TOOL_BASE_DIR = '/home/agent/tools'\nconst SAFE_TOOL_SEGMENT = /^[A-Za-z0-9._-]+$/\n\nfunction normalizeSandboxToolSegment(value: string, label: string): string {\n const segment = value.trim()\n if (!segment || segment === '.' || segment === '..' || !SAFE_TOOL_SEGMENT.test(segment)) {\n throw new Error(`${label} must contain only letters, numbers, dots, underscores, or hyphens.`)\n }\n return segment\n}\n\nfunction normalizeSandboxToolDir(value: string, label: string): string {\n const dir = value.trim().replace(/\\/+$/, '')\n if (!dir || !dir.startsWith('/') || dir.includes('\\0') || dir.includes('\\n')) {\n throw new Error(`${label} must be an absolute sandbox path.`)\n }\n return dir === '' ? '/' : dir\n}\n\n/** Resolve the root directory path for a sandbox tool based on provided options */\nexport function sandboxToolRootDir(options: SandboxToolPathOptions): string {\n const appName = normalizeSandboxToolSegment(options.appName, 'sandbox tool appName')\n const baseDir = normalizeSandboxToolDir(\n options.baseDir ?? DEFAULT_SANDBOX_TOOL_BASE_DIR,\n 'sandbox tool baseDir',\n )\n return `${baseDir}/${appName}`\n}\n\n/** Resolve the binary directory path for a sandbox tool based on provided options */\nexport function sandboxToolBinDir(options: SandboxToolPathOptions): string {\n normalizeSandboxToolSegment(options.appName, 'sandbox tool appName')\n if (options.binDir) return normalizeSandboxToolDir(options.binDir, 'sandbox tool binDir')\n return `${sandboxToolRootDir(options)}/bin`\n}\n\n/** Resolve the file system path to a specified sandbox tool based on given options */\nexport function sandboxToolPath(options: SandboxToolPathOptions & { toolName: string }): string {\n const toolName = normalizeSandboxToolSegment(options.toolName, 'sandbox tool name')\n return `${sandboxToolBinDir(options)}/${toolName}`\n}\n\n/** Build file mounts for sandbox tools based on provided options and tool configurations */\nexport function buildSandboxToolFileMounts(\n options: BuildSandboxToolFileMountsOptions,\n): AgentProfileFileMount[] {\n return options.tools.map((tool) => {\n const name = normalizeSandboxToolSegment(tool.name, 'sandbox tool name')\n return {\n path: sandboxToolPath({ ...options, toolName: name }),\n resource: { kind: 'inline' as const, name, content: tool.content },\n executable: tool.executable ?? true,\n }\n })\n}\n\n/** Build a shell script that sets up and exports the sandbox tool binary directory in user profiles */\nexport function buildSandboxToolPathSetupScript(options: SandboxToolPathOptions): string {\n const binDir = sandboxToolBinDir(options)\n const exportLine = `export PATH=${binDir}:$PATH`\n return [\n 'set -eu',\n `mkdir -p ${shellSingleQuote(binDir)}`,\n `PATH=${shellSingleQuote(binDir)}:$PATH`,\n 'export PATH',\n 'for profile in \"${HOME:-/home/agent}/.profile\" \"${HOME:-/home/agent}/.bashrc\" \"${HOME:-/home/agent}/.zshrc\"; do',\n ' mkdir -p \"$(dirname \"$profile\")\"',\n ' touch \"$profile\"',\n ` grep -Fqx ${shellSingleQuote(exportLine)} \"$profile\" || printf '\\\\n%s\\\\n' ${shellSingleQuote(exportLine)} >> \"$profile\"`,\n 'done',\n ].join('\\n')\n}\n\n/** Resolve the sandbox environment PATH setup by executing the configuration script with given options */\nexport async function runSandboxToolPathSetup(\n box: SandboxInstance,\n options: SandboxToolPathOptions,\n): Promise<Outcome<void>> {\n try {\n const res = await box.exec(buildSandboxToolPathSetupScript(options))\n if (res.exitCode !== 0) {\n return fail(\n new Error(\n `runSandboxToolPathSetup: failed to configure PATH for ${sandboxToolBinDir(options)} ` +\n `(exit ${res.exitCode}): ${res.stderr.slice(0, 500)}`,\n ),\n )\n }\n return ok(undefined)\n } catch (err) {\n return fail(new Error('runSandboxToolPathSetup: exec failed', { cause: err }))\n }\n}\n\n// Build a shell-safe path token that preserves tilde-home semantics. A path\n// beginning `~/` (or a bare `~`) must resolve to the box user's real `$HOME`,\n// but single-quoting the whole path suppresses shell `~` expansion and lands\n// the file in a literal directory named `~`. Expand the leading `~` to an\n// UNQUOTED `\"$HOME\"` (so the shell expands it) and single-quote only the\n// remainder. Absolute and relative paths are single-quoted unchanged.\nfunction shellPath(path: string): string {\n if (path === '~') return '\"$HOME\"'\n if (path.startsWith('~/')) {\n const rest = path.slice(2)\n return rest ? `\"$HOME\"/${shellSingleQuote(rest)}` : '\"$HOME\"'\n }\n return shellSingleQuote(path)\n}\n\n// Split a profile's `resources.files` into the inline mounts that can be\n// written into a running box and the rest (non-inline refs that the\n// orchestrator must resolve, so they stay in the create payload). Returns the\n// inline set to defer and a profile copy with those inline files removed.\n/** Split profile files into inline deferred files and a lean profile without them */\nexport function splitDeferredProfileFiles(\n profile: AgentProfile,\n): { leanProfile: AgentProfile; deferredFiles: AgentProfileFileMount[] } {\n const files = profile.resources?.files ?? []\n const deferredFiles: AgentProfileFileMount[] = []\n const keptFiles: AgentProfileFileMount[] = []\n for (const mount of files) {\n if (mount.resource.kind === 'inline') deferredFiles.push(mount)\n else keptFiles.push(mount)\n }\n if (deferredFiles.length === 0) return { leanProfile: profile, deferredFiles }\n const leanProfile: AgentProfile = {\n ...profile,\n resources: { ...(profile.resources ?? {}), files: keptFiles },\n }\n return { leanProfile, deferredFiles }\n}\n\n// The runtime exec proxy (`box.exec` → /terminals/commands) hangs (30s\n// timeout) on any request whose body crosses ~4096 bytes, and one oversized\n// exec wedges the channel so every later exec on the box hangs too. We slice\n// each file's base64 into appends whose full command string stays well under\n// that cap. 3000 chars of base64 leaves ~1000 bytes of headroom for the\n// surrounding `printf '%s' '<slice>' >> <path>.b64` command plus the proxy's\n// JSON request envelope — comfortably below 4096.\nconst PROFILE_WRITE_B64_CHUNK_CHARS = 3000\n\n// gtm's corpus is ~135 small execs; on a cold box the runtime exec proxy\n// hard-throttles (HTTP 429 after ~21 execs in ~34s) and, under sustained load,\n// SILENTLY HANGS instead of returning 429 — one parked exec wedges the channel\n// and `writeProfileFilesToBox` never returns, so `ensureWorkspaceSandbox`\n// parks the worker (~140s, no exception). These failures plus sidecar exec-plane\n// readiness/transport failures (5xx, reset, fetch/network errors) are transient:\n// retry the SAME exec with exponential backoff before failing loud. A non-zero\n// exit is a real command failure, never retried.\nconst PROFILE_WRITE_MAX_RETRIES = 4\nconst PROFILE_WRITE_RETRY_BASE_MS = 250\nconst PROFILE_WRITE_RETRY_MAX_MS = 2000\n\n// Client-side hard ceiling per exec. The proxy's own 30s timeout is unreliable\n// once the channel wedges (it can hang past it), so we race each exec against an\n// independent timer we control: a wedged exec is abandoned here and retried as a\n// transient transport error, never silently parked. We also pass timeoutMs to\n// the SDK as defense-in-depth, but the race is the guarantee.\nconst PROFILE_WRITE_EXEC_TIMEOUT_MS = 30_000\n\n// Pacing between execs to stay under the proxy throttle that triggers the hang.\n// The drill saw throttling at ~0.6 exec/s bursts; ~150ms between ~135 execs\n// adds well under a minute total and keeps the burst rate below the trip point.\n// On by default for the deferred path; overridable for tests/tuning.\nconst PROFILE_WRITE_PACE_MS = 150\n\nconst sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms))\n\n// A mount whose path sits in a bin directory is made executable. Shared so the\n// file-API eligibility check and the exec branch agree on \"executable by dir\".\nconst PROFILE_BIN_DIR_RE = /(^|\\/)(s?bin)\\//\n\n// Sentinel cause for a client-side per-exec timeout (a hung/wedged proxy exec).\n// Carried as the `.cause` of the fail-loud Outcome so callers see the wedged\n// command instead of an opaque infinite hang.\nclass ProfileWriteExecTimeoutError extends Error {\n constructor(timeoutMs: number) {\n super(`exec exceeded ${timeoutMs}ms (proxy hang/wedge)`)\n this.name = 'ProfileWriteExecTimeoutError'\n }\n}\n\n// Run a box.exec, abandoning it if it does not settle within timeoutMs. The\n// returned promise rejects with ProfileWriteExecTimeoutError on timeout so the\n// retry loop treats a hang exactly like a transient transport error. We also\n// forward timeoutMs to the SDK so the underlying request is cancelled when the\n// SDK honors it; the race covers the case where it does not.\nfunction execWithTimeout(\n box: SandboxInstance,\n cmd: string,\n timeoutMs: number,\n): Promise<ExecResult> {\n return new Promise<ExecResult>((resolve, reject) => {\n let settled = false\n const timer = setTimeout(() => {\n if (settled) return\n settled = true\n reject(new ProfileWriteExecTimeoutError(timeoutMs))\n }, timeoutMs)\n box.exec(cmd, { timeoutMs }).then(\n (res) => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n resolve(res)\n },\n (err) => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n reject(err)\n },\n )\n })\n}\n\nconst TRANSIENT_EXEC_STATUS_CODES = new Set([408, 409, 425, 429, 500, 502, 503, 504])\nconst TRANSIENT_EXEC_CODE_RE = /^(ECONNRESET|ECONNREFUSED|ETIMEDOUT|EPIPE|ECONNABORTED)$/i\nconst TRANSIENT_EXEC_MESSAGE_RE =\n /\\b(408|409|425|429|500|502|503|504)\\b|rate.?limit|too many requests|\\bfetch failed\\b|network error|connection reset|socket hang up|timed? out|service unavailable|bad gateway|gateway timeout|internal server error|\\b(?:sidecar|runtime|exec(?:ution)?|terminal|sandbox|service|command(?:s)?|proxy)\\b.{0,80}\\bnot ready\\b|\\bnot ready\\b.{0,80}\\b(?:sidecar|runtime|exec(?:ution)?|terminal|sandbox|service|command(?:s)?|proxy)\\b/i\nconst RUNTIME_AUTH_REFRESH_SKEW_MS = 60_000\n\nfunction errorStatus(err: { status?: unknown; statusCode?: unknown; response?: unknown }): number | undefined {\n const rawStatus = err.status ?? err.statusCode ?? (\n err.response && typeof err.response === 'object'\n ? (err.response as { status?: unknown }).status\n : undefined\n )\n if (typeof rawStatus === 'number') return rawStatus\n if (typeof rawStatus === 'string' && /^\\d+$/.test(rawStatus)) return Number(rawStatus)\n return undefined\n}\n\nfunction retryAfterMs(err: unknown, seen = new Set<object>()): number | undefined {\n if (!err || typeof err !== 'object') return undefined\n if (seen.has(err)) return undefined\n seen.add(err)\n const e = err as { retryAfterMs?: unknown; cause?: unknown }\n if (typeof e.retryAfterMs === 'number') return e.retryAfterMs\n return retryAfterMs(e.cause, seen)\n}\n\nfunction isTransientExecError(err: unknown, seen = new Set<object>()): boolean {\n if (!err || typeof err !== 'object') return false\n if (seen.has(err)) return false\n seen.add(err)\n const e = err as {\n status?: unknown\n statusCode?: unknown\n response?: unknown\n code?: unknown\n message?: unknown\n cause?: unknown\n }\n const status = errorStatus(e)\n if (status !== undefined && TRANSIENT_EXEC_STATUS_CODES.has(status)) return true\n if (typeof e.code === 'string') {\n if (TRANSIENT_EXEC_CODE_RE.test(e.code)) return true\n if (/rate.?limit|too.?many.?requests|429|server.?error|service.?unavailable/i.test(e.code)) return true\n }\n if (typeof e.message === 'string' && TRANSIENT_EXEC_MESSAGE_RE.test(e.message)) return true\n return isTransientExecError(e.cause, seen)\n}\n\nfunction isRuntimeExecAuthError(err: unknown, seen = new Set<object>()): boolean {\n if (!err || typeof err !== 'object') return false\n if (seen.has(err)) return false\n seen.add(err)\n const e = err as {\n status?: unknown\n statusCode?: unknown\n response?: unknown\n code?: unknown\n name?: unknown\n message?: unknown\n cause?: unknown\n }\n if (errorStatus(e) === 401) return true\n if (\n typeof e.code === 'string' &&\n /^(AUTH_ERROR|AUTHENTICATION_ERROR|UNAUTHORIZED|UNAUTHENTICATED|ERR_UNAUTHORIZED|ERR_UNAUTHENTICATED|401)$/i.test(e.code)\n ) {\n return true\n }\n if (\n typeof e.name === 'string' &&\n /^(AuthError|AuthenticationError|UnauthorizedError|UnauthenticatedError|SandboxAuthError)$/i.test(e.name)\n ) {\n return true\n }\n return isRuntimeExecAuthError(e.cause, seen)\n}\n\nfunction isRuntimeAuthRefreshDenied(err: unknown): boolean {\n if (!err || typeof err !== 'object') return false\n return (\n isRuntimeExecAuthError(err) ||\n errorStatus(err as { status?: unknown; statusCode?: unknown; response?: unknown }) === 403\n )\n}\n\n// Classify an exec failure as transient-retryable. Retryable shapes share the\n// same backoff path: rate-limit (HTTP 429 + retryAfterMs), client-side timeout,\n// and transient sidecar/transport/readiness failures surfaced as SandboxError-\n// shaped objects, fetch errors, network resets, or 5xx-ish messages. Everything\n// else fails loud. Non-zero shell exits never reach this classifier.\nfunction transientExecError(err: unknown): { retryable: boolean; retryAfterMs?: number } {\n if (err instanceof ProfileWriteExecTimeoutError) return { retryable: true }\n if (isTransientExecError(err)) return { retryable: true, retryAfterMs: retryAfterMs(err) }\n return { retryable: false }\n}\n\nfunction deferredProfileWriteFailed(stage: 'new' | 'reused' | 'resumed', name: string, cause: Error): Error {\n return new Error(`deferred file write failed on ${stage} box ${name}: ${cause.message}`, { cause })\n}\n\nexport type SandboxExistingBoxStage = 'reused' | 'resumed'\n\nexport type SandboxEgressPolicySource = Awaited<\n ReturnType<SandboxInstance['egress']['get']>\n>['source']\n\n/**\n * Thrown when an existing sandbox cannot be proven to have the requested\n * outbound network policy. The sandbox is preserved and its policy is not\n * changed. The caller explicitly decides whether to preserve, migrate, or\n * replace the sandbox.\n */\nexport class SandboxEgressPolicyMismatchError extends Error {\n readonly stage: SandboxExistingBoxStage\n readonly boxName: string\n readonly currentPolicy: EgressPolicy\n readonly currentSource: SandboxEgressPolicySource\n readonly desiredPolicy: EgressPolicy\n\n constructor(\n stage: SandboxExistingBoxStage,\n boxName: string,\n currentPolicy: EgressPolicy,\n currentSource: SandboxEgressPolicySource,\n desiredPolicy: EgressPolicy,\n ) {\n super(\n `egress policy mismatch on ${stage} box ${boxName}: ` +\n `current ${currentPolicy.mode} policy from ${currentSource} does not explicitly match ` +\n `desired ${desiredPolicy.mode} policy; the existing box was preserved and egress was not updated.`,\n )\n this.name = 'SandboxEgressPolicyMismatchError'\n this.stage = stage\n this.boxName = boxName\n this.currentPolicy = currentPolicy\n this.currentSource = currentSource\n this.desiredPolicy = desiredPolicy\n }\n}\n\n/** Represent an error thrown when sandbox runtime authentication refresh fails for a specific stage and name */\nexport class SandboxRuntimeAuthRefreshError extends Error {\n constructor(stage: SandboxExistingBoxStage, name: string, detail: string, cause?: unknown) {\n super(`${stage} sandbox auth refresh failed for ${name}: ${detail}`, { cause })\n this.name = 'SandboxRuntimeAuthRefreshError'\n }\n}\n\n/** Which step of the state-preserving stop→resume recovery failed. `stop` with a\n * driver-unsupported cause means the platform cannot restart this box (the\n * `tangle` driver exposes create/delete only); `probe` means the box restarted\n * but is still unresponsive. */\nexport type SandboxRecoveryPhase = 'stop' | 'resume' | 'probe'\n\n/**\n * Thrown when an unresponsive box could not be recovered by a state-preserving\n * restart. Contract: this error is only ever thrown with the workspace intact —\n * recovery never deletes. The caller decides what to do next (retry, surface to\n * the user, or explicitly replace via `forceNew`).\n */\nexport class SandboxRecoveryFailedError extends Error {\n readonly boxKey: string\n readonly stage: SandboxExistingBoxStage\n readonly phase: SandboxRecoveryPhase\n constructor(\n stage: SandboxExistingBoxStage,\n boxKey: string,\n phase: SandboxRecoveryPhase,\n detail: string,\n cause?: unknown,\n ) {\n super(\n `${stage} sandbox ${boxKey} failed liveness recovery at ${phase}: ${detail}. ` +\n `The workspace is preserved; pass forceNew to replace the box.`,\n { cause },\n )\n this.name = 'SandboxRecoveryFailedError'\n this.boxKey = boxKey\n this.stage = stage\n this.phase = phase\n }\n}\n\n// Materialize inline profile files into a running box via `box.exec`. Uses a\n// base64 pipe so arbitrary content (scripts, unicode, special chars) lands\n// byte-exact, and writes to ANY absolute path (e.g. /usr/local/bin) or a\n// `~`-relative path — the exec runs as the sidecar, which is not bound by the\n// safe-prefix allow-list the /files/write API enforces. Sets the executable\n// bit when the mount declares it OR the target is a bin directory.\n//\n// Each file is written in several small execs (mkdir, one append per base64\n// chunk, then a decode+cleanup) so no single exec request body trips the\n// ~4 KiB proxy cap. Writes are sequential; a single bad mount stops the batch\n// and the first failure is returned (fail-loud), the rest are not attempted.\n//\n// Each exec is bounded by a client-side hard timeout and retried with backoff\n// on transient rate-limit/readiness/transport failures, so one wedged or early\n// exec-plane request can never park the caller (and thus never park provisioning).\n// A small pace between execs keeps the burst rate below the proxy throttle that\n// triggers the hang.\n/** Define options to control execution timeout, pacing, and retry behavior when writing profile files */\nexport interface WriteProfileFilesOptions {\n // Hard ceiling per exec (ms). A hung/wedged exec is abandoned and retried.\n execTimeoutMs?: number\n // Delay between execs (ms) to stay under the proxy throttle. 0 disables it.\n paceMs?: number\n // Max retries for a transient exec before failing loud.\n maxRetries?: number\n}\n\n// The workspace-relative target for a deferred inline mount when it can be\n// materialized through the sidecar file API (`box.fs.writeMany` — FILES rate\n// group, 300/min, whole-file, auto-mkdir) instead of the chunked terminal-exec\n// path (50/min); null when it must stay on exec. The file API resolves relative\n// paths from the workspace root, so eligibility is: inline and a path that\n// resolves under the workspace root with no `..`/`.sidecar` segment. `~/…` and\n// `/home/agent/…` are home-relative; agent sandboxes set $HOME to the workspace\n// root, so both map to the same relative target. Other absolute (`/…`) and bare\n// `~`/`~user` mounts stay on exec.\nfunction fileApiTarget(mount: AgentProfileFileMount): string | null {\n if (mount.resource.kind !== 'inline') return null\n let rel: string\n if (mount.path.startsWith('~/')) rel = mount.path.slice(2)\n else if (mount.path.startsWith('/home/agent/')) rel = mount.path.slice('/home/agent/'.length)\n else if (mount.path.startsWith('/') || mount.path.startsWith('~')) return null\n else rel = mount.path\n if (rel.length === 0 || rel.startsWith('/') || rel.split('/').some((seg) => seg === '..' || seg === '.sidecar')) {\n return null\n }\n return rel\n}\n\nfunction isExecutableProfileFile(mount: AgentProfileFileMount): boolean {\n return mount.executable ?? PROFILE_BIN_DIR_RE.test(mount.path)\n}\n\nfunction profileFileMode(mount: AgentProfileFileMount): number | undefined {\n return isExecutableProfileFile(mount) ? 0o755 : undefined\n}\n\nfunction fileApiSupportsMode(box: SandboxInstance): boolean {\n const fs = box.fs as (SandboxInstance['fs'] & { supportsWriteMode?: boolean }) | undefined\n return fs?.supportsWriteMode === true\n}\n\n/** Write profile files to a sandbox with pacing, retries, and optional execution timeout handling */\nexport async function writeProfileFilesToBox(\n box: SandboxInstance,\n files: AgentProfileFileMount[],\n options: WriteProfileFilesOptions = {},\n): Promise<Outcome<void>> {\n const execTimeoutMs = options.execTimeoutMs ?? PROFILE_WRITE_EXEC_TIMEOUT_MS\n const paceMs = options.paceMs ?? PROFILE_WRITE_PACE_MS\n const maxRetries = options.maxRetries ?? PROFILE_WRITE_MAX_RETRIES\n // The bulk of a profile corpus (skills, `~/.claude/skills/…`, config) is\n // inline and workspace-relative — write it in ONE paced,\n // retry-aware batch via the SDK's file API (`box.fs.writeMany`, FILES rate\n // group 300/min). The SDK owns the pacing + transient-retry this module used\n // to hand-roll. Absolute / bare-`~` paths can't use the file API\n // (prefix-restricted), so they stay on the chunked exec path.\n // Capability-guarded: SDKs without `writeMany` route everything through\n // exec; SDKs before @tangle-network/sandbox 0.9.4 keep executable files on\n // exec because they do not expose `supportsWriteMode` or forward file modes.\n const fileApiAvailable = typeof box.fs?.writeMany === 'function'\n const modeAwareFileApi = fileApiSupportsMode(box)\n const viaFileApi: { path: string; content: string; mode?: number }[] = []\n const viaExec: AgentProfileFileMount[] = []\n for (const mount of files) {\n if (mount.resource.kind !== 'inline') continue\n const fileApiPath = fileApiAvailable ? fileApiTarget(mount) : null\n const executable = isExecutableProfileFile(mount)\n if (fileApiPath !== null && (!executable || modeAwareFileApi)) {\n const mode = profileFileMode(mount)\n viaFileApi.push({\n path: fileApiPath,\n content: mount.resource.content ?? '',\n ...(mode !== undefined ? { mode } : {}),\n })\n }\n else viaExec.push(mount)\n }\n if (viaFileApi.length > 0) {\n // `writeMany` is fail-loud on the first file it can't write. Preserve the\n // cause so the runtime-auth-refresh wrapper still detects a 401 — it\n // recurses `.cause` for an AuthError/401 shape (see isRuntimeExecAuthError).\n try {\n await box.fs.writeMany(viaFileApi, { paceMs, maxRetries })\n } catch (err) {\n return fail(new Error('writeProfileFilesToBox: file-API batch write failed', { cause: err }))\n }\n }\n\n // Pace BETWEEN execs, not before the first or after the last. Shared across\n // the exec mounts so the (now smaller) executable/edge-case set stays paced.\n let execStarted = false\n\n // Pace + transient-retry ONE exec attempt: backoff/pace/fail-loud in one place.\n // `run` must be idempotent under unknown-outcome retries (exec writes\n // deterministic parts). A transient error retries with backoff up to\n // maxRetries (honoring a server Retry-After); anything else, or exhausting\n // retries, fails loud with the cause.\n const paceAndRetry = async <T>(run: () => Promise<T>, path: string): Promise<Outcome<T>> => {\n for (let attempt = 0; ; attempt++) {\n if (execStarted && paceMs > 0) await sleep(paceMs)\n execStarted = true\n try {\n return ok(await run())\n } catch (err) {\n const { retryable, retryAfterMs } = transientExecError(err)\n if (retryable && attempt < maxRetries) {\n const backoff = Math.min(PROFILE_WRITE_RETRY_BASE_MS * 2 ** attempt, PROFILE_WRITE_RETRY_MAX_MS)\n await sleep(retryAfterMs ?? backoff)\n continue\n }\n return fail(new Error(`writeProfileFilesToBox: exec failed for ${path}`, { cause: err }))\n }\n }\n }\n\n for (const mount of viaExec) {\n if (mount.resource.kind !== 'inline') continue // always true (viaExec is inline) — narrows for TS\n const content = mount.resource.content ?? ''\n const path = mount.path\n\n const b64 = Buffer.from(content, 'utf8').toString('base64')\n const b64Chunks: string[] = []\n for (let i = 0; i < b64.length; i += PROFILE_WRITE_B64_CHUNK_CHARS) {\n b64Chunks.push(b64.slice(i, i + PROFILE_WRITE_B64_CHUNK_CHARS))\n }\n const expectedSha256 = createHash('sha256').update(content, 'utf8').digest('hex')\n const dir = path.replace(/\\/[^/]*$/, '')\n const executable = isExecutableProfileFile(mount)\n const q = shellPath(path)\n const qb64 = shellPath(`${path}.b64`)\n const qtmp = shellPath(`${path}.tmp`)\n const qpartPrefix = shellPath(`${path}.b64.part.`)\n\n // Run one exec step. Transport errors (incl. a wedged-proxy timeout) are\n // paced + retried by paceAndRetry; a non-zero exit is a REAL command failure\n // — surfaced immediately, never retried.\n const step = async (cmd: string): Promise<Outcome<void>> => {\n const res = await paceAndRetry(() => execWithTimeout(box, cmd, execTimeoutMs), path)\n if (!res.succeeded) return res\n const exec = res.value\n if (exec.exitCode !== 0) {\n return fail(\n new Error(\n `writeProfileFilesToBox: failed to write ${path} (exit ${exec.exitCode}): ${exec.stderr.slice(0, 500)}`,\n ),\n )\n }\n return ok(undefined)\n }\n\n // Ensure the target directory exists. Chunk/final commands below are\n // idempotent under unknown-outcome transport retries; this no-op/mkdir is too.\n const mkdir = dir && dir !== path ? `mkdir -p ${shellPath(dir)}` : ':'\n let res = await step(mkdir)\n if (!res.succeeded) return res\n\n // Write each deterministic part with overwrite, not append. If the server\n // writes a part but the HTTP response is lost, retrying the same step lands\n // the same bytes instead of duplicating them.\n for (let i = 0; i < b64Chunks.length; i++) {\n const slice = b64Chunks[i]!\n res = await step(`printf '%s' '${slice}' > ${shellPath(`${path}.b64.part.${i}`)}`)\n if (!res.succeeded) return res\n }\n\n // Materialize from deterministic parts into a temp file, verify its content\n // hash, then atomically move into place. If the final exec succeeds but its\n // response is lost, a retry first accepts an already-correct target and only\n // re-runs idempotent chmod/cleanup. Parts are cleaned only after the target\n // hash is known-good, so a retried final step can always reconstruct.\n const chmod = executable ? `chmod +x ${q} || exit 1; ` : ''\n const checksumMismatch = shellSingleQuote(`writeProfileFilesToBox: checksum mismatch for ${path}`)\n const finalCmd =\n `expected='${expectedSha256}'; ` +\n `if [ -f ${q} ] && [ \"$(sha256sum ${q} | awk '{print $1}')\" = \"$expected\" ]; then ` +\n `${chmod}rm -f ${qb64} ${qtmp}; i=0; while [ \"$i\" -lt ${b64Chunks.length} ]; do rm -f ${qpartPrefix}$i; i=$((i+1)); done; exit 0; fi; ` +\n `: > ${qb64} && ` +\n `i=0; while [ \"$i\" -lt ${b64Chunks.length} ]; do cat ${qpartPrefix}$i >> ${qb64} || exit 1; i=$((i+1)); done && ` +\n `base64 -d ${qb64} > ${qtmp} && ` +\n `[ \"$(sha256sum ${qtmp} | awk '{print $1}')\" = \"$expected\" ] || { echo ${checksumMismatch} >&2; exit 1; }; ` +\n `mv ${qtmp} ${q} && ` +\n `${executable ? `chmod +x ${q} && ` : ''}` +\n `[ \"$(sha256sum ${q} | awk '{print $1}')\" = \"$expected\" ] || { echo ${checksumMismatch} >&2; exit 1; }; ` +\n `rm -f ${qb64} ${qtmp}; i=0; while [ \"$i\" -lt ${b64Chunks.length} ]; do rm -f ${qpartPrefix}$i; i=$((i+1)); done`\n res = await step(finalCmd)\n if (!res.succeeded) return res\n }\n return ok(undefined)\n}\n\n// Resolve the shell's deferred (inline) profile files and write them into a\n// box that already exists (reuse/resume paths). No-op unless the shell opts\n// into deferProfileFiles. Idempotent overwrite — a redeploy with new skills\n// refreshes the corpus on the next ensure call.\n// Box-metadata key holding the content hash of the deferred corpus that was\n// written to the box at CREATE. On reuse, an unchanged hash means the skills are\n// already on disk, so the (large, file-API) re-write can be skipped.\nconst DEFERRED_CORPUS_HASH_KEY = 'agentAppDeferredCorpusHash'\n\n/** Stable content hash of the deferred file corpus (path + inline content).\n * Unchanged corpus ⇒ same hash; a new/edited/removed skill ⇒ different hash.\n * Exported for the reuse-skip test. */\nexport function deferredCorpusHash(files: AgentProfileFileMount[]): string {\n const norm = files\n .map((f) => ({\n p: f.path,\n c: f.resource.kind === 'inline' ? ((f.resource as { content?: string }).content ?? '') : `ref:${f.resource.kind}`,\n }))\n .sort((a, b) => (a.p < b.p ? -1 : a.p > b.p ? 1 : 0))\n return createHash('sha256').update(JSON.stringify(norm), 'utf8').digest('hex')\n}\n\nasync function materializeDeferredFilesForExistingBox(\n shell: SandboxRuntimeConfig,\n client: Sandbox,\n box: SandboxInstance,\n stage: SandboxExistingBoxStage,\n name: string,\n workspaceId: string,\n userId: string | undefined,\n harness: Harness,\n): Promise<Outcome<SandboxInstance>> {\n if (!shell.deferProfileFiles) return ok(box)\n const connectedIntegrationIds = await shell.connectedIntegrationIds(workspaceId)\n const buildCtx: SandboxBuildContext = {\n workspaceId,\n connectedIntegrationIds,\n ...(userId ? { userId } : {}),\n }\n const files = await shell.files(buildCtx)\n const fullProfile = shell.profile({ extraFiles: files, harness })\n const { deferredFiles } = splitDeferredProfileFiles(fullProfile)\n if (deferredFiles.length === 0) return ok(box)\n // Skip the whole re-write when the corpus is UNCHANGED since the box was\n // created with it. The skill corpus is large and the bulk goes through the\n // file API (writeMany) UNCONDITIONALLY, so re-writing it on every reuse adds\n // seconds of latency to each turn. The create payload stamps the corpus hash\n // into box metadata; a matching hash means the skills are already on disk.\n // Fail-safe: a missing or mismatched hash (e.g. a redeploy with new skills, or\n // a box created before this optimization) WRITES — a stale skip is never\n // risked. Reads metadata only, so no extra exec/round-trip.\n const stampedHash = (box.metadata as Record<string, unknown> | undefined)?.[DEFERRED_CORPUS_HASH_KEY]\n if (typeof stampedHash === 'string' && stampedHash === deferredCorpusHash(deferredFiles)) return ok(box)\n return writeDeferredFilesWithRuntimeAuthRefresh(client, box, deferredFiles, stage, name)\n}\n\nasync function listRunning(\n client: Sandbox,\n name: string,\n): Promise<Outcome<SandboxInstance | null>> {\n try {\n const running = await client.list({ status: 'running' })\n return ok(running.find((s) => s.name === name) ?? null)\n } catch (err) {\n return fail(err)\n }\n}\n\nasync function listStopped(\n client: Sandbox,\n name: string,\n): Promise<Outcome<SandboxInstance | null>> {\n try {\n const stopped = await client.list({ status: 'stopped' })\n return ok(stopped.find((s) => s.name === name) ?? null)\n } catch (err) {\n return fail(err)\n }\n}\n\nasync function deleteBox(box: SandboxInstance): Promise<Outcome<void>> {\n try {\n await box.delete()\n return ok(undefined)\n } catch (err) {\n return fail(err)\n }\n}\n\n// The SDK narrows `backend.type` to its own BackendType union and\n// `initialUsers[].role` to PermissionLevel — neither symbol is exported. The\n// create payload is assembled with the product's Harness/role strings, which\n// are a superset surface; the localized cast at the boundary is the only place\n// this widening is allowed, and the runtime contract (the sidecar boots the\n// named harness) is what enforces correctness.\ntype CreatePayload = Parameters<Sandbox['create']>[0]\n\n// ---------------------------------------------------------------------------\n// Provision-time S-cost gates. Both run immediately before `client.create` —\n// each catches an input class that can NEVER produce a working sandbox, so\n// failing at POST-time with actionable detail beats a platform 4xx (or a box\n// that boots and then E2BIGs on every exec).\n\n/** Appended to a prompt-budget throw from any of this module's three choke\n * points. A product that already raised the cap at compose time\n * (`composeAgentProfile(base, channels, overlay, { maxSystemPromptBytes })` —\n * gtm-agent uses 50_000) has to declare the SAME decision on the shell, or the\n * shell's default reads as a contradiction instead of a missing declaration. */\nconst SHELL_PROMPT_BUDGET_HINT =\n 'If this prompt size is a decision you already made at compose time, mirror it on the shell as ' +\n '`promptBudget: { maxSystemPromptBytes, overBudgetReason }` — the shell gate is the one that sees the profile actually sent.'\n\n/** Gate on the provision body: the platform orchestrator caps the create\n * payload at 256 KiB; 240 KB leaves headroom for transport framing. An\n * over-cap payload fails provisioning 100% of the time (a 282 KB payload\n * shipped once and no sandbox could ever be created). */\nexport const PROVISION_PAYLOAD_MAX_BYTES = 240_000\n\n/** Per-variable env gate: the kernel rejects any single `NAME=value` env entry\n * over MAX_ARG_STRLEN (131072 bytes) with E2BIG, killing every exec inside\n * the box. 120 KB leaves headroom for the name and framing. */\nexport const ENV_VALUE_MAX_BYTES = 120_000\n\n/** Total env gate: the whole environment block shares the payload budget with\n * the profile; past 200 KB the provision body cannot stay under the cap. */\nexport const ENV_TOTAL_MAX_BYTES = 200_000\n\nfunction utf8ByteLength(value: unknown): number {\n return new TextEncoder().encode(typeof value === 'string' ? value : JSON.stringify(value ?? null))\n .byteLength\n}\n\n/** Structural slice of the profile the payload gate reads: it only measures\n * the profile's serialized size and names `resources.files` in the breakdown,\n * so callers composing a payload outside the SDK (products, tests) can pass a\n * plain object without casting through `AgentProfile`. */\nexport interface ProvisionProfileSection {\n resources?: { files?: readonly unknown[] }\n}\n\n/** The provision-payload sections the size gates need to see. Structural so\n * the gate is testable without the SDK's (unexported) create-payload type. */\nexport interface ProvisionPayloadSections {\n env?: Record<string, string>\n secrets?: readonly string[]\n /** `profile` may also be a named-profile string ref (the SDK's\n * `BackendConfig` union) — a string ref is tiny and has no files channel. */\n backend?: { profile?: string | ProvisionProfileSection }\n}\n\n/**\n * Throw when the serialized provision payload exceeds\n * {@link PROVISION_PAYLOAD_MAX_BYTES}. The error carries a per-section byte\n * breakdown (profile/files/env/secrets) so the offending channel is named, not\n * guessed.\n */\nexport function assertProvisionPayloadWithinCap(payload: ProvisionPayloadSections): void {\n const total = utf8ByteLength(payload)\n if (total <= PROVISION_PAYLOAD_MAX_BYTES) return\n const profile = payload.backend?.profile\n const files = (typeof profile === 'string' ? undefined : profile?.resources?.files) ?? []\n const breakdown =\n `profile=${utf8ByteLength(profile ?? null)}B ` +\n `(files=${utf8ByteLength(files)}B), ` +\n `env=${utf8ByteLength(payload.env ?? {})}B, ` +\n `secrets=${utf8ByteLength(payload.secrets ?? [])}B`\n throw new Error(\n `sandbox provision payload is ${total} bytes — over the ${PROVISION_PAYLOAD_MAX_BYTES}-byte gate ` +\n `(the platform caps the create body at 256 KiB; an over-cap payload can never create a sandbox). ` +\n `Breakdown: ${breakdown}. ` +\n `Hint: set deferProfileFiles: true or move content to resources.`,\n )\n}\n\n/**\n * Throw when any single env value exceeds {@link ENV_VALUE_MAX_BYTES} or the\n * whole env block exceeds {@link ENV_TOTAL_MAX_BYTES}, naming the offending\n * variable. This is the E2BIG incident class: the box may even provision, but\n * every exec inside it dies on the oversized entry.\n */\nexport function assertEnvWithinLimits(env: Record<string, string>): void {\n let total = 0\n let largest: { name: string; bytes: number } | null = null\n for (const [name, value] of Object.entries(env)) {\n const bytes = utf8ByteLength(`${name}=${value}`)\n total += bytes\n if (!largest || bytes > largest.bytes) largest = { name, bytes }\n if (bytes > ENV_VALUE_MAX_BYTES) {\n throw new Error(\n `sandbox env var ${name} is ${bytes} bytes — over the ${ENV_VALUE_MAX_BYTES}-byte gate ` +\n `(kernel MAX_ARG_STRLEN is 131072 bytes per env entry; anything larger E2BIGs every exec). ` +\n `Write large content to a file mount or resource instead of an env var.`,\n )\n }\n }\n if (total > ENV_TOTAL_MAX_BYTES) {\n const worst = largest ? ` Largest: ${largest.name} (${largest.bytes}B).` : ''\n throw new Error(\n `sandbox env block is ${total} bytes total — over the ${ENV_TOTAL_MAX_BYTES}-byte gate.${worst} ` +\n `Write large content to a file mount or resource instead of env vars.`,\n )\n }\n}\n\nfunction resolveSidecarProcessPattern(\n probe: LivenessProbeConfig,\n harness: Harness,\n): string {\n const pattern =\n probe.sidecarProcessPattern?.(harness) ?? DEFAULT_SIDECAR_PROCESS_PATTERN\n if (pattern.includes('\\\\|')) {\n throw new Error(\n 'Invalid livenessProbe.sidecarProcessPattern: pgrep -f uses extended regular expressions; ' +\n 'use a bare \"|\" for alternation, not \"\\\\|\".',\n )\n }\n return pattern\n}\n\n// Generic exec+sidecar liveness probe. Absent probe => always alive (the prior\n// reuse-on-metadata-match behavior). With a probe: the container must answer an\n// `echo alive` exec within execTimeoutMs, and the harness process must be found\n// by pgrep within psTimeoutMs (an inconclusive pgrep is treated as reusable).\nasync function isBoxAlive(\n box: SandboxInstance,\n harness: Harness,\n probe: LivenessProbeConfig | undefined,\n): Promise<boolean> {\n if (!probe) return true\n const execTimeout = probe.execTimeoutMs ?? 5000\n const psTimeout = probe.psTimeoutMs ?? 3000\n // Resolve and validate configuration before entering the operational catch:\n // a bad ERE is a caller bug, not evidence that the box needs a restart.\n const pattern = resolveSidecarProcessPattern(probe, harness)\n const race = <T>(p: Promise<T>, ms: number, label: string): Promise<T> =>\n Promise.race([\n p,\n new Promise<T>((_, reject) => setTimeout(() => reject(new Error(label)), ms)),\n ])\n try {\n const alive = await race(box.exec('echo alive'), execTimeout, 'alive check timeout')\n if (!alive.stdout.includes('alive')) return false\n try {\n const ps = await race(\n box.exec(`pgrep -f ${shellSingleQuote(pattern)} || echo no-sidecar`),\n psTimeout,\n 'ps check timeout',\n )\n if (ps.stdout.includes('no-sidecar')) return false\n } catch {\n // sidecar probe inconclusive — container is alive, treat as reusable\n }\n return true\n } catch {\n return false\n }\n}\n\nconst RUNTIME_CONNECTION_WAIT_MS = 30_000\nconst RUNTIME_CONNECTION_POLL_MS = 1_000\n\n// `sidecarUrl` is a product/forward-compat field the orchestrator may set on\n// the connection ahead of the SDK declaring it — the v0.6 `SandboxConnection`\n// exposes only `runtimeUrl`. Read it through a typed augmentation rather than an\n// ad-hoc cast (a plain `SandboxConnection` satisfies the optional field), and\n// fall back to the SDK runtime URL. The product-facing `WorkspaceSandboxInstanceLike`\n// carries the same field for consumers driving their own box types.\ntype RuntimeConnectionFields = SandboxConnection & {\n sidecarUrl?: string\n authToken?: string\n sidecarToken?: string\n authTokenExpiresAt?: string | number | Date\n sidecarTokenExpiresAt?: string | number | Date\n}\n\nfunction sandboxRuntimeUrl(box: SandboxInstance): string | undefined {\n const connection: RuntimeConnectionFields | undefined = box.connection\n return connection?.sidecarUrl ?? connection?.runtimeUrl\n}\n\nfunction runtimeAuthExpiresAtMs(value: string | number | Date | undefined): number | undefined {\n if (value instanceof Date) return value.getTime()\n if (typeof value === 'number') return value\n if (typeof value !== 'string' || value.trim() === '') return undefined\n const parsed = Date.parse(value)\n return Number.isNaN(parsed) ? undefined : parsed\n}\n\nfunction hasFreshRuntimeExecAuth(box: SandboxInstance, now = Date.now()): boolean {\n const connection: RuntimeConnectionFields | undefined = box.connection\n const token = connection?.authToken ?? connection?.sidecarToken\n if (!sandboxRuntimeUrl(box) || !token) return false\n const expiresAt = runtimeAuthExpiresAtMs(\n connection?.authTokenExpiresAt ?? connection?.sidecarTokenExpiresAt,\n )\n return expiresAt === undefined || expiresAt > now + RUNTIME_AUTH_REFRESH_SKEW_MS\n}\n\nfunction sandboxEdgeFailed(box: SandboxInstance): boolean {\n // `edgeStatus`/`edgeError` are declared on the SDK's `SandboxConnection`, so no cast.\n const connection = box.connection\n return connection?.edgeStatus === 'failed' || Boolean(connection?.edgeError)\n}\n\nasync function refreshRuntimeConnection(\n client: Sandbox,\n box: SandboxInstance,\n): Promise<SandboxInstance> {\n let current = box\n if (sandboxRuntimeUrl(current)) return current\n\n const deadline = Date.now() + RUNTIME_CONNECTION_WAIT_MS\n while (Date.now() < deadline) {\n // Tolerate transient refresh/get failures (5xx, network blips) while the\n // orchestrator is still attaching the connection: swallow and retry. A box\n // that never surfaces a runtime URL is returned as-is so the caller's\n // readiness gate (isReusableBox) sends it into state-preserving recovery\n // (or fails loud) — rather than this poll throwing a hard provisioning\n // failure on a recoverable hiccup.\n try {\n await current.refresh()\n if (sandboxRuntimeUrl(current)) return current\n\n const latest = await client.get(current.id)\n if (latest) current = latest\n if (sandboxRuntimeUrl(current)) return current\n } catch {\n // transient — fall through to the poll delay and retry\n }\n\n await new Promise((resolve) => setTimeout(resolve, RUNTIME_CONNECTION_POLL_MS))\n }\n\n return current\n}\n\nasync function bestEffortRefreshRuntimeExecAuth(\n client: Sandbox,\n box: SandboxInstance,\n stage: SandboxExistingBoxStage,\n name: string,\n): Promise<Outcome<SandboxInstance>> {\n let current = box\n\n try {\n await current.refresh()\n if (hasFreshRuntimeExecAuth(current)) return ok(current)\n } catch (err) {\n if (isRuntimeAuthRefreshDenied(err)) {\n return fail(\n new SandboxRuntimeAuthRefreshError(\n stage,\n name,\n 'runtime exec auth refresh was unauthorized',\n err,\n ),\n )\n }\n }\n\n try {\n const latest = await client.get(current.id)\n if (latest) current = latest\n if (hasFreshRuntimeExecAuth(current)) return ok(current)\n } catch (err) {\n if (isRuntimeAuthRefreshDenied(err)) {\n return fail(\n new SandboxRuntimeAuthRefreshError(\n stage,\n name,\n 'runtime exec auth re-fetch was unauthorized',\n err,\n ),\n )\n }\n }\n\n return ok(current)\n}\n\nasync function refreshRuntimeExecAuth(\n client: Sandbox,\n box: SandboxInstance,\n stage: SandboxExistingBoxStage,\n name: string,\n): Promise<Outcome<SandboxInstance>> {\n let current = box\n let lastError: unknown\n const deadline = Date.now() + RUNTIME_CONNECTION_WAIT_MS\n\n while (Date.now() < deadline) {\n try {\n await current.refresh()\n if (hasFreshRuntimeExecAuth(current)) return ok(current)\n\n const latest = await client.get(current.id)\n if (latest) current = latest\n if (hasFreshRuntimeExecAuth(current)) return ok(current)\n } catch (err) {\n lastError = err\n }\n\n await new Promise((resolve) => setTimeout(resolve, RUNTIME_CONNECTION_POLL_MS))\n }\n\n const detail = sandboxRuntimeUrl(current)\n ? 'runtime exec credentials are missing or expired after refresh'\n : 'runtime connection is missing after refresh'\n return fail(new SandboxRuntimeAuthRefreshError(stage, name, detail, lastError))\n}\n\nasync function writeDeferredFilesWithRuntimeAuthRefresh(\n client: Sandbox,\n box: SandboxInstance,\n files: AgentProfileFileMount[],\n stage: SandboxExistingBoxStage,\n name: string,\n): Promise<Outcome<SandboxInstance>> {\n let writeBox = box\n\n if (!hasFreshRuntimeExecAuth(writeBox)) {\n const refreshed = await bestEffortRefreshRuntimeExecAuth(client, writeBox, stage, name)\n if (!refreshed.succeeded) return fail(refreshed.error)\n writeBox = refreshed.value\n }\n\n const first = await writeProfileFilesToBox(writeBox, files)\n if (first.succeeded) return ok(writeBox)\n if (!isRuntimeExecAuthError(first.error)) return fail(first.error)\n\n const refreshed = await refreshRuntimeExecAuth(client, writeBox, stage, name)\n if (!refreshed.succeeded) return fail(refreshed.error)\n\n const second = await writeProfileFilesToBox(refreshed.value, files)\n if (second.succeeded) return ok(refreshed.value)\n if (!isRuntimeExecAuthError(second.error)) return fail(second.error)\n\n return fail(\n new SandboxRuntimeAuthRefreshError(\n stage,\n name,\n 'runtime exec remained unauthorized after auth refresh',\n second.error,\n ),\n )\n}\n\n// Decide whether an existing (reused or resumed) box is safe to hand back.\n// `refreshRuntimeConnection` has already polled for the runtime URL, so a box\n// that still has none never became connectable and must not be silently\n// reused — downstream exec/terminal traffic would fail against it. A failed\n// edge is likewise unusable. Only when the connection is present do we spend\n// an exec round-trip on the liveness probe. A box that fails this gate is\n// RECOVERED (stop→resume, workspace intact) or fails loud — never deleted.\nasync function isReusableBox(\n box: SandboxInstance,\n harness: Harness,\n probe: LivenessProbeConfig | undefined,\n): Promise<boolean> {\n if (sandboxEdgeFailed(box)) return false\n if (!sandboxRuntimeUrl(box)) return false\n return isBoxAlive(box, harness, probe)\n}\n\n// Resume a stopped box and wait for it to reach running.\nasync function resumeStoppedBox(\n box: SandboxInstance,\n timeoutMs: number,\n onProgress?: (event: ProvisionEvent) => void,\n): Promise<Outcome<SandboxInstance>> {\n try {\n await box.resume()\n await box.waitFor('running', { timeoutMs, ...(onProgress ? { onProgress } : {}) })\n return ok(box)\n } catch (err) {\n return fail(err)\n }\n}\n\n// State-preserving recovery for a box that failed the reuse gate: stop (keeps\n// the workspace — the platform's suspend path never scrubs storage), resume,\n// then re-probe. `box.delete()` is DEPROVISIONING — it wipes the persistent\n// workspace (sessions, uncommitted files, everything outside the product's own\n// DB) — so it must never be a recovery action (#299: a single slow exec or a\n// crashed harness process used to destroy a user's workspace). This restarts\n// the container, which also reboots a dead sidecar/harness process. Every\n// failure throws SandboxRecoveryFailedError with the workspace intact —\n// including on the `tangle` driver, where stop is unsupported and the throw\n// carries the platform's rejection as its cause.\nasync function recoverUnresponsiveBox(\n client: Sandbox,\n box: SandboxInstance,\n harness: Harness,\n probe: LivenessProbeConfig | undefined,\n stage: SandboxExistingBoxStage,\n name: string,\n resumeTimeout: number,\n onProgress?: (event: ProvisionEvent) => void,\n): Promise<SandboxInstance> {\n try {\n await box.stop()\n } catch (err) {\n throw new SandboxRecoveryFailedError(\n stage,\n name,\n 'stop',\n 'the platform could not stop the box for a state-preserving restart',\n err,\n )\n }\n const resumed = await resumeStoppedBox(box, resumeTimeout, onProgress)\n if (!resumed.succeeded) {\n throw new SandboxRecoveryFailedError(\n stage,\n name,\n 'resume',\n 'the box did not reach running after a state-preserving restart',\n resumed.error,\n )\n }\n const recovered = await refreshRuntimeConnection(client, resumed.value)\n if (!(await isReusableBox(recovered, harness, probe))) {\n throw new SandboxRecoveryFailedError(\n stage,\n name,\n 'probe',\n 'the box is still unresponsive after a state-preserving restart',\n )\n }\n return recovered\n}\n\n/** Scope + the client and box key every workspace-scoped sandbox call needs.\n * One place resolves credentials and derives the box name, so the ensure and\n * peek paths cannot drift on which key a workspace's box lives under. */\nasync function resolveWorkspaceSandboxClient(\n shell: SandboxRuntimeConfig,\n workspaceId: string,\n userId: string | undefined,\n): Promise<{ scope: SandboxScope; client: Sandbox; name: string }> {\n const scope: SandboxScope = { workspaceId, ...(userId ? { userId } : {}) }\n const creds = await shell.credentials(scope)\n if (!creds) throw new Error('sandbox credentials are required (apiKey/baseUrl)')\n return {\n scope,\n client: getClientFromCreds(creds),\n name: shell.boxKey ? shell.boxKey(scope) : shell.name(workspaceId),\n }\n}\n\n/** What a peek can find. `not-running` carries the platform's own state string\n * (`stopped`, `starting`, `failed`, …) — narrowing it to a union here would\n * drop states the platform adds later, and every caller wants it for a log. */\nexport type PeekWorkspaceSandboxOutcome =\n | { status: 'running'; box: SandboxInstance }\n | { status: 'not-running'; state: string; box: SandboxInstance }\n | { status: 'absent' }\n\n/**\n * Read-only twin of {@link ensureWorkspaceSandbox}: report whether a\n * workspace's box exists and is running, WITHOUT provisioning, resuming, or\n * bootstrapping anything.\n *\n * This is what a read-mostly path needs — a file-index route's `authorize`\n * seam, a stale-lock reconciliation, a status badge. Calling `ensure` from one\n * of those spins a box up as a side effect of a read (legal-agent #509), and\n * costs the caller a cold start it never asked for.\n *\n * Matching is on BOTH the box key and the display name, IN THAT ORDER.\n * `client.get(id)` keys on the platform's opaque sandbox id, not the\n * deterministic key a product derives from a workspace, and is itself a\n * `list().find` underneath — so a lookup by identity has to list and match.\n * Provisioning here always stamps `name` with the box key, so the key is the\n * authoritative match; the display-name pass exists only to adopt boxes on a\n * host that predates that convention. The order matters: a single unordered\n * `find` returns whichever the platform happens to list first, so a stopped\n * display-name box could shadow a running box-key one and report\n * `not-running` for a live workspace.\n *\n * Unlike `ensure`, this lists ALL statuses in one call: distinguishing \"no box\"\n * from \"box is stopped\" is the whole point, and a status-filtered list cannot.\n *\n * A `client.list()` rejection propagates RAW, unlike the `Outcome`-wrapping\n * helpers `ensure` uses internally. That is deliberate: there is no honest\n * outcome to map a listing failure onto — it is not `absent` and not\n * `not-running`, and inventing one would have callers act on a status the\n * platform never reported. Callers that must tolerate it say so explicitly\n * (the stale-turn-lock policy documents \"a throw is treated as unreachable\").\n */\nexport async function peekWorkspaceSandbox(\n shell: SandboxRuntimeConfig,\n options: { workspaceId: string; userId?: string },\n): Promise<PeekWorkspaceSandboxOutcome> {\n const { client, name } = await resolveWorkspaceSandboxClient(shell, options.workspaceId, options.userId)\n const displayName = shell.name(options.workspaceId)\n const boxes = await client.list()\n const match = boxes.find((box) => box.name === name) ?? boxes.find((box) => box.name === displayName)\n if (!match) return { status: 'absent' }\n if (match.status !== 'running') return { status: 'not-running', state: match.status, box: match }\n return { status: 'running', box: match }\n}\n\n// The shared tail for handing back an existing (reused/resumed/recovered) box:\n// materialize deferred profile files, then run the product bootstrap. One\n// implementation so the reuse, resume, and recovery paths cannot drift.\nasync function finalizeExistingBox(\n shell: SandboxRuntimeConfig,\n client: Sandbox,\n box: SandboxInstance,\n stage: SandboxExistingBoxStage,\n name: string,\n workspaceId: string,\n userId: string | undefined,\n harness: Harness,\n scope: SandboxScope,\n): Promise<SandboxInstance> {\n await assertExistingBoxEgress(box, shell.egressPolicy, stage, name)\n const written = await materializeDeferredFilesForExistingBox(\n shell,\n client,\n box,\n stage,\n name,\n workspaceId,\n userId,\n harness,\n )\n if (!written.succeeded) {\n throw deferredProfileWriteFailed(stage, name, written.error)\n }\n const finalBox = written.value\n if (shell.bootstrap) {\n const boot = await shell.bootstrap(finalBox, scope)\n if (!boot.succeeded) {\n throw new Error(`bootstrap failed on ${stage} box ${name}`, { cause: boot.error })\n }\n }\n return finalBox\n}\n\nfunction canonicalizeJson(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(canonicalizeJson)\n if (value === null || typeof value !== 'object') return value\n return Object.fromEntries(\n Object.entries(value as Record<string, unknown>)\n .sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0)\n .map(([key, entry]) => [key, canonicalizeJson(entry)]),\n )\n}\n\nfunction normalizedEgressPolicy(policy: EgressPolicy): string {\n const normalized: Record<string, unknown> = { ...policy }\n if (policy.mode === 'strict') {\n normalized.allowDomains = [...new Set(\n (policy.allowDomains ?? [])\n .map((domain) => domain.trim().toLowerCase())\n .filter(Boolean),\n )].sort()\n normalized.includeImplicitDomains = policy.includeImplicitDomains !== false\n }\n return JSON.stringify(canonicalizeJson(normalized))\n}\n\nasync function assertExistingBoxEgress(\n box: SandboxInstance,\n desired: EgressPolicy | undefined,\n stage: SandboxExistingBoxStage,\n name: string,\n): Promise<void> {\n if (!desired) return\n let current: Awaited<ReturnType<SandboxInstance['egress']['get']>>\n try {\n current = await box.egress.get()\n } catch (cause) {\n const error = cause instanceof Error ? cause : new Error(String(cause))\n throw new Error(`egress policy read failed on ${stage} box ${name}: ${error.message}`, {\n cause: error,\n })\n }\n const matchingPolicy = normalizedEgressPolicy(current.policy) === normalizedEgressPolicy(desired)\n const explicitSource = current.source !== 'platform'\n if (matchingPolicy && explicitSource) return\n throw new SandboxEgressPolicyMismatchError(\n stage,\n name,\n current.policy,\n current.source,\n desired,\n )\n}\n\n/** Resolve or create a workspace sandbox instance with optional reuse and progress tracking */\nexport async function ensureWorkspaceSandbox(\n shell: SandboxRuntimeConfig,\n options: EnsureWorkspaceSandboxOptions,\n): Promise<SandboxInstance> {\n const { workspaceId, userId, harness, forceNew, onProgress, billingOwnerId } = options\n const resolved = await resolveWorkspaceSandboxClient(shell, workspaceId, userId)\n const { scope, client } = resolved\n let name = resolved.name\n let recoveryRestore: SandboxRestoreSpec | null | undefined\n const resources = shell.resources ?? DEFAULT_SANDBOX_RESOURCES\n const resumeTimeout = 120_000\n\n // Stage 1 — running-box reuse (skipped on forceNew).\n const existing = await listRunning(client, name)\n if (existing.succeeded && existing.value) {\n const found = existing.value\n if (forceNew) {\n const dropped = await deleteBox(found)\n if (!dropped.succeeded) {\n throw new Error(`forceNew: sandbox ${name} could not be deleted`, { cause: dropped.error })\n }\n } else if (found.metadata?.harness === harness) {\n const ready = await refreshRuntimeConnection(client, found)\n if (await isReusableBox(ready, harness, shell.livenessProbe)) {\n return finalizeExistingBox(shell, client, ready, 'reused', name, workspaceId, userId, harness, scope)\n }\n // Unresponsive (or never-connectable) box with the RIGHT harness: recover\n // in place — stop→resume preserves the workspace — and fail loud if the\n // box is still dead. Deleting here wiped user workspaces (#299); delete\n // is reachable only via forceNew now.\n const recovered = await recoverUnresponsiveBox(\n client,\n ready,\n harness,\n shell.livenessProbe,\n 'reused',\n name,\n resumeTimeout,\n onProgress,\n )\n return finalizeExistingBox(shell, client, recovered, 'reused', name, workspaceId, userId, harness, scope)\n } else {\n const dropped = await deleteBox(found)\n if (!dropped.succeeded) {\n throw new Error(\n `sandbox ${name} ` +\n `(was ${String(found.metadata?.harness ?? 'unknown')}, want ${harness}) ` +\n `could not be deleted`,\n { cause: dropped.error },\n )\n }\n }\n }\n\n // Stage 2 — stopped-box resume (skipped on forceNew or resumeStopped===false).\n if (!forceNew && shell.resumeStopped !== false) {\n const stopped = await listStopped(client, name)\n if (!stopped.succeeded) throw stopped.error\n if (stopped.value) {\n const resumed = await resumeStoppedBox(stopped.value, resumeTimeout, onProgress)\n if (!resumed.succeeded) {\n if (!shell.recoverStoppedSandbox) throw resumed.error\n const recovery = await shell.recoverStoppedSandbox({\n box: stopped.value,\n error: resumed.error,\n scope,\n boxKey: name,\n })\n if (!recovery.succeeded) throw recovery.error\n if (!recovery.value) throw resumed.error\n const replacementBoxKey = recovery.value.replacementBoxKey.trim()\n if (!replacementBoxKey || replacementBoxKey === name) {\n throw new Error(\n `stopped sandbox recovery must return a fresh replacement box key for ${name}`,\n { cause: resumed.error },\n )\n }\n name = replacementBoxKey\n recoveryRestore = recovery.value.restore\n } else {\n const box = await refreshRuntimeConnection(client, resumed.value)\n if (await isReusableBox(box, harness, shell.livenessProbe)) {\n return finalizeExistingBox(shell, client, box, 'resumed', name, workspaceId, userId, harness, scope)\n }\n // The box resumed but is unresponsive: one full stop→resume cycle is\n // the state-preserving recovery; still-dead fails loud. Never delete —\n // that wipes the workspace (#299).\n const recovered = await recoverUnresponsiveBox(\n client,\n box,\n harness,\n shell.livenessProbe,\n 'resumed',\n name,\n resumeTimeout,\n onProgress,\n )\n return finalizeExistingBox(shell, client, recovered, 'resumed', name, workspaceId, userId, harness, scope)\n }\n }\n }\n\n // Stage 3 — create fresh.\n const connectedIntegrationIds = await shell.connectedIntegrationIds(workspaceId)\n const buildCtx: SandboxBuildContext = {\n workspaceId,\n connectedIntegrationIds,\n ...(userId ? { userId } : {}),\n }\n const [secrets, env, files] = await Promise.all([\n shell.secrets(workspaceId),\n shell.env(buildCtx),\n shell.files(buildCtx),\n ])\n const fullProfile = shell.profile({ extraFiles: files, harness })\n // When deferring, strip inline files from the create payload and write them\n // into the box after it reaches running. Keeps the provision body under the\n // orchestrator's 256 KiB cap and lands real files on disk.\n const { leanProfile, deferredFiles } = shell.deferProfileFiles\n ? splitDeferredProfileFiles(fullProfile)\n : { leanProfile: fullProfile, deferredFiles: [] as AgentProfileFileMount[] }\n const profile = leanProfile\n\n const role = userId && shell.permissionRole ? shell.permissionRole('developer') : undefined\n\n // Bake the model at create when opted in. childKeyMint overrides the apiKey\n // per-workspace; a typed mint failure falls through to the parent key (logged).\n let model = shell.backendModelAtCreate\n ? requireTransportableModel(resolveModelSelection(shell.provider), `backendModelAtCreate for ${name}`)\n : undefined\n if (model && shell.childKeyMint && model.provider === 'openai-compat') {\n const minted = await shell.childKeyMint(scope)\n if (minted.succeeded) model = { ...model, apiKey: minted.value }\n else {\n console.error(\n `[sandbox] childKeyMint failed for ${workspaceId}; using parent key:`,\n minted.error.message,\n )\n }\n }\n\n const storage = shell.storage?.(buildCtx)\n const restore = recoveryRestore === undefined ? shell.restore?.(buildCtx) : recoveryRestore\n\n const payload = {\n name,\n image: resources.image,\n // Stamp the deferred-corpus hash so a later REUSE can skip re-writing an\n // unchanged skill corpus (materializeDeferredFilesForExistingBox reads it).\n metadata: {\n ...shell.metadata(harness),\n ...(deferredFiles.length > 0 ? { [DEFERRED_CORPUS_HASH_KEY]: deferredCorpusHash(deferredFiles) } : {}),\n },\n idempotencyKey: name,\n // Passed through untyped (the SDK payload type predates it); the platform\n // authz-gates it server-side and ignores it when unsupported.\n ...(billingOwnerId ? { billingOwnerId } : {}),\n ...(userId ? { permissions: { initialUsers: [{ userId, role }] } } : {}),\n env,\n secrets,\n backend: { type: harness, profile, ...(model ? { model } : {}) },\n ...(storage ? { storage } : {}),\n ...(restore ? restore : {}),\n ...(shell.egressPolicy ? { egressPolicy: shell.egressPolicy } : {}),\n ...(shell.webTerminalEnabled ? { webTerminalEnabled: true } : {}),\n maxLifetimeSeconds: resources.maxLifetimeSeconds,\n idleTimeoutSeconds: resources.idleTimeoutSeconds,\n resources: {\n cpuCores: resources.cpuCores,\n memoryMB: resources.memoryMB,\n diskGB: resources.diskGB,\n },\n } as CreatePayload\n\n // S-cost gates: an oversized env entry (E2BIG class), an over-cap provision\n // body, or a system prompt past the degradation cliff — fail loud here,\n // before the POST, with the offending section named. The prompt gate is\n // separate from the payload gate on purpose: the 122,659-byte prompt that\n // produced empty answers is a THIRD of the 240 KB payload cap, so the\n // payload gate never sees it.\n assertEnvWithinLimits(env)\n assertProfilePromptWithinBudget(\n profile,\n shell.promptBudget ?? {},\n `provision profile systemPrompt for ${name}`,\n SHELL_PROMPT_BUDGET_HINT,\n )\n // `?? {}` only narrows the SDK parameter's `| undefined`; the literal above\n // is always defined. The structural sections type needs no cast.\n assertProvisionPayloadWithinCap(payload ?? {})\n\n let box = await client.create(payload)\n\n await box.waitFor('running', { timeoutMs: 120_000, ...(onProgress ? { onProgress } : {}) })\n box = await refreshRuntimeConnection(client, box)\n\n if (deferredFiles.length > 0) {\n const written = await writeProfileFilesToBox(box, deferredFiles)\n if (!written.succeeded) {\n throw deferredProfileWriteFailed('new', name, written.error)\n }\n }\n\n if (shell.bootstrap) {\n const boot = await shell.bootstrap(box, scope)\n if (!boot.succeeded) {\n throw new Error(`bootstrap failed on new box ${name}`, { cause: boot.error })\n }\n }\n return box\n}\n\n// The SDK's SandboxInstance.streamPrompt/.prompt accept `string | PromptInputPart[]`\n// but the published package does not re-export the PromptInputPart type by name from\n// any of its entry points, so it's derived structurally off the method signature\n// itself — this stays in lockstep with the SDK's actual accepted shape.\n/** Extract a single element type from the array parameter of SandboxInstance's streamPrompt method */\nexport type PromptInputPart = Extract<\n Parameters<SandboxInstance['streamPrompt']>[0],\n readonly unknown[]\n>[number]\n\nfunction historyTranscript(\n history: Array<{ role: 'user' | 'assistant'; content: string }>,\n): string {\n return history\n .map((entry) => `${entry.role === 'assistant' ? 'Assistant' : 'User'}: ${entry.content}`)\n .join('\\n\\n')\n}\n\n/** Build a single string combining conversation history and the current user message */\nexport function flattenHistory(\n message: string,\n history?: Array<{ role: 'user' | 'assistant'; content: string }>,\n): string {\n if (!history?.length) return message\n return `${historyTranscript(history)}\\n\\nUser: ${message}`\n}\n\n/**\n * History-aware equivalent of flattenHistory for multimodal prompt parts: the\n * transcript is folded into the first text part (image/file parts carry no\n * text to prepend to) rather than replacing the message wholesale.\n */\nexport function mergeHistoryIntoParts(\n parts: PromptInputPart[],\n history?: Array<{ role: 'user' | 'assistant'; content: string }>,\n): PromptInputPart[] {\n if (!history?.length) return parts\n const textIndex = parts.findIndex((part) => part.type === 'text')\n if (textIndex === -1) {\n throw new Error('mergeHistoryIntoParts requires at least one text part to carry the history')\n }\n const textPart = parts[textIndex] as Extract<PromptInputPart, { type: 'text' }>\n const merged = [...parts]\n merged[textIndex] = { ...textPart, text: `${historyTranscript(history)}\\n\\nUser: ${textPart.text}` }\n return merged\n}\n\n/** Resolve conflicts and merge extra MCP profiles into the app tool MCP without overwriting existing keys */\nexport function mergeExtraMcp(\n appToolMcp: Record<string, AgentProfileMcpServer>,\n baseProfileMcp: Record<string, AgentProfileMcpServer>,\n extra: Record<string, AgentProfileMcpServer> | undefined,\n): Record<string, AgentProfileMcpServer> {\n for (const key of Object.keys(extra ?? {})) {\n if (key in appToolMcp || key in baseProfileMcp) {\n throw new Error(`extraMcp key '${key}' collides with an existing profile MCP server`)\n }\n }\n return { ...appToolMcp, ...(extra ?? {}) }\n}\n\n/** Attach a specified reasoning effort level to an agent profile for a given harness */\nexport function attachReasoningEffort(\n profile: AgentProfile,\n harness: Harness,\n effort: 'auto' | ReasoningEffort | undefined,\n): AgentProfile {\n if (!effort || effort === 'auto') return profile\n return {\n ...profile,\n extensions: {\n ...(profile.extensions ?? {}),\n [harness]: {\n ...(profile.extensions?.[harness] ?? {}),\n reasoningEffort: effort,\n },\n },\n }\n}\n\n/** Define options for configuring and controlling a streaming sandbox prompt session */\nexport interface StreamSandboxPromptOptions {\n sessionId?: string\n executionId?: string\n /** Stable idempotency key for one logical dispatch. Reuse it with the same\n * `sessionId` when a caller may retry the initial request. */\n turnId?: string\n lastEventId?: string\n systemPrompt?: string\n model?: string\n modelApiKey?: string\n history?: Array<{ role: 'user' | 'assistant'; content: string }>\n harness?: Harness\n effort?: 'auto' | ReasoningEffort\n appToolMcp?: Record<string, AgentProfileMcpServer>\n baseProfileMcp?: Record<string, AgentProfileMcpServer>\n extraMcp?: Record<string, AgentProfileMcpServer>\n signal?: AbortSignal\n timeoutMs?: number\n requireVisibleAssistantOutput?: boolean\n // When true, an interactive question event throws instead of yielding —\n // detached (cron/mission-step) runs have no consumer to answer it.\n disallowQuestions?: boolean\n // Per-turn question/permission/plan channel toggles, forwarded VERBATIM into the\n // backend config. agent-app does not validate kinds per harness — the sidecar fails\n // session init loudly for unsupported ones; a local matrix would drift. Only honored\n // on the streaming path; the detached driveTurn path (driveSandboxTurn) never sets it.\n interactions?: { question?: boolean; permission?: boolean; plan?: boolean }\n // Detach the run from THIS stream's lifetime. When true, dropping the stream —\n // a Worker/isolate restart, a browser refresh, a network blip — does NOT cancel\n // the run: the platform keeps executing it server-side and buffers its events,\n // so a later reconnect (same `sessionId` + `lastEventId`) replays the tail and\n // the run's result survives. This is what makes a WATCHED interactive turn\n // durable — the run no longer dies with the Worker that opened it — while still\n // streaming live (unlike the fire-and-forget `dispatchPrompt`/`driveTurn` path).\n // Omit for a run where closing the tab should stop burning tokens.\n detach?: boolean\n // Observe the EXACT profile handed to the sandbox SDK for this turn — after\n // the system-prompt override, the MCP merge, and reasoning-effort attachment\n // — as a ProfileFingerprint. This is the seam an eval/backtest uses to PROVE\n // it executed the shipped profile instead of re-deriving one and asserting\n // nothing. Invoked once, before the first event is yielded.\n onProfileResolved?: (fingerprint: ProfileFingerprint) => void\n}\n\ntype StreamPromptOptions = Parameters<SandboxInstance['streamPrompt']>[1]\n\n/** Resolve and stream AI-generated responses from a sandboxed environment based on input messages and options */\nexport async function* streamSandboxPrompt(\n shell: SandboxRuntimeConfig,\n box: SandboxInstance,\n message: string | PromptInputPart[],\n options?: StreamSandboxPromptOptions,\n): AsyncGenerator<unknown> {\n const harness = options?.harness ?? 'opencode'\n const model = requireTransportableModel(\n resolveModelSelection(shell.provider, {\n model: options?.model,\n modelApiKey: options?.modelApiKey,\n }),\n 'streamSandboxPrompt',\n )\n\n // Server-side enforcement of the harness↔model policy: a vendor-locked harness\n // (claude-code/codex/kimi-code) must not be sent a foreign-provider model, even\n // if the UI snap was bypassed. Provider-less ids pass (session's own config).\n if (model?.model) assertHarnessModelCompatible(harness, model.model)\n\n const prompt =\n typeof message === 'string'\n ? flattenHistory(message, options?.history)\n : mergeHistoryIntoParts(message, options?.history)\n\n const appToolMcp = options?.appToolMcp ?? {}\n const extraMcp = mergeExtraMcp(appToolMcp, options?.baseProfileMcp ?? {}, options?.extraMcp)\n\n const profile = shell.profile({ systemPrompt: options?.systemPrompt, extraMcp, harness })\n const profileWithEffort = attachReasoningEffort(profile, harness, options?.effort)\n // The per-turn backend can carry a system prompt the create-time profile\n // never had (creative-agent does exactly that), so the budget is re-checked\n // on the profile this turn actually executes.\n assertProfilePromptWithinBudget(\n profileWithEffort,\n shell.promptBudget ?? {},\n 'streamSandboxPrompt profile systemPrompt',\n SHELL_PROMPT_BUDGET_HINT,\n )\n\n // Fingerprint is taken HERE — the one place the final profile exists — so an\n // observer proves what was executed rather than re-deriving what should be.\n if (options?.onProfileResolved) {\n options.onProfileResolved(\n await fingerprintAgentProfile(profileWithEffort, { model: model?.model, harness }),\n )\n }\n\n const stream = box.streamPrompt(prompt, {\n sessionId: options?.sessionId,\n executionId: options?.executionId,\n turnId: options?.turnId,\n lastEventId: options?.lastEventId,\n ...(options?.signal ? { signal: options.signal } : {}),\n ...(options?.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),\n ...(options?.requireVisibleAssistantOutput !== undefined\n ? { requireVisibleAssistantOutput: options.requireVisibleAssistantOutput }\n : {}),\n ...(options?.detach ? { detach: true } : {}),\n backend: {\n type: harness,\n profile: profileWithEffort,\n ...(model ? { model } : {}),\n ...(options?.interactions ? { interactions: options.interactions } : {}),\n },\n } as StreamPromptOptions)\n\n let severedFinishReason: string | null = null\n for await (const event of stream) {\n const step = classifySeveredStream(event)\n if (step) severedFinishReason = step.kind === 'step-finish' && step.severed ? step.reason : null\n if (severedFinishReason && isTerminalPromptEvent(event)) {\n throw new Error(`sandbox model stream severed mid-turn (reason=\"${severedFinishReason}\")`)\n }\n if (options?.disallowQuestions) {\n const q = detectInteractiveQuestion(event)\n if (q) {\n throw new Error(`sandbox agent asked an interactive question during an autonomous run: ${q}`)\n }\n }\n yield event\n }\n // Reconnect-exhausted path: the stream ended on a severed step without a\n // terminal event. A truncated turn must fail loud, not return silently.\n if (severedFinishReason) {\n throw new Error(`sandbox model stream severed mid-turn (reason=\"${severedFinishReason}\")`)\n }\n}\n\n/**\n * Stable identity for a streamed text part, so deltas of one part accumulate\n * together and two concurrent parts never overwrite each other. Harnesses spell\n * the id differently and some snapshot updates carry none at all.\n *\n * Deliberately NOT `/stream`'s `getPartKey`, despite the overlapping property\n * lookup: that one resolves a key for any persisted part across every type and\n * falls back to the CONSTANT `'current'`, which merges all id-less parts into a\n * single lane. Here an id-less part must stay distinct — merging two of them\n * would silently drop one candidate answer, the exact failure class this\n * function was changed to fix. Sharing three lines of lookup would also couple\n * `/sandbox` to `/stream`, which are independent subpaths today.\n */\nfunction textPartId(part: Record<string, unknown> | undefined, fallback: string): string {\n for (const key of ['id', 'partId', 'messagePartId']) {\n const value = part?.[key]\n if (typeof value === 'string' && value.trim()) return value\n }\n return fallback\n}\n\n/**\n * The prompt text a harness may replay back as an assistant text part before it\n * answers. Both spellings are collected because `streamSandboxPrompt` dispatches\n * the HISTORY-FOLDED prompt, not the raw message — an echo replays what was sent.\n */\nfunction dispatchedPromptTexts(\n message: string | PromptInputPart[],\n history: StreamSandboxPromptOptions['history'],\n): string[] {\n const raw =\n typeof message === 'string'\n ? message\n : ((message.find((part) => part.type === 'text') as { text?: string } | undefined)?.text ?? '')\n return [...new Set([raw, flattenHistory(raw, history)].map((text) => text.trim()))].filter(Boolean)\n}\n\n/**\n * The last streamed text part that is not a replay of the prompt. Used only when\n * the turn carried no `result` receipt.\n *\n * Two distinct decisions, and only the first is content-based. WHICH parts are\n * eligible is decided by CONTENT — an echo is excluded because it matches the\n * dispatched prompt, at whatever position it arrived. Choosing among the\n * remaining eligible parts is then ordinal, last-wins, because a harness emits\n * reasoning/preamble parts before the answer. That ordering assumption is safe\n * in a way \"skip the first part\" was not: it can only pick the wrong part when a\n * turn produced several genuine non-echo texts and the final one is not the\n * answer, whereas the positional rule mis-fired on the single-answer case that\n * is overwhelmingly the common one.\n */\nfunction lastNonPromptTextPart(parts: Map<string, string>, promptTexts: string[]): string {\n const values = Array.from(parts.values())\n .map((value) => value.trim())\n .filter((value) => value && !promptTexts.includes(value))\n return values.at(-1) ?? ''\n}\n\n/**\n * Aggregate a sandbox prompt event stream down to the turn's one final answer.\n *\n * Exported SEPARATELY from `runSandboxPrompt` because the aggregation is pure —\n * `AsyncIterable<event> -> string` — while the streaming half is not: a product\n * that mounts per-turn MCP servers or resolves its harness per workspace wraps\n * `streamSandboxPrompt` in its own generator. Binding this logic to one stream\n * function is exactly what pushed three products into forking the whole thing,\n * bug and all. Take this over a local copy no matter whose generator you drive.\n *\n * Two rules earn their keep, and both were learned from the naive version:\n *\n * - Text accumulates PER PART ID, never into one running buffer, so two\n * concurrent text parts cannot overwrite each other.\n * - A prompt echo is identified by CONTENT, never by arrival position. The\n * \"skip whichever text part arrives first\" shortcut is wrong on both sandbox\n * lanes: on the delta lane (`box.streamPrompt`, explicit deltas) the first\n * part is the answer's opening token, so the answer silently loses it; and\n * when the echo arrives AFTER the answer, the function returns the caller's\n * own prompt as the agent's reply. Both failures produce a plausible-looking\n * string, the worst shape for the unattended cron/judge turns this exists for.\n *\n * A blank-but-present `result.finalText` is ignored in favour of the streamed\n * text rather than overwriting a real answer with whitespace.\n *\n * @param events raw sandbox turn events, in order\n * @param message the prompt as handed to the stream, so an echo of it is dropped\n * @param history folded into the dispatched prompt by `streamSandboxPrompt`;\n * pass whatever was passed there, since the echo replays the FOLDED text\n */\nexport async function collectSandboxPromptText(\n events: AsyncIterable<unknown>,\n message: string | PromptInputPart[],\n history?: StreamSandboxPromptOptions['history'],\n): Promise<string> {\n let finalText = ''\n const textParts = new Map<string, string>()\n let anonymousTextPart = 0\n\n for await (const rawEvent of events) {\n const event = rawEvent as { type?: string; data?: Record<string, unknown> }\n if (!event.type) continue\n\n if (event.type === 'message.part.updated') {\n const part = event.data?.part as Record<string, unknown> | undefined\n const delta = typeof event.data?.delta === 'string' ? event.data.delta : null\n if (String(part?.type ?? '') === 'text') {\n const partId = textPartId(part, delta ? 'delta' : `text-${anonymousTextPart++}`)\n if (delta) textParts.set(partId, `${textParts.get(partId) ?? ''}${delta}`)\n else if (typeof part?.text === 'string') textParts.set(partId, part.text)\n }\n } else if (event.type === 'result') {\n const resultText = typeof event.data?.finalText === 'string' ? event.data.finalText : null\n if (resultText?.trim()) finalText = resultText\n }\n }\n\n return finalText || lastNonPromptTextPart(textParts, dispatchedPromptTexts(message, history))\n}\n\n/**\n * Resolve a sandbox prompt by streaming it and aggregating the turn down to one\n * final string. The shell's profile / model / MCP resolution and severed-stream\n * fail-loud come from `streamSandboxPrompt`; the aggregation is\n * `collectSandboxPromptText`, which products driving their own generator should\n * import directly.\n */\nexport async function runSandboxPrompt(\n shell: SandboxRuntimeConfig,\n box: SandboxInstance,\n message: string | PromptInputPart[],\n options?: StreamSandboxPromptOptions,\n): Promise<string> {\n return collectSandboxPromptText(\n streamSandboxPrompt(shell, box, message, options),\n message,\n options?.history,\n )\n}\n\n// Mirrors the SDK's PermissionLevel union (not re-exported by\n// @tangle-network/sandbox). The product's role-mapping seam must produce one of\n// these; binding the seam's return type to the union makes a wrong mapping a\n// compile error rather than a runtime 400 from the orchestrator.\n/** Define permission levels for sandbox access and control */\nexport type SandboxPermissionLevel = 'owner' | 'admin' | 'developer' | 'viewer'\n\n/** Map workspace roles to corresponding sandbox permission levels */\nexport interface MemberSyncSeam {\n roleToSandboxRole: (workspaceRole: string) => SandboxPermissionLevel\n}\n\n/** Resolve adding a user with a specific role to a sandbox and return the operation outcome */\nexport async function syncSandboxMemberAdd(\n box: SandboxInstance,\n seam: MemberSyncSeam,\n userId: string,\n role: string,\n): Promise<Outcome<void>> {\n try {\n await box.permissions.add({ userId, role: seam.roleToSandboxRole(role) })\n return ok(undefined)\n } catch (err) {\n return fail(err)\n }\n}\n\n/** Remove a member from the sandbox while preserving their home directory and handle the outcome */\nexport async function syncSandboxMemberRemove(\n box: SandboxInstance,\n userId: string,\n): Promise<Outcome<void>> {\n try {\n await box.permissions.remove(userId, { preserveHomeDir: true })\n return ok(undefined)\n } catch (err) {\n return fail(err)\n }\n}\n\n/** Synchronize a sandbox member's role by updating permissions based on the provided role mapping */\nexport async function syncSandboxMemberRole(\n box: SandboxInstance,\n seam: MemberSyncSeam,\n userId: string,\n role: string,\n): Promise<Outcome<void>> {\n try {\n await box.permissions.update(userId, { role: seam.roleToSandboxRole(role) })\n return ok(undefined)\n } catch (err) {\n return fail(err)\n }\n}\n\n/** Define methods to create, update, retrieve, and delete secrets asynchronously */\nexport interface SecretStore {\n create: (name: string, value: string) => Promise<void>\n update: (name: string, value: string) => Promise<void>\n get: (name: string) => Promise<string>\n delete: (name: string) => Promise<void>\n}\n\n/** Resolve a SecretStore interface using the provided SandboxRuntimeConfig shell */\nexport function secretStoreFromClient(shell: SandboxRuntimeConfig): SecretStore {\n const client = getClient(shell)\n return {\n create: async (name, value) => {\n await client.secrets.create(name, value)\n },\n update: async (name, value) => {\n await client.secrets.update(name, value)\n },\n get: (name) => client.secrets.get(name),\n delete: async (name) => {\n await client.secrets.delete(name)\n },\n }\n}\n\n/** Resolve storing a secret by creating or updating it in the given SecretStore */\nexport async function storeSecret(\n store: SecretStore,\n name: string,\n value: string,\n): Promise<Outcome<void>> {\n try {\n await store.create(name, value)\n return ok(undefined)\n } catch {\n try {\n await store.update(name, value)\n return ok(undefined)\n } catch (err) {\n return fail(new Error(`Failed to store sandbox secret ${name}`, { cause: err }))\n }\n }\n}\n\n/** Resolve a secret value from the store by its name and return the outcome asynchronously */\nexport async function readSecret(store: SecretStore, name: string): Promise<Outcome<string>> {\n try {\n return ok(await store.get(name))\n } catch (err) {\n return fail(err)\n }\n}\n\n/** Delete a secret by name from the given secret store and return the operation outcome */\nexport async function deleteSecret(store: SecretStore, name: string): Promise<Outcome<void>> {\n try {\n await store.delete(name)\n return ok(undefined)\n } catch (err) {\n return fail(err)\n }\n}\n\n/** Represent a token with its expiration date and associated scope */\nexport interface ScopedTokenResult {\n token: string\n expiresAt: Date\n scope: ScopedTokenScope\n}\n\n/**\n * Mint a scoped token for an already-provisioned box (e.g. to hand a terminal\n * proxy a narrowed credential). Uses the SDK's native `box.mintScopedToken`,\n * which normalizes `expiresAt` to a Date — no hand-rolled wire call.\n */\nexport async function mintSandboxScopedToken(\n box: SandboxInstance,\n options: MintScopedTokenOptions,\n): Promise<Outcome<ScopedTokenResult>> {\n try {\n const token = await box.mintScopedToken(options)\n return ok({ token: token.token, expiresAt: token.expiresAt, scope: token.scope })\n } catch (err) {\n return fail(err)\n }\n}\n\n/** Define options to manage deterministic session resumption and turn idempotency in sandboxed drive turns */\nexport interface DriveSandboxTurnOptions extends StreamSandboxPromptOptions {\n /** Deterministic resume key — required. Every tick for the same logical turn\n * MUST reuse it so a crash + re-drive finds the in-flight session instead of\n * starting a second agent run. */\n sessionId: string\n /** Wall-clock cap in ms from the session's start. A still-running session past\n * the cap is cancelled and reported `failed` — bounds an unattended run (e.g. a\n * turn that stalled on an interactive question nothing will answer). Omit for no cap. */\n wallCapMs?: number\n}\n\n// One settle → poll → dispatch pass over a detached turn. Delegates to the SDK's\n// `box.driveTurn` (@tangle-network/sandbox ≥ 0.10.5) — the turn runs\n// fire-and-detached server-side and ONE invocation returns immediately with where\n// it stands. It never awaits the whole turn in-process, so it does not hold the\n// worker alive for the run's duration (the durability trap the older box.prompt\n// implementation quietly caused).\n//\n// This is the durable path for cron / mission-step / queue callers: re-invoke on\n// your own schedule (Workflow step, DO alarm, queue tick) with the SAME\n// `sessionId` and `turnId`. Dispatch is idempotent on that pair, so a crash +\n// re-drive is a lookup, not a second agent run.\n//\n// The Outcome boundary separates a retryable transport failure from a settled\n// turn: `fail` means the drive call itself threw (network blip — retry the tick);\n// `ok` carries the SDK's discriminated `TurnDriveResult` — inspect `.state`:\n// - `running` → the turn is still executing; re-tick after a delay of your choosing.\n// - `completed` → terminal; `.text` / `.result` hold the payload.\n// - `failed` → terminal and deterministic; re-invoking will not change it (do not retry).\n/** Resolve a sandbox turn by processing a message with given configuration and options */\nexport async function driveSandboxTurn(\n shell: SandboxRuntimeConfig,\n box: SandboxInstance,\n message: string | PromptInputPart[],\n options: DriveSandboxTurnOptions,\n): Promise<Outcome<TurnDriveResult>> {\n const harness = options.harness ?? 'opencode'\n // Resolved (and, for an explicit override/config model, enforced-transportable)\n // BEFORE the try below: a misconfigured model is a deterministic config error,\n // not a retryable transport blip, so it throws here rather than returning\n // `fail(...)` — a driver re-ticking this session would otherwise retry it forever.\n const model = requireTransportableModel(\n resolveModelSelection(shell.provider, {\n model: options.model,\n modelApiKey: options.modelApiKey,\n }),\n 'driveSandboxTurn',\n )\n if (model?.model) assertHarnessModelCompatible(harness, model.model)\n const prompt =\n typeof message === 'string'\n ? flattenHistory(message, options.history)\n : mergeHistoryIntoParts(message, options.history)\n const appToolMcp = options.appToolMcp ?? {}\n const extraMcp = mergeExtraMcp(appToolMcp, options.baseProfileMcp ?? {}, options.extraMcp)\n const profile = attachReasoningEffort(\n shell.profile({ systemPrompt: options.systemPrompt, extraMcp, harness }),\n harness,\n options.effort,\n )\n // Autonomous lane: nobody is watching, so an empty answer from an oversized\n // prompt would be recorded as a completed turn with no output.\n assertProfilePromptWithinBudget(\n profile,\n shell.promptBudget ?? {},\n 'driveSandboxTurn profile systemPrompt',\n SHELL_PROMPT_BUDGET_HINT,\n )\n try {\n const drive = await box.driveTurn(prompt, {\n sessionId: options.sessionId,\n ...(options.turnId ? { turnId: options.turnId } : {}),\n ...(options.wallCapMs !== undefined ? { wallCapMs: options.wallCapMs } : {}),\n ...(options.executionId ? { executionId: options.executionId } : {}),\n ...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),\n ...(options.signal ? { signal: options.signal } : {}),\n // Deliberately NO `interactions` here: detached turns (cron / mission steps) have\n // no consumer to answer a question. Interactive Q&A is streaming-path only.\n backend: { type: harness, profile, ...(model ? { model } : {}) },\n } as Parameters<SandboxInstance['driveTurn']>[1])\n return ok(drive)\n } catch (err) {\n return fail(err)\n }\n}\n\n// Severed-stream classifier. Generic to any router-backed harness: a final step\n// that finished with error/other/unknown is a truncated turn, not a completed one.\nconst SEVERED_FINISH_REASONS = new Set(['error', 'other', 'unknown'])\n\n/** Define transitions marking the start or finish of a sandbox step with associated details */\nexport type SandboxStepTransition =\n | { kind: 'step-start' }\n | { kind: 'step-finish'; reason: string; severed: boolean }\n\nfunction asPlainRecord(v: unknown): Record<string, unknown> | null {\n return v && typeof v === 'object' && !Array.isArray(v) ? (v as Record<string, unknown>) : null\n}\n\n/** Resolve the severed stream event to a corresponding sandbox step transition or null */\nexport function classifySeveredStream(event: unknown): SandboxStepTransition | null {\n const root = asPlainRecord(event)\n if (!root || root.type !== 'message.part.updated') return null\n const body = asPlainRecord(root.properties) ?? asPlainRecord(root.data) ?? root\n const part = asPlainRecord(body.part)\n if (!part) return null\n if (part.type === 'step-start') return { kind: 'step-start' }\n if (part.type !== 'step-finish') return null\n const reason = typeof part.reason === 'string' && part.reason ? part.reason : 'unknown'\n return { kind: 'step-finish', reason, severed: SEVERED_FINISH_REASONS.has(reason) }\n}\n\n/** Determine if an event is a terminal prompt event with type 'result' or 'done */\nexport function isTerminalPromptEvent(event: unknown): boolean {\n const t = asPlainRecord(event)?.type\n return t === 'result' || t === 'done'\n}\n\n// Interactive-question detector. Returns the question text or null. Used by\n// streamSandboxPrompt when disallowQuestions is set.\n/** Resolve the interactive question text from a structured event or return null if none found */\nexport function detectInteractiveQuestion(event: unknown): string | null {\n const root = asPlainRecord(event)\n if (!root) return null\n const type = typeof root.type === 'string' ? root.type : undefined\n const data = asPlainRecord(root.data)\n const props = asPlainRecord(root.properties)\n const body = props ?? data ?? root\n if (type === 'question.asked' || type === 'question') return firstQuestionText(body)\n // Generic structured-interaction event (BackendConfig.interactions kinds surface as\n // `interaction` events with a `kind` discriminator); treat kind:\"question\" as a question.\n if (type === 'interaction' && body.kind === 'question') return firstQuestionText(body)\n const part = asPlainRecord(data?.part) ?? asPlainRecord(body.part)\n const tool =\n (typeof part?.tool === 'string' && part.tool) ||\n (typeof part?.name === 'string' && part.name) ||\n (typeof body.tool === 'string' && body.tool) ||\n undefined\n const isQ =\n type === 'message.part.updated' &&\n (tool === 'question' || asPlainRecord(part)?.type === 'question')\n if (!isQ) return null\n const state = asPlainRecord(asPlainRecord(part)?.state)\n return firstQuestionText(asPlainRecord(state?.input) ?? state ?? part ?? body)\n}\n\nfunction firstQuestionText(value: Record<string, unknown> | null): string {\n const arr = Array.isArray(value?.questions)\n ? value!.questions\n : Array.isArray(asPlainRecord(value?.input)?.questions)\n ? (asPlainRecord(value!.input)!.questions as unknown[])\n : []\n const first = asPlainRecord(arr[0])\n const q =\n (typeof first?.question === 'string' && first.question) ||\n (typeof first?.prompt === 'string' && first.prompt) ||\n undefined\n return q ?? 'interactive question'\n}\n// Workspace sandbox terminal handlers: WebSocket upgrade proxy, connection\n// + runtime-proxy handlers, and scoped terminal-token mint/verify.\nexport * from './terminal-proxy-token'\nexport * from './workspace-terminal'\n// Background box warming on project open: single-flight, non-blocking, and\n// explicit about spend. Imports `ensureWorkspaceSandbox`/`peekWorkspaceSandbox`\n// back out of this module; tsup inlines both files into the one\n// `sandbox/index` chunk, so the cycle exists only in source.\nexport * from './prewarm'\n\nexport * from './prewarm-claim-d1'\n","// Shared Outcome triple for the sandbox modules. Lives in a dependency-free\n// leaf so both index.ts and terminal-proxy-token.ts import it instead of\n// re-declaring the type (index.ts re-exports * from terminal-proxy-token, so\n// the token module cannot import from index without a cycle).\n/** Represent success or failure of an operation with corresponding value or error information */\nexport type Outcome<T> =\n | { succeeded: true; value: T }\n | { succeeded: false; error: Error }\n\nexport const ok = <T>(value: T): Outcome<T> => ({ succeeded: true, value })\nexport const fail = (error: unknown): Outcome<never> => ({\n succeeded: false,\n error: error instanceof Error ? error : new Error(String(error)),\n})\n","import { trimOrNull } from '../runtime/model'\n\n/** Define configuration options for resolving a provider and its model with optional API keys and routing details */\nexport interface ProviderResolutionConfig {\n routerBaseUrl?: string\n apiKey?: string\n providerName?: string\n modelName?: string\n defaultModel?: string\n openaiApiKey?: string\n // Opt-in: a resolvable provider+model WITHOUT an api key still yields model\n // metadata (model/provider/baseUrl, no apiKey) instead of undefined. Keyless\n // metadata makes the sandbox platform mint its OWN per-user router key at\n // create (its requiresRouterKey gate), so turns bill the box's billing owner\n // instead of a product-baked shared key. Requires an explicit providerName —\n // provider inference from key presence cannot fire keyless. Default false:\n // a keyless config resolves to undefined exactly as before.\n allowKeylessModel?: boolean\n}\n\n/** Represent a fully configured model with optional API key and base URL for sandbox platform integration */\nexport interface ResolvedModel {\n model: string\n provider: string\n // Omitted only under `allowKeylessModel` — keyless metadata tells the\n // sandbox platform to mint its own per-user router key for the box.\n apiKey?: string\n baseUrl?: string\n}\n\n/**\n * Why a model failed to resolve into something transportable to the sandbox\n * platform. `no_provider` — a model id exists but no provider name could be\n * derived (no explicit `providerName`, and no key present to infer one from).\n * `no_api_key` — a provider AND model both resolved, but no credential is\n * configured and the caller did not opt into `allowKeylessModel`.\n */\nexport type ModelSelectionError = 'no_provider' | 'no_api_key'\n\n/**\n * Which precedence slot supplied the failed/succeeded model id:\n * `override` — the caller's per-turn `{ model }` argument.\n * `config` — `provider.modelName`.\n * `default` — `provider.defaultModel` (only ever consulted for an\n * openai/openai-compat provider shape).\n */\nexport type ModelSelectionSource = 'override' | 'config' | 'default'\n\n/**\n * A model id was named (by override, config, or default) but is not\n * transportable to the sandbox platform. Carries enough to explain WHY\n * without the caller re-deriving the override/config/default precedence\n * chain itself (that re-derivation is how gtm-agent#665 happened — a caller\n * guessed loudness from the wrong slot and dropped a user-selected model).\n */\nexport type ModelSelectionFailure =\n | { succeeded: false; error: 'no_provider'; model: string; source: ModelSelectionSource }\n | {\n succeeded: false\n error: 'no_api_key'\n model: string\n provider: string\n source: ModelSelectionSource\n }\n\n/**\n * The three-state outcome of resolving a model: `{ succeeded: true, value:\n * undefined }` means NOTHING was requested (the legitimate box-default\n * configuration — not an error); `{ succeeded: true, value: ResolvedModel }`\n * means a fully transportable model resolved; anything else is a\n * {@link ModelSelectionFailure} — a model WAS named but can't be sent.\n */\nexport type ModelSelection =\n | { succeeded: true; value: ResolvedModel | undefined }\n | ModelSelectionFailure\n\n/**\n * Resolve a provider + model configuration into a typed three-state outcome\n * that separates \"nothing requested\" from \"something requested but\n * untransportable\" — the distinction {@link resolveModel} collapses and the\n * one that caused gtm-agent#665 (a validated user-selected model silently\n * dropped, box default substituted, durable row still recording the user's\n * choice).\n *\n * Precedence (identical to the legacy `resolveModel`, byte-for-byte):\n * provider is computed first (`providerName`, else inferred `openai-compat`\n * from a present key, else inferred `openai` from a present\n * `openaiApiKey`, else unresolved); the model id is `override.model` else\n * `config.modelName` else — ONLY when the provider is `openai` or\n * `openai-compat` — `config.defaultModel`; the api key is\n * `override.modelApiKey` else `config.apiKey` else — only for provider\n * `openai` — `config.openaiApiKey`.\n *\n * Two behavioral deltas from the legacy function:\n * 1. Every string field (`routerBaseUrl`, `apiKey`, `providerName`,\n * `modelName`, `defaultModel`, `openaiApiKey`, `override.model`,\n * `override.modelApiKey`) is normalized through {@link trimOrNull} first,\n * so `''` (and whitespace-only strings) are treated as absent instead of\n * poisoning the `??` precedence chain — the issue's second stated defect.\n * This also means a value is trimmed (`' gpt-5 '` resolves to `'gpt-5'`).\n * 2. The outcome is three-state instead of collapsing to `undefined`: no\n * model derivable from any slot → `{ succeeded: true, value: undefined }`\n * (still the legitimate \"let the box pick its own default\" case, NOT an\n * error); a model id resolved but no provider could be derived → a\n * `no_provider` failure; a model + provider resolved but no api key (and\n * `allowKeylessModel` was not set) → a `no_api_key` failure. Both failure\n * arms carry `source` so a caller can apply a loudness policy without\n * re-deriving which precedence slot supplied the model.\n */\nexport function resolveModelSelection(\n config: ProviderResolutionConfig | undefined,\n override?: { model?: string; modelApiKey?: string },\n): ModelSelection {\n const c = config ?? {}\n const explicitBaseUrl = trimOrNull(c.routerBaseUrl)\n const explicitApiKey = trimOrNull(override?.modelApiKey) ?? trimOrNull(c.apiKey)\n const providerName = trimOrNull(c.providerName)\n const openaiApiKey = trimOrNull(c.openaiApiKey)\n const provider =\n providerName ?? (explicitApiKey ? 'openai-compat' : openaiApiKey ? 'openai' : undefined)\n\n const overrideModel = trimOrNull(override?.model)\n const configModel = trimOrNull(c.modelName)\n const defaultModel = trimOrNull(c.defaultModel)\n const modelName =\n overrideModel ??\n configModel ??\n (provider === 'openai' || provider === 'openai-compat' ? defaultModel : null)\n\n if (!modelName) return { succeeded: true, value: undefined }\n\n const source: ModelSelectionSource = overrideModel ? 'override' : configModel ? 'config' : 'default'\n\n if (!provider) return { succeeded: false, error: 'no_provider', model: modelName, source }\n\n const apiKey = explicitApiKey ?? (provider === 'openai' ? openaiApiKey : undefined)\n if (!apiKey && !c.allowKeylessModel) {\n return { succeeded: false, error: 'no_api_key', model: modelName, provider, source }\n }\n\n return {\n succeeded: true,\n value: {\n model: modelName,\n provider,\n ...(apiKey ? { apiKey } : {}),\n ...(explicitBaseUrl ? { baseUrl: explicitBaseUrl } : {}),\n },\n }\n}\n\n/**\n * Resolve and return the appropriate model configuration based on provider\n * settings and optional overrides.\n *\n * Migration note (intentionally NOT tagged `@deprecated`): this is a thin,\n * source-compatible wrapper over {@link resolveModelSelection} that collapses\n * its three-state outcome back down to `ResolvedModel | undefined`, exactly\n * as before. It stays correct for the common case (no explicit model requested, or a\n * fully-transportable one), but it CANNOT distinguish \"nothing was\n * requested\" from \"a named model could not be transported\" — the ambiguity\n * that caused gtm-agent#665. Prefer {@link resolveModelSelection} directly,\n * or {@link requireTransportableModel} for the fail-loud-on-explicit-model\n * policy the internal sandbox callers use. Not tagged `@deprecated`: it\n * remains the correct call for a caller that never sets an explicit\n * override and only wants \"the box's default is fine\" semantics; a hard\n * deprecation is a later-major decision, not this fix's.\n *\n * The only behavior change on this entry point versus before is the\n * empty-string bugfix that comes free through delegation (issue #302's\n * second defect) — every signature and the `undefined` return semantics are\n * unchanged.\n */\nexport function resolveModel(\n config: ProviderResolutionConfig | undefined,\n override?: { model?: string; modelApiKey?: string },\n): ResolvedModel | undefined {\n const selection = resolveModelSelection(config, override)\n return selection.succeeded ? selection.value : undefined\n}\n\n/**\n * Thrown by {@link requireTransportableModel} when a model that was\n * EXPLICITLY requested via a per-turn `{ model }` override cannot be\n * transported to the sandbox platform. The message names the model, which\n * precedence slot supplied it, what's missing, and the fix, and states\n * plainly that the requested model was NOT sent to the box (the failure mode\n * this error exists to make impossible to miss — gtm-agent#665 silently\n * substituted the box default instead of a user-selected model).\n *\n * The error class itself carries no opinion about *when* it should be\n * thrown — it is constructed from any {@link ModelSelectionFailure},\n * regardless of `source`. A caller wanting a stricter policy (e.g. also\n * fail-loud on an untransportable configured `provider.modelName`) can call\n * {@link resolveModelSelection} directly and throw this itself.\n */\nexport class SandboxModelResolutionError extends Error {\n readonly code: ModelSelectionError\n readonly model: string\n readonly provider?: string\n readonly source: ModelSelectionSource\n\n constructor(failure: ModelSelectionFailure, context: string) {\n const missing =\n failure.error === 'no_provider'\n ? 'no provider could be resolved for it'\n : `provider \"${failure.provider}\" resolved but no API key is configured`\n const fix =\n failure.error === 'no_provider'\n ? 'set providerName explicitly (provider cannot be inferred without one)'\n : 'set apiKey (or openaiApiKey when provider is \"openai\"), or pass allowKeylessModel:true to mint a keyless box'\n super(\n `${context}: model \"${failure.model}\" (from ${failure.source}) is not transportable — ` +\n `${missing}. Fix: ${fix}. The requested model was NOT sent to the box.`,\n )\n this.name = 'SandboxModelResolutionError'\n this.code = failure.error\n this.model = failure.model\n if (failure.error === 'no_api_key') this.provider = failure.provider\n this.source = failure.source\n }\n}\n\n/**\n * Shared fail-loud policy for the sandbox platform's three internal model\n * callers (`ensureWorkspaceSandbox`'s `backendModelAtCreate`,\n * `streamSandboxPrompt`, `driveSandboxTurn`): a `ModelSelection` in, a plain\n * `ResolvedModel | undefined` out, so downstream code is unchanged from\n * before this fix.\n *\n * The policy: success delegates straight through. A failure whose `source`\n * is `'override'` means a PER-TURN model was explicitly selected THIS turn\n * (a live, user-driven choice — passed as `{ model }`) and could not be\n * sent; substituting the box default there is exactly the gtm-agent#665\n * defect, so it throws {@link SandboxModelResolutionError} rather than\n * silently falling back.\n *\n * A failure whose `source` is `'config'` or `'default'` means a\n * *configured* `provider.modelName` / `provider.defaultModel` couldn't\n * resolve — nobody made a choice this turn; the value came from board\n * config that may simply describe \"the platform supplies the credential.\"\n * Shipped consumers rely on exactly that: tax-agent ships a shell with\n * `provider: { providerName: 'openai-compat', modelName, routerBaseUrl }`\n * and no `apiKey`/`allowKeylessModel`, with a contract test asserting\n * `ensureWorkspaceSandbox` creation SUCCEEDS with the model silently\n * dropped so the sandbox platform mints its own in-container credential\n * (`apps/web/tests/sandbox-service-contract.test.ts`). A config-loud policy\n * here would break every fresh tax sandbox provisioning and every tax turn.\n * So both `'config'` and `'default'` keep the pre-#302 logged-skip\n * behavior: `console.error` and drop, letting the box use its own default.\n * A product wanting strict enforcement of a configured `provider.modelName`\n * can call {@link resolveModelSelection} directly and apply its own policy.\n */\nexport function requireTransportableModel(\n selection: ModelSelection,\n context: string,\n): ResolvedModel | undefined {\n if (selection.succeeded) return selection.value\n if (selection.source === 'override') {\n throw new SandboxModelResolutionError(selection, context)\n }\n const reason =\n selection.error === 'no_api_key'\n ? `provider \"${selection.provider}\" has no api key`\n : 'no provider resolved'\n if (selection.source === 'config') {\n console.error(\n `[sandbox] ${context}: dropping configured provider.modelName \"${selection.model}\" (${reason}); ` +\n `the box will use its own default model — set allowKeylessModel:true to bake a keyless model, or configure an api key`,\n )\n } else {\n console.error(\n `[sandbox] ${context}: dropping provider.defaultModel \"${selection.model}\" (${reason}); using the box default`,\n )\n }\n return undefined\n}\n","/**\n * Reading arbitrary bytes out of a sandbox over an exec channel that only\n * speaks text.\n *\n * `box.exec` returns stdout as a string, so a binary file has to be encoded to\n * survive the trip: `wc -c` gives the on-disk length, `base64` gives the\n * payload, and the decoded byte count is checked against the stat. That last\n * check is the point of the module — an exec channel that caps or clips its\n * output still reports `exitCode: 0` with a short buffer, which decodes into a\n * perfectly valid but TRUNCATED file. Verifying the length turns that silent\n * corruption into a loud failure at the boundary.\n *\n * Both helpers return typed outcomes; callers must inspect `succeeded` before\n * touching `value`.\n */\n\n/** The `box.exec` surface these helpers use — structural, so a caller can pass\n * the sandbox SDK's `SandboxInstance` directly or a narrower test double. */\nexport interface SandboxExecChannel {\n exec(\n command: string,\n options?: { sessionId?: string },\n ): Promise<{ stdout: string; stderr: string; exitCode: number }>\n}\n\n/** Define options to execute code within a sandbox environment with optional session control */\nexport interface SandboxExecOptions {\n /** Run inside a named session rather than the box's default one. */\n sessionId?: string\n}\n\n/** Resolve the outcome of a sandbox file size check with success status and value or error message */\nexport type SandboxFileSizeOutcome =\n | { succeeded: true; value: number }\n | { succeeded: false; error: string }\n\n/** Represent the outcome of reading sandbox file bytes with success status and corresponding data or error */\nexport type SandboxFileBytesOutcome =\n | { succeeded: true; value: { bytes: Uint8Array; size: number } }\n | { succeeded: false; error: string }\n\n/** Wraps a value in single quotes for `sh`, closing and reopening the quote\n * around each embedded quote (`'` → `'\"'\"'`). Every path these helpers\n * interpolate into a command goes through this — in-box filenames are\n * arbitrary, so spaces, quotes and `$` are ordinary content, not syntax. */\nexport function shellQuote(value: string): string {\n return `'${value.replaceAll(\"'\", `'\"'\"'`)}'`\n}\n\n/** Decodes with `atob` rather than `Buffer.from(s, 'base64')`: Buffer SKIPS\n * characters outside the alphabet, so a corrupted payload would decode to\n * something plausible instead of throwing. */\nfunction base64ToBytes(base64: string): Uint8Array {\n const binary = atob(base64)\n const bytes = new Uint8Array(binary.length)\n for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i)\n return bytes\n}\n\nfunction execInSandbox(box: SandboxExecChannel, command: string, options?: SandboxExecOptions) {\n return options?.sessionId ? box.exec(command, { sessionId: options.sessionId }) : box.exec(command)\n}\n\n/** Stats a sandbox file's byte length via `wc -c`. A caller enforcing a size\n * cap must check this BEFORE {@link readSandboxBinaryBytes}, so an oversize\n * file is rejected without paying for a base64 round trip of it. */\nexport async function statSandboxFileSize(\n box: SandboxExecChannel,\n absolutePath: string,\n options?: SandboxExecOptions,\n): Promise<SandboxFileSizeOutcome> {\n const quotedPath = shellQuote(absolutePath)\n let result\n try {\n result = await execInSandbox(box, `wc -c < ${quotedPath}`, options)\n } catch (err) {\n return { succeeded: false, error: err instanceof Error ? err.message : String(err) }\n }\n if (result.exitCode !== 0) {\n return { succeeded: false, error: result.stderr || 'unknown error' }\n }\n const size = Number.parseInt(result.stdout.trim(), 10)\n if (!Number.isFinite(size)) {\n return { succeeded: false, error: `could not parse file size from \"${result.stdout.trim()}\"` }\n }\n return { succeeded: true, value: size }\n}\n\n/** Reads a sandbox file as base64 and decodes it, verifying the decoded byte\n * length against `expectedSize` (from a prior {@link statSandboxFileSize}). A\n * mismatch is reported, never returned as a short buffer. */\nexport async function readSandboxBinaryBytes(\n box: SandboxExecChannel,\n absolutePath: string,\n expectedSize: number,\n options?: SandboxExecOptions,\n): Promise<SandboxFileBytesOutcome> {\n const quotedPath = shellQuote(absolutePath)\n let result\n try {\n result = await execInSandbox(box, `base64 ${quotedPath}`, options)\n } catch (err) {\n return { succeeded: false, error: err instanceof Error ? err.message : String(err) }\n }\n if (result.exitCode !== 0) {\n return { succeeded: false, error: result.stderr || 'unknown error' }\n }\n // GNU coreutils wraps base64 output at 76 columns; BusyBox may not wrap at\n // all. Stripping all whitespace makes both shapes decode identically.\n const cleaned = result.stdout.replace(/\\s+/g, '')\n let bytes: Uint8Array\n try {\n bytes = base64ToBytes(cleaned)\n } catch (err) {\n return {\n succeeded: false,\n error: `could not decode file contents: ${err instanceof Error ? err.message : String(err)}`,\n }\n }\n if (bytes.byteLength !== expectedSize) {\n return {\n succeeded: false,\n error: `read returned ${bytes.byteLength} bytes, expected ${expectedSize} — output truncated in transit`,\n }\n }\n return { succeeded: true, value: { bytes, size: expectedSize } }\n}\n","import {\n base64UrlDecodeText,\n base64UrlEncodeText,\n constantTimeEqual,\n hmacSha256Base64Url,\n} from '../crypto/web-token'\nimport { ok, fail, type Outcome } from './outcome'\n\n// Terminal-proxy HMAC token. Identity tuple is generic; the secret comes from a\n// closure (fail-loud if absent).\n/** Define identity details for a terminal proxy including user, workspace, and sandbox identifiers */\nexport interface TerminalProxyIdentity {\n userId: string\n workspaceId: string\n sandboxId: string\n}\n\nconst TERMINAL_PROXY_TOKEN_TTL_MS = 15 * 60 * 1000\n\n/** Generate a signed token for TerminalProxyIdentity with an expiration based on TTL milliseconds */\nexport async function mintTerminalProxyToken(\n secret: string,\n identity: TerminalProxyIdentity,\n ttlMs = TERMINAL_PROXY_TOKEN_TTL_MS,\n now: () => number = Date.now,\n): Promise<Outcome<{ token: string; expiresAt: Date }>> {\n if (!secret) return fail(new Error('mintTerminalProxyToken: secret is required'))\n if (!identity.userId || !identity.workspaceId || !identity.sandboxId) {\n return fail(new Error('mintTerminalProxyToken: userId/workspaceId/sandboxId are required'))\n }\n const expiresAt = new Date(now() + ttlMs)\n const payload = { ...identity, exp: Math.floor(expiresAt.getTime() / 1000) }\n const encoded = base64UrlEncodeText(JSON.stringify(payload))\n const sig = await hmacSha256Base64Url(encoded, secret)\n return ok({ token: `${encoded}.${sig}`, expiresAt })\n}\n\n/** Verify the authenticity and validity of a terminal proxy token against expected identity and timestamp */\nexport async function verifyTerminalProxyToken(\n secret: string,\n token: string,\n expected: TerminalProxyIdentity,\n now: () => number = Date.now,\n): Promise<boolean> {\n if (!secret) return false\n const [encoded, sig, extra] = token.split('.')\n if (!encoded || !sig || extra !== undefined) return false\n const expectedSig = await hmacSha256Base64Url(encoded, secret)\n if (!constantTimeEqual(sig, expectedSig)) return false\n let payload: TerminalProxyIdentity & { exp: number }\n try {\n payload = JSON.parse(base64UrlDecodeText(encoded))\n } catch {\n return false\n }\n return (\n payload.userId === expected.userId &&\n payload.workspaceId === expected.workspaceId &&\n payload.sandboxId === expected.sandboxId &&\n Number.isFinite(payload.exp) &&\n payload.exp > Math.floor(now() / 1000)\n )\n}\n","import { base64UrlDecodeText } from '../crypto/web-token'\nimport {\n mintTerminalProxyToken,\n verifyTerminalProxyToken,\n type TerminalProxyIdentity,\n} from './terminal-proxy-token'\n\n/** Define the shape of a workspace sandbox instance including its connection details and status */\nexport interface WorkspaceSandboxInstanceLike {\n id: string\n name?: string\n status?: string\n connection?: {\n runtimeUrl?: string\n sidecarUrl?: string\n authToken?: string\n sidecarToken?: string\n authTokenExpiresAt?: string\n } | null\n}\n\n// Generic name-keyed sandbox lifecycle manager. A structural, substrate-free\n// helper for products that drive their own SDK/box types (distinct from the\n// concrete `ensureWorkspaceSandbox` in ./index, which is bound to the\n// @tangle-network/sandbox client). Kept as a public export — external\n// consumers compose it; removing it would be a breaking change.\n/** Define the context containing workspace and user identifiers for sandbox environment operations */\nexport interface WorkspaceSandboxEnsureContext {\n workspaceId: string\n userId: string\n}\n\n/** Define configuration options for managing and interacting with workspace sandboxes */\nexport interface WorkspaceSandboxManagerOptions<TClient, TBox extends WorkspaceSandboxInstanceLike, TEnsureOptions = void> {\n getClient: (ctx: WorkspaceSandboxEnsureContext) => Promise<TClient> | TClient\n nameForWorkspace: (workspaceId: string, ctx: WorkspaceSandboxEnsureContext) => string\n listSandboxes: (client: TClient, ctx: WorkspaceSandboxEnsureContext) => Promise<TBox[]>\n createSandbox: (args: {\n client: TClient\n ctx: WorkspaceSandboxEnsureContext\n name: string\n options: TEnsureOptions\n listError?: unknown\n }) => Promise<TBox>\n waitForRunning?: (box: TBox, ctx: WorkspaceSandboxEnsureContext) => Promise<void>\n prepareExisting?: (box: TBox, ctx: WorkspaceSandboxEnsureContext, options: TEnsureOptions) => Promise<TBox | void>\n prepareCreated?: (box: TBox, ctx: WorkspaceSandboxEnsureContext, options: TEnsureOptions) => Promise<TBox | void>\n onListError?: (error: unknown, ctx: WorkspaceSandboxEnsureContext) => void\n}\n\n/** Manage workspace sandboxes by ensuring their creation and retrieval for specified users */\nexport interface WorkspaceSandboxManager<TBox extends WorkspaceSandboxInstanceLike, TEnsureOptions = void> {\n ensureWorkspaceSandbox: (\n workspaceId: string,\n userId: string,\n options?: TEnsureOptions,\n ) => Promise<TBox>\n}\n\n/** Create a manager to handle workspace sandbox instances with client and options configuration */\nexport function createWorkspaceSandboxManager<TClient, TBox extends WorkspaceSandboxInstanceLike, TEnsureOptions = void>(\n opts: WorkspaceSandboxManagerOptions<TClient, TBox, TEnsureOptions>,\n): WorkspaceSandboxManager<TBox, TEnsureOptions> {\n return {\n async ensureWorkspaceSandbox(workspaceId, userId, options) {\n if (!workspaceId) throw new Error('workspaceId is required')\n if (!userId) throw new Error('userId is required')\n const ctx = { workspaceId, userId }\n const client = await opts.getClient(ctx)\n const name = opts.nameForWorkspace(workspaceId, ctx)\n let listError: unknown\n let existing: TBox[] = []\n\n try {\n existing = await opts.listSandboxes(client, ctx)\n } catch (err) {\n listError = err\n opts.onListError?.(err, ctx)\n }\n\n const found = existing.find((box) => box.name === name)\n if (found) {\n return (await opts.prepareExisting?.(found, ctx, options as TEnsureOptions)) ?? found\n }\n\n const created = await opts.createSandbox({\n client,\n ctx,\n name,\n options: options as TEnsureOptions,\n listError,\n })\n await opts.waitForRunning?.(created, ctx)\n return (await opts.prepareCreated?.(created, ctx, options as TEnsureOptions)) ?? created\n },\n }\n}\n\n/** Define options for generating a sandbox terminal token including secret and expiration settings */\nexport interface SandboxTerminalTokenOptions {\n secret?: string\n expiresInMs?: number\n now?: () => number\n}\n\n/** Resolve the identity type used for sandbox terminal token subjects */\nexport type SandboxTerminalTokenSubject = TerminalProxyIdentity\n\n/** Provide token and expiration details for a sandbox terminal session */\nexport interface SandboxTerminalTokenResult {\n token: string\n expiresAt: Date\n}\n\nconst DEFAULT_TERMINAL_TOKEN_TTL_MS = 15 * 60 * 1000\nconst BEARER_SUBPROTOCOL_PREFIX = 'bearer.'\n// Legacy `createSandboxTerminalToken` (pre proxy-token extraction) prefixed\n// minted tokens with `sbxt_` and signed the unprefixed payload. New tokens\n// carry no prefix. Strip it on verify so tokens minted by a prior deploy still\n// validate within their (default 15-min) TTL window — avoids a wave of 403s on\n// in-flight browser terminal sessions right after rollout.\nconst LEGACY_TERMINAL_TOKEN_PREFIX = 'sbxt_'\n\n/** Generate a sandbox terminal token for a given subject with specified options */\nexport async function createSandboxTerminalToken(\n subject: SandboxTerminalTokenSubject,\n opts: SandboxTerminalTokenOptions,\n): Promise<SandboxTerminalTokenResult> {\n validateTerminalSubject(subject)\n const secret = opts.secret?.trim()\n if (!secret) throw new Error('terminal token secret is required')\n const now = opts.now ?? Date.now\n const expiresInMs = opts.expiresInMs ?? DEFAULT_TERMINAL_TOKEN_TTL_MS\n if (!Number.isFinite(expiresInMs) || expiresInMs <= 0) throw new Error('expiresInMs must be a positive number')\n const minted = await mintTerminalProxyToken(secret, subject, expiresInMs, now)\n if (!minted.succeeded) throw minted.error\n return minted.value\n}\n\n/** Verify the validity of a sandbox terminal token against the expected identity and options */\nexport async function verifySandboxTerminalToken(\n token: string,\n expected: SandboxTerminalTokenSubject,\n opts: SandboxTerminalTokenOptions,\n): Promise<boolean> {\n validateTerminalSubject(expected)\n const secret = opts.secret?.trim()\n const now = opts.now ?? Date.now\n const normalized = token.startsWith(LEGACY_TERMINAL_TOKEN_PREFIX)\n ? token.slice(LEGACY_TERMINAL_TOKEN_PREFIX.length)\n : token\n return verifyTerminalProxyToken(secret ?? '', normalized, expected, now)\n}\n\n/** Represent an authenticated user within a sandbox environment with a unique identifier */\nexport interface AuthenticatedSandboxUser {\n id: string\n}\n\n/** Define options to handle workspace sandbox connections with user authentication and access control */\nexport interface WorkspaceSandboxConnectionHandlerOptions<TBox extends WorkspaceSandboxInstanceLike> {\n requireUser: (request: Request) => Promise<AuthenticatedSandboxUser>\n requireWorkspaceAccess: (args: { request: Request; userId: string; workspaceId: string }) => Promise<void>\n ensureWorkspaceSandbox: (workspaceId: string, userId: string) => Promise<TBox>\n tokenSecret: string | (() => string | undefined)\n tokenExpiresInMs?: number\n proxyRuntimeUrl?: (args: { request: Request; workspaceId: string; sandboxId: string; box: TBox }) => string\n exposeDirectSidecar?: boolean\n}\n\n/** Define arguments required to establish a workspace sandbox connection */\nexport interface WorkspaceSandboxConnectionArgs {\n request: Request\n params: {\n workspaceId?: string\n }\n}\n\n/** Create a handler to resolve workspace sandbox connections with user and access validation */\nexport function createWorkspaceSandboxConnectionHandler<TBox extends WorkspaceSandboxInstanceLike>(\n opts: WorkspaceSandboxConnectionHandlerOptions<TBox>,\n) {\n return async function handleWorkspaceSandboxConnection({ request, params }: WorkspaceSandboxConnectionArgs): Promise<Response> {\n const user = await opts.requireUser(request)\n const workspaceId = params.workspaceId\n if (!workspaceId) return Response.json({ error: 'workspaceId is required' }, { status: 400 })\n await opts.requireWorkspaceAccess({ request, userId: user.id, workspaceId })\n\n let box: TBox\n try {\n box = await opts.ensureWorkspaceSandbox(workspaceId, user.id)\n } catch (err) {\n return Response.json(\n { error: err instanceof Error ? err.message : 'Failed to provision workspace sandbox' },\n { status: 500 },\n )\n }\n\n const directSidecarUrl = box.connection?.sidecarUrl ?? box.connection?.runtimeUrl\n const directSidecarToken = box.connection?.authToken ?? box.connection?.sidecarToken\n const directSidecarExpiresAt = box.connection?.authTokenExpiresAt\n if (opts.exposeDirectSidecar && directSidecarUrl && directSidecarToken && directSidecarExpiresAt) {\n return Response.json({\n runtimeUrl: directSidecarUrl,\n sidecarUrl: directSidecarUrl,\n token: directSidecarToken,\n expiresAt: directSidecarExpiresAt,\n status: box.status,\n sandboxId: box.id,\n })\n }\n\n if (!directSidecarUrl) {\n return Response.json(\n {\n error: 'Workspace sandbox runtime not ready. The sandbox is still initializing -- retry in a few seconds.',\n status: box.status,\n },\n { status: 503 },\n )\n }\n\n const secret = typeof opts.tokenSecret === 'function' ? opts.tokenSecret() : opts.tokenSecret\n let scoped: SandboxTerminalTokenResult\n try {\n scoped = await createSandboxTerminalToken(\n { userId: user.id, workspaceId, sandboxId: box.id },\n { secret, expiresInMs: opts.tokenExpiresInMs },\n )\n } catch (err) {\n return Response.json(\n { error: err instanceof Error ? err.message : 'Failed to mint sandbox token' },\n { status: 503 },\n )\n }\n\n const runtimeUrl = opts.proxyRuntimeUrl\n ? opts.proxyRuntimeUrl({ request, workspaceId, sandboxId: box.id, box })\n : `/api/workspaces/${encodeURIComponent(workspaceId)}/sandbox/runtime/${encodeURIComponent(box.id)}`\n\n return Response.json({\n runtimeUrl,\n sidecarUrl: runtimeUrl,\n token: scoped.token,\n expiresAt: scoped.expiresAt.toISOString(),\n status: box.status,\n sandboxId: box.id,\n })\n }\n}\n\n/** Define credentials required to access the sandbox API environment */\nexport interface SandboxApiCredentials {\n baseUrl: string\n apiKey: string\n}\n\n/**\n * Build the sandbox API's sidecar-proxy base for a box:\n * `{baseUrl}/v1/sidecar-proxy/{sandboxId}`.\n *\n * This is the ONLY upstream that serves the interactive terminal. Measured on\n * production (`sandbox.tangle.tools`, one box, `ws` client, same credential in\n * every arm):\n *\n * | upstream base | result |\n * |----------------------------------------|---------------------------------|\n * | `/v1/sidecar-proxy/{id}` | 101 -> `ready` 2551ms -> shell |\n * | `/v1/sandboxes/{id}/runtime/` | HTTP 500 |\n * | `connection.runtimeUrl` (the box host) | 101 then close 1000, 0 bytes |\n *\n * The box's own `connection.runtimeUrl` (`https://sandbox-*.tangle.sh`) accepts\n * the upgrade — its Caddy front end upgrades every path, including ones that do\n * not exist — and then hangs up without a PTY. A 101 from that host therefore\n * proves nothing; only a `ready` control frame does. Two products shipped a\n * terminal against it and rendered a permanent spinner.\n *\n * Exported so no product writes the path literal a fourth time.\n */\nexport function sandboxSidecarProxyUrl(baseUrl: string, sandboxId: string): string {\n if (!baseUrl) throw new Error('baseUrl is required')\n if (!sandboxId) throw new Error('sandboxId is required')\n return new URL(`/v1/sidecar-proxy/${encodeURIComponent(sandboxId)}`, baseUrl).toString().replace(/\\/+$/, '')\n}\n\n/** Define a connection configuration for sandbox runtime including URL and optional server-side auth token */\nexport interface SandboxRuntimeConnection {\n runtimeUrl: string\n /** Server-side sidecar bearer. Must authorize terminal routes; never expose it to browser code. */\n authToken?: string\n}\n\n/** Define options for handling workspace sandbox runtime proxy including user, access, credentials, and connection retrieval */\nexport interface WorkspaceSandboxRuntimeProxyHandlerOptions {\n requireUser: (request: Request) => Promise<AuthenticatedSandboxUser>\n requireWorkspaceAccess: (args: { request: Request; userId: string; workspaceId: string; sandboxId: string }) => Promise<void>\n getSandboxApiCredentials: (args: { request: Request; userId: string; workspaceId: string; sandboxId: string }) => Promise<SandboxApiCredentials>\n getSandboxRuntimeConnection?: (args: { request: Request; userId: string; workspaceId: string; sandboxId: string }) => Promise<SandboxRuntimeConnection | null | undefined>\n tokenSecret: string | (() => string | undefined)\n fetch?: typeof fetch\n forwardHeaders?: string[]\n}\n\n/** Define arguments for proxying runtime requests within a workspace sandbox environment */\nexport interface WorkspaceSandboxRuntimeProxyArgs {\n request: Request\n params: {\n workspaceId?: string\n sandboxId?: string\n '*'?: string\n }\n}\n\n/** Create a proxy handler to resolve sandbox runtime requests with user and workspace access validation */\nexport function createWorkspaceSandboxRuntimeProxyHandler(opts: WorkspaceSandboxRuntimeProxyHandlerOptions) {\n return async function handleWorkspaceSandboxRuntimeProxy({ request, params }: WorkspaceSandboxRuntimeProxyArgs): Promise<Response> {\n const user = await opts.requireUser(request)\n const workspaceId = params.workspaceId\n const sandboxId = params.sandboxId\n const runtimePath = params['*']\n if (!workspaceId || !sandboxId || !runtimePath) {\n return Response.json({ error: 'workspaceId, sandboxId, and runtime path are required' }, { status: 400 })\n }\n const encodedRuntimePath = encodeSandboxRuntimePath(runtimePath)\n if (!encodedRuntimePath) return Response.json({ error: 'Invalid sandbox runtime path' }, { status: 400 })\n\n await opts.requireWorkspaceAccess({ request, userId: user.id, workspaceId, sandboxId })\n\n const token = terminalTokenFromRequest(request.headers)\n const secret = typeof opts.tokenSecret === 'function' ? opts.tokenSecret() : opts.tokenSecret\n if (!token || !(await verifySandboxTerminalToken(token, { userId: user.id, workspaceId, sandboxId }, { secret }))) {\n return Response.json({ error: 'Invalid terminal token' }, { status: 403 })\n }\n\n const requestUrl = new URL(request.url)\n const runtimeConnection = await opts.getSandboxRuntimeConnection?.({ request, userId: user.id, workspaceId, sandboxId })\n const directRuntimeConnection = runtimeConnection?.runtimeUrl && runtimeConnection.authToken ? runtimeConnection : null\n const credentials = directRuntimeConnection ? null : await opts.getSandboxApiCredentials({ request, userId: user.id, workspaceId, sandboxId })\n const upstreamUrl = directRuntimeConnection\n ? new URL(encodedRuntimePath, `${directRuntimeConnection.runtimeUrl.replace(/\\/+$/, '')}/`)\n : new URL(encodedRuntimePath, `${sandboxSidecarProxyUrl(credentials!.baseUrl, sandboxId)}/`)\n upstreamUrl.search = requestUrl.search\n\n const headers = buildSandboxRuntimeProxyHeaders(\n request.headers,\n directRuntimeConnection?.authToken ?? credentials!.apiKey,\n opts.forwardHeaders,\n )\n const init: RequestInit & { duplex?: 'half' } = {\n method: request.method,\n headers,\n redirect: 'manual',\n }\n if (request.method !== 'GET' && request.method !== 'HEAD' && request.body) {\n init.body = request.body\n init.duplex = 'half'\n }\n\n const fetchImpl = opts.fetch ?? fetch\n const response = await fetchImpl(upstreamUrl, init)\n const responseHeaders = new Headers(response.headers)\n responseHeaders.delete('set-cookie')\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers: responseHeaders,\n })\n }\n}\n\n// ---------------------------------------------------------------------------\n// Terminal WebSocket upgrade\n//\n// The interactive terminal is WebSocket-only on the current sidecar (the REST\n// `POST /terminals` create route was removed in the websocket-first migration).\n// `createWorkspaceSandboxRuntimeProxyHandler` runs inside a React Router\n// loader/action, which can only return a normal Response — never a 101 — so it\n// cannot perform the upgrade. The upgrade must be intercepted at the Worker\n// fetch entry (server.ts) BEFORE React Router, mirroring the session-stream WS\n// interceptor. This handler does exactly that: it auth-gates the upgrade (the\n// scoped terminal token rides in the `bearer.` subprotocol because browsers\n// can't set Authorization on a WS handshake) and forwards it to the sandbox API\n// runtime proxy with the server-to-server credential. Returning the upstream\n// 101 passes the live socket straight through to the browser — the same idiom\n// the sandbox API uses to reach the orchestrator.\n//\n// NOTE: this only runs under a WebSocket-capable runtime (Cloudflare Workers /\n// `wrangler`). `react-router dev` (Vite) never invokes the Worker fetch entry,\n// so the terminal WS is exercised under `wrangler dev` / production.\n// ---------------------------------------------------------------------------\n\nconst SANDBOX_TERMINAL_WS_PATHNAME =\n /^\\/api\\/workspaces\\/([^/]+)\\/sandbox\\/runtime\\/([^/]+)\\/(terminals\\/[^/]+\\/ws)$/\n\n/** Define the structure for matching a sandbox terminal WebSocket with workspace and path details */\nexport interface SandboxTerminalWsMatch {\n workspaceId: string\n sandboxId: string\n subPath: string\n}\n\n/**\n * Parse a same-origin terminal-WS pathname into its parts, or `null` when the\n * path is not a sandbox terminal WebSocket. Matches the default `runtimeUrl`\n * convention emitted by {@link createWorkspaceSandboxConnectionHandler}\n * (`/api/workspaces/:workspaceId/sandbox/runtime/:sandboxId`) with a canonical\n * `terminals/:id/ws` sub-path. `subPath` is left URL-encoded for re-use in the\n * upstream URL; the ids are decoded for auth checks.\n */\nexport function matchSandboxTerminalWsPath(pathname: string): SandboxTerminalWsMatch | null {\n const m = SANDBOX_TERMINAL_WS_PATHNAME.exec(pathname)\n if (!m) return null\n const [, workspaceId, sandboxId, subPath] = m\n if (!workspaceId || !sandboxId || !subPath) return null\n const decodedWorkspaceId = safeDecodeURIComponent(workspaceId)\n const decodedSandboxId = safeDecodeURIComponent(sandboxId)\n if (!decodedWorkspaceId || !decodedSandboxId) return null\n return { workspaceId: decodedWorkspaceId, sandboxId: decodedSandboxId, subPath }\n}\n\n/** True when `request` is a WebSocket upgrade for a sandbox terminal path. */\nexport function isSandboxTerminalWsUpgrade(request: Request): boolean {\n if (request.headers.get('Upgrade')?.toLowerCase() !== 'websocket') return false\n try {\n return matchSandboxTerminalWsPath(new URL(request.url).pathname) !== null\n } catch {\n return false\n }\n}\n\n/** Define options to handle user authentication, workspace access, and sandbox API credential retrieval */\nexport interface WorkspaceSandboxTerminalUpgradeHandlerOptions {\n requireUser: (request: Request) => Promise<AuthenticatedSandboxUser>\n requireWorkspaceAccess: (args: { request: Request; userId: string; workspaceId: string; sandboxId: string }) => Promise<void>\n getSandboxApiCredentials: (args: { request: Request; userId: string; workspaceId: string; sandboxId: string }) => Promise<SandboxApiCredentials>\n getSandboxRuntimeConnection?: (args: { request: Request; userId: string; workspaceId: string; sandboxId: string }) => Promise<SandboxRuntimeConnection | null | undefined>\n tokenSecret: string | (() => string | undefined)\n fetch?: typeof fetch\n}\n\n/**\n * Build a Worker-entry handler that proxies a sandbox terminal WebSocket\n * upgrade to the sandbox API runtime proxy. Returns `null` when the request is\n * not a terminal WS upgrade, so the caller can fall through to its normal\n * request handler:\n *\n * ```ts\n * const handled = await handleSandboxTerminalUpgrade(request)\n * if (handled) return handled\n * ```\n */\nexport function createWorkspaceSandboxTerminalUpgradeHandler(opts: WorkspaceSandboxTerminalUpgradeHandlerOptions) {\n return async function handleWorkspaceSandboxTerminalUpgrade(request: Request): Promise<Response | null> {\n if (request.headers.get('Upgrade')?.toLowerCase() !== 'websocket') return null\n let url: URL\n try {\n url = new URL(request.url)\n } catch {\n return null\n }\n const match = matchSandboxTerminalWsPath(url.pathname)\n if (!match) return null\n const { workspaceId, sandboxId, subPath } = match\n\n let user: AuthenticatedSandboxUser\n try {\n user = await opts.requireUser(request)\n } catch {\n return new Response('Unauthorized', { status: 401 })\n }\n try {\n await opts.requireWorkspaceAccess({ request, userId: user.id, workspaceId, sandboxId })\n } catch {\n return new Response('Forbidden', { status: 403 })\n }\n\n const token = terminalTokenFromRequest(request.headers)\n const secret = typeof opts.tokenSecret === 'function' ? opts.tokenSecret() : opts.tokenSecret\n if (!token || !(await verifySandboxTerminalToken(token, { userId: user.id, workspaceId, sandboxId }, { secret }))) {\n return new Response('Invalid terminal token', { status: 403 })\n }\n\n const runtimeConnection = await opts.getSandboxRuntimeConnection?.({ request, userId: user.id, workspaceId, sandboxId })\n const directRuntimeConnection = runtimeConnection?.runtimeUrl && runtimeConnection.authToken ? runtimeConnection : null\n const credentials = directRuntimeConnection ? null : await opts.getSandboxApiCredentials({ request, userId: user.id, workspaceId, sandboxId })\n const upstreamUrl = directRuntimeConnection\n ? new URL(subPath, `${directRuntimeConnection.runtimeUrl.replace(/\\/+$/, '')}/`)\n : new URL(subPath, `${sandboxSidecarProxyUrl(credentials!.baseUrl, sandboxId)}/`)\n upstreamUrl.search = url.search\n\n // Forward the upgrade verbatim — keep the Upgrade/Connection + Sec-WebSocket-*\n // headers the handshake needs, but strip the browser-only bearer subprotocol\n // and send the server-to-server sandbox credential only as Authorization.\n // Returning the upstream 101 passes the live socket straight through to the browser.\n const upstreamHeaders = new Headers(request.headers)\n const upstreamBearer = directRuntimeConnection?.authToken ?? credentials!.apiKey\n upstreamHeaders.set('Authorization', `Bearer ${upstreamBearer}`)\n upstreamHeaders.delete('host')\n const browserProtocol = selectedBearerSubprotocol(request.headers.get('Sec-WebSocket-Protocol'))\n stripBearerSubprotocol(upstreamHeaders)\n const fetchImpl = opts.fetch ?? fetch\n const upstream = await fetchImpl(upstreamUrl.toString(), { method: request.method, headers: upstreamHeaders })\n\n const echo = terminalUpgradeSubprotocolEcho(upstream, browserProtocol)\n if (!echo) return upstream\n // Only reachable on a WebSocket-capable runtime: Cloudflare Workers is the\n // one place a 101 Response can be constructed, and only while carrying the\n // live socket. Dropping `webSocket` here would hand the browser a dead 101 —\n // and on Node the construction throws outright, which is exactly the\n // \"status codes in the range 200 to 599\" 500 the old upstream returned.\n return new Response(null, {\n status: echo.status,\n statusText: echo.statusText,\n headers: echo.headers,\n webSocket: (upstream as Response & { webSocket?: WebSocket | null }).webSocket ?? null,\n } as ResponseInit)\n }\n}\n\n/** A response-like shape carrying just what the subprotocol echo decision reads. */\nexport interface TerminalUpgradeResponseLike {\n status: number\n statusText?: string\n headers: Headers\n}\n\n/**\n * Decide whether a terminal upgrade's 101 needs the browser's own subprotocol\n * echoed back onto it, and return the headers to answer with. `null` means\n * \"pass the upstream response through untouched\".\n *\n * Why this exists: the browser's terminal credential rides in a\n * `bearer.<base64url>` WebSocket subprotocol, because a browser cannot set\n * `Authorization` on a WS handshake. That subprotocol is a browser-to-Worker\n * credential, so it is stripped before the upstream hop — and the upstream then\n * answers the 101 selecting nothing. A browser MUST fail the connection when a\n * 101 selects no subprotocol after it offered one (RFC 6455 s4.1), so the socket\n * dies on open and the terminal renders a spinner forever.\n *\n * Kept as a pure function because a 101 `Response` cannot be constructed off\n * Workers, so this is the only part of the decision a test can drive directly.\n */\nexport function terminalUpgradeSubprotocolEcho(\n upstream: TerminalUpgradeResponseLike,\n browserProtocol: string | null,\n): { status: number; statusText: string; headers: Headers } | null {\n if (upstream.status !== 101 || !browserProtocol) return null\n // The upstream's own selection is authoritative; overwriting it would tell the\n // browser a protocol was agreed that the server never agreed to.\n if (upstream.headers.has('Sec-WebSocket-Protocol')) return null\n const headers = new Headers(upstream.headers)\n headers.set('Sec-WebSocket-Protocol', browserProtocol)\n return { status: upstream.status, statusText: upstream.statusText ?? '', headers }\n}\n\n/**\n * The exact `bearer.*` subprotocol string the browser offered, so it can be\n * echoed verbatim on the 101. Returns null when the browser offered none.\n *\n * Takes the raw `Sec-WebSocket-Protocol` value rather than the `Headers`, to\n * match its siblings `bearerSubprotocolToken` and `stripBearerSubprotocol` —\n * one shape for the whole family, and the caller reads the header once.\n */\nexport function selectedBearerSubprotocol(value: string | null): string | null {\n if (!value) return null\n for (const part of value.split(',')) {\n const protocol = part.trim()\n if (protocol.toLowerCase().startsWith(BEARER_SUBPROTOCOL_PREFIX)) return protocol\n }\n return null\n}\n\nconst DEFAULT_RUNTIME_PROXY_HEADERS = ['accept', 'content-type', 'last-event-id', 'x-session-id']\n\n/** Build proxy headers for sandbox runtime including authorization and forwarded headers */\nexport function buildSandboxRuntimeProxyHeaders(source: Headers, sandboxApiKey: string, forwardHeaders = DEFAULT_RUNTIME_PROXY_HEADERS): Headers {\n const headers = new Headers()\n headers.set('Authorization', `Bearer ${sandboxApiKey}`)\n for (const name of forwardHeaders) {\n const value = source.get(name)\n if (value) headers.set(name, value)\n }\n return headers\n}\n\n/** Encode a runtime path by URI-encoding each valid segment and returning null for invalid segments */\nexport function encodeSandboxRuntimePath(runtimePath: string): string | null {\n const segments = runtimePath.split('/')\n if (segments.some((segment) => !segment || segment === '.' || segment === '..')) return null\n return segments.map((segment) => encodeURIComponent(segment)).join('/')\n}\n\n/** Extract the token from a bearer authorization string or return null if invalid or missing */\nexport function bearerToken(value: string | null): string | null {\n if (!value) return null\n const trimmed = value.trim()\n if (!trimmed) return null\n if (trimmed.toLowerCase() === 'bearer') return null\n if (trimmed.toLowerCase().startsWith('bearer ')) {\n const token = trimmed.slice('bearer '.length).trim()\n return token || null\n }\n return trimmed\n}\n\n/** Resolve and decode a bearer token from a comma-separated subprotocol string or return null */\nexport function bearerSubprotocolToken(value: string | null): string | null {\n if (!value) return null\n for (const part of value.split(',')) {\n const protocol = part.trim()\n if (!protocol.toLowerCase().startsWith(BEARER_SUBPROTOCOL_PREFIX)) continue\n const encoded = protocol.slice(BEARER_SUBPROTOCOL_PREFIX.length)\n if (!encoded) return null\n try {\n const token = base64UrlDecodeText(encoded).trim()\n return token || null\n } catch {\n return null\n }\n }\n return null\n}\n\n/** Resolve the terminal token from request headers using Authorization or Sec-WebSocket-Protocol fields */\nexport function terminalTokenFromRequest(headers: Headers): string | null {\n return bearerToken(headers.get('Authorization')) ?? bearerSubprotocolToken(headers.get('Sec-WebSocket-Protocol'))\n}\n\nfunction safeDecodeURIComponent(value: string): string | null {\n try {\n return decodeURIComponent(value)\n } catch {\n return null\n }\n}\n\nfunction stripBearerSubprotocol(headers: Headers): void {\n const value = headers.get('Sec-WebSocket-Protocol')\n if (!value) return\n const protocols = value\n .split(',')\n .map((part) => part.trim())\n .filter((part) => part && !part.toLowerCase().startsWith(BEARER_SUBPROTOCOL_PREFIX))\n if (protocols.length) {\n headers.set('Sec-WebSocket-Protocol', protocols.join(', '))\n } else {\n headers.delete('Sec-WebSocket-Protocol')\n }\n}\n\nfunction validateTerminalSubject(subject: SandboxTerminalTokenSubject): void {\n if (!subject.userId) throw new Error('userId is required')\n if (!subject.workspaceId) throw new Error('workspaceId is required')\n if (!subject.sandboxId) throw new Error('sandboxId is required')\n}\n","/**\n * `createSandboxPrewarmer` — \"this user just opened this project; start warming\n * their box\" as a shell primitive, so every agent-app product gets the same\n * answer instead of forking one.\n *\n * WHY THIS IS SHELL, NOT ENGINE. The engine rule asks whether the capability\n * makes sense without a specific app's side channel. \"Warm a box\" does — but\n * `@tangle-network/sandbox` has no notion of a WORKSPACE. It keys boxes by an\n * opaque sandbox id; the workspace→box mapping, the harness match, and the\n * profile materialisation all live in `ensureWorkspaceSandbox` here. A\n * prewarmer is that mapping plus a scheduling policy, so it belongs beside it.\n * It is deliberately NOT a new subpath: it composes `peekWorkspaceSandbox` and\n * `ensureWorkspaceSandbox` directly and needs exactly the peers `/sandbox`\n * already needs, so a separate entry would add a second place to look for \"how\n * do I get a box\" and buy no peer isolation (the reason `/work-product-react`\n * is split out).\n *\n * WHAT IT IS NOT. It does not make cold starts fast — they already are.\n * Measured on the real platform (staging-sandbox, n=5, 2026-07-28): a box goes\n * from nothing to terminal-ready in 2.34–3.19 s (median 2.73 s), and an\n * already-running box answers in 1.16–2.29 s (median 1.35 s). Prewarming buys\n * ~1.4 s. The reason it matters is not latency: it is that a product which\n * only ever provisions lazily, on a path whose guard never passes, never\n * provisions AT ALL — and the UI then shows a spinner over a box that does not\n * exist and is not being created. That is the failure this primitive removes.\n *\n * ── COST POSTURE (read before adopting) ────────────────────────────────────\n * A warmed box is a REAL charge. It bills from creation until the platform's\n * idle timeout reclaims it — `SandboxRuntimeConfig`'s create-time\n * `idleTimeoutSeconds`, not anything this module sets. A product warming on\n * every project open pays that timeout for every user who opens and bounces.\n * With a 3600 s idle timeout against a ~131 s mean session life, a bounce\n * costs an hour of box time to save ~1.4 s. THAT TRADE IS USUALLY WRONG.\n *\n * So the levers are explicit and the defaults are the cheap ones:\n * - `mode: 'resume-only'` (DEFAULT) never creates a box that does not exist.\n * It only revives one the user already has, so the spend is bounded by\n * boxes the user already caused. This is the safe fleet default.\n * - `mode: 'create-or-resume'` is the owner-requested behaviour — warm on\n * open even for a first-time user. Opt in per product, and lower the\n * shell's `idleTimeoutSeconds` when you do.\n * - `shouldPrewarm(scope)` is the product's own policy hook (paid tier only,\n * returning user only, has-documents only …). Returning false costs nothing.\n * - `failureCooldownMs` stops a hard-failing workspace from retry-storming;\n * every retry is another create attempt, which is more spend.\n * Warm with the SAME harness the next turn will use. `ensureWorkspaceSandbox`\n * DELETES and recreates a name-matched box whose harness differs, so warming\n * `opencode` and then turning `claude-code` pays for two boxes and is slower\n * than not warming at all. The prewarm key includes the harness so the two are\n * never deduped into one.\n *\n * ── SINGLE-FLIGHT (measured, not assumed) ──────────────────────────────────\n * The sandbox platform does NOT dedupe by box name. Two concurrent\n * `POST /v1/sandboxes` with an identical name both returned HTTP 201 and left\n * two running boxes (verified against staging-sandbox, 2026-07-28). So two\n * tabs, or two isolates, racing a warm genuinely leak a box — a prewarm that\n * races is worse than no prewarm. Hence two layers:\n * 1. an in-process map, which is free and catches same-isolate races\n * (double-mount, two requests on one isolate);\n * 2. a `claim` store the product supplies, which is the only thing that can\n * make this correct ACROSS isolates — the usual deployment target here is\n * Cloudflare Workers, where \"same isolate\" guarantees nothing.\n * `claim` is REQUIRED, with `'single-isolate-only'` as the explicit opt-out,\n * so nobody gets the unsafe behaviour by forgetting a field. Say it out loud\n * or supply a store.\n *\n * ── FAILURE IS LOUD, NEVER FATAL ───────────────────────────────────────────\n * A failed warm degrades to exactly today's lazy path: the next real request\n * calls `ensureWorkspaceSandbox` itself. It never throws into the caller's\n * render path — `completion` RESOLVES with `{ ok: false }` rather than\n * rejecting, because an unhandled rejection handed to `waitUntil` can fail the\n * request it rode in on. But it is never silent: every failure fires\n * `onEvent({ type: 'failed' })` and is readable afterwards through\n * `readiness()` as `{ status: 'failed' }`. The bug class this whole module\n * exists to kill is a soft failure that surfaces as an unusable panel ten\n * minutes later, so a warm that dies must leave a trace a product can render.\n */\n\nimport type { SandboxInstance } from '@tangle-network/sandbox/core'\nimport type { Harness } from '../harness/index'\nimport {\n ensureWorkspaceSandbox,\n peekWorkspaceSandbox,\n type SandboxRuntimeConfig,\n} from './index'\n\n/** The workspace a warm targets. Mirrors `EnsureWorkspaceSandboxOptions`'\n * identity fields — the prewarmer forwards them verbatim so a warmed box is\n * byte-identical to the one the lazy path would have built. */\nexport interface SandboxPrewarmScope {\n workspaceId: string\n userId?: string\n /** Must match the harness the next turn will use — see the cost note above. */\n harness: Harness\n billingOwnerId?: string\n}\n\n/**\n * Cross-isolate claim. `acquire` must be atomic (a D1 conditional insert, a DO,\n * a KV `put` with `onlyIf`) — a read-then-write is exactly the race this exists\n * to close. `ttlSeconds` bounds a claim leaked by an isolate that died\n * mid-warm; without expiry a single crash wedges a workspace forever.\n */\nexport interface PrewarmClaimStore {\n /** True when THIS caller now owns the right to warm `key`. */\n acquire(key: string, ttlSeconds: number): Promise<boolean>\n /** Best-effort release. A throw here is swallowed — the TTL is the backstop. */\n release(key: string): Promise<void>\n /** Optional: lets `readiness()` report `warming` for a warm running in\n * ANOTHER isolate. Without it, `warming` is only visible in the isolate\n * that started it, and every other one reports `absent`. */\n isHeld?(key: string): Promise<boolean>\n}\n\n/** What `prewarm()` decided. Every value except `started` means no box was\n * created and nothing was spent on this call. */\nexport type PrewarmOutcome =\n | 'started'\n | 'already-running'\n | 'already-warming'\n | 'warming-elsewhere'\n | 'declined-by-policy'\n | 'cooling-down'\n | 'absent-and-resume-only'\n\n/** Terminal result of a warm this caller owns. Never a rejection. */\nexport interface PrewarmResult {\n ok: boolean\n boxId?: string\n error?: string\n /** Wall time of the warm itself, for the product's own timing trace. */\n ms: number\n}\n\nexport interface PrewarmDecision {\n outcome: PrewarmOutcome\n /** Present ONLY when `outcome === 'started'`. Hand it to `ctx.waitUntil` so a\n * client disconnect cannot kill the warm. Never rejects. */\n completion?: Promise<PrewarmResult>\n}\n\n/** Readiness for the UI. `ready`/`warming` reuse the vocabulary\n * `createSandboxFileIndexRoute` (`/chat-routes`) and `useFileMentions`\n * (`/web-react`) already speak, so a product renders ONE warming state rather\n * than inventing a second spinner for boxes. */\nexport type SandboxReadiness =\n | { status: 'ready'; boxId: string }\n | { status: 'warming' }\n | { status: 'absent' }\n | { status: 'failed'; error: string; retryAfterMs: number }\n\nexport type PrewarmEvent =\n | { type: 'started'; key: string; workspaceId: string }\n | { type: 'succeeded'; key: string; workspaceId: string; boxId: string; ms: number }\n | { type: 'failed'; key: string; workspaceId: string; error: string; ms: number }\n | { type: 'skipped'; key: string; workspaceId: string; outcome: PrewarmOutcome }\n\nexport interface SandboxPrewarmerOptions {\n /** Cross-isolate single-flight, or the explicit acknowledgement that you are\n * accepting per-isolate dedupe only. No default — see the header. */\n claim: PrewarmClaimStore | 'single-isolate-only'\n /** `'resume-only'` (default) never creates a box that does not exist.\n * `'create-or-resume'` warms from nothing — the expensive one. */\n mode?: 'resume-only' | 'create-or-resume'\n /** Product policy gate. Not called when a box is already running. */\n shouldPrewarm?(scope: SandboxPrewarmScope): boolean | Promise<boolean>\n /** Observability seam. A failed warm MUST be visible somewhere. */\n onEvent?(event: PrewarmEvent): void\n /** Claim lifetime. Default 180 s — comfortably over a cold create. */\n claimTtlSeconds?: number\n /** Suppress re-warming a workspace that just failed. Default 60_000 ms. */\n failureCooldownMs?: number\n /** Clock seam for tests. */\n now?(): number\n}\n\nexport interface SandboxPrewarmer {\n /**\n * Non-blocking warm. The returned promise settles as soon as the DECISION is\n * known (at most one `list()` against the platform, plus a claim `acquire`);\n * the provisioning itself rides on `completion`. On a render path either\n * ignore the returned promise or run the whole call inside `waitUntil` — do\n * not await `completion` before responding.\n */\n prewarm(scope: SandboxPrewarmScope): Promise<PrewarmDecision>\n /** Zero-provisioning status read for a UI. Never creates or resumes. */\n readiness(scope: SandboxPrewarmScope): Promise<SandboxReadiness>\n /** Clear a recorded failure so the next `prewarm` retries immediately. */\n clearFailure(scope: SandboxPrewarmScope): void\n}\n\n/** Identity of a warm. Harness is part of it because `ensureWorkspaceSandbox`\n * destroys and rebuilds a box whose harness does not match. */\nfunction prewarmKey(scope: SandboxPrewarmScope): string {\n return `${scope.workspaceId}::${scope.harness}`\n}\n\nfunction errText(err: unknown): string {\n return err instanceof Error ? err.message : String(err)\n}\n\nexport function createSandboxPrewarmer(\n shell: SandboxRuntimeConfig,\n options: SandboxPrewarmerOptions,\n): SandboxPrewarmer {\n const mode = options.mode ?? 'resume-only'\n const claimTtlSeconds = options.claimTtlSeconds ?? 180\n const failureCooldownMs = options.failureCooldownMs ?? 60_000\n const now = options.now ?? (() => Date.now())\n const claim = options.claim === 'single-isolate-only' ? null : options.claim\n\n const inFlight = new Map<string, Promise<PrewarmResult>>()\n const failures = new Map<string, { error: string; until: number }>()\n\n const emit = (event: PrewarmEvent): void => {\n try {\n options.onEvent?.(event)\n } catch {\n // An observability seam must never take down the warm it is reporting on.\n }\n }\n\n async function releaseClaim(key: string): Promise<void> {\n if (!claim) return\n try {\n await claim.release(key)\n } catch {\n // Best effort by contract — the TTL is what actually guarantees release.\n }\n }\n\n /**\n * Owns the box work for one warm. Resolves; never rejects.\n *\n * Clears `inFlight` in its own `finally`, BEFORE the returned promise\n * settles, so the invariant a caller can rely on is: once `completion`\n * resolves, the key is free. Deferring the delete to a `.finally()` chained\n * outside would leave it queued one microtask later, and a caller that\n * awaits `completion` and immediately re-warms would get a stale\n * `already-warming` instead of a real decision.\n */\n async function runWarm(\n key: string,\n scope: SandboxPrewarmScope,\n holdsClaim: boolean,\n ): Promise<PrewarmResult> {\n const startedAt = now()\n emit({ type: 'started', key, workspaceId: scope.workspaceId })\n try {\n const box: SandboxInstance = await ensureWorkspaceSandbox(shell, {\n workspaceId: scope.workspaceId,\n userId: scope.userId,\n harness: scope.harness,\n billingOwnerId: scope.billingOwnerId,\n })\n const ms = now() - startedAt\n failures.delete(key)\n emit({ type: 'succeeded', key, workspaceId: scope.workspaceId, boxId: box.id, ms })\n return { ok: true, boxId: box.id, ms }\n } catch (err) {\n const ms = now() - startedAt\n const error = errText(err)\n // Recorded, not thrown: the next real request still takes the lazy path,\n // and `readiness()` can now tell the UI the warm failed instead of\n // leaving it to spin over a box that is never coming.\n failures.set(key, { error, until: now() + failureCooldownMs })\n emit({ type: 'failed', key, workspaceId: scope.workspaceId, error, ms })\n return { ok: false, error, ms }\n } finally {\n inFlight.delete(key)\n if (holdsClaim) await releaseClaim(key)\n }\n }\n\n return {\n async prewarm(scope: SandboxPrewarmScope): Promise<PrewarmDecision> {\n const key = prewarmKey(scope)\n\n // Synchronous guards first — these run before any `await`, so two calls\n // in one isolate cannot both get past this point.\n if (inFlight.has(key)) {\n emit({ type: 'skipped', key, workspaceId: scope.workspaceId, outcome: 'already-warming' })\n return { outcome: 'already-warming' }\n }\n const failure = failures.get(key)\n if (failure && now() < failure.until) {\n emit({ type: 'skipped', key, workspaceId: scope.workspaceId, outcome: 'cooling-down' })\n return { outcome: 'cooling-down' }\n }\n\n // Reserve the key for THIS isolate before the first await. The reservation\n // is a placeholder the real warm replaces; without it, two concurrent\n // callers would both suspend on `peek` and both proceed.\n let settle: (result: PrewarmResult) => void = () => {}\n const reservation = new Promise<PrewarmResult>((resolve) => {\n settle = resolve\n })\n inFlight.set(key, reservation)\n\n const abandon = (outcome: PrewarmOutcome): PrewarmDecision => {\n inFlight.delete(key)\n settle({ ok: false, error: outcome, ms: 0 })\n emit({ type: 'skipped', key, workspaceId: scope.workspaceId, outcome })\n return { outcome }\n }\n\n try {\n // Cheapest question first: is it already running? Costs one list() and\n // no claim, so the common \"user reopens a warm project\" path spends\n // nothing.\n const peek = await peekWorkspaceSandbox(shell, {\n workspaceId: scope.workspaceId,\n userId: scope.userId,\n })\n if (peek.status === 'running') return abandon('already-running')\n if (peek.status === 'absent' && mode === 'resume-only') {\n return abandon('absent-and-resume-only')\n }\n\n if (options.shouldPrewarm) {\n const allowed = await options.shouldPrewarm(scope)\n if (!allowed) return abandon('declined-by-policy')\n }\n\n let holdsClaim = false\n if (claim) {\n holdsClaim = await claim.acquire(key, claimTtlSeconds)\n if (!holdsClaim) return abandon('warming-elsewhere')\n }\n\n // The key is already reserved above; `runWarm` owns clearing it.\n const warm = runWarm(key, scope, holdsClaim)\n void warm.then(settle)\n return { outcome: 'started', completion: warm }\n } catch (err) {\n // A peek/policy/claim failure is a failed warm like any other: recorded,\n // surfaced, and degraded to the lazy path.\n const error = errText(err)\n inFlight.delete(key)\n failures.set(key, { error, until: now() + failureCooldownMs })\n settle({ ok: false, error, ms: 0 })\n emit({ type: 'failed', key, workspaceId: scope.workspaceId, error, ms: 0 })\n return { outcome: 'cooling-down' }\n }\n },\n\n async readiness(scope: SandboxPrewarmScope): Promise<SandboxReadiness> {\n const key = prewarmKey(scope)\n // A running box outranks a recorded failure: the failure may be stale\n // (a later warm, or the lazy path, may have succeeded since).\n const peek = await peekWorkspaceSandbox(shell, {\n workspaceId: scope.workspaceId,\n userId: scope.userId,\n })\n if (peek.status === 'running') return { status: 'ready', boxId: peek.box.id }\n\n if (inFlight.has(key)) return { status: 'warming' }\n if (claim?.isHeld) {\n try {\n if (await claim.isHeld(key)) return { status: 'warming' }\n } catch {\n // An unreadable claim store must not turn a status read into a 500.\n }\n }\n\n const failure = failures.get(key)\n if (failure && now() < failure.until) {\n return { status: 'failed', error: failure.error, retryAfterMs: failure.until - now() }\n }\n\n // A stopped box is not 'absent' to the platform, but to a user staring at\n // a dead terminal the distinction is meaningless: neither can serve them\n // until something warms it.\n return { status: 'absent' }\n },\n\n clearFailure(scope: SandboxPrewarmScope): void {\n failures.delete(prewarmKey(scope))\n },\n }\n}\n","/**\n * `createD1PrewarmClaimStore` — the cross-isolate half of\n * `createSandboxPrewarmer`'s single-flight, implemented once.\n *\n * WHY THIS SHIPS HERE. `SandboxPrewarmerOptions.claim` is REQUIRED and has no\n * default, which is correct — the unsafe behaviour must be said out loud. But\n * \"required with no implementation\" is how five products end up hand-rolling\n * five subtly different atomic leases, and a lease that is subtly wrong is\n * indistinguishable from one that works until two tabs leak a box. Every\n * agent-app product deploys to Cloudflare Workers with a D1 binding, so the\n * store is the same code in all of them: it is mechanism, not domain, and it\n * belongs beside the prewarmer.\n *\n * The measured stake (staging-sandbox, 2026-07-28): two concurrent\n * `POST /v1/sandboxes` with an identical name BOTH returned HTTP 201 and left\n * two running boxes. The platform does not dedupe by name, so nothing below\n * this line is defensive programming — an unclaimed race genuinely doubles\n * spend.\n *\n * ── ATOMICITY IS THE WHOLE POINT ───────────────────────────────────────────\n * `acquire` is ONE statement. A `SELECT` followed by an `INSERT` is exactly the\n * race this exists to close: both isolates read \"free\", both write, both warm.\n * The upsert's `DO UPDATE ... WHERE expires_at <= now` means an unexpired claim\n * makes the conflict path a no-op, and `RETURNING` then yields no row — so the\n * loser learns it lost from the same statement that would have made it the\n * winner. `RETURNING` is used rather than `meta.changes` because a no-op upsert\n * reporting `changes: 0` is a SQLite detail, whereas \"no row came back\" is the\n * statement telling you directly.\n *\n * ── STRUCTURAL, NOT A DEPENDENCY ───────────────────────────────────────────\n * The binding is taken as the narrow shape actually used (`prepare().bind()`,\n * `.first()`, `.run()`), per the package's structural-over-hard-dep rule. No\n * `@cloudflare/workers-types` import, so `/sandbox` stays importable in a plain\n * node test. A real `D1Database` satisfies it.\n *\n * ── THE TABLE IS THE PRODUCT'S MIGRATION, NOT A LAZY CREATE ────────────────\n * The store never runs DDL. A `CREATE TABLE IF NOT EXISTS` on every project\n * open costs a round trip on the exact path this feature exists to keep fast,\n * and it hides schema drift instead of failing on it. `PREWARM_CLAIM_TABLE_DDL`\n * is exported so a product pastes it into a real migration. If the table is\n * missing, `acquire` throws — and the prewarmer treats a throwing claim as a\n * failed warm: it records the failure, emits `onEvent({type:'failed'})`, and\n * degrades to the lazy path. Fail-closed and loud, never a silent double-warm.\n */\n\n/** The columns and statements this store uses, and nothing else. A real\n * `D1Database` structurally satisfies it. */\nexport interface PrewarmClaimD1Like {\n prepare(query: string): {\n bind(...values: unknown[]): {\n first<T = Record<string, unknown>>(): Promise<T | null>\n run(): Promise<unknown>\n }\n }\n}\n\nexport interface D1PrewarmClaimStoreOptions {\n /** Defaults to `sandbox_prewarm_claims`. Must be a bare SQL identifier — it\n * is interpolated, because a table name cannot be a bound parameter. */\n table?: string\n /** Clock seam for tests. */\n now?(): number\n}\n\nexport const DEFAULT_PREWARM_CLAIM_TABLE = 'sandbox_prewarm_claims'\n\n/** Paste into a migration. `expires_at` is epoch MILLISECONDS, matching\n * `Date.now()`, so no unit conversion sits between the lease and its clock. */\nexport const PREWARM_CLAIM_TABLE_DDL = `CREATE TABLE IF NOT EXISTS ${DEFAULT_PREWARM_CLAIM_TABLE} (\n key TEXT PRIMARY KEY,\n expires_at INTEGER NOT NULL\n)`\n\nconst SAFE_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/\n\n/**\n * A `PrewarmClaimStore` backed by one D1 table.\n *\n * ```ts\n * const prewarmer = createSandboxPrewarmer(shell, {\n * claim: createD1PrewarmClaimStore(env.DB),\n * mode: 'create-or-resume',\n * })\n * ```\n */\nexport function createD1PrewarmClaimStore(\n db: PrewarmClaimD1Like,\n options: D1PrewarmClaimStoreOptions = {},\n) {\n const table = options.table ?? DEFAULT_PREWARM_CLAIM_TABLE\n // A table name cannot be bound, so it is interpolated — which makes it the\n // one injection surface here. Reject anything that is not a bare identifier\n // at construction time, where the stack still points at the caller.\n if (!SAFE_IDENTIFIER.test(table)) {\n throw new Error(`Invalid prewarm claim table name: ${JSON.stringify(table)}`)\n }\n const now = options.now ?? (() => Date.now())\n\n const acquireSql = `INSERT INTO ${table} (key, expires_at) VALUES (?1, ?2)\nON CONFLICT(key) DO UPDATE SET expires_at = ?2 WHERE ${table}.expires_at <= ?3\nRETURNING key`\n const releaseSql = `DELETE FROM ${table} WHERE key = ?1`\n const isHeldSql = `SELECT 1 AS held FROM ${table} WHERE key = ?1 AND expires_at > ?2`\n\n return {\n async acquire(key: string, ttlSeconds: number): Promise<boolean> {\n const at = now()\n const row = await db\n .prepare(acquireSql)\n .bind(key, at + ttlSeconds * 1000, at)\n .first<{ key: string }>()\n return row != null\n },\n\n async release(key: string): Promise<void> {\n // Best effort by the `PrewarmClaimStore` contract: the prewarmer swallows\n // a throw here because the TTL is what actually guarantees release.\n await db.prepare(releaseSql).bind(key).run()\n },\n\n async isHeld(key: string): Promise<boolean> {\n const row = await db.prepare(isHeldSql).bind(key, now()).first<{ held: number }>()\n return row != null\n },\n }\n}\n"],"mappings":"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","names":["value","credentials","retryAfterMs","res","refreshed","box"]}
|