@basein/runner 0.3.18 → 0.3.20
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/dist/bin/bir-hooks.d.ts +11 -8
- package/dist/bin/bir-hooks.js +11 -8
- package/dist/bin/bir.js +20 -7
- package/dist/bin/investigate.d.ts +9 -0
- package/dist/bin/investigate.js +31 -12
- package/dist/bin/scenario-edit.js +10 -0
- package/dist/control/recorder-main.d.ts +15 -0
- package/dist/control/recorder-main.js +33 -8
- package/dist/control/server.d.ts +91 -11
- package/dist/control/server.js +736 -49
- package/dist/record/recorder.d.ts +47 -4
- package/dist/record/recorder.js +3 -0
- package/dist/record/remote-recorder.d.ts +9 -1
- package/dist/record/remote-recorder.js +30 -0
- package/dist/record/switchable-recorder.js +2 -0
- package/dist/replay/condition-words.d.ts +28 -0
- package/dist/replay/condition-words.js +59 -0
- package/dist/replay/controller.d.ts +45 -6
- package/dist/replay/controller.js +184 -51
- package/dist/replay/derive.d.ts +10 -0
- package/dist/replay/derive.js +3 -0
- package/dist/replay/handover.d.ts +7 -0
- package/dist/replay/handover.js +89 -51
- package/dist/replay/plan.d.ts +11 -4
- package/dist/replay/plan.js +28 -8
- package/dist/replay/reads-only.d.ts +29 -0
- package/dist/replay/reads-only.js +261 -0
- package/dist/replay/types.d.ts +19 -3
- package/docs/calculatedReplay.md +45 -5
- package/docs/calculatedReplayGuide.md +16 -4
- package/package.json +1 -1
- package/production.md +34 -7
|
@@ -197,8 +197,13 @@ export declare function isRecorderHealthReporter(r: Recorder): r is Recorder & R
|
|
|
197
197
|
* callers must stop recording it. What the caller does *instead* of recording is
|
|
198
198
|
* v2's subject (docs/calculatedReplay.md); stopping is mandatory either way.
|
|
199
199
|
*/
|
|
200
|
-
/**
|
|
201
|
-
|
|
200
|
+
/**
|
|
201
|
+
* Which of a segment's two keys scored against the live step (R-OUT-3).
|
|
202
|
+
* `decision`: no score at all — one of the calculated scenarios that can follow
|
|
203
|
+
* a plan's decision, handed out for the agent's next call (the service's
|
|
204
|
+
* offer1.md O3).
|
|
205
|
+
*/
|
|
206
|
+
export type MatchKey = "intent" | "firstStep" | "decision";
|
|
202
207
|
/** The cheap model's answer about one live step and one segment (R-VERIFY-1). */
|
|
203
208
|
export interface MatchVerdict {
|
|
204
209
|
model: string;
|
|
@@ -335,13 +340,21 @@ export interface IntentMatchRequest {
|
|
|
335
340
|
stepPosition?: number;
|
|
336
341
|
/** Recordings this turn must not count hits against (R-HIT-7). */
|
|
337
342
|
excludeRuns?: string[];
|
|
338
|
-
/**
|
|
343
|
+
/**
|
|
344
|
+
* Per plan of this turn: its recording, and how far it got in its own chain
|
|
345
|
+
* — the step it handed over at (a decision included), or its last step when
|
|
346
|
+
* it ran to its end (R-OUT-6). One entry per plan; the service maps each onto
|
|
347
|
+
* the recording and keeps the largest (its segmentsbreakdown.md D18).
|
|
348
|
+
*/
|
|
339
349
|
ranThrough?: Array<{
|
|
340
350
|
runId: string;
|
|
341
351
|
position: number;
|
|
342
352
|
scenarioId?: string;
|
|
343
353
|
}>;
|
|
344
|
-
/**
|
|
354
|
+
/**
|
|
355
|
+
* Present only while segment arming is on (R-OUT-9, R-OUT-10): the
|
|
356
|
+
* recorder's default from 0.3.19, absent with `BIR_SEGMENT_ARM=0`.
|
|
357
|
+
*/
|
|
345
358
|
acceptSegments?: boolean;
|
|
346
359
|
/** Always true from this runner (R-CALL-28). */
|
|
347
360
|
supportsCalls?: boolean;
|
|
@@ -398,6 +411,36 @@ export interface IntentMatcher {
|
|
|
398
411
|
matchIntent(req: IntentMatchRequest, opts?: IntentMatchOptions): Promise<IntentMatchAnswer | null>;
|
|
399
412
|
}
|
|
400
413
|
export declare function isIntentMatcher(r: Recorder): r is Recorder & IntentMatcher;
|
|
414
|
+
/** What a turn sends when a plan of it has stopped at its decision (the service's offer1.md O3). */
|
|
415
|
+
export interface NextScenariosRequest {
|
|
416
|
+
/** The turn's identity, as a probe sends it (R-HIT-6). */
|
|
417
|
+
turnId?: string;
|
|
418
|
+
/** Always true from this runner (R-CALL-28). */
|
|
419
|
+
supportsCalls?: boolean;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* One calculated scenario that can follow a plan's decision: a hand-out in the
|
|
423
|
+
* shape of a match, with its own ticket, `key: "decision"`, and the tool of its
|
|
424
|
+
* first step. The service leaves withheld ones out.
|
|
425
|
+
*/
|
|
426
|
+
export interface NextHandOut extends RunMatch {
|
|
427
|
+
/** The tool its first served step calls; null when the service could not say. */
|
|
428
|
+
firstTool?: string | null;
|
|
429
|
+
}
|
|
430
|
+
/** The service's answer: the scenarios, and where the plan's decision is. */
|
|
431
|
+
export interface NextScenariosAnswer {
|
|
432
|
+
next: NextHandOut[];
|
|
433
|
+
decisionStep: number | null;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Optional capability: the calculated scenarios that can follow a decision
|
|
437
|
+
* (offer1.md O3) need a service. `null` is "none" — an older service (404), a
|
|
438
|
+
* cool-down, or any failure: the agent then does the work itself, as before.
|
|
439
|
+
*/
|
|
440
|
+
export interface NextScenarioSource {
|
|
441
|
+
nextScenarios(planId: string, req: NextScenariosRequest, opts?: IntentMatchOptions): Promise<NextScenariosAnswer | null>;
|
|
442
|
+
}
|
|
443
|
+
export declare function isNextScenarioSource(r: Recorder): r is Recorder & NextScenarioSource;
|
|
401
444
|
/**
|
|
402
445
|
* What a matched turn actually cost, reported once per match
|
|
403
446
|
* (docs/calculatedReplay.md §11).
|
package/dist/record/recorder.js
CHANGED
|
@@ -20,6 +20,9 @@ export function isServiceProber(r) {
|
|
|
20
20
|
export function isIntentMatcher(r) {
|
|
21
21
|
return typeof r.matchIntent === "function";
|
|
22
22
|
}
|
|
23
|
+
export function isNextScenarioSource(r) {
|
|
24
|
+
return typeof r.nextScenarios === "function";
|
|
25
|
+
}
|
|
23
26
|
export function isToolListReporter(r) {
|
|
24
27
|
return typeof r.reportTools === "function";
|
|
25
28
|
}
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
*/
|
|
51
51
|
import { type AuthSession } from "../auth/client.js";
|
|
52
52
|
import { type ServiceHealth } from "../util/service-health.js";
|
|
53
|
-
import type { AnswerCall, AttentionPlan, DisputeAnswer, ExecutionReport, FlushOptions, IntentMatchAnswer, IntentMatchOptions, IntentMatchRequest, Recorder, RecorderHealth, RecorderHealthReporter, RunCreation, RunMatch, RunMetrics, StartRunOptions, StepMetadata, ToolListEntry } from "./recorder.js";
|
|
53
|
+
import type { AnswerCall, AttentionPlan, DisputeAnswer, ExecutionReport, FlushOptions, IntentMatchAnswer, IntentMatchOptions, IntentMatchRequest, NextScenariosAnswer, NextScenariosRequest, Recorder, RecorderHealth, RecorderHealthReporter, RunCreation, RunMatch, RunMetrics, StartRunOptions, StepMetadata, ToolListEntry } from "./recorder.js";
|
|
54
54
|
/**
|
|
55
55
|
* Caps for the free text this recorder sends, in UTF-8 bytes. Tool payloads
|
|
56
56
|
* arrive capped at 64 KiB by their producers; text never passed through one,
|
|
@@ -206,6 +206,14 @@ export declare class RemoteRecorder implements Recorder, RecorderHealthReporter
|
|
|
206
206
|
* waiting, so an abandoned probe does not hold a socket for minutes.
|
|
207
207
|
*/
|
|
208
208
|
matchIntent(req: IntentMatchRequest, opts?: IntentMatchOptions): Promise<IntentMatchAnswer | null>;
|
|
209
|
+
/**
|
|
210
|
+
* The calculated scenarios that can follow a plan's decision (the service's
|
|
211
|
+
* offer1.md O3), each handed out with a ticket of its own. Asked once per
|
|
212
|
+
* decision hand-over, while the agent decides. An older service (404), a
|
|
213
|
+
* cool-down or any failure answers null: the agent then does the work
|
|
214
|
+
* itself, exactly as before.
|
|
215
|
+
*/
|
|
216
|
+
nextScenarios(planId: string, req: NextScenariosRequest, opts?: IntentMatchOptions): Promise<NextScenariosAnswer | null>;
|
|
209
217
|
/**
|
|
210
218
|
* The similar-prompt match for a run's create call (null = fresh run). Like
|
|
211
219
|
* {@link runCreated}, answers null at once while a failure is on record —
|
|
@@ -713,6 +713,36 @@ export class RemoteRecorder {
|
|
|
713
713
|
return null;
|
|
714
714
|
}
|
|
715
715
|
}
|
|
716
|
+
/**
|
|
717
|
+
* The calculated scenarios that can follow a plan's decision (the service's
|
|
718
|
+
* offer1.md O3), each handed out with a ticket of its own. Asked once per
|
|
719
|
+
* decision hand-over, while the agent decides. An older service (404), a
|
|
720
|
+
* cool-down or any failure answers null: the agent then does the work
|
|
721
|
+
* itself, exactly as before.
|
|
722
|
+
*/
|
|
723
|
+
async nextScenarios(planId, req, opts = {}) {
|
|
724
|
+
try {
|
|
725
|
+
if (!this.mayHotWait())
|
|
726
|
+
return null;
|
|
727
|
+
const answer = await this.request("POST", `/scenarios/${encodeURIComponent(planId)}/next`, JSON.stringify(req), { signal: opts.signal, timeoutMs: opts.timeoutMs ?? PROBE_TIMEOUT_MS });
|
|
728
|
+
const b = answer && typeof answer === "object" ? answer : {};
|
|
729
|
+
const next = Array.isArray(b.next)
|
|
730
|
+
? b.next.filter((n) => !!n && typeof n === "object" && typeof n.scenarioId === "string")
|
|
731
|
+
: [];
|
|
732
|
+
return { next, decisionStep: typeof b.decisionStep === "number" ? b.decisionStep : null };
|
|
733
|
+
}
|
|
734
|
+
catch (err) {
|
|
735
|
+
if (!opts.signal?.aborted) {
|
|
736
|
+
logEvery("recorder.next_scenarios_failed", 30_000, "replay.next_failed", {
|
|
737
|
+
error: errText(err),
|
|
738
|
+
why: err instanceof ServiceError && err.status === 404
|
|
739
|
+
? "the service has no next scenarios — an older service, or not this account's plan"
|
|
740
|
+
: "no next scenarios this time — the agent does the work after the decision",
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
return null;
|
|
744
|
+
}
|
|
745
|
+
}
|
|
716
746
|
/**
|
|
717
747
|
* The similar-prompt match for a run's create call (null = fresh run). Like
|
|
718
748
|
* {@link runCreated}, answers null at once while a failure is on record —
|
|
@@ -49,6 +49,8 @@ const NEUTRAL = {
|
|
|
49
49
|
settledMatch: () => Promise.resolve(null),
|
|
50
50
|
runCreated: () => Promise.resolve({ created: true }),
|
|
51
51
|
matchIntent: () => Promise.resolve(null),
|
|
52
|
+
// Signed out: no scenario follows a decision (offer1.md O3); the agent goes on.
|
|
53
|
+
nextScenarios: () => Promise.resolve(null),
|
|
52
54
|
reportExecution: () => undefined,
|
|
53
55
|
backfillContext: () => undefined,
|
|
54
56
|
probeIfFailing: () => undefined,
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* condition-words — the request's own words for its decision (the service's
|
|
3
|
+
* segmentsbreakdown.md SB-18).
|
|
4
|
+
*
|
|
5
|
+
* When a plan stops at its decision, the hand-over note quotes the sentences
|
|
6
|
+
* of the live request that hold a word for a condition ("if … otherwise"), so
|
|
7
|
+
* the agent sees which choice is its own to make. Only the request's words:
|
|
8
|
+
* never the condition the service keeps, which is written from the recorded
|
|
9
|
+
* run and would hint at the half the recording took.
|
|
10
|
+
*
|
|
11
|
+
* The word lists are the service's (its src/scenarios/condition.ts), copied
|
|
12
|
+
* exactly. Keep the two the same: the service decides which requests may hold
|
|
13
|
+
* a condition with them, and this quotes the sentences they find.
|
|
14
|
+
*/
|
|
15
|
+
/** The longest quote a decision note carries, in characters. */
|
|
16
|
+
export declare const MAX_DECISION_QUOTE = 600;
|
|
17
|
+
/** Whether a text has a word for a condition in it: the service's `mayHoldCondition`. */
|
|
18
|
+
export declare function mayHoldCondition(text: string): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* The sentences of `request` that hold a word for a condition, in order,
|
|
21
|
+
* each on one line of single spaces, joined by a space. Cut to at most `max`
|
|
22
|
+
* characters, ending in "…" when cut. Null when no sentence holds one.
|
|
23
|
+
*
|
|
24
|
+
* Lines are sentences of their own: the request is split at line breaks
|
|
25
|
+
* first, then at {@link SENTENCE_BREAK}.
|
|
26
|
+
*/
|
|
27
|
+
export declare function conditionSentences(request: string | null | undefined, max?: number): string | null;
|
|
28
|
+
//# sourceMappingURL=condition-words.d.ts.map
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* condition-words — the request's own words for its decision (the service's
|
|
3
|
+
* segmentsbreakdown.md SB-18).
|
|
4
|
+
*
|
|
5
|
+
* When a plan stops at its decision, the hand-over note quotes the sentences
|
|
6
|
+
* of the live request that hold a word for a condition ("if … otherwise"), so
|
|
7
|
+
* the agent sees which choice is its own to make. Only the request's words:
|
|
8
|
+
* never the condition the service keeps, which is written from the recorded
|
|
9
|
+
* run and would hint at the half the recording took.
|
|
10
|
+
*
|
|
11
|
+
* The word lists are the service's (its src/scenarios/condition.ts), copied
|
|
12
|
+
* exactly. Keep the two the same: the service decides which requests may hold
|
|
13
|
+
* a condition with them, and this quotes the sentences they find.
|
|
14
|
+
*/
|
|
15
|
+
/** Words that say a part of a task depends on something: English, and the Hebrew the service reads. */
|
|
16
|
+
const CONDITION_WORDS = /\b(if|otherwise|else|unless|only when|only if|in case|whether)\b/i;
|
|
17
|
+
const HEBREW_CONDITION = /(^|[\s,.;:!?"'()\-])(אם|אחרת|אלא אם|במקרה ש|רק אם|רק כאשר)($|[\s,.;:!?"'()\-])/;
|
|
18
|
+
/** The longest quote a decision note carries, in characters. */
|
|
19
|
+
export const MAX_DECISION_QUOTE = 600;
|
|
20
|
+
/** Whether a text has a word for a condition in it: the service's `mayHoldCondition`. */
|
|
21
|
+
export function mayHoldCondition(text) {
|
|
22
|
+
return CONDITION_WORDS.test(text) || HEBREW_CONDITION.test(text);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Where one sentence ends and the next begins: after `.`, `!`, `?` or `…` and
|
|
26
|
+
* white space, in front of a capital letter, a letter of a script with no
|
|
27
|
+
* capitals (Hebrew), a quote mark or a bracket. Never in front of a digit or a
|
|
28
|
+
* small letter, so "Nov. 20" and "e.g. the" stay in one sentence.
|
|
29
|
+
*/
|
|
30
|
+
const SENTENCE_BREAK = /(?<=[.!?…])\s+(?=[\p{Lu}\p{Lo}"'“‘(\[])/u;
|
|
31
|
+
/**
|
|
32
|
+
* The sentences of `request` that hold a word for a condition, in order,
|
|
33
|
+
* each on one line of single spaces, joined by a space. Cut to at most `max`
|
|
34
|
+
* characters, ending in "…" when cut. Null when no sentence holds one.
|
|
35
|
+
*
|
|
36
|
+
* Lines are sentences of their own: the request is split at line breaks
|
|
37
|
+
* first, then at {@link SENTENCE_BREAK}.
|
|
38
|
+
*/
|
|
39
|
+
export function conditionSentences(request, max = MAX_DECISION_QUOTE) {
|
|
40
|
+
if (typeof request !== "string" || request.trim() === "")
|
|
41
|
+
return null;
|
|
42
|
+
const sentences = request
|
|
43
|
+
.replace(/\r\n?/g, "\n")
|
|
44
|
+
.split("\n")
|
|
45
|
+
.flatMap((line) => line.split(SENTENCE_BREAK))
|
|
46
|
+
.map((s) => s.replace(/\s+/g, " ").trim())
|
|
47
|
+
.filter((s) => s !== "" && mayHoldCondition(s));
|
|
48
|
+
if (sentences.length === 0)
|
|
49
|
+
return null;
|
|
50
|
+
const quote = sentences.join(" ");
|
|
51
|
+
if (quote.length <= max)
|
|
52
|
+
return quote;
|
|
53
|
+
let cut = quote.slice(0, Math.max(0, max - 1));
|
|
54
|
+
// Never half of a character written as two code units (an emoji).
|
|
55
|
+
if (/[\uD800-\uDBFF]$/.test(cut))
|
|
56
|
+
cut = cut.slice(0, -1);
|
|
57
|
+
return `${cut.trimEnd()}…`;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=condition-words.js.map
|
|
@@ -240,8 +240,11 @@ export interface ReplayOptions {
|
|
|
240
240
|
/** On unless `BIR_REPLAY=0`. Everything here is inert when false. */
|
|
241
241
|
enabled: boolean;
|
|
242
242
|
/**
|
|
243
|
-
*
|
|
244
|
-
*
|
|
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).
|
|
245
248
|
*/
|
|
246
249
|
segmentArm?: boolean;
|
|
247
250
|
/** `BIR_REPLAY_ALLOW_SERVERS`; undefined means every wrapped server. */
|
|
@@ -398,6 +401,14 @@ export interface ReplayState {
|
|
|
398
401
|
retired: boolean;
|
|
399
402
|
/** What armed it: the prompt, or a ReAct iteration's intent (fallbk.md). */
|
|
400
403
|
armedBy: "prompt" | "intent";
|
|
404
|
+
/**
|
|
405
|
+
* The user's request this plan serves, in their own words: the prompt for a
|
|
406
|
+
* plan the prompt armed (set in `arm`), the turn's prompt for one an intent
|
|
407
|
+
* match armed (set by the control server). Read only for the note of a plan
|
|
408
|
+
* stopped at its decision, which quotes the request's condition sentences
|
|
409
|
+
* (the service's segmentsbreakdown.md SB-18). Never sent anywhere.
|
|
410
|
+
*/
|
|
411
|
+
request?: string;
|
|
401
412
|
/**
|
|
402
413
|
* The Claude Code session this plan serves, set by the control server. Its
|
|
403
414
|
* steps run on that session's own proxies (executor.ts). Absent for `bir
|
|
@@ -485,7 +496,10 @@ export interface ReplayState {
|
|
|
485
496
|
* A plan that finished its work stops costing at that moment: the agent's
|
|
486
497
|
* later work in the same turn is the agent's, not this plan's. A plan that
|
|
487
498
|
* handed over or diverged takes no mark, because the follow-on work is the
|
|
488
|
-
* direct consequence of its stopping (R-MONEY-3, R-FALL-5)
|
|
499
|
+
* direct consequence of its stopping (R-MONEY-3, R-FALL-5) — except a plan
|
|
500
|
+
* stopped at its decision after a step ran: it did all it was for, and the
|
|
501
|
+
* agent's decision and the half it chose are the agent's own (the service's
|
|
502
|
+
* segmentsbreakdown.md SB-26).
|
|
489
503
|
*/
|
|
490
504
|
retiredMark?: unknown;
|
|
491
505
|
retiredAt?: number;
|
|
@@ -687,9 +701,11 @@ export declare class ReplayController {
|
|
|
687
701
|
/**
|
|
688
702
|
* Whether a handed-out segment may actually arm (segmented.md R-OUT-10).
|
|
689
703
|
*
|
|
690
|
-
*
|
|
691
|
-
* `BIR_SEGMENT_ARM=
|
|
692
|
-
*
|
|
704
|
+
* Only when the option says so. The recorder passes it on unless
|
|
705
|
+
* `BIR_SEGMENT_ARM=0` (from 0.3.19; the service's segmentsbreakdown.md
|
|
706
|
+
* SB-21); a controller built without it stays observe-only, asks for no
|
|
707
|
+
* segment and arms none. The server enforces the same thing from its side
|
|
708
|
+
* (R-OUT-9).
|
|
693
709
|
*/
|
|
694
710
|
get segmentArm(): boolean;
|
|
695
711
|
/** The plan's own delivery vehicle is never a scenario step. */
|
|
@@ -732,6 +748,13 @@ export declare class ReplayController {
|
|
|
732
748
|
* the field. Built from the same entries the directive lists.
|
|
733
749
|
*/
|
|
734
750
|
planData(state: ReplayState): PlanData | undefined;
|
|
751
|
+
/**
|
|
752
|
+
* Whether the plan's steps end in front of a decision (the service's
|
|
753
|
+
* segmentsbreakdown.md SB-6): it runs the steps before it, then hands the
|
|
754
|
+
* task back there. The control server gives such a plan a usage mark, so
|
|
755
|
+
* that it is billed only up to the stop (SB-26).
|
|
756
|
+
*/
|
|
757
|
+
stopsAtDecision(state: ReplayState): boolean;
|
|
735
758
|
/**
|
|
736
759
|
* Take back a plan that armed and has run nothing, as a decline.
|
|
737
760
|
*
|
|
@@ -1021,6 +1044,10 @@ export declare class ReplayController {
|
|
|
1021
1044
|
* there is nothing to be similar *to* and nothing to decline. It books no
|
|
1022
1045
|
* execution either — there is no ticket, and inventing a saving for a manual
|
|
1023
1046
|
* invocation is exactly the kind of number a ledger must never contain.
|
|
1047
|
+
*
|
|
1048
|
+
* One served stop is kept: a plan's decision (the service's
|
|
1049
|
+
* segmentsbreakdown.md SB-6). The run stops in front of it, and `stopped`
|
|
1050
|
+
* says so with the reason `decision`.
|
|
1024
1051
|
*/
|
|
1025
1052
|
runAdHoc(scenario: SerializedScenario, prompt: string, wrapped: ReadonlySet<string>): Promise<{
|
|
1026
1053
|
ok: boolean;
|
|
@@ -1096,6 +1123,12 @@ export declare class ReplayController {
|
|
|
1096
1123
|
* step.
|
|
1097
1124
|
*/
|
|
1098
1125
|
private stopKindOf;
|
|
1126
|
+
/**
|
|
1127
|
+
* A divergence's bundle that ended at the plan's decision (SB-17): logged
|
|
1128
|
+
* as `handOver` logs every other hand-over, and its usage mark taken there
|
|
1129
|
+
* when a step of the plan ran (SB-26).
|
|
1130
|
+
*/
|
|
1131
|
+
private decidedInDivergence;
|
|
1099
1132
|
/**
|
|
1100
1133
|
* The steps before flat position `bound` that ran, 1-based, and the ones
|
|
1101
1134
|
* their tests left out (AC5) — the plan cursor's own, and a divergence
|
|
@@ -1209,6 +1242,12 @@ export declare class ReplayController {
|
|
|
1209
1242
|
/** Outcomes only move forward; `not_steered` is the floor (§11.1). */
|
|
1210
1243
|
private upgrade;
|
|
1211
1244
|
private retire;
|
|
1245
|
+
/**
|
|
1246
|
+
* Where the plan's work ended, for its bill: now, and the transcript's
|
|
1247
|
+
* watermark now. Taken at a completing retire, and at a decision hand-over
|
|
1248
|
+
* after a step ran (the service's segmentsbreakdown.md SB-26).
|
|
1249
|
+
*/
|
|
1250
|
+
private markRetired;
|
|
1212
1251
|
}
|
|
1213
1252
|
/** The subset of `RunMatch` replay consumes. Structural, so tests need no recorder. */
|
|
1214
1253
|
export interface RunMatchLike {
|