npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@copilotkit/bot

v0.0.3

Published

Platform-agnostic JSX bot engine for CopilotKit (createBot, Thread, PlatformAdapter, ActionStore).

Readme

@copilotkit/bot

The platform-agnostic bot engine. It owns everything between an incoming message and a rendered reply — handler registration, the agent run/tool/interrupt loop, JSX action binding, and the PlatformAdapter contract — without knowing anything about Slack (or any other surface). A platform adapter plugs in at the boundary; @copilotkit/bot-slack is the concrete Slack one.

It builds on @copilotkit/bot-ui (the JSX runtime + component vocabulary, re-exported from here for convenience) and AG-UI (@ag-ui/client, @ag-ui/core).

Install

pnpm add @copilotkit/bot @copilotkit/bot-ui
# plus a platform adapter, e.g.
pnpm add @copilotkit/bot-slack

Quickstart

import { createBot } from "@copilotkit/bot";
import { slack } from "@copilotkit/bot-slack"; // a concrete PlatformAdapter

const bot = createBot({
  adapters: [slack({ botToken, appToken })],
  agent: (threadId) => makeAgent(threadId), // AbstractAgent or (threadId) => AbstractAgent
  tools: [...myTools], // BotTool[] forwarded on every runAgent
  context: [...myContext], // ContextEntry[] forwarded on every runAgent
});

bot.onMention(({ thread }) => thread.runAgent());
bot.onMessage(({ thread }) => thread.runAgent());

await bot.start();

createBot(opts) returns a Bot:

  • onMention(handler) / onMessage(handler) — turn handlers receiving { thread, message }. (Routing is mention-preferred: if any mention handler is registered, all turns route to it; otherwise message handlers fire.)
  • onThreadStarted(handler) — a conversation surface opened (e.g. the Slack assistant pane); receives { thread, user? }. Greet, set suggested prompts or a title, or run the agent. Adapters without the concept never fire it.
  • onInteraction<TValue>(id, handler) — explicit escape-hatch handler for a known action id, bypassing the registry; ctx.action.value is typed TValue.
  • onInterrupt<TPayload>(eventName, handler) — handle a captured agent interrupt (LangGraph-style on_interrupt); receives { payload, thread } with payload typed TPayload.
  • onCommand(command) / onCommand(name, handler) — register a slash command. The handler gets { thread, command, text, options, user }. text is the raw args (Slack); options is the typed, parsed form (defineBotCommand with an options Standard Schema) for surfaces with native structured args (e.g. Discord). Forwarded to adapters that support commands and ignored elsewhere — also pass them up front via commands in CreateBotOptions.
  • tool(t) — register a BotTool (alternative to opts.tools); must be added before start().
  • start() / stop() — bring adapters up / down.

agent is optional. If omitted, calling thread.runAgent() throws; supply an AbstractAgent or a (threadId) => AbstractAgent factory.

Thread

A Thread is the per-conversation handle handed to your handlers and tool contexts. It accepts any Renderable (JSX or a string) for posting.

interface Thread {
  readonly platform: string;
  post(ui: Renderable): Promise<MessageRef>;
  update(ref: MessageRef, ui: Renderable): Promise<MessageRef>;
  delete(ref: MessageRef): Promise<void>;
  stream(src: string | AsyncIterable<string>): Promise<MessageRef>;
  runAgent(input?: {
    context?: ContextEntry[];
    tools?: BotTool[];
  }): Promise<MessageRef | undefined>;
  resume(value: unknown): Promise<MessageRef | undefined>;
  awaitChoice<T = unknown>(ui: Renderable): Promise<T>;
  // Capability-gated (return { ok: false } on surfaces without support):
  setSuggestedPrompts(
    prompts: ReadonlyArray<{ title: string; message: string }>,
    opts?: { title?: string },
  ): Promise<{ ok: boolean; error?: string }>;
  setTitle(title: string): Promise<{ ok: boolean; error?: string }>;
}
  • post / update render the JSX to IR, bind every event-prop handler in the tree (mint a content-stable id, snapshot it, rewrite the prop to { id }), then hand the IR to the adapter.
  • runAgent resolves the conversation's agent session, creates the adapter's RunRenderer, and drives the run/tool/interrupt loop. Per-run tools / context are merged on top of the bot-level defaults for that run only.
  • resume(value) re-enters a paused interrupt run with forwardedProps.command.
  • awaitChoice<T>(ui) posts a picker and blocks until an interaction in this conversation resolves it to the clicked control's value (HITL); pass T to type the returned value.

Tools & context

A BotTool is forwarded to the agent as a frontend tool; its handler runs in the bot when the agent calls it. The handler ctx carries the thread, so a tool can render JSX (ctx.thread.post(<Card .../>)) or run the agent further.

interface BotTool<Schema extends ObjectSchema = ObjectSchema> {
  name: string;
  description: string;
  parameters: Schema; // any Standard Schema (Zod/Valibot/ArkType/…)
  handler(args, ctx: BotToolContext): Promise<unknown> | unknown;
}

Define one with the non-curried defineBotTool, which infers the arg types from parameters:

defineBotTool({
  name: "read_thread",
  description: "Read the messages in the current conversation.",
  parameters: z.object({}),
  async handler(_args, { thread }) {
    return await thread.getMessages();
  },
});

parameters (a Standard Schema) is converted to JSON Schema for the LLM and validated on the way back. BotToolContext is { thread, message?, user?, signal?, platform } — a single shared type with no per-adapter generic. Platform-specific power is reached only through capability-gated thread methods (e.g. thread.getMessages(), thread.lookupUser(query), thread.postFile(...)), so a tool stays portable across surfaces.

A ContextEntry is { description: string; value: string } — knowledge folded into the agent's system context on each runAgent.

ActionStore

Inline JSX handlers are bound by content. Each interactive node gets a content-stable, opaque minted id — mintId(componentName, path, props) = "ck:" + sha1(name | path | stableStringify(props)).slice(0,16). Only the opaque id (plus any small bind() args) is stamped on the native token; no props, PII, or secrets go over the wire.

On a click, the ActionRegistry resolves the handler from a hot in-memory cache; on a miss it rehydrates by loading the snapshot from the ActionStore, re-rendering the named component with the frozen props, and re-walking to the handler's path.

The default ActionStore is InMemoryActionStore (a Map with optional TTL). It is lost on restart: after a restart an old button click degrades to an ActionExpiredError ("this action expired"), which createBot swallows. Durable actions require an external store (Redis / DB) — not shipped in v1. Implement the ActionStore interface (put / get / delete) and pass it as actionStore to make actions survive restarts.

Writing a PlatformAdapter

To target a new surface, implement PlatformAdapter from this package. The engine drives ingress through the IngressSink you receive in start(sink) (sink.onTurn(IncomingTurn) / sink.onInteraction(InteractionEvent) / sink.onCommand(IncomingCommand) / sink.onThreadStarted(IncomingThreadStart)) and egress through your post / update / stream / delete (which receive BotNode[] to translate to a native payload via render). You also provide createRunRenderer(target) (an AG-UI RunRenderer: the subscriber to stream into, plus accessors for captured tool calls and interrupts that the run-loop reads after each runAgent), decodeInteraction(raw) (native event → opaque InteractionEvent), lookupUser, a conversationStore (getOrCreateAgentSession), and the surface capabilities / ackDeadlineMs. Optional capability methods like getMessages(target) and postFile(target, args) back the matching thread methods when the surface supports them — likewise setSuggestedPrompts(target, prompts, opts?) and setThreadTitle(target, title) back thread.setSuggestedPrompts / thread.setTitle, and sink.onThreadStarted(...) emits the "conversation opened" lifecycle event. Slash commands are also capability-gated: an adapter forwards invocations via sink.onCommand(IncomingCommand), and may implement registerCommands(specs) to publish the bot's declared commands up front (e.g. Discord's application-command API); adapters that omit it are skipped. See @copilotkit/bot-slack for a complete implementation.

Exports

createBot, Bot, CreateBotOptions, BotHandler, ThreadStartHandler; Thread; the PlatformAdapter boundary types (RunRenderer, IngressSink, IncomingTurn, InteractionEvent, IncomingCommand, IncomingThreadStart, SurfaceCapabilities, ReplyTarget, ConversationStore, AgentSession, CapturedToolCall, CapturedInterrupt, UserQuery); ActionStore / InMemoryActionStore / ActionSnapshot / ActionRegistry / ActionExpiredError; BotTool / BotToolContext / defineBotTool / BotCommand / CommandContext / CommandSpec / defineBotCommand / ContextEntry / AgentToolDescriptor / ObjectSchema and the tool helpers (toAgentToolDescriptors, parseToolArgs, stringifyHandlerResult); mintId / stableStringify; runAgentLoop; plus the re-exported @copilotkit/bot-ui vocabulary.