Skip to main content
The harness needs exactly three touchpoints with your loop, whatever it is built with: the system context in the prompt, the toolset among the tools, and a turn-end signal. No adapter required.
Harness.create() provisions the workspace, wires every component (hook, mailbox, journal, rules, eval set, toolset, restricted shell), and schedules the startup health check. It accepts an explicit HarnessConfig and, for the closed loop, your replay function:

1. The system context

Rebuild it each turn if you can — the mailbox banner and the retrieved rules are current-state. Pass a task hint to sharpen rule retrieval:
For static-prompt setups, the standing protocol’s “check your mailbox at the start of each turn” instruction is the trigger instead of the banner.

2. The tools

as_langchain_tools and as_openai_function_tools cover the other native formats, and the pandaprobe-harness-agent companion CLI covers sandboxed shells — see the toolset.

3. The turn boundary

Three equivalent styles — pick whichever fits your loop:
Or call the hook directly with a raw payload — the end_state you pass is what a captured eval case will carry as its replay input:
Use the same session id for the harness turn and the SDK trace context (pandaprobe.session(session_id)) — the evaluation scores whatever traces landed under that session.

Determinism helpers

Everything after on_turn_end is detached and non-blocking. When a test or script needs to observe results deterministically:
These are bounded waits (drain_timeout_s) for callers’ convenience — correctness never depends on them; each background task handles its own result.

Everything on the facade