Appearance
Configure
VeloxSaarthi is configured via ~/.vlx/vlx.yaml. The normal way to create it is the guided setup — vlx init writes a complete config (and the secrets file) from your answers. Use vlx.example.yaml in the repo as a commented reference, or as a starting point when writing the file by hand:
bash
cp vlx.example.yaml ~/.vlx/vlx.yaml # then replace the placeholder valuesSet VLX_CONFIG to use a different location. When hand-writing the file, use absolute paths for runtime.source_repo_path and runtime.worktree_root — the daemon can be started from any directory, so CWD-relative values are fragile.
The schema is validated at startup by a strict Zod parser. Unknown keys are rejected. See src/config/schema.ts for the authoritative definition.
ado — Azure DevOps tracker
yaml
ado:
org_url: "https://dev.azure.com/<org>"
project: "<project>"
pat_env: "ADO_PAT"
# bot_assignee: "saarthi@veloxcore.com"| Key | Required | Description |
|---|---|---|
org_url | Yes | ADO organisation URL. |
project | Yes | ADO project name (case-sensitive). |
pat_env | Yes | Name of the env var holding the ADO PAT (not the token value). The PAT must have Work Items R/W and Code R/W scopes. |
bot_assignee | No | Email of the ADO user stories must be assigned to. Omit to default to the PAT owner (@Me). Set when the PAT user differs from the bot assignee (e.g., a service account). |
github — GitHub tracker / SCM host (alternative)
Use this section when adapters.tracker: "github" or adapters.scm_host: "github". One section serves both adapter slots.
yaml
github:
owner: "veloxcore"
repo: "veloxsaarthi"
token_env: "GITHUB_TOKEN"
# bot_login: "saarthi-bot"
# default_branch: "main"| Key | Required | Description |
|---|---|---|
owner | Yes | GitHub organisation or user owning the repository. |
repo | Yes | Repository name. |
token_env | Yes | Name of the env var holding the GitHub token. Required scopes: repo (full), read:org. |
bot_login | No | GitHub login stories must be assigned to. Defaults to the token owner. |
default_branch | No | PR target branch for the SCM host adapter. Defaults to main. |
jira — Jira Cloud tracker (alternative)
Use when adapters.tracker: "jira".
yaml
jira:
base_url: "https://yourorg.atlassian.net"
project_key: "VLX"
email_env: "JIRA_EMAIL"
api_token_env: "JIRA_API_TOKEN"
# bot_account_id: "5b10ac8d82e05b22cc7d4ef5"
# actionable_statuses: ["To Do", "Open"]
# statuses:
# active: "In Progress"
# resolved: "In Review"
# closed: "Done"| Key | Required | Description |
|---|---|---|
base_url | Yes | Jira Cloud base URL. |
project_key | Yes | Jira project key (e.g. VLX). Stories in this project are polled. |
email_env | Yes | Name of the env var holding the Atlassian account email. |
api_token_env | Yes | Name of the env var holding the Atlassian API token. |
bot_account_id | No | Poll issues assigned to this Jira accountId. Defaults to the API token owner. |
actionable_statuses | No | Issue statuses the agent will pick up. Defaults to ["To Do", "Open"]. |
statuses | No | Map domain statuses (active, resolved, closed) to Jira transition names. |
telegram — Human channel
yaml
telegram:
bot_token_env: "TELEGRAM_BOT_TOKEN"
group_chat_id_env: "TELEGRAM_GROUP_CHAT_ID"
authorized_user_ids: [123456789]
# state_file: "~/.vlx/telegram-state.json"| Key | Required | Description |
|---|---|---|
bot_token_env | Yes | Env var name holding the Telegram bot token. |
group_chat_id_env | Yes | Env var name holding the group chat ID. Must be a forum-mode supergroup (Topics enabled). Numeric; negative for groups; -100-prefixed for supergroups. |
authorized_user_ids | Yes | Array of Telegram user IDs allowed to interact with the bot. Unauthorised updates are silently ignored. |
state_file | No | Path to the JSON file persisting the getUpdates offset and topic cache across daemon restarts. Defaults to ~/.vlx/telegram-state.json. |
Deriving the chat ID:
Add the bot to the group, then call https://api.telegram.org/bot<token>/getUpdates. Look for the chat.id field in the response. For supergroups the value is negative and starts with -100.
teams — Microsoft Teams human channel (alternative)
Use when adapters.human_channel: "teams".
yaml
teams:
tenant_id: "00000000-0000-0000-0000-000000000000"
client_id: "00000000-0000-0000-0000-000000000000"
client_secret_env: "TEAMS_CLIENT_SECRET"
team_id: "00000000-0000-0000-0000-000000000000"
channel_id: "19:xxxxxxxx@thread.tacv2"
authorized_user_ids: ["00000000-0000-0000-0000-000000000000"]| Key | Required | Description |
|---|---|---|
tenant_id | Yes | Azure AD tenant ID. |
client_id | Yes | App registration client ID. The app needs ChannelMessage.Send, Chat.ReadWrite, TeamsAppInstallation.ReadWriteForTeam Microsoft Graph permissions. |
client_secret_env | Yes | Env var name holding the client secret. |
team_id | Yes | ID of the Teams team. |
channel_id | Yes | Full channel ID (thread format). |
authorized_user_ids | Yes | AAD object IDs allowed to interact. The bot fails closed when this list is empty. |
azure_repos — SCM host (default)
yaml
azure_repos:
org_url: "https://dev.azure.com/<org>"
project: "<project>"
repository_id: "<uuid>"
repo_name: "<repo>"
pat_env: "ADO_PAT"| Key | Required | Description |
|---|---|---|
org_url | Yes | ADO organisation URL (same as ado.org_url typically). |
project | Yes | ADO project name. |
repository_id | Yes | Repository UUID (visible in the ADO repository settings URL). |
repo_name | Yes | Repository name (used for display and branch operations). |
pat_env | Yes | Env var name for the PAT. The same ADO_PAT covers both ADO API and git push over HTTPS. |
runtime — Worktree and pipeline settings
yaml
runtime:
worktree_root: ".vlx/worktrees"
source_repo_path: "."
base_branch: "main"| Key | Required | Description |
|---|---|---|
worktree_root | No | Root directory for per-run git worktrees. Relative to the CWD where the daemon runs. Defaults to .vlx/worktrees. |
source_repo_path | No | Path to the source git repo the daemon manages. Defaults to . (CWD). |
base_branch | No | Base branch new story branches are forked from. Defaults to main. |
log — Logging
yaml
log:
level: "info"| Key | Required | Description |
|---|---|---|
level | No | Log verbosity: trace / debug / info / warn / error / fatal. Defaults to info. Overridden by the LOG_LEVEL env var. |
permissions — Tool-use gating
Controls which operations the ACP agent can perform without a human tap.
yaml
permissions:
allowed_hosts: []
allowed_registries: []
# approval_timeout_ms: 1500000| Key | Required | Description |
|---|---|---|
allowed_hosts | No | Hostnames the agent may call without a Telegram approval tap (e.g. ["api.github.com", "dev.azure.com"]). Requests to unlisted hosts are posted to Telegram for one-tap approval. |
allowed_registries | No | Package registry URLs the agent may install from without approval (e.g. ["https://registry.npmjs.org"]). |
approval_timeout_ms | No | Milliseconds to wait for a Telegram approval tap before auto-denying. Defaults to 1500000 (25 minutes — just under the watchdog's 30-minute stale-heartbeat threshold). |
approval_gate — Human plan approval (off by default)
When enabled, every plan pauses at the APPROVAL stage and waits for an Approve/Reject tap in Telegram before Build starts. Disabled by default — Plan routes straight to Build for fully unattended runs. vlx init asks this question during setup (60 seconds of silence → disabled); a daemon started interactively with a config that predates the key asks once and persists the answer to vlx.yaml.
yaml
approval_gate:
enabled: false| Key | Required | Description |
|---|---|---|
enabled | No | Master switch. Defaults to false. |
ui_test — App under test for UI stories
When a story's spec declares ui: true (set by the Architect for anything a user sees in a browser), the Test stage starts this command in the worktree, waits for the URL to respond, has the Inspector drive real browser flows with video + screenshots per acceptance criterion (and compare against any mockups attached to the story), then kills the process tree. The evidence is posted to the story's Telegram topic and attached to the work item.
A UI story escalates fail-closed when this section is missing — it will never silently ship untested.
yaml
ui_test:
start_command: "bun run dev"
url: "http://localhost:3000/"
ready_timeout_ms: 60000| Key | Required | Description |
|---|---|---|
start_command | Yes | Shell command that starts the app (run in the story's worktree). |
ready_timeout_ms | No | How long to wait for the first HTTP response. Defaults to 60000. |
url | Yes | URL the app serves once ready — also handed to the Inspector's browser run. |
Per-project override: each entry under projects: may carry its own ui_test block; the top-level section is the fallback.
auto_merge — Automatic merge (feature-flagged)
Auto-merge is off by default. Enable only after you are comfortable with the agent's track record on a project. See Concepts — Auto-merge for the eligibility model.
yaml
auto_merge:
enabled: false
cooldown_minutes: 240
min_trust_score: 0.80| Key | Required | Description |
|---|---|---|
enabled | No | Master switch. Defaults to false. |
cooldown_minutes | No | Minutes to wait after vote_approved before merging. Defaults to 240 (4 hours). |
min_trust_score | No | Minimum trust score (0.0–1.0) the project must have for a story to be auto-merge eligible. Defaults to 0.80. |
adapters — Swap adapter implementations
Omit this section to use all defaults.
yaml
adapters:
tracker: "ado" # ado | github | jira
human_channel: "telegram" # telegram | teams
scm_host: "azure_repos" # azure_repos | github
agent_session: "claude_code"Each value is a type string resolved to a factory in src/bootstrap/adapter-registry.ts. Unknown types fail at startup with the slot name and known types listed.
Environment variables quick-reference
| Variable | Purpose |
|---|---|
ADO_PAT | ADO Personal Access Token (Work Items + Code R/W) |
TELEGRAM_BOT_TOKEN | Telegram bot token |
TELEGRAM_GROUP_CHAT_ID | Telegram group chat ID |
GITHUB_TOKEN | GitHub token (when using GitHub adapter) |
JIRA_EMAIL | Jira account email (when using Jira adapter) |
JIRA_API_TOKEN | Jira API token (when using Jira adapter) |
TEAMS_CLIENT_SECRET | Teams app client secret (when using Teams adapter) |
VLX_HOME | Operational root (default: ~/.vlx) |
VLX_CONFIG | Path to vlx.yaml (default: ~/.vlx/vlx.yaml) |
VLX_DB_PATH | Path to state.db (default: ~/.vlx/state.db) |
VLX_UPDATE_URL | Override the self-update release-channel base URL |
VLX_NO_UPDATE | 1 skips the daemon's on-start update check |
LOG_LEVEL | Log verbosity override (overrides log.level in config) |
Secrets (ADO_PAT, TELEGRAM_*, adapter tokens) are auto-loaded from ~/.vlx/secrets/global.env at startup. Variables already set in the host environment always win over the file.