Docs / Coding agents

Your own agents: Python and TypeScript SDKs

For agents you build yourself (support bots, ops agents, LangGraph graphs), route changes through a shared executor. The agent proposes; the executor plans, decides, applies and keeps the undo data. The agent never holds the database credentials.

changeward executor token refund-bot --roles propose     # prints the token once, plus the config entry
changeward executor serve --addr 127.0.0.1:8443          # or deploy with the Helm chart

Both SDKs read CHANGEWARD_EXECUTOR_URL and CHANGEWARD_EXECUTOR_TOKEN when no URL or token is passed.

Python (sdk/python, standard library only)

from changeward_sdk import Client, PendingApproval, Denied, Conflict

cw = Client("http://127.0.0.1:8443", token="cwx_...")
session = cw.start_session(target="app", agent="refund-bot", incident_ref="TICKET-881")

change = session.propose("UPDATE orders SET status = 'refunded' WHERE id = 42", intent="refund order 42")
print(change.diff, change.reversibility, change.decision)   # exact preview before anything happens

try:
    change.apply()                       # applies if policy approved
except PendingApproval:
    print("a human must approve", change.plan_id)   # changeward approve <plan>, Slack, or the web view
except Denied as e:
    print("denied:", change.reasons)

change.undo()                            # dependency-aware; refuses on conflicting edits
session.end()
Integration Helper
LangGraph changeward_sdk.integrations.langgraph.apply_with_interrupt(change, interrupt) pauses the graph with the plan as the interrupt payload. When you resume with approval, the change is confirmed and applied.
OpenAI Agents SDK, Claude Agent SDK tool bodies changeward_sdk.integrations.agents.propose_and_apply_text(session, sql, intent) returns model-readable text: APPLIED …, PENDING APPROVAL … do not try another way, DENIED … do not retry, or CONFLICT … propose again.
Claude Agent SDK permissions claude_can_use_tool(session, sql_tools=("execute_sql",)) is a can_use_tool callback that denies raw SQL tools and points Claude at Changeward.
Any function @guarded(lambda: session, intent="refund") on a function that returns SQL: the SQL is proposed and applied through Changeward.

Example tool for the OpenAI Agents SDK:

from agents import function_tool
from changeward_sdk.integrations.agents import propose_and_apply_text

@function_tool
def execute_sql(sql: str, why: str) -> str:
    """Change the orders database. Every change is previewed, policy-checked and undoable."""
    return propose_and_apply_text(session, sql, intent=why)

TypeScript (sdk/typescript, no dependencies, uses fetch)

import { Client, PendingApproval, proposeAndApplyText, applyWithInterrupt } from "@changeward/sdk";

const cw = new Client({ url: "http://127.0.0.1:8443", token: process.env.CHANGEWARD_EXECUTOR_TOKEN });
const session = await cw.startSession({ target: "app", agent: "refund-bot" });

const change = await session.propose("UPDATE orders SET status = 'refunded' WHERE id = 42", { intent: "refund order 42" });
console.log(change.diff, change.reversibility, change.decision);
try { await change.apply(); } catch (e) { if (e instanceof PendingApproval) console.log("waiting for a human"); }
  • Vercel AI SDK, OpenAI Agents SDK (JS), Claude Agent SDK (TS): use proposeAndApplyText(session, sql, intent) as the tool's execute.
  • LangGraph.js: applyWithInterrupt(change, interrupt).

Choosing the identity

The token's name is the delegating human or service, recorded as user:<name>. agent is the agent's identity. Policy can tell them apart (principal.kind == "agent"), and an approval must come from a token other than the proposer's, with the approve role.

Edit this page on GitHub · synced from 89c04c9