magi-code 0.77.1

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

#[test]
fn dispatch_returns_structured_json() {
    let temp = tempfile::TempDir::new().unwrap();
    let provider = Arc::new(CountingProvider::new("never"));
    let result = dispatch_subagents(
        json!({"tasks":[{"intent":"one","identity":"frontend-dev"}],"concurrency":1}),
        {
            let mut cfg = config(provider, temp.path());
            cfg.profiles.insert(
                "frontend-dev".to_string(),
                profile("frontend-dev", "Frontend prompt"),
            );
            cfg
        },
    );
    assert!(result.success, "{}", result.content);
    assert!(result.content.contains("\"id\": \"g1\""));
    assert!(result.content.contains("\"identity\": \"frontend-dev\""));
}

#[test]
fn dispatch_truncates_large_result_fields_without_changing_json_schema() {
    let mut output = SubagentsOutput {
        summary: SubagentsSummary {
            total: 2,
            completed: 1,
            failed: 1,
            total_tokens: None,
        },
        results: vec![
            SubagentTaskResult {
                id: "g1".to_string(),
                status: SubagentStatus::Completed,
                intent: "large output".to_string(),
                agent: None,
                identity: None,
                cwd: PathBuf::from("/tmp"),
                session_id: Some("child-session-1".to_string()),
                session_path: Some(PathBuf::from("/tmp/subagents/child-session-1.jsonl")),
                total_tokens: None,
                usage: None,
                changed_files: vec![PathBuf::from("src/lib.rs")],
                output: "x".repeat(SUBAGENT_RESULT_OUTPUT_CHAR_LIMIT + 100),
                structured_output: None,
                output_truncated: false,
                error: None,
            },
            SubagentTaskResult {
                id: "g2".to_string(),
                status: SubagentStatus::Failed,
                intent: "large error".to_string(),
                agent: None,
                identity: None,
                cwd: PathBuf::from("/tmp"),
                session_id: Some("child-session-2".to_string()),
                session_path: Some(PathBuf::from("/tmp/subagents/child-session-2.jsonl")),
                total_tokens: None,
                usage: None,
                changed_files: vec![PathBuf::from("src/main.rs")],
                output: String::new(),
                structured_output: None,
                output_truncated: false,
                error: Some("e".repeat(SUBAGENT_RESULT_ERROR_CHAR_LIMIT + 100)),
            },
        ],
    };

    truncate_subagents_output(&mut output);
    let json = serde_json::to_string_pretty(&output).unwrap();
    let value: Value = serde_json::from_str(&json).unwrap();

    assert_eq!(value["summary"]["total"], 2);
    assert_eq!(value["results"].as_array().unwrap().len(), 2);
    assert_eq!(value["results"][0]["id"], "g1");
    assert_eq!(value["results"][0]["status"], "completed");
    assert_eq!(
        value["results"][0]["session_path"],
        "/tmp/subagents/child-session-1.jsonl"
    );
    assert_eq!(value["results"][0]["changed_files"][0], "src/lib.rs");
    assert_eq!(value["results"][0]["output_truncated"], true);
    assert!(value["results"][0]["error"].is_null());
    assert!(
        value["results"][0]["output"]
            .as_str()
            .unwrap()
            .contains(SUBAGENT_TRUNCATION_MARKER.trim_start())
    );
    assert_eq!(value["results"][1]["id"], "g2");
    assert_eq!(value["results"][1]["status"], "failed");
    assert_eq!(
        value["results"][1]["session_path"],
        "/tmp/subagents/child-session-2.jsonl"
    );
    assert_eq!(value["results"][1]["changed_files"][0], "src/main.rs");
    assert_eq!(value["results"][1]["output_truncated"], true);
    assert!(
        value["results"][1]["error"]
            .as_str()
            .unwrap()
            .contains(SUBAGENT_TRUNCATION_MARKER.trim_start())
    );
}

#[test]
fn subagents_structured_output_serializes() {
    let output = SubagentsOutput {
        summary: SubagentsSummary {
            total: 1,
            completed: 1,
            failed: 0,
            total_tokens: None,
        },
        results: vec![SubagentTaskResult {
            id: "g1".to_string(),
            status: SubagentStatus::Completed,
            intent: "structured".to_string(),
            agent: None,
            identity: Some("tars-code-writing-execution".to_string()),
            cwd: PathBuf::from("."),
            session_id: None,
            session_path: None,
            total_tokens: None,
            usage: None,
            changed_files: Vec::new(),
            output: "{}".to_string(),
            structured_output: Some(json!({"phase": "IMPLEMENT"})),
            output_truncated: false,
            error: None,
        }],
    };

    let json = serde_json::to_string_pretty(&SerializableSubagentsOutput::from(&output)).unwrap();
    let value: Value = serde_json::from_str(&json).unwrap();

    assert_eq!(
        value["results"][0]["structured_output"]["phase"],
        "IMPLEMENT"
    );
}

#[cfg(unix)]
#[test]
fn subagents_output_serializes_non_utf8_paths_lossily() {
    use std::ffi::OsString;
    use std::os::unix::ffi::OsStringExt;

    let non_utf8 = PathBuf::from(OsString::from_vec(b"bad-\xFF-path".to_vec()));
    let output = SubagentsOutput {
        summary: SubagentsSummary {
            total: 1,
            completed: 1,
            failed: 0,
            total_tokens: None,
        },
        results: vec![SubagentTaskResult {
            id: "g1".to_string(),
            status: SubagentStatus::Completed,
            intent: "non utf8 paths".to_string(),
            agent: None,
            identity: None,
            cwd: non_utf8.clone(),
            session_id: Some("session".to_string()),
            session_path: Some(non_utf8.clone()),
            total_tokens: None,
            usage: None,
            changed_files: vec![non_utf8],
            output: "done".to_string(),
            structured_output: None,
            output_truncated: false,
            error: None,
        }],
    };

    let json = serde_json::to_string_pretty(&SerializableSubagentsOutput::from(&output)).unwrap();
    let value: Value = serde_json::from_str(&json).unwrap();

    assert_ne!(json, "{}");
    assert_eq!(value["summary"]["total"], 1);
    assert!(value["results"][0]["cwd"].as_str().unwrap().contains(''));
    assert!(
        value["results"][0]["session_path"]
            .as_str()
            .unwrap()
            .contains('')
    );
    assert!(
        value["results"][0]["changed_files"][0]
            .as_str()
            .unwrap()
            .contains('')
    );
}

#[test]
fn subagent_usage_deserializes_legacy_snapshot_shape() {
    let snapshot = crate::output::NormalizedUsageSnapshot {
        effective_input: 7,
        output: 2,
        cache_read: 1,
        cache_known: true,
    };
    let result: SubagentTaskResult = serde_json::from_value(json!({
        "id": "g1",
        "status": "completed",
        "intent": "legacy usage",
        "agent": null,
        "identity": null,
        "cwd": ".",
        "session_id": null,
        "session_path": null,
        "total_tokens": null,
        "usage": {
            "effective_input": 7,
            "output": 2,
            "cache_read": 1,
            "cache_known": true
        },
        "changed_files": [],
        "output": "done",
        "structured_output": null,
        "output_truncated": false,
        "error": null
    }))
    .unwrap();

    assert_eq!(
        result.usage,
        Some(crate::output::NormalizedUsageAggregate {
            whole_run: snapshot,
            latest: Some(snapshot),
            latest_request_sequence: None,
            latest_final: false,
        })
    );
}

#[test]
fn subagent_usage_metadata_round_trips_through_result_serde() {
    let snapshot = crate::output::NormalizedUsageSnapshot {
        effective_input: 18_400,
        output: 1_300,
        cache_read: 7_912,
        cache_known: true,
    };
    let usage = crate::output::NormalizedUsageAggregate {
        whole_run: snapshot,
        latest: None,
        latest_request_sequence: Some(9),
        latest_final: false,
    };
    let result = SubagentTaskResult {
        id: "g1".to_string(),
        status: SubagentStatus::Completed,
        intent: "metadata".to_string(),
        agent: None,
        identity: None,
        cwd: PathBuf::from("."),
        session_id: None,
        session_path: None,
        total_tokens: None,
        usage: Some(usage),
        changed_files: Vec::new(),
        output: "done".to_string(),
        structured_output: None,
        output_truncated: false,
        error: None,
    };

    let encoded = serde_json::to_value(&result).unwrap();
    assert_eq!(encoded["usage"]["latest_request_sequence"], 9);
    assert_eq!(encoded["usage"]["latest_final"], false);
    let decoded: SubagentTaskResult = serde_json::from_value(encoded).unwrap();
    assert_eq!(decoded.usage, Some(usage));
}