Integration Walkthrough
This guide shows the standard OpenBox-on-CopilotKit path. OpenBox sits on top of CopilotKit, and CopilotKit can connect to multiple backend agent frameworks. The reference path here uses LangGraph.
It follows the same shape as the OpenBox-on-CopilotKit reference app.
- Need the short path? Start with Getting Started with CopilotKit.
- Already have a CopilotKit app on a LangGraph backend? See Add OpenBox to CopilotKit.
Prerequisites
- Node.js
22+ - Next.js App Router
@copilotkit/react-coreand@copilotkit/runtime@copilotkit/sdk-jsand@langchain/langgraphopenbox-sdkwith CopilotKit exports- an OpenBox agent runtime key
- an OpenBox agent DID and private key unless signing is disabled for the registered agent
OpenBox governs CopilotKit. CopilotKit talks to the backend agent framework. This walkthrough uses LangGraph as that backend.
Part 1: Register Your Agent In OpenBox
- Open the OpenBox Dashboard
- Go to Agents
- Create or open the agent you want to govern
- Generate an agent runtime key
- Copy the generated DID and private key unless Require signing is disabled
- Keep those credentials for the CopilotKit runtime and LangGraph backend
See Registering Agents for the full dashboard flow.
Part 2: Configure Trust Controls
Configure the OpenBox controls this CopilotKit app should enforce in Authorize before you run traffic through it:
- Use guardrails for prompt, tool, and output checks.
- Use policies for allow, block, halt, approval, and transformation behavior.
- Use behavior rules for natural-language instructions that shape how the registered agent should operate.
These controls live in OpenBox. CopilotKit provides the assistant UI and runtime path, while the OpenBox integration sends CopilotKit and LangGraph events to the registered OpenBox agent for evaluation.
Part 3: Configure Environment
OPENBOX_ENABLED=true
OPENBOX_CORE_URL=https://core.openbox.ai
OPENBOX_API_KEY=obx_live_or_obx_test_agent_runtime_key
# Required by default for newly created agents unless Require signing is disabled.
OPENBOX_AGENT_DID=did:aip:550e8400-e29b-41d4-a716-446655440000
OPENBOX_AGENT_PRIVATE_KEY=base64_raw_ed25519_private_key
AGENT_URL=http://localhost:8123
These are the credentials for the generic runtime integration. Platform/backend variables are only needed by optional server-side helpers, such as an approval-decision route that posts human approve/reject decisions back to OpenBox.
Part 4: Wire The CopilotKit Runtime
import {
CopilotRuntime,
createCopilotRuntimeHandler,
InMemoryAgentRunner,
} from "@copilotkit/runtime/v2";
import { LangGraphAgent } from "@copilotkit/runtime/langgraph";
import {
createOpenBoxCopilotKitAdapter,
createOpenBoxCopilotRuntime,
} from "openbox-sdk/copilotkit";
const agent = new LangGraphAgent({
deploymentUrl: process.env.AGENT_URL ?? "http://localhost:8123",
graphId: "openbox_copilotkit_agent",
langsmithApiKey: process.env.LANGSMITH_API_KEY ?? "",
});
const runner = new InMemoryAgentRunner();
const runtime = new CopilotRuntime({
agents: { default: agent },
runner,
a2ui: {
injectA2UITool: false,
},
});
const openboxRuntime = createOpenBoxCopilotRuntime({
runtime,
runner,
agents: ["default"],
adapter: createOpenBoxCopilotKitAdapter({
agentWorkflowType: "CopilotKitRuntime",
taskQueue: "copilotkit-runtime",
selfGovernedToolNames: [
"openbox_governed_action",
"openbox_governed_approval_action",
"openbox_resume_governed_action",
],
}),
});
const handler = createCopilotRuntimeHandler({
runtime: openboxRuntime.runtime,
basePath: "/api/copilotkit",
hooks: openboxRuntime.hooks,
});
export const GET = handler;
export const POST = handler;
This governs the runtime prompt before CopilotKit calls the LangGraph backend and gives OpenBox a chance to govern assistant output as events stream back.
Part 5: Add LangGraph Middleware
Create a local helper in your LangGraph backend. The file name is arbitrary; this walkthrough uses agent/src/openbox_governance.ts to keep the OpenBox adapter setup in one place.
import { AIMessage } from "@langchain/core/messages";
import { createMiddleware, type AgentMiddleware } from "langchain";
import {
createOpenBoxCopilotKitAdapter,
} from "openbox-sdk/copilotkit";
export const openBoxCopilotKitAdapter = createOpenBoxCopilotKitAdapter({
agentWorkflowType: "CopilotKitLangGraphAgent",
taskQueue: "copilotkit-langgraph",
selfGovernedToolNames: [
"openbox_governed_action",
"openbox_governed_approval_action",
"openbox_resume_governed_action",
],
});
export function createOpenBoxGovernanceMiddleware(): AgentMiddleware {
return openBoxCopilotKitAdapter.createLangChainMiddleware({
createMiddleware,
AIMessage,
}) as AgentMiddleware;
}
Later imports of ./openbox_governance.js refer to this local agent/src/openbox_governance.ts file. The .js extension is used because the LangGraph backend runs as Node ESM after TypeScript compilation.
Install the OpenBox middleware before CopilotKit middleware:
import { z } from "zod";
import { createAgent } from "langchain";
import {
copilotkitMiddleware,
CopilotKitStateSchema,
zodState,
} from "@copilotkit/sdk-js/langgraph";
import { StateSchema } from "@langchain/langgraph";
import { createOpenBoxGovernanceMiddleware } from "./openbox_governance.js";
const AgentStateSchema = new StateSchema({
openboxSession: zodState(
z.object({
status: z.enum(["active", "halted"]).default("active"),
reason: z.string().optional(),
haltedAt: z.string().optional(),
}).default(() => ({ status: "active" as const })),
),
...(CopilotKitStateSchema.fields as Record<string, any>),
});
export const graph = createAgent({
model,
tools,
middleware: [
createOpenBoxGovernanceMiddleware(),
copilotkitMiddleware,
],
stateSchema: AgentStateSchema,
});
Part 6: Govern Business Tools
App-owned tools that return OpenBox-governed results should produce one result that contains the governance decision and the releasable business artifact:
import {
createGovernedCopilotTool,
type OpenBoxCopilotActionInput,
} from "openbox-sdk/copilotkit";
import { openBoxCopilotKitAdapter } from "./openbox_governance.js";
type SupportTicketInput = OpenBoxCopilotActionInput & {
customerId: string;
summary: string;
priority: "low" | "medium" | "high";
};
const governedCopilotTool = createGovernedCopilotTool<SupportTicketInput>({
adapter: openBoxCopilotKitAdapter,
toolName: "openbox_governed_action",
description: "Create a governed customer support ticket.",
normalizeInput: (input) => input,
execute: async (input) => {
return {
type: "support_ticket",
ticketId: `SUP-${Date.now()}`,
customerId: input.customerId,
summary: input.summary,
priority: input.priority,
status: "created",
};
},
});
export async function governAction(input, config) {
return governedCopilotTool.execute(input, config);
}
The result carries schema version openbox.copilotkit.result.v1, verdict status, workflow IDs, approval IDs when needed, guardrail details, and optional transformed artifacts.
Part 7: Add An Approval Decision Route
If your CopilotKit UI renders approve/reject controls, point those controls at a server-side route owned by your app. The reference demo uses createOpenBoxApprovalRoute() for that route. That helper posts the human decision back to OpenBox through the platform/backend API, so its credentials stay server-side.
import { NextResponse } from "next/server";
import { createOpenBoxApprovalRoute } from "openbox-sdk/copilotkit";
const approvalRoute = createOpenBoxApprovalRoute({
clientName: "my-copilotkit-app",
backendTimeoutMs: 180_000,
});
export async function POST(request: Request) {
const body = await request.json();
const resolved = await approvalRoute.decide({
governanceEventId: body.governanceEventId,
decision: body.decision,
});
return NextResponse.json({
ok: true,
decision: body.decision,
eventId: resolved.eventId,
});
}
Part 8: Render OpenBox Results
Use the React helpers to register approval, interactive review, governance decision, and action-result renderers:
The approval client endpoint should match the server route from Part 7, or your app's equivalent approval-decision path.
import { z } from "zod";
import {
useHumanInTheLoop,
useDefaultRenderTool,
} from "@copilotkit/react-core/v2";
import {
createOpenBoxApprovalClient,
OpenBoxGovernanceDecision,
useOpenBoxCopilotKit,
} from "openbox-sdk/copilotkit/react";
export function useOpenBoxRendering() {
useOpenBoxCopilotKit({
bindings: {
useHumanInTheLoop,
useDefaultRenderTool,
},
approvalParameters: z.object({
workflowId: z.string(),
runId: z.string(),
activityId: z.string(),
governanceEventId: z.string(),
action: z.string(),
request: z.string(),
}),
approvalClient: createOpenBoxApprovalClient({
endpoint: "/api/openbox/approvals/decide",
}),
renderGovernanceDecision: (props) => (
<OpenBoxGovernanceDecision {...props} />
),
});
}
Part 9: Verify A Live Run
Trigger one real CopilotKit request, then check OpenBox for:
- a CopilotKit runtime session
- a LangGraph backend session
- prompt, tool-input, tool-output, and assistant-output decisions
- tool artifacts rendered only after OpenBox allows or transforms them
- approval decisions when the registered policy requires review
- halted session state when OpenBox returns a halt verdict