harn-vm 0.10.87

Async bytecode virtual machine for the Harn programming language
Documentation
//! Tool-channel history normalization tests, split from the host tests so the
//! production module keeps its exact source-length ratchet.

use serde_json::json;

use super::super::{
    assistant_message_from_llm_result, dict_get, list_items, record_tool_results_for_test,
    reset_agent_session_host_state, seed_host_session_provider_model, vm_to_json,
};

#[test]
fn text_only_route_repairs_stale_native_history_format() {
    let result = crate::stdlib::json_to_vm_value(&json!({
        "provider": "fireworks",
        "model": "accounts/fireworks/models/gpt-oss-120b",
        "text": "",
        "_agent_tool_format": "native",
        "native_tool_calls": [{
            "id": "call_1",
            "name": "look",
            "arguments": {"file": "README.md"}
        }],
    }));

    let message = vm_to_json(&assistant_message_from_llm_result(&result));

    assert_eq!(message["role"], "assistant");
    let content = message["content"].as_str().expect("text history content");
    assert!(
        content.contains("<tool_call>") && content.contains("look({"),
        "the route's effective text channel must own history shape: {content}"
    );
    assert!(
        message.get("tool_calls").is_none(),
        "a stale requested format must not leak native calls onto a text-only route"
    );
}

#[test]
fn json_text_route_reserializes_native_surprise_in_its_own_grammar() {
    let result = crate::stdlib::json_to_vm_value(&json!({
        "provider": "minimax",
        "model": "MiniMax-M2",
        "text": "",
        "_agent_tool_format": "json",
        "native_tool_calls": [{
            "id": "call_1",
            "name": "look",
            "arguments": {"file": "README.md"}
        }],
    }));

    let message = vm_to_json(&assistant_message_from_llm_result(&result));

    let content = message["content"]
        .as_str()
        .expect("JSON text history content");
    assert!(
        content.starts_with("```tool\n{"),
        "unexpected JSON history: {content}"
    );
    assert!(content.contains("\"name\": \"look\""));
    assert!(content.contains("\"args\""));
    assert!(message.get("tool_calls").is_none());
}

#[test]
fn fireworks_stale_native_turn_records_text_channel_result_end_to_end() {
    reset_agent_session_host_state();
    let session_id =
        crate::agent_sessions::open_or_create(Some("fireworks-stale-native-history".to_string()));
    crate::agent_sessions::claim_tool_format(&session_id, "native")
        .expect("stale native session lock claims");
    seed_host_session_provider_model(
        &session_id,
        "fireworks",
        "accounts/fireworks/models/gpt-oss-120b",
    );
    crate::agent_sessions::inject_message(
        &session_id,
        crate::stdlib::json_to_vm_value(&json!({"role": "user", "content": "inspect"})),
    )
    .expect("user turn injects");

    let llm_result = crate::stdlib::json_to_vm_value(&json!({
        "provider": "fireworks",
        "model": "accounts/fireworks/models/gpt-oss-120b",
        "text": "",
        "_agent_tool_format": "native",
        "native_tool_calls": [{
            "id": "call_1",
            "name": "look",
            "arguments": {"file": "README.md"}
        }],
    }));
    crate::agent_sessions::inject_message(
        &session_id,
        assistant_message_from_llm_result(&llm_result),
    )
    .expect("assistant turn injects");
    record_tool_results_for_test(
        &session_id,
        crate::stdlib::json_to_vm_value(&json!([{
            "tool_name": "look",
            "tool_call_id": "call_1",
            "observation": "[result of look]\nok\n[end of look result]\n",
            "ok": true
        }])),
    );

    let transcript = crate::agent_sessions::transcript(&session_id).expect("transcript");
    let messages = list_items(
        &dict_get(&transcript, "messages")
            .cloned()
            .unwrap_or(crate::value::VmValue::Nil),
    );
    let assistant = vm_to_json(&messages[1]);
    let result = vm_to_json(&messages[2]);
    assert!(assistant["content"]
        .as_str()
        .expect("assistant text history")
        .contains("<tool_call>"));
    assert!(assistant.get("tool_calls").is_none());
    assert_eq!(result["role"], "user");
    assert!(result.get("tool_call_id").is_none());
}