MaestroBot is managed as a Linux systemd --user service.

Install

For a normal user install:

bash install.sh
bash install.sh --shell bash --shell zsh --shell fish

That builds maestrobot, installs it to ~/.local/bin, bootstraps ~/.maestrobot when needed, and installs the systemd --user unit. Without --shell, the installer writes completions for the user’s default login shell when that shell is bash, zsh, or fish. When --shell is provided, the installer writes completion files for the requested shell or shells instead. Unsupported shells are rejected. If the installer cannot reach the systemd --user bus, it still writes the unit files and prints the systemctl --user commands to run later from a normal login session.

Initialize

maestrobot init --workspace-root . --openrouter-api-key "$OPENROUTER_API_KEY" --model "$MAESTROBOT_MODEL"
maestrobot init --workspace-root . --openrouter-api-key "$OPENROUTER_API_KEY" --model "$MAESTROBOT_MODEL" --telegram-bot-token "$TELEGRAM_BOT_TOKEN"

After install or init, normal operator setup is editing two files:

$EDITOR ~/.maestrobot/config.yaml
$EDITOR ~/.maestrobot/SOUL.md

config.yaml is the deployment config: credentials, model presets, frontends, onboarding policy, Myria generation, runtime limits, and optional MCP tools. SOUL.md is the agent’s identity, voice, and standing preferences. Avoid putting identity text in config.yaml.

Run the setup doctor before starting or after any config change:

maestrobot doctor

doctor validates config loading, SOUL.md, runtime binaries, derived Myria startup, user service reachability, model preset availability and compatibility, and Telegram bot credentials when a Telegram frontend is configured. Use maestrobot doctor --skip-network for a local-only check.

If you cloned the repo fresh, run make git-hooks once before normal development so checkout/merge hooks keep maestro/ and myria/ submodules synchronized.

Start

maestrobot daemon start --verbose

daemon start writes or updates ~/.config/systemd/user/maestrobot.service, runs systemctl --user daemon-reload, and enables/starts the unit.

Add --debug when you want frontend-visible gateway telemetry:

maestrobot daemon start --verbose --debug

--verbose controls daemon log verbosity. --debug sends compact operator messages through configured frontends for tool calls, sanitized parameters, tool results, cumulative LLM token usage, prompt compaction, and final sleep/wake bookkeeping. These messages are telemetry only: they are not written to the channel transcript and are not emitted to Myria as conversation events.

The daemon then:

  1. loads config.yaml, runtime.yaml, and state.json
  2. ensures the root-local maestro/ source files exist
  3. compiles root/maestro/*.mstr into a runtime artifact when needed
  4. generates the Myria template and config files from config.yaml
  5. starts Myria as a supervised stdio subprocess
  6. starts any enabled external MCP servers
  7. starts any configured frontends such as Telegram
  8. starts the multi-worker channel scheduler
  9. serves the Unix socket control plane

The generated Myria config uses SQLite by default in the current MaestroBot version only as a convenience path for local setup. myria/myria.json is derived from config.yaml and is regenerated by MaestroBot, so do not treat it as the source of truth.

Stop

maestrobot daemon stop

Status And Logs

maestrobot daemon status
maestrobot daemon logs --lines 100
maestrobot status
maestrobot config show

daemon status reports both systemd --user state and daemon IPC reachability. daemon logs reads from journalctl --user-unit maestrobot.

Channel Operations

Register a channel:

maestrobot channels add local demo
maestrobot channels add telegram <telegram-chat-id>
maestrobot add-channel local demo
maestrobot add-channel telegram <telegram-chat-id>

Queue a normalized inbound message:

maestrobot channel send channel-0001 "hello"

Deliver a canonical outbound message directly through the channel frontend:

maestrobot channel deliver channel-0001 "**hello** from MaestroBot"

Use the local CLI chat helper:

maestrobot chat "hello"
maestrobot chat --channel demo
maestrobot chat --channel demo --verbose "read /notes.txt and report back"

chat is black-box. It only talks to the daemon through Unix IPC. chat --verbose still stays black-box from the CLI side, but it tails only matching daemon trace events from logs/service.log.

channel deliver is the simplest operator-facing way to test frontend rendering and delivery without waiting for a live model reply.

The verbose trace boundary is one full channel run:

  • it starts when the channel leaves a finalized/waiting boundary and a new active run begins
  • it ends when the runtime reaches the next finalizing boundary

That means one verbose trace may cover more than one inbound message if the current run consumes them before finalization. The streamed daemon events include the built prompt, model/preset selection, raw LLM response payload, tool execution results, and finalization.

The built prompt now comes from the root-local Maestro surface:

  • state-machine source in ~/.maestrobot/maestro/*.mstr

So chat --verbose exposes both the host-provided runtime sections and the current user-editable Maestro schema boundary, including prompt text embedded directly in the .mstr source.

Verbose traces also include the schema-requested tool policy and context section list. That is the quickest way to confirm whether a behavior is coming from the Maestro schema surface or from host-side tool execution.

Telegram-specific output formatting is handled later by the Telegram frontend. The canonical runtime output remains Markdown.

For Telegram-backed channels, the frontend emits a typing chat action while the channel run is actively executing. The typing indicator stops when the reply is delivered or the run finalizes without sending one.

Probe every configured preset directly without the daemon:

maestrobot test

This uses a plain hello world probe for availability and a single-tool probe for agent compatibility. It is useful when a model is reachable for plain completions but a particular OpenRouter route does not currently support tool parameters. daemon start runs the same check before it tries to start the user service.

Model presets may configure an OpenRouter sort value, but MaestroBot does not pin concrete providers. Runtime calls let OpenRouter choose the route for the configured model and sort mode.

Probe provider-side prompt caching:

maestrobot models cache

This sends the same long stable prompt twice for each preset and reports cached_tokens, cache_write_tokens, latency, and resolved provider metadata. A zero cache hit usually means the selected provider/model route does not support prompt caching, the prompt is below that provider’s cache threshold, or the provider did not reuse the same cache backend.

Inspect transcript history:

maestrobot channel history channel-0001

The local channel transcript is also the daemon’s canonical recent same-channel conversation source. Myria remains global persistent memory for older, cross-channel, or uncertain recall.

Wake or force-page a channel:

maestrobot channel wake channel-0001
maestrobot channel page channel-0001

channel wake schedules a host/runtime wake, not a synthetic user message. The next run sees it as a runtime wake in retained wake metadata. Scheduled automatic wakes created by the agent carry a private wake self-note from the prior run. That self-note is context for the next run, not a user-facing message. Agent finalization always schedules a future wake. If there is no concrete future work or useful check, the agent should schedule a long wake and tell its future self not to message the user if nothing changed.

Pause or resume the scheduler:

maestrobot runtime show
maestrobot runtime reload
maestrobot runtime pause
maestrobot runtime resume

Identity Operations

Internal users live in runtime.yaml. External account mappings and unknown account discoveries live in daemon-owned state. An unknown account cannot trigger the agent until it is attached to an internal user.

Create an internal user:

maestrobot user onboard "Ada Lovelace"

List known internal users:

maestrobot user list

List unresolved active identities:

maestrobot user unknown
maestrobot list-unknown-identities

List archived unresolved identities:

maestrobot user unknown --archive

Attach one unresolved identity to a stable internal user:

maestrobot user attach usr_k7m4x9q2p8vd unknown-0001

Rename an internal user:

maestrobot user rename usr_k7m4x9q2p8vd "Ada Byron"

The old associate command remains as a compatibility shorthand:

maestrobot associate unknown-0001 "Ada Lovelace"

The preferred flow is user onboard followed by user attach, because internal account creation and platform-account linking are separate operations.

Unknown Telegram accounts receive the deterministic onboarding notice from the runtime/frontend gate, not from the agent. The default notice limit is three messages per unknown account. After that, messages from the same unknown account are ignored until attachment. Unknowns move to the archive view after the configured archive window, which defaults to 24 hours from last seen.

Workspace Inspection

maestrobot workspace path channel-0001
maestrobot workspace mounts channel-0001

The runtime presents a VFS view with:

  • / writable channel workspace
  • /.host-path/<n> read-only host PATH mounts

The built-in developer tools use that same VFS and lease system:

  • internal.fs_stat, internal.fs_read, internal.fs_read_window, internal.fs_find, internal.fs_grep, internal.fs_replace
  • internal.exec, internal.exec_start, internal.exec_poll, internal.exec_stdin, internal.exec_wait, internal.exec_kill

Use internal.fs_read_window for large files. It reads a 1-based inclusive line range and returns the total line count plus the next start line, so the agent can inspect repositories incrementally without loading whole files into context.

Long-running command sessions keep the channel workspace lease until they exit, so side effects remain host-serialized across the main agent and subagents.

Browser Support

Install the Playwright driver and Chromium into the runtime root:

maestrobot browser install

Probe a live browser session directly:

maestrobot browser probe https://example.com

The agent-facing browser tools are:

  • internal.browser_open
  • internal.browser_navigate
  • internal.browser_click
  • internal.browser_type
  • internal.browser_wait
  • internal.browser_extract
  • internal.browser_eval
  • internal.browser_screenshot
  • internal.browser_close

Separate non-browser retrieval helpers also exist:

  • internal.web_fetch
  • internal.web_search
  • internal.image_info
  • internal.image_ocr

Tool Server Management

List known servers:

maestrobot tools list

Discover MCP manifests and probe them for compatibility:

maestrobot tools discover --path .

Register a new stdio MCP server and start it immediately:

maestrobot tools register-stdio example --description "Example MCP server" --enable=true -- /abs/path/server --stdio

Register a remote MCP server and connect to it immediately:

maestrobot tools register-remote remote-example --transport streamable-http --url https://example.invalid/mcp --enable=true

Import a discovered server from a manifest and start it immediately:

maestrobot tools import-manifest /abs/path/mcp.json example --enable=true

Inspect one configured server and re-probe its exposed tools:

maestrobot tools inspect example

Toggle or remove it:

maestrobot tools enable example
maestrobot tools disable example
maestrobot tools remove example

Registration updates config.yaml and live daemon state. Removal stops the server, drops the runtime record, and removes the config entry.

Discovery/import currently supports:

  • dedicated JSON MCP manifests such as mcp.json, .mcp.json, and mcp-server.json
  • Claude-style claude_desktop_config.json files with mcpServers

Supported execution transports are:

  • stdio
  • streamable-http
  • sse