Skip to content

Core Concepts

This page defines the mental model behind mono-agent: one config file, one agent definition, per-channel responders, and explicit runtime boundaries. The config tool surface defaults open; sandboxing and channel allowlists are the separate controls that constrain side effects, and guided init reconfirms allow-all when no sandbox is selected. Read it once and the rest of the docs will line up.

A mono-agent is fully described by a single mono-agent.config.json in the agent folder. The CLI does not watch that file: you (or an agent) edit the JSON, then run mono-agent restart to load the new config. An embedded app can instead call app.applyConfigChange(reason) explicitly to rebuild the current services and reconfigure its already-resolved drivers from the edited config; adding or removing a plugin package still requires a process restart.

{
"runtime": {
"model": "openai-codex:gpt-5.6-terra"
},
"context": {
"identityPath": "./IDENTITY.md"
}
}
Terminal window
mono-agent restart # apply config edits
mono-agent restart --clear-sessions # apply and clear provider/history/ACP continuity (durable memory kept)

Because the config is plain JSON, agents can edit their own config and restart themselves. Most capabilities are coverage type config — set a key, then restart the CLI host or explicitly re-apply an embedded app. A few are cli (run a command), auto (default behavior), code (only available programmatically — see Programmatic), or dev (test-time tooling).

The full annotated config lives in Configuration → Blueprint, and folder conventions in Folder Layout.

One agent definition, per-channel responders

Section titled “One agent definition, per-channel responders”

Each active channel gets its own configured responder and runtime harness — the components that turn an incoming prompt into a reply using the configured model, tools, context, and memory. Those responders are built from the same resolved agent config and share app-owned resources such as the configured memory store, but each harness has its own admission, session, and lifecycle boundary. Channels feed prompts into their own responder and deliver its output:

ChannelSectionTransport
Telegramtelegramlong-polling bot
SlackslackSocket Mode bot
WhatsAppchannels.plugins[] (@mono-agent/whatsapp-adapter)Baileys socket (QR login)
WebhookwebhookHTTP POST, sync/async
OpenAI APIopenaiApiOpenAI-compatible /v1/chat/completions
A2Achannels.plugins[] (@mono-agent/a2a-adapter)Agent-to-Agent provider
Croncronscheduled prompts

Each channel is its own JSON section and runs independently — one failing or waiting on config never blocks the others. This per-channel harness boundary is also why configured concurrency limits apply per channel rather than globally. See Channels for per-channel setup.

Communication channels are off by default and turn on with their enabled flag. The loopback tui operator endpoint and read-only live relay are deliberate exceptions: both default on with ephemeral ports and opt out with enabled: false. Put credentials such as MONO_AGENT_TELEGRAM_BOT_TOKEN in .env; the source-config example omits them:

{
"telegram": {
"enabled": true,
"allowedChatIds": ["123456789"]
}
}

When you run mono-agent start, each channel prints exactly one status line:

StatusMeaning
disabledThe resolved enabled value is false. For most channels omission resolves false; tui and live require an explicit false because they default on.
waiting_for_configEnabled but a required setting is missing. The start line names the exact missing field.
runningEnabled and configured; the line shows its endpoint facts.
degradedWas running, but the live transport hit a transient failure (e.g. the Telegram poller crashed on a network switch / ENETUNREACH). The channel owns its own recovery, so the responder/harness stays alive and keeps serving while the transport restarts; the line shows degraded: <reason> with a warning badge. It flips back to running once the restarted transport stays up.
failedEnabled and configured but it could not start (or hit a fatal error); the line shows the reason. Unlike degraded, this is terminal — the responder is disposed and there is no auto-restart.

An enabled-but-incomplete channel reports waiting_for_config rather than crashing the process — the rest of the agent keeps serving. A degraded channel is non-fatal too: it is still serving and self-recovering, distinct from a failed channel.

mono-agent ships with an open tool surface. Memory, channel admission, HTTP bind, and sandbox controls are separate; do not mistake one for another. Guided init names the shell/file/web/channel effects of allow-all and requires a second confirmation when no enforceable sandbox will constrain them. Native mono-agent SRT applies to Pi-owned tools.

  • Allow-all tools, runtime-specific narrowing. Omit tools.allowedTools (or include "*" anywhere in it) and the agent can call every built-in available on its route (the managed Read, Write, Edit, Glob, Grep, Exec, Bash, NodeRepl, WebFetch, WebSearch set on the Pi runtime) and every enabled channel’s send tools. See Tools → Policy.

    {
    "tools": {
    "allowedTools": ["*"],
    "disallowedTools": []
    }
    }
  • No memory writes. memory.writeMode defaults to disabled — the agent records nothing until you choose append-host-summary or (bujo only) capture. See Memory → Capture and Recall.

  • Loopback-only network. HTTP channels (webhook, openaiApi, and the A2A plugin) bind to localhost and refuse non-loopback callers until you set allowNonLoopback: true. For Pi-owned tools, the native sandbox likewise starts with network mode: "none" and a deny-by-default filesystem (.env*, .git/config, .git/hooks/** are denied even when you widen the roots). See Tools → Sandbox.

Configuration precedence: env > JSON > defaults

Section titled “Configuration precedence: env > JSON > defaults”

Fields with a documented MONO_AGENT_* environment mapping use this resolution order; JSON-only fields stay in mono-agent.config.json:

  1. Process environment (MONO_AGENT_*) — highest priority.
  2. mono-agent.config.json — the JSON value.
  3. Built-in default — used when neither is set.

So MONO_AGENT_MODEL=opencode-go:kimi-k2.6 overrides runtime.model in the JSON for that process. A .env file in the agent folder is loaded automatically (exported shell variables still win); use --env-file <path> for an alternate file.

Config keyEnv var
runtime.modelMONO_AGENT_MODEL
tools.allowedToolsMONO_AGENT_ALLOWED_TOOLS
memory.writeModeMONO_AGENT_MEMORY_WRITE_MODE
telegram.enabledMONO_AGENT_TELEGRAM_*

The complete key → env mapping is in Configuration → Env Vars.