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
- Enable
[lsp] enabled = true and [lsp.hover] enabled = true in config
- Connect mcpls MCP server
- Ask the agent to read a Rust source file
- Observe: RUST_LOG=debug logs show
LSP hover: injecting hover note entries=N estimated_tokens=M (hover data collected from mcpls successfully)
- Observe: debug dump
*-request.json has no [lsp hover] system message in the messages array
- 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).
Description
LSP hover notes collected via
LspHookRunner::after_tool(\"read\")are never injected into the LLM context. Thedrain_notescall in the tool loop is guarded by!last_msg_has_tool_results(), which returnstrueat 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
[lsp] enabled = trueand[lsp.hover] enabled = truein configLSP hover: injecting hover note entries=N estimated_tokens=M(hover data collected from mcpls successfully)*-request.jsonhas no[lsp hover]system message in the messages arraylast_msg_has_tool_results()returnstrueat the injection point (line 766 ofnative.rs) after every tool batch — hover notes are dropped silentlyExpected Behavior
After reading a Rust file, hover notes (
[lsp hover]\n<hover info>) should appear as aRole::Systemmessage 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 becauselast_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
--features full[lsp] enabled = true,[lsp.hover] enabled = true, mcpls connectedLogs / Evidence
From RUST_LOG=debug session:
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-778The 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: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).