Skip to main content

harn_vm/agent_events/
worker.rs

1use serde::{Deserialize, Serialize};
2
3/// Structured terminal outcome for one delegated sub-agent run.
4#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
5#[serde(rename_all = "snake_case")]
6pub enum SubagentTerminalStatus {
7    Success,
8    Failure,
9    Cancellation,
10    Timeout,
11}
12
13/// One coalesced filesystem notification from a hostlib `fs_watch`
14/// subscription.
15#[derive(Clone, Debug, Serialize, Deserialize)]
16pub struct FsWatchEvent {
17    pub kind: String,
18    pub paths: Vec<String>,
19    pub relative_paths: Vec<String>,
20    pub raw_kind: String,
21    pub error: Option<String>,
22}
23
24/// Typed worker lifecycle events emitted by delegated/background agent
25/// execution. Bridge-facing worker updates still derive a string status
26/// from these variants, but the runtime no longer passes raw status
27/// strings around internally.
28///
29/// `Spawned`/`Completed`/`Failed`/`Stopped`/`Cancelled` are the terminal-or-start
30/// states. `Progressed` is fired on intermediate milestones (e.g. a
31/// retriggerable worker resuming from `awaiting_input`, or a workflow
32/// stage completing without ending the worker). `WaitingForInput` covers
33/// retriggerable workers that finish a cycle but stay alive pending the
34/// next host-supplied trigger payload. `Suspended`/`Resumed` cover
35/// cooperative mid-loop pause and warm resume (harn#1836); the
36/// `agent_loop` honors the pause signal at the next turn boundary,
37/// distinct from a graceful `Stopped` handoff or hard `Cancelled` interrupt.
38#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
39pub enum WorkerEvent {
40    WorkerSpawned,
41    WorkerProgressed,
42    WorkerWaitingForInput,
43    WorkerSuspended,
44    WorkerResumed,
45    WorkerCompleted,
46    WorkerFailed,
47    WorkerStopped,
48    WorkerCancelled,
49}
50
51impl WorkerEvent {
52    /// The full set of `WorkerEvent` variants in canonical order. Mirrors
53    /// the pattern used by `ToolCallStatus::ALL` so the protocol-artifact
54    /// dumper can enumerate worker status wire values without
55    /// special-casing each lifecycle event.
56    pub const ALL: [Self; 9] = [
57        Self::WorkerSpawned,
58        Self::WorkerProgressed,
59        Self::WorkerWaitingForInput,
60        Self::WorkerSuspended,
61        Self::WorkerResumed,
62        Self::WorkerCompleted,
63        Self::WorkerFailed,
64        Self::WorkerStopped,
65        Self::WorkerCancelled,
66    ];
67
68    /// Wire-level status string used by bridge `worker_update` payloads
69    /// and ACP `worker_update` session updates. The four canonical
70    /// states are mirrored from harn's internal worker `status` field
71    /// (`running`/`completed`/`failed`/`cancelled`), and the four newer
72    /// lifecycle states pick names that don't collide with any existing
73    /// status string.
74    pub fn as_status(self) -> &'static str {
75        match self {
76            Self::WorkerSpawned => "running",
77            Self::WorkerProgressed => "progressed",
78            Self::WorkerWaitingForInput => "awaiting_input",
79            Self::WorkerSuspended => "suspended",
80            Self::WorkerResumed => "running",
81            Self::WorkerCompleted => "completed",
82            Self::WorkerFailed => "failed",
83            Self::WorkerStopped => "stopped",
84            Self::WorkerCancelled => "cancelled",
85        }
86    }
87
88    pub fn as_str(self) -> &'static str {
89        match self {
90            Self::WorkerSpawned => "WorkerSpawned",
91            Self::WorkerProgressed => "WorkerProgressed",
92            Self::WorkerWaitingForInput => "WorkerWaitingForInput",
93            Self::WorkerSuspended => "WorkerSuspended",
94            Self::WorkerResumed => "WorkerResumed",
95            Self::WorkerCompleted => "WorkerCompleted",
96            Self::WorkerFailed => "WorkerFailed",
97            Self::WorkerStopped => "WorkerStopped",
98            Self::WorkerCancelled => "WorkerCancelled",
99        }
100    }
101
102    /// True for lifecycle events that mean the worker has reached a
103    /// final, non-resumable state. Retriggerable awaiting, progressed,
104    /// and cooperative suspend/resume milestones are *not* terminal —
105    /// the worker keeps running, is waiting for a trigger, or is parked
106    /// awaiting an external resume.
107    pub fn is_terminal(self) -> bool {
108        matches!(
109            self,
110            Self::WorkerCompleted
111                | Self::WorkerFailed
112                | Self::WorkerStopped
113                | Self::WorkerCancelled
114        )
115    }
116}