Configuration

Project YAML reference

A Suzumio project is declared in YAML, resolved into one canonical config, and then stored with the project. The config describes the task, agents, message channels, Docker backend, scheduler, and model presets.

Resolution Pipeline

Suzumio treats configuration as source material, not as mutable runtime state. When you run suzumio init, the loader performs the same steps as suzumio config render and stores the result as resolved.yaml.

source YAML
  -> quote bare @import(...) markers
  -> substitute environment placeholders in text
  -> parse YAML
  -> resolve whole-field imports recursively
  -> apply extends profiles
  -> apply defaults and validate
  -> write resolved.yaml and SQLite project config

Use suzumio config render path/to/project.yaml before review or initialization. It is the easiest way to see the exact config Suzumio will run.

Minimal Project

name: demo
task: |
  Demonstrate one non-preemptive turn.

backend:
  runner:
    mode: mock

agents:
  pm:
    role: project-manager
    prompt: |
      Handle the user request and stay concise.
    tools:
      - messages.send

Complete Shape

The example below shows the main fields in one file. Most projects should split task text, long prompts, and reusable backend settings into imports or profiles.

name: research-demo
task: @import(tasks/main.md)

scheduler:
  kind: nonpreemptive-mailbox
  intervalMs: 2000
  maxPromptMessages: 20

channels:
  - "#project"
  - "#blocked"
  - "#reviews"

backend:
  kind: docker-chat
  image: suzumio-runner:dev
  controllerUrl: http://host.docker.internal:39400
  docker:
    network: bridge
  runner:
    mode: ai
    model: main
    models:
      default: main
      providers:
        gateway:
          type: openai-compatible
          baseURL: https://your-gateway.example/v1
          apiKeyEnv: SUZUMIO_GATEWAY_API_KEY
          timeoutMs: 300000
      presets:
        main:
          provider: gateway
          model: gpt-5.5
          apiModel: gpt-5.5
          toolChoice: auto
          maxOutputTokens: 2000

agents:
  pm:
    role: project-manager
    prompt: @import(prompts/pm.md)
    model: main
    tools:
      - messages.send
      - artifacts.list
      - completion.submit
  worker:
    role: worker
    count: 2
    prompt: @import(prompts/worker.md)
    model: main
    tools:
      - messages.send
      - artifacts.publish
      - artifacts.list

Top-level Fields

FieldRequiredDescription
nameYesProject id and runtime directory name under SUZUMIO_ROOT.
taskYesDurable task statement rendered into every turn prompt.
agentsYesMap of agent ids to agent configs. At least one agent is required.
extendsNoOne profile object or a list of profile objects to merge before local fields.
schedulerNoScheduler kind and prompt-message batching. Defaults to nonpreemptive-mailbox.
backendNoDocker runner image, ToolHost URL, Docker options, and model runner settings.
channelsNoAllowed channel names. Defaults include #project and #blocked.
observabilityNoDocumentation-level server defaults for HTTP/WebUI. The CLI flags still control the actual server bind address.

YAML Conventions

Suzumio uses ordinary YAML maps, arrays, scalars, and block strings. Keep long text in block strings or imported files so rendered prompts are readable.

PatternUse it forExample
Block scalarTasks and prompts with multiple lines.task: |
Quoted stringsChannel names and strings that contain punctuation."#project"
ArraysTools, channels, profile lists.- messages.send
MapsAgents, providers, presets, Docker options.agents: { ... }
task: |
  Write the final result as a short report.
  Mention any assumptions and artifacts.

channels:
  - "#project"
  - "#blocked"

Whole-field Imports

A field whose entire value is @import(path) is replaced by the imported file. This is the key rule: the import marker must occupy the whole field value. Suzumio does not support string interpolation inside a larger string.

task: @import(tasks/main.md)
agents:
  pm: @import(agents/pm.yaml)
  worker:
    prompt: @import(prompts/worker.md)

If an imported YAML or JSON file contains an object, that object becomes the value at the import site. If an imported Markdown or text file is used, its full text becomes the value at the import site.

Imported fileHow Suzumio reads itTypical use
.yaml or .ymlParsed as YAML, then imports inside it are resolved.Agents, backend profiles, scheduler profiles.
.jsonParsed as JSON, then imports inside it are resolved.Generated model preset maps or tool lists.
Other extensionImported as raw UTF-8 text.Tasks, prompts, report templates.

Import paths

Import paths are resolved relative to the file that contains the import, not relative to the process working directory. That means nested profile files can import their own neighboring fragments predictably.

# configs/project.yaml
agents:
  pm: @import(agents/pm.yaml)

# configs/agents/pm.yaml
role: project-manager
prompt: @import(../prompts/pm.md)

Valid and invalid import forms

FormResult
prompt: @import(prompts/pm.md)Valid. The field value is replaced by the file content.
pm: @import(agents/pm.yaml)Valid. The imported object becomes agents.pm.
prompt: "Read this: @import(x.md)"Invalid for import purposes. It remains an ordinary string; no interpolation is performed.
url: @import(https://example.com/x.yaml)Rejected. HTTP imports are disabled.

Nested object example

If agents/pm.yaml contains the fields below, importing it at agents.pm preserves that nesting exactly.

# agents/pm.yaml
role: project-manager
prompt: @import(../prompts/pm.md)
tools:
  - messages.send
  - completion.submit

# resolved shape
agents:
  pm:
    role: project-manager
    prompt: "...contents of prompts/pm.md..."
    tools:
      - messages.send
      - completion.submit

Import loops and excessive import depth are rejected so a project cannot accidentally create an infinite config expansion.

Extends and Merge Rules

extends is for reusable profiles. Each entry must resolve to an object. Suzumio merges profile objects from first to last, then merges the local file on top.

extends:
  - @import(profiles/base.yaml)
  - @import(profiles/ai.yaml)

name: theorem-project
task: @import(tasks/theorem.md)

agents:
  worker:
    count: 3
Merge caseBehavior
Object into objectDeep-merged recursively.
Array into arrayThe later array replaces the earlier array.
Scalar into any valueThe later scalar replaces the earlier value.
Local file vs profileThe local file wins.

Profile merge example

# profiles/base.yaml
backend:
  image: suzumio-runner:dev
  runner:
    mode: mock
channels:
  - "#project"
  - "#blocked"

# project.yaml
extends:
  - @import(profiles/base.yaml)
name: demo
task: @import(tasks/demo.md)
backend:
  runner:
    mode: ai
channels:
  - "#project"
  - "#reviews"

# important resolved effects
backend.image: suzumio-runner:dev
backend.runner.mode: ai
channels: ["#project", "#reviews"]

The backend object deep-merges, so backend.image remains from the profile while backend.runner.mode is overridden locally. The channels array is replaced, not appended.

Scheduler Config

scheduler:
  kind: nonpreemptive-mailbox
  intervalMs: 2000
  maxPromptMessages: 20
FieldDescription
kindOnly nonpreemptive-mailbox exists in the first version.
intervalMsIntended scheduler loop interval. The current server uses a fixed loop aligned with this default.
maxPromptMessagesMaximum unread inbound messages rendered into one turn prompt.

Backend Config

backend:
  kind: docker-chat
  image: suzumio-runner:dev
  controllerUrl: http://host.docker.internal:39400
  docker:
    network: bridge
  runner:
    mode: mock
FieldDescription
kindBackend implementation. The current backend is Docker-based.
imageDocker image used for each turn container.
controllerUrlURL the container uses to call Suzumio ToolHost. For local Docker, use host.docker.internal.
docker.networkOptional Docker network mode.
runner.modemock for infrastructure tests, ai for model-backed turns.

AI Runner Config

backend:
  runner:
    mode: ai
    model: main
    models:
      default: main
      providers:
        gateway:
          type: openai-compatible
          baseURL: https://your-gateway.example/v1
          apiKeyEnv: SUZUMIO_GATEWAY_API_KEY
          timeoutMs: 300000
      presets:
        main:
          provider: gateway
          model: gpt-5.5
          apiModel: gpt-5.5
          toolChoice: auto
          maxOutputTokens: 2000

Committed examples must stay sanitized. Put real provider endpoints and keys in local untracked config and environment variables.

Agent Config

agents:
  worker:
    role: worker
    count: 2
    prompt: @import(prompts/worker.md)
    model: main
    tools:
      - messages.send
      - artifacts.publish
      - artifacts.list
FieldDescription
roleHuman-readable role stored with the agent.
countExpands one config entry into numbered agents such as worker-1 and worker-2.
promptAgent instructions included in every turn prompt. Usually imported from Markdown.
modelOptional model preset override for AI mode.
toolsAllowed tool names for this agent.
envAdditional environment variables for runner containers.

Channels

channels:
  - "#project"
  - "#blocked"
  - "#reviews"

Channel messages to undeclared channels fail. This prevents accidental creation of noisy or misspelled channels.

Validation Workflow

suzumio config render path/to/project.yaml
suzumio init path/to/project.yaml
suzumio status project-name

Render configs in code review, especially when using multiple profiles. Check the resolved output for unexpected array replacement, unexpected inherited model settings, and private endpoints that should stay local.