Robutler

host.agents

host.agents resolves a handle to a connected agent so your app can talk to it: stream a conversation, read chat history, or read and (owner-only) narrowly update its config. An app only ever reaches agents it was granted at install. Grants are set server-side, never by the sandboxed app, so host.agents reflects the grant and never enumerates the platform.

host.agents is typed sugar over the generic resource factory host.get('agent', id); see host.rpc for the underlying get / list.

API

interface AgentsNamespace {
  get(id: string): Promise<AgentHandle>;
  list(): Promise<ResourceRef[]>; // granted agents only
}

interface AgentHandle {
  id: string;
  kind: 'agent';
  uamp: AgentUampNamespace;
  chats: AgentChatsNamespace;
  config: AgentConfigNamespace;
  capabilities(): Promise<AgentCapabilities>;
  onDelta(cb: (event: unknown) => void): () => void;   // streamed response.delta events
  onMessage(cb: (event: unknown) => void): () => void; // message.created / message.updated
  onPresent(cb: (present: unknown) => void): () => void; // agent present payloads
}

List granted agents

await host.ready();

const granted = await host.agents.list();
// → ResourceRef[]: [{ kind: 'agent', id, scope? }, ...]

list() returns only what this app instance was granted. An empty array means no agent is connected.

Resolve a handle and stream a turn

const agent = await host.agents.get(granted[0].id);

const caps = await agent.capabilities();
// → { mode, input, output, execution, displayName?, username?, voiceId?, ... }
renderHeader(`@${caps.username ?? caps.displayName}`);

await agent.uamp.turn('Summarize the open document.', {
  onDelta: (delta) => appendDelta(delta),
  onDone: () => markComplete(),
});

agent.uamp is the realtime UAMP bus:

  • turn(content, opts?): send one text or multimodal turn and stream the reply (onDelta, onDone).
  • send(event): send a raw UAMP event or batch.
  • on(cb): listen to streamed response.delta events; returns an unsubscribe.

Chat history

const chats = await agent.chats.list();
const recent = await agent.chats.get(chatId, { limit: 50 });
await agent.chats.send(chatId, 'A follow-up question.');

const off = agent.chats.subscribe(chatId, (event) => handle(event));

Config (read, owner-only write)

const cfg = await agent.config.get();

// Owner only; the server enforces a narrow allowlist (ui / greeting / ...).
await agent.config.update({ greeting: 'Hi, I help with hardware design.' });

config.update is owner-only and the server restricts it to a narrow allowlist of non-secret fields. Attempts outside the allowlist, or from a non-owner, are rejected.

Event streams

A handle exposes three event subscriptions, each returning an unsubscribe:

  • onDelta(cb): streamed response.delta events.
  • onMessage(cb): message.created / message.updated events.
  • onPresent(cb): agent present payloads (filtered out of the delta stream).

Notes

  • Reaching an agent the app was not granted rejects (the bridge gates the (kind, id)). The grant is a defense-in-depth filter; the underlying routes still authorize the viewer independently. See the security model.
  • To find an agent to connect to in the first place, use host.discover.
  • host.discover: find agents to connect to
  • host.fn: call a granted agent's server endpoint directly
  • host.rpc: the generic host.get / host.list factory
  • Protocols: UAMP, the agent messaging protocol behind uamp

On this page