Skip to content

feat: Improve Hermes Relay hook injection and event alignment#205

Open
yczhang-nv wants to merge 19 commits into
NVIDIA:mainfrom
yczhang-nv:codex/nat-294-relay-telemetry
Open

feat: Improve Hermes Relay hook injection and event alignment#205
yczhang-nv wants to merge 19 commits into
NVIDIA:mainfrom
yczhang-nv:codex/nat-294-relay-telemetry

Conversation

@yczhang-nv
Copy link
Copy Markdown
Contributor

@yczhang-nv yczhang-nv commented Jun 3, 2026

Overview

Adds Relay-side support needed for NVIDIA NeMo Agent Toolkit to consume Hermes Relay telemetry.

This change:

  • Updates transparent Hermes Relay runs to temporarily patch Hermes hook config, route events to the local Relay gateway, and restore the original config afterward.
  • Improves Hermes session alignment by correlating task-scoped tool events back to the owning Hermes session.
  • Handles Hermes pre/post tool hook ID mismatches by matching active tools by name and arguments when provider tool IDs differ.

Why

Hermes invokes hooks from its own config and can execute through a different process boundary than the caller. For NeMo Agent Toolkit integration, Relay needs to capture Hermes lifecycle events reliably and make them available as replayable ATOF JSONL so the toolkit can import them into its telemetry stream.

Details

Where should the reviewer start?

Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

  • Closes #

Summary by CodeRabbit

  • New Features

    • Hermes hook lifecycle: backup, patch, restore (dry-run supported); restores on failed runs/health checks.
    • Improved tool tracking retains tool name, arguments, and owner context for better correlation.
    • Better LLM/response parsing and span de-duplication for observability exports.
  • Bug Fixes

    • Hermes task→session routing and lifecycle cleanup corrected to avoid duplicate sessions and stale mappings.
    • More robust tool-end resolution with owner-context matching.
  • Tests

    • New unit/integration tests covering Hermes hooks, routing, tool correlation, and observability dedupe.
  • Documentation

    • Readme, CLI, Hermes, Cursor, integrations, and release notes updated; Node/WebAssembly/Python examples call out subscriber flush and Node.js 24+ requirement.

@yczhang-nv yczhang-nv requested a review from a team as a code owner June 3, 2026 01:05
@yczhang-nv yczhang-nv self-assigned this Jun 3, 2026
@github-actions github-actions Bot added size:L PR is large lang:python PR changes/introduces Python code lang:rust PR changes/introduces Rust code labels Jun 3, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 3, 2026

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds Hermes task→session correlation and routing, per-tool ActiveTool tracking for robust end-event matching, Hermes hook backup/merge/restore in launcher runs, LLM span de-duplication and metric merging in ATIF, Node API main-thread Arg0 builders, many tests, and documentation/workflow updates.

Changes

Hermes Task-Session Correlation and Tool-ID Resilience

Layer / File(s) Summary
Hermes Task-Session State and Extraction Utilities
crates/cli/src/alignment/mod.rs
SessionAlignmentState adds hermes_task_sessions HashMap for task-to-session mappings; extraction helpers parse task ids and optional session scope from Hermes provider payloads/metadata and JSON paths.
Hermes Routing Integration into Event Flow
crates/cli/src/alignment/mod.rs
route_event records Hermes task-session mappings and rewrites Hermes ToolStarted/ToolEnded events via task lookup before continuing alias-based routing; lifecycle pruning removes mappings on agent/subagent end.
Hermes adapter correlatability guard
crates/cli/src/adapters/hermes.rs
Adapter emits nothing and returns empty response for uncorrelatable Hermes pre_tool_call payloads; helper functions validate required session/tool identifiers.
Active Tool State with Name and Arguments
crates/cli/src/session.rs
Session.tools now stores ActiveTool { handle, name, arguments, owner_subagent_id }. start_tool records resolved args and owner; end_tool uses remove_tool_handle_for_event fallback matching by tool_call_id or unique (tool_name, exact_arguments) within owner context.
Hermes Hook Lifecycle Management
crates/cli/src/launcher.rs
Launcher adds HermesRestore and hermes_restore in PreparedRun; prepare_hermes backs up and merges .hermes/config.yaml (via merge_hermes_config), sets HERMES_ACCEPT_HOOKS, and records restore state; PreparedRun::restore restores Cursor and Hermes hooks with shared helper.
Hermes Integration and Tool-Matching Tests
crates/cli/tests/coverage/*
Adds/updates tests validating Hermes hook patching and restoration, uncorrelatable pre_tool_call suppression, Hermes task-session routing with/without explicit parent session, tool-id mismatch handling, and launcher restore on gateway health failure.

Observability: ATIF & OpenInference

Layer / File(s) Summary
LLM span de-duplication & metric merging
crates/core/src/observability/atif.rs, crates/core/tests/*
New LLM dedupe subsystem collects span candidates, computes signatures, suppresses lower-fidelity overlapping spans, merges supplemental metrics into canonical spans, and skips suppressed events during step conversion; multiple unit tests added to validate dedupe behavior.
Anthropic messages & tool promotion
crates/core/src/observability/atif.rs, crates/core/tests/unit/atif_tests.rs
Detect Anthropic messages payloads, aggregate content blocks, promote tool_use blocks into tool_calls, and accept args/input alt fields; update tool_call_extra recognized keys.
OpenInference display & cache-read handling
crates/core/src/observability/openinference.rs, tests
Cache-read token extraction extended to nested *_details.cached_tokens; prefer OpenAI Responses-style output parsing and synthesize “Requested tools” from function calls; tests added/updated accordingly.

Node API: main-thread Arg0 and with_scope

Layer / File(s) Summary
PrimaryArg / Arg0Builder plumbing
crates/node/src/promise_call.rs
Introduce Arg0Builder type and PrimaryArg to allow building the first JS callback argument on the main thread; add call_with_arg0 public API and update TSFN payload shape.
with_scope forwarding real ScopeHandle
crates/node/src/api/mod.rs, crates/node/tests/scope_tests.mjs
with_scope now passes a real ScopeHandle via call_with_arg0; tests validate the received handle works as event target, parent for child scopes, and execution scope for tool/LLM calls.
JS/WASM examples
crates/node/README.md, crates/wasm/README.md
Examples updated to include flushSubscribers and call it after scope usage to flush async subscriber delivery.

OpenClaw integration & replay tests

Layer / File(s) Summary
Provider usage normalization
integrations/openclaw/src/hook-replay/llm.ts, tests
mapUsage accepts more alias keys and nested cached-token paths; adds nestedNumberField; tests added for Anthropic/OpenAI replay normalization.

Docs, workflows, and scripts

Layer / File(s) Summary
Fern docs workflow & sync script
.github/workflows/fern-docs.yml, scripts/docs/sync_fern_docs_branch.py
Increase release job timeout; checkout workflow tools separately; pin source checkout to release tag; add Rust/Python/setup steps and source API generation; run sync script from workflow-checkout; sync_fern_docs_branch.py now accepts --source-root and snapshots source_root/docs.
Release notes, MDX docs, READMEs, redirects
docs/**, README.md, AGENTS.md, fern/docs.yml
Major release-notes content added, support matrix updates, small wording changes across many docs, README updates (Node.js requirement, ATIF v1.7), CODE_OF_CONDUCT header update, and a fern redirects entry.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • NVIDIA/NeMo-Relay#195: Shares related fern docs workflow checkout and running sync flow from workflow-checkout.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@yczhang-nv yczhang-nv added Improvement improvement to existing functionality and removed lang:rust PR changes/introduces Rust code lang:python PR changes/introduces Python code labels Jun 3, 2026
@github-actions github-actions Bot added lang:python PR changes/introduces Python code lang:rust PR changes/introduces Rust code and removed Improvement improvement to existing functionality labels Jun 3, 2026
@yczhang-nv yczhang-nv changed the title Add NAT telemetry export support for Hermes Relay runs feat: Add NAT telemetry export support for Hermes Relay runs Jun 3, 2026
@github-actions github-actions Bot added the Feature a new feature label Jun 3, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/cli/src/alignment/mod.rs`:
- Line 113: hermes_task_sessions is keyed only by task_id, so
record_hermes_task_session and route_hermes_task_event can collide across
sessions; change the storage to be collision-resistant by including session
scope in the key (e.g., use a composite key session_id + task_id or make
hermes_task_sessions a HashMap<session_id, HashMap<task_id, session_id_or_meta>>
/ nested map) and update record_hermes_task_session and route_hermes_task_event
to read/write using that composite/session-scoped key so entries cannot be
overwritten when different sessions reuse the same task_id.

In `@crates/cli/src/launcher.rs`:
- Around line 466-483: prepare_hermes currently mutates the user's Hermes config
during PreparedRun::new which can leave config.yaml patched if execute_live_run
returns early on failed health checks; update the flow so we either defer
calling prepare_hermes (or the write_merged_hermes_hooks step) until after
wait_for_health succeeds in execute_live_run, or ensure PreparedRun::restore is
always invoked on every early-return path from execute_live_run (add
try/finally-like cleanup or explicit restore on error paths). Refer to
prepare_hermes, write_merged_hermes_hooks, execute_live_run, wait_for_health,
and PreparedRun::restore to locate and implement the change.

In `@crates/cli/src/session.rs`:
- Around line 129-134: ActiveTool currently only stores handle, name, and
arguments so identical name+args from different subagents get conflated; add a
resolved owner/subagent identity field (e.g., owner or subagent_id) to the
ActiveTool struct and populate it wherever ActiveTool instances are created (the
synthetic start path and any start_tool constructors), update derives as needed,
and change the fallback/matching logic in end_tool (and the analogous code at
the other referenced range) to require the owner/subagent to match before
considering a name+args fallback so the real handle for a different subagent is
not left open.

In `@crates/cli/tests/coverage/session_tests.rs`:
- Around line 1615-1634: The pre/post tool-call test is vacuous because the
pre_tool_call JSON uses an empty session_id and can be filtered out, so the
subsequent assertion on session.tools.is_empty() doesn't prove mismatched
tool_call_id handling; update the test to make the pre_tool_call and
post_tool_call belong to a real session (non-empty session_id) and split the
test into phases: after injecting the pre_tool_call assert
!session.tools.is_empty() (e.g., session.tools.len() > 0), then inject the
post_tool_call with a different tool_call_id and assert session.tools.is_empty()
to verify the mismatch causes the tool to be removed; apply the same change
pattern to the other occurrence referenced (the block around the second instance
at the later lines).

In `@python/nemo_relay/nat_exporter.py`:
- Around line 40-47: The subscriber() currently reopens the file on every event
causing syscall overhead; instead open a single append handle in __init__ (e.g.
self._output = self._path.open("a")) and reuse it in subscriber() to write
_event_to_json(event) + "\n", calling self._output.flush() after each write for
durability; keep the existing self._lock and self._closed checks in
subscriber(), and close/cleanup the handle in shutdown() (and guard against
double-close) so the file is only opened once and closed on shutdown.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 539e1bb4-0a60-4cd1-880a-82c4de95efd0

📥 Commits

Reviewing files that changed from the base of the PR and between b85bd24 and 49893ec.

📒 Files selected for processing (9)
  • crates/cli/src/alignment/mod.rs
  • crates/cli/src/launcher.rs
  • crates/cli/src/session.rs
  • crates/cli/tests/coverage/launcher_tests.rs
  • crates/cli/tests/coverage/session_tests.rs
  • python/nemo_relay/README.md
  • python/nemo_relay/__init__.py
  • python/nemo_relay/nat_exporter.py
  • python/tests/test_nat_exporter.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Check / Run
  • GitHub Check: Preview docs
🧰 Additional context used
📓 Path-based instructions (29)
**/*.rs

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Use snake_case naming convention for Rust identifiers (e.g., nemo_relay_tool_call)

**/*.rs: Any Rust change must run just test-rust
Any Rust change must run cargo fmt --all
Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all for all FFI work since it is Rust work
Run just test-rust to validate FFI changes
Run cargo clippy --workspace --all-targets -- -D warnings to enforce strict linting on FFI work

When Rust files changed as part of Go work, also run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all when Rust files are changed as part of Node work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files are changed as part of Node work
Run just test-rust when Rust files are changed as part of Node work

**/*.rs: Run cargo fmt --all to format all Rust code
Run cargo clippy --workspace --all-targets -- -D warnings to enforce all clippy lints as errors

**/*.rs: Run cargo fmt --all when Rust files changed as part of WebAssembly work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files changed as part of WebAssembly work

**/*.rs: If any Rust code changed, always run just test-rust
If any Rust code changed, also run cargo fmt --all
If any Rust code changed, also run cargo clippy --workspace --all-targets -- -D warnings
Run Rust formatting with cargo fmt --all
Run Rust linting with cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Use cargo fmt for Rust code formatting
Run cargo clippy -- -D warnings to lint Rust code and treat all warnings as errors
Use Rust snake_case naming convention for Rust identifiers
Include SPDX license header in all Rust source files using double-slash comment syntax
Validate Rust code with uv run pre-commit run --all-files to enforce cargo fmt formatting check, cargo clippy lints, and cargo deny aud...

Files:

  • crates/cli/tests/coverage/session_tests.rs
  • crates/cli/src/launcher.rs
  • crates/cli/tests/coverage/launcher_tests.rs
  • crates/cli/src/session.rs
  • crates/cli/src/alignment/mod.rs
{crates/adaptive/**/*.rs,**/*test*.{rs,py,go,ts,js},**/*adaptive*test*.{rs,py,go,ts,js},docs/plugins/adaptive/**}

📄 CodeRabbit inference engine (.agents/skills/maintain-optimizer/SKILL.md)

Maintain documented and tested validation and report behavior for adaptive surfaces

Files:

  • crates/cli/tests/coverage/session_tests.rs
  • python/tests/test_nat_exporter.py
  • crates/cli/tests/coverage/launcher_tests.rs
**/{Cargo.toml,**/*.rs}

📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)

Maintain consistency between Rust package names in Cargo.toml and their actual usage across the codebase

Files:

  • crates/cli/tests/coverage/session_tests.rs
  • crates/cli/src/launcher.rs
  • crates/cli/tests/coverage/launcher_tests.rs
  • crates/cli/src/session.rs
  • crates/cli/src/alignment/mod.rs
**/*.{h,hpp,c,cpp,rs}

📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)

Ensure FFI header and library naming follows consistent conventions across platform-specific builds

Files:

  • crates/cli/tests/coverage/session_tests.rs
  • crates/cli/src/launcher.rs
  • crates/cli/tests/coverage/launcher_tests.rs
  • crates/cli/src/session.rs
  • crates/cli/src/alignment/mod.rs
**/*.{rs,toml}

📄 CodeRabbit inference engine (.agents/skills/rename-surfaces/SKILL.md)

Update Rust crate names and module prefixes during coordinated rename operations

Files:

  • crates/cli/tests/coverage/session_tests.rs
  • crates/cli/src/launcher.rs
  • crates/cli/tests/coverage/launcher_tests.rs
  • crates/cli/src/session.rs
  • crates/cli/src/alignment/mod.rs
**/*.{rs,py,js,ts,tsx,jsx,go,sh,toml,yaml,yml,md}

📄 CodeRabbit inference engine (AGENTS.md)

Keep SPDX headers on source, docs, scripts, and configuration files. The project is Apache-2.0.

Files:

  • crates/cli/tests/coverage/session_tests.rs
  • python/nemo_relay/__init__.py
  • python/nemo_relay/nat_exporter.py
  • python/tests/test_nat_exporter.py
  • python/nemo_relay/README.md
  • crates/cli/src/launcher.rs
  • crates/cli/tests/coverage/launcher_tests.rs
  • crates/cli/src/session.rs
  • crates/cli/src/alignment/mod.rs
**/*.{rs,py,go,js,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions: Rust and Python use snake_case, C FFI exports prefixed nemo_relay_, Go uses PascalCase for public APIs, Node.js uses camelCase.

Files:

  • crates/cli/tests/coverage/session_tests.rs
  • python/nemo_relay/__init__.py
  • python/nemo_relay/nat_exporter.py
  • python/tests/test_nat_exporter.py
  • crates/cli/src/launcher.rs
  • crates/cli/tests/coverage/launcher_tests.rs
  • crates/cli/src/session.rs
  • crates/cli/src/alignment/mod.rs
crates/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

crates/**/*.rs: Keep async behavior on the existing tokio-based model. Bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.
Use Json = serde_json::Value in Rust-facing runtime APIs for JSON payload handling.

Files:

  • crates/cli/tests/coverage/session_tests.rs
  • crates/cli/src/launcher.rs
  • crates/cli/tests/coverage/launcher_tests.rs
  • crates/cli/src/session.rs
  • crates/cli/src/alignment/mod.rs
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}

⚙️ CodeRabbit configuration file

{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}: Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant.
Prefer assertions on lifecycle events, scope stacks, middleware ordering, and binding parity over shallow smoke tests.

Files:

  • crates/cli/tests/coverage/session_tests.rs
  • python/tests/test_nat_exporter.py
  • crates/cli/tests/coverage/launcher_tests.rs
{crates/python/src/py_api/**/*.rs,python/nemo_relay/**/*.py,python/nemo_relay/**/*.pyi}

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Update Python native binding in crates/python/src/py_api/mod.rs with Python wrapper docstring in python/nemo_relay/<module>.py and type stubs in python/nemo_relay/*.pyi modules

Files:

  • python/nemo_relay/__init__.py
  • python/nemo_relay/nat_exporter.py
python/nemo_relay/**/*.py

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Use snake_case naming convention for Python identifiers (e.g., nemo_relay.tools.call)

Format changed Python wrapper and test files with uv run ruff format python

Python wrapper modules live under python/nemo_relay/; the native extension is built from crates/python with maturin.

Files:

  • python/nemo_relay/__init__.py
  • python/nemo_relay/nat_exporter.py
{pyproject.toml,**/*.py}

📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)

Maintain consistency between Python package names in pyproject.toml and import paths used throughout the codebase

Files:

  • python/nemo_relay/__init__.py
  • python/nemo_relay/nat_exporter.py
  • python/tests/test_nat_exporter.py
**/*.{py,txt,toml,cfg,yaml,yml}

📄 CodeRabbit inference engine (.agents/skills/rename-surfaces/SKILL.md)

Update Python package names and top-level module imports during coordinated rename operations

Files:

  • python/nemo_relay/__init__.py
  • python/nemo_relay/nat_exporter.py
  • python/tests/test_nat_exporter.py
**/*.py

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

**/*.py: Run Python formatting with uv run ruff format python
Run Python testing with uv run pytest -k "<pattern>"

**/*.py: Use Ruff with rule sets E, F, W, I for Python linting
Use Ruff formatter with line length 120 and double quotes for Python code formatting
Run ty for Python type checking
Use Python snake_case naming convention for Python identifiers
Include SPDX license header in all Python source files using hash comment syntax
Validate Python code with uv run pre-commit run --all-files to enforce Ruff linting and formatting, and ty type checking

Files:

  • python/nemo_relay/__init__.py
  • python/nemo_relay/nat_exporter.py
  • python/tests/test_nat_exporter.py
**/*.{md,mdx,py,sh,yaml,yml,toml,json}

📄 CodeRabbit inference engine (.agents/skills/contribute-docs/SKILL.md)

Keep package names, repo references, and build commands current

Files:

  • python/nemo_relay/__init__.py
  • python/nemo_relay/nat_exporter.py
  • python/tests/test_nat_exporter.py
  • python/nemo_relay/README.md
python/nemo_relay/**/*

⚙️ CodeRabbit configuration file

python/nemo_relay/**/*: Review Python wrapper changes for typed API consistency, contextvars-based scope isolation, async behavior, and parity with the native extension.
Stubs and runtime implementations should stay aligned.

Files:

  • python/nemo_relay/__init__.py
  • python/nemo_relay/nat_exporter.py
  • python/nemo_relay/README.md
**/test_*.{py,py}

📄 CodeRabbit inference engine (.agents/skills/add-integration/SKILL.md)

Relevant integration tests or smoke coverage must exist for the integration path

Files:

  • python/tests/test_nat_exporter.py
python/**/*test*.py

📄 CodeRabbit inference engine (.agents/skills/test-python-binding/SKILL.md)

python/**/*test*.py: Do not add @pytest.mark.asyncio to any test in Python test files
Do not add a -> None return type annotation to test functions
When mocking a class, use unittest.mock.MagicMock or unittest.mock.AsyncMock with the spec constructor argument when necessary, rather than defining a new class
Prefix mocked class names with mock, not fake
Prefer pytest fixtures over helper methods in Python tests
Prefer pytest.mark.parametrize over creating individual tests for different input types

Files:

  • python/tests/test_nat_exporter.py
python/**/{conftest.py,*test*.py}

📄 CodeRabbit inference engine (.agents/skills/test-python-binding/SKILL.md)

When creating a fixture follow the pattern: @pytest.fixture(name="<fixture_name>"[, scope="<scope>"]) def <fixture_name>_fixture() -> <return_type>: and only specify the scope argument when the value is something other than "function"

Files:

  • python/tests/test_nat_exporter.py
**/*.{md,rst,html,txt}

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/assets/nvidia-style-brand-terminology.md)

**/*.{md,rst,html,txt}: Always spell NVIDIA in all caps. Do not use Nvidia, nvidia, nVidia, nVIDIA, or NV.
Use an NVIDIA before a noun because the name starts with an 'en' sound.
Do not add a registered trademark symbol after NVIDIA when referring to the company.
Use trademark symbols with product names only when the document type or legal guidance requires them.
Verify official capitalization, spacing, and hyphenation for product names.
Precede NVIDIA product names with NVIDIA on first mention when it is natural and accurate.
Do not rewrite product names for grammar or title-case rules.
Preserve third-party product names according to the owner's spelling.
Include the company name and full model qualifier on first use when it helps identify the model.
Preserve the official capitalization and punctuation of model names.
Use shorter family names only after the full name is established.
Spell out a term on first use and put the acronym in parentheses unless the acronym is widely understood by the intended audience.
Use the acronym on later mentions after it has been defined.
For long documents, reintroduce the full term if readers might lose context.
Form plurals of acronyms with s, not an apostrophe, such as GPUs.
In headings, common acronyms can remain abbreviated. Spell out the term in the first or second sentence of the body.
Common terms such as CPU, GPU, PC, API, and UI usually do not need to be spelled out for developer audiences.

Files:

  • python/nemo_relay/README.md
**/*.{md,rst,html}

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/assets/nvidia-style-brand-terminology.md)

Link the first mention of a product name when the destination helps the reader.

Files:

  • python/nemo_relay/README.md
**/*.md

📄 CodeRabbit inference engine (.agents/skills/contribute-integration/SKILL.md)

Documentation must be updated if activation or usage changed

**/*.md: Use title case consistently in technical documentation headings
Avoid quotation marks, ampersands, and exclamation marks in headings
Keep product, event, research, and whitepaper names in their official title case
Use title case for table headers
Do not force social-media sentence case into technical docs
Format code elements, commands, parameters, package names, and expressions in monospace
Format directories, file names, and paths in monospace using backticks
Use angle brackets inside monospace for variables inside paths, such as /home/<username>/.login
Format error messages and strings in quotation marks, keeping literal code strings in code formatting when clearer
Format UI buttons, menus, fields, and labels in bold
Use angle brackets between UI labels for menu paths, such as File > Save As
Use italics for new terms on first use, sparingly and only when introducing the term
Use italics for publication titles
Format keyboard shortcuts in plain text, such as Press Ctrl+Alt+Delete
Use owner/repo link text for GitHub repositories, preferring [NVIDIA/NeMo](link) over prose references like 'the GitHub repo'
Introduce every code block with a complete sentence
Do not make a code block complete the grammar of the previous sentence
Do not continue a sentence after a code block
Use syntax highlighting when the format supports it for code blocks
Avoid the word 'snippet' unless the surrounding docs already use it as a term of art
Keep inline method, function, and class references consistent with nearby docs, omitting empty parentheses for prose readability when no call is shown
Use descriptive anchor text that matches the destination title when possible for links
Avoid raw URLs in running text
Avoid generic anchor text such as 'here,' 'this page,' and 'read more'
Include acronyms in link text when a linked term includes an acronym
Do not link long sentences or multiple sentences
Avoid links ...

Files:

  • python/nemo_relay/README.md
**/{docs,examples,**/*.md,*.patch,*.diff,.github,*.sh,*.yaml,*.yml}

📄 CodeRabbit inference engine (.agents/skills/rename-surfaces/SKILL.md)

Update documentation, examples, CI configuration, and patch artifacts when performing rename operations

Files:

  • python/nemo_relay/README.md
**/*.{md,rst,txt}

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/assets/nvidia-style-guide.md)

Spell NVIDIA in all caps. Do not use Nvidia, nvidia, or NV.

Files:

  • python/nemo_relay/README.md
**/*.{md,rst}

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/assets/nvidia-style-guide.md)

**/*.{md,rst}: Format commands, code elements, expressions, package names, file names, and paths as inline code.
Use descriptive link text. Avoid raw URLs and weak anchors such as "here" or "read more."
Use title case consistently for technical documentation headings.
Introduce code blocks, lists, tables, and images with complete sentences.
Write procedures as imperative steps. Keep steps parallel and split long procedures into smaller tasks.
Prefer active voice, present tense, short sentences, contractions, and plain English.
Use can for possibility and reserve may for permission.
Use after for temporal relationships instead of once.
Prefer refer to over see when the wording points readers to another resource.
Avoid culture-specific idioms, unnecessary Latinisms, jokes, and marketing exaggeration in technical docs.
Spell out months in body text, avoid ordinal dates, and use clear time zones.
Spell out whole numbers from zero through nine unless they are technical values, parameters, versions, or UI values.
Use numerals for 10 or greater and include commas in thousands.
Do not add trademark symbols to learning-oriented docs unless the source, platform, or legal guidance explicitly requires them.

Files:

  • python/nemo_relay/README.md
{docs/**,README.md,CONTRIBUTING.md,**/*.md}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

Run docs link validation with just docs-linkcheck when links change

Files:

  • python/nemo_relay/README.md
{docs/**,README.md,**/Cargo.toml,**/package.json,**/*.md}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

Ensure renamed public surfaces are reflected consistently in manifests and docs for large or public-facing changes

Files:

  • python/nemo_relay/README.md
**/*.{html,md,mdx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Include SPDX license header in HTML and Markdown files using HTML comment syntax

Files:

  • python/nemo_relay/README.md
**/README.md

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Update relevant crate or package README when that surface changed

Files:

  • python/nemo_relay/README.md
🪛 Ruff (0.15.15)
python/nemo_relay/nat_exporter.py

[warning] 33-33: Avoid specifying long messages outside the exception class

(TRY003)

python/tests/test_nat_exporter.py

[warning] 41-41: Do not call getattr with a constant attribute value. It is not any safer than normal property access.

Replace getattr with attribute access

(B009)

🔇 Additional comments (7)
python/nemo_relay/nat_exporter.py (3)

23-33: LGTM!


49-68: LGTM!


71-76: LGTM!

python/nemo_relay/__init__.py (1)

27-28: LGTM!

Also applies to: 192-201, 431-464

python/nemo_relay/README.md (1)

188-210: LGTM!

python/tests/test_nat_exporter.py (2)

40-41: LGTM!


14-37: ⚡ Quick win

Confirm scope_category end discriminant — Canonical ScopeEvent JSON includes a scope_category field and emits "scope_category": "end" for the pop/end event (e.g., crates/core/tests/unit/types_tests.rs and crates/ffi/tests/integration/api_tests.rs assert "scope_category" == "end"), so assert lines[2]["scope_category"] == "end" matches the real serialization.

Comment thread crates/cli/src/alignment/mod.rs Outdated
Comment thread crates/cli/src/launcher.rs
Comment thread crates/cli/src/session.rs
Comment thread crates/cli/tests/coverage/session_tests.rs
Comment thread python/nemo_relay/nat_exporter.py Outdated
willkill07
willkill07 previously approved these changes Jun 3, 2026
Copy link
Copy Markdown
Member

@willkill07 willkill07 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The improvements are more representative than the added exporter. Could the PR title be updated accordingly?

Otherwise, my only feedback is related to the added exporter and what value it adds over the existing AtofExporter.

Conditionally approving.

Comment thread python/nemo_relay/nat_exporter.py Outdated
@github-actions github-actions Bot removed the lang:python PR changes/introduces Python code label Jun 3, 2026
@yczhang-nv yczhang-nv changed the title feat: Add NAT telemetry export support for Hermes Relay runs feat: Improve Hermes Relay hook injection and event alignment Jun 3, 2026
yczhang-nv and others added 9 commits June 2, 2026 21:09
Temporarily patch and restore Hermes hook configuration during transparent Relay runs so Hermes lifecycle events reach the local gateway without requiring a permanent user config change.

Correlate Hermes task and tool hook events back to the owning session, including fallback matching when provider tool IDs differ between pre and post hooks.

Add a Python NatTelemetryExporter that writes canonical ATOF JSONL for NAT ingestion, with README and tests.

Signed-off-by: Yuchen Zhang <yuchenz@nvidia.com>
#### Overview

Fixes a Hermes CLI hook-forward issue where sparse `pre_tool_call` payloads could create a separate task-id session and export a one-step ATIF trajectory containing only `gateway_shutdown`.

- [x] I confirm this contribution is my own work, or I have the right to submit it under this project's license.
- [x] I searched existing issues and open pull requests, and this does not duplicate existing work.

#### Details

Public Hermes releases can emit `pre_tool_call` hooks with a task id but without enough stable session/tool-call identity to pair the hook with the eventual `post_tool_call`. NeMo Relay previously treated the task id as a session id, opened a synthetic Hermes session, and later closed it during gateway shutdown.

This PR drops uncorrelatable Hermes `pre_tool_call` hooks unless they include both a real session identifier and a stable tool-call id. The `post_tool_call` hook still records the tool result and attaches it to the main trajectory.

Added regression coverage for the sparse `pre_tool_call` case and updated adapter coverage for correlatable Hermes tool payloads.

#### Where should the reviewer start?

Start with `crates/cli/src/adapters/hermes.rs`, especially the `pretoolcall` guard and `hermes_pre_tool_call_is_correlatable` helper. The key regression test is `hermes_uncorrelatable_pre_tool_call_does_not_create_shutdown_trajectory` in `crates/cli/tests/coverage/session_tests.rs`.

#### Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

- Relates to NVIDIA#176

## Summary by CodeRabbit

* **Improvements**
  * Hermes adapter now filters pre-tool-call events that lack valid session and tool call identifiers, reducing noise in the event stream.

* **Tests**
  * Added test coverage validating proper filtering of uncorrelatable Hermes payloads and session trajectory behavior.

[![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/NVIDIA/NeMo-Relay/pull/182?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

Authors:
  - Bryan Bednarski (https://github.com/bbednarski9)

Approvers:
  - Will Killian (https://github.com/willkill07)

URL: NVIDIA#182
Signed-off-by: Yuchen Zhang <yuchenz@nvidia.com>
#### Overview

Adds ATIF exporter de-duplication for overlapping LLM spans that represent the same physical provider request, such as a hook-observed span and a gateway-observed span.

- [x] I confirm this contribution is my own work, or I have the right to submit it under this project's license.
- [x] I searched existing issues and open pull requests, and this does not duplicate existing work.

#### Details

Some harnesses can emit multiple LLM spans for one underlying request. Without de-duplication, ATIF can contain repeated user/agent steps for a single model call.

This PR adds an exporter pre-pass that collects complete LLM start/end span candidates, detects overlapping duplicates under the same parent/model, suppresses the lower-fidelity span, and merges metrics from the suppressed span into the canonical step when needed.

It also adds tests for overlapping hook/gateway spans, preferring a higher-fidelity gateway span over a non-exact hook summary, and preserving sequential same-content LLM calls as separate steps.

#### Where should the reviewer start?

Start with `crates/core/src/observability/atif.rs`, specifically `build_llm_dedupe`, `same_physical_llm_request`, and `llm_event_fidelity_score`. The main tests are in `crates/core/tests/unit/atif_tests.rs` around `test_exporter_dedupes_overlapping_hook_and_gateway_llm_spans`.

#### Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

- Relates to NVIDIA#176

## Summary by CodeRabbit

* **New Features**
  * Enhanced ATIF exporter with improved LLM span deduplication and token-metric consolidation from multiple instrumentation sources.

* **Bug Fixes**
  * Fixed metric handling for overlapping LLM requests to prevent inaccurate data consolidation.

* **Tests**
  * Added comprehensive unit tests validating LLM span deduplication behavior across instrumentation scenarios.

[![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/NVIDIA/NeMo-Relay/pull/183?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

Authors:
  - Bryan Bednarski (https://github.com/bbednarski9)

Approvers:
  - Will Killian (https://github.com/willkill07)

URL: NVIDIA#183
Signed-off-by: Yuchen Zhang <yuchenz@nvidia.com>
#### Overview

Documentation cleanup that removes stale "NeMo Flow" / "NAT" / "NeMo Agent
Toolkit" naming in favor of the current project name (NeMo Relay), refreshes the
Quick Start landing page with per-binding navigation, and corrects a few stale
integration notes. Docs-only; no runtime or public API behavior changes.

- [x] I confirm this contribution is my own work, or I have the right to submit it under this project's license.
- [x] I searched existing issues and open pull requests, and this does not duplicate existing work.

#### Details

- Quick Start (`docs/getting-started/quick-start/index.mdx`): add a 3-column
  `CardGroup` linking to the Python, Node.js, and Rust Quick Start guides.
- Correct stale naming to "NeMo Relay":
  - `.github/CODE_OF_CONDUCT.md`: title is now "NVIDIA NeMo Relay Code of Conduct".
  - `README.md`, `docs/integrate-into-frameworks/about.mdx`,
    `docs/integrate-into-frameworks/non-serializable-data.mdx`,
    `docs/nemo-relay-cli/hermes.mdx`, `docs/resources/support-and-faqs.mdx`:
    replace leftover "NeMo Flow" references with "NeMo Relay".
  - `docs/about-nemo-relay/ecosystem.mdx`: rename the `Flow` node to `Relay` in
    the architecture mermaid diagram.
- `AGENTS.md`: refresh the public-API integration notes to list LangGraph and
  Deep Agents alongside LangChain, and point to `docs/supported-integrations/`
  and `python/tests/integrations/`.
- `integrations/coding-agents/README.md`: remove the stale `hermes/` native
  plugin bundle bullet.

#### Where should the reviewer start?

Start with the naming corrections, especially `.github/CODE_OF_CONDUCT.md` (it
was mistakenly set to "NeMo Flow") and the mermaid node rename in
`docs/about-nemo-relay/ecosystem.mdx`. Then review the new `CardGroup` in
`docs/getting-started/quick-start/index.mdx`. Validated with `just docs-linkcheck`
(0 errors; the only warning is the auth-gated redirects check being skipped).

#### Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

- Relates to: none

## Summary by CodeRabbit

* **New Features**
  * Added interactive quick-start cards for Python, Node.js, and Rust linking to language guides.

* **Documentation**
  * Broadened public integrations list to include LangGraph and Deep Agents.
  * Standardized product name/terminology from “Flow” to “Relay” across docs and diagrams.
  * Clarified framework boundary guidance and non-serializable data wording.
  * Updated Hermes agent guidance and support/FAQ wording.
  * Adjusted Hermes packaging guidance in integrations docs.

[![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/NVIDIA/NeMo-Relay/pull/188?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

Authors:
  - Zhongxuan (Daniel) Wang (https://github.com/zhongxuanwang-nv)

Approvers:
  - Will Killian (https://github.com/willkill07)

URL: NVIDIA#188
Signed-off-by: Yuchen Zhang <yuchenz@nvidia.com>
#### Overview

Register the adaptive plugin component before `nemo-relay doctor` validates plugin configuration, and make Cursor support limitations more explicit in the CLI docs.

- [x] I confirm this contribution is my own work, or I have the right to submit it under this project's license.
- [x] I searched existing issues and open pull requests, and this does not duplicate existing work.

#### Details

- Register the built-in adaptive component before doctor plugin validation so adaptive plugin configs no longer report `plugin.unknown_component`.
- Add a focused doctor regression test for adaptive validation.
- Mark Cursor support as partial/highly experimental in the CLI overview.
- Add prominent Cursor cautions for Fern and GitHub docs covering manual proxy/API-key setup, lack of automatic model routing, and subagent model behavior.

Validation:

- `cargo fmt --check`
- `cargo test -p nemo-relay-cli collect_observability_registers_adaptive_before_validation -- --nocapture`
- `just docs`
- Commit pre-checks: docs linkcheck, `cargo fmt`, `cargo clippy`, and `cargo check`

#### Where should the reviewer start?

Start with `crates/cli/src/doctor.rs` for the adaptive registration path, then `docs/nemo-relay-cli/cursor.mdx` for the Cursor support warning.

#### Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

- Relates to: none

## Summary by CodeRabbit

* **Documentation**
  * Clarified Cursor integration support as highly experimental with limitations; cannot automatically route model traffic through the gateway
  * Updated observability support matrix for Cursor from fully supported to partial (highly experimental)

* **New Features**
  * Enhanced doctor command to register adaptive components during observability health checks

[![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/NVIDIA/NeMo-Relay/pull/186?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

Authors:
  - Will Killian (https://github.com/willkill07)

Approvers:
  - Bryan Bednarski (https://github.com/bbednarski9)

URL: NVIDIA#186
Signed-off-by: Yuchen Zhang <yuchenz@nvidia.com>
#### Overview

Fixes the Hermes gateway session fallback and tightens ATIF LLM dedupe so complementary hook/gateway spans are only collapsed when they represent the same physical request.

- [x] I confirm this contribution is my own work, or I have the right to submit it under this project's license.
- [x] I searched existing issues and open pull requests, and this does not duplicate existing work.

#### Details

- Uses the OpenAI-compatible request body session_id as a gateway fallback when explicit session headers are absent.
- Keeps the existing explicit Claude/Codex session fallbacks ahead of the OpenAI body fallback.
- Requires complementary hook/gateway LLM spans to share a request signature or strong request correlation key before ATIF dedupes them.
- Adds regression coverage for gateway fallback selection and concurrent overlapping LLM spans that should remain distinct.

#### Where should the reviewer start?

Start with `crates/cli/src/alignment/mod.rs` for the gateway fallback behavior, then review `crates/core/src/observability/atif.rs` for the strengthened complementary hook/gateway dedupe guard. The focused regression tests are in `crates/cli/tests/coverage/alignment_tests.rs`, `crates/cli/tests/coverage/gateway_tests.rs`, and `crates/core/tests/unit/atif_tests.rs`.

#### Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

- Closes NVIDIA#176

## Summary by CodeRabbit

* **Bug Fixes**
  * Session ID resolution enhanced to properly support OpenAI-compatible API request formats, including additional fallback to request body identifiers
  * LLM span correlation and deduplication logic improved with request-level identifier matching, enabling more accurate observability tracking and better event correlation for request tracing

[![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/NVIDIA/NeMo-Relay/pull/189?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

Authors:
  - Bryan Bednarski (https://github.com/bbednarski9)

Approvers:
  - Will Killian (https://github.com/willkill07)

URL: NVIDIA#189
Signed-off-by: Yuchen Zhang <yuchenz@nvidia.com>
#### Overview

Update the 0.3 release documentation so the release notes, README, and integration support pages reflect the current 0.3 feature set and compatibility notes.

- [x] I confirm this contribution is my own work, or I have the right to submit it under this project's license.
- [x] I searched existing issues and open pull requests, and this does not duplicate existing work.

#### Details

- Expanded the 0.3 release notes with breaking changes, feature highlights, known issues, fixed issues, and links to the most relevant feature documentation.
- Removed the extra release-notes related-topics page and added a Fern redirect for the removed URL.
- Updated README and docs for ATIF v1.7, Node.js 24, non-blocking subscriber delivery, CLI harness support, OpenClaw partial security support, and current OpenClaw requirements.
- Cleaned stale rename and subscriber wording in support, performance, subscriber, and OpenClaw documentation.

#### Where should the reviewer start?

Start with `docs/about-nemo-relay/release-notes/index.mdx` and `docs/about-nemo-relay/release-notes/highlights.mdx` for the release note shape, then review `README.md` and `docs/supported-integrations/openclaw-plugin.mdx` for the feature-support reconciliation.

#### Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

- Relates to: none

## Summary by CodeRabbit

* **Documentation**
  * Updated release notes, highlights, and installation docs for NeMo Relay 0.3 (project rename, explicit Apache 2.0 license).
  * Node.js 24+ requirement, CLI installation instructions, support-matrix and known-issues expanded (agent harness details, OpenClaw notes, non-blocking native subscriber delivery requiring explicit flush).
  * Removed/redirected an obsolete related-topics page.

* **New Features**
  * Pre-tool guardrails via OpenClaw hook-backed telemetry; OpenClaw minimum version bumped.

[![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/NVIDIA/NeMo-Relay/pull/180?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

Authors:
  - Will Killian (https://github.com/willkill07)

Approvers:
  - Bryan Bednarski (https://github.com/bbednarski9)

URL: NVIDIA#180
Signed-off-by: Yuchen Zhang <yuchenz@nvidia.com>
#### Overview

Fix Fern release publishing so version snapshots are built from the selected release tag source docs instead of whatever `docs-website` already has in `fern/pages-dev`.

- [x] I confirm this contribution is my own work, or I have the right to submit it under this project's license.
- [x] I searched existing issues and open pull requests, and this does not duplicate existing work.

#### Details

- Check out the selected SemVer tag in `release-version-docs` for both tag pushes and manual dispatches.
- Generate ignored API reference pages from that tag before snapshotting so versioned docs include tag-accurate Python, Node.js, and Rust references.
- Make `sync_fern_docs_branch.py release-version` require `--source-root` and copy source docs directly into the versioned Fern pages, removing the stale `pages-dev` fallback.

Validation:

- `ruby -e 'require "yaml"; Dir[".github/workflows/*.{yml,yaml}"].each { |f| YAML.load_file(f) }; puts "yaml-ok"'`
- `uv run --no-sync python -m py_compile scripts/docs/sync_fern_docs_branch.py`
- Temporary `release-version --source-root ... --tag 0.3.0` snapshot check verified the detailed release highlights land in `fern/pages-v0.3.0`.
- `uv run pre-commit run --files .github/workflows/fern-docs.yml scripts/docs/sync_fern_docs_branch.py`
- `uv run pre-commit run --all-files`

#### Where should the reviewer start?

Start with `.github/workflows/fern-docs.yml`, specifically the `release-version-docs` job, then review `scripts/docs/sync_fern_docs_branch.py` for the simplified source-to-version snapshot path.

#### Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

- Relates to: none

## Summary by CodeRabbit

* **Chores**
  * Improved documentation release workflow: longer job timeout, added workspace env var, and checkout of the computed release tag.
  * Release docs now include generated API reference and build snapshots directly from the source docs using a configurable source-root.
  * Updated link/path handling for versioned docs and CLI now requires an explicit source-root for release snapshot creation.

[![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/NVIDIA/NeMo-Relay/pull/194?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

Authors:
  - Will Killian (https://github.com/willkill07)

Approvers:
  - Bryan Bednarski (https://github.com/bbednarski9)

URL: NVIDIA#194
Signed-off-by: Yuchen Zhang <yuchenz@nvidia.com>
#### Overview

Fix manual Fern release snapshot reruns for historical tags. The release job should read documentation from the selected tag, but it must use the current workflow helper script so older tags can be repaired after workflow fixes land.

- [x] I confirm this contribution is my own work, or I have the right to submit it under this project's license.
- [x] I searched existing issues and open pull requests, and this does not duplicate existing work.

#### Details

- Add a separate `workflow-checkout` in `release-version-docs` for current workflow tooling.
- Keep `source-checkout` pinned to the selected release tag so the docs content remains tag-sourced.
- Invoke `sync_fern_docs_branch.py` from `workflow-checkout`, fixing manual reruns such as `tag=0.3.0` where the tag contains an older helper that does not support `--source-root`.

Validation:

- Reproduced the failing shape from https://github.com/NVIDIA/NeMo-Relay/actions/runs/26668928588/job/78608028705: source checkout from `0.3.0`, helper script from current branch, `release-version --source-root ... --tag 0.3.0`.
- Verified the snapshot writes `fern/pages-v0.3.0` with the detailed release highlights.
- `ruby -e 'require "yaml"; Dir[".github/workflows/*.{yml,yaml}"].each { |f| YAML.load_file(f) }; puts "yaml-ok"'`
- `uv run pre-commit run --files .github/workflows/fern-docs.yml`
- `uv run pre-commit run --all-files`

#### Where should the reviewer start?

Start with `.github/workflows/fern-docs.yml` in the `release-version-docs` job. The important detail is that workflow tools and release docs content now come from different checkouts.

#### Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

- Relates to: https://github.com/NVIDIA/NeMo-Relay/actions/runs/26668928588/job/78608028705

## Summary by CodeRabbit

* **Chores**
  * Updated internal CI/CD workflow configuration for documentation synchronization processes.

**Note:** This release contains no end-user visible changes. Updates are limited to workflow infrastructure improvements.

[![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/NVIDIA/NeMo-Relay/pull/195?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

Signed-off-by: Will Killian <wkillian@nvidia.com>
Signed-off-by: Yuchen Zhang <yuchenz@nvidia.com>
zhongxuanwang-nv and others added 10 commits June 2, 2026 21:09
* docs: fix Node.js quickstart scope handle and LLM request usage

The Node.js quick start and the nemo-relay-node package README passed the
informational handle from `withScope` straight into `event`, `toolCallExecute`,
and `llmCallExecute`. Those APIs expect either `null` (the current scope) or a
real `ScopeHandle`, so the plain handle object failed with "Failed to recover
`ScopeHandle` type from napi value" on the first call. The rejected promise then
skipped `deregisterSubscriber`, leaving the subscriber's ThreadsafeFunction
ref'd and hanging the process.

The LLM example also built the request with `new LlmRequest(...)`, but
`llmCallExecute` takes a plain `{ headers, content }` JSON object.

Update both examples to pass `null` for the active scope and a plain request
object, matching the behavior covered by the Node binding tests.

Signed-off-by: Zhongxuan Wang <daniewang@nvidia.com>

* fix: pass a real ScopeHandle to Node withScope callbacks

Node's withScope handed its callback a plain JSON object instead of a
real ScopeHandle, so passing it back into event, toolCallExecute, or
llmCallExecute threw "Failed to recover ScopeHandle type from napi
value" on the first call, and the skipped deregisterSubscriber left the
process hung. Materialize a real ScopeHandle on the JS thread inside the
threadsafe-function call (via a new Arg0Builder path in PromiseAwareFn)
so the callback receives a usable handle, matching the Rust, Python, and
WebAssembly bindings.

Keep the Node quick start passing the handle and send the LLM request as
a plain { headers, content } object, and add a Node test that reuses the
callback handle across event, pushScope, toolCallExecute, and
llmCallExecute.

Signed-off-by: Zhongxuan Wang <daniewang@nvidia.com>

* docs: keep Node quick start using LlmRequest

LlmRequest delivers headers and content to llmCallExecute correctly, so
the quick start needs no plain-object request. Restore the quick start to
its original LlmRequest usage; the scope-handle fix alone makes the
example run end to end.

Signed-off-by: Zhongxuan Wang <daniewang@nvidia.com>

---------

Signed-off-by: Zhongxuan Wang <daniewang@nvidia.com>
Signed-off-by: Yuchen Zhang <yuchenz@nvidia.com>
…VIDIA#201)

#### Overview

The package READMEs' "Getting Started" examples register a subscriber, emit events, then deregister without calling flush. Native subscriber delivery is non-blocking, so an example that exits right after deregistering can lose queued events. This updates all four package READMEs to flush before deregister, matching the quickstarts (which already flush).

- [x] I confirm this contribution is my own work, or I have the right to submit it under this project's license.
- [x] I searched existing issues and open pull requests, and this does not duplicate existing work.

#### Details

**Functional fixes** (verified by running — the example dropped events without flush):
- `python/nemo_relay/README.md`: printed nothing, or intermittently a single partial line; the `to_dict()` / `to_json()` callback never ran. Adds `nemo_relay.subscribers.flush()` before each `deregister(...)`.
- `go/nemo_relay/README.md`: flaky — dropped the trailing tool-end and scope-end events on exit (2 of 3 runs partial). Adds `defer nemo.FlushSubscribers()`, ordered to run after the deferred `scope.Pop` emits the scope-end event and before the deferred deregister.

**Parity** (not currently broken; updated so every README models the same pattern):
- `crates/node/README.md`: Node keeps the event loop alive via the ref'd ThreadsafeFunction, so it already delivers; adds `flushSubscribers()` for consistency.
- `crates/wasm/README.md`: WASM's `flushSubscribers()` is a single-threaded no-op (tested as such); adds it for consistency.

#### Where should the reviewer start?

The four README diffs — each adds a flush before `deregister(...)` (Node/WASM also import `flushSubscribers`). Verified by running each example with the flush added: events deliver deterministically (Go 5/5, Python and Node 3/3 across repeated runs). WASM's `flushSubscribers` is an exported no-op (`crates/wasm/src/api/mod.rs`; covered by `crates/wasm/tests-js/scope_tests.mjs`).

Note: the `crates/node/README.md` example also depends on the Node binding change in NVIDIA#196 (pass a real `ScopeHandle` to `withScope` callbacks). That is orthogonal to this flush change — NVIDIA#196 is code-only and does not touch the README.

#### Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

- Relates to: NVIDIA#196 (Node `withScope` ScopeHandle; orthogonal, touches the same Node README example)

## Summary by CodeRabbit

* **Documentation**
  * Updated Getting Started examples across Python, Node.js, WebAssembly, and Go language bindings to reflect the proper sequence for flushing subscribers before deregistration.

Authors:
  - Zhongxuan (Daniel) Wang (https://github.com/zhongxuanwang-nv)

Approvers:
  - Will Killian (https://github.com/willkill07)

URL: NVIDIA#201
Signed-off-by: Yuchen Zhang <yuchenz@nvidia.com>
…VIDIA#200)

#### Overview

Reorder the "Register Plugin Behavior" guide so a plugin kind is registered before it is activated, and document the `validate()`-warns / `initialize()`-raises behavior. Run on its own, the **Activation APIs** example previously failed with `RuntimeError: ... 'header-plugin' is not registered`, because the kind it activates is only registered later on the page in the **Header Plugin Example**.

- [x] I confirm this contribution is my own work, or I have the right to submit it under this project's license.
- [x] I searched existing issues and open pull requests, and this does not duplicate existing work.

#### Details

- `docs/build-plugins/register-behavior.mdx`: move the **Header Plugin Example** (which calls `plugin.register("header-plugin", ...)`) above the **Activation APIs** lifecycle so the page reads and runs top to bottom. Drop the now-redundant "Register the plugin kind" item from the activation list, and note that an unregistered kind is reported by `validate()` only as a warning under the default `unknown_component="warn"` policy, while `initialize()` raises. The reorder moves whole sections only; no code-block content changed (verified by an order-independent line diff).
- `docs/build-plugins/validate-configuration.mdx`: add the same clarification to **Validate Before Initialization** — the error-only `has_errors` check does not catch an unknown/unregistered kind; register kinds first, or set `unknown_component="error"`.

Heads-up: this edits the same `register-behavior.mdx` **Activation APIs** section as NVIDIA#199 (which adds `plugin.layer(...)` to that example but does not change the registration ordering). The two overlap, so whichever lands second will need a small conflict resolution.

#### Where should the reviewer start?

`docs/build-plugins/register-behavior.mdx` — confirm the section reorder preserved every code block (an order-independent line diff shows only the numbered list and intro changed) and that the Activation APIs example now follows the Header Plugin Example that registers the kind.

Verified locally: running the Header Plugin Example block and then the Activation APIs block in one process registers `header-plugin`, after which `validate()` / `initialize()` / `clear()` succeed with empty diagnostics and no `RuntimeError`. Targeted `pre-commit` (trailing whitespace, end-of-files, docs markdown linkcheck) passes on both files.

#### Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

- Relates to: NVIDIA#199 (edits the same `register-behavior.mdx` section)

## Summary by CodeRabbit

* **Documentation**
  * Clarified that plugin kinds must be registered before calling initialize()
  * Explained that unknown/unregistered component kinds are reported as warnings by default during validation and may still cause errors at initialization unless policy is changed
  * Reorganized and updated the activation guide and language across Python/Node/Rust examples for clearer ordering and guidance

Authors:
  - Zhongxuan (Daniel) Wang (https://github.com/zhongxuanwang-nv)

Approvers:
  - Will Killian (https://github.com/willkill07)

URL: NVIDIA#200
Signed-off-by: Yuchen Zhang <yuchenz@nvidia.com>
#### Overview

This PR covers the OpenClaw-focused ATIF, ATOF, and OpenInference consistency work against the shared observability contract.

- [x] I confirm this contribution is my own work, or I have the right to submit it under this project's license.
- [x] I searched existing issues and open pull requests, and this does not duplicate existing work.

#### Details

- Normalizes Anthropic `/v1/messages` content blocks so ATIF promotes `tool_use` blocks into tool calls while keeping readable assistant text and cache usage metrics.
- Improves OpenInference display and usage extraction for Anthropic messages, OpenAI Responses, and Chat Completions cache-token shapes.
- Preserves raw ATOF JSONL lifecycle payloads for `/v1/messages`, `/v1/responses`, and `/v1/chat/completions`.
- Extends OpenClaw hook replay usage normalization for provider-specific token and cache fields.

#### Where should the reviewer start?

Start with `crates/core/src/observability/atif.rs` and `crates/core/src/observability/openinference.rs`, then review the new fixture coverage in the matching Rust tests and `integrations/openclaw/test/llm-replay.test.ts`.

#### Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

- Relates to OpenClaw observability consistency work.

#### Validation

- `cargo test -p nemo-relay`
- `cargo test -p nemo-relay atif`
- `cargo test -p nemo-relay openinference`
- `cargo test -p nemo-relay atof`
- `cargo fmt --all --check`
- `env PYO3_PYTHON=/Users/mnajafian/projects/NeMo-Relay/.venv/bin/python cargo clippy --workspace --all-targets -- -D warnings`
- `just test-rust`
- `npm test --workspace=nemo-relay-openclaw`
- `npm run typecheck --workspace=nemo-relay-openclaw`
- `npm run test:live --workspace=nemo-relay-openclaw`
- `npm run pack:check --workspace=nemo-relay-openclaw`
- `uv run pre-commit run --files crates/core/src/observability/atif.rs crates/core/src/observability/openinference.rs crates/core/tests/unit/atif_tests.rs crates/core/tests/unit/observability/atof_tests.rs crates/core/tests/unit/observability/openinference_tests.rs integrations/openclaw/src/hook-replay/llm.ts integrations/openclaw/test/llm-replay.test.ts`

#### Additional local validation

- Ran OpenClaw + NeMo Relay examples locally with NVIDIA Inference and Tavily-backed tool use across diverse prompts.
- Inspected generated ATIF, ATOF, OpenInference, and Phoenix traces for multi-step agent runs.

#### Follow-up Work
I’ll keep the remaining items as follow-ups rather than expanding this PR: flattened OpenInference message/tool attrs, OpenClaw ATOF provenance/fidelity audit, and nested-subagent fixture coverage if we have a real OpenClaw scenario.

## Summary by CodeRabbit

* **New Features**
  * Added support for Anthropic Claude message workflows including tool-use and function calling.
  * Extended OpenAI integration with response format support and cached token tracking.
  * Improved multi-provider LLM observability and message parsing.

* **Tests**
  * Added test coverage for Anthropic and OpenAI LLM interaction scenarios.

Authors:
  - https://github.com/mnajafian-nv

Approvers:
  - Will Killian (https://github.com/willkill07)

URL: NVIDIA#203
Signed-off-by: Yuchen Zhang <yuchenz@nvidia.com>
Signed-off-by: Will Killian <wkillian@nvidia.com>
Signed-off-by: Yuchen Zhang <yuchenz@nvidia.com>
Signed-off-by: Yuchen Zhang <yuchenz@nvidia.com>
Signed-off-by: Yuchen Zhang <yuchenz@nvidia.com>
Signed-off-by: Yuchen Zhang <yuchenz@nvidia.com>
Signed-off-by: Yuchen Zhang <yuchenz@nvidia.com>
Signed-off-by: Yuchen Zhang <yuchenz@nvidia.com>
@yczhang-nv yczhang-nv force-pushed the codex/nat-294-relay-telemetry branch from 73d3761 to b1df460 Compare June 3, 2026 04:10
@github-actions github-actions Bot added size:XL PR is extra large lang:js PR changes/introduces Javascript/Typescript code lang:python PR changes/introduces Python code and removed size:L PR is large labels Jun 3, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/fern-docs.yml:
- Around line 377-378: Add an explanatory comment above the job-level
permissions block so reviewers know why contents: write is required;
specifically, update the permissions block that contains "permissions:" and
"contents: write" to include a short comment explaining the reason (e.g., token
usage for pushing docs or updating releases) and any security scope constraints.

In `@crates/cli/src/session.rs`:
- Around line 1605-1633: The matcher currently restricts to root-owned tools
when owner resolution returns None, causing a root-biased fallback; modify
matching_active_tool_key so the owner check only applies when owner_subagent_id
is Some: in matching_active_tool_key (used by remove_tool_handle_for_event)
change the filter closure to require active.owner_subagent_id ==
owner_subagent_id only when owner_subagent_id.is_some(), otherwise ignore
owner_subagent_id and match on name and arguments across all owners, then keep
the existing uniqueness check (matches.len() == 1) to return a single global
match.

In `@crates/cli/tests/coverage/adapters_tests.rs`:
- Around line 312-320: The test drops_uncorrelatable_hermes_pre_tool_call
currently omits both session_id and tool_call_id so it can pass when only one
correlatability gate exists; change the test input passed to hermes::adapt to
include a valid tool_call_id (e.g. "tool_call_id": "toolcall-1") while keeping
session_id omitted so the test isolates the missing-session correlatability
check and still expects the adapter to drop the event.

In `@crates/core/src/observability/atif.rs`:
- Around line 1327-1329: The current llm_response_signature function reduces the
signature to only the extracted message text (via extract_llm_response_message),
which causes different tool-call/raw-response structures — including tool-only
Anthropic responses that become empty strings — to be treated as identical;
update llm_response_signature to include the full promoted tool-call/raw
response structure (or a stable request correlation key when available) in the
returned signature so that tool invocations and raw response metadata are part
of the uniqueness check used by same_physical_llm_request; locate
llm_response_signature and include either the promoted tool call fields or a
correlation id from the response (and apply the same change to the other
occurrences noted around the commented ranges) so signature-only suppression no
longer collapses distinct calls.

In `@integrations/openclaw/src/hook-replay/llm.ts`:
- Around line 1197-1202: The cacheRead normalization currently misses top-level
fields named cached_tokens (and camelCase cachedTokens), causing cache-read
metrics to be dropped; update the cacheRead resolution in the llm replay
normalization to check for numberField(usage, 'cached_tokens') and
numberField(usage, 'cachedTokens') (prior to or alongside the other fallbacks)
so that usage objects already using the canonical NeMo Relay name or camelCase
variant are preserved; locate the cacheRead variable assignment and add those
checks to the existing chain that uses numberField(...) and
nestedNumberField(...).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 862a7d2a-a412-4c2a-8780-757a946da69f

📥 Commits

Reviewing files that changed from the base of the PR and between d53e2ca and b1df460.

📒 Files selected for processing (53)
  • .github/CODE_OF_CONDUCT.md
  • .github/nightly-alpha-branches.yaml
  • .github/workflows/fern-docs.yml
  • AGENTS.md
  • README.md
  • crates/cli/src/adapters/hermes.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/src/doctor.rs
  • crates/cli/src/launcher.rs
  • crates/cli/src/session.rs
  • crates/cli/tests/coverage/adapters_tests.rs
  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/tests/coverage/doctor_tests.rs
  • crates/cli/tests/coverage/gateway_tests.rs
  • crates/cli/tests/coverage/launcher_tests.rs
  • crates/cli/tests/coverage/session_tests.rs
  • crates/core/src/observability/atif.rs
  • crates/core/src/observability/openinference.rs
  • crates/core/tests/unit/atif_tests.rs
  • crates/core/tests/unit/observability/atof_tests.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/node/README.md
  • crates/node/src/api/mod.rs
  • crates/node/src/promise_call.rs
  • crates/node/tests/scope_tests.mjs
  • crates/wasm/README.md
  • docs/about-nemo-relay/concepts/subscribers.mdx
  • docs/about-nemo-relay/ecosystem.mdx
  • docs/about-nemo-relay/release-notes/highlights.mdx
  • docs/about-nemo-relay/release-notes/index.mdx
  • docs/about-nemo-relay/release-notes/known-issues.mdx
  • docs/about-nemo-relay/release-notes/related-topics.mdx
  • docs/build-plugins/register-behavior.mdx
  • docs/build-plugins/validate-configuration.mdx
  • docs/getting-started/quick-start/index.mdx
  • docs/integrate-into-frameworks/about.mdx
  • docs/integrate-into-frameworks/non-serializable-data.mdx
  • docs/nemo-relay-cli/about.mdx
  • docs/nemo-relay-cli/cursor.mdx
  • docs/nemo-relay-cli/hermes.mdx
  • docs/reference/performance.mdx
  • docs/resources/support-and-faqs.mdx
  • docs/supported-integrations/about.mdx
  • docs/supported-integrations/openclaw-plugin.mdx
  • fern/docs.yml
  • go/nemo_relay/README.md
  • integrations/coding-agents/README.md
  • integrations/coding-agents/cursor/README.md
  • integrations/openclaw/README.md
  • integrations/openclaw/src/hook-replay/llm.ts
  • integrations/openclaw/test/llm-replay.test.ts
  • python/nemo_relay/README.md
  • scripts/docs/sync_fern_docs_branch.py
💤 Files with no reviewable changes (3)
  • docs/about-nemo-relay/release-notes/related-topics.mdx
  • .github/nightly-alpha-branches.yaml
  • integrations/coding-agents/README.md
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Check / Run
  • GitHub Check: Preview docs
🧰 Additional context used
📓 Path-based instructions (54)
{docs/**,README.md,CONTRIBUTING.md}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

{docs/**,README.md,CONTRIBUTING.md}: For docs-only changes, run targeted checks only if commands, package names, or examples changed. Use just docs for docs-site builds and just docs-linkcheck when links changed
Run docs site build with just docs

Files:

  • docs/build-plugins/validate-configuration.mdx
  • docs/nemo-relay-cli/about.mdx
  • docs/supported-integrations/about.mdx
  • docs/nemo-relay-cli/hermes.mdx
  • docs/about-nemo-relay/ecosystem.mdx
  • docs/getting-started/quick-start/index.mdx
  • docs/reference/performance.mdx
  • docs/integrate-into-frameworks/non-serializable-data.mdx
  • docs/about-nemo-relay/release-notes/highlights.mdx
  • docs/integrate-into-frameworks/about.mdx
  • docs/resources/support-and-faqs.mdx
  • docs/nemo-relay-cli/cursor.mdx
  • docs/about-nemo-relay/concepts/subscribers.mdx
  • README.md
  • docs/supported-integrations/openclaw-plugin.mdx
  • docs/about-nemo-relay/release-notes/known-issues.mdx
  • docs/build-plugins/register-behavior.mdx
  • docs/about-nemo-relay/release-notes/index.mdx
{docs/**,README.md,CONTRIBUTING.md,**/*.md}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

Run docs link validation with just docs-linkcheck when links change

Files:

  • docs/build-plugins/validate-configuration.mdx
  • docs/nemo-relay-cli/about.mdx
  • python/nemo_relay/README.md
  • go/nemo_relay/README.md
  • docs/supported-integrations/about.mdx
  • docs/nemo-relay-cli/hermes.mdx
  • integrations/coding-agents/cursor/README.md
  • docs/about-nemo-relay/ecosystem.mdx
  • docs/getting-started/quick-start/index.mdx
  • docs/reference/performance.mdx
  • docs/integrate-into-frameworks/non-serializable-data.mdx
  • crates/node/README.md
  • crates/wasm/README.md
  • docs/about-nemo-relay/release-notes/highlights.mdx
  • docs/integrate-into-frameworks/about.mdx
  • docs/resources/support-and-faqs.mdx
  • docs/nemo-relay-cli/cursor.mdx
  • docs/about-nemo-relay/concepts/subscribers.mdx
  • integrations/openclaw/README.md
  • AGENTS.md
  • README.md
  • docs/supported-integrations/openclaw-plugin.mdx
  • docs/about-nemo-relay/release-notes/known-issues.mdx
  • docs/build-plugins/register-behavior.mdx
  • docs/about-nemo-relay/release-notes/index.mdx
{docs/**,README.md}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

Verify README and docs entry points still match current package names and paths for large or public-facing changes

Files:

  • docs/build-plugins/validate-configuration.mdx
  • docs/nemo-relay-cli/about.mdx
  • docs/supported-integrations/about.mdx
  • docs/nemo-relay-cli/hermes.mdx
  • docs/about-nemo-relay/ecosystem.mdx
  • docs/getting-started/quick-start/index.mdx
  • docs/reference/performance.mdx
  • docs/integrate-into-frameworks/non-serializable-data.mdx
  • docs/about-nemo-relay/release-notes/highlights.mdx
  • docs/integrate-into-frameworks/about.mdx
  • docs/resources/support-and-faqs.mdx
  • docs/nemo-relay-cli/cursor.mdx
  • docs/about-nemo-relay/concepts/subscribers.mdx
  • README.md
  • docs/supported-integrations/openclaw-plugin.mdx
  • docs/about-nemo-relay/release-notes/known-issues.mdx
  • docs/build-plugins/register-behavior.mdx
  • docs/about-nemo-relay/release-notes/index.mdx
{docs/**,examples/**,README.md}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

Verify examples still run with documented commands for large or public-facing changes

Files:

  • docs/build-plugins/validate-configuration.mdx
  • docs/nemo-relay-cli/about.mdx
  • docs/supported-integrations/about.mdx
  • docs/nemo-relay-cli/hermes.mdx
  • docs/about-nemo-relay/ecosystem.mdx
  • docs/getting-started/quick-start/index.mdx
  • docs/reference/performance.mdx
  • docs/integrate-into-frameworks/non-serializable-data.mdx
  • docs/about-nemo-relay/release-notes/highlights.mdx
  • docs/integrate-into-frameworks/about.mdx
  • docs/resources/support-and-faqs.mdx
  • docs/nemo-relay-cli/cursor.mdx
  • docs/about-nemo-relay/concepts/subscribers.mdx
  • README.md
  • docs/supported-integrations/openclaw-plugin.mdx
  • docs/about-nemo-relay/release-notes/known-issues.mdx
  • docs/build-plugins/register-behavior.mdx
  • docs/about-nemo-relay/release-notes/index.mdx
{docs/**,README.md,**/Cargo.toml,**/package.json,**/*.md}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

Ensure renamed public surfaces are reflected consistently in manifests and docs for large or public-facing changes

Files:

  • docs/build-plugins/validate-configuration.mdx
  • docs/nemo-relay-cli/about.mdx
  • python/nemo_relay/README.md
  • go/nemo_relay/README.md
  • docs/supported-integrations/about.mdx
  • docs/nemo-relay-cli/hermes.mdx
  • integrations/coding-agents/cursor/README.md
  • docs/about-nemo-relay/ecosystem.mdx
  • docs/getting-started/quick-start/index.mdx
  • docs/reference/performance.mdx
  • docs/integrate-into-frameworks/non-serializable-data.mdx
  • crates/node/README.md
  • crates/wasm/README.md
  • docs/about-nemo-relay/release-notes/highlights.mdx
  • docs/integrate-into-frameworks/about.mdx
  • docs/resources/support-and-faqs.mdx
  • docs/nemo-relay-cli/cursor.mdx
  • docs/about-nemo-relay/concepts/subscribers.mdx
  • integrations/openclaw/README.md
  • AGENTS.md
  • README.md
  • docs/supported-integrations/openclaw-plugin.mdx
  • docs/about-nemo-relay/release-notes/known-issues.mdx
  • docs/build-plugins/register-behavior.mdx
  • docs/about-nemo-relay/release-notes/index.mdx
**/*.{md,mdx,py,sh,yaml,yml,toml,json}

📄 CodeRabbit inference engine (.agents/skills/contribute-docs/SKILL.md)

Keep package names, repo references, and build commands current

Files:

  • docs/build-plugins/validate-configuration.mdx
  • docs/nemo-relay-cli/about.mdx
  • python/nemo_relay/README.md
  • go/nemo_relay/README.md
  • docs/supported-integrations/about.mdx
  • docs/nemo-relay-cli/hermes.mdx
  • integrations/coding-agents/cursor/README.md
  • docs/about-nemo-relay/ecosystem.mdx
  • docs/getting-started/quick-start/index.mdx
  • docs/reference/performance.mdx
  • docs/integrate-into-frameworks/non-serializable-data.mdx
  • crates/node/README.md
  • crates/wasm/README.md
  • docs/about-nemo-relay/release-notes/highlights.mdx
  • docs/integrate-into-frameworks/about.mdx
  • docs/resources/support-and-faqs.mdx
  • docs/nemo-relay-cli/cursor.mdx
  • docs/about-nemo-relay/concepts/subscribers.mdx
  • integrations/openclaw/README.md
  • AGENTS.md
  • fern/docs.yml
  • README.md
  • docs/supported-integrations/openclaw-plugin.mdx
  • docs/about-nemo-relay/release-notes/known-issues.mdx
  • docs/build-plugins/register-behavior.mdx
  • scripts/docs/sync_fern_docs_branch.py
  • docs/about-nemo-relay/release-notes/index.mdx
**/*.mdx

📄 CodeRabbit inference engine (.agents/skills/contribute-docs/SKILL.md)

In MDX files, top-of-file comments must use JSX comment delimiters: {/* to open and */} to close. Do not use HTML comments for MDX SPDX headers.

MDX top-of-file SPDX comments must use {/* ... */} delimiters instead of HTML comment delimiters (Must-Fix)

Files:

  • docs/build-plugins/validate-configuration.mdx
  • docs/nemo-relay-cli/about.mdx
  • docs/supported-integrations/about.mdx
  • docs/nemo-relay-cli/hermes.mdx
  • docs/about-nemo-relay/ecosystem.mdx
  • docs/getting-started/quick-start/index.mdx
  • docs/reference/performance.mdx
  • docs/integrate-into-frameworks/non-serializable-data.mdx
  • docs/about-nemo-relay/release-notes/highlights.mdx
  • docs/integrate-into-frameworks/about.mdx
  • docs/resources/support-and-faqs.mdx
  • docs/nemo-relay-cli/cursor.mdx
  • docs/about-nemo-relay/concepts/subscribers.mdx
  • docs/supported-integrations/openclaw-plugin.mdx
  • docs/about-nemo-relay/release-notes/known-issues.mdx
  • docs/build-plugins/register-behavior.mdx
  • docs/about-nemo-relay/release-notes/index.mdx
**/*.{html,md,mdx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Include SPDX license header in HTML and Markdown files using HTML comment syntax

Files:

  • docs/build-plugins/validate-configuration.mdx
  • docs/nemo-relay-cli/about.mdx
  • python/nemo_relay/README.md
  • go/nemo_relay/README.md
  • docs/supported-integrations/about.mdx
  • docs/nemo-relay-cli/hermes.mdx
  • integrations/coding-agents/cursor/README.md
  • docs/about-nemo-relay/ecosystem.mdx
  • docs/getting-started/quick-start/index.mdx
  • docs/reference/performance.mdx
  • docs/integrate-into-frameworks/non-serializable-data.mdx
  • crates/node/README.md
  • crates/wasm/README.md
  • docs/about-nemo-relay/release-notes/highlights.mdx
  • docs/integrate-into-frameworks/about.mdx
  • docs/resources/support-and-faqs.mdx
  • docs/nemo-relay-cli/cursor.mdx
  • docs/about-nemo-relay/concepts/subscribers.mdx
  • integrations/openclaw/README.md
  • AGENTS.md
  • README.md
  • docs/supported-integrations/openclaw-plugin.mdx
  • docs/about-nemo-relay/release-notes/known-issues.mdx
  • docs/build-plugins/register-behavior.mdx
  • docs/about-nemo-relay/release-notes/index.mdx
docs/**/*.{md,mdx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Update embedded documentation snippets, patch docs, and binding-support notes if examples or supported bindings changed

Files:

  • docs/build-plugins/validate-configuration.mdx
  • docs/nemo-relay-cli/about.mdx
  • docs/supported-integrations/about.mdx
  • docs/nemo-relay-cli/hermes.mdx
  • docs/about-nemo-relay/ecosystem.mdx
  • docs/getting-started/quick-start/index.mdx
  • docs/reference/performance.mdx
  • docs/integrate-into-frameworks/non-serializable-data.mdx
  • docs/about-nemo-relay/release-notes/highlights.mdx
  • docs/integrate-into-frameworks/about.mdx
  • docs/resources/support-and-faqs.mdx
  • docs/nemo-relay-cli/cursor.mdx
  • docs/about-nemo-relay/concepts/subscribers.mdx
  • docs/supported-integrations/openclaw-plugin.mdx
  • docs/about-nemo-relay/release-notes/known-issues.mdx
  • docs/build-plugins/register-behavior.mdx
  • docs/about-nemo-relay/release-notes/index.mdx
docs/**

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Run just docs or ./scripts/build-docs.sh html to regenerate ignored Fern API reference pages before validation for documentation site changes

Files:

  • docs/build-plugins/validate-configuration.mdx
  • docs/nemo-relay-cli/about.mdx
  • docs/supported-integrations/about.mdx
  • docs/nemo-relay-cli/hermes.mdx
  • docs/about-nemo-relay/ecosystem.mdx
  • docs/getting-started/quick-start/index.mdx
  • docs/reference/performance.mdx
  • docs/integrate-into-frameworks/non-serializable-data.mdx
  • docs/about-nemo-relay/release-notes/highlights.mdx
  • docs/integrate-into-frameworks/about.mdx
  • docs/resources/support-and-faqs.mdx
  • docs/nemo-relay-cli/cursor.mdx
  • docs/about-nemo-relay/concepts/subscribers.mdx
  • docs/supported-integrations/openclaw-plugin.mdx
  • docs/about-nemo-relay/release-notes/known-issues.mdx
  • docs/build-plugins/register-behavior.mdx
  • docs/about-nemo-relay/release-notes/index.mdx
{docs/**,README.md,CONTRIBUTING.md,RELEASING.md,SECURITY.md}

⚙️ CodeRabbit configuration file

{docs/**,README.md,CONTRIBUTING.md,RELEASING.md,SECURITY.md}: Review documentation for technical accuracy against the current API, command correctness, and consistency across language bindings.
Flag stale examples, missing SPDX headers where required, and instructions that no longer match CI or pre-commit behavior.

Files:

  • docs/build-plugins/validate-configuration.mdx
  • docs/nemo-relay-cli/about.mdx
  • docs/supported-integrations/about.mdx
  • docs/nemo-relay-cli/hermes.mdx
  • docs/about-nemo-relay/ecosystem.mdx
  • docs/getting-started/quick-start/index.mdx
  • docs/reference/performance.mdx
  • docs/integrate-into-frameworks/non-serializable-data.mdx
  • docs/about-nemo-relay/release-notes/highlights.mdx
  • docs/integrate-into-frameworks/about.mdx
  • docs/resources/support-and-faqs.mdx
  • docs/nemo-relay-cli/cursor.mdx
  • docs/about-nemo-relay/concepts/subscribers.mdx
  • README.md
  • docs/supported-integrations/openclaw-plugin.mdx
  • docs/about-nemo-relay/release-notes/known-issues.mdx
  • docs/build-plugins/register-behavior.mdx
  • docs/about-nemo-relay/release-notes/index.mdx
**/*.{md,rst,html,txt}

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/assets/nvidia-style-brand-terminology.md)

**/*.{md,rst,html,txt}: Always spell NVIDIA in all caps. Do not use Nvidia, nvidia, nVidia, nVIDIA, or NV.
Use an NVIDIA before a noun because the name starts with an 'en' sound.
Do not add a registered trademark symbol after NVIDIA when referring to the company.
Use trademark symbols with product names only when the document type or legal guidance requires them.
Verify official capitalization, spacing, and hyphenation for product names.
Precede NVIDIA product names with NVIDIA on first mention when it is natural and accurate.
Do not rewrite product names for grammar or title-case rules.
Preserve third-party product names according to the owner's spelling.
Include the company name and full model qualifier on first use when it helps identify the model.
Preserve the official capitalization and punctuation of model names.
Use shorter family names only after the full name is established.
Spell out a term on first use and put the acronym in parentheses unless the acronym is widely understood by the intended audience.
Use the acronym on later mentions after it has been defined.
For long documents, reintroduce the full term if readers might lose context.
Form plurals of acronyms with s, not an apostrophe, such as GPUs.
In headings, common acronyms can remain abbreviated. Spell out the term in the first or second sentence of the body.
Common terms such as CPU, GPU, PC, API, and UI usually do not need to be spelled out for developer audiences.

Files:

  • python/nemo_relay/README.md
  • go/nemo_relay/README.md
  • integrations/coding-agents/cursor/README.md
  • crates/node/README.md
  • crates/wasm/README.md
  • integrations/openclaw/README.md
  • AGENTS.md
  • README.md
**/*.{md,rst,html}

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/assets/nvidia-style-brand-terminology.md)

Link the first mention of a product name when the destination helps the reader.

Files:

  • python/nemo_relay/README.md
  • go/nemo_relay/README.md
  • integrations/coding-agents/cursor/README.md
  • crates/node/README.md
  • crates/wasm/README.md
  • integrations/openclaw/README.md
  • AGENTS.md
  • README.md
**/*.md

📄 CodeRabbit inference engine (.agents/skills/contribute-integration/SKILL.md)

Documentation must be updated if activation or usage changed

**/*.md: Use title case consistently in technical documentation headings
Avoid quotation marks, ampersands, and exclamation marks in headings
Keep product, event, research, and whitepaper names in their official title case
Use title case for table headers
Do not force social-media sentence case into technical docs
Format code elements, commands, parameters, package names, and expressions in monospace
Format directories, file names, and paths in monospace using backticks
Use angle brackets inside monospace for variables inside paths, such as /home/<username>/.login
Format error messages and strings in quotation marks, keeping literal code strings in code formatting when clearer
Format UI buttons, menus, fields, and labels in bold
Use angle brackets between UI labels for menu paths, such as File > Save As
Use italics for new terms on first use, sparingly and only when introducing the term
Use italics for publication titles
Format keyboard shortcuts in plain text, such as Press Ctrl+Alt+Delete
Use owner/repo link text for GitHub repositories, preferring [NVIDIA/NeMo](link) over prose references like 'the GitHub repo'
Introduce every code block with a complete sentence
Do not make a code block complete the grammar of the previous sentence
Do not continue a sentence after a code block
Use syntax highlighting when the format supports it for code blocks
Avoid the word 'snippet' unless the surrounding docs already use it as a term of art
Keep inline method, function, and class references consistent with nearby docs, omitting empty parentheses for prose readability when no call is shown
Use descriptive anchor text that matches the destination title when possible for links
Avoid raw URLs in running text
Avoid generic anchor text such as 'here,' 'this page,' and 'read more'
Include acronyms in link text when a linked term includes an acronym
Do not link long sentences or multiple sentences
Avoid links ...

Files:

  • python/nemo_relay/README.md
  • go/nemo_relay/README.md
  • integrations/coding-agents/cursor/README.md
  • crates/node/README.md
  • crates/wasm/README.md
  • integrations/openclaw/README.md
  • AGENTS.md
  • README.md
**/{docs,examples,**/*.md,*.patch,*.diff,.github,*.sh,*.yaml,*.yml}

📄 CodeRabbit inference engine (.agents/skills/rename-surfaces/SKILL.md)

Update documentation, examples, CI configuration, and patch artifacts when performing rename operations

Files:

  • python/nemo_relay/README.md
  • go/nemo_relay/README.md
  • integrations/coding-agents/cursor/README.md
  • crates/node/README.md
  • crates/wasm/README.md
  • integrations/openclaw/README.md
  • AGENTS.md
  • fern/docs.yml
  • README.md
**/*.{md,rst,txt}

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/assets/nvidia-style-guide.md)

Spell NVIDIA in all caps. Do not use Nvidia, nvidia, or NV.

Files:

  • python/nemo_relay/README.md
  • go/nemo_relay/README.md
  • integrations/coding-agents/cursor/README.md
  • crates/node/README.md
  • crates/wasm/README.md
  • integrations/openclaw/README.md
  • AGENTS.md
  • README.md
**/*.{md,rst}

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/assets/nvidia-style-guide.md)

**/*.{md,rst}: Format commands, code elements, expressions, package names, file names, and paths as inline code.
Use descriptive link text. Avoid raw URLs and weak anchors such as "here" or "read more."
Use title case consistently for technical documentation headings.
Introduce code blocks, lists, tables, and images with complete sentences.
Write procedures as imperative steps. Keep steps parallel and split long procedures into smaller tasks.
Prefer active voice, present tense, short sentences, contractions, and plain English.
Use can for possibility and reserve may for permission.
Use after for temporal relationships instead of once.
Prefer refer to over see when the wording points readers to another resource.
Avoid culture-specific idioms, unnecessary Latinisms, jokes, and marketing exaggeration in technical docs.
Spell out months in body text, avoid ordinal dates, and use clear time zones.
Spell out whole numbers from zero through nine unless they are technical values, parameters, versions, or UI values.
Use numerals for 10 or greater and include commas in thousands.
Do not add trademark symbols to learning-oriented docs unless the source, platform, or legal guidance explicitly requires them.

Files:

  • python/nemo_relay/README.md
  • go/nemo_relay/README.md
  • integrations/coding-agents/cursor/README.md
  • crates/node/README.md
  • crates/wasm/README.md
  • integrations/openclaw/README.md
  • AGENTS.md
  • README.md
**/README.md

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Update relevant crate or package README when that surface changed

Files:

  • python/nemo_relay/README.md
  • go/nemo_relay/README.md
  • integrations/coding-agents/cursor/README.md
  • crates/node/README.md
  • crates/wasm/README.md
  • integrations/openclaw/README.md
  • README.md
**/*.{rs,py,js,ts,tsx,jsx,go,toml,yaml,yml,md,sh,bash,json}

📄 CodeRabbit inference engine (AGENTS.md)

Keep SPDX headers on source, docs, scripts, and configuration files. The project is Apache-2.0.

Files:

  • python/nemo_relay/README.md
  • go/nemo_relay/README.md
  • integrations/coding-agents/cursor/README.md
  • crates/node/README.md
  • crates/wasm/README.md
  • integrations/openclaw/README.md
  • AGENTS.md
  • crates/cli/src/doctor.rs
  • fern/docs.yml
  • crates/cli/tests/coverage/doctor_tests.rs
  • README.md
  • crates/cli/src/adapters/hermes.rs
  • integrations/openclaw/src/hook-replay/llm.ts
  • crates/cli/tests/coverage/adapters_tests.rs
  • crates/cli/tests/coverage/gateway_tests.rs
  • crates/node/src/promise_call.rs
  • crates/node/src/api/mod.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/cli/tests/coverage/session_tests.rs
  • integrations/openclaw/test/llm-replay.test.ts
  • crates/core/tests/unit/observability/atof_tests.rs
  • crates/core/src/observability/openinference.rs
  • crates/core/tests/unit/atif_tests.rs
  • crates/cli/tests/coverage/launcher_tests.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/core/src/observability/atif.rs
  • crates/cli/src/launcher.rs
  • scripts/docs/sync_fern_docs_branch.py
  • crates/cli/src/session.rs
python/nemo_relay/**/*

⚙️ CodeRabbit configuration file

python/nemo_relay/**/*: Review Python wrapper changes for typed API consistency, contextvars-based scope isolation, async behavior, and parity with the native extension.
Stubs and runtime implementations should stay aligned.

Files:

  • python/nemo_relay/README.md
{crates/adaptive/**,python/nemo_relay/plugin.py,go/nemo_relay/**,**/node/**,**/wasm/**}

📄 CodeRabbit inference engine (.agents/skills/maintain-optimizer/SKILL.md)

{crates/adaptive/**,python/nemo_relay/plugin.py,go/nemo_relay/**,**/node/**,**/wasm/**}: Maintain consistent plugin lifecycle across all language bindings (Python, Go, Node/WebAssembly, and Rust)
Keep plugin context surfaces aligned across all language implementations

Files:

  • go/nemo_relay/README.md
  • crates/node/README.md
  • crates/wasm/README.md
  • crates/node/tests/scope_tests.mjs
  • crates/node/src/promise_call.rs
  • crates/node/src/api/mod.rs
go/nemo_relay/**/*

⚙️ CodeRabbit configuration file

go/nemo_relay/**/*: Review Go binding changes for cgo memory ownership, race safety, callback cleanup, idiomatic exported APIs, and parity with Rust/FFI behavior.
Any API change should include focused Go tests and consider race-test behavior.

Files:

  • go/nemo_relay/README.md
docs/reference/**

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Update relevant reference documentation for any public API changes

Files:

  • docs/reference/performance.mdx
{crates/adaptive/**,python/nemo_relay/adaptive.py,python/nemo_relay/plugin.py,go/nemo_relay/adaptive/**,go/nemo_relay/!(adaptive)/**,**/node/**,**/wasm/**}

📄 CodeRabbit inference engine (.agents/skills/maintain-optimizer/SKILL.md)

Keep adaptive surface in sync across crates/adaptive, shared plugin behavior in core and bindings, Python adaptive/plugin wrappers in python/nemo_relay/adaptive.py and python/nemo_relay/plugin.py, Go adaptive helpers under go/nemo_relay/adaptive plus shared plugin helpers in go/nemo_relay, and Node/WebAssembly adaptive helpers and plugin wrappers

Files:

  • crates/node/README.md
  • crates/wasm/README.md
  • crates/node/tests/scope_tests.mjs
  • crates/node/src/promise_call.rs
  • crates/node/src/api/mod.rs
crates/{python,ffi,node,wasm}/**/*

⚙️ CodeRabbit configuration file

crates/{python,ffi,node,wasm}/**/*: Treat binding changes as public API changes. Check for parity with the other language bindings, FFI ownership/lifetime safety,
callback error propagation, stable type conversion, and consistent async/stream semantics.
Flag changes that update one binding without corresponding tests or documentation for the same surface elsewhere.

Files:

  • crates/node/README.md
  • crates/wasm/README.md
  • crates/node/tests/scope_tests.mjs
  • crates/node/src/promise_call.rs
  • crates/node/src/api/mod.rs
AGENTS.md

📄 CodeRabbit inference engine (CLAUDE.md)

Document agent implementations in AGENTS.md with clear descriptions of functionality and usage

Files:

  • AGENTS.md
{.github/**,.gitlab-ci.yml,.pre-commit-config.yaml,justfile,scripts/**}

⚙️ CodeRabbit configuration file

{.github/**,.gitlab-ci.yml,.pre-commit-config.yaml,justfile,scripts/**}: Review automation changes for reproducibility, pinned versions where appropriate, secret handling, and consistency with the documented validation matrix.
Pay attention to commands that need generated native artifacts, FFI libraries, or platform-specific environment variables.

Files:

  • .github/CODE_OF_CONDUCT.md
  • .github/workflows/fern-docs.yml
  • scripts/docs/sync_fern_docs_branch.py
**/{crates/**/*.rs,python/**/*.py,crates/ffi/**/*.{h,c},go/**/*.go,crates/node/**/*.{js,ts,mjs}}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions: Rust and Python snake_case, C FFI exports prefixed nemo_relay_, Go PascalCase for public APIs, Node.js camelCase.

Files:

  • crates/node/tests/scope_tests.mjs
  • crates/cli/src/doctor.rs
  • crates/cli/tests/coverage/doctor_tests.rs
  • crates/cli/src/adapters/hermes.rs
  • crates/cli/tests/coverage/adapters_tests.rs
  • crates/cli/tests/coverage/gateway_tests.rs
  • crates/node/src/promise_call.rs
  • crates/node/src/api/mod.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/cli/tests/coverage/session_tests.rs
  • crates/core/tests/unit/observability/atof_tests.rs
  • crates/core/src/observability/openinference.rs
  • crates/core/tests/unit/atif_tests.rs
  • crates/cli/tests/coverage/launcher_tests.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/core/src/observability/atif.rs
  • crates/cli/src/launcher.rs
  • crates/cli/src/session.rs
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}

⚙️ CodeRabbit configuration file

{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}: Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant.
Prefer assertions on lifecycle events, scope stacks, middleware ordering, and binding parity over shallow smoke tests.

Files:

  • crates/node/tests/scope_tests.mjs
  • crates/cli/tests/coverage/doctor_tests.rs
  • crates/cli/tests/coverage/adapters_tests.rs
  • crates/cli/tests/coverage/gateway_tests.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/cli/tests/coverage/session_tests.rs
  • crates/core/tests/unit/observability/atof_tests.rs
  • crates/core/tests/unit/atif_tests.rs
  • crates/cli/tests/coverage/launcher_tests.rs
  • crates/cli/tests/coverage/alignment_tests.rs
**/*.rs

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Use snake_case naming convention for Rust identifiers (e.g., nemo_relay_tool_call)

**/*.rs: Any Rust change must run just test-rust
Any Rust change must run cargo fmt --all
Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all for all FFI work since it is Rust work
Run just test-rust to validate FFI changes
Run cargo clippy --workspace --all-targets -- -D warnings to enforce strict linting on FFI work

When Rust files changed as part of Go work, also run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all when Rust files are changed as part of Node work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files are changed as part of Node work
Run just test-rust when Rust files are changed as part of Node work

**/*.rs: Run cargo fmt --all to format all Rust code
Run cargo clippy --workspace --all-targets -- -D warnings to enforce all clippy lints as errors

**/*.rs: Run cargo fmt --all when Rust files changed as part of WebAssembly work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files changed as part of WebAssembly work

**/*.rs: If any Rust code changed, always run just test-rust
If any Rust code changed, also run cargo fmt --all
If any Rust code changed, also run cargo clippy --workspace --all-targets -- -D warnings
Run Rust formatting with cargo fmt --all
Run Rust linting with cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Use cargo fmt for Rust code formatting
Run cargo clippy -- -D warnings to lint Rust code and treat all warnings as errors
Use Rust snake_case naming convention for Rust identifiers
Include SPDX license header in all Rust source files using double-slash comment syntax
Validate Rust code with uv run pre-commit run --all-files to enforce cargo fmt formatting check, cargo clippy lints, and cargo deny aud...

Files:

  • crates/cli/src/doctor.rs
  • crates/cli/tests/coverage/doctor_tests.rs
  • crates/cli/src/adapters/hermes.rs
  • crates/cli/tests/coverage/adapters_tests.rs
  • crates/cli/tests/coverage/gateway_tests.rs
  • crates/node/src/promise_call.rs
  • crates/node/src/api/mod.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/cli/tests/coverage/session_tests.rs
  • crates/core/tests/unit/observability/atof_tests.rs
  • crates/core/src/observability/openinference.rs
  • crates/core/tests/unit/atif_tests.rs
  • crates/cli/tests/coverage/launcher_tests.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/core/src/observability/atif.rs
  • crates/cli/src/launcher.rs
  • crates/cli/src/session.rs
**/{Cargo.toml,**/*.rs}

📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)

Maintain consistency between Rust package names in Cargo.toml and their actual usage across the codebase

Files:

  • crates/cli/src/doctor.rs
  • crates/cli/tests/coverage/doctor_tests.rs
  • crates/cli/src/adapters/hermes.rs
  • crates/cli/tests/coverage/adapters_tests.rs
  • crates/cli/tests/coverage/gateway_tests.rs
  • crates/node/src/promise_call.rs
  • crates/node/src/api/mod.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/cli/tests/coverage/session_tests.rs
  • crates/core/tests/unit/observability/atof_tests.rs
  • crates/core/src/observability/openinference.rs
  • crates/core/tests/unit/atif_tests.rs
  • crates/cli/tests/coverage/launcher_tests.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/core/src/observability/atif.rs
  • crates/cli/src/launcher.rs
  • crates/cli/src/session.rs
**/*.{h,hpp,c,cpp,rs}

📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)

Ensure FFI header and library naming follows consistent conventions across platform-specific builds

Files:

  • crates/cli/src/doctor.rs
  • crates/cli/tests/coverage/doctor_tests.rs
  • crates/cli/src/adapters/hermes.rs
  • crates/cli/tests/coverage/adapters_tests.rs
  • crates/cli/tests/coverage/gateway_tests.rs
  • crates/node/src/promise_call.rs
  • crates/node/src/api/mod.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/cli/tests/coverage/session_tests.rs
  • crates/core/tests/unit/observability/atof_tests.rs
  • crates/core/src/observability/openinference.rs
  • crates/core/tests/unit/atif_tests.rs
  • crates/cli/tests/coverage/launcher_tests.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/core/src/observability/atif.rs
  • crates/cli/src/launcher.rs
  • crates/cli/src/session.rs
**/*.{rs,toml}

📄 CodeRabbit inference engine (.agents/skills/rename-surfaces/SKILL.md)

Update Rust crate names and module prefixes during coordinated rename operations

Files:

  • crates/cli/src/doctor.rs
  • crates/cli/tests/coverage/doctor_tests.rs
  • crates/cli/src/adapters/hermes.rs
  • crates/cli/tests/coverage/adapters_tests.rs
  • crates/cli/tests/coverage/gateway_tests.rs
  • crates/node/src/promise_call.rs
  • crates/node/src/api/mod.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/cli/tests/coverage/session_tests.rs
  • crates/core/tests/unit/observability/atof_tests.rs
  • crates/core/src/observability/openinference.rs
  • crates/core/tests/unit/atif_tests.rs
  • crates/cli/tests/coverage/launcher_tests.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/core/src/observability/atif.rs
  • crates/cli/src/launcher.rs
  • crates/cli/src/session.rs
crates/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

Use Json = serde_json::Value in Rust-facing runtime APIs where the existing code expects JSON payloads.

Use Result<T> with FlowError in 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.

Files:

  • crates/cli/src/doctor.rs
  • crates/cli/tests/coverage/doctor_tests.rs
  • crates/cli/src/adapters/hermes.rs
  • crates/cli/tests/coverage/adapters_tests.rs
  • crates/cli/tests/coverage/gateway_tests.rs
  • crates/node/src/promise_call.rs
  • crates/node/src/api/mod.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/cli/tests/coverage/session_tests.rs
  • crates/core/tests/unit/observability/atof_tests.rs
  • crates/core/src/observability/openinference.rs
  • crates/core/tests/unit/atif_tests.rs
  • crates/cli/tests/coverage/launcher_tests.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/core/src/observability/atif.rs
  • crates/cli/src/launcher.rs
  • crates/cli/src/session.rs
**/*.{py,txt,toml,cfg,yaml,yml}

📄 CodeRabbit inference engine (.agents/skills/rename-surfaces/SKILL.md)

Update Python package names and top-level module imports during coordinated rename operations

Files:

  • fern/docs.yml
  • scripts/docs/sync_fern_docs_branch.py
{README.md,docs/**/*.{md,rst,txt},fern/**/*}

📄 CodeRabbit inference engine (.agents/skills/prepare-code-freeze/SKILL.md)

Search and update documentation source for references to the old version in README.md, docs, and fern directories, updating current-version install commands, package examples, and configuration examples to <next-version>

Files:

  • fern/docs.yml
  • README.md
{crates/adaptive/**/*.rs,**/*test*.{rs,py,go,ts,js},**/*adaptive*test*.{rs,py,go,ts,js},docs/plugins/adaptive/**}

📄 CodeRabbit inference engine (.agents/skills/maintain-optimizer/SKILL.md)

Maintain documented and tested validation and report behavior for adaptive surfaces

Files:

  • crates/cli/tests/coverage/doctor_tests.rs
  • crates/cli/tests/coverage/adapters_tests.rs
  • crates/cli/tests/coverage/gateway_tests.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/cli/tests/coverage/session_tests.rs
  • integrations/openclaw/test/llm-replay.test.ts
  • crates/core/tests/unit/observability/atof_tests.rs
  • crates/core/tests/unit/atif_tests.rs
  • crates/cli/tests/coverage/launcher_tests.rs
  • crates/cli/tests/coverage/alignment_tests.rs
{README.md,docs/getting-started/**/*.md}

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Update README.md, docs/getting-started/, or binding-level READMEs if behavior differs by language or usage changed

Files:

  • README.md
README.md

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Update README.md to reflect current workspace members and top-level documentation when workspace structure changes

Files:

  • README.md
**/*.{wasm,js,ts}{,x}

📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)

Ensure WebAssembly package naming conventions are consistent with generated package expectations and downstream consumption

Files:

  • integrations/openclaw/src/hook-replay/llm.ts
  • integrations/openclaw/test/llm-replay.test.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

Run Node.js formatting with npm run format --workspace=nemo-relay-node

Include SPDX license header in all JavaScript and TypeScript source files using double-slash comment syntax

Files:

  • integrations/openclaw/src/hook-replay/llm.ts
  • integrations/openclaw/test/llm-replay.test.ts
crates/node/src/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Use camelCase naming convention for Node.js identifiers (e.g., toolCall)

Files:

  • crates/node/src/promise_call.rs
  • crates/node/src/api/mod.rs
crates/node/src/api/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Update Node.js binding in crates/node/src/api/mod.rs for language-native bindings

Files:

  • crates/node/src/api/mod.rs
{crates/core,crates/adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

Changes to crates/core or crates/adaptive must run the full language matrix

Files:

  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/atof_tests.rs
  • crates/core/src/observability/openinference.rs
  • crates/core/tests/unit/atif_tests.rs
  • crates/core/src/observability/atif.rs
crates/core/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)

If the change touched crates/core or shared runtime semantics, also use validate-change for broader validation

Files:

  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/atof_tests.rs
  • crates/core/src/observability/openinference.rs
  • crates/core/tests/unit/atif_tests.rs
  • crates/core/src/observability/atif.rs
crates/{core,adaptive}/**

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If crates/core or crates/adaptive changed, run the full matrix across Rust, Python, Go, Node.js, and WebAssembly

Files:

  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/atof_tests.rs
  • crates/core/src/observability/openinference.rs
  • crates/core/tests/unit/atif_tests.rs
  • crates/core/src/observability/atif.rs
crates/{core,adaptive}/**/*.rs

⚙️ CodeRabbit configuration file

crates/{core,adaptive}/**/*.rs: Review the Rust runtime for async correctness, scope isolation, middleware ordering, and event lifecycle regressions.
Pay close attention to task-local/thread-local scope propagation, callback lifetimes, stream finalization, and root_uuid isolation.
Public API changes should preserve existing behavior unless tests and docs show the intended migration path.

Files:

  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/atof_tests.rs
  • crates/core/src/observability/openinference.rs
  • crates/core/tests/unit/atif_tests.rs
  • crates/core/src/observability/atif.rs
**/*{test,spec,smoke}.{js,ts,py}

📄 CodeRabbit inference engine (.agents/skills/contribute-integration/SKILL.md)

Relevant integration tests or smoke path must pass

Files:

  • integrations/openclaw/test/llm-replay.test.ts
crates/core/src/observability/{atif,otel,openinference}.rs

📄 CodeRabbit inference engine (.agents/skills/maintain-observability/SKILL.md)

When changing event fields in ATIF, OpenTelemetry, or OpenInference observability surfaces, keep the core event model in crates/core/src/observability/atif.rs, crates/core/src/observability/otel.rs, and crates/core/src/observability/openinference.rs in sync

Files:

  • crates/core/src/observability/openinference.rs
  • crates/core/src/observability/atif.rs
.github/workflows/*.{yml,yaml}

📄 CodeRabbit inference engine (.agents/skills/maintain-ci/SKILL.md)

.github/workflows/*.{yml,yaml}: Put permissions: on each job that needs token access in GitHub Actions workflows
Avoid workflow-level permissions unless the repository intentionally centralizes them and the inheritance tradeoff is documented
Keep third-party actions pinned to full commit SHAs and preserve the readable version comment after the SHA
Prefer action-native or ecosystem-native caching over generic actions/cache
Use lockfiles or dependency manifests to drive cache invalidation in GitHub Actions workflows
Keep deploy and publish permissions isolated to the jobs that need them in GitHub Actions
Read both caller and callee when a workflow uses workflow_call in GitHub Actions
Put release-tag validation in the earliest practical caller job when the pipeline has tag-based publish behavior
Keep release-tag policy aligned with RELEASING.md: raw SemVer tags only, no leading v
contents: read is the default minimum permission for checkout-based build, test, docs, and packaging jobs
pull-requests: read is required for PR metadata lookup jobs in GitHub Actions workflows
pages: write and id-token: write should be limited to Pages deployment jobs and callers that invoke them through reusable workflows
For reusable workflows, the caller must grant every permission the called jobs require; the callee cannot elevate beyond what the caller provides
Prefer astral-sh/setup-uv cache support with cache-dependency-glob anchored to uv.lock
Prefer Swatinem/rust-cache with explicit shared-key and workspaces instead of ad hoc target-directory caching
Avoid caching generated outputs that can hide stale behavior unless the repo already relies on them deliberately

Files:

  • .github/workflows/fern-docs.yml
.{github/workflows/*.{yml,yaml},gitlab-ci.yml}

📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)

Ensure CI workflows reference the same package names, install commands, and build commands as local development workflows

Files:

  • .github/workflows/fern-docs.yml
{pyproject.toml,**/*.py}

📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)

Maintain consistency between Python package names in pyproject.toml and import paths used throughout the codebase

Files:

  • scripts/docs/sync_fern_docs_branch.py
{scripts/**,third-party/**}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

{scripts/**,third-party/**}: For third-party integration or patch changes, run patch validation with ./scripts/apply-patches.sh --check and relevant integration tests. Keep root ./scripts/*.sh wrappers for third-party flows
Run third-party patch bootstrap with ./scripts/bootstrap-third-party.sh
Run third-party patch validation with ./scripts/apply-patches.sh --check

Files:

  • scripts/docs/sync_fern_docs_branch.py
**/*.py

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

**/*.py: Run Python formatting with uv run ruff format python
Run Python testing with uv run pytest -k "<pattern>"

**/*.py: Use Ruff with rule sets E, F, W, I for Python linting
Use Ruff formatter with line length 120 and double quotes for Python code formatting
Run ty for Python type checking
Use Python snake_case naming convention for Python identifiers
Include SPDX license header in all Python source files using hash comment syntax
Validate Python code with uv run pre-commit run --all-files to enforce Ruff linting and formatting, and ty type checking

Files:

  • scripts/docs/sync_fern_docs_branch.py
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
Repo: NVIDIA/NeMo-Relay

Timestamp: 2026-06-03T04:11:40.730Z
Learning: 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.
Learnt from: CR
Repo: NVIDIA/NeMo-Relay

Timestamp: 2026-06-03T04:11:40.730Z
Learning: 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.
Learnt from: CR
Repo: NVIDIA/NeMo-Relay

Timestamp: 2026-06-03T04:11:40.730Z
Learning: Run 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, validate every affected binding.
Learnt from: CR
Repo: NVIDIA/NeMo-Relay

Timestamp: 2026-06-03T04:11:40.730Z
Learning: Before review, prefer `uv run pre-commit run --all-files` when the change crosses languages or tooling.
Learnt from: CR
Repo: NVIDIA/NeMo-Relay

Timestamp: 2026-06-03T04:11:40.730Z
Learning: Preserve the shared runtime model across bindings. Do not add behavior to one primary binding without considering Rust, Python, and Node.js parity.
Learnt from: CR
Repo: NVIDIA/NeMo-Relay

Timestamp: 2026-06-03T04:11:40.730Z
Learning: Prefer documented public APIs and stable wrapper commands. Do not rely on internal helpers in examples or user-facing docs.
Learnt from: CR
Repo: NVIDIA/NeMo-Relay

Timestamp: 2026-06-03T04:11:40.730Z
Learning: 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.
Learnt from: CR
Repo: NVIDIA/NeMo-Relay

Timestamp: 2026-06-03T04:11:40.730Z
Learning: Update `README.md`, `fern/`, package READMEs, and binding-support notes when public behavior, package names, examples, or supported bindings change.
Learnt from: CR
Repo: NVIDIA/NeMo-Relay

Timestamp: 2026-06-03T04:11:40.730Z
Learning: Keep release-process details in maintainer docs such as `RELEASING.md`. Do not move release-history policy into user-facing docs or `CHANGELOG.md`.
Learnt from: CR
Repo: NVIDIA/NeMo-Relay

Timestamp: 2026-06-03T04:11:40.730Z
Learning: Keep stable public wrappers at the `scripts/` root in docs and examples. Reference namespaced helper paths only when documenting internal maintenance work.
Learnt from: CR
Repo: NVIDIA/NeMo-Relay

Timestamp: 2026-06-03T04:11:40.730Z
Learning: Use branch prefixes from the contributor docs: `feat/`, `fix/`, `docs/`, `test/`, or `refactor/`.
Learnt from: CR
Repo: NVIDIA/NeMo-Relay

Timestamp: 2026-06-03T04:11:40.730Z
Learning: Use signed-off commits for PR work: `git commit -s`.
Learnt from: CR
Repo: NVIDIA/NeMo-Relay

Timestamp: 2026-06-03T04:11:40.730Z
Learning: Before creating, opening, publishing, or editing a pull request, read `.github/pull_request_template.md` and use it as the PR body skeleton. Preserve its visible headings, checklist items, and related-issue guidance.
Learnt from: CR
Repo: NVIDIA/NeMo-Relay

Timestamp: 2026-06-03T04:11:40.730Z
Learning: PR descriptions should include what changed, why, how it was tested, and any breaking changes within the repository template format.
Learnt from: CR
Repo: NVIDIA/NeMo-Relay

Timestamp: 2026-06-03T04:11:40.730Z
Learning: If repo-local PR guidance conflicts with generic GitHub connector or plugin guidance, follow the repo-local PR guidance for PR body format and review handoff details.
📚 Learning: 2026-05-07T18:04:44.387Z
Learnt from: mnajafian-nv
Repo: NVIDIA/NeMo-Flow PR: 67
File: integrations/openclaw/src/modules.ts:1-2
Timestamp: 2026-05-07T18:04:44.387Z
Learning: In NVIDIA/NeMo-Flow, TypeScript source files should use `//` line comments for SPDX headers (e.g., `// SPDX-FileCopyrightText: ...` and `// SPDX-License-Identifier: ...`) rather than C-style block comments (`/* ... */`). The repo’s copyright checker enforces this mapping, so `//` SPDX headers in `.ts` files should not be flagged as a style violation.

Applied to files:

  • integrations/openclaw/src/hook-replay/llm.ts
  • integrations/openclaw/test/llm-replay.test.ts
📚 Learning: 2026-05-03T04:23:07.497Z
Learnt from: willkill07
Repo: NVIDIA/NeMo-Flow PR: 46
File: .github/workflows/ci_rust.yml:31-64
Timestamp: 2026-05-03T04:23:07.497Z
Learning: In GitHub Actions workflow YAML, it’s valid to conditionally disable a service container by setting the service container’s `image` to an empty string (`''`) via a matrix variable (e.g., `redis_service_image: ''`). This intentionally makes the runner skip service initialization for that matrix entry rather than failing the job. When reviewing workflows, don’t flag this as an error if the workflow uses an empty `image` to disable the service on specific matrix entries (e.g., OS-specific setups); verify the `image` is sourced from the matrix variable and that the service is only expected to be available when a non-empty image is provided.

Applied to files:

  • .github/workflows/fern-docs.yml
🪛 LanguageTool
docs/about-nemo-relay/release-notes/highlights.mdx

[style] ~37-~37: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ... for guardrails plugin configuration. - Added guardrail scopes for conditional guardr...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~71-~71: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...t-in guardrails plugin documentation. - Added CI path filters, CLI draft release asse...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

🪛 markdownlint-cli2 (0.22.1)
.github/CODE_OF_CONDUCT.md

[warning] 6-6: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)

🪛 Ruff (0.15.15)
scripts/docs/sync_fern_docs_branch.py

[warning] 203-203: Avoid specifying long messages outside the exception class

(TRY003)

🪛 zizmor (1.25.2)
.github/workflows/fern-docs.yml

[warning] 378-378: permissions without explanatory comments (undocumented-permissions): needs an explanatory comment

(undocumented-permissions)


[info] 451-451: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)


[error] 439-439: runtime artifacts potentially vulnerable to a cache poisoning attack (cache-poisoning): enables caching by default

(cache-poisoning)

Comment thread .github/workflows/fern-docs.yml
Comment thread crates/cli/src/session.rs
Comment on lines +1605 to +1633
fn remove_tool_handle_for_event(&mut self, event: &ToolEvent) -> Option<ToolHandle> {
if let Some(active) = self.tools.remove(&event.tool_call_id) {
return Some(active.handle);
}
let owner_subagent_id = self.tool_event_owner_subagent_id(event);
let key = self.matching_active_tool_key(event, owner_subagent_id.as_deref())?;
self.tools.remove(&key).map(|active| active.handle)
}

fn matching_active_tool_key(
&self,
event: &ToolEvent,
owner_subagent_id: Option<&str>,
) -> Option<String> {
if event.arguments.is_null() {
return None;
}
let matches = self
.tools
.iter()
.filter_map(|(key, active)| {
(active.owner_subagent_id.as_deref() == owner_subagent_id
&& active.name == event.tool_name
&& active.arguments == event.arguments)
.then_some(key.clone())
})
.collect::<Vec<_>>();
(matches.len() == 1).then(|| matches[0].clone())
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid root-biased fallback when tool owner is unresolved.

When owner resolution returns None, the matcher currently filters to root-owned tools only. That can close the wrong root handle if the event owner is actually unknown and a subagent has the same (name, arguments) active. In the unresolved-owner case, match across all owners and require a single global match.

Proposed patch
 fn matching_active_tool_key(
     &self,
     event: &ToolEvent,
     owner_subagent_id: Option<&str>,
 ) -> Option<String> {
     if event.arguments.is_null() {
         return None;
     }
     let matches = self
         .tools
         .iter()
         .filter_map(|(key, active)| {
-            (active.owner_subagent_id.as_deref() == owner_subagent_id
+            let owner_matches = match owner_subagent_id {
+                Some(owner) => active.owner_subagent_id.as_deref() == Some(owner),
+                None => true,
+            };
+            (owner_matches
                 && active.name == event.tool_name
                 && active.arguments == event.arguments)
                 .then_some(key.clone())
         })
         .collect::<Vec<_>>();
     (matches.len() == 1).then(|| matches[0].clone())
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fn remove_tool_handle_for_event(&mut self, event: &ToolEvent) -> Option<ToolHandle> {
if let Some(active) = self.tools.remove(&event.tool_call_id) {
return Some(active.handle);
}
let owner_subagent_id = self.tool_event_owner_subagent_id(event);
let key = self.matching_active_tool_key(event, owner_subagent_id.as_deref())?;
self.tools.remove(&key).map(|active| active.handle)
}
fn matching_active_tool_key(
&self,
event: &ToolEvent,
owner_subagent_id: Option<&str>,
) -> Option<String> {
if event.arguments.is_null() {
return None;
}
let matches = self
.tools
.iter()
.filter_map(|(key, active)| {
(active.owner_subagent_id.as_deref() == owner_subagent_id
&& active.name == event.tool_name
&& active.arguments == event.arguments)
.then_some(key.clone())
})
.collect::<Vec<_>>();
(matches.len() == 1).then(|| matches[0].clone())
}
fn remove_tool_handle_for_event(&mut self, event: &ToolEvent) -> Option<ToolHandle> {
if let Some(active) = self.tools.remove(&event.tool_call_id) {
return Some(active.handle);
}
let owner_subagent_id = self.tool_event_owner_subagent_id(event);
let key = self.matching_active_tool_key(event, owner_subagent_id.as_deref())?;
self.tools.remove(&key).map(|active| active.handle)
}
fn matching_active_tool_key(
&self,
event: &ToolEvent,
owner_subagent_id: Option<&str>,
) -> Option<String> {
if event.arguments.is_null() {
return None;
}
let matches = self
.tools
.iter()
.filter_map(|(key, active)| {
let owner_matches = match owner_subagent_id {
Some(owner) => active.owner_subagent_id.as_deref() == Some(owner),
None => true,
};
(owner_matches
&& active.name == event.tool_name
&& active.arguments == event.arguments)
.then_some(key.clone())
})
.collect::<Vec<_>>();
(matches.len() == 1).then(|| matches[0].clone())
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/cli/src/session.rs` around lines 1605 - 1633, The matcher currently
restricts to root-owned tools when owner resolution returns None, causing a
root-biased fallback; modify matching_active_tool_key so the owner check only
applies when owner_subagent_id is Some: in matching_active_tool_key (used by
remove_tool_handle_for_event) change the filter closure to require
active.owner_subagent_id == owner_subagent_id only when
owner_subagent_id.is_some(), otherwise ignore owner_subagent_id and match on
name and arguments across all owners, then keep the existing uniqueness check
(matches.len() == 1) to return a single global match.

Comment on lines +312 to +320
fn drops_uncorrelatable_hermes_pre_tool_call() {
let headers = HeaderMap::new();
let outcome = hermes::adapt(
json!({
"hook_event_name": "pre_tool_call",
"task_id": "task-1",
"tool_name": "terminal",
"tool_input": { "command": "pwd" }
}),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Isolate the missing-session correlatability check in this test.

This case currently omits both session_id and tool-call id, so it can pass even if only one gate is enforced. Keep a valid tool-call id and omit only session_id to target the intended behavior.

Proposed patch
 fn drops_uncorrelatable_hermes_pre_tool_call() {
     let headers = HeaderMap::new();
     let outcome = hermes::adapt(
         json!({
             "hook_event_name": "pre_tool_call",
             "task_id": "task-1",
+            "tool_call_id": "tool-1",
             "tool_name": "terminal",
             "tool_input": { "command": "pwd" }
         }),
         &headers,
     );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fn drops_uncorrelatable_hermes_pre_tool_call() {
let headers = HeaderMap::new();
let outcome = hermes::adapt(
json!({
"hook_event_name": "pre_tool_call",
"task_id": "task-1",
"tool_name": "terminal",
"tool_input": { "command": "pwd" }
}),
fn drops_uncorrelatable_hermes_pre_tool_call() {
let headers = HeaderMap::new();
let outcome = hermes::adapt(
json!({
"hook_event_name": "pre_tool_call",
"task_id": "task-1",
"tool_call_id": "tool-1",
"tool_name": "terminal",
"tool_input": { "command": "pwd" }
}),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/cli/tests/coverage/adapters_tests.rs` around lines 312 - 320, The test
drops_uncorrelatable_hermes_pre_tool_call currently omits both session_id and
tool_call_id so it can pass when only one correlatability gate exists; change
the test input passed to hermes::adapt to include a valid tool_call_id (e.g.
"tool_call_id": "toolcall-1") while keeping session_id omitted so the test
isolates the missing-session correlatability check and still expects the adapter
to drop the event.

Comment thread crates/core/src/observability/atif.rs
Comment on lines +1197 to +1202
const cacheRead =
numberField(usage, 'cacheRead') ??
numberField(usage, 'cache_read_tokens') ??
numberField(usage, 'cache_read_input_tokens') ??
nestedNumberField(usage, 'input_tokens_details', 'cached_tokens') ??
nestedNumberField(usage, 'prompt_tokens_details', 'cached_tokens');
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Preserve top-level cached_tokens during replay normalization.

This branch still drops usage objects that already use the canonical NeMo Relay field name cached_tokens (and the common camelCase variant). That silently loses cache-read metrics on replayed spans.

Suggested fix
   const cacheRead =
     numberField(usage, 'cacheRead') ??
+    numberField(usage, 'cached_tokens') ??
+    numberField(usage, 'cachedTokens') ??
     numberField(usage, 'cache_read_tokens') ??
     numberField(usage, 'cache_read_input_tokens') ??
     nestedNumberField(usage, 'input_tokens_details', 'cached_tokens') ??
     nestedNumberField(usage, 'prompt_tokens_details', 'cached_tokens');
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const cacheRead =
numberField(usage, 'cacheRead') ??
numberField(usage, 'cache_read_tokens') ??
numberField(usage, 'cache_read_input_tokens') ??
nestedNumberField(usage, 'input_tokens_details', 'cached_tokens') ??
nestedNumberField(usage, 'prompt_tokens_details', 'cached_tokens');
const cacheRead =
numberField(usage, 'cacheRead') ??
numberField(usage, 'cached_tokens') ??
numberField(usage, 'cachedTokens') ??
numberField(usage, 'cache_read_tokens') ??
numberField(usage, 'cache_read_input_tokens') ??
nestedNumberField(usage, 'input_tokens_details', 'cached_tokens') ??
nestedNumberField(usage, 'prompt_tokens_details', 'cached_tokens');
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@integrations/openclaw/src/hook-replay/llm.ts` around lines 1197 - 1202, The
cacheRead normalization currently misses top-level fields named cached_tokens
(and camelCase cachedTokens), causing cache-read metrics to be dropped; update
the cacheRead resolution in the llm replay normalization to check for
numberField(usage, 'cached_tokens') and numberField(usage, 'cachedTokens')
(prior to or alongside the other fallbacks) so that usage objects already using
the canonical NeMo Relay name or camelCase variant are preserved; locate the
cacheRead variable assignment and add those checks to the existing chain that
uses numberField(...) and nestedNumberField(...).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature a new feature lang:js PR changes/introduces Javascript/Typescript code lang:python PR changes/introduces Python code lang:rust PR changes/introduces Rust code size:XL PR is extra large

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants