@bound-ai/sdk
v0.1.0
Published
Official SDK for Agent Guardrails
Readme
@bounded/sdk
Official TypeScript SDK for Bounded (Agent Guardrails).
Installation
npm install @bounded/sdk
# or
pnpm add @bounded/sdk
# or
yarn add @bounded/sdkQuick Start
import { BoundedClient } from '@bounded/sdk';
import Stripe from 'stripe';
const bounded = new BoundedClient({
apiKey: process.env.BOUNDED_API_KEY!,
baseUrl: 'https://api.bounded.example.com', // Optional, defaults to localhost:3001
});
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
// Wrap Stripe refund with guardrail
const result = await bounded.guardrail({
actionType: 'stripe.refund',
payload: {
amount: 12000,
currency: 'usd',
payment_intent: 'pi_123',
},
execute: () => stripe.refunds.create({
payment_intent: 'pi_123',
amount: 12000,
}),
redactResult: (refund) => ({
refund_id: refund.id,
status: refund.status,
}),
});
if (result.status === 'executed') {
console.log('Refund executed:', result.result);
} else if (result.status === 'pending_approval') {
console.log('Approval required:', result.approvalId);
} else if (result.status === 'blocked') {
console.log('Action blocked:', result.explanation);
}API Reference
BoundedClient
Constructor
new BoundedClient(config: BoundedClientConfig)Config:
apiKey(string, required): Your Bounded API keybaseUrl(string, optional): API base URL (default:http://localhost:3001)
Methods
guardrail<T>(request: GuardrailRequest<T>): Promise<GuardrailResult<T>>
Evaluate policy decision and execute callback if allowed.
Request:
actionType(string): Type of action (e.g.,'stripe.refund')resource(string, optional): Resource identifier (e.g.,'payment_intent:pi_123')payload(object): Action payloadcontext(object, optional): Additional context for policy evaluationexecute(function): Callback to execute if allowedredactResult(function, optional): Function to redact sensitive data from resultonPendingApproval(function, optional): Callback when approval is required
Result:
status:'executed'|'pending_approval'|'blocked'|'failed'actionId: Unique action IDdecision: Policy decisionresult: Execution result (ifstatus === 'executed')approvalId: Approval ID (ifstatus === 'pending_approval')explanation: Human-readable explanationerror: Error object (ifstatus === 'failed')
Example:
const result = await bounded.guardrail({
actionType: 'user.delete',
resource: 'user:usr_123',
payload: { user_id: 'usr_123' },
execute: async () => {
await db.users.delete({ id: 'usr_123' });
return { deleted: true };
},
redactResult: (r) => ({ deleted: r.deleted }),
onPendingApproval: (approvalId, actionId) => {
console.log(`Approval required: ${approvalId}`);
},
});decide(request: DecideRequest): Promise<DecideResult>
Get decision without executing (decide-only mode).
Request:
actionType(string): Type of actionresource(string, optional): Resource identifierpayload(object): Action payloadcontext(object, optional): Additional context
Result:
actionId: Unique action IDdecision:'allow'|'block'|'require_approval'policyId: Policy ID (if matched)approvalId: Approval ID (ifdecision === 'require_approval')explanation: Human-readable explanation
Example:
const decision = await bounded.decide({
actionType: 'stripe.refund',
payload: { amount: 50000 },
});
if (decision.decision === 'allow') {
// Execute manually
await stripe.refunds.create({ ... });
}waitForApproval(approvalId: string, options?): Promise<ApprovalStatus>
Poll for approval decision (blocking).
Options:
pollInterval(number, optional): Polling interval in ms (default: 2000)timeout(number, optional): Timeout in ms (default: 300000 = 5 min)
Result:
id: Approval IDstatus:'pending'|'approved'|'rejected'decidedBy: User ID who decided (if decided)decidedAt: Decision timestamp (if decided)comment: Optional comment
Example:
const approval = await bounded.waitForApproval(approvalId, {
pollInterval: 2000,
timeout: 300000,
});
if (approval.status === 'approved') {
// Re-execute action
}Error Handling
import { BoundedError, AuthError, PolicyError } from '@bounded/sdk';
try {
await bounded.guardrail({ ... });
} catch (error) {
if (error instanceof AuthError) {
console.error('Invalid API key');
} else if (error instanceof PolicyError) {
console.error('Policy violation:', error.decision);
} else if (error instanceof BoundedError) {
console.error('Bounded error:', error.type, error.details);
}
}Error Classes
BoundedError: Base error classstatusCode: HTTP status codetype: Error typedetails: Additional error details
HttpError extends BoundedError: HTTP errorsAuthError extends BoundedError: Authentication errors (401)PolicyError extends BoundedError: Policy violations (403)decision: Policy decisionpolicyId: Policy ID
TypeScript Support
The SDK is written in TypeScript and includes full type definitions.
import { GuardrailRequest, GuardrailResult, DecideResult } from '@bounded/sdk';
// Type-safe guardrail request
const request: GuardrailRequest<{ refund_id: string }> = {
actionType: 'stripe.refund',
payload: { amount: 12000 },
execute: async () => {
const refund = await stripe.refunds.create({ ... });
return { refund_id: refund.id };
},
};
const result: GuardrailResult<{ refund_id: string }> = await bounded.guardrail(request);
if (result.status === 'executed') {
console.log(result.result.refund_id); // Type-safe!
}Best Practices
- Redact sensitive data in
redactResult(don't log full credit card numbers, passwords, etc.) - Use resource IDs for better audit trails (
resource: "payment_intent:pi_123") - Handle all statuses (executed, pending_approval, blocked, failed)
- Set up policies before deploying agents
- Monitor audit trail regularly via dashboard
- Use environment variables for API keys (never hardcode)
Examples
Stripe Refund
const result = await bounded.guardrail({
actionType: 'stripe.refund',
resource: `payment_intent:${paymentIntentId}`,
payload: {
payment_intent: paymentIntentId,
amount: 12000,
reason: 'requested_by_customer',
},
execute: () => stripe.refunds.create({
payment_intent: paymentIntentId,
amount: 12000,
}),
redactResult: (refund) => ({
refund_id: refund.id,
status: refund.status,
amount: refund.amount,
}),
});Jira Issue Creation
const result = await bounded.guardrail({
actionType: 'jira.create_issue',
payload: {
project: 'PROJ',
summary: 'Bug report from agent',
description: 'User reported issue...',
issue_type: 'Bug',
},
execute: () => jira.issues.createIssue({
fields: {
project: { key: 'PROJ' },
summary: 'Bug report from agent',
description: 'User reported issue...',
issuetype: { name: 'Bug' },
},
}),
redactResult: (issue) => ({
issue_key: issue.key,
issue_id: issue.id,
}),
});Database Mutation
const result = await bounded.guardrail({
actionType: 'db.user.delete',
resource: `user:${userId}`,
payload: { user_id: userId },
execute: async () => {
await db.users.delete({ id: userId });
return { deleted: true };
},
onPendingApproval: (approvalId) => {
console.log(`User deletion requires approval: ${approvalId}`);
// Send notification to admins
},
});License
MIT
