Skip to content

Host capabilities

Status: Current | Last reviewed: 2026-05-28

A surface-app agent can suggest host actions — UI actions the host page should carry out, like "open the email composer," "scroll to record X," "show a modal." For those suggestions to work, the host must tell surface-app which actions it supports. That's what set-capabilities and context-update do.

Why capabilities exist

Without capability advertisement, an agent has two bad choices:

  • Suggest every host action it knows about, half of which the host can't actually do, and have the user click a dead button.
  • Suggest no host actions, and miss helpful in-product nudges.

By advertising capabilities, the host narrows the agent's suggestions to ones that will work. The agent's prompt is augmented with the capability list at runtime; suggestions outside the list are filtered before they're rendered.

Capability naming

Capability names are dot-namespaced strings:

salesforce.emailComposer.open
salesforce.recordPage.navigate
dashboard.modal.open
dashboard.notification.show
sales.cart.addItem

The convention is <host-family>.<surface>.<action>. Anything stable and unique works — surface-app does no semantic matching; it's a string equality check.

Advertising capabilities

After the iframe sends ready, the host posts:

iframe.contentWindow.postMessage(
  {
    source: "trueserve-sfdc",
    type: "set-capabilities",
    payload: {
      capabilities: [
        "salesforce.emailComposer.open",
        "salesforce.recordPage.navigate",
        "salesforce.utilityBar.expand",
      ],
      hostFamily: "sfdc-lightning",
      hostVersion: "60.0",
    },
  },
  embedTokenAllowedOrigin,
);

surface-app accepts the message (origin check + source check pass), updates its capability set, and from this point forward filters tool suggestions accordingly.

You can call set-capabilities more than once. The second call replaces the first. Useful when the host's available actions change as the user navigates.

Reacting to host_action

When the agent emits a host-action conversation-event, the host receives:

{
  "source": "trueserve-surface-app",
  "type": "conversation-event",
  "payload": {
    "embedId": "...",
    "conversationId": "...",
    "event": {
      "kind": "host-action",
      "action": "salesforce.emailComposer.open",
      "params": { "to": "lead@example.com", "subject": "Following up" }
    }
  }
}

The host listens for this, dispatches to its own action handler, and either:

  • Performs the action immediately (suggested action becomes invisible to the user — the action just happens).
  • Renders a confirmation UI ("The agent suggests sending this email — Send / Edit / Cancel").

Confirmation is the right pattern when the action has user-visible side effects. Auto-execution is the right pattern for navigations and modal opens.

Pushing context

Capabilities are static-ish — they describe what the host can do. Context is dynamic — what the user is looking at right now. Push context whenever it changes:

iframe.contentWindow.postMessage(
  {
    source: "trueserve-sfdc",
    type: "context-update",
    payload: {
      pageContext: {
        pageType: "case-detail",
        pageLabel: "Case 00001234",
        entityId: "5003X00001abcd",
        summary: "Open Case for Acme Insurance, opened 2026-05-15, escalated to Tier 2.",
      },
      componentContext: null,
    },
  },
  embedTokenAllowedOrigin,
);

surface-app folds the summary strings into the agent's system prompt at the next turn. Keep them tight — the LLM has a finite context window, and verbose context burns tokens that could go toward the user's actual question.

Putting it together

A complete embed lifecycle:

iframe loads → emits "ready"
host receives "ready" → posts "set-capabilities"
host receives "ready" → posts "context-update" (initial state)
user interacts → host posts "context-update" each time the visible page changes
agent suggests host_action → host receives "conversation-event" with kind="host-action"
host acts on it (or renders confirmation)

Capabilities once, context many times, host actions on demand. That's the pattern.

See also