feat(#2569): DB-backed per-agent todo store + mcp.sock upsert/clear/list/mark-done ops

This commit is contained in:
damocles 2026-07-19 01:15:17 +02:00 committed by mara
commit 6685b33c9d
8 changed files with 505 additions and 0 deletions

View file

@ -269,6 +269,23 @@ pub enum LooseEnd {
#[serde(default)]
summary: String,
},
/// A dynamic, subsystem-pushed todo (loose-ends v2, #2569). Produced
/// by an in-container subsystem (matrix / forge / bash) via
/// `UpsertTodo`. Cleared by that subsystem (`ClearTodo`) or by the
/// agent itself (`MarkTodoDone`, by `id`).
Todo {
id: i64,
/// Producing subsystem marker (`"matrix"`, `"forge"`, `"bash"`, …).
subsystem: String,
/// Optional subsystem-specific key (matrix room id, bash task id).
#[serde(default, skip_serializing_if = "Option::is_none")]
subsystem_key: Option<String>,
summary: String,
/// Optional free-text provenance (room name / task label).
#[serde(default, skip_serializing_if = "Option::is_none")]
source: Option<String>,
age_seconds: u64,
},
}
/// Kind discriminator for `CancelLooseEnd`. Per-kind store +
@ -422,6 +439,38 @@ pub enum Request {
#[serde(default, skip_serializing_if = "Option::is_none")]
agent: Option<String>,
},
/// Upsert a *todo* (loose-ends v2, #2569) from an in-container
/// subsystem (matrix / forge / bash). `subsystem` is the producer
/// marker; `key` is the optional subsystem-specific dedup key (a
/// matrix room id, a bash task id). Re-pushing an identical keyed
/// todo is a no-op; a new-or-changed one coalesces a wake to the
/// agent. Keyless todos always insert as one-offs.
UpsertTodo {
subsystem: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
key: Option<String>,
summary: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
source: Option<String>,
},
/// Clear a producer-resolved todo by `(subsystem, key)`. `key = None`
/// targets the keyless one-off; `all = true` wipes the producer's
/// whole set (cancel-and-recreate on daemon restart).
ClearTodo {
subsystem: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
key: Option<String>,
#[serde(default)]
all: bool,
},
/// List todos, optionally filtered to one `subsystem` (a producer
/// enumerating its own set). `None` = all.
ListTodos {
#[serde(default, skip_serializing_if = "Option::is_none")]
subsystem: Option<String>,
},
/// The agent marks one of its own todos done, by id.
MarkTodoDone { id: i64 },
/// Count of pending (un-delivered) reminders. On the agent socket:
/// same target rules as `GetLooseEnds` (self/children free;
/// non-children require `query_agent_state`; `"*"` rejected).