supercode_harness/reduce/
session_adapter.rs1use 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
11pub fn project(
14 session: &Session,
15 policy: &ReductionPolicy,
16 prior: &ReductionLog,
17) -> (Vec<ChatMessage>, ReductionLog) {
18 project_messages(&session.messages, policy, prior)
19}
20
21pub 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
30pub fn verify_log(log: &ReductionLog, sidecar: &Session) -> Result<()> {
32 verify_log_messages(log, &sidecar.messages)?;
33 Ok(())
34}
35
36pub 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
51pub 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
58pub 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
67pub 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}