Triggers make MagicFrog act without a human turn: a cron trigger fires a scheduled action, and a watcher loops a small script that decides whether something interesting happened. Both kinds share one overview, and both refuse to guess where they run.
Scheduled triggers (cron)
A scheduled trigger carries its identity, a schedule, and an action:
| Field | Meaning |
|---|---|
triggerId |
Stable identity of the trigger |
kind |
cron |
schedule |
Non-empty schedule expression |
intervalMinutes |
Integer; the minimum is 30 minutes, anything below is rejected |
targetComputerId |
The computer the action runs on — required for cron |
ownerTaskId |
Optional: the task the trigger belongs to |
action |
What to run (type plus optional parameters) |
Two contracts make schedules trustworthy:
- Fail-closed targeting. A cron trigger without
targetComputerIdis rejected at registration and re-checked at every execution. It never runs against the local machine and is never silently re-targeted. - No catch-up replay. If a fire is missed while the service is down or a run overruns, the trigger fires normally at the next cycle — the missed fire is skipped, never replayed.
Every execution outcome is recorded: ok, error, or rejected.
Watchers
A watcher is a bun script authored by you or by an agent, run on a loop against one target.
| Field | Meaning |
|---|---|
watcherId |
Stable identity of the watcher |
scriptPath |
Path to the bun script |
targetKind |
task or agent |
targetId |
The task or agent being watched |
interval |
Loop cadence in seconds (the 30-minute floor is a cron contract, not a watch-loop contract) |
ownerAgentId |
Optional: the agent that owns the watch |
status |
stopped, running, or failed |
The script contract
Each iteration receives its target context on stdin as JSON:
{
"watcherId": "wat_...",
"targetKind": "agent",
"targetId": "...",
"now": 1792000000000,
"artifactsDir": ".../watchers/wat_.../",
"events": [],
"eventCursor": 0
}
The script answers with a single JSON finding line on stdout:
{ "matched": true, "summary": "Build failed twice in a row", "data": { "runId": "run_..." } }
What counts as "matched" is entirely the script's decision. A script exiting non-zero counts as a failed run and the watcher keeps running; failed status means the script itself could not start, for example an unreadable file.
Every watcher gets its own artifact directory, used as the child's working directory and exposed as MAGICFROG_WATCHER_ARTIFACT_DIR, so script-produced files can never collide across watchers or leak into the workspace.
Targets
agent— the loop reads the cloud agent's events:run.started,tool.called,tool.result,form.pending,form.answered, andtakeover.requested. After a disconnect it resumes from where it left off instead of starting over.task— the loop reads a snapshot of task status. If task status cannot be read, starting the watcher fails explicitly — a fabricated status is never presented.
Watchers run on your machine. A watcher can watch a remote cloud agent's events without the watcher itself moving anywhere.
One overview
GET /v1/triggers/overview is one account-scoped read listing every task's cron triggers and watchers, each with its status and next due time. The console and the local command show the same state.