bimtex
For agents

The drafter loop

The shipped agent harness — sessions a host makes, services a host injects, drawing rounds an independent crit closes — and the measured failure behind every gate in it.

bimtex/drafter is the loop the studio page runs, packaged. It is not a framework: it is one working loop whose every gate answers a failure that was measured on a live run, and the fastest way to understand it is to read it as a list of those failures.

The shape

brief ──→ scheme turn ──→ (your gate) ──→ round 1..N ──→ verdict
              │                              │
              │                              ├─ drawing turn   write / edit · inspect · evaluate
              │                              ├─ independent crit — fresh eyes, fixed sheets
              └─ state_scheme                └─ findings carried into the next round

A job is: one turn that produces a scheme, an optional human gate, then drawing rounds. Each round is a drafting turn followed by an independent review, and only findings that survive the review re-enter as the next round's work order. The loop stops on counts — findings closed, likeness plateaued, ceiling reached — never on a score the drafter wrote itself.

Running it

import { createSession, runJob } from 'bimtex/drafter';

const session = createSession();          // the working drawing; one per operator
await runJob({
  session,
  hosts: { rasterize },                   // what YOUR machine can do — see below
  model: 'google/gemini-3.5-flash',       // any OpenRouter route with vision
  critModel: 'google/gemini-3.5-flash',
  apiKey: process.env.OPENROUTER_API_KEY,
  brief: 'A one-room garden studio, door to the south.',
  references: [dataUrl],                  // photographs the drawing is held to
  site: [[0, 0], [18, 0], [18, 12], [0, 12]],  // a metre-coordinate lot ring
  ceiling: 3,                             // most rounds you will pay for
  patience: 2,                            // rounds with no closer likeness before it stops
  emit: event => record.push(event),      // the page IS a fold over this stream
});

Install the peers first — they are deliberately not dependencies, so an engine-only install never pays for them:

npm i bimtex ai zod @openrouter/ai-sdk-provider

Sessions are made, not found

createSession() returns the working drawing: source, parsed entities, message transcript, the scheme, and the bookkeeping the gates read. The harness never reaches for a shared one. A browser tab makes one for its whole life; a server makes one per request — two jobs handed the same session are not two jobs, they are one drawing being written by two writers.

Hosts: what your machine can do

The loop needs two services it refuses to own, because they are the only parts that differ between a tab and a server:

ServiceContractBrowser answerServer answer
rasterizeSVG string → base64 PNGa canvas@resvg/resvg-js, or resvg-wasm on an edge runtime
renderShotsmodel source + shot list → rendered 3D perspectivesWebGLomit it, or a headless renderer

rasterize is required — runJob throws before spending a token without it, because a loop that cannot look can never pass its own gates. renderShots is optional the way WebGL always was: absent or failing, the two exterior perspectives are lost and every flat sheet survives.

The tools, and what each gate prevents

The drafter gets seven tools. Five write or look; two are gates with teeth.

ToolWhat it is
state_schemeWhat the building is, its parti, what each view must show, the facts the givens fix, and the rubric its likeness is graded in
getVocabularyEvery legal entity type with its fields; every room use, symbol, material, element
browseElementsThe appearance catalogue, as cards or one full body
write_bimtex_modelThe whole model, first pass or deliberate redraw
apply_bimtex_opsAtomic edits, checked, with a semantic diff and the checker's delta
inspectCompile once and report; with review: true, print the drawings and look
evaluateHand in an assessment — which the harness may refuse

The refusals are the design. Each one moved a rule that had been prose — and was therefore optimised away by the first live run — into a gate:

  • No write without a scheme. A loop that goes straight from brief to entities produces a parts list, not a building.
  • No verdict without a look. evaluate refuses while the last printed review trails the current revision. Measured twice: the model printed its sheets once, at the end, after deciding it was done.
  • No self-certification past the checker. Outstanding errors overrule satisfied: true, always.
  • Every promise answered. Each shows item and each fixed fact must be accounted for — visible, or departed from with a written reason. Dropping one silently is the failure; dropping one on purpose is allowed.
  • Oscillation is a brief problem. A finding fixed, reintroduced, and fixed again stops the loop and says the requirements conflict — instead of paying for the third reversal.

The independent crit

After each drawing turn, a second model call — with no drafting history at all — receives the brief, a measured area schedule, the reference photographs, and a fixed sheet set chosen by the harness: every storey's plan plus both exterior perspectives, whether the scheme named them or not. A drafter that picks its own sheets shows its good side; the fixed floor is what makes the review independent.

The crit returns at most five findings and, when the scheme named a rubric, a scorecard graded 0–2 per dimension in words a reader can check ("would a reader holding the photograph accept these as the same place"). The harness sums the bands — likeness, 0–10, the one number two runs can be compared by — and the crit never sees the pass bar either.

Findings carried into the next round are recheck-only: a finding the crit says still stands twice in a row is marked stuck and never retried, and a round that closes nothing ends the job with the honest sentence about why.

What it costs

Every event the loop emits carries per-step usage, and estimateCost prices it against per-model rates that include the cache-write rate — which is, measured, most of the bill. Two facts worth designing around: an image costs a flat ~1,085 prompt tokens regardless of resolution, and the transcript is append-only on purpose — editing history to save tokens invalidates the prefix cache behind the edit and costs 12.5× what it saves.

The studio page is this exact loop with a browser host injected — its key stays in the reader's tab. A server host inverts one thing only: the key is yours, so meter your callers. Everything else, including every gate above, is the same code.