Skip to main content

poly-harness-dispatcher (PHD)

CI PyPI Python License

Spawn headless AI coding agents (Claude Code claude -p, Codex codex exec, OpenCode opencode run) from one harness-neutral spec, and get back one normalized result.

You describe the agent once as a stack (identity, skills, MCP servers, hooks in a config/ directory) and the run as a spec (harness, model, working directory, credentials, options). PHD builds an isolated config home per run, launches the CLI, parses its stream into events and a RunResult, and records everything under ~/.phd/runs/<run_id>/.

Status: layer 1 (the core spawner) is built. It runs agents on the host with no sandbox; sandboxing, an API and other components are planned (architecture).

Install

Python 3.12+; the harness CLIs you use must be on PATH.

pip install poly-harness-dispatcher    # import name: phd

From a clone (development, with uv):

uv sync --extra dev        # or: uv venv && uv pip install -e ".[dev]"
uv run pytest -q           # offline test suite

Quickstart

from phd import Credentials, Dispatcher, Inputs, Spec, Stack

spec = Spec(
    harness="claude",                          # or "codex", "opencode"
    model="haiku",
    stack=Stack.load("tests/fixtures/stack"),  # the bundled test stack
    cwd="path/to/repo",
    credentials=Credentials.from_host("claude"),
    secrets={"ECHO_PREFIX": "phd-echo"},       # fills ${env:ECHO_PREFIX} in the stack
)
d = Dispatcher(spec)
result = d.spawn(Inputs(msg="List the names of your skills."))
print(result.status, result.cost_usd, result.result_text)
followup = d.spawn(Inputs(msg="Which one has a scripts/ folder?"), session_id=result.session_id)

To compose a stack from several sources (a base stack plus extra skills, MCP servers or hooks, local or https://) and keep it in a local store, use phd.stacks:

import phd.stacks as stacks

stack = stacks.resolve(stacks.compose("reviewer", "1.0.0", base="tests/fixtures/stack",
                                      skills=["path/to/extra-skill"]))
spec = Spec(harness="claude", model="haiku", stack=stack, cwd="path/to/repo",
            credentials=Credentials.from_host("claude"))

In async code, d.run(...) ties the run to a block: leaving it cancels a run that has not finished, and a harness whose caller dies is killed too (Windows Job Object, POSIX reaper).

async with d.run(Inputs(msg="List the names of your skills.")) as handle:
    async for ev in handle.events():
        print(ev.type)                  # run.started, session.started, message, ..., run.finished
    result = await handle.wait()

session_id= continues that harness session (the home is re-materialized from the current spec). spawn() blocks; in Jupyter or async code use await d.start(inputs), then async for ev in handle.events() and await handle.wait() (see examples/quickstart.ipynb). A failed run does not raise: check result.status and result.error. Problems detectable before launch raise a PhdError.

On macOS the Claude login lives in the Keychain: use Credentials.claude_oauth_token(token) with a token from claude setup-token instead of from_host("claude").

Documentation

Build the docs site locally: uv sync --all-extras && uv run mkdocs serve.

Metadata

Release files for poly-harness-dispatcher 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for poly-harness-dispatcher 0.1.0
File Size Uploaded
poly_harness_dispatcher-0.1.0.tar.gz 333.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for poly-harness-dispatcher 0.1.0
File Interpreter ABI Platform
poly_harness_dispatcher-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 453.6 kB

Release files / poly_harness_dispatcher-0.1.0.tar.gz

Download URL poly_harness_dispatcher-0.1.0.tar.gz
Size 333.0 kB
Tags Source
SHA-256 checksum
How to use checksums
048767a945782d29b82b3992e1b8cde4dab05ede2432b7ed5619f1493a086d07
BLAKE2b-256 checksum
How to use checksums
2ae8ee5d2a7e7f673f1ea27b7e12274a4cb5a2ff564742b7fd123b394fa029cf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release files / poly_harness_dispatcher-0.1.0-py3-none-any.whl

Download URL poly_harness_dispatcher-0.1.0-py3-none-any.whl
Size 120.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a80a6d18afbd1e03b65ddfd212a14d12236927b7d9791b888440a374fb7cbd28
BLAKE2b-256 checksum
How to use checksums
a53c67975aa83a218068c8db8c6b50f222dff51bc013808bbdf67af7fee0d254
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release history Release notifications | RSS feed

0.1.1

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page