kcode-k1-chat-codex-codec 0.5.0

Native Codex call wrapper validator and structured K1 provider-call codec
Documentation
use kcode_k1_chat_chatend::{
    AGENT_ATTACHMENT_TYPE, AGENT_MESSAGE_TYPE, BoxId, ChatBox, ProviderCall, SYSTEM_MESSAGE_TYPE,
    TOOL_ATTACHMENT_TYPE, TOOL_CALL_TYPE, TOOL_MESSAGE_TYPE, TOOL_RESULT_TYPE,
    USER_ATTACHMENT_TYPE, USER_MESSAGE_TYPE,
};
use kcode_k1_chat_codex_codec::{BoxValue, Codec, project};
use kcode_k1_codex_adapter::{BoxCodec, ToolCall};
use serde_json::{Value, json};

fn call(call_id: &str, name: &str, arguments: Value) -> ToolCall {
    ToolCall {
        call_id: call_id.into(),
        name: name.into(),
        arguments,
    }
}

fn native(wrapper: &str) -> ToolCall {
    call(
        "provider-private-id",
        "call_ktool",
        Value::String(wrapper.into()),
    )
}

#[test]
fn parses_nested_structured_arguments_in_provider_order() {
    let mut codec = Codec;
    let BoxValue::Call(Ok(provider)) = codec.tool_call_box(&native(
        r#"{"name":"run","arguments":{"b":[2,{"z":false,"a":true}],"a":true}}"#,
    )) else {
        panic!("native wrapper must produce a provider call");
    };
    assert_eq!(
        provider,
        ProviderCall {
            tool: "run".into(),
            tool_version: "1.0.0".into(),
            arguments: json!({"b":[2,{"z":false,"a":true}],"a":true}),
        }
    );
    assert_eq!(
        serde_json::to_string(&provider.arguments).unwrap(),
        r#"{"b":[2,{"z":false,"a":true}],"a":true}"#
    );
}

#[test]
fn rejects_malformed_extra_and_reserved_native_wrappers() {
    let mut codec = Codec;
    let cases = [
        (
            call("id", "other", Value::String("{}".into())),
            "tool name must be call_ktool",
        ),
        (
            call("id", "call_ktool", json!({})),
            "tool arguments must be a JSON string",
        ),
        (native("{"), "tool arguments must be valid JSON"),
        (native("[]"), "tool arguments must be an object"),
        (
            native(r#"{"name":"x"}"#),
            "tool arguments must have name and arguments",
        ),
        (
            native(r#"{"name":"x","arguments":{},"extra":true}"#),
            "tool arguments must have name and arguments",
        ),
        (
            native(r#"{"name":1,"arguments":{}}"#),
            "tool arguments name must be a string",
        ),
        (
            native(r#"{"name":"x","arguments":null}"#),
            "tool arguments must be an object",
        ),
        (
            native(r#"{"name":"x","arguments":{"tool":"reserved"}}"#),
            "tool arguments may not contain reserved tool",
        ),
    ];

    for (call, error) in cases {
        assert_eq!(
            codec.tool_call_box(&call),
            BoxValue::Call(Err(error.into()))
        );
    }
}

#[test]
fn provider_native_id_is_not_adopted() {
    let mut codec = Codec;
    let BoxValue::Call(Ok(provider)) = codec.tool_call_box(&call(
        "native-99",
        "call_ktool",
        Value::String(r#"{"name":"status","arguments":{}}"#.into()),
    )) else {
        panic!("valid call expected");
    };
    assert_eq!(provider.tool, "status");
    assert_eq!(provider.tool_version, "1.0.0");
    assert_eq!(provider.arguments, json!({}));
}

#[test]
fn projects_all_familiar_types_and_omits_hidden_fields() {
    let box_types = [
        SYSTEM_MESSAGE_TYPE,
        USER_MESSAGE_TYPE,
        AGENT_MESSAGE_TYPE,
        USER_ATTACHMENT_TYPE,
        AGENT_ATTACHMENT_TYPE,
        TOOL_CALL_TYPE,
        TOOL_MESSAGE_TYPE,
        TOOL_ATTACHMENT_TYPE,
        TOOL_RESULT_TYPE,
    ];
    for (index, box_type) in box_types.into_iter().enumerate() {
        let id = u64::try_from(index + 1).unwrap();
        let box_ = ChatBox::new(
            BoxId::new(id),
            box_type.into(),
            format!("visible {id}"),
            "HIDDEN-TYPE".into(),
            "HIDDEN-CONTENTS".into(),
        );
        assert_eq!(
            project(&box_),
            BoxValue::History(format!("[Box {id} | {box_type}]\nvisible {id}"))
        );
    }
}

#[test]
fn text_and_calls_remain_terminal() {
    let unknown = ChatBox::new(
        BoxId::new(10),
        "Future\nType".into(),
        "literal\ntext".into(),
        "secret".into(),
        "secret".into(),
    );
    assert_eq!(
        project(&unknown),
        BoxValue::History("[Box 10 | Future\nType]\nliteral\ntext".into())
    );
    assert_eq!(Codec.box_text(&BoxValue::Call(Err("bad".into()))), "");
    assert_eq!(
        Codec.box_text(&BoxValue::History("history".into())),
        "history"
    );
}