Switch alarms to structured triggers

This commit is contained in:
Eric Traut
2026-04-06 21:58:28 -07:00
parent 117ce78783
commit da2e581411
40 changed files with 1754 additions and 768 deletions
@@ -12,6 +12,63 @@
],
"type": "string"
},
"AlarmTrigger": {
"oneOf": [
{
"properties": {
"kind": {
"enum": [
"delay"
],
"type": "string"
},
"repeat": {
"type": [
"boolean",
"null"
]
},
"seconds": {
"format": "uint64",
"minimum": 0.0,
"type": "integer"
}
},
"required": [
"kind",
"seconds"
],
"type": "object"
},
{
"properties": {
"dtstart": {
"type": [
"string",
"null"
]
},
"kind": {
"enum": [
"schedule"
],
"type": "string"
},
"rrule": {
"type": [
"string",
"null"
]
}
},
"required": [
"kind"
],
"title": "KindAlarmTrigger",
"type": "object"
}
]
},
"ApprovalsReviewer": {
"description": "Configures who approval requests are routed to for review. Examples include sandbox escapes, blocked network access, MCP approval prompts, and ARC escalations. Defaults to `user`. `guardian_subagent` uses a carefully prompted subagent to gather relevant context and apply a risk-based decision framework before approving or denying the request.",
"enum": [
@@ -97,6 +97,63 @@
],
"type": "string"
},
"AlarmTrigger": {
"oneOf": [
{
"properties": {
"kind": {
"enum": [
"delay"
],
"type": "string"
},
"repeat": {
"type": [
"boolean",
"null"
]
},
"seconds": {
"format": "uint64",
"minimum": 0.0,
"type": "integer"
}
},
"required": [
"kind",
"seconds"
],
"type": "object"
},
{
"properties": {
"dtstart": {
"type": [
"string",
"null"
]
},
"kind": {
"enum": [
"schedule"
],
"type": "string"
},
"rrule": {
"type": [
"string",
"null"
]
}
},
"required": [
"kind"
],
"title": "KindAlarmTrigger",
"type": "object"
}
]
},
"AppBranding": {
"description": "EXPERIMENTAL - app metadata returned by app-list APIs.",
"properties": {
@@ -2532,9 +2589,6 @@
"format": "int64",
"type": "integer"
},
"cronExpression": {
"type": "string"
},
"delivery": {
"$ref": "#/definitions/AlarmDelivery"
},
@@ -2558,17 +2612,16 @@
"prompt": {
"type": "string"
},
"runOnce": {
"type": "boolean"
"trigger": {
"$ref": "#/definitions/AlarmTrigger"
}
},
"required": [
"createdAt",
"cronExpression",
"delivery",
"id",
"prompt",
"runOnce"
"trigger"
],
"type": "object"
},
@@ -4966,6 +4966,63 @@
],
"type": "string"
},
"AlarmTrigger": {
"oneOf": [
{
"properties": {
"kind": {
"enum": [
"delay"
],
"type": "string"
},
"repeat": {
"type": [
"boolean",
"null"
]
},
"seconds": {
"format": "uint64",
"minimum": 0.0,
"type": "integer"
}
},
"required": [
"kind",
"seconds"
],
"type": "object"
},
{
"properties": {
"dtstart": {
"type": [
"string",
"null"
]
},
"kind": {
"enum": [
"schedule"
],
"type": "string"
},
"rrule": {
"type": [
"string",
"null"
]
}
},
"required": [
"kind"
],
"title": "KindAlarmTrigger",
"type": "object"
}
]
},
"AnalyticsConfig": {
"additionalProperties": true,
"properties": {
@@ -12333,9 +12390,6 @@
"format": "int64",
"type": "integer"
},
"cronExpression": {
"type": "string"
},
"delivery": {
"$ref": "#/definitions/v2/AlarmDelivery"
},
@@ -12359,17 +12413,16 @@
"prompt": {
"type": "string"
},
"runOnce": {
"type": "boolean"
"trigger": {
"$ref": "#/definitions/v2/AlarmTrigger"
}
},
"required": [
"createdAt",
"cronExpression",
"delivery",
"id",
"prompt",
"runOnce"
"trigger"
],
"type": "object"
},
@@ -149,6 +149,63 @@
],
"type": "string"
},
"AlarmTrigger": {
"oneOf": [
{
"properties": {
"kind": {
"enum": [
"delay"
],
"type": "string"
},
"repeat": {
"type": [
"boolean",
"null"
]
},
"seconds": {
"format": "uint64",
"minimum": 0.0,
"type": "integer"
}
},
"required": [
"kind",
"seconds"
],
"type": "object"
},
{
"properties": {
"dtstart": {
"type": [
"string",
"null"
]
},
"kind": {
"enum": [
"schedule"
],
"type": "string"
},
"rrule": {
"type": [
"string",
"null"
]
}
},
"required": [
"kind"
],
"title": "KindAlarmTrigger",
"type": "object"
}
]
},
"AnalyticsConfig": {
"additionalProperties": true,
"properties": {
@@ -10188,9 +10245,6 @@
"format": "int64",
"type": "integer"
},
"cronExpression": {
"type": "string"
},
"delivery": {
"$ref": "#/definitions/AlarmDelivery"
},
@@ -10214,17 +10268,16 @@
"prompt": {
"type": "string"
},
"runOnce": {
"type": "boolean"
"trigger": {
"$ref": "#/definitions/AlarmTrigger"
}
},
"required": [
"createdAt",
"cronExpression",
"delivery",
"id",
"prompt",
"runOnce"
"trigger"
],
"type": "object"
},
@@ -8,15 +8,69 @@
],
"type": "string"
},
"AlarmTrigger": {
"oneOf": [
{
"properties": {
"kind": {
"enum": [
"delay"
],
"type": "string"
},
"repeat": {
"type": [
"boolean",
"null"
]
},
"seconds": {
"format": "uint64",
"minimum": 0.0,
"type": "integer"
}
},
"required": [
"kind",
"seconds"
],
"type": "object"
},
{
"properties": {
"dtstart": {
"type": [
"string",
"null"
]
},
"kind": {
"enum": [
"schedule"
],
"type": "string"
},
"rrule": {
"type": [
"string",
"null"
]
}
},
"required": [
"kind"
],
"title": "KindAlarmTrigger",
"type": "object"
}
]
},
"ThreadAlarm": {
"properties": {
"createdAt": {
"format": "int64",
"type": "integer"
},
"cronExpression": {
"type": "string"
},
"delivery": {
"$ref": "#/definitions/AlarmDelivery"
},
@@ -40,17 +94,16 @@
"prompt": {
"type": "string"
},
"runOnce": {
"type": "boolean"
"trigger": {
"$ref": "#/definitions/AlarmTrigger"
}
},
"required": [
"createdAt",
"cronExpression",
"delivery",
"id",
"prompt",
"runOnce"
"trigger"
],
"type": "object"
}
@@ -8,15 +8,69 @@
],
"type": "string"
},
"AlarmTrigger": {
"oneOf": [
{
"properties": {
"kind": {
"enum": [
"delay"
],
"type": "string"
},
"repeat": {
"type": [
"boolean",
"null"
]
},
"seconds": {
"format": "uint64",
"minimum": 0.0,
"type": "integer"
}
},
"required": [
"kind",
"seconds"
],
"type": "object"
},
{
"properties": {
"dtstart": {
"type": [
"string",
"null"
]
},
"kind": {
"enum": [
"schedule"
],
"type": "string"
},
"rrule": {
"type": [
"string",
"null"
]
}
},
"required": [
"kind"
],
"title": "KindAlarmTrigger",
"type": "object"
}
]
},
"ThreadAlarm": {
"properties": {
"createdAt": {
"format": "int64",
"type": "integer"
},
"cronExpression": {
"type": "string"
},
"delivery": {
"$ref": "#/definitions/AlarmDelivery"
},
@@ -40,17 +94,16 @@
"prompt": {
"type": "string"
},
"runOnce": {
"type": "boolean"
"trigger": {
"$ref": "#/definitions/AlarmTrigger"
}
},
"required": [
"createdAt",
"cronExpression",
"delivery",
"id",
"prompt",
"runOnce"
"trigger"
],
"type": "object"
}
@@ -0,0 +1,5 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type AlarmTrigger = { "kind": "delay", seconds: number, repeat: boolean | null, } | { "kind": "schedule", dtstart: string | null, rrule: string | null, };
@@ -2,5 +2,6 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { AlarmDelivery } from "./AlarmDelivery";
import type { AlarmTrigger } from "./AlarmTrigger";
export type ThreadAlarm = { id: string, cronExpression: string, prompt: string, runOnce: boolean, delivery: AlarmDelivery, createdAt: number, nextRunAt: number | null, lastRunAt: number | null, };
export type ThreadAlarm = { id: string, trigger: AlarmTrigger, prompt: string, delivery: AlarmDelivery, createdAt: number, nextRunAt: number | null, lastRunAt: number | null, };
@@ -9,6 +9,7 @@ export type { AdditionalNetworkPermissions } from "./AdditionalNetworkPermission
export type { AdditionalPermissionProfile } from "./AdditionalPermissionProfile";
export type { AgentMessageDeltaNotification } from "./AgentMessageDeltaNotification";
export type { AlarmDelivery } from "./AlarmDelivery";
export type { AlarmTrigger } from "./AlarmTrigger";
export type { AnalyticsConfig } from "./AnalyticsConfig";
export type { AppBranding } from "./AppBranding";
export type { AppInfo } from "./AppInfo";
@@ -1838,41 +1838,6 @@ mod tests {
assert_eq!(reason, Some("thread/realtime/outputAudio/delta"));
}
#[test]
fn thread_alarm_create_is_marked_experimental() {
let request = ClientRequest::ThreadAlarmCreate {
request_id: RequestId::Integer(1),
params: v2::ThreadAlarmCreateParams {
thread_id: "thr_123".to_string(),
cron_expression: "@after-turn".to_string(),
prompt: "run tests".to_string(),
run_once: Some(false),
delivery: v2::AlarmDelivery::AfterTurn,
},
};
let reason = crate::experimental_api::ExperimentalApi::experimental_reason(&request);
assert_eq!(reason, Some("thread/alarm/create"));
}
#[test]
fn thread_alarm_fired_notification_is_marked_experimental() {
let notification = ServerNotification::ThreadAlarmFired(v2::ThreadAlarmFiredNotification {
thread_id: "thr_123".to_string(),
alarm: v2::ThreadAlarm {
id: "alarm_123".to_string(),
cron_expression: "@after-turn".to_string(),
prompt: "run tests".to_string(),
run_once: false,
delivery: v2::AlarmDelivery::AfterTurn,
created_at: 1,
next_run_at: None,
last_run_at: None,
},
});
let reason = crate::experimental_api::ExperimentalApi::experimental_reason(&notification);
assert_eq!(reason, Some("thread/alarm/fired"));
}
#[test]
fn command_execution_request_approval_additional_permissions_is_marked_experimental() {
let params = v2::CommandExecutionRequestApprovalParams {
@@ -3161,14 +3161,33 @@ pub enum AlarmDelivery {
SteerCurrentTurn,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(tag = "kind", rename_all = "camelCase")]
#[ts(tag = "kind")]
#[ts(export_to = "v2/")]
pub enum AlarmTrigger {
#[serde(rename_all = "camelCase")]
#[ts(rename_all = "camelCase")]
Delay {
#[ts(type = "number")]
seconds: u64,
repeat: Option<bool>,
},
#[serde(rename_all = "camelCase")]
#[ts(rename_all = "camelCase")]
Schedule {
dtstart: Option<String>,
rrule: Option<String>,
},
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ThreadAlarm {
pub id: String,
pub cron_expression: String,
pub trigger: AlarmTrigger,
pub prompt: String,
pub run_once: bool,
pub delivery: AlarmDelivery,
#[ts(type = "number")]
pub created_at: i64,
@@ -3183,10 +3202,8 @@ pub struct ThreadAlarm {
#[ts(export_to = "v2/")]
pub struct ThreadAlarmCreateParams {
pub thread_id: String,
pub cron_expression: String,
pub trigger: AlarmTrigger,
pub prompt: String,
#[ts(optional = nullable)]
pub run_once: Option<bool>,
pub delivery: AlarmDelivery,
}