diff --git a/docs/nemo-relay-cli/basic-usage.mdx b/docs/nemo-relay-cli/basic-usage.mdx index 5a5166f97..324b9b113 100644 --- a/docs/nemo-relay-cli/basic-usage.mdx +++ b/docs/nemo-relay-cli/basic-usage.mdx @@ -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 @@ -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: @@ -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: @@ -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 @@ -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 " +export NEMO_RELAY_ANTHROPIC_AUTH_HEADER="Basic " +``` + +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. @@ -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` @@ -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`. @@ -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 ` 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 ` 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 `. 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 ` 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 @@ -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. diff --git a/docs/nemo-relay-cli/claude-code.mdx b/docs/nemo-relay-cli/claude-code.mdx index a8cc0b108..f94d2d59d 100644 --- a/docs/nemo-relay-cli/claude-code.mdx +++ b/docs/nemo-relay-cli/claude-code.mdx @@ -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: @@ -54,6 +64,26 @@ 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 @@ -61,8 +91,8 @@ 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 @@ -70,10 +100,17 @@ 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`: @@ -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 @@ -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 @@ -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 @@ -173,9 +210,11 @@ agent scope and wrote Agent Trajectory Interchange Format (ATIF): ls .nemo-relay/atif ``` -The gateway exports `.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-.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 @@ -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.