What is Basic2202?
An agent given a task works out a plan, calls tools, reads what comes back, and answers. Give it the same task tomorrow with one value changed — a different region, a different customer, a different week — and it works the same plan out again from nothing.
The tokens, the latency and the tool calls are all spent a second time to arrive at a conclusion the first run already reached. Basic2202 records those runs, derives the plan from one of them, and runs the plan directly the next time the same question comes round.
The loop
Three steps. Each is a thing you can see in the console, and each can be inspected before you trust the next.
The agent works normally. Every tool call, its arguments, what came back, how long it took and what it cost is stored — along with the prompt that started it and the answer it reached.
Produces · a runThe transcript becomes logic: an intent, a parameter schema naming what was incidental to that particular run, and one body per step that recomputes that step's arguments.
Produces · a scenarioA prompt that means the same thing as a recorded one runs the logic instead of being solved again. The tools are really called; only the rediscovery is skipped.
Produces · a savingRecord
The runner sits in the agent's execution path and stores each run. Nothing about the model's behaviour changes — the recorder executes the calls the model was going to make anyway and returns the results unaltered.
A run with three tool actions or fewer is not listed as a recording: it is never embedded, matches nothing, and is eventually pruned. Below that threshold there is no plan to derive, only a single call and its answer. Each finished run gets a title of at most 40 characters, written by a model from its transcript, so the list reads as a list of tasks rather than a list of ids.
fleet tool, and answers. Nothing about the agent changed; the runner recorded it from the side, and a run like this is what a recording is made of.Calculate
A recorded run is a transcript: what happened, once, with those exact values. A calculated scenario is what Basic2202 derives from it — an intent, a parameter schema, and one logic body per step.
Press Calculate yourself, or let the service choose: whenever a prompt comes back it takes the recording with the most to save — among those whose prompt has come back at least three times and that passed end to end, the run finishing without an error and no tool call in it failing — and calculates it. It never replaces a scenario you already have. Would rather it were a different recording? Prefer that one: it is calculated at once, taking the place of an automatic scenario (or, after you confirm, one you calculated by hand), and nothing evicts it in turn until you withdraw the preference.
The transcript says what the agent did. The scenario says what the agent was doing, which is the only half worth running again.
fleet and id — are what may change next time. The last execution says what one reuse cost and what it saved.
eu-west-1 fleet. return { fleet: parameters.fleet } sends whatever fleet the new prompt names; in this example the output logic sorts what comes back by risk and hands the worst device to the next step. Nothing is remembered — everything is recomputed. A plan that had memorised one device id would look identical today and be wrong the day the data changed.Replay
Matching is on the prompt text, embedded and compared by
similarity — not the transcript and not the answer. Above the detection
threshold (default 0.9) the service recognises a match; above
the steering threshold (default 0.92) the runner acts on it.
- Fully wrapped — every step is a tool the proxy already holds a connection to. The sequence runs through those connections with zero model tokens; the model's only job is to read the results.
- Mixed — some steps are built-in tools the proxy cannot execute. The live session is steered instead: each step runs as normal, with its arguments pinned to what the scenario computed.
The savings ledger, and why it is careful
The headline is verified saved. It is deliberately smaller than the number Basic2202 could report, and the gap is the point.
Money we watched move. Costs measured on both sides — what the agent's run cost, what the replay cost — reported by the process that spent them. This is the number the dashboard leads with.
Booked from costs nobody measured end to end. Real rows, honest arithmetic, partial inputs. Shown beside the headline and never added into it.
The distinction matters because of how the arithmetic fails when a cost is
missing. An unmeasured replay usually has only its parameter-derivation call
priced, so baseline − cost books nearly the whole baseline as
profit. A ledger that summed those would report a large, confident, wrong
number.
Two more rules in the same spirit:
- A baseline drawn from one run is labelled as one run. Every saving on a row is measured against what the agent costs on that task. If that figure comes from a single sample it is a guess, and the console says so rather than presenting a statistic.
- A scenario can lose money, and it will say so. If recent replays cost more than the agent does, the row is flagged as not paying off. Savings are not clamped at zero — a scenario quietly costing you money is exactly the thing you need to be told.
Two run types are counted but save nothing, and are reported separately. A dry replay evaluates stored logic against recorded outputs — a check, not work. A not-steered hit is a matched prompt the runner declined to replay, which becomes another baseline sample instead.
Scenario caching, not semantic caching
AI gateways such as TrueFoundry's and Gravitee's offer semantic caching. The gateway keeps the model's reply to each prompt, keyed by an embedding of that prompt. When a new prompt is close enough to one it has seen, above a similarity threshold you set, it returns the stored reply and the model is never called. For what it is built for it is a good tool: the same question asked many times in slightly different words, where the same answer is still right. A help desk answering the password-reset question is the textbook case.
Basic2202 starts the same way and then does something different. It also embeds the request and looks for a close match among your earlier ones, with a stricter threshold and more checks before anything runs. But what it keeps and reuses is not the answer. It is the scenario: the plan the agent worked out, with the values that vary taken out of it, and a rule per step for recomputing that step's arguments from the new request and from what the previous step returned. On a match the tools are really called, against today's data, and the model reads the results and writes today's answer. What is skipped is the working-out, not the work. The honest name for it is scenario caching: it caches the plan.
| Semantic cache | Basic2202: scenario caching | |
|---|---|---|
| What is stored | The model's reply, keyed by the prompt's embedding | The plan: an intent, the parameters, one rule per step |
| What a hit returns | The stored reply, without calling the model | A fresh run of the plan: live tool calls, a new answer |
| Right when | The same question deserves the same answer | The same task comes back with different values: another fleet, another customer, another week |
| Wrong when | The world moved since the reply was stored, or the near-match was a different question | The task is exploratory, so there is no plan to reuse |
| Where it sits | In front of the model, in the gateway | Beside the agent, on the path to its tools |
| What it saves | The whole model call, on questions that repeat word for word or nearly | The planning turns, on tasks that repeat in shape |
| What it reports | A hit rate | A saving: what the agent costs on the task, minus what the replay measurably cost, with verified kept apart from projected |
| The failure to watch for | A hit that should have been a miss returns the wrong answer, confidently. Both vendors' own documentation warns of it | A step that remembers a value instead of computing it. The free dry run exists to catch that |
The two are not rivals for the same request. A question with a fixed answer belongs in a semantic cache, and Basic2202 would save nothing on it: there are no tool calls to plan. A task that calls tools, whose answer depends on what those tools return today, cannot be cached at all. Only its plan can, and that is what Basic2202 caches.
What Basic2202 is not
| Not this | Because |
|---|---|
| A caching layer, or a semantic cache | A cache returns a stored answer. A scenario re-runs the work with new arguments — the tools are really called, against live data, and the answer is about today. The side-by-side is under Scenario caching, not semantic caching, above. |
| A prompt library | Nothing here changes what the model is told. Tool descriptions are never edited to steer the model's choices. |
| An observability product | It records runs and you can read them, but the recording exists to become a scenario. If you only want traces there are cheaper things to run. |
| Automatic replay | A run is recorded automatically, and a scenario is calculated automatically once its prompt keeps coming back — or because you asked, or preferred it. A replay runs only in a project you have set the runner up in, and only through the servers you leave on its allow-list (bir replay allow); bir replay off stops it for that project. |
Who should use it?
Basic2202 pays off on a narrow, common shape of work: a task you give an agent more than once, where what changes between runs is the values and not the plan.
If that describes something you do weekly, read on. If it does not, the honest answer is that Basic2202 will record your work faithfully and save you nothing — and you should know that now rather than in three weeks.
The three-question test
All three have to be yes.
-
Will I run this again?
Not "could someone"; will you. A task run once is a task whose plan is worth nothing to derive.
-
Does the plan stay the same when the values change?
"Check the eu-west-1 fleet and tell me which device is most at risk" and "check us-east-2" are one plan with a parameter moved. "Figure out why the build broke" is a different investigation every time, even when the words are identical.
-
Is it more than three tool calls?
Below four actions there is no plan to capture, and the service does not list the run as a recording. A one-call task is already as cheap as it gets.
The shapes that work — and the ones that don't
Worth recording
- Scheduled sweeps and reports. Fleet health, cost reports, open-PR triage, error-budget checks. Same steps, new window, run on a cadence.
- Per-entity investigations on a fixed path. The customer changes; the four tools and their order do not.
- Fan-out over a list. Record once against one repository, region or account; the other thirty-nine are the same scenario with a different parameter. This is where the arithmetic gets loud.
- Runbooks with a stable spine. Nobody should be paying a model to rediscover the opening moves at 3am.
Not worth it
- Genuinely exploratory work. Debugging, code review, research — anywhere the second tool call depends on what the first said in a way you could not have predicted. The plan is the work.
- Non-MCP tools without hooks. You get a partial recording, correctly labelled as one.
- Anything that must not run unattended. If that sentence makes you uneasy about a particular tool, do not put it on the allow-list — that instinct is correct.
- High-variance, low-frequency work. Something you run twice a year, where the environment has moved on by the time it comes round again, so the plan the scenario holds is no longer the plan.
Who, in job terms
| You are | What you get out of it |
|---|---|
| Platform / SRE | Scheduled sweeps and runbook openings. The highest-yield case: fixed spine, changing scope, run constantly |
| Data / analytics engineering | Report pipelines where the shape is stable and the window moves |
| Support & success engineering | Per-customer investigations that always start the same way |
| Agent and tooling teams | A measured answer to "is this agent worth what it costs", from the ledger, before you touch replay at all |
| Engineering leadership | The verified-savings figure — deliberately conservative, and therefore quotable |
Recording is useful on its own, and it is the whole of what runs out of the box. A team that never calculates a single scenario still ends up with a priced, searchable record of what its agents did and what each run cost. For some teams that is the entire value, with replay as a later question.
What it costs you to find out
An afternoon, and no money. Recording, calculating and replaying are all free, there is no limit on how many scenarios you hold, and none of them expires. Take the task you repeat most, record it, calculate it (or repeat it three times and let the service calculate it for you), dry-replay it, and read the ledger after a week. If the number is unconvincing on your best candidate, it will not improve on your second-best.
If it is convincing, the question that follows is usually not about price but about where the recordings live. That is what running it in-house is for.
The honest limits
None of these is a bug to be fixed later.
- A saving is only counted when it is measured. The verified figure needs the runner to report what a run cost. A session that ends without reporting loses you the measurement, and the saving lands under projected instead — the ledger stays conservative either way.
- Calculation can be paused. Working a scenario out costs us several model calls, so the free service calculates one scenario at a time per account and stops for the day if the whole service has spent its budget. An ordinary account never meets the second one.
- A match is a match on the prompt. Phrase the first prompt of a reusable task as though someone else will have to phrase it again next month — because that is exactly what has to happen.
- Standalone recording is partial. Without the recorder —
bir-hooks, whichbir setupstarts for the folder and the SessionStart hook keeps started — there is no prompt, no final answer and no built-in steps, so there is nothing for a match to compare against and replay cannot arm. - Calculation needs a model. Deriving a scenario, and deriving parameters at replay time, both call Anthropic. A service without a key configured will refuse to calculate.
Using the console
The console is where recordings, scenarios and money are visible. What follows is a tour of the screens and, more usefully, of what each number is actually claiming.
Getting an account
Register
With an email and password, or sign in with Google.
Verify the address
Registration does not create a session; the link in the email does. That link is the proof of the inbox, and it may be opened on a different device from the one you registered on.
Sign in
You land on the dashboard. If a verification link has gone stale, ask for another from the sign-in screen — a new link supersedes the old one.
A new account sees Getting started on the dashboard where the tiles would be. The next step is to install the runner — about ten minutes — and the first recording lands there.
Getting around
The menu is on the left and stays there. It is grouped, because the destinations are about different things.
| Section | Screens |
|---|---|
| — | Dashboard, Recordings — and, while you are inside Recordings, its saved views — then Set up the runner |
| Account | Account, Sign-in history |
| Admin | Users, Invoices — admin accounts only. Each row of Users has a Recordings link that opens that account's recordings, read-only |
Pinned to the bottom of the rail:
- Documentation — this handbook, where the deployment has been given a docs URL.
- The account chip — name and role, with account settings, the light/dark toggle and sign out.
⌘K opens search from anywhere. It searches the menu, and offers one row that hands your query to the recordings search. It does not search across everything — there is no index behind that, and a palette pretending otherwise would be worse than one that is clear about its scope.
At the foot of every page: API status, Help and support, Feedback. The status light is a single call on load — it does not poll, because a status indicator that hammers a service during an incident is a second outage stacked on the first.
Help and support opens a page of its own: is the API up, is the runner recording, the usual problems with their fixes, and a Write to us button whose email already carries your account address and the console version, so a first reply can be an answer.
Recordings — the columns
The list, and the only screen with thirteen columns. Everything about it lives in the URL — sort, page, search, filters — so a view worth building can be bookmarked or sent to somebody.
| Column | Reading |
|---|---|
| Run | The generated title, ≤40 characters. Falls back to the first line of the prompt before a title exists. Calculated rows carry a green edge |
| Started | When the agent ran |
| Steps | Actions the agent took. Not every stored row — a tool's response and the recorder's own bookkeeping are not steps the agent took |
| Status | done, failed, or running |
| LLM $/run | The baseline: what this task costs when the agent does it. Its ⓘ says how many runs the baseline is drawn from |
| Scenario $/run | What a replay costs. Marked est. when it is a pre-flight estimate rather than an observed mean — "we measured this" and "we guessed this" must not look alike |
| Saved %/run | How much cheaper one calculated run is than one agent run: (LLM $/run − Scenario $/run) ÷ LLM $/run, from the two columns beside it. The bar under the figure fills to the same percentage, in a green that deepens as the saving grows; red only when a replay costs more than the agent. Marked est. while the scenario cost is still an estimate; — without a ready scenario or a baseline |
| Original time | Wall-clock for the agent's run |
| Calculated? | yes, no, calculating… or failed. A scenario whose recent replays cost more than the agent is flagged not paying off here too. auto beside the badge means the service calculated it by itself. The ★ Preferred and ⊘ Switched off badges under a title are the two things you can tell the service about a recording |
| Calculated runs | Replays of this scenario that did work — the same count as the tile above. Its ⓘ breaks out unmeasured, dry and not-steered runs |
| Similar hits | Prompts matched against this recording |
| Saved $ | Verified savings for this row, signed. — until a measured replay has actually happened. Projected savings, when there are any, sit beneath it and are never added in |
Every column with a figure in it sorts, Saved %/run included. Calculated recordings sort to the top of every column by default; the note above the table says so and lets you turn it off. Runs with three actions or fewer are hidden, with a link saying how many — hidden rather than deleted, because they are also below the threshold for being listed at all.
Recordings — the detail page
Everything stored for one run: the prompt, each step with its arguments and
output, the final answer, and the scenario if there is one. This is where you
press Calculate, read what calculation produced, and run the
dry replay that is worth doing before you trust anything. There is no
Recalculate button: a calculation that failed is run again by preferring the
recording, which resets it and starts a fresh job. From the command line it is
still bir scenario calc --force.
check the us-east-2 fleet; the agent's answer; each tool call with what it sent and what came back. On the right, the scenario calculated from it, pictured up close in section One.It is also where you overrule the service's pick, in either direction. ☆ Prefer this recording calculates this one straight away, and takes nothing from anybody: you may hold as many scenarios as you like, so nothing has to be replaced to make room. A preferred recording is also first in line whenever the service looks for something to calculate on its own, whatever its hit count, and is kept however short it is.
Switch off calculation and replay is the opposite instruction: the service never picks that recording on its own, however often its prompt comes back and however much it has at stake — and while it is off, a matching prompt is not replayed from its scenario either: the agent runs again and nothing is saved. The scenario is kept, and comes back when you switch calculation and replay on again. The two are one switch, so each button is offered only while the other flag is not set: a recording is preferred, switched off, or neither. Two badges beside them are about the server rather than the recording — server cannot calculate when the deployment has no Anthropic key and nothing can be calculated on it at all, no automatic calculation when the key is there and only the picking is switched off, which leaves Prefer working.
Steps changed by hand (not released yet). You, or
Claude working for you, can correct one step of a scenario with
bir — see Let Claude fix a step. The
detail page shows it:
- The scenario's details say edited by hand, and how many steps.
- A note names those steps. It says the service does not rebuild this plan on its own while they are there. It also gives you the commands:
bir scenario editsshows the history;bir scenario undotakes back the newest edit, and run again it goes one further back. Once every edit is taken back, the service can rebuild the plan as usual. To recalculate now and drop the edits:bir scenario calc <scenario id> --force --discard-edits. An administrator sees the note without the commands. - The step's own card has an edited by hand badge: its code, its mark, or both were changed. It has accepted on purpose as well when the change was saved with
--forcealthough its check against the recording did not pass. Hold the pointer over a badge to read more. - A step marked needs a judgement says how to fix it with
bir scenario edit. A step you marked yourself says marked by hand. - In a plan with hand edits, a step that keeps failing is not repaired on its own. Its label says not repaired: hand edits, and the text under it names the ways out: fix the step, take the edits back with
bir scenario undoonce per edit, or recalculate and drop them. - Recalculate on a sub-task with hand edits is refused. The message names the same ways out, with the sub-task's own id.
The console only shows edits: making one, reading the history and undoing it
are done with bir.
What it costs
The service here is free. Recording, calculating, replaying, as many scenarios as you like, and none of them expires. There is no card, no balance and no fee. Your recordings are stored on our server in Germany, and the page on security and privacy says exactly what that means.
That last sentence is why the one paid product exists. A recording holds the request you typed, every tool call, and everything those tools returned — file contents, query rows, whatever a browser fetched. Plenty of companies cannot keep that on somebody else’s server, whatever the terms say. For them there is Basic2202 In-house: the same service, running in the company’s own AWS account. It is the only thing we charge for, and the price is one fifth of what it saves you. You keep the other four fifths, and a month that saves nothing costs nothing. Only the verified saving counts, after what calculating scenarios cost; projected savings are never billed. There are no seats, no minimum and no trial to run out.
Our aim is to save you as much money as we can. A share of the saving is the price that says so: we earn only when you save, and we earn more only when you save more.
One thing to be exact about, because it is the part people assume away: an in-house instance still sends data out, to AWS services in your own account. Claude, through Amazon Bedrock, sees the whole recording when it calculates a scenario, a short excerpt when it writes a recording’s title, and the new request and the scenario’s parameters when a replay works out what to pass. And every hour one number goes to AWS Marketplace — the dollars the instance saved you — which is how you are billed. Nothing goes to us.
In-house is not available yet. To talk about running it, write to support@bi2202.com.
Reading the numbers honestly
Lead with verified
It is the smaller number and the defensible one. If you need the larger one for a slide, say which half is projected.
Check the baseline's sample count
Before quoting a saving. One run is a guess, and every saving on that row is measured against it.
Believe a negative
A scenario flagged as not paying off is costing you money on its recent runs. The two useful responses are to retire it (delete the recording) or to replace it by preferring a different recording; both beat looking away.
How to record
Everything starts with a recorded run. The recorder is
BaseInstRunner — bir — which lives in its own
repository, BaseInstRunnerMCP. Getting
started installs it in about ten minutes.
Three files there are the source of truth for everything below:
README.md (the operator's half),
docs/BaseInstRunner.md (the design), and
docs/calculatedReplayGuide.md (the replay runbook in more
detail than this page goes into). Where they disagree with this page, they
are right — they sit next to the code.
Getting started — install the runner
One line in a terminal. It installs what is missing — Node, the runner, Claude
Code — signs the runner in as you, wires the project you paste it in and starts
the recorder in the background. Nothing has to stay open. Then claude,
as always. A few minutes, most of them downloads; nothing here needs a copy of
the runner's source code. Signed in to the console, the same steps are on its
Set up the runner page, with the line ready to copy, your
account filled in, and a line that turns green when your first recording
arrives.
npm install, bir install, bir login, the recorder in a window of its own, and the first verified saving. The install and the sign-in look the same today; the rest is what the line now does for you, and the recorder no longer needs a window. 7 min 54 s, silent; the step being done is named in the corner. The desktop is wide, so watch it full screen.Two things. The line does the rest.
- You have a Basic2202 account, and you are signed in to the console. Register at bi2202.com, open the link in the verification email, and sign in once. The line on the console's Set up the runner page carries a one-time setup token tied to that account — single use, good for ten minutes — and that is how the runner learns which account it belongs to. There is no account name or identifier to type anywhere.
- A project folder — the one you start Claude Code in. Best with an MCP server in it, a
.mcp.jsonin the folder or servers listed in~/.claude.json: those are what a calculated scenario can run directly. Without one, recording works all the same and a scenario steers the session instead. No project with an MCP server? That is fine: step 2 builds you a small, real one in about a minute.
Node and Claude Code are each installed for you only when missing or unusable: Node when the machine has none, one older than 20, one a version manager, Homebrew, snap or Scoop put there, or one with no npm beside it; Claude Code when claude is not found. On Windows use PowerShell or cmd, whichever your terminal is; step 1 has a line for each.
Paste one line into a terminal in your project folder
Open a terminal in the project folder you start Claude Code in, and paste this. The console's Set up the runner page shows it with the token filled in — copy it from there, not from here.
$env:BIR_SETUP_TOKEN="<token>"; irm https://api.bi2202.com/install.ps1 | iexcurl -fsSL https://api.bi2202.com/install.sh | BIR_SETUP_TOKEN="<token>" shA Windows terminal that is
cmd.exerather than PowerShell — the prompt does not begin withPS— takes the same line, wrapped:powershell -NoProfile -Command "$env:BIR_SETUP_TOKEN='<token>'; irm https://api.bi2202.com/install.ps1 | iex". The console's Windows (cmd) tab has it, beside Windows (PowerShell) and macOS / Linux.The token is single use and good for ten minutes; the line lands in your shell's history, which is why it dies at first use. Expired, or not at the console? The same line without the
BIR_SETUP_TOKENpart still installs and wires everything;bir setupthen prints a link and a short code and waits for you to approve it in the browser, from any device. On an in-house instance, its own address stands in forhttps://api.bi2202.com; the line its console shows already has it.You should see
==>lines as it goes through Node, the runner, Claude Code and the sign-in, then a summary that starts withDone.:Done. Recording and replay are on for C:\path\to\your\project account you@example.com (https://api.bi2202.com) wrapped fleet replay on — direct execution allowed for: all wrapped (narrow it with `bir replay allow fleet`) recorder started in the background (pid 1234) — it starts itself with every Claude Code session log C:\Users\you\.baseinstrunner\logs\3f9c1a2b7d4e5f60.log Now: claude first time in this folder: say Yes to "trust this folder" just installed Claude Code? it asks you to sign in to Claude (it needs a Claude subscription) give it a task that takes 4+ tool calls — it appears at https://bi2202.com/recordings within a minute of finishing Any time: bir doctor bir investigate bir down (stop) bir uninstall (undo)What it did, in order, each step under a
==>line of its own: installed Node if the machine had no usable one — none, older than 20, in a version manager's, Homebrew's, snap's or Scoop's path, or withoutnpmbeside it — as a private copy under~/.baseinstrunner, never on your PATH; installed the runner; installed Claude Code ifclaudewas missing, which can take a minute; then ranbir setup, which signed in, stored the service address, wrapped the project's MCP servers, wired Claude Code's hooks into.claude/settings.local.json, added and pre-approved thebirscenario server, and started the recorder in the background. In the summary, wrapped names the project's MCP servers, each now routed through the runner under its old name; replay says which of them a calculated scenario may call unattended (narrow it withbir replay allow <names>); recorder is the background process, and log is where its audit lines go.A project with no MCP servers says
steer mode onlyon the replay line; recording works all the same. Pasted it in your home folder, or the root of a drive (or disk), by mistake? The line stops before downloading anything and says so: nothing is installed, nobody is signed in, and the token has not been used. Open a terminal in your project folder (cdthere) and paste the same line again.If it goes wrong, the usual cases are under If something is wrong, after the steps.
Make something to record
Skip this if the folder you pasted the line in already has an MCP server — a
.mcp.jsonin it, or servers listed in~/.claude.json— and go on to step 3. If it has none, build a small one now. It takes a minute and gives you a task worth recording rather than a toy that records nothing; the Sample section below uses the same folder. It needs anodeon your PATH (node -v): the private copy the line may have installed is not on it, so if that fails, install the LTS build from nodejs.org first.mkdir fleet-demo cd fleet-demoSave this into it as
fleet-server.js:#!/usr/bin/env node /** * A tiny, real MCP server over stdio — the thing `bir-proxy` will wrap. * * Two tools, chosen so a scenario built from them has to thread state: * list_devices(fleet) -> the fleet's devices, with risk scores * device_detail(id) -> one device's detail * * Step 2 of a scenario can only compute its `id` argument from step 1's output, * which is exactly what `respParams` threading is for. */ const FLEETS = { "eu-west-1": [ { id: "dev_88f1", name: "edge-01", risk: 0.94, firmware: "3.1.2" }, { id: "dev_2c30", name: "edge-02", risk: 0.31, firmware: "3.2.0" }, { id: "dev_9a05", name: "edge-03", risk: 0.62, firmware: "3.1.9" }, ], "us-east-2": [ { id: "dev_4411", name: "core-01", risk: 0.77, firmware: "2.9.4" }, { id: "dev_7b22", name: "core-02", risk: 0.12, firmware: "3.2.0" }, ], }; const TOOLS = [ { name: "list_devices", description: "List the devices in a fleet, with their risk scores.", inputSchema: { type: "object", properties: { fleet: { type: "string", description: "Fleet id, e.g. eu-west-1" } }, required: ["fleet"], }, }, { name: "device_detail", description: "Full detail for one device.", inputSchema: { type: "object", properties: { id: { type: "string", description: "Device id, e.g. dev_88f1" } }, required: ["id"], }, }, ]; function callTool(name, args) { if (name === "list_devices") { const devices = FLEETS[args.fleet] ?? []; const worst = [...devices].sort((a, b) => b.risk - a.risk)[0]; return { fleet: args.fleet, count: devices.length, devices, worstDeviceId: worst ? worst.id : null, }; } if (name === "device_detail") { for (const devices of Object.values(FLEETS)) { const found = devices.find((d) => d.id === args.id); if (found) return { ...found, lastSeen: "2026-08-31T09:12:00Z", status: "online" }; } return { error: `unknown device: ${args.id}` }; } throw new Error(`unknown tool: ${name}`); } // ── minimal MCP stdio plumbing (newline-delimited JSON-RPC) ───────────────── let buf = ""; process.stdin.on("data", (chunk) => { buf += chunk.toString("utf8"); let nl; while ((nl = buf.indexOf("\n")) >= 0) { const line = buf.slice(0, nl).trim(); buf = buf.slice(nl + 1); if (line) handle(JSON.parse(line)); } }); const send = (msg) => process.stdout.write(JSON.stringify(msg) + "\n"); const reply = (id, result) => send({ jsonrpc: "2.0", id, result }); function handle(msg) { const { id, method, params } = msg; if (id === undefined || id === null) return; // notification switch (method) { case "initialize": return reply(id, { protocolVersion: params?.protocolVersion ?? "2025-06-18", capabilities: { tools: {} }, serverInfo: { name: "fleet", version: "1.0.0" }, }); case "tools/list": return reply(id, { tools: TOOLS }); case "ping": return reply(id, {}); case "tools/call": { try { const out = callTool(params.name, params.arguments ?? {}); // A normal MCP result: a text block holding JSON. return reply(id, { content: [{ type: "text", text: JSON.stringify(out) }], isError: false }); } catch (err) { return reply(id, { content: [{ type: "text", text: String(err.message) }], isError: true }); } } default: return send({ jsonrpc: "2.0", id, error: { code: -32601, message: `no method ${method}` } }); } } // Diagnostics go to stderr. A stray byte on stdout corrupts the JSON-RPC stream. process.stderr.write("fleet-server: ready\n");That is a real MCP server, not a mock: 111 lines, no dependencies, speaking newline-delimited JSON-RPC over stdio exactly as any other MCP server does. It holds two fleets of pretend devices and offers two tools:
list_devices(fleet) → the fleet's devices, with risk scores device_detail(id) → one device's detailTwo tools chosen for one reason: step 2 of a scenario can only work out its
idargument from step 1's output. That makes it the smallest honest test of whether scenario calculation really works, rather than the smallest test that passes.Now tell Claude Code the server is there. Save this as
.mcp.jsonin the same folder:{ "mcpServers": { "fleet": { "command": "node", "args": ["fleet-server.js"] } } }You should end up with those two files and nothing else. Do not start the server yourself — Claude Code launches it when it needs it, and
birsits in between. Then, still in that folder, wire it. The sign-in from step 1 is kept on this computer, so no token and no address are needed:bir setupIt ends with the same
Done.summary as step 1, withfleeton the wrapped line. This is also how you record any other project later:cdthere,bir setup.Then:
claudeIn the same terminal, same folder — from a
cmdwindow, a new one in the same folder: the line ran in a child PowerShell, and its PATH change does not reach the window you pasted in — start Claude Code as you always do. The first time in this folder it asks whether to trust it: say Yes, or the hooks do not run. Then give it a real task, one that takes four or more tool calls.claudeIf you built the demo in step 2, ask for "check the eu-west-1 fleet and tell me which device is most at risk". In the sample data the answer is
dev_88f1—edge-01, risk 0.94, on firmware 3.1.2: two tool calls, plus the model's turns around them. That sits near the four-call minimum, so ask a second thing in the same session ("and check us-east-2 as well") and it clears the threshold comfortably. A run with three tool calls or fewer is not listed as a recording, because there is no plan worth reusing in a couple of calls.You should see nothing new in the terminal: the recorder runs in the background and starts itself with every Claude Code session in this folder — tomorrow, and after a reboot, the same way. Nothing else to start, and nothing to leave open. The run appears in Recordings at bi2202.com within a minute of finishing, with its title, its step count and what it cost; on the console's Set up the runner page, the line at the bottom turns green when it does. The session itself looks like the run pictured under Record, in section One. What the recorder sends, and what it strips out first, is under Security and privacy, section Five.
Just installed Claude Code? It asks you to sign in to Claude the first time. That is a Claude subscription, separate from your Basic2202 account.
Check it
In the project folder, any time:
bir doctorThis is the one command worth remembering. It does not read a settings file and tell you what should happen; it asks the running system what is happening — which service and account it records to, that the recorder runs in the background, with its pid and its log file, what replay is set to and where that setting came from — and says so loudly when something is broken.
bir statusshows the configuration without needing anything running.
Healthy: bound (Tier 1)andRecording: yes. The one problem listed only says that no agent session was open at that moment; startclaudeand it goes away.
No recorder up: not runningandstandalone (Tier 2). Before the first session, or afterbir down, that is normal — the recorder starts with the next Claude Code session, orbir upstarts it now. If it still reads this way during a session, tool calls are being recorded but your prompt is not, and nothing could ever match; the table below has it.For a turn that did not do what you expected — a scenario that did not run, a saving that was not made —
bir investigatereads the journal and the service and says why. Ask why, below, walks through it.
If something is wrong
bir doctor first. It names the broken link in the chain, which is most of the answer. Then:
| What you see | What to do |
|---|---|
| This is your home folder / the root of a drive (or disk) | The line stopped before downloading anything: nothing is installed, nobody is signed in, and the token in it has not been used. A terminal opened from the Start menu begins in the home folder, which is where a first paste often lands. cd to your project folder and paste the same line again. (It is bir setup run by hand there, with the runner already installed, that keeps the sign-in and prints the two commands left: cd to the project, then bir setup, no token needed.) |
| not a BaseIn service | The address in the line is not the service's API: the website, or a stale BIR_AUTH_URL in a shell profile (bir setup warns when the two disagree). Copy the line from the console's Set up the runner page again — it points at https://api.bi2202.com, or at your own instance's API — and take the variable out of the profile. |
control.port_busy | Another program (or the system) holds the recorder's port — on Windows, Hyper-V and WSL reserve ranges that 53411 can fall inside. In the project folder: bir setup --port 53412 (any free port). It keeps the self-starting hook; nothing to restart by hand. |
recorder.disabled · no BaseIn session | The runner is not signed in on this computer. Run bir setup again in the project folder; without a token it signs in through the browser. |
| The run is not in Recordings | Three tool calls or fewer are not listed as a recording: give it a bigger task. Still nothing: the runner is signed in as another account (bir doctor names it on its recording to line). Switch it: bir logout, sign in to the console as the other account, and paste the line from its Set up the runner page again. |
| Tools are recorded, but not what you typed | No recorder was running for this folder, so the proxies recorded on their own (Tier 2). bir doctor says whether one runs now; bir up starts it, and the next Claude Code session would have anyway. |
| Claude Code asks to sign in | It needs a Claude subscription, and that sign-in is separate from your Basic2202 account. Sign in there once; from then on it starts signed in. |
command not found: claude or bir (macOS / Linux) · 'bir' is not recognized (Windows cmd) | The terminal you pasted in cannot see what was just installed. macOS / Linux: paste the export PATH=… line the script printed, or open a fresh terminal in the same folder. Windows cmd: the line ran in a child PowerShell, and its PATH change does not reach the cmd window; open a new cmd window in the same folder, or run set PATH=%USERPROFILE%\.baseinstrunner\bin;%USERPROFILE%\.local\bin;%PATH% in this one. (A PowerShell window is updated in place: bir works there at once, and in every new terminal.) |
bir doctor: the SessionStart hook … points at a Node that is gone | The Node the hook names was removed, or moved by a version manager. bir setup in the project folder rewires the hook to the current Node. |
| Anything else | Run bir doctor and keep the output. Paste it into Claude Code, or into a mail to support from the console's Help and support page. |
command not found row, as it looks. A window that was open before the install does not know the word yet. A PowerShell window the line was pasted in does; a cmd window has to be a new one, because the line ran in a child PowerShell; on macOS and Linux, paste the export PATH=… line it printed.The same, by hand
Two commands, if you already have Node 20 or newer (node -v prints
v20. or higher): install the package, then bir setup.
Below them, what bir setup does and the pieces it is made of, for
when you would rather run each one yourself. Working on the runner itself, from
a checkout of BaseInstRunnerMCP? The Guide
below has that.
npm install -g @basein/runner
added 1 package. That is the whole install. There is no address to set and no terminal to reopen: bir setup stores the address.Then, in the project folder you start Claude Code in, one command, with the address of the service (your own instance's, on an in-house one):
cd C:\path\to\your\project
bir setup --auth-url https://api.bi2202.com
cd ~/path/to/your/project
bir setup --auth-url https://api.bi2202.com
==> Setting up the runner (0.2.9)
service https://api.bi2202.com
signing in through the browser (a setup token from the console skips this)
Open https://bi2202.com/activate?code=FQSB-JCNQ
Code FQSB-JCNQ
Opening your browser…
Waiting for approval… (Ctrl-C to cancel)
account you@example.com
==> Wiring the project
==> Starting the recorder in the background
Done. Recording and replay are on for …
You should see your browser open the console on Approve a sign-in. Check that the code on the page is the one in the terminal and press Approve; the terminal goes on by itself. What it does, in order:
- Signs in as the account this browser is signed in with, and keeps the session on that computer under
~/.baseinstrunner, in a file only your user can read. You are not asked again. - Stores the address, so every command and every recorder from now on knows where the service is. No environment variable to set.
- Wraps the project: routes its MCP servers through the runner, each under its old name, and wires Claude Code's hooks into
.claude/settings.local.json, which stays out of git. It keeps a copy of every file it touches. - Pre-approves the
birscenario server, so Claude Code shows no approval dialog for it. - Starts the recorder in the background, and from then on the recorder starts itself with every Claude Code session in that folder.
bir login, the sign-in on its own; bir setup opens the same page. The blurred line is the machine's public address, shown so you can tell your own request from someone else's; if you did not just start a sign-in, press Deny.This is the moment the runner is tied to your account. There is no account name or identifier to type anywhere: whichever account is signed in in the browser that presses Approve is the account this computer signs in as. The page says so, under Will sign in as, and the terminal confirms it on its account line. You never type a password into the terminal, which is also why this works for an account that only signs in with Google. From then on every recording from this computer goes to that account, until you run bir logout. If the page names the wrong account, press Deny, sign out of the site, sign in as the right one, and run bir setup again.
If it goes wrong. This is your home folder: unlike the line, which stops before installing anything, bir setup keeps the sign-in, and the two commands left are cd to the project and bir setup again. not a BaseIn service: the address is the website, not the API; use https://api.bi2202.com. On a machine with no browser — a server over SSH, a container — add --no-browser: it prints the address and the code, and you approve from your laptop. Prefer no browser step at all? The console's Set up the runner page makes a one-time setup token already tied to your account: bir setup --token <the token from the page>, within ten minutes.
To record another project, cd there and run bir setup again; the sign-in is kept, so the address is not needed either.
Or the pieces, one at a time. bir setup is these three, in this order. Run them yourself to see each one's output, or to redo one of them.
bir login # the sign-in on its own; takes --no-browser and --token too
bir install --global --replay # wrap the project, wire the hooks, add the scenario server
bir up # the recorder, in the background — now, rather than with the next session
bir install rewrites the project's MCP entries in place — every server keeps its name, so nothing the model sees changes — and wires Claude Code's hooks. Without --replay the project is recorded and nothing is ever replayed. It keeps a copy of every file it touches, and bir uninstall puts them back byte for byte. A folder with no MCP servers is wired all the same: the hooks and the scenario server go in, and the summary says steer mode only.
fleet reads = already wrapped and the count is 0; on a first run that line starts with + and the count is 1. Today the hooks line names .claude/settings.local.json, with --replay a line before it adds the bir scenario server, and what came after Next: — run bir-hooks in a window of its own — now reads start your session here as usual. The recorder starts itself with it.The switches. A replayed step runs without the permission prompt you would otherwise see, so bir replay allow names the servers you are content to have called unattended, using the names on the wrapped line; left unset, every wrapped server is eligible. The setting is kept for this project, so a recorder started by a hook keeps it; a change is picked up by the next recorder, or now with bir up --restart. Read Turning replay on in the Guide before using replay on a project that matters.
bir replay allow fleet,github # the servers replay may call unattended (default: all wrapped)
bir replay status # what is stored for this project (bir doctor: what the recorder runs with, and its source)
bir replay off # record only; no scenario answers a matching prompt
bir down # stop the recorder; it starts again with the next Claude Code session
bir uninstall # put every file the install touched back
What it changed on your machine
| Path | What it holds |
|---|---|
~/.baseinstrunner/config.json | the service address; BIR_AUTH_URL in the environment overrides it |
~/.baseinstrunner/credentials.json | the cached session, bound to the service that issued it |
~/.baseinstrunner/installed.json | the original text of every file it rewrote, and each project's port, token and replay switches |
~/.baseinstrunner/logs/<key>.log | the recorder's audit log, one file per project |
~/.baseinstrunner/node/, npm/, bin/ | the line's own Node (only if the machine had no usable one; not on PATH), the runner package, and the bir and bir-hooks launchers, first on your user PATH. An install by hand puts the runner wherever npm does instead |
<project>/.claude/settings.local.json | Claude Code's hooks for this project and the pre-approved bir scenario server. Yours, not the team's: it carries this project's token and an absolute Node path |
<project>/.mcp.json (or ~/.claude.json) | each MCP entry rewritten to run through bir-proxy, same keys; plus the bir entry |
<project>/.git/info/exclude | one line, .claude/settings.local.json, so it never shows in git status |
Each project gets its own control port (from 53411 up) and its own token, so one project's settings file cannot reach another project's recorder.
Updating. Paste the line from the console again — the very same
line is fine, used token and all: bir setup says the setup token
was already used; keeping the sign-in from before (you@example.com) and goes
on. It installs the newest version and runs bir setup, which re-wraps
any entry whose runner moved. Not npm install -g over a one-line
install: that puts a second copy where the launchers never look. Installed by hand
in the first place? Then npm install -g @basein/runner again, and
bir setup in each project. A recorder of the older version is replaced
by bir setup, bir up or the next session's SessionStart
hook, provided no session is mid-run just then; nothing watches for a session to
end. bir setup says so when one is open (it restarts to the new
version with the next session), and bir up --restart does it now.
Undoing it. In the project folder, bir down stops
the recorder and bir uninstall puts every file it touched back
exactly as it was; the sign-in stays, so a later bir setup needs no
token. To remove the software as well: bir logout first, so the
session is revoked on the service rather than only forgotten here; then
npm rm -g @basein/runner if you installed by hand, or delete
~/.baseinstrunner — the runner, its private Node, the launchers, the
session and the logs — and take ~/.baseinstrunner/bin off your PATH.
Guide
What the recorder is
bir is an MCP proxy. It registers under your
existing MCP server keys, so the model still emits
mcp__chrome-devtools__navigate_page and still gets exactly what
chrome-devtools-mcp would have returned. The difference is that
bir executed the call on the client's behalf, and wrote down
what happened.
Interception is a configuration fact, not a prompting one. The model is never asked to cooperate, and tool descriptions are never edited to steer its choices.
An MCP server sees only MCP traffic addressed to it. Bash, Read, Edit, Grep are not MCP. Neither is your prompt, the model's reasoning, or the final answer.
Which is why there are two halves. bir-proxy records MCP calls;
bir-hooks is the recorder: a hook receiver that records everything
else and owns the run. bir setup starts it in the background, and
the SessionStart hook (bir-hooks ensure) starts it again with every
Claude Code session, so nothing has to stay open.
The two tiers
| Tier | When | What ends up in the recording |
|---|---|---|
| 1 — Bound | A recorder (bir-hooks) is running for this working directory — the SessionStart hook starts one with each session | Built-ins and MCP, in one ordered step stream, with the prompt and the final answer |
| 2 — Standalone | No control server found within five seconds | MCP calls only. No prompt, no final answer, no built-in steps |
Tier 2 is not a degraded bug — it is the honest ceiling of what a proxy alone can observe, and a run records which tier produced it so nothing downstream mistakes a partial trace for a complete one. For anything beyond raw recording you want Tier 1: a match is a match on the prompt, and a standalone proxy never sees one.
Install
The beginner's path is Getting started above: the one line
from the console's Set up the runner page, or by hand
npm install -g @basein/runner and then
bir setup --auth-url https://api.bi2202.com in the project (the
address is stored from then on, so a second project needs only bir setup).
Working on the runner itself? From a checkout of
BaseInstRunnerMCP, build it and run the same setup from the
checkout. It points the install at the checkout's proxy instead of the published
package (--local, decided for you), and a later flag-less
bir install keeps that shape:
npm install; npm run build
cd C:\path\to\the\project\you\record
node C:\path\to\BaseInstRunnerMCP\dist\bin\bir.js setup --auth-url https://api.bi2202.com # sign in, wire it, start the recorder
npm install && npm run build
cd ~/path/to/the/project/you/record
node ~/path/to/BaseInstRunnerMCP/dist/bin/bir.js setup --auth-url https://api.bi2202.com # sign in, wire it, start the recorder
The line in Getting started is the runner repository's own installers,
scripts/install.ps1 and scripts/install.sh, served by
the API with its address filled in. For a fleet of machines — packaging, offline
installs, updates — see its docs/quickstart.md and
docs/installRun.md.
bir install, the middle of bir setup, rewrites your MCP
entries in place, keeping every key — so
mcp__<server>__<tool> names never change — and wires
Claude Code's hooks into .claude/settings.local.json, the file
Claude Code keeps out of git. It stashes the original text of every file first,
and bir uninstall puts it back byte for byte.
Then your session, now recorded:
claude
Nothing else to start. The SessionStart hook is bir-hooks ensure:
it starts the recorder for the project in the background when none is running.
bir up starts it by hand, bir down stops it. Whenever
you want to know where you stand:
bir status # what is wrapped, which config scope won, and where the audit log is
bir doctor # is it working right now — exits non-zero if not
doctor is the one to trust. status tells you what is
configured; doctor tells you whether a proxy can currently reach
a control server — which is the difference between Tier 1 and Tier 2 — and
which service and account the recorder records to (recording to … as …),
with its pid and its log. recorder not running before the first session
is normal: the next session starts it.
For other MCP clients, nothing in bir knows where Cursor or Codex
keep their settings, and it does not guess:
bir install --config <path>, or
bir wrap --server-name my-server -- node my-server.js to print an
entry to paste. Those clients have no hooks, so they record at Tier 2.
Record a run worth keeping
Work normally. Two server-side facts decide whether the run becomes reusable, and both bite quietly.
A run needs more than three tool actions. Below that it is not embedded, does not enter the similarity pool, does not appear in the list, and is eventually pruned. A two-tool run will never match anything however often you repeat it.
The prompt is what gets embedded — the text you typed, not the transcript and not the answer. Write the first prompt of a reusable task as though someone else will have to phrase it again next month, because that is exactly what has to happen.
Watch it land in the audit log:
[bir] … run.start run=run_… sess=birsess_… tier=bound
[bir] … run.finish run=run_… steps=14 durationMs=48210 lossy=false recorded=true
recorded=false means the service declined the run — usually
because it matched something you already have, which is similarity detection
working. Every decision is one key=value line, and the background
recorder writes them to ~/.baseinstrunner/logs/<key>.log, one
file per project; bir status and bir doctor name the
file, and so did the log line of bir setup's summary. There
is no terminal to watch, so follow the file:
Get-Content -Wait -Tail 20 $HOME\.baseinstrunner\logs\<key>.log
tail -f ~/.baseinstrunner/logs/<key>.log
A bir-hooks run by hand in a terminal, which still works, prints
the same lines there instead — on stderr, never stdout, which
belongs to the host's JSON-RPC stream.
Calculate, then check
bir scenario list # what you have
bir scenario calc run_5f3a… # start the background job
bir scenario show run_5f3a… # poll until state leaves `calculating`
failed carries an error field; fix what it points at
and recalculate with --force. Or do all of this from the console's
recording detail page, which is the same API with the results laid out — with
one exception: the console has no Recalculate button, so a failed calculation is
run again there by preferring the recording.
Or do nothing. Once a prompt has come back three times
(SCENARIO_AUTO_MIN_HITS) and the run it keeps matching passed end
to end — it finished without an error and no tool call in it failed — the
service calculates the recording with the most to save by itself, and the
console marks it auto. A run has to have been quiet for ten
minutes first (SCENARIO_AUTO_SETTLE_MS): a session that has
just finished is often still sending steps. It never deletes anything and
never retries a failed calculation, and it works through one calculation at
a time per account.
Or say which ones to leave alone: press Switch off the
whole scenario on a recording's page and the picker never chooses it,
however often its prompt comes back, and a matching prompt is not replayed
from it either — though you can still calculate it
yourself. Or say which one you want: press ☆ Prefer
this recording on its page. It is calculated straight away and takes
nothing from anything else you hold. SCENARIO_AUTO_CALCULATE=false
leaves calculation entirely to the button.
Dry-replay before you trust it. It derives parameters from a new prompt and evaluates the stored logic against the recorded outputs — no real tool is called, nothing is billed, and it is the cheapest way to find out that step 3 computes an argument you did not expect.
bir scenario replay scn_9c1b… --prompt "check the us-east-2 fleet" --dry
If the dry replay shows a step working out the wrong thing, you do not have to recalculate the whole scenario. That one step can be corrected: see Let Claude fix a step, below (not released yet).
Turning replay on
Recording is the default. Calculated replay is installed and on as well, once
bir setup has run: it added the bir scenario server to
.mcp.json, pre-approved it in .claude/settings.local.json,
and said so at the end of its wiring step:
Wrapped 1 server.
Next: start your session here as usual. The recorder starts itself with it;
`bir up` starts it now, `bir down` stops it, `bir doctor` checks it.
Calculated replay is INSTALLED and ON. `bir replay off` turns it off here, and
`bir replay allow <servers>` narrows what it may call unattended — read
docs/calculatedReplayGuide.md §5.1 first: a replayed step is auto-approved,
and a directly executed one never reaches the permission system at all.
The switches are per project, stored so a recorder started by the hook keeps them; a running recorder reads a change when it restarts:
bir replay allow fleet,github # the servers replay may call unattended (default: all wrapped); `allow all` widens it again
bir replay off # record only; `bir replay on` brings it back
bir replay status # what is stored for this project
bir up --restart # apply a change now, rather than with the next session
By hand, on a project wired without it: bir install --replay adds
the server and the pre-approval (a flag-less bir install keeps the
way the project was installed — global, local or npx), then
bir up --restart.
A scenario is a pre-approved list of tool calls with computed
arguments. A steered step is answered
permissionDecision: "allow", so a pinned Bash or
Edit runs without the prompt you would otherwise have seen. A
directly executed step never reaches the permission system at all.
Run bir replay allow with the servers you are content to have
called unattended. Leaving it unset means every wrapped server is
eligible, and that is not the setting you want. Where a wrapped server does
things you would not want done unattended, bir replay off until
you have dry-replayed and read the scenarios.
Configuration
The switches above are stored per project and are the reliable way. The
variables below override them where the recorder is started from — for one
started by the SessionStart hook that is Claude Code's environment, not your
terminal's, so a variable set in one terminal is not seen. bir doctor
says which won (source=env, sidecar or
default).
| Variable | Effect |
|---|---|
BIR_AUTH_URL | The Basic2202 service's API address. Overrides the one bir setup stored in ~/.baseinstrunner/config.json. Neither set means nothing is recorded — everything else still works |
BIR_CONTROL_PORT | Preferred control-server port (default 53411). Each project has its own; bir setup --port <n> is the way to change it |
BIR_CORRELATION_DECISION | allow (default) or ask. Correlation auto-approves wrapped MCP calls; ask keeps the prompt, at the cost of lossier matching |
BIR_HOME | Where state lives (default ~/.baseinstrunner) |
BIR_VERBOSE=1 | Per-message detail in the audit log |
BIR_REPLAY=0 | Turn replay off. It is on once installed; bir replay off is the per-project way, and this overrides it |
BIR_REPLAY_ALLOW_SERVERS | Server keys eligible for direct execution; overrides bir replay allow. Unset means the stored list, else every wrapped server |
BIR_MIN_STEER_SIMILARITY | Minimum match similarity to replay (default 0.92) |
ANTHROPIC_API_KEY | Optional. Working out what a new request is about — which fleet, which week — is done for you by the service on its own key while the runner is signed in. Set this only to keep that step on your machine, on your key |
When it goes wrong
The governing rule is that a host session never fails because of BaseInstRunner. Every failure degrades to passthrough or to not-recording.
| Symptom | Cause | Fix |
|---|---|---|
| Nothing appears in the console | Not signed in, or no service address stored | bir setup in the project folder (or bir login on its own), then bir up --restart; check bir doctor |
tier: standalone | No recorder running for this cwd — the hook starts one with each session, so this is a session that began before bir setup, or a recorder that was stopped | bir up in the project folder, or start a fresh session; bir doctor says whether one runs |
| Recorded, but not in the list | Three actions or fewer | Nothing to fix — below the threshold on purpose |
recorded=false | The service matched it to an existing run | Working as intended |
| No prompt ever matches | Similarity detection off, or the prompts really are different | Check SIMILARITY_DETECTION_ENABLED; re-read the prompt you wrote |
| Calculate returns 503 | No Anthropic key on the server | Server-side configuration |
Sample — the fleet demo
A complete run-through against a real MCP server, on your machine, with no
cloud resources and no money spent. About ten minutes. It uses the
fleet-demo folder you built in step 2 of
Getting started: a tiny but genuine MCP server over
stdio, with two tools.
list_devices(fleet) → the fleet's devices, with risk scores
device_detail(id) → one device's detail
Step 2 can only compute its id argument from step 1's
output. That makes this the smallest honest test of whether scenario
calculation actually works, rather than the smallest test that passes.
The task: "check the eu-west-1 fleet and tell me which device is most at
risk" — which resolves, in the sample data, to dev_88f1:
edge-01, risk 0.94, firmware 3.1.2.
Set up the project
This is the
fleet-demofolder from step 2. If you have not built it yet, go back and do that first — two files and a minute. If you ranbir setupin it there, it is already wired: skip to step 2. Otherwise, in the folder:cd fleet-demo bir setupThe sign-in from Getting started is kept on this computer, so no token and no address are needed.
bir setuprewrites.mcp.jsonso thefleetserver runs throughbir-proxy, writes Claude Code's hooks into.claude/settings.local.json(kept out of git), adds and pre-approves thebirscenario server, and starts the recorder in the background; both files are generated for your machine, absolute paths and control-server port included. It keeps a copy of everything it touches, andbir uninstallputs it all back byte for byte. Replay is on from here, so step 7 needs no second install.Check the recorder
Nothing to start:
bir setupstarted the recorder, and the SessionStart hook starts it again with every Claude Code session in this folder. In the same folder,bir doctorshould saybound (Tier 1)andRecording: yes; recorder not running before the first session is normal — the session starts it, orbir upnow. If it readsstandaloneduring a session, the recorder and the session disagree about either the working directory or the port — the only two things discovery matches on.Record the run
In the same terminal, same folder, start Claude Code and ask it the task:
claudeAsk for "check the eu-west-1 fleet and tell me which device is most at risk". The two tool calls land in the audit log —
~/.baseinstrunner/logs/<key>.log, the filebir doctornames — then arun.finishline.Look at it in the console
Recordings holds the run with its generated title, step count and cost. This is the baseline: what the task costs when the agent does it.
Calculate the scenario — and read it
bir scenario list # find the run id bir scenario calc run_… bir scenario show run_…Read what comes back before going further. You are looking for three things:
- an intent that describes the task, not the specific fleet;
fleetas a parameter witheu-west-1as its sample value — if the region is baked into a step's logic, the scenario only ever answers one question;- step 2's logic reading step 1's output to pick the highest-risk id. A scenario that hard-codes
dev_88f1looks identical on a green run and is wrong the moment the data moves.
Dry-replay against a different fleet
bir scenario replay scn_… --prompt "check the us-east-2 fleet" --dryThe derived parameter should be
us-east-2. Since a dry run evaluates against recorded outputs, step 2 still picks from the eu-west-1 devices — correct behaviour, and the reason those lines are markedResult (recorded):. What you are checking is the parameter derivation and the step logic, not the answer.Replay it for real
Replay has been on since
bir setup. Narrow it to thefleetserver — the only one this scenario needs — and let the recorder read the change:bir replay allow fleet bir up --restartThen start
claudeagain and ask for "check the us-east-2 fleet and tell me which device is most at risk".Now the tools are really called.
us-east-2's highest-risk device isdev_4411—core-01, risk 0.77. That is the proof the scenario computed the answer rather than remembering it.Read the ledger
Back in the console, check three things: Saved $ is populated and the baseline's ⓘ says how many runs it is drawn from (on a first pass, one — and one sample is a guess); the headline moved under verified, not projected; and the scenario is not flagged as not paying off. Saved %/run is the same verdict per run at a glance — a green bar is a cut, a red one is a loss. On a two-step task against a local server the arithmetic is tight — on real work with real latency it is not.
The second sample. There is another demo of the same shape
with a longer chain — list_services,
service_metrics, error_logs,
recent_deploys, dependency_graph. It lives in the
runner repository, which is not public, so it is not something you can fetch
today; the point of it survives without it. Extend the fleet server
above until it has five tools, at least one of them needing an
argument computed from an earlier step's output. That is worth doing once the
fleet demo works, because a five-step scenario is where the interesting
failures start: a step whose logic reads the wrong earlier output
looks fine on two steps and falls apart on five.
Ask why — did my task use its calculated scenario?
Once a task has been recorded and replayed a few times, three questions come
up: did this run use the calculated scenario, and if not, why?
How much did it save, and why not more? What happened, step by
step? The runner answers all three with one command,
bir investigate, and Claude Code can run it for you and explain
the answer in plain words. Every finding names the problem, the cause and
the fix. Steps 1 to 4 below only read. Step 5 lets Claude fix one step of
the scenario, after your yes (not released yet).
The recorder keeps a journal of every decision it makes in
a folder — which recording a prompt matched, which gate declined and why,
whether the plan ran in direct or steer mode, what
the run cost. The service adds the recording, the scenario, the baseline
and the ledger. You see your own data; an admin sees everyone's.
Do a task twice
In a folder the runner is set up in, ask Claude Code for a task, let it finish, then ask for the same task again. The second time the audit log (
~/.baseinstrunner/logs/<key>.log) gets arun.matchedline, and eitherplan.armedorreplay.decision verdict=no-steer. That second turn is what you are about to investigate.Ask Claude Code why
Do this. In the same Claude Code session, in the same folder, paste:
Run `bir investigate` in this folder and explain in plain words: did my last task run its calculated scenario, and if not, why? If it did, how much did it save and why not more? List the fixes it suggests. Do not run any fix — just report.You should see Claude run the command and report on it. The command's own output looks like this (a real run from the fleet demo):
Turn run_0544… 2026-09-20 12:19:18Z session e5b66bfb prompt Give me a full detail report for every device in eu-west-1 matched run_be1c… (similarity 1.000) → scenario scn_4641… plan steer, 4 steps Bash:live Bash:live Bash:live Bash:live replay steered_full 4/4 steps in 27.3 s reported steered_full derive $0.0004 session $0.4482 fallback $0.0000 → saved $0.0910 (16.9% of $0.5396) finished 8 steps, 33.6 s, cost $0.4482, not recorded Runner findings 1. [warn] The plan armed in steer mode: 4 of 4 steps are reachable only inside the session (Bash). why: In steer mode the runner pins each call's inputs … the whole live turn is still paid for. fix: Put the data those tools reach behind an MCP server, wrap it (`bir install --server <name>`) … 2. [warn] Saved 16.9%: $0.0910 of a $0.5396 baseline. From the service (execution sexec_…), owner you: recording run_be1c… listed, embedded 4 tool calls (Bash) cost $0.5397 hits 5 scenario scn_4641… ready 4 steps (Bash) baseline $0.5397 from 1 sample(s) Service findings 1. [warn] 4 of 4 steps use built-in tools (Bash), so this scenario can only run in steer mode. 2. [info] The baseline is one run ($0.5397), so every percentage is against that single measurement.Claude's summary of that would be: the task did use its scenario, but in steer mode, because every step is a Bash command; the model still paid for the whole turn, so it saved 17%. To save more, put the fleet data behind an MCP server and wrap it.
If it goes wrong.
No turns in the journal for this directory: the recorder was not running in this folder when the task ran —bir doctorsays whether it is now; run the task again once it is — or the runner is older than this feature: paste the line from the console again (npm i -g @basein/runner@latestonly if you installed by hand) and check thatbir --helplistsinvestigate.service not consulted: not signed in — run `bir login`: the runner is not signed in on this computer;bir setupin this folder signs it in (orbir loginon its own).the service has nothing for …: the run belongs to another account, or the service is older than the runner.Ask about one run, or about the money
The same command takes an id, so Claude can be pointed at a specific run, scenario or execution. Useful prompts:
- "Run
bir investigate listand tell me which turns were steered and what each saved." - "Run
bir investigate scn_…and explain why the saving is low and how to fix it." - "Run
bir investigate executions --jsonand show me the executions that failed, with the step that broke." - "Run
bir investigate run_…— why was this recording never calculated?"
Everything Claude runs for these prompts only reads: it changes nothing. When a finding says fix: recalculate or fix: wrap the server, that is a command you choose to run next — or ask Claude to. To let Claude change a step of the scenario itself, see step 5.
- "Run
Without Claude Code
The command works on its own in any terminal in the folder, with the recorder running or not:
bir investigate # the newest turn in this folder bir investigate run_… | scn_… | sexec_… # one run, scenario or execution bir investigate list --limit 10 # recent turns and their verdicts bir investigate executions # the ledger, newest first bir investigate executions --user ada@example.com # admins onlyAdd
--jsonto any of them for the raw data.
| The finding says | It means | Fix |
|---|---|---|
declined_not_ready | the prompt matched a recording that has no ready scenario yet | wait for the automatic calculation (three similar prompts), or bir scenario calc run_… |
declined_similarity | a hit, but below the runner's steering threshold (0.92) | phrase the prompt closer to the recorded one |
declined_coverage | none of the scenario's tools is reachable in this session | bir install --server <name>, start a fresh session |
steer_mode / steer_only | some steps are built-in tools (Bash, Read…), so the model still runs the whole turn | put that data behind an MCP server, wrap it, record the prompt again |
saving_low / saving_negative | the replay cost most of, or more than, the baseline | the finding names the dominant cost — session, fallback or derivation — and its fix |
baseline_single | the % is against one measured run | let the agent run the task a few times unsteered (bir replay off, then bir up --restart; bir replay on afterwards) |
run_below_threshold | fewer than four tool calls, so the run is not a recording | record a longer task, or mark it preferred |
awaiting_hits | 2 of 3 similar prompts so far | run it once more, or calculate now |
execution_failed / fell_back | a step broke, and where | the fix for that stage: recalculate, check the tool, check the target |
step_nondeterministic | a step needs a judgement: its code could not work out what to send, so every replay hands over to the agent in front of it | recalculate once (bir scenario calc scn_… --force, with the id the finding prints; it adds --discard-edits when steps were edited by hand); if it is still marked, fix the step (step 5). A step you marked yourself (--freeze) is unmarked with bir scenario edit scn_… --step n --unfreeze |
step_edited | a step's code or mark was changed by hand, with bir scenario edit | nothing to fix. bir scenario edits scn_… shows the change; bir scenario undo scn_… --step n takes it back |
step_edit_forced | a step was saved with --force, although its check against the recording failed | read the note with bir scenario edits scn_…; take it back with bir scenario undo scn_… --step n, or save a version that passes the check |
Let Claude fix a step
Not released yetThis is built in the service and in the runner. It arrives with the next version of the runner (
@basein/runner) and the next update of the service. Until then it does not work. A runner of version 0.2.10 or older does not knowscenario check. Version 0.2.11 lists an early form of it, but the service does not answer it yet: you get(not_found)or not yours.Sometimes a finding is about one step of the scenario. The most common one is
step_nondeterministic: Step 4 (mcp__postgres__execute_sql) needs a judgement, so runs hand over in front of it. Every replay then stops before that step and the agent does the rest, so the saving from that step on is lost. Often the cause is a small mistake in the step's code, and Claude can fix it.A step's code is a few lines of JavaScript that work out what the step sends. In the hiring-year task of 2026-09-25, step 4 had this code:
return {"sql":"SELECT * FROM employees.hiring_year_summary ORDER BY hire_year;"};That is a copy of the old command. It sends the same table every time, whatever the new request says. The fix builds the command from the request:
return { sql: "SELECT * FROM " + parameters.summary_table + " ORDER BY hire_year;" };The service tries every change against the recording before it keeps it. It saves the change only if the new code, given the recording's values, makes the same call the recording made, and is not just a copy of it like the old code above. Only you can change your scenarios: not another account, and not an administrator.
The new code runs at replay without asking you, exactly like the code the calculation wrote. The check looks only at what the code sends for the recording's values. Code that does something more in another case (for example, reads a file or calls a website only when a value is different) can still pass the check. So read the code before you say yes (What runs without asking you, the last point).
Do this. In the same Claude Code session, in the same folder, paste:
First run `bir investigate` in this folder; that only reads. If a step needs a judgement, or works out the wrong input, fix that step: 1. Read it with `bir scenario show <scenario> --step <n>`. 2. Write the corrected input logic to a file. 3. Check it: `bir scenario check <scenario> --step <n> --input-logic <file>`. Show me what the check printed, and wait for my yes. 4. Only if the check says "reproduces the recorded call", save it: `bir scenario edit <scenario> --step <n> --input-logic <file> --note "<why>"`. Always give --note. Never use --force. If the check refuses the change, tell me why, fix the code and check again.You should see Claude run
bir investigate, thenbir scenario show, then the check.bir scenario showprints the step's code, with the scenario's id and itschainRevisionat the top. The check prints this:Step 4 (mcp__postgres__execute_sql) — checked against run_67afb010-… input reproduces the recorded call later not affected: only the input logic changed mark needs a judgement (input_mismatch) → runs by itself Nothing saved. To save: bir scenario edit scn_4a17a126-… --step 4 --input-logic step4.jsIn plain words:
input reproduces the recorded call: given the recording's values, the new code makes the same call the recording made. This is the test that matters.later not affected: only the code that works out what this step sends has changed. The steps after it use what this step gets back, and that did not change. (When a change can reach the later steps, the check tests them too, and the final answer.)mark … → runs by itself: the step no longer needs a judgement. Replays will run it instead of handing over to the agent.Nothing saved: a check never changes anything.
When you say yes, Claude saves it:
Step 4 (mcp__postgres__execute_sql) — checked against run_67afb010-… input reproduces the recorded call mark needs a judgement (input_mismatch) → runs by itself Saved as sedit_633dba0d-bbbe-4517-ac29-74cc85a3abda (revision 2). Undo: bir scenario undo scn_4a17a126-… --edit sedit_633dba0d-bbbe-4517-ac29-74cc85a3abdaFrom then on, replays no longer hand over in front of that step. (A replay still hands over at an earlier step that needs a judgement. In the hiring-year task step 3 did too, and it was fixed the same way.)
bir investigatenow says the step was edited by hand (step_edited), and the console shows an edited by hand badge on the step.A change that does not pass is not saved. The check says where it differs. Here the new code put the schema name in twice (
employees.employees):Step 4 (mcp__postgres__execute_sql) — checked against run_67afb010-… input differs from the recorded call at character 32 computed … FROM employees.employees.hiring_year_summary ORDER BY h… recorded … FROM employees.hiring_year_summary ORDER BY hire_year;"… later not affected: only the input logic changed mark needs a judgement (input_mismatch) → stays marked Nothing saved, and `bir scenario edit` would refuse it: the change does not reproduce the recording. Fix the code and check again. If the recording itself was wrong, edit can save it on purpose: add --force --note "why".To undo it.
bir scenario edits scn_… # every change: when, which step, and the note bir scenario undo scn_… # take back the newest change bir scenario undo scn_… --step 4 # take back the newest change of step 4 onlyUndo walks back. Run
bir scenario undoagain, and it takes back the change before that one. Keep going, and the steps get the calculation's code back. In the hiring-year task, step 3 was fixed the same way, after step 4. Two undos took back step 3, then step 4:$ bir scenario undo scn_4a17a126-… Step 3 (mcp__postgres__execute_sql) — checked against run_67afb010-… input back as it was before that edit mark runs by itself → needs a judgement (input_mismatch) Step 3 is back as it was before sedit_02cb1210-1938-4902-9395-5a82b321276d (revision 4). Redo: bir scenario undo scn_4a17a126-… --edit sedit_1bcba735-a526-4a1c-a890-261addf2d82b One further back: bir scenario undo scn_4a17a126-… $ bir scenario undo scn_4a17a126-… Step 4 (mcp__postgres__execute_sql) — checked against run_67afb010-… input back as it was before that edit mark runs by itself → needs a judgement (input_mismatch) Step 4 is back as it was before sedit_633dba0d-bbbe-4517-ac29-74cc85a3abda (revision 5). Redo: bir scenario undo scn_4a17a126-… --edit sedit_8d3da392-1d96-4b2e-8c5d-34f8ab668b53 One further back: bir scenario undo scn_4a17a126-…To bring a change back, run the
Redo:command. It undoes the undo.An undo is checked like a change. If taking a change back would break a later step or the final answer, the undo is refused, and nothing changes. You can undo it anyway with
--force --note "why". That is your decision, not Claude's.Let Claude use tools instead of the terminal (optional). Claude can always run
birin its terminal, where you see each command. Thebirserver in Claude Code can also give Claude the same actions as tools. The tools that only read (scenario_show,scenario_edits,investigate) are always there. The tools that change a scenario (scenario_check,scenario_edit,scenario_undo) are there only in a project where you switched them on:bir scenario editing on # this project only bir scenario editing status # is it on here? bir scenario editing off # take the tools away againThen quit Claude Code and start
claudeagain. A session that is already running does not see the change.The fixing is never recorded. The runner does not record its own
birtools: thebirserver's tools (exceptrun_scenario), and terminal commands (Bash or PowerShell) that only runbir. So a session that fixes a plan does not become a scenario of its own. A command that mixesbirwith other work, such asnpm test && bir investigate, is still recorded.What a recalculation does to a fix. A recalculation builds every step again from the recording, so it would throw the fix away. So it refuses:
$ bir scenario calc run_67afb010-… --force Not recalculated: steps 3, 4 were edited by hand, and a recalculation rebuilds every step. To recalculate anyway and discard the edits: bir scenario calc run_67afb010-… --force --discard-edits (the edits stay in `bir scenario edits`, marked replaced)Add
--discard-editsonly when you want the calculation's code back. You can also give the scenario's own id:bir scenario calc scn_… --force --discard-edits. That works for every scenario, and it is the only way from the terminal for a sub-task's scenario. The service's own automatic repair also leaves a scenario with edits alone, andbir investigatesays so (repair_skipped_edits).Saving on purpose. Sometimes the recording itself was wrong, and no correct code can match it. Then you can save the change anyway, with
bir scenario edit … --force --note "why". If the check found any problem, the save is marked as forced. The step shows accepted on purpose, andbir investigatekeeps saying so (step_edit_forced). Do this yourself, after you have read the code. Do not leave it to Claude.If it goes wrong. Each point but the last starts with words that
birprints.usage: bir scenario <list|show|calc|replay> …afterbir scenario check. Your runner is older than this feature. Once it is released, paste the line from the console again (installed by hand:npm i -g @basein/runner@latest).the change does not reproduce the recording. The new code makes a different call. Claude should compare thecomputedandrecordedlines, fix the code, and check again.is a copy. The code returns the old values whatever the new request says. It would do the old job every time, so it is refused. The code must use the request's values (parameters) or what earlier steps returned.too costly to check. Checking the code took more than its time (5 seconds of running code, at the default). Make the code do less, and check again.Nothing would change(from a check) orNothing to change(from an edit). The step already has this code. Nothing to do.(not_a_tool_step). The step is a call to a sub-task. Only tool steps can be changed.comes after a sub-task call. The check cannot rebuild what the step reads, so it can only be saved with--force. That is your decision, not Claude's.(revision_changed). The scenario changed since Claude read it. Read the step again, and check again.(not_ready)or(scenario_calculating). The scenario is not ready, or a calculation of it is running. Wait for it to finish, then check again.(rate_limited). More than 30 checks, edits and undos in a minute. Wait a minute.(not_found)ornot yours. The id is wrong, or the scenario belongs to another account. Until this feature is released, the service itself answers this way.(logic_sandbox_unavailable). The service could not run step code just then. Nothing was checked, saved or undone. Try again in a minute.(invalid_input). Something in the request is not valid, for example code over 20 000 characters or a note over 500. The words after it say what.(payload_too_large). The request is far too large. A step's code may be at most 20 000 characters, and a note 500.Not undone:withafter this undo. Taking the change back would break a later step or the final answer, so it was refused. The message names what would break. Fix or undo that first, or leave this change as it is.(not_latest_edit). A newer change of the same step came after it.birprints two commands: run them in that order.(already_undone). That change was taken back already. To bring it back, run the commandbirprints (it undoes the undo).Nothing to undo. There is no change left to take back.- The tools do not appear after
bir scenario editing on. Restart Claude Code.
Videos
One of the four is recorded. Video 2, the install, was filmed on 2026-09-12 and is embedded at the top of Getting started — 7 min 54 s, silent, captioned. The other three are still shot lists and narration, written so somebody can sit down and record them without having to invent the structure first.
Common conventions, so the four feel like one set:
- 1280×720, terminal at a size legible on a phone.
- Light theme in the console — it is the default and it screenshots better.
- No music. Voice-over or captions, not both.
- Real data from the fleet sample. Never a mock-up of a screen that does not exist.
- Cut every wait longer than two seconds. Calculation takes about ten; nobody needs to watch it.
| Time | Shot | Narration |
|---|---|---|
| 0:00 | Terminal: the same task run twice, cost visible both times | "Your agent solved this yesterday. Today it paid full price to solve it again." |
| 0:15 | The record → calculate → replay diagram | "Basic2202 records the run, derives the plan, and runs the plan next time." |
| 0:35 | Console: the recording, then its scenario — intent and parameters | "A transcript says what the agent did. A scenario says what it was doing." |
| 0:55 | Dashboard, verified tile, ⓘ open | "And it measures what that saved. The number it leads with is the one it watched move." |
| 1:15 | Recordings list, the savings column | "It is free to use. What we sell is running the whole thing inside your own network, when the recordings cannot leave it." |
The guide, executed. One terminal, one browser window.
Recorded — the film in Getting started. It was filmed before the one-line path existed and follows this list loosely: the demo project and one run of the task by hand come first, then the install and the sign-in by hand and the recorder in a window of its own, and it runs on past the first recording to the scenario being calculated and the saving being verified. It is silent, with the step being done named in the corner, so it was cut without narration; the shot list below is the retake, with the line.
- Paste the line from the console's Set up the runner page — say what it installs, what
bir setuprewrites, and thatuninstallreverses it byte for byte. bir doctor. Point atbound (Tier 1)and the recorder in the background with its pid and log; say what Tier 2 would mean, and why this is the command to trust, notstatus.- Run the fleet task. Watch
run.startandrun.finishland in the audit log. - Console → Recordings → the row. Name the columns: Steps, LLM $/run, and the baseline's sample count.
- Close on the threshold: "three tool calls or fewer and it is not listed — there is no plan in a single call."
The video that earns its place, because this is where people get burned.
scenario calc, thenscenario show. Read the intent aloud.- Stop on the parameters.
fleet: eu-west-1— a sample value, not a constant. - Stop on step 2's logic. Show it reading step 1's output. Then show, by hand, what a hard-coded
dev_88f1would look like: identical on this run, wrong on the next. - Dry replay against
us-east-2. ExplainResult (recorded):and why a dry run still shows eu-west-1 devices. - Real replay.
dev_4411. "It computed that. It did not remember it." - Close on the ledger row and the not-paying-off flag: "a scenario can cost you money, and this is where it says so."
- The sidebar: the sections, and what each one is for.
- ⌘K, including the recordings-search row. Say plainly that it does not search everything.
- Recordings: verified versus projected, with the ⓘ open. The segment worth over-explaining.
- The Saved %/run bars: read one row's two $/run figures against its percentage, then show a red one.
autoin the origin column, and the ★ Preferred and ⊘ Switched off badges under two titles: a row the service calculated by itself, one the user preferred over it, and one they switched off.- The delete confirmation, naming the savings that will leave the headline.
They live in videos/ beside this page, deployed with it, and are
embedded beside the section each one covers; the console embeds them from this
address, VITE_DOCS_URL. The install film is there. Until the others
exist, their written sections stand on their own — which is why they were
written to.
Security and privacy
What Basic2202 sees, where it goes, how long it stays, and what it does without asking you. This section describes what the software does today, in plain words.
The formal version is the Privacy Policy on the website; where the two differ, the policy is the one that counts. Terms of service will be published separately.
The runner records what your agent did: the request you typed, every tool call with its arguments and results, the model's reasoning before each call, the final answer, and what it all cost. It strips the secrets it recognises, then sends the rest over HTTPS to your Basic2202 account, where you can read it, and so can an administrator of the service, read-only (see Who can read them below). Working out a plan from a recording sends that recording to Anthropic. Nothing is sold, nothing is used for advertising, and no analytics run anywhere. You can delete a recording at any time, and everything derived from it goes with it.
What is recorded
One recording is one run of your agent: from the request you typed to the answer it gave. For each run the runner keeps:
- The request you typed, word for word.
- Every tool call: the tool's name, the arguments the agent sent, and what came back, or the error if it failed. Calls to the project's MCP servers pass through the runner itself. The agent's built-in tools, such as reading a file or running a shell command, are seen through Claude Code's hooks, which is why the recorder (
bir-hooks) has to be running —bir setupstarts it, and the SessionStart hook starts it with every session. - The model's reasoning written just before each tool call. The runner reads it from the session's transcript file on your own machine and attaches it to the step.
- The final answer, the model used, token counts, timings and the cost.
- Run details: which recorder made it, whether the trace is complete or MCP-only, the host application, the names of the wrapped servers, whether any steps were dropped, the session id, and the path of the project folder, which can include your username. An MCP-only run also carries your computer's name.
A recording can therefore contain anything a tool read or returned: the contents of a file the agent opened, the rows a query returned, a page a browser tool fetched. Record only what you are comfortable storing.
What is not recorded. Your password: you never type it into the terminal, because bir login opens the browser, where you approve a code, or takes a one-time setup token made in the console. Anything the agent did not do: the runner does not scan your disk or watch other programs, and sees only what passed through a wrapped server or a hook, in a folder you ran bir setup in. Sessions in other folders: each project is recorded only while its own recorder is running there, and only a folder that was set up has one.
What is stripped before sending. Redaction runs on your machine, on the copy that is about to be sent, before it is even queued:
- By name. A value stored under a name that looks secret is replaced with
[redacted], whatever it is:token,secret,password,api_key,authorization,cookie,session_id,credential,private_key,access_key,client_secretand their variants. - Every value under an
envblock, because MCP server configurations put credentials there under any name at all. - By shape, wherever it appears, including inside free text: bearer tokens, JWTs,
sk-keys of the OpenAI and Anthropic kind, GitHub, Slack and Google API keys, and AWS access key ids. - Size. A single string is cut at 8 KB and a step at 64 KB. A file a tool returned in full arrives as its first 8 KB.
This is pattern matching, not understanding. A secret stored under an unusual name, or shaped like ordinary text, goes through. Treat a recording the way you would treat a log file from your own machine, and do not wrap a tool that handles data you are not allowed to keep.
Where it goes
| Who | What they receive | When |
|---|---|---|
| Basic2202's server | The recording, as described above, over HTTPS | Every recorded run |
| Anthropic | The whole recording: reasoning, every tool input and output, and the final answer | When a scenario is calculated, by you or automatically |
| Anthropic | The first 1,200 characters of the request, the names of the tools used, and a short excerpt of the first reasoning. Tool outputs and the final answer are deliberately left out | When a run finishes, to write its title |
| Anthropic | The new request, the scenario's intent and its parameter samples, and — on a plan armed mid-task — the agent's reasoning, its pending call and its last few tool results | On a replay, so the service can work out what the request acts on. Nothing is stored. A runner with its own ANTHROPIC_API_KEY does this reading on your machine and your key instead, and never asks the service |
| PayPal | The invoice reference, the amount and the service's name. You pay on PayPal's own pages; Basic2202 never sees a card number | Only if an invoice on this service is paid through PayPal. Using the service here is free, and In-house is paid on your AWS bill, so today nothing is sent |
| That you signed in here; Basic2202 receives your Google id, email address and name | Only if you sign in with Google | |
| Resend | Your email address and the one-time link | Verification and password-reset emails |
| Google Fonts | The IP address and browser of whoever opens the page | This handbook site only. The console loads nothing from anyone else |
- In transit. The runner talks to
api.bi2202.comover HTTPS only, and refuses to follow a redirect to a different site while carrying your token. - At rest. Recordings are stored in a database on one server, rented from Hetzner in Germany. The application does not encrypt them itself; they are protected by the server and its disk.
- Who can read them. You, signed in to your own account, and an administrator of the service: the Users screen opens any account's recordings, in full, read-only. An administrator cannot change, calculate, replay or delete a recording from there, and every time one is opened the service logs who looked at whose. The person who operates the server can read the database and the logs directly.
- Recognising a repeated request is done with a small open-source model that runs on Basic2202's own server. Nothing leaves for that.
- No analytics, no tracking pixels, no advertising scripts, in the console, the runner or this handbook.
How long it stays, and how to delete it
- A recording stays until you delete it, from its own page in the console. Deleting it also deletes its steps, its fingerprint, its scenario and its replay history, and the savings it booked leave your totals with it.
- Very short runs, a couple of tool calls, that were never calculated and never matched are pruned automatically.
- A scenario does not expire. It stays with its recording, and steers a matching prompt, until the recording is deleted or its owner switches calculation and replay off.
- Sign-in records keep the time, the method, the IP address and the browser or runner for every sign-in attempt, successful or not. They are kept for as long as the service runs, and your own are shown on your Sign-in history page. A
bir loginapproval shows the runner's address on the approval page and is pruned a day later; a setup token made in the console is the same kind of record. - Sessions last thirty days from their last use. An access token lasts a day, a verification link a day, a password-reset link an hour, and a sign-in code ten minutes.
- Closing your account, from Account settings, removes your profile, your password, your Google link and your sessions. It does not yet remove your recordings, scenarios, replay history or sign-in records. Delete your recordings first, and write to us for the rest.
- On your own machine the runner keeps your session in
~/.baseinstrunner/credentials.json, readable by your user only, and a copy of every configuration file it changed, so thatbir uninstallcan put them back.bir logoutrevokes the session. - Server logs hold, for every request, the method, path, status, duration, IP address and, when signed in, your user id. Secrets are masked before they are written. Backups, when they are taken, can hold deleted data until they expire.
What runs without asking you
Four things happen without a prompt, and each is a switch you own.
Wrapped tool calls are approved automatically while recording
To attach a call id to a tool call, the runner answers Claude Code's permission hook with allow. So a call to a wrapped MCP server that you would otherwise have been asked about is not asked about. Set
BIR_CORRELATION_DECISION=askwhere the recorder is started from — Claude Code's environment, for one the hook starts — thenbir up --restart, to keep the prompts at the cost of less exact matching.Replay is on once the runner is set up, and it is a switch
bir setupinstalls the scenario server, and replay with it;bir replay offturns it off for that project (BIR_REPLAY=0in the recorder's environment does the same). Nothing is replayed in a project that was never set up. When it is on, a steered step is approved for the agent, and a step the runner executes directly never reaches the permission system at all. Name the servers you are content to have called that way withbir replay allow <servers>(orBIR_REPLAY_ALLOW_SERVERS), and keep anything destructive, or anything that must not run unattended, off that list.Calculation can start by itself
Once the same request has come back three times, the service works out the plan for the best candidate without anyone pressing the button, which sends that recording to Anthropic as described above. Every recording's page has a switch that turns calculation and replay off for that recording.
Step code that you or Claude change runs like calculated code
(Not released yet.) Each step of a scenario is a few lines of JavaScript that work out what the step sends. You can change a step with
bir scenario edit, or let Claude do it for you (Let Claude fix a step). The new code then runs at replay on your machines, unattended, exactly like the code the calculation wrote. On your own machine the runner runs it with no sandbox, as it always has for calculated code, so read what Claude wrote before you agree. The limits are these:- The service saves a change only if it makes the same call the recording made, and is not just a copy of it. Saving anything else needs
--forceand a note saying why, andbir investigatekeeps pointing it out. - The check looks only at what the code sends for the recording's values. Code that does something more in another case (for example, reads a file or calls a website only when a value is different) can pass the check, and then do that on your machine. That is why you read it.
- Only the scenario's owner can change it. An administrator cannot.
- Claude gets tools to change steps only in a project where you ran
bir scenario editing on.bir scenario editing offtakes them away. Claude can still runbirin its terminal, where you see each command. bir scenario editslists every change, when it was made and the note that says why.bir scenario undotakes back the newest change; run it again to go one further back.- The fixing itself is not recorded. The runner never records its own
birtools (exceptrun_scenario, which runs a plan), or terminal commands that only runbir. - On the service, all step code runs in a sandbox, whoever wrote it. Each piece of code runs in a fresh, empty space inside a separate program (the evaluator). That program has none of the service's secrets, cannot read or write files, and cannot start other programs. The code gets only JavaScript's own built-ins: no network and no timers. It has a time limit and a memory limit (
LOGIC_TIMEOUT_MS,LOGIC_MEMORY_MB). Code that goes past one is stopped, and the same code is refused at once for the next 10 minutes. The check of a change runs apart from the part of the service that answers other requests. It stops after about 5 seconds of running code (EDIT_CHECK_BUDGET_MS), and the change is then refused as too costly to check.
- The service saves a change only if it makes the same call the recording made, and is not just a copy of it. Saving anything else needs
How the service is protected
- Passwords are stored as argon2id hashes. Session, verification and reset tokens are random, stored only as hashes, and rotated when used.
- An email address must be verified before it can sign in. Sign-in and account routes are rate-limited per address.
- Everything is served over TLS, with strict transport security. The console loads scripts from its own origin only, and the API accepts browser requests from the console's origin only.
- The API runs as an unprivileged user in a hardened system service that can write to one directory.
- The runner package has no third-party dependencies at run time, so nothing else is loaded into your agent's process tree.
Your side of it
- You choose what is recorded: which project you run
bir setupin, which servers, and whether its recorder runs (bir downstops it;bir uninstalltakes it out). Do not wrap a tool that returns secrets or personal data you are not allowed to store. - Delete what you no longer need. A recording is as sensitive as the tools it called.
- Keep replay off destructive tools. A replayed scenario is a list of tool calls you recorded, run again with computed arguments. What those calls do is yours.
- On a shared machine, run
bir logoutwhen you finish.
Reporting a security problem
Write to support@bi2202.com, the address on the Privacy Policy. Describe what you found and how to reproduce it, and please do not post it publicly before we have replied.
This section describes the runner at version 0.2.0 and the service as deployed on 2026-09-11. It is a statement of practice, not a contract; the Privacy Policy governs, and terms of service will be published separately. When the software changes in a way that matters here, this section changes with it.