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.