触发器让 MagicFrog 在没有人类回合的情况下行动:cron 触发器按计划触发一个动作,watcher 则循环运行一小段脚本,判断是否发生了需要你注意的事。两类共享同一个总览,也都拒绝猜测自己在哪里运行。
定时触发器(cron)
一条定时触发器携带自己的身份、一份计划和一个动作:
| 字段 | 含义 |
|---|---|
triggerId |
触发器的稳定身份 |
kind |
cron |
schedule |
非空的计划表达式 |
intervalMinutes |
整数;下限 30 分钟,低于即被拒绝 |
targetComputerId |
动作在其上运行的计算机——cron 必填 |
ownerTaskId |
可选:触发器所属的任务 |
action |
要运行什么(type 加可选参数) |
两条契约让计划值得信赖:
- **失败即关闭(fail-closed)的目标定向。**没有
targetComputerId的 cron 触发器在注册时即被拒绝,并在每次执行时复查。它绝不对本地机器运行,也绝不被静默改投他处。 - **不做补跑重放。**服务宕机或 run 超时错过一次触发时,触发器在下一个周期照常触发——错过的那次被跳过,绝不重放。
每次执行的结果都会记录:ok、error 或 rejected。
Watcher
watcher 是一段由你或 agent 编写的 bun 脚本,针对一个目标循环运行。
| 字段 | 含义 |
|---|---|
watcherId |
watcher 的稳定身份 |
scriptPath |
bun 脚本路径 |
targetKind |
task 或 agent |
targetId |
被观察的任务或 agent |
interval |
循环节拍,单位秒(30 分钟下限是 cron 的契约,不是 watch 循环的契约) |
ownerAgentId |
可选:拥有该观察的 agent |
status |
stopped、running 或 failed |
脚本契约
每轮迭代通过 stdin 收到自己的目标上下文,JSON 形态:
{
"watcherId": "wat_...",
"targetKind": "agent",
"targetId": "...",
"now": 1792000000000,
"artifactsDir": ".../watchers/wat_.../",
"events": [],
"eventCursor": 0
}
脚本在 stdout 上以一行 JSON finding 作答:
{ "matched": true, "summary": "Build failed twice in a row", "data": { "runId": "run_..." } }
什么算 matched 完全由脚本决定。脚本以非零码退出算一次失败,watcher 继续运行;failed 状态指脚本本身无法启动,例如文件不可读。
每个 watcher 有自己的产物目录,它既是子进程的工作目录,也以 MAGICFROG_WATCHER_ARTIFACT_DIR 暴露,所以脚本产出的文件绝不会在 watcher 之间相撞,也绝不会漏进工作区。
目标
agent——循环读取云端 agent 的事件:run.started、tool.called、tool.result、form.pending、form.answered与takeover.requested。断连之后从上次的进度继续,而不是从头开始。task——循环读取任务状态快照。读不到任务状态时启动会显式失败,绝不出示伪造状态。
watcher 在你的机器上运行。它可以监视远端云端 agent 的事件,但 watcher 本身不迁移。
一个总览
GET /v1/triggers/overview 一次读取账号范围内每个任务的 cron 触发器与 watcher,各自带状态与下次到期时间。控制台与本机命令看到的是同一份状态。