SDK
Overview
The mental model behind the SDK, the objects you'll use, and how Python and TypeScript map to each other.
The core objects
The SDK is small on purpose. Almost everything starts from one client:
Harnext— the agent client. Holds a provider, model, working directory, tools, and permission policy. Callrun()orstream().RunResult— the outcome of a run: final text, ordered tool steps, token usage, changed files, and the session id.Session— a persisted, replayable transcript of a run. List, load, resume, export.tool()— wrap any function as a typed tool the agent can call.HarnessandRunner— generate the GitHub Actions pipeline and manage a self-hosted runner from code.
The agent loop
When you call run(), the client starts a loop: the model receives your prompt and the available tool schemas, decides whether to respond or call a tool, the SDK executes the tool against cwd, feeds the result back, and repeats. The loop ends when the model returns a final answer with no tool call, or when max_turns is reached. stream() surfaces every step of that loop as it happens.
from harnext import Harnext
agent = Harnext(
provider="anthropic",
model="claude-opus-4-8",
cwd=".",
tools=["read", "write", "edit", "bash"],
permission_mode="auto",
max_turns=40,
)
result = agent.run("Fix the failing test in tests/test_users.py")
for step in result.steps:
print(step.tool, step.status)Python ↔ TypeScript
The two SDKs are intentionally symmetric. The only routine differences:
- Naming: Python is
snake_case, TypeScript iscamelCase(e.g.max_turns↔maxTurns,files_changed↔filesChanged). - Async: every TypeScript method returns a
Promise. Python methods are synchronous by default, witha-prefixed async variants (arun,astream) forasyncio. - Streaming: Python yields from a generator; TypeScript exposes an
AsyncIterableyou consume withfor await … of.
Async in Python
Use await agent.arun(...) and async for event in agent.astream(...) inside an asyncio event loop. The synchronous methods are thin wrappers around these.