Skip to main content

supercode_harness/reduce/
session_adapter.rs

1//! Session-container adapters over the message-slice reduction engine.
2
3use super::{
4    invert_messages, invert_one_messages, project_messages, stub, verify_log_messages,
5    ReductionLog, ReductionPolicy, REDUCTION_SENTINEL,
6};
7use crate::message::ChatMessage;
8use crate::session::{Session, SessionFormat};
9use crate::{Error, Result};
10
11/// Pure function of `(session, policy, prior)`: the compatibility entry point
12/// producing what the model sees (SPEC.md A5).
13pub fn project(
14    session: &Session,
15    policy: &ReductionPolicy,
16    prior: &ReductionLog,
17) -> (Vec<ChatMessage>, ReductionLog) {
18    project_messages(&session.messages, policy, prior)
19}
20
21/// Reconstruct a full view from a reduced view and sidecar session.
22pub fn invert(
23    reduced: &[ChatMessage],
24    log: &ReductionLog,
25    sidecar_session: &Session,
26) -> Result<Vec<ChatMessage>> {
27    Ok(invert_messages(reduced, log, &sidecar_session.messages)?)
28}
29
30/// Verify every reduction against a sidecar session.
31pub fn verify_log(log: &ReductionLog, sidecar: &Session) -> Result<()> {
32    verify_log_messages(log, &sidecar.messages)?;
33    Ok(())
34}
35
36/// Rehydrate one reduction by id against a sidecar session.
37pub fn invert_one(
38    reduced: &[ChatMessage],
39    log: &ReductionLog,
40    id: &str,
41    sidecar_session: &Session,
42) -> Result<(Vec<ChatMessage>, ReductionLog)> {
43    Ok(invert_one_messages(
44        reduced,
45        log,
46        id,
47        &sidecar_session.messages,
48    )?)
49}
50
51/// Export a live session from its full-fidelity sidecar.
52pub fn export_session(sidecar: &str, format: SessionFormat) -> Result<String> {
53    let session = Session::from_sidecar_str(sidecar)?;
54    reject_reduced_sidecar(&session, "export_session")?;
55    Ok(session.to_jsonl(format)?)
56}
57
58/// Export a live session while replaying an imported native prefix verbatim.
59pub fn export_session_spliced(
60    sidecar: &str,
61    format: SessionFormat,
62    session_id: Option<&str>,
63) -> Result<String> {
64    export_session_spliced_with_overrides(sidecar, format, session_id, None)
65}
66
67/// Spliced sidecar export with an optional working-directory override.
68pub fn export_session_spliced_with_overrides(
69    sidecar: &str,
70    format: SessionFormat,
71    session_id: Option<&str>,
72    cwd: Option<&std::path::Path>,
73) -> Result<String> {
74    let mut session = Session::from_sidecar_str(sidecar)?;
75    reject_reduced_sidecar(&session, "export_session_spliced")?;
76    if let Some(cwd) = cwd {
77        session.meta.cwd = Some(cwd.to_path_buf());
78    }
79    Ok(session.to_jsonl_spliced(format, session_id)?)
80}
81
82fn reject_reduced_sidecar(session: &Session, operation: &str) -> Result<()> {
83    if session_contains_reduction_stub(session) {
84        return Err(Error::Other(format!(
85            "{operation}: refusing to export — the sidecar contains a grammar-valid \
86             reduction stub beginning with {REDUCTION_SENTINEL:?}; a reduced view leaked \
87             into an export path that must only read the full-fidelity sidecar"
88        )));
89    }
90    Ok(())
91}
92
93fn string_contains_reduction_stub(value: &str) -> bool {
94    value.lines().any(|line| stub::parse(line).is_some())
95}
96
97fn json_value_contains_reduction_stub(value: &serde_json::Value) -> bool {
98    match value {
99        serde_json::Value::String(value) => string_contains_reduction_stub(value),
100        serde_json::Value::Array(values) => values.iter().any(json_value_contains_reduction_stub),
101        serde_json::Value::Object(fields) => {
102            fields.values().any(json_value_contains_reduction_stub)
103        }
104        _ => false,
105    }
106}
107
108fn session_contains_reduction_stub(session: &Session) -> bool {
109    session.messages.iter().any(|message| {
110        message
111            .content
112            .as_deref()
113            .is_some_and(string_contains_reduction_stub)
114            || message
115                .content_parts
116                .as_ref()
117                .is_some_and(|parts| parts.iter().any(json_value_contains_reduction_stub))
118            || message.tool_calls().iter().any(|call| {
119                serde_json::from_str::<serde_json::Value>(&call.function.arguments)
120                    .map(|value| json_value_contains_reduction_stub(&value))
121                    .unwrap_or_else(|_| string_contains_reduction_stub(&call.function.arguments))
122            })
123    })
124}