Skip to main content

objectiveai_sdk/cli/command/agents/instances/list/
mod.rs

1//! `agents instances list` — enumerate agent instances under one or
2//! more targets, reporting per-agent aggregates (tags, queued count,
3//! spawn/active timestamps, total logged messages).
4
5use crate::cli::command::CommandRequest;
6
7/// Reuse the shared `--target` enum (`instance=L[,parent=P]`, `tag=T`,
8/// `me`) from `agents logs read all`. Each target resolves to an AIH
9/// whose direct children this command lists.
10pub use super::super::logs::list::Target;
11
12#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
13#[schemars(rename = "cli.command.agents.instances.list.Request")]
14pub struct Request {
15    pub path_type: Path,
16    /// Resolved targets whose direct children are listed. Must be
17    /// empty when `all` is set.
18    pub targets: Vec<Target>,
19    /// List EVERY instance in the state — mutually exclusive with
20    /// `targets`.
21    #[serde(default, skip_serializing_if = "Option::is_none")]
22    #[schemars(extend("omitempty" = true))]
23    pub all: Option<bool>,
24    #[serde(flatten)]
25    pub base: crate::cli::command::RequestBase,
26}
27
28#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
29#[schemars(rename = "cli.command.agents.instances.list.Path")]
30pub enum Path {
31    #[serde(rename = "agents/instances/list")]
32    AgentsInstancesList,
33}
34
35impl CommandRequest for Request {
36    fn request_base(&self) -> &crate::cli::command::RequestBase {
37        &self.base
38    }
39
40    fn request_base_mut(&mut self) -> Option<&mut crate::cli::command::RequestBase> {
41        Some(&mut self.base)
42    }
43}
44
45/// One discovered agent instance under a target. Aggregated from the
46/// `logs.messages`, `message_queue`, and `tags` tiers.
47#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
48#[schemars(rename = "cli.command.agents.instances.list.ResponseItem")]
49pub struct ResponseItem {
50    /// Full hierarchy of this agent instance.
51    pub agent_instance_hierarchy: String,
52    /// Tag names currently bound to this AIH, newest-bound first.
53    pub tags: Vec<String>,
54    /// Active `message_queue` rows targeting this agent — counting
55    /// both direct-AIH rows and rows whose tag is bound to this AIH.
56    pub queued: u64,
57    /// RFC3339 timestamp of the first `logs.messages` row for this
58    /// agent. `None` when the agent has no logs yet (queue-only).
59    #[serde(default, skip_serializing_if = "Option::is_none")]
60    #[schemars(extend("omitempty" = true))]
61    pub created_at: Option<String>,
62    /// RFC3339 timestamp of the most recent `logs.messages` row for
63    /// this agent. `None` when the agent has no logs yet (queue-only).
64    #[serde(default, skip_serializing_if = "Option::is_none")]
65    #[schemars(extend("omitempty" = true))]
66    pub last_active_at: Option<String>,
67    /// Total `logs.messages` rows for this agent over all time.
68    pub logged: u64,
69    /// The agent definition recorded for this AIH — from
70    /// `objectiveai.agent_refs`, with the legacy most-recent-request
71    /// fallback (`lookup_session`). Populated by `agents instances
72    /// get`; `agents instances list` leaves it unset.
73    #[serde(default, skip_serializing_if = "Option::is_none")]
74    #[schemars(extend("omitempty" = true))]
75    pub agent: Option<crate::agent::InlineAgentBaseWithFallbacksOrRemoteCommitOptional>,
76}
77
78#[derive(clap::Args)]
79pub struct Args {
80    /// One or more `--target instance=L[,parent=P]` entries. Also
81    /// accepts `--target tag=T` and `--target me`. Lists the direct
82    /// children of each resolved target.
83    #[arg(long = "target", required_unless_present = "all")]
84    pub targets: Vec<String>,
85    /// List EVERY instance in the state. Mutually exclusive with
86    /// `--target`.
87    #[arg(long = "all", conflicts_with = "targets")]
88    pub all: bool,
89    #[command(flatten)]
90    pub base: crate::cli::command::RequestBaseArgs,
91}
92
93#[derive(clap::Args)]
94#[command(args_conflicts_with_subcommands = true)]
95pub struct Command {
96    #[command(flatten)]
97    pub args: Args,
98    #[command(subcommand)]
99    pub schema: Option<Schema>,
100}
101
102#[derive(clap::Subcommand)]
103pub enum Schema {
104    /// Emit the JSON Schema for this leaf's `Request` type and exit.
105    RequestSchema(request_schema::Args),
106    /// Emit the JSON Schema for this leaf's `Response` type and exit.
107    ResponseSchema(response_schema::Args),
108}
109
110impl TryFrom<Args> for Request {
111    type Error = crate::cli::command::FromArgsError;
112    fn try_from(args: Args) -> Result<Self, Self::Error> {
113        let targets = args
114            .targets
115            .iter()
116            .map(|s| {
117                s.parse::<Target>().map_err(|msg| {
118                    crate::cli::command::FromArgsError::path_parse("target", msg)
119                })
120            })
121            .collect::<Result<Vec<_>, _>>()?;
122        Ok(Self {
123            path_type: Path::AgentsInstancesList,
124            targets,
125            all: args.all.then_some(true),
126            base: args.base.into(),
127        })
128    }
129}
130
131#[cfg(feature = "cli-executor")]
132pub async fn execute<E: crate::cli::command::CommandExecutor>(
133    executor: &E,
134    mut request: Request,
135
136        agent_arguments: Option<&crate::cli::command::AgentArguments>,
137    ) -> Result<E::Stream<ResponseItem>, E::Error> {
138    request.base.clear_transform();
139    executor.execute(request, agent_arguments).await
140}
141
142#[cfg(feature = "cli-executor")]
143pub async fn execute_transform<E: crate::cli::command::CommandExecutor>(
144    executor: &E,
145    mut request: Request,
146    transform: crate::cli::command::Transform,
147
148        agent_arguments: Option<&crate::cli::command::AgentArguments>,
149    ) -> Result<E::Stream<serde_json::Value>, E::Error> {
150    request.base.set_transform(transform);
151    executor.execute(request, agent_arguments).await
152}
153
154#[cfg(feature = "mcp")]
155impl crate::cli::command::CommandResponse for ResponseItem {
156    fn into_mcp(self) -> crate::cli::command::McpResponseItem {
157        crate::cli::command::McpResponseItem::JSONL(serde_json::to_value(self).unwrap())
158    }
159}
160
161pub mod request_schema;
162
163pub mod response_schema;
164
165/// One `/listen` broadcast run of `agents instances list`: the actual
166/// [`Request`], the producer's
167/// [`AgentArguments`](crate::cli::command::AgentArguments), and the
168/// response-item stream. See [`crate::cli::websocket_listener`].
169#[cfg(feature = "cli-listener")]
170pub struct ListenerExecution {
171    pub request: Request,
172    pub agent_arguments: crate::cli::command::AgentArguments,
173    pub response: crate::cli::websocket_listener::ResponseItemStream<ResponseItem>,
174}