@finos/sail-browser-agent
Browser-ready FDC3 Desktop Agent for 2.2 and 3.0 — SailDesktopAgent, versioned DACP handlers, channel and intent state, app directory, and WCP BrowserAppConnection.
Location: packages/sail-browser-agent/
What it does
- Implements FDC3 Desktop Agent APIs via DACP for wire versions 2.2 and 3.0 (negotiated per app at WCP1; default advertised max is
3.0) - Browser-first: one
DesktopAgentper host page, with FDC3 web apps connecting via WCP and per-appMessagePort - Keeps platform concerns (layout, workspace, storage, config) out of the core — those belong in
@finos/sail-platform
Handler-level tests run under Node.js via Cucumber and Vitest, but the browser is the only supported deployment target.
How to integrate
SailDesktopAgent is the entry point — construct it, implement AppLauncher, wire host
UI through the grouped controllers. The app-connection edge (WCP handshake, per-app
MessagePort, routing) is internal to the agent; there is no transport to configure and
no lower-level composition path to assemble.
FDC3 Apps Your host FDC3 engine (in-tab)
(external) → contracts + controllers → new SailDesktopAgent()
fdc3.getAgent() launcher · intentResolver (BrowserAppConnection + DA)
channels · apps
Remote or server-hosted Desktop Agent (createWCPClient) is not a supported adoption path on v3. Cross-tab sync and native app transports are deferred as explicit future adapters — see the integrator guide.
Package layout
packages/sail-browser-agent/src/
├── agent/ # DesktopAgent, SailDesktopAgent, config
├── host-contracts/ # AppLauncher, IntentResolver, ChannelControl, …
├── app-connection/ # BrowserAppConnection, MessagePortTransport, WCP protocol
├── handlers/ # DACP dispatch and request handlers
└── state/ # AgentState selectors and mutators
Documentation map
| Doc | Purpose |
|---|---|
| Add your app to Sail | App developer onboarding — @finos/fdc3, app directory metadata, contexts and intents |
| Getting started | Adoption paths — browser-ready agent, host contracts, @finos/fdc3 |
| Integrator guide | Deep reference — host contracts, browser-first agent, WCP/DACP detail, heartbeat config |
| Composition & internals | Diagrams — how edge, DA, and host contracts interact |
| Engine BDD inventory | Cucumber DACP suite in sail-headless-agent (v2/ / v3/) |
| Conformance harness | FINOS toolbox against sail-one, sail-finance, sail-v2-web |
Quick start
import { SailDesktopAgent } from "@finos/sail-browser-agent"
import type { AppLauncher } from "@finos/sail-browser-agent"
const appLauncher: AppLauncher = {
async launch(request, app) {
const instanceId = request.app?.instanceId ?? crypto.randomUUID()
// Mount iframe with name={instanceId} — required for WCP4 identity
return { appId: app.appId, instanceId }
},
}
const desktopAgent = new SailDesktopAgent({ appLauncher })
const { intentResolver, channels, apps } = desktopAgent
await apps.addDirectory("/apps.json")
intentResolver.onRequest(/* your picker UI */)
channels.onAppChannelChange(/* update channel chrome */)
apps.onConnect(/* tab / tile lifecycle */)
// Auto-started by default — iframe apps can await fdc3.getAgent()
Returns a single SailDesktopAgent with grouped host controllers (intentResolver, channels, apps); the browser edge starts and stops with desktopAgent.start() / desktopAgent.stop().
See the integrator guide for intent resolution, channel chrome, runtime catalog registration, and lifecycle.
Imports
Everything public comes from the package root — there are no subpath exports.
import { SailDesktopAgent, DEFAULT_FDC3_USER_CHANNELS } from "@finos/sail-browser-agent"
import type { AppLauncher, IntentResolver, DirectoryApp } from "@finos/sail-browser-agent"
Message validation
Inbound DACP and WCP messages are validated against the FDC3 schemas shipped by
@finos/fdc3-schema for the connection's negotiated wire version (2.2 or 3.0).
| Mode | Behaviour |
|---|---|
"off" | No validation |
"warn" (default) | Log the failure, dispatch the message anyway |
"strict" | Reject the message with an FDC3 MalformedMessage error |
const desktopAgent = new SailDesktopAgent({ appLauncher, validation: "strict" })
"warn" is the default because rejecting is a behaviour change — a client library sending
a slightly off-spec shape works today, and "strict" would break it. Warn surfaces the
problem in logs first so you can decide.