Adapters¶
Adapters make runeward's governed tools a first-class citizen in the agent
frameworks you already use. Instead of hand-rolling HTTP calls to the control
plane, you import a small client (or a ready-made set of framework "tools") and
hand them to your agent. The agent then calls runeward_shell,
runeward_python, runeward_read_file, … exactly as it would any other tool —
but every call is routed through the full governance path:
So work runs in a disposable, deny-by-default Citadel; risky actions are blocked or escalated to a human; and everything is recorded in a tamper-evident Chronicle.
Which one do I want?¶
- Using an MCP client (Claude Desktop, Cursor, VS Code)? You don't need an
adapter at all — point the client at the runeward MCP server (
runeward mcp). See the REST API and thedist/mcp/manifest. - Building a Python agent with LangChain, CrewAI, LlamaIndex, the OpenAI
Agents SDK, or Strands? Use the
runewardPython package. - Building a TypeScript agent on the Vercel AI SDK, LangChain.js, or Strands?
Use the
@runeward/sdkpackage. - Rolling your own? Both
RunewardClients are thin, typed wrappers over the REST control plane and work standalone with zero third-party runtime dependencies.
All adapters target the same control plane started with runeward serve (default
http://localhost:8080) and expose the same method surface, mirroring the
governed tool set: create_sandbox, shell, python, node,
read_file/write_file/list_files/search_files,
list_approvals/approve/deny, kill_sandbox, plus audit/verify_audit.
(The client method names keep their original spellings even though the matching
MCP tools are now runeward_create_citadel, runeward_kill_citadel, and
runeward_list_conclave.)
The governance contract every adapter preserves¶
Every tool call resolves to one of three verdicts. Adapters surface these consistently — as typed exceptions on the raw client, and as short, model-readable strings on the framework tools:
allow— the action ran (for shell, still checkexit_code).deny— policy refused it. Do not retry the same action; choose an allowed approach or report the block.require-approval— a human must sign off. Pause and surface theapproval_id; don't route around the gate.
Authentication
If runeward serve is started with a token (--token / RUNEWARD_API_TOKEN)
or RBAC, pass it to the client — RunewardClient(..., token="...") in Python
or new RunewardClient({ token: "..." }) in TypeScript.
Python (runeward)¶
The core client uses only the Python standard library (urllib). Framework
helpers are optional extras, imported lazily, so the base client works with
nothing else installed.
python -m pip install runeward
python -m pip install 'runeward[langchain]'
python -m pip install 'runeward[crewai]'
python -m pip install 'runeward[llamaindex]'
python -m pip install 'runeward[openai-agents]'
python -m pip install 'runeward[strands]'
For repository development, use python -m pip install -e ./adapters/python.
Raw client¶
from runeward import RunewardClient, RunewardDenied, RunewardApprovalRequired
rw = RunewardClient("http://localhost:8080")
sbx = rw.create_sandbox("dev")
print(rw.shell(sbx["id"], ["python3", "--version"])["stdout"])
rw.kill_sandbox(sbx["id"])
try:
rw.shell(sbx["id"], ["rm", "-rf", "/"])
except RunewardDenied as e:
print("blocked by policy:", e.reason) # do NOT retry
except RunewardApprovalRequired as e:
print("needs a human:", e.approval_id) # pause for an operator
Framework tools¶
Each framework module exposes a single make_runeward_tools(client) factory that
returns that framework's native tool objects, all named runeward_*:
from runeward import RunewardClient
client = RunewardClient("http://localhost:8080")
# Pick one, matching your framework:
from runeward.langchain_tools import make_runeward_tools # LangChain
from runeward.crewai_tools import make_runeward_tools # CrewAI
from runeward.llamaindex_tools import make_runeward_tools # LlamaIndex
from runeward.openai_agents_tools import make_runeward_tools # OpenAI Agents SDK
from runeward.strands_tools import make_runeward_tools # Strands Agents SDK
tools = make_runeward_tools(client)
# LangChain: pass `tools` to an AgentExecutor / create_react_agent(...)
# CrewAI: crewai.Agent(tools=tools, ...)
# LlamaIndex: FunctionAgent(tools=tools, ...) / ReActAgent
# OpenAI Agents: agents.Agent(name="...", tools=tools)
# Strands: strands.Agent(tools=tools)
See the Python adapter README for full, per-framework examples.
TypeScript (@runeward/sdk)¶
The core RunewardClient uses the global fetch and has no runtime
dependencies (Node 18+, Deno, Bun, browsers). Tool wrappers require optional
peer dependencies, imported lazily.
npm install @runeward/sdk # core client
npm install @runeward/sdk ai zod # + Vercel AI SDK tools
npm install @runeward/sdk @langchain/core zod
npm install @runeward/sdk @strands-agents/sdk zod
For repository development, use npm install ./adapters/typescript.
Raw client¶
import { RunewardClient, RunewardDenied } from "@runeward/sdk";
const rw = new RunewardClient({ baseUrl: "http://localhost:8080" });
const sbx = await rw.createSandbox("dev");
console.log((await rw.shell(sbx.id, ["node", "--version"])).stdout);
await rw.killSandbox(sbx.id);
Vercel AI SDK¶
import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
import { RunewardClient } from "@runeward/sdk";
import { makeRunewardTools } from "@runeward/sdk/ai-tools";
const tools = await makeRunewardTools(new RunewardClient());
await generateText({ model: openai("gpt-4o"), tools, maxSteps: 8, prompt: "…" });
LangChain.js¶
import { ChatOpenAI } from "@langchain/openai";
import { createReactAgent } from "@langchain/langgraph/prebuilt";
import { RunewardClient } from "@runeward/sdk";
import { makeRunewardTools } from "@runeward/sdk/langchain-tools";
const tools = await makeRunewardTools(new RunewardClient());
const agent = createReactAgent({ llm: new ChatOpenAI({ model: "gpt-4o" }), tools });
Strands Agents SDK¶
import { Agent } from "@strands-agents/sdk";
import { RunewardClient } from "@runeward/sdk";
import { makeRunewardTools } from "@runeward/sdk/strands-tools";
const tools = await makeRunewardTools(new RunewardClient());
const agent = new Agent({ tools });
await agent.invoke("Create a dev sandbox, run `node --version`, then tear it down.");
See the TypeScript adapter README for more.
Notes¶
denyis a policy decision, not a transient error. Don't retry the same action; pick a different, allowed approach.require-approvalis a hard pause. Surface the approval id to a human and wait for the outcome (resolve it via the dashboard, the CLI, orPOST /v1/conclave/{id}/{approve,deny}).- Prefer the tightest Charter that lets the task succeed.