playclaw-sdk
v1.1.0
Published
Connect any AI agent to PlayClaw — the professional audit playground. One bridge. Any model.
Downloads
200
Maintainers
Readme
playclaw-sdk
Connect any AI agent to PlayClaw — the professional audit playground.
npm install playclaw-sdkThe 5-line version
const { PlayclawBridge } = require('playclaw-sdk');
const bridge = new PlayclawBridge({ token: 'PC-XXXX-XXXX-XXXX' });
bridge.onMessage(async (message, context) => {
return await myAgent.reply(message);
});
bridge.connect();That's it. The SDK handles the connection, sessions, turn limits, error recovery, and auto-reconnect.
What this SDK does
PlayClaw audits AI agents by running structured conversations through your agent. The SDK is the bridge between PlayClaw's platform and your agent code.
Each time someone clicks "Start Audit" in PlayClaw, a session begins. The SDK:
- Detects the new session and fires
onSessionStart - Routes each message from PlayClaw to your
onMessagehandler - Sends your agent's reply back to PlayClaw
- Enforces the turn limit (default: 5)
- Fires
onSessionEndwith the full conversation history - Reconnects automatically if the connection drops
Full API reference
new PlayclawBridge(options)
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| token | string | required | Your PC-XXXX PlayClaw token |
| auditTurns | number | 5 | Max turns per audit session |
| autoReconnect | boolean | true | Reconnect if channel drops |
| logLevel | 'debug'\|'info'\|'warn'\|'error'\|'silent' | 'info' | Console log verbosity |
| logger | function(level, message, meta) | null | Custom log output function |
| rateLimit | RateLimitOptions | null | Enable built-in rate limiting |
| supabaseUrl | string | PlayClaw default | Override for self-hosted setups |
| supabaseKey | string | PlayClaw default | Override for self-hosted setups |
Hooks
All hooks return this for chaining.
.onMessage(handler) ← required
bridge.onMessage(async (message, context) => {
// message: string — what the user sent
// context.sessionId : 'sess_abc123'
// context.turnNumber : 1, 2, 3, 4, or 5
// context.totalTurns : 5
// context.isLastTurn : true on turn 5
// context.history : [{ role, content, timestamp }, ...]
return "your reply as a string";
});.onSessionStart(handler)
bridge.onSessionStart((sessionId) => {
myAgent.resetMemory(); // new audit = fresh state
});.onSessionEnd(handler)
bridge.onSessionEnd((sessionId, history) => {
database.save({ sessionId, history });
});.onError(handler)
bridge.onError((error, sessionId) => {
monitoring.report(error); // bridge stays alive
});.onConnect(handler) / .onDisconnect(handler)
bridge.onConnect(() => console.log("Live"));
bridge.onDisconnect(() => console.log("Reconnecting..."));Middleware
// Transform messages BEFORE your handler sees them
bridge.use(async (message, context) => {
return sanitize(message);
});
// Transform replies BEFORE they're sent to PlayClaw
bridge.useReply(async (reply, context) => {
return translate(reply, "en");
});Middlewares run in registration order. Throwing inside a middleware cancels the message.
State & Introspection
bridge.isConnected // boolean
bridge.sessionInfo // { sessionId, turnCount, auditTurns, isExhausted }
bridge.stats // { sessionsHandled, messagesHandled, errorsCount, reconnectsCount }Rate Limiting
const bridge = new PlayclawBridge({
token: 'PC-...',
rateLimit: {
maxPerWindow: 60, // max 60 messages per minute (global)
windowMs: 60_000, // 1-minute window
maxConcurrent: 1, // max 1 in-flight call per session
}
});Or as middleware for custom logic:
const { PlayclawRateLimiter } = require('playclaw-sdk');
const limiter = new PlayclawRateLimiter({ maxPerWindow: 30, windowMs: 60_000 });
bridge.use(limiter.middleware());Real-world examples
OpenAI (GPT-4o)
const { PlayclawBridge } = require('playclaw-sdk');
const OpenAI = require('openai');
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const bridge = new PlayclawBridge({ token: process.env.PC_TOKEN });
let history = [];
bridge.onSessionStart(() => { history = []; });
bridge.onMessage(async (message, context) => {
history.push({ role: "user", content: message });
const res = await openai.chat.completions.create({
model: "gpt-4o",
messages: [
{ role: "system", content: context.isLastTurn ? "Give a conclusive answer." : "Be helpful." },
...history,
],
});
const reply = res.choices[0].message.content;
history.push({ role: "assistant", content: reply });
return reply;
});
bridge.connect();Anthropic (Claude)
const { PlayclawBridge } = require('playclaw-sdk');
const Anthropic = require('@anthropic-ai/sdk');
const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
const bridge = new PlayclawBridge({ token: process.env.PC_TOKEN });
let history = [];
bridge.onSessionStart(() => { history = []; });
bridge.onMessage(async (message) => {
history.push({ role: "user", content: message });
const res = await anthropic.messages.create({
model: "claude-opus-4-6",
max_tokens: 1024,
messages: history,
});
const reply = res.content[0].text;
history.push({ role: "assistant", content: reply });
return reply;
});
bridge.connect();Any HTTP agent (n8n, Flowise, local LLM)
const { PlayclawBridge } = require('playclaw-sdk');
const bridge = new PlayclawBridge({ token: process.env.PC_TOKEN });
bridge.onMessage(async (message) => {
const res = await fetch("https://my-agent.internal/chat", {
method: "POST",
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${process.env.AGENT_KEY}` },
body: JSON.stringify({ message }),
});
const data = await res.json();
return data.reply;
});
bridge.connect();Multiple PlayClaw accounts (multi-tenant)
const { PlayclawBridge } = require('playclaw-sdk');
const tokens = process.env.PC_TOKENS.split(",");
const bridges = tokens.map((token) => {
const bridge = new PlayclawBridge({ token });
bridge.onMessage(async (message) => await myAgent.reply(message));
return bridge;
});
await Promise.all(bridges.map((b) => b.connect()));
console.log(`${bridges.length} bridges connected.`);Logging
By default the SDK logs to stdout at info level with timestamps and color.
2026-03-23T14:00:01.123Z INFO [playclaw:PC-TEST] Bridge connected — listening for audit messages
2026-03-23T14:00:02.456Z INFO [playclaw:PC-TEST] Audit session started { sessionId: 'sess_abc' }
2026-03-23T14:00:05.789Z INFO [playclaw:PC-TEST] Audit session ended { sessionId: 'sess_abc', messages: 10 }Levels: debug | info | warn | error | silent
// Verbose mode (see every message processed)
new PlayclawBridge({ token: 'PC-...', logLevel: 'debug' });
// No output
new PlayclawBridge({ token: 'PC-...', logLevel: 'silent' });
// Pipe to your own logger (winston, pino, etc.)
new PlayclawBridge({
token: 'PC-...',
logger: (level, message, meta) => winston.log(level, message, meta),
});Running tests
node test/bridge.test.jsTests run without any real Supabase connection using the built-in MockTransport.
Advanced: build your own transport
If you want to use a different backend (custom WebSocket server, etc.):
const { PlayclawBridge, PlayclawTransport } = require('playclaw-sdk');
class MyCustomTransport extends PlayclawTransport {
async connect() { /* your connection logic */ }
async send(event, payload) { /* your send logic */ }
}
const bridge = new PlayclawBridge({ token: 'PC-...' });
bridge._transport = new MyCustomTransport('PC-...');
bridge.connect();License
MIT — playclaw.info
