@basein/runner 0.3.21 → 0.3.23

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.
Files changed (50) hide show
  1. package/dist/bin/bir-hooks.d.ts +12 -16
  2. package/dist/bin/bir-hooks.js +12 -16
  3. package/dist/bin/bir-scenario.js +1 -1
  4. package/dist/bin/bir.js +1 -21
  5. package/dist/bin/investigate.d.ts +0 -9
  6. package/dist/bin/investigate.js +14 -42
  7. package/dist/bin/scenario-edit.js +13 -44
  8. package/dist/control/pin-guard.d.ts +16 -0
  9. package/dist/control/pin-guard.js +36 -0
  10. package/dist/control/recorder-main.d.ts +0 -15
  11. package/dist/control/recorder-main.js +7 -39
  12. package/dist/control/server.d.ts +81 -131
  13. package/dist/control/server.js +460 -968
  14. package/dist/record/recorder.d.ts +47 -138
  15. package/dist/record/recorder.js +0 -3
  16. package/dist/record/remote-recorder.d.ts +1 -9
  17. package/dist/record/remote-recorder.js +11 -49
  18. package/dist/record/switchable-recorder.js +0 -2
  19. package/dist/replay/bundle.d.ts +2 -2
  20. package/dist/replay/bundle.js +3 -3
  21. package/dist/replay/controller.d.ts +75 -124
  22. package/dist/replay/controller.js +207 -529
  23. package/dist/replay/derive.d.ts +0 -10
  24. package/dist/replay/derive.js +0 -3
  25. package/dist/replay/handover.d.ts +2 -37
  26. package/dist/replay/handover.js +38 -106
  27. package/dist/replay/logic.d.ts +0 -11
  28. package/dist/replay/logic.js +0 -24
  29. package/dist/replay/plan.d.ts +43 -163
  30. package/dist/replay/plan.js +144 -556
  31. package/dist/replay/safety.d.ts +4 -5
  32. package/dist/replay/safety.js +7 -7
  33. package/dist/replay/source-run.d.ts +15 -24
  34. package/dist/replay/source-run.js +25 -58
  35. package/dist/replay/targets.d.ts +11 -21
  36. package/dist/replay/targets.js +30 -69
  37. package/dist/replay/types.d.ts +9 -96
  38. package/dist/replay/types.js +3 -16
  39. package/dist/util/service-headers.d.ts +1 -2
  40. package/dist/util/service-headers.js +1 -2
  41. package/docs/calculatedReplay.md +17 -74
  42. package/docs/calculatedReplayGuide.md +10 -30
  43. package/package.json +1 -1
  44. package/production.md +5 -35
  45. package/dist/replay/condition-words.d.ts +0 -28
  46. package/dist/replay/condition-words.js +0 -59
  47. package/dist/replay/flatten.d.ts +0 -161
  48. package/dist/replay/flatten.js +0 -222
  49. package/dist/replay/reads-only.d.ts +0 -29
  50. package/dist/replay/reads-only.js +0 -261
@@ -11,14 +11,13 @@
11
11
  * degrades to "run the turn normally" or returns something the session can
12
12
  * ignore. Nothing throws at a hook.
13
13
  */
14
- import type { ExecutionReport, MatchKey, MatchSegment, MatchVerdict } from "../record/recorder.js";
14
+ import type { ExecutionReport } from "../record/recorder.js";
15
15
  import { type ReplayMode, type StepReach } from "./coverage.js";
16
16
  import { deriveParameters, type AgentDay, type DeriveContext, type DeriveResult } from "./derive.js";
17
17
  import { ProxyWorkQueue } from "./executor.js";
18
18
  import { ScenarioReplayPlan } from "./plan.js";
19
19
  import { type HostSinks } from "./safety.js";
20
20
  import { SourceRunOutputs } from "./source-run.js";
21
- import { type FlatStop } from "./flatten.js";
22
21
  import { type ExecutionOutcome, type ExecutionStepResult, type FallbackKind, type SerializedScenario, type SerializedScenarioStep } from "./types.js";
23
22
  /** The first-party tool a `direct` plan is delivered through (§6.3). */
24
23
  export declare const DIRECT_TOOL_NAME = "mcp__bir__run_scenario";
@@ -239,14 +238,6 @@ export declare const RUN_NO_PLAN = "no_plan";
239
238
  export interface ReplayOptions {
240
239
  /** On unless `BIR_REPLAY=0`. Everything here is inert when false. */
241
240
  enabled: boolean;
242
- /**
243
- * Whether a handed-out segment may run mid-task. The recorder sets it from
244
- * `BIR_SEGMENT_ARM`: on unless that is `0`, from runner 0.3.19 (the
245
- * service's segmentsbreakdown.md SB-21). Omitted, as by a test or an
246
- * embedder, it is off: observe-only, where the runner asks for no segment,
247
- * arms none, and only logs what would have armed (R-OUT-10).
248
- */
249
- segmentArm?: boolean;
250
241
  /** `BIR_REPLAY_ALLOW_SERVERS`; undefined means every wrapped server. */
251
242
  allowServers?: ReadonlySet<string>;
252
243
  /**
@@ -307,21 +298,13 @@ export interface ReplayOptions {
307
298
  export interface IntentMatchOptions {
308
299
  /** `BIR_INTENT_MATCH`. */
309
300
  enabled: boolean;
310
- /**
311
- * How long a `PreToolUse` waits for the service. A timeout is a miss.
312
- *
313
- * 4 000 ms, not the 1 500 of Part 1: a request whose best segment lands in
314
- * the *not clear* band has one live question to ask under the server's own
315
- * 2 500 ms attempt, and must still answer well inside the hook's 30 s
316
- * (segmented.md R-HIT-11). A clear match or a miss answers as fast as before.
317
- */
301
+ /** How long a probe's request may take. A timeout is a miss. */
318
302
  budgetMs: number;
319
303
  /** Plans intent matching may arm in one turn. */
320
304
  maxPerTurn: number;
321
305
  /**
322
- * Probes one turn may *send*. Separate from `maxPerTurn`, and much larger:
323
- * arming three plans is a claim on the turn, while counting hits is only
324
- * bookkeeping, and it must not stop at the third arm (R-HIT-5).
306
+ * Probes one turn may *send*. Separate from `maxPerTurn`, and larger: most
307
+ * probes find nothing, and a turn that armed nothing yet may still match.
325
308
  */
326
309
  maxRequestsPerTurn: number;
327
310
  }
@@ -401,24 +384,20 @@ export interface ReplayState {
401
384
  armedAt: number;
402
385
  reported: boolean;
403
386
  sourceRun?: SourceRunOutputs;
404
- /**
405
- * Why the flattener stopped the plan, when it is what stopped it: a call this
406
- * chain cannot run right now (segmented.md R-CALL-29). The note names the
407
- * sub-task and the reason, and the report carries `unusable_call`.
408
- */
409
- flatStop?: FlatStop;
410
387
  /** Set once a plan has been retired, so nothing re-arms mid-turn. */
411
388
  retired: boolean;
412
389
  /** What armed it: the prompt, or a ReAct iteration's intent (fallbk.md). */
413
390
  armedBy: "prompt" | "intent";
414
391
  /**
415
- * The user's request this plan serves, in their own words: the prompt for a
416
- * plan the prompt armed (set in `arm`), the turn's prompt for one an intent
417
- * match armed (set by the control server). Read only for the note of a plan
418
- * stopped at its decision, which quotes the request's condition sentences
419
- * (the service's segmentsbreakdown.md SB-18). Never sent anywhere.
392
+ * Set on a continuation (the service's fallbk.md Part 3): a plan armed
393
+ * mid-turn because the plan before it handed over at a step, and the work
394
+ * recorded after an earlier hand-over at that step was calculated into this
395
+ * plan. `armedBy` is `intent` — it arms mid-turn, and is billed for its own
396
+ * window like any plan armed there — but it finishes the user's request
397
+ * rather than a part of the agent's own task, and its report books no
398
+ * saving: the plan it continued books the whole turn.
420
399
  */
421
- request?: string;
400
+ continuation?: ContinuationOf;
422
401
  /**
423
402
  * The Claude Code session this plan serves, set by the control server. Its
424
403
  * steps run on that session's own proxies (executor.ts). Absent for `bir
@@ -443,25 +422,6 @@ export interface ReplayState {
443
422
  * paths, which resolve against the project directory as the host's do.
444
423
  */
445
424
  bashCwd?: string;
446
- /**
447
- * Which kind of row was handed out (segmented.md R-OUT-7). A `segment` is a
448
- * named sub-task of a recording rather than a whole task, and it is judged
449
- * against its own similarity gate. Absent from an older server, and then read
450
- * as `scenario` (R-COMPAT-2).
451
- */
452
- kind: "scenario" | "segment";
453
- /** Where a handed-out segment sits in its recording (R-OUT-8). */
454
- segment?: MatchSegment;
455
- /** Which of the segment's two keys scored (R-OUT-3). For logs. */
456
- key?: MatchKey;
457
- /** The cheap model's answer, when the score sat in the not-clear band. */
458
- verified?: MatchVerdict;
459
- /**
460
- * Segments of this plan's own recording that armed later in the same turn
461
- * (R-MONEY-4). Their share comes off this plan's baseline, so one recording's
462
- * steps are never counted in two baselines of one turn.
463
- */
464
- sharedWith: string[];
465
425
  /** Set when the plan handed the task to the model part-way (fallbk.md). */
466
426
  handover?: Handover;
467
427
  /**
@@ -483,11 +443,9 @@ export interface ReplayState {
483
443
  */
484
444
  diverging?: boolean;
485
445
  /**
486
- * Which gate declined, as a fixed code (segmented.md R-HIT-14). The prose
487
- * `why` that `decline()` logs stays free text; this is what code reads — the
488
- * position rule (R-OUT-6) reports a position only for `known_bad_first_step`,
489
- * and reading prose for that would break the first time a sentence was
490
- * reworded.
446
+ * Which gate declined, as a fixed code. The prose `why` that `decline()`
447
+ * logs stays free text; this is what code and the report read, and reading
448
+ * prose would break the first time a sentence was reworded.
491
449
  */
492
450
  declined?: DeclineCode;
493
451
  /**
@@ -507,10 +465,7 @@ export interface ReplayState {
507
465
  * A plan that finished its work stops costing at that moment: the agent's
508
466
  * later work in the same turn is the agent's, not this plan's. A plan that
509
467
  * handed over or diverged takes no mark, because the follow-on work is the
510
- * direct consequence of its stopping (R-MONEY-3, R-FALL-5) — except a plan
511
- * stopped at its decision after a step ran: it did all it was for, and the
512
- * agent's decision and the half it chose are the agent's own (the service's
513
- * segmentsbreakdown.md SB-26).
468
+ * direct consequence of its stopping (R-MONEY-3, R-FALL-5).
514
469
  */
515
470
  retiredMark?: unknown;
516
471
  retiredAt?: number;
@@ -578,10 +533,7 @@ export interface ReplayState {
578
533
  * copies are cut to 3,800 bytes, and the agent's work is built on them.
579
534
  */
580
535
  keepCalls?: boolean;
581
- /**
582
- * Those calls, in the order the plan made them, keyed by frame and step —
583
- * a sub-task called twice keeps both calls of each step.
584
- */
536
+ /** Those calls, in the order the plan made them, keyed by step. */
585
537
  turnCalls?: Map<string, TurnCall>;
586
538
  /** What they hold, in characters: bounded by {@link TURN_CALLS_MAX_CHARS}. */
587
539
  turnCallChars?: number;
@@ -624,7 +576,7 @@ export interface TurnCall {
624
576
  * Why a match did not arm. One code per gate of the ladder, so the caller can
625
577
  * act on a decline without parsing the sentence that explains it.
626
578
  */
627
- export type DeclineCode = "replay_disabled" | "not_ready" | "flatten_failed" | "known_bad_first_step" | "nondeterministic_first_step" | "unusable_first_step" | "coverage" | "missing_target" | "no_derive_key"
579
+ export type DeclineCode = "replay_disabled" | "not_ready" | "known_bad_first_step" | "nondeterministic_first_step" | "coverage" | "missing_target" | "no_derive_key"
628
580
  /** The service handed over no plan: this runner is too old for it. */
629
581
  | "runner_update_required"
630
582
  /**
@@ -654,7 +606,33 @@ export type DeclineCode = "replay_disabled" | "not_ready" | "flatten_failed" | "
654
606
  * the prompt had been answered, or while prompts were not waiting on it
655
607
  * (the match gate). Set by the control server, like the two above.
656
608
  */
657
- | "match_late";
609
+ | "match_late"
610
+ /**
611
+ * A continuation (the service's fallbk.md Part 3) whose first step is not
612
+ * the call the model is making after the hand-over: another tool, or the
613
+ * same tool on something else. The model chose what to do next — the
614
+ * judgement the plan before it stopped for — and chose otherwise. Set by
615
+ * the control server.
616
+ */
617
+ | "not_first_step";
618
+ /**
619
+ * What a continuation continues (the service's fallbk.md Part 3): the plan
620
+ * that handed over, the step it stopped in front of, and the recording of the
621
+ * work done after an earlier hand-over there, which this plan was calculated
622
+ * from.
623
+ */
624
+ export interface ContinuationOf {
625
+ ofScenarioId: string;
626
+ stepIndex: number;
627
+ runId: string;
628
+ }
629
+ /**
630
+ * Whether a plan is a part of the task the agent is already doing — armed
631
+ * mid-turn by its intent — so its words say "continue" and never "answer the
632
+ * user". A continuation is armed mid-turn too, and it is the rest of the
633
+ * user's request: it ends with the answer.
634
+ */
635
+ export declare function isSubTask(state: Pick<ReplayState, "armedBy" | "continuation">): boolean;
658
636
  /** What `PreToolUse` should do about this call. */
659
637
  export type PreToolAction =
660
638
  /** Pin the arguments and let the real tool run. */
@@ -711,16 +689,6 @@ export declare class ReplayController {
711
689
  */
712
690
  private hostSignals;
713
691
  constructor(opts: ReplayOptions);
714
- /**
715
- * Whether a handed-out segment may actually arm (segmented.md R-OUT-10).
716
- *
717
- * Only when the option says so. The recorder passes it on unless
718
- * `BIR_SEGMENT_ARM=0` (from 0.3.19; the service's segmentsbreakdown.md
719
- * SB-21); a controller built without it stays observe-only, asks for no
720
- * segment and arms none. The server enforces the same thing from its side
721
- * (R-OUT-9).
722
- */
723
- get segmentArm(): boolean;
724
692
  /** The plan's own delivery vehicle is never a scenario step. */
725
693
  isDirectTool(toolName: string): boolean;
726
694
  /**
@@ -742,6 +710,14 @@ export declare class ReplayController {
742
710
  * itself as one that never reported.
743
711
  */
744
712
  lateMatch(match: RunMatchLike, host?: HostContext): ReplayState;
713
+ /**
714
+ * The state of a continuation handed out and not armed (the service's
715
+ * fallbk.md Part 3): declined before any gate ran — its first step is not
716
+ * the model's call (`not_first_step`), or the service's answer came too
717
+ * late (`match_late`). Like {@link lateMatch}, it exists only for its
718
+ * ticket's report, with the reason.
719
+ */
720
+ declinedContinuation(match: RunMatchLike, code: DeclineCode, continuation: ContinuationOf): ReplayState;
745
721
  /**
746
722
  * Run the gate ladder and, if every gate passes, arm a plan.
747
723
  *
@@ -750,24 +726,21 @@ export declare class ReplayController {
750
726
  * up with a denominator nobody measured.
751
727
  *
752
728
  * `host` is given for a session whose host is not Claude Code, and is kept
753
- * on the state (see {@link HostContext}).
729
+ * on the state (see {@link HostContext}). `extra.continuation` arms a
730
+ * continuation (the service's fallbk.md Part 3): see
731
+ * {@link ReplayState.continuation}.
754
732
  */
755
- arm(match: RunMatchLike, prompt: string, wrapped: ReadonlySet<string>, armedBy?: "prompt" | "intent", ctx?: DeriveContext, host?: HostContext): Promise<ReplayState>;
733
+ arm(match: RunMatchLike, prompt: string, wrapped: ReadonlySet<string>, armedBy?: "prompt" | "intent", ctx?: DeriveContext, host?: HostContext, extra?: {
734
+ continuation?: ContinuationOf;
735
+ }): Promise<ReplayState>;
756
736
  /**
757
737
  * The armed plan as data: the steps it will run, in order, and where it
758
738
  * stops when that is before its end. For a host that steps through the plan
759
739
  * itself and must not have to pick the tool names out of a directive
760
740
  * written for a model — a reworded sentence would break every adapter in
761
- * the field. Built from the same entries the directive lists.
741
+ * the field. Built from the same steps the directive lists.
762
742
  */
763
743
  planData(state: ReplayState): PlanData | undefined;
764
- /**
765
- * Whether the plan's steps end in front of a decision (the service's
766
- * segmentsbreakdown.md SB-6): it runs the steps before it, then hands the
767
- * task back there. The control server gives such a plan a usage mark, so
768
- * that it is billed only up to the stop (SB-26).
769
- */
770
- stopsAtDecision(state: ReplayState): boolean;
771
744
  /**
772
745
  * Take back a plan that armed and has run nothing, as a decline.
773
746
  *
@@ -914,9 +887,9 @@ export declare class ReplayController {
914
887
  */
915
888
  postTool(state: ReplayState, toolUseId: string, output: string, failed?: string, how?: PostToolOptions): boolean;
916
889
  /**
917
- * A steer plan whose cursor reached its end: in front of a parked step (or a
918
- * call the service could not answer), which hands over; otherwise it ran to
919
- * its end — and from here every call is the model's own.
890
+ * A steer plan whose cursor reached its end: in front of a parked step,
891
+ * which hands over; otherwise it ran to its end — and from here every call
892
+ * is the model's own.
920
893
  */
921
894
  private finishSteer;
922
895
  /**
@@ -1035,6 +1008,13 @@ export declare class ReplayController {
1035
1008
  runId: string;
1036
1009
  ownCalls: number;
1037
1010
  };
1011
+ /**
1012
+ * What the continuations of this plan spent reading their values this
1013
+ * turn (the service's fallbk.md Part 3). Booked here, on the plan whose
1014
+ * report bills the whole turn: theirs book no saving, so a cost left on
1015
+ * them would be left out of the turn's saving.
1016
+ */
1017
+ chainDeriveUsd?: number;
1038
1018
  }): ExecutionReport | undefined;
1039
1019
  /**
1040
1020
  * The step a failed replay is fairly blamed on: the first whose own logic
@@ -1057,10 +1037,6 @@ export declare class ReplayController {
1057
1037
  * there is nothing to be similar *to* and nothing to decline. It books no
1058
1038
  * execution either — there is no ticket, and inventing a saving for a manual
1059
1039
  * invocation is exactly the kind of number a ledger must never contain.
1060
- *
1061
- * One served stop is kept: a plan's decision (the service's
1062
- * segmentsbreakdown.md SB-6). The run stops in front of it, and `stopped`
1063
- * says so with the reason `decision`.
1064
1040
  */
1065
1041
  runAdHoc(scenario: SerializedScenario, prompt: string, wrapped: ReadonlySet<string>): Promise<{
1066
1042
  ok: boolean;
@@ -1130,23 +1106,10 @@ export declare class ReplayController {
1130
1106
  */
1131
1107
  private handOver;
1132
1108
  /**
1133
- * Which kind of stop a plan's end is (plan-services.md D8). The stopping
1134
- * step's own served reason wins — the calculation's verdict is more exact
1135
- * than a counter — then a call the service could not answer, then a parked
1136
- * step.
1137
- */
1138
- private stopKindOf;
1139
- /**
1140
- * A divergence's bundle that ended at the plan's decision (SB-17): logged
1141
- * as `handOver` logs every other hand-over, and its usage mark taken there
1142
- * when a step of the plan ran (SB-26).
1143
- */
1144
- private decidedInDivergence;
1145
- /**
1146
- * The steps before flat position `bound` that ran, 1-based, and the ones
1147
- * their tests left out (AC5) — the plan cursor's own, and a divergence
1148
- * bundle's (`extra`). `left` is empty when no step was left out; `ran` is
1149
- * then not filled, since every step before `bound` ran.
1109
+ * The steps before position `bound` that ran, 1-based, and the ones their
1110
+ * tests left out (AC5) — the plan cursor's own, and a divergence bundle's
1111
+ * (`extra`). `left` is empty when no step was left out; `ran` is then not
1112
+ * filled, since every step before `bound` ran.
1150
1113
  */
1151
1114
  private ranBefore;
1152
1115
  private noteFor;
@@ -1255,12 +1218,6 @@ export declare class ReplayController {
1255
1218
  /** Outcomes only move forward; `not_steered` is the floor (§11.1). */
1256
1219
  private upgrade;
1257
1220
  private retire;
1258
- /**
1259
- * Where the plan's work ended, for its bill: now, and the transcript's
1260
- * watermark now. Taken at a completing retire, and at a decision hand-over
1261
- * after a step ran (the service's segmentsbreakdown.md SB-26).
1262
- */
1263
- private markRetired;
1264
1221
  }
1265
1222
  /** The subset of `RunMatch` replay consumes. Structural, so tests need no recorder. */
1266
1223
  export interface RunMatchLike {
@@ -1272,11 +1229,6 @@ export interface RunMatchLike {
1272
1229
  fallback?: {
1273
1230
  maxStepFailures: number;
1274
1231
  };
1275
- /** All absent from an older server (segmented.md R-COMPAT-2). */
1276
- kind?: "scenario" | "segment";
1277
- segment?: MatchSegment;
1278
- key?: MatchKey;
1279
- verified?: MatchVerdict;
1280
1232
  /** The frozen name, beside the runtime intent the plan runs on (R-INTENT-12). */
1281
1233
  intentName?: string;
1282
1234
  /** Why the service handed over no plan, when it says. Read for `runner_update_required`. */
@@ -1295,8 +1247,7 @@ export declare function knownBadStepIndex(steps: readonly SerializedScenarioStep
1295
1247
  *
1296
1248
  * A step that carries `stop` was decided by the service — `null` means run it,
1297
1249
  * even when its count is past the limit: its retry is due. A step without the
1298
- * field came from an older service, and the old counter rule decides it. Both
1299
- * can sit in one chain, since a called segment is served on its own terms.
1250
+ * field came from an older service, and the old counter rule decides it.
1300
1251
  */
1301
1252
  export declare function stopIndex(steps: readonly SerializedScenarioStep[], maxStepFailures: number | undefined): number | undefined;
1302
1253
  export {};