Skip to main content

Yoke

yoke (yōk), n. A harness that joins two, that they may pull as one. [akin to Skr. yoga, union.]

Python 3.11+ Typed Claude harness Codex harness License: Apache-2.0

A Python SDK for building agents on Claude Code and Codex.

Claude Code and Codex have become general-purpose agents: give them instructions, skills, and subagents, and they can be shaped to any task. Yoke lets you reuse them from code — one Harness that drives both.

Quickstart · How it compares · Sessions · Skills · Subagents · Workflows · Goals · Surfaces · CLI

Quickstart

pip install almanac-yoke

Install a provider extra when you want Yoke to manage that SDK directly:

pip install 'almanac-yoke[claude]'  # or [codex], or [all]

Define an agent, pick a harness, run:

from pathlib import Path

from yoke import Agent, Goal, Harness

agent = Agent(
    instructions="You are a careful maintainer. Make small, safe changes.",
    goal=Goal("Finish the requested implementation safely."),
)
harness = Harness("codex", agent=agent, cwd=Path.cwd())

result = await harness.run("Implement the bundle loader.")
print(result.output)

Swap "codex" for "claude" and the same agent runs there. Your existing Claude Code or ChatGPT login is all it needs — no API keys.

Embedding applications can observe a one-shot run while it is happening:

from yoke import RunOptions

seen = []
result = await harness.run(
    "Implement the bundle loader.",
    RunOptions(on_event=seen.append),
)

assert tuple(seen) == result.events

on_event is a synchronous callback receiving each normalized Event once. It is runtime-only—it is excluded from serialized options and agent folders. Live callback delivery is supported by the Claude Python SDK and Codex app-server surfaces. Passing on_event selects one of those surfaces when the surface is automatic, and raises UnsupportedFeature when an explicitly selected surface cannot deliver callbacks. Use harness.stream(...) for a portable event iterator.

How it compares

The question that places Yoke: who runs the agent?

The agent runs in Yoke
Claude Agent SDK · Codex SDK the lab's harness — one provider each builds on them. They are the surfaces Yoke drives; one definition runs on both.
Pydantic AI · LangChain your process — a loop you assemble over model APIs starts from a different premise: the harness already is the agent, so there is no loop to assemble.
Eve Eve's own durable runtime, deployed on Vercel is closest in spirit — an agent is a directory — but compiles that directory onto Claude and Codex instead of shipping a runtime.

Sessions

session = await harness.start()

plan = await session.run("Draft the migration plan.")
step = await session.run("Apply step one.")

await session.close()

State lives with the provider; Yoke holds the handle. Sessions are native on every surface except the Codex CLI, where they work by resuming threads.

Skills

from yoke import Skill

agent = Agent(
    instructions="You are a careful maintainer.",
    skills=(Skill(path=Path("skills/source-grounding")),),
)

A skill is a folder with a SKILL.md. Claude loads skills natively, the Codex app-server mounts them as native skill roots, and other surfaces get them compiled to files.

Subagents

agent = Agent(
    instructions="You are a careful maintainer.",
    subagents={
        "reviewer": Agent(
            description="Find correctness and architecture risks.",
            instructions="Review concretely. Prefer file and line evidence.",
        ),
    },
)

A subagent is just another Agent. Claude runs declared subagents through its native Agent tool. Codex app-server derives temporary custom-agent TOML and the parent selects it with spawn_agent(agent_type=..., fork_turns="none"). Yoke does not silently use a generic child: an incompatible Codex model/backend fails honestly. Codex SDK/CLI surfaces retain their documented lowerings.

Runtime files are derived outside cwd and removed when the session closes. Set Harness(runtime_root=...) to choose their parent cache directory. This is different from agent.bundle(...).write(...), which explicitly exports durable provider files for a project. A later deployment reclaims runtime directories whose owning process exited before normal cleanup.

Workflows

from yoke import Step, Workflow, WorkflowOptions

workflow = Workflow(
    name="review",
    steps=(
        Step(name="draft", agent="main", prompt="Draft release notes."),
        Step(
            name="review",
            agent="reviewer",
            depends_on=("draft",),
            prompt="Review this draft: {draft}",
        ),
    ),
)

result = await harness.workflow(
    workflow,
    options=WorkflowOptions(timeout_seconds=120, step_timeout_seconds=60),
)

A workflow is a small dependency graph over the agent and its subagents; main is the reserved name for the root agent, and WorkflowOptions bounds the run. Neither provider has a native equivalent yet, so workflows are portable Yoke constructs — they run the same on every surface.

Goals

from yoke import Goal

session = await session.set_goal(Goal("Land the bundle loader.", token_budget=200_000))
print(await session.get_goal())

A goal is intent that outlives a single prompt. On the Codex app-server it is real thread state — readable, replaceable, clearable. Everywhere else it compiles into the provider loop, and explain() tells you which you got.

No pretending

Claude and Codex expose different primitives, and Yoke does not flatten them into a weak common denominator. The capability map is part of the API:

for row in harness.explain().reports:
    print(row.feature, row.support, row.lowering)  # native, compiled, emulated, unsupported
Feature Claude SDK Codex app-server Codex SDK Codex CLI
Sessions native native native resume-based
Streaming native native native transport JSONL/process
Skills native native skill roots compiled files/compiled
Subagents native compiled → native tool compiled files/compiled
Goals provider loop native state compiled context provider loop
Workflows portable Yoke portable Yoke portable Yoke portable/limited
Permissions/hooks native callbacks native request events sandbox/approval flags/config

Agents are folders

Everything defined in Python can be saved as files, edited by hand, and loaded back:

agent.save("agent")
agent = Agent.from_folder("agent")
agent/
  agent.yaml
  instructions.md
  skills/
    source-grounding/SKILL.md
  subagents/
    reviewer/
  workflows/
    ship/

Provider files like .claude/ and .codex/ are compiled from this source, only when you ask:

agent.bundle(provider="codex", surface="codex_cli").write(Path.cwd())

Surfaces

This is the deeper layer:

agent definition -> provider surface -> real harness

Each provider ships more than one way in. Harness("codex") picks the strongest one for you (the app-server); address a surface directly when you require an exact one:

Surface What it is
codex:app Codex app-server — sessions, native goals, skill roots
codex:sdk Codex Python SDK
codex:cli Codex CLI — codex exec, resumable threads
claude:sdk Claude Agent SDK for Python

discover reports what this machine already has — surfaces installed, logins ready, models available — and picks the first ready surface satisfying the requested features:

from yoke import Feature, discover

found = await discover("codex", Path.cwd(), agent)  # reuses your local login

for surface in found.surfaces:
    print(surface.surface, [model.id for model in surface.models])

harness = found.harness(Feature.STREAMING)

Claude also accepts runtime-only Credentials (redacted, never serialized); Codex logins persist provider state, so they go through an explicit await harness.login(...).

CLI

The same agents and folders, from the shell:

yoke run agents codealmanac "Review this repo"
yoke explain agents codealmanac
yoke status agents codealmanac
yoke install agents codealmanac --provider codex:cli
yoke runs

CLI runs leave inspectable snapshots under .yoke/runs/. SDK users can persist returned results explicitly with RunStore.at(".yoke/runs").record(result).

Status

Yoke is an early alpha. Everything shown above is built and smoke-tested against live providers. The API may still change before 1.0; durable workflow execution and typed coverage of every provider-specific option remain future work.

Apache-2.0. See LICENSE.md.

Metadata

Release files for almanac-yoke 0.1.7

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

Source distribution (sdist)

Source distribution for almanac-yoke 0.1.7
File Size Uploaded
almanac_yoke-0.1.7.tar.gz 118.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for almanac-yoke 0.1.7
File Interpreter ABI Platform
almanac_yoke-0.1.7-py3-none-any.whl Python 3 none any Details

Total release size: 257.1 kB

Release files / almanac_yoke-0.1.7.tar.gz

Download URL almanac_yoke-0.1.7.tar.gz
Size 118.7 kB
Tags Source
SHA-256 checksum
How to use checksums
6876e41dbef580d99f7c2e2f1e4d1d244a620c1d8dab76f2aa5097af09a04410
BLAKE2b-256 checksum
How to use checksums
85d64fb29bc1dff1fc41029caee56b7072c9ec1f3af28fc72ad2fc7ea59f446b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 Jul 11, 2026.

Transparency log

Release files / almanac_yoke-0.1.7-py3-none-any.whl

Download URL almanac_yoke-0.1.7-py3-none-any.whl
Size 138.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
58547b5954dd10f582df6cad6caff6631ee8afb2c9db100042068893ad64b8d8
BLAKE2b-256 checksum
How to use checksums
42eaea68b5adc3b87b25c6eac80cf10c16dd4a3193bfec91674f0a75ee902b7e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 Jul 11, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.7 This release

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

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