Environment protocol
HTTP operations, callbacks and resource lifecycle.
This reference is for authors of environment services: HTTP endpoints that run code or manage resources for Brain. Start with Write an environment. To use an existing provider, follow Choose an environment.
Factory
import { environment } from "@aexhq/brain";
import { z } from "zod";
export const sandbox = environment({
options: z.object({ url: z.url(), token: z.string(), image: z.string() }),
url: ({ url }) => url,
credential: ({ token }) => token,
configure: ({ image }) => ({ image }),
});
const env = sandbox({ name: "sandbox", url: "https://sandbox.example",
token: "your-service-token", image: "node:22" });Load the real service token from your credential store. Brain posts to {url}/v1/operations.
The service must authenticate requests; the factory alone does not enforce authentication.
Credentials are sealed by the server and never journaled.
Configuration is recorded and forwarded unread. The Environment name is unique within its session.
Operations
A command has
contract: "environment/v1" and operation: { session_id, environment, sequence, request }.
New operations also carry the pinned binding reference, current configuration, source
template, operation-scoped context and binding-scoped reporter capabilities. The template
selects the originally authorized driver credential; provider configuration cannot replace it.
The response carries the same contract and sequence and a receipt.
| Request | Responsibility |
|---|---|
setup { configuration } | Validate Environment-owned options and establish the binding. Allocation can be lazy. |
execute { implementation, input, deadline_ms?, callback? } | Prepare and run the opaque implementation within configured authority. deadline_ms is the remaining duration in milliseconds, not a Unix timestamp. An absent deadline is unlimited. |
call { name, input } | An Environment-specific operation, such as explicit workspace reset. |
cancel { target_sequence } | Best-effort cancellation of the named invocation. |
detach | Release session attachment mechanisms while retaining owned resources. |
teardown | Release resources owned by this Environment instance. |
Receipts are accepted, progress, result { output }, returned { output? }, failure, or unknown. Return an
unsupported failure for an unsupported operation or descriptor. Neither turn nor invoke
is an Environment operation. An Agentloop adapter interprets its typed input and output above
this generic boundary; an Environment can support both Agentloops and Tools.
An Agentloop returns its turn result. For a Tool, result and returned end only the synchronous
phase, optionally emitting output. Call the granted finish service to complete the Tool, either
before returning or later. Its callback remains valid after return under the original deadline.
Returning accepted or progress leaves the dispatched outcome unknown. Failure fields code, message, retryable,
and optional JSON details reach Tool results unchanged. Retryability is advisory; Brain does
not retry or restore failed resources automatically.
For Tool invocations, return a failure with code timeout when its deadline expires, or cancelled
for explicit cancellation. Keep the first stop cause if cleanup also fails, including cleanup
evidence in details. These codes do not promise rollback. Use unknown only when dispatch may
have happened but no reliable result is available. An explicit unknown receipt does not itself
mark the Environment unreachable; a transport failure still does.
Invocation services
An execution may receive callback: { url, token, methods }. POST { method, input } to that
URL with its bearer token. The response is the method's JSON result. Only listed methods are
granted. A Tool receives emit, result, returned, finish, model, environments, and telemetry;
an Agentloop receives emit, telemetry, events, model,
dispatch, environments, set_transcript, kv_read, kv_put, and kv_delete. Dispatch input is an array of { call_id, name, environment, environment_sequence?, input }; model
input is ModelRequest; events input is a cursor number; emit input is { event_type, data };
telemetry input is the diagnostic record. Results are respectively ToolReturn[], ModelResult,
EventPage, committed sequence, and null.
Tool result takes an Outcome; returned and finish take an optional Outcome (JSON null
means no new result). Each returns its committed sequence. Finish orders after earlier admitted
emissions and closes further emission authority. Successful Agentloop return completes its delivered event batch. Failure or cancellation
leaves that batch pending; saved writes remain durable. Journal observations never automatically
edit model messages. Event-triggered Agentloop input omits the user input field.
set_transcript takes a message array. kv_put takes { key, value }; kv_delete takes
an identifier string. Mutations return the committed sequence. kv_read takes an identifier
string and returns {} for absence or { value } for a stored value, including JSON null.
Reads after an awaited mutation observe it. These services belong only to the Agentloop.
The packaged JavaScript loop uses the loop API
names ctx.kv.get, ctx.kv.set and ctx.kv.delete. Turn output is { result? }, with
no transcript or KV snapshot. Deletion appends a journal mutation, preserving earlier records.
The token belongs to one invocation and is revoked on completion or cancellation. It is never
part of the journal. environments accepts the generated EnvironmentControlRequest and is
bounded by that extension's own grants. It grants no general session API access. Cancellation makes
subsequent calls fail. Never preserve a callback for the next execution.
Lifecycle and delivery
Resource lifetime belongs to the Environment's configuration. Automatic session creation calls setup, ending calls detach, and deletion calls teardown. Brain passivating between turns does not end the session or release its resources.
Setup can return { type: "accepted", on_turn_end: "release" } to register a method for
turn completion. After a successful, failed, or cancelled turn, Brain sends
call { name: "release", input: { sequence } }, where sequence names turn_started.
The Environment decides what this method does and whether its configuration enables it.
For example, a disposable sandbox can terminate; a workspace can remain available.
The answer is saved and returned before this call finishes. Unfinished Tools defer turn-end cleanup until their execution resources have been released. The session can process later messages and Tool observations while that background work remains open. Brain journals cleanup and waits for it before completing a graceful drain. A failed cleanup does not replace the answer. Like other effects, callbacks are not retried or replayed after a restart; a provider deadline still bounds resources if Brain stops before delivery. Report resource loss explicitly, without silently allocating replacements.
Controller services and binding observations
An Env operation receives its own context callback with environments, emit and result.
It uses the Env's grants, independently of the execute.callback belonging to a hosted Tool
or Agentloop. environmentHandler(...) exposes this as ctx.environments, ctx.emit and
ctx.emitResult; the context closes when that operation returns or is cancelled.
ctx.reporter may remain with the provider controller. POST { type: "event", event_type, data }
for diagnostics, or { type: "result", output: { observation, content? } } for actionable facts,
to operation.reporter.url with its bearer token. Brain assigns Environment provenance and
returns the committed sequence. The reporter survives Brain restart but cannot control the
session; closed or replaced bindings reject old reports. Pending observations share the existing
emission limit. Never give a provider controller's capability to untrusted executed code.
Observation scopes are environment (availability), resource (provider identifier, code and
message), and operation (pending sequence and confirmed resolution). An unknown lifecycle
operation remains reserved until its correlated resolution. A resource observation does not
change binding availability. Actionable observations wake the Agentloop through the existing
watermark and coalescing mechanism; diagnostics alone do not. See
Control environments for grants and templates.
Brain commits each operation before delivery and sends it once. Identify it by session and sequence, retain uncertainty when an effect may have occurred, and never automatically retry an unknown execution. The provider defines the scope and lifetime of retained files; invocation state and callback credentials belong only to the current call.
The lazy workspace example tests concurrent first allocation and caller-controlled cleanup. The remote Agentloop example resolves a JavaScript implementation and calls granted Brain services. Both bind loopback and are authoring examples; they do not isolate untrusted code.
Optional implementation preparation
Keep dependency details inside the implementation's package and this Environment's loader. The descriptor can select a project with a lockfile and optional setup script. Bootstrap the interpreter and import-time dependencies before loading setup or run functions. A setup function written in Python cannot install the Python interpreter required to invoke itself.
The Python Environment example
uses operator-selected projects, uv sync --locked, an optional setup module, then
uv run --no-sync. Concurrent first use shares preparation by installation directory, even
across sessions. Detach and teardown release the binding, not the externally owned installation.
A failed preparation remains failed until the operator replaces the loader; no request silently
retries it. A preprepared package needs no custom setup hook.
This helper is an OS-runtime example, not a sandbox. The operator must supply an isolated runtime, package-network policy, and appropriately limited process credentials. Project paths and module names are operator configuration, not arbitrary session input. Brain does not run these commands, resolve dependencies, or fall back from incompatible Wasm placement.