Skip to main content

objectiveai_sdk/cli/command/agents/logs/open/
mod.rs

1//! `agents logs read id` — fetch one logged row by its
2//! `logs.messages."index"` BIGSERIAL. The handler resolves the row's
3//! target table via the `logs.message_table` discriminator and
4//! returns a typed [`Response`] variant carrying that table's
5//! payload — no `serde_json::Value` anywhere in the public shape.
6//!
7//! The underlying `logs.*` tables collapse into 8 variants:
8//!
9//! 1. **Request-blob tiers (3)** — agent / vector / function
10//!    request bodies. The JSONB column round-trips through the
11//!    matching SDK `…CreateParams` type so callers see a proper
12//!    typed object, not a raw blob.
13//! 2. **Content payloads (5)** — `Text` / `Image` / `Audio` /
14//!    `Video` / `File`. The `Text` variant subsumes all text-bearing
15//!    rows (refusal, reasoning, assistant content text, tool content
16//!    text, AND a tool call's `arguments`); media variants carry the
17//!    SDK media type directly so MCP rendering routes through the
18//!    existing `ContentBlock` projections.
19//!
20//! An `assistant_response_tool_calls` row reads back as `Text` (its
21//! `arguments`) — the call's metadata (function_name / tool_call_id /
22//! tool_call_index) is surfaced inline by `agents logs read all`. The
23//! `tool_response` container head is no longer registered in
24//! `logs.messages`, so it is never addressable here.
25
26use crate::agent::completions::message::{File, ImageUrl, InputAudio, VideoUrl};
27use crate::agent::completions::request::AgentCompletionCreateParams;
28use crate::cli::command::CommandRequest;
29use crate::functions::executions::request::FunctionExecutionCreateParams;
30use crate::vector::completions::request::VectorCompletionCreateParams;
31
32#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
33#[schemars(rename = "cli.command.agents.logs.open.Request")]
34pub struct Request {
35    pub path_type: Path,
36    pub id: i64,
37    #[serde(flatten)]
38    pub base: crate::cli::command::RequestBase,
39}
40
41#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
42#[schemars(rename = "cli.command.agents.logs.open.Path")]
43pub enum Path {
44    #[serde(rename = "agents/logs/open")]
45    AgentsLogsOpen,
46}
47
48impl CommandRequest for Request {
49    fn request_base(&self) -> &crate::cli::command::RequestBase {
50        &self.base
51    }
52
53    fn request_base_mut(&mut self) -> Option<&mut crate::cli::command::RequestBase> {
54        Some(&mut self.base)
55    }
56}
57
58/// Resolved payload for one `logs.messages."index"`. Tagged by
59/// `type`, snake_case discriminant. The MCP projection in
60/// [`CommandResponse::into_mcp`] hands media variants over as
61/// [`ContentBlock`]s and text as a bare JSON string — matching the
62/// existing `agents queue read id` projection of `RichContentPart`.
63/// The three request-blob variants render as JSONL with their full
64/// typed `…CreateParams` body so callers can introspect request
65/// metadata.
66///
67/// [`ContentBlock`]: crate::mcp::tool::ContentBlock
68#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
69#[serde(tag = "type", rename_all = "snake_case")]
70#[schemars(rename = "cli.command.agents.logs.open.Response")]
71pub enum Response {
72    #[schemars(title = "AgentCompletionRequest")]
73    AgentCompletionRequest {
74        response_id: String,
75        sender_agent_instance_hierarchy: String,
76        body: AgentCompletionCreateParams,
77        created_at: i64,
78    },
79    #[schemars(title = "VectorCompletionRequest")]
80    VectorCompletionRequest {
81        response_id: String,
82        sender_agent_instance_hierarchy: String,
83        body: VectorCompletionCreateParams,
84        created_at: i64,
85    },
86    #[schemars(title = "FunctionExecutionRequest")]
87    FunctionExecutionRequest {
88        response_id: String,
89        sender_agent_instance_hierarchy: String,
90        body: FunctionExecutionCreateParams,
91        created_at: i64,
92    },
93    #[schemars(title = "Text")]
94    Text { text: String },
95    #[schemars(title = "Image")]
96    Image(ImageUrl),
97    #[schemars(title = "Audio")]
98    Audio(InputAudio),
99    #[schemars(title = "Video")]
100    Video(VideoUrl),
101    #[schemars(title = "File")]
102    File(File),
103}
104
105#[derive(clap::Args)]
106#[command(group(clap::ArgGroup::new("id_required").required(true).args(["id"])))]
107pub struct Args {
108    /// `logs.messages."index"` — the BIGSERIAL position of the
109    /// event in the cross-agent history.
110    #[arg(long)]
111    pub id: Option<i64>,
112    #[command(flatten)]
113    pub base: crate::cli::command::RequestBaseArgs,
114}
115
116#[derive(clap::Args)]
117#[command(args_conflicts_with_subcommands = true)]
118pub struct Command {
119    #[command(flatten)]
120    pub args: Args,
121    #[command(subcommand)]
122    pub schema: Option<Schema>,
123}
124
125#[derive(clap::Subcommand)]
126pub enum Schema {
127    /// Emit the JSON Schema for this leaf's `Request` type and exit.
128    RequestSchema(request_schema::Args),
129    /// Emit the JSON Schema for this leaf's `Response` type and exit.
130    ResponseSchema(response_schema::Args),
131}
132
133impl TryFrom<Args> for Request {
134    type Error = crate::cli::command::FromArgsError;
135    fn try_from(args: Args) -> Result<Self, Self::Error> {
136        Ok(Self {
137            path_type: Path::AgentsLogsOpen,
138            id: args.id.ok_or_else(|| {
139                crate::cli::command::FromArgsError::path_parse(
140                    "id",
141                    "--id is required".to_string(),
142                )
143            })?,
144            base: args.base.into(),
145        })
146    }
147}
148
149#[cfg(feature = "mcp")]
150impl crate::cli::command::CommandResponse for Response {
151    fn into_mcp(self) -> crate::cli::command::McpResponseItem {
152        use crate::cli::command::CommandResponse;
153        match self {
154            // Content payloads delegate to the existing inner-type
155            // projections so they ride MCP exactly the way bare
156            // `RichContentPart` does today.
157            Response::Text { text } => text.into_mcp(),
158            Response::Image(image_url) => image_url.into_mcp(),
159            Response::Audio(input_audio) => input_audio.into_mcp(),
160            Response::Video(video_url) => video_url.into_mcp(),
161            Response::File(file) => file.into_mcp(),
162            // Everything else: the full typed variant rides as JSONL.
163            other => crate::cli::command::McpResponseItem::JSONL(
164                serde_json::to_value(other).unwrap(),
165            ),
166        }
167    }
168}
169
170#[cfg(feature = "cli-executor")]
171pub async fn execute<E: crate::cli::command::CommandExecutor>(
172    executor: &E,
173    mut request: Request,
174
175        agent_arguments: Option<&crate::cli::command::AgentArguments>,
176    ) -> Result<Response, E::Error> {
177    request.base.clear_transform();
178    executor.execute_one(request, agent_arguments).await
179}
180
181#[cfg(feature = "cli-executor")]
182pub async fn execute_transform<E: crate::cli::command::CommandExecutor>(
183    executor: &E,
184    mut request: Request,
185    transform: crate::cli::command::Transform,
186
187        agent_arguments: Option<&crate::cli::command::AgentArguments>,
188    ) -> Result<serde_json::Value, E::Error> {
189    request.base.set_transform(transform);
190    executor.execute_one(request, agent_arguments).await
191}
192
193pub mod request_schema;
194
195
196pub mod response_schema;