Skip to main content

Agent Configuration Reference

The Agent daemon takes -dir, the data directory it works out of. agent.toml is only one of the things that live there: skills, memory, cache and logs are all resolved relative to it (see Directory layout). The config, config-check and config-test subcommands take -config with the path to a TOML config file. Every field below lives in agent.toml. Most fields can be edited through the on-device Config Web page; sections without dedicated controls are preserved by Config Web and can be edited by hand. TOML is the only supported config format; JSON config is deprecated.

Contents

Directory layout

Passed to the daemon as -dir /userdata/agent. Everything except agent.toml is created on demand, so a directory holding only agent.toml is a valid start.

/userdata/agent/
├── agent.toml # required
├── quick_actions.json # optional, falls back to the bundled defaults
├── skills/ # optional, auto-discovers **/SKILL.md
├── skill-state/ # bundled skill sync manifest
├── memory/ # conversation memory persistence directory
│ └── extraction.yaml # optional memory extraction overrides
├── cache/ # provider model metadata cache
├── log/ # runtime log directory
└── board_id # generated on first run when live activity is on

Config Web: the device config page

config_web is a lightweight C++ web service for maintaining the device Agent configuration, system environment variables, and Wi-Fi configuration. It is the primary way to edit the fields documented on this page without manually editing agent.toml.

On a device, open the config page in a browser at the USB-network gateway address:

http://192.168.42.1

The firmware starts config_web on port 80.

What the page can configure

The page fields cover the following config sections (all detailed later on this page). The language selector in the page header persists the device-level locale; switching it immediately updates the Config Web UI and restarts the Agent. If the locale changes the system prompt, startup creates a new context session instead of rewriting the previous session, so subsequent LLM responses use the selected language while old session history remains append-only.

  • agent: locale, input_mode, trigger_mode, VAD params, load_all_tools, max_iterations, custom_instruction, additional_prompt
  • model: provider, model, api_key, base_url, temperature, max_response_tokens, context_window, model_max_output_tokens. context_window = 0 means auto-discover from OpenRouter/Ollama metadata when available.
  • stt: provider, api_key, model, base_url, Tencent ASR fields
  • tts: provider, api_key, model, voice_id, emotion, speed
  • audio: socket, sample_rate, channels, bit_width, playback_backend
  • voice_notifications: preserved by Config Web when other settings are saved; dedicated form controls are not currently rendered
  • log: LLM HTTP log retention
  • device: device_type
  • hid: keyboard_device, keyboard_layout, mouse_device, android_keyboard_device, frame_socket, input_backend
  • env: shell-style environment text written to /userdata/system/env, including optional proxy variables such as http_proxy, HTTPS_PROXY, and NO_PROXY
  • Wi-Fi: SSID / PSK etc. (written to /userdata/wpa_supplicant.conf)

Minimal config examples

HTTP/Web UI without the device voice loop (text)

locale = "zh-CN"
custom_instruction = ""
max_iterations = -1
screenshot_keep_n = 3
screenshot_prune_interval = 2
input_mode = "text"

[model_providers.openrouter-main]
type = "openrouter"
api_key = "$OPENROUTER_API_KEY"

[device]
device_type = "iOS"

[model]
provider = "openrouter-main"
model = "bytedance-seed/seed-2.0-lite"
temperature = 0.2
max_response_tokens = 1000
# Optional model metadata overrides. Leave unset or 0 for provider metadata auto-discovery when available.
# context_window = 128000
# model_max_output_tokens = 8192

[audio]
socket = "/run/audio_service/audio_service.sock"
sample_rate = 16000
channels = 1
bit_width = 16
playback_backend = "auto"

[log]
llm_http_retention_days = 7

[hid]
keyboard_device = "/dev/hidg0"
keyboard_layout = "qwerty"
mouse_device = "/dev/hidg1"
android_keyboard_device = "/dev/hidg2"
frame_socket = "/run/frame_service/frame_service.sock"

Provider credentials use one field everywhere. Set api_key = "$VAR_NAME" to read from an environment variable, or set a literal key directly. Config Web accepts the same two forms in its API Key box.

STT voice mode

locale = "zh-CN"
custom_instruction = ""
input_mode = "stt"
trigger_mode = "manual"
vad_backend = "rknn"
vad_model_path = "/oem/usr/model/silero_vad_6_2_encoder_rv1106_w8a8_v1.rknn"
vad_helper_path = "/oem/usr/bin/rknn_vad"
vad_speech_threshold = 0.5
silence_ms = 550
min_speech_ms = 300
voice_followup_enabled = false
voice_followup_timeout_ms = 5000
voice_first_turn_timeout_ms = 10000
voice_max_turns = 0
voice_interrupt_on_wakeup = true
voice_streaming_tts_enabled = true
voice_tool_call_speech = true
voice_progress_speech_enabled = true
voice_max_response_tokens = 300

[model_providers.openrouter-main]
type = "openrouter"
api_key = "$OPENROUTER_API_KEY"

[device]
device_type = "iOS"

[model]
provider = "openrouter-main"
model = "bytedance-seed/seed-2.0-lite"

[stt_providers.openrouter-main]
type = "openrouter"
api_key = "$OPENROUTER_API_KEY"
model = "qwen/qwen3-asr-flash-2026-02-10"

[stt]
provider = "openrouter-main"

[tts_providers.minimax-main]
type = "minimax"
api_key = "$MINIMAX_API_KEY"
model = "speech-2.8-hd"
voice_id = "male-qn-qingse"
emotion = "happy"

[tts]
provider = "minimax-main"
speed = 1.0

[audio]
socket = "/run/audio_service/audio_service.sock"
sample_rate = 16000
channels = 1
bit_width = 16
playback_backend = "auto"

[hid]
keyboard_device = "/dev/hidg0"
keyboard_layout = "qwerty"
mouse_device = "/dev/hidg1"
android_keyboard_device = "/dev/hidg2"
frame_socket = "/run/frame_service/frame_service.sock"

Top-level fields

General

FieldDefault / allowed valuesDescription
localezh-CN (default) / en-USDevice-level language for Config Web and user-facing Agent responses, including progress messages and <tts> content. This is independent from [stt].language, which only controls speech recognition.
custom_instruction-Optional deployment/persona override for the built-in runtime instruction. Leave empty to use the agent binary default; set only for internal testing or deployment-specific behavior.
additional_prompt-Additional prompt field; appended after the base instruction at runtime
load_all_toolsfalseWhen true, also send list_scripts, read_script, and write_script to the conversational model. This does not expose HTTP-blocked maintenance tools.
max_iterations-1Maximum number of tool-call loops per run; -1 means unlimited
screenshot_keep_n3Number of most recent screenshots to keep when pruning screenshots from the LLM context; unset or 0 uses the default
screenshot_prune_interval2Once screenshots exceed screenshot_keep_n + screenshot_prune_interval, replace old screenshots with placeholders in batches; unset or 0 uses the default
input_modetext / sttInput mode
todo_reminder_tool_calls3In single-agent/default mode, after how many consecutive tool calls to remind the model to update the todo; set to 0 to use the default

Voice & VAD

These fields apply to the stt input mode.

FieldDefaultDescription
trigger_modemanual / wakeupVoice-mode trigger method
vad_backendrknnVAD backend: rknn uses NPU encoder + CPU LSTM/decoder, cpu uses a pure-CPU helper
vad_model_path/oem/usr/model/silero_vad_6_2_encoder_rv1106_w8a8_v1.rknnSilero VAD RKNN encoder model path; not used when vad_backend="cpu"
vad_helper_path/oem/usr/bin/rknn_vadVAD helper executable path; the CPU backend defaults to /oem/usr/bin/cpu_vad
vad_speech_threshold0.5Silero VAD speech probability threshold
silence_ms550How many milliseconds of silence before an utterance is considered finished
min_speech_ms300Minimum valid speech duration
voice_followup_enabledfalseEnable continuous follow-up after a single wakeup in wakeup mode; defaults to one wakeup per turn
voice_followup_timeout_ms5000Window to wait for a user follow-up after the Agent replies
voice_first_turn_timeout_ms10000Window to wait for the first utterance after wakeup
voice_max_turns0Maximum turns per wakeup session; 0 means unlimited
voice_interrupt_on_wakeuptrueWhen a wakeup is received again within a session, cancel thinking/TTS and listen again; repeated wakeups during the listening or recording phase are merged or ignored
voice_streaming_tts_enabledtrueFeed the LLM streaming output into TTS sentence by sentence, reducing the wait before the first sentence plays
voice_tool_call_speechtrueWhether to asynchronously read the content of a tool-call event; this content comes only from the assistant content in the same LLM tool-call response, and stays silent when absent
voice_progress_speech_enabledtrueWhether to announce a short progress message when a todo item enters in_progress; todo state is still sent to the UI/trace
voice_max_response_tokens300Per-turn output token limit for voice replies (must be >= 0)

The model pointed to by vad_model_path must first be converted from the Silero ONNX to RV1106 RKNN on a PC using silero-vad/convert_silero_vad_to_rknn.py, then placed at the corresponding path on the device. The CPU backend requires silero_vad_6_2_lstm_decoder_weights.bin to include the Conv1d encoder extension, which can be generated from the TorchScript file shipped with the repo using silero-vad/export_silero_vad_v6_2_weights.py. When vad_helper_path is still the built-in default, switching vad_backend automatically switches the helper; only when set to a custom path does it run that custom path.

[termination_policy]

The termination policy prevents stalled runs from looping indefinitely. These fields can be edited directly in agent.toml; omitted or zero-valued numeric fields use the defaults below.

[termination_policy]
enabled = true
max_seconds = 0
repeat_action_limit = 3
same_result_limit = 3
screen_unchanged_limit = 5
soft_notice_stall_score = 2
restrict_tools_stall_score = 4
terminate_stall_score = 6
parse_failure_limit = 3
FieldDefaultDescription
enabledtrueEnable tiered loop detection and graceful termination
max_seconds0Wall-clock budget per instruction; 0 disables the time budget, and a consumed steer starts a fresh budget
repeat_action_limit3Stop after this many identical tool calls with identical results
same_result_limit3Number of repeated identical results considered stalled
screen_unchanged_limit5Stop after this many UI actions without a screen change
soft_notice_stall_score2Stall score that injects a one-shot strategy-change notice
restrict_tools_stall_score4Stall score that temporarily blocks repeated UI action tools
terminate_stall_score6Stall score that ends the run gracefully
parse_failure_limit3Stop after this many consecutive unparseable model outputs

The three stall-score thresholds must satisfy soft_notice_stall_score < restrict_tools_stall_score < terminate_stall_score.

[model_providers.<name>]

Optional named provider configurations. Each section holds the credentials for one endpoint, and [model] references it by putting the name in its provider field. This lets several providers stay configured at once so switching is a one-line change instead of a re-entry of keys.

FieldDescription
typeRequired provider type: openai, anthropic, openrouter, kimi, kimi-cn, volcengine, ollama, fake
api_keyLiteral API key, or $VAR_NAME to read it from an environment variable
base_urlCustom endpoint; supported by openai, anthropic, and ollama
[model_providers.openai-work]
type = "openai"
api_key = "sk-..."

[model_providers.ollama-local]
type = "ollama"
base_url = "http://127.0.0.1:11434"

[model_providers.claude-work]
type = "anthropic"
api_key = "$ANTHROPIC_AUTH_TOKEN"
base_url = "https://api.anthropic.com/v1"

[model]
provider = "openai-work" # references [model_providers.openai-work]
model = "gpt-5.5"

On load, a provider value that names a section under [model_providers] is replaced by that section's provider type, and its api_key and base_url fill in any field the model section leaves empty — values set directly on [model] always win. A provider that matches no section is treated as a provider type, so existing configs keep working unchanged.

token_env is not supported. Replace it with api_key = "$VAR_NAME".

Only the [model_providers.<name>] namespace is supported. The former [providers.<name>] namespace is rejected with an error. The record-level provider field is still accepted as a read-time alias for type; saving always writes type, and type wins when both fields are present.

A provider that is neither a section name nor a known provider type is rejected at load, so a typo or a reference left behind after deleting a section fails with a clear error instead of surfacing later when the model client is built. When a section is named exactly like a provider type, the section wins.

[model]

FieldDescription
providerA provider type, or the name of a [model_providers.<name>] section. Types: openai, anthropic, openrouter, kimi, kimi-cn, volcengine, ollama, fake. kimi targets the Moonshot global site (https://api.moonshot.ai/v1) and kimi-cn targets the mainland China site (https://api.moonshot.cn/v1); volcengine targets Volcengine Ark (https://ark.cn-beijing.volces.com/api/v3).
modelModel name; usually required except for fake
base_urlCustom provider endpoint. openai, anthropic, and ollama accept overrides; other providers use built-in endpoints and a stored value is dropped on load. Anthropic accepts either a host URL or a URL ending in /v1.
api_keyAPI key written directly
temperatureSampling temperature. When unset, the default is model-dependent (some models such as Kimi K3 require a fixed temperature), falling back to 0.2. An explicit value always takes precedence.
reasoning_effortThinking effort. Unset is auto. For no-tool requests, native Anthropic maps low/medium/high to adaptive thinking output_config.effort; tool requests retain Claude's default reasoning because Aiden does not persist Anthropic thinking signatures. minimal is supported by OpenRouter and Volcengine Ark; none is supported by OpenRouter, OpenAI, Kimi, Ollama, and the fake provider, but not by native Anthropic or Ark. Some models pin a lighter default (see the registry in model_specs.go); an explicit value always wins.
max_response_tokensMaximum output tokens passed to the model on request
context_windowOptional total context window override in tokens. Unset or 0 uses provider metadata for OpenRouter/Ollama when available, then the built-in registry, then memory fallback.
model_max_output_tokensOptional advertised max output override in tokens. Unset or 0 uses provider metadata when fetched, then the built-in registry.

Moonshot Kimi K3

Use the dedicated kimi (global) or kimi-cn (mainland China) provider. Each has a built-in Moonshot OpenAI-compatible endpoint, so only model and the API key are required. The kimi-k3 context window and max output are in the built-in registry, so the metadata overrides can stay unset.

# Global site (https://api.moonshot.ai/v1)
[model]
provider = "kimi"
model = "kimi-k3"
api_key = "MOONSHOT_API_KEY"

# Mainland China site (https://api.moonshot.cn/v1)
# [model]
# provider = "kimi-cn"
# model = "kimi-k3"
# api_key = "MOONSHOT_API_KEY"

Volcengine Ark (Doubao)

Use the volcengine provider. It targets Ark's OpenAI-compatible endpoint (https://ark.cn-beijing.volces.com/api/v3), so only model and the API key are required. model is the Ark model ID, and api_key is an Ark API key.

[model]
provider = "volcengine"
model = "doubao-seed-2-1-pro-260628"
api_key = "ARK_API_KEY"

To read the key from the environment instead of writing it here, put it on a named provider and reference that:

[model_providers.ark]
type = "volcengine"
api_key = "$ARK_API_KEY"

[model]
provider = "ark"
model = "doubao-seed-2-1-pro-260628"

Ark also exposes an Anthropic-protocol endpoint at /api/compatible. This agent always speaks the OpenAI-compatible protocol, so use the /api/v3 path above.

reasoning_effort accepts minimal (no thinking), low, medium, and high. Ark treats an omitted value as high, which delays the first streamed token by several seconds; the built-in registry therefore pins low as the default for doubao-seed-2-1-pro-260628 so voice replies stay responsive. Set the field explicitly to override. Note that none is not an Ark level — use minimal.

The context window and max output for doubao-seed-2-1-pro-260628 are in the built-in registry, so the metadata overrides can stay unset. Other dated releases are not registered; for those, set context_window and model_max_output_tokens explicitly, or add a registry entry keyed by the exact Ark model ID.

The [tts] section has an unrelated provider that is also named volcengine. It speaks a separate WebSocket protocol with its own host and credentials, so an Ark API key and base URL do not carry over to it.

[log]

FieldDefaultDescription
llm_http_retention_days7Number of days to keep raw LLM HTTP logs under <config_dir>/log (llm-http-*.log). Cleanup runs when the agent starts; unset or 0 uses the default.

[audio]

FieldDefaultDescription
socket/run/audio_service/audio_service.sockAudio Service socket
sample_rate16000Sample rate
channels1Number of channels
bit_width16Bit width
playback_backendautoTTS playback backend. auto uses audio_service on board and the local OS player when the Agent is running in desktop/PC mode through ADB input backend or environment bridge. Use audio_service or local to force one.

[voice_notifications]

Voice notifications attach system reminders to a normal spoken reply or replace a final failed LLM turn with a fixed error message. They never start an independent background announcement. See Voice Notifications for the lifecycle and delivery contract.

[voice_notifications]
enabled = true
max_pending = 8

[voice_notifications.response_tail]
enabled = true
max_items = 1
max_text_chars = 40

[voice_notifications.expiration]
default_ttl_seconds = 0

[voice_notifications.expiration.code_ttl_seconds]
storage = 900
FieldDefaultDescription
enabledtrueEnable persistent tails and final-turn replacements
max_pending8Maximum active condition records kept by the in-memory manager
response_tail.enabledtrueAllow persistent reminders to be appended to normal replies
response_tail.max_items1Maximum reminders per reply; the current implementation supports only 1
response_tail.max_text_chars40Maximum reminder length in Unicode characters
expiration.default_ttl_seconds0Default active-condition lease; 0 disables automatic expiration
expiration.code_ttl_seconds.<code>storage = 900Per-code lease override renewed by each active heartbeat

Config Web preserves this section through GET/POST and TOML save operations. Edit it directly in agent.toml until dedicated controls are added to the page.

[device]

FieldDefaultDescription
device_typeiOSTarget host type for USB HID descriptors and Agent global device state. Accepted values: iOS, Android, macOS, windows, linux. Android derives HID pointer_mode = "touchscreen"; every other value derives pointer_mode = "absolute". Changing it requires a reboot so USB descriptors are re-enumerated.

[hid]

FieldDefaultDescription
keyboard_device/dev/hidg0Keyboard HID device
keyboard_layoutqwertyHow the phone interprets the external USB HID keyboard: qwerty, azerty, or qwertz. The visible soft-keyboard layout may differ. Used by keyboard_text and standard text-like keyboard_tap keys. iOS locks the hardware layout at USB enumeration, so switch the phone's input language to match before saving, then follow the Config Web reboot prompt. A same-identity soft re-enumeration is avoided because it can leave the iOS keyboard and pointer session inconsistent. See USB HID.
mouse_device/dev/hidg1Mouse/touch HID device
android_keyboard_device/dev/hidg2Consumer Control HID device (hid.usb2) used for Android extension keys when [device].device_type = "Android" and media/volume/brightness/screenshot keys for other device types
frame_socket/run/frame_service/frame_service.sockFrame Service socket used by the screenshot tool
input_backendhidLow-level input backend for click/touch/keyboard tools. hid writes USB HID reports; adb uses the paired Android ADB connection and adb shell input/ADBKeyboard commands.

[tts_providers.<name>] and [stt_providers.<name>]

Named voice provider configurations, the same shape [model_providers.<name>] gives [model]. Each section holds the credentials and settings for one voice service, and [tts] / [stt] reference one by putting the name in their own provider field. Several providers stay configured at once, so switching is a one-line change instead of a re-entry of keys.

Unlike [model_providers.<name>], these are separate namespaces: the [tts] volcengine provider speaks a different protocol with its own host and credentials than the Ark LLM provider of the same name, so one map could not serve both. Each namespace also validates its own provider types — a TTS type is rejected for [model] and vice versa.

Several records may share one provider type, which is how two accounts of the same service (different keys, different voices) stay configured together.

[tts_providers.minimax-main]
type = "minimax"
api_key = "sk-aaa"
voice_id = "male-qn-qingse"

[tts_providers.minimax-alt] # same type, second account
type = "minimax"
api_key = "sk-bbb"
voice_id = "female-shaonv"

[tts_providers.fish]
type = "fish-audio"
api_key = "$FISH_API_KEY"
reference_id = "abc123"

[tts]
provider = "minimax-main" # references [tts_providers.minimax-main]
speed = 1.0

[stt_providers.tencent]
type = "tencent-asr"
app_id = "123"
secret_id = "AKID..."
secret_key = "..."
region = "ap-shanghai"

[stt]
provider = "tencent"
language = "zh"

Field placement

A field lives on the record when it stops meaning anything once the provider changes; it stays on [tts] / [stt] when it holds regardless of provider.

Record fieldsStays on the flat section
TTStype, api_key, model, voice_id, emotion, reference_idprovider (reference), speed
STTtype, api_key, model, base_url, app_id, secret_id, secret_key, region, engine_model_typeprovider (reference), language

speed is a listening preference and language a transcription preference: neither should change because the voice changed, so both stay global.

For all provider records, api_key accepts either a literal key or $VAR_NAME. Config Web stores exactly the same representation.

Backward compatibility

  • The record-level provider field remains read-only compatible in all three provider maps. type wins if both fields are present, and the next save emits only type.
  • A bare provider type in [tts] / [stt] keeps working. provider = "minimax-cn" with a flat api_key needs no migration to keep speaking.
  • Flat credentials on [tts] / [stt] are upgraded to records on load, keyed by provider type. The upgrade is written back the next time the config is saved, and an existing record is never overwritten.
  • An unresolvable reference does not stop the device from booting: voice is optional at runtime, so a stale name is reported and the agent starts without voice. Config Web rejects such a reference when saving instead, while the form is still on screen.

[stt] and [tts]

[stt] is required when input_mode = "stt"; [tts] is required when input_mode = "stt".

provider here is a reference to a [tts_providers.<name>] / [stt_providers.<name>] record (a bare provider type still works — see above). The provider-specific credentials listed below live on that record; Config Web edits them in the provider dialog rather than on the [tts] / [stt] card.

STT:

  • provider = "openai-whisper": currently available;
  • provider = "openrouter": currently available, default endpoint is https://openrouter.ai/api/v1/audio/transcriptions, request body uses base64 WAV;
  • provider = "tencent-asr": Tencent Cloud Sentence Recognition (SentenceRecognition), uses secret_id / secret_key, no base_url needed; the legacy values tencent / tencent_asr are retained only as compatibility aliases;
  • provider = "qwen-asr": Alibaba Cloud DashScope Realtime ASR over WebSocket, default model qwen3-asr-flash-realtime; supports streaming upload and accepts optional model / base_url overrides;
  • provider = "google-cloud": Google Cloud Speech-to-Text REST API with API key authentication, default endpoint https://speech.googleapis.com/v1/speech:recognize; accepts optional model / base_url overrides and does not support streaming upload.

TTS:

  • provider = "minimax": Minimax WebSocket, global endpoint api.minimax.io;
  • provider = "minimax-cn": Minimax WebSocket, mainland China endpoint api.minimaxi.com;
  • provider = "fish-audio": Fish Audio WebSocket;
  • provider = "alicloud": Alibaba Cloud Qwen-TTS Realtime;
  • provider = "volcengine": Volcengine WebSocket bidirectional streaming V3. Currently only the new console's X-Api-Key authentication is supported: api_key maps to X-Api-Key, model maps to X-Api-Resource-Id (default seed-tts-2.0), and voice_id maps to the speaker;
  • provider = "openrouter": OpenRouter HTTP speech API, default model google/gemini-3.1-flash-tts-preview; model and voice_id select the routed TTS model and its voice;
  • provider = "google-cloud": Google Cloud Text-to-Speech REST API with API key authentication; voice_id defaults to en-US-Neural2-C.

TTS configuration fields:

FieldLocationDescription
provider[tts]Required reference to a named provider record; a bare provider type remains supported for backward compatibility
api_key[tts_providers.<name>]Required authentication key for the selected provider
model[tts_providers.<name>]Optional Minimax model, Fish Audio model header, Alibaba Cloud Realtime model, Volcengine X-Api-Resource-Id, or OpenRouter model
voice_id[tts_providers.<name>]Optional Minimax, Alibaba Cloud, OpenRouter, or Google Cloud voice; Volcengine speaker. Not used by Fish Audio (see reference_id)
reference_id[tts_providers.<name>]Optional Fish Audio reference id; defaults to the built-in demo voice shown by Config Web. Ignored by other providers
emotion[tts_providers.<name>]Optional Minimax emotion; Volcengine passes it through as audio_params.emotion and requires voice support
speed[tts]Optional speech rate, default 1.0; the supported range varies by provider, refer to the official docs

The examples use placeholder keys to make the required record placement explicit.

Common TTS adapter configs:

Providermodel exampleVoice/reference fieldDescription
minimaxspeech-2.8-hdvoice_id = "male-qn-qingse"Minimax WebSocket via api.minimax.io; emotion is passed through to Minimax
minimax-cnspeech-2.8-hdvoice_id = "male-qn-qingse"Minimax WebSocket via api.minimaxi.com; emotion is passed through to Minimax
fish-audios2-proreference_id = "98655a12fa944e26b274c535e5e03842"WebSocket live TTS; the shown reference is used by default, and voice_id is not used
alicloudqwen3-tts-flash-realtimevoice_id = "Cherry"DashScope Realtime; the adapter outputs 24 kHz PCM, automatically resampling when the sample rate differs
volcengineseed-tts-2.0voice_id = "zh_female_vv_uranus_bigtts"model maps to X-Api-Resource-Id, voice_id maps to the speaker, and the two must match
openroutergoogle/gemini-3.1-flash-tts-previewvoice_id = "Kore"OpenRouter /audio/speech; voice defaults depend on the selected model and output is 24 kHz PCM
google-cloudvoice_id = "en-US-Neural2-C"Google Cloud Text-to-Speech REST API with API key authentication; output is 24 kHz PCM

Provider examples

Minimax WebSocket:

[tts_providers.minimax-main]
type = "minimax"
api_key = "..."
model = "speech-2.8-hd"
voice_id = "male-qn-qingse"
emotion = "happy"

[tts]
provider = "minimax-main"
speed = 1.0

Fish Audio WebSocket:

[tts_providers.fish-main]
type = "fish-audio"
api_key = "..."
model = "s2-pro"
reference_id = "98655a12fa944e26b274c535e5e03842"

[tts]
provider = "fish-main"
speed = 1.0

Fish Audio model defaults to s2-pro and is sent as a WebSocket handshake header. An empty reference_id uses the built-in demo voice shown in Config Web; configure reference_id on the selected [tts_providers.<name>] record to override it. voice_id is not used by Fish Audio and is ignored (this avoids inheriting a voice_id meant for another provider). In some networks, the public Fish Audio endpoint may require ALL_PROXY or HTTPS_PROXY in /userdata/system/env.

Alibaba Cloud Qwen-TTS Realtime:

[tts_providers.alicloud-main]
type = "alicloud"
api_key = "..."
model = "qwen3-tts-flash-realtime"
voice_id = "Cherry"

[tts]
provider = "alicloud-main"
speed = 1.0

The Alibaba Cloud adapter uses the DashScope WebSocket Realtime endpoint and outputs a fixed 24 kHz PCM; when the device playback sample rate differs, it automatically resamples.

Volcengine WebSocket bidirectional streaming V3:

[tts_providers.volcengine-main]
type = "volcengine"
api_key = "..."
model = "seed-tts-2.0"
voice_id = "zh_female_vv_uranus_bigtts"

[tts]
provider = "volcengine-main"
speed = 1.0

For Volcengine, api_key is the new console's X-Api-Key, model is the X-Api-Resource-Id, and voice_id is the speaker. voice_id must match the resource corresponding to model; when they do not match, the server returns resource ID is mismatched with speaker related resource. A verified working voice example for seed-tts-2.0 is zh_female_vv_uranus_bigtts.

Switching providers at runtime

curl -X POST http://<device-ip>:8080/api/settings/tts \
-H 'Content-Type: application/json' \
-d '{"provider":"volcengine","voice":"zh_female_vv_uranus_bigtts"}'

If you need to store the keys of multiple providers in the same config, use named provider records. When switching providers via runtime POST, the corresponding record is read first, then overridden by the request body.

[tts_providers.minimax-main]
type = "minimax"
api_key = "..."
model = "speech-2.8-hd"
voice_id = "male-qn-qingse"

[tts_providers.fish-main]
type = "fish-audio"
api_key = "..."
model = "s2-pro"
reference_id = "98655a12fa944e26b274c535e5e03842"

[tts_providers.alicloud-main]
type = "alicloud"
api_key = "..."
model = "qwen3-tts-flash-realtime"
voice_id = "Cherry"

[tts_providers.volcengine-main]
type = "volcengine"
api_key = "..."
model = "seed-tts-2.0"
voice_id = "zh_female_vv_uranus_bigtts"

[tts]
provider = "minimax-main"
speed = 1.0

[live_activity]

For the iOS companion app's Live Activity / Dynamic Island task status. The agent-side status snapshot is enabled by default. See the full flow in Live Activity / Dynamic Island.

Relay-based updates (legacy, used when APNs credentials are not configured):

FieldDefaultDescription
relay_urlpreconfigured in official firmwareAiden Live Activity relay URL; only advanced deployments need to override it
relay_api_keypreconfigured in official firmwareShared relay Bearer token; must match the app build config and relay server AIDEN_RELAY_API_KEY
board_idgenerated in /userdata/agent/board_idBoard ID in relay; generated on first run. Empty or default is not a valid relay identity

APNs-based updates (for remote updates when the app is backgrounded, on lock screen, or not open):

FieldDefaultDescription
bundle_id-iOS app bundle id; required only when configuring background APNs and topic is not explicitly set
topic<bundle_id>.push-type.liveactivityAPNs topic; usually does not need to be set manually
environmentsandboxsandbox or production
team_id-Apple Developer Team ID; used only by background APNs
key_id-APNs Auth Key ID; used only by background APNs
private_key_path-APNs .p8 private key path; used only by background APNs
private_key_pem-Inline APNs .p8 PEM directly; for development/debugging only, do not place in open-source config or on user boards in production
timeout_sec10Background APNs request timeout

Episode telemetry (Langfuse)

Optional. After a task ends, asynchronously report the full episode to Langfuse; see telemetry-langfuse.md for details.

[telemetry]
enabled = false
provider = "langfuse"
base_url = "http://langfuse.example.com:3000"
public_key = "pk-lf-..."
secret_key = "sk-lf-..."
upload_screenshots = true
upload_timeout_sec = 30
max_retry = 2
environment = "default"
tags = ["aiden-hardware"]

System environment variables

The Agent no longer reads [proxy] from agent.toml. Outbound HTTP/WebSocket requests, shell tool subprocesses, OTA commands launched through aiden-env-run, and SSH login shells all use environment variables from /userdata/system/env. The file is loaded with shell syntax, for example:

HTTP_PROXY=http://127.0.0.1:7890
HTTPS_PROXY=http://127.0.0.1:7890
NO_PROXY=localhost,127.0.0.1,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
OPENROUTER_API_KEY=...
VariableDescription
HTTP_PROXY / http_proxyHTTP proxy URL, for example http://127.0.0.1:7890
HTTPS_PROXY / https_proxyHTTPS proxy URL, usually the same HTTP proxy endpoint
ALL_PROXY / all_proxyGeneric proxy used by HTTP clients and some WebSocket adapters
NO_PROXY / no_proxyComma-separated bypass rules; when a proxy URL is set and no bypass value is present, the launcher injects the default private-network bypass list

memory/extraction.yaml

Optional. Place memory/extraction.yaml under the config directory to control session-memory compaction and chunk extraction. Missing files and invalid fields fall back to defaults. See session-memory.md for the full flow.

FieldDefaultDescription
reserve_tokens8192Token headroom reserved below the active model context window. Compaction triggers when prompt_tokens >= context_window - reserve_tokens. The value is clamped to at most half of the window so small-window models remain usable.
keep_recent_tokens20000Approximate token budget for the hot window retained by token-based cut-point selection. It is clamped together with reserve_tokens to fit the active window.
hot_window_events30Target number of recent events retained by the count fallback. Used only when prompt-token data is unavailable.
count_compress_after_eventshot_window_events * 2Event-count trigger used only when prompt-token data is unavailable. If omitted, it is derived from the normalized hot_window_events; explicit values must be greater than hot_window_events.
context_window32000Fallback context window for compaction when the active model is not present in model_specs. Runtime normally derives this from ModelResolver.Spec(); this value is only used for unknown models.
compress_at_percent50Percentage trigger: compaction starts when prompt_tokens / context_window >= compress_at_percent%.
summary_max_chunks10Number of chunk summaries kept in the Recent Chunks section of summary.md. Older entries move to the archive and are folded into the Rolling Summary.
session_boundary_enabledtrueClassify each new user turn as continuing the current session or starting a new one. A new boundary archives the current memory/session/ directory and recreates an empty active session.
session_boundary_short_gap_seconds300Gap below which a turn is treated as continuation regardless of lexical signals.
session_boundary_long_gap_seconds1800Gap above which a turn is treated as a fresh session regardless of lexical signals.
tag_candidatessee defaultsCandidate keywords matched when tagging chunk summaries.
entity_suffixes["App","app","APP"]Suffixes recognized during entity extraction.

Known limitations

  • preferred_model and allowed_children are currently parsed but not fully wired into execution;
  • Example skills may reference old tools and should be checked before production use.