Skip to content

lsp hover notes never reach LLM: last_msg_has_tool_results always blocks injection #3595

@bug-ops

Description

@bug-ops

Description

LSP hover notes collected via LspHookRunner::after_tool(\"read\") are never injected into the LLM context. The drain_notes call in the tool loop is guarded by !last_msg_has_tool_results(), which returns true at every injection point in the tool loop — because tool loop runs inside a sequence where the last non-system message is always a ToolResult.

Reproduction Steps

  1. Enable [lsp] enabled = true and [lsp.hover] enabled = true in config
  2. Connect mcpls MCP server
  3. Ask the agent to read a Rust source file
  4. Observe: RUST_LOG=debug logs show LSP hover: injecting hover note entries=N estimated_tokens=M (hover data collected from mcpls successfully)
  5. Observe: debug dump *-request.json has no [lsp hover] system message in the messages array
  6. Confirm: last_msg_has_tool_results() returns true at the injection point (line 766 of native.rs) after every tool batch — hover notes are dropped silently

Expected Behavior

After reading a Rust file, hover notes ([lsp hover]\n<hover info>) should appear as a Role::System message in the next LLM call, making type signatures and documentation available to the LLM.

Actual Behavior

Hover notes are collected (logged as LSP hover: injecting hover note entries=N) but immediately dropped because last_msg_has_tool_results() is always true at the only injection point. The [lsp hover] system message never appears in any LLM request dump.

Environment

  • Version: v0.20.1 (HEAD 4e77b4f)
  • Features: --features full
  • Config: [lsp] enabled = true, [lsp.hover] enabled = true, mcpls connected

Logs / Evidence

From RUST_LOG=debug session:

LSP hover: extracted symbol positions path=crates/.../hover.rs symbols=5 extractor="tree-sitter"
LSP hook: queuing hover fetch path=... symbols=5 concurrency=3 timeout_secs=10
LSP hover: injecting hover note path=... entries=5 estimated_tokens=598

Debug dump 0002-request.json: 4 system messages present, none contains [lsp hover].

Relevant code: crates/zeph-core/src/agent/tool_execution/native.rs:764-778

The fix should defer hover injection to a point in the loop where tool results are not the last non-system message, or inject after tool-result messages by restructuring the injection order.

Root Cause

native.rs:766:

if !last_msg_has_tool_results(&self.msg.messages) {
    // inject hover — never reached during tool loop
}

The guard correctly prevents OpenAI 400 errors (system between assistant+tool_result), but incorrectly blocks all hover injection. The hover note should be injected after draining tool results (i.e., before the next LLM call, not between tool_calls and tool_results).

Metadata

Metadata

Assignees

Labels

P2High value, medium complexitybugSomething isn't workingllmzeph-llm crate (Ollama, Claude)

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions