From 33edc71c47b5959678f2c05d111895b96e530b28 Mon Sep 17 00:00:00 2001 From: Will Killian Date: Fri, 29 May 2026 16:22:09 -0400 Subject: [PATCH] fix: register adaptive plugin in doctor Signed-off-by: Will Killian --- crates/cli/src/doctor.rs | 9 +++++ crates/cli/tests/coverage/doctor_tests.rs | 37 +++++++++++++++++++++ docs/nemo-relay-cli/about.mdx | 2 +- docs/nemo-relay-cli/cursor.mdx | 14 ++++++++ integrations/coding-agents/cursor/README.md | 12 +++++++ 5 files changed, 73 insertions(+), 1 deletion(-) diff --git a/crates/cli/src/doctor.rs b/crates/cli/src/doctor.rs index 14ae0e51..2d93cc94 100644 --- a/crates/cli/src/doctor.rs +++ b/crates/cli/src/doctor.rs @@ -15,6 +15,7 @@ use std::time::Duration; use nemo_relay::observability::plugin_component::OBSERVABILITY_PLUGIN_KIND; use nemo_relay::plugin::{DiagnosticLevel, PluginConfig, validate_plugin_config}; +use nemo_relay_adaptive::plugin_component::register_adaptive_component; use serde::Serialize; use serde_json::Value; use tokio::time::timeout; @@ -592,6 +593,14 @@ async fn collect_observability(gateway: &GatewayConfig) -> Vec { return checks; } }; + if let Err(error) = register_adaptive_component() { + checks.push(Check { + name: "Adaptive plugin", + status: Status::Fail, + details: format!("registration failed: {error}"), + }); + return checks; + } let report = validate_plugin_config(&plugin_config); if report.diagnostics.is_empty() { checks.push(Check { diff --git a/crates/cli/tests/coverage/doctor_tests.rs b/crates/cli/tests/coverage/doctor_tests.rs index b924d74c..6cfcabdd 100644 --- a/crates/cli/tests/coverage/doctor_tests.rs +++ b/crates/cli/tests/coverage/doctor_tests.rs @@ -620,6 +620,43 @@ async fn collect_observability_warns_for_missing_atif_dir_without_creating_it() assert!(!missing.exists()); } +#[tokio::test] +async fn collect_observability_registers_adaptive_before_validation() { + let gateway = GatewayConfig { + plugin_config: Some(serde_json::json!({ + "version": 1, + "components": [ + { + "kind": "observability", + "enabled": true, + "config": { "version": 1 } + }, + { + "kind": "adaptive", + "enabled": false, + "config": { + "policy": { + "unknown_component": "warn", + "unknown_field": "warn", + "unsupported_value": "error" + } + } + } + ] + })), + ..GatewayConfig::default() + }; + + let checks = collect_observability(&gateway).await; + + assert!( + !checks.iter().any(|check| check + .details + .contains("plugin component kind 'adaptive' is unsupported")), + "doctor should register adaptive before plugin validation: {checks:?}" + ); +} + #[test] fn format_agents_human_lists_supported_and_separates_detected() { let agents = vec![ diff --git a/docs/nemo-relay-cli/about.mdx b/docs/nemo-relay-cli/about.mdx index d3350e44..c9fc082e 100644 --- a/docs/nemo-relay-cli/about.mdx +++ b/docs/nemo-relay-cli/about.mdx @@ -42,7 +42,7 @@ NeMo Relay CLI support is experimental and observability-focused. | Claude Code | ✅ Yes | ❌ No | ❌ No | Observability only; no known issues. | | Codex | ✅ Yes | ❌ No | ❌ No | Observability only; some hooks needed for full feature coverage are missing. | | Hermes Agent | ✅ Yes | ❌ No | ❌ No | Observability only; no known issues. | -| Cursor | ✅ Yes | ❌ No | ❌ No | Observability only; missing hooks under `cursor-agent` limit feature coverage. | +| Cursor | 🚧 Partial | ❌ No | ❌ No | Observability only; highly experimental | ## Guides diff --git a/docs/nemo-relay-cli/cursor.mdx b/docs/nemo-relay-cli/cursor.mdx index 727c407a..b1b195df 100644 --- a/docs/nemo-relay-cli/cursor.mdx +++ b/docs/nemo-relay-cli/cursor.mdx @@ -12,6 +12,20 @@ repository ships a Cursor hook bundle under `integrations/coding-agents/cursor/` because this integration does not assume an official Cursor plugin package format. + +Cursor support is highly experimental and limited. NeMo Relay can install or +temporarily patch Cursor hooks, but it cannot automatically route Cursor model +traffic through the gateway. Complete LLM observability requires manual Cursor +provider/proxy configuration, including any required API keys, and that +configuration is outside NeMo Relay's control. + +Cursor subagents may choose or inherit models independently from the top-level +session. If those subagent calls bypass the NeMo Relay gateway, their LLM +requests and responses will not appear in NeMo Relay events even when hook +events are present. + + + Cursor GUI or IDE sessions can provide agent, subagent, tool, shell, MCP, file, and response lifecycle events through `.cursor/hooks.json`. Complete LLM lifecycle observability additionally requires Cursor model traffic to route diff --git a/integrations/coding-agents/cursor/README.md b/integrations/coding-agents/cursor/README.md index 0735899d..96e93bfd 100644 --- a/integrations/coding-agents/cursor/README.md +++ b/integrations/coding-agents/cursor/README.md @@ -9,6 +9,18 @@ This package is a Cursor hook bundle, not an official Cursor plugin package. It contains `.cursor/hooks.json` entries that forward canonical Cursor hook JSON to `nemo-relay` at `/hooks/cursor`. +> [!CAUTION] +> Cursor support is highly experimental and limited. NeMo Relay can install or +> temporarily patch Cursor hooks, but it cannot automatically route Cursor model +> traffic through the gateway. Complete LLM observability requires manual Cursor +> provider/proxy configuration, including any required API keys, and that +> configuration is outside NeMo Relay's control. +> +> Cursor subagents may choose or inherit models independently from the top-level +> session. If those subagent calls bypass the NeMo Relay gateway, their LLM +> requests and responses will not appear in NeMo Relay events even when hook +> events are present. + Cursor GUI or IDE sessions can provide agent, subagent, tool, shell, MCP, file, and response lifecycle events through `.cursor/hooks.json`. Complete LLM lifecycle observability additionally requires Cursor model traffic to route