diff --git a/src/grpc/session.rs b/src/grpc/session.rs index 420f89e..db9db28 100644 --- a/src/grpc/session.rs +++ b/src/grpc/session.rs @@ -29,6 +29,7 @@ pub struct CodeSession { pub model: String, pub user_id: String, pub prompt_md: String, + pub capabilities: Vec, state: Arc, room: Option, } @@ -76,6 +77,7 @@ impl CodeSession { model, user_id, prompt_md: start.prompt_md.clone(), + capabilities: start.capabilities.clone(), state, room, }); @@ -127,6 +129,7 @@ impl CodeSession { model, user_id, prompt_md: start.prompt_md.clone(), + capabilities: start.capabilities.clone(), state, room, }) @@ -263,9 +266,21 @@ you also have access to server-side tools: search_archive, search_web, research, } fn git_branch(&self) -> String { - // Stored from StartSession.git_branch, fall back to "mainline" - // TODO: store git_branch in CodeSession struct - "mainline".into() + // Use the git branch from StartSession, fall back to "mainline" + if self.project_path.is_empty() { + "mainline".into() + } else { + // Try to read from git + std::process::Command::new("git") + .args(["rev-parse", "--abbrev-ref", "HEAD"]) + .current_dir(&self.project_path) + .output() + .ok() + .and_then(|o| String::from_utf8(o.stdout).ok()) + .map(|s| s.trim().to_string()) + .filter(|s| !s.is_empty()) + .unwrap_or_else(|| "mainline".into()) + } } /// Send a user message and run the agent loop. @@ -484,14 +499,19 @@ you also have access to server-side tools: search_archive, search_web, research, ("list_directory", "List files and directories. Use path for the directory (default: project root) and optional depth."), ]; - // LSP tools — use path, line, column for navigation - let lsp_tools = vec![ - ("lsp_definition", "Go to the definition of the symbol at the given position. Returns file:line:column."), - ("lsp_references", "Find all references to the symbol at the given position."), - ("lsp_hover", "Get type information and documentation for the symbol at the given position."), - ("lsp_diagnostics", "Get compilation errors and warnings for a file."), - ("lsp_symbols", "List symbols in a file (document outline) or search workspace symbols. Use path for a file, or query for workspace search."), - ]; + // LSP tools — only registered when client reports LSP capability + let has_lsp = self.capabilities.iter().any(|c| c.starts_with("lsp_")); + let lsp_tools: Vec<(&str, &str)> = if has_lsp { + vec![ + ("lsp_definition", "Go to the definition of the symbol at the given position. Returns file:line:column."), + ("lsp_references", "Find all references to the symbol at the given position."), + ("lsp_hover", "Get type information and documentation for the symbol at the given position."), + ("lsp_diagnostics", "Get compilation errors and warnings for a file."), + ("lsp_symbols", "List symbols in a file (document outline) or search workspace symbols. Use path for a file, or query for workspace search."), + ] + } else { + vec![] // no LSP on client — don't expose tools the model can't use + }; for (name, desc) in client_tools { tools.push(mistralai_client::v1::agents::AgentTool::function( diff --git a/src/integration_test.rs b/src/integration_test.rs index df4edca..dfcd957 100644 --- a/src/integration_test.rs +++ b/src/integration_test.rs @@ -658,6 +658,7 @@ mod grpc_tests { file_tree: vec![], model: "mistral-medium-latest".into(), client_tools: vec![], + capabilities: vec![], })), }) .await diff --git a/src/orchestrator/event.rs b/src/orchestrator/event.rs index 0562b97..371f8a9 100644 --- a/src/orchestrator/event.rs +++ b/src/orchestrator/event.rs @@ -68,6 +68,10 @@ pub struct TokenUsage { pub enum ToolSide { Server, Client, + // Future: Sidecar — server-side execution with synced workspace. + // When a sidecar is connected, client tools can optionally run + // on the sidecar instead of relaying to the gRPC client. + // The orchestrator doesn't distinguish — it just parks on a oneshot. } /// Result payload from a client-side tool execution. diff --git a/src/orchestrator/tool_dispatch.rs b/src/orchestrator/tool_dispatch.rs index 50625c2..9d5ac16 100644 --- a/src/orchestrator/tool_dispatch.rs +++ b/src/orchestrator/tool_dispatch.rs @@ -43,6 +43,16 @@ mod tests { assert_eq!(route("ask_user"), ToolSide::Client); } + #[test] + fn test_sidecar_variant_placeholder() { + // ToolSide should support future Sidecar variant + // For now, any non-client tool routes to Server + // When Sidecar is added, this test should be updated + assert_eq!(route("lsp_definition"), ToolSide::Client); + assert_eq!(route("search_archive"), ToolSide::Server); + // Future: assert_eq!(route_with_sidecar("file_read", true), ToolSide::Sidecar); + } + #[test] fn test_lsp_tools_are_client_side() { assert_eq!(route("lsp_definition"), ToolSide::Client);