@vite-hub/agent
v0.0.3
Published
Agent DX layer and Vite integration for ViteHub.
Maintainers
Readme
@vite-hub/agent
@vite-hub/agent defines Agents from files such as server/agents/support/agent.ts. Each Agent selects one Driver: an AI SDK model, a coding harness, or application-owned driver.run logic.
Keep the three pieces separate:
- Agent Driver: the model, coding harness, or application-owned function that runs the Agent.
- Capabilities: opt-in abilities such as chat, shell, search, storage, sandbox, and MCP tools.
- Workspace: file-system context the agent can inspect, reason from, and optionally update while doing a task.
Install
pnpm add @vite-hub/agent @vite-hub/workspace aiai is required for model-backed drivers and AI SDK-powered capabilities such as model-backed title(), chatSummary(), llmGate(), and transcribe(). Agents with driver.run can bundle without installing ai.
Add the AI SDK model provider you pass to model.
The built-in "codex" driver includes the exact AI SDK harness packages that ViteHub supports. For Claude Code, add its driver package:
pnpm add @ai-sdk/harness-claude-codeMinimal API
// server/agents/support/agent.ts
import { defineAgent, gateway } from "@vite-hub/agent"
import { workspaceShell } from "@vite-hub/agent/capabilities"
import { webChat } from "@vite-hub/agent/channels"
import { file } from "@vite-hub/workspace"
export default defineAgent({
driver: {
model: gateway("openai/gpt-5.1-mini"),
instructions: [
"Answer support questions from the workspace.",
"Use the support Source for support policies and known answers.",
],
},
channels: {
web: webChat(),
},
capabilities: [workspaceShell()],
workspace: {
sources: {
support: file({
path: "support.md",
}),
},
},
});Harness drivers
Harness-backed agents use AI SDK HarnessAgent behind the ViteHub Agent Driver boundary.
// server/agents/codex/agent.ts
import { defineAgent } from "@vite-hub/agent";
import { file } from "@vite-hub/workspace";
export default defineAgent({
driver: {
kind: "codex",
instructions: "Review the exact pull request head before changing code.",
model: "gpt-5.5",
reasoningEffort: "low",
workDir: "repositories/vitehub",
},
workspace: {
mode: "write",
sources: {
guide: file("AGENTS.md"),
},
},
});Put Agent-owned Skills under server/agents/codex/skills/; discovery materializes them into the Harness Workspace and the isolated Codex profile automatically. Use skills() for Workspace-backed or external Source Skills.
Put static Box Home files under server/agents/codex/home/; discovery embeds dotfiles and binary files into box.home.files. Use box.home.state for credentials or other files that must refresh or persist.
Use driver: "codex" or driver: "claude-code" for the defaults. Use a tagged value such as { kind: "claude-code", model, maxTurns } when configuration is needed. The Claude Code default owns a local harness sandbox; use { kind: "claude-code", sandbox: false } when a Box should own its process environment and working directory.
driver.harness is the AI SDK harness adapter instance. Harness drivers use ViteHub's local harness sandbox by default on process-capable hosts and receive a Harness Workspace Session when the Agent has a Workspace. Cloudflare Agents and Deno require an explicit provider. The local sandbox is a tempdir-backed shell convenience, not OS/process isolation; pass a real harness sandbox provider through driver.sandbox when isolation matters. Harness sandbox provider setup is Agent Package runtime plumbing; use driver.sandbox when an Agent needs a specific harness process or session provider. driver.workDir selects a relative directory inside the sandbox default working directory. Add sandbox({ commands }) only when the model should receive sandbox_exec. driver.harness, driver.instructions, driver.sessionKey, driver.sandbox, and driver.workDir can be callbacks when one Agent Definition needs invocation-scoped harness setup. ViteHub resolves harness instructions before constructing the AI SDK HarnessAgent, so stock harness adapters receive the selected instructions for both generated and streamed turns. When access() narrows Workspace Scope, ViteHub materializes only that selected scope plus generated source descriptors. Read mode materializes the selected Workspace into the harness sandbox and discards sandbox changes; write mode syncs additions, updates, and deletions back through Workspace rules. Colocated skills/ files are merged into the Harness Workspace and supported global profile without replacing existing files. V1 configures built-in harness permissions internally with the no-approval policy and does not expose a public permission option. skills() remains available for Workspace-backed and external Source Skills and does not inject model instructions or Workspace Shell tools. Put repository-wide guidance in Workspace files such as AGENTS.md; use driver.instructions for invocation-specific harness policy.
Immutable deployments can set VITEHUB_CODEX_BRIDGE_NODE_MODULES inside the Codex sandbox to an absolute preinstalled node_modules tree containing @openai/codex-sdk. ViteHub reuses that tree without installing at startup; an invalid or conflicting configured path fails before network access, while an unset variable keeps the pinned pnpm installer.
Boxes
Use a Box when a harness Agent should boot in one project-declared execution environment. A trusted-host Box uses the host's installed tools while materializing a private Home and sanitized process environment:
import { defineAgent } from "@vite-hub/agent";
import { useServerEnv } from "#vitehub/env/server";
export default defineAgent<any, { ref: string; remote: string; sha: string }>({
box: {
runtime: { kind: "trusted-host", stateRoot: "/var/lib/vitehub/boxes" },
checkout: {
ref: ({ input }) => input.options?.ref,
remote: ({ input }) => input.options?.remote,
sha: ({ input }) => input.options?.sha,
},
env: {
GH_TOKEN: () => useServerEnv().githubToken.unseal(),
},
home: {
files: {
".gitconfig": { from: ".vitehub/box/gitconfig" },
".codex/config.toml": { from: ".vitehub/box/codex.toml" },
},
state: {
".codex": {
key: "babysitter/codex",
seed: {
"auth.json": { contents: () => useServerEnv().codexAuthJson.unseal() },
},
},
},
},
requires: [{ command: "gh", args: ["auth", "status"] }, "pnpm"],
},
driver: "codex",
});checkout fetches one invocation-resolved Git ref, verifies its full SHA, and runs the harness in an isolated detached checkout with normal commit and explicit-push behavior. The Box deletes it on completion or boot failure. Use cwd instead for a caller-owned authoritative directory; the two modes are mutually exclusive.
env and home.files are immutable boot inputs. home.state is writable, persists CLI refreshes under an exclusive session lease, and resolves its seed only when state does not exist. Every Box gets a private Home; missing declarations fail instead of falling back to the machine's normal Home. The "codex" driver contributes a generic codex login status check, and other CLIs use string or direct-argv requires entries without provider-specific Box APIs. Do not combine box.cwd or box.checkout with Agent Workspace materialization.
For model-backed drivers, put free-form guidance for configured Sources, Capabilities, and Skills in driver.instructions or a deterministic imported instruction file. Tool descriptions and schemas stay with the tools as structured contracts.
// vite.config.ts
import { hubAgent } from "@vite-hub/agent/vite";
import { hubWorkspace } from "@vite-hub/workspace/vite";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [hubWorkspace(), hubAgent({ routes: { chat: true } })],
});CLI inspection
With hubAgent() active, start the Vite Development Server and run vitehub agent info --agent <name>.
The command reads the resolved Agent Definition metadata without invoking the Agent Driver, so use it to verify the selected Driver, tools, Workspace files and Sources, instructions, Agent Invoker Profiles, warnings, and metadata status before debugging model output.
Pass --json for the structured inspection contract.
Capabilities
- Set
routes.chatonhubAgent()to publish the web chat dispatcher, then use awebChat()Channel on each Agent that should answer it. Usechat()when an app-owned trigger needs Chat History andchat.messagebehavior without Channel-owned route exposure; see the First Agent guide. workspaceShell()runs scoped shell/file work through@vite-hub/shell.webSearch()searches and reads the web with Brave, Exa, Jina, SearXNG, SerpApi, SerpBase, or Tavily.openapi()turns an allowed OpenAPIoperationIdsubset into bounded HTTP tools, or into a generated Capability CLI whencliis set.papercuts()lets an Agent report small runtime and developer-experience friction to an application-owned sink, with an optional Capability CLI command.transcribe()uses the AI SDK transcription API.createTranscription()composes remote asynchronous submission and completion through a provider-neutral driver;elevenLabsScribe()is the built-in Scribe v2 adapter.mcp()connects tools from Model Context Protocol servers through@ai-sdk/mcp.kv(),blob(),db(), andemail()expose@vite-hub/kv,@vite-hub/blob,@vite-hub/database, and@vite-hub/email.sandbox()andschedule()expose@vite-hub/sandboxand@vite-hub/schedule.skills(),access(),memory(),fetch(),llmRoute(), andllmGate()cover prompt skills, workspace scope, durable notes, HTTP reads, and pre-run decisions.
import { openapi } from "@vite-hub/agent/capabilities";
openapi({
spec: "https://api.example.com/openapi.json",
cli: {
name: "billing",
description: "Inspect live billing API data.",
},
operations: ["billingListCustomers", "billingGetInvoice", "billingCreateTicket"],
hooks: {
request: {
provides: {
body: ["tenantId"],
},
handler({ context, request }) {
request.body = {
...(request.body as Record<string, unknown> | undefined),
tenantId: context.get<{ tenantId: string }>("billing")?.tenantId,
};
request.headers.set(
"authorization",
`Bearer ${context.get<{ token: string }>("billing")?.token}`,
);
},
},
},
transformResponse: (response, { operation }) => ({
operationId: operation.id,
response,
}),
});spec can be a callback when the OpenAPI document comes from the current Agent Invocation context. Request servers come from OpenAPI servers; use server only as an override escape hatch when the spec has no usable server.
When cli is set, the operation tools are replaced by one CLI-named tool. ViteHub generates one subcommand per allowed operation, using the OpenAPI operation summary or description for command guidance.
Capability cli can be a static command tree or an invocation resolver that returns undefined when the CLI should not be available. Generated command trees stay behind adapter-owned options such as openapi({ cli }), whose resolver may return false or undefined for the current invocation.
Chat state
Chat History and the Concurrent Invocation Guard need an Agent State Provider when they should survive a process restart. The default provider: "auto" uses Cloudflare state on Cloudflare and local SQLite at file:.data/vitehub-agent-state.sqlite during Vite development. Production Node and serverless output require VITEHUB_AGENT_STATE_URL or explicit provider options because ViteHub cannot infer a durable filesystem there.
// vite.config.ts
export default defineConfig({
agent: {
providers: {
state: {
provider: "sqlite",
url: process.env.VITEHUB_AGENT_STATE_URL,
},
},
},
});provider: "sqlite" uses the built-in libSQL-compatible state backend, so file: URLs work for local or explicitly persistent Node deployments and hosted libSQL URLs work remotely. Cloudflare, Vercel, and Netlify production output rejects file: Agent state before it can write to an ephemeral filesystem.
You can also wire the adapter manually when chat({ state }) should own the state provider:
import { createLibsqlAgentState } from "@vite-hub/agent/state/sqlite";
chat({
state: () =>
createLibsqlAgentState({
url: process.env.VITEHUB_AGENT_STATE_URL!,
}),
});This is not the Database Capability. It is Agent-owned runtime state for chat behavior.
Built on
Vite discovers Agent files and generates runtime state for the active server host. Route-enabled Channels contribute host routes. Model execution uses AI SDK; Provider Tools stay Capability-scoped instead of becoming one global Agent config.
Learn more at vitehub.dev.
