openstation

Configure

Automations

Run Markdown jobs, interval schedules, and event-driven work.

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, and disable. 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.

View Markdown source on GitHub ↗