@openbox-ai/openbox-copilotkit
v0.4.0
Published
OpenBox server-only governance and observability SDK for CopilotKit runtime v2
Readme
@openbox-ai/openbox-copilotkit
Server-only governance and observability SDK for CopilotKit runtime/v2. Attaches at the CopilotKit boundary to observe frontend tools, AG-UI final messages, and HITL approvals — the seams a per-framework SDK can't see.
Beta. Public APIs may change before
1.0.0. Pin a tilde range (~0.4.0) until then. Deprecated surfaces (the./client/./config/./identity/./typessubpaths,enforceApprovals, and legacy config aliases) are removed at1.0.0— seeMIGRATION.md.
Features
- One-line adopter integration — wrap
CopilotRuntimeOptionswithwithOpenBoxRuntime(); no other code changes required. - AG-UI middleware — observes every
TOOL_CALL_*,TEXT_MESSAGE_*,RUN_*event and ships them to the OpenBox API asworkflow_type: "copilotkit". - Telemetry-default, enforcement opt-in — records everything by default. Two independent, explicit enforcement boundaries opt in via
enforcement(OpenBoxEnforcementOptions): the frontend AG-UI delivery gate, andcreateOpenBoxCopilotKit(...).serverTool(), which prevents a wrapped server tool'sexecutefrom running at all on a non-allow verdict. An unwrapped server tool, an MCP tool, or an external-agent call is always observation-only — see Boundary truthfulness in MIGRATION.md. - Verdict enforcement — BLOCK, HALT, and REQUIRE_APPROVAL (real approval waiting) are enforced at the boundaries above. CONSTRAIN is not supported in
0.4.0: an enforcing caller raises an explicit, typedCopilotKitUnsupportedVerdictErrorrather than silently allowing it. - Tool-span synthesis — synthesize
function_callspans from AG-UI tool-call triples with hashed args/results, idempotency key, audit envelope, and JSONPath-based redaction. - DID-signed governance requests — Ed25519 5-header envelope when
OPENBOX_AGENT_DID+OPENBOX_AGENT_PRIVATE_KEYare set (signing itself is delegated to@openbox-ai/openbox-sdk-ts). - Co-runs with
@openbox-ai/openbox-mastra-sdk— each SDK observes a boundary the other doesn't; no duplicate spans. - No OpenTelemetry dependency by default — events flow via the base SDK's client; instrumentation (HTTP/DB/file) is available but off unless explicitly enabled.
Requirements
| | |
|---|---|
| Node.js | >=24.10.0 (ESM-only deps + AsyncLocalStorage.enterWith) |
| CopilotKit | @copilotkit/runtime ^1.61.0 (peer; runtime/v2 paths only) |
| AG-UI | @ag-ui/client ^0.0.57 (peer) |
| Runtime | Node only — Edge runtimes (Vercel Edge, Cloudflare Workers) are not supported |
Install
npm install @openbox-ai/openbox-copilotkit
# or
pnpm add @openbox-ai/openbox-copilotkit
# or
yarn add @openbox-ai/openbox-copilotkitQuick start
1. Wrap your CopilotKit route
// src/app/api/copilotkit/[[...slug]]/route.ts
import { createCopilotEndpoint, CopilotRuntime } from "@copilotkit/runtime/v2";
import { withOpenBoxRuntime } from "@openbox-ai/openbox-copilotkit";
const { runtime, shutdown } = await withOpenBoxRuntime(
{ agents },
{ middlewareOptions: { frontendToolNames: ["setThemeColor"] } },
);
process.on("SIGINT", async () => {
await shutdown();
process.exit(0);
});
export const { GET, POST } = createCopilotEndpoint({
runtime,
basePath: "/api/copilotkit",
});2. Mark the SDK as a server-external package
// next.config.ts
const nextConfig = {
serverExternalPackages: [
"@copilotkit/runtime",
"@openbox-ai/openbox-copilotkit",
],
};3. Set your environment
# .env.local
OPENBOX_API_KEY=obx_live_...
OPENBOX_URL=https://api.openbox.ai
# Optional — enable DID-signed governance requests:
# OPENBOX_AGENT_DID=did:openbox:...
# OPENBOX_AGENT_PRIVATE_KEY=...That's it. The full env reference lives in docs/installation.md.
What this SDK observes
| Seam | Span type | Owner |
|---|---|---|
| AG-UI TOOL_CALL_* triple | function_call | this SDK |
| Vercel AI SDK LanguageModelV1 call | llm_completion | @openbox-ai/openbox-mastra-sdk |
If you run both SDKs in the same process, they emit independently — no duplicate spans. See docs/troubleshooting.md.
Tool-span buffer (optional)
Synthesize and inspect function_call spans locally before they ship to OpenBox.
// src/lib/openbox-span-buffer.ts
import { SpanBuffer } from "@openbox-ai/openbox-copilotkit";
const g = globalThis as unknown as { __openboxSpanBuffer?: SpanBuffer };
export const spanBuffer = g.__openboxSpanBuffer ?? new SpanBuffer();
if (process.env.NODE_ENV !== "production") g.__openboxSpanBuffer = spanBuffer;// src/app/api/copilotkit/[[...slug]]/route.ts
import { spanBuffer } from "@/lib/openbox-span-buffer";
const { runtime } = await withOpenBoxRuntime(
{ agents },
{
middlewareOptions: {
spanBuffer,
// Recommended starter set — protects common credential keys at any depth.
redactPaths: ["$..password", "$..secret", "$..token", "$..apiKey"],
},
},
);A dev-only debug route can drain the buffer for inspection — see docs/integration-patterns.md. Never deploy that route to production.
SpanBuffer environment variables
| Variable | Default | Effect |
|---|---|---|
| OPENBOX_SPAN_BUFFER_MAX_PER_WORKFLOW | 1000 | Per-workflow span cap; oldest evicted on overflow |
| OPENBOX_SPAN_BUFFER_TTL_MS | 300000 (5 min) | TTL after which a quiet workflow's spans are evicted |
| OPENBOX_DISABLE_SPAN_BUFFER | unset | Set to 1 to skip synthesis entirely (emergency bypass) |
Documentation
| Page | What it covers |
|---|---|
| Project overview | Problem, scope, requirements, success criteria — start here. |
| Installation | Install steps, runtime requirements, full env-var reference, adopter diff. |
| Integration patterns | Drop-in withOpenBoxRuntime vs manual createOpenBoxMiddleware per-agent attach. |
| API reference | Every public export with signature, parameters, and examples. |
| Troubleshooting | The eight scenarios adopters hit most often. |
| System architecture | 13-component breakdown, request flow, data shapes, env vars. |
| Codebase summary | Source tree, public surface, LOC by module, dependency graph. |
| Code standards | Language, naming, import, testing conventions. |
| Project roadmap | Version history, phase definitions, known limitations. |
Release status
| Version | Status | Highlights |
|---|---|---|
| 0.2.0-beta.0 | Published (2026-06-29) | Public framework + shared APIs, AG-UI middleware, frontend-tool labelling, DID-signed requests, OTel install removed. |
| 0.3.0 | Published (2026-06-30) | Verdict discriminated union, SpanBuffer, tool-span synthesis, sibling-event hook transport (not wired into real enforcement). |
| 0.4.0 | Current | Thin adapter over @openbox-ai/openbox-sdk-ts; createOpenBoxCopilotKit/serverTool() pre-execution enforcement; real REQUIRE_APPROVAL waiting; truthful interrupt/resume semantics; non-blocking bounded telemetry; opt-in instrumentation. HALT/BLOCK/REQUIRE_APPROVAL enforced — CONSTRAIN explicitly unsupported (typed failure), not silently allowed. See MIGRATION.md. |
| 1.0.0 (planned) | — | Removes the deprecated ./client/./config/./identity/./types facade subpaths and the deprecated enforceApprovals/config-alias fields; targets API stability. |
See docs/project-roadmap.md and CHANGELOG.md for full history. Migration notes live in MIGRATION.md.
Contributing
Bug reports, questions, and PRs are welcome on GitHub.
Local development:
npm install
npm run ci:check # lint + typecheck + test + build + CI guardsci:check enforces:
- ESLint + TypeScript strict typecheck.
- Vitest with coverage (lines/statements 60%, functions 70%, branches 50%).
scripts/check-no-otel.mjs— fails the build if any@opentelemetry/*import re-enters the SDK.scripts/check-no-mastra-imports.mjs— fails if any@mastra/*import sneaks in (the SDK is framework-agnostic).scripts/check-no-duplicate-signing.mjs— fails if productionsrc/re-implements Core endpoints,X-OpenBox-Agent-*header construction, or canonical signing-byte assembly instead of delegating to@openbox-ai/openbox-sdk-ts.
Commits follow the Conventional Commits format. Do not include AI-attribution lines.
License
MIT — see LICENSE.
