Prototype #1 of the APESS innovation set: make the Uno Q's unique story — a real LLM running on the board — visible and demonstrable. A team can inject a cloud outage and watch their agent keep reasoning on the on-board Qwen, which is exactly ADD Layer 4 (failure modes) / Layer 5 (edge vs cloud) made concrete instead of merely documented. - New `fallback` NodeActivityKind (api + client, kept in sync). `mapNodeEvent` now recognizes ZeroClaw's failover log lines ("ModelProvider call failed", "Exhausted retries, trying next model") and surfaces them as a first-class resilience signal — NOT swallowed by the generic error branch. Rendered in a distinct rose in the board-activity feeds. - ResiliencePanel (Module 2): a "Simulate cloud outage" button that routes a prompt through the board's new `chaos` agent; streams the live failover and shows a "survived" banner when a fallback is followed by a response. Sim mode plays a deterministic failover so it demos with zero hardware. - Board config: a `chaos` agent backed by a deliberately-dead cloud endpoint (:9099) with `fallback = ["llamacpp.local"]`, so the outage is deterministic and workshop-safe (no tunnel-hacking, no real cloud to kill). Tests: api 34, front-end 191 (+ResiliencePanel), typecheck clean, build passes. Live-on-board validation pending (board USB link down at commit time). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
110 lines
3.9 KiB
TOML
110 lines
3.9 KiB
TOML
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" button in APESS (Module 2, failure modes / L4).
|
|
[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.
|
|
# ---------------------------------------------------------------------------
|
|
[gateway]
|
|
port = 8080
|
|
# Bind 0.0.0.0 + allow_public_bind for a real LAN fleet (participants reach the
|
|
# board's WiFi IP). Leave default (localhost) when reaching it over adb-forward.
|
|
# host = "0.0.0.0"
|
|
# allow_public_bind = true
|
|
# paired_tokens are added by the pairing flow (see 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.
|
|
# ---------------------------------------------------------------------------
|
|
[risk_profiles.default]
|
|
level = "supervised"
|
|
allowed_tools = ["uno_q_flash", "sysfs_led", "camera", "network", "i2cdetect"]
|
|
auto_approve = ["uno_q_flash", "sysfs_led", "camera", "network", "i2cdetect", "file_read", "content_search"]
|
|
|
|
[runtime_profiles.unoq]
|
|
agentic = true
|
|
max_tool_iterations = 6
|
|
strict_tool_parsing = false
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Agents — one per provider strategy. The APESS harness choice routes here via
|
|
# ?agent= (see harnessToAgent in src/lib/harness.ts).
|
|
# ---------------------------------------------------------------------------
|
|
[agents.default] # cloud + on-board Qwen fallback
|
|
enabled = true
|
|
model_provider = "custom.claude"
|
|
risk_profile = "default"
|
|
runtime_profile = "unoq"
|
|
|
|
[agents.cloud] # cloud only, no fallback
|
|
enabled = true
|
|
model_provider = "custom.cloud"
|
|
risk_profile = "default"
|
|
runtime_profile = "unoq"
|
|
|
|
[agents.local] # on-board Qwen only (fully offline)
|
|
enabled = true
|
|
model_provider = "llamacpp.local"
|
|
risk_profile = "default"
|
|
runtime_profile = "unoq"
|
|
|
|
[agents.chaos] # simulated cloud outage → falls back to on-board Qwen
|
|
enabled = true
|
|
model_provider = "custom.dead"
|
|
risk_profile = "default"
|
|
runtime_profile = "unoq"
|