cancel_thread: new mcp tool — unify reminder + question cancel on both surfaces

This commit is contained in:
damocles 2026-05-18 18:07:44 +02:00
parent fcd407da11
commit b1d0a62cb9
11 changed files with 331 additions and 25 deletions

View file

@ -191,6 +191,11 @@ pub enum ReminderTiming {
/// caller (claude in the agent harness) is expected to render these
/// as a short bulleted list — the per-row fields are all the context
/// needed without a follow-up fetch.
///
/// `Question` and `Reminder` rows are cancellable via the
/// `CancelThread` request (and the `cancel_thread` MCP tool);
/// `Approval` rows are not (operator approves/denies via the
/// dashboard, manager has no withdraw path today).
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum OpenThread {
@ -223,6 +228,33 @@ pub enum OpenThread {
/// Wall-clock seconds since `asked_at`. Saturates at zero.
age_seconds: u64,
},
/// A scheduled but un-delivered reminder. For agent-flavour calls:
/// only the agent's own reminders (where `owner == self`). For
/// manager-flavour calls: every pending reminder in the swarm.
/// `owner` is the agent who scheduled it; `due_at` is the absolute
/// unix timestamp the scheduler is targeting.
Reminder {
id: i64,
owner: String,
message: String,
due_at: i64,
/// Wall-clock seconds since the reminder was scheduled. Saturates
/// at zero on clock anomalies. (For time-until-fire, compute
/// `due_at - now` client-side from the wire timestamp.)
age_seconds: u64,
},
}
/// Kind discriminator for `CancelThread`. Maps to which underlying
/// store the dispatcher reaches into (`OperatorQuestions` vs
/// `Broker::reminders`). Approvals are deliberately not cancellable
/// — the operator approves/denies via the dashboard, manager has no
/// withdraw path today.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum CancelThreadKind {
Question,
Reminder,
}
/// Requests on a per-agent socket. The agent's identity is the socket
@ -319,6 +351,13 @@ pub enum AgentRequest {
/// identity after a rename or session-continue boundary where the
/// system-prompt-substituted label is no longer reliable.
Whoami,
/// Cancel an open thread the agent owns: a `Question` they asked
/// (returns `[cancelled by <self>]` as the answer to the asker)
/// or a `Reminder` they scheduled (hard-deletes the row).
/// Authorisation on the sub-agent surface: caller must own the
/// row. The manager surface uses the same wire variant but
/// accepts any id.
CancelThread { kind: CancelThreadKind, id: i64 },
}
/// Responses on a per-agent socket.
@ -625,6 +664,10 @@ pub enum ManagerRequest {
/// Manager-flavour self-introspection. Same wire shape as
/// `AgentRequest::Whoami`, but `role` is always `"manager"`.
Whoami,
/// Cancel an open thread (question or reminder). Manager surface
/// can cancel any row (no owner check) — same dispatch as
/// `AgentRequest::CancelThread` but with privileged auth.
CancelThread { kind: CancelThreadKind, id: i64 },
}
#[derive(Debug, Clone, Serialize, Deserialize)]