Skip to main content

agent_event_emit/
lib.rs

1//! # agent-event-emit
2//!
3//! Structured event emitter for agent runs. One event per significant
4//! step (LLM call, tool call, error, etc.). Each event carries:
5//!
6//! - `run_id` — caller-supplied; same across the whole run.
7//! - `id` — monotonic per-emitter; assigned on emit.
8//! - `ts_unix_ms` — wall-clock millis when emitted.
9//! - `kind` — short string label.
10//! - `payload` — arbitrary `serde_json::Value`.
11//!
12//! Serializes cleanly to JSON Lines for log shipping.
13//!
14//! ## Example
15//!
16//! ```
17//! use agent_event_emit::Emitter;
18//! use serde_json::json;
19//! let mut e = Emitter::new("run-123");
20//! let ev = e.emit("tool_call", json!({"name": "read_file", "path": "a.txt"}));
21//! assert_eq!(ev.id, 1);
22//! assert_eq!(ev.run_id, "run-123");
23//! ```
24
25#![deny(missing_docs)]
26
27use serde::{Deserialize, Serialize};
28use serde_json::Value;
29use std::time::{SystemTime, UNIX_EPOCH};
30
31/// One emitted event.
32#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct Event {
34    /// Run identifier supplied to the emitter.
35    pub run_id: String,
36    /// Monotonic event id, 1-based.
37    pub id: u64,
38    /// Wall-clock millis since the Unix epoch.
39    pub ts_unix_ms: u64,
40    /// Short event kind label.
41    pub kind: String,
42    /// Caller-provided payload.
43    pub payload: Value,
44}
45
46impl Event {
47    /// Serialize as one JSON line (no trailing newline).
48    pub fn to_json_line(&self) -> String {
49        serde_json::to_string(self).expect("serialize Event")
50    }
51}
52
53/// Stateful event emitter for a single run.
54#[derive(Debug, Clone)]
55pub struct Emitter {
56    run_id: String,
57    next_id: u64,
58}
59
60impl Emitter {
61    /// Build an emitter for `run_id`. First emit will have id=1.
62    pub fn new(run_id: impl Into<String>) -> Self {
63        Self {
64            run_id: run_id.into(),
65            next_id: 1,
66        }
67    }
68
69    /// Emit an event. Returns the constructed event (also write it
70    /// wherever you want — this crate doesn't pick a sink).
71    pub fn emit(&mut self, kind: impl Into<String>, payload: Value) -> Event {
72        let id = self.next_id;
73        self.next_id += 1;
74        Event {
75            run_id: self.run_id.clone(),
76            id,
77            ts_unix_ms: now_ms(),
78            kind: kind.into(),
79            payload,
80        }
81    }
82
83    /// Current run id.
84    pub fn run_id(&self) -> &str {
85        &self.run_id
86    }
87
88    /// How many events have been emitted so far.
89    pub fn count(&self) -> u64 {
90        self.next_id - 1
91    }
92}
93
94fn now_ms() -> u64 {
95    SystemTime::now()
96        .duration_since(UNIX_EPOCH)
97        .map(|d| d.as_millis() as u64)
98        .unwrap_or(0)
99}