From db4ec61fc8d31e228707fa4a13fe72c3b06d7124 Mon Sep 17 00:00:00 2001 From: saladday <1203511142@qq.com> Date: Tue, 29 Sep 2026 16:32:49 +0800 Subject: [PATCH 1/5] docs: slim CONTRIBUTING and move component rules to their owners CONTRIBUTING keeps only repository-wide rules. Component rules move to their canonical owners (runtime-protocol, environments, harness docs, Sandbox Provider guide, maintainers, service and adapter READMEs, and the new services/agents-api/IMPLEMENTATION.md). Sandbox Provider and Harness guides become numbered start-to-finish paths with verified registration files, the five required provider operations, engine selection and the Turn cancellation owner. --- AGENTS.md | 2 + CONTRIBUTING.md | 4020 +---------------- apps/web/README.md | 119 + contracts/agents-api/README.md | 11 +- contracts/agents-api/admin-api.md | 7 + contracts/agents-api/core-errors.md | 16 + contracts/agents-api/core-metrics.md | 20 + contracts/agents-api/environment-templates.md | 50 +- contracts/agents-api/environments.md | 534 ++- contracts/agents-api/harness-onboarding.md | 277 +- contracts/agents-api/harness-selection.md | 16 + contracts/agents-api/harnesses.md | 127 +- contracts/agents-api/node-host-history.md | 10 + contracts/agents-api/runtime-observability.md | 37 + contracts/agents-api/sandbox-deployment.md | 13 + contracts/agents-api/session-diagnostics.md | 12 + contracts/agents-api/structured-output.md | 37 + contracts/agents-api/tool-search.md | 24 + contracts/agents-api/write-audit.md | 12 + docs/development.md | 87 +- docs/maintainers.md | 506 +++ docs/runtime-protocol.md | 281 ++ docs/sandbox-provider.md | 193 +- packages/claude-sdk-adapter/README.md | 2 +- services/agents-api/IMPLEMENTATION.md | 1693 +++++++ services/agents-api/README.md | 76 +- services/agents-api/deploy/claude/README.md | 76 +- services/agents-api/deploy/mcode/README.md | 63 +- .../agents-api/tools/e2b-provider/README.md | 24 + 29 files changed, 4204 insertions(+), 4141 deletions(-) create mode 100644 services/agents-api/IMPLEMENTATION.md diff --git a/AGENTS.md b/AGENTS.md index 0f10cc689..25c344b6f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,6 +6,8 @@ Use [docs/development.md](docs/development.md) for checkout and component guidan - Keep Core, Runtime, Harness and Sandbox Provider ownership separate through the shared protocols; see [decoupling](CONTRIBUTING.md#decoupling-principle). +- To add a Harness or Sandbox Provider, start at + [Choose an extension boundary](docs/development.md#choose-an-extension-boundary). - Identify every API route's caller and credential in [the API index](docs/api/README.md). - Run `make sqlc-generate` after query changes and `make openapi` after handler changes; review and commit the generated contracts with their source changes. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c5c17cff8..6ac7013ed 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,9 +1,12 @@ # Contributing to OpenAgentCore Start with [Develop OpenAgentCore](docs/development.md) for checkout, toolchains, -component locations and focused checks. This guide owns repository-wide workflow -and architecture rules. Detailed protocols and native implementation rules have -one canonical owner, listed below; update that owner when changing its contract. +the repository map and focused checks. This guide owns repository-wide rules only: +documentation ownership, the repository boundary, the decoupling principle, +workflow and review, required checks and naming. + +Every other rule has one canonical owner, listed below. Change that owner, not a +copy, when a contract changes. ## Documentation ownership @@ -11,3823 +14,284 @@ one canonical owner, listed below; update that owner when changing its contract. | --- | --- | | User concepts and authority | [Design principles](docs/design-principles.md) | | Architecture overview and diagrams (a map that links to the owners below) | [Architecture](docs/architecture.md) | +| Developer setup, repository map and extension boundaries | [Develop OpenAgentCore](docs/development.md) | | API callers, credentials and route inventory | [API index](docs/api/README.md) | | Public wire types and qualified behavior | [Agents API contracts](contracts/agents-api/README.md), [pinned upstream](contracts/agents-api/upstream.json), and linked operation contracts | -| Runtime messages, receipts and failure ownership | [Core–Runtime protocol](docs/runtime-protocol.md) and `internal/agentdaemon/proto` | -| Harness interfaces and onboarding | [Harness onboarding](contracts/agents-api/harness-onboarding.md) and `agent/harness.go` | -| Provider interfaces and onboarding | [Sandbox Provider guide](docs/sandbox-provider.md) and `sandbox/sandbox_provider.go` | +| Core service implementation constraints | [Agents API implementation constraints](services/agents-api/IMPLEMENTATION.md) and [service README](services/agents-api/README.md) | +| Runtime messages, Executor/Turn lifetimes, receipts and failure ownership | [Core–Runtime protocol](docs/runtime-protocol.md) and `internal/agentdaemon/proto` | +| Environment ownership and capability preparation (Skills, Plugins, MCP, `packages.system`) | [Environments](contracts/agents-api/environments.md) | +| Adding a Harness (steps) | [Harness onboarding](contracts/agents-api/harness-onboarding.md) and `apps/parsar-daemon/internal/agent/harness.go` | +| Harness qualification and acceptance | [Harness integration](contracts/agents-api/harnesses.md) | +| Harness selection and Agent defaults | [Harness selection](contracts/agents-api/harness-selection.md) | +| Adding a Sandbox Provider | [Sandbox Provider guide](docs/sandbox-provider.md) and `services/agents-api/internal/sandbox/sandbox_provider.go` | +| Provider selection, sandbox deployment and E2B setup | [Sandbox deployment](contracts/agents-api/sandbox-deployment.md) | +| Hosted sandbox nodes | [Hosted sandbox manager](services/agents-api/HOSTED-SANDBOX-MANAGER.md) | | Claude private bridge and Runtime artifact | [Claude SDK adapter](packages/claude-sdk-adapter/README.md) | -| Operator installation and configuration | [Installation](docs/getting-started/install.md), [configuration](docs/configuration.md), [operations](docs/getting-started/operations.md) | +| MiniMax Code and Claude Runtime adapter rules | [MiniMax Code Runtime](services/agents-api/deploy/mcode/README.md), [Claude Runtime](services/agents-api/deploy/claude/README.md) | +| CI, distribution builds, installer internals and release publication | [Maintainer guide](docs/maintainers.md) | +| Operator installation and configuration | [Installation](docs/getting-started/install.md), [installation options](docs/getting-started/install-options.md), [configuration](docs/configuration.md), [operations](docs/getting-started/operations.md) | +| Core Web console server and sign-in | [Web README](apps/web/README.md) | | Web components, interaction and visual rules | [Web design](apps/web/DESIGN.md) and [Web architecture](docs/web/architecture.md) | | Documentation website generation | [Docs app](apps/docs/README.md) | -English Markdown in `docs/`, component guides and `contracts/` is authored source. -The docs app generates guide pages and API references from these sources. Never -edit generated copies to establish a different rule. Keep documentation and code -comments in English; the root project README is provided in English and Chinese, -and user-facing internationalized product copy may be bilingual. +### Documentation rules -Historical acceptance records retain their original revisions and limits. They -are evidence, not current installation instructions or authority to restore a -retired implementation. Keep task chronology and one-off rollout reports out of -new contributor rules. +- English Markdown in `docs/`, component guides and `contracts/` is authored + source. The docs app generates guide pages and API references from it. Never + edit generated copies to establish a different rule. +- Keep documentation and code comments in English. The root README is provided + in English and Chinese; user-facing product copy may be bilingual. +- Historical acceptance records keep their original revisions and limits. They + are evidence, not current instructions or authority to restore a retired + implementation. Keep task chronology and rollout reports out of contributor rules. +- Keep current integration guidance separate from historical qualification evidence. ## Repository boundary This repository is the standalone execution substrate copied from Parsar at the -revision in `provenance/source.json`. Keep the API, its migrations, protocol, -execution daemon, runtime adapters, shared execution packages, the standalone Core -Web console and build/test tools here. Product users, workspaces, model catalogs, -business assets, the Parsar product Web, product API and product migrations remain -in Parsar. Do not import `server/`, `apps/parsar/`, product CLI/plugin packages or -their deployment stack. +revision in `provenance/source.json`. It holds the API and its migrations, the +Runtime protocol and daemon, Harness adapters, shared execution packages, the +standalone Core Web console and build/test tools. + +Product users, workspaces, model catalogs, business assets, the Parsar product +Web, product API and product migrations remain in Parsar. Do not import +`server/`, `apps/parsar/`, product CLI/plugin packages or their deployment stack. + +Preserve copied Runtime and protocol behavior. Existing Go import paths stay +unchanged and do not require fetching the original repository. The source +snapshot and per-file hashes are an audit trail; future Core development need +not preserve them. Do not automatically sync or delete the original repository's Core. + +### Optional application example + +`example/parsar/` is an optional, independently started Agent workbench. Its +[README](example/parsar/README.md) owns its product behavior. The boundary rules are: + +- It calls only public `/v1` APIs. Its Project key stays server-side; it never + holds a Core key or issues machine credentials. Core-only credential issuance + stays in the operator console. +- It may reuse product UI and keep a small product-owned SQLite database (Node's + built-in module, Node 22.13+), outside the checkout and isolated by Core origin + and Project key fingerprint. Provider keys never reach the browser. +- Core owns Skills and all execution and history state. The example stores only + Session references and pending creation requests with stable idempotency keys. +- Product resources use `/app/` and never become Core API or database conventions. +- It is excluded from Core distributions and cannot become a service dependency. -`example/parsar/` is an optional, independently started Agent workbench, -authorized to reuse the product UI and maintain a small product-owned SQLite database. -Named Provider groups and their models, anonymous HTTP MCP configurations, runtime profiles and reusable Agents -belong to that database. An Agent contains model, harness, instructions, Skills -and MCP bindings; it excludes the runtime. Starting a Session selects a runtime -and passes an inline Agent/configuration snapshot to the public Core API. One -Agent may have many independent Sessions. Existing Sessions keep their original -configuration and history. Hosted Sessions have independent workspaces; native -Sessions use the selected host directory, so selecting the same path shares files. -There is no task layer or automatic workspace coordination. -Providers are explicit catalog groups without default groups. Each model references -one Provider. The example can fetch an OpenAI-shaped model list from a user-entered -Provider Base URL using that Provider's key, then save checked/custom models and -the Provider in one SQLite transaction. Editing reads the Provider revision and -its models as one snapshot; individual model changes advance the owning Provider -revision. Provider keys stay in the local restricted -SQLite file and are omitted from every browser response. Discovery uses only the -Provider credential, never the Core Project key, and never follows redirects. -Catalog import does not reconfigure deployment routing. Self-hosted Session creation -uses the selected Provider's URL/key in the explicit Session model-provider bundle; -Core freezes and delivers it. Pending requests are never serialized to browsers. -Core owns Skills and all execution/history state. SQLite stores Session references -and freezes pending creation requests with stable idempotency keys for retry; -confirmed requests are removed from local storage. Earlier example templates and -instances migrate to Agent configurations, never to fabricated Sessions. -Hosted HTTP MCP bindings become inline environment Plugins; none uses service-origin -MCP. Unsupported harness/runtime combinations fail before execution. The example -calls only public `/v1` APIs, using `OpenAIAgentsClient` for browser resource calls -and a small server adapter for Session creation. Its Project key stays server-side. -Session output uses the public SSE event stream through a non-buffering proxy; -durable history reads reconcile completion and recover missed events. Browser -disconnection aborts the upstream stream, never the running Session. Send latency -measurements are browser-local observations (request return and first nonempty -text delta), not inferred Core execution timings. -Product resources use `/app/` and never become Core API or database conventions. -The example supports native self-hosted runtime profiles with a platform and existing -absolute workspace/capability paths. Create an empty Session first, issue its credential -in Core Web, then run the displayed native install/start command. The example never -holds a Core key or issues machine credentials; Core's public Environment status gates -message sending. Core-only credential issuance remains in the operator console. -Native Skills come from local capability directories: managed Skill references are -rejected explicitly rather than ignored. Bound service-origin MCP is also rejected -for self-hosted Sessions; use local Plugin capability directories. This example supports Codex and Claude Code on user machines; MiniMax Code's -required token-limit configuration is not exposed here. No scheduler or user permissions -are included. SQLite uses -Node's built-in module (Node 22.13+), lives outside the checkout, and is isolated by -Core origin and Project key fingerprint. The example is excluded from Core -distributions and cannot become a service dependency. +## Decoupling principle -Preserve copied runtime and protocol behavior. Existing Go import paths remain unchanged and do not require fetching the original -repository. Installed commands and environment settings use the OpenAgentCore names -documented below. The source snapshot and per-file -hashes provide an audit trail; future Core development need not preserve those -hashes. Do not automatically sync or delete the original repository's Core. +Core orchestrates protocol-defined operations. Sandbox Providers, Runtime +implementations, Harnesses and model providers are replaceable components. +User-owned machines, E2B, Docker and other Environments expose the same +execution protocol. -## Decoupling principle +Two lifetimes stay separate: -Core orchestrates protocol-defined operations. Sandbox providers, Runtime -implementations, harnesses and model providers are replaceable components. -User-owned machines, E2B, Docker and other environments must expose the same -execution protocol. Resource management selects machines, allocates capacity and -creates, bootstraps, renews and reclaims Environments; Runtime initializes files, -tool configuration, packages, setup and capabilities, then executes -work and recovers within an Environment. Keep these contracts and lifetimes -separate: closing a Session executor does not release its allocation, destroy its -Environment or delete its workspace. Reclamation is an explicit resource-manager -operation coordinated with active work. +| Component | Owns | +| --- | --- | +| Resource management (Sandbox Provider) | Selecting machines and capacity; creating, bootstrapping, renewing and reclaiming Environments | +| Runtime | Initializing files, tool configuration, packages and capabilities; executing work and recovering inside an Environment | -Capability preparation uses one Runtime path for managed bundles and self-hosted -local directories. Core resolves configuration, versions -and resources; Runtime uses one parser and `installed.json` snapshot to supply -Skill paths and MCP declarations to adapters. Preparation must complete before -execution; reconnect reuses installed contents, and new Sessions freeze a new -configuration snapshot. The protocol is platform-neutral. Self-hosted daemons run natively on Linux, -Windows and macOS; managed Providers remain Linux-only. The daemon executes with -its launching user's permissions. It does not sandbox tools, files or networks, -including on Linux. Managed isolation belongs to the outer Docker/E2B Environment, -created by Core through a Provider. Harness adapters use bypass execution on all -platforms. Runtime authentication, process cleanup and state consistency remain -required; none constitutes isolation from tools running as the same user. -Installation layout may differ, but Core preparation and execution cannot branch -on operating system or Environment source. Platform support requires native CI builds and automated tests; cross-compilation -alone is insufficient. +Closing a Session Executor does not release its allocation, destroy its +Environment or delete its workspace; see +[Executor and Turn lifetimes](docs/runtime-protocol.md#executor-and-turn-lifetimes). +Capability preparation follows [Environments](contracts/agents-api/environments.md#runtime-capability-preparation). +The Runtime is not a sandbox; see +[Runtime and outer isolation](docs/design-principles.md#runtime-and-outer-isolation). - Keep component boundaries explicit through shared interfaces and versioned protocols. Register implementations behind those interfaces. Adding an implementation must not require a new orchestration path selected by its name. - Core owns durable Session/Turn state and scheduling. Runtime owns local execution resources. Harness adapters translate the common execution contract - into native operations; model and sandbox provider details remain behind their - respective interfaces. + into native operations; model and sandbox provider details stay behind their + interfaces. - Fix shared lifecycle, admission, cancellation, reuse and performance problems - in the common protocol or flow. Do not patch them with harness-, runtime- or - vendor-specific branches in Core. Adapters may implement native differences, - but must preserve the shared operation and event semantics. + in the common protocol or flow, not with Harness-, Runtime- or vendor-specific + branches in Core. Adapters may differ natively but keep shared semantics. - Express compatibility through declared capabilities and validate selected - combinations explicitly. Replaceability does not guarantee that every model, - harness and environment combination is supported. Do not silently substitute - another implementation or give a capability different meanings per vendor. -- Evolve shared contracts and their implementations together, documenting - ownership and validating the same contract across implementations. This rule - does not claim existing implementations already satisfy every target, change - the pinned public API, or authorize unrelated refactors or live Session - configuration switching. - -### Sandbox Provider contract - -The [Sandbox Provider onboarding guide](docs/sandbox-provider.md) owns integration -and operation semantics. Its canonical interface is -[`SandboxProvider`](services/agents-api/internal/sandbox/sandbox_provider.go). -Use that interface for compute lifecycle and bounded bootstrap; optional -interfaces own checkpoint and read-only observation behavior. Construction owns -vendor configuration, and common orchestration selects capabilities rather than -provider names. - -## Core–Runtime integration contract - -The [Core–Runtime protocol](docs/runtime-protocol.md) owns message order, -identities, receipts and failure ownership. Shared types and validators live only -in `internal/agentdaemon/proto`; change both peers together with an exact -wire-version check. Do not add a parallel schema or historical wire fallback. -Hosted and self-hosted peers use the same contract. Change current callers and -implementations together. Historical upgrades remain unsupported; do not add -compatibility layers, aliases or migrations without an explicit upgrade contract. + combinations explicitly. Replaceability does not mean every model, Harness and + Environment combination is supported. Never silently substitute another + implementation or give a capability different meanings per vendor. +- Core preparation and execution never branch on operating system or + Environment source. Platform support requires native CI builds and automated + tests; cross-compilation alone is insufficient. +- Evolve shared contracts and their implementations together, document + ownership and validate the same contract across implementations. This rule + does not claim every implementation already meets every target, change the + pinned public API, or authorize unrelated refactors. + +### Extension contracts + +| Boundary | Canonical guide | Code entry point | +| --- | --- | --- | +| Core–Runtime wire | [Core–Runtime protocol](docs/runtime-protocol.md) | `internal/agentdaemon/proto` | +| Harness | [Harness onboarding](contracts/agents-api/harness-onboarding.md) | `apps/parsar-daemon/internal/agent/harness.go` | +| Sandbox Provider | [Sandbox Provider guide](docs/sandbox-provider.md) | `services/agents-api/internal/sandbox/sandbox_provider.go` | + +Shared wire types and validators live only in `internal/agentdaemon/proto`. +Change both peers together with an exact wire-version check; do not add a +parallel schema or a historical wire fallback. + +### Pre-release policy + +OpenAgentCore is pre-release. Replace superseded internal interfaces and +execution paths cleanly; do not retain version fallbacks, compatibility shims, +aliases or migrations without an explicit upgrade contract. Preserve the pinned +official public protocol, valid data and still-used infrastructure. + +## Workflow and review + +### Before you start + +1. Record the requirements, acceptance criteria and scope. +2. Work in an isolated Git worktree on a feature branch and submit a PR. Do not + edit or commit implementation directly on `main`. +3. Make only the changes that scope needs; keep unrelated refactors separate. + +When documents conflict, apply the latest explicit user decision and update the +affected current guidance. Historical evidence does not override it. + +If requirements are unresolved, object ownership is unclear, or a design would +need parallel compatibility paths, raise the issue with a concrete +recommendation and tradeoffs before implementing it. Continue independent work +meanwhile. Do not silently preserve obsolete private designs. + +Record unrelated findings without automatically starting them. Do not claim a +broader compatibility target is complete from one merged batch. + +### Implementation conventions + +- Search for existing formatters, parsers, validation and error mappers before + adding one. Keep one error mapper per API surface. +- Share frontend formatting and labels in `apps/web/src/lib/`; reuse components + and tokens. +- Split growing files at an existing ownership boundary instead of adding + unrelated responsibilities. +- Use `internal/obs/log` for logs. Keep credentials out of source and logs. +- Require absolute user-supplied working directories. +- Keep test artifacts under `~/.oac/` and build output under + `${OAC_DEV_HOME:-$HOME/.oac}`. Runtime state uses `${OAC_RUNTIME_HOME:-$HOME/.oac}`. +- New or changed routes identify their caller and credential in the + [API index](docs/api/README.md) and link their detailed contract. +- Update the owning guide when architecture, ownership or generated contracts change. + +### Review + +1. After implementation and validation, have a fresh independent subagent review + the complete diff. +2. Give it only the requirements, acceptance criteria, boundaries, repository + path and comparison baseline. Do not give an implementation summary, + self-assessment or earlier findings. Explain these criteria to the user. +3. Fix substantiated in-scope findings, validate, then use another fresh reviewer. +4. If the cycle repeats, reassess design and scope before adding changes. Report + an unresolved blocker instead of broadening the task. -`make check-runtime-contract` is the focused shared-contract entry point. Its -checks also run through `check-go` and `check-agents-api` in the required full gate. -Keep failure, cancellation, disconnect and cleanup assertions in that common -suite; native adapter acceptance remains required for advertised capabilities. - -## Workflow and quality - -Before development, record the requirements, acceptance criteria and scope. Make -only the changes needed for that scope; keep unrelated refactors separate. Work -in an isolated Git worktree on a feature branch and submit a PR. Do not edit or -commit implementation directly on main. - -After implementation and necessary validation, have a fresh independent subagent -review the complete diff. Give it only the requirements, acceptance criteria, -boundaries, repository path and comparison baseline, without an implementation -summary, self-assessment or earlier review findings. Explain those review criteria -to the user. Fix substantiated in-scope findings, validate, then use another fresh -reviewer. If the review/fix cycle repeats, reassess the design and scope before -adding more changes; report an unresolved blocker rather than broadening the task. Do not use `codex exec` as a substitute reviewer. -Search for existing formatters, parsers, validation and error mappers before adding -one. Share frontend formatting and labels in `apps/web/src/lib/`; reuse components -and tokens. Keep one error mapper per API surface. Split growing files at an -existing ownership boundary rather than adding unrelated responsibilities. - -The [API documentation index](docs/api/README.md) lists the three namespaces: -`/v1` for applications (Project API key), `/core/v1` for Core Web's server and -operator scripts (Core key) and `/api/v1` for machine connections (executor credentials issued through `/core/v1` -or claimed using a short-lived Session installation authorization). New or changed routes must identify their caller and -credential there, and link their detailed contract. -Keep current integration guidance separate from historical qualification evidence. - -The Core Web is an administrator console. Web calls only `/core/v1`, with the Core -key held on its server, and never `/v1` or `/api/v1`. Applications use an API key issued inside a Project. One Project owns one execution -tenant and principal; all its keys share assets and permissions while writes retain -individual key provenance. Projects and keys are database-owned, with no static -business keys or configuration synchronization. Revocation affects one key; -archiving a Project revokes all its keys, retaining assets and admitted execution. -Do not add Core users, roles, memberships or cross-Project sharing. Management -provides safe reads, public deletion preconditions, explicit hosted Session archive, -Project and key operations and credential issuance; it cannot copy, execute or edit -arbitrary assets. -Keep administrator target scope separate from caller principals. See -[design principles](docs/design-principles.md) and the -[administrator contract](contracts/agents-api/admin-api.md). - -Administrator writes and their audit record share one PostgreSQL transaction. -Reuse existing resource deletion and serialization code. Cross-Project copying was -removed; only the historical `source:"admin_copy"` provenance read remains, fed by -`admin_resource_owners`. Unknown historical provenance remains unknown. No secrets -or request bodies enter logs. The console's fixed actor label (`console`) is only -an audit display label, never an authorization input. - -Apply the latest explicit user decisions when older documents conflict, and -update the affected current guidance. Historical evidence does not override those -decisions. When requirements remain unresolved, object ownership is unclear, or a -design would need parallel compatibility paths, raise the issue with a concrete recommendation and -tradeoffs before implementing the disputed behavior. Continue independent work -while the decision is pending. Do not silently preserve obsolete private designs. - -Core Web leads with operations: Monitor (Overview, Core metrics, Agent metrics, -Sandbox metrics, Session log), Resources and Platform. Pages use the shared -components in `apps/web/src/components` and the tokens in `apps/web/src/styles`, -described in `apps/web/DESIGN.md`. Keep explanations behind help tips, but keep -errors, warnings and safety notices visible. Browser-derived metrics state their -coverage, keep missing values missing, bound their fan-out and time, report a failed -read as failed and never imply deployment-wide or billing totals. -Sandbox deployment setup, configuration, reset and progress belong to the System -secondary page (`#system?id=sandbox`). Nodes owns node management; Overview and -metrics pages link to these owners instead of repeating their controls or details. -Keep uncommon resource edits and rollout details in dialogs, and avoid repeating -the same information within or across pages. Core responses remain the source of -truth for deployment and connection state. - -Track the selected milestone and its evidence in the active project board when -one is in use. Record unrelated findings without automatically starting them. -Do not claim completion of a broader compatibility target from one merged batch. - -Session creation requires initial input for `none`, and for streaming creation -outside `self_hosted`. Report inline agent protocol errors first, then check these -conditions before creation retry lookup or -resource resolution. The parser remains shared with subsequent message admission; -non-streaming hosted and self-hosted requests may omit input. Do not retain an -idle-none creation compatibility exception. Valid requests retain their documented -local idempotency behavior; clients may use the same request/key with stream=false -to recover a lost creation response. Session metadata updates require a supplied -metadata field, with null/empty clearing it. Validate an empty update before any -resource lookup, after authentication. - -List order parsing distinguishes omission from an explicit empty value. Lists and -single-resource routes ignore unknown query keys; a repeated supported list key -still rejects. The Environment Files list keeps its own path and cursor parsing but -uses the same unknown-key and duplicate-key rules, except that it still rejects -malformed query encoding (such as `%GG` or `;` separators) that the shared lists -drop. Reuse the shared parser and error serializer, preserving the observed Beta, Files and Skills error fields and -per-family limit bounds rather than applying one policy to every resource. Change -page bounds, cursor ownership or parent lookup order only with owned evidence for -that family. Record uncertain range/lookup behavior separately; do not reproduce -observed upstream server failures as compatibility behavior. See -`contracts/agents-api/list-query-semantics.md` for the bounded evidence. - -Every Agents API JSON route reads its body through the shared gate -(`readJSONObject`) before route decoding, validation or lookup. It requires a JSON -Content-Type, applies the route's body limit and rejects invalid UTF-8, malformed -JSON (including unpaired surrogate escapes), repeated keys and non-object roots -with the official messages; an empty body or null becomes `{}`. DELETE, multipart, -Core extension and internal routes keep their own readers. Member names match -exactly: decode request objects with `decodeInputObject`, or check -`inexactMember` before another decoder, so that encoding/json never matches -a case variant to a field. See -`contracts/agents-api/official-semantics-alignment.md#request-body-parsing--september-23`. -Report validation failures with official evidence through the typed field error, -which emits `invalid_request_error` with the observed param and message; keep -other local codes until their official fields are sampled. Every 409 has type -`conflict_error`. Session input conflicts and changed tool results also use code -`conflict_error`; documented Core-only conflicts, such as Idempotency-Key reuse, -sandbox administration and Environment input states, keep their local codes. Agent configuration -(saved create/update and the inline Session agent) uses one path-tracking -validator of the pinned shapes before its parsers and harness admission, which -keep their local codes; do not grow it into a JSON Schema engine. A malformed path -identifier must produce exactly the response of a well-formed missing one on that -route, including invalid bodies, queries and storage availability: resolve it to -the never-assigned maximum UUID and let the missing path run, or reject it -directly only where the lookup is the next check. Request-body references keep -their own errors. An `after` cursor that does not resolve inside its already -resolved parent, malformed ones included, returns that list family's observed -error: the missing-resource 404 on lookup lists, otherwise the typed store cursor -error. Foreign and missing cursors stay identical; see -`contracts/agents-api/list-query-semantics.md`. Reject U+0000 in metadata -explicitly with its `metadata.` param; other stored strings rely on the -PostgreSQL error mapping, so keep each request's writes in one transaction. See -`contracts/agents-api/official-semantics-alignment.md`. - -Serve requests on their canonical path and never redirect. `api.CanonicalPaths` -wraps the complete server handler in both configurations (the daemon ServeMux and -the API router alone), so every route group, middleware, authentication check and -handler sees one path. It starts from the request's own spelling, never a path -re-escaped from its decoded form: invalid bytes are percent-encoded, unreserved -escapes decoded, empty and dot segments resolved with ServeMux semantics, the -trailing slash kept, and other escapes such as `%2F` and `%5C` left encoded; -`Path` and `RawPath` are set consistently for chi and the ServeMux. Do not route -or authorize on a path outside that wrapper. On the Beta -group the constant OpenAI-Beta check (exactly one `agents=v1` value) runs before -authentication, and authentication still precedes every Beta handler, 404 and 405. -Every Agents API 401 has type `invalid_request_error`: null code on Beta routes; on Files -and Skills `invalid_api_key` only for a rejected Bearer credential. Agents API responses carry a fresh `X-Request-Id` (also in the log -context), `OpenAI-Version`, `OpenAI-Processing-Ms` and nosniff through the API -router's own middleware, not the shared log middleware. HEAD runs GET routes; -streaming, content-download, live directory, Runtime observation and Runtime -history routes register an explicit HEAD 405 instead. Every 405 of the API router, unknown methods included, has the JSON -body and lists the route's methods in `Allow`. - -Keep test artifacts under `~/.oac/` and build output under -`${OAC_DEV_HOME:-$HOME/.oac}`. Runtime state uses `${OAC_RUNTIME_HOME:-$HOME/.oac}`. Require -absolute user-supplied working directories. Keep credentials out of source and -logs. Update this guide when architecture, ownership or generated contracts change. -Comments and documentation are English. Reuse existing helpers and error mapping; -split oversized components before extending them. Use `internal/obs/log` for logs. - ## Required checks -Release checks and distribution builds may run concurrently against the same -immutable source commit. Publication must depend on both successful jobs; building -an artifact does not qualify it for release. Use the shared content-addressed Go -compiler/module caches in CI, keyed by platform, module inputs and source revision. -Caches may seed compilation/downloads, never replace checks or select release -artifacts. The [maintainer guide](docs/maintainers.md#publish-a-version) owns the -workflow, cache behavior and failure-cost tradeoff. - - -CI coverage has three owners: `core-check` runs the complete `make check` gate; -`api-acceptance.yml` adds the pinned official-client, migration-command and container -acceptance without repeating the full service test suite; `native.yml` -builds and tests the daemon, process lifecycle, Harness protocols and installer -bundle together on Linux, macOS and Windows. Native tests use the packaged -Harnesses and share one daemon build per platform. Changes to native sources, -shared dependencies or packaging inputs trigger that matrix; documentation-only -and unrelated Web changes do not. Manual native validation remains available. -Superseded native runs on the same ref are cancelled. Workflow syntax validation -and release qualification remain separate checks. - -`make check-example` validates the optional application example with TypeScript, -proxy/product-persistence tests, a build and fixture browser acceptance; it also runs in -`make check`. Its synthetic responses are not live model qualification. - -Run `make check` before completion. The standalone gate includes all daemon/shared -Go tests, Core contract/client/service tests, Core Web and TypeScript client -checks (including fixture-only Playwright acceptance), a real dedicated PostgreSQL test -database, byte-for-byte sqlc regeneration checks, standalone API builds, Claude SDK -tests and packaging, MiniMax companion checks, and native daemon filesystem tests. It includes the Core distribution and installer gates, but no Parsar product Web or server gates. The full gate fails when the database variable is missing. The test database role -needs CREATE DATABASE permission: managed-provider tests create and drop isolated -`oac_*_tests` databases because provider identity is deployment-wide. -Set `OAC_TEST_DATABASE_URL` to that dedicated database and `OAC_TEST_OFFICIAL_SDK_PYTHON` -to the pinned SDK interpreter. `PARSAR_AGENTS_API_TEST_DATABASE_URL` is retired; -`make check-database` reports its replacement when only the old name is set. -Tests must not bypass the production provider-switch guard. - -Use Go from `go.mod`, Node 22, pnpm 10.30.3 and Python 3.9+. `make sqlc-generate` owns only -`services/agents-api/internal/db/sqlc` (sqlc v1.29.0). Do not rewrite landed -migrations. The public protocol schema is `contracts/agents-api/openapi.yaml`; -there is no product swaggo contract in this repository. Preserve its pinned types, -coverage ledgers and official SDK/raw HTTP tests when changing API behavior. -Run `make openapi` after handler annotation changes. It reuses the original -Core-only swaggo v1.16.4 generator, then splits the result by namespace: `/v1` -into `openapi.yaml`, `/core/v1` into `core.openapi.yaml` and `/api/v1` into -`runtime.openapi.yaml`; the last two use base path `/`, and each document keeps -only the security schemes its operations use. All generated schemas remain free -of product routes. - -Core changes must retain the independent build and official-client workflow. -Native adapter changes require their applicable build/check targets and live provider -acceptance. Real execution checks require real models; do not count omitted -prerequisites or mocked responses as live acceptance. Workspace Files run in the -daemon's portable Go implementation and are covered by its tests. Historical -remote native probes are not current validation entrypoints. - -## Core operational metrics - -The Core-key-only `/core/v1/metrics` contract is documented in -[core-metrics.md](contracts/agents-api/core-metrics.md). Keep this separate from -Agent outcome and Sandbox capacity views. Instrument existing worker and job -owners without changing scheduling, lease or retention behavior. Periodic pool -pings and bounded in-process samples have explicit restart gaps; unknown values -must remain null. Complete UTC buckets exclude the active partial bucket. Root -Turn history is queried read-only from PostgreSQL with native timestamps. -Count `execution_unavailable` at the existing HTTP error writer, once per rejected -response; never record request/response bodies or infer this count from every -503 or failed Turn. Builds inject the source commit with ldflags. No new monitoring -service or storage system is required. Keep the frontend response shape aligned -with the paired console contract. - -Core process CPU, RSS and cgroup limits are sampled by the existing 30-second -Core metrics loop; Go heap and goroutine reads retain their meaning. Process -series use the same bounded ring and complete buckets, with null first CPU -intervals and restart gaps. Do not substitute host usage for process usage. - -Administrator node detail adds host observations and history as documented in -[node-host-history.md](contracts/agents-api/node-host-history.md). Keep the node -list unchanged apart from the address each node enrolled with (`core_url`). Reuse authenticated heartbeat ownership, the Runtime sampling -sweep and PostgreSQL retention cleanup; node observations have their own table -because they do not belong to a Project, Session or Environment. History is -best-effort telemetry, never scheduling truth. No read-triggered sampling or -offline backfill is allowed. - -## Architecture boundaries - -These rules govern execution ownership across components. Operation-specific -wire behavior and acceptance evidence live in the linked contracts. References -to the Parsar product describe an external client boundary. - -### Product and execution service separation - -Agents API is the primary infrastructure deliverable. Parsar is an ordinary client -and example application; its feature backlog must not dictate the execution -service's public protocol or internal model. Agents API must build, deploy and run -without the Parsar product service, frontend or database. An optional Compose -deployment may install both services with one PostgreSQL instance, but separate -databases, credentials and migrations. The product uses Core exclusively; it has no native daemon or HTTP Agent fallback. - -#### Design and compatibility requirements - -- The complete pinned `openai/openai-python` `beta/agents` protocol is the target, - including its referenced resources and types. Match paths, methods, headers, - field presence, nullability, discriminators, defaults, status transitions, - pagination, errors and streaming behavior. Engine limitations are implementation - gaps to solve, not grounds for narrowing or redefining the upstream contract. -- Preserve qualified native capability differences across harnesses. If a material - difference from the official API has no clear mapping, pause that part and ask - the user before changing its semantics. Explicit unsupported enablement rejects; - ordinary requests retain native behavior with any official default discrepancy - recorded in the coverage ledger. In particular, native programmatic tool calling - is not currently qualified as the official default-on behavior. Do not build - a separate executor or model loop to fabricate parity. This does not relax - authentication, isolation, credential protection or data consistency. -- Pin upstream source and SDK versions in `contracts/agents-api/upstream.json`. - Use official SDKs for clients and reuse upstream types or schemas where suitable. - SDK deserialization alone is not server validation or proof of compatibility: - test raw HTTP payloads and observable workflows as well. Synthetic data and mock - model responses may support controlled tests; live execution acceptance must - call a real model API through the service, daemon and harness. A real daemon - with a synthetic model does not constitute live model validation. Keep provider - credentials in private test configuration, outside source, logs and task records. - Record unspecified or unverified behavior explicitly; never invent official - semantics. When current documentation adds operations or fields absent from the - fixed baseline, queue a protocol upgrade instead of silently implementing a new - version. Owned-resource live probes can qualify status codes and wire details - left unspecified by the SDK; retain request evidence and distinguish observations - from guaranteed or fully covered behavior. Track partial - coverage in `contracts/agents-api/README.md` until the complete target is verified. - Reconcile current coverage summaries with merged routes and recorded acceptance; - distinguish accepted profiles, partial implementation, missing operations and - unverified semantics. Retain historical evidence with its original scope. Handler - counts are not compatibility percentages, and an active provider probe is not - deployment qualification. -- No legacy Agents API compatibility requirement takes precedence over this - design. Replace an unsuitable implementation instead of growing compatibility - branches. Preserve reusable, verified infrastructure rather than rewriting it - merely for new names or directories. Replacements may retire obsolete private - interfaces and history backfills in bounded PRs; this does not authorize deleting - product data or changing unrelated product behavior. -- Every concrete harness interaction goes through the common Runtime contract - and its adapter. Extend that contract minimally when a current operation cannot - be expressed; never put native capability logic or transport conversion into - Core handlers, storage or scheduling. Qualify public workflows through the same - shared chain; direct native probes establish feasibility only. -- Keep engine-specific types, process management and protocol translation inside - execution adapters. The public API and persistence/application core must not - interpret Parsar product payloads or depend on one engine's native item types. - Prefer maintained upstream SDKs and native execution protocols over a second - hand-written model/tool loop or a general-purpose compatibility framework. -- Verify an independent official-client workflow before a Parsar integration. - Parsar uses the same public contract as any other client, with no privileged - endpoint or direct execution-table access. An OpenAI endpoint is a possible - client target only where the requested capabilities and credentials support it. - -- Parsar owns users, workspaces, business authorization, Agent/Team definitions, - capabilities, product conversations, IM/sharing, approval decisions and billing. -- Agents API owns protocol saved Agents, execution sessions/turns, effective - configuration snapshots, dispatch/cancel, environments, vaults, raw usage, - pending interactions, protocol subagents and durable events. Protocol saved - Agents/vaults are execution resources, not Parsar marketplace or business roles. - Neither service reads the other's tables. Parsar uses a versioned client contract. -- A product conversation may map to several execution sessions. An execution - session is distinct from a live daemon socket, process or sandbox. Native engine - session identifiers belong to the execution service. -- Establish single-Agent execution, approval, cancellation, idempotent submission, - persisted recovery queries before Team orchestration. The upstream SSE stream - is live-only; recover through Session/Turn/Items reads. Any additional product - cursor replay must be documented as an extension, not upstream semantics. - Team definitions, management and orchestration belong to Parsar. Agents API - establishes single-Agent execution first; business Team loops are deferred. - This does not exclude upstream `multi_agent` configuration or subagent resources - from protocol coverage. Future business Team orchestration directly depends on - `openai/openai-agents-python` in Parsar. -- Daemon Skill/SP authoring remains a product operation: forward through a scoped - product callback with the original requester and workspace checks. A runtime - credential alone must not grant business write permissions. - -#### Environment ownership and placement - -Environment identity, tenant/Session association, configuration and lifecycle belong -in Agents API, independently of provider compute, authenticated device identity, -daemon sockets and native harness Sessions. Create an Environment association in -the same transaction as its Session and creation identity when this resource is -implemented. Keep mutable connection/registration state out of immutable -configuration; replacement ownership must fence stale observations. - -The current hosted architecture is V1: Core runs independently; each Environment -sandbox contains its daemon, selected native harness, local tools and workspace. -Execution and Files use the same authorized workspace through the existing -Core/Runtime contract. Native tool calls stay local. -CLI discovery uses a bounded 15-second version probe per installed harness; -missing binaries fail immediately. A version result is availability, not Environment -readiness, and does not change initialization or connection ownership. Process placement and native -transport remain adapter responsibilities, without a second model/tool loop. -The former separated Runtime/harness and workspace executor topology is a distant -future V2 option, to revisit only after V1 is stable and concrete needs justify it. -Do not extend that topology for hosted delivery, maintain two current hosted routes, -or introduce dormant V2 compatibility scaffolding. - -When an execution Session has a previously started Turn but no recorded native -Session ID, Core requires existing-history recovery through a verified Runtime -capability. Read that condition before claiming the next Turn. A supplied native ID -remains authoritative. The Codex adapter may recover only a unique, nonarchived -root in the exact Session-private native home and expected working directory, using -native listing and exact-ID resume. Missing, incomplete or ambiguous history must -fail without starting a fresh root. A recorded start can precede native work; that -uncertain case also fails conservatively. Recovery does not replay interrupted -inputs, erase prior outcomes or promise transparent continuation of running tools. -Keep Device identity and Environment scope in `ExecutionDevice`; native Session -identity and prior API Turn state belong to `SessionExecutionBinding`. - -Platform-managed and user-managed deployment reuse this same Runtime. For platform -management, SandboxProvider creates and reclaims it. For user management, the user -starts the Runtime and its daemon authenticates and initiates the Core connection; -Core verifies principal ownership and the exact Environment binding. These are -management responsibilities, not separate execution architectures. - -Runtime telemetry uses a separate read-only service boundary documented in -[`contracts/agents-api/runtime-observability.md`](contracts/agents-api/runtime-observability.md). -Resolve durable Session, Environment and Runtime-instance identity before selecting -a provider source. Observation never extends a lease or changes compute lifecycle. -Keep observed zero, unavailable data and unsupported Runtime modes distinct. Metrics -may inform operators, but automatic suspension requires durable Core-owned activity -state and must not use a monitoring backend as lifecycle authority. -Managed Docker observes one non-streaming Inspect/Stats sample. Managed microsandbox -observes the exact persisted compute generation through the existing one-shot helper -and pinned native CLI metrics report, with SDK identity checks before and after -observation. Derive compute start from the same native sample timestamp and precise -uptime; never subtract rounded uptime from a new wall-clock timestamp. Preserve -cumulative CPU seconds, memory usage/limit and -compute uptime semantics across both. Do not use microsandbox's instantaneous CPU -percent, wake suspended compute, or expose provider-native identifiers to fill a -common field. E2B has no cumulative CPU time: one read-only helper request per -page of at most 100 allocations reads E2B's batch metrics and confirms each -receipt's sandbox in a labelled running listing. Its reported CPU share fills -`utilization_ratio`; disk appears only in the administrator list. Page reads go -through the Service's optional batch source; do not add a second collector. - -Runtime history uses the existing Core PostgreSQL database: one sanitized row per -periodic observation, seven-day retention and bounded reads. It is best-effort -operational evidence, not execution or Usage authority. The execution owner samples -by default every 30 seconds. Core's internal measured Session usage (every -recorded root Turn snapshot, active Turns included) supplies token snapshots, not -the public Session usage rule; never aggregate provider counters as model tokens. Preserve missing data -and reset CPU derivation across compute incarnations or counter regressions. E2B -history stores the reported utilization ratio; a bucket holds their mean. -The bounded asynchronous database writer and optional OTLP exporter have independent -queues; external telemetry outages must not stall local history or execution. -Retention cleanup also runs without active Runtimes. The browser queries only Core, -never storage or a Collector, and stays a lightweight administrator console. -No additional metrics database or Collector is required for retained charts. - -In V1, our daemon fills the user-side executor role. Users deploy daemon, the -selected harness, local tools and workspace together. Do not require Codex -`exec-server`, a service-side harness, registry/Noise transport or remote tool -forwarding. The explicit daemon-executor decision supersedes the previous native -executor interoperability requirement. The superseded execution route is removed; -retain reusable filesystem helpers, -necessary regressions and historical evidence without a compatibility layer. -The private daemon wire protocol is 0.8.0. Initial, prepared and active input use -the same ordered MessageInput contract, replacing scalar prompts and attachments. -User-message boundaries and text/image order remain intact through Core and the -Runtime wire; adapters own native conversion and receipt aggregation. Text-only -transports reject image content rather than dropping it. Text is never trimmed: -engine profiles declare whether whitespace-only messages are qualified, and -unqualified harnesses (Claude SDK, MiniMax Code) reject them at admission rather than having -their input rewritten. Codex has a flat native -input list and uses blank-line separators between messages; this does not preserve -independent native user-message boundaries. No old wire fallback is maintained. -Deploy Core and daemon together; the WebSocket check requires the exact wire -version, including patch, and rejects mismatches before dispatch. -The independently packaged Claude bridge uses protocol 3 for a prepared Executor -and separately identified Turns; readiness rejects other protocol versions. -Image-bearing messages require a qualified profile/placement before persistence -and image support from the selected Runtime before native delivery. These checks -apply to that operation only; ordinary text retains offline queueing. Initial, -prepared and active paths use the same content and preserve receipt ownership. -The qualified public profile is inline PNG/JPEG on Codex/Claude `none` and -Core-managed Docker `openai_hosted`. Self-hosted/user-managed images, MiniMax -images and remote URLs remain explicit implementation gaps. Hosted images reuse -the existing preparation, active-input and workspace authority; they do not add -a downloader, a mount or a separate execution lifecycle. Core -does not fetch or transform media. See [message input coverage](contracts/agents-api/message-input.md). - -User-managed onboarding creates a `self_hosted` Session first, then passes its -Environment ID and unchanged `remote_url` to our Runtime with connect-only -authorization. This is our daemon connection contract, not stock OpenAI -`exec-server` transport compatibility. Validate the pinned public HTTP/SDK -resources, state transitions and lifecycle separately; do not infer complete -compatibility from a working connection. User-side tooling owns local Runtime or -E2B allocation, renewal and cleanup. Session deletion and credential revocation do -not transfer ownership of user compute to Core or prove process quiescence. - -Public Environment Templates belong to Core and its execution database, independently -of provider image/build templates. Resolve a tenant-owned reference once at Session -creation, freeze the effective ordinary hosted configuration and reuse inline -initialization. Do not pass template IDs into Provider or Runtime. Omitted or null -network inherits the complete template policy; overrides may only narrow policy. -Preserve unresolved caller intent for creation retries and recover committed -results before reading mutable templates. -For template-reference Session initialization, omitted/null env, files, commands -and packages inherit. Overlay non-null env keys; replace non-null files and command -lists, including empty lists. Select each package manager independently: omitted/null -inherits, while a supplied list replaces that manager. Revalidate the effective -configuration through the existing validators and freeze it through the same -transaction as inline initialization. Keep caller intent separate from resolved -configuration; do not add a second installer or pass merge rules to adapters. -Updates and deletion cannot rewrite existing Session snapshots. Initial files use -one Core-owned installer for template and inline configurations. Keep confidential -bytes encrypted under the execution-service key and resource-bound AEAD, separately -from ordinary configuration and public metadata. Templates retain source references; -Session creation freezes tenant-authorized source bytes in the same commit, independent -of later source/template deletion. Public resource reads must not require decryption -or load encrypted file bodies. Record original creation intent before resolution. - -Template parsing, persistence and resolution must not select a harness or Provider, -or depend on native tool names and private harness paths. The shared initializer -uses the packaged Runtime contract for trusted commands, workspace/staging paths, -confidential input and completion receipts. Each Provider bootstraps the same -daemon; all initialization uses its authenticated `runtime_prepare` protocol. -The common runner depends on Environment and Session identity and a Runtime peer, -not a Provider, deployment type or operating system. Each adapter owns native tool -configuration. A new harness or Provider must not require template -business-logic changes. Reuse qualified shared helpers even when their executable -names have historical engine prefixes; renaming is not a boundary fix. Select real -regressions by the changed shared, Provider and adapter boundaries, rather than -repeating every deployment combination for each configuration field. - -The allocation lifecycle owns pending/running/complete initialization. Authentication -may connect the daemon during initialization; execution bindings, native preparation, -live Files and connected publication wait for completion. Keep Provider bootstrap -settlement distinct. Advance at most one bounded initialization operation per full -maintenance scan. At allocation EOF, begin the next page in the same call rather -than consume an observation interval on an empty page. Refill at most once, retain -the 32-allocation per-call bound and the five-second ticker, and never loop on an -empty store. For managed nodes these bounds apply independently to each node. -Use process-local progress and the same node lifecycle gate as direct provisioning. - -After a next-Turn input is durably pending, a completed managed allocation in a -suspension/recovery phase may hint this loop. Initial inputs, cold creation, -running/disabled compute, terminal receipts, cancellation/tool-result events, -history and file operations do not use this hint. Eligibility lookup and delivery -are best effort; persisted work and the normal ticker remain authoritative. -Coalesce hints without blocking, and allow at most one extra scan per normal -five-second cycle. Keep the ticker independent of requests. A normal tick consumes -already queued hints before scanning; simultaneous tick/hint readiness is one -normal scan. Preserve hints arriving during a scan, the allocation cursor and all -ownership checks. Never close the hint channel while handlers may still send. -This bounds extra maintenance work but does not bypass capacity, a busy lifecycle -gate or multi-page scheduling, and does not guarantee a resume deadline. - -A recovered or uncertain running installation fails and uses existing cleanup, without replaying writes. -A hosted provisioning failure is terminal for its Session. The allocation's cleanup -transaction stores a safe reason with the failed Environment and records -`environment.failed`, `error` and one `agent.session.failed`; Session reads derive -`failed`, that reason and the failure time from the same record, and live streams end -after the failed event. The initializer reports only the integer exit status of a -failed initialization command. Core composes the reason from a fixed step label and that status, -never from command, package-manager or file output; unknown effects, timeouts and -receipts without a status keep a generic reason. New input then gets the observed 409 -`conflict_error`; expiry and pending-input settlement keep their behavior. -Completed environments never reinstall initial files on reconnect or native recovery. -The typed Runtime preparation protocol carries bounded confidential input. The -daemon owns the common Go initialization implementation; Core supplies no -executable or host-platform field. Initial files and tool configuration precede -Skill/Plugin bundles, npm/Python packages and ordered setup, followed by capability -directory capture. Commands run directly with the starting account's permissions -and host network. Outer Environments own managed isolation; unsupported network -restrictions must reject rather than silently run unrestricted. Confidential env -and setup snapshots are encrypted independently of ordinary metadata. Adapters -apply explicit tool variables without automatically inheriting daemon credentials; -this does not prevent same-user tools from reading local Runtime state. -Reuse runtimefs atomic replacement and anchored logical-workspace paths for initial -files on every platform. Public Files creation keeps its own non-replacement rule. - -System dependencies must be preinstalled in the managed image/template or by the -self-hosted user. `packages.system` rejects explicitly, including a supplied null -or empty list. Runtime does not run apt, sudo, an unprivileged system-root installer -or any other automatic system-package operation. No daemon privilege increase or -managed-only exception is permitted. Missing dependencies fail the consuming -operation. npm and Python use local prefix/target directories; setup requires Bash -and Windows requires Git Bash, without a substitute shell. -Package responses retain the official required `system: []` field as empty -response metadata; it is not stored as an initialization option. - -Initialization and package directories default under `OAC_RUNTIME_HOME` and may be -selected with `OAC_RUNTIME_INITIALIZATION_DIRECTORY` and -`OAC_RUNTIME_PACKAGE_DIRECTORY`. Packaged Linux images select their existing -`/environment` layout through those settings. `OAC_RUNTIME_TOOL_ENV_FILE` selects -explicit configuration. These are resource paths, never Environment-source or OS -switches in Core. Runtime process ownership waits for exit and I/O settlement; -confirmed failures retain only a bounded exit status, never command output. - -Skills and their immutable versions are Core-owned tenant resources, independent of -Sessions and native Skill installations. Serialize version allocation and pointer -mutations under the owning Skill row. Keep top-level name and description aligned -with the default version in the same transaction, including default-changing uploads; -nondefault uploads preserve that metadata. Preserve unique version identities across -concurrent uploads and deletion. Metadata reads never load or decrypt bundle bytes. -Encrypt bundle contents with a tenant, Skill and version binding using the existing -service cipher. Deleting a Skill reclaims its versions without affecting already -frozen Session initialization. Public reference metadata, unresolved template intent -and the resolved Runtime bundle are distinct; do not report a reference as inline -merely because it reuses the same installer. No compatibility reader, source -cache, extra lifecycle owner or per-harness resource implementation is required. -Resolve references inside the Session creation transaction, after the creation -upsert establishes ownership. Lock referenced resources in a stable order; freeze -the selected version, descriptive metadata and bytes together. Creation retries -recover the recorded intent before reading mutable templates or Skill sources. -Templates preserve default, latest and explicit version selectors. An omitted or -null reference version selects the default at Session creation and projects as -`version: null` in Template responses. Session responses contain concrete versions; -only validated installation metadata crosses the Runtime boundary. A supplied -Session Skill, Plugin or capability-directory list replaces its template list; -omission and null inherit, while an empty list clears that selection. This differs -from Template resource updates, where null clears lists and resets network to the -pinned enabled default. Preserve caller intent and frozen Session snapshots in -both cases. Public capability directories remain caller paths; adapter-owned -installation directories are not portable public paths. - -Inline and referenced Skill ZIPs use the same confidential initialization snapshot and installer. -Core validates portable manifests and bounded regular-file archives, returns only -safe Skill metadata, and freezes content before native preparation. The Runtime -owns `skills/` below its configured capability root; the manifest validates -the installed snapshot before reuse. This is state -consistency, not protection from the launching user. Public Plugin ZIPs preserve their complete package -layout and reuse the shared archive and portable Skill parsers. Core keeps safe -Plugin metadata separate from encrypted archives. Templates inherit or replace -Plugin and capability-directory lists through the same hosted resolver. - -After ordered setup, the existing initializer snapshots declared workspace-contained -capability directories into Runtime storage and writes one installed manifest. -This is an initialization artifact, not a second lifecycle owner or database ledger. -Directory bytes are observed after setup; they are not frozen at Session creation. -The common daemon resolves the manifest only for executable preparation and passes -validated Runtime-owned Skill/package roots to adapters. Files reads do not require -that artifact. Reconnection and recovery read installed bytes, never mutable source -directories. Missing or inconsistent installations fail preparation without replay. - -Adapters register only selected Skill roots without changing the execution loop. -Codex uses explicit extra roots; MiniMax projects its native catalog; Claude creates -one controlled envelope per package with real directories and immutable hardlinks -under content. Its explicit paths remain inside that envelope; original native -control files are not activated. Keep automatic native MCP discovery disabled. -Explicit Environment MCP declarations -follow the separately qualified transport path below; unsupported native activation -fails explicitly, without silent partial activation or a generic plugin framework. - -Environment-origin MCP declarations use the shared Plugin parser and frozen -installed packages. The installation manifest retains selected MCP package roots; -Runtime re-parses those installed packages without another configuration copy, -credential cache or lifecycle ledger. Native adapters must explicitly qualify and -project supported declarations before public admission. Parent-directory Skill -discovery does not activate nested Plugin MCP configuration. - -The common daemon stdio entry resolves the installed declaration and launches its -server under the same user permissions as the Harness. Unix replaces the helper -process; Windows forwards native stdio within the owned process tree. Explicit -initialized values override the selected declaration's variables. There is no -Python sandbox launcher, mount policy, shell prefix or separate credential sandbox. -Process groups and Windows Jobs own cancellation and descendant cleanup only. -Claude composes MCP identity and observation with the same workspace profile; -MiniMax keeps its existing supported transport qualification. A public capability -advertisement must still match the installed engine's actual supported transport. - -Environment-origin literal HTTP headers remain rejected for the pinned Claude -client because its interpolation and cross-origin forwarding change their meaning. -MiniMax accepts environment stdio only. Its adapter reads the existing Session-private -native runtime-name registry for exact first-frame identities and cross-checks -completed native results. Reuse existing observation and cancellation settlement; -never fabricate a delayed start event, guess normalized identities or add a registry -of our own. Its unqualified HTTP transport remains rejected. -These mechanisms alone do not establish public MCP support or full compatibility. - -Name, initial files, inline/referenced Skills, Plugins, workspace capability directories and env/setup/npm/Python are -implemented independently of remaining installation fields. Reject unsupported -inputs rather than persisting them for silent -omission; expand inline and template initialization together in separately qualified -batches. Resource reads need only tenant authorization, not a live Runtime. -See the [Template coverage and unresolved semantics](contracts/agents-api/environment-templates.md). - -SandboxProvider owns Create, GetInfo, Renew and Kill, with placement and daemon -bootstrap at the resource-management boundary. Use maintained provider SDKs and -thin adapters. Hosted deployments select one deployment-wide Provider: E2B cloud, -or Docker/microsandbox on administrator-owned nodes. Providers do not execute Core -initialization commands. Initialization, daily execution and Files use the same -daemon through typed Runtime operations and native or bounded local capabilities. -Docker's lack of a native renewable lease -does not remove service-owned hosted expiry and cleanup requirements. - -The official `openai_hosted` discriminator means hosting by this independent Core -service, using its configured hosted Provider. Keep the public value unchanged; `parsar_hosted` is not -a new API type. Public Environment Templates apply only to this hosted path. -E2B onboarding follows the application-managed `self_hosted` resource workflow: -the application owns sandbox provisioning and cleanup, and our daemon connects -with the returned Environment ID, unchanged `remote_url` and scoped environment -authorization. Reuse the same Runtime and thin provider components. No OpenAI -executor process or additional execution architecture is required. Qualify -principal/tenant ownership, credentials and connection lifecycle using the pinned -client and actual execution; document our transport boundary explicitly. - -Core-managed E2B is a separate hosted deployment choice, using the official pinned -Python SDK through a packaged private helper. Do not restore the retired custom -HTTP/Connect or envd implementation. The adapter implements the same five operations; -cloud allocations use direct placement with no synthetic node, while Runtime execution -and file access keep the shared daemon contract. Its immutable Runtime template build -is deployment configuration, not a public Environment Template. Keep the account API -key encrypted in the database, write-only through admin input and absent from helper -arguments, logs, metadata and receipts. SDK connection materials and attempted-create -receipts belong in the private durable provider state directory; never replace missing -state to make cleanup appear successful. Create runs once. Unknown control-plane -outcomes remain blockers even if a listing is empty. Explicit matching-reference -CreateSettled evidence proves that the original initialization cannot mutate further; -confirmed absent compute may then be released. Ordinary 404 responses do not prove it. -The helper's pinned SDK, dependencies and licenses ship with Core; users do not install -Python packages after selecting E2B in Web. Application-managed self_hosted tooling -remains independent and uses the same Runtime. Qualify each changed path using actual -provider and model execution before claiming acceptance. Run `make check-e2b-provider` -with `OAC_TEST_E2B_SDK_PYTHON` pointing to the pinned SDK environment; the packaged -helper build runs the provider tests as well. The SDK gate also covers the -application-managed launch tests. `make check` covers shared initialization and -managed initialization using only the Python standard library. - -The independent Docker Provider consumes an immutable Runtime image and retains -one caller-owned allocation reference through partial creation and cleanup. Persist -that reference before Create and serialize its lifecycle; resolve a lost response -with observed state, without rewriting bootstrap credentials or replaying startup. -Provider state is compute state, not public Environment readiness. Named Runtime -volumes need explicit owned cleanup after container removal. A second mount of the -same workspace volume subdirectory provides the public `/workspace` path to native -tools; trusted staging and atomic rename retain the original parent mount. Do not -copy files or widen private-path reads to preserve an alias. Initialization command -timeouts can leave processes alive and require allocation cleanup before reuse. -The [managed Runtime build and operator configuration](services/agents-api/deploy/codex/README.md#managed-runtime-image-and-docker-adapter) -defines the explicit opt-in for basic hosted admission. Building an image alone -does not qualify its isolation or enable public creation. - -### Hosted sandbox nodes and optional suspension - -Default installation includes Core, Web and PostgreSQL but no execution node. -It always creates the Core key. Core receives only its digest; the paired console -server receives the private key, uses it for sign-in and injects it only on -`/core/v1` requests after console login and same-origin checks. The browser -never receives that key. Node and daemon connections use `/api/v1` -with their own credentials; the reverse proxy sends them directly to Core, never -through Web. Zero-node Core receives neither the Docker socket nor KVM. -The Web and deployment administrator API select one provider, per-sandbox -resources and immutable Runtime release; the Core address comes from the -installation public URL. PostgreSQL owns this complete, -generation-tagged selection under the existing execution lease and deployment lock. -The shared `sandbox.DeploymentSpec` defines required CPU/memory and supported disk -limits plus Runtime provenance; neither a node file nor the installer owns another -selection. Request `resources` describes limits; response `specification.resources` -contains those limits, while response `resources` counts retained allocations and -pending hosted Environments. Keep these meanings distinct in clients and UI. -See the [deployment contract](contracts/agents-api/sandbox-deployment.md). - -Docker and E2B accept CPU/memory but reject independent nonzero disk capacities; -do not claim hard root/workspace disk quotas for them. Docker creation and native -inspection enforce the declared CPU/memory and exact image. E2B setup verifies the -exact ready template build and matching CPU/memory through the pinned SDK before -saving its encrypted account key, and records the build as read for the safe view; -an omitted E2B `resources` adopts that build's CPU and memory. Loading a committed selection reconstructs its -provider from its owned generation, current committed credential and receipts without repeating candidate -template validation; a template endpoint outage must not block cleanup of existing -sandboxes. Creation and instance inspection still enforce the saved resources. -E2B uses direct placement without a node. Node -providers require one immutable distribution with source commit, Docker image ID, -OCI manifest digest, microsandbox image reference, Runtime and firmware hashes. -These identities are distinct and cannot substitute for each other. - -Startup claims the stable installation identity and a new owner epoch before -provider selection. The runtime manager retains generation-aware provider facades. -Initial setup and replacement prepare and validate candidates before database -writes. Rejected candidates preserve the active configuration and workers. A node -replacement uses the existing mutation gate, pauses manager admission, drains old -calls and loops, then repeats the resource/generation guards in the commit -transaction. A changed selection, its generation and retirement of old nodes and -unused enrollment tokens commit together. Publish the prevalidated configuration -and shared observation/bootstrap cache under the manager mutex without further -external work or a fallible activation step. Request cancellation after commit -cannot discard that publication. Interrupted drains remain barriers for retries -and resume. Keep the Worker and runtime manager as single owners; provider I/O and -draining hold no database transaction or manager map mutex. - -A locally unavailable provider dependency keeps hosted admission closed while the -existing scan waits for repair; administrator recovery remains available, including -on restart. Database and ownership errors remain failures. Unconfigured hosted -admission creates no Session state. Derive Runtime bootstrap and daemon WebSocket -addresses from the validated installation public URL, never inbound Host headers. Read the -current selection from the live deployment API; there is no startup configuration -read. - -All sandbox writes require the observed generation, including initial POST at -zero; reject stale state before provider preparation, reset or no-op checks, and -repeat it under the committing row lock. Same-provider PUT advances the target -without draining execution or retiring nodes, tokens or the owner epoch. Node -providers prepare independently and keep their old qualified serving pin. A different backend, E2B team or unspecified old selection requires reset. -Preserve historical allocation ownership and placement, never migrate a Session. - -Persist immutable allocation and placement deployment generations, distinct from -compute generations. Node allocations copy their placement; E2B binds at reservation. -Keep superseded specification/build rows without credentials while current, owned by -an unreleased allocation/placement, or pinned by any nonremoved node. A durable node -serving pin survives offline state and zero resources. GC uses the deployment lock -and bounded pages; reset clears pins only after release. Never downgrade facts needed -for generation routing. Bind node readiness to exact generation, current connection -and owner epoch. Promote a durable pin only for readiness of the then-current target -under deployment serialization. Late superseded readiness cannot acquire a pin. -Filter online/readiness/address/capacity before preferring the newest eligible pin; -newest-full must not mask older-free. Route Create, restore and cleanup through the -immutable allocation/placement generation. V1 keeps its qualified enrolled fallback. - -Use sparse v2 control batches of at most eight entries with no lifetime cap. Omitted -facts never authorize deletion. Correlate whole retention grants to connection, -epoch, sequence, generation and digest; recheck queued/inflight/helper references. -Permanent generation flock files survive updates and GC. Before any helper starts, -the installer durably binds the original inode to installation/generation/specification -identity; Python and Go openers verify it after every restart and never adopt a -replacement or missing record. Preparation plans stay distinct from write-once final -provider configurations: pending-only entries may recover or collect under Core -authority, never serve. Resolve Docker's supported local image ID before publication. -Only transfer/checksum/provenance errors report runtime_download_failed; preserve -other fixed causes without parsing native error text. Retained Runtime bytes and -the console's exact-release HTTP allowlist must agree. See -`contracts/agents-api/node-generation-protocol.md` for recovery, immutable artifacts -and the conservative v1 legacy-helper retention boundary. -Interrupted node collection keeps an exact private generation journal and immutable -configuration. Restart treats it only as a candidate for a fresh correlated Core -drop grant, never as preparation or serving readiness. Persist native cleanup -before removing its executable and durable file cleanup before the dropped marker; -CLI errors and unknown native ownership cannot establish absence. Docker daemon -images are shared host content, so automatic node GC retains them; only the host -administrator can establish whole-host authority to remove them. Microsandbox -image GC remains scoped to the installation-private store. Fresh nodes persist -verified original Runtime file ownership separately from the host program; collect -only exact unreferenced Runtime paths, keeping program, identity, base configuration -and manifests readable across interrupted cleanup and restart. - -Use one E2B classifier. Omitted key preserves the current key; identical selection -with omitted key is a no-op. Explicit key submission, including identical bytes, -verifies and increments generation. Check the candidate and retained builds, paginated -team-owned template membership and every settled live receipt under the candidate key. -Initial E2B selection must also belong to the submitted key's team. Before online -replacement, verify the current exact template under the committed key, then under -the candidate key: shared membership is the ownership anchor, including for legacy -installations with no retained resources. The pinned SDK exposes team-owned template -listing, not an authenticated team ID; never invent or persist an inferred team ID. -Public readability cannot anchor ownership. A legacy selection outside the committed -key's team, or a revoked committed key, requires reset without blaming the candidate -key. Transport uncertainty remains unconfirmed. Keep the old key valid until the PUT -returns 200; revoke it only afterward. Missing/unsettled receipts and -unconfirmed reads reject. Fence credential commits against all provider calls and -actual helper subprocess completion after cancellation, then reverify. Helper exit -never settles unknown remote Create. Bounded failure keeps the original key and -lifecycles. Resolve each allocation's immutable specification and current key in one -snapshot; no stale-key cache, fallback to current spec, or provider-map unloading. -Audit the committed change without secret fields. - -Project rollout and reset from the same database snapshot. Old retained resources -alone never imply preparation or high-frequency polling. Rollout state is settled -unless actual target preparation is active. Offline/unconfirmed nodes are unknown; -only exact current connection/epoch/generation readiness is ready. A durable pin is -not connectivity. Fixed diagnostics alone may explain failed preparation. Report -preparing only from an actual target observation; an online v1 node can be -update_required while its valid pin remains available for admission. - -Reset is durable execution state, advanced by the existing manager outside its -counted work. Serialize start, escalation, cancel, setup, update and finalization -through the mutation gate. Auto waits only for in-progress/waiting root/subagent -Turns and pending file writes; queued work, idle and suspended Sessions can archive. -Recheck idleness with Session then deployment locks; never invert that order during -finalization. Keyset-page bounded work, bind candidates to the reset request time -as well as generation, and persist the original absolute deadline and validated -audit provenance. Cancellation cannot revive archived work. Auto's deadline -escalates to force durably; self-hosted Sessions are excluded. - -Use one snapshot and timestamp for the held-resource partition: unreleased -allocations plus pending hosted Environments without any allocation row. Cleanup -has precedence, then busy/idle. Count deleted/expired receipts until release. -Offline ownership comes from allocation or active placement node identity, with -the existing 45-second connection/owner-epoch predicate; provider readiness is -independent. Project every offline node and make its sum equal on_offline_nodes. -No cleanup failure, offline state or empty read authorizes a synthetic release. - -At zero held resources, drain outside database transactions, recheck under the -deployment lock, and atomically clear all provider policy, E2B credential/build -metadata and specification, retire nodes/tokens, advance generation/owner epoch -and audit completion. Publish a generation-bearing nil provider tombstone without -fallible work after commit, preventing delayed loads from reviving the old provider. -On a failed final write or interrupted drain, restore the committed provider using -a bounded owner context before releasing the mutation gate; recovery failure keeps -admission fenced and stops the owner. Ordinary migration resumes old Web-managed -maintenance with reset null. Refuse downgrade during reset or for an unconfigured -completed reset with generation above zero; never discard the generation. - -Explicit administrator Session archive requires a Web-managed deployment and its -current generation, without a reset precondition. Keep Project scope and hosted -eligibility checks in its Session-first transaction, alongside Environment expiry, -cancellation, Runtime authority revocation and audit. Background reset reconstructs -the actual Project scope from trusted records while retaining requester provenance. -The ordinary provider lifecycle owns compute/snapshot release. Retain history and -persisted Files/Artifacts; archived Sessions cannot resume unpersisted workspace. -The archive GET reports resource disposition, not provenance or Turn finalization. - -Archive cancellation must not destroy a healthy receipt path before its terminal -commit. Only the archive that first revokes a device may persist its exact -`archive_cancel_turn_id`; ordinary revocation clears it, and repeated cleanup -preserves rather than recreates it. The existing authenticated delivery can drain -its cancellation for at most 20 seconds from the Turn's original -`cancel_requested_at`. Track that delivery through Done, cancellation ACK and -terminal commit, independently of subscription removal. Heartbeats and both normal -and checkpoint cleanup use the same identity/deadline check; no new connection, -input, file/MCP authority or lease renewal is granted. Do not hold a transaction or -lifecycle gate waiting for the receipt. Lost peers, expiry and restart retain the -ordinary failure/cleanup fallback, never a fabricated cancelled outcome. Preserve -unknown Create ownership and reject schema downgrade with unsettled markers. - -Core rejects `AGENTS_API_MANAGED_RUNTIMES_FILE`; there is no file-managed startup -path or embedded local node. An older file-managed database is not automatically -adopted after its environment variable is removed. Settle and drain that deployment -with its previous release and original backend, preserving business data, private -receipts, identities and storage. The legacy file-managed path has no automatic adoption or force conversion. The supported current path is a database-managed -deployment. Harness selection and public/self-hosted contracts remain unchanged. - -The paired console serves only matched, non-secret distribution artifacts for node -installation. Never serve private installation files or arbitrary paths. Installation -reads `GET /api/v1/sandbox-node/configuration` using an unconsumed enrollment token, -or a retained node credential with `X-OAC-Node-ID`. Reads never consume enrollment; -registered nodes can read their matching configuration during reset. Validate -installation, generation, specification digest and release before writing node files, -registering or reconnecting. Reject drift rather than overwriting retained identity -or using local resource defaults. Registration consumes a token only after these -checks. The node installer requires root or sudo, verifies downloaded files and -starts a root-owned system service for the dedicated `oac-node` user it prepares. -It performs no SSH installation, Session creation or model call. Ordinary-user -installation and removal are rejected before reading credentials or mutating state. -Internal generation preparation and collection still run as the service account. -Web exposes one root/sudo command; do not retain a user-service alternative. This -boundary is specific to Sandbox Provider nodes, not native self-hosted daemons. - -Node management (Web's **Nodes** page; see the -[operator reference](services/agents-api/HOSTED-SANDBOX-MANAGER.md)) is a -deployment-level admin surface, separate from Project credentials. The paired -console's Core key stays on its server. Enrollment credentials authorize -initial node configuration reads and registration; durable node credentials authorize -retained configuration reads and node transport. Project keys cannot read nodes, -placement or allocations. - -One execution owner manages every node through the same finite Provider protocol. -The Core host joins through the same standalone node installer and service as any -other host; every node needs a guest-reachable, non-loopback HTTPS origin. -All managed nodes actively connect over authenticated TLS. Persist private node -identity and highest owner epoch; refuse another process using the same identity -or a changed backend namespace. Reserve each NodeID before transport upgrade and -retain that reservation through disconnect cleanup; a duplicate connection must -not replace a live or opening connection. Keep one private state directory per -node and never copy its identity to another host. This is connection exclusion, -not host attestation. The Hub's global mutex protects only in-memory connection -state. Authentication, ownership and Store callbacks run synchronously outside -that mutex, respect cancellation and have a five-second limit; never detach -database writes. Closing the Hub cancels opening and live connections without -waiting for database callbacks. Keep each node reservation until its fenced -disconnect cleanup finishes. Register database presence in an explicit transaction: -a canceled statement must not later publish presence through autocommit. Disconnect -cleanup first locks the node row by identity, then applies the connection/epoch -fence with a fresh READ COMMITTED statement so an in-flight commit cannot be -missed. These transactions must not acquire the deployment-wide manager lock. -Heartbeats establish provider readiness and -last-observed host metrics, never Session activity. An unready provider reports -one fixed diagnostic code, classified by typed probe errors where the node detects -the cause; probe text and host paths stay on the node, and Core stores unknown -codes as `provider_unavailable`. Keep that set closed. Transport reconnects use -bounded backoff. Send relative operation budgets, anchored to the node clock at -receipt and consumed while queued; clocks on different hosts need not agree. Core -still bounds its own response wait. Do not replay mutations after a timeout or -lost response. Retain allocation -and checkpoint operation receipts and observe the original operation instead. -Disconnects and read timeouts are unavailable/uncertain, never resource absence. -Node transport preserves an exact-reference, explicit `CreateSettled` receipt -alongside its original provider error. Settlement authorizes eventual allocation -release, not execution. A confirmed native Create rejected by the subsequent -read-only configuration check, before bootstrap starts, can return that receipt. -Do not infer settlement from timeout, missing compute or successful Kill. Strict -configuration rejection must not prevent already-authorized cleanup: Kill still -checks ownership independently, and release still requires creation settlement. -Runtime resource observation uses the same immutable node placement through one -bounded read-only Provider operation. Preserve main's Runtime observation/history -service and authorization boundaries. The node delegates only to a provider-owned -observation source; absent capability or transport returns unavailable, never a -Core-local fallback. Observation must not create, renew, restore, or touch Session -activity. Preserve the durable compute receipt and provider timestamps; existing -observation clock validation can reject skewed samples without changing idle policy. -Online-state writes compare the handshake epoch atomically in PostgreSQL so a -stale Core cannot publish readiness for a new owner. Node-managed allocations -do not expire merely because the internal observation keepalive is an hour old; -explicit deletion and configured snapshot retention still authorize cleanup. -Persist only bounded, sanitized observation codes for offline, missing or -unconfirmed resources; keep these separate from the lifecycle and do not invent -a successful running observation after a host restart. - -Managed lifecycle state is owned by one serial worker per registered node: -its gate, allocation and pending cursors, connections, initialization progress and -wake hints are not shared with other nodes. A thin coordinator discovers nodes -and owns worker shutdown; it never holds its map mutex during database, provider -or wait operations. Each worker advances independently, including when another -node is online but its provider is stuck. Do not add a shared scan barrier or -global provider pool: lifecycle concurrency is at most one operation per node, -and grows with the registered node count. This is not a fixed global limit. -Keep offline workers so retained resources remain observable after reconnect. - -Allocation scans filter by the fixed node before their 32-row page limit; pending -scans join the unreleased committed placement. Each node advances its own cursor, -including failed observations, and wraps once at EOF. Direct provisioning resolves -the tenant-scoped existing placement before entering that same node's gate; an -existing allocation must agree with the placement. Never choose another node. -E2B direct allocations use one serial lifecycle without a node identity. -The coordinator stops accepting work and cancels and drains all node workers and -direct callers before releasing the sole execution lease. Lease loss is global; -ordinary provider failures stay within their node. A planned deployment drain or -inventory retirement cancels lifecycle contexts synchronously between leased -operations, using the existing lease gate with its five-second bound. Include -an active manual reconcile's cancel function: an asynchronous `AfterFunc` alone -can cancel a later leased query after the gate reopens. Never cancel an in-flight -leased query merely to change deployment configuration. A failed cancellation -fence synchronously closes manager admission and reports owner failure, even -before its coordinator starts. Failed inventory retirement retains the original -lifecycle identity and gate through owner shutdown; gate availability is not -proof that cancellation succeeded. The drain barrier stays closed and cannot -activate a replacement. Release the lease gate before waiting for provider -settlement or lifecycle accounting. Ordinary caller deadlines and owner shutdown retain their -existing cancellation and fail-closed lease-loss behavior. Session locks, deployment -capacity transactions and revision/one-shot receipts remain authoritative, with -no external operation holding a database lock. - -Commit environment-to-node placement with Session creation and its creation retry -identity. Placement is automatic: Core chooses an eligible node, and callers cannot -select one. Existing retries keep their original node even when it is offline. Node capacity counts pending reservations and -unresolved resources; new placement and suspended-to-restoring admission share a -database lock. Confirmed cleanup releases placement capacity. Retained ownership requires exact -provider evidence; a socket path, missing instance or empty listing cannot prove -cleanup or authorize replacement. Historical file-managed ownership must be resolved -using its previous release and original backend before retiring that configuration. -Do not add a startup adoption path to bypass the database-managed selection. - -Do not add node-level drain controls. Refuse node removal with pending allocations, -instances, snapshots, unknown results or cleanup resources. Offline ownership is -retained. Removing a node does not delete compute. Local and remote nodes share -the same resource guard. Keep the deployment-wide reset and configuration -change guard. This boundary does not add cross-node Session -migration, Core multi-active, autoscaling, Kubernetes or harness residency. - -The common -`services/agents-api/internal/sandbox` contract owns the four base operations -(Create, GetInfo, Renew, Kill) and the optional CheckpointProvider -capability. Core orchestration must not import an adapter or SDK. Exact compute -identity, generation construction, inspection, full snapshot capture, restore, -thaw and owned artifact cleanup use that common capability. Initialization and -execution use the authenticated Runtime peer. -Provider-specific names and snapshot identities are opaque to Core. Self-hosted -compute and providers without checkpoint support keep their existing behavior. - -The [microsandbox deployment profile](services/agents-api/deploy/microsandbox/README.md) -pins the SDK, runtime, firmware and image. Core remains a pure-Go binary. The -one-shot native Linux helper contains the SDK/FFI and runs under the same private -service namespace; it is not another scheduler or network control plane. Ordinary -pause does not release RAM. Suspension captures and verifies a full snapshot, -stops the exact source, and removes its writable compute closure only after the -artifact is durably identified. Explicit network policy applies on create and -restore. Do not inherit undeclared host resources. -The native SDK owns a dedicated ext4 disk mounted at `/environment`, separately -bounded by `environment_disk_mib` alongside `root_disk_mib`. Workspace, staging -and outputs must share that filesystem; do not weaken cross-device or link -checks to accommodate the layered root. Creation uses `/` until bootstrap creates -the workspace. Existing full snapshots and sandbox cleanup own the disk, with -no external mount or separate storage lifecycle. Native restore may omit a configured -root-disk size because it inherits the verified full snapshot. Accept that omission -only with matching snapshot resource proof and exact source/target identity; inspect -other native limits before retaining the inherited proof on the restored target. -Never treat a missing root size as unlimited capacity or resize retained state. -Snapshot receipt observation uses ownership and artifact integrity checks, so a -resource mismatch cannot hide an existing snapshot from authorized cleanup. -Restore recovery may finish a missing derived resource proof on the exact target -of the verified snapshot, after checking its native limits. It must not repeat -Restore, start stopped compute or alter resource limits; reread the same target -strictly before returning success. - -Suspend only after at least one Turn is terminal, no queued/in-progress/waiting -root or subagent Turn, pending input/file operation or initialization remains, -and real activity has been idle for the configured interval. For node-managed -allocations, record the first root or child terminal transition in the same -transaction using Core's database clock and the existing compute activity field. -For every Environment source, positive native completion timestamps remain -unchanged in public history, even when host clock skew places them before Core's -Turn creation time. They cannot drive idle admission across hosts; repeated terminal projections never reset that timer. -Read activity together with the database observation time. Candidate filtering and -the Session-locked phase recheck compare elapsed database time with the configured -idle duration; callers must not supply a Core-wall-clock cutoff. Anchor the initial -snapshot retention deadline to that same database observation. Core and database -host clocks need not be synchronized for these decisions. -Heartbeats do not reset activity. The daemon must close admission and drain native -cleanup, output receipts and file work before acknowledging planned suspension. Never change a -harness or keep an agent process alive across Turns solely to meet this feature. -The acceptance boundary is a next Turn in the same Session with history, files -and configuration intact, without replaying an earlier request. - -The existing Worker lease, Session lock and per-node lifecycle gates own both providers. -New Turn claims, file-write intents and capture admission serialize under the -Session lock. Turn and file-write admission share the same compute-phase check; -existing receipts remain readable. New pending work cancels capture and wakes -the same source. Normal preparation waits for the -compute phase to be running, after the authenticated resume handshake; a pending -input remains pending if its promotion conflicts with a lifecycle transition. -Private compute phases and revision-checked JSON receipts live on the existing -allocation. Persist quiesce/capture/restore intent before effects; only the fresh -receipt performs a capture or restore. Recovery observes the exact attempt and -never retries an unknown creation, capture or restore. A consumed snapshot cannot -roll a running generation back. Deletion, revocation and retention expiry take -precedence over wake, including at the final database compare-and-swap. Retain -unknown cleanup identities until owned resources are confirmed absent. - -Database-owned guest CPU/memory and supported disk settings, max_active reservations, max_retained -allocation count and snapshot retention bound each assigned node. Unknown operations -retain capacity reservations. Source teardown must be confirmed before releasing -active capacity. Delete consumed artifacts and old compute closures; do not grow -a chain of old writable disks across suspension cycles. No Kubernetes, distributed -scheduler or snapshot replication belongs in this V1 profile. - -Queued work and live Environment file access request wake. History and published -artifact reads do not. Planned suspension uses private daemon wire 0.8.0 with an -Environment and suspension token; a PID/start-time fenced local control signal -wakes the parked daemon, which reauthenticates before admitting new work. A -transient disconnect before confirmation retries the same armed suspension with -bounded attempts and backoff; permanent authentication or protocol rejection -still closes it. Snapshot -lifetime has no daemon wall-clock timer: Core owns its retention deadline. A lost -quiesce acknowledgement may thaw the same source using explicit rollback control; -it does not authorize capturing it. Ordinary disconnect keeps the existing -conservative shutdown behavior. Authentication rejection cannot create a new -Runtime or replay a request. - -All normal `make check` gates still apply. Linux qualification additionally runs -the pinned helper module tests/build through `check-microsandbox-provider`, a real -KVM full-snapshot/reclamation probe, and idle-to-next-Turn integration acceptance. -Synthetic process-memory probes support the backend claim only; they do not prove -agent continuity. Independent blind review uses the clarified idle-only scope. - -Managed Runtime allocation, dedicated daemon credential hash and exact Session -binding commit atomically before Provider.Create, using the existing execution -lease and Session lock. Only the fresh allocation receipt permits Create; retries -and Core restart observe that same reference without replay or credential rotation. -The operator's stable provider key identifies one backend/installation; retain its -adapter for cleanup, and use a different key when changing the target. Never treat -absence on another backend as successful reclamation. -Disabling the default provider stops new hosted admission/bootstrap; it must not -block existing Session cancellation, tool results or input retry outcomes. -Input HTTP response budgets follow the persisted Environment type, covering the -admission wait for both hosted and self-hosted Sessions independently of operator -creation switches or remote executor configuration. - -With an explicitly configured default managed provider, the same Worker scans -committed pending hosted Environments that have no allocation. This includes idle -Session creation and recovery after commit-before-bootstrap interruption; an -existing allocation never enters that startup path. Keep the scan bounded and -serialized by the existing lifecycle owner. Hosted provisioning requires no caller -connection action. An initial reservation without a Turn leaves its Session idle, -as allowed by the pinned contract; do not emit an in-progress event before a Turn -starts or treat a daemon connection as native readiness. - -The same serialized scan publishes authenticated connection observations using -the existing durable generations after verifying the exact Session/device binding -and settled bootstrap. Socket loss remains observable during a provider outage; -Core restart fences old observations. Do not create a separate connection owner. - -Terminal managed cleanup atomically revokes authority, persists Environment failure -or expiry, settles pending input and requests cancellation before external cleanup. -Preserve original input deadlines and retry outcomes. Temporary provider outages, -unknown Create results and stopped compute do not prove permanent failure. The -pinned stream has no Environment expired event; do not invent one. Exact hosted -failure codes and ordering remain explicitly unverified. - -Allocation state is private compute ownership, separate from public Environment -connection/native readiness. Adapters qualify bootstrap completion; Core does not -infer it from an engine or provider name. Connected, observed compute receives -service keepalives between Turns. Keepalives cannot revive a one-hour lapse or a -cleanup request. The Docker provider keeps its current idle behavior; only an -explicit checkpoint policy may suspend completed, idle work as described below. A stopped/missing container -does not authorize discarding retained workspace or history. Session deletion or -expiry requests cleanup, revokes the scoped device and cancels pending work before -Provider.Kill; the existing Worker serializes these lifecycle operations and drains -them before releasing its execution lease. - -Keep the allocation after public Session deletion. Mark it released only after -owned compute/volume cleanup and evidence that its original Create has settled. -An unknown creation retains cleanup ownership even after an absence observation; -continue bounded scans for late resources without issuing another Create. This -conservative internal lifecycle does not define user-managed enrollment or prove -complete upstream expiry/error semantics. - -Qualify the actual Runtime before cutover: execution, file access, owned -cancellation, restart with retained history and files, and clear failure when -required history is missing. Daemon tools run with the launching account's full -permissions. Managed isolation is provided by the outer Environment. A user who -installs on a host does not receive a sandbox or protection from their own tools. -Keep failed probes and unverified platform combinations explicit. - -Before migration, archive existing edits and validation evidence. Reuse verified -authorization, resource/lifecycle ownership and safe filesystem primitives as -needed by V1. Stop work on separated-only enrollment, mirrored manifests and relay -mechanisms. Remove superseded unused code, configuration, tests, scripts and -task-owned temporary resources as each replacement is accepted. Preserve necessary -regressions, still-used official capabilities, product data and others' work. - -All Harness adapters use bypass execution. Do not restore named Codex permission -profiles, bubblewrap wrappers, native Claude sandbox settings or MiniMax -SandboxManager branches. There is one execution path for every Environment origin. -Resource paths are ordinary operator configuration, not a permission boundary. - -The daemon does not enforce disabled or restricted network policies. Such a -combination must be rejected unless its outer Environment implementation provides -and qualifies the requested behavior. Do not advertise daemon-level network -isolation or silently run a restricted request with unrestricted semantics. -The normal self-hosted combination uses the host's existing network access. - -A dedicated local Runtime uses one Environment-scoped device credential and an -immutable binding to that Environment's Session. It is excluded from general -device selection; another Session cannot claim it, including within the same -tenant. Deleting its Session invalidates credential lookup and heartbeat renewal. -Provision a new scoped device atomically rather than widening an existing shared -device credential. Revocation does not authorize silent placement replacement. - -The private local Environment reference contains its identity and, for policy-aware -execution, its immutable network policy. Trusted Runtime deployment configuration -freezes the Environment, Session and workspace root; -requests cannot supply a replacement root. The V1 path uses only the exact local -Environment reference. Use the same preparation/start lifecycle for native execution and the -existing bounded workspace controls for directory access. Local idle directory -reads use the common Go filesystem implementation without a temporary Harness. These private capabilities alone do not authorize public requests or establish -Provider lifecycle. Self-hosted enrollment supplies the exact local binding. -Core rechecks the persisted Environment/device binding for preparation and active -reads; capability discovery cannot select or authorize a general device for this -placement. Local work uses the existing pending-input reservation and Worker -ownership without a remote connection resolver. The hosted profile supports `network.access: enabled`, `disabled` and exact-host -`restricted`; each image must qualify the supported policies before public deployment. Omitted -network settings mean enabled upstream and must not be silently treated as disabled. - -Core and Runtime use common preparation, start, input-receipt, cancellation, -release and recovery semantics for Codex, Claude Code and MiniMax Code. Retain each -harness's native implementation behind its adapter. Core acts on verified capabilities and runtime -conditions; a capability declaration alone never grants public feature admission. -Extend existing interfaces during related functional work without introducing a -second framework or a broad rewrite. Codex, Claude Code and MiniMax Code have -qualified dedicated Docker profiles and historical Core-managed E2B evidence. -New user-managed enrollment requires separate real acceptance. Each harness has equal standing; -qualify each image/template with the common full-loop acceptance before deploying. -Additional engines remain separate work; V1 has no separate remote executor. -Later engines must satisfy the same applicable acceptance contract while keeping -their suitable native deployment layout. - -Workspace reads may request the private `workspace_read_only` preparation profile -through the existing preparation factory and verified `workspace_read_preparation` -capability. It accepts only the bound Environment and resource identity; execution -options, model/MCP credentials, native Session continuation and model/tool input are excluded. -The Codex adapter creates temporary local state, reuses its native connection and -directory transport, and rejects Start. Its child inherits only process/transport -essentials. The private native read mode excludes system, managed, user and project -execution configuration and plugin startup while preserving native security -requirements. Ordinary execution keeps its stable state and configuration. -Reject the legacy mixed managed-config profile for reads rather than discarding -its enforced constraints together with execution settings. -For this read profile, `released` is published only after local Close succeeds; -cleanup errors retain ownership and report `cleanup_unconfirmed`. A failed factory -must return its resource with the error if cleanup remains unconfirmed; wrappers -must preserve both values. Successful cleanup retries publish confirmed release, -and stale status snapshots cannot publish success. Failed terminal status delivery -does not retry cleanup; ownership remains until an explicit release or shutdown retry. -A release request, -HTTP disconnect or remote socket closure alone is not cleanup confirmation. This -profile does not establish remote mutation quiescence or public Files admission. - -Core directory reads reuse the Worker's Session scheduling reservation for idle -preparation and target the exact Run for active execution. Device selection uses -operation-specific capabilities; reading files never resolves model/MCP options -or creates a Turn. HTTP cancellation ends observation, not an admitted native read. -Keep the idle reservation through the bounded read and release attempt. Return -directory data only after confirmed Close; incomplete reads or uncertain cleanup -return unavailable without data. Release the Worker's scheduling reservation before -delivering the result so the caller can immediately request the next page. -Revoke the scoped read transport credential on -completion or failure. Runtime retains uncertain cleanup ownership and capacity; -this does not require a second durable Core owner registry or establish remote -write retirement. Public Files.list delegates workspace access to this reader; -the API owns tenant authorization, path validation and protocol pagination. Only -the reader's distinct `not_directory` result (a missing path, a regular file or an -unfollowed symlink) becomes an empty page; root, permission, transport and -uncertain failures keep their errors. Keep partial directory coverage and -unverified defaults explicit in the Files contract. - -Source Files belong to the execution project and have an independent lifecycle -from copied workspace files. Store immutable source metadata and PostgreSQL large -objects in the execution database with the pinned pgx driver. Upload validation, -metadata insertion and bytes commit atomically; deletion removes metadata and -unlinks the object in one transaction. Keep OIDs private and authorize every -metadata/content/delete lookup by tenant before opening a body. Stream bounded -chunks; never hold an entire general Files upload in memory or use filenames as -filesystem paths. Public source download admission is separate from internal byte -consumption: reject direct downloads of the supported `user_data` purpose after -tenant-scoped metadata lookup; initialization and workspace copies keep their -authorized Store read. Core Web must not offer that unavailable download action. -A read-only repeatable-read transaction preserves an admitted -source snapshot across concurrent deletion. Resolve that snapshot before entering -the existing Environment write path; deleting a source does not undo a completed -workspace copy. Bound request/transaction lifetimes, roll back incomplete bodies, -and never automatically retry ambiguous commits. Backups must include PostgreSQL -large objects; live deletion does not erase WAL or historical backups. Schema -rollback must not orphan existing source objects. Do not reuse product capability -tables or introduce a second destination writer for file_id. - -Session Artifacts are immutable published output copies, separate from live -workspace files and general source Files. A private output exporter must reuse -the authorized workspace path boundary and stream bounded bytes. Require complete -capture and confirmed helper/transport success before publication; valid archive -syntax alone is insufficient. The daemon owns and drains the exporter stdout pipe -separately from child reaping, so pull-transport backpressure cannot consume a -process-exit I/O deadline. After helper exit, bound each actual pipe read to one -second to reject inherited pipes that never close; reset that allowance after -consumer delays. Cancellation closes the owned reader and the dispatch consumer, -then waits for child settlement. An arbitrary blocked writer cannot be interrupted -by the exporter itself. Never extract an output archive into Core's -filesystem or hold the global execution lease through a large transfer. Keep -publication ordered with Turn completion, and authorize stored reads independently -of Environment availability so published outputs can survive its expiration. -Capture bytes into private PostgreSQL large objects without a Session admission -lock; after confirmed export, lock and recheck the live Turn before staging metadata. -Before capture, seal native input under that lock using the private capture marker. -Later messages reuse the existing Environment input reservation and await the next -Turn; the public Turn stays in progress until publication settles. Directory reads -during capture use an independent authorized read-only preparation, not the released -native Run; they do not request model credentials or mutate the workspace. Cancellation -retains the existing Turn/reservation semantics. Do not introduce a second queue. -Publish metadata in the same transaction as Turn completion. Failed/cancelled Turns -discard private objects, and Session deletion removes both private and published -copies. The exporter skips output symlinks by their `lstat` type without following, -opening or resolving them; hard links, other special files, device crossings and -concurrent changes still reject the capture. In that completion transaction, drop -staged paths whose sha256 equals the newest remaining published Artifact for the -path in the Session, so later Turns publish only new, changed or no-longer-published -paths and never modify existing Artifacts. Reuse the source-file snapshot reader -pattern and common content response; artifact deletion does not alter workspace -files. Hosted execution requires the -Runtime's bounded output-export capability and exact read-only preparation binding; -capability advertisement alone does not qualify an operator's deployment. -Exporter component checks do not establish public Artifact compatibility. - -Local inline file delivery uses the same authenticated daemon connection and exact -Environment/Session binding. All platforms use the daemon's Go implementation -for bounded file reads, directory listing, file creation and output export. -Files operations require no external helper executable or staging directory. -The Files API keeps workspace-relative paths and no-overwrite creation semantics; -it does not restrict native Harness tools' host permissions. - -Transfer a complete bounded body in acknowledged 64 KiB frames before invoking -the native file writer, verify the declared digest, and run no model for upload. -Keep the private 50 MiB transfer bound distinct from the official 5 MiB decoded -inline bound, which the API checks before any Runtime work. Files.create uses the -native writer's no-overwrite operation. Initial Session files retain their separate -atomic replacement behavior; do not change one caller's semantics for another. -The dedicated Runtime excludes execution while receiving or applying a write; -malformed, incomplete or expired transfers cannot reach the installer. Exact -commit/rejection receipts release the mutation owner. Missing or ambiguous -receipts retain uncertainty; observer cancellation and local process exit cannot -prove non-mutation. Before public admission, Core must durably reserve the write -under the Session lock and prevent successor mutation across restart until exact -settlement. Do not replay the request or introduce general replacement machinery. -Read-only operations retain their own authority and bounded ownership requirements. - -Keep prerequisites specific to the public operation being implemented. Native -harnesses execute; adapters translate protocols and fill demonstrated capability -gaps; Core owns public semantics, authorization and resources. Before adding a -mechanism, identify the current operation it enables and why existing native -capabilities or interfaces do not suffice. Durable metadata queries need no live -runtime. Live file reads require an authorized, isolated view of the exact workspace -and bounded operation ownership, but not a complete file-write, environment -replacement or placement-retirement implementation. Apply mutation fencing and -retirement guarantees where an operation can write, replace or retire that owner. -Read-only access still requires tenant/resource checks, path isolation and safe -failure when the authorized workspace cannot be reached; it never grants public -admission merely because an adapter advertises a capability. - -Use distinct authorization for callers, devices and environment connections. A -managed outer Environment must exclude broader application credentials and other -tenants' secrets. The daemon does not hide its own state from same-user tools. -Directory bindings and process identities do not provide filesystem isolation. -Preserve or demonstrably restore native history across -compute replacement; never silently move a bound Session or replay unknown work. -Self-hosted compute/files remain caller-owned, with explicit cleanup separate from -Session deletion. The full Environment implementation remains pending; follow the -[pinned contract and acceptance sequence](contracts/agents-api/environments.md) -and the [two-engine placement prerequisites](contracts/agents-api/workspace-placement.md). -For co-location, qualify the outer deployment boundary and the shared Runtime -lifecycle. Native tools use the starting account's permissions; do not claim a -daemon or harness sandbox. Host operators choose their own outer isolation. -Never enable an execution combination whose required outer behavior is unverified. - -The internal Store creates one Environment with an environment-bearing Session in -its creation transaction. The Session upsert selects the retry winner; retries -never create or repair associations. Environment identity/state live in their own -table. Tenant ownership and immutable configuration come from the owning Session, -without duplicated JSON, tenant columns or generated IDs in the creation hash. -Environment reads join that Session and exclude deleted Sessions; deletion retains -ownership for later settlement/cleanup. Existing `none` and legacy missing -configuration create no Environment, and historical internal snapshots are not -backfilled. Creation and recorded-intent retry snapshots load the Environment with -the Session row/cursor in the same transaction, without borrowing subsequent -activity or Turn state. Initial state is `pending`; authenticated connection observations follow -the lifecycle rules below. -Public creation supports `self_hosted` on the three enabled native profiles when -the daemon gateway is configured. The workspace is a clean absolute host path matching the bound Runtime root; -`/workspace` denotes that root through the existing logical mapping. Optional -capability directories are clean absolute host-local selections. Supported non-deferred functions keep their engine-specific -validation and native callback bridge. Enrollment binds the dedicated Runtime; -Session output uses the owned Environment association. Public Files reuse the exact -local workspace; populated self-hosted installation metadata remains unsupported. - -Environment retrieval uses the existing tenant-scoped join to a live owning Session -and its durable connection status, independently of a live Runtime or gateway setup. -It preserves project-shared read access and exposes only the pinned resource fields. -The current closed self-hosted configuration has no API-managed file, plugin or skill -installations, so those required arrays are empty. They are not a filesystem listing -or a claim about native discovery. Reuse the strict Environment configuration parser -and reject unsupported installation fields/capabilities or resource states instead -of treating unknown inventory as empty. No read initiates native work or changes -connection state; connection does not establish readiness or process quiescence. - - -The private Environment input reservation stores one canonical message batch before -Turn admission, with a five-minute deadline from the database clock. It requires -an Environment-bearing Session without active work. Reservation and direct input -paths share the Session lock and retry identity; pending or settled keys cannot -bypass the reservation through direct admission. A pending reservation blocks new -direct batches, including cancellation, while successful earlier retries remain -readable. Promotion commits the original inputs, history, reservation settlement -and execution claim (`queued` to `in_progress`) together; expiration and targeted -cancellation retain the terminal identity. Session deletion -is rejected while input is pending and changes nothing. A terminal reservation retry must not -affect a later reservation or Turn. Evaluate deadlines after acquiring the Session -lock, and return terminal storage outcomes without rolling their transaction back. - -Initial messages for a newly created Environment-bearing Session use that same -reservation in the creation transaction, including its connection-action event. -The creation winner alone inserts it; the stream cursor still precedes that -event, and creation retries never re-insert it. A durable initial/later flag -defaults historical rows to later input without inferring origin. Initial expiry -projects a failed Session and safe error before any Turn exists; later expiry -retains idle semantics. Failure -events capture the settled activity and Usage atomically. Late connections and -creation retries cannot reset or replay expired input, and newer work supersedes -old activity without changing its event snapshots. The Environment itself is not -failed by an input deadline. This Store rule covers actual self-hosted and internal -hosted associations; it does not enable hosted providers. None/absent Environment initial input retains -immediate Turn admission. Cancellation/deletion keep their existing semantics. - -Ordinary and streamed public self-hosted creation accept initial text through this -transaction after configuration and new-work lease checks. They return the owned -Environment ID and executor URL while offline, without waiting for admission. -The creation stream's `created` snapshot is the committed JSON 201 projection, -including the connection action; the committed action event then follows from the -creation cursor. A disconnected observer leaves committed input intact; -only the existing Worker prepares, promotes and starts it. Saved-Agent retries with recorded intent -recover before fresh execution admission or source resolution; inline retries keep -their existing resolved-snapshot validation. -Later live subscribers observe only future events and recover history through queries. +Toolchain setup and focused commands are in +[Develop OpenAgentCore](docs/development.md#set-up-a-checkout). CI coverage, +caches and release publication are owned by the +[maintainer guide](docs/maintainers.md#publish-a-version). -The public self-hosted input profile accepts message-only batches. Under the same -Session lock, recover the original reservation or direct receipt before choosing -current active input or idle reservation. Active messages use existing ordered input -receipts without a new Turn, preparation or reservation; idle messages retain the -readiness and promotion path, including already-connected environments. Pending -reservations keep their gate and deadline. An unlocked activity read or retry after -a conflict must never choose a different admission path. Mixed inputs remain gaps; -these restrictions do not narrow the pinned protocol target. Cancellation-only batches -use the existing direct admission after configuration and execution-ownership checks. -Only the Session-locked transaction chooses the active Turn or an idle receipt; -matching retries retain that target even during later work. Cancellation cannot -create a Turn or bypass preparation. A new cancellation still conflicts with a -pending reservation; it does not cancel pre-Turn input. Its 204 response confirms -durable admission, not native completion or process exit. Homogeneous function-result -batches also use direct admission after those same checks: their explicit Turn/call -identity selects an existing pending call, never new work. Reuse function validation, -Session-locked whole-batch receipts, preserved output/error fields and native -application acknowledgements. Matching retries remain bound to their original calls -after completion or during later work; new results cannot bypass a pending reservation. -Definitions remain fixed through preparation and cold native continuation. These -callbacks are not installed Environment metadata. Mixed result/cancel publication -and exact hosted action-removal timing remain separate gaps. -Promotion requires the current leased execution writer and the caller's -retained native preparation; never hold a database lock during external preparation. Only -the first successful non-replay receipts authorize Start on that same preparation. -An admitted retry returns the original receipts without reclaiming execution; a -read or uncertain commit never authorizes another Start. A crash after promotion -but before Start uses existing claimed-Turn reconciliation (`execution_interrupted`), -including unbound or deleted Sessions, rather than ordinary queued dispatch. Deletion -after claim is rejected like any active Turn. -The Worker expires at most 32 due reservations on each existing tick, after -checking ownership and before checking devices or execution slots. The sweep -requires the leased Store and uses its connection with the existing transaction -timeout; it never falls back to a pooled writer. A partial deadline index and -Session row locks with SKIP LOCKED let unrelated work proceed around contention. -The candidate cutoff is statement time; settlement rechecks the database clock -after acquiring the Session lock. This bounds mutations and transaction time, not -the number of examined locked rows. Restart resumes expiry on normal ticks without -a separate scheduler or backlog-draining loop. No failed Turn may stand in for a -pre-Turn connection failure. +### Full gate -Session activity before a Turn is derived from the latest relevant reservation and -authenticated connection state. Offline input requests `environment_connection`; -connection arrival clears that action to `idle`, while the prepared Worker still -owns native readiness and admission. An idle offline Environment alone requests no -connection. Reservation/connection changes commit immutable Session activity and -usage snapshots in the same transaction; SSE must not substitute a later Turn or -action set. A newer or active Turn owns subsequent activity. Settled non-initial -reservations clear their action to `idle` until newer work exists, even after an -earlier failed Turn. This local settlement policy does not establish hosted expiry -errors or initial-input asynchronous failure semantics; those remain unverified. +Run `make check` before completion. It includes: -Session GET/list/metadata responses and live SSE share the safe `self_hosted` -output projection. Its `remote_url` comes only from the daemon gateway -configuration, never request headers or a daemon address. Include -the owned Environment ID, workspace and capability directories without exposing -private configuration. The standalone Environment resource remains separate. -Acceptance must pass that exact URL and ID to the -caller-started executor and observe real remote execution through the existing -Worker, daemon and harness, with fixed SDK and raw HTTP/SSE checks. +- all daemon and shared Go tests, including native daemon filesystem tests; +- Core contract, client and service tests and standalone API builds; +- a real dedicated PostgreSQL test database and byte-for-byte sqlc checks; +- Core Web and TypeScript client checks, including fixture-only Playwright acceptance; +- Claude SDK tests and packaging, and MiniMax companion checks; +- the Core distribution and installer gates; +- `make check-example` for the optional application example (TypeScript, + proxy/persistence tests, build and fixture browser acceptance). Its synthetic + responses are not live model qualification. -A self-hosted input HTTP request returns 204 only after durable admission. Its -wait uses bounded pooled operations, outside transactions and execution lease -ownership; it cannot prepare or start native work. Only that route extends its -response write deadline to six minutes for the original five-minute database -admission deadline plus response grace. Request/observer disconnect stops waiting, -not the durable reservation or execution; retries keep the original identity and -deadline. The Worker remains the readiness, promotion and Start owner. Local failure -mapping uses 409 `environment_input_expired` / `environment_input_cancelled`, 503 -`execution_unavailable` for ownership loss, and existing 404 for deletion. New input -after a hosted provisioning failure returns the observed 409 `conflict_error`; other -exact hosted failure statuses/bodies and pending-input crash recovery remain unverified. -Principal acceptance must use public Session creation and input against the built -standalone service, including a wait exceeding its ordinary 30-second write timeout, -real remote commands/files and a second native-history Turn. Private provisioning -or injected API handlers cannot substitute for that workflow. +It excludes Parsar product Web and server gates. +### Test database -User-managed Runtime enrollment authenticates the existing principal executor key -against the exact live Session/Environment and its recorded creator. Keys retain -stable management IDs, immutable principals, optional exact-Environment restrictions, -rotation/revocation and digest-only storage. They grant connection authority, never -Session API access. No raw-token import or secret read-back is added. - -Enrollment atomically creates or recovers one dedicated device and immutable Session -binding under the Session lock. The frozen workspace and capability directory selection come from the Session -configuration and must match the local binding. Runtime snapshots declared local -Skill or Plugin directories before creating the native executor. No `runtime_allocation` is -created for user-owned compute. A retry cannot replace a device, change its bound -key or adopt another native history. Gateway authentication and dispatch recheck -current key authority; rotation/revocation and deletion deny further use. - -The daemon, selected harness, local tools and workspace run together. The Dispatcher -passes the existing typed `LocalEnvironment` after exact tenant/Session/Environment/ -device checks. Native preparation, Files and Artifacts reuse the same protected -local workspace and existing lifecycle owners. There is no registry/Noise relay, -transient harness credential, service-side harness or remote tool forwarding path. -Keep model credentials, daemon authorization and native histories private; connection -success alone establishes neither native readiness nor filesystem isolation. - -The private daemon `workspace_read` control targets an existing preparation handle -or its transferred active Run on the same authenticated device connection. Require -the exact frozen Environment identity; callers cannot supply sockets, credentials -or workspace roots. Shared routing uses the optional `agent.WorkspaceReader` -interface, without selecting an engine by name. -The same control accepts `operation: directory` through the optional -`agent.WorkspaceDirectoryLister`, with mutually exclusive byte/entry limits and -typed directory metadata. Directory responses carry at most 1024 single-component -UTF-8 names of at most 255 bytes, so escaped metadata stays below the existing -frame bound. These are private transport limits, not public Files parameters. -Byte and directory operations share target checks, correlation, capacity and -retained operation waits; neither creates a Run or selects an engine by name. -This control does not itself authorize a public Files endpoint or placement. - -The separate optional `agent.WorkspaceDirectoryLister` observes one workspace-relative -directory on the existing Prepared/Session owner; empty path selects its root. -Return single-component names, entry kind, regular-file byte size and explicit -truncation only after directory/metadata access and handle cleanup settle. Reuse -byte-read admission, uncertainty and caller-detach ownership where applicable. -Do not promise a snapshot, recursive traversal or public pagination through this -private interface. The Runtime invokes the retained directory helper against the -frozen local workspace. Keep the qualified helper outside writable paths, anchor -traversal to no-follow descriptors, bound enumeration and require a complete -validated result. Helper availability alone does not enable public Files admission. - -Bound encoded request payloads to 8 KiB and correlation IDs to 128 bytes before -admission. Do not echo oversized IDs; omit oversized trace metadata in replies. -Bound raw control results to 1 MiB within the existing 4 MiB transport frame; the -limits are local policies, not pinned public protocol limits. Successful reads require complete bytes/truncation and acknowledged native -close. Safe native rejections carry no bytes; interrupted or ambiguous reads remain -unknown and stop further reads on that owner. Local RPC reap never establishes file -settlement. Retain a dispatched read's original bounded waiter across observer -cancellation and resource transfer/release; stop new admission on resource closure. -The gateway bounds subscriptions and never retries or replays on reconnect. Duplicate -pending operation IDs cannot start another read; this control does not promise durable -idempotency or result recovery. Preparation/Run ownership, public path authorization, -public file authorization and native cleanup retain their separate requirements. - -The retained Rust directory, write and workspace-export helpers provide bounded -filesystem operations for the colocated Runtime. Their protected executable paths, -descriptor-relative traversal and exact workspace binding remain required. They -do not implement an executor transport or grant tenant authority. - -Connection observations use the existing execution lease and Session lock. A -separate `environment_connections` row retains the current generation and revision; -`environments.status` and its Session Environment-event snapshot commit together. -The producer serializes replacements, then numbers socket observations within each -generation. Duplicate or older revisions and superseded generations are inert. -Replacement retires a previously connected observation before publishing its new -registration. Registration alone creates no connected event. Event payloads contain -only public Environment identity/type/status and nullable error, never configuration, -credentials, registration IDs or revisions. Transport observations have no asserted -Turn association. `connected`/`disconnected` are distinct from native preparation -readiness; do not cast resource `expired` into the event vocabulary or emit `ready` -for a self-hosted connection. - -The existing Worker observes authenticated daemon peers for enrolled Environments, -using durable generation/revision fencing under its execution lease. On restart it -reconciles old connection observations before admitting new ones. A connection or -heartbeat does not prove native readiness or process quiescence. Failed or stale -observations cannot establish a current connection. - -A Session owns one reusable Executor in its connected Runtime. A Turn owns one -input execution, its output stream and its cancellation. `agent.ExecutorFactory` -prepares the fixed configuration; `Executor.StartTurn` creates a new `agent.Turn` -without replacing healthy native resources. Normal completion settles only the -Turn. `Executor.Close` releases native resources on idle expiry, environment -shutdown or confirmed invalidation. Core does not keep a second Executor cache. -The same lifecycle applies after managed or user-managed environments connect, -and to the qualified no-environment profiles. Resource management owns machine -selection, allocation and Environment creation/reclamation. Closing an Executor -does not release the Environment allocation or delete its workspace. Environment -reclamation explicitly coordinates with Runtime execution. Connection, installed -capability snapshot, Session Executor and Turn retain separate lifetimes. - -The Runtime binds its Executor record to Session, Environment, connection and -immutable execution configuration. Resume identity and prior-Turn recovery flags -are continuity assertions, not configuration changes. A supplied native identity -must match the retained owner; exact-history recovery never starts a new root -when existing history is required. A configuration conflict is an error, not a -hot switch. Lost connections retire their owners and handles. Old timers, output -and cancellation cannot affect replacements. - -Each Turn receives a fresh wrapper, output channel and receipt state. Optional -steering, functions, permissions and user-choice interfaces belong to that fixed -Turn. Native callbacks capture the originating Turn before asynchronous work; -late events cannot be assigned to whichever Turn happens to be active. Native -processes, query/transport connections, fixed capability configuration and native -Session identity belong to the Executor. Do not reset completed `sync.Once` -values or repurpose an old Turn object. - -`StartTurn` returning nil guarantees that no native input was submitted and the -output channel was not retained. The Runtime then closes that channel. Once input -may have been submitted, return a non-nil Turn even with an error: the Turn owns -exactly-once output closure and remains tracked until settlement. Unknown input -is never replayed. A definite `executor_unavailable` Start rejection permits one -common recovery attempt only after the previous Executor has been closed and no -input was submitted. Recheck the same physical peer and current authorization. - -`Turn.Cancel` targets only that Turn and does not close a healthy Executor. -`AwaitSettlement` applies after both natural completion and cancellation. Success -means output can no longer be written and the Turn's native events, input, -functions, interactions and child work have settled. Native completion or -cancellation confirmation is independent of resource retirement: closing a -transport cannot supply missing native terminal or operation receipts. -`Reusable=true` additionally -confirms that the native owner can accept the next Turn. `Reusable=false` requires -a reason and subsequent confirmed Executor close. An error means settlement is -unconfirmed; it cannot free ownership or capacity. Caller deadlines stop waiting, -not tracked cleanup. Retry the same cleanup target serially. Failed cleanup -blocks replacement and retains its resource slot. Executor Close confirms resource -retirement independently of the Turn outcome: an immutable Turn error must not -prevent closing the native transport and releasing resources once their work and -output have stopped. - -One output consumer starts before native Start, drains the bounded 64-frame -channel, and retains the terminal observation until Start publication, Turn -settlement and admitted operation receipts finish. Natural Done never calls -Cancel. Input, function and interaction admission close before settlement, and -operations already admitted hold their barrier through native receipts and -outbound acknowledgement. Send cancellation to the fixed Turn before waiting -for that barrier: a written input may need native interruption to produce its -receipt. Join native settlement, any required confirmed Executor close, output -drain and all admitted operations before an applied acknowledgement or reuse. -A failed Close may report failure while retaining the same Run and outstanding -operations for retry. Closing a caller wait cannot manufacture an applied input receipt. Only then forward Done or an applied cancellation -receipt. Commit native continuity and release the old Run admission before -publishing Done, since the receiver may immediately start another Turn. A late -terminal-send failure belongs to the old Run; it cannot invalidate a successor -that already owns the Executor. Connection shutdown owns transport-loss cleanup. -Preserve the ten-second settlement wait and separate five-second receipt -send budget; timeout is not proof of quiescence. The observed cancellation outcome -retains native identity, Usage and output without fabricating missing evidence. -Codex native Turn interruption can leave background terminals alive. Its adapter -uses the exact thread-owned terminal list and confirmed per-terminal termination -before settling cancellation, and repeats this cleanup during Executor close. -The bulk clean acknowledgement does not prove termination. Failed cleanup keeps -the native owner available for a later close attempt. - -Private preparation controls reserve a per-Turn admission, not a new Executor. -They carry an explicit Session identity and immutable configuration without model -input or Run identity. A fresh request returns a connection-local admission handle -and the owning Executor ID. Start supplies both identities and its actual Run ID -and ordered MessageInput. Per-admission revisions order status observations; -rejections describe control errors without inventing Run events. A reused healthy -Executor returns ready without native preparation. An admission release abandons -that admission; it does not close the Session's healthy idle Executor or cancel a -later Turn. Cancellation uses the exact Run identity. - -Preparations and Start execute outside the receive loop and Router lock. Admission -expires after five minutes; retries do not extend that deadline. Bound active -preparation and execution separately from idle retained resources, and count -closing or uncertain resources until cleanup succeeds. A definite -`execution_prepare` rejection with `preparation_capacity` leaves an unclaimed -queued Turn for the existing Worker scheduler to retry, including capacity held -by cleanup. Other errors and uncertain input delivery do not authorize replay. -At most 64 admission records are retained; old handles never consume replacement -admissions. Idle expiry -is a Runtime resource policy, not Core active-Turn concurrency. Shutdown tracks and -closes active and idle Executors, retains failed close targets, and allows a later -serialized retry. Ordinary disconnection closes the failed transport and keeps -the exact Router until shutdown succeeds. A wait timeout or failed cleanup cannot -authorize reconnect; process shutdown also keeps waiting rather than silently -discarding owned native resources. These records are connection-local, not durable -input replay. - -Read-only workspace preparations remain separate bounded filesystem operations; -they cannot start model work. Workspace operations retain exact binding and -settlement rules across Turn boundaries and Executor closure. - -Successful input commits send a coalesced hint to the existing Worker scheduler. -The scheduler keeps lease, capacity, cursor fairness and per-Session ownership -checks; a hint does not admit work itself. If capacity is occupied, preserve one -rescan for completion without turning failed preparation into a busy retry loop. -HTTP readiness waits and active input delivery subscribe before reading relevant -state and wake after committed promotion or input. They recheck storage after each -hint. Polling remains the fallback for external writers, expiry and lost hints; -notifications contain no execution authority and no durable input data. - -Core readiness, Start acknowledgement and input-to-first-text logs use a single -process monotonic clock. Start acknowledgement confirms adapter ownership, not -model input consumption. -Runtime logs identify Executor creation, reuse, idle and close independently of -Turn completion. Do not call these durations model-only latency or subtract clocks -from different machines. Native process creation and same-owner successive Turns, -real provider results and same-condition timings must substantiate reuse claims. - -The Dispatcher prepares pending Environment input only on its exact enrolled or -managed device. Preserve the same physical peer and preparation handle through -readiness, atomic promotion/claim and the first non-replay Start. Derive workspace -and Environment identity from Store ownership, never caller-selected private paths. -Initial prompt/cursor come from the reserved batch; later messages use ordinary -steering. Never hold a database lock during native preparation. - -Observe the original pending deadline, cancellation, deletion and peer loss while -waiting for readiness. Preparation failure leaves pending input and its deadline -intact unless storage has settled it; it creates no failed Turn or input history. -During Start, consume preparation controls alongside the ordinary Run stream so a -control-only rejection or pending-start cancellation can settle promptly. Reuse -ordinary journal, receipt and completion/native-history persistence. Once cancellation -is sent, preparation errors/closure cannot replace its receipt or timeout path. -Pending-start cancellation uses the adapter's observed outcome after daemon handoff -and output forwarding. Missing or unconfirmed outcomes still fail conservatively; -preparation control errors cannot substitute for the cancellation receipt. -Do not fabricate an empty cancellation outcome or infer native quiescence. -The connection owner spans preparation and the transferred Run without a reservation-derived Run -deadline; every exit releases it. - -The existing Worker scans pending inputs using the same bounded scheduling slots, -Session locks, durable deadlines and engine capability checks. Keep the one-second -Environment-input scan cadence and at most 100 candidates per scan. At EOF after a -nonempty cursor, refill the first page once in the same scan; an empty queue must -not spin. Advance the cursor before readiness checks so an unavailable Runtime -cannot starve later candidates. Preserve the configured execution concurrency (default four) and alternation -between ordinary Turns and Environment inputs. A self-hosted Session -waits for its dedicated enrolled device; it cannot select an arbitrary same-tenant -device or migrate an existing binding. Preparation failure can retry while still -pending without extending the deadline. Preparation retries share this scan cadence; -execution concurrency bounds simultaneous work, not attempt frequency. Managed-provider lifecycle -polling retains its separate five-second interval. Unknown promotion results or -errors after admission retain the existing no-replay settlement rules. - -`OAC_PUBLIC_URL` enables the private gateway; Core derives the public -`remote_url` from it. `OAC_HARNESSES` explicitly adds deployment-supported -engines to the default engine and configured managed profiles; advertising a -heartbeat alone does not enable an engine. The three native profiles share enrollment -at `/workspace`. Their new user-managed public chain requires fixed-client/raw HTTP, -real-model, recovery, cancellation and credential-lifecycle acceptance separately -from prior Docker or retired remote-executor evidence. - -#### Independent build artifacts - -`make build-agents-api` produces `oac-core`, `oac-core-migrate`, -`oac-core-device` and `oac-core-environment-key` under `${OAC_DEV_HOME:-$HOME/.oac}/build/oac-core`. -`OAC_DEV_CORE_BUILD_DIR` may select another absolute output directory. The build -uses only the explicit source set in `scripts/build-agents-api.sh`: the execution -service, its Go contracts and required shared daemon/logging packages, plus the -root Go module manifests. Product server/frontend, other applications and their -migrations/assets are absent from the temporary build context. Keep this boundary -explicit when introducing shared dependencies; do not copy the whole repository -to make an accidental product dependency compile. - -The build uses Go directly with workspace discovery and CGO disabled, read-only -module manifests and trimmed paths. It requires no Node, Docker or product setup. -`make check-agents-api` runs this build before its tests, so the full `make check` -and the dedicated CI workflow enforce the same boundary. CI exercises the built -migration command and uses the built server for official-client HTTP checks. -`make docker-build-agents-api` reuses that build for Linux amd64 and sends only -its executables, the E2B helper and `services/agents-api/Dockerfile` to Docker. The -digest-pinned Debian slim runtime runs without root, product assets or an embedded -harness. Keep runtime credentials outside the image and migrations explicit. -`make check-agents-api-container` runs the existing official-client suite against -the image with a read-only root filesystem; it requires Linux Docker, a non-root -host user, the pinned SDK and a dedicated execution test database. Dedicated CI -runs this after binary validation. Changes to the image/build path require this -check in addition to `make check`; do not make ordinary Go builds require Docker. -Registry publication, additional runtime architectures, daemon packaging and -product cutover remain separate work. - -`make build-agents-api-release` reuses the isolated build for a Linux amd64 archive -under `~/.oac/`, with its four commands, license, operator guide, source/tree and -protocol manifest, and file/archive checksums. It requires clean committed source -and Python 3.9+, stages output privately, and packages fixed artifacts deterministically. -Keep runtime configuration, credentials, product sources and separately installed -daemons/harnesses out of the archive. Archive changes require content/hash and -fresh-extraction operator checks plus `make check`; execution acceptance uses the -packaged operators and public protocol, not private Store provisioning. Preserve -the database and native history when replacing the API package. This target does -not publish a release or provide an installer/supervisor. - -The archive stays Docker-free. Its former Docker-hosted variant -(`AGENTS_API_RELEASE_RUNTIME_IMAGE`) is retired and the builder refuses the variable: -an image ID alone is not the complete Runtime release a Docker deployment needs. -Docker-hosted deployments use the matched Core distribution below. The archive and -the standalone container are advanced paths for running Core alone; keep them off the -newcomer installation path and document them under -[Maintainers and advanced deployments](docs/maintainers.md). - -#### Matched Core and console distribution - -[Configuration](docs/configuration.md) is the canonical operator parameter reference. -[Installation options](docs/getting-started/install-options.md) owns installer usage; -README Quick start and the installation guide link there instead of copying option -lists. Generate its flag-to-key table and the configuration reference from the schema. -`host` is the single Core/Web listener address; `ports.web` uses only `--port`, and -`ports.core` uses `--core-port`. Defaults, validation and flag mappings live in the -schema. Flags seed config.json; health checks, setup, apply and generated service -files derive their addresses from that same config. Apply uses the last applied -address to contact running services before changing listeners. Non-loopback binds -require the existing HTTPS public origin; PostgreSQL remains loopback/private. -Every process setting has one home: the installation's private `config.json`, -described by `deploy/install/config.schema.json`. The operator edits only that -file; `oac apply` validates it, derives `generated/` (Compose file, `core.env`, -native unit, Core key digest file, settings snapshot) and converges on what actually -runs: each service carries the digest of its inputs (Compose label -`io.oac.inputs`, native `OAC_INPUTS`), and exactly the services whose running -inputs differ are recreated or restarted. Decide restarts from what runs, never -from recorded bookkeeping, so the next apply finishes any interrupted one. Installation flags only seed it, and -rerunning the installer rejects them. Runtime settings stay in PostgreSQL and -change through Web or `/core/v1`. Secrets live once each in `secrets/`; identity and -install facts live in tool-written `state.json`. State format 2 uses an `oac-` Compose -project; config.json's independent schema format stays 1. The operator command is -`oac` (`oac_cli.py`, packaged as `oac.pyz`), with default installation directory -`~/.oac/core`, private `~/.oac`, generated `x-oac` annotations and `.oac.lock`. -The project has no historical installation compatibility or in-place version upgrade -contract. Install only into an empty directory, or repair the exact same source -revision. Refuse old formats, conversion journals and different revisions before -installation mutation; retain their data and direct operators to reinstall separately. -The installer and every mutating `oac` command share the stable `.oac.lock` inode. -The installer owns this lock across creation, payload/native/launcher repair and -apply, invoking the already-locked apply implementation without nested locking. -Never unlink or replace the lock, including after an interrupted fresh install. -Current-version interrupted apply and rotation retain their existing recovery path. -The packaged `oac.pyz` entrypoint embeds the build source revision and checks it -against the existing `state.json.source_commit`; this adds no installation state -format. Node `--update` is refused; runtime generation operations are unchanged. - Core process settings use `OAC_*`, Web settings use `OAC_WEB_*`, and shared Go -logging uses `OAC_LOG_*`. Retired settings fail startup even when empty or when -the new name is also set; report every matching name without values. No supported installation entrypoint converts pre-rename files. Core and operator executables -are `oac-core`, `oac-core-migrate`, `oac-core-device` and -`oac-core-environment-key`; Web is `oac-web`, and the Core-host E2B helper is -`oac-e2b-provider` under `/opt/oac/e2b` in the image. -Core still reads only its -environment and has no config loader; it serves the non-secret snapshot at -`GET /core/v1/installation`. Keep the schema, the subset validator -(`config_model.py`), the generator and the generated reference table in -`docs/configuration.md` (`scripts/config-reference.py`) in step. Do not add a -second operator configuration file, loader precedence, hot reload, compatibility -reading of retired names, or an embedded Core node. `install.sh --sandbox` calls -the ordinary administrator API once; PostgreSQL owns the resulting selection. -Administrator-issued enrollment approves capacity (default two active/eight -retained); a node cannot supply or overwrite those limits. Downloaded specification -copies remain validated against the existing database-owned resources/Runtime -contract. - -The E2B template builder assigns traversable modes only to synthetic public archive -ancestors. Runtime file and directory permissions, private build contexts, key inputs -and output umask remain unchanged, including when invoked under umask 077. - -The installer packages Core and the Web console together, -with independent `--core-only` and `--web-only` modes. `site/` is the public static -landing, separate from `apps/web`; it must not create an onboarding prerequisite, -call a model, or claim complete protocol compatibility. Use Web's page and action names in user docs, and `OPENAI_BASE_URL` and -`OPENAI_API_KEY` for application examples. The [documentation ownership](#documentation-ownership) -map defines the authored sources. - -A distribution carries a fixed list of docs (`BUNDLED_DOCS` in -`scripts/core-distribution-manifest.py`). The build keeps relative links between -bundled docs, rewrites every other relative link to the same file on GitHub at the -bundle's commit (`@SOURCE_REVISION@`), and fails on a link or anchor that does not -resolve; `make check-distribution` runs the same check on the repository's docs. Keep -the list self-consistent when adding or moving a doc the installer or its output -refers to. - -`make build-core-distribution` builds from clean committed source and reuses the -existing API, Runtime, SDK, helper and Web builders. Artifacts record source and -immutable image identities, the actual Runtime manifest digest, checksums and -microsandbox runtime/firmware hashes and executable native payloads. Local distribution builds do not publish. The tag-triggered release workflow -reuses the full repository check on the exact build source, then publishes the -matched assets automatically; manual runs remain artifact-only or draft-only. -Only publication receives repository write permission. Never overwrite release -assets or move an existing version tag. The [maintainer guide](docs/maintainers.md#publish-a-version) -owns tag syntax, prereleases and failed-publication recovery. -Release assets include `deploy/install-release.sh` as standalone `install.sh` -with a checksum. This public downloader resolves latest once (or a selected tag), -verifies the control-plane archive before safe extraction, and delegates to that -bundle's installer. Default installation downloads Core, Web and PostgreSQL payloads, -never the Runtime image or node execution artifacts. Offline archives remain an -explicit distribution option. It introduces no separate installation state, upgrade path or login flow. -Build/test success is distinct from real-model qualification; maintainers assess -that evidence before pushing a release tag, and no synthetic result substitutes -for native execution acceptance. - -Distribution `images` records each exported image's config digest; -`image_manifest_digests` records its OCI manifest/index digest. Derive and verify -both from the same archive, including its referenced config and layer bytes, and -require the build host's selected image ID to match one of them. Docker's classic -store identifies images by config, while its containerd store uses the OCI -descriptor. The builder therefore selects the digest from BuildKit's build metadata -that the local store resolves, never the `--iidfile` config digest alone, and -disables provenance attestations so each image and archive holds one platform -manifest in both stores. For the same reason the default PostgreSQL input is pinned -by its linux/amd64 platform manifest digest: a pulled multi-platform tag keeps its -whole index in the containerd store, and that export holds every platform. Core, -node and self-hosted installers share one resolver -for these required identities: confirm Linux amd64 and the returned immutable local ID, -then use that ID in service/provider configuration and Runtime launches. Tags do -not replace identity verification. The microsandbox-qualified `runtime_ref` -remains independent of Docker's local store identity. - -The manifest is the shared download contract for Core, node and self-hosted -installers: flat versioned filenames, compressed Runtime size/hash and unpacked -size/hash. Nodes obtain bootstrap metadata from their configured console (or a local offline -bundle). Web serves locally available artifacts first; for missing declared execution -artifacts it redirects the node to the versioned HTTPS release base in the verified -distribution manifest. Web does not download or cache those bytes. Only artifact -requests may follow HTTPS redirects, without credentials or cookies; metadata and -enrollment requests must remain on the configured console. Nodes retain size and -SHA-256 verification, resumable transfers and immutable release selection. Download into -private temporary files, verify before atomic promotion, and reuse only verified -cache entries or exact image identities. Core's default image must not acquire -execution-only payloads. Python zipapps bundle the shared resolver with each -remote bootstrap; the console publishes only fixed non-secret files and declared -artifact names. Candidate build automation creates artifacts and may create an -unpublished draft; a successful build is not real execution qualification. A -separate existing-host batch controller may publish that draft automatically only -after directly supervised real qualification and verified batch landing. Repository -visibility is public. Published release downloads are anonymous and must not -require GitHub login or repository credentials. -Manual builds use the legal `build-` release tag; tag-triggered -builds use the actual `v*` tag. The manifest download base and draft tag must match, -while artifact filenames and source provenance retain the full source SHA. -Qualify the exact downloaded production artifacts before publishing the draft; -keep qualified executable, image and source payload bytes and source identity -unchanged. A recorded release-address/checksum-only repack requires proof that -every other archive member is unchanged and verification of final published asset -digests and URLs. Never use an acceptance -image containing a private test CA or model credential as a release input. -Repository visibility is independent of publication. Do not add repository -credentials to installed node/Runtime configuration to bypass download access. - -`scripts/promote-qualified-release.py` requires an explicit full candidate source -SHA, binds it to the archive manifests and matching `build-` tag, and uses -existing local gh authentication and SSH. It uploads/downloads the complete -matching thin/offline/Runtime asset set and verifies archive members and asset -hashes. The separate qualification package is supplied by the maintainer with an -explicit reviewed manifest SHA256. Its complete file inventory, ordered Python -commands, bounded stage timeouts and private path/resource configuration are -verified before any Release mutation and again by the remote supervisor. Candidate -assets cannot select or replace this execution package. Keep host-specific -acceptance scripts, usernames and credential paths outside this public repository; -never put credential values in either manifest. - -The reviewed adapter receives a fresh canonical UUID and exact inventory over the -authenticated command channel. It directly supervises fresh-install, -current-lifecycle, managed-native-smoke, diagnostics-observations-smoke and -node-runtime-smoke in that order. This batch uses one fresh container installation, -one completed managed Session, read-only diagnostics for that Session, and one -current Runtime Session on one new node. It does not rerun the full multi-host, -generation or GC matrix. Every child must exit successfully and -return only its own passed check, the current controller identity and its observed -owned resources. Resources and the previous result flow between live children; -a supplied pass file, skipped check or old report cannot release the candidate. -Verify package and candidate bytes again after each stage. The small shared -`qualification_control.py` is pinned to the reviewed tooling commit and private -package. A live SSH stdin channel carries the request then heartbeats; EOF, timeout, -SIGTERM or SIGHUP stops later work. Each local stage or remote worker has one -foreground process group and a waiting owner outside that group. The owner cleans -the group on success, nonzero exit, timeout and cancellation, including foreground -descendants orphaned by an inner timeout or SIGKILL. Nested foreground commands -inherit the group; only explicitly recorded background resources may detach. -Those retained background resources are outside foreground cleanup. Private nested -workers use the same channel. -Already-issued writes may have unknown outcomes: retain intents/resources and do -not replay or claim rollback. Control tests exercise -short-lived fixture children only and never establish live qualification. - -The caller supplies the independently reviewed promotion-tooling commit. Its -changes from the candidate may only affect the exact promotion files enumerated -in the controller, including CONTRIBUTING, docs/maintainers and the current-batch -node-generation protocol wording correction; the Makefile -exception permits only registration of the controller and control-channel tests. Main must contain the -candidate source and have the reviewed tooling commit's tree. This permits normal -merge commit identity changes and release-only documentation updates without -rebuilding or relabeling the original candidate. The candidate's bundled docs and -source archive retain source 48 (CONTRIBUTING and the node-generation protocol -are present through the source archive, not as direct bundled docs); new release instructions live in the tooling -commit. Product changes or a different main tree block promotion of the old -candidate. Never infer batch membership from all open PRs or automatically merge -them in the publication command. - -Use one controller invocation for the batch. After successful qualification it -waits in the same process, within the explicit merge-wait budget, for the exact -reviewed batch tree to reach main. An ancestor main waits; conflicting main changes -fail immediately. Cancellation or expiration retains evidence and cannot turn a -saved result into resume authority. It verifies unchanged draft identity, -target, tag and downloaded bytes immediately before publication. After the final -download it rechecks main/tree/tag and the same draft ID, then updates that verified -Release ID directly rather than resolving the tag again. It checks the -published bytes afterward. Conflicting assets are never overwritten. An interrupted -run is reconciled before another invocation; stored qualification output is evidence, -not a resumable permission to publish. Preserve its isolated local/remote evidence -and installation resources. No runner, background service, new GitHub secret or -repository-visibility change is required by this finite batch path. - -Executor credentials are issued by the operator with the Core key, through Web or -a Core-key script, under -`/core/v1/projects/{project_id}/environments/{environment_id}/executor-credentials` -and reuse the existing restricted issuer. The target must be a self_hosted -Environment of that Project whose Session exists; anything else is 404. The -Project's principal is the credential's execution principal, its scope stays -daemon enrollment and connection for that one Environment, and issue, rotate and -revoke each record an administrator audit entry in the write's transaction without -the secret. Project API keys cannot issue them. Native self-hosted installation runs the daemon on Linux, macOS or Windows with -its starting account's permissions. It owns no sandbox node or Core allocation, -adds no isolation and retains user-owned native history after uncertain launches. -Report started, connected and real execution success separately. -Native installation uses `oac-daemon install` and `start` with an explicit -credential-file path and the same `OAC_RUNTIME_HOME` for lifecycle commands. It is -current-version only and does not adopt an older container installation. Rotation -replaces the configured credential file for the same key and restarts the daemon; -never create a replacement Session history to recover a credential. The console's -older container-installer command remains a distinct packaged workflow, not the -native installation interface. Report connection and actual execution separately. -Core-key executor credential lists expose a required connection observation with -never_enrolled, connected or disconnected status, immutable bound key identity, -enrollment time and last authenticated heartbeat time. Read credential metadata -and binding facts in a closed read-only snapshot, then reuse runtimeenrollment's -current authority and actual gateway peer checks. Recheck executor and device -authority after reading the peer; rotation, revocation or Environment retirement -must not inherit a former key's connected state. No gateway means not connected, -never an authentication bypass. Known authority loss is disconnected; storage -errors remain errors. Keep digests/device IDs internal and public /v1 unchanged. -Connection timestamps are history, not execution/native/model readiness. - -Self-hosted installation confirms connection through the private daemon transport -using only its restricted executor credential. The read checks the exact live -Environment/key binding and current authenticated connection; it never enrolls, -allocates, wakes a sandbox or grants project resource access. It is an `/api/v1` -machine route that reaches Core directly, never through the console. Bounded -polling retains the original Runtime identity and history; timeout is a -diagnostic failure, not permission to replay initialization or replace history. The installation public URL -(`public_url` in the installation's `config.json`, seeded by `--public-url`, and -`OAC_PUBLIC_URL` for Core) is the one origin for -applications, nodes, sandbox guests and self-hosted executors, and also the console -origin. Core derives the daemon `wss` URL, the self-hosted `remote_url`, hosted -Runtime bootstrap and the deployment's read-only `core_url` from it; the deployment -API does not accept a Core address, and no deployment row stores one. Bootstrap -never uses request Host or caller-supplied placement fields. Enrollment names the -Core address the node uses; Core refuses one that is not the public URL (409, -token unconsumed) and records it. After the public URL changes, a node receives no -new sandboxes until re-added. This does -not widen sandbox network policies or change credential admission. - -The distribution build sets umask 022 for non-root-readable payloads; installation -credentials and state retain their explicit private permissions. -For a system node installation, capture the trusted bootstrap bytes before -dropping to the service account. Pass those bytes through the fork; the service -account writes its own retained generation helper. Never make the caller's private -download directory accessible or let root write into service-owned state to -work around bootstrap access. - -Installer progress describes the operation about to run. Do not imply fresh -health checks on a no-change repair. Keep terminal styling optional, honor -`NO_COLOR`, and preserve plain redirected logs. Summaries show credential file -locations, never their values. `install_display.py` owns shared terminal formatting; -`install_output.py` and `node_output.py` own their respective completion guidance. -Ship and checksum the display modules, including them in both the distributed node -bootstrap and retained helper. A node summary reports success only after Core -connection and provider readiness are confirmed. Service-user output stays plain -and passes through the existing terminal-control sanitizer. - -The Core/Web installer uses the launching account, including root, and a writable -installation directory. It never invokes sudo, switches accounts or changes host -Docker permissions. Check actual platform, Docker and directory prerequisites; -root alone is not a reason to refuse installation. Native Core retains its -systemd user-manager and lingering prerequisites for that same account. - -The first installer targets a trusted Linux amd64 Docker host. It installs a -private dedicated PostgreSQL service and separate Core and console services in -Compose by default, with zero execution nodes. The default requires neither KVM -nor systemd user services, imports no Runtime image, mounts neither the Docker -socket nor host devices into Core, and never adds its own host as a node. -`--sandbox docker|microsandbox|e2b|none` (default `microsandbox`; `none` and nothing -else with `--web-only`; `docker` prints its weaker isolation and needs a y/N -confirmation or `--accept-docker-risks` before anything is created) is a one-time -install action: once the services are healthy, the -installer POSTs `/core/v1/sandbox/deployment` as Web's setup would, and never on a -repair. It is not written to `config.json`; PostgreSQL owns the -selection. E2B needs a non-loopback HTTPS `public_url`, `--e2b-api-key-file` and -`--e2b-template`, and is refused before anything is installed. A loopback Docker or -microsandbox selection is saved, but no node can serve it until `public_url` is -guest-reachable HTTPS. `--sandbox-provider` and `--provider` are retired and fail. -The thin distribution supplies native Core binaries. Provider helpers, the node -agent, Runtime image and pinned msb runtime/firmware are separate, same-revision -assets. Web serves local offline artifacts or redirects the node to the verified -manifest's versioned HTTPS release. `/console/config` reports providers with a -complete set of local files or declared release downloads. Core packaging is independent of provider: -`--native-core` runs Core as a systemd user service, with PostgreSQL/Web in Compose -and a private loopback database port. Native Core needs no KVM or node assets. Core receives no -Docker socket or node identity mount in either mode. The ordinary standalone node -service owns its provider processes outside the Core container. Its `KillMode=process` -preserves resident microVM/helper processes across a node-service restart. User KVM -access and the Linux runtime libraries are prerequisites for microsandbox. The -installer runs as root and prepares the host: it creates or adopts the -`oac-node` system user, adds it to the `docker` or `kvm` device group (no other -group), and installs one root-owned system service per installation that runs the -same node program with `User=oac-node`. Sudo mode serves one Core per host, -because its nodes share that account. Docker group membership makes that user, -and so the node, root-equivalent on the host; that is inherent to Docker sandboxes, -not a least-privilege boundary. Microsandbox needs only `kvm`. Files the service -user owns are read, written and deleted only with its credentials, never by root, -in a child that starts its own session with /dev/null as input, so nothing it runs -can reach the administrator's terminal. That child also joins a new session -keyring and dies with its parent, and root shows its output only as plain text -(terminal controls become `?`). The installer turns SIGINT, SIGHUP and SIGTERM -into stopping that child and what it started, which would otherwise outlive a -closed terminal. Root never runs a file that user can write, -opens a URL it wrote, or follows a link in its home. Sudo mode -never installs Docker, KVM or packages, never changes device permissions, refuses -SELinux-enforcing hosts and a token in the environment, and changes nothing when a -check fails. `--uninstall` removes a node only after Core rejects its credential, -never touches sandboxes, volumes or images (it keeps the Runtime image and the -microsandbox store), deletes the account only when the installer created it and no -node remains, and otherwise removes only the groups it added. Do not add any other launcher, scheduler or -recovery path. Node services restart after failures without a start limit, so a node -outlasts a Core outage, and stop restarting when the node program exits 78 -because Core answered 401 to its credential (a removed or retired node). -The basic API image and binary builds remain independent artifacts. -The standalone API release and Core distribution both include the nodes operator -reference (`HOSTED-SANDBOX-MANAGER.md`) at the relative path used by their packaged README. Include the -guide in each artifact checksum list so extracted documentation matches its build. -The node asset includes the `oac-node` binary. The installer's Docker and -microsandbox selections use Web's Standard size from -`apps/web/src/features/sandbox/standard-sizes.json`, which the distribution build -copies into the bundle; keep no second copy of those values. An existing database -selection is never overwritten by installer defaults. Node configuration and identity live under -`~/.oac/nodes//` in the node account's home (`/var/lib/oac-node` -in sudo mode); microsandbox uses its separate short private -Runtime home. Zero-node installs create no node identity state but retain the paired -Core key for first setup. - -Node installation refuses pre-rename resources for the same installation ID: old -records, node directories, units and Docker networks. It never adopts those -resources or removes another installation. Remove the node on its old Core, then -uninstall with the previous release before adding it again. The machine -configuration route rejects `X-Parsar-Node-ID` with `400 invalid_request`; only -`X-OAC-Node-ID` identifies a retained node credential. - -User-managed hosts use the native daemon installer on Linux, macOS and Windows. -It does not select a supplier or create compute resources. The retired Docker -self-hosted installer/launcher and console payload are not retained as fallback. -Existing environments, credentials, workspaces and native history are never -automatically deleted or adopted by a new installation. - -One Runtime image contains the existing daemon, shared helpers and three native -harness packages. Their differences remain in the adapters. Core keeps exclusive -ownership of Session allocation, initialization, cancellation, snapshots and -cleanup. The node installer imports the Runtime image and prepares running -conditions; neither installer creates an execution Session or supplies a model -credential. Applications use the -existing write-only model execution extension, with the installation's persistent -credential encryption key. Provider identity/backend namespace and native history -must not change on a repeated install. - -`services/core-console` serves the production Web build and, after console login -and same-origin checks, forwards every `/core/v1` request with the Core key; Core -decides whether the route exists. It requires the private Core key file named by -`OAC_WEB_CORE_KEY_FILE` and holds no project caller credential. Every `/v1` -and `/api/v1` request returns 404, including explicit Bearer and WebSocket -requests; Web forwards no node or daemon transport. The installer mounts only the -Core key into Web and only its digest (`OAC_CORE_KEY_DIGESTS_FILE`) into -Core. The browser receives safe configuration, never that key. The deployment's -TLS reverse proxy routes `/v1` (applications) and `/api/v1` (nodes and Runtime -daemons, with their own credentials) directly to Core and everything else, -including `/core/v1`, to Web. Operator scripts call `/core/v1` on Core's loopback -port. -Nodes and Core come from one distribution. Older nodes using the removed -`/core/v1/sandbox` paths are unsupported; preserve their installation and data and -use a separate fresh installation. There is no drained in-place upgrade or -historical re-enrollment procedure. Current-version Runtime generation rollout -retains its separate resource lifecycle. - -The Web manager offers no manual Core key entry outside sign-in, and Web refuses -to start without its Core key file. It holds no Project API key and never calls -`/v1`. -Chinese/English sandbox text, status and diagnostic formatting live in the shared -`apps/web/src/lib/` locale modules. A persisted explicit language preference wins -before the first browser language; unrelated product surfaces are outside this -translation scope. Preserve zero-node setup and node installation behavior when -localizing their controls. The sandbox manager centers node readiness and capacity in a desktop topology, -with Core surrounded by actual node buttons. Connection animation represents -liveness only, never invented traffic or work; offline/stale connections are -static and reduced-motion preferences disable decorative animation. Node selection -reveals inspection details. Installation identifiers, provider metadata and -allocation records are secondary content. Node enrollment is an explicit Add node action in a focused -dialog, using the deployment's `core_url` (the installation public URL). -Do not expose routine network wiring or manual runtime setup as the primary flow. -Generate a one-time command only on user intent, never retry enrollment writes -automatically, and discard credentials and late responses when the dialog closes -or the Core connection changes. Core reports the command's `enrollment_id` on the -node it registered (null for nodes enrolled before Core recorded it), and Web follows -the added node by an exact match on it; an existing node reconnecting is not a new -enrollment. -The command verifies the installer checksum before execution, retains normal TLS -verification, and passes the enrollment credential only to the installer process, -on standard input. - - -The `/core/v1` proxy retains fixed-origin, cross-site, safe-path, redirect and Upgrade -restrictions through the standard Go reverse proxy with streaming/cancellation; -literal or encoded dot segments can never move a request out of `/core/v1`. -The console implements no product identity, resource semantics, Runtime discovery -or execution loop. Signing in with the Core key grants the complete console -surface; do not introduce Web accounts, roles, invitations or per-project Web -identities. Agent API caller keys remain independent of the Core key and cookie. - -Web signs in only with the Core key (`POST /console/auth/login` with -`{"core_key":"…"}`), compared in constant time with the console's configured key -and never logged or echoed. There are no accounts, passwords, first-run setup or -Basic authentication; `CORE_CONSOLE_AUTH_MODE`, `CORE_CONSOLE_STATE_DIR`, -`CORE_CONSOLE_PASSWORD_FILE` and the renamed `CORE_CONSOLE_ADMIN_TOKEN_FILE` fail -startup. Cookie sessions are in memory, bounded, HttpOnly, SameSite Strict and -Secure for HTTPS origins; a restart or Core key rotation requires sign-in again. -Unauthenticated access is limited to the static login UI, finite console -authentication routes and the static node installation payload. Sign-in uses -same-origin JSON POSTs with bounded bodies and bounded concurrent work. Only -failed attempts are rate limited, so the correct key always signs in; Web and the -installer therefore require Core keys of at least 32 characters. See the -[Core key operations guide](docs/getting-started/operations.md#core-key). - -Projects and application API keys live in Core PostgreSQL. Project creation owns -its scope and shared principal; key issuance, revocation and Project archive share -a transaction with audit. Issuance stores only a digest and metadata and returns -plaintext once. Keys cannot be read back or reset in place; rotate by issuing a -new key in the same Project and revoking the old key. Authentication checks the -key and Project on every request, without a credential cache, and fails closed on -database errors. Deployment credentials cannot authenticate to the public API. -Configuration defines no Projects or business API keys. Fresh installation starts -with no Projects; an administrator creates a Project and then issues a key. - -Administrator onboarding covers console login, Project creation, key issuance and -optional node enrollment. Model execution belongs in an external API example using an issued -key. Keep secrets out of browser persistence, generated examples and URLs. Observe -confirmed resources through the management API; do not infer Agent-to-node ownership -or execution readiness from a host connection. Preserve keyboard focus, reduced -motion and the existing node enrollment/topology contract. -The console has neither KVM nor Docker authority; its static root contains no -secrets. Installation exposes only loopback API/console ports. Remote exposure -requires an operator-configured HTTPS/access boundary. Web-only mode can connect -to a loopback existing Core on the same Linux host or a remote HTTPS Core. - -Installation state and secrets live in a private directory under `~/.oac/` by -default. No credential enters build arguments, image layers, browser bundles or -diagnostic output. Compose configuration is confidential. The generated database, -Projects and their issued keys, provider identity and encryption -key survive reruns; automatic -revision replacement and provider migration are outside this initial installer. -Reruns also refuse enabling or disabling a sandbox provider on an existing -installation, including adding one to the default zero-node installation. -Stopping control-plane services does not stop all Provider resources; use Core's -existing release operations for full cleanup. No native restart promise covers -host reboot or a lost running microVM. Do not delete data or issue broad -container/volume pruning as recovery. - -`make check-distribution` covers the production proxy, installation rules and -release metadata. Real bundle validation covers default/provider selection, -component modes, existing Web connection, public native execution and restart -retention. Diagnostics report observed service health, not fabricated model or -complete environment readiness. Runtime observations are Core-owned; do not add -a duplicate monitoring/lifecycle framework to installation or the public landing. - -#### Current implementation - -The constraints below describe existing code, not requirements to preserve legacy -design. The [protocol assessment](contracts/agents-api/README.md#implementation-direction) -identifies replacements and gaps. Update these rules when their implementation is -replaced; do not carry obsolete compatibility code forward to satisfy this section. - -- `internal/agentdaemon/gateway` is the shared daemon connection implementation. - Its persistence interfaces use `internal/agentdaemon/device`, never product Store - types. Shared protocol frames and validators live in `internal/agentdaemon/proto`. -- Session deletion uses a durable `sessions.deleted_at` marker. Public deletion - accepts only a durably idle or failed Session without required actions: no - queued, in-progress or waiting root Turn and no pending input reservation, the - same settlement rule as the creation stream. Subagent child Turns and pending - Environment file writes are not checked, as before this rule; their official - behavior is unobserved. Take that decision and commit the marker - under the tenant Session lock that orders Turn and input admission, so either - admission commits first and deletion conflicts, or admission observes the - deletion. A busy Session returns 409 `conflict_error` with the observed official - message and nothing changes: no cancellation, marker, event or cleanup. Callers - cancel first (`agent.session.input.cancel`), wait until the Session is idle and - delete it. Core admits a Turn synchronously, so it also conflicts right after an - `events.create` 202, where the official service was observed to return 200. - Deleting a provisioning hosted Session with reserved input used to release its - sandbox node placement at once; it now conflicts, and the placement counts - toward node capacity until the input is admitted or its five-minute deadline - expires. A later allowed deletion releases an unallocated placement. - The owner's repeated deletion returns the same 200 confirmation without writing; - foreign, missing and malformed identifiers keep the byte-identical 404. Public - reads, metadata changes, event streams and input admission exclude deleted - Sessions; admission checks visibility under that lock before retry lookup. - Creation keys remain reserved and cannot resurrect deleted Sessions; reuse of a - deleted creation identity returns 409. Existing streams close when removal is - observed without a fabricated deletion event; overlapping stream timing remains - unverified. Earlier releases also deleted busy Sessions after requesting - cancellation, so upgraded databases can hold markers with hidden work. - Internal Turn/receipt/finalization and restart reconciliation retain access so - that work settles under the existing execution lease; queued work cannot be - claimed, and Runtime cleanup still cancels pending work. Confirmation does not - guarantee native quiescence. Never revoke a shared device, - remove a saved Agent or touch product data as part of Session deletion. Physical - SQL/native history cleanup remains a separate required implementation gap; these - records are retained, not claimed purged, and purging may end repeat idempotency. - Do not deploy a pre-deletion service - against a database with deletion markers; migration rollback refuses to remove - the column while deleted records exist, preventing public resurrection. -- `services/agents-api` owns its SQL schema, sqlc queries and embedded goose - migrations. `OAC_DATABASE_URL` is required; never fall back to the product - database URL. It stores tenant-scoped Sessions with a stable engine and durable - creation retry identity. -- Reusable Agents have their own tenant-scoped `agents` records, independent of - Session snapshots, engine bindings and product Agent definitions. The Store - persists caller-validated non-secret configuration and metadata without applying - harness capability restrictions or model defaults. Resource identity and equal - initial creation/update timestamps come from the persistence boundary. The - create primitive creates a fresh resource; public retry conformance remains - unverified. Internal storage admission is 512 KiB for configuration and 64 KiB - for metadata, not a claim about upstream limits. -- Vaults have their own tenant-scoped records in the execution database. Initial - `POST /v1/vaults` and `GET /v1/vaults/{vault_id}` operations persist and read the - resource without creating Sessions or contacting an engine. Omitted name is - null; a supplied non-null string is trimmed and limited to 1–256 UTF-8 bytes. - Omitted/null metadata becomes an empty object. Reuse the 64 KiB encoded metadata - storage bound, without applying Session-specific pair/character limits. This is - a local bound, not hosted parity. `GET /v1/vaults` lists the authenticated project's - records using creation-time/ID keysets, default descending order and a default - limit of 20 clamped to 1–100. Other resource limit policies are unchanged. - Status accepts `active`/`archived` as a scalar or SDK `status[]` array, with both - included by default. Private stored classification defaults existing/new rows - to active; it is never exposed in the Vault response. Listing reads no Credentials - and needs no encryption key or execution service connection. A scalar status - combined with `status[]` filters by their union; a repeated scalar parameter is - rejected. Exact hosted errors, equal-time - ordering and changes between pages remain unverified. Private archived fixtures - prove filtering only: there is no public archive writer, archive timestamp or - inferred delete-to-archive behavior. Retrieval, Session binding and dispatch retain - their existing rules. Archive/revocation lifecycle remains a separate gap; - do not introduce product roles or speculative lifecycle fields. Migration rollback - refuses to discard classification while archived rows exist. -- Vault `DELETE /v1/vaults/{vault_id}` removes the project-owned parent and all - Credentials through the existing foreign-key cascade in one SQL mutation. Do not - loop through child deletions, decrypt secrets, require the storage key or call - providers. Deletion applies to both stored classifications. Local missing/repeated - deletion returns not-found; subsequent parent/child reads and new attachments - cannot use the removed resources. Preserve Session snapshots, frozen choices, - history and recorded retries. Later secret lookups fail without selecting another - attached Vault or anonymous MCP. Already-resolved tokens and running Sessions - are not revoked. Exact hosted archive, visibility, overlapping-mutation and error - semantics remain unverified; row removal does not prove physical storage erasure. -- Static-bearer and OAuth Credentials are children of tenant-owned Vaults in the execution - database. Creation admits the owner in the same SQL statement as the insert; - retrieval joins the owning Vault and selects public metadata only. Public reads do not decrypt or return a token. OAuth replacement authenticates - the stored grant before applying a partial update. Encrypt before passing secret values to - SQL, using the execution service's separately configured random 32-byte key and - the standard library's random-nonce AES-GCM. The versioned authenticated binding - includes tenant, Vault, Credential, authentication purpose and exact destination. - Never reuse product master-key conventions or daemon transport encryption for - this storage boundary. Missing key configuration disables credential writes; - malformed explicit configuration fails startup. See - [`services/agents-api/credentials.md`](services/agents-api/credentials.md) for - key persistence and current limits. Storage-key rotation remains - separate work; resource creation never contacts the destination. -- `GET /v1/vaults/{vault_id}/credentials` lists safe metadata only, with both - project and Vault ownership enforced on the parent, cursor and row query. An - inaccessible parent returns not-found, even when the collection would be empty. - Reuse the Vault status/limit parser and Credential metadata mapping. SQL must - never select ciphertext for listing; no encryption key or execution is needed. - Credential status is a separate private active/archived classification, defaulting - historical/new records to active and never derived from the parent Vault's status. - Synthetic archived fixtures prove filtering only. There is no public archive writer, - timestamp or delete-to-archive inference; existing create/retrieve/token replacement, - Session bindings and dispatch keep their rules. Migration rollback refuses to lose - archived classification. Archive lifecycle and hosted query/concurrency - semantics remain gaps. -- For static auth, Credential `POST /v1/vaults/{vault_id}/credentials/{credential_id}` - replaces only the token and update time. Require `auth.type=static_bearer` and a - string `auth.token`, preserving opaque bytes; reject extra mutation fields before - writing. Reuse safe metadata for the immutable encryption binding, then scope the - atomic SQL mutation independently by tenant, Vault, Credential, static auth type - and exact destination. Never decrypt the previous token or send plaintext to SQL. - Missing encryption configuration or a failed write preserves the old row. Return - the existing safe metadata projection; identity, name, destination, creation time - and Session snapshots stay unchanged. Subsequent dispatch reads use the committed - replacement through existing scoped lookup; already-resolved requests may retain - the old token. This is not storage-key rotation, in-flight revocation or hot reload. - Exact hosted concurrent-update/retry/timestamp semantics remain gaps. -- OAuth grant ownership stays in Core. The application performs authorization and - provider revocation; do not add public login/callback/refresh/revoke routes. - Store access/refresh/client secrets together under existing authenticated tenant, - Vault, Credential, auth-type and destination encryption. Authenticate refresh - metadata against its encrypted copy before using an endpoint or grant. Read/list - queries still select safe metadata only. Shared MCP selection admits both auth - types and freezes one identity without changing native adapter contracts. - At dispatch, a known-expired grant is refreshed through the declared endpoint - auth method, with stored scope/resource, then persisted before returning access. - Serialize refresh and replacement with the same PostgreSQL Credential row lock; - deletion and Vault cascade cannot be undone by a stale refresh. Network exchanges - are bounded and fail closed; never return provider error bodies or claim an - uncertain grant exchange was committed. No background scheduler, 401 retry, - hot replacement, output repair or harness-specific OAuth path is introduced. - Refresh uses verified HTTPS, rejects redirects, and checks resolved addresses - before dialing them. Private issuer origins need explicit operator configuration - in `OAC_OAUTH_TRUSTED_ORIGINS`; tenants cannot relax that boundary and TLS - verification remains mandatory. Keycloak is acceptance infrastructure only. - Preserve the pinned update omission/null and immutable-field rules described in - [OAuth credentials](services/agents-api/oauth-credentials.md); record unspecified - hosted semantics. Native processes receive only access tokens. Provider revocation, - withdrawal of already-dispatched tokens and Session cancellation remain distinct. -- Credential `DELETE /v1/vaults/{vault_id}/credentials/{credential_id}` removes one - owned row, including ciphertext, with tenant/Vault/ID checked in the same SQL - mutation. It needs no encryption key, secret read or network call. Local reads, - updates, listings and subsequent dispatch lookups cannot use that ID; missing - and repeated deletion return not-found. Preserve frozen Session choices, retry - identities and history without fallback to another credential or anonymous MCP. - A token already read before deletion may remain in a dispatched request. Deletion - does not revoke provider tokens, cancel Sessions or prove physical erasure from - native history, WAL or backups. Do not infer a delete-to-archive mapping; exact - hosted archive, post-delete visibility and repeat/error semantics remain unverified. -- Public reusable Agent create/retrieve uses `/v1/agents` and the same authenticated - tenant/Beta-header boundary as Sessions. The resource envelope owns identity, - timestamps and metadata, separately from saved configuration and Session state. - Resolve known defaults and validate supported schema before writing. Preserve - model/name/instructions verbatim, nullable fields and structured JSON numbers. - Stored reasoning/service tiers, enabled multi-agent settings, JSON Schema output, - enabled web-search modes and deferred/tool-search/programmatic tools do not imply - execution support. Reuse function wire validation, keeping Session execution - restrictions separate. Model-derived reasoning effort is unresolved when omitted; - do not infer it from the selected harness. Omitted/null service tier currently - uses `auto`; complete upstream default/error/retry conformance and remaining MCP - variants remain gaps. Unknown/unsupported variants fail explicitly. No product - lookup is permitted. -- Service-origin public HTTP MCP uses the native harness client and tool loop on - trusted service-owned `environment:none` compute, with Codex or Claude SDK. - The V1 colocated `self_hosted` profile rejects it: user-owned compute cannot be - relabeled service-origin or receive its attached Vault credentials. Hosted - service-origin MCP also remains unsupported. Environment-origin Plugin MCP uses - its separate qualified local Runtime transport and isolation contract; do not - disable that path or infer optional-feature equality across engines. - Admission, device selection and preclaim still require the exact supported MCP - capabilities. Native declarations do not widen public placement authorization. - -- Keep accepted public MCP credential profiles separate from private adapter - capabilities. The execution service declares the verified public bearer profiles - centrally; a daemon capability alone cannot open a public profile. Reuse frozen - binding validation at admission and later input, then the same MCP capability and - placement checks at device selection, final preclaim and request construction, - before scoped decryption. Native configuration and token injection stay in the - adapters. Add bounded shared checks while changing the related execution path; - do not defer known duplication to a general engine or plugin framework. -- The shared MCP resolver preserves omitted/null `allowed_tools` as unrestricted - and an explicit empty list as deny-all. Saved HTTP transport output includes - `headers:{}`; the effective Session transport omits headers, matching the two - pinned resource types. Saved-Agent updates never change existing Session - snapshots; per-Session tools replace the whole field. The initial profile admits - HTTP(S), boolean `required` (default false), empty/null metadata and empty/null headers. - Static and OAuth bearer authentication require HTTPS and the attached-Vault rules below. - An omitted/null `connection_origin` on HTTP transport is stored as `service` - before the other checks, identical to an explicit declaration, as observed - officially. Inline authorization, URL userinfo/query/fragment, the `environment` - origin and stdio remain explicitly unsupported. -- Codex required MCP initialization additionally needs `mcp_http_required`, advertised - only for the verified native pin and checked during selection, final preclaim - and daemon dispatch/preparation. Preserve the boolean through typed messages, - native rendering and exact configuration preflight. Reuse native required-server - initialization during root thread creation and cold resume; send no native Turn - until it succeeds, and never replace a failed strict resume with a new thread. - Public work may already be accepted/queued/in progress while native initialization - waits. This is not a continuing health monitor or a new public readiness state; - exact hosted Session creation timing and initialization errors remain unverified. -- Send MCP declarations through typed daemon fields, independently of function - callbacks. In Codex, a non-nil declaration replaces operator MCP options; use the existing - native renderer and original tool names for `enabled_tools`, including `[]`. - Before thread creation/resume, query native `config/read` with the exact cwd and - reject additional servers or effective configuration differences. Disable native - plugins/apps and select file-only MCP credentials; reject existing credentials - in the private native home without deleting them or native history. Native - reserved labels are an adapter restriction, not a saved-resource schema rule. - Requests without this typed field keep the existing product behavior. The check - is a snapshot on trusted service compute, not an atomic barrier against concurrent - operator configuration changes. Discovery of a declared deny-all server can still - contact it; deny-all governs tool exposure. Reuse neutral tool observations and - the existing public `mcp_call` projection, never add a second MCP/model loop. -- The private Codex adapter's HTTPS MCP bearer authentication requires - `mcp_http_bearer_auth` and the existing MCP/environment capabilities, checked - before the factory. It is restricted to trusted service-side Codex with - `environment:none`. A transient - per-server `bearer_token` becomes a fresh daemon-owned `bearer_token_env_var` - reference for each native process. Put the exact secret only in that app-server - child's environment, after auxiliary launch probes; never in global environment, - arguments, configuration/history, public snapshots or logs. Preflight accepts - only the expected server/reference pairing and retains the existing rejection - of ambient credential sources. Service-side bearer variables must not enter - generated commands, files or public history/snapshots. Use the native HTTP client - with TLS verification. - This execution profile rejects empty values and bytes outside RFC 6750 b64token - syntax with generic errors; it never trims tokens or narrows opaque Credential - storage. Core-managed OAuth uses this same access-token path; native OAuth - login/refresh and hosted redirect/error equivalence remain separate work. -- Session `vault_ids` omission/null/empty means `[]`; nonempty attachments must all - belong to the authenticated tenant. Preserve caller order and the stored caller - `credential_id`. Saved Agents may store a nullable/nonempty credential reference - without authorizing its use. Session admission resolves an explicit credential - only inside attached Vaults for the exact declared URL, or selects the unique - matching static or OAuth credential when the ID is omitted/null. No match remains - anonymous. Selection errors use the observed official messages with a null param: - a reference without attachments, one outside the attached Vaults and one for - another URL are 400 `invalid_request_error`; several implicit matches are 409 - `conflict_error`. Missing, foreign-tenant, unattached and malformed references - share one message; echo caller values only within `internal/echotext`. Unknown - or foreign Vaults keep the same 404. Resolve after the input requirement and - before any Session, initial input or event write. Freeze safe bindings, - including anonymous decisions, in private Session configuration. Session - projections, never the stored configuration, show an implicitly selected ID in a - null/omitted public `credential_id`, also after deletion. At actual dispatch, recheck - tenant, attached Vault, selected ID, frozen auth type and exact URL before scoped - decryption. Metadata queries select no ciphertext; tokens enter only the existing - transient daemon request. Selected authentication requires `mcp_http_bearer_auth` - during device selection and the final preclaim check. Missing/wrong keys or - binding failures never fall back to anonymous execution. Exact URL equality, - immutable selection timing and hosted redirect semantics remain local decisions - or unverified gaps. No new MCP loop is permitted. -- Saved Agent execution defaults use separate input and safe-output Core extensions. - Keep model-provider bundles whole at every replacement boundary: endpoint, key, - protocol and limits must never be independently inherited. Ordinary Agent JSON - contains only safe provider fields and an output-only configured flag; encrypt the - complete bundle separately with tenant/Agent binding and a distinct purpose. - Commit configuration and secret changes together under the Agent row lock. Merge - only the extension's supplied members; omission preserves, provider null clears - its bundle, and extension null clears both defaults and secret. Model-only edits - require no key. Validate the merged harness/protocol/limits without reading keys. - Read safe defaults and ciphertext in one database snapshot for Session creation; - a complete Session override need not decrypt the inherited bundle. -- Session provider selection resolves explicit bundle, saved bundle, then the - deployment default of the resolved harness, and freezes it in the existing - encrypted Session-owned row. The deployment default is a runtime setting: one - complete bundle per harness in PostgreSQL, encrypted with its own - harness-bound purpose, managed only with the Core key through - `/core/v1/harnesses/{harness}/model-provider`, audited as a deployment-wide - write without the key, and never read back. It applies to `openai_hosted` and - operator-registered `none` Sessions, never to `self_hosted`, whose executor host - belongs to the application. Caller bundles apply to `openai_hosted` and - `self_hosted`. Hosted and self-hosted Sessions with no bundle are rejected at - creation with `model_provider_required`; legacy rows without a snapshot are - rejected at message admission and dispatch, never sent to a harness that would - fall back to a built-in endpoint. There is no operator options file: its - retirement fails startup. Sessions retain only the common frozen provider - bundle; historical native-option snapshots are unsupported. Keep runtime dispatch on the common adapter path and fail closed for - missing/decryption-failed snapshots. Agent edits/deletion, default changes, - restart and idle suspend/resume never resolve defaults again. Record caller - intent for every new hosted Session before resolving defaults; other inline - Sessions keep the resolved-request rule, whose hash leaves out the deployment - default. Provider keys enter retry hashes only as keyed fingerprints. Matching - retries return committed state without replay. No Turn-level overrides or provider catalog is - included. Public input/null semantics and examples live in - `contracts/agents-api/model-execution.md`. -- Deployment-default observations use a private UUID generated on every PUT, - including identical replacements. Read its ciphertext/revision together and freeze - the pair only at new Session creation; retries and historical Sessions never gain - or replace revision metadata. After a successful root terminal commit, one - independent pool operation has at most one second to update the matching current - revision. SQL verifies tenant, root Turn and committed outcome; only completed - Turns and fixed native provider failures with authoritative `engine_failed` qualify. - Never observe cancelled work, Core/runtime errors or input-policy classifications. - The metadata-only transaction sets server statement/lock timeouts within the - remaining client budget and issues one UPDATE, so a client timeout cannot leave - an indefinitely waiting statement. The update locks only the default and samples - DB receipt time after the lock. - Errors throttle for 30 seconds regardless of code; ordinary successes throttle - for 30 seconds, with one immediate recovery write after each accepted error. - An unchanged revision has at most three effective writes in any half-open - 30-second interval of nondecreasing DB time. Clock rollback may suppress ordinary - writes; private recovery state permits only one recovery without clamping time. - Observations never change `updated_at`, readiness or execution truth. They can be - lost or stay stale indefinitely without another eligible Turn; there is no queue, - retry, probe, history backfill or provider text in the safe fields. -- Session execution-configuration reads use a separate immutable safe projection, - written with provenance in the same creation transaction as Session resources. - Read no credential ciphertext and never recompute sources from current Agents - or operator defaults. Model/harness sources are independent; provider bundles - retain one source. The read is Core-key only, under `/core/v1`; `/v1` has no - execution-configuration read. Old Sessions expose persisted model/harness with - unknown sources and unavailable provider metadata, without backfill. Projection metadata does not alter retry - identity; retries cannot replace it. Keep this administrator query separate from - runtime observations and do not touch activity or wake sandboxes. The versioned - contract is `contracts/agents-api/execution-configuration.md`. -- Model communication uses `internal/modeltransport` in the Runtime. Core sends one - frozen confidential `model_provider` bundle, independent of engine and placement; - it must not manufacture native provider options or retain a historical-options - dispatch path. The adapter declares native protocols. Native matches connect - directly; mismatches use one private loopback endpoint owned by the existing - Session execution resource. Its credential is distinct from the upstream key. - Keep endpoint cleanup on final native teardown, including preparation failure, - rather than on individual Turn completion. No second Session manager is added. - CLIProxyAPI's pinned translator is an embedded conversion dependency, not a - gateway service. Requests, JSON responses and incremental SSE events share the - same conversion implementation for hosted and self-hosted execution. The - dependency owns protocol fields, tools, reasoning and usage conversion. Do not - add local field maps, parameter restoration or parallel compatibility rules. - Keep SDK format selection, HTTP/SSE framing, limits, cancellation and resource - cleanup here. Pin dependency versions and qualify upgrades with contract and - native engine tests; document upstream limitations instead of silently promising - lossless conversion. Preserve model identity, reject incomplete streams and - never redirect upstream credentials. See `contracts/agents-api/model-protocol-conversion.md` for coverage, - limits and dependency qualification. Go 1.26.8 is the pinned build toolchain. -- Provider input validation uses the adapter-owned rules in `internal/harnessconfig`. - Keep one internal registry for protocol and token-limit validation; Core owns - credential environment and endpoint admission policy. These rules are not a - public discovery API or Runtime registration descriptor. Operation qualification - and live readiness retain their existing owners. There is no startup - configuration read; Session frozen execution-configuration reads remain a - separate administrator read. -- Public Agent updates use `POST /v1/agents/{agent_id}` with the same tenant/Beta - boundary and shared saved-field validation. Preserve omission separately from - null; only supplied fields replace saved values. Metadata is a separate whole-map - replacement, with null/empty clearing it. Lock the tenant-owned Agent row while - merging validated fields and enforcing the complete configuration bound, then - commit configuration, metadata and update timestamp together. Never write a stale - full snapshot over another update. No-field updates read without changing timestamps. - Except for the Core execution-default extension described above, supplied nested - fields replace the whole field and explicit null uses - existing saved defaults; exact hosted nested/null and no-op timestamp semantics - remain unverified. Model-derived reasoning defaults remain a separate gap. - Neither updates nor retries modify existing Session snapshots or execution state. -- Public Agent deletion uses `DELETE /v1/agents/{agent_id}` and one tenant-scoped - `DELETE RETURNING id` statement. Return the stored canonical ID with - `object=agent.deleted` and `deleted=true`; missing/repeated deletion locally - returns not found. It never deletes Sessions, history or runtime state and does - not cancel accepted execution. Recorded creation identities still recover the - accepted Session; new references cannot resolve an absent source. Historical - identities retain their documented limitation. Exact hosted errors and ordering - of overlapping source creation/deletion remain unverified; no tombstone or - successful result is fabricated for an absent resource. Reject body data; unknown - query keys are ignored. -- Reusable Agent listing uses the same tenant/Beta-header and response mapping as - create/retrieve. Page by `(created_at, id)` with a same-tenant saved-Agent cursor; - listing never resolves Sessions, product objects or execution capabilities. - Reuse shared list-query parsing and its per-family limit policy. Agent, Session, - Item, Subagent Item and Template lists treat limit 0 as 1 and larger limits as - 100; Vault and Credential lists also clamp negative limits; Turn, Subagent, - Subagent Turn and Artifact lists reject limits outside 1–100; Skill lists accept - 0–100, where 0 returns an empty page; Files accept 1–10000. Pages hold at most - 100 records (Files 10000) with accurate continuation. The local default is 20 - (Files 10000). Return the list envelope with data/has_more and first/last IDs - (null for empty pages). - Exact pinned upstream default/cap, empty-envelope and error semantics remain - unverified; do not present local limits or generic SDK parsing as full conformance. -- Session `agent_id` lookup uses the authenticated tenant. Copy the saved resource - ID and effective configuration into the immutable Session snapshot; saved metadata - does not become Session metadata. Never look up the source Agent when reading or - executing an existing Session. Omitted override fields inherit; supplied objects - and arrays replace whole fields before defaults and execution admission apply. - Reuse saved configuration validation and keep native capability restrictions at - Session admission. Reject unsupported effective options instead of dropping them; - an explicit supported replacement may make a saved configuration executable. - Inline Sessions use the same admission path. New saved-reference Sessions and - inline requests with Vault attachments or credential references record a separate - caller-intent hash: source ID (empty for inline), - supplied overrides (including field presence), environment/vaults, original metadata - and normalized initial input. Exclude response streaming and resolved source values. - Compare that same-tenant retry identity before looking up the source. A matching - retry returns the existing Session without input admission or source revalidation; - ordinary reads include current activity. Stream retries use the row's committed - event cursor and emit no created event. Recheck after source resolution failure - for a concurrently committed creator; do not hold a lock across resolution. - The unique creation upsert remains authoritative when concurrent resolutions differ. - Unrelated inline requests keep their existing resolved/default equivalences. - Recorded credential-bound retries recover before reading mutable Vault contents, - so another same-URL Credential cannot change an accepted selection. Rows with a - known creator but without recorded request intent retain resolved-hash behavior; - original overrides cannot be reconstructed, so no backfill is permitted. Source - mutation-independent retries apply only to recorded identities. Exact hosted - retry/error semantics remain separate work. -- Tenant scope must come from authenticated service identity before calling the - execution Store. Product workspace/user references in metadata grant no access. - Keep credentials and effective execution options out of Session metadata. - Store resolved, non-secret Agent/environment configuration in the Session's - immutable configuration snapshot. Inline and historical creation identities include - the resolved configuration; new saved references use the separate caller intent. - Session metadata updates replace only metadata under the authenticated tenant: - a supplied metadata field is required, null/empty clears, and a nonempty object - replaces all pairs. - Keep execution state, timestamps and the original creation request hash unchanged; - creation retries return the current resource without restoring its old metadata. - Public schema validation belongs to the API; the Store validates JSON structure. -- Session listing optionally filters by the immutable root `configuration.agent.id` - within the authenticated tenant. IDs are opaque and include inline Agents; never - require a surviving saved Agent or resolve product ownership. Apply filtering - before pagination and activity projection, using the tenant/Agent/creation index. - Omission retains unfiltered listing; a supplied empty string remains a filter. - Preserve the existing tenant-owned cursor and creation-time/ID ordering rules. - Hosted empty-filter, mismatched-filter cursor and exact error semantics remain - unverified. Other resource lists do not accept this parameter. -- `make sqlc-generate` and the drift gate cover this service. Run - `make check-agents-api` with `OAC_TEST_DATABASE_URL` pointing to a - dedicated `oac_*_tests` database for Session integration tests. - CI provides a separate PostgreSQL service. Migration immutability and ordering - apply independently to each service directory. -- Turn writes serialize on the tenant-scoped Session row. An idle message starts - a Turn; messages during queued/running/waiting work belong to that same Turn. - Store input retry identities and order durably. Cancellation retains its first - target, including an idle no-op, so retries cannot stop later work. Queued work - can cancel before dispatch; active work needs an executor outcome. Terminal - states and outcomes cannot be overwritten. Public event admission uses these primitives; live output streams remain separate. -- Input requests are ordered batches committed under the same Session lock. A - retry key identifies the complete ordered batch; changed length/order/content - conflicts and a failed transaction leaves no partial inputs or cancellation. - Existing single-event requests retain their identities at batch position zero. - Internal admission limits are 64 events and 512 KiB of payload per request; - the public API must still validate the upstream event schema. -- The external protocol reference is `openai/openai-python`'s `beta/agents`, pinned - in `contracts/agents-api/upstream.json`. Follow its Session/Turn/event semantics - and verify supported behavior using the official client. Track current coverage - in `contracts/agents-api/README.md`; SDK workflow objects are not this contract. -- Shared supported wire types live in `contracts/agents-api/v1`. `make openapi` - writes `openapi.yaml` (`/v1`), `core.openapi.yaml` (`/core/v1`) and - `runtime.openapi.yaml` (`/api/v1`); never mix their routes or authentication - schemes. Review the generated diffs; contract tests hold `openapi.yaml` to the - pinned routes and fields. -- The standalone service uses `OAC_DATABASE_URL`. PostgreSQL stores Projects, - their immutable execution scopes and API-key digests. Keys in the same Project - resolve to one shared service-account principal and tenant. Project/key writes - go through Core-key `/core/v1` routes, not configuration files. - Optional `OpenAI-Organization` and `OpenAI-Project` headers must match the key; - repeated/conflicting values fail authentication. Metadata, forwarded identities - and product session cookies grant no access. Issue/revoke operations take effect - without restarting Core. Deployment credentials cannot authenticate public calls. - Every new Session requires an explicit typed creator at the Store boundary, - including internal callers. Public creation derives it only from the authenticated - principal. Persist creator kind/ID in the creation transaction and never rewrite - them on retry, update or source mutation. The tenant remains the project partition; - do not duplicate project identifiers or create a product identity dependency. - Both early saved-reference recovery and the authoritative creation upsert require - matching creator kind/ID before returning a Session or event cursor. Different - credentials for the same principal can retry; another principal using the same - project/key receives the local idempotency conflict. This does not introduce - creator-only resource reads or mutations, or claim verified hosted retry parity. - Pre-migration Sessions retain null creator columns and remain project-readable; - creation retries cannot claim them. Missing creator and missing request intent - are distinct. Never infer historical ownership from keys, metadata or product - records. Retire older API writers before serving the creator-enforced deployment; - mixed-version writers are not supported. Tests must supply explicit synthetic - creators; only controlled historical fixtures may seed unknown ownership. - The operator-selected - `OAC_DEFAULT_HARNESS` is separate from the requested model. - Public execution supports the enabled `none` profiles, the three colocated - self-hosted profiles and qualified three-harness Docker hosted profiles; reject unsupported - input/environment/agent options explicitly. -- `packages/agents-client/v1` configures the pinned official `openai-go` Session - service. Use SDK request/response types, pagination and errors directly rather - than reimplementing transport or copying wire types. Supply an explicit service - base URL/key and creation retry key; SDK retries are disabled by default. Product - integration is a later cutover, not a side effect of constructing this client. -- `services/agents-api/tests/official_client.py` verifies the actual server with - the pinned SDK and strict response validation. It requires a dedicated test DB - prepared by the Store tests and `OAC_TEST_SERVER_BIN`; it never starts Docker. - The same harness runs the official Go client with fresh execution tenants and - checks its created Sessions through the Python SDK. -- Execution devices are operator-provisioned in the Agents API database with - tenant ownership and a credential digest. Their internal daemon gateway uses - `/api/v1/agent-daemon/*`, separately from the official `/v1/agents/*` surface; - device credentials grant no Session API or product permissions. The optional - `OAC_PUBLIC_URL` enables that gateway. It is a single-process registry, - not a claim of multi-pod execution or stock `exec-server` interoperability. - Self-hosted enrollment uses this gateway with an exact Environment binding. - Session/device bindings are tenant-scoped and immutable. Revocation denies new - connections and binding reads; an existing connection closes on its next - heartbeat. Connectivity comes from the live registry, not a persisted online - flag. `last_seen_at` is diagnostic only. Product gateway behavior is unchanged. -- `services/agents-api/internal/execution` dispatches internally resolved Turns - through that gateway. Claim `queued` to `in_progress` before subscribing/sending; - never automatically replay a claimed or interrupted Turn. Ordered extra inputs - require native steering receipts. Commit terminal outcome and native Session ID - together under the admission lock; unapplied messages prevent successful completion. - Resolve credentials separately from the immutable non-secret snapshot. -- Internal execution requires advertised durable Turns, strict resume, preparation, - applied input receipts and the qualified operation capabilities. Reject - unadvertised peers before claiming; failed strict resume cannot start unrelated - history. A completed Turn closes steering admission, settles existing native - operations and receipt sends, and closes its output before the retained Executor - can accept another Turn. Normal completion does not cancel the Executor. - Cached input identities and conflicts remain readable while completing; - queue/write success is not consumption. The receipt worker stays busy through - its send, and router shutdown cancels its native and transport waits. - The per-input `durable_receipt` opt-in requires a phased adapter. Its ten-second - transport timer stops only after a complete native write; a separate `written` - acknowledgement stops the API's thirty-second delivery timer. Neither phase - advances the input cursor. Await final native acceptance/consumption under the - Turn lifetime without automatic redelivery. Receipt sends retain a separate - five-second shutdown-aware context, and Done retains a fifteen-second final - settlement bound. Once cancellation is sent, its receipt owns the terminal - outcome even if an input becomes unknown first. Calls without the opt-in retain - their existing response deadlines. Native history still requires the device's - persisted engine files; IDs alone cannot restore deleted history. Cancellation - receipts carry the stopped Turn's confirmed continuity snapshot when no Done is - emitted. Preserve separately reported usage on failure; do not add the same - counters again when Done also includes them. - Usage frames carry cumulative snapshots for the current execution, not deltas. - Adapters publish observed snapshots promptly through the same ordered stream; - waiting for Done unnecessarily loses known measurements if the Runtime stops. - Core replaces complete valid token breakdowns and preserves the last committed - measurement on interruption. Missing measurements remain unknown. Do not infer - token consumption from context occupancy or estimated costs, or parse native - Raw payloads in Core. Public Session totals cover recorded root Turns and are - null while any root Turn has not ended or once one ends with unknown usage; - Runtime telemetry uses the separate measured sum of recorded snapshots. Subagent - Turn listings are not a summable accounting ledger. A cumulative native total that has not advanced - past the Turn's baseline is not a measurement of that Turn. Native measurement coverage - and exact provider/model attribution remain explicit qualification boundaries. - No separate public usage event or historical SSE replay is introduced. -- The dispatcher is an internal entry point used by the standalone service worker. - Legacy internal `daemon` configuration is not a public Environment type. - Self-hosted enrollment uses exact local binding; further pending interactions - remain separate slices. Unexpected interaction requests fail explicitly until supported. -- `environment_none` advertises an adapter's explicit environment-disable - path. Execution snapshots with public `environment.type=none` require that - capability and set `disable_execution_environment` on the internal prompt. - For Codex, the daemon forces `CODEX_EXEC_SERVER_URL=none` after caller environment options - and confirms native `local` and `remote` environments are unknown before starting - or resuming a thread. Unsupported binaries fail closed. The bound device hosts - the engine process; it is not a user execution environment. This is not an OS - isolation guarantee, and engine state still lives on that host. Ordinary product - requests retain their existing environment. The public worker selects an authenticated same-tenant engine host for this mode. -- `execution_controls` advertises the typed search/verbosity block on the daemon - prompt. Agents API requires it in addition to the selected engine's required capabilities - before binding/claiming work. Older peers with only option-based capabilities - must not receive controls they would ignore. The API sends resolved search and - text verbosity values; native option names belong to adapters. Codex translates - them using its existing validation/catalog path after cloning adapter options, - so explicit controls take precedence without mutating those options. Omitting - the entire block preserves ordinary product behavior; a supplied block requires - both valid fields. This internal contract does not add public configuration or - engine support. Future native adapters must verify the same semantics before - advertising the capability. -- Explicit public `programmatic_tool_calling.enabled=false` uses the common - `ExecutionControls.DisableProgrammaticToolCalling` field and the operation-specific - `programmatic_tool_calling_disable` capability. Both public qualification and - Runtime support are required for that request; omission creates no prerequisite. - New and resumed executions retain the frozen setting. Codex disables native - code-mode features and checks managed requirements before starting/resuming a - thread, rejecting a conflicting requirement. Claude and MiniMax retain their - restricted native inventories, which exclude programmatic execution. This does - not remove unrelated native utilities or claim enabled programmatic support. - Explicit `web_search.mode=disabled` reuses the existing disabled search control. - Search remains off when omitted. Optional search settings are resource data and - do not cause execution while disabled. Saved Agents keep every pinned search mode; - enabled search remains unqualified and rejects at Session admission. -- `web_search_control` advertises the Codex adapter's explicit `web_search` option - (`disabled`, `cached`, or `live`). Agents API requires this capability before Codex dispatch; - the typed execution controls force search off on new and resumed Turns. Native configuration translation stays in the - adapter. Product requests that omit the option inherit their existing defaults. - This is tool selection, not a network isolation guarantee. -- Inline Agent `text.verbosity` accepts `low`, `medium` and `high`; omitted or - null values resolve to `medium` in the immutable configuration snapshot. The - Codex dispatcher requires `text_verbosity` support and sends the effective value in - typed execution controls through the Codex adapter for both new and resumed Turns. The adapter queries - the native active catalog with `codex debug models`, checks model support and - pins that catalog snapshot for execution. The probe requires Unix process-group - cancellation; other daemon hosts do not advertise this capability. For models - without declared verbosity support, including the native unknown-model fallback, - `medium` selects native default text generation by omitting the override. The - pinned protocol defines `medium` as the default text amount. Supported models - still receive explicit `medium`, even when their catalog default differs. - Unsupported `low`/`high` and unreadable catalogs fail before model execution; - unsupported non-default levels remain an explicit implementation gap. - Product requests that omit the native option retain their existing defaults. - Structured output has a separately qualified profile described in - [Structured output execution](#structured-output-execution). -- `subagent_control` advertises native subagent tool control. Agents API requires - it when resolved `multi_agent.enabled` is false and sends the typed internal - `disable_subagents` policy on both new and resumed Turns. Native translation - stays in the adapter: Codex disables both multi-agent feature generations, - overriding operator feature preferences. Product prompts that omit the policy - retain their defaults. Enabled multi-agent observations require separate operation qualification; the Agent tools list is not - proven to enumerate every harness-internal utility. -- Subagent resources use the common observations in - `internal/agentdaemon/proto/subagents.go`: verified identity, successful lifecycle - effects, native-owned Turns/Items and neutral coordination operations. Core - assigns public IDs and projects them under the existing Session lock and leased - execution journal. Native names, history parsing and outcome proof stay in - adapters. Public GETs read persisted resources without starting native work. - Child Turns have a native writer and a separate table from the Core queue. - Session Turn reads and the Session event stream carry root work only: read child - Turns and Items through the Subagent routes, and never publish child Turn or Item - events on the Session stream. A child Turn's `agent_id` is the Session's Agent - ID; `subagent_id` names the child. The migration-defined `public_execution_turns` - view has no public reader; do not reintroduce mixed Session Turn pages. - Session Items stay root-owned; copied parent transcripts never become child work. - Repeated effects are idempotent. Active includes idle; task completion, process - release and cancellation cannot fabricate public closure. Native timestamps - retain their actual precision and unknown Usage stays null. - Reuse the existing native owner for child settlement and cancellation, freeze - root output first, and deliver child Items before their terminal Turn snapshot. - Do not add another scheduler or a broad recovery framework. Capability - advertisements do not qualify unsupported native facts. The exact read contract, - admission limits and remaining evidence are in [Subagents](contracts/agents-api/subagents.md). -- `function_tools` advertises the optional native function-call bridge. Explicit - prompt definitions become Codex dynamic tools; unchanged prompts carry none. - Requests and ordered text/image results are scoped by Run and native call ID. - Normalize string results into one text part at the public execution boundary; - the internal result carries a typed content array, and adapters translate it - to native content without fetching images or dropping parts. Validate content - before consuming a pending call. Retry identity includes the complete ordered - content and success flag. Reuse - application receipts and conflict detection; a receipt confirms the native - reply was written, not that an external side effect succeeded. Pending calls - end with their Run; the execution service owns persistence and recovery, while - Parsar retains business approval and credential-owner authorization. Do not - map native approval requests to invented official protocol resources. -- Function-call storage is scoped by authenticated tenant, Session and Turn, with - immutable public/executor call identities and arguments. Result admission and - application receipts serialize on the same Session lock as cancellation and - terminal transitions. Store the complete caller-validated result object; wire - validation and native translation belong to their API and execution boundaries. - Identical retries return the saved decision; changed results conflict. Pending - reads exclude applied calls and cancelling/terminal Turns while history remains - readable. Persistence does not imply transparent native-process recovery. - Recording a call moves the Turn to `waiting`; the last application receipt - resumes it. Session reads use one database snapshot for Turn, actions and usage. - Session state events retain their action snapshot, without private executor IDs - or results. Cancelling/terminal Turns expose no actionable calls. A waiting Turn - can fail or cancel before a result arrives; successful execution requires resume. - Actions remain visible until native application is acknowledged. This timing is - an implementation choice, not verified upstream event sequencing. -- Function results can join internal message/cancel input batches. Their explicit - Turn/call identity selects an existing call; admission never creates a Turn for - a result. Save the complete result and its input retry record in the same Session - transaction. Any invalid target, conflicting result or later batch error rolls - back the whole request. Resolve targets only after the tenant Session lookup: - an unknown call or a call of another Turn is 400 `invalid_request_error`, and - missing or foreign Sessions keep one 404. Identical saved results remain retryable after termination - without applying them again. The execution input cursor skips function results; - their separate native receipts still determine application. Public result events - validate variant-specific fields and required values before admission; store - omitted versus null error/output and ordered text/image parts. Inline function - tools resolve into the immutable configuration with explicit - `defer_loading=false`. Validate required strings and parameter objects before - persistence; reject unsupported deferred discovery. Omitted/null/empty tool - lists resolve to no tools. The public worker selects or waits for a same-tenant - device advertising `function_tools` when the Session has functions. - Function results are Session input Items: emit `item.added` with a null output - index, and never emit `item.done`, whose upstream union only allows agent output. - Project their public output/error from the saved submission; the wire always - carries both, null when not submitted, while stored payloads keep the submitted - presence. Native content normalization must not change public history. -- Codex function application requires a matching live native dynamic-tool completion, - including root thread/Turn/call identity, function, success and ordered content. - Writing its JSON-RPC response is not application. The adapter owns pending - receipts without holding their state lock across IO or waiting; terminal state, - cancellation and native loss settle unconfirmed submissions before release. - Uncertain receipt timeout ends that native execution without resending the result. - The common Runtime interface and router continue to own delivery identity, - retry/conflict and terminal ordering; Core never parses native tool events. - Record native confirmation before potentially blocking observation publication; - output backpressure cannot turn a known application into an unknown outcome. - The function submission owns raw write completion and receipt failure together; - its writer never applies an independent timeout/close decision. A confirmed - receipt releases submission even if writer completion has not yet been scheduled. -- Internal function execution requires an advertised `function_tools` capability - before claiming a Turn. Translate resolved definitions in the execution adapter, - persist declared callbacks before exposing actions, and deliver each saved result - once per live dispatch. Keep its success flag and ordered text/image output; - append a non-null error as a final text part because the native result has no - separate error field. Retain the original complete result in storage. Do not - treat transport delivery as application or automatically replay an uncertain - result. The adapter waits for outstanding application receipts even when Done - arrives first. Waiting Turns still accept execution observations and cancellation. - The public function workflow is verified with the pinned SDK and a real daemon - and Codex process against a synthetic model endpoint. This does not verify - other tool types, deferred discovery or upstream service timing. -- `message_items` advertises native assistant-message observations. Agents API - opts in with `observe_messages` only for advertised peers; ordinary product - requests retain their existing frame sequence. Opted-in text deltas carry their - native item ID, and `output_message` records start/completion, phase and the - completion text snapshot. A snapshot is not another delta; uncompleted messages - remain partial when their Turn ends. Keep these observations in the journal - before projecting public Items. This does not promise daemon event replay. -- Legacy `tool_items` / `observe_tools` raw snapshots remain available to old - daemon callers. New Agents API execution does not request or decode them. -- `tool_observations` advertises engine-neutral tool snapshots. The opt-in - `observe_tool_observations` takes precedence over legacy `observe_tools`: - attach the typed `observation` to existing tool-call frames without `native_item`. - Native adapters own discriminator/status/action translation and preserve raw - structured values; reuse the shared function-result content type. Kinds are - `command`, `mcp`, `function` and `web_search`; observation status is - `in_progress`, `completed`, `failed` or `incomplete`. A present empty function - content array remains distinct from missing content. These are - execution facts, not public Items: the API owns public IDs, schema projection, - lifecycle events and persistence. Product requests that omit the opt-in keep - their frame sequence and fields. Agents API requires this capability before - claiming work and always requests neutral observations. Its Item projector - validates this shared contract and never decodes engine-native tool snapshots. -- Codex callers opting into neutral tool observations also receive `command_output` - fragments with the existing native command identity. The adapter filters the root - Thread/Turn; the service requires an already indexed command in the same Turn. - Journal and Item updates commit with `agent.output.command_execution_output.delta` - events, retaining original fragments and the command's stable output index. - Completion output replaces accumulated drafts; absent completion output retains - observed text. Terminal Items ignore late fragments, and cancellation preserves - partial output without inventing successful command completion. Native text - conversion and output quotas still apply; this is not a byte-complete stdout/stderr - guarantee. Pinned native 0.153.4 also has an early-output subscription window; - missing native notifications/aggregate bytes remain a separate execution gap, - not output to reconstruct from model tool-result prose. Older peers may supply - only completion snapshots. Product requests - without the observation opt-in retain their existing frames. -- Execution observations are written to tenant-scoped `turn_events` in ordered, - idempotent batches before they can back recovery or publication. Keep daemon - payloads intact; this internal journal is not the public SSE protocol. Flush at - least every 100 ms while consuming events and before terminal persistence; - uncommitted observations can be lost on a hard process crash. Terminal outcome, - journal entry and native continuity commit together. Preserve partial text on - cancellation, including frames queued before a separate cancellation receipt. - Do not infer successful completion after a persistence error or stream overflow. -- Agents API uses the gateway's durable subscription; overflow or disconnection - closes it with an explicit error. Product subscriptions retain their existing - best-effort behavior. Journal limits are 512 KiB per payload, 1 MiB per batch, - 65,536 observations and 32 MiB per Turn; terminal persistence reserves one - additional outcome entry. These are internal admission limits, not promises - about upstream API limits or durable daemon-to-service replay. - -- Live Session SSE reads execution-owned `session_events`, committed with the - corresponding input, Item or lifecycle transition under the Session lock. - Store immutable transition snapshots; never render an old event from a later - Turn state. Reuse the API's response mapping and keep internal snapshots out of - wire payloads. Historical index rebuilding emits no live events. -- The notification buffer retains at most 256 events and 64 MiB per Session - after each transaction, retaining a single oversized event if necessary. - Read batches are bounded to 32 events / 1 MiB, with the same single-event - exception. This buffer is not a public replay log: GET begins at the committed - high-water mark, ignores Last-Event-ID, and polls committed events every 100 ms. - Missing sequence positions produce a safe stream error and close; recover via - Session/Turn/Items queries. Socket writes have a five-second deadline and hold - no database connection. Client disconnect releases the handler; comments keep - idle connections alive. GET SSE does not close merely because one Turn finishes; - only creation responses end on settlement (below). - -- Session creation with `stream=true` reuses atomic input admission and the live - event loop. The upsert returns its cursor under the Session lock, before initial - inputs; never replace it with a post-commit cursor lookup. A new response emits - one request-local `agent.session.created` with the committed Session projection - that the JSON 201 response returns (read after the commit), then committed - changes from that cursor exactly once. A fresh creation stream ends right - after the first `agent.session.idle` recorded when a Turn ends or an input - reservation stops being pending (expired, cancelled or failed), or any - `agent.session.failed`, and never sends the events after it. A self-hosted - connection clearing pending input to idle, `requires_action`, function results - and resumed work keep it open. A creation that admitted nothing (no Turn or - reservation) ends right after `created`. Settlements that record no event use - a fallback: after an empty drain the stream reads the JSON-path projection and - the event cursor in one database snapshot and, if the Session is idle or failed - with no queued, running or waiting Turn and no pending reservation, sends only - events up to that cursor, then ends. Accepted follow-ups: another client's work - drained before that read can still be sent, and idles recorded by an older - binary during a rolling deploy carry no settled marker and rely on the - fallback. An input reservation made while the ending Turn captured Artifacts - can start a later Turn that the stream does not follow. The settled marker and - pending-input flag are Store-internal, never wire fields, and add no events. - Re-read the projection after a sent Session status event and otherwise at most - once a second. The local creation retry key excludes response mode; a same-key - `stream=true` retry of an existing creation returns 201 with only the - connection comment and ends at once, admitting nothing and following no work, - because official same-key requests create distinct Sessions. Retry the same - request/key with `stream=false`, or use the GET events stream, to recover. GET - event streams keep their live-only start and never end on settlement or a Turn - failure; the only server-side end is the terminal `agent.session.failed` of a - hosted provisioning failure (and Session deletion), since that Session can - never run again. - Disconnect never cancels admitted work. Official observations cover `none` - creation; self-hosted, hosted and no-input stream lifetimes and the retry - behavior are local choices, and the separate SDK one-Turn helper does not - define this endpoint. Do - not present local retry behavior as replay. A new Turn records `turn.created`, - its user input Items, then Session activity in one transaction. Terminal Turn - events carry top-level `usage` copied from their Turn snapshot, null when - unknown; never derive or sum it. - -- Public Turn retrieve/list project persisted execution state and the immutable - Session Agent identity. Scope both resources and pagination cursors to the - authenticated tenant and Session, ordering by creation time then ID. Do not - expose adapter outcomes, native IDs or raw errors. Failure uses a customer-safe - category; usage is nullable when a complete upstream breakdown is unavailable. Submission uses the separate Session events endpoint. - - -- Public Items list reads a persisted execution-owned projection, updated in the - same Session transaction as admitted messages and journal batches. IDs derive - from the Turn and source identity; the first-observation timestamp and stable - tie breakers never change when content or status changes. Allocate each new - Item's Session position under the Session lock, preserving observation order - for equal timestamps. Allocate a separate zero-based `output_index` per Turn; - inputs do not consume output indexes. Updates and retries retain both values. - Persisted indexed history keeps its deterministic order; missing original - ordering cannot be reconstructed. Cursors are scoped to the authenticated Session. - Terminal Turns expose unfinished Items as `incomplete`, preserving completed - message/tool states independently of the Turn outcome. -- Public history reads use the persisted index; the private pre-Items journal - backfill is retired. Migration 15 and its historical validation remain evidence, - not a supported upgrade procedure. An old installation is unsupported and must - be retained separately from a fresh installation. Never mark unprepared history - indexed by hand or replay engine execution to convert it. -- Project only the declared public Item variants; native adapter metadata is not - a response schema. Preserve structured tool JSON without float conversion. - Completion text replaces accumulated deltas. Item merging must not mutate the - incoming observation or the previous snapshot: public text delta events read the - original fragment after merging, while Items retain the accumulated text. - Copy the content slice before replacing its text pointer. Assistant text - Items follow the official event sequence: `item.added` in progress with empty - content, an empty `content_part.added`, deltas, then the done events. A first - observation without its own fragment (a non-streamed native final) carries its - unchanged text in one delta; this frames the text and never alters it. Wire-only - explicit nulls (`phase`, function result `output`/`error`, Item event - `output_index`, Agent `reasoning` keys) come from response marshalling. Stored - Item payloads keep their original encoding through `Item.MarshalStored`, so - replayed child Items still compare equal, and stored configuration keeps - omitting unset reasoning keys. Keep partial output on termination; - do not turn an unfinished call into a successful result. Thinking fragments are - internal observations, not a claim of upstream reasoning-item support. - -- Public `POST /v1/agents/sessions/{session_id}/events` accepts ordered text-message - and cancellation batches through the pinned official client. Preserve individual - input messages in the Item index while deriving text for native dispatch. Batch - idempotency and cancellation targets remain durable; unsupported variants fail - before admission. Session creation accepts initial text as a string - or user-message array through the same parser and admission path. Commit the - Session, initial input, first Turn and Item/event projections in one transaction. - A creation retry returns the existing Session without re-admitting initial work, - including after terminal or later Turns. Omitted/null input is permitted only - for non-streaming hosted creation and self-hosted creation. - Creation streaming uses the shared live path above. Image support requires the - qualification in [the message-input contract](contracts/agents-api/message-input.md). -- Enabling daemon transport with `OAC_PUBLIC_URL` also starts a bounded execution worker. Select - only connected, capable devices owned by the authenticated tenant; bind once and - preserve native continuity. Metadata cannot select a device. Offline work stays - queued and can be cancelled. An engine host is not a self-hosted environment. -- One worker service owns an execution database through a dedicated PostgreSQL - advisory-lock connection. Its execution Store view uses that same connection for - every Session transaction: binding, claim/reconciliation, journal/Items/Usage, - function callbacks/application receipts and terminal/native continuity. Serialize - these short transactions and lease pings; execution transactions have a five-second - deadline including gate and Session-lock waits. Sandbox reset snapshots bound the - deployment relation explicitly to its legal singleton row before resource joins. - Keep this cardinality visible even on fresh databases without statistics: inflated - join estimates can trigger expensive JIT compilation inside the lease deadline. - Never hold a transaction across daemon/model work, reconnect the writer or fall - back to the pool after lease loss. - The original Store handles public admission and device/auth maintenance on pooled - connections. Execution reads may also use the pool; a read grants no write authority. - At startup, reconcile previously claimed work as failed, preserve queued inputs - and never replay uncertain execution. Shutdown cancels active dispatch and attempts - terminal persistence before releasing the lease; a lost owner cannot commit it. - Lease Close invalidates its writer and waits for pgx connection cleanup within - the caller deadline. A later Close can resume that wait after a timeout. This - drains client resources; it does not acknowledge remote advisory-lock release. - Tests that immediately transfer ownership must observe the previous owner's - exact database advisory lock disappearing before starting its successor. Bound - that wait and fail on query errors; do not retry Worker startup to mask competing - owners or change production lease behavior for a test's timing assumption. - Worker shutdown retains its existing bounded best-effort close policy. - This fences database writes, not already queued daemon commands or native effects. - Native quiescence/reconnect and recovery of unreported outcomes remain separate - gaps; this is not distributed exactly-once side-effect execution. -- Session state and last activity derive from its latest persisted Turn. Queued or - active work is `in_progress`, successful/cancelled work is `idle`, and failures - use a safe public error. The worker does not replace product dispatch, business - authorization, or the separate approval/environment lifecycle work. - -### SDK subprocess ownership - -The shared daemon `clirunner` offers opt-in Unix process-group ownership for -adapters whose SDK launches a native child. Existing callers keep their current -process policy. Explicit cancellation and parent-context cancellation share a -TERM grace period and bounded KILL escalation. An internal reaper also cleans -remaining group members when the direct process exits, even if a descendant -still holds stdout open. During cancellation, surviving descendants keep the -remaining TERM grace after the leader exits. Unsupported hosts reject this mode before launch. - -Owned output pipes remain readable after the leader exits. Consumers must drain -stdout and stderr before calling `Wait`, which joins the cached process result -and closes the readers. `Done` reports leader reaping and group cleanup signals; -it is not a native execution receipt or proof of persisted history. SDK adapters -must settle each Turn and drain its observations before publishing completion. -Executor close additionally closes the query and awaits the native child. Process groups are lifecycle supervision, not OS isolation -or containment of descendants that deliberately leave the group. - -### Harness qualification and onboarding - -`apps/parsar-daemon/internal/agent/harness.go` is the single source entry point -for Harness authors. Keep required lifecycle declarations, separate optional -interfaces and the existing registration methods there. Operation result types, -errors and Registry lookup/storage implementation may remain in focused files. -Use the existing `proto.SupportedAgentKind` and `AgentKindCapabilities` schema; -do not introduce a second capability descriptor or a combined optional interface. -Core service qualification remains separate from Runtime registration. Keep the -README and onboarding guide linked to this entry point. - -Codex, Claude and future harnesses have equal architectural status. The common -Runtime wire protocol and Executor/Turn interfaces own lifecycle, -input receipts, cancellation, recovery and resource access; each native adapter -retains its implementation and model/tool loop. A new engine supplies an adapter, -a qualified profile in `services/agents-api/internal/engine`, registration and -an independently verified deployment. It does not add engine-name branches to -API handlers, persistence, dispatch or scheduling. - -OpenAgentCore is pre-release. Replace superseded internal interfaces and execution -paths cleanly; do not retain version fallbacks or compatibility shims. Preserve -the pinned official public protocol, valid data and still-used infrastructure. - -The small static profile catalog owns engine-specific public admission and value -limits. Profile callbacks are pure and use existing public/protocol types; they -cannot query business data, decrypt credentials or control native processes. -Public schema validation, qualified engine support and actual Runtime capabilities -remain separate. Runtime advertisements alone never enable public operations. -Shared dispatch checks capability combinations, not a whitelist of engine names. -Harness onboarding does not require feature equality. Verify common lifecycle -obligations and use the same public assertions for each declared operation, -qualifying outer isolation where the deployment requires it. Optional native differences -remain independently prioritized capability/protocol work, not onboarding blockers. -Keep the complete pinned public protocol target and accepted functionality intact. -Never equate accepted parameters with applied native behavior. - -The base daemon `Session` owns cancellation. Permission and user-choice response -methods are optional `PermissionResponder` and `UserChoiceResponder` interfaces; -an adapter only implements them when it emits those interactions. Unsupported -responses receive a negative receipt, never fabricated application. The router -retains its existing interaction routing and retry ownership. - -`execution.Policy` supplies immutable service qualification to HTTP admission, -Worker device selection and final dispatch. Custom service composition must give -the same Policy to `api.WithExecutionPolicy` and `Dispatcher.Policy`. Zero values -use built-in profiles; an explicitly empty catalog authorizes none. Runtime -advertisements cannot add service profiles. No mutable global registration or -compatibility fallback is permitted. The synthetic third-harness acceptance under -`services/agents-api/internal/store` exercises the actual gateway and daemon router; -its fixture under `apps/parsar-daemon/testdata` is never a production engine. -See [the integration guide](contracts/agents-api/harnesses.md) for contract and -operation-specific acceptance and the [onboarding reference](contracts/agents-api/harness-onboarding.md) -for implementation and registration steps. - -MiniMax Code's opt-in Agents API profile qualifies native ACP 0.4.12 for -`environment:none` text execution. It reuses the shared lifecycle without public -workspace, functions or MCP. Enabled Subagents use the separately qualified -common observation path below. Native configuration disables -file/shell authority and external -capability discovery; the child receives a private Session home and a restricted -environment. Active-input application requires a native ACP receipt. Normal -Turns retain one ACP connection and native Session. Cancellation requires native -root and child completion evidence before reuse. Without a qualified root-state -reader, the ACP cancellation response is insufficient: stop the invalid native -process and wait for its exit before settling the Turn as non-reusable. Common -Runtime recovery then loads the exact owned native history in a new Executor. Do not infer history IDs or qualify hosted execution from this text -profile. See [deployment and acceptance](services/agents-api/deploy/mcode/README.md). - -MiniMax companion readiness uses private protocol 2; old companions are rejected -even when the upstream version matches. Cancellation retires the executor and -settles its native workers and detached Bash groups before acknowledgement; -later work recovers the same native history in a new owner without replay. - -The MiniMax workspace profile builds one CLI from the fixed upstream source and -lockfile through the existing companion packaging path, and connects native -workspace tools through its standard MCP client. The process and native Session -share one private control directory; public workspace files cannot configure that -process or become privileged project instructions. A trusted adapter-owned bridge -runs the original six tool implementations with the launching user's ordinary -permissions. It adds no inner sandbox on any platform. Keep native history bound -to the control directory and Files/Artifacts bound to the public workspace. Core and shared file helpers remain engine -neutral. This internal MCP transport does not admit public MCP configuration. -Record the upstream revision, native admission patch hashes and worker-source -provenance. The bounded patch checks the shared descendant-task limit inside the -existing native SQLite admission transaction before start, without another -scheduler. ACP initialization must acknowledge the applied limit before input. -The native tool catalog selects the same admitted workspace tools for every child, -not only the root's configured profile; this is tool selection, not filesystem -isolation. Only the Session's authorized internal -workspace MCP entry crosses the native child selector; this does not grant -external MCP access. Initialization must acknowledge the admitted tool inventory -before input as well. Subagent reads use the Session's native database; the daemon -does not protect it from other tools running as the same user. Multi-agent -workspace execution installs only the existing authorized workspace MCP entry in -that private native configuration so children inherit the same tools; public MCP -and Environment-origin MCP combinations remain separately qualified. Complete -[workspace acceptance](contracts/agents-api/mcode-workspace-v1.md) before enabling -hosted execution. The standalone companion uses its own npm lock; `make check` -runs its lifecycle tests and script checks, while its exact-source Linux build and -native qualification for the actual supported platform and outer deployment -are required when the companion changes. Historical tests of both inner network -policies do not qualify the current bypass implementation. - -Claude hosted functions compose the existing SDK function bridge with the common -workspace execution profile. Only declared function tools and the verified native tool -inventory are available. The bundle advertises this combination separately from -basic workspace execution; function preparations require that verified combination. -Service-origin hosted MCP remains unqualified; Environment Plugin declarations -use the separately qualified initialization path above. Function callbacks do not change file, -credential, history, subagent or network authority. - -### Claude dedicated Docker Runtime - -Build the pinned SDK bundle with `scripts/build-claude-sdk-runtime.sh`, then use -`scripts/build-claude-runtime.sh`. Native self-hosted installations use the same -bundle and daemon protocol. See [engine setup](services/agents-api/deploy/claude/README.md). -The shared local binding selects the actual workspace. SDK history, home and -scratch remain under `OAC_RUNTIME_HOME/runtime/claude-sdk` for session ownership, -without restricting tools. Authentication remains under `OAC_RUNTIME_HOME/daemon`. -The daemon requires neither nested sandbox privileges nor host security changes. - -The `local_runtime_v2` capability identifies the current workspace and direct MCP -launcher contract. Reject earlier workspace bundles; matching SDK versions alone -do not establish adapter compatibility. -The adapter advertises `local_runtime_v2` after checking the installed bridge and -native binary. Windows additionally needs Git Bash for its Bash tool. Registration -combines that check with the local binding; Core consumes the same readiness -contract on every platform. The profile supports Bash/Read/Edit, preparation, -shared Files/Artifacts, cancellation and history continuation. Other capabilities -remain subject to their actual engine qualification; bypass execution does not -silently qualify a new MCP or function combination. - -Recovery uses the SDK's history APIs. An explicitly supplied native identity must -exist. If Core requires existing history without having recorded an identity, the -adapter accepts only one nonempty native history for the exact bound cwd. Missing, -foreign, ambiguous or metadata-only history rejects before model input. The Runtime -volume and shared Environment/Session binding establish ownership; this lookup -cannot select another Session's home or infer ownership from a model response. - -### Deferred function discovery - -Public `tool_search` and function `defer_loading` are shared Runtime intent. -Core preserves complete immutable definitions, sends `PromptRequestPayload.ToolSearch` -and each `FunctionTool.DeferLoading`, and requires the operation's existing profile -qualification plus the Runtime `tool_search` capability. It never performs native -search, selects native names, interprets provider policies or implements another -model/tool loop. Additional harnesses implement the same intent in their adapters. -The pinned Session AgentTool response union excludes the tool_search input member; -project it out of Session/SSE resources while preserving saved and frozen input. - -The bounded implementation targets Claude's single-agent `environment:none` -function profile, including qualified inline message images. The adapter explicitly enables native ToolSearch and sets -per-function MCP `anthropic/alwaysLoad` from the requested deferral flag. Ordinary -functions stay eager. Existing function callbacks, application receipts, cancellation -and cold continuation remain the only execution/result lifecycle. Runtime discovery -advertises this operation only with an installed bridge supporting `tool_search`. - -Known conflicting native provider modes and search/beta settings reject in the -adapter. The maintained native harness owns dynamic model/provider eligibility; -its SDK exposes no reliable pre-input receipt proving effective deferral after a -policy change. Do not represent tool inventory or an operator allowlist as that -proof. Record exact real model/provider evidence and this detection gap separately. -Search-only, missing-search, workspace, MCP and Subagent combinations remain -unqualified; a repeated `tool_search` is a protocol error. See [the operation coverage](contracts/agents-api/tool-search.md). - -### Structured output execution - -The public `text.format={type:"json_schema",schema:{...}}` is resolved with saved -Agent overrides and frozen in the existing Session configuration. Core transports -it in `ExecutionControls.OutputFormat`; it does not append prompt instructions, -validate/retry model answers, repair JSON or select native tool names. Public -admission requires the selected profile's structured-output qualification, and -only requests using this option require the Runtime's `structured_output` and -message-observation capabilities. A capability advertisement does not qualify a -new public combination. Claude advertises this operation only when the installed -SDK bridge reports its `structured_output` feature. A workspace Runtime also -requires the complete local Runtime contract and `workspace_structured_output`; -preparation checks that bundle before native launch. These remain adapter readiness -features, not new Core lifecycle or public protocol variants. - -The current qualified path is Claude SDK, `environment:none` or Core-managed -Docker `openai_hosted`, medium verbosity, single Agent, with optional ordinary -function tools and text results. The workspace uses its existing preparation and -bypass execution profile with the SDK's configured `StructuredOutput` tool added to -inventory and permission checks. Frozen schemas reach preparation before the -input handoff; Start cannot replace them. Skills, Plugins, capability directories, -HTTP MCP, Subagent/tool-discovery combinations and schemas without an explicit -object root remain unqualified; an explicit non-object root type is a protocol -error for every harness. Check resolved template contents as well as inline configuration; -ordinary text requests retain their existing qualifications. -The SDK uses binary64 JSON numbers: reject execution schemas whose numeric values -would change during that conversion, without narrowing saved Agent storage. -Codex and MiniMax structured output remain explicit execution gaps. - -The Claude adapter passes `outputFormat` to the maintained native SDK and allows -its native `StructuredOutput` terminal tool. A matching live root tool result and -an attributed successful SDK result confirm the final output. Publish the native -`result.result` string unchanged as a completed `final_answer` Message using the -native tool-use ID; the parent assistant ID can already own a prose Item. Do not -publish unvalidated retry candidates or serialize `structured_output` back to -JSON. Native retries remain harness-owned. Existing input receipts, usage, -cancellation, release and recovery rules apply unchanged. The implementation and -qualification limits are recorded in [the coverage note](contracts/agents-api/structured-output.md). - -### Claude SDK adapter foundation - -The [Claude SDK adapter guide](packages/claude-sdk-adapter/README.md#bridge-and-native-lifecycle) -owns the private bridge, native configuration, observation translation and -SDK-specific settlement rules. Use the common Executor/Turn interfaces and the -same Runtime path as every other harness. - -### Private Claude SDK runtime artifact - -The [package artifact contract](packages/claude-sdk-adapter/README.md#runtime-artifact) -owns build isolation, dependency export, readiness and relocation checks. -Keep the adapter artifact independent of the Core binary and product sources. - -### Harness model configuration - -The configuration preparation contract in `apps/parsar-daemon/internal/agent/harness.go` -is mandatory for every Executor factory. Shared wire types live in -`internal/agentdaemon/proto`; native model fields and their application are documented -in [Harness onboarding](contracts/agents-api/harness-onboarding.md#native-model-configuration). -The standalone Core build includes the shared `internal/modeltransport` route -qualification code alongside the adapter declarations; Runtime still owns proxy -startup and request forwarding. -Public configuration sources and inheritance belong to -[model execution](contracts/agents-api/model-execution.md). - -### Harness selection and Agent defaults - -Core accepts the optional `agent.x_agents_core.harness` extension through the -saved Agent and inline Session configuration paths. Define the extension once in -`contracts/agents-api/v1`; never use metadata or a competing top-level selector. -Resolve saved overrides before selecting the existing Session engine, and apply -that engine's execution policy before persistence. Omitted selection preserves -the deployment default; explicit unavailable selection fails without fallback. -Effective extension reads use the persisted engine; Sessions without the extension -retain the official Agent response shape. See the [extension contract](contracts/agents-api/harness-selection.md) -for null/retry behavior and operator configuration. - -Hosted provider selection belongs to deployment configuration and is independent -of the engine. Session creation fixes a node through automatic placement; retained allocations keep that node and provider identity. Runtime images must satisfy their existing qualification rules. -Transient model options are partitioned by engine and must not expose another -engine's credentials. Do not infer an engine from a model name or template. - -### Shared Runtime capability preparation - -Resource management retains provider placement, capacity, allocation and Environment -create/renew/reclaim operations. The authenticated Runtime connection carries -initialization, capability preparation and executor operations; it does not implicitly -allocate or destroy compute. Connection loss, executor idle close and Turn cancellation preserve -the workspace and installed snapshot. Resource reclamation coordinates with active -work through its existing owner. - -Core freezes resource versions, metadata and local source selections. Managed -initialization keeps its existing order: initial files, tool configuration, -Skill/Plugin bundle import, packages and setup commands, then directory snapshot -finalization. Every step uses the same daemon and `runtime_prepare` exchange. -Providers only place, create, bootstrap, inspect, renew and reclaim resources; they -do not execute Core initialization commands. The common runner uses only neutral -Environment/Session identity and a Runtime peer, with no Provider, deployment or OS -branch. Harness differences belong to native adapters. Package installation and -setup use the host network; the daemon does not introduce a network sandbox. -A missing authenticated Runtime connection waits before capability initialization -is claimed; an operation whose effect is unknown is not replayed. Self-hosted -capability directories remain in immutable Environment configuration and never -create a managed initialization row or allocation. - -The private `runtime_prepare`/`runtime_prepare_result` exchange carries typed -initial files, configure/npm/python/setup operations, inert Skill/Plugin -archives and finalization selections, with canonical Session and Environment -identities. Files and setup working directories use logical `/workspace` addresses; -setup commands are explicit typed input, while executable selection and physical -destinations remain Runtime-owned. Ordered 64 KiB chunks and SHA-256 receipts -bound each file or archive to 50 MiB, each control frame to 1 MiB and each connection -to one transfer. Initialization and finalization headers carry no file data. Begin, -chunk and commit are never retried; -rejected, failed and unknown effects remain distinct. Runtime owns transfer and -filesystem work through settlement, including disconnect or cancellation. - -The source-selection protocol accepts portable absolute Unix, Windows drive and -UNC paths without consulting Core's filesystem. Runtime interprets and authorizes -local paths. Linux, Windows and macOS use the same preparation implementation, -parser and installed manifest. Runtime paths are chosen by the operator and never -by a transfer request. Installation and package directories default under -`OAC_RUNTIME_HOME`; packaged images can select their existing directories through -explicit Runtime configuration. The manifest binds Session/Environment identities -and the ordered source-selection digest. A completion receipt prevents a deleted -snapshot from being mistaken for first preparation, while filesystem locking -prevents concurrent installation. Invalid, partial or foreign snapshots fail -without deleting data or silently reinstalling. Reconnection loads installed -contents without rereading sources; a new Session captures a fresh snapshot. - -The daemon runs as its launching account and never uses sudo or elevates its -permissions. Only user-directory dependencies are installed during preparation. -System dependencies belong in managed image/template builds or must be installed -by the self-hosted user. `packages.system` is rejected explicitly, including in -templates; it is not ignored or translated into a privileged operation. - -Synchronous executor admission validates the frozen descriptor only. The admitted -asynchronous preparation owner ensures capabilities are ready before invoking the -native factory, including an empty inventory. Resolved Skill paths and MCP declarations -remain Runtime-only fields; engine adapters consume this common result. Reused -Session executors retain their original configuration. Files reads retain their -separate authorization and do not require capability or native execution readiness. - -Self-hosted public input remains `workspace_directory` and optional -`capability_directories`; it does not accept managed archive fields. Its public -installation arrays describe API-managed uploads and stay empty for local directories. -MCP environment variables must come from explicit immutable Runtime tool configuration; -missing variables never fall back to daemon credentials or ambient process variables. -No project-version upgrade or historical manifest compatibility is introduced. -These implementation rules do not establish new real-deployment acceptance; -historical evidence remains limited to its recorded binaries and inputs. - -### API-key write provenance - -Public resource writes carry authenticated key provenance separately from the -execution principal. Persist their operation record and genuine creation ownership -in the same business transaction; no best-effort response middleware or async audit -queue. A failed audit must roll back the write. Internal lifecycle/refresh work does -not acquire public provenance. Retries never replace ownership. Environment uploads -persist safe request origin before dispatch and record success with the confirmed -receipt, not the native filesystem call. Never put payloads, paths or secrets in -audit metadata. Read models are Core-key `/core/v1` routes; keep `/v1` wire -contracts unchanged. See -[write-audit.md](contracts/agents-api/write-audit.md) for coverage, retention and -console integration. Do not confuse key identity with Session creator identity. - +| Variable | Value | +| --- | --- | +| `OAC_TEST_DATABASE_URL` | A dedicated test database. The full gate fails when it is missing. | +| `OAC_TEST_OFFICIAL_SDK_PYTHON` | The pinned official SDK interpreter | + +The role needs `CREATE DATABASE`: managed-provider tests create and drop +isolated `oac_*_tests` databases because provider identity is deployment-wide. +`PARSAR_AGENTS_API_TEST_DATABASE_URL` is retired; `make check-database` reports +its replacement when only the old name is set. Tests must not bypass the +production provider-switch guard. + +### Contract and schema rules + +- `make sqlc-generate` owns only `services/agents-api/internal/db/sqlc` + (sqlc v1.29.0). Do not rewrite landed migrations. +- The public protocol schema is `contracts/agents-api/openapi.yaml`. Preserve + its pinned types, coverage ledgers and official SDK/raw HTTP tests when + changing API behavior. Core changes keep the independent build and + official-client workflow. +- `make check-runtime-contract` is the focused Core–Runtime contract entry + point; see [Contract verification](docs/runtime-protocol.md#contract-verification). + It also runs through `check-go` and `check-agents-api`. + +### Live acceptance + +Native adapter changes require their build/check targets and live provider +acceptance. Real execution checks need real models; omitted prerequisites or +mocked responses do not count as live acceptance. Historical remote native +probes are not current validation entry points. ## OpenAgentCore Runtime names -Runtime distributions use `oac-daemon`, the `oac-*` filesystem and initialization -helpers, `OAC_RUNTIME_*` settings, and `~/.oac/daemon` state. Provider bootstrap, -Runtime images and harness adapters must agree on these names. Daemon startup -rejects renamed settings before any subcommand and reports replacements without -values; the separate Parsar product integration settings remain unchanged. -Environment `env` reserves every `OAC_` name. Provider ownership labels use -`io.oac.*`, and E2B metadata uses `oac_*`; neither accepts old labels as a fallback. -Historical Runtime and project-version upgrades are not supported. Preserve older -installations, Runtime files, provider resources and Session history; install the -current release separately. Use this release's template builder for new E2B -templates. Landed migration files and historical acceptance evidence remain intact -as repository history; they do not establish a supported upgrade procedure. Normal -current-version database initialization still uses the ordinary migration runner. - -The dormant Pi adapter retains its `parsar` provider slug because the separate -Parsar product pins model selections to that external identity. This is a product -boundary exception for the name guard, like the skill-upload integration, rather -than a legacy Runtime setting. Build the MiniMax companion from this revision's -patched native sources when packaging a renamed Runtime; an older companion still -uses the old model-provider, workspace and subagent names and cannot be reused. - +| Surface | Current name | +| --- | --- | +| Runtime binary | `oac-daemon` | +| Filesystem and initialization helpers | `oac-*` | +| Runtime settings | `OAC_RUNTIME_*` | +| Runtime state | `~/.oac/daemon` | +| Reserved Environment `env` prefix | `OAC_` | +| Provider ownership labels | `io.oac.*` | +| E2B metadata | `oac_*` | + +Provider bootstrap, Runtime images and Harness adapters must agree on these +names. Daemon startup rejects renamed settings before any subcommand and +reports replacements without values; the separate Parsar product integration +settings remain unchanged. No old label is accepted as a fallback. + +Historical Runtime and project-version upgrades are not supported. Preserve +older installations, Runtime files, provider resources and Session history; +install the current release separately. Use this release's template builder for +new E2B templates. Landed migrations and historical evidence stay as repository +history; ordinary current-version database initialization uses the migration runner. + +The dormant Pi adapter keeps its `parsar` provider slug because the separate +Parsar product pins model selections to that identity. This is a product +boundary exception for the name guard, like the skill-upload integration. + +Build the MiniMax companion from this revision's patched native sources when +packaging a renamed Runtime. An older companion still uses the old +model-provider, workspace and subagent names and cannot be reused. ## OpenAgentCore name guard -`make check-names` scans tracked text for retired branding, settings and installed -command names. Exceptions in `scripts/name-allowlist.json` name a path glob, a -regular expression and a reason. An exception covers only its matched text: an -allowed repository import cannot hide a retired setting elsewhere on the line. -Keep exceptions narrow and explain the preserved contract or historical input. - -Go module and source directory paths, npm and Cargo package identities, public -`AgentCoreError`, upstream contract fields and the separate Parsar product remain -unchanged. Persisted credential encryption domains and native-session resume keys -also remain stable so existing data can be decrypted and Sessions can resume. -Conversion inputs, retirement diagnostics and historical evidence must still name -the identifiers they reject; historical migration files retain their original -identifiers as evidence, while current examples use the new names. - - -Core administration error details are scoped by the `/core/v1` router writer mark, -not a request path test. Use `writeCoreError` with typed `CoreErrorDetails` values -and document fixed keys in `contracts/agents-api/core-errors.md` when adding a -code. Include only safe Core-owned facts; never pass submitted values, secrets, -native text or provider bodies. Invalid/empty details are omitted. Preserve the -public and machine error serializers, observer callbacks and streaming interfaces. -The Core client ignores malformed optional details and never retries a mutation. - -Core operation validators preserve the original error text, sentinel identity, -and validation precedence. Package-owned typed errors carry fixed field metadata; -only the marked Core error mapper translates it to operation codes and safe -bounds/catalog details. Preserve Project/key rune limits and node byte limits -separately. Keep sandbox validation metadata through its existing store wrapper -without changing transaction or provider authority. Public Session provider -validation remains byte-compatible; cover it with handler-level golden responses. - -Root Session/Turn diagnostics are Core-key reads from one committed database -snapshot, reusing public status projection and precedence. Keep their finite -failure catalog separate from private outcome text. Persist safe provisioning -details in the existing failure transaction; never reconstruct historical details -from reason strings. Item settlement belongs to the existing Session lock and -terminal transaction: event/input receipt for normal terminal projections, one -post-lock database wall-clock sample for forced incomplete Items. Preserve -historical terminal nulls and public native completion times. See the -[diagnostics contract](contracts/agents-api/session-diagnostics.md). - -## Native daemon and Harness installation - -`oac-daemon install` owns interactive selection and CLI-only installation through -one options/validation path. No installation-options file input is supported. -Persisted installation state and explicitly supplied credential/tool-variable -files serve runtime operation, not a second installer configuration language. -The release bundles pinned Node/npm, native Harnesses and required adapter assets; -registration lives in CLI and native activation/readiness in each adapter's optional -`agent.Installation` descriptor. Core never selects native paths or OS-specific -installation steps. See [native installation](docs/self-hosted-native.md). -Self-hosted onboarding extends authenticated Session creation/detail responses with -`x_agents_core.installation`; Web displays the same Core-produced commands. Lists -and durable event journals never retain installation authorizations. The command -uses a 30-minute, Environment- and build-scoped grant to claim one connect-only -credential. The installer persists its generated secret before claiming it; retries -must prove that same secret. Reserve the Environment UUID as the onboarding key ID. -Existing, rotated or revoked credentials are never replaced by onboarding. Machine -bootstrap routes use this grant, not an Environment ID as authentication. Public -artifact routes contain no credentials. Native bundles must match the Core source -revision and Runtime wire version. Core release qualification consumes the same -three-platform native CI artifacts and includes them in its distribution. -Bootstrap scripts own platform download/extraction only; installation, startup, -connection verification and Runtime execution remain common. Serialize background -PID inspection and publication so concurrent starts cannot create duplicate daemons. +`make check-names` scans tracked text for retired branding, settings and +installed command names. Each exception in `scripts/name-allowlist.json` names a +path glob, a regular expression and a reason. -An installed daemon discovers and registers only the adapter kinds named by its -verified installation manifest. The host PATH stays available to tools; its other -Harness executables and activation variables cannot extend that installation. -Direct `connect` rejects an installation manifest and directs the operator to -`start`; unmanaged image bootstrap remains available without that manifest. +- An exception covers only its matched text: an allowed repository import cannot + hide a retired setting elsewhere on the line. +- Keep exceptions narrow and explain the preserved contract or historical input. -All mutations use the installation directory lock. Publish complete checksum-verified -components from staging, then commit configuration after native readiness passes. -Re-running with the same connection settings adds selected Harnesses and validates -existing contents. Never overwrite, upgrade, auto-repair or migrate installed -components. Missing, modified, wrong-platform or incompatible content is an explicit -error. A partial addition must preserve the old configuration and allow reuse of -complete components; it must not remove previous files or data. +These identities stay unchanged: -Installers run as the current user in writable directories. Native subprocess -diagnostics must not expose sensitive parameters or environment values. Adapter -readiness checks receive the installer cancellation context and must reap owned -processes before returning after interruption. Readiness, -authenticated connection and model configuration are separate reported facts. -Starting execution must not download or install Harnesses. Ordinary stop/reconnect -must preserve capability snapshots and native Session state. +- Go module and source directory paths, npm and Cargo package identities; +- public `AgentCoreError`, upstream contract fields and the separate Parsar product; +- persisted credential encryption domains and native-session resume keys, so + existing data can be decrypted and Sessions can resume. -`scripts/build-native-installer.mjs` packages native inputs, validates pins/startup -and hashes every component file. It uses contained regular files and rejects -escaping links. Claude's dedicated frozen `pnpm deploy` export is reified with -the hoisted linker for this distribution before contained links are flattened; -the original standalone Claude archive contract remains unchanged. The native -installer workflow builds and tests on Linux, macOS and Windows, including actual -installation, addition/reuse, missing arguments and unsupported Windows MiniMax. -Native CI startup checks do not replace real model execution evidence or claim -manual Windows acceptance. Heavy builds belong on remote servers or CI. +Conversion inputs, retirement diagnostics and historical evidence must still +name the identifiers they reject. Historical migration files keep their original +identifiers; current examples use the new names. diff --git a/apps/web/README.md b/apps/web/README.md index 035b8db4e..f64cb7170 100644 --- a/apps/web/README.md +++ b/apps/web/README.md @@ -25,6 +25,125 @@ See the [Core Web guide](../../docs/web/README.md), [frontend handoff](../../docs/web/roadmap.md) and [administrator API](../../contracts/agents-api/admin-api.md). +The Core Web is an administrator console. Web calls only `/core/v1`, with the Core +key held on its server, and never `/v1` or `/api/v1`. Applications use an API key issued inside a Project. One Project owns one execution +tenant and principal; all its keys share assets and permissions while writes retain +individual key provenance. Projects and keys are database-owned, with no static +business keys or configuration synchronization. Revocation affects one key; +archiving a Project revokes all its keys, retaining assets and admitted execution. +Do not add Core users, roles, memberships or cross-Project sharing. Management +provides safe reads, public deletion preconditions, explicit hosted Session archive, +Project and key operations and credential issuance; it cannot copy, execute or edit +arbitrary assets. +Keep administrator target scope separate from caller principals. See +[design principles](../../docs/design-principles.md) and the +[administrator contract](../../contracts/agents-api/admin-api.md). + +### Console structure + +Core Web leads with operations: Monitor (Overview, Core metrics, Agent metrics, +Sandbox metrics, Session log), Resources and Platform. Pages use the shared +components in `apps/web/src/components` and the tokens in `apps/web/src/styles`, +described in `apps/web/DESIGN.md`. Keep explanations behind help tips, but keep +errors, warnings and safety notices visible. Browser-derived metrics state their +coverage, keep missing values missing, bound their fan-out and time, report a failed +read as failed and never imply deployment-wide or billing totals. +Sandbox deployment setup, configuration, reset and progress belong to the System +secondary page (`#system?id=sandbox`). Nodes owns node management; Overview and +metrics pages link to these owners instead of repeating their controls or details. +Keep uncommon resource edits and rollout details in dialogs, and avoid repeating +the same information within or across pages. Core responses remain the source of +truth for deployment and connection state. + +### Console server and sign-in + +`services/core-console` serves the production Web build and, after console login +and same-origin checks, forwards every `/core/v1` request with the Core key; Core +decides whether the route exists. It requires the private Core key file named by +`OAC_WEB_CORE_KEY_FILE` and holds no project caller credential. Every `/v1` +and `/api/v1` request returns 404, including explicit Bearer and WebSocket +requests; Web forwards no node or daemon transport. The installer mounts only the +Core key into Web and only its digest (`OAC_CORE_KEY_DIGESTS_FILE`) into +Core. The browser receives safe configuration, never that key. The deployment's +TLS reverse proxy routes `/v1` (applications) and `/api/v1` (nodes and Runtime +daemons, with their own credentials) directly to Core and everything else, +including `/core/v1`, to Web. Operator scripts call `/core/v1` on Core's loopback +port. +Nodes and Core come from one distribution. Older nodes using the removed +`/core/v1/sandbox` paths are unsupported; preserve their installation and data and +use a separate fresh installation. There is no drained in-place upgrade or +historical re-enrollment procedure. Current-version Runtime generation rollout +retains its separate resource lifecycle. + +The Web manager offers no manual Core key entry outside sign-in, and Web refuses +to start without its Core key file. It holds no Project API key and never calls +`/v1`. +Chinese/English sandbox text, status and diagnostic formatting live in the shared +`apps/web/src/lib/` locale modules. A persisted explicit language preference wins +before the first browser language; unrelated product surfaces are outside this +translation scope. Preserve zero-node setup and node installation behavior when +localizing their controls. The sandbox manager centers node readiness and capacity in a desktop topology, +with Core surrounded by actual node buttons. Connection animation represents +liveness only, never invented traffic or work; offline/stale connections are +static and reduced-motion preferences disable decorative animation. Node selection +reveals inspection details. Installation identifiers, provider metadata and +allocation records are secondary content. Node enrollment is an explicit Add node action in a focused +dialog, using the deployment's `core_url` (the installation public URL). +Do not expose routine network wiring or manual runtime setup as the primary flow. +Generate a one-time command only on user intent, never retry enrollment writes +automatically, and discard credentials and late responses when the dialog closes +or the Core connection changes. Core reports the command's `enrollment_id` on the +node it registered (null for nodes enrolled before Core recorded it), and Web follows +the added node by an exact match on it; an existing node reconnecting is not a new +enrollment. +The command verifies the installer checksum before execution, retains normal TLS +verification, and passes the enrollment credential only to the installer process, +on standard input. + + +The `/core/v1` proxy retains fixed-origin, cross-site, safe-path, redirect and Upgrade +restrictions through the standard Go reverse proxy with streaming/cancellation; +literal or encoded dot segments can never move a request out of `/core/v1`. +The console implements no product identity, resource semantics, Runtime discovery +or execution loop. Signing in with the Core key grants the complete console +surface; do not introduce Web accounts, roles, invitations or per-project Web +identities. Agent API caller keys remain independent of the Core key and cookie. + +Web signs in only with the Core key (`POST /console/auth/login` with +`{"core_key":"…"}`), compared in constant time with the console's configured key +and never logged or echoed. There are no accounts, passwords, first-run setup or +Basic authentication. The retired authentication-mode, state-directory, +password-file and admin-token-file settings fail startup; their names are listed +in [`config.go`](../../services/core-console/config.go). Cookie sessions are in memory, bounded, HttpOnly, SameSite Strict and +Secure for HTTPS origins; a restart or Core key rotation requires sign-in again. +Unauthenticated access is limited to the static login UI, finite console +authentication routes and the static node installation payload. Sign-in uses +same-origin JSON POSTs with bounded bodies and bounded concurrent work. Only +failed attempts are rate limited, so the correct key always signs in; Web and the +installer therefore require Core keys of at least 32 characters. See the +[Core key operations guide](../../docs/getting-started/operations.md#core-key). + +Projects and application API keys live in Core PostgreSQL. Project creation owns +its scope and shared principal; key issuance, revocation and Project archive share +a transaction with audit. Issuance stores only a digest and metadata and returns +plaintext once. Keys cannot be read back or reset in place; rotate by issuing a +new key in the same Project and revoking the old key. Authentication checks the +key and Project on every request, without a credential cache, and fails closed on +database errors. Deployment credentials cannot authenticate to the public API. +Configuration defines no Projects or business API keys. Fresh installation starts +with no Projects; an administrator creates a Project and then issues a key. + +Administrator onboarding covers console login, Project creation, key issuance and +optional node enrollment. Model execution belongs in an external API example using an issued +key. Keep secrets out of browser persistence, generated examples and URLs. Observe +confirmed resources through the management API; do not infer Agent-to-node ownership +or execution readiness from a host connection. Preserve keyboard focus, reduced +motion and the existing node enrollment/topology contract. +The console has neither KVM nor Docker authority; its static root contains no +secrets. Installation exposes only loopback API/console ports. Remote exposure +requires an operator-configured HTTPS/access boundary. Web-only mode can connect +to a loopback existing Core on the same Linux host or a remote HTTPS Core. + ## Local checks From the repository root, with Node 22 and pnpm 10.30.3: diff --git a/contracts/agents-api/README.md b/contracts/agents-api/README.md index 30d9cf2c7..dc771e4b1 100644 --- a/contracts/agents-api/README.md +++ b/contracts/agents-api/README.md @@ -22,7 +22,7 @@ Parsar owns product Agents and Teams. This service owns upstream execution resources, including reusable Agents and protocol subagents. The OpenAI Agents Python **SDK** is a separate future dependency for business Team orchestration in Parsar, not the HTTP contract. Design rules live in -[CONTRIBUTING.md](../../CONTRIBUTING.md#design-and-compatibility-requirements). +[CONTRIBUTING.md](../../services/agents-api/IMPLEMENTATION.md#design-and-compatibility-requirements). The [resource selector and error qualification](resource-selector-semantics.md) records nullable Skill references and source Files not-found parameter fields, @@ -164,8 +164,8 @@ this documentation update. Evidence on `zju_a100_2`: `acceptance-results.json`: final baseline, runs, reused evidence and cleanup. - `~/.parsar/remediation/20260919/docker-mvp/` and `claude-v1/`: preceding deployment and safety acceptance. -- [MiniMax workspace qualification](mcode-workspace-v1.md) and - `~/.parsar/remediation/20260919/mcode-workspace/`: native isolation and recovery. +- `~/.parsar/remediation/20260919/mcode-workspace/`: MiniMax workspace native + isolation and recovery (historical). Qualification is Linux amd64 Docker V1, not arbitrary host isolation, production HA, E2B, or Anthropic-model acceptance for Claude Code. No exactly-once guarantee @@ -211,8 +211,7 @@ user-managed enrollment remain outside this qualification. An implementation gap and an unknown upstream behavior require different follow-up work. Retain both explicitly; a restrictive local policy or successful SDK parse -cannot establish upstream equivalence. The Feishu board owns live task selection, -including further deployment qualification; this inventory describes merged behavior. +cannot establish upstream equivalence. This inventory describes merged behavior. ## Public semantics @@ -920,7 +919,7 @@ establish complete ownership, hosted key lifecycle or error compatibility. See t Core documents its optional [harness selection extension](harness-selection.md) separately from the pinned upstream contract. -The former [Core startup configuration](startup-configuration.md) read is removed. +The former Core startup configuration read is removed; the [installation read](installation.md) reports the public URL and installer process settings. Model endpoints and credentials may be supplied at Session creation through the [write-only execution extension](model-execution.md). Provider catalogs and their diff --git a/contracts/agents-api/admin-api.md b/contracts/agents-api/admin-api.md index d81275c99..87b6b6b5c 100644 --- a/contracts/agents-api/admin-api.md +++ b/contracts/agents-api/admin-api.md @@ -179,6 +179,13 @@ action `set` or `delete`; a `project_id` filter excludes them. No credential values or request bodies are recorded. Logs and historical copy ownership do not cascade away on resource removal or key revocation. +Administrator writes and their audit record share one PostgreSQL transaction. +Reuse existing resource deletion and serialization code. Cross-Project copying was +removed; only the historical `source:"admin_copy"` provenance read remains, fed by +`admin_resource_owners`. Unknown historical provenance remains unknown. No secrets +or request bodies enter logs. The console's fixed actor label (`console`) is only +an audit display label, never an authorization input. + ## Private installation transition The old configured business keys, inherited-binding issuer and key-space diff --git a/contracts/agents-api/core-errors.md b/contracts/agents-api/core-errors.md index 5cb715b76..87c45d857 100644 --- a/contracts/agents-api/core-errors.md +++ b/contracts/agents-api/core-errors.md @@ -117,6 +117,22 @@ public `/v1` and machine routes retain their previous complete error bodies. Unknown sandbox providers retain the existing untyped error. E2B provider errors above retain their fixed redaction contract with no echoed template or key. +Core administration error details are scoped by the `/core/v1` router writer mark, +not a request path test. Use `writeCoreError` with typed `CoreErrorDetails` values +and document fixed keys in `contracts/agents-api/core-errors.md` when adding a +code. Include only safe Core-owned facts; never pass submitted values, secrets, +native text or provider bodies. Invalid/empty details are omitted. Preserve the +public and machine error serializers, observer callbacks and streaming interfaces. +The Core client ignores malformed optional details and never retries a mutation. + +Core operation validators preserve the original error text, sentinel identity, +and validation precedence. Package-owned typed errors carry fixed field metadata; +only the marked Core error mapper translates it to operation codes and safe +bounds/catalog details. Preserve Project/key rune limits and node byte limits +separately. Keep sandbox validation metadata through its existing store wrapper +without changing transaction or provider authority. Public Session provider +validation remains byte-compatible; cover it with handler-level golden responses. + ## Diagnostic failure categories The [root diagnostics reads](session-diagnostics.md) return these categories diff --git a/contracts/agents-api/core-metrics.md b/contracts/agents-api/core-metrics.md index 1879a0ce0..3059e34b9 100644 --- a/contracts/agents-api/core-metrics.md +++ b/contracts/agents-api/core-metrics.md @@ -115,3 +115,23 @@ retention and execution lifecycles keep their existing owners and timing. No additional telemetry database, monitoring server, model-provider probe, message queue, object store, host disk measurement or scheduling mechanism is introduced. Metrics cannot authorize execution or change resource ownership. + +## Implementation rules + +The Core-key-only `/core/v1/metrics` contract is documented in +[core-metrics.md](core-metrics.md). Keep this separate from +Agent outcome and Sandbox capacity views. Instrument existing worker and job +owners without changing scheduling, lease or retention behavior. Periodic pool +pings and bounded in-process samples have explicit restart gaps; unknown values +must remain null. Complete UTC buckets exclude the active partial bucket. Root +Turn history is queried read-only from PostgreSQL with native timestamps. +Count `execution_unavailable` at the existing HTTP error writer, once per rejected +response; never record request/response bodies or infer this count from every +503 or failed Turn. Builds inject the source commit with ldflags. No new monitoring +service or storage system is required. Keep the frontend response shape aligned +with the paired console contract. + +Core process CPU, RSS and cgroup limits are sampled by the existing 30-second +Core metrics loop; Go heap and goroutine reads retain their meaning. Process +series use the same bounded ring and complete buckets, with null first CPU +intervals and restart gaps. Do not substitute host usage for process usage. diff --git a/contracts/agents-api/environment-templates.md b/contracts/agents-api/environment-templates.md index 34a09ad82..a8635a950 100644 --- a/contracts/agents-api/environment-templates.md +++ b/contracts/agents-api/environment-templates.md @@ -698,7 +698,7 @@ test and the complete Core `make check` passed. Optional Docker fault fixtures w not enabled; real public Docker runs cover the accepted paths. The first full-check invocation lacked the server's OpenSSL development paths; the corrected invocation passed with a fresh database. Evidence and failed attempts remain under -`~/.parsar/remediation/20260921/template-network-native/` and the Feishu task record. +`~/.parsar/remediation/20260921/template-network-native/`. Core SHA-256: `0dc40384192fc75c4be9896072dfe0089c30a02e44804faca7a14c7d8525efa3`. E2B probes remain mechanism evidence only; this batch does not qualify official E2B self-hosted onboarding or complete upstream network semantics. @@ -795,7 +795,7 @@ historical qualification is recorded below. Unconfirmed reference overrides remained gaps in that evidence, and the retired native Codex hook's failure limitation was not resolved by this run. Private sanitized run/check/build evidence is retained under -`~/.parsar/remediation/20260920/environment-template-setup/` and the linked board. +`~/.parsar/remediation/20260920/environment-template-setup/`. These results do not establish complete Template or Agents API compatibility. @@ -831,8 +831,7 @@ qualified base images after registry DNS failure; current daemon, adapter and initializer artifacts were copied using the repository packaging recipe. Raw receipts retain their inherited historical manifest fields; accompanying source, Core and image hashes identify the actual candidates. Evidence is retained under -`~/.parsar/remediation/20260920/environment-template-skills` and the linked board -record. This profile does not establish complete upstream Skill semantics. +`~/.parsar/remediation/20260920/environment-template-skills`. This profile does not establish complete upstream Skill semantics. ### System-package qualification (2026-09-20) @@ -900,7 +899,7 @@ and private-state checks. Interactive PTY behavior and packages needing addition Unix identities or privileged services are not qualified by these results. Sanitized results, image/source hashes, full checks and failed evidence are retained -under `~/.parsar/remediation/20260920/environment-template-capabilities` and the board. +under `~/.parsar/remediation/20260920/environment-template-capabilities`. Early Docker result manifests contain inherited installer archive fields; those fields do not qualify a new installer archive. Current binary and image hashes identify the tested deployment. These checks do not establish complete upstream @@ -955,3 +954,44 @@ Core updates the default pointer and top-level name/description atomically, whil concrete version bytes and previously frozen Sessions remain immutable. Deleting the last version deletes the Skill; version numbers are intentionally never reused. Complete errors and visibility timing remain gaps. + +## Implementation rules + +Public Environment Templates belong to Core and its execution database, independently +of provider image/build templates. Resolve a tenant-owned reference once at Session +creation, freeze the effective ordinary hosted configuration and reuse inline +initialization. Do not pass template IDs into Provider or Runtime. Omitted or null +network inherits the complete template policy; overrides may only narrow policy. +Preserve unresolved caller intent for creation retries and recover committed +results before reading mutable templates. +For template-reference Session initialization, omitted/null env, files, commands +and packages inherit. Overlay non-null env keys; replace non-null files and command +lists, including empty lists. Select each package manager independently: omitted/null +inherits, while a supplied list replaces that manager. Revalidate the effective +configuration through the existing validators and freeze it through the same +transaction as inline initialization. Keep caller intent separate from resolved +configuration; do not add a second installer or pass merge rules to adapters. +Updates and deletion cannot rewrite existing Session snapshots. Initial files use +one Core-owned installer for template and inline configurations. Keep confidential +bytes encrypted under the execution-service key and resource-bound AEAD, separately +from ordinary configuration and public metadata. Templates retain source references; +Session creation freezes tenant-authorized source bytes in the same commit, independent +of later source/template deletion. Public resource reads must not require decryption +or load encrypted file bodies. Record original creation intent before resolution. + +Template parsing, persistence and resolution must not select a harness or Provider, +or depend on native tool names and private harness paths. The shared initializer +uses the packaged Runtime contract for trusted commands, workspace/staging paths, +confidential input and completion receipts, following +[Runtime capability preparation](environments.md#runtime-capability-preparation). +Each adapter owns native tool configuration. A new harness or Provider must not require template +business-logic changes. Reuse qualified shared helpers even when their executable +names have historical engine prefixes; renaming is not a boundary fix. Select real +regressions by the changed shared, Provider and adapter boundaries, rather than +repeating every deployment combination for each configuration field. + +Name, initial files, inline/referenced Skills, Plugins, workspace capability directories and env/setup/npm/Python are +implemented independently of remaining installation fields. Reject unsupported +inputs rather than persisting them for silent +omission; expand inline and template initialization together in separately qualified +batches. Resource reads need only tenant authorization, not a live Runtime. diff --git a/contracts/agents-api/environments.md b/contracts/agents-api/environments.md index a20123c77..e1565139b 100644 --- a/contracts/agents-api/environments.md +++ b/contracts/agents-api/environments.md @@ -122,7 +122,7 @@ connection alone is not readiness. Retries preserve identity and the five-minute database deadline. HTTP disconnect retains the reservation. Local expired/cancelled outcomes return 409, lost ownership 503, and deletion 404; exact hosted error status/body and pending-input crash recovery are unverified. See the -[canonical wait rules](../../CONTRIBUTING.md#environment-ownership-and-placement). +[canonical wait rules](#ownership-and-placement-decision). Cancellation-only batches use the existing locked admission and native delivery. An idle cancellation creates no Turn, and retry identity preserves the original @@ -155,49 +155,455 @@ exercise fixed SDK/raw HTTP creation/input, actual native tools, Files/Artifacts second-Turn history, Core/Runtime restart, cancellation and credential rotation/ revocation/deletion on each declared deployment. +User-managed onboarding creates a `self_hosted` Session first, then passes its +Environment ID and unchanged `remote_url` to our Runtime with connect-only +authorization. This is our daemon connection contract, not stock OpenAI +`exec-server` transport compatibility. Validate the pinned public HTTP/SDK +resources, state transitions and lifecycle separately; do not infer complete +compatibility from a working connection. User-side tooling owns local Runtime or +E2B allocation, renewal and cleanup. Session deletion and credential revocation do +not transfer ownership of user compute to Core or prove process quiescence. + +Keep prerequisites specific to the public operation being implemented. Native +harnesses execute; adapters translate protocols and fill demonstrated capability +gaps; Core owns public semantics, authorization and resources. Before adding a +mechanism, identify the current operation it enables and why existing native +capabilities or interfaces do not suffice. Durable metadata queries need no live +runtime. Live file reads require an authorized, isolated view of the exact workspace +and bounded operation ownership, but not a complete file-write, environment +replacement or placement-retirement implementation. Apply mutation fencing and +retirement guarantees where an operation can write, replace or retire that owner. +Read-only access still requires tenant/resource checks, path isolation and safe +failure when the authorized workspace cannot be reached; it never grants public +admission merely because an adapter advertises a capability. + +Use distinct authorization for callers, devices and environment connections. A +managed outer Environment must exclude broader application credentials and other +tenants' secrets. The daemon does not hide its own state from same-user tools. +Directory bindings and process identities do not provide filesystem isolation. +Preserve or demonstrably restore native history across +compute replacement; never silently move a bound Session or replay unknown work. +Self-hosted compute/files remain caller-owned, with explicit cleanup separate from +Session deletion. The full Environment implementation remains pending; follow the +[pinned contract and acceptance sequence](environments.md) +and the [two-engine placement prerequisites](workspace-placement.md). +For co-location, qualify the outer deployment boundary and the shared Runtime +lifecycle. Native tools use the starting account's permissions; do not claim a +daemon or harness sandbox. Host operators choose their own outer isolation. +Never enable an execution combination whose required outer behavior is unverified. + +The internal Store creates one Environment with an environment-bearing Session in +its creation transaction. The Session upsert selects the retry winner; retries +never create or repair associations. Environment identity/state live in their own +table. Tenant ownership and immutable configuration come from the owning Session, +without duplicated JSON, tenant columns or generated IDs in the creation hash. +Environment reads join that Session and exclude deleted Sessions; deletion retains +ownership for later settlement/cleanup. Existing `none` and legacy missing +configuration create no Environment, and historical internal snapshots are not +backfilled. Creation and recorded-intent retry snapshots load the Environment with +the Session row/cursor in the same transaction, without borrowing subsequent +activity or Turn state. Initial state is `pending`; authenticated connection observations follow +the lifecycle rules below. +Public creation supports `self_hosted` on the three enabled native profiles when +the daemon gateway is configured. The workspace is a clean absolute host path matching the bound Runtime root; +`/workspace` denotes that root through the existing logical mapping. Optional +capability directories are clean absolute host-local selections. Supported non-deferred functions keep their engine-specific +validation and native callback bridge. Enrollment binds the dedicated Runtime; +Session output uses the owned Environment association. Public Files reuse the exact +local workspace; populated self-hosted installation metadata remains unsupported. + +Environment retrieval uses the existing tenant-scoped join to a live owning Session +and its durable connection status, independently of a live Runtime or gateway setup. +It preserves project-shared read access and exposes only the pinned resource fields. +The current closed self-hosted configuration has no API-managed file, plugin or skill +installations, so those required arrays are empty. They are not a filesystem listing +or a claim about native discovery. Reuse the strict Environment configuration parser +and reject unsupported installation fields/capabilities or resource states instead +of treating unknown inventory as empty. No read initiates native work or changes +connection state; connection does not establish readiness or process quiescence. + + +The private Environment input reservation stores one canonical message batch before +Turn admission, with a five-minute deadline from the database clock. It requires +an Environment-bearing Session without active work. Reservation and direct input +paths share the Session lock and retry identity; pending or settled keys cannot +bypass the reservation through direct admission. A pending reservation blocks new +direct batches, including cancellation, while successful earlier retries remain +readable. Promotion commits the original inputs, history, reservation settlement +and execution claim (`queued` to `in_progress`) together; expiration and targeted +cancellation retain the terminal identity. Session deletion +is rejected while input is pending and changes nothing. A terminal reservation retry must not +affect a later reservation or Turn. Evaluate deadlines after acquiring the Session +lock, and return terminal storage outcomes without rolling their transaction back. + +Initial messages for a newly created Environment-bearing Session use that same +reservation in the creation transaction, including its connection-action event. +The creation winner alone inserts it; the stream cursor still precedes that +event, and creation retries never re-insert it. A durable initial/later flag +defaults historical rows to later input without inferring origin. Initial expiry +projects a failed Session and safe error before any Turn exists; later expiry +retains idle semantics. Failure +events capture the settled activity and Usage atomically. Late connections and +creation retries cannot reset or replay expired input, and newer work supersedes +old activity without changing its event snapshots. The Environment itself is not +failed by an input deadline. This Store rule covers actual self-hosted and internal +hosted associations; it does not enable hosted providers. None/absent Environment initial input retains +immediate Turn admission. Cancellation/deletion keep their existing semantics. + +Ordinary and streamed public self-hosted creation accept initial text through this +transaction after configuration and new-work lease checks. They return the owned +Environment ID and executor URL while offline, without waiting for admission. +The creation stream's `created` snapshot is the committed JSON 201 projection, +including the connection action; the committed action event then follows from the +creation cursor. A disconnected observer leaves committed input intact; +only the existing Worker prepares, promotes and starts it. Saved-Agent retries with recorded intent +recover before fresh execution admission or source resolution; inline retries keep +their existing resolved-snapshot validation. +Later live subscribers observe only future events and recover history through queries. + +The public self-hosted input profile accepts message-only batches. Under the same +Session lock, recover the original reservation or direct receipt before choosing +current active input or idle reservation. Active messages use existing ordered input +receipts without a new Turn, preparation or reservation; idle messages retain the +readiness and promotion path, including already-connected environments. Pending +reservations keep their gate and deadline. An unlocked activity read or retry after +a conflict must never choose a different admission path. Mixed inputs remain gaps; +these restrictions do not narrow the pinned protocol target. Cancellation-only batches +use the existing direct admission after configuration and execution-ownership checks. +Only the Session-locked transaction chooses the active Turn or an idle receipt; +matching retries retain that target even during later work. Cancellation cannot +create a Turn or bypass preparation. A new cancellation still conflicts with a +pending reservation; it does not cancel pre-Turn input. Its 204 response confirms +durable admission, not native completion or process exit. Homogeneous function-result +batches also use direct admission after those same checks: their explicit Turn/call +identity selects an existing pending call, never new work. Reuse function validation, +Session-locked whole-batch receipts, preserved output/error fields and native +application acknowledgements. Matching retries remain bound to their original calls +after completion or during later work; new results cannot bypass a pending reservation. +Definitions remain fixed through preparation and cold native continuation. These +callbacks are not installed Environment metadata. Mixed result/cancel publication +and exact hosted action-removal timing remain separate gaps. +Promotion requires the current leased execution writer and the caller's +retained native preparation; never hold a database lock during external preparation. Only +the first successful non-replay receipts authorize Start on that same preparation. +An admitted retry returns the original receipts without reclaiming execution; a +read or uncertain commit never authorizes another Start. A crash after promotion +but before Start uses existing claimed-Turn reconciliation (`execution_interrupted`), +including unbound or deleted Sessions, rather than ordinary queued dispatch. Deletion +after claim is rejected like any active Turn. +The Worker expires at most 32 due reservations on each existing tick, after +checking ownership and before checking devices or execution slots. The sweep +requires the leased Store and uses its connection with the existing transaction +timeout; it never falls back to a pooled writer. A partial deadline index and +Session row locks with SKIP LOCKED let unrelated work proceed around contention. +The candidate cutoff is statement time; settlement rechecks the database clock +after acquiring the Session lock. This bounds mutations and transaction time, not +the number of examined locked rows. Restart resumes expiry on normal ticks without +a separate scheduler or backlog-draining loop. No failed Turn may stand in for a +pre-Turn connection failure. + +Session activity before a Turn is derived from the latest relevant reservation and +authenticated connection state. Offline input requests `environment_connection`; +connection arrival clears that action to `idle`, while the prepared Worker still +owns native readiness and admission. An idle offline Environment alone requests no +connection. Reservation/connection changes commit immutable Session activity and +usage snapshots in the same transaction; SSE must not substitute a later Turn or +action set. A newer or active Turn owns subsequent activity. Settled non-initial +reservations clear their action to `idle` until newer work exists, even after an +earlier failed Turn. This local settlement policy does not establish hosted expiry +errors or initial-input asynchronous failure semantics; those remain unverified. + +Session GET/list/metadata responses and live SSE share the safe `self_hosted` +output projection. Its `remote_url` comes only from the daemon gateway +configuration, never request headers or a daemon address. Include +the owned Environment ID, workspace and capability directories without exposing +private configuration. The standalone Environment resource remains separate. +Acceptance must pass that exact URL and ID to the +caller-started executor and observe real remote execution through the existing +Worker, daemon and harness, with fixed SDK and raw HTTP/SSE checks. + +A self-hosted input HTTP request returns 204 only after durable admission. Its +wait uses bounded pooled operations, outside transactions and execution lease +ownership; it cannot prepare or start native work. Only that route extends its +response write deadline to six minutes for the original five-minute database +admission deadline plus response grace. Request/observer disconnect stops waiting, +not the durable reservation or execution; retries keep the original identity and +deadline. The Worker remains the readiness, promotion and Start owner. Local failure +mapping uses 409 `environment_input_expired` / `environment_input_cancelled`, 503 +`execution_unavailable` for ownership loss, and existing 404 for deletion. New input +after a hosted provisioning failure returns the observed 409 `conflict_error`; other +exact hosted failure statuses/bodies and pending-input crash recovery remain unverified. +Principal acceptance must use public Session creation and input against the built +standalone service, including a wait exceeding its ordinary 30-second write timeout, +real remote commands/files and a second native-history Turn. Private provisioning +or injected API handlers cannot substitute for that workflow. + ## Runtime capability preparation -Core freezes resource versions, metadata and source selections. Managed initialization -keeps initial files and tool configuration before Runtime Skill/Plugin import, -then runs packages and ordered setup before Runtime snapshots local capability -directories. All steps use the common `runtime_prepare` protocol; Providers do -not execute Core initialization commands. The common runner depends on neutral -Environment/Session identity and a Runtime peer, not Provider, deployment or OS. Self-hosted input -accepts only local `capability_directories` with `workspace_directory`, not managed -Skill/Plugin archive fields or a hosted Template. Local directory discovery does not -populate the public API-managed `skills` or `plugins` installation arrays. - -Both origins use the common Runtime parser and `installed.json` on Linux, macOS -and Windows. The operator selects the capability root; the native installer -uses `capabilities` under `OAC_RUNTIME_HOME` by default. Core and public requests -cannot select that destination. Read-only snapshot modes are integrity hints, -not protection from the launching user. The manifest binds one -Session and Environment to the source-selection digest. The admitted asynchronous -preparation owner verifies or creates it before native execution, including for an -empty selection. Reconnect and replacement executors reuse installed contents; -source edits are observed only by a new Session with its own Environment and fresh -snapshot. A private completion record retains only the operator installation root, -so loss of a completed snapshot cannot trigger source recapture. Missing, partial or -conflicting snapshots fail without automatic repair or replay. Adapters receive only the resulting Runtime-owned Skill paths and MCP -declarations. Files access retains its separate readiness and authorization. - -Resource management owns allocation and Environment creation, renewal and reclamation. -Executor close, Turn cancellation and transport loss preserve the installed snapshot, -workspace and allocation. Reclamation is an explicit operation coordinated with active -work; a disconnected socket is not proof that native effects have stopped. - -Source selections accept portable absolute Unix, Windows drive and UNC paths; -Core does not resolve these paths on its own host. The daemon applies its local -path and access checks. Initial files and setup working directories use logical -`/workspace` paths. Harness differences remain in adapters. +This section is the single owner of capability preparation: initial files, tool +configuration, Skills, Plugins, Environment MCP, npm/Python packages, setup and +`packages.system`. Hosted and self-hosted Environments use one Runtime path. + +### Ownership and lifetimes + +Resource management retains provider placement, capacity, allocation and +Environment create/renew/reclaim. The authenticated Runtime connection carries +initialization, capability preparation and executor operations; it never +implicitly allocates or destroys compute. + +Executor close, Turn cancellation and transport loss preserve the installed +snapshot, workspace and allocation. Reclamation is an explicit operation +coordinated with active work; a disconnected socket is not proof that native +effects have stopped. See +[Executor and Turn lifetimes](../../docs/runtime-protocol.md#executor-and-turn-lifetimes). + +### Preparation order + +Core freezes resource versions, metadata and source selections. Managed +initialization runs in this order, every step over the common `runtime_prepare` +exchange with the same daemon: + +1. initial files and tool configuration; +2. Skill/Plugin bundle import; +3. npm/Python packages and ordered setup commands; +4. capability directory snapshot finalization. + +Providers only place, create, bootstrap, inspect, renew and reclaim resources; +they never execute Core initialization commands. The common runner uses only +neutral Environment/Session identity and a Runtime peer, with no Provider, +deployment or OS branch. Harness differences belong to native adapters. + +A missing authenticated Runtime connection waits before capability +initialization is claimed; an operation whose effect is unknown is not replayed. +Self-hosted capability directories stay in immutable Environment configuration +and never create a managed initialization row or allocation. + +Self-hosted input accepts only `workspace_directory` and optional local +`capability_directories`, not managed Skill/Plugin archive fields or a hosted +Template. Local directory discovery does not populate the public API-managed +`skills` or `plugins` installation arrays; those describe API-managed uploads. + +### Transfer and paths + +The private `runtime_prepare`/`runtime_prepare_result` exchange carries typed +initial files, configure/npm/python/setup operations, inert Skill/Plugin +archives and finalization selections, with canonical Session and Environment +identities. + +- Files and setup working directories use logical `/workspace` addresses. + Setup commands are explicit typed input; executable selection and physical + destinations stay Runtime-owned. Core supplies no executable or host-platform field. +- Ordered 64 KiB chunks and SHA-256 receipts bound each file or archive to + 50 MiB, each control frame to 1 MiB and each connection to one transfer. + Initialization and finalization headers carry no file data. +- Begin, chunk and commit are never retried. Rejected, failed and unknown effects + stay distinct. Runtime owns transfer and filesystem work through settlement, + including disconnect or cancellation. +- Source selections accept portable absolute Unix, Windows drive and UNC paths. + Core never resolves them on its own host; the daemon applies its local path and + access checks. + +### Installed snapshot + +Both origins use the common Runtime parser and `installed.json` manifest on +Linux, macOS and Windows. The operator chooses the capability root (by default +`capabilities` under `OAC_RUNTIME_HOME`); Core and transfer requests cannot. + +- The manifest binds the Session and Environment to the ordered source-selection + digest. The admitted asynchronous preparation owner verifies or creates it + before native execution, including for an empty selection. +- Filesystem locking prevents concurrent installation. A private completion + record keeps only the operator installation root, so a deleted snapshot is + never mistaken for first preparation or recaptured. +- Missing, partial, conflicting or foreign snapshots fail without deleting data, + automatic repair or replay. +- Reconnection and replacement Executors load installed contents without + rereading sources. Source edits are seen only by a new Session with its own + Environment and fresh snapshot. +- Read-only snapshot modes are integrity hints, not protection from the + launching user. + +### Executor admission + +Synchronous Executor admission validates the frozen descriptor only. The +asynchronous preparation owner ensures capabilities are ready before invoking the +native factory. Adapters receive only the resolved Runtime-owned Skill paths and +MCP declarations; these remain Runtime-only fields. Reused Session Executors +keep their original configuration. + +Files reads keep their separate readiness and authorization and do not require +capability or native execution readiness. MCP environment variables come only +from explicit immutable Runtime tool configuration; missing variables never fall +back to daemon credentials or ambient process variables. + +### System dependencies and Runtime directories + +The daemon runs as its launching account and never uses sudo or elevates its +permissions. Only user-directory dependencies install during preparation. + +- System dependencies must be preinstalled in the managed image/template or by + the self-hosted user. +- `packages.system` is rejected explicitly, in Templates and inline + configuration, including a supplied null or empty list. It is never ignored or + translated into a privileged operation. +- Runtime runs no apt, sudo, unprivileged system-root installer or other + automatic system-package operation. No managed-only exception exists. Missing + dependencies fail the consuming operation. +- npm and Python use local prefix/target directories. Setup requires Bash; + Windows requires Git Bash, without a substitute shell. +- Package responses keep the official required `system: []` field as empty + response metadata; it is not stored as an initialization option. + +Initialization and package directories default under `OAC_RUNTIME_HOME` and may +be selected with `OAC_RUNTIME_INITIALIZATION_DIRECTORY` and +`OAC_RUNTIME_PACKAGE_DIRECTORY`. Packaged Linux images select their existing +`/environment` layout through those settings. `OAC_RUNTIME_TOOL_ENV_FILE` +selects explicit tool configuration. These are resource paths, never +Environment-source or OS switches in Core. + +Runtime process ownership waits for exit and I/O settlement. Confirmed failures +keep only a bounded exit status, never command output. + +### Platforms and isolation The protocol and Go preparation implementation are shared by all three native -platforms. Resource directories are operator settings, not deployment branches. -Managed Providers remain Linux-only. The daemon does not sandbox tools, files or -network access; tools use the starting account's permissions. Native installation -and current validation limits are in the [native guide](../../docs/self-hosted-native.md). -Historical deployment acceptance does not qualify later platform or capability changes. See the -[canonical preparation rules](../../CONTRIBUTING.md#shared-runtime-capability-preparation). +platforms. Managed Providers remain Linux-only. Commands run directly with the +starting account's permissions and host network; the daemon does not sandbox +tools, files or network access. Outer Environments own managed isolation, and +unsupported network restrictions reject instead of silently running unrestricted. + +Native installation and validation limits are in the +[native guide](../../docs/self-hosted-native.md). No project-version upgrade or +historical manifest compatibility is introduced. These rules do not establish +new real-deployment acceptance; historical evidence stays limited to its +recorded binaries and inputs. + +### Allocation initialization lifecycle + +The allocation lifecycle owns pending/running/complete initialization. Authentication +may connect the daemon during initialization; execution bindings, native preparation, +live Files and connected publication wait for completion. Keep Provider bootstrap +settlement distinct. Advance at most one bounded initialization operation per full +maintenance scan. At allocation EOF, begin the next page in the same call rather +than consume an observation interval on an empty page. Refill at most once, retain +the 32-allocation per-call bound and the five-second ticker, and never loop on an +empty store. For managed nodes these bounds apply independently to each node. +Use process-local progress and the same node lifecycle gate as direct provisioning. + +After a next-Turn input is durably pending, a completed managed allocation in a +suspension/recovery phase may hint this loop. Initial inputs, cold creation, +running/disabled compute, terminal receipts, cancellation/tool-result events, +history and file operations do not use this hint. Eligibility lookup and delivery +are best effort; persisted work and the normal ticker remain authoritative. +Coalesce hints without blocking, and allow at most one extra scan per normal +five-second cycle. Keep the ticker independent of requests. A normal tick consumes +already queued hints before scanning; simultaneous tick/hint readiness is one +normal scan. Preserve hints arriving during a scan, the allocation cursor and all +ownership checks. Never close the hint channel while handlers may still send. +This bounds extra maintenance work but does not bypass capacity, a busy lifecycle +gate or multi-page scheduling, and does not guarantee a resume deadline. + +A recovered or uncertain running installation fails and uses existing cleanup, without replaying writes. +A hosted provisioning failure is terminal for its Session. The allocation's cleanup +transaction stores a safe reason with the failed Environment and records +`environment.failed`, `error` and one `agent.session.failed`; Session reads derive +`failed`, that reason and the failure time from the same record, and live streams end +after the failed event. The initializer reports only the integer exit status of a +failed initialization command. Core composes the reason from a fixed step label and that status, +never from command, package-manager or file output; unknown effects, timeouts and +receipts without a status keep a generic reason. New input then gets the observed 409 +`conflict_error`; expiry and pending-input settlement keep their behavior. +Completed environments never reinstall initial files on reconnect or native recovery. +The typed Runtime preparation protocol carries bounded confidential input; the +daemon owns the common Go initialization implementation. Confidential env +and setup snapshots are encrypted independently of ordinary metadata. Adapters +apply explicit tool variables without automatically inheriting daemon credentials; +this does not prevent same-user tools from reading local Runtime state. +Reuse runtimefs atomic replacement and anchored logical-workspace paths for initial +files on every platform. Public Files creation keeps its own non-replacement rule. + +### Skills, Plugins and Environment MCP + +Skills and their immutable versions are Core-owned tenant resources, independent of +Sessions and native Skill installations. Serialize version allocation and pointer +mutations under the owning Skill row. Keep top-level name and description aligned +with the default version in the same transaction, including default-changing uploads; +nondefault uploads preserve that metadata. Preserve unique version identities across +concurrent uploads and deletion. Metadata reads never load or decrypt bundle bytes. +Encrypt bundle contents with a tenant, Skill and version binding using the existing +service cipher. Deleting a Skill reclaims its versions without affecting already +frozen Session initialization. Public reference metadata, unresolved template intent +and the resolved Runtime bundle are distinct; do not report a reference as inline +merely because it reuses the same installer. No compatibility reader, source +cache, extra lifecycle owner or per-harness resource implementation is required. +Resolve references inside the Session creation transaction, after the creation +upsert establishes ownership. Lock referenced resources in a stable order; freeze +the selected version, descriptive metadata and bytes together. Creation retries +recover the recorded intent before reading mutable templates or Skill sources. +Templates preserve default, latest and explicit version selectors. An omitted or +null reference version selects the default at Session creation and projects as +`version: null` in Template responses. Session responses contain concrete versions; +only validated installation metadata crosses the Runtime boundary. A supplied +Session Skill, Plugin or capability-directory list replaces its template list; +omission and null inherit, while an empty list clears that selection. This differs +from Template resource updates, where null clears lists and resets network to the +pinned enabled default. Preserve caller intent and frozen Session snapshots in +both cases. Public capability directories remain caller paths; adapter-owned +installation directories are not portable public paths. + +Inline and referenced Skill ZIPs use the same confidential initialization snapshot and installer. +Core validates portable manifests and bounded regular-file archives, returns only +safe Skill metadata, and freezes content before native preparation. The Runtime +owns `skills/` below its configured capability root; the manifest validates +the installed snapshot before reuse. This is state +consistency, not protection from the launching user. Public Plugin ZIPs preserve their complete package +layout and reuse the shared archive and portable Skill parsers. Core keeps safe +Plugin metadata separate from encrypted archives. Templates inherit or replace +Plugin and capability-directory lists through the same hosted resolver. + +After ordered setup, the existing initializer snapshots declared workspace-contained +capability directories into Runtime storage and writes one installed manifest. +This is an initialization artifact, not a second lifecycle owner or database ledger. +Directory bytes are observed after setup; they are not frozen at Session creation. +The common daemon resolves the manifest only for executable preparation and passes +validated Runtime-owned Skill/package roots to adapters. Files reads do not require +that artifact. Reconnection and recovery read installed bytes, never mutable source +directories. Missing or inconsistent installations fail preparation without replay. + +Adapters register only selected Skill roots without changing the execution loop. +Codex uses explicit extra roots; MiniMax projects its native catalog; Claude creates +one controlled envelope per package with real directories and immutable hardlinks +under content. Its explicit paths remain inside that envelope; original native +control files are not activated. Keep automatic native MCP discovery disabled. +Explicit Environment MCP declarations +follow the separately qualified transport path below; unsupported native activation +fails explicitly, without silent partial activation or a generic plugin framework. + +Environment-origin MCP declarations use the shared Plugin parser and frozen +installed packages. The installation manifest retains selected MCP package roots; +Runtime re-parses those installed packages without another configuration copy, +credential cache or lifecycle ledger. Native adapters must explicitly qualify and +project supported declarations before public admission. Parent-directory Skill +discovery does not activate nested Plugin MCP configuration. + +The common daemon stdio entry resolves the installed declaration and launches its +server under the same user permissions as the Harness. Unix replaces the helper +process; Windows forwards native stdio within the owned process tree. Explicit +initialized values override the selected declaration's variables. There is no +Python sandbox launcher, mount policy, shell prefix or separate credential sandbox. +Process groups and Windows Jobs own cancellation and descendant cleanup only. +Claude composes MCP identity and observation with the same workspace profile; +MiniMax keeps its existing supported transport qualification. A public capability +advertisement must still match the installed engine's actual supported transport. + +Environment-origin literal HTTP headers remain rejected for the pinned Claude +client because its interpolation and cross-origin forwarding change their meaning. +MiniMax accepts environment stdio only. Its adapter reads the existing Session-private +native runtime-name registry for exact first-frame identities and cross-checks +completed native results. Reuse existing observation and cancellation settlement; +never fabricate a delayed start event, guess normalized identities or add a registry +of our own. Its unqualified HTTP transport remains rejected. +These mechanisms alone do not establish public MCP support or full compatibility. ## Contract inventory @@ -252,7 +658,7 @@ from Session and Turn status. ## Ownership and placement decision -The canonical [architecture rules](../../CONTRIBUTING.md#environment-ownership-and-placement) +The canonical [architecture rules](#ownership-and-placement-decision) keep Environment lifecycle common while leaving process placement and native transport to adapters. Logical ownership does not require a machine per object. @@ -275,3 +681,49 @@ unrestricted process account. A directory binding alone is not isolation. Retain native history independently of disposable compute, or prove native restoration; never treat an Environment ID as a filesystem or history backup. Do not silently move an existing Session away from its bound device. + +Environment identity, tenant/Session association, configuration and lifecycle belong +in Agents API, independently of provider compute, authenticated device identity, +daemon sockets and native harness Sessions. Create an Environment association in +the same transaction as its Session and creation identity when this resource is +implemented. Keep mutable connection/registration state out of immutable +configuration; replacement ownership must fence stale observations. + +The current hosted architecture is V1: Core runs independently; each Environment +sandbox contains its daemon, selected native harness, local tools and workspace. +Execution and Files use the same authorized workspace through the existing +Core/Runtime contract. Native tool calls stay local. +CLI discovery uses a bounded 15-second version probe per installed harness; +missing binaries fail immediately. A version result is availability, not Environment +readiness, and does not change initialization or connection ownership. Process placement and native +transport remain adapter responsibilities, without a second model/tool loop. +The former separated Runtime/harness and workspace executor topology is a distant +future V2 option, to revisit only after V1 is stable and concrete needs justify it. +Do not extend that topology for hosted delivery, maintain two current hosted routes, +or introduce dormant V2 compatibility scaffolding. + +When an execution Session has a previously started Turn but no recorded native +Session ID, Core requires existing-history recovery through a verified Runtime +capability. Read that condition before claiming the next Turn. A supplied native ID +remains authoritative. The Codex adapter may recover only a unique, nonarchived +root in the exact Session-private native home and expected working directory, using +native listing and exact-ID resume. Missing, incomplete or ambiguous history must +fail without starting a fresh root. A recorded start can precede native work; that +uncertain case also fails conservatively. Recovery does not replay interrupted +inputs, erase prior outcomes or promise transparent continuation of running tools. +Keep Device identity and Environment scope in `ExecutionDevice`; native Session +identity and prior API Turn state belong to `SessionExecutionBinding`. + +Platform-managed and user-managed deployment reuse this same Runtime. For platform +management, SandboxProvider creates and reclaims it. For user management, the user +starts the Runtime and its daemon authenticates and initiates the Core connection; +Core verifies principal ownership and the exact Environment binding. These are +management responsibilities, not separate execution architectures. + +In V1, our daemon fills the user-side executor role. Users deploy daemon, the +selected harness, local tools and workspace together. Do not require Codex +`exec-server`, a service-side harness, registry/Noise transport or remote tool +forwarding. The explicit daemon-executor decision supersedes the previous native +executor interoperability requirement. The superseded execution route is removed; +retain reusable filesystem helpers, +necessary regressions and historical evidence without a compatibility layer. diff --git a/contracts/agents-api/harness-onboarding.md b/contracts/agents-api/harness-onboarding.md index ec3f0958b..df9d3ac3f 100644 --- a/contracts/agents-api/harness-onboarding.md +++ b/contracts/agents-api/harness-onboarding.md @@ -1,16 +1,16 @@ -# Add a native harness to OpenAgentCore +# Add a native Harness to OpenAgentCore -Start with [`agent/harness.go`](../../apps/parsar-daemon/internal/agent/harness.go). -It defines the required lifecycle, separate optional interfaces and existing -registration methods in one place. Implement one adapter for a native SDK or machine-readable protocol. Core and -Runtime use the common contract; adding a harness should not add engine-name -branches to public handlers, storage, scheduling or environment providers. +A **Harness** is a native agent engine (Codex, Claude Code, MiniMax Code) that +runs the model/tool loop. A **Harness adapter** translates the common +Core–Runtime execution contract into that engine's SDK or protocol. This guide is +the single numbered path for adding one. Qualification evidence and the +per-operation support table live in [Harness integration](harnesses.md). -The [harness contract](harnesses.md) defines observable behavior. Start with the -[developer guide](../../docs/development.md) for a working checkout; the -[contributor guide](../../CONTRIBUTING.md#harness-qualification-and-onboarding) -owns architecture and required checks. The pinned public Agents API in -[upstream.json](upstream.json) is separate from the internal adapter protocol. +The code entry point is +[`agent/harness.go`](../../apps/parsar-daemon/internal/agent/harness.go). It +declares the required lifecycle, the separate optional interfaces and the +registration methods. The pinned public Agents API in [upstream.json](upstream.json) +is separate from this internal adapter contract. ## Ownership @@ -42,16 +42,59 @@ qualification. The native daemon supports Linux, macOS and Windows; each adapter declares its qualified platform scope. Managed Providers remain Linux-only. A platform-neutral interface alone does not qualify a harness on another platform. See [native Runtime validation](../../docs/self-hosted-native.md) for the current -acceptance limits. Resource management owns machine selection, allocation and -Environment creation and reclamation. Executor close does not release that allocation or delete the -workspace. Environment reclamation is an explicit resource-management action that -coordinates with execution. Runtime connection, installed capability snapshot, -Session Executor and Turn each have their own lifetime. Native factories receive +acceptance limits. Runtime connection, installed capability snapshot, Session Executor and Turn +each have their own lifetime; see +[Executor and Turn lifetimes](../../docs/runtime-protocol.md#executor-and-turn-lifetimes). +Native factories receive capabilities only after the common Runtime has loaded its bound installed snapshot; see [capability preparation](environments.md#runtime-capability-preparation). Model providers supply model communication configuration, not Turn scheduling or native process ownership. +## Steps + +1. **Pin the native source.** Record the upstream package version and source + revision and document the native entry point next to the adapter. +2. **Implement the adapter** in `apps/parsar-daemon/internal/agent/`: an + `ExecutorFactory`, an `Executor` and a `Turn`. See + [Required adapter interfaces](#required-adapter-interfaces). Reuse shared + process, credential/configuration and local workspace helpers. +3. **Register the kind in the Runtime** in `apps/parsar-daemon/internal/cli`. + See [Register the adapter](#register-the-adapter). +4. **Add the Core engine identifier and service profile.** See + [Add the engine to Core](#add-the-engine-to-core). +5. **Package native prerequisites.** Add a Runtime image under + `services/agents-api/deploy/` and, optionally, + [native installer participation](#native-installer-participation). +6. **Enable and select the engine** through + [engine selection](#engine-selection). +7. **Qualify it.** Run the synthetic integration test, then the real acceptance in + [Harness integration](harnesses.md#acceptance-checklist). Record results in + the [qualification table](harnesses.md#current-qualified-operations). + +Start with the mandatory text lifecycle, then qualify optional operations one at +a time. Do not copy an adapter's native limitations into the shared Core protocol. + +## Architecture rules + +- Codex, Claude Code and future Harnesses have equal architectural status. The + common Runtime wire protocol and Executor/Turn interfaces own lifecycle, input + receipts, cancellation, recovery and resource access. Each adapter keeps its + native implementation and model/tool loop. +- A new engine supplies an adapter, a qualified profile, registration and an + independently verified deployment. It adds no engine-name branches to API + handlers, persistence, dispatch, scheduling or Environment providers. +- Keep required lifecycle declarations, optional interfaces and registration + methods in `agent/harness.go`. Result types, errors and Registry storage may + stay in focused files. Keep this guide linked to that entry point. +- Use the existing `proto.SupportedAgentKind` and `AgentKindCapabilities` + schema. Do not add a second capability descriptor or a combined optional + interface. +- Onboarding does not require feature equality. Verify common lifecycle + obligations and use the same public assertions for each declared operation. + Optional native differences are separate capability work, not onboarding blockers. +- Never equate accepted parameters with applied native behavior. + ## Native model configuration [`agent/harness.go`](../../apps/parsar-daemon/internal/agent/harness.go) owns the @@ -79,6 +122,14 @@ in `agent/harness.go`; do not add a second model capability registry or infer capabilities from model names. Nonempty `harness_config` currently requires a native protocol route because the translator has no loss report for those parameters. +Every Executor factory must honor the configuration preparation contract in +`agent/harness.go`. Shared wire types live in `internal/agentdaemon/proto`. The +standalone Core build includes the shared `internal/modeltransport` route +qualification code alongside the adapter declarations; Runtime still owns proxy +startup and request forwarding. Public configuration sources and inheritance belong +to [model execution](model-execution.md#native-model-parameters). + + ## Required adapter interfaces [`agent/harness.go`](../../apps/parsar-daemon/internal/agent/harness.go) is the @@ -100,7 +151,8 @@ was retained. Runtime closes the output channel in that case. Once input may hav been submitted, return a non-nil Turn even with an error: that Turn owns exactly one output-channel close, and uncertain input must not be replayed. -`Cancel` addresses that exact Turn. `AwaitSettlement` confirms that native events, +`Cancel` addresses that exact Turn (see +[Cancellation ownership](#cancellation-ownership)). `AwaitSettlement` confirms that native events, inputs, function results, interactions, child work and output writes have settled. Only then may `Reusable` be true. False requires a reason and confirmed Executor close before replacement. An error means settlement is unconfirmed. A caller @@ -115,6 +167,20 @@ thread, a Claude adapter retains one streaming Query, and a MiniMax adapter reta its ACP connection and native Session. Core sees the same Executor and Turn semantics in each case. +### Cancellation ownership + +Cancellation is declared on the base `agent.Session` interface, which every +`Turn` embeds. The adapter's Turn implements `Cancel` for that exact Turn and +never retargets it to a successor; `Executor` has no `Cancel`. Runtime dispatch +decides when to call it (for example on `prompt_cancel` or shutdown). + +`Cancel` alone does not transfer resource ownership. `AwaitSettlement` confirms +the Turn settled, and `Executor.Close` retires native resources. Permission and +user-choice responses are the optional `PermissionResponder` and +`UserChoiceResponder` interfaces; implement them only when the adapter emits +those interactions. Unsupported responses receive a negative receipt. The router +keeps its interaction routing and retry ownership. + ## Events, inputs and optional capabilities Use [`internal/agentdaemon/proto`](../../internal/agentdaemon/proto) for neutral @@ -144,37 +210,68 @@ without implementing its semantics is an error. Workspace reads may use separate read-only preparations; those do not start model work or provide another execution lifecycle. -## Register a supported operation set - -The registration methods are also defined in `agent/harness.go`. The existing -`proto.SupportedAgentKind` descriptor supplies kind, availability, version and -`AgentKindCapabilities`; its schema remains in `internal/agentdaemon/proto`. -`RegisterKind` resets the Executor and preparation registrations, so call it -first. `RegisterExecutor` and `RegisterPreparation` derive preparation flags; -other capability declarations must match verified behavior. Core qualification -still belongs to the service profile and is not granted by Runtime registration. - -1. Pin the upstream source/package version and document the native entry point. -2. Implement the adapter using its SDK or native protocol. Reuse shared process, - credential/configuration and local workspace helpers where applicable. -3. Register its `SupportedAgentKind` and direct-call factory with `RegisterKind`, - then register the shared `ExecutorFactory` with `RegisterExecutor`. A direct-call - factory should delegate to the same Executor implementation. Register separate - read-only preparations only when required by qualified workspace operations. Runtime - advertisements must describe behavior verified for that installation. -4. Add a profile to the existing static catalog, or supply an immutable catalog - through service composition. Custom composition supplies the same - `execution.Policy` to `api.WithExecutionPolicy` and `Dispatcher.Policy`. -5. Package the native prerequisites and select the engine through operator - configuration (`OAC_DEFAULT_HARNESS`). Do not invent a public harness field. - -The static registration surface requires a build. Dynamic plugins are outside -this contract. A small adapter does not remove the need for native qualification. +## Register the adapter + +Registration is static and requires a build; dynamic plugins are outside this +contract. The methods live in `agent/harness.go`, and built-in adapters call them +from [`cli/agent_registration.go`](../../apps/parsar-daemon/internal/cli/agent_registration.go) +(Codex, MiniMax Code) and [`cli/claude_sdk.go`](../../apps/parsar-daemon/internal/cli/claude_sdk.go) +(Claude Code). + +| Order | Method | Registers | +| --- | --- | --- | +| 1 | `RegisterKind(proto.SupportedAgentKind, agent.Factory)` | Kind, availability, version, `AgentKindCapabilities` and the direct-call factory. Resets the other registrations, so call it first. | +| 2 | `RegisterExecutor(kind, agent.ExecutorFactory)` | The shared Executor/Turn lifecycle used for execution. Derives the `Preparation` capability. | +| 3 | `RegisterPreparation(kind, workspaceRead, agent.PreparationFactory)` | Optional. Separate read-only workspace preparation when qualified workspace operations need it. | + +The direct-call `agent.Factory` should delegate to the same Executor +implementation. Every other capability declaration must match behavior verified +for that installation. Runtime registration does not grant Core qualification; +that belongs to the service profile. + The runnable test-only example is [`testdata/onboarding/main.go`](../../apps/parsar-daemon/testdata/onboarding/main.go). -It registers a text-only synthetic harness and is never shipped as a real engine. +It registers a text-only synthetic Harness, demonstrates a Session-owned +Executor, fresh Turns, durable steering, cancellation and history binding, and is +never shipped as a real engine. + +## Add the engine to Core + +Core recognizes engine identifiers explicitly. Add the new identifier to: + +| File | Purpose | +| --- | --- | +| [`internal/engine/profile.go`](../../services/agents-api/internal/engine/profile.go) | The static service profile catalog | +| [`contracts/agents-api/v1/core_extension.go`](v1/core_extension.go) | Accepted `x_agents_core.harness` values | +| [`internal/api/saved_core_input.go`](../../services/agents-api/internal/api/saved_core_input.go) | Saved Agent extension validation | +| [`internal/harnessconfig/builtin/registry.go`](../../internal/harnessconfig/builtin/registry.go) | Built-in Harness configuration | + +The profile is pure: it declares supported placements, public configuration and +result limits and required Runtime controls, using existing public/protocol +types. Profile callbacks cannot query business data, decrypt credentials or +control native processes. Public schema validation, qualified engine support and +actual Runtime capabilities remain separate checks; Runtime advertisements alone +never enable public operations. Shared dispatch checks capability combinations, +not a whitelist of engine names. + +`execution.Policy` supplies immutable service qualification to HTTP admission, +Worker device selection and final dispatch. Custom composition gives the same +Policy to `api.WithExecutionPolicy` and `Dispatcher.Policy`. The zero value uses +built-in profiles; an explicitly empty catalog authorizes none. There is no +mutable global registration or compatibility fallback. + +## Engine selection + +| Setting | Scope | Effect | +| --- | --- | --- | +| `OAC_DEFAULT_HARNESS` | Core deployment | Default engine for new Sessions; `codex` when unset | +| `OAC_HARNESSES` | Core deployment | Comma-separated extra engines to enable, without requiring a managed Provider | +| `agent.x_agents_core.harness` | Saved Agent or inline Session Agent | Explicitly selects an enabled engine; never falls back to another | -## Required behavior versus optional operations +Existing Sessions keep their engine. Do not add another selector; the +[harness selection extension](harness-selection.md) owns the field's semantics. + +## Required versus optional operations The current public text path requires durable turns, applied input receipts, ordered observations, cancellation and enforcement of disabled execution controls. @@ -205,57 +302,6 @@ same dedicated Runtime binding and shared Files helpers. A native Bash sandbox alone does not establish isolation for other native file tools. Enable a placement only after its required security and lifecycle behavior is demonstrated. -## Acceptance and delivery - -Before implementation, record the operation set, expected results, exclusions and -stopping conditions in the board. A batch ends when its declared operations pass; -it does not expand to match another harness's feature list. - -- Adapter tests: two ordinary Turns share one native process/connection and history; - cancellation followed by another Turn; stale cancellation and late events; native - exit, cleanup failure, input write/application receipts and unknown outcomes. - Verify fresh per-Turn usage, function, input and child-observation state. -- Shared integration: public admission, actual Worker/device selection, gateway, - daemon registration/dispatch and durable terminal projection. See - `TestThirdHarnessPublicOnboarding` for a synthetic example, not native evidence. -- Real acceptance: pinned official Python SDK and raw HTTP against our Agents API, - real provider API, native harness and independent execution database. Verify - initial execution, warm follow-up, cancellation and restart/continuation. - Record native owner identity and same-condition cold/warm timing; mock results - cannot establish native reuse or performance gains. - For hosted qualification also verify Files/Artifacts, workspace identity, - credential protection and foreign-history rejection. -- Regression: existing qualified engines keep working. Run targeted tests during - development, then `make check` and applicable real regressions. API changes - require `make openapi`; query changes require `make sqlc-generate`. -- Review: follow the repository - [blind review workflow](../../CONTRIBUTING.md#workflow-and-quality). - -Record exact revisions, image/package versions, commands, results and limits. -Keep keys in private operator files; never commit them or include them in logs or -Feishu. Failed or synthetic runs cannot be counted as real acceptance. Merge the -bounded PR after required checks/review, update its board child and reassess the -full board. Acceptance of one engine is not complete public protocol compatibility. - -## Minimal runnable example and native references - -[`testdata/onboarding/main.go`](../../apps/parsar-daemon/testdata/onboarding/main.go) -is a runnable synthetic adapter. It demonstrates registration, a Session-owned -Executor, fresh Turns, durable steering, cancellation and history binding through -the actual gateway and Router. Run `TestThirdHarnessPublicOnboarding` against a -dedicated test database to exercise the complete integration path. The fixture is -not shipped and is not evidence for a real SDK or sandbox. - -Use the native adapters as implementation references after choosing a native API: - -- [Codex](../../apps/parsar-daemon/internal/agent/codex/executor.go): app-server transport. -- [Claude](../../apps/parsar-daemon/internal/agent/claudesdk/executor.go): Go ownership and a [TypeScript SDK bridge](../../packages/claude-sdk-adapter/README.md). -- [MiniMax](../../apps/parsar-daemon/internal/agent/mcode): ACP and native workspace companion. - -Start with the mandatory text lifecycle, then qualify optional operations using -[harness coverage](harnesses.md) and each operation's contract. Do not copy an -adapter's native limitations into the shared Core protocol. - ## Optional Subagent observations A harness that supports the Subagent resource reads implements the existing @@ -277,3 +323,42 @@ Executor/Turn semantics. Runtime owns checksums, copying, locks and additive installation; adapters own native layout and probes. Validate installation and actual execution on each advertised platform. Missing or incompatible native content must fail, never install itself during a Turn. + +## Native process ownership + +The shared daemon `clirunner` offers opt-in Unix process-group ownership for +adapters whose SDK launches a native child. Existing callers keep their current +process policy. Explicit cancellation and parent-context cancellation share a +TERM grace period and bounded KILL escalation. An internal reaper also cleans +remaining group members when the direct process exits, even if a descendant +still holds stdout open. During cancellation, surviving descendants keep the +remaining TERM grace after the leader exits. Unsupported hosts reject this mode before launch. + +Owned output pipes remain readable after the leader exits. Consumers must drain +stdout and stderr before calling `Wait`, which joins the cached process result +and closes the readers. `Done` reports leader reaping and group cleanup signals; +it is not a native execution receipt or proof of persisted history. SDK adapters +must settle each Turn and drain its observations before publishing completion. +Executor close additionally closes the query and awaits the native child. Process groups are lifecycle supervision, not OS isolation +or containment of descendants that deliberately leave the group. + +All Harness adapters use bypass execution. Do not restore named Codex permission +profiles, bubblewrap wrappers, native Claude sandbox settings or MiniMax +SandboxManager branches. There is one execution path for every Environment origin. +Resource paths are ordinary operator configuration, not a permission boundary. + +The daemon does not enforce disabled or restricted network policies. Such a +combination must be rejected unless its outer Environment implementation provides +and qualifies the requested behavior. Do not advertise daemon-level network +isolation or silently run a restricted request with unrestricted semantics. +The normal self-hosted combination uses the host's existing network access. + +## Native references + +Use these adapters as implementation references after choosing a native API: + +| Harness | Adapter | Native transport | Runtime guide | +| --- | --- | --- | --- | +| Codex | [`agent/codex`](../../apps/parsar-daemon/internal/agent/codex/executor.go) | app-server | [Codex Runtime](../../services/agents-api/deploy/codex/README.md) | +| Claude Code | [`agent/claudesdk`](../../apps/parsar-daemon/internal/agent/claudesdk/executor.go) | [TypeScript SDK bridge](../../packages/claude-sdk-adapter/README.md) | [Claude Runtime](../../services/agents-api/deploy/claude/README.md) | +| MiniMax Code | [`agent/mcode`](../../apps/parsar-daemon/internal/agent/mcode) | ACP and native workspace companion | [MiniMax Code Runtime](../../services/agents-api/deploy/mcode/README.md) | diff --git a/contracts/agents-api/harness-selection.md b/contracts/agents-api/harness-selection.md index a5addbab6..abef34fc2 100644 --- a/contracts/agents-api/harness-selection.md +++ b/contracts/agents-api/harness-selection.md @@ -85,3 +85,19 @@ before creating a Session; provider model availability is checked during native startup/execution. There is no invented cross-provider model-name catalog. Current profile limits remain in the [engine coverage table](README.md#public-engine-profiles). + +## Implementation rules + +Core accepts the optional `agent.x_agents_core.harness` extension through the +saved Agent and inline Session configuration paths. Define the extension once in +`contracts/agents-api/v1`; never use metadata or a competing top-level selector. +Resolve saved overrides before selecting the existing Session engine, and apply +that engine's execution policy before persistence. Omitted selection preserves +the deployment default; explicit unavailable selection fails without fallback. +Effective extension reads use the persisted engine; Sessions without the extension +retain the official Agent response shape. Null and retry behavior are described above. + +Hosted provider selection belongs to deployment configuration and is independent +of the engine. Session creation fixes a node through automatic placement; retained allocations keep that node and provider identity. Runtime images must satisfy their existing qualification rules. +Transient model options are partitioned by engine and must not expose another +engine's credentials. Do not infer an engine from a model name or template. diff --git a/contracts/agents-api/harnesses.md b/contracts/agents-api/harnesses.md index 1b77f2d65..045518efa 100644 --- a/contracts/agents-api/harnesses.md +++ b/contracts/agents-api/harnesses.md @@ -9,43 +9,25 @@ feature sets. Optional native limitations are separate capability work and do no block completion of otherwise qualified onboarding. For implementation steps and interface obligations, see -[Add a native harness](harness-onboarding.md). +[Add a native Harness](harness-onboarding.md). ## Integration surface -1. Implement `agent.ExecutorFactory`, `Executor` and `Turn`. Prepare fixed native - resources once, start fresh Turns on that owner, cancel an exact Turn, confirm - settlement and close the Executor. Implement optional interfaces only for - declared operations. Reuse `internal/agentdaemon/proto` requests, neutral events, - input receipts and errors. -2. Register the Executor factory and verified Runtime capabilities in the daemon - registry. Keep native translation in the adapter. Managed and user-managed - environments use the same execution lifecycle after connection; dedicated local - environments retain shared Files/write/export helpers and binding checks. -3. Add a pure qualified profile to `services/agents-api/internal/engine` and its - static catalog (or supply an immutable catalog at service composition). Declare - supported placements, public configuration/result limits - and required Runtime controls. A profile uses existing public/protocol types; - it has no database, credential-decryption or native-process responsibilities. -4. Supply the native deployment prerequisites. Verify common lifecycle behavior - and run public acceptance for each declared operation. Do not require MCP, - functions, images, verbosity control or another engine's optional features simply - to register a harness. - No new handler, store table, scheduler, event projector or model loop is needed - for capabilities already represented by the contract. - -The catalog is the explicit service qualification boundary; a Runtime heartbeat -cannot authorize new public functionality. Unknown profiles fail closed. Schema -validity, qualified service support and the available Runtime remain independent -checks. Additional capability combinations require evidence, not an engine-name -exception. The static registry requires a build to add an implementation; dynamic -plugin loading and untrusted code execution are outside this design. - -New Session selection uses the default `OAC_DEFAULT_HARNESS` or the documented -[harness extension](harness-selection.md). `OAC_HARNESSES` explicitly adds -deployment-supported profiles without requiring a managed Provider; existing -Sessions retain their engine. The default is a deployment convenience, -not a different contract or authority level. The documented extension remains separate from the pinned public protocol. +Follow the numbered steps in [Add a native Harness](harness-onboarding.md#steps) +to implement, register and select an adapter. This page owns what counts as +qualified. + +The service profile catalog is the explicit qualification boundary; a Runtime +heartbeat cannot authorize new public functionality. Unknown profiles fail +closed. Schema validity, qualified service support and the available Runtime +remain independent checks. Additional capability combinations require evidence, +not an engine-name exception. No new handler, store table, scheduler, event +projector or model loop is needed for capabilities the contract already represents. + +Do not require MCP, functions, images, verbosity control or another engine's +optional features simply to register a Harness. The default engine is a +deployment convenience, not a different contract or authority level; see +[engine selection](harness-onboarding.md#engine-selection). ## Shared behavioral obligations @@ -68,36 +50,71 @@ not a different contract or authority level. The documented extension remains se ## Current qualified operations The baseline is actual supported behavior on main, not everything Codex accepts -syntactically or everything either upstream harness can theoretically perform. - -| Operation | Codex | Claude Code | -| --- | --- | --- | -| Docker hosted text execution, native local tools | Qualified | Qualified | -| Files, immutable Artifacts, cancellation, restart/history recovery | Qualified | Qualified | -| Public functions in `none` | Qualified | Qualified; object-root schemas; text or successful inline PNG/JPEG results | -| Public functions alongside hosted workspace tools | Qualified | Qualified; object-root schemas and text or successful inline PNG/JPEG results | -| HTTP MCP and static-bearer Vault credentials in `none` | Qualified | Qualified subset | -| Required MCP initialization | Qualified | Qualified on `none`; native readiness before initial input | -| Hosted HTTP MCP | Gap | Gap | -| Function image results | Supported subset; early acknowledgement is transport-only | Successful inline PNG/JPEG on `none` and Docker `openai_hosted`; native resizing allowed, error images rejected | -| Non-default verbosity | Native/model-dependent support | No equivalent qualified; medium only | -| Public detailed Usage | Supported native counters | Native raw usage retained; public breakdown gap | -| V1 `self_hosted` daemon enrollment at `/workspace` | [Qualified deployment scope](user-managed-runtime-v1.md) | [Qualified deployment scope](user-managed-runtime-v1.md) | -| Deferred function discovery | Unqualified; explicit rejection | [Single-agent text/function profile](tool-search.md) | -| Structured output | Unqualified; explicit rejection | [Qualified single-agent function profile](structured-output.md) | -| Message images | Inline PNG/JPEG on `none` and Docker `openai_hosted` | Inline PNG/JPEG on `none` and Docker `openai_hosted` | -| Explicit reasoning | Shared service gap | Shared service gap | -| Six Subagent reads | [Qualified scope](subagents.md) | [Qualified scope](subagents.md) | +syntactically or everything an upstream Harness can theoretically perform. The +MiniMax Code column summarizes the `mcode` row of the +[public engine profiles](README.md#public-engine-profiles) and the linked operation +contracts. + +| Operation | Codex | Claude Code | MiniMax Code | +| --- | --- | --- | --- | +| Docker hosted text execution, native local tools | Qualified | Qualified | Qualified (Docker `openai_hosted`) | +| Files, immutable Artifacts, cancellation, restart/history recovery | Qualified | Qualified | Qualified on the dedicated Docker profile; see [MiniMax Code Runtime](../../services/agents-api/deploy/mcode/README.md) | +| Public functions in `none` | Qualified | Qualified; object-root schemas; text or successful inline PNG/JPEG results | Unsupported | +| Public functions alongside hosted workspace tools | Qualified | Qualified; object-root schemas and text or successful inline PNG/JPEG results | Unsupported | +| HTTP MCP and static-bearer Vault credentials in `none` | Qualified | Qualified subset | Unsupported | +| Required MCP initialization | Qualified | Qualified on `none`; native readiness before initial input | Unsupported | +| Hosted HTTP MCP | Gap | Gap | Gap | +| Function image results | Supported subset; early acknowledgement is transport-only | Successful inline PNG/JPEG on `none` and Docker `openai_hosted`; native resizing allowed, error images rejected | Unsupported | +| Non-default verbosity | Native/model-dependent support | No equivalent qualified; medium only | Medium only | +| Public detailed Usage | Supported native counters | Native raw usage retained; public breakdown gap | Public breakdown unsupported | +| V1 `self_hosted` daemon enrollment at `/workspace` | [Qualified deployment scope](user-managed-runtime-v1.md) | [Qualified deployment scope](user-managed-runtime-v1.md) | [Qualified deployment scope](user-managed-runtime-v1.md) | +| Deferred function discovery | Unqualified; explicit rejection | [Single-agent text/function profile](tool-search.md) | Gap; see [tool search](tool-search.md) | +| Structured output | Unqualified; explicit rejection | [Qualified single-agent function profile](structured-output.md) | Gap; see [structured output](structured-output.md) | +| Message images | Inline PNG/JPEG on `none` and Docker `openai_hosted` | Inline PNG/JPEG on `none` and Docker `openai_hosted` | Unsupported; see [message input](message-input.md) | +| Explicit reasoning | Shared service gap | Shared service gap | Shared service gap | +| Six Subagent reads | [Qualified scope](subagents.md) | [Qualified scope](subagents.md) | [Qualified scope](subagents.md) | This inventory records supported combinations, not a feature-equality checklist. Do not silently drop options, fabricate measurements, weaken isolation or remove working features. Unsupported operations stay explicit; implementing them is a -separate board decision, not an onboarding prerequisite. MiniMax also implements the same colocated Runtime enrollment. This V1 decision +separately prioritized decision, not an onboarding prerequisite. MiniMax also implements the same colocated Runtime enrollment. This V1 decision uses our daemon as executor and explicitly does not claim stock `exec-server` interoperability. The old service-side harness/remote executor route is retired. Service-origin HTTP MCP remains unsupported on `self_hosted`; `none` MCP and qualified hosted Template Plugin MCP retain their separate scopes. +## Acceptance checklist + +Before implementation, record the operation set, expected results, exclusions and +stopping conditions. A batch ends when its declared operations pass; it does not +expand to match another Harness's feature list. A small adapter does not remove +the need for native qualification. + +- Adapter tests: two ordinary Turns share one native process/connection and history; + cancellation followed by another Turn; stale cancellation and late events; native + exit, cleanup failure, input write/application receipts and unknown outcomes. + Verify fresh per-Turn usage, function, input and child-observation state. +- Shared integration: public admission, actual Worker/device selection, gateway, + daemon registration/dispatch and durable terminal projection. See + `TestThirdHarnessPublicOnboarding` for a synthetic example, not native evidence. +- Real acceptance: pinned official Python SDK and raw HTTP against our Agents API, + real provider API, native harness and independent execution database. Verify + initial execution, warm follow-up, cancellation and restart/continuation. + Record native owner identity and same-condition cold/warm timing; mock results + cannot establish native reuse or performance gains. + For hosted qualification also verify Files/Artifacts, workspace identity, + credential protection and foreign-history rejection. +- Regression: existing qualified engines keep working. Run targeted tests during + development, then `make check` and applicable real regressions. API changes + require `make openapi`; query changes require `make sqlc-generate`. +- Review: follow the repository + [blind review workflow](../../CONTRIBUTING.md#review). + +Record exact revisions, image/package versions, commands, results and limits. +Keep keys in private operator files; never commit them or include them in logs. +Failed or synthetic runs cannot be counted as real acceptance. Acceptance of one +engine is not complete public protocol compatibility. + ## Common contract acceptance The synthetic [third-harness fixture](../../apps/parsar-daemon/testdata/onboarding/main.go) diff --git a/contracts/agents-api/node-host-history.md b/contracts/agents-api/node-host-history.md index 44406ca1e..6c4a03af1 100644 --- a/contracts/agents-api/node-host-history.md +++ b/contracts/agents-api/node-host-history.md @@ -67,3 +67,13 @@ No interpolation, offline backfill or whole-host process inventory is performed. History survives Core and node restarts until normal retention expiry. Core process history remains a separate in-memory series and resets when Core restarts. Neither series contains credentials, model data, paths or user content. + +## Implementation rules + +Administrator node detail adds host observations and history as documented in +[node-host-history.md](node-host-history.md). Keep the node +list unchanged apart from the address each node enrolled with (`core_url`). Reuse authenticated heartbeat ownership, the Runtime sampling +sweep and PostgreSQL retention cleanup; node observations have their own table +because they do not belong to a Project, Session or Environment. History is +best-effort telemetry, never scheduling truth. No read-triggered sampling or +offline backfill is allowed. diff --git a/contracts/agents-api/runtime-observability.md b/contracts/agents-api/runtime-observability.md index 1aaedabf0..d4c4cd9cb 100644 --- a/contracts/agents-api/runtime-observability.md +++ b/contracts/agents-api/runtime-observability.md @@ -153,3 +153,40 @@ Additional provider telemetry and idle-policy authority remain separate work. The OTLP resource identifies Core with `service.name=oac-core` and `service.namespace=oac`. Metric names retain the `agents.*` namespace. + +## Implementation rules + +Runtime telemetry uses a separate read-only service boundary documented in +[`contracts/agents-api/runtime-observability.md`](runtime-observability.md). +Resolve durable Session, Environment and Runtime-instance identity before selecting +a provider source. Observation never extends a lease or changes compute lifecycle. +Keep observed zero, unavailable data and unsupported Runtime modes distinct. Metrics +may inform operators, but automatic suspension requires durable Core-owned activity +state and must not use a monitoring backend as lifecycle authority. +Managed Docker observes one non-streaming Inspect/Stats sample. Managed microsandbox +observes the exact persisted compute generation through the existing one-shot helper +and pinned native CLI metrics report, with SDK identity checks before and after +observation. Derive compute start from the same native sample timestamp and precise +uptime; never subtract rounded uptime from a new wall-clock timestamp. Preserve +cumulative CPU seconds, memory usage/limit and +compute uptime semantics across both. Do not use microsandbox's instantaneous CPU +percent, wake suspended compute, or expose provider-native identifiers to fill a +common field. E2B has no cumulative CPU time: one read-only helper request per +page of at most 100 allocations reads E2B's batch metrics and confirms each +receipt's sandbox in a labelled running listing. Its reported CPU share fills +`utilization_ratio`; disk appears only in the administrator list. Page reads go +through the Service's optional batch source; do not add a second collector. + +Runtime history uses the existing Core PostgreSQL database: one sanitized row per +periodic observation, seven-day retention and bounded reads. It is best-effort +operational evidence, not execution or Usage authority. The execution owner samples +by default every 30 seconds. Core's internal measured Session usage (every +recorded root Turn snapshot, active Turns included) supplies token snapshots, not +the public Session usage rule; never aggregate provider counters as model tokens. Preserve missing data +and reset CPU derivation across compute incarnations or counter regressions. E2B +history stores the reported utilization ratio; a bucket holds their mean. +The bounded asynchronous database writer and optional OTLP exporter have independent +queues; external telemetry outages must not stall local history or execution. +Retention cleanup also runs without active Runtimes. The browser queries only Core, +never storage or a Collector, and stays a lightweight administrator console. +No additional metrics database or Collector is required for retained charts. diff --git a/contracts/agents-api/sandbox-deployment.md b/contracts/agents-api/sandbox-deployment.md index ca2fd3c1d..a6937525f 100644 --- a/contracts/agents-api/sandbox-deployment.md +++ b/contracts/agents-api/sandbox-deployment.md @@ -74,6 +74,19 @@ existed before Core recorded it reports null until its next phase change. A suspended microsandbox allocation's age, combined with the deployment's snapshot retention, tells roughly when Core reclaims it. +### Hosted and application-managed placement + +The official `openai_hosted` discriminator means hosting by this independent Core +service, using its configured hosted Provider. Keep the public value unchanged; a product-named hosted value is not +a new API type. Public Environment Templates apply only to this hosted path. +E2B onboarding follows the application-managed `self_hosted` resource workflow: +the application owns sandbox provisioning and cleanup, and our daemon connects +with the returned Environment ID, unchanged `remote_url` and scoped environment +authorization. Reuse the same Runtime and thin provider components. No OpenAI +executor process or additional execution architecture is required. Qualify +principal/tenant ownership, credentials and connection lifecycle using the pinned +client and actual execution; document our transport boundary explicitly. + ## Selection request POST and PUT take the same complete selection and require `expected_generation` diff --git a/contracts/agents-api/session-diagnostics.md b/contracts/agents-api/session-diagnostics.md index cfd8e1c6d..9be3c76f3 100644 --- a/contracts/agents-api/session-diagnostics.md +++ b/contracts/agents-api/session-diagnostics.md @@ -79,3 +79,15 @@ The typed client exposes `retrieveSessionDiagnostics` and `retrieveTurnDiagnostics` on `AdminClient`, retaining request cancellation and validating scope, categories, nullability and safe parameter values. No browser credential or Core Web implementation is added by this contract. + +## Implementation rules + +Root Session/Turn diagnostics are Core-key reads from one committed database +snapshot, reusing public status projection and precedence. Keep their finite +failure catalog separate from private outcome text. Persist safe provisioning +details in the existing failure transaction; never reconstruct historical details +from reason strings. Item settlement belongs to the existing Session lock and +terminal transaction: event/input receipt for normal terminal projections, one +post-lock database wall-clock sample for forced incomplete Items. Preserve +historical terminal nulls and public native completion times. See the +[diagnostics contract](session-diagnostics.md). diff --git a/contracts/agents-api/structured-output.md b/contracts/agents-api/structured-output.md index e15e6a642..530f1ea62 100644 --- a/contracts/agents-api/structured-output.md +++ b/contracts/agents-api/structured-output.md @@ -54,6 +54,43 @@ Recovery uses the existing Session/Turn/Items queries and native continuation. SSE is still live-only. Frozen schemas apply to both initial and resumed execution; ordinary text configuration retains its prior behavior. +The public `text.format={type:"json_schema",schema:{...}}` is resolved with saved +Agent overrides and frozen in the existing Session configuration. Core transports +it in `ExecutionControls.OutputFormat`; it does not append prompt instructions, +validate/retry model answers, repair JSON or select native tool names. Public +admission requires the selected profile's structured-output qualification, and +only requests using this option require the Runtime's `structured_output` and +message-observation capabilities. A capability advertisement does not qualify a +new public combination. Claude advertises this operation only when the installed +SDK bridge reports its `structured_output` feature. A workspace Runtime also +requires the complete local Runtime contract and `workspace_structured_output`; +preparation checks that bundle before native launch. These remain adapter readiness +features, not new Core lifecycle or public protocol variants. + +The current qualified path is Claude SDK, `environment:none` or Core-managed +Docker `openai_hosted`, medium verbosity, single Agent, with optional ordinary +function tools and text results. The workspace uses its existing preparation and +bypass execution profile with the SDK's configured `StructuredOutput` tool added to +inventory and permission checks. Frozen schemas reach preparation before the +input handoff; Start cannot replace them. Skills, Plugins, capability directories, +HTTP MCP, Subagent/tool-discovery combinations and schemas without an explicit +object root remain unqualified; an explicit non-object root type is a protocol +error for every harness. Check resolved template contents as well as inline configuration; +ordinary text requests retain their existing qualifications. +The SDK uses binary64 JSON numbers: reject execution schemas whose numeric values +would change during that conversion, without narrowing saved Agent storage. +Codex and MiniMax structured output remain explicit execution gaps. + +The Claude adapter passes `outputFormat` to the maintained native SDK and allows +its native `StructuredOutput` terminal tool. A matching live root tool result and +an attributed successful SDK result confirm the final output. Publish the native +`result.result` string unchanged as a completed `final_answer` Message using the +native tool-use ID; the parent assistant ID can already own a prose Item. Do not +publish unvalidated retry candidates or serialize `structured_output` back to +JSON. Native retries remain harness-owned. Existing input receipts, usage, +cancellation, release and recovery rules apply unchanged. The implementation and +qualification limits are recorded in [the coverage note](structured-output.md). + ## Acceptance `TestNativeStructuredOutputPublicExecution` and diff --git a/contracts/agents-api/tool-search.md b/contracts/agents-api/tool-search.md index 1acad6fc4..c9e0fa9e1 100644 --- a/contracts/agents-api/tool-search.md +++ b/contracts/agents-api/tool-search.md @@ -33,6 +33,30 @@ gaps, not claimed upstream restrictions. Codex and MiniMax discovery remain gaps Unknown public combinations reject before execution; an actual Runtime must also advertise the operation. An advertisement alone cannot qualify a public profile. +Public `tool_search` and function `defer_loading` are shared Runtime intent. +Core preserves complete immutable definitions, sends `PromptRequestPayload.ToolSearch` +and each `FunctionTool.DeferLoading`, and requires the operation's existing profile +qualification plus the Runtime `tool_search` capability. It never performs native +search, selects native names, interprets provider policies or implements another +model/tool loop. Additional harnesses implement the same intent in their adapters. +The pinned Session AgentTool response union excludes the tool_search input member; +project it out of Session/SSE resources while preserving saved and frozen input. + +The bounded implementation targets Claude's single-agent `environment:none` +function profile, including qualified inline message images. The adapter explicitly enables native ToolSearch and sets +per-function MCP `anthropic/alwaysLoad` from the requested deferral flag. Ordinary +functions stay eager. Existing function callbacks, application receipts, cancellation +and cold continuation remain the only execution/result lifecycle. Runtime discovery +advertises this operation only with an installed bridge supporting `tool_search`. + +Known conflicting native provider modes and search/beta settings reject in the +adapter. The maintained native harness owns dynamic model/provider eligibility; +its SDK exposes no reliable pre-input receipt proving effective deferral after a +policy change. Do not represent tool inventory or an operator allowlist as that +proof. Record exact real model/provider evidence and this detection gap separately. +Search-only, missing-search, workspace, MCP and Subagent combinations remain +unqualified; a repeated `tool_search` is a protocol error. See [the operation coverage](tool-search.md). + ## Evidence and limitations Native feasibility passed with Kimi `kimi-k3`: initial provider requests excluded diff --git a/contracts/agents-api/write-audit.md b/contracts/agents-api/write-audit.md index 64408a88f..5f72cc843 100644 --- a/contracts/agents-api/write-audit.md +++ b/contracts/agents-api/write-audit.md @@ -68,6 +68,18 @@ secrets, model credentials, tokens, file paths or file contents in these tables. Explicit public OAuth Credential writes are covered. Automatic OAuth refresh is not a separate public write. Public resource IDs retain their existing formats. +Public resource writes carry authenticated key provenance separately from the +execution principal. Persist their operation record and genuine creation ownership +in the same business transaction; no best-effort response middleware or async audit +queue. A failed audit must roll back the write. Internal lifecycle/refresh work does +not acquire public provenance. Retries never replace ownership. Environment uploads +persist safe request origin before dispatch and record success with the confirmed +receipt, not the native filesystem call. Never put payloads, paths or secrets in +audit metadata. Read models are Core-key `/core/v1` routes; keep `/v1` wire +contracts unchanged. See +[write-audit.md](write-audit.md) for coverage, retention and +console integration. Do not confuse key identity with Session creator identity. + ## Console queries Both endpoints require the Core key under `/core/v1/projects/{project_id}`. The path identifies the Project, including an diff --git a/docs/development.md b/docs/development.md index 18f1d81ac..c07381ba6 100644 --- a/docs/development.md +++ b/docs/development.md @@ -40,10 +40,10 @@ pnpm exec playwright install --with-deps chrome export OAC_TEST_OFFICIAL_SDK_PYTHON="$PWD/.venv/bin/python" ``` -Set `OAC_TEST_DATABASE_URL` privately to a dedicated `oac_*_tests` PostgreSQL -database. Its role needs permission to create databases: managed-provider tests -create and drop isolated test databases. Never point the test suite at an -installation or product database. The full gate fails if this setting is missing. +Set `OAC_TEST_DATABASE_URL` privately to a dedicated PostgreSQL test database. +Never point the test suite at an installation or product database. The +[test database rules](../CONTRIBUTING.md#test-database) list the required role +permission. Third-party native packages are pinned build inputs; changing a pin requires the relevant native qualification as well as compilation. @@ -85,7 +85,7 @@ site; `pnpm dev:docs` starts its development server. | `services/agents-api/internal/engine` | Pure qualification of harness operations and placements | [Harness onboarding](../contracts/agents-api/harness-onboarding.md) | | `internal/agentdaemon/proto` and `gateway` | Shared wire types, validators and authenticated Runtime connections | [Runtime protocol](runtime-protocol.md) | | `apps/parsar-daemon/internal/dispatch` | Runtime preparation, Executor reuse, Turn and cleanup ownership | [Harness lifecycle](../contracts/agents-api/harness-onboarding.md#required-adapter-interfaces) | -| `apps/parsar-daemon/internal/agent` | Native harness adapters | [Native references](../contracts/agents-api/harness-onboarding.md#minimal-runnable-example-and-native-references) | +| `apps/parsar-daemon/internal/agent` | Native harness adapters | [Native references](../contracts/agents-api/harness-onboarding.md#native-references) | | `services/agents-api/internal/sandbox` | Provider interfaces and managed compute lifecycle | [Provider onboarding](sandbox-provider.md) | | `services/core-console` | Console login and the server-side management proxy | [Web architecture](web/architecture.md) | | `apps/web` and `packages/agents-client` | Console UI and typed clients | [Web guide](../apps/web/README.md) | @@ -100,65 +100,38 @@ apply to user-owned machines and Core-managed environments; read the ## Choose an extension boundary -### Add a Harness adapter - -Start with [Harness onboarding](../contracts/agents-api/harness-onboarding.md). -Choose a native SDK or machine-readable protocol, declare the operations and -placements you intend to support, and implement the shared -[`Executor` and `Turn` interfaces](../apps/parsar-daemon/internal/agent/harness.go). -The guide covers factory registration, service profiles, capability declarations, -input receipts, event translation, cancellation, history and cleanup. - -Use the [synthetic onboarding fixture](../apps/parsar-daemon/testdata/onboarding/main.go) -to see the full registration path. With the dedicated test database configured: +Each boundary has one canonical guide. Read it before changing code; this page +only helps you pick the right one. -```sh -go test ./services/agents-api/internal/store -run '^TestThirdHarnessPublicOnboarding$' -count=1 -``` - -This test reaches the actual gateway and Runtime router, but its synthetic engine -cannot establish native acceptance. Qualify the real adapter through the official -client, Core, Runtime and a real model. Verify two ordinary Turns, cancellation, -continuation after restart, unknown outcomes and the advertised optional operations. -Record exact native versions and limitations in the linked operation contracts. - -The [Claude bridge guide](../packages/claude-sdk-adapter/README.md) explains the -separate TypeScript SDK process. Codex and MiniMax implementation references are -linked from the onboarding guide. Reuse common process and workspace helpers; -keep native event interpretation inside the adapter. - -### Extend the Core–Runtime protocol - -Read [the protocol guide](runtime-protocol.md) before adding a message or receipt. -Change the shared types and validators in `internal/agentdaemon/proto`, both peers, -and their contract tests together. Keep exact wire-version checks and update the -message ordering, identity and failure ownership documentation. A transport write -cannot be used as evidence of native application or completion. +| Boundary | You are adding or changing | Canonical guide | +| --- | --- | --- | +| Harness adapter | A native agent engine behind the Runtime | [Harness onboarding](../contracts/agents-api/harness-onboarding.md) | +| Sandbox Provider | Outer compute that creates and reclaims Environments | [Sandbox Provider guide](sandbox-provider.md) | +| Core–Runtime protocol | A message, receipt or lifecycle rule between Core and the daemon | [Core–Runtime protocol](runtime-protocol.md) | +| Public API operation | A `/v1`, `/core/v1` or `/api/v1` route | [API index](api/README.md) and [contracts](../contracts/agents-api/README.md) | +| Environment capability | Skills, Plugins, MCP or `packages.system` preparation | [Environments](../contracts/agents-api/environments.md#runtime-capability-preparation) | -```sh -make check-runtime-contract -``` +### Add a Harness adapter -The focused gate covers wire validation, gateway behavior, transport, dispatcher, -real WebSocket contract fixtures and Core observation failures. Adapter-specific -native acceptance remains a separate requirement. +Implement the shared `ExecutorFactory`, `Executor` and `Turn` interfaces in +[`agent/harness.go`](../apps/parsar-daemon/internal/agent/harness.go), register +the adapter and add a service profile. Follow the numbered steps in +[Harness onboarding](../contracts/agents-api/harness-onboarding.md); qualification +evidence belongs in [Harness integration](../contracts/agents-api/harnesses.md). ### Add a Sandbox Provider -Read [Provider onboarding](sandbox-provider.md). Implement -[`SandboxProvider`](../services/agents-api/internal/sandbox/sandbox_provider.go) -for the outer compute lifecycle. Register construction and vendor configuration at -that boundary; use optional interfaces for observation and checkpoints. Runtime -capability preparation and native execution keep their existing owners. +Implement the five required `SandboxProvider` operations in +[`sandbox_provider.go`](../services/agents-api/internal/sandbox/sandbox_provider.go), +register the provider kind and pass `make check-sandbox-provider-contract`. Follow +the numbered steps in the [Sandbox Provider guide](sandbox-provider.md). -```sh -make check-sandbox-provider-contract -``` +### Extend the Core–Runtime protocol -Run the [shared contract suite](../services/agents-api/internal/sandbox/contracttest/provider.go) -through the adapter's native boundary, then qualify actual resource creation, -bootstrap, observation, uncertain results and cleanup. Mocked compute cannot prove -that resources were reclaimed or that the outer environment provides isolation. +Change shared types and validators in `internal/agentdaemon/proto`, both peers +and their contract tests together, keeping the exact wire-version check. The +[protocol guide](runtime-protocol.md) owns message order, receipts and failure +ownership; `make check-runtime-contract` is its focused gate. ## Validate a change @@ -189,7 +162,7 @@ with `AGENTS_FIXTURE_PORT` and `AGENTS_WEB_PORT` when running parallel validatio Keep databases, ports and containers separate between validation workers. Compilation, fixture success and live model/provider acceptance establish different facts; report skipped or unavailable checks explicitly. Follow the -[independent blind review workflow](../CONTRIBUTING.md#workflow-and-quality) +[independent blind review workflow](../CONTRIBUTING.md#review) after validation. ## Change documentation diff --git a/docs/maintainers.md b/docs/maintainers.md index 6effd091a..5a6b36ad7 100644 --- a/docs/maintainers.md +++ b/docs/maintainers.md @@ -33,6 +33,54 @@ A bundle carries a fixed set of docs (the build lists them). Links between them relative; every other relative link is rewritten to the same file on GitHub at the bundle's commit, and the build fails if a link or anchor does not resolve. +### Independent Core build artifacts + +`make build-agents-api` produces `oac-core`, `oac-core-migrate`, +`oac-core-device` and `oac-core-environment-key` under `${OAC_DEV_HOME:-$HOME/.oac}/build/oac-core`. +`OAC_DEV_CORE_BUILD_DIR` may select another absolute output directory. The build +uses only the explicit source set in `scripts/build-agents-api.sh`: the execution +service, its Go contracts and required shared daemon/logging packages, plus the +root Go module manifests. Product server/frontend, other applications and their +migrations/assets are absent from the temporary build context. Keep this boundary +explicit when introducing shared dependencies; do not copy the whole repository +to make an accidental product dependency compile. + +The build uses Go directly with workspace discovery and CGO disabled, read-only +module manifests and trimmed paths. It requires no Node, Docker or product setup. +`make check-agents-api` runs this build before its tests, so the full `make check` +and the dedicated CI workflow enforce the same boundary. CI exercises the built +migration command and uses the built server for official-client HTTP checks. +`make docker-build-agents-api` reuses that build for Linux amd64 and sends only +its executables, the E2B helper and `services/agents-api/Dockerfile` to Docker. The +digest-pinned Debian slim runtime runs without root, product assets or an embedded +harness. Keep runtime credentials outside the image and migrations explicit. +`make check-agents-api-container` runs the existing official-client suite against +the image with a read-only root filesystem; it requires Linux Docker, a non-root +host user, the pinned SDK and a dedicated execution test database. Dedicated CI +runs this after binary validation. Changes to the image/build path require this +check in addition to `make check`; do not make ordinary Go builds require Docker. +Registry publication, additional runtime architectures, daemon packaging and +product cutover remain separate work. + +`make build-agents-api-release` reuses the isolated build for a Linux amd64 archive +under `~/.oac/`, with its four commands, license, operator guide, source/tree and +protocol manifest, and file/archive checksums. It requires clean committed source +and Python 3.9+, stages output privately, and packages fixed artifacts deterministically. +Keep runtime configuration, credentials, product sources and separately installed +daemons/harnesses out of the archive. Archive changes require content/hash and +fresh-extraction operator checks plus `make check`; execution acceptance uses the +packaged operators and public protocol, not private Store provisioning. Preserve +the database and native history when replacing the API package. This target does +not publish a release or provide an installer/supervisor. + +The archive stays Docker-free. Its former Docker-hosted variant, selected by a +retired release Runtime image variable, is gone and the builder refuses that variable: +an image ID alone is not the complete Runtime release a Docker deployment needs. +Docker-hosted deployments use the matched Core distribution below. The archive and +the standalone container are advanced paths for running Core alone; keep them off the +newcomer installation path and document them under +[Maintainers and advanced deployments](maintainers.md). + ## Publish a version Push a version tag on the reviewed commit to run `core-release`: @@ -109,6 +157,19 @@ Release. With `draft_release=false`, files remain in the Actions artifact only. Use the exact matched asset set; do not mix builds or resolve components through `latest`. The historical batch qualification tools are not part of tag publication. +### Continuous integration coverage + +CI coverage has three owners: `core-check` runs the complete `make check` gate; +`api-acceptance.yml` adds the pinned official-client, migration-command and container +acceptance without repeating the full service test suite; `native.yml` +builds and tests the daemon, process lifecycle, Harness protocols and installer +bundle together on Linux, macOS and Windows. Native tests use the packaged +Harnesses and share one daemon build per platform. Changes to native sources, +shared dependencies or packaging inputs trigger that matrix; documentation-only +and unrelated Web changes do not. Manual native validation remains available. +Superseded native runs on the same ref are cancelled. Workflow syntax validation +and release qualification remain separate checks. + ## Run Core without the installer These paths give you Core alone, without Web, the `oac` command or `config.json`. @@ -128,3 +189,448 @@ Core reads only its environment; the lists the variables. The Docker-hosted variant of the standalone archive is retired: it could not describe a complete Runtime release by itself. Docker-hosted deployments use the Core distribution and its installer, whose manifest carries the complete release. + +## Distribution and installer rules + +[Configuration](configuration.md) is the canonical operator parameter reference. +[Installation options](getting-started/install-options.md) owns installer usage; +README Quick start and the installation guide link there instead of copying option +lists. Generate its flag-to-key table and the configuration reference from the schema. +`host` is the single Core/Web listener address; `ports.web` uses only `--port`, and +`ports.core` uses `--core-port`. Defaults, validation and flag mappings live in the +schema. Flags seed config.json; health checks, setup, apply and generated service +files derive their addresses from that same config. Apply uses the last applied +address to contact running services before changing listeners. Non-loopback binds +require the existing HTTPS public origin; PostgreSQL remains loopback/private. +Every process setting has one home: the installation's private `config.json`, +described by `deploy/install/config.schema.json`. The operator edits only that +file; `oac apply` validates it, derives `generated/` (Compose file, `core.env`, +native unit, Core key digest file, settings snapshot) and converges on what actually +runs: each service carries the digest of its inputs (Compose label +`io.oac.inputs`, native `OAC_INPUTS`), and exactly the services whose running +inputs differ are recreated or restarted. Decide restarts from what runs, never +from recorded bookkeeping, so the next apply finishes any interrupted one. Installation flags only seed it, and +rerunning the installer rejects them. Runtime settings stay in PostgreSQL and +change through Web or `/core/v1`. Secrets live once each in `secrets/`; identity and +install facts live in tool-written `state.json`. State format 2 uses an `oac-` Compose +project; config.json's independent schema format stays 1. The operator command is +`oac` (`oac_cli.py`, packaged as `oac.pyz`), with default installation directory +`~/.oac/core`, private `~/.oac`, generated `x-oac` annotations and `.oac.lock`. +The project has no historical installation compatibility or in-place version upgrade +contract. Install only into an empty directory, or repair the exact same source +revision. Refuse old formats, conversion journals and different revisions before +installation mutation; retain their data and direct operators to reinstall separately. +The installer and every mutating `oac` command share the stable `.oac.lock` inode. +The installer owns this lock across creation, payload/native/launcher repair and +apply, invoking the already-locked apply implementation without nested locking. +Never unlink or replace the lock, including after an interrupted fresh install. +Current-version interrupted apply and rotation retain their existing recovery path. +The packaged `oac.pyz` entrypoint embeds the build source revision and checks it +against the existing `state.json.source_commit`; this adds no installation state +format. Node `--update` is refused; runtime generation operations are unchanged. + Core process settings use `OAC_*`, Web settings use `OAC_WEB_*`, and shared Go +logging uses `OAC_LOG_*`. Retired settings fail startup even when empty or when +the new name is also set; report every matching name without values. No supported installation entrypoint converts pre-rename files. Core and operator executables +are `oac-core`, `oac-core-migrate`, `oac-core-device` and +`oac-core-environment-key`; Web is `oac-web`, and the Core-host E2B helper is +`oac-e2b-provider` under `/opt/oac/e2b` in the image. +Core still reads only its +environment and has no config loader; it serves the non-secret snapshot at +`GET /core/v1/installation`. Keep the schema, the subset validator +(`config_model.py`), the generator and the generated reference table in +`docs/configuration.md` (`scripts/config-reference.py`) in step. Do not add a +second operator configuration file, loader precedence, hot reload, compatibility +reading of retired names, or an embedded Core node. `install.sh --sandbox` calls +the ordinary administrator API once; PostgreSQL owns the resulting selection. +Administrator-issued enrollment approves capacity (default two active/eight +retained); a node cannot supply or overwrite those limits. Downloaded specification +copies remain validated against the existing database-owned resources/Runtime +contract. + +The E2B template builder assigns traversable modes only to synthetic public archive +ancestors. Runtime file and directory permissions, private build contexts, key inputs +and output umask remain unchanged, including when invoked under umask 077. + +The installer packages Core and the Web console together, +with independent `--core-only` and `--web-only` modes. `site/` is the public static +landing, separate from `apps/web`; it must not create an onboarding prerequisite, +call a model, or claim complete protocol compatibility. Use Web's page and action names in user docs, and `OPENAI_BASE_URL` and +`OPENAI_API_KEY` for application examples. The [documentation ownership](../CONTRIBUTING.md#documentation-ownership) +map defines the authored sources. + +A distribution carries a fixed list of docs (`BUNDLED_DOCS` in +`scripts/core-distribution-manifest.py`). The build keeps relative links between +bundled docs, rewrites every other relative link to the same file on GitHub at the +bundle's commit (`@SOURCE_REVISION@`), and fails on a link or anchor that does not +resolve; `make check-distribution` runs the same check on the repository's docs. Keep +the list self-consistent when adding or moving a doc the installer or its output +refers to. + +`make build-core-distribution` builds from clean committed source and reuses the +existing API, Runtime, SDK, helper and Web builders. Artifacts record source and +immutable image identities, the actual Runtime manifest digest, checksums and +microsandbox runtime/firmware hashes and executable native payloads. Local distribution builds do not publish. The tag-triggered release workflow +reuses the full repository check on the exact build source, then publishes the +matched assets automatically; manual runs remain artifact-only or draft-only. +Only publication receives repository write permission. Never overwrite release +assets or move an existing version tag. The [maintainer guide](#publish-a-version) +owns tag syntax, prereleases and failed-publication recovery. +Release assets include `deploy/install-release.sh` as standalone `install.sh` +with a checksum. This public downloader resolves latest once (or a selected tag), +verifies the control-plane archive before safe extraction, and delegates to that +bundle's installer. Default installation downloads Core, Web and PostgreSQL payloads, +never the Runtime image or node execution artifacts. Offline archives remain an +explicit distribution option. It introduces no separate installation state, upgrade path or login flow. +Build/test success is distinct from real-model qualification; maintainers assess +that evidence before pushing a release tag, and no synthetic result substitutes +for native execution acceptance. + +Distribution `images` records each exported image's config digest; +`image_manifest_digests` records its OCI manifest/index digest. Derive and verify +both from the same archive, including its referenced config and layer bytes, and +require the build host's selected image ID to match one of them. Docker's classic +store identifies images by config, while its containerd store uses the OCI +descriptor. The builder therefore selects the digest from BuildKit's build metadata +that the local store resolves, never the `--iidfile` config digest alone, and +disables provenance attestations so each image and archive holds one platform +manifest in both stores. For the same reason the default PostgreSQL input is pinned +by its linux/amd64 platform manifest digest: a pulled multi-platform tag keeps its +whole index in the containerd store, and that export holds every platform. Core, +node and self-hosted installers share one resolver +for these required identities: confirm Linux amd64 and the returned immutable local ID, +then use that ID in service/provider configuration and Runtime launches. Tags do +not replace identity verification. The microsandbox-qualified `runtime_ref` +remains independent of Docker's local store identity. + +The manifest is the shared download contract for Core, node and self-hosted +installers: flat versioned filenames, compressed Runtime size/hash and unpacked +size/hash. Nodes obtain bootstrap metadata from their configured console (or a local offline +bundle). Web serves locally available artifacts first; for missing declared execution +artifacts it redirects the node to the versioned HTTPS release base in the verified +distribution manifest. Web does not download or cache those bytes. Only artifact +requests may follow HTTPS redirects, without credentials or cookies; metadata and +enrollment requests must remain on the configured console. Nodes retain size and +SHA-256 verification, resumable transfers and immutable release selection. Download into +private temporary files, verify before atomic promotion, and reuse only verified +cache entries or exact image identities. Core's default image must not acquire +execution-only payloads. Python zipapps bundle the shared resolver with each +remote bootstrap; the console publishes only fixed non-secret files and declared +artifact names. Candidate build automation creates artifacts and may create an +unpublished draft; a successful build is not real execution qualification. A +separate existing-host batch controller may publish that draft automatically only +after directly supervised real qualification and verified batch landing. Repository +visibility is public. Published release downloads are anonymous and must not +require GitHub login or repository credentials. +Manual builds use the legal `build-` release tag; tag-triggered +builds use the actual `v*` tag. The manifest download base and draft tag must match, +while artifact filenames and source provenance retain the full source SHA. +Qualify the exact downloaded production artifacts before publishing the draft; +keep qualified executable, image and source payload bytes and source identity +unchanged. A recorded release-address/checksum-only repack requires proof that +every other archive member is unchanged and verification of final published asset +digests and URLs. Never use an acceptance +image containing a private test CA or model credential as a release input. +Repository visibility is independent of publication. Do not add repository +credentials to installed node/Runtime configuration to bypass download access. + +`scripts/promote-qualified-release.py` requires an explicit full candidate source +SHA, binds it to the archive manifests and matching `build-` tag, and uses +existing local gh authentication and SSH. It uploads/downloads the complete +matching thin/offline/Runtime asset set and verifies archive members and asset +hashes. The separate qualification package is supplied by the maintainer with an +explicit reviewed manifest SHA256. Its complete file inventory, ordered Python +commands, bounded stage timeouts and private path/resource configuration are +verified before any Release mutation and again by the remote supervisor. Candidate +assets cannot select or replace this execution package. Keep host-specific +acceptance scripts, usernames and credential paths outside this public repository; +never put credential values in either manifest. + +The reviewed adapter receives a fresh canonical UUID and exact inventory over the +authenticated command channel. It directly supervises fresh-install, +current-lifecycle, managed-native-smoke, diagnostics-observations-smoke and +node-runtime-smoke in that order. This batch uses one fresh container installation, +one completed managed Session, read-only diagnostics for that Session, and one +current Runtime Session on one new node. It does not rerun the full multi-host, +generation or GC matrix. Every child must exit successfully and +return only its own passed check, the current controller identity and its observed +owned resources. Resources and the previous result flow between live children; +a supplied pass file, skipped check or old report cannot release the candidate. +Verify package and candidate bytes again after each stage. The small shared +`qualification_control.py` is pinned to the reviewed tooling commit and private +package. A live SSH stdin channel carries the request then heartbeats; EOF, timeout, +SIGTERM or SIGHUP stops later work. Each local stage or remote worker has one +foreground process group and a waiting owner outside that group. The owner cleans +the group on success, nonzero exit, timeout and cancellation, including foreground +descendants orphaned by an inner timeout or SIGKILL. Nested foreground commands +inherit the group; only explicitly recorded background resources may detach. +Those retained background resources are outside foreground cleanup. Private nested +workers use the same channel. +Already-issued writes may have unknown outcomes: retain intents/resources and do +not replay or claim rollback. Control tests exercise +short-lived fixture children only and never establish live qualification. + +The caller supplies the independently reviewed promotion-tooling commit. Its +changes from the candidate may only affect the exact promotion files enumerated +in the controller, including CONTRIBUTING, docs/maintainers and the current-batch +node-generation protocol wording correction; the Makefile +exception permits only registration of the controller and control-channel tests. Main must contain the +candidate source and have the reviewed tooling commit's tree. This permits normal +merge commit identity changes and release-only documentation updates without +rebuilding or relabeling the original candidate. The candidate's bundled docs and +source archive retain source 48 (CONTRIBUTING and the node-generation protocol +are present through the source archive, not as direct bundled docs); new release instructions live in the tooling +commit. Product changes or a different main tree block promotion of the old +candidate. Never infer batch membership from all open PRs or automatically merge +them in the publication command. + +Use one controller invocation for the batch. After successful qualification it +waits in the same process, within the explicit merge-wait budget, for the exact +reviewed batch tree to reach main. An ancestor main waits; conflicting main changes +fail immediately. Cancellation or expiration retains evidence and cannot turn a +saved result into resume authority. It verifies unchanged draft identity, +target, tag and downloaded bytes immediately before publication. After the final +download it rechecks main/tree/tag and the same draft ID, then updates that verified +Release ID directly rather than resolving the tag again. It checks the +published bytes afterward. Conflicting assets are never overwritten. An interrupted +run is reconciled before another invocation; stored qualification output is evidence, +not a resumable permission to publish. Preserve its isolated local/remote evidence +and installation resources. No runner, background service, new GitHub secret or +repository-visibility change is required by this finite batch path. + +Executor credentials are issued by the operator with the Core key, through Web or +a Core-key script, under +`/core/v1/projects/{project_id}/environments/{environment_id}/executor-credentials` +and reuse the existing restricted issuer. The target must be a self_hosted +Environment of that Project whose Session exists; anything else is 404. The +Project's principal is the credential's execution principal, its scope stays +daemon enrollment and connection for that one Environment, and issue, rotate and +revoke each record an administrator audit entry in the write's transaction without +the secret. Project API keys cannot issue them. Native self-hosted installation runs the daemon on Linux, macOS or Windows with +its starting account's permissions. It owns no sandbox node or Core allocation, +adds no isolation and retains user-owned native history after uncertain launches. +Report started, connected and real execution success separately. +Native installation uses `oac-daemon install` and `start` with an explicit +credential-file path and the same `OAC_RUNTIME_HOME` for lifecycle commands. It is +current-version only and does not adopt an older container installation. Rotation +replaces the configured credential file for the same key and restarts the daemon; +never create a replacement Session history to recover a credential. The console's +older container-installer command remains a distinct packaged workflow, not the +native installation interface. Report connection and actual execution separately. +Core-key executor credential lists expose a required connection observation with +never_enrolled, connected or disconnected status, immutable bound key identity, +enrollment time and last authenticated heartbeat time. Read credential metadata +and binding facts in a closed read-only snapshot, then reuse runtimeenrollment's +current authority and actual gateway peer checks. Recheck executor and device +authority after reading the peer; rotation, revocation or Environment retirement +must not inherit a former key's connected state. No gateway means not connected, +never an authentication bypass. Known authority loss is disconnected; storage +errors remain errors. Keep digests/device IDs internal and public /v1 unchanged. +Connection timestamps are history, not execution/native/model readiness. + +Self-hosted installation confirms connection through the private daemon transport +using only its restricted executor credential. The read checks the exact live +Environment/key binding and current authenticated connection; it never enrolls, +allocates, wakes a sandbox or grants project resource access. It is an `/api/v1` +machine route that reaches Core directly, never through the console. Bounded +polling retains the original Runtime identity and history; timeout is a +diagnostic failure, not permission to replay initialization or replace history. The installation public URL +(`public_url` in the installation's `config.json`, seeded by `--public-url`, and +`OAC_PUBLIC_URL` for Core) is the one origin for +applications, nodes, sandbox guests and self-hosted executors, and also the console +origin. Core derives the daemon `wss` URL, the self-hosted `remote_url`, hosted +Runtime bootstrap and the deployment's read-only `core_url` from it; the deployment +API does not accept a Core address, and no deployment row stores one. Bootstrap +never uses request Host or caller-supplied placement fields. Enrollment names the +Core address the node uses; Core refuses one that is not the public URL (409, +token unconsumed) and records it. After the public URL changes, a node receives no +new sandboxes until re-added. This does +not widen sandbox network policies or change credential admission. + +The distribution build sets umask 022 for non-root-readable payloads; installation +credentials and state retain their explicit private permissions. +For a system node installation, capture the trusted bootstrap bytes before +dropping to the service account. Pass those bytes through the fork; the service +account writes its own retained generation helper. Never make the caller's private +download directory accessible or let root write into service-owned state to +work around bootstrap access. + +Installer progress describes the operation about to run. Do not imply fresh +health checks on a no-change repair. Keep terminal styling optional, honor +`NO_COLOR`, and preserve plain redirected logs. Summaries show credential file +locations, never their values. `install_display.py` owns shared terminal formatting; +`install_output.py` and `node_output.py` own their respective completion guidance. +Ship and checksum the display modules, including them in both the distributed node +bootstrap and retained helper. A node summary reports success only after Core +connection and provider readiness are confirmed. Service-user output stays plain +and passes through the existing terminal-control sanitizer. + +The Core/Web installer uses the launching account, including root, and a writable +installation directory. It never invokes sudo, switches accounts or changes host +Docker permissions. Check actual platform, Docker and directory prerequisites; +root alone is not a reason to refuse installation. Native Core retains its +systemd user-manager and lingering prerequisites for that same account. + +The first installer targets a trusted Linux amd64 Docker host. It installs a +private dedicated PostgreSQL service and separate Core and console services in +Compose by default, with zero execution nodes. The default requires neither KVM +nor systemd user services, imports no Runtime image, mounts neither the Docker +socket nor host devices into Core, and never adds its own host as a node. +`--sandbox docker|microsandbox|e2b|none` (default `microsandbox`; `none` and nothing +else with `--web-only`; `docker` prints its weaker isolation and needs a y/N +confirmation or `--accept-docker-risks` before anything is created) is a one-time +install action: once the services are healthy, the +installer POSTs `/core/v1/sandbox/deployment` as Web's setup would, and never on a +repair. It is not written to `config.json`; PostgreSQL owns the +selection. E2B needs a non-loopback HTTPS `public_url`, `--e2b-api-key-file` and +`--e2b-template`, and is refused before anything is installed. A loopback Docker or +microsandbox selection is saved, but no node can serve it until `public_url` is +guest-reachable HTTPS. `--sandbox-provider` and `--provider` are retired and fail. +The thin distribution supplies native Core binaries. Provider helpers, the node +agent, Runtime image and pinned msb runtime/firmware are separate, same-revision +assets. Web serves local offline artifacts or redirects the node to the verified +manifest's versioned HTTPS release. `/console/config` reports providers with a +complete set of local files or declared release downloads. Core packaging is independent of provider: +`--native-core` runs Core as a systemd user service, with PostgreSQL/Web in Compose +and a private loopback database port. Native Core needs no KVM or node assets. Core receives no +Docker socket or node identity mount in either mode. The ordinary standalone node +service owns its provider processes outside the Core container. Its `KillMode=process` +preserves resident microVM/helper processes across a node-service restart. User KVM +access and the Linux runtime libraries are prerequisites for microsandbox. The +installer runs as root and prepares the host: it creates or adopts the +`oac-node` system user, adds it to the `docker` or `kvm` device group (no other +group), and installs one root-owned system service per installation that runs the +same node program with `User=oac-node`. Sudo mode serves one Core per host, +because its nodes share that account. Docker group membership makes that user, +and so the node, root-equivalent on the host; that is inherent to Docker sandboxes, +not a least-privilege boundary. Microsandbox needs only `kvm`. Files the service +user owns are read, written and deleted only with its credentials, never by root, +in a child that starts its own session with /dev/null as input, so nothing it runs +can reach the administrator's terminal. That child also joins a new session +keyring and dies with its parent, and root shows its output only as plain text +(terminal controls become `?`). The installer turns SIGINT, SIGHUP and SIGTERM +into stopping that child and what it started, which would otherwise outlive a +closed terminal. Root never runs a file that user can write, +opens a URL it wrote, or follows a link in its home. Sudo mode +never installs Docker, KVM or packages, never changes device permissions, refuses +SELinux-enforcing hosts and a token in the environment, and changes nothing when a +check fails. `--uninstall` removes a node only after Core rejects its credential, +never touches sandboxes, volumes or images (it keeps the Runtime image and the +microsandbox store), deletes the account only when the installer created it and no +node remains, and otherwise removes only the groups it added. Do not add any other launcher, scheduler or +recovery path. Node services restart after failures without a start limit, so a node +outlasts a Core outage, and stop restarting when the node program exits 78 +because Core answered 401 to its credential (a removed or retired node). +The basic API image and binary builds remain independent artifacts. +The standalone API release and Core distribution both include the nodes operator +reference (`HOSTED-SANDBOX-MANAGER.md`) at the relative path used by their packaged README. Include the +guide in each artifact checksum list so extracted documentation matches its build. +The node asset includes the `oac-node` binary. The installer's Docker and +microsandbox selections use Web's Standard size from +`apps/web/src/features/sandbox/standard-sizes.json`, which the distribution build +copies into the bundle; keep no second copy of those values. An existing database +selection is never overwritten by installer defaults. Node configuration and identity live under +`~/.oac/nodes//` in the node account's home (`/var/lib/oac-node` +in sudo mode); microsandbox uses its separate short private +Runtime home. Zero-node installs create no node identity state but retain the paired +Core key for first setup. + +Node installation refuses pre-rename resources for the same installation ID: old +records, node directories, units and Docker networks. It never adopts those +resources or removes another installation. Remove the node on its old Core, then +uninstall with the previous release before adding it again. The machine +configuration route rejects the retired product-named node header with +`400 invalid_request`; only +`X-OAC-Node-ID` identifies a retained node credential. + +User-managed hosts use the native daemon installer on Linux, macOS and Windows. +It does not select a supplier or create compute resources. The retired Docker +self-hosted installer/launcher and console payload are not retained as fallback. +Existing environments, credentials, workspaces and native history are never +automatically deleted or adopted by a new installation. + +One Runtime image contains the existing daemon, shared helpers and three native +harness packages. Their differences remain in the adapters. Core keeps exclusive +ownership of Session allocation, initialization, cancellation, snapshots and +cleanup. The node installer imports the Runtime image and prepares running +conditions; neither installer creates an execution Session or supplies a model +credential. Applications use the +existing write-only model execution extension, with the installation's persistent +credential encryption key. Provider identity/backend namespace and native history +must not change on a repeated install. + +Installation state and secrets live in a private directory under `~/.oac/` by +default. No credential enters build arguments, image layers, browser bundles or +diagnostic output. Compose configuration is confidential. The generated database, +Projects and their issued keys, provider identity and encryption +key survive reruns; automatic +revision replacement and provider migration are outside this initial installer. +Reruns also refuse enabling or disabling a sandbox provider on an existing +installation, including adding one to the default zero-node installation. +Stopping control-plane services does not stop all Provider resources; use Core's +existing release operations for full cleanup. No native restart promise covers +host reboot or a lost running microVM. Do not delete data or issue broad +container/volume pruning as recovery. + +`make check-distribution` covers the production proxy, installation rules and +release metadata. Real bundle validation covers default/provider selection, +component modes, existing Web connection, public native execution and restart +retention. Diagnostics report observed service health, not fabricated model or +complete environment readiness. Runtime observations are Core-owned; do not add +a duplicate monitoring/lifecycle framework to installation or the public landing. + +## Native daemon and Harness installation + +`oac-daemon install` owns interactive selection and CLI-only installation through +one options/validation path. No installation-options file input is supported. +Persisted installation state and explicitly supplied credential/tool-variable +files serve runtime operation, not a second installer configuration language. +The release bundles pinned Node/npm, native Harnesses and required adapter assets; +registration lives in CLI and native activation/readiness in each adapter's optional +`agent.Installation` descriptor. Core never selects native paths or OS-specific +installation steps. See [native installation](self-hosted-native.md). + +Self-hosted onboarding extends authenticated Session creation/detail responses with +`x_agents_core.installation`; Web displays the same Core-produced commands. Lists +and durable event journals never retain installation authorizations. The command +uses a 30-minute, Environment- and build-scoped grant to claim one connect-only +credential. The installer persists its generated secret before claiming it; retries +must prove that same secret. Reserve the Environment UUID as the onboarding key ID. +Existing, rotated or revoked credentials are never replaced by onboarding. Machine +bootstrap routes use this grant, not an Environment ID as authentication. Public +artifact routes contain no credentials. Native bundles must match the Core source +revision and Runtime wire version. Core release qualification consumes the same +three-platform native CI artifacts and includes them in its distribution. +Bootstrap scripts own platform download/extraction only; installation, startup, +connection verification and Runtime execution remain common. Serialize background +PID inspection and publication so concurrent starts cannot create duplicate daemons. +An installed daemon discovers and registers only the adapter kinds named by its +verified installation manifest. The host PATH stays available to tools; its other +Harness executables and activation variables cannot extend that installation. +Direct `connect` rejects an installation manifest and directs the operator to +`start`; unmanaged image bootstrap remains available without that manifest. + +All mutations use the installation directory lock. Publish complete checksum-verified +components from staging, then commit configuration after native readiness passes. +Re-running with the same connection settings adds selected Harnesses and validates +existing contents. Never overwrite, upgrade, auto-repair or migrate installed +components. Missing, modified, wrong-platform or incompatible content is an explicit +error. A partial addition must preserve the old configuration and allow reuse of +complete components; it must not remove previous files or data. + +Installers run as the current user in writable directories. Native subprocess +diagnostics must not expose sensitive parameters or environment values. Adapter +readiness checks receive the installer cancellation context and must reap owned +processes before returning after interruption. Readiness, +authenticated connection and model configuration are separate reported facts. +Starting execution must not download or install Harnesses. Ordinary stop/reconnect +must preserve capability snapshots and native Session state. + +`scripts/build-native-installer.mjs` packages native inputs, validates pins/startup +and hashes every component file. It uses contained regular files and rejects +escaping links. Claude's dedicated frozen `pnpm deploy` export is reified with +the hoisted linker for this distribution before contained links are flattened; +the original standalone Claude archive contract remains unchanged. The native +installer workflow builds and tests on Linux, macOS and Windows, including actual +installation, addition/reuse, missing arguments and unsupported Windows MiniMax. +Native CI startup checks do not replace real model execution evidence or claim +manual Windows acceptance. Heavy builds belong on remote servers or CI. diff --git a/docs/runtime-protocol.md b/docs/runtime-protocol.md index df3cc515d..c25ca0ae5 100644 --- a/docs/runtime-protocol.md +++ b/docs/runtime-protocol.md @@ -56,6 +56,126 @@ leases and evicts old Run/interaction routes; the new connection does not inheri them. A valid credential and connection are not authority to choose another Session or Environment binding. +### Executor and Turn lifetimes + +This section owns the separation of execution and resource lifetimes. + +| Lifetime | Owner | Ends when | +| --- | --- | --- | +| Environment allocation | Resource management (Sandbox Provider) | Explicit reclamation, coordinated with Runtime execution | +| Runtime connection | Runtime transport | Disconnection or replacement by a newer connection | +| Installed capability snapshot | Runtime | Its Environment is reclaimed; never on Executor close | +| Session Executor | Runtime | `Executor.Close` on idle expiry, shutdown or confirmed invalidation | +| Turn | Adapter `Turn`, tracked by Runtime | `AwaitSettlement` confirms settlement | + +A Session owns one reusable Executor in its connected Runtime. A Turn owns one +input execution, its output stream and its cancellation. `agent.ExecutorFactory` +prepares the fixed configuration; `Executor.StartTurn` creates a new `agent.Turn` +without replacing healthy native resources. Normal completion settles only the +Turn. `Executor.Close` releases native resources on idle expiry, environment +shutdown or confirmed invalidation. Core does not keep a second Executor cache. +The same lifecycle applies after managed or user-managed environments connect, +and to the qualified no-environment profiles. Resource management owns machine +selection, allocation and Environment creation/reclamation. Closing an Executor +does not release the Environment allocation or delete its workspace. Environment +reclamation explicitly coordinates with Runtime execution. Connection, installed +capability snapshot, Session Executor and Turn retain separate lifetimes. + +The Runtime binds its Executor record to Session, Environment, connection and +immutable execution configuration. Resume identity and prior-Turn recovery flags +are continuity assertions, not configuration changes. A supplied native identity +must match the retained owner; exact-history recovery never starts a new root +when existing history is required. A configuration conflict is an error, not a +hot switch. Lost connections retire their owners and handles. Old timers, output +and cancellation cannot affect replacements. + +Each Turn receives a fresh wrapper, output channel and receipt state. Optional +steering, functions, permissions and user-choice interfaces belong to that fixed +Turn. Native callbacks capture the originating Turn before asynchronous work; +late events cannot be assigned to whichever Turn happens to be active. Native +processes, query/transport connections, fixed capability configuration and native +Session identity belong to the Executor. Do not reset completed `sync.Once` +values or repurpose an old Turn object. + +`StartTurn` returning nil guarantees that no native input was submitted and the +output channel was not retained. The Runtime then closes that channel. Once input +may have been submitted, return a non-nil Turn even with an error: the Turn owns +exactly-once output closure and remains tracked until settlement. Unknown input +is never replayed. A definite `executor_unavailable` Start rejection permits one +common recovery attempt only after the previous Executor has been closed and no +input was submitted. Recheck the same physical peer and current authorization. + +`Turn.Cancel` targets only that Turn and does not close a healthy Executor. +`AwaitSettlement` applies after both natural completion and cancellation. Success +means output can no longer be written and the Turn's native events, input, +functions, interactions and child work have settled. Native completion or +cancellation confirmation is independent of resource retirement: closing a +transport cannot supply missing native terminal or operation receipts. +`Reusable=true` additionally +confirms that the native owner can accept the next Turn. `Reusable=false` requires +a reason and subsequent confirmed Executor close. An error means settlement is +unconfirmed; it cannot free ownership or capacity. Caller deadlines stop waiting, +not tracked cleanup. Retry the same cleanup target serially. Failed cleanup +blocks replacement and retains its resource slot. Executor Close confirms resource +retirement independently of the Turn outcome: an immutable Turn error must not +prevent closing the native transport and releasing resources once their work and +output have stopped. + +One output consumer starts before native Start, drains the bounded 64-frame +channel, and retains the terminal observation until Start publication, Turn +settlement and admitted operation receipts finish. Natural Done never calls +Cancel. Input, function and interaction admission close before settlement, and +operations already admitted hold their barrier through native receipts and +outbound acknowledgement. Send cancellation to the fixed Turn before waiting +for that barrier: a written input may need native interruption to produce its +receipt. Join native settlement, any required confirmed Executor close, output +drain and all admitted operations before an applied acknowledgement or reuse. +A failed Close may report failure while retaining the same Run and outstanding +operations for retry. Closing a caller wait cannot manufacture an applied input receipt. Only then forward Done or an applied cancellation +receipt. Commit native continuity and release the old Run admission before +publishing Done, since the receiver may immediately start another Turn. A late +terminal-send failure belongs to the old Run; it cannot invalidate a successor +that already owns the Executor. Connection shutdown owns transport-loss cleanup. +Preserve the ten-second settlement wait and separate five-second receipt +send budget; timeout is not proof of quiescence. The observed cancellation outcome +retains native identity, Usage and output without fabricating missing evidence. +Codex native Turn interruption can leave background terminals alive. Its adapter +uses the exact thread-owned terminal list and confirmed per-terminal termination +before settling cancellation, and repeats this cleanup during Executor close. +The bulk clean acknowledgement does not prove termination. Failed cleanup keeps +the native owner available for a later close attempt. + +Private preparation controls reserve a per-Turn admission, not a new Executor. +They carry an explicit Session identity and immutable configuration without model +input or Run identity. A fresh request returns a connection-local admission handle +and the owning Executor ID. Start supplies both identities and its actual Run ID +and ordered MessageInput. Per-admission revisions order status observations; +rejections describe control errors without inventing Run events. A reused healthy +Executor returns ready without native preparation. An admission release abandons +that admission; it does not close the Session's healthy idle Executor or cancel a +later Turn. Cancellation uses the exact Run identity. + +Preparations and Start execute outside the receive loop and Router lock. Admission +expires after five minutes; retries do not extend that deadline. Bound active +preparation and execution separately from idle retained resources, and count +closing or uncertain resources until cleanup succeeds. A definite +`execution_prepare` rejection with `preparation_capacity` leaves an unclaimed +queued Turn for the existing Worker scheduler to retry, including capacity held +by cleanup. Other errors and uncertain input delivery do not authorize replay. +At most 64 admission records are retained; old handles never consume replacement +admissions. Idle expiry +is a Runtime resource policy, not Core active-Turn concurrency. Shutdown tracks and +closes active and idle Executors, retains failed close targets, and allows a later +serialized retry. Ordinary disconnection closes the failed transport and keeps +the exact Router until shutdown succeeds. A wait timeout or failed cleanup cannot +authorize reconnect; process shutdown also keeps waiting rather than silently +discarding owned native resources. These records are connection-local, not durable +input replay. + +Read-only workspace preparations remain separate bounded filesystem operations; +they cannot start model work. Workspace operations retain exact binding and +settlement rules across Turn boundaries and Executor closure. + ## Envelope and identity Every data frame is one JSON @@ -102,6 +222,29 @@ Adapters preserve message/content order and explicitly reject unsupported conten Usage frames and the final usage snapshot replace earlier cumulative snapshots; do not add them. An absent measurement is unknown, not zero. +The wire version is `proto.Version` (see above). Initial, prepared and active input use +the same ordered MessageInput contract, replacing scalar prompts and attachments. +User-message boundaries and text/image order remain intact through Core and the +Runtime wire; adapters own native conversion and receipt aggregation. Text-only +transports reject image content rather than dropping it. Text is never trimmed: +engine profiles declare whether whitespace-only messages are qualified, and +unqualified harnesses (Claude SDK, MiniMax Code) reject them at admission rather than having +their input rewritten. Codex has a flat native +input list and uses blank-line separators between messages; this does not preserve +independent native user-message boundaries. +The independently packaged Claude bridge uses protocol 3 for a prepared Executor +and separately identified Turns; readiness rejects other protocol versions. +Image-bearing messages require a qualified profile/placement before persistence +and image support from the selected Runtime before native delivery. These checks +apply to that operation only; ordinary text retains offline queueing. Initial, +prepared and active paths use the same content and preserve receipt ownership. +The qualified public profile is inline PNG/JPEG on Codex/Claude `none` and +Core-managed Docker `openai_hosted`. Self-hosted/user-managed images, MiniMax +images and remote URLs remain explicit implementation gaps. Hosted images reuse +the existing preparation, active-input and workspace authority; they do not add +a downloader, a mount or a separate execution lifecycle. Core +does not fetch or transform media. See [message input coverage](../contracts/agents-api/message-input.md). + ## Preparation and execution order Hosted and self-hosted execution use the same preparation semantics. Placement @@ -142,6 +285,80 @@ by `released`, `expired`, or `failed`. A `rejected` control operation has an Read-only workspace preparation cannot start a Turn. Expiry does not remove the Runtime's obligation to settle cleanup. +Core and Runtime use common preparation, start, input-receipt, cancellation, +release and recovery semantics for Codex, Claude Code and MiniMax Code. Retain each +harness's native implementation behind its adapter. Core acts on verified capabilities and runtime +conditions; a capability declaration alone never grants public feature admission. +Extend existing interfaces during related functional work without introducing a +second framework or a broad rewrite. Codex, Claude Code and MiniMax Code have +qualified dedicated Docker profiles and historical Core-managed E2B evidence. +New user-managed enrollment requires separate real acceptance. Each harness has equal standing; +qualify each image/template with the common full-loop acceptance before deploying. +Additional engines remain separate work; V1 has no separate remote executor. +Later engines must satisfy the same applicable acceptance contract while keeping +their suitable native deployment layout. + +Workspace reads may request the private `workspace_read_only` preparation profile +through the existing preparation factory and verified `workspace_read_preparation` +capability. It accepts only the bound Environment and resource identity; execution +options, model/MCP credentials, native Session continuation and model/tool input are excluded. +The Codex adapter creates temporary local state, reuses its native connection and +directory transport, and rejects Start. Its child inherits only process/transport +essentials. The private native read mode excludes system, managed, user and project +execution configuration and plugin startup while preserving native security +requirements. Ordinary execution keeps its stable state and configuration. +Reject the legacy mixed managed-config profile for reads rather than discarding +its enforced constraints together with execution settings. +For this read profile, `released` is published only after local Close succeeds; +cleanup errors retain ownership and report `cleanup_unconfirmed`. A failed factory +must return its resource with the error if cleanup remains unconfirmed; wrappers +must preserve both values. Successful cleanup retries publish confirmed release, +and stale status snapshots cannot publish success. Failed terminal status delivery +does not retry cleanup; ownership remains until an explicit release or shutdown retry. +A release request, +HTTP disconnect or remote socket closure alone is not cleanup confirmation. This +profile does not establish remote mutation quiescence or public Files admission. + +Core directory reads reuse the Worker's Session scheduling reservation for idle +preparation and target the exact Run for active execution. Device selection uses +operation-specific capabilities; reading files never resolves model/MCP options +or creates a Turn. HTTP cancellation ends observation, not an admitted native read. +Keep the idle reservation through the bounded read and release attempt. Return +directory data only after confirmed Close; incomplete reads or uncertain cleanup +return unavailable without data. Release the Worker's scheduling reservation before +delivering the result so the caller can immediately request the next page. +Revoke the scoped read transport credential on +completion or failure. Runtime retains uncertain cleanup ownership and capacity; +this does not require a second durable Core owner registry or establish remote +write retirement. Public Files.list delegates workspace access to this reader; +the API owns tenant authorization, path validation and protocol pagination. Only +the reader's distinct `not_directory` result (a missing path, a regular file or an +unfollowed symlink) becomes an empty page; root, permission, transport and +uncertain failures keep their errors. Keep partial directory coverage and +unverified defaults explicit in the Files contract. + +Local inline file delivery uses the same authenticated daemon connection and exact +Environment/Session binding. All platforms use the daemon's Go implementation +for bounded file reads, directory listing, file creation and output export. +Files operations require no external helper executable or staging directory. +The Files API keeps workspace-relative paths and no-overwrite creation semantics; +it does not restrict native Harness tools' host permissions. + +Transfer a complete bounded body in acknowledged 64 KiB frames before invoking +the native file writer, verify the declared digest, and run no model for upload. +Keep the private 50 MiB transfer bound distinct from the official 5 MiB decoded +inline bound, which the API checks before any Runtime work. Files.create uses the +native writer's no-overwrite operation. Initial Session files retain their separate +atomic replacement behavior; do not change one caller's semantics for another. +The dedicated Runtime excludes execution while receiving or applying a write; +malformed, incomplete or expired transfers cannot reach the installer. Exact +commit/rejection receipts release the mutation owner. Missing or ambiguous +receipts retain uncertainty; observer cancellation and local process exit cannot +prove non-mutation. Before public admission, Core must durably reserve the write +under the Session lock and prevent successor mutation across restart until exact +settlement. Do not replay the request or introduce general replacement machinery. +Read-only operations retain their own authority and bounded ownership requirements. + ## What each acknowledgement proves | Observation | Proven fact | @@ -204,6 +421,70 @@ error permits reconnecting the channel; authentication/version rejection require operator correction. Cleanup of an Executor remains separate from the Sandbox Provider's confirmed reclamation of compute. +## Workspace operations and connection observations + +The private daemon `workspace_read` control targets an existing preparation handle +or its transferred active Run on the same authenticated device connection. Require +the exact frozen Environment identity; callers cannot supply sockets, credentials +or workspace roots. Shared routing uses the optional `agent.WorkspaceReader` +interface, without selecting an engine by name. +The same control accepts `operation: directory` through the optional +`agent.WorkspaceDirectoryLister`, with mutually exclusive byte/entry limits and +typed directory metadata. Directory responses carry at most 1024 single-component +UTF-8 names of at most 255 bytes, so escaped metadata stays below the existing +frame bound. These are private transport limits, not public Files parameters. +Byte and directory operations share target checks, correlation, capacity and +retained operation waits; neither creates a Run or selects an engine by name. +This control does not itself authorize a public Files endpoint or placement. + +The separate optional `agent.WorkspaceDirectoryLister` observes one workspace-relative +directory on the existing Prepared/Session owner; empty path selects its root. +Return single-component names, entry kind, regular-file byte size and explicit +truncation only after directory/metadata access and handle cleanup settle. Reuse +byte-read admission, uncertainty and caller-detach ownership where applicable. +Do not promise a snapshot, recursive traversal or public pagination through this +private interface. The Runtime invokes the retained directory helper against the +frozen local workspace. Keep the qualified helper outside writable paths, anchor +traversal to no-follow descriptors, bound enumeration and require a complete +validated result. Helper availability alone does not enable public Files admission. + +Bound encoded request payloads to 8 KiB and correlation IDs to 128 bytes before +admission. Do not echo oversized IDs; omit oversized trace metadata in replies. +Bound raw control results to 1 MiB within the existing 4 MiB transport frame; the +limits are local policies, not pinned public protocol limits. Successful reads require complete bytes/truncation and acknowledged native +close. Safe native rejections carry no bytes; interrupted or ambiguous reads remain +unknown and stop further reads on that owner. Local RPC reap never establishes file +settlement. Retain a dispatched read's original bounded waiter across observer +cancellation and resource transfer/release; stop new admission on resource closure. +The gateway bounds subscriptions and never retries or replays on reconnect. Duplicate +pending operation IDs cannot start another read; this control does not promise durable +idempotency or result recovery. Preparation/Run ownership, public path authorization, +public file authorization and native cleanup retain their separate requirements. + +The retained Rust directory, write and workspace-export helpers provide bounded +filesystem operations for the colocated Runtime. Their protected executable paths, +descriptor-relative traversal and exact workspace binding remain required. They +do not implement an executor transport or grant tenant authority. + +Connection observations use the existing execution lease and Session lock. A +separate `environment_connections` row retains the current generation and revision; +`environments.status` and its Session Environment-event snapshot commit together. +The producer serializes replacements, then numbers socket observations within each +generation. Duplicate or older revisions and superseded generations are inert. +Replacement retires a previously connected observation before publishing its new +registration. Registration alone creates no connected event. Event payloads contain +only public Environment identity/type/status and nullable error, never configuration, +credentials, registration IDs or revisions. Transport observations have no asserted +Turn association. `connected`/`disconnected` are distinct from native preparation +readiness; do not cast resource `expired` into the event vocabulary or emit `ready` +for a self-hosted connection. + +The existing Worker observes authenticated daemon peers for enrolled Environments, +using durable generation/revision fencing under its execution lease. On restart it +reconciles old connection observations before admitting new ones. A connection or +heartbeat does not prove native readiness or process quiescence. Failed or stale +observations cannot establish a current connection. + ## Contract verification Run `make check-runtime-contract` from the repository root. It exercises the diff --git a/docs/sandbox-provider.md b/docs/sandbox-provider.md index 1a96727a1..42396bad3 100644 --- a/docs/sandbox-provider.md +++ b/docs/sandbox-provider.md @@ -1,23 +1,72 @@ # Add a Sandbox Provider -Start at [`sandbox_provider.go`](../services/agents-api/internal/sandbox/sandbox_provider.go). -It defines the required `SandboxProvider` and optional `CheckpointProvider` -interfaces. Use the existing types; do not introduce another lifecycle protocol or -an execution path for a particular vendor. +A **Sandbox Provider** supplies the outer compute (an *Environment*) that a Core +Runtime daemon runs in, plus the bounded bootstrap that starts it. This guide is +the single start-to-finish path for adding one. The canonical interface is +[`SandboxProvider`](../services/agents-api/internal/sandbox/sandbox_provider.go). -Core owns durable Environment, allocation, placement and cleanup state. A Sandbox -Provider supplies compute and the bootstrap needed to start its Runtime (daemon). -Runtime prepares capabilities and workspaces through the common daemon protocol; -Harness adapters translate execution into native operations. The same Runtime -preparation and execution contract applies to a user-owned machine. That machine -has no Core-owned compute allocation to create or destroy. +## Before you start -Sandbox isolation belongs to the outer infrastructure. A daemon running directly -on a user's machine runs with that user's authority; installing the daemon does -not establish a filesystem, tool or network sandbox. Provider isolation and -network enforcement must be qualified independently of daemon connectivity. +| Term | Meaning | +| --- | --- | +| Environment | Durable execution place owned by Core; see [Environments](../contracts/agents-api/environments.md) | +| Allocation | One Core-owned compute lease for an Environment, identified by `Reference` | +| Runtime | The daemon inside the Environment; it prepares capabilities and executes Turns | +| Deployment | The single deployment-wide provider selection; see [Sandbox deployment](../contracts/agents-api/sandbox-deployment.md) | -## What to implement +Core owns durable Environment, allocation, placement and cleanup state. The +Provider supplies compute and bootstrap only. Runtime prepares capabilities and +workspaces through the common [daemon protocol](runtime-protocol.md); Harness +adapters translate execution. A user-owned machine uses the same Runtime +contract but has no Core-owned allocation to create or destroy. + +Use maintained provider SDKs behind thin adapters. Hosted deployments select one +deployment-wide Provider: E2B cloud, or Docker/microsandbox on +administrator-owned nodes. Providers never execute Core initialization commands; +initialization, daily execution and Files use the daemon's typed Runtime operations. + +Isolation belongs to the outer infrastructure. The daemon runs with its +launching user's authority and adds no filesystem, tool or network sandbox. +Qualify provider isolation and network enforcement independently of daemon +connectivity; see [Runtime and outer isolation](design-principles.md#runtime-and-outer-isolation). + +## Steps + +1. **Read the contract.** Implement the five required operations and any + optional interfaces in [Implement the interface](#implement-the-interface). +2. **Write the adapter package** under `services/agents-api/internal/sandbox/` + (native SDK calls, ownership checks, identity translation, private config). + Assert `var _ sandbox.SandboxProvider = (*YourAdapter)(nil)` at compile time. + Out-of-process helpers live in `services/agents-api/tools/-provider`. +3. **Register the kind** in every file listed in + [Register the provider kind](#register-the-provider-kind). Registration is + explicit construction, not an init-time plugin registry. +4. **Label owned resources** with `io.oac.*` labels, or `oac_*` metadata keys + where the vendor rejects dotted keys. See [Label naming](#label-naming). +5. **Run the contract suite** with `make check-sandbox-provider-contract`. See + [Validate the integration](#validate-the-integration). +6. **Run native acceptance** against real compute, then `make check`. Record + evidence and limits in [Sandbox deployment](../contracts/agents-api/sandbox-deployment.md). +7. **Document operator setup** next to the adapter, like the + [reference adapters](#reference-adapters). + +## Implement the interface + +`SandboxProvider` has **five required operations**: + +| Operation | Purpose | +| --- | --- | +| `Create` | Create compute for a `Reference` and run the bounded daemon bootstrap | +| `GetInfo` | Observe current compute without mutation | +| `Renew` | Extend a native lease, or observe when the backend has none | +| `Kill` | Reclaim the allocation's compute and retained resources | +| `RunCommand` | Bounded administrative bootstrap/diagnostic command | + +Use the existing types; do not introduce another lifecycle protocol or a +vendor-specific execution path. A backend without a native renewable lease +(Docker) still keeps service-owned hosted expiry and cleanup requirements. + +### Required and optional interfaces | Contract | Requirement | Responsibility | | --- | --- | --- | @@ -37,48 +86,7 @@ alternate route for Skills, Plugins, MCP setup, initial files, Session execution Files. Those use Runtime. Confidential command input travels in `Command.Stdin`, not argv or logs. Preserve its byte order, bounded output and actual exit status. -## Register an adapter - -Registration is explicit construction, not an init-time plugin registry: - -1. Put native SDK calls, ownership checks, identity translation and private - configuration in an adapter package under `services/agents-api/internal/sandbox`. - Assert `var _ sandbox.SandboxProvider = (*YourAdapter)(nil)` at compile time. -2. For a node-local adapter, add its typed construction and readiness probe in - [`sandbox/config`](../services/agents-api/internal/sandbox/config/config.go). - The existing `Built` result returns the provider, probe, installation identity, - backend fingerprint and specification digest; the factory also returns its - close function. Construction must not create an Environment. -3. For a direct adapter, register its construction in - [`managedSetup.provider`](../services/agents-api/cmd/server/managed_setup.go). - Node-local adapters are selected there through the existing node proxy. Keep - vendor-specific deployment validation and SDK setup at this boundary. -4. Preserve the existing `execution.RuntimeProvider` deployment binding: - `ProviderKind`, installation ID, backend fingerprint, generation, mode and - node ownership identify the selected backend. The database owns the selection; - the in-memory selection is not an alternate authority. Register optional - interfaces on direct adapters and their transport wrappers consistently. -5. Qualify the provider's operator configuration and resource limits in the - existing deployment specification/enrollment path. The shipped deployment - parser and persistence constraints explicitly enumerate supported backends; - update that registration surface when adding one. Do not bypass it with an - arbitrary provider string. This is deployment admission, not a new execution - workflow. Adding a new public capability requires its own protocol change. - -Today Docker and microsandbox use nodes; E2B is constructed directly. The node -proxy exposes checkpoint operations only for its registered checkpoint-capable -backend. A new node backend must register both construction and the corresponding -proxy capability; otherwise that capability is unavailable. Provider names belong -in this adapter/configuration wiring, not Session scheduling, capability -preparation or Turn execution. Common lifecycle code uses `CheckpointProvider` -to admit suspension, independently of a provider name. - -The backend fingerprint identifies a native resource namespace, not mutable -capacity. Core retains deployment generations so old owned allocations continue -to resolve to their original backend. This is resource ownership, not historical -binary compatibility. Do not repoint retained allocations at a replacement backend. - -## Identity and resource responsibility +### Identity and resource ownership `Reference` is the exact `(TenantID, EnvironmentID, AllocationID)` tuple. Core persists a fresh allocation ID **before** `Create`; it is not the Environment ID. @@ -113,7 +121,7 @@ name collision. Core releases durable ownership only after confirmed cleanup **and** settled creation. Executor release or Harness cancellation does not delete an Environment, its workspace or its Sandbox Provider allocation. -## Operation outcomes and retries +### Operation outcomes and retries All calls receive bounded contexts. Expiry or cancellation ends the caller's wait; it does not prove rollback, native stop, cleanup or absence. An adapter or @@ -143,7 +151,7 @@ and snapshot, not whichever instance currently has the same display name. See [the lifecycle implementation](../services/agents-api/internal/execution/runtime_compute.go) and its failure tests before advertising this capability. -## Four distinct readiness facts +### Four distinct readiness facts | Fact | Evidence | Does not establish | | --- | --- | --- | @@ -158,6 +166,59 @@ Runtime installs/reads local capabilities and freezes the result for the Session Reconnection can reuse that prepared content; a new Session takes a new snapshot. A provider must not implement a competing preparation path. +## Register the provider kind + +Provider kinds are enumerated explicitly. Add a new kind to each surface +below; none accepts an arbitrary provider string. + +| Surface | File | What it enumerates | +| --- | --- | --- | +| Node-local construction | [`sandbox/config/config.go`](../services/agents-api/internal/sandbox/config/config.go), [`specification.go`](../services/agents-api/internal/sandbox/config/specification.go) | Typed construction, readiness probe and node specification | +| Direct construction | [`cmd/server/managed_setup.go`](../services/agents-api/cmd/server/managed_setup.go) (`managedSetup.provider`) | Direct adapters (E2B) and node-proxied kinds (Docker, microsandbox) | +| Resource validation | [`sandbox/deployment.go`](../services/agents-api/internal/sandbox/deployment.go) (`Resources.Validate`) | Per-kind resource limits | +| Node proxy and identity | [`sandbox/node/proxy.go`](../services/agents-api/internal/sandbox/node/proxy.go), [`node/identity.go`](../services/agents-api/internal/sandbox/node/identity.go) | Node-capable kinds and their checkpoint capability | +| Deployment persistence | [`sandbox_deployment_mutations.go`](../services/agents-api/internal/store/sandbox_deployment_mutations.go), [`sandbox_deployment_setup.go`](../services/agents-api/internal/store/sandbox_deployment_setup.go), [`sandbox_specification.go`](../services/agents-api/internal/store/sandbox_specification.go), [`runtime_node_deployment.go`](../services/agents-api/internal/store/runtime_node_deployment.go) | Deployment admission and node-mode kinds | +| Database constraint | A new migration in [`migrations/`](../services/agents-api/migrations) | `provider_kind` `CHECK` constraints, last set in `000081_sandbox_generations.sql`; never edit a landed migration | +| Node installation | [`node_installation.go`](../services/core-console/node_installation.go) | Node artifacts per kind | +| Installer | [`deploy/install/sandbox_setup.py`](../deploy/install/sandbox_setup.py) (`CHOICES`, used by `install.py --sandbox`) | Installer backend choices | +| Distribution | [`scripts/core-distribution-manifest.py`](../scripts/core-distribution-manifest.py) | Bundled provider helper binaries | +| Clients and Web | [`sandbox-client.ts`](../packages/agents-client/src/sandbox-client.ts) (`SandboxProvider`), [`features/sandbox`](../apps/web/src/features/sandbox) (`SandboxSetupWizard.tsx`, `console-config.ts`, `node-enrollment.ts`), [`sandbox-labels.ts`](../apps/web/src/lib/sandbox-labels.ts) | Backend choices and labels shown to operators | + +Keep vendor-specific deployment validation and SDK setup at the construction +boundary. Construction must not create an Environment. For node-local adapters +the `Built` result returns the provider, probe, installation identity, backend +fingerprint and specification digest; the factory also returns its close function. + +Preserve the `execution.RuntimeProvider` deployment binding: `ProviderKind`, +installation ID, backend fingerprint, generation, mode and node ownership +identify the backend. The database owns the selection; the in-memory selection +is not an alternate authority. Register optional interfaces consistently on +direct adapters and their transport wrappers. A new public capability needs its +own protocol change. + +Today Docker and microsandbox use nodes; E2B is constructed directly. The node +proxy exposes checkpoint operations only for its registered checkpoint-capable +backend. A new node backend must register both construction and the corresponding +proxy capability; otherwise that capability is unavailable. Provider names belong +in this adapter/configuration wiring, not Session scheduling, capability +preparation or Turn execution. Common lifecycle code uses `CheckpointProvider` +to admit suspension, independently of a provider name. + +The backend fingerprint identifies a native resource namespace, not mutable +capacity. Core retains deployment generations so old owned allocations continue +to resolve to their original backend. This is resource ownership, not historical +binary compatibility. Do not repoint retained allocations at a replacement backend. + +## Label naming + +Every owned native resource carries ownership labels so mutations, reads and +cleanup can verify the `Reference` and installation. + +- Use the `io.oac.` prefix, for example `io.oac.installation` and `io.oac.tenant`. +- Where the vendor rejects dotted keys, use `oac_` metadata keys (E2B). +- Never accept older label names as a fallback; see + [Runtime names](../CONTRIBUTING.md#openagentcore-runtime-names). + ## Validate the integration Run `make check-sandbox-provider-contract` while developing. It runs the shared @@ -181,3 +242,17 @@ The opt-in Docker lifecycle/recovery tests use `AGENTS_RUNTIME_DOCKER_TEST_IMAGE SDK helper tests use `make check-e2b-provider` and `make check-microsandbox-provider`. Passing controlled contract tests is not a claim of live cloud or model acceptance. + +Mocked compute cannot prove that resources were reclaimed or that the outer +Environment provides isolation. + +## Reference adapters + +| Kind | Adapter | Helper | Operator guide | +| --- | --- | --- | --- | +| Docker (node) | [`sandbox/docker`](../services/agents-api/internal/sandbox/docker) | Node proxy in [`sandbox/node`](../services/agents-api/internal/sandbox/node) | [Hosted sandbox manager](../services/agents-api/HOSTED-SANDBOX-MANAGER.md) | +| microsandbox (node) | [`sandbox/microsandbox`](../services/agents-api/internal/sandbox/microsandbox) | [`tools/microsandbox-provider`](../services/agents-api/tools/microsandbox-provider) | [`deploy/microsandbox`](../services/agents-api/deploy/microsandbox/README.md) | +| E2B (direct) | [`sandbox/e2b`](../services/agents-api/internal/sandbox/e2b) | [`tools/e2b-provider`](../services/agents-api/tools/e2b-provider/README.md) | [`deploy/e2b`](../services/agents-api/deploy/e2b/README.md) | + +Provider selection and E2B setup are owned by +[Sandbox deployment](../contracts/agents-api/sandbox-deployment.md). diff --git a/packages/claude-sdk-adapter/README.md b/packages/claude-sdk-adapter/README.md index d193c9a9b..c0a1b08ce 100644 --- a/packages/claude-sdk-adapter/README.md +++ b/packages/claude-sdk-adapter/README.md @@ -231,7 +231,7 @@ failed readiness reports an unavailable descriptor with a rejecting factory. Runtime checks establish local readiness, not provider authentication. Installed daemons use `start` and their verified installation manifest for adapter selection and activation; ambient activation variables cannot extend that selection. See -[the native installation contract](../../CONTRIBUTING.md#native-daemon-and-harness-installation). +[the native installation contract](../../docs/maintainers.md#native-daemon-and-harness-installation). SDK state lives under `paths.ProfileDir(profile)/runtime/claude-sdk`, independently of the replaceable runtime bundle. Both the entrypoint and managed state root must diff --git a/services/agents-api/IMPLEMENTATION.md b/services/agents-api/IMPLEMENTATION.md new file mode 100644 index 000000000..485d09c97 --- /dev/null +++ b/services/agents-api/IMPLEMENTATION.md @@ -0,0 +1,1693 @@ +# Agents API implementation constraints + +These rules describe how the Core service (`services/agents-api`) currently +implements the public, administrator and machine contracts. They are contributor +constraints, not user documentation. Wire behavior and qualification evidence +stay in the linked [contracts](../../contracts/agents-api/README.md); Runtime +message semantics stay in the [Core–Runtime protocol](../../docs/runtime-protocol.md). + +## Public request handling + +Session creation requires initial input for `none`, and for streaming creation +outside `self_hosted`. Report inline agent protocol errors first, then check these +conditions before creation retry lookup or +resource resolution. The parser remains shared with subsequent message admission; +non-streaming hosted and self-hosted requests may omit input. Do not retain an +idle-none creation compatibility exception. Valid requests retain their documented +local idempotency behavior; clients may use the same request/key with stream=false +to recover a lost creation response. Session metadata updates require a supplied +metadata field, with null/empty clearing it. Validate an empty update before any +resource lookup, after authentication. + +List order parsing distinguishes omission from an explicit empty value. Lists and +single-resource routes ignore unknown query keys; a repeated supported list key +still rejects. The Environment Files list keeps its own path and cursor parsing but +uses the same unknown-key and duplicate-key rules, except that it still rejects +malformed query encoding (such as `%GG` or `;` separators) that the shared lists +drop. Reuse the shared parser and error serializer, preserving the observed Beta, Files and Skills error fields and +per-family limit bounds rather than applying one policy to every resource. Change +page bounds, cursor ownership or parent lookup order only with owned evidence for +that family. Record uncertain range/lookup behavior separately; do not reproduce +observed upstream server failures as compatibility behavior. See +`contracts/agents-api/list-query-semantics.md` for the bounded evidence. + +Every Agents API JSON route reads its body through the shared gate +(`readJSONObject`) before route decoding, validation or lookup. It requires a JSON +Content-Type, applies the route's body limit and rejects invalid UTF-8, malformed +JSON (including unpaired surrogate escapes), repeated keys and non-object roots +with the official messages; an empty body or null becomes `{}`. DELETE, multipart, +Core extension and internal routes keep their own readers. Member names match +exactly: decode request objects with `decodeInputObject`, or check +`inexactMember` before another decoder, so that encoding/json never matches +a case variant to a field. See +`contracts/agents-api/official-semantics-alignment.md#request-body-parsing--september-23`. +Report validation failures with official evidence through the typed field error, +which emits `invalid_request_error` with the observed param and message; keep +other local codes until their official fields are sampled. Every 409 has type +`conflict_error`. Session input conflicts and changed tool results also use code +`conflict_error`; documented Core-only conflicts, such as Idempotency-Key reuse, +sandbox administration and Environment input states, keep their local codes. Agent configuration +(saved create/update and the inline Session agent) uses one path-tracking +validator of the pinned shapes before its parsers and harness admission, which +keep their local codes; do not grow it into a JSON Schema engine. A malformed path +identifier must produce exactly the response of a well-formed missing one on that +route, including invalid bodies, queries and storage availability: resolve it to +the never-assigned maximum UUID and let the missing path run, or reject it +directly only where the lookup is the next check. Request-body references keep +their own errors. An `after` cursor that does not resolve inside its already +resolved parent, malformed ones included, returns that list family's observed +error: the missing-resource 404 on lookup lists, otherwise the typed store cursor +error. Foreign and missing cursors stay identical; see +`contracts/agents-api/list-query-semantics.md`. Reject U+0000 in metadata +explicitly with its `metadata.` param; other stored strings rely on the +PostgreSQL error mapping, so keep each request's writes in one transaction. See +`contracts/agents-api/official-semantics-alignment.md`. + +Serve requests on their canonical path and never redirect. `api.CanonicalPaths` +wraps the complete server handler in both configurations (the daemon ServeMux and +the API router alone), so every route group, middleware, authentication check and +handler sees one path. It starts from the request's own spelling, never a path +re-escaped from its decoded form: invalid bytes are percent-encoded, unreserved +escapes decoded, empty and dot segments resolved with ServeMux semantics, the +trailing slash kept, and other escapes such as `%2F` and `%5C` left encoded; +`Path` and `RawPath` are set consistently for chi and the ServeMux. Do not route +or authorize on a path outside that wrapper. On the Beta +group the constant OpenAI-Beta check (exactly one `agents=v1` value) runs before +authentication, and authentication still precedes every Beta handler, 404 and 405. +Every Agents API 401 has type `invalid_request_error`: null code on Beta routes; on Files +and Skills `invalid_api_key` only for a rejected Bearer credential. Agents API responses carry a fresh `X-Request-Id` (also in the log +context), `OpenAI-Version`, `OpenAI-Processing-Ms` and nosniff through the API +router's own middleware, not the shared log middleware. HEAD runs GET routes; +streaming, content-download, live directory, Runtime observation and Runtime +history routes register an explicit HEAD 405 instead. Every 405 of the API router, unknown methods included, has the JSON +body and lists the route's methods in `Allow`. + +## Product and execution service separation + +Agents API is the primary infrastructure deliverable. Parsar is an ordinary client +and example application; its feature backlog must not dictate the execution +service's public protocol or internal model. Agents API must build, deploy and run +without the Parsar product service, frontend or database. An optional Compose +deployment may install both services with one PostgreSQL instance, but separate +databases, credentials and migrations. The product uses Core exclusively; it has no native daemon or HTTP Agent fallback. + +### Design and compatibility requirements + +- The complete pinned `openai/openai-python` `beta/agents` protocol is the target, + including its referenced resources and types. Match paths, methods, headers, + field presence, nullability, discriminators, defaults, status transitions, + pagination, errors and streaming behavior. Engine limitations are implementation + gaps to solve, not grounds for narrowing or redefining the upstream contract. +- Preserve qualified native capability differences across harnesses. If a material + difference from the official API has no clear mapping, pause that part and ask + the user before changing its semantics. Explicit unsupported enablement rejects; + ordinary requests retain native behavior with any official default discrepancy + recorded in the coverage ledger. In particular, native programmatic tool calling + is not currently qualified as the official default-on behavior. Do not build + a separate executor or model loop to fabricate parity. This does not relax + authentication, isolation, credential protection or data consistency. +- Pin upstream source and SDK versions in `contracts/agents-api/upstream.json`. + Use official SDKs for clients and reuse upstream types or schemas where suitable. + SDK deserialization alone is not server validation or proof of compatibility: + test raw HTTP payloads and observable workflows as well. Synthetic data and mock + model responses may support controlled tests; live execution acceptance must + call a real model API through the service, daemon and harness. A real daemon + with a synthetic model does not constitute live model validation. Keep provider + credentials in private test configuration, outside source, logs and task records. + Record unspecified or unverified behavior explicitly; never invent official + semantics. When current documentation adds operations or fields absent from the + fixed baseline, queue a protocol upgrade instead of silently implementing a new + version. Owned-resource live probes can qualify status codes and wire details + left unspecified by the SDK; retain request evidence and distinguish observations + from guaranteed or fully covered behavior. Track partial + coverage in `contracts/agents-api/README.md` until the complete target is verified. + Reconcile current coverage summaries with merged routes and recorded acceptance; + distinguish accepted profiles, partial implementation, missing operations and + unverified semantics. Retain historical evidence with its original scope. Handler + counts are not compatibility percentages, and an active provider probe is not + deployment qualification. +- No legacy Agents API compatibility requirement takes precedence over this + design. Replace an unsuitable implementation instead of growing compatibility + branches. Preserve reusable, verified infrastructure rather than rewriting it + merely for new names or directories. Replacements may retire obsolete private + interfaces and history backfills in bounded PRs; this does not authorize deleting + product data or changing unrelated product behavior. +- Every concrete harness interaction goes through the common Runtime contract + and its adapter. Extend that contract minimally when a current operation cannot + be expressed; never put native capability logic or transport conversion into + Core handlers, storage or scheduling. Qualify public workflows through the same + shared chain; direct native probes establish feasibility only. +- Keep engine-specific types, process management and protocol translation inside + execution adapters. The public API and persistence/application core must not + interpret Parsar product payloads or depend on one engine's native item types. + Prefer maintained upstream SDKs and native execution protocols over a second + hand-written model/tool loop or a general-purpose compatibility framework. +- Verify an independent official-client workflow before a Parsar integration. + Parsar uses the same public contract as any other client, with no privileged + endpoint or direct execution-table access. An OpenAI endpoint is a possible + client target only where the requested capabilities and credentials support it. + +- Parsar owns users, workspaces, business authorization, Agent/Team definitions, + capabilities, product conversations, IM/sharing, approval decisions and billing. +- Agents API owns protocol saved Agents, execution sessions/turns, effective + configuration snapshots, dispatch/cancel, environments, vaults, raw usage, + pending interactions, protocol subagents and durable events. Protocol saved + Agents/vaults are execution resources, not Parsar marketplace or business roles. + Neither service reads the other's tables. Parsar uses a versioned client contract. +- A product conversation may map to several execution sessions. An execution + session is distinct from a live daemon socket, process or sandbox. Native engine + session identifiers belong to the execution service. +- Establish single-Agent execution, approval, cancellation, idempotent submission, + persisted recovery queries before Team orchestration. The upstream SSE stream + is live-only; recover through Session/Turn/Items reads. Any additional product + cursor replay must be documented as an extension, not upstream semantics. + Team definitions, management and orchestration belong to Parsar. Agents API + establishes single-Agent execution first; business Team loops are deferred. + This does not exclude upstream `multi_agent` configuration or subagent resources + from protocol coverage. Future business Team orchestration directly depends on + `openai/openai-agents-python` in Parsar. +- Daemon Skill/SP authoring remains a product operation: forward through a scoped + product callback with the original requester and workspace checks. A runtime + credential alone must not grant business write permissions. + +## Source Files and Artifacts + +Source Files belong to the execution project and have an independent lifecycle +from copied workspace files. Store immutable source metadata and PostgreSQL large +objects in the execution database with the pinned pgx driver. Upload validation, +metadata insertion and bytes commit atomically; deletion removes metadata and +unlinks the object in one transaction. Keep OIDs private and authorize every +metadata/content/delete lookup by tenant before opening a body. Stream bounded +chunks; never hold an entire general Files upload in memory or use filenames as +filesystem paths. Public source download admission is separate from internal byte +consumption: reject direct downloads of the supported `user_data` purpose after +tenant-scoped metadata lookup; initialization and workspace copies keep their +authorized Store read. Core Web must not offer that unavailable download action. +A read-only repeatable-read transaction preserves an admitted +source snapshot across concurrent deletion. Resolve that snapshot before entering +the existing Environment write path; deleting a source does not undo a completed +workspace copy. Bound request/transaction lifetimes, roll back incomplete bodies, +and never automatically retry ambiguous commits. Backups must include PostgreSQL +large objects; live deletion does not erase WAL or historical backups. Schema +rollback must not orphan existing source objects. Do not reuse product capability +tables or introduce a second destination writer for file_id. + +Session Artifacts are immutable published output copies, separate from live +workspace files and general source Files. A private output exporter must reuse +the authorized workspace path boundary and stream bounded bytes. Require complete +capture and confirmed helper/transport success before publication; valid archive +syntax alone is insufficient. The daemon owns and drains the exporter stdout pipe +separately from child reaping, so pull-transport backpressure cannot consume a +process-exit I/O deadline. After helper exit, bound each actual pipe read to one +second to reject inherited pipes that never close; reset that allowance after +consumer delays. Cancellation closes the owned reader and the dispatch consumer, +then waits for child settlement. An arbitrary blocked writer cannot be interrupted +by the exporter itself. Never extract an output archive into Core's +filesystem or hold the global execution lease through a large transfer. Keep +publication ordered with Turn completion, and authorize stored reads independently +of Environment availability so published outputs can survive its expiration. +Capture bytes into private PostgreSQL large objects without a Session admission +lock; after confirmed export, lock and recheck the live Turn before staging metadata. +Before capture, seal native input under that lock using the private capture marker. +Later messages reuse the existing Environment input reservation and await the next +Turn; the public Turn stays in progress until publication settles. Directory reads +during capture use an independent authorized read-only preparation, not the released +native Run; they do not request model credentials or mutate the workspace. Cancellation +retains the existing Turn/reservation semantics. Do not introduce a second queue. +Publish metadata in the same transaction as Turn completion. Failed/cancelled Turns +discard private objects, and Session deletion removes both private and published +copies. The exporter skips output symlinks by their `lstat` type without following, +opening or resolving them; hard links, other special files, device crossings and +concurrent changes still reject the capture. In that completion transaction, drop +staged paths whose sha256 equals the newest remaining published Artifact for the +path in the Session, so later Turns publish only new, changed or no-longer-published +paths and never modify existing Artifacts. Reuse the source-file snapshot reader +pattern and common content response; artifact deletion does not alter workspace +files. Hosted execution requires the +Runtime's bounded output-export capability and exact read-only preparation binding; +capability advertisement alone does not qualify an operator's deployment. +Exporter component checks do not establish public Artifact compatibility. + +## Dispatch and pending input + +Successful input commits send a coalesced hint to the existing Worker scheduler. +The scheduler keeps lease, capacity, cursor fairness and per-Session ownership +checks; a hint does not admit work itself. If capacity is occupied, preserve one +rescan for completion without turning failed preparation into a busy retry loop. +HTTP readiness waits and active input delivery subscribe before reading relevant +state and wake after committed promotion or input. They recheck storage after each +hint. Polling remains the fallback for external writers, expiry and lost hints; +notifications contain no execution authority and no durable input data. + +Core readiness, Start acknowledgement and input-to-first-text logs use a single +process monotonic clock. Start acknowledgement confirms adapter ownership, not +model input consumption. +Runtime logs identify Executor creation, reuse, idle and close independently of +Turn completion. Do not call these durations model-only latency or subtract clocks +from different machines. Native process creation and same-owner successive Turns, +real provider results and same-condition timings must substantiate reuse claims. + +The Dispatcher prepares pending Environment input only on its exact enrolled or +managed device. Preserve the same physical peer and preparation handle through +readiness, atomic promotion/claim and the first non-replay Start. Derive workspace +and Environment identity from Store ownership, never caller-selected private paths. +Initial prompt/cursor come from the reserved batch; later messages use ordinary +steering. Never hold a database lock during native preparation. + +Observe the original pending deadline, cancellation, deletion and peer loss while +waiting for readiness. Preparation failure leaves pending input and its deadline +intact unless storage has settled it; it creates no failed Turn or input history. +During Start, consume preparation controls alongside the ordinary Run stream so a +control-only rejection or pending-start cancellation can settle promptly. Reuse +ordinary journal, receipt and completion/native-history persistence. Once cancellation +is sent, preparation errors/closure cannot replace its receipt or timeout path. +Pending-start cancellation uses the adapter's observed outcome after daemon handoff +and output forwarding. Missing or unconfirmed outcomes still fail conservatively; +preparation control errors cannot substitute for the cancellation receipt. +Do not fabricate an empty cancellation outcome or infer native quiescence. +The connection owner spans preparation and the transferred Run without a reservation-derived Run +deadline; every exit releases it. + +The existing Worker scans pending inputs using the same bounded scheduling slots, +Session locks, durable deadlines and engine capability checks. Keep the one-second +Environment-input scan cadence and at most 100 candidates per scan. At EOF after a +nonempty cursor, refill the first page once in the same scan; an empty queue must +not spin. Advance the cursor before readiness checks so an unavailable Runtime +cannot starve later candidates. Preserve the configured execution concurrency (default four) and alternation +between ordinary Turns and Environment inputs. A self-hosted Session +waits for its dedicated enrolled device; it cannot select an arbitrary same-tenant +device or migrate an existing binding. Preparation failure can retry while still +pending without extending the deadline. Preparation retries share this scan cadence; +execution concurrency bounds simultaneous work, not attempt frequency. Managed-provider lifecycle +polling retains its separate five-second interval. Unknown promotion results or +errors after admission retain the existing no-replay settlement rules. + +`OAC_PUBLIC_URL` enables the private gateway; Core derives the public +`remote_url` from it. `OAC_HARNESSES` explicitly adds deployment-supported +engines to the default engine and configured managed profiles; advertising a +heartbeat alone does not enable an engine. The three native profiles share enrollment +at `/workspace`. Their new user-managed public chain requires fixed-client/raw HTTP, +real-model, recovery, cancellation and credential-lifecycle acceptance separately +from prior Docker or retired remote-executor evidence. + +## Current implementation constraints + +The constraints below describe existing code, not requirements to preserve legacy +design. The [protocol assessment](../../contracts/agents-api/README.md#implementation-direction) +identifies replacements and gaps. Update these rules when their implementation is +replaced; do not carry obsolete compatibility code forward to satisfy this section. + +### Gateway, Session deletion and schema ownership + +- `internal/agentdaemon/gateway` is the shared daemon connection implementation. + Its persistence interfaces use `internal/agentdaemon/device`, never product Store + types. Shared protocol frames and validators live in `internal/agentdaemon/proto`. +- Session deletion uses a durable `sessions.deleted_at` marker. Public deletion + accepts only a durably idle or failed Session without required actions: no + queued, in-progress or waiting root Turn and no pending input reservation, the + same settlement rule as the creation stream. Subagent child Turns and pending + Environment file writes are not checked, as before this rule; their official + behavior is unobserved. Take that decision and commit the marker + under the tenant Session lock that orders Turn and input admission, so either + admission commits first and deletion conflicts, or admission observes the + deletion. A busy Session returns 409 `conflict_error` with the observed official + message and nothing changes: no cancellation, marker, event or cleanup. Callers + cancel first (`agent.session.input.cancel`), wait until the Session is idle and + delete it. Core admits a Turn synchronously, so it also conflicts right after an + `events.create` 202, where the official service was observed to return 200. + Deleting a provisioning hosted Session with reserved input used to release its + sandbox node placement at once; it now conflicts, and the placement counts + toward node capacity until the input is admitted or its five-minute deadline + expires. A later allowed deletion releases an unallocated placement. + The owner's repeated deletion returns the same 200 confirmation without writing; + foreign, missing and malformed identifiers keep the byte-identical 404. Public + reads, metadata changes, event streams and input admission exclude deleted + Sessions; admission checks visibility under that lock before retry lookup. + Creation keys remain reserved and cannot resurrect deleted Sessions; reuse of a + deleted creation identity returns 409. Existing streams close when removal is + observed without a fabricated deletion event; overlapping stream timing remains + unverified. Earlier releases also deleted busy Sessions after requesting + cancellation, so upgraded databases can hold markers with hidden work. + Internal Turn/receipt/finalization and restart reconciliation retain access so + that work settles under the existing execution lease; queued work cannot be + claimed, and Runtime cleanup still cancels pending work. Confirmation does not + guarantee native quiescence. Never revoke a shared device, + remove a saved Agent or touch product data as part of Session deletion. Physical + SQL/native history cleanup remains a separate required implementation gap; these + records are retained, not claimed purged, and purging may end repeat idempotency. + Do not deploy a pre-deletion service + against a database with deletion markers; migration rollback refuses to remove + the column while deleted records exist, preventing public resurrection. +- `services/agents-api` owns its SQL schema, sqlc queries and embedded goose + migrations. `OAC_DATABASE_URL` is required; never fall back to the product + database URL. It stores tenant-scoped Sessions with a stable engine and durable + creation retry identity. + +### Agents, Vaults and credentials + +- Reusable Agents have their own tenant-scoped `agents` records, independent of + Session snapshots, engine bindings and product Agent definitions. The Store + persists caller-validated non-secret configuration and metadata without applying + harness capability restrictions or model defaults. Resource identity and equal + initial creation/update timestamps come from the persistence boundary. The + create primitive creates a fresh resource; public retry conformance remains + unverified. Internal storage admission is 512 KiB for configuration and 64 KiB + for metadata, not a claim about upstream limits. +- Vaults have their own tenant-scoped records in the execution database. Initial + `POST /v1/vaults` and `GET /v1/vaults/{vault_id}` operations persist and read the + resource without creating Sessions or contacting an engine. Omitted name is + null; a supplied non-null string is trimmed and limited to 1–256 UTF-8 bytes. + Omitted/null metadata becomes an empty object. Reuse the 64 KiB encoded metadata + storage bound, without applying Session-specific pair/character limits. This is + a local bound, not hosted parity. `GET /v1/vaults` lists the authenticated project's + records using creation-time/ID keysets, default descending order and a default + limit of 20 clamped to 1–100. Other resource limit policies are unchanged. + Status accepts `active`/`archived` as a scalar or SDK `status[]` array, with both + included by default. Private stored classification defaults existing/new rows + to active; it is never exposed in the Vault response. Listing reads no Credentials + and needs no encryption key or execution service connection. A scalar status + combined with `status[]` filters by their union; a repeated scalar parameter is + rejected. Exact hosted errors, equal-time + ordering and changes between pages remain unverified. Private archived fixtures + prove filtering only: there is no public archive writer, archive timestamp or + inferred delete-to-archive behavior. Retrieval, Session binding and dispatch retain + their existing rules. Archive/revocation lifecycle remains a separate gap; + do not introduce product roles or speculative lifecycle fields. Migration rollback + refuses to discard classification while archived rows exist. +- Vault `DELETE /v1/vaults/{vault_id}` removes the project-owned parent and all + Credentials through the existing foreign-key cascade in one SQL mutation. Do not + loop through child deletions, decrypt secrets, require the storage key or call + providers. Deletion applies to both stored classifications. Local missing/repeated + deletion returns not-found; subsequent parent/child reads and new attachments + cannot use the removed resources. Preserve Session snapshots, frozen choices, + history and recorded retries. Later secret lookups fail without selecting another + attached Vault or anonymous MCP. Already-resolved tokens and running Sessions + are not revoked. Exact hosted archive, visibility, overlapping-mutation and error + semantics remain unverified; row removal does not prove physical storage erasure. +- Static-bearer and OAuth Credentials are children of tenant-owned Vaults in the execution + database. Creation admits the owner in the same SQL statement as the insert; + retrieval joins the owning Vault and selects public metadata only. Public reads do not decrypt or return a token. OAuth replacement authenticates + the stored grant before applying a partial update. Encrypt before passing secret values to + SQL, using the execution service's separately configured random 32-byte key and + the standard library's random-nonce AES-GCM. The versioned authenticated binding + includes tenant, Vault, Credential, authentication purpose and exact destination. + Never reuse product master-key conventions or daemon transport encryption for + this storage boundary. Missing key configuration disables credential writes; + malformed explicit configuration fails startup. See + [`services/agents-api/credentials.md`](credentials.md) for + key persistence and current limits. Storage-key rotation remains + separate work; resource creation never contacts the destination. +- `GET /v1/vaults/{vault_id}/credentials` lists safe metadata only, with both + project and Vault ownership enforced on the parent, cursor and row query. An + inaccessible parent returns not-found, even when the collection would be empty. + Reuse the Vault status/limit parser and Credential metadata mapping. SQL must + never select ciphertext for listing; no encryption key or execution is needed. + Credential status is a separate private active/archived classification, defaulting + historical/new records to active and never derived from the parent Vault's status. + Synthetic archived fixtures prove filtering only. There is no public archive writer, + timestamp or delete-to-archive inference; existing create/retrieve/token replacement, + Session bindings and dispatch keep their rules. Migration rollback refuses to lose + archived classification. Archive lifecycle and hosted query/concurrency + semantics remain gaps. +- For static auth, Credential `POST /v1/vaults/{vault_id}/credentials/{credential_id}` + replaces only the token and update time. Require `auth.type=static_bearer` and a + string `auth.token`, preserving opaque bytes; reject extra mutation fields before + writing. Reuse safe metadata for the immutable encryption binding, then scope the + atomic SQL mutation independently by tenant, Vault, Credential, static auth type + and exact destination. Never decrypt the previous token or send plaintext to SQL. + Missing encryption configuration or a failed write preserves the old row. Return + the existing safe metadata projection; identity, name, destination, creation time + and Session snapshots stay unchanged. Subsequent dispatch reads use the committed + replacement through existing scoped lookup; already-resolved requests may retain + the old token. This is not storage-key rotation, in-flight revocation or hot reload. + Exact hosted concurrent-update/retry/timestamp semantics remain gaps. +- OAuth grant ownership stays in Core. The application performs authorization and + provider revocation; do not add public login/callback/refresh/revoke routes. + Store access/refresh/client secrets together under existing authenticated tenant, + Vault, Credential, auth-type and destination encryption. Authenticate refresh + metadata against its encrypted copy before using an endpoint or grant. Read/list + queries still select safe metadata only. Shared MCP selection admits both auth + types and freezes one identity without changing native adapter contracts. + At dispatch, a known-expired grant is refreshed through the declared endpoint + auth method, with stored scope/resource, then persisted before returning access. + Serialize refresh and replacement with the same PostgreSQL Credential row lock; + deletion and Vault cascade cannot be undone by a stale refresh. Network exchanges + are bounded and fail closed; never return provider error bodies or claim an + uncertain grant exchange was committed. No background scheduler, 401 retry, + hot replacement, output repair or harness-specific OAuth path is introduced. + Refresh uses verified HTTPS, rejects redirects, and checks resolved addresses + before dialing them. Private issuer origins need explicit operator configuration + in `OAC_OAUTH_TRUSTED_ORIGINS`; tenants cannot relax that boundary and TLS + verification remains mandatory. Keycloak is acceptance infrastructure only. + Preserve the pinned update omission/null and immutable-field rules described in + [OAuth credentials](oauth-credentials.md); record unspecified + hosted semantics. Native processes receive only access tokens. Provider revocation, + withdrawal of already-dispatched tokens and Session cancellation remain distinct. +- Credential `DELETE /v1/vaults/{vault_id}/credentials/{credential_id}` removes one + owned row, including ciphertext, with tenant/Vault/ID checked in the same SQL + mutation. It needs no encryption key, secret read or network call. Local reads, + updates, listings and subsequent dispatch lookups cannot use that ID; missing + and repeated deletion return not-found. Preserve frozen Session choices, retry + identities and history without fallback to another credential or anonymous MCP. + A token already read before deletion may remain in a dispatched request. Deletion + does not revoke provider tokens, cancel Sessions or prove physical erasure from + native history, WAL or backups. Do not infer a delete-to-archive mapping; exact + hosted archive, post-delete visibility and repeat/error semantics remain unverified. +- Public reusable Agent create/retrieve uses `/v1/agents` and the same authenticated + tenant/Beta-header boundary as Sessions. The resource envelope owns identity, + timestamps and metadata, separately from saved configuration and Session state. + Resolve known defaults and validate supported schema before writing. Preserve + model/name/instructions verbatim, nullable fields and structured JSON numbers. + Stored reasoning/service tiers, enabled multi-agent settings, JSON Schema output, + enabled web-search modes and deferred/tool-search/programmatic tools do not imply + execution support. Reuse function wire validation, keeping Session execution + restrictions separate. Model-derived reasoning effort is unresolved when omitted; + do not infer it from the selected harness. Omitted/null service tier currently + uses `auto`; complete upstream default/error/retry conformance and remaining MCP + variants remain gaps. Unknown/unsupported variants fail explicitly. No product + lookup is permitted. + +### Service-origin MCP + +- Service-origin public HTTP MCP uses the native harness client and tool loop on + trusted service-owned `environment:none` compute, with Codex or Claude SDK. + The V1 colocated `self_hosted` profile rejects it: user-owned compute cannot be + relabeled service-origin or receive its attached Vault credentials. Hosted + service-origin MCP also remains unsupported. Environment-origin Plugin MCP uses + its separate qualified local Runtime transport and isolation contract; do not + disable that path or infer optional-feature equality across engines. + Admission, device selection and preclaim still require the exact supported MCP + capabilities. Native declarations do not widen public placement authorization. + +- Keep accepted public MCP credential profiles separate from private adapter + capabilities. The execution service declares the verified public bearer profiles + centrally; a daemon capability alone cannot open a public profile. Reuse frozen + binding validation at admission and later input, then the same MCP capability and + placement checks at device selection, final preclaim and request construction, + before scoped decryption. Native configuration and token injection stay in the + adapters. Add bounded shared checks while changing the related execution path; + do not defer known duplication to a general engine or plugin framework. +- The shared MCP resolver preserves omitted/null `allowed_tools` as unrestricted + and an explicit empty list as deny-all. Saved HTTP transport output includes + `headers:{}`; the effective Session transport omits headers, matching the two + pinned resource types. Saved-Agent updates never change existing Session + snapshots; per-Session tools replace the whole field. The initial profile admits + HTTP(S), boolean `required` (default false), empty/null metadata and empty/null headers. + Static and OAuth bearer authentication require HTTPS and the attached-Vault rules below. + An omitted/null `connection_origin` on HTTP transport is stored as `service` + before the other checks, identical to an explicit declaration, as observed + officially. Inline authorization, URL userinfo/query/fragment, the `environment` + origin and stdio remain explicitly unsupported. +- Codex required MCP initialization additionally needs `mcp_http_required`, advertised + only for the verified native pin and checked during selection, final preclaim + and daemon dispatch/preparation. Preserve the boolean through typed messages, + native rendering and exact configuration preflight. Reuse native required-server + initialization during root thread creation and cold resume; send no native Turn + until it succeeds, and never replace a failed strict resume with a new thread. + Public work may already be accepted/queued/in progress while native initialization + waits. This is not a continuing health monitor or a new public readiness state; + exact hosted Session creation timing and initialization errors remain unverified. +- Send MCP declarations through typed daemon fields, independently of function + callbacks. In Codex, a non-nil declaration replaces operator MCP options; use the existing + native renderer and original tool names for `enabled_tools`, including `[]`. + Before thread creation/resume, query native `config/read` with the exact cwd and + reject additional servers or effective configuration differences. Disable native + plugins/apps and select file-only MCP credentials; reject existing credentials + in the private native home without deleting them or native history. Native + reserved labels are an adapter restriction, not a saved-resource schema rule. + Requests without this typed field keep the existing product behavior. The check + is a snapshot on trusted service compute, not an atomic barrier against concurrent + operator configuration changes. Discovery of a declared deny-all server can still + contact it; deny-all governs tool exposure. Reuse neutral tool observations and + the existing public `mcp_call` projection, never add a second MCP/model loop. +- The private Codex adapter's HTTPS MCP bearer authentication requires + `mcp_http_bearer_auth` and the existing MCP/environment capabilities, checked + before the factory. It is restricted to trusted service-side Codex with + `environment:none`. A transient + per-server `bearer_token` becomes a fresh daemon-owned `bearer_token_env_var` + reference for each native process. Put the exact secret only in that app-server + child's environment, after auxiliary launch probes; never in global environment, + arguments, configuration/history, public snapshots or logs. Preflight accepts + only the expected server/reference pairing and retains the existing rejection + of ambient credential sources. Service-side bearer variables must not enter + generated commands, files or public history/snapshots. Use the native HTTP client + with TLS verification. + This execution profile rejects empty values and bytes outside RFC 6750 b64token + syntax with generic errors; it never trims tokens or narrows opaque Credential + storage. Core-managed OAuth uses this same access-token path; native OAuth + login/refresh and hosted redirect/error equivalence remain separate work. + +### Session resources and model providers + +- Session `vault_ids` omission/null/empty means `[]`; nonempty attachments must all + belong to the authenticated tenant. Preserve caller order and the stored caller + `credential_id`. Saved Agents may store a nullable/nonempty credential reference + without authorizing its use. Session admission resolves an explicit credential + only inside attached Vaults for the exact declared URL, or selects the unique + matching static or OAuth credential when the ID is omitted/null. No match remains + anonymous. Selection errors use the observed official messages with a null param: + a reference without attachments, one outside the attached Vaults and one for + another URL are 400 `invalid_request_error`; several implicit matches are 409 + `conflict_error`. Missing, foreign-tenant, unattached and malformed references + share one message; echo caller values only within `internal/echotext`. Unknown + or foreign Vaults keep the same 404. Resolve after the input requirement and + before any Session, initial input or event write. Freeze safe bindings, + including anonymous decisions, in private Session configuration. Session + projections, never the stored configuration, show an implicitly selected ID in a + null/omitted public `credential_id`, also after deletion. At actual dispatch, recheck + tenant, attached Vault, selected ID, frozen auth type and exact URL before scoped + decryption. Metadata queries select no ciphertext; tokens enter only the existing + transient daemon request. Selected authentication requires `mcp_http_bearer_auth` + during device selection and the final preclaim check. Missing/wrong keys or + binding failures never fall back to anonymous execution. Exact URL equality, + immutable selection timing and hosted redirect semantics remain local decisions + or unverified gaps. No new MCP loop is permitted. +- Saved Agent execution defaults use separate input and safe-output Core extensions. + Keep model-provider bundles whole at every replacement boundary: endpoint, key, + protocol and limits must never be independently inherited. Ordinary Agent JSON + contains only safe provider fields and an output-only configured flag; encrypt the + complete bundle separately with tenant/Agent binding and a distinct purpose. + Commit configuration and secret changes together under the Agent row lock. Merge + only the extension's supplied members; omission preserves, provider null clears + its bundle, and extension null clears both defaults and secret. Model-only edits + require no key. Validate the merged harness/protocol/limits without reading keys. + Read safe defaults and ciphertext in one database snapshot for Session creation; + a complete Session override need not decrypt the inherited bundle. +- Session provider selection resolves explicit bundle, saved bundle, then the + deployment default of the resolved harness, and freezes it in the existing + encrypted Session-owned row. The deployment default is a runtime setting: one + complete bundle per harness in PostgreSQL, encrypted with its own + harness-bound purpose, managed only with the Core key through + `/core/v1/harnesses/{harness}/model-provider`, audited as a deployment-wide + write without the key, and never read back. It applies to `openai_hosted` and + operator-registered `none` Sessions, never to `self_hosted`, whose executor host + belongs to the application. Caller bundles apply to `openai_hosted` and + `self_hosted`. Hosted and self-hosted Sessions with no bundle are rejected at + creation with `model_provider_required`; legacy rows without a snapshot are + rejected at message admission and dispatch, never sent to a harness that would + fall back to a built-in endpoint. There is no operator options file: its + retirement fails startup. Sessions retain only the common frozen provider + bundle; historical native-option snapshots are unsupported. Keep runtime dispatch on the common adapter path and fail closed for + missing/decryption-failed snapshots. Agent edits/deletion, default changes, + restart and idle suspend/resume never resolve defaults again. Record caller + intent for every new hosted Session before resolving defaults; other inline + Sessions keep the resolved-request rule, whose hash leaves out the deployment + default. Provider keys enter retry hashes only as keyed fingerprints. Matching + retries return committed state without replay. No Turn-level overrides or provider catalog is + included. Public input/null semantics and examples live in + `contracts/agents-api/model-execution.md`. +- Deployment-default observations use a private UUID generated on every PUT, + including identical replacements. Read its ciphertext/revision together and freeze + the pair only at new Session creation; retries and historical Sessions never gain + or replace revision metadata. After a successful root terminal commit, one + independent pool operation has at most one second to update the matching current + revision. SQL verifies tenant, root Turn and committed outcome; only completed + Turns and fixed native provider failures with authoritative `engine_failed` qualify. + Never observe cancelled work, Core/runtime errors or input-policy classifications. + The metadata-only transaction sets server statement/lock timeouts within the + remaining client budget and issues one UPDATE, so a client timeout cannot leave + an indefinitely waiting statement. The update locks only the default and samples + DB receipt time after the lock. + Errors throttle for 30 seconds regardless of code; ordinary successes throttle + for 30 seconds, with one immediate recovery write after each accepted error. + An unchanged revision has at most three effective writes in any half-open + 30-second interval of nondecreasing DB time. Clock rollback may suppress ordinary + writes; private recovery state permits only one recovery without clamping time. + Observations never change `updated_at`, readiness or execution truth. They can be + lost or stay stale indefinitely without another eligible Turn; there is no queue, + retry, probe, history backfill or provider text in the safe fields. +- Session execution-configuration reads use a separate immutable safe projection, + written with provenance in the same creation transaction as Session resources. + Read no credential ciphertext and never recompute sources from current Agents + or operator defaults. Model/harness sources are independent; provider bundles + retain one source. The read is Core-key only, under `/core/v1`; `/v1` has no + execution-configuration read. Old Sessions expose persisted model/harness with + unknown sources and unavailable provider metadata, without backfill. Projection metadata does not alter retry + identity; retries cannot replace it. Keep this administrator query separate from + runtime observations and do not touch activity or wake sandboxes. The versioned + contract is `contracts/agents-api/execution-configuration.md`. +- Model communication uses `internal/modeltransport` in the Runtime. Core sends one + frozen confidential `model_provider` bundle, independent of engine and placement; + it must not manufacture native provider options or retain a historical-options + dispatch path. The adapter declares native protocols. Native matches connect + directly; mismatches use one private loopback endpoint owned by the existing + Session execution resource. Its credential is distinct from the upstream key. + Keep endpoint cleanup on final native teardown, including preparation failure, + rather than on individual Turn completion. No second Session manager is added. + CLIProxyAPI's pinned translator is an embedded conversion dependency, not a + gateway service. Requests, JSON responses and incremental SSE events share the + same conversion implementation for hosted and self-hosted execution. The + dependency owns protocol fields, tools, reasoning and usage conversion. Do not + add local field maps, parameter restoration or parallel compatibility rules. + Keep SDK format selection, HTTP/SSE framing, limits, cancellation and resource + cleanup here. Pin dependency versions and qualify upgrades with contract and + native engine tests; document upstream limitations instead of silently promising + lossless conversion. Preserve model identity, reject incomplete streams and + never redirect upstream credentials. See `contracts/agents-api/model-protocol-conversion.md` for coverage, + limits and dependency qualification. Go 1.26.8 is the pinned build toolchain. +- Provider input validation uses the adapter-owned rules in `internal/harnessconfig`. + Keep one internal registry for protocol and token-limit validation; Core owns + credential environment and endpoint admission policy. These rules are not a + public discovery API or Runtime registration descriptor. Operation qualification + and live readiness retain their existing owners. There is no startup + configuration read; Session frozen execution-configuration reads remain a + separate administrator read. + +### Agent updates, listing and tenancy + +- Public Agent updates use `POST /v1/agents/{agent_id}` with the same tenant/Beta + boundary and shared saved-field validation. Preserve omission separately from + null; only supplied fields replace saved values. Metadata is a separate whole-map + replacement, with null/empty clearing it. Lock the tenant-owned Agent row while + merging validated fields and enforcing the complete configuration bound, then + commit configuration, metadata and update timestamp together. Never write a stale + full snapshot over another update. No-field updates read without changing timestamps. + Except for the Core execution-default extension described above, supplied nested + fields replace the whole field and explicit null uses + existing saved defaults; exact hosted nested/null and no-op timestamp semantics + remain unverified. Model-derived reasoning defaults remain a separate gap. + Neither updates nor retries modify existing Session snapshots or execution state. +- Public Agent deletion uses `DELETE /v1/agents/{agent_id}` and one tenant-scoped + `DELETE RETURNING id` statement. Return the stored canonical ID with + `object=agent.deleted` and `deleted=true`; missing/repeated deletion locally + returns not found. It never deletes Sessions, history or runtime state and does + not cancel accepted execution. Recorded creation identities still recover the + accepted Session; new references cannot resolve an absent source. Historical + identities retain their documented limitation. Exact hosted errors and ordering + of overlapping source creation/deletion remain unverified; no tombstone or + successful result is fabricated for an absent resource. Reject body data; unknown + query keys are ignored. +- Reusable Agent listing uses the same tenant/Beta-header and response mapping as + create/retrieve. Page by `(created_at, id)` with a same-tenant saved-Agent cursor; + listing never resolves Sessions, product objects or execution capabilities. + Reuse shared list-query parsing and its per-family limit policy. Agent, Session, + Item, Subagent Item and Template lists treat limit 0 as 1 and larger limits as + 100; Vault and Credential lists also clamp negative limits; Turn, Subagent, + Subagent Turn and Artifact lists reject limits outside 1–100; Skill lists accept + 0–100, where 0 returns an empty page; Files accept 1–10000. Pages hold at most + 100 records (Files 10000) with accurate continuation. The local default is 20 + (Files 10000). Return the list envelope with data/has_more and first/last IDs + (null for empty pages). + Exact pinned upstream default/cap, empty-envelope and error semantics remain + unverified; do not present local limits or generic SDK parsing as full conformance. +- Session `agent_id` lookup uses the authenticated tenant. Copy the saved resource + ID and effective configuration into the immutable Session snapshot; saved metadata + does not become Session metadata. Never look up the source Agent when reading or + executing an existing Session. Omitted override fields inherit; supplied objects + and arrays replace whole fields before defaults and execution admission apply. + Reuse saved configuration validation and keep native capability restrictions at + Session admission. Reject unsupported effective options instead of dropping them; + an explicit supported replacement may make a saved configuration executable. + Inline Sessions use the same admission path. New saved-reference Sessions and + inline requests with Vault attachments or credential references record a separate + caller-intent hash: source ID (empty for inline), + supplied overrides (including field presence), environment/vaults, original metadata + and normalized initial input. Exclude response streaming and resolved source values. + Compare that same-tenant retry identity before looking up the source. A matching + retry returns the existing Session without input admission or source revalidation; + ordinary reads include current activity. Stream retries use the row's committed + event cursor and emit no created event. Recheck after source resolution failure + for a concurrently committed creator; do not hold a lock across resolution. + The unique creation upsert remains authoritative when concurrent resolutions differ. + Unrelated inline requests keep their existing resolved/default equivalences. + Recorded credential-bound retries recover before reading mutable Vault contents, + so another same-URL Credential cannot change an accepted selection. Rows with a + known creator but without recorded request intent retain resolved-hash behavior; + original overrides cannot be reconstructed, so no backfill is permitted. Source + mutation-independent retries apply only to recorded identities. Exact hosted + retry/error semantics remain separate work. +- Tenant scope must come from authenticated service identity before calling the + execution Store. Product workspace/user references in metadata grant no access. + Keep credentials and effective execution options out of Session metadata. + Store resolved, non-secret Agent/environment configuration in the Session's + immutable configuration snapshot. Inline and historical creation identities include + the resolved configuration; new saved references use the separate caller intent. + Session metadata updates replace only metadata under the authenticated tenant: + a supplied metadata field is required, null/empty clears, and a nonempty object + replaces all pairs. + Keep execution state, timestamps and the original creation request hash unchanged; + creation retries return the current resource without restoring its old metadata. + Public schema validation belongs to the API; the Store validates JSON structure. +- Session listing optionally filters by the immutable root `configuration.agent.id` + within the authenticated tenant. IDs are opaque and include inline Agents; never + require a surviving saved Agent or resolve product ownership. Apply filtering + before pagination and activity projection, using the tenant/Agent/creation index. + Omission retains unfiltered listing; a supplied empty string remains a filter. + Preserve the existing tenant-owned cursor and creation-time/ID ordering rules. + Hosted empty-filter, mismatched-filter cursor and exact error semantics remain + unverified. Other resource lists do not accept this parameter. +- `make sqlc-generate` and the drift gate cover this service. Run + `make check-agents-api` with `OAC_TEST_DATABASE_URL` pointing to a + dedicated `oac_*_tests` database for Session integration tests. + CI provides a separate PostgreSQL service. Migration immutability and ordering + apply independently to each service directory. + +### Turns, input, schema and clients + +- Turn writes serialize on the tenant-scoped Session row. An idle message starts + a Turn; messages during queued/running/waiting work belong to that same Turn. + Store input retry identities and order durably. Cancellation retains its first + target, including an idle no-op, so retries cannot stop later work. Queued work + can cancel before dispatch; active work needs an executor outcome. Terminal + states and outcomes cannot be overwritten. Public event admission uses these primitives; live output streams remain separate. +- Input requests are ordered batches committed under the same Session lock. A + retry key identifies the complete ordered batch; changed length/order/content + conflicts and a failed transaction leaves no partial inputs or cancellation. + Existing single-event requests retain their identities at batch position zero. + Internal admission limits are 64 events and 512 KiB of payload per request; + the public API must still validate the upstream event schema. +- The external protocol reference is `openai/openai-python`'s `beta/agents`, pinned + in `contracts/agents-api/upstream.json`. Follow its Session/Turn/event semantics + and verify supported behavior using the official client. Track current coverage + in `contracts/agents-api/README.md`; SDK workflow objects are not this contract. +- Shared supported wire types live in `contracts/agents-api/v1`. `make openapi` + writes `openapi.yaml` (`/v1`), `core.openapi.yaml` (`/core/v1`) and + `runtime.openapi.yaml` (`/api/v1`); never mix their routes or authentication + schemes. Review the generated diffs; contract tests hold `openapi.yaml` to the + pinned routes and fields. +- The standalone service uses `OAC_DATABASE_URL`. PostgreSQL stores Projects, + their immutable execution scopes and API-key digests. Keys in the same Project + resolve to one shared service-account principal and tenant. Project/key writes + go through Core-key `/core/v1` routes, not configuration files. + Optional `OpenAI-Organization` and `OpenAI-Project` headers must match the key; + repeated/conflicting values fail authentication. Metadata, forwarded identities + and product session cookies grant no access. Issue/revoke operations take effect + without restarting Core. Deployment credentials cannot authenticate public calls. + Every new Session requires an explicit typed creator at the Store boundary, + including internal callers. Public creation derives it only from the authenticated + principal. Persist creator kind/ID in the creation transaction and never rewrite + them on retry, update or source mutation. The tenant remains the project partition; + do not duplicate project identifiers or create a product identity dependency. + Both early saved-reference recovery and the authoritative creation upsert require + matching creator kind/ID before returning a Session or event cursor. Different + credentials for the same principal can retry; another principal using the same + project/key receives the local idempotency conflict. This does not introduce + creator-only resource reads or mutations, or claim verified hosted retry parity. + Pre-migration Sessions retain null creator columns and remain project-readable; + creation retries cannot claim them. Missing creator and missing request intent + are distinct. Never infer historical ownership from keys, metadata or product + records. Retire older API writers before serving the creator-enforced deployment; + mixed-version writers are not supported. Tests must supply explicit synthetic + creators; only controlled historical fixtures may seed unknown ownership. + The operator-selected + `OAC_DEFAULT_HARNESS` is separate from the requested model. + Public execution supports the enabled `none` profiles, the three colocated + self-hosted profiles and qualified three-harness Docker hosted profiles; reject unsupported + input/environment/agent options explicitly. +- `packages/agents-client/v1` configures the pinned official `openai-go` Session + service. Use SDK request/response types, pagination and errors directly rather + than reimplementing transport or copying wire types. Supply an explicit service + base URL/key and creation retry key; SDK retries are disabled by default. Product + integration is a later cutover, not a side effect of constructing this client. +- `services/agents-api/tests/official_client.py` verifies the actual server with + the pinned SDK and strict response validation. It requires a dedicated test DB + prepared by the Store tests and `OAC_TEST_SERVER_BIN`; it never starts Docker. + The same harness runs the official Go client with fresh execution tenants and + checks its created Sessions through the Python SDK. + +### Execution dispatch and Runtime capabilities + +- Execution devices are operator-provisioned in the Agents API database with + tenant ownership and a credential digest. Their internal daemon gateway uses + `/api/v1/agent-daemon/*`, separately from the official `/v1/agents/*` surface; + device credentials grant no Session API or product permissions. The optional + `OAC_PUBLIC_URL` enables that gateway. It is a single-process registry, + not a claim of multi-pod execution or stock `exec-server` interoperability. + Self-hosted enrollment uses this gateway with an exact Environment binding. + Session/device bindings are tenant-scoped and immutable. Revocation denies new + connections and binding reads; an existing connection closes on its next + heartbeat. Connectivity comes from the live registry, not a persisted online + flag. `last_seen_at` is diagnostic only. Product gateway behavior is unchanged. +- `services/agents-api/internal/execution` dispatches internally resolved Turns + through that gateway. Claim `queued` to `in_progress` before subscribing/sending; + never automatically replay a claimed or interrupted Turn. Ordered extra inputs + require native steering receipts. Commit terminal outcome and native Session ID + together under the admission lock; unapplied messages prevent successful completion. + Resolve credentials separately from the immutable non-secret snapshot. +- Internal execution requires advertised durable Turns, strict resume, preparation, + applied input receipts and the qualified operation capabilities. Reject + unadvertised peers before claiming; failed strict resume cannot start unrelated + history. A completed Turn closes steering admission, settles existing native + operations and receipt sends, and closes its output before the retained Executor + can accept another Turn. Normal completion does not cancel the Executor. + Cached input identities and conflicts remain readable while completing; + queue/write success is not consumption. The receipt worker stays busy through + its send, and router shutdown cancels its native and transport waits. + The per-input `durable_receipt` opt-in requires a phased adapter. Its ten-second + transport timer stops only after a complete native write; a separate `written` + acknowledgement stops the API's thirty-second delivery timer. Neither phase + advances the input cursor. Await final native acceptance/consumption under the + Turn lifetime without automatic redelivery. Receipt sends retain a separate + five-second shutdown-aware context, and Done retains a fifteen-second final + settlement bound. Once cancellation is sent, its receipt owns the terminal + outcome even if an input becomes unknown first. Calls without the opt-in retain + their existing response deadlines. Native history still requires the device's + persisted engine files; IDs alone cannot restore deleted history. Cancellation + receipts carry the stopped Turn's confirmed continuity snapshot when no Done is + emitted. Preserve separately reported usage on failure; do not add the same + counters again when Done also includes them. + Usage frames carry cumulative snapshots for the current execution, not deltas. + Adapters publish observed snapshots promptly through the same ordered stream; + waiting for Done unnecessarily loses known measurements if the Runtime stops. + Core replaces complete valid token breakdowns and preserves the last committed + measurement on interruption. Missing measurements remain unknown. Do not infer + token consumption from context occupancy or estimated costs, or parse native + Raw payloads in Core. Public Session totals cover recorded root Turns and are + null while any root Turn has not ended or once one ends with unknown usage; + Runtime telemetry uses the separate measured sum of recorded snapshots. Subagent + Turn listings are not a summable accounting ledger. A cumulative native total that has not advanced + past the Turn's baseline is not a measurement of that Turn. Native measurement coverage + and exact provider/model attribution remain explicit qualification boundaries. + No separate public usage event or historical SSE replay is introduced. +- The dispatcher is an internal entry point used by the standalone service worker. + Legacy internal `daemon` configuration is not a public Environment type. + Self-hosted enrollment uses exact local binding; further pending interactions + remain separate slices. Unexpected interaction requests fail explicitly until supported. +- `environment_none` advertises an adapter's explicit environment-disable + path. Execution snapshots with public `environment.type=none` require that + capability and set `disable_execution_environment` on the internal prompt. + For Codex, the daemon forces `CODEX_EXEC_SERVER_URL=none` after caller environment options + and confirms native `local` and `remote` environments are unknown before starting + or resuming a thread. Unsupported binaries fail closed. The bound device hosts + the engine process; it is not a user execution environment. This is not an OS + isolation guarantee, and engine state still lives on that host. Ordinary product + requests retain their existing environment. The public worker selects an authenticated same-tenant engine host for this mode. +- `execution_controls` advertises the typed search/verbosity block on the daemon + prompt. Agents API requires it in addition to the selected engine's required capabilities + before binding/claiming work. Older peers with only option-based capabilities + must not receive controls they would ignore. The API sends resolved search and + text verbosity values; native option names belong to adapters. Codex translates + them using its existing validation/catalog path after cloning adapter options, + so explicit controls take precedence without mutating those options. Omitting + the entire block preserves ordinary product behavior; a supplied block requires + both valid fields. This internal contract does not add public configuration or + engine support. Future native adapters must verify the same semantics before + advertising the capability. +- Explicit public `programmatic_tool_calling.enabled=false` uses the common + `ExecutionControls.DisableProgrammaticToolCalling` field and the operation-specific + `programmatic_tool_calling_disable` capability. Both public qualification and + Runtime support are required for that request; omission creates no prerequisite. + New and resumed executions retain the frozen setting. Codex disables native + code-mode features and checks managed requirements before starting/resuming a + thread, rejecting a conflicting requirement. Claude and MiniMax retain their + restricted native inventories, which exclude programmatic execution. This does + not remove unrelated native utilities or claim enabled programmatic support. + Explicit `web_search.mode=disabled` reuses the existing disabled search control. + Search remains off when omitted. Optional search settings are resource data and + do not cause execution while disabled. Saved Agents keep every pinned search mode; + enabled search remains unqualified and rejects at Session admission. +- `web_search_control` advertises the Codex adapter's explicit `web_search` option + (`disabled`, `cached`, or `live`). Agents API requires this capability before Codex dispatch; + the typed execution controls force search off on new and resumed Turns. Native configuration translation stays in the + adapter. Product requests that omit the option inherit their existing defaults. + This is tool selection, not a network isolation guarantee. +- Inline Agent `text.verbosity` accepts `low`, `medium` and `high`; omitted or + null values resolve to `medium` in the immutable configuration snapshot. The + Codex dispatcher requires `text_verbosity` support and sends the effective value in + typed execution controls through the Codex adapter for both new and resumed Turns. The adapter queries + the native active catalog with `codex debug models`, checks model support and + pins that catalog snapshot for execution. The probe requires Unix process-group + cancellation; other daemon hosts do not advertise this capability. For models + without declared verbosity support, including the native unknown-model fallback, + `medium` selects native default text generation by omitting the override. The + pinned protocol defines `medium` as the default text amount. Supported models + still receive explicit `medium`, even when their catalog default differs. + Unsupported `low`/`high` and unreadable catalogs fail before model execution; + unsupported non-default levels remain an explicit implementation gap. + Product requests that omit the native option retain their existing defaults. + Structured output has a separately qualified profile described in + [Structured output execution](../../CONTRIBUTING.md#structured-output-execution). +- `subagent_control` advertises native subagent tool control. Agents API requires + it when resolved `multi_agent.enabled` is false and sends the typed internal + `disable_subagents` policy on both new and resumed Turns. Native translation + stays in the adapter: Codex disables both multi-agent feature generations, + overriding operator feature preferences. Product prompts that omit the policy + retain their defaults. Enabled multi-agent observations require separate operation qualification; the Agent tools list is not + proven to enumerate every harness-internal utility. +- Subagent resources use the common observations in + `internal/agentdaemon/proto/subagents.go`: verified identity, successful lifecycle + effects, native-owned Turns/Items and neutral coordination operations. Core + assigns public IDs and projects them under the existing Session lock and leased + execution journal. Native names, history parsing and outcome proof stay in + adapters. Public GETs read persisted resources without starting native work. + Child Turns have a native writer and a separate table from the Core queue. + Session Turn reads and the Session event stream carry root work only: read child + Turns and Items through the Subagent routes, and never publish child Turn or Item + events on the Session stream. A child Turn's `agent_id` is the Session's Agent + ID; `subagent_id` names the child. The migration-defined `public_execution_turns` + view has no public reader; do not reintroduce mixed Session Turn pages. + Session Items stay root-owned; copied parent transcripts never become child work. + Repeated effects are idempotent. Active includes idle; task completion, process + release and cancellation cannot fabricate public closure. Native timestamps + retain their actual precision and unknown Usage stays null. + Reuse the existing native owner for child settlement and cancellation, freeze + root output first, and deliver child Items before their terminal Turn snapshot. + Do not add another scheduler or a broad recovery framework. Capability + advertisements do not qualify unsupported native facts. The exact read contract, + admission limits and remaining evidence are in [Subagents](../../contracts/agents-api/subagents.md). +- `function_tools` advertises the optional native function-call bridge. Explicit + prompt definitions become Codex dynamic tools; unchanged prompts carry none. + Requests and ordered text/image results are scoped by Run and native call ID. + Normalize string results into one text part at the public execution boundary; + the internal result carries a typed content array, and adapters translate it + to native content without fetching images or dropping parts. Validate content + before consuming a pending call. Retry identity includes the complete ordered + content and success flag. Reuse + application receipts and conflict detection; a receipt confirms the native + reply was written, not that an external side effect succeeded. Pending calls + end with their Run; the execution service owns persistence and recovery, while + Parsar retains business approval and credential-owner authorization. Do not + map native approval requests to invented official protocol resources. +- Function-call storage is scoped by authenticated tenant, Session and Turn, with + immutable public/executor call identities and arguments. Result admission and + application receipts serialize on the same Session lock as cancellation and + terminal transitions. Store the complete caller-validated result object; wire + validation and native translation belong to their API and execution boundaries. + Identical retries return the saved decision; changed results conflict. Pending + reads exclude applied calls and cancelling/terminal Turns while history remains + readable. Persistence does not imply transparent native-process recovery. + Recording a call moves the Turn to `waiting`; the last application receipt + resumes it. Session reads use one database snapshot for Turn, actions and usage. + Session state events retain their action snapshot, without private executor IDs + or results. Cancelling/terminal Turns expose no actionable calls. A waiting Turn + can fail or cancel before a result arrives; successful execution requires resume. + Actions remain visible until native application is acknowledged. This timing is + an implementation choice, not verified upstream event sequencing. +- Function results can join internal message/cancel input batches. Their explicit + Turn/call identity selects an existing call; admission never creates a Turn for + a result. Save the complete result and its input retry record in the same Session + transaction. Any invalid target, conflicting result or later batch error rolls + back the whole request. Resolve targets only after the tenant Session lookup: + an unknown call or a call of another Turn is 400 `invalid_request_error`, and + missing or foreign Sessions keep one 404. Identical saved results remain retryable after termination + without applying them again. The execution input cursor skips function results; + their separate native receipts still determine application. Public result events + validate variant-specific fields and required values before admission; store + omitted versus null error/output and ordered text/image parts. Inline function + tools resolve into the immutable configuration with explicit + `defer_loading=false`. Validate required strings and parameter objects before + persistence; reject unsupported deferred discovery. Omitted/null/empty tool + lists resolve to no tools. The public worker selects or waits for a same-tenant + device advertising `function_tools` when the Session has functions. + Function results are Session input Items: emit `item.added` with a null output + index, and never emit `item.done`, whose upstream union only allows agent output. + Project their public output/error from the saved submission; the wire always + carries both, null when not submitted, while stored payloads keep the submitted + presence. Native content normalization must not change public history. +- Codex function application requires a matching live native dynamic-tool completion, + including root thread/Turn/call identity, function, success and ordered content. + Writing its JSON-RPC response is not application. The adapter owns pending + receipts without holding their state lock across IO or waiting; terminal state, + cancellation and native loss settle unconfirmed submissions before release. + Uncertain receipt timeout ends that native execution without resending the result. + The common Runtime interface and router continue to own delivery identity, + retry/conflict and terminal ordering; Core never parses native tool events. + Record native confirmation before potentially blocking observation publication; + output backpressure cannot turn a known application into an unknown outcome. + The function submission owns raw write completion and receipt failure together; + its writer never applies an independent timeout/close decision. A confirmed + receipt releases submission even if writer completion has not yet been scheduled. +- Internal function execution requires an advertised `function_tools` capability + before claiming a Turn. Translate resolved definitions in the execution adapter, + persist declared callbacks before exposing actions, and deliver each saved result + once per live dispatch. Keep its success flag and ordered text/image output; + append a non-null error as a final text part because the native result has no + separate error field. Retain the original complete result in storage. Do not + treat transport delivery as application or automatically replay an uncertain + result. The adapter waits for outstanding application receipts even when Done + arrives first. Waiting Turns still accept execution observations and cancellation. + The public function workflow is verified with the pinned SDK and a real daemon + and Codex process against a synthetic model endpoint. This does not verify + other tool types, deferred discovery or upstream service timing. +- `message_items` advertises native assistant-message observations. Agents API + opts in with `observe_messages` only for advertised peers; ordinary product + requests retain their existing frame sequence. Opted-in text deltas carry their + native item ID, and `output_message` records start/completion, phase and the + completion text snapshot. A snapshot is not another delta; uncompleted messages + remain partial when their Turn ends. Keep these observations in the journal + before projecting public Items. This does not promise daemon event replay. +- Legacy `tool_items` / `observe_tools` raw snapshots remain available to old + daemon callers. New Agents API execution does not request or decode them. +- `tool_observations` advertises engine-neutral tool snapshots. The opt-in + `observe_tool_observations` takes precedence over legacy `observe_tools`: + attach the typed `observation` to existing tool-call frames without `native_item`. + Native adapters own discriminator/status/action translation and preserve raw + structured values; reuse the shared function-result content type. Kinds are + `command`, `mcp`, `function` and `web_search`; observation status is + `in_progress`, `completed`, `failed` or `incomplete`. A present empty function + content array remains distinct from missing content. These are + execution facts, not public Items: the API owns public IDs, schema projection, + lifecycle events and persistence. Product requests that omit the opt-in keep + their frame sequence and fields. Agents API requires this capability before + claiming work and always requests neutral observations. Its Item projector + validates this shared contract and never decodes engine-native tool snapshots. +- Codex callers opting into neutral tool observations also receive `command_output` + fragments with the existing native command identity. The adapter filters the root + Thread/Turn; the service requires an already indexed command in the same Turn. + Journal and Item updates commit with `agent.output.command_execution_output.delta` + events, retaining original fragments and the command's stable output index. + Completion output replaces accumulated drafts; absent completion output retains + observed text. Terminal Items ignore late fragments, and cancellation preserves + partial output without inventing successful command completion. Native text + conversion and output quotas still apply; this is not a byte-complete stdout/stderr + guarantee. Pinned native 0.153.4 also has an early-output subscription window; + missing native notifications/aggregate bytes remain a separate execution gap, + not output to reconstruct from model tool-result prose. Older peers may supply + only completion snapshots. Product requests + without the observation opt-in retain their existing frames. + +### Observations and streaming + +- Execution observations are written to tenant-scoped `turn_events` in ordered, + idempotent batches before they can back recovery or publication. Keep daemon + payloads intact; this internal journal is not the public SSE protocol. Flush at + least every 100 ms while consuming events and before terminal persistence; + uncommitted observations can be lost on a hard process crash. Terminal outcome, + journal entry and native continuity commit together. Preserve partial text on + cancellation, including frames queued before a separate cancellation receipt. + Do not infer successful completion after a persistence error or stream overflow. +- Agents API uses the gateway's durable subscription; overflow or disconnection + closes it with an explicit error. Product subscriptions retain their existing + best-effort behavior. Journal limits are 512 KiB per payload, 1 MiB per batch, + 65,536 observations and 32 MiB per Turn; terminal persistence reserves one + additional outcome entry. These are internal admission limits, not promises + about upstream API limits or durable daemon-to-service replay. + +- Live Session SSE reads execution-owned `session_events`, committed with the + corresponding input, Item or lifecycle transition under the Session lock. + Store immutable transition snapshots; never render an old event from a later + Turn state. Reuse the API's response mapping and keep internal snapshots out of + wire payloads. Historical index rebuilding emits no live events. +- The notification buffer retains at most 256 events and 64 MiB per Session + after each transaction, retaining a single oversized event if necessary. + Read batches are bounded to 32 events / 1 MiB, with the same single-event + exception. This buffer is not a public replay log: GET begins at the committed + high-water mark, ignores Last-Event-ID, and polls committed events every 100 ms. + Missing sequence positions produce a safe stream error and close; recover via + Session/Turn/Items queries. Socket writes have a five-second deadline and hold + no database connection. Client disconnect releases the handler; comments keep + idle connections alive. GET SSE does not close merely because one Turn finishes; + only creation responses end on settlement (below). + +- Session creation with `stream=true` reuses atomic input admission and the live + event loop. The upsert returns its cursor under the Session lock, before initial + inputs; never replace it with a post-commit cursor lookup. A new response emits + one request-local `agent.session.created` with the committed Session projection + that the JSON 201 response returns (read after the commit), then committed + changes from that cursor exactly once. A fresh creation stream ends right + after the first `agent.session.idle` recorded when a Turn ends or an input + reservation stops being pending (expired, cancelled or failed), or any + `agent.session.failed`, and never sends the events after it. A self-hosted + connection clearing pending input to idle, `requires_action`, function results + and resumed work keep it open. A creation that admitted nothing (no Turn or + reservation) ends right after `created`. Settlements that record no event use + a fallback: after an empty drain the stream reads the JSON-path projection and + the event cursor in one database snapshot and, if the Session is idle or failed + with no queued, running or waiting Turn and no pending reservation, sends only + events up to that cursor, then ends. Accepted follow-ups: another client's work + drained before that read can still be sent, and idles recorded by an older + binary during a rolling deploy carry no settled marker and rely on the + fallback. An input reservation made while the ending Turn captured Artifacts + can start a later Turn that the stream does not follow. The settled marker and + pending-input flag are Store-internal, never wire fields, and add no events. + Re-read the projection after a sent Session status event and otherwise at most + once a second. The local creation retry key excludes response mode; a same-key + `stream=true` retry of an existing creation returns 201 with only the + connection comment and ends at once, admitting nothing and following no work, + because official same-key requests create distinct Sessions. Retry the same + request/key with `stream=false`, or use the GET events stream, to recover. GET + event streams keep their live-only start and never end on settlement or a Turn + failure; the only server-side end is the terminal `agent.session.failed` of a + hosted provisioning failure (and Session deletion), since that Session can + never run again. + Disconnect never cancels admitted work. Official observations cover `none` + creation; self-hosted, hosted and no-input stream lifetimes and the retry + behavior are local choices, and the separate SDK one-Turn helper does not + define this endpoint. Do + not present local retry behavior as replay. A new Turn records `turn.created`, + its user input Items, then Session activity in one transaction. Terminal Turn + events carry top-level `usage` copied from their Turn snapshot, null when + unknown; never derive or sum it. + +### Public Turns, Items and events + +- Public Turn retrieve/list project persisted execution state and the immutable + Session Agent identity. Scope both resources and pagination cursors to the + authenticated tenant and Session, ordering by creation time then ID. Do not + expose adapter outcomes, native IDs or raw errors. Failure uses a customer-safe + category; usage is nullable when a complete upstream breakdown is unavailable. Submission uses the separate Session events endpoint. + + +- Public Items list reads a persisted execution-owned projection, updated in the + same Session transaction as admitted messages and journal batches. IDs derive + from the Turn and source identity; the first-observation timestamp and stable + tie breakers never change when content or status changes. Allocate each new + Item's Session position under the Session lock, preserving observation order + for equal timestamps. Allocate a separate zero-based `output_index` per Turn; + inputs do not consume output indexes. Updates and retries retain both values. + Persisted indexed history keeps its deterministic order; missing original + ordering cannot be reconstructed. Cursors are scoped to the authenticated Session. + Terminal Turns expose unfinished Items as `incomplete`, preserving completed + message/tool states independently of the Turn outcome. +- Public history reads use the persisted index; the private pre-Items journal + backfill is retired. Migration 15 and its historical validation remain evidence, + not a supported upgrade procedure. An old installation is unsupported and must + be retained separately from a fresh installation. Never mark unprepared history + indexed by hand or replay engine execution to convert it. +- Project only the declared public Item variants; native adapter metadata is not + a response schema. Preserve structured tool JSON without float conversion. + Completion text replaces accumulated deltas. Item merging must not mutate the + incoming observation or the previous snapshot: public text delta events read the + original fragment after merging, while Items retain the accumulated text. + Copy the content slice before replacing its text pointer. Assistant text + Items follow the official event sequence: `item.added` in progress with empty + content, an empty `content_part.added`, deltas, then the done events. A first + observation without its own fragment (a non-streamed native final) carries its + unchanged text in one delta; this frames the text and never alters it. Wire-only + explicit nulls (`phase`, function result `output`/`error`, Item event + `output_index`, Agent `reasoning` keys) come from response marshalling. Stored + Item payloads keep their original encoding through `Item.MarshalStored`, so + replayed child Items still compare equal, and stored configuration keeps + omitting unset reasoning keys. Keep partial output on termination; + do not turn an unfinished call into a successful result. Thinking fragments are + internal observations, not a claim of upstream reasoning-item support. + +- Public `POST /v1/agents/sessions/{session_id}/events` accepts ordered text-message + and cancellation batches through the pinned official client. Preserve individual + input messages in the Item index while deriving text for native dispatch. Batch + idempotency and cancellation targets remain durable; unsupported variants fail + before admission. Session creation accepts initial text as a string + or user-message array through the same parser and admission path. Commit the + Session, initial input, first Turn and Item/event projections in one transaction. + A creation retry returns the existing Session without re-admitting initial work, + including after terminal or later Turns. Omitted/null input is permitted only + for non-streaming hosted creation and self-hosted creation. + Creation streaming uses the shared live path above. Image support requires the + qualification in [the message-input contract](../../contracts/agents-api/message-input.md). + +### Worker ownership + +- Enabling daemon transport with `OAC_PUBLIC_URL` also starts a bounded execution worker. Select + only connected, capable devices owned by the authenticated tenant; bind once and + preserve native continuity. Metadata cannot select a device. Offline work stays + queued and can be cancelled. An engine host is not a self-hosted environment. +- One worker service owns an execution database through a dedicated PostgreSQL + advisory-lock connection. Its execution Store view uses that same connection for + every Session transaction: binding, claim/reconciliation, journal/Items/Usage, + function callbacks/application receipts and terminal/native continuity. Serialize + these short transactions and lease pings; execution transactions have a five-second + deadline including gate and Session-lock waits. Sandbox reset snapshots bound the + deployment relation explicitly to its legal singleton row before resource joins. + Keep this cardinality visible even on fresh databases without statistics: inflated + join estimates can trigger expensive JIT compilation inside the lease deadline. + Never hold a transaction across daemon/model work, reconnect the writer or fall + back to the pool after lease loss. + The original Store handles public admission and device/auth maintenance on pooled + connections. Execution reads may also use the pool; a read grants no write authority. + At startup, reconcile previously claimed work as failed, preserve queued inputs + and never replay uncertain execution. Shutdown cancels active dispatch and attempts + terminal persistence before releasing the lease; a lost owner cannot commit it. + Lease Close invalidates its writer and waits for pgx connection cleanup within + the caller deadline. A later Close can resume that wait after a timeout. This + drains client resources; it does not acknowledge remote advisory-lock release. + Tests that immediately transfer ownership must observe the previous owner's + exact database advisory lock disappearing before starting its successor. Bound + that wait and fail on query errors; do not retry Worker startup to mask competing + owners or change production lease behavior for a test's timing assumption. + Worker shutdown retains its existing bounded best-effort close policy. + This fences database writes, not already queued daemon commands or native effects. + Native quiescence/reconnect and recovery of unreported outcomes remain separate + gaps; this is not distributed exactly-once side-effect execution. +- Session state and last activity derive from its latest persisted Turn. Queued or + active work is `in_progress`, successful/cancelled work is `idle`, and failures + use a safe public error. The worker does not replace product dispatch, business + authorization, or the separate approval/environment lifecycle work. + +## Hosted sandbox nodes and optional suspension + +Default installation includes Core, Web and PostgreSQL but no execution node. +It always creates the Core key. Core receives only its digest; the paired console +server receives the private key, uses it for sign-in and injects it only on +`/core/v1` requests after console login and same-origin checks. The browser +never receives that key. Node and daemon connections use `/api/v1` +with their own credentials; the reverse proxy sends them directly to Core, never +through Web. Zero-node Core receives neither the Docker socket nor KVM. +The Web and deployment administrator API select one provider, per-sandbox +resources and immutable Runtime release; the Core address comes from the +installation public URL. PostgreSQL owns this complete, +generation-tagged selection under the existing execution lease and deployment lock. +The shared `sandbox.DeploymentSpec` defines required CPU/memory and supported disk +limits plus Runtime provenance; neither a node file nor the installer owns another +selection. Request `resources` describes limits; response `specification.resources` +contains those limits, while response `resources` counts retained allocations and +pending hosted Environments. Keep these meanings distinct in clients and UI. +See the [deployment contract](../../contracts/agents-api/sandbox-deployment.md). + +Docker and E2B accept CPU/memory but reject independent nonzero disk capacities; +do not claim hard root/workspace disk quotas for them. Docker creation and native +inspection enforce the declared CPU/memory and exact image. E2B setup verifies the +exact ready template build and matching CPU/memory through the pinned SDK before +saving its encrypted account key, and records the build as read for the safe view; +an omitted E2B `resources` adopts that build's CPU and memory. Loading a committed selection reconstructs its +provider from its owned generation, current committed credential and receipts without repeating candidate +template validation; a template endpoint outage must not block cleanup of existing +sandboxes. Creation and instance inspection still enforce the saved resources. +E2B uses direct placement without a node. Node +providers require one immutable distribution with source commit, Docker image ID, +OCI manifest digest, microsandbox image reference, Runtime and firmware hashes. +These identities are distinct and cannot substitute for each other. + +Startup claims the stable installation identity and a new owner epoch before +provider selection. The runtime manager retains generation-aware provider facades. +Initial setup and replacement prepare and validate candidates before database +writes. Rejected candidates preserve the active configuration and workers. A node +replacement uses the existing mutation gate, pauses manager admission, drains old +calls and loops, then repeats the resource/generation guards in the commit +transaction. A changed selection, its generation and retirement of old nodes and +unused enrollment tokens commit together. Publish the prevalidated configuration +and shared observation/bootstrap cache under the manager mutex without further +external work or a fallible activation step. Request cancellation after commit +cannot discard that publication. Interrupted drains remain barriers for retries +and resume. Keep the Worker and runtime manager as single owners; provider I/O and +draining hold no database transaction or manager map mutex. + +A locally unavailable provider dependency keeps hosted admission closed while the +existing scan waits for repair; administrator recovery remains available, including +on restart. Database and ownership errors remain failures. Unconfigured hosted +admission creates no Session state. Derive Runtime bootstrap and daemon WebSocket +addresses from the validated installation public URL, never inbound Host headers. Read the +current selection from the live deployment API; there is no startup configuration +read. + +All sandbox writes require the observed generation, including initial POST at +zero; reject stale state before provider preparation, reset or no-op checks, and +repeat it under the committing row lock. Same-provider PUT advances the target +without draining execution or retiring nodes, tokens or the owner epoch. Node +providers prepare independently and keep their old qualified serving pin. A different backend, E2B team or unspecified old selection requires reset. +Preserve historical allocation ownership and placement, never migrate a Session. + +Persist immutable allocation and placement deployment generations, distinct from +compute generations. Node allocations copy their placement; E2B binds at reservation. +Keep superseded specification/build rows without credentials while current, owned by +an unreleased allocation/placement, or pinned by any nonremoved node. A durable node +serving pin survives offline state and zero resources. GC uses the deployment lock +and bounded pages; reset clears pins only after release. Never downgrade facts needed +for generation routing. Bind node readiness to exact generation, current connection +and owner epoch. Promote a durable pin only for readiness of the then-current target +under deployment serialization. Late superseded readiness cannot acquire a pin. +Filter online/readiness/address/capacity before preferring the newest eligible pin; +newest-full must not mask older-free. Route Create, restore and cleanup through the +immutable allocation/placement generation. V1 keeps its qualified enrolled fallback. + +Use sparse v2 control batches of at most eight entries with no lifetime cap. Omitted +facts never authorize deletion. Correlate whole retention grants to connection, +epoch, sequence, generation and digest; recheck queued/inflight/helper references. +Permanent generation flock files survive updates and GC. Before any helper starts, +the installer durably binds the original inode to installation/generation/specification +identity; Python and Go openers verify it after every restart and never adopt a +replacement or missing record. Preparation plans stay distinct from write-once final +provider configurations: pending-only entries may recover or collect under Core +authority, never serve. Resolve Docker's supported local image ID before publication. +Only transfer/checksum/provenance errors report runtime_download_failed; preserve +other fixed causes without parsing native error text. Retained Runtime bytes and +the console's exact-release HTTP allowlist must agree. See +`contracts/agents-api/node-generation-protocol.md` for recovery, immutable artifacts +and the conservative v1 legacy-helper retention boundary. +Interrupted node collection keeps an exact private generation journal and immutable +configuration. Restart treats it only as a candidate for a fresh correlated Core +drop grant, never as preparation or serving readiness. Persist native cleanup +before removing its executable and durable file cleanup before the dropped marker; +CLI errors and unknown native ownership cannot establish absence. Docker daemon +images are shared host content, so automatic node GC retains them; only the host +administrator can establish whole-host authority to remove them. Microsandbox +image GC remains scoped to the installation-private store. Fresh nodes persist +verified original Runtime file ownership separately from the host program; collect +only exact unreferenced Runtime paths, keeping program, identity, base configuration +and manifests readable across interrupted cleanup and restart. + +Use one E2B classifier. Omitted key preserves the current key; identical selection +with omitted key is a no-op. Explicit key submission, including identical bytes, +verifies and increments generation. Check the candidate and retained builds, paginated +team-owned template membership and every settled live receipt under the candidate key. +Initial E2B selection must also belong to the submitted key's team. Before online +replacement, verify the current exact template under the committed key, then under +the candidate key: shared membership is the ownership anchor, including for legacy +installations with no retained resources. The pinned SDK exposes team-owned template +listing, not an authenticated team ID; never invent or persist an inferred team ID. +Public readability cannot anchor ownership. A legacy selection outside the committed +key's team, or a revoked committed key, requires reset without blaming the candidate +key. Transport uncertainty remains unconfirmed. Keep the old key valid until the PUT +returns 200; revoke it only afterward. Missing/unsettled receipts and +unconfirmed reads reject. Fence credential commits against all provider calls and +actual helper subprocess completion after cancellation, then reverify. Helper exit +never settles unknown remote Create. Bounded failure keeps the original key and +lifecycles. Resolve each allocation's immutable specification and current key in one +snapshot; no stale-key cache, fallback to current spec, or provider-map unloading. +Audit the committed change without secret fields. + +Project rollout and reset from the same database snapshot. Old retained resources +alone never imply preparation or high-frequency polling. Rollout state is settled +unless actual target preparation is active. Offline/unconfirmed nodes are unknown; +only exact current connection/epoch/generation readiness is ready. A durable pin is +not connectivity. Fixed diagnostics alone may explain failed preparation. Report +preparing only from an actual target observation; an online v1 node can be +update_required while its valid pin remains available for admission. + +Reset is durable execution state, advanced by the existing manager outside its +counted work. Serialize start, escalation, cancel, setup, update and finalization +through the mutation gate. Auto waits only for in-progress/waiting root/subagent +Turns and pending file writes; queued work, idle and suspended Sessions can archive. +Recheck idleness with Session then deployment locks; never invert that order during +finalization. Keyset-page bounded work, bind candidates to the reset request time +as well as generation, and persist the original absolute deadline and validated +audit provenance. Cancellation cannot revive archived work. Auto's deadline +escalates to force durably; self-hosted Sessions are excluded. + +Use one snapshot and timestamp for the held-resource partition: unreleased +allocations plus pending hosted Environments without any allocation row. Cleanup +has precedence, then busy/idle. Count deleted/expired receipts until release. +Offline ownership comes from allocation or active placement node identity, with +the existing 45-second connection/owner-epoch predicate; provider readiness is +independent. Project every offline node and make its sum equal on_offline_nodes. +No cleanup failure, offline state or empty read authorizes a synthetic release. + +At zero held resources, drain outside database transactions, recheck under the +deployment lock, and atomically clear all provider policy, E2B credential/build +metadata and specification, retire nodes/tokens, advance generation/owner epoch +and audit completion. Publish a generation-bearing nil provider tombstone without +fallible work after commit, preventing delayed loads from reviving the old provider. +On a failed final write or interrupted drain, restore the committed provider using +a bounded owner context before releasing the mutation gate; recovery failure keeps +admission fenced and stops the owner. Ordinary migration resumes old Web-managed +maintenance with reset null. Refuse downgrade during reset or for an unconfigured +completed reset with generation above zero; never discard the generation. + +Explicit administrator Session archive requires a Web-managed deployment and its +current generation, without a reset precondition. Keep Project scope and hosted +eligibility checks in its Session-first transaction, alongside Environment expiry, +cancellation, Runtime authority revocation and audit. Background reset reconstructs +the actual Project scope from trusted records while retaining requester provenance. +The ordinary provider lifecycle owns compute/snapshot release. Retain history and +persisted Files/Artifacts; archived Sessions cannot resume unpersisted workspace. +The archive GET reports resource disposition, not provenance or Turn finalization. + +Archive cancellation must not destroy a healthy receipt path before its terminal +commit. Only the archive that first revokes a device may persist its exact +`archive_cancel_turn_id`; ordinary revocation clears it, and repeated cleanup +preserves rather than recreates it. The existing authenticated delivery can drain +its cancellation for at most 20 seconds from the Turn's original +`cancel_requested_at`. Track that delivery through Done, cancellation ACK and +terminal commit, independently of subscription removal. Heartbeats and both normal +and checkpoint cleanup use the same identity/deadline check; no new connection, +input, file/MCP authority or lease renewal is granted. Do not hold a transaction or +lifecycle gate waiting for the receipt. Lost peers, expiry and restart retain the +ordinary failure/cleanup fallback, never a fabricated cancelled outcome. Preserve +unknown Create ownership and reject schema downgrade with unsettled markers. + +Core rejects `AGENTS_API_MANAGED_RUNTIMES_FILE`; there is no file-managed startup +path or embedded local node. An older file-managed database is not automatically +adopted after its environment variable is removed. Settle and drain that deployment +with its previous release and original backend, preserving business data, private +receipts, identities and storage. The legacy file-managed path has no automatic adoption or force conversion. The supported current path is a database-managed +deployment. Harness selection and public/self-hosted contracts remain unchanged. + +The paired console serves only matched, non-secret distribution artifacts for node +installation. Never serve private installation files or arbitrary paths. Installation +reads `GET /api/v1/sandbox-node/configuration` using an unconsumed enrollment token, +or a retained node credential with `X-OAC-Node-ID`. Reads never consume enrollment; +registered nodes can read their matching configuration during reset. Validate +installation, generation, specification digest and release before writing node files, +registering or reconnecting. Reject drift rather than overwriting retained identity +or using local resource defaults. Registration consumes a token only after these +checks. The node installer requires root or sudo, verifies downloaded files and +starts a root-owned system service for the dedicated `oac-node` user it prepares. +It performs no SSH installation, Session creation or model call. Ordinary-user +installation and removal are rejected before reading credentials or mutating state. +Internal generation preparation and collection still run as the service account. +Web exposes one root/sudo command; do not retain a user-service alternative. This +boundary is specific to Sandbox Provider nodes, not native self-hosted daemons. + +Node management (Web's **Nodes** page; see the +[operator reference](HOSTED-SANDBOX-MANAGER.md)) is a +deployment-level admin surface, separate from Project credentials. The paired +console's Core key stays on its server. Enrollment credentials authorize +initial node configuration reads and registration; durable node credentials authorize +retained configuration reads and node transport. Project keys cannot read nodes, +placement or allocations. + +One execution owner manages every node through the same finite Provider protocol. +The Core host joins through the same standalone node installer and service as any +other host; every node needs a guest-reachable, non-loopback HTTPS origin. +All managed nodes actively connect over authenticated TLS. Persist private node +identity and highest owner epoch; refuse another process using the same identity +or a changed backend namespace. Reserve each NodeID before transport upgrade and +retain that reservation through disconnect cleanup; a duplicate connection must +not replace a live or opening connection. Keep one private state directory per +node and never copy its identity to another host. This is connection exclusion, +not host attestation. The Hub's global mutex protects only in-memory connection +state. Authentication, ownership and Store callbacks run synchronously outside +that mutex, respect cancellation and have a five-second limit; never detach +database writes. Closing the Hub cancels opening and live connections without +waiting for database callbacks. Keep each node reservation until its fenced +disconnect cleanup finishes. Register database presence in an explicit transaction: +a canceled statement must not later publish presence through autocommit. Disconnect +cleanup first locks the node row by identity, then applies the connection/epoch +fence with a fresh READ COMMITTED statement so an in-flight commit cannot be +missed. These transactions must not acquire the deployment-wide manager lock. +Heartbeats establish provider readiness and +last-observed host metrics, never Session activity. An unready provider reports +one fixed diagnostic code, classified by typed probe errors where the node detects +the cause; probe text and host paths stay on the node, and Core stores unknown +codes as `provider_unavailable`. Keep that set closed. Transport reconnects use +bounded backoff. Send relative operation budgets, anchored to the node clock at +receipt and consumed while queued; clocks on different hosts need not agree. Core +still bounds its own response wait. Do not replay mutations after a timeout or +lost response. Retain allocation +and checkpoint operation receipts and observe the original operation instead. +Disconnects and read timeouts are unavailable/uncertain, never resource absence. +Node transport preserves an exact-reference, explicit `CreateSettled` receipt +alongside its original provider error. Settlement authorizes eventual allocation +release, not execution. A confirmed native Create rejected by the subsequent +read-only configuration check, before bootstrap starts, can return that receipt. +Do not infer settlement from timeout, missing compute or successful Kill. Strict +configuration rejection must not prevent already-authorized cleanup: Kill still +checks ownership independently, and release still requires creation settlement. +Runtime resource observation uses the same immutable node placement through one +bounded read-only Provider operation. Preserve main's Runtime observation/history +service and authorization boundaries. The node delegates only to a provider-owned +observation source; absent capability or transport returns unavailable, never a +Core-local fallback. Observation must not create, renew, restore, or touch Session +activity. Preserve the durable compute receipt and provider timestamps; existing +observation clock validation can reject skewed samples without changing idle policy. +Online-state writes compare the handshake epoch atomically in PostgreSQL so a +stale Core cannot publish readiness for a new owner. Node-managed allocations +do not expire merely because the internal observation keepalive is an hour old; +explicit deletion and configured snapshot retention still authorize cleanup. +Persist only bounded, sanitized observation codes for offline, missing or +unconfirmed resources; keep these separate from the lifecycle and do not invent +a successful running observation after a host restart. + +Managed lifecycle state is owned by one serial worker per registered node: +its gate, allocation and pending cursors, connections, initialization progress and +wake hints are not shared with other nodes. A thin coordinator discovers nodes +and owns worker shutdown; it never holds its map mutex during database, provider +or wait operations. Each worker advances independently, including when another +node is online but its provider is stuck. Do not add a shared scan barrier or +global provider pool: lifecycle concurrency is at most one operation per node, +and grows with the registered node count. This is not a fixed global limit. +Keep offline workers so retained resources remain observable after reconnect. + +Allocation scans filter by the fixed node before their 32-row page limit; pending +scans join the unreleased committed placement. Each node advances its own cursor, +including failed observations, and wraps once at EOF. Direct provisioning resolves +the tenant-scoped existing placement before entering that same node's gate; an +existing allocation must agree with the placement. Never choose another node. +E2B direct allocations use one serial lifecycle without a node identity. +The coordinator stops accepting work and cancels and drains all node workers and +direct callers before releasing the sole execution lease. Lease loss is global; +ordinary provider failures stay within their node. A planned deployment drain or +inventory retirement cancels lifecycle contexts synchronously between leased +operations, using the existing lease gate with its five-second bound. Include +an active manual reconcile's cancel function: an asynchronous `AfterFunc` alone +can cancel a later leased query after the gate reopens. Never cancel an in-flight +leased query merely to change deployment configuration. A failed cancellation +fence synchronously closes manager admission and reports owner failure, even +before its coordinator starts. Failed inventory retirement retains the original +lifecycle identity and gate through owner shutdown; gate availability is not +proof that cancellation succeeded. The drain barrier stays closed and cannot +activate a replacement. Release the lease gate before waiting for provider +settlement or lifecycle accounting. Ordinary caller deadlines and owner shutdown retain their +existing cancellation and fail-closed lease-loss behavior. Session locks, deployment +capacity transactions and revision/one-shot receipts remain authoritative, with +no external operation holding a database lock. + +Commit environment-to-node placement with Session creation and its creation retry +identity. Placement is automatic: Core chooses an eligible node, and callers cannot +select one. Existing retries keep their original node even when it is offline. Node capacity counts pending reservations and +unresolved resources; new placement and suspended-to-restoring admission share a +database lock. Confirmed cleanup releases placement capacity. Retained ownership requires exact +provider evidence; a socket path, missing instance or empty listing cannot prove +cleanup or authorize replacement. Historical file-managed ownership must be resolved +using its previous release and original backend before retiring that configuration. +Do not add a startup adoption path to bypass the database-managed selection. + +Do not add node-level drain controls. Refuse node removal with pending allocations, +instances, snapshots, unknown results or cleanup resources. Offline ownership is +retained. Removing a node does not delete compute. Local and remote nodes share +the same resource guard. Keep the deployment-wide reset and configuration +change guard. This boundary does not add cross-node Session +migration, Core multi-active, autoscaling, Kubernetes or harness residency. + +The common +`services/agents-api/internal/sandbox` contract owns the four base operations +(Create, GetInfo, Renew, Kill) and the optional CheckpointProvider +capability. Core orchestration must not import an adapter or SDK. Exact compute +identity, generation construction, inspection, full snapshot capture, restore, +thaw and owned artifact cleanup use that common capability. Initialization and +execution use the authenticated Runtime peer. +Provider-specific names and snapshot identities are opaque to Core. Self-hosted +compute and providers without checkpoint support keep their existing behavior. + +The [microsandbox deployment profile](deploy/microsandbox/README.md) +pins the SDK, runtime, firmware and image. Core remains a pure-Go binary. The +one-shot native Linux helper contains the SDK/FFI and runs under the same private +service namespace; it is not another scheduler or network control plane. Ordinary +pause does not release RAM. Suspension captures and verifies a full snapshot, +stops the exact source, and removes its writable compute closure only after the +artifact is durably identified. Explicit network policy applies on create and +restore. Do not inherit undeclared host resources. +The native SDK owns a dedicated ext4 disk mounted at `/environment`, separately +bounded by `environment_disk_mib` alongside `root_disk_mib`. Workspace, staging +and outputs must share that filesystem; do not weaken cross-device or link +checks to accommodate the layered root. Creation uses `/` until bootstrap creates +the workspace. Existing full snapshots and sandbox cleanup own the disk, with +no external mount or separate storage lifecycle. Native restore may omit a configured +root-disk size because it inherits the verified full snapshot. Accept that omission +only with matching snapshot resource proof and exact source/target identity; inspect +other native limits before retaining the inherited proof on the restored target. +Never treat a missing root size as unlimited capacity or resize retained state. +Snapshot receipt observation uses ownership and artifact integrity checks, so a +resource mismatch cannot hide an existing snapshot from authorized cleanup. +Restore recovery may finish a missing derived resource proof on the exact target +of the verified snapshot, after checking its native limits. It must not repeat +Restore, start stopped compute or alter resource limits; reread the same target +strictly before returning success. + +Suspend only after at least one Turn is terminal, no queued/in-progress/waiting +root or subagent Turn, pending input/file operation or initialization remains, +and real activity has been idle for the configured interval. For node-managed +allocations, record the first root or child terminal transition in the same +transaction using Core's database clock and the existing compute activity field. +For every Environment source, positive native completion timestamps remain +unchanged in public history, even when host clock skew places them before Core's +Turn creation time. They cannot drive idle admission across hosts; repeated terminal projections never reset that timer. +Read activity together with the database observation time. Candidate filtering and +the Session-locked phase recheck compare elapsed database time with the configured +idle duration; callers must not supply a Core-wall-clock cutoff. Anchor the initial +snapshot retention deadline to that same database observation. Core and database +host clocks need not be synchronized for these decisions. +Heartbeats do not reset activity. The daemon must close admission and drain native +cleanup, output receipts and file work before acknowledging planned suspension. Never change a +harness or keep an agent process alive across Turns solely to meet this feature. +The acceptance boundary is a next Turn in the same Session with history, files +and configuration intact, without replaying an earlier request. + +The existing Worker lease, Session lock and per-node lifecycle gates own both providers. +New Turn claims, file-write intents and capture admission serialize under the +Session lock. Turn and file-write admission share the same compute-phase check; +existing receipts remain readable. New pending work cancels capture and wakes +the same source. Normal preparation waits for the +compute phase to be running, after the authenticated resume handshake; a pending +input remains pending if its promotion conflicts with a lifecycle transition. +Private compute phases and revision-checked JSON receipts live on the existing +allocation. Persist quiesce/capture/restore intent before effects; only the fresh +receipt performs a capture or restore. Recovery observes the exact attempt and +never retries an unknown creation, capture or restore. A consumed snapshot cannot +roll a running generation back. Deletion, revocation and retention expiry take +precedence over wake, including at the final database compare-and-swap. Retain +unknown cleanup identities until owned resources are confirmed absent. + +Database-owned guest CPU/memory and supported disk settings, max_active reservations, max_retained +allocation count and snapshot retention bound each assigned node. Unknown operations +retain capacity reservations. Source teardown must be confirmed before releasing +active capacity. Delete consumed artifacts and old compute closures; do not grow +a chain of old writable disks across suspension cycles. No Kubernetes, distributed +scheduler or snapshot replication belongs in this V1 profile. + +Queued work and live Environment file access request wake. History and published +artifact reads do not. Planned suspension uses private daemon wire 0.8.0 with an +Environment and suspension token; a PID/start-time fenced local control signal +wakes the parked daemon, which reauthenticates before admitting new work. A +transient disconnect before confirmation retries the same armed suspension with +bounded attempts and backoff; permanent authentication or protocol rejection +still closes it. Snapshot +lifetime has no daemon wall-clock timer: Core owns its retention deadline. A lost +quiesce acknowledgement may thaw the same source using explicit rollback control; +it does not authorize capturing it. Ordinary disconnect keeps the existing +conservative shutdown behavior. Authentication rejection cannot create a new +Runtime or replay a request. + +All normal `make check` gates still apply. Linux qualification additionally runs +the pinned helper module tests/build through `check-microsandbox-provider`, a real +KVM full-snapshot/reclamation probe, and idle-to-next-Turn integration acceptance. +Synthetic process-memory probes support the backend claim only; they do not prove +agent continuity. Independent blind review uses the clarified idle-only scope. + +Managed Runtime allocation, dedicated daemon credential hash and exact Session +binding commit atomically before Provider.Create, using the existing execution +lease and Session lock. Only the fresh allocation receipt permits Create; retries +and Core restart observe that same reference without replay or credential rotation. +The operator's stable provider key identifies one backend/installation; retain its +adapter for cleanup, and use a different key when changing the target. Never treat +absence on another backend as successful reclamation. +Disabling the default provider stops new hosted admission/bootstrap; it must not +block existing Session cancellation, tool results or input retry outcomes. +Input HTTP response budgets follow the persisted Environment type, covering the +admission wait for both hosted and self-hosted Sessions independently of operator +creation switches or remote executor configuration. + +With an explicitly configured default managed provider, the same Worker scans +committed pending hosted Environments that have no allocation. This includes idle +Session creation and recovery after commit-before-bootstrap interruption; an +existing allocation never enters that startup path. Keep the scan bounded and +serialized by the existing lifecycle owner. Hosted provisioning requires no caller +connection action. An initial reservation without a Turn leaves its Session idle, +as allowed by the pinned contract; do not emit an in-progress event before a Turn +starts or treat a daemon connection as native readiness. + +The same serialized scan publishes authenticated connection observations using +the existing durable generations after verifying the exact Session/device binding +and settled bootstrap. Socket loss remains observable during a provider outage; +Core restart fences old observations. Do not create a separate connection owner. + +Terminal managed cleanup atomically revokes authority, persists Environment failure +or expiry, settles pending input and requests cancellation before external cleanup. +Preserve original input deadlines and retry outcomes. Temporary provider outages, +unknown Create results and stopped compute do not prove permanent failure. The +pinned stream has no Environment expired event; do not invent one. Exact hosted +failure codes and ordering remain explicitly unverified. + +Allocation state is private compute ownership, separate from public Environment +connection/native readiness. Adapters qualify bootstrap completion; Core does not +infer it from an engine or provider name. Connected, observed compute receives +service keepalives between Turns. Keepalives cannot revive a one-hour lapse or a +cleanup request. The Docker provider keeps its current idle behavior; only an +explicit checkpoint policy may suspend completed, idle work as described below. A stopped/missing container +does not authorize discarding retained workspace or history. Session deletion or +expiry requests cleanup, revokes the scoped device and cancels pending work before +Provider.Kill; the existing Worker serializes these lifecycle operations and drains +them before releasing its execution lease. + +Keep the allocation after public Session deletion. Mark it released only after +owned compute/volume cleanup and evidence that its original Create has settled. +An unknown creation retains cleanup ownership even after an absence observation; +continue bounded scans for late resources without issuing another Create. This +conservative internal lifecycle does not define user-managed enrollment or prove +complete upstream expiry/error semantics. + +Qualify the actual Runtime before cutover: execution, file access, owned +cancellation, restart with retained history and files, and clear failure when +required history is missing. Daemon tools run with the launching account's full +permissions. Managed isolation is provided by the outer Environment. A user who +installs on a host does not receive a sandbox or protection from their own tools. +Keep failed probes and unverified platform combinations explicit. diff --git a/services/agents-api/README.md b/services/agents-api/README.md index f9c839345..6845dd610 100644 --- a/services/agents-api/README.md +++ b/services/agents-api/README.md @@ -74,7 +74,7 @@ The default output is `${OAC_DEV_HOME:-$HOME/.oac}/build/oac-core`: Use these executables in place of the corresponding `go run` commands below. The build needs Go and access to its pinned module dependencies; it does not need Node, Docker, the product service or frontend. An isolated source context enforces -that boundary on every build. [Contributor rules](../../CONTRIBUTING.md#independent-build-artifacts) +that boundary on every build. [Contributor rules](../../docs/maintainers.md#independent-core-build-artifacts) define the allowed shared packages and required checks. Runtime database/key configuration and a separately installed execution daemon are still required; these binaries do not establish full protocol coverage. For a standalone Linux @@ -282,6 +282,20 @@ A provisioning failure fails the Session with a safe step and exit-status reason ([initialization failure](../../contracts/agents-api/environment-templates.md#initialization-failure--september-23)); other exact hosted failure and expiry semantics remain unverified. +The independent Docker Provider consumes an immutable Runtime image and retains +one caller-owned allocation reference through partial creation and cleanup. Persist +that reference before Create and serialize its lifecycle; resolve a lost response +with observed state, without rewriting bootstrap credentials or replaying startup. +Provider state is compute state, not public Environment readiness. Named Runtime +volumes need explicit owned cleanup after container removal. A second mount of the +same workspace volume subdirectory provides the public `/workspace` path to native +tools; trusted staging and atomic rename retain the original parent mount. Do not +copy files or widen private-path reads to preserve an alias. Initialization command +timeouts can leave processes alive and require allocation cleanup before reuse. +The [managed Runtime build and operator configuration](deploy/codex/README.md#managed-runtime-image-and-docker-adapter) +defines the explicit opt-in for basic hosted admission. Building an image alone +does not qualify its isolation or enable public creation. + ## Internal execution device connection The standalone service can accept existing daemon connections without a Parsar @@ -322,7 +336,7 @@ connections alone do not start a Turn. Submit text, cancellation or function res through the official Session events endpoint; the worker assigns a same-tenant host and preserves that binding. Managed Docker has three-harness evidence. This generic device provisioning path is for `none`; self-hosted Sessions require the dedicated enrollment below. -User-managed enrollment has a separate [qualification record](../../contracts/agents-api/user-managed-runtime-v1.md); complete protocol semantics remain partial. See the [ownership rules](../../CONTRIBUTING.md#product-and-execution-service-separation). +User-managed enrollment has a separate [qualification record](../../contracts/agents-api/user-managed-runtime-v1.md); complete protocol semantics remain partial. See the [ownership rules](IMPLEMENTATION.md#product-and-execution-service-separation). ### Enable Claude SDK execution @@ -330,7 +344,7 @@ Build and extract the runtime archive into a fresh managed directory on a matchi executor host. The archive contains the compiled bridge and pinned production SDK/MCP/native dependencies; Node is installed separately. Linux x64/glibc with Node22 is the accepted platform. See the -[runtime artifact contract](../../CONTRIBUTING.md#private-claude-sdk-runtime-artifact) +[runtime artifact contract](../../packages/claude-sdk-adapter/README.md#runtime-artifact) for build outputs, version checks and platform restrictions. ```bash @@ -392,6 +406,13 @@ the `OpenAgentCore checks` CI workflow always supplies its own PostgreSQL servic full `make check` before review as well. Product OpenAPI generation excludes this service; its supported HTTP contract is generated separately. +Run `make openapi` after handler annotation changes. It reuses the original +Core-only swaggo v1.16.4 generator, then splits the result by namespace: `/v1` +into `openapi.yaml`, `/core/v1` into `core.openapi.yaml` and `/api/v1` into +`runtime.openapi.yaml`; the last two use base path `/`, and each document keeps +only the security schemes its operations use. All generated schemas remain free +of product routes. + ## Public text execution With the daemon gateway enabled, the service owns one worker per execution database @@ -598,7 +619,7 @@ Anonymous requests suppress native OAuth/credential injection with a blank Authorization header, without deleting native state. Servers rejecting that header, normalized name collisions, changing inventories and original MCP metadata fidelity remain gaps. Items retain the observed native JSON, which may differ from the original -MCP envelope. See the [Claude SDK profile](../../CONTRIBUTING.md#claude-sdk-adapter-foundation). +MCP envelope. See the [Claude SDK profile](../../packages/claude-sdk-adapter/README.md#bridge-and-native-lifecycle). The current subset rejects native OAuth login, inline authorization, nonempty headers or request metadata, URL userinfo/query/fragment, the `environment` origin, stdio @@ -610,9 +631,56 @@ service compute; it does not provide filesystem isolation or guard against concurrent operator configuration mutation. These limits are implementation gaps, not changes to the pinned official protocol. +A dedicated local Runtime uses one Environment-scoped device credential and an +immutable binding to that Environment's Session. It is excluded from general +device selection; another Session cannot claim it, including within the same +tenant. Deleting its Session invalidates credential lookup and heartbeat renewal. +Provision a new scoped device atomically rather than widening an existing shared +device credential. Revocation does not authorize silent placement replacement. + +The private local Environment reference contains its identity and, for policy-aware +execution, its immutable network policy. Trusted Runtime deployment configuration +freezes the Environment, Session and workspace root; +requests cannot supply a replacement root. The V1 path uses only the exact local +Environment reference. Use the same preparation/start lifecycle for native execution and the +existing bounded workspace controls for directory access. Local idle directory +reads use the common Go filesystem implementation without a temporary Harness. These private capabilities alone do not authorize public requests or establish +Provider lifecycle. Self-hosted enrollment supplies the exact local binding. +Core rechecks the persisted Environment/device binding for preparation and active +reads; capability discovery cannot select or authorize a general device for this +placement. Local work uses the existing pending-input reservation and Worker +ownership without a remote connection resolver. The hosted profile supports `network.access: enabled`, `disabled` and exact-host +`restricted`; each image must qualify the supported policies before public deployment. Omitted +network settings mean enabled upstream and must not be silently treated as disabled. + +User-managed Runtime enrollment authenticates the existing principal executor key +against the exact live Session/Environment and its recorded creator. Keys retain +stable management IDs, immutable principals, optional exact-Environment restrictions, +rotation/revocation and digest-only storage. They grant connection authority, never +Session API access. No raw-token import or secret read-back is added. + +Enrollment atomically creates or recovers one dedicated device and immutable Session +binding under the Session lock. The frozen workspace and capability directory selection come from the Session +configuration and must match the local binding. Runtime snapshots declared local +Skill or Plugin directories before creating the native executor. No `runtime_allocation` is +created for user-owned compute. A retry cannot replace a device, change its bound +key or adopt another native history. Gateway authentication and dispatch recheck +current key authority; rotation/revocation and deletion deny further use. + +The daemon, selected harness, local tools and workspace run together. The Dispatcher +passes the existing typed `LocalEnvironment` after exact tenant/Session/Environment/ +device checks. Native preparation, Files and Artifacts reuse the same protected +local workspace and existing lifecycle owners. There is no registry/Noise relay, +transient harness credential, service-side harness or remote tool forwarding path. +Keep model credentials, daemon authorization and native histories private; connection +success alone establishes neither native readiness nor filesystem isolation. + ## Hosted sandbox nodes The release includes `oac-node` for local and remote hosts. Add nodes with Web's one-command flow in the [nodes guide](../../docs/getting-started/nodes.md); the [operator reference](HOSTED-SANDBOX-MANAGER.md) covers provider selection, manual registration, administrator credentials, fixed Session placement and reset. + +Implementation rules for hosted sandbox nodes and optional suspension are in +[Agents API implementation constraints](IMPLEMENTATION.md#hosted-sandbox-nodes-and-optional-suspension). diff --git a/services/agents-api/deploy/claude/README.md b/services/agents-api/deploy/claude/README.md index 72a87aa21..41273787f 100644 --- a/services/agents-api/deploy/claude/README.md +++ b/services/agents-api/deploy/claude/README.md @@ -55,38 +55,48 @@ self-hosted platforms or every feature combination. The existing `environment:none` function/MCP profile is separate. This is not complete upstream protocol compatibility. -## Adding another native engine - -Start with the existing `agent.Factory`, `agent.Session` and -`agent.PreparationFactory` interfaces; do not create another scheduler or loop. -Registration itself is small: +## Runtime and adapter rules + +Build the pinned SDK bundle with `scripts/build-claude-sdk-runtime.sh`, then use +`scripts/build-claude-runtime.sh`. Native self-hosted installations use the same +bundle and daemon protocol. See [Build and configure](#build-and-configure). +The shared local binding selects the actual workspace. SDK history, home and +scratch remain under `OAC_RUNTIME_HOME/runtime/claude-sdk` for session ownership, +without restricting tools. Authentication remains under `OAC_RUNTIME_HOME/daemon`. +The daemon requires neither nested sandbox privileges nor host security changes. + +The `local_runtime_v2` capability identifies the current workspace and direct MCP +launcher contract. Reject earlier workspace bundles; matching SDK versions alone +do not establish adapter compatibility. +The adapter advertises `local_runtime_v2` after checking the installed bridge and +native binary. Windows additionally needs Git Bash for its Bash tool. Registration +combines that check with the local binding; Core consumes the same readiness +contract on every platform. The profile supports Bash/Read/Edit, preparation, +shared Files/Artifacts, cancellation and history continuation. Other capabilities +remain subject to their actual engine qualification; bypass execution does not +silently qualify a new MCP or function combination. + +Recovery uses the SDK's history APIs. An explicitly supplied native identity must +exist. If Core requires existing history without having recorded an identity, the +adapter accepts only one nonempty native history for the exact bound cwd. Missing, +foreign, ambiguous or metadata-only history rejects before model input. The Runtime +volume and shared Environment/Session binding establish ownership; this lookup +cannot select another Session's home or infer ownership from a model response. + +Claude hosted functions compose the existing SDK function bridge with the common +workspace execution profile. Only declared function tools and the verified native tool +inventory are available. The bundle advertises this combination separately from +basic workspace execution; function preparations require that verified combination. +Service-origin hosted MCP remains unqualified; Environment Plugin declarations +use the separately qualified initialization path above. Function callbacks do not change file, +credential, history, subagent or network authority. -```go -registry.RegisterKind(descriptor, adapter.NewFactory(config)) -registry.RegisterPreparation(descriptor.Kind, true, adapter.NewPreparationFactory(config)) -``` +## Adding another native engine -These calls refer to the existing daemon registry. `descriptor` must advertise -only capabilities established by your adapter and deployment tests. A dedicated -local Runtime automatically reuses workspace authorization, idle directory reads, -file writes and output export; it does not need an engine-specific Files API. - -The adapter translates native configuration and events, owns its child processes, -confirms input receipts, and implements cancellation and preparation ownership. -`Prepared.Start` transfers ownership once; `Close` releases only unused preparation; -`PreparedCancellation.Cancel` follows the same resource through transfer and waits -for output settlement. Reuse the shared process runner and existing error types. -Native history belongs to the bound API Session, not the device descriptor. - -Add the qualified placement and value constraints to the qualified profile in -`services/agents-api/internal/engine/` and register it in its catalog. That declaration gates public support independently -of Runtime capability flags. Do not add engine branches to handlers, stores, -resource managers or scheduling. A genuinely new public capability may require a -bounded extension of the shared contract; registration alone cannot make an -incompatible harness conformant. - -Acceptance must use the pinned official OpenAI client, raw HTTP and a real model: -execute and continue; list/create files and retrieve artifacts; cancel and prove -process settlement; reconnect/restart without replay; verify tenant, credential and -history isolation. Missing history fails closed. Run focused race tests, `make -check` and independent review before merging. Keep unsupported combinations explicit. +Follow [Add a native Harness](../../../../contracts/agents-api/harness-onboarding.md). +The Claude adapter is one of its [native references](../../../../contracts/agents-api/harness-onboarding.md#native-references): +it implements the shared `agent.ExecutorFactory`, `Executor` and `Turn` +interfaces and registers them in +[`cli/claude_sdk.go`](../../../../apps/parsar-daemon/internal/cli/claude_sdk.go). +Acceptance requirements are in +[Harness integration](../../../../contracts/agents-api/harnesses.md#acceptance-checklist). diff --git a/services/agents-api/deploy/mcode/README.md b/services/agents-api/deploy/mcode/README.md index 54765061a..229e8db35 100644 --- a/services/agents-api/deploy/mcode/README.md +++ b/services/agents-api/deploy/mcode/README.md @@ -2,10 +2,9 @@ MiniMax Code uses the same Core/Runtime execution contract as the other engines. The text profile supports `environment:none`; the dedicated Docker profile adds -workspace execution and the shared Files/Artifacts path. See -[workspace qualification](../../../../contracts/agents-api/mcode-workspace-v1.md) -for historical acceptance evidence and its limits. Inner sandbox denial results -from that qualification do not describe the current bypass profile. +workspace execution and the shared Files/Artifacts path. The historical +workspace qualification (evidence under `~/.parsar/remediation/20260919/mcode-workspace/`) +does not describe the current bypass profile. Public MCP/functions, image input and native Subagent execution remain outside this batch. @@ -114,6 +113,57 @@ private cwd. Missing/foreign history fails; recovery by guessing an ID from nati session listings is not qualified. Public usage breakdown is unavailable because native ACP context occupancy and cumulative cost are not per-Turn usage. +### Adapter rules + +MiniMax Code's opt-in Agents API profile qualifies native ACP 0.4.12 for +`environment:none` text execution. It reuses the shared lifecycle without public +workspace, functions or MCP. Enabled Subagents use the separately qualified +common observation path below. Native configuration disables +file/shell authority and external +capability discovery; the child receives a private Session home and a restricted +environment. Active-input application requires a native ACP receipt. Normal +Turns retain one ACP connection and native Session. Cancellation requires native +root and child completion evidence before reuse. Without a qualified root-state +reader, the ACP cancellation response is insufficient: stop the invalid native +process and wait for its exit before settling the Turn as non-reusable. Common +Runtime recovery then loads the exact owned native history in a new Executor. Do not infer history IDs or qualify hosted execution from this text +profile. See [Acceptance](#acceptance). + +MiniMax companion readiness uses private protocol 2; old companions are rejected +even when the upstream version matches. Cancellation retires the executor and +settles its native workers and detached Bash groups before acknowledgement; +later work recovers the same native history in a new owner without replay. + +The MiniMax workspace profile builds one CLI from the fixed upstream source and +lockfile through the existing companion packaging path, and connects native +workspace tools through its standard MCP client. The process and native Session +share one private control directory; public workspace files cannot configure that +process or become privileged project instructions. A trusted adapter-owned bridge +runs the original six tool implementations with the launching user's ordinary +permissions. It adds no inner sandbox on any platform. Keep native history bound +to the control directory and Files/Artifacts bound to the public workspace. Core and shared file helpers remain engine +neutral. This internal MCP transport does not admit public MCP configuration. +Record the upstream revision, native admission patch hashes and worker-source +provenance. The bounded patch checks the shared descendant-task limit inside the +existing native SQLite admission transaction before start, without another +scheduler. ACP initialization must acknowledge the applied limit before input. +The native tool catalog selects the same admitted workspace tools for every child, +not only the root's configured profile; this is tool selection, not filesystem +isolation. Only the Session's authorized internal +workspace MCP entry crosses the native child selector; this does not grant +external MCP access. Initialization must acknowledge the admitted tool inventory +before input as well. Subagent reads use the Session's native database; the daemon +does not protect it from other tools running as the same user. Multi-agent +workspace execution installs only the existing authorized workspace MCP entry in +that private native configuration so children inherit the same tools; public MCP +and Environment-origin MCP combinations remain separately qualified. Complete the hosted +[acceptance checklist](../../../../contracts/agents-api/harnesses.md#acceptance-checklist) before enabling +hosted execution. The standalone companion uses its own npm lock; `make check` +runs its lifecycle tests and script checks, while its exact-source Linux build and +native qualification for the actual supported platform and outer deployment +are required when the companion changes. Historical tests of both inner network +policies do not qualify the current bypass implementation. + ## Acceptance Recorded results below are historical evidence for their exact binaries and @@ -153,8 +203,9 @@ and covers new/resume, model/instruction refresh, Skill discovery and MCP using its existing synthetic provider fixture. That text acceptance used an in-process Core HTTP server and a separate real -daemon. The subsequent [Docker workspace qualification](../../../../contracts/agents-api/mcode-workspace-v1.md) -uses independently built Core binaries, its own database and managed Runtime. +daemon. The subsequent Docker workspace qualification +(`~/.parsar/remediation/20260919/mcode-workspace/`) used independently built +Core binaries, its own database and managed Runtime. It covers public Files/Artifacts, cancellation, reconnect, exact-history recovery and isolation with real Kimi and MiniMax APIs. Read its recorded Kimi continuation limitation: new model-issued commands are not automatic recovery replay. Neither diff --git a/services/agents-api/tools/e2b-provider/README.md b/services/agents-api/tools/e2b-provider/README.md index 41868477a..011f49dda 100644 --- a/services/agents-api/tools/e2b-provider/README.md +++ b/services/agents-api/tools/e2b-provider/README.md @@ -126,3 +126,27 @@ report. These checks do not establish cloud authentication, native isolation or real execution. Live acceptance must use owned E2B compute and the same Runtime, with actual lease renewal, restart/unknown outcome reconciliation and confirmed cleanup. Initializer and three-harness qualification remain deployment checks. + +## Adapter rules + +Core-managed E2B is a separate hosted deployment choice, using the official pinned +Python SDK through a packaged private helper. Do not restore the retired custom +HTTP/Connect or envd implementation. The adapter implements the same five operations; +cloud allocations use direct placement with no synthetic node, while Runtime execution +and file access keep the shared daemon contract. Its immutable Runtime template build +is deployment configuration, not a public Environment Template. Keep the account API +key encrypted in the database, write-only through admin input and absent from helper +arguments, logs, metadata and receipts. SDK connection materials and attempted-create +receipts belong in the private durable provider state directory; never replace missing +state to make cleanup appear successful. Create runs once. Unknown control-plane +outcomes remain blockers even if a listing is empty. Explicit matching-reference +CreateSettled evidence proves that the original initialization cannot mutate further; +confirmed absent compute may then be released. Ordinary 404 responses do not prove it. +The helper's pinned SDK, dependencies and licenses ship with Core; users do not install +Python packages after selecting E2B in Web. Application-managed self_hosted tooling +remains independent and uses the same Runtime. Qualify each changed path using actual +provider and model execution before claiming acceptance. Run `make check-e2b-provider` +with `OAC_TEST_E2B_SDK_PYTHON` pointing to the pinned SDK environment; the packaged +helper build runs the provider tests as well. The SDK gate also covers the +application-managed launch tests. `make check` covers shared initialization and +managed initialization using only the Python standard library. From eaeee8e5b73327a7e2df5f4f46cd7a8bf2582a15 Mon Sep 17 00:00:00 2001 From: saladday <1203511142@qq.com> Date: Tue, 29 Sep 2026 16:32:50 +0800 Subject: [PATCH 2/5] docs: remove historical contracts and internal board references Delete mcode-workspace-v1.md (historical evidence only) and startup-configuration.md (removed route), fix inbound links and drop task-board/Feishu process steps from contracts. --- .../agents-api/function-result-images.md | 2 +- contracts/agents-api/mcode-workspace-v1.md | 162 ------------------ .../official-semantics-alignment.md | 4 +- contracts/agents-api/startup-configuration.md | 10 -- contracts/agents-api/workspace-placement.md | 8 +- packages/mcode-harness/README.md | 4 +- 6 files changed, 8 insertions(+), 182 deletions(-) delete mode 100644 contracts/agents-api/mcode-workspace-v1.md delete mode 100644 contracts/agents-api/startup-configuration.md diff --git a/contracts/agents-api/function-result-images.md b/contracts/agents-api/function-result-images.md index 49ebc7bb2..ff98e51a0 100644 --- a/contracts/agents-api/function-result-images.md +++ b/contracts/agents-api/function-result-images.md @@ -61,7 +61,7 @@ Files/Artifacts and cold Core/Runtime continuation. It checks Claude's rejected error/remote result directly on an outstanding call before accepting a valid image on that same call; no mixed-message rejection substitutes for this check. -The accepted combinations and run evidence are recorded in the task board. This +Run evidence is retained outside the repository. This coverage does not qualify self-hosted/user-managed image results, all native image limits, provider parity, arbitrary managed output rewrites, crash recovery or full Agents API compatibility. No downloader, image converter or second tool loop belongs in Core. diff --git a/contracts/agents-api/mcode-workspace-v1.md b/contracts/agents-api/mcode-workspace-v1.md deleted file mode 100644 index 7bd3067d6..000000000 --- a/contracts/agents-api/mcode-workspace-v1.md +++ /dev/null @@ -1,162 +0,0 @@ -# MiniMax Code workspace Runtime qualification - -Historical evidence only: the current daemon runs tools with its starting user's -permissions and no inner sandbox. The isolation probes below apply to the recorded -old binaries, not current native or managed support. - -Status at the recorded revision: implementation, public qualification and independent review passed, -2026-09-20. Baseline main -`55502c87c3f722bbefc5449348309e3fcc03a7ee`. Board batch: -`MCODE-WORKSPACE-V1-001`, under `ENGINE-EXTENSIBILITY-001`. - -## Goal and boundaries - -Complete the requested MiniMax Code Docker workspace loop through the existing -Core/Runtime contract: create a Session, prepare its Environment, execute native -tools, upload/list workspace files, export immutable Artifacts, cancel, reconnect -and continue the exact native history. Prove independent deployment without Parsar. -PR #702 qualified text execution only; it does not complete this goal. - -Core owns the public protocol, authorization and durable resources. The Docker -Provider creates and reclaims one dedicated Runtime; daemon, native harness and -local tools share that Runtime. Native configuration and isolation belong in the -adapter. Reuse preparation ownership, localworkspace binding and common file and -export helpers. Do not add engine-name branches to public handlers, persistence or -scheduling, or implement another model/tool loop. - -This batch excludes E2B, public MCP/functions/Subagents, a general plugin or sandbox -framework, product migration and unrelated refactoring. Existing qualified Codex -and Claude behavior remains protected. Optional feature equality is not required; -the explicitly requested workspace and Files/Artifacts loop is required. - -## Design gate before hosted wiring - -The inspected native source is `MiniMax-AI/minimax-code` revision -`33b259bbbeb1c16433390869938191d09bdb0680`, package 0.4.12. A deterministic -Linux ACP probe returned a private synthetic canary through Read with sandboxing -enabled and `denyRead` configured. The disabled control also returned it. This -probe used no real secrets or real model and is not acceptance evidence. - -The native registry supplies sandbox operations to Bash only; native file tools -execute in the harness process that owns model configuration and history. Docker -alone does not separate those tools from that process's private data. - -A first prototype moved six native tools into an isolated worker. Ordinary tool -operations, disabled networking and detached-child cancellation passed in Docker, -as did real Kimi and MiniMax Write/Read/Bash calls. However, the native process -still auto-started a workspace `.mcp.json` command and followed a `CLAUDE.md` -symlink into private data. Both bypasses were reproduced with synthetic canaries. -Native pre-tool file capture also reads paths before tool execution. The registry -patch is therefore not an adequate authority boundary and will not be shipped. - -The revised candidate retains the published CLI and uses its existing MCP client. -The native process and ACP Session use one private control directory. A single -trusted stdio MCP bridge exposes the original Read, Write, Edit, Bash, Grep and -Glob implementations under workspace-prefixed names, using upstream JSON schemas. -The bridge runs tools in the upstream-vendored Linux sandbox. Only this isolated -worker receives the real workspace as its execution root. Automatic native -project loading remains confined to the private control directory; caller files -cannot configure native processes or redirect privileged instruction reads. - -This uses MCP as an adapter-internal transport, not public MCP feature admission. -Keep native model execution, ACP and history. Reuse native tools rather than -reimplement them. Workspace project instructions can be read through the isolated -tools; do not silently import them into the privileged control process. Preserve -accurate workspace guidance in the trusted tool descriptions. - -Before hosted wiring, prove the published CLI actually selects these tools, -rejects private-file reads, ignores workspace MCP/instruction canaries, enforces -network policy and settles all owned workers on cancellation or transport loss. -Repeat real-provider acceptance against this candidate; earlier prototype results -do not qualify it. If the bridge needs a new model loop or a general compatibility -framework, stop and reassess rather than weakening isolation. - -## Acceptance and stop conditions - -| Area | Required observable result | -| --- | --- | -| Shared lifecycle | Preparation consumes no model input; Start transfers ownership once; cancellation/release settle once after effects stop; events stay ordered. | -| Workspace | Real model reads uploaded input, edits/creates files and runs a native command in the bound workspace. | -| Files and Artifacts | Official SDK and raw HTTP verify actual content, path filtering and pagination; exports remain immutable and scoped to their owner. | -| Recovery | Reconnect and cold continuation retain exact native history and workspace; no duplicate execution; missing or foreign history rejects before model input. | -| Isolation | Tenant/auth checks, daemon/model credentials, native history, staging and cross-Session paths remain protected; tools cannot escape through filesystem or process aliases. | -| Network and cancellation | Each exposed network mode is enforced; cancellation leaves no late file writes or surviving detached tool children. | -| Deployment | Separate Core and execution database with Docker-managed Runtime, no Parsar service or product database dependency. | -| Validation | Targeted/race checks, real Kimi and MiniMax API runs, `make check`, applicable generated artifacts and a fresh independent Astra high blind review. | - -Synthetic fixtures may isolate failures but cannot replace real provider acceptance. -Record exact source/image versions, commands, results and unverified behavior. Keep -credentials in private operator files, outside commits and reports. New mechanisms -must address this batch's demonstrated functional or security risk. - -The batch ends when this loop passes and its bounded PR is merged. Record -nonblocking issues in the board without expanding the batch. Do not close the -parent protocol objective or claim complete official protocol compatibility. - -## Qualification evidence - -The initial qualification candidate uses published CLI 0.4.12, the source revision above, Node.js -22.23.1 and Docker image -`sha256:9bfcf2c3a1bcf2ebf844878897d561dacb6a525c938c5d059645d0f88e1fdb99`. -The npm registry still reported 0.4.12 as latest on 2026-09-20. The Core was built -with `scripts/build-agents-api.sh` and run from a source-free package with its own -PostgreSQL database and credentials. - -| Check | Result | -| --- | --- | -| Official SDK 3.13.0 and raw HTTP, real Kimi, standalone Docker workspace | Passed: Session admission/auth/tenant scope, Files upload/list/filter/order/pages, native input reads, both network policies, protected paths, Core restart, delayed Runtime reconnect, cancellation with no late writes. | -| Public Artifacts, real Kimi and MiniMax independently | Passed: binary/empty/nested/source-file exports, exact content, immutable versions, ownership, restart, cancelled-Turn exclusion, Environment expiry and orphan-free deletion. | -| Core SIGKILL during execution, real Kimi | Passed: failed Turn queries, no automatic/idempotent replay, stopped effects, exact history continuation, retained Items/Artifacts and foreign-Session isolation. | -| Runtime SIGKILL during execution, real MiniMax | Passed the same recovery checks, including continuation that did not execute the interrupted command again. | -| Lifecycle and repository checks | `make check`, targeted Go race tests and the existing published-CLI product ACP regression passed. `make openapi` produced no generated changes; no query/schema change requires sqlc regeneration. | - -Evidence is retained on `zju_a100_2` under -`~/.parsar/remediation/20260919/mcode-workspace/`: `public-hosted-latest.json`, -`public-artifacts-latest.json`, `public-minimax-artifacts-latest.json`, -`public-recovery-latest.json`, `public-minimax-recovery-diagnostic-latest.json`, -`race.log`, `make-check.log` and `product-native-regression.log`. These private -fixtures reuse the fixed official client and existing public Files/Artifacts -acceptance helpers; they do not substitute model responses. A locally cached base -image matching the pinned Node manifest was used with Docker's legacy builder; -only its temporary build context omitted BuildKit chmod syntax. - -Kimi's Runtime-kill continuation did not pass the no-repeat assertion: after a new -input, a new tool-call identity executed the interrupted command again. The -pre-input recovery and idempotency checks passed. Keep the failed evidence and -source inspection confirms ACP preserves native call IDs and the native loop -executes only calls from a fresh model response after the new input. This was a -new model-issued command, not automatic recovery replay. The passing MiniMax run -does not erase the Kimi failure. Model obedience is not an exactly-once execution -guarantee; no model-output patch or public command filter was added. - -The initial-image real MiniMax isolation probe also passed: workspace MCP and -instruction-symlink canaries did not affect the privileged process; native file -tools and `/proc` aliases could not read synthetic credentials/history; visible -process environments contained no privileged values. Missing and foreign native -Session loads rejected before any model request. All task-owned probe containers -were removed. Evidence: `final-isolation/docker-final-isolation-result.json`. - -The final candidate is -`sha256:da8ab6840fe33bf493771419d68fe271ad155ff34f2c258e43bd6cd0a79260ec`. -It directs native temporary files to the existing writable scratch directory and -includes the upstream tool license. The earlier image failed the same native -temporary-file regression; the final image passed under both network policies: -`mktemp`, Node temporary paths, 272 KB Bash output spill and native Read. Private -canaries remained unreadable, real private directories were unchanged, the control -directory stayed read-only, and network/seccomp restrictions remained enforced. -MCP cancellation and EOF stopped detached children with no delayed writes. - -On this final image, real MiniMax public Artifacts and both Core/Runtime SIGKILL -recovery suites passed with no cleanup errors. Full `make check`, focused race -tests and OpenAPI generation also passed after the change. The default full check -skips the opt-in packaged native test; both actual Docker runs above executed it. -The broader initial-image acceptance remains evidence for unchanged public wiring -and isolation, rather than a claim that every suite reran on the final image. -Final evidence: `public-minimax-recovery-latest.json`, -`public-minimax-artifacts-latest.json`, and `proof/temp-regression/`. - -A fresh independent GPT-6 Astra high review of the complete diff found no blocking -issues. Its independent mcode/CLI/engine/execution race tests and companion checks -passed; the reviewer did not rerun the full gate or real-provider deployment. -Those results above were run by the implementation owner. This qualification does -not establish complete protocol compatibility. diff --git a/contracts/agents-api/official-semantics-alignment.md b/contracts/agents-api/official-semantics-alignment.md index ae3500ce5..3cc81c16c 100644 --- a/contracts/agents-api/official-semantics-alignment.md +++ b/contracts/agents-api/official-semantics-alignment.md @@ -31,7 +31,7 @@ query envelopes and accumulated usage; their owned Sessions and Agent were also deleted. Separate missing File/Skill probes used randomly generated IDs. Private request/status/body evidence and cleanup results are retained under `~/.parsar/remediation/20260922/official-semantics-alignment/`; no API keys or -credential values belong in the repository or task board. +credential values belong in the repository. ## Explicit remaining differences @@ -51,7 +51,7 @@ credential values belong in the repository or task board. malformed queries, page limits and overlapping mutation behavior need qualification. The error mapping above must not be extrapolated to every status or resource. - Template references with inline installation overrides, optional Skill version - semantics and the other active board entries remain outstanding. + semantics and the other open items remain outstanding. - Files.create cannot tell a file written by an earlier Files.create from any other existing file, so both report the untracked-file message; see the [write semantics](environment-files.md#write-semantics--september-23-2026). diff --git a/contracts/agents-api/startup-configuration.md b/contracts/agents-api/startup-configuration.md deleted file mode 100644 index 6e3d4a959..000000000 --- a/contracts/agents-api/startup-configuration.md +++ /dev/null @@ -1,10 +0,0 @@ -# Core startup configuration (removed) - -The startup configuration read is removed. The separate -[installation read](installation.md), `GET /core/v1/installation`, reports the public -URL and the installer's process settings. Neither -`GET /core/v1/admin/startup-configuration` nor the earlier -`GET /v1/agents/core/startup-configuration` exists; `/v1` serves only the pinned -upstream routes and `/core/v1` has no startup-configuration route. A Session's committed harness -and model selection remain available through the administrator -[execution configuration](execution-configuration.md) read. diff --git a/contracts/agents-api/workspace-placement.md b/contracts/agents-api/workspace-placement.md index e44456128..bdadd8d96 100644 --- a/contracts/agents-api/workspace-placement.md +++ b/contracts/agents-api/workspace-placement.md @@ -93,7 +93,7 @@ connection generations fence observations; they do not revoke an old process or an already-dispatched file write. Determine prerequisites for each public operation under the -[Core/Runtime rules](../../CONTRIBUTING.md#environment-ownership-and-placement). +[Core/Runtime rules](environments.md#ownership-and-placement-decision). Environment metadata retrieval already reads durable resources without preparing execution. Files.list needs live path/size metadata from the authorized workspace; the private bounded byte reader alone does not implement that route. Reuse native @@ -144,9 +144,7 @@ Use the same fixed SDK/raw HTTP and real execution acceptance for both public engines before claiming the complete milestone. `NATIVE-COMMAND-OUTPUT-001` remains a material Codex retained-output gap. The user -has deferred it from the current principal-workflow milestone acceptance on the -task board; this does not establish complete output fidelity. The recorded failed real run +has deferred it from the current principal-workflow milestone acceptance; this does not establish complete output fidelity. The recorded failed real run is not fixed by a later gated success. No production-ready maintained remedy was verified in the checked upstream sources; do not fabricate output, repair model -prose or silently adopt a native fork. Reassess that dependency from the full -board alongside other material security, state and data-loss issues. +prose or silently adopt a native fork. Reassess that dependency alongside other material security, state and data-loss issues. diff --git a/packages/mcode-harness/README.md b/packages/mcode-harness/README.md index 7aec22c42..bcb2cd161 100644 --- a/packages/mcode-harness/README.md +++ b/packages/mcode-harness/README.md @@ -60,8 +60,8 @@ Native tool schemas are retained. Text and image results use standard MCP conten video results reject explicitly. The pinned native CLI may add task/skill utility tools; qualification must inspect the actual inventory rather than assume exactly six. -See [workspace qualification](../../contracts/agents-api/mcode-workspace-v1.md) for -the required tests and stopping conditions. Synthetic isolation probes and native +Qualify changes with the +[Harness acceptance checklist](../../contracts/agents-api/harnesses.md#acceptance-checklist). Synthetic isolation probes and native model runs do not complete public Files/Artifacts or independent Core acceptance. For the packaged Linux regression, provide an operator-owned private profile and From ed4dbd0b5639a639e57d7ce50ceee7c7dc42a8c1 Mon Sep 17 00:00:00 2001 From: saladday <1203511142@qq.com> Date: Tue, 29 Sep 2026 16:32:51 +0800 Subject: [PATCH 3/5] docs: regenerate documentation site --- apps/docs/content/docs/development.mdx | 117 ++-- .../content/docs/environments-and-files.mdx | 562 ++++++++++++++++-- apps/docs/content/docs/harness-onboarding.mdx | 308 ++++++---- apps/docs/content/docs/runtime-protocol.mdx | 281 +++++++++ apps/docs/content/docs/sandbox-provider.mdx | 201 +++++-- apps/docs/content/guide-sources.json | 98 ++- 6 files changed, 1198 insertions(+), 369 deletions(-) diff --git a/apps/docs/content/docs/development.mdx b/apps/docs/content/docs/development.mdx index 405bdb408..31fc89826 100644 --- a/apps/docs/content/docs/development.mdx +++ b/apps/docs/content/docs/development.mdx @@ -6,7 +6,7 @@ description: "Set up a checkout and choose the right extension boundary." This guide is for contributors changing Core, Runtime, adapters, the Web console or the documentation site. For using a deployment, start with the [getting started guide](/). Read the -[contributor rules](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/CONTRIBUTING.md) before changing code. +[contributor rules](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/CONTRIBUTING.md) before changing code. ## Set up a checkout @@ -18,8 +18,8 @@ git worktree add ../openagentcore-change -b codex/my-change main cd ../openagentcore-change ``` -Install Go at the version in [go.mod](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/go.mod), Node 22, pnpm at the version -in [package.json](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/package.json), and Python 3.9 or newer. The complete gate +Install Go at the version in [go.mod](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/go.mod), Node 22, pnpm at the version +in [package.json](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/package.json), and Python 3.9 or newer. The complete gate runs on Linux and needs a dedicated PostgreSQL database, OpenSSL development libraries for the microsandbox helper, and a Playwright browser. Provider and Runtime builds have additional prerequisites in their component guides. @@ -43,10 +43,10 @@ pnpm exec playwright install --with-deps chrome export OAC_TEST_OFFICIAL_SDK_PYTHON="$PWD/.venv/bin/python" ``` -Set `OAC_TEST_DATABASE_URL` privately to a dedicated `oac_*_tests` PostgreSQL -database. Its role needs permission to create databases: managed-provider tests -create and drop isolated test databases. Never point the test suite at an -installation or product database. The full gate fails if this setting is missing. +Set `OAC_TEST_DATABASE_URL` privately to a dedicated PostgreSQL test database. +Never point the test suite at an installation or product database. The +[test database rules](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/CONTRIBUTING.md#test-database) list the required role +permission. Third-party native packages are pinned build inputs; changing a pin requires the relevant native qualification as well as compilation. @@ -65,17 +65,17 @@ under `~/.oac/build/daemon/` by default. `OAC_DEV_HOME` selects another build ro `OAC_DEV_CORE_BUILD_DIR` selects an absolute Core output directory. Building does not configure a database, start a deployment or qualify native execution. -Use the [service guide](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/README.md#build-standalone-binaries) +Use the [service guide](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/README.md#build-standalone-binaries) to run the Core migrator and server with a separate development database. The [configuration appendix](/configure#appendix-core-environment-without-the-installer) owns standalone process settings. For a complete operator installation, use the [installation guide](/install); building Core alone is a separate contributor workflow. -The [Web package guide](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/web/README.md) describes console development and +The [Web package guide](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/web/README.md) describes console development and its management-only integration. `pnpm dev:web` starts the frontend development server; it does not install Core, issue keys or start native execution. The -[docs app guide](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/docs/README.md) describes the independent documentation +[docs app guide](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/docs/README.md) describes the independent documentation site; `pnpm dev:docs` starts its development server. ## Repository map @@ -83,18 +83,18 @@ site; `pnpm dev:docs` starts its development server. | Location | Responsibility | Read next | | --- | --- | --- | | `services/agents-api/internal/api` | Public, administrator and machine HTTP boundaries | [API index](/public-api) | -| `services/agents-api/internal/store` and `internal/db` | Core persistence, transactions, queries and migrations | [Service guide](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/README.md#database-ownership) | +| `services/agents-api/internal/store` and `internal/db` | Core persistence, transactions, queries and migrations | [Service guide](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/README.md#database-ownership) | | `services/agents-api/internal/execution` | Durable Turn dispatch and scheduling | [Runtime protocol](/runtime-protocol) | | `services/agents-api/internal/engine` | Pure qualification of harness operations and placements | [Harness onboarding](/harness-onboarding) | | `internal/agentdaemon/proto` and `gateway` | Shared wire types, validators and authenticated Runtime connections | [Runtime protocol](/runtime-protocol) | | `apps/parsar-daemon/internal/dispatch` | Runtime preparation, Executor reuse, Turn and cleanup ownership | [Harness lifecycle](/harness-onboarding#required-adapter-interfaces) | -| `apps/parsar-daemon/internal/agent` | Native harness adapters | [Native references](/harness-onboarding#minimal-runnable-example-and-native-references) | +| `apps/parsar-daemon/internal/agent` | Native harness adapters | [Native references](/harness-onboarding#native-references) | | `services/agents-api/internal/sandbox` | Provider interfaces and managed compute lifecycle | [Provider onboarding](/sandbox-provider) | | `services/core-console` | Console login and the server-side management proxy | [Web architecture](/execution-model) | -| `apps/web` and `packages/agents-client` | Console UI and typed clients | [Web guide](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/web/README.md) | -| `deploy/install` and `scripts` | Distribution, installation and validation tools | [Maintainers](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/maintainers.md) | -| `contracts/agents-api` | Pinned schema, local semantic contracts and qualification evidence | [Coverage ledger](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/README.md) | -| `apps/docs` | Generated guide and API-reference website | [Generation workflow](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/docs/README.md) | +| `apps/web` and `packages/agents-client` | Console UI and typed clients | [Web guide](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/web/README.md) | +| `deploy/install` and `scripts` | Distribution, installation and validation tools | [Maintainers](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/docs/maintainers.md) | +| `contracts/agents-api` | Pinned schema, local semantic contracts and qualification evidence | [Coverage ledger](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/README.md) | +| `apps/docs` | Generated guide and API-reference website | [Generation workflow](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/docs/README.md) | Core owns durable execution facts. Runtime owns local execution and cleanup. Adapters translate native operations. Providers own outer compute. These boundaries @@ -103,70 +103,43 @@ apply to user-owned machines and Core-managed environments; read the ## Choose an extension boundary -### Add a Harness adapter - -Start with [Harness onboarding](/harness-onboarding). -Choose a native SDK or machine-readable protocol, declare the operations and -placements you intend to support, and implement the shared -[`Executor` and `Turn` interfaces](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/internal/agent/harness.go). -The guide covers factory registration, service profiles, capability declarations, -input receipts, event translation, cancellation, history and cleanup. - -Use the [synthetic onboarding fixture](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/testdata/onboarding/main.go) -to see the full registration path. With the dedicated test database configured: +Each boundary has one canonical guide. Read it before changing code; this page +only helps you pick the right one. -```sh -go test ./services/agents-api/internal/store -run '^TestThirdHarnessPublicOnboarding$' -count=1 -``` - -This test reaches the actual gateway and Runtime router, but its synthetic engine -cannot establish native acceptance. Qualify the real adapter through the official -client, Core, Runtime and a real model. Verify two ordinary Turns, cancellation, -continuation after restart, unknown outcomes and the advertised optional operations. -Record exact native versions and limitations in the linked operation contracts. - -The [Claude bridge guide](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/packages/claude-sdk-adapter/README.md) explains the -separate TypeScript SDK process. Codex and MiniMax implementation references are -linked from the onboarding guide. Reuse common process and workspace helpers; -keep native event interpretation inside the adapter. - -### Extend the Core–Runtime protocol - -Read [the protocol guide](/runtime-protocol) before adding a message or receipt. -Change the shared types and validators in `internal/agentdaemon/proto`, both peers, -and their contract tests together. Keep exact wire-version checks and update the -message ordering, identity and failure ownership documentation. A transport write -cannot be used as evidence of native application or completion. +| Boundary | You are adding or changing | Canonical guide | +| --- | --- | --- | +| Harness adapter | A native agent engine behind the Runtime | [Harness onboarding](/harness-onboarding) | +| Sandbox Provider | Outer compute that creates and reclaims Environments | [Sandbox Provider guide](/sandbox-provider) | +| Core–Runtime protocol | A message, receipt or lifecycle rule between Core and the daemon | [Core–Runtime protocol](/runtime-protocol) | +| Public API operation | A `/v1`, `/core/v1` or `/api/v1` route | [API index](/public-api) and [contracts](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/README.md) | +| Environment capability | Skills, Plugins, MCP or `packages.system` preparation | [Environments](/environments-and-files#runtime-capability-preparation) | -```sh -make check-runtime-contract -``` +### Add a Harness adapter -The focused gate covers wire validation, gateway behavior, transport, dispatcher, -real WebSocket contract fixtures and Core observation failures. Adapter-specific -native acceptance remains a separate requirement. +Implement the shared `ExecutorFactory`, `Executor` and `Turn` interfaces in +[`agent/harness.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/parsar-daemon/internal/agent/harness.go), register +the adapter and add a service profile. Follow the numbered steps in +[Harness onboarding](/harness-onboarding); qualification +evidence belongs in [Harness integration](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/harnesses.md). ### Add a Sandbox Provider -Read [Provider onboarding](/sandbox-provider). Implement -[`SandboxProvider`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/sandbox/sandbox_provider.go) -for the outer compute lifecycle. Register construction and vendor configuration at -that boundary; use optional interfaces for observation and checkpoints. Runtime -capability preparation and native execution keep their existing owners. +Implement the five required `SandboxProvider` operations in +[`sandbox_provider.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/sandbox/sandbox_provider.go), +register the provider kind and pass `make check-sandbox-provider-contract`. Follow +the numbered steps in the [Sandbox Provider guide](/sandbox-provider). -```sh -make check-sandbox-provider-contract -``` +### Extend the Core–Runtime protocol -Run the [shared contract suite](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/sandbox/contracttest/provider.go) -through the adapter's native boundary, then qualify actual resource creation, -bootstrap, observation, uncertain results and cleanup. Mocked compute cannot prove -that resources were reclaimed or that the outer environment provides isolation. +Change shared types and validators in `internal/agentdaemon/proto`, both peers +and their contract tests together, keeping the exact wire-version check. The +[protocol guide](/runtime-protocol) owns message order, receipts and failure +ownership; `make check-runtime-contract` is its focused gate. ## Validate a change Run checks for the affected boundary while developing. The repository -[required checks](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/CONTRIBUTING.md#required-checks) define completion, including +[required checks](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/CONTRIBUTING.md#required-checks) define completion, including `make check` and any changed native component's real acceptance. | Change | Focused validation | @@ -192,13 +165,13 @@ with `AGENTS_FIXTURE_PORT` and `AGENTS_WEB_PORT` when running parallel validatio Keep databases, ports and containers separate between validation workers. Compilation, fixture success and live model/provider acceptance establish different facts; report skipped or unavailable checks explicitly. Follow the -[independent blind review workflow](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/CONTRIBUTING.md#workflow-and-quality) +[independent blind review workflow](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/CONTRIBUTING.md#review) after validation. ## Change documentation Edit the source that owns the subject, using the -[documentation ownership map](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/CONTRIBUTING.md#documentation-ownership). +[documentation ownership map](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/CONTRIBUTING.md#documentation-ownership). User guides explain a workflow and link to detailed contracts. Contributor rules explain ownership and required checks. Semantic contracts document exact behavior, implementation gaps and evidence. Avoid copying the same rule into all three. @@ -206,9 +179,9 @@ implementation gaps and evidence. Avoid copying the same rule into all three. Update authored sources before regenerating the docs site. Adding a site page also requires a source entry and navigation entry; source/output hashes verify freshness. API references render the namespace-specific generated schemas. Follow -[the docs app workflow](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/docs/README.md) for link, type, build and browser +[the docs app workflow](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/docs/README.md) for link, type, build and browser checks. The release bundle has a separate explicit documentation list in `scripts/core-distribution-manifest.py`; changing a bundled path or heading must also pass its relative-link and anchor checks. -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/development.md) +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/docs/development.md) diff --git a/apps/docs/content/docs/environments-and-files.mdx b/apps/docs/content/docs/environments-and-files.mdx index 665efeddf..9fa4f6102 100644 --- a/apps/docs/content/docs/environments-and-files.mdx +++ b/apps/docs/content/docs/environments-and-files.mdx @@ -3,19 +3,19 @@ title: "Environments and files" description: "Execution placement, Environment resources and workspace ownership." --- -This assessment covers the fixed [Python SDK contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/upstream.json), with partial +This assessment covers the fixed [Python SDK contract](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/upstream.json), with partial coverage. Core-managed Docker runs the three qualified native harnesses. V1 user-managed `self_hosted` enrollment uses the same colocated Runtime for Codex, Claude SDK and MiniMax. `/workspace` names the packaged workspace; a self-hosted Session may instead select its exact canonical physical directory. The -[recorded deployment qualification](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/user-managed-runtime-v1.md) retains its historical +[recorded deployment qualification](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/user-managed-runtime-v1.md) retains its historical source, paths and tested capability scope. For hosted compute, the deployment selects E2B, Docker or microsandbox through -[Hosted Sandbox Manager](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/HOSTED-SANDBOX-MANAGER.md). +[Hosted Sandbox Manager](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/HOSTED-SANDBOX-MANAGER.md). For the separate caller-managed E2B path, the user owns allocation, renewal and -cleanup through the official SDK and [Runtime packaging](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/deploy/e2b/README.md). -[Templates](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/environment-templates.md) remain a hosted-only resource path; -[Files](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/environment-files.md) reuse the exact authorized local workspace. +cleanup through the official SDK and [Runtime packaging](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/deploy/e2b/README.md). +[Templates](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/environment-templates.md) remain a hosted-only resource path; +[Files](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/environment-files.md) reuse the exact authorized local workspace. The internal Store now owns a durable Environment association for newly created `self_hosted` and `openai_hosted` snapshots, atomically with Session creation. @@ -33,7 +33,7 @@ readiness. Rotation/revocation, Session deletion and ownership loss deny further access without promising immediate cessation of native effects. Native history remains local to the bound Runtime and cannot be replaced on retry. There is no registry/Noise relay or transient service-side harness credential. -See the [enrollment guide](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/README.md#user-managed-runtime-enrollment). +See the [enrollment guide](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/README.md#user-managed-runtime-enrollment). ## Basic public Docker-hosted profile @@ -41,8 +41,8 @@ An explicitly configured default managed Docker provider enables `type=openai_ho for the qualified Codex, Claude Code and MiniMax Code profiles. Each uses the same Runtime lifecycle and workspace interfaces with its native adapter. The outer Environment provides managed isolation; the daemon adds no inner sandbox. -See the [engine profile guides](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/README.md#public-engine-profiles) for setup and limits. -The standalone [operator configuration](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/deploy/codex/README.md#standalone-operator-configuration) +See the [engine profile guides](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/README.md#public-engine-profiles) for setup and limits. +The standalone [operator configuration](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/deploy/codex/README.md#standalone-operator-configuration) selects the qualified immutable Runtime image; advertised capabilities alone do not enable admission. An idle or initial-text creation commits Session, Environment and retry identity before the existing leased Worker provisions its allocation. @@ -63,7 +63,7 @@ rejects explicitly, including null and empty lists, and never invokes apt, sudo or elevated execution. Package responses include the official required `system: []` field as empty metadata; it does not enable installation. Unsupported hostname forms and installation combinations reject explicitly; see -the [Template coverage and limits](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/environment-templates.md). Empty/null installation +the [Template coverage and limits](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/environment-templates.md). Empty/null installation defaults produce safe empty metadata, not a live workspace inventory. Service-origin hosted MCP remains unsupported; Environment Plugin MCP has its own qualified transport matrix. @@ -83,11 +83,11 @@ semantics; this profile does not establish complete Environment compatibility. The application creates, renews and destroys its E2B sandbox through the official SDK. It deploys the shared Runtime, then enrolls that Runtime into a `self_hosted` Session. Core neither keeps an E2B allocation nor issues Provider renew/kill calls. -The [E2B guide](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/deploy/e2b/README.md) owns packaging and +The [E2B guide](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/deploy/e2b/README.md) owns packaging and user-side lifecycle instructions. Expiry or lost workspace/history must not trigger -transparent replacement or replay. The [new enrollment qualification](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/user-managed-runtime-v1.md) +transparent replacement or replay. The [new enrollment qualification](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/user-managed-runtime-v1.md) records its own real deployment evidence. -The [prior E2B qualification](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/README.md#e2b-v1-qualification) records a historical +The [prior E2B qualification](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/README.md#e2b-v1-qualification) records a historical Core-managed route; it does not establish this caller-managed enrollment result. Current deployment-managed E2B is a separate hosted configuration in the manager guide. @@ -125,7 +125,7 @@ connection alone is not readiness. Retries preserve identity and the five-minute database deadline. HTTP disconnect retains the reservation. Local expired/cancelled outcomes return 409, lost ownership 503, and deletion 404; exact hosted error status/body and pending-input crash recovery are unverified. See the -[canonical wait rules](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/CONTRIBUTING.md#environment-ownership-and-placement). +[canonical wait rules](#ownership-and-placement-decision). Cancellation-only batches use the existing locked admission and native delivery. An idle cancellation creates no Turn, and retry identity preserves the original @@ -158,49 +158,455 @@ exercise fixed SDK/raw HTTP creation/input, actual native tools, Files/Artifacts second-Turn history, Core/Runtime restart, cancellation and credential rotation/ revocation/deletion on each declared deployment. +User-managed onboarding creates a `self_hosted` Session first, then passes its +Environment ID and unchanged `remote_url` to our Runtime with connect-only +authorization. This is our daemon connection contract, not stock OpenAI +`exec-server` transport compatibility. Validate the pinned public HTTP/SDK +resources, state transitions and lifecycle separately; do not infer complete +compatibility from a working connection. User-side tooling owns local Runtime or +E2B allocation, renewal and cleanup. Session deletion and credential revocation do +not transfer ownership of user compute to Core or prove process quiescence. + +Keep prerequisites specific to the public operation being implemented. Native +harnesses execute; adapters translate protocols and fill demonstrated capability +gaps; Core owns public semantics, authorization and resources. Before adding a +mechanism, identify the current operation it enables and why existing native +capabilities or interfaces do not suffice. Durable metadata queries need no live +runtime. Live file reads require an authorized, isolated view of the exact workspace +and bounded operation ownership, but not a complete file-write, environment +replacement or placement-retirement implementation. Apply mutation fencing and +retirement guarantees where an operation can write, replace or retire that owner. +Read-only access still requires tenant/resource checks, path isolation and safe +failure when the authorized workspace cannot be reached; it never grants public +admission merely because an adapter advertises a capability. + +Use distinct authorization for callers, devices and environment connections. A +managed outer Environment must exclude broader application credentials and other +tenants' secrets. The daemon does not hide its own state from same-user tools. +Directory bindings and process identities do not provide filesystem isolation. +Preserve or demonstrably restore native history across +compute replacement; never silently move a bound Session or replay unknown work. +Self-hosted compute/files remain caller-owned, with explicit cleanup separate from +Session deletion. The full Environment implementation remains pending; follow the +[pinned contract and acceptance sequence](/environments-and-files) +and the [two-engine placement prerequisites](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/workspace-placement.md). +For co-location, qualify the outer deployment boundary and the shared Runtime +lifecycle. Native tools use the starting account's permissions; do not claim a +daemon or harness sandbox. Host operators choose their own outer isolation. +Never enable an execution combination whose required outer behavior is unverified. + +The internal Store creates one Environment with an environment-bearing Session in +its creation transaction. The Session upsert selects the retry winner; retries +never create or repair associations. Environment identity/state live in their own +table. Tenant ownership and immutable configuration come from the owning Session, +without duplicated JSON, tenant columns or generated IDs in the creation hash. +Environment reads join that Session and exclude deleted Sessions; deletion retains +ownership for later settlement/cleanup. Existing `none` and legacy missing +configuration create no Environment, and historical internal snapshots are not +backfilled. Creation and recorded-intent retry snapshots load the Environment with +the Session row/cursor in the same transaction, without borrowing subsequent +activity or Turn state. Initial state is `pending`; authenticated connection observations follow +the lifecycle rules below. +Public creation supports `self_hosted` on the three enabled native profiles when +the daemon gateway is configured. The workspace is a clean absolute host path matching the bound Runtime root; +`/workspace` denotes that root through the existing logical mapping. Optional +capability directories are clean absolute host-local selections. Supported non-deferred functions keep their engine-specific +validation and native callback bridge. Enrollment binds the dedicated Runtime; +Session output uses the owned Environment association. Public Files reuse the exact +local workspace; populated self-hosted installation metadata remains unsupported. + +Environment retrieval uses the existing tenant-scoped join to a live owning Session +and its durable connection status, independently of a live Runtime or gateway setup. +It preserves project-shared read access and exposes only the pinned resource fields. +The current closed self-hosted configuration has no API-managed file, plugin or skill +installations, so those required arrays are empty. They are not a filesystem listing +or a claim about native discovery. Reuse the strict Environment configuration parser +and reject unsupported installation fields/capabilities or resource states instead +of treating unknown inventory as empty. No read initiates native work or changes +connection state; connection does not establish readiness or process quiescence. + + +The private Environment input reservation stores one canonical message batch before +Turn admission, with a five-minute deadline from the database clock. It requires +an Environment-bearing Session without active work. Reservation and direct input +paths share the Session lock and retry identity; pending or settled keys cannot +bypass the reservation through direct admission. A pending reservation blocks new +direct batches, including cancellation, while successful earlier retries remain +readable. Promotion commits the original inputs, history, reservation settlement +and execution claim (`queued` to `in_progress`) together; expiration and targeted +cancellation retain the terminal identity. Session deletion +is rejected while input is pending and changes nothing. A terminal reservation retry must not +affect a later reservation or Turn. Evaluate deadlines after acquiring the Session +lock, and return terminal storage outcomes without rolling their transaction back. + +Initial messages for a newly created Environment-bearing Session use that same +reservation in the creation transaction, including its connection-action event. +The creation winner alone inserts it; the stream cursor still precedes that +event, and creation retries never re-insert it. A durable initial/later flag +defaults historical rows to later input without inferring origin. Initial expiry +projects a failed Session and safe error before any Turn exists; later expiry +retains idle semantics. Failure +events capture the settled activity and Usage atomically. Late connections and +creation retries cannot reset or replay expired input, and newer work supersedes +old activity without changing its event snapshots. The Environment itself is not +failed by an input deadline. This Store rule covers actual self-hosted and internal +hosted associations; it does not enable hosted providers. None/absent Environment initial input retains +immediate Turn admission. Cancellation/deletion keep their existing semantics. + +Ordinary and streamed public self-hosted creation accept initial text through this +transaction after configuration and new-work lease checks. They return the owned +Environment ID and executor URL while offline, without waiting for admission. +The creation stream's `created` snapshot is the committed JSON 201 projection, +including the connection action; the committed action event then follows from the +creation cursor. A disconnected observer leaves committed input intact; +only the existing Worker prepares, promotes and starts it. Saved-Agent retries with recorded intent +recover before fresh execution admission or source resolution; inline retries keep +their existing resolved-snapshot validation. +Later live subscribers observe only future events and recover history through queries. + +The public self-hosted input profile accepts message-only batches. Under the same +Session lock, recover the original reservation or direct receipt before choosing +current active input or idle reservation. Active messages use existing ordered input +receipts without a new Turn, preparation or reservation; idle messages retain the +readiness and promotion path, including already-connected environments. Pending +reservations keep their gate and deadline. An unlocked activity read or retry after +a conflict must never choose a different admission path. Mixed inputs remain gaps; +these restrictions do not narrow the pinned protocol target. Cancellation-only batches +use the existing direct admission after configuration and execution-ownership checks. +Only the Session-locked transaction chooses the active Turn or an idle receipt; +matching retries retain that target even during later work. Cancellation cannot +create a Turn or bypass preparation. A new cancellation still conflicts with a +pending reservation; it does not cancel pre-Turn input. Its 204 response confirms +durable admission, not native completion or process exit. Homogeneous function-result +batches also use direct admission after those same checks: their explicit Turn/call +identity selects an existing pending call, never new work. Reuse function validation, +Session-locked whole-batch receipts, preserved output/error fields and native +application acknowledgements. Matching retries remain bound to their original calls +after completion or during later work; new results cannot bypass a pending reservation. +Definitions remain fixed through preparation and cold native continuation. These +callbacks are not installed Environment metadata. Mixed result/cancel publication +and exact hosted action-removal timing remain separate gaps. +Promotion requires the current leased execution writer and the caller's +retained native preparation; never hold a database lock during external preparation. Only +the first successful non-replay receipts authorize Start on that same preparation. +An admitted retry returns the original receipts without reclaiming execution; a +read or uncertain commit never authorizes another Start. A crash after promotion +but before Start uses existing claimed-Turn reconciliation (`execution_interrupted`), +including unbound or deleted Sessions, rather than ordinary queued dispatch. Deletion +after claim is rejected like any active Turn. +The Worker expires at most 32 due reservations on each existing tick, after +checking ownership and before checking devices or execution slots. The sweep +requires the leased Store and uses its connection with the existing transaction +timeout; it never falls back to a pooled writer. A partial deadline index and +Session row locks with SKIP LOCKED let unrelated work proceed around contention. +The candidate cutoff is statement time; settlement rechecks the database clock +after acquiring the Session lock. This bounds mutations and transaction time, not +the number of examined locked rows. Restart resumes expiry on normal ticks without +a separate scheduler or backlog-draining loop. No failed Turn may stand in for a +pre-Turn connection failure. + +Session activity before a Turn is derived from the latest relevant reservation and +authenticated connection state. Offline input requests `environment_connection`; +connection arrival clears that action to `idle`, while the prepared Worker still +owns native readiness and admission. An idle offline Environment alone requests no +connection. Reservation/connection changes commit immutable Session activity and +usage snapshots in the same transaction; SSE must not substitute a later Turn or +action set. A newer or active Turn owns subsequent activity. Settled non-initial +reservations clear their action to `idle` until newer work exists, even after an +earlier failed Turn. This local settlement policy does not establish hosted expiry +errors or initial-input asynchronous failure semantics; those remain unverified. + +Session GET/list/metadata responses and live SSE share the safe `self_hosted` +output projection. Its `remote_url` comes only from the daemon gateway +configuration, never request headers or a daemon address. Include +the owned Environment ID, workspace and capability directories without exposing +private configuration. The standalone Environment resource remains separate. +Acceptance must pass that exact URL and ID to the +caller-started executor and observe real remote execution through the existing +Worker, daemon and harness, with fixed SDK and raw HTTP/SSE checks. + +A self-hosted input HTTP request returns 204 only after durable admission. Its +wait uses bounded pooled operations, outside transactions and execution lease +ownership; it cannot prepare or start native work. Only that route extends its +response write deadline to six minutes for the original five-minute database +admission deadline plus response grace. Request/observer disconnect stops waiting, +not the durable reservation or execution; retries keep the original identity and +deadline. The Worker remains the readiness, promotion and Start owner. Local failure +mapping uses 409 `environment_input_expired` / `environment_input_cancelled`, 503 +`execution_unavailable` for ownership loss, and existing 404 for deletion. New input +after a hosted provisioning failure returns the observed 409 `conflict_error`; other +exact hosted failure statuses/bodies and pending-input crash recovery remain unverified. +Principal acceptance must use public Session creation and input against the built +standalone service, including a wait exceeding its ordinary 30-second write timeout, +real remote commands/files and a second native-history Turn. Private provisioning +or injected API handlers cannot substitute for that workflow. + ## Runtime capability preparation -Core freezes resource versions, metadata and source selections. Managed initialization -keeps initial files and tool configuration before Runtime Skill/Plugin import, -then runs packages and ordered setup before Runtime snapshots local capability -directories. All steps use the common `runtime_prepare` protocol; Providers do -not execute Core initialization commands. The common runner depends on neutral -Environment/Session identity and a Runtime peer, not Provider, deployment or OS. Self-hosted input -accepts only local `capability_directories` with `workspace_directory`, not managed -Skill/Plugin archive fields or a hosted Template. Local directory discovery does not -populate the public API-managed `skills` or `plugins` installation arrays. - -Both origins use the common Runtime parser and `installed.json` on Linux, macOS -and Windows. The operator selects the capability root; the native installer -uses `capabilities` under `OAC_RUNTIME_HOME` by default. Core and public requests -cannot select that destination. Read-only snapshot modes are integrity hints, -not protection from the launching user. The manifest binds one -Session and Environment to the source-selection digest. The admitted asynchronous -preparation owner verifies or creates it before native execution, including for an -empty selection. Reconnect and replacement executors reuse installed contents; -source edits are observed only by a new Session with its own Environment and fresh -snapshot. A private completion record retains only the operator installation root, -so loss of a completed snapshot cannot trigger source recapture. Missing, partial or -conflicting snapshots fail without automatic repair or replay. Adapters receive only the resulting Runtime-owned Skill paths and MCP -declarations. Files access retains its separate readiness and authorization. - -Resource management owns allocation and Environment creation, renewal and reclamation. -Executor close, Turn cancellation and transport loss preserve the installed snapshot, -workspace and allocation. Reclamation is an explicit operation coordinated with active -work; a disconnected socket is not proof that native effects have stopped. - -Source selections accept portable absolute Unix, Windows drive and UNC paths; -Core does not resolve these paths on its own host. The daemon applies its local -path and access checks. Initial files and setup working directories use logical -`/workspace` paths. Harness differences remain in adapters. +This section is the single owner of capability preparation: initial files, tool +configuration, Skills, Plugins, Environment MCP, npm/Python packages, setup and +`packages.system`. Hosted and self-hosted Environments use one Runtime path. + +### Ownership and lifetimes + +Resource management retains provider placement, capacity, allocation and +Environment create/renew/reclaim. The authenticated Runtime connection carries +initialization, capability preparation and executor operations; it never +implicitly allocates or destroys compute. + +Executor close, Turn cancellation and transport loss preserve the installed +snapshot, workspace and allocation. Reclamation is an explicit operation +coordinated with active work; a disconnected socket is not proof that native +effects have stopped. See +[Executor and Turn lifetimes](/runtime-protocol#executor-and-turn-lifetimes). + +### Preparation order + +Core freezes resource versions, metadata and source selections. Managed +initialization runs in this order, every step over the common `runtime_prepare` +exchange with the same daemon: + +1. initial files and tool configuration; +2. Skill/Plugin bundle import; +3. npm/Python packages and ordered setup commands; +4. capability directory snapshot finalization. + +Providers only place, create, bootstrap, inspect, renew and reclaim resources; +they never execute Core initialization commands. The common runner uses only +neutral Environment/Session identity and a Runtime peer, with no Provider, +deployment or OS branch. Harness differences belong to native adapters. + +A missing authenticated Runtime connection waits before capability +initialization is claimed; an operation whose effect is unknown is not replayed. +Self-hosted capability directories stay in immutable Environment configuration +and never create a managed initialization row or allocation. + +Self-hosted input accepts only `workspace_directory` and optional local +`capability_directories`, not managed Skill/Plugin archive fields or a hosted +Template. Local directory discovery does not populate the public API-managed +`skills` or `plugins` installation arrays; those describe API-managed uploads. + +### Transfer and paths + +The private `runtime_prepare`/`runtime_prepare_result` exchange carries typed +initial files, configure/npm/python/setup operations, inert Skill/Plugin +archives and finalization selections, with canonical Session and Environment +identities. + +- Files and setup working directories use logical `/workspace` addresses. + Setup commands are explicit typed input; executable selection and physical + destinations stay Runtime-owned. Core supplies no executable or host-platform field. +- Ordered 64 KiB chunks and SHA-256 receipts bound each file or archive to + 50 MiB, each control frame to 1 MiB and each connection to one transfer. + Initialization and finalization headers carry no file data. +- Begin, chunk and commit are never retried. Rejected, failed and unknown effects + stay distinct. Runtime owns transfer and filesystem work through settlement, + including disconnect or cancellation. +- Source selections accept portable absolute Unix, Windows drive and UNC paths. + Core never resolves them on its own host; the daemon applies its local path and + access checks. + +### Installed snapshot + +Both origins use the common Runtime parser and `installed.json` manifest on +Linux, macOS and Windows. The operator chooses the capability root (by default +`capabilities` under `OAC_RUNTIME_HOME`); Core and transfer requests cannot. + +- The manifest binds the Session and Environment to the ordered source-selection + digest. The admitted asynchronous preparation owner verifies or creates it + before native execution, including for an empty selection. +- Filesystem locking prevents concurrent installation. A private completion + record keeps only the operator installation root, so a deleted snapshot is + never mistaken for first preparation or recaptured. +- Missing, partial, conflicting or foreign snapshots fail without deleting data, + automatic repair or replay. +- Reconnection and replacement Executors load installed contents without + rereading sources. Source edits are seen only by a new Session with its own + Environment and fresh snapshot. +- Read-only snapshot modes are integrity hints, not protection from the + launching user. + +### Executor admission + +Synchronous Executor admission validates the frozen descriptor only. The +asynchronous preparation owner ensures capabilities are ready before invoking the +native factory. Adapters receive only the resolved Runtime-owned Skill paths and +MCP declarations; these remain Runtime-only fields. Reused Session Executors +keep their original configuration. + +Files reads keep their separate readiness and authorization and do not require +capability or native execution readiness. MCP environment variables come only +from explicit immutable Runtime tool configuration; missing variables never fall +back to daemon credentials or ambient process variables. + +### System dependencies and Runtime directories + +The daemon runs as its launching account and never uses sudo or elevates its +permissions. Only user-directory dependencies install during preparation. + +- System dependencies must be preinstalled in the managed image/template or by + the self-hosted user. +- `packages.system` is rejected explicitly, in Templates and inline + configuration, including a supplied null or empty list. It is never ignored or + translated into a privileged operation. +- Runtime runs no apt, sudo, unprivileged system-root installer or other + automatic system-package operation. No managed-only exception exists. Missing + dependencies fail the consuming operation. +- npm and Python use local prefix/target directories. Setup requires Bash; + Windows requires Git Bash, without a substitute shell. +- Package responses keep the official required `system: []` field as empty + response metadata; it is not stored as an initialization option. + +Initialization and package directories default under `OAC_RUNTIME_HOME` and may +be selected with `OAC_RUNTIME_INITIALIZATION_DIRECTORY` and +`OAC_RUNTIME_PACKAGE_DIRECTORY`. Packaged Linux images select their existing +`/environment` layout through those settings. `OAC_RUNTIME_TOOL_ENV_FILE` +selects explicit tool configuration. These are resource paths, never +Environment-source or OS switches in Core. + +Runtime process ownership waits for exit and I/O settlement. Confirmed failures +keep only a bounded exit status, never command output. + +### Platforms and isolation The protocol and Go preparation implementation are shared by all three native -platforms. Resource directories are operator settings, not deployment branches. -Managed Providers remain Linux-only. The daemon does not sandbox tools, files or -network access; tools use the starting account's permissions. Native installation -and current validation limits are in the [native guide](/self-hosted-native). -Historical deployment acceptance does not qualify later platform or capability changes. See the -[canonical preparation rules](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/CONTRIBUTING.md#shared-runtime-capability-preparation). +platforms. Managed Providers remain Linux-only. Commands run directly with the +starting account's permissions and host network; the daemon does not sandbox +tools, files or network access. Outer Environments own managed isolation, and +unsupported network restrictions reject instead of silently running unrestricted. + +Native installation and validation limits are in the +[native guide](/self-hosted-native). No project-version upgrade or +historical manifest compatibility is introduced. These rules do not establish +new real-deployment acceptance; historical evidence stays limited to its +recorded binaries and inputs. + +### Allocation initialization lifecycle + +The allocation lifecycle owns pending/running/complete initialization. Authentication +may connect the daemon during initialization; execution bindings, native preparation, +live Files and connected publication wait for completion. Keep Provider bootstrap +settlement distinct. Advance at most one bounded initialization operation per full +maintenance scan. At allocation EOF, begin the next page in the same call rather +than consume an observation interval on an empty page. Refill at most once, retain +the 32-allocation per-call bound and the five-second ticker, and never loop on an +empty store. For managed nodes these bounds apply independently to each node. +Use process-local progress and the same node lifecycle gate as direct provisioning. + +After a next-Turn input is durably pending, a completed managed allocation in a +suspension/recovery phase may hint this loop. Initial inputs, cold creation, +running/disabled compute, terminal receipts, cancellation/tool-result events, +history and file operations do not use this hint. Eligibility lookup and delivery +are best effort; persisted work and the normal ticker remain authoritative. +Coalesce hints without blocking, and allow at most one extra scan per normal +five-second cycle. Keep the ticker independent of requests. A normal tick consumes +already queued hints before scanning; simultaneous tick/hint readiness is one +normal scan. Preserve hints arriving during a scan, the allocation cursor and all +ownership checks. Never close the hint channel while handlers may still send. +This bounds extra maintenance work but does not bypass capacity, a busy lifecycle +gate or multi-page scheduling, and does not guarantee a resume deadline. + +A recovered or uncertain running installation fails and uses existing cleanup, without replaying writes. +A hosted provisioning failure is terminal for its Session. The allocation's cleanup +transaction stores a safe reason with the failed Environment and records +`environment.failed`, `error` and one `agent.session.failed`; Session reads derive +`failed`, that reason and the failure time from the same record, and live streams end +after the failed event. The initializer reports only the integer exit status of a +failed initialization command. Core composes the reason from a fixed step label and that status, +never from command, package-manager or file output; unknown effects, timeouts and +receipts without a status keep a generic reason. New input then gets the observed 409 +`conflict_error`; expiry and pending-input settlement keep their behavior. +Completed environments never reinstall initial files on reconnect or native recovery. +The typed Runtime preparation protocol carries bounded confidential input; the +daemon owns the common Go initialization implementation. Confidential env +and setup snapshots are encrypted independently of ordinary metadata. Adapters +apply explicit tool variables without automatically inheriting daemon credentials; +this does not prevent same-user tools from reading local Runtime state. +Reuse runtimefs atomic replacement and anchored logical-workspace paths for initial +files on every platform. Public Files creation keeps its own non-replacement rule. + +### Skills, Plugins and Environment MCP + +Skills and their immutable versions are Core-owned tenant resources, independent of +Sessions and native Skill installations. Serialize version allocation and pointer +mutations under the owning Skill row. Keep top-level name and description aligned +with the default version in the same transaction, including default-changing uploads; +nondefault uploads preserve that metadata. Preserve unique version identities across +concurrent uploads and deletion. Metadata reads never load or decrypt bundle bytes. +Encrypt bundle contents with a tenant, Skill and version binding using the existing +service cipher. Deleting a Skill reclaims its versions without affecting already +frozen Session initialization. Public reference metadata, unresolved template intent +and the resolved Runtime bundle are distinct; do not report a reference as inline +merely because it reuses the same installer. No compatibility reader, source +cache, extra lifecycle owner or per-harness resource implementation is required. +Resolve references inside the Session creation transaction, after the creation +upsert establishes ownership. Lock referenced resources in a stable order; freeze +the selected version, descriptive metadata and bytes together. Creation retries +recover the recorded intent before reading mutable templates or Skill sources. +Templates preserve default, latest and explicit version selectors. An omitted or +null reference version selects the default at Session creation and projects as +`version: null` in Template responses. Session responses contain concrete versions; +only validated installation metadata crosses the Runtime boundary. A supplied +Session Skill, Plugin or capability-directory list replaces its template list; +omission and null inherit, while an empty list clears that selection. This differs +from Template resource updates, where null clears lists and resets network to the +pinned enabled default. Preserve caller intent and frozen Session snapshots in +both cases. Public capability directories remain caller paths; adapter-owned +installation directories are not portable public paths. + +Inline and referenced Skill ZIPs use the same confidential initialization snapshot and installer. +Core validates portable manifests and bounded regular-file archives, returns only +safe Skill metadata, and freezes content before native preparation. The Runtime +owns `skills/` below its configured capability root; the manifest validates +the installed snapshot before reuse. This is state +consistency, not protection from the launching user. Public Plugin ZIPs preserve their complete package +layout and reuse the shared archive and portable Skill parsers. Core keeps safe +Plugin metadata separate from encrypted archives. Templates inherit or replace +Plugin and capability-directory lists through the same hosted resolver. + +After ordered setup, the existing initializer snapshots declared workspace-contained +capability directories into Runtime storage and writes one installed manifest. +This is an initialization artifact, not a second lifecycle owner or database ledger. +Directory bytes are observed after setup; they are not frozen at Session creation. +The common daemon resolves the manifest only for executable preparation and passes +validated Runtime-owned Skill/package roots to adapters. Files reads do not require +that artifact. Reconnection and recovery read installed bytes, never mutable source +directories. Missing or inconsistent installations fail preparation without replay. + +Adapters register only selected Skill roots without changing the execution loop. +Codex uses explicit extra roots; MiniMax projects its native catalog; Claude creates +one controlled envelope per package with real directories and immutable hardlinks +under content. Its explicit paths remain inside that envelope; original native +control files are not activated. Keep automatic native MCP discovery disabled. +Explicit Environment MCP declarations +follow the separately qualified transport path below; unsupported native activation +fails explicitly, without silent partial activation or a generic plugin framework. + +Environment-origin MCP declarations use the shared Plugin parser and frozen +installed packages. The installation manifest retains selected MCP package roots; +Runtime re-parses those installed packages without another configuration copy, +credential cache or lifecycle ledger. Native adapters must explicitly qualify and +project supported declarations before public admission. Parent-directory Skill +discovery does not activate nested Plugin MCP configuration. + +The common daemon stdio entry resolves the installed declaration and launches its +server under the same user permissions as the Harness. Unix replaces the helper +process; Windows forwards native stdio within the owned process tree. Explicit +initialized values override the selected declaration's variables. There is no +Python sandbox launcher, mount policy, shell prefix or separate credential sandbox. +Process groups and Windows Jobs own cancellation and descendant cleanup only. +Claude composes MCP identity and observation with the same workspace profile; +MiniMax keeps its existing supported transport qualification. A public capability +advertisement must still match the installed engine's actual supported transport. + +Environment-origin literal HTTP headers remain rejected for the pinned Claude +client because its interpolation and cross-origin forwarding change their meaning. +MiniMax accepts environment stdio only. Its adapter reads the existing Session-private +native runtime-name registry for exact first-frame identities and cross-checks +completed native results. Reuse existing observation and cancellation settlement; +never fabricate a delayed start event, guess normalized identities or add a registry +of our own. Its unqualified HTTP transport remains rejected. +These mechanisms alone do not establish public MCP support or full compatibility. ## Contract inventory @@ -255,7 +661,7 @@ from Session and Turn status. ## Ownership and placement decision -The canonical [architecture rules](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/CONTRIBUTING.md#environment-ownership-and-placement) +The canonical [architecture rules](#ownership-and-placement-decision) keep Environment lifecycle common while leaving process placement and native transport to adapters. Logical ownership does not require a machine per object. @@ -279,4 +685,50 @@ native history independently of disposable compute, or prove native restoration; never treat an Environment ID as a filesystem or history backup. Do not silently move an existing Session away from its bound device. -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/environments.md) +Environment identity, tenant/Session association, configuration and lifecycle belong +in Agents API, independently of provider compute, authenticated device identity, +daemon sockets and native harness Sessions. Create an Environment association in +the same transaction as its Session and creation identity when this resource is +implemented. Keep mutable connection/registration state out of immutable +configuration; replacement ownership must fence stale observations. + +The current hosted architecture is V1: Core runs independently; each Environment +sandbox contains its daemon, selected native harness, local tools and workspace. +Execution and Files use the same authorized workspace through the existing +Core/Runtime contract. Native tool calls stay local. +CLI discovery uses a bounded 15-second version probe per installed harness; +missing binaries fail immediately. A version result is availability, not Environment +readiness, and does not change initialization or connection ownership. Process placement and native +transport remain adapter responsibilities, without a second model/tool loop. +The former separated Runtime/harness and workspace executor topology is a distant +future V2 option, to revisit only after V1 is stable and concrete needs justify it. +Do not extend that topology for hosted delivery, maintain two current hosted routes, +or introduce dormant V2 compatibility scaffolding. + +When an execution Session has a previously started Turn but no recorded native +Session ID, Core requires existing-history recovery through a verified Runtime +capability. Read that condition before claiming the next Turn. A supplied native ID +remains authoritative. The Codex adapter may recover only a unique, nonarchived +root in the exact Session-private native home and expected working directory, using +native listing and exact-ID resume. Missing, incomplete or ambiguous history must +fail without starting a fresh root. A recorded start can precede native work; that +uncertain case also fails conservatively. Recovery does not replay interrupted +inputs, erase prior outcomes or promise transparent continuation of running tools. +Keep Device identity and Environment scope in `ExecutionDevice`; native Session +identity and prior API Turn state belong to `SessionExecutionBinding`. + +Platform-managed and user-managed deployment reuse this same Runtime. For platform +management, SandboxProvider creates and reclaims it. For user management, the user +starts the Runtime and its daemon authenticates and initiates the Core connection; +Core verifies principal ownership and the exact Environment binding. These are +management responsibilities, not separate execution architectures. + +In V1, our daemon fills the user-side executor role. Users deploy daemon, the +selected harness, local tools and workspace together. Do not require Codex +`exec-server`, a service-side harness, registry/Noise transport or remote tool +forwarding. The explicit daemon-executor decision supersedes the previous native +executor interoperability requirement. The superseded execution route is removed; +retain reusable filesystem helpers, +necessary regressions and historical evidence without a compatibility layer. + +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/environments.md) diff --git a/apps/docs/content/docs/harness-onboarding.mdx b/apps/docs/content/docs/harness-onboarding.mdx index 984b0b0fc..62d3c5c18 100644 --- a/apps/docs/content/docs/harness-onboarding.mdx +++ b/apps/docs/content/docs/harness-onboarding.mdx @@ -3,17 +3,17 @@ title: "Add a Harness" description: "Implement and validate a native Harness adapter." --- -Start with [`agent/harness.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/internal/agent/harness.go). -It defines the required lifecycle, separate optional interfaces and existing -registration methods in one place. Implement one adapter for a native SDK or machine-readable protocol. Core and -Runtime use the common contract; adding a harness should not add engine-name -branches to public handlers, storage, scheduling or environment providers. - -The [harness contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/harnesses.md) defines observable behavior. Start with the -[developer guide](/development) for a working checkout; the -[contributor guide](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/CONTRIBUTING.md#harness-qualification-and-onboarding) -owns architecture and required checks. The pinned public Agents API in -[upstream.json](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/upstream.json) is separate from the internal adapter protocol. +A **Harness** is a native agent engine (Codex, Claude Code, MiniMax Code) that +runs the model/tool loop. A **Harness adapter** translates the common +Core–Runtime execution contract into that engine's SDK or protocol. This guide is +the single numbered path for adding one. Qualification evidence and the +per-operation support table live in [Harness integration](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/harnesses.md). + +The code entry point is +[`agent/harness.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/parsar-daemon/internal/agent/harness.go). It +declares the required lifecycle, the separate optional interfaces and the +registration methods. The pinned public Agents API in [upstream.json](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/upstream.json) +is separate from this internal adapter contract. ## Ownership @@ -45,46 +45,62 @@ qualification. The native daemon supports Linux, macOS and Windows; each adapter declares its qualified platform scope. Managed Providers remain Linux-only. A platform-neutral interface alone does not qualify a harness on another platform. See [native Runtime validation](/self-hosted-native) for the current -acceptance limits. Resource management owns machine selection, allocation and -Environment creation and reclamation. Executor close does not release that allocation or delete the -workspace. Environment reclamation is an explicit resource-management action that -coordinates with execution. Runtime connection, installed capability snapshot, -Session Executor and Turn each have their own lifetime. Native factories receive +acceptance limits. Runtime connection, installed capability snapshot, Session Executor and Turn +each have their own lifetime; see +[Executor and Turn lifetimes](/runtime-protocol#executor-and-turn-lifetimes). +Native factories receive capabilities only after the common Runtime has loaded its bound installed snapshot; see [capability preparation](/environments-and-files#runtime-capability-preparation). Model providers supply model communication configuration, not Turn scheduling or native process ownership. -## Native model configuration - -[`agent/harness.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/internal/agent/harness.go) owns the -preparation, validation, reserved-field, effective-connection and cleanup contract. -The shared wire object is `proto.HarnessConfig`. Adapter declarations in -`internal/harnessconfig/` own these native fields: - -| Harness | Accepted native fields | Application | -| --- | --- | --- | -| Codex | `model_reasoning_effort`: `none`, `minimal`, `low`, `medium`, `high`, `xhigh` | Native app-server `-c model_reasoning_effort=...` and each Turn's `collaborationMode.settings.reasoning_effort` | -| Claude SDK | `effort`: `low`, `medium`, `high`, `xhigh`, `max`; `thinking`: the SDK's `adaptive`, `enabled` or `disabled` object | SDK `Options.effort` and `Options.thinking` | -| MiniMax Code | Empty object only | Existing provider token limits remain required; additional native generation parameters are not qualified | - -Claude `thinking` accepts `display` (`summarized` or `omitted`) for adaptive or -enabled thinking, and an optional positive integer `budgetTokens` only for -`enabled`. The deprecated `maxThinkingTokens` alternative is rejected. -These are native settings, not a shared reasoning vocabulary; model availability -and provider support remain the selected harness's responsibility. - -Native protocol and parameter declarations also feed Core administration's small -configuration-support descriptor. `modeltransport` selects and validates routes -from those declarations. Its conversion qualification is distinct from adapter -acceptance and remote-model support. Follow the route and per-input requirements -in `agent/harness.go`; do not add a second model capability registry or infer -capabilities from model names. Nonempty `harness_config` currently requires a native -protocol route because the translator has no loss report for those parameters. +## Steps + +1. **Pin the native source.** Record the upstream package version and source + revision and document the native entry point next to the adapter. +2. **Implement the adapter** in `apps/parsar-daemon/internal/agent/`: an + `ExecutorFactory`, an `Executor` and a `Turn`. See + [Required adapter interfaces](#required-adapter-interfaces). Reuse shared + process, credential/configuration and local workspace helpers. +3. **Register the kind in the Runtime** in `apps/parsar-daemon/internal/cli`. + See [Register the adapter](#register-the-adapter). +4. **Add the Core engine identifier and service profile.** See + [Add the engine to Core](#add-the-engine-to-core). +5. **Package native prerequisites.** Add a Runtime image under + `services/agents-api/deploy/` and, optionally, + [native installer participation](#native-installer-participation). +6. **Enable and select the engine** through + [engine selection](#engine-selection). +7. **Qualify it.** Run the synthetic integration test, then the real acceptance in + [Harness integration](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/harnesses.md#acceptance-checklist). Record results in + the [qualification table](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/harnesses.md#current-qualified-operations). + +Start with the mandatory text lifecycle, then qualify optional operations one at +a time. Do not copy an adapter's native limitations into the shared Core protocol. + +## Architecture rules + +- Codex, Claude Code and future Harnesses have equal architectural status. The + common Runtime wire protocol and Executor/Turn interfaces own lifecycle, input + receipts, cancellation, recovery and resource access. Each adapter keeps its + native implementation and model/tool loop. +- A new engine supplies an adapter, a qualified profile, registration and an + independently verified deployment. It adds no engine-name branches to API + handlers, persistence, dispatch, scheduling or Environment providers. +- Keep required lifecycle declarations, optional interfaces and registration + methods in `agent/harness.go`. Result types, errors and Registry storage may + stay in focused files. Keep this guide linked to that entry point. +- Use the existing `proto.SupportedAgentKind` and `AgentKindCapabilities` + schema. Do not add a second capability descriptor or a combined optional + interface. +- Onboarding does not require feature equality. Verify common lifecycle + obligations and use the same public assertions for each declared operation. + Optional native differences are separate capability work, not onboarding blockers. +- Never equate accepted parameters with applied native behavior. ## Required adapter interfaces -[`agent/harness.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/internal/agent/harness.go) is the +[`agent/harness.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/parsar-daemon/internal/agent/harness.go) is the canonical interface entry point. Its required lifecycle is `ExecutorFactory`, `Executor`, `Turn` and `TurnSettlement`. Optional Turn and workspace interfaces remain separate; their result types and error values stay in the corresponding @@ -103,7 +119,8 @@ was retained. Runtime closes the output channel in that case. Once input may hav been submitted, return a non-nil Turn even with an error: that Turn owns exactly one output-channel close, and uncertain input must not be replayed. -`Cancel` addresses that exact Turn. `AwaitSettlement` confirms that native events, +`Cancel` addresses that exact Turn (see +[Cancellation ownership](#cancellation-ownership)). `AwaitSettlement` confirms that native events, inputs, function results, interactions, child work and output writes have settled. Only then may `Reusable` be true. False requires a reason and confirmed Executor close before replacement. An error means settlement is unconfirmed. A caller @@ -118,9 +135,23 @@ thread, a Claude adapter retains one streaming Query, and a MiniMax adapter reta its ACP connection and native Session. Core sees the same Executor and Turn semantics in each case. +### Cancellation ownership + +Cancellation is declared on the base `agent.Session` interface, which every +`Turn` embeds. The adapter's Turn implements `Cancel` for that exact Turn and +never retargets it to a successor; `Executor` has no `Cancel`. Runtime dispatch +decides when to call it (for example on `prompt_cancel` or shutdown). + +`Cancel` alone does not transfer resource ownership. `AwaitSettlement` confirms +the Turn settled, and `Executor.Close` retires native resources. Permission and +user-choice responses are the optional `PermissionResponder` and +`UserChoiceResponder` interfaces; implement them only when the adapter emits +those interactions. Unsupported responses receive a negative receipt. The router +keeps its interaction routing and retry ownership. + ## Events, inputs and optional capabilities -Use [`internal/agentdaemon/proto`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/internal/agentdaemon/proto) for neutral +Use [`internal/agentdaemon/proto`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/internal/agentdaemon/proto) for neutral requests, events and receipts. Each Turn emits only its own events with its Run ID, in order, and one terminal outcome. Native IDs and usage must be observed rather than invented. Capture the originating Turn before asynchronous native callbacks; @@ -147,37 +178,68 @@ without implementing its semantics is an error. Workspace reads may use separate read-only preparations; those do not start model work or provide another execution lifecycle. -## Register a supported operation set - -The registration methods are also defined in `agent/harness.go`. The existing -`proto.SupportedAgentKind` descriptor supplies kind, availability, version and -`AgentKindCapabilities`; its schema remains in `internal/agentdaemon/proto`. -`RegisterKind` resets the Executor and preparation registrations, so call it -first. `RegisterExecutor` and `RegisterPreparation` derive preparation flags; -other capability declarations must match verified behavior. Core qualification -still belongs to the service profile and is not granted by Runtime registration. - -1. Pin the upstream source/package version and document the native entry point. -2. Implement the adapter using its SDK or native protocol. Reuse shared process, - credential/configuration and local workspace helpers where applicable. -3. Register its `SupportedAgentKind` and direct-call factory with `RegisterKind`, - then register the shared `ExecutorFactory` with `RegisterExecutor`. A direct-call - factory should delegate to the same Executor implementation. Register separate - read-only preparations only when required by qualified workspace operations. Runtime - advertisements must describe behavior verified for that installation. -4. Add a profile to the existing static catalog, or supply an immutable catalog - through service composition. Custom composition supplies the same - `execution.Policy` to `api.WithExecutionPolicy` and `Dispatcher.Policy`. -5. Package the native prerequisites and select the engine through operator - configuration (`OAC_DEFAULT_HARNESS`). Do not invent a public harness field. - -The static registration surface requires a build. Dynamic plugins are outside -this contract. A small adapter does not remove the need for native qualification. +## Register the adapter + +Registration is static and requires a build; dynamic plugins are outside this +contract. The methods live in `agent/harness.go`, and built-in adapters call them +from [`cli/agent_registration.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/parsar-daemon/internal/cli/agent_registration.go) +(Codex, MiniMax Code) and [`cli/claude_sdk.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/parsar-daemon/internal/cli/claude_sdk.go) +(Claude Code). + +| Order | Method | Registers | +| --- | --- | --- | +| 1 | `RegisterKind(proto.SupportedAgentKind, agent.Factory)` | Kind, availability, version, `AgentKindCapabilities` and the direct-call factory. Resets the other registrations, so call it first. | +| 2 | `RegisterExecutor(kind, agent.ExecutorFactory)` | The shared Executor/Turn lifecycle used for execution. Derives the `Preparation` capability. | +| 3 | `RegisterPreparation(kind, workspaceRead, agent.PreparationFactory)` | Optional. Separate read-only workspace preparation when qualified workspace operations need it. | + +The direct-call `agent.Factory` should delegate to the same Executor +implementation. Every other capability declaration must match behavior verified +for that installation. Runtime registration does not grant Core qualification; +that belongs to the service profile. + The runnable test-only example is -[`testdata/onboarding/main.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/testdata/onboarding/main.go). -It registers a text-only synthetic harness and is never shipped as a real engine. +[`testdata/onboarding/main.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/parsar-daemon/testdata/onboarding/main.go). +It registers a text-only synthetic Harness, demonstrates a Session-owned +Executor, fresh Turns, durable steering, cancellation and history binding, and is +never shipped as a real engine. + +## Add the engine to Core + +Core recognizes engine identifiers explicitly. Add the new identifier to: + +| File | Purpose | +| --- | --- | +| [`internal/engine/profile.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/engine/profile.go) | The static service profile catalog | +| [`contracts/agents-api/v1/core_extension.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/v1/core_extension.go) | Accepted `x_agents_core.harness` values | +| [`internal/api/saved_core_input.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/api/saved_core_input.go) | Saved Agent extension validation | +| [`internal/harnessconfig/builtin/registry.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/internal/harnessconfig/builtin/registry.go) | Built-in Harness configuration | + +The profile is pure: it declares supported placements, public configuration and +result limits and required Runtime controls, using existing public/protocol +types. Profile callbacks cannot query business data, decrypt credentials or +control native processes. Public schema validation, qualified engine support and +actual Runtime capabilities remain separate checks; Runtime advertisements alone +never enable public operations. Shared dispatch checks capability combinations, +not a whitelist of engine names. + +`execution.Policy` supplies immutable service qualification to HTTP admission, +Worker device selection and final dispatch. Custom composition gives the same +Policy to `api.WithExecutionPolicy` and `Dispatcher.Policy`. The zero value uses +built-in profiles; an explicitly empty catalog authorizes none. There is no +mutable global registration or compatibility fallback. + +## Engine selection + +| Setting | Scope | Effect | +| --- | --- | --- | +| `OAC_DEFAULT_HARNESS` | Core deployment | Default engine for new Sessions; `codex` when unset | +| `OAC_HARNESSES` | Core deployment | Comma-separated extra engines to enable, without requiring a managed Provider | +| `agent.x_agents_core.harness` | Saved Agent or inline Session Agent | Explicitly selects an enabled engine; never falls back to another | + +Existing Sessions keep their engine. Do not add another selector; the +[harness selection extension](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/harness-selection.md) owns the field's semantics. -## Required behavior versus optional operations +## Required versus optional operations The current public text path requires durable turns, applied input receipts, ordered observations, cancellation and enforcement of disabled execution controls. @@ -193,14 +255,14 @@ explicitly and record the gap. Never advertise a capability to bypass selection. Structured-output adapters consume `ExecutionControls.OutputFormat` and publish confirmed native output through the existing Message contract. Register public qualification separately from the Runtime capability; see the -[structured-output boundary](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/structured-output.md). No Core engine-name branch is required. +[structured-output boundary](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/structured-output.md). No Core engine-name branch is required. Initial requests, `Executor.StartTurn` and steering consume the same ordered `proto.MessageInput`. Text-only adapters use `TextOnly()` to reject images without discarding content. Image adapters translate each part natively and acknowledge an active batch only after all its messages are applied. Register `MessageImages` and qualify `MessageImagePlacements` separately; see the -[message-input contract and real acceptance](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/message-input.md). +[message-input contract and real acceptance](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/message-input.md). Hosted workspace execution additionally requires verified preparation, workspace reads/output export, network behavior and credential/history isolation. Reuse the @@ -208,61 +270,10 @@ same dedicated Runtime binding and shared Files helpers. A native Bash sandbox alone does not establish isolation for other native file tools. Enable a placement only after its required security and lifecycle behavior is demonstrated. -## Acceptance and delivery - -Before implementation, record the operation set, expected results, exclusions and -stopping conditions in the board. A batch ends when its declared operations pass; -it does not expand to match another harness's feature list. - -- Adapter tests: two ordinary Turns share one native process/connection and history; - cancellation followed by another Turn; stale cancellation and late events; native - exit, cleanup failure, input write/application receipts and unknown outcomes. - Verify fresh per-Turn usage, function, input and child-observation state. -- Shared integration: public admission, actual Worker/device selection, gateway, - daemon registration/dispatch and durable terminal projection. See - `TestThirdHarnessPublicOnboarding` for a synthetic example, not native evidence. -- Real acceptance: pinned official Python SDK and raw HTTP against our Agents API, - real provider API, native harness and independent execution database. Verify - initial execution, warm follow-up, cancellation and restart/continuation. - Record native owner identity and same-condition cold/warm timing; mock results - cannot establish native reuse or performance gains. - For hosted qualification also verify Files/Artifacts, workspace identity, - credential protection and foreign-history rejection. -- Regression: existing qualified engines keep working. Run targeted tests during - development, then `make check` and applicable real regressions. API changes - require `make openapi`; query changes require `make sqlc-generate`. -- Review: follow the repository - [blind review workflow](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/CONTRIBUTING.md#workflow-and-quality). - -Record exact revisions, image/package versions, commands, results and limits. -Keep keys in private operator files; never commit them or include them in logs or -Feishu. Failed or synthetic runs cannot be counted as real acceptance. Merge the -bounded PR after required checks/review, update its board child and reassess the -full board. Acceptance of one engine is not complete public protocol compatibility. - -## Minimal runnable example and native references - -[`testdata/onboarding/main.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/testdata/onboarding/main.go) -is a runnable synthetic adapter. It demonstrates registration, a Session-owned -Executor, fresh Turns, durable steering, cancellation and history binding through -the actual gateway and Router. Run `TestThirdHarnessPublicOnboarding` against a -dedicated test database to exercise the complete integration path. The fixture is -not shipped and is not evidence for a real SDK or sandbox. - -Use the native adapters as implementation references after choosing a native API: - -- [Codex](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/internal/agent/codex/executor.go): app-server transport. -- [Claude](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/internal/agent/claudesdk/executor.go): Go ownership and a [TypeScript SDK bridge](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/packages/claude-sdk-adapter/README.md). -- [MiniMax](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/internal/agent/mcode): ACP and native workspace companion. - -Start with the mandatory text lifecycle, then qualify optional operations using -[harness coverage](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/harnesses.md) and each operation's contract. Do not copy an -adapter's native limitations into the shared Core protocol. - ## Optional Subagent observations A harness that supports the Subagent resource reads implements the existing -[neutral observation contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/subagents.md#common-adapter-contract). It reports +[neutral observation contract](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/subagents.md#common-adapter-contract). It reports verified child identity, lifecycle effects and owned Turn/Item history through the authenticated Run, then qualifies those facts with real execution. It does not add routes, storage branches or a harness-specific Core scheduler. Report @@ -281,4 +292,43 @@ installation; adapters own native layout and probes. Validate installation and actual execution on each advertised platform. Missing or incompatible native content must fail, never install itself during a Turn. -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/harness-onboarding.md) +## Native process ownership + +The shared daemon `clirunner` offers opt-in Unix process-group ownership for +adapters whose SDK launches a native child. Existing callers keep their current +process policy. Explicit cancellation and parent-context cancellation share a +TERM grace period and bounded KILL escalation. An internal reaper also cleans +remaining group members when the direct process exits, even if a descendant +still holds stdout open. During cancellation, surviving descendants keep the +remaining TERM grace after the leader exits. Unsupported hosts reject this mode before launch. + +Owned output pipes remain readable after the leader exits. Consumers must drain +stdout and stderr before calling `Wait`, which joins the cached process result +and closes the readers. `Done` reports leader reaping and group cleanup signals; +it is not a native execution receipt or proof of persisted history. SDK adapters +must settle each Turn and drain its observations before publishing completion. +Executor close additionally closes the query and awaits the native child. Process groups are lifecycle supervision, not OS isolation +or containment of descendants that deliberately leave the group. + +All Harness adapters use bypass execution. Do not restore named Codex permission +profiles, bubblewrap wrappers, native Claude sandbox settings or MiniMax +SandboxManager branches. There is one execution path for every Environment origin. +Resource paths are ordinary operator configuration, not a permission boundary. + +The daemon does not enforce disabled or restricted network policies. Such a +combination must be rejected unless its outer Environment implementation provides +and qualifies the requested behavior. Do not advertise daemon-level network +isolation or silently run a restricted request with unrestricted semantics. +The normal self-hosted combination uses the host's existing network access. + +## Native references + +Use these adapters as implementation references after choosing a native API: + +| Harness | Adapter | Native transport | Runtime guide | +| --- | --- | --- | --- | +| Codex | [`agent/codex`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/parsar-daemon/internal/agent/codex/executor.go) | app-server | [Codex Runtime](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/deploy/codex/README.md) | +| Claude Code | [`agent/claudesdk`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/parsar-daemon/internal/agent/claudesdk/executor.go) | [TypeScript SDK bridge](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/packages/claude-sdk-adapter/README.md) | [Claude Runtime](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/deploy/claude/README.md) | +| MiniMax Code | [`agent/mcode`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/parsar-daemon/internal/agent/mcode) | ACP and native workspace companion | [MiniMax Code Runtime](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/deploy/mcode/README.md) | + +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/harness-onboarding.md) diff --git a/apps/docs/content/docs/runtime-protocol.mdx b/apps/docs/content/docs/runtime-protocol.mdx index 03f7537a5..62c9b3bd2 100644 --- a/apps/docs/content/docs/runtime-protocol.mdx +++ b/apps/docs/content/docs/runtime-protocol.mdx @@ -59,6 +59,126 @@ leases and evicts old Run/interaction routes; the new connection does not inheri them. A valid credential and connection are not authority to choose another Session or Environment binding. +### Executor and Turn lifetimes + +This section owns the separation of execution and resource lifetimes. + +| Lifetime | Owner | Ends when | +| --- | --- | --- | +| Environment allocation | Resource management (Sandbox Provider) | Explicit reclamation, coordinated with Runtime execution | +| Runtime connection | Runtime transport | Disconnection or replacement by a newer connection | +| Installed capability snapshot | Runtime | Its Environment is reclaimed; never on Executor close | +| Session Executor | Runtime | `Executor.Close` on idle expiry, shutdown or confirmed invalidation | +| Turn | Adapter `Turn`, tracked by Runtime | `AwaitSettlement` confirms settlement | + +A Session owns one reusable Executor in its connected Runtime. A Turn owns one +input execution, its output stream and its cancellation. `agent.ExecutorFactory` +prepares the fixed configuration; `Executor.StartTurn` creates a new `agent.Turn` +without replacing healthy native resources. Normal completion settles only the +Turn. `Executor.Close` releases native resources on idle expiry, environment +shutdown or confirmed invalidation. Core does not keep a second Executor cache. +The same lifecycle applies after managed or user-managed environments connect, +and to the qualified no-environment profiles. Resource management owns machine +selection, allocation and Environment creation/reclamation. Closing an Executor +does not release the Environment allocation or delete its workspace. Environment +reclamation explicitly coordinates with Runtime execution. Connection, installed +capability snapshot, Session Executor and Turn retain separate lifetimes. + +The Runtime binds its Executor record to Session, Environment, connection and +immutable execution configuration. Resume identity and prior-Turn recovery flags +are continuity assertions, not configuration changes. A supplied native identity +must match the retained owner; exact-history recovery never starts a new root +when existing history is required. A configuration conflict is an error, not a +hot switch. Lost connections retire their owners and handles. Old timers, output +and cancellation cannot affect replacements. + +Each Turn receives a fresh wrapper, output channel and receipt state. Optional +steering, functions, permissions and user-choice interfaces belong to that fixed +Turn. Native callbacks capture the originating Turn before asynchronous work; +late events cannot be assigned to whichever Turn happens to be active. Native +processes, query/transport connections, fixed capability configuration and native +Session identity belong to the Executor. Do not reset completed `sync.Once` +values or repurpose an old Turn object. + +`StartTurn` returning nil guarantees that no native input was submitted and the +output channel was not retained. The Runtime then closes that channel. Once input +may have been submitted, return a non-nil Turn even with an error: the Turn owns +exactly-once output closure and remains tracked until settlement. Unknown input +is never replayed. A definite `executor_unavailable` Start rejection permits one +common recovery attempt only after the previous Executor has been closed and no +input was submitted. Recheck the same physical peer and current authorization. + +`Turn.Cancel` targets only that Turn and does not close a healthy Executor. +`AwaitSettlement` applies after both natural completion and cancellation. Success +means output can no longer be written and the Turn's native events, input, +functions, interactions and child work have settled. Native completion or +cancellation confirmation is independent of resource retirement: closing a +transport cannot supply missing native terminal or operation receipts. +`Reusable=true` additionally +confirms that the native owner can accept the next Turn. `Reusable=false` requires +a reason and subsequent confirmed Executor close. An error means settlement is +unconfirmed; it cannot free ownership or capacity. Caller deadlines stop waiting, +not tracked cleanup. Retry the same cleanup target serially. Failed cleanup +blocks replacement and retains its resource slot. Executor Close confirms resource +retirement independently of the Turn outcome: an immutable Turn error must not +prevent closing the native transport and releasing resources once their work and +output have stopped. + +One output consumer starts before native Start, drains the bounded 64-frame +channel, and retains the terminal observation until Start publication, Turn +settlement and admitted operation receipts finish. Natural Done never calls +Cancel. Input, function and interaction admission close before settlement, and +operations already admitted hold their barrier through native receipts and +outbound acknowledgement. Send cancellation to the fixed Turn before waiting +for that barrier: a written input may need native interruption to produce its +receipt. Join native settlement, any required confirmed Executor close, output +drain and all admitted operations before an applied acknowledgement or reuse. +A failed Close may report failure while retaining the same Run and outstanding +operations for retry. Closing a caller wait cannot manufacture an applied input receipt. Only then forward Done or an applied cancellation +receipt. Commit native continuity and release the old Run admission before +publishing Done, since the receiver may immediately start another Turn. A late +terminal-send failure belongs to the old Run; it cannot invalidate a successor +that already owns the Executor. Connection shutdown owns transport-loss cleanup. +Preserve the ten-second settlement wait and separate five-second receipt +send budget; timeout is not proof of quiescence. The observed cancellation outcome +retains native identity, Usage and output without fabricating missing evidence. +Codex native Turn interruption can leave background terminals alive. Its adapter +uses the exact thread-owned terminal list and confirmed per-terminal termination +before settling cancellation, and repeats this cleanup during Executor close. +The bulk clean acknowledgement does not prove termination. Failed cleanup keeps +the native owner available for a later close attempt. + +Private preparation controls reserve a per-Turn admission, not a new Executor. +They carry an explicit Session identity and immutable configuration without model +input or Run identity. A fresh request returns a connection-local admission handle +and the owning Executor ID. Start supplies both identities and its actual Run ID +and ordered MessageInput. Per-admission revisions order status observations; +rejections describe control errors without inventing Run events. A reused healthy +Executor returns ready without native preparation. An admission release abandons +that admission; it does not close the Session's healthy idle Executor or cancel a +later Turn. Cancellation uses the exact Run identity. + +Preparations and Start execute outside the receive loop and Router lock. Admission +expires after five minutes; retries do not extend that deadline. Bound active +preparation and execution separately from idle retained resources, and count +closing or uncertain resources until cleanup succeeds. A definite +`execution_prepare` rejection with `preparation_capacity` leaves an unclaimed +queued Turn for the existing Worker scheduler to retry, including capacity held +by cleanup. Other errors and uncertain input delivery do not authorize replay. +At most 64 admission records are retained; old handles never consume replacement +admissions. Idle expiry +is a Runtime resource policy, not Core active-Turn concurrency. Shutdown tracks and +closes active and idle Executors, retains failed close targets, and allows a later +serialized retry. Ordinary disconnection closes the failed transport and keeps +the exact Router until shutdown succeeds. A wait timeout or failed cleanup cannot +authorize reconnect; process shutdown also keeps waiting rather than silently +discarding owned native resources. These records are connection-local, not durable +input replay. + +Read-only workspace preparations remain separate bounded filesystem operations; +they cannot start model work. Workspace operations retain exact binding and +settlement rules across Turn boundaries and Executor closure. + ## Envelope and identity Every data frame is one JSON @@ -105,6 +225,29 @@ Adapters preserve message/content order and explicitly reject unsupported conten Usage frames and the final usage snapshot replace earlier cumulative snapshots; do not add them. An absent measurement is unknown, not zero. +The wire version is `proto.Version` (see above). Initial, prepared and active input use +the same ordered MessageInput contract, replacing scalar prompts and attachments. +User-message boundaries and text/image order remain intact through Core and the +Runtime wire; adapters own native conversion and receipt aggregation. Text-only +transports reject image content rather than dropping it. Text is never trimmed: +engine profiles declare whether whitespace-only messages are qualified, and +unqualified harnesses (Claude SDK, MiniMax Code) reject them at admission rather than having +their input rewritten. Codex has a flat native +input list and uses blank-line separators between messages; this does not preserve +independent native user-message boundaries. +The independently packaged Claude bridge uses protocol 3 for a prepared Executor +and separately identified Turns; readiness rejects other protocol versions. +Image-bearing messages require a qualified profile/placement before persistence +and image support from the selected Runtime before native delivery. These checks +apply to that operation only; ordinary text retains offline queueing. Initial, +prepared and active paths use the same content and preserve receipt ownership. +The qualified public profile is inline PNG/JPEG on Codex/Claude `none` and +Core-managed Docker `openai_hosted`. Self-hosted/user-managed images, MiniMax +images and remote URLs remain explicit implementation gaps. Hosted images reuse +the existing preparation, active-input and workspace authority; they do not add +a downloader, a mount or a separate execution lifecycle. Core +does not fetch or transform media. See [message input coverage](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/message-input.md). + ## Preparation and execution order Hosted and self-hosted execution use the same preparation semantics. Placement @@ -145,6 +288,80 @@ by `released`, `expired`, or `failed`. A `rejected` control operation has an Read-only workspace preparation cannot start a Turn. Expiry does not remove the Runtime's obligation to settle cleanup. +Core and Runtime use common preparation, start, input-receipt, cancellation, +release and recovery semantics for Codex, Claude Code and MiniMax Code. Retain each +harness's native implementation behind its adapter. Core acts on verified capabilities and runtime +conditions; a capability declaration alone never grants public feature admission. +Extend existing interfaces during related functional work without introducing a +second framework or a broad rewrite. Codex, Claude Code and MiniMax Code have +qualified dedicated Docker profiles and historical Core-managed E2B evidence. +New user-managed enrollment requires separate real acceptance. Each harness has equal standing; +qualify each image/template with the common full-loop acceptance before deploying. +Additional engines remain separate work; V1 has no separate remote executor. +Later engines must satisfy the same applicable acceptance contract while keeping +their suitable native deployment layout. + +Workspace reads may request the private `workspace_read_only` preparation profile +through the existing preparation factory and verified `workspace_read_preparation` +capability. It accepts only the bound Environment and resource identity; execution +options, model/MCP credentials, native Session continuation and model/tool input are excluded. +The Codex adapter creates temporary local state, reuses its native connection and +directory transport, and rejects Start. Its child inherits only process/transport +essentials. The private native read mode excludes system, managed, user and project +execution configuration and plugin startup while preserving native security +requirements. Ordinary execution keeps its stable state and configuration. +Reject the legacy mixed managed-config profile for reads rather than discarding +its enforced constraints together with execution settings. +For this read profile, `released` is published only after local Close succeeds; +cleanup errors retain ownership and report `cleanup_unconfirmed`. A failed factory +must return its resource with the error if cleanup remains unconfirmed; wrappers +must preserve both values. Successful cleanup retries publish confirmed release, +and stale status snapshots cannot publish success. Failed terminal status delivery +does not retry cleanup; ownership remains until an explicit release or shutdown retry. +A release request, +HTTP disconnect or remote socket closure alone is not cleanup confirmation. This +profile does not establish remote mutation quiescence or public Files admission. + +Core directory reads reuse the Worker's Session scheduling reservation for idle +preparation and target the exact Run for active execution. Device selection uses +operation-specific capabilities; reading files never resolves model/MCP options +or creates a Turn. HTTP cancellation ends observation, not an admitted native read. +Keep the idle reservation through the bounded read and release attempt. Return +directory data only after confirmed Close; incomplete reads or uncertain cleanup +return unavailable without data. Release the Worker's scheduling reservation before +delivering the result so the caller can immediately request the next page. +Revoke the scoped read transport credential on +completion or failure. Runtime retains uncertain cleanup ownership and capacity; +this does not require a second durable Core owner registry or establish remote +write retirement. Public Files.list delegates workspace access to this reader; +the API owns tenant authorization, path validation and protocol pagination. Only +the reader's distinct `not_directory` result (a missing path, a regular file or an +unfollowed symlink) becomes an empty page; root, permission, transport and +uncertain failures keep their errors. Keep partial directory coverage and +unverified defaults explicit in the Files contract. + +Local inline file delivery uses the same authenticated daemon connection and exact +Environment/Session binding. All platforms use the daemon's Go implementation +for bounded file reads, directory listing, file creation and output export. +Files operations require no external helper executable or staging directory. +The Files API keeps workspace-relative paths and no-overwrite creation semantics; +it does not restrict native Harness tools' host permissions. + +Transfer a complete bounded body in acknowledged 64 KiB frames before invoking +the native file writer, verify the declared digest, and run no model for upload. +Keep the private 50 MiB transfer bound distinct from the official 5 MiB decoded +inline bound, which the API checks before any Runtime work. Files.create uses the +native writer's no-overwrite operation. Initial Session files retain their separate +atomic replacement behavior; do not change one caller's semantics for another. +The dedicated Runtime excludes execution while receiving or applying a write; +malformed, incomplete or expired transfers cannot reach the installer. Exact +commit/rejection receipts release the mutation owner. Missing or ambiguous +receipts retain uncertainty; observer cancellation and local process exit cannot +prove non-mutation. Before public admission, Core must durably reserve the write +under the Session lock and prevent successor mutation across restart until exact +settlement. Do not replay the request or introduce general replacement machinery. +Read-only operations retain their own authority and bounded ownership requirements. + ## What each acknowledgement proves | Observation | Proven fact | @@ -207,6 +424,70 @@ error permits reconnecting the channel; authentication/version rejection require operator correction. Cleanup of an Executor remains separate from the Sandbox Provider's confirmed reclamation of compute. +## Workspace operations and connection observations + +The private daemon `workspace_read` control targets an existing preparation handle +or its transferred active Run on the same authenticated device connection. Require +the exact frozen Environment identity; callers cannot supply sockets, credentials +or workspace roots. Shared routing uses the optional `agent.WorkspaceReader` +interface, without selecting an engine by name. +The same control accepts `operation: directory` through the optional +`agent.WorkspaceDirectoryLister`, with mutually exclusive byte/entry limits and +typed directory metadata. Directory responses carry at most 1024 single-component +UTF-8 names of at most 255 bytes, so escaped metadata stays below the existing +frame bound. These are private transport limits, not public Files parameters. +Byte and directory operations share target checks, correlation, capacity and +retained operation waits; neither creates a Run or selects an engine by name. +This control does not itself authorize a public Files endpoint or placement. + +The separate optional `agent.WorkspaceDirectoryLister` observes one workspace-relative +directory on the existing Prepared/Session owner; empty path selects its root. +Return single-component names, entry kind, regular-file byte size and explicit +truncation only after directory/metadata access and handle cleanup settle. Reuse +byte-read admission, uncertainty and caller-detach ownership where applicable. +Do not promise a snapshot, recursive traversal or public pagination through this +private interface. The Runtime invokes the retained directory helper against the +frozen local workspace. Keep the qualified helper outside writable paths, anchor +traversal to no-follow descriptors, bound enumeration and require a complete +validated result. Helper availability alone does not enable public Files admission. + +Bound encoded request payloads to 8 KiB and correlation IDs to 128 bytes before +admission. Do not echo oversized IDs; omit oversized trace metadata in replies. +Bound raw control results to 1 MiB within the existing 4 MiB transport frame; the +limits are local policies, not pinned public protocol limits. Successful reads require complete bytes/truncation and acknowledged native +close. Safe native rejections carry no bytes; interrupted or ambiguous reads remain +unknown and stop further reads on that owner. Local RPC reap never establishes file +settlement. Retain a dispatched read's original bounded waiter across observer +cancellation and resource transfer/release; stop new admission on resource closure. +The gateway bounds subscriptions and never retries or replays on reconnect. Duplicate +pending operation IDs cannot start another read; this control does not promise durable +idempotency or result recovery. Preparation/Run ownership, public path authorization, +public file authorization and native cleanup retain their separate requirements. + +The retained Rust directory, write and workspace-export helpers provide bounded +filesystem operations for the colocated Runtime. Their protected executable paths, +descriptor-relative traversal and exact workspace binding remain required. They +do not implement an executor transport or grant tenant authority. + +Connection observations use the existing execution lease and Session lock. A +separate `environment_connections` row retains the current generation and revision; +`environments.status` and its Session Environment-event snapshot commit together. +The producer serializes replacements, then numbers socket observations within each +generation. Duplicate or older revisions and superseded generations are inert. +Replacement retires a previously connected observation before publishing its new +registration. Registration alone creates no connected event. Event payloads contain +only public Environment identity/type/status and nullable error, never configuration, +credentials, registration IDs or revisions. Transport observations have no asserted +Turn association. `connected`/`disconnected` are distinct from native preparation +readiness; do not cast resource `expired` into the event vocabulary or emit `ready` +for a self-hosted connection. + +The existing Worker observes authenticated daemon peers for enrolled Environments, +using durable generation/revision fencing under its execution lease. On restart it +reconciles old connection observations before admitting new ones. A connection or +heartbeat does not prove native readiness or process quiescence. Failed or stale +observations cannot establish a current connection. + ## Contract verification Run `make check-runtime-contract` from the repository root. It exercises the diff --git a/apps/docs/content/docs/sandbox-provider.mdx b/apps/docs/content/docs/sandbox-provider.mdx index d8e341529..b7810177c 100644 --- a/apps/docs/content/docs/sandbox-provider.mdx +++ b/apps/docs/content/docs/sandbox-provider.mdx @@ -3,24 +3,73 @@ title: "Add a Sandbox Provider" description: "Environment allocation, bootstrap and reclamation." --- -Start at [`sandbox_provider.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/sandbox/sandbox_provider.go). -It defines the required `SandboxProvider` and optional `CheckpointProvider` -interfaces. Use the existing types; do not introduce another lifecycle protocol or -an execution path for a particular vendor. +A **Sandbox Provider** supplies the outer compute (an *Environment*) that a Core +Runtime daemon runs in, plus the bounded bootstrap that starts it. This guide is +the single start-to-finish path for adding one. The canonical interface is +[`SandboxProvider`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/sandbox/sandbox_provider.go). -Core owns durable Environment, allocation, placement and cleanup state. A Sandbox -Provider supplies compute and the bootstrap needed to start its Runtime (daemon). -Runtime prepares capabilities and workspaces through the common daemon protocol; -Harness adapters translate execution into native operations. The same Runtime -preparation and execution contract applies to a user-owned machine. That machine -has no Core-owned compute allocation to create or destroy. +## Before you start -Sandbox isolation belongs to the outer infrastructure. A daemon running directly -on a user's machine runs with that user's authority; installing the daemon does -not establish a filesystem, tool or network sandbox. Provider isolation and -network enforcement must be qualified independently of daemon connectivity. +| Term | Meaning | +| --- | --- | +| Environment | Durable execution place owned by Core; see [Environments](/environments-and-files) | +| Allocation | One Core-owned compute lease for an Environment, identified by `Reference` | +| Runtime | The daemon inside the Environment; it prepares capabilities and executes Turns | +| Deployment | The single deployment-wide provider selection; see [Sandbox deployment](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/sandbox-deployment.md) | -## What to implement +Core owns durable Environment, allocation, placement and cleanup state. The +Provider supplies compute and bootstrap only. Runtime prepares capabilities and +workspaces through the common [daemon protocol](/runtime-protocol); Harness +adapters translate execution. A user-owned machine uses the same Runtime +contract but has no Core-owned allocation to create or destroy. + +Use maintained provider SDKs behind thin adapters. Hosted deployments select one +deployment-wide Provider: E2B cloud, or Docker/microsandbox on +administrator-owned nodes. Providers never execute Core initialization commands; +initialization, daily execution and Files use the daemon's typed Runtime operations. + +Isolation belongs to the outer infrastructure. The daemon runs with its +launching user's authority and adds no filesystem, tool or network sandbox. +Qualify provider isolation and network enforcement independently of daemon +connectivity; see [Runtime and outer isolation](/concepts#runtime-and-outer-isolation). + +## Steps + +1. **Read the contract.** Implement the five required operations and any + optional interfaces in [Implement the interface](#implement-the-interface). +2. **Write the adapter package** under `services/agents-api/internal/sandbox/` + (native SDK calls, ownership checks, identity translation, private config). + Assert `var _ sandbox.SandboxProvider = (*YourAdapter)(nil)` at compile time. + Out-of-process helpers live in `services/agents-api/tools/-provider`. +3. **Register the kind** in every file listed in + [Register the provider kind](#register-the-provider-kind). Registration is + explicit construction, not an init-time plugin registry. +4. **Label owned resources** with `io.oac.*` labels, or `oac_*` metadata keys + where the vendor rejects dotted keys. See [Label naming](#label-naming). +5. **Run the contract suite** with `make check-sandbox-provider-contract`. See + [Validate the integration](#validate-the-integration). +6. **Run native acceptance** against real compute, then `make check`. Record + evidence and limits in [Sandbox deployment](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/sandbox-deployment.md). +7. **Document operator setup** next to the adapter, like the + [reference adapters](#reference-adapters). + +## Implement the interface + +`SandboxProvider` has **five required operations**: + +| Operation | Purpose | +| --- | --- | +| `Create` | Create compute for a `Reference` and run the bounded daemon bootstrap | +| `GetInfo` | Observe current compute without mutation | +| `Renew` | Extend a native lease, or observe when the backend has none | +| `Kill` | Reclaim the allocation's compute and retained resources | +| `RunCommand` | Bounded administrative bootstrap/diagnostic command | + +Use the existing types; do not introduce another lifecycle protocol or a +vendor-specific execution path. A backend without a native renewable lease +(Docker) still keeps service-owned hosted expiry and cleanup requirements. + +### Required and optional interfaces | Contract | Requirement | Responsibility | | --- | --- | --- | @@ -33,55 +82,14 @@ Do not implement an optional interface with successful no-op methods. Core uses interface assertions to select optional operations. Advertised support requires contract and native acceptance evidence; a healthy node or an available CLI does not establish it. Observation never renews a lease, starts compute or prepares a -Harness. See the [observation contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/runtime-observability.md). +Harness. See the [observation contract](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/runtime-observability.md). `RunCommand` is an existing administrative bootstrap/diagnostic facility, not an alternate route for Skills, Plugins, MCP setup, initial files, Session execution or Files. Those use Runtime. Confidential command input travels in `Command.Stdin`, not argv or logs. Preserve its byte order, bounded output and actual exit status. -## Register an adapter - -Registration is explicit construction, not an init-time plugin registry: - -1. Put native SDK calls, ownership checks, identity translation and private - configuration in an adapter package under `services/agents-api/internal/sandbox`. - Assert `var _ sandbox.SandboxProvider = (*YourAdapter)(nil)` at compile time. -2. For a node-local adapter, add its typed construction and readiness probe in - [`sandbox/config`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/sandbox/config/config.go). - The existing `Built` result returns the provider, probe, installation identity, - backend fingerprint and specification digest; the factory also returns its - close function. Construction must not create an Environment. -3. For a direct adapter, register its construction in - [`managedSetup.provider`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/cmd/server/managed_setup.go). - Node-local adapters are selected there through the existing node proxy. Keep - vendor-specific deployment validation and SDK setup at this boundary. -4. Preserve the existing `execution.RuntimeProvider` deployment binding: - `ProviderKind`, installation ID, backend fingerprint, generation, mode and - node ownership identify the selected backend. The database owns the selection; - the in-memory selection is not an alternate authority. Register optional - interfaces on direct adapters and their transport wrappers consistently. -5. Qualify the provider's operator configuration and resource limits in the - existing deployment specification/enrollment path. The shipped deployment - parser and persistence constraints explicitly enumerate supported backends; - update that registration surface when adding one. Do not bypass it with an - arbitrary provider string. This is deployment admission, not a new execution - workflow. Adding a new public capability requires its own protocol change. - -Today Docker and microsandbox use nodes; E2B is constructed directly. The node -proxy exposes checkpoint operations only for its registered checkpoint-capable -backend. A new node backend must register both construction and the corresponding -proxy capability; otherwise that capability is unavailable. Provider names belong -in this adapter/configuration wiring, not Session scheduling, capability -preparation or Turn execution. Common lifecycle code uses `CheckpointProvider` -to admit suspension, independently of a provider name. - -The backend fingerprint identifies a native resource namespace, not mutable -capacity. Core retains deployment generations so old owned allocations continue -to resolve to their original backend. This is resource ownership, not historical -binary compatibility. Do not repoint retained allocations at a replacement backend. - -## Identity and resource responsibility +### Identity and resource ownership `Reference` is the exact `(TenantID, EnvironmentID, AllocationID)` tuple. Core persists a fresh allocation ID **before** `Create`; it is not the Environment ID. @@ -116,7 +124,7 @@ name collision. Core releases durable ownership only after confirmed cleanup **and** settled creation. Executor release or Harness cancellation does not delete an Environment, its workspace or its Sandbox Provider allocation. -## Operation outcomes and retries +### Operation outcomes and retries All calls receive bounded contexts. Expiry or cancellation ends the caller's wait; it does not prove rollback, native stop, cleanup or absence. An adapter or @@ -143,10 +151,10 @@ Persist operation IDs and provider-returned snapshot provenance unchanged. another capture or restore. `ResumeCompute` only thaws the retained source; it must not cold-start a stopped one. Cleanup targets the exact compute incarnation and snapshot, not whichever instance currently has the same display name. See -[the lifecycle implementation](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/execution/runtime_compute.go) +[the lifecycle implementation](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/execution/runtime_compute.go) and its failure tests before advertising this capability. -## Four distinct readiness facts +### Four distinct readiness facts | Fact | Evidence | Does not establish | | --- | --- | --- | @@ -161,10 +169,63 @@ Runtime installs/reads local capabilities and freezes the result for the Session Reconnection can reuse that prepared content; a new Session takes a new snapshot. A provider must not implement a competing preparation path. +## Register the provider kind + +Provider kinds are enumerated explicitly. Add a new kind to each surface +below; none accepts an arbitrary provider string. + +| Surface | File | What it enumerates | +| --- | --- | --- | +| Node-local construction | [`sandbox/config/config.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/sandbox/config/config.go), [`specification.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/sandbox/config/specification.go) | Typed construction, readiness probe and node specification | +| Direct construction | [`cmd/server/managed_setup.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/cmd/server/managed_setup.go) (`managedSetup.provider`) | Direct adapters (E2B) and node-proxied kinds (Docker, microsandbox) | +| Resource validation | [`sandbox/deployment.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/sandbox/deployment.go) (`Resources.Validate`) | Per-kind resource limits | +| Node proxy and identity | [`sandbox/node/proxy.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/sandbox/node/proxy.go), [`node/identity.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/sandbox/node/identity.go) | Node-capable kinds and their checkpoint capability | +| Deployment persistence | [`sandbox_deployment_mutations.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/store/sandbox_deployment_mutations.go), [`sandbox_deployment_setup.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/store/sandbox_deployment_setup.go), [`sandbox_specification.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/store/sandbox_specification.go), [`runtime_node_deployment.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/store/runtime_node_deployment.go) | Deployment admission and node-mode kinds | +| Database constraint | A new migration in [`migrations/`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/migrations) | `provider_kind` `CHECK` constraints, last set in `000081_sandbox_generations.sql`; never edit a landed migration | +| Node installation | [`node_installation.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/core-console/node_installation.go) | Node artifacts per kind | +| Installer | [`deploy/install/sandbox_setup.py`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/deploy/install/sandbox_setup.py) (`CHOICES`, used by `install.py --sandbox`) | Installer backend choices | +| Distribution | [`scripts/core-distribution-manifest.py`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/scripts/core-distribution-manifest.py) | Bundled provider helper binaries | +| Clients and Web | [`sandbox-client.ts`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/packages/agents-client/src/sandbox-client.ts) (`SandboxProvider`), [`features/sandbox`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/web/src/features/sandbox) (`SandboxSetupWizard.tsx`, `console-config.ts`, `node-enrollment.ts`), [`sandbox-labels.ts`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/web/src/lib/sandbox-labels.ts) | Backend choices and labels shown to operators | + +Keep vendor-specific deployment validation and SDK setup at the construction +boundary. Construction must not create an Environment. For node-local adapters +the `Built` result returns the provider, probe, installation identity, backend +fingerprint and specification digest; the factory also returns its close function. + +Preserve the `execution.RuntimeProvider` deployment binding: `ProviderKind`, +installation ID, backend fingerprint, generation, mode and node ownership +identify the backend. The database owns the selection; the in-memory selection +is not an alternate authority. Register optional interfaces consistently on +direct adapters and their transport wrappers. A new public capability needs its +own protocol change. + +Today Docker and microsandbox use nodes; E2B is constructed directly. The node +proxy exposes checkpoint operations only for its registered checkpoint-capable +backend. A new node backend must register both construction and the corresponding +proxy capability; otherwise that capability is unavailable. Provider names belong +in this adapter/configuration wiring, not Session scheduling, capability +preparation or Turn execution. Common lifecycle code uses `CheckpointProvider` +to admit suspension, independently of a provider name. + +The backend fingerprint identifies a native resource namespace, not mutable +capacity. Core retains deployment generations so old owned allocations continue +to resolve to their original backend. This is resource ownership, not historical +binary compatibility. Do not repoint retained allocations at a replacement backend. + +## Label naming + +Every owned native resource carries ownership labels so mutations, reads and +cleanup can verify the `Reference` and installation. + +- Use the `io.oac.` prefix, for example `io.oac.installation` and `io.oac.tenant`. +- Where the vendor rejects dotted keys, use `oac_` metadata keys (E2B). +- Never accept older label names as a fallback; see + [Runtime names](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/CONTRIBUTING.md#openagentcore-runtime-names). + ## Validate the integration Run `make check-sandbox-provider-contract` while developing. It runs the shared -[`contracttest`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/sandbox/contracttest) suite through +[`contracttest`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/sandbox/contracttest) suite through real adapter boundaries using controlled native failures, plus existing adapter and node transport tests. The same packages are included in `make check`. New adapters should call the public failure runner with native-side fixtures, @@ -185,4 +246,18 @@ SDK helper tests use `make check-e2b-provider` and `make check-microsandbox-provider`. Passing controlled contract tests is not a claim of live cloud or model acceptance. -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/sandbox-provider.md) +Mocked compute cannot prove that resources were reclaimed or that the outer +Environment provides isolation. + +## Reference adapters + +| Kind | Adapter | Helper | Operator guide | +| --- | --- | --- | --- | +| Docker (node) | [`sandbox/docker`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/sandbox/docker) | Node proxy in [`sandbox/node`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/sandbox/node) | [Hosted sandbox manager](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/HOSTED-SANDBOX-MANAGER.md) | +| microsandbox (node) | [`sandbox/microsandbox`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/sandbox/microsandbox) | [`tools/microsandbox-provider`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/tools/microsandbox-provider) | [`deploy/microsandbox`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/deploy/microsandbox/README.md) | +| E2B (direct) | [`sandbox/e2b`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/sandbox/e2b) | [`tools/e2b-provider`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/tools/e2b-provider/README.md) | [`deploy/e2b`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/deploy/e2b/README.md) | + +Provider selection and E2B setup are owned by +[Sandbox deployment](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/sandbox-deployment.md). + +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/docs/sandbox-provider.md) diff --git a/apps/docs/content/guide-sources.json b/apps/docs/content/guide-sources.json index 50476d35d..eefb1d50e 100644 --- a/apps/docs/content/guide-sources.json +++ b/apps/docs/content/guide-sources.json @@ -1,60 +1,58 @@ { - "revision": "f45a4198aca79bf252412c55cad3647f5d704515", + "revision": "e4d5a1d30520a967b3a44a257ed1b6c21e388388", "sources": { - "docs/getting-started/README.md": "d46e1b6cd0693a282f504297c87df6ddf26b99ce8c7afdb613666acd836767f5", - "docs/design-principles.md": "21a05fb089804db6fab5674ec9a6cf849a3affedad2ca927a38d9b46eeec9a50", - "docs/web/architecture.md": "b00352d49295a8ac4440420b748421af81a90778531698c707fefff4eb6223b1", - "docs/getting-started/install.md": "30c12edfdfb40c136065d0a83aad28568d7041328cc5d480d0546847d1b72e7d", - "docs/getting-started/install-options.md": "c806f8f229edccc8bcc4a853976e330cfee6025c9d3d00f6960fe2ee97a8c1d6", - "docs/configuration.md": "6f1bda6a9d16f893a14dcca6fe7df0b93acba77654a23248dee3b0f0ec288a24", - "docs/web/core-connection.md": "6467595fde1c8931f27de76ac38762dfb5a7880651e859d9aa71e233ea95a9c1", - "docs/getting-started/quickstart.md": "963e39eb81c65511a28d0826b2d8c3849dc29d1b810de12df2859d7aefd6d266", - "docs/api/README.md": "5e0b3ae0e178673f2727b74a55ce566fd562006cc5e9a8a7929ea6a183cef154", + "docs/getting-started/README.md": "cdf8046c616574102a0ffc7644ac0fe82e63eb1906e6c9f1f6b635810e223757", + "docs/design-principles.md": "a33acd6e7bc80105a5c934d0be214c0cff59103e96c098f5b09179c86156c01d", + "docs/web/architecture.md": "80bdb8053c06c2b2030b193aae17d5434545f9c72af50e9d3df3ade522fe25ed", + "docs/getting-started/install.md": "a437b90c2cc4755e0fd760066837bfec96e9aa4a4a4bc5bc865b8ad02162c197", + "docs/getting-started/install-options.md": "aac219f367619036aa01573a295802fb8f0418e3d6db931efcfc88f17cf7734c", + "docs/configuration.md": "39cfa7f17025a615ebe2806651800ce8437cea741dd43227828b0b311c4c174b", + "docs/web/core-connection.md": "46f86520e70bf41079708e2c398655aa9087ad9488c8e661d9a73035d11098c4", + "docs/getting-started/quickstart.md": "238b4de1137edb8c2998b38f4525fea345a19fd4de7a455aa9c4c9e4fc3c9b36", + "docs/api/README.md": "592fc2aef474e36eb760be0dd1b2269b6766c0d984f826eaf6f47711ea139387", "contracts/agents-api/execution-tools.md": "e0b61f6c0c236c362186c5f6d0ad69a16dd8a8d9afabe1329a1fe22e5f4a71e9", - "docs/api/public-agent-api.md": "219efab820d8b5cd35855d105b7cfc2b45c7e703f04a0145bf435a99b08a9e38", - "docs/examples.md": "0e1bdaeff51c9c36779f817be31ea9816b7d8cb2290cf8350d3c4801f436f4f9", - "contracts/agents-api/environments.md": "4c4b9423b22919d6aa6bd98d96422e1a8d610d3442d4cf7731a575bd31fd43f0", - "docs/getting-started/nodes.md": "13e1f1e867d3e361ff860960ceacc2dd5806e64f4176cd33f5e49f9a9f4d57e4", - "docs/getting-started/self-hosted.md": "4591e05bfa7657f647bedecd9db07b7086acd94b5548511c88f7e5e3567fdb11", - "docs/self-hosted-native.md": "a3b470461285f0aa9507b262ec8edf11e02f6683328d05206849debe1360b77f", + "docs/api/public-agent-api.md": "5b04e5ec9311df851376f66fc1584dd48ca123cd8c26ca63277c10b9172e9955", + "contracts/agents-api/environments.md": "9c0e794738c017e9dde57a659dc184d347e33730103583534c43e86d5d4183fd", + "docs/getting-started/nodes.md": "e50a2ca588c9d7cb836aa58e7b86ca685cd9da57742ceaee83ee0d549e538a94", + "docs/getting-started/self-hosted.md": "99d8851399bde2310196ac89dae1c365bba144421dbb33153094d30072de1783", + "docs/self-hosted-native.md": "e69819c3201109c8be5413716853eafa22d9d84b57aac780a4d335149b6a926b", "apps/web/public/onboarding/monitor-en.webp": "29dc220cb1250c7016b7c4bf7f30e9510b07c07b814a48c3aa9303b2d76f20b2", - "docs/web/README.md": "47159689b2488f0a94a1af8488bcf98b465e0cca4003d64a4699d7b4db24e086", - "docs/api/web-management.md": "ec9a583c0a94845082ec31c7220ddcfb9aff7147c63fb36fa4c87463570f1b8c", + "docs/web/README.md": "9fce295f6f6ff04acefe338679c1e54723e123dd0027a778202adfa9ab95b6c4", + "docs/api/web-management.md": "d5f982922320069be5017cd524095d86eee0d66fd0d1cf97d1cdd97544d0dce5", "contracts/agents-api/runtime-observability-api.md": "cd46e777fe716a7e76374f4655c96dfbad9d8be1bfca6203ed6118ab87874c2b", - "docs/getting-started/operations.md": "f5c0942003373aaf604fc0f5bb88e0d022f465f1ade2db2192975a173efe7d95", - "docs/user-guide.md": "1069d0f12aa42dde49693f25e8d964984e1c335fc67527123d31ee6a99617bab", - "docs/development.md": "3e03e2666c92380d207ab53285e3b85fdcb042481c8ca76219ed08d629d52812", - "contracts/agents-api/harness-onboarding.md": "2c744598f39c156f868418423abdf45d0b52e04b7debaa50ffc95b1b4bb91a87", - "docs/runtime-protocol.md": "6baa332705743e34b8565613d743d1b30099d8af78159b06ad921ffa7e164a9f", - "docs/sandbox-provider.md": "dd8c2c2b2e2acf4d6ae564f56ffaf5685d74a84fa7a7a15b4cad851015ee36ab", - "apps/docs/scripts/guides.json": "f4b2e58b64a89068a68a58ed1d8fe75893ee09d415e87391121e0e99bb1fbe3f" + "docs/getting-started/operations.md": "2cba15fe56936cbc51e2ec8b8a888686273b21f729711e45c14ca6e32758663d", + "docs/user-guide.md": "a601c7f9df02c3bfe9c4ca3a43b91ab5721f3f8e904fccd116418dbdc1665a53", + "docs/development.md": "b40b7dd035247ecce8532f6e4c7c6365c1265506a1ed6d77e321bc4b00281667", + "contracts/agents-api/harness-onboarding.md": "c2913dd39881c153c15bbb2c73a0d348dbc949d288f98a57d9f398f6dd7f74ca", + "docs/runtime-protocol.md": "1ef452ca82c25ab8d1b4b97893a0449b467edccd69f86c4233343358a1ea8b04", + "docs/sandbox-provider.md": "cd2791affb8404391577a63accbc5bd3c536b251849c6bdf3b16344452c9b8ce", + "apps/docs/scripts/guides.json": "109374d4bf1a1d20d10ea85c9400ec6a2f9b62ca0fc6ee6030d3aab94a50d217" }, "outputs": { - "content/docs/index.mdx": "63318e9faa387a608efa11af7bd67917579df22a1e7fed00f907b4794112581a", - "content/docs/concepts.mdx": "cf4dc9e8838782bbc16539c8dec1ccb16680fe14cd1472eabceab6b79885f503", - "content/docs/execution-model.mdx": "bf528e34ace32ee1c256aaa4f2e31575bcbbd62d5c5efa7177ddc7b8e275becb", - "content/docs/install.mdx": "9cf6b03ae1c0c6caca768ea4c3ff2a47d7dfbfc3648cc7a73b7c848c7e257d7b", - "content/docs/install-options.mdx": "3bbfa77af2c34f2b9f203079778b51ee9425624ca516c6392c859ee2add43591", - "content/docs/configure.mdx": "a39be53e21c65159af311724d7746e35a3c0dfcc9b87d5e79d01e54e7bb6d3ed", - "content/docs/bootstrap-projects-keys.mdx": "2ac720c071a0b37210c671b2d6fa704bb5720c2acaadac2650b893580965d2d8", - "content/docs/quickstart.mdx": "bab3cadd3cee86b6dcc11768cb81deb9d37c89521dfcace0a33d387e8d553815", - "content/docs/public-api.mdx": "055cb7e06d7647ff24e669a993ad1cfb806b2dfca51e12716f06df8fdf7ecac5", - "content/docs/agents-and-tools.mdx": "9ffa5a5883847f2e4d3641bfd5975baf448fd371542797fd93204f69ea6df5e2", - "content/docs/sessions.mdx": "4f3a5e6e774f2ad687c1e00841eb716e1370c9206a8bd7a118325fa77594630c", - "content/docs/examples.mdx": "50dc70b46605d83df289cc555c064884645cd875cc46d0688782c1fac8752a0f", - "content/docs/environments-and-files.mdx": "2b8a0e0783032ac7e187361cab2225e9fed524f0ae9bacd3edf43804356cd389", - "content/docs/hosted-providers.mdx": "c504f42cea0c8c1ac6bd218a051b523cd8cd8609bd486233a787e2f4ff4f050c", - "content/docs/self-hosted-execution.mdx": "f190a4c40e532f3f24e8969c3df70f561b892ceb61b8b447eaf119312bffa071", - "content/docs/self-hosted-native.mdx": "e95c984383679bc1b2d016f864597eb625077e286228899c28eccbc5a89cc6b0", + "content/docs/index.mdx": "f98ce22ef71c0d5921f92b473c465cc27ddbe4616a7606e2138617b9aef4d55f", + "content/docs/concepts.mdx": "131d926a9efd2446517cb20c3c5d780e2bafda4817d93b1be3ce4a734c3530c5", + "content/docs/execution-model.mdx": "582e06b1c59fb6fb55da0849db753c6280585e636f68cbb34a812071cd11c4fb", + "content/docs/install.mdx": "1bd34e90289187eb072753c2f0a3161b7682916e36c90dc4d90400d6ed7ea221", + "content/docs/install-options.mdx": "8979ba5464db33310a3623d1e9bb8a33f74883e5ec7c4e7412354e62620f247d", + "content/docs/configure.mdx": "6ad08ae6de1e1d6376fbafa2c658338022cca202e36a0b31847167b196b607bd", + "content/docs/bootstrap-projects-keys.mdx": "174d0e61e72e51b08b558fb81fd413f89af58c423435d189d84ba6cb67976d34", + "content/docs/quickstart.mdx": "c92e0eba73e8725c44062be063e245704de7002e15f92ea0a4eb17498853c171", + "content/docs/public-api.mdx": "2136bb9a96ffe2bfde504df9833d1147c3d22548bbf510c42523cecea2f49e29", + "content/docs/agents-and-tools.mdx": "a3a6a669e25af073bf17cfb9e9699a6fd875dc31cd770fd28c12401078396ae4", + "content/docs/sessions.mdx": "c4abe10b9115174d10c10c73a373088460276f99f6c2cfe6df6b6aba8d6c7382", + "content/docs/environments-and-files.mdx": "72f9e2e1a276479480c4f5187df5e54840a888c03d35e6af29825a1f20bde454", + "content/docs/hosted-providers.mdx": "1076b39c59b0b63cc57a92eb738f4b5bd6b842787b16f51f9da0faa3e184ff9b", + "content/docs/self-hosted-execution.mdx": "dc94873d1b1bb5aa49380b095e92b988699f79c1ae497e0a28dab54aac4957c1", + "content/docs/self-hosted-native.mdx": "80185731954638a697652650112a8e2ebc348a6c8b42712a34f9755912774d14", "public/images/source/apps/web/public/onboarding/monitor-en.webp": "29dc220cb1250c7016b7c4bf7f30e9510b07c07b814a48c3aa9303b2d76f20b2", - "content/docs/console.mdx": "0a6a04c13a130a107f74c7c6cc518d24f6985780c94a6d0b9b0eb18384fb3253", - "content/docs/admin-api.mdx": "d553f7712b22e2eb46bf7c84c2b7a1d67df51996e1533aca2451d24c2caf7628", - "content/docs/observability.mdx": "595ab9f9918d04c058b64fa3a31d81913bbcbbc4d03138dca63a590979dde112", - "content/docs/troubleshooting.mdx": "ef71d3a2d93915c55741869c40c9b2c0cb870922b60732ce65ecc4c38f60cbf0", - "content/docs/user-guide.mdx": "5389281b1fa4743479d0d4f49baa631793187d2484e7e55984efc8a9f96d1b55", - "content/docs/development.mdx": "02136ed522b0545dc84fa210cf61d86058e85e1d3acd7890160bd1cce1a13a58", - "content/docs/harness-onboarding.mdx": "115c6fcdc640c9260cbdbcb7315630045d1643c86a1b37a4c33a61ecc8053a9a", - "content/docs/runtime-protocol.mdx": "59be1013b20c9236ec3480cdc29310e19bd08bbcf2d61d5bee45a9400b43c7be", - "content/docs/sandbox-provider.mdx": "3814c84245951b404a786176f7b958d73a7f4548edded55be02be22e143f9880" + "content/docs/console.mdx": "f190022b65fa2ca1ac6dc9cac8e743f63bcbfb6278e8c85b7773f2fc6ca62728", + "content/docs/admin-api.mdx": "f9711580e69545daae9f364d4d5e246b2a7cf364fb8d8d7691576c63cde0e7b3", + "content/docs/observability.mdx": "f3edbbd338133c6938bdf42dc3be1938673755bd23f364dd4fa8253b46f06aa8", + "content/docs/troubleshooting.mdx": "fe75946cb164e2be21fcb0e68bcecddb4688a8385d589ae2b689902d144b0c0b", + "content/docs/user-guide.mdx": "005b775680b170032fa7f94c6f406f4ceef1d184bb6e58096e38fb6c5be13d43", + "content/docs/development.mdx": "f425b8cb6cf690b048e15647662abc3dbad9443ec6998028fee0fa0d9cf0a3e1", + "content/docs/harness-onboarding.mdx": "1533cc137dc0d0c05149ef9b02c1dfa6b9e460a35006fc912585797528a9941f", + "content/docs/runtime-protocol.mdx": "5fa5d61df52aa20f38fb57f9114d25524d0a1a31a03b165916f12f8d2b3bef5f", + "content/docs/sandbox-provider.mdx": "d72357c8a7ccd00fe316eb51b4f55a50ef9c880921ec40e654d2457b089df3bc" } } From 625125895e8270270facab0c41498bf559876db7 Mon Sep 17 00:00:00 2001 From: saladday <1203511142@qq.com> Date: Tue, 29 Sep 2026 16:37:47 +0800 Subject: [PATCH 4/5] docs: keep product and execution separation in CONTRIBUTING The Parsar/Core product boundary is a repository-wide rule; restore it under Repository boundary instead of the service implementation notes, and reword two implementation lines to current names. --- CONTRIBUTING.md | 88 +++++++++++++++++++++++++ contracts/agents-api/README.md | 2 +- services/agents-api/IMPLEMENTATION.md | 94 +-------------------------- services/agents-api/README.md | 2 +- 4 files changed, 93 insertions(+), 93 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6ac7013ed..e3bff9c38 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -62,6 +62,94 @@ unchanged and do not require fetching the original repository. The source snapshot and per-file hashes are an audit trail; future Core development need not preserve them. Do not automatically sync or delete the original repository's Core. +### Product and execution service separation + +Agents API is the primary infrastructure deliverable. Parsar is an ordinary client +and example application; its feature backlog must not dictate the execution +service's public protocol or internal model. Agents API must build, deploy and run +without the Parsar product service, frontend or database. An optional Compose +deployment may install both services with one PostgreSQL instance, but separate +databases, credentials and migrations. The product uses Core exclusively; it has no native daemon or HTTP Agent fallback. + +#### Design and compatibility requirements + +- The complete pinned `openai/openai-python` `beta/agents` protocol is the target, + including its referenced resources and types. Match paths, methods, headers, + field presence, nullability, discriminators, defaults, status transitions, + pagination, errors and streaming behavior. Engine limitations are implementation + gaps to solve, not grounds for narrowing or redefining the upstream contract. +- Preserve qualified native capability differences across harnesses. If a material + difference from the official API has no clear mapping, pause that part and ask + the user before changing its semantics. Explicit unsupported enablement rejects; + ordinary requests retain native behavior with any official default discrepancy + recorded in the coverage ledger. In particular, native programmatic tool calling + is not currently qualified as the official default-on behavior. Do not build + a separate executor or model loop to fabricate parity. This does not relax + authentication, isolation, credential protection or data consistency. +- Pin upstream source and SDK versions in `contracts/agents-api/upstream.json`. + Use official SDKs for clients and reuse upstream types or schemas where suitable. + SDK deserialization alone is not server validation or proof of compatibility: + test raw HTTP payloads and observable workflows as well. Synthetic data and mock + model responses may support controlled tests; live execution acceptance must + call a real model API through the service, daemon and harness. A real daemon + with a synthetic model does not constitute live model validation. Keep provider + credentials in private test configuration, outside source, logs and task records. + Record unspecified or unverified behavior explicitly; never invent official + semantics. When current documentation adds operations or fields absent from the + fixed baseline, queue a protocol upgrade instead of silently implementing a new + version. Owned-resource live probes can qualify status codes and wire details + left unspecified by the SDK; retain request evidence and distinguish observations + from guaranteed or fully covered behavior. Track partial + coverage in `contracts/agents-api/README.md` until the complete target is verified. + Reconcile current coverage summaries with merged routes and recorded acceptance; + distinguish accepted profiles, partial implementation, missing operations and + unverified semantics. Retain historical evidence with its original scope. Handler + counts are not compatibility percentages, and an active provider probe is not + deployment qualification. +- No legacy Agents API compatibility requirement takes precedence over this + design. Replace an unsuitable implementation instead of growing compatibility + branches. Preserve reusable, verified infrastructure rather than rewriting it + merely for new names or directories. Replacements may retire obsolete private + interfaces and history backfills in bounded PRs; this does not authorize deleting + product data or changing unrelated product behavior. +- Every concrete harness interaction goes through the common Runtime contract + and its adapter. Extend that contract minimally when a current operation cannot + be expressed; never put native capability logic or transport conversion into + Core handlers, storage or scheduling. Qualify public workflows through the same + shared chain; direct native probes establish feasibility only. +- Keep engine-specific types, process management and protocol translation inside + execution adapters. The public API and persistence/application core must not + interpret Parsar product payloads or depend on one engine's native item types. + Prefer maintained upstream SDKs and native execution protocols over a second + hand-written model/tool loop or a general-purpose compatibility framework. +- Verify an independent official-client workflow before a Parsar integration. + Parsar uses the same public contract as any other client, with no privileged + endpoint or direct execution-table access. An OpenAI endpoint is a possible + client target only where the requested capabilities and credentials support it. + +- Parsar owns users, workspaces, business authorization, Agent/Team definitions, + capabilities, product conversations, IM/sharing, approval decisions and billing. +- Agents API owns protocol saved Agents, execution sessions/turns, effective + configuration snapshots, dispatch/cancel, environments, vaults, raw usage, + pending interactions, protocol subagents and durable events. Protocol saved + Agents/vaults are execution resources, not Parsar marketplace or business roles. + Neither service reads the other's tables. Parsar uses a versioned client contract. +- A product conversation may map to several execution sessions. An execution + session is distinct from a live daemon socket, process or sandbox. Native engine + session identifiers belong to the execution service. +- Establish single-Agent execution, approval, cancellation, idempotent submission, + persisted recovery queries before Team orchestration. The upstream SSE stream + is live-only; recover through Session/Turn/Items reads. Any additional product + cursor replay must be documented as an extension, not upstream semantics. + Team definitions, management and orchestration belong to Parsar. Agents API + establishes single-Agent execution first; business Team loops are deferred. + This does not exclude upstream `multi_agent` configuration or subagent resources + from protocol coverage. Future business Team orchestration directly depends on + `openai/openai-agents-python` in Parsar. +- Daemon Skill/SP authoring remains a product operation: forward through a scoped + product callback with the original requester and workspace checks. A runtime + credential alone must not grant business write permissions. + ### Optional application example `example/parsar/` is an optional, independently started Agent workbench. Its diff --git a/contracts/agents-api/README.md b/contracts/agents-api/README.md index dc771e4b1..9f7920569 100644 --- a/contracts/agents-api/README.md +++ b/contracts/agents-api/README.md @@ -22,7 +22,7 @@ Parsar owns product Agents and Teams. This service owns upstream execution resources, including reusable Agents and protocol subagents. The OpenAI Agents Python **SDK** is a separate future dependency for business Team orchestration in Parsar, not the HTTP contract. Design rules live in -[CONTRIBUTING.md](../../services/agents-api/IMPLEMENTATION.md#design-and-compatibility-requirements). +[CONTRIBUTING.md](../../CONTRIBUTING.md#design-and-compatibility-requirements). The [resource selector and error qualification](resource-selector-semantics.md) records nullable Skill references and source Files not-found parameter fields, diff --git a/services/agents-api/IMPLEMENTATION.md b/services/agents-api/IMPLEMENTATION.md index 485d09c97..c33998d35 100644 --- a/services/agents-api/IMPLEMENTATION.md +++ b/services/agents-api/IMPLEMENTATION.md @@ -82,94 +82,6 @@ streaming, content-download, live directory, Runtime observation and Runtime history routes register an explicit HEAD 405 instead. Every 405 of the API router, unknown methods included, has the JSON body and lists the route's methods in `Allow`. -## Product and execution service separation - -Agents API is the primary infrastructure deliverable. Parsar is an ordinary client -and example application; its feature backlog must not dictate the execution -service's public protocol or internal model. Agents API must build, deploy and run -without the Parsar product service, frontend or database. An optional Compose -deployment may install both services with one PostgreSQL instance, but separate -databases, credentials and migrations. The product uses Core exclusively; it has no native daemon or HTTP Agent fallback. - -### Design and compatibility requirements - -- The complete pinned `openai/openai-python` `beta/agents` protocol is the target, - including its referenced resources and types. Match paths, methods, headers, - field presence, nullability, discriminators, defaults, status transitions, - pagination, errors and streaming behavior. Engine limitations are implementation - gaps to solve, not grounds for narrowing or redefining the upstream contract. -- Preserve qualified native capability differences across harnesses. If a material - difference from the official API has no clear mapping, pause that part and ask - the user before changing its semantics. Explicit unsupported enablement rejects; - ordinary requests retain native behavior with any official default discrepancy - recorded in the coverage ledger. In particular, native programmatic tool calling - is not currently qualified as the official default-on behavior. Do not build - a separate executor or model loop to fabricate parity. This does not relax - authentication, isolation, credential protection or data consistency. -- Pin upstream source and SDK versions in `contracts/agents-api/upstream.json`. - Use official SDKs for clients and reuse upstream types or schemas where suitable. - SDK deserialization alone is not server validation or proof of compatibility: - test raw HTTP payloads and observable workflows as well. Synthetic data and mock - model responses may support controlled tests; live execution acceptance must - call a real model API through the service, daemon and harness. A real daemon - with a synthetic model does not constitute live model validation. Keep provider - credentials in private test configuration, outside source, logs and task records. - Record unspecified or unverified behavior explicitly; never invent official - semantics. When current documentation adds operations or fields absent from the - fixed baseline, queue a protocol upgrade instead of silently implementing a new - version. Owned-resource live probes can qualify status codes and wire details - left unspecified by the SDK; retain request evidence and distinguish observations - from guaranteed or fully covered behavior. Track partial - coverage in `contracts/agents-api/README.md` until the complete target is verified. - Reconcile current coverage summaries with merged routes and recorded acceptance; - distinguish accepted profiles, partial implementation, missing operations and - unverified semantics. Retain historical evidence with its original scope. Handler - counts are not compatibility percentages, and an active provider probe is not - deployment qualification. -- No legacy Agents API compatibility requirement takes precedence over this - design. Replace an unsuitable implementation instead of growing compatibility - branches. Preserve reusable, verified infrastructure rather than rewriting it - merely for new names or directories. Replacements may retire obsolete private - interfaces and history backfills in bounded PRs; this does not authorize deleting - product data or changing unrelated product behavior. -- Every concrete harness interaction goes through the common Runtime contract - and its adapter. Extend that contract minimally when a current operation cannot - be expressed; never put native capability logic or transport conversion into - Core handlers, storage or scheduling. Qualify public workflows through the same - shared chain; direct native probes establish feasibility only. -- Keep engine-specific types, process management and protocol translation inside - execution adapters. The public API and persistence/application core must not - interpret Parsar product payloads or depend on one engine's native item types. - Prefer maintained upstream SDKs and native execution protocols over a second - hand-written model/tool loop or a general-purpose compatibility framework. -- Verify an independent official-client workflow before a Parsar integration. - Parsar uses the same public contract as any other client, with no privileged - endpoint or direct execution-table access. An OpenAI endpoint is a possible - client target only where the requested capabilities and credentials support it. - -- Parsar owns users, workspaces, business authorization, Agent/Team definitions, - capabilities, product conversations, IM/sharing, approval decisions and billing. -- Agents API owns protocol saved Agents, execution sessions/turns, effective - configuration snapshots, dispatch/cancel, environments, vaults, raw usage, - pending interactions, protocol subagents and durable events. Protocol saved - Agents/vaults are execution resources, not Parsar marketplace or business roles. - Neither service reads the other's tables. Parsar uses a versioned client contract. -- A product conversation may map to several execution sessions. An execution - session is distinct from a live daemon socket, process or sandbox. Native engine - session identifiers belong to the execution service. -- Establish single-Agent execution, approval, cancellation, idempotent submission, - persisted recovery queries before Team orchestration. The upstream SSE stream - is live-only; recover through Session/Turn/Items reads. Any additional product - cursor replay must be documented as an extension, not upstream semantics. - Team definitions, management and orchestration belong to Parsar. Agents API - establishes single-Agent execution first; business Team loops are deferred. - This does not exclude upstream `multi_agent` configuration or subagent resources - from protocol coverage. Future business Team orchestration directly depends on - `openai/openai-agents-python` in Parsar. -- Daemon Skill/SP authoring remains a product operation: forward through a scoped - product callback with the original requester and workspace checks. A runtime - credential alone must not grant business write permissions. - ## Source Files and Artifacts Source Files belong to the execution project and have an independent lifecycle @@ -916,7 +828,7 @@ replaced; do not carry obsolete compatibility code forward to satisfy this secti unsupported non-default levels remain an explicit implementation gap. Product requests that omit the native option retain their existing defaults. Structured output has a separately qualified profile described in - [Structured output execution](../../CONTRIBUTING.md#structured-output-execution). + [Structured output execution](../../contracts/agents-api/structured-output.md#core-and-runtime-boundary). - `subagent_control` advertises native subagent tool control. Agents API requires it when resolved `multi_agent.enabled` is false and sends the typed internal `disable_subagents` policy on both new and resumed Turns. Native translation @@ -956,7 +868,7 @@ replaced; do not carry obsolete compatibility code forward to satisfy this secti application receipts and conflict detection; a receipt confirms the native reply was written, not that an external side effect succeeded. Pending calls end with their Run; the execution service owns persistence and recovery, while - Parsar retains business approval and credential-owner authorization. Do not + the product client retains business approval and credential-owner authorization. Do not map native approval requests to invented official protocol resources. - Function-call storage is scoped by authenticated tenant, Session and Turn, with immutable public/executor call identities and arguments. Result admission and @@ -1404,7 +1316,7 @@ lifecycle gate waiting for the receipt. Lost peers, expiry and restart retain th ordinary failure/cleanup fallback, never a fabricated cancelled outcome. Preserve unknown Create ownership and reject schema downgrade with unsettled markers. -Core rejects `AGENTS_API_MANAGED_RUNTIMES_FILE`; there is no file-managed startup +Core rejects the retired managed-runtimes file setting; there is no file-managed startup path or embedded local node. An older file-managed database is not automatically adopted after its environment variable is removed. Settle and drain that deployment with its previous release and original backend, preserving business data, private diff --git a/services/agents-api/README.md b/services/agents-api/README.md index 6845dd610..6e190d07e 100644 --- a/services/agents-api/README.md +++ b/services/agents-api/README.md @@ -336,7 +336,7 @@ connections alone do not start a Turn. Submit text, cancellation or function res through the official Session events endpoint; the worker assigns a same-tenant host and preserves that binding. Managed Docker has three-harness evidence. This generic device provisioning path is for `none`; self-hosted Sessions require the dedicated enrollment below. -User-managed enrollment has a separate [qualification record](../../contracts/agents-api/user-managed-runtime-v1.md); complete protocol semantics remain partial. See the [ownership rules](IMPLEMENTATION.md#product-and-execution-service-separation). +User-managed enrollment has a separate [qualification record](../../contracts/agents-api/user-managed-runtime-v1.md); complete protocol semantics remain partial. See the [ownership rules](../../CONTRIBUTING.md#product-and-execution-service-separation). ### Enable Claude SDK execution From 63b02789b7b00416ca090dbdad0e30b7f33009a5 Mon Sep 17 00:00:00 2001 From: saladday <1203511142@qq.com> Date: Tue, 29 Sep 2026 19:44:58 +0800 Subject: [PATCH 5/5] docs: drop the name-guard entry for the deleted contract and regenerate the site --- apps/docs/content/docs/development.mdx | 42 ++++----- .../content/docs/environments-and-files.mdx | 30 +++---- apps/docs/content/docs/harness-onboarding.mdx | 79 +++++++++++----- apps/docs/content/docs/runtime-protocol.mdx | 2 +- apps/docs/content/docs/sandbox-provider.mdx | 44 ++++----- apps/docs/content/guide-sources.json | 90 ++++++++++--------- scripts/name-allowlist.json | 5 -- 7 files changed, 162 insertions(+), 130 deletions(-) diff --git a/apps/docs/content/docs/development.mdx b/apps/docs/content/docs/development.mdx index 31fc89826..b32ff9936 100644 --- a/apps/docs/content/docs/development.mdx +++ b/apps/docs/content/docs/development.mdx @@ -6,7 +6,7 @@ description: "Set up a checkout and choose the right extension boundary." This guide is for contributors changing Core, Runtime, adapters, the Web console or the documentation site. For using a deployment, start with the [getting started guide](/). Read the -[contributor rules](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/CONTRIBUTING.md) before changing code. +[contributor rules](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/CONTRIBUTING.md) before changing code. ## Set up a checkout @@ -18,8 +18,8 @@ git worktree add ../openagentcore-change -b codex/my-change main cd ../openagentcore-change ``` -Install Go at the version in [go.mod](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/go.mod), Node 22, pnpm at the version -in [package.json](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/package.json), and Python 3.9 or newer. The complete gate +Install Go at the version in [go.mod](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/go.mod), Node 22, pnpm at the version +in [package.json](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/package.json), and Python 3.9 or newer. The complete gate runs on Linux and needs a dedicated PostgreSQL database, OpenSSL development libraries for the microsandbox helper, and a Playwright browser. Provider and Runtime builds have additional prerequisites in their component guides. @@ -45,7 +45,7 @@ export OAC_TEST_OFFICIAL_SDK_PYTHON="$PWD/.venv/bin/python" Set `OAC_TEST_DATABASE_URL` privately to a dedicated PostgreSQL test database. Never point the test suite at an installation or product database. The -[test database rules](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/CONTRIBUTING.md#test-database) list the required role +[test database rules](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/CONTRIBUTING.md#test-database) list the required role permission. Third-party native packages are pinned build inputs; changing a pin requires the @@ -65,17 +65,17 @@ under `~/.oac/build/daemon/` by default. `OAC_DEV_HOME` selects another build ro `OAC_DEV_CORE_BUILD_DIR` selects an absolute Core output directory. Building does not configure a database, start a deployment or qualify native execution. -Use the [service guide](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/README.md#build-standalone-binaries) +Use the [service guide](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/README.md#build-standalone-binaries) to run the Core migrator and server with a separate development database. The [configuration appendix](/configure#appendix-core-environment-without-the-installer) owns standalone process settings. For a complete operator installation, use the [installation guide](/install); building Core alone is a separate contributor workflow. -The [Web package guide](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/web/README.md) describes console development and +The [Web package guide](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/web/README.md) describes console development and its management-only integration. `pnpm dev:web` starts the frontend development server; it does not install Core, issue keys or start native execution. The -[docs app guide](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/docs/README.md) describes the independent documentation +[docs app guide](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/docs/README.md) describes the independent documentation site; `pnpm dev:docs` starts its development server. ## Repository map @@ -83,7 +83,7 @@ site; `pnpm dev:docs` starts its development server. | Location | Responsibility | Read next | | --- | --- | --- | | `services/agents-api/internal/api` | Public, administrator and machine HTTP boundaries | [API index](/public-api) | -| `services/agents-api/internal/store` and `internal/db` | Core persistence, transactions, queries and migrations | [Service guide](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/README.md#database-ownership) | +| `services/agents-api/internal/store` and `internal/db` | Core persistence, transactions, queries and migrations | [Service guide](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/README.md#database-ownership) | | `services/agents-api/internal/execution` | Durable Turn dispatch and scheduling | [Runtime protocol](/runtime-protocol) | | `services/agents-api/internal/engine` | Pure qualification of harness operations and placements | [Harness onboarding](/harness-onboarding) | | `internal/agentdaemon/proto` and `gateway` | Shared wire types, validators and authenticated Runtime connections | [Runtime protocol](/runtime-protocol) | @@ -91,10 +91,10 @@ site; `pnpm dev:docs` starts its development server. | `apps/parsar-daemon/internal/agent` | Native harness adapters | [Native references](/harness-onboarding#native-references) | | `services/agents-api/internal/sandbox` | Provider interfaces and managed compute lifecycle | [Provider onboarding](/sandbox-provider) | | `services/core-console` | Console login and the server-side management proxy | [Web architecture](/execution-model) | -| `apps/web` and `packages/agents-client` | Console UI and typed clients | [Web guide](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/web/README.md) | -| `deploy/install` and `scripts` | Distribution, installation and validation tools | [Maintainers](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/docs/maintainers.md) | -| `contracts/agents-api` | Pinned schema, local semantic contracts and qualification evidence | [Coverage ledger](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/README.md) | -| `apps/docs` | Generated guide and API-reference website | [Generation workflow](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/docs/README.md) | +| `apps/web` and `packages/agents-client` | Console UI and typed clients | [Web guide](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/web/README.md) | +| `deploy/install` and `scripts` | Distribution, installation and validation tools | [Maintainers](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/maintainers.md) | +| `contracts/agents-api` | Pinned schema, local semantic contracts and qualification evidence | [Coverage ledger](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/README.md) | +| `apps/docs` | Generated guide and API-reference website | [Generation workflow](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/docs/README.md) | Core owns durable execution facts. Runtime owns local execution and cleanup. Adapters translate native operations. Providers own outer compute. These boundaries @@ -111,21 +111,21 @@ only helps you pick the right one. | Harness adapter | A native agent engine behind the Runtime | [Harness onboarding](/harness-onboarding) | | Sandbox Provider | Outer compute that creates and reclaims Environments | [Sandbox Provider guide](/sandbox-provider) | | Core–Runtime protocol | A message, receipt or lifecycle rule between Core and the daemon | [Core–Runtime protocol](/runtime-protocol) | -| Public API operation | A `/v1`, `/core/v1` or `/api/v1` route | [API index](/public-api) and [contracts](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/README.md) | +| Public API operation | A `/v1`, `/core/v1` or `/api/v1` route | [API index](/public-api) and [contracts](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/README.md) | | Environment capability | Skills, Plugins, MCP or `packages.system` preparation | [Environments](/environments-and-files#runtime-capability-preparation) | ### Add a Harness adapter Implement the shared `ExecutorFactory`, `Executor` and `Turn` interfaces in -[`agent/harness.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/parsar-daemon/internal/agent/harness.go), register +[`agent/harness.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/internal/agent/harness.go), register the adapter and add a service profile. Follow the numbered steps in [Harness onboarding](/harness-onboarding); qualification -evidence belongs in [Harness integration](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/harnesses.md). +evidence belongs in [Harness integration](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/harnesses.md). ### Add a Sandbox Provider Implement the five required `SandboxProvider` operations in -[`sandbox_provider.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/sandbox/sandbox_provider.go), +[`sandbox_provider.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/sandbox/sandbox_provider.go), register the provider kind and pass `make check-sandbox-provider-contract`. Follow the numbered steps in the [Sandbox Provider guide](/sandbox-provider). @@ -139,7 +139,7 @@ ownership; `make check-runtime-contract` is its focused gate. ## Validate a change Run checks for the affected boundary while developing. The repository -[required checks](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/CONTRIBUTING.md#required-checks) define completion, including +[required checks](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/CONTRIBUTING.md#required-checks) define completion, including `make check` and any changed native component's real acceptance. | Change | Focused validation | @@ -165,13 +165,13 @@ with `AGENTS_FIXTURE_PORT` and `AGENTS_WEB_PORT` when running parallel validatio Keep databases, ports and containers separate between validation workers. Compilation, fixture success and live model/provider acceptance establish different facts; report skipped or unavailable checks explicitly. Follow the -[independent blind review workflow](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/CONTRIBUTING.md#review) +[independent blind review workflow](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/CONTRIBUTING.md#review) after validation. ## Change documentation Edit the source that owns the subject, using the -[documentation ownership map](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/CONTRIBUTING.md#documentation-ownership). +[documentation ownership map](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/CONTRIBUTING.md#documentation-ownership). User guides explain a workflow and link to detailed contracts. Contributor rules explain ownership and required checks. Semantic contracts document exact behavior, implementation gaps and evidence. Avoid copying the same rule into all three. @@ -179,9 +179,9 @@ implementation gaps and evidence. Avoid copying the same rule into all three. Update authored sources before regenerating the docs site. Adding a site page also requires a source entry and navigation entry; source/output hashes verify freshness. API references render the namespace-specific generated schemas. Follow -[the docs app workflow](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/docs/README.md) for link, type, build and browser +[the docs app workflow](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/docs/README.md) for link, type, build and browser checks. The release bundle has a separate explicit documentation list in `scripts/core-distribution-manifest.py`; changing a bundled path or heading must also pass its relative-link and anchor checks. -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/docs/development.md) +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/development.md) diff --git a/apps/docs/content/docs/environments-and-files.mdx b/apps/docs/content/docs/environments-and-files.mdx index 9fa4f6102..8036cf906 100644 --- a/apps/docs/content/docs/environments-and-files.mdx +++ b/apps/docs/content/docs/environments-and-files.mdx @@ -3,19 +3,19 @@ title: "Environments and files" description: "Execution placement, Environment resources and workspace ownership." --- -This assessment covers the fixed [Python SDK contract](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/upstream.json), with partial +This assessment covers the fixed [Python SDK contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/upstream.json), with partial coverage. Core-managed Docker runs the three qualified native harnesses. V1 user-managed `self_hosted` enrollment uses the same colocated Runtime for Codex, Claude SDK and MiniMax. `/workspace` names the packaged workspace; a self-hosted Session may instead select its exact canonical physical directory. The -[recorded deployment qualification](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/user-managed-runtime-v1.md) retains its historical +[recorded deployment qualification](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/user-managed-runtime-v1.md) retains its historical source, paths and tested capability scope. For hosted compute, the deployment selects E2B, Docker or microsandbox through -[Hosted Sandbox Manager](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/HOSTED-SANDBOX-MANAGER.md). +[Hosted Sandbox Manager](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/HOSTED-SANDBOX-MANAGER.md). For the separate caller-managed E2B path, the user owns allocation, renewal and -cleanup through the official SDK and [Runtime packaging](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/deploy/e2b/README.md). -[Templates](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/environment-templates.md) remain a hosted-only resource path; -[Files](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/environment-files.md) reuse the exact authorized local workspace. +cleanup through the official SDK and [Runtime packaging](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/deploy/e2b/README.md). +[Templates](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/environment-templates.md) remain a hosted-only resource path; +[Files](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/environment-files.md) reuse the exact authorized local workspace. The internal Store now owns a durable Environment association for newly created `self_hosted` and `openai_hosted` snapshots, atomically with Session creation. @@ -33,7 +33,7 @@ readiness. Rotation/revocation, Session deletion and ownership loss deny further access without promising immediate cessation of native effects. Native history remains local to the bound Runtime and cannot be replaced on retry. There is no registry/Noise relay or transient service-side harness credential. -See the [enrollment guide](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/README.md#user-managed-runtime-enrollment). +See the [enrollment guide](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/README.md#user-managed-runtime-enrollment). ## Basic public Docker-hosted profile @@ -41,8 +41,8 @@ An explicitly configured default managed Docker provider enables `type=openai_ho for the qualified Codex, Claude Code and MiniMax Code profiles. Each uses the same Runtime lifecycle and workspace interfaces with its native adapter. The outer Environment provides managed isolation; the daemon adds no inner sandbox. -See the [engine profile guides](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/README.md#public-engine-profiles) for setup and limits. -The standalone [operator configuration](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/deploy/codex/README.md#standalone-operator-configuration) +See the [engine profile guides](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/README.md#public-engine-profiles) for setup and limits. +The standalone [operator configuration](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/deploy/codex/README.md#standalone-operator-configuration) selects the qualified immutable Runtime image; advertised capabilities alone do not enable admission. An idle or initial-text creation commits Session, Environment and retry identity before the existing leased Worker provisions its allocation. @@ -63,7 +63,7 @@ rejects explicitly, including null and empty lists, and never invokes apt, sudo or elevated execution. Package responses include the official required `system: []` field as empty metadata; it does not enable installation. Unsupported hostname forms and installation combinations reject explicitly; see -the [Template coverage and limits](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/environment-templates.md). Empty/null installation +the [Template coverage and limits](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/environment-templates.md). Empty/null installation defaults produce safe empty metadata, not a live workspace inventory. Service-origin hosted MCP remains unsupported; Environment Plugin MCP has its own qualified transport matrix. @@ -83,11 +83,11 @@ semantics; this profile does not establish complete Environment compatibility. The application creates, renews and destroys its E2B sandbox through the official SDK. It deploys the shared Runtime, then enrolls that Runtime into a `self_hosted` Session. Core neither keeps an E2B allocation nor issues Provider renew/kill calls. -The [E2B guide](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/deploy/e2b/README.md) owns packaging and +The [E2B guide](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/deploy/e2b/README.md) owns packaging and user-side lifecycle instructions. Expiry or lost workspace/history must not trigger -transparent replacement or replay. The [new enrollment qualification](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/user-managed-runtime-v1.md) +transparent replacement or replay. The [new enrollment qualification](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/user-managed-runtime-v1.md) records its own real deployment evidence. -The [prior E2B qualification](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/README.md#e2b-v1-qualification) records a historical +The [prior E2B qualification](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/README.md#e2b-v1-qualification) records a historical Core-managed route; it does not establish this caller-managed enrollment result. Current deployment-managed E2B is a separate hosted configuration in the manager guide. @@ -189,7 +189,7 @@ compute replacement; never silently move a bound Session or replay unknown work. Self-hosted compute/files remain caller-owned, with explicit cleanup separate from Session deletion. The full Environment implementation remains pending; follow the [pinned contract and acceptance sequence](/environments-and-files) -and the [two-engine placement prerequisites](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/workspace-placement.md). +and the [two-engine placement prerequisites](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/workspace-placement.md). For co-location, qualify the outer deployment boundary and the shared Runtime lifecycle. Native tools use the starting account's permissions; do not claim a daemon or harness sandbox. Host operators choose their own outer isolation. @@ -731,4 +731,4 @@ executor interoperability requirement. The superseded execution route is removed retain reusable filesystem helpers, necessary regressions and historical evidence without a compatibility layer. -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/environments.md) +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/environments.md) diff --git a/apps/docs/content/docs/harness-onboarding.mdx b/apps/docs/content/docs/harness-onboarding.mdx index 62d3c5c18..18e16d585 100644 --- a/apps/docs/content/docs/harness-onboarding.mdx +++ b/apps/docs/content/docs/harness-onboarding.mdx @@ -7,12 +7,12 @@ A **Harness** is a native agent engine (Codex, Claude Code, MiniMax Code) that runs the model/tool loop. A **Harness adapter** translates the common Core–Runtime execution contract into that engine's SDK or protocol. This guide is the single numbered path for adding one. Qualification evidence and the -per-operation support table live in [Harness integration](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/harnesses.md). +per-operation support table live in [Harness integration](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/harnesses.md). The code entry point is -[`agent/harness.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/parsar-daemon/internal/agent/harness.go). It +[`agent/harness.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/internal/agent/harness.go). It declares the required lifecycle, the separate optional interfaces and the -registration methods. The pinned public Agents API in [upstream.json](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/upstream.json) +registration methods. The pinned public Agents API in [upstream.json](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/upstream.json) is separate from this internal adapter contract. ## Ownership @@ -72,8 +72,8 @@ model communication configuration, not Turn scheduling or native process ownersh 6. **Enable and select the engine** through [engine selection](#engine-selection). 7. **Qualify it.** Run the synthetic integration test, then the real acceptance in - [Harness integration](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/harnesses.md#acceptance-checklist). Record results in - the [qualification table](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/harnesses.md#current-qualified-operations). + [Harness integration](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/harnesses.md#acceptance-checklist). Record results in + the [qualification table](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/harnesses.md#current-qualified-operations). Start with the mandatory text lifecycle, then qualify optional operations one at a time. Do not copy an adapter's native limitations into the shared Core protocol. @@ -98,9 +98,44 @@ a time. Do not copy an adapter's native limitations into the shared Core protoco Optional native differences are separate capability work, not onboarding blockers. - Never equate accepted parameters with applied native behavior. +## Native model configuration + +[`agent/harness.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/internal/agent/harness.go) owns the +preparation, validation, reserved-field, effective-connection and cleanup contract. +The shared wire object is `proto.HarnessConfig`. Adapter declarations in +`internal/harnessconfig/` own these native fields: + +| Harness | Accepted native fields | Application | +| --- | --- | --- | +| Codex | `model_reasoning_effort`: `none`, `minimal`, `low`, `medium`, `high`, `xhigh` | Native app-server `-c model_reasoning_effort=...` and each Turn's `collaborationMode.settings.reasoning_effort` | +| Claude SDK | `effort`: `low`, `medium`, `high`, `xhigh`, `max`; `thinking`: the SDK's `adaptive`, `enabled` or `disabled` object | SDK `Options.effort` and `Options.thinking` | +| MiniMax Code | Empty object only | Existing provider token limits remain required; additional native generation parameters are not qualified | + +Claude `thinking` accepts `display` (`summarized` or `omitted`) for adaptive or +enabled thinking, and an optional positive integer `budgetTokens` only for +`enabled`. The deprecated `maxThinkingTokens` alternative is rejected. +These are native settings, not a shared reasoning vocabulary; model availability +and provider support remain the selected harness's responsibility. + +Native protocol and parameter declarations also feed Core administration's small +configuration-support descriptor. `modeltransport` selects and validates routes +from those declarations. Its conversion qualification is distinct from adapter +acceptance and remote-model support. Follow the route and per-input requirements +in `agent/harness.go`; do not add a second model capability registry or infer +capabilities from model names. Nonempty `harness_config` currently requires a native +protocol route because the translator has no loss report for those parameters. + +Every Executor factory must honor the configuration preparation contract in +`agent/harness.go`. Shared wire types live in `internal/agentdaemon/proto`. The +standalone Core build includes the shared `internal/modeltransport` route +qualification code alongside the adapter declarations; Runtime still owns proxy +startup and request forwarding. Public configuration sources and inheritance belong +to [model execution](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/model-execution.md#native-model-parameters). + + ## Required adapter interfaces -[`agent/harness.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/parsar-daemon/internal/agent/harness.go) is the +[`agent/harness.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/internal/agent/harness.go) is the canonical interface entry point. Its required lifecycle is `ExecutorFactory`, `Executor`, `Turn` and `TurnSettlement`. Optional Turn and workspace interfaces remain separate; their result types and error values stay in the corresponding @@ -151,7 +186,7 @@ keeps its interaction routing and retry ownership. ## Events, inputs and optional capabilities -Use [`internal/agentdaemon/proto`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/internal/agentdaemon/proto) for neutral +Use [`internal/agentdaemon/proto`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/internal/agentdaemon/proto) for neutral requests, events and receipts. Each Turn emits only its own events with its Run ID, in order, and one terminal outcome. Native IDs and usage must be observed rather than invented. Capture the originating Turn before asynchronous native callbacks; @@ -182,8 +217,8 @@ lifecycle. Registration is static and requires a build; dynamic plugins are outside this contract. The methods live in `agent/harness.go`, and built-in adapters call them -from [`cli/agent_registration.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/parsar-daemon/internal/cli/agent_registration.go) -(Codex, MiniMax Code) and [`cli/claude_sdk.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/parsar-daemon/internal/cli/claude_sdk.go) +from [`cli/agent_registration.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/internal/cli/agent_registration.go) +(Codex, MiniMax Code) and [`cli/claude_sdk.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/internal/cli/claude_sdk.go) (Claude Code). | Order | Method | Registers | @@ -198,7 +233,7 @@ for that installation. Runtime registration does not grant Core qualification; that belongs to the service profile. The runnable test-only example is -[`testdata/onboarding/main.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/parsar-daemon/testdata/onboarding/main.go). +[`testdata/onboarding/main.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/testdata/onboarding/main.go). It registers a text-only synthetic Harness, demonstrates a Session-owned Executor, fresh Turns, durable steering, cancellation and history binding, and is never shipped as a real engine. @@ -209,10 +244,10 @@ Core recognizes engine identifiers explicitly. Add the new identifier to: | File | Purpose | | --- | --- | -| [`internal/engine/profile.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/engine/profile.go) | The static service profile catalog | -| [`contracts/agents-api/v1/core_extension.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/v1/core_extension.go) | Accepted `x_agents_core.harness` values | -| [`internal/api/saved_core_input.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/api/saved_core_input.go) | Saved Agent extension validation | -| [`internal/harnessconfig/builtin/registry.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/internal/harnessconfig/builtin/registry.go) | Built-in Harness configuration | +| [`internal/engine/profile.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/engine/profile.go) | The static service profile catalog | +| [`contracts/agents-api/v1/core_extension.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/v1/core_extension.go) | Accepted `x_agents_core.harness` values | +| [`internal/api/saved_core_input.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/api/saved_core_input.go) | Saved Agent extension validation | +| [`internal/harnessconfig/builtin/registry.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/internal/harnessconfig/builtin/registry.go) | Built-in Harness configuration | The profile is pure: it declares supported placements, public configuration and result limits and required Runtime controls, using existing public/protocol @@ -237,7 +272,7 @@ mutable global registration or compatibility fallback. | `agent.x_agents_core.harness` | Saved Agent or inline Session Agent | Explicitly selects an enabled engine; never falls back to another | Existing Sessions keep their engine. Do not add another selector; the -[harness selection extension](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/harness-selection.md) owns the field's semantics. +[harness selection extension](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/harness-selection.md) owns the field's semantics. ## Required versus optional operations @@ -255,14 +290,14 @@ explicitly and record the gap. Never advertise a capability to bypass selection. Structured-output adapters consume `ExecutionControls.OutputFormat` and publish confirmed native output through the existing Message contract. Register public qualification separately from the Runtime capability; see the -[structured-output boundary](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/structured-output.md). No Core engine-name branch is required. +[structured-output boundary](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/structured-output.md). No Core engine-name branch is required. Initial requests, `Executor.StartTurn` and steering consume the same ordered `proto.MessageInput`. Text-only adapters use `TextOnly()` to reject images without discarding content. Image adapters translate each part natively and acknowledge an active batch only after all its messages are applied. Register `MessageImages` and qualify `MessageImagePlacements` separately; see the -[message-input contract and real acceptance](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/message-input.md). +[message-input contract and real acceptance](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/message-input.md). Hosted workspace execution additionally requires verified preparation, workspace reads/output export, network behavior and credential/history isolation. Reuse the @@ -273,7 +308,7 @@ only after its required security and lifecycle behavior is demonstrated. ## Optional Subagent observations A harness that supports the Subagent resource reads implements the existing -[neutral observation contract](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/subagents.md#common-adapter-contract). It reports +[neutral observation contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/subagents.md#common-adapter-contract). It reports verified child identity, lifecycle effects and owned Turn/Item history through the authenticated Run, then qualifies those facts with real execution. It does not add routes, storage branches or a harness-specific Core scheduler. Report @@ -327,8 +362,8 @@ Use these adapters as implementation references after choosing a native API: | Harness | Adapter | Native transport | Runtime guide | | --- | --- | --- | --- | -| Codex | [`agent/codex`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/parsar-daemon/internal/agent/codex/executor.go) | app-server | [Codex Runtime](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/deploy/codex/README.md) | -| Claude Code | [`agent/claudesdk`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/parsar-daemon/internal/agent/claudesdk/executor.go) | [TypeScript SDK bridge](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/packages/claude-sdk-adapter/README.md) | [Claude Runtime](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/deploy/claude/README.md) | -| MiniMax Code | [`agent/mcode`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/parsar-daemon/internal/agent/mcode) | ACP and native workspace companion | [MiniMax Code Runtime](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/deploy/mcode/README.md) | +| Codex | [`agent/codex`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/internal/agent/codex/executor.go) | app-server | [Codex Runtime](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/deploy/codex/README.md) | +| Claude Code | [`agent/claudesdk`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/internal/agent/claudesdk/executor.go) | [TypeScript SDK bridge](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/packages/claude-sdk-adapter/README.md) | [Claude Runtime](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/deploy/claude/README.md) | +| MiniMax Code | [`agent/mcode`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/internal/agent/mcode) | ACP and native workspace companion | [MiniMax Code Runtime](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/deploy/mcode/README.md) | -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/harness-onboarding.md) +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/harness-onboarding.md) diff --git a/apps/docs/content/docs/runtime-protocol.mdx b/apps/docs/content/docs/runtime-protocol.mdx index 62c9b3bd2..344ca0d5b 100644 --- a/apps/docs/content/docs/runtime-protocol.mdx +++ b/apps/docs/content/docs/runtime-protocol.mdx @@ -246,7 +246,7 @@ Core-managed Docker `openai_hosted`. Self-hosted/user-managed images, MiniMax images and remote URLs remain explicit implementation gaps. Hosted images reuse the existing preparation, active-input and workspace authority; they do not add a downloader, a mount or a separate execution lifecycle. Core -does not fetch or transform media. See [message input coverage](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/message-input.md). +does not fetch or transform media. See [message input coverage](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/message-input.md). ## Preparation and execution order diff --git a/apps/docs/content/docs/sandbox-provider.mdx b/apps/docs/content/docs/sandbox-provider.mdx index b7810177c..3ae6debd6 100644 --- a/apps/docs/content/docs/sandbox-provider.mdx +++ b/apps/docs/content/docs/sandbox-provider.mdx @@ -6,7 +6,7 @@ description: "Environment allocation, bootstrap and reclamation." A **Sandbox Provider** supplies the outer compute (an *Environment*) that a Core Runtime daemon runs in, plus the bounded bootstrap that starts it. This guide is the single start-to-finish path for adding one. The canonical interface is -[`SandboxProvider`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/sandbox/sandbox_provider.go). +[`SandboxProvider`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/sandbox/sandbox_provider.go). ## Before you start @@ -15,7 +15,7 @@ the single start-to-finish path for adding one. The canonical interface is | Environment | Durable execution place owned by Core; see [Environments](/environments-and-files) | | Allocation | One Core-owned compute lease for an Environment, identified by `Reference` | | Runtime | The daemon inside the Environment; it prepares capabilities and executes Turns | -| Deployment | The single deployment-wide provider selection; see [Sandbox deployment](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/sandbox-deployment.md) | +| Deployment | The single deployment-wide provider selection; see [Sandbox deployment](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/sandbox-deployment.md) | Core owns durable Environment, allocation, placement and cleanup state. The Provider supplies compute and bootstrap only. Runtime prepares capabilities and @@ -49,7 +49,7 @@ connectivity; see [Runtime and outer isolation](/concepts#runtime-and-outer-isol 5. **Run the contract suite** with `make check-sandbox-provider-contract`. See [Validate the integration](#validate-the-integration). 6. **Run native acceptance** against real compute, then `make check`. Record - evidence and limits in [Sandbox deployment](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/sandbox-deployment.md). + evidence and limits in [Sandbox deployment](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/sandbox-deployment.md). 7. **Document operator setup** next to the adapter, like the [reference adapters](#reference-adapters). @@ -82,7 +82,7 @@ Do not implement an optional interface with successful no-op methods. Core uses interface assertions to select optional operations. Advertised support requires contract and native acceptance evidence; a healthy node or an available CLI does not establish it. Observation never renews a lease, starts compute or prepares a -Harness. See the [observation contract](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/runtime-observability.md). +Harness. See the [observation contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/runtime-observability.md). `RunCommand` is an existing administrative bootstrap/diagnostic facility, not an alternate route for Skills, Plugins, MCP setup, initial files, Session execution or @@ -151,7 +151,7 @@ Persist operation IDs and provider-returned snapshot provenance unchanged. another capture or restore. `ResumeCompute` only thaws the retained source; it must not cold-start a stopped one. Cleanup targets the exact compute incarnation and snapshot, not whichever instance currently has the same display name. See -[the lifecycle implementation](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/execution/runtime_compute.go) +[the lifecycle implementation](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/execution/runtime_compute.go) and its failure tests before advertising this capability. ### Four distinct readiness facts @@ -176,16 +176,16 @@ below; none accepts an arbitrary provider string. | Surface | File | What it enumerates | | --- | --- | --- | -| Node-local construction | [`sandbox/config/config.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/sandbox/config/config.go), [`specification.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/sandbox/config/specification.go) | Typed construction, readiness probe and node specification | -| Direct construction | [`cmd/server/managed_setup.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/cmd/server/managed_setup.go) (`managedSetup.provider`) | Direct adapters (E2B) and node-proxied kinds (Docker, microsandbox) | -| Resource validation | [`sandbox/deployment.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/sandbox/deployment.go) (`Resources.Validate`) | Per-kind resource limits | -| Node proxy and identity | [`sandbox/node/proxy.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/sandbox/node/proxy.go), [`node/identity.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/sandbox/node/identity.go) | Node-capable kinds and their checkpoint capability | -| Deployment persistence | [`sandbox_deployment_mutations.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/store/sandbox_deployment_mutations.go), [`sandbox_deployment_setup.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/store/sandbox_deployment_setup.go), [`sandbox_specification.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/store/sandbox_specification.go), [`runtime_node_deployment.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/store/runtime_node_deployment.go) | Deployment admission and node-mode kinds | -| Database constraint | A new migration in [`migrations/`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/migrations) | `provider_kind` `CHECK` constraints, last set in `000081_sandbox_generations.sql`; never edit a landed migration | -| Node installation | [`node_installation.go`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/core-console/node_installation.go) | Node artifacts per kind | -| Installer | [`deploy/install/sandbox_setup.py`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/deploy/install/sandbox_setup.py) (`CHOICES`, used by `install.py --sandbox`) | Installer backend choices | -| Distribution | [`scripts/core-distribution-manifest.py`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/scripts/core-distribution-manifest.py) | Bundled provider helper binaries | -| Clients and Web | [`sandbox-client.ts`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/packages/agents-client/src/sandbox-client.ts) (`SandboxProvider`), [`features/sandbox`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/web/src/features/sandbox) (`SandboxSetupWizard.tsx`, `console-config.ts`, `node-enrollment.ts`), [`sandbox-labels.ts`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/apps/web/src/lib/sandbox-labels.ts) | Backend choices and labels shown to operators | +| Node-local construction | [`sandbox/config/config.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/sandbox/config/config.go), [`specification.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/sandbox/config/specification.go) | Typed construction, readiness probe and node specification | +| Direct construction | [`cmd/server/managed_setup.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/cmd/server/managed_setup.go) (`managedSetup.provider`) | Direct adapters (E2B) and node-proxied kinds (Docker, microsandbox) | +| Resource validation | [`sandbox/deployment.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/sandbox/deployment.go) (`Resources.Validate`) | Per-kind resource limits | +| Node proxy and identity | [`sandbox/node/proxy.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/sandbox/node/proxy.go), [`node/identity.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/sandbox/node/identity.go) | Node-capable kinds and their checkpoint capability | +| Deployment persistence | [`sandbox_deployment_mutations.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/store/sandbox_deployment_mutations.go), [`sandbox_deployment_setup.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/store/sandbox_deployment_setup.go), [`sandbox_specification.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/store/sandbox_specification.go), [`runtime_node_deployment.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/store/runtime_node_deployment.go) | Deployment admission and node-mode kinds | +| Database constraint | A new migration in [`migrations/`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/migrations) | `provider_kind` `CHECK` constraints, last set in `000081_sandbox_generations.sql`; never edit a landed migration | +| Node installation | [`node_installation.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/core-console/node_installation.go) | Node artifacts per kind | +| Installer | [`deploy/install/sandbox_setup.py`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/deploy/install/sandbox_setup.py) (`CHOICES`, used by `install.py --sandbox`) | Installer backend choices | +| Distribution | [`scripts/core-distribution-manifest.py`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/scripts/core-distribution-manifest.py) | Bundled provider helper binaries | +| Clients and Web | [`sandbox-client.ts`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/packages/agents-client/src/sandbox-client.ts) (`SandboxProvider`), [`features/sandbox`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/web/src/features/sandbox) (`SandboxSetupWizard.tsx`, `console-config.ts`, `node-enrollment.ts`), [`sandbox-labels.ts`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/web/src/lib/sandbox-labels.ts) | Backend choices and labels shown to operators | Keep vendor-specific deployment validation and SDK setup at the construction boundary. Construction must not create an Environment. For node-local adapters @@ -220,12 +220,12 @@ cleanup can verify the `Reference` and installation. - Use the `io.oac.` prefix, for example `io.oac.installation` and `io.oac.tenant`. - Where the vendor rejects dotted keys, use `oac_` metadata keys (E2B). - Never accept older label names as a fallback; see - [Runtime names](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/CONTRIBUTING.md#openagentcore-runtime-names). + [Runtime names](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/CONTRIBUTING.md#openagentcore-runtime-names). ## Validate the integration Run `make check-sandbox-provider-contract` while developing. It runs the shared -[`contracttest`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/sandbox/contracttest) suite through +[`contracttest`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/sandbox/contracttest) suite through real adapter boundaries using controlled native failures, plus existing adapter and node transport tests. The same packages are included in `make check`. New adapters should call the public failure runner with native-side fixtures, @@ -253,11 +253,11 @@ Environment provides isolation. | Kind | Adapter | Helper | Operator guide | | --- | --- | --- | --- | -| Docker (node) | [`sandbox/docker`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/sandbox/docker) | Node proxy in [`sandbox/node`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/sandbox/node) | [Hosted sandbox manager](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/HOSTED-SANDBOX-MANAGER.md) | -| microsandbox (node) | [`sandbox/microsandbox`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/sandbox/microsandbox) | [`tools/microsandbox-provider`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/tools/microsandbox-provider) | [`deploy/microsandbox`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/deploy/microsandbox/README.md) | -| E2B (direct) | [`sandbox/e2b`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/internal/sandbox/e2b) | [`tools/e2b-provider`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/tools/e2b-provider/README.md) | [`deploy/e2b`](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/services/agents-api/deploy/e2b/README.md) | +| Docker (node) | [`sandbox/docker`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/sandbox/docker) | Node proxy in [`sandbox/node`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/sandbox/node) | [Hosted sandbox manager](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/HOSTED-SANDBOX-MANAGER.md) | +| microsandbox (node) | [`sandbox/microsandbox`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/sandbox/microsandbox) | [`tools/microsandbox-provider`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/tools/microsandbox-provider) | [`deploy/microsandbox`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/deploy/microsandbox/README.md) | +| E2B (direct) | [`sandbox/e2b`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/sandbox/e2b) | [`tools/e2b-provider`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/tools/e2b-provider/README.md) | [`deploy/e2b`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/deploy/e2b/README.md) | Provider selection and E2B setup are owned by -[Sandbox deployment](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/contracts/agents-api/sandbox-deployment.md). +[Sandbox deployment](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/sandbox-deployment.md). -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/e4d5a1d30520a967b3a44a257ed1b6c21e388388/docs/sandbox-provider.md) +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/sandbox-provider.md) diff --git a/apps/docs/content/guide-sources.json b/apps/docs/content/guide-sources.json index eefb1d50e..1faeedd18 100644 --- a/apps/docs/content/guide-sources.json +++ b/apps/docs/content/guide-sources.json @@ -1,58 +1,60 @@ { - "revision": "e4d5a1d30520a967b3a44a257ed1b6c21e388388", + "revision": "f45a4198aca79bf252412c55cad3647f5d704515", "sources": { - "docs/getting-started/README.md": "cdf8046c616574102a0ffc7644ac0fe82e63eb1906e6c9f1f6b635810e223757", - "docs/design-principles.md": "a33acd6e7bc80105a5c934d0be214c0cff59103e96c098f5b09179c86156c01d", - "docs/web/architecture.md": "80bdb8053c06c2b2030b193aae17d5434545f9c72af50e9d3df3ade522fe25ed", - "docs/getting-started/install.md": "a437b90c2cc4755e0fd760066837bfec96e9aa4a4a4bc5bc865b8ad02162c197", - "docs/getting-started/install-options.md": "aac219f367619036aa01573a295802fb8f0418e3d6db931efcfc88f17cf7734c", - "docs/configuration.md": "39cfa7f17025a615ebe2806651800ce8437cea741dd43227828b0b311c4c174b", - "docs/web/core-connection.md": "46f86520e70bf41079708e2c398655aa9087ad9488c8e661d9a73035d11098c4", - "docs/getting-started/quickstart.md": "238b4de1137edb8c2998b38f4525fea345a19fd4de7a455aa9c4c9e4fc3c9b36", - "docs/api/README.md": "592fc2aef474e36eb760be0dd1b2269b6766c0d984f826eaf6f47711ea139387", + "docs/getting-started/README.md": "d46e1b6cd0693a282f504297c87df6ddf26b99ce8c7afdb613666acd836767f5", + "docs/design-principles.md": "21a05fb089804db6fab5674ec9a6cf849a3affedad2ca927a38d9b46eeec9a50", + "docs/web/architecture.md": "b00352d49295a8ac4440420b748421af81a90778531698c707fefff4eb6223b1", + "docs/getting-started/install.md": "30c12edfdfb40c136065d0a83aad28568d7041328cc5d480d0546847d1b72e7d", + "docs/getting-started/install-options.md": "c806f8f229edccc8bcc4a853976e330cfee6025c9d3d00f6960fe2ee97a8c1d6", + "docs/configuration.md": "6f1bda6a9d16f893a14dcca6fe7df0b93acba77654a23248dee3b0f0ec288a24", + "docs/web/core-connection.md": "6467595fde1c8931f27de76ac38762dfb5a7880651e859d9aa71e233ea95a9c1", + "docs/getting-started/quickstart.md": "963e39eb81c65511a28d0826b2d8c3849dc29d1b810de12df2859d7aefd6d266", + "docs/api/README.md": "5e0b3ae0e178673f2727b74a55ce566fd562006cc5e9a8a7929ea6a183cef154", "contracts/agents-api/execution-tools.md": "e0b61f6c0c236c362186c5f6d0ad69a16dd8a8d9afabe1329a1fe22e5f4a71e9", - "docs/api/public-agent-api.md": "5b04e5ec9311df851376f66fc1584dd48ca123cd8c26ca63277c10b9172e9955", + "docs/api/public-agent-api.md": "219efab820d8b5cd35855d105b7cfc2b45c7e703f04a0145bf435a99b08a9e38", + "docs/examples.md": "0e1bdaeff51c9c36779f817be31ea9816b7d8cb2290cf8350d3c4801f436f4f9", "contracts/agents-api/environments.md": "9c0e794738c017e9dde57a659dc184d347e33730103583534c43e86d5d4183fd", - "docs/getting-started/nodes.md": "e50a2ca588c9d7cb836aa58e7b86ca685cd9da57742ceaee83ee0d549e538a94", - "docs/getting-started/self-hosted.md": "99d8851399bde2310196ac89dae1c365bba144421dbb33153094d30072de1783", - "docs/self-hosted-native.md": "e69819c3201109c8be5413716853eafa22d9d84b57aac780a4d335149b6a926b", + "docs/getting-started/nodes.md": "13e1f1e867d3e361ff860960ceacc2dd5806e64f4176cd33f5e49f9a9f4d57e4", + "docs/getting-started/self-hosted.md": "4591e05bfa7657f647bedecd9db07b7086acd94b5548511c88f7e5e3567fdb11", + "docs/self-hosted-native.md": "a3b470461285f0aa9507b262ec8edf11e02f6683328d05206849debe1360b77f", "apps/web/public/onboarding/monitor-en.webp": "29dc220cb1250c7016b7c4bf7f30e9510b07c07b814a48c3aa9303b2d76f20b2", - "docs/web/README.md": "9fce295f6f6ff04acefe338679c1e54723e123dd0027a778202adfa9ab95b6c4", - "docs/api/web-management.md": "d5f982922320069be5017cd524095d86eee0d66fd0d1cf97d1cdd97544d0dce5", + "docs/web/README.md": "47159689b2488f0a94a1af8488bcf98b465e0cca4003d64a4699d7b4db24e086", + "docs/api/web-management.md": "ec9a583c0a94845082ec31c7220ddcfb9aff7147c63fb36fa4c87463570f1b8c", "contracts/agents-api/runtime-observability-api.md": "cd46e777fe716a7e76374f4655c96dfbad9d8be1bfca6203ed6118ab87874c2b", - "docs/getting-started/operations.md": "2cba15fe56936cbc51e2ec8b8a888686273b21f729711e45c14ca6e32758663d", - "docs/user-guide.md": "a601c7f9df02c3bfe9c4ca3a43b91ab5721f3f8e904fccd116418dbdc1665a53", + "docs/getting-started/operations.md": "f5c0942003373aaf604fc0f5bb88e0d022f465f1ade2db2192975a173efe7d95", + "docs/user-guide.md": "1069d0f12aa42dde49693f25e8d964984e1c335fc67527123d31ee6a99617bab", "docs/development.md": "b40b7dd035247ecce8532f6e4c7c6365c1265506a1ed6d77e321bc4b00281667", - "contracts/agents-api/harness-onboarding.md": "c2913dd39881c153c15bbb2c73a0d348dbc949d288f98a57d9f398f6dd7f74ca", + "contracts/agents-api/harness-onboarding.md": "a1110017cc31e1b95a9eed5615dcc5aea426db819638aee16fb5f1d0c5aa21df", "docs/runtime-protocol.md": "1ef452ca82c25ab8d1b4b97893a0449b467edccd69f86c4233343358a1ea8b04", "docs/sandbox-provider.md": "cd2791affb8404391577a63accbc5bd3c536b251849c6bdf3b16344452c9b8ce", - "apps/docs/scripts/guides.json": "109374d4bf1a1d20d10ea85c9400ec6a2f9b62ca0fc6ee6030d3aab94a50d217" + "apps/docs/scripts/guides.json": "f4b2e58b64a89068a68a58ed1d8fe75893ee09d415e87391121e0e99bb1fbe3f" }, "outputs": { - "content/docs/index.mdx": "f98ce22ef71c0d5921f92b473c465cc27ddbe4616a7606e2138617b9aef4d55f", - "content/docs/concepts.mdx": "131d926a9efd2446517cb20c3c5d780e2bafda4817d93b1be3ce4a734c3530c5", - "content/docs/execution-model.mdx": "582e06b1c59fb6fb55da0849db753c6280585e636f68cbb34a812071cd11c4fb", - "content/docs/install.mdx": "1bd34e90289187eb072753c2f0a3161b7682916e36c90dc4d90400d6ed7ea221", - "content/docs/install-options.mdx": "8979ba5464db33310a3623d1e9bb8a33f74883e5ec7c4e7412354e62620f247d", - "content/docs/configure.mdx": "6ad08ae6de1e1d6376fbafa2c658338022cca202e36a0b31847167b196b607bd", - "content/docs/bootstrap-projects-keys.mdx": "174d0e61e72e51b08b558fb81fd413f89af58c423435d189d84ba6cb67976d34", - "content/docs/quickstart.mdx": "c92e0eba73e8725c44062be063e245704de7002e15f92ea0a4eb17498853c171", - "content/docs/public-api.mdx": "2136bb9a96ffe2bfde504df9833d1147c3d22548bbf510c42523cecea2f49e29", - "content/docs/agents-and-tools.mdx": "a3a6a669e25af073bf17cfb9e9699a6fd875dc31cd770fd28c12401078396ae4", - "content/docs/sessions.mdx": "c4abe10b9115174d10c10c73a373088460276f99f6c2cfe6df6b6aba8d6c7382", - "content/docs/environments-and-files.mdx": "72f9e2e1a276479480c4f5187df5e54840a888c03d35e6af29825a1f20bde454", - "content/docs/hosted-providers.mdx": "1076b39c59b0b63cc57a92eb738f4b5bd6b842787b16f51f9da0faa3e184ff9b", - "content/docs/self-hosted-execution.mdx": "dc94873d1b1bb5aa49380b095e92b988699f79c1ae497e0a28dab54aac4957c1", - "content/docs/self-hosted-native.mdx": "80185731954638a697652650112a8e2ebc348a6c8b42712a34f9755912774d14", + "content/docs/index.mdx": "63318e9faa387a608efa11af7bd67917579df22a1e7fed00f907b4794112581a", + "content/docs/concepts.mdx": "cf4dc9e8838782bbc16539c8dec1ccb16680fe14cd1472eabceab6b79885f503", + "content/docs/execution-model.mdx": "bf528e34ace32ee1c256aaa4f2e31575bcbbd62d5c5efa7177ddc7b8e275becb", + "content/docs/install.mdx": "9cf6b03ae1c0c6caca768ea4c3ff2a47d7dfbfc3648cc7a73b7c848c7e257d7b", + "content/docs/install-options.mdx": "3bbfa77af2c34f2b9f203079778b51ee9425624ca516c6392c859ee2add43591", + "content/docs/configure.mdx": "a39be53e21c65159af311724d7746e35a3c0dfcc9b87d5e79d01e54e7bb6d3ed", + "content/docs/bootstrap-projects-keys.mdx": "2ac720c071a0b37210c671b2d6fa704bb5720c2acaadac2650b893580965d2d8", + "content/docs/quickstart.mdx": "bab3cadd3cee86b6dcc11768cb81deb9d37c89521dfcace0a33d387e8d553815", + "content/docs/public-api.mdx": "055cb7e06d7647ff24e669a993ad1cfb806b2dfca51e12716f06df8fdf7ecac5", + "content/docs/agents-and-tools.mdx": "9ffa5a5883847f2e4d3641bfd5975baf448fd371542797fd93204f69ea6df5e2", + "content/docs/sessions.mdx": "4f3a5e6e774f2ad687c1e00841eb716e1370c9206a8bd7a118325fa77594630c", + "content/docs/examples.mdx": "50dc70b46605d83df289cc555c064884645cd875cc46d0688782c1fac8752a0f", + "content/docs/environments-and-files.mdx": "1c30645e80b6f099a859b7956ac6ef5e0e7d1d2a6a858b37215fba59d2dbf55d", + "content/docs/hosted-providers.mdx": "c504f42cea0c8c1ac6bd218a051b523cd8cd8609bd486233a787e2f4ff4f050c", + "content/docs/self-hosted-execution.mdx": "f190a4c40e532f3f24e8969c3df70f561b892ceb61b8b447eaf119312bffa071", + "content/docs/self-hosted-native.mdx": "e95c984383679bc1b2d016f864597eb625077e286228899c28eccbc5a89cc6b0", "public/images/source/apps/web/public/onboarding/monitor-en.webp": "29dc220cb1250c7016b7c4bf7f30e9510b07c07b814a48c3aa9303b2d76f20b2", - "content/docs/console.mdx": "f190022b65fa2ca1ac6dc9cac8e743f63bcbfb6278e8c85b7773f2fc6ca62728", - "content/docs/admin-api.mdx": "f9711580e69545daae9f364d4d5e246b2a7cf364fb8d8d7691576c63cde0e7b3", - "content/docs/observability.mdx": "f3edbbd338133c6938bdf42dc3be1938673755bd23f364dd4fa8253b46f06aa8", - "content/docs/troubleshooting.mdx": "fe75946cb164e2be21fcb0e68bcecddb4688a8385d589ae2b689902d144b0c0b", - "content/docs/user-guide.mdx": "005b775680b170032fa7f94c6f406f4ceef1d184bb6e58096e38fb6c5be13d43", - "content/docs/development.mdx": "f425b8cb6cf690b048e15647662abc3dbad9443ec6998028fee0fa0d9cf0a3e1", - "content/docs/harness-onboarding.mdx": "1533cc137dc0d0c05149ef9b02c1dfa6b9e460a35006fc912585797528a9941f", - "content/docs/runtime-protocol.mdx": "5fa5d61df52aa20f38fb57f9114d25524d0a1a31a03b165916f12f8d2b3bef5f", - "content/docs/sandbox-provider.mdx": "d72357c8a7ccd00fe316eb51b4f55a50ef9c880921ec40e654d2457b089df3bc" + "content/docs/console.mdx": "0a6a04c13a130a107f74c7c6cc518d24f6985780c94a6d0b9b0eb18384fb3253", + "content/docs/admin-api.mdx": "d553f7712b22e2eb46bf7c84c2b7a1d67df51996e1533aca2451d24c2caf7628", + "content/docs/observability.mdx": "595ab9f9918d04c058b64fa3a31d81913bbcbbc4d03138dca63a590979dde112", + "content/docs/troubleshooting.mdx": "ef71d3a2d93915c55741869c40c9b2c0cb870922b60732ce65ecc4c38f60cbf0", + "content/docs/user-guide.mdx": "5389281b1fa4743479d0d4f49baa631793187d2484e7e55984efc8a9f96d1b55", + "content/docs/development.mdx": "15ab2547830dc1a3debb0a66b7a93e6fd8118f224b2d608f84c912e78ecf2a76", + "content/docs/harness-onboarding.mdx": "683cbb12bb27ee2ac6d9bface4d773eb204233b636ea293323eec24f268b3979", + "content/docs/runtime-protocol.mdx": "fe008d47bea55bdeb16b4f55bcab8a77033242678778e12a425043cb3fbbeeff", + "content/docs/sandbox-provider.mdx": "1e5ed8f7f7c12882924da5d9e40f012d85d26edfab02e5a675c549a70f67727d" } } diff --git a/scripts/name-allowlist.json b/scripts/name-allowlist.json index 9831f2413..c249fe4f2 100644 --- a/scripts/name-allowlist.json +++ b/scripts/name-allowlist.json @@ -589,11 +589,6 @@ "regex": "Parsar owns product|Parsar, not the HTTP contract|Parsar cutover|Parsar services|no Parsar dependency", "reason": "These exact phrases refer to the separate Parsar product, its ownership or historical source, not the OpenAgentCore brand." }, - { - "path": "contracts/agents-api/mcode-workspace-v1.md", - "regex": "without Parsar", - "reason": "These exact phrases refer to the separate Parsar product, its ownership or historical source, not the OpenAgentCore brand." - }, { "path": "contracts/agents-api/model-execution.md", "regex": "Parsar manages",