1#![allow(
2 missing_docs,
3 dead_code,
4 unused_imports,
5 reason = "Intentional compatibility, platform, or test-only suppression."
6)]
7use serde::{Deserialize, Serialize};
21use serde_json::Value;
22
23pub mod atif;
24pub mod diagnostic;
25pub use diagnostic::{Diagnostic, DiagnosticAction, DiagnosticSeverity};
26pub mod items;
27pub use items::{
28 AgentMessageItem, AppPreviewItem, AppUiPosture, CommandExecutionItem, CommandExecutionStatus, DecisionItem,
29 FileChangeItem, FileUpdateChange, McpToolCallItem, McpToolCallStatus, PatchApplyStatus, PatchChangeKind, PlanItem,
30 ReasoningItem, ThreadItem, ThreadItemDetails, ToolCallStatus, ToolInvocationItem, ToolOutcome, ToolOutputItem,
31 WebSearchItem, tool_outcome_from_status,
32};
33pub mod matrix;
34pub mod planning;
35pub mod trace;
36
37pub const EVENT_SCHEMA_VERSION: &str = "0.23.0";
39
40#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
43#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
44pub struct VersionedThreadEvent {
45 schema_version: String,
47 event: ThreadEvent,
49}
50
51impl VersionedThreadEvent {
52 pub fn new(event: ThreadEvent) -> Self {
55 Self {
56 schema_version: EVENT_SCHEMA_VERSION.to_string(),
57 event,
58 }
59 }
60
61 pub fn event(&self) -> &ThreadEvent {
63 &self.event
64 }
65
66 pub fn into_event(self) -> ThreadEvent {
68 self.event
69 }
70}
71
72impl From<ThreadEvent> for VersionedThreadEvent {
73 fn from(event: ThreadEvent) -> Self {
74 Self::new(event)
75 }
76}
77
78pub trait EventEmitter {
80 fn emit(&mut self, event: &ThreadEvent);
82}
83
84impl<F> EventEmitter for F
85where
86 F: FnMut(&ThreadEvent),
87{
88 fn emit(&mut self, event: &ThreadEvent) {
89 self(event);
90 }
91}
92
93#[cfg(feature = "serde-json")]
94pub(crate) mod json;
95
96#[cfg(feature = "telemetry-log")]
97mod log_support;
98
99#[cfg(feature = "telemetry-log")]
100pub use log_support::PublicLogEmitter as LogEmitter;
101
102#[cfg(feature = "telemetry-tracing")]
103mod tracing_support;
104
105#[cfg(feature = "telemetry-tracing")]
106pub use tracing_support::PublicTracingEmitter as TracingEmitter;
107
108#[cfg(feature = "telemetry-otel")]
109mod otel_support;
110
111#[cfg(feature = "telemetry-otel")]
112pub use otel_support::PublicOtelEmitter as OtelEmitter;
113
114#[cfg(feature = "schema-export")]
115pub mod schema;
116
117#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
119#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
120#[serde(tag = "type")]
121pub enum ThreadEvent {
122 #[serde(rename = "matrix.updated")]
124 MatrixUpdated(Box<matrix::MatrixSnapshot>),
125 #[serde(rename = "thread.started")]
127 ThreadStarted(ThreadStartedEvent),
128 #[serde(rename = "thread.completed")]
130 ThreadCompleted(Box<ThreadCompletedEvent>),
131 #[serde(rename = "thread.compact_boundary")]
133 ThreadCompactBoundary(Box<ThreadCompactBoundaryEvent>),
134 #[serde(rename = "context.reset")]
136 ContextReset(ContextResetEvent),
137 #[serde(rename = "turn.started")]
139 TurnStarted(TurnStartedEvent),
140 #[serde(rename = "turn.completed")]
142 TurnCompleted(TurnCompletedEvent),
143 #[serde(rename = "turn.failed")]
145 TurnFailed(TurnFailedEvent),
146 #[serde(rename = "turn.blocked")]
150 TurnBlocked(Box<TurnBlockedEvent>),
151 #[serde(rename = "item.started")]
153 ItemStarted(ItemStartedEvent),
154 #[serde(rename = "item.updated")]
156 ItemUpdated(ItemUpdatedEvent),
157 #[serde(rename = "item.completed")]
159 ItemCompleted(ItemCompletedEvent),
160 #[serde(rename = "app.sandbox_denied")]
164 AppSandboxDenied(Box<AppSandboxDeniedEvent>),
165 #[serde(rename = "permission.requested")]
167 PermissionRequested(PermissionRequestedEvent),
168 #[serde(rename = "permission.resolved")]
170 PermissionResolved(PermissionResolvedEvent),
171 #[serde(rename = "interjected")]
173 Interjected(InterjectedEvent),
174 #[serde(rename = "plan.delta")]
176 PlanDelta(Box<PlanDeltaEvent>),
177 #[serde(rename = "plan.approval.requested")]
179 PlanApprovalRequested(PlanApprovalRequestedEvent),
180 #[serde(rename = "plan.approval.resolved")]
182 PlanApprovalResolved(PlanApprovalResolvedEvent),
183 #[serde(rename = "error")]
185 Error(ThreadErrorEvent),
186 #[serde(other)]
189 Unknown,
190}
191
192#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
193#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
194pub struct ThreadStartedEvent {
195 pub thread_id: String,
197}
198
199#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
200#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
201#[serde(rename_all = "snake_case")]
202pub enum ThreadCompletionSubtype {
203 Success,
204 ErrorMaxTurns,
205 ErrorMaxBudgetUsd,
206 ErrorDuringExecution,
207 Cancelled,
208 #[serde(other)]
210 Unknown,
211}
212
213impl ThreadCompletionSubtype {
214 pub const fn as_str(&self) -> &'static str {
215 match self {
216 Self::Success => "success",
217 Self::ErrorMaxTurns => "error_max_turns",
218 Self::ErrorMaxBudgetUsd => "error_max_budget_usd",
219 Self::ErrorDuringExecution => "error_during_execution",
220 Self::Cancelled => "cancelled",
221 Self::Unknown => "unknown",
222 }
223 }
224
225 pub const fn is_success(self) -> bool {
226 matches!(self, Self::Success)
227 }
228}
229
230#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
231#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
232#[serde(rename_all = "snake_case")]
233pub enum CompactionTrigger {
234 Manual,
235 Auto,
236 Recovery,
237 ModelSwitch,
240 #[serde(other)]
242 Unknown,
243}
244
245impl CompactionTrigger {
246 pub const fn as_str(self) -> &'static str {
247 match self {
248 Self::Manual => "manual",
249 Self::Auto => "auto",
250 Self::Recovery => "recovery",
251 Self::ModelSwitch => "model_switch",
252 Self::Unknown => "unknown",
253 }
254 }
255}
256
257#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
258#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
259#[serde(rename_all = "snake_case")]
260pub enum CompactionMode {
261 Provider,
262 Local,
263 #[serde(other)]
265 Unknown,
266}
267
268impl CompactionMode {
269 pub const fn as_str(self) -> &'static str {
270 match self {
271 Self::Provider => "provider",
272 Self::Local => "local",
273 Self::Unknown => "unknown",
274 }
275 }
276}
277
278#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
279#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
280pub struct ThreadCompletedEvent {
281 #[serde(default, skip_serializing_if = "Option::is_none")]
283 pub completed_at: Option<String>,
284 pub thread_id: String,
286 pub session_id: String,
288 pub subtype: ThreadCompletionSubtype,
290 pub outcome_code: String,
292 #[serde(skip_serializing_if = "Option::is_none")]
294 pub result: Option<String>,
295 #[serde(skip_serializing_if = "Option::is_none")]
297 pub stop_reason: Option<String>,
298 pub usage: Usage,
300 #[serde(skip_serializing_if = "Option::is_none")]
302 pub total_cost_usd: Option<serde_json::Number>,
303 pub num_turns: usize,
305}
306
307#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
308#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
309pub struct ThreadCompactBoundaryEvent {
310 pub thread_id: String,
312 pub trigger: CompactionTrigger,
314 pub mode: CompactionMode,
316 pub original_message_count: usize,
318 pub compacted_message_count: usize,
320 #[serde(skip_serializing_if = "Option::is_none")]
322 pub history_artifact_path: Option<String>,
323 #[serde(skip_serializing_if = "Option::is_none")]
325 pub previous_segment_id: Option<String>,
326 #[serde(skip_serializing_if = "Option::is_none")]
328 pub new_segment_id: Option<String>,
329 #[serde(skip_serializing_if = "Option::is_none")]
331 pub previous_prefix_hash: Option<String>,
332 #[serde(skip_serializing_if = "Option::is_none")]
334 pub new_prefix_hash: Option<String>,
335 #[serde(skip_serializing_if = "Option::is_none")]
337 pub previous_catalog_hash: Option<String>,
338 #[serde(skip_serializing_if = "Option::is_none")]
340 pub new_catalog_hash: Option<String>,
341}
342
343#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
344#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
345#[serde(rename_all = "snake_case")]
346pub enum ContextResetTrigger {
347 PlanApproval,
349 #[serde(other)]
351 Unknown,
352}
353
354#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
355#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
356pub struct ContextResetEvent {
357 pub thread_id: String,
359 pub turn_id: String,
361 pub trigger: ContextResetTrigger,
363 pub plan_preserved: bool,
365 pub previous_context_usage_percent: u8,
367 pub tool_budget_reset: bool,
369}
370
371#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
372#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
373pub struct TurnStartedEvent {
374 #[serde(skip_serializing_if = "Option::is_none")]
378 token_breakdown: Option<Box<TokenBreakdown>>,
379 #[serde(default, skip_serializing_if = "Option::is_none")]
381 pub context: Option<Box<ExecutionContext>>,
382}
383
384impl TurnStartedEvent {
385 pub fn token_breakdown(&self) -> Option<&TokenBreakdown> {
387 self.token_breakdown.as_deref()
388 }
389}
390
391#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
393#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
394#[serde(rename_all = "snake_case")]
395pub enum InputOrigin {
396 User,
397 Correction,
398 PlanApproval,
399 Continuation,
400 Retry,
401}
402
403#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
405#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
406pub struct ExecutionContext {
407 pub task_id: String,
408 pub turn_id: String,
409 pub actor_id: String,
410 #[serde(default, skip_serializing_if = "Option::is_none")]
411 pub parent_actor_id: Option<String>,
412 pub origin: InputOrigin,
413 pub timestamp: String,
414 #[serde(default, skip_serializing_if = "Option::is_none")]
415 pub goal: Option<String>,
416}
417
418#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
420#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
421#[serde(rename_all = "snake_case")]
422pub enum CommandActivity {
423 Inspection,
424 Verification,
425 Mutation,
426}
427
428#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
430#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
431pub struct ItemContext {
432 pub task_id: String,
433 pub turn_id: String,
434 pub actor_id: String,
435 #[serde(default, skip_serializing_if = "Option::is_none")]
436 pub parent_actor_id: Option<String>,
437 pub timestamp: String,
438 #[serde(default, skip_serializing_if = "Option::is_none")]
439 pub activity: Option<CommandActivity>,
440}
441
442#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
444#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
445pub struct TokenBreakdown {
446 system_prompt_tokens: u64,
448 tool_schema_tokens: u64,
450 instruction_file_tokens: u64,
452 message_history_tokens: u64,
454 cache_read_tokens: u64,
456 cache_write_tokens: u64,
458 cache_miss_tokens: u64,
460 #[serde(skip_serializing_if = "Option::is_none")]
462 subagent_bootstrap_tokens: Option<u64>,
463}
464
465pub const MAX_IN_PROGRESS_EXEC_SESSIONS: usize = 4;
469
470#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
471#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
472pub struct TurnCompletedEvent {
473 #[serde(default, skip_serializing_if = "Option::is_none")]
474 pub completed_at: Option<Box<String>>,
475 pub usage: Usage,
477 #[serde(
482 default,
483 skip_serializing_if = "Vec::is_empty",
484 deserialize_with = "deserialize_null_as_default"
485 )]
486 pub in_progress_exec_sessions: Vec<String>,
487}
488
489#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
490#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
491pub struct TurnFailedEvent {
492 #[serde(default, skip_serializing_if = "Option::is_none")]
493 pub completed_at: Option<Box<String>>,
494 pub message: String,
496 #[serde(skip_serializing_if = "Option::is_none")]
498 pub usage: Option<Usage>,
499}
500
501#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
502#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
503pub struct TurnBlockedEvent {
504 #[serde(default, skip_serializing_if = "Option::is_none")]
506 pub completed_at: Option<String>,
507 pub message: String,
509 #[serde(skip_serializing_if = "Option::is_none")]
511 pub last_tool: Option<String>,
512 #[serde(default)]
514 pub blocked_streak: usize,
515 #[serde(default)]
517 pub blocked_total: usize,
518 #[serde(default)]
520 pub consecutive_cap: usize,
521 #[serde(default)]
523 pub total_cap: usize,
524 #[serde(default)]
526 pub recovery_active: bool,
527 #[serde(skip_serializing_if = "Option::is_none")]
529 pub usage: Option<Usage>,
530}
531
532#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
533#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
534pub struct ThreadErrorEvent {
535 pub message: String,
537}
538
539#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
540#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
541pub struct AppSandboxDeniedEvent {
542 pub tool: String,
544 pub tier: String,
546 pub uris: Vec<String>,
548}
549
550#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
551#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
552pub struct Usage {
553 #[serde(default, deserialize_with = "deserialize_null_as_default")]
555 pub input_tokens: u64,
556 #[serde(default, deserialize_with = "deserialize_null_as_default")]
558 pub cached_input_tokens: u64,
559 #[serde(default, deserialize_with = "deserialize_null_as_default")]
561 pub cache_creation_tokens: u64,
562 #[serde(default, deserialize_with = "deserialize_null_as_default")]
564 pub output_tokens: u64,
565}
566
567pub fn deserialize_null_as_default<'de, D, T>(deserializer: D) -> Result<T, D::Error>
574where
575 D: serde::Deserializer<'de>,
576 T: Deserialize<'de> + Default,
577{
578 Ok(Option::<T>::deserialize(deserializer)?.unwrap_or_default())
579}
580
581impl Usage {
582 #[must_use]
587 fn uncached_input_tokens(&self) -> u64 {
588 self.input_tokens
589 .saturating_sub(self.cached_input_tokens)
590 .saturating_sub(self.cache_creation_tokens)
591 }
592
593 #[must_use]
596 pub fn cache_hit_rate(&self) -> Option<f64> {
597 if self.input_tokens == 0 {
598 return None;
599 }
600 Some(self.cached_input_tokens as f64 / self.input_tokens as f64)
601 }
602
603 #[must_use]
605 pub fn cache_summary(&self) -> String {
606 let total_input = self.input_tokens;
607 if total_input == 0 {
608 return "No input tokens recorded.".to_string();
609 }
610
611 let cached = self.cached_input_tokens;
612 let creation = self.cache_creation_tokens;
613 let uncached = self.uncached_input_tokens();
614 let rate = cached as f64 / total_input as f64 * 100.0;
615 format!(
616 "Cache: {cached} cached / {total_input} total input ({rate:.1}% hit rate), \
617 {creation} cache-creation, {uncached} uncached"
618 )
619 }
620
621 pub fn add(&mut self, other: &Usage) {
623 self.input_tokens = self.input_tokens.saturating_add(other.input_tokens);
624 self.cached_input_tokens = self.cached_input_tokens.saturating_add(other.cached_input_tokens);
625 self.cache_creation_tokens = self.cache_creation_tokens.saturating_add(other.cache_creation_tokens);
626 self.output_tokens = self.output_tokens.saturating_add(other.output_tokens);
627 }
628}
629
630#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
631#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
632pub struct ItemCompletedEvent {
633 pub item: ThreadItem,
635}
636
637#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
638#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
639pub struct ItemStartedEvent {
640 pub item: ThreadItem,
642}
643
644#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
645#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
646pub struct ItemUpdatedEvent {
647 pub item: ThreadItem,
649}
650
651#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
652#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
653pub struct PlanDeltaEvent {
654 pub thread_id: String,
656 pub turn_id: String,
658 pub item_id: String,
660 pub delta: String,
662 #[serde(default, skip_serializing_if = "Option::is_none")]
664 pub recovery: Option<Box<planning::PlanningRecoverySnapshot>>,
665}
666
667#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
668#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
669pub struct PlanApprovalRequestedEvent {
670 pub thread_id: String,
672 pub turn_id: String,
674 #[serde(skip_serializing_if = "Option::is_none")]
676 pub plan_file: Option<String>,
677}
678
679#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
680#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
681#[serde(rename_all = "snake_case")]
682pub enum PlanApprovalDecision {
683 Execute,
685 AutoAccept,
687 FreshContext,
689 Revise,
691 Cancel,
693 SwitchBuild,
695 SwitchAuto,
697 #[serde(other)]
699 Unknown,
700}
701
702#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
703#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
704pub struct PlanApprovalResolvedEvent {
705 pub thread_id: String,
707 pub turn_id: String,
709 pub decision: PlanApprovalDecision,
711 pub automatic: bool,
713}
714
715#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
716#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
717#[serde(rename_all = "snake_case")]
718pub enum HarnessEventKind {
719 PlanningStarted,
720 PlanningCompleted,
721 ContinuationStarted,
722 ContinuationSkipped,
723 TurnBlocked,
726 BlockedRecoveryStarted,
728 BlockedRecoveryFinished,
730 BlockedHandoffWritten,
731 BlockedHandoffResolved,
734 EvaluationStarted,
735 EvaluationPassed,
736 EvaluationFailed,
737 RevisionStarted,
738 EscalationTriggered,
739 EscalationBypassed,
740 VerificationStarted,
741 VerificationPassed,
742 VerificationFailed,
743 ErrorRecovered,
745 ToolRetryAttempted,
747 ToolLatencyRecorded,
749 SnapshotCreated,
751 SnapshotRestored,
753 SessionToolLimitIncreased,
756 ToolLoopLimitIncreased,
758 BackgroundSubprocessCompleted,
760 DelegatedAgentStatus,
762 SessionMessageSent,
764 SessionMessageReceived,
766 SessionMessageExpired,
768}
769
770#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
771#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
772#[serde(rename_all = "snake_case")]
773pub enum PermissionDecision {
774 Allow,
775 Deny,
776 Cancelled,
777 Followup,
778}
779
780#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
781#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
782pub struct PermissionRequestedEvent {
783 pub tool_name: String,
785 #[serde(default, skip_serializing_if = "Option::is_none")]
787 pub tool_call_id: Option<String>,
788}
789
790#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
791#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
792pub struct PermissionResolvedEvent {
793 pub tool_name: String,
795 #[serde(default, skip_serializing_if = "Option::is_none")]
797 pub tool_call_id: Option<String>,
798 pub decision: PermissionDecision,
800 pub wait_ms: u64,
802}
803
804#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
805#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
806#[serde(rename_all = "snake_case")]
807pub enum InterjectionSource {
808 Direct,
809 Queue,
810}
811
812#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
813#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
814#[serde(rename_all = "snake_case")]
815pub enum RedirectKind {
816 Interjection,
817}
818
819#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
820#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
821pub struct InterjectedEvent {
822 #[serde(default, skip_serializing_if = "Option::is_none")]
824 pub text: Option<Box<String>>,
825 pub source: InterjectionSource,
827 pub image_count: u32,
829 pub redirect_kind: RedirectKind,
832}
833
834#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
835#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
836pub struct HarnessEventItem {
837 pub event: HarnessEventKind,
839 #[serde(skip_serializing_if = "Option::is_none")]
841 pub message: Option<String>,
842 #[serde(skip_serializing_if = "Option::is_none")]
844 pub command: Option<String>,
845 #[serde(skip_serializing_if = "Option::is_none")]
847 pub path: Option<String>,
848 #[serde(skip_serializing_if = "Option::is_none")]
850 pub exit_code: Option<i32>,
851 #[serde(skip_serializing_if = "Option::is_none")]
853 pub attempt: Option<u32>,
854 #[serde(skip_serializing_if = "Option::is_none")]
856 pub error_category: Option<String>,
857 #[serde(skip_serializing_if = "Option::is_none")]
859 pub duration_ms: Option<u64>,
860 #[serde(skip_serializing_if = "Option::is_none")]
862 pub task_id: Option<String>,
863 #[serde(skip_serializing_if = "Option::is_none")]
866 pub session_id: Option<String>,
867 #[serde(skip_serializing_if = "Option::is_none")]
870 pub correlation_id: Option<String>,
871 #[serde(skip_serializing_if = "Option::is_none")]
873 pub exec_session_id: Option<String>,
874 #[serde(skip_serializing_if = "Option::is_none")]
876 pub status: Option<String>,
877 #[serde(skip_serializing_if = "Option::is_none")]
879 pub transcript_path: Option<String>,
880 #[serde(skip_serializing_if = "Option::is_none")]
882 pub archive_path: Option<String>,
883}
884
885impl HarnessEventItem {
886 #[must_use]
889 pub fn new(event: HarnessEventKind) -> Self {
890 Self {
891 event,
892 message: None,
893 command: None,
894 path: None,
895 exit_code: None,
896 attempt: None,
897 error_category: None,
898 duration_ms: None,
899 task_id: None,
900 session_id: None,
901 correlation_id: None,
902 exec_session_id: None,
903 status: None,
904 transcript_path: None,
905 archive_path: None,
906 }
907 }
908}
909
910#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
911#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
912pub struct ErrorItem {
913 pub message: String,
915}
916
917#[cfg(test)]
918mod tests;