# Agent Actors An Agent Actor is the trusted caller identity for one invocation. It can drive access, rate limits, state partitioning, and inspection without turning a Channel or chat user object into an authorization decision. The current public fields retain the name `invoker`: configure Actors with `defineAgent({ invoker })`, pass one through `context.invoker`, and read the normalized Actor as `actor` or `invoker` in callbacks. ## Pass a trusted Actor Authenticate at the application boundary, then pass only validated identity facts. ```ts [server/api/support.post.ts] import { runAgent } from 'vite-hub/agent' import support from '../agents/support' import { getRuntimeContext } from '../runtime-context' export default defineEventHandler(async (event) => { const user = await requireAuthenticatedUser(event) const { prompt } = await readBody<{ prompt: string }>(event) return runAgent(support, getRuntimeContext(event), { prompt, context: { invoker: { id: user.id, kind: 'customer', label: user.email, meta: { customer: user.customerId }, }, }, }) }) ``` ViteHub trusts this server-owned value. Never copy unverified request fields into `context.invoker`. ## Actor fields | Field | Required | Purpose | | ------- | -------- | ---------------------------------------------------------------------------------- | | `id` | Yes | Stable identity for access, limits, state, and inspection. Empty ids are rejected. | | `kind` | No | Identity family such as `customer`, `chat`, or `anonymous`. | | `label` | No | Human-readable value for logs and CLI inspection. | | `email` | No | Normalized `{ address, domain }`; invalid values are omitted. | | `meta` | No | Application-owned trusted facts. Validate them before invocation. | ## Configure profiles Profiles provide known Actors for local development, schedules, CLI use, and trusted routes. ```ts [server/agents/support.ts] import { defineAgent, defineAgentInvoker } from 'vite-hub/agent' export default defineAgent({ invoker: defineAgentInvoker({ profiles: [ { id: 'portal-acme', kind: 'customer', label: 'Acme Portal', meta: { customer: 'acme' }, }, { id: 'support-admin', kind: 'support', label: 'Support Admin', meta: { scope: 'all' }, }, ], }), driver: { model: 'openai/gpt-5.1-mini' }, }) ``` Select a profile with `context.invokerProfileId` for direct invocation or top-level `invokerProfileId` for `chat.message`. Unknown ids fail instead of falling back silently. Use `invoker.resolve` to normalize or reject the trusted input before Capabilities run: ```ts [server/agents/support-actor.ts] import { defineAgentInvoker } from 'vite-hub/agent' export const supportActor = defineAgentInvoker({ resolve({ context, defaultInvoker, selectedProfile }) { const customer = typeof defaultInvoker.meta?.customer === 'string' ? defaultInvoker.meta.customer.trim() : undefined context.set('support.customer', { customer }, { overwrite: true }) return selectedProfile ?? defaultInvoker }, }) ``` Resolution happens before Capabilities and the Driver run. ## Use Actors for access Actor metadata can select a Workspace Scope, but authorization must remain deterministic. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { access } from 'vite-hub/agent/capabilities' export default defineAgent({ capabilities: [ access({ workspace: { resolve({ invoker }) { return invoker.meta?.customer === 'acme' ? 'acme' : 'public' }, scopes: { public: { paths: ['public'] }, acme: { paths: ['customers/acme'] }, }, }, }), ], driver: { model: 'openai/gpt-5.1-mini' }, workspace: 'product-docs', }) ``` Do not ask the model to decide its own Actor or access scope. Authenticate first, normalize once, and let Capabilities consume the trusted result. ## Current API names | Task | API | | ----------------------- | ----------------------------------------------------------- | | Configure resolution | `defineAgent({ invoker })`, `defineAgentInvoker()` | | Direct invocation input | `input.context.invoker` | | `chat.message` input | top-level `invoker` | | Read in callbacks | `actor` or `invoker` | | Read from context store | `context.get('actor')` or `context.get('invoker')` | | Public type | `AgentActor`; invoker-named APIs also expose `AgentInvoker` | # Agent Definitions An Agent Definition is the single configuration object for one Agent. It selects an [Agent Driver](https://vitehub.dev/docs/agents/agent-drivers), attaches Capabilities and Workspace context, and defines any Channels, Actor resolution, hooks, or hosted runtime behavior. ViteHub discovers definitions in `server/agents`. Both `server/agents/support.ts` and `server/agents/support/agent.ts` create an Agent named `support`. ## Define an Agent Start with the execution path. This Agent uses ViteHub's built-in Codex Driver: ```ts [server/agents/review.ts] import { defineAgent } from 'vite-hub/agent' export default defineAgent({ description: 'Reviews the current repository change.', driver: 'codex', }) ``` Use a tagged built-in value when the Driver needs options: ```ts [server/agents/review.ts] export default defineAgent({ driver: { kind: 'codex', model: 'gpt-5.5', permissions: 'ask', }, }) ``` For application-supplied execution, use exactly one structural Driver variant: `{ model }` or `{ run }`. ## Add abilities and context Capabilities decide which runtime abilities the selected Driver receives. Workspace context decides which files and Sources those abilities can reach. ```ts [server/agents/support/agent.ts] import { defineAgent } from 'vite-hub/agent' import { workspaceShell } from 'vite-hub/agent/capabilities' import { glob } from 'vite-hub/workspace' export default defineAgent({ driver: { model: 'openai/gpt-5.1-mini', instructions: 'Answer from the docs Workspace. Say when the answer is absent.', }, capabilities: [workspaceShell({ mode: 'read' })], workspace: { sourceRootDir: process.cwd(), sources: { docs: glob({ cwd: '.', include: ['docs/content/**/*.md'] }), }, }, }) ``` Declaring a Workspace does not automatically grant model-backed or custom Drivers file access. Provider Drivers receive the selected Workspace as their working directory; Capabilities still control additional tools and invocation behavior. ## Return structured output Set `driver.output` when downstream code needs validated data instead of free-form text. ```ts [server/agents/triage.ts] import * as v from 'valibot' import { defineAgent } from 'vite-hub/agent' export default defineAgent({ driver: { model: 'openai/gpt-5.1-mini', instructions: 'Classify the request and explain the next action.', output: { schema: v.object({ priority: v.picklist(['low', 'normal', 'urgent']), nextAction: v.string(), }), }, }, }) ``` Inline `runAgent()` execution returns the validated structured result. A schema failure fails the invocation instead of returning unchecked model output. Workflow-backed calls return an `AgentWorkflowRun` after the Workflow starts. Poll `getWorkflowRun(workflowName, run.id)` until its status is `completed`, then read `result` for the validated Agent value. Treat `failed`, `cancelled`, and `unknown` as terminal states instead of waiting indefinitely. When a model returns invalid native structured output, the Agent Driver makes one output-only correction call before failing validation. The correction keeps the invocation's prepared model and provider route, but it does not replay conversation messages or expose tools, so completed tool effects cannot run again. Tool results remain available as bounded evidence for the corrected output. Usage records include both model calls, with per-call attribution, aggregate token totals, and aggregate cost when provider metadata or configured pricing supplies it. ## Choose hosted execution Discovered Agents use the active host's Workflow integration by default. Set `runtime: false` when a hosted Agent must complete inline, or select a named Workflow identity with `runtime: workflow('support')`. With the implicit discovery-default Workflow binding, direct `runAgent()` calls without a discovered host identity remain inline. An explicit `runtime: workflow('support')` binding starts that named Workflow even for a direct call. ## Definition options | Option | Purpose | | -------------------------------- | ----------------------------------------------------------------------------------------------------------------- | | `driver` | Required. Selects one built-in provider, model-backed, or custom-run execution path. | | `capabilities` | Attaches a static list or invocation-time Capability resolver. | | `workspace` | Declares or reuses scoped files, Sources, bindings, and access policy. | | `driver.instructions` | Configures instructions on the selected Driver; see [Instructions](https://vitehub.dev/docs/agents/instructions). | | `driver.output` | Validates structured Agent output. | | `channels` | Declares named Agent Channels and generated routes. | | `messages` | Applies shared delivery, streaming, concurrency, session, and transcript settings to adapter Channels. | | `invoker` | Configures Agent Actor profiles and resolution using the current API name. | | `runtime` | Selects inline or Workflow-backed hosted execution. | | `hooks` | Observes input, completion, failure, Capability lifecycle, or hook execution. | | `runEvents` | Publishes application-owned progress for an invocation with a stable run id. | | `name`, `description`, `version` | Adds explicit discovery and inspection metadata. | | `cli.capabilities` | Enables or disables Capability-contributed CLI commands. | Use the dedicated pages for option details rather than growing the Definition itself: [Drivers](https://vitehub.dev/docs/agents/agent-drivers), [Channels](https://vitehub.dev/docs/agents/channels), [Actors](https://vitehub.dev/docs/agents/actors), and [Invocations](https://vitehub.dev/docs/agents/invocations). # Agent Drivers An Agent Driver decides how one invocation runs. Choose the execution method that matches the work. | Choose | Use it when | | --------------- | ------------------------------------------------------------------------------- | | Model-backed | ViteHub runs a model and its Capability-contributed tool loop. | | Provider-backed | Codex or Claude Code runs the coding-agent loop, tools, approvals, and session. | | Custom run | Application code runs the entire operation. | Built-in `"codex"` and `"claude-code"` values are provider-backed. Application-supplied Drivers use exactly one of `{ model }` or `{ run }`. ## Use a model-backed Driver Model-backed execution fits support answers, classification, extraction, structured output, and bounded tool use. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' export default defineAgent({ driver: { model: 'openai/gpt-5.1-mini', instructions: 'Answer support requests from inspected evidence.', execution: { callSettings: { temperature: 0.2 }, stepLimit: 8, }, }, }) ``` Model strings run through AI Gateway. ViteHub discovers `AI_GATEWAY_API_KEY` from the process or Cloudflare Server Env. Supply an explicit descriptor when the Definition owns the credential: ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' const apiKey = process.env.SUPPORT_AI_GATEWAY_API_KEY if (!apiKey) throw new Error('SUPPORT_AI_GATEWAY_API_KEY is required') export default defineAgent({ driver: { model: { id: 'zai/glm-5v-turbo', apiKey }, }, }) ``` The `model` value may also be a compatible AI SDK model or an invocation-time callback. Keep authorization in Access or Capability policy; use model callbacks and instrumentation for routing and call settings. ### Model options | Option | Purpose | | ----------------------------- | ------------------------------------------------------------------------------------------- | | `model` | Required model id, `{ id, apiKey }`, AI SDK model, or callback. | | `instructions` | String, string array, or callback parts. Defaults to colocated instructions when available. | | `maxRetries` | Common model retry count. Do not also set `execution.callSettings.maxRetries`. | | `execution.callSettings` | Provider and AI SDK call settings. | | `execution.stepLimit` | Maximum model tool-loop steps; defaults to `20`. | | `execution.instrumentation` | Invocation-scoped model wrapping or call-setting overrides. | | `execution.workspaceFallback` | Controls synthesis from Workspace evidence when a run produced tool results but no text. | ## Use a provider-backed Driver The built-in Drivers reuse T3 Code's normalized Codex and Claude Code runtime while ViteHub owns Agent Definitions, Capabilities, Workspaces, Invocations, and public lifecycle events. ```ts [server/agents/review/agent.ts] import { defineAgent } from 'vite-hub/agent' export default defineAgent({ driver: { kind: 'codex', instructions: 'Review the exact pull request head before changing code.', model: 'gpt-5.5', permissions: 'ask', }, workspace: { mode: 'write' }, }) ``` Provider Drivers require a local Node.js host with the matching CLI and credentials available to the process. Provider Workspaces also require a POSIX host. Each invocation receives a temporary working directory, optional Workspace files, `AGENTS.md` or `CLAUDE.md`, and Capability tools through a private loopback MCP server. Successful write-mode runs commit through Workspace rules; failed and cancelled runs do not write back. Provider runtime cursors resume a thread while the Agent Definition process remains active. Chat-backed cursors are also partitioned by origin, invoker, and resolved Chat Session, so a new session cannot inherit provider context from an earlier one. Cursors are process-local and do not survive restarts or resume on another worker; use the Agent Invocation message history as the durable conversation boundary. Threads resume with the provider's opaque cursor. ViteHub normalizes assistant text, reasoning, native and Capability tool activity, approvals, provider questions, usage, warnings, errors, and terminal state into Agent Invocation events. | Option | Purpose | | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `kind` | Required tagged provider name: `"codex"` or `"claude-code"`. | | `model` | Optional provider model id. | | `env` | Explicit environment values passed to the local provider process. ViteHub otherwise inherits only standard host paths, locale, and user-directory variables, not arbitrary application secrets. | | `execution.attachments.maxBytes` | Optional positive per-invocation image attachment budget; defaults to 25 MiB. Inline and application-resolved lazy images share the budget. | | `instructions` | Invocation-scoped instructions composed with colocated instructions. | | `permissions` | `"ask"`, `"allow-edits"`, or `"allow-all"`; defaults to `"allow-all"`. | | `output` | Optional structured Agent output contract. | | `capacity` | Optional process-local concurrency and queue limits. | Provider Drivers do not accept Agent Boxes, model-specific Provider Tool contributions, Cloudflare Agents, or Deno. Provider Workspaces are also unsupported on Windows. These boundaries fail explicitly. Workspace-scoped Skills and ordinary Capability tools are supported. ## Use a custom run Driver Use `driver.run` when application code owns the result and no model loop is needed. ```ts [server/agents/router.ts] import { defineAgent } from 'vite-hub/agent' export default defineAgent({ driver: { run({ input, invoker }) { return { text: `Accepted ${invoker.id}: ${String(input.prompt ?? '')}` } }, }, }) ``` The callback receives prepared input, messages, tools, Workspace access, invocation context, and the resolved Actor as both `actor` and `invoker`. A custom run callback may call a model internally, but ViteHub treats that execution and usage as application-owned behavior. Read [Instructions](https://vitehub.dev/docs/agents/instructions) for model-facing behavior and [Workspace context](https://vitehub.dev/docs/agents/workspace-context) for files and writeback. # Boxes Boxes prepare isolated or trusted process environments with explicit Home, checkout, environment, requirements, and state. Use `@vite-hub/box` directly when application code owns that process lifecycle. Boxes do not attach to Agent Definitions. The built-in Codex and Claude Code Agent Drivers run through the local provider runtime with a temporary Workspace, so `defineAgent({ box })` is intentionally unsupported. Use [`sandbox()`](https://vitehub.dev/docs/capabilities/sandbox) to give a model-backed Agent an allowlisted executable tool, or a custom `driver.run` when application code must compose Box execution with an Agent Invocation. # Channels A Channel describes where an Agent Invocation came from and how replies return there. It carries transport, event, thread, message, and delivery facts. It does not prove who the caller is. Use [Agent Actors](https://vitehub.dev/docs/agents/actors) for trusted identity and [Input Commands](https://vitehub.dev/docs/capabilities/input-commands) for explicit command handling. ## Add a Channel Import Channel helpers from `@vite-hub/agent/channels`. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { github, webChat } from 'vite-hub/agent/channels' export default defineAgent({ channels: { portal: webChat(), github: github({ pullRequest: true }), }, driver: { model: 'openai/gpt-5.1-mini' }, }) ``` Built-in helpers include `discord()`, `github()`, `http()`, `slack()`, `teams()`, `telegram()`, and `webChat()`. Use `defineChannel()` for an application-owned Channel Kind. `webChat()` enables a generated AI SDK chat route by default. `http()` is a generic HTTP Channel and keeps its route disabled unless you pass `http({ route: true })`. ## Connect a web chat `webChat()` exposes the Agent through `/api/_vitehub/agents/[agent]/chat`. Set `route: false` to keep that Agent unreachable through the shared dispatcher. ```ts [vite.config.ts] import { defineConfig } from 'vite' import { vitehub } from 'vite-hub' export default defineConfig({ plugins: [ vitehub({ preset: 'node', agent: true }), ], }) ``` Use the Vue client from the application: ```vue [app/components/SupportChat.vue] ``` Add `route.admission.authenticate` when the generated route needs authentication. ViteHub reads the raw body once, verifies the shared UI-message contract, and copies only fields named in `route.input.trust` after authentication. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { webChat } from 'vite-hub/agent/channels' export default defineAgent({ channels: { portal: webChat({ route: { admission: { authenticate({ rawBody, request }) { verifyPortalSignature(rawBody, request.headers.get('x-portal-signature')) return { customer: request.headers.get('x-customer') } }, }, input: { trust: ['meta', 'user', 'session'] }, }, }), }, driver: { run: () => 'ok' }, }) ``` Use an application-owned route and [`streamAgentTrigger()`](https://vitehub.dev/docs/agents/triggers#consume-a-capability-trigger) when the shared dispatcher is not the right authentication or request boundary. ### Resume a web chat in one process Set `resume: true` in `useChat()` and opt the generated route into process-scoped replay when a browser should follow an active response after reconnecting. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { webChat } from 'vite-hub/agent/channels' export default defineAgent({ channels: { portal: webChat({ route: { admission: { authenticate: ({ request }) => requireSession(request), }, resumable: { owner: ({ auth }) => auth.user.id, scope: 'process', ttlMs: 10 * 60 * 1000, }, }, }), }, driver: { model: 'openai/gpt-5.1-mini' }, }) ``` The route de-duplicates one owner's repeated submission, replays buffered UI-message stream bytes, follows the live response, and retains a completed response for `ttlMs`. `scope: 'process'` is literal: active streams do not survive process replacement and cannot be discovered by another instance. Use this only where deployment keeps a chat on one process, or put durable execution, stream storage, and coordination behind an application-owned route. ## Connect an adapter platform Adapter-backed Channels deliver the completed response by default. Set Agent-level `messages.stream: true` to publish draft and edit updates everywhere, or set `messages.stream` on one Channel. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { discord } from 'vite-hub/agent/channels' export default defineAgent({ channels: { discord: discord({ adapter: { botToken: process.env.DISCORD_BOT_TOKEN, publicKey: process.env.DISCORD_PUBLIC_KEY, }, messages: { lockScope: 'thread' }, }), }, driver: { run: () => 'Hello from ViteHub.' }, }) ``` Install the matching `@chat-adapter/*` package when a built-in Channel uses provider adapter options. Keep provider credentials in Server Env. For Telegram, ViteHub can own the verified webhook route and synchronize it after deployment: ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' export default defineAgent({ channels: { telegram: { allowedUserIds: ['123'] }, }, driver: { run: () => 'Hello from ViteHub.' }, }) ``` ```bash [Terminal] pnpm vitehub channels sync \ --stage staging \ --url https://staging.example.com \ --agent support \ --channel telegram \ --json ``` The command is a dry run by default. Apply a reviewed plan with `--apply` and the exact `--confirm-origin`; see [CLI channel synchronization](https://vitehub.dev/docs/development/cli#synchronize-channel-webhooks) for deletion and secret safeguards. ## Control admission and delivery Set `messages.filter` to ignore unsupported adapter messages before an invocation starts: ```ts teams({ adapter, messages: { filter: ({ deliveryKind }) => deliveryKind === 'direct' || deliveryKind === 'mention', }, }) ``` `deliveryKind` is `direct`, `mention`, or `subscribed`. Returning `false` posts no fallback error because the Agent never started. Set `messages.commentary: 'message'` only when the Driver emits explicit commentary phases for public progress. Commentary is hidden by default; ViteHub never publishes reasoning as progress. Use `messages.delivery: 'manual'` when finish hooks own replies. A generated Workflow may carry manual delivery across a durable boundary when the Channel and host support it. An explicit `messages.timeout` bounds inline execution and the durable handoff's typing indicator, but it does not cap the durable Agent Workflow. `steer` queues overlapping messages and preserves that Workflow handoff. Other overlap policies such as `serial`, `drop`, `queue`, and `reject` remain inline and cannot be combined with required durable delivery. ## Inspect delivery custody Every built-in and custom Agent Channel records a delivery timeline before the Agent starts. The record keeps the provider event id separate from ViteHub's delivery id, then appends admission, invocation, retry, outbound, completion, and failure events. Discord Gateway and Telegram polling listeners also emit structured lifecycle events, so a listener gap can be distinguished from an event that reached ViteHub. The evidence boundary stays explicit: no ViteHub record can prove a provider event existed when it never reached the process. Provider audit logs and Gateway session history remain the source for that side of an incident. The journal uses the Channel's existing State Adapter and retains the timelines referenced by the 10,000 most recent admissions. Inspection de-duplicates concurrent or retried admissions of the same timeline. Each delivery and its newest 256 events expire 30 days after their last update. Records contain identifiers, timestamps, attempts, provider reply ids, and bounded error messages; ViteHub does not copy message text, attachment data, webhook bodies, or connector options into the journal. Production durability therefore follows the configured Agent state provider, while the default in-memory development state remains process-local. Invocation hooks and Drivers receive the active record as `context.channelDelivery`. Trace Events repeat `channel.delivery.id`, `channel.delivery.provider`, and `channel.delivery.source.id`, while JSON logs use the `vitehub.channel.delivery` and `vitehub.channel.listener` scopes. The webhook route handler exposes `handler.deliveries(request, webhookId, options)` so host integrations inspect records through the same scoped State Adapter used by the Channel. ## Scope abilities to one Channel Channel Capabilities apply only when that Channel is active. Agent-level Capabilities remain available to every invocation. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { openapi } from 'vite-hub/agent/capabilities' import { teams, webChat } from 'vite-hub/agent/channels' const portalApi = openapi({ cli: { name: 'portal-api' }, operations: ['purchaseOrders'], spec: 'https://portal.example.com/_openapi.json', }) export default defineAgent({ channels: { portal: webChat({ capabilities: [portalApi] }), teams: teams(), }, driver: { run: () => 'ok' }, }) ``` Channel-scoped Capabilities select abilities, not identity. Authenticate and resolve the Actor at the route, trigger, or `access()` boundary. ## Handle attachments Adapter Channels preserve incoming images, audio, and files as typed message parts. Normalization can fetch a URL-only text attachment on the server to produce text bytes. It does not call lazy provider callbacks, write local files, or persist blobs. Model-backed Drivers can consume inline data, adapter-owned `fetchData`, and HTTPS references within one invocation-wide byte budget. The default is 25 MiB; set `driver.execution.attachments.maxBytes` to lower it. Image, audio, and file HTTPS references are forwarded, but URL-only text attachments use the runtime's server-side `fetch()` without built-in scheme or host restrictions. Treat adapter-supplied text URLs as an SSRF boundary: reject untrusted URLs or validate them against an application-owned allowlist or fetch proxy before they reach normalization. Channel history export archives inline data, size-declared adapter-owned `fetchData`, and Blob data within a 25 MiB total attachment budget and a 35 MiB total response budget. Lazy adapter reads need a trustworthy non-negative `size` within the remaining budget. URL-only attachments remain unavailable references because the Agent server cannot infer an application-owned host trust policy safely. Persist their bytes through the adapter when they must be recoverable. The export stops waiting for each provider history read or adapter-owned read after 30 seconds or when its request is aborted. The Chat SDK history and `fetchData` contracts have no cancellation channel, so their underlying private I/O remains adapter-owned and may settle after the export stops waiting. Attachments that exceed the remaining budget, contain malformed retained data, fail rehydration, omit the size required for a lazy read, or otherwise cannot be read remain in `history.json` as unavailable references. The export fails instead of building an archive above its total response limit. Provider-backed Drivers materialize inline data and application-owned `fetchData` results. URL-only attachments require the application to validate and resolve the URL through `fetchData` before crossing the provider boundary; the Driver does not fetch arbitrary URLs from the ViteHub host. ## Separate responsibilities | Concern | Owner | | ----------------------------------------------------------- | ------------------------- | | Origin, event, thread, message, custody, and reply delivery | Channel | | Trusted caller identity | Agent Actor | | User-authored command parsing | Input Commands Capability | | Prior conversational messages | Chat History and sessions | | Product event to Agent input | Trigger | # Chat History and sessions Chat History is the ordered set of prior messages eligible for one chat invocation. A Chat Session selects the host-visible conversation boundary. Neither is durable Agent Memory. | Need | Use | | --------------------------------------------------------- | ----------------------------------------------------------------- | | Continue the visible thread | Thread-backed Chat History | | Continue a conversation across changing transport threads | A Chat Session | | Preserve knowledge or preferences across conversations | [Memory Capability](https://vitehub.dev/docs/capabilities/memory) | ## Enable thread history Configure history on the Chat Capability: ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { chat } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model: 'openai/gpt-5.1-mini', instructions: 'Answer support chat messages.', }, capabilities: [ chat({ concurrency: 'queue', lockScope: 'thread', triggerHistory: { maxMessages: 20, source: 'thread', }, }), ], }) ``` The window limits messages supplied to the next invocation; it does not delete preserved history. For application-owned routes that call `runAgentTrigger()` or `streamAgentTrigger()`, supply the ordered messages for the current thread, including the new message. `triggerHistory` bounds that caller-supplied array; it does not load history from `threadId` or a session id. Adapter-backed Channels can perform their own history backfill. Thread scope is the normal choice for Discord threads, Slack threads, Teams conversations, GitHub comment threads, and application-owned support chats. ## Add a session Use a session when the product has a stable conversation id that is independent of the current provider thread. ```ts [server/agents/support.ts] import { chat } from 'vite-hub/agent/capabilities' export const supportChat = chat({ sessions: { idleTimeoutMs: 30 * 60 * 1000, metadataKey: 'sessionId', strategy: 'hybrid', }, triggerHistory: { maxMessages: 20, source: 'thread', }, }) ``` Use `strategy: 'manual'` when a trusted host passes explicit session IDs, `idle-timeout` when inactivity starts a conversation, or `hybrid` for both. The `chat.message` input selects a manual session with `session: { action: 'switch', id }`. The authenticated route or Channel supplies that ID. Do not accept an arbitrary session ID from an untrusted request, because that can expose another conversation's history. ## Partition transcripts Keep transcript keys aligned with the product boundary. Use thread keys when each platform thread is independent; include Channel or tenant identity when ids can collide across providers. History selection and persistence are separate decisions. The Chat Capability can select a bounded window, but the configured store owns durability, ordering, retention, and deletion. Cloudflare output defaults Agent State to its generated Durable Object binding, including Channel handlers invoked from generated Workflows. An explicit state provider still wins; a state configuration with only `url` keeps automatic libSQL selection instead of being replaced by the Cloudflare default. Generated non-Cloudflare production output requires a durable `VITEHUB_AGENT_STATE_URL` or explicit Agent State provider URL before stateful traffic. Cloudflare, Vercel, and Netlify production output reject `file:` URLs because their compute filesystems are ephemeral. ## Inspect the result Run two messages through the same thread or session, then inspect the second invocation in the [CLI](https://vitehub.dev/docs/development/cli). The prepared input contains the bounded prior messages plus the current message. A different thread or session starts without that history. # Child invocations Use `startAgentInvocation()` when trusted host or parent code must control child Agent work after starting it. Use the [Subagents Capability](https://vitehub.dev/docs/capabilities/subagents) when the active model chooses and awaits delegated work itself. ## Start and inspect a child ```ts import { startAgentInvocation } from 'vite-hub/agent' import researcher from '../agents/researcher' const child = await startAgentInvocation(researcher, runtimeContext, { prompt: 'Compare the two deployment options.', }) const current = await child.inspect() if (current.outcome === 'available') { console.log(current.invocation.id, current.invocation.status) } ``` Every start gets a fresh stable id. `inspect()` returns an available snapshot or an explicit unavailable outcome. Available lifecycle states are `pending`, `running`, `completed`, `failed`, and `cancelled`. Inline and serverless runtimes may become unavailable after their process ends. Workflow-backed children delegate inspection to their Workflow Run while the returned controller remains available. ViteHub does not add a separate invocation registry or public lookup by id. ## Cancel active work ```ts const cancellation = await child.cancel('The parent no longer needs this work.') if (cancellation.outcome === 'accepted') { const latest = await child.inspect() } ``` `accepted` means the runtime accepted the request; inspect again for the observed terminal state. A provider may return `unsupported`, and terminal invocations return `invalid-state`. ## Respond to provider requests Check the controller's current support before sending input, then handle the operation result because support can change with lifecycle state. ```ts if (child.support.respond) { const result = await child.sendInput( { messages: [responseMessage] }, { mode: 'respond' }, ) } ``` Inline provider runtimes accept approval decisions and `data-agent-input` answers while the matching provider request is pending. Text steering, follow-up turns, and Workflow-backed input remain unsupported until their runtime adapters provide equivalent ordering and lifecycle semantics. The `subagents()` Capability uses the same start seam but returns a serializable tool result and waits for the child. The model cannot choose or reuse the trusted child id. # Evals Agent Evals run the real Agent Definition against repeatable inputs. They preserve its Driver, Capabilities, and Workspace while running inline, so a passing eval covers more than a standalone model prompt test. Verify Workflow scheduling, durability, and provider lifecycle separately on the configured host. ## Add one behavior check Install the explicit runner dependencies: ```bash [Terminal] pnpm add -D @vite-hub/agent evalite vitest ``` Create the eval beside the Agent it protects: ```ts [server/agents/support.eval.ts] import { defineEval } from '@vite-hub/agent/eval' import support from './support' export default defineEval({ agent: support, async test(t) { await t.send('How do I configure billing retries?') t.completed() t.textContains('billing') }, }) ``` Run it from the workspace: ```bash [Terminal] pnpm vitehub agent eval server/agents/support.eval.ts ``` A completed invocation containing `billing` passes and exits successfully. A failed invocation or missing text assertion fails the eval and exits non-zero. Sibling `support.eval.ts` files can infer `support.ts`; a folder-level `eval.ts` can infer `agent.ts`. Keep the explicit `agent` import when it makes the relationship easier to see. ## Test several scenarios Use declarative scenarios when independent inputs share scorers. ```ts [server/agents/support.eval.ts] import { callsTool, defineEval, doesNotCallTool, textContains, } from '@vite-hub/agent/eval' import support from './support' export default defineEval({ agent: support, scenarios: [ { name: 'inspects workspace before answering', input: { prompt: 'Where is the billing retry policy documented?' }, scorers: [ callsTool('shell'), doesNotCallTool('refund'), textContains('billing'), ], }, ], }) ``` Scenarios accept normal Agent Invocation input, including `prompt`, `messages`, `context`, call options, timeout, and abort signal. Split unrelated behavior into separate scenarios so a failure identifies the boundary that changed. Use imperative `test(t)` for a conversation. Repeated `t.send()` calls preserve that test's Chat History, and helpers inspect the latest observation: | Helper | Check | | -------------------------------------------- | ---------------------------------------------------------------- | | `completed()` | The latest invocation completed. | | `textContains(value)` | Response text contains a string or matches a regular expression. | | `calledTool(name)` / `doesNotCallTool(name)` | Normalized tool steps include or exclude a tool. | | `hasCapabilityExtension(id, key?)` | A Capability finish extension exists. | | `expect(scorer)` | A custom scorer passes. | | `observation` / `reply` | Access the latest normalized observation or response text. | ## Compare model variants Variants run the same cases with model or instruction changes: ```ts [server/agents/support.eval.ts] export default defineEval({ agent: support, scenarios, variants: [ { name: 'baseline' }, { name: 'strict', instructions: 'Answer only from inspected evidence.', }, ], }) ``` Instruction-only variants require a model-backed Driver. A `model` variant may replace a model-backed or provider-backed Driver for the eval run. Use a separate Agent Definition when the change affects Capabilities, Workspace context, custom `driver.run` behavior, or host configuration. ## Configure the runner Executable `*.eval.ts`, `*.eval.mts`, `*.eval.tsx`, and folder `eval.*` files enable the generated Evalite configuration. Configure defaults through `hubAgent({ eval })`: ```ts [vite.config.ts] import { hubAgent } from '@vite-hub/agent/vite' import { defineConfig } from 'vite' export default defineConfig({ plugins: [ hubAgent({ eval: { cache: true, maxConcurrency: 2, scoreThreshold: 85, testTimeout: 60_000, }, }), ], }) ``` Useful one-run flags are `--watch`, `--threshold `, `--output `, `--hide-table`, and `--no-cache`. CLI flags override integration defaults. ## Score product behavior Prefer assertions about the behavior that matters: grounded answers, expected tool use, refusal when evidence is missing, Capability finish effects, and regressions in usage or latency. Read normalized usage from `observation.usage` and the finalized trace from `observation.trace`. Keep provider credentials, model selection, permissions, and runtime selection on the Agent Definition. An eval owns scenarios and scores; duplicating runtime setup produces a different system than the application runs. # Agents An Agent is a named server-side program. Its definition records how it runs, which files and tools it can use, and how callers reach it. Start with an offline Agent if you have not built one yet. The tutorial creates the definition, calls it from an H3 route, and shows the response without a model key or hosted service. ::u-page-grid{.not-prose.mt-8.sm:grid-cols-2} :::u-page-card --- description: Define and call an Agent with a complete local example. icon: i-lucide-rocket title: Build your first Agent to: https://vitehub.dev/docs/getting-started/first-agent --- ::: :::u-page-card --- description: Choose a Driver, Capabilities, Workspace, and Channels. icon: i-lucide-file-user title: Define an Agent to: https://vitehub.dev/docs/agents/agent-definitions --- ::: :::u-page-card --- description: Run a model, Codex, Claude Code, or application code. icon: i-lucide-cpu title: Choose a Driver to: https://vitehub.dev/docs/agents/agent-drivers --- ::: :::u-page-card --- description: Give the active Driver selected tools and runtime behavior. icon: i-lucide-blocks title: Add Capabilities to: https://vitehub.dev/docs/capabilities --- ::: :: ## How an Agent fits together | Part | What you choose | | ---------------------------------------------------------------------- | ----------------------------------------------------------------- | | [Agent Definition](https://vitehub.dev/docs/agents/agent-definitions) | One Agent's Driver, Capabilities, Workspace, Channels, and hooks. | | [Agent Driver](https://vitehub.dev/docs/agents/agent-drivers) | A model, a coding provider, or your own `run` function. | | [Agent Invocation](https://vitehub.dev/docs/agents/invocations) | The input for one run and whether the result returns or streams. | | [Capabilities](https://vitehub.dev/docs/capabilities) | The tools and behavior available during an invocation. | | [Workspace context](https://vitehub.dev/docs/agents/workspace-context) | The files, Sources, and bindings available to the Agent. | | [Instructions](https://vitehub.dev/docs/agents/instructions) | Durable guidance for a model or coding provider. | Capabilities grant access deliberately. Adding KV, Blob, a Workspace, or another server feature to the application does not give a model access to it. Attach the matching Capability only when the Agent needs that action. ## Connect an Agent Call an Agent directly from trusted server code, or connect it to a product entry point: - [Channels](https://vitehub.dev/docs/agents/channels) connect web chat, Discord, Telegram, GitHub, and other message transports. - [Triggers](https://vitehub.dev/docs/agents/triggers) turn application events into Agent input. - [Agent Actors](https://vitehub.dev/docs/agents/actors) carry trusted caller identity. - [Chat History and sessions](https://vitehub.dev/docs/agents/chat-history-sessions) select the earlier messages supplied to a chat invocation. ## Verify behavior Use the [CLI development loop](https://vitehub.dev/docs/development/cli) to inspect and run the Agent locally. Add an [Eval](https://vitehub.dev/docs/agents/evals) for behavior that must keep working. Deployment-specific behavior still needs a build and runtime check on the selected host. ## Advanced execution - [Controlled child invocations](https://vitehub.dev/docs/agents/controlled-child-invocations) start, inspect, cancel, or respond to child work from trusted code. - [Boxes](https://vitehub.dev/docs/agents/boxes) prepare a process environment when application code owns that lifecycle. # Instructions Instructions tell a model or coding provider how to behave. Keep tool schemas with Capabilities; use instructions for durable behavior, source policy, trust boundaries, escalation, and uncertainty handling. ## Start with a colocated document Put longer guidance beside the Agent as `instructions.md`. ```md [server/agents/support/instructions.md] # Support Answer from inspected Workspace evidence before using outside knowledge. When the docs do not answer the question, say that directly. ``` ```ts [server/agents/support/agent.ts] import { defineAgent } from 'vite-hub/agent' export default defineAgent({ driver: { model: 'openai/gpt-5.1-mini', }, workspace: { sources: {}, }, }) ``` ViteHub parses instruction Markdown through Comark. A colocated document becomes the default when `driver.instructions` is absent. Provider Drivers receive the rendered document as `AGENTS.md` for Codex or `CLAUDE.md` for Claude Code. Use `driver.instructions` for short or invocation-specific text: ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' export default defineAgent({ driver: { model: 'openai/gpt-5.1-mini', instructions: [ 'You are a support engineer.', 'Answer from inspected evidence. State when evidence is missing.', ], }, }) ``` ## Split reusable guidance Use static `@./path.md` imports when one document becomes difficult to scan. ```md [server/agents/support/instructions.md] # Support @./shared-style.md @./escalation-policy.md ``` Imports are relative, recursive up to four levels, and processed like the parent document. Remote URLs, absolute paths, and globs fail instead of widening instruction reachability. ## Insert trusted invocation values Read explicit `context.*` values with double braces for scalars and triple braces for trusted Markdown. ```md [server/agents/support/instructions.md] Answer for {{ context.customerName }}. {{{ context.supportPolicy }}} ``` The caller or a Capability must set these values before composition. Missing bindings fail instead of rendering empty text; templates cannot read arbitrary request fields, environment variables, or JavaScript expressions. Use conditions for small policy branches: ```md [server/agents/support/instructions.md] ::if{condition="context.audience === 'technical'"} Include implementation details and cite file paths. ::else Prefer customer-facing language and next actions. :: ``` Conditions support `context.*` paths, scalar literals, equality, `&&`, `||`, `!`, and parentheses. ## Insert Workspace bindings Declare values or Markdown files under `workspace.bindings`, then reference only those named bindings. ```ts [server/agents/support/agent.ts] import { defineAgent } from 'vite-hub/agent' export default defineAgent({ driver: { model: 'openai/gpt-5.1-mini', instructions: [ 'Use {{ workspace.tone }} tone.', '@workspace.policy', ], }, workspace: { bindings: { tone: 'short', policy: { path: 'policies/support.md' }, }, }, }) ``` `@workspace.policy` inserts the declared Markdown and composes it again. ViteHub does not scan or auto-load every Markdown file in the Workspace. ## Cover configured primitives Name how to use each configured Source, Capability, or Skill. ViteHub records this coverage for inspection and warns when a configured primitive has no explicit policy. ```md [server/agents/support/instructions.md] ::source{key="docs"} Use the docs Source for published product behavior. Say when it does not answer. :: ::capability{key="workspaceShell"} Inspect the Workspace before answering implementation questions. :: ::skill{path="skills/review-browser-evidence"} Use this Skill only when the task needs browser evidence. :: ``` ViteHub strips the wrapper directives before model execution and keeps their prose. A file that merely exists in the Workspace does not count as instruction coverage. ## Use the right instruction lifetime | Instruction source | Use it for | | ------------------------------ | ----------------------------------------------------------------------------------------------- | | Colocated `instructions.md` | Durable guidance shared by model and provider-backed execution. | | Model `driver.instructions` | Model-facing behavior, including invocation-time callbacks and bindings. | | Provider `driver.instructions` | Invocation-scoped policy written into the provider working directory. | | Custom `driver.run` | Application code reads prepared context directly; ViteHub does not build a model prompt for it. | Read [Agent Drivers](https://vitehub.dev/docs/agents/agent-drivers) for execution-specific behavior and [Workspace context](https://vitehub.dev/docs/agents/workspace-context) for file visibility. # Invocations An Agent Invocation is one request to an Agent. ViteHub prepares its input, Actor, Capabilities, Workspace, and Driver, then returns or streams the result. ## Run an Agent Use `runAgent()` when the caller needs the final result. ```ts [server/api/support.post.ts] import { runAgent } from 'vite-hub/agent' import support from '../agents/support' import { getRuntimeContext } from '../runtime-context' export default defineEventHandler(async (event) => { const { prompt } = await readBody<{ prompt: string }>(event) const user = await requireAuthenticatedUser(event) return runAgent(support, getRuntimeContext(event), { prompt, context: { invoker: { id: user.id, kind: 'customer', label: user.email, }, }, }) }) ``` Authenticate the request before passing trusted identity or access facts. `context.invoker` is the current input field for an [Agent Actor](https://vitehub.dev/docs/agents/actors). The second argument is [Runtime Context](https://vitehub.dev/docs/concepts/runtime-context); the third is invocation input. The application-owned `getRuntimeContext()` helper supplies the host's required `runtime`, `memo`, and `waitUntil` values. Keeping them separate prevents host resources from becoming user-controlled task data. ## Stream an Agent Use `streamAgent()` when a chat UI or internal consumer needs incremental output. ```ts [server/api/support-stream.post.ts] import { streamAgent } from 'vite-hub/agent' import support from '../agents/support' import { getRuntimeContext } from '../runtime-context' export default defineEventHandler(async (event) => { const { prompt } = await readBody<{ prompt: string }>(event) return streamAgent( support, getRuntimeContext(event), { prompt }, { output: 'ui-message-stream' }, ) }) ``` Use `output: 'ui-message-stream'` for an AI SDK-compatible chat response. Use `output: 'events'` when server code needs ViteHub stream events. The stream becomes terminal when the caller consumes it, cancels it, or receives an error. A caller that abandons the stream also abandons completion observation. ## Invoke a Trigger Use `runAgentTrigger()` or `streamAgentTrigger()` when a Capability owns the event shape. This example invokes the Chat Capability's `chat.message` trigger: ```ts [server/api/support-chat.post.ts] import { streamAgentTrigger } from 'vite-hub/agent' import support from '../agents/support' import { getRuntimeContext } from '../runtime-context' export default defineEventHandler(async (event) => { const { text } = await readBody<{ text: string }>(event) const runId = crypto.randomUUID() return streamAgentTrigger( support, getRuntimeContext(event), 'chat.message', { messages: [{ id: runId, role: 'user', parts: [{ type: 'text', text }], }], run: { channelId: 'support-web', messageId: runId, origin: 'portal', runId, }, }, { output: 'ui-message-stream' }, ) }) ``` The consumer supplies the product event. The Capability prepares the Agent input and policy before the Driver starts. Read [Triggers](https://vitehub.dev/docs/agents/triggers) for when to use this path instead of direct invocation. ## Validate input Use an `agent:input` hook for trusted invocation requirements that must be present before the Driver runs. ```ts [server/agents/review.ts] import { defineAgent } from 'vite-hub/agent' export default defineAgent({ driver: { run: () => 'ok' }, hooks: { 'agent:input'({ input }) { if (!input.context?.pullRequest) { throw new Error('Missing context.pullRequest') } }, }, }) ``` Validate untrusted request data at the route boundary. The hook protects the Agent contract when multiple trusted callers invoke the same Definition. ## Observe the outcome Finish hooks receive normalized duration, result kind, and usage. Error hooks receive failed invocations. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' export default defineAgent({ driver: { model: 'openai/gpt-5.1-mini' }, hooks: { 'agent:finish'(event) { const { durationMs, resultKind, usage } = event.invocation event.runtime.waitUntil(recordInvocation({ durationMs, resultKind, usage })) }, 'agent:error'(event) { event.runtime.waitUntil(recordFailure(event.publicError)) }, }, }) ``` Error hooks receive the raw `event.error` for protected server diagnostics and a sanitized `event.publicError` for logs, HTTP responses, or Channel replies. See [Agent public errors](https://vitehub.dev/docs/reference/errors-diagnostics#agent-public-errors) for the stable codes and redaction rules. Every invocation also has an in-memory metadata trace through `runtime.trace` and `runtime.traceLog`. The default log is process-local and is not persisted across a Workflow boundary. Attach the `otlp()` Capability to send completed invocation traces to any OTLP/HTTP JSON receiver: ```ts [server/agents/support.ts] import { defineAgent } from '@vite-hub/agent' import { otlp } from '@vite-hub/agent/capabilities' export default defineAgent({ name: 'support', capabilities: [ otlp({ endpoint: 'https://telemetry.example/otlp', headers: { authorization: `Bearer ${process.env.OTLP_TOKEN!}` }, live: true, resource: { 'service.namespace': 'quiver' }, }), ], driver: { model: 'openai/gpt-5.1-mini' }, }) ``` Pass the OTLP base endpoint; ViteHub appends `/v1/logs` and `/v1/traces`. With `live: true`, new Trace Events are batched as correlated OTLP LogRecords while the invocation runs, then ViteHub exports one completed trace. Without `live`, it exports only the completed trace and retains Trace Events as span events. Invocation content is metadata-only by default. Use `content.inputs`, `content.outputs`, and `content.instructions` to opt a trusted receiver into each content class independently. Export runs through `runtime.waitUntil()`, so delivery failures do not replace the Agent result. See [`otlp()`](https://vitehub.dev/docs/capabilities/otlp) for batching, deduplication, privacy, and Capability-contribution details. To persist a queryable invocation journal, attach Agent Invocations to the Agent Definition. Storage durability and recovery guarantees still depend on the selected store and host lifecycle. The SQLite adapter accepts a local SQLite or remote libSQL URL: ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { createLibsqlAgentInvocationStore } from 'vite-hub/agent/invocations/sqlite' import { defineAgentInvocations } from 'vite-hub/agent/server' const invocations = defineAgentInvocations({ store: createLibsqlAgentInvocationStore({ url: 'file:./.data/invocations.db' }), }) export default defineAgent({ driver: { model: 'openai/gpt-5.1-mini' }, invocations, }) ``` Invocation journals are metadata-only by default. Set `content: 'content'` only when the application must persist prompts, messages, reasoning, tool inputs and outputs, and result text. That opt-in stores sensitive model content in the configured durable store; apply the same access controls, retention policy, and encryption requirements as the source data. The journal records pending, running, completed, failed, and cancelled states plus bounded invocation metadata and trace observations. Failed records retain bounded `cause` and `AggregateError.errors` trees, common status and code fields, and public ViteHub error details. Use `invocations.list()` for cursor-based summaries, `invocations.get(id)` for a stored record ID, and `invocations.getByRunId(runId, agentName?)` when starting from the source run ID. Always pass the Agent Definition name for a named Definition; the name is part of its durable invocation identity. Journal failures never change the Agent Invocation result. When an application exposes the standard invocation journal route, inspect it without a dashboard: ```sh vitehub agent invocations list --status running vitehub agent invocations show INVOCATION_ID vitehub agent invocations tail INVOCATION_ID ``` The CLI defaults to `http://localhost:5173/api/invocations`. Use `--url` or `VITEHUB_AGENT_INVOCATIONS_URL` for another local endpoint, and `--json` for automation-safe output. Cloudflare and OpenWorkflow create the journal after durable recovery dispatch and reconcile failures after the generated Agent module loads but before the Agent handler starts. If that module cannot be evaluated, use Workflow inspection because the Agent-owned invocation store is unavailable. Vercel Agent Definitions currently run through the inline Workflow adapter because arbitrary Agent handlers cannot be embedded in Vercel's deterministic native Workflow bundle. An accepted run starts its journal in that Agent worker, and ViteHub keeps bounded journal recovery work inside the active execution. Vercel does not expose a lifecycle hook that can guarantee arbitrary Agent recovery after that execution settles, so treat its journal as best-effort and use Workflow inspection as the authority for accepted runs. A synchronous Vercel start rejection is still recorded as a failed Agent Invocation. The run inspection metadata reports `mode: "inline"` for this path. ## Inspect invocations in the console Enable the [ViteHub Console](https://vitehub.dev/docs/development/console) to browse retained sessions and inspect invocation events at `/_vitehub`. The Console is opt-in. Its page, API, plugin, and assets do not exist when `console` is omitted or set to `false`. The Console guide covers Vite and Nuxt setup, fallback storage, production limits, usage records, and route authorization. An explicit `defineAgent({ invocations })` store remains authoritative when the Console is enabled. ## Control child work Use [`startAgentInvocation()`](https://vitehub.dev/docs/agents/controlled-child-invocations) when trusted parent code must inspect or cancel a child after starting it. Use the [Subagents Capability](https://vitehub.dev/docs/capabilities/subagents) when the active model delegates work itself. # Triggers A Trigger turns a product event into Agent Invocation input. Use it when a Capability owns the event's shape or policy. The Agent Driver still owns execution. ## Call an Agent directly An application route can call `runAgent()` when no Capability needs to prepare the event. ```ts [server/api/support.post.ts] import { runAgent } from 'vite-hub/agent' import support from '../agents/support' import { getRuntimeContext } from '../runtime-context' export default defineEventHandler(async (event) => { const { prompt } = await readBody<{ prompt: string }>(event) return runAgent(support, getRuntimeContext(event), { prompt }) }) ``` This is a direct consumer, not a registered Trigger. Prefer it for ordinary authenticated server routes and scheduled application code. ## Use a Capability Trigger Use a Trigger when a Capability owns event preparation. The Chat Capability registers `chat.message` and can apply history, session, concurrency, and delivery behavior before the Driver runs. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { chat } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model: 'openai/gpt-5.1-mini', instructions: 'Answer support messages.', }, capabilities: [ chat({ triggerHistory: { maxMessages: 20, source: 'thread' } }), ], }) ``` ### Consume a Capability Trigger Call the trigger from a server-owned route: ```ts [server/api/support-chat.post.ts] import { streamAgentTrigger } from 'vite-hub/agent' import support from '../agents/support' import { loadAuthorizedSupportThreadMessages } from '../support-history' import { getRuntimeContext } from '../runtime-context' export default defineEventHandler(async (event) => { const { text, threadId } = await readBody<{ text: string threadId?: string }>(event) const user = await requireAuthenticatedUser(event) const runId = crypto.randomUUID() const messages = await loadAuthorizedSupportThreadMessages({ actorId: user.id, threadId, }) messages.push({ id: runId, role: 'user', parts: [{ type: 'text', text }], }) return streamAgentTrigger( support, getRuntimeContext(event), 'chat.message', { messages, run: { channelId: 'portal', messageId: runId, origin: 'portal', runId, threadId, }, }, { output: 'ui-message-stream' }, ) }) ``` `run` contains origin and trace metadata; it is not chat context. Authenticate before passing Actor identity, session selection, or trusted metadata into the Trigger input. Direct Trigger consumers must authenticate first, reject threads the caller does not own, then load and supply the current thread's ordered messages, including the new message. `triggerHistory` limits that input; it does not backfill messages from `threadId` or a session id. ## Add an application-owned Trigger Use `defineChannel()` when an application-owned Channel Kind prepares its own event. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { defineChannel } from 'vite-hub/agent/channels' const ticketing = defineChannel('ticketing', { messages: false, triggers: { 'ticket.opened': { invoke(context, event: { ticketId: string, summary: string }) { return { input: { prompt: `Triage ticket ${event.ticketId}: ${event.summary}`, }, run: { channelId: context.trigger.channelId, origin: 'ticketing', runId: event.ticketId, }, } }, }, }, }) export default defineAgent({ channels: { ticketing }, driver: { model: 'openai/gpt-5.1-mini' }, }) ``` The Trigger translates the event and attaches trusted context. Keep model selection, tools, and execution behavior in the Agent Definition. ## Choose how to call the Agent | Situation | Use | | ------------------------------------------------------- | ----------------------------------------------------------------------- | | A server route already owns validation and input | `runAgent()` or `streamAgent()` | | A Capability owns history, policy, or event preparation | `runAgentTrigger()` or `streamAgentTrigger()` | | A messaging provider delivers an event | A [Channel](https://vitehub.dev/docs/agents/channels) and its Trigger | | A model delegates to another Agent | [Subagents Capability](https://vitehub.dev/docs/capabilities/subagents) | Webhook adapters may retain ownership until delivery finishes. Configure Channel timeout, concurrency, and durable delivery there rather than adding webhook policy to the Driver. # Workspace context Workspace context gives an Agent a named file tree and optional Sources. The Workspace decides what exists; Capabilities and the selected Driver decide how the Agent can access it. Use a Workspace for project files, documentation, generated state, Source-backed paths, and controlled writeback. Do not use it as hidden prompt storage; model-facing policy belongs in [Instructions](https://vitehub.dev/docs/agents/instructions). ## Add a read-only Workspace Enable both integrations in the ViteHub preset. ```ts [vite.config.ts] import { defineConfig } from 'vite' import { vitehub } from 'vite-hub' export default defineConfig({ plugins: [ vitehub({ preset: 'node', agent: true, workspace: true }), ], }) ``` Declare a Source and grant a model-backed Driver read-only shell access: ```ts [server/agents/docs/agent.ts] import { defineAgent } from 'vite-hub/agent' import { workspaceShell } from 'vite-hub/agent/capabilities' import { glob } from 'vite-hub/workspace' export default defineAgent({ driver: { model: 'openai/gpt-5.1-mini', instructions: [ 'Answer from the docs Source.', 'Use Workspace inspection before answering. Say when evidence is missing.', ], }, capabilities: [workspaceShell({ mode: 'read' })], workspace: { sourceRootDir: process.cwd(), sources: { docs: glob({ cwd: '.', include: ['docs/content/**/*.md'] }), }, }, }) ``` The Source makes files available under the Workspace. `workspaceShell({ mode: 'read' })` exposes read operations to the model. Without that Capability, declaring a Source alone does not grant model-facing file access. ## Reuse a Workspace Use `defineWorkspace()` when several Agents share the same file tree or Source configuration. ```ts [server/workspaces/product-docs.ts] import { defineWorkspace, glob } from 'vite-hub/workspace' export default defineWorkspace({ sourceRootDir: process.cwd(), sources: { docs: glob({ cwd: '.', include: ['docs/content/**/*.md'] }), }, }) ``` ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { workspaceShell } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model: 'openai/gpt-5.1-mini' }, capabilities: [workspaceShell({ mode: 'read' })], workspace: 'product-docs', }) ``` ## Scope access by Actor Use `access()` when trusted caller identity narrows the files visible to one invocation. Place it before `workspaceShell()` so the shell receives the scoped Workspace. ```ts [server/agents/editor.ts] import { defineAgent } from 'vite-hub/agent' import { access, workspaceShell } from 'vite-hub/agent/capabilities' export default defineAgent({ capabilities: [ access({ workspace: { defaultScope: 'support', scopes: { support: { paths: ['support'] }, }, }, }), workspaceShell({ mode: 'read' }), ], driver: { model: 'openai/gpt-5.1-mini' }, workspace: 'product-docs', }) ``` Authenticate the request and pass an [Agent Actor](https://vitehub.dev/docs/agents/actors) before deriving Actor-specific access. Workspace policy enforces authorization, so base it only on trusted identity and application-owned facts. Actor-scoped Workspace access from `access()` is read-only for model-backed and custom Drivers; provider Drivers receive a writable session limited to the selected paths. Without Actor-scoped Access, write authority depends on the Workspace mode, its rules, and the tools exposed to the Driver. ## Use Workspace context with a provider Provider Drivers receive the rendered instruction document and selected Workspace files in a temporary local working directory. Successful write-mode invocations commit through Workspace rules. ```ts [server/agents/review/agent.ts] import { defineAgent } from 'vite-hub/agent' export default defineAgent({ driver: { kind: 'codex', model: 'gpt-5.5' }, workspace: { mode: 'write' }, }) ``` ## Keep context explicit | Need | Use | | -------------------------- | ------------------------------------- | | Files or generated state | Workspace files and Sources | | Model-facing rules | Colocated or Driver Instructions | | Read or write tools | Capabilities such as `workspaceShell` | | Caller-specific file scope | Access plus a trusted Agent Actor | | Provider working directory | A write-mode Workspace | Inspect the resolved Workspace, Sources, and access policy through the [CLI](https://vitehub.dev/docs/development/cli) before relying on them in production. # ViteHub coding-agent skill The public ViteHub skill helps coding agents build complete applications from current documentation and the application's installed package contract. It keeps one compact proof loop in `SKILL.md`, then loads only the project-shape, feature, authority, host, or recovery references that match the task. ## Install the skill Run the skills CLI from your project: ```bash [Terminal] npx skills add https://vitehub.dev ``` The CLI discovers the published `vitehub` skill and installs it for the supported coding agents you select. Run `npx skills list` to inspect installed project skills. ## Follow one proof loop The skill orients in the current project, routes the smallest matching reference set, validates every planned import and option against installed exports and types, builds a coherent file set, and proves every requested behavior through its real runtime path. The bundled references teach reusable composition rather than copying the API reference. They cover project shapes, preview contracts, Server Primitives, framework composition, Agent Definitions and Drivers, Workspaces and Sources, Channels and Triggers, Capabilities, orchestration, Boxes and hosts, proof and recovery, and public project patterns. Links inside a reference are selection menus. The agent opens the smallest live raw page for the current behavior instead of loading the full reference library or documentation set. ## Ask for an outcome The skill activates from normal ViteHub requests. State the result you want and include any host or runtime constraint. | Task | Example prompt | | ---------------- | -------------------------------------------------------------------------------------- | | Server primitive | `Add ViteHub KV to this route and prove that a value survives a restart.` | | Agent | `Create a provider-backed review Agent with repository context and invoke it locally.` | | Host boundary | `Build this ViteHub application for Cloudflare and inspect its Provider Output.` | The skill selects one primary product lane, reads only matching references and the smallest live docs pages, checks installed exports and types, and reports the proof for each requested behavior. When documentation and an installed version differ, the installed contract controls the implementation and the agent reports the mismatch. ## Keep instruction sources distinct ViteHub uses several instruction sources for different actors. Keeping them separate prevents repository guidance from leaking into runtime Agent behavior. | Source | Audience | Purpose | | ------------------------------------------------------------------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------ | | Public ViteHub skill | Coding agents building a ViteHub application | Routes project patterns through live docs, installed contracts, explicit authority, and runtime proof. | | Repository `AGENTS.md` | Coding agents contributing to a repository | Defines local development rules and project boundaries. | | [Agent Driver Instructions](https://vitehub.dev/docs/agents/instructions) | Agents that run inside an application | Defines model-facing runtime behavior. | | Agent-local `skills/` | Provider-backed Agent Invocations | Automatically installs Skills owned by a folder Agent Definition. | | [`skills()` Capability](https://vitehub.dev/docs/capabilities/skills) | ViteHub Agent Invocations | Makes Workspace-backed or external Source Skills available to the Agent. | Agent-local Skills require a folder Definition. Place them beside `server/agents//agent.ts` under `server/agents//skills//SKILL.md`. A flat Definition such as `server/agents/review.ts` cannot own a sibling Skill tree; move it to `server/agents/review/agent.ts` when it needs colocated Skills. ## Use the docs fallback When a coding environment cannot install skills, start from [`llms.txt`](https://vitehub.dev/llms.txt){rel=""nofollow""} and load one [raw Markdown page](https://vitehub.dev/docs/ai-resources/markdown-pages). Keep the page URL with the supplied context so the agent can report which contract it followed. # AI-readable documentation ViteHub publishes a coding-agent skill, a compact documentation index, and raw Markdown pages. Use the smallest resource that gives your agent enough context to complete the task. ## Public resources | ViteHub resource | Use | | --------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | | [ViteHub coding-agent skill](https://vitehub.dev/docs/ai-resources/agent-instructions-skills) | Give Cursor, Claude Code, Codex, and other coding agents a repeatable ViteHub process. | | [`/llms.txt`](https://vitehub.dev/llms.txt){rel=""nofollow""} | Discover the current documentation map. | | [Raw Markdown pages](https://vitehub.dev/docs/ai-resources/markdown-pages) | Load one canonical page without the rendered site shell. | | [`/llms-full.txt`](https://vitehub.dev/llms-full.txt){rel=""nofollow""} | Load the complete documentation set when a broad audit genuinely needs it. | | [ViteHub OpenAPI document](https://vitehub.dev/openapi.json){rel=""nofollow""} | Discover the machine-readable resources served by the documentation host. | | [ViteHub MCP server](https://vitehub.dev/mcp){rel=""nofollow""} | Search the documentation from an MCP client over Streamable HTTP. | | [ViteHub CLI on npm](https://www.npmjs.com/package/vite-hub){rel=""nofollow""} | Install the official `vitehub` command with the framework distribution. | The OpenAPI document describes `vitehub.dev`, not a shared hosted runtime API. ViteHub runs inside your application, so its application endpoints depend on the Agent Definitions, Channels, and server routes that application declares. ## Install the skill The public skill is the recommended entry point for a coding agent that edits a ViteHub application. Install it through the skills CLI: ```bash [Terminal] npx skills add https://vitehub.dev ``` Ask the agent for the application outcome rather than a package recipe. The skill chooses the Server Primitives or Agents lane, checks the installed package contract, and proves the result. ```txt [Prompt] Add durable rate limiting to this server route with ViteHub and prove it locally. ``` ## Work without the skill An AI tool that cannot install skills can use the same public sources directly. Keep the context narrow so current task details remain prominent. ```txt [Agent flow] 1. Read https://vitehub.dev/llms.txt. 2. Choose the single smallest raw Markdown page for the task. 3. Inspect the application's installed ViteHub exports and types. 4. Keep the source URL with any copied context. ``` ::tip Use `llms-full.txt` for broad analysis, not routine implementation. One raw page usually gives a coding agent better signal. :: ## Choose the rendered site Use rendered pages when navigation, diagrams, or visual examples matter. Use raw Markdown when an agent needs source context or when you copy documentation into another tool. # Raw Markdown pages Raw Markdown pages expose canonical ViteHub content without the rendered site shell. Use them after `llms.txt` identifies the smallest page that answers the current task. ## Route patterns | Rendered route | Raw Markdown route | | ------------------------ | ------------------------------- | | `/docs` | `/raw/docs.md` | | `/docs/
` | `/raw/docs/
.md` | | `/docs/
/` | `/raw/docs/
/.md` | For example, the rendered page `/docs/ai-resources/markdown-pages` is available as `/raw/docs/ai-resources/markdown-pages.md`. ## Give an agent one page Start with the compact index, select one raw page, and preserve its URL with the supplied context. ```txt [Agent flow] 1. Fetch https://vitehub.dev/llms.txt. 2. Select one raw Markdown URL for the task. 3. Read that page and inspect the installed package contract. 4. Keep the URL in the final implementation report. ``` Add a second page only when the first page links to a required concept or reference. This keeps task context focused and makes documentation drift easier to identify. ## Copy context into another tool Copy the relevant section with its source URL. The receiving tool can then preserve provenance and fetch current context when needed. ```txt [Prompt context] Source: https://vitehub.dev/raw/docs/agents/instructions.md ``` ## Inspect local source Agents contributing to this repository can inspect `docs/content/docs/` directly. Use the public raw URL when building an external application so the context remains portable. ::important Raw pages describe the current published documentation. If their examples disagree with an application's installed exports or types, use the installed contract and report the mismatch. :: ## Choose another format Use [AI-readable documentation](https://vitehub.dev/docs/ai-resources) to choose between the public skill, `llms.txt`, `llms-full.txt`, raw Markdown, and rendered pages. # Access `access()` adds invocation-time access resolution for chat admission and Workspace Scope. Attach it first to restrict the Workspace seen by later Capabilities or to admit trusted chat webhooks. `access()` can resolve chat access and apply read-only Workspace Scope before other Capabilities run. Workspace scopes can grant paths or Sources and set a role. Model-facing scope guidance belongs in Agent Driver Instructions or deterministic instruction imports. ## Configure access Place `access()` before Workspace and storage Capabilities. The selected scope narrows the Workspace facade before `workspaceShell()` exposes tools. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { access, workspaceShell } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model }, workspace, capabilities: [ access({ workspace: { defaultScope: 'support', scopes: { support: { paths: ['support'] }, }, }, }), workspaceShell({ mode: 'read' }), ], }) ``` ## How access works The Capability records the selected Workspace Scope in invocation context and replaces the active Workspace facade with a scoped facade. Put model-facing guidance for each Access scope in Agent Driver Instructions or an imported instruction file, and cover the Access Capability with an explicit `::capability{key="access"}` block when that guidance depends on Access. Workspace Sources do not own authorization. Grant a Source by key from each Access scope that may use it, or grant its concrete Workspace path. Invocation-aware Source Resolution can then narrow the Source's repository, root, or Mount; Access recalculates a Source grant against that resolved shape before exposing the scoped Workspace. ## Requirements `access({ workspace })` requires an explicit Workspace. Model-backed and custom-run-backed Agents receive a read-only scoped Workspace; writable Workspace access is supported only for Provider Agent Drivers and remains limited to the selected scope. An admin role is required for an all-Workspace scope. `access({ chat })` requires a resolver that returns an allow or reject decision for chat traffic. Use trusted Agent Invoker or platform identity metadata; do not treat model text as access authority. ## Driver support | Agent Driver | Support | | ----------------- | ----------------------------------------------------------------------------------------------- | | Model-backed | Receives the scoped Workspace and any explicitly authored Agent instructions. | | Provider-backed | Receives the scoped Workspace behavior; model-facing instructions require provider support. | | Custom-run-backed | Receives the prepared context value and scoped Workspace; `driver.run` decides how to use them. | ## Verify access Run an Agent Invocation that includes `access()` and inspect its traces or run events for the `access` Capability. Verify that `access.workspaceScope` appears in invocation context and that later Workspace tools cannot read outside the selected paths. Trigger a scope failure during development. Confirm that a missing scope, root-mounted Source grant, missing Workspace, or invalid path escape fails before model execution. ## Options | Option | Type | Default | Description | | -------------------------------- | ------------------------------- | ---------- | ----------------------------------------------------------------------------------- | | `chat.resolve` | `(context) => boolean | void` | none | Allow or reject trusted Chat Platform traffic before the Agent Invocation runs. | | `workspace.defaultScope` | `string` | none | Fallback Workspace Scope name when `resolve` does not choose one. | | `workspace.resolve` | `string | selection | function` | none | Select a Workspace Scope from trusted invocation context. | | `workspace.scopes` | `Record` | none | Optional named Workspace Scope definitions for explicit grants or full access. | | `selection.role` | `AccessRoleName` | `"viewer"` | Role applied to the selected scope. Full-Workspace access requires `"admin"`. | | `scope.all` | `boolean` | `false` | Grant the full Workspace for that scope when the selection uses the `"admin"` role. | | `scope.path` / `scope.paths` | `string | string[]` | none | Grant Workspace paths. | | `scope.source` / `scope.sources` | `string | string[]` | none | Grant Workspace Sources. | | `scope.grants` | `AccessWorkspaceScopeGrant[]` | none | Combine path and Source grants. | ## Related pages - [Workspace context](https://vitehub.dev/docs/agents/workspace-context) - [workspaceShell()](https://vitehub.dev/docs/capabilities/workspace-shell) # Blob `blob()` adds model-facing tools for a configured ViteHub Blob primitive. It exposes object read, metadata, and list operations by default, then adds edits only in write mode. The Capability contributes `blob_read` for get, head, and list operations. When configured with write mode, it also contributes `blob_edit` for putting or deleting objects. `blob_edit` can upload inline content, a current input attachment through `attachmentId`, or a Workspace file through `workspacePath`. For Provider Agents, `assetPaths` also turns final-answer Markdown references into published delivery artifacts. ## Configure Blob access Attach Blob in read mode until the Agent needs to write objects. The Blob primitive must already be configured by the app. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { blob } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model }, capabilities: [ blob({ mode: 'read' }), ], }) ``` ## How Blob access works ViteHub selects the configured Blob store and exposes the Blob tools. Read mode supports one object read, metadata read, or prefix list operation per tool call. Write mode adds put/delete operations and allows them by default. Put operations accept exactly one of `attachmentId`, `body`, or `workspacePath`. Delete operations return `{ pathname, deleted: true }`. ## Requirements `blob()` uses the configured `blob` primitive when present, or the default export from an installed `@vite-hub/blob` package. Named store selection requires the Blob primitive to expose store selection. Writes require explicit write mode. Set `policy: 'require-approval'` or `policy: 'deny'` when the product needs an additional gate. ## Driver support | Agent Driver | Support | | ----------------- | ----------------------------------------------------------------------------------------------------------------------------- | | Model-backed | Receives `blob_read` and, in write mode, `blob_edit`. | | Provider-backed | Receives the Capability tools. In write mode, `assetPaths` also publishes current-run files referenced by the final Markdown. | | Custom-run-backed | The configured primitive is available through runtime context; `driver.run` decides how to use it. | ## Verify Blob access Run `vitehub agent info --agent --json` and inspect the resolved tool list. Confirm that read mode shows only `blob_read`. Write mode also lists `blob_edit` with the configured policy. Run one invocation against a missing Blob primitive during development. Confirm that the Capability fails before it exposes tools. ## Options | Option | Type | Default | Description | | ------------ | ------------------------------------ | ------------- | ------------------------------------------------------------------------------------------------------------------------------------- | | `mode` | `"read" | "write"` | `"read"` | Adds `blob_edit` when set to `"write"`. | | `assetPaths` | `boolean | string | string[]` | `false` | Materializes Provider asset paths and publishes current-run files explicitly referenced by final Markdown. `true` uses `screenshots`. | | `store` | `string` | default store | Selects a named Blob store when the Blob primitive supports `store()`. | | `policy` | `AgentToolPolicyDecision | function` | `"allow"` | Policy for `blob_edit`. | ## Provider artifacts Declare the directories where a Provider Agent may write public artifacts. The Agent can use its normal filesystem workflow, then reference a generated file in its final answer. ```ts [server/agents/review.ts] import { defineAgent } from 'vite-hub/agent' import { blob } from 'vite-hub/agent/capabilities' import { github } from 'vite-hub/agent/channels' export default defineAgent({ capabilities: [ blob({ assetPaths: ['artifacts'], mode: 'write', policy: 'deny' }), ], channels: { github: github({ pullRequest: true }), }, driver: 'codex', workspace: { commit: true, mode: 'write' }, }) ``` If Codex adds `![Preview](artifacts/preview.png)` to its final answer, ViteHub publishes the file through Blob, records it in `AgentRunResult.artifacts`, and rewrites that exact Markdown destination during Channel delivery. Publication is deliberately bounded. ViteHub accepts only Markdown links or images under `assetPaths`, intersects them with files added or modified by the current Provider Workspace write-back, and ignores bare paths, stale files, removed files, and paths outside the declared roots. `policy` still controls the model-facing `blob_edit` tool; host-owned artifact publication does not require that tool to be enabled. Configure Blob serving or a Blob driver that returns public URLs. When `blob.serve` returns a route-relative URL, Agent delivery resolves it against the invocation request URL. ## Workspace uploads Use `workspacePath` to upload a Workspace artifact written by another Capability to Blob storage. The path is Workspace-relative. ```ts [Agent tool call] await blob_edit({ operation: 'put', pathname: 'review/screenshots/home.png', workspacePath: 'screenshots/home.png', options: { contentType: 'image/png' }, }) ``` ## Related pages - [Blob primitive](https://vitehub.dev/docs/server-primitives/blob) - [Official capabilities](https://vitehub.dev/docs/capabilities/official-capabilities) # Browser `browser()` mounts an inspectable browser Skill at `skills/browser/SKILL.md` for a Provider Agent. The matching CLI must already be available to the provider process. ```ts [server/agents/review.ts] import { defineAgent } from 'vite-hub/agent' import { browser } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: 'codex', workspace: { mode: 'write' }, capabilities: [browser()], }) ``` The Skill tells the provider to use `agent-browser` through its native command tools and save screenshots inside the Workspace. ViteHub does not install the CLI or add a parallel shell tool. Model-backed and custom Drivers fail explicitly because they do not own the required provider command loop. | Option | Type | Default | Description | | -------------- | -------- | --------------------------- | ---------------------------------------- | | `command` | `string` | `"agent-browser"` | Executable name named in the Skill. | | `skillContent` | `string` | built-in browser Skill | Markdown content for the mounted Skill. | | `skillPath` | `string` | `"skills/browser/SKILL.md"` | Workspace path for the Skill. | | `sourceKey` | `string` | `"skill.browser"` | Workspace Source key for the Skill file. | Use the [Browser primitive](https://vitehub.dev/docs/server-primitives/browser) when trusted server code owns the Browser Session lifecycle. # Chat `chat()` adds chat-oriented runtime behavior to an Agent Definition. It contributes a `chat.message` Agent Trigger, Chat History state requirements, and a chat finish extension. The Chat Capability turns message-shaped input into Agent Invocations and exposes the trigger to the CLI Dev Loop. Message-shaped Channels own route admission and delivery into that trigger. ## Configure chat Attach `chat()` to call the Agent from a chat interface through the Agent Trigger API. Use [Channels](https://vitehub.dev/docs/agents/channels) to deliver messages from Slack, Telegram, Teams, web chat, or another adapter. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { chat } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model }, capabilities: [ chat(), ], }) ``` ## How chat works `chat()` registers the `chat.message` trigger with `ui-message[]` input and `ui-message-stream` output. It prepares Chat History state when available, records chat context, and provides chat finish data after the Agent Invocation completes. ## Requirements Chat History state is optional in local development but needs an Agent State Provider when the deployed stack requires durable sessions or concurrency coordination. External Chat Platform Adapters remain explicit application dependencies configured through Channels. ## Driver support | Agent Driver | Support | | ----------------- | ------------------------------------------------------------------------------------------------------------- | | Model-backed | Receives message-shaped input and can stream chat output through the model-backed path. | | Provider-backed | Receives the prepared invocation input and chat context; provider chat behavior follows the provider adapter. | | Custom-run-backed | Receives the chat trigger input and context; `driver.run` owns the response shape. | ## Verify chat Run `vitehub agent dev --agent --prompt "hello"` and confirm the Agent responds through the configured Chat Capability. Send one message through `vitehub agent dev` and verify the invocation origin, Chat Session behavior, and finish extension through traces or run events. For adapter-backed delivery, inspect the Channel-generated webhook registrations for the expected route metadata. ## Options | Option | Type | Default | Description | | ---------------------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `hooks` | `AgentChatEventHooks` | none | Chat event hooks such as `onDirectMessage`. | | `lifecycleHooks` | `Record` | none | Additional lifecycle-hook settings for integrations that consume Chat Capability configuration. | | `event` | `"directMessage"` | none | Chat event binding hint. | | `triggerHistory` | `"none" | { source: "thread"; maxMessages?: number }` | last 20 messages, or `threadHistory.maxMessages` when derived | Chat History Window sent into the `chat.message` Agent Trigger. | | `threadHistory` | `{ maxMessages?: number; ttlMs?: number }` | inherited | Adapter thread backfill/cache; stores messages but does not by itself define model input. | | `messageHistory` | Chat SDK message-history configuration | inherited | Adapter message-history behavior passed to the Chat SDK. | | `logger` | Chat SDK logger | inherited | Logger passed to adapter-backed Chat SDK delivery. | | `sessions` | `boolean | AgentChatSessionOptions` | inherited | Chat Session behavior, including `strategy`, `idleTimeoutMs`, and `metadataKey`. | | `state` | `AgentChatStateResolver` | runtime state | Chat State adapter override. | | `transcripts` | Chat SDK `TranscriptsConfig` | none | Transcript persistence configuration for adapter-backed Channels. | | `identity` | `IdentityResolver` | channel-qualified user id when transcripts are enabled | Resolve the identity used to partition transcripts. | | `stream` | `boolean` | inherited | Streams chat trigger output when enabled. | | `streamingUpdateIntervalMs` | `number` | inherited | Minimum interval between streamed Channel message updates. | | `concurrency` | `"drop" | "parallel" | "queue" | "reject" | "serial" | "steer" | string` | inherited | Overlapping message behavior. `serial` runs each retained message as a separate awaited Agent Invocation in queue order; `queue` coalesces retained messages into one invocation. `steer` is accepted for API compatibility and currently uses the same coalescing behavior as `queue`. Queue retention and failure guarantees come from the configured Chat State runtime. | | `lockScope` | `"agent" | "channel" | "thread" | string` | inherited | Scope used for message locks. | | `dedupeTtlMs` | `number` | inherited | Time-to-live for Chat SDK duplicate-message keys. | | `userName` | `string` | `"vitehub"` | Agent username used by adapter-backed Chat SDK delivery. | | `fallbackStreamingPlaceholderText` | `string | string[] | null | function` | inherited | Placeholder text while streaming starts. Arrays pick one entry per Agent Invocation; empty arrays skip the placeholder. | | `errorFallbackText` | `string | null | function` | inherited | Fallback message when chat handling fails. | ## Related pages - [Agent triggers](https://vitehub.dev/docs/agents/triggers) - [Chat History and sessions](https://vitehub.dev/docs/agents/chat-history-sessions) - [title()](https://vitehub.dev/docs/capabilities/title) - [chatSummary()](https://vitehub.dev/docs/capabilities/chat-summary) # Chat summary `chatSummary()` adds a conversation summary command. It looks for an explicit command in the latest user input, generates a summary, replaces the command with summary text, and exposes the generated summary as output metadata. The Capability contributes input behavior similar to `inputCommands()`. By default it recognizes a summary command, summarizes the current conversation, and writes the summary into Agent Run Input context. ## Configure summaries Attach `chatSummary()` with `chat()` to let users request a summary from a chat interface. The default command uses the standard input command trigger. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { chat, chatSummary } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model }, capabilities: [ chat(), chatSummary(), ], }) ``` ## How summaries work `chatSummary()` runs during the input phase. When the configured command is present, it removes the command text, summarizes the source messages with a model, custom executor, or heuristic fallback, then replaces the command with `Conversation summary`. The generated value is available as `chatSummary` in input context and as a finish extension for the invocation that generated it. ## Requirements The command name must be a valid Input Command name. Model-based summaries require an explicit model option; otherwise the Capability uses its heuristic fallback. Disable the command only when application code invokes the summary behavior directly. ## Driver support | Agent Driver | Support | | ----------------- | ---------------------------------------------------------------------- | | Model-backed | Receives the transformed input containing the generated summary. | | Provider-backed | Receives the transformed input before provider execution. | | Custom-run-backed | Receives the transformed input and context values before `driver.run`. | ## Verify summaries Run a chat invocation with the summary command. Inspect the final Agent Run Input and confirm the command was replaced with `Conversation summary` text. Inspect the finish extension and verify it appears only on the invocation that generated the summary. ## Options | Option | Type | Default | Description | | --------------------- | ------------------------------------------ | ----------------------------------- | ---------------------------------------------------- | | `command` | `false | ChatSummaryCommandOptions` | `{ name: "summary", trigger: "/" }` | Enables or configures the summary Input Command. | | `command.name` | `string` | `"summary"` | Command name. | | `command.trigger` | `string` | `"/"` | Command prefix. | | `command.description` | `string` | generated | Command description. | | `execute` | `(input) => string | { summary?: string }` | none | Custom summary generator. | | `fallback` | `string` | `"No conversation to summarize."` | Summary used when generation returns no usable text. | | `id` | `string` | `"chat-summary"` | Capability id and summary context key prefix. | | `instructions` | `string` | generated | System instructions for model-backed summaries. | | `maxLength` | `number` | `1200` | Maximum summary length. | | `model` | AI SDK model | heuristic fallback | Model used for summaries. | ## Related pages - [chat()](https://vitehub.dev/docs/capabilities/chat) - [inputCommands()](https://vitehub.dev/docs/capabilities/input-commands) # Cost Add `cost()` when an application needs exact USD for arithmetic and a ready-to-render value for display. The Capability enriches the Agent Usage Record before Agent Finish Hooks run and before streamed usage is emitted to clients; raw usage capture works without it. ## Add cost ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { cost } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model: 'zai/glm-5v-turbo' }, capabilities: [ cost(), ], }) ``` By default, `cost()` prices regular input, cache-read, cache-write, and output tokens from Vercel AI Gateway's public model catalog. ViteHub uses exact decimal arithmetic, caches successful catalog responses for five minutes, and bounds each request to ten seconds. Pricing is best-effort. A missing model match, unavailable catalog, timeout, invalid price, or pricing callback error leaves the usage record and successful Agent Invocation unchanged. A cost already reported by the provider remains authoritative. ## Read the enriched record Finish Hooks read the canonical record from `event.invocation.usage`. Use `cost.usd` for arithmetic or persistence and `cost.display` for UI; consumers do not need to format the value themselves. The Capability's typed `cost` finish extension returns the same record. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { cost } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model: 'zai/glm-5v-turbo' }, capabilities: [cost()], hooks: { 'agent:finish'(event) { const usageCost = event.invocation.usage?.cost if (!usageCost) return console.log(usageCost.usd) console.log(usageCost.display) }, }, }) ``` ```txt [Output] 0.00125 ~$0.00125 ``` The canonical record keeps the full model identifier and Gateway transport separate, preserves exact USD for arithmetic, and includes a ready-to-render display value. ```ts { model: 'zai/glm-5v-turbo', transport: 'gateway', usage: { inputTokens: 1000, outputTokens: 50, totalTokens: 1050 }, cost: { usd: '0.00125', display: '~$0.00125', estimated: true, source: 'vercel-ai-gateway', }, } ``` For streams, ViteHub waits to resolve pricing until the usage record becomes available during consumption, then enriches it before client emission and Finish Hooks. ## Provide application pricing Pass `pricing` when the application owns its catalog or provider mapping. Return exact USD as a decimal string; ViteHub derives the display value. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { cost, type AgentUsagePricing } from 'vite-hub/agent/capabilities' const pricing: AgentUsagePricing = ({ model }) => { if (model !== 'internal/support-model') return return { usd: '0.00125', estimated: true, source: 'custom', } } export default defineAgent({ driver: { model: 'internal/support-model' }, capabilities: [cost({ pricing })], }) ``` Return `undefined` when pricing is unavailable. Custom pricing receives the model, response metadata, Agent Run metadata, and token usage, and can return a provider quote or a calculation from an application-owned decimal library. Keep it deterministic for those inputs because ViteHub may call it while a stream is consumed. Import `vercelAiGatewayPricing()` when application-owned work adds usage after the Capability runs and must reprice the record with the same catalog behavior. ```ts import { vercelAiGatewayPricing } from 'vite-hub/agent/capabilities' const pricing = vercelAiGatewayPricing() ``` ## Options | Option | Type | Default | Description | | --------- | ------------------- | --------------------------------- | --------------------------------------------------------------------------------------- | | `pricing` | `AgentUsagePricing` | Vercel AI Gateway catalog pricing | Resolves a cost from the model, response metadata, Agent Run metadata, and token usage. | ## Verify it Invoke the Agent with a model that reports token usage. Confirm that `event.invocation.usage` is present without the Capability, then install `cost()` and confirm a matched model adds `cost`. Test missing and failing pricing callbacks too: both must preserve the successful Agent Invocation and raw usage. ## Related - [Agent Invocations](https://vitehub.dev/docs/concepts/agent-invocations) - [Runtime events](https://vitehub.dev/docs/reference/runtime-events) - [Custom capabilities](https://vitehub.dev/docs/capabilities/custom-capabilities) # Custom capabilities Create a custom Capability when the official catalog does not describe the Agent ability your product needs. Start from the product ability, not from the raw tool or primitive call. A custom Capability names what the Agent can do and declares the requirements it needs. Inspection output lists the tools, triggers, context values, metadata, and policy decisions contributed by that Capability. ## Minimum shape Define the Capability near the application code that owns the behavior. Use `defineCapability()` so ViteHub validates the id and composes the Capability through the normal lifecycle. ```ts [server/agents/capabilities/tickets.ts] import { defineCapability } from 'vite-hub/agent' import { z } from 'zod' const searchTicketsInput = z.object({ query: z.string(), }) export function tickets() { return defineCapability({ id: 'tickets', tools: { searchTickets: { name: 'searchTickets', description: 'Search support tickets by query.', inputSchema: searchTicketsInput, execute: async (input: z.output) => searchTickets(input.query), }, }, }) } ``` Agent tools accept raw JSON Schema or a validator that implements both Standard Schema and Standard JSON Schema. Zod 4 implements both directly. ViteHub does not bundle a validator; use the one your app owns. Attach the custom Capability like any official Capability. Keep instructions explicit in the Agent Driver so Capability config does not become a hidden prompt bag. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { tickets } from './capabilities/tickets' export default defineAgent({ driver: { model, instructions: [ 'Triage support requests.', 'Use ticket tools only for support ticket lookup and triage.', ].join('\n\n'), }, capabilities: [ tickets(), ], }) ``` ## Add requirements Requirements fail before the Capability exposes behavior. Use them when the Capability needs a configured primitive, a Workspace, a writable Workspace, or a specific Workspace path. Do not create missing storage, Workspace paths, or execution authority implicitly. If the product needs provisioning, keep that in the primitive or framework integration layer. ## Add policy Tool policy defaults to `allow` when omitted. Use `require-approval` or `deny` when a model-facing action needs an additional runtime gate after the Capability has established its modes, scopes, allowlists, and input validation. Keep custom policy narrow and visible. A reviewer needs to understand the operations it permits by reading the Capability Definition. ## Contribute Workspace inputs Use `workspace` to add Workspace Sources or rules for an invocation. The contribution is add-only and inspectable; it does not mutate the Agent's authored Workspace Definition. ```ts [server/agents/capabilities/tickets.ts] import { defineCapability } from 'vite-hub/agent' export function ticketContext() { return defineCapability({ id: 'ticket-context', workspace: ({ context }) => { const ticketId = context.get<{ id?: string }>('ticket')?.id if (!ticketId) return return { rules: { 'support/tickets/**': { read: true }, }, sources: { ticket: { mount: 'support/tickets', async getKeys() { return [`${ticketId}.md`] }, async getItem(key) { return { content: await loadTicketMarkdown(key), key, mediaType: 'text/markdown', } }, }, }, } }, }) } ``` For provider-backed Agents, declare required files through `requires.workspace.paths` or contribute them through Workspace Sources. The Provider Workspace session materializes the selected scope, so the application does not need a provider-specific path list. ## Add a Capability CLI Use `cli` when the Capability owns commands that agents and developers can run instead of a generic shell command. The public API accepts a static command tree or an invocation resolver on the Capability Definition. ```ts [server/agents/capabilities/inventory-runtime.ts] import { defineCapability } from 'vite-hub/agent' import * as v from 'valibot' const inventoryItemsInput = v.object({ limit: v.optional(v.pipe(v.number(), v.integer(), v.minValue(1))), }) const inventoryItemsOutput = v.object({ items: v.array(v.object({ id: v.string() })), }) export const inventoryRuntime = defineCapability({ id: 'inventory-runtime', cli: { name: 'inventory', description: 'Inspect live inventory data.', commands: { items: { description: 'Inventory item data.', commands: { list: { description: 'List inventory items for the current application context.', input: inventoryItemsInput, output: { format: 'json', schema: inventoryItemsOutput }, effects: ['read', 'network:inventory'], async run({ input }) { return await listInventoryItems(input) }, }, }, }, }, }, }) ``` The `input` and `output.schema` values accept any Standard Schema-compatible validation library. ViteHub exposes command metadata through the generated CLI-named tool. Keep `instructions.md` focused on policy. Use `::capability{key="inventoryRuntime"}` to mark guidance for that Capability. Return `undefined` from a resolver to hide the CLI for the current invocation. The Capability remains attached and inspectable. To omit the entire Capability, resolve the Agent Definition's `capabilities` list instead. This decides selection before the Capability contributes tools, CLI commands, requirements, hooks, or cleanup work. ```ts [server/agents/capabilities/inventory-runtime.ts] export const inventoryRuntime = defineCapability({ id: 'inventory-runtime', cli: ({ actor }) => actor.kind === 'support' ? { name: 'inventory', commands: { list: { run: () => listInventoryItems(), }, }, } : undefined, }) ``` First-party adapters can generate the same CLI shape from their own metadata. For example, `openapi({ cli: { name: 'billing' }, ... })` creates one subcommand per allowed OpenAPI operation and preserves each operation summary or description in the tool contract. Use a resolver for invocation-specific availability, not to mutate command ownership after a run starts. During development, run the Capability CLI through the Agent Dev Loop. Agents expose attached Capability CLI Contributions to compatible Agent Drivers and the Agent Dev Loop by default. Use `defineAgent({ cli: { capabilities: false } })` to attach the Capability without exposing its CLI. ```bash [Terminal] pnpm vitehub agent dev --url http://localhost:3000 --agent support --cli inventory -- items list --json ``` ## Driver support | Agent Driver | Custom Capability behavior | | ----------------- | -------------------------------------------------------------------------------------------------------------------------------- | | Model-backed | Receives model-facing tools when the Capability contributes them. | | Provider-backed | Receives Agent tools through the private MCP bridge plus supported runtime effects. Provider Tool contributions are unsupported. | | Custom-run-backed | Receives prepared input and invocation context; `driver.run` decides which custom Capability outputs to consume. | ## Verify a custom Capability Run one Agent Invocation through `vitehub agent dev` and inspect its streamed tool events. Check that the custom Capability id appears once, its requirements pass, and its tools are exposed only when expected. Add an Agent Eval when the Capability changes product behavior. Use a focused fixture that proves the Capability exposes the intended ability and does not expose adjacent authority. ## Expose eval-visible metadata Use a `finish` provider to publish invocation metadata for finish hooks, channel delivery code, or eval assertions. The value is keyed by Capability id and is available through `observation.extensions.get(id)` in Agent Evals. ```ts [server/agents/capabilities/tickets.ts] import { defineCapability } from 'vite-hub/agent' export function tickets() { return defineCapability({ id: 'tickets', finish(event) { return { resultKind: typeof event.result, status: event.error ? 'failed' : 'completed', } }, }) } ``` ```ts [server/agents/support.eval.ts] import { defineEval, hasCapabilityExtension } from 'vite-hub/agent/eval' import support from './support' export default defineEval({ agent: support, scenarios: [{ name: 'uses ticket context', input: { prompt: 'Find the open billing ticket' }, scorers: [ hasCapabilityExtension('tickets', 'status'), ], }], }) ``` ## Related APIs - [Capabilities overview](https://vitehub.dev/docs/capabilities) - [Official capabilities](https://vitehub.dev/docs/capabilities/official-capabilities) # Database `db()` adds model-facing tools for a configured ViteHub Database primitive. It exposes read-only query and schema inspection by default, then adds SQL mutation only when write modes allow it. Cloudflare and Vercel hosted Agent routes receive the Database primitive automatically when the app configures `hubDb()`. The Capability contributes `db_query` for one read-only SQL statement and `db_schema` for schema inspection. When data or schema write modes allow it, it also contributes `db_exec` for one mutation statement with a rationale. ## Configure database access Attach DB in read mode until the Agent needs guarded mutations. The Database primitive must already be configured by the app. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { db } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model }, capabilities: [ db({ mode: 'read' }), ], }) ``` ## How database access works ViteHub selects the configured database handle and enforces the single-statement SQL guardrail. `db_query` accepts one read-only query. `db_exec` rejects read-only SQL, requires a rationale, and separates data mutations from schema changes through `mode` and `schemaMode`. ## Requirements `db()` requires a configured `db` primitive. The primitive must expose raw string `query()` for reads and `exec()` for mutations. Mutation tools require write mode. DDL requires schema write mode. Enabled mutations are allowed by default, while the single-statement, rationale, and SQL-kind checks still apply. Set `policy: 'require-approval'` or `policy: 'deny'` when the product needs an additional gate. ## Driver support | Agent Driver | Support | | ----------------- | -------------------------------------------------------------------------------------------------- | | Model-backed | Receives `db_query`, `db_schema`, and write tools when enabled. | | Provider-backed | Receives Database tools through the provider MCP bridge. | | Custom-run-backed | The configured primitive is available through runtime context; `driver.run` decides how to use it. | ## Verify database access Run `vitehub agent info --agent --json` and inspect the resolved tool list. Confirm that read mode shows `db_query` and `db_schema`. A write-capable configuration also lists `db_exec` with the configured policy. Run a multi-statement SQL input during development. Confirm that the Capability rejects it before it reaches the Database primitive. ## Options | Option | Type | Default | Description | | ------------ | ------------------------------------ | ----------- | --------------------------------------------------------------------- | | `database` | `string` | `"default"` | Selects a named database when the DB primitive supports `database()`. | | `mode` | `"read" | "write"` | `"read"` | Allows data mutation through `db_exec` when set to `"write"`. | | `schemaMode` | `"read" | "write"` | `"read"` | Allows DDL through `db_exec` when set to `"write"`. | | `policy` | `AgentToolPolicyDecision | function` | `"allow"` | Policy for `db_exec`. | ## Related pages - [Database primitive](https://vitehub.dev/docs/server-primitives/database) - [Official capabilities](https://vitehub.dev/docs/capabilities/official-capabilities) # Diagnostics `diagnostics()` is an opt-in operational Capability. It reports a terminal event for every Agent Invocation and can sample resources through a Runtime inspector. Reporters receive structured events, so an application can write JSON logs, metrics, or another operations sink without coupling the Agent Definition to one dashboard. ## Observe a Node service Use ViteHub's Node adapter to observe process, host, and Linux cgroup resources: ```ts [server/agents/worker.ts] import { defineAgent } from 'vite-hub/agent' import { diagnostics } from 'vite-hub/agent/capabilities' import { nodeRuntimeResources } from 'vite-hub/runtime/node' export default defineAgent({ name: 'worker', capabilities: [ diagnostics({ resources: nodeRuntimeResources(), }), ], driver: { model: 'openai/gpt-5.1-mini' }, }) ``` The default reporter writes structured console objects. Pass `reporter` to own delivery: ```ts diagnostics({ resources: nodeRuntimeResources(), reporter: event => operations.write(event), }) ``` Reporter and inspector failures are contained. They produce a local diagnostic and do not replace a successful Agent result. ## Event contract The Capability reports: | Event | When | | ------------------------------- | ------------------------------------------------------------------------------------------------------------------- | | `agent.invocation.terminal` | The invocation completes or fails. Includes outcome, duration, run ID when present, and a bounded structured error. | | `agent.resource.snapshot` | Sampling starts, finishes, or reaches the heartbeat interval. | | `agent.resource.peak` | A peak observation grows by at least `peakStepBytes`. | | `agent.resource.inspect.failed` | The inspector fails or exceeds its timeout. | Resource observations declare a `scope`, `source`, `unit`, and numeric `value`. The Node adapter uses `process` for Node memory and CPU, `host` for available host memory, and `service` for Linux cgroup v2 values. A service observation provides correlation with an invocation's run ID; it is not per-invocation attribution when multiple invocations share the service. Unsupported sources are recorded in `support`. Unlimited cgroup values are omitted rather than reported as zero. This keeps small machines and non-Linux hosts honest without requiring application-specific `/proc` parsing. ## Sampling behavior Sampling is bounded to one active inspection and one coalesced pending reason. Reporter delivery is ordered and bounded by `timeout`. A slow inspector or reporter cannot create an unbounded polling backlog. Finish supersedes a stale poll and waits for the final observation before the Capability closes. `diagnostics()` is separate from `otlp()`: diagnostics records operator health and resource pressure, while OTLP exports the Agent Invocation trace. Keeping the lanes separate prevents a broken telemetry receiver from recursively hiding its own delivery failure. ## Options | Option | Type | Default | Description | | --------------- | --------------------------- | ------------------------- | --------------------------------------------------------------------------------- | | `reporter` | `RuntimeDiagnosticReporter` | Structured console output | Receives operational events. | | `resources` | `RuntimeResourceInspector` | None | Enables scoped resource sampling. | | `interval` | `number` | `10000` | Resource polling interval in milliseconds. Must not exceed `heartbeat`. | | `heartbeat` | `number` | `60000` | Maximum interval between snapshot events in milliseconds. | | `peakStepBytes` | `number` | `67108864` | Minimum peak increase before a peak event. | | `timeout` | `number` | `1000` | Maximum duration of one resource inspection or reporter delivery in milliseconds. | ## Related - [OTLP](https://vitehub.dev/docs/capabilities/otlp) - [Invocations](https://vitehub.dev/docs/agents/invocations) - [Runtime context](https://vitehub.dev/docs/concepts/runtime-context) # Email `email()` grants an Agent one external side effect: `email_send` sends a plain-text message from an application-owned sender through the configured ViteHub Email primitive. Attach it only when the Agent needs to contact external recipients. Restrict exact addresses with `recipients`, then add policy when delivery requires approval or contextual authorization. ::warning An approved call can contact real people and incur provider charges. Start with a short `recipients` allowlist, `policy: 'require-approval'` , a provider test account, and an approved test recipient. :: ## Configure the Email primitive first Configure one Unemail provider in the ViteHub preset. Runtime Env resolves the credential on the server for every send. ```ts [vite.config.ts] import { defineConfig } from 'vite' import { vitehub } from 'vite-hub' import { env } from 'vite-hub/env' export default defineConfig({ plugins: [ vitehub({ preset: 'node', email: { driver: 'unemail/driver/resend', options: { apiKey: env({ secret: true, source: env.source('RESEND_API_KEY') }), }, }, }), ], }) ``` Follow [Configure Resend](https://vitehub.dev/docs/server-primitives/email#configure-resend), or select another `unemail/driver/*` provider through the same `driver` option. Keep credentials in Server Env or the deployment platform's secret store and reference them with an Env declaration without a default. Literal options and non-secret Env defaults are included in build output; ViteHub rejects defaults on declarations marked secret. The Capability never exposes runtime credentials to the model. ## Requirements - The application must run on Node.js 24.15 or later. - Email configuration requires Vite 8 or later and one configured provider. - The configured provider must authorize the `from` address. - Generated Agent routes receive the Email runtime handle only while the Email Vite integration is active. ## Grant the Agent permission to send Import `email()` from the official Capability catalog. Set `from` to a sender that the configured provider authorizes. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { email } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model }, capabilities: [ email({ from: 'support@example.com', recipients: [ 'customer@example.net', 'owner@example.com', ], policy: 'require-approval', }), ], }) ``` This configuration exposes one tool: | Tool | Side effect | Result | | ------------ | -------------------------------------------------------- | --------------------------------------------------------- | | `email_send` | Sends one plain-text message from the configured sender. | The Email primitive's `{ id, driver }` acceptance result. | A successful result means the active provider accepted the message and returned an ID. It does not prove inbox delivery, display, or reading. ## Send a message `email_send` accepts exactly three fields. | Field | Type | Required | Description | | --------- | ---------------------------- | -------- | --------------------------------------------------------------------------- | | `to` | `string | readonly string[]` | Yes | One recipient address or a non-empty list. Every address must be non-empty. | | `subject` | `string` | Yes | A non-empty subject. | | `text` | `string` | Yes | A non-empty plain-text body. Do not include credentials or other secrets. | The Capability fixes `from` from application configuration. The model cannot set HTML, headers, attachments, carbon-copy recipients, blind-carbon-copy recipients, or reply routing through this tool. The Capability checks that recipient strings are non-empty; the Email driver and provider still own mailbox syntax, sender authorization, and delivery rules. ViteHub does not add recipient-count, text-length, payload-size, or send-rate limits beyond non-whitespace validation. Your delivery provider owns those limits and may charge for every accepted recipient or message. ## Authorize recipients Use `recipients` as the allowlist of exact addresses the Agent may contact. Every address in `email_send.to` must match the configured list; one address outside the list denies the entire call before the Email primitive runs, so ViteHub never partially sends a multi-recipient message. ```ts [server/agents/support.ts] email({ from: 'support@example.com', recipients: [ 'customer@example.net', 'owner@example.com', ], }) ``` ViteHub includes this list in Capability metadata and the `email_send` tool description, so the Agent can select a valid address without guessing. The list becomes part of the Agent's model context; include only addresses that the model is allowed to see. Matching trims surrounding whitespace and ignores letter case, but the Capability forwards the original address values to the Email provider. Aliases, display-name forms, and other address variations remain different strings unless they appear explicitly in `recipients`. Set `recipients: []` to deny all sends, or omit `recipients` when static recipient restriction belongs elsewhere. The optional `policy` is an additional gate after this allowlist. It cannot widen `recipients`: a configured `policy: 'allow'` still denies an address outside the list, while `policy: 'require-approval'` prompts only for an address that passed the list. Without `policy`, allowed recipients send immediately; a policy callback can apply contextual authorization by returning `allow`, `deny`, `require-approval`, or `retryable-failure`. Read [Runtime policy, approvals, and traces](https://vitehub.dev/docs/concepts/runtime-policy-approvals-and-traces) before enabling unattended delivery. ## Handle failures without duplicate delivery The Capability forwards the Email primitive result and error unchanged. It does not retry. Capability-owned validation and runtime failures happen before the Email driver runs, so these failures cannot have delivered a message: | Failure | When it occurs | | --------------------------------------------------------------- | ---------------------------------------------------------------------------- | | Invalid or missing `from` | `email()` rejects the Agent Definition during construction. | | Non-array `recipients`, or a blank, non-string, or sparse entry | `email()` rejects the Agent Definition during construction. | | Missing Email primitive | Capability resolution rejects before the Agent Driver receives `email_send`. | | Runtime handle without `send()` | Capability resolution rejects before the Agent Driver receives `email_send`. | | Empty `to`, `subject`, or `text` | Tool execution rejects before calling the Email primitive. | | Recipient outside `recipients` | Policy denies the entire tool call before calling the Email primitive. | After the Capability calls the Email primitive, handle the `EMAIL_*` ViteHub error code according to the delivery state: | Failure | What to do | | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | | `EMAIL_NOT_CONFIGURED` | Configure the Email integration with one provider. | | `EMAIL_AUTHENTICATION` | Fix provider credentials or sender authorization before retrying. | | `EMAIL_RATE_LIMITED` | Apply an application-owned backoff or queue policy. | | `EMAIL_NETWORK` or `EMAIL_TIMEOUT` | Treat delivery as uncertain. Check provider delivery logs before retrying, because the provider may already have accepted the message. | | `EMAIL_PROVIDER_FAILED` | Inspect protected server logs and provider delivery records. Never expose `cause` to the model. | ViteHub-produced `ViteHubError.message` values are safe to return from the public runtime. ViteHub-wrapped provider failures remain in `cause` for protected server-side diagnostics and may contain addresses, credentials, or response content; custom drivers must preserve the same rule. ## Keep Dynamic Markdown application-owned `email_send` is plain-text-only by design. It does not render model-authored Markdown into HTML because [`renderEmailMarkdown()`](https://vitehub.dev/docs/server-primitives/email#compose-dynamic-markdown) accepts trusted templates and does not sanitize authored HTML or trusted fragments. When a product needs branded HTML, compose a trusted template in application code and call the Email primitive directly, or expose a [Custom Capability](https://vitehub.dev/docs/capabilities/custom-capabilities) with a narrow set of escaped template values. Do not pass unrestricted model output into a trusted HTML fragment. ## Driver support | Agent Driver | Support | | ----------------- | ------------------------------------------------------------------------------------------- | | Model-backed | Receives `email_send` after the Email primitive resolves. | | Provider-backed | Runtime requirements apply; model-facing Email tools are not passed by default. | | Custom-run-backed | Receives the resolved tool set; `driver.run` decides whether and when to call `email_send`. | ## Verify email delivery Run `vitehub agent info --agent --json` and confirm its tool list contains only `email_send` for this Capability. Confirm that the Capability reports write mode and an `email` primitive requirement. For the first delivery, use an approved test recipient and a test or sandbox provider account. Approve the call, confirm the tool returns a non-empty `id`, then verify the same message in provider delivery logs or the recipient mailbox. ## Options | Option | Type | Default | Description | | ------------ | ------------------------------------ | --------------------------------- | ----------------------------------------------------------------------------------------------------------- | | `from` | `string` | Required | Non-empty application-owned sender passed to every message. The provider still validates and authorizes it. | | `recipients` | `readonly string[]` | `undefined` (no static allowlist) | Exact recipient allowlist. Every `to` address must match; an empty list denies all sends. | | `policy` | `AgentToolPolicyDecision | function` | `"allow"` | Optional approval or authorization policy for `email_send`. | `email()` has no `mode` option because Email exposes no read operation. The Capability always reports `mode: 'write'` so inspection and policy tooling can identify the side effect. ## Related pages - [Email primitive](https://vitehub.dev/docs/server-primitives/email) - [Official capabilities](https://vitehub.dev/docs/capabilities/official-capabilities) - [Runtime policy, approvals, and traces](https://vitehub.dev/docs/concepts/runtime-policy-approvals-and-traces) # Fetch `fetch()` adds model-facing HTTP tools that the developer names and defines. Use it for specific endpoints, not for unrestricted web browsing. The Capability creates one tool per entry in `tools`. Each tool can validate input, build a request, parse JSON or text, validate the response, and transform the output. ## Configure HTTP requests Define at least one named fetch tool. Keep the endpoint and method explicit. ```ts [server/agents/status.ts] import { defineAgent } from 'vite-hub/agent' import { fetch } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model }, capabilities: [ fetch({ tools: { serviceStatus: { description: 'Fetch current service status.', method: 'GET', url: 'https://status.example.com/api/status', }, }, }), ], }) ``` ## How requests work ViteHub validates the configured tool map and creates internal Agent tools. At invocation time, each tool resolves the request, executes the HTTP call, parses the configured response type, and returns either the parsed data or a transformed output. ## Requirements `fetch({ tools })` requires at least one tool definition. Each tool must provide a URL directly or return one from its request resolver. Use schemas for input and response validation when the endpoint accepts arguments or returns data that model behavior depends on. ## Driver support | Agent Driver | Support | | ----------------- | ---------------------------------------------------------------------------------------- | | Model-backed | Receives the named fetch tools. | | Provider-backed | Receives the named fetch tools through the provider MCP bridge. | | Custom-run-backed | Receives prepared context; `driver.run` decides whether to call HTTP endpoints directly. | ## Verify HTTP requests Inspect the Agent tool list and confirm only the named fetch tools appear. Run one invocation with invalid input when a schema is configured and verify the request does not leave the process. ## Options | Option | Type | Default | Description | | ------------------------- | -------------------------------------------- | ------------------------------ | ------------------------------------------------------------------------ | | `tools` | `Record` | required | Named fetch tools exposed to the model. | | `tools.*.description` | `string` | `Fetch .` | Tool description. | | `tools.*.url` | `string | URL` | none | Static request URL. | | `tools.*.request` | `object | function` | none | Static or input-derived request definition. | | `tools.*.method` | `"GET" | "HEAD" | "POST"` | `"GET"` | HTTP method used when the request definition does not override it. | | `tools.*.request.url` | `string | URL` | `tools.*.url` | Request URL. A request resolver can derive it from validated tool input. | | `tools.*.request.method` | `"GET" | "HEAD" | "POST"` | `tools.*.method`, then `"GET"` | Per-request HTTP method override. | | `tools.*.request.headers` | `Record` | none | Request headers. | | `tools.*.request.query` | `Record` | none | Query parameters appended to the URL. | | `tools.*.request.body` | `unknown` | none | Request body. | | `tools.*.request.timeout` | `number` | none | Request timeout in milliseconds. | | `tools.*.inputSchema` | Standard Schema | none | Validates model tool input before request construction. | | `tools.*.schema` | Standard Schema | none | Validates parsed response data. | | `tools.*.responseType` | `"json" | "text"` | `"json"` | Response parser. | | `tools.*.transform` | `(data, input) => output` | none | Maps validated response data before returning it to the model. | ## Related pages - [webSearch()](https://vitehub.dev/docs/capabilities/web-search) - [Custom capabilities](https://vitehub.dev/docs/capabilities/custom-capabilities) # Git `git()` exposes bounded Git inspection and selected local Workspace Session state changes. Use it for review, source-history inspection, and local branch selection, not for publishing repository history. The Agent must use a git-capable Workspace Session. The Capability adds one model-facing `shell` tool for controlled Git commands. In write mode, the same tool can also run a narrow set of local Git operations. ## Configure Git access ```ts [server/agents/reviewer.ts] import { defineAgent } from 'vite-hub/agent' import { git } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model }, workspace, capabilities: [ git({ mode: 'read' }), ], }) ``` ## How Git access works `shell` accepts one `git` command without shell composition. Read mode allows source-history commands such as `status`, `diff`, `log`, `show`, `grep`, and ref inspection. Write mode supports only `fetch`, `checkout`, and `switch` on a clean working tree. It blocks commit, push, reset, rebase, tag, arbitrary remote URLs, shell composition, and path escapes outside the Workspace. Supported write commands are allowed by default after the developer enables write mode. An explicit policy can require approval or deny them, but it cannot enable blocked commands. ## Requirements `git()` requires a Workspace primitive that can start a Workspace Session. The Workspace requirement is write-mode because ViteHub may need session-local Git state even when the exposed tool mode is read-only. ## Driver support | Agent Driver | Support | | ----------------- | --------------------------------------------------------------------------- | | Model-backed | Receives one controlled Git `shell` tool. | | Provider-backed | Receives the controlled Git `shell` tool through the provider MCP bridge. | | Custom-run-backed | Can use the Workspace Session directly and may inspect Capability metadata. | ## Options | Option | Type | Default | Description | | ----------------- | ------------------------------------ | ---------- | ---------------------------------------------------------------------------------------------- | | `mode` | `"read" | "write"` | `"read"` | Allows local `fetch`, `checkout`, and `switch` commands through `shell` when set to `"write"`. | | `maxOutputLength` | `number` | `Infinity` | Maximum stdout/stderr characters returned per command. | | `policy` | `AgentToolPolicyDecision | function` | `"allow"` | Policy for write-mode `shell` commands. Read-only Git commands remain allowed. | | `timeout` | `number` | `60000` | Execution timeout in milliseconds passed to Workspace Session Git commands. | ## Verify Git access Run `vitehub agent info --agent --json` and inspect the resolved tool list. Run `git status --short` through `shell`, then verify unsupported commands such as `git push` are rejected. ## Related pages - [Workspace](https://vitehub.dev/docs/server-primitives/workspace) - [Workspace shell](https://vitehub.dev/docs/capabilities/workspace-shell) # Gmail `gmail()` gives an Agent structured Gmail search and authorization tools. Draft mode adds draft creation, but the Capability never exposes a send tool or the underlying `gog` executable. Use [`email()`](https://vitehub.dev/docs/capabilities/email) for application-owned transactional email through the Email primitive. Use `gmail()` for an operator-owned Gmail account and structured Gmail tools. ## Configure the Agent Install [`gog`](https://github.com/openclaw/gogcli){rel=""nofollow""} on the Workspace Session host, configure its Google OAuth client, and keep its authentication state under the service account. The application owns this setup; the Capability never accepts OAuth client secrets or keyring passwords as tool input. ```ts [server/agents/inbox.ts] import { defineAgent } from 'vite-hub/agent' import { gmail } from 'vite-hub/agent/capabilities' export default defineAgent({ capabilities: [ gmail({ mode: 'draft' }), ], driver: 'codex', workspace: { mode: 'write', }, }) ``` Current [`gog` path conventions](https://github.com/openclaw/gogcli/blob/main/docs/paths.md){rel=""nofollow""} keep configuration in `.config/gogcli` and OAuth metadata plus file-keyring entries in `.local/share/gogcli` on Linux, so persist both directories for the service account. Supply `GOG_KEYRING_PASSWORD` through Server Env or the deployment secret store. Follow the [`gog` OAuth client setup](https://github.com/openclaw/gogcli/blob/main/docs/quickstart.md){rel=""nofollow""} before the first authorization attempt. ## Choose a mode Read mode is the default and exposes two tools: | Tool | Behavior | | -------------- | -------------------------------------------------------------------------- | | `gmail_auth` | Starts or completes remote authorization for one Gmail address. | | `gmail_search` | Searches or lists Gmail threads. It does not retrieve full message bodies. | Draft mode exposes the same tools plus `gmail_draft`, which creates an unsent draft with `to`, optional `cc` and `bcc`, a subject, and a plain-text body. ```ts gmail() gmail({ mode: 'draft' }) ``` `gmail()` has no send mode. Search commands run with read-only and no-send controls. Draft creation runs with `--gmail-no-send`, and no Capability-owned tool can send the resulting draft. This limits the tools exposed to the Agent, not the credential itself. If sending must be impossible, isolate the credential behind a runtime or provider policy that cannot send. `gmail()` does not provide that isolation. ## Complete authorization Gmail tools return authorization as structured states instead of asking the user to run shell commands: | Status | Next action | | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ | | `account_required` | Ask which Gmail address to use, then retry the original tool with `account`. | | `authorization_required` | Send `authorizationUrl` to the user. Google may redirect to a localhost page that does not load; collect the full browser address-bar URL. | | `connected` | Retry the original Gmail tool. | | `configuration_required` | The operator must configure the OAuth client using `setupUrl`. Do not request secrets in chat. | Complete a pending redirect through `gmail_auth`: ```ts [Agent tool call] await gmail_auth({ action: 'complete', account: 'owner@example.com', redirectUrl: 'http://localhost:8080/?code=...&state=...', }) ``` The Capability accepts only an HTTP loopback URL with both `code` and `state`. It exchanges the URL on the Workspace Session host and does not return it in the result. ## Runtime requirements `gmail()` requires all of the following: - An explicit Workspace with `workspace.mode: 'write'`, because each structured Gmail call opens a writable Workspace Session. - A Workspace Session host with command execution and `gog` available. - Operator-owned OAuth client configuration and persistent service-account state. Each underlying `gog` command opens its own Workspace Session and closes the Session on success or failure. Gmail search results remain untrusted external content and the contributed `skills/gmail/SKILL.md` tells the Agent to treat them as data, not instructions. Draft authorization may grant the Gmail account scope that `gog` needs to create drafts. The no-send contract applies only to the Capability-owned tools and their command flags. ## Verify Gmail access Run `vitehub agent info --agent --json` and inspect the resolved tools. Read mode lists only `gmail_auth` and `gmail_search`. Draft mode also lists `gmail_draft`. Start with a test Gmail account. Search for `in:inbox`, create a draft in draft mode, and verify in Gmail that the message remains in Drafts and was not sent. ## Options | Option | Type | Default | Description | | ------ | ------------------ | -------- | ------------------------------------------------------------------------------------- | | `mode` | `"read" | "draft"` | `"read"` | Exposes search and authorization tools, with draft creation added only in draft mode. | ## Related pages - [Workspace shell](https://vitehub.dev/docs/capabilities/workspace-shell) - [Email Capability](https://vitehub.dev/docs/capabilities/email) # Capabilities Capabilities give an Agent a named ability through `defineAgent({ capabilities })`. They can add tools, triggers, input processing, output metadata, and checks that run before an invocation. A Capability is not a server primitive. Server primitives give trusted app code authority. Capabilities decide which operations an Agent Invocation can use. ## Capability lifecycle ViteHub applies Capabilities in the order listed or returned by the Agent Definition. It validates duplicate ids, checks runtime requirements, applies Capability Trigger Contributions, and then runs configure, prepare, bind, input, resolve, and output phases for each invocation. `access()` is the only official Capability with a fixed position rule. Place it first so later Capabilities receive the restricted Workspace and tool access. ## Attach a Capability Import official factories from `@vite-hub/agent/capabilities`. Import the factory from the Capabilities entry point: ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { workspaceShell } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model }, workspace, capabilities: [ workspaceShell({ mode: 'read' }), ], }) ``` Attaching a Capability opts the Agent into that ability. Model-facing tool policy defaults to `allow`. Set `policy: 'require-approval'` or `policy: 'deny'` when a tool needs another runtime check. Modes, scopes, allowlists, requirements, and input validation still restrict the operation before policy runs. Use a callback when invocation context decides the Agent Definition's Capability list. ViteHub calls it once after resolving the Agent Invoker and before Capability setup; Capabilities contributed by the active Channel still compose normally. ```ts [server/agents/support.ts] export default defineAgent({ driver: { model }, capabilities: ({ actor }) => [ workspaceShell({ mode: 'read' }), ...(actor.meta?.support === true ? [internalDiagnostics] : []), ], }) ``` Return only invocation-scoped behavior from the callback. Capabilities that contribute Agent Triggers, chat admission, or static Workspace Sources must stay in a static list because ViteHub registers those contributions before an invocation exists. ## Use an Eve extension ViteHub detects compatible Eve extension packages in a static Capability list and compiles their tools into a Capability. Install the extension, then use its existing factory and options: ```ts [server/agents/reviewer.ts] import github from '@github-tools/eve-extension' import { defineAgent } from 'vite-hub/agent' export default defineAgent({ driver: { model }, capabilities: [ github({ preset: 'code-review' }), ], }) ``` The Vite plugin reads the package's Eve manifest and fails the build when a declared contract version is unsupported. The first bridge supports one mount per extension package, direct default-import factory calls, static and `session.started` tools, tool schemas and output conversion, and Eve's `always`, `never`, and `once` approval modes. ViteHub maps `session.started` to the start of each Agent Invocation and uses the invocation's `runId` as the Eve session ID, so every invocation resolves a fresh tool set without relying on process-local state. The built-in HTTP chat route persists pending approvals in its configured Chat state, reconstructs the authoritative tool call server-side, and consumes each response once under a session lock. Client-supplied chat history never creates approval authority. Unsupported dynamic events fail when ViteHub resolves the extension's tools for an Agent Invocation. This bridge is not yet a complete Eve runtime. Tool and approval contexts do not support `getSandbox()`, `getSkill()`, `getToken()`, or `requireAuth()`; using one throws at runtime. Session authentication is unavailable and turn sequence metadata is not preserved. ViteHub Agent Invocations are not Eve durable sessions, so extensions that depend on one `session.started` resolution spanning several invocations are not supported yet. ## What Capabilities can contribute | Contribution | What it changes | | --------------- | --------------------------------------------------------------------------------------------------------- | | Requirements | Primitive, Workspace mode, Workspace path, or policy checks that must pass before the Capability applies. | | Tools | Model-facing operations exposed only to compatible Agent Drivers. | | Provider tools | Provider-native tool requests, such as model web search mode. | | Agent Triggers | Product events that start Agent Invocations through the Agent Package trigger API. | | Input behavior | Pre-invocation input transforms, transcription, decisions, gates, and rate limits. | | Output behavior | Stream renderers, finish extensions, usage records, titles, and summaries. | | Metadata | Inspectable configuration for runtime diagnostics and CLI inspection. | Capability metadata appears under the Capability id in Agent inspection output. ViteHub keeps JSON values, sorts object keys and Capability ids, drops unsupported or cyclic values, and redacts keys shaped like auth, API keys, credentials, passwords, secrets, or tokens. Metadata must describe configuration or an explicit check result; it must not include Env values, authentication material, or credentials. Use `defineCapability({ finish })` for metadata read by evals, finish hooks, or channel delivery code after an invocation. Agent Evals expose those values through `observation.extensions.get(capabilityId)` and the `hasCapabilityExtension(capabilityId)` scorer. ## Driver support A model-backed Agent Driver can consume model-facing tools and Provider Tool contributions. A provider-backed Agent Driver receives Agent tools through the private MCP bridge and scoped Workspace behavior. Provider-backed Drivers do not support model-specific Capability Provider Tool contributions such as `webSearch({ mode: 'model' })`. A custom-run-backed Agent Driver receives prepared input and invocation context; the `driver.run` implementation decides which Capability outputs to read. Free-form guidance about when and why to use a Capability belongs in Agent Driver Instructions or deterministic imported instruction Markdown. Tool descriptions and schemas remain part of the model-facing tool contract. ## Next steps - [Official capabilities](https://vitehub.dev/docs/capabilities/official-capabilities) - [Custom capabilities](https://vitehub.dev/docs/capabilities/custom-capabilities) - [Agent definitions](https://vitehub.dev/docs/agents/agent-definitions) # Input commands `inputCommands()` adds command parsing for explicit user input before the main Agent Invocation runs. Use it for commands that transform or enrich the user's prompt, not for host UI state or shell execution. The Capability scans the latest prompt or user message for configured Input Commands. Each command can replace text, update the Agent Run Input, or add invocation context before model execution. Commands that produce model-facing text must return it. Accepting a command without handler text removes the matched text. ## Configure input commands Define lowercase command names. Add a description to include the command's purpose in CLI and inspection output. The default trigger is `/`. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { inputCommands } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model }, capabilities: [ inputCommands({ commands: { docs: { description: 'Add documentation context to the request.', call: ({ args }) => `Use documentation context for: ${args}`, }, }, }), ], }) ``` ## How input commands work `inputCommands()` runs during the input phase. It finds command invocations in the latest user text, calls the matching command handler, and updates the Agent Run Input before other model-facing behavior consumes it. Commands without a handler are accepted and removed from model input; they do not implicitly pass arguments or command names through as prompts. Command `agent:input` hooks run after the command updates the input and before the Agent Driver runs. Command `agent:finish` hooks run for completed and failed Agent Invocations. The Capability records command names and descriptions in metadata. It stops command expansion when no configured command remains. ## Requirements Command names must be lowercase stable identifiers. The trigger must be a non-empty string without whitespace. Input Commands are Capability concerns. Host Commands that change chat, session, UI, or product state belong outside this Capability. ## Driver support | Agent Driver | Support | | ----------------- | ----------------------------------------------------------------------------------------- | | Model-backed | Receives the transformed prompt, messages, or context before model execution. | | Provider-backed | Receives the transformed Agent Run Input before provider execution. | | Custom-run-backed | Receives the transformed Agent Run Input; `driver.run` decides how to use context values. | ## Verify input commands Run an invocation with the configured command text. Inspect the final Agent Run Input and confirm the command text was replaced or the expected context value was added before the Agent Driver ran. Check Agent inspection metadata for the `inputCommands` Capability and its command descriptions. ## Options | Option | Type | Default | Description | | ------------------------ | ----------------------------------------------------- | ------------------- | ----------------------------------------------------------------------------------------- | | `commands` | `Record` | required | Command map keyed by lowercase stable command names. | | `id` | `string` | `"inputCommands"` | Capability id. | | `trigger` | `string` | `"/"` | Non-whitespace command prefix. | | `commands.*.description` | `string` | none | Optional command description for metadata and inspection. | | `commands.*.call` | `(input) => AgentRunInput | Response | string | void` | remove command text | Handler that accepts, rejects, transforms, or enriches invocation input. | | `commands.*.channels` | `string[]` | all channels | Optional configured Channel ID allowlist. | | `commands.*.hooks` | `{ 'agent:input'?, 'agent:finish'? }` | none | Command-scoped lifecycle hooks with `ctx.message.reply/update/react` delivery primitives. | ## Related pages - [chatSummary()](https://vitehub.dev/docs/capabilities/chat-summary) - [Agent invocations](https://vitehub.dev/docs/agents/invocations) # KV `kv()` adds model-facing tools for a configured ViteHub KV primitive. It exposes read tools by default and edit tools only in write mode. The Capability contributes `kv_read` for exact-key reads or prefix key listing. When configured with write mode, it also contributes `kv_edit` for putting or deleting one key. ## Configure KV access Attach KV in read mode until the product needs model-facing writes. The KV primitive must already be configured by the app. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { kv } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model }, capabilities: [ kv({ mode: 'read' }), ], }) ``` ## How KV access works ViteHub selects the configured KV store and exposes the KV tools. Read mode supports one exact key or one prefix per tool call. Write mode adds a put/delete tool and allows its normal operations by default. ## Requirements `kv()` requires a configured `kv` primitive. Named store selection requires the KV primitive to expose store selection. Writes require explicit write mode. Set `policy: 'require-approval'` or `policy: 'deny'` when the product needs an additional gate. ## Driver support | Agent Driver | Support | | ----------------- | -------------------------------------------------------------------------------------------------- | | Model-backed | Receives `kv_read` and, in write mode, `kv_edit`. | | Provider-backed | Receives KV tools through the provider MCP bridge. | | Custom-run-backed | The configured primitive is available through runtime context; `driver.run` decides how to use it. | ## Verify KV access Run `vitehub agent info --agent --json` and inspect the resolved tool list. Confirm that read mode shows only `kv_read`. Write mode also lists `kv_edit` with the configured policy. Run one invocation against a missing KV primitive during development. Confirm that the Capability fails before it exposes tools. ## Options | Option | Type | Default | Description | | -------- | ------------------------------------ | ------------- | ------------------------------------------------------------------ | | `mode` | `"read" | "write"` | `"read"` | Adds `kv_edit` when set to `"write"`. | | `store` | `string` | default store | Selects a named KV store when the KV primitive supports `store()`. | | `policy` | `AgentToolPolicyDecision | function` | `"allow"` | Policy for `kv_edit`. | ## Related pages - [KV primitive](https://vitehub.dev/docs/server-primitives/kv) - [Official capabilities](https://vitehub.dev/docs/capabilities/official-capabilities) # LLM gate `llmGate()` adds a pre-invocation model decision that classifies a request into allow or reject categories. It can stop the Agent Invocation before the main Agent Driver runs. The Capability asks a model to choose one configured allow or reject category. It records the decision as an Agent Invocation Context Value, exposes it as a finish extension, and throws `ViteHubError` with code `LLM_GATE_REJECTED` when the selected category is rejected. ## Configure the gate Define allow and reject categories with stable keys. Set `message` to return a product-specific rejection message from the host. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { llmGate } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model }, capabilities: [ llmGate({ allow: { support: 'Support request the Agent can answer.', }, reject: { unrelated: 'Request unrelated to support.', }, }), ], }) ``` ## How the gate works `llmGate()` runs during the input phase. It resolves a model, renders a classifier prompt from the latest user text and configured categories, validates the structured output, and stores the decision in invocation context. When the decision rejects the request, ViteHub throws `ViteHubError` with code `LLM_GATE_REJECTED`; the HTTP handler maps that code to `403`. ## Requirements `llmGate()` requires at least one allow category and one reject category. Category keys must be stable identifiers. The Capability requires either an explicit model option or an Agent model resolver available to Capabilities. ## Driver support | Agent Driver | Support | | ----------------- | ------------------------------------------------------------------------------------------ | | Model-backed | Runs the pre-invocation gate before model execution. | | Provider-backed | Runs the pre-invocation gate before provider execution when a model resolver is available. | | Custom-run-backed | Runs before `driver.run`; rejected requests do not reach custom code. | ## Verify the gate Run one allowed and one rejected invocation. Inspect the context value for `llm-gate` or your custom id, then verify rejected requests stop with code `LLM_GATE_REJECTED`. Check that the gate does not attach, remove, or grant Capabilities. It records a decision; later behavior must read that decision explicitly. ## Options | Option | Type | Default | Description | | --------- | ------------------------ | ------------------------- | ------------------------------------------------------------- | | `allow` | `Record` | required | Allowed categories. | | `reject` | `Record` | required | Rejected categories. | | `history` | `boolean | number` | `false` | Include recent conversation history in the classifier prompt. | | `id` | `string` | `"llm-gate"` | Capability id and invocation context key. | | `message` | `string | function` | default rejection message | Error message when the gate rejects. | | `model` | `AgentModelResolver` | Agent model | Model used for the pre-invocation decision. | | `prompt` | `string` | generated | Additional classifier prompt text. | ## Related pages - [llmRoute()](https://vitehub.dev/docs/capabilities/llm-route) - [rateLimit()](https://vitehub.dev/docs/capabilities/rate-limit) # LLM route `llmRoute()` adds a pre-invocation model decision that chooses one developer-defined route. It records the chosen route as an Agent Invocation Context Value and does not apply route effects by itself. The Capability asks a model to select exactly one configured choice. It can include recent conversation history, records the decision under a stable id, and exposes the decision as a finish extension. ## Configure routing Define stable choice keys with short descriptions. Later callbacks can read the recorded context value and decide how to use the route. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { llmRoute } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model }, capabilities: [ llmRoute({ choices: { billing: 'Billing and invoice requests.', technical: 'Technical troubleshooting requests.', }, }), ], }) ``` ## How routing works `llmRoute()` runs during the input phase before the main Agent Driver. It resolves a model, renders a decision prompt from the latest user text and configured choices, validates the structured output, and stores the result in invocation context. The default context id is `llm-route`. Duplicate writers for the same invocation context value fail early. ## Requirements `llmRoute()` requires at least one choice. Choice keys must be stable identifiers. The Capability requires either an explicit model option or an Agent model resolver available to Capabilities. ## Driver support | Agent Driver | Support | | ----------------- | ---------------------------------------------------------------------------------------------- | | Model-backed | Runs the pre-invocation decision and records the route before model execution. | | Provider-backed | Runs the pre-invocation decision before provider execution when a model resolver is available. | | Custom-run-backed | Records the decision before `driver.run`; custom code decides how to use it. | ## Verify routing Run one invocation and inspect the context value for `llm-route` or your custom id. Confirm that the value includes `choice`. It may also include confidence or a reason. Add a test case for an invalid model response if you provide a custom model wrapper. Confirm that the Capability rejects choices outside the configured map. ## Options | Option | Type | Default | Description | | --------- | ------------------------------------------------------------------- | ------------- | ------------------------------------------------------------- | | `choices` | `Record` | required | Developer-defined route choices. | | `history` | `boolean | number` | `false` | Include recent conversation history in the classifier prompt. | | `id` | `string` | `"llm-route"` | Capability id and invocation context key. | | `model` | `AgentModelResolver` | Agent model | Model used for the pre-invocation decision. | | `prompt` | `string` | generated | Additional classifier prompt text. | ## Related pages - [llmGate()](https://vitehub.dev/docs/capabilities/llm-gate) - [Agent invocations](https://vitehub.dev/docs/agents/invocations) # MCP `mcp()` connects an Agent to external Model Context Protocol servers. It resolves each configured MCP Server and exposes its tools as model-facing Agent tools. The Capability normalizes MCP tool names with the server name, attaches sanitized MCP metadata, and closes MCP clients created from configs or resolvers after the invocation. Static direct clients stay application-owned so they can be reused across invocations. Tool names, descriptions, and schemas stay with the MCP tool contract. Put broader guidance about when to use an MCP server in Agent Driver Instructions. An optional approved fingerprint map can block added or changed tool definitions before they reach an Agent Driver. ## Configure MCP servers Pass a server map. Each entry can be a static direct MCP client borrowed from the application, or a client config or resolver whose resolved client is owned by the Agent Invocation. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { mcp } from 'vite-hub/agent/capabilities' import { docsMcpServer } from '../mcp/docs' export default defineAgent({ driver: { model }, capabilities: [ mcp({ servers: { docs: docsMcpServer, }, }), ], }) ``` ## How MCP connections work During resolution, `mcp()` connects to each configured MCP Server and asks for its tool set. ViteHub prefixes normalized tool names with `mcp__` and rejects duplicate normalized names. Pass a resolver or client config for an invocation-owned connection, or a static direct client when the application owns its lifetime. The Capability redacts secret-shaped metadata keys before exposing MCP metadata. ## Pin tool definitions An MCP Server can return a different tool description, title, or input schema after its tools were reviewed. Use `fingerprintTools()` during a trusted review step, persist the approved result in application code or configuration, then pass it to `integrity` under the matching server name. ```ts [scripts/review-docs-mcp.ts] import { fingerprintTools } from 'ai' const approved = await fingerprintTools(await client.tools()) console.log(JSON.stringify(approved, null, 2)) ``` Review that output before saving it. Do not generate the baseline during normal application startup, because that would trust whichever definitions the server returns first. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { mcp } from 'vite-hub/agent/capabilities' import { docsMcpServer } from '../mcp/docs' import { docsToolFingerprints } from '../mcp/docs-tool-fingerprints' export default defineAgent({ driver: { model }, capabilities: [ mcp({ integrity: { docs: docsToolFingerprints, }, servers: { docs: docsMcpServer, }, }), ], }) ``` ViteHub fingerprints each configured server independently before it normalizes or contributes tools. Added and changed definitions fail Capability resolution; removal-only changes remain allowed because MCP tool lists can narrow by feature or authorization. Drift errors include the server name and the added, changed, and removed original tool names. Fingerprints cover tool names, string descriptions, titles, and resolved input schemas. They do not prove that the first reviewed definition was safe or detect changed remote behavior behind an unchanged definition. ## Requirements `mcp({ servers })` requires a server map. Each configured entry must resolve to an MCP client or MCP client configuration. MCP client configuration uses the optional `@ai-sdk/mcp` runtime package when ViteHub creates the client from config. Tool integrity requires `ai` 7.0.19 or newer only when `integrity` is configured. The external MCP Server owns its own credentials, availability, and tool behavior. ## Driver support | Agent Driver | Support | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------ | | Model-backed | Receives normalized MCP tools. | | Provider-backed | Receives normalized MCP tools through the provider MCP bridge; runtime connection and cleanup still run around the invocation. | | Custom-run-backed | Receives prepared context; `driver.run` decides whether to call MCP clients or tools through custom code. | ## Verify MCP connections Successful invocations expose normalized MCP tools through `agent info` and stream tool steps through `agent dev`. Confirm that MCP tools use normalized names such as `mcp_docs_search`. Integrity checks run during invocation resolution. Static Agent inspection metadata does not connect to MCP Servers or claim that a configured baseline currently matches. Run one invocation with a duplicate normalized tool name during development. Confirm that the Capability fails before model execution. ## Options | Option | Type | Default | Description | | ----------- | ------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------- | | `integrity` | `Record` | none | Approved AI SDK tool fingerprints keyed by configured server name. Blocks added or changed definitions. | | `servers` | `Record` | required | MCP clients, client configs, or resolvers keyed by server name. | Cover MCP usage guidance in Agent Driver Instructions with explicit Capability coverage blocks. Keep MCP tool descriptions with the MCP Server because they are structured tool contracts. ## Related pages - [Official capabilities](https://vitehub.dev/docs/capabilities/official-capabilities) - [Custom capabilities](https://vitehub.dev/docs/capabilities/custom-capabilities) - [AI SDK MCP tool-definition drift](https://ai-sdk.dev/docs/ai-sdk-core/mcp-tools#detecting-tool-definition-drift-rug-pull){rel=""nofollow""} # Memory `memory()` adds scoped durable records that an Agent can search, read, remember, or delete through configured Memory Stores. Memory is explicit Agent behavior and is not the same as Chat History. The Capability exposes `memory_search` and `memory_read`, plus remember and delete tools when a store opts into tool writes. Each store owns its adapter, scope, allowed kinds, read behavior, and write policy. ## Configure memory Configure at least one store with an explicit scope. The workspace JSONL helper stores records inside the Agent Workspace. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { memory, workspaceJsonlMemoryStore } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model }, workspace, capabilities: [ memory({ stores: { agent: { adapter: workspaceJsonlMemoryStore(), scope: { agent: 'support' }, }, }, }), ], }) ``` ## How memory works During resolve, `memory()` creates read tools for stores that allow reading and write tools for stores that opt into tool writes. Write tools add provenance from the current Agent Invocation and allow writes by default after a store opts into `write.mode: 'tool'`. Set the store policy to `require-approval` or `deny` when writes need an additional gate. ## Requirements `memory()` requires a store map. Each store requires an adapter and an explicit non-empty scope. `workspaceJsonlMemoryStore()` requires a Workspace. It requires writable Workspace access when the Agent creates, supersedes, or deletes memory records. ## Driver support | Agent Driver | Support | | ----------------- | --------------------------------------------------------------------------------------------------------- | | Model-backed | Receives the configured memory tools. | | Provider-backed | Receives the configured memory tools through the provider MCP bridge. | | Custom-run-backed | Receives prepared context and can call store adapters from custom code when the application exposes them. | ## Verify memory Inspect Agent inspection metadata for the configured memory stores. Inspect the tool list and confirm write tools appear only for stores with `write.mode: 'tool'`. For the workspace JSONL store, inspect the configured Workspace file and verify records include scope and provenance. ## Options | Option | Type | Default | Description | | ---------------------------- | ----------------------------------------- | --------- | -------------------------------------------------- | | `stores` | `Record` | required | Named Memory Stores available to the Agent. | | `stores.*.adapter` | `MemoryStoreAdapter | MemoryStoreFactory` | required | Store implementation. | | `stores.*.scope` | `MemoryScope | function` | required | Scope attached to all operations for that store. | | `stores.*.allowKinds` | `MemoryKind[]` | all kinds | Allowed memory kinds for the store. | | `stores.*.read.tools.search` | `boolean` | `true` | Expose memory search. | | `stores.*.read.tools.read` | `boolean` | `true` | Expose exact memory read. | | `stores.*.write.mode` | `"off" | "tool"` | `"off"` | Expose remember/delete tools when set to `"tool"`. | | `stores.*.write.policy` | `AgentToolPolicyDecision` | `"allow"` | Policy for write tools. | ### Workspace JSONL store `workspaceJsonlMemoryStore()` persists records as append-only JSONL inside the Agent Workspace. | Option | Type | Default | Description | | ------ | -------- | ----------------------- | ----------------------------------- | | `path` | `string` | `"memory/memory.jsonl"` | Workspace-relative JSONL file path. | Cover Memory usage guidance in Agent Driver Instructions with explicit Capability coverage blocks. Keep memory tool descriptions with the tool definitions because they are structured tool contracts. ## Related pages - [chat()](https://vitehub.dev/docs/capabilities/chat) - [Workspace primitive](https://vitehub.dev/docs/server-primitives/workspace) # Official capabilities ViteHub exports its built-in Capabilities from `@vite-hub/agent/capabilities`. Choose a Capability by what the Agent needs to do. Each linked page shows how to configure it, what the Agent receives, and how to verify it. ```ts [server/agents/support.ts] import { access, blob, browser, chat, chatSummary, title, db, email, fetch, git, gmail, inputCommands, kv, llmGate, llmRoute, mcp, memory, openapi, otlp, papercuts, progressSummary, rateLimit, repositoryHost, repositoryHostContext, sandbox, schedule, skills, subagents, transcribe, usage, cost, diagnostics, webSearch, workspaceShell, } from 'vite-hub/agent/capabilities' ``` ## Catalog ### Invocation | Ability | Capability | Use it when | | ----------------- | ------------------------------------------------------------------------- | --------------------------------------------------------------------------- | | Invocation access | [`access()`](https://vitehub.dev/docs/capabilities/access) | Narrow chat admission or Workspace access from trusted invocation identity. | | Chat behavior | [`chat()`](https://vitehub.dev/docs/capabilities/chat) | Start Agent Invocations from chat messages and manage Chat History. | | Input commands | [`inputCommands()`](https://vitehub.dev/docs/capabilities/input-commands) | Transform command-shaped user input before the Agent runs. | | Subagents | [`subagents()`](https://vitehub.dev/docs/capabilities/subagents) | Delegate bounded work to named Agent Definitions through tools. | ### Workspace | Ability | Capability | Use it when | | ------------------ | --------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | | Browser automation | [`browser()`](https://vitehub.dev/docs/capabilities/browser) | A Provider Agent needs headless browser guidance and the `agent-browser` CLI is installed. | | Workspace files | [`workspaceShell()`](https://vitehub.dev/docs/capabilities/workspace-shell) | Inspect or edit Workspace files, or run configured Workspace commands. | | Git source history | [`git()`](https://vitehub.dev/docs/capabilities/git) | The Agent needs bounded Git source-history inspection or local Workspace Session git state selection. | | Skills file | [`skills()`](https://vitehub.dev/docs/capabilities/skills) | The Agent requires a Workspace skill file at invocation time. | | Durable memory | [`memory()`](https://vitehub.dev/docs/capabilities/memory) | The Agent needs scoped durable records across invocations. | ### Runtime primitives | Ability | Capability | Use it when | | ----------------------- | -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | | KV storage | [`kv()`](https://vitehub.dev/docs/capabilities/kv) | The Agent needs scoped key-value read or edit tools. | | Blob storage | [`blob()`](https://vitehub.dev/docs/capabilities/blob) | The Agent needs scoped object read or edit tools. | | Database | [`db()`](https://vitehub.dev/docs/capabilities/db) | The Agent needs guarded SQL query, schema, or mutation tools. | | Email | [`email()`](https://vitehub.dev/docs/capabilities/email) | Send authorized plain-text messages through the configured Email primitive. | | Sandbox execution | [`sandbox()`](https://vitehub.dev/docs/capabilities/sandbox) | The Agent may run an allowlisted executable in an isolated runtime. | | Schedules | [`schedule()`](https://vitehub.dev/docs/capabilities/schedule) | The Agent declares scheduled invocations or manages Runtime Schedules through tools. | | OTLP telemetry | [`otlp()`](https://vitehub.dev/docs/capabilities/otlp) | Live Agent Invocation events and completed traces should be exported to an OpenTelemetry receiver. | | Operational diagnostics | [`diagnostics()`](https://vitehub.dev/docs/capabilities/diagnostics) | Invocation outcomes and scoped runtime resource observations should go to an application-owned reporter. | ### External context | Ability | Capability | Use it when | | ----------------------- | ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | | Repository host | [`repositoryHost()`](https://vitehub.dev/docs/capabilities/repository-host) | The Agent needs provider-hosted repository, Change Request, issue, comment, check, or status data through a configured Repository Host client. | | Repository host context | [`repositoryHostContext()`](https://vitehub.dev/docs/capabilities/repository-host-context) | Read issue or Change Request data identified by a trigger or host. | | MCP servers | [`mcp()`](https://vitehub.dev/docs/capabilities/mcp) | Add tools from external MCP servers to the Agent. | | Web search | [`webSearch()`](https://vitehub.dev/docs/capabilities/web-search) | The Agent needs model web search or normalized web search/read tools. | | Fetch tools | [`fetch()`](https://vitehub.dev/docs/capabilities/fetch) | The Agent needs named HTTP tools for developer-approved endpoints. | | OpenAPI tools | [`openapi()`](https://vitehub.dev/docs/capabilities/openapi) | The Agent needs a selected OpenAPI operation catalog exposed as bounded HTTP tools or a generated Capability CLI. | | Transcription | [`transcribe()`](https://vitehub.dev/docs/capabilities/transcribe) | Turn audio input into text before model execution. | | Gmail | [`gmail()`](https://vitehub.dev/docs/capabilities/gmail) | Search Gmail or create unsent drafts through structured tools. | ### Decisions and output | Ability | Capability | Use it when | | ---------------- | ----------------------------------------------------------------------------- | --------------------------------------------------------------------------- | | LLM routing | [`llmRoute()`](https://vitehub.dev/docs/capabilities/llm-route) | Choose one developer-defined route with a model before the invocation. | | LLM gate | [`llmGate()`](https://vitehub.dev/docs/capabilities/llm-gate) | Allow or reject a request with a model before the invocation. | | Rate limit | [`rateLimit()`](https://vitehub.dev/docs/capabilities/rate-limit) | Consume a trusted invocation budget before the Agent runs. | | Title | [`title()`](https://vitehub.dev/docs/capabilities/title) | Generate a title for Agent output, finish extensions, or Channel threads. | | Chat summary | [`chatSummary()`](https://vitehub.dev/docs/capabilities/chat-summary) | Replace a summary command with a conversation summary. | | Progress summary | [`progressSummary()`](https://vitehub.dev/docs/capabilities/progress-summary) | Summarize current reasoning and tool activity while an Agent streams. | | Papercut reports | [`papercuts()`](https://vitehub.dev/docs/capabilities/papercuts) | Report small runtime or developer-experience problems to application code. | | Usage | [`usage()`](https://vitehub.dev/docs/capabilities/usage) | Request provider usage metadata and expose a normalized Agent Usage Record. | | Cost | [`cost()`](https://vitehub.dev/docs/capabilities/cost) | Add exact and display-ready USD cost to Agent Usage Records. | ## Next steps - [Custom capabilities](https://vitehub.dev/docs/capabilities/custom-capabilities) - [Capabilities API](https://vitehub.dev/docs/capabilities) - [Agent definitions](https://vitehub.dev/docs/agents/agent-definitions) # OpenAPI `openapi()` turns selected OpenAPI `operationId`s into bounded HTTP operations. Use it to generate Agent tools from a known API contract instead of writing one `fetch()` tool per endpoint. Attaching the Capability is the opt-in. Channel, customer, or tenant admission belongs in `access()`, Agent Trigger routing, or separate Agent Definitions, not in `openapi()`. ## Configure an OpenAPI Capability Add `openapi()` to the Agent when every invocation needs the API, or to one [Channel's capabilities](https://vitehub.dev/docs/agents/channels#scope-abilities-to-one-channel) when only that Channel needs it. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { access, openapi } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model }, capabilities: [ access({ chat: { resolve({ invoker }) { return invoker.kind === 'portal' }, }, }), openapi({ spec: 'https://portal.example.com/_openapi.json', operations: ['portalProductSearch', 'portalPurchaseOrders'], }), ], }) ``` By default, `openapi()` creates one model-facing tool per selected operation. Each tool uses the OpenAPI request path, query parameters, request body schema, operation summary, and response parser. When `cli` is set, ViteHub replaces those operation tools with one Capability CLI tool. The generated CLI has one subcommand per selected operation and can also run through `vitehub agent dev --cli`. ## Select operations Always pass the operation allowlist directly. ViteHub does not expose every operation by default. ```ts [server/agents/support.ts] openapi({ spec: 'https://billing.example.com/openapi.json', operations: ['billingListCustomers', 'billingGetInvoice'], }) ``` Unsupported HTTP methods are ignored unless the operation is selected. In v1, selected operations can use `GET`, `HEAD`, or `POST`. ## Configure requests ViteHub derives the request server from the OpenAPI document by default: 1. `servers[0].url` 2. the OpenAPI spec URL origin Use `hooks.request` for runtime auth, cookies, tenant values, body additions, query additions, and timeout changes. The hook receives the selected operation, Agent Capability context, visible model input, and a mutable draft request. When the hook owns OpenAPI fields such as tenant path params or runtime body tokens, declare them in `provides`; ViteHub removes those fields from the model and generated CLI schemas, strips them from caller input, then validates the final prepared request after the hook runs. ```ts [server/agents/support.ts] openapi({ spec: 'https://portal.example.com/_openapi.json', operations: ['portalProductSearch', 'portalPurchaseOrders'], hooks: { request: { provides: { body: ['cubeToken'], path: ['tenantId'], }, async handler({ context, operation, request }) { const session = context.get<{ cubeToken: string tenantId: string token: string }>('portalSession') if (!session) throw new Error('Portal session missing.') request.headers.set('authorization', `Bearer ${session.token}`) request.path.tenantId = session.tenantId if (operation.id === 'portalPurchaseOrders') { request.body = { ...(request.body as Record | undefined), cubeToken: session.cubeToken, } } }, }, }, }) ``` ViteHub still keeps caller-owned required fields in the model and generated CLI schemas. If the hook only provides `tenantId`, another required path param such as `orderId` remains required from the caller. For a broken, missing, or environment-neutral `servers` entry, use `server` as an override escape hatch. `server` can also be a callback when the override comes from the current Agent Invocation context. ```ts [server/agents/support.ts] openapi({ spec: './openapi.json', server: 'https://preview.example.com/api', operations: ['listCustomers'], }) ``` ## Generate a Capability CLI Use `cli` to give agents and developers the same operation catalog as commands. ```ts [server/agents/support.ts] openapi({ spec: 'https://billing.example.com/openapi.json', operations: ['billingListCustomers', 'billingGetInvoice'], cli: { name: 'billing', description: 'Inspect live billing API data.', }, }) ``` `cli` can also resolve from the current Agent Invocation. Return `false` or `undefined` to omit the generated CLI for that invocation without detaching the OpenAPI Capability or changing its lifecycle. ```ts [server/agents/support.ts] openapi({ spec: 'https://portal.example.com/_openapi.json', operations: ['portalProductSearch', 'portalPurchaseOrders'], cli: ({ run }) => run?.channelId === 'portal' ? { name: 'portal-api', description: 'Inspect live Portal data.', } : false, }) ``` Treat Channel metadata as an availability hint, not authorization. Check trusted request, Agent Actor, or `access()` evidence before privileged API calls. Run the generated CLI through the Agent Dev Loop. Agents expose generated Capability CLI Contributions by default. Use `defineAgent({ cli: { capabilities: false } })` to attach the OpenAPI Capability without exposing its CLI. ```bash [Terminal] pnpm vitehub agent dev --url http://localhost:3000 --agent support --cli billing -- list-customers --json ``` ## Shape responses Use `transformResponse` when the raw API response contains transport fields or verbose provider-specific rows. ```ts [server/agents/support.ts] openapi({ spec: 'https://billing.example.com/openapi.json', operations: ['billingGetInvoice'], transformResponse(response, { operation, response: http }) { return { operationId: operation.id, status: http.status, data: response, } }, }) ``` ## Driver support | Agent Driver | Support | | ----------------- | ------------------------------------------------------------------------------------------------------- | | Model-backed | Receives selected OpenAPI tools, or one generated Capability CLI tool when `cli` is set. | | Provider-backed | Receives selected OpenAPI tools, or the generated Capability CLI tool, through the provider MCP bridge. | | Custom-run-backed | Receives prepared context; `driver.run` decides whether to call API operations directly. | ## Options | Option | Type | Default | Description | | ------------------------ | ------------------------------------------------------- | -------------- | ----------------------------------------------------------------------------------------------------------- | | `spec` | `string | URL | object | function` | required | OpenAPI document URL, inline document, or invocation-scoped document resolver. | | `operations` | `readonly string[]` | required | Selected OpenAPI `operationId`s exposed by this Capability. | | `description` | `string` | none | Prefix for generated operation-tool descriptions and fallback description for the generated Capability CLI. | | `hooks.request` | `(context) => patch | void` or `{ provides?, handler }` | none | Fetch-style request preparation hook for runtime headers, cookies, path, query, body, and timeout values. | | `hooks.request.provides` | `{ body?, path?, query? }` | none | Runtime-owned OpenAPI input fields to remove from model and generated CLI schemas before caller validation. | | `server` | `string | URL | function` | OpenAPI server | Override escape hatch for specs without a usable `servers[0].url` or spec URL origin. | | `cli` | `false | { name, description? }` | `false` | Generates a Capability CLI instead of one model-facing tool per operation. | | `responseType` | `"json" | "text"` | `"json"` | Response parser for operation results. | | `transformResponse` | `(response, context) => output` | none | Maps parsed operation responses before returning them to the Agent. | | `specHeaders` | `Record` | none | Headers used only when fetching the OpenAPI document. | | `timeout` | `number` | none | Default request timeout in milliseconds. | ## Related pages - [Fetch](https://vitehub.dev/docs/capabilities/fetch) - [Access](https://vitehub.dev/docs/capabilities/access) - [Custom capabilities](https://vitehub.dev/docs/capabilities/custom-capabilities) - [CLI](https://vitehub.dev/docs/development/cli) # OTLP `otlp()` exports Agent Invocation telemetry using OTLP/HTTP JSON. It is a transport Capability, so the same Agent Definition works with any receiver that accepts ordinary OpenTelemetry logs and traces. ## Configuration Import the Capability from `@vite-hub/agent/capabilities` and pass the receiver's OTLP base endpoint. ViteHub appends the conventional `/v1/logs` and `/v1/traces` signal paths. ```ts [server/agents/support.ts] import { defineAgent } from '@vite-hub/agent' import { otlp } from '@vite-hub/agent/capabilities' export default defineAgent({ capabilities: [ otlp({ endpoint: process.env.OTLP_ENDPOINT!, headers: { authorization: `Bearer ${process.env.OTLP_TOKEN!}`, }, resource: { 'service.namespace': 'support', }, live: true, }), ], driver: { model: 'openai/gpt-5.1-mini' }, name: 'support', }) ``` ## Trace contract ViteHub always exports one completed trace. Its ordinary OTLP spans carry structural timing and `gen_ai.*` attributes. Without `live`, the root span also contains the invocation's Trace Events, including one `vitehub.agent.configured` event with sanitized Agent Definition metadata: Agent identity, Capability metadata, Driver and model identity when resolved, tool names, runtime, and Workspace name, mode, and Sources. With `live: true`, each Trace Event is exported once as a correlated OTLP LogRecord while the invocation runs. LogRecords use the same trace and span IDs as the completed trace and carry `agent.invocation.id` plus `vitehub.event.sequence` for deduplication. ViteHub batches for up to five seconds or 512 new Trace Events, whichever comes first, and flushes immediately when the invocation ends. After all records are delivered, the completed trace omits span events because those events were already sent as logs. If a live batch fails, the completed trace retains its span events so the terminal export does not lose that evidence. This is append-only export, not polling: ViteHub never resends an evolving in-progress span snapshot. OTLP's HTTP and gRPC exports are request/response protocols, so ViteHub does not add a receiver-specific SSE or WebSocket channel. The configuration event is not a user message. User prompts and model or tool content remain governed by trace content policy and are metadata-only in this exporter. Agent instructions are prompt content, so they are excluded by default. Opt in explicitly when the receiver is trusted: ```ts otlp({ endpoint: process.env.OTLP_ENDPOINT!, content: { inputs: true, instructions: true, outputs: true, }, }) ``` Secret-shaped Capability metadata keys are replaced with `[redacted]`. Keep receiver credentials in `headers`; `otlp()` does not expose its endpoint or headers in Agent metadata. ## Custom Capability metadata Every Capability contributes its public `metadata` automatically. A custom Capability can add invocation-resolved facts without knowing which exporter is installed: ```ts import { defineCapability } from '@vite-hub/agent' export const repository = defineCapability({ id: 'repository', metadata: { provider: 'github' }, async resolve(context) { const installation = await resolveInstallation(context) context.telemetry.metadata({ installation: installation.slug }) }, }) ``` This contribution belongs to the Capability's entry in the configuration event. It does not create another invocation stream or require a receiver-specific integration. ## Delivery behavior The exporter retries transient HTTP failures and honors `Retry-After`. Export is best effort and runs through the host's `waitUntil()` boundary; an unavailable receiver does not change Agent output. Receiver failures produce a bounded structured local error with the Capability ID, invocation ID, run ID, and export phase. Receivers should deduplicate spans by trace and span ID, and live records by `agent.invocation.id` plus `vitehub.event.sequence`, because a retry can repeat a request. Streaming command output remains trace activity, not a log drain. Provider command start, output deltas, and completion use the same tool-call ID so a session UI can group them without inspecting terminal escape sequences. ## Options | Option | Type | Default | Description | | ---------------------- | ------------------------------------ | -------------------------- | --------------------------------------------------------------------------------- | | `endpoint` | `string` | Required | Absolute OTLP base endpoint. ViteHub appends `/v1/logs` and `/v1/traces`. | | `headers` | `Record` or resolver | None | Request headers resolved for each export. | | `resource` | OTLP resource attributes or resolver | Agent and runtime defaults | Additional resource attributes. | | `content.inputs` | `boolean` | `false` | Includes user and tool input content in exported telemetry. | | `content.instructions` | `boolean` | `false` | Includes resolved Agent instructions in the configuration telemetry. | | `content.outputs` | `boolean` | `false` | Includes assistant, tool, and result output content in exported telemetry. | | `live` | `boolean` | `false` | Sends append-only Trace Events as correlated OTLP logs while the invocation runs. | # Papercuts `papercuts()` adds the `report_papercut` tool to an Agent. Use it to capture small, non-blocking friction while the details are still available to the current Agent Invocation. The Capability owns the reporting contract and provenance. Your application owns persistence, redaction, retention, deduplication, and triage. Provide a `report` callback that accepts each papercut before the tool reports success. The Capability always adds `report_papercut`. The tool accepts one trimmed message between 1 and 1000 characters and asks the Agent to describe what it was doing and what got in the way. Each report includes an id, creation time, source, and available Agent, run, and trace provenance. The callback also receives the current Capability runtime context for application-specific routing. ## Configure papercut reports Persist the normalized `papercut` record and use `context` only when the sink needs invocation-specific information. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { papercuts } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model }, capabilities: [ papercuts({ async report({ papercut, context }) { await savePapercut({ ...papercut, actorId: context.actor.id, }) }, }), ], }) ``` Do not serialize the runtime `context` wholesale. It is invocation-scoped and may contain request, actor, Workspace, or application values that do not belong in a papercut record; `workspace` and `fs` are absent on Agents without a Workspace. ## Add the Capability CLI Set `cli: true` to let agents and developers submit the same reports from a command. This adds the fixed `papercuts report` command without replacing `report_papercut`. ```ts [server/agents/support.ts] papercuts({ cli: true, report: ({ papercut }) => savePapercut(papercut), }) ``` Run the command through the Agent Dev Loop. ```bash [Terminal] pnpm vitehub agent dev --agent support --cli papercuts -- report "The retry hid the original error." ``` Successful command output is `Papercut reported.` ## How reports work ViteHub trims and validates the message, generates the record, and awaits `report`. The tool returns `{ reported: true, id }` only after the sink accepts the report; callback errors fail the tool call instead of returning a false success. The normalized record contains: | Field | Description | | ----------- | ------------------------------------------------------ | | `id` | Generated `papercut_` identifier. | | `createdAt` | ISO timestamp created when the report is submitted. | | `message` | Trimmed report text. | | `source` | `"tool"` or `"cli"`. | | `agent` | Agent identity when the host provides one. | | `run` | Agent Run metadata, including `runId`, when available. | | `trace` | Runtime Trace Context when available. | Attaching the Capability grants access to the developer-provided reporting sink, so `papercuts()` does not add an approval policy. It does not provide a `when` option. Attach the Capability only to Agent Definitions that report papercuts. ## Requirements `papercuts({ report })` requires a report callback. Resolve the callback only after its destination accepts the record. The tool description tells the Agent not to include secrets or customer data, but the sink still owns redaction and data handling appropriate to the application. ## Driver support | Agent Driver | Support | | ----------------- | -------------------------------------------------------------------------------------- | | Model-backed | Receives `report_papercut` and the optional `papercuts` Capability CLI tool. | | Provider-backed | Receives the same Capability tools through the Provider Agent tool bridge. | | Custom-run-backed | Receives resolved tools in the run context; `driver.run` decides whether to call them. | ## Verify reports Run one Agent Invocation that encounters a known problem and inspect the `report_papercut` call and normalized sink record. When `cli` is enabled, run `papercuts report` through the Agent Dev Loop and confirm the same sink receives a report with `source: "cli"`. ## Options | Option | Type | Default | Description | | -------- | ------------------------------------------------------ | -------- | ------------------------------------------------------------------------------------------ | | `report` | `(event: PapercutReportEvent) => void | Promise` | required | Application-owned sink for the normalized papercut and current Capability runtime context. | | `cli` | `boolean` | `false` | Adds the fixed `papercuts report` Capability CLI while keeping `report_papercut`. | ## Related pages - [Agent Evals](https://vitehub.dev/docs/agents/evals) - [Custom capabilities](https://vitehub.dev/docs/capabilities/custom-capabilities) # Progress summary `progressSummary()` observes reasoning and tool lifecycle events while an Agent works, then emits a replaceable one-sentence status as transient `data-progress-summary` stream data. ## Add progress summaries Import the Capability from `@vite-hub/agent/capabilities` and give it the independent Agent Driver that writes progress: ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { progressSummary } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: primaryDriver, capabilities: [ progressSummary({ driver: { kind: 'codex', model: 'gpt-5.6-luna', }, }), ], }) ``` The Capability owns the summarizer instructions. The configured Driver selects the model and execution environment using the same contract as an Agent Definition or `title()`. ## Render the current summary Listen for `data-progress-summary` parts and replace the currently displayed sentence when `revision` increases: ```ts { type: 'data-progress-summary', data: { type: 'progress-summary', summary: 'Checking current SKU costs against the planning data.', revision: 1, }, } ``` The part is transient, so it does not become conversation history. Keep structured reasoning and tool logs separate when your interface exposes them. With manual chat delivery, ViteHub edits the current placeholder as summaries arrive. When the Agent finishes, ViteHub deletes that placeholder and posts the final reply as a new message so chat platforms can deliver their normal notification. ## Understand the runtime behavior With event-driven `intervalMs: 0`, the Capability starts its initial summary when the first non-terminal primary stream chunk arrives. A terminal-only or failed stream does not start unused auxiliary work. Positive intervals begin when the first primary stream chunk arrives and use a fixed cadence from that point, so auxiliary generation never delays primary Driver startup. At most one generation runs at a time, and interval ticks are skipped while a generation is pending. Set `intervalMs: 0` to generate from reasoning and tool activity through the event-driven microtask behavior instead. Activity that arrives during an event-driven generation schedules one follow-up after it settles. Raw reasoning, tool input, and tool output are excluded from the generated prompt; reasoning is represented only as an `Active` presence signal. The default prompt uses only reasoning presence, sanitized tool names, and the previous summary. It does not include user message text, code, commands, paths, traces, hidden instructions, credentials, raw tool details, or trusted `` payloads. The Capability stops its cadence and aborts every in-flight generation when the parent invocation aborts or the primary stream finishes, cancels, or errors. A generation failure does not interrupt the primary response stream; ViteHub emits a sanitized warning and trace event without logging the Driver error message. ## Requirements The primary Agent Driver must expose a compatible async stream or UI message stream. With a positive interval, the Capability attempts a summary on every tick and suppresses unchanged output. With `intervalMs: 0`, it remains silent after the first-chunk summary until reasoning or tool lifecycle events provide new activity. Configure a `driver`, `model`, or `execute` option for summary generation. Without one of those options, the Capability uses the Agent model when available. ## Driver support | Agent Driver | Support | | ----------------- | ------------------------------------------------------------------------------- | | Model-backed | Uses an explicit summary model or the Agent model. | | Provider-backed | Uses an independent Agent Driver, including its model and environment settings. | | Custom-run-backed | Uses an independent custom Driver or the `execute` callback. | ## Verify the result Run an invocation and confirm that the primary stream continues immediately while an initial transient `data-progress-summary` part arrives. Keep the invocation open beyond `intervalMs`, then confirm that a later changed summary arrives with a higher `revision`. Stop the invocation before the next interval and confirm that no later progress part appears. ## Options | Option | Type | Default | Description | | -------------- | ------------------------------------------ | ----------------------------- | ----------------------------------------------------------- | | `driver` | `AgentDriver` | none | Independent Agent Driver used for progress generation. | | `execute` | `(input) => string | { summary?: string }` | none | Custom progress generator. | | `id` | `string` | `"progress-summary"` | Capability id. | | `instructions` | `string` | Capability-owned instructions | System instructions for model-backed generation. | | `intervalMs` | `number` | `10000` | Fixed generation cadence. Use `0` for event-driven updates. | | `maxLength` | `number` | `180` | Maximum summary length. | | `model` | `AgentModelResolver` | Agent model | Model used when no independent Driver is configured. | | `template` | `string | function` | generated | Markdown prompt template. | | `variables` | `Record` | none | Extra Markdown template variables. | String templates use `@vite-hub/markdown-template` and receive `userText`, the `reasoning` presence signal, `activeTools`, `completedTools`, and `previous`. Referencing `userText` opts a custom template into handling user message text; keep sensitive content out of prompts you construct. ## Related pages - [title()](https://vitehub.dev/docs/capabilities/title) - [Agent Drivers](https://vitehub.dev/docs/agents/agent-drivers) - [Markdown pages](https://vitehub.dev/docs/ai-resources/markdown-pages) # Rate limit `rateLimit()` consumes one budget unit before the main Agent Invocation starts. The Capability owns trusted Agent identity and rejection behavior, while the [Rate Limit primitive](https://vitehub.dev/docs/server-primitives/rate-limit) owns atomic enforcement. ## Configure a limiter Create a direct limiter beside the Agent and pass it to the Capability. The Capability needs the portable decision to attach Agent identity and rejection context, so the handler-only `requireRateLimit()` guard is not its input. The built-in memory driver is an owner-package API. Install that package when using it. ```bash [Terminal] pnpm add @vite-hub/rate-limit ``` ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { rateLimit } from 'vite-hub/agent/capabilities' import { createRateLimiter } from '@vite-hub/rate-limit' import { memoryRateLimitDriver } from '@vite-hub/rate-limit/drivers/memory' const invocations = createRateLimiter({ driver: memoryRateLimitDriver(), limit: 20, window: '1m', }) export default defineAgent({ driver: { model }, capabilities: [ rateLimit({ limiter: invocations, }), ], }) ``` ## How rate limits work The Capability runs during the input phase. It derives a stable key from the Capability id, scope, and trusted identity, then calls the `RateLimiter` exactly once. A rejected decision throws `ViteHubError` with code `RATE_LIMIT_REJECTED`; the HTTP handler maps that code to `429` and emits `retry-after` headers only when the selected driver reports `retryAfter`. Cloudflare native enforcement does not return portable quota metadata. The decision is stored under the Capability id in Agent Invocation Context and exposed as a finish extension. It contains the primitive decision plus `capabilityId`, `identity`, `identitySource`, `key`, and `scope`. ## Choose identity The Agent Definition chooses the identity, not the Rate Limit Driver. The default `identity: 'auto'` prefers the Agent Invoker, then Agent Run metadata, then trusted IP headers, and finally an anonymous identity. Use `identity: 'invoker'` when authentication provides a stable Agent Invoker. Use `identity: 'ip'` only after naming headers that the deployed host sets and sanitizes. ```ts [server/agents/public-support.ts] rateLimit({ identity: 'ip', limiter: invocations, trustedIpHeaders: ['cf-connecting-ip'], }) ``` Do not trust a client-controlled forwarding header. The Capability reads only the headers listed in `trustedIpHeaders`, but the application remains responsible for ensuring the host overwrites them. ## Use a custom driver Pass any `RateLimiter` when the application owns state or enforcement outside managed ViteHub Rate Limits. ```ts [server/rate-limiter.ts] import { createRateLimiter } from 'vite-hub/rate-limit' import type { RateLimitDriver } from 'vite-hub/rate-limit' declare const driver: RateLimitDriver export const invocationLimiter = createRateLimiter({ driver, enforcement: 'strict', limit: 20, window: '1m', }) ``` ```ts [server/agents/support.ts] import { rateLimit } from 'vite-hub/agent/capabilities' import { invocationLimiter } from '../rate-limiter' rateLimit({ limiter: invocationLimiter }) ``` The custom driver must implement atomic `consume()`. ViteHub does not provide a generic KV adapter because a portable `get()` followed by `set()` cannot guarantee an atomic decision under concurrency. ## Options | Option | Type | Default | Description | | ------------------ | ---------------------------------------------- | ------------------------- | --------------------------------------------------- | | `limiter` | `RateLimiter | function` | required | Direct limiter or runtime resolver. | | `id` | `string` | `"rate-limit"` | Capability id and Agent Invocation Context key. | | `identity` | `"auto" | "invoker" | "ip" | "run" | function` | `"auto"` | Identity used to derive the private rate-limit key. | | `scope` | `string | function` | Capability id | Additional key partition. | | `trustedIpHeaders` | `string[]` | none | Host-controlled headers allowed for IP identity. | | `message` | `string | function` | default rejection message | Error message for a rejected decision. | | `onDecision` | `function` | none | Callback after every decision. | | `onAllowed` | `function` | none | Callback after an allowed decision. | | `onRejected` | `function` | none | Callback after a rejected decision. | ## Migrate from an inline store The Capability no longer owns `limit`, `window`, `action`, or `store`. Move policy into a direct `RateLimiter`, then replace `store` with `limiter`. ```ts [Before] rateLimit({ limit: 20, store: 'memory', window: '1m', }) ``` ```ts [After] const invocations = createRateLimiter({ driver: memoryRateLimitDriver(), limit: 20, window: '1m', }) rateLimit({ limiter: invocations, }) ``` ## Verify it Run repeated Agent Invocations with the same identity. Confirm that the first `limit` invocations reach the Agent Driver and the next fails with code `RATE_LIMIT_REJECTED` before model, provider, or custom-run execution. For local tests, use a dedicated memory driver instance. For Cloudflare, resolve the request binding in the limiter resolver and test the deployed binding because the native decision depends on request-scoped Worker environment. ## Related - [Rate Limit primitive](https://vitehub.dev/docs/server-primitives/rate-limit) - [Agent invocations](https://vitehub.dev/docs/agents/invocations) - [Auth Users and Agent Invokers](https://vitehub.dev/docs/concepts/auth-users-and-agent-invokers) # Repository host `repositoryHost()` gives an Agent a provider-neutral Repository Host Capability. Use it when the Agent needs GitHub, GitLab, Bitbucket, or another repository host through a configured client. Provide a client directly or configure a `repository-host` primitive. The Capability adds `repository_host_read` in read mode. In write mode it also adds `repository_host_write` for comments and reactions through the configured Repository Host client. ## Configure repository tools ```ts [server/agents/reviewer.ts] import { defineAgent } from 'vite-hub/agent' import { repositoryHost } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model }, capabilities: [ repositoryHost({ client: githubRepositoryHostClient, mode: 'read', provider: 'github', }), ], }) ``` ## How repository tools work `repository_host_read` accepts normalized operations for repositories, Change Requests, Change Request files, issues, comments, checks, and statuses. Operations that target a single Change Request, issue, comment, check, or status require `target.id`. `repository_host_write` requires write mode and a client with `write()`. Comment writes require a `body`; all writes require a target id. Supported writes are allowed by default after the developer enables write mode. Set an explicit policy when comments or reactions need approval or must be denied at runtime. ## Requirements When `client` is omitted, ViteHub requires a configured `repository-host` primitive. The client must expose `read()`, and write mode also requires `write()` before the write tool can succeed. ## Driver support | Agent Driver | Support | | ----------------- | ------------------------------------------------------------------------------------------------ | | Model-backed | Receives `repository_host_read`, plus `repository_host_write` in write mode. | | Provider-backed | Receives Repository Host tools through the provider MCP bridge. | | Custom-run-backed | Can use the configured client directly through runtime context if the runner owns that behavior. | ## Options | Option | Type | Default | Description | | ---------- | -------------------------------------------- | --------------- | ----------------------------------------------------- | | `client` | `RepositoryHostClient | function` | primitive | Provider client with `read()` and optional `write()`. | | `mode` | `"read" | "write"` | `"read"` | Adds `repository_host_write` when set to `"write"`. | | `policy` | `AgentToolPolicyDecision | function` | `"allow"` | Policy for `repository_host_write`. | | `provider` | `"github" | "gitlab" | "bitbucket" | string` | client provider | Provider metadata for inspection. | ## Verify repository tools Run `vitehub agent info --agent --json` and inspect the resolved tool list. Read one repository or Change Request through `repository_host_read`, then verify read mode exposes no write tool. When using an explicit approval policy, verify that posting comments or reactions requests approval. ## Related pages - [Custom capabilities](https://vitehub.dev/docs/capabilities/custom-capabilities) - [Agent triggers](https://vitehub.dev/docs/agents/triggers) # Repository host context `repositoryHostContext()` records repository-host context for one Agent Invocation. Use it when a trigger, webhook, or host identifies the current issue or Change Request and runtime code needs its provider data on demand. Provide a Repository Host client directly, or configure a `repository-host` primitive for the invocation. The Capability stores an async record in Agent Invocation Context under `repositoryHost` by default. The record exposes `keys()`, `has(key)`, `get(key)`, `pick(keys)`, `entries(keys?)`, and `resolveAll()`. Each key loads only when caller code requests it. ViteHub caches in-flight and successful key loads for the current record, so repeated `get('comments')` calls reuse the same request. The default keys are `issue`, `pullRequest`, `body`, `labels`, `comments`, and `files`. Known keys that do not apply return `undefined`. Unknown keys throw an error. ## Configure repository context ```ts [server/agents/reviewer.ts] import { defineAgent } from 'vite-hub/agent' import { repositoryHostContext } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model }, capabilities: [ repositoryHostContext({ client: githubRepositoryHostClient, materialize: './PULL_REQUEST.template.md', target: { repo: 'acme/app', number: 42, }, }), ], }) ``` ## Read context Read the async record from invocation context when hooks, custom runners, or host code need repository-host data. The caller owns presentation and decides whether to render Markdown, JSON, or another format. ```ts [server/agents/reviewer.ts] import { repositoryHostContext } from 'vite-hub/agent/capabilities' const host = repositoryHostContext.read(ctx) const keys = await host.keys() const issue = await host.get('issue') const pullRequest = await host.get('pullRequest') const comments = await host.get('comments') const labels = await host.get('labels') ``` Use `resolveAll()` when code needs a plain object with every available key. The async record is not a JSON container, and `JSON.stringify(host)` throws instead of silently resolving async values. ## Target resolution For GitHub, `repositoryHostContext()` reads the issue shape first. When the issue includes pull request metadata, the record also exposes Change Request data through `pullRequest` and `files`. ```ts [server/agents/reviewer.ts] repositoryHostContext({ client: githubRepositoryHostClient, target: { repo: 'acme/app', number: 42 }, }) repositoryHostContext({ client: githubRepositoryHostClient, target: { repo: 'acme/app', issue: 42 }, }) repositoryHostContext({ client: githubRepositoryHostClient, target: { repo: 'acme/app', pullRequest: 42 }, }) ``` V1 supports GitHub issues and pull requests. Node ids, discussions, actions, and non-GitHub providers are not part of this context record yet. ## How context is loaded `repositoryHostContext()` keeps context data-only unless `materialize` is configured. With `materialize: './PULL_REQUEST.template.md'`, ViteHub bundles the colocated Markdown renderer and writes the resolved context to `PULL_REQUEST.md` in the Agent Workspace. The generated path preserves directories and case while removing only the final `.template`. Use `repositoryHost()` separately when a model-backed Agent needs repository-host tools such as `repository_host_read`. Use `repositoryHostContext()` when trusted runtime code needs a typed invocation context value. ## Requirements Static context does not require a Repository Host client. Target-based context requires a client option or a configured `repository-host` primitive. ## Driver support | Agent Driver | Support | | ----------------- | ------------------------------------------------------------------------------------------------------------------------ | | Model-backed | Does not receive rendered context automatically. Caller code must add selected values to instructions, input, or a tool. | | Provider-backed | Receives the configured materialized Markdown file in its Agent Workspace. | | Custom-run-backed | Can read the async record directly through `repositoryHostContext.read(ctx)`. | ## Options | Option | Type | Default | Description | | ------------- | ---------------------------------------- | --------------------------- | ------------------------------------------------------------------------------------------------- | | `client` | `RepositoryHostClient | function` | primitive | Provider client with `read()`. | | `context` | `RepositoryHostContextInput | function` | invocation context | Static issue, Change Request, or selected key values. | | `contextKey` | `string` | `"repositoryHost"` | Agent Invocation Context key used to store the async record. | | `id` | `string` | `"repository-host-context"` | Capability id. | | `materialize` | relative `*.template.md` path | none | Renders resolved context into the matching Workspace `.md` path. | | `provider` | `"github" | string` | client provider | Provider guard. V1 accepts GitHub targets. | | `target` | `RepositoryHostContextTarget | function` | none | Repository host target such as `{ repo, number }`, `{ repo, issue }`, or `{ repo, pullRequest }`. | | `triggers` | `Record` | none | Trigger contributions tied to this context. | ## Verify repository context Call `keys()` to inspect which values are available for the target. Call `resolveAll()` in tests when you need to assert the full resolved shape. ## Related pages - [Repository host](https://vitehub.dev/docs/capabilities/repository-host) - [Agent invocations](https://vitehub.dev/docs/agents/invocations) # Sandbox `sandbox()` owns sandbox execution for Agents. Use `commands` only when the Agent needs a model-facing allowlist of executable names. With `commands`, the Capability contributes `sandbox_exec`. The tool accepts one configured executable name, optional args, cwd, environment, and timeout, then delegates execution to the Sandbox primitive. ## Choose sandbox commands Pass executable names, not shell command strings. The Capability rejects names that are not in the allowlist. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { sandbox } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model }, workspace, capabilities: [ sandbox({ commands: ['node', 'pnpm'] }), ], }) ``` ## How sandbox sessions work ViteHub validates the command allowlist before the Capability attaches. At invocation time, `sandbox_exec` checks the requested executable against the allowlist and calls the configured Sandbox primitive. ## Requirements `sandbox({ commands })` requires an explicit Workspace and a configured `sandbox` primitive. The `commands` option must contain at least one executable name when present. Sandbox is not Workspace Shell. Use `workspaceShell()` for Workspace inspection and structured Workspace mutation. ## Driver support | Agent Driver | Support | | ----------------- | ---------------------------------------------------------------------------------------------------- | | Model-backed | Receives `sandbox_exec`. | | Provider-backed | Receives `sandbox_exec` through the private MCP bridge when `commands` are configured. | | Custom-run-backed | The Sandbox primitive is available through runtime context; `driver.run` decides whether to call it. | ## Verify the sandbox Run `vitehub agent info --agent --json` and confirm `sandbox_exec` lists only the allowed executables. Run a disallowed command during development and verify ViteHub rejects it before the Sandbox primitive executes. ## Options | Option | Type | Default | Description | | ---------- | ---------- | -------- | ------------------------------------------------------------------------------------------- | | `commands` | `string[]` | required | Allowlisted executable names. Pass at least one executable name, not shell command strings. | ## Related pages - [Sandbox primitive](https://vitehub.dev/docs/server-primitives/sandbox) - [workspaceShell()](https://vitehub.dev/docs/capabilities/workspace-shell) # Schedule `schedule()` covers two schedule-related Agent abilities. It can declare fixed Agent Schedules as Capability metadata, or it can expose one `cronjob` tool for Runtime Schedules when configured with a mode. Static Agent Schedule mode records one or more five-field UTC cron expressions on the Capability. Runtime Schedule mode contributes one `cronjob` tool. Read mode supports `targets`, `list`, and `get`. Write mode also supports `create`, `edit`, `pause`, `resume`, `run`, and `delete`. ## Configure schedules Use static schedules to run the Agent on known cron entries. ViteHub derives a stable id from the cron expression when you do not provide one. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { schedule } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model }, capabilities: [ schedule({ schedules: ['0 9 * * 1'], }), ], }) ``` To let an Agent create recurring turns for itself, enable self-targeting. ViteHub derives the target from the discovered Agent name and stores the prompt with a metadata-free copy of the resolved invoker identity. Every run passes through the Agent's normal Capability policies and `agent:input` hooks again. When `invoker.resolve` is configured, ViteHub also reruns it and continues only when the resolved `id` and `kind` still match. Without a resolver, ViteHub restores the durable identity; it does not perform external authentication automatically. ```ts [server/agents/mini.ts] import { defineAgent } from 'vite-hub/agent' import { schedule } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model }, capabilities: [ schedule({ allowSelfTarget: true, delivery: 'origin', mode: 'write', timeZone: 'Asia/Bangkok', }), ], }) ``` The Agent can now create a cron job with a `prompt`, such as a daily report. Only an invocation with the same resolved invoker `id` and `kind` can inspect or manage that scheduled turn. `delivery: 'origin'` sends the result back to the channel thread where the schedule was created. Creating that schedule fails when the invocation has no deliverable channel thread. ## How schedules work Static schedules add metadata that framework integrations and schedule-aware runtime behavior can inspect. Runtime Schedule mode reads visible Runtime Schedules and can create, edit, pause, resume, run, or delete scoped schedules when write mode is enabled. The `cronjob` tool accepts an optional IANA `timeZone` on create and edit, while schedules without one continue to use UTC. A configured `timeZone` provides the default for new schedules; edits change it only when the tool supplies a new value. ## Requirements Static schedules require at least one five-field UTC cron expression. Runtime Schedule mode requires a configured `schedule` primitive. Runtime Schedule edits require explicit write mode and are allowed by default. Self-targeting requires explicit self-target permission. Runtime Schedules still require a provider wake or a long-running Schedule runner to execute when due. Set `policy: 'require-approval'` or `policy: 'deny'` when mutations need an additional gate; read operations remain allowed. ## Driver support | Agent Driver | Support | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------- | | Model-backed | Receives the Runtime Schedule tool when mode is configured; static schedules are runtime metadata. | | Provider-backed | Receives the Runtime Schedule tool through the provider MCP bridge when mode is configured; static schedules remain runtime metadata. | | Custom-run-backed | Receives prepared metadata and context; `driver.run` decides how to use schedule context. | ## Verify schedules Inspect Capability metadata for static schedule ids and cron expressions. For Runtime Schedule mode, inspect the tool list and verify it contains only `cronjob` for scheduling. Its schema exposes only read operations in read mode. Run a schedule with a six-field cron expression during development. Confirm that the Capability rejects it before the Agent starts. ## Options | Option | Type | Default | Description | | ----------------- | ----------------------------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------ | | `schedules` | `Array` | required for Agent Schedules | Declares fixed five-field UTC Agent Schedules. | | `mode` | `"read" | "write"` | required for Runtime Schedule tools | Selects read or write Runtime Schedule tools. | | `targets` | `string[]` | all visible targets | Allowlist of Runtime Schedule target names. | | `allowSelfTarget` | `boolean` | `false` | Lets the Agent create scheduled turns for itself. ViteHub derives the target from the discovered Agent name. | | `delivery` | `"origin"` | none | Delivers a scheduled Agent turn to the channel thread where it was created. Requires `allowSelfTarget: true`. | | `timeZone` | `string` | none (UTC fallback) | Default IANA time zone for Runtime Schedules created by the tool. New schedules use it before falling back to UTC. | | `policy` | `AgentToolPolicyDecision | function` | `"allow"` | Policy for mutating `cronjob` operations. Read operations remain allowed. | ## Related pages - [Schedule primitive](https://vitehub.dev/docs/server-primitives/schedule) - [Agent triggers](https://vitehub.dev/docs/agents/triggers) # Skills `skills()` makes a Workspace or external Source Skill available to an Agent Invocation. For an Agent-owned Skill, use a folder Agent Definition whose entry file is named `agent.ts`, `agent.js`, `index.ts`, or `index.js`, including their `c` and `m` variants, then place `skills/` beside that entry file. ViteHub discovers and materializes those files automatically, so local Skills do not need a Capability declaration. Flat files such as `server/agents/support.ts` do not discover sibling Skills. The Capability records the configured Skill path in metadata and requires the Workspace path to exist. When `shellExecution` is set, model-backed Agents receive the normal Workspace Shell tools in the requested mode. When `source` is set, ViteHub adds that source to the Agent Workspace at definition time and mounts it at the skill path. Model-facing guidance for a Skill belongs in Agent Driver Instructions or deterministic imported instruction Markdown. Agent inspection metadata warns when `skills()` makes a Skill available but no explicit instruction coverage names it. ## Configure a Skill The default path is `skills/SKILL.md`. Pass a custom path when the Workspace stores the skill somewhere else. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { skills } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model }, workspace, capabilities: [ skills(), ], }) ``` Mount a remote skill source when the skill file lives outside the local project: ```ts [server/agents/review/browser.ts] import { defineAgent } from 'vite-hub/agent' import { skills } from 'vite-hub/agent/capabilities' import { github } from 'vite-hub/workspace' export default defineAgent({ driver: { model }, workspace: { name: 'review', mode: 'write' }, capabilities: [ skills({ path: 'skills/agent-browser', source: github({ repo: 'vercel/vercel-plugin', root: 'skills/agent-browser', include: ['SKILL.md', 'references/**', 'templates/**'], materialize: 'build', }), shellExecution: 'write', }), ], }) ``` ## How Skills are loaded ViteHub validates the Workspace read requirement before the Agent Driver runs. The Capability metadata includes the directory path and the resolved `SKILL.md` path. Model-facing Skill guidance belongs in Agent Driver Instructions or deterministic imported instruction Markdown with an explicit `::skill{path="..."}` coverage block. For provider-backed drivers, `skills()` contributes the skill directory to the Provider Workspace session instead of adding model-facing instructions or tools. With `shellExecution: 'write'`, model-backed Workspace Shell writes commit Workspace Session changes back into the Workspace. With `source`, ViteHub still uses normal Workspace Source materialization, visibility, and Agent inspection metadata. `skills()` does not fetch source files at invocation time. ## Requirements Workspace-scoped `skills()` requires an explicit Workspace with read access to the configured skill file. The path can point to a directory or directly to a `SKILL.md` file. When `source` is configured, `path` is the canonical mount. ViteHub mounts the source at the configured skill directory even when the source helper has its own default mount. File Sources are single-file and root-confined. Use a directory-capable `github()` or `custom()` Source, or a root-confined `glob()` Source, when a Skill source is a directory. ## Driver support | Agent Driver | Support | | ----------------- | --------------------------------------------------------------------------------------------------------------------- | | Model-backed | Validates the skill file requirement and can read the mounted Skill through Workspace tools when tools are available. | | Provider-backed | Mounts Skills into the Provider Workspace session. | | Custom-run-backed | Validates the skill file requirement before `driver.run`. | ## Verify the Skill Run the Agent with the configured Workspace. Confirm that a missing skill file fails before model execution with a Workspace path requirement error. Inspect Capability metadata for the normalized `path` and `skillPath` values. Agent inspection metadata warns when a configured Skill lacks explicit instruction coverage. The warning clears when Agent Driver Instructions or a deterministic imported instruction file covers the Skill. ## Options | Option | Type | Default | Description | | ---------------- | ---------------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | | `path` | `string` | `"skills"` | Directory or `SKILL.md` path required in the Workspace. | | `shellExecution` | `"read" | "write"` | none | Optional Workspace Shell mode for model-backed Agents. Provider-backed Agents still receive the skill files, not Workspace Shell tools. | | `source` | `WorkspaceSourceInput` | none | Workspace Source to mount at the skill directory. | | `sourceKey` | `string` | derived from `path` | Workspace source key used when `source` is configured. | Cover Skill usage guidance in Agent Driver Instructions with explicit Skill coverage blocks. Keep tool descriptions with Workspace Shell tools because they are structured tool contracts. ## Related pages - [Workspace primitive](https://vitehub.dev/docs/server-primitives/workspace) - [Agent instructions](https://vitehub.dev/docs/agents/instructions) # Subagents `subagents()` exposes one tool per configured child Agent Definition. Use it to delegate bounded work without giving the model a generic agent runner. Name each subagent with a lowercase identifier and give it a concrete description. The Capability adds model-facing tools such as `run_researcher` or an explicit `toolName`. Each tool starts the configured child Agent with a message, optional structured context, optional call options, and an inherited invoker. ## Add subagents ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { subagents } from 'vite-hub/agent/capabilities' import researcher from './researcher' export default defineAgent({ driver: { model }, capabilities: [ subagents({ agents: { researcher: { agent: researcher, description: 'Research one narrow question and return sourced notes.', }, }, }), ], }) ``` ## How delegation works `subagents()` validates the configured names and tool names before the Agent runs. At invocation time, each subagent tool starts a fresh child Agent Invocation with the parent runtime context, then awaits the existing serializable Agent result. The model cannot select or reuse the child invocation id. ## Requirements Each subagent entry requires an `agent` and a non-empty `description`. Subagent keys must be lowercase stable identifiers, and generated tool names use underscores instead of dashes. ## Driver support | Agent Driver | Support | | ----------------- | -------------------------------------------------------------------------------------------------- | | Model-backed | Receives one model-facing tool per subagent. | | Provider-backed | Receives one tool for each configured subagent through the provider MCP bridge. | | Custom-run-backed | Can inspect the configured tools and invoke child Agents directly if the custom runner chooses to. | ## Options | Option | Type | Default | Description | | ---------------------- | ------------------------------------ | ------------- | --------------------------------------------------------------- | | `agents` | `Record` | required | Named child Agent Definitions exposed as tools. | | `id` | `string` | `"subagents"` | Capability id and instruction context key. | | `agents.*.agent` | `AgentInput` | required | Child Agent Definition or Agent input accepted by `runAgent()`. | | `agents.*.description` | `string` | required | Tool description shown to the model. | | `agents.*.toolName` | `string` | `run_` | Explicit model-facing tool name. | Cover delegation guidance in Agent Driver Instructions with explicit Capability coverage blocks. Keep each subagent's description with `agents.*.description` because it is the model-facing tool contract. ## Verify delegation Run `vitehub agent info --agent --json` and confirm each subagent appears as a tool with the expected name and description. Run one delegated task and verify the child invocation id and inherited invoker metadata. Use [`startAgentInvocation()`](https://vitehub.dev/docs/agents/controlled-child-invocations) from trusted code when the caller needs to inspect or cancel the child after start. ## Related pages - [Agent definitions](https://vitehub.dev/docs/agents/agent-definitions) - [Agent invocations](https://vitehub.dev/docs/agents/invocations) # Title `title()` generates a short title for an Agent Invocation. It can use a model, a custom executor, or a local heuristic, then streams or returns the title as output metadata and optionally delivers it to a Channel thread. The Capability reads the prepared first user message, generates a short title, provides it as a finish extension, and injects title data into compatible streams. When that message has no semantic text, such as an attachment-only audio or image message, it waits for the successful Agent reply and uses that text instead. Applications can use the finish extension to name a job, run, artifact, or other durable record without depending on a chat interface. It can limit title generation to selected Agent Trigger ids. ## Configure titles Attach `title()` to any Agent Definition that needs a generated title. When no model is available to the Capability, ViteHub falls back to a short heuristic title. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { title } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model }, capabilities: [ title(), ], }) ``` ## How titles are generated `title()` runs in the output phase. It wraps compatible async streams or UI message streams so the title can arrive alongside the response, and it provides `{ title }` in the finish extension. Input Capabilities run first, so `transcribe()` can replace audio with transcript text before `title()` reads it. Audio with authored text uses both in their prepared order. If the prepared input is still empty, `title()` waits for the normalized Agent reply; when that reply is also empty or the invocation fails, it leaves the title unset. For framework-managed Chat SDK message Channels, ViteHub also delivers the title once per thread by default, even when each webhook contains only the current message or the handler is recreated. Follow-up Channel invocations skip title generation after successful delivery. Set `channelDelivery: "always"` to refresh the platform title on every invocation. Plain `runAgent()` and UI invocations without framework-managed Chat SDK delivery still receive stream data and finish extensions per invocation. The Capability avoids wrapping the same result twice. ## Requirements `title()` needs message-shaped input with at least one user message and semantic text from either the prepared input or a successful Agent reply. A model, custom executor, or heuristic path must be available. Use a custom template, variables, or executor when the title must include product-specific context. ## Driver support | Agent Driver | Support | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | Model-backed | Can use the Agent model or an explicit model to generate the title and decorate streams. | | Provider-backed | Can decorate compatible output streams when the invocation produces them; model-based title generation still needs a model resolver. | | Custom-run-backed | Can decorate compatible custom output; custom `driver.run` controls the response shape. | ## Verify titles Run one Agent Invocation and inspect the stream for title data. Confirm that the finish extension includes `{ title }` when title generation succeeds. Test a vague first message and confirm the fallback title is used instead of an empty string. ## Options | Option | Type | Default | Description | | ----------------- | ---------------------------------------- | ------------------------------------ | --------------------------------------------------------------------------------------------------------- | | `channelDelivery` | `"once-per-thread" | "always"` | `"once-per-thread"` | Deliver framework-managed Chat SDK Channel titles once per thread, or on every invocation. | | `driver` | `AgentDriver` | none | Agent Driver used only for title generation. | | `execute` | `(input) => string | { title?: string }` | none | Custom title generator. `input.source` is `"input"` or `"response"`. | | `fallback` | `string` | `"Untitled"` | Title used when generation returns no usable text. | | `id` | `string` | `"title"` | Capability id. | | `instructions` | `string` | none | System instructions for model-backed title generation. | | `maxLength` | `number` | `80` | Maximum title length. | | `model` | `AgentModelResolver` | Agent model, then heuristic fallback | Model used for title generation. | | `template` | `string | function` | generated | Prompt template for model-backed generation. String templates can use `{{ message }}` and `{{ source }}`. | | `trigger` | `string | string[]` | all triggers | Limit title generation to selected Agent Trigger ids. | | `variables` | `Record` | none | Extra template variables. | | `when` | `(input) => boolean` | none | Predicate that decides whether title generation runs. | ## Related pages - [chat()](https://vitehub.dev/docs/capabilities/chat) - [chatSummary()](https://vitehub.dev/docs/capabilities/chat-summary) # Transcribe `transcribe()` is an input-phase Official Capability for audio. It turns audio message parts into transcript text before the Agent Driver receives the final input. The Capability finds audio parts in input messages, transcribes them, appends transcript text to the message, and records transcription results in invocation context. It can also persist transcript and source-audio artifacts into a writable Workspace. ## Configure transcription Provide either an AI SDK transcription model configuration or an `execute()` function. The example keeps artifacts off, so it does not require a writable Workspace. ```ts [server/agents/voice.ts] import { defineAgent } from 'vite-hub/agent' import { transcribe } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model }, capabilities: [ transcribe({ model: transcriptionModel, }), ], }) ``` ### OpenRouter Use `openRouterTranscriptionModel()` to keep OpenRouter authentication, audio encoding, request shape, and provider errors behind the AI SDK transcription model interface. ```ts [server/agents/voice.ts] import { defineAgent } from 'vite-hub/agent' import { openRouterTranscriptionModel, transcribe, } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model }, capabilities: [ transcribe({ model: openRouterTranscriptionModel({ apiKey: () => env.OPENROUTER_API_KEY, model: 'openai/gpt-4o-transcribe', }), }), ], }) ``` The adapter uses OpenRouter's base64 JSON request with `response_format: 'json'`. This avoids provider-specific multipart handling in Agent Definitions and does not request `verbose_json`, which OpenRouter only supports for some upstream providers. AI SDK `providerOptions.openrouter` values for `language`, `temperature`, and `provider` routing are forwarded to OpenRouter. Unsupported OpenRouter transcription options fail explicitly instead of being silently ignored. ## How transcription works `transcribe()` runs before model execution. It enforces the configured maximum audio size, resolves audio data from direct data, `fetchData`, or URL, and replaces the consumed audio parts with transcript text in the user message. When artifacts are enabled, it writes sanitized transcript and optional audio files to the Agent's writable Workspace and exposes results as a finish extension. Use `artifacts.directory` to keep the transcript and source audio together. The generated paths share a sanitized timestamp/message stem. ```ts transcribe({ model: transcriptionModel, artifacts: { directory: 'inputs/voice-notes', transcript: { format: 'markdown' }, }, }) ``` ## Streaming transcription Use `streamTranscription()` for live raw audio. It wraps AI SDK streaming transcription and exposes `textStream`, an append-only text stream that can be passed directly to `event.reply()`. ```ts import type { AgentFinishHookEvent } from 'vite-hub/agent' import { streamTranscription } from 'vite-hub/agent/capabilities' export async function liveTranscriptReply( event: AgentFinishHookEvent, audio: ReadableStream, ) { const transcription = await streamTranscription({ model: 'openai/gpt-realtime-whisper', audio, inputAudioFormat: { type: 'audio/pcm', rate: 24_000, }, }) return event.reply(transcription.textStream) } ``` Return the reply intent without awaiting `transcription.text`; consuming the reply drives the provider stream and resolves the final text promise. Chat-backed Channels use native streaming when the adapter supports it and Chat SDK's post-and-edit fallback otherwise. Other message Channels use their native stream method when available and fall back to one final reply. `streamTranscription()` emits provider `transcript-delta` events as reply chunks. Providers that only emit partial and final snapshots produce one final reply, which avoids duplicating corrected partial text. ## Asynchronous remote transcription Use `createTranscription()` to submit a private remote object from a durable workflow and resume after the provider completes it. The client returns the provider operation ID used for acknowledgement and idempotency, then normalizes an authenticated completion payload into a provider-neutral transcript or failure. ```ts import { createTranscription, elevenLabsScribe, } from 'vite-hub/agent/capabilities' const transcription = createTranscription({ driver: elevenLabsScribe({ apiKey: () => env.ELEVENLABS_API_KEY, diarize: true, tagAudioEvents: true, timestampsGranularity: 'word', webhookId: env.ELEVENLABS_WEBHOOK_ID, }), }) const submission = await transcription.submit({ metadata: { attemptId, jobId }, source: { url: signedAudioUrl }, }) const completion = await transcription.receive(authenticatedProviderPayload) if (completion.status === 'failed') { console.error(completion.error.code, completion.error.message) } ``` `submit()` never downloads the remote object into the application process. Use a signed HTTPS URL for private Blob objects, with an expiry long enough for the provider to fetch it. The caller still owns callback authentication before `receive()`, durable operation state, duplicate-delivery handling, timeouts, and workflow resumption. Compose those concerns with the Workflow primitive; correlation metadata is untrusted until it matches the stored workflow attempt. Provider callback payloads and SDK types do not cross the transcription client interface. Failed completions contain a `ViteHubError` with a fixed `TRANSCRIPTION_*` code and message. Raw provider diagnostics stay behind the in-memory `cause` and are omitted when the completion is serialized. ## Requirements Basic transcription requires a model or custom executor. Artifact persistence requires an explicit writable Workspace. Streaming transcription requires an AI SDK streaming transcription model, a `ReadableStream` of raw audio, and its input audio format. Asynchronous remote transcription requires a `TranscriptionDriver`. The built-in ElevenLabs Scribe driver requires an API key and an explicitly configured speech-to-text webhook ID. Audio data must stay within `maxBytes`. Artifact paths must stay inside the Workspace and cannot target reserved `.git` or `.vitehub` paths. ## Driver support | Agent Driver | Support | | ----------------- | --------------------------------------------------------------------------------------- | | Model-backed | Receives text-enriched messages after transcription. | | Provider-backed | Receives text-enriched Agent Run Input before provider execution. | | Custom-run-backed | Receives text-enriched Agent Run Input and can read transcription results from context. | Asynchronous transcription is independent of the Agent Driver because the caller composes its submitted operation and completion result into a durable Workflow. ## Verify transcription Run an invocation with one audio part and inspect the final message text. Confirm that the transcript appears before the Agent Driver runs. When artifacts are enabled, inspect the Workspace for transcript files and the finish extension for transcription metadata. ## Options | Option | Type | Default | Description | | -------------------------------- | ---------------------------- | -------------------------------- | --------------------------------------------------------------- | | `model` | AI SDK transcription model | required unless `execute` is set | Model used by AI SDK transcription. | | `execute` | `(input) => string | result` | none | Custom transcription function; mutually exclusive with `model`. | | `maxBytes` | `number` | `26214400` | Maximum accepted audio bytes. | | `artifacts.directory` | `string | function` | generated | Directory for generated transcript and audio artifacts. | | `artifacts.transcript` | `false | object` | disabled | Persist transcript artifacts to Workspace. | | `artifacts.transcript.format` | `"text" | "markdown"` | `"text"` | Default transcript artifact body and generated extension. | | `artifacts.transcript.path` | `string | function` | generated | Transcript artifact path. | | `artifacts.transcript.mediaType` | `string | function` | inferred | Transcript artifact media type. | | `artifacts.transcript.template` | `function` | default text | Custom transcript artifact body. | | `artifacts.audio` | `boolean | object` | disabled | Persist source audio artifacts to Workspace. | | `artifacts.audio.path` | `string | function` | generated | Audio artifact path. | | `artifacts.audio.mediaType` | `string | function` | audio media type | Audio artifact media type. | ### Asynchronous client | Interface | Result | Description | | -------------------------------------------------- | ------------------------------- | --------------------------------------------------------------------------------------- | | `createTranscription({ driver })` | `TranscriptionClient` | Creates a provider-neutral asynchronous client. | | `client.submit({ source, metadata, abortSignal })` | `submitted` operation | Submits a remote HTTP(S) source and returns the provider operation ID. | | `client.receive(payload)` | `completed | failed` completion | Normalizes an already-authenticated provider completion payload. | | `elevenLabsScribe(options)` | `TranscriptionDriver` | Maps remote Scribe v2 submission and callback payloads without exposing provider types. | `openRouterTranscriptionModel({ apiKey, model })` returns an AI SDK transcription model for synchronous `transcribe({ model })` usage. Without `artifacts.directory`, transcripts use `transcripts//.txt` and audio is placed beside the transcript. If transcripts are disabled, audio uses `audio//.`. ## Public helpers Import these helpers from `@vite-hub/agent/capabilities` when custom hooks or executors need the same normalized data as the Capability. | Helper | Return value | Behavior | | ---------------------------------- | --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | `audioBytes(audio, { maxBytes? })` | `Promise` | Resolves direct data, `fetchData`, or an audio URL and enforces a `26214400` byte default limit. | | `getTranscriptionResults(context)` | `TranscriptionResult[]` | Reads the current invocation's results from an invocation context store or an object containing one. Returns an empty array when none exist. | | `streamTranscription(options)` | `Promise` | Starts AI SDK streaming transcription and exposes `textStream` for streamed replies, `text` for the final transcript, and the underlying `result` metadata. | Each `TranscriptionResult` contains `createdAt`, `date`, `messageId`, `stem`, and `transcript`, plus `audioPath` or `transcriptPath` when those artifacts were written. ## Related pages - [AI Gateway streaming transcription](https://vercel.com/changelog/ai-gateway-now-supports-streaming-transcription){rel=""nofollow""} - [Workspace primitive](https://vitehub.dev/docs/server-primitives/workspace) - [Agent invocations](https://vitehub.dev/docs/agents/invocations) # Usage Add `usage()` to request the provider's complete usage metadata and expose ViteHub's normalized Agent Usage Record as a typed Finish Extension. ## Add usage ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { usage } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model: 'anthropic/claude-sonnet-4.5' }, capabilities: [usage()], }) ``` For OpenRouter calls, the Capability sets `providerOptions.openrouter.usage.include` to `true`. Existing provider options and OpenRouter usage settings are preserved. ## Read the record The Capability's typed `usage` Finish Extension returns the same normalized record available at `event.invocation.usage`. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { usage } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model: 'anthropic/claude-sonnet-4.5' }, capabilities: [usage()], hooks: { 'agent:finish'(event) { console.log(event.invocation.usage) console.log(event.extensions.get('usage')) }, }, }) ``` The record can contain normalized token usage, model and transport metadata, provider-reported cost, and other provider usage metadata. Fields remain optional when the provider does not report them. ## Verify it Invoke the Agent through OpenRouter and inspect the provider call settings and Finish Event. Confirm that `usage.include` is enabled without replacing existing provider options, and that the `usage` Finish Extension matches `event.invocation.usage`. ## Related - [Cost capability](https://vitehub.dev/docs/capabilities/cost) - [Agent Invocations](https://vitehub.dev/docs/concepts/agent-invocations) - [Runtime events](https://vitehub.dev/docs/reference/runtime-events) # Web search `webSearch()` gives an Agent access to web context through an explicit mode. Use model mode for provider-native web search, or tool mode for normalized `web_search` and `web_read` tools. Model mode contributes a provider tool request for `web_search`. Tool mode contributes `web_search` and `web_read` model-facing tools backed by a single configured search provider. ## Configure web search Use model mode when the selected model provider supports provider-native web search. Tool mode requires a web search provider configuration. ```ts [server/agents/research.ts] import { defineAgent } from 'vite-hub/agent' import { webSearch } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model }, capabilities: [ webSearch({ mode: 'model' }), ], }) ``` ## How web search works Model mode adds a Provider Tool contribution and leaves search execution to the model provider. Tool mode loads the configured provider, executes normalized search requests, and reads URLs as Markdown or text. ## Requirements `webSearch()` requires `mode: 'model'` or `mode: 'tool'`. Model mode requires an Agent Driver and model provider that support the provider tool. Tool mode requires the application to install `askweb` and configure one web search provider with any required credentials. ## Driver support | Agent Driver | Support | | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Model-backed | Receives the provider tool in model mode or `web_search` and `web_read` in tool mode. | | Provider-backed | Receives `web_search` and `web_read` through the provider MCP bridge in tool mode. Model mode is unsupported because Provider Agent Drivers do not accept Provider Tool contributions. | | Custom-run-backed | Receives prepared context; `driver.run` decides how to perform web access. | ## Verify web search Inspect provider tool contributions for model mode or the Agent tool list for tool mode. Confirm that tool mode exposes `web_search` and `web_read`. Run tool mode without `askweb` during development. Confirm that the Capability reports the missing package and suggests model mode instead. ## Options | Option | Type | Default | Description | | ------------------ | ----------------------------------------------------------------------------------- | --------------------- | --------------------------------------------------------------------------- | | `mode` | `"model" | "tool"` | required | Uses provider-native model web search or ViteHub-managed search/read tools. | | `provider` | `WebSearchProviderInput` | required in tool mode | Provider name or provider options for tool mode. | | `provider.name` | `"brave" | "exa" | "jina" | "searxng" | "serpapi" | "serpbase" | "tavily" | string` | required | Tool-mode web search provider. | | `provider.apiKey` | `string | unsealer | function` | environment | Credential for providers that require one. | | `provider.baseURL` | `string` | provider default | Override provider endpoint. | ## Credentials Tool mode resolves credentials in this order: `provider.apiKey`, `VITEHUB__API_KEY`, then `_API_KEY`. ViteHub uppercases the provider name and replaces non-alphanumeric characters with underscores, so `my-search` uses `VITEHUB_MY_SEARCH_API_KEY` before `MY_SEARCH_API_KEY`. ## Tool inputs Tool mode rejects properties outside these public input contracts. ### `web_search` | Input | Type | Default | Description | | ---------------- | ---------- | ---------------- | ----------------------------------- | | `query` | `string` | required | Non-empty search query. | | `includeDomains` | `string[]` | provider default | Restrict results to these domains. | | `excludeDomains` | `string[]` | provider default | Exclude these domains from results. | | `maxResults` | `number` | provider default | Maximum number of search results. | ### `web_read` | Input | Type | Default | Description | | ----------- | -------- | -------------- | -------------------------------- | | `url` | `string` | required | Non-empty page URL to read. | | `maxTokens` | `number` | reader default | Maximum normalized content size. | ## Related pages - [Official capabilities](https://vitehub.dev/docs/capabilities/official-capabilities) - [fetch()](https://vitehub.dev/docs/capabilities/fetch) # Workspace shell `workspaceShell()` adds Workspace inspection tools in read mode, structured mutation tools in write mode, and an optional `workspace_exec` tool for explicitly configured executables. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { workspaceShell } from 'vite-hub/agent/capabilities' export default defineAgent({ driver: { model: 'openai/gpt-5.1-mini' }, workspace: { mode: 'write' }, capabilities: [workspaceShell()], }) ``` Provider Drivers can also expose configured commands in write mode: ```ts [server/agents/coder.ts] export default defineAgent({ driver: { kind: 'codex' }, workspace: { mode: 'write' }, capabilities: [workspaceShell({ commands: ['git'], mode: 'write', timeout: 30_000 })], }) ``` Command entries accept executable names or absolute paths, never shell command strings. Use `commands: 'all'` only on a trusted host because it permits any executable reachable by the Workspace Session. Successful command changes commit through Workspace rules. ViteHub validates Workspace requirements before resolving tools. Configured commands require a writable Workspace because every command opens a Workspace Session. Workspace Sources, rules, and Actor Scope bound visible and committed paths, but they do not isolate host side effects outside the Workspace. Provider Drivers already receive their materialized Workspace as the working directory, so ViteHub avoids duplicate file tools there. Configured `workspace_exec` commands still reach Provider Drivers through the private MCP bridge. | Option | Type | Default | Description | | ---------- | ------------------ | -------- | ------------------------------------------------------------------- | | `mode` | `"read" | "write"` | `"read"` | Selects inspection tools or write-capable Workspace tools. | | `commands` | `string[] | "all"` | - | Adds a provider-Driver executable allowlist in explicit write mode. | | `timeout` | `number` | `60000` | Default command timeout in milliseconds. | Use [`sandbox()`](https://vitehub.dev/docs/capabilities/sandbox) when a model-backed Agent needs an allowlisted executable. Provider Drivers use their native command tools inside the materialized working directory. ## Related pages - [Workspace context](https://vitehub.dev/docs/agents/workspace-context) - [Workspace primitive](https://vitehub.dev/docs/server-primitives/workspace) - [sandbox()](https://vitehub.dev/docs/capabilities/sandbox) # Agent Invocations An Agent Invocation is one request to run an Agent. It includes the input, trusted caller, selected Capabilities, available context, execution method, and result. An Agent Definition describes reusable behavior. An Invocation records one execution of that behavior. ## What happens during an Invocation | Stage | What happens | | ------------ | ------------------------------------------------------------------------------------- | | Entry | A route, Channel, schedule, webhook, CLI command, or another caller provides input. | | Identity | ViteHub identifies the trusted Agent Invoker and Actor. | | Capabilities | The Definition and invocation context select the abilities available to this request. | | Context | ViteHub prepares tools, policy, context values, and Workspace Scope. | | Execution | The Agent Driver processes the prepared request. | | Result | ViteHub returns or streams output and records events and usage. | The Agent can use only the Capabilities selected for that Invocation. A Capability that isn't selected contributes nothing to the request. ## Don't confuse an Invocation with related records | Term | Describes | | ---------------- | --------------------------------------------------------------- | | Agent Definition | Reusable Agent behavior. | | Agent Invocation | One execution for one input. | | Channel | Message origin and delivery facts around an invocation. | | Workflow Run | Durable work that can continue across waits or server restarts. | | Agent Memory | Persistent context stored outside the Invocation. | A Channel can start many Invocations, and a Workflow Run can carry an Invocation. Neither one replaces the request record. ## Inspect an invocation Inspect the input, invoker, Channel, Capabilities, Workspace Scope, Driver, events, usage, and output together. Run `vitehub agent info` to inspect the Agent definition and `vitehub agent dev` to stream a local Invocation. Read [Invocations](https://vitehub.dev/docs/agents/invocations) for `runAgent()` and `streamAgent()`, or [Runtime policy, approvals, and traces](https://vitehub.dev/docs/concepts/runtime-policy-approvals-and-traces) for the records produced during execution. # Auth Users and Agent Invokers An Auth User is the person signed in to your application. An Agent Invoker is the trusted caller of one Agent Invocation. Agent and Capability code read that caller from `context.invoker`. Auth answers "who is signed in?" An Agent Invoker answers "who or what started this invocation?" ## Choose the identity you need | | Auth User | Agent Invoker | | -------- | ---------------------------------------- | ------------------------------------------- | | Scope | The application session | One Agent Invocation | | Source | An auth provider | A trusted entry point or Auth bridge | | Used by | Routes and application authorization | Agent and Capability code | | Required | Only where the application requires auth | Every invocation, including anonymous calls | ## Auth can provide the invoker An Agent can also start from a Channel, schedule, webhook, service account, CLI command, or local development. Those entry points don't need an Auth User, but they still provide an Agent Invoker. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { authenticated } from 'vite-hub/auth/agent' export default defineAgent({ invoker: authenticated(), driver: { run: ({ invoker }) => ({ invoker }), }, }) ``` `authenticated()` maps the signed-in user to the Agent Invoker. Configuring Auth alone does not require a user session for every Agent. ## The invoker carries trusted caller data | Field | Meaning | | ------- | --------------------------------------------------------------- | | `id` | Stable caller ID for the invocation. | | `kind` | Caller type such as `authUser`, `chat`, or `anonymous`. | | `label` | Optional label for inspection. | | `meta` | Structured application data used by Capabilities and callbacks. | Don't put secrets or raw session payloads in `meta`. Read [Auth](https://vitehub.dev/docs/server-primitives/auth) for session setup and [Access](https://vitehub.dev/docs/capabilities/access) for decisions based on invoker identity. # Bash Bash is the tool an Agent uses to run commands contributed by its Capabilities. Each call selects a registered executable and passes its arguments through a Workspace Session. Bash is not an unrestricted host shell. The Agent Definition and its selected Capabilities determine which commands exist for the invocation. ## Many commands share one tool A browser Capability and a deployment Capability can each contribute a command. ViteHub exposes both through one `bash` tool, but each Capability still defines its own arguments and behavior. Use Bash when an Agent needs to combine commands. Use a structured tool when an operation needs typed input and output or a separate policy for each action. Read [Workspace Shell](https://vitehub.dev/docs/capabilities/workspace-shell) for the Capability API and [Shell](https://vitehub.dev/docs/server-primitives/shell) for the application-facing execution primitive. # Capabilities A Capability gives an Agent a selected ability. It can add tools, instructions, requirements, policy, triggers, metadata, or invocation context. Installing a Server Primitive does not give an Agent access to it. Attach a Capability when the Agent needs that operation. ## Choose the API by its caller | | Server Primitive | Capability | | ------------ | -------------------------- | ------------------------------------------------ | | Caller | Application server code | An Agent during an Invocation | | Access | A documented server import | Selected tools, policy, requirements, or context | | Selection | Application code calls it | The Agent Definition or invocation selects it | | Model access | None | Only the operations the Capability contributes | ## Select the abilities an Agent can use ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' import { kv, workspaceShell } from 'vite-hub/agent/capabilities' export default defineAgent({ workspace: { mode: 'read' }, capabilities: [ workspaceShell({ mode: 'read' }), kv({ mode: 'read' }), ], driver: { run: ({ input }) => input, }, }) ``` The Agent receives only what the selected Capabilities contribute. Application code can call the same Server Primitives through their server APIs. ## Use a fixed or resolved list Use an array when every invocation needs the same abilities. Use a resolver when trusted invocation data changes the list: ```ts capabilities: ({ actor }) => [ customerRecords, ...(actor.meta?.support === true ? [internalDiagnostics] : []), ], ``` ViteHub resolves the Agent Invoker before it calls the resolver. The returned array is the complete Capability list for that request. ## Limit what the Agent can use A Capability does not expose the full Runtime Context or unrestricted host access. Its tools, requirements, policy, and metadata define what the Agent can inspect and use. Read the [Capabilities](https://vitehub.dev/docs/capabilities) section for implementation details and [First agent](https://vitehub.dev/docs/getting-started/first-agent) for a runnable Definition. # Channels A Channel records where a message came from and how to reply. It carries the message, sender, attachments, reply target, delivery facts, and host commands around an Agent Invocation. A Channel handles message transport. An Agent Invocation runs the Agent for one input. One Agent Definition can run behind several Channel adapters because these records stay separate. ## Channel and Invocation have different jobs | | Channel | Agent Invocation | | --------------- | -------------------------------------------------------------- | ------------------------------------------------------------------ | | Describes | Message origin and delivery | Identity, Capabilities, execution, and result | | Lifetime | Can contain many messages and Invocations | One request | | Can exist alone | Yes, a Channel can receive a message without starting an Agent | Yes, a route or schedule can start an Invocation without a Channel | ## Message and command facts stay separate | Term | Carries | | ---------------- | -------------------------------------------------------------------- | | Message | Content, sender, attachments, reply target, and Channel metadata. | | Command | A host action such as stop, retry, or inspect. | | Agent Invocation | The resolved identity, Capabilities, context, execution, and result. | | Chat Session | The message history selected for an invocation. | A message can start an Invocation. A host command can affect a session or a running Invocation without becoming model input. ## Treat Channel metadata as input from the adapter Use verified Channel metadata to identify the Agent Invoker, choose a Capability, or select a Workspace Scope. Inspect the Channel and Invocation together when a message reaches the wrong Agent, carries the wrong identity, or loses delivery data. Read [Channels](https://vitehub.dev/docs/agents/channels), [Chat history and sessions](https://vitehub.dev/docs/agents/chat-history-sessions), and [Agent Invocations](https://vitehub.dev/docs/concepts/agent-invocations) for the operational APIs. # Definition discovery A Definition is a file that declares named behavior or state, such as an Agent, Queue, or Workspace. Discovery finds the file and assigns the name that application code uses. ## The file location supplies the name ViteHub derives a Definition's name from its file path. Each package defines which paths it scans. For example: ```txt server/agents/support.ts -> support server/agents/docs/agent.ts -> docs src/triager.agent.ts -> triager ``` Each package documents its paths and file suffixes. Put the Definition in one of those paths instead of adding another name in application code. ## The integration finds the file The package integration scans the project during development and build. It prepares the routes, imports, bindings, or metadata that the package needs. ```ts [server/agents/support.ts] import { defineAgent } from 'vite-hub/agent' export default defineAgent({ driver: { run: () => 'ok', }, }) ``` The Definition stays in application code. Generated files show what the integration prepared, but application code doesn't import them unless the package documents that import. ## Check the discovered result If a Definition is missing or has the wrong name, check its location, the package integration, and the generated metadata. Read [Agent Definitions](https://vitehub.dev/docs/agents/agent-definitions) for Agent-specific files and [Vite Integrations and Provider Output](https://vitehub.dev/docs/concepts/vite-integrations-and-provider-output) for the build side. # Concepts ViteHub adds server features to Vite applications and lets Agents use selected features through Capabilities. These pages explain the terms that connect those two paths. For setup and API options, go to [Server primitives](https://vitehub.dev/docs/server-primitives), [Agents](https://vitehub.dev/docs/agents), [Capabilities](https://vitehub.dev/docs/capabilities), or [Reference](https://vitehub.dev/docs/reference). ## Start here | Page | Read it when | | ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | | [Server primitives](https://vitehub.dev/docs/concepts/server-primitives-for-any-host) | You need storage, background work, auth, isolated execution, or another server feature. | | [Agents](https://vitehub.dev/docs/agents) | You need a named actor that runs with a model, coding provider, or application code. | ## Core vocabulary | Page | Defines | | ------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------- | | [Definition discovery](https://vitehub.dev/docs/concepts/definitions-and-discovery) | How ViteHub finds and names a definition file. | | [Agent Invocations](https://vitehub.dev/docs/concepts/agent-invocations) | What ViteHub resolves and records for one Agent request. | | [Capabilities](https://vitehub.dev/docs/concepts/capabilities-api) | How an Agent receives a selected ability. | | [Workspace and Sources](https://vitehub.dev/docs/concepts/workspace-and-sources) | How a writable file tree differs from the read-only content mounted into it. | | [Auth Users and Agent Invokers](https://vitehub.dev/docs/concepts/auth-users-and-agent-invokers) | How application identity becomes trusted invocation identity. | | [Channels](https://vitehub.dev/docs/concepts/channels-api) | How messages, delivery facts, and host commands reach an Agent. | | [Bash](https://vitehub.dev/docs/concepts/bash) | How Capability-provided commands appear as one Agent tool. | ## Runtime execution | Page | Defines | | -------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | | [Runtime Context](https://vitehub.dev/docs/concepts/runtime-context) | The host resources available to a server operation or Agent request. | | [Runtime Helpers and stable imports](https://vitehub.dev/docs/concepts/runtime-helpers-and-stable-imports) | The imports application code uses to call ViteHub. | | [Runtime policy, approvals, and traces](https://vitehub.dev/docs/concepts/runtime-policy-approvals-and-traces) | The records that explain whether work ran, waited, or failed. | ## Host and build model | Page | Defines | | ---------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | | [Vite Integrations and Provider Output](https://vitehub.dev/docs/concepts/vite-integrations-and-provider-output) | How ViteHub prepares a package for development and deployment. | ## Next steps Open [Installation](https://vitehub.dev/docs/getting-started/installation) for a runnable project. If you already know what you need, go to [Server primitives](https://vitehub.dev/docs/server-primitives) or [Agents](https://vitehub.dev/docs/agents). # Runtime Context Runtime Context contains the trusted host resources available to one ViteHub operation. It can include platform resources, request data, provider bindings, background work, memoized values, and trace information. Invocation input describes the task. Runtime Context provides the host resources needed to run it. ## Runtime Context comes from the host Framework and provider integrations create Runtime Context in the generated route or handler. A custom server call passes the same values itself because `runAgent()` does not read framework globals. | Value | Used for | | ------------------------ | ------------------------------------------------------- | | `runtime` and `platform` | Identifying the active runtime and platform. | | `memo` | Resolve one value once during the current execution. | | `waitUntil` | Continue background work after the handler returns. | | Provider context | Reading trusted host resources such as bindings. | | `trace` and `traceLog` | Keeping trace identity and recording structured events. | Keep reusable behavior in Definitions and task data in invocation input. Runtime Context passes host resources to the operation. ## Adapt an H3 event Application-owned routes need one host adapter for the Runtime Context used by the Agent examples: ```ts [server/runtime-context.ts] import type { AgentRuntimeName, AgentWaitUntil } from 'vite-hub/agent' import type { H3Event } from 'h3' function waitUntilFrom(value: unknown): AgentWaitUntil | undefined { const owner = value as { waitUntil?: AgentWaitUntil } | undefined return typeof owner?.waitUntil === 'function' ? owner.waitUntil.bind(value) : undefined } function waitUntilFor(event: H3Event): AgentWaitUntil { const context = event.context as { cloudflare?: { context?: unknown } _platform?: { cloudflare?: { context?: unknown } } } const node = event.node as { req?: { runtime?: { cloudflare?: { context?: unknown } } } } const req = event.req as { runtime?: { cloudflare?: { context?: unknown } } } return waitUntilFrom(event) ?? waitUntilFrom(context) ?? waitUntilFrom(context.cloudflare?.context) ?? waitUntilFrom(context._platform?.cloudflare?.context) ?? waitUntilFrom(req?.runtime?.cloudflare?.context) ?? waitUntilFrom(node?.req?.runtime?.cloudflare?.context) ?? (task => { void Promise.resolve(task).catch(error => console.error(error)) }) } function cloudflareFor(event: H3Event) { const runtimeEvent = event as H3Event & { env?: Record context: H3Event['context'] & { cloudflare?: { context?: unknown, env?: Record } _platform?: { cloudflare?: { context?: unknown, env?: Record } } } req?: { runtime?: { cloudflare?: { context?: unknown, env?: Record } } } node?: { req?: { runtime?: { cloudflare?: { context?: unknown, env?: Record } } } } } const env = runtimeEvent.env ?? runtimeEvent.context.cloudflare?.env ?? runtimeEvent.context._platform?.cloudflare?.env ?? runtimeEvent.req?.runtime?.cloudflare?.env ?? runtimeEvent.node?.req?.runtime?.cloudflare?.env const context = runtimeEvent.context.cloudflare?.context ?? runtimeEvent.context._platform?.cloudflare?.context ?? runtimeEvent.req?.runtime?.cloudflare?.context ?? runtimeEvent.node?.req?.runtime?.cloudflare?.context return env ? { env, ...(context ? { context } : {}) } : undefined } function runtimeFor(event: H3Event): AgentRuntimeName { const env = typeof process === 'object' && process ? process.env : undefined if (cloudflareFor(event)) return 'cloudflare-agents' if ('Deno' in globalThis) return 'deno' if (env?.VERCEL) return 'vercel' return env?.NODE_ENV === 'development' ? 'vite' : 'unknown' } export function getRuntimeContext(event: H3Event) { const cloudflare = cloudflareFor(event) const values = new Map() return { ...(cloudflare ? { cloudflare } : {}), memo(key: string, create: () => T): T { if (!values.has(key)) values.set(key, create()) return values.get(key) as T }, runtime: runtimeFor(event), waitUntil: waitUntilFor(event), } } ``` Keep this adapter host-owned. Add provider resources here and delegate `waitUntil` to the real host lifetime when one exists. The fallback observes failures for long-lived local Node processes; serverless deployments must expose their provider lifetime adapter through the event context. ## Runtime Context is not a Capability A Runtime Capability handle passes an implementation between packages. An Agent Capability gives an Agent a selected ability. It can use Runtime Context without exposing that context to the model. ## Inspect the handoff Inspect the generated route or custom server call that starts the operation. It shows which host supplies the runtime, background work, provider resources, and trace information. Read [Runtime events](https://vitehub.dev/docs/reference/runtime-events) for the records carried through Runtime Context and [Agent Invocations](https://vitehub.dev/docs/concepts/agent-invocations) for the request record. # Runtime Helpers and stable imports A Runtime Helper is the documented server API that application code uses to call or inspect a ViteHub feature. ViteHub keeps generated registries, provider bindings, and framework adapters behind that import. The package page documents what the API returns and which host resources it needs. ## Call the helper from server code ```ts [server/api/health.ts] import { useServerEnv } from '#vitehub/env/server' export function health() { const env = useServerEnv() return { environment: env.APP_ENV } } ``` Use the package import or generated `#vitehub/...` path documented for the feature. Don't import `.vitehub` registry files directly. The integration can change their structure. ## Keep configuration and runtime code separate | Part | What it contains | | --------------- | ------------------------------------------------------------------------ | | Runtime Helper | The application call or inspection API. | | Definition | Named configuration and behavior in application code. | | Runtime Context | Host resources needed during execution. | | Provider Output | Generated routes, bindings, functions, workers, or other host artifacts. | The Runtime Helper can use Runtime Context without making application code construct provider-specific objects. ## Check an import failure Check the package's documented import, the registered Vite integration, and the generated type files. Read [Import paths](https://vitehub.dev/docs/reference/import-paths) for exact path rules and [Provider Output](https://vitehub.dev/docs/reference/provider-output) for generated files. # Runtime policy, approvals, and traces Runtime policy decides whether an operation can run. An Approval Request pauses it until a trusted actor responds. A Trace Event records what happened before, during, and after the operation. Use these records to explain a runtime decision. They don't replace application logs, Agent Memory, or the final Agent output. ## Each record answers a different question | Record | Question | | ------------------- | ------------------------------------------------------------------------------- | | Policy Decision | Was the operation allowed, denied, or sent for approval? | | Approval Request | Which trusted response is needed before work continues? | | Trace Event | Which runtime action or transition occurred? | | Lease or wait state | Which work remains active while execution waits or continues in the background? | Runtime Context passes these records between packages and the host. ## Put approval beside the action A Capability can request approval for a tool or Server Primitive operation. The Invocation stream and runtime events include the request and result, so the host does not need to parse model text. If the host cannot satisfy an approval requirement, the operation stays pending or fails according to the package contract. ## Inspect the decision Inspect the invocation id, policy decision, approval request, trace event, and final result together. This shows whether work was rejected, waited, executed, or failed after execution began. Read [Runtime events](https://vitehub.dev/docs/reference/runtime-events) for event fields and [Capabilities](https://vitehub.dev/docs/concepts/capabilities-api) for the model-facing contribution that can trigger policy. # Server primitives Server Primitives are APIs for environment values, databases, queues, workflows, files, sandboxes, and other server features. Application code calls the ViteHub API. The Vite integration connects that call to the implementation supported by the current development or deployment host. ## Choose a primitive for the job | You need | Start with | | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Configure the app | [Env](https://vitehub.dev/docs/server-primitives/env), [Auth](https://vitehub.dev/docs/server-primitives/auth), or [Rate Limit](https://vitehub.dev/docs/server-primitives/rate-limit) | | Store data and files | [Database](https://vitehub.dev/docs/server-primitives/database), [KV](https://vitehub.dev/docs/server-primitives/kv), [Blob](https://vitehub.dev/docs/server-primitives/blob), [Workspace](https://vitehub.dev/docs/server-primitives/workspace), or [Source](https://vitehub.dev/docs/server-primitives/source) | | Send or receive messages | [Email](https://vitehub.dev/docs/server-primitives/email) or [Channels](https://vitehub.dev/docs/agents/channels) | | Run work later | [Queue](https://vitehub.dev/docs/server-primitives/queue), [Schedule](https://vitehub.dev/docs/server-primitives/schedule), or [Workflow](https://vitehub.dev/docs/server-primitives/workflows) | | Run isolated automation | [Browser](https://vitehub.dev/docs/server-primitives/browser), [Shell](https://vitehub.dev/docs/server-primitives/shell), or [Sandbox](https://vitehub.dev/docs/server-primitives/sandbox) | ## How a primitive works in your app Most ViteHub primitives follow the same pattern: ::steps{level="3"} ### Configure ViteHub Add ViteHub to your configuration. ViteHub uses the [Vite Environment API](https://vite.dev/guide/api-environment){rel=""nofollow""}, which requires Vite 8+, Nitro 3+, or Nuxt 5+. :::tabs{.framework-tabs} ::::tabs-item{icon="i-simple-icons-vite" label="Vite"} ```ts [vite.config.ts] import { defineConfig } from 'vite' import { vitehub } from 'vite-hub' export default defineConfig({ plugins: [ vitehub({ preset: 'node', database: true }), ], }) ``` :::: ::::tabs-item{icon="i-simple-icons-nuxtdotjs" label="Nuxt 5"} ```ts [nuxt.config.ts] import viteHubNuxt from 'vite-hub/nuxt' export default defineNuxtConfig({ modules: [ [viteHubNuxt, { preset: 'node', database: true }], ], }) ``` :::: ::::tabs-item{icon="i-unjs-nitro" label="Nitro 3"} ```ts [vite.config.ts] import { defineConfig } from 'vite' import { nitro } from 'nitro/vite' import { vitehub } from 'vite-hub' export default defineConfig({ plugins: [ vitehub({ preset: 'node', database: true }), nitro(), ], }) ``` :::: ::: ### Define the database Create a Database Definition file that ViteHub discovers automatically. Its file name becomes the database name, and the file defines the schema and options. :::tabs{.framework-tabs} ::::tabs-item{icon="i-simple-icons-vite" label="Vite"} ```ts [src/notes.database.ts] import { defineDatabase } from 'vite-hub/database' import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core' export default defineDatabase({ name: 'notes', schema: { notes: sqliteTable('notes', { id: integer('id').primaryKey(), title: text('title').notNull(), }), }, }) ``` :::: ::::tabs-item{icon="i-simple-icons-nuxtdotjs" label="Nuxt 5"} ```ts [server/databases/notes/config.ts] import { defineDatabase } from 'vite-hub/database' import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core' export default defineDatabase({ name: 'notes', schema: { notes: sqliteTable('notes', { id: integer('id').primaryKey(), title: text('title').notNull(), }), }, }) ``` :::: ::::tabs-item{icon="i-unjs-nitro" label="Nitro 3"} ```ts [server/databases/notes/config.ts] import { defineDatabase } from 'vite-hub/database' import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core' export default defineDatabase({ name: 'notes', schema: { notes: sqliteTable('notes', { id: integer('id').primaryKey(), title: text('title').notNull(), }), }, }) ``` :::: ::: ### Use it from server code Import the database API in a server route and query the named database. ViteHub connects that call to the provider configured for the current environment. :::tabs{.framework-tabs} ::::tabs-item{icon="i-simple-icons-vite" label="Vite"} ```ts [src/server.ts] import { useDatabase } from 'vite-hub/database/drizzle' export default { async fetch() { const { db, schema } = useDatabase('notes') return Response.json(await db.select().from(schema.notes)) }, } ``` :::: ::::tabs-item{icon="i-simple-icons-nuxtdotjs" label="Nuxt 5"} ```ts [server/api/notes.get.ts] import { useDatabase } from 'vite-hub/database/drizzle' export default defineEventHandler(() => { const { db, schema } = useDatabase('notes') return db.select().from(schema.notes) }) ``` :::: ::::tabs-item{icon="i-unjs-nitro" label="Nitro 3"} ```ts [server/api/notes.get.ts] import { useDatabase } from 'vite-hub/database/drizzle' export default defineEventHandler(() => { const { db, schema } = useDatabase('notes') return db.select().from(schema.notes) }) ``` :::: ::: :: The same path applies to other Server Primitives. Configure ViteHub, add a definition when the feature needs a name or schema, and call its server API. Check the [host support matrix](https://vitehub.dev/docs/frameworks-hosts/support-matrix) before choosing a deployment target. # Vite integrations and Provider Output A Vite integration connects a package to Vite development and builds. Provider Output is what that integration generates for a host, such as routes, functions, bindings, workers, crons, or runtime files. Configure the integration in Vite. Inspect Provider Output when you need to check host wiring. Application code uses the package's documented server API instead of generated internals. ## Configure the integration ```ts [vite.config.ts] import { defineConfig } from 'vite' import { vitehub } from 'vite-hub' export default defineConfig({ plugins: [ vitehub({ preset: 'node', agent: true, kv: true }), ], }) ``` Put options in `vite.config.ts` when they affect discovery, generated files, provider bindings, or deployment. Put options in a Definition when they apply to that named item. ## Use Provider Output as build evidence Inspect generated output to verify routes, bindings, or host wiring. Import it from application code only when the package documents a path such as `#vitehub/env/server`. | Need | Surface | | ---------------------------- | --------------------------------------------- | | Development and build wiring | The package Vite Integration. | | One named declaration | A Definition file. | | Host deployment material | Provider Output generated by the integration. | | Server code access | A documented package or generated import. | Read [Definition discovery](https://vitehub.dev/docs/concepts/definitions-and-discovery), [Runtime Helpers and stable imports](https://vitehub.dev/docs/concepts/runtime-helpers-and-stable-imports), and [Provider Output](https://vitehub.dev/docs/reference/provider-output) for more detail. # Workspace and Sources A Workspace is a named file tree that can persist changes. A Source provides read-only files, items, or controlled requests. Mounting a Source makes its content available inside a Workspace. Use a Workspace for files, rules, sessions, snapshots, and writes. Use a Source to read content from a local file, remote service, API, or another provider. ## Workspace and Source have different roles | | Workspace | Source | | ------------ | ---------------------------------------------------- | ------------------------------------------------ | | Provides | A file tree and file operations | Read-only content from an origin | | Writes | Can allow writes through Workspace rules | Does not accept Workspace writes | | Placement | Defines the tree and mount points | Appears at a mount inside a Workspace | | Agent access | Requires Workspace context and a selected Capability | Visible only within the selected Workspace Scope | ## Define the tree and its origins ```ts [server/workspaces/docs.ts] import { defineWorkspace, file, glob } from 'vite-hub/workspace' export default defineWorkspace({ sources: { readme: file({ path: 'README.md' }), docs: glob({ cwd: '.', include: ['docs/**/*.md'] }), }, rules: { '/**': { write: false }, '/drafts/**': { write: true, mediaType: 'text/markdown' }, }, }) ``` The Workspace defines the rules and tree. The Sources provide the content mounted into that tree. ## Agent access is another choice Workspace context does not give an Agent file tools. Attach a Capability such as `workspaceShell()` when the Agent needs to read or change Workspace files. Workspace Scope narrows the visible tree for one invocation. The trusted host or invocation context selects that scope. The model cannot widen it. ## Inspect the result Use `useWorkspace()` from server code to inspect the file tree. Generated Workspace and Source metadata lists discovered names and request descriptors without exposing provider credentials. Read [Workspace](https://vitehub.dev/docs/server-primitives/workspace), [Source](https://vitehub.dev/docs/server-primitives/source), and [Workspace context](https://vitehub.dev/docs/agents/workspace-context) for the APIs. # CLI The ViteHub CLI loads the local Vite config and collects package-contributed command namespaces. Commands stay owned by the package that understands the workflow, while `vitehub` gives agents and developers one predictable entry point. The official [`vite-hub` package on npm](https://www.npmjs.com/package/vite-hub){rel=""nofollow""} publishes both `vitehub` and `vite-hub` binaries. ## Install and open help The `vite-hub` framework distribution includes the CLI. The command reads active Vite plugins, so a missing package integration also means a missing package-owned command. ```bash [Terminal] pnpm add vite-hub pnpm vitehub --help ``` Libraries and advanced integrations that do not use the framework distribution can install `@vite-hub/cli` directly. Expected help lists available namespaces. The Agent Package contributes `agent` and `channels` when `hubAgent()` is active, Database contributes `db` when `hubDb()` is active, Workspace contributes `workspace` when `hubWorkspace()` is active, and the CLI includes the built-in `provision` namespace. ```txt [Output] Usage: vitehub [args...] Available namespaces: agent Agent development workflows. channels External Channel registration workflows. db Database development workflows. workspace Workspace development workflows. provision Idempotently create missing provider resources. ``` ## Commands | Command | Status | Owner | Use it for | | -------------------------- | -------------- | ---------------------------------------- | ----------------------------------------------------------------------------------------- | | `vitehub agent eval` | Opt-in tooling | Agent Package | Run discovered Agent Evals through ViteHub defaults. | | `vitehub agent info` | Available | Agent Package | Inspect resolved Agent metadata through a running Vite Development Server. | | `vitehub agent dev` | Available | Agent Package | Talk to a discovered Agent through a running Vite Development Server. | | `vitehub channels history` | Available | Agent Package | Download one deployed conversation and its attachments. | | `vitehub channels sync` | Available | Agent Package | Inspect or apply provider-owned webhook registrations for a deployed stage. | | `vitehub db generate` | Available | Database Package | Refresh generated Database artifacts and generate Drizzle migrations. | | `vitehub db migrate` | Available | Database Package | Refresh generated Database artifacts and apply Drizzle migrations. | | `vitehub workspace dev` | Available | Workspace Package | Run commands through a Workspace Session exposed by a Compatible Vite Development Server. | | `vitehub provision run` | Available | ViteHub CLI plus package Provision Steps | Create missing provider resources idempotently. | ## Synchronize Channel webhooks Deploy the application stage before registering its Channel webhooks. `channels sync` loads the discovered Agent Definitions with the selected Vite stage, checks that every desired webhook route is live at the exact public HTTPS origin, and then compares the provider state. Telegram is the first supported provider. The command is read-only by default. Start with sanitized JSON when an agent or another command needs to review the complete plan. ```bash [Terminal] pnpm vitehub channels sync \ --stage staging \ --url https://staging.example.com \ --json ``` `--stage staging` loads Vite's stage-specific environment files, such as `.env.staging`; existing process environment values take precedence. Keep the Telegram bot token and webhook secret in Server Env. The command does not accept credentials as flags and does not include them in human or JSON output. Apply the reviewed plan by repeating the exact origin in `--confirm-origin`. The confirmation is non-interactive, so the same contract works for developers, CI, and coding agents without silently selecting a deployment. ```bash [Terminal] pnpm vitehub channels sync \ --stage staging \ --url https://staging.example.com \ --apply \ --confirm-origin https://staging.example.com ``` Use `--agent ` or `--channel ` to narrow a multi-Agent application. Switching a Telegram Channel to polling, or setting `webhooks: false`, plans removal of an existing Telegram webhook; applying that plan also requires `--allow-delete`, and the current provider URL must belong to the confirmed origin. ViteHub preserves pending updates during registration and removal. Telegram exposes the registered URL and delivery errors through `getWebhookInfo`, but it does not return the configured secret token or allowed update list. The plan marks those fields as unverifiable. Use `--force` to reapply them when the URL already matches and credential or subscription configuration changed. Telegram accepts public webhook ports 443, 80, 88, and 8443; the CLI rejects other explicit ports before applying. `channels sync` owns only the provider's mechanical registration. The first Telegram synchronizer subscribes to message updates because that is the built-in Channel's supported inbound event. Admission rules, allowed users, secrets, and additional update types remain in the application. An app that needs a custom adapter, certificate, fixed IP, or connection policy must keep the provider lifecycle application-owned; an app-owned `adapter` is not a synchronization target. ## Download Channel history `channels history` loads the same stage-specific Agent and Channel configuration, then authenticates to the deployed webhook route with its configured webhook secret. It writes portable message metadata to `history.json` and downloads attachment data into `media/`; Agent traces and tool events are not included. ```bash [Terminal] pnpm vitehub channels history \ --stage production \ --url https://app.example.com \ --agent calories \ --channel telegram \ --output ./channel-history ``` A Telegram direct-message Channel infers its thread when the adapter allows exactly one user. Pass `--thread ` for group conversations and adapters where one Channel serves multiple conversations, issues, or tickets. When a Channel declares multiple webhook registrations, select the deployed route and its authentication with `--webhook `. The export can only contain history available through the Chat SDK adapter or its configured State Adapter. Telegram's Bot API cannot backfill arbitrary old messages, so its durable fallback uses the configured `threadHistory` window, which defaults to 100 messages retained for seven days. Export before that window expires when the archive is intended for recovery. ## Manage Database migrations The Database commands refresh the discovered Database Definitions before running Drizzle Kit. When every Database is named, ViteHub runs the command once for each generated Drizzle config and stops at the first failure. ```bash [Terminal] pnpm vitehub db generate pnpm vitehub db generate --name add-audit-log pnpm vitehub db generate --custom --name backfill-state pnpm vitehub db migrate ``` `db generate` forwards Drizzle Kit arguments, supports `--name ` for a migration name, and uses `--custom` to create an empty custom migration. `db migrate` accepts forwarded Drizzle Kit migration arguments. ## Run Agent Evals Use `vitehub agent eval` when the proof is Agent behavior, not just TypeScript. The Agent namespace includes the command, while Eval authoring and execution keep their test-only dependencies explicit. Install them before creating an Eval; the optional path then narrows the Agent Eval Target. ```bash [Terminal] pnpm add -D @vite-hub/agent evalite vitest ``` ```bash [Terminal] pnpm vitehub agent eval pnpm vitehub agent eval server/agents/support.eval.ts --threshold 90 pnpm vitehub agent eval --output .vitehub/evals/support.json --hide-table pnpm vitehub agent eval --watch pnpm vitehub agent eval --no-cache ``` Use `--watch` to rerun affected evals after file changes and `--no-cache` to bypass cached model output. Set `agent.eval.testTimeout` in `vite.config.ts` for long-running model or provider evals. ## Inspect an Agent Definition Start the app's Vite Development Server, then inspect the resolved metadata for one Agent Definition. The command does not invoke the Agent Driver. ```bash [Terminal] pnpm vitehub agent info --agent support pnpm vitehub agent info --agent support --json ``` The default output summarizes the selected Driver, its execution authority, tools, visible Workspace files and Sources, instructions, Agent Invoker Profiles, warnings, and metadata status. Execution authority is a resolution-time snapshot of filesystem, network, environment, credential, process, and isolation authority. An `unknown` value means the runtime or provider cannot prove that dimension during inspection; it does not mean restricted or denied. The snapshot describes runtime truth for the inspected context, not an enforcement decision or proof of safety. Use `--json` for the structured inspection contract at `config.driver.executionAuthority`, and `--url` when Vite is not listening on `http://localhost:5173`. When multiple Agents are discovered, `--agent` is required. `agent info` reads resolved runtime metadata from the guarded Agent Dev Loop endpoint exposed by `hubAgent()`. ## Talk to an Agent during development Start the app's Vite dev server in one terminal. Then attach the Agent Dev Loop from another terminal. ```bash [Terminal] pnpm vitehub agent dev --agent support --url http://localhost:5173 ``` Pass a message or `--prompt` for a one-shot invocation, or omit both to enter an interactive session. ```bash [Terminal] pnpm vitehub agent dev "/summary" --agent support pnpm vitehub agent dev --agent support --prompt "/summary" pnpm vitehub agent dev --agent support -p "/summary" ``` Use `--payload` when the invocation needs event input that would normally come from a Channel, route, webhook, or app transport. The file can live anywhere in the app, but colocating it as `server/agents//dev.payload.json` keeps local fixtures next to the Agent Definition without making them part of production behavior. It must contain one JSON object shaped for the selected Agent Trigger. For non-chat triggers, pass `--trigger` and shape the file for that trigger instead of Agent Invocation Context Values. ```json [server/agents/support/dev.payload.json] { "user": { "id": "user_123", "name": "Local Developer" }, "session": { "id": "local-support" }, "meta": { "audience": "technical" } } ``` ```bash [Terminal] pnpm vitehub agent dev --agent support --payload server/agents/support/dev.payload.json pnpm vitehub agent dev --agent support --payload server/agents/support/dev.payload.json -p "/summary" pnpm vitehub agent dev --agent support --timeout 180000 -p "/summary" ``` Use `--cli` when a Capability attached to the Agent declares a Capability CLI. Everything after `--` is parsed as the nested Capability CLI command. Attached Capability CLI Contributions are available to the Agent Dev Loop by default, including for provider-backed Agents; set `defineAgent({ cli: { capabilities: false } })` to hide them from this surface. During Agent runs, ViteHub renders operation tool calls as command lines, such as `api listCustomers --query '{"status":"active"}'`, instead of dumping the raw input object. ```bash [Terminal] pnpm vitehub agent dev --url http://localhost:3000 --agent support --cli inventory -- items list --json ``` Expected output includes the resolved payload file path before the Agent Invocation starts. In interactive mode, type a message or command such as `/summary` at the prompt. ```txt [Output] Loaded payload: /Users/acme/app/server/agents/support/dev.payload.json Connected to support at http://localhost:5173 > /summary ``` Prefix input with `!` when you need to run a direct Workspace command through the selected Agent Dev Loop Target. The selected Agent must declare a writable Workspace. ViteHub runs the command through that Workspace Session and commits successful changes back to the Workspace Store. ```bash [Terminal] pnpm vitehub agent dev --agent support "!pnpm test" pnpm vitehub agent dev --url http://localhost:5173 --timeout 180000 support !pnpm test --filter api ``` Put Agent Dev Loop options before the `!` command; flags after `!` are passed to the Workspace command. In interactive mode, `!` input bypasses the Agent Driver for that turn. Use normal messages for Agent reasoning, `!` commands for direct Workspace shell work, and `--cli` for Capability CLI Contributions. ```txt [Output] Connected to support at http://localhost:5173 > !pnpm test ``` ## Run Workspace commands during development Use `vitehub workspace dev` when you want a direct command against a Workspace without routing through an Agent. Start the app's Vite dev server first, then run the command from another terminal. ```bash [Terminal] pnpm vitehub workspace dev --url http://localhost:5173 docs exec pnpm test --filter api pnpm vitehub workspace dev --timeout 180000 docs exec "npm run lint" pnpm vitehub workspace dev --path guides --path examples docs exec pnpm test ``` The command runs through the Workspace dev endpoint exposed by `hubWorkspace()` on the Compatible Vite Development Server. ViteHub materializes a Workspace Session, executes the command, prints stdout and stderr, and commits the session when the command exits successfully. Put Workspace Dev options before the Workspace target; use `exec` before one-shot command args. Repeat `--path ` to materialize only those Workspace paths for the command session. If you omit the command in an interactive terminal, the CLI opens a prompt for repeated Workspace commands. ```txt [Output] Connected to docs at http://localhost:5173 > pnpm test ``` ## Preview provisioning Use `--dry-run` before writing Provider resources. Provision never deletes or mutates existing resources, and non-secret ids are written only when a real run applies actions. ```bash [Terminal] CLOUDFLARE_ACCOUNT_ID=... CLOUDFLARE_API_TOKEN=... pnpm vitehub provision run --provider cloudflare --dry-run VERCEL_TOKEN=... VERCEL_PROJECT_ID=... pnpm vitehub provision run --provider vercel --dry-run ``` ## Troubleshooting | Symptom | Likely cause | Fix | | --------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | | `Unknown ViteHub CLI namespace` | The package Vite Integration is not installed or is disabled. | Add the package's `hubX()` plugin to `vite.config.ts`. | | `Provision requires --provider cloudflare|vercel` | The provider flag is missing or misspelled. | Pass a supported provider explicitly. | | Provision fails before applying actions | Required provider credentials are missing. | Set `CLOUDFLARE_ACCOUNT_ID` and `CLOUDFLARE_API_TOKEN`, or set `VERCEL_TOKEN`. | | Provision dry-run reports no actions | A package plan skipped provider lookup because its read credentials are missing. | Supply the provider credentials to inspect existing resources; `--dry-run` still prevents `apply()`. | | Vercel Provision reports no resources for Blob | `VERCEL_PROJECT_ID` is missing, or the active Blob store is not `vercel-blob`. | Set the project id and select the Vercel Blob driver before rerunning the plan. | | Agent eval CLI is disabled | `agent.eval` or `agent.cli` disables the Agent Eval Runner. | Re-enable the Agent integration option for local development. | | Agent eval times out | The eval case, model call, or provider run exceeds `agent.eval.testTimeout`. | Increase `agent.eval.testTimeout` in `vite.config.ts` or narrow the eval case. | | Vite config fails while loading a ViteHub plugin import | A fresh npm project is loading `vite.config.ts` as CommonJS, but ViteHub packages are ESM-only. | Set `"type": "module"` in `package.json` or rename the config to `vite.config.mts`. | | `No Compatible Vite Development Server found` | The app dev server is not running or `--url` points at the wrong port. | Start Vite separately, then pass the dev server URL. | | `Unknown Workspace Dev target` | The named Workspace is not discovered by the running Vite dev server. | Check the Workspace Definition name and make sure `hubWorkspace()` is active. | | `Agent Dev Loop command requires workspace.mode: "write"` | A `!` command targeted an Agent without writable Workspace access. | Configure the selected Agent with `workspace: { mode: 'write' }`, or send a normal Agent message instead. | | Agent Dev Loop request times out | A streamed invocation emitted no events before the inactivity timeout, or a Capability CLI/Workspace command exceeded its wall-clock deadline. | Pass `--timeout ` for the dev-loop operation or inspect the stalled work. | | `Agent Dev Loop payload file must contain a JSON object` | The `--payload` file is not a JSON object. | Replace the file contents with one object shaped for the selected Agent Trigger. | ## Next steps - Use [Agent Evals](https://vitehub.dev/docs/agents/evals) for behaviour checks. - Use [Workspace](https://vitehub.dev/docs/server-primitives/workspace) for Workspace Sessions and write access. - Use [Provisioning](https://vitehub.dev/docs/development/provisioning) for provider resource ids. - Use [Config options](https://vitehub.dev/docs/reference/config-options) for package integration switches. # Console The ViteHub Console is a read-only app for inspecting discovered Agents and retained Agent Invocations. It is off by default. Enable it, start the app, then open `/_vitehub` to browse sessions, search retained text, and inspect invocation events. Console data can contain user prompts, model output, tool activity, and provider metadata. Protect the Console before making it reachable on a production URL. ## Enable the Console Set `console: true` in the root ViteHub integration. The Node preset supports the Console in development and production. ```ts [vite.config.ts] import { vitehub } from 'vite-hub' import { defineConfig } from 'vite' export default defineConfig({ plugins: [vitehub({ agent: true, console: true, preset: 'node', })], }) ``` A standalone Nitro 3 app adds Nitro after ViteHub. The Console app and its UI dependencies ship inside `vite-hub`, so an existing Nitro app does not need another Console or UI package. ```ts [vite.config.ts] import { nitro } from 'nitro/vite' import { vitehub } from 'vite-hub' import { defineConfig } from 'vite' export default defineConfig({ plugins: [ vitehub({ agent: true, console: true, preset: 'node', }), nitro(), ], }) ``` Nuxt uses the same option. Install Nuxt UI because the Console uses the ViteHub UI module. ```bash [Terminal] pnpm add @nuxt/ui ``` ```ts [nuxt.config.ts] import viteHubNuxt from 'vite-hub/nuxt' export default defineNuxtConfig({ modules: [ [viteHubNuxt, { agent: true, console: true, preset: 'node', }], ], }) ``` Restart the development server after changing the option. Open `http://localhost:3000/_vitehub`, using your app's actual origin and port. If `console` is omitted or set to `false`, ViteHub does not register a Console page, API handler, Nitro plugin, or public asset path. A disabled Console returns the host's normal not-found response. ## Protect both route groups `console: true` registers the page under `/_vitehub/**` and its read API under `/api/_vitehub/console/**`. It does not add authentication or an admin role. If the app uses ViteHub Auth, guard both route groups in the Primary Auth Definition. The host decides what makes a user an administrator. ```ts [server/auth.ts] import { defineAuth, type AuthAccessAuthorize } from 'vite-hub/auth' const authorizeConsole: AuthAccessAuthorize = ({ user }) => user.role === 'admin' export default defineAuth({ access: { routes: [ { route: '/_vitehub/**', authorize: authorizeConsole }, { route: '/api/_vitehub/console/**', authorize: authorizeConsole }, ], }, }) ``` ViteHub checks for an Auth Session before it calls `authorizeConsole`. A missing session returns `401`. Returning `false` from the callback returns `403`. The callback can return a `Response` when the app needs another rejection or redirect. The `role` field above is an application example, not a ViteHub field. Replace it with the role, permission, or allowlist already used by the host. Apps that use another authentication library should protect the same two route groups in host middleware. Read [Auth](https://vitehub.dev/docs/server-primitives/auth#authorize-access-routes) for sign-in redirects and the complete callback contract. ## Know what the Console stores The Console installs a fallback Agent Invocation journal at `.vitehub/data/console.sqlite`. It retains invocation records and selected searchable text, including prompts, messages, final text, and progress updates. The journal has no automatic TTL or deletion. In production, the operator must define how long to retain the file and how to remove records that may contain sensitive data. The fallback applies only when an Agent Definition does not configure `invocations`. An explicit `defineAgent({ invocations })` store remains authoritative, and its sessions are not copied into `console.sqlite` or read by the built-in Console. The automatic fallback also requires `defineAgent` from `vite-hub/agent`. Definitions imported directly from `@vite-hub/agent` must configure their own `invocations` store. Use the [Invocation UI](https://vitehub.dev/docs/ui/invocation) with that store when the app needs a custom inspection page. Production Console builds currently require `preset: 'node'` because the fallback journal uses local SQLite. The Node preset supports the build, but it does not make `.vitehub/data/console.sqlite` persistent: the host must provide durable storage that survives process and deployment replacement. The file is also local to one replica and is not shared across replicas. Other presets can run the Console during development. Their production builds fail while `console: true` is set, so ViteHub does not write the journal to storage that may disappear between requests or deployments. The Console API accepts `GET` requests only. Responses set `Cache-Control: no-store` and `X-Content-Type-Options: nosniff`. ## Inspect usage Session details show recorded token totals when the invocation trace contains provider usage. Add the [Usage Capability](https://vitehub.dev/docs/capabilities/usage) when the provider needs an explicit usage request or the Agent must expose the normalized Agent Usage Record at finish. The Console does not calculate missing provider data. Token counts, model metadata, and provider-reported cost remain absent when the provider does not report them. ## Fix common failures | Symptom | Check | | ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------- | | `/_vitehub` returns `404` | Confirm `console: true`, then restart the development server. Omitted and false configurations register no route. | | The Console opens but has no sessions | Invoke a discovered Agent. Confirm it uses the framework fallback instead of a separate `invocations` store. | | A production build rejects `console: true` | Use the Node preset or disable the Console for that production build. | | The page returns `401` | Sign in through the Auth provider configured by the host. | | The page returns `403` | Check the host's `authorize` callback and the current user's role or permission. | Use [Agent Invocations](https://vitehub.dev/docs/agents/invocations) for custom stores and invocation lifecycle behavior. Use [Invocation UI](https://vitehub.dev/docs/ui/invocation) when building an application-owned inspection page instead of mounting the complete Console. # Generated files Generated files prove how ViteHub resolved Definitions, Runtime Registries, stable imports, and Provider Output. Application code uses documented ViteHub imports instead of generated files. ## Common generated paths | Path | Owner | Purpose | | ------------------------------------------ | ------------------------- | ---------------------------------------------------------------------------------------------------- | | `.vitehub/env/public.mjs` | Env Package | Generated Public Env runtime module. | | `.vitehub/env/server.mjs` | Env Package | Generated Server Env runtime module. | | `.vitehub/types/env.d.ts` | Env Package | Generated Public Env and Server Env types. | | `.vitehub/types/browser.d.ts` | Browser Package | Generated Browser Definition names and input/result module types. | | `.vitehub/types/email.d.ts` | Email Package | Exact module declarations for discovered `#vitehub/emails/` imports. | | `.vitehub/types/markdown-template.d.ts` | Markdown Template Package | Generated module type for direct `*.template.md` imports. | | `.vitehub/types/workspace.d.ts` | Workspace Package | Generated Workspace name types. | | `.vitehub/email/templates/*.mjs` | Email Package | Bundled Markdown email templates used by provider builds. | | `.vitehub/rate-limit/manifest.json` | Rate Limit Package | Sorted Rate Limit IDs, resolved providers, and inspectable driver capabilities. | | `.vitehub/nitro/realtime/registry.mjs` | Realtime Package | Generated registry for discovered Realtime Definitions. | | `.vitehub/nitro/realtime/handler.ts` | Realtime Package | Generated WebSocket and checkpoint route handler. | | `.vitehub/agent/chat-webhook-route.ts` | Agent Package | Generated Chat Webhook Route handler for discovered chat-capable Agents. | | `.vitehub/agent/discord-gateway-plugin.ts` | Agent Package | Generated Nitro process lifecycle for Discord Gateway listeners on the Node preset. | | `.vitehub/agent/discord-gateway-route.ts` | Agent Package | Generated Nitro route handler that wakes the Discord Gateway listener for discovered Discord Agents. | | `.vitehub/agent/deno-server.ts` | Agent Package | Generated Deno server output for Agent chat and webhook routes. | | `.vitehub/schedule/deno-cron.mjs` | Schedule Package | Deno `Deno.cron` wake output for Static Schedule Definitions. | | `.vitehub/nitro/schedule/*` | Schedule Package | Narrow Nitro bridge for Schedule Provider Wake. | | `.vitehub/provision.json` | ViteHub CLI | Non-secret Provision State read by Vite Integrations. | | `.vercel/output/**` | Provider Output | Vercel Build Output generated during provider builds. | | `.netlify/v1/functions/**` | Provider Output | Netlify Agent and static Schedule functions generated by their package integrations. | | `dist//wrangler.json` | Provider Output | Cloudflare worker config generated during provider builds. | The `.vitehub/agent/**` paths above are the default for Vite integrations. The Nuxt integration keeps generated Agent artifacts inside Nuxt's build directory instead, under `/vitehub/agent/**` (normally `.nuxt/vitehub/agent/**`). Agent Definition discovery still uses the project and server directories. ## Inspect generation List generated files after Vite startup or build. The exact set depends on the package integrations and Provider Selection. ```bash [Terminal] find .vitehub -maxdepth 4 -type f | sort ``` For Nuxt, inspect the framework-owned generated directory instead. ```bash [Terminal] find .nuxt/vitehub -maxdepth 4 -type f | sort ``` Inspect resolved Rate Limit guarantees before deployment. The manifest uses `schemaVersion: 2` and contains sorted `rateLimits` entries with `name`, `provider`, and `capabilities`; it is generated state for agents and tooling, not an application import. ```bash [Terminal] cat .vitehub/rate-limit/manifest.json ``` Inspect env aliases through the stable import paths. The generated files are implementation details behind those imports. ```ts [server/config.ts] import { useServerEnv } from '#vitehub/env/server' const env = useServerEnv() ``` ## Regenerate after edits Vite Integrations refresh generated files during dev startup, hot updates, or build hooks depending on the package. Restart the Vite dev server when a generated file does not match the current Definition set. ```bash [Terminal] pnpm dev ``` ## Troubleshooting | Symptom | Likely cause | Fix | | ------------------------------ | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | | Stable import does not resolve | Generated type path is missing from TypeScript includes, or the Vite Integration did not run. | Add `.vitehub/types/**/*.d.ts` to `tsconfig.json` and restart Vite. | | Generated registry is empty | Discovery path does not match the package file convention. | Check [File conventions](https://vitehub.dev/docs/reference/file-conventions). | | Provider output is stale | Build skipped provider output during dev or e2e mode. | Run a production-shaped build. | | Provision ids are missing | Provision State was never written, or env vars override it. | Run `vitehub provision run --dry-run`, then apply with credentials if needed. | ## Next steps - Use [Import paths](https://vitehub.dev/docs/reference/import-paths) for public import boundaries. - Use [Provider output](https://vitehub.dev/docs/reference/provider-output) for host artifacts. - Use [Runtime and host support](https://vitehub.dev/docs/frameworks-hosts/support-matrix) for qualified host coverage. - Use [File conventions](https://vitehub.dev/docs/reference/file-conventions) for discovery rules. # Local development Run the application locally before testing a hosted build. ViteHub uses the same Vite config to discover definitions, prepare generated files, and resolve Agent metadata. ## Local proof map | Proof path | Use it for | Output to inspect | | ----------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | | Vite dev server | Definition discovery, server imports, Agent streams, and local providers | Terminal output, CLI behavior, and resolved Agent metadata | | ViteHub Console | Discovered Agents, retained sessions, search, invocation events, and provider usage | `/_vitehub` and the configured Agent Invocation store | | ViteHub CLI | Package-owned command workflows such as Agent Evals and Provision | CLI exit code, concise output, optional JSON files | | Generated files | Registries, generated env access, deployment output, and provision state | `.vitehub/**`, `.vercel/output/**`, `dist/**`, or provider config files | | Application tests | Server API behavior and application regressions | Test output and application fixtures | ## Run the local app Start with the app's normal development command. ViteHub Vite Integrations run during Vite startup, so discovery and generated local files use the same root as the app. ```bash [Terminal] pnpm dev ``` Run the application's normal test and build commands after the development server proves discovery. ```bash [Terminal] pnpm test pnpm build ``` ## Inspect generated state Generated files help you debug discovery and host output. Do not import them from application code unless a reference page documents the path. ```bash [Terminal] find .vitehub -maxdepth 3 -type f | sort cat .vitehub/provision.json ``` Common generated paths include env modules, Workspace types, Agent webhook route handlers, schedule Nitro bridge files, and deployment output. The installed packages and selected host determine which files appear. ## Inspect interactive Agent behaviour Use [the ViteHub CLI](https://vitehub.dev/docs/development/cli) to inspect Agent metadata or run an Agent locally. Enable the [ViteHub Console](https://vitehub.dev/docs/development/console) when you need retained session search and invocation event inspection in a browser. The [Agents overview](https://vitehub.dev/docs/agents) links to registration, invocation, and deployment details. ## Verify before deploy Run the narrowest check that proves the behavior you changed. Use a package test for public contract changes, `vitehub agent eval` for Agent behavior, `vitehub provision run --dry-run` for provider resources, and a provider build when generated Provider Output changed. ```bash [Terminal] pnpm vitehub agent eval pnpm vitehub provision run --provider cloudflare --dry-run pnpm build ``` ## Next steps - Open [CLI](https://vitehub.dev/docs/development/cli) for command-owned proof paths. - Open [Console](https://vitehub.dev/docs/development/console) to inspect retained Agent Invocations in a browser. - Open [Agent Evals](https://vitehub.dev/docs/agents/evals) for repeatable Agent behavior checks. - Open [Generated files](https://vitehub.dev/docs/development/generated-files) when a Runtime Registry or Provider Output looks wrong. - Open [Errors and diagnostics](https://vitehub.dev/docs/reference/errors-diagnostics) for failure families. # Provisioning Provision is the ViteHub CLI workflow that creates missing provider resources required by app Definitions. Provision Steps are package-contributed, idempotent, and create-only; they never delete or mutate existing resources. ## Preview the plan Run a dry run first. The CLI loads the Vite config, collects Provision Steps from active package integrations, and prints the actions for one provider. ```bash [Terminal] CLOUDFLARE_ACCOUNT_ID=... CLOUDFLARE_API_TOKEN=... pnpm vitehub provision run --provider cloudflare --dry-run VERCEL_TOKEN=... VERCEL_PROJECT_ID=... pnpm vitehub provision run --provider vercel --dry-run ``` Provider steps use read credentials during planning to distinguish existing resources from resources to create. A dry run does not call `apply()` or write Provision State, but a useful plan still needs the provider credentials required to inspect current state. ```txt [Output] create d1-database app-content exists r2-bucket uploads ``` ## Apply the plan The apply command uses the same provider credentials as the plan. Cloudflare and Vercel use different credential sets. ```bash [Terminal] CLOUDFLARE_ACCOUNT_ID=... CLOUDFLARE_API_TOKEN=... pnpm vitehub provision run --provider cloudflare VERCEL_TOKEN=... VERCEL_PROJECT_ID=... VERCEL_TEAM_ID=... pnpm vitehub provision run --provider vercel ``` Vercel Blob provisioning requires `VERCEL_PROJECT_ID` so the provision step can attach `BLOB_READ_WRITE_TOKEN` to the target project. `VERCEL_TEAM_ID` or `VERCEL_ORG_ID` supplies an optional team scope. After a successful apply, the CLI writes non-secret ids to `.vitehub/provision.json`. Vite Integrations may read that file as a binding-id source during dev or build. ```json [.vitehub/provision.json] { "cloudflare": { "d1": { "default": "database-id" } } } ``` ## Resource ownership | Owner | Responsibility | | ----------------- | -------------------------------------------------------------------------------------------------------- | | ViteHub CLI | Loads Vite config, collects Provision Steps, validates provider credentials, and writes Provision State. | | Primitive package | Plans and applies resources for the primitive it owns. | | Provider | Owns cloud resources, credentials, and existing-resource lookup behavior. | | Vite Integration | Reads Provision State when generated Provider Output needs resource ids. | ## Production boundary Provision is not a build step. Builds may read Provision State, but they must not create provider resources. ::warning Do not commit `.vitehub/provision.json` unless a project deliberately decides that non-secret provider ids belong in source control. The root repository ignores `.vitehub/**` by default. :: ## Next steps - Use [Provider output](https://vitehub.dev/docs/reference/provider-output) to understand generated host artifacts. - Use [Cloudflare](https://vitehub.dev/docs/frameworks-hosts/cloudflare) or [Vercel](https://vitehub.dev/docs/frameworks-hosts/vercel) for host boundaries. - Use [Troubleshooting](https://vitehub.dev/docs/development/troubleshooting) for credential and output failures. # Troubleshooting Troubleshooting starts from the failed proof path. Identify whether the failure comes from discovery, generated files, provider resources, Runtime Helpers, the CLI Dev Loop, Agent behavior, or host output before changing code. ## Quick checks | Symptom | First check | Proof path | | ---------------------------------- | --------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | | Definition is missing | File path and default export shape | [File conventions](https://vitehub.dev/docs/reference/file-conventions) and `.vitehub/**` | | Stable import fails | Vite Integration and generated TypeScript includes | [Generated files](https://vitehub.dev/docs/development/generated-files) | | Provider build fails | Provider Selection and required resource ids | [Provider output](https://vitehub.dev/docs/reference/provider-output) | | Agent CLI cannot inspect or invoke | Running Vite server and `hubAgent()` registration | [CLI](https://vitehub.dev/docs/development/cli) | | Agent changed behaviour | Agent Eval result and Agent Usage Record | [Agent Evals](https://vitehub.dev/docs/agents/evals) | | Agent proof times out | Dev-loop `--timeout`, `agent.eval.testTimeout`, or stalled provider/session setup | [CLI](https://vitehub.dev/docs/development/cli) and [Agent Evals](https://vitehub.dev/docs/agents/evals) | | Runtime error lacks context | Package error family and diagnostics output | [Errors and diagnostics](https://vitehub.dev/docs/reference/errors-diagnostics) | ## Discovery failures Discovery Identity comes from the file location. Do not add inline ids to force a name; move the file to the expected convention instead. ```txt [File tree] server/ agents/ support.ts queues/ welcome-email.ts workspaces/ docs.ts ``` If a package requires a direct default export of a Definition Boundary Helper, avoid named aggregate exports and local indirection. The direct export keeps Build-Extracted Definition Options inspectable. ## Provider failures Provider failures usually belong to one of three layers: missing provider credentials, missing Provision State, or invalid Provider Output. Dry-run provisioning first, then inspect generated host output. ```bash [Terminal] pnpm vitehub provision run --provider cloudflare --dry-run pnpm build find dist -maxdepth 4 -type f | sort ``` ## Agent failures Separate Agent runtime failures from model behavior. Use `vitehub agent dev` to inspect one interactive Agent Invocation, then use Agent Evals when the failure is repeatable behavior. If the proof is timing out before it reaches the interesting failure, increase the dev-loop inactivity `--timeout` or the Agent Eval Runner `agent.eval.testTimeout` in `vite.config.ts`. ```bash [Terminal] pnpm vitehub agent eval server/agents/support.eval.ts --output .vitehub/evals/support.json ``` When the Agent Dev Loop reports `Agent Invocation Stream timed out after of inactivity`, first decide whether the streamed invocation is expected to stay silent longer than the default timeout. If so, rerun with `vitehub agent dev --timeout `. If the timeout is surprising, inspect the Agent Driver boundary and any Workspace or provider session setup before changing prompts. For Capability CLI and `!` Workspace commands, the same option is a wall-clock command deadline because those operations do not emit Agent Invocation Stream events. ## When to escalate Escalate to the owning ViteHub package when the same failure reproduces outside application code. Include the smallest reproduction, the generated artifact that failed, and the narrow command that demonstrates the problem. ## Next steps - Use [Verification](https://vitehub.dev/docs/development/verification) to choose the right check. - Use [Errors and diagnostics](https://vitehub.dev/docs/reference/errors-diagnostics) to classify the failure. - Use [Local development](https://vitehub.dev/docs/development) to restart from the full proof map. # Verification Verification proves that ViteHub primitives keep working across generated output, local provider execution, and live providers. Use the narrowest tier that covers the risk introduced by the change. ## Verification tiers | Tier | Runs where | Proves | | ------------------------ | ----------------------------- | ------------------------------------------------------------------------ | | Unit or package test | Package test suite | Pure runtime behavior, config normalization, and error branches. | | Provider Output Contract | Pull request check | Generated Provider Output shape without cloud execution. | | Local Provider Run | Pull request check | Built Provider Output can execute the application proof fixture locally. | | Live Smoke | Scheduled provider deployment | Thin real-provider coverage for the same application behaviour. | | Agent Eval | Local or CI behavior check | Agent Definition behavior and scored Agent Invocations. | ## Run application checks Run the application's tests before inspecting generated host output. A successful test suite proves application behaviour, while a production-shaped build proves that the selected integrations can generate their current artifacts. ```bash [Terminal] pnpm test pnpm build ``` ## Verify Provider Output Provider Output Contracts inspect generated files rather than cloud state. Use them when the change affects bindings, worker bundles, Vercel Build Output, generated functions, cron entries, or runtime imports. Inspect the selected host directory after the build. The [Provider output reference](https://vitehub.dev/docs/reference/provider-output) lists the expected artifact families. ## Keep Live Smoke thin A deployment smoke exercises the same application behavior as the local checks. Keep the deployed check narrow, but verify every provider binding or hosted service that local adapters cannot reproduce. ## Next steps - Use [Provider output](https://vitehub.dev/docs/reference/provider-output) for generated artifact families. - Use [Generated files](https://vitehub.dev/docs/development/generated-files) to inspect local output. - Use [Troubleshooting](https://vitehub.dev/docs/development/troubleshooting) when a proof fails. # Cloudflare Cloudflare is a Provider Selection for packages that can generate Workers, bindings, queues, workflows, schedules, storage, or sandbox output. Keep the Definition and Runtime Helper host-neutral. Put Cloudflare details in Integration Options and Provider Output. ## Cloudflare boundaries | Concern | ViteHub boundary | | ----------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | Workers and generated bundles | Provider Output written during production-shaped builds. | | D1, R2, KV, Queues, Browser Run, Rate Limiting bindings, Workflows, and Sandbox resources | Primitive package configuration plus Provision Steps where available. | | Credentials | Provider env vars for provisioning or host runtime secrets through Server Env. | | Runtime context | Runtime Host Context passed by the host integration, not app-owned global state. | | Agent state | Agent Package state provider configuration when Cloudflare-backed state is selected. | | Cloudflare Computer Boxes | App-owned Computer Durable Object, backend, bindings, migrations, and compatibility flags; ViteHub adapts the configured namespace at runtime. | ## Provider-owned configuration The primitive package options select Cloudflare. Each provider field stays with the package that owns the primitive. ```ts [vite.config.ts] import { hubDb } from '@vite-hub/database/vite' import { hubQueue } from '@vite-hub/queue/vite' import { defineConfig } from 'vite' export default defineConfig({ plugins: [ hubDb(), hubQueue(), ], queue: { provider: 'cloudflare', binding: 'JOBS', }, }) ``` Database D1 metadata belongs to each Database Definition because a Vite app can have multiple Named Databases. ```ts [src/database.ts] import { defineDatabase } from '@vite-hub/database' import { notes } from './schema' export default defineDatabase({ cloudflare: { binding: 'DB', databaseName: 'app', }, schema: { notes }, }) ``` The Nuxt-only `database.driver: 'd1'` option configures one Nuxt Content and Nitro host resource; it is not a Vite Database Definition shortcut. ## Provision boundary Provision exposes a dry-run plan before it applies changes. A successful apply writes non-secret ids into `.vitehub/provision.json`; secrets remain in environment variables or provider env stores. ```bash [Terminal] CLOUDFLARE_ACCOUNT_ID=... CLOUDFLARE_API_TOKEN=... pnpm vitehub provision run --provider cloudflare --dry-run CLOUDFLARE_ACCOUNT_ID=... CLOUDFLARE_API_TOKEN=... pnpm vitehub provision run --provider cloudflare ``` ## Generated output Cloudflare output can include worker bundles, `wrangler.json`, D1 bindings, queue consumers, Rate Limiting bindings, cron triggers, and package-specific runtime imports. A production-shaped build materialises the selected output under `dist`. ```bash [Terminal] pnpm build find dist -maxdepth 4 -type f | sort ``` Generate Agent routes through Provider Output. Raw Cloudflare Worker fetch handlers are not a public Agent API. Required secret Server Env declarations with one exact Env Source are written to `secrets.required` in the generated Wrangler configuration, including each configured named Wrangler environment because secrets are not inherited. Wrangler reuses an existing Worker secret and stops deployment when that binding is absent; ViteHub records only the binding name and never resolves or writes its value during build. Optional secrets, non-secret values, defaults, and alternative source lists remain runtime-only because Wrangler's required list cannot express fallback names. ### Workers Builds Use Nitro's generated deployment command for production and non-production Workers Builds: ```bash [Deploy command] pnpm exec nitro deploy --prebuilt ``` When Sandbox is enabled, ViteHub writes an explicit gradual Container rollout into `.output/nitro.json`. This deploys the Worker and its Container application through the same generated contract. A direct Wrangler command with `--containers-rollout=none` bypasses ViteHub's deployment command and can leave a newly scoped Sandbox binding without a Container application to run. ### Rate Limiting bindings Register the Rate Limit integration. A Cloudflare Nitro preset infers the provider, and each handler-local `requireRateLimit()` policy contributes one `ratelimits` entry to Nitro's Wrangler config. Do not repeat those bindings in `nitro.cloudflare.wrangler`; plain Vite builds continue to write them to generated `wrangler.json`. ```ts [vite.config.ts] import { hubRateLimit } from '@vite-hub/rate-limit/vite' import { defineConfig } from 'vite' export default defineConfig({ plugins: [hubRateLimit({ namespace: 'acme-image-service-production' })], }) ``` Cloudflare native enforcement is best-effort and supports 10-second or 60-second fixed windows. The integration rejects `enforcement: 'strict'` and unsupported periods during the build instead of silently weakening the policy. Its inspectable capabilities report location-scoped counters, unknown rejected-attempt behavior, and `availability: 'never'` for every quota metadata field; the binding returns only an allow-or-reject decision. The generated binding is request-scoped. If it is unavailable at runtime, the handle's `failure` policy decides whether the request is denied or allowed. Give every separately deployed Worker and environment a unique namespace because Cloudflare shares counters with the same namespace ID across Workers in one account. Rate Limiting bindings do not use Cloudflare KV and require no separate resource provisioning. Workspace adds an Artifacts binding when its Store explicitly selects Cloudflare Artifacts: ```ts [vite.config.ts] import { hubWorkspace } from '@vite-hub/workspace/vite' import { defineConfig } from 'vite' export default defineConfig({ plugins: [hubWorkspace()], workspace: { store: { provider: 'cloudflare-artifacts', binding: 'WORKSPACE_ARTIFACTS', namespace: 'vitehub', }, }, }) ``` Inspect the generated `artifacts` entry in `wrangler.json` before deployment. Workspace preserves unrelated app-owned Artifacts bindings. Cloudflare hosting still defaults to the ephemeral `memory` Store because Artifacts requires explicit beta access. ### Browser Run bindings Register the Browser integration when trusted server code needs Cloudflare Browser Run sessions. ```ts [vite.config.ts] import { defineConfig } from 'vite' import { vitehub } from 'vite-hub' export default defineConfig({ plugins: [ vitehub({ preset: 'cloudflare', browser: true, }), ], }) ``` ViteHub writes the generated `browser` binding, a compatible default `compatibility_date`, and the `nodejs_compat` flag. Browser Definitions import runtime helpers from `vite-hub/browser`; provider modules and the generated `wrangler.json` are not application import surfaces. `wrangler dev` can run Browser Run against a local browser. Set `browser: { remote: true }` to keep Worker code local while connecting its Browser binding to Cloudflare, which is useful when a proof must exercise the hosted Browser Run service. ### Cloudflare Computer Boxes Cloudflare Computer is an optional preview runtime for [Boxes](https://vitehub.dev/docs/agents/boxes). The application configures its `withWorkspace()` Durable Object, execution backends, Worker Loader or Container bindings, migrations, compatibility flags, and deployment lifecycle. Pass the configured Durable Object namespace to ViteHub with `{ kind: 'cloudflare-computer' }`. ViteHub does not create or modify Computer infrastructure. Keep the Computer filesystem and backend configuration in the Cloudflare integration. Box callers supply inputs and select a registered backend ID without importing Computer filesystem or execution APIs. ## Production notes Cloudflare local development and deployed Workers do not always expose the same runtime behavior. Use Provider Output Contracts and Local Provider Runs for pull request checks, then keep Live Smoke thin against real Cloudflare deployments. ::warning Cloudflare Provider Output can require real Worker bindings such as D1, R2, KV, Rate Limiting, Queues, Durable Objects, Cloudflare Artifacts, or Agent state. Verify generated bindings before deploy, then smoke test the deployed Worker when runtime bindings matter. :: Agent Definitions run on Cloudflare through generated host output where the Agent integration owns the route. Keep model keys, Durable Object state bindings, and other Runtime Env in Worker bindings. ## Next steps - Use [Runtime and host support](https://vitehub.dev/docs/frameworks-hosts/support-matrix) for exact package and proof coverage. - Use [Provisioning](https://vitehub.dev/docs/development/provisioning) for resource creation. - Use [Provider output](https://vitehub.dev/docs/reference/provider-output) for generated artifact families. - Use [Verification](https://vitehub.dev/docs/development/verification) for Cloudflare proof tiers. # Deno Deno is an Agent Package runtime target and a host boundary for Deno-shaped Provider Output. ViteHub keeps Agent Definitions, Schedule Definitions, KV Stores, and Runtime Helpers portable; Deno-specific code stays in generated output and driver configuration. ## Deno boundaries | Concern | ViteHub boundary | | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Agent chat and webhook routes | Agent Package writes `.vitehub/agent/deno-server.ts` when `runtime: 'deno'` and hosted Agent Definitions exist. It mounts the conventional chat dispatcher and webhook route; route-enabled Channels select which Agents answer chat requests. | | Static cron schedules | Schedule Package writes `.vitehub/schedule/deno-cron.mjs` for Deno `Deno.cron` wake output. | | Lightweight state | KV Package can use `driver: 'deno-kv'` and native `Deno.openKv()`. | | Deployment | Deno Deploy owns app entrypoint configuration, environment variables, permissions, logs, and production rollout. | ## Deno output boundary The Agent integration selects Deno when generated Agent routes run through `Deno.serve`. Other primitives retain their package-owned runtime options. ```ts [vite.config.ts] import { hubAgent } from '@vite-hub/agent/vite' import { hubKv } from '@vite-hub/kv/vite' import { hubSchedule } from '@vite-hub/schedule/vite' import { defineConfig } from 'vite' export default defineConfig({ plugins: [ hubAgent({ runtime: 'deno' }), hubKv(), hubSchedule(), ], kv: { driver: 'deno-kv', }, }) ``` The generated Agent server imports discovered Agent Definitions and mounts both the webhook route pattern and the conventional `/api/_vitehub/agents/[agent]/chat` dispatcher. If Schedule output exists, the generated server loads `.vitehub/schedule/deno-cron.mjs` before serving requests. ## Generated output A production-shaped build writes the Deno files under `.vitehub`. In a Nuxt app, the Agent entrypoint is written under Nuxt's build directory at `.nuxt/vitehub/agent/deno-server.ts` by default; the Schedule output remains project-owned under `.vitehub/schedule`. ```bash [Terminal] pnpm build test -f .vitehub/agent/deno-server.ts find .vitehub -maxdepth 4 -type f | sort ``` The generated server runs locally with Deno network permission for the selected port. ```bash [Terminal] deno run --allow-net=127.0.0.1:8787 .vitehub/agent/deno-server.ts --host 127.0.0.1 --port 8787 ``` A route using `driver: 'deno-kv'` also requires Deno KV support. ```bash [Terminal] deno run --unstable-kv --allow-net=127.0.0.1:8787 .vitehub/agent/deno-server.ts --host 127.0.0.1 --port 8787 ``` For a single discovered `support` Agent with the default chat route enabled, the generated route accepts the following request. The target Agent must attach a route-enabled `webChat()` Channel; Agents without one remain unreachable through the dispatcher. ```bash [Terminal] curl -X POST http://127.0.0.1:8787/api/_vitehub/agents/support/chat \ -H 'content-type: application/json' \ -d '{"id":"local","messages":[{"id":"user-1","role":"user","parts":[{"type":"text","text":"ping"}]}]}' ``` ## Production notes Deno Deploy uses `.vitehub/agent/deno-server.ts` as the generated server entrypoint. For Nuxt, configure `.nuxt/vitehub/agent/deno-server.ts` instead, or the equivalent path under a custom `buildDir`. Do not import generated files from application code to work around deployment configuration; keep application code on Agent Definitions, Runtime Helpers, and stable ViteHub imports. Use Deno environment variables for model keys and other Runtime Env. If you use Deno KV, verify the deployed runtime can call `Deno.openKv()` and choose an explicit KV Store when local development must not share production state. ## Next steps - Use [Runtime and host support](https://vitehub.dev/docs/frameworks-hosts/support-matrix) for the qualified host boundary. - Use [Provider output](https://vitehub.dev/docs/reference/provider-output) for generated artifact boundaries. - Use [Generated files](https://vitehub.dev/docs/development/generated-files) to inspect `.vitehub/**`. - Use [Config options](https://vitehub.dev/docs/reference/config-options) for Agent `runtime` and KV `driver` placement. # Frameworks and hosts ViteHub discovers definitions during the Vite build, then prepares the files and bindings required by the selected host. Application code keeps using ViteHub imports instead of generated paths or provider SDKs. Host support remains package-specific. A host can support one primitive without supporting every ViteHub package, and a Runtime Helper can work without a ViteHub-generated deployment bundle. ## Choose a host | Need | Open | | ------------------------------------------------------- | ------------------------------------------------------------------------------------ | | Compare current host coverage and proof maturity | [Runtime and host support](https://vitehub.dev/docs/frameworks-hosts/support-matrix) | | Generate Cloudflare Worker output and bindings | [Cloudflare](https://vitehub.dev/docs/frameworks-hosts/cloudflare) | | Generate Vercel Build Output | [Vercel](https://vitehub.dev/docs/frameworks-hosts/vercel) | | Use package-specific Netlify functions and Blob runtime | [Netlify](https://vitehub.dev/docs/frameworks-hosts/netlify) | | Run Agent routes, schedules, or KV on Deno | [Deno](https://vitehub.dev/docs/frameworks-hosts/deno) | | Understand package-owned Nitro bridges | [Nitro and UnJS](https://vitehub.dev/docs/frameworks-hosts/nitro-unjs) | | Mount supported helpers in a Node-shaped server | [Node and self-hosted](https://vitehub.dev/docs/frameworks-hosts/node-self-hosted) | ## What the Vite integration does | Step | Result | | -------------------- | ------------------------------------------------------------------------------------ | | Discover definitions | Finds named Agents, Workspaces, queues, workflows, and other configured resources. | | Generate registries | Lets server code load discovered definitions by name. | | Resolve options | Applies the host and provider choices from `vite.config.ts`. | | Write host output | Generates only the bindings, routes, functions, or config supported by that package. | ## Compose integrations Use `vite-hub` in applications. Queue remains opt-in, and each feature keeps its own public import. ```ts [vite.config.ts] import { defineConfig } from 'vite' import { vitehub } from 'vite-hub' export default defineConfig({ plugins: [vitehub({ preset: "node" })], }) ``` Register an individual `hubX()` integration from its `@vite-hub/*/vite` package when a library or focused integration needs direct control. ## Keep runtime imports stable Import server APIs through documented ViteHub paths. Do not import framework virtual modules or generated files unless a reference page marks the path public. ```ts [server/settings.ts] import { kv } from 'vite-hub/kv' export async function saveSettings(settings: Record) { const [error] = await kv.set('settings', settings) if (error) throw error } ``` ## Inspect the output Vite development proves discovery and local generation. A production build creates the output for the selected host. Netlify development can also create functions for the Netlify CLI. ```bash [Terminal] pnpm dev find .vitehub -maxdepth 4 -type f | sort pnpm build ``` ## Next steps - Use [Runtime and host support](https://vitehub.dev/docs/frameworks-hosts/support-matrix) before making a portability claim. - Use [File conventions](https://vitehub.dev/docs/reference/file-conventions) for discovery paths. - Use [Provider output](https://vitehub.dev/docs/reference/provider-output) for generated host artifacts. - Use [Local development](https://vitehub.dev/docs/development) for proof paths. # Netlify Netlify support is package-specific. ViteHub currently provides Netlify-owned behaviour for Blob, generated Agent HTTP routes, and static Schedule wake functions; it does not expose a platform-wide Netlify provider for every primitive. ## Available boundaries | Surface | Current contract | | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Blob | `hubBlob()` selects the `netlify-blobs` driver when the build reports Netlify hosting. Application code continues to use `@vite-hub/blob`. | | Agent routes | `hubAgent()` writes one `vitehub-agent` function when hosted Agent Definitions exist. It mounts the conventional chat dispatcher and webhook route; route-enabled Channels select which Agents answer chat requests. `routes.discordGateway` remains explicit. | | Static schedules | `hubSchedule()` writes one scheduled Netlify function per discovered static Schedule Definition. | | Local proof | The repository runs a real-project fixture through Netlify CLI in pull-request CI. | Agent function output lives under `.netlify/v1/functions`, with its generated source wrapper under `.vitehub/agent/netlify-function.mjs`. The wrapper and deployed function are Provider Output, not public application imports. In a Nuxt app, the source wrapper follows Nuxt's build directory and is normally `.nuxt/vitehub/agent/netlify-function.mjs`; the deployed function path is unchanged. ## Package output composition Each active package integration contributes only its owned Netlify output. ```ts [vite.config.ts] import { hubAgent } from '@vite-hub/agent/vite' import { hubBlob } from '@vite-hub/blob/vite' import { hubSchedule } from '@vite-hub/schedule/vite' import { defineConfig } from 'vite' export default defineConfig({ plugins: [ hubBlob(), hubSchedule(), hubAgent(), ], }) ``` Netlify environment detection selects the Blob driver and Agent function output. Static Schedule Definitions generate Netlify functions alongside the other supported Schedule output families. ## Generated functions A Netlify-shaped build writes the generated functions and wrappers to their provider-owned directories. ```bash [Terminal] pnpm build find .netlify/v1/functions -maxdepth 2 -type f | sort find .vitehub/agent -maxdepth 2 -type f | sort ``` For Nuxt, replace the wrapper inspection command with `find .nuxt/vitehub/agent -maxdepth 2 -type f | sort`, or use the equivalent path under a custom `buildDir`. ## Unsupported inference ViteHub does not infer native Netlify providers for Queue, Workflow, or Sandbox. Disable an unused preset integration or select an explicit supported provider only when that external provider is valid from the Netlify runtime. The ViteHub Provision CLI does not create Netlify resources. It currently accepts Cloudflare and Vercel plans only. Netlify-specific KV Provider Output is also not provided. Configure a remote KV driver explicitly for deployed state; do not rely on the local `fs-lite` fallback in a serverless deployment. Workspace has no Netlify-specific hosted store. Select a durable remote Workspace store explicitly; the local filesystem fallback does not persist safely across serverless instances. ## Production proof Pull-request CI exercises the Netlify output through Netlify CLI. ViteHub does not currently publish a deployed Netlify Live Smoke, so verify the generated functions in the target Netlify site before treating an application-specific combination as production-proven. ## Related pages - [Runtime and host support](https://vitehub.dev/docs/frameworks-hosts/support-matrix) - [Provider output](https://vitehub.dev/docs/reference/provider-output) - [Import paths](https://vitehub.dev/docs/reference/import-paths) # Nitro and UnJS ViteHub is not a Nitro module system. ViteHub uses Vite Integrations as the public integration layer, while package-owned Nitro wiring appears only where a host boundary needs generated runtime hooks. ## Boundary | Layer | ViteHub expectation | | ----------------------- | ----------------------------------------------------------------------------------------------------- | | Vite | Public integration layer for discovery, generated files, the CLI Agent Dev Loop, and Provider Output. | | Nitro | Host runtime bridge when a package must register generated handlers, middleware, or runtime hooks. | | UnJS libraries | Useful implementation dependencies for server primitives, not public ViteHub framework identity. | | Application server code | Calls Runtime Helpers and stable handlers without importing generated Nitro internals. | ## Accepted Nitro handoffs | Bridge | Status | Owner | Purpose | | ------------------------------- | ---------------------------------------- | ----------------- | ------------------------------------------------------------------------------------ | | Schedule Provider Wake | Available | Schedule Package | Registers Cloudflare scheduled runtime hooks and cron output for Nitro-shaped hosts. | | Workspace hosted runtime setup | Available where hosted stores require it | Workspace Package | Moves generated Workspace runtime setup into Nuxt's top-level Nitro config. | | Database Nuxt D1 host wiring | Available for Nuxt D1 host resources | Database Package | Keeps one D1 Database Host Resource in sync with Nuxt Content and Cloudflare output. | | General Nitro-first integration | Not the public direction | Not applicable | ViteHub keeps the public contract on Vite Integrations. | ## Generated route output ::warning Auth and Agent integrations generate Nitro handlers for their owned routes. Treat those files as Provider Output, not as a general Nitro Framework Integration or a public `@vite-hub/*/nitro` authoring surface. :: | Output | Current owner | Boundary | | ----------------------------- | ------------- | -------------------------------------------------------------------------------------------------- | | Auth route handler | Auth Package | Exposes the configured Auth route through a generated Nitro handler. | | Agent chat and webhook routes | Agent Package | Dispatches generated Agent route output without making Nitro discovery or route files the app API. | ## Package-owned handlers The package Vite Integration reads the application Auth Definition and generates Nitro route output when `route` is enabled. ```ts [server/auth.ts] import { defineAuth } from '@vite-hub/auth' export default defineAuth({ appName: 'Acme', database: true, }) ``` ```ts [vite.config.ts] import { hubAuth } from '@vite-hub/auth/vite' import { defineConfig } from 'vite' export default defineConfig({ plugins: [ hubAuth(), ], }) ``` ## What not to do Do not treat Nitro route files as the primary ViteHub API. If a package generates Nitro output, inspect it as Provider Output and keep application code on the package's Runtime Helpers or stable server handler. ## Next steps - Use [Frameworks and hosts](https://vitehub.dev/docs/frameworks-hosts) for the public integration model. - Use [Runtime and host support](https://vitehub.dev/docs/frameworks-hosts/support-matrix) for the complete qualified matrix. - Use [Provider output](https://vitehub.dev/docs/reference/provider-output) for generated Nitro and host artifacts. - Use [Import paths](https://vitehub.dev/docs/reference/import-paths) for public imports. # Node and self-hosted Node and self-hosted runtimes can use ViteHub server primitives when the package exposes host-neutral Runtime Helpers or stable server handlers. Unified self-hosted Provider Output is not the default contract for every package. ## What works today | Surface | Status | Boundary | | ------------------------------------- | ----------------------------------------- | ---------------------------------------------------------------------------- | | Runtime Helpers in server code | Available per package | Use the package root or runtime subpath imports. | | Local filesystem and memory providers | Available where a primitive supports them | Useful for development and simple self-hosted setups. | | Stable server handlers | Available per package | Mount the handler the package marks stable, such as Auth server behavior. | | Unified self-hosted Provider Output | Not provided | ViteHub does not emit one general Node deployment bundle for all primitives. | ## Runtime Helper boundary Server code calls the same Runtime Helpers used in hosted applications. The provider or store choice remains in package configuration. ```ts [server/settings.ts] import { kv } from '@vite-hub/kv' export async function saveSettings(settings: Record) { const [error] = await kv.set('settings', settings) if (error) throw error } ``` ```ts [vite.config.ts] import { hubKv } from '@vite-hub/kv/vite' import { defineConfig } from 'vite' export default defineConfig({ plugins: [ hubKv(), ], kv: { driver: 'fs-lite', base: '.vitehub/data/kv', }, }) ``` ## Auth handler boundary The Auth Package exposes a stable server handler that a Node framework can mount through its request API. The handler comes from the application Definition; generated host files remain implementation details unless the package reference marks them public. ```ts [server/manual-auth-handler.ts] import { defineAuth } from '@vite-hub/auth' import { createAuthHandler } from '@vite-hub/auth/server' const definition = defineAuth({ appName: 'Acme', route: false, }) export const handleAuth = createAuthHandler(definition) ``` `handleAuth` accepts a Web `Request` and returns a `Promise`. Adapt that handler at the Node framework boundary instead of importing a generated Nitro route. ## Production notes Self-hosted deployments must make durability explicit. Memory providers and single-process local state are development providers, not production coordination systems. Use Server Env for runtime secrets, configure durable stores for stateful primitives, and add verification that starts the deployed Node process rather than only typechecking package code. ## Next steps - Use [Runtime and host support](https://vitehub.dev/docs/frameworks-hosts/support-matrix) for the qualified self-hosted boundary. - Use [Import paths](https://vitehub.dev/docs/reference/import-paths) for stable runtime imports. - Use [Config options](https://vitehub.dev/docs/reference/config-options) for local and hosted providers. - Use [Verification](https://vitehub.dev/docs/development/verification) to choose a self-hosted proof path. # Runtime and host support `Package-specific` means support belongs to the named package or generated output, not the host as a whole. ## Server primitives | Primitive | Local Vite | Cloudflare | Vercel | Netlify | Deno | Nitro and UnJS | Node and self-hosted | | ---------- | ---------------- | --------------------- | --------------------- | -------------------- | ---------------------- | -------------------- | --------------------- | | Browser | Local provider | Browser Run | — | — | — | — | Local provider | | Blob | `fs` | R2 | Vercel Blob | Netlify Blobs | S3-compatible | Host driver | `fs` or S3-compatible | | Database | SQLite | D1 | libSQL or D1 HTTP | libSQL | libSQL | Nuxt D1 | SQLite or libSQL | | Email | Unemail driver | Cloudflare Email | Unemail driver | Unemail driver | Unemail driver | Host driver | Unemail driver | | KV | `fs-lite` | Workers KV | Upstash Redis | Upstash Redis | Deno KV | Host driver | `fs-lite` or Upstash | | Queue | Discovery only | Cloudflare Queues | Vercel Queues | Cloudflare or Vercel | — | Cloudflare or Vercel | — | | Rate Limit | `memory` | Rate Limiting binding | — | — | — | Cloudflare | `memory` | | Realtime | `memory` | Durable Objects | — | — | — | Host authority | `memory` | | Sandbox | Box provider | Cloudflare Sandbox | Vercel Sandbox | Vercel Sandbox | — | Cloudflare or Vercel | Box provider | | Schedule | Local or process | Cron triggers | Vercel Cron Jobs | Scheduled functions | Standalone `Deno.cron` | Provider Wake | Process runtime | | Workflow | OpenWorkflow | Cloudflare Workflows | Vercel Workflow | OpenWorkflow | OpenWorkflow | Host provider | OpenWorkflow | | Workspace | Local or memory | Artifacts or GitHub | Vercel Blob or GitHub | GitHub | GitHub | Host store | Local or GitHub | Names in this table are concrete built-in providers or adapters. Browser Definitions currently require the Cloudflare preset; local Wrangler can connect to Browser Run with remote mode. Trusted local and self-hosted Node processes can call `createBrowser({ provider: localBrowser({ executablePath }) })`, but Browser Definitions do not select that provider. Email's boolean default selects Cloudflare Email only on the Cloudflare preset; every other host requires an explicit compatible Unemail driver. Realtime production uses Cloudflare Durable Objects or explicitly selected memory on a single-process Node server; distributed Vercel, Netlify, and Deno presets reject memory. A remote provider shown under Netlify, Deno, Nitro, or Node is an explicit package choice, not host inference. Local filesystem and memory options remain single-process development providers. Local Vite discovers Queue Definitions and generates provider output, but it does not deliver Queue Jobs. Netlify requires an explicit Cloudflare or Vercel Queue Provider because it cannot infer one. Netlify can use an explicit Vercel Sandbox provider when Vercel credentials are configured; the Cloudflare Sandbox provider requires a Cloudflare binding and cannot run on Netlify. ## Deployment and proof | Contract | Local Vite | Cloudflare | Vercel | Netlify | Deno | Nitro and UnJS | Node and self-hosted | | ------------------------- | ------------------- | -------------------- | -------------------- | ---------------------------- | ---------------------------- | ---------------------------- | ---------------------------- | | Runtime helpers | **Available** | **Package-specific** | **Package-specific** | **Package-specific** | **Package-specific** | **Package-specific** | **Package-specific** | | Local providers | **Available** | **Package-specific** | **Package-specific** | **Package-specific** | **Package-specific** | **Not provided** | **Local-only** | | Generated Provider Output | **Not provided** | **Package-specific** | **Package-specific** | **Package-specific** | **Package-specific** | **Package-specific** | **Not provided** | | Provision support | **Not provided** | **Package-specific** | **Package-specific** | **Not provided** | **Not provided** | **Not provided** | **Not provided** | | Contract tests | **Contract-tested** | **Contract-tested** | **Contract-tested** | **Contract-tested** | **Contract-tested** | **Contract-tested** | **Contract-tested** | | Local Provider Run | — | ✓ | ✓ | ✓ | — | — | — | | Live Smoke | — | ✓ | ✓ | **Live proof not published** | **Live proof not published** | **Live proof not published** | **Live proof not published** | Cloudflare's nightly run covers nine primitives, including Rate Limit. Vercel covers eight because ViteHub has no native Vercel Rate Limit driver. Browser and Agent routes have contract tests but are outside those deployed runs. ## Qualifications - **Local Vite:** Active integrations expose their package imports and generated registries. Blob `fs`, KV `fs-lite`, Rate Limit `memory`, and Workspace `local` or `memory` provide local state. A local build can still generate output for an explicit or inferred hosted provider. - **Cloudflare:** Blob, Database, KV, Queue, Rate Limit, Sandbox, Schedule, Workflow, and Workspace run in the live playground. Browser and Agent have package-owned output outside the nightly run. Enabled integrations compose the Worker, `wrangler.json`, bindings, callbacks, and runtime modules. ViteHub can provision R2 buckets, D1 databases, and Cloudflare Queues. - **Vercel:** Blob, Database, KV, Queue, Sandbox, Schedule, Workflow, and Workspace run in the live playground. Agent routes have separate package output outside the nightly run. Enabled integrations write Vercel Build Output, functions, routes, cron entries, and runtime modules. ViteHub can create a Blob store and configure the project environment. - **Netlify:** Blob uses `netlify-blobs`. Agent HTTP routes and static Schedules write functions under `.netlify/v1/functions`. CI runs the real-project fixture through Netlify CLI. ViteHub does not provide Netlify provisioning or published live proof. - **Deno:** Agent chat and webhook routes and KV with `deno-kv` are supported with their documented permissions. The standalone Schedule integration writes a `Deno.cron` entrypoint, but `vitehub({ preset: "deno", schedule: true })` rejects Schedule because that output is outside the deployed Nitro entrypoint. ViteHub does not generate a general Deno bundle or publish live proof. - **Nitro and UnJS:** Auth and Agent handlers, the Schedule Nitro bridge, Workspace runtime setup, and Database Nuxt D1 wiring are package-owned integrations. Nitro is integration glue rather than a storage or execution provider. ViteHub does not provide Nitro provisioning or one unified live matrix. - **Node and self-hosted:** Server APIs and handlers run when their selected driver supports Node. Blob `fs`, KV `fs-lite`, Rate Limit `memory`, and Workspace `local` or `memory` are single-process providers. ViteHub does not emit one Node deployment bundle, provision a self-hosted plan, or publish one live suite. Local memory and filesystem providers stay single-process after deployment. Generated files remain package-owned and must not be imported by application code. # Vercel Vercel is a Provider Selection for packages that can emit Vercel Build Output, functions, queues, workflows, blob-backed storage, or sandbox integration. ViteHub keeps Definitions portable and moves Vercel-specific behavior into package Integration Options and Provider Output. ## Vercel boundaries | Concern | ViteHub boundary | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | Build Output | Generated `.vercel/output/**` files written by package integrations. | | Vercel Blob | Blob Store or Workspace Store configuration, depending on which primitive owns the behavior. | | Vercel Queues | Queue provider configuration and generated callback output. | | Vercel Workflow | Workflow provider configuration and generated runtime output. | | Vercel Sandbox | Sandbox Provider configuration, with Sandbox Identity passed only when the run needs reuse. | | External Database | A Database Definition backed by Cloudflare D1 over authenticated HTTP, or Database integration `connection` backed by hosted libSQL. | | Credentials | `VERCEL_TOKEN` and `VERCEL_PROJECT_ID` for Blob Provision, with an optional team id; Server Env for app runtime secrets. | ## Provider-owned configuration The package that owns a primitive also owns its Vercel selection. Provider fields remain in Integration Options when they affect generated output rather than one runtime invocation. ```ts [vite.config.ts] import { hubBlob } from '@vite-hub/blob/vite' import { hubQueue } from '@vite-hub/queue/vite' import { defineConfig } from 'vite' export default defineConfig({ plugins: [ hubBlob(), hubQueue(), ], blob: { driver: 'vercel-blob', }, queue: { provider: 'vercel', region: 'iad1', }, }) ``` ::warning Vercel-hosted state needs hosted stores. Use `driver: 'vercel-blob'` with `BLOB_READ_WRITE_TOKEN` for Blob, and use Upstash-backed KV with `KV_REST_API_URL` and `KV_REST_API_TOKEN` when KV runs on Vercel. Local filesystem stores are development-only in Vercel deployments. :: Database Definitions own tables and identity, while the Database Integration selects the hosted connection used by Vercel output. Use Runtime Env declarations for Marketplace-provisioned credentials so generated output reads them at runtime. A Definition uses Cloudflare D1 from Vercel only when it declares `cloudflare.http`. Set it to `true` for Cloudflare's D1 raw API, then configure `CLOUDFLARE_ACCOUNT_ID` and `CLOUDFLARE_API_TOKEN` as Vercel Server Env. Cloudflare deployments still prefer the D1 binding. ```ts [server/databases/config.ts] import { defineDatabase } from '@vite-hub/database' import { notes } from './schema' export default defineDatabase({ cloudflare: { databaseId: process.env.CLOUDFLARE_D1_DATABASE_ID, databaseName: process.env.CLOUDFLARE_D1_DATABASE_NAME, http: true, }, schema: { notes }, }) ``` For sustained application traffic, Cloudflare recommends a proxy Worker because its built-in D1 REST API is intended primarily for administrative use and shares the global Cloudflare API rate limit. Set `cloudflare.http` to `{ url, authToken }` for an authenticated raw-compatible HTTP(S) proxy. Omitting `cloudflare.http` preserves the hosted libSQL selection even when the Definition includes a D1 database id. ```ts [vite.config.ts] import { hubDb } from '@vite-hub/database/vite' import { env, hubEnv } from '@vite-hub/env/vite' import { defineConfig } from 'vite' export default defineConfig({ plugins: [ hubEnv(), hubDb({ connection: { url: env({ source: env.source('TURSO_DATABASE_URL') }), authToken: env({ secret: true, source: env.source('TURSO_AUTH_TOKEN') }), }, }), ], }) ``` ## Provision boundary Provision exposes a dry-run plan before applying actions. `VERCEL_TEAM_ID` or `VERCEL_ORG_ID` supplies team scope when the token requires it. ```bash [Terminal] VERCEL_TOKEN=... VERCEL_PROJECT_ID=... pnpm vitehub provision run --provider vercel --dry-run VERCEL_TOKEN=... VERCEL_PROJECT_ID=... VERCEL_TEAM_ID=... pnpm vitehub provision run --provider vercel ``` ## Generated output Vercel output appears under `.vercel/output`. Server functions include their own `.vc-config.json`, while the root output config describes routing and build metadata. ```bash [Terminal] pnpm build find .vercel/output -maxdepth 4 -type f | sort ``` ## Production notes Vercel provider output makes provider-specific runtime packages reachable only when selected. If a build bundles an unselected provider dependency, treat that as a Provider Output Contract issue in the owning package. Agent Definitions run on Vercel through generated host output where the Agent integration owns the route. Keep model keys, state credentials, and other Runtime Env in Vercel environment variables. ## Next steps - Use [Runtime and host support](https://vitehub.dev/docs/frameworks-hosts/support-matrix) for exact package and proof coverage. - Use [Provisioning](https://vitehub.dev/docs/development/provisioning) for provider resource ids. - Use [Config options](https://vitehub.dev/docs/reference/config-options) for Provider Selection placement. - Use [Provider output](https://vitehub.dev/docs/reference/provider-output) for generated Vercel output. # First Agent An Agent is a server file that tells ViteHub what to run. Every Agent needs a Driver, which can be a function, model, or coding provider such as Codex or Claude Code, among others. You can add Capabilities, Channels, Workspace access, and other options later. This tutorial starts with a function that returns a fixed greeting. It runs offline and needs no credentials. ::note You need Node.js 24.15 or newer and `pnpm` . This project runs completely offline. :: ## Install ViteHub and the server packages Create an empty project, then install ViteHub with Vite and H3. ```bash [Terminal] mkdir vitehub-agent-start cd vitehub-agent-start pnpm init pnpm pkg set type=module pnpm add vite-hub h3 vite ``` ## Configure the server build Add `vitehub()` to the Vite config. Vite builds `src/server.ts` for Node.js, and ViteHub discovers Agent Definitions under `server/agents`. ```ts [vite.config.ts] import { resolve } from "node:path" import { defineConfig } from "vite" import { vitehub } from "vite-hub" export default defineConfig({ root: import.meta.dirname, appType: "custom", build: { outDir: "dist", rollupOptions: { input: resolve(import.meta.dirname, "src/server.ts"), output: { entryFileNames: "server.js" }, }, ssr: true, }, plugins: [vitehub({ preset: "node", agent: true, env: false, })], ssr: { external: ["vite-hub/agent"], }, }) ``` ## Define the greeting Agent Create `server/agents/greeting.ts`. Its required `driver.run` function reads the prompt and returns the greeting without calling a provider. ```ts [server/agents/greeting.ts] import { defineAgent } from "vite-hub/agent" export default defineAgent({ driver: { run({ prompt }) { const name = typeof prompt === "string" ? prompt : "friend" return { text: `Hello, ${name}. This result came from an Agent Invocation.`, } }, }, }) ``` ## Call the Agent from H3 `runAgent()` takes the Definition, runtime values for the current request, and the invocation input. The route creates a new memo cache for each request, identifies Vite as the runtime, and reports errors from background tasks. ```ts [src/server.ts] import { createServer } from "node:http" import { H3, readBody } from "h3" import { toNodeHandler } from "h3/node" import { runAgent } from "vite-hub/agent" import greeting from "../server/agents/greeting" function createMemo() { const values = new Map() return (key: string, create: () => T): T => { if (!values.has(key)) values.set(key, create()) return values.get(key) as T } } const app = new H3().post("/greet", async (event) => { const body = await readBody<{ name?: string }>(event) || {} return await runAgent(greeting, { memo: createMemo(), runtime: "vite", waitUntil: task => { void task.catch(error => console.error(error)) }, }, { prompt: body.name?.trim() || "friend", }) }) const port = Number(process.env.PORT || 5173) createServer(toNodeHandler(app)).listen(port, () => { console.log(`ViteHub Agents tutorial listening on http://localhost:${port}`) }) ``` The route imports the Definition directly, so the greeting returns in the same request. ## Run the Agent and see the response Build the project and start the generated Node.js server. ```bash [Terminal] pnpm vite build node dist/server.js ``` From another terminal, send a name to the H3 route. ```bash [Terminal] curl -X POST http://localhost:5173/greet \ -H 'content-type: application/json' \ -d '{"name":"Ada"}' ``` The Agent returns the greeting: ```json [Response] {"text":"Hello, Ada. This result came from an Agent Invocation."} ``` From here, add only what your Agent needs: - Read [Agent Definitions](https://vitehub.dev/docs/agents/agent-definitions) to choose another Driver or add Channels, Workspace context, trusted caller settings, or hooks. - Read [Capabilities](https://vitehub.dev/docs/capabilities) before you give a model tools, triggers, policy, metadata, or context values. - Read [Invocations](https://vitehub.dev/docs/agents/invocations) when the route needs streaming or failure handling. # First Server Primitive This quickstart adds a local KV store to a small H3 server. One request writes a value, reads it back, and returns the result. ::note You need Node.js 24.15 or newer and `pnpm` . The first result runs locally without an account or credential. :: ## Install KV Create an empty project and install ViteHub with Vite and H3. ```bash [Terminal] mkdir vitehub-kv-start cd vitehub-kv-start pnpm init pnpm pkg set type=module pnpm add vite-hub h3 vite ``` ## Configure the Vite Integration Register `vitehub()` and select the file-backed local KV driver. The explicit configuration stores values under `.vitehub/data/kv`. ```ts [vite.config.ts] import { resolve } from "node:path" import { defineConfig } from "vite" import { vitehub } from "vite-hub" export default defineConfig({ root: import.meta.dirname, appType: "custom", build: { outDir: "dist", rollupOptions: { input: resolve(import.meta.dirname, "src/server.ts"), output: { entryFileNames: "server.js" }, }, ssr: true, }, plugins: [ vitehub({ preset: "node", blob: false, env: false, kv: { driver: "fs-lite", base: ".vitehub/data/kv" }, }), ], }) ``` ## Write and read one value Create one H3 route and use `kv` to write and read the setting. ```ts [src/server.ts] import { createServer } from "node:http" import { H3, readBody } from "h3" import { toNodeHandler } from "h3/node" import { kv } from "vite-hub/kv" const app = new H3().post("/settings", async (event) => { const settings = await readBody<{ theme: string }>(event) const [writeError] = await kv.set("settings", settings) if (writeError) throw writeError const [readError, storedSettings] = await kv.get("settings") if (readError) throw readError return { settings: storedSettings } }) const port = Number(process.env.PORT || 5173) createServer(toNodeHandler(app)).listen(port, () => { console.log(`ViteHub KV tutorial listening on http://localhost:${port}`) }) ``` ## Run the server Build and start the generated Node.js entry. ```bash [Terminal] pnpm vite build node dist/server.js ``` Send a value from another terminal. ```bash [Terminal] curl -X POST http://localhost:5173/settings \ -H 'content-type: application/json' \ -d '{"theme":"system"}' ``` The response proves that the route wrote and read through ViteHub: ```json [Response] {"settings":{"theme":"system"}} ``` To move to a hosted store, change the provider in `vite.config.ts`. The server route keeps importing `kv` from `vite-hub/kv`. ## Next steps - Follow the longer [Server Primitives tutorial](https://vitehub.dev/blog/server-primitives) for a complete walkthrough. - Read [KV](https://vitehub.dev/docs/server-primitives/kv) for named stores and hosted drivers. - Read [Runtime Helpers and stable imports](https://vitehub.dev/docs/concepts/runtime-helpers-and-stable-imports) to see how provider changes stay out of server code. # Introduction ViteHub adds a server layer to Vite. Server Primitives give application code APIs for storage, background work, auth, and other server features. Agents combine those APIs with models, coding providers, or application code. Agents can use Server Primitives. Server Primitives also work on their own. Start with the path that matches the result your product needs today. ::u-page-grid{.not-prose.mt-8.sm:grid-cols-2} :::u-page-card --- description: Store a value in local KV and read it from a server route. icon: i-lucide-server-cog title: Use a Server Primitive to: https://vitehub.dev/docs/getting-started/first-server-primitive --- ::: :::u-page-card --- description: Define an Agent and inspect the result of one invocation. icon: i-lucide-bot title: Run an Agent to: https://vitehub.dev/docs/getting-started/first-agent --- ::: :: ## Choose a first path Start with Server Primitives when application code needs auth, environment values, storage, files, background work, or isolated execution. Your server code calls the ViteHub API, and the Vite integration connects it to the selected provider. Start with Agents when the product needs a named server-side actor. An Agent Definition puts its instructions, execution method, Capabilities, and Workspace in one file. | You want to build | Start here | | ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | | Settings, feature flags, caches, cursors, or small records | [First Server Primitive](https://vitehub.dev/docs/getting-started/first-server-primitive) | | A support, coding, research, or workspace-aware actor | [First Agent](https://vitehub.dev/docs/getting-started/first-agent) | | Relational data, uploads, background work, workflows, schedules, or sandboxes | [Server Primitives](https://vitehub.dev/docs/server-primitives) | | Model-facing tools, guarded product abilities, or chat entry points | [Capabilities](https://vitehub.dev/docs/capabilities) | ## What ViteHub does Most features use the same path: | Part | What it does | | ---------------- | ------------------------------------------------------------------------------------------------------- | | Vite integration | Finds definitions and prepares the selected provider during development and build. | | Definition | Declares named work or state, such as an Agent, Workspace, Queue, Workflow, or Schedule. | | Server API | Lets application code call a feature through an import such as `kv`, `useWorkspace()`, or `runAgent()`. | | Capability | Lets an Agent use a selected operation such as `workspaceShell()` or `kv()`. | Application code uses ViteHub imports. The integration handles the provider-specific routes, bindings, and files. ## Verify your setup Check the ViteHub plugin in `vite.config.ts`, the definition file when the feature needs one, and the ViteHub call in server code. Each first guide ends with a response you can inspect before adding another feature. ## Next steps - Read [Installation](https://vitehub.dev/docs/getting-started/installation) to start with the framework distribution or choose a direct owner package for advanced composition. - Follow the longer [Server Primitives tutorial](https://vitehub.dev/blog/server-primitives). - Follow the longer [Agents tutorial](https://vitehub.dev/blog/agents). - Open [Concepts](https://vitehub.dev/docs/concepts) when you need the full runtime model. # Installation Install `vite-hub` when you are building an application. It provides the Vite integration and public feature imports through one dependency. ## Prerequisites - Node.js 24.15 or newer. - Vite 8 or newer. - An ESM package with `"type": "module"` or a `vite.config.mts` file. - A package manager such as `pnpm`, `npm`, `yarn`, or `bun`. Model providers and hosted primitives may require credentials. Each feature guide lists its own environment, network, and billing prerequisites before the first call. ## Install the framework distribution Add `vite-hub` to an existing Vite application. ```bash [Terminal] pnpm add vite-hub ``` Register the framework integration in Vite. ```ts [vite.config.ts] import { vitehub } from "vite-hub" import { defineConfig } from "vite" export default defineConfig({ plugins: [ vitehub({ preset: "node" }), ], }) ``` In Nuxt, register the framework module. It installs the same Vite integrations and carries their Nitro configuration through Nuxt's lifecycle. ```ts [nuxt.config.ts] import viteHubNuxt from "vite-hub/nuxt" export default defineNuxtConfig({ modules: [ [viteHubNuxt, { preset: "node" }], ], }) ``` Import application APIs from explicit feature subpaths. ```ts [server/agents/support.ts] import { defineAgent } from "vite-hub/agent" import { access } from "vite-hub/agent/capabilities" import { requireRateLimit } from "vite-hub/rate-limit" import { defineWorkspace } from "vite-hub/workspace" ``` Install third-party model providers and chat adapters separately. Built-in coding providers use the provider runtime pinned by ViteHub. The distribution includes the Workflow DevKit runtime and builders for Vercel Workflow; install other provider SDKs only when you use them. Until T3 publishes the provider runtime on npm, pnpm consumers using a built-in coding provider must set `blockExoticSubdeps: false` in `pnpm-workspace.yaml`; ViteHub pins an exact pkg.pr.new tarball rather than a moving branch. ## Install an owner package directly Every `@vite-hub/*` package can also be installed on its own. Use a package directly when you are building a library or need to configure one integration without the framework distribution. | Path | Direct install | Integration | | ----------------- | ------------------------------------ | ------------------------------------------------- | | Server Primitives | `pnpm add @vite-hub/kv vite` | `hubKv()` from `@vite-hub/kv/vite` | | Rate Limit | `pnpm add @vite-hub/rate-limit vite` | `hubRateLimit()` from `@vite-hub/rate-limit/vite` | | Agents | `pnpm add @vite-hub/agent vite` | `hubAgent()` from `@vite-hub/agent/vite` | ::tip Start new applications with `vite-hub` . Direct owner packages are the escape hatch when package-level control is the goal. :: ## Add generated types Some packages write types under `.vitehub/types`. Include that directory when your application uses generated names or stable `#vitehub/...` imports. ```json [tsconfig.json] { "include": [ "server/**/*.ts", "src/**/*.ts", ".vitehub/types/**/*.d.ts" ] } ``` ## Verify the integration Run the application with its Vite-based development command. If a server API is missing its Vite integration, ViteHub reports the configuration error instead of selecting another provider. The two first-success guides include complete build and runtime commands: - [First Server Primitive](https://vitehub.dev/docs/getting-started/first-server-primitive) stores and reads a KV value without credentials. - [First Agent](https://vitehub.dev/docs/getting-started/first-agent) runs a deterministic Agent Invocation without a model key. ## Next steps - Read [Vite Integrations and Provider Output](https://vitehub.dev/docs/concepts/vite-integrations-and-provider-output) to understand integration ownership. - Open [Server Primitives](https://vitehub.dev/docs/server-primitives) to choose infrastructure. - Open [Agents](https://vitehub.dev/docs/agents) to choose an Agent Driver and Capabilities. # ViteHub docs ViteHub adds a server layer to Vite. Use **Server Primitives** directly from application code, or combine them with models and tools in an **Agent**. Server Primitives work without an Agent. When you build an Agent, Capabilities control which operations it can use. ::u-page-grid{.not-prose.mt-8} :::u-page-card --- description: Add auth, storage, queues, schedules, sandboxes, and other server APIs. icon: i-lucide-server-cog title: Server primitives to: https://vitehub.dev/docs/server-primitives --- ::: :::u-page-card --- description: Define an Agent, select its Capabilities and Workspace, then run and inspect it. icon: i-lucide-bot title: Agents to: https://vitehub.dev/docs/agents --- ::: :::u-page-card --- description: Install ViteHub and run one Server Primitive or Agent. icon: i-lucide-rocket title: Get started to: https://vitehub.dev/docs/getting-started --- ::: :::u-page-card --- description: Learn how definitions, integrations, Workspaces, Sources, and Capabilities fit together. icon: i-lucide-map title: Concepts to: https://vitehub.dev/docs/concepts --- ::: :: ## Find what you need | You are building | Start with | | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | | A new installation | [Get started](https://vitehub.dev/docs/getting-started) | | Auth, storage, background work, schedules, sandboxes, or environment values | [Server primitives](https://vitehub.dev/docs/server-primitives) | | Model-backed actors, Agent Invocations, triggers, chat history, evals, or CLI inspection | [Agents](https://vitehub.dev/docs/agents) | | Tools and product operations that an Agent can use | [Capabilities](https://vitehub.dev/docs/capabilities) | | The difference between definitions, server APIs, Workspaces, Sources, and Capabilities | [Concepts](https://vitehub.dev/docs/concepts) | | Host support, generated output, or deployment support | [Runtime and host support](https://vitehub.dev/docs/frameworks-hosts/support-matrix) | ## How ViteHub connects your code to a host Most features use this path: 1. Start with [Installation](https://vitehub.dev/docs/getting-started/installation). 2. Register `vitehub()` in the Vite configuration. 3. Add a definition when the feature needs a name, schema, or reusable configuration. 4. Call the documented server API from application code. 5. Attach a Capability if an Agent needs to call that operation. The Vite integration discovers definitions and prepares the provider-specific files for the selected host. Application code keeps using ViteHub imports. ## Check host support Not every Server Primitive runs on every host. Check the [runtime and host support matrix](https://vitehub.dev/docs/frameworks-hosts/support-matrix) before you choose a deployment target. Open [Installation](https://vitehub.dev/docs/getting-started/installation) for a runnable path, or read [Concepts](https://vitehub.dev/docs/concepts) when you need the runtime model. # Channels `vite-hub/channels` gives server code one named destination for outbound messages. You define the connectors that a Channel can use, then call `useChannel(name).send(text, options)` from an H3 or Nitro handler. ## Enable Channel discovery Add the Channels integration to your Vite config. ViteHub then discovers files below `server/channels` and files that end in `.channel.ts`. ```ts [vite.config.ts] import { defineConfig } from 'vite' import { vitehub } from 'vite-hub' import { env } from 'vite-hub/env' export default defineConfig({ plugins: [vitehub({ preset: 'node', channels: true })], env: { server: { telegram: { botToken: env({ secret: true, source: env.source('TELEGRAM_BOT_TOKEN'), }), }, }, }, }) ``` ## Define a named Channel Create `server/channels/alerts.ts`. Read typed Server Env inside the connector's `send()` method so the value is resolved when the message is delivered. Unseal a secret only when the provider call needs the raw value. ```ts [server/channels/alerts.ts] import { defineChannel } from 'vite-hub/channels' import { useServerEnv } from '#vitehub/env/server' type TelegramOptions = { chatId: string } export default defineChannel({ connectors: { telegram: { async send(text: string, { chatId }: TelegramOptions) { const { telegram } = useServerEnv() const response = await fetch(`https://api.telegram.org/bot${telegram.botToken.unseal()}/sendMessage`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ chat_id: chatId, text }), }) if (!response.ok) throw new Error(`Telegram returned ${response.status}.`) const result = await response.json() as { result?: { message_id?: number } } return { id: result.result?.message_id?.toString() } }, }, }, }) ``` This example calls Telegram directly to keep the connector contract visible; use a provider client when your application already has one. Channels does not bundle provider adapters. For a connector that does not need credentials, omit the `useServerEnv()` call. The file name becomes the Channel name. For a Vite suffix definition, use `src/alerts.channel.ts` instead; both forms discover the same `alerts` Channel. ## Send from an H3 or Nitro handler `useChannel()` returns immediately. `send()` performs the connector call and returns a normalized result with the Channel name, connector name, ViteHub delivery id, and optional provider message id. ```ts [server/api/build-finished.post.ts] import { defineEventHandler } from 'h3' import { useChannel } from 'vite-hub/channels/server' export default defineEventHandler(async () => { return await useChannel('alerts').send('Build finished.', { connector: 'telegram', chatId: 'build-room', }) }) ``` The handler returns a result like this: ```json { "channel": "alerts", "connector": "telegram", "deliveryId": "e4875238-2922-4787-9f7f-b13e2e7839be", "id": "1730000000000" } ``` Each send emits metadata-only JSON events with the `vitehub.channel.send` scope for `started`, `completed`, and `failed`. The events include `deliveryId`, Channel, connector, provider message id, and error message, but never include message text or connector options. This outbound-only package has no State Adapter, so durability comes from the application's configured log drain; use Agent Channels when inbound custody and recovery are required. ## Add another connector Add another entry to `connectors` when the same logical destination can deliver through more than one provider. Each entry defines its own options, so Telegram can require `chatId` while Slack requires `channelId` and optionally accepts `threadTs`. ```ts await useChannel('alerts').send('Build finished.', { connector: 'slack', channelId: 'builds', threadTs: '1730000000.000100', }) ``` Keep `connector` explicit when a Channel has more than one delivery path. This makes the delivery choice visible at each call site. ## Know what this primitive includes Channels provide discovery, connector selection, and a normalized outbound send contract. The current package does not ship Telegram or Slack adapters and does not generate inbound webhook routes; implement those connectors on top of the contract or add them as a later provider package. `vite-hub/channels` is separate from `vite-hub/agent/channels`. Ordinary Channels send application messages. Agent Channels describe Agent conversation origins, inbound events, and Agent delivery policy; the Agent API stays unchanged. See [Agent Channels](https://vitehub.dev/docs/agents/channels) when the destination starts or drives an Agent Invocation. # Config options Integration Options configure ViteHub package integrations. Provider Selection belongs in Integration Options when it changes generated output, bindings, imports, or deployment behavior. ## Built-in deployment preset `vitehub()` requires exactly one built-in `preset`: `cloudflare`, `netlify`, `vercel`, `deno`, or `node`. The selection is the single source for host identity, runtime, Nitro output, packaging, and built-in Blob, Queue, Rate Limit, and Sandbox adapters. Conflicting Nitro or hosting environment selections fail configuration. `name` is ViteHub's logical deployment identity. Cloudflare Workers Builds supplies its connected Worker through `WRANGLER_CI_OVERRIDE_NAME`, which the Cloudflare preset resolves below explicit `name` and above the nearest `package.json` name and Vite root directory name. A differing explicit ViteHub identity fails because the connected Worker remains the deployment target. Cloudflare uses the resolved identity for default Worker, Blob bucket, Queue prefix, Rate Limit namespace, Sandbox, and Container names. Explicit Wrangler Worker names remain authoritative outside Workers Builds, while explicit Blob bucket, driver, or store options still win. This fallback derives deterministic names but does not provision the corresponding R2 bucket or Queue. The generated deployment manifest records the resolved identity and its source. | Import | Public type | Placement | Defaults | | ---------- | ---------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `vite-hub` | `ViteHubOptions` | `vitehub({ preset })` in Vite `plugins` | Composes Env. Agent, Auth, Blob, Browser, Channels, Database, KV, Queue, Rate Limit, Sandbox, Schedule, Workflow, and Workspace are enabled with `true` or explicit options. On Cloudflare, Email also supports `true`; other presets reject the Cloudflare-only default. | Unsupported requested capabilities fail before a production build can silently select a weaker provider. The `node` preset intentionally exposes its filesystem Blob store as single-host and its memory Rate Limiter as single-process. The `deno` preset rejects Schedule and `agent.runtime: "deno"` because those generated servers are not part of its deployed Nitro entrypoint. Deno output includes runtime package staging, a validated deployment manifest, and a non-interactive create-or-update runner. Direct `hubX()` integration functions remain available from their independent `@vite-hub/*/vite` owner-package paths. The root `vitehub()` facade enables Agent, Blob, Browser, Channels, Database, KV, Queue, Rate Limit, Sandbox, Schedule, Workflow, and Workspace with `true`. Email accepts `true` with the Cloudflare preset, where it selects the Cloudflare Email driver; other presets reject that boolean default and require explicit provider options. Direct owner-package integrations retain their detailed option types. Auth follows the same opt-in shape but currently has no plugin option bag. ## Vite Integration options | Package | Public type | Placement | Confirmed options and defaults | | ---------- | -------------------------------------------------- | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Agent | `AgentModuleOptions` | `agent` config key or `hubAgent(options)` | Omission or `false` disables Agent in `vitehub()`; `true` enables inferred defaults, and an options object enables and configures it. `runtime`: `auto`, `cloudflare-agents`, `deno`, `unknown`, `vercel`, `vite`; default `auto`. `execution`: `inline`, `sandbox`, `workflow`; default `inline`. `imports` defaults to `true`. `integrations.sandbox` and `integrations.workflow` default to `auto`. Provider groups `sandbox`, `scheduler`, and `state` default to provider `auto`. Automatic state uses Cloudflare state on Cloudflare and local SQLite at `file:.vitehub/data/agent-state.sqlite` during Vite development; production output requires a durable `VITEHUB_AGENT_STATE_URL` or explicit provider options. Hosted Agent Definitions mount `/api/_vitehub/agents/[agent]/chat`; each Agent's route-enabled Channel controls whether it answers. Webhook routes remain Channel-owned and available for adapter delivery. Set `routes.discordGateway` to generate the Discord Gateway listener route; `true` selects the package default route. `routes.inspection` is disabled by default; `true` mounts `/api/_vitehub/agents/[agent]/inspection`, while a string selects a custom route. Inspection includes operational metadata and does not add authorization. The host must authorize the route before ViteHub resolves the Agent. | | Auth | `AuthModuleOptions` | `auth` config key or `hubAuth(options)` | `false` disables the integration. The enabled integration has no plugin option bag yet. `defineAuth()` owns `basePath` default `/api/auth`, `route: false`, `access`, `database`, `secondaryStorage`, and `runtime`. | | Blob | `BlobModuleOptions` | `blob` config key or `hubBlob(options)` | Omission or `false` disables Blob in `vitehub()`; `true` enables the selected preset's store, and an options object enables and configures it. Driver literals include `fs`, `cloudflare-r2`, `netlify-blobs`, `vercel-blob`, `minio`, `s3`, `gcs`, `azure`, and other exported Blob drivers. Defaults: Cloudflare hosting selects `cloudflare-r2` binding `BLOB`; Netlify hosting selects `netlify-blobs`; `BLOB_READ_WRITE_TOKEN` or Vercel hosting selects `vercel-blob` with `access: "public"`; otherwise the integration selects `fs` at `.vitehub/data/blob`. MinIO defaults to bucket `vitehub-blob`, endpoint `http://localhost:9000`, region `us-east-1`, and `forcePathStyle: true`. | | Browser | `BrowserModuleOptions` | `browser` config key or `hubBrowser(options)` | Omission or `false` disables Browser in `vitehub()`; `true` enables Cloudflare Browser Run actions with binding `BROWSER`, `{ binding }` changes the binding name, and `remote: true` connects local Wrangler development to the hosted service. Browser Definitions currently require the Cloudflare preset. The root integration and direct standalone `hubBrowser()` output generate the binding and required compatibility fields while preserving unrelated Wrangler fields. | | Channels | `ChannelsVitePluginOptions` | `channels` config key or `hubChannels(options)` | Omission or `false` disables Channel discovery in `vitehub()`; `true` discovers `server/channels/.ts` and `.channel.ts`. `projectRoot` changes where ViteHub looks for those files. Connectors and provider credentials belong in the Channel Definition. | | Console | `boolean` | `console` config key in `vitehub()` | Omission or `false` registers no Console page, API handler, plugin, or assets. `true` mounts the complete read-only [Console](https://vitehub.dev/docs/development/console) at `/_vitehub`, its API at `/api/_vitehub/console/**`, and a fallback SQLite invocation journal at `.vitehub/data/console.sqlite`. Production builds currently require the Node preset. The host must protect both route groups. | | Database | `DBModulePublicOptions` | `database` config key or `hubDb(options)` | Omission or `false` disables Database in `vitehub()`; `true` enables inferred defaults, and an options object enables and configures it. `projectRoot` sets the Database discovery, generated-artifact, and provisioning root; relative paths resolve from the Vite root in Vite and the Nuxt `rootDir` in Nuxt. Integration options are `cli.generate` and `cli.migrate`, each disableable with `false`. `connection` supplies a hosted libSQL default for Vercel and other hosted output. Cloudflare D1 runtime fields are `driver: "d1"`, `binding`, `databaseId`, `previewDatabaseId`, `databaseName`, `migrationsTable`, and `local.filename`. Database Definitions own tables and may override integration connection values. | | Email | `EmailVitePluginOptions` | `email` config key or `hubEmail(options)` | `driver` is required for explicit options and selects an exact `unemail/driver/*` subpath; `options` accepts serializable literals and runtime Env declarations. Omission disables Email in `vitehub()`. The root package also accepts `email: true` on Cloudflare and rejects it on other presets. Markdown under `server/emails/**/*.md` is discovered recursively and exposed through typed `#vitehub/emails/` renderer imports. | | Env | `EnvIntegrationOptions` and `EnvViteConfigOptions` | `hubEnv(options)` plus Vite `env` config | `diagnostics`: `off`, `summary`, `trace`; default `summary`. `prefix` changes inferred environment variable names. `projectRoot` changes generated file placement. Vite `env.public`, `env.define`, and `env.server` own Public Env, build define values, and Server Env declarations. | | KV | `KVModuleOptions` | `kv` config key or `hubKv(options)` | Accepts `false`, one store config, or `{ stores }` with `stores.default`. Driver literals are `fs-lite`, `cloudflare-kv-binding`, `deno-kv`, and `upstash`. Defaults: Deno hosting selects `deno-kv`; Upstash env selects `upstash`; Vercel hosting selects `upstash`; Cloudflare hosting selects `cloudflare-kv-binding` binding `KV`; otherwise `fs-lite` at `.vitehub/data/kv`. | | Queue | `QueueModuleOptions` | `queue` config key or `hubQueue(options)` | `false` disables the integration. When active, `provider` is `cloudflare` or `vercel`; Cloudflare hosting selects `cloudflare`, and other supported hosts select `vercel`. Netlify does not infer a provider. Shared `cache` belongs here. Cloudflare uses `binding`; Vercel uses `region`. Queue concurrency and retry behaviour belong to Queue Definition or enqueue options. | | Rate Limit | `RateLimitVitePluginOptions` | `rateLimit` config key or `hubRateLimit(options)` | `provider`: `auto`, `cloudflare`, or `memory`; default `auto`. Auto selects memory for Vite serve and Cloudflare for a known Cloudflare production host. Cloudflare requires a deployment-unique `namespace`. `projectRoot` and `scanDirs` are source-collection escape hatches. Handler-local `requireRateLimit()` calls own static limits, windows, enforcement guarantees, and failure behavior. | | Realtime | `RealtimeModuleOptions` | `realtime` config key or `hubRealtime(options)` | `authority`: `auto`, `cloudflare`, or `memory`; default `auto`. Auto uses a Durable Object when Realtime can resolve a Cloudflare Nitro preset or hosting environment. With only `vitehub({ preset: 'cloudflare' })` during Vite development, set `authority: 'cloudflare'` explicitly. Other development presets fall back to process memory. Other production builds require an explicit authority; `memory` is accepted only for a single-process server. Realtime Definitions keep the engine and document format separate from this deployment choice. | | Sandbox | `SandboxPublicOptions` | `sandbox` config key or `hubSandbox(options)` | `false` disables the integration. Provider selection belongs here: `cloudflare`, `vercel`, or inferred provider options. Netlify requires an explicit provider when Sandbox is active. Cloudflare defaults are binding `SANDBOX`, class name `Sandbox`, and migration tag `v1`. Per-run sandbox identity belongs to Sandbox Run invocation options. | | Schedule | `ScheduleVitePluginOptions` | `hubSchedule(options)` | `providerOutput`: `auto`, `standalone`, `nitro`, or `false`; default `auto`. `projectRoot` changes where generated schedule output is written. There is no public `schedule.provider` option. | | Workflow | `WorkflowModuleOptions` | `workflow` config key or `hubWorkflow(options)` | Omission or `false` disables Workflow in `vitehub()`; `true` enables inferred defaults, and an options object enables and configures it. `provider`: `cloudflare`, `openworkflow`, or `vercel`. Cloudflare hosting selects `cloudflare`; Node or Docker with OpenWorkflow storage config selects `openworkflow`; other supported hosts select `vercel`. Netlify does not infer a provider. Shared fields are `binding` and `name`. OpenWorkflow fields are `database`, `postgres`, `sqlite`, and `worker.concurrency`. | | Workspace | `WorkspaceModuleOptions` | `workspace` config key or `hubWorkspace(options)` | Omission or `false` disables Workspace in `vitehub()`; `true` enables inferred defaults, and an options object enables and configures it. `root` defaults to `.vitehub/workspaces`. `projectRoot` changes source root resolution. `assets` controls build-time asset generation. `store` provider literals are `local`, `memory`, `cloudflare-artifacts`, `vercel-blob`, and `github`. Explicit `cloudflare-artifacts` selection generates its Cloudflare binding. Defaults: local development uses `local`; Cloudflare hosting uses `memory`; `BLOB_READ_WRITE_TOKEN` selects `vercel-blob`; Vercel hosting without Blob env uses `memory`; otherwise `local`. | ## Option placement | Option kind | Belongs in | Example | | ------------------- | --------------------------------------- | ---------------------------------------------------------------- | | Integration Options | Vite config or package integration call | Provider Selection, generated output mode, project root. | | Definition Options | Definition Boundary Helper file | Queue concurrency, Database tables, Workspace Sources and rules. | | Invocation Options | Runtime Helper call | Sandbox Identity, Agent input options, schedule creation input. | | Runtime Env | Env Package Server Env | Provider tokens, app secrets, request-time runtime values. | Provider-specific driver fields are intentionally summarized here. Read the exported package types when configuring a deep provider adapter, and keep provider choices in Integration Options unless the owning package documents an invocation-time option. ## Agent eval options Agent Eval Runner defaults live under the Agent Package integration. ```ts [vite.config.ts] import { defineConfig } from 'vite' import { vitehub } from 'vite-hub' export default defineConfig({ plugins: [ vitehub({ preset: "node", agent: { eval: { cache: true, maxConcurrency: 2, scoreThreshold: 85, testTimeout: 60_000, }, }, }), ], }) ``` ## Env options Env separates Public Env, compile-time define values, and Server Env. Secret Env values belong in `env.server`, not `env.public` or `env.define`. ```ts [vite.config.ts] import { defineConfig } from 'vite' import { vitehub } from 'vite-hub' import { env } from 'vite-hub/env' export default defineConfig({ plugins: [vitehub({ preset: "node", env: { diagnostics: 'summary' } })], env: { public: { appName: env({ default: 'Acme' }), }, server: { apiToken: env({ secret: true, source: env.source('API_TOKEN') }), }, }, }) ``` ## Related - [Vite](https://vitehub.dev/docs/frameworks-hosts) - [Runtime and host support](https://vitehub.dev/docs/frameworks-hosts/support-matrix) - [Provisioning](https://vitehub.dev/docs/development/provisioning) - [Provider output](https://vitehub.dev/docs/reference/provider-output) # Errors and diagnostics Errors and diagnostics belong to the package that owns the failing boundary. ViteHub exposes one operational error class, `ViteHubError` from `@vite-hub/runtime`; use its namespaced `code` to choose the next proof path. `ViteHubError` snapshots its public `name`, `code`, `message`, `details`, and `requestId` fields at construction. The snapshot and its details are frozen, `cause` remains private, and later mutation cannot change `toJSON()`. Details must be bounded JSON data without accessors, cycles, `bigint`, non-finite numbers, or class instances; invalid public contracts fail with a fixed `TypeError` instead of serializing the rejected value. ## Code families | Code prefix | Owner | Usually means | | ----------------------------------------------- | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------- | | `CAPABILITY_*` | Runtime Package | Capability lookup or policy failed. | | `ENV_*` | Env Package | Env Declaration or runtime resolution failed. | | `BLOB_*` | Blob Package | Blob lookup or Provider-backed storage failed. | | `KV_*` | KV Package | Provider-backed key-value storage failed. | | `AUTH_*` and `AUTHENTICATION_*` | Auth Package | Authentication is required or a provider operation failed. HTTP adapters map `AUTHENTICATION_REQUIRED` to `401`. | | `EMAIL_*` | Email Package | Message validation, configuration, credentials, throttling, network, timeout, or delivery failed. | | `QUEUE_*`, `CLOUDFLARE_*`, and `VERCEL_*` | Queue Package | Queue dispatch, callback, or Provider handling failed. Queue Delivery owns retry and acknowledgement decisions. | | `WORKSPACE_*` | Workspace Package | Workspace lookup, path, runtime, store, rule, or file-tree behavior failed. | | `SOURCE_*` | Source Package | Source lookup, path validation, retrieval, or loader behavior failed. | | `SCHEDULE_*` | Schedule Package | Static or runtime Schedule behavior failed. | | `SANDBOX_*` | Sandbox Package | Sandbox Provider setup, execution, or output recovery failed. | | `WORKFLOW_*` and `OPENWORKFLOW_*` | Workflow Package | Workflow run, step, or Provider behavior failed. | | Rate Limit policy or driver error | Rate Limit Package | A policy is invalid, the selected driver cannot satisfy its guarantees, a Definition is unknown, or a provider binding is unavailable. | | `RATE_LIMIT_REJECTED` | Agent Package | Rate Limit Capability rejected an Agent Invocation. | | `LLM_GATE_REJECTED` | Agent Package | LLM Gate Capability rejected before the main Agent Invocation. | | `Agent Invocation Stream timed out after .` | Agent Package | The dev-loop stream aborted a long or stalled Agent Invocation after its timeout. | ## Agent public errors Agent routes and hooks expose a sanitized `AgentPublicError` beside the original server error. It is safe to serialize to a caller or use in an application-owned reply: ```ts interface AgentPublicError { code: AgentPublicErrorCode error: string details?: { capability?: string category?: string retryAfter?: number } requestId?: string } ``` `agent:error` hooks receive the raw failure as `error` and the sanitized value as `publicError`. Chat error hooks receive the same pair. Keep the raw error in protected diagnostics; provider payloads and causes can contain credentials or private response data. | Public code | Meaning | | ------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | | `PROVIDER_AUTHENTICATION_FAILED` | The model provider rejected its credentials. | | `PROVIDER_QUOTA_EXHAUSTED` | The account or project has no remaining provider quota. | | `PROVIDER_RATE_LIMITED` | The provider returned a temporary rate limit. | | `PROVIDER_UNAVAILABLE` | The provider returned a server or availability failure. | | `APPROVAL_REQUIRED` | A Capability needs approval before it can continue. `requestId` identifies the approval request when available. | | `AUTHENTICATION_REQUIRED`, `RATE_LIMIT_*`, `LLM_GATE_REJECTED`, `CAPABILITY_*`, `TRANSCRIPTION_*` | ViteHub recognized a public application or Capability failure. | | `INTERNAL` | The failure has no approved public mapping. The message stays generic. | The mapper includes only bounded identifiers, categories, retry delays, and request IDs. It replaces unknown errors with a context-specific `INTERNAL` message instead of copying `error.message`. ## Diagnostics sources | Source | Use | | ------------------------ | ------------------------------------------------------------------- | | CLI exit code and stderr | Command parsing, provisioning, and Agent Eval failures. | | Env diagnostics | Missing, defaulted, valid, and masked Env Declaration status. | | Generated files | Discovery, Runtime Registry, and Provider Output inspection. | | Agent Dev Loop responses | Local Agent inspection and invocation failures. | | Trace Events | Runtime policy, approval, capability, lifecycle, and error records. | | Package tests | Contract failures owned by the primitive package. | ## Local response Start with `getViteHubErrorShape(error)?.code`, then inspect the owning package and failing proof path. For packages that generate Provider Output, inspect that output before changing runtime code. Authenticated Agent bridges distinguish `AUTHENTICATION_REQUIRED` from `AUTH_PROVIDER_OPERATION_FAILED`; use `details.operation` for safe diagnostics and `cause` only in protected server-side diagnostics. Email emits no Provider Output; inspect the `EMAIL_*` code and `details.driver`. For Env, inspect the `ENV_*` code first. Its code set and public messages are fixed. `ENV_DECLARATION_INVALID` can include `details.path`, `ENV_REQUIRED_MISSING` can include a bounded source identifier and declaration path, and `ENV_RUNTIME_VALUE_INVALID` and `ENV_SOURCE_FAILED` can include a bounded source identifier such as `env`, `git:branch`, `package.json`, or `custom`. Raw labels and diagnostics remain in `cause`, which the serialized shape omits. Custom source resolvers keep application-owned errors unchanged. ```bash [Terminal] pnpm vitehub provision run --provider cloudflare --dry-run find .vitehub -maxdepth 4 -type f | sort pnpm --filter @vite-hub/sandbox test ``` ### Rate Limit diagnostics | Symptom | Likely cause | Verify | | ------------------------------------------------ | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Conflicting Rate Limit policy | Multiple `requireRateLimit()` calls use the same stable ID with different static policies. | Follow both reported source locations and make the policies identical or rename one ID. | | Driver provides best-effort enforcement | A policy requires `strict`, but the selected provider cannot guarantee it. | Keep strict enforcement and choose another driver, or change the policy only when best-effort protection is acceptable. | | Driver does not support the window | The provider accepts fewer fixed-window periods than the portable policy type. | Use a supported period or select a driver that advertises the required window. | | Production hosting requires an explicit provider | The build target is unknown or has no native inferred Rate Limit provider. | Set `provider: 'cloudflare'` with a project-unique `namespace` for Cloudflare, set `provider: 'memory'` only for a deliberate single-process deployment, or construct a custom Rate Limiter. | | Cloudflare binding was not found | Generated `ratelimits` output is missing from the running Worker or request context. | Inspect `wrangler.json`, then exercise the deployed Worker rather than an unrelated Node process. | | `reason: 'unavailable'` with `allowed: true` | A `failure: 'allow'` policy allowed work after a driver error. | Record the unavailable decision and inspect provider health before changing the budget. | ## Production response Keep secrets out of production diagnostics. Use Server Env and Secret Env for runtime secret values, and rely on package diagnostics to redact known secret values where supported. ## Related - [Troubleshooting](https://vitehub.dev/docs/development/troubleshooting) - [Runtime events](https://vitehub.dev/docs/reference/runtime-events) - [Verification](https://vitehub.dev/docs/development/verification) # File conventions File conventions produce Discovered Definitions. Discovery Identity comes from the discovery location, not from arbitrary inline Definition Options. ## Definition files | Definition | Directory convention | Suffix convention | Discovery Identity | | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Agent | `server/agents/.ts`, `server/agents//agent.ts`, or `server/agents//index.ts` | `.agent.ts` outside `server/` | Relative file or directory path. A leading `src/` is removed from suffix identities. | | Auth | `server/auth.ts` | `server.auth.ts` | `default`. Only one Auth Definition is allowed. | | Browser | `server/browsers/.ts` | `.browser.ts` | Normalized relative path. A leading `src/` is removed from suffix identities. | | Channel | `server/channels/.ts` | `.channel.ts` | Normalized relative path. A leading `src/` is removed from suffix identities. | | Database | `server/databases/config.ts` for one default database, or `server/databases//config.ts` for named databases | `src/database.ts` for the default database, or `.database.ts` for a named database | `default` or the normalized relative path. Default and named modes cannot be mixed. | | Queue | `server/queues/.ts` | `.queue.ts` | Normalized relative path. A leading `src/` is removed from suffix identities. | | Realtime | `server/realtime/.ts` | None | Normalized relative path. | | Workflow | `server/workflows/.ts` or a folder containing `index.ts` or numbered step files | `.workflow.ts` | Normalized relative file or folder path. Agent Definitions contribute their Agent identity by default, `workflow(...)` can override it, and `runtime: false` opts out. | | Schedule | `server/schedules/.ts` | `.schedule.ts` | Normalized relative path. A leading `src/` is removed from suffix identities. | | Sandbox | `server/sandboxes//{package.json,index.ts}` | `.sandbox.ts` outside `server/sandboxes/` | Normalized folder or suffix path, without a trailing `.sandbox` segment. | | Workspace | `server/workspaces/.ts`, `server/workspaces//config.ts`, or `server/agents//agent.ts` when the Agent declares a Workspace | `.workspace.ts` | Normalized relative path or the colocated Agent name. | The table uses `.ts` for brevity. Directory and suffix patterns accept JavaScript and TypeScript module variants where the owning package permits them. `src/database.ts` is the exact default Database suffix-mode file. Rate Limit deliberately has no file convention. Call `requireRateLimit(event, id, options)` inside ordinary H3 handlers; its explicit ID is the provider identity. ## Export shape Most discovered Definition files default-export the package-owned Definition Boundary Helper. This keeps Build-Extracted Definition Options limited to the direct discovered default export. Canonical Sandbox package projects are the exception: `server/sandboxes//index.ts` default-exports an async `(payload, context) => result` function. The adjacent `package.json` must set `"type": "module"`. Local TypeScript uses explicit relative ESM imports, while bare dependencies must expose runtime-ready JavaScript; CommonJS source and package-local import aliases are not compiled. The folder supplies the Definition identity, and optional static wall-clock policy comes from `vitehub.sandbox.timeout`. Free-form `.sandbox.ts` files still default-export `defineSandbox(...)`. ```ts [server/queues/welcome-email.ts] import { defineQueue } from '@vite-hub/queue' export default defineQueue<{ email: string }>(async (job) => { await sendWelcomeEmail(job.payload.email) }) ``` Avoid aggregate named exports for discovered Definitions. The generated Runtime Registry expects one discovered boundary per file convention. ## Colocated Workspace files Agent folders can colocate Workspace content beside the Agent Definition. When a folder contains `workspace/`, that folder becomes the Workspace Source Root for the colocated Workspace Definition. ```txt [File tree] server/ agents/ docs/ agent.ts workspace/ README.md guides/ setup.md ``` ## Colocated Agent Skills An Agent folder can own Skills in an adjacent `skills/` directory. ViteHub recursively embeds every file during discovery and materializes the directory into the Provider Workspace. Existing files remain in place, and files below a `scripts/` directory become executable. ```txt [File tree] server/ agents/ review/ agent.ts skills/ code-review/ SKILL.md scripts/ review.sh ``` This convention needs no `skills()` Capability declaration. Use [`skills()`](https://vitehub.dev/docs/capabilities/skills) when the Skill comes from a Workspace or external Source instead of the Agent folder. ## Markdown templates Place a `*.template.md` file beside the TypeScript or JavaScript module that renders it, then import the generated render function directly. For example, `server/agents/review/agent.ts` can import `./reply.template.md`. When one caller owns several templates, you can group them in a local directory such as `server/agents/review/templates/`. The directory has no discovery behavior; keep the `.template.md` suffix and import each file explicitly. See [Markdown templates](https://vitehub.dev/docs/reference/markdown-templates) for rendering and generated-type examples. ## Email templates Markdown files under `server/emails` become typed email renderers. ViteHub removes the directory prefix and `.md` extension, so `server/emails/welcome.md` becomes `#vitehub/emails/welcome` and `server/emails/monthly/recap.md` becomes `#vitehub/emails/monthly/recap`. Email template names must use non-empty path segments and cannot contain `.` or `..` segments, query strings, fragments, backslashes, or a trailing `.md`. ViteHub rejects duplicate names across configured server directories. ## Generated files Generated files live under `.vitehub/**` and host output directories. They prove discovery and Provider Output, but the source Definition files remain the authoring surface. ## Related - [Generated files](https://vitehub.dev/docs/development/generated-files) - [Definitions and discovery](https://vitehub.dev/docs/concepts/definitions-and-discovery) - [Import paths](https://vitehub.dev/docs/reference/import-paths) # Import paths Stable ViteHub Import Paths are ViteHub-owned app-facing import specifiers. They may resolve to Runtime Registries, generated files, virtual modules, or owner-package runtime code, but application code must not depend on that implementation detail. ## Canonical application imports Applications that install `vite-hub` use the root only for framework composition and explicit feature subpaths for application APIs. | Import path | Use | | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `vite-hub` | Register the framework Vite Integration with `vitehub()`. | | `vite-hub/agent` | Agent Definition, invocation, trigger, and Agent Actor APIs. | | `vite-hub/agent/capabilities` | Official Capability factories. | | `vite-hub/agent/channels` | Official Channel Kind helpers. | | `vite-hub/agent/eval` | Agent Eval authoring helpers; install Evalite and the test runner explicitly. | | `vite-hub/agent/cloudflare` | Cloudflare Agent state configuration helpers. | | `vite-hub/agent/vue` | Vue Agent client handle and AI SDK chat composable. | | `vite-hub/agent/server` and `vite-hub/agent/state/sqlite` | Manual server integration and libSQL-compatible durable Agent state. | | `vite-hub/agent/invocations/sqlite` | LibSQL-compatible durable Agent Invocation Journal. | | `vite-hub/console` | Route metadata for the local read-only invocation console. | | `vite-hub/console/server` | Local console invocation journal for server integrations. | | `vite-hub/auth` and `vite-hub/auth/server` | Auth Definitions and server runtime helpers. | | `vite-hub/auth/agent` | Better Auth session mapping into Agent Invokers. | | `vite-hub/auth/vue` | Better Auth Vue client and normalized session composables. | | `vite-hub/blob` | Blob Runtime Helpers and Blob Store access. | | `vite-hub/blob/content-type` | Detect common image and PDF signatures from leading bytes before upload. | | `vite-hub/browser` | Browser Definitions, invocation-scoped Playwright sessions, and named Browser runs. | | `vite-hub/browser/actions` | ViteHub Browser actions backed by Cloudflare Browser Run. | | `vite-hub/browser/controllers/cdp` and `vite-hub/browser/controllers/playwright` | Advanced raw CDP and Playwright Browser Session controllers. | | `vite-hub/browser/providers/cloudflare` and `vite-hub/browser/providers/local` | Advanced explicit provider selection for low-level Browser Clients. | | `vite-hub/channels` and `vite-hub/channels/server` | Channel Definitions and discovered named delivery. | | `vite-hub/box` | Box Definitions and built-in runtime selection for trusted-host, Crabbox, ASCII, Cloudflare Sandbox, Cloudflare Computer, and Vercel Sandbox execution. | | `vite-hub/database` and `vite-hub/database/drizzle` | Database Definitions and generated `useDatabase()` access. | | `vite-hub/env` | Env Declaration helpers and authoring types. | | `vite-hub/email`, `vite-hub/email/server`, and `vite-hub/email/markdown` | Email clients, configured runtime delivery, and Dynamic Markdown HTML with a composed Markdown text fallback. | | `vite-hub/env/presets` and `vite-hub/env/schema` | Reusable Env presets and schema helpers. | | `vite-hub/env/secret` and `vite-hub/env/server` | Secret declarations and server-only Env access. | | `vite-hub/history` | Durable Workspace history checkpoint contract and types. | | `vite-hub/kv` | KV Runtime Helper. | | `vite-hub/markdown-template` | Deterministic Markdown rendering from explicit template strings. | | `vite-hub/queue` | Queue Definitions and dispatch helpers. | | `vite-hub/rate-limit` | Source-local managed Rate Limit handles and direct Rate Limiters. | | `vite-hub/realtime`, `vite-hub/realtime/server`, and `vite-hub/realtime/vue` | Realtime Definitions, manual server integration, and Vue collaborative editing with canonical [Realtime checkpoints](https://vitehub.dev/docs/reference/realtime). | | `vite-hub/runtime` | Runtime Host Context, policy, approval, trace, and capability APIs. | | `vite-hub/runtime/node` | Node process, host, and Linux cgroup resource observations. | | `vite-hub/sandbox` | Sandbox Definitions and Sandbox Run helpers. | | `vite-hub/schedule` and `vite-hub/schedule/runtime` | Static and runtime Schedule APIs. | | `vite-hub/schedule/runtime/driver` and `vite-hub/schedule/runtime/process` | Host wake registration and process-backed runtime Schedule controls. | | `vite-hub/shell` | Shell runtime and command analysis APIs. | | `vite-hub/shell/providers/cloudflare` and `vite-hub/shell/providers/just-bash` | Cloudflare and Just Bash Shell providers. | | `vite-hub/shell/workspace` | Workspace-backed Shell execution helpers. | | `vite-hub/source` | Runtime-neutral Source Definitions, custom loaders, and registry APIs. | | `vite-hub/source/client` and `vite-hub/source/server` | Vue Collection pagination and the H3 Collection route adapter. | | `vite-hub/source/content` and `vite-hub/source/content/client` | Comark Content definition, Source adaptation, generated runtime handler, and typed client. | | `vite-hub/source/file`, `vite-hub/source/glob`, and `vite-hub/source/markdown` | Local file implementations, loaded only when selected. | | `vite-hub/source/github` | GitHub Source implementation, loaded only when selected. | | `vite-hub/source/mcp` | MCP Resources implementation with its private SDK closure. | | `vite-hub/ui`, `vite-hub/ui/headless`, and `vite-hub/ui/styles.css` | AI interface components, headless message scrolling, and default styles. | | `vite-hub/ui/nuxt` and `vite-hub/ui/vite` | Register the canonical UI package for Nuxt or Vue with Vite. | | `vite-hub/tsconfig` | TypeScript config that includes ViteHub's generated declaration entry without taking ownership of application source includes. | | `vite-hub/workflow` | Workflow Definitions and run helpers. | | `vite-hub/workspace` and `vite-hub/workspace/runtime` | Workspace Definitions, Sources, runtime facades, and registry APIs. | | `vite-hub/workspace/cloudflare` | Cloudflare Workspace runtime setup. | | `vite-hub/workspace/collections` and `vite-hub/workspace/collections/client` | Bounded Workspace Collection queries and optional Vue client composables. | | `vite-hub/workspace/loader`, `vite-hub/workspace/publish`, and `vite-hub/workspace/server` | Workspace loader, publisher, and manual server extension APIs. | ViteHub-owned adapters use canonical `vite-hub/*` imports. Their optional third-party providers and SDKs remain explicit dependencies. Provider Output, build integrations, tests, and unlisted provider-specific modules stay on their owner packages. ## Direct owner-package imports Every owner package remains independently installable. These paths are stable for libraries, focused integrations, and advanced composition. | Import path | Owner | Use | | ---------------------------------------------------------------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------ | | `@vite-hub/agent` | Agent Package | Agent Definition helpers, invocation helpers, trigger helpers, and trusted caller types. | | `@vite-hub/agent/capabilities` | Agent Package | Official Capability factories such as `access()`, `browser()`, `workspaceShell()`, `inputCommands()`, and `subagents()`. | | `@vite-hub/agent/channels` | Agent Package | Official Channel Kind helpers such as `github()`, `teams()`, `telegram()`, `webChat()`, and `defineChannel()`. | | `@vite-hub/agent/eval` | Agent Package | Agent Eval authoring helpers. | | `@vite-hub/agent/test` | Agent Package | Agent test runner helpers for local and CI Agent Invocation checks. | | `@vite-hub/agent/cloudflare` | Agent Package | Cloudflare Agent state helpers. | | `@vite-hub/agent/vue` | Agent Package | Vue Agent client handle and AI SDK chat composable. | | `@vite-hub/auth` | Auth Package | Auth Definition helpers. | | `@vite-hub/auth/server` | Auth Package | Better Auth runtime creation, request handlers, and session access for manual host integration. | | `@vite-hub/blob` | Blob Package | Blob Runtime Helpers and Blob Store access. | | `@vite-hub/blob/content-type` | Blob Package | Detect common image and PDF signatures from leading bytes before upload. | | `@vite-hub/browser` | Browser Package | Browser Definitions, invocation-scoped sessions, and low-level Browser Client lifecycle. | | `@vite-hub/browser/actions` | Browser Package | ViteHub Browser actions backed by Cloudflare Browser Run. | | `@vite-hub/browser/controllers/cdp` and `@vite-hub/browser/controllers/playwright` | Browser Package | Raw CDP and Playwright Browser Session controllers. | | `@vite-hub/browser/providers/cloudflare` and `@vite-hub/browser/providers/local` | Browser Package | Cloudflare Browser Run and local Chromium providers. | | `@vite-hub/box` | Box Package | Box Definitions, sessions, and built-in runtime selection. | | `@vite-hub/channels` | Channels Package | Channel Definitions, explicit clients, portable types, and normalized delivery results. | | `@vite-hub/channels/server` | Channels Runtime | Server-only discovered named delivery. | | `@vite-hub/email` | Email Package | Explicit clients, portable types, and normalized errors. | | `@vite-hub/email/server` | Email Runtime | Server-only configured `email` Runtime Helper. | | `@vite-hub/email/markdown` | Email Package | Dynamic Markdown composition into HTML and a composed Markdown text fallback. | | `@vite-hub/email/test` | Email Package | Isolated in-memory message capture for tests. | | `#vitehub/emails/` | Email Package | Generated async renderer for a discovered `server/emails/**/*.md` template. | | `@vite-hub/database/drizzle` | Database Package | Generated `useDatabase()` access to a Drizzle database and schema. | | `@vite-hub/env` | Env Package | Env Declaration helpers. | | `@vite-hub/history` | History Package | Durable history checkpoint contract and types. | | `#vitehub/env/public` | Env Package | Generated Public Env access. | | `#vitehub/env/server` | Env Package | Generated Server Env access. | | `@vite-hub/kv` | KV Package | KV Runtime Helper. | | `@vite-hub/markdown-template` | Markdown Template Package | Deterministic Markdown rendering from explicit template strings. | | `@vite-hub/queue` | Queue Package | Queue Definition and enqueue Runtime Helper. | | `@vite-hub/rate-limit` | Rate Limit Package | Source-local managed Rate Limit handles and direct Rate Limiters. | | `@vite-hub/rate-limit/drivers/memory` | Rate Limit Package | Local, test, and single-process fixed-window enforcement. | | `@vite-hub/rate-limit/drivers/cloudflare` | Rate Limit Package | Direct access to a Cloudflare Rate Limiting binding. | | `@vite-hub/realtime` | Realtime Package | Realtime Definitions and portable collaboration types. | | `@vite-hub/realtime/server` and `@vite-hub/realtime/vue` | Realtime Package | Manual server integration and Vue collaborative editing. | | `@vite-hub/ui` and `@vite-hub/ui/headless` | UI Package | AI SDK-native Vue components and headless message scrolling. | | `@vite-hub/sandbox` | Sandbox Package | Sandbox Definition and Sandbox Run helpers. | | `@vite-hub/schedule/runtime` | Schedule Package | Runtime schedule helpers. | | `@vite-hub/schedule/runtime/driver` | Schedule Package | Host integration boundary for reconciling stored Runtime Schedules with native wake registrations. | | `#vitehub/schedule/registry` | Schedule Package | Generated static schedule registry for host bridges. | | `@vite-hub/workflow` | Workflow Package | Workflow Definition and run helpers. | | `@vite-hub/workflow/runtime/openworkflow-worker` | Workflow Package | OpenWorkflow-specific worker lifecycle helpers; install `openworkflow` explicitly. | | `@vite-hub/workspace` | Workspace Package | Workspace Definition, Source helpers, Workspace facade access, and authoring types. | | `@vite-hub/workspace/runtime` | Workspace Package | Workspace runtime registry, `useWorkspace()`, and source resolution/request helpers for integrations. | ## Integration imports | Import path | Use | | ------------------------------------------- | --------------------------------------------------------------------------------------- | | `vite-hub` | Canonical application import for `vitehub()`. | | `vite-hub/nuxt` | Register the framework Nuxt module and carry Vite integration configuration into Nitro. | | `@vite-hub/agent/vite` | Register the Agent Vite Integration. | | `@vite-hub/auth/vite` | Register the Auth Vite Integration. | | `@vite-hub/blob/vite` | Register the Blob Vite Integration. | | `@vite-hub/browser/vite` | Register Cloudflare Browser Run Provider Output. | | `@vite-hub/channels/vite` | Register Channel Definition discovery and generated runtime bindings. | | `@vite-hub/database/vite` | Register the Database Vite Integration. | | `@vite-hub/email/vite` | Configure one Unemail provider and generate its runtime binding. | | `@vite-hub/env/vite` | Register the Env Vite Integration and `env()` declaration helper. | | `@vite-hub/ui/nuxt` and `@vite-hub/ui/vite` | Register the UI package for Nuxt or Vue with Vite. | | `@vite-hub/kv/vite` | Register the KV Vite Integration. | | `@vite-hub/markdown-template/vite` | Register generated types and direct `.template.md` imports. | | `@vite-hub/queue/vite` | Register the Queue Vite Integration. | | `@vite-hub/rate-limit/vite` | Register Rate Limit source collection and provider output. | | `@vite-hub/realtime/vite` | Register Realtime Definition discovery and generated runtime wiring. | | `@vite-hub/sandbox/vite` | Register the Sandbox Vite Integration. | | `@vite-hub/schedule/vite` | Register the Schedule Vite Integration. | | `@vite-hub/workflow/vite` | Register the Workflow Vite Integration. | | `@vite-hub/workspace/vite` | Register the Workspace Vite Integration. | ## Generated and internal paths | Path family | Status | Guidance | | ------------------------------------------ | ------------------------- | ---------------------------------------------------------------------- | | `.vitehub/**` | Generated | Inspect during development; do not author imports against these files. | | `.vercel/output/**` | Generated Provider Output | Deploy or inspect as Vercel Build Output. | | `.netlify/v1/**` | Generated Provider Output | Deploy or inspect as Netlify function output. | | `dist/**/wrangler.json` | Generated Provider Output | Deploy or inspect as Cloudflare output. | | Vite virtual module ids with `\0` prefixes | Internal | Never import directly. | | `vite-hub/_internal/*` | Internal | Generated ViteHub code only; application imports are unsupported. | | `@vite-hub/internal/*` | Internal | Package implementation only. | The Agent Package does not expose an `@vite-hub/agent/netlify` application import. Netlify Agent output is generated Provider Output under `.netlify/v1` plus the `.vitehub/agent/netlify-function.mjs` source wrapper. With Nuxt, that source wrapper is generated under `/vitehub/agent/netlify-function.mjs` (normally `.nuxt/vitehub/agent/netlify-function.mjs`). The framework distribution does not introduce public `vite-hub/*/vite` or provider-specific application aliases. Use root `vitehub()` for framework composition and the owner-package paths above for advanced integration control. ## Related - [Generated files](https://vitehub.dev/docs/development/generated-files) - [File conventions](https://vitehub.dev/docs/reference/file-conventions) - [Package reference](https://vitehub.dev/docs/reference) - [Runtime and host support](https://vitehub.dev/docs/frameworks-hosts/support-matrix) # Package reference Most applications need only `vite-hub`. Install an individual `@vite-hub/*` package when you are building a library or configuring one integration directly. ## Framework distribution | Package | Owns | Primary imports | | ---------- | ----------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | | `vite-hub` | Framework defaults, the `vitehub()` Vite Integration, the tested compatibility matrix, and intentional feature subpaths | `vite-hub`, `vite-hub/agent`, `vite-hub/env`, `vite-hub/workspace`, `vite-hub/workflow` | The root export stays focused on framework composition. Runtime Helpers, Definitions, Capabilities, and other application APIs use feature subpaths instead of one root barrel. ## Independent owner packages | Package | Owns | Primary imports | | ----------------------------- | ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | `@vite-hub/agent` | Agent Definitions, Agent Invocations, Agent Driver boundary, Capability composition, Agent Evals, Agent Trigger API | `@vite-hub/agent`, `@vite-hub/agent/capabilities`, `@vite-hub/agent/channels`, `@vite-hub/agent/eval`, `@vite-hub/agent/vite` | | `@vite-hub/auth` | Auth Definitions, Better Auth server wiring, generated Auth route behavior | `@vite-hub/auth`, `@vite-hub/auth/server`, `@vite-hub/auth/vite` | | `@vite-hub/blob` | Blob Stores, Default Blob Store behavior, Blob Driver Modules, provider storage output | `@vite-hub/blob`, `@vite-hub/blob/vite`, `@vite-hub/blob/drivers/*` | | `@vite-hub/browser` | Browser Definitions, invocation-scoped sessions, controllers and providers, live handoff, and Browser Run output | `@vite-hub/browser`, `@vite-hub/browser/controllers/*`, `@vite-hub/browser/providers/*`, `@vite-hub/browser/vite` | | `@vite-hub/box` | Box Definitions and provider-neutral execution sessions | `@vite-hub/box` | | `@vite-hub/channels` | Provider-neutral Channel definitions, server access, and Vite discovery | `@vite-hub/channels`, `@vite-hub/channels/server`, `@vite-hub/channels/vite` | | `@vite-hub/database` | Database Definitions, Drizzle schema generation, D1 and hosted database wiring | `@vite-hub/database`, `@vite-hub/database/drizzle`, `@vite-hub/database/vite` | | `@vite-hub/email` | Declarative Unemail provider integration, runtime delivery, Dynamic Markdown composition, and test capture | `@vite-hub/email`, `@vite-hub/email/markdown`, `@vite-hub/email/server`, `@vite-hub/email/test`, `@vite-hub/email/vite` | | `@vite-hub/env` | Env Declarations, Public Env, Server Env, Secret Env, generated env access | `@vite-hub/env`, `@vite-hub/env/vite`, `@vite-hub/env/server`, `@vite-hub/env/secret` | | `@vite-hub/history` | Shared history records, cursors, pages, and store contracts for stateful features | `@vite-hub/history` | | `@vite-hub/kv` | KV Runtime Helper and configured KV Stores | `@vite-hub/kv`, `@vite-hub/kv/vite` | | `@vite-hub/markdown-template` | Markdown templates with data bindings, conditions, fragments, and direct Vite imports | `@vite-hub/markdown-template`, `@vite-hub/markdown-template/vite` | | `@vite-hub/queue` | Queue Definitions, queue dispatch Runtime Helpers, provider queue output | `@vite-hub/queue`, `@vite-hub/queue/vite` | | `@vite-hub/rate-limit` | Rate Limit declarations, runtime decisions, drivers, and provider output | `@vite-hub/rate-limit`, `@vite-hub/rate-limit/runtime`, `@vite-hub/rate-limit/drivers/*`, `@vite-hub/rate-limit/vite` | | `@vite-hub/realtime` | Realtime documents, server routes, history, and Vue bindings | `@vite-hub/realtime`, `@vite-hub/realtime/server`, `@vite-hub/realtime/vue`, `@vite-hub/realtime/vite` | | `@vite-hub/runtime` | Runtime Host Context, Runtime Capability handles, Policy Decisions, approvals, Trace Events, leases | `@vite-hub/runtime` | | `@vite-hub/sandbox` | Sandbox Definitions, Sandbox Runs, Sandbox Provider integration | `@vite-hub/sandbox`, `@vite-hub/sandbox/vite` | | `@vite-hub/schedule` | Static schedules, runtime schedules, Schedule Targets, cron Provider Output | `@vite-hub/schedule`, `@vite-hub/schedule/runtime`, `@vite-hub/schedule/vite` | | `@vite-hub/shell` | Shell-shaped runtime execution providers and Workspace shell integration helpers | `@vite-hub/shell`, `@vite-hub/shell/workspace` | | `@vite-hub/source` | Source Definitions and Source Loaders for file, glob, markdown, GitHub, custom, and MCP resource retrieval | `@vite-hub/source`, `@vite-hub/source/*` | | `@vite-hub/ui` | AI SDK-native Vue and Nuxt components, headless message scrolling, Agent inspection, and Pierre code views | `@vite-hub/ui`, `@vite-hub/ui/headless`, `@vite-hub/ui/nuxt`, `@vite-hub/ui/vite` | | `@vite-hub/workflow` | Workflow Definitions, durable run state, step execution, provider workflow output | `@vite-hub/workflow`, `@vite-hub/workflow/vite` | | `@vite-hub/workspace` | Workspace Definitions, Workspace Stores, Source Bindings, Workspace runtime facades, Workspace extensions | `@vite-hub/workspace`, `@vite-hub/workspace/vite`, `@vite-hub/workspace/runtime` | ## Internal and support packages | Package | Status | Purpose | | -------------------- | ------------------ | ---------------------------------------------------------------------------- | | `@vite-hub/cli` | Public CLI package | Loads Vite config and runs package-owned CLI namespaces. | | `@vite-hub/internal` | Internal package | Shared discovery, Provider Output, provisioning, runtime, and build helpers. | ## Package rules Applications start with `vite-hub` and its documented feature imports. Libraries and focused integrations can depend on one `@vite-hub/*` package directly. Do not import `@vite-hub/internal`, generated files, or framework virtual modules unless a reference page documents that path. Provider-specific behavior belongs to the package that owns the primitive. For example, Blob Provider SDK Adapters belong behind Blob Driver Modules, and Workspace Provider Adapters stay behind Workspace configuration and generated runtime wiring. ## Related - [Import paths](https://vitehub.dev/docs/reference/import-paths) - [Config options](https://vitehub.dev/docs/reference/config-options) - [Provider output](https://vitehub.dev/docs/reference/provider-output) - [Runtime and host support](https://vitehub.dev/docs/frameworks-hosts/support-matrix) # Markdown templates `@vite-hub/markdown-template` composes Markdown without evaluating JavaScript or reading files implicitly. Use it when Agent Instructions, review prompts, or other generated documents need predictable data binding and conditional sections while preserving authored Markdown structure. ## Install The package requires Node.js 24 or later. ```bash [Terminal] pnpm add @vite-hub/markdown-template ``` ## Import a template file Place a `*.template.md` file beside the module that renders it. Importing the file returns an asynchronous render function. ```md [server/agents/reviewer/prompt.template.md] # Review {{ pullRequest.number }} Title: {{ pullRequest.title }} ``` ```ts [server/agents/reviewer/agent.ts] import renderPrompt from './prompt.template.md' const prompt = await renderPrompt({ pullRequest: { number: 611, title: 'Refine navigation' }, }) ``` ViteHub bundles the template and its relative Markdown imports before deployment. The deployed application does not read these source files at runtime. The generated module type accepts an optional `Record` and returns `Promise`. When one caller owns several templates, you may group them in a local directory such as `./templates/`. The directory has no discovery behavior; import each `*.template.md` file directly. Multiple callers can also import the same template from an explicitly shared source path. For a fixed runtime choice, define the allowed names with an ordinary TypeScript map: ```ts [server/agents/reviewer/replies.ts] import renderFailure from './failure.template.md' import renderSuccess from './success.template.md' export const replies = { failure: renderFailure, success: renderSuccess, } as const ``` The `vitehub()` preset installs the template module integration. Modular Vite configurations can add `hubMarkdownTemplate()` from `@vite-hub/markdown-template/vite`. Both forms generate the ambient module type under `.vitehub/types`, which the application `tsconfig.json` must include. Imported fragments can remain ordinary `.md` files. `repositoryHostContext({ materialize })` uses the same path convention. Pass a caller-relative `.template.md` path; ViteHub bundles its renderer and derives the generated `.md` path by removing only the final `.template`, preserving directories and case. ## Render a template string Pass the template string and the complete data available to it. Scalar bindings are escaped as Markdown text, while triple bindings insert an intentional Markdown fragment. ```ts [src/review-template.ts] import { renderMarkdownTemplate } from '@vite-hub/markdown-template' const markdown = await renderMarkdownTemplate([ '# Review {{ pullRequest.number }}', '', 'Title: {{ pullRequest.title }}', '', '::if{pullRequest.draft}', 'This pull request is a draft.', '::else', '{{{ sections.files }}}', '::', ].join('\n'), { data: { pullRequest: { draft: false, number: 611, title: 'Refine navigation', }, sections: { files: '## Files\n\n- `DocsAsideLeftBody.vue`', }, }, }) ``` The result keeps the fragment as document structure: ```md [Rendered Markdown] # Review 611 Title: Refine navigation ## Files - `DocsAsideLeftBody.vue` ``` ## Template syntax | Syntax | Purpose | Behavior | | --------------------------------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `{{ path.to.value }}` | Scalar binding | Accepts a string, number, or boolean and escapes Markdown syntax in the value. A scalar may occupy a complete inline link destination, such as `[Open]({{ url }})`; unsafe destinations and values whose URL meaning cannot be preserved fail rendering. Missing paths and non-scalar values fail rendering. | | `{{{ path.to.markdown }}}` | Markdown fragment | Inserts trusted Markdown without evaluating bindings, conditions, or imports inside the fragment again. Block Markdown is rejected when the binding appears in an inline position. | | `::if{condition}` | Conditional section | Selects an `if`, `else-if`, or `else` branch. Conditions support data paths, literals, `!`, equality and inequality (`===`, `!==`, `==`, and `!=` use strict semantics), `&&`, `||`, and parentheses. | | `@./relative.md` | Template import | Calls `resolveImport` for a relative file. Absolute paths, URLs, and globs are rejected. | | `{{ value }}` in a quoted XML-style attribute | Attribute binding | Escapes HTML attribute characters before inserting the scalar value. | Template syntax inside code spans, fenced code blocks, and indented code blocks remains literal. Authored XML-style tags remain in the rendered Markdown. ## Render options `renderMarkdownTemplate(template, options)` returns a `Promise` and accepts every `RenderMarkdownTemplateOptions` field below. | Option | Type | Default | Purpose | | ---------------- | ------------------------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------- | | `data` | `Record` | `{}` | Supplies values for scalar bindings, fragments, and conditions. Paths resolve own properties only. | | `maxImportDepth` | `number` | `4` | Limits nested imports when `resolveImport` is present. Use a non-negative integer; `0` rejects every import. | | `resolveImport` | `ResolveMarkdownTemplateImport` | none | Resolves one relative specifier against the current canonical source id. Without it, relative-looking text remains literal. | | `sourceId` | `string` | `