diff --git a/adapters/python/claude/README.md b/adapters/python/claude/README.md index 31c06290..efb10342 100644 --- a/adapters/python/claude/README.md +++ b/adapters/python/claude/README.md @@ -118,8 +118,10 @@ Schema defaults are documentation only; planning preserves the supplied settings without adding `setting_sources`. The adapter filters the inherited environment before launching Claude Code. -It retains portable OS/config variables, the selected model's `api_key_env`, -and explicitly configured `environment.env` values. Raw Claude stderr is consumed +It retains portable OS/config variables, NeMo Relay logging variables +(`NEMO_RELAY_LOG`, `NEMO_RELAY_LOG_CONFIG_PATH`, `NEMO_RELAY_LOG_STDERR`, and +`NEMO_RELAY_LOG_STDERR_FORMAT`), the selected model's `api_key_env`, and +explicitly configured `environment.env` values. Raw Claude stderr is consumed by the SDK and is not persisted as a NeMo Fabric artifact. ## Relay Observability diff --git a/adapters/python/claude/src/nemo_fabric_adapters/claude/adapter.py b/adapters/python/claude/src/nemo_fabric_adapters/claude/adapter.py index 804553c4..c7880fa9 100644 --- a/adapters/python/claude/src/nemo_fabric_adapters/claude/adapter.py +++ b/adapters/python/claude/src/nemo_fabric_adapters/claude/adapter.py @@ -79,6 +79,12 @@ "LC_ALL", "LC_CTYPE", "LOCALAPPDATA", + # Relay hook commands reject these when set to an empty string, and the + # SDK can only override inherited variables, so keep the parent's values. + "NEMO_RELAY_LOG", + "NEMO_RELAY_LOG_CONFIG_PATH", + "NEMO_RELAY_LOG_STDERR", + "NEMO_RELAY_LOG_STDERR_FORMAT", "NO_PROXY", "PATH", "PATHEXT", diff --git a/tests/adapters/test_claude_adapter.py b/tests/adapters/test_claude_adapter.py index dd71f7c9..0a4650e6 100644 --- a/tests/adapters/test_claude_adapter.py +++ b/tests/adapters/test_claude_adapter.py @@ -700,6 +700,36 @@ def test_build_options_adds_relay_plugin_and_gateway_environment( ).exists() +@pytest.mark.parametrize( + "logging_environment", + [ + { + "NEMO_RELAY_LOG": "debug", + "NEMO_RELAY_LOG_STDERR": "true", + "NEMO_RELAY_LOG_STDERR_FORMAT": "jsonl", + }, + {"NEMO_RELAY_LOG_CONFIG_PATH": "/etc/relay/logging.toml"}, + ], +) +def test_build_options_relay_logging_environment_set_forwards_parent_values( + relay_payload, tmp_path, logging_environment +): + # Relay rejects an empty logging variable, so a blanked value makes every + # Claude hook fail before it reaches the gateway. + os.environ.update(logging_environment) + os.environ["NEMO_RELAY_CLIENT_TOKEN"] = "parent-route-token" + os.environ["FABRIC_UNRELATED_SECRET"] = "do-not-forward" + + options = build_options(relay_payload, relay=relay_settings(tmp_path, {})) + + forwarded_logging_environment = { + name: options.env[name] for name in logging_environment + } + assert forwarded_logging_environment == logging_environment + assert options.env["NEMO_RELAY_CLIENT_TOKEN"] == "" + assert options.env["FABRIC_UNRELATED_SECRET"] == "" + + def test_build_options_does_not_enable_skills_for_relay_plugin_alone( relay_payload, tmp_path ):