Skip to main content

objectiveai_sdk/cli/command/agents/wait/
mod.rs

1//! `agents wait` — block until an agent reaches the REQUESTED status:
2//! exactly one of `--inactive` (the agent is done — the original
3//! behavior, now explicit) or `--active` (the agent is up).
4//!
5//! Takes an instance hierarchy or a tag (the `agents enqueue`
6//! selector shape — a plain ref has no live identity to wait on and
7//! errors). The wait is uncapped either way — it blocks until the
8//! target reaches the requested status, however long that takes, and
9//! a target ALREADY in that status returns immediately.
10//!
11//! `--inactive`:
12//! - **Instance**: subscribe to the AIH lock's release; a free lock
13//!   returns immediately.
14//! - **Un-upgraded (GROUPED) tag**: the tag lock's holder is the
15//!   spawn materializing the tag. If nobody holds it, nothing is
16//!   materializing it — return immediately. Otherwise subscribe to
17//!   its release, re-resolve the tag (the spawn flow upgrades
18//!   GROUPED→BOUND strictly before releasing the tag lock, so a
19//!   still-GROUPED tag after release is a systemic invariant
20//!   violation and errors fatally), then fall through to the
21//!   instance wait on the freshly bound hierarchy.
22//!
23//! `--active`:
24//! - **Instance**: resolves when the AIH holds its instance lock —
25//!   the live-registry `Activated` edge; already-held returns
26//!   immediately.
27//! - **Tag**: a BOUND tag waits on its hierarchy as above; a GROUPED
28//!   tag first waits for the tag to BIND (someone spawns it), then
29//!   for the bound agent to be up.
30//!
31//! One use among many: a plugin daemon can gate its actions on its
32//! agent being up by running `agents wait --active` before acting.
33//!
34//! Success is the bare `"Ok"` sentinel either way.
35
36use crate::cli::command::CommandRequest;
37use crate::cli::command::agents::selector::AgentSelector;
38
39#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
40#[schemars(rename = "cli.command.agents.wait.Request")]
41pub struct Request {
42    pub path_type: Path,
43    /// Who to wait on — an instance hierarchy or a tag. A plain ref
44    /// has no live identity and errors.
45    pub agent: AgentSelector,
46    /// The status to wait FOR: `true` resolves when the agent is
47    /// ACTIVE (up — its instance lock held), `false` when it is
48    /// INACTIVE (done — its instance lock free). Defaults to `false`
49    /// on the wire, so a request predating this field keeps its
50    /// original wait-until-done meaning.
51    #[serde(default)]
52    pub active: bool,
53    #[serde(flatten)]
54    pub base: crate::cli::command::RequestBase,
55}
56
57#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
58#[schemars(rename = "cli.command.agents.wait.Path")]
59pub enum Path {
60    #[serde(rename = "agents/wait")]
61    AgentsWait,
62}
63
64impl CommandRequest for Request {
65    fn request_base(&self) -> &crate::cli::command::RequestBase {
66        &self.base
67    }
68
69    fn request_base_mut(&mut self) -> Option<&mut crate::cli::command::RequestBase> {
70        Some(&mut self.base)
71    }
72}
73
74/// Success-only: the wait completed — the target reached the
75/// requested status.
76pub type Response = crate::cli::command::Ok;
77
78#[derive(clap::Args)]
79#[command(group(
80    clap::ArgGroup::new("wait_mode")
81        .required(true)
82        .multiple(false)
83        .args(["active", "inactive"])
84))]
85pub struct Args {
86    #[command(flatten)]
87    pub agent: crate::cli::command::agents::selector::AgentSelectorArgs,
88    /// Resolve when the agent becomes ACTIVE — it is up, holding its
89    /// instance lock. Mutually exclusive with `--inactive`; exactly
90    /// one of the two is required.
91    #[arg(long)]
92    pub active: bool,
93    /// Resolve when the agent is INACTIVE — done, its instance lock
94    /// free (the former default behavior, now explicit). Mutually
95    /// exclusive with `--active`; exactly one of the two is required.
96    #[arg(long)]
97    pub inactive: bool,
98    #[command(flatten)]
99    pub base: crate::cli::command::RequestBaseArgs,
100}
101
102#[derive(clap::Args)]
103#[command(args_conflicts_with_subcommands = true)]
104pub struct Command {
105    #[command(flatten)]
106    pub args: Args,
107    #[command(subcommand)]
108    pub schema: Option<Schema>,
109}
110
111#[derive(clap::Subcommand)]
112pub enum Schema {
113    /// Emit the JSON Schema for this leaf's `Request` type and exit.
114    RequestSchema(request_schema::Args),
115    /// Emit the JSON Schema for this leaf's `Response` type and exit.
116    ResponseSchema(response_schema::Args),
117}
118
119impl TryFrom<Args> for Request {
120    type Error = crate::cli::command::FromArgsError;
121    fn try_from(args: Args) -> Result<Self, Self::Error> {
122        let agent = AgentSelector::try_from(args.agent)?;
123        Ok(Self {
124            path_type: Path::AgentsWait,
125            agent,
126            // The `wait_mode` group guarantees exactly one of
127            // `--active` / `--inactive`, so `active` is the full mode.
128            active: args.active,
129            base: args.base.into(),
130        })
131    }
132}
133
134#[cfg(feature = "cli-executor")]
135pub async fn execute<E: crate::cli::command::CommandExecutor>(
136    executor: &E,
137    mut request: Request,
138
139        agent_arguments: Option<&crate::cli::command::AgentArguments>,
140    ) -> Result<Response, E::Error> {
141    request.base.clear_transform();
142    executor.execute_one(request, agent_arguments).await
143}
144
145#[cfg(feature = "cli-executor")]
146pub async fn execute_transform<E: crate::cli::command::CommandExecutor>(
147    executor: &E,
148    mut request: Request,
149    transform: crate::cli::command::Transform,
150
151        agent_arguments: Option<&crate::cli::command::AgentArguments>,
152    ) -> Result<serde_json::Value, E::Error> {
153    request.base.set_transform(transform);
154    executor.execute_one(request, agent_arguments).await
155}
156
157pub mod request_schema;
158
159pub mod response_schema;
160
161/// One `/listen` broadcast run of `agents wait`: the actual
162/// [`Request`], the producer's
163/// [`AgentArguments`](crate::cli::command::AgentArguments), and the
164/// unary response future. See [`crate::cli::broadcast_listener`].
165#[cfg(feature = "cli-listener")]
166pub struct ListenerExecution {
167    pub request: Request,
168    pub agent_arguments: crate::cli::command::AgentArguments,
169    pub response: crate::cli::broadcast_listener::UnaryResponse<Response>,
170}