Skip to main content

objectiveai_sdk/cli/command/tasks/
mod.rs

1//! `agents tasks` — task store + (eventual) runner.
2//!
3//! Two leaves today:
4//! - `schedule` — register a command + interval in `tasks.sqlite`.
5//! - `list` — inspect rows with filters on kind / readiness /
6//!   hierarchy + depth + pagination.
7//!
8//! The runner that fires schedules is follow-up work (#216).
9
10use crate::cli::command::CommandRequest;
11
12pub mod list;
13pub mod run;
14pub mod schedule;
15
16#[derive(clap::Subcommand)]
17pub enum Command {
18    /// Register a command + interval in `tasks.sqlite`.
19    Schedule(schedule::Command),
20    /// Inspect rows in `tasks.sqlite` with optional filters.
21    List(list::Command),
22    /// Fire every pending schedule in scope, in parallel.
23    Run(run::Command),
24}
25
26#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
27#[serde(untagged)]
28#[schemars(rename = "cli.command.tasks.Request")]
29pub enum Request {
30    #[schemars(title = "Schedule")]
31    Schedule(schedule::Request),
32    #[schemars(title = "ScheduleRequestSchema")]
33    ScheduleRequestSchema(schedule::request_schema::Request),
34    #[schemars(title = "ScheduleResponseSchema")]
35    ScheduleResponseSchema(schedule::response_schema::Request),
36    #[schemars(title = "List")]
37    List(list::Request),
38    #[schemars(title = "ListRequestSchema")]
39    ListRequestSchema(list::request_schema::Request),
40    #[schemars(title = "ListResponseSchema")]
41    ListResponseSchema(list::response_schema::Request),
42    #[schemars(title = "Run")]
43    Run(run::Request),
44    #[schemars(title = "RunRequestSchema")]
45    RunRequestSchema(run::request_schema::Request),
46    #[schemars(title = "RunResponseSchema")]
47    RunResponseSchema(run::response_schema::Request),
48}
49
50// Exempt from json-schema coverage: tier aggregate (see the root
51// `ResponseItem` in command.rs - TS7056).
52#[objectiveai_sdk_macros::json_schema_ignore]
53#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
54#[schemars(rename = "cli.command.tasks.ResponseItem")]
55#[serde(untagged)]
56pub enum ResponseItem {
57    #[schemars(title = "Schedule")]
58    Schedule(schedule::Response),
59    #[schemars(title = "ScheduleRequestSchema")]
60    ScheduleRequestSchema(schedule::request_schema::Response),
61    #[schemars(title = "ScheduleResponseSchema")]
62    ScheduleResponseSchema(schedule::response_schema::Response),
63    #[schemars(title = "List")]
64    List(list::ResponseItem),
65    #[schemars(title = "ListRequestSchema")]
66    ListRequestSchema(list::request_schema::Response),
67    #[schemars(title = "ListResponseSchema")]
68    ListResponseSchema(list::response_schema::Response),
69    #[schemars(title = "Run")]
70    Run(run::ResponseItem),
71    #[schemars(title = "RunRequestSchema")]
72    RunRequestSchema(run::request_schema::Response),
73    #[schemars(title = "RunResponseSchema")]
74    RunResponseSchema(run::response_schema::Response),
75}
76
77#[cfg(feature = "mcp")]
78impl crate::cli::command::CommandResponse for ResponseItem {
79    fn into_mcp(self) -> crate::cli::command::McpResponseItem {
80        match self {
81            ResponseItem::Schedule(v) => v.into_mcp(),
82            ResponseItem::ScheduleRequestSchema(v) => v.into_mcp(),
83            ResponseItem::ScheduleResponseSchema(v) => v.into_mcp(),
84            ResponseItem::List(v) => v.into_mcp(),
85            ResponseItem::ListRequestSchema(v) => v.into_mcp(),
86            ResponseItem::ListResponseSchema(v) => v.into_mcp(),
87            ResponseItem::Run(v) => v.into_mcp(),
88            ResponseItem::RunRequestSchema(v) => v.into_mcp(),
89            ResponseItem::RunResponseSchema(v) => v.into_mcp(),
90        }
91    }
92}
93
94impl TryFrom<Command> for Request {
95    type Error = crate::cli::command::FromArgsError;
96    fn try_from(command: Command) -> Result<Self, Self::Error> {
97        match command {
98            Command::Schedule(cmd) => match cmd.schema {
99                None => Ok(Request::Schedule(schedule::Request::try_from(cmd.args)?)),
100                Some(schedule::Schema::RequestSchema(args)) => Ok(
101                    Request::ScheduleRequestSchema(schedule::request_schema::Request::try_from(args)?),
102                ),
103                Some(schedule::Schema::ResponseSchema(args)) => Ok(
104                    Request::ScheduleResponseSchema(schedule::response_schema::Request::try_from(args)?),
105                ),
106            },
107            Command::List(cmd) => match cmd.schema {
108                None => Ok(Request::List(list::Request::try_from(cmd.args)?)),
109                Some(list::Schema::RequestSchema(args)) => Ok(
110                    Request::ListRequestSchema(list::request_schema::Request::try_from(args)?),
111                ),
112                Some(list::Schema::ResponseSchema(args)) => Ok(
113                    Request::ListResponseSchema(list::response_schema::Request::try_from(args)?),
114                ),
115            },
116            Command::Run(cmd) => match cmd.schema {
117                None => Ok(Request::Run(run::Request::try_from(cmd.args)?)),
118                Some(run::Schema::RequestSchema(args)) => Ok(
119                    Request::RunRequestSchema(run::request_schema::Request::try_from(args)?),
120                ),
121                Some(run::Schema::ResponseSchema(args)) => Ok(
122                    Request::RunResponseSchema(run::response_schema::Request::try_from(args)?),
123                ),
124            },
125        }
126    }
127}
128
129impl CommandRequest for Request {
130    fn into_command(&self) -> Vec<String> {
131        match self {
132            Request::Schedule(inner) => inner.into_command(),
133            Request::ScheduleRequestSchema(inner) => inner.into_command(),
134            Request::ScheduleResponseSchema(inner) => inner.into_command(),
135            Request::List(inner) => inner.into_command(),
136            Request::ListRequestSchema(inner) => inner.into_command(),
137            Request::ListResponseSchema(inner) => inner.into_command(),
138            Request::Run(inner) => inner.into_command(),
139            Request::RunRequestSchema(inner) => inner.into_command(),
140            Request::RunResponseSchema(inner) => inner.into_command(),
141        }
142    }
143}
144
145#[cfg(feature = "cli-executor")]
146pub async fn execute<E: crate::cli::command::CommandExecutor>(
147    executor: &E,
148    request: Request,
149    agent_arguments: Option<&crate::cli::command::AgentArguments>,
150) -> Result<
151    std::pin::Pin<Box<dyn futures::Stream<Item = Result<ResponseItem, E::Error>> + Send>>,
152    E::Error,
153> {
154    let stream: std::pin::Pin<
155        Box<dyn futures::Stream<Item = Result<ResponseItem, E::Error>> + Send>,
156    > = match request {
157        Request::Schedule(req) => {
158            let value = schedule::execute(executor, req, agent_arguments).await?;
159            Box::pin(crate::cli::command::StreamOnce::new(Ok(ResponseItem::Schedule(value))))
160        }
161        Request::ScheduleRequestSchema(req) => {
162            let value =
163                schedule::request_schema::execute(executor, req, agent_arguments).await?;
164            Box::pin(crate::cli::command::StreamOnce::new(Ok(
165                ResponseItem::ScheduleRequestSchema(value),
166            )))
167        }
168        Request::ScheduleResponseSchema(req) => {
169            let value =
170                schedule::response_schema::execute(executor, req, agent_arguments).await?;
171            Box::pin(crate::cli::command::StreamOnce::new(Ok(
172                ResponseItem::ScheduleResponseSchema(value),
173            )))
174        }
175        Request::List(req) => {
176            use futures::StreamExt;
177            let inner = list::execute(executor, req, agent_arguments).await?;
178            Box::pin(inner.map(|r| r.map(ResponseItem::List)))
179        }
180        Request::ListRequestSchema(req) => {
181            let value =
182                list::request_schema::execute(executor, req, agent_arguments).await?;
183            Box::pin(crate::cli::command::StreamOnce::new(Ok(
184                ResponseItem::ListRequestSchema(value),
185            )))
186        }
187        Request::ListResponseSchema(req) => {
188            let value =
189                list::response_schema::execute(executor, req, agent_arguments).await?;
190            Box::pin(crate::cli::command::StreamOnce::new(Ok(
191                ResponseItem::ListResponseSchema(value),
192            )))
193        }
194        Request::Run(req) => {
195            use futures::StreamExt;
196            let inner = run::execute(executor, req, agent_arguments).await?;
197            Box::pin(inner.map(|r| r.map(ResponseItem::Run)))
198        }
199        Request::RunRequestSchema(req) => {
200            let value =
201                run::request_schema::execute(executor, req, agent_arguments).await?;
202            Box::pin(crate::cli::command::StreamOnce::new(Ok(
203                ResponseItem::RunRequestSchema(value),
204            )))
205        }
206        Request::RunResponseSchema(req) => {
207            let value =
208                run::response_schema::execute(executor, req, agent_arguments).await?;
209            Box::pin(crate::cli::command::StreamOnce::new(Ok(
210                ResponseItem::RunResponseSchema(value),
211            )))
212        }
213    };
214    Ok(stream)
215}
216
217#[cfg(feature = "cli-executor")]
218pub async fn execute_jq<E: crate::cli::command::CommandExecutor>(
219    executor: &E,
220    request: Request,
221    jq: String,
222    agent_arguments: Option<&crate::cli::command::AgentArguments>,
223) -> Result<
224    std::pin::Pin<Box<dyn futures::Stream<Item = Result<serde_json::Value, E::Error>> + Send>>,
225    E::Error,
226> {
227    let stream: std::pin::Pin<
228        Box<dyn futures::Stream<Item = Result<serde_json::Value, E::Error>> + Send>,
229    > = match request {
230        Request::Schedule(req) => {
231            let value = schedule::execute_jq(executor, req, jq, agent_arguments).await?;
232            Box::pin(crate::cli::command::StreamOnce::new(Ok(value)))
233        }
234        Request::ScheduleRequestSchema(req) => {
235            let value =
236                schedule::request_schema::execute_jq(executor, req, jq, agent_arguments).await?;
237            Box::pin(crate::cli::command::StreamOnce::new(Ok(value)))
238        }
239        Request::ScheduleResponseSchema(req) => {
240            let value =
241                schedule::response_schema::execute_jq(executor, req, jq, agent_arguments).await?;
242            Box::pin(crate::cli::command::StreamOnce::new(Ok(value)))
243        }
244        Request::List(req) => {
245            let inner = list::execute_jq(executor, req, jq, agent_arguments).await?;
246            Box::pin(inner)
247        }
248        Request::ListRequestSchema(req) => {
249            let value =
250                list::request_schema::execute_jq(executor, req, jq, agent_arguments).await?;
251            Box::pin(crate::cli::command::StreamOnce::new(Ok(value)))
252        }
253        Request::ListResponseSchema(req) => {
254            let value =
255                list::response_schema::execute_jq(executor, req, jq, agent_arguments).await?;
256            Box::pin(crate::cli::command::StreamOnce::new(Ok(value)))
257        }
258        Request::Run(req) => {
259            let inner = run::execute_jq(executor, req, jq, agent_arguments).await?;
260            Box::pin(inner)
261        }
262        Request::RunRequestSchema(req) => {
263            let value =
264                run::request_schema::execute_jq(executor, req, jq, agent_arguments).await?;
265            Box::pin(crate::cli::command::StreamOnce::new(Ok(value)))
266        }
267        Request::RunResponseSchema(req) => {
268            let value =
269                run::response_schema::execute_jq(executor, req, jq, agent_arguments).await?;
270            Box::pin(crate::cli::command::StreamOnce::new(Ok(value)))
271        }
272    };
273    Ok(stream)
274}