@basein/runner 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +276 -0
- package/dist/auth/client.d.ts +85 -0
- package/dist/auth/client.js +284 -0
- package/dist/bin/bir-hooks.d.ts +48 -0
- package/dist/bin/bir-hooks.js +201 -0
- package/dist/bin/bir-proxy.d.ts +45 -0
- package/dist/bin/bir-proxy.js +207 -0
- package/dist/bin/bir-scenario.d.ts +24 -0
- package/dist/bin/bir-scenario.js +177 -0
- package/dist/bin/bir.d.ts +21 -0
- package/dist/bin/bir.js +876 -0
- package/dist/config/adapters/claude-code.d.ts +76 -0
- package/dist/config/adapters/claude-code.js +181 -0
- package/dist/config/adapters/generic.d.ts +17 -0
- package/dist/config/adapters/generic.js +36 -0
- package/dist/config/generate.d.ts +127 -0
- package/dist/config/generate.js +114 -0
- package/dist/config/resolve.d.ts +68 -0
- package/dist/config/resolve.js +132 -0
- package/dist/control/client.d.ts +56 -0
- package/dist/control/client.js +86 -0
- package/dist/control/correlation.d.ts +86 -0
- package/dist/control/correlation.js +0 -0
- package/dist/control/discovery.d.ts +50 -0
- package/dist/control/discovery.js +123 -0
- package/dist/control/ordering.d.ts +38 -0
- package/dist/control/ordering.js +44 -0
- package/dist/control/paths.d.ts +32 -0
- package/dist/control/paths.js +56 -0
- package/dist/control/server.d.ts +272 -0
- package/dist/control/server.js +1131 -0
- package/dist/control/transcript.d.ts +75 -0
- package/dist/control/transcript.js +241 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +32 -0
- package/dist/jsonrpc/framing.d.ts +49 -0
- package/dist/jsonrpc/framing.js +143 -0
- package/dist/jsonrpc/types.d.ts +52 -0
- package/dist/jsonrpc/types.js +46 -0
- package/dist/proxy/intercept.d.ts +55 -0
- package/dist/proxy/intercept.js +147 -0
- package/dist/proxy/relay.d.ts +97 -0
- package/dist/proxy/relay.js +166 -0
- package/dist/proxy/session.d.ts +116 -0
- package/dist/proxy/session.js +319 -0
- package/dist/record/housekeeping.d.ts +34 -0
- package/dist/record/housekeeping.js +39 -0
- package/dist/record/queue.d.ts +48 -0
- package/dist/record/queue.js +96 -0
- package/dist/record/recorder.d.ts +111 -0
- package/dist/record/recorder.js +39 -0
- package/dist/record/redact.d.ts +37 -0
- package/dist/record/redact.js +119 -0
- package/dist/record/remote-recorder.d.ts +110 -0
- package/dist/record/remote-recorder.js +301 -0
- package/dist/record/truncate.d.ts +36 -0
- package/dist/record/truncate.js +85 -0
- package/dist/replay/bundle.d.ts +36 -0
- package/dist/replay/bundle.js +89 -0
- package/dist/replay/controller.d.ts +300 -0
- package/dist/replay/controller.js +807 -0
- package/dist/replay/coverage.d.ts +41 -0
- package/dist/replay/coverage.js +56 -0
- package/dist/replay/derive.d.ts +58 -0
- package/dist/replay/derive.js +166 -0
- package/dist/replay/executor.d.ts +78 -0
- package/dist/replay/executor.js +233 -0
- package/dist/replay/logic.d.ts +31 -0
- package/dist/replay/logic.js +50 -0
- package/dist/replay/plan.d.ts +181 -0
- package/dist/replay/plan.js +397 -0
- package/dist/replay/pricing.d.ts +41 -0
- package/dist/replay/pricing.js +76 -0
- package/dist/replay/source-run.d.ts +50 -0
- package/dist/replay/source-run.js +98 -0
- package/dist/replay/tool-error.d.ts +22 -0
- package/dist/replay/tool-error.js +60 -0
- package/dist/replay/types.d.ts +116 -0
- package/dist/replay/types.js +35 -0
- package/dist/upstream/client.d.ts +78 -0
- package/dist/upstream/client.js +114 -0
- package/dist/upstream/http-client.d.ts +78 -0
- package/dist/upstream/http-client.js +261 -0
- package/dist/upstream/lazy-client.d.ts +31 -0
- package/dist/upstream/lazy-client.js +53 -0
- package/dist/upstream/stdio-client.d.ts +57 -0
- package/dist/upstream/stdio-client.js +203 -0
- package/dist/util/log.d.ts +27 -0
- package/dist/util/log.js +51 -0
- package/dist/util/version.d.ts +2 -0
- package/dist/util/version.js +40 -0
- package/docs/BaseInstRunner.md +621 -0
- package/docs/calculatedReplay.md +1185 -0
- package/docs/calculatedReplayGuide.md +448 -0
- package/docs/installRun.md +413 -0
- package/docs/mcpmark.md +752 -0
- package/docs/quickstart.md +201 -0
- package/docs/t-bench.md +394 -0
- package/package.json +56 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* paths — where BaseInstRunner keeps its state on disk.
|
|
3
|
+
*
|
|
4
|
+
* One module so the discovery file, the credentials file and the install sidecar
|
|
5
|
+
* cannot drift apart, and so tests can redirect all three at once with
|
|
6
|
+
* `BIR_HOME`. Everything lives under `~/.baseinstrunner` (mode 0700), and no
|
|
7
|
+
* assumption is made about `$HOME` being set — Windows sets `USERPROFILE`
|
|
8
|
+
* instead, and `os.homedir()` already knows the difference.
|
|
9
|
+
*/
|
|
10
|
+
import { homedir } from "node:os";
|
|
11
|
+
import { createHash } from "node:crypto";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import { mkdirSync } from "node:fs";
|
|
14
|
+
/** Root of BaseInstRunner's state. `BIR_HOME` overrides it (tests, sandboxes). */
|
|
15
|
+
export function configDir() {
|
|
16
|
+
return process.env.BIR_HOME ?? join(homedir(), ".baseinstrunner");
|
|
17
|
+
}
|
|
18
|
+
/** Where control servers advertise themselves, one file per project cwd. */
|
|
19
|
+
export function controlDir() {
|
|
20
|
+
return join(configDir(), "control");
|
|
21
|
+
}
|
|
22
|
+
/** The sidecar holding original MCP entries so `bir uninstall` can restore them. */
|
|
23
|
+
export function installedPath() {
|
|
24
|
+
return join(configDir(), "installed.json");
|
|
25
|
+
}
|
|
26
|
+
/** Create a directory with restrictive permissions. Idempotent, never throws. */
|
|
27
|
+
export function ensureDir(dir, mode = 0o700) {
|
|
28
|
+
try {
|
|
29
|
+
mkdirSync(dir, { recursive: true, mode });
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
/* already there, or unwritable — callers surface the later failure */
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Separator- and case-normalised form of a path.
|
|
37
|
+
*
|
|
38
|
+
* Claude Code stores project keys with forward slashes on Windows while
|
|
39
|
+
* `process.cwd()` returns backslashes; comparing the literal strings is the
|
|
40
|
+
* number one cause of "the proxy is installed but never runs" (Phase 7.1). The
|
|
41
|
+
* same normalisation keys the discovery file, so a proxy started from
|
|
42
|
+
* `C:\proj` finds the control server registered by a hook reporting `C:/proj`.
|
|
43
|
+
*/
|
|
44
|
+
export function normalizePath(p) {
|
|
45
|
+
const slashed = p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
46
|
+
return process.platform === "win32" ? slashed.toLowerCase() : slashed;
|
|
47
|
+
}
|
|
48
|
+
/** The discovery key for a project directory: `sha256(normalised cwd)[0..16]`. */
|
|
49
|
+
export function controlKey(cwd) {
|
|
50
|
+
return createHash("sha256").update(normalizePath(cwd)).digest("hex").slice(0, 16);
|
|
51
|
+
}
|
|
52
|
+
/** Absolute path of the discovery file for `cwd`. */
|
|
53
|
+
export function discoveryPath(cwd) {
|
|
54
|
+
return join(controlDir(), `${controlKey(cwd)}.json`);
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* control/server — the local control plane (Phase 5) and the Tier 1 recorder.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS EXISTS AT ALL (§3.2). Three proxies recording independently would
|
|
5
|
+
* produce three interleaved step streams with no shared ordering, and would
|
|
6
|
+
* double-record every call the hook also sees. One control server gives one
|
|
7
|
+
* `stepIndex` sequence, one run lifecycle, one HTTP chain to the recorder, and
|
|
8
|
+
* one place to dedupe.
|
|
9
|
+
*
|
|
10
|
+
* It is two things in one process, deliberately: the **Claude Code hook
|
|
11
|
+
* receiver** (Claude Code POSTs each lifecycle event to an HTTP hook) and the
|
|
12
|
+
* **control plane** the proxies report to. They share the run, so they must
|
|
13
|
+
* share the process.
|
|
14
|
+
*
|
|
15
|
+
* RUN BOUNDARIES follow RRepeat's: a Claude Code *session* holds many prompts,
|
|
16
|
+
* and each `UserPromptSubmit → Stop` turn is one run. Creation is deferred to
|
|
17
|
+
* the first prompt so the server's similar-prompt detection runs against the
|
|
18
|
+
* real prompt rather than an empty string; a proxy step that arrives before any
|
|
19
|
+
* prompt (an SDK session with no hooks) lazily opens a run with empty input,
|
|
20
|
+
* which is honest rather than lossy.
|
|
21
|
+
*
|
|
22
|
+
* SECURITY (§9): binds 127.0.0.1 only, requires the discovery file's bearer
|
|
23
|
+
* token on every route, and never echoes the token back. A loopback port that
|
|
24
|
+
* accepts unauthenticated step reports is a local exfiltration channel.
|
|
25
|
+
*/
|
|
26
|
+
import { type Recorder } from "../record/recorder.js";
|
|
27
|
+
import { type ReplayOptions } from "../replay/controller.js";
|
|
28
|
+
/** The subset of the Claude Code hook payload we consume (all optional, all defensive). */
|
|
29
|
+
export interface HookPayload {
|
|
30
|
+
session_id?: string;
|
|
31
|
+
transcript_path?: string;
|
|
32
|
+
cwd?: string;
|
|
33
|
+
hook_event_name?: string;
|
|
34
|
+
prompt?: string;
|
|
35
|
+
tool_name?: string;
|
|
36
|
+
tool_input?: unknown;
|
|
37
|
+
tool_response?: unknown;
|
|
38
|
+
tool_use_id?: string;
|
|
39
|
+
error?: unknown;
|
|
40
|
+
agent_id?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface ControlServerOptions {
|
|
43
|
+
recorder: Recorder;
|
|
44
|
+
/** Session working directory — keys the discovery file. */
|
|
45
|
+
cwd: string;
|
|
46
|
+
/** Preferred port; 0 (or a busy port) falls back to an ephemeral one. */
|
|
47
|
+
port?: number;
|
|
48
|
+
/** Loopback bearer token. Generated when omitted. */
|
|
49
|
+
token?: string;
|
|
50
|
+
/** Config keys of the servers a proxy wraps. Grows as proxies register. */
|
|
51
|
+
wrappedServers?: string[];
|
|
52
|
+
/** Host application, for run metadata. */
|
|
53
|
+
host?: {
|
|
54
|
+
app?: string;
|
|
55
|
+
version?: string;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* What `PreToolUse` answers when it injects a correlation id.
|
|
59
|
+
*
|
|
60
|
+
* `"allow"` is what §6/Phase 6 specifies, and what makes injection reliable —
|
|
61
|
+
* but it also **auto-approves the call**, skipping a permission prompt the user
|
|
62
|
+
* might otherwise have seen. `"ask"` keeps the prompt at the cost of some hosts
|
|
63
|
+
* ignoring `updatedInput` on a non-allow decision (correlation then degrades to
|
|
64
|
+
* the fingerprint path). Set with `BIR_CORRELATION_DECISION`.
|
|
65
|
+
*/
|
|
66
|
+
correlationDecision?: "allow" | "ask";
|
|
67
|
+
/** Disable id injection; use `(server, tool, hash(args))` matching instead. */
|
|
68
|
+
noCorrelation?: boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Whether steps actually reach a service. Defaults to "the recorder is not a
|
|
71
|
+
* {@link NullRecorder}", which is right in every case so far.
|
|
72
|
+
*
|
|
73
|
+
* It is reported on `/health` because **this process is the only one that
|
|
74
|
+
* knows**: `bir doctor` runs in a different shell, with a different
|
|
75
|
+
* environment, and inferring it from `BIR_AUTH_URL` there once produced a
|
|
76
|
+
* confident "nothing will be recorded" on a session that was recording fine.
|
|
77
|
+
*/
|
|
78
|
+
recording?: boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Calculated replay (v2, docs/calculatedReplay.md). Omitted or
|
|
81
|
+
* `{ enabled: false }` leaves v1's behaviour exactly as it was: a match stops
|
|
82
|
+
* recording and nothing else happens.
|
|
83
|
+
*/
|
|
84
|
+
replay?: ReplayOptions;
|
|
85
|
+
}
|
|
86
|
+
export interface ControlServerAddress {
|
|
87
|
+
url: string;
|
|
88
|
+
port: number;
|
|
89
|
+
token: string;
|
|
90
|
+
sessionId: string;
|
|
91
|
+
}
|
|
92
|
+
export declare class ControlServer {
|
|
93
|
+
readonly token: string;
|
|
94
|
+
readonly sessionId: string;
|
|
95
|
+
private readonly opts;
|
|
96
|
+
private readonly recorder;
|
|
97
|
+
private readonly queue;
|
|
98
|
+
private readonly sessions;
|
|
99
|
+
/** Config keys known to be wrapped: from install config plus live registrations. */
|
|
100
|
+
private readonly wrapped;
|
|
101
|
+
/** serverName → registration facts, for `bir doctor`. */
|
|
102
|
+
private readonly registrations;
|
|
103
|
+
private server?;
|
|
104
|
+
private address?;
|
|
105
|
+
private lossy;
|
|
106
|
+
/** Seals whose answer text is still being waited for. Awaited before any flush. */
|
|
107
|
+
private readonly pendingSeals;
|
|
108
|
+
/** Calculated replay. Inert unless `opts.replay.enabled`. */
|
|
109
|
+
private readonly replay;
|
|
110
|
+
constructor(opts: ControlServerOptions);
|
|
111
|
+
listen(): Promise<ControlServerAddress>;
|
|
112
|
+
close(): Promise<void>;
|
|
113
|
+
/** Facts for `bir doctor` / `GET /health`. */
|
|
114
|
+
health(): Record<string, unknown>;
|
|
115
|
+
private handle;
|
|
116
|
+
private send;
|
|
117
|
+
private readJson;
|
|
118
|
+
private ensureSession;
|
|
119
|
+
/** Open a run for `session`, or return the open one. */
|
|
120
|
+
private ensureRun;
|
|
121
|
+
private runMetadata;
|
|
122
|
+
/**
|
|
123
|
+
* A similar-prompt hit means the service created no run, so every later step
|
|
124
|
+
* post would 404. Stop recording it — that half is v1 and is mandatory.
|
|
125
|
+
*
|
|
126
|
+
* What replaces the recording is v2 (docs/calculatedReplay.md): the matched
|
|
127
|
+
* scenario is put through the gate ladder and, if every gate passes, armed as a
|
|
128
|
+
* plan. Memoized, because `ensureRun` is reached from five different hooks and
|
|
129
|
+
* arming twice would run the scenario twice.
|
|
130
|
+
*
|
|
131
|
+
* Resolves with the steering directive to inject, or undefined.
|
|
132
|
+
*/
|
|
133
|
+
private watchForMatch;
|
|
134
|
+
/**
|
|
135
|
+
* Close the run's books, without waiting for the network.
|
|
136
|
+
*
|
|
137
|
+
* SEALING IS SPLIT FROM FLUSHING ON PURPOSE. `Stop` — the end of the turn, and
|
|
138
|
+
* the run boundary this whole design names — is a synchronous hook the session
|
|
139
|
+
* is waiting on, so it must not block on an HTTP flush. And `SessionEnd`, the
|
|
140
|
+
* event that used to be the *only* thing that finished a run, does not reliably
|
|
141
|
+
* arrive at all: it fires as the host process is exiting and loses the race.
|
|
142
|
+
* A run whose completeness depends on winning that race is a run that is
|
|
143
|
+
* routinely left half-open, which is what happened in practice.
|
|
144
|
+
*
|
|
145
|
+
* Idempotent: the second caller sees `finished` and does nothing.
|
|
146
|
+
*/
|
|
147
|
+
private sealRun;
|
|
148
|
+
/**
|
|
149
|
+
* Report what a matched turn cost (docs/calculatedReplay.md §11).
|
|
150
|
+
*
|
|
151
|
+
* The live session's tokens come from the transcript **delta** since this run
|
|
152
|
+
* opened — never the whole file, which would bill run 3 for runs 1+2+3 and hand
|
|
153
|
+
* the ledger a baseline several times the truth (§11.4). A run with no
|
|
154
|
+
* watermark (one opened lazily by a proxy step, before any prompt) is reported
|
|
155
|
+
* `measured: false`: saying "unmeasured" is better than quietly inflating a
|
|
156
|
+
* baseline.
|
|
157
|
+
*
|
|
158
|
+
* Queued rather than awaited — `Stop` is a synchronous hook the session is
|
|
159
|
+
* waiting on, and a report is never worth a stall.
|
|
160
|
+
*/
|
|
161
|
+
/**
|
|
162
|
+
* What this turn cost the model, from the transcript **delta** since the run
|
|
163
|
+
* opened — never the whole file, which would bill run 3 for runs 1+2+3
|
|
164
|
+
* (docs/calculatedReplay.md §11.4).
|
|
165
|
+
*
|
|
166
|
+
* No watermark means the delta is unknowable, so this reports `measured:
|
|
167
|
+
* false` and zero rather than a number that is confidently wrong.
|
|
168
|
+
*/
|
|
169
|
+
private runCost;
|
|
170
|
+
private reportExecution;
|
|
171
|
+
/** Seal the run *and* wait for everything queued to reach the service. */
|
|
172
|
+
private finalizeRun;
|
|
173
|
+
private onSessionStart;
|
|
174
|
+
private onPrompt;
|
|
175
|
+
/**
|
|
176
|
+
* `PreToolUse`. Two shapes of answer:
|
|
177
|
+
*
|
|
178
|
+
* - **Wrapped MCP tool** → mint a call id and ask Claude Code to carry it in
|
|
179
|
+
* the tool's arguments, so the proxy's report and this hook's view join into
|
|
180
|
+
* one step (Phase 6). No step is recorded yet: the proxy owns the result and
|
|
181
|
+
* allocates the index when it reports.
|
|
182
|
+
* - **Anything else** (built-ins, unwrapped MCP) → allocate the pair now and
|
|
183
|
+
* record `tool_selected`. The hook is this step's only observer.
|
|
184
|
+
*/
|
|
185
|
+
private onToolPre;
|
|
186
|
+
/**
|
|
187
|
+
* `PostToolUse` / `PostToolUseFailure`. For a built-in this closes the pair.
|
|
188
|
+
* For a correlated MCP call it normally does nothing — the proxy owns that
|
|
189
|
+
* result — but it arms a short fallback: if the proxy never reports (it died,
|
|
190
|
+
* or the call never reached it), the hook's own view is recorded after
|
|
191
|
+
* {@link PROXY_REPORT_GRACE_MS} rather than the step being lost entirely.
|
|
192
|
+
*/
|
|
193
|
+
private onToolPost;
|
|
194
|
+
private errorText;
|
|
195
|
+
/**
|
|
196
|
+
* `SubagentStart` / `SubagentStop`. These carry `agent_id`, which is the only
|
|
197
|
+
* way to tell a subagent's tool calls from the main thread's — Claude Code
|
|
198
|
+
* interleaves them into one hook stream otherwise.
|
|
199
|
+
*
|
|
200
|
+
* v1 attributes rather than partitions: the active agent id is remembered and
|
|
201
|
+
* prefixed onto each step's `context`, because the recording schema has no
|
|
202
|
+
* agent column and inventing one server-side is out of scope (D6). The
|
|
203
|
+
* partition is available in the audit log for anyone who needs it sooner.
|
|
204
|
+
*/
|
|
205
|
+
private onAgent;
|
|
206
|
+
private onStop;
|
|
207
|
+
private onSessionEnd;
|
|
208
|
+
private onProxyRegister;
|
|
209
|
+
/**
|
|
210
|
+
* `POST /proxy/poll` — the control→proxy channel (docs/calculatedReplay.md §16.2).
|
|
211
|
+
*
|
|
212
|
+
* The proxy dials out and parks; the server answers with work, or empty at the
|
|
213
|
+
* hold deadline so the proxy re-polls. Keeping the direction outbound is what
|
|
214
|
+
* avoids N new loopback listeners and N new tokens for a latency saving that
|
|
215
|
+
* would be invisible next to the upstream's own round trip (D12).
|
|
216
|
+
*/
|
|
217
|
+
private onProxyPoll;
|
|
218
|
+
/** `POST /proxy/result` — the answer to one dispatched `tools/call`. */
|
|
219
|
+
private onProxyResult;
|
|
220
|
+
/**
|
|
221
|
+
* `POST /scenario/run` — `bir-scenario` asking for the armed plan to run.
|
|
222
|
+
*
|
|
223
|
+
* This is `direct` mode's whole execution: every step goes to the proxy that
|
|
224
|
+
* already owns its upstream, so the model spends nothing beyond the turn that
|
|
225
|
+
* reads the results. There is no hook timeout here, which is exactly why the
|
|
226
|
+
* derivation wait was deferred to it (§10).
|
|
227
|
+
*/
|
|
228
|
+
private onScenarioRun;
|
|
229
|
+
/**
|
|
230
|
+
* `POST /scenario/replay` — `bir replay`, i.e. a scenario nobody matched.
|
|
231
|
+
*
|
|
232
|
+
* The CLI holds the credentials, so it fetches the scenario and posts it here;
|
|
233
|
+
* this process holds the proxies, so it runs it. Enabled independently of
|
|
234
|
+
* `BIR_REPLAY`: the operator typed a scenario id, which is a far more explicit
|
|
235
|
+
* consent than a similarity score, and the gate that flag guards is automatic
|
|
236
|
+
* arming rather than execution itself.
|
|
237
|
+
*/
|
|
238
|
+
private onScenarioReplay;
|
|
239
|
+
/**
|
|
240
|
+
* A proxy's `ProxyStepReport`. Three joins, tried in order:
|
|
241
|
+
* 1. the injected `callId` (Tier 1, exact);
|
|
242
|
+
* 2. the fingerprint, inside a 30 s window (`--no-correlation`, lossy under
|
|
243
|
+
* identical concurrent calls — §6 says so, and so does this code);
|
|
244
|
+
* 3. nothing — record it standalone, which is what an SDK session with no
|
|
245
|
+
* hooks produces and is a complete MCP step in its own right.
|
|
246
|
+
*/
|
|
247
|
+
private onProxyStep;
|
|
248
|
+
/**
|
|
249
|
+
* Which session a proxy's step belongs to.
|
|
250
|
+
*
|
|
251
|
+
* A proxy has no session id of its own — stdio MCP servers are never told one
|
|
252
|
+
* — so the control server decides. The order matters: preferring *any known*
|
|
253
|
+
* session over minting a new one is what stops a step that lands between
|
|
254
|
+
* `SessionStart` and the first prompt from opening a second, parallel run,
|
|
255
|
+
* which would split one session's steps across two recordings.
|
|
256
|
+
*/
|
|
257
|
+
private sessionForProxyStep;
|
|
258
|
+
private matchByFingerprint;
|
|
259
|
+
/** Record the merged pair for a correlation and latch it against re-entry. */
|
|
260
|
+
private settle;
|
|
261
|
+
/** The one place a tool step pair is written. Redaction and caps live here. */
|
|
262
|
+
private record;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* The port `bir install` writes into the hook URLs, and the one `bir-hooks`
|
|
266
|
+
* prefers. Claude Code's `settings.json` needs a *static* URL, so the control
|
|
267
|
+
* server cannot be purely ephemeral: it asks for this port and falls back to an
|
|
268
|
+
* ephemeral one only if it is taken — in which case the installed hooks point at
|
|
269
|
+
* a port nobody is listening on, and both `bir-hooks` and `bir doctor` say so.
|
|
270
|
+
*/
|
|
271
|
+
export declare const DEFAULT_CONTROL_PORT = 53411;
|
|
272
|
+
//# sourceMappingURL=server.d.ts.map
|