detect → steer
Steer the coding agent you already run, on the subscription you already pay for.
A harness is two planes: control — what the agent does next — and capability — what it can reach. Every runtime already exposes both, as hooks and as MCP. Open Harness puts the steering brain server-side and rides in through those two seams, on any host. Advisory-only and content-free — it never carries your model traffic.
PostToolUse Grep {"pattern": "registerRoute"} ✓ PostToolUse Grep {"pattern": "registerRoute"} signature repeat x2 PostToolUse Grep {"pattern": "registerRoute"} signature repeat x3 tool_loop fired threshold 3 → template context_note_v1 inject "Open Harness: tool loop detected: Grep has been called 3 times in a row with identical arguments... vary the arguments." PostToolUse Read src/router.ts signature changed — loop broken ledger #4821 fire recorded · params hash only, no transcript
The control plane
The session's own hook points stream events out; verdicts come back and steer the trajectory. What to do next, when to redirect, when to stop.
The capability plane
One aggregated endpoint fans in the tool suite server-side, behind one auth and one meter. What the agent can reach.
A harness anywhere
A harness stops being an app you switch to and becomes a layer you attach — any host that speaks hooks and MCP. The model leg is never touched.
What it is
Your agent. Your subscription. One steering layer between them — that never touches the model leg.
You already pay for inference and you already run a harness. Open Harness adds the missing piece: a backend that watches the session's trajectory and corrects it in flight. It attaches through the two extension points every modern harness ships — nothing else is installed, and the model leg is never intercepted. Routing advice is applied by your harness through its own mechanisms.
01 A thin hook shim — the control surface
One dependency-free script your harness's own hook mechanism invokes on UserPromptSubmit, PreToolUse, PostToolUse, and Stop. Events go out, verdicts come back, and the shim renders them from its local template library. Backend unreachable? The shim exits as an identity hook — your agent never depends on Open Harness being up.
02 One MCP endpoint — the capability surface
A single streamable-HTTP server that fans in the whole tool suite server-side, behind one auth and one meter. The client contract stays small and stable; the detectors, composer, consent, and ledger live in the backend and iterate without you reinstalling anything.
The steering brain is server-side: deterministic detectors run over canonical session events, and verdicts degrade to what your harness can express. Design of record: the whitepaper.
Detectors
Your agent loops, thrashes, stalls, pads, and quits early. Something should notice.
Five detectors ship today. Every one is a pure state machine over hook events — signature compares, counters, and overlap ratios. No model reads your session to decide an intervention, and injected notes are composed only from a fixed template library.
tool_loop detector
Same tool, identical canonical arguments, N times in a row. A context note names the loop and asks for a different approach.
edit_thrash detector
The same file edited back and forth, each edit reversing the last. The note says: re-read, decide, then edit once.
progress_stall detector
A window of tool results that are all repeats of results the session has already seen — activity without a new outcome.
premature_stop detector
A Stop while the session's own task state still shows open work. In autonomous mode, the Stop hook re-drives the session.
filler_inflation detector
Consecutive replies that restate each other with no tool activity between them — words accumulating without action.
composer templates only
Verdicts carry a template_id plus scalar params; the shim renders them locally. No free-form generation, ever.
Every fire is idempotency-keyed (one decision per episode), ledgered append-only, and excluded from detector inputs — Open Harness never counts its own notes as filler.
Consent
Four tiers. Nothing intervenes without opt-in.
Consent gates what the backend may emit, never what it can see: detectors always run, and a suppressed intervention is ledgered exactly like an emitted one. Dropping back to observe is one config change.
1 observe default
Digest and signals only. Every verdict is allow; fired detectors are recorded as suppressed. Watch the ledger before you let it steer.
2 annotate
Context notes: tool-loop, edit-thrash, progress-stall, and filler notes ride the next prompt as attributed additional context.
3 steer
Annotate plus steering turns and reroute hints — still template-composed, still attributed, still ledgered.
4 autonomous
Steer plus Stop-hook force-continuation: a premature stop with open work is re-driven with a continuation template.
A configurable holdout fraction pins steer/autonomous sessions to observe as a permanent A/B control. No outcome numbers appear on this page until the pooled floor is met.
Content-free
Sees the session's shape. Keeps none of its words.
never your model traffic
Routing is advisory only, by design, not configuration. Model requests terminate at your own endpoints under your own auth — Open Harness is not in that path.
digests, not transcripts
The ledger stores signal names, template ids, and a hash of the parameters — never prompt or tool-result text. Metrics are counts and durations with hashed principals.
no path from content to steering text
Detectors emit booleans and counts; the composer speaks only from the fixed template library. A poisoned tool result cannot write the words that steer you.
fail-open, measured
Missing key, backend down, timeout, bad verdict — the shim exits 0 with empty output, an identity hook. Its own overhead is instrumented and test-asserted under 50ms per event.
every write is attributed
Injected turns carry an explicit Open Harness prefix and a ledger id. You can diff every intervention against your own transcript.
Quickstart
Three steps, one of them optional.
01 Install the hook shim
Clone the repo; the shim is a single dependency-free Bun script. Point your harness's hooks at it — the settings block on the right is complete.
02 Set two env vars
CONDENSATE_URL is the backend (self-host default is http://localhost:4787); CONDENSATE_KEY is your API key. The key never appears in a payload — only its digest is sent.
03 Pick a tier
Self-hosting? Set CONDENSATE_CONSENT_TIER=observe on the backend and watch the ledger before you allow annotate, steer, or autonomous.
# the shim is one dependency-free Bun script git clone https://github.com/dextracker/condensate # point the shim at the backend (shell profile or settings env block) export CONDENSATE_URL=https://oh.condensate.dev export CONDENSATE_KEY=<your API key>
// ~/.claude/settings.json — adjust the absolute path { "hooks": { "UserPromptSubmit": [{ "hooks": [{ "type": "command", "command": "bun /path/to/condensate/apps/middleware/hook-shim/condensate-hook.ts", "timeout": 5 }] }], "PreToolUse": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "bun /path/to/condensate/apps/middleware/hook-shim/condensate-hook.ts", "timeout": 5 }] }], "PostToolUse": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "bun /path/to/condensate/apps/middleware/hook-shim/condensate-hook.ts", "timeout": 5 }] }], "Stop": [{ "hooks": [{ "type": "command", "command": "bun /path/to/condensate/apps/middleware/hook-shim/condensate-hook.ts", "timeout": 5 }] }] } }
# backend env (self-host): gate what may be emitted # observe | annotate | steer | autonomous CONDENSATE_CONSENT_TIER=observe # optional §6 control arm: pin this fraction of # steer/autonomous sessions to observe CONDENSATE_HOLDOUT_FRACTION=0.1
The shim's own 800ms fetch budget sits well inside the hook timeout, so the shim — not the harness — always decides the outcome. Full contract: hook-contract.md · tiers: consent-tiers.md.
The condensate family
Open Harness is the harness-steering half of condensate — this page. The MCP capability half ships too: the backend's aggregated /mcp endpoint is verified live end-to-end, and the first resident suite — 22 onchain tools — lives at onchain.condensate.dev. Same identity, same metering, same content-free posture.