ViteHub is still experimental. Expect bugs and breaking changes.

Agent Actors

Carry trusted caller identity into one Agent Invocation while using the current invoker-named API fields.

An Agent Actor is the trusted caller identity for one Agent Invocation. It carries a stable id, optional kind, optional display label, normalized email, and application-owned meta.

Agent Actors are not Auth Users, Channels, or Access roles. Auth and Channels can establish an Actor, and Access can use it, but each boundary keeps its own responsibility.

Use the current API names

The public concept is Agent Actor. The current configuration and invocation fields retain their invoker names.

PurposeCurrent API
Configure profiles and resolutiondefineAgent({ invoker }) and defineAgentInvoker()
Pass a trusted Actor to runAgent() or streamAgent()input.context.invoker
Pass a trusted Actor to chat.messagetop-level invoker trigger input
Select a profile for a direct invocationinput.context.invokerProfileId
Select a profile for chat.messagetop-level invokerProfileId trigger input
Read the resolved Actor in callbacksactor or invoker
Read the resolved Actor from the context storecontext.get('actor') or context.get('invoker')
Type the public conceptAgentActor; AgentInvoker remains the type used by invoker-named APIs

Both callback properties and both context-store keys contain the same normalized Actor. Use actor in domain-facing callback code and keep the exact invoker spelling at configuration and invocation boundaries.

Actor fields

FieldTypeRequiredDescription
idstringYesStable identity used by Access, rate limits, state partitioning, and inspection. Empty ids are rejected.
kindstringNoIdentity family such as anonymous, chat, devtools, customer, or an app-owned value.
labelstringNoHuman-readable display value for DevTools and logs.
email{ address, domain }NoNormalized lowercase email output. ViteHub derives domain from the address and omits invalid email values.
metaRecord<string, unknown>NoApplication-owned trusted facts. Validate them before invocation.

Pass an Actor from server code

Server-owned invocation surfaces can pass an Agent Actor after authenticating the request. The current trusted input key is context.invoker.

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

export default defineEventHandler(async (event) => {
  const body = await readBody<{ prompt: string }>(event)
  const user = await requireAuthenticatedUser(event)

  return runAgent(support, { runtime: 'unknown' }, {
    prompt: body.prompt,
    context: {
      invoker: {
        id: user.id,
        kind: 'customer',
        label: user.email,
        meta: { customer: user.customer },
      },
    },
  })
})

ViteHub trusts this value because it comes from server code. Validate signatures, sessions, and product permissions before creating it.

When no Actor is supplied, ViteHub creates a fallback Actor from Agent Run metadata. DevTools uses id: 'devtools'; other origins use id: 'anonymous:<origin>'.

Configure Actor profiles

Profiles are static selectable Actors on an Agent Definition. They are useful for DevTools, local development, schedules, and trusted app routes that need a known profile id.

server/agents/support.ts
import { gateway } from '@ai-sdk/gateway'
import { defineAgent, defineAgentInvoker } from '@vite-hub/agent'

export default defineAgent({
  driver: {
    model: gateway('openai/gpt-5.1-mini'),
    instructions: 'Answer support requests.',
  },
  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' },
      },
    ],
  }),
})

Select one profile with input.context.invokerProfileId. The chat.message trigger accepts the same selector as top-level invokerProfileId. Unknown profile ids fail the invocation instead of falling back silently.

await runAgent(support, runtime, {
  context: { invokerProfileId: 'support-admin' },
  prompt: 'Summarize open incidents.',
})
invoker optionTypeDescription
profilesreadonly AgentInvokerProfile[]Declares an ordered list of unique Actor profiles.
resolvecallbackNormalizes, replaces, or rejects the selected Actor before Capabilities run. Returning null or undefined keeps the selected or fallback Actor.

Resolve and normalize an Actor

Use invoker.resolve when an Agent needs to normalize trusted invocation metadata before Capabilities run. The resolver can also store typed Agent Invocation Context Values for later callbacks.

server/agents/support.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
  },
})
Resolver valueDescription
defaultInvokerTrusted input Actor, or the fallback Actor when the input omitted one.
selectedProfileProfile selected by invokerProfileId, when present.
profilesAll configured profiles for this Definition.
inputFull Agent Invocation input.
contextMutable Agent Invocation Context Store.
runOptional origin, channel, thread, message, schedule, and run metadata.

When a selected profile and an input Actor both contain meta, ViteHub keeps input metadata and lets profile metadata override matching keys. A profile email takes precedence over the input email.

Use Actors for access

Access decisions should use the Agent Actor rather than Channel identity. A shared Channel can contain multiple users with different permissions.

server/agents/support.ts
import { access } from '@vite-hub/agent/capabilities'

export const supportAccess = access({
  workspace: {
    resolve({ actor }) {
      if (actor.meta?.scope === 'all') {
        return { all: true, scope: 'support' }
      }

      const customer = String(actor.meta?.customer ?? '')
      return {
        grants: [{ path: `customers/${customer}` }],
        scope: customer || 'public',
      }
    },
  },
})

Keep Access roles inside the Access Capability and caller identity on the Agent Actor. A normalized Actor email is available as actor.email?.address and actor.email?.domain; normalization does not verify ownership of the address.

Next steps

  • Read Channels for origin and delivery metadata.
  • Read Invocations for trusted input and lifecycle behavior.
  • Read Capabilities for Access and rate-limit policy.
Copyright © 2026