[{"data":1,"prerenderedAt":604},["ShallowReactive",2],{"navigation_docs":3,"-guide-architecture-execution-boundary":168,"-guide-architecture-execution-boundary-surround":601},[4,146],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":36},"Guide","i-lucide-book-open","\u002Fguide","1.guide",[10,14,37,55,59,63,67,71,75,79,83,87,109,134,138,142],{"title":11,"path":12,"stem":13},"What is Agent Zero?","\u002Fguide\u002Fintroduction","1.guide\u002F1.introduction",{"title":15,"icon":16,"path":17,"stem":18,"children":19,"page":36},"API","i-lucide-plug","\u002Fguide\u002Fapi","1.guide\u002F10.api",[20,24,28,32],{"title":21,"path":22,"stem":23},"API overview","\u002Fguide\u002Fapi\u002Foverview","1.guide\u002F10.api\u002F1.overview",{"title":25,"path":26,"stem":27},"Define endpoints","\u002Fguide\u002Fapi\u002Fdefine-endpoints","1.guide\u002F10.api\u002F2.define-endpoints",{"title":29,"path":30,"stem":31},"Use the API from a client","\u002Fguide\u002Fapi\u002Fuse-from-client","1.guide\u002F10.api\u002F3.use-from-client",{"title":33,"path":34,"stem":35},"Protect endpoints","\u002Fguide\u002Fapi\u002Fprotect-endpoints","1.guide\u002F10.api\u002F4.protect-endpoints",false,{"title":38,"icon":39,"path":40,"stem":41,"children":42,"page":36},"Authentication","i-lucide-lock","\u002Fguide\u002Fauthentication","1.guide\u002F11.authentication",[43,47,51],{"title":44,"path":45,"stem":46},"Authentication overview","\u002Fguide\u002Fauthentication\u002Foverview","1.guide\u002F11.authentication\u002F1.overview",{"title":48,"path":49,"stem":50},"GitHub OAuth","\u002Fguide\u002Fauthentication\u002Foauth","1.guide\u002F11.authentication\u002F2.oauth",{"title":52,"path":53,"stem":54},"Permissions","\u002Fguide\u002Fauthentication\u002Fpermissions","1.guide\u002F11.authentication\u002F3.permissions",{"title":56,"path":57,"stem":58},"Organizations","\u002Fguide\u002Forganizations","1.guide\u002F12.organizations",{"title":60,"path":61,"stem":62},"Frontend","\u002Fguide\u002Ffrontend","1.guide\u002F13.frontend",{"title":64,"path":65,"stem":66},"Mails","\u002Fguide\u002Fmails","1.guide\u002F14.mails",{"title":68,"path":69,"stem":70},"Internationalization","\u002Fguide\u002Finternationalization","1.guide\u002F15.internationalization",{"title":72,"path":73,"stem":74},"Deployment","\u002Fguide\u002Fdeployment","1.guide\u002F16.deployment",{"title":76,"path":77,"stem":78},"Tech stack","\u002Fguide\u002Ftech-stack","1.guide\u002F2.tech-stack",{"title":80,"path":81,"stem":82},"Installation","\u002Fguide\u002Finstallation","1.guide\u002F3.installation",{"title":84,"path":85,"stem":86},"Environment variables","\u002Fguide\u002Fenvironment-variables","1.guide\u002F4.environment-variables",{"title":88,"icon":89,"path":90,"stem":91,"children":92,"page":36},"Codebase","i-lucide-folder-tree","\u002Fguide\u002Fcodebase","1.guide\u002F5.codebase",[93,97,101,105],{"title":94,"path":95,"stem":96},"Codebase structure","\u002Fguide\u002Fcodebase\u002Fstructure","1.guide\u002F5.codebase\u002F1.structure",{"title":98,"path":99,"stem":100},"Dependencies","\u002Fguide\u002Fcodebase\u002Fdependencies","1.guide\u002F5.codebase\u002F2.dependencies",{"title":102,"path":103,"stem":104},"Formatting and linting","\u002Fguide\u002Fcodebase\u002Fformatting-linting","1.guide\u002F5.codebase\u002F3.formatting-linting",{"title":106,"path":107,"stem":108},"Agent Skills","\u002Fguide\u002Fcodebase\u002Fagent-skills","1.guide\u002F5.codebase\u002F4.agent-skills",{"title":110,"icon":111,"path":112,"stem":113,"children":114,"page":36},"Architecture","i-lucide-layers","\u002Fguide\u002Farchitecture","1.guide\u002F6.architecture",[115,118,122,126,130],{"title":110,"path":116,"stem":117},"\u002Fguide\u002Farchitecture\u002Foverview","1.guide\u002F6.architecture\u002F1.overview",{"title":119,"path":120,"stem":121},"State machine","\u002Fguide\u002Farchitecture\u002Fstate-machine","1.guide\u002F6.architecture\u002F2.state-machine",{"title":123,"path":124,"stem":125},"Execution boundary","\u002Fguide\u002Farchitecture\u002Fexecution-boundary","1.guide\u002F6.architecture\u002F3.execution-boundary",{"title":127,"path":128,"stem":129},"Issue-to-PR workflow","\u002Fguide\u002Farchitecture\u002Fissue-to-pr","1.guide\u002F6.architecture\u002F4.issue-to-pr",{"title":131,"path":132,"stem":133},"Adding a capability","\u002Fguide\u002Farchitecture\u002Fadding-a-capability","1.guide\u002F6.architecture\u002F5.adding-a-capability",{"title":135,"path":136,"stem":137},"Repository policy","\u002Fguide\u002Fconfiguration","1.guide\u002F7.configuration",{"title":139,"path":140,"stem":141},"Safety model","\u002Fguide\u002Fsafety","1.guide\u002F8.safety",{"title":143,"path":144,"stem":145},"Database","\u002Fguide\u002Fdatabase","1.guide\u002F9.database",{"title":147,"icon":148,"path":149,"stem":150,"children":151,"page":36},"Reference","i-lucide-book-marked","\u002Freference","2.reference",[152,156,160,164],{"title":153,"path":154,"stem":155},"CLI","\u002Freference\u002Fcli","2.reference\u002F1.cli",{"title":157,"path":158,"stem":159},"Model providers","\u002Freference\u002Fmodel-providers","2.reference\u002F2.model-providers",{"title":161,"path":162,"stem":163},"Source-control providers","\u002Freference\u002Fsource-control-providers","2.reference\u002F3.source-control-providers",{"title":165,"path":166,"stem":167},"Sandbox providers","\u002Freference\u002Fsandbox-providers","2.reference\u002F4.sandbox-providers",{"id":169,"title":123,"body":170,"description":591,"extension":595,"links":596,"meta":597,"navigation":598,"path":124,"seo":599,"stem":125,"__hash__":600},"docs\u002F1.guide\u002F6.architecture\u002F3.execution-boundary.md",{"type":171,"value":172,"toc":590},"minimark",[173,177,186,196,214,217,230,248,263,285,348,446,507,575],[174,175,123],"h2",{"id":176},"execution-boundary",[178,179,180,181,185],"p",{},"Only ",[182,183,184],"code",{},"packages\u002Frunner"," may execute commands or mutate a target repository at runtime. The boundary is responsible for validating working directories, arguments, timeouts, output limits, and execution mode. A transport handler, source-control adapter, model provider, or state transition must request runner work through typed contracts rather than invoking a shell directly.",[178,187,188,191,192,195],{},[182,189,190],{},"observe"," is the default mode. It can inspect and report but cannot write. Enabling ",[182,193,194],{},"fix"," requires both an explicit mode and repository policy permission.",[178,197,198,201,202,205,206,209,210,213],{},[182,199,200],{},"RepositoryBoundary"," holds the filesystem and git behavior shared by every runner; subclasses decide only how a repository command is executed. ",[182,203,204],{},"LocalRunner"," runs it on the host, ",[182,207,208],{},"ContainerRunner"," runs it in an ephemeral sandbox. Both report a ",[182,211,212],{},"RunnerDescription"," that is recorded in evidence, so a claim of isolated verification is auditable rather than assumed.",[178,215,216],{},"Git inspection runs in the trusting process because its argv is fixed by the runner package. Repository-supplied commands are the untrusted ones, and those are what isolation moves into a sandbox.",[178,218,219,222,223,226,227,229],{},[182,220,221],{},"createRunner"," and ",[182,224,225],{},"runnerOptionsFromPolicy"," are the only mapping from policy to a concrete boundary. ",[182,228,225],{}," declares the policy fields it needs structurally, so the runner does not depend on the configuration package. Composition roots call both; nothing else constructs a runner.",[178,231,232,233,236,237,239,240,243,244,247],{},"Hosted sandboxes follow the same rule. ",[182,234,235],{},"RunnerPool"," lives in ",[182,238,184],{},", accepts credential-free ",[182,241,242],{},"SandboxRequest"," values, enforces global\u002Frepository quotas and lease ceilings before provisioning, and returns only a ",[182,245,246],{},"Runner",". Provider credentials are constructor state of a vendor adapter and never enter a request, lease snapshot, agent state, or log. A composition root may schedule and release a lease, but it cannot execute a command itself.",[178,249,250,251,254,255,258,259,262],{},"Model transports follow the same adapter rule. ",[182,252,253],{},"packages\u002Fmodels"," owns the AI SDK integrations for OpenAI, Anthropic, Google, AI Gateway, and OpenAI-compatible endpoints behind one ",[182,256,257],{},"ModelProvider"," contract. Composition roots pass the validated provider policy; credentials come only from fixed provider-specific environment variables, and a custom endpoint can only come from the operator-owned ",[182,260,261],{},"AGENT_ZERO_MODEL_BASE_URL"," environment variable. The agent runtime sees neither SDK objects nor credentials, and all adapters share one structured-output, usage-accounting, timeout, and error-redaction path.",[178,264,265,266,222,269,272,273,276,277,280,281,284],{},"Two of those transports are subscription-based: ",[182,267,268],{},"claude-code",[182,270,271],{},"codex-cli"," drive a vendor CLI that is already logged in on the host, so ",[182,274,275],{},"modelProviderCredentialKind"," reports ",[182,278,279],{},"subscription"," and there is no credential for a composition root to supply, redact, or persist. They are the only transports whose SDK spawns a subprocess, which is why three things hold: each stays inert unless its operator flag is exactly ",[182,282,283],{},"true",", the vendor SDK is imported lazily so an unused transport costs nothing, and the CLI is configured with its own tools disabled (Claude Code) or read-only with approvals off (Codex) so it cannot read outside the supplied context or edit a checkout behind the runner boundary.",[178,286,287,289,290,293,294,296,297,300,301,304,305,308,309,312,313,316,317,222,320,323,324,326,327,330,331,334,335,337,338,296,341,343,344,347],{},[182,288,253],{}," still contains no ",[182,291,292],{},"child_process"," import of its own — it never spawns anything itself, matching every other package outside ",[182,295,184],{},". ",[182,298,299],{},"subscriptionProbeCommand"," returns the liveness command as a string for ",[182,302,303],{},"zero doctor"," to run through the runner like every other command, and the CLI process behind a live ",[182,306,307],{},"decide()"," call is spawned the same way: ",[182,310,311],{},"modelFromEnvironment"," takes an optional ",[182,314,315],{},"ClaudeCodeProcessSpawner",", and ",[182,318,319],{},"packages\u002Fcli",[182,321,322],{},"packages\u002Fapi"," supply one backed by ",[182,325,184],{},"'s ",[182,328,329],{},"spawnManagedProcess"," — the streaming counterpart to ",[182,332,333],{},"execFileProcessRunner",", for a caller that needs a live duplex process instead of one buffered result. Wired that way, the ",[182,336,268],{}," transport's CLI process is spawned through the same boundary as every repository check, not through the vendor SDK's own default ",[182,339,340],{},"child_process.spawn",[182,342,271],{}," cannot be closed the same way: ",[182,345,346],{},"ai-sdk-provider-codex-cli"," exposes no equivalent spawn hook, so that transport's process is spawned by the vendor SDK directly regardless of what a composition root supplies — a vendor limitation, not a choice this codebase makes. The same read-only, no-MCP, approvals-off configuration is still the containment for that one transport.",[178,349,350,351,354,355,358,359,222,361,363,364,367,368,370,371,373,374,377,378,380,381,377,384,326,386,388,389,391,392,222,395,398,399,402,403,406,407,410,411,414,415,418,419,423,424,427,428,430,431,434,435,438,439,442,443,445],{},"Both composition roots additionally read ",[182,352,353],{},"config.runner.isolation"," to decide how they build that spawner, in a local module (",[182,356,357],{},"subscription-isolation.ts",", duplicated in ",[182,360,319],{},[182,362,322],{}," rather than pulled into a shared package — this decision is composition-root-specific, needing both ",[182,365,366],{},"AgentZeroConfig"," and an operator environment variable, and neither ",[182,369,253],{}," nor ",[182,372,184],{}," should own it). On ",[182,375,376],{},"local"," isolation it wires ",[182,379,329],{}," directly, spawning the CLI on the host, same as before. On ",[182,382,383],{},"container",[182,385,329],{},[182,387,383],{}," option instead: ",[182,390,184],{}," exports ",[182,393,394],{},"ManagedProcessContainerOptions",[182,396,397],{},"containerizedProcessArgv",", a ",[182,400,401],{},"docker","\u002F",[182,404,405],{},"podman run"," invocation deliberately distinct from ",[182,408,409],{},"ContainerRunner.engineArguments()"," — no repository-checkout volume (the CLI never touches one) and no ",[182,412,413],{},"--network"," tied to ",[182,416,417],{},"permissions.network"," (that policy contains an ",[420,421,422],"em",{},"untrusted checkout's"," commands, not Agent Zero's own necessary calls to the vendor API). When container isolation is declared but no CLI container image is configured (",[182,425,426],{},"AGENT_ZERO_CLAUDE_CODE_CONTAINER_IMAGE","), the composition root refuses the transport rather than silently falling back to an unisolated host spawn. The refusal is reported to ",[182,429,311],{}," as a ",[182,432,433],{},"subscriptionRefusalReason"," — a synchronous throw from Agent Zero's own code the moment the transport is asked to build a model, never touching the vendor SDK — rather than by turning the enable flag off: the flag also gates fallback selection, so disabling it would have reported the transport as never configured at all and skipped a configured ",[182,436,437],{},"AGENT_ZERO_MODEL_FALLBACK_PROVIDER"," entirely, turning a run that could have degraded into one that fails outright. ",[182,440,441],{},"environmentForModel"," (used only by ",[182,444,303],{},"'s diagnostics, which want a plain \"not ready\" signal rather than this nuance) is the one place that still disables the flag.",[178,447,448,449,451,452,316,454,457,458,402,461,402,464,402,467,470,471,473,474,476,477,457,480,483,484,486,487,490,491,494,495,497,498,500,501,503,504,506],{},"A ",[182,450,235],{}," lease is a separate isolation mechanism from ",[182,453,353],{},[182,455,456],{},"SandboxProvider"," (",[182,459,460],{},"vitehub",[182,462,463],{},"cloudflare",[182,465,466],{},"vercel",[182,468,469],{},"custom",") returns only the ordinary ",[182,472,246],{}," contract — bounded command execution, never a live process handle — so there is no ",[182,475,268],{}," spawner that could route the CLI's duplex stream through the same boundary a lease already gives repository commands. ",[182,478,479],{},"runTask",[182,481,482],{},"packages\u002Fapi\u002Fsrc\u002Foperations.ts",") refuses ",[182,485,268],{}," outright whenever ",[182,488,489],{},"options.runnerPool"," is configured, regardless of ",[182,492,493],{},"runner.isolation",", the same way it refuses container isolation without an image and for the same reason: a ",[182,496,433],{},", not a disabled flag, so a configured fallback still gets its turn rather than the run failing outright. ",[182,499,319],{}," has no ",[182,502,235],{}," concept at all, so this check lives only in ",[182,505,322],{},".",[178,508,509,510,513,514,517,518,521,522,525,526,529,530,533,534,537,538,541,542,545,546,549,550,553,554,557,558,560,561,563,564,567,568,571,572,574],{},"Getting an authenticated session into that container took three fixes beyond the mount, each verified against a real ",[182,511,512],{},"docker run"," rather than assumed: the vendor SDK resolves the CLI to an absolute host path (its bundled native binary, or ",[182,515,516],{},"AGENT_ZERO_CLAUDE_CODE_PATH",") that does not exist in the container, so the spawner substitutes a bare, image-relative executable name instead (",[182,519,520],{},"AGENT_ZERO_CLAUDE_CODE_CONTAINER_EXECUTABLE",", default ",[182,523,524],{},"claude",") and passes the vendor's own args through unchanged (they carry no host paths). The CLI's session spans two host locations that are not nested — ",[182,527,528],{},"~\u002F.claude\u002F"," and a sibling file ",[182,531,532],{},"~\u002F.claude.json"," — and Docker refuses to bind-mount a file inside an already-",[182,535,536],{},":ro"," directory mount (an OCI runtime restriction, not a policy choice here), so both are mounted as siblings under one synthetic directory with the container's ",[182,539,540],{},"$HOME"," pointed at it, letting the CLI's own default resolution find both without a ",[182,543,544],{},"CLAUDE_CONFIG_DIR"," override. And the container runs as the host's UID:GID rather than ",[182,547,548],{},"root",": the mounted credential file's ",[182,551,552],{},"0600"," mode plus ",[182,555,556],{},"--cap-drop ALL"," (which strips even ",[182,559,548],{},"'s permission-bypass capability inside the container) means only a matching UID can satisfy the CLI's own ownership check on it. ",[182,562,397],{}," also forwards ",[182,565,566],{},"env"," as ",[182,569,570],{},"-e KEY=VALUE"," flags now — the container engine's own process env configures its client, not the process it starts, so ",[182,573,540],{}," and the vendor SDK's other env entries would otherwise never reach the code running inside.",[178,576,577,578,581,582,585,586,589],{},"A subscription transport also owns the one failure that repairs itself. A spent usage window is not a permanent error, so ",[182,579,580],{},"ResumingSubscriptionProvider"," waits for the reset the transport reported and retries, resuming the interrupted session through a ",[182,583,584],{},"SubscriptionSession"," handle shared by the model factory that writes to it and the error translator that reads it. The wait is bounded by a cumulative budget and an attempt count, and only ever on a reset the transport actually stated, so no run blocks for a duration nobody chose. It sits inside the API-key fallback rather than outside it: the subscription is already paid for. Both wrappers degrade on ",[182,587,588],{},"SubscriptionProviderUnavailableError"," and nothing else, so a model that merely returned an unusable decision never causes a transport to be swapped or a run to sleep.",{"title":591,"searchDepth":592,"depth":592,"links":593},"",2,[594],{"id":176,"depth":592,"text":123},"md",null,{},true,{"title":123,"description":591},"-GDCJcXmpjY-Y-5DgnRBg1jl8zUgG7lxjiQSYrjUUjw",[602,603],{"title":119,"path":120,"stem":121,"description":591,"children":-1},{"title":127,"path":128,"stem":129,"description":591,"children":-1},1787482154357]