This file provides guidance to agents, including Claude Code and OpenAI Codex, when working in this repository.
NeMo Relay is a multi-language agent runtime framework for execution scopes, lifecycle events, middleware, plugins, and observability around tool and LLM calls. The core runtime is Rust. Primary supported bindings are Rust, Python, and Node.js. Go, WebAssembly, and the raw C FFI are experimental and source-first.
The shared runtime model is:
- Scope stacks decide where work belongs and which scope-local behavior is visible.
- Middleware registries decide what guardrails and intercepts run around managed calls.
- Plugins install reusable runtime behavior from configuration.
- Events record runtime behavior in ATOF form.
- Subscribers and exporters consume events in-process or export them to ATIF, OpenTelemetry, OpenInference, or other backends.
The repository layout separates the Rust runtime, language bindings, documentation, integration patches, and agent-facing skills.
crates/
core/ # Rust core runtime crate, published as nemo-relay
adaptive/ # Adaptive runtime primitives and plugin components
python/ # PyO3 native extension for the Python package
ffi/ # Raw C ABI layer used by downstream bindings such as Go
node/ # NAPI Node.js binding and JavaScript/TypeScript entry points
wasm/ # wasm-bindgen WebAssembly binding and JS wrappers
python/
nemo_relay/ # Python wrapper package: scopes, tools, LLM, middleware, typed helpers, plugins, adaptive helpers
tests/ # Python tests
go/
nemo_relay/ # Experimental Go CGo binding and tests
fern/ # Fern documentation site
scripts/ # Stable wrappers and helper scripts; build/test/docs entry points live in justfile
third_party/ # Pinned upstream checkouts for sample integration patches
patches/ # NeMo Relay patch sets applied to third_party checkouts
skills/ # Published Codex/agent skills for NeMo Relay usage patterns
Install the tools needed for the surfaces you touch. For a full repository validation environment, install all of these:
| Tool | Version / Notes | Required For |
|---|---|---|
| Rust | Docs minimum is 1.86 or newer; the repo pins the active toolchain in rust-toolchain.toml |
Rust core, native bindings, FFI, WebAssembly |
| Python | 3.11 or newer | Python package, PyO3 builds, docs tooling |
| Node.js | 24 or newer, with npm | Node.js binding, WebAssembly JS tests, generated API docs |
| Go | 1.21 or newer | Experimental Go binding |
uv |
Current project workflow tool | Python environments, docs dependencies, pre-commit |
just |
1.40 or newer | Canonical build, test, docs, package task runner |
wasm-pack |
0.14.0 or newer | WebAssembly build and integration tests |
cargo-deny |
Current stable | Rust dependency auditing |
cargo-nextest |
0.9.111 or newer | CI-style Rust test runs |
cargo-llvm-cov |
0.8.5 or newer | CI-style coverage reports |
Common setup commands:
cargo install just --locked
cargo install cargo-deny --locked
cargo install cargo-nextest --version 0.9.111 --locked
cargo install cargo-llvm-cov --version 0.8.5 --locked
cargo install wasm-pack --version 0.14.0 --locked
uv sync
uv run pre-commit install
npm install --ignore-scriptsuv sync installs Python development and test dependencies, including maturin, ruff, ty, and pre-commit. Documentation recipes sync the docs dependency group as needed, but Python, Node.js, npm, uv, and just still need to exist on PATH.
Prefer the repository just recipes over raw tool commands. Use raw cargo, pytest, go test, npm, or wasm-pack commands only for focused debugging or targeted single-test reruns that do not have a just recipe.
Discover the current task surface with:
just --listBuild targets:
just build-rust
just build-python
just build-node
just build-go
just build-wasm
just build-allTest targets:
just test-rust
just ci=true test-rust # CI-style Rust test run; uses nextest and coverage tooling when available
just test-python
just test-node
just test-go
just test-wasm
just test-allDocumentation targets:
just docs
just docs-api-referencePackage targets:
just package-python
just package-node
just package-wasmCleanup:
just cleanFocused fallback commands are acceptable for narrow loops:
cargo test -p nemo-relay -- <test_name>
uv run pytest python/tests/test_scope.py
uv run pytest -k "test_name"
cd crates/node && node --test --test-name-pattern="pattern" tests/*.mjs
cd go/nemo_relay && go test -v -run TestFoo ./...
wasm-pack test --node crates/wasmRun tests for every language affected by a change. If you touch the Rust core runtime, middleware semantics, event shape, scope behavior, typed codecs, plugins, or observability, expect to validate every affected binding because the bindings share the same runtime contract.
Minimum guidance:
- Rust core or adaptive changes:
just test-rust; add binding tests when public behavior changes. - Python binding or wrapper changes:
just test-python. - Node.js binding or wrapper changes:
just test-node. - Go binding or raw FFI changes:
just test-goand the relevant Rust/FFI checks. - WebAssembly binding changes:
just test-wasm. - Documentation site changes:
just docs. Run docs link validation withjust docs-linkcheckwhen links change. The recipes regenerate the ignored Fern API reference pages before validation. - Cross-language API changes: run the touched binding tests and update docs, package READMEs, and generated surfaces where applicable.
Before review, prefer uv run pre-commit run --all-files when the change crosses languages or tooling. The hooks enforce SPDX headers, file hygiene, Ruff, ty, Markdown link checks, Cargo formatting/lints/audits, Go formatting/vet, Node formatting, and public docstring checks.
These conventions keep source, documentation, and binding behavior consistent across the repository.
- Keep SPDX headers on source, docs, scripts, and configuration files. The project is Apache-2.0.
SKILL.mdfiles are skill entrypoints and do not need SPDX headers, but they must always start with YAML frontmatter containing at leastnameanddescription.- Follow binding naming conventions: Rust and Python
snake_case, C FFI exports prefixednemo_relay_, GoPascalCasefor public APIs, Node.jscamelCase. - Preserve the shared runtime model across bindings. Do not add behavior to one primary binding without considering Rust, Python, and Node.js parity.
- Prefer documented public APIs and stable wrapper commands. Do not rely on internal helpers in examples or user-facing docs.
- Keep primary documentation focused on Rust, Python, and Node.js. Treat Go, WebAssembly, and raw FFI as experimental and source-first unless binding-support guidance changes.
- Use
Json = serde_json::Valuein Rust-facing runtime APIs where the existing code expects JSON payloads. - Use
Result<T>withFlowErrorin core runtime paths. Keep errors explicit and binding-appropriate at the wrapper layer. - Keep async behavior on the existing tokio-based model. Bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.
- Do not hand-edit generated or packaged outputs unless the repository workflow expects them to be checked in. Regenerate through the documented recipe or script.
These runtime patterns describe the shared semantics that bindings and integrations must preserve.
- Scope stacks are hierarchical and always have a root scope. They establish parent-child event relationships, visibility for scope-local middleware and subscribers, cleanup boundaries, and concurrent request isolation.
- Scope-local middleware and subscribers are owned by a scope and disappear when that scope closes. Global registrations stay process-wide until removed.
- Middleware is priority-ordered after merging global and visible scope-local entries.
- Intercepts change the real execution path. Request intercepts rewrite the request. Execution intercepts wrap or replace the callback. Stream execution intercepts handle streaming lifecycle behavior.
- Guardrails either block execution or sanitize emitted observability payloads. Sanitize guardrails do not rewrite the real callback arguments or return value.
- Managed execution order is conditional guardrails, request intercepts, sanitize-request guardrails for start events, execution intercepts, callback execution, then sanitize-response guardrails for end events.
- Events use ATOF
0.1as the canonical event format. Scope events use start/end pairs; mark events record runtime checkpoints. - LLM and tool event metadata belongs in the category profile, such as
model_name,tool_call_id, and customsubtypefields. - Exporters can transform runtime events to ATIF trajectories, OpenTelemetry traces, or OpenInference-compatible output. Root scope identity is used to isolate concurrent agents.
These notes summarize how each language binding relates to the Rust runtime source of truth.
- Rust is the source of truth for runtime behavior. Binding APIs should mirror the Rust semantics unless a language-specific wrapper intentionally improves ergonomics.
- Python wrapper modules live under
python/nemo_relay/; the native extension is built fromcrates/pythonwithmaturin. - Node.js public entry points include the main runtime package plus
nemo-relay-node/typed,nemo-relay-node/plugin, andnemo-relay-node/adaptive. - Go uses the C FFI and requires the FFI library build before tests;
just test-gohandles the library path setup. - WebAssembly includes Rust wasm-bindgen tests plus JS wrapper/package tests;
just test-wasmruns both paths.
Sample integrations are maintained as patch sets, not as primary package source. The pinned upstream checkouts are listed in third_party/sources.lock, local checkouts live under third_party/, and NeMo Relay patches live under patches/.
Current integration patch sets include:
- Hermes Agent:
third_party/hermes-agent - LangChain:
third_party/langchain - LangChain NVIDIA:
third_party/langchain-nvidia - LangGraph:
third_party/langgraph - OpenClaw:
third_party/openclaw - opencode:
third_party/opencode
Use the stable root-level wrappers:
./scripts/bootstrap-third-party.sh
./scripts/apply-patches.sh
./scripts/apply-patches.sh --check
./scripts/generate-patches.shapply-patches.sh expects clean third-party checkouts. After editing an integration checkout, run ./scripts/generate-patches.sh to regenerate patch files and verify they apply to a clean detached checkout.
Some integrations can be implemented using public APIs without patching. Currently the Python based integrations are located under python/nemo_relay/integrations/ with their own README files and test suites.
Current public API-based integrations include:
- LangChain:
python/nemo_relay/integrations/langchain
These workflow notes keep public documentation, examples, and PR preparation aligned with repository expectations.
- Update
README.md,fern/, package READMEs, and binding-support notes when public behavior, package names, examples, or supported bindings change. - Keep release-process details in maintainer docs such as
RELEASING.md. Do not move release-history policy into user-facing docs orCHANGELOG.md. - Keep stable public wrappers at the
scripts/root in docs and examples. Reference namespaced helper paths only when documenting internal maintenance work. - Use branch prefixes from the contributor docs:
feat/,fix/,docs/,test/, orrefactor/. - Use signed-off commits for PR work:
git commit -s. - Before creating, opening, publishing, or editing a pull request, read
.github/pull_request_template.mdand use it as the PR body skeleton. Preserve its visible headings, checklist items, and related-issue guidance; fill the sections instead of replacing them with a generic summary. - If repo-local PR guidance such as the
prepare-prskill conflicts with generic GitHub connector or plugin guidance, follow the repo-local PR guidance for PR body format and review handoff details. - PR descriptions should include what changed, why, how it was tested, and any breaking changes within the repository template format.