A production-grade Python SDK for the Autohand CLI, enabling programmatic control of code agents through a high-level async API over the CLI's JSON-RPC mode.
Beta: this SDK is actively evolving while the Agent SDK APIs stabilize. Pin versions in production and review release notes before upgrading.
The Agent SDK is available in multiple beta language packages. Use the same CLI-backed SDK model from another programming language:
- TypeScript -
Agent,Run, streaming, and JSON helpers for Node and Bun hosts. - Go - idiomatic Go package with
context.Context, typed events, and channel-based streaming. - Python - this package, with
asyncio,async forevent streams, and typed Pydantic models. - Java - Java 21 records, sealed events, and virtual-thread-ready APIs.
- Swift - SwiftPM package with
Agent,Runner, async streams, tools, hooks, and permissions.
- Async/await native - First-class support for async programming
- Type-safe - Full type hints with Pydantic models
- Skill support - Configure skills by name or file path
- Event streaming - Real-time JSON-RPC notifications from the agent
- Typed event parsing - Optional Pydantic parsing for known event types
- Automatic cleanup - Context manager support for resource management
- Production transport - Response matching, notification routing, timeouts, and typed RPC errors
- 90%+ test coverage - Subprocess-backed transport and streaming tests
Using uv:
uv add autohand-sdkUsing pip:
pip install autohand-sdkimport asyncio
from autohand_sdk import AutohandSDK
async def main():
async with AutohandSDK(
cwd=".",
model="fantail2",
) as sdk:
async for event in sdk.stream_prompt("Hello, world!"):
if event["type"] == "message_update":
print(event.get("delta", ""), end="")
elif event["type"] == "tool_start":
print(f"\nRunning {event.get('tool_name') or event.get('toolName')}")
asyncio.run(main())Configure skills so the agent can reference them via /skill <name>:
sdk = AutohandSDK(
cwd=".",
model="fantail2",
skill_refs=["typescript", "testing", "react"],
)Provide skills directly, including custom skill files:
sdk = AutohandSDK(
cwd=".",
model="fantail2",
skill_refs=[
"typescript", # Built-in
"./skills/my-custom/SKILL.md", # Local file
{"name": "api", "path": "/path/SKILL.md"}, # Named skill
],
)The SDK automatically:
- Copies skill files to
~/.autohand/skills/ - Extracts skill names from file paths
- Passes skill names to CLI via
--skillsflag
Set copy_skill_files=False if you only want to activate skills that are
already installed and do not want startup to write into ~/.autohand/skills.
from autohand_sdk import AutohandSDK
sdk = AutohandSDK(
cwd=".", # Working directory
model="fantail2", # Model to use
temperature=0.7, # Sampling temperature
max_iterations=10, # Max iterations in auto-mode
startup_check=True, # Probe CLI readiness on start
debug=True, # Enable debug logging
unrestricted=True, # Unrestricted mode
auto_mode=True, # Enable auto-mode
permission_mode="default", # CLI permission mode
env_vars={"AUTOHAND_NO_BANNER": "1"},
)Full guides live in docs/:
docs/GETTING_STARTED.md- install, authenticate, configure providers, and run the first prompt.docs/API_REFERENCE.md- constructor, lifecycle, prompts, events, permissions, and config models.docs/configuration.md- provider, execution, skills, context, session, and environment options.docs/event-streaming.md- event shapes and streaming patterns.docs/error-handling.md- transport, RPC, timeout, abort, and recovery patterns.docs/advanced-patterns.md- system prompts, sessions, model switching, JSON output, and AGENTS.md.docs/permissions.md- permission modes and programmatic approval.docs/plan-mode.md- read-only planning and gated implementation.docs/memory.md- CLI memory behavior through Python event streams.docs/sdlc-workflows.md- discovery, gated implementation, and release-readiness flows.
Main SDK class for interacting with the Autohand CLI.
start()- Start the SDK and connect to CLIstop()/close()- Stop the SDK and cleanupstream_prompt(message, **kwargs)- Send a prompt and stream eventsabort(reason=None)- Abort current operationget_state()- Get current agent stateget_messages(limit=None, before=None)- Get messagesset_model(model)- Change the modelset_temperature(temperature)- Set temperatureget_account_info()- Get account information
config- Current SDK configurationskills- Get/set skill references
The SDK emits events during agent execution:
agent_start- Agent startedturn_start/turn_end- Prompt turn lifecyclemessage_start- Assistant message startedmessage_update- New token/chunkmessage_end- Message completetool_start- Tool execution startedtool_update- Streaming tool outputtool_end- Tool execution completedpermission_request- Permission neededfile_modified- File mutation hook notificationagent_end- Agent finishederror- Error occurred
Events are dictionaries. Known camelCase RPC keys are also exposed with Python-friendly snake_case aliases, so toolName is available as tool_name, requestId as request_id, and so on.
For typed handling, parse known event dictionaries into Pydantic models:
from autohand_sdk import parse_sdk_event
async for raw_event in sdk.stream_prompt("Hello"):
event = parse_sdk_event(raw_event)
if not isinstance(event, dict) and event.type == "message_update":
print(event.delta or "", end="")Transport and RPC failures raise SDK-specific exceptions:
from autohand_sdk import RPCError, RequestTimeoutError, TransportNotStartedErrorTransportNotStartedError- request attempted beforestart()RequestTimeoutError- request exceededtimeoutRPCError- CLI returned a JSON-RPC error response; exposescodeanddata
Using uv for dependency management:
# Install dependencies
uv sync
# Run tests with coverage
uv run pytest
# Run type checking
uv run mypy src
# Run linting
uv run ruff check .
# Format code
uv run ruff format .MIT License - see LICENSE file for details.