@basein/runner 0.2.11 → 0.3.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/README.md +540 -350
- package/dist/auth/client.d.ts +65 -3
- package/dist/auth/client.js +305 -53
- package/dist/bin/bir-hooks.d.ts +72 -31
- package/dist/bin/bir-hooks.js +176 -284
- package/dist/bin/bir-proxy.d.ts +17 -2
- package/dist/bin/bir-proxy.js +222 -28
- package/dist/bin/bir-scenario.d.ts +61 -10
- package/dist/bin/bir-scenario.js +704 -135
- package/dist/bin/bir.d.ts +14 -8
- package/dist/bin/bir.js +2937 -488
- package/dist/bin/investigate.js +41 -16
- package/dist/bin/scenario-edit.d.ts +43 -10
- package/dist/bin/scenario-edit.js +281 -80
- package/dist/bin/scenario-liveness.d.ts +194 -0
- package/dist/bin/scenario-liveness.js +459 -0
- package/dist/bin/scenario-stderr.d.ts +43 -0
- package/dist/bin/scenario-stderr.js +76 -0
- package/dist/bin/service-read.d.ts +88 -0
- package/dist/bin/service-read.js +128 -0
- package/dist/bin/setup.d.ts +19 -0
- package/dist/bin/setup.js +146 -20
- package/dist/config/adapters/claude-code.d.ts +92 -14
- package/dist/config/adapters/claude-code.js +291 -101
- package/dist/config/adapters/generic.d.ts +4 -1
- package/dist/config/adapters/generic.js +13 -14
- package/dist/config/generate.d.ts +392 -14
- package/dist/config/generate.js +666 -19
- package/dist/config/mcp-approval.d.ts +76 -0
- package/dist/config/mcp-approval.js +210 -0
- package/dist/config/node-path.d.ts +42 -0
- package/dist/config/node-path.js +102 -0
- package/dist/config/overrides.d.ts +62 -0
- package/dist/config/overrides.js +136 -0
- package/dist/config/resolve.d.ts +70 -8
- package/dist/config/resolve.js +186 -15
- package/dist/control/client.d.ts +22 -0
- package/dist/control/client.js +26 -7
- package/dist/control/correlation.d.ts +23 -5
- package/dist/control/correlation.js +0 -0
- package/dist/control/daemon.d.ts +400 -18
- package/dist/control/daemon.js +1105 -102
- package/dist/control/discovery.d.ts +77 -5
- package/dist/control/discovery.js +186 -11
- package/dist/control/ensure-hook.d.ts +61 -0
- package/dist/control/ensure-hook.js +160 -8
- package/dist/control/paths.d.ts +26 -0
- package/dist/control/paths.js +34 -0
- package/dist/control/pin-guard.d.ts +83 -0
- package/dist/control/pin-guard.js +534 -0
- package/dist/control/recorder-keeper.d.ts +229 -0
- package/dist/control/recorder-keeper.js +721 -0
- package/dist/control/recorder-main.d.ts +73 -0
- package/dist/control/recorder-main.js +778 -0
- package/dist/control/server.d.ts +707 -31
- package/dist/control/server.js +2803 -306
- package/dist/control/transcript.d.ts +21 -1
- package/dist/control/transcript.js +378 -112
- package/dist/index.d.ts +5 -1
- package/dist/index.js +5 -1
- package/dist/jsonrpc/framing.d.ts +76 -12
- package/dist/jsonrpc/framing.js +216 -59
- package/dist/proxy/intercept.d.ts +49 -5
- package/dist/proxy/intercept.js +188 -59
- package/dist/proxy/relay.d.ts +16 -0
- package/dist/proxy/relay.js +77 -5
- package/dist/proxy/revive.d.ts +122 -0
- package/dist/proxy/revive.js +251 -0
- package/dist/proxy/session.d.ts +222 -14
- package/dist/proxy/session.js +708 -99
- package/dist/record/housekeeping.d.ts +71 -0
- package/dist/record/housekeeping.js +417 -0
- package/dist/record/recorder.d.ts +51 -3
- package/dist/record/recorder.js +3 -0
- package/dist/record/redact.d.ts +103 -9
- package/dist/record/redact.js +659 -30
- package/dist/record/remote-recorder.d.ts +327 -32
- package/dist/record/remote-recorder.js +1517 -187
- package/dist/record/switchable-recorder.d.ts +126 -0
- package/dist/record/switchable-recorder.js +351 -0
- package/dist/record/truncate.d.ts +40 -1
- package/dist/record/truncate.js +111 -4
- package/dist/replay/bash-channel.d.ts +35 -0
- package/dist/replay/bash-channel.js +133 -0
- package/dist/replay/bundle.d.ts +10 -0
- package/dist/replay/bundle.js +10 -0
- package/dist/replay/controller.d.ts +371 -20
- package/dist/replay/controller.js +1185 -111
- package/dist/replay/derive.d.ts +1 -0
- package/dist/replay/derive.js +81 -24
- package/dist/replay/executor.d.ts +51 -16
- package/dist/replay/executor.js +114 -41
- package/dist/replay/handover.d.ts +54 -1
- package/dist/replay/handover.js +55 -5
- package/dist/replay/logic.d.ts +62 -10
- package/dist/replay/logic.js +140 -22
- package/dist/replay/plan.d.ts +201 -27
- package/dist/replay/plan.js +581 -141
- package/dist/replay/safety.d.ts +72 -0
- package/dist/replay/safety.js +303 -0
- package/dist/replay/source-run.d.ts +10 -0
- package/dist/replay/source-run.js +80 -35
- package/dist/replay/targets.d.ts +15 -1
- package/dist/replay/targets.js +62 -4
- package/dist/replay/tool-error.d.ts +15 -1
- package/dist/replay/tool-error.js +33 -1
- package/dist/replay/types.d.ts +7 -4
- package/dist/upstream/client.d.ts +56 -3
- package/dist/upstream/client.js +61 -4
- package/dist/upstream/headers.d.ts +43 -0
- package/dist/upstream/headers.js +90 -0
- package/dist/upstream/http-client.d.ts +213 -22
- package/dist/upstream/http-client.js +1034 -168
- package/dist/upstream/http-transport.d.ts +143 -0
- package/dist/upstream/http-transport.js +391 -0
- package/dist/upstream/lazy-client.d.ts +8 -0
- package/dist/upstream/lazy-client.js +29 -1
- package/dist/upstream/sse.d.ts +78 -0
- package/dist/upstream/sse.js +189 -0
- package/dist/upstream/stdio-client.d.ts +61 -1
- package/dist/upstream/stdio-client.js +356 -33
- package/dist/util/atomic-write.d.ts +29 -0
- package/dist/util/atomic-write.js +96 -0
- package/dist/util/journal.d.ts +15 -0
- package/dist/util/journal.js +48 -3
- package/dist/util/log.d.ts +10 -0
- package/dist/util/log.js +28 -1
- package/dist/util/off-switch.d.ts +46 -0
- package/dist/util/off-switch.js +91 -0
- package/dist/util/service-health.d.ts +133 -0
- package/dist/util/service-health.js +217 -0
- package/docs/BaseInstRunner.md +694 -621
- package/docs/calculatedReplay.md +1625 -1244
- package/docs/calculatedReplayGuide.md +1212 -879
- package/docs/installRun.md +1013 -772
- package/docs/loginWeb.md +627 -607
- package/docs/mcpmark.md +49 -16
- package/docs/my-first-sample.md +569 -545
- package/docs/quickstart.md +384 -271
- package/docs/t-bench.md +406 -394
- package/package.json +58 -57
- package/production.md +1371 -0
- package/scripts/install.ps1 +97 -6
- package/scripts/install.sh +104 -7
|
@@ -23,17 +23,160 @@ import { type ExecutionOutcome, type ExecutionStepResult, type FallbackKind, typ
|
|
|
23
23
|
export declare const DIRECT_TOOL_NAME = "mcp__bir__run_scenario";
|
|
24
24
|
/** How long a `/proxy/poll` is held open before it answers empty. */
|
|
25
25
|
export declare const POLL_HOLD_MS = 25000;
|
|
26
|
+
/**
|
|
27
|
+
* The longest a prompt ever waits for a steer plan's parameters so that its
|
|
28
|
+
* directive can preview them ({@link ReplayController.settleForPreview}).
|
|
29
|
+
* `UserPromptSubmit` is on the user's critical path: a preview is worth a
|
|
30
|
+
* little of it, never the derivation's whole budget.
|
|
31
|
+
*/
|
|
32
|
+
export declare const PREVIEW_SETTLE_MAX_MS = 1200;
|
|
26
33
|
export interface ReplayBudgets {
|
|
27
34
|
/** How long `/session/prompt` waits for the match. */
|
|
28
35
|
matchMs: number;
|
|
29
|
-
/** How long
|
|
36
|
+
/** How long `/scenario/run` waits for derivation, which has no hook timeout. */
|
|
30
37
|
deriveMs: number;
|
|
31
|
-
/**
|
|
38
|
+
/**
|
|
39
|
+
* Ceiling on a whole direct plan. 100 s, so that `deriveMs + planMs` plus the
|
|
40
|
+
* last step's `MIN_STEP_MS` and its logic stays under Claude Code's 2-minute
|
|
41
|
+
* auto-background of an MCP tool call: a backgrounded `run_scenario` leaves
|
|
42
|
+
* the model doing the task while this keeps starting its steps.
|
|
43
|
+
* `BIR_REPLAY_PLAN_BUDGET_MS` (120 000 was the old default; past about
|
|
44
|
+
* 110 000 a long run may be backgrounded).
|
|
45
|
+
*/
|
|
32
46
|
planMs: number;
|
|
33
47
|
/** Ceiling on one direct `tools/call`. */
|
|
34
48
|
stepMs: number;
|
|
49
|
+
/**
|
|
50
|
+
* How long a *hook* waits for derivation: arming inside `UserPromptSubmit`
|
|
51
|
+
* (whose whole answer, match included, must land within the control server's
|
|
52
|
+
* prompt deadline) and the first `PreToolUse`. Capped by `deriveMs`.
|
|
53
|
+
* `BIR_HOOK_DERIVE_BUDGET_MS`.
|
|
54
|
+
*/
|
|
55
|
+
hookDeriveMs: number;
|
|
56
|
+
/**
|
|
57
|
+
* Everything replay does inside one `PreToolUse`: the wait for the
|
|
58
|
+
* parameters and, on a divergence, executing the rest of the plan. The host
|
|
59
|
+
* gives the hook 15 s and the control server answers by 12 s whatever
|
|
60
|
+
* happens, so this stays below both: a step started after the host stopped
|
|
61
|
+
* waiting is a side effect the model is never told about.
|
|
62
|
+
* `BIR_PRETOOL_REPLAY_BUDGET_MS`, held at or under
|
|
63
|
+
* {@link MAX_PRETOOL_REPLAY_MS} whatever is asked.
|
|
64
|
+
*/
|
|
65
|
+
preToolMs: number;
|
|
35
66
|
}
|
|
36
67
|
export declare const DEFAULT_BUDGETS: ReplayBudgets;
|
|
68
|
+
/**
|
|
69
|
+
* The most {@link ReplayBudgets.preToolMs} may be: the last step started at the
|
|
70
|
+
* budget's end still gets its {@link MIN_STEP_MS} and the margin before the
|
|
71
|
+
* route's deadline answers the host. Past it, a divergence kept starting steps
|
|
72
|
+
* — writes, unattended — after the model had been told to carry on itself.
|
|
73
|
+
*/
|
|
74
|
+
export declare const MAX_PRETOOL_REPLAY_MS: number;
|
|
75
|
+
/** What the caller's hook knows about its own deadline (see {@link ReplayController.preTool}). */
|
|
76
|
+
export interface PreToolOptions {
|
|
77
|
+
/**
|
|
78
|
+
* When the caller answers the host whatever happens (epoch ms). No step is
|
|
79
|
+
* started later than {@link MIN_STEP_MS} and a margin before it.
|
|
80
|
+
*/
|
|
81
|
+
answerBy?: number;
|
|
82
|
+
/**
|
|
83
|
+
* Aborted once the host has been answered without this call's action — the
|
|
84
|
+
* route's deadline guard spoke first. No further step is started.
|
|
85
|
+
*/
|
|
86
|
+
signal?: AbortSignal;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* What the caller knows about how a pinned call ended, beyond its error text
|
|
90
|
+
* ({@link ReplayController.postTool}). Both optional: without them the text
|
|
91
|
+
* and the output are read (`pinFailure`).
|
|
92
|
+
*/
|
|
93
|
+
export interface PostToolOptions {
|
|
94
|
+
/**
|
|
95
|
+
* The call was sent and never answered — its proxy abandoned it (the
|
|
96
|
+
* upstream closed or restarted, the host cancelled it) or its answer could
|
|
97
|
+
* not be delivered. It may or may not have done its work.
|
|
98
|
+
*/
|
|
99
|
+
uncertain?: boolean;
|
|
100
|
+
/** The call was never sent: its upstream was already gone. Nothing of it happened. */
|
|
101
|
+
notSent?: boolean;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* The longest a direct run may take by its budgets, end to end.
|
|
105
|
+
*
|
|
106
|
+
* `bir-scenario` gives `/scenario/run` 290 s (`BIR_SCENARIO_RUN_TIMEOUT_MS`),
|
|
107
|
+
* just under Claude Code's 5-minute idle timeout for an MCP call, and a run
|
|
108
|
+
* that answers after its client stopped waiting is a replay thrown away with
|
|
109
|
+
* its steps already done. So the budgets are held to 270 s, and the defaults
|
|
110
|
+
* sit far below it: 8 s of derivation + 100 s of plan (more only when an
|
|
111
|
+
* operator sets `BIR_REPLAY_BUDGET_MS` or `BIR_REPLAY_PLAN_BUDGET_MS`) + 12 s
|
|
112
|
+
* of slack (one last step's 1 s, six 1 s logic bodies, a 5 s margin —
|
|
113
|
+
* {@link clampDirectRun}) = 120 s.
|
|
114
|
+
*
|
|
115
|
+
* Only `/scenario/run` is held to it. `bir replay` (`runAdHoc`) answers the
|
|
116
|
+
* operator's own command, which no MCP client times out, and keeps the budgets
|
|
117
|
+
* the environment asked for.
|
|
118
|
+
*/
|
|
119
|
+
export declare const DIRECT_RUN_CEILING_MS = 270000;
|
|
120
|
+
/**
|
|
121
|
+
* The direct-run budgets, held under {@link DIRECT_RUN_CEILING_MS}.
|
|
122
|
+
*
|
|
123
|
+
* The worst case is `deriveMs + planMs` plus what can happen past the plan's
|
|
124
|
+
* deadline: one last step's {@link MIN_STEP_MS}, {@link LOGIC_BODIES_OUTSIDE_PLAN}
|
|
125
|
+
* logic bodies, and the margin. `stepMs` never adds to it — each step is
|
|
126
|
+
* clipped to what the plan has left. Budgets that would pass the ceiling are
|
|
127
|
+
* brought under it — the derivation wait first to {@link MAX_DIRECT_DERIVE_MS},
|
|
128
|
+
* then the plan — and `clamped` says what changed, for the log.
|
|
129
|
+
*/
|
|
130
|
+
export declare function clampDirectRun(budgets: ReplayBudgets, logicMs?: number): {
|
|
131
|
+
budgets: ReplayBudgets;
|
|
132
|
+
clamped?: {
|
|
133
|
+
deriveMs?: [number, number];
|
|
134
|
+
planMs?: [number, number];
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* What `/scenario/run` answers — {@link ReplayController.runArmed}'s result,
|
|
139
|
+
* sent as the response body as it is. `bir-scenario` renders it (bin/
|
|
140
|
+
* bir-scenario.ts, `toolResult`); these are the shapes it can meet:
|
|
141
|
+
*
|
|
142
|
+
* - **ok** — the plan ran to its end: `{ ok: true, text, responseModel, steps,
|
|
143
|
+
* partial: false }`. `text` is the bundle; `responseModel` what the
|
|
144
|
+
* scenario's own logic built from it (`{}` when it has none).
|
|
145
|
+
* - **partial** — the plan stopped part-way after running steps here: `{ ok:
|
|
146
|
+
* true, text, responseModel: {}, steps, partial }`. `text` opens with the
|
|
147
|
+
* hand-over note (text beginning `[BaseInstRunner calculated replay]`) and
|
|
148
|
+
* carries the bundle of what ran below it. `partial` is true only when the
|
|
149
|
+
* time budget, or the caller leaving, cut it short; a step that broke says
|
|
150
|
+
* so in the note instead.
|
|
151
|
+
* - **hand-over note** — nothing ran here, and the model does the task: `{ ok:
|
|
152
|
+
* false, why: <note>, note: true }`. `why` is written for the model and
|
|
153
|
+
* opens with `[BaseInstRunner calculated replay]`; `note` marks it so, for
|
|
154
|
+
* a client that should pass it through rather than say something of its own.
|
|
155
|
+
* - **no_plan** — nothing is armed for this call, or the plan retired before
|
|
156
|
+
* it ran: `{ ok: false, why: "no_plan" }`. The common case, never an error.
|
|
157
|
+
* - **not run** — the run itself threw, or the scenario's logic did: `{ ok:
|
|
158
|
+
* false, why }` with a short reason for the log (`"not_ready"`, `"the
|
|
159
|
+
* scenario could not be run"`), never written for the model.
|
|
160
|
+
* - **already running** — a second `runArmed` for a plan that is running is
|
|
161
|
+
* handed the first one's promise and so the first one's result, whichever
|
|
162
|
+
* shape it takes: the plan is never driven twice. (At HTTP, the control
|
|
163
|
+
* server answers a second `/scenario/run` for a running plan itself, with
|
|
164
|
+
* `{ ok: false, why: <its ALREADY_RUNNING text> }`, before it reaches here.)
|
|
165
|
+
*
|
|
166
|
+
* Only `ok: true` results carry `text`; only `ok: false` ones carry `why`.
|
|
167
|
+
*/
|
|
168
|
+
export interface RunArmedResult {
|
|
169
|
+
ok: boolean;
|
|
170
|
+
why?: string;
|
|
171
|
+
/** Set on a hand-over: `why` is a note written for the model, not a reason for the log. */
|
|
172
|
+
note?: boolean;
|
|
173
|
+
text?: string;
|
|
174
|
+
responseModel?: Record<string, unknown>;
|
|
175
|
+
steps?: number;
|
|
176
|
+
partial?: boolean;
|
|
177
|
+
}
|
|
178
|
+
/** The `why` of a {@link RunArmedResult} with nothing armed to run. */
|
|
179
|
+
export declare const RUN_NO_PLAN = "no_plan";
|
|
37
180
|
export interface ReplayOptions {
|
|
38
181
|
/** On unless `BIR_REPLAY=0`. Everything here is inert when false. */
|
|
39
182
|
enabled: boolean;
|
|
@@ -65,6 +208,15 @@ export interface ReplayOptions {
|
|
|
65
208
|
fetchImpl?: typeof fetch;
|
|
66
209
|
/** Anthropic key for derivation. Absent → recorded sample values, free. */
|
|
67
210
|
apiKey?: string;
|
|
211
|
+
/**
|
|
212
|
+
* Let a step whose tool could not be *sent* here — a built-in during
|
|
213
|
+
* divergence recovery, a server with no proxy — stand on its recorded output,
|
|
214
|
+
* labelled `(recorded)`, instead of stopping the plan there. Off by default:
|
|
215
|
+
* a recorded "INSERT 0 1" is last week's write, not this turn's
|
|
216
|
+
* (plan.ts, `DriveOptions`). `BIR_REPLAY_RECORDED_OUTPUTS=1` when omitted.
|
|
217
|
+
* Never applies to a step that was sent and failed or did not answer.
|
|
218
|
+
*/
|
|
219
|
+
recordedFallback?: boolean;
|
|
68
220
|
/**
|
|
69
221
|
* Intent matching in the ReAct loop (fallbk.md §Runner 4): while the model is
|
|
70
222
|
* driving, each tool call's reasoning is matched against scenario intents.
|
|
@@ -113,6 +265,14 @@ interface PinnedCall {
|
|
|
113
265
|
toolName: string;
|
|
114
266
|
/** When it was pinned — a steered step's duration is measured across hooks. */
|
|
115
267
|
pinnedAt: number;
|
|
268
|
+
/**
|
|
269
|
+
* When the host said the call finished (`PostToolUse`), if the control
|
|
270
|
+
* server told us ({@link ReplayController.hostCompleted}). A wrapped step
|
|
271
|
+
* threads only when the proxy's report lands, which can be up to the
|
|
272
|
+
* server's 1.5 s fallback later: until then the *next* call is sequential,
|
|
273
|
+
* not concurrent, and is worth waiting for.
|
|
274
|
+
*/
|
|
275
|
+
hostDoneAt?: number;
|
|
116
276
|
}
|
|
117
277
|
/**
|
|
118
278
|
* Per-run replay state. Created on every match — including declined ones,
|
|
@@ -130,6 +290,13 @@ export interface ReplayState {
|
|
|
130
290
|
plan?: ScenarioReplayPlan;
|
|
131
291
|
/** `tool_use_id` → the step it pinned. */
|
|
132
292
|
pinned: Map<string, PinnedCall>;
|
|
293
|
+
/**
|
|
294
|
+
* Pins still in flight when the plan handed over because another call ran
|
|
295
|
+
* beside them. The plan is retired, but those calls were pinned (and
|
|
296
|
+
* approved) under it: their results are still read when they land, so the
|
|
297
|
+
* step's verdict is recorded and the ledger counts work that happened.
|
|
298
|
+
*/
|
|
299
|
+
lingering?: Map<string, PinnedCall>;
|
|
133
300
|
outcome: ExecutionOutcome;
|
|
134
301
|
deriveCostUsd: number;
|
|
135
302
|
fallbackCostUsd: number;
|
|
@@ -214,6 +381,25 @@ export interface ReplayState {
|
|
|
214
381
|
*/
|
|
215
382
|
retiredMark?: unknown;
|
|
216
383
|
retiredAt?: number;
|
|
384
|
+
/**
|
|
385
|
+
* The one `/scenario/run` executing this plan. A second call — a retry after
|
|
386
|
+
* a client timeout, a double call — waits for its answer rather than driving
|
|
387
|
+
* the same cursor a second time.
|
|
388
|
+
*/
|
|
389
|
+
running?: Promise<RunArmedResult>;
|
|
390
|
+
/** The model called `run_scenario` for this plan (its `PreToolUse` was seen). */
|
|
391
|
+
directRequested?: boolean;
|
|
392
|
+
/**
|
|
393
|
+
* The direct run's caller went away mid-run — Esc on `run_scenario`, or its
|
|
394
|
+
* client timing out. The turn it cut short is no measurement of the task, so
|
|
395
|
+
* the execution report says `measured: false`.
|
|
396
|
+
*/
|
|
397
|
+
callerCancelled?: boolean;
|
|
398
|
+
/**
|
|
399
|
+
* The derivation is the service's to answer (no key here), so waiting on it
|
|
400
|
+
* is waiting on the service, and obeys its breaker (util/service-health.ts).
|
|
401
|
+
*/
|
|
402
|
+
deriveOnService?: boolean;
|
|
217
403
|
}
|
|
218
404
|
/**
|
|
219
405
|
* Why a match did not arm. One code per gate of the ladder, so the caller can
|
|
@@ -249,9 +435,26 @@ export type PreToolAction =
|
|
|
249
435
|
export declare class ReplayController {
|
|
250
436
|
readonly enabled: boolean;
|
|
251
437
|
readonly work: ProxyWorkQueue;
|
|
438
|
+
/** The budgets every path runs on, a direct run's held under {@link DIRECT_RUN_CEILING_MS}. */
|
|
252
439
|
readonly budgets: ReplayBudgets;
|
|
440
|
+
/**
|
|
441
|
+
* `bir replay`'s derivation wait and plan budget: what the environment asked
|
|
442
|
+
* for, unclamped — no MCP client times that run out (see
|
|
443
|
+
* {@link DIRECT_RUN_CEILING_MS}).
|
|
444
|
+
*/
|
|
445
|
+
readonly adHocBudgets: Readonly<Pick<ReplayBudgets, "deriveMs" | "planMs">>;
|
|
253
446
|
readonly intentMatch: IntentMatchOptions;
|
|
254
447
|
private readonly opts;
|
|
448
|
+
/** See {@link ReplayOptions.recordedFallback}. */
|
|
449
|
+
private readonly recordedFallback;
|
|
450
|
+
/** Calls waiting for a plan's pins to change (see `awaitPins`). */
|
|
451
|
+
private readonly pinWaiters;
|
|
452
|
+
/**
|
|
453
|
+
* Set the first time the control server reports a host completion
|
|
454
|
+
* ({@link hostCompleted}). From then on a pin the host has not finished is a
|
|
455
|
+
* call really running beside the next one, and nothing waits for it.
|
|
456
|
+
*/
|
|
457
|
+
private hostSignals;
|
|
255
458
|
constructor(opts: ReplayOptions);
|
|
256
459
|
/**
|
|
257
460
|
* Whether a handed-out segment may actually arm (segmented.md R-OUT-10).
|
|
@@ -279,8 +482,39 @@ export declare class ReplayController {
|
|
|
279
482
|
* up with a denominator nobody measured.
|
|
280
483
|
*/
|
|
281
484
|
arm(match: RunMatchLike, prompt: string, wrapped: ReadonlySet<string>, armedBy?: "prompt" | "intent", ctx?: DeriveContext): Promise<ReplayState>;
|
|
282
|
-
/**
|
|
283
|
-
|
|
485
|
+
/**
|
|
486
|
+
* The directive to inject via `additionalContext`, or undefined when declined.
|
|
487
|
+
*
|
|
488
|
+
* A steer directive previews each step's arguments (plan.ts,
|
|
489
|
+
* `previewArguments`), which can take up to {@link PREVIEW_BUDGET_MS}.
|
|
490
|
+
* `previewMs` — the time the caller's hook has left — lowers that bound,
|
|
491
|
+
* never raises it; under ~10 ms nothing is previewed and the directive keeps
|
|
492
|
+
* its old wording.
|
|
493
|
+
*/
|
|
494
|
+
directiveFor(state: ReplayState, opts?: {
|
|
495
|
+
previewMs?: number;
|
|
496
|
+
}): string | undefined;
|
|
497
|
+
/**
|
|
498
|
+
* Give a steer plan's parameters a short, bounded chance to settle before its
|
|
499
|
+
* directive is written, so {@link directiveFor} can show the arguments the
|
|
500
|
+
* steps run with. Resolves true when they are settled.
|
|
501
|
+
*
|
|
502
|
+
* Without it a derivation the service answers — nearly every one, since a
|
|
503
|
+
* signed-in runner has no key of its own — has never answered by the time
|
|
504
|
+
* `arm` returns (it waits one turn of the loop, on purpose), the directive
|
|
505
|
+
* says "the system supplies the exact arguments", the model writes a guessed
|
|
506
|
+
* command, the pin guard declines it, and the plan retires having run
|
|
507
|
+
* nothing.
|
|
508
|
+
*
|
|
509
|
+
* Only for the steps the pin guard holds to their exact shape — a shell
|
|
510
|
+
* command, a file write (control/pin-guard.ts) — and never longer than
|
|
511
|
+
* {@link PREVIEW_SETTLE_MAX_MS} or `budgetMs`, whichever is less: the caller
|
|
512
|
+
* passes what its prompt hook has left, less what the preview itself needs.
|
|
513
|
+
* Not at all while the service is cooling down. A late answer is not an
|
|
514
|
+
* overrun of anything, so it marks nothing against the service. `arm`
|
|
515
|
+
* itself still never waits (integ-replay R2).
|
|
516
|
+
*/
|
|
517
|
+
settleForPreview(state: ReplayState, budgetMs: number): Promise<boolean>;
|
|
284
518
|
/**
|
|
285
519
|
* A hand-over note not yet delivered, claimed for delivery (fallbk.md D4).
|
|
286
520
|
* Returns it once and marks it delivered; undefined when there is none.
|
|
@@ -298,8 +532,46 @@ export declare class ReplayController {
|
|
|
298
532
|
* Three tiers (§8): pin the expected call; on divergence execute the remainder
|
|
299
533
|
* and hand it back through the best channel available; if even that fails,
|
|
300
534
|
* abort to an ordinary turn.
|
|
535
|
+
*
|
|
536
|
+
* `hook` is optional and only ever tightens the work: `answerBy` lowers the
|
|
537
|
+
* deadline to the caller's own, and `signal` stops a divergence from
|
|
538
|
+
* starting another step once the host has been answered without it.
|
|
539
|
+
*/
|
|
540
|
+
preTool(state: ReplayState, toolName: string, toolUseId: string,
|
|
541
|
+
/** The call's `tool_input`: whether a `Bash` call is only `bir` is read from it (D13). */
|
|
542
|
+
toolInput?: unknown, hook?: PreToolOptions): Promise<PreToolAction>;
|
|
543
|
+
/**
|
|
544
|
+
* A call arrived while another pinned call's output has not threaded yet
|
|
545
|
+
* (see `preTool`). The plan stops here rather than pin a step twice.
|
|
301
546
|
*/
|
|
302
|
-
|
|
547
|
+
private concurrentCall;
|
|
548
|
+
/**
|
|
549
|
+
* Wait until the plan's outstanding pins thread, or until waiting is no
|
|
550
|
+
* longer worth it (see `preTool`, ONE CALL AT A TIME).
|
|
551
|
+
*
|
|
552
|
+
* Always bounded by the hook's `deadline`. Within it:
|
|
553
|
+
* - every pin finished by the host ({@link hostCompleted}): until
|
|
554
|
+
* {@link PIN_THREAD_WAIT_MS} after the last of them finished — the
|
|
555
|
+
* server's fallback has threaded it by then, if anything will;
|
|
556
|
+
* - the server is known to report host completions and a pin is not
|
|
557
|
+
* finished: a call really running beside this one, which may run for
|
|
558
|
+
* minutes, so only {@link HOST_RACE_MS} in case its completion is racing
|
|
559
|
+
* this hook;
|
|
560
|
+
* - otherwise (a server that reports nothing): {@link PIN_THREAD_WAIT_MS}.
|
|
561
|
+
*/
|
|
562
|
+
private awaitPins;
|
|
563
|
+
/** Resolves on the next change to a plan's pins, or after `ms`. */
|
|
564
|
+
private pinChange;
|
|
565
|
+
/** Wake every call waiting on this plan's pins. */
|
|
566
|
+
private notifyPins;
|
|
567
|
+
/**
|
|
568
|
+
* The host finished a call (`PostToolUse` / `PostToolUseFailure`) — whether
|
|
569
|
+
* or not its output has threaded yet. The control server calls this for
|
|
570
|
+
* every pinned call; it is what tells the model's next, sequential call
|
|
571
|
+
* apart from one running beside a pin (see `awaitPins`). Optional: without
|
|
572
|
+
* it every such call waits the bounded {@link PIN_THREAD_WAIT_MS}.
|
|
573
|
+
*/
|
|
574
|
+
hostCompleted(state: ReplayState | undefined, toolUseId: string): void;
|
|
303
575
|
/**
|
|
304
576
|
* `PostToolUse` for a call this plan pinned.
|
|
305
577
|
*
|
|
@@ -307,26 +579,60 @@ export declare class ReplayController {
|
|
|
307
579
|
* wrapped MCP step that is the proxy's whole `CallToolResult`, which is why the
|
|
308
580
|
* server threads those from `/proxy/step` rather than from the hook (§7.2).
|
|
309
581
|
* Returns true when the plan is now complete.
|
|
310
|
-
|
|
311
|
-
|
|
582
|
+
*
|
|
583
|
+
* `failed` is the host's verdict that the call failed — `PostToolUseFailure`,
|
|
584
|
+
* or a proxy report with `isError` — with its error text when there is one.
|
|
585
|
+
* It outranks anything read from `output`: a built-in's response is Claude
|
|
586
|
+
* Code's rendering and is never judged here, so for an `Edit` whose
|
|
587
|
+
* `old_string` moved since the recording this is the only word there is. A
|
|
588
|
+
* step that failed must never thread as if it worked: the next step would be
|
|
589
|
+
* pinned, and auto-approved, on top of it.
|
|
590
|
+
*
|
|
591
|
+
* Not every failure is one. A call its proxy *abandoned* — the upstream
|
|
592
|
+
* closed, restarted or was cancelled mid-call, reported with `isError` and
|
|
593
|
+
* no result — may well have done its work, and so may one the host says
|
|
594
|
+
* timed out or was interrupted: the note says "may or may not have
|
|
595
|
+
* completed", never "failed", or the model runs a write a second time
|
|
596
|
+
* ({@link pinFailure}). `how` lets the caller say so outright.
|
|
597
|
+
*/
|
|
598
|
+
postTool(state: ReplayState, toolUseId: string, output: string, failed?: string, how?: PostToolOptions): boolean;
|
|
599
|
+
/**
|
|
600
|
+
* The result of a call pinned under a plan that has since handed over
|
|
601
|
+
* because another call ran beside it (`concurrentCall`). Nothing threads —
|
|
602
|
+
* the plan is retired and the model is doing the rest — but the step's
|
|
603
|
+
* verdict is recorded, and a step that did its work is counted: the outcome
|
|
604
|
+
* moves from `failed` to `fell_back` when it is the first that did.
|
|
605
|
+
*/
|
|
606
|
+
private lateThread;
|
|
312
607
|
/** Whether a pinned call's output is threaded from the proxy's report (§7.2). */
|
|
313
608
|
threadsFromProxy(state: ReplayState, toolUseId: string): boolean;
|
|
314
|
-
/**
|
|
609
|
+
/**
|
|
610
|
+
* True when this call belongs to the plan: pinned by it and not yet
|
|
611
|
+
* threaded — including a pin still in flight when the plan handed over, so
|
|
612
|
+
* its result still reaches `postTool` (see `lateThread`).
|
|
613
|
+
*/
|
|
315
614
|
isPinned(state: ReplayState, toolUseId: string): boolean;
|
|
316
615
|
/**
|
|
317
616
|
* `POST /scenario/run` — the whole of a `direct` plan, executed here.
|
|
318
617
|
*
|
|
319
618
|
* Every step runs through the proxy that already owns its upstream, so the
|
|
320
619
|
* model spends nothing beyond the turn that reads the results.
|
|
620
|
+
*
|
|
621
|
+
* `signal` aborts when the caller is gone — `bir-scenario`'s request was
|
|
622
|
+
* cancelled (Esc) or timed out — so no further step is started (C4). The
|
|
623
|
+
* worst case this can take is `deriveMs + planMs` plus one step's
|
|
624
|
+
* `MIN_STEP_MS`, six logic bodies at their full limit and a 5 s margin —
|
|
625
|
+
* 120 s by default, more only for a plan budget an operator raised
|
|
626
|
+
* ({@link clampDirectRun}) — which `bir-scenario`'s own timeout (290 s) must
|
|
627
|
+
* exceed, and which is held under {@link DIRECT_RUN_CEILING_MS} whatever the
|
|
628
|
+
* environment asks for. That bound counts every body at its limit; a run
|
|
629
|
+
* that does not stall ends well inside its plan budget.
|
|
630
|
+
*
|
|
631
|
+
* One run per plan: a second call while the first is running gets the first
|
|
632
|
+
* one's answer, never a second execution of the same steps.
|
|
321
633
|
*/
|
|
322
|
-
runArmed(state: ReplayState): Promise<
|
|
323
|
-
|
|
324
|
-
why?: string;
|
|
325
|
-
text?: string;
|
|
326
|
-
responseModel?: Record<string, unknown>;
|
|
327
|
-
steps?: number;
|
|
328
|
-
partial?: boolean;
|
|
329
|
-
}>;
|
|
634
|
+
runArmed(state: ReplayState, signal?: AbortSignal): Promise<RunArmedResult>;
|
|
635
|
+
private runArmedOnce;
|
|
330
636
|
/**
|
|
331
637
|
* Build the execution report for a sealed run, or undefined when there is
|
|
332
638
|
* nothing to report.
|
|
@@ -381,6 +687,13 @@ export declare class ReplayController {
|
|
|
381
687
|
outcome: string;
|
|
382
688
|
ms: number;
|
|
383
689
|
}>;
|
|
690
|
+
/** Where the run stopped short of its end, and why, when it did. */
|
|
691
|
+
stopped?: {
|
|
692
|
+
step: number;
|
|
693
|
+
tool: string;
|
|
694
|
+
reason: string;
|
|
695
|
+
error?: string;
|
|
696
|
+
};
|
|
384
697
|
}>;
|
|
385
698
|
/** Release every parked poller. Called at control-server shutdown. */
|
|
386
699
|
close(): void;
|
|
@@ -406,8 +719,18 @@ export declare class ReplayController {
|
|
|
406
719
|
*
|
|
407
720
|
* The plan is retired first: whatever happens next, this turn neither steers
|
|
408
721
|
* nor re-injects again.
|
|
722
|
+
*
|
|
723
|
+
* This runs inside the diverging call's `PreToolUse`, so `deadline` bounds
|
|
724
|
+
* it: no step is started after it, and none is waited on past it. The bundle
|
|
725
|
+
* then says exactly what ran, and hands the rest over.
|
|
409
726
|
*/
|
|
410
727
|
private diverge;
|
|
728
|
+
/**
|
|
729
|
+
* The note for a divergence bundle that stopped at a step, recorded as this
|
|
730
|
+
* plan's hand-over (already delivered — it goes out with the bundle), so the
|
|
731
|
+
* model's own work from here is recorded as a fragment.
|
|
732
|
+
*/
|
|
733
|
+
private stopNote;
|
|
411
734
|
/**
|
|
412
735
|
* Hand the rest of the task to the model (fallbk.md D4/D5).
|
|
413
736
|
*
|
|
@@ -429,11 +752,40 @@ export declare class ReplayController {
|
|
|
429
752
|
* The executor handed to a plan: dispatch a step to the proxy that owns its
|
|
430
753
|
* upstream.
|
|
431
754
|
*
|
|
432
|
-
*
|
|
433
|
-
*
|
|
434
|
-
*
|
|
755
|
+
* A tool that ran and failed resolves with its failure as the response,
|
|
756
|
+
* exactly as it would in a session. A rejection is a {@link StepRunError}
|
|
757
|
+
* saying what happened to the call — not sent, sent and unanswered, or
|
|
758
|
+
* refused — because the model is told differently about each (plan.ts).
|
|
759
|
+
*
|
|
760
|
+
* `deadline` is the whole run's: no step is waited on past it (but each gets
|
|
761
|
+
* at least {@link MIN_STEP_MS}). `adHoc` is `bir replay`, which the operator
|
|
762
|
+
* asked for by id and which the allowlist does not gate.
|
|
435
763
|
*/
|
|
436
764
|
private executeStep;
|
|
765
|
+
/** Where recorded outputs come from, when they may stand in at all. */
|
|
766
|
+
private recordedSource;
|
|
767
|
+
/**
|
|
768
|
+
* Wait for a plan's parameters within `budgetMs`, then judge whether the plan
|
|
769
|
+
* may run on them. Never throws.
|
|
770
|
+
*
|
|
771
|
+
* Out of time, the parameters settle without the derivation
|
|
772
|
+
* (`useFallbackParams`: settings at their samples, targets null) — never on
|
|
773
|
+
* `{}` — and the plan runs only if it needs no target it was not given. A
|
|
774
|
+
* `paramsLogic` that throws, or a value too dangerous to template into a
|
|
775
|
+
* command (safety.ts), stops it. `adHoc` keeps `bir replay`'s contract: the
|
|
776
|
+
* operator named the scenario, so neither judgement applies.
|
|
777
|
+
*/
|
|
778
|
+
private readyFor;
|
|
779
|
+
/**
|
|
780
|
+
* Wait for something the derivation decides, within `budgetMs`. Resolves
|
|
781
|
+
* undefined when the time ran out.
|
|
782
|
+
*
|
|
783
|
+
* When the service is the one deriving, this is a service wait on a hot path
|
|
784
|
+
* (C1): it does not wait at all while the service is cooling down, and an
|
|
785
|
+
* overrun is reported to the breaker so the next hook does not wait either.
|
|
786
|
+
* Even a zero wait lets an answer that has already arrived through.
|
|
787
|
+
*/
|
|
788
|
+
private waitDerivation;
|
|
437
789
|
/**
|
|
438
790
|
* Where a pinned step's output will arrive from — the proxy's report, or the
|
|
439
791
|
* hook's `tool_response` (§7.2).
|
|
@@ -463,7 +815,6 @@ export declare class ReplayController {
|
|
|
463
815
|
/** Outcomes only move forward; `not_steered` is the floor (§11.1). */
|
|
464
816
|
private upgrade;
|
|
465
817
|
private retire;
|
|
466
|
-
private withBudget;
|
|
467
818
|
}
|
|
468
819
|
/** The subset of `RunMatch` replay consumes. Structural, so tests need no recorder. */
|
|
469
820
|
export interface RunMatchLike {
|