@basein/runner 0.3.14 → 0.3.16
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/README.md +7 -0
- package/dist/bin/bir-scenario.js +8 -5
- package/dist/bin/bir.js +2 -1
- package/dist/bin/investigate.d.ts +71 -0
- package/dist/bin/investigate.js +145 -13
- package/dist/bin/scenario-edit.d.ts +45 -1
- package/dist/bin/scenario-edit.js +134 -14
- package/dist/control/server.d.ts +41 -10
- package/dist/control/server.js +169 -20
- package/dist/record/recorder.d.ts +45 -7
- package/dist/record/remote-recorder.d.ts +3 -4
- package/dist/record/remote-recorder.js +16 -0
- package/dist/record/switchable-recorder.d.ts +1 -5
- package/dist/replay/bundle.d.ts +10 -0
- package/dist/replay/bundle.js +23 -2
- package/dist/replay/controller.d.ts +151 -0
- package/dist/replay/controller.js +510 -58
- package/dist/replay/flatten.d.ts +39 -3
- package/dist/replay/flatten.js +42 -2
- package/dist/replay/handover.d.ts +39 -1
- package/dist/replay/handover.js +81 -2
- package/dist/replay/logic.d.ts +8 -0
- package/dist/replay/logic.js +19 -0
- package/dist/replay/plan.d.ts +165 -4
- package/dist/replay/plan.js +488 -25
- package/dist/replay/source-run.d.ts +14 -1
- package/dist/replay/source-run.js +25 -2
- package/dist/replay/targets.js +8 -1
- package/dist/replay/types.d.ts +52 -3
- package/dist/replay/types.js +14 -0
- package/dist/util/service-headers.d.ts +7 -1
- package/dist/util/service-headers.js +7 -1
- package/docs/BaseInstRunner.md +3 -1
- package/docs/calculatedReplay.md +117 -6
- package/docs/calculatedReplayGuide.md +41 -3
- package/package.json +1 -1
|
@@ -17,7 +17,9 @@
|
|
|
17
17
|
* The queue semantics deliberately match the server's own dry replay
|
|
18
18
|
* (`recordedOutputsByTool` in `scenarios/replay.ts`): a FIFO per tool name, drawn
|
|
19
19
|
* in order, with a per-tool cursor. A scenario that behaved one way under
|
|
20
|
-
* `bir scenario replay --dry` and another way here would be untestable.
|
|
20
|
+
* `bir scenario replay --dry` and another way here would be untestable. A step
|
|
21
|
+
* a test leaves out passes over its own recorded output — unless a correction
|
|
22
|
+
* added it, and it made no call in the recording ({@link SourceRunCache.leftOut}).
|
|
21
23
|
*
|
|
22
24
|
* Fetched **lazily** — only when a step actually needs it — and cached for the
|
|
23
25
|
* turn, so a fully-executable replay never makes this call at all.
|
|
@@ -63,6 +65,17 @@ export declare class SourceRunCache {
|
|
|
63
65
|
* skipped, which the caller already handles.
|
|
64
66
|
*/
|
|
65
67
|
outputFor(entry: FlatEntry): Promise<string | undefined>;
|
|
68
|
+
/**
|
|
69
|
+
* A step its test left out (the service's autocorrect.md AC5). Every step
|
|
70
|
+
* of a plan was in the recording, and made its call there — unless a
|
|
71
|
+
* correction added it from the agent's redo (`correctionKind: "added"`),
|
|
72
|
+
* which made none. So one that was in it passes over its recorded output,
|
|
73
|
+
* and the next step of its tool takes its own; an added one passes over
|
|
74
|
+
* nothing. Nothing is fetched: the cursor is a count of this frame's calls
|
|
75
|
+
* of the tool, whichever of them are read later (docs/calculatedReplay.md
|
|
76
|
+
* §9.2).
|
|
77
|
+
*/
|
|
78
|
+
leftOut(entry: FlatEntry): void;
|
|
66
79
|
private load;
|
|
67
80
|
private fetchRun;
|
|
68
81
|
}
|
|
@@ -17,7 +17,9 @@
|
|
|
17
17
|
* The queue semantics deliberately match the server's own dry replay
|
|
18
18
|
* (`recordedOutputsByTool` in `scenarios/replay.ts`): a FIFO per tool name, drawn
|
|
19
19
|
* in order, with a per-tool cursor. A scenario that behaved one way under
|
|
20
|
-
* `bir scenario replay --dry` and another way here would be untestable.
|
|
20
|
+
* `bir scenario replay --dry` and another way here would be untestable. A step
|
|
21
|
+
* a test leaves out passes over its own recorded output — unless a correction
|
|
22
|
+
* added it, and it made no call in the recording ({@link SourceRunCache.leftOut}).
|
|
21
23
|
*
|
|
22
24
|
* Fetched **lazily** — only when a step actually needs it — and cached for the
|
|
23
25
|
* turn, so a fully-executable replay never makes this call at all.
|
|
@@ -80,11 +82,28 @@ export class SourceRunCache {
|
|
|
80
82
|
(o.position >= entry.stepFrom && o.position <= entry.stepTo)));
|
|
81
83
|
if (inRange.length === 0)
|
|
82
84
|
return undefined;
|
|
83
|
-
const key =
|
|
85
|
+
const key = cursorKey(entry, toolName);
|
|
84
86
|
const at = this.cursor.get(key) ?? 0;
|
|
85
87
|
this.cursor.set(key, at + 1);
|
|
86
88
|
return inRange[at]?.output;
|
|
87
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* A step its test left out (the service's autocorrect.md AC5). Every step
|
|
92
|
+
* of a plan was in the recording, and made its call there — unless a
|
|
93
|
+
* correction added it from the agent's redo (`correctionKind: "added"`),
|
|
94
|
+
* which made none. So one that was in it passes over its recorded output,
|
|
95
|
+
* and the next step of its tool takes its own; an added one passes over
|
|
96
|
+
* nothing. Nothing is fetched: the cursor is a count of this frame's calls
|
|
97
|
+
* of the tool, whichever of them are read later (docs/calculatedReplay.md
|
|
98
|
+
* §9.2).
|
|
99
|
+
*/
|
|
100
|
+
leftOut(entry) {
|
|
101
|
+
const toolName = entry.step.toolName;
|
|
102
|
+
if (!toolName || entry.step.correctionKind === "added")
|
|
103
|
+
return;
|
|
104
|
+
const key = cursorKey(entry, toolName);
|
|
105
|
+
this.cursor.set(key, (this.cursor.get(key) ?? 0) + 1);
|
|
106
|
+
}
|
|
88
107
|
load(runId) {
|
|
89
108
|
let pending = this.loading.get(runId);
|
|
90
109
|
if (!pending) {
|
|
@@ -166,6 +185,10 @@ export class SourceRunCache {
|
|
|
166
185
|
* recording. Kept so nothing outside has to change its import.
|
|
167
186
|
*/
|
|
168
187
|
export { SourceRunCache as SourceRunOutputs };
|
|
188
|
+
/** One frame's queue of one tool's recorded outputs: a sub-task called twice replays from its start twice. */
|
|
189
|
+
function cursorKey(entry, toolName) {
|
|
190
|
+
return `${entry.frame.id}#${toolName}`;
|
|
191
|
+
}
|
|
169
192
|
/**
|
|
170
193
|
* Report a service answer to the breaker with the time its request started:
|
|
171
194
|
* an answer to a request sent before a hot path gave up on the service does
|
package/dist/replay/targets.js
CHANGED
|
@@ -130,15 +130,22 @@ function callerBodies(planned) {
|
|
|
130
130
|
bodies.push(entry.step.toolInputLogic);
|
|
131
131
|
if (entry.step.toolOutputLogic)
|
|
132
132
|
bodies.push(entry.step.toolOutputLogic);
|
|
133
|
+
// A step's test reads the parameters too (autocorrect.md AC5): a test
|
|
134
|
+
// that decides on a target nobody named decides on nothing.
|
|
135
|
+
if (entry.step.when?.logic)
|
|
136
|
+
bodies.push(entry.step.when.logic);
|
|
133
137
|
continue;
|
|
134
138
|
}
|
|
135
|
-
// The frame's own mapping, once, and only when its caller is frame 0
|
|
139
|
+
// The frame's own mapping, once, and only when its caller is frame 0 —
|
|
140
|
+
// and the call row's test, which is decided in the caller's scope.
|
|
136
141
|
const frame = entry.frame;
|
|
137
142
|
if (frame.depth !== 1 || seenFrames.has(frame.id))
|
|
138
143
|
continue;
|
|
139
144
|
seenFrames.add(frame.id);
|
|
140
145
|
if (frame.paramMapLogic)
|
|
141
146
|
bodies.push(frame.paramMapLogic);
|
|
147
|
+
if (frame.when?.logic)
|
|
148
|
+
bodies.push(frame.when.logic);
|
|
142
149
|
}
|
|
143
150
|
return bodies;
|
|
144
151
|
}
|
package/dist/replay/types.d.ts
CHANGED
|
@@ -56,8 +56,40 @@ export interface StepStop {
|
|
|
56
56
|
/** For `parked` past its grace, or whose tool is gone: a repair is due. */
|
|
57
57
|
repairDue?: boolean;
|
|
58
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* A step's test (the service's autocorrect.md AC5, contract 2): the step runs
|
|
61
|
+
* only when `logic` says `true`. When it says `false`, `otherwise` decides —
|
|
62
|
+
* `skip` leaves the step out and the plan goes on with the next one;
|
|
63
|
+
* `hand_over` stops the plan in front of the step and the agent does the rest
|
|
64
|
+
* (the other half of an "if … otherwise"). A test that throws, or answers
|
|
65
|
+
* anything but `true` or `false`, stops the plan: nothing is guessed.
|
|
66
|
+
*/
|
|
67
|
+
export interface StepTest {
|
|
68
|
+
/** JS body: `(parameters, intent, respParams) => true | false`. */
|
|
69
|
+
logic: string;
|
|
70
|
+
otherwise: "skip" | "hand_over";
|
|
71
|
+
/** The test in words ("engaged users went over the threshold since the start date"). */
|
|
72
|
+
text?: string | null;
|
|
73
|
+
}
|
|
74
|
+
/** Whether a served test is one this runner can run; anything else is no plan to arm. */
|
|
75
|
+
export declare function isStepTest(value: unknown): value is StepTest;
|
|
59
76
|
export interface SerializedScenarioStep {
|
|
60
77
|
stepIndex: number;
|
|
78
|
+
/**
|
|
79
|
+
* Run this step only when its test says so (autocorrect.md AC5). Absent or
|
|
80
|
+
* null on a step with no test — every step of an older service. On a call
|
|
81
|
+
* row the test decides the whole sub-task.
|
|
82
|
+
*/
|
|
83
|
+
when?: StepTest | null;
|
|
84
|
+
/**
|
|
85
|
+
* The correction that wrote this step, and what it did (the service's
|
|
86
|
+
* autocorrect.md AC6). `"added"` is a step taken from the agent's redo: it
|
|
87
|
+
* made no call in the recording the plan was calculated from, so it pairs
|
|
88
|
+
* with no recorded output (source-run.ts). Absent or null on every other
|
|
89
|
+
* step, and on every step of an older service.
|
|
90
|
+
*/
|
|
91
|
+
correctionId?: string | null;
|
|
92
|
+
correctionKind?: "added" | "tested" | "changed" | null;
|
|
61
93
|
/**
|
|
62
94
|
* `"segment"` is a call: a sub-task this chain runs by calling a recorded
|
|
63
95
|
* scenario instead of carrying a copy of its steps (R-CALL-1). Absent means
|
|
@@ -171,6 +203,12 @@ export interface SerializedScenario {
|
|
|
171
203
|
paramsLogic: string | null;
|
|
172
204
|
/** JS body: `(respParams, parameters, intent) => object`. The final model. */
|
|
173
205
|
responseParamsLogic: string | null;
|
|
206
|
+
/**
|
|
207
|
+
* The chain's revision as served. Carried on the plan's own calls when they
|
|
208
|
+
* are recorded into the turn's newest run (autocorrect.md AC2), so the
|
|
209
|
+
* service can tell which chain made them. Absent from an older service.
|
|
210
|
+
*/
|
|
211
|
+
chainRevision?: number;
|
|
174
212
|
outParamsObject?: unknown;
|
|
175
213
|
originalCostUsd: number | null;
|
|
176
214
|
originalMs: number | null;
|
|
@@ -207,7 +245,13 @@ export type FallbackKind =
|
|
|
207
245
|
* non-deterministic: its input needs a judgement no code computes
|
|
208
246
|
* (plan-services.md D8, kind 3). Never tried, counts nothing.
|
|
209
247
|
*/
|
|
210
|
-
| "nondeterministic_step"
|
|
248
|
+
| "nondeterministic_step"
|
|
249
|
+
/**
|
|
250
|
+
* The plan stopped in front of a step whose test said no, with `hand_over`
|
|
251
|
+
* (autocorrect.md AC5): this request is the other half of an "if …
|
|
252
|
+
* otherwise", and the agent does it. No fault of the plan; counts nothing.
|
|
253
|
+
*/
|
|
254
|
+
| "other_branch";
|
|
211
255
|
/**
|
|
212
256
|
* Upgrade order. `not_steered` is the floor — the control group, and the safe
|
|
213
257
|
* answer if a turn dies mid-flight — and each later decision point can only move
|
|
@@ -228,18 +272,23 @@ export declare const OUTCOME_RANK: Record<ExecutionOutcome, number>;
|
|
|
228
272
|
* `failed` — the *scenario's own logic* threw, or its tool ran and reported
|
|
229
273
|
* an error (stage `tool_call`). Either way the step's work did
|
|
230
274
|
* not happen, and a plan cannot claim success past it.
|
|
275
|
+
* `not_taken` — its test said this request does not need it, and it was
|
|
276
|
+
* left out on purpose (autocorrect.md AC5): no call, no output
|
|
277
|
+
* logic. The plan went on with the next step.
|
|
231
278
|
*
|
|
232
279
|
* A step the run never reached is simply absent from the report. The console
|
|
233
280
|
* renders those as "not run" — asserting anything about a step that never
|
|
234
281
|
* started would be inventing a result.
|
|
235
282
|
*/
|
|
236
|
-
export type ExecutionStepStatus = "ok" | "recorded" | "skipped" | "failed";
|
|
283
|
+
export type ExecutionStepStatus = "ok" | "recorded" | "skipped" | "failed" | "not_taken";
|
|
237
284
|
/**
|
|
238
285
|
* Where inside a step (or a run) something broke. Mirrors the service's
|
|
239
286
|
* `ExecutionStage` (its `src/db.ts`) — a closed vocabulary, so the console can
|
|
240
287
|
* say which part failed without parsing an error message.
|
|
241
288
|
*/
|
|
242
|
-
export type ExecutionStage = "derive_params" | "params_logic"
|
|
289
|
+
export type ExecutionStage = "derive_params" | "params_logic"
|
|
290
|
+
/** A step's test threw, or answered neither true nor false (autocorrect.md AC5). */
|
|
291
|
+
| "when_logic" | "tool_input_logic" | "tool_call" | "tool_output_logic" | "response_logic" | "setup" | "unknown";
|
|
243
292
|
/**
|
|
244
293
|
* One step's verdict, as reported to the service alongside the execution.
|
|
245
294
|
*
|
package/dist/replay/types.js
CHANGED
|
@@ -10,6 +10,16 @@
|
|
|
10
10
|
* everywhere and nothing is assumed present. A scenario whose `state` is not
|
|
11
11
|
* `"ready"` carries an **empty** `steps` array, by the service's own rule.
|
|
12
12
|
*/
|
|
13
|
+
/** Whether a served test is one this runner can run; anything else is no plan to arm. */
|
|
14
|
+
export function isStepTest(value) {
|
|
15
|
+
if (!value || typeof value !== "object")
|
|
16
|
+
return false;
|
|
17
|
+
const t = value;
|
|
18
|
+
return (typeof t.logic === "string" &&
|
|
19
|
+
t.logic.trim().length > 0 &&
|
|
20
|
+
(t.otherwise === "skip" || t.otherwise === "hand_over") &&
|
|
21
|
+
(t.text === undefined || t.text === null || typeof t.text === "string"));
|
|
22
|
+
}
|
|
13
23
|
/** Whether this schema has anything the runner may not guess (R-PARAM-4). */
|
|
14
24
|
export function hasTargets(schema) {
|
|
15
25
|
if (!schema)
|
|
@@ -51,6 +61,10 @@ export function isReadyScenario(value) {
|
|
|
51
61
|
function readyStep(step) {
|
|
52
62
|
if (!step)
|
|
53
63
|
return false;
|
|
64
|
+
// A test this runner cannot read is a step it cannot decide: the plan is
|
|
65
|
+
// not armed rather than run with the step always in, or always out.
|
|
66
|
+
if (step.when !== undefined && step.when !== null && !isStepTest(step.when))
|
|
67
|
+
return false;
|
|
54
68
|
if (step.kind === "segment") {
|
|
55
69
|
if (step.unusable)
|
|
56
70
|
return true;
|
|
@@ -5,8 +5,14 @@
|
|
|
5
5
|
*
|
|
6
6
|
* - **1** — stops in front of a step that carries `stop`, and reads
|
|
7
7
|
* `withheld: "runner_update_required"` on a match.
|
|
8
|
+
* - **2** — runs a step (or a sub-task's call row) only when its test, `when`,
|
|
9
|
+
* says so: leaves it out (`not_taken`) or stops in front of it
|
|
10
|
+
* (`other_branch`) when the test says no, and stops when the test cannot
|
|
11
|
+
* answer (stage `when_logic`). The service's autocorrect.md AC5: a plan
|
|
12
|
+
* holding a test is handed only to a runner that names contract 2 or newer,
|
|
13
|
+
* because an older one would run every step — both halves of the task.
|
|
8
14
|
*/
|
|
9
|
-
export declare const CONTRACT =
|
|
15
|
+
export declare const CONTRACT = 2;
|
|
10
16
|
/** Spread into the `headers` of a request to the service. */
|
|
11
17
|
export declare function serviceHeaders(): Record<string, string>;
|
|
12
18
|
/**
|
|
@@ -22,8 +22,14 @@ import { packageVersion } from "./version.js";
|
|
|
22
22
|
*
|
|
23
23
|
* - **1** — stops in front of a step that carries `stop`, and reads
|
|
24
24
|
* `withheld: "runner_update_required"` on a match.
|
|
25
|
+
* - **2** — runs a step (or a sub-task's call row) only when its test, `when`,
|
|
26
|
+
* says so: leaves it out (`not_taken`) or stops in front of it
|
|
27
|
+
* (`other_branch`) when the test says no, and stops when the test cannot
|
|
28
|
+
* answer (stage `when_logic`). The service's autocorrect.md AC5: a plan
|
|
29
|
+
* holding a test is handed only to a runner that names contract 2 or newer,
|
|
30
|
+
* because an older one would run every step — both halves of the task.
|
|
25
31
|
*/
|
|
26
|
-
export const CONTRACT =
|
|
32
|
+
export const CONTRACT = 2;
|
|
27
33
|
/** Spread into the `headers` of a request to the service. */
|
|
28
34
|
export function serviceHeaders() {
|
|
29
35
|
return {
|
package/docs/BaseInstRunner.md
CHANGED
|
@@ -475,7 +475,9 @@ What the control server does for such a session:
|
|
|
475
475
|
text) has every request answered `{}`, and nothing of it is recorded or
|
|
476
476
|
replayed (`session.host_refused`).
|
|
477
477
|
- **The plan.** One starts only for a prompt that said `replay: true`.
|
|
478
|
-
Otherwise a match is declined with `host_records_only
|
|
478
|
+
Otherwise a match is declined with `host_records_only`, and a plan with
|
|
479
|
+
steps that run only when a test says so with `host_tests_unsupported`: the
|
|
480
|
+
adapter reads no tests yet (calculatedReplay.md §9.2). The plan's mode is
|
|
479
481
|
always `steer`, and the runner runs no step itself: the host runs its own
|
|
480
482
|
tools. The prompt's answer carries the plan as data (`plan`), because the
|
|
481
483
|
directive beside it is a text written for a model.
|
package/docs/calculatedReplay.md
CHANGED
|
@@ -219,7 +219,9 @@ plan is declined when no running `bir-scenario` has said it serves `run_scenario
|
|
|
219
219
|
(`no_delivery_tool`, §6.3). In plan mode (`permission_mode: "plan"`) nothing is announced at all.
|
|
220
220
|
In a session of another host (§7.4) a plan that armed is taken back, before any step of it runs,
|
|
221
221
|
when it names a tool the agent that asked does not hold (`host_tool_missing`): it is booked like
|
|
222
|
-
any gate's decline, `not_steered`, with no step marked failed.
|
|
222
|
+
any gate's decline, `not_steered`, with no step marked failed. A plan with steps that run only when
|
|
223
|
+
a test says so (§9.2) is declined for such a host before anything is spent
|
|
224
|
+
(`host_tests_unsupported`): its adapter steps through a plan call by call and reads no tests yet.
|
|
223
225
|
|
|
224
226
|
Gate 4 is retired. Up to 0.3.6 it declined a match under `BIR_MIN_STEER_SIMILARITY` (0.92),
|
|
225
227
|
above the server's 0.9, on the argument that the server only decided *"do not record this again"*
|
|
@@ -879,6 +881,77 @@ time limit, §13.1; argument order and the plain-object guard still match the se
|
|
|
879
881
|
In direct mode both live inside one `for` loop. In steer mode they are split across *separate HTTP
|
|
880
882
|
requests* — which is what §7.3's ordering rule protects.
|
|
881
883
|
|
|
884
|
+
### 9.2 Steps that run only when (contract 2)
|
|
885
|
+
|
|
886
|
+
Built 2026-10-23 for the service's autocorrect.md AC5; not yet released. A task can have two
|
|
887
|
+
halves: "if engaged users went over the threshold, book a meeting; otherwise send a note". A
|
|
888
|
+
recording shows one half, so its plan knows one half. The service's correction adds the other half
|
|
889
|
+
and a test on each step that belongs to only one of them. A served step, and a sub-task's call
|
|
890
|
+
row, may carry:
|
|
891
|
+
|
|
892
|
+
```ts
|
|
893
|
+
when?: { logic: string; otherwise: "skip" | "hand_over"; text?: string | null } | null
|
|
894
|
+
// logic: a function body, (parameters, intent, respParams) => true | false
|
|
895
|
+
```
|
|
896
|
+
|
|
897
|
+
The test runs in the same sandbox as the other logic (`evalWhenLogic`, `src/replay/logic.ts`). It is
|
|
898
|
+
decided when its step comes due, with what is known then: the request's values and the results of
|
|
899
|
+
the steps before it. A call row's test is decided once, in the caller's scope, before the sub-task
|
|
900
|
+
starts — where its first step *or its callee's* is: a sub-task whose chain begins with a call
|
|
901
|
+
begins with that call's steps, so nothing of it runs before its test says it is needed. A sub-task
|
|
902
|
+
is entered there too, before its callee, whose mapping reads its parameters, and left after the
|
|
903
|
+
last step of it or its callee (`frameEdges`, `src/replay/flatten.ts`).
|
|
904
|
+
|
|
905
|
+
| The test says | What happens |
|
|
906
|
+
|---|---|
|
|
907
|
+
| `true` | The step runs. |
|
|
908
|
+
| `false`, `otherwise: "skip"` | Nothing is sent. The step is reported `not_taken` and the plan goes on. A skipped call row leaves out its whole sub-task and is reported `not_taken` under its own index. |
|
|
909
|
+
| `false`, `otherwise: "hand_over"` | The plan stops in front of the step and hands the rest of the task to the model, saying the steps from here are only for when `text` (`fallbackKind: other_branch`). |
|
|
910
|
+
| throws, or not exactly `true` or `false` | The plan stops: the step is `failed` at stage `when_logic` (`fallbackKind: step_failed`). The model is told the plan could not tell whether this request needs the step, and decides itself. Nothing is guessed. |
|
|
911
|
+
|
|
912
|
+
A plan whose tests left out every step it came to, and ran none, hands the whole task to the model
|
|
913
|
+
(`other_branch`, step 0). Its report is `failed` with `fallbackKind: other_branch`, and it is sent
|
|
914
|
+
although it cost nothing: it tells the service the request took the half the plan does not know.
|
|
915
|
+
|
|
916
|
+
A step left out that is the last of a sub-task still hands the sub-task's results back to its
|
|
917
|
+
caller. When the body that maps them throws, the plan stops there: the call row is `failed` at stage
|
|
918
|
+
`tool_output_logic`, and the note says the sub-task could not hand back its results — never that
|
|
919
|
+
the step left out ran.
|
|
920
|
+
|
|
921
|
+
- **Direct mode** decides the tests in its loop. The bundle gives a left-out step its own heading,
|
|
922
|
+
`--- Step 5: tool (not needed) ---`, and says it is only for when `text`, so the model does not run
|
|
923
|
+
it itself.
|
|
924
|
+
- **Steer mode** settles the tests after each call. The `PostToolUse` answer names the next step
|
|
925
|
+
this request does not need, and the step to call instead. A call to a left-out tool — every tool
|
|
926
|
+
of a sub-task left out whole — is answered in its place with the same words, at most twice, and
|
|
927
|
+
does not end the plan. When the left-out step and the next one share a tool and the note has not
|
|
928
|
+
reached the model yet, a call the pin guard (§7) finds is not the next step gets the note in its
|
|
929
|
+
place too, so the step left out never runs because the note was late. A note naming a step to call
|
|
930
|
+
next is dropped when the plan retires — a divergence, a hand-over — since the plan no longer has
|
|
931
|
+
that step; only "the sequence is complete" outlives a plan that finished.
|
|
932
|
+
- **The divergence bundle** (§8) decides the tests the same way, going on from the sub-task the
|
|
933
|
+
cursor is inside, with what that sub-task made so far. A `hand_over` stop ends the bundle there,
|
|
934
|
+
and the turn is booked `fell_back` when a step did its work (`failed` when none did) — a hand-over,
|
|
935
|
+
as in the other modes, never `diverged`, so the service takes the agent's work after it as that
|
|
936
|
+
half's evidence.
|
|
937
|
+
- **The directive** (§6.4) marks a tested step `(only when <text>)` and says the results, or the
|
|
938
|
+
notes after each call, name the steps this request does not need.
|
|
939
|
+
- **A hand-over note** counts only the steps that ran, names the ones their tests left out ("Not
|
|
940
|
+
needed for this request, so not run: step 2 (Grep)"), and marks a remaining step that runs only
|
|
941
|
+
under a test `(only when …)`, so the list does not read as work this request needs.
|
|
942
|
+
- **Another host** (a LangChain application) steps through a plan call by call, and its adapter
|
|
943
|
+
reads no tests yet: a plan with any test is declined for it before anything is spent
|
|
944
|
+
(`host_tests_unsupported`), and the model does the task.
|
|
945
|
+
|
|
946
|
+
A step left out passes over its own recorded output (§8.1): it was in the recording and made its
|
|
947
|
+
call there, so the next step of its tool takes its own output, not the one left out. A step a
|
|
948
|
+
correction added from the agent's redo (`correctionKind: "added"`) made no call in the recording
|
|
949
|
+
and passes over nothing (`SourceRunCache.leftOut`, `src/replay/source-run.ts`).
|
|
950
|
+
|
|
951
|
+
The runner names `x-bir-contract: 2` on its calls to the service (`src/util/service-headers.ts`).
|
|
952
|
+
The service hands a plan with any test only to a runner that names 2 or more; an older runner gets
|
|
953
|
+
no plan (`runner_update_required`) and the agent does the task.
|
|
954
|
+
|
|
882
955
|
---
|
|
883
956
|
|
|
884
957
|
## 10. Timing: what may block, and for how long
|
|
@@ -971,7 +1044,8 @@ threaded before the divergence. A divergence that ran nothing books `failed`, an
|
|
|
971
1044
|
stopped at a step that broke (a logic failure, a tool error, a call sent and never answered). A
|
|
972
1045
|
recorded stand-in (`BIR_REPLAY_RECORDED_OUTPUTS=1`) is never work done: it counts toward neither
|
|
973
1046
|
`stepsPinned` nor the upgrade, and a direct run that used one is `diverged` at best. A call that ran
|
|
974
|
-
beside a pinned one hands over as `fell_back` or `failed` (§7)
|
|
1047
|
+
beside a pinned one hands over as `fell_back` or `failed` (§7), and so does a divergence whose
|
|
1048
|
+
bundle stops in front of the other half of an "if … otherwise" (§9.2). A direct run cancelled by its caller
|
|
975
1049
|
(Esc, or `bir-scenario`'s own timeout) books `fell_back` or `failed` with `measured: false`, never
|
|
976
1050
|
`diverged`: that turn's cost says nothing about what the task costs.
|
|
977
1051
|
|
|
@@ -1012,7 +1086,8 @@ bounded flush — runs in the background, so `Stop` never blocks on the network:
|
|
|
1012
1086
|
POST /scenarios/:scenarioId/executions
|
|
1013
1087
|
{ ticket, outcome, deriveCostUsd, sessionCostUsd, fallbackCostUsd,
|
|
1014
1088
|
durationMs, stepsPlanned, stepsPinned, measured, pricingVersion, prompt,
|
|
1015
|
-
steps, error, errorStage, errorStepIndex, errorToolName
|
|
1089
|
+
steps, error, errorStage, errorStepIndex, errorToolName,
|
|
1090
|
+
redoneBy } // { runId, ownCalls }, only after a redo (below)
|
|
1016
1091
|
```
|
|
1017
1092
|
|
|
1018
1093
|
An **unredeemed** ticket leaves no execution row at all. That asymmetry is intentional and worth
|
|
@@ -1023,6 +1098,14 @@ was handed out, and answers a later report `409 invalid_ticket`. The service as
|
|
|
1023
1098
|
yet) redeems it for 24 hours. It also answers a report whose write failed `503 report_not_booked`,
|
|
1024
1099
|
and the recorder sends that report once more.
|
|
1025
1100
|
|
|
1101
|
+
**After a redo** (the service's autocorrect.md AC1; built 2026-10-23, not yet released). When a
|
|
1102
|
+
plan ran to its end and the agent then did the task again itself, the rest of the turn is recorded
|
|
1103
|
+
as a repeat of the recording, with reason `redone` (§12). The report then says
|
|
1104
|
+
`redoneBy: { runId, ownCalls }`: the repeat's run id, and how many tool calls the agent made of its
|
|
1105
|
+
own in it. A call the plan pinned is not one of them, and neither is one a proxy reports after
|
|
1106
|
+
`Stop`. Only the plan the prompt armed sends it. The service judges from it whether the agent
|
|
1107
|
+
rejected the plan's result, and why.
|
|
1108
|
+
|
|
1026
1109
|
### 11.2a What each step did (the service's errorshandling.md)
|
|
1027
1110
|
|
|
1028
1111
|
The last five fields are the diagnosis, and none of the money depends on them. The service's
|
|
@@ -1035,7 +1118,7 @@ at a web page.
|
|
|
1035
1118
|
steps: Array<{
|
|
1036
1119
|
stepIndex: number; // the scenario's own index, not a position in this array
|
|
1037
1120
|
toolName: string;
|
|
1038
|
-
status: "ok" | "recorded" | "skipped" | "failed";
|
|
1121
|
+
status: "ok" | "recorded" | "skipped" | "failed" | "not_taken";
|
|
1039
1122
|
stage?: ExecutionStage; // where in the step, for `failed` / `skipped`
|
|
1040
1123
|
error?: string; // message only — this is rendered in a web page
|
|
1041
1124
|
durationMs?: number;
|
|
@@ -1083,7 +1166,9 @@ only another open session's proxy), or it was sent and timed out or was refused.
|
|
|
1083
1166
|
host stopped itself, in a host that is not Claude Code, is `skipped` as well: a person refused it,
|
|
1084
1167
|
or changed its call (§7.4). The plan stops at a `skipped` step.
|
|
1085
1168
|
**`failed` is the only one that means the scenario is broken** rather than the environment — it is
|
|
1086
|
-
the scenario's own logic throwing, and it is what `Recalculate` fixes.
|
|
1169
|
+
the scenario's own logic throwing, and it is what `Recalculate` fixes. A step's test that throws is
|
|
1170
|
+
`failed` too, at stage `when_logic`. `not_taken` is a step its test left out (§9.2): nothing was
|
|
1171
|
+
sent, so it has neither payload.
|
|
1087
1172
|
|
|
1088
1173
|
Three rules this must keep:
|
|
1089
1174
|
|
|
@@ -1197,6 +1282,20 @@ Three consequences to keep straight:
|
|
|
1197
1282
|
`request()` is *"Never used by the relay"* — it was built for `bir doctor` and tests, and this is
|
|
1198
1283
|
its second, load-bearing user.
|
|
1199
1284
|
|
|
1285
|
+
**A plan the agent redid.** The service's newestRun.md N7 makes one exception. When the agent
|
|
1286
|
+
does the task again after a plan ran to its end, the rest of the turn is recorded as a repeat of the
|
|
1287
|
+
recording, with reason `redone`. Since autocorrect.md AC2 (built 2026-10-23, not yet released) the
|
|
1288
|
+
repeat starts with the plan's own calls, then the agent's. Each of the plan's calls is a pair of
|
|
1289
|
+
rows marked `metadata.byPlan: { scenarioId, stepIndex, chainRevision }`, and each is kept whole,
|
|
1290
|
+
like a recorded step (`serializeCapped(redact(…))`, up to 64 KiB), not cut to the report's 3,800
|
|
1291
|
+
bytes. So the service can set what the plan did beside what the agent did instead, call by call.
|
|
1292
|
+
|
|
1293
|
+
- Only the plan the prompt armed keeps its calls, and only when the match offered a repeat.
|
|
1294
|
+
- Only calls that were sent and answered are kept, with a result or with a tool error. A step left
|
|
1295
|
+
out, not sent, or standing on a recorded output is not among them.
|
|
1296
|
+
- The calls kept in one turn are held to about 8 MB. Past that, a call keeps only the report's cut.
|
|
1297
|
+
- `chainRevision` is the plan's revision as it was served, or `null` when the service did not send one.
|
|
1298
|
+
|
|
1200
1299
|
### 12.1 `bir replay` — the Tier 2 and debugging path
|
|
1201
1300
|
|
|
1202
1301
|
```bash
|
|
@@ -1400,6 +1499,7 @@ fail because of BaseInstRunner.**
|
|
|
1400
1499
|
| Plan mode | Nothing is announced, pinned or approved |
|
|
1401
1500
|
| Another host (§7.4): the run's adapter asked for no plan with its prompt | Declined at gate 2 (`host_records_only`), `not_steered`; the model does the task |
|
|
1402
1501
|
| Another host: the plan names a tool the agent that asked does not hold | Taken back before any step runs (`host_tool_missing`), `not_steered`; the model does the task |
|
|
1502
|
+
| Another host: the plan has steps that run only when a test says so (§9.2) | Declined before anything is spent (`host_tests_unsupported`), `not_steered`; the model does the task |
|
|
1403
1503
|
| Another host: the model makes a call of its own while the plan is live | The plan is handed over. The call runs as the model wrote it, never with the plan's arguments, and the note comes with its `/tool/post` |
|
|
1404
1504
|
| Another host: a person refuses a step, or changes its call | The step is `skipped`, never `failed`, and the plan hands over; `baselineEligible: false`, and after a refusal `measured: false` (§11.1) |
|
|
1405
1505
|
| Another host: the run fails, goes silent for two hours, or is closed with its plan still live | A live plan is handed over first: `fell_back` or `failed`. The report says `measured: false` and `baselineEligible: false`. A run that ended early is finished with an `error` and no final answer |
|
|
@@ -1653,7 +1753,8 @@ bir scenario check <id> --step <n> POST /scenarios/:id/steps/:n/check
|
|
|
1653
1753
|
bir scenario edit <id> --step <n> PATCH /scenarios/:id/steps/:n the same check, then save
|
|
1654
1754
|
bir scenario edits <id> GET /scenarios/:id/edits the history, newest first
|
|
1655
1755
|
bir scenario undo <id> [--edit <e> | --step <n>]
|
|
1656
|
-
POST /scenarios/:id/edits/:e/revert take back the newest edit (of step n)
|
|
1756
|
+
POST /scenarios/:id/edits/:e/revert take back the newest edit (of step n);
|
|
1757
|
+
<e> is a sedit_, pedit_ or scor_ id
|
|
1657
1758
|
bir scenario editing on|off|status (no route) ~/.baseinstrunner/installed.json which `bir` MCP tools are offered
|
|
1658
1759
|
|
|
1659
1760
|
bir replay --scenario <scnId> --prompt "…" [--dry] alias; the Tier 2 / debugging path (§12.1)
|
|
@@ -1684,6 +1785,16 @@ are where `bir` changes a scenario by hand, so the rules are strict:
|
|
|
1684
1785
|
steps and the final answer (so only when it changes output logic), and answers
|
|
1685
1786
|
`422 undo_refused` when it would break them, unless `--force --note "<why>"` is given. Every
|
|
1686
1787
|
save and every undo bumps the scenario's `chainRevision`.
|
|
1788
|
+
- **Corrections** (the service's autocorrect.md AC7; built 2026-10-23, not yet released). After the
|
|
1789
|
+
agent redid a plan, the service may correct the plan itself, for example by adding the half of
|
|
1790
|
+
the task it did not know (§9.2). `edits` lists each correction beside the hand edits, under its
|
|
1791
|
+
`scor_` id, with its state (`proposed`, `used`, `failed`, `undone`, `replaced`), the revisions
|
|
1792
|
+
before and after, and what it did. `undo --edit scor_…` puts back the plan from before it, all of
|
|
1793
|
+
it, and the service then holds the plan: it is not handed out until its owner chooses "Use it
|
|
1794
|
+
again" on the recording page. A plain `undo` takes back a correction in use when it is the newest
|
|
1795
|
+
change; `--step` never picks one, because a correction changes the whole plan. A correction is
|
|
1796
|
+
protected like a hand edit: `calc --force` answers `409 scenario_has_edits` with its
|
|
1797
|
+
`correctionId`, and `bir` says the plan carries it.
|
|
1687
1798
|
- A save lifts a mark made by hand (`--freeze`, `by_hand`) only with `--unfreeze`; undoing the
|
|
1688
1799
|
freeze, or `calc --force --discard-edits`, also removes it. A forced save lifts only a mark the
|
|
1689
1800
|
calculation set.
|
|
@@ -491,6 +491,7 @@ Everything replay decides is one line. `grep` for these:
|
|
|
491
491
|
| `replay.concurrent_call` | A call ran beside a pinned step; the plan handed over |
|
|
492
492
|
| `replay.thread_fallback` | A wrapped step's output was threaded from the hook's view because the proxy did not report in time |
|
|
493
493
|
| `replay.step` | One direct step executed |
|
|
494
|
+
| `replay.step_not_taken` | A step's test said this request does not need it, and nothing was sent for it ([calculatedReplay.md](calculatedReplay.md) §9.2). `plan.armed … tests=` lists the tested steps by their place in the plan: `4:hand_over,11:skip` |
|
|
494
495
|
| `replay.diverge` / `replay.compose` | Off-script, and the recovery |
|
|
495
496
|
| `replay.handover` | The plan stopped and handed the rest of the task to the model; `kind=` and `step=` say where |
|
|
496
497
|
| `replay.vetoed` | A direct plan retired without running anything: `run_scenario` was not the first call, or never arrived |
|
|
@@ -501,7 +502,8 @@ Everything replay decides is one line. `grep` for these:
|
|
|
501
502
|
| `replay.delivery_tool` | A `bir` server said it can, or cannot, deliver a direct plan |
|
|
502
503
|
| `control.hook_deadline` | A hook route answered `{}` at its deadline |
|
|
503
504
|
| `replay.done` | Final outcome, steps completed, elapsed |
|
|
504
|
-
| `execution.reported` | The ticket was redeemed and the saving booked |
|
|
505
|
+
| `execution.reported` | The ticket was redeemed and the saving booked. `redoneBy=` and `ownCalls=` when the agent did the task again after the plan |
|
|
506
|
+
| `run.repeat … reason=redone` | The plan ran to its end, and the agent did the task again itself. The rest of the turn is recorded as the recording's newest run, `run=`; `planCalls=` says how many of the plan's calls open it |
|
|
505
507
|
| `run.unmeasured` | The turn's cost is not measured (§7). `why=` says what was missing, and `models=` names a model the runner has no price for |
|
|
506
508
|
| `run.start … host=` / `plan.armed … host=` | The turn ran in a host that is not Claude Code, named after `host=` (§6.5). A Claude Code turn has no such field |
|
|
507
509
|
| `replay.own_call` / `replay.step_host_stopped` | Another host only. The model made a call of its own while the plan was live, and the plan handed over / the host stopped a step itself: `stop=rejected` or `stop=edited` |
|
|
@@ -732,6 +734,25 @@ LangChain recording it raises no `steer_only` finding, and it explains an
|
|
|
732
734
|
unmeasured cost in its own words. That part is built on the service and not
|
|
733
735
|
deployed yet.
|
|
734
736
|
|
|
737
|
+
**A plan the agent redid** (built 2026-10-23, not yet released; the service's
|
|
738
|
+
autocorrect.md). The plan ran to its end, and then the agent did the task again
|
|
739
|
+
with calls of its own. The turn shows it three ways:
|
|
740
|
+
|
|
741
|
+
- the `reported` line ends `redone by run_… (N calls of its own)`;
|
|
742
|
+
- a `repeat` line names the run that holds the redo: the newest run of the
|
|
743
|
+
recording, never a recording of its own and never a plan;
|
|
744
|
+
- a `redone` finding says the agent did not take the plan's result.
|
|
745
|
+
|
|
746
|
+
From the service you also see:
|
|
747
|
+
|
|
748
|
+
- the verdict on the execution, `verdict wrong_branch: …`, with the plan's
|
|
749
|
+
steps it rejected and what the plan left in place. Remove that by hand if it
|
|
750
|
+
should not be there;
|
|
751
|
+
- `HELD since …` on the plan: it is not handed out until you choose "Use it
|
|
752
|
+
again" on the recording page;
|
|
753
|
+
- a step's test, `only when …, otherwise skip`, with the correction that added it;
|
|
754
|
+
- the newest correction, and whether it is used.
|
|
755
|
+
|
|
735
756
|
You see your own data. An admin of the service sees everyone's and may pass
|
|
736
757
|
`--user` to `executions`. `--json` on any form prints the raw merge.
|
|
737
758
|
|
|
@@ -760,7 +781,7 @@ bir scenario edit <runId|scnId> --step <n> [--input-logic <file|->] [--output-l
|
|
|
760
781
|
[--freeze | --unfreeze] [--note "<why>"] [--force --note "<why>"] [--revision <n>]
|
|
761
782
|
bir scenario edit <runId|scnId> --param <name> --kind target|setting [--note "<why>"] [--revision <n>]
|
|
762
783
|
bir scenario edits <runId|scnId>
|
|
763
|
-
bir scenario undo <runId|scnId> [--edit <sedit_id|pedit_id> | --step <n> | --param <name>] [--force --note "<why>"]
|
|
784
|
+
bir scenario undo <runId|scnId> [--edit <sedit_id|pedit_id|scor_id> | --step <n> | --param <name>] [--force --note "<why>"]
|
|
764
785
|
bir scenario calc <runId|scnId> [--force [--discard-edits]]
|
|
765
786
|
bir scenario editing on|off|status
|
|
766
787
|
```
|
|
@@ -1093,6 +1114,21 @@ How undo works:
|
|
|
1093
1114
|
- Once every hand edit of a plan is taken back, the service repairs and rebuilds it as usual again.
|
|
1094
1115
|
An undo forced past a refusal is the exception: it leaves its step counted as edited and
|
|
1095
1116
|
forced, so `bir investigate` keeps warning about it.
|
|
1117
|
+
- **A correction the service made** (built 2026-10-23, not yet released). After the agent redid a
|
|
1118
|
+
plan, the service may correct the plan itself (its autocorrect.md), for example by adding the
|
|
1119
|
+
half of the task the recording did not show. `bir scenario edits` lists the correction beside
|
|
1120
|
+
your edits, under its `scor_` id:
|
|
1121
|
+
|
|
1122
|
+
```
|
|
1123
|
+
scor_5b2e… 2026-10-22 17:58:10Z correction used revision 7 → 8
|
|
1124
|
+
16 steps added for the otherwise half; …; 16 steps added (11–26), tests on steps 4–10, the answer changed; …
|
|
1125
|
+
```
|
|
1126
|
+
|
|
1127
|
+
`bir scenario undo <id> --edit scor_…` puts back the whole plan from before it. The plan is then
|
|
1128
|
+
held: it is not handed out until you choose "Use it again" on the recording page. A plain
|
|
1129
|
+
`bir scenario undo` takes back a correction in use when it is the newest change. `--step` never
|
|
1130
|
+
picks one: a correction changes the whole plan. A recalculation is refused on a corrected plan
|
|
1131
|
+
as on an edited one (`Not recalculated: the plan carries correction scor_…`).
|
|
1096
1132
|
|
|
1097
1133
|
#### What a save changes
|
|
1098
1134
|
|
|
@@ -1185,6 +1221,7 @@ The tools, as the `bir` server defines them. **Bold** arguments are required.
|
|
|
1185
1221
|
| `scenario_undo` | with scenario editing on | **`target`**, one of `edit`, `step` or `param`, `force`, `note` | `bir scenario undo` |
|
|
1186
1222
|
|
|
1187
1223
|
- `target` is a `scn_` id, or the `run_` id of a whole-run scenario. `step` is the `stepIndex`.
|
|
1224
|
+
- `edit` is an id from `scenario_edits`: a `sedit_`, `pedit_` or `scor_` id.
|
|
1188
1225
|
- `inputLogic` and `outputLogic` are the code itself, as text. The server writes them to temporary
|
|
1189
1226
|
files for `bir`, and removes the files afterwards.
|
|
1190
1227
|
- `revision` is the `chainRevision` at the top of `scenario_show` with `step`.
|
|
@@ -1300,7 +1337,7 @@ Parameter database: target → setting (revision 4). Undo: bir scenario undo scn
|
|
|
1300
1337
|
| `revision_changed` | The plan changed since you read it: another edit, or a recalculation | `show` it again, check again, save again |
|
|
1301
1338
|
| `not_ready` or `scenario_calculating` | The scenario is not `ready`, or a recalculation is running | Wait for it to finish |
|
|
1302
1339
|
| `undo_refused` | The undo would stop a later step from giving its recorded call, or leave the final answer without its values | Fix that later step first. Or undo it on purpose: `--force --note "why"` |
|
|
1303
|
-
| `not_latest_edit` | The step has changed since the edit you named: a newer entry changed it | `bir` prints two commands: undo the newer one first, then yours |
|
|
1340
|
+
| `not_latest_edit` | The step has changed since the edit you named: a newer entry changed it. For a correction: the plan has changed since it | `bir` prints two commands: undo the newer one first, then yours |
|
|
1304
1341
|
| `already_undone` | That edit was undone already | To redo it, undo the undo. `bir` prints the command |
|
|
1305
1342
|
| `edit_replaced` | A recalculation replaced the plan after that edit | Nothing to undo; the edit is history now |
|
|
1306
1343
|
| `logic_sandbox_unavailable` | The service could not run step code just now (HTTP 503). Nothing was checked, saved or undone | Try again in a minute |
|
|
@@ -1333,6 +1370,7 @@ part of it is not deployed yet.
|
|
|
1333
1370
|
| **R6** | Divergence, source-run fallback, `bir replay` | `diverge()` in [controller.ts](../src/replay/controller.ts), [source-run.ts](../src/replay/source-run.ts) |
|
|
1334
1371
|
| **R7** | Usage watermark, reporting, ticket, doctor | [control/transcript.ts](../src/control/transcript.ts), [remote-recorder.ts](../src/record/remote-recorder.ts) |
|
|
1335
1372
|
| **§6.5** *(the package is not on PyPI; the service's part is not deployed)* | A plan stepped through by the adapter of a host that is not Claude Code: the declines `host_records_only` and `host_tool_missing`, the stops `host_rejected` and `host_edited`, the cost from the adapter's `usage` | [control/server.ts](../src/control/server.ts), [controller.ts](../src/replay/controller.ts), [handover.ts](../src/replay/handover.ts), [safety.ts](../src/replay/safety.ts), [bin/investigate.ts](../src/bin/investigate.ts); the proof: `examples/langchain-proof/` in the repository (not in the npm package) |
|
|
1373
|
+
| **Redo and corrections** *(not yet released; the service's autocorrect.md)* | The report's `redoneBy`; a redone repeat opens with the plan's own calls, whole and marked `byPlan`; steps that run only when (contract 2); `scor_` corrections in `bir scenario edits` and `undo`; `bir investigate`'s repeat, verdict, hold and correction lines | [control/server.ts](../src/control/server.ts), [controller.ts](../src/replay/controller.ts), [plan.ts](../src/replay/plan.ts), [scenario-edit.ts](../src/bin/scenario-edit.ts), [investigate.ts](../src/bin/investigate.ts); tests: [step-tests.test.ts](../test/step-tests.test.ts), [newest-run.test.ts](../test/newest-run.test.ts) |
|
|
1336
1374
|
| **§9.2** *(not yet released)* | `bir scenario show/check/edit/edits/undo/calc/editing`, the `bir` server's scenario tools, `bir` calls never recorded (`editSteps.md` D13) | [bin/scenario-edit.ts](../src/bin/scenario-edit.ts), [bin/bir-scenario.ts](../src/bin/bir-scenario.ts), [record/housekeeping.ts](../src/record/housekeeping.ts); tests: [scenario-edit.test.ts](../test/scenario-edit.test.ts), [bir-scenario.test.ts](../test/bir-scenario.test.ts), [housekeeping.test.ts](../test/housekeeping.test.ts), [replay-server.test.ts](../test/replay-server.test.ts) |
|
|
1337
1375
|
|
|
1338
1376
|
```bash
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@basein/runner",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.16",
|
|
4
4
|
"description": "A recording MCP proxy: sits between any MCP client and its MCP servers, executes each call on the client's behalf, and records the run as a reusable BaseIn scenario.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|