Skip to main content
On the GCS half the host runs the bridge; the plugin consumes two flavors of message:
  • Events: pushed from the host, no response expected (config.changed, theme.changed, telemetry.<topic>).
  • Responses: returned by the host in reply to a plugin request.
Plugins subscribe to events and send requests; they never receive unsolicited requests.

Events the host pushes

Subscribe via ctx.client.on("config.changed", handler) or the typed wrappers ctx.config.onChange, ctx.theme.onChange, and ctx.telemetry.subscribe. For any other host-pushed topic (for example the video.overlay host props the host forwards to a video overlay iframe, or an agent plugin’s state read-back), use the generic ctx.events.subscribe(topic, handler), which returns an unsubscribe function and does no request/response round-trip.

Requests the plugin can send

The SDK context exposes these high-level wrappers. Each delegates to a single PluginClient; drop to ctx.client.request(method, capability, args) for anything else. The host registry also recognizes telemetry.unsubscribe, recording.start and recording.stop (capability recording.write), events.subscribe and events.publish (event.subscribe / event.publish, both require a topic arg), cloud.read and cloud.write, plus three always-allowed methods that need no capability: ping, notify, and i18n.t. A method that is not in the registry is rejected with method_unknown. The host re-resolves the required capability from the method (and, for telemetry, from the topic) and checks the granted set. The plugin never computes the capability id itself.

Wire shape

The plugin never builds these by hand. The SDK’s PluginClient generates the id, attaches the protocol version, and waits for the correlated response.

Error envelopes

When the host rejects a request it returns a response with error: { code, message }. The SDK throws a HostError whose code field carries the machine-readable code: Branch on code, not on message. Messages are for log lines.
There is also an origin_mismatch error, but the host never sends it to the plugin. When a message arrives from a window that is not the trusted iframe, the bridge drops it and records a security event host-side rather than replying. A plugin will never see it on the wire.

Theming

theme.changed arrives once on mount and again on every theme toggle. The payload is a Record<string, string> of CSS variables. Apply them by walking the entries:
The SDK does not auto-apply because plugins choose how granular they want to be (full root, scoped to a panel, or ignored).

i18n

Plugin authors ship a locale bundle in their archive; the host streams the active bundle to the plugin on mount. The SDK formats keys with ctx.i18n.t(key, params):
Missing keys fall back to the key itself; missing parameters render the {name} placeholder so it is visible in QA.

See also