Back to Copilotkit

Channel

showcase/shell-docs/src/content/reference/channels/classes/Channel.mdx

1.64.212.8 KB
Original Source

createChannel(options) returns the Channel you declare on CopilotRuntime({ channels }).

ts
import { createChannel } from "@copilotkit/channels";
import { makeAgent } from "./agent.js";

const channel = createChannel({
  name: "support-slack",
  provider: "slack",
  agent: makeAgent,
});

Use provider: "teams" and a project-unique Teams Channel Code when declaring the Microsoft Teams provider.

createChannel options

OptionTypeDescription
namestringProject-unique Intelligence Code. Required for managed Channels; lowercase kebab-case, 3–64 characters, and not channels.
provider"slack" | "teams"Managed provider declared to Intelligence. Slack is the default when omitted; set it explicitly in production code.
showToolStatusbooleanControls managed Slack tool-call progress. It is hidden by default; set true to show it.
agentAbstractAgent | (threadId: string) => AbstractAgentAgent instance or factory. Factory preferred; a singleton is isolated per run via clone().
toolsChannelTool[]Typed tools forwarded to the agent.
contextContextEntry[]Stable { description, value } context sent on each run.
componentsChannelComponent[]Named JSX components whose callbacks can be reconstructed from stored snapshots.
commandsChannelCommand[]Declared commands for providers that support them.
storeStoreConfigState schema, persistence, turn concurrency (parallel default), dedup, and optional transcript bridge.
adaptersPlatformAdapter[]Low-level direct transports. Managed Slack/Teams quickstarts do not use this option.

One Channel declares one provider. Names must be unique inside one CopilotRuntime.

StoreConfig

OptionTypeDescription
stateStandardSchemaTypes thread.state() and validates every setState(value).
adapterStateStorePersistence implementation for SDK state. Without it, the current managed realtime path falls back to process memory.
identityIdentityResolves a stable user key for the optional transcript bridge. Must be paired with transcripts.
transcriptsTranscriptsConfigSDK-owned cross-platform transcript configuration. Distinct from managed conversation history. Must be paired with identity.
concurrency"parallel" | "serial" | "drop"How overlapping turns on the same conversation are handled. Default: "parallel". See StoreConfig.
onLockConflict"drop" | "force" | callbackDeprecated. Prefer concurrency. Maps to drop/parallel when static.
lockTtlnumberConversation lock TTL in milliseconds (drop / legacy paths). Default: 60_000.
dedupTtlnumberInbound event deduplication window in milliseconds. Default: 300_000.

StateStore contains kv, list, lock, dedup, and queue facets. Remote implementations must preserve JSON-serializable values.

StateStore contract

A production adapter implements this asynchronous contract. Locks, deduplication, and queue operations must remain atomic when multiple runner instances share the same backend.

ts
interface StateStore {
  kv: {
    get<T>(key: string): Promise<T | undefined>;
    set<T>(key: string, value: T, ttlMs?: number): Promise<void>;
    delete(key: string): Promise<void>;
  };
  list: {
    append<T>(
      key: string,
      value: T,
      options?: { maxLen?: number; ttlMs?: number },
    ): Promise<number>;
    range<T>(key: string, start?: number, stop?: number): Promise<T[]>;
    trim(key: string, maxLen: number): Promise<void>;
    delete(key: string): Promise<void>;
  };
  lock: {
    acquire(
      key: string,
      options?: { ttlMs?: number },
    ): Promise<{ token: string } | null>;
    release(key: string, token: string): Promise<void>;
  };
  dedup: {
    // true means the key was already recorded inside the TTL.
    seen(key: string, ttlMs: number): Promise<boolean>;
  };
  queue: {
    enqueue<T>(
      key: string,
      value: T,
      options?: {
        maxSize?: number;
        onFull?: "drop-oldest" | "drop-newest";
      },
    ): Promise<number>;
    dequeue<T>(key: string): Promise<T | undefined>;
    depth(key: string): Promise<number>;
  };
}

All values must round-trip through JSON. A distributed implementation must release a lock only when the supplied token still owns it.

Properties

PropertyTypeDescription
namestring | undefinedDeclared Intelligence Code.
provider"slack" | "teams" | undefinedManaged provider. undefined resolves to Slack.
showToolStatusboolean | undefinedManaged Slack tool-call visibility preference from createChannel().
adaptersreadonly PlatformAdapter[]Attached transport snapshot. Managed Channels start empty and receive the Intelligence adapter at activation.
commandNamesstring[]Normalized names declared through commands or onCommand.
transcriptsTranscriptsOptional SDK transcript API. It is not the managed provider-history store.

The transcripts getter is only available after Channel activation and throws if it is read before the runtime listener starts the Channel.

Message handlers

ts
channel.onMessage(async ({ thread, message }) => {
  await thread.runAgent({
    prompt: message.contentParts?.length
      ? [
          ...(message.text
            ? [{ type: "text" as const, text: message.text }]
            : []),
          ...message.contentParts,
        ]
      : message.text,
  });
});
MethodHandler inputNotes
onMessage(handler){ thread, message }Use this for managed Slack and Teams turns.
onMention(handler){ thread, message }V1 shares turn routing with onMessage. If any mention handler is registered, it receives all turns and message handlers are skipped. Do not register both for managed Channels.
onThreadStarted(handler){ thread, user? }Fires for provider surfaces that report a conversation-open event.

message.platform is the native provider ("slack" or "teams"). thread.platform is "intelligence" on a managed Channel.

Interaction and interrupt handlers

MethodPurposeManaged Slack/Teams
onInteraction(id, handler)Handle a specific opaque action id.Supported for delivered actions. JSX callbacks are usually simpler.
onInterrupt(eventName, handler)Post UI for an agent interrupt; later call thread.resume(value).Use eventName: "on_interrupt" for the current managed renderer.
onReaction(emoji?, handler)Handle delivered reaction events.Inbound support is provider-dependent.
onCommand(command)Register a typed or free-text command.Provider-dependent.
onModalSubmit(callbackId, handler)Handle a modal form submission.Not delivered by the managed realtime path.
onModalClose(callbackId, handler)Handle a modal dismissal.Not delivered by the managed realtime path.

See the Slack interactive messages guide, the Teams interactive messages guide, and the JSX callback reference.

Register a tool

Pass tools in createChannel({ tools }), or add one before activation:

ts
channel.tool(getIncident);

See Tools and context for Slack or Tools and context for Teams.

Runtime lifecycle

A Channel has no public start() method. Creating the long-running runtime listener exposes lifecycle control but opens no connection. The first ready() call is required and lazily opens the Realtime Gateway connection:

ts
const listener = createCopilotNodeListener({ runtime });

const channels = listener.channels;
if (!channels) throw new Error("Channels were not configured.");

await channels.ready({ timeoutMs: 30_000 });
const status = channels.status();
if (status.overall !== "online") {
  throw new Error(`Channel not online: ${JSON.stringify(status)}`);
}

// During graceful shutdown:
await channels.stop();

ready() reports the initial online or setup_required outcome once; inspect status() before accepting traffic. Observe later drops and reconnects through status().

StatusMeaning
connectingready() has not activated the Channel yet, or activation is in progress.
onlineThe managed session is healthy and can receive delivery invitations.
setup_requiredThe runtime declaration exists but provider setup is incomplete.
reconnectingThe gateway connection dropped and is retrying.
errorActivation or bounded reconnect failed.
stoppedThe listener was torn down.