OpenStation can run work without an incoming chat message. Choose a Markdown automation for a versioned instruction, a manifest schedule for an operator-defined recurring prompt, or a trigger for work that should start when an event occurs.
Choose what starts the work
| Mechanism | Declared in | Starts when | Changes take effect |
|---|---|---|---|
| Markdown automation | automations: binding plus a Markdown file |
An interval elapses | File metadata on the next scan; body on the next run |
| Manifest schedule | schedules: |
An interval elapses | Next serve startup |
| Event trigger | triggers: |
A matching event is published | Next serve startup |
Only serve runs the background automation machinery. The local dev REPL does not run
schedules, Markdown automations, or triggers. Intervals are supported; cron expressions and
calendar/timezone schedules are not.
Add a Markdown automation
First create an agent and verify it works locally. This example assumes a notes agent already
exists. Add a folder binding to .openstation/openstation.yaml:
automations:
- path: okf/loops
type: Loop
agents: [notes]
maxPerHour: 4
The binding controls which agents files may invoke and how often each file may run. The
maxPerHour ceiling applies to each file, not the aggregate number of runs in the folder.
Create okf/loops/review-notes.md in that workspace:
---
type: Loop
agent: notes
every: 30m
session: fresh
enabled: true
---
Read the notes under okf/issues/. Update okf/summary.md with open decisions and unresolved
questions, linking to the source notes. Do not modify the source notes.
The filename gives this job its ID, review-notes. Use distinct basenames across bound folders
and manifest schedules. The runner sends a pointer to the Markdown file; the agent reads its
body at run time. Its gate must allow that read and any output writes the instructions need.
every accepts a number and a unit: s, m, h, or d, such as 90s, 30m, or 1d.
session: fresh starts each firing without the previous session; the default, resume, retains
the job's own conversation. enabled: false pauses a file without deleting it.
Start and inspect it
Run these commands from the OpenStation repository, substituting your workspace name:
bun packages/cli/src/index.ts my-space serve
Then inspect it from another terminal:
bun packages/cli/src/index.ts my-space automations
bun packages/cli/src/index.ts my-space automations show review-notes
bun packages/cli/src/index.ts my-space automations runs --failed
bun packages/cli/src/index.ts my-space automations run review-notes
bun packages/cli/src/index.ts my-space automations disable review-notes
bun packages/cli/src/index.ts my-space automations enable review-notes
run requests a firing through the job store; a live serve process consumes it. Inspect
skipped files as well as scheduled jobs: invalid cadence, a disallowed agent, duplicate IDs,
and missing required metadata can prevent a file from becoming runnable.
Bound folders are rescanned at boot and every scheduler tick, normally 30 seconds. A new or edited file is picked up on that scan. The scanner reads the first 4096 bytes, so keep complete frontmatter at the beginning of the file. Changing the manifest binding itself requires a restart.
Use a manifest schedule
When the instruction belongs in operator configuration, use schedules: instead:
schedules:
- id: daily-summary
agent: notes
everyMs: 86400000
prompt: Read the notes and update okf/summary.md with unresolved questions.
This runs at an interval of 24 hours, not at a particular local clock time. The first run of a
new job is one interval after startup. Jobs and run counts persist in var/jobs.db; an
unchanged job keeps its pending fire time across restarts. Each schedule resumes its own session.
Deliver the result
A Markdown job uses a flat frontmatter field:
deliver: "telegram:-1001234567890"
A manifest schedule uses an object:
deliver:
to: conversation
conversation: "telegram:-1001234567890"
Replace the example with a real conversation key and run a connector that can deliver to it.
Delivery sends successful, nonblank output as text. It does not lift send-files blocks into
attachments. Failed runs do not post a scheduled reply; inspect automations runs --failed
and the event log. A delivery failure is logged separately and does not change the run outcome.
Run work on an event
Use triggers: when cadence is the wrong signal. Triggers can filter artifact events by path,
extension, artifact type, and change type, or react to turn outcomes. They use the background
lane and are listed by openstation <workspace> triggers.
The triggers: reference covers
filters, retry behavior, and delivery. The eval/learn/improve pipeline from
loops add is event-driven and is distinct from Markdown jobs.
Bound authority and cost
- Set the automation agent's permission gate and give it an explicit budget. A job uses the agent's budget, not a per-file override.
- Keep the folder binding operator-owned. Only grant writes to the job folder when an agent should be allowed to author its own recurring instructions.
- Grant inspection commands separately from
run,enable, anddisable. Those commands can cause another agent to act with that agent's authority. - There is no aggregate spend ceiling across jobs or turns. Frequency limits and per-turn budgets must be planned together.
Full details: manifest automation reference and automation CLI.