Files
MetaCrate/docs/grid-agent-architecture.md
Chili Palmer c101007362
Some checks failed
CI / rust-skia (Rust only) (push) Has been cancelled
CI / required (push) Has been cancelled
Add headless wgpu vision and camera controls
2026-08-22 11:36:34 +02:00

9.3 KiB

Grid-agent architecture foundation

The grid agent is a workspace-owned Rust library and small service binary. Its core depends on Tokio for scheduling and bounded channels. Its live-grid feature owns the existing libremetaverse composition root for grid protocol managers; the default offline graph does not compile live transports. It does not create a second login, UDP, capabilities, inventory, or world client. It contains no CLR/.NET loading, sidecar, subprocess adapter, provider SDK, native ABI, or platform-specific core path.

Ownership and bounds

AgentService::start owns offline task creation, while SessionSupervisor::start owns the live lifecycle task. Configuration is validated before either allocates channels, calls a backend, or permits network access. Their handles exclusively own cancellation, join handles, controls, and observable receivers.

Resource Owner Hard/configured bound Backpressure/termination
Grid-event queue coordinator receives; backend sends 8,192 / grid_event_queue async send or cancellation
Control queue coordinator receives; handle sends 256 / control_queue async send; closed after stop
Observable queue handle receives; coordinator/backend send 8,192 / observable_queue async send or cancellation
Backend task ServiceHandle.tasks[0] exactly one in offline mode shared cancellation token, joined first
Coordinator task ServiceHandle.tasks[1] exactly one shared cancellation token, joined second
Live session supervisor SessionSupervisorHandle one owner; one active generation/session generation cancellation, exact-once logout, bounded join
Offline session work live supervisor 1,024 hard / reconnect.offline_work_capacity read-only queue; mutations rejected while not ready
Body / message typed boundary owners 8 MiB / 64 KiB hard ceilings, with lower configured limits rejected before enqueue
Conversation / tool calls request owner 256 messages / 64 calls, with lower configured limits rejected before request
Per-avatar conversation memory ConversationStore mutex 4,096 sessions / 64 MiB hard, lower conversation limits monotonic expiry, deterministic compaction/LRU eviction
Interaction ingress / observations InteractionHandle 8,192 each hard, lower queue configuration bounded admission; lifecycle uses nonblocking watch state
Per-avatar interaction FIFO InteractionCoordinator 4,096 senders / 64 messages each hard, lower interaction limits fair ready queue, one active request per avatar/channel
Interaction inference / outbound generation tasks and channel rate limiters 64 concurrent hard; 1,023 bytes per grid part timeout/cancellation fencing; independent public/IM pacing
LLM conversation runtime avatar-scoped Mentra agent configured model/tool budgets and 2-minute default wall time SSE cancellation, compaction, or supersession
Autonomous approval review fresh volatile Mentra runtime one model request, no tools/history/memory exact ALLOW/DENY; every error fails closed
Policy tools / approvals / schedules PolicyGateway mutex 64 tools / 4,096 approval records / 1,024 scheduler grants hard deny before opaque authorization
Principal/global resource budget PolicyGateway time window validated calls, zero L$, upload, inventory, movement, and build ceilings atomic charge or stable denial
Authorized avatars immutable AgentConfig set 1,024 hard ceiling, lower configured limit malformed, nil, duplicate, and wildcard input rejected
Configuration / secret file loader 64 KiB / 16 KiB regular non-symlink file only

BoundedText and BoundedVec make message and collection ceilings part of the type. Dynamic configuration can lower these absolute ceilings but cannot raise them. Backend error text is not forwarded; observations publish a fixed bounded diagnostic.

State machines and shutdown

The explicit service states are starting -> running <-> paused -> stopping -> stopped, with failed reserved for task failure. The offline backend publishes BackendReady, after which the coordinator enters running. A shutdown control, direct handle shutdown, backend failure, closed owner queue, or handle drop triggers the same cancellation token.

Orderly shutdown first cancels, joins the backend, and then joins the coordinator. Each join has the validated shutdown timeout. A late task is aborted and awaited before return. A partially polled shutdown future only borrows each join handle, so cancelling that future leaves every task in the handle's fixed ownership slots for a retry or final drop. Dropping the handle cancels and aborts all remaining owned tasks, so no task is detached. A feature-enabled live adapter drops its LibremetaverseClientOwner last, invoking the existing client ownership shutdown.

Live modes use stopped, connecting, degraded (transport connected but not fully ready), online, backoff, authentication-blocked, paused, and shutting-down. Every attempt rotates a generation cancellation token. Disconnect, pause, logout, force reconnect, or shutdown invalidates it before cleanup, fencing old events and late LLM/tool results. See grid-agent-session.md.

Trust boundaries

  • YAML configuration and legacy migration input are untrusted. Unknown fields, oversized files, invalid booleans, conflicting modes, non-HTTP(S) URLs, URL fragments, malformed/noncanonical/nil UUIDs, wildcard authorization, multiline secrets, and unsafe limits fail before startup.
  • SecretString and EndpointUrl are the only credential-bearing value types. Secrets are never serializable or printable. Endpoint diagnostics replace user information, passwords, and the complete query.
  • Grid input crosses GridBackend only through bounded GridEvent values. The live-grid feature supplies LibremetaverseClientOwner; live implementations must own it and reuse its client and managers.
  • World changes cross only WorldMutator::apply, which always receives the non-forgeable AuthorizedAction produced by PolicyGateway. Raw calls and caller-created decisions are not accepted. This issue supplies no live mutation implementation.
  • LLM traffic crosses the configured provider base through Mentra's Responses SSE runtime. MetaCrate supplies only endpoint, credential, model, permitted tools, and run bounds. Proposed calls cross PolicyToolExecutor only after Mentra schema handling and MetaCrate's independent policy evaluation.
  • Conversation context is keyed by immutable avatar UUID and either public chat or direct IM. Group channels are not representable. The LLM projection can retrieve only one exact key, and recovered/untrusted summaries remain user-role prompt data rather than system authority.
  • Public-chat and direct-IM input crosses a bounded normalization boundary. Authority is derived only from channel plus sender UUID; public chat can never acquire operator authority. Output is safety-filtered, UTF-8 split, rate limited, and associated with its trigger, session, generation, and delivery result.
  • Signals and console output belong to the binary. The reusable core relies on no terminal, Unix socket, Unix signal, separator, or fixed platform path.

Dependency diagram

metacrate-grid-agent binary (portable Ctrl-C + config path)
                    |
                    v
offline AgentService -> bounded Tokio channels/tasks -> injected GridBackend
     |                                           |
     v                                           v
typed config/events/policy boundaries    live-grid feature boundary
     |                                           |
     +-----------------> libremetaverse-types    +--> libremetaverse::GridClient

avatar session -> persistent Mentra agent -> Responses SSE / compaction / memory
                       |
                       +-> PolicyToolExecutor -> PolicyGateway
                               |                   |
                               |                   +-> AuthorizedToolBackend
                               +-> one-shot Mentra safety reviewer when required

scene snapshot -> cached headless wgpu color/depth render -> JPEG -> Mentra image
                      `-> deterministic software fallback

The package has no build script. Its cross-platform wgpu path uses the platform graphics backend selected by wgpu and requires no window or display; the software fallback remains available when adapter creation or rendering fails. The focused dependency_policy test rejects subprocess launch sites, unsafe/native ABI source, build scripts, and unreviewed direct dependency names in this package. The precise LLM compatibility and cancellation contract is documented in grid-agent-llm.md. The origin/capability matrix and opaque mutation boundary are documented in grid-agent-policy.md. The live lifecycle and generation contract is documented in grid-agent-session.md. The conversation isolation and persistence contract is documented in grid-agent-conversation.md. The public-chat/IM admission, fairness, authorization, egress, and lifecycle contract is documented in grid-agent-interaction.md.