Skip to main content

Python SDK for controlling Happy agent sessions

Project description

happy-engineering-sdk

PyPI version Python versions License: MIT

Python SDK for controlling Happy agent sessions.

Installation

pip install happy-engineering-sdk

Credentials

The SDK supports three ways to supply credentials.

1. Environment variables (recommended for containers)

export HAPPY_SERVER_URL=https://api.happy.engineering
export HAPPY_TOKEN=eyJ...
export HAPPY_SECRET=DroKzo0w...==

HAPPY_TOKEN is the bearer token and HAPPY_SECRET is the raw base64 machineKey string from your access.key file.

2. Key file (default for local use)

Download access.key (or agent.key) from the Happy dashboard and place it at:

~/.happy/access.key   # written by the Happy CLI
~/.happy/agent.key    # legacy location

The SDK understands both formats — the CLI-written access.key format (encryption.machineKey) and the older agent.key format (secret).

Set the server URL:

export HAPPY_SERVER_URL=https://api.happy.engineering

3. Inline kwargs

client = HappyClient(
    server_url="https://api.happy.engineering",
    token="eyJ...",
    secret_b64="DroKzo0w...==",
)

Quick start — async (HappyClient)

import asyncio
from happy_sdk import HappyClient

async def main():
    client = HappyClient()          # reads ~/.happy/agent.key + HAPPY_SERVER_URL
    session_id = await client.run_task(
        machine_id="my-machine",
        directory="/home/user/project",
        prompt="Summarise this week's PRs",
    )
    print(f"Task complete — session {session_id}")

asyncio.run(main())

Using environment variables:

client = HappyClient.from_env()    # reads HAPPY_TOKEN, HAPPY_SECRET, HAPPY_SERVER_URL

Quick start — sync (SyncHappyClient)

For Django management commands, CLI scripts, or any sync context — use SyncHappyClient. It has the same API as HappyClient but wraps every call with asyncio.run() internally so you never touch async machinery:

from happy_sdk import SyncHappyClient

# From environment variables
client = SyncHappyClient.from_env()

session_id = client.run_task(
    machine_id="my-machine",
    directory="/home/user/project",
    prompt="Summarise this week's PRs",
)
print(f"Task complete — session {session_id}")

All three constructor styles work with SyncHappyClient:

# From env vars
client = SyncHappyClient.from_env()
client = SyncHappyClient.from_env(server_url="https://...")

# From inline kwargs
client = SyncHappyClient(server_url="...", token="...", secret_b64="...")

# From key file
client = SyncHappyClient(server_url="...", credentials_path="~/.happy/access.key")

Manual session lifecycle

import asyncio
from happy_sdk import HappyClient

async def main():
    client = HappyClient()

    session_id = await client.spawn_session(
        machine_id="my-machine",
        directory="/home/user/project",
    )
    await client.send_message(session_id, "Hello")
    await client.wait_for_turn_completion(session_id)
    messages = await client.get_messages(session_id)
    await client.stop_session(session_id)

asyncio.run(main())

API reference

HappyClient (async) / SyncHappyClient (sync)

Both classes expose identical method signatures. HappyClient methods are async; SyncHappyClient methods are regular (blocking) functions.

Constructors

Constructor Description
HappyClient(server_url=None, credentials_path=None, token=None, secret_b64=None) File or kwargs. token+secret_b64 take precedence over credentials_path. server_url falls back to HAPPY_SERVER_URL.
HappyClient.from_env(server_url=None) Reads HAPPY_TOKEN, HAPPY_SECRET, HAPPY_SERVER_URL. Raises AuthenticationError if any are missing.
SyncHappyClient(...) Same arguments as HappyClient.
SyncHappyClient.from_env(server_url=None) Same as HappyClient.from_env.

Session lifecycle

Method Signature Description
spawn_session (machine_id, directory, agent="claude", create_dir=False, name=None) → str Create a new agent session — returns the session ID. Pass name= to label it in the Happy apps (applied right after spawn)
stop_session (session_id) Stop a running session
delete_session (session_id) Permanently delete a session

Naming & metadata

Method Signature Description
set_session_name (session_id, name) → Session Set the session's human-visible name (shown in the Happy web/mobile apps)
update_session_metadata (session_id, changes: dict) → Session Merge changes into the session's metadata and persist it. Shallow merge (your keys win, others preserved), with optimistic-concurrency retries

A session's name lives in its encrypted metadata rather than being a spawn-time argument, so naming is a quick follow-up write after the session exists. The Happy apps show metadata.summary.text as the session title, so set_session_name writes the name there (and mirrors it to metadata.name for read-back):

sid = await client.spawn_session(machine_id, "/repo", name="Nightly build")
# ...or rename later:
await client.set_session_name(sid, "Nightly build (retry)")
# read it back:
session = await client.get_session(sid)
print(session.metadata["summary"]["text"], session.metadata_version)

Messaging

Method Signature Description
send_message (session_id, text, permission_mode="yolo") Send a message to an active session

Waiting

Method Signature Description
wait_for_turn_completion (session_id, timeout_seconds=300) Block until the agent finishes its current turn
wait_for_idle (session_id, timeout_seconds=300) Block until the session enters an idle state

Query

Method Signature Description
list_sessions (active_only=False) → list[Session] List all (or only active) sessions
get_session (session_id) → Session Fetch a single session — raises SessionNotFound if it doesn't exist
is_alive (session_id) → bool Whether the session is currently active on the server
get_messages (session_id) → list[Message] Fetch all messages for a session
list_machines (active_only=False) → list[Machine] List all (or only active) machines
get_machine (machine_id) → Machine Fetch a single machine

Convenience

Method Signature Description
run_task (machine_id, directory, prompt, agent="claude", timeout_seconds=600) → str Spawn, send, wait, stop — returns session ID

Cleanup

Method Signature Description
close () Release any held resources (no-op in the current implementation)

Types

Type Fields
Session id: str, active: bool, created_at: int, metadata: dict (decrypted; the name is metadata["name"]), agent_state: str | None, metadata_version: int
Machine id: str, active: bool, metadata: dict
Message id: str, seq: int, content: dict, created_at: int
Agent Literal["claude"]
PermissionMode Literal["yolo"]

Exceptions

All exceptions inherit from HappyError.

Exception Raised when
AuthenticationError Credentials missing, expired, or malformed
MachineOfflineError Target machine is not connected to the server
SessionNotFound No session with the given id exists on the server
SpawnError Session spawn failed
TimeoutError Wait exceeded the specified timeout
EncryptionError Encrypt or decrypt operation failed
ConnectionError Socket connection failed or disconnected unexpectedly
MetadataUpdateError The server rejected a session metadata update (or returned a malformed ack)
MetadataConflictError A metadata update lost too many optimistic-concurrency races to complete

License

MIT — see LICENSE.

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

happy_engineering_sdk-0.3.0.tar.gz (51.0 kB view details)

Uploaded Source

Built Distribution

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

happy_engineering_sdk-0.3.0-py3-none-any.whl (16.3 kB view details)

Uploaded Python 3

File details

Details for the file happy_engineering_sdk-0.3.0.tar.gz.

File metadata

  • Download URL: happy_engineering_sdk-0.3.0.tar.gz
  • Upload date:
  • Size: 51.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for happy_engineering_sdk-0.3.0.tar.gz
Algorithm Hash digest
SHA256 e9ab2329057bfffbfc3961c11e22eaebfb16a83d58ccecb253f095c1a5367876
MD5 eb3807e1d9470d56362df3c7b8b23b17
BLAKE2b-256 366ba4438dbe76323a0c7144db57e8c88fe013f9871c544a0d9aaaa5fa06f437

See more details on using hashes here.

File details

Details for the file happy_engineering_sdk-0.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for happy_engineering_sdk-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8b426c06c0b1058de5ccbb2b1c261c029e7a0fd007f9e01e93f53458731d8907
MD5 ae93ebcd53c577ca4138958b86e51b2b
BLAKE2b-256 7c8ae2c592eb110db34c89e6ff4e786ac27f10e593a8d06959c414715b913816

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