Dashboard|

Coming from OpenAI

Move an agent from the OpenAI Agents SDK, ChatKit or Agent Builder to Cencori: what maps to what, and what changes.

cencori/agents follows the same shape as the OpenAI Agents SDK for JavaScript: agents, tools, run(), handoffs, guardrails, approvals. Most code moves by changing imports. What you get in return: any model in the catalog, durable runs that can wait on a person, traces, budgets and PII rules on every call, and a chat UI that works with all of it.

At a glance

OpenAICencoriNotes
@openai/agentscencori/agentsSame concepts and names
OPENAI_API_KEYCENCORI_API_KEYA secret project key (csk_...)
OpenAI modelsAny catalog modelOpenAI, Anthropic, Gemini, open-weight. Default: GPT OSS 120B on Cerebras
ChatKitAgent UI (cencori/react)Widgets the model can write itself, charts and tables built in
Agent Builder workflowsStored agents or Business AgentsDefine in Cencori, run by id
Traces dashboardObservability → TracesEvery run, turn, tool call and handoff

1. Install and set your key

npm uninstall @openai/agents
npm install cencori zod
# .env
CENCORI_API_KEY=csk_...

2. Change the imports

// Before
import { Agent, run, tool } from '@openai/agents';
 
// After
import { Agent, run, tool } from 'cencori/agents';

Agents, tools and runs are written the same way:

const getWeather = tool({
    name: 'get_weather',
    description: 'Current weather for a city.',
    parameters: z.object({ city: z.string() }),
    execute: async ({ city }) => fetchWeather(city),
});
 
const agent = new Agent({
    name: 'Assistant',
    instructions: 'Answer briefly.',
    tools: [getWeather],
});
 
const result = await run(agent, "What's the weather in Lagos?");
console.log(result.finalOutput);

3. Pick a model

Agents without model use GPT OSS 120B on Cerebras: fast and good at tools. To keep using an OpenAI model, name it; to try another, change the string:

new Agent({ name: 'Assistant', model: 'gpt-4o', ... });
new Agent({ name: 'Assistant', model: 'claude-sonnet-4-5', ... });

What maps one to one

OpenAI Agents SDKcencori/agents
Agentnew Agent({ name, instructions, model, tools, handoffs, outputType })Same
Dynamic instructionsinstructions: (runContext) => ...Same
Tooltool({ name, description, parameters, execute })Same; zod 3 or 4, any Standard Schema, or JSON Schema
Runrun(agent, input)Same
ResultfinalOutput, lastAgent, newItems, interruptions, stateSame
Streamrun(agent, input, { stream: true }), toTextStream(), completedSame; events are simpler (text_delta, tool_call, handoff...)
Handoffshandoffs: [billing], handoffDescriptionSame
Agents as toolsagent.asTool({ toolName, toolDescription })Same
Structured outputoutputType: z.object(...)Same
GuardrailsinputGuardrails, outputGuardrails with tripwireTriggeredSame
ApprovalsneedsApproval, result.state.approve(item), run(agent, state)Same
Contextrun(agent, input, { context })Same: never sent to the model provider or Cencori
Max turnsmaxTurnsSame
MCP serversmcpServers: [new MCPServerStreamableHttp(...)], MCPServerStdioSame names. See MCP servers

What's different

Saving a paused run. Both serialize the state. Restoring differs slightly:

// OpenAI
const state = await RunState.fromString(agent, saved);
 
// Cencori
const state = RunState.fromJSON(saved);

Then approve or reject and call run(agent, state) as before. Cencori keeps the paused session for 24 hours; the request and decision land in your governance ledger.

Guardrail errors. One class, GuardrailTripwireTriggered, with kind: 'input' | 'output' instead of two classes.

Conversations. Each run is a Cencori session. To continue one, pass its id: run(agent, input, { sessionId: result.sessionId }). The session holds the history server-side.

Stream events. Instead of raw model events plus run items, you get one flat list: agent_updated, text_delta, tool_call, tool_output, handoff, widget, interruption.

ChatKit to Agent UI

ChatKitAgent UI
<ChatKit control={useChatKit({ api: { getClientSecret } })} /><AgentChat api="/api/chat" />
A ChatKit server (or Agent Builder workflow)createAgentChatHandler(agent) in your own route, or a stored agent with no backend
Widgets built in your server codeWidgets the model writes itself (widgets: true), or returned by your tools as { widget }
Card, ListView, Text, Title, Badge, Button, Form, Select...The same idea, plus Chart, Table and Stat. See Widgets
Actions: { type, payload, handler: 'server' | 'client' }{ type, payload, handler: 'agent' | 'client' }
Theme optionstheme={{ colorScheme, accent, radius, density, fontFamily }} and CSS variables
// app/api/chat/route.ts
import { createAgentChatHandler } from 'cencori/agents';
import { agent } from '@/lib/agent';
 
export const POST = createAgentChatHandler(agent);
// app/page.tsx
import { AgentChat } from 'cencori/react';
 
<AgentChat api="/api/chat" />

Agent Builder to Cencori

If your agent was built in Agent Builder rather than in code:

  • No code needed: build it under Business Agents in the console (instructions, knowledge, tools, website and WhatsApp), then put it in your app with <AgentChat widgetKey="cwk_..." />.
  • Defined by API: create a stored agent and run it by id from the SDK (Agent.fromCencori(id)) or straight from a browser (<AgentChat agent publishableKey />).

Not here yet

  • OpenAI hosted tools (file search, code interpreter, computer use). Web search is available as a built-in tool on stored agents.
  • Voice and realtime agents. Use the Voice API for now.
  • Python. The Agents SDK is TypeScript today; from Python, use the Agents API over HTTP.

A complete example

The Agent UI starter is a Next.js app with tools, widgets and an approval, ready to copy.