vv-agent 0.7.0

VectorVein agent runtime, SDK, CLI, tools, and workspace backends
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
# vv-agent-rs

[中文文档](README_ZH.md)

`vv-agent-rs` is the Rust workspace for the `vv-agent` crate: an embeddable
agent runtime, SDK, CLI, tool system, memory layer, and workspace abstraction
for model-driven automation.

It is designed around explicit agent control flow. The backward-compatible
default uses `task_finish` to complete and `ask_user` to pause. Hosts can
instead opt into `NoToolPolicy::Finish` or `NoToolPolicy::WaitUser` when a
normal assistant response should be terminal. The runtime applies the declared
policy without classifying whether the text looks like a final answer.

## Architecture

```text
AgentRuntime
├── LLM client              # vv-llm backed chat client, endpoint resolution, streaming
├── CycleRunner             # one model turn: prompt, response, tool-call plan
├── ToolOrchestrator        # tool policy, approval, dispatch, timeout, telemetry
├── RuntimeHookManager      # before/after hooks for LLM, tools, and memory
├── MemoryManager           # context budgeting, compaction, artifacts, session memory
├── RunHandle / RunEvent    # live control, typed events, event-store replay
├── RuntimeExecutionBackend # run scheduling
│   ├── InlineBackend       # synchronous default
│   ├── ThreadBackend       # non-blocking task submission
│   └── DistributedBackend  # checkpointed cycles with pluggable dispatch
└── WorkspaceBackend        # file/object I/O boundary for tools
    ├── LocalWorkspaceBackend
    ├── MemoryWorkspaceBackend
    └── S3WorkspaceBackend
```

Provider request building, endpoint transport, retries, streaming deltas, token
limits, usage accounting, and provider-specific protocol details are delegated
to the published `vv-llm` crate. `vv-agent` focuses on agent execution: prompts,
tools, hooks, memory, sessions, workspace access, and orchestration.

## Setup

Run commands from this repository root:

```bash
cd vv-agent-rs
cargo test -p vv-agent
```

Most real-model examples and the CLI read a local `vv-llm` settings file. Keep
the credential-bearing file untracked:

```bash
cp crates/vv-agent/tests/dev_settings.example.json local_settings.json
# Fill endpoint keys in local_settings.json.
```

The default settings path is `local_settings.json`. You can override it with
`VV_AGENT_LOCAL_SETTINGS` for examples or `--settings-file` for the CLI.

## Quick Start

### CLI

```bash
cargo run -p vv-agent -- \
  --prompt "Summarize this repository" \
  --backend deepseek \
  --model deepseek-v4-pro \
  --settings-file local_settings.json \
  --workspace ./workspace \
  --verbose
```

CLI flags:

| Flag | Purpose |
| --- | --- |
| `--prompt` | Required user task. |
| `--backend` | Backend key under `LLM_SETTINGS.backends`. |
| `--model` | Model key under the selected backend. |
| `--settings-file` | Local `vv-llm` settings file. |
| `--workspace` | Directory exposed to workspace tools. |
| `--max-cycles` | Maximum runtime cycles before stopping. |
| `--language` | Prompt/tool guidance locale. |
| `--agent-type` | Optional agent profile type such as `computer`. |
| `--verbose` | Emit per-cycle runtime events. |

### Agent + Runner SDK

Use `Agent` + `Runner` for new embedded applications. `Agent`
describes instructions, model, tools, handoffs, hooks, and defaults. `Runner`
owns model providers, workspace defaults, and execution. `RunConfig` overrides
one run without changing the agent definition, including the public
`ExecutionMode` for inline, threaded, or distributed execution.
Per-run controls also cover tool registry factories, before-cycle and
interruption messages, sub-task management, runtime observers, log previews,
and LLM request debug dumps. See `docs/runtime-control.md` for precedence and
language-adaptation details.

```rust
use vv_agent::{Agent, ExecutionMode, ModelRef, Runner, RunConfig, VvLlmModelProvider};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let provider = VvLlmModelProvider::from_settings_file("local_settings.json")
        .with_default_backend("deepseek");
    let runner = Runner::builder()
        .model_provider(provider)
        .workspace("./workspace")
        .build()?;

    let agent = Agent::builder("assistant")
        .instructions("You plan, use tools when useful, and call task_finish when done.")
        .model(ModelRef::backend("deepseek", "deepseek-v4-pro"))
        .build()?;

    let result = runner
        .run_with_config(
            &agent,
            "Create notes.md with three project takeaways.",
            RunConfig::builder()
                .max_cycles(12)
                .max_handoffs(4)
                .execution_mode(ExecutionMode::Inline)
                .build(),
        )
        .await?;
    println!("{:?}", result.final_output());
    Ok(())
}
```

A handoff is an outer Runner control transfer, not an agent-as-tool call. The
target Agent resolves its own model and model settings, while the active
session, cancellation token, and mutated shared state continue across the
transition. `max_handoffs` defaults to `10` and limits control transfers
independently from `max_cycles`. Approval resume preserves the same behavior.

No-tool completion is an explicit host control. Configure it on an Agent or
override it for one run; per-run configuration wins over a configured Runner
default, which wins over the Agent value. Omitting every layer keeps
`NoToolPolicy::Continue`.

```rust
use vv_agent::{Agent, NoToolPolicy, RunConfig};

let natural_answer_agent = Agent::builder("assistant")
    .instructions("Answer from the available context.")
    .no_tool_policy(NoToolPolicy::Finish)
    .build()?;
let force_tool_driven_run = RunConfig::builder()
    .no_tool_policy(NoToolPolicy::Continue)
    .build();
```

Inspect `RunResult::completion_reason()`, `completion_tool_name()`, and
`partial_output()` to distinguish natural completion, tool-driven completion,
waits, cancellation, failure, and max-cycle exhaustion.

Reusable run defaults belong in
`Runner::builder().default_run_config(...)`. Provider resolution is per-run
then Runner. Model resolution is per-run, Agent, Runner, then the selected
provider default. Model settings merge from provider to Runner to Agent to
per-run, with each later layer overriding earlier fields. Replacing the
provider for one run does not reuse a backend-bound model from the Runner.

Sessions keep conversation history across runner calls:

```rust
use vv_agent::{MemorySession, RunConfig};

let session = MemorySession::new("thread-001");
runner
    .run_with_config(&agent, "Analyze the current workspace.", RunConfig::builder().session(session.clone()).build())
    .await?;
let result = runner
    .run_with_config(&agent, "Continue with follow-up suggestions.", RunConfig::builder().session(session).build())
    .await?;
```

### Live Runs and Events

`Runner::run()` and `run_with_config()` are the one-shot entrypoints. Use
`Runner::start()` when an application needs live UI/server control: subscribe
to events, approve pending tools, cancel a run, or await the final result from
one `RunHandle`. `Runner::stream()` is a convenience wrapper over `start()` for
typed live events.

```rust
use vv_agent::{ApprovalDecision, RunConfig, RunEventPayload};

let handle = runner
    .start(&agent, "Inspect the workspace and report findings.", RunConfig::default())
    .await?;
let mut events = handle.events();

while let Some(event) = events.next().await {
    match event?.payload() {
        RunEventPayload::AssistantDelta { delta } => print!("{delta}"),
        RunEventPayload::ToolCallStarted { tool_name, .. } => {
            eprintln!("tool started: {tool_name}");
        }
        RunEventPayload::ApprovalRequested { request_id, .. } => {
            handle.approve(request_id, ApprovalDecision::allow()).await?;
        }
        _ => {}
    }
}

let result = handle.result().await?;
```

Each `RunEvent` is a v1 envelope with `event_id`, `run_id`, `trace_id`,
optional session and parent identifiers, timing, metadata, and a typed
`RunEventPayload`. `JsonlRunEventStore` can append events and replay a run,
including child events linked by parent run id.

Live tool approval uses `ApprovalProvider` and the handle-owned broker. The
model-facing `ask_user` tool remains for requesting user input as part of the
conversation. Host applications can also attach `ContextProvider` values for
ordered prompt fragments and `MemoryProvider` values for external search, save,
and compaction lifecycle hooks.

`ToolPolicy` exposes `Default`, `Always`, `Never`, and `OnRequest` approval
modes. `Default` inherits the next configured policy; explicit `OnRequest`
follows each tool's static or dynamic approval declaration. `Always` forces
approval and `Never` bypasses it without evaluating a dynamic tool predicate.

### Run Budgets

`RunConfig::budget_limits` can independently limit total tokens, uncached input
tokens, total or exact-name tool calls, active wall time, and host-metered cost.
All limits are optional and task-neutral: the framework does not inspect the
prompt, task category, milestones, or answer quality when enforcing them.

Inspect `result.budget_usage()` and `result.budget_exhaustion()`. A budget stop
is a typed failed result with completion reason `budget_exhausted`, not a
successful answer. Runs without configured limits preserve the existing event
flow. See [Run Budgets](docs/run-budgets.md) and
`crates/vv-agent/examples/07_token_budget_guard.rs`.

### App Server

Use the App Server when a product shell needs to drive `vv-agent` over a stable
JSON-RPC protocol instead of linking directly to runtime internals. It supports
stdio JSONL transport, thread and turn lifecycle requests, live item
notifications, approval server requests, replay, schema generation, and a typed
Rust test client.

```bash
vv-agent app-server --listen stdio
vv-agent app-server generate-json-schema --out target/app-server-schema/json
vv-agent app-server generate-ts --out target/app-server-schema/typescript
```

See `crates/vv-agent/docs/app_server.md` for protocol examples and client
responsibilities.

### Low-Level Runtime

Use the runtime directly only when you need to assemble the LLM client, prompt,
tool registry, workspace, and run controls yourself. New embedded applications
should start with `Agent` + `Runner`.

```rust
use std::path::PathBuf;

use vv_agent::config::build_vv_llm_from_local_settings;
use vv_agent::prompt::{build_system_prompt_with_options, BuildSystemPromptOptions};
use vv_agent::{build_default_registry, AgentRuntime, AgentTask, RuntimeRunControls};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let (llm, resolved) = build_vv_llm_from_local_settings(
        "local_settings.json",
        "deepseek",
        "deepseek-v4-pro",
        90.0,
    )?;
    let runtime = AgentRuntime::new(llm).with_tool_registry(build_default_registry());
    let system_prompt = build_system_prompt_with_options(
        "You are a reliable execution agent.",
        BuildSystemPromptOptions {
            language: "zh-CN".to_string(),
            use_workspace: true,
            enable_todo_management: true,
            ..BuildSystemPromptOptions::default()
        },
    );

    let mut task = AgentTask::new(
        "demo",
        resolved.model_id,
        system_prompt,
        "Read the workspace README and summarize the project.",
    );
    task.max_cycles = 12;

    let result = runtime.run_with_controls(
        task,
        RuntimeRunControls {
            workspace: Some(PathBuf::from("./workspace")),
            ..RuntimeRunControls::default()
        },
    )?;
    println!("{:?}: {:?}", result.status, result.final_answer);
    Ok(())
}
```

See `crates/vv-agent/examples/01_quick_start.rs` for a complete low-level
runtime version with event logging.

## Core Capabilities

| Area | What `vv-agent` provides |
| --- | --- |
| Runtime | Multi-cycle model execution, explicit terminal states, live `RunHandle`, cancellation, typed events, event replay, and max-cycle handling. |
| Tools | Built-in tools plus a `ToolOrchestrator` path for policy, approval, dispatch, timeout, and telemetry. |
| SDK | `Agent`, `Runner`, `RunConfig`, `ModelSettings`, typed tools, `Agent::as_tool()`, `RunEvent`, providers, and `Session`. |
| Memory | Token budgeting, prompt-too-long retries, micro and full compaction, artifact-backed large tool results, image trimming, session memory, and external provider hooks. |
| Hooks | Rust `RuntimeHook` implementations can inspect or patch LLM calls, tool calls, memory compaction, and run lifecycle behavior. |
| Sub-agents | Runtime-backed sub-task creation, batch submission, background status queries with wait-for-completion support, continuation, steering, and inherited streaming callbacks. |
| Skills | Skill directory discovery, frontmatter parsing, validation, prompt rendering with budget limits, activation, and activation history. |
| Workspace | Local, in-memory, and S3 object-store backends behind one `WorkspaceBackend` boundary. |

## Execution Backends

The public SDK selects scheduling through `ExecutionMode`. Lower-level runtime
backend structs remain available for advanced integrations:

| Backend | Use case |
| --- | --- |
| `ExecutionMode::Inline` | Default synchronous execution in the current process. |
| `ExecutionMode::Threaded` | Submit runs without blocking the caller. |
| `ExecutionMode::Distributed` | Checkpointed cycle execution with serializable runtime recipes and pluggable dispatch. |

Checkpointed runs can store state in memory, SQLite, or Redis. The optional
`apalis` feature adds an Apalis job bridge for applications that already use
Apalis workers:

```bash
cargo test -p vv-agent --features apalis --test apalis_backend
```

The distributed API also has an inline fallback, which is useful for local
development and tests. See `crates/vv-agent/examples/23_distributed_backend.rs`.

## Workspace Backends

All built-in file tools go through `WorkspaceBackend`. That keeps local files,
memory-backed files, and S3-compatible object storage on the same tool contract.

`find_files` and `search_files` include safety defaults for large workspaces:
bounded result counts, hidden/dependency directory filtering, explicit ignored
path inclusion, and local `rg` acceleration when available.

## Examples

The numbered examples are the best way to explore the public API:

```bash
cargo run -p vv-agent --example 01_quick_start
cargo run -p vv-agent --example 03_sdk_client
cargo run -p vv-agent --example 04_session_api
cargo run -p vv-agent --example 23_distributed_backend
cargo run -p vv-agent --example 24_workspace_backends
cargo run -p vv-agent --example 26_agent_runner_facade
cargo run -p vv-agent --example 27_facade_handoff
cargo run -p vv-agent --example 28_facade_approval_background_trace
```

See `crates/vv-agent/examples/README.md` for the full example index covering
Agent + Runner, runtime hooks, custom tools, handoffs, live approval,
background tasks, tracing, sub-agent pipelines, skills, streaming, cancellation,
state stores, execution backends, workspace backends, and temporary tool
injection.

## Live Smoke Tests

Live tests are opt-in and use a local settings file without printing
credentials. By default they read the untracked
`crates/vv-agent/tests/dev_settings.json`; start from
`crates/vv-agent/tests/dev_settings.example.json`.

```bash
VV_AGENT_RUN_LIVE_TESTS=1 \
cargo test -p vv-agent --test live_deepseek -- --ignored

VV_AGENT_RUN_LIVE_TESTS=1 \
cargo test -p vv-agent --test live_edit_file -- --ignored --test-threads=1
```

The live suite exercises direct runtime completion, SDK completion,
`ask_user`, todo updates, memory notes, skill activation, workspace tools,
image reading, safe `edit_file` recovery, foreground and background shell
commands, sub-agent waiting/status checks, and configured sub-agent delegation.

## Verification

Run the standard checks from `vv-agent-rs/`:

```bash
cargo fmt --check
cargo test -p vv-agent
cargo check --examples
cargo clippy --all-targets --all-features -- -D warnings
```

Focused checks that are useful while editing public docs and examples:

```bash
cargo test -p vv-agent --test public_api
cargo test -p vv-agent --test examples_coverage
```

## Repository Layout

```text
vv-agent-rs/
  Cargo.toml
  crates/vv-agent/
    src/
      cli/        # CLI entrypoint and task construction
      config/     # LLM settings loading and model resolution
      llm/        # LLM trait, scripted test client, vv-llm client bridge
      memory/     # compaction, artifacts, session memory, token budgeting
      prompt/     # system prompt sections and prompt-cache metadata
      agent.rs    # public Agent builder
      runner.rs   # public Runner over runtime execution
      run_config.rs
      model.rs
      model_settings.rs
      sessions.rs
      runtime/    # agent runtime, hooks, backends, cancellation, sub-agents
      skills/     # skill discovery, parsing, validation, activation
      tools/      # registry, schemas, dispatcher, built-in handlers
      workspace/  # local, memory, and S3 workspace backends
    examples/
    tests/
  docs/
```

Additional design notes live under `docs/`, especially `docs/architecture.md`
and `docs/model-settings.md`.