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"
);
}