Approval mode of automations in the list; Telegram switch per chat

- Automations: the approval mode is a selection in the list; PATCH /jobs/{id}
  writes permission_mode into the job file's frontmatter (rest of the file
  unchanged, invalid modes rejected). "Allow everything" lets nightly runs work
  without waiting for approvals via Telegram until the timeout.
- Session.telegram_quiet: approvals and questions of a chat (and its subagents)
  are not sent to Telegram; the chat open in Telegram always reports. Toggle in
  the chat's ⋯ menu when a bot is paired. New chats from the web UI and
  automation runs start quiet; chats started from Telegram and existing chats
  keep reporting.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-10 08:24:34 +01:00
1 parent 9339e2db41
commit bd490efda5
13 files changed
+148 -2

No files matched your search

+13
View File
@@ -620,6 +620,8 @@ async fn create_session(Extension(user): Extension<User>, State(app): State<AppS
archived: false,
folder: None,
instructions: String::new(),
// New chats keep their approvals and questions out of Telegram (⋯ menu turns it on).
telegram_quiet: true,
};
let s = app.save_session(s)?;
Ok(Json(json!(s)))
@@ -831,6 +833,8 @@ struct UpdateSession {
folder: Option<String>,
/// Instructions for this chat ("" clears them).
instructions: Option<String>,
/// Approvals and questions of this chat are not sent to Telegram.
telegram_quiet: Option<bool>,
}
async fn update_session(Extension(user): Extension<User>, State(app): State<AppState>, Path(id): Path<String>, Json(body): Json<UpdateSession>) -> ApiResult {
@@ -879,6 +883,9 @@ async fn update_session(Extension(user): Extension<User>, State(app): State<AppS
if let Some(i) = body.instructions {
s.instructions = i.trim().to_string();
}
if let Some(q) = body.telegram_quiet {
s.telegram_quiet = q;
}
})?;
Ok(Json(json!(s)))
}
@@ -1654,10 +1661,16 @@ async fn create_job(Extension(user): Extension<User>, State(app): State<AppState
#[derive(Deserialize)]
struct UpdateJob {
enabled: Option<bool>,
/// Written into the job file's frontmatter.
permission_mode: Option<String>,
}
async fn update_job(Extension(user): Extension<User>, State(app): State<AppState>, Path(id): Path<String>, Json(b): Json<UpdateJob>) -> ApiResult {
let job = own_job(&app, &user, &id)?;
if let Some(m) = b.permission_mode.as_deref() {
crate::jobs::set_permission_mode(std::path::Path::new(&job.path), m).map_err(|e| ApiError(StatusCode::BAD_REQUEST, format!("{e:#}")))?;
app.audit(Some(&user), "job.permission_mode", &job.path, m);
}
if let Some(e) = b.enabled {
app.store.job_set_enabled(&job.id, e)?;
}