Skip to main content

codewhale_core/engine/thread/
events.rs

1//! Thread events — `RuntimeEventEnvelope` mapping + `EventMsg` fan-out
2//! (issue #5261 / #3313).
3//!
4//! The TUI's `runtime_threads.rs` emits `RuntimeEventEnvelope` for the
5//! app-server SSE stream and `Event` for the transcript. This module owns
6//! that mapping in `core` so the headless `exec` and the TUI render the
7//! same envelope for the same turn — byte-identical on the wire.
8
9use codewhale_protocol::event_msg::EventMsg;
10use codewhale_protocol::ids::{SessionId, ThreadId};
11
12/// Narrow the `EventMsg` to the envelope shape the app-server expects.
13/// The real `RuntimeEventEnvelope` adds `seq` + `timestamp`; this helper
14/// stamps them consistently so headless and TUI produce identical sequences.
15#[must_use]
16pub fn to_envelope_seq(
17    seq: u64,
18    thread_id: ThreadId,
19    _session_id: SessionId,
20    msg: EventMsg,
21) -> codewhale_protocol::runtime::RuntimeEventEnvelope {
22    codewhale_protocol::runtime::RuntimeEventEnvelope {
23        schema_version: codewhale_protocol::runtime::RUNTIME_EVENT_ENVELOPE_SCHEMA_VERSION,
24        seq,
25        event: msg.kind_str().to_string(),
26        kind: msg.kind_str().to_string(),
27        thread_id: thread_id.to_string(),
28        turn_id: None,
29        item_id: None,
30        timestamp: chrono::Utc::now().to_rfc3339(),
31        created_at: None,
32        payload: serde_json::to_value(&msg).unwrap_or(serde_json::Value::Null),
33        extra: Default::default(),
34    }
35}
36
37#[cfg(test)]
38mod tests {
39    use super::*;
40
41    #[test]
42    fn envelope_preserves_thread_and_kind() {
43        let tid = ThreadId::new();
44        let sid = SessionId::new();
45        let env = to_envelope_seq(
46            1,
47            tid.clone(),
48            sid.clone(),
49            EventMsg::TurnStarted {
50                thread_id: tid.clone(),
51                session_id: sid.clone(),
52                turn_id: "turn-1".into(),
53            },
54        );
55        assert_eq!(env.thread_id, tid.to_string());
56        assert_eq!(env.seq, 1);
57    }
58}