Skip to content

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 values

Set 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"
KeyRequiredDescription
org_urlYesADO organisation URL.
projectYesADO project name (case-sensitive).
pat_envYesName 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_assigneeNoEmail 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"
KeyRequiredDescription
ownerYesGitHub organisation or user owning the repository.
repoYesRepository name.
token_envYesName of the env var holding the GitHub token. Required scopes: repo (full), read:org.
bot_loginNoGitHub login stories must be assigned to. Defaults to the token owner.
default_branchNoPR 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"
KeyRequiredDescription
base_urlYesJira Cloud base URL.
project_keyYesJira project key (e.g. VLX). Stories in this project are polled.
email_envYesName of the env var holding the Atlassian account email.
api_token_envYesName of the env var holding the Atlassian API token.
bot_account_idNoPoll issues assigned to this Jira accountId. Defaults to the API token owner.
actionable_statusesNoIssue statuses the agent will pick up. Defaults to ["To Do", "Open"].
statusesNoMap 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"
KeyRequiredDescription
bot_token_envYesEnv var name holding the Telegram bot token.
group_chat_id_envYesEnv 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_idsYesArray of Telegram user IDs allowed to interact with the bot. Unauthorised updates are silently ignored.
state_fileNoPath 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"]
KeyRequiredDescription
tenant_idYesAzure AD tenant ID.
client_idYesApp registration client ID. The app needs ChannelMessage.Send, Chat.ReadWrite, TeamsAppInstallation.ReadWriteForTeam Microsoft Graph permissions.
client_secret_envYesEnv var name holding the client secret.
team_idYesID of the Teams team.
channel_idYesFull channel ID (thread format).
authorized_user_idsYesAAD 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"
KeyRequiredDescription
org_urlYesADO organisation URL (same as ado.org_url typically).
projectYesADO project name.
repository_idYesRepository UUID (visible in the ADO repository settings URL).
repo_nameYesRepository name (used for display and branch operations).
pat_envYesEnv 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"
KeyRequiredDescription
worktree_rootNoRoot directory for per-run git worktrees. Relative to the CWD where the daemon runs. Defaults to .vlx/worktrees.
source_repo_pathNoPath to the source git repo the daemon manages. Defaults to . (CWD).
base_branchNoBase branch new story branches are forked from. Defaults to main.

log — Logging

yaml
log:
  level: "info"
KeyRequiredDescription
levelNoLog 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
KeyRequiredDescription
allowed_hostsNoHostnames 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_registriesNoPackage registry URLs the agent may install from without approval (e.g. ["https://registry.npmjs.org"]).
approval_timeout_msNoMilliseconds 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
KeyRequiredDescription
enabledNoMaster 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
KeyRequiredDescription
start_commandYesShell command that starts the app (run in the story's worktree).
ready_timeout_msNoHow long to wait for the first HTTP response. Defaults to 60000.
urlYesURL 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
KeyRequiredDescription
enabledNoMaster switch. Defaults to false.
cooldown_minutesNoMinutes to wait after vote_approved before merging. Defaults to 240 (4 hours).
min_trust_scoreNoMinimum 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

VariablePurpose
ADO_PATADO Personal Access Token (Work Items + Code R/W)
TELEGRAM_BOT_TOKENTelegram bot token
TELEGRAM_GROUP_CHAT_IDTelegram group chat ID
GITHUB_TOKENGitHub token (when using GitHub adapter)
JIRA_EMAILJira account email (when using Jira adapter)
JIRA_API_TOKENJira API token (when using Jira adapter)
TEAMS_CLIENT_SECRETTeams app client secret (when using Teams adapter)
VLX_HOMEOperational root (default: ~/.vlx)
VLX_CONFIGPath to vlx.yaml (default: ~/.vlx/vlx.yaml)
VLX_DB_PATHPath to state.db (default: ~/.vlx/state.db)
VLX_UPDATE_URLOverride the self-update release-channel base URL
VLX_NO_UPDATE1 skips the daemon's on-start update check
LOG_LEVELLog 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.

Internal Veloxcore tool — not a public product.