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.26.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")]
475 pub final_response_item_id: Option<Box<String>>,
476 #[serde(default, skip_serializing_if = "Option::is_none")]
477 pub completed_at: Option<Box<String>>,
478 pub usage: Usage,
480 #[serde(
485 default,
486 skip_serializing_if = "Vec::is_empty",
487 deserialize_with = "deserialize_null_as_default"
488 )]
489 pub in_progress_exec_sessions: Vec<String>,
490}
491
492#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
493#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
494pub struct TurnFailedEvent {
495 #[serde(default, skip_serializing_if = "Option::is_none")]
496 pub completed_at: Option<Box<String>>,
497 pub message: String,
499 #[serde(skip_serializing_if = "Option::is_none")]
501 pub usage: Option<Usage>,
502}
503
504#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
505#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
506pub struct TurnBlockedEvent {
507 #[serde(default, skip_serializing_if = "Option::is_none")]
509 pub completed_at: Option<String>,
510 pub message: String,
512 #[serde(skip_serializing_if = "Option::is_none")]
514 pub last_tool: Option<String>,
515 #[serde(default)]
517 pub blocked_streak: usize,
518 #[serde(default)]
520 pub blocked_total: usize,
521 #[serde(default)]
523 pub consecutive_cap: usize,
524 #[serde(default)]
526 pub total_cap: usize,
527 #[serde(default)]
529 pub recovery_active: bool,
530 #[serde(skip_serializing_if = "Option::is_none")]
532 pub usage: Option<Usage>,
533}
534
535#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
536#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
537pub struct ThreadErrorEvent {
538 pub message: String,
540}
541
542#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
543#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
544pub struct AppSandboxDeniedEvent {
545 pub tool: String,
547 pub tier: String,
549 pub uris: Vec<String>,
551}
552
553#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
554#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
555pub struct Usage {
556 #[serde(default, deserialize_with = "deserialize_null_as_default")]
558 pub input_tokens: u64,
559 #[serde(default, deserialize_with = "deserialize_null_as_default")]
561 pub cached_input_tokens: u64,
562 #[serde(default, deserialize_with = "deserialize_null_as_default")]
564 pub cache_creation_tokens: u64,
565 #[serde(default, deserialize_with = "deserialize_null_as_default")]
567 pub output_tokens: u64,
568}
569
570pub fn deserialize_null_as_default<'de, D, T>(deserializer: D) -> Result<T, D::Error>
577where
578 D: serde::Deserializer<'de>,
579 T: Deserialize<'de> + Default,
580{
581 Ok(Option::<T>::deserialize(deserializer)?.unwrap_or_default())
582}
583
584impl Usage {
585 #[must_use]
590 fn uncached_input_tokens(&self) -> u64 {
591 self.input_tokens
592 .saturating_sub(self.cached_input_tokens)
593 .saturating_sub(self.cache_creation_tokens)
594 }
595
596 #[must_use]
599 pub fn cache_hit_rate(&self) -> Option<f64> {
600 if self.input_tokens == 0 {
601 return None;
602 }
603 Some(self.cached_input_tokens as f64 / self.input_tokens as f64)
604 }
605
606 #[must_use]
608 pub fn cache_summary(&self) -> String {
609 let total_input = self.input_tokens;
610 if total_input == 0 {
611 return "No input tokens recorded.".to_string();
612 }
613
614 let cached = self.cached_input_tokens;
615 let creation = self.cache_creation_tokens;
616 let uncached = self.uncached_input_tokens();
617 let rate = cached as f64 / total_input as f64 * 100.0;
618 format!(
619 "Cache: {cached} cached / {total_input} total input ({rate:.1}% hit rate), \
620 {creation} cache-creation, {uncached} uncached"
621 )
622 }
623
624 pub fn add(&mut self, other: &Usage) {
626 self.input_tokens = self.input_tokens.saturating_add(other.input_tokens);
627 self.cached_input_tokens = self.cached_input_tokens.saturating_add(other.cached_input_tokens);
628 self.cache_creation_tokens = self.cache_creation_tokens.saturating_add(other.cache_creation_tokens);
629 self.output_tokens = self.output_tokens.saturating_add(other.output_tokens);
630 }
631}
632
633#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
634#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
635pub struct ItemCompletedEvent {
636 pub item: ThreadItem,
638}
639
640#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
641#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
642pub struct ItemStartedEvent {
643 pub item: ThreadItem,
645}
646
647#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
648#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
649pub struct ItemUpdatedEvent {
650 pub item: ThreadItem,
652}
653
654#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
655#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
656pub struct PlanDeltaEvent {
657 pub thread_id: String,
659 pub turn_id: String,
661 pub item_id: String,
663 pub delta: String,
665 #[serde(default, skip_serializing_if = "Option::is_none")]
667 pub recovery: Option<Box<planning::PlanningRecoverySnapshot>>,
668}
669
670#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
671#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
672pub struct PlanApprovalRequestedEvent {
673 pub thread_id: String,
675 pub turn_id: String,
677 #[serde(skip_serializing_if = "Option::is_none")]
679 pub plan_file: Option<String>,
680}
681
682#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
683#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
684#[serde(rename_all = "snake_case")]
685pub enum PlanApprovalDecision {
686 Execute,
688 AutoAccept,
690 FreshContext,
692 Revise,
694 Cancel,
696 SwitchBuild,
698 SwitchAuto,
700 #[serde(other)]
702 Unknown,
703}
704
705#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
706#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
707pub struct PlanApprovalResolvedEvent {
708 pub thread_id: String,
710 pub turn_id: String,
712 pub decision: PlanApprovalDecision,
714 pub automatic: bool,
716}
717
718#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
719#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
720#[serde(rename_all = "snake_case")]
721pub enum HarnessEventKind {
722 PlanningStarted,
723 PlanningCompleted,
724 ContinuationStarted,
725 ContinuationSkipped,
726 TurnBlocked,
729 BlockedRecoveryStarted,
731 BlockedRecoveryFinished,
733 BlockedHandoffWritten,
734 BlockedHandoffResolved,
737 EvaluationStarted,
738 EvaluationPassed,
739 EvaluationFailed,
740 RevisionStarted,
741 EscalationTriggered,
742 EscalationBypassed,
743 VerificationStarted,
744 VerificationPassed,
745 VerificationFailed,
746 ErrorRecovered,
748 ToolRetryAttempted,
750 ToolLatencyRecorded,
752 SnapshotCreated,
754 SnapshotRestored,
756 SessionToolLimitIncreased,
759 ToolLoopLimitIncreased,
761 BackgroundSubprocessCompleted,
763 DelegatedAgentStatus,
765 SessionMessageSent,
767 SessionMessageReceived,
769 SessionMessageExpired,
771}
772
773#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
774#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
775#[serde(rename_all = "snake_case")]
776pub enum PermissionDecision {
777 Allow,
778 Deny,
779 Cancelled,
780 Followup,
781}
782
783#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
784#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
785pub struct PermissionRequestedEvent {
786 pub tool_name: String,
788 #[serde(default, skip_serializing_if = "Option::is_none")]
790 pub tool_call_id: Option<String>,
791}
792
793#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
794#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
795pub struct PermissionResolvedEvent {
796 pub tool_name: String,
798 #[serde(default, skip_serializing_if = "Option::is_none")]
800 pub tool_call_id: Option<String>,
801 pub decision: PermissionDecision,
803 pub wait_ms: u64,
805}
806
807#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
808#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
809#[serde(rename_all = "snake_case")]
810pub enum InterjectionSource {
811 Direct,
812 Queue,
813}
814
815#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
816#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
817#[serde(rename_all = "snake_case")]
818pub enum RedirectKind {
819 Interjection,
820}
821
822#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
823#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
824pub struct InterjectedEvent {
825 #[serde(default, skip_serializing_if = "Option::is_none")]
827 pub text: Option<Box<String>>,
828 pub source: InterjectionSource,
830 pub image_count: u32,
832 pub redirect_kind: RedirectKind,
835}
836
837#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
838#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
839pub struct HarnessEventItem {
840 pub event: HarnessEventKind,
842 #[serde(skip_serializing_if = "Option::is_none")]
844 pub message: Option<String>,
845 #[serde(skip_serializing_if = "Option::is_none")]
847 pub command: Option<String>,
848 #[serde(skip_serializing_if = "Option::is_none")]
850 pub path: Option<String>,
851 #[serde(skip_serializing_if = "Option::is_none")]
853 pub exit_code: Option<i32>,
854 #[serde(skip_serializing_if = "Option::is_none")]
856 pub attempt: Option<u32>,
857 #[serde(skip_serializing_if = "Option::is_none")]
859 pub error_category: Option<String>,
860 #[serde(skip_serializing_if = "Option::is_none")]
862 pub duration_ms: Option<u64>,
863 #[serde(skip_serializing_if = "Option::is_none")]
865 pub task_id: Option<String>,
866 #[serde(skip_serializing_if = "Option::is_none")]
869 pub session_id: Option<String>,
870 #[serde(skip_serializing_if = "Option::is_none")]
873 pub correlation_id: Option<String>,
874 #[serde(skip_serializing_if = "Option::is_none")]
876 pub exec_session_id: Option<String>,
877 #[serde(skip_serializing_if = "Option::is_none")]
879 pub status: Option<String>,
880 #[serde(skip_serializing_if = "Option::is_none")]
882 pub transcript_path: Option<String>,
883 #[serde(skip_serializing_if = "Option::is_none")]
885 pub archive_path: Option<String>,
886}
887
888impl HarnessEventItem {
889 #[must_use]
892 pub fn new(event: HarnessEventKind) -> Self {
893 Self {
894 event,
895 message: None,
896 command: None,
897 path: None,
898 exit_code: None,
899 attempt: None,
900 error_category: None,
901 duration_ms: None,
902 task_id: None,
903 session_id: None,
904 correlation_id: None,
905 exec_session_id: None,
906 status: None,
907 transcript_path: None,
908 archive_path: None,
909 }
910 }
911}
912
913#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
914#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
915pub struct ErrorItem {
916 pub message: String,
918}
919
920#[cfg(test)]
921mod tests;