Adapter SDK

Runtime

The createAdapterRuntime factory — wiring all components and managing lifecycle.

Runtime

createAdapterRuntime() is the main entry point. It wires all SDK components (API client, WebSocket, store, orchestrators) and manages their lifecycle.

Factory Function

import { createAdapterRuntime } from '@oro.ad/bridge-adapter-sdk'

const runtime = createAdapterRuntime(config)

AdapterConfig

interface AdapterConfig {
  bridge: {
    url: string                     // Bridge server URL
    socketPath?: string             // Custom Socket.IO path
    clientId?: string               // Client identifier
    clientType?: 'adapter' | 'bot' | 'browser' | 'devtools' | 'media'
    authToken?: string              // Bearer token for an @oro.ad/auth-protected bridge
    defaultHeaders?: Record<string, string> // Extra headers on every REST request
  }
  platform: PlatformAdapter          // Your platform implementation
  formatter: Formatter               // Your formatter implementation
  store?: AdapterStore               // Custom store (default: SQLite under dataDir)
  dataDir?: string                   // Directory for bundled SQLite DB (required unless `store` is provided)
  replayLimitPerSession?: number     // Max messages to replay per session (default: 200)
  skipReplay?: boolean               // Skip historical replay on start
  reconciler?: Partial<ReconcilerConfig>
  bridgeToPlatform?: Partial<BridgeToPlatformConfig>
  platformToBridge?: Partial<PlatformToBridgeConfig>
  dedup?: {
    pendingOutboundTtlMs?: number   // TTL for pending outbound dedup tokens (default: 10_000)
  }
  logger?: Logger                   // default: noopLogger
}
Either store or dataDirmust be provided. Omitting both throws an AdapterConfigError from createAdapterRuntime.

Authenticating to a protected bridge

When the bridge enforces @oro.ad/auth, set bridge.authToken. The runtime forwards it as Authorization: Bearer <token> on every REST request and in the Socket.IO handshake auth payload. Use bridge.defaultHeaders for additional headers (or a custom Authorization scheme — it is merged on top of the token header, so an explicit Authorization key wins). Both fields are optional and leave the unauthenticated path unchanged.

const runtime = createAdapterRuntime({
  bridge: {
    url: 'https://bridge.example.com',
    authToken: process.env.BRIDGE_TOKEN,
  },
  // ...platform, formatter, dataDir
})

Runtime Interface

interface Runtime {
  readonly bridgeApi: BridgeApi
  readonly bridgeClient: BridgeClient
  readonly store: AdapterStore
  readonly reconciler: Reconciler
  readonly replayer: Replayer
  readonly bridgeToPlatform: BridgeToPlatform
  readonly platformToBridge: PlatformToBridge

  start(): Promise<void>
  stop(): Promise<void>
}

Startup Sequence

When you call runtime.start(), the following happens in order:

  1. Store initialized — SQLite database opened, tables created
  2. Bridge API — fetches initial session list
  3. WebSocket connected — connects to bridge, joins all session rooms
  4. Reconciler sync — creates/renames containers for all sessions
  5. Title repair — fixes containers with placeholder titles
  6. Replay — replays missed messages (unless skipReplay: true)
  7. Event handlers attached — bridge events routed to platform
  8. Platform started — your PlatformAdapter.start() called
  9. Session list listener — ongoing reconciliation on sessions:list

Shutdown

await runtime.stop()

Calls PlatformAdapter.stop(), disconnects the WebSocket, and closes the store.

Configuration Overrides

All default configs are exported as DEFAULT_*_CONFIG constants — import and inspect at runtime if you need to merge with your overrides programmatically.

ReconcilerConfig

interface ReconcilerConfig {
  titleMaxLength: number             // default: 40
  titleFallback?: (session) => string  // custom fallback when session.title is empty
  guardAgainstEmptyIncomingTitle: boolean  // default: true
}

BridgeToPlatformConfig

interface BridgeToPlatformConfig {
  toolCallChronologicalOrdering: boolean  // default: true
  showThinkingBlocks: boolean             // default: true
  showSessionStatus: boolean              // default: false
  maxStreamEditsPerSecond: number         // default: 1
}

PlatformToBridgeConfig

interface PlatformToBridgeConfig {
  onContainerClosed: 'delete-session' | 'leave-room' | 'noop'  // default: 'delete-session'
  propagateRenames: boolean    // default: true
  uploadAttachments: boolean   // default: true
  adapterSource: string        // default: 'adapter'
}

ReplayerConfig

interface ReplayerConfig {
  limitPerSession: number       // default: 200 (override via AdapterConfig.replayLimitPerSession)
  skipIfNoContainer: boolean    // default: true
}

Accessing Components

After creation, you can access individual components for advanced use cases:

const runtime = createAdapterRuntime(config)

// Direct API access
const sessions = await runtime.bridgeApi.listSessions()

// Direct store access
const mapping = runtime.store.containers.findByContainerId('123')

// Direct WebSocket access
runtime.bridgeClient.on('connect', () => { })

Graceful shutdown

async function shutdown() {
  try { await runtime.stop() }
  finally { process.exit(0) }
}
process.on('SIGTERM', shutdown)
process.on('SIGINT',  shutdown)

runtime.stop() is idempotent and:

  1. Calls PlatformAdapter.stop() (log-only on error — does not throw).
  2. Closes the Socket.IO connection.
  3. Closes the SQLite store.

Pending outbound messages already POSTed to bridge are not cancelled — the bridge continues processing them. Incoming events after stop() are discarded because the socket is gone.

Utilities re-exported for adapter authors

The adapter SDK re-exports helpers that come in handy when building a Formatter or custom orchestrator:

import {
  chunkText,       // split text to N chunks ≤ maxLen
  truncateText,    // ellipsis-truncate to ≤ maxLen
  computeContainerTitle,  // standard title-fallback logic
  PendingOutbound, // dedup primitive — runtime creates one for you
  toolSummary,            // one-liner summary for tool cards
  streamUserMessageToBlocks,
  streamToolCallToBlocks,
  streamResultToBlocks,
  sessionErrorToBlocks,
  bridgeMessageToBlocks,
} from '@oro.ad/bridge-adapter-sdk'