5. Background mode
The first four tutorials all ran in runtime.mode: conversation -
the agent only thinks when a user types something. Background
mode flips that: Digitorn wakes the agent on a schedule or when
an external event arrives. No human in the loop.
The smallest background app declares one channel provider and a one-line prompt the provider uses to wake the agent.
Prerequisites
Same local Digitorn setup and credential as the previous tutorials.
The YAML
Save this as daily-monitor.yaml. The app fires on a cron at 09:00
on weekdays.
app:
app_id: daily-monitor
name: Daily Monitor
version: "1.0"
runtime:
mode: background
workdir_mode: auto
max_turns: 4
timeout: 60
agents:
- id: main
role: assistant
brain:
provider: deepseek
model: deepseek-chat
backend: openai_compat
credential:
ref: deepseek_main
scope: per_user
provider: deepseek
config:
api_key: "{{env.DEEPSEEK_API_KEY}}"
base_url: https://api.deepseek.com/v1
temperature: 0
max_tokens: 64
system_prompt: |
You are a background monitor. Each time you wake up, reply
with one short status line. Do not ask questions; the user
is not online when you fire.
tools:
modules:
channels:
config:
providers:
morning_check:
adapter: cron
enabled: true
config:
schedule: "0 9 * * 1-5" # 09:00 Mon-Fri
activation:
agent: main
message: "Time for the daily check. Reply with: monitor running."
session: per_event
reply: none
capabilities:
default_policy: auto
What arms the app is the channel provider, not runtime.mode
alone: digitorn-background reads
tools.modules.channels.config.providers and arms one listener per
entry. A background app without a provider compiles but never
fires - the daemon says so (DGT-E0407).
Two new things vs the previous tutorials:
runtime.mode: background- the app is event-driven, not chat-driven.tools.modules.channels.config.providers- the activation source. This one is acronprovider. For webhooks and chat networks, see Channels.
Deploy
digitorn install daily-monitor.yaml
After install, Digitorn arms the cron provider. At 09:00 on
weekdays the agent wakes with the activation message as input.
Try it without waiting for cron
For a faster feedback loop while learning, temporarily change the
schedule to every minute ("* * * * *"), re-install, and watch
the app's session / activity in the Digitorn UI. Then restore the
weekday schedule.
You can also keep a conversation-mode sibling app that uses
scheduler.schedule from a chat turn (see
scheduler) if you want an
interactive demo.
What a successful wake looks like
When the provider fires, the agent should reply with a short status
line such as monitor running. Session history for
daily-monitor shows the activation message and that reply.
Each fire is recorded as a run against the background job store, exposed by the daemon's API in this shape:
{
"id": "r_1a2b3c4d",
"job_id": "j_5e6f7a8b",
"app_id": "daily-monitor",
"trigger_id": "morning_check",
"provider": "morning_check",
"adapter": "cron",
"attempt": 1,
"outcome": "ok",
"duration_ms": 842,
"started_at": "2026-08-21T09:00:00Z",
"ended_at": "2026-08-21T09:00:00.842Z"
}
The agent woke up, ran, and finished with outcome: "ok".
Other adapter types
cron is the simplest. tools.modules.channels.config.providers
accepts several other adapters; pick the one that matches your
event source - see Channels for the
full field list of each:
| Adapter | What it watches | Example use |
|---|---|---|
cron | Time of day / day of week | Daily digest, hourly poll |
webhook | An incoming HTTP POST | GitHub webhook handler |
rss | An RSS/Atom feed, polled | "When a new post lands, summarize it" |
telegram | A Telegram bot's messages | Telegram assistant |
discord | A Discord bot's messages | Discord assistant |
whatsapp | WhatsApp (Meta Cloud API) | WhatsApp assistant |
connector | A connector event | "On new Slack message in #alerts, ..." |
Each adapter's event payload is exposed to the agent's prompt
template as {{event.payload.X}} (or the normalized
{{event.message}} on the chat-shaped adapters) - see
Channels → Template variables.
Wiring multiple providers
A single app can declare several providers. Each targets an agent
with its own message template and gets its own activation log:
tools:
modules:
channels:
config:
providers:
morning_check:
adapter: cron
enabled: true
config:
schedule: "0 9 * * 1-5"
activation:
agent: main
message: "Daily 09:00 check. Read the inbox, summarise."
reply: none
alert_webhook:
adapter: webhook
enabled: true
config:
inbound_path: /hooks/alerts
auth: none
activation:
agent: main
message: "Alert received: {{event.payload.title}}. Investigate."
reply: auto
V1 arms seven adapters: cron, webhook, rss, telegram,
discord, whatsapp, connector. Anything else is declared but
never listened to.
See Channels for payload fields and filtering options you can set in YAML.
When to use background mode
- The agent does work without a human (digests, monitors, ingestion pipelines, scheduled reports).
- The work is triggered by external events (new file, new message, webhook).
- You want multiple parallel users, each with their own
continuous thread across repeated activations - set the channel
provider's
activation.sessionto a per-user template (e.g."{{event.payload.user_id}}"). The default,per_event, already gives every fire its own fresh session (safe for concurrent users, but no memory between fires); a template groups one user's events into the same recurring session instead. Avoidsession: shared- it pins every event from that provider to one fixed session id, so all users' activations land in the same thread. See Background sessions.
For interactive chat-driven apps, stay on runtime.mode: conversation. Mixed apps can host both - a chat surface plus a
cron provider that posts a daily summary into the same session.
Next: 6. UI surfaces - the workspace pane and declarative widgets.