Model contract
The terminal engine is provider and API agnostic. You choose an endpoint, choose a model when that endpoint exposes model choice, provide the key, and Veyyon calls that API directly. The endpoint can be a local server (Ollama, LM Studio), a direct provider API (OpenAI, Anthropic, Google), or any OpenAI-compatible gateway.
This page is the contract between the harness and the model. For copy-paste provider setup, see Configuring providers. For day-to-day model switching, see Models and providers.
The three things you bring
A BYOK (bring-your-own-key) run needs three facts:
| Fact | What it is | Where it lives |
|---|---|---|
| Endpoint | Base URL and API kind | A built-in provider, or a custom provider under providers: in ~/.veyyon/agent/models.yml |
| Model | The model id the endpoint understands | Pinned with --model / /model, or discovered from the provider |
| Key | Credential the endpoint accepts | A provider environment variable, /login, or a models.yml apiKey |
Veyyon does not route your requests through a service of its own for BYOK providers and does not add telemetry egress. The key is used only to talk to the provider you chose.
Example shape
# ~/.veyyon/agent/models.yml
providers:
deepseek:
baseUrl: https://api.deepseek.com
api: openai-completions
apiKey: DEEPSEEK_API_KEY # env-var name or literal
models:
- id: deepseek-chat
name: DeepSeek Chat
contextWindow: 128000
maxTokens: 8192
$ export DEEPSEEK_API_KEY=sk-...
$ veyyon --model deepseek/deepseek-chat
What the harness owns
These behaviors stay constant no matter which endpoint you point at:
- The workflow: read, edit, verify, stop when the work is done.
- Tool dispatch, argument handling, and edit verification through the hashline edit engine
(with
apply_patch/patch/replaceavailable viaedit.mode). - OS-level shell isolation (Landlock/seccomp on Linux, Seatbelt on macOS) and approval modes.
- Context compaction, goal cards, session branching, and rollout persistence.
- Per-model prompt order and tool-form selection once a model (or API kind) is known.
You are not locked to any provider. Point the harness at any compatible API and keep the same commands.
What the provider owns
The provider owns the wire protocol, auth scheme, model list, rate limits, and the tokens it returns.
Veyyon adapts to that surface through the provider’s api kind:
- Chat-Completions-style endpoints (
api: openai-completions) talk/chat/completions. - Responses-style and native provider endpoints use their own request shape.
- Model ids come from the provider’s discovery endpoint when discovery runs. There is no hardcoded allowlist for BYOK providers, and discovery fails loud rather than serving a silent empty list.
Everything beyond the built-in catalog is data in models.yml — see
Providers and docs/providers.md.
System prompts and tool schemas
Each turn the harness builds a request that includes:
- Base instructions for the active model or backend (execution order, stop-when-green, format-neutral tool guidance). See Execution-order prompts.
- User and project instructions (
AGENTS.md/SYSTEM.mdlayers and any session steers). - Tool schemas the model is allowed to call on this turn (bash, edit/write, web search, MCP tools,
skills, and so on), filtered by approval mode,
disallowed_tools, and feature flags. - Conversation context for the active thread, possibly compacted.
The model is expected to call tools using the schemas it was given. When arguments are almost right but malformed, hashline returns recovery hints so the model can retry inside the same turn budget.
Freeform vs Function tools
Veyyon advertises the structured edit tool in one of two shapes. The payload (the patch or edit body) is the same; only the transport differs.
| Form | How the model calls it | Typical API kind |
|---|---|---|
| Freeform | A custom / grammar tool. The raw body is the tool payload (for example a full *** Begin Patch envelope). | Responses-style |
| Function | A JSON-schema function tool. Arguments are a JSON object (for example {"input": "<envelope>"}). | Chat Completions |
The form is derived from the provider’s API kind by default; an optional catalog override can pin the
apply_patch tool to function, freeform, or none. The Function form makes a structured edit tool
real for chat-wire endpoints (Ollama, LM Studio, DeepSeek, and similar) that historically saw no
Freeform tool at all. See The hashline edit engine for the default edit wire format.
Harness vs provider: a clear split
┌──────────────────────── harness (veyyon) ─────────────────────┐
│ session / turn loop │
│ prompts, tool schemas, edit, sandbox, approvals, compaction │
└────────────────────────────┬──────────────────────────────────┘
│ HTTPS / local HTTP
▼
┌──────────────────────── provider ─────────────────────────────┐
│ endpoint auth + model discovery + completions/responses │
│ model weights, rate limits, provider-side refusals │
└───────────────────────────────────────────────────────────────┘
If something fails, ask which side owns it:
- Config rejected at load, malformed
models.yml, missing key → harness / your config. - HTTP 401 / 429 / empty model list → provider or key.
- Patch applied but tests red → harness did its job; the change still needs work.
- Sandbox denial → permission model, not the model provider.
Provider data at load time
For BYOK providers, model and provider entries are data in models.yml:
- A YAML or schema error makes the registry skip the custom file loudly; it does not silently drop models.
- Custom providers are merged alongside the built-in catalog. A custom entry with the same id as an
implicit local engine (
ollama,lm-studio,llama.cpp) replaces that engine’s discovery. - Provider availability requires the id not be in
disabledProvidersand the provider be keyless or have resolvable credentials.
Malformed provider data fails at load. Silent fallback to a weaker provider is treated as a bug.
Per-role models
The conversation model (/model or --model) is separate from background roles. Roles are configured
under modelRoles:
modelRoles.task— default for spawned subagents unless an agent definition pins its own model.modelRoles.tiny(orsmol) — lightweight background work (titles, memory, auto-thinking).
Precedence for subagents is explicit: an agent definition’s own model pattern wins, otherwise
modelRoles.task, otherwise the conversation model. There is no silent blend. /status and
veyyon plugin doctor report the effective values. See
Models and providers.
Automation note
For non-interactive runs, pass the prompt and pick an approval mode that matches your trust boundary:
$ veyyon --approval-mode auto-edit "run the unit tests and fix failures"
Use --yolo (auto-approve everything) only in trusted automation, ideally with an outer sandbox.
What stays constant
- The workflow stays the same: read, edit, verify, and stop when the work is done.
- Edit verification, OS isolation, approvals, and context handling are harness behavior.
- You are not locked to any provider; point the harness at any compatible API.
Next
- Configuring providers — Ollama, LM Studio, Anthropic, and custom OpenAI-compatible endpoints.
- Models and providers — choosing and switching models in a session.
- Safety — boundaries around tool use and model output.
- Permission model — approval modes + OS sandbox.
- Signing in — interactive and env-var auth paths.