feat: code search tool + breadcrumb context injection + integration tests

search_code tool:
- Server-side tool querying sol_code OpenSearch index
- BM25 search across symbol_name, signature, docstring, content
- Branch-aware with boost for current branch, mainline fallback
- Registered in ToolRegistry execute dispatch

Breadcrumb injection:
- build_context_header() now async, injects adaptive breadcrumbs
- Hybrid search: _analyze → wildcard symbol matching → BM25
- Token budget enforcement (default outline + relevant expansion)
- Graceful degradation when OpenSearch unavailable

GrpcState:
- Added Option<OpenSearch> for breadcrumb retrieval
- code_index_name() accessor

Integration tests (6 new, 226 total):
- Index + search: bulk index symbols, verify BM25 retrieval
- Breadcrumb outline: aggregation query returns project structure
- Breadcrumb expansion: substantive query triggers relevant symbols
- Token budget: respects character limit
- Branch scoping: feat/code symbols preferred over mainline
- Branch deletion: cleanup removes branch symbols, mainline survives
This commit is contained in:
2026-03-24 00:19:17 +00:00
parent 57f8d608a5
commit c213d74620
8 changed files with 563 additions and 25 deletions

View File

@@ -324,6 +324,16 @@ async fn main() -> anyhow::Result<()> {
store: store.clone(),
mistral: state.mistral.clone(),
matrix: Some(matrix_client.clone()),
opensearch: {
// Rebuild a fresh OpenSearch client (os_client was moved into AppState)
let os_url = url::Url::parse(&config.opensearch.url).ok();
os_url.map(|u| {
let transport = opensearch::http::transport::TransportBuilder::new(
opensearch::http::transport::SingleNodeConnectionPool::new(u),
).build().unwrap();
opensearch::OpenSearch::new(transport)
})
},
system_prompt: system_prompt_text.clone(),
orchestrator_agent_id: orchestrator_id,
orchestrator: Some(orch),