use serde::{Deserialize, Serialize};
use std::path::PathBuf;
pub use observation::{
ChunkRef, Diagnostic, Event, EventScope, EventSource, ForgeId, Level, TaskRunId,
RESERVED_FIELD_PATHS,
};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "status", rename_all = "snake_case")]
pub enum RunStatus {
Pending,
Running,
Done { exit_code: i32, ended_at: u64 },
Killed { signal: i32, ended_at: u64 },
Lost { reason: String },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum Initiator {
Human { camp: String },
Agent { camp: String, agent: String, session: String },
Gnome { camp: String, shift: String },
Cron { camp: String, schedule: String },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BeholderStatus {
pub text: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub rewrite_added: Option<Vec<String>>,
}
impl BeholderStatus {
fn make(text: String) -> Self {
Self { text, rewrite_added: None }
}
pub fn none_auto() -> Self {
Self::make("none:auto".to_string())
}
pub fn none_explicit() -> Self {
Self::make("none:explicit".to_string())
}
pub fn attached(name: &str, version: &str) -> Self {
Self::make(format!("attached:{name}@{version}"))
}
pub fn declined(name: &str, reason: &str) -> Self {
Self::make(format!("declined:{name} reason=\"{reason}\""))
}
pub fn forced(name: &str, version: &str) -> Self {
Self::make(format!("forced:{name}@{version}"))
}
pub fn forced_against_flags(name: &str, version: &str) -> Self {
Self::make(format!("forced-against-flags:{name}@{version}"))
}
pub fn forced_against_tty(name: &str, version: &str) -> Self {
Self::make(format!("forced-against-tty:{name}@{version}"))
}
pub fn unknown_format() -> Self {
Self::make("unknown_format".to_string())
}
pub fn unknown_format_with_reason(name: &str, reason: &str) -> Self {
Self::make(format!("unknown_format:{name} reason=\"{reason}\""))
}
pub fn with_rewrite(mut self, added: Vec<String>) -> Self {
if !added.is_empty() {
let repr = added.join(" ");
self.text = format!("{} rewrite=\"{repr}\"", self.text);
self.rewrite_added = Some(added);
}
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TaskRunMeta {
pub id: TaskRunId,
pub command: String,
pub cwd: PathBuf,
pub env: Vec<(String, String)>,
pub started_at: u64,
pub status: RunStatus,
pub label: Option<String>,
pub initiator: Initiator,
pub beholder_status: Option<BeholderStatus>,
#[serde(default)]
pub pinned: bool,
#[serde(default)]
pub origin: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Stream {
Stdout,
Stderr,
Synth,
}
impl Stream {
pub fn as_str(self) -> &'static str {
match self {
Stream::Stdout => "stdout",
Stream::Stderr => "stderr",
Stream::Synth => "synth",
}
}
}
impl std::str::FromStr for Stream {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"stdout" => Ok(Stream::Stdout),
"stderr" => Ok(Stream::Stderr),
"synth" => Ok(Stream::Synth),
other => Err(format!("unknown stream: {other}")),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OutputChunk {
pub run_id: TaskRunId,
pub seq: u32,
pub offset_ms: u32,
pub stream: Stream,
pub bytes: Vec<u8>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SeqRange {
pub lo: u32,
pub hi: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Triage {
pub run_id: TaskRunId,
pub synopsis: String,
pub keep: Vec<KeepRange>,
pub primary: SeqRange,
pub model: String,
pub prompt_version: u32,
pub cached_at: u64,
pub partial: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KeepRange {
pub range: SeqRange,
pub reason: String,
}