scheduled prompts: fire-now operator + manager surfaces (closes #467)

This commit is contained in:
damocles 2026-05-26 13:43:04 +02:00 committed by Mara
commit 3cd9240594
6 changed files with 251 additions and 1 deletions

View file

@ -358,6 +358,9 @@ async fn dispatch(req: &ManagerRequest, coord: &Arc<Coordinator>) -> ManagerResp
message: format!("list scheduled prompts: {e:#}"),
},
},
ManagerRequest::FireScheduleNow { id } => {
handle_fire_schedule_now(coord, hive_sh4re::MANAGER_AGENT, *id).await
}
ManagerRequest::Ask {
question,
options,
@ -821,10 +824,50 @@ fn handle_cancel_schedule(
}
}
/// Authorize + dispatch a `FireScheduleNow` request from the
/// manager surface. Same ownership rules as `CancelSchedule`:
/// requester can fire its own schedules + any owned by an agent
/// in its subtree. The actual fan-out lives in
/// `scheduled_prompts_worker::fire_now`.
async fn handle_fire_schedule_now(
coord: &Arc<Coordinator>,
requester: &str,
schedule_id: i64,
) -> ManagerResponse {
let schedule = match coord.scheduled_prompts.get(schedule_id) {
Ok(Some(s)) => s,
Ok(None) => {
return ManagerResponse::Err {
message: format!("schedule {schedule_id} not found"),
}
}
Err(e) => {
return ManagerResponse::Err {
message: format!("read schedule {schedule_id}: {e:#}"),
}
}
};
if !cancel_authorized(requester, &schedule.owner) {
return ManagerResponse::Err {
message: format!(
"not authorized: {requester} cannot fire schedule owned by {owner}",
owner = schedule.owner
),
};
}
match crate::scheduled_prompts_worker::fire_now(coord, schedule_id).await {
Ok(_report) => ManagerResponse::Ok,
Err(e) => ManagerResponse::Err {
message: format!("fire schedule {schedule_id} now: {e:#}"),
},
}
}
/// Permission check for `CancelSchedule` on the manager surface.
/// `requester` (always `hm1nd` here) can cancel its own schedules.
/// Sub-agent ownership is delegated to topology — see
/// `crate::topology::is_descendant_of`.
/// `crate::topology::is_descendant_of`. Also reused by
/// `handle_fire_schedule_now` — fire-auth follows the same shape.
fn cancel_authorized(requester: &str, owner: &str) -> bool {
if requester == owner {
return true;