@tangle-network/agent-app 0.45.4 → 0.45.6

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/recovery.ts","../src/sandbox/diagnostics.ts","../src/sandbox/workspace-sandbox-manager.ts","../src/sandbox/terminal-connection.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 /**\n * Raw box environment written once at sandbox CREATION — deliberately plain\n * strings, and the one place a credential value legitimately lives.\n *\n * This is the private side of the tagged-config contract, not profile\n * material: an `AgentProfileMcpServer` may only carry a `secret-ref` naming a\n * key, and the sandbox resolves that key against THIS map (or against the\n * platform secret store fed by {@link SandboxRuntimeConfig.secrets}). So a\n * `tokenEnvKey` passed to `buildAppToolMcpServers` must name a variable this\n * seam places, or the reference resolves to nothing.\n *\n * It is NOT widened to tagged values: the sandbox SDK's create payload types\n * `env` as `Record<string, string>`, and {@link assertEnvWithinLimits}\n * measures those bytes against the kernel's per-entry `MAX_ARG_STRLEN`.\n * Tagging it would make the box env reference itself.\n *\n * Values are workspace-wide and fixed for the box's lifetime, so a per-user\n * or per-resource credential cannot be placed here — see\n * `unresolvableSurfaceCredential` in `../tools/mcp`.\n */\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 // Opt in to replacing a box the platform cannot bring up.\n //\n // Default false, and the default is the conservative one on purpose: a\n // replacement is a delete, and delete DEPROVISIONS the persistent workspace\n // (#299). An app whose durable state lives in the box's filesystem must not\n // set this — losing the box loses the user's work.\n //\n // Set it when the box is a CACHE of state held elsewhere (a vault, a DB, an\n // object store) and can be rebuilt. Then a box that cannot be started is\n // abandoned and re-created once, instead of leaving the workspace unable to\n // run for as long as the box stays unbringable — which, without this, is\n // forever: the box key is derived from the workspace, so every later attempt\n // resumes the same dead box and fails identically.\n replaceUnbringableBox?: 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 /**\n * NAME of the box-environment variable holding the capability token — never\n * the token itself. Every emitted `Authorization` header is a `secret-ref` to\n * this key, which the sandbox resolves privately; see\n * `BuildHttpMcpServerOptions.tokenEnvKey` in `../tools/mcp`.\n *\n * The key must name a variable the box carries — placed by\n * {@link SandboxRuntimeConfig.env} at creation, or injected from the platform\n * secret store via {@link SandboxRuntimeConfig.secrets}.\n */\n tokenEnvKey: 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 // No cast: since the tagged-config contract (agent-interface 0.38.0) the\n // builder's own return type IS an AgentProfileMcpServer. The cast that used\n // to sit here is what let the pre-0.38.0 plain-string shape ship.\n entries[key] = buildAppToolMcpServer({\n tool,\n baseUrl: options.baseUrl,\n tokenEnvKey: options.tokenEnvKey,\n ctx: options.ctx,\n description,\n headerNames: options.headerNames,\n })\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\nimport {\n isSandboxHostCapacityFailure,\n serializeSandboxProvisioningError,\n} from './diagnostics'\n\nexport * from './recovery'\n\nexport {\n serializeSandboxProvisioningError,\n formatSandboxProvisioningSupportDetails,\n formatSandboxProvisioningUserMessage,\n isSandboxAuthFailure,\n isSandboxApiBearerAuthFailure,\n isSandboxApiSandboxMissingFailure,\n isSandboxHostCapacityFailure,\n type SafeSandboxErrorCause,\n type SafeSandboxErrorDiagnostics,\n} from './diagnostics'\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 try {\n return await provisionWorkspaceSandbox(shell, options)\n } catch (err) {\n // A box the platform cannot bring up is not a transient failure: the box\n // key is derived from the workspace, so every later call resumes the same\n // dead box and fails the same way. Left alone the workspace never runs\n // again. Replacing it is a delete, so it happens only where the shell has\n // declared the box rebuildable — and only once, so a replacement that dies\n // the same way surfaces the error instead of looping.\n if (!shell.replaceUnbringableBox) throw err\n if (options.forceNew) throw err\n if (!isUnbringableBoxError(err)) throw err\n return await provisionWorkspaceSandbox(shell, { ...options, forceNew: true })\n }\n}\n\n/**\n * True when the platform cannot bring this box up where it lives.\n *\n * Two shapes reach here. `SandboxRecoveryFailedError` is the substrate's own\n * verdict after it tried a state-preserving restart and failed. A host-capacity\n * rejection is the same verdict one layer earlier: a box is pinned to a host,\n * and a full host rejects every resume for every box on it until something else\n * there goes away. Neither is transient, and neither is fixed by trying again\n * at the same key.\n */\nfunction isUnbringableBoxError(error: unknown): boolean {\n if (error instanceof SandboxRecoveryFailedError) return true\n return isSandboxHostCapacityFailure(serializeSandboxProvisioningError(error))\n}\n\nasync function provisionWorkspaceSandbox(\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// Generic name-keyed workspace sandbox lifecycle manager.\nexport * from './workspace-sandbox-manager'\n// Browser-direct scoped-token terminal connection route (#341/#349/#350).\nexport * from './terminal-connection'\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 helpers can use it without importing the substrate-bound index.\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","/**\n * What an app does after the platform hands back a sandbox it cannot use.\n *\n * `replaceUnbringableBox` (see `./index`) decides whether to abandon a dead box.\n * This decides everything around that: which box key the next attempt uses,\n * whether the old box's snapshot is safe to restore from, whether an owner has\n * to confirm the loss first, and what the person waiting is told. It is\n * bookkeeping, not I/O — the app supplies storage through\n * {@link WorkspaceSandboxRecoveryStore}.\n *\n * ── Why the tables ──────────────────────────────────────────────────────────\n * Every fact about an action or a code lives in ACTIONS/CODES below, and the\n * types are DERIVED from those tables. That is deliberate, and it is the whole\n * reliability argument for this module.\n *\n * The hand-maintained alternative — a union type plus a separate `x === 'a' ||\n * x === 'b' || …` list per question — silently drops anything not on the list.\n * A recovery recorded under an unlisted action is written to storage, read\n * back, discarded as malformed, and the workspace re-provisions the box it just\n * abandoned. Nothing throws. The fix looks correct, ships, and does nothing.\n * That failure was hit twice inside one change before this module existed.\n *\n * With the tables, adding an action or code is a compile error until every\n * question about it is answered. There is no list to forget.\n */\n\n/** The box exists and still holds unsnapshotted state, so discarding it is an\n * owner's decision, not the runtime's. */\nexport const EGRESS_PROXY_RECOVERY_REQUIRED = 'EGRESS_PROXY_RECOVERY_REQUIRED'\nexport const EGRESS_PROXY_RECOVERY_PHASE = 'egress_proxy_recovery'\n/** The platform no longer has the box. Nothing to confirm, delete, or restore. */\nexport const WORKSPACE_SANDBOX_MISSING = 'WORKSPACE_SANDBOX_MISSING'\n/** The box exists but its host has no free slot, so it can never be resumed\n * where it is. A replacement can be placed on a host with room. */\nexport const WORKSPACE_SANDBOX_HOST_EXHAUSTED = 'WORKSPACE_SANDBOX_HOST_EXHAUSTED'\n/** The platform ran its own recovery, failed, and asked for a replacement.\n * Covers every way a box ends up unbringable with no more specific cause. */\nexport const WORKSPACE_SANDBOX_UNRECOVERABLE = 'WORKSPACE_SANDBOX_UNRECOVERABLE'\n\nexport const WORKSPACE_SANDBOX_SNAPSHOT_MAX_AGE_MS = 24 * 60 * 60 * 1000\n\n/**\n * Every recovery cause, and the one thing the runtime must know about each:\n * whether the box it names can still be read from.\n *\n * `snapshotUsable: false` is not a preference. A snapshot is addressed by the\n * sandbox it was taken from, so when that sandbox cannot be started the restore\n * fails exactly the way the resume did — and an app that tried it would turn a\n * recoverable workspace into a stuck one.\n */\nconst CODES = {\n [EGRESS_PROXY_RECOVERY_REQUIRED]: { snapshotUsable: true },\n [WORKSPACE_SANDBOX_MISSING]: { snapshotUsable: false },\n [WORKSPACE_SANDBOX_HOST_EXHAUSTED]: { snapshotUsable: false },\n [WORKSPACE_SANDBOX_UNRECOVERABLE]: { snapshotUsable: false },\n} as const\n\nexport type WorkspaceSandboxRecoveryCode = keyof typeof CODES\n\n/**\n * Every recovery action, and whether it means a replacement box has been\n * CHOSEN — the single question that decides which box key the next provisioning\n * attempt uses.\n *\n * `replacementChosen: false` covers the states where a key may be recorded but\n * must not be used yet: an owner has been asked and has not answered, or has\n * answered no. Handing back a key in those states replaces a box the owner\n * declined to lose.\n */\nconst ACTIONS = {\n confirmation_required: { replacementChosen: false },\n deletion_declined: { replacementChosen: false },\n replacement_authorized: { replacementChosen: false },\n snapshot_replacement_authorized: { replacementChosen: false },\n replacement_started: { replacementChosen: true },\n replacement_completed: { replacementChosen: true },\n snapshot_replacement_started: { replacementChosen: true },\n snapshot_restore_failed: { replacementChosen: true },\n snapshot_replacement_completed: { replacementChosen: true },\n missing_replacement_started: { replacementChosen: true },\n missing_replacement_completed: { replacementChosen: true },\n unrecoverable_replacement_started: { replacementChosen: true },\n unrecoverable_replacement_completed: { replacementChosen: true },\n} as const\n\nexport type WorkspaceSandboxRecoveryAction = keyof typeof ACTIONS\n\nexport type WorkspaceSandboxSnapshotAvailability = 'available' | 'missing' | 'stale'\nexport type WorkspaceSandboxSnapshotFreshness = 'fresh' | 'stale' | 'unknown'\n\n/** A snapshot the app took of a box, addressed by the box it came from. */\nexport interface WorkspaceSandboxSnapshot {\n fromSandboxId: string\n createdAt: string\n [key: string]: unknown\n}\n\nexport interface WorkspaceSandboxSnapshotAssessment {\n availability: WorkspaceSandboxSnapshotAvailability\n freshness: WorkspaceSandboxSnapshotFreshness\n snapshot?: WorkspaceSandboxSnapshot\n}\n\nexport interface WorkspaceSandboxRecoveryState {\n code: WorkspaceSandboxRecoveryCode\n sandboxId: string\n detectedAt: string\n snapshot: WorkspaceSandboxSnapshotAssessment\n action: WorkspaceSandboxRecoveryAction\n replacementBoxKey?: string\n replacementSandboxId?: string\n confirmedAt?: string\n}\n\nexport type WorkspaceSandboxRecoveryDecision = 'replace' | 'decline'\n\n/** Raised when chat cannot continue until an owner decides about the box. */\nexport class WorkspaceSandboxRecoveryRequiredError extends Error {\n readonly code = EGRESS_PROXY_RECOVERY_REQUIRED\n readonly status = 409\n readonly phase = EGRESS_PROXY_RECOVERY_PHASE\n readonly recovery: WorkspaceSandboxRecoveryState\n\n constructor(recovery: WorkspaceSandboxRecoveryState, cause: Error) {\n super(workspaceSandboxRecoveryMessage(recovery), { cause })\n this.name = 'WorkspaceSandboxRecoveryRequiredError'\n this.recovery = recovery\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null\n}\n\nfunction asNonEmptyString(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim().length > 0 ? value : undefined\n}\n\n/** Walk `cause` and `errors` breadth-first, cycle-safe. */\nfunction errorChain(error: unknown): unknown[] {\n const pending = [error]\n const visited = new Set<unknown>()\n const chain: unknown[] = []\n\n while (pending.length > 0) {\n const current = pending.shift()\n if (current === undefined || current === null || visited.has(current)) continue\n visited.add(current)\n chain.push(current)\n if (!isRecord(current)) continue\n if (current.cause !== undefined) pending.push(current.cause)\n if (Array.isArray(current.errors)) pending.push(...current.errors)\n }\n\n return chain\n}\n\nexport function isEgressProxyRecoveryRequiredError(error: unknown): boolean {\n return errorChain(error).some((current) =>\n isRecord(current) && current.code === EGRESS_PROXY_RECOVERY_REQUIRED,\n )\n}\n\nexport function isWorkspaceSandboxSnapshotRestoreError(error: unknown): boolean {\n return errorChain(error).some((current) => {\n const message = current instanceof Error\n ? current.message\n : isRecord(current) && typeof current.message === 'string'\n ? current.message\n : ''\n return /snapshot|fromSnapshot|restore/i.test(message)\n })\n}\n\n/**\n * Judge a snapshot against the box being replaced.\n *\n * Fresh means BOTH that it came from this exact sandbox and that it is inside\n * the age bound — a snapshot from a different box restores someone else's\n * filesystem, which is worse than starting empty.\n */\nexport function assessWorkspaceSandboxSnapshot(\n snapshot: WorkspaceSandboxSnapshot | undefined,\n sandboxId: string,\n now = Date.now(),\n): WorkspaceSandboxSnapshotAssessment {\n if (!snapshot) return { availability: 'missing', freshness: 'unknown' }\n\n const createdAt = Date.parse(snapshot.createdAt)\n const ageMs = now - createdAt\n const isFresh = snapshot.fromSandboxId === sandboxId\n && Number.isFinite(createdAt)\n && ageMs >= 0\n && ageMs <= WORKSPACE_SANDBOX_SNAPSHOT_MAX_AGE_MS\n\n return isFresh\n ? { availability: 'available', freshness: 'fresh', snapshot }\n : { availability: 'stale', freshness: 'stale', snapshot }\n}\n\nfunction isSnapshotAssessment(value: unknown): value is WorkspaceSandboxSnapshotAssessment {\n if (!isRecord(value)) return false\n const availability = value.availability\n const freshness = value.freshness\n return (availability === 'available' || availability === 'missing' || availability === 'stale')\n && (freshness === 'fresh' || freshness === 'stale' || freshness === 'unknown')\n}\n\nexport function isWorkspaceSandboxRecoveryAction(\n value: unknown,\n): value is WorkspaceSandboxRecoveryAction {\n return typeof value === 'string' && Object.hasOwn(ACTIONS, value)\n}\n\nexport function isWorkspaceSandboxRecoveryCode(\n value: unknown,\n): value is WorkspaceSandboxRecoveryCode {\n return typeof value === 'string' && Object.hasOwn(CODES, value)\n}\n\nexport function isWorkspaceSandboxRecoveryState(\n value: unknown,\n): value is WorkspaceSandboxRecoveryState {\n if (!isRecord(value)) return false\n return isWorkspaceSandboxRecoveryCode(value.code)\n && !!asNonEmptyString(value.sandboxId)\n && !!asNonEmptyString(value.detectedAt)\n && isSnapshotAssessment(value.snapshot)\n && isWorkspaceSandboxRecoveryAction(value.action)\n}\n\nexport function workspaceSandboxRecoveryFromError(\n error: unknown,\n): WorkspaceSandboxRecoveryState | undefined {\n for (const current of errorChain(error)) {\n if (!isRecord(current) || !isWorkspaceSandboxRecoveryState(current.recovery)) continue\n return current.recovery\n }\n return undefined\n}\n\n/** What to tell the person waiting. Never names the product, so an app can\n * surface it verbatim. */\nexport function workspaceSandboxRecoveryMessage(recovery: WorkspaceSandboxRecoveryState): string {\n if (recovery.code === WORKSPACE_SANDBOX_MISSING) {\n return 'The sandbox behind this workspace no longer exists on the platform. A replacement is being provisioned and your saved work is restored into it.'\n }\n if (!CODES[recovery.code].snapshotUsable) {\n return 'The sandbox behind this workspace could not be started again. A replacement is being provisioned and your saved work is restored into it.'\n }\n if (recovery.action === 'deletion_declined') {\n return 'Sandbox recovery remains paused because replacement was declined. The stopped sandbox has not been deleted.'\n }\n if (recovery.action === 'confirmation_required') {\n const snapshotReason = recovery.snapshot.availability === 'missing'\n ? 'No restorable sandbox snapshot is available.'\n : 'The available sandbox snapshot is stale.'\n return `Sandbox recovery requires owner confirmation before replacement. ${snapshotReason} The stopped sandbox has not been deleted.`\n }\n if (recovery.action === 'snapshot_restore_failed') {\n return 'Sandbox replacement could not restore its verified snapshot, and an empty fallback was not created because it could lose workspace state.'\n }\n return 'Sandbox recovery is required before chat can continue. The stopped sandbox has not been deleted.'\n}\n\nexport function workspaceSandboxRecoveryRecommendedActions(\n recovery: WorkspaceSandboxRecoveryState,\n): string[] {\n if (!CODES[recovery.code].snapshotUsable) {\n return ['Retry after the replacement sandbox finishes provisioning.']\n }\n if (recovery.action === 'deletion_declined') {\n return ['Keep the stopped sandbox for support or ask a workspace owner to authorize replacement.']\n }\n if (recovery.action === 'confirmation_required') {\n return ['An owner can explicitly replace the sandbox after accepting loss of unsnapshotted sandbox-local changes.']\n }\n if (recovery.action === 'snapshot_restore_failed') {\n return ['Retry snapshot restoration or contact support. An empty replacement is not created automatically.']\n }\n return ['Retry after sandbox recovery completes.']\n}\n\n/** Flat key/value shape for a log line — no nesting, no secrets. */\nexport function workspaceSandboxRecoveryDiagnostic(\n recovery: WorkspaceSandboxRecoveryState,\n): Record<string, string | undefined> {\n return {\n sandboxId: recovery.sandboxId,\n recoveryCode: recovery.code,\n snapshotAvailability: recovery.snapshot.availability,\n snapshotFreshness: recovery.snapshot.freshness,\n selectedRecoveryAction: recovery.action,\n replacementSandboxId: recovery.replacementSandboxId,\n }\n}\n\n/**\n * The box key the next provisioning attempt should use, or undefined to keep\n * using the workspace's own key.\n *\n * Reads {@link ACTIONS} rather than a hand-kept list, because the failure mode\n * of a hand-kept list here is invisible: the key is recorded, silently ignored,\n * and provisioning goes back to the box the app just decided to abandon.\n */\nexport function preferredWorkspaceSandboxRecoveryBoxKey(\n recovery: WorkspaceSandboxRecoveryState | undefined,\n): string | undefined {\n if (!recovery?.replacementBoxKey) return undefined\n return ACTIONS[recovery.action].replacementChosen ? recovery.replacementBoxKey : undefined\n}\n\n/** Whether the replacement should be restored from the old box's snapshot. */\nexport function shouldRestoreWorkspaceSandboxRecovery(\n recovery: WorkspaceSandboxRecoveryState | undefined,\n): boolean {\n if (!recovery) return false\n if (!CODES[recovery.code].snapshotUsable) return false\n return !!preferredWorkspaceSandboxRecoveryBoxKey(recovery)\n && recovery.snapshot.availability === 'available'\n}\n\n/**\n * Where an app keeps recovery state. One row per workspace, last write wins —\n * a recovery is a current situation, not a history.\n */\nexport interface WorkspaceSandboxRecoveryStore {\n read: (workspaceId: string) => Promise<WorkspaceSandboxRecoveryState | undefined>\n write: (workspaceId: string, recovery: WorkspaceSandboxRecoveryState) => Promise<void>\n}\n\nexport interface WorkspaceSandboxRecoveryManager {\n read: (workspaceId: string) => Promise<WorkspaceSandboxRecoveryState | undefined>\n record: (workspaceId: string, recovery: WorkspaceSandboxRecoveryState) => Promise<void>\n /** Record an owner's decision. Returns undefined when the stored recovery does\n * not name this sandbox — a decision about a box that has already been\n * replaced must not resurrect it. */\n decide: (args: {\n workspaceId: string\n sandboxId: string\n decision: WorkspaceSandboxRecoveryDecision\n replacementBoxKey?: string\n }) => Promise<WorkspaceSandboxRecoveryState | undefined>\n /** Mark a replacement finished and name the box that took over. */\n complete: (args: {\n workspaceId: string\n replacementSandboxId: string\n }) => Promise<WorkspaceSandboxRecoveryState | undefined>\n}\n\n/**\n * Bind the recovery bookkeeping to an app's storage.\n *\n * The app owns persistence — a D1 column, a KV key, a Postgres row — and\n * nothing else. Every rule about which action means what stays here, so it\n * cannot drift between apps.\n */\nexport function createWorkspaceSandboxRecoveryManager(\n store: WorkspaceSandboxRecoveryStore,\n): WorkspaceSandboxRecoveryManager {\n async function record(workspaceId: string, recovery: WorkspaceSandboxRecoveryState) {\n await store.write(workspaceId, recovery)\n }\n\n return {\n read: store.read,\n record,\n async decide({ workspaceId, sandboxId, decision, replacementBoxKey }) {\n const current = await store.read(workspaceId)\n if (!current || current.sandboxId !== sandboxId) return undefined\n const next: WorkspaceSandboxRecoveryState = {\n ...current,\n action: decision === 'replace'\n ? (current.snapshot.availability === 'available'\n ? 'snapshot_replacement_authorized'\n : 'replacement_authorized')\n : 'deletion_declined',\n confirmedAt: new Date().toISOString(),\n ...(decision === 'replace' && replacementBoxKey ? { replacementBoxKey } : {}),\n }\n await record(workspaceId, next)\n return next\n },\n async complete({ workspaceId, replacementSandboxId }) {\n const current = await store.read(workspaceId)\n if (!current) return undefined\n const next: WorkspaceSandboxRecoveryState = {\n ...current,\n action: completionFor(current.action),\n replacementSandboxId,\n }\n await record(workspaceId, next)\n return next\n },\n }\n}\n\n/** The finished form of an in-flight replacement action. */\nfunction completionFor(action: WorkspaceSandboxRecoveryAction): WorkspaceSandboxRecoveryAction {\n switch (action) {\n case 'missing_replacement_started':\n return 'missing_replacement_completed'\n case 'unrecoverable_replacement_started':\n return 'unrecoverable_replacement_completed'\n case 'snapshot_replacement_started':\n case 'snapshot_replacement_authorized':\n return 'snapshot_replacement_completed'\n default:\n return 'replacement_completed'\n }\n}\n\n/** Every declared action, for exhaustiveness tests in apps and here. */\nexport const WORKSPACE_SANDBOX_RECOVERY_ACTIONS = Object.keys(\n ACTIONS,\n) as readonly WorkspaceSandboxRecoveryAction[]\n\n/** Every declared cause. */\nexport const WORKSPACE_SANDBOX_RECOVERY_CODES = Object.keys(\n CODES,\n) as readonly WorkspaceSandboxRecoveryCode[]\n","/**\n * Read a sandbox provisioning failure without leaking what it carries.\n *\n * Provisioning errors arrive as deep `cause` chains from the sandbox API, the\n * runtime sidecar, and the app's own vault code, and they routinely carry\n * bearer tokens and signed URLs in their messages. Every app on this shell\n * needs the same three things from one: a redacted shape it can log, a\n * classification it can act on, and a sentence it can show a person. Before\n * this module each app either wrote its own or — far more often — wrote none,\n * and surfaced the raw error or a generic apology.\n *\n * The classifiers are deliberately narrow. `isSandboxApiSandboxMissingFailure`\n * does not fire on a 404 from inside a live box; `isSandboxHostCapacityFailure`\n * does not fire on capacity wording from any origin but the sandbox API. A\n * classifier that over-matches turns a transient failure into a box deletion.\n */\nimport { EGRESS_PROXY_RECOVERY_REQUIRED } from './recovery'\n\nconst SAFE_ERROR_FIELDS = [\n 'code',\n 'status',\n 'phase',\n 'endpoint',\n 'origin',\n 'retryAfterMs',\n 'sidecarVersion',\n 'containerImage',\n] as const\n\nexport interface SafeSandboxErrorCause {\n name?: string\n message?: string\n code?: string | number\n status?: string | number\n phase?: string\n endpoint?: string\n origin?: string\n retryAfterMs?: number\n sidecarVersion?: string\n containerImage?: string\n}\n\nexport interface SafeSandboxErrorDiagnostics {\n message: string\n causes: SafeSandboxErrorCause[]\n truncated: boolean\n truncatedAtDepth?: number\n cycle: boolean\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null\n}\n\nfunction asSafeScalar(value: unknown): string | number | undefined {\n if (typeof value === 'string' && value.length > 0) return redactDiagnosticText(value)\n if (typeof value === 'number' && Number.isFinite(value)) return value\n return undefined\n}\n\nfunction redactDiagnosticText(value: string): string {\n const secretKeyPattern = '(?:(?:[A-Z0-9]+_)*(?:api_?key|auth_?token|access_?token|refresh_?token|key|token|password|secret|credential)|apiKey|authToken|accessToken|refreshToken)'\n return value\n .replace(/(['\"]authorization['\"]\\s*:\\s*)(['\"])[^'\"]*\\2/gi, '$1$2[REDACTED]$2')\n .replace(/\\b(authorization\\s*[:=]\\s*)(['\"])[^'\"]*\\2/gi, '$1$2[REDACTED]$2')\n .replace(/\\b(authorization\\s*[:=]\\s*)Digest\\b[^;}\\n]*(?:\\r?\\n[ \\t]+[^;}\\n]*)*/gi, '$1[REDACTED]')\n .replace(/\\b(authorization\\s*[:=]\\s*)(?:Basic|Bearer|Negotiate)\\s+[^\\s;,&}\\n]+(?:\\r?\\n[ \\t]+[^\\s;,&}\\n]+)*/gi, '$1[REDACTED]')\n .replace(new RegExp(`\\\\b(authorization\\\\s*[:=]\\\\s*)(?!(?:Basic|Bearer|Digest|Negotiate)\\\\b)([A-Za-z][A-Za-z0-9._-]+)\\\\s+(?!(?:authorization|${secretKeyPattern})\\\\s*[:=]|['\"](?:authorization|${secretKeyPattern})['\"]\\\\s*:)([^}\\\\n]*(?:\\\\r?\\\\n[ \\\\t]+[^}\\\\n]*)*)`, 'gi'), (_match, prefix: string, _scheme: string, valuePart: string) => {\n if (valuePart.includes(';') || /\\r?\\n[ \\t]+/.test(valuePart)) return `${prefix}[REDACTED]`\n const tokenMatch = valuePart.match(/^\\S+(.*)$/)\n return `${prefix}[REDACTED]${tokenMatch?.[1] ?? ''}`\n })\n .replace(/\\b(authorization\\s*[:=]\\s*)[A-Za-z][\\w.-]+\\s+[^\\s;,&}\\n]+(?=[;&}\\n]|$)/gi, '$1[REDACTED]')\n .replace(/\\b(authorization\\s*[:=]\\s*)(?!\\[REDACTED\\])[^'\"\\s&}]+(?:[;,][^'\"\\s&}]+)*/gi, '$1[REDACTED]')\n .replace(/\\bBearer\\s+[A-Za-z0-9._~+/-]+=*/gi, 'Bearer [REDACTED]')\n .replace(/\\bsk-[A-Za-z0-9_-]{6,}\\b/g, 'sk-[REDACTED]')\n .replace(\n /([?&][^=&#\\s]*(?:token|key|secret|password|authorization)[^=&#\\s]*=)[^&#\\s]+/gi,\n '$1[REDACTED]',\n )\n .replace(\n new RegExp(`(['\"])(${secretKeyPattern})\\\\1\\\\s*:\\\\s*(['\"])[^'\"]+\\\\3`, 'gi'),\n '$1$2$1:$3[REDACTED]$3',\n )\n .replace(\n new RegExp(`\\\\b(${secretKeyPattern})\\\\s*([:=])(\\\\s*)(['\"]?)(?!Bearer\\\\b)[^'\"\\\\s;,&}]+(?:[;,]\\\\s*[^'\"\\\\s;,&}]+)*`, 'gi'),\n '$1$2$3$4[REDACTED]',\n )\n}\n\nfunction readMessage(value: unknown): string | undefined {\n if (value instanceof Error) return redactDiagnosticText(value.message)\n if (typeof value === 'string' && value.length > 0) return redactDiagnosticText(value)\n if (typeof value === 'number' && Number.isFinite(value)) return String(value)\n if (typeof value === 'boolean') return String(value)\n if (!isRecord(value)) return undefined\n const message = value.message\n return typeof message === 'string' && message.length > 0 ? redactDiagnosticText(message) : undefined\n}\n\nfunction readName(value: unknown): string | undefined {\n if (value instanceof Error) return redactDiagnosticText(value.name)\n if (!isRecord(value)) return undefined\n const name = value.name\n return typeof name === 'string' && name.length > 0 ? redactDiagnosticText(name) : undefined\n}\n\nfunction readCause(value: unknown): unknown {\n if (!isRecord(value)) return undefined\n return value.cause\n}\n\nexport function serializeSandboxProvisioningError(\n error: unknown,\n options: { maxDepth?: number } = {},\n): SafeSandboxErrorDiagnostics {\n const maxDepth = options.maxDepth ?? 6\n const causes: SafeSandboxErrorCause[] = []\n const seen = new Set<unknown>()\n let truncatedAtDepth: number | undefined\n let cycle = false\n\n let current: unknown = error\n let depth = 0\n for (; depth < maxDepth && current !== undefined && current !== null; depth += 1) {\n if (seen.has(current)) {\n cycle = true\n causes.push({\n name: 'CauseChainCycle',\n message: `cause chain cycle detected after ${depth} entries`,\n })\n break\n }\n if (isRecord(current)) seen.add(current)\n\n const cause: SafeSandboxErrorCause = {}\n const name = readName(current)\n const message = readMessage(current)\n if (name) cause.name = name\n if (message) cause.message = message\n\n if (isRecord(current)) {\n for (const field of SAFE_ERROR_FIELDS) {\n const safeValue = asSafeScalar(current[field])\n if (safeValue !== undefined) {\n Object.assign(cause, { [field]: safeValue })\n }\n }\n }\n\n if (Object.keys(cause).length > 0) causes.push(cause)\n current = readCause(current)\n }\n\n if (!cycle && isRecord(current) && seen.has(current)) {\n cycle = true\n causes.push({\n name: 'CauseChainCycle',\n message: `cause chain cycle detected after ${depth} entries`,\n })\n } else if (current !== undefined && current !== null && depth >= maxDepth) {\n truncatedAtDepth = maxDepth\n causes.push({\n name: 'CauseChainTruncated',\n message: `cause chain truncated after ${maxDepth} entries`,\n })\n }\n\n return {\n message: readMessage(error) ?? 'Sandbox unavailable',\n causes,\n truncated: truncatedAtDepth !== undefined,\n truncatedAtDepth,\n cycle,\n }\n}\n\nexport function formatSandboxProvisioningSupportDetails(\n diagnostics: SafeSandboxErrorDiagnostics,\n): string {\n const actionableCause = diagnostics.causes.find((cause) =>\n cause.code !== undefined\n || cause.status !== undefined\n || cause.phase !== undefined\n || cause.endpoint !== undefined\n || cause.origin !== undefined\n || cause.retryAfterMs !== undefined\n || cause.sidecarVersion !== undefined\n || cause.containerImage !== undefined,\n ) ?? diagnostics.causes[1]\n\n if (!actionableCause) return 'Support details: no nested sandbox cause details were available.'\n\n const details = [\n typeof actionableCause.name === 'string' ? redactDiagnosticText(actionableCause.name) : undefined,\n actionableCause.code !== undefined ? `code=${actionableCause.code}` : undefined,\n actionableCause.status !== undefined ? `status=${actionableCause.status}` : undefined,\n actionableCause.phase !== undefined ? `phase=${redactDiagnosticText(actionableCause.phase)}` : undefined,\n actionableCause.endpoint !== undefined ? `endpoint=${redactDiagnosticText(actionableCause.endpoint)}` : undefined,\n actionableCause.origin !== undefined ? `origin=${redactDiagnosticText(actionableCause.origin)}` : undefined,\n actionableCause.retryAfterMs !== undefined ? `retryAfterMs=${actionableCause.retryAfterMs}` : undefined,\n actionableCause.sidecarVersion !== undefined ? `sidecarVersion=${redactDiagnosticText(actionableCause.sidecarVersion)}` : undefined,\n actionableCause.containerImage !== undefined ? `containerImage=${redactDiagnosticText(actionableCause.containerImage)}` : undefined,\n typeof actionableCause.message === 'string' ? redactDiagnosticText(actionableCause.message) : undefined,\n ].filter(Boolean)\n\n if (details.length === 0) return 'Support details: no nested sandbox cause details were available.'\n return `Support details: ${details.join('; ')}`\n}\n\nexport function isSandboxAuthFailure(diagnostics: SafeSandboxErrorDiagnostics): boolean {\n return diagnostics.causes.some((cause) => {\n const code = typeof cause.code === 'string' ? cause.code.toUpperCase() : undefined\n const status = typeof cause.status === 'number'\n ? cause.status\n : typeof cause.status === 'string'\n ? Number.parseInt(cause.status, 10)\n : undefined\n const name = typeof cause.name === 'string' ? cause.name.toLowerCase() : ''\n const message = typeof cause.message === 'string' ? cause.message.toLowerCase() : ''\n\n return code === 'AUTH_ERROR'\n || status === 401\n || name.includes('autherror')\n || message.includes('missing or invalid authentication')\n })\n}\n\nexport function isSandboxApiBearerAuthFailure(diagnostics: SafeSandboxErrorDiagnostics): boolean {\n return diagnostics.causes.some((cause) => {\n const status = typeof cause.status === 'number'\n ? cause.status\n : typeof cause.status === 'string'\n ? Number.parseInt(cause.status, 10)\n : undefined\n if (status !== 401) return false\n if (cause.origin !== 'sandbox-api') return false\n if (typeof cause.endpoint !== 'string') return false\n const endpointPath = sandboxApiEndpointPath(cause.endpoint)\n if (!endpointPath) return false\n return /^\\/v1\\/sandboxes\\/[^/?#]+(?:\\/(?!runtime(?:[/?#]|$))[^?#]*)?(?:[?#].*)?$/.test(endpointPath)\n })\n}\n\n/**\n * True when the sandbox API answered 404 for a specific sandbox resource — the\n * box behind a persisted sandbox id no longer exists.\n *\n * A sandbox id is a cache of where a workspace's box lives, not the workspace's\n * identity: the platform reaps, suspends, and loses boxes as ordinary lifecycle\n * events. Callers use this to discard the dead id and provision a replacement,\n * so the match is deliberately narrow — a 404 from the runtime sidecar\n * (`/runtime/...`, a missing file or session inside a live box) is NOT this.\n */\nexport function isSandboxApiSandboxMissingFailure(diagnostics: SafeSandboxErrorDiagnostics): boolean {\n return diagnostics.causes.some((cause) => {\n const status = typeof cause.status === 'number'\n ? cause.status\n : typeof cause.status === 'string'\n ? Number.parseInt(cause.status, 10)\n : undefined\n if (status !== 404) return false\n if (cause.origin !== 'sandbox-api') return false\n if (typeof cause.endpoint !== 'string') return false\n const endpointPath = sandboxApiEndpointPath(cause.endpoint)\n if (!endpointPath) return false\n return /^\\/v1\\/sandboxes\\/[^/?#]+(?:\\/(?!runtime(?:[/?#]|$))[^?#]*)?(?:[?#].*)?$/.test(endpointPath)\n })\n}\n\n/**\n * True when a resume failed because the host the box is pinned to cannot seat\n * it — the host's slot budget is exhausted, not the box's fault and not\n * something waiting fixes.\n *\n * A box lives on one host. When that host fills, every future resume for every\n * box on it fails identically and permanently, so a workspace whose box landed\n * on a full host is bricked until it is placed somewhere else. Callers use this\n * the same way they use {@link isSandboxApiSandboxMissingFailure}: discard the\n * dead id and provision a replacement, which the orchestrator is free to place\n * on a host with room. The workspace itself is preserved — it lives in the\n * Vault, not in the box's filesystem.\n *\n * Matched on the message because the sandbox API returns a generic\n * `SERVER_ERROR` for it; a dedicated code upstream would replace this.\n */\nexport function isSandboxHostCapacityFailure(diagnostics: SafeSandboxErrorDiagnostics): boolean {\n return diagnostics.causes.some((cause) => {\n if (cause.origin !== 'sandbox-api') return false\n if (typeof cause.message !== 'string') return false\n return /host has no available slot|host capacity reservation failed/i.test(cause.message)\n })\n}\n\nfunction sandboxApiEndpointPath(endpoint: string): string | null {\n if (endpoint.startsWith('/')) return endpoint\n try {\n const url = new URL(endpoint)\n return `${url.pathname}${url.search}${url.hash}`\n } catch {\n return null\n }\n}\n\nexport function formatSandboxProvisioningUserMessage(\n diagnostics: SafeSandboxErrorDiagnostics,\n): string {\n if (diagnostics.causes.some((cause) => cause.code === EGRESS_PROXY_RECOVERY_REQUIRED)) {\n return 'Sandbox recovery is required before chat can continue. The stopped sandbox has not been deleted.'\n }\n\n // The sandbox itself is reachable — copying the Vault into it is what did not\n // finish. Saying \"the service is unavailable\" sends people to inspect\n // infrastructure that is healthy.\n if (diagnostics.causes.some((cause) => cause.code === 'vault.hydration_incomplete')) {\n return 'I couldn\\'t finish copying your Vault into the sandbox, so I stopped rather than work from a partial copy. The copy resumes where it left off — try again in a moment.'\n }\n\n if (isSandboxApiBearerAuthFailure(diagnostics)) {\n return 'I\\'m unable to reconnect to the sandbox because its sandbox API credential was rejected. Another request may have rotated the bearer used for this operation.'\n }\n\n if (isSandboxAuthFailure(diagnostics)) {\n return 'I\\'m unable to reconnect to the sandbox because its runtime authentication failed. This can happen when an existing sandbox is reused with stale credentials.'\n }\n\n if (diagnostics.causes.some((cause) => (\n cause.code === 'PAYLOAD_TOO_LARGE'\n || cause.code === 'FILE_TOO_LARGE'\n || cause.status === 413\n || cause.status === '413'\n ))) {\n return 'An attachment is too large for the sandbox to accept. Use a smaller file and try again.'\n }\n\n // Matched by code, not bare 429: the sidecar's rate limiters also answer\n // 429, and those are not attachment-staging pressure.\n if (diagnostics.causes.some((cause) => cause.code === 'UPLOAD_BUDGET_EXHAUSTED')) {\n return 'Too much attachment data is staged in the sandbox at once. Retry shortly.'\n }\n\n return 'I\\'m unable to connect to the sandbox right now. This usually means the sandbox service is not configured or is temporarily unavailable.'\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/** 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 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/**\n * Create a generic name-keyed sandbox lifecycle manager for products that\n * drive their own SDK or box types.\n */\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 * Browser-direct scoped-token terminal connection route (#341/#349).\n *\n * Epic #343 / decision #341: the fleet's terminal transport is browser-direct\n * — the product's server authenticates the request and provisions/resolves\n * the sandbox exactly once, mints a short-lived SDK scoped token\n * (`box.mintScopedToken`), and hands the browser just enough to connect\n * `TerminalView` straight to the sidecar. No worker relays terminal bytes.\n * The deprecated same-origin relay was removed in #350 after the fleet\n * migrated, leaving this as the package's only terminal transport.\n *\n * VERIFIED PLATFORM CONTRACT (`@tangle-network/sandbox` 0.15.2 + the\n * orchestrator/sidecar enforcement source, verified 2026-07-30):\n *\n * 1. Only a `scope: 'session-runtime'` token carries the `terminal`\n * capability (`cap: [\"read\", \"workspace\", \"terminal\"]`). `'project'` and\n * `'session'` mint SessionGateway READ tokens (HS256, `typ: \"read\"`, no\n * `cap` claim at all — a different token family the sidecar rejects\n * outright); `'read-only'` carries `cap: [\"read\"]` only. **No scope other\n * than `'session-runtime'` can ever open a terminal**, which is why this\n * route no longer takes `scope` as a parameter — it is pinned.\n * 2. The orchestrator's terminal WS gate fails closed unless the token's\n * `sid` claim EQUALS the `<connectionId>` path segment of\n * `/terminals/<connectionId>/ws`\n * (`verifyScopedSidecarCapability(sidecar, token, \"terminal\", connectionId)`).\n * So `mintScopedToken({ scope: 'session-runtime', sessionId })` MUST be\n * called with `sessionId === connectionId` — the exact id `TerminalView`\n * will dial. That is why this route threads a `connectionId` end to end\n * instead of deriving its own session id.\n * 3. The browser-safe terminal base is the mint result's `sidecarProxyUrl`\n * (`/v1/sidecar-proxy/{id}` on the Sandbox API) — **not**\n * `box.connection.runtimeUrl`. The SDK's own `_attachTerminal` is the\n * reference implementation:\n * `mintScopedToken({ scope: 'session-runtime', sessionId: connectionId })`\n * then dial `${scoped.sidecarProxyUrl}/terminals/${connectionId}/ws`.\n * Pointing `TerminalView` at `runtimeUrl` fails auth regardless of scope —\n * only the sidecar-proxy hop decodes the browser's\n * `bearer.<base64url>` WS subprotocol.\n *\n * HISTORICAL NOTE: an earlier shape of this route (and legal-agent's\n * production route it was modeled on, `scope: 'project'` +\n * `connection.runtimeUrl`) predates the platform's terminal-auth hardening —\n * the terminal capability gate (2026-06-19), the scoped-token terminal WS\n * path (2026-07-15), and the `sid`-binding fail-closed check\n * (2026-07-17/18). That shape does not authenticate on the current platform\n * and is not a valid spec for the token path anymore.\n *\n * SECURITY POSTURE: unlike the read-only session-gateway streaming token\n * (`box.mintScopedToken({scope:'session'})` paired with\n * `SessionGatewayClient`), the token this route mints grants **command\n * execution** in the sandbox once handed to `TerminalView`. Its safety rests\n * on a **short TTL** (default 15 minutes, the SDK's own max — still a caller\n * parameter) and, structurally, the narrowest scope the platform offers: one\n * box (`ensureSandbox` resolved it for THIS user), one terminal connection\n * (`session-runtime` bound to a single `sid`). Widening the TTL default\n * without an explicit reason is a security regression, not a convenience.\n *\n * Wire contract: the browser passes the response's `sidecarUrl` as\n * `TerminalView`'s `apiUrl` prop, `token` as its token prop, and the\n * response's echoed `connectionId` as `TerminalView`'s `connectionId` prop —\n * the token's `sid` is bound to exactly that id, so any other value fails the\n * WS upgrade. sandbox-ui sends the token as a `bearer.<base64url>` WebSocket\n * subprotocol (browsers cannot set `Authorization` on a WS handshake).\n * `useSandboxTerminalConnection` (`../web-react/sandbox-terminal`) carries\n * `connectionId` through automatically.\n */\n\n/** The structural surface this route needs from an SDK sandbox box — no `@tangle-network/sandbox` class import (invariant 3). */\nexport interface TerminalConnectionBoxLike {\n id: string\n status?: string\n connection?: { runtimeUrl?: string }\n mintScopedToken(opts: {\n scope: string\n sessionId?: string\n ttlMinutes?: number\n }): Promise<{ token: string; expiresAt: Date; sidecarProxyUrl: string }>\n}\n\n/** Configuration for {@link createSandboxTerminalConnectionRoute}. */\nexport interface SandboxTerminalConnectionRouteOptions<TBox extends TerminalConnectionBoxLike, TUser> {\n /**\n * Authenticate the incoming request. Return the authenticated user, or a\n * `Response` to short-circuit the route (e.g. a 401) — that Response is\n * returned to the caller verbatim.\n */\n requireUser(request: Request): Promise<TUser | Response>\n /**\n * Resolve (provisioning if needed) the sandbox this user's terminal should\n * connect to. Domain-specific provisioning (workspace- vs user-scoped,\n * naming, reuse) stays entirely in the product's implementation of this\n * seam — the route only reacts to success/failure.\n */\n ensureSandbox(user: TUser, request: Request): Promise<TBox>\n /** `box.mintScopedToken` TTL in minutes. Default `15` (the SDK's own max). */\n ttlMinutes?: number\n /**\n * Resolve the terminal connection id the minted token's `sid` is bound to\n * — this MUST be the same id `TerminalView` dials\n * (`tabTerminalConnectionId()`), because the orchestrator's terminal WS\n * gate fails closed unless `sid === <connectionId>` in\n * `/terminals/<connectionId>/ws`.\n *\n * `requested` is `new URL(request.url).searchParams.get('connectionId')`.\n * The default resolver returns `requested` unchanged. A product may\n * validate/namespace the id here (e.g. a per-user prefix in a shared box)\n * — if it rewrites the id, the client MUST use the response's echoed\n * `connectionId`, since the sid binding makes any other id fail the WS\n * upgrade.\n *\n * A resolved falsy/empty value fails the request with a 400 before any\n * sandbox work happens.\n */\n resolveConnectionId?: (ctx: {\n request: Request\n user: TUser\n box: TBox\n requested: string | null\n }) => string | null | undefined | Promise<string | null | undefined>\n}\n\nconst DEFAULT_TTL_MINUTES = 15\nconst TERMINAL_SCOPE = 'session-runtime'\n\nfunction defaultResolveConnectionId(ctx: { requested: string | null }): string | null {\n return ctx.requested\n}\n\n/**\n * Build the browser-direct terminal connection route: `GET` handler that\n * authenticates, resolves the sandbox, mints a scoped token pinned to\n * `scope: 'session-runtime'` (the ONLY scope the platform grants the\n * `terminal` capability to), and returns exactly what the browser needs to\n * connect `TerminalView` directly to the sidecar — no worker in the data\n * path once the terminal is open.\n *\n * Response shapes:\n * - `requireUser` returns a `Response` → that `Response`, verbatim.\n * - `ensureSandbox` throws → `500 {error}` (the thrown message surfaced).\n * - `box.connection?.runtimeUrl` missing → `503 {error, status: box.status}`\n * — this is a READINESS gate only; the URL returned to the client on\n * success is always the mint's `sidecarProxyUrl`, never `runtimeUrl`.\n * - resolved connection id is falsy/empty → `400 {error}`.\n * - `box.mintScopedToken` rejects → `503 {error}`.\n * - success → `200 {sidecarUrl, token, expiresAt, status, sandboxId, connectionId}`.\n * `connectionId` is the id the token's `sid` is bound to — the client MUST\n * hand this (not its own) to `TerminalView`.\n */\nexport function createSandboxTerminalConnectionRoute<TBox extends TerminalConnectionBoxLike, TUser>(\n opts: SandboxTerminalConnectionRouteOptions<TBox, TUser>,\n): (request: Request) => Promise<Response> {\n const resolveConnectionId = opts.resolveConnectionId ?? defaultResolveConnectionId\n\n return async function handleSandboxTerminalConnection(request: Request): Promise<Response> {\n const user = await opts.requireUser(request)\n if (user instanceof Response) return user\n\n let box: TBox\n try {\n box = await opts.ensureSandbox(user, request)\n } catch (err) {\n return Response.json(\n { error: err instanceof Error ? err.message : 'Failed to provision sandbox' },\n { status: 500 },\n )\n }\n\n const runtimeUrl = box.connection?.runtimeUrl\n if (!runtimeUrl) {\n return Response.json(\n {\n error: '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 requested = new URL(request.url).searchParams.get('connectionId')\n const connectionId = await resolveConnectionId({ request, user, box, requested })\n if (!connectionId) {\n return Response.json(\n {\n error:\n \"connectionId is required — pass the same id TerminalView dials (tabTerminalConnectionId()) as the 'connectionId' query parameter\",\n },\n { status: 400 },\n )\n }\n\n let scoped: { token: string; expiresAt: Date; sidecarProxyUrl: string }\n try {\n scoped = await box.mintScopedToken({\n scope: TERMINAL_SCOPE,\n ttlMinutes: opts.ttlMinutes ?? DEFAULT_TTL_MINUTES,\n sessionId: connectionId,\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 return Response.json({\n sidecarUrl: scoped.sidecarProxyUrl,\n token: scoped.token,\n expiresAt: scoped.expiresAt.toISOString(),\n status: box.status,\n sandboxId: box.id,\n connectionId,\n })\n }\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":["isRecord","value","credentials","retryAfterMs","res","refreshed","box"]}