1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// SPDX-License-Identifier: Apache-2.0
//! Attribution and summary helpers for `ctx_read`.
//!
//! Split out of `ctx_read.rs` to keep that file under the #660 LOC gate. These
//! are leaf helpers with no access to the read pipeline's local state.
use std::sync::atomic::Ordering;
use crate::server::tool_trait::{ToolContext, ToolOutput};
pub(super) fn record_attribution_result(ctx: &ToolContext, source: String, output: &ToolOutput) {
let session_id = lean_ctx::core::task_spine::TaskSpine::task_id()
.or_else(|| {
ctx.session
.as_ref()
.and_then(|session| session.try_read().ok().map(|state| state.id.clone()))
})
.unwrap_or_else(|| "mcp-session".to_string());
let turn_provided = ctx
.call_count
.as_ref()
.map_or(0, |count| count.load(Ordering::Relaxed) as u64);
let token_cost = lean_ctx::core::tokens::count_tokens(&output.text);
let chunk = lean_ctx::core::causal_attribution::ContextChunkRecord::new(
&output.text,
source,
token_cost,
turn_provided,
);
if let Err(error) = lean_ctx::core::causal_attribution::record_chunk(&session_id, chunk) {
tracing::debug!(%error, "causal attribution ctx_read recording failed");
}
}
pub(super) fn extract_file_summary(output: &str, path: &str) -> String {
let hint = lean_ctx::core::auto_findings::extract_content_hint(output);
if !hint.is_empty() {
return hint;
}
let ext = std::path::Path::new(path)
.extension()
.and_then(|e| e.to_str())
.unwrap_or("");
let line_count = output.lines().count();
if line_count > 5 {
format!("{ext} file, {line_count} lines")
} else {
String::new()
}
}
/// May a session task with this `intent` steer how a file is read?
///
/// Only a task the caller actually stated may. `auto_infer_task` marks the
/// descriptions it fabricates from touched-file patterns — "Working on
/// /repo/src/printer (explore)" — with `intent = "inferred"`. Those words are a
/// telemetry label, not a statement about any file's contents, and letting them
/// drive the information-bottleneck filter or the intent-target override
/// silently answered a question the caller never asked (#1590).
///
/// The same holds for `intent = "git"`: a summary of the working tree's dirty
/// files describes *state*, often left over from unrelated work, not what the
/// caller is asking about — filtering reads by "files changed, insertions"
/// dropped the very lines the caller wanted.
pub(crate) fn task_intent_steers_read(intent: Option<&str>) -> bool {
!lean_ctx::core::session::is_auto_task_intent(intent)
}