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}