Skip to main content

objectiveai_sdk/cli/
error.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3
4/// A failure or advisory written to stdout. `fatal: true` means the
5/// process is exiting with a non-zero status; `fatal: false` is a
6/// non-blocking warning (e.g. auto-update failed but the requested
7/// command still ran).
8///
9/// `message` is an arbitrary JSON value so producers can emit
10/// structured payloads (e.g. `{"code": ..., "detail": ...}`). Wrap
11/// a plain string as `Value::String(...)` (or use `.into()`) and the
12/// wire bytes stay identical to the old `String`-only shape.
13///
14/// The `type` field is a single-variant `ErrorType` enum that
15/// always serializes to `"error"`. This is what disambiguates the
16/// untagged `Output` enum from a notification — `Output` tries
17/// `Error` first, and the constant `type:"error"` tag is what
18/// rejects every non-error wire shape.
19#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema)]
20#[schemars(rename = "cli.Error")]
21pub struct Error {
22    pub r#type: ErrorType,
23    #[serde(default, skip_serializing_if = "Option::is_none")]
24    #[schemars(extend("omitempty" = true))]
25    pub level: Option<Level>,
26    #[serde(default, skip_serializing_if = "Option::is_none")]
27    #[schemars(extend("omitempty" = true))]
28    pub fatal: Option<bool>,
29    pub message: serde_json::Value,
30}
31
32/// Single-variant discriminator for [`Error`]'s `type` field.
33/// Always `"error"` on the wire.
34#[derive(
35    Serialize,
36    Deserialize,
37    Debug,
38    Clone,
39    Copy,
40    PartialEq,
41    Eq,
42    JsonSchema,
43)]
44#[serde(rename_all = "snake_case")]
45#[schemars(rename = "cli.ErrorType")]
46pub enum ErrorType {
47    Error,
48}
49
50/// Severity matching the conventions used by bunyan / pino / `log` crate
51/// JSON encoders. `fatal` is encoded separately on [`Error`].
52#[derive(
53    Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema,
54)]
55#[serde(rename_all = "lowercase")]
56#[schemars(rename = "cli.Level")]
57pub enum Level {
58    Trace,
59    Debug,
60    Info,
61    Warn,
62    Error,
63}
64
65#[cfg(feature = "mcp")]
66impl crate::cli::command::CommandResponse for Error {
67    fn into_mcp(self) -> crate::cli::command::McpResponseItem {
68        crate::cli::command::McpResponseItem::JSONL(serde_json::to_value(self).unwrap())
69    }
70}
71
72impl std::fmt::Display for Error {
73    /// Render `message` as a plain string when it's a JSON string,
74    /// otherwise as compact JSON. `level` and `fatal` are dropped —
75    /// `Display` is the human-readable message; the metadata lives
76    /// on the struct itself for callers that want it.
77    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78        match &self.message {
79            serde_json::Value::String(s) => f.write_str(s),
80            other => write!(f, "{other}"),
81        }
82    }
83}
84
85impl std::error::Error for Error {}