Skip to main content

Framework Integrations

Add governance to any agent framework with minimal code changes.

Coding Agents

Claude Code (hooks)

Claude Code supports hooks that run before/after tool execution:

// .claude/settings.json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash|Write|Edit",
"hook": ".claude/hooks/sequenceware-check.sh"
}
],
"PostToolUse": [
{
"matcher": "Bash|Write|Edit",
"hook": ".claude/hooks/sequenceware-report.sh"
}
]
}
}

Pre-tool hook (blocks if policy denies):

#!/bin/bash
# .claude/hooks/sequenceware-check.sh

RESPONSE=$(curl -s -X POST "${SEQUENCEWARE_URL}/v1/events" \
-H "Content-Type: application/json" \
-H "X-API-Key: ${SEQUENCEWARE_API_KEY}" \
-d "{
\"eventId\": \"evt_$(date +%s%N)\",
\"runId\": \"${CLAUDE_SESSION_ID:-run_$(date +%s)}\",
\"agentId\": \"claude-code-$(whoami)\",
\"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",
\"eventType\": \"tool.called\",
\"status\": \"pending\",
\"metadata\": {
\"toolCallId\": \"tc_$(date +%s%N)\",
\"toolName\": \"$CLAUDE_TOOL_NAME\",
\"inputPayload\": $CLAUDE_TOOL_INPUT
}
}")

STATUS=$(echo "$RESPONSE" | python3 -c "
import sys, json
try:
r = json.load(sys.stdin)
print(r.get('toolCallStatus', 'allowed'))
except: print('allowed')
" 2>/dev/null)

if [ "$STATUS" = "blocked" ]; then
echo "Sequenceware: Action blocked by policy" >&2
exit 1
fi

Codex, Cursor, Windsurf, Aider

For agents that execute shell commands, use git hooks:

#!/bin/bash
# .git/hooks/pre-push

BRANCH=$(git symbolic-ref --short HEAD)

if [[ "$BRANCH" =~ ^(main|master|release) ]]; then
RESPONSE=$(curl -s -X POST "${SEQUENCEWARE_URL}/v1/events" \
-H "Content-Type: application/json" \
-H "X-API-Key: ${SEQUENCEWARE_API_KEY}" \
-d "{
\"eventId\": \"evt_$(date +%s%N)\",
\"runId\": \"run_$(date +%s)\",
\"agentId\": \"git-hook-$(whoami)\",
\"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",
\"eventType\": \"tool.called\",
\"status\": \"pending\",
\"metadata\": {
\"toolCallId\": \"tc_$(date +%s%N)\",
\"toolName\": \"git_push\",
\"inputPayload\": {\"branch\": \"$BRANCH\"}
}
}")

if echo "$RESPONSE" | grep -q '"blocked"'; then
echo "Sequenceware: Push to $BRANCH blocked by policy."
exit 1
fi
fi

LangChain

One callback. That's it.

from sequenceware.integrations.langchain import SequencewareCallbackHandler

governance = SequencewareCallbackHandler(
base_url="https://sequenceware.yourcompany.com",
api_key="acl_your_key",
agent_id="my-langchain-agent",
)

# Your existing agent code — unchanged
agent = create_tool_calling_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools)

result = executor.invoke(
{"input": "Find top customers and post to #analytics"},
config={"callbacks": [governance]},
)

Auto-tracked: chain start/end, every tool call with inputs/outputs, LLM calls with token usage, errors.


CrewAI

from sequenceware import SequencewareClient
from sequenceware.integrations.crewai import SequencewareCrewAIListener

listener = SequencewareCrewAIListener(SequencewareClient(
base_url="https://sequenceware.yourcompany.com",
api_key="acl_your_key",
agent_id="crew-deployer",
))

listener.on_crew_start(metadata={"crew": "incident-response"})
listener.on_task_start("investigate")

tc_id = listener.on_tool_start("query_logs", {"service": "api"})
listener.on_tool_end(tc_id, {"errors": 847})

listener.on_task_end()
listener.on_crew_end()

OpenAI Agents SDK

from openai import OpenAI
from sequenceware import SequencewareClient
from sequenceware.integrations.openai_agents import SequencewareOpenAIAgentsTracer

client = OpenAI()
tracer = SequencewareOpenAIAgentsTracer(SequencewareClient(
base_url="https://sequenceware.yourcompany.com",
api_key="acl_your_key",
agent_id="my-openai-agent",
))

tracer.start_run(metadata={"task": "Review auth module"})

for tool_call in response.message.tool_calls:
tc_id = tracer.track_function_call(tool_call.function.name, args)
result = execute_function(tool_call.function.name, args)
tracer.complete_function_call(tc_id, result)

tracer.complete_run()

Or use the decorator:

from sequenceware.integrations.openai_agents import instrument_openai_agents_run

@instrument_openai_agents_run(governance_client)
def my_agent_workflow():
# Runs and errors tracked automatically
pass

Anthropic Claude (tool use)

Python

from sequenceware.integrations.anthropic import SequencewareAnthropicToolUseTracer

tracer = SequencewareAnthropicToolUseTracer(SequencewareClient(
base_url="https://sequenceware.yourcompany.com",
api_key="acl_your_key",
agent_id="my-claude-agent",
))

tracer.start_conversation(metadata={"model": "claude-sonnet-4-20250514"})

# In your tool loop:
tracked_ids = tracer.handle_tool_use_blocks(tool_blocks)
for block, tc_id in zip(tool_blocks, tracked_ids):
result = execute_tool(block.name, block.input)
tracer.complete_tool_result(tc_id, result)

tracer.complete_conversation()

TypeScript

import { SequencewareClient } from '@sequenceware/sdk';

const governance = new SequencewareClient({
baseUrl: 'https://sequenceware.yourcompany.com',
apiKey: 'acl_your_key',
agentId: 'my-claude-agent',
});

const runId = await governance.startRun(undefined, { task: 'Fix auth bug' });

for (const block of response.content.filter((b) => b.type === 'tool_use')) {
const tcId = await governance.trackToolCall(runId, block.name, block.input);

const run = await governance.getRun(runId);
const tc = run.toolCalls?.find((t: any) => t.toolCallId === tcId);

if (tc?.status === 'blocked') {
continue; // Skip blocked tool calls
}

const result = await executeTool(block.name, block.input);
await governance.completeToolCall(runId, tcId, result);
}

await governance.completeRun(runId);

Vercel AI SDK

import { SequencewareClient } from '@sequenceware/sdk';
import { generateText, tool } from 'ai';
import { z } from 'zod';

const governance = new SequencewareClient({
baseUrl: 'https://sequenceware.yourcompany.com',
apiKey: 'acl_your_key',
agentId: 'my-vercel-agent',
});

// Wrap tools with governance
function governed(name: string, schema: z.ZodType, fn: Function) {
return tool({
description: name,
parameters: schema,
execute: async (input) => {
const tcId = await governance.trackToolCall(runId, name, input);
const run = await governance.getRun(runId);
const tc = run.toolCalls?.find((t: any) => t.toolCallId === tcId);
if (tc?.status === 'blocked') return { error: `Blocked: ${tc.blockedReason}` };

const result = await fn(input);
await governance.completeToolCall(runId, tcId, result);
return result;
},
});
}

CI/CD Pipelines

// scripts/governed-deploy.ts
import { SequencewareClient } from '@sequenceware/sdk';

const gov = new SequencewareClient({
baseUrl: process.env.SEQUENCEWARE_URL!,
apiKey: process.env.SEQUENCEWARE_API_KEY!,
agentId: 'github-deploy-agent',
});

const runId = await gov.startRun(undefined, {
task: `Deploy to ${process.env.DEPLOY_ENV}`,
sha: process.env.GITHUB_SHA,
});

const tcId = await gov.trackToolCall(runId, 'deploy', {
environment: process.env.DEPLOY_ENV,
}, undefined, 'deploy');

const run = await gov.getRun(runId);
const tc = run.toolCalls?.find((t: any) => t.toolCallId === tcId);

if (tc?.status === 'blocked') {
console.error('Deploy blocked:', tc.blockedReason);
process.exit(1);
}

if (tc?.status === 'awaiting_approval') {
console.log('Waiting for approval...');
// Poll or use webhooks
}

REST API (any language)

For Go, Java, Rust, or any language without an SDK, use POST /v1/events directly. See the Events API reference.