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.
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.
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:
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.
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 and its Trigger |
| A model delegates to another Agent | Subagents Capability |
Webhook adapters may retain ownership until delivery finishes. Configure Channel timeout, concurrency, and durable delivery there rather than adding webhook policy to the Driver.