Adapter SDK

PlatformAdapter

The interface your platform must implement to integrate with the bridge adapter system.

PlatformAdapter

The core interface that platform implementations must fulfill. Each method maps to a specific platform capability.

Interface

interface PlatformAdapter {
  // Lifecycle
  start(handlers: PlatformHandlers): Promise<void>
  stop(): Promise<void>

  // Container lifecycle
  createContainer(session: SessionSummary): Promise<ContainerId>
  renameContainer(id: ContainerId, title: string): Promise<void>
  closeContainer(id: ContainerId): Promise<void>
  deleteContainer(id: ContainerId): Promise<void>

  // Outgoing messages
  postMessage(id: ContainerId, content: FormattedContent): Promise<PlatformMessageId[]>
  editMessage(id: ContainerId, msgId: PlatformMessageId, content: FormattedContent): Promise<void>
  deleteMessage?(id: ContainerId, msgId: PlatformMessageId): Promise<void>

  // Streaming
  readonly supportsNativeStreaming: boolean
  streamDraft?(id: ContainerId, draftId: number, text: string): Promise<void>

  // Attachments
  downloadPlatformFile(file: PlatformFile): Promise<DownloadedFile>
  postAttachment(id: ContainerId, attachment: MessageAttachment, caption?: string): Promise<PlatformMessageId>
}

Type Aliases

type ContainerId = string       // platform-side container ID (topic, channel, thread)
type PlatformMessageId = string // platform-side message ID

Lifecycle

start(handlers)

Called by the runtime before any other method. Use this to initialize your platform client (bot polling, webhook server, etc.) and wire the PlatformHandlers callbacks for incoming events.

interface PlatformHandlers {
  onMessage(msg: IncomingMessage): Promise<void>
  onContainerClosed(e: ContainerClosedEvent): Promise<void>
  onContainerRenamed(e: ContainerRenamedEvent): Promise<void>
}

stop()

Graceful shutdown. Stop polling, close connections, clean up resources.

Container Lifecycle

A "container" is the platform-side construct that maps to a bridge session. On Telegram this is a forum topic, on Slack a channel or thread, on Discord a thread.

createContainer(session)

Create a new container for the given session. Return its platform ID.

async createContainer(session: SessionSummary): Promise<ContainerId> {
  const topic = await this.bot.createForumTopic(this.chatId, {
    name: session.title || session.conversationId,
    icon_color: hashToColor(session.projectName)
  })
  return String(topic.message_thread_id)
}

renameContainer(id, title)

Rename an existing container. Called when the bridge session title changes.

closeContainer(id) / deleteContainer(id)

Close or delete a container when the bridge session is deleted.

Outgoing Messages

postMessage(id, content)

Send a message to a container. Returns one or more platform message IDs (a single logical message may require multiple platform messages due to size limits).

interface FormattedContent {
  kind: 'text' | 'rich'
  text?: string
  parseMode?: 'plain' | 'html' | 'markdown' | 'markdownv2' | 'mrkdwn'
  rich?: unknown  // platform-specific payload
}

editMessage(id, msgId, content)

Edit an existing message. Used by TurnStreamer for incremental text updates and by the tool card flow to combine tool_use + tool_result into a single edited message.

deleteMessage(id, msgId) — optional

Delete a message. Used for cleanup of streaming placeholders.

Streaming

supportsNativeStreaming

Return true if the platform has native draft/typing indicators (e.g., Telegram sendChatAction). The SDK will try streamDraft() first and fall back to send+edit if unavailable.

streamDraft(id, draftId, text)

Show a live draft to the user. Most platforms don't support this natively — return supportsNativeStreaming: false and the SDK will use the edit fallback automatically.

Attachments

downloadPlatformFile(file)

Download a file from the platform (e.g., a photo the user sent). Returns a temporary file path with an optional cleanup function.

interface PlatformFile {
  fileId: string
  filename: string
  mimeType: string
  size?: number
}

interface DownloadedFile {
  filepath: string
  filename: string
  mimeType: string
  cleanup?: () => Promise<void>
}

postAttachment(id, attachment, caption?)

Send an attachment (image, document, etc.) to a container. The attachment object comes from the bridge and includes path, filename, mimeType, and type.

Incoming Events

Your start() implementation receives PlatformHandlers. Call these when platform events occur:

onMessage

interface IncomingMessage {
  updateId: string              // dedup key
  containerId: ContainerId      // which container
  platformMessageId: PlatformMessageId
  text: string                  // message content
  files: PlatformFile[]         // attached files
  sender: IncomingSender
}

interface IncomingSender {
  id: string                    // unique sender ID
  username?: string             // platform username
  displayName?: string          // display name
}

onContainerClosed

interface ContainerClosedEvent {
  updateId: string
  containerId: ContainerId
}

onContainerRenamed

interface ContainerRenamedEvent {
  updateId: string
  containerId: ContainerId
  newName: string
}