Skip to main content

objectiveai_sdk/cli/command/agents/logs/read/id/
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.read.id.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.read.id.Path")]
43pub enum Path {
44    #[serde(rename = "agents/logs/read/id")]
45    AgentsLogsReadId,
46}
47
48impl CommandRequest for Request {
49    fn into_command(&self) -> Vec<String> {
50        let mut argv = vec![
51            "agents".to_string(),
52            "logs".to_string(),
53            "read".to_string(),
54            "id".to_string(),
55            self.id.to_string(),
56        ];
57        self.base.push_flags(&mut argv);
58        argv
59    }
60
61    fn request_base(&self) -> &crate::cli::command::RequestBase {
62        &self.base
63    }
64
65    fn request_base_mut(&mut self) -> Option<&mut crate::cli::command::RequestBase> {
66        Some(&mut self.base)
67    }
68}
69
70/// Resolved payload for one `logs.messages."index"`. Tagged by
71/// `type`, snake_case discriminant. The MCP projection in
72/// [`CommandResponse::into_mcp`] hands media variants over as
73/// [`ContentBlock`]s and text as a bare JSON string — matching the
74/// existing `agents queue read id` projection of `RichContentPart`.
75/// The three request-blob variants render as JSONL with their full
76/// typed `…CreateParams` body so callers can introspect request
77/// metadata.
78///
79/// [`ContentBlock`]: crate::mcp::tool::ContentBlock
80#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
81#[serde(tag = "type", rename_all = "snake_case")]
82#[schemars(rename = "cli.command.agents.logs.read.id.Response")]
83pub enum Response {
84    #[schemars(title = "AgentCompletionRequest")]
85    AgentCompletionRequest {
86        response_id: String,
87        sender_agent_instance_hierarchy: String,
88        body: AgentCompletionCreateParams,
89        created_at: i64,
90    },
91    #[schemars(title = "VectorCompletionRequest")]
92    VectorCompletionRequest {
93        response_id: String,
94        sender_agent_instance_hierarchy: String,
95        body: VectorCompletionCreateParams,
96        created_at: i64,
97    },
98    #[schemars(title = "FunctionExecutionRequest")]
99    FunctionExecutionRequest {
100        response_id: String,
101        sender_agent_instance_hierarchy: String,
102        body: FunctionExecutionCreateParams,
103        created_at: i64,
104    },
105    #[schemars(title = "Text")]
106    Text { text: String },
107    #[schemars(title = "Image")]
108    Image(ImageUrl),
109    #[schemars(title = "Audio")]
110    Audio(InputAudio),
111    #[schemars(title = "Video")]
112    Video(VideoUrl),
113    #[schemars(title = "File")]
114    File(File),
115}
116
117#[derive(clap::Args)]
118pub struct Args {
119    /// `logs.messages."index"` — the BIGSERIAL position of the
120    /// event in the cross-agent history.
121    pub id: i64,
122    #[command(flatten)]
123    pub base: crate::cli::command::RequestBaseArgs,
124}
125
126#[derive(clap::Args)]
127#[command(args_conflicts_with_subcommands = true)]
128pub struct Command {
129    #[command(flatten)]
130    pub args: Args,
131    #[command(subcommand)]
132    pub schema: Option<Schema>,
133}
134
135#[derive(clap::Subcommand)]
136pub enum Schema {
137    /// Emit the JSON Schema for this leaf's `Request` type and exit.
138    RequestSchema(request_schema::Args),
139    /// Emit the JSON Schema for this leaf's `Response` type and exit.
140    ResponseSchema(response_schema::Args),
141}
142
143impl TryFrom<Args> for Request {
144    type Error = crate::cli::command::FromArgsError;
145    fn try_from(args: Args) -> Result<Self, Self::Error> {
146        Ok(Self {
147            path_type: Path::AgentsLogsReadId,
148            id: args.id,
149            base: args.base.into(),
150        })
151    }
152}
153
154#[cfg(feature = "mcp")]
155impl crate::cli::command::CommandResponse for Response {
156    fn into_mcp(self) -> crate::cli::command::McpResponseItem {
157        use crate::cli::command::CommandResponse;
158        match self {
159            // Content payloads delegate to the existing inner-type
160            // projections so they ride MCP exactly the way bare
161            // `RichContentPart` does today.
162            Response::Text { text } => text.into_mcp(),
163            Response::Image(image_url) => image_url.into_mcp(),
164            Response::Audio(input_audio) => input_audio.into_mcp(),
165            Response::Video(video_url) => video_url.into_mcp(),
166            Response::File(file) => file.into_mcp(),
167            // Everything else: the full typed variant rides as JSONL.
168            other => crate::cli::command::McpResponseItem::JSONL(
169                serde_json::to_value(other).unwrap(),
170            ),
171        }
172    }
173}
174
175#[cfg(feature = "cli-executor")]
176pub async fn execute<E: crate::cli::command::CommandExecutor>(
177    executor: &E,
178    mut request: Request,
179
180        agent_arguments: Option<&crate::cli::command::AgentArguments>,
181    ) -> Result<Response, E::Error> {
182    request.base.clear_transform();
183    executor.execute_one(request, agent_arguments).await
184}
185
186#[cfg(feature = "cli-executor")]
187pub async fn execute_transform<E: crate::cli::command::CommandExecutor>(
188    executor: &E,
189    mut request: Request,
190    transform: crate::cli::command::Transform,
191
192        agent_arguments: Option<&crate::cli::command::AgentArguments>,
193    ) -> Result<serde_json::Value, E::Error> {
194    request.base.set_transform(transform);
195    executor.execute_one(request, agent_arguments).await
196}
197
198pub mod request_schema;
199
200
201pub mod response_schema;