PlatformAdapter
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
}