ViteHub is still experimental. Expect bugs and breaking changes.

Channels

Keep message origins, delivery facts, Agent Actors, and input commands separate.

A Channel names where an Agent Invocation came from and how message-shaped events move through the system. Channels carry origin, event, delivery, thread, and message facts; they do not carry trusted caller identity by themselves.

Use Channels for reachability and delivery. Use Agent Actors for identity, and use input commands for explicit user-authored command handling. The current API still exposes Agent Actor values through context.invoker and defineAgent({ invoker }) while the public language migrates.

Channel Kind helpers are imported from @vite-hub/agent/channels, not the root @vite-hub/agent entry.

server/agents/support.ts
import { defineAgent } from '@vite-hub/agent'
import { github, stream, webChat } from '@vite-hub/agent/channels'

export default defineAgent({
  channels: {
    github: github({ events: { pullRequestComments: true } }),
    portal: stream({ route: true }),
    web: webChat(),
  },
  run: () => 'ok',
})

Built-in helpers include discord(), github(), http(), slack(), teams(), telegram(), stream(), and webChat(). Use defineChannel(kind, options) for an app-owned Channel Kind.

Boundary map

BoundaryOwnsDoes not own
ChannelOrigin, event, delivery, thread, and message metadata.Trusted identity, access decisions, command rewriting.
Agent ActorTrusted caller identity for one Agent Invocation.Transport delivery, webhook shape, UI session state.
Input CommandUser-authored command parsing and input rewriting before the Agent Driver runs.Channel verification, delivery, caller identity.

This split keeps shared channels from becoming implicit access roles. A Teams channel, GitHub comment, or app chat thread can reach an Agent without proving who the trusted caller is.

Message-shaped input

Chat and channel surfaces usually start Agents with messages. The Chat Capability registers the chat.message Agent Trigger and translates UI-message-like input into Agent messages.

server/api/support-chat.post.ts
import { streamAgentTrigger } from '@vite-hub/agent'
import support from '../agents/support'

export default defineEventHandler(async (event) => {
  const body = await readBody<{ text: string, threadId?: string }>(event)
  const runId = crypto.randomUUID()

  return streamAgentTrigger(support, { runtime: 'unknown' }, 'chat.message', {
    messages: [{
      id: runId,
      parts: [{ text: body.text, type: 'text' }],
      role: 'user',
    }],
    run: {
      channelId: 'portal-support',
      messageId: runId,
      origin: 'portal',
      runId,
      threadId: body.threadId,
    },
  })
})

The run fields are Agent Run State and observability metadata. They help DevTools, traces, and finish hooks explain where the invocation came from.

Add identity separately

When the channel handler authenticates a user, pass that identity as the Agent Actor. The current runtime input field is still named invoker, so Agents and Capabilities read context.invoker until the compatibility API is replaced.

server/api/support-chat.post.ts
return streamAgentTrigger(support, { runtime: 'unknown' }, 'chat.message', {
  messages,
  invoker: {
    id: user.id,
    kind: 'customer',
    label: user.email,
    meta: { customer: user.customer },
  },
  run,
})

Validate the channel request before passing the Agent Actor. ViteHub trusts actor values supplied by server-owned Agent Trigger Consumers.

Keep commands in Capabilities

Input Commands are Capability behavior. A channel can deliver /summary, but inputCommands() should own command admission, rewriting, and command-specific trust.

Link command docs and command examples to Capabilities, not to channel configuration.

Next steps

Copyright © 2026