objectiveai_cli/db/agent_continuations.rs
1//! Latest-continuation registry keyed by `agent_instance_hierarchy`.
2//!
3//! Single function: [`upsert`]. Called by the chunk-yielder loops
4//! (`agents spawn`'s `run_multi_pass` and `functions execute`'s
5//! `runner::run`) before each chunk yield. The row holds whichever
6//! continuation was most recently observed for that AIH; the
7//! `ON CONFLICT DO UPDATE` clause keeps it idempotent against
8//! repeated upserts and overwrites stale rows from prior runs.
9//!
10//! No read API today — this is write-only scaffolding for future
11//! consumers (resume-from-continuation flows, etc.).
12
13use super::{Error, Pool};
14
15fn now_seconds() -> i64 {
16 use std::time::{SystemTime, UNIX_EPOCH};
17 SystemTime::now()
18 .duration_since(UNIX_EPOCH)
19 .map(|d| d.as_secs() as i64)
20 .unwrap_or(0)
21}
22
23/// Insert-or-replace the continuation for an AIH. Matches the
24/// `INSERT … ON CONFLICT … DO UPDATE` idiom used by `tags::apply`.
25pub async fn upsert(
26 pool: &Pool,
27 agent_instance_hierarchy: &str,
28 continuation: &str,
29) -> Result<(), Error> {
30 sqlx::query(
31 "INSERT INTO objectiveai.agent_continuations \
32 (agent_instance_hierarchy, continuation, updated_at) \
33 VALUES ($1, $2, $3) \
34 ON CONFLICT (agent_instance_hierarchy) DO UPDATE SET \
35 continuation = EXCLUDED.continuation, \
36 updated_at = EXCLUDED.updated_at",
37 )
38 .bind(agent_instance_hierarchy)
39 .bind(continuation)
40 .bind(now_seconds())
41 .execute(&**pool)
42 .await?;
43 Ok(())
44}