import type { AuthContext } from "./support/index.js"; import type { Run } from "./wire/run.js"; import { type IdentityRelay } from "./identity_relay.js"; import type { ByoInferenceProvider } from "../contract.js"; import { type BrowserBackend } from "./browser_session.js"; import { type DesktopDriver, type GuestDesktopConfig } from "./desktop_driver.js"; import { type GuestBrowserConfig } from "./browser_session_backend.js"; import { type CaptureBackend } from "./screen_capture.js"; import { type CaptureConfig } from "./screen_capture_backend.js"; import { type ProgramWorkerDeps } from "./program_worker.js"; /** Constructed primitives the pure assembly consumes (real ones in main(), fakes in tests). The * runner holds NO platform credential — only the per-run control-plane handle (run token + broker * URL); everything else is reached through the Runner Control API (the Runner Credential Broker model). */ export interface WorkerRuntime { /** Stable worker identity stamped onto the lease (task ARN / hostname / run-derived). */ workerId: string; /** Sandbox workspace root for filesystem/shell/git tools — and the working directory + HOME for * author code (docs/WORKSPACE_PERSISTENCE.md I1). */ workspaceRoot: string; /** Where the program artifact is extracted. Deliberately OUTSIDE {@link workspaceRoot} (I2): a * bundle inside the workspace rides into every pre-sleep snapshot and, because each run's dir is * uniquely named, accumulates there forever. */ programRoot: string; /** This run's durable workspace directory — its (workflow, environment) scope, ALREADY RESOLVED by * the daemon (PERSIST_SCOPE_DIR). Set only by a SELF-HOSTED runner, which owns a disk that * outlives a run. Present ⇒ persistence is a plain directory tree here (never our S3: a * self-hosted workspace is the customer's data on the customer's disk). Absent ⇒ the hosted lane, * which has no disk outliving the VM and persists through the broker. The daemon resolves the * scope because it owns the disk and, under container isolation, binds this exact dir as a mount. * See docs/WORKSPACE_PERSISTENCE.md I3. */ persistScopeDir?: string; /** The run this worker task is executing (RUN_ID) — binds the Runner Control API client. */ runId: string; /** The org's BYO inference providers (claim-delivered, BOARDWALK_BYO_PROVIDERS) for the * runner-direct model path (D7). Empty/omitted ⇒ every model call goes through the broker. */ byoProviders?: readonly ByoInferenceProvider[]; /** Broker control-plane handle (run token + base URL), injected by the dispatcher as * BOARDWALK_CONTROL_PLANE_URL + BOARDWALK_RUN_TOKEN. The runner's ONLY broker credential. * `apiToken` is the run's separate public-API bearer (was BOARDWALK_API_KEY) — served to the * program on demand via `runtime.apiToken()`, never placed back into process.env. */ controlPlane: { baseUrl: string; runToken: string; apiToken?: string; }; /** vCPUs provisioned for this task (the dispatcher's resolved machine size ÷ 1024 cpu units). Runtime * is billed per vCPU-SECOND, so the RuntimeFlusher scales wall-clock by this. Defaults to 1. */ vcpus: number; /** The in-guest identity relay, present ONLY on the snapshot-based microVM substrate (relay-mode * boot). When set, the worker suspends by FREEZING — the FreezeCoordinator parks seams over this * relay's suspend/wake channel — the whole VM freezes and the wake resolves seams in place. */ freezeRelay?: IdentityRelay; /** The browser tier's process backend, present ONLY when the runner IMAGE ships the browser stack * (Chromium + a pre-installed Playwright MCP + an X display; gated by BOARDWALK_BROWSER_TIER). * When absent, `computer.openBrowser()` fails with a clear "not available on this runner image". */ browserBackend?: BrowserBackend; /** Screen-capture backend (session recording + live-view frames), present ONLY when the runner IMAGE * ships the desktop stack (ffmpeg + an X display) and recording isn't disabled. Absent ⇒ no capture. */ captureBackend?: CaptureBackend; /** The desktop tier's OS driver (screenshot + raw-coordinate input), present ONLY when the image * declares the tier (BOARDWALK_DESKTOP_TIER=1) on a supported OS. Absent ⇒ `computer.openDesktop()` * fails with a clear "not available on this runner". */ desktopDriver?: DesktopDriver; /** Per-OS permission check run at session open (macOS only today). Absent ⇒ nothing to check. */ desktopPreflight?: () => Promise; /** Path for the on-screen run-log mirror an xterm in the ambient desktop tails (BOARDWALK_RUN_LOG_FILE, * set by the desktop guest image). Resolved by `main` from the trusted platform BOOT env so a run's * author `meta.env` can't repoint it; absent off the desktop tier ⇒ no local sink. */ runLogFilePath?: string; } /** Durable events are deferred (durable event storage) — live fan-out via the broker only. */ /** Synthesize the run's AuthContext. The run was already authorized at trigger time; the * worker acts on the org's behalf, so it carries the org + an owner role. Tool-level * boundaries (the broker's server-side manifest allowlist) are the real guard. * * source='workflow' (NOT 'session_jwt'): the program must never perform SESSION_JWT_ONLY * credential mutations, so a tool that ever exposed such a service is denied by * construction regardless of the owner role. */ export declare function workerAuthContext(run: Run): AuthContext; /** * Sequencer for per-call token-metering identifiers, `:::`. * * The worker's `meterUsage` hook fires once per model TURN (the engine's leaf loop reports usage * after every model call), but the display aggregate is idempotent on this identifier * (UsageEventRepository.recordAndAggregate → `onConflictDoNothing`). A key that didn't vary per call * (it was `::`) collapsed every turn after a leaf's first into a dropped * duplicate — so `runs.tokens_in/out` reflected only each leaf's FIRST turn and under-reported * multi-turn leaves by orders of magnitude, even as the BudgetMeter (which accumulates every turn) * still tripped on the real spend. Bumping a per-session counter gives every turn its own idempotent * event. Restart safety rides the session id (a fresh `meteringSessionId` per worker session) — a * restart genuinely re-spends inference, so it must re-meter rather than dedupe against a prior run. */ export declare function tokenMeterIdentifiers(runId: string, sessionId: string): (leafIndex: number) => string; /** Pure wiring: build the full ProgramWorkerDeps from the control-plane handle. The runner reaches * every privileged seam through the broker over its run token — no database, cache, billing, or model creds. */ export declare function assembleWorkerDeps(runtime: WorkerRuntime): ProgramWorkerDeps; /** * The per-run platform values the dispatcher injects as container env. They * are captured into private worker state at bootstrap and DELETED from `process.env` before any user * program / agent leaf / subprocess can run — the run token + API token are credentials, and nothing * untrusted run code touches should inherit them (the run env/credential rules). The user owns the rest * of `process.env` outright. */ export declare const PLATFORM_ENV_KEYS: readonly ["RUN_ID", "BOARDWALK_CONTROL_PLANE_URL", "BOARDWALK_RUN_TOKEN", "BOARDWALK_API_KEY", "BOARDWALK_TASK_CPU_UNITS"]; /** The captured platform context, shaped to feed {@link assembleWorkerDeps}. */ export interface PlatformContext { runId: string; controlPlane: { baseUrl: string; runToken: string; apiToken?: string; }; vcpus: number; } /** The public-API origin a run uses for raw API / MCP / CLI calls. The broker (Runner Control API) * shares an origin with the public API, so the program appends `/v1` or * `/mcp/v1`. Falls back to the broker URL unchanged if it can't be parsed. */ export declare function publicApiOrigin(controlPlaneBaseUrl: string): string; /** * Read the dispatcher-injected platform context from `env`, then DELETE every {@link PLATFORM_ENV_KEYS} * key from it so the user program, the agent leaf, and any subprocess inherit none of them. Mutates * `env` (called once on `process.env` at the very top of `main`, before any run code can read it). */ export declare function capturePlatformContext(env: NodeJS.ProcessEnv): PlatformContext; /** * The platform-owned config the worker reads for ITSELF: the browser/desktop tier, screen capture, and * the run's sandbox roots / worker id / run-log mirror. All resolved from the passed env — which `main` * captures from the trusted BOOT env (image-baked `/etc/bwimage.env`) BEFORE the identity relay overlays * the run's author env onto process.env. Consumers use these TYPED fields, never process.env, so a * workflow author's `meta.env` can't shadow platform behavior while the author still owns process.env * outright (docs/RUN_ENV_AND_CREDS.md). Distinct from {@link capturePlatformContext}, which resolves the * per-run CREDENTIALS the relay injects (read post-overlay, then scrubbed). */ export interface PlatformConfig { /** Browser/desktop tier backend config, or null when the image ships no browser stack. */ browser: GuestBrowserConfig | null; /** Screen-capture config, or null when the image ships no desktop stack / recording is off. */ capture: CaptureConfig | null; /** Desktop-tier config, or null when the image doesn't declare the tier / the OS is unsupported. */ desktop: GuestDesktopConfig | null; /** Stable worker id (WORKER_ID); absent ⇒ the caller derives one from the run id. */ workerId?: string; /** Sandbox workspace root (WORKSPACE_ROOT), default `/workspace`. */ workspaceRoot: string; /** Program-extraction root (PROGRAM_ROOT), default `/bw-programs`. */ programRoot: string; /** Self-hosted durable-workspace scope (PERSIST_SCOPE_DIR); set by the daemon only. */ persistScopeDir?: string; /** Ambient-desktop run-log mirror path (BOARDWALK_RUN_LOG_FILE); set by the desktop image only. */ runLogFilePath?: string; } /** * Resolve {@link PlatformConfig} from the trusted BOOT env. This is the ONE place env is read for the * worker's own platform config — every other module consumes the typed result — so platform behavior * never depends on the author-mutable process.env. Pure (unit-tested). MUST be called on the boot-env * snapshot taken BEFORE the identity relay overlays the author env (see `main`). */ export declare function capturePlatformConfig(bootEnv: NodeJS.ProcessEnv, platform?: NodeJS.Platform): PlatformConfig; export declare function main(): Promise;