Examples
Complete app YAML sketches. Prefer tool names from
YAML building blocks. Top-level blocks
follow AppDefinition (app, runtime, agents, tools,
security, ui, dev, flow, plus optional templates /
requirements / docs / ...).
channels: lives under tools: (not top-level).
runtime.watchers / runtime.scheduler do not register watcher
or cron_native tools.
1 · Minimal chat
The simplest possible conversational app - LLM + filesystem read access.
app:
app_id: chat-assistant
name: Chat Assistant
description: Interactive conversation with tool access.
runtime:
mode: conversation
agents:
- id: assistant
role: assistant
brain:
provider: deepseek
model: deepseek-chat
backend: openai_compat
config:
api_key: "{{secret.DEEPSEEK_API_KEY}}"
base_url: https://api.deepseek.com/v1
system_prompt: |
Tu es un assistant intelligent et amical. Tu réponds en français.
Utilise les outils disponibles quand c'est pertinent.
tools:
modules:
filesystem: {}
capabilities:
default_policy: auto
ui:
greeting: "Bienvenue ! Je suis ton assistant Digitorn."
2 · One-shot task
Process a single input and return; the agent loop exits as soon as the LLM produces text without calling any tool.
app:
app_id: hello-oneshot
name: Hello One-Shot
runtime:
mode: one_shot
input:
type: text
description: A question or message
output:
type: text
agents:
- id: assistant
role: assistant
brain:
provider: deepseek
model: deepseek-chat
backend: openai_compat
config: {api_key: "{{secret.DEEPSEEK_API_KEY}}"}
system_prompt: "Be concise and helpful. Use tools when relevant."
tools:
modules:
filesystem:
constraints:
allowed_actions: [read, glob, grep]
capabilities:
default_policy: auto
install hello-oneshot.yaml
digitorn chat hello-oneshot -m "Say hello in 3 languages"
3 · Smart chat with context management
Conversation mode with automatic context compaction
(summarize strategy + auto_compact: true).
app:
app_id: smart-chat
name: Smart Chat
runtime:
mode: conversation
max_turns: 40
timeout: 1200
agents:
- id: assistant
role: assistant
brain:
provider: deepseek
model: deepseek-chat
backend: openai_compat
config: {api_key: "{{secret.DEEPSEEK_API_KEY}}"}
context:
max_tokens: 80000
output_reserved: 1000
strategy: summarize # truncate | summarize
keep_recent: 20 # last N messages always kept verbatim
compression_trigger: 0.9 # compact when usage > 90 %
summary_max_tokens: 5120
auto_compact: true
system_prompt: |
Tu es un assistant intelligent. Réponds en français.
Limite-toi à 3-5 appels d'outils maximum par question.
Si un outil échoue, explique l'erreur, ne ré-essaie pas en boucle.
tools:
modules:
filesystem:
constraints:
allowed_actions: [read, glob, grep, write]
capabilities:
default_policy: auto
ui:
greeting: "Salut ! Assistant avec gestion automatique du contexte."
4 · Local LLM (Ollama, no native tools)
Local model, tool definitions injected in the system prompt since the model doesn't support native function calling.
app:
app_id: ollama-chat
name: Ollama Chat
runtime:
mode: conversation
max_turns: 10
timeout: 300
agents:
- id: assistant
role: assistant
brain:
provider: ollama
model: qwen2.5:14b-instruct-q4_K_M
backend: openai_compat
config: {base_url: http://localhost:11434/v1}
context:
max_tokens: 8000
strategy: truncate
keep_recent: 6
compression_trigger: 0.6
auto_compact: true
system_prompt: "Tu es un assistant local. Limite-toi à 3-5 appels d'outils max."
tools:
modules:
filesystem:
constraints:
allowed_actions: [read, glob, grep]
capabilities:
default_policy: auto
ui:
greeting: "Assistant local prêt."
Local-LLM tips: timeout: 300 (or higher) - local
models are slower than cloud APIs. If your model supports
native tool calling (e.g. qwen2.5-coder), set
native_tool_use: true on the brain for much better tool
reliability:
brain:
provider: ollama
model: qwen2.5-coder:7b
native_tool_use: true
5 · Context-management stress test
Aggressive compression_trigger so the compaction code path
fires within a few turns. Includes a runtime.hooks: entry
that logs token pressure on every turn.
app:
app_id: context-test
name: Context Management Test
runtime:
mode: conversation
max_turns: 50
timeout: 120
hooks:
- id: pressure_log
"on": turn_start
condition: {type: always}
action:
type: log
message: "Turn {turn}: ~{tokens} tokens, {messages} messages"
cooldown: 0
agents:
- id: assistant
role: assistant
brain:
provider: deepseek
model: deepseek-chat
backend: openai_compat
config: {api_key: "{{secret.DEEPSEEK_API_KEY}}"}
context:
max_tokens: 0 # 0 = use provider default
output_reserved: 4096
strategy: summarize
keep_recent: 6
compression_trigger: 0.15 # very aggressive
summary_max_tokens: 512
auto_compact: true
system_prompt: "Réponds en français. Sois détaillé pour générer du contenu."
tools:
modules:
filesystem:
constraints:
allowed_actions: [read, glob, grep]
capabilities:
default_policy: auto
6 · Smart chat with summary brain
Same as example 3 but the summary brain is a small local model - keeps the main conversation on the cloud LLM while compaction runs locally for free.
agents:
- id: assistant
role: assistant
brain:
provider: deepseek
model: deepseek-chat
backend: openai_compat
config: {api_key: "{{secret.DEEPSEEK_API_KEY}}"}
context:
max_tokens: 80000
strategy: summarize
keep_recent: 10
compression_trigger: 0.75
summary_max_tokens: 1024
auto_compact: true
summary_brain:
provider: ollama
model: qwen2.5:3b
backend: openai_compat
config: {base_url: http://localhost:11434/v1}
system_prompt: "Réponds en français."
7 · Secure read-only analyst
Read-only filesystem + database access, with explicit
grant: / deny: and max_risk_level: low. Demonstrates
the security model in Security.
app:
app_id: secure-analyst
name: Secure Analyst
runtime:
mode: conversation
workdir: "{{env.PWD}}"
agents:
- id: analyst
role: assistant
brain:
provider: deepseek
model: deepseek-chat
backend: openai_compat
config: {api_key: "{{secret.DEEPSEEK_API_KEY}}"}
system_prompt: |
You are a data analyst with read-only access.
Never attempt to modify files or execute write queries.
tools:
modules:
filesystem:
constraints:
allowed_actions: [read, glob, grep]
database:
config:
databases:
- name: main
kind: postgres
dsn: "{{secret.ANALYST_DATABASE_URL}}"
security:
mode: read_only
constraints:
allowed_actions: [query]
capabilities:
default_policy: auto
max_risk_level: low
grant:
- {module: filesystem, actions: [read, glob, grep]}
- {module: database, actions: [query]}
deny:
- {module: filesystem, actions: [write, edit], reason: "Read-only mode"}
- {module: database, actions: [disconnect], reason: "Only query allowed"}
ui:
greeting: "Data analyst ready. I can read files and query databases."
dev:
variables:
workspace: "{{env.PWD}}"
8 · Multi-agent (coordinator + worker)
Two agents. The coordinator delegates with
agent(agent="worker", task="...", wait=true).
app:
app_id: multi-agent
name: Multi-Agent
runtime:
mode: conversation
entry_agent: coordinator
max_turns: 30
workdir: "{{env.PWD}}"
agents:
- id: coordinator
role: coordinator
brain:
provider: deepseek
model: deepseek-chat
backend: openai_compat
config: {api_key: "{{secret.DEEPSEEK_API_KEY}}"}
system_prompt: "Orchestrate via the agent tool (agent + task params)."
- id: worker
role: specialist
brain:
provider: groq
model: llama-3.3-70b-versatile
backend: openai_compat
config:
api_key: "{{secret.GROQ_API_KEY}}"
base_url: https://api.groq.com/openai/v1
system_prompt: "Execute the delegated task."
tools:
modules:
filesystem:
constraints:
allowed_actions: [read, glob, grep]
agent_spawn: {}
capabilities:
default_policy: auto
grant:
- module: agent_spawn
- module: filesystem
ui:
greeting: "Multi-agent system ready."
See Multi-agent for real agent modes
(agent/task, batch agents, wait, list, cancel). There
is no reassign tool param in this build.
9 · Background mode with channel providers
A daemon app that sweeps an inbox folder every 10 minutes and emits an hourly summary. Digitorn has no file watcher: polling on a cron is how you pick up new files today. See Channels for the adapters that arm.
app:
app_id: csv-watcher
name: CSV Watcher
runtime:
mode: background
max_turns: 10
timeout: 60
workdir: "{{env.PWD}}"
agents:
- id: analyst
role: assistant
brain:
provider: deepseek
model: deepseek-chat
backend: openai_compat
config: {api_key: "{{secret.DEEPSEEK_API_KEY}}"}
system_prompt: |
When activated, glob the inbox for CSV files, read the ones
you have not summarised yet, and report what they contain.
tools:
modules:
filesystem:
constraints:
allowed_actions: [read, glob, grep]
channels:
config:
providers:
inbox_sweep:
adapter: cron
enabled: true
config:
schedule: "*/10 * * * *"
activation:
agent: analyst
message: "Sweep {{workspace}}/inbox/ for new CSV files and analyse them."
reply: none
hourly_report:
adapter: cron
enabled: true
config:
schedule: "0 * * * *"
activation:
agent: analyst
message: "Generate an hourly summary of {{workspace}}/inbox/."
reply: none
capabilities:
default_policy: auto
dev:
variables:
workspace: "{{env.PWD}}"
Background mode requires at least one trigger. The agent is activated each time a trigger fires with the trigger's
messageas input. For multi-user routing (broadcast/user/session), see Background Sessions.
10 · Parallel execution + background tasks
A polyvalent assistant showing the auto-injected execution
primitives (run_parallel, background_run) - no YAML
config needed for those.
app:
app_id: smart-chat
name: Smart Chat
runtime:
mode: conversation
max_turns: 200
timeout: 1200
workdir: "{{env.PWD}}"
agents:
- id: assistant
role: assistant
brain:
provider: openai
model: gpt-4o-mini
backend: openai_compat
config: {api_key: "{{secret.OPENAI_API_KEY}}"}
context:
max_tokens: 128000
output_reserved: 2000
strategy: summarize
keep_recent: 20
compression_trigger: 0.85
auto_compact: true
system_prompt: |
Tu disposes de primitives d'exécution :
- run_parallel : actions indépendantes en parallèle
- background_run : tâches longues non bloquantes
Utilise run_parallel pour les actions indépendantes.
Utilise background_run pour les téléchargements et opérations lentes.
tools:
modules:
filesystem:
constraints:
allowed_actions: [read, glob, grep, write]
bash:
constraints:
allowed_actions: [run]
http:
constraints:
allowed_actions: [request, download, upload]
database:
config:
databases:
- name: test_db
kind: postgres
dsn: "{{secret.TEST_DATABASE_URL}}"
security:
mode: read_write
constraints:
allowed_actions: [connect, query, disconnect]
capabilities:
default_policy: approve
ui:
greeting: "Assistant polyvalent : exécution parallèle + tâches background."
dev:
variables:
workspace: "{{env.PWD}}"
Key takeaways:
- Modules such as filesystem, bash, http, database can load together.
- Execution primitives:
run_parallelandbackground_run(one tool, several modes) come from auto-loadedcontext_builder. There are no separatebackground_status/background_result/background_cancel/background_list/background_waittools. default_policy: approvemakes tool calls ask for confirmation by default when configured that way.
11 · Monitoring bot - channels + scheduler
A background-oriented monitor. Prefer channel / trigger cron and
scheduler.schedule for wake-ups. Do not rely on
runtime.watchers or fictional watch_* / cancel_schedule tools.
app:
app_id: monitoring-bot
name: Monitoring Bot
runtime:
mode: background
entry_agent: monitor
default_channel: slack_alerts
max_turns: 200
timeout: 3600
workdir: "{{env.PWD}}"
agents:
- id: monitor
role: assistant
brain:
provider: openai
model: gpt-4o-mini
backend: openai_compat
config: {api_key: "{{secret.OPENAI_API_KEY}}"}
system_prompt: |
You are a monitoring agent.
Use http.request for health checks.
Use scheduler.schedule for recurring session wake-ups when needed.
Use memory.remember for durable notes.
tools:
modules:
http: {}
scheduler: {}
filesystem:
constraints:
allowed_actions: [read, glob, grep]
memory: {}
channels:
config:
providers:
cron_health:
adapter: cron
enabled: true
config:
schedule: "*/5 * * * *"
activation:
agent: monitor
message: "Run a health check on all monitored endpoints."
reply: none
capabilities:
default_policy: auto
grant:
- module: http
- module: scheduler
- module: filesystem
- module: memory
channels:
slack_alerts:
type: webhook
config:
url: "{{secret.SLACK_WEBHOOK_URL}}"
payload_template: |
{"text": "{{event.message}}", "channel": "#production-alerts"}
audit:
type: log
config:
logger_name: digitorn.audit
level: INFO
format: json
include_data: true
ui:
greeting: |
Monitoring bot prêt. Je peux surveiller, planifier, alerter Slack ou journaliser.
Que veux-tu surveiller ?
Key takeaways:
- Prefer
runtime.triggerscron and/orscheduler.schedulefor recurring work.runtime.watchers/runtime.schedulerflags do not register watcher tools. runtime.default_channel: slack_alertssets a default outbound channel when the runtime supports it for that app mode.tools.channels:declares channel instances (herewebhookandlog). See Channels.- When no client is connected, delivery behavior depends on the channel / background path; do not assume a fixed KV buffer size from older docs without checking the daemon.
12 · MCP - multiple external servers
An agent connected to three MCP servers (Slack, GitHub, Brave Search). Each server declares its OS sandbox permissions.
app:
app_id: mcp-multi
name: MCP Multi-Server Agent
runtime:
mode: conversation
max_turns: 200
agents:
- id: assistant
role: assistant
brain:
provider: openai
model: gpt-4o
backend: openai_compat
config: {api_key: "{{secret.OPENAI_API_KEY}}"}
context:
max_tokens: 128000
strategy: summarize
keep_recent: 20
auto_compact: true
system_prompt: |
You are connected to Slack, GitHub, and Brave Search via MCP.
Use search_tools to discover virtual MCP tools.
tools:
modules:
filesystem:
constraints:
allowed_actions: [read, glob, grep]
mcp:
config:
servers:
slack:
transport: stdio
command: npx
args: ["-y", "@modelcontextprotocol/server-slack"]
env:
SLACK_BOT_TOKEN: "{{secret.SLACK_BOT_TOKEN}}"
sandbox:
permissions: [process.exec, net.http]
allowed_hosts: [slack.com, api.slack.com]
github:
transport: stdio
command: npx
args: ["-y", "@modelcontextprotocol/server-github"]
env:
GITHUB_PERSONAL_ACCESS_TOKEN: "{{secret.GITHUB_PAT}}"
sandbox:
permissions: [process.exec, net.http]
allowed_hosts: [api.github.com, github.com]
brave:
transport: stdio
command: npx
args: ["-y", "@modelcontextprotocol/server-brave-search"]
env:
BRAVE_API_KEY: "{{secret.BRAVE_API_KEY}}"
sandbox:
permissions: [process.exec, net.http]
allowed_hosts: [api.search.brave.com]
capabilities:
default_policy: auto
grant:
- module: mcp
- module: filesystem
# Tighten with allowed tool name patterns in your deploy if needed.
ui:
greeting: "3 serveurs MCP connectés : Slack, GitHub, Brave."
Key takeaways:
- Virtual MCP tools are exposed as
mcp_<server>__<tool>names under themcpmodule (not as separate compile-time modules). - Per-server sandbox with
permissions(+allowed_hostswhen needed) is mandatory for inline server blocks (deny-by-default). Catalog shorthandid: {}can omit it. search_toolsfinds MCP tools the same way it finds native ones.
13 · MCP with OAuth2 - Google Calendar (SSE transport)
OAuth2 with PKCE for per-user authorization. SSE transport injects the bearer token via HTTP header.
app:
app_id: mcp-oauth-demo
name: Calendar Assistant
runtime:
mode: conversation
agents:
- id: assistant
role: assistant
brain:
provider: openai
model: gpt-4o
backend: openai_compat
config: {api_key: "{{secret.OPENAI_API_KEY}}"}
system_prompt: |
Tu as accès au Google Calendar et à Slack.
Si un outil requiert une autorisation OAuth, présente le lien.
tools:
modules:
mcp:
config:
servers:
google_calendar:
transport: sse
url: http://localhost:3000/sse
auth:
type: oauth2
provider: google
client_id: "{{secret.GOOGLE_CLIENT_ID}}"
client_secret: "{{secret.GOOGLE_CLIENT_SECRET}}"
scopes:
- https://www.googleapis.com/auth/calendar.readonly
- https://www.googleapis.com/auth/calendar.events
sandbox:
permissions: [net.http]
allowed_hosts: [www.googleapis.com, oauth2.googleapis.com]
slack:
transport: stdio
command: npx
args: ["-y", "@anthropic/mcp-server-slack"]
env:
SLACK_TOKEN: "{{secret.SLACK_BOT_TOKEN}}"
sandbox:
permissions: [process.exec, net.http]
allowed_hosts: [slack.com]
capabilities:
grant:
# Virtual MCP tools are namespaced under the mcp module at
# runtime (e.g. mcp_google_calendar__list_events). Grant the
# mcp module; tighten with tool-name patterns in production.
- module: mcp
ui:
greeting: "Assistant Calendar + Slack prêt."
Key takeaways:
- OAuth2 + PKCE for SSE / HTTP transports - token sent in
Authorization: Bearer ...header. - Mixed auth models - Google = OAuth2, Slack = static bot token via the credentials vault.
- There are no separate compile-time modules named
mcp_google_calendar/mcp_slack; those ids are runtime namespaces for virtual tools undermcp. - Auto-refresh - the OAuth refresh loop renews tokens within 10 min of expiry (credentials.md).
requires_oauthflow - when the user hasn't yet authorised, the tool result carries anauth_urlthe agent surfaces.
14 · MCP with OAuth2 - Notion (stdio + env_token_var)
stdio transport injects the OAuth token as an environment variable and restarts the subprocess when the token refreshes.
app:
app_id: notion-agent
name: Notion Agent
runtime:
mode: conversation
max_turns: 200
timeout: 1200
workdir: "{{env.PWD}}"
agents:
- id: assistant
role: assistant
brain:
provider: openai
model: gpt-4o-mini
backend: openai_compat
config: {api_key: "{{secret.OPENAI_API_KEY}}"}
context:
max_tokens: 128000
strategy: summarize
keep_recent: 20
auto_compact: true
system_prompt: |
Tu es connecté au workspace Notion de l'utilisateur.
Tu peux rechercher, lire et modifier ses pages et bases de données.
tools:
modules:
mcp:
config:
servers:
notion:
transport: stdio
command: mcp-notion
args: []
auth:
type: oauth2
provider: notion
client_id: "{{secret.NOTION_CLIENT_ID}}"
client_secret: "{{secret.NOTION_CLIENT_SECRET}}"
env_token_var: NOTION_API_KEY # token injected as env var
redirect_uri: http://localhost:8913/callback
sandbox:
permissions: [process.exec, net.http]
allowed_hosts: [api.notion.com]
capabilities:
default_policy: approve
ui:
greeting: |
Agent Notion prêt ! Première connexion : tu dois autoriser l'accès
à ton workspace Notion (1 clic).
Key takeaways:
env_token_var: NOTION_API_KEY- the field that bridges OAuth2 tokens into stdio MCP servers. The daemon restarts the subprocess when the token refreshes.- Notion provider - pre-configured in the credentials catalog (Basic auth for token exchange, JSON body - not form-encoded).
- Local OAuth callback - in standalone mode, a temporary
HTTP server on the configured
redirect_uriport handles the callback and opens the browser automatically. - User-scoped sharing - the user must select the pages / databases to share at authorisation time. Skipping that step yields an empty workspace; modify later in Notion → Settings → My connections.
Cross-references
- Block-by-block YAML reference: App Configuration
- Agents + brains + sub-agent dispatch (
Agenttool, 8 modes): Agents - Triggers + routing modes: Triggers, Background Sessions
- Channels (webhook, log, slack, email, ...) with notification buffering: Channels
- Security model + capabilities resolution: Security
- Credentials vault, OAuth flow, KMS: credentials.md
- Behavior engine (declarative rules + classifier): Behavior Engine
- Calling third-party HTTP APIs from an app: External APIs