Skip to content

Jobs

A job is a prompt you leave behind. You ask the agent for it once, in a chat, and from then on it runs on its own: no conversation, no one watching, and, if the job was created with a notification requested, a message when it has something to say.

Jobs are how q15 does the work you would otherwise have to remember to ask for. A morning summary of a folder, a check on a page that changes, a weekly tidy-up of a workspace.

Read this before you create a dozen of them.

  • Model calls. Every run is real turns against a real model, on your provider account. A job with no tools is cheap; a job that reads twelve files and searches the web is not.
  • Five minutes, then it is killed. A run is stopped after five minutes of wall time. That cap is not configurable through the agent config today; the field request is filed.
  • A tool allow-list decided at creation. A job can use only the tools named when it was created or last updated. That is a security property, not a convenience: a job that should read a file has no reason to hold your shell.
  • Ownership. A job belongs to the chat that created it. Create it in Telegram and it reports in Telegram; it is not visible from the browser client, and the browser client cannot run it.

You do not write a file. You ask, in chat:

Every weekday at 07:00 Berlin time, summarise anything new in /workspace/inbox and send me three lines. Do not reply if there is nothing new.

The agent calls schedule_create, which takes:

Field What it means
name The label you will recognise in a list.
prompt The task, written for an isolated agent that has no memory of this conversation.
kind oneshot or recurring.
run_at An RFC3339 UTC timestamp. Required for a one-shot.
cron A strict five-field UTC cron expression. Required for a recurring job.
notify Whether the run reports back. A silent job still writes its record.
model Optional provider and model ref to pin the run. Omitted, it uses the model you are on when you create it.
max_turns Optional turn cap for a run, bounded by the deployment.
allowed_tools The exact tool names the job may use. An empty list means it needs none.
context_profile minimal (default) or agent. See below.

Context profiles. minimal is the default and the one you want most of the time: the job gets its prompt and its tools, and nothing of your conversation. agent adds the stable agent context (core memory and the skill catalog) but still never the interactive conversation. Neither profile changes which tools the job may use; only allowed_tools does.

Local time is your problem, not the scheduler’s: both run_at and cron are UTC. If you think in Berlin time, subtract the offset, and remember it moves twice a year.

Limit Default Where it comes from
Jobs per agent 64 agent.tools.schedule.max_jobs, bounded at 1000
Turns per run 16 agent.tools.schedule.max_run_turns, bounded at 128
Wall time per run 5 min Fixed. Not in the YAML.
Concurrent runs 1 A job that is already running is skipped when its next slot arrives.

Reaching max_jobs fails the create rather than silently dropping the oldest job.

From chat:

  • schedule_list — the jobs this chat owns, with their next fire time and last status.
  • schedule_runs — the archived history: exact counts by status, and the newest records, filtered by job id, name, status or time window. Use it when you want to know whether the thing actually ran.
  • schedule_update — change the prompt, the schedule, the tools or the model without changing the job’s identity or its history.
  • schedule_delete — remove it. Its run records stay.

On disk, everything is a file you can read:

/var/lib/q15/agent/schedule/
├── jobs/<job-id>.json one file per job: prompt, schedule, tools, last status
└── runs/YYYY/MM/DD/<time>-<run-id>.json
one record per run: model, tools, start, finish,
status, output, error

The records are the audit trail. last_run_at, last_success_at, consecutive_failures and last_error sit on the job itself.

  • The model is gone. If the pinned model is no longer in the roster, the run is skipped and the refusal is reported once, not on every fire, until the model comes back or you re-pin the job.
  • Ordinary failure. The run is recorded with its error and consecutive_failures climbs. You are told, if the job asked to be. A job is never disabled automatically: a failing job keeps failing until you look.
  • A crash mid-run. The durable record is written before the notification is delivered, so a crash can duplicate a notification but cannot lose the run or replay it.
  • A digest: “what changed in these folders since yesterday”.
  • A watch: “tell me when this page changes in a way that matters”.
  • A rhythm: “every Monday, tidy /workspace/scratch and report what you removed”.
  • A reminder with teeth: “on the 20th, check whether the invoice in /workspace/finance was paid”.

Not good for: anything that needs more than five minutes, anything that needs a conversation, or anything you would not want a slightly confused agent doing unattended with the tools you gave it.

  • Tools for what a job can be allowed to do.
  • Models for pinning a run to a specific model.
  • Telegram for where a job’s report arrives.
  • Backups: /var/lib/q15/agent is the volume that holds all of this.