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 17 underlying `logs.*` tables collapse into 10 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. **`ToolResponse`** — the per-message tool-response container
14//!    row (`tool_call_id` + index).
15//! 3. **`ResponseToolCalls`** — one assistant tool-call slot
16//!    (`tool_call_id` + `arguments` + indices).
17//! 4. **Content payloads (5)** — `Text` / `Image` / `Audio` /
18//!    `Video` / `File`. The `Text` variant subsumes all text-bearing
19//!    rows (refusal, reasoning, assistant content text, tool
20//!    content text); media variants carry the SDK media type
21//!    directly so MCP rendering routes through the existing
22//!    `ContentBlock` projections.
23
24use crate::agent::completions::message::{File, ImageUrl, InputAudio, VideoUrl};
25use crate::agent::completions::request::AgentCompletionCreateParams;
26use crate::cli::command::CommandRequest;
27use crate::functions::executions::request::FunctionExecutionCreateParams;
28use crate::vector::completions::request::VectorCompletionCreateParams;
29
30#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
31#[schemars(rename = "cli.command.agents.logs.read.id.Request")]
32pub struct Request {
33    pub path_type: Path,
34    pub id: i64,
35    pub jq: Option<String>,
36}
37
38#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
39#[schemars(rename = "cli.command.agents.logs.read.id.Path")]
40pub enum Path {
41    #[serde(rename = "agents/logs/read/id")]
42    AgentsLogsReadId,
43}
44
45impl CommandRequest for Request {
46    fn into_command(&self) -> Vec<String> {
47        let mut argv = vec![
48            "agents".to_string(),
49            "logs".to_string(),
50            "read".to_string(),
51            "id".to_string(),
52            self.id.to_string(),
53        ];
54        if let Some(jq) = &self.jq {
55            argv.push("--jq".to_string());
56            argv.push(jq.clone());
57        }
58        argv
59    }
60}
61
62/// Resolved payload for one `logs.messages."index"`. Tagged by
63/// `type`, snake_case discriminant. The MCP projection in
64/// [`CommandResponse::into_mcp`] hands media variants over as
65/// [`ContentBlock`]s and text as a bare JSON string — matching the
66/// existing `agents queue read id` projection of `RichContentPart`.
67/// The five non-content variants render as JSONL with their full
68/// typed body so callers can introspect request-blob /
69/// tool-response / tool-call metadata.
70///
71/// [`ContentBlock`]: crate::mcp::tool::ContentBlock
72#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
73#[serde(tag = "type", rename_all = "snake_case")]
74#[schemars(rename = "cli.command.agents.logs.read.id.Response")]
75pub enum Response {
76    #[schemars(title = "AgentCompletionRequest")]
77    AgentCompletionRequest {
78        response_id: String,
79        sender_agent_instance_hierarchy: String,
80        body: AgentCompletionCreateParams,
81        created_at: i64,
82    },
83    #[schemars(title = "VectorCompletionRequest")]
84    VectorCompletionRequest {
85        response_id: String,
86        sender_agent_instance_hierarchy: String,
87        body: VectorCompletionCreateParams,
88        created_at: i64,
89    },
90    #[schemars(title = "FunctionExecutionRequest")]
91    FunctionExecutionRequest {
92        response_id: String,
93        sender_agent_instance_hierarchy: String,
94        body: FunctionExecutionCreateParams,
95        created_at: i64,
96    },
97    #[schemars(title = "ToolResponse")]
98    ToolResponse {
99        response_id: String,
100        index: i64,
101        tool_call_id: String,
102    },
103    #[schemars(title = "ResponseToolCalls")]
104    ResponseToolCalls {
105        response_id: String,
106        index: i64,
107        tool_call_index: i64,
108        tool_call_id: String,
109        /// Function name from the openai tool_call payload
110        /// (`tool_calls[i].function.name`).
111        function_name: String,
112        arguments: String,
113    },
114    #[schemars(title = "Text")]
115    Text(String),
116    #[schemars(title = "Image")]
117    Image(ImageUrl),
118    #[schemars(title = "Audio")]
119    Audio(InputAudio),
120    #[schemars(title = "Video")]
121    Video(VideoUrl),
122    #[schemars(title = "File")]
123    File(File),
124}
125
126#[derive(clap::Args)]
127pub struct Args {
128    /// `logs.messages."index"` — the BIGSERIAL position of the
129    /// event in the cross-agent history.
130    pub id: i64,
131    /// jq filter applied to the JSON output.
132    #[arg(long)]
133    pub jq: Option<String>,
134}
135
136#[derive(clap::Args)]
137#[command(args_conflicts_with_subcommands = true)]
138pub struct Command {
139    #[command(flatten)]
140    pub args: Args,
141    #[command(subcommand)]
142    pub schema: Option<Schema>,
143}
144
145#[derive(clap::Subcommand)]
146pub enum Schema {
147    /// Emit the JSON Schema for this leaf's `Request` type and exit.
148    RequestSchema(request_schema::Args),
149    /// Emit the JSON Schema for this leaf's `Response` type and exit.
150    ResponseSchema(response_schema::Args),
151}
152
153impl TryFrom<Args> for Request {
154    type Error = crate::cli::command::FromArgsError;
155    fn try_from(args: Args) -> Result<Self, Self::Error> {
156        Ok(Self {
157            path_type: Path::AgentsLogsReadId,
158            id: args.id,
159            jq: args.jq,
160        })
161    }
162}
163
164#[cfg(feature = "mcp")]
165impl crate::cli::command::CommandResponse for Response {
166    fn into_mcp(self) -> crate::cli::command::McpResponseItem {
167        use crate::cli::command::CommandResponse;
168        match self {
169            // Content payloads delegate to the existing inner-type
170            // projections so they ride MCP exactly the way bare
171            // `RichContentPart` does today.
172            Response::Text(text) => text.into_mcp(),
173            Response::Image(image_url) => image_url.into_mcp(),
174            Response::Audio(input_audio) => input_audio.into_mcp(),
175            Response::Video(video_url) => video_url.into_mcp(),
176            Response::File(file) => file.into_mcp(),
177            // Everything else: the full typed variant rides as JSONL.
178            other => crate::cli::command::McpResponseItem::JSONL(
179                serde_json::to_value(other).unwrap(),
180            ),
181        }
182    }
183}
184
185#[cfg(feature = "cli-executor")]
186pub async fn execute<E: crate::cli::command::CommandExecutor>(
187    executor: &E,
188    mut request: Request,
189
190        agent_arguments: Option<&crate::cli::command::AgentArguments>,
191    ) -> Result<Response, E::Error> {
192    request.jq = None;
193    executor.execute_one(request, agent_arguments).await
194}
195
196#[cfg(feature = "cli-executor")]
197pub async fn execute_jq<E: crate::cli::command::CommandExecutor>(
198    executor: &E,
199    mut request: Request,
200    jq: String,
201
202        agent_arguments: Option<&crate::cli::command::AgentArguments>,
203    ) -> Result<serde_json::Value, E::Error> {
204    request.jq = Some(jq);
205    executor.execute_one(request, agent_arguments).await
206}
207
208pub mod request_schema;
209
210
211pub mod response_schema;