grpc-v1 Protocol Reference
grpc-v1 Protocol Reference
grpc-v1 is the stable worker protocol implemented by the Rust and Python SDKs. The
current source of truth is
crates/worker-proto/proto/nemo/relay/worker/v1/plugin_worker.proto.
Generated protobuf types are wire material, not the recommended authoring API.
Relay 0.8 retains the grpc-v1 identifier and nemo.relay.worker.v1 package, but it
changes the tool-result boundary to structural protobuf messages. Rebuild workers,
regenerate custom bindings, and declare compat.relay beginning at 0.8.0; an earlier
worker cannot decode the current ToolNext response or tool-execution outcome.
The protocol consists of the worker-facing service implemented by the plugin process and the host-runtime service implemented by Relay. These are the current service definitions, including cancellation, codecs, and streaming continuations:
Worker Service
Each lifecycle message carries the following fields. Fields described as envelopes use the schema identifiers in the JSON envelope table later on this page.
Registration Surfaces
The numeric values are part of the wire contract. Unknown value zero never represents a valid registration.
An invocation names the activation, invocation, registration, surface, optional continuation, captured scope, and token. Its payload is exactly one event, tool invocation, or LLM invocation. LLM sanitizer invocations additionally carry codec identity and an opaque invocation-scoped codec capability.
For EVENT_METADATA_INJECTOR, Relay sends the immutable Event snapshot in
InvokeRequest.event. The worker returns an InvokeResponse.json object containing
proposed additions to Event.metadata. Relay validates and merges accepted additions
before Event sanitizers run. A WorkerError omits that callback’s additions without
dropping the Event.
Tool values remain JSON, but the result boundary is structural so Relay can keep an opaque annotation beside the application value without treating either field as a schema-tagged envelope.
continuation_id is present only for execution intercepts. scope captures the host
context used for continuation and runtime calls. registration_name is the
component-local name the worker returned in RegisterResponse; Relay separately owns
its qualification in the host registries.
LlmCodecKind has four wire values: unspecified, built-in, runtime, and opaque. A built-in
identity carries openai_chat, openai_responses, anthropic_messages, oci_genai,
or gemini_generate_content; a runtime identity carries its registered ID. An opaque
identity deliberately withholds an ID. The capability ID is optional and
invocation-scoped. A worker must treat it as a secret and must not use it after the owner
invocation ends.
Host-Runtime Service
The host-runtime request and response fields are complete in the following table:
ScopeContext contains scope_stack_id and parent_scope_id. ScopeType supports
agent, function, tool, LLM, retriever, embedder, reranker, guardrail, evaluator, custom,
and unknown scopes. The unspecified wire value is invalid for a pushed scope.
EmitMarkRequest.data_schema uses the nemo.relay.DataSchema@1 envelope, and
severity accepts trace, debug, info, warn, or error. Omitting both preserves
the original mark behavior. GetRuntimeDiagnostics aggregates repeated codes, retains
the latest message, sorts entries by code, and returns at most 32 entries. It is a
host-level view, so it does not identify the plugin that recorded a diagnostic.
Authentication and Endpoints
Relay creates a fresh activation ID and high-entropy token, passes the worker and host endpoints through the activation environment, and sends the same values in handshake. Every later worker and host-runtime request includes the activation ID and token. SDKs bind the endpoint locally, prefer Unix domain sockets where supported, constrain TCP fallback to loopback, and reject mismatched credentials. Local authentication prevents accidental cross-activation calls; it does not make untrusted worker code safe.
Relay supplies the following process environment to Rust, Python, and custom-command workers:
JSON Envelopes and Errors
JsonEnvelope contains a schema identifier and UTF-8 JSON bytes. General values use
nemo.relay.Json@1; typed request, annotation, outcome, and event schemas identify their
own expected shape. The envelope owns its bytes for the message lifetime, so neither side
borrows language-runtime objects across RPCs.
The current envelope schemas are as follows:
JsonValue contains exactly one JSON value and carries arbitrary tool results without
numeric coercion. ToolExecutionResult requires that value in result and permits an
optional annotation; JSON null annotations normalize to absence. A tool execution
intercept returns ToolExecutionInterceptOutcome, which adds an optional JSON array of
Relay-owned pending_marks. Surface results otherwise use empty, JSON, guardrail, LLM
request outcome, or structured error variants. Stream chunks contain one JSON value or
one terminal error. WorkerError carries a stable code, human-readable message, and
retryable flag; transport errors remain distinct from plugin callback errors.
A clean stream ends when the server stream closes after its last value. A callback
failure travels as the terminal error item. Transport cancellation or an unavailable
process remains a gRPC status and must not be rewritten into a plugin WorkerError by a
custom implementation.
Cancellation and Shutdown Sequence
The host and worker use the following sequence to end active work and release the worker process:
- Relay stops routing new calls to the component and sends
CancelInvocationfor in-flight work that cannot drain normally. - The worker acknowledges known active invocations and cooperatively cancels their async tasks. Continuation and codec capabilities become unusable when their owner invocation ends.
- Relay sends
Shutdownwith the activation ID, token, and reason. The worker stops its service and closes the local endpoint. - Relay waits for the managed process within its shutdown policy, terminates it if necessary, removes component registrations, and deletes a managed Python environment only during explicit package removal.
Successful protocol verification covers authentication failures, envelope schema failures, every registration and result variant, repeated unary and incremental stream continuations, cancellation before and during callbacks, codec capability expiry, host runtime ownership errors, health, and orderly plus forced shutdown.