Skip to main content

gemini_live_harness/
task.rs

1//! Durable task metadata and lifecycle records.
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6use crate::fs::{next_id, now_ms};
7
8/// High-level durable status for one persisted harness task.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
10#[serde(rename_all = "camelCase")]
11pub enum TaskStatus {
12    Running,
13    Succeeded,
14    Failed,
15    Cancelled,
16    Interrupted,
17}
18
19impl TaskStatus {
20    pub fn is_terminal(self) -> bool {
21        matches!(
22            self,
23            Self::Succeeded | Self::Failed | Self::Cancelled | Self::Interrupted
24        )
25    }
26}
27
28/// Process/runtime instance metadata for a task that is already executing.
29///
30/// The harness does not treat tasks as a durable work queue with worker
31/// claiming. Instead, background tool tasks are created after a blocking tool
32/// call has already begun running inside the current host process. These
33/// fields identify which runtime instance owns that execution so a later
34/// process restart can mark stale `Running` tasks as `Interrupted`.
35#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
36#[serde(rename_all = "camelCase")]
37pub struct TaskRuntimeInstance {
38    pub instance_id: String,
39    pub pid: u32,
40    pub started_at_ms: u64,
41}
42
43impl TaskRuntimeInstance {
44    pub fn current() -> Self {
45        Self {
46            instance_id: next_id("runtime"),
47            pid: std::process::id(),
48            started_at_ms: now_ms(),
49        }
50    }
51}
52
53/// Primary persisted task record.
54#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
55#[serde(rename_all = "camelCase")]
56pub struct HarnessTask {
57    pub id: String,
58    pub title: String,
59    pub instructions: String,
60    pub status: TaskStatus,
61    pub created_at_ms: u64,
62    pub updated_at_ms: u64,
63    #[serde(default)]
64    pub requested_by: Option<String>,
65    #[serde(default)]
66    pub tags: Vec<String>,
67    #[serde(default)]
68    pub metadata: Option<Value>,
69    #[serde(default)]
70    pub runtime: Option<TaskRuntimeInstance>,
71    #[serde(default)]
72    pub origin_call_id: Option<String>,
73    #[serde(default)]
74    pub summary: Option<String>,
75    #[serde(default)]
76    pub last_error: Option<String>,
77}
78
79/// Request to persist a task that is already running inside the current
80/// runtime instance.
81#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
82#[serde(rename_all = "camelCase")]
83pub struct NewRunningTask {
84    pub title: String,
85    pub instructions: String,
86    pub runtime: TaskRuntimeInstance,
87    #[serde(default)]
88    pub requested_by: Option<String>,
89    #[serde(default)]
90    pub tags: Vec<String>,
91    #[serde(default)]
92    pub metadata: Option<Value>,
93    #[serde(default)]
94    pub origin_call_id: Option<String>,
95}
96
97/// Persisted terminal result for a successful task.
98#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
99#[serde(rename_all = "camelCase")]
100pub struct TaskResult {
101    pub task_id: String,
102    pub completed_at_ms: u64,
103    #[serde(default)]
104    pub summary: Option<String>,
105    pub output: Value,
106}
107
108/// Append-only event stream for task lifecycle changes.
109#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
110#[serde(rename_all = "camelCase")]
111pub struct TaskEvent {
112    pub sequence: u64,
113    pub recorded_at_ms: u64,
114    #[serde(flatten)]
115    pub kind: TaskEventKind,
116}
117
118/// Event payload variants stored in `events.jsonl`.
119#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
120#[serde(tag = "type", rename_all = "camelCase")]
121pub enum TaskEventKind {
122    Created,
123    Started {
124        runtime_instance_id: String,
125        pid: u32,
126    },
127    Progress {
128        message: String,
129        #[serde(default)]
130        details: Option<Value>,
131    },
132    Succeeded {
133        #[serde(default)]
134        summary: Option<String>,
135    },
136    Failed {
137        message: String,
138    },
139    Cancelled {
140        #[serde(default)]
141        reason: Option<String>,
142    },
143    Interrupted {
144        #[serde(default)]
145        runtime_instance_id: Option<String>,
146        #[serde(default)]
147        pid: Option<u32>,
148        reason: String,
149    },
150}
151
152/// Rich task projection returned to hosts and tools.
153#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
154#[serde(rename_all = "camelCase")]
155pub struct TaskDetail {
156    pub task: HarnessTask,
157    #[serde(default)]
158    pub result: Option<TaskResult>,
159    #[serde(default)]
160    pub events: Vec<TaskEvent>,
161}