Skip to main content

roder_api/
events.rs

1use serde::{Deserialize, Serialize};
2use time::OffsetDateTime;
3
4use crate::artifacts::{ContextArtifact, ContextArtifactId};
5use crate::automations::{
6    AutomationCompleted, AutomationCreated, AutomationDeleted, AutomationDue, AutomationFailed,
7    AutomationLeaseExpired, AutomationLeased, AutomationQueued, AutomationSkipped,
8    AutomationStarted, AutomationUpdated,
9};
10use crate::code_index::{
11    CodeIndexChunked, CodeIndexEmbedded, CodeIndexFailed, CodeIndexProofFilteredResultDropped,
12    CodeIndexReady, CodeIndexStale, CodeIndexingStarted,
13};
14use crate::discovery::{
15    DiscoveryAuthRequired, DiscoveryCatalogBuilt, DiscoveryItemPromoted, DiscoveryItemRead,
16    DiscoveryItemUpdated, DiscoveryPromotionExpired, DiscoveryPromotionReused,
17    DiscoveryWarmCacheHit,
18};
19use crate::dynamic_workflows::{
20    WorkflowAgentCompleted, WorkflowAgentFailed, WorkflowAgentQueued, WorkflowAgentStarted,
21    WorkflowApprovalRequested, WorkflowCheckpointRecorded, WorkflowOutputRecorded,
22    WorkflowPhaseCompleted, WorkflowPhaseStarted, WorkflowRunApproved, WorkflowRunCompleted,
23    WorkflowRunDenied, WorkflowRunDrafted, WorkflowRunFailed, WorkflowRunPaused, WorkflowRunQueued,
24    WorkflowRunResumed, WorkflowRunStarted, WorkflowRunStopped,
25};
26use crate::extension::{ExtensionId, InferenceEngineId};
27use crate::goals::{ThreadGoalCleared, ThreadGoalUpdated};
28use crate::inference::{
29    InferenceEvent, ModelSelection, ReasoningConfig, RuntimeProfile, SpeedPolicyDecision,
30    TokenUsage,
31};
32use crate::inference_routing::InferenceRoutingDecision;
33use crate::media::{MediaArtifact, MediaArtifactId, MediaPreview};
34use crate::knowledge::{KnowledgeDocId, KnowledgeDocSummary, KnowledgeLinkType};
35use crate::memory::{MemoryCitation, MemoryId, MemoryProviderSelection, MemoryRecord, MemoryScope};
36use crate::plan_review::{
37    HunkId, HunkRecord, PlanComment, PlanReview, PlanReviewId, PlanReviewStatus, PlanRewrite,
38};
39use crate::processes::{
40    ProcessExited, ProcessFailed, ProcessOutput, ProcessStarted, ProcessStopped, ProcessStopping,
41};
42use crate::reliability::{
43    ReliabilityFailureRecorded, ReliabilityLimitRecorded, ReliabilityMetricRecorded,
44    ReliabilityRetryRecorded,
45};
46use crate::retrieval::{
47    RetrievalDiscoveryItemPromoted, RetrievalPromotionSkipped, RetrievalResultUsed,
48    RetrievalRouteAccepted, RetrievalRouteFailed, RetrievalRouteIgnored, RetrievalRoutePlanned,
49};
50use crate::skills::{
51    SkillActivationResolved, SkillAutoActivated, SkillConfigApplied, SkillIndexRendered,
52    SkillInvoked, SkillSkipped, SkillsCatalogLoaded,
53};
54use crate::subagents::SubagentExitReason;
55use crate::task_ledger::TaskLedgerItem;
56use crate::teams::{
57    AgentTeamDisplayMode, TeamId, TeamMemberId, TeamMemberRole, TeamMemberStatus,
58    TeamTaskDescriptor,
59};
60use crate::trace::{
61    ParentTurnRef, SubagentTraceDelta, SubagentTraceId, SubagentTraceStatus, SubagentTraceSummary,
62};
63use crate::transcript::TranscriptItem;
64use crate::workflow::{WorkflowImportDecision, WorkflowImportError, WorkflowImportItem};
65use crate::workspace_changes::WorkspaceChangeObservation;
66
67pub use crate::policy_mode::{
68    PolicyBypassActive, PolicyDecisionRecorded, PolicyExitPlanRequested, PolicyExitPlanResolved,
69    PolicyModeChanged,
70};
71pub use crate::tasks::{TaskCancelled, TaskCompleted, TaskFailed, TaskOutput, TaskStarted};
72
73pub type ThreadId = String;
74pub type TurnId = String;
75pub type EventId = String;
76
77#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
78pub enum EventSource {
79    Runtime,
80    Core,
81    Provider,
82    Tool,
83    AppServer,
84    Tui,
85    Extension,
86    System,
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct RuntimeStarted {
91    #[serde(with = "time::serde::rfc3339")]
92    pub timestamp: OffsetDateTime,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
96pub struct ExtensionRegistered {
97    pub extension_id: ExtensionId,
98    #[serde(with = "time::serde::rfc3339")]
99    pub timestamp: OffsetDateTime,
100}
101
102#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct ThreadCreated {
104    pub thread_id: ThreadId,
105    #[serde(with = "time::serde::rfc3339")]
106    pub timestamp: OffsetDateTime,
107}
108
109/// A conversation fork into a worktree-backed child thread was requested.
110#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct ThreadForkRequested {
112    pub parent_thread_id: ThreadId,
113    pub name: String,
114    #[serde(with = "time::serde::rfc3339")]
115    pub timestamp: OffsetDateTime,
116}
117
118/// A child thread was created with its workspace fork.
119#[derive(Debug, Clone, Serialize, Deserialize)]
120pub struct ThreadForked {
121    pub parent_thread_id: ThreadId,
122    pub child_thread_id: ThreadId,
123    pub fork: crate::forks::WorkspaceFork,
124    #[serde(with = "time::serde::rfc3339")]
125    pub timestamp: OffsetDateTime,
126}
127
128/// A requested conversation fork failed (worktree or thread creation).
129#[derive(Debug, Clone, Serialize, Deserialize)]
130pub struct ThreadForkFailed {
131    pub parent_thread_id: ThreadId,
132    pub name: String,
133    pub message: String,
134    #[serde(with = "time::serde::rfc3339")]
135    pub timestamp: OffsetDateTime,
136}
137
138/// A fork's worktree was explicitly removed.
139#[derive(Debug, Clone, Serialize, Deserialize)]
140pub struct ThreadForkRemoved {
141    pub thread_id: ThreadId,
142    pub fork_id: String,
143    pub worktree_path: String,
144    #[serde(with = "time::serde::rfc3339")]
145    pub timestamp: OffsetDateTime,
146}
147
148/// A typed event emitted by an extension (e.g. a process-hosted child)
149/// through the extension-owned event channel. Payloads are redacted and
150/// schema-versioned by the emitter; the host enforces a size cap.
151#[derive(Debug, Clone, Serialize, Deserialize)]
152pub struct ExtensionEventEmitted {
153    pub extension_id: String,
154    pub event_kind: String,
155    pub schema_version: u32,
156    pub payload: serde_json::Value,
157    #[serde(with = "time::serde::rfc3339")]
158    pub timestamp: OffsetDateTime,
159}
160
161/// An event sink failed or timed out while handling an envelope. The
162/// message is redacted; sink-failure events are never re-dispatched to
163/// sinks, so a broken sink cannot create an event loop.
164#[derive(Debug, Clone, Serialize, Deserialize)]
165pub struct EventSinkFailed {
166    pub sink_id: String,
167    pub event_kind: String,
168    pub message: String,
169    #[serde(with = "time::serde::rfc3339")]
170    pub timestamp: OffsetDateTime,
171}
172
173#[derive(Debug, Clone, Serialize, Deserialize)]
174pub struct ThreadLoaded {
175    pub thread_id: ThreadId,
176    #[serde(with = "time::serde::rfc3339")]
177    pub timestamp: OffsetDateTime,
178}
179
180#[derive(Debug, Clone, Serialize, Deserialize)]
181pub struct TurnStarted {
182    pub thread_id: ThreadId,
183    pub turn_id: TurnId,
184    #[serde(default)]
185    pub runtime_profile: RuntimeProfile,
186    #[serde(with = "time::serde::rfc3339")]
187    pub timestamp: OffsetDateTime,
188}
189
190#[derive(Debug, Clone, Serialize, Deserialize)]
191pub struct ContextAssemblyStarted {
192    pub thread_id: ThreadId,
193    pub turn_id: TurnId,
194    #[serde(with = "time::serde::rfc3339")]
195    pub timestamp: OffsetDateTime,
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize)]
199pub struct ContextBlockAdded {
200    pub thread_id: ThreadId,
201    pub turn_id: TurnId,
202    pub block_type: String,
203    #[serde(default)]
204    pub byte_count: u64,
205    #[serde(default)]
206    pub estimated_tokens: u32,
207    #[serde(default)]
208    pub priority: i32,
209    #[serde(with = "time::serde::rfc3339")]
210    pub timestamp: OffsetDateTime,
211}
212
213#[derive(Debug, Clone, Serialize, Deserialize)]
214pub struct ContextAssemblyCompleted {
215    pub thread_id: ThreadId,
216    pub turn_id: TurnId,
217    #[serde(default)]
218    pub block_count: u64,
219    #[serde(default)]
220    pub total_byte_count: u64,
221    #[serde(default)]
222    pub estimated_tokens: u32,
223    #[serde(default)]
224    pub prompt_estimated_tokens: u32,
225    #[serde(default, skip_serializing_if = "Option::is_none")]
226    pub token_budget: Option<u32>,
227    #[serde(with = "time::serde::rfc3339")]
228    pub timestamp: OffsetDateTime,
229}
230
231#[derive(Debug, Clone, Serialize, Deserialize)]
232pub struct ContextEntrypointCandidatesInjected {
233    pub thread_id: ThreadId,
234    pub turn_id: TurnId,
235    pub candidate_count: u64,
236    pub block_byte_count: u64,
237    pub estimated_tokens: u32,
238    #[serde(with = "time::serde::rfc3339")]
239    pub timestamp: OffsetDateTime,
240}
241
242#[derive(Debug, Clone, Serialize, Deserialize)]
243pub struct ContextCompactionStarted {
244    pub thread_id: ThreadId,
245    pub turn_id: TurnId,
246    pub original_item_count: u64,
247    pub original_estimated_tokens: u32,
248    #[serde(with = "time::serde::rfc3339")]
249    pub timestamp: OffsetDateTime,
250}
251
252#[derive(Debug, Clone, Serialize, Deserialize)]
253pub struct ContextCompactionRecorded {
254    pub thread_id: ThreadId,
255    pub turn_id: TurnId,
256    pub original_item_count: u64,
257    pub original_estimated_tokens: u32,
258    pub compacted_item_count: u64,
259    pub compacted_estimated_tokens: u32,
260    pub file_backed: bool,
261    #[serde(with = "time::serde::rfc3339")]
262    pub timestamp: OffsetDateTime,
263}
264
265#[derive(Debug, Clone, Serialize, Deserialize)]
266pub struct InferenceStarted {
267    pub thread_id: ThreadId,
268    pub turn_id: TurnId,
269    pub engine_id: InferenceEngineId,
270    #[serde(default = "default_model_selection")]
271    pub model: ModelSelection,
272    #[serde(default)]
273    pub reasoning: ReasoningConfig,
274    #[serde(default, skip_serializing_if = "Option::is_none")]
275    pub speed_policy: Option<SpeedPolicyDecision>,
276    #[serde(default, skip_serializing_if = "Option::is_none")]
277    pub deadline_remaining_seconds: Option<u64>,
278    #[serde(with = "time::serde::rfc3339")]
279    pub timestamp: OffsetDateTime,
280}
281
282fn default_model_selection() -> ModelSelection {
283    ModelSelection {
284        provider: String::new(),
285        model: String::new(),
286    }
287}
288
289#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
290pub struct InferenceRoutingDecisionEvent {
291    pub thread_id: ThreadId,
292    pub turn_id: TurnId,
293    #[serde(default)]
294    pub round_index: u32,
295    pub default_selection: ModelSelection,
296    pub selected_selection: ModelSelection,
297    pub decision: InferenceRoutingDecision,
298    #[serde(with = "time::serde::rfc3339")]
299    pub timestamp: OffsetDateTime,
300}
301
302#[derive(Debug, Clone, Serialize, Deserialize)]
303pub struct InferenceEventReceived {
304    pub thread_id: ThreadId,
305    pub turn_id: TurnId,
306    pub event: InferenceEvent,
307    #[serde(with = "time::serde::rfc3339")]
308    pub timestamp: OffsetDateTime,
309}
310
311#[derive(Debug, Clone, Serialize, Deserialize)]
312pub struct ToolCallRequested {
313    pub thread_id: ThreadId,
314    pub turn_id: TurnId,
315    pub tool_id: String,
316    pub tool_name: String,
317    #[serde(default, skip_serializing_if = "Option::is_none")]
318    pub display_payload: Option<serde_json::Value>,
319    #[serde(with = "time::serde::rfc3339")]
320    pub timestamp: OffsetDateTime,
321}
322
323#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
324#[serde(rename_all = "snake_case")]
325pub enum ToolCallValidationFailureClass {
326    InvalidJson,
327    UnknownTool,
328    MissingRequired,
329    UnexpectedProperty,
330    WrongType,
331    EmptyRequiredString,
332    SchemaRepairApplied,
333    SchemaRepairRejected,
334}
335
336#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
337#[serde(rename_all = "snake_case")]
338pub enum ToolCallValidationRepairStatus {
339    NotNeeded,
340    Applied,
341    Rejected,
342}
343
344#[derive(Debug, Clone, Serialize, Deserialize)]
345pub struct ToolCallValidationRecorded {
346    pub thread_id: ThreadId,
347    pub turn_id: TurnId,
348    pub tool_id: String,
349    pub tool_name: String,
350    pub failure_class: ToolCallValidationFailureClass,
351    pub repair_status: ToolCallValidationRepairStatus,
352    pub message: String,
353    #[serde(with = "time::serde::rfc3339")]
354    pub timestamp: OffsetDateTime,
355}
356
357#[derive(Debug, Clone, Serialize, Deserialize)]
358pub struct ApprovalRequested {
359    pub thread_id: ThreadId,
360    pub turn_id: TurnId,
361    pub approval_id: String,
362    pub tool_id: String,
363    pub tool_name: String,
364    pub reason: Option<String>,
365    #[serde(with = "time::serde::rfc3339")]
366    pub timestamp: OffsetDateTime,
367}
368
369#[derive(Debug, Clone, Serialize, Deserialize)]
370pub struct ApprovalResolved {
371    pub thread_id: ThreadId,
372    pub turn_id: TurnId,
373    pub approval_id: String,
374    pub tool_id: String,
375    pub tool_name: String,
376    pub approved: bool,
377    #[serde(with = "time::serde::rfc3339")]
378    pub timestamp: OffsetDateTime,
379}
380
381#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
382#[serde(rename_all = "camelCase")]
383pub enum ExternalToolCallOutcome {
384    Resolved,
385    TimedOut,
386    Cancelled,
387}
388
389#[derive(Debug, Clone, Serialize, Deserialize)]
390pub struct ExternalToolCallRequested {
391    pub thread_id: ThreadId,
392    pub turn_id: TurnId,
393    pub request_id: String,
394    pub tool_id: String,
395    pub tool_name: String,
396    pub arguments: serde_json::Value,
397    #[serde(with = "time::serde::rfc3339")]
398    pub timestamp: OffsetDateTime,
399}
400
401#[derive(Debug, Clone, Serialize, Deserialize)]
402pub struct ExternalToolCallResolved {
403    pub thread_id: ThreadId,
404    pub turn_id: TurnId,
405    pub request_id: String,
406    pub tool_id: String,
407    pub tool_name: String,
408    pub outcome: ExternalToolCallOutcome,
409    pub is_error: bool,
410    #[serde(with = "time::serde::rfc3339")]
411    pub timestamp: OffsetDateTime,
412}
413
414#[derive(Debug, Clone, Serialize, Deserialize)]
415pub struct UserInputRequested {
416    pub thread_id: ThreadId,
417    pub turn_id: TurnId,
418    pub request_id: String,
419    pub questions: serde_json::Value,
420    #[serde(with = "time::serde::rfc3339")]
421    pub timestamp: OffsetDateTime,
422}
423
424#[derive(Debug, Clone, Serialize, Deserialize)]
425pub struct UserInputResolved {
426    pub thread_id: ThreadId,
427    pub turn_id: TurnId,
428    pub request_id: String,
429    pub answers: serde_json::Value,
430    #[serde(with = "time::serde::rfc3339")]
431    pub timestamp: OffsetDateTime,
432}
433
434#[derive(Debug, Clone, Serialize, Deserialize)]
435pub struct TaskLedgerUpdated {
436    pub thread_id: ThreadId,
437    pub turn_id: TurnId,
438    pub tasks: Vec<TaskLedgerItem>,
439    pub completed_count: u64,
440    #[serde(with = "time::serde::rfc3339")]
441    pub timestamp: OffsetDateTime,
442}
443
444#[derive(Debug, Clone, Serialize, Deserialize)]
445pub struct VerificationRequired {
446    pub thread_id: ThreadId,
447    pub turn_id: TurnId,
448    pub reason: String,
449    pub changed_files: Vec<String>,
450    pub tool_evidence: Vec<String>,
451    pub tests_run: Vec<String>,
452    pub open_gaps: Vec<String>,
453    #[serde(with = "time::serde::rfc3339")]
454    pub timestamp: OffsetDateTime,
455}
456
457#[derive(Debug, Clone, Serialize, Deserialize)]
458pub struct VerificationCompleted {
459    pub thread_id: ThreadId,
460    pub turn_id: TurnId,
461    pub passed: bool,
462    pub changed_files: Vec<String>,
463    pub tool_evidence: Vec<String>,
464    pub tests_run: Vec<String>,
465    pub open_gaps: Vec<String>,
466    #[serde(with = "time::serde::rfc3339")]
467    pub timestamp: OffsetDateTime,
468}
469
470#[derive(Debug, Clone, Serialize, Deserialize)]
471pub struct VerificationSkipped {
472    pub thread_id: ThreadId,
473    pub turn_id: TurnId,
474    pub reason: String,
475    #[serde(with = "time::serde::rfc3339")]
476    pub timestamp: OffsetDateTime,
477}
478
479#[derive(Debug, Clone, Serialize, Deserialize)]
480pub struct ToolCallStarted {
481    pub thread_id: ThreadId,
482    pub turn_id: TurnId,
483    pub tool_id: String,
484    #[serde(default, skip_serializing_if = "Option::is_none")]
485    pub tool_name: Option<String>,
486    #[serde(default, skip_serializing_if = "Option::is_none")]
487    pub display_payload: Option<serde_json::Value>,
488    #[serde(with = "time::serde::rfc3339")]
489    pub timestamp: OffsetDateTime,
490}
491
492#[derive(Debug, Clone, Serialize, Deserialize)]
493pub struct ToolCallCompleted {
494    pub thread_id: ThreadId,
495    pub turn_id: TurnId,
496    pub tool_id: String,
497    #[serde(default, skip_serializing_if = "Option::is_none")]
498    pub tool_name: Option<String>,
499    #[serde(default, skip_serializing_if = "Option::is_none")]
500    pub display_payload: Option<serde_json::Value>,
501    #[serde(default)]
502    pub is_error: bool,
503    #[serde(default)]
504    pub output: Option<String>,
505    #[serde(with = "time::serde::rfc3339")]
506    pub timestamp: OffsetDateTime,
507}
508
509#[derive(Debug, Clone, Serialize, Deserialize)]
510pub struct ToolOutputTruncated {
511    pub thread_id: ThreadId,
512    pub turn_id: TurnId,
513    pub tool_id: String,
514    #[serde(default, skip_serializing_if = "Option::is_none")]
515    pub tool_name: Option<String>,
516    pub original_line_count: u64,
517    pub original_char_count: u64,
518    pub inline_char_count: u64,
519    pub artifact_backed: bool,
520    #[serde(with = "time::serde::rfc3339")]
521    pub timestamp: OffsetDateTime,
522}
523
524#[derive(Debug, Clone, Serialize, Deserialize)]
525pub struct SubagentStarted {
526    pub thread_id: ThreadId,
527    pub turn_id: TurnId,
528    pub parent_thread_id: ThreadId,
529    pub parent_turn_id: TurnId,
530    pub agent_type: String,
531    pub description: String,
532    pub model: Option<String>,
533    #[serde(with = "time::serde::rfc3339")]
534    pub timestamp: OffsetDateTime,
535}
536
537#[derive(Debug, Clone, Serialize, Deserialize)]
538pub struct SubagentMessage {
539    pub thread_id: ThreadId,
540    pub turn_id: TurnId,
541    pub parent_thread_id: ThreadId,
542    pub parent_turn_id: TurnId,
543    pub agent_type: String,
544    pub text: String,
545    #[serde(with = "time::serde::rfc3339")]
546    pub timestamp: OffsetDateTime,
547}
548
549#[derive(Debug, Clone, Serialize, Deserialize)]
550pub struct SubagentToolCall {
551    pub thread_id: ThreadId,
552    pub turn_id: TurnId,
553    pub parent_thread_id: ThreadId,
554    pub parent_turn_id: TurnId,
555    pub agent_type: String,
556    pub tool_id: String,
557    pub tool_name: String,
558    #[serde(with = "time::serde::rfc3339")]
559    pub timestamp: OffsetDateTime,
560}
561
562#[derive(Debug, Clone, Serialize, Deserialize)]
563pub struct SubagentCompleted {
564    pub thread_id: ThreadId,
565    pub turn_id: TurnId,
566    pub parent_thread_id: ThreadId,
567    pub parent_turn_id: TurnId,
568    pub agent_type: String,
569    pub exit_reason: SubagentExitReason,
570    #[serde(with = "time::serde::rfc3339")]
571    pub timestamp: OffsetDateTime,
572}
573
574#[derive(Debug, Clone, Serialize, Deserialize)]
575pub struct SubagentFailed {
576    pub thread_id: ThreadId,
577    pub turn_id: TurnId,
578    pub parent_thread_id: ThreadId,
579    pub parent_turn_id: TurnId,
580    pub agent_type: String,
581    pub error: String,
582    #[serde(with = "time::serde::rfc3339")]
583    pub timestamp: OffsetDateTime,
584}
585
586#[derive(Debug, Clone, Serialize, Deserialize)]
587pub struct SubagentTraceCreated {
588    pub summary: SubagentTraceSummary,
589    #[serde(with = "time::serde::rfc3339")]
590    pub timestamp: OffsetDateTime,
591}
592
593#[derive(Debug, Clone, Serialize, Deserialize)]
594pub struct SubagentTraceDeltaEvent {
595    pub delta: SubagentTraceDelta,
596    #[serde(with = "time::serde::rfc3339")]
597    pub timestamp: OffsetDateTime,
598}
599
600#[derive(Debug, Clone, Serialize, Deserialize)]
601pub struct SubagentTraceStatusChanged {
602    pub trace_id: SubagentTraceId,
603    pub parent: ParentTurnRef,
604    pub status: SubagentTraceStatus,
605    #[serde(default, skip_serializing_if = "Option::is_none")]
606    pub detail: Option<String>,
607    #[serde(with = "time::serde::rfc3339")]
608    pub timestamp: OffsetDateTime,
609}
610
611#[derive(Debug, Clone, Serialize, Deserialize)]
612pub struct SubagentTraceCompleted {
613    pub summary: SubagentTraceSummary,
614    #[serde(with = "time::serde::rfc3339")]
615    pub timestamp: OffsetDateTime,
616}
617
618#[derive(Debug, Clone, Serialize, Deserialize)]
619pub struct SubagentTraceFailed {
620    pub summary: SubagentTraceSummary,
621    pub error: String,
622    #[serde(with = "time::serde::rfc3339")]
623    pub timestamp: OffsetDateTime,
624}
625
626#[derive(Debug, Clone, Serialize, Deserialize)]
627#[serde(rename_all = "camelCase")]
628pub struct PlanReviewCreated {
629    pub review: PlanReview,
630    #[serde(with = "time::serde::rfc3339")]
631    pub timestamp: OffsetDateTime,
632}
633
634#[derive(Debug, Clone, Serialize, Deserialize)]
635#[serde(rename_all = "camelCase")]
636pub struct PlanReviewStatusChanged {
637    pub thread_id: ThreadId,
638    pub turn_id: TurnId,
639    pub review_id: PlanReviewId,
640    pub status: PlanReviewStatus,
641    #[serde(default, skip_serializing_if = "Option::is_none")]
642    pub detail: Option<String>,
643    #[serde(with = "time::serde::rfc3339")]
644    pub timestamp: OffsetDateTime,
645}
646
647#[derive(Debug, Clone, Serialize, Deserialize)]
648#[serde(rename_all = "camelCase")]
649pub struct PlanReviewCommentAdded {
650    pub thread_id: ThreadId,
651    pub turn_id: TurnId,
652    pub review_id: PlanReviewId,
653    pub comment: PlanComment,
654    #[serde(with = "time::serde::rfc3339")]
655    pub timestamp: OffsetDateTime,
656}
657
658#[derive(Debug, Clone, Serialize, Deserialize)]
659#[serde(rename_all = "camelCase")]
660pub struct PlanReviewRewritten {
661    pub thread_id: ThreadId,
662    pub turn_id: TurnId,
663    pub review_id: PlanReviewId,
664    pub rewrite: PlanRewrite,
665    #[serde(with = "time::serde::rfc3339")]
666    pub timestamp: OffsetDateTime,
667}
668
669#[derive(Debug, Clone, Serialize, Deserialize)]
670#[serde(rename_all = "camelCase")]
671pub struct PlanReviewApproved {
672    pub thread_id: ThreadId,
673    pub turn_id: TurnId,
674    pub review_id: PlanReviewId,
675    #[serde(with = "time::serde::rfc3339")]
676    pub timestamp: OffsetDateTime,
677}
678
679#[derive(Debug, Clone, Serialize, Deserialize)]
680#[serde(rename_all = "camelCase")]
681pub struct PlanReviewRejected {
682    pub thread_id: ThreadId,
683    pub turn_id: TurnId,
684    pub review_id: PlanReviewId,
685    #[serde(default, skip_serializing_if = "Option::is_none")]
686    pub reason: Option<String>,
687    #[serde(with = "time::serde::rfc3339")]
688    pub timestamp: OffsetDateTime,
689}
690
691#[derive(Debug, Clone, Serialize, Deserialize)]
692#[serde(rename_all = "camelCase")]
693pub struct HunkRecorded {
694    pub hunk: HunkRecord,
695    #[serde(with = "time::serde::rfc3339")]
696    pub timestamp: OffsetDateTime,
697}
698
699#[derive(Debug, Clone, Serialize, Deserialize)]
700#[serde(rename_all = "camelCase")]
701pub struct WorkspaceChangeObserved {
702    pub change: WorkspaceChangeObservation,
703    #[serde(with = "time::serde::rfc3339")]
704    pub timestamp: OffsetDateTime,
705}
706
707#[derive(Debug, Clone, Serialize, Deserialize)]
708#[serde(rename_all = "camelCase")]
709pub struct HunkRollbackRequested {
710    pub thread_id: ThreadId,
711    pub turn_id: TurnId,
712    pub hunk_id: HunkId,
713    #[serde(with = "time::serde::rfc3339")]
714    pub timestamp: OffsetDateTime,
715}
716
717#[derive(Debug, Clone, Serialize, Deserialize)]
718#[serde(rename_all = "camelCase")]
719pub struct HunkRollbackCompleted {
720    pub thread_id: ThreadId,
721    pub turn_id: TurnId,
722    pub hunk_id: HunkId,
723    #[serde(default, skip_serializing_if = "Option::is_none")]
724    pub error: Option<String>,
725    #[serde(with = "time::serde::rfc3339")]
726    pub timestamp: OffsetDateTime,
727}
728
729#[derive(Debug, Clone, Serialize, Deserialize)]
730#[serde(rename_all = "camelCase")]
731pub struct WorkflowImportsDetected {
732    pub workspace: String,
733    pub items: Vec<WorkflowImportItem>,
734    #[serde(default, skip_serializing_if = "Vec::is_empty")]
735    pub errors: Vec<WorkflowImportError>,
736    #[serde(with = "time::serde::rfc3339")]
737    pub timestamp: OffsetDateTime,
738}
739
740#[derive(Debug, Clone, Serialize, Deserialize)]
741#[serde(rename_all = "camelCase")]
742pub struct WorkflowImportPreviewed {
743    pub item: WorkflowImportItem,
744    #[serde(with = "time::serde::rfc3339")]
745    pub timestamp: OffsetDateTime,
746}
747
748#[derive(Debug, Clone, Serialize, Deserialize)]
749#[serde(rename_all = "camelCase")]
750pub struct WorkflowImportEnabled {
751    pub item: WorkflowImportItem,
752    pub decision: WorkflowImportDecision,
753    #[serde(with = "time::serde::rfc3339")]
754    pub timestamp: OffsetDateTime,
755}
756
757#[derive(Debug, Clone, Serialize, Deserialize)]
758#[serde(rename_all = "camelCase")]
759pub struct WorkflowImportDisabled {
760    pub item_id: String,
761    pub decision: WorkflowImportDecision,
762    #[serde(with = "time::serde::rfc3339")]
763    pub timestamp: OffsetDateTime,
764}
765
766#[derive(Debug, Clone, Serialize, Deserialize)]
767#[serde(rename_all = "camelCase")]
768pub struct WorkflowImportStale {
769    pub item: WorkflowImportItem,
770    pub previous_hash: String,
771    #[serde(with = "time::serde::rfc3339")]
772    pub timestamp: OffsetDateTime,
773}
774
775#[derive(Debug, Clone, Serialize, Deserialize)]
776#[serde(rename_all = "camelCase")]
777pub struct WorkflowImportFailed {
778    pub item_id: Option<String>,
779    pub error: WorkflowImportError,
780    #[serde(with = "time::serde::rfc3339")]
781    pub timestamp: OffsetDateTime,
782}
783
784#[derive(Debug, Clone, Serialize, Deserialize)]
785#[serde(rename_all = "camelCase")]
786pub struct MediaArtifactCreated {
787    pub thread_id: ThreadId,
788    pub turn_id: TurnId,
789    pub artifact: MediaArtifact,
790    #[serde(with = "time::serde::rfc3339")]
791    pub timestamp: OffsetDateTime,
792}
793
794#[derive(Debug, Clone, Serialize, Deserialize)]
795#[serde(rename_all = "camelCase")]
796pub struct MediaArtifactUpdated {
797    pub thread_id: ThreadId,
798    pub turn_id: TurnId,
799    pub artifact: MediaArtifact,
800    #[serde(with = "time::serde::rfc3339")]
801    pub timestamp: OffsetDateTime,
802}
803
804#[derive(Debug, Clone, Serialize, Deserialize)]
805#[serde(rename_all = "camelCase")]
806pub struct MediaArtifactDeleted {
807    pub artifact_id: MediaArtifactId,
808    #[serde(with = "time::serde::rfc3339")]
809    pub timestamp: OffsetDateTime,
810}
811
812#[derive(Debug, Clone, Serialize, Deserialize)]
813#[serde(rename_all = "camelCase")]
814pub struct MediaPreviewReady {
815    pub thread_id: ThreadId,
816    pub turn_id: TurnId,
817    pub preview: MediaPreview,
818    #[serde(with = "time::serde::rfc3339")]
819    pub timestamp: OffsetDateTime,
820}
821
822#[derive(Debug, Clone, Serialize, Deserialize)]
823#[serde(rename_all = "camelCase")]
824pub struct ContextArtifactCreated {
825    pub thread_id: ThreadId,
826    pub turn_id: TurnId,
827    pub artifact: ContextArtifact,
828    #[serde(with = "time::serde::rfc3339")]
829    pub timestamp: OffsetDateTime,
830}
831
832#[derive(Debug, Clone, Serialize, Deserialize)]
833#[serde(rename_all = "camelCase")]
834pub struct ContextArtifactAppended {
835    pub thread_id: ThreadId,
836    pub turn_id: TurnId,
837    pub artifact_id: ContextArtifactId,
838    pub appended_bytes: u64,
839    pub byte_count: u64,
840    pub line_count: u64,
841    #[serde(with = "time::serde::rfc3339")]
842    pub timestamp: OffsetDateTime,
843}
844
845#[derive(Debug, Clone, Serialize, Deserialize)]
846#[serde(rename_all = "camelCase")]
847pub struct ContextArtifactCapped {
848    pub thread_id: ThreadId,
849    pub turn_id: TurnId,
850    pub artifact_id: ContextArtifactId,
851    pub inline_byte_count: u64,
852    pub original_byte_count: u64,
853    #[serde(with = "time::serde::rfc3339")]
854    pub timestamp: OffsetDateTime,
855}
856
857#[derive(Debug, Clone, Serialize, Deserialize)]
858#[serde(rename_all = "camelCase")]
859pub struct ContextArtifactDeleted {
860    pub thread_id: ThreadId,
861    pub artifact_id: ContextArtifactId,
862    #[serde(with = "time::serde::rfc3339")]
863    pub timestamp: OffsetDateTime,
864}
865
866#[derive(Debug, Clone, Serialize, Deserialize)]
867#[serde(rename_all = "camelCase")]
868pub struct ContextArtifactRetentionExpired {
869    pub thread_id: ThreadId,
870    pub artifact_id: ContextArtifactId,
871    #[serde(with = "time::serde::rfc3339")]
872    pub timestamp: OffsetDateTime,
873}
874
875#[derive(Debug, Clone, Serialize, Deserialize)]
876pub struct FileChanged {
877    pub thread_id: ThreadId,
878    pub turn_id: TurnId,
879    pub path: String,
880    pub change_type: String,
881    #[serde(with = "time::serde::rfc3339")]
882    pub timestamp: OffsetDateTime,
883}
884
885#[derive(Debug, Clone, Serialize, Deserialize)]
886pub struct FileChangePreviewReady {
887    pub thread_id: ThreadId,
888    pub turn_id: TurnId,
889    pub tool_id: String,
890    pub tool_name: String,
891    pub path: String,
892    pub change_type: String,
893    pub before: Option<String>,
894    pub after: String,
895    pub supports_partial: bool,
896    #[serde(with = "time::serde::rfc3339")]
897    pub timestamp: OffsetDateTime,
898}
899
900#[derive(Debug, Clone, Serialize, Deserialize)]
901pub struct TranscriptItemAppended {
902    pub thread_id: ThreadId,
903    pub turn_id: TurnId,
904    pub item_type: String,
905    #[serde(default, skip_serializing_if = "Option::is_none")]
906    pub item_index: Option<usize>,
907    /// Full transcript item for runtime appends. `None` means the record carries
908    /// append metadata without an embedded transcript item.
909    #[serde(default, skip_serializing_if = "Option::is_none")]
910    pub item: Option<TranscriptItem>,
911    #[serde(with = "time::serde::rfc3339")]
912    pub timestamp: OffsetDateTime,
913}
914
915#[derive(Debug, Clone, Serialize, Deserialize)]
916pub struct TurnCompleted {
917    pub thread_id: ThreadId,
918    pub turn_id: TurnId,
919    #[serde(default, skip_serializing_if = "Option::is_none")]
920    pub usage: Option<TokenUsage>,
921    /**
922     * Normalized stop reason of the turn's terminal inference step; see
923     * `crate::inference::finish_reason_from_stop_reason` for the vocabulary.
924     */
925    #[serde(default, skip_serializing_if = "Option::is_none")]
926    pub finish_reason: Option<String>,
927    #[serde(with = "time::serde::rfc3339")]
928    pub timestamp: OffsetDateTime,
929}
930
931#[derive(Debug, Clone, Serialize, Deserialize)]
932pub struct TurnFailed {
933    pub thread_id: ThreadId,
934    pub turn_id: TurnId,
935    pub error: String,
936    #[serde(default, skip_serializing_if = "Option::is_none")]
937    pub error_kind: Option<String>,
938    #[serde(default, skip_serializing_if = "Option::is_none")]
939    pub usage: Option<TokenUsage>,
940    #[serde(with = "time::serde::rfc3339")]
941    pub timestamp: OffsetDateTime,
942}
943
944#[derive(Debug, Clone, Serialize, Deserialize)]
945pub struct TurnPartialResult {
946    pub thread_id: ThreadId,
947    pub turn_id: TurnId,
948    pub summary: String,
949    #[serde(with = "time::serde::rfc3339")]
950    pub timestamp: OffsetDateTime,
951}
952
953#[derive(Debug, Clone, Serialize, Deserialize)]
954pub struct TurnDeadlineExceeded {
955    pub thread_id: ThreadId,
956    pub turn_id: TurnId,
957    #[serde(with = "time::serde::rfc3339")]
958    pub deadline: OffsetDateTime,
959    pub partial_result: String,
960    #[serde(with = "time::serde::rfc3339")]
961    pub timestamp: OffsetDateTime,
962}
963
964#[derive(Debug, Clone, Serialize, Deserialize)]
965pub struct TurnInterrupted {
966    pub thread_id: ThreadId,
967    pub turn_id: TurnId,
968    #[serde(with = "time::serde::rfc3339")]
969    pub timestamp: OffsetDateTime,
970}
971
972#[derive(Debug, Clone, Serialize, Deserialize)]
973pub struct TurnSteered {
974    pub thread_id: ThreadId,
975    pub turn_id: TurnId,
976    pub message: String,
977    #[serde(with = "time::serde::rfc3339")]
978    pub timestamp: OffsetDateTime,
979}
980
981#[derive(Debug, Clone, Serialize, Deserialize)]
982pub struct RunnerLifecycle {
983    pub destination_id: String,
984    pub provider_id: String,
985    pub state: String,
986    #[serde(default, skip_serializing_if = "Option::is_none")]
987    pub session_id: Option<String>,
988    #[serde(with = "time::serde::rfc3339")]
989    pub timestamp: OffsetDateTime,
990}
991
992#[derive(Debug, Clone, Serialize, Deserialize)]
993pub struct TeamStarted {
994    pub team_id: TeamId,
995    pub lead_thread_id: ThreadId,
996    pub display_mode: AgentTeamDisplayMode,
997    #[serde(with = "time::serde::rfc3339")]
998    pub timestamp: OffsetDateTime,
999}
1000
1001#[derive(Debug, Clone, Serialize, Deserialize)]
1002pub struct TeamMemberStarted {
1003    pub team_id: TeamId,
1004    pub member_id: TeamMemberId,
1005    pub member_thread_id: ThreadId,
1006    pub role: TeamMemberRole,
1007    pub name: String,
1008    #[serde(with = "time::serde::rfc3339")]
1009    pub timestamp: OffsetDateTime,
1010}
1011
1012#[derive(Debug, Clone, Serialize, Deserialize)]
1013pub struct TeamMemberStatusChanged {
1014    pub team_id: TeamId,
1015    pub member_id: TeamMemberId,
1016    pub member_thread_id: ThreadId,
1017    pub status: TeamMemberStatus,
1018    #[serde(with = "time::serde::rfc3339")]
1019    pub timestamp: OffsetDateTime,
1020}
1021
1022#[derive(Debug, Clone, Serialize, Deserialize)]
1023pub struct TeamMemberMessageDelta {
1024    pub team_id: TeamId,
1025    pub member_id: TeamMemberId,
1026    pub member_thread_id: ThreadId,
1027    pub turn_id: TurnId,
1028    pub delta: String,
1029    #[serde(with = "time::serde::rfc3339")]
1030    pub timestamp: OffsetDateTime,
1031}
1032
1033#[derive(Debug, Clone, Serialize, Deserialize)]
1034pub struct TeamMemberCompleted {
1035    pub team_id: TeamId,
1036    pub member_id: TeamMemberId,
1037    pub member_thread_id: ThreadId,
1038    pub turn_id: Option<TurnId>,
1039    pub status: TeamMemberStatus,
1040    #[serde(with = "time::serde::rfc3339")]
1041    pub timestamp: OffsetDateTime,
1042}
1043
1044#[derive(Debug, Clone, Serialize, Deserialize)]
1045pub struct TeamDisplayModeChanged {
1046    pub team_id: TeamId,
1047    pub display_mode: AgentTeamDisplayMode,
1048    #[serde(with = "time::serde::rfc3339")]
1049    pub timestamp: OffsetDateTime,
1050}
1051
1052#[derive(Debug, Clone, Serialize, Deserialize)]
1053pub struct TeamTaskChanged {
1054    pub team_id: TeamId,
1055    pub task: TeamTaskDescriptor,
1056    #[serde(with = "time::serde::rfc3339")]
1057    pub timestamp: OffsetDateTime,
1058}
1059
1060#[derive(Debug, Clone, Serialize, Deserialize)]
1061pub struct TeamCleanupCompleted {
1062    pub team_id: TeamId,
1063    pub forced: bool,
1064    #[serde(with = "time::serde::rfc3339")]
1065    pub timestamp: OffsetDateTime,
1066}
1067
1068#[derive(Debug, Clone, Serialize, Deserialize)]
1069pub struct MemorySaved {
1070    pub memory: MemoryRecord,
1071    #[serde(with = "time::serde::rfc3339")]
1072    pub timestamp: OffsetDateTime,
1073}
1074
1075#[derive(Debug, Clone, Serialize, Deserialize)]
1076pub struct MemoryUpdated {
1077    pub memory: MemoryRecord,
1078    #[serde(with = "time::serde::rfc3339")]
1079    pub timestamp: OffsetDateTime,
1080}
1081
1082#[derive(Debug, Clone, Serialize, Deserialize)]
1083pub struct MemoryDeleted {
1084    pub memory_id: MemoryId,
1085    #[serde(with = "time::serde::rfc3339")]
1086    pub timestamp: OffsetDateTime,
1087}
1088
1089#[derive(Debug, Clone, Serialize, Deserialize)]
1090pub struct MemoryQueried {
1091    pub scope: Option<MemoryScope>,
1092    pub query: String,
1093    pub result_count: usize,
1094    #[serde(with = "time::serde::rfc3339")]
1095    pub timestamp: OffsetDateTime,
1096}
1097
1098#[derive(Debug, Clone, Serialize, Deserialize)]
1099pub struct MemoryRecallReady {
1100    pub thread_id: ThreadId,
1101    pub turn_id: TurnId,
1102    pub citations: Vec<MemoryCitation>,
1103    #[serde(with = "time::serde::rfc3339")]
1104    pub timestamp: OffsetDateTime,
1105}
1106
1107#[derive(Debug, Clone, Serialize, Deserialize)]
1108pub struct MemoryReembedQueued {
1109    pub scope: Option<MemoryScope>,
1110    pub provider: MemoryProviderSelection,
1111    #[serde(with = "time::serde::rfc3339")]
1112    pub timestamp: OffsetDateTime,
1113}
1114
1115#[derive(Debug, Clone, Serialize, Deserialize)]
1116pub struct MemoryProviderChanged {
1117    pub provider: MemoryProviderSelection,
1118    #[serde(with = "time::serde::rfc3339")]
1119    pub timestamp: OffsetDateTime,
1120}
1121
1122#[derive(Debug, Clone, Serialize, Deserialize)]
1123pub struct MemoryObservationRecorded {
1124    pub thread_id: ThreadId,
1125    pub turn_id: TurnId,
1126    pub memory_id: MemoryId,
1127    #[serde(with = "time::serde::rfc3339")]
1128    pub timestamp: OffsetDateTime,
1129}
1130
1131#[derive(Debug, Clone, Serialize, Deserialize)]
1132pub struct KnowledgeSaved {
1133    pub document: KnowledgeDocSummary,
1134    #[serde(with = "time::serde::rfc3339")]
1135    pub timestamp: OffsetDateTime,
1136}
1137
1138#[derive(Debug, Clone, Serialize, Deserialize)]
1139pub struct KnowledgeUpdated {
1140    pub document: KnowledgeDocSummary,
1141    #[serde(with = "time::serde::rfc3339")]
1142    pub timestamp: OffsetDateTime,
1143}
1144
1145#[derive(Debug, Clone, Serialize, Deserialize)]
1146pub struct KnowledgeArchived {
1147    pub doc_id: KnowledgeDocId,
1148    #[serde(with = "time::serde::rfc3339")]
1149    pub timestamp: OffsetDateTime,
1150}
1151
1152#[derive(Debug, Clone, Serialize, Deserialize)]
1153pub struct KnowledgeLinked {
1154    pub from: KnowledgeDocId,
1155    pub to: KnowledgeDocId,
1156    pub link_type: KnowledgeLinkType,
1157    pub removed: bool,
1158    #[serde(with = "time::serde::rfc3339")]
1159    pub timestamp: OffsetDateTime,
1160}
1161
1162#[derive(Debug, Clone, Serialize, Deserialize)]
1163pub struct RemoteServerStarted {
1164    pub listen_addr: String,
1165    pub connect_urls: Vec<String>,
1166    pub token_preview: String,
1167    #[serde(with = "time::serde::rfc3339")]
1168    pub timestamp: OffsetDateTime,
1169}
1170
1171#[derive(Debug, Clone, Serialize, Deserialize)]
1172pub struct RemoteServerStopped {
1173    pub listen_addr: String,
1174    #[serde(with = "time::serde::rfc3339")]
1175    pub timestamp: OffsetDateTime,
1176}
1177
1178#[derive(Debug, Clone, Serialize, Deserialize)]
1179pub struct RemoteAuthFailed {
1180    pub remote_addr: Option<String>,
1181    #[serde(with = "time::serde::rfc3339")]
1182    pub timestamp: OffsetDateTime,
1183}
1184
1185#[derive(Debug, Clone, Serialize, Deserialize)]
1186pub struct RemoteClientConnected {
1187    pub remote_addr: Option<String>,
1188    #[serde(with = "time::serde::rfc3339")]
1189    pub timestamp: OffsetDateTime,
1190}
1191
1192#[derive(Debug, Clone, Serialize, Deserialize)]
1193pub struct RemoteClientDisconnected {
1194    pub remote_addr: Option<String>,
1195    #[serde(with = "time::serde::rfc3339")]
1196    pub timestamp: OffsetDateTime,
1197}
1198
1199#[derive(Debug, Clone, Serialize, Deserialize)]
1200pub struct RoadmapChanged {
1201    pub event_kind: String,
1202    pub path: String,
1203    pub task_id: Option<String>,
1204    pub thread_id: Option<String>,
1205    #[serde(with = "time::serde::rfc3339")]
1206    pub timestamp: OffsetDateTime,
1207}
1208
1209#[derive(Debug, Clone, Serialize, Deserialize)]
1210pub enum RoderEvent {
1211    RuntimeStarted(RuntimeStarted),
1212    ExtensionRegistered(ExtensionRegistered),
1213    ExtensionEventEmitted(ExtensionEventEmitted),
1214    EventSinkFailed(EventSinkFailed),
1215    ThreadCreated(ThreadCreated),
1216    ThreadLoaded(ThreadLoaded),
1217    ThreadForkRequested(ThreadForkRequested),
1218    ThreadForked(ThreadForked),
1219    ThreadForkFailed(ThreadForkFailed),
1220    ThreadForkRemoved(ThreadForkRemoved),
1221    TurnStarted(TurnStarted),
1222    ContextAssemblyStarted(ContextAssemblyStarted),
1223    ContextBlockAdded(ContextBlockAdded),
1224    ContextAssemblyCompleted(ContextAssemblyCompleted),
1225    ContextEntrypointCandidatesInjected(ContextEntrypointCandidatesInjected),
1226    ContextCompactionStarted(ContextCompactionStarted),
1227    ContextCompactionRecorded(ContextCompactionRecorded),
1228    InferenceRoutingDecision(InferenceRoutingDecisionEvent),
1229    InferenceStarted(InferenceStarted),
1230    InferenceEventReceived(InferenceEventReceived),
1231    ToolCallRequested(ToolCallRequested),
1232    ToolCallValidationRecorded(ToolCallValidationRecorded),
1233    ReliabilityFailureRecorded(ReliabilityFailureRecorded),
1234    ReliabilityRetryRecorded(ReliabilityRetryRecorded),
1235    ReliabilityLimitRecorded(ReliabilityLimitRecorded),
1236    ReliabilityMetricRecorded(ReliabilityMetricRecorded),
1237    CodeIndexingStarted(CodeIndexingStarted),
1238    CodeIndexChunked(CodeIndexChunked),
1239    CodeIndexEmbedded(CodeIndexEmbedded),
1240    CodeIndexReady(CodeIndexReady),
1241    CodeIndexStale(CodeIndexStale),
1242    CodeIndexFailed(CodeIndexFailed),
1243    CodeIndexProofFilteredResultDropped(CodeIndexProofFilteredResultDropped),
1244    ApprovalRequested(ApprovalRequested),
1245    ApprovalResolved(ApprovalResolved),
1246    ExternalToolCallRequested(ExternalToolCallRequested),
1247    ExternalToolCallResolved(ExternalToolCallResolved),
1248    UserInputRequested(UserInputRequested),
1249    UserInputResolved(UserInputResolved),
1250    TaskLedgerUpdated(TaskLedgerUpdated),
1251    VerificationRequired(VerificationRequired),
1252    VerificationCompleted(VerificationCompleted),
1253    VerificationSkipped(VerificationSkipped),
1254    PolicyDecisionRecorded(PolicyDecisionRecorded),
1255    PolicyBypassActive(PolicyBypassActive),
1256    PolicyModeChanged(PolicyModeChanged),
1257    PolicyExitPlanRequested(PolicyExitPlanRequested),
1258    PolicyExitPlanResolved(PolicyExitPlanResolved),
1259    ToolCallStarted(ToolCallStarted),
1260    ToolCallCompleted(ToolCallCompleted),
1261    ToolOutputTruncated(ToolOutputTruncated),
1262    SubagentStarted(SubagentStarted),
1263    SubagentMessage(SubagentMessage),
1264    SubagentToolCall(SubagentToolCall),
1265    SubagentCompleted(SubagentCompleted),
1266    SubagentFailed(SubagentFailed),
1267    SubagentTraceCreated(SubagentTraceCreated),
1268    SubagentTraceDelta(SubagentTraceDeltaEvent),
1269    SubagentTraceStatusChanged(SubagentTraceStatusChanged),
1270    SubagentTraceCompleted(SubagentTraceCompleted),
1271    SubagentTraceFailed(SubagentTraceFailed),
1272    PlanReviewCreated(PlanReviewCreated),
1273    PlanReviewStatusChanged(PlanReviewStatusChanged),
1274    PlanReviewCommentAdded(PlanReviewCommentAdded),
1275    PlanReviewRewritten(PlanReviewRewritten),
1276    PlanReviewApproved(PlanReviewApproved),
1277    PlanReviewRejected(PlanReviewRejected),
1278    HunkRecorded(HunkRecorded),
1279    WorkspaceChangeObserved(WorkspaceChangeObserved),
1280    HunkRollbackRequested(HunkRollbackRequested),
1281    HunkRollbackCompleted(HunkRollbackCompleted),
1282    WorkflowImportsDetected(WorkflowImportsDetected),
1283    WorkflowImportPreviewed(WorkflowImportPreviewed),
1284    WorkflowImportEnabled(WorkflowImportEnabled),
1285    WorkflowImportDisabled(WorkflowImportDisabled),
1286    WorkflowImportStale(WorkflowImportStale),
1287    WorkflowImportFailed(WorkflowImportFailed),
1288    WorkflowRunDrafted(WorkflowRunDrafted),
1289    WorkflowApprovalRequested(WorkflowApprovalRequested),
1290    WorkflowRunApproved(WorkflowRunApproved),
1291    WorkflowRunDenied(WorkflowRunDenied),
1292    WorkflowRunQueued(WorkflowRunQueued),
1293    WorkflowRunStarted(WorkflowRunStarted),
1294    WorkflowPhaseStarted(WorkflowPhaseStarted),
1295    WorkflowPhaseCompleted(WorkflowPhaseCompleted),
1296    WorkflowAgentQueued(WorkflowAgentQueued),
1297    WorkflowAgentStarted(WorkflowAgentStarted),
1298    WorkflowAgentCompleted(WorkflowAgentCompleted),
1299    WorkflowAgentFailed(WorkflowAgentFailed),
1300    WorkflowOutputRecorded(WorkflowOutputRecorded),
1301    WorkflowCheckpointRecorded(WorkflowCheckpointRecorded),
1302    WorkflowRunPaused(WorkflowRunPaused),
1303    WorkflowRunResumed(WorkflowRunResumed),
1304    WorkflowRunStopped(WorkflowRunStopped),
1305    WorkflowRunCompleted(WorkflowRunCompleted),
1306    WorkflowRunFailed(WorkflowRunFailed),
1307    MediaArtifactCreated(MediaArtifactCreated),
1308    MediaArtifactUpdated(MediaArtifactUpdated),
1309    MediaArtifactDeleted(MediaArtifactDeleted),
1310    MediaPreviewReady(MediaPreviewReady),
1311    ContextArtifactCreated(ContextArtifactCreated),
1312    ContextArtifactAppended(ContextArtifactAppended),
1313    ContextArtifactCapped(ContextArtifactCapped),
1314    ContextArtifactDeleted(ContextArtifactDeleted),
1315    ContextArtifactRetentionExpired(ContextArtifactRetentionExpired),
1316    DiscoveryCatalogBuilt(DiscoveryCatalogBuilt),
1317    DiscoveryItemUpdated(DiscoveryItemUpdated),
1318    DiscoveryAuthRequired(DiscoveryAuthRequired),
1319    DiscoveryItemRead(DiscoveryItemRead),
1320    DiscoveryItemPromoted(DiscoveryItemPromoted),
1321    DiscoveryPromotionReused(DiscoveryPromotionReused),
1322    DiscoveryWarmCacheHit(DiscoveryWarmCacheHit),
1323    DiscoveryPromotionExpired(DiscoveryPromotionExpired),
1324    RetrievalRoutePlanned(RetrievalRoutePlanned),
1325    RetrievalRouteAccepted(RetrievalRouteAccepted),
1326    RetrievalRouteIgnored(RetrievalRouteIgnored),
1327    RetrievalRouteFailed(RetrievalRouteFailed),
1328    RetrievalResultUsed(RetrievalResultUsed),
1329    RetrievalDiscoveryItemPromoted(RetrievalDiscoveryItemPromoted),
1330    RetrievalPromotionSkipped(RetrievalPromotionSkipped),
1331    MemorySaved(MemorySaved),
1332    MemoryUpdated(MemoryUpdated),
1333    MemoryDeleted(MemoryDeleted),
1334    MemoryQueried(MemoryQueried),
1335    MemoryRecallReady(MemoryRecallReady),
1336    MemoryReembedQueued(MemoryReembedQueued),
1337    MemoryProviderChanged(MemoryProviderChanged),
1338    MemoryObservationRecorded(MemoryObservationRecorded),
1339    KnowledgeSaved(KnowledgeSaved),
1340    KnowledgeUpdated(KnowledgeUpdated),
1341    KnowledgeArchived(KnowledgeArchived),
1342    KnowledgeLinked(KnowledgeLinked),
1343    RemoteServerStarted(RemoteServerStarted),
1344    RemoteServerStopped(RemoteServerStopped),
1345    RemoteAuthFailed(RemoteAuthFailed),
1346    RemoteClientConnected(RemoteClientConnected),
1347    RemoteClientDisconnected(RemoteClientDisconnected),
1348    ThreadGoalUpdated(ThreadGoalUpdated),
1349    ThreadGoalCleared(ThreadGoalCleared),
1350    RoadmapChanged(RoadmapChanged),
1351    AutomationCreated(AutomationCreated),
1352    AutomationUpdated(AutomationUpdated),
1353    AutomationDeleted(AutomationDeleted),
1354    AutomationDue(AutomationDue),
1355    AutomationLeased(AutomationLeased),
1356    AutomationQueued(AutomationQueued),
1357    AutomationStarted(AutomationStarted),
1358    AutomationCompleted(AutomationCompleted),
1359    AutomationFailed(AutomationFailed),
1360    AutomationSkipped(AutomationSkipped),
1361    AutomationLeaseExpired(AutomationLeaseExpired),
1362    SkillsCatalogLoaded(SkillsCatalogLoaded),
1363    SkillConfigApplied(SkillConfigApplied),
1364    SkillActivationResolved(SkillActivationResolved),
1365    SkillIndexRendered(SkillIndexRendered),
1366    SkillInvoked(SkillInvoked),
1367    SkillAutoActivated(SkillAutoActivated),
1368    SkillSkipped(SkillSkipped),
1369    TaskStarted(TaskStarted),
1370    TaskOutput(TaskOutput),
1371    TaskCompleted(TaskCompleted),
1372    TaskFailed(TaskFailed),
1373    TaskCancelled(TaskCancelled),
1374    ProcessStarted(ProcessStarted),
1375    ProcessOutput(ProcessOutput),
1376    ProcessExited(ProcessExited),
1377    ProcessStopping(ProcessStopping),
1378    ProcessStopped(ProcessStopped),
1379    ProcessFailed(ProcessFailed),
1380    FileChangePreviewReady(FileChangePreviewReady),
1381    FileChanged(FileChanged),
1382    TranscriptItemAppended(TranscriptItemAppended),
1383    TurnCompleted(TurnCompleted),
1384    TurnFailed(TurnFailed),
1385    TurnPartialResult(TurnPartialResult),
1386    TurnDeadlineExceeded(TurnDeadlineExceeded),
1387    TurnInterrupted(TurnInterrupted),
1388    TurnSteered(TurnSteered),
1389    RunnerLifecycle(RunnerLifecycle),
1390    TeamStarted(TeamStarted),
1391    TeamMemberStarted(TeamMemberStarted),
1392    TeamMemberStatusChanged(TeamMemberStatusChanged),
1393    TeamMemberMessageDelta(TeamMemberMessageDelta),
1394    TeamMemberCompleted(TeamMemberCompleted),
1395    TeamDisplayModeChanged(TeamDisplayModeChanged),
1396    TeamTaskChanged(TeamTaskChanged),
1397    TeamCleanupCompleted(TeamCleanupCompleted),
1398}
1399
1400impl RoderEvent {
1401    pub fn kind(&self) -> &'static str {
1402        match self {
1403            RoderEvent::RuntimeStarted(_) => "runtime.started",
1404            RoderEvent::ExtensionRegistered(_) => "extension.registered",
1405            RoderEvent::ExtensionEventEmitted(_) => "extension.event",
1406            RoderEvent::EventSinkFailed(_) => "extension.event_sink_failed",
1407            RoderEvent::ThreadCreated(_) => "thread.created",
1408            RoderEvent::ThreadLoaded(_) => "thread.loaded",
1409            RoderEvent::ThreadForkRequested(_) => "thread.fork_requested",
1410            RoderEvent::ThreadForked(_) => "thread.forked",
1411            RoderEvent::ThreadForkFailed(_) => "thread.fork_failed",
1412            RoderEvent::ThreadForkRemoved(_) => "thread.fork_removed",
1413            RoderEvent::TurnStarted(_) => "turn.started",
1414            RoderEvent::ContextAssemblyStarted(_) => "context.assembly_started",
1415            RoderEvent::ContextBlockAdded(_) => "context.block_added",
1416            RoderEvent::ContextAssemblyCompleted(_) => "context.assembly_completed",
1417            RoderEvent::ContextEntrypointCandidatesInjected(_) => {
1418                "context.entrypoint_candidates_injected"
1419            }
1420            RoderEvent::ContextCompactionStarted(_) => "context.compaction_started",
1421            RoderEvent::ContextCompactionRecorded(_) => "context.compaction_recorded",
1422            RoderEvent::InferenceRoutingDecision(_) => "inference.routing_decision",
1423            RoderEvent::InferenceStarted(_) => "inference.started",
1424            RoderEvent::InferenceEventReceived(_) => "inference.event_received",
1425            RoderEvent::ToolCallRequested(_) => "tool.call_requested",
1426            RoderEvent::ToolCallValidationRecorded(_) => "tool.call_validation",
1427            RoderEvent::ReliabilityFailureRecorded(_) => "reliability.failure",
1428            RoderEvent::ReliabilityRetryRecorded(_) => "reliability.retry",
1429            RoderEvent::ReliabilityLimitRecorded(_) => "reliability.limit",
1430            RoderEvent::ReliabilityMetricRecorded(_) => "reliability.metric",
1431            RoderEvent::CodeIndexingStarted(_) => "code_index.started",
1432            RoderEvent::CodeIndexChunked(_) => "code_index.chunked",
1433            RoderEvent::CodeIndexEmbedded(_) => "code_index.embedded",
1434            RoderEvent::CodeIndexReady(_) => "code_index.ready",
1435            RoderEvent::CodeIndexStale(_) => "code_index.stale",
1436            RoderEvent::CodeIndexFailed(_) => "code_index.failed",
1437            RoderEvent::CodeIndexProofFilteredResultDropped(_) => {
1438                "code_index.proof_filtered_result_dropped"
1439            }
1440            RoderEvent::ApprovalRequested(_) => "approval.requested",
1441            RoderEvent::ApprovalResolved(_) => "approval.resolved",
1442            RoderEvent::ExternalToolCallRequested(_) => "external_tool.requested",
1443            RoderEvent::ExternalToolCallResolved(_) => "external_tool.resolved",
1444            RoderEvent::UserInputRequested(_) => "user_input.requested",
1445            RoderEvent::UserInputResolved(_) => "user_input.resolved",
1446            RoderEvent::TaskLedgerUpdated(_) => "task_ledger.updated",
1447            RoderEvent::VerificationRequired(_) => "verification.required",
1448            RoderEvent::VerificationCompleted(_) => "verification.completed",
1449            RoderEvent::VerificationSkipped(_) => "verification.skipped",
1450            RoderEvent::PolicyDecisionRecorded(_) => "policy.decision",
1451            RoderEvent::PolicyBypassActive(_) => "policy.bypass_active",
1452            RoderEvent::PolicyModeChanged(_) => "policy.mode_changed",
1453            RoderEvent::PolicyExitPlanRequested(_) => "policy.exit_plan_requested",
1454            RoderEvent::PolicyExitPlanResolved(_) => "policy.exit_plan_resolved",
1455            RoderEvent::ToolCallStarted(_) => "tool.call_started",
1456            RoderEvent::ToolCallCompleted(_) => "tool.call_completed",
1457            RoderEvent::ToolOutputTruncated(_) => "tool.output_truncated",
1458            RoderEvent::SubagentStarted(_) => "subagent.started",
1459            RoderEvent::SubagentMessage(_) => "subagent.message",
1460            RoderEvent::SubagentToolCall(_) => "subagent.tool_call",
1461            RoderEvent::SubagentCompleted(_) => "subagent.completed",
1462            RoderEvent::SubagentFailed(_) => "subagent.failed",
1463            RoderEvent::SubagentTraceCreated(_) => "turn/subagentTraceCreated",
1464            RoderEvent::SubagentTraceDelta(_) => "turn/subagentTraceDelta",
1465            RoderEvent::SubagentTraceStatusChanged(_) => "turn/subagentTraceStatusChanged",
1466            RoderEvent::SubagentTraceCompleted(_) => "turn/subagentTraceCompleted",
1467            RoderEvent::SubagentTraceFailed(_) => "turn/subagentTraceFailed",
1468            RoderEvent::PlanReviewCreated(_) => "plan/reviewCreated",
1469            RoderEvent::PlanReviewStatusChanged(_) => "plan/reviewStatusChanged",
1470            RoderEvent::PlanReviewCommentAdded(_) => "plan/reviewCommentAdded",
1471            RoderEvent::PlanReviewRewritten(_) => "plan/reviewRewritten",
1472            RoderEvent::PlanReviewApproved(_) => "plan/reviewApproved",
1473            RoderEvent::PlanReviewRejected(_) => "plan/reviewRejected",
1474            RoderEvent::HunkRecorded(_) => "hunk/recorded",
1475            RoderEvent::WorkspaceChangeObserved(_) => "workspace/changeObserved",
1476            RoderEvent::HunkRollbackRequested(_) => "hunk/rollbackRequested",
1477            RoderEvent::HunkRollbackCompleted(_) => "hunk/rollbackCompleted",
1478            RoderEvent::WorkflowImportsDetected(_) => "workflow/importsDetected",
1479            RoderEvent::WorkflowImportPreviewed(_) => "workflow/importPreviewed",
1480            RoderEvent::WorkflowImportEnabled(_) => "workflow/importEnabled",
1481            RoderEvent::WorkflowImportDisabled(_) => "workflow/importDisabled",
1482            RoderEvent::WorkflowImportStale(_) => "workflow/importStale",
1483            RoderEvent::WorkflowImportFailed(_) => "workflow/importFailed",
1484            RoderEvent::WorkflowRunDrafted(_) => "workflows/drafted",
1485            RoderEvent::WorkflowApprovalRequested(_) => "workflows/approvalRequested",
1486            RoderEvent::WorkflowRunApproved(_) => "workflows/approved",
1487            RoderEvent::WorkflowRunDenied(_) => "workflows/denied",
1488            RoderEvent::WorkflowRunQueued(_) => "workflows/queued",
1489            RoderEvent::WorkflowRunStarted(_) => "workflows/started",
1490            RoderEvent::WorkflowPhaseStarted(_) => "workflows/phaseStarted",
1491            RoderEvent::WorkflowPhaseCompleted(_) => "workflows/phaseCompleted",
1492            RoderEvent::WorkflowAgentQueued(_) => "workflows/agentQueued",
1493            RoderEvent::WorkflowAgentStarted(_) => "workflows/agentStarted",
1494            RoderEvent::WorkflowAgentCompleted(_) => "workflows/agentCompleted",
1495            RoderEvent::WorkflowAgentFailed(_) => "workflows/agentFailed",
1496            RoderEvent::WorkflowOutputRecorded(_) => "workflows/outputRecorded",
1497            RoderEvent::WorkflowCheckpointRecorded(_) => "workflows/checkpointRecorded",
1498            RoderEvent::WorkflowRunPaused(_) => "workflows/paused",
1499            RoderEvent::WorkflowRunResumed(_) => "workflows/resumed",
1500            RoderEvent::WorkflowRunStopped(_) => "workflows/stopped",
1501            RoderEvent::WorkflowRunCompleted(_) => "workflows/completed",
1502            RoderEvent::WorkflowRunFailed(_) => "workflows/failed",
1503            RoderEvent::MediaArtifactCreated(_) => "media/artifactCreated",
1504            RoderEvent::MediaArtifactUpdated(_) => "media/artifactUpdated",
1505            RoderEvent::MediaArtifactDeleted(_) => "media/artifactDeleted",
1506            RoderEvent::MediaPreviewReady(_) => "media/previewReady",
1507            RoderEvent::ContextArtifactCreated(_) => "artifact/created",
1508            RoderEvent::ContextArtifactAppended(_) => "artifact/appended",
1509            RoderEvent::ContextArtifactCapped(_) => "artifact/capped",
1510            RoderEvent::ContextArtifactDeleted(_) => "artifact/deleted",
1511            RoderEvent::ContextArtifactRetentionExpired(_) => "artifact/retentionExpired",
1512            RoderEvent::DiscoveryCatalogBuilt(_) => "discovery/catalogBuilt",
1513            RoderEvent::DiscoveryItemUpdated(_) => "discovery/itemUpdated",
1514            RoderEvent::DiscoveryAuthRequired(_) => "discovery/authRequired",
1515            RoderEvent::DiscoveryItemRead(_) => "discovery/itemRead",
1516            RoderEvent::DiscoveryItemPromoted(_) => "discovery/itemPromoted",
1517            RoderEvent::DiscoveryPromotionReused(_) => "discovery/promotionReused",
1518            RoderEvent::DiscoveryWarmCacheHit(_) => "discovery/warmCacheHit",
1519            RoderEvent::DiscoveryPromotionExpired(_) => "discovery/promotionExpired",
1520            RoderEvent::RetrievalRoutePlanned(_) => "retrieval/routePlanned",
1521            RoderEvent::RetrievalRouteAccepted(_) => "retrieval/routeAccepted",
1522            RoderEvent::RetrievalRouteIgnored(_) => "retrieval/routeIgnored",
1523            RoderEvent::RetrievalRouteFailed(_) => "retrieval/routeFailed",
1524            RoderEvent::RetrievalResultUsed(_) => "retrieval/resultUsed",
1525            RoderEvent::RetrievalDiscoveryItemPromoted(_) => "retrieval/discoveryItemPromoted",
1526            RoderEvent::RetrievalPromotionSkipped(_) => "retrieval/promotionSkipped",
1527            RoderEvent::MemorySaved(_) => "memory/saved",
1528            RoderEvent::MemoryUpdated(_) => "memory/updated",
1529            RoderEvent::MemoryDeleted(_) => "memory/deleted",
1530            RoderEvent::MemoryQueried(_) => "memory/queried",
1531            RoderEvent::MemoryRecallReady(_) => "memory/recallReady",
1532            RoderEvent::MemoryReembedQueued(_) => "memory/reembedQueued",
1533            RoderEvent::MemoryProviderChanged(_) => "memory/providerChanged",
1534            RoderEvent::MemoryObservationRecorded(_) => "memory/observationRecorded",
1535            RoderEvent::KnowledgeSaved(_) => "knowledge/saved",
1536            RoderEvent::KnowledgeUpdated(_) => "knowledge/updated",
1537            RoderEvent::KnowledgeArchived(_) => "knowledge/archived",
1538            RoderEvent::KnowledgeLinked(_) => "knowledge/linked",
1539            RoderEvent::RemoteServerStarted(_) => "remote/serverStarted",
1540            RoderEvent::RemoteServerStopped(_) => "remote/serverStopped",
1541            RoderEvent::RemoteAuthFailed(_) => "remote/authFailed",
1542            RoderEvent::RemoteClientConnected(_) => "remote/clientConnected",
1543            RoderEvent::RemoteClientDisconnected(_) => "remote/clientDisconnected",
1544            RoderEvent::ThreadGoalUpdated(_) => "thread/goal/updated",
1545            RoderEvent::ThreadGoalCleared(_) => "thread/goal/cleared",
1546            RoderEvent::RoadmapChanged(_) => "roadmap.changed",
1547            RoderEvent::AutomationCreated(_) => "automations/created",
1548            RoderEvent::AutomationUpdated(_) => "automations/updated",
1549            RoderEvent::AutomationDeleted(_) => "automations/deleted",
1550            RoderEvent::AutomationDue(_) => "automations/due",
1551            RoderEvent::AutomationLeased(_) => "automations/leased",
1552            RoderEvent::AutomationQueued(_) => "automations/queued",
1553            RoderEvent::AutomationStarted(_) => "automations/started",
1554            RoderEvent::AutomationCompleted(_) => "automations/completed",
1555            RoderEvent::AutomationFailed(_) => "automations/failed",
1556            RoderEvent::AutomationSkipped(_) => "automations/skipped",
1557            RoderEvent::AutomationLeaseExpired(_) => "automations/leaseExpired",
1558            RoderEvent::SkillsCatalogLoaded(_) => "skills/catalogLoaded",
1559            RoderEvent::SkillConfigApplied(_) => "skills/configApplied",
1560            RoderEvent::SkillActivationResolved(_) => "skills/activationResolved",
1561            RoderEvent::SkillIndexRendered(_) => "skills/indexRendered",
1562            RoderEvent::SkillInvoked(_) => "skills/invoked",
1563            RoderEvent::SkillAutoActivated(_) => "skills/autoActivated",
1564            RoderEvent::SkillSkipped(_) => "skills/skipped",
1565            RoderEvent::TaskStarted(_) => "task.started",
1566            RoderEvent::TaskOutput(_) => "task.output",
1567            RoderEvent::TaskCompleted(_) => "task.completed",
1568            RoderEvent::TaskFailed(_) => "task.failed",
1569            RoderEvent::TaskCancelled(_) => "task.cancelled",
1570            RoderEvent::ProcessStarted(_) => "process.started",
1571            RoderEvent::ProcessOutput(_) => "process.output",
1572            RoderEvent::ProcessExited(_) => "process.exited",
1573            RoderEvent::ProcessStopping(_) => "process.stopping",
1574            RoderEvent::ProcessStopped(_) => "process.stopped",
1575            RoderEvent::ProcessFailed(_) => "process.failed",
1576            RoderEvent::FileChangePreviewReady(_) => "file.change_preview_ready",
1577            RoderEvent::FileChanged(_) => "file.changed",
1578            RoderEvent::TranscriptItemAppended(_) => "turn.transcript_item_appended",
1579            RoderEvent::TurnCompleted(_) => "turn.completed",
1580            RoderEvent::TurnFailed(_) => "turn.failed",
1581            RoderEvent::TurnPartialResult(_) => "turn.partial_result",
1582            RoderEvent::TurnDeadlineExceeded(_) => "turn.deadline_exceeded",
1583            RoderEvent::TurnInterrupted(_) => "turn.interrupted",
1584            RoderEvent::TurnSteered(_) => "turn.steered",
1585            RoderEvent::RunnerLifecycle(_) => "runner.lifecycle",
1586            RoderEvent::TeamStarted(_) => "team.started",
1587            RoderEvent::TeamMemberStarted(_) => "team.member_started",
1588            RoderEvent::TeamMemberStatusChanged(_) => "team.member_status_changed",
1589            RoderEvent::TeamMemberMessageDelta(_) => "team.member_message_delta",
1590            RoderEvent::TeamMemberCompleted(_) => "team.member_completed",
1591            RoderEvent::TeamDisplayModeChanged(_) => "team.display_mode_changed",
1592            RoderEvent::TeamTaskChanged(_) => "team.task_changed",
1593            RoderEvent::TeamCleanupCompleted(_) => "team.cleanup_completed",
1594        }
1595    }
1596
1597    pub fn source(&self) -> EventSource {
1598        match self {
1599            RoderEvent::InferenceEventReceived(_) | RoderEvent::InferenceStarted(_) => {
1600                EventSource::Provider
1601            }
1602            RoderEvent::InferenceRoutingDecision(_) => EventSource::Core,
1603            RoderEvent::ReliabilityRetryRecorded(_) => EventSource::Provider,
1604            RoderEvent::ReliabilityFailureRecorded(_)
1605            | RoderEvent::ReliabilityLimitRecorded(_)
1606            | RoderEvent::ReliabilityMetricRecorded(_) => EventSource::Core,
1607            RoderEvent::ToolCallRequested(_)
1608            | RoderEvent::ToolCallValidationRecorded(_)
1609            | RoderEvent::ToolCallStarted(_)
1610            | RoderEvent::ToolCallCompleted(_) => EventSource::Tool,
1611            RoderEvent::SubagentStarted(_)
1612            | RoderEvent::SubagentMessage(_)
1613            | RoderEvent::SubagentToolCall(_)
1614            | RoderEvent::SubagentCompleted(_)
1615            | RoderEvent::SubagentFailed(_)
1616            | RoderEvent::SubagentTraceCreated(_)
1617            | RoderEvent::SubagentTraceDelta(_)
1618            | RoderEvent::SubagentTraceStatusChanged(_)
1619            | RoderEvent::SubagentTraceCompleted(_)
1620            | RoderEvent::SubagentTraceFailed(_)
1621            | RoderEvent::PlanReviewCreated(_)
1622            | RoderEvent::PlanReviewStatusChanged(_)
1623            | RoderEvent::PlanReviewCommentAdded(_)
1624            | RoderEvent::PlanReviewRewritten(_)
1625            | RoderEvent::PlanReviewApproved(_)
1626            | RoderEvent::PlanReviewRejected(_)
1627            | RoderEvent::HunkRecorded(_)
1628            | RoderEvent::WorkspaceChangeObserved(_)
1629            | RoderEvent::HunkRollbackRequested(_)
1630            | RoderEvent::HunkRollbackCompleted(_)
1631            | RoderEvent::WorkflowImportsDetected(_)
1632            | RoderEvent::WorkflowImportPreviewed(_)
1633            | RoderEvent::WorkflowImportEnabled(_)
1634            | RoderEvent::WorkflowImportDisabled(_)
1635            | RoderEvent::WorkflowImportStale(_)
1636            | RoderEvent::WorkflowImportFailed(_)
1637            | RoderEvent::MediaArtifactCreated(_)
1638            | RoderEvent::MediaArtifactUpdated(_)
1639            | RoderEvent::MediaArtifactDeleted(_)
1640            | RoderEvent::MediaPreviewReady(_)
1641            | RoderEvent::ContextArtifactCreated(_)
1642            | RoderEvent::ContextArtifactAppended(_)
1643            | RoderEvent::ContextArtifactCapped(_)
1644            | RoderEvent::ContextArtifactDeleted(_)
1645            | RoderEvent::ContextArtifactRetentionExpired(_)
1646            | RoderEvent::DiscoveryCatalogBuilt(_)
1647            | RoderEvent::DiscoveryItemUpdated(_)
1648            | RoderEvent::DiscoveryAuthRequired(_)
1649            | RoderEvent::DiscoveryItemRead(_)
1650            | RoderEvent::DiscoveryItemPromoted(_)
1651            | RoderEvent::DiscoveryPromotionReused(_)
1652            | RoderEvent::DiscoveryWarmCacheHit(_)
1653            | RoderEvent::DiscoveryPromotionExpired(_)
1654            | RoderEvent::RetrievalRoutePlanned(_)
1655            | RoderEvent::RetrievalRouteAccepted(_)
1656            | RoderEvent::RetrievalRouteIgnored(_)
1657            | RoderEvent::RetrievalRouteFailed(_)
1658            | RoderEvent::RetrievalResultUsed(_)
1659            | RoderEvent::RetrievalDiscoveryItemPromoted(_)
1660            | RoderEvent::RetrievalPromotionSkipped(_)
1661            | RoderEvent::MemorySaved(_)
1662            | RoderEvent::MemoryUpdated(_)
1663            | RoderEvent::MemoryDeleted(_)
1664            | RoderEvent::MemoryQueried(_)
1665            | RoderEvent::MemoryRecallReady(_)
1666            | RoderEvent::MemoryReembedQueued(_)
1667            | RoderEvent::MemoryProviderChanged(_)
1668            | RoderEvent::MemoryObservationRecorded(_)
1669            | RoderEvent::TaskStarted(_)
1670            | RoderEvent::TaskOutput(_)
1671            | RoderEvent::TaskCompleted(_)
1672            | RoderEvent::TaskFailed(_)
1673            | RoderEvent::TaskCancelled(_)
1674            | RoderEvent::ProcessStarted(_)
1675            | RoderEvent::ProcessOutput(_)
1676            | RoderEvent::ProcessExited(_)
1677            | RoderEvent::ProcessStopping(_)
1678            | RoderEvent::ProcessStopped(_)
1679            | RoderEvent::ProcessFailed(_) => EventSource::Extension,
1680            RoderEvent::RemoteServerStarted(_)
1681            | RoderEvent::RemoteServerStopped(_)
1682            | RoderEvent::RemoteAuthFailed(_)
1683            | RoderEvent::RemoteClientConnected(_)
1684            | RoderEvent::RemoteClientDisconnected(_) => EventSource::AppServer,
1685            RoderEvent::RoadmapChanged(_)
1686            | RoderEvent::ThreadGoalUpdated(_)
1687            | RoderEvent::ThreadGoalCleared(_)
1688            | RoderEvent::AutomationCreated(_)
1689            | RoderEvent::AutomationUpdated(_)
1690            | RoderEvent::AutomationDeleted(_)
1691            | RoderEvent::AutomationDue(_)
1692            | RoderEvent::AutomationLeased(_)
1693            | RoderEvent::AutomationQueued(_)
1694            | RoderEvent::AutomationStarted(_)
1695            | RoderEvent::AutomationCompleted(_)
1696            | RoderEvent::AutomationFailed(_)
1697            | RoderEvent::AutomationSkipped(_)
1698            | RoderEvent::AutomationLeaseExpired(_)
1699            | RoderEvent::SkillsCatalogLoaded(_)
1700            | RoderEvent::SkillConfigApplied(_)
1701            | RoderEvent::SkillActivationResolved(_)
1702            | RoderEvent::SkillIndexRendered(_)
1703            | RoderEvent::SkillInvoked(_)
1704            | RoderEvent::SkillAutoActivated(_)
1705            | RoderEvent::SkillSkipped(_) => EventSource::Core,
1706            RoderEvent::FileChangePreviewReady(_) => EventSource::Tool,
1707            RoderEvent::UserInputRequested(_)
1708            | RoderEvent::UserInputResolved(_)
1709            | RoderEvent::TaskLedgerUpdated(_)
1710            | RoderEvent::TurnPartialResult(_)
1711            | RoderEvent::TurnDeadlineExceeded(_)
1712            | RoderEvent::VerificationRequired(_)
1713            | RoderEvent::VerificationCompleted(_)
1714            | RoderEvent::VerificationSkipped(_) => EventSource::Core,
1715            RoderEvent::ExtensionRegistered(_) => EventSource::Extension,
1716            RoderEvent::RunnerLifecycle(_) => EventSource::Extension,
1717            RoderEvent::TeamStarted(_)
1718            | RoderEvent::TeamMemberStarted(_)
1719            | RoderEvent::TeamMemberStatusChanged(_)
1720            | RoderEvent::TeamMemberMessageDelta(_)
1721            | RoderEvent::TeamMemberCompleted(_)
1722            | RoderEvent::TeamDisplayModeChanged(_)
1723            | RoderEvent::TeamTaskChanged(_)
1724            | RoderEvent::TeamCleanupCompleted(_) => EventSource::Core,
1725            _ => EventSource::Core,
1726        }
1727    }
1728
1729    pub fn thread_id(&self) -> Option<&ThreadId> {
1730        match self {
1731            RoderEvent::ExtensionEventEmitted(_) | RoderEvent::EventSinkFailed(_) => None,
1732            RoderEvent::ThreadCreated(e) => Some(&e.thread_id),
1733            RoderEvent::ThreadLoaded(e) => Some(&e.thread_id),
1734            RoderEvent::ThreadForkRequested(e) => Some(&e.parent_thread_id),
1735            RoderEvent::ThreadForked(e) => Some(&e.child_thread_id),
1736            RoderEvent::ThreadForkFailed(e) => Some(&e.parent_thread_id),
1737            RoderEvent::ThreadForkRemoved(e) => Some(&e.thread_id),
1738            RoderEvent::TurnStarted(e) => Some(&e.thread_id),
1739            RoderEvent::ContextAssemblyStarted(e) => Some(&e.thread_id),
1740            RoderEvent::ContextBlockAdded(e) => Some(&e.thread_id),
1741            RoderEvent::ContextAssemblyCompleted(e) => Some(&e.thread_id),
1742            RoderEvent::ContextEntrypointCandidatesInjected(e) => Some(&e.thread_id),
1743            RoderEvent::ContextCompactionStarted(e) => Some(&e.thread_id),
1744            RoderEvent::ContextCompactionRecorded(e) => Some(&e.thread_id),
1745            RoderEvent::InferenceRoutingDecision(e) => Some(&e.thread_id),
1746            RoderEvent::InferenceStarted(e) => Some(&e.thread_id),
1747            RoderEvent::InferenceEventReceived(e) => Some(&e.thread_id),
1748            RoderEvent::ToolCallRequested(e) => Some(&e.thread_id),
1749            RoderEvent::ToolCallValidationRecorded(e) => Some(&e.thread_id),
1750            RoderEvent::ReliabilityFailureRecorded(e) => Some(&e.context.thread_id),
1751            RoderEvent::ReliabilityRetryRecorded(e) => Some(&e.context.thread_id),
1752            RoderEvent::ReliabilityLimitRecorded(e) => Some(&e.context.thread_id),
1753            RoderEvent::ReliabilityMetricRecorded(e) => Some(&e.context.thread_id),
1754            RoderEvent::CodeIndexingStarted(e) => e.context.thread_id.as_ref(),
1755            RoderEvent::CodeIndexChunked(e) => e.context.thread_id.as_ref(),
1756            RoderEvent::CodeIndexEmbedded(e) => e.context.thread_id.as_ref(),
1757            RoderEvent::CodeIndexReady(_) => None,
1758            RoderEvent::CodeIndexStale(e) => e.context.thread_id.as_ref(),
1759            RoderEvent::CodeIndexFailed(e) => e.context.thread_id.as_ref(),
1760            RoderEvent::CodeIndexProofFilteredResultDropped(e) => e.context.thread_id.as_ref(),
1761            RoderEvent::ApprovalRequested(e) => Some(&e.thread_id),
1762            RoderEvent::ApprovalResolved(e) => Some(&e.thread_id),
1763            RoderEvent::ExternalToolCallRequested(e) => Some(&e.thread_id),
1764            RoderEvent::ExternalToolCallResolved(e) => Some(&e.thread_id),
1765            RoderEvent::UserInputRequested(e) => Some(&e.thread_id),
1766            RoderEvent::UserInputResolved(e) => Some(&e.thread_id),
1767            RoderEvent::TaskLedgerUpdated(e) => Some(&e.thread_id),
1768            RoderEvent::VerificationRequired(e) => Some(&e.thread_id),
1769            RoderEvent::VerificationCompleted(e) => Some(&e.thread_id),
1770            RoderEvent::VerificationSkipped(e) => Some(&e.thread_id),
1771            RoderEvent::PolicyDecisionRecorded(e) => Some(&e.thread_id),
1772            RoderEvent::PolicyBypassActive(e) => Some(&e.thread_id),
1773            RoderEvent::PolicyModeChanged(e) => Some(&e.thread_id),
1774            RoderEvent::PolicyExitPlanRequested(e) => Some(&e.thread_id),
1775            RoderEvent::PolicyExitPlanResolved(e) => Some(&e.thread_id),
1776            RoderEvent::ToolCallStarted(e) => Some(&e.thread_id),
1777            RoderEvent::ToolCallCompleted(e) => Some(&e.thread_id),
1778            RoderEvent::ToolOutputTruncated(e) => Some(&e.thread_id),
1779            RoderEvent::SubagentStarted(e) => Some(&e.thread_id),
1780            RoderEvent::SubagentMessage(e) => Some(&e.thread_id),
1781            RoderEvent::SubagentToolCall(e) => Some(&e.thread_id),
1782            RoderEvent::SubagentCompleted(e) => Some(&e.thread_id),
1783            RoderEvent::SubagentFailed(e) => Some(&e.thread_id),
1784            RoderEvent::SubagentTraceCreated(e) => Some(&e.summary.parent.thread_id),
1785            RoderEvent::SubagentTraceDelta(e) => Some(&e.delta.parent.thread_id),
1786            RoderEvent::SubagentTraceStatusChanged(e) => Some(&e.parent.thread_id),
1787            RoderEvent::SubagentTraceCompleted(e) => Some(&e.summary.parent.thread_id),
1788            RoderEvent::SubagentTraceFailed(e) => Some(&e.summary.parent.thread_id),
1789            RoderEvent::PlanReviewCreated(e) => Some(&e.review.thread_id),
1790            RoderEvent::PlanReviewStatusChanged(e) => Some(&e.thread_id),
1791            RoderEvent::PlanReviewCommentAdded(e) => Some(&e.thread_id),
1792            RoderEvent::PlanReviewRewritten(e) => Some(&e.thread_id),
1793            RoderEvent::PlanReviewApproved(e) => Some(&e.thread_id),
1794            RoderEvent::PlanReviewRejected(e) => Some(&e.thread_id),
1795            RoderEvent::HunkRecorded(e) => Some(&e.hunk.thread_id),
1796            RoderEvent::WorkspaceChangeObserved(e) => Some(&e.change.thread_id),
1797            RoderEvent::HunkRollbackRequested(e) => Some(&e.thread_id),
1798            RoderEvent::HunkRollbackCompleted(e) => Some(&e.thread_id),
1799            RoderEvent::MediaArtifactCreated(e) => Some(&e.thread_id),
1800            RoderEvent::MediaArtifactUpdated(e) => Some(&e.thread_id),
1801            RoderEvent::MediaPreviewReady(e) => Some(&e.thread_id),
1802            RoderEvent::ContextArtifactCreated(e) => Some(&e.thread_id),
1803            RoderEvent::ContextArtifactAppended(e) => Some(&e.thread_id),
1804            RoderEvent::ContextArtifactCapped(e) => Some(&e.thread_id),
1805            RoderEvent::ContextArtifactDeleted(e) => Some(&e.thread_id),
1806            RoderEvent::ContextArtifactRetentionExpired(e) => Some(&e.thread_id),
1807            RoderEvent::DiscoveryItemRead(e) => Some(&e.thread_id),
1808            RoderEvent::DiscoveryItemPromoted(e) => Some(&e.record.thread_id),
1809            RoderEvent::DiscoveryPromotionReused(e) => Some(&e.record.thread_id),
1810            RoderEvent::DiscoveryWarmCacheHit(e) => Some(&e.record.thread_id),
1811            RoderEvent::DiscoveryPromotionExpired(e) => Some(&e.record.thread_id),
1812            RoderEvent::RetrievalRoutePlanned(e) => Some(&e.plan.thread_id),
1813            RoderEvent::RetrievalRouteAccepted(e) => Some(&e.thread_id),
1814            RoderEvent::RetrievalRouteIgnored(e) => Some(&e.thread_id),
1815            RoderEvent::RetrievalRouteFailed(e) => Some(&e.thread_id),
1816            RoderEvent::RetrievalResultUsed(e) => Some(&e.thread_id),
1817            RoderEvent::RetrievalDiscoveryItemPromoted(e) => Some(&e.thread_id),
1818            RoderEvent::RetrievalPromotionSkipped(e) => Some(&e.thread_id),
1819            RoderEvent::MemoryRecallReady(e) => Some(&e.thread_id),
1820            RoderEvent::MemoryObservationRecorded(e) => Some(&e.thread_id),
1821            RoderEvent::TaskStarted(e) => e.thread_id.as_ref(),
1822            RoderEvent::TaskOutput(e) => e.thread_id.as_ref(),
1823            RoderEvent::TaskCompleted(e) => e.thread_id.as_ref(),
1824            RoderEvent::TaskFailed(e) => e.thread_id.as_ref(),
1825            RoderEvent::TaskCancelled(e) => e.thread_id.as_ref(),
1826            RoderEvent::FileChangePreviewReady(e) => Some(&e.thread_id),
1827            RoderEvent::FileChanged(e) => Some(&e.thread_id),
1828            RoderEvent::TranscriptItemAppended(e) => Some(&e.thread_id),
1829            RoderEvent::TurnCompleted(e) => Some(&e.thread_id),
1830            RoderEvent::TurnFailed(e) => Some(&e.thread_id),
1831            RoderEvent::TurnPartialResult(e) => Some(&e.thread_id),
1832            RoderEvent::TurnDeadlineExceeded(e) => Some(&e.thread_id),
1833            RoderEvent::TurnInterrupted(e) => Some(&e.thread_id),
1834            RoderEvent::TurnSteered(e) => Some(&e.thread_id),
1835            RoderEvent::ThreadGoalUpdated(e) => Some(&e.thread_id),
1836            RoderEvent::ThreadGoalCleared(e) => Some(&e.thread_id),
1837            RoderEvent::TeamStarted(e) => Some(&e.lead_thread_id),
1838            RoderEvent::TeamMemberStarted(e) => Some(&e.member_thread_id),
1839            RoderEvent::TeamMemberStatusChanged(e) => Some(&e.member_thread_id),
1840            RoderEvent::TeamMemberMessageDelta(e) => Some(&e.member_thread_id),
1841            RoderEvent::TeamMemberCompleted(e) => Some(&e.member_thread_id),
1842            RoderEvent::SkillActivationResolved(e) => Some(&e.thread_id),
1843            RoderEvent::SkillIndexRendered(e) => Some(&e.thread_id),
1844            RoderEvent::SkillInvoked(e) => Some(&e.thread_id),
1845            RoderEvent::SkillAutoActivated(e) => Some(&e.thread_id),
1846            RoderEvent::SkillSkipped(e) => Some(&e.thread_id),
1847            RoderEvent::RuntimeStarted(_)
1848            | RoderEvent::ExtensionRegistered(_)
1849            | RoderEvent::WorkflowImportsDetected(_)
1850            | RoderEvent::WorkflowImportPreviewed(_)
1851            | RoderEvent::WorkflowImportEnabled(_)
1852            | RoderEvent::WorkflowImportDisabled(_)
1853            | RoderEvent::WorkflowImportStale(_)
1854            | RoderEvent::WorkflowImportFailed(_)
1855            | RoderEvent::MediaArtifactDeleted(_)
1856            | RoderEvent::DiscoveryCatalogBuilt(_)
1857            | RoderEvent::DiscoveryItemUpdated(_)
1858            | RoderEvent::DiscoveryAuthRequired(_)
1859            | RoderEvent::MemorySaved(_)
1860            | RoderEvent::MemoryUpdated(_)
1861            | RoderEvent::MemoryDeleted(_)
1862            | RoderEvent::MemoryQueried(_)
1863            | RoderEvent::MemoryReembedQueued(_)
1864            | RoderEvent::MemoryProviderChanged(_)
1865            | RoderEvent::KnowledgeSaved(_)
1866            | RoderEvent::KnowledgeUpdated(_)
1867            | RoderEvent::KnowledgeArchived(_)
1868            | RoderEvent::KnowledgeLinked(_)
1869            | RoderEvent::RemoteServerStarted(_)
1870            | RoderEvent::RemoteServerStopped(_)
1871            | RoderEvent::RemoteAuthFailed(_)
1872            | RoderEvent::RemoteClientConnected(_)
1873            | RoderEvent::RemoteClientDisconnected(_)
1874            | RoderEvent::RoadmapChanged(_)
1875            | RoderEvent::AutomationCreated(_)
1876            | RoderEvent::AutomationUpdated(_)
1877            | RoderEvent::AutomationDeleted(_)
1878            | RoderEvent::AutomationDue(_)
1879            | RoderEvent::AutomationLeased(_)
1880            | RoderEvent::AutomationQueued(_)
1881            | RoderEvent::AutomationSkipped(_)
1882            | RoderEvent::AutomationLeaseExpired(_)
1883            | RoderEvent::SkillsCatalogLoaded(_)
1884            | RoderEvent::SkillConfigApplied(_)
1885            | RoderEvent::RunnerLifecycle(_)
1886            | RoderEvent::TeamDisplayModeChanged(_)
1887            | RoderEvent::TeamTaskChanged(_)
1888            | RoderEvent::TeamCleanupCompleted(_) => None,
1889            RoderEvent::ProcessStarted(e) => e.process.thread_id.as_ref(),
1890            RoderEvent::ProcessOutput(e) => e.thread_id.as_ref(),
1891            RoderEvent::ProcessExited(e) => e.process.thread_id.as_ref(),
1892            RoderEvent::ProcessStopping(_) => None,
1893            RoderEvent::ProcessStopped(e) => e.process.thread_id.as_ref(),
1894            RoderEvent::ProcessFailed(e) => e.process.thread_id.as_ref(),
1895            RoderEvent::AutomationStarted(e) => e.run.thread_id.as_ref(),
1896            RoderEvent::AutomationCompleted(e) => e.run.thread_id.as_ref(),
1897            RoderEvent::AutomationFailed(e) => e.run.thread_id.as_ref(),
1898            RoderEvent::WorkflowRunDrafted(e) => e.thread_id.as_ref(),
1899            RoderEvent::WorkflowApprovalRequested(e) => e.thread_id.as_ref(),
1900            RoderEvent::WorkflowRunApproved(e) => e.thread_id.as_ref(),
1901            RoderEvent::WorkflowRunDenied(e) => e.thread_id.as_ref(),
1902            RoderEvent::WorkflowRunQueued(e) => e.thread_id.as_ref(),
1903            RoderEvent::WorkflowRunStarted(e) => e.thread_id.as_ref(),
1904            RoderEvent::WorkflowPhaseStarted(e) => e.thread_id.as_ref(),
1905            RoderEvent::WorkflowPhaseCompleted(e) => e.thread_id.as_ref(),
1906            RoderEvent::WorkflowAgentQueued(e) => e.thread_id.as_ref(),
1907            RoderEvent::WorkflowAgentStarted(e) => e.thread_id.as_ref(),
1908            RoderEvent::WorkflowAgentCompleted(e) => e.thread_id.as_ref(),
1909            RoderEvent::WorkflowAgentFailed(e) => e.thread_id.as_ref(),
1910            RoderEvent::WorkflowOutputRecorded(e) => e.thread_id.as_ref(),
1911            RoderEvent::WorkflowCheckpointRecorded(e) => e.thread_id.as_ref(),
1912            RoderEvent::WorkflowRunPaused(e) => e.thread_id.as_ref(),
1913            RoderEvent::WorkflowRunResumed(e) => e.thread_id.as_ref(),
1914            RoderEvent::WorkflowRunStopped(e) => e.thread_id.as_ref(),
1915            RoderEvent::WorkflowRunCompleted(e) => e.thread_id.as_ref(),
1916            RoderEvent::WorkflowRunFailed(e) => e.thread_id.as_ref(),
1917        }
1918    }
1919
1920    pub fn turn_id(&self) -> Option<&TurnId> {
1921        match self {
1922            RoderEvent::ExtensionEventEmitted(_) | RoderEvent::EventSinkFailed(_) => None,
1923            RoderEvent::ThreadForkRequested(_)
1924            | RoderEvent::ThreadForked(_)
1925            | RoderEvent::ThreadForkFailed(_)
1926            | RoderEvent::ThreadForkRemoved(_) => None,
1927            RoderEvent::TurnStarted(e) => Some(&e.turn_id),
1928            RoderEvent::ContextAssemblyStarted(e) => Some(&e.turn_id),
1929            RoderEvent::ContextBlockAdded(e) => Some(&e.turn_id),
1930            RoderEvent::ContextAssemblyCompleted(e) => Some(&e.turn_id),
1931            RoderEvent::ContextEntrypointCandidatesInjected(e) => Some(&e.turn_id),
1932            RoderEvent::ContextCompactionStarted(e) => Some(&e.turn_id),
1933            RoderEvent::ContextCompactionRecorded(e) => Some(&e.turn_id),
1934            RoderEvent::InferenceRoutingDecision(e) => Some(&e.turn_id),
1935            RoderEvent::InferenceStarted(e) => Some(&e.turn_id),
1936            RoderEvent::InferenceEventReceived(e) => Some(&e.turn_id),
1937            RoderEvent::ToolCallRequested(e) => Some(&e.turn_id),
1938            RoderEvent::ToolCallValidationRecorded(e) => Some(&e.turn_id),
1939            RoderEvent::ReliabilityFailureRecorded(e) => Some(&e.context.turn_id),
1940            RoderEvent::ReliabilityRetryRecorded(e) => Some(&e.context.turn_id),
1941            RoderEvent::ReliabilityLimitRecorded(e) => Some(&e.context.turn_id),
1942            RoderEvent::ReliabilityMetricRecorded(e) => Some(&e.context.turn_id),
1943            RoderEvent::CodeIndexingStarted(e) => e.context.turn_id.as_ref(),
1944            RoderEvent::CodeIndexChunked(e) => e.context.turn_id.as_ref(),
1945            RoderEvent::CodeIndexEmbedded(e) => e.context.turn_id.as_ref(),
1946            RoderEvent::CodeIndexReady(_) => None,
1947            RoderEvent::CodeIndexStale(e) => e.context.turn_id.as_ref(),
1948            RoderEvent::CodeIndexFailed(e) => e.context.turn_id.as_ref(),
1949            RoderEvent::CodeIndexProofFilteredResultDropped(e) => e.context.turn_id.as_ref(),
1950            RoderEvent::ApprovalRequested(e) => Some(&e.turn_id),
1951            RoderEvent::ApprovalResolved(e) => Some(&e.turn_id),
1952            RoderEvent::ExternalToolCallRequested(e) => Some(&e.turn_id),
1953            RoderEvent::ExternalToolCallResolved(e) => Some(&e.turn_id),
1954            RoderEvent::UserInputRequested(e) => Some(&e.turn_id),
1955            RoderEvent::UserInputResolved(e) => Some(&e.turn_id),
1956            RoderEvent::TaskLedgerUpdated(e) => Some(&e.turn_id),
1957            RoderEvent::VerificationRequired(e) => Some(&e.turn_id),
1958            RoderEvent::VerificationCompleted(e) => Some(&e.turn_id),
1959            RoderEvent::VerificationSkipped(e) => Some(&e.turn_id),
1960            RoderEvent::PolicyDecisionRecorded(e) => Some(&e.turn_id),
1961            RoderEvent::PolicyBypassActive(e) => Some(&e.turn_id),
1962            RoderEvent::PolicyModeChanged(e) => e.turn_id.as_ref(),
1963            RoderEvent::PolicyExitPlanRequested(e) => Some(&e.turn_id),
1964            RoderEvent::PolicyExitPlanResolved(e) => Some(&e.turn_id),
1965            RoderEvent::ToolCallStarted(e) => Some(&e.turn_id),
1966            RoderEvent::ToolCallCompleted(e) => Some(&e.turn_id),
1967            RoderEvent::ToolOutputTruncated(e) => Some(&e.turn_id),
1968            RoderEvent::SubagentStarted(e) => Some(&e.turn_id),
1969            RoderEvent::SubagentMessage(e) => Some(&e.turn_id),
1970            RoderEvent::SubagentToolCall(e) => Some(&e.turn_id),
1971            RoderEvent::SubagentCompleted(e) => Some(&e.turn_id),
1972            RoderEvent::SubagentFailed(e) => Some(&e.turn_id),
1973            RoderEvent::SubagentTraceCreated(e) => Some(&e.summary.parent.turn_id),
1974            RoderEvent::SubagentTraceDelta(e) => Some(&e.delta.parent.turn_id),
1975            RoderEvent::SubagentTraceStatusChanged(e) => Some(&e.parent.turn_id),
1976            RoderEvent::SubagentTraceCompleted(e) => Some(&e.summary.parent.turn_id),
1977            RoderEvent::SubagentTraceFailed(e) => Some(&e.summary.parent.turn_id),
1978            RoderEvent::PlanReviewCreated(e) => Some(&e.review.turn_id),
1979            RoderEvent::PlanReviewStatusChanged(e) => Some(&e.turn_id),
1980            RoderEvent::PlanReviewCommentAdded(e) => Some(&e.turn_id),
1981            RoderEvent::PlanReviewRewritten(e) => Some(&e.turn_id),
1982            RoderEvent::PlanReviewApproved(e) => Some(&e.turn_id),
1983            RoderEvent::PlanReviewRejected(e) => Some(&e.turn_id),
1984            RoderEvent::HunkRecorded(e) => Some(&e.hunk.turn_id),
1985            RoderEvent::WorkspaceChangeObserved(e) => Some(&e.change.turn_id),
1986            RoderEvent::HunkRollbackRequested(e) => Some(&e.turn_id),
1987            RoderEvent::HunkRollbackCompleted(e) => Some(&e.turn_id),
1988            RoderEvent::MediaArtifactCreated(e) => Some(&e.turn_id),
1989            RoderEvent::MediaArtifactUpdated(e) => Some(&e.turn_id),
1990            RoderEvent::MediaPreviewReady(e) => Some(&e.turn_id),
1991            RoderEvent::ContextArtifactCreated(e) => Some(&e.turn_id),
1992            RoderEvent::ContextArtifactAppended(e) => Some(&e.turn_id),
1993            RoderEvent::ContextArtifactCapped(e) => Some(&e.turn_id),
1994            RoderEvent::DiscoveryItemRead(e) => Some(&e.turn_id),
1995            RoderEvent::DiscoveryItemPromoted(e) => e.record.turn_id.as_ref(),
1996            RoderEvent::DiscoveryPromotionReused(e) => e.record.turn_id.as_ref(),
1997            RoderEvent::DiscoveryWarmCacheHit(e) => e.record.turn_id.as_ref(),
1998            RoderEvent::DiscoveryPromotionExpired(e) => e.record.turn_id.as_ref(),
1999            RoderEvent::RetrievalRoutePlanned(e) => Some(&e.plan.turn_id),
2000            RoderEvent::RetrievalRouteAccepted(e) => Some(&e.turn_id),
2001            RoderEvent::RetrievalRouteIgnored(e) => Some(&e.turn_id),
2002            RoderEvent::RetrievalRouteFailed(e) => Some(&e.turn_id),
2003            RoderEvent::RetrievalResultUsed(e) => Some(&e.turn_id),
2004            RoderEvent::RetrievalDiscoveryItemPromoted(e) => Some(&e.turn_id),
2005            RoderEvent::RetrievalPromotionSkipped(e) => Some(&e.turn_id),
2006            RoderEvent::MemoryRecallReady(e) => Some(&e.turn_id),
2007            RoderEvent::MemoryObservationRecorded(e) => Some(&e.turn_id),
2008            RoderEvent::TaskStarted(e) => e.turn_id.as_ref(),
2009            RoderEvent::TaskOutput(e) => e.turn_id.as_ref(),
2010            RoderEvent::TaskCompleted(e) => e.turn_id.as_ref(),
2011            RoderEvent::TaskFailed(e) => e.turn_id.as_ref(),
2012            RoderEvent::TaskCancelled(e) => e.turn_id.as_ref(),
2013            RoderEvent::FileChangePreviewReady(e) => Some(&e.turn_id),
2014            RoderEvent::FileChanged(e) => Some(&e.turn_id),
2015            RoderEvent::TranscriptItemAppended(e) => Some(&e.turn_id),
2016            RoderEvent::TurnCompleted(e) => Some(&e.turn_id),
2017            RoderEvent::TurnFailed(e) => Some(&e.turn_id),
2018            RoderEvent::TurnPartialResult(e) => Some(&e.turn_id),
2019            RoderEvent::TurnDeadlineExceeded(e) => Some(&e.turn_id),
2020            RoderEvent::TurnInterrupted(e) => Some(&e.turn_id),
2021            RoderEvent::TurnSteered(e) => Some(&e.turn_id),
2022            RoderEvent::TeamMemberMessageDelta(e) => Some(&e.turn_id),
2023            RoderEvent::TeamMemberCompleted(e) => e.turn_id.as_ref(),
2024            RoderEvent::SkillActivationResolved(e) => Some(&e.turn_id),
2025            RoderEvent::SkillIndexRendered(e) => Some(&e.turn_id),
2026            RoderEvent::SkillInvoked(e) => Some(&e.turn_id),
2027            RoderEvent::SkillAutoActivated(e) => Some(&e.turn_id),
2028            RoderEvent::SkillSkipped(e) => Some(&e.turn_id),
2029            RoderEvent::RuntimeStarted(_)
2030            | RoderEvent::ExtensionRegistered(_)
2031            | RoderEvent::ThreadCreated(_)
2032            | RoderEvent::ThreadLoaded(_)
2033            | RoderEvent::WorkflowImportsDetected(_)
2034            | RoderEvent::WorkflowImportPreviewed(_)
2035            | RoderEvent::WorkflowImportEnabled(_)
2036            | RoderEvent::WorkflowImportDisabled(_)
2037            | RoderEvent::WorkflowImportStale(_)
2038            | RoderEvent::WorkflowImportFailed(_)
2039            | RoderEvent::MediaArtifactDeleted(_)
2040            | RoderEvent::ContextArtifactDeleted(_)
2041            | RoderEvent::ContextArtifactRetentionExpired(_)
2042            | RoderEvent::DiscoveryCatalogBuilt(_)
2043            | RoderEvent::DiscoveryItemUpdated(_)
2044            | RoderEvent::DiscoveryAuthRequired(_)
2045            | RoderEvent::MemorySaved(_)
2046            | RoderEvent::MemoryUpdated(_)
2047            | RoderEvent::MemoryDeleted(_)
2048            | RoderEvent::MemoryQueried(_)
2049            | RoderEvent::MemoryReembedQueued(_)
2050            | RoderEvent::MemoryProviderChanged(_)
2051            | RoderEvent::KnowledgeSaved(_)
2052            | RoderEvent::KnowledgeUpdated(_)
2053            | RoderEvent::KnowledgeArchived(_)
2054            | RoderEvent::KnowledgeLinked(_)
2055            | RoderEvent::RemoteServerStarted(_)
2056            | RoderEvent::RemoteServerStopped(_)
2057            | RoderEvent::RemoteAuthFailed(_)
2058            | RoderEvent::RemoteClientConnected(_)
2059            | RoderEvent::RemoteClientDisconnected(_)
2060            | RoderEvent::ThreadGoalUpdated(_)
2061            | RoderEvent::ThreadGoalCleared(_)
2062            | RoderEvent::RoadmapChanged(_)
2063            | RoderEvent::AutomationCreated(_)
2064            | RoderEvent::AutomationUpdated(_)
2065            | RoderEvent::AutomationDeleted(_)
2066            | RoderEvent::AutomationDue(_)
2067            | RoderEvent::AutomationLeased(_)
2068            | RoderEvent::AutomationQueued(_)
2069            | RoderEvent::AutomationSkipped(_)
2070            | RoderEvent::AutomationLeaseExpired(_)
2071            | RoderEvent::SkillsCatalogLoaded(_)
2072            | RoderEvent::SkillConfigApplied(_)
2073            | RoderEvent::RunnerLifecycle(_)
2074            | RoderEvent::TeamStarted(_)
2075            | RoderEvent::TeamMemberStarted(_)
2076            | RoderEvent::TeamMemberStatusChanged(_)
2077            | RoderEvent::TeamDisplayModeChanged(_)
2078            | RoderEvent::TeamTaskChanged(_)
2079            | RoderEvent::TeamCleanupCompleted(_) => None,
2080            RoderEvent::ProcessStarted(e) => e.process.turn_id.as_ref(),
2081            RoderEvent::ProcessOutput(e) => e.turn_id.as_ref(),
2082            RoderEvent::ProcessExited(e) => e.process.turn_id.as_ref(),
2083            RoderEvent::ProcessStopping(_) => None,
2084            RoderEvent::ProcessStopped(e) => e.process.turn_id.as_ref(),
2085            RoderEvent::ProcessFailed(e) => e.process.turn_id.as_ref(),
2086            RoderEvent::AutomationStarted(e) => e.run.turn_id.as_ref(),
2087            RoderEvent::AutomationCompleted(e) => e.run.turn_id.as_ref(),
2088            RoderEvent::AutomationFailed(e) => e.run.turn_id.as_ref(),
2089            RoderEvent::WorkflowRunDrafted(e) => e.turn_id.as_ref(),
2090            RoderEvent::WorkflowApprovalRequested(e) => e.turn_id.as_ref(),
2091            RoderEvent::WorkflowRunApproved(e) => e.turn_id.as_ref(),
2092            RoderEvent::WorkflowRunDenied(e) => e.turn_id.as_ref(),
2093            RoderEvent::WorkflowRunQueued(e) => e.turn_id.as_ref(),
2094            RoderEvent::WorkflowRunStarted(e) => e.turn_id.as_ref(),
2095            RoderEvent::WorkflowPhaseStarted(e) => e.turn_id.as_ref(),
2096            RoderEvent::WorkflowPhaseCompleted(e) => e.turn_id.as_ref(),
2097            RoderEvent::WorkflowAgentQueued(e) => e.turn_id.as_ref(),
2098            RoderEvent::WorkflowAgentStarted(e) => e.turn_id.as_ref(),
2099            RoderEvent::WorkflowAgentCompleted(e) => e.turn_id.as_ref(),
2100            RoderEvent::WorkflowAgentFailed(e) => e.turn_id.as_ref(),
2101            RoderEvent::WorkflowOutputRecorded(e) => e.turn_id.as_ref(),
2102            RoderEvent::WorkflowCheckpointRecorded(e) => e.turn_id.as_ref(),
2103            RoderEvent::WorkflowRunPaused(e) => e.turn_id.as_ref(),
2104            RoderEvent::WorkflowRunResumed(e) => e.turn_id.as_ref(),
2105            RoderEvent::WorkflowRunStopped(e) => e.turn_id.as_ref(),
2106            RoderEvent::WorkflowRunCompleted(e) => e.turn_id.as_ref(),
2107            RoderEvent::WorkflowRunFailed(e) => e.turn_id.as_ref(),
2108        }
2109    }
2110}
2111
2112#[derive(Debug, Clone, Serialize, Deserialize)]
2113pub struct EventEnvelope {
2114    pub event_id: EventId,
2115    pub seq: u64,
2116    #[serde(with = "time::serde::rfc3339")]
2117    pub timestamp: OffsetDateTime,
2118    pub source: EventSource,
2119    pub kind: String,
2120    pub thread_id: Option<ThreadId>,
2121    pub turn_id: Option<TurnId>,
2122    pub event: RoderEvent,
2123}
2124
2125impl EventEnvelope {
2126    pub fn matches_filter(&self, filter: &EventFilter) -> bool {
2127        filter.matches(self)
2128    }
2129}
2130
2131#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
2132pub struct EventFilter {
2133    pub thread_id: Option<ThreadId>,
2134    pub turn_id: Option<TurnId>,
2135    pub source: Option<EventSource>,
2136    pub kinds: Vec<String>,
2137}
2138
2139impl EventFilter {
2140    pub fn for_thread(thread_id: impl Into<ThreadId>) -> Self {
2141        Self {
2142            thread_id: Some(thread_id.into()),
2143            ..Self::default()
2144        }
2145    }
2146
2147    pub fn for_turn(thread_id: impl Into<ThreadId>, turn_id: impl Into<TurnId>) -> Self {
2148        Self {
2149            thread_id: Some(thread_id.into()),
2150            turn_id: Some(turn_id.into()),
2151            ..Self::default()
2152        }
2153    }
2154
2155    pub fn with_source(mut self, source: EventSource) -> Self {
2156        self.source = Some(source);
2157        self
2158    }
2159
2160    pub fn with_kind(mut self, kind: impl Into<String>) -> Self {
2161        self.kinds.push(kind.into());
2162        self
2163    }
2164
2165    pub fn matches(&self, envelope: &EventEnvelope) -> bool {
2166        if self
2167            .thread_id
2168            .as_ref()
2169            .is_some_and(|thread_id| envelope.thread_id.as_ref() != Some(thread_id))
2170        {
2171            return false;
2172        }
2173        if self
2174            .turn_id
2175            .as_ref()
2176            .is_some_and(|turn_id| envelope.turn_id.as_ref() != Some(turn_id))
2177        {
2178            return false;
2179        }
2180        if self
2181            .source
2182            .as_ref()
2183            .is_some_and(|source| &envelope.source != source)
2184        {
2185            return false;
2186        }
2187        if !self.kinds.is_empty() && !self.kinds.iter().any(|kind| kind == &envelope.kind) {
2188            return false;
2189        }
2190        true
2191    }
2192}
2193
2194#[cfg(test)]
2195mod tests {
2196    use super::*;
2197
2198    fn envelope(thread_id: Option<&str>, turn_id: Option<&str>, kind: &str) -> EventEnvelope {
2199        EventEnvelope {
2200            event_id: "event-1".to_string(),
2201            seq: 1,
2202            timestamp: OffsetDateTime::UNIX_EPOCH,
2203            source: EventSource::Core,
2204            kind: kind.to_string(),
2205            thread_id: thread_id.map(str::to_string),
2206            turn_id: turn_id.map(str::to_string),
2207            event: RoderEvent::TurnStarted(TurnStarted {
2208                thread_id: thread_id.unwrap_or("thread-a").to_string(),
2209                turn_id: turn_id.unwrap_or("turn-a").to_string(),
2210                runtime_profile: RuntimeProfile::Interactive,
2211                timestamp: OffsetDateTime::UNIX_EPOCH,
2212            }),
2213        }
2214    }
2215
2216    #[test]
2217    fn event_filter_matches_thread_turn_source_and_kind() {
2218        let envelope = envelope(Some("thread-a"), Some("turn-a"), "turn.started");
2219        let filter = EventFilter::for_turn("thread-a", "turn-a")
2220            .with_source(EventSource::Core)
2221            .with_kind("turn.started");
2222
2223        assert!(filter.matches(&envelope));
2224        assert!(envelope.matches_filter(&filter));
2225        assert!(!EventFilter::for_thread("thread-b").matches(&envelope));
2226        assert!(!EventFilter::for_turn("thread-a", "turn-b").matches(&envelope));
2227        assert!(
2228            !EventFilter::default()
2229                .with_source(EventSource::Provider)
2230                .matches(&envelope)
2231        );
2232        assert!(
2233            !EventFilter::default()
2234                .with_kind("turn.completed")
2235                .matches(&envelope)
2236        );
2237    }
2238
2239    #[test]
2240    fn empty_event_filter_matches_everything() {
2241        assert!(EventFilter::default().matches(&envelope(None, None, "runtime.started")));
2242    }
2243
2244    #[test]
2245    fn code_index_event_kind_and_scope_are_visible() {
2246        let event = RoderEvent::CodeIndexProofFilteredResultDropped(
2247            crate::code_index::CodeIndexProofFilteredResultDropped {
2248                context: crate::code_index::CodeIndexEventContext {
2249                    workspace_root: std::path::PathBuf::from("/repo"),
2250                    generation_id: Some("gen-1".to_string()),
2251                    thread_id: Some("thread-1".to_string()),
2252                    turn_id: Some("turn-1".to_string()),
2253                },
2254                drop: crate::code_index::ProofFilteredDrop {
2255                    query_id: "query-1".to_string(),
2256                    path_hash: "path-hash".to_string(),
2257                    content_hash: "content-hash".to_string(),
2258                    reason: "proof missing".to_string(),
2259                },
2260                timestamp: OffsetDateTime::UNIX_EPOCH,
2261            },
2262        );
2263
2264        assert_eq!(event.kind(), "code_index.proof_filtered_result_dropped");
2265        assert_eq!(event.thread_id().map(String::as_str), Some("thread-1"));
2266        assert_eq!(event.turn_id().map(String::as_str), Some("turn-1"));
2267        assert_eq!(event.source(), EventSource::Core);
2268    }
2269
2270    #[test]
2271    fn workflow_event_kind_scope_and_agent_metadata_are_visible() {
2272        let agent = crate::dynamic_workflows::WorkflowAgentRun {
2273            agent_id: "agent-1".to_string(),
2274            phase_id: "phase-1".to_string(),
2275            description: "Review findings".to_string(),
2276            status: crate::dynamic_workflows::WorkflowAgentStatus::Completed,
2277            lane: Some(crate::subagents::SubagentLane::Reviewer),
2278            model: Some("mock-model".to_string()),
2279            thread_id: Some("child-thread".to_string()),
2280            turn_id: Some("child-turn".to_string()),
2281            usage: Some(crate::inference::TokenUsage::new(10, 5, 15)),
2282            exit_reason: None,
2283            error: None,
2284            started_at: Some(OffsetDateTime::UNIX_EPOCH),
2285            completed_at: Some(OffsetDateTime::UNIX_EPOCH),
2286        };
2287        let event = RoderEvent::WorkflowAgentCompleted(WorkflowAgentCompleted {
2288            run_id: "run-1".to_string(),
2289            thread_id: Some("thread-1".to_string()),
2290            turn_id: Some("turn-1".to_string()),
2291            agent,
2292            timestamp: OffsetDateTime::UNIX_EPOCH,
2293        });
2294
2295        assert_eq!(event.kind(), "workflows/agentCompleted");
2296        assert_eq!(event.source(), EventSource::Core);
2297        assert_eq!(event.thread_id().map(String::as_str), Some("thread-1"));
2298        assert_eq!(event.turn_id().map(String::as_str), Some("turn-1"));
2299
2300        let value = serde_json::to_value(&event).unwrap();
2301        let event_value = &value["WorkflowAgentCompleted"];
2302        assert_eq!(event_value["runId"], "run-1");
2303        assert_eq!(event_value["agent"]["phaseId"], "phase-1");
2304        assert_eq!(event_value["agent"]["agentId"], "agent-1");
2305        assert_eq!(event_value["agent"]["status"], "completed");
2306        assert_eq!(event_value["agent"]["usage"]["total_tokens"], 15);
2307    }
2308
2309    #[test]
2310    fn event_timestamps_serialize_as_rfc3339_strings() {
2311        let value =
2312            serde_json::to_value(envelope(Some("thread-a"), Some("turn-a"), "turn.started"))
2313                .unwrap();
2314
2315        assert_eq!(value["timestamp"], "1970-01-01T00:00:00Z");
2316        assert_eq!(
2317            value["event"]["TurnStarted"]["timestamp"],
2318            "1970-01-01T00:00:00Z"
2319        );
2320    }
2321
2322    #[test]
2323    fn subagent_event_envelope_round_trips_parent_ids() {
2324        let event = RoderEvent::SubagentStarted(SubagentStarted {
2325            thread_id: "child-thread".to_string(),
2326            turn_id: "child-turn".to_string(),
2327            parent_thread_id: "parent-thread".to_string(),
2328            parent_turn_id: "parent-turn".to_string(),
2329            agent_type: "explore".to_string(),
2330            description: "Inspect repository".to_string(),
2331            model: Some("test-model".to_string()),
2332            timestamp: OffsetDateTime::UNIX_EPOCH,
2333        });
2334        let envelope = EventEnvelope {
2335            event_id: "event-subagent-started".to_string(),
2336            seq: 7,
2337            timestamp: OffsetDateTime::UNIX_EPOCH,
2338            source: event.source(),
2339            kind: event.kind().to_string(),
2340            thread_id: event.thread_id().cloned(),
2341            turn_id: event.turn_id().cloned(),
2342            event,
2343        };
2344
2345        let serialized = serde_json::to_string(&envelope).unwrap();
2346        let round_trip: EventEnvelope = serde_json::from_str(&serialized).unwrap();
2347
2348        assert_eq!(round_trip.kind, "subagent.started");
2349        assert_eq!(round_trip.source, EventSource::Extension);
2350        assert_eq!(round_trip.thread_id.as_deref(), Some("child-thread"));
2351        assert_eq!(round_trip.turn_id.as_deref(), Some("child-turn"));
2352
2353        match round_trip.event {
2354            RoderEvent::SubagentStarted(started) => {
2355                assert_eq!(started.parent_thread_id, "parent-thread");
2356                assert_eq!(started.parent_turn_id, "parent-turn");
2357            }
2358            other => panic!("unexpected event: {other:?}"),
2359        }
2360    }
2361
2362    #[test]
2363    fn subagent_trace_event_uses_parent_turn_for_filtering() {
2364        let summary = SubagentTraceSummary {
2365            trace_id: "trace-1".to_string(),
2366            parent: ParentTurnRef {
2367                thread_id: "parent-thread".to_string(),
2368                turn_id: "parent-turn".to_string(),
2369            },
2370            child_thread_id: "child-thread".to_string(),
2371            child_turn_id: "child-turn".to_string(),
2372            title: "Inspect repository".to_string(),
2373            role: "explorer".to_string(),
2374            model: Some("test-model".to_string()),
2375            lane: None,
2376            status: SubagentTraceStatus::Running,
2377            elapsed_ms: 10,
2378            usage: None,
2379            destination: None,
2380            latest_activity: None,
2381            error_summary: None,
2382            exit_reason: None,
2383        };
2384        let event = RoderEvent::SubagentTraceCreated(SubagentTraceCreated {
2385            summary,
2386            timestamp: OffsetDateTime::UNIX_EPOCH,
2387        });
2388
2389        assert_eq!(event.kind(), "turn/subagentTraceCreated");
2390        assert_eq!(event.source(), EventSource::Extension);
2391        assert_eq!(event.thread_id().map(String::as_str), Some("parent-thread"));
2392        assert_eq!(event.turn_id().map(String::as_str), Some("parent-turn"));
2393    }
2394
2395    #[test]
2396    fn task_events_round_trip_with_replay_ids() {
2397        let event = RoderEvent::TaskOutput(TaskOutput {
2398            task_id: "task-1".to_string(),
2399            stream: crate::tasks::TaskOutputStream::Stdout,
2400            chunk: "building\n".to_string(),
2401            dropped_bytes: 0,
2402            thread_id: Some("thread-a".to_string()),
2403            turn_id: Some("turn-a".to_string()),
2404            timestamp: OffsetDateTime::UNIX_EPOCH,
2405        });
2406        let envelope = EventEnvelope {
2407            event_id: "event-task-output".to_string(),
2408            seq: 9,
2409            timestamp: OffsetDateTime::UNIX_EPOCH,
2410            source: event.source(),
2411            kind: event.kind().to_string(),
2412            thread_id: event.thread_id().cloned(),
2413            turn_id: event.turn_id().cloned(),
2414            event,
2415        };
2416
2417        let serialized = serde_json::to_string(&envelope).unwrap();
2418        let round_trip: EventEnvelope = serde_json::from_str(&serialized).unwrap();
2419
2420        assert_eq!(round_trip.kind, "task.output");
2421        assert_eq!(round_trip.source, EventSource::Extension);
2422        assert_eq!(round_trip.thread_id.as_deref(), Some("thread-a"));
2423        assert_eq!(round_trip.turn_id.as_deref(), Some("turn-a"));
2424        match round_trip.event {
2425            RoderEvent::TaskOutput(output) => {
2426                assert_eq!(output.task_id, "task-1");
2427                assert_eq!(output.stream, crate::tasks::TaskOutputStream::Stdout);
2428                assert_eq!(output.chunk, "building\n");
2429            }
2430            other => panic!("unexpected event: {other:?}"),
2431        }
2432    }
2433
2434    #[test]
2435    fn automations_event_exposes_kind_source_and_replay_scope() {
2436        let run = crate::automations::AutomationRunSummary {
2437            run_id: "run-1".to_string(),
2438            automation_id: "automation-1".to_string(),
2439            occurrence_key: "automation-1:1970-01-01T00:00:00Z".to_string(),
2440            state: crate::automations::AutomationRunState::Completed,
2441            scheduled_for: OffsetDateTime::UNIX_EPOCH,
2442            queued_at: Some(OffsetDateTime::UNIX_EPOCH),
2443            started_at: Some(OffsetDateTime::UNIX_EPOCH),
2444            finished_at: Some(OffsetDateTime::UNIX_EPOCH),
2445            thread_id: Some("thread-a".to_string()),
2446            turn_id: Some("turn-a".to_string()),
2447            task_id: Some("task-1".to_string()),
2448            server_id: Some("desktop-main".to_string()),
2449            server_role: Some("desktop".to_string()),
2450            exit_code: Some(0),
2451            error: None,
2452            skip_reason: None,
2453        };
2454        let event = RoderEvent::AutomationCompleted(crate::automations::AutomationCompleted {
2455            run,
2456            timestamp: OffsetDateTime::UNIX_EPOCH,
2457        });
2458
2459        assert_eq!(event.kind(), "automations/completed");
2460        assert_eq!(event.source(), EventSource::Core);
2461        assert_eq!(event.thread_id().map(String::as_str), Some("thread-a"));
2462        assert_eq!(event.turn_id().map(String::as_str), Some("turn-a"));
2463
2464        let value = serde_json::to_value(&event).unwrap();
2465        assert_eq!(
2466            value["AutomationCompleted"]["run"]["occurrenceKey"],
2467            "automation-1:1970-01-01T00:00:00Z"
2468        );
2469        assert_eq!(
2470            value["AutomationCompleted"]["run"]["serverId"],
2471            "desktop-main"
2472        );
2473    }
2474
2475    #[test]
2476    fn tool_call_completed_round_trips_error_status() {
2477        let event = RoderEvent::ToolCallCompleted(ToolCallCompleted {
2478            thread_id: "thread-a".to_string(),
2479            turn_id: "turn-a".to_string(),
2480            tool_id: "tool-a".to_string(),
2481            tool_name: Some("list_files".to_string()),
2482            display_payload: Some(serde_json::json!({ "path": "." })),
2483            is_error: true,
2484            output: Some("tool failed".to_string()),
2485            timestamp: OffsetDateTime::UNIX_EPOCH,
2486        });
2487        let envelope = EventEnvelope {
2488            event_id: "event-tool-completed".to_string(),
2489            seq: 10,
2490            timestamp: OffsetDateTime::UNIX_EPOCH,
2491            source: event.source(),
2492            kind: event.kind().to_string(),
2493            thread_id: event.thread_id().cloned(),
2494            turn_id: event.turn_id().cloned(),
2495            event,
2496        };
2497
2498        let serialized = serde_json::to_string(&envelope).unwrap();
2499        let round_trip: EventEnvelope = serde_json::from_str(&serialized).unwrap();
2500
2501        assert_eq!(round_trip.kind, "tool.call_completed");
2502        match round_trip.event {
2503            RoderEvent::ToolCallCompleted(completed) => {
2504                assert_eq!(completed.tool_id, "tool-a");
2505                assert!(completed.is_error);
2506                assert_eq!(completed.output.as_deref(), Some("tool failed"));
2507            }
2508            other => panic!("unexpected event: {other:?}"),
2509        }
2510    }
2511
2512    #[test]
2513    fn file_change_preview_event_round_trips_public_metadata() {
2514        let event = RoderEvent::FileChangePreviewReady(FileChangePreviewReady {
2515            thread_id: "thread-a".to_string(),
2516            turn_id: "turn-a".to_string(),
2517            tool_id: "tool-a".to_string(),
2518            tool_name: "edit".to_string(),
2519            path: "src/lib.rs".to_string(),
2520            change_type: "modify".to_string(),
2521            before: Some("old\n".to_string()),
2522            after: "new\n".to_string(),
2523            supports_partial: false,
2524            timestamp: OffsetDateTime::UNIX_EPOCH,
2525        });
2526        let envelope = EventEnvelope {
2527            event_id: "event-file-preview".to_string(),
2528            seq: 8,
2529            timestamp: OffsetDateTime::UNIX_EPOCH,
2530            source: event.source(),
2531            kind: event.kind().to_string(),
2532            thread_id: event.thread_id().cloned(),
2533            turn_id: event.turn_id().cloned(),
2534            event,
2535        };
2536
2537        let serialized = serde_json::to_string(&envelope).unwrap();
2538        let round_trip: EventEnvelope = serde_json::from_str(&serialized).unwrap();
2539
2540        assert_eq!(round_trip.kind, "file.change_preview_ready");
2541        assert_eq!(round_trip.source, EventSource::Tool);
2542        assert_eq!(round_trip.thread_id.as_deref(), Some("thread-a"));
2543        assert_eq!(round_trip.turn_id.as_deref(), Some("turn-a"));
2544        match round_trip.event {
2545            RoderEvent::FileChangePreviewReady(preview) => {
2546                assert_eq!(preview.tool_id, "tool-a");
2547                assert_eq!(preview.tool_name, "edit");
2548                assert_eq!(preview.path, "src/lib.rs");
2549                assert_eq!(preview.change_type, "modify");
2550                assert_eq!(preview.before.as_deref(), Some("old\n"));
2551                assert_eq!(preview.after, "new\n");
2552                assert!(!preview.supports_partial);
2553            }
2554            other => panic!("unexpected event: {other:?}"),
2555        }
2556    }
2557
2558    #[test]
2559    fn inference_started_deserializes_older_records_without_model_fields() {
2560        let value = serde_json::json!({
2561            "InferenceStarted": {
2562                "thread_id": "thread-a",
2563                "turn_id": "turn-a",
2564                "engine_id": "mock",
2565                "timestamp": "1970-01-01T00:00:00Z"
2566            }
2567        });
2568
2569        let event: RoderEvent = serde_json::from_value(value).unwrap();
2570
2571        match event {
2572            RoderEvent::InferenceStarted(started) => {
2573                assert_eq!(started.model.provider, "");
2574                assert_eq!(started.model.model, "");
2575                assert_eq!(started.reasoning, ReasoningConfig::default());
2576            }
2577            other => panic!("expected inference started, got {other:?}"),
2578        }
2579    }
2580
2581    #[test]
2582    fn processes_events_expose_kind_source_scope_and_round_trip() {
2583        let descriptor = crate::processes::ProcessDescriptor {
2584            process_id: "process-1".to_string(),
2585            origin: crate::processes::ProcessOrigin::CommandExec,
2586            state: crate::processes::ProcessState::Running,
2587            command: vec!["sleep".to_string(), "10".to_string()],
2588            command_summary: "sleep 10".to_string(),
2589            cwd: Some("/repo".to_string()),
2590            pid: Some(1234),
2591            task_id: Some("task-1".to_string()),
2592            thread_id: Some("thread-a".to_string()),
2593            turn_id: Some("turn-a".to_string()),
2594            runner_destination_id: None,
2595            runner_session_id: None,
2596            stoppable: true,
2597            started_at: OffsetDateTime::UNIX_EPOCH,
2598            updated_at: OffsetDateTime::UNIX_EPOCH,
2599            stdout_tail: None,
2600            stderr_tail: None,
2601        };
2602        let event = RoderEvent::ProcessStarted(crate::processes::ProcessStarted {
2603            process: descriptor,
2604            timestamp: OffsetDateTime::UNIX_EPOCH,
2605        });
2606        let envelope = EventEnvelope {
2607            event_id: "event-process-started".to_string(),
2608            seq: 11,
2609            timestamp: OffsetDateTime::UNIX_EPOCH,
2610            source: event.source(),
2611            kind: event.kind().to_string(),
2612            thread_id: event.thread_id().cloned(),
2613            turn_id: event.turn_id().cloned(),
2614            event,
2615        };
2616
2617        let serialized = serde_json::to_string(&envelope).unwrap();
2618        let round_trip: EventEnvelope = serde_json::from_str(&serialized).unwrap();
2619
2620        assert_eq!(round_trip.kind, "process.started");
2621        assert_eq!(round_trip.source, EventSource::Extension);
2622        assert_eq!(round_trip.thread_id.as_deref(), Some("thread-a"));
2623        assert_eq!(round_trip.turn_id.as_deref(), Some("turn-a"));
2624        match round_trip.event {
2625            RoderEvent::ProcessStarted(started) => {
2626                assert_eq!(started.process.process_id, "process-1");
2627                assert_eq!(started.process.pid, Some(1234));
2628            }
2629            other => panic!("unexpected event: {other:?}"),
2630        }
2631
2632        let output = RoderEvent::ProcessOutput(crate::processes::ProcessOutput {
2633            process_id: "process-1".to_string(),
2634            stream: crate::tasks::TaskOutputStream::Stdout,
2635            chunk: "ready\n".to_string(),
2636            dropped_bytes: 0,
2637            thread_id: Some("thread-a".to_string()),
2638            turn_id: Some("turn-a".to_string()),
2639            timestamp: OffsetDateTime::UNIX_EPOCH,
2640        });
2641        assert_eq!(output.kind(), "process.output");
2642        assert_eq!(output.thread_id().map(String::as_str), Some("thread-a"));
2643        assert_eq!(output.turn_id().map(String::as_str), Some("turn-a"));
2644    }
2645
2646    #[test]
2647    fn skill_activation_event_exposes_kind_source_and_turn_scope() {
2648        let descriptor = crate::skills::SkillDescriptor {
2649            id: "builtin:commit".to_string(),
2650            name: "commit".to_string(),
2651            canonical_path: "roder-builtin://commit/SKILL.md".to_string(),
2652            source: crate::skills::SkillSource::BuiltIn,
2653            exposure: crate::skills::SkillExposure::DirectOnly,
2654            activation: crate::skills::SkillActivationState::Enabled,
2655            description: "Commit staged changes safely.".to_string(),
2656            short_description: Some("Commit safely".to_string()),
2657            experimental: false,
2658            diagnostics: Vec::new(),
2659            agent_metadata: None,
2660        };
2661        let event = RoderEvent::SkillActivationResolved(crate::skills::SkillActivationResolved {
2662            thread_id: "thread-a".to_string(),
2663            turn_id: "turn-a".to_string(),
2664            selector: crate::skills::SkillSelector::Name {
2665                name: "commit".to_string(),
2666            },
2667            activation_reason: crate::skills::SkillActivationReason::FeatureBinding,
2668            activated: true,
2669            descriptor: Some(descriptor),
2670            diagnostic: None,
2671            timestamp: OffsetDateTime::UNIX_EPOCH,
2672        });
2673
2674        assert_eq!(event.kind(), "skills/activationResolved");
2675        assert_eq!(event.source(), EventSource::Core);
2676        assert_eq!(event.thread_id().map(String::as_str), Some("thread-a"));
2677        assert_eq!(event.turn_id().map(String::as_str), Some("turn-a"));
2678    }
2679}