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

@theokit/plugin-copilot

v0.1.0

Published

AI Copilot pattern for TheoKit — defineCopilot factory + AgentRoomMember (P#9 RoomMember) + CopilotRuntime + React hooks family + <CopilotChat /> composição theo-ui composites. Differentiator: copilot is presence-visible to other users (multi-user awarene

Downloads

92

Readme

@theokit/plugin-copilot

AI Copilot pattern for TheoKit — defineCopilot factory + AgentRoomMember (P#9 RoomMember) + CopilotRuntime orchestrator + React hooks family + <CopilotChat /> composição. Form 4 Hybrid per plan p11-plugin-copilot v1.0.

Differentiator from CopilotKit: the copilot is a first-class participant in the realtime room — every human in the room sees the copilot's name, avatar, color, and typing status in the presence Map. Multiple copilots can coexist in the same room with policy-driven dispatch (first-wins / round-robin / all / custom function).

Integration plugin — composes @theokit/sdk Agent + @theokit/plugin-realtime (P#9) + optional @theokit/plugin-rate-limit (P#10) + opt-in @theokit/plugin-canvas + @theokit/plugin-voice + opt-in @theokit/ui composites. Structural type mirrors avoid hard imports of peers — the plugin compiles standalone and resolves peers at runtime.

Install

pnpm add @theokit/plugin-copilot @theokit/sdk @theokit/plugin-realtime theokit
# Optional rate-limit guard (P#10):
pnpm add @theokit/plugin-rate-limit
# Optional Zod schemas for room.presence / room.broadcast:
pnpm add zod
# Optional React peer for the /react sub-path:
pnpm add react react-dom
# Optional theo-ui composites (only when using <CopilotChat /> or
# the headless hooks alongside @theokit/ui):
pnpm add @theokit/ui
# Opt-in capability integrations:
pnpm add @theokit/plugin-voice  # voice STT/TTS
pnpm add @theokit/plugin-canvas # canvas artifact emission

Quick start

// app/copilots/support.ts
import { defineCopilot } from "@theokit/plugin-copilot";
import { z } from "zod";

export default defineCopilot({
  id: "support-bot",
  room: {
    id: "support-room",
    presence: z.object({
      name: z.string().optional(),
      cursor: z.tuple([z.number(), z.number()]).optional(),
    }),
    broadcast: z.object({
      kind: z.enum(["question", "answer", "tool-call"]).optional(),
      text: z.string().optional(),
    }),
  },
  agent: {
    name: "SupportBot",
    model: "openai/gpt-4o-mini",
    systemPrompt: "You are SupportBot. Be concise and helpful.",
  },
  identity: {
    name: "Support Bot",
    avatar: "/avatars/support.png",
    color: "#7c3aed",
  },
  triggers: [
    { on: "broadcast:question", action: "respond" },
    { on: "presence:idle", action: "suggest", idleMs: 30_000 },
  ],
  budget: {
    perRoom: {
      perRequestUsd: 0.01,
      dailyUsd: 1.00,
    },
  },
});
// server bootstrap
import { Agent } from "@theokit/sdk";
import { createMemoryRealtimeProvider } from "@theokit/plugin-realtime";
import { CopilotRuntime } from "@theokit/plugin-copilot";
import supportCopilot from "./copilots/support.js";

const provider = createMemoryRealtimeProvider();

// Bridge SDK Agent (static methods) to CopilotAgentLike (instance shape).
const agent = {
  async *streamObject(opts: {
    schema: unknown;
    prompt: string;
    model: string | { id: string };
    systemPrompt?: string;
  }) {
    const modelSel = typeof opts.model === "string" ? { id: opts.model } : opts.model;
    const sys = opts.systemPrompt ?? "";
    const fullPrompt = sys ? `${sys}\n\n${opts.prompt}` : opts.prompt;
    const result = await Agent.prompt(fullPrompt, {
      model: modelSel,
      apiKey: process.env.OPENROUTER_API_KEY ?? "",
      local: { settingSources: [] },
      providers: {
        routes: [{ capability: "chat", provider: "openrouter" }],
        fallback: ["openrouter"],
      },
    });
    if (result.status !== "finished") {
      throw new Error(`Agent failed: ${JSON.stringify((result as { error?: unknown }).error)}`);
    }
    const text = typeof result.result === "string" ? result.result : "";
    yield { type: "partial", partial: { text }, attempt: 0 } as const;
    yield { type: "complete", object: { text } } as const;
  },
};

const runtime = new CopilotRuntime({
  provider,
  agent,
  copilots: [supportCopilot],
  estimatedCostPerInvocationUsd: 0.001,
});

await runtime.activate("support-bot");

React composição

// app/page.tsx
import { CopilotProvider, CopilotChat, useCopilot, useCopilotPresence } from "@theokit/plugin-copilot/react";
import { provider, runtime } from "./bootstrap";

export default function Page() {
  return (
    <CopilotProvider
      roomId="support-room"
      copilotId="support-bot"
      provider={provider}
      localConnectionId="alice"
      runtime={runtime}
    >
      <CopilotChat />
    </CopilotProvider>
  );
}

Or use the headless hooks family for full theme control:

import {
  useCopilotMessages,
  useCopilotPresence,
  useCopilotTyping,
  useCopilotReadable,
  useCopilotTool,
} from "@theokit/plugin-copilot/react";

function MyCustomChat() {
  const messages = useCopilotMessages();
  const presence = useCopilotPresence();      // human peers (filtered)
  const typing = useCopilotTyping();          // {copilotId, progress?} | null
  useCopilotReadable("currentPage", { url: "/dashboard" });   // broadcasts context to copilot
  useCopilotTool({ name: "create-task", schema: { /* JSON schema */ } }); // exposes a tool to copilot
  // …render however you want
}

Triggers — when the copilot acts

Three declarative trigger families per ADR D3:

| Trigger | When it fires | Action types | |---|---|---| | broadcast:<event> | Any human broadcasts a frame with event === <event> | respond / execute-tool | | presence:idle | No human activity in the room for idleMs | suggest | | custom | Custom filter function returns true for a frame | respond / suggest / execute-tool |

The CopilotRuntime filters out frames originating from any connection id starting with copilot: BEFORE evaluating triggers (EC-4 + EC-8 — cost-runaway and copilot impersonation guards).

Multi-copilot per room — dispatcher policy (ADR D6)

When multiple copilots are registered in the same room, the dispatcher field of each CopilotDescriptor (or defaultDispatcher on the runtime) decides who responds:

| Policy | Behaviour | |---|---| | "first-wins" (default) | Only the first registered copilot in the room responds. Prevents cost runaway by default. | | "round-robin" | Cursor cycles through copilots one frame at a time. | | "all" | Every copilot in the room responds to every triggering frame. Opt-in only — expensive. | | (copilots, frame) => string[] | Custom function returns the array of copilot ids that should respond. |

Budget integration — opt-in cost guard (ADR D7)

Each copilot can declare budget.perRoom: { perRequestUsd, dailyUsd, monthlyUsd }. Before each agent invocation, the runtime runs a preflight against the rolling daily + monthly windows. On budget exceeded, the copilot broadcasts a typed budget-exceeded frame to the room instead of invoking the agent:

{
  "type": "broadcast",
  "connectionId": "copilot:support-bot",
  "event": "budget-exceeded",
  "payload": { "message": "Per-request budget exceeded: $0.05 limit, would consume $0.10", "code": "budget_per_request_exceeded" }
}

runtime.getUsage(copilotId) returns { dailyUsedUsd, monthlyUsedUsd } for theo-ui usage-meter integration.

Custom provider (Liveblocks / PartyKit / Redis / TheoCloud)

import { defineCopilotRealtimeProvider } from "@theokit/plugin-copilot";

const myProvider = defineCopilotRealtimeProvider({
  async joinRoom(roomId, conn, initialPresence) { /* ... */ },
  async leaveRoom(roomId, connectionId) { /* ... */ },
  async broadcast(roomId, connectionId, event, payload) { /* ... */ },
  async updatePresence(roomId, connectionId, patch) { /* ... */ },
  async getPresence(roomId) { return {}; },
  subscribeRoom(roomId, listener) { return () => {}; },
});

The helper validates that all 6 required methods are present at construction.

Security threats addressed

| Threat | Mitigation | |---|---| | Cost runaway via copilot loop (copilot A triggers copilot B which triggers copilot A) | TriggerEvaluator filters out frames where connectionId.startsWith("copilot:") BEFORE matching triggers (EC-4). Default dispatcher "first-wins" further bounds same-room cost. | | Copilot impersonation by a malicious human client | The copilot: connection-id prefix is reserved; humans cannot claim a copilot:* connection id when joining via the realtime layer (EC-8 — enforced by the consumer's wire layer; the copilot runtime never accepts a frame from a copilot:* connectionId as a trigger source). | | Per-request cost spike (large prompt, model hallucination loop) | Optional budget.perRoom.perRequestUsd preflight — exceeds emit typed budget-exceeded frame instead of invoking the agent. | | Rolling daily / monthly cost overrun | budget.perRoom.{dailyUsd, monthlyUsd} rolling windows reset at UTC day / month boundaries. | | Tool/knowledge registry injection via React hooks | useCopilotReadable / useCopilotTool broadcast register / deregister events scoped to the local connection; the copilot runtime decides whether to use them — never assumes trust. | | Trigger ReDoS via malicious broadcast: event names | Trigger event names are matched via exact-string equality (no regex). Custom-filter triggers run in the consumer's process — consumer responsibility. |

Comparison vs CopilotKit

| Feature | CopilotKit | @theokit/plugin-copilot | |---|---|---| | Frontend SDK | ✓ extensive (React + custom) | ✓ React hooks family + <CopilotChat /> | | Agent runtime | bridge via runtime tier (AG-UI) | direct binding to @theokit/sdk Agent.streamObject / Agent.prompt | | Tool registration | useCopilotAction | useCopilotTool (registers via broadcast event) | | Context registration | useCopilotReadable | useCopilotReadable (registers via broadcast event) | | Multi-user awareness | ✗ copilot is invisible to other users | ✓ copilot is a RoomMember — visible in presence Map with name + avatar + color + typing | | Multi-copilot in same room | ✗ | ✓ dispatcher policy: first-wins / round-robin / all / custom fn | | Budget per-room | ✗ | ✓ perRequestUsd + dailyUsd + monthlyUsd rolling windows + typed budget-exceeded frame | | Provider abstraction | ✗ specific runtime | ✓ any P#9 RealtimeProvider (Memory + Yjs + Liveblocks + PartyKit + TheoCloud) | | Voice (STT + TTS) | ✗ | opt-in via @theokit/plugin-voice peer | | Canvas (artifacts) | ✗ | opt-in via @theokit/plugin-canvas peer |

License

MIT.