A zero-dependency, self-adjusting Python agent harness: LangGraph-style state-machine orchestration plus LangChain-style composability, implemented entirely on the standard library. At startup it ingests the project it lives in (metadata, directory structure, goal), asks Fable 5 to design its own execution graph — nodes, edges, routing rules, and a tailored tool selection — then executes that graph under a hard cost cap with full live observability.
harness/
engine.py state-machine executor, dynamic graph topology, cost gateway
prompts.py project-context ingestion + self-bootstrapping architect contracts
dashboard.py local observability server (stdlib HTTP + SSE) with embedded UI
tools.py modular tool registry with sandboxed execution
jsonspec.py minimal JSON-schema validator / extractor / fabricator
__main__.py CLI: python -m harness
tests/
smoke_test.py offline end-to-end tests (mock transport)
- Internal component. The harness is a plain package inside your
codebase. Graphs are native Python objects (
Graph,Node) executed byEngine; you can hand-build them fluently or let the model generate them. - Context-based self-adjustment.
ProjectContext.gather()snapshots the repo (manifests, tree, language mix); the architect prompt turns that plus your goal into a JSON graph spec — validated, compiled, and run. Invalid specs get bounded repair rounds; if the architect still fails, a stock fallback graph boots so the run never dies at startup. - Observability dashboard.
python -m harness --openserves a live page (127.0.0.1 only): the execution graph as SVG with per-node status, agent execution paths, active sub-agents, token/cost totals with a budget meter, step-latency chart, per-node usage table, and the full event log — streamed over SSE. Implemented withhttp.server, honoring the zero-dependency constraint (the HTTP layer is isolated indashboard.py; swapping in FastAPI is a drop-in change if you prefer it). - Fail-safe & cost gateway. Every model call passes
ModelGateway: budget preflight + hard USD cap shared across sub-agents, transport retries withretry-afterrespect, refusal handling with server-side fallback to Opus, and structured-JSON verification gates. A step that fails validation N times (default 3) routes the run to a manual intervention state: the dashboard shows the question and the operator answers (retry / skip / raise budget / abort). Tools run under a sandbox policy: reads confined to the project root, writes confined to the run workspace, subprocesses isolated (python -I, scrubbed env, hard timeouts), network deny-by-default.
No install needed — it runs in place on Python 3.10+ (3.11+ enables
pyproject.toml name sniffing via tomllib).
# Offline demo: full pipeline incl. self-generated graph, sub-agent, dashboard
python -m harness --mock --open
# Real run against Fable 5 (set ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN)
python -m harness --goal "Find and rank the top risks in this repo" --budget-usd 3 --open
# Tests
python tests/smoke_test.pyUseful flags: --budget-usd, --model, --effort low|medium|high|xhigh|max,
--fallback-model none, --allow-network, --max-steps, --human-timeout,
--port, --no-dashboard, --exit-when-done, --print-graph (bootstrap
only, emit Mermaid, exit), --resume <run-id>, -v.
Every run writes a full trace to .harness_runs/<run-id>/: events.jsonl,
graph.json (topology + Mermaid), spec.json (the resumable graph spec),
checkpoint.json, state.json, report.json, meta.json, summary.md,
plus the sandboxed workspace/ the run's write-tools used.
The engine checkpoints state and position before every step. If a manual intervention times out with no operator present, the run parks as suspended instead of failing — resume it later and the operator gate reopens exactly where it left off, with prior spend counted against the same budget cap:
python -m harness --resume 20260706-153000-a1b2c3 # or a path to the run dirProgrammatically: Harness.resume(run_dir) (pass graph= for hand-built
graphs that wrap Python callables — declarative graphs round-trip from the
persisted spec automatically). Crashed, failed, and aborted runs resume the
same way; a resumed run re-enters the checkpointed node with fresh
step/loop counters.
from harness import Graph, Harness, HarnessConfig, Node
# Self-adjusting path: the model designs the graph
harness = Harness("summarize this codebase", root=".",
config=HarnessConfig(budget_usd=2.0))
report = harness.run()
print(report.status, report.summary)
# Hand-built path: LangGraph-style explicit state machine
graph = (
Graph("review")
.add(Node(name="ask", kind="human",
config={"question": "Ship it?", "options": ["approve", "abort"]}))
.add(Node(name="done", kind="terminal", config={"summary_from": "human_decision"}))
.add_edge("ask", "done")
.set_entry("ask")
)
Harness("gated release", graph=graph, config=HarnessConfig(mock=True)).run()Node kinds: llm (one structured call), agent (model+tool loop), tool
(deterministic call), router (state-driven branch), parallel (concurrent
fan-out with reducer merge), subgraph (nested sub-agent with its own graph),
human (operator gate), terminal, and func (any Python callable —
state -> delta — for hand-built pipelines). State channels support
replace / append / merge / add reducers.
LangChain-style composition drops into func nodes via Pipe:
from harness import pipe
enrich = pipe(load_meta, score) | summarize # each stage: state -> delta
graph.add_node("enrich", enrich) # later stages see earlier deltasGraph.to_mermaid() renders any graph as a Mermaid flowchart (also saved
into every run's graph.json; preview a goal's generated design with
python -m harness --mock --print-graph).
- Model calls hit
POST /v1/messagesdirectly overurllib(the zero-dependency constraint rules out the official SDK; the transport is one class, so migrating toanthropiclater is trivial). Fable 5 rules are honored: nothinkingparam (always on), no sampling params, refusalstop_reasonhandled,fallbacks+ beta header sent by default so a safety-classifier false positive degrades to Opus 4.8 instead of failing. - Structured output uses forced
tool_choice(emittool) so arbitrary JSON-schema contracts work; the local validator is the enforcement gate, with repair feedback sent back as an erroredtool_result. - Cost is computed from response
usage(cache-aware) against a pricing table; the preflight projects worst-case spend before each call, so the cap cannot be blown mid-flight. The cap is shared by all sub-agents. - Mock transport (
--mock) deterministically exercises the whole system offline — architect, agent tool-loops, sub-agent spawn, review/route, dashboard — with zero credentials. - The dashboard binds loopback only; mutating endpoints require a per-session token injected into the page, so other local web pages cannot drive a run.