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.27.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,
763 ToolLoopLimitIncreased,
765 BackgroundSubprocessCompleted,
767 DelegatedAgentStatus,
769 SessionMessageSent,
771 SessionMessageReceived,
773 SessionMessageExpired,
775}
776
777#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
778#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
779#[serde(rename_all = "snake_case")]
780pub enum PermissionDecision {
781 Allow,
782 Deny,
783 Cancelled,
784 Followup,
785}
786
787#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
788#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
789pub struct PermissionRequestedEvent {
790 pub tool_name: String,
792 #[serde(default, skip_serializing_if = "Option::is_none")]
794 pub tool_call_id: Option<String>,
795}
796
797#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
798#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
799pub struct PermissionResolvedEvent {
800 pub tool_name: String,
802 #[serde(default, skip_serializing_if = "Option::is_none")]
804 pub tool_call_id: Option<String>,
805 pub decision: PermissionDecision,
807 pub wait_ms: u64,
809}
810
811#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
812#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
813#[serde(rename_all = "snake_case")]
814pub enum InterjectionSource {
815 Direct,
816 Queue,
817}
818
819#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
820#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
821#[serde(rename_all = "snake_case")]
822pub enum RedirectKind {
823 Interjection,
824}
825
826#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
827#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
828pub struct InterjectedEvent {
829 #[serde(default, skip_serializing_if = "Option::is_none")]
831 pub text: Option<Box<String>>,
832 pub source: InterjectionSource,
834 pub image_count: u32,
836 pub redirect_kind: RedirectKind,
839}
840
841#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
842#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
843pub struct HarnessEventItem {
844 pub event: HarnessEventKind,
846 #[serde(skip_serializing_if = "Option::is_none")]
848 pub message: Option<String>,
849 #[serde(skip_serializing_if = "Option::is_none")]
851 pub command: Option<String>,
852 #[serde(skip_serializing_if = "Option::is_none")]
854 pub path: Option<String>,
855 #[serde(skip_serializing_if = "Option::is_none")]
857 pub exit_code: Option<i32>,
858 #[serde(skip_serializing_if = "Option::is_none")]
860 pub attempt: Option<u32>,
861 #[serde(skip_serializing_if = "Option::is_none")]
863 pub error_category: Option<String>,
864 #[serde(skip_serializing_if = "Option::is_none")]
866 pub duration_ms: Option<u64>,
867 #[serde(skip_serializing_if = "Option::is_none")]
869 pub task_id: Option<String>,
870 #[serde(skip_serializing_if = "Option::is_none")]
873 pub session_id: Option<String>,
874 #[serde(skip_serializing_if = "Option::is_none")]
877 pub correlation_id: Option<String>,
878 #[serde(skip_serializing_if = "Option::is_none")]
880 pub exec_session_id: Option<String>,
881 #[serde(skip_serializing_if = "Option::is_none")]
883 pub status: Option<String>,
884 #[serde(skip_serializing_if = "Option::is_none")]
886 pub transcript_path: Option<String>,
887 #[serde(skip_serializing_if = "Option::is_none")]
889 pub archive_path: Option<String>,
890}
891
892impl HarnessEventItem {
893 #[must_use]
896 pub fn new(event: HarnessEventKind) -> Self {
897 Self {
898 event,
899 message: None,
900 command: None,
901 path: None,
902 exit_code: None,
903 attempt: None,
904 error_category: None,
905 duration_ms: None,
906 task_id: None,
907 session_id: None,
908 correlation_id: None,
909 exec_session_id: None,
910 status: None,
911 transcript_path: None,
912 archive_path: None,
913 }
914 }
915}
916
917#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
918#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
919pub struct ErrorItem {
920 pub message: String,
922}
923
924#[cfg(test)]
925mod tests;