Core concepts
Middleware
bridge.use() runs before your handler. bridge.useReply() runs after. Both are ordered pipelines — throw to cancel the turn.
Two pipelines
bridge.use(fn) is a pre-message pipeline. Each function receives the message string, transforms it, and passes it to the next function (or to your onMessage handler).
bridge.useReply(fn) is a post-reply pipeline. Each function receives your handler's return value, transforms it, and passes the result back to PlayClaw.
middleware.js
1// Pre-message: runs before your onMessage handler2bridge.use(async (message) => {3 return message.trim().slice(0, 2000);4});5 6bridge.use(async (message) => {7 return message.replace(/\b(password|token|key)\b/gi, "[REDACTED]");8});9 10// Post-reply: runs after your handler returns, before sending back11bridge.useReply(async (reply) => {12 return reply.trim();13});14 15bridge.useReply(async (reply) => {16 // Enforce max reply length17 return reply.slice(0, 4000);18});Note
Middleware runs in registration order. You can chain as many as you need. Each function must return a string.
Cancelling a turn
Throw an error inside any middleware to cancel the turn. The bridge catches it, fires your onError hook, and sends an error response to PlayClaw — it never crashes your process.
middleware.js
1// Throw inside a middleware to cancel the turn2bridge.use(async (message) => {3 if (message.toLowerCase().includes("jailbreak")) {4 throw new Error("Rejected by content policy");5 }6 return message;7});8 9// The error is forwarded to your onError hook10bridge.onError((error, sessionId) => {11 console.error("Turn cancelled:", error.message, { sessionId });12});Content safety example
Combine pre-message and post-reply middleware for a full safety wrapper around your agent.
safety.js
1// Full content safety example2bridge.use(async (message) => {3 const risk = await contentSafety.analyze(message);4 if (risk.score > 0.8) throw new Error("Unsafe content detected");5 return message;6});7 8bridge.useReply(async (reply) => {9 // Strip PII from outgoing replies10 return piiScrubber.clean(reply);11});