Skip to main content

Composition & internals

How @finos/sail-browser-agent modules compose and interact. For integration steps and copy-paste examples, see the integrator guide.

Layered runtime model​

Key rule: apps never talk to DesktopAgent directly. In browser hosts, app traffic flows MessagePort → BrowserAppConnection → DesktopAgent → AppConnectionRegistry → MessagePort.

One construction path​

SailDesktopAgent owns the browser edge, the state binding, and the lifecycle. It is not an assembly of parts you can swap: it extends DesktopAgent, and a bare DesktopAgent throws on any DACP routing until an edge is attached.

You provideYou get
AppLauncher — mount the iframe with name = instanceIdintentResolver, channels, apps controllers plus start() / stop()

AgentAppConnection — the contract the edge implements — is deliberately not exported. A replacement edge (WebSocket, native host) would ship as its own package implementing that contract, at the point one is actually needed.

Deferred deployment paths (not on v3)​

Remote Desktop Agent (createWCPClient + server-hosted engine) was removed from presets. Native desktop apps and multi-device sync are planned as app-connection or sync adapters — not as a reason to host the core DA on a remote process. See the integrator guide — deferred paths.

Source tree responsibilities​

packages/sail-browser-agent/src/
│
├── agent/
│ ├── desktop-agent.ts # DesktopAgent class — start/stop, handler dispatch
│ ├── sail-desktop-agent.ts # Browser-ready constructor + host controllers
│ └── default-config.ts
│
├── host-contracts/
│ ├── app-launcher.ts # AppLauncher — host opens iframes/windows
│ ├── intent-resolver.ts # IntentResolver — disambiguation UI contract
│ └── channel-control.ts # ChannelControl — picker contract shape
│
├── app-connection/
│ ├── browser-app-connection.ts # WCP listener + MessagePort registry
│ ├── wcp/ # WCP handshake, routing, connection map
│ └── message-port.ts
│
├── handlers/
├── state/
├── dacp/
│ └── validate-dacp-message.ts # FDC3 schema validation — off | warn | strict
└── app-directory/

WCP and DACP ownership​

PhaseOwnerCode location
WCP1–3Edgeapp-connection/browser-app-connection.ts, app-connection/wcp/wcp1-3-handshake.ts
MessagePort bridgeEdgeapp-connection/message-port.ts, app-connection/wcp/wcp-message-routing.ts
WCP4–5DA (+ edge port migration)app-connection/wcp/wcp-identity-validation.ts, handlers/open/handlers.ts
WCP6 GoodbyeBothEdge drops port; DA removes instance
DACP (all fdc3.*)DAhandlers/*

Instance identity pipeline​

Toolbox AppTimeout usually means a break in this chain:

StepModule
Launcher returns idhost-contracts/app-launcher.ts
Open registers PENDINGhandlers/open/handlers.ts
WCP4 adopt vs mintapp-connection/wcp/wcp-identity-validation.ts
Port map migrationapp-connection/wcp/wcp-connection-management.ts

Intent resolution flow​

Two mechanisms exist for intent UI — see integrator guide — intent resolver:

  • Host shell (default): intentResolver controller or low-level IntentResolver contract
  • WCP3 injection: appConnectionOptions.intentResolverUrl — @finos/fdc3 loads iframe in app window

Channel change flow (host chrome)​

Browser hosts use channels.changeAppChannel and channels.onAppChannelChange — see integrator guide.

SailDesktopAgent exposes these controllers directly.

Testing layers​

LayerSuiteProves
DACP handlersCucumber in sail-headless-agent (v2/ + v3/ features)FDC3 2.2 and 3.0 handler behaviour
Handler unitsVitest in dacp/__tests__/Individual request paths
Edge seamwcp-desktop-agent.integration.test.tsWCP + MessagePort + DA routing
Full oracleFINOS toolbox via conformance harnessEnd-to-end browser behaviour

See engine BDD inventory and conformance harness for the two separate signals.