Add actions and schedules to a bag
Use this when a bag should carry a repeatable procedure, or run work on a
timer, for every identity that packs it. An action is a named procedure with
typed inputs and a recorded run, in actions/<name>/action.yaml. A schedule
in bag.yaml runs an action, a job, or a script on a trigger. The example is
standup, a bag that writes standup notes every weekday morning and sweeps old
ones every hour.
Before you begin
Section titled “Before you begin”- Create a bag.
- Read Using actions to see how a person runs an action and reads its result.
- A prompt action that runs on a schedule goes to Barry’s API, so the API must be running when it fires.
Write an action
Section titled “Write an action”An action lives in its own directory under actions/, named after it. To
scaffold one, run barry new action standup-notes --bag standup. This is the
finished file:
name: standup-notesdescription: Summarize a repository's last day of commits as three standup bullets. Use when the user asks for standup notes.context: noneinput_schema: type: object properties: repo: type: string description: Path to the repository, such as ~/repos/app. required: [repo]output_schema: type: string description: Three standup bullets as Markdown.timeout: 10mprompt: | Read the commits made in ${INPUT.repo} in the last 24 hours with `git log`. Write three one-line bullets: what changed, what is in progress, and what is blocked. Save them to ~/standup-notes/ as a Markdown file named for today's date, and return them.The fields that shape an action:
nameis kebab-case, anddescriptionsays when to use the action, in at most 1,024 characters. The description joins the actions index in every session’s prompt, so the agent finds the action by it.prompt,run, orjobsays what does the work: an agent following a prompt, a command Barry runs itself, or one of the bag’s jobs. Declare exactly one.contextissessionwhen the prompt refers to the calling conversation, andnonewhen the action stands alone. A prompt action defaults tosession, which runs it inside the conversation that calls it. A schedule has no calling conversation, so Barry refuses to fire a prompt action withcontext: session; setcontext: none, as this file does, and a fresh agent runs it detached.input_schemais a JSON Schema whose root hastype: object. Barry rejects malformed inputs before it starts a run, and fills each${INPUT.<name>}in the prompt with the input’s value.timeoutbounds the run, in seconds or with ans,m,h, ordsuffix.
The action.yaml reference covers
requires, preflight, validate, steps, and the rest.
Declare the schedules
Section titled “Declare the schedules”name: standupdescription: Writes standup notes every weekday morning and sweeps old ones every hour.
schedules: weekday-notes: description: Standup notes for the main repository cron: "0 9 * * 1-5" timezone: America/New_York action: standup-notes inputs: repo: ~/repos/app bounds: [no-network-write] overlap: skip catch_up: none sweep: description: Delete standup notes older than a week every: 1h action: description: Delete standup notes older than a week run: entry: src/sweep.ts timeout: 2mEach key under schedules: is a kebab-case name, and Barry gives the schedule
the ID <bag>/<name>, such as standup/weekday-notes. A schedule declares
exactly one trigger and exactly one payload.
The trigger is one of:
cron: a five-field cron expression, read intimezone, an IANA zone name that defaults to UTC.every:1m,5m,15m,30m,1h, or1d, aligned to the clock.1dfires at midnight in the schedule’s timezone.at: one ISO 8601 time with an explicit offset, which fires once, orlogin, which fires once for each login session.
The payload is one of:
action: an action by name, or bybag:namefor another bag’s, or an inline action body, as thesweepschedule shows. An inline action takes every action field exceptname, which comes from the schedule’s key, and defaults tocontext: none.job: one of the bag’s jobs, by name or asbag:name.entry: a script inside the bag, with optionalargs,env,secrets, andtimeout.
The rest of a schedule says how it runs:
| Field | What it does |
|---|---|
inputs |
Inputs for the action, checked against its input_schema when it runs |
identity |
The identity an agent action runs as; omit it to use the default resolution |
traits, bounds |
Grants and restrictions for the run’s session. bounds: [no-network-write] blocks recognized network writes, such as a git push, and still lets the notes run read git log and save its file. The readonly bound denies Bash and file writes, so the notes run fails under it |
overlap |
A fire that lands while the last run is still going: skip, the default, or queue |
catch_up |
A fire missed while the machine slept or was off: once, the default, runs it on return; none drops it |
enabled |
false keeps the schedule declared but off |
Barry checks every trigger when it parses the manifest: an invalid cron expression, an unknown timezone, or a schedule with two triggers fails the bag, not its first fire.
The sweep schedule’s inline action runs src/sweep.ts itself, with no
agent. Barry runs a TypeScript entry with the tsx in the bag’s own
node_modules, so the bag depends on tsx:
import { existsSync, readdirSync, rmSync, statSync } from "node:fs";import { homedir } from "node:os";import { join } from "node:path";
const dir = join(homedir(), "standup-notes");const weekAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;let removed = 0;if (existsSync(dir)) { for (const entry of readdirSync(dir, { withFileTypes: true })) { const path = join(dir, entry.name); if (entry.isFile() && statSync(path).mtimeMs < weekAgo) { rmSync(path); removed += 1; } }}console.log(`removed ${removed} note(s)`);{ "name": "standup-bag", "private": true, "type": "module", "dependencies": { "tsx": "4.23.12" }}Verify
Section titled “Verify”Check the bag, install it, and list the schedules:
barry bag check ~/repos/standupbarry install ~/repos/standupbonanza schedule listThe list shows standup/weekday-notes and standup/sweep, each with its next
fire time. To fire a schedule now instead of waiting, run
bonanza schedule run standup/sweep, then read the run with
bonanza schedule show standup/sweep. To run the action by itself, run
bonanza action run standup-notes --input repo=~/repos/app.
If it fails
Section titled “If it fails”- The bag fails to load after you add a schedule. The schedule has no trigger or two, a cron expression without five fields, a timezone that isn’t an IANA name, or two payloads. The error names the schedule.
barry bag checksays the schedule refers to an action the bag doesn’t ship. A bare action name must match a directoryactions/<name>/that holds anaction.yaml. Name another bag’s action asbag:name.- A scheduled prompt action refuses to run. It declares
context: session, or the API isn’t running. - For anything else, see Troubleshooting.
What’s next
Section titled “What’s next”- Schedule and distribute work to pause, resume, and inspect schedules.
- The bag.yaml reference for every schedule field.
- Call Barry from a bag to schedule a one-off run from code.