magi-code 0.64.0

Repository-aware CLI coding agent for terminal work
Documentation
use super::*;

fn enabled_ttsr_settings() -> crate::config::TtsrSettings {
    crate::config::TtsrSettings {
        enabled: true,
        rules: Vec::new(),
    }
}

fn ttsr_run_request<'a, 'sink>(
    prompt: &'a str,
    cwd: &'a std::path::Path,
    session: Option<&'a crate::sessions::Session>,
    output_sink: Option<&'sink mut dyn AgentOutputSink>,
) -> AgentRunRequest<'a, 'sink> {
    AgentRunRequest {
        prompt,
        prompt_origin: crate::output::UserPromptOrigin::User,
        effective_prompt: None,
        tools: None,
        hooks: None,
        session,
        cwd,
        output_sink,
        cancellation: AgentCancellation::default(),
        session_title_job: None,
        semantic_progress_timeout: None,
        invocation_mode: crate::output::InvocationMode::Print,
        agent_id: None,
        initial_instructions: &[],
        ttsr: enabled_ttsr_settings(),
        herdr_reporter: None,
        continuation_auto_compaction_policy: None,
    }
}

#[test]
fn ttsr_stream_match_blocks_delta_before_sink() {
    let temp = tempfile::TempDir::new().unwrap();
    let session = crate::sessions::SessionManager::new(temp.path().join("sessions"))
        .create()
        .unwrap();
    let provider = ScriptedProvider::new(vec![vec![text("safe "), text("rm -rf / "), done()]]);
    let agent = AgentSession::new("model", &[], &SkillDiscovery::default());
    let mut sink = CapturingOutputSink::default();

    let output = agent
        .run_print_with_tools_streaming_output_cancellable(
            &provider,
            ttsr_run_request("say", temp.path(), Some(&session), Some(&mut sink)),
        )
        .unwrap();

    assert_eq!(output.text, "safe ");
    assert_eq!(sink.text, "safe ");
    assert!(!assistant_chunk_text(&session.read_events().unwrap()).contains("rm -rf"));
    assert!(event_types(&session).contains(&"ttsr_injection".to_string()));
}

#[test]
fn ttsr_match_aborts_and_retries_with_reminder() {
    let temp = tempfile::TempDir::new().unwrap();
    let session = crate::sessions::SessionManager::new(temp.path().join("sessions"))
        .create()
        .unwrap();
    let provider = ScriptedProvider::new(vec![
        vec![text("git reset --hard"), done()],
        text_done(" safe retry"),
    ]);
    let agent = AgentSession::new("model", &[], &SkillDiscovery::default());

    let output = agent
        .run_print_with_tools_streaming_output_cancellable(
            &provider,
            ttsr_run_request("say", temp.path(), Some(&session), None),
        )
        .unwrap();

    assert_eq!(output.text, " safe retry");
    assert_eq!(provider.requests().len(), 2);
    assert!(provider.requests()[1].messages().iter().any(|message| {
        message.role == crate::providers::MessageRole::System
            && message.content.contains("Git safety reminder")
    }));
    let events = session.read_events().unwrap();
    assert!(
        events
            .iter()
            .any(|event| event.event_type == "ttsr_injection")
    );
    assert!(!events.iter().any(|event| {
        event.event_type == "turn_status" && event.payload["status"] == "cancelled"
    }));
}

#[test]
fn ttsr_retry_blocks_soft_compaction_after_tool_growth() {
    let temp = tempfile::TempDir::new().unwrap();
    std::fs::write(temp.path().join("file.txt"), "large enough").unwrap();
    let provider = ScriptedProvider::new(vec![
        vec![text("git reset --hard"), done()],
        read_done("recovery_read"),
        text_done("safe after tool"),
    ]);
    let tools = ToolRuntime::new(temp.path()).unwrap();
    let agent = AgentSession::new("model", &[], &SkillDiscovery::default());
    let mut request = ttsr_run_request("say", temp.path(), None, None);
    request.tools = Some(&tools);
    request.continuation_auto_compaction_policy = Some((1, "1 token".to_string()));

    let output = agent
        .run_print_with_tools_streaming_output_cancellable(&provider, request)
        .unwrap();

    assert_eq!(provider.requests().len(), 3);
    assert_eq!(output.text, "safe after tool");
    assert!(output.auto_compaction_blocked_by_recovery);
}

#[test]
fn ttsr_request_cancellation_does_not_poison_outer_user_cancellation() {
    let temp = tempfile::TempDir::new().unwrap();
    let provider = ScriptedProvider::new(vec![
        vec![text("git reset --hard"), done()],
        text_done("safe after retry"),
    ]);
    let agent = AgentSession::new("model", &[], &SkillDiscovery::default());

    let output = agent
        .run_print_with_tools_streaming_output_cancellable(
            &provider,
            ttsr_run_request("say", temp.path(), None, None),
        )
        .unwrap();

    assert_eq!(output.text, "safe after retry");
    assert_eq!(provider.requests().len(), 2);
}

#[test]
fn ttsr_completed_tool_call_arguments_are_checked_before_tool_lifecycle() {
    let temp = tempfile::TempDir::new().unwrap();
    let session = crate::sessions::SessionManager::new(temp.path().join("sessions"))
        .create()
        .unwrap();
    let provider = ScriptedProvider::new(vec![
        vec![bash_call("bad", "rm -rf / "), done()],
        text_done("no tool"),
    ]);
    let agent = AgentSession::new("model", &[], &SkillDiscovery::default());

    let output = agent
        .run_print_with_tools_streaming_output_cancellable(
            &provider,
            ttsr_run_request("use tool", temp.path(), Some(&session), None),
        )
        .unwrap();

    assert_eq!(output.text, "no tool");
    assert!(!event_types(&session).contains(&"tool_call".to_string()));
    assert!(event_types(&session).contains(&"ttsr_injection".to_string()));
}

#[test]
fn ttsr_retry_exhaustion_records_failed_turn() {
    let temp = tempfile::TempDir::new().unwrap();
    let session = crate::sessions::SessionManager::new(temp.path().join("sessions"))
        .create()
        .unwrap();
    let provider = ScriptedProvider::new(vec![
        vec![text("git clean -fd"), done()],
        vec![text("git clean -fd"), done()],
        vec![text("git clean -fd"), done()],
        vec![text("git clean -fd"), done()],
    ]);
    let agent = AgentSession::new("model", &[], &SkillDiscovery::default());

    let error = agent
        .run_print_with_tools_streaming_output_cancellable(
            &provider,
            ttsr_run_request("say", temp.path(), Some(&session), None),
        )
        .unwrap_err()
        .to_string();

    assert!(
        error.contains("TTSR") || error.contains("interrupted"),
        "{error}"
    );
    let events = session.read_events().unwrap();
    assert_eq!(
        events
            .iter()
            .filter(|event| event.event_type == "ttsr_injection")
            .count(),
        3
    );
    assert!(
        events.iter().any(|event| {
            event.event_type == "turn_status" && event.payload["status"] == "failed"
        })
    );
}

#[test]
fn invalid_ttsr_config_fails_before_durable_turn_start() {
    let temp = tempfile::TempDir::new().unwrap();
    let session = crate::sessions::SessionManager::new(temp.path().join("sessions"))
        .create()
        .unwrap();
    let provider = ScriptedProvider::new(vec![text_done("unreachable")]);
    let agent = AgentSession::new("model", &[], &SkillDiscovery::default());
    let mut request = ttsr_run_request("say", temp.path(), Some(&session), None);
    request.ttsr.rules.push(crate::config::TtsrRuleSetting {
        pattern: "[".to_string(),
        reminder: "invalid".to_string(),
    });

    let error = agent
        .run_print_with_tools_streaming_output_cancellable(&provider, request)
        .unwrap_err();

    assert!(error.to_string().contains("regex"));
    assert!(provider.requests().is_empty());
    assert!(session.read_events().unwrap().is_empty());
}