vtai-agent 0.6.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 VT-AIAgent Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,535 @@
1
+ Metadata-Version: 2.4
2
+ Name: vtai-agent
3
+ Version: 0.6.6
4
+ Summary: VT-AIAgent — system-automation agent exposed as an MCP server for opencode/claude/pi
5
+ Keywords: mcp,mcp-server,claude,opencode,agent,guardrails,audit,automation,sandbox,llm-agent
6
+ Author: VT-AIAgent Contributors
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: End Users/Desktop
13
+ Classifier: Operating System :: POSIX :: Linux
14
+ Classifier: Operating System :: MacOS
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Classifier: Topic :: Security
17
+ Classifier: Topic :: System :: Systems Administration
18
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
19
+ Requires-Dist: mcp>=2.2.0
20
+ Requires-Dist: pydantic>=2.13.5
21
+ Requires-Dist: pydantic-ai>=2.45.0
22
+ Requires-Dist: pydantic-settings>=2.15.0
23
+ Requires-Dist: sqlmodel>=0.0.42
24
+ Requires-Python: >=3.14
25
+ Project-URL: repository, https://github.com/velsrocky/vtai-agent
26
+ Project-URL: issues, https://github.com/velsrocky/vtai-agent/issues
27
+ Project-URL: changelog, https://github.com/velsrocky/vtai-agent/blob/main/CHANGELOG.md
28
+ Description-Content-Type: text/markdown
29
+
30
+ <!-- mcp-name: io.github.velsrocky/vtai-agent -->
31
+ # VT-AIAgent
32
+
33
+ [![CI](https://github.com/velsrocky/vtai-agent/actions/workflows/ci.yml/badge.svg)](https://github.com/velsrocky/vtai-agent/actions/workflows/ci.yml)
34
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
35
+
36
+ System-automation agent for **your** machine, exposed as an MCP server for
37
+ opencode / Claude Code / pi — and drivable directly via the `vtai` CLI.
38
+
39
+ ## Tools
40
+
41
+ | Tool | What it does |
42
+ |---|---|
43
+ | `system_info` | Read-only machine snapshot (CPU, RAM, disks, load, GPU) plus the config and guardrails in effect |
44
+ | `run_shell` | Allowlisted shell commands, sandboxed (see below) |
45
+ | `organize_files` | Content-aware directory cleanup inside writable roots |
46
+ | `backup_sync` | rsync incremental backup with verification; `--delete` is opt-in |
47
+ | `transcode_media` | VAAPI-accelerated batch video transcode; never touches originals |
48
+ | `disk_audit` | Read-only scan for reclaimable disk space |
49
+ | `delegate_coding` | Hand tasks to opencode/claude/pi; git snapshot + verify + rollback; require human approval by default |
50
+ | `auto_mode` | Autonomous loop: hand a goal to a coding CLI, review the reply, iterate until it matches |
51
+ | `inspect_application` | REA-powered static analysis of an app under guardrails: deny-glob check → quarantine copy → minimal-env run → evidence digest in the audit trail → optional judge pass (off until enabled in config) |
52
+
53
+ ## Safety model
54
+
55
+ - **Dry-run by default** (`guardrails.dry_run_default`) — previews never touch disk.
56
+ - **Path containment**: writes only inside `paths.writable_roots`, resolved
57
+ paths defeat `..`/symlink escapes; `guardrails.deny_globs` protect secrets.
58
+ - **Fail-closed shell sandbox**: the binary must be a bare allowlisted name
59
+ with an explicit read/write policy, resolve inside `trusted_bin_dirs`, carry
60
+ no exec-enabling flags, and every path argument passes the same checks.
61
+ - **Kill switch**: drop a `KILL` file in `data_dir` to abort executing tools.
62
+ - **Echo-loop breaker**: N identical denials in a window escalate the refusal
63
+ into a `CIRCUIT BREAKER` instruction the looping model can't miss.
64
+ - **Budget cap**: `budget_usd_per_run` aborts an orchestrated run when accrued
65
+ token cost exceeds it; spend is recorded per run.
66
+ - **Audit**: every allow and deny is logged to SQLite in `data_dir`, each
67
+ invocation (orchestrated, MCP, or CLI) attributed to a run row.
68
+
69
+ ## Quick start
70
+
71
+ ### Prerequisites
72
+
73
+ | | Requirement | Notes |
74
+ |---|---|---|
75
+ | **OS** | Linux, macOS, or Windows | all three run in CI (blocking since 0.6.0) |
76
+ | **Python** | 3.14+ | pinned by `.python-version`; `uv` fetches it for you |
77
+ | **uv** | >= 0.13.1 | `curl -LsSf https://astral.sh/uv/install.sh \| sh` |
78
+ | **Planner LLM** | one of: Ollama, any OpenAI-compatible endpoint, or a cloud key | see [Choosing a model](#choosing-a-model) |
79
+ | *rsync* | optional | required by `backup_sync`; without it the tool fails closed |
80
+ | *ffmpeg* | optional | required by `transcode_media` and the voice helpers |
81
+ | *Lemonade Server* | optional | speech in/out for `agent-listen` / `agent-speak` |
82
+
83
+ ### 1. Install
84
+
85
+ ```bash
86
+ git clone https://github.com/velsrocky/vtai-agent.git
87
+ cd vtai-agent
88
+ uv sync --dev
89
+ ```
90
+
91
+ `uv sync` needs uv >= 0.13.1 (the lockfile is `revision = 5`; the floor is
92
+ declared as `[tool.uv] required-version`, so an older uv says so plainly
93
+ instead of failing on a lockfile parse).
94
+
95
+ ### 2. Configure
96
+
97
+ The agent reads one TOML file. **Which** one is resolved in this order:
98
+
99
+ 1. `$VT_CONFIG` — explicit override
100
+ 2. `config/settings.toml` next to the package (i.e. the clone you just made)
101
+ 3. `~/.vtaiagent/settings.toml` — the per-user file
102
+
103
+ A fresh clone contains the **maintainer's** config (their provider, their
104
+ writable roots), so point the agent at your own file instead of editing the
105
+ tracked one:
106
+
107
+ ```bash
108
+ export VT_CONFIG="$HOME/.vtaiagent/settings.toml"
109
+ uv run vtai init # writes a starter config there + creates the dirs
110
+ ```
111
+
112
+ Put that `export` in your shell profile so every later command (and the MCP
113
+ server) picks it up.
114
+
115
+ `vtai init` creates `~/.vtaiagent` (audit DB + data dir), `~/Downloads`, and
116
+ `~/Documents/vt-data`, and writes a starter config with safe defaults:
117
+ `dry_run_default = true`, `deny_globs` for `~/.ssh`, `~/.gnupg`, `~/.aws` and
118
+ `**/.env`, and writable roots limited to `~/Downloads`, `~/Documents/vt-data`
119
+ and `~/.vtaiagent/tmp`. It is safe to re-run.
120
+
121
+ Check which config you ended up with — `vtai info` names the file and its
122
+ origin:
123
+
124
+ ```bash
125
+ uv run vtai info
126
+ # config file : /Users/you/.vtaiagent/settings.toml (user-level)
127
+ ```
128
+
129
+ <details>
130
+ <summary>Alternative: keep the checkout's config and override with env vars</summary>
131
+
132
+ A `.env` file (gitignored; copy `.env.example`) in the working directory
133
+ overrides any setting, using the `VT_` prefix with `__` for nesting:
134
+
135
+ ```bash
136
+ VT_LOCAL__BASE_URL=http://localhost:13305/api/v1
137
+ VT_LOCAL__MODEL=<a pulled chat model>
138
+ VT_ACTIVE_PROVIDER=local
139
+ ```
140
+
141
+ This is how a machine with no Ollama points the agent at a Lemonade server
142
+ instead. The `.env` is read from the **current working directory**, so run
143
+ the CLI from the checkout, or export the variables in your shell.
144
+ </details>
145
+
146
+ ### Choosing a model
147
+
148
+ The planner/judge model comes from `active_provider`:
149
+
150
+ - `local` — any OpenAI-compatible endpoint: Ollama (`http://localhost:11434/v1`),
151
+ llama.cpp, vLLM, LM Studio, Lemonade. No API key.
152
+ - `anthropic` / `openai` — cloud models; put `ANTHROPIC_API_KEY` or
153
+ `OPENAI_API_KEY` in `.env`.
154
+
155
+ Set `cost_per_1k_input` / `cost_per_1k_output` in the provider block to make
156
+ `budget_usd_per_run` meaningful; without them the agent falls back to the
157
+ model's native price table.
158
+
159
+ ### 3. Verify
160
+
161
+ Start with the preflight — it checks uv, your config, the writable roots,
162
+ what is protected, the optional tools, the model endpoint, the MCP
163
+ registration and any delegate waiting for review:
164
+
165
+ ```bash
166
+ uv run vtai doctor
167
+ ```
168
+
169
+ ```
170
+ VT-AIAgent doctor — v0.6.2
171
+ ✓ agent version 0.6.2
172
+ ✓ uv 0.13.1
173
+ ✓ config file /Users/you/.vtaiagent/settings.toml (user-level)
174
+ ✓ config valid no issues
175
+ ✓ writable roots 3 present
176
+ ✓ protected paths 4 deny globs
177
+ ✓ shell allowlist 22 binaries
178
+ ✓ model endpoint http://localhost:11434/v1
179
+ ! optional tools not installed: ffmpeg (for transcode_media, voice helpers)
180
+ ✓ mcp registration opencode (global)
181
+ ✓ pending approvals none
182
+ ```
183
+
184
+ It exits non-zero if something needs fixing. Then, to try the pieces:
185
+
186
+ ```bash
187
+ uv run vtai info # config, provider, guardrails
188
+ uv run vtai tools # the registered tools
189
+ uv run vtai run system_info # a read-only call, no LLM involved
190
+ uv run vtai goal --dry-run "list my Downloads" # full loop with the model
191
+ ```
192
+
193
+ ### 4. Use it from your agent CLI
194
+
195
+ ```bash
196
+ scripts/register-mcp.sh # opencode (global), claude (user), pi (CLI)
197
+ ```
198
+
199
+ opencode's registration lands in `~/.config/opencode/opencode.jsonc` and
200
+ merges with your existing providers. The script registers an **absolute**
201
+ `uv` path on purpose: a bare `uv` fails with
202
+ `NotFound: ChildProcess.spawn (uv ...)` when the client spawns the server
203
+ from a daemon with a minimal PATH.
204
+
205
+ ### Use
206
+
207
+ ```bash
208
+ uv sync # install
209
+ uv run vtai init # create/validate config, dirs, writable roots
210
+ uv run vtai info # show config
211
+ uv run vtai tools # list tools
212
+ uv run vtai run system_info # invoke a tool
213
+ uv run vtai run run_shell --params '{"command":"ls ~/Downloads","dry_run":true}'
214
+ scripts/register-mcp.sh # register with opencode / claude
215
+ ```
216
+
217
+ To get the `vtai` CLI outside the checkout, install it as an editable tool so
218
+ it tracks your working copy:
219
+
220
+ ```bash
221
+ uv tool install --editable /path/to/vtai-agent # -> ~/.local/bin/vtai
222
+ ```
223
+
224
+ Install into another project as a dependency with
225
+ `uv add git+https://github.com/velsrocky/vtai-agent@v0.6.6`, or pull the
226
+ image with `docker build -t vtai-agent .`.
227
+
228
+ ## Registry & distribution
229
+
230
+ PyPI: `uv tool install vtai-agent` (or `pipx install vtai-agent`) — publishes
231
+ on `v*` tags via Trusted Publishing with build attestations (see
232
+ `.github/workflows/publish.yml`). The `<!-- mcp-name: ... -->` comment above
233
+ is the PyPI-package proof the official MCP registry expects; `server.json`
234
+ carries the registry manifest (`io.github.velsrocky/vtai-agent`, stdio)
235
+ alongside a metadata-consistency gate in the publish workflow. Only install
236
+ from these sources; a similar repository name does not establish provenance.
237
+
238
+ Register with the official MCP registry after a PyPI release:
239
+ ```bash
240
+ mcp-publisher auth --oauth2-github
241
+ mcp-publisher publish server.json
242
+ ```
243
+
244
+ ## Certification (vtai-cert)
245
+
246
+ Safety claims are probes, not prose: `vtai certify` runs the executable
247
+ guardrail-compliance suite (deny-glob containment, dotdot/symlink escapes,
248
+ fail-closed shell, injected-argv resistance, dry-run-first, kill switch,
249
+ audit exactness, echo-loop breaker) and emits a digest-stamped certificate;
250
+ exit 0 gates the release in CI. See [docs/certification.md](docs/certification.md).
251
+
252
+ ## HTTP transport
253
+
254
+ `vtai serve --transport http --port 8765 --token $VT_HTTP_TOKEN` exposes the
255
+ same guarded tools over MCP StreamableHTTP at `/mcp`. The bearer token is
256
+ required when binding anything other than localhost; the server refuses to
257
+ bind externally without one.
258
+
259
+ ## Development
260
+
261
+ ```bash
262
+ uv sync --dev
263
+ uv run pytest tests/ -v
264
+ uv run ruff check src/ tests/
265
+ uv run python scripts/generate_tool_catalog.py --check # tool-catalog doc drift
266
+ ```
267
+
268
+ The config file is resolved as described in [Configure](#2-configure): `$VT_CONFIG`
269
+ → the checkout's `config/settings.toml` → `~/.vtaiagent/settings.toml`. Env
270
+ overrides use the `VT_` prefix with `__` nesting (e.g.
271
+ `VT_GUARDRAILS__DRY_RUN_DEFAULT=false`). Config is validated at startup
272
+ (`validate_settings`) — a bad `deny_globs` pattern or a missing writable root
273
+ fails fast, not mid-run.
274
+
275
+ **Windows:** the shell sandbox needs the allowlisted binaries to resolve, and a
276
+ fresh Windows runner does not put Git for Windows' coreutils on PATH. Install
277
+ Git for Windows and prepend its `usr\bin` (this is what CI does):
278
+
279
+ ```powershell
280
+ Add-Content $env:GITHUB_PATH "C:\Program Files\Git\usr\bin" # CI
281
+ # locally: add "C:\Program Files\Git\usr\bin" to your PATH
282
+ ```
283
+
284
+ Everything else (install, config, MCP registration) is the same on all three
285
+ platforms.
286
+
287
+ ## Orchestrator
288
+
289
+ `vtai goal` runs a bounded ReAct-style agent loop. Each step the model
290
+ either emits one fenced-JSON tool call (parsed, guarded, executed, result fed
291
+ back) or a final natural-language answer. Bounds enforced every step:
292
+ `max_steps`, kill switch, and `budget_usd_per_run`. A lonely `dry_run: true`
293
+ text blob that fails to parse or misses its `tool` key is retried with a
294
+ corrective note instead of ending the run as "success"; the run mode is
295
+ authoritative — the model cannot override it — and every tool call is
296
+ persisted as a `Step` row and audit entry. If a paid provider's spend can't
297
+ be determined (no `cost_per_1k_*` config and no native cost), the run fails
298
+ closed rather than reporting free. The system prompt describes the machine's
299
+ actual OS.
300
+
301
+ We use a text protocol rather than native function-calling on purpose: the
302
+ local Ollama models on this box emit tool calls as raw JSON text even when
303
+ native tool schemas are provided (verified 2026-10-03 with qwen2.5-coder:7b),
304
+ so the text protocol behaves identically across local and cloud providers.
305
+
306
+ ## Auto mode
307
+
308
+ `auto_mode` runs the loop you describe: it dispatches the goal to a coding
309
+ CLI (opencode by default), reviews the reply against the goal with the
310
+ configured model, and if the reply is insufficient, sends a follow-up prompt
311
+ quoting the critique — up to `max_iterations`. The judge only accepts a bare
312
+ `OK` as satisfied (hedged replies fail). Spend is metered against
313
+ `guardrails.budget_usd_per_run` and attributed in the run's `spend` detail:
314
+ the judge's own LLM calls always; the delegate CLI when it can report it —
315
+ `claude -p --output-format json` exposes `total_cost_usd`, and an unreadable
316
+ report from a cost-capable CLI fails closed. `opencode`/`pi` report no
317
+ headless spend (documented vendor behavior, not guess-parsed event streams),
318
+ so their runs carry `"cli_usd": "unmeasured"` and stay bounded only by
319
+ `max_iterations`/`timeout`. From inside opencode or
320
+ Claude Code, start the prompt with `@vt-ai_automode_on`: the MCP server's
321
+ instructions route that to the tool.
322
+
323
+ ```bash
324
+ uv run vtai run auto_mode --params '{"goal":"add a unit test for parser.py","working_dir":".","max_iterations":3}'
325
+ ```
326
+
327
+ ## Delegate coding
328
+
329
+ `delegate_coding` snapshots the target repo, runs the chosen CLI headlessly,
330
+ captures the diff, and on `verify_command` failure (or timeout / non-zero
331
+ exit) rolls back. Three guards worth knowing:
332
+
333
+ - **Human approval gate**: `require_approval` defaults to `true`. Interactively
334
+ it shows `git diff --stat` and asks before keeping changes. Non-interactive
335
+ runs (MCP, scripted, orchestrated) keep the changes in the worktree as a
336
+ **pending approval** — the run reports failure so automated loops stop — and
337
+ `vtai approve <id>` (keep) or `vtai approve <id> --rollback` resolves it.
338
+ While a pending approval exists for a repo, delegate_coding refuses new
339
+ work there, even with `require_approval: false`. Pass `require_approval:
340
+ false` only for tasks a human has already reviewed.
341
+ - **`pi` needs credentials**: with no authenticated provider, `pi -p` exits 0
342
+ having emitted a tool-call JSON block and applied nothing. The tool now
343
+ fails closed with a clear message. Run `pi auth check` — or prefer
344
+ `opencode` / `claude` — before delegating to pi.
345
+
346
+ Claude delegations run with `--output-format json`, so the reply text comes
347
+ from `result` and the invocation's `total_cost_usd` is captured into the
348
+ tool detail as `cli_cost_usd` (and audited). Delegations to opencode/pi
349
+ report `"cli_cost_usd": "unmeasured"`.
350
+
351
+ ```bash
352
+ uv run vtai approve # list pending delegate approvals
353
+ uv run vtai approve 7 # keep the pending diff
354
+ uv run vtai approve 7 --rollback
355
+ ```
356
+
357
+ `vtai info` shows how many approvals are waiting, so a delegate that was kept
358
+ for review does not get lost.
359
+
360
+ ## Inspect applications (REA capability)
361
+
362
+ `inspect_application` integrates [REA](https://github.com/morluto/rea) so the
363
+ agent can statically explain how a local app works without source code.
364
+ Because REA's inspection is powerful, it runs entirely inside vtai-agent's
365
+ guards and is **off by default**:
366
+
367
+ ```toml
368
+ [capabilities.rea]
369
+ enabled = true # enable only after install vetting
370
+ binary = "rea"
371
+ command_prefix = ["analyze-javascript-application"]
372
+ timeout = 1800.0
373
+ keep_copy = false
374
+ ```
375
+
376
+ Safety contract, in order: the target must pass `deny_globs`; it is then
377
+ copied into a quarantine dir under `data_dir` (denied entries and
378
+ out-of-policy symlinks are skipped on the way in, so REA never reads a path
379
+ you couldn't); REA runs on the copy with a minimal child environment (no
380
+ inherited API keys) and is killed after `timeout`; output JSON is parsed and
381
+ a sha256 evidence digest plus REA's evidence id are written to the audit
382
+ trail; the optional judge pass (same model and fail-closed policy as
383
+ auto_mode) verifies the evidence answers the goal before the result reports
384
+ success. The original target is never touched, and the quarantine copy is
385
+ deleted unless `keep_copy` is true.
386
+
387
+ ```bash
388
+ uv run vtai run inspect_application \
389
+ --params '{"goal":"explain how Notes builds its CSV export header",
390
+ "target":"~/quarantine/Notes-app","dry_run":true}'
391
+ ```
392
+
393
+ Install REA only from official sources (github.com/morluto/rea or the
394
+ `rea-agents` package on npm — verify provenance), not mirror sites.
395
+
396
+ ## Audit log
397
+
398
+ Every allow/deny is stored in `~/.vtaiagent/vtai.db`. Query it without a SQL
399
+ client:
400
+
401
+ ```bash
402
+ uv run vtai audit --limit 20
403
+ uv run vtai audit --denied --grep ssh
404
+ uv run vtai audit --action delegate --run-id 193
405
+ ```
406
+
407
+ To see one run end to end — the goal, every tool call, what was allowed or
408
+ refused, and the spend:
409
+
410
+ ```bash
411
+ uv run vtai runs # list runs
412
+ uv run vtai runs --summary # aggregate: spend percentiles, tool counts, denial codes (JSON with --json)
413
+ uv run vtai show 193 # one run in full
414
+ ```
415
+
416
+ ## Voice control (`agent-listen` / `agent-speak`)
417
+
418
+ Both helpers talk to a local Lemonade Server (TTS `kokoro-v1`, STT
419
+ `Whisper-Tiny` on Linux/macOS — Windows defaults to `Moonshine-Tiny-Streaming`
420
+ because the whisper.cpp recipe misbehaves on some AMD GPUs) at
421
+ `http://localhost:13305`. `LEMONADE_STT_MODEL` overrides the STT model on any
422
+ platform. Use `--seconds N` to change the
423
+ record window, `--vad` for silence-based early stop, `LEMONADE_BASE_URL` to
424
+ point elsewhere.
425
+
426
+ The scripts are versioned in `scripts/`; install them with:
427
+
428
+ ```bash
429
+ uv run vtai voice-sync
430
+ ```
431
+
432
+ `voice-sync` copies both helpers to `~/.local/bin` (override with `--dest`),
433
+ marks them executable, and reports `installed` / `updated` / `unchanged` per
434
+ file. `vtai voice-sync --check` only reports drift (exit 1 when out of sync)
435
+ — re-run without `--check` after pulling script updates. Set
436
+ `VTAI_SCRIPTS_DIR` to sync from a different checkout.
437
+
438
+ Cross-platform notes (verified on macOS 27/ARM, 2026-10-12):
439
+
440
+ > **Supported: Linux + macOS + Windows** — all three run in CI (Linux and
441
+ > macOS push-blocked; Windows reinstated in 0.6.0 after the suite became
442
+ > portable, with Git Bash coreutils prepended to PATH so the allowlisted
443
+ > sandbox resolves real binaries). rsync-dependent tests skip on runners
444
+ > without rsync and are complemented by a fail-closed counter-test.
445
+
446
+ - `arecord` is used for recording on Linux; elsewhere the helpers fall back
447
+ to `ffmpeg` (`-f pulse` / `avfoundation`), so macOS and Windows work too.
448
+ - `--vad` currently requires Linux `arecord`; on other platforms use
449
+ `--seconds N`.
450
+ - macOS mic selection: avfoundation device indexes vary per machine — list
451
+ them with `ffmpeg -f avfoundation -list_devices true -i ""` and override
452
+ via `AGENT_LISTEN_FF_INPUT` (default audio-only `-f avfoundation -i ":0"`).
453
+ Machines with no built-in microphone (many mini PCs) need an external/USB
454
+ device attached, otherwise recording yields silence (exit 6, no speech).
455
+ - Playback tries `ffplay` → `afplay` (macOS) → `mpg123` → `aplay` in order.
456
+ - The ALSA mic-level fix and `vtai-audio-fix` systemd unit below are
457
+ Linux-specific; Windows and macOS handle this in System Settings.
458
+ - `guardrails.trusted_bin_dirs` defaults per OS (`C:\Windows\System32`,
459
+ `/usr/bin, /bin, /usr/local/bin, /opt/homebrew/bin`, and the Linux set);
460
+ entries missing on the current machine are ignored (validation only fails
461
+ when none exist), so one config can list both Linux and macOS paths.
462
+ - `system_info` is portable: memory via `sysctl`/`vm_stat` and disks via
463
+ `/`, `$HOME`, `/Volumes/*` on macOS (Metal GPU note; no `rocm-smi` there).
464
+ - `transcode_media` defaults to `libx264` off Linux (VAAPI is Linux-only);
465
+ an explicit `"codec"` still overrides.
466
+ - `disk_audit` also measures `~/Library/Caches`, `~/Library/Logs`,
467
+ `/Library/Caches`, and `~/.Trash` on macOS; fresh mac installs get
468
+ `vm_stat` (instead of Linux-only `free`/`nproc`) in the starter allowlist.
469
+
470
+ ### Troubleshooting
471
+
472
+ **Setup and MCP**
473
+
474
+ - **`opencode mcp list` shows `✗ vtai-agent failed: NotFound: ChildProcess.spawn (uv ...)`**:
475
+ the client spawned the server with a minimal PATH (daemons give
476
+ `PATH=/usr/bin:/bin:...`), so a bare `uv` was unresolvable. Re-run
477
+ `scripts/register-mcp.sh` — it registers the absolute `uv` path — or edit
478
+ the `mcp.servers.vtai-agent.command` array in your opencode config to the
479
+ full path of `uv` (`command -v uv`).
480
+ - **`opencode mcp list` says `No MCP servers configured`**: the registration
481
+ is per-project. Run it from the checkout, or use the global registration
482
+ `scripts/register-mcp.sh` writes to `~/.config/opencode/opencode.jsonc`.
483
+ - **"config exists: …/config/settings.toml" and your settings are ignored**:
484
+ the checkout's config wins over `~/.vtaiagent/settings.toml`. Set
485
+ `VT_CONFIG="$HOME/.vtaiagent/settings.toml"` and re-run `vtai init`;
486
+ `vtai info` prints the file in use and where it came from.
487
+ - **`uv sync` fails with `Required uv version ... does not match`**: install
488
+ uv >= 0.13.1 (the lockfile is revision 5).
489
+
490
+ **Voice (Linux / PipeWire / ALSA)**
491
+
492
+ - **Mic produces silence or clipped garbage transcripts**: the internal mic
493
+ boost is pinned too high and clips the input. Fix (also applied at
494
+ every login via `~/.config/systemd/user/vtai-audio-fix.service`):
495
+
496
+ ```bash
497
+ amixer -c 2 sset 'Internal Mic Boost' 0
498
+ amixer -c 2 sset 'Capture' 75%
499
+ ```
500
+
501
+ - **Headphone jack not detected**: the kernel reports no jack on the combo
502
+ port, so the headphone amp stays off. Check `pw-cli enum-params 53
503
+ EnumRoute` — the `analog-output-headphones` route shows `available: no`
504
+ until a headset is firmly plugged in. Route manually with
505
+ `wpctl set-route 38 1` if detection is flaky.
506
+
507
+ - **Default source is the wrong mic**: list routes with
508
+ `pw-cli enum-params 53 Route` and select
509
+ with `wpctl set-route 47 <index>` (`0` = internal mic, `1` = headset mic).
510
+
511
+ - **STT returns `[BLANK_AUDIO]` or noise transcripts**: verify levels with
512
+ `arecord -d 3 -f cd /tmp/t.wav` and inspect the peak; near 0 means the
513
+ selected route carries no signal, full-scale means clipping (lower the
514
+ boost).
515
+
516
+ **Delegate coding**
517
+
518
+ - **Files changed via `delegate_coding` are still sitting in the repo**: that
519
+ is a pending approval — the changes are kept for your review until you run
520
+ `vtai approve <id>` to keep them or `--rollback` to discard them. Pass
521
+ `require_approval: false` only for tasks a human has already reviewed.
522
+
523
+ ## Contributing
524
+
525
+ See [CONTRIBUTING.md](CONTRIBUTING.md) — setup, the guardrail rules any
526
+ change has to respect, and the test/lint commands.
527
+
528
+ ## Security
529
+
530
+ Guardrails, what counts as a vulnerability, and how to report one privately:
531
+ [SECURITY.md](SECURITY.md).
532
+
533
+ ## License
534
+
535
+ MIT — see [LICENSE](LICENSE). Use it, fork it, run it on your own machine.