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 pacerelle

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

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.0a2.tar.gz (53.0 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.0a2-cp312-cp312-win_amd64.whl (781.1 kB view details)

Uploaded CPython 3.12Windows x86-64

pacerelle-0.1.0a2-cp312-cp312-manylinux_2_34_x86_64.whl (928.6 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.34+ x86-64

pacerelle-0.1.0a2-cp312-cp312-manylinux_2_34_aarch64.whl (738.8 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.34+ ARM64

pacerelle-0.1.0a2-cp312-cp312-macosx_14_0_arm64.whl (625.3 kB view details)

Uploaded CPython 3.12macOS 14.0+ ARM64

File details

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

File metadata

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

File hashes

Hashes for pacerelle-0.1.0a2.tar.gz
Algorithm Hash digest
SHA256 c21dab06fb30a4720648e799ad8ef9efff1364dc46c5f4ae74da60287f16ba39
MD5 e76e13a7358a4578f43a7f0103f3059f
BLAKE2b-256 e21d56cbbc8eeb61ca5a3d10153579b25bd520f1f39e285b4ebb2f3bd380b77c

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pacerelle-0.1.0a2-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 9b373096f7b4bf2db107a47fa48b0d508638737cf794a42f91cfc6f7ae51b2ef
MD5 b74966e969c381236d88e1211804a074
BLAKE2b-256 677a453c431f4916d51b6ac8243f589cb7c77f01cb4f2d7bdce861a988798d4a

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pacerelle-0.1.0a2-cp312-cp312-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 716a5510e848bc3ee69aa750380052aa796cc4fb9332cd837c29d5301e929145
MD5 b52b56cc1be92c9e76aa5a66c5ee165c
BLAKE2b-256 95e2d84fa38927093480ed27235c261b9e26455a6e19f3bc68b61079b4c51869

See more details on using hashes here.

File details

Details for the file pacerelle-0.1.0a2-cp312-cp312-manylinux_2_34_aarch64.whl.

File metadata

File hashes

Hashes for pacerelle-0.1.0a2-cp312-cp312-manylinux_2_34_aarch64.whl
Algorithm Hash digest
SHA256 5bd048fd6b48a0bd772f2783cbe128d5ef71ca8bc6d391adb2ed819b176bde3a
MD5 8b7ff436a9f67e77e69041cc4cba286d
BLAKE2b-256 f6214997787fe4b5de700052d5787512081792357d901792d0060dcee77c19f4

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pacerelle-0.1.0a2-cp312-cp312-macosx_14_0_arm64.whl
Algorithm Hash digest
SHA256 db3f2d5adfae793c03ec45456008cc3513877121443273a65f70f5fdc62a0ae4
MD5 cd3e84390ecbdf0d774196dc438966ae
BLAKE2b-256 264caff05d6f5be7613d56f5afebee9b67a522e04437833ecb78859594bf4968

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