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.
- vtai_agent-0.6.6/LICENSE +21 -0
- vtai_agent-0.6.6/PKG-INFO +535 -0
- vtai_agent-0.6.6/README.md +506 -0
- vtai_agent-0.6.6/pyproject.toml +82 -0
- vtai_agent-0.6.6/pyproject.toml.orig +65 -0
- vtai_agent-0.6.6/src/vtai_agent/__init__.py +12 -0
- vtai_agent-0.6.6/src/vtai_agent/certify.py +385 -0
- vtai_agent-0.6.6/src/vtai_agent/cli.py +581 -0
- vtai_agent-0.6.6/src/vtai_agent/config.py +274 -0
- vtai_agent-0.6.6/src/vtai_agent/db.py +395 -0
- vtai_agent-0.6.6/src/vtai_agent/doctor.py +296 -0
- vtai_agent-0.6.6/src/vtai_agent/errors.py +46 -0
- vtai_agent-0.6.6/src/vtai_agent/guardrails.py +370 -0
- vtai_agent-0.6.6/src/vtai_agent/mcp_server.py +321 -0
- vtai_agent-0.6.6/src/vtai_agent/models.py +70 -0
- vtai_agent-0.6.6/src/vtai_agent/orchestrator.py +305 -0
- vtai_agent-0.6.6/src/vtai_agent/providers.py +115 -0
- vtai_agent-0.6.6/src/vtai_agent/py.typed +0 -0
- vtai_agent-0.6.6/src/vtai_agent/tools/__init__.py +17 -0
- vtai_agent-0.6.6/src/vtai_agent/tools/automode.py +279 -0
- vtai_agent-0.6.6/src/vtai_agent/tools/backup.py +134 -0
- vtai_agent-0.6.6/src/vtai_agent/tools/delegate.py +417 -0
- vtai_agent-0.6.6/src/vtai_agent/tools/disk.py +155 -0
- vtai_agent-0.6.6/src/vtai_agent/tools/files.py +194 -0
- vtai_agent-0.6.6/src/vtai_agent/tools/media.py +145 -0
- vtai_agent-0.6.6/src/vtai_agent/tools/rea.py +363 -0
- vtai_agent-0.6.6/src/vtai_agent/tools/registry.py +90 -0
- vtai_agent-0.6.6/src/vtai_agent/tools/shell.py +96 -0
- vtai_agent-0.6.6/src/vtai_agent/tools/system_info.py +255 -0
vtai_agent-0.6.6/LICENSE
ADDED
|
@@ -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
|
+
[](https://github.com/velsrocky/vtai-agent/actions/workflows/ci.yml)
|
|
34
|
+
[](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.
|