ViteHub

Agent runtime API

Reference for Agent exports, inputs, context, and module options.

Use this page for exact names and shapes. For setup, start with Quickstart.

Imports

import {
  defineAgent,
  defineCapability,
  getAgent,
  runAgent,
  streamAgent,
  usageTelemetry,
  vercelAiGatewayPricing,
} from '@vitehub/agent'
import {
  workspaceShell,
  blob,
  db,
  kv,
  mcp,
  sandbox,
  skills,
} from '@vitehub/agent/capabilities'
import {
  callsTool,
  defineEval,
  doesNotCallTool,
  doesNotLeakSource,
  staysUnderTokenBudget,
  textContains,
} from '@vitehub/agent/eval'

In Nitro, the module auto-imports defineAgent for discovered agent definitions. Capability factories such as workspaceShell() stay explicit imports because they expose model-facing tools.

export default defineNitroConfig({
  modules: ['@vitehub/agent/nitro'],
})

Define an agent

defineAgent({
  description?: string
  capabilities?: AgentCapabilityDefinition[]
  hooks?: AgentInvocationHooks
  instructions?: AgentAdapterInstructions
  model?: unknown
  adapter?: 'ai-sdk' | 'tanstack-ai' | string
  adapterOptions?: Record<string, unknown>
  run?: AgentRunHandler
  workspace?: string | ({ mode?: 'read' | 'write' } & WorkspaceAgentWorkspaceOptions)
})

Capabilities are the public model-facing extension surface. adapter explicitly selects the model adapter; ViteHub does not infer it from installed packages or model object shape.

defineAgent({
  workspace: {
    mode: 'read',
    sources,
  },
  capabilities: [
    workspaceShell(),
  ],
  instructions: 'Use workspace sources.',
  model,
  adapter: 'ai-sdk',
  adapterOptions: {
    providerOptions: {
      openai: { reasoningEffort: 'medium' },
    },
  },
})

Adapter options forward settings to the selected model adapter. For AI SDK adapters, providerOptions remains the place for model-provider-specific options.

Workspace agents do not attach workspace tools automatically. The workspace option defines the explicit primary filesystem boundary; capabilities decide what the model can use at runtime.

defineAgent({
  workspace: { mode: 'read', sources },
  capabilities: [
    workspaceShell(),
  ],
  instructions: async ({ fs }) => await fs.readFile('AGENTS.md'),
  model,
  adapter: 'ai-sdk',
})

Use workspaceShell() for default read-only workspace shell inspection. Use workspaceShell({ mode: 'write' }) only with workspace.mode: 'write'. Use inline or factory capabilities to expose raw tools:

defineAgent({
  capabilities: [
    defineCapability({
      id: 'custom-tools',
      tools: {
        lookup: {
          name: 'lookup',
          execute: async input => input,
        },
      },
    }),
  ],
  model,
  adapter: 'ai-sdk',
})

Agent invocation hooks

Use agent:finish to observe a completed Agent Invocation. The hook runs after object results are rendered and after streamed or Response results are consumed or canceled.

defineAgent({
  hooks: {
    'agent:finish'(event) {
      const usage = event.extensions.get<AgentUsageRecord>('usage-telemetry')
    },
  },
  capabilities: [
    usageTelemetry(),
  ],
  model,
  provider: 'ai-sdk',
})
interface AgentFinishEvent {
  input: AgentRunInput
  result?: unknown
  error?: unknown
  runtime: ResolvedAgentRuntimeContext
  invocation: {
    durationMs: number
    run?: AgentRunMetadata
  }
  extensions: {
    get<T = unknown>(capabilityId: string): T | undefined
  }
}

Capabilities can expose optional data on finish events through extension keys that match their Capability ID. Return undefined to omit the extension value for that invocation.

defineCapability({
  id: 'audit-log',
  output(context) {
    context.finish.provide(event => event.result)
  },
})

ViteHub-owned usage telemetry uses usage-telemetry.

Run input

interface AgentRunInput {
  messages?: Message[]
  prompt?: string | Message[]
}

Message comes from @vitehub/agent and is re-exported by @vitehub/agent.

Agent usage telemetry

usageTelemetry({
  includeRaw?: boolean
  pricing?: AgentUsagePricing
})
interface AgentUsage {
  inputTokens?: number
  outputTokens?: number
  totalTokens?: number
  inputTokenDetails?: Record<string, number>
  outputTokenDetails?: Record<string, number>
  raw?: unknown
}

interface AgentUsageRecord {
  usage?: AgentUsage
  model?: { id?: string; provider?: string }
  response?: { id?: string; timestamp?: Date | string; finishReason?: unknown }
  latency?: { durationMs?: number; timeToFirstTokenMs?: number; tokensPerSecond?: number }
  cost?: AgentUsageCost
  run?: Partial<AgentRunMetadata>
  raw?: unknown
}

usageTelemetry() uses the Capability output phase. It normalizes finished object results only; streamed Response and async iterable results are returned unchanged. When an Agent Finish Hook is configured, the same record is available through event.extensions.get('usage-telemetry').

usageTelemetry({
  pricing: vercelAiGatewayPricing(),
})

Agent evaluations

Agent evaluations use the Agent Definition's Runtime Config type. @vitehub/agent/eval does not depend on @vitehub/env; ViteHub Env is one optional producer of values passed through runtimeConfig.

defineEval({
  agent?: AgentInput | (() => MaybePromise<AgentInput>)
  name?: string
  runtimeConfig?: AgentRuntimeConfig | (() => MaybePromise<AgentRuntimeConfig>)
  scenarios: AgentEvalScenario[]
  scorers?: AgentScorer[]
  variants?: AgentEvalVariant[]
  workspace?: string
})
interface AgentEvalScenario {
  name: string
  input: AgentRunInput
  metadata?: unknown
  scorers?: AgentScorer[]
}

interface AgentEvalVariant {
  name: string
  instructions?: string | string[]
  model?: unknown
}

interface AgentScore {
  score: number
  passed?: boolean
  reason?: string
  metadata?: unknown
}

Built-in scorers are textContains(), doesNotLeakSource(), callsTool(), doesNotCallTool(), and staysUnderTokenBudget().

When agent is omitted, defineEval() imports the Agent Definition by convention. name.eval.ts resolves to sibling name.ts; folder-level eval.ts resolves to sibling config.ts.

Runtime context

interface AgentRuntimeContext {
  request?: Request
  runtime: 'nitro' | 'vercel' | 'cloudflare-agents' | 'unknown'
  waitUntil?: (promise: Promise<unknown>) => void
  capabilities?: AgentCapabilities
  memo: <T>(key: string, factory: () => T | Promise<T>) => T | Promise<T>
}

Agent callbacks receive runtime host metadata, but not raw runtime config. Use useServerEnv() from #vitehub/env/server for app-owned Runtime Env values.

Module options

interface AgentModuleOptions {
  route?: boolean | string
  runtime?: 'auto' | 'nitro' | 'vercel' | 'cloudflare-agents'
  execution?: 'inline' | 'workflow' | 'sandbox'
  imports?: boolean
  integrations?: {
    workflow?: 'auto' | boolean
    sandbox?: 'auto' | boolean
  }
  providers?: {
    state?: { provider?: 'auto' | 'memory' | 'cloudflare-agents' | string }
    scheduler?: { provider?: 'auto' | 'memory' | 'cloudflare-agents' | string }
    sandbox?: { provider?: 'auto' | 'cloudflare' | 'vercel' | string }
  }
}

Tool policy

const refundTool = {
  name: 'refund',
  description: 'Refund an order',
  policy: 'require-approval',
}

Tool policy metadata travels with the tool definition. Runtime enforcement belongs to the executor that receives the tool handle.

Copyright © 2026