@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.
- package/dist/bin/bir-hooks.d.ts +12 -16
- package/dist/bin/bir-hooks.js +12 -16
- package/dist/bin/bir-scenario.js +1 -1
- package/dist/bin/bir.js +1 -21
- package/dist/bin/investigate.d.ts +0 -9
- package/dist/bin/investigate.js +14 -42
- package/dist/bin/scenario-edit.js +13 -44
- package/dist/control/pin-guard.d.ts +16 -0
- package/dist/control/pin-guard.js +36 -0
- package/dist/control/recorder-main.d.ts +0 -15
- package/dist/control/recorder-main.js +7 -39
- package/dist/control/server.d.ts +81 -131
- package/dist/control/server.js +460 -968
- package/dist/record/recorder.d.ts +47 -138
- package/dist/record/recorder.js +0 -3
- package/dist/record/remote-recorder.d.ts +1 -9
- package/dist/record/remote-recorder.js +11 -49
- package/dist/record/switchable-recorder.js +0 -2
- package/dist/replay/bundle.d.ts +2 -2
- package/dist/replay/bundle.js +3 -3
- package/dist/replay/controller.d.ts +75 -124
- package/dist/replay/controller.js +207 -529
- package/dist/replay/derive.d.ts +0 -10
- package/dist/replay/derive.js +0 -3
- package/dist/replay/handover.d.ts +2 -37
- package/dist/replay/handover.js +38 -106
- package/dist/replay/logic.d.ts +0 -11
- package/dist/replay/logic.js +0 -24
- package/dist/replay/plan.d.ts +43 -163
- package/dist/replay/plan.js +144 -556
- package/dist/replay/safety.d.ts +4 -5
- package/dist/replay/safety.js +7 -7
- package/dist/replay/source-run.d.ts +15 -24
- package/dist/replay/source-run.js +25 -58
- package/dist/replay/targets.d.ts +11 -21
- package/dist/replay/targets.js +30 -69
- package/dist/replay/types.d.ts +9 -96
- package/dist/replay/types.js +3 -16
- package/dist/util/service-headers.d.ts +1 -2
- package/dist/util/service-headers.js +1 -2
- package/docs/calculatedReplay.md +17 -74
- package/docs/calculatedReplayGuide.md +10 -30
- package/package.json +1 -1
- package/production.md +5 -35
- package/dist/replay/condition-words.d.ts +0 -28
- package/dist/replay/condition-words.js +0 -59
- package/dist/replay/flatten.d.ts +0 -161
- package/dist/replay/flatten.js +0 -222
- package/dist/replay/reads-only.d.ts +0 -29
- package/dist/replay/reads-only.js +0 -261
|
@@ -7,9 +7,15 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type { ExecutionStage, ExecutionStepResult, FallbackKind } from "../replay/types.js";
|
|
9
9
|
export interface StartRunOptions {
|
|
10
|
+
/**
|
|
11
|
+
* Marks a *fragment* (fallbk.md D7). `counted`: the runner asked for this
|
|
12
|
+
* hand-over's continuation first, and the service counted the hand-over as
|
|
13
|
+
* a hit then, so this request must not count it again (Part 3).
|
|
14
|
+
*/
|
|
10
15
|
fallbackOf?: {
|
|
11
16
|
scenarioId: string;
|
|
12
17
|
stepIndex: number;
|
|
18
|
+
counted?: boolean;
|
|
13
19
|
};
|
|
14
20
|
/**
|
|
15
21
|
* Marks a *repeat* (the service's newestRun.md): a matched turn whose plan
|
|
@@ -130,20 +136,18 @@ export interface Recorder {
|
|
|
130
136
|
toolInput: string;
|
|
131
137
|
context?: string;
|
|
132
138
|
/**
|
|
133
|
-
* The thinking text this call was chosen in
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
* carried apart from `context` rather than folded into it.
|
|
139
|
+
* The thinking text this call was chosen in. The service keeps it for
|
|
140
|
+
* the recording page, for people to read and never for a model, which
|
|
141
|
+
* is why it is carried apart from `context` rather than folded into it.
|
|
137
142
|
*/
|
|
138
143
|
intent?: {
|
|
139
144
|
text: string;
|
|
140
145
|
source: "thinking";
|
|
141
146
|
};
|
|
142
147
|
/**
|
|
143
|
-
* Set when a plan executed this step, to the plan's scenario id
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
* system's own replays never count as recurrences of the work.
|
|
148
|
+
* Set when a plan executed this step, to the plan's scenario id, or made
|
|
149
|
+
* it before the agent redid the task (`byPlan`, AC2): how the service
|
|
150
|
+
* tells the system's own steps from the agent's work.
|
|
147
151
|
*/
|
|
148
152
|
metadata?: StepMetadata;
|
|
149
153
|
}): string;
|
|
@@ -197,28 +201,6 @@ export declare function isRecorderHealthReporter(r: Recorder): r is Recorder & R
|
|
|
197
201
|
* callers must stop recording it. What the caller does *instead* of recording is
|
|
198
202
|
* v2's subject (docs/calculatedReplay.md); stopping is mandatory either way.
|
|
199
203
|
*/
|
|
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";
|
|
207
|
-
/** The cheap model's answer about one live step and one segment (R-VERIFY-1). */
|
|
208
|
-
export interface MatchVerdict {
|
|
209
|
-
model: string;
|
|
210
|
-
verdict: "same" | "different" | "skipped";
|
|
211
|
-
reason?: string;
|
|
212
|
-
}
|
|
213
|
-
/** Where a handed-out segment sits in its recording (R-OUT-8). */
|
|
214
|
-
export interface MatchSegment {
|
|
215
|
-
runId: string;
|
|
216
|
-
stepFrom: number;
|
|
217
|
-
stepTo: number;
|
|
218
|
-
/** `tool_selected` rows of the recording before `stepFrom` (R-OUT-6). */
|
|
219
|
-
firstPosition: number;
|
|
220
|
-
hitCount?: number;
|
|
221
|
-
}
|
|
222
204
|
export interface RunMatch {
|
|
223
205
|
runId: string;
|
|
224
206
|
scenarioId: string | null;
|
|
@@ -233,14 +215,6 @@ export interface RunMatch {
|
|
|
233
215
|
fallback?: {
|
|
234
216
|
maxStepFailures: number;
|
|
235
217
|
};
|
|
236
|
-
/**
|
|
237
|
-
* What kind of row matched (segmented.md R-OUT-7). Absent from an older
|
|
238
|
-
* service, and then read as `scenario`.
|
|
239
|
-
*/
|
|
240
|
-
kind?: "scenario" | "segment";
|
|
241
|
-
segment?: MatchSegment;
|
|
242
|
-
key?: MatchKey;
|
|
243
|
-
verified?: MatchVerdict;
|
|
244
218
|
/** The frozen name, beside the runtime `intent` the plan runs on (R-INTENT-12). */
|
|
245
219
|
intentName?: string;
|
|
246
220
|
/**
|
|
@@ -286,7 +260,7 @@ export declare function isMatchAware(r: Recorder): r is Recorder & MatchAware;
|
|
|
286
260
|
export interface RunCreation {
|
|
287
261
|
created: boolean;
|
|
288
262
|
/**
|
|
289
|
-
* Set when this was a hit on an existing fragment (R-FALL-7
|
|
263
|
+
* Set when this was a hit on an existing fragment (R-FALL-7) —
|
|
290
264
|
* with `created` too when the service recorded it all the same, as that
|
|
291
265
|
* fragment's newest run (its newestRun.md N2).
|
|
292
266
|
*/
|
|
@@ -320,84 +294,47 @@ export declare function isServiceProber(r: Recorder): r is Recorder & ServicePro
|
|
|
320
294
|
/** What a ReAct iteration sends to find a scenario by its intent (fallbk.md §Runner 4). */
|
|
321
295
|
export interface IntentMatchRequest {
|
|
322
296
|
/**
|
|
323
|
-
*
|
|
324
|
-
*
|
|
325
|
-
* and
|
|
297
|
+
* What the agent wrote, or was thinking, as it chose the call. Never empty
|
|
298
|
+
* on an intent probe. A continuation request ({@link handover}) is answered
|
|
299
|
+
* by the hand-over alone: it sends the text, possibly empty, and the service
|
|
300
|
+
* does not read it.
|
|
326
301
|
*/
|
|
327
302
|
text: string;
|
|
328
303
|
currentToolName?: string;
|
|
329
304
|
/** Scenario ids already armed this turn. */
|
|
330
305
|
exclude?: string[];
|
|
331
|
-
/** Which rung the text came from (R-INTENT-3). */
|
|
332
|
-
source?: IntentSourceName;
|
|
333
|
-
/** The same value as `currentToolName`; what the new server reads (R-HIT-6). */
|
|
334
|
-
toolName?: string;
|
|
335
|
-
/** Redacted and capped arguments of the call about to be made (R-HIT-4). */
|
|
336
|
-
toolInput?: string;
|
|
337
|
-
/** The turn's identity, fixed for its whole life (R-HIT-6). */
|
|
338
|
-
turnId?: string;
|
|
339
|
-
/** `run.ordering.next` — where this call sits in the turn. */
|
|
340
|
-
stepPosition?: number;
|
|
341
|
-
/** Recordings this turn must not count hits against (R-HIT-7). */
|
|
342
|
-
excludeRuns?: string[];
|
|
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
|
-
*/
|
|
349
|
-
ranThrough?: Array<{
|
|
350
|
-
runId: string;
|
|
351
|
-
position: number;
|
|
352
|
-
scenarioId?: string;
|
|
353
|
-
}>;
|
|
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
|
-
*/
|
|
358
|
-
acceptSegments?: boolean;
|
|
359
|
-
/** Always true from this runner (R-CALL-28). */
|
|
360
|
-
supportsCalls?: boolean;
|
|
361
306
|
/**
|
|
362
307
|
* The host the turn runs in, as its runs' `framework` says it (see
|
|
363
308
|
* {@link StartRunOptions.framework}). Left out for Claude Code, which is
|
|
364
309
|
* what a missing value means to the service.
|
|
365
310
|
*/
|
|
366
311
|
framework?: string;
|
|
312
|
+
/**
|
|
313
|
+
* A continuation request (the service's fallbk.md Part 3): the plan that
|
|
314
|
+
* just handed the task to the model, and the step it stopped in front of.
|
|
315
|
+
* The service answers with the plan of the work recorded after an earlier
|
|
316
|
+
* hand-over at that step, if there is one, and matches no words.
|
|
317
|
+
*/
|
|
318
|
+
handover?: {
|
|
319
|
+
scenarioId: string;
|
|
320
|
+
stepIndex: number;
|
|
321
|
+
};
|
|
367
322
|
}
|
|
368
|
-
/**
|
|
369
|
-
export type IntentSourceName = "text" | "thinking" | "tool";
|
|
370
|
-
/** One recorded step a live step matched (R-HIT-6). */
|
|
371
|
-
export interface StepHit {
|
|
372
|
-
runId: string;
|
|
373
|
-
stepIndex: number;
|
|
374
|
-
similarity: number;
|
|
375
|
-
hitCount?: number;
|
|
376
|
-
}
|
|
377
|
-
/** What a segment *would* have armed, when no ticket was issued (R-OUT-9). */
|
|
378
|
-
export interface SegmentWouldArm {
|
|
379
|
-
scenarioId: string;
|
|
380
|
-
runId: string;
|
|
381
|
-
key?: MatchKey;
|
|
382
|
-
similarity: number;
|
|
383
|
-
band?: string;
|
|
384
|
-
verified?: MatchVerdict;
|
|
385
|
-
tool?: string;
|
|
386
|
-
firstTool?: string;
|
|
387
|
-
stepFrom?: number;
|
|
388
|
-
stepTo?: number;
|
|
389
|
-
hitCount?: number;
|
|
390
|
-
}
|
|
391
|
-
/**
|
|
392
|
-
* The whole answer to a probe, not just its match. The counting fields are what
|
|
393
|
-
* make the observe-only period readable (R-OUT-10), so they must survive the
|
|
394
|
-
* trip rather than being discarded with the rest of the body.
|
|
395
|
-
*/
|
|
323
|
+
/** The answer to a probe. */
|
|
396
324
|
export interface IntentMatchAnswer {
|
|
397
325
|
matched: RunMatch | null;
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
326
|
+
/** The best score the service saw, matched or not — for the log, to tune by. */
|
|
327
|
+
bestSimilarity?: number | null;
|
|
328
|
+
/**
|
|
329
|
+
* The answer to a continuation request: the recording of the earlier
|
|
330
|
+
* hand-over at that step, and whether this hand-over was counted as a hit
|
|
331
|
+
* on it. Null when there is none; absent from a service that does not know
|
|
332
|
+
* continuations, which matched the text by words instead.
|
|
333
|
+
*/
|
|
334
|
+
continuation?: {
|
|
335
|
+
runId: string;
|
|
336
|
+
counted?: boolean;
|
|
337
|
+
} | null;
|
|
401
338
|
}
|
|
402
339
|
/** How long a probe's caller will wait; the request is abandoned past it. */
|
|
403
340
|
export interface IntentMatchOptions {
|
|
@@ -411,36 +348,6 @@ export interface IntentMatcher {
|
|
|
411
348
|
matchIntent(req: IntentMatchRequest, opts?: IntentMatchOptions): Promise<IntentMatchAnswer | null>;
|
|
412
349
|
}
|
|
413
350
|
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;
|
|
444
351
|
/**
|
|
445
352
|
* What a matched turn actually cost, reported once per match
|
|
446
353
|
* (docs/calculatedReplay.md §11).
|
|
@@ -499,12 +406,14 @@ export interface ExecutionReport {
|
|
|
499
406
|
*/
|
|
500
407
|
baselineEligible?: boolean;
|
|
501
408
|
/**
|
|
502
|
-
*
|
|
503
|
-
*
|
|
504
|
-
*
|
|
505
|
-
*
|
|
409
|
+
* Set on a continuation's report (the service's fallbk.md Part 3): the plan
|
|
410
|
+
* it continued in this turn, which handed over in front of it. That plan's
|
|
411
|
+
* report bills the whole turn against its own baseline, which covers this
|
|
412
|
+
* part of the task too, so the service books this one no saving.
|
|
506
413
|
*/
|
|
507
|
-
|
|
414
|
+
continues?: {
|
|
415
|
+
scenarioId: string;
|
|
416
|
+
};
|
|
508
417
|
/**
|
|
509
418
|
* Which gate of the ladder declined, on a `not_steered` report.
|
|
510
419
|
*
|
package/dist/record/recorder.js
CHANGED
|
@@ -20,9 +20,6 @@ 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
|
-
}
|
|
26
23
|
export function isToolListReporter(r) {
|
|
27
24
|
return typeof r.reportTools === "function";
|
|
28
25
|
}
|
|
@@ -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,
|
|
53
|
+
import type { AnswerCall, AttentionPlan, DisputeAnswer, ExecutionReport, FlushOptions, IntentMatchAnswer, IntentMatchOptions, IntentMatchRequest, 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,14 +206,6 @@ 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>;
|
|
217
209
|
/**
|
|
218
210
|
* The similar-prompt match for a run's create call (null = fresh run). Like
|
|
219
211
|
* {@link runCreated}, answers null at once while a failure is on record —
|
|
@@ -604,9 +604,6 @@ export class RemoteRecorder {
|
|
|
604
604
|
// A repeat (the service's newestRun.md). Sent only after a match
|
|
605
605
|
// that offered it, so a service that would not know it never sees it.
|
|
606
606
|
...(options?.repeatOf ? { repeatOf: options.repeatOf } : {}),
|
|
607
|
-
// A chain with calls is inlined for this runner rather than stopped in
|
|
608
|
-
// front of its first call (segmented.md R-CALL-28).
|
|
609
|
-
supportsCalls: true,
|
|
610
607
|
}),
|
|
611
608
|
// The id is client-generated, so the service can tell a repeat. A
|
|
612
609
|
// hook waiting on this answer is let go before the retry: a match
|
|
@@ -616,9 +613,7 @@ export class RemoteRecorder {
|
|
|
616
613
|
const b = body && typeof body === "object"
|
|
617
614
|
? body
|
|
618
615
|
: {};
|
|
619
|
-
// `id: null` is the service saying it kept a run of its own.
|
|
620
|
-
// that was matters: a turn must not count step hits against the
|
|
621
|
-
// recording it is itself a repeat of (R-HIT-7).
|
|
616
|
+
// `id: null` is the service saying it kept a run of its own.
|
|
622
617
|
if (entry.released && b.id === null) {
|
|
623
618
|
logLine("recorder.matched_after_retry", {
|
|
624
619
|
run: id,
|
|
@@ -679,23 +674,18 @@ export class RemoteRecorder {
|
|
|
679
674
|
const body = {
|
|
680
675
|
...req,
|
|
681
676
|
text: capText(req.text ?? "", TEXT_CAPS.intent, redactText),
|
|
682
|
-
...(typeof req.toolInput === "string"
|
|
683
|
-
? { toolInput: capJsonText(req.toolInput, DEFAULT_MAX_PAYLOAD_BYTES, redactJsonText) }
|
|
684
|
-
: {}),
|
|
685
677
|
};
|
|
686
678
|
const answer = await this.request("POST", "/scenarios/match-intent", JSON.stringify(body), {
|
|
687
679
|
signal: opts.signal,
|
|
688
680
|
timeoutMs: opts.timeoutMs ?? PROBE_TIMEOUT_MS,
|
|
689
681
|
});
|
|
690
|
-
// The whole answer, not just `matched`: the counting fields are what make
|
|
691
|
-
// the observe-only period readable (segmented.md R-OUT-10), and an older
|
|
692
|
-
// server simply sends none of them (R-COMPAT-2).
|
|
693
682
|
const b = answer && typeof answer === "object" ? answer : {};
|
|
694
683
|
return {
|
|
695
684
|
matched: b.matched ?? null,
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
685
|
+
...(typeof b.bestSimilarity === "number" ? { bestSimilarity: b.bestSimilarity } : {}),
|
|
686
|
+
// Null and absent mean different things: no continuation, or a
|
|
687
|
+
// service that does not know continuations (fallbk.md Part 3).
|
|
688
|
+
...(b.continuation !== undefined ? { continuation: b.continuation } : {}),
|
|
699
689
|
};
|
|
700
690
|
}
|
|
701
691
|
catch (err) {
|
|
@@ -713,36 +703,6 @@ export class RemoteRecorder {
|
|
|
713
703
|
return null;
|
|
714
704
|
}
|
|
715
705
|
}
|
|
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
|
-
}
|
|
746
706
|
/**
|
|
747
707
|
* The similar-prompt match for a run's create call (null = fresh run). Like
|
|
748
708
|
* {@link runCreated}, answers null at once while a failure is on record —
|
|
@@ -805,8 +765,7 @@ export class RemoteRecorder {
|
|
|
805
765
|
const intentSource = data.intent?.source;
|
|
806
766
|
this.enqueueRun(runId, "step", sizeOf(toolInput, context, intentText), () =>
|
|
807
767
|
// `intent` and `metadata` are omitted, not nulled, when absent, so an
|
|
808
|
-
// older service sees the body it has always seen
|
|
809
|
-
// R-COMPAT-1).
|
|
768
|
+
// older service sees the body it has always seen.
|
|
810
769
|
this.post(`/recordings/runs/${runId}/steps`, {
|
|
811
770
|
id,
|
|
812
771
|
stepIndex,
|
|
@@ -1084,7 +1043,9 @@ export class RemoteRecorder {
|
|
|
1084
1043
|
fallbackStepIndex: report.fallbackStepIndex,
|
|
1085
1044
|
fallbackKind: report.fallbackKind,
|
|
1086
1045
|
baselineEligible: report.baselineEligible,
|
|
1087
|
-
|
|
1046
|
+
// A continuation's report: the plan it continued, whose report
|
|
1047
|
+
// books the turn (the service's fallbk.md Part 3). Additive.
|
|
1048
|
+
continues: report.continues,
|
|
1088
1049
|
// Why nothing ran, when nothing ran. Additive: an older service drops
|
|
1089
1050
|
// the field and books the cost exactly as it always did.
|
|
1090
1051
|
declined: report.declined,
|
|
@@ -1114,8 +1075,9 @@ export class RemoteRecorder {
|
|
|
1114
1075
|
steps: report.steps?.length,
|
|
1115
1076
|
stepsFailed: failed || undefined,
|
|
1116
1077
|
baselineEligible: report.baselineEligible === false ? false : undefined,
|
|
1117
|
-
sharedWith: report.sharedWith?.length,
|
|
1118
1078
|
declined: report.declined,
|
|
1079
|
+
// A continuation's report: the plan whose report books the turn.
|
|
1080
|
+
continues: report.continues?.scenarioId,
|
|
1119
1081
|
// `bir investigate` prints it beside the outcome: "redone by run_…".
|
|
1120
1082
|
redoneBy: report.redoneBy?.runId,
|
|
1121
1083
|
ownCalls: report.redoneBy?.ownCalls,
|
|
@@ -49,8 +49,6 @@ 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),
|
|
54
52
|
reportExecution: () => undefined,
|
|
55
53
|
backfillContext: () => undefined,
|
|
56
54
|
probeIfFailing: () => undefined,
|
package/dist/replay/bundle.d.ts
CHANGED
|
@@ -51,8 +51,8 @@ export interface AssembleOptions {
|
|
|
51
51
|
/** The plan stopped part-way and the model is to carry on (fallbk.md). */
|
|
52
52
|
handover?: boolean;
|
|
53
53
|
/**
|
|
54
|
-
* The plan was armed mid-task by intent
|
|
55
|
-
*
|
|
54
|
+
* The plan was armed mid-task by intent. Its results feed the agent's own
|
|
55
|
+
* task, which continues. `handover` wins over this.
|
|
56
56
|
*/
|
|
57
57
|
subTask?: boolean;
|
|
58
58
|
/**
|
package/dist/replay/bundle.js
CHANGED
|
@@ -100,8 +100,8 @@ const HANDOVER_HEADER = "[BaseInstRunner calculated replay] The tool calls below
|
|
|
100
100
|
"continue the task yourself from where they end.";
|
|
101
101
|
const HANDOVER_FOOTER = "\n\nThe calls above are done. Continue the rest of the task yourself, calling tools as needed.";
|
|
102
102
|
/**
|
|
103
|
-
* The plan ran inside the agent's own task (
|
|
104
|
-
* but the task did not, so the model must not be told to answer without tools.
|
|
103
|
+
* The plan ran inside the agent's own task (armed mid-task by intent): it
|
|
104
|
+
* finished, but the task did not, so the model must not be told to answer without tools.
|
|
105
105
|
* This is {@link HANDOVER_HEADER} without "The scenario stopped before the task
|
|
106
106
|
* was finished" — nothing stopped, the sub-task is simply done.
|
|
107
107
|
*/
|
|
@@ -121,7 +121,7 @@ export function assembleBundle(entries, maxChars, opts = {}) {
|
|
|
121
121
|
const anyRecorded = entries.some((e) => e.recorded);
|
|
122
122
|
// A hand-over outranks a sub-task: it says "continue" too, and it also has to
|
|
123
123
|
// say that the plan stopped short. A sub-task outranks the live and mixed
|
|
124
|
-
// headers, whose wording forbids further tool calls
|
|
124
|
+
// headers, whose wording forbids further tool calls.
|
|
125
125
|
const header = opts.handover
|
|
126
126
|
? HANDOVER_HEADER
|
|
127
127
|
: opts.subTask
|