SDK Guide
@agentfirewall/sdk and agentfirewall for Python) have been removed from the codebase. Use the proxy quickstart instead - zero code changes, works with any agent framework. The content below is kept for historical reference only.Installation note
The SDKs are currently distributed as part of the TameFlare monorepo. Install them locally:
# Node.js - from the monorepo root
npm install ./packages/sdk-node
# Python - from the monorepo root
pip install ./packages/sdk-pythonWhen published to npm/PyPI (coming soon), you'll be able to install with npm install @agentfirewall/sdk and pip install agentfirewall.
Node.js
# Install from source (npm package coming soon)
npm install ./packages/sdk-nodeBasic usage
import { AgentFirewall } from "@agentfirewall/sdk";
const TameFlare = new AgentFirewall({
apiKey: process.env.AAF_API_KEY,
baseUrl: process.env.AAF_BASE_URL ?? "http://localhost:3000/api",
});
const result = await TameFlare.requestAction({
type: "github.pr.merge",
resource: {
provider: "github",
account: "my-org",
target: "my-org/my-repo",
environment: "production",
},
parameters: { pull_number: 42 },
});
if (result.status === "allowed") {
await TameFlare.execute(result.action_request_id, result.decision_token);
} else if (result.status === "pending_approval") {
const approved = await TameFlare.waitForApproval(result.action_request_id);
if (approved) await TameFlare.execute(result.action_request_id, approved.decision_token);
}Handling all outcomes
const result = await TameFlare.requestAction({
type: "payment.transfer.initiate",
resource: {
provider: "stripe",
account: "acct_123",
target: "transfers",
environment: "production",
},
parameters: { amount: 25000, currency: "USD" },
risk_hints: {
financial_impact: true,
irreversible: true,
production_target: true,
},
});
switch (result.status) {
case "allowed":
// Action approved immediately - execute it
const execResult = await TameFlare.execute(
result.action_request_id,
result.decision_token
);
console.log("Executed:", execResult);
break;
case "pending_approval":
// Action needs human approval - wait or use webhook
console.log("Waiting for approval from:", result.approval.required_approver_groups);
const approved = await TameFlare.waitForApproval(result.action_request_id, {
timeoutMs: 300_000, // 5 minutes
pollIntervalMs: 5_000, // check every 5s
});
if (approved) {
await TameFlare.execute(result.action_request_id, approved.decision_token);
} else {
console.log("Approval timed out or was rejected");
}
break;
case "denied":
// Action blocked by policy
console.log("Denied:", result.decision.reason);
// Do NOT retry - the policy will deny it again
break;
}Error handling and rate limits
try {
const result = await TameFlare.requestAction({ /* ... */ });
} catch (error) {
if (error.status === 429) {
// Rate limited - the SDK auto-retries by default
// but you can handle it manually if needed
const retryAfter = error.retryAfter; // seconds
console.log(`Rate limited. Retry after ${retryAfter}s`);
} else if (error.status === 401) {
// Invalid API key
console.error("Check your AAF_API_KEY");
} else {
console.error("Unexpected error:", error.message);
}
}429 (rate limit) and 5xx (server error) responses with exponential backoff. You can disable this with autoRetry: false in the client config.Failover mode (circuit breaker)
By default, the SDK throws an error when TameFlare is unreachable (failoverMode: "closed"). You can switch to "open" mode to get a synthetic deny result instead of an exception:
const TameFlare = new AgentFirewall({
apiKey: process.env.AAF_API_KEY,
failoverMode: "open",
onFailover: (error) => {
// Alert your monitoring system
console.error("TameFlare unreachable, failover triggered:", error.message);
},
});
// If TameFlare is down, this returns a deny result instead of throwing
const result = await TameFlare.requestAction({ /* ... */ });
if (result.status === "denied" && result.decision.reason.includes("failover")) {
// Handle gracefully - TameFlare was unreachable
}| Mode | Behavior when TameFlare is down | Use case |
|---|---|---|
| closed (default) | Throws error - agent must handle | Safety-critical: no action without TameFlare |
| open | Returns synthetic deny | Graceful degradation: agent gets a clear signal |
Using webhooks instead of polling
Instead of polling for approval status, you can provide a webhook_url to be notified when a decision is made:
const result = await TameFlare.requestAction({
type: "github.pr.merge",
resource: { /* ... */ },
parameters: { pull_number: 42 },
}, {
webhookUrl: "https://your-server.com/TameFlare-webhook",
});
// Your webhook endpoint receives:
// POST /TameFlare-webhook
// {
// "action_request_id": "act_abc123",
// "status": "approved",
// "decision_token": "eyJhbGci..."
// }Python
# Install from source (PyPI package coming soon)
pip install ./packages/sdk-pythonBasic usage
from agentfirewall import AgentFirewall
import os
TameFlare = AgentFirewall(
api_key=os.environ["AAF_API_KEY"],
base_url=os.environ.get("AAF_BASE_URL", "http://localhost:3000/api"),
)
result = TameFlare.request_action(
action_type="github.issue.create",
provider="github",
account="my-org",
target="my-org/my-repo",
environment="development",
parameters={"title": "Bug fix", "body": "Details..."},
)
if result.status == "allowed":
TameFlare.execute(result.action_request_id, result.decision_token)Handling all outcomes
result = TameFlare.request_action(
action_type="payment.transfer.initiate",
provider="stripe",
account="acct_123",
target="transfers",
environment="production",
parameters={"amount": 25000, "currency": "USD"},
risk_hints={
"financial_impact": True,
"irreversible": True,
"production_target": True,
},
)
if result.status == "allowed":
exec_result = TameFlare.execute(result.action_request_id, result.decision_token)
print(f"Executed: {exec_result}")
elif result.status == "pending_approval":
print(f"Waiting for approval from: {result.approval.required_approver_groups}")
approved = TameFlare.wait_for_approval(
result.action_request_id,
timeout_seconds=300,
poll_interval_seconds=5,
)
if approved:
TameFlare.execute(result.action_request_id, approved.decision_token)
else:
print("Approval timed out or was rejected")
elif result.status == "denied":
print(f"Denied: {result.decision.reason}")Async client
from agentfirewall import AsyncAgentFirewall
import asyncio
async def main():
TameFlare = AsyncAgentFirewall(
api_key=os.environ["AAF_API_KEY"],
)
result = await TameFlare.request_action(
action_type="infra.server.provision",
provider="aws",
account="123456789",
target="ec2",
environment="staging",
parameters={"instance_type": "t3.large", "count": 3},
)
if result.status == "allowed":
await TameFlare.execute(result.action_request_id, result.decision_token)
asyncio.run(main())Error handling
from agentfirewall.exceptions import RateLimitError, AuthError, AAFError
try:
result = TameFlare.request_action(...)
except RateLimitError as e:
print(f"Rate limited. Retry after {e.retry_after}s")
except AuthError:
print("Invalid API key")
except AAFError as e:
print(f"TameFlare error: {e.message}")CLI
The CLI is useful for managing agents and policies from your terminal or CI/CD pipelines.
npx tf init # Set up TameFlare config in current directory
npx tf status # Check control plane connectivity
npx tf agents list # List all registered agents
npx tf policies list # List all policiesAgent management
# Create a new agent
npx tf agents create --name "deploy-bot" --env production
# Revoke an agent's API key
npx tf agents revoke --id agent_abc123
# View agent activity
npx tf agents activity --id agent_abc123 --limit 20Policy management
# Install a policy from a YAML file
npx tf policies install ./policies/payment-controls.yaml
# List all policies with status
npx tf policies list
# Dry-run a policy against a test action
npx tf policies dry-run \
--type payment.transfer.initiate \
--provider stripe \
--env production \
--param amount=25000.TameFlare.yaml in your project root. Run npx tf init to create one with your API key and base URL.Framework integration
LangChain (Python)
Wrap TameFlare checks around LangChain tool calls:
from langchain.tools import tool
from agentfirewall import AgentFirewall
TameFlare = AgentFirewall(api_key=os.environ["AAF_API_KEY"])
@tool
def create_github_issue(title: str, body: str, repo: str) -> str:
"""Create a GitHub issue with TameFlare policy enforcement."""
result = TameFlare.request_action(
action_type="github.issue.create",
provider="github",
account="my-org",
target=repo,
environment="production",
parameters={"title": title, "body": body},
)
if result.status == "denied":
return f"Blocked by policy: {result.decision.reason}"
if result.status == "allowed":
exec_result = TameFlare.execute(result.action_request_id, result.decision_token)
return f"Issue created: {exec_result.result}"
return f"Pending approval from: {result.approval.required_approver_groups}"OpenAI function calling (Node.js)
Gate tool execution behind TameFlare before calling external APIs:
import { AgentFirewall } from "@agentfirewall/sdk";
import OpenAI from "openai";
const TameFlare = new AgentFirewall({ apiKey: process.env.AAF_API_KEY! });
const openai = new OpenAI();
async function handleToolCall(toolCall: OpenAI.ChatCompletionMessageToolCall) {
const { name, arguments: args } = toolCall.function;
const parsed = JSON.parse(args);
// Check with TameFlare before executing
const result = await TameFlare.requestAction({
type: `tool.${name}`,
resource: {
provider: "openai-tools",
account: "my-org",
target: name,
environment: "production",
},
parameters: parsed,
context: { reason: "OpenAI function call" },
risk_hints: {},
});
if (result.status !== "allowed") {
return { error: `Blocked: ${result.decision.reason}` };
}
// Execute the actual tool call
return executeToolFunction(name, parsed);
}CrewAI (Python)
Add TameFlare as a pre-execution hook for CrewAI agents:
from crewai import Agent, Task, Crew
from agentfirewall import AgentFirewall
TameFlare = AgentFirewall(api_key=os.environ["AAF_API_KEY"])
def aaf_guarded_tool(action_type: str, params: dict):
"""Wrapper that checks TameFlare before executing any tool."""
result = TameFlare.request_action(
action_type=action_type,
provider="crewai",
account="my-org",
target=action_type,
environment="production",
parameters=params,
)
if result.status != "allowed":
raise PermissionError(f"TameFlare denied: {result.decision.reason}")
return result
# Use in your CrewAI tools
researcher = Agent(
role="Researcher",
goal="Find information",
tools=[your_aaf_wrapped_tools],
)requestAction() before executing the tool, check the result, and only proceed if allowed. TameFlare is framework-agnostic - it works with any agent that makes API calls.