schema_version = 3 # --------------------------------------------------------------------------- # Providers # --------------------------------------------------------------------------- # "cloud" primary. During bring-up this points at the local claude_shim # (adb-reverse tunnel on :8090). For a real fleet, swap in a shared cloud # endpoint + key, e.g.: # uri = "https://api.anthropic.com/v1" (or an OpenRouter/LiteLLM gateway) # api_key = "..." (prefer an env-injected key) [providers.models.custom.cloud] uri = "__CLOUD_URI__" model = "__CLOUD_MODEL__" native_tools = false # Same cloud endpoint, but with an on-board Qwen fallback ("cloud first, # local if it fails"). Used by the `default` agent. [providers.models.custom.claude] uri = "__CLOUD_URI__" model = "__CLOUD_MODEL__" native_tools = false fallback = ["llamacpp.local"] # A deliberately-dead cloud endpoint (nothing listens on :9099) that fails over # to the on-board Qwen. Used by the `chaos` agent to DEMONSTRATE resilience: a # prompt routed here always finds the cloud unreachable and answers locally — # the "simulate cloud outage" demo (ADD L4 Harness: reasoning + tiering). [providers.models.custom.dead] uri = "http://127.0.0.1:9099/v1" model = "__CLOUD_MODEL__" native_tools = false fallback = ["llamacpp.local"] [providers.models.llamacpp] # On-board Qwen via llama-server (see zeroclaw-llama.service). [providers.models.llamacpp.local] uri = "http://127.0.0.1:8083/v1" model = "qwen" native_tools = false [providers.models.custom] # --------------------------------------------------------------------------- # Hardware — the Uno Q's onboard MCU over the GPIO bridge. # --------------------------------------------------------------------------- [[peripherals.boards]] board = "arduino-uno-q" transport = "bridge" [peripherals] enabled = true # --------------------------------------------------------------------------- # Gateway — the HTTP/WS/SSE surface APESS talks to, AND the embedded web # dashboard + chat UI a team opens directly at http://:8080. # --------------------------------------------------------------------------- [gateway] port = 8080 # LAN-open by default: participants reach the board's WiFi IP directly (the # "Open your node →" link in APESS). 0.0.0.0 + allow_public_bind make the # dashboard/web-chat reachable across the workshop subnet. host = "0.0.0.0" allow_public_bind = true # Open during SETUP so a team's browser can chat + edit config (paste a Telegram # token, etc.) with no token on the isolated workshop LAN. The reload-watcher # (loopback) applies any dashboard config edit. Run `zeroclaw-lockdown.sh` to # flip this on and mint a pair code once a team has finished setting up. require_pairing = false # paired_tokens are added by the pairing flow (lockdown / provision-uno-q.sh); # never commit a real token. [skills] prompt_injection_mode = "compact" # --------------------------------------------------------------------------- # Risk profile — only the on-board hardware tools, auto-approved so the agent # can flash without a human in the loop. # --------------------------------------------------------------------------- # read_skill + file_read are allowed AND auto-approved so agents can load the # bundled skills (e.g. arduino-uno-q) and read their references/*.md on demand # without a human approver (the webhook path is non-interactive). [risk_profiles.default] level = "supervised" allowed_tools = ["matrix_pattern", "matrix_text", "i2c_scan", "uno_q_flash", "sysfs_led", "camera", "network", "i2cdetect", "read_skill", "file_read", "content_search"] auto_approve = ["matrix_pattern", "matrix_text", "i2c_scan", "uno_q_flash", "sysfs_led", "camera", "network", "i2cdetect", "read_skill", "file_read", "content_search"] [runtime_profiles.unoq] agentic = true max_tool_iterations = 6 strict_tool_parsing = false # --------------------------------------------------------------------------- # Agents — one per provider strategy. APESS routes here via ?agent=; callers # pass the alias explicitly (see sendPrompt in src/lib/api.ts). # --------------------------------------------------------------------------- # THE ONE workshop agent. Everything routes here — web say-hi, the module chat, # Refine, and Telegram — via the frontend's single `AGENT` constant (= "default") # and the daemon's no-alias fallback. It's fully loaded: cloud model + on-board # Qwen fallback, risk_profile "default" (all tools), skill_bundles "unoq" (all # skills). The other agents below are DISABLED so no interaction can land on the # wrong one; a stray request for a disabled alias falls back to this agent. [agents.default] # cloud + on-board Qwen fallback — the sole workshop agent enabled = true model_provider = "custom.claude" risk_profile = "default" runtime_profile = "unoq" # Modalities this agent answers on, beyond the APESS proxy + web chat: the team's # own Telegram bot. A team enables it from the dashboard (Config → channels → # telegram) by pasting their @BotFather token; the reload-watcher applies it. channels = ["telegram.default"] # Demo-mode variants — DISABLED for the workshop so there's exactly one agent. # Re-enable individually only if you specifically want to demo cloud-only / # offline-only / simulated-outage behaviour. [agents.cloud] # cloud only, no fallback enabled = false model_provider = "custom.cloud" risk_profile = "default" runtime_profile = "unoq" [agents.local] # on-board Qwen only (fully offline) enabled = false model_provider = "llamacpp.local" risk_profile = "default" runtime_profile = "unoq" # --------------------------------------------------------------------------- # Channels — extra ways a team talks to its node (all bind to `default`, above). # --------------------------------------------------------------------------- # Telegram: each board is its own agent, so each needs its own bot. A team # creates one live via @BotFather, then pastes the token in the dashboard # (Config → channels → telegram.default), sets enabled = true, and adds their # Telegram username to allowed_users. The reload-watcher restarts the listener. # Seeded disabled so the block shows up pre-bound in the config editor. [channels.telegram.default] enabled = false bot_token = "" # from @BotFather (paste via the dashboard) # allowed_users = ["your_tg_username"] # lock the bot to your team (no leading @) mention_only = false # Browser-mic streaming voice — talk to the agent inside the web chat, it speaks # back. Requires a daemon built with the voice feature (see README §Voice): # cargo xtask web build && cargo build --release \ # --features "hardware,peripheral-rpi,embedded-web,zeroclaw-gateway/gateway-voice-duplex" # Enable once the board runs a voice-capable binary: # [channels.voice_duplex.default] # enabled = true [agents.chaos] # simulated cloud outage → falls back to on-board Qwen enabled = false # DISABLED — see the single-agent note on [agents.default] model_provider = "custom.dead" risk_profile = "default" runtime_profile = "unoq"