Skip to content

Repository files navigation

Code Agent SDK for Python

Python 3.10+Code style: blackType Checker: mypy

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.

Other Programming Languages (Beta)

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 for event 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.
  • Rust - async Rust crate with Tokio, typed events, and stream-based runs.
  • C++ - modern C++20 package with CMake targets and typed event callbacks.
  • C# - .NET package with IAsyncEnumerable, CancellationToken, and System.Text.Json.

Features

  • 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
  • Skill discovery - Search the community registry and install skills with typed results
  • MCP discovery - List servers, tools, and server configurations through typed APIs
  • Event streaming - Real-time JSON-RPC notifications from the agent
  • Typed event parsing - Optional Pydantic parsing for known event types
  • Replayable autoresearch - Typed lifecycle, history, replay, rescore, compare, Pareto, pin, and prune APIs
  • 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

Installation

Using uv:

uv add autohand-sdk

Using pip:

pip install autohand-sdk

Quick Start

importasyncioimportosfromautohand_sdkimportAutohandSDKasyncdefmain():
asyncwithAutohandSDK(
cwd=".",
provider="autohandai",
model="fantail",
api_key=os.environ["AUTOHAND_AI_API_KEY"],
) assdk:
asyncforeventinsdk.stream_prompt("Hello, world!"):
ifevent["type"] =="message_update":
print(event.get("delta", ""), end="")
elifevent["type"] =="tool_start":
print(f"\nRunning {event.get('tool_name') orevent.get('toolName')}")
asyncio.run(main())

Skills API

Option A: Skills in Prompt

Configure skills so the agent can reference them via /skill <name>:

sdk=AutohandSDK(
cwd=".",
provider="autohandai",
model="fantail",
api_key=os.environ["AUTOHAND_AI_API_KEY"],
skill_refs=["typescript", "testing", "react"],
)

Option B: Direct Skill Provision

Provide skills directly, including custom skill files:

sdk=AutohandSDK(
cwd=".",
provider="autohandai",
model="fantail",
api_key=os.environ["AUTOHAND_AI_API_KEY"],
skill_refs=[
"typescript", # Built-in"./skills/my-custom/SKILL.md", # Local file
{"name": "api", "path": "/path/SKILL.md"}, # Named skill
],
)

The SDK automatically:

  1. Copies skill files to ~/.autohand/skills/
  2. Extracts skill names from file paths
  3. Passes skill names to CLI via --skills flag

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. Assign sdk.skills only before start(). Changing it while the SDK is running raises RuntimeError; stop the SDK first so the live CLI subprocess remains owned and is not replaced.

Discover and install skills at runtime:

registry=awaitsdk.get_skills_registry(force_refresh=False)
forskillinregistry.skills:
print(skill.name, skill.download_count)
installed=awaitsdk.install_skill("typescript", scope="project")
ifnotinstalled.success:
raiseRuntimeError(installed.error)

MCP discovery uses the same typed style:

servers=awaitsdk.list_mcp_servers()
tools=awaitsdk.list_mcp_tools(server_name="github")
configs=awaitsdk.get_mcp_server_configs()

Configuration

importosfromautohand_sdkimportAutohandSDKsdk=AutohandSDK(
cwd=".", # Working directoryprovider="autohandai", # Provider to usemodel="fantail", # Model to useapi_key=os.environ["AUTOHAND_AI_API_KEY"],
temperature=0.7, # Sampling temperaturemax_iterations=10, # Max iterations in auto-modestartup_check=True, # Probe CLI readiness on startdebug=True, # Enable debug loggingunrestricted=True, # Unrestricted modeauto_mode=True, # Enable auto-modepermission_mode="default", # CLI permission modeenv_vars={"AUTOHAND_NO_BANNER": "1"},
)

API Reference

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.
  • docs/autoresearch.md - replayable /autoresearch lifecycle and experiment ledger.

Replayable Autoresearch

The Python SDK exposes the same typed JSON-RPC autoresearch contract as the TypeScript v1.0.3 SDK. Start or resume a session, execute the returned loop instruction through the normal event stream, then inspect or replay its ledger:

asyncwithAutohandSDK(cwd=".", unrestricted=True) assdk:
started=awaitsdk.start_autoresearch(
objective="Reduce test latency without changing behavior",
max_iterations=10,
metric_name="test_ms",
metric_unit="ms",
direction="lower",
measure_command="uv run pytest",
files_in_scope=["src", "tests"],
)
ifnotstarted.success:
raiseRuntimeError(started.erroror"autoresearch could not start")
ifstarted.instruction:
asyncforeventinsdk.stream_prompt(started.instruction):
ifevent["type"] =="autoresearch":
print(event)
history=awaitsdk.get_autoresearch_history()
pareto=awaitsdk.get_autoresearch_pareto()
awaitsdk.prune_autoresearch(dry_run=True)
awaitsdk.stop_autoresearch()

See docs/autoresearch.md and docs/examples/27-autoresearch-ledger.py.

AutohandSDK

Main SDK class for interacting with the Autohand CLI.

Methods

  • start() - Start the SDK and connect to CLI
  • stop() / close() - Stop the SDK and cleanup
  • stream_prompt(message, **kwargs) - Send a prompt and stream events
  • abort(reason=None) - Abort current operation
  • get_state() - Get current agent state
  • get_messages(limit=None, before=None) - Get messages
  • get_skills_registry(force_refresh=None) / install_skill(...) - Discover and install skills
  • list_mcp_servers() / list_mcp_tools(...) / get_mcp_server_configs() - Inspect MCP integrations
  • set_plan_mode(enabled) - Change plan mode for the live session
  • set_model(model) - Change the model
  • set_temperature(temperature) - Set temperature
  • get_account_info() - Get account information
  • start_autoresearch(...) / get_autoresearch_status() / stop_autoresearch() - Manage the persisted experiment loop
  • get_autoresearch_history() / replay_autoresearch(...) / rescore_autoresearch(...) - Inspect and re-evaluate ledger entries
  • compare_autoresearch(...) / get_autoresearch_pareto() - Analyze candidate quality
  • pin_autoresearch(...) / prune_autoresearch(...) - Manage retained candidate artifacts

Prompt RPC responses are acknowledgements, so stream_prompt() stays open through the terminal agent_end notification. Closing an iterator early sends an abort and drains that turn; a CLI that cannot settle within two seconds is retired before another prompt can use the transport. Event queues are bounded at 1,024 entries per consumer.

Startup performance gate

The repository gates cold public import, public start(), and fixture spawn-to-first-getState p95 latency below 50 ms. Interpreter boot is excluded from the import metric; provider/network readiness is environment-dependent. The command emits stable JSON with top-level language, budgetMs, metrics, and passed. Each metric contains samples, medianMs, p95Ms, maxMs, and its own passed result.

uv run python benchmarks/startup.py

Properties

  • config - Current SDK configuration
  • skills - Get/set skill references before start()

Events

The SDK emits events during agent execution:

  • agent_start - Agent started
  • turn_start / turn_end - Prompt turn lifecycle
  • message_start - Assistant message started
  • message_update - New token/chunk
  • message_end - Message complete
  • tool_start - Tool execution started
  • tool_update - Streaming tool output
  • tool_end - Tool execution completed
  • permission_request - Permission needed
  • file_modified - File mutation hook notification
  • autoresearch - Lifecycle or replay-ledger operation notification
  • agent_end - Agent finished
  • error - 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:

fromautohand_sdkimportparse_sdk_eventasyncforraw_eventinsdk.stream_prompt("Hello"):
event=parse_sdk_event(raw_event)
ifnotisinstance(event, dict) andevent.type=="message_update":
print(event.deltaor"", end="")

Errors

Transport and RPC failures raise SDK-specific exceptions:

fromautohand_sdkimportRPCError, RequestTimeoutError, TransportNotStartedError
  • TransportNotStartedError - request attempted before start()
  • RequestTimeoutError - request exceeded timeout
  • RPCError - CLI returned a JSON-RPC error response; exposes code and data

Development

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 .

License

MIT License - see LICENSE file for details.

About

Autohand Code Agent SDK for Python: async CLI-backed agent orchestration with streaming events, typed models, and examples.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages