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;
36pub mod tracker;
37
38pub const EVENT_SCHEMA_VERSION: &str = "0.28.0";
40
41#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
44#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
45pub struct VersionedThreadEvent {
46 schema_version: String,
48 event: ThreadEvent,
50}
51
52impl VersionedThreadEvent {
53 pub fn new(event: ThreadEvent) -> Self {
56 Self {
57 schema_version: EVENT_SCHEMA_VERSION.to_string(),
58 event,
59 }
60 }
61
62 pub fn event(&self) -> &ThreadEvent {
64 &self.event
65 }
66
67 pub fn into_event(self) -> ThreadEvent {
69 self.event
70 }
71}
72
73impl From<ThreadEvent> for VersionedThreadEvent {
74 fn from(event: ThreadEvent) -> Self {
75 Self::new(event)
76 }
77}
78
79pub trait EventEmitter {
81 fn emit(&mut self, event: &ThreadEvent);
83}
84
85impl<F> EventEmitter for F
86where
87 F: FnMut(&ThreadEvent),
88{
89 fn emit(&mut self, event: &ThreadEvent) {
90 self(event);
91 }
92}
93
94#[cfg(feature = "serde-json")]
95pub(crate) mod json;
96
97#[cfg(feature = "telemetry-log")]
98mod log_support;
99
100#[cfg(feature = "telemetry-log")]
101pub use log_support::PublicLogEmitter as LogEmitter;
102
103#[cfg(feature = "telemetry-tracing")]
104mod tracing_support;
105
106#[cfg(feature = "telemetry-tracing")]
107pub use tracing_support::PublicTracingEmitter as TracingEmitter;
108
109#[cfg(feature = "telemetry-otel")]
110mod otel_support;
111
112#[cfg(feature = "telemetry-otel")]
113pub use otel_support::PublicOtelEmitter as OtelEmitter;
114
115#[cfg(feature = "schema-export")]
116pub mod schema;
117
118#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
120#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
121#[serde(tag = "type")]
122pub enum ThreadEvent {
123 #[serde(rename = "matrix.updated")]
125 MatrixUpdated(Box<matrix::MatrixSnapshot>),
126 #[serde(rename = "tracker.updated")]
128 TrackerUpdated(Box<tracker::TrackerSnapshot>),
129 #[serde(rename = "thread.started")]
131 ThreadStarted(ThreadStartedEvent),
132 #[serde(rename = "thread.completed")]
134 ThreadCompleted(Box<ThreadCompletedEvent>),
135 #[serde(rename = "thread.compact_boundary")]
137 ThreadCompactBoundary(Box<ThreadCompactBoundaryEvent>),
138 #[serde(rename = "context.reset")]
140 ContextReset(ContextResetEvent),
141 #[serde(rename = "turn.started")]
143 TurnStarted(TurnStartedEvent),
144 #[serde(rename = "turn.completed")]
146 TurnCompleted(TurnCompletedEvent),
147 #[serde(rename = "turn.failed")]
149 TurnFailed(TurnFailedEvent),
150 #[serde(rename = "turn.blocked")]
154 TurnBlocked(Box<TurnBlockedEvent>),
155 #[serde(rename = "item.started")]
157 ItemStarted(ItemStartedEvent),
158 #[serde(rename = "item.updated")]
160 ItemUpdated(ItemUpdatedEvent),
161 #[serde(rename = "item.completed")]
163 ItemCompleted(ItemCompletedEvent),
164 #[serde(rename = "app.sandbox_denied")]
168 AppSandboxDenied(Box<AppSandboxDeniedEvent>),
169 #[serde(rename = "permission.requested")]
171 PermissionRequested(PermissionRequestedEvent),
172 #[serde(rename = "permission.resolved")]
174 PermissionResolved(PermissionResolvedEvent),
175 #[serde(rename = "interjected")]
177 Interjected(InterjectedEvent),
178 #[serde(rename = "plan.delta")]
180 PlanDelta(Box<PlanDeltaEvent>),
181 #[serde(rename = "plan.approval.requested")]
183 PlanApprovalRequested(PlanApprovalRequestedEvent),
184 #[serde(rename = "plan.approval.resolved")]
186 PlanApprovalResolved(PlanApprovalResolvedEvent),
187 #[serde(rename = "error")]
189 Error(ThreadErrorEvent),
190 #[serde(other)]
193 Unknown,
194}
195
196#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
197#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
198pub struct ThreadStartedEvent {
199 pub thread_id: String,
201}
202
203#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
204#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
205#[serde(rename_all = "snake_case")]
206pub enum ThreadCompletionSubtype {
207 Success,
208 ErrorMaxTurns,
209 ErrorMaxBudgetUsd,
210 ErrorDuringExecution,
211 Cancelled,
212 #[serde(other)]
214 Unknown,
215}
216
217impl ThreadCompletionSubtype {
218 pub const fn as_str(&self) -> &'static str {
219 match self {
220 Self::Success => "success",
221 Self::ErrorMaxTurns => "error_max_turns",
222 Self::ErrorMaxBudgetUsd => "error_max_budget_usd",
223 Self::ErrorDuringExecution => "error_during_execution",
224 Self::Cancelled => "cancelled",
225 Self::Unknown => "unknown",
226 }
227 }
228
229 pub const fn is_success(self) -> bool {
230 matches!(self, Self::Success)
231 }
232}
233
234#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
235#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
236#[serde(rename_all = "snake_case")]
237pub enum CompactionTrigger {
238 Manual,
239 Auto,
240 Recovery,
241 ModelSwitch,
244 #[serde(other)]
246 Unknown,
247}
248
249impl CompactionTrigger {
250 pub const fn as_str(self) -> &'static str {
251 match self {
252 Self::Manual => "manual",
253 Self::Auto => "auto",
254 Self::Recovery => "recovery",
255 Self::ModelSwitch => "model_switch",
256 Self::Unknown => "unknown",
257 }
258 }
259}
260
261#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
262#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
263#[serde(rename_all = "snake_case")]
264pub enum CompactionMode {
265 Provider,
266 Local,
267 #[serde(other)]
269 Unknown,
270}
271
272impl CompactionMode {
273 pub const fn as_str(self) -> &'static str {
274 match self {
275 Self::Provider => "provider",
276 Self::Local => "local",
277 Self::Unknown => "unknown",
278 }
279 }
280}
281
282#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
283#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
284pub struct ThreadCompletedEvent {
285 #[serde(default, skip_serializing_if = "Option::is_none")]
287 pub completed_at: Option<String>,
288 pub thread_id: String,
290 pub session_id: String,
292 pub subtype: ThreadCompletionSubtype,
294 pub outcome_code: String,
296 #[serde(skip_serializing_if = "Option::is_none")]
298 pub result: Option<String>,
299 #[serde(skip_serializing_if = "Option::is_none")]
301 pub stop_reason: Option<String>,
302 pub usage: Usage,
304 #[serde(skip_serializing_if = "Option::is_none")]
306 pub total_cost_usd: Option<serde_json::Number>,
307 pub num_turns: usize,
309}
310
311#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
312#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
313pub struct ThreadCompactBoundaryEvent {
314 pub thread_id: String,
316 pub trigger: CompactionTrigger,
318 pub mode: CompactionMode,
320 pub original_message_count: usize,
322 pub compacted_message_count: usize,
324 #[serde(skip_serializing_if = "Option::is_none")]
326 pub history_artifact_path: Option<String>,
327 #[serde(skip_serializing_if = "Option::is_none")]
329 pub previous_segment_id: Option<String>,
330 #[serde(skip_serializing_if = "Option::is_none")]
332 pub new_segment_id: Option<String>,
333 #[serde(skip_serializing_if = "Option::is_none")]
335 pub previous_prefix_hash: Option<String>,
336 #[serde(skip_serializing_if = "Option::is_none")]
338 pub new_prefix_hash: Option<String>,
339 #[serde(skip_serializing_if = "Option::is_none")]
341 pub previous_catalog_hash: Option<String>,
342 #[serde(skip_serializing_if = "Option::is_none")]
344 pub new_catalog_hash: Option<String>,
345}
346
347#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
348#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
349#[serde(rename_all = "snake_case")]
350pub enum ContextResetTrigger {
351 PlanApproval,
353 #[serde(other)]
355 Unknown,
356}
357
358#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
359#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
360pub struct ContextResetEvent {
361 pub thread_id: String,
363 pub turn_id: String,
365 pub trigger: ContextResetTrigger,
367 pub plan_preserved: bool,
369 pub previous_context_usage_percent: u8,
371 pub tool_budget_reset: bool,
373}
374
375#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
376#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
377pub struct TurnStartedEvent {
378 #[serde(skip_serializing_if = "Option::is_none")]
382 token_breakdown: Option<Box<TokenBreakdown>>,
383 #[serde(default, skip_serializing_if = "Option::is_none")]
385 pub context: Option<Box<ExecutionContext>>,
386}
387
388impl TurnStartedEvent {
389 pub fn token_breakdown(&self) -> Option<&TokenBreakdown> {
391 self.token_breakdown.as_deref()
392 }
393}
394
395#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
397#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
398#[serde(rename_all = "snake_case")]
399pub enum InputOrigin {
400 User,
401 Correction,
402 PlanApproval,
403 Continuation,
404 Retry,
405}
406
407#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
409#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
410pub struct ExecutionContext {
411 pub task_id: String,
412 pub turn_id: String,
413 pub actor_id: String,
414 #[serde(default, skip_serializing_if = "Option::is_none")]
415 pub parent_actor_id: Option<String>,
416 pub origin: InputOrigin,
417 pub timestamp: String,
418 #[serde(default, skip_serializing_if = "Option::is_none")]
419 pub goal: Option<String>,
420}
421
422#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
424#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
425#[serde(rename_all = "snake_case")]
426pub enum CommandActivity {
427 Inspection,
428 Verification,
429 Mutation,
430}
431
432#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
434#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
435pub struct ItemContext {
436 pub task_id: String,
437 pub turn_id: String,
438 pub actor_id: String,
439 #[serde(default, skip_serializing_if = "Option::is_none")]
440 pub parent_actor_id: Option<String>,
441 pub timestamp: String,
442 #[serde(default, skip_serializing_if = "Option::is_none")]
443 pub activity: Option<CommandActivity>,
444}
445
446#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
448#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
449pub struct TokenBreakdown {
450 system_prompt_tokens: u64,
452 tool_schema_tokens: u64,
454 instruction_file_tokens: u64,
456 message_history_tokens: u64,
458 cache_read_tokens: u64,
460 cache_write_tokens: u64,
462 cache_miss_tokens: u64,
464 #[serde(skip_serializing_if = "Option::is_none")]
466 subagent_bootstrap_tokens: Option<u64>,
467}
468
469pub const MAX_IN_PROGRESS_EXEC_SESSIONS: usize = 4;
473
474#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
475#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
476pub struct TurnCompletedEvent {
477 #[serde(default, skip_serializing_if = "Option::is_none")]
479 pub final_response_item_id: Option<Box<String>>,
480 #[serde(default, skip_serializing_if = "Option::is_none")]
481 pub completed_at: Option<Box<String>>,
482 pub usage: Usage,
484 #[serde(
489 default,
490 skip_serializing_if = "Vec::is_empty",
491 deserialize_with = "deserialize_null_as_default"
492 )]
493 pub in_progress_exec_sessions: Vec<String>,
494}
495
496#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
497#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
498pub struct TurnFailedEvent {
499 #[serde(default, skip_serializing_if = "Option::is_none")]
500 pub completed_at: Option<Box<String>>,
501 pub message: String,
503 #[serde(skip_serializing_if = "Option::is_none")]
505 pub usage: Option<Usage>,
506}
507
508#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
509#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
510pub struct TurnBlockedEvent {
511 #[serde(default, skip_serializing_if = "Option::is_none")]
513 pub completed_at: Option<String>,
514 pub message: String,
516 #[serde(skip_serializing_if = "Option::is_none")]
518 pub last_tool: Option<String>,
519 #[serde(default)]
521 pub blocked_streak: usize,
522 #[serde(default)]
524 pub blocked_total: usize,
525 #[serde(default)]
527 pub consecutive_cap: usize,
528 #[serde(default)]
530 pub total_cap: usize,
531 #[serde(default)]
533 pub recovery_active: bool,
534 #[serde(skip_serializing_if = "Option::is_none")]
536 pub usage: Option<Usage>,
537}
538
539#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
540#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
541pub struct ThreadErrorEvent {
542 pub message: String,
544}
545
546#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
547#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
548pub struct AppSandboxDeniedEvent {
549 pub tool: String,
551 pub tier: String,
553 pub uris: Vec<String>,
555}
556
557#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
558#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
559pub struct Usage {
560 #[serde(default, deserialize_with = "deserialize_null_as_default")]
562 pub input_tokens: u64,
563 #[serde(default, deserialize_with = "deserialize_null_as_default")]
565 pub cached_input_tokens: u64,
566 #[serde(default, deserialize_with = "deserialize_null_as_default")]
568 pub cache_creation_tokens: u64,
569 #[serde(default, deserialize_with = "deserialize_null_as_default")]
571 pub output_tokens: u64,
572}
573
574pub fn deserialize_null_as_default<'de, D, T>(deserializer: D) -> Result<T, D::Error>
581where
582 D: serde::Deserializer<'de>,
583 T: Deserialize<'de> + Default,
584{
585 Ok(Option::<T>::deserialize(deserializer)?.unwrap_or_default())
586}
587
588impl Usage {
589 #[must_use]
594 fn uncached_input_tokens(&self) -> u64 {
595 self.input_tokens
596 .saturating_sub(self.cached_input_tokens)
597 .saturating_sub(self.cache_creation_tokens)
598 }
599
600 #[must_use]
603 pub fn cache_hit_rate(&self) -> Option<f64> {
604 if self.input_tokens == 0 {
605 return None;
606 }
607 Some(self.cached_input_tokens as f64 / self.input_tokens as f64)
608 }
609
610 #[must_use]
612 pub fn cache_summary(&self) -> String {
613 let total_input = self.input_tokens;
614 if total_input == 0 {
615 return "No input tokens recorded.".to_string();
616 }
617
618 let cached = self.cached_input_tokens;
619 let creation = self.cache_creation_tokens;
620 let uncached = self.uncached_input_tokens();
621 let rate = cached as f64 / total_input as f64 * 100.0;
622 format!(
623 "Cache: {cached} cached / {total_input} total input ({rate:.1}% hit rate), \
624 {creation} cache-creation, {uncached} uncached"
625 )
626 }
627
628 pub fn add(&mut self, other: &Usage) {
630 self.input_tokens = self.input_tokens.saturating_add(other.input_tokens);
631 self.cached_input_tokens = self.cached_input_tokens.saturating_add(other.cached_input_tokens);
632 self.cache_creation_tokens = self.cache_creation_tokens.saturating_add(other.cache_creation_tokens);
633 self.output_tokens = self.output_tokens.saturating_add(other.output_tokens);
634 }
635}
636
637#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
638#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
639pub struct ItemCompletedEvent {
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 ItemStartedEvent {
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 ItemUpdatedEvent {
654 pub item: ThreadItem,
656}
657
658#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
659#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
660pub struct PlanDeltaEvent {
661 pub thread_id: String,
663 pub turn_id: String,
665 pub item_id: String,
667 pub delta: String,
669 #[serde(default, skip_serializing_if = "Option::is_none")]
671 pub recovery: Option<Box<planning::PlanningRecoverySnapshot>>,
672}
673
674#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
675#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
676pub struct PlanApprovalRequestedEvent {
677 pub thread_id: String,
679 pub turn_id: String,
681 #[serde(skip_serializing_if = "Option::is_none")]
683 pub plan_file: Option<String>,
684}
685
686#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
687#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
688#[serde(rename_all = "snake_case")]
689pub enum PlanApprovalDecision {
690 Execute,
692 AutoAccept,
694 FreshContext,
696 Revise,
698 Cancel,
700 SwitchBuild,
702 SwitchAuto,
704 #[serde(other)]
706 Unknown,
707}
708
709#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
710#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
711pub struct PlanApprovalResolvedEvent {
712 pub thread_id: String,
714 pub turn_id: String,
716 pub decision: PlanApprovalDecision,
718 pub automatic: bool,
720}
721
722#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
723#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
724#[serde(rename_all = "snake_case")]
725pub enum HarnessEventKind {
726 PlanningStarted,
727 PlanningCompleted,
728 ContinuationStarted,
729 ContinuationSkipped,
730 TurnBlocked,
733 BlockedRecoveryStarted,
735 BlockedRecoveryFinished,
737 BlockedHandoffWritten,
738 BlockedHandoffResolved,
741 EvaluationStarted,
742 EvaluationPassed,
743 EvaluationFailed,
744 RevisionStarted,
745 EscalationTriggered,
746 EscalationBypassed,
747 VerificationStarted,
748 VerificationPassed,
749 VerificationFailed,
750 ErrorRecovered,
752 ToolRetryAttempted,
754 ToolLatencyRecorded,
756 SnapshotCreated,
758 SnapshotRestored,
760 SessionToolLimitIncreased,
766 ToolLoopLimitIncreased,
768 BackgroundSubprocessCompleted,
770 DelegatedAgentStatus,
772 SessionMessageSent,
774 SessionMessageReceived,
776 SessionMessageExpired,
778}
779
780#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
781#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
782#[serde(rename_all = "snake_case")]
783pub enum PermissionDecision {
784 Allow,
785 Deny,
786 Cancelled,
787 Followup,
788}
789
790#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
791#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
792pub struct PermissionRequestedEvent {
793 pub tool_name: String,
795 #[serde(default, skip_serializing_if = "Option::is_none")]
797 pub tool_call_id: Option<String>,
798}
799
800#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
801#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
802pub struct PermissionResolvedEvent {
803 pub tool_name: String,
805 #[serde(default, skip_serializing_if = "Option::is_none")]
807 pub tool_call_id: Option<String>,
808 pub decision: PermissionDecision,
810 pub wait_ms: u64,
812}
813
814#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
815#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
816#[serde(rename_all = "snake_case")]
817pub enum InterjectionSource {
818 Direct,
819 Queue,
820}
821
822#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
823#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
824#[serde(rename_all = "snake_case")]
825pub enum RedirectKind {
826 Interjection,
827}
828
829#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
830#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
831pub struct InterjectedEvent {
832 #[serde(default, skip_serializing_if = "Option::is_none")]
834 pub text: Option<Box<String>>,
835 pub source: InterjectionSource,
837 pub image_count: u32,
839 pub redirect_kind: RedirectKind,
842}
843
844#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
845#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
846pub struct HarnessEventItem {
847 pub event: HarnessEventKind,
849 #[serde(skip_serializing_if = "Option::is_none")]
851 pub message: Option<String>,
852 #[serde(skip_serializing_if = "Option::is_none")]
854 pub command: Option<String>,
855 #[serde(skip_serializing_if = "Option::is_none")]
857 pub path: Option<String>,
858 #[serde(skip_serializing_if = "Option::is_none")]
860 pub exit_code: Option<i32>,
861 #[serde(skip_serializing_if = "Option::is_none")]
863 pub attempt: Option<u32>,
864 #[serde(skip_serializing_if = "Option::is_none")]
866 pub error_category: Option<String>,
867 #[serde(skip_serializing_if = "Option::is_none")]
869 pub duration_ms: Option<u64>,
870 #[serde(skip_serializing_if = "Option::is_none")]
872 pub task_id: Option<String>,
873 #[serde(skip_serializing_if = "Option::is_none")]
876 pub session_id: Option<String>,
877 #[serde(skip_serializing_if = "Option::is_none")]
880 pub correlation_id: Option<String>,
881 #[serde(skip_serializing_if = "Option::is_none")]
883 pub exec_session_id: Option<String>,
884 #[serde(skip_serializing_if = "Option::is_none")]
886 pub status: Option<String>,
887 #[serde(skip_serializing_if = "Option::is_none")]
889 pub transcript_path: Option<String>,
890 #[serde(skip_serializing_if = "Option::is_none")]
892 pub archive_path: Option<String>,
893}
894
895impl HarnessEventItem {
896 #[must_use]
899 pub fn new(event: HarnessEventKind) -> Self {
900 Self {
901 event,
902 message: None,
903 command: None,
904 path: None,
905 exit_code: None,
906 attempt: None,
907 error_category: None,
908 duration_ms: None,
909 task_id: None,
910 session_id: None,
911 correlation_id: None,
912 exec_session_id: None,
913 status: None,
914 transcript_path: None,
915 archive_path: None,
916 }
917 }
918}
919
920#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
921#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
922pub struct ErrorItem {
923 pub message: String,
925}
926
927#[cfg(test)]
928mod tests;