Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 129 additions & 28 deletions docs/nemo-relay-cli/basic-usage.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,12 @@ under the active session scope.
Use the agent shortcuts for no-install local observability. The wrapper starts
a gateway on a dynamic `127.0.0.1` port, injects the resolved hook and gateway
configuration into the launched coding agent, and stops the gateway when the
agent exits.
agent exits. Relay owns the launched wrapper and its descendants as one process
tree, so a gateway failure cannot leave an agent running without its private
gateway and hook configuration. Interactive Unix launches transfer foreground
terminal ownership and preserve Ctrl-C and Ctrl-Z job control. Non-interactive
Unix launches forward termination signals to the complete agent process tree
before Relay removes the injected configuration.

```bash
nemo-relay codex
Expand All @@ -69,6 +74,14 @@ instead of the built-in shortcut:
nemo-relay run -- codex
```

For Claude Code and Codex, transparent mode leaves the caller's source settings,
selected profile, and installed plugin state unchanged. A process marker makes
any installed Relay MCP borrow the wrapper-owned dynamic gateway. Claude's
persistent hooks exit without forwarding; Codex disables the known Relay plugin
hook identities in its process-local CLI state. Only the injected wrapper hooks
deliver events. Hermes gets the same isolation through its process-private
configuration overlay.

If a launcher or wrapper hides the real agent name, set that wrapper as the
configured command and pass `--agent`. The same pattern applies to Claude Code,
Codex, and Hermes:
Expand All @@ -82,27 +95,40 @@ command = "my-codex-wrapper"
nemo-relay run --agent codex
```

Hermes is different from the other transparent modes: `run --agent hermes`
starts the gateway and exports the dynamic `NEMO_RELAY_GATEWAY_URL`, but Hermes
shell hooks still need to be installed or otherwise approved in Hermes config.
For Hermes, interactive setup configures only Relay's transparent wrapper.
Persistent MCP and trusted shell-hook state is owned by
`nemo-relay install hermes`. Transparent `run --agent hermes` exports the
dynamic `NEMO_RELAY_GATEWAY_URL` through a process-private `HERMES_HOME`
overlay; it never rewrites the user's Hermes config.

Use `--dry-run --print` to inspect the generated hook config, gateway
environment, gateway URL, and final command without launching the agent.

## Persistent Host Plugins
## Persistent Host Integrations

Use persistent plugin installation when Claude Code or Codex should load NeMo
Relay through the host's plugin system instead of through a wrapper command:
Use persistent integration installation to let Claude Code, Codex, or Hermes
Agent load Relay without a wrapper command:

```bash
nemo-relay install claude-code
nemo-relay install codex
nemo-relay install hermes
nemo-relay install all
```

Plugin installation writes a local marketplace, installs the generated
`nemo-relay-plugin`, and configures the host-specific hook and provider routing
needed for full observability. It still relies on the same local `nemo-relay`
Claude Code and Codex use marketplace plugins. Hermes exposes the same stdio
MCP lifecycle through user configuration.

Hermes, Claude Code, and Codex MCP clients can share the native gateway on
`127.0.0.1:47632`. Every `nemo-relay mcp` process acquires that gateway when it
launches. Claude Code uses `alwaysLoad` to wait for the MCP connection;
Hermes starts discovery asynchronously, so its hook command waits for gateway
readiness before it sends the original payload.

For Claude Code and Codex, installation writes a local marketplace, installs
the generated `nemo-relay-plugin`, and configures host-specific hooks and
provider routing. For Hermes, installation updates the Relay-owned portions of
the user configuration instead. All three paths use the local `nemo-relay`
binary on `PATH`.

Use plugin doctor and uninstall for the installed host state:
Expand All @@ -112,8 +138,8 @@ nemo-relay doctor --plugin codex
nemo-relay uninstall codex
```

Refer to [Plugin Installation](/nemo-relay-cli/plugin-installation) for install
directories, host-specific behavior, and Codex lazy sidecar constraints.
Refer to [Coding Agent Installation](/nemo-relay-cli/plugin-installation) for
install directories, host-specific behavior, and the shared-sidecar lifecycle.

## Shared Configuration

Expand Down Expand Up @@ -142,6 +168,67 @@ leaving the plugin editor does not remove the saved base configuration. You can
open the plugin editor again later with `nemo-relay plugins edit`, or use
`nemo-relay plugins edit --project` for project configuration.

### Provider Upstreams

Set provider base URLs under `[upstream]` when you want Relay to forward
compatible requests to a proxy, enterprise endpoint, or another provider host:

```toml
[upstream]
openai_base_url = "https://api.openai.com/v1"
anthropic_base_url = "https://api.anthropic.com"
```

Relay normally chooses the upstream from the request path, regardless of which
compatible client sends the request. The gateway routes map to settings as
follows:

| Gateway Endpoint | Upstream Setting |
| --- | --- |
| `/responses`, `/v1/responses` | `openai_base_url` |
| `/chat/completions`, `/v1/chat/completions` | `openai_base_url` |
| `/models`, `/v1/models` | `openai_base_url` |
| `/v1/messages`, `/v1/messages/count_tokens` | `anthropic_base_url` |

For ordinary provider requests, Relay resolves credentials in this order:

1. Relay preserves an inbound `Authorization`, `x-api-key`, `api-key`, or
`anthropic-api-key` header.
2. If no inbound credential is present, Relay uses the route-specific custom
`Authorization` value from `[upstream]` or its environment override.
3. If no custom value is configured, Relay reads `OPENAI_API_KEY` or
`ANTHROPIC_API_KEY` and applies the provider's standard authentication
scheme.

Set `openai_auth_header` or `anthropic_auth_header` under `[upstream]` only when
an enterprise gateway, proxy, or compatible endpoint requires a complete
`Authorization` value such as `Bearer ...` or `Basic ...`. Prefer the
corresponding environment variables so you don't store credentials in
`config.toml`:

```bash
export NEMO_RELAY_OPENAI_AUTH_HEADER="Bearer <openai-credential>"
export NEMO_RELAY_ANTHROPIC_AUTH_HEADER="Basic <anthropic-credential>"
```

If you override a base URL with `NEMO_RELAY_OPENAI_BASE_URL` or
`NEMO_RELAY_ANTHROPIC_BASE_URL`, set the matching auth-header environment
variable when the new endpoint needs custom authentication. Relay doesn't carry
a custom header from `config.toml` across an environment-level endpoint
override.

An MCP-managed gateway uses custom or environment credentials only after the
client proves that it belongs to that managed gateway.

Codex ChatGPT credentials are the exception to the normal inbound precedence.
On OpenAI routes, Relay recognizes `Bearer eyJ...` JWTs and `Bearer at-...`
access tokens. If `openai_auth_header`, `NEMO_RELAY_OPENAI_AUTH_HEADER`, or a
nonempty `OPENAI_API_KEY` is available, Relay removes the ChatGPT credential,
applies the replacement, and forwards the request to the configured
`openai_base_url`. Without a replacement, Relay preserves the ChatGPT credential
and routes the request to the Codex ChatGPT backend instead of
`openai_base_url`.

### Operational Logging

The CLI initializes operational logging before operational commands run.
Expand Down Expand Up @@ -243,7 +330,9 @@ port is discovered by the wrapper and exposed to hooks through
Common environment variables for direct gateway server use are:

- `NEMO_RELAY_GATEWAY_BIND`
- `NEMO_RELAY_OPENAI_AUTH_HEADER`
- `NEMO_RELAY_OPENAI_BASE_URL`
- `NEMO_RELAY_ANTHROPIC_AUTH_HEADER`
- `NEMO_RELAY_ANTHROPIC_BASE_URL`
- `NEMO_RELAY_MAX_HOOK_PAYLOAD_BYTES`
- `NEMO_RELAY_MAX_PASSTHROUGH_BODY_BYTES`
Expand Down Expand Up @@ -323,10 +412,12 @@ Correlation hints expire after five minutes. If the gateway cannot select one
unambiguous hint, it falls back to the previous LLM owner, then to the only
active subagent, then to the top-level agent scope.

Every gateway LLM event includes `llm_correlation_status` metadata. Possible
values are `explicit`, `single_hint`, `matched_hint`, `sticky_last_owner`,
`active_subagent`, `agent_fallback`, and `ambiguous_fallback`. Matched hints can
also add `llm_correlation_source`, `llm_correlation_subagent_id`,
Every gateway LLM event includes `llm_correlation_status` metadata. Managed
requests can use `explicit`, `single_hint`, `matched_hint`,
`request_affinity`, `sticky_last_owner`, `recent_tool_owner`,
`subagent_start`, `active_subagent`, `agent_fallback`, or
`ambiguous_fallback`. Claude Code startup probes use `pre_turn_probe`. Matched
hints can also add `llm_correlation_source`, `llm_correlation_subagent_id`,
`llm_correlation_conversation_id`, `llm_correlation_generation_id`,
`llm_correlation_request_id`, and `llm_correlation_agent_type`.

Expand All @@ -344,26 +435,36 @@ map to NeMo Relay LLM start/end events when present. Hermes `pre_llm_call` and

## Hook Forwarding

Hooks generated by the wrapper (Claude/Codex ephemeral, Hermes via
setup) invoke `nemo-relay hook-forward <agent>` from stdin. Inside the wrapper
the gateway URL comes from `NEMO_RELAY_GATEWAY_URL` injected on every run;
outside the wrapper (Hermes standalone, IDE-launched Claude/Codex) the hook
command falls back to its embedded `--gateway-url`.
Transparent Claude Code and Codex hooks invoke
`nemo-relay hook-forward <agent>` with the canonical payload on standard input.
The wrapper-owned hook command embeds its ephemeral gateway URL and is marked
as transparent so it cannot recover the fixed gateway.

Hooks installed by Claude Code and Codex plugins use the plugin shim path
managed by `nemo-relay install`. That path starts or reuses the local plugin
sidecar before forwarding the hook payload.
Persistent Claude Code, Codex, and Hermes hooks also use
`nemo-relay hook-forward <agent>`. Each generated command identifies the fixed
gateway and its private install-generation fence, waits for the MCP-owned Relay
gateway, verifies it, and forwards the unchanged payload once. Hermes setup
stores the canonical absolute command and trusts only its exact event pairs; it
does not enable global hook auto-acceptance.

`hook-forward` reads the canonical hook payload from standard input, sends it
to the matching endpoint, and prints the endpoint response. It fails open by
default so observability outages do not block the coding agent. Add
`--fail-closed` only when policy requires hook delivery to block the agent.

Optional flags map to gateway headers:
These flags control delivery and metadata:

- `--gateway-url <url>` selects the Relay gateway that receives the payload.
- `--forward-only` allows source plugins and custom automation to use an
existing compatible gateway without an installer-owned generation fence. It
verifies the gateway but never launches or recovers Relay. Generated
installed hooks use a private generation fence instead.
- `--session-metadata` sets `x-nemo-relay-session-metadata`.
- `--profile` sets `x-nemo-relay-config-profile`.
- `--gateway-mode` sets `x-nemo-relay-gateway-mode`.
- `--fail-closed` returns a failure when delivery fails or Relay rejects the
hook. Without it, forwarding fails open so an observability outage does not
block the coding agent.

## Agent Guides

Expand All @@ -372,9 +473,9 @@ application-mode caveats.

- [Claude Code](/nemo-relay-cli/claude-code)
- [Codex](/nemo-relay-cli/codex)
- [Plugin Installation](/nemo-relay-cli/plugin-installation)
- [Coding Agent Installation](/nemo-relay-cli/plugin-installation)
- [Hermes Agent](/nemo-relay-cli/hermes)

Each guide covers transparent run setup, gateway routing, hook smoke tests,
Agent Trajectory Interchange Format (ATIF) export verification on session end,
and troubleshooting missing LLM lifecycle data.
Agent Trajectory Interchange Format (ATIF) export verification at the host's
supported snapshot boundary, and troubleshooting missing LLM lifecycle data.
70 changes: 55 additions & 15 deletions docs/nemo-relay-cli/claude-code.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,18 @@ nemo-relay claude -- "summarize this repository"

This shortcut is equivalent to `nemo-relay run -- claude`. The wrapper starts a
gateway on a dynamic `127.0.0.1` port, creates a temporary Claude plugin
directory with NeMo Relay hooks, passes that plugin with `--plugin-dir`, and
sets `ANTHROPIC_BASE_URL` to the gateway URL for the launched process.
directory with NeMo Relay hooks, and passes that plugin with `--plugin-dir`.
Because Claude Code gives its first `--settings` source precedence over the
process environment, Relay also creates a private settings overlay that
preserves that source and overrides only `ANTHROPIC_BASE_URL` for the launched
process. The source settings and installed plugin enablement remain unchanged.
If a Relay plugin is already enabled, its MCP process authenticates and borrows
the dynamic gateway instead of launching the fixed sidecar, then monitors that
exact gateway while MCP stdio remains open. Its persistent hooks exit without
forwarding. Only the wrapper-owned temporary hooks deliver lifecycle payloads,
and those hooks authenticate the wrapper gateway before sending a payload, so
installed and source-marketplace plugin IDs cannot duplicate the captured
stream.

Inspect what would be launched without starting Claude Code:

Expand All @@ -54,26 +64,53 @@ The installer creates a local marketplace, installs
provider routing through the local NeMo Relay sidecar. It uses the existing
`nemo-relay` binary on `PATH`; it does not install a plugin-local Relay binary.

The plugin starts `nemo-relay mcp`, a lightweight Rust lifecycle client that
starts or reuses the shared gateway on `127.0.0.1:47632` immediately when the
MCP process launches. The client verifies the gateway identity and effective
persistent configuration, heartbeats it while MCP stdio remains open, and
performs one coordinated restart if the gateway becomes unhealthy. Claude
Code, Codex, and Hermes MCP clients share a compatible gateway, and the gateway
exits after the final client's idle timeout. The MCP server advertises no tools.

The generated entry sets `alwaysLoad: true`, so Claude Code 2.1.121 or newer
waits for the MCP connection before session startup. The installed command hook
carries the same install-generation fence, waits for the MCP-owned gateway, and
forwards the canonical payload once. MCP owns the session-long gateway
lifecycle.

Persistent plugin mode loads system and user Relay configuration only and uses
the user configuration directory as its working directory. Use the transparent
wrapper for project-specific `.nemo-relay` configuration. Run
`nemo-relay install claude-code --force` to replace an existing
generation-fenced installation safely.

Check or remove the installed plugin with:

```bash
nemo-relay doctor --plugin claude-code
nemo-relay uninstall claude-code
```

Refer to [Plugin Installation](/nemo-relay-cli/plugin-installation) for install
directories, rollback behavior, and source marketplace notes.
Refer to [Coding Agent Installation](/nemo-relay-cli/plugin-installation) for
install directories, rollback behavior, and source marketplace notes.

## Shared Config

Create `.nemo-relay/config.toml` for project defaults or
`~/.config/nemo-relay/config.toml` for user defaults:

```toml
[upstream]
anthropic_base_url = "https://api.anthropic.com"

[agents.claude]
command = "claude"
```

To forward Claude Code requests to a custom provider host, configure Relay's
endpoint and authentication as described in
[Provider Upstreams](/nemo-relay-cli/basic-usage#provider-upstreams).

Then configure observability with `nemo-relay plugins edit --project` or
`.nemo-relay/plugins.toml`:

Expand Down Expand Up @@ -113,10 +150,10 @@ claude
```

The gateway forwards Anthropic `/v1/messages`, `/v1/messages/count_tokens`, and
model routes without rewriting provider JSON. Hook events (tool calls, session
markers) are only captured when running through `nemo-relay claude` or
`nemo-relay run --agent claude`, which inject ephemeral hooks into the launched
process.
model routes without rewriting provider JSON. Hook events require either the
persistent NeMo Relay plugin or a transparent `nemo-relay claude` or
`nemo-relay run --agent claude` invocation, which injects ephemeral hooks into
the launched process.

## Captured Events

Expand Down Expand Up @@ -150,7 +187,7 @@ probe bypasses for debugging.

## Smoke Test

Run a small Claude Code prompt that starts a session and uses one simple tool.
Run a small Claude Code prompt that starts a session and uses one tool.
Then check that hook forwarding reaches the gateway:

```bash
Expand All @@ -162,7 +199,7 @@ printf '{"session_id":"smoke-claude","hook_event_name":"SessionStart"}' \
The response should be valid Claude Code hook JSON. For most lifecycle events it
is an allow/continue response. A full observability smoke fixture should produce
expected OpenInference spans, raw ATOF events, and ATIF trajectories for at
least one user turn, one LLM call, and one simple tool call.
least one user turn, one LLM call, and one tool call.

## Verify Export

Expand All @@ -173,9 +210,11 @@ agent scope and wrote Agent Trajectory Interchange Format (ATIF):
ls .nemo-relay/atif
```

The gateway exports `<session-id>.atif.json` on session end. If no file appears,
confirm that `SessionEnd` hooks fire, `plugins.toml` enables the ATIF exporter,
and the gateway process can write to the configured directory.
The gateway exports the session's ATIF file on `SessionEnd`. Unless you change
`filename_template`, the file is named
`nemo-relay-atif-<session-id>.json`. If no file appears, confirm that
`SessionEnd` hooks fire, `plugins.toml` enables the ATIF exporter, and the
gateway process can write to the configured directory.

## Troubleshoot LLM Lifecycle

Expand Down Expand Up @@ -210,8 +249,9 @@ mark under that turn.

## Hook Limitations

Claude Code hooks are available only when Claude Code loads the NeMo Relay
plugin, such as through `nemo-relay claude` or `nemo-relay run --agent claude`.
Claude Code hooks are available only when Claude Code loads the persistent NeMo
Relay plugin or the ephemeral plugin generated by `nemo-relay claude` or
`nemo-relay run --agent claude`.
The standalone gateway can still observe Anthropic LLM traffic, but it cannot
invent missing tool, prompt, compaction, notification, or subagent hooks.

Expand Down
Loading