SDK integration
Build complete JavaScript and Python agent runtimes with real credentials.
The SDK is the recommended way to build a custom local agent. It handles the repetitive runtime work:
- connecting your process to Pacerelle
- using the agent id and token from the app
- incoming message parsing
- replies
- widgets
- file and media uploads
Start with the integration path if you want the shortest tested path, then come back here for SDK details.
Required environment
PACERELLE_AGENT_ID="agent id copied from the New agent dialog"
PACERELLE_AGENT_TOKEN="token copied from the New agent dialog"
Hosted Pacerelle users only need these two values.
JavaScript SDK
Install
npm i @pacerelle/sdk
Create the agent file
import {
createNodeAgentGatewayClient,
createNodeSignalSnapshotStore,
} from "@pacerelle/sdk/node";
const agentId = process.env.PACERELLE_AGENT_ID!;
const signalStore = createNodeSignalSnapshotStore(agentId);
const client = createNodeAgentGatewayClient({
agentId,
token: process.env.PACERELLE_AGENT_TOKEN!,
baseUrl: process.env.PACERELLE_BASE_URL,
wsUrl: process.env.PACERELLE_WS_URL,
e2ee: true,
signalSnapshot: signalStore.signalSnapshot,
onSignalSnapshot: signalStore.onSignalSnapshot,
onOpen: () => console.log("connected"),
onClose: () => console.log("closed"),
onError: console.error,
});
client.onMessage(async (message, agent) => {
await agent.sendMessage({
conversationId: message.conversationId,
to: message.from,
replyToMessageId: message.id,
text: `Received: ${message.text}`,
});
});
await client.connect();
Run the agent
node --env-file=.env agent.mjs
JavaScript handler contract
The handler receives message and agent.
client.onMessage(async (message, agent) => {
console.log(message.id);
console.log(message.conversationId);
console.log(message.from);
console.log(message.text);
console.log(message.attachments);
console.log(message.widgetResponse);
});
When replying, always use the inbound conversationId and from:
await agent.sendMessage({
conversationId: message.conversationId,
to: message.from,
replyToMessageId: message.id,
text: "Done.",
});
Connect your own logic
Replace the echo with your model, script, or workflow:
client.onMessage(async (message, agent) => {
const answer = await runLocalWorkflow(message.text);
await agent.sendMessage({
conversationId: message.conversationId,
to: message.from,
replyToMessageId: message.id,
text: answer,
});
});
Python SDK
Install
pip install --pre pacerelle
Create the agent file
import asyncio
import os
from pacerelle import AgentGatewayClient
client = AgentGatewayClient(
agent_id=os.environ["PACERELLE_AGENT_ID"],
token=os.environ["PACERELLE_AGENT_TOKEN"],
base_url=os.environ.get("PACERELLE_BASE_URL"),
ws_url=os.environ.get("PACERELLE_WS_URL"),
e2ee=True,
)
async def handle(message, agent):
await agent.send_message(
conversation_id=message.conversation_id,
to=message.from_id,
reply_to_message_id=message.id,
text=f"Received: {message.text}",
)
client.on_message(handle)
asyncio.run(client.connect())
Python requirements
- Python 3.12 for the current alpha wheels.
- Windows x64, Linux x64, Linux ARM64, or macOS ARM64.
Long-running agents
For long-running encrypted agents, persist Signal state between restarts:
- JavaScript: use
createNodeAgentGatewayClient(...)andcreateNodeSignalSnapshotStore(agentId)from@pacerelle/sdk/node. - Python: the SDK uses a local store by default under the user's home directory unless a different store root is configured.
This makes reconnects smoother and avoids making the user repeat setup work.
An encrypted agent has two identities: PACERELLE_AGENT_ID and the local Signal
snapshot. Reusing the same agent id with a new snapshot can break older
encrypted conversations. The Node helper stores state at:
~/.pacerelle/agents/<agent-id>.signal.b64
Override the location with PACERELLE_AGENT_SIGNAL_STATE_FILE or
PACERELLE_AGENT_SIGNAL_STATE_DIR when you need a managed path.
Error handling checklist
- Store agent tokens in environment variables or a secret manager.
- Add
onOpen,onClose, andonErrorcallbacks in JavaScript. - Keep the process alive; the SDK listens continuously over WebSocket.
- Do not log decrypted user messages in production.
- Reconnect after network drops.
- Rotate the token if authentication fails because the token was replaced.
When to use MCP instead
Use the SDK when you are building your own runtime. Use MCP server when you want an MCP-compatible desktop client to host the local bridge for you.