supercode-harness 0.4.8

The optional native Supercode agent and tool harness
Documentation
//! TR-13 acceptance proofs: Codex v1 tool outputs have no structured
//! success bit, so they remain explicitly unknown and cannot enter either
//! success-only TR-10 elision or error-only TR-6 aged pruning.

use supercode_harness::reduce::{
    project_messages, tool_outcome, ReductionKind, ReductionLog, ReductionPolicy, ToolOutcome,
};
use supercode_harness::{ChatMessage, Role, Session, SessionFormat};

fn filler(len: usize) -> String {
    (0..len).map(|i| (b'a' + (i % 26) as u8) as char).collect()
}

fn codex_session(output_kind: &str, output: &str) -> Session {
    let payload = filler(20_000);
    let call_kind = match output_kind {
        "function_call_output" => "function_call",
        "custom_tool_call_output" => "custom_tool_call",
        other => panic!("unsupported output kind {other}"),
    };
    let call_payload = if call_kind == "function_call" {
        serde_json::json!({
            "type": call_kind,
            "call_id": "c1",
            "name": "mcp__blobstore__put",
            "arguments": serde_json::json!({"key":"asset/1", "data": payload}).to_string()
        })
    } else {
        serde_json::json!({
            "type": call_kind,
            "call_id": "c1",
            "name": "mcp__blobstore__put",
            "input": {"key":"asset/1", "data": payload}
        })
    };
    let jsonl = [
        serde_json::json!({"type":"response_item", "payload": call_payload}),
        serde_json::json!({
            "type":"response_item",
            "payload":{"type":output_kind, "call_id":"c1", "output":output}
        }),
    ]
    .into_iter()
    .map(|record| serde_json::to_string(&record).unwrap())
    .collect::<Vec<_>>()
    .join("\n")
        + "\n";
    Session::from_codex_str(&jsonl).unwrap()
}

fn opted_in_policy() -> ReductionPolicy {
    let mut policy = ReductionPolicy::default();
    policy
        .tool_input_elidable_fields
        .insert("mcp__blobstore__put".to_string(), "data".to_string());
    policy
}

fn assert_no_tool_input_reduction(log: &ReductionLog) {
    assert!(
        !log.reductions
            .iter()
            .any(|r| matches!(r.kind, ReductionKind::ToolInputElided { .. })),
        "unknown Codex outcome must fail closed on both input-reduction paths: {:?}",
        log.reductions
    );
}

#[test]
fn codex_failed_output_is_neither_tr10_elided_nor_tr6_pruned() {
    let session = codex_session("function_call_output", "Process exited with code 1");
    assert_eq!(session.messages.len(), 2);
    assert_eq!(tool_outcome(&session.messages[1]), ToolOutcome::Unknown);

    let mut tr10_policy = opted_in_policy();
    tr10_policy.prune_errored_inputs = false;
    let (tr10_view, tr10_log) =
        project_messages(&session.messages, &tr10_policy, &ReductionLog::default());
    assert_no_tool_input_reduction(&tr10_log);
    assert_eq!(
        tr10_view[0].tool_calls()[0].function.arguments,
        session.messages[0].tool_calls()[0].function.arguments
    );

    let mut tr6_policy = opted_in_policy();
    tr6_policy.elide_tool_inputs = false;
    tr6_policy.prune_errored_inputs = true;
    tr6_policy.errored_input_prune_after_turns = 0;
    let (tr6_view, tr6_log) =
        project_messages(&session.messages, &tr6_policy, &ReductionLog::default());
    assert_no_tool_input_reduction(&tr6_log);
    assert_eq!(
        tr6_view[0].tool_calls()[0].function.arguments,
        session.messages[0].tool_calls()[0].function.arguments
    );
}

#[test]
fn codex_custom_output_is_also_explicitly_unknown() {
    let session = codex_session("custom_tool_call_output", "tool reported failure");
    assert_eq!(tool_outcome(&session.messages[1]), ToolOutcome::Unknown);
    let (_, log) = project_messages(
        &session.messages,
        &opted_in_policy(),
        &ReductionLog::default(),
    );
    assert_no_tool_input_reduction(&log);
}

#[test]
fn claude_omitted_is_error_and_live_success_remain_known_success() {
    let payload = filler(20_000);
    let claude = format!(
        "{{\"type\":\"assistant\",\"uuid\":\"a1\",\"sessionId\":\"s\",\"message\":{{\"id\":\"m1\",\"role\":\"assistant\",\"content\":[{{\"type\":\"tool_use\",\"id\":\"c1\",\"name\":\"mcp__blobstore__put\",\"input\":{{\"key\":\"asset/1\",\"data\":{payload:?}}}}}]}}}}\n{{\"type\":\"user\",\"uuid\":\"u1\",\"parentUuid\":\"a1\",\"sessionId\":\"s\",\"message\":{{\"role\":\"user\",\"content\":[{{\"type\":\"tool_result\",\"tool_use_id\":\"c1\",\"content\":\"ok\"}}]}}}}\n"
    );
    let imported = Session::from_claude_code_str(&claude).unwrap();
    let claude_result = imported
        .messages
        .iter()
        .find(|message| message.role == Role::Tool)
        .expect("Claude tool result");
    assert_eq!(tool_outcome(claude_result), ToolOutcome::KnownSuccess);
    let (_, claude_log) = project_messages(
        &imported.messages,
        &opted_in_policy(),
        &ReductionLog::default(),
    );
    assert!(claude_log
        .reductions
        .iter()
        .any(|r| matches!(r.kind, ReductionKind::ToolInputElided { .. })));

    let live_call = imported
        .messages
        .iter()
        .find(|message| message.role == Role::Assistant)
        .expect("Claude tool call")
        .clone();
    let live_result = ChatMessage::tool_result("c1", "mcp__blobstore__put", "ok");
    assert_eq!(tool_outcome(&live_result), ToolOutcome::KnownSuccess);
    let (_, live_log) = project_messages(
        &[live_call, live_result],
        &opted_in_policy(),
        &ReductionLog::default(),
    );
    assert!(live_log
        .reductions
        .iter()
        .any(|r| matches!(r.kind, ReductionKind::ToolInputElided { .. })));
}

#[test]
fn codex_unknown_outcome_survives_native_sidecar_disk_round_trip() {
    let imported = codex_session("function_call_output", "Process exited with code 1");
    let empty = Session::from_codex_str("").unwrap();
    let native = empty.to_native_jsonl_v2(&imported.messages);
    assert!(
        native.contains("sc.tool_outcome_unknown"),
        "the appended NativeTurn must persist the explicit marker, not merely re-derive it from a raw Codex prefix"
    );
    let dir = std::env::temp_dir().join(format!(
        "supercode-tr13-{}-{}",
        std::process::id(),
        std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_nanos()
    ));
    std::fs::create_dir_all(&dir).unwrap();
    let path = dir.join("session.sidecar.jsonl");
    std::fs::write(&path, native).unwrap();

    let reloaded = Session::from_native_str(&std::fs::read_to_string(&path).unwrap()).unwrap();
    let result = reloaded
        .messages
        .iter()
        .find(|message| message.role == Role::Tool)
        .expect("reloaded Codex tool result");
    assert_eq!(tool_outcome(result), ToolOutcome::Unknown);

    std::fs::remove_dir_all(dir).ok();
}

#[test]
fn codex_unknown_outcome_survives_claude_translation_round_trip() {
    let imported = codex_session("function_call_output", "Process exited with code 1");
    let claude = imported.to_jsonl(SessionFormat::ClaudeCode).unwrap();
    assert!(claude.contains("_supercode_tool_outcome"));

    let reloaded = Session::from_claude_code_str(&claude).unwrap();
    let result = reloaded
        .messages
        .iter()
        .find(|message| message.role == Role::Tool)
        .expect("translated Claude tool result");
    assert_eq!(tool_outcome(result), ToolOutcome::Unknown);
}

#[test]
fn codex_unknown_outcome_survives_every_format_and_onward_translation() {
    for output_kind in ["function_call_output", "custom_tool_call_output"] {
        let imported = codex_session(output_kind, "Process exited with code 1");
        for format in [
            SessionFormat::ClaudeCode,
            SessionFormat::Codex,
            SessionFormat::OpenCode,
            SessionFormat::Pi,
            SessionFormat::Grok,
        ] {
            let exported = imported.to_jsonl(format).unwrap();
            let reloaded = Session::load_str(&exported, format).unwrap();
            let result = reloaded
                .messages
                .iter()
                .find(|message| message.role == Role::Tool)
                .unwrap_or_else(|| panic!("{output_kind} -> {format:?} dropped the tool result"));
            assert_eq!(
                tool_outcome(result),
                ToolOutcome::Unknown,
                "{output_kind} -> {format:?} upgraded unknown to a known outcome"
            );

            let mut policy = opted_in_policy();
            policy.errored_input_prune_after_turns = 0;
            let (_, log) = project_messages(&reloaded.messages, &policy, &ReductionLog::default());
            assert_no_tool_input_reduction(&log);

            let onward = reloaded.to_jsonl(SessionFormat::ClaudeCode).unwrap();
            let onward_reloaded = Session::from_claude_code_str(&onward).unwrap();
            let onward_result = onward_reloaded
                .messages
                .iter()
                .find(|message| message.role == Role::Tool)
                .unwrap_or_else(|| {
                    panic!("{output_kind} -> {format:?} -> Claude dropped the tool result")
                });
            assert_eq!(
                tool_outcome(onward_result),
                ToolOutcome::Unknown,
                "{output_kind} -> {format:?} -> Claude lost unknown"
            );
        }
    }
}