Skip to content

Channel adapters ​

A channel adapter brings an external messaging platform into DotCraft as a first-class channel. It resolves a thread per user, runs turns on it, and delivers replies back to the platform.

The adapter is the runtime abstraction. To make the same adapter discoverable and manageable by DotCraft hosts such as Desktop, package it as a Channel module. A module adds host metadata and lifecycle packaging; it does not replace the adapter.

NOTE

The channel adapter is a language-specific profile, available in TypeScript only. The .NET SDK does not ship a channel adapter.

Subclass the adapter base class when you want the built-in Channel policy: per-identity message queues, thread resolution, slash-command routing, turn-stream reduction, server-request handlers, and heartbeat.

One platform message through a channel adapter: queued per identity, routed to a thread, run as one turn on the server, reduced to one reply, and delivered back to the same chat

Minimal adapter ​

ts
import { ChannelAdapter } from "@dotcraft/channel";

class MyChannel extends ChannelAdapter {
  async onDeliver(target: string, content: string, _metadata: Record<string, unknown>): Promise<boolean> {
    await platform.send(target, content);
    return true;
  }

  async onApprovalRequest(request: Record<string, unknown>): Promise<string> {
    return await platform.requestApproval(request);
  }

  protected async onSegmentCompleted(
    _threadId: string,
    _turnId: string,
    content: string,
    _isFinal: boolean,
    target: string,
  ): Promise<boolean> {
    return await this.onDeliver(target, content, {});
  }
}

Lifecycle and recovery ​

  • Call start() before accepting platform events. It connects the Wire client, registers Channel handlers, and then advertises the Channel capabilities during initialize. Call stop() during shutdown.
  • Forward each platform event with handleMessage. The call accepts the event into an in-memory queue; it does not mean the turn or platform delivery has completed.
  • Queue identity is the combination of user id and channel context. Messages for one identity run serially; different identities can run concurrently. A slash command may bypass the queue when the adapter already knows the thread, so a command such as stop can affect an active turn.
  • The adapter replaces a stale or inactive thread, retries against that replacement, and requeues an input when the server reports another turn is already running.
  • The Wire client reconnects and repeats initialization. It does not persist or replay platform events or delivery calls it already made. Keep the platform receiver alive and add platform-side deduplication or retry where needed. Reconnect is not a delivery-recovery mechanism.

Handler rules ​

HookContract
onDeliverRequired. Deliver plain text to the platform target and report success. The default structured-send handler delegates text messages here.
onApprovalRequestRequired. Return a valid approval decision. If the hook throws, the adapter answers cancel.
onSendOptional. Override for structured delivery, and declare matching capabilities from getDeliveryCapabilities. The default accepts text and rejects other kinds with UnsupportedDeliveryKind.
getChannelTools + onToolCallOptional. Advertise only tools the call hook implements; the default call hook returns UnsupportedTool.
onReplyProgressOptional observer for ordered AgentMessage text while a turn is running. It does not mark text as delivered; coalesce platform updates and use onSegmentCompleted or onTurnCompleted for delivery fallback.
onGeneratedImageOptional. The default sends each completed generated image through onSend after the reply text before it: as image when getDeliveryCapabilities declares base64 images within maxBytes, otherwise as file. Failures are logged and the turn continues.
onTurnCompleted, onTurnFailed, onTurnCancelled, onSegmentCompletedOverride for platform formatting, progressive delivery, and failed/cancelled notifications.

The adapter also handles user-input requests through onUserInputRequest; its default returns an empty answer set. The base adapter registers heartbeat replies itself, so a platform subclass never implements them.

For progressive delivery, return false from onSegmentCompleted when a segment was not delivered. Any other return marks it delivered, and the default onTurnCompleted then skips the full reply.

Package ​

TypeScript Channel authoring uses @dotcraft/channel. Import adapter authoring APIs from its root, queues and routing from /runtime, media helpers from /media, conformance helpers from /testing, and Channel contract metadata from /meta.

DotCraft uses the same base for its built-in hosted channels. Their setup and configuration are documented in the channel configuration.