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    fn request_base(&self) -> &crate::cli::command::RequestBase {
145        match self {
146            Request::Schedule(inner) => inner.request_base(),
147            Request::ScheduleRequestSchema(inner) => inner.request_base(),
148            Request::ScheduleResponseSchema(inner) => inner.request_base(),
149            Request::List(inner) => inner.request_base(),
150            Request::ListRequestSchema(inner) => inner.request_base(),
151            Request::ListResponseSchema(inner) => inner.request_base(),
152            Request::Run(inner) => inner.request_base(),
153            Request::RunRequestSchema(inner) => inner.request_base(),
154            Request::RunResponseSchema(inner) => inner.request_base(),
155        }
156    }
157
158    fn request_base_mut(&mut self) -> Option<&mut crate::cli::command::RequestBase> {
159        match self {
160            Request::Schedule(inner) => inner.request_base_mut(),
161            Request::ScheduleRequestSchema(inner) => inner.request_base_mut(),
162            Request::ScheduleResponseSchema(inner) => inner.request_base_mut(),
163            Request::List(inner) => inner.request_base_mut(),
164            Request::ListRequestSchema(inner) => inner.request_base_mut(),
165            Request::ListResponseSchema(inner) => inner.request_base_mut(),
166            Request::Run(inner) => inner.request_base_mut(),
167            Request::RunRequestSchema(inner) => inner.request_base_mut(),
168            Request::RunResponseSchema(inner) => inner.request_base_mut(),
169        }
170    }
171}
172
173#[cfg(feature = "cli-executor")]
174pub async fn execute<E: crate::cli::command::CommandExecutor>(
175    executor: &E,
176    request: Request,
177    agent_arguments: Option<&crate::cli::command::AgentArguments>,
178) -> Result<
179    std::pin::Pin<Box<dyn futures::Stream<Item = Result<ResponseItem, E::Error>> + Send>>,
180    E::Error,
181> {
182    let stream: std::pin::Pin<
183        Box<dyn futures::Stream<Item = Result<ResponseItem, E::Error>> + Send>,
184    > = match request {
185        Request::Schedule(req) => {
186            let value = schedule::execute(executor, req, agent_arguments).await?;
187            Box::pin(crate::cli::command::StreamOnce::new(Ok(ResponseItem::Schedule(value))))
188        }
189        Request::ScheduleRequestSchema(req) => {
190            let value =
191                schedule::request_schema::execute(executor, req, agent_arguments).await?;
192            Box::pin(crate::cli::command::StreamOnce::new(Ok(
193                ResponseItem::ScheduleRequestSchema(value),
194            )))
195        }
196        Request::ScheduleResponseSchema(req) => {
197            let value =
198                schedule::response_schema::execute(executor, req, agent_arguments).await?;
199            Box::pin(crate::cli::command::StreamOnce::new(Ok(
200                ResponseItem::ScheduleResponseSchema(value),
201            )))
202        }
203        Request::List(req) => {
204            use futures::StreamExt;
205            let inner = list::execute(executor, req, agent_arguments).await?;
206            Box::pin(inner.map(|r| r.map(ResponseItem::List)))
207        }
208        Request::ListRequestSchema(req) => {
209            let value =
210                list::request_schema::execute(executor, req, agent_arguments).await?;
211            Box::pin(crate::cli::command::StreamOnce::new(Ok(
212                ResponseItem::ListRequestSchema(value),
213            )))
214        }
215        Request::ListResponseSchema(req) => {
216            let value =
217                list::response_schema::execute(executor, req, agent_arguments).await?;
218            Box::pin(crate::cli::command::StreamOnce::new(Ok(
219                ResponseItem::ListResponseSchema(value),
220            )))
221        }
222        Request::Run(req) => {
223            use futures::StreamExt;
224            let inner = run::execute(executor, req, agent_arguments).await?;
225            Box::pin(inner.map(|r| r.map(ResponseItem::Run)))
226        }
227        Request::RunRequestSchema(req) => {
228            let value =
229                run::request_schema::execute(executor, req, agent_arguments).await?;
230            Box::pin(crate::cli::command::StreamOnce::new(Ok(
231                ResponseItem::RunRequestSchema(value),
232            )))
233        }
234        Request::RunResponseSchema(req) => {
235            let value =
236                run::response_schema::execute(executor, req, agent_arguments).await?;
237            Box::pin(crate::cli::command::StreamOnce::new(Ok(
238                ResponseItem::RunResponseSchema(value),
239            )))
240        }
241    };
242    Ok(stream)
243}
244
245#[cfg(feature = "cli-executor")]
246pub async fn execute_transform<E: crate::cli::command::CommandExecutor>(
247    executor: &E,
248    request: Request,
249    transform: crate::cli::command::Transform,
250    agent_arguments: Option<&crate::cli::command::AgentArguments>,
251) -> Result<
252    std::pin::Pin<Box<dyn futures::Stream<Item = Result<serde_json::Value, E::Error>> + Send>>,
253    E::Error,
254> {
255    let stream: std::pin::Pin<
256        Box<dyn futures::Stream<Item = Result<serde_json::Value, E::Error>> + Send>,
257    > = match request {
258        Request::Schedule(req) => {
259            let value = schedule::execute_transform(executor, req, transform, agent_arguments).await?;
260            Box::pin(crate::cli::command::StreamOnce::new(Ok(value)))
261        }
262        Request::ScheduleRequestSchema(req) => {
263            let value =
264                schedule::request_schema::execute_transform(executor, req, transform, agent_arguments).await?;
265            Box::pin(crate::cli::command::StreamOnce::new(Ok(value)))
266        }
267        Request::ScheduleResponseSchema(req) => {
268            let value =
269                schedule::response_schema::execute_transform(executor, req, transform, agent_arguments).await?;
270            Box::pin(crate::cli::command::StreamOnce::new(Ok(value)))
271        }
272        Request::List(req) => {
273            let inner = list::execute_transform(executor, req, transform, agent_arguments).await?;
274            Box::pin(inner)
275        }
276        Request::ListRequestSchema(req) => {
277            let value =
278                list::request_schema::execute_transform(executor, req, transform, agent_arguments).await?;
279            Box::pin(crate::cli::command::StreamOnce::new(Ok(value)))
280        }
281        Request::ListResponseSchema(req) => {
282            let value =
283                list::response_schema::execute_transform(executor, req, transform, agent_arguments).await?;
284            Box::pin(crate::cli::command::StreamOnce::new(Ok(value)))
285        }
286        Request::Run(req) => {
287            let inner = run::execute_transform(executor, req, transform, agent_arguments).await?;
288            Box::pin(inner)
289        }
290        Request::RunRequestSchema(req) => {
291            let value =
292                run::request_schema::execute_transform(executor, req, transform, agent_arguments).await?;
293            Box::pin(crate::cli::command::StreamOnce::new(Ok(value)))
294        }
295        Request::RunResponseSchema(req) => {
296            let value =
297                run::response_schema::execute_transform(executor, req, transform, agent_arguments).await?;
298            Box::pin(crate::cli::command::StreamOnce::new(Ok(value)))
299        }
300    };
301    Ok(stream)
302}