codex-mobile-wire 0.3.8

Shared wire protocol types for codex-mobile rust components.
Documentation
use codex_mobile_contracts::RuntimeStatusSnapshot;
use codex_mobile_wire::{PersistedEvent, ServerEnvelope, event_envelope};
use serde_json::json;

#[test]
fn hello_envelope_serializes_canonical_handshake_fields() {
    let payload = serde_json::to_value(ServerEnvelope::Hello {
        bridge_version: "0.3.13".to_string(),
        protocol_version: 4,
        runtime: Box::new(RuntimeStatusSnapshot {
            runtime_id: "primary".to_string(),
            status: "running".to_string(),
            updated_at_ms: 1234,
            ..RuntimeStatusSnapshot::default()
        }),
        runtimes: Vec::new(),
        directory_bookmarks: Vec::new(),
        directory_history: Vec::new(),
        pending_requests: Vec::new(),
    })
    .expect("hello envelope 应可序列化");

    assert_eq!(payload["kind"], json!("hello"));
    assert_eq!(payload["bridgeVersion"], json!("0.3.13"));
    assert_eq!(payload["protocolVersion"], json!(4));
    assert!(payload.get("bridge_version").is_none());
    assert!(payload.get("protocol_version").is_none());
    assert!(payload.get("directoryBookmarks").is_some());
    assert!(payload.get("directoryHistory").is_some());
    assert!(payload.get("directory_bookmarks").is_none());
    assert!(payload.get("directory_history").is_none());
    assert!(payload.get("pending_requests").is_some());
}

#[test]
fn event_envelope_keeps_existing_snake_case_fields() {
    let payload = serde_json::to_value(event_envelope(PersistedEvent {
        seq: 9,
        event_type: "thread/updated".to_string(),
        runtime_id: Some("primary".to_string()),
        thread_id: Some("thread-9".to_string()),
        payload: json!({ "threadId": "thread-9" }),
        created_at_ms: 1234,
    }))
    .expect("event envelope 应可序列化");

    assert_eq!(payload["kind"], json!("event"));
    assert_eq!(payload["event_type"], json!("thread/updated"));
    assert_eq!(payload["runtime_id"], json!("primary"));
    assert_eq!(payload["thread_id"], json!("thread-9"));
    assert!(payload.get("eventType").is_none());
    assert!(payload.get("runtimeId").is_none());
    assert!(payload.get("threadId").is_none());
}