Python SDK for controlling Happy agent sessions
Project description
happy-engineering-sdk
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) → str |
Create a new agent session — returns the session ID |
stop_session |
(session_id) |
Stop a running session |
delete_session |
(session_id) |
Permanently delete a session |
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, agent_state: str | None |
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 |
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file happy_engineering_sdk-0.2.1.tar.gz.
File metadata
- Download URL: happy_engineering_sdk-0.2.1.tar.gz
- Upload date:
- Size: 38.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
53f31b09a4b7c1804b4d7e146e78cc14d553413513ef7d81a28681601b2307ca
|
|
| MD5 |
34e5bef8f8a710e02ef5300ff5deab58
|
|
| BLAKE2b-256 |
faf1da6085fa39aaecca3b7d8018ece1029dd6197ab5858ee2d93a31339c3af4
|
File details
Details for the file happy_engineering_sdk-0.2.1-py3-none-any.whl.
File metadata
- Download URL: happy_engineering_sdk-0.2.1-py3-none-any.whl
- Upload date:
- Size: 13.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
37bb3c0ed0d6923700946eaddf685cfc0a8d40e23aecd880eb471f7f88b58cad
|
|
| MD5 |
7cb79841b532c00dfeecf92e3b506a29
|
|
| BLAKE2b-256 |
17335efcf4842033c048753989b5de99331d9394e4d338c48092f4aeeb2ff71b
|