Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,9 @@ The Runtime is not a sandbox; see
implementation must not require a new orchestration path selected by its name.
Sandbox registration, configuration adaptation and persistence boundaries follow
the [Sandbox Provider guide](docs/sandbox-provider.md#register-the-provider-kind).
Resource operation declarations are exhaustive and validated against the existing
small interfaces; support is never inferred from method presence. See the
[explicit operation contract](docs/sandbox-provider.md#explicit-operation-contracts).
- 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 stay behind their
Expand Down
65 changes: 49 additions & 16 deletions apps/docs/content/docs/sandbox-provider.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ connectivity; see [Runtime and outer isolation](/concepts#runtime-and-outer-isol

## Steps

1. **Read the contract.** Implement the five required operations and any
optional interfaces in [Implement the interface](#implement-the-interface).
1. **Read the contract.** Implement the five required operations and explicitly handle all
extension interfaces in [Implement the interface](#implement-the-interface).
2. **Write the adapter package** under `services/agents-api/internal/sandbox/<kind>`
(native SDK calls, ownership checks, identity translation, private config).
Assert `var _ sandbox.SandboxProvider = (*YourAdapter)(nil)` at compile time.
Expand Down Expand Up @@ -69,20 +69,53 @@ 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
### Explicit operation contracts

Keep the existing small interfaces. Every Provider must implement their methods
and return a complete `ProviderOperations()` declaration. The interface methods
are the operation inventory; `sandbox.ValidateOperations` checks it without a
second hand-maintained list.

| Contract | Requirement | Responsibility |
| --- | --- | --- |
| `sandbox.SandboxProvider` | Required | `Create`, `GetInfo`, `Renew`, `Kill`, and bounded bootstrap/diagnostic `RunCommand` |
| `sandbox.CheckpointProvider` | Optional, separate interface | Exact compute incarnations, snapshot capture/restore, retained-source resume and cleanup |
| `runtimeobs.Source` | Optional, separate interface | Read-only, ownership-checked resource observations |
| `runtimeobs.BatchSource` | Optional, separate interface | Bounded observations in input order, with per-target errors; `ok=false` means no batch read occurred |

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/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/runtime-observability.md).
| `sandbox.SandboxProvider` | Five operations must be supported | Allocation lifecycle and bounded administrative commands |
| `sandbox.CheckpointProvider` | Explicit supported or unsupported decision for every method | Exact compute incarnations, capture/restore, retained-source resume and cleanup |
| `runtimeobs.Source` | Explicit decision | Ownership-checked read-only observations |
| `runtimeobs.BatchSource` | Explicit decision | Bounded observations in input order, with per-target errors |
| `sandbox.SelectionDiscoverer` | Explicit decision | Read-only native configuration discovery before commit |
| `sandbox.CredentialVerifier` | Explicit decision | Verify access to owned resources without mutation |

Each declaration entry has `state: supported` with no reason, or
`state: unsupported` with an authored reason code. Missing entries, zero states,
unknown entries, missing methods and unsafe reasons fail validation. The entire
checkpoint lifecycle must agree on support; batch observation requires single
observation. Adding a method to an existing interface requires an explicit
decision and implementation in every adapter. Never supply a default base class
or generate blanket unsupported implementations for future methods.

Unsupported methods return `providercontract.UnsupportedError` before native
I/O. The error identifies the exact operation and a safe code, not a native
message, resource identity, endpoint or credential. Empty results, nil errors,
`Unavailable`, and unknown mutation outcomes cannot substitute for unsupported.
The five required methods cannot return unsupported; a backend without a native
lease preserves the existing read-only `Renew` semantics.

Each adapter owns one `Operations()` function, shared by its concrete instance
and registration. `providers.ValidateBinding` checks both against the existing
interfaces and against each other. Runtime admission and node generation loading
also reject incomplete providers. Interface assertions establish method shape
only; callers use the declaration to decide whether an operation is supported.

`ObserveBatch` returns an error instead of an ambiguous boolean. Only a typed,
safe `UnsupportedError` for `ObserveBatch` permits per-target `Observe` calls.
An unavailable service, timeout or other failure never triggers that fallback.
Observation never renews, starts, prepares or stops compute; see the
[observation contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/runtime-observability.md).

Contract tests call every declared unsupported native method with no configured
native client, require its matching error and zero result, and reject incomplete
or contradictory declarations. Supported behavior still requires native and
lifecycle tests; declaration validation alone cannot prove SDK semantics.

`RunCommand` is an existing administrative bootstrap/diagnostic facility, not an
alternate route for Skills, Plugins, MCP setup, initial files, Session execution or
Expand Down Expand Up @@ -173,20 +206,20 @@ A provider must not implement a competing preparation path.

`sandbox/providers/registry.go` is the sole registration table. Each entry binds
an adapter's specification/resource validators, selection normalization, deployment
mode, defaults, optional checkpoint capability, and local or direct constructor.
mode, defaults, the adapter-owned operation declaration, and local or direct constructor.
`providers.Build` constructs node-local adapters; `providers.BuildDirect` constructs
direct adapters. Neither allocates compute. There is no init-time registration or
runtime plugin loading.

For a new implementation:

1. Implement `SandboxProvider` in its adapter package and add native contract tests.
1. Implement the operation contracts above in the adapter package and add native contract tests.
2. Add its configuration validators and optional read-only `SelectionDiscoverer`
for native resource discovery. Normalization must copy input before changing it.
`RestoreSelection` must retain access to owned resources without requiring new
template validation. Put native credential verification behind
`CredentialVerifier` when needed.
3. Register its constructor, policies and defaults in `providers/registry.go`.
3. Register its constructor, policies, operation declaration and defaults in `providers/registry.go`.
Node proxy identity and checkpoint advertisement consume this same entry.
The installer projection uses those registered policies and the common field
bounds in `sandbox/deployment_contract.go`; regenerate it with
Expand Down
4 changes: 2 additions & 2 deletions apps/docs/content/guide-sources.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"contracts/agents-api/harness-onboarding.md": "e8fceb236e7fb0794eade26639d86b0622ec7dbb1fad90c30f41742c9d029232",
"docs/runtime-bootstrap.md": "0d49aed73b298039e04453e6f465b0e925fb2227fa35d4206820bb3acbd8df39",
"docs/runtime-protocol.md": "1d48186fa84140403358d2d01ddaca56d7122e1b3606cb6855ddefffeb3b8ed6",
"docs/sandbox-provider.md": "4d47b234a457f874f3ff7e61a7f5da6fc8b75b0c6df0ce0ce9ead1c07afdfb3e",
"docs/sandbox-provider.md": "a1323c7f6a28a4f5f0e823274c08422707a5dab85b11e008ccb28a3aafc02058",
"apps/docs/scripts/guides.json": "3c3768fb94fd3d42464d4ce8c55724b9ba8360133c7cc19d513d8ec83a8e034f"
},
"outputs": {
Expand Down Expand Up @@ -59,6 +59,6 @@
"content/docs/harness-onboarding.mdx": "8959febf77b2e9e4b16d301eabd8027f09ebd635f2c2713a40d22884afe95d12",
"content/docs/runtime-bootstrap.mdx": "58982955811c9ad46a9fc04d8d2aef5a762fc9d25d5833f88aa75ee3fc46523f",
"content/docs/runtime-protocol.mdx": "caa7700e442b0666340c091f3ec0bff32e6217b3be4bc845c3b9c23ee0c8d0f6",
"content/docs/sandbox-provider.mdx": "9db833fe9ff3f30a65451f80d7251348695a08e7830f9e95871739a710529818"
"content/docs/sandbox-provider.mdx": "83a6244fb2140836aa6746c990c9cd04d04e58d5b3a52faf1a84e5e846dfd99f"
}
}
16 changes: 16 additions & 0 deletions contracts/agents-api/node-generation-protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,22 @@ Every Provider request carries its exact allocation-owned deployment generation;
Core never strips it for an older peer. Automatic preparation and retention frames
are sent only to nodes advertising generation management.

## Provider operation outcomes

Node startup and generation loading validate complete Provider operation
declarations before accepting work. Proxies use the same registered declaration
for admission; unsupported operations reject before node resolution or native I/O.
The Provider [operation contract](../../docs/sandbox-provider.md#explicit-operation-contracts)
owns the inventory and support rules.

A Provider response with `error_code: unsupported` carries an `unsupported` object
containing the exact method `operation` and an authored safe `reason` code. The
proxy checks both against the request. Missing, malformed or mismatched evidence
is an unconfirmed result, never proof that a mutation was rejected. Unsupported
remains distinct from observation unavailability and unknown compute/command
results; it does not settle resource ownership or authorize replay. The current
private wire version requires both peers to understand this outcome.

## Bounded control

A generation-managing node's hello or heartbeat contains at most eight generation observations.
Expand Down
65 changes: 49 additions & 16 deletions docs/sandbox-provider.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ connectivity; see [Runtime and outer isolation](design-principles.md#runtime-and

## Steps

1. **Read the contract.** Implement the five required operations and any
optional interfaces in [Implement the interface](#implement-the-interface).
1. **Read the contract.** Implement the five required operations and explicitly handle all
extension interfaces in [Implement the interface](#implement-the-interface).
2. **Write the adapter package** under `services/agents-api/internal/sandbox/<kind>`
(native SDK calls, ownership checks, identity translation, private config).
Assert `var _ sandbox.SandboxProvider = (*YourAdapter)(nil)` at compile time.
Expand Down Expand Up @@ -66,20 +66,53 @@ 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
### Explicit operation contracts

Keep the existing small interfaces. Every Provider must implement their methods
and return a complete `ProviderOperations()` declaration. The interface methods
are the operation inventory; `sandbox.ValidateOperations` checks it without a
second hand-maintained list.

| Contract | Requirement | Responsibility |
| --- | --- | --- |
| `sandbox.SandboxProvider` | Required | `Create`, `GetInfo`, `Renew`, `Kill`, and bounded bootstrap/diagnostic `RunCommand` |
| `sandbox.CheckpointProvider` | Optional, separate interface | Exact compute incarnations, snapshot capture/restore, retained-source resume and cleanup |
| `runtimeobs.Source` | Optional, separate interface | Read-only, ownership-checked resource observations |
| `runtimeobs.BatchSource` | Optional, separate interface | Bounded observations in input order, with per-target errors; `ok=false` means no batch read occurred |

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](../contracts/agents-api/runtime-observability.md).
| `sandbox.SandboxProvider` | Five operations must be supported | Allocation lifecycle and bounded administrative commands |
| `sandbox.CheckpointProvider` | Explicit supported or unsupported decision for every method | Exact compute incarnations, capture/restore, retained-source resume and cleanup |
| `runtimeobs.Source` | Explicit decision | Ownership-checked read-only observations |
| `runtimeobs.BatchSource` | Explicit decision | Bounded observations in input order, with per-target errors |
| `sandbox.SelectionDiscoverer` | Explicit decision | Read-only native configuration discovery before commit |
| `sandbox.CredentialVerifier` | Explicit decision | Verify access to owned resources without mutation |

Each declaration entry has `state: supported` with no reason, or
`state: unsupported` with an authored reason code. Missing entries, zero states,
unknown entries, missing methods and unsafe reasons fail validation. The entire
checkpoint lifecycle must agree on support; batch observation requires single
observation. Adding a method to an existing interface requires an explicit
decision and implementation in every adapter. Never supply a default base class
or generate blanket unsupported implementations for future methods.

Unsupported methods return `providercontract.UnsupportedError` before native
I/O. The error identifies the exact operation and a safe code, not a native
message, resource identity, endpoint or credential. Empty results, nil errors,
`Unavailable`, and unknown mutation outcomes cannot substitute for unsupported.
The five required methods cannot return unsupported; a backend without a native
lease preserves the existing read-only `Renew` semantics.

Each adapter owns one `Operations()` function, shared by its concrete instance
and registration. `providers.ValidateBinding` checks both against the existing
interfaces and against each other. Runtime admission and node generation loading
also reject incomplete providers. Interface assertions establish method shape
only; callers use the declaration to decide whether an operation is supported.

`ObserveBatch` returns an error instead of an ambiguous boolean. Only a typed,
safe `UnsupportedError` for `ObserveBatch` permits per-target `Observe` calls.
An unavailable service, timeout or other failure never triggers that fallback.
Observation never renews, starts, prepares or stops compute; see the
[observation contract](../contracts/agents-api/runtime-observability.md).

Contract tests call every declared unsupported native method with no configured
native client, require its matching error and zero result, and reject incomplete
or contradictory declarations. Supported behavior still requires native and
lifecycle tests; declaration validation alone cannot prove SDK semantics.

`RunCommand` is an existing administrative bootstrap/diagnostic facility, not an
alternate route for Skills, Plugins, MCP setup, initial files, Session execution or
Expand Down Expand Up @@ -170,20 +203,20 @@ A provider must not implement a competing preparation path.

`sandbox/providers/registry.go` is the sole registration table. Each entry binds
an adapter's specification/resource validators, selection normalization, deployment
mode, defaults, optional checkpoint capability, and local or direct constructor.
mode, defaults, the adapter-owned operation declaration, and local or direct constructor.
`providers.Build` constructs node-local adapters; `providers.BuildDirect` constructs
direct adapters. Neither allocates compute. There is no init-time registration or
runtime plugin loading.

For a new implementation:

1. Implement `SandboxProvider` in its adapter package and add native contract tests.
1. Implement the operation contracts above in the adapter package and add native contract tests.
2. Add its configuration validators and optional read-only `SelectionDiscoverer`
for native resource discovery. Normalization must copy input before changing it.
`RestoreSelection` must retain access to owned resources without requiring new
template validation. Put native credential verification behind
`CredentialVerifier` when needed.
3. Register its constructor, policies and defaults in `providers/registry.go`.
3. Register its constructor, policies, operation declaration and defaults in `providers/registry.go`.
Node proxy identity and checkpoint advertisement consume this same entry.
The installer projection uses those registered policies and the common field
bounds in `sandbox/deployment_contract.go`; regenerate it with
Expand Down
Loading