Skip to main content

Python SDK for Pacerelle encrypted local agent relays.

Project description

Pacerelle Python SDK

Python agent client for Pacerelle encrypted local agent relays.

Use this SDK to connect a local Python process to Pacerelle, receive messages, reply to conversations, drive widgets, and return encrypted files or media.

pip install --pre pacerelle

Alpha release: APIs may change before the first stable release. Production wheels bundle the native Signal runtime for the target platform.

Requirements

  • Python 3.12 for the current alpha wheels.
  • A supported platform wheel: Windows x64, Linux x64, Linux ARM64, or macOS ARM64.

Python 3.11 support is planned, but the current alpha release is tested and published for CPython 3.12 only.

Before You Run

Create an agent in Pacerelle. The confirmation panel shows both Identifiant de l'agent and Jeton d'authentification. Use Copier la configuration .env to copy the required variables.

export PACERELLE_AGENT_ID="agent-id"
export PACERELLE_AGENT_TOKEN="agent-token"

On Windows PowerShell:

$env:PACERELLE_AGENT_ID = "agent-id"
$env:PACERELLE_AGENT_TOKEN = "agent-token"

Published packages connect to the Pacerelle API by default. Local source builds default to http://localhost:8080 for development.

Minimal Echo Agent

import asyncio
import os

from pacerelle import AgentGatewayClient

client = AgentGatewayClient(
    token=os.environ["PACERELLE_AGENT_TOKEN"],
    agent_id=os.environ["PACERELLE_AGENT_ID"],
    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())

Incoming Messages

The handler receives an AgentMessage:

async def handle(message, agent):
    print(message.id)
    print(message.conversation_id)
    print(message.from_id)
    print(message.text)
    print(message.attachments)
    print(message.widget_response)

Use message.from_id as the to value when replying to the user.

Sending Messages And Replies

Send a normal message:

await agent.send_message(
    conversation_id=message.conversation_id,
    to=message.from_id,
    text="I can help with that.",
)

Reply to a specific user message:

await agent.send_message(
    conversation_id=message.conversation_id,
    to=message.from_id,
    reply_to_message_id=message.id,
    text="Replying to your last message.",
)

Running Your Own Agent Logic

async def handle(message, agent):
    result = await run_my_agent(message.text)

    await agent.send_message(
        conversation_id=message.conversation_id,
        to=message.from_id,
        reply_to_message_id=message.id,
        text=result,
    )

Widgets

Widgets are sent as encrypted conversation messages. Each method returns the widget id. User answers arrive later as message.widget_response.

Confirm

await agent.send_confirm_widget(
    conversation_id=message.conversation_id,
    to=message.from_id,
    widget_id="confirm-delete",
    title="Delete file?",
    body="This cannot be undone.",
    danger=True,
    labels={"yes": "Delete", "no": "Cancel"},
)

Handle the answer:

if message.widget_response and message.widget_response.ref == "confirm-delete":
    if message.widget_response.cancelled:
        return
    if message.widget_response.value is True:
        await agent.send_message(
            conversation_id=message.conversation_id,
            to=message.from_id,
            text="Confirmed.",
        )

Choice

await agent.send_choice_widget(
    conversation_id=message.conversation_id,
    to=message.from_id,
    widget_id="choose-format",
    title="Choose a format",
    options=[
        {"id": "pdf", "label": "PDF"},
        {"id": "csv", "label": "CSV"},
    ],
    multi=False,
)

Permission

await agent.send_permission_widget(
    conversation_id=message.conversation_id,
    to=message.from_id,
    widget_id="permission-files",
    title="Allow file access?",
    body="The agent needs access to selected files.",
    scopes=["once", "session"],
)

Form

await agent.send_form_widget(
    conversation_id=message.conversation_id,
    to=message.from_id,
    widget_id="profile-form",
    title="Complete profile",
    submitLabel="Save",
    fields=[
        {"name": "email", "label": "Email", "type": "email", "required": True},
        {"name": "notes", "label": "Notes", "type": "textarea"},
    ],
)

Progress

progress_id = await agent.send_progress_widget(
    conversation_id=message.conversation_id,
    to=message.from_id,
    widget_id="import-progress",
    title="Importing files",
    value=10,
    max=100,
    cancellable=True,
)

Update it:

await agent.send_widget_update(
    conversation_id=message.conversation_id,
    to=message.from_id,
    ref=progress_id,
    spec={"value": 65, "body": "Almost done"},
)

File Picker

await agent.send_file_picker_widget(
    conversation_id=message.conversation_id,
    to=message.from_id,
    widget_id="pick-files",
    title="Choose files",
    multiple=True,
    accept=[".pdf", "image/*"],
    max_files=5,
)

Date And Time

await agent.send_datetime_widget(
    conversation_id=message.conversation_id,
    to=message.from_id,
    widget_id="schedule",
    title="Pick a meeting time",
    mode="datetime",
    min="2026-05-21T09:00:00",
)

Files And Media

send_file and send_media encrypt bytes locally with AES-GCM, upload only ciphertext to /agent/blobs, then send the attachment key and IV inside the E2EE message payload.

await agent.send_file(
    conversation_id=message.conversation_id,
    to=message.from_id,
    reply_to_message_id=message.id,
    text="Here is the report.",
    name="report.txt",
    mime="text/plain",
    data=b"private report",
)

Media adds optional dimensions or duration:

await agent.send_media(
    conversation_id=message.conversation_id,
    to=message.from_id,
    text="Preview attached.",
    name="chart.png",
    mime="image/png",
    data=png_bytes,
    width=1200,
    height=800,
)

Encryption

When e2ee=True, the SDK encrypts and decrypts messages locally before they leave your machine. On connect, the client publishes the agent pre-key bundle, establishes encrypted sessions for conversations, and keeps message contents opaque to the relay.

Use e2ee=False only for local debugging or non-encrypted transports.

MCP

This package is the Python SDK for building agents. The MCP server is distributed separately:

npx -y @pacerelle/mcp-server

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pacerelle-0.1.0a3.tar.gz (103.2 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

pacerelle-0.1.0a3-cp312-cp312-win_amd64.whl (780.5 kB view details)

Uploaded CPython 3.12Windows x86-64

pacerelle-0.1.0a3-cp312-cp312-manylinux_2_34_x86_64.whl (928.2 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.34+ x86-64

pacerelle-0.1.0a3-cp312-cp312-macosx_14_0_arm64.whl (625.6 kB view details)

Uploaded CPython 3.12macOS 14.0+ ARM64

File details

Details for the file pacerelle-0.1.0a3.tar.gz.

File metadata

  • Download URL: pacerelle-0.1.0a3.tar.gz
  • Upload date:
  • Size: 103.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.0

File hashes

Hashes for pacerelle-0.1.0a3.tar.gz
Algorithm Hash digest
SHA256 52c48a618877de7da22dcb783a8fd4be0c22f6dcc652c0d81edbcbc6d57d1332
MD5 d853d19758322444d800de817a61fe25
BLAKE2b-256 b999ea4881df295b5e33df3a284c6cc6cb0cef912c0f28e1cc2d5f733fec64fb

See more details on using hashes here.

File details

Details for the file pacerelle-0.1.0a3-cp312-cp312-win_amd64.whl.

File metadata

File hashes

Hashes for pacerelle-0.1.0a3-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 6af28aecf4a1d4f82bf8b2b86c7687200ed5924ddfa6df3bc99184e7585ba32e
MD5 749af87fd4fe47f100c60444f0a1ece6
BLAKE2b-256 fe11b41ca98d0d9f58eac76fb294db553b2b4c5ac52a02edd87e2a355c5a9b54

See more details on using hashes here.

File details

Details for the file pacerelle-0.1.0a3-cp312-cp312-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for pacerelle-0.1.0a3-cp312-cp312-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 0cfe1500f32584f6afcc79b63b1843b362ca7123932eafc28c678ec278347c9d
MD5 b0830d7c35dcd476367804452c253d34
BLAKE2b-256 545b6a08ee42154b1329922f1def8e578d9b554ec45460aa2c5ec7f108234045

See more details on using hashes here.

File details

Details for the file pacerelle-0.1.0a3-cp312-cp312-macosx_14_0_arm64.whl.

File metadata

File hashes

Hashes for pacerelle-0.1.0a3-cp312-cp312-macosx_14_0_arm64.whl
Algorithm Hash digest
SHA256 4606512c1bc86d85a36096233b0dded50612f72e4cd7b7f48ebaa1b014335f3e
MD5 f0cd61db86dfbefc419bd46f2b092749
BLAKE2b-256 1e8af8a6d1e8b376b21dbb46e82259f39176f44234c2480ef2e2b4192236b60f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page