@basein/runner 0.3.17 → 0.3.19

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 (51) hide show
  1. package/dist/bin/bir-hooks.d.ts +11 -8
  2. package/dist/bin/bir-hooks.js +11 -8
  3. package/dist/bin/bir-scenario.d.ts +5 -1
  4. package/dist/bin/bir-scenario.js +155 -5
  5. package/dist/bin/bir.js +22 -9
  6. package/dist/bin/investigate.d.ts +9 -0
  7. package/dist/bin/investigate.js +26 -12
  8. package/dist/bin/scenario-edit.d.ts +7 -0
  9. package/dist/bin/scenario-edit.js +19 -2
  10. package/dist/bin/scenario-liveness.d.ts +12 -1
  11. package/dist/bin/scenario-liveness.js +8 -4
  12. package/dist/bin/setup.js +3 -3
  13. package/dist/control/client.d.ts +5 -0
  14. package/dist/control/client.js +7 -0
  15. package/dist/control/ensure-hook.d.ts +14 -3
  16. package/dist/control/ensure-hook.js +18 -5
  17. package/dist/control/recorder-main.d.ts +15 -0
  18. package/dist/control/recorder-main.js +33 -8
  19. package/dist/control/server.d.ts +192 -13
  20. package/dist/control/server.js +819 -41
  21. package/dist/control/transcript.d.ts +19 -0
  22. package/dist/control/transcript.js +57 -0
  23. package/dist/proxy/intercept.js +13 -0
  24. package/dist/proxy/session.d.ts +23 -0
  25. package/dist/proxy/session.js +71 -3
  26. package/dist/record/recorder.d.ts +156 -2
  27. package/dist/record/recorder.js +9 -0
  28. package/dist/record/remote-recorder.d.ts +59 -1
  29. package/dist/record/remote-recorder.js +297 -20
  30. package/dist/record/switchable-recorder.js +6 -0
  31. package/dist/replay/bundle.d.ts +8 -0
  32. package/dist/replay/bundle.js +20 -6
  33. package/dist/replay/condition-words.d.ts +28 -0
  34. package/dist/replay/condition-words.js +59 -0
  35. package/dist/replay/controller.d.ts +78 -7
  36. package/dist/replay/controller.js +318 -62
  37. package/dist/replay/derive.d.ts +15 -0
  38. package/dist/replay/derive.js +5 -0
  39. package/dist/replay/handover.d.ts +7 -0
  40. package/dist/replay/handover.js +89 -51
  41. package/dist/replay/plan.d.ts +101 -5
  42. package/dist/replay/plan.js +260 -18
  43. package/dist/replay/source-run.d.ts +2 -2
  44. package/dist/replay/source-run.js +3 -3
  45. package/dist/replay/targets.d.ts +40 -1
  46. package/dist/replay/targets.js +105 -3
  47. package/dist/replay/types.d.ts +25 -8
  48. package/docs/calculatedReplay.md +45 -5
  49. package/docs/calculatedReplayGuide.md +13 -4
  50. package/package.json +1 -1
  51. package/production.md +21 -7
@@ -49,9 +49,14 @@
49
49
  * only with enough of the budget left.
50
50
  *
51
51
  * STDOUT IS CONTEXT. For SessionStart, Claude Code adds whatever a command hook
52
- * prints on stdout to the model's context. So nothing is printed unless the
53
- * control server answered with something (today it answers `{}`), and then it
54
- * is the answer, verbatim, as JSON.
52
+ * prints on stdout to the model's context — unless it is one JSON object, which
53
+ * it reads as the hook's answer instead. So nothing is printed unless the
54
+ * control server answered with something, and then it is the answer, as one
55
+ * line of JSON and nothing else. The control server answers `{}`, or — when
56
+ * plans of the account wait for their owner (the service's review of
57
+ * calcscenarioautocorrect.md, item 8) — `{ "systemMessage": "bir: …" }`, a
58
+ * line Claude Code shows the person and never the model. Characters past ASCII
59
+ * are written as `\u` escapes, so a console code page cannot garble the line.
55
60
  */
56
61
  import { type EnsureOptions } from "./daemon.js";
57
62
  /**
@@ -93,6 +98,12 @@ export interface EnsureHookOptions extends EnsureOptions {
93
98
  */
94
99
  overrideCheck?: ((projectCwd: string) => StaleOverrides) | false;
95
100
  }
101
+ /**
102
+ * `value` as one line of JSON whose every character past ASCII is a `\u`
103
+ * escape: the same JSON to any reader, and nothing a console's code page can
104
+ * turn into something else on the way to Claude Code.
105
+ */
106
+ export declare function asciiJson(value: unknown): string;
96
107
  /**
97
108
  * Run the hook for `cwd`. Resolves the exit code, which is always 0.
98
109
  */
@@ -49,9 +49,14 @@
49
49
  * only with enough of the budget left.
50
50
  *
51
51
  * STDOUT IS CONTEXT. For SessionStart, Claude Code adds whatever a command hook
52
- * prints on stdout to the model's context. So nothing is printed unless the
53
- * control server answered with something (today it answers `{}`), and then it
54
- * is the answer, verbatim, as JSON.
52
+ * prints on stdout to the model's context — unless it is one JSON object, which
53
+ * it reads as the hook's answer instead. So nothing is printed unless the
54
+ * control server answered with something, and then it is the answer, as one
55
+ * line of JSON and nothing else. The control server answers `{}`, or — when
56
+ * plans of the account wait for their owner (the service's review of
57
+ * calcscenarioautocorrect.md, item 8) — `{ "systemMessage": "bir: …" }`, a
58
+ * line Claude Code shows the person and never the model. Characters past ASCII
59
+ * are written as `\u` escapes, so a console code page cannot garble the line.
55
60
  */
56
61
  import { appendFileSync } from "node:fs";
57
62
  import { Console } from "node:console";
@@ -152,6 +157,14 @@ function reportStaleOverrides(projectCwd, logFile, msLeft, check) {
152
157
  /* a check that failed leaves things as they were; `bir doctor` names what is stale */
153
158
  }
154
159
  }
160
+ /**
161
+ * `value` as one line of JSON whose every character past ASCII is a `\u`
162
+ * escape: the same JSON to any reader, and nothing a console's code page can
163
+ * turn into something else on the way to Claude Code.
164
+ */
165
+ export function asciiJson(value) {
166
+ return JSON.stringify(value).replace(/[\u007f-￿]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`);
167
+ }
155
168
  /** True when this process's own environment switches the runner off (`BIR_OFF=1` or `true`). */
156
169
  function offInThisEnvironment() {
157
170
  const value = (process.env.BIR_OFF ?? "").trim().toLowerCase();
@@ -228,8 +241,8 @@ export async function runEnsureHook(cwd, opts = {}) {
228
241
  signal: timeoutSignal(Math.max(300, Math.min(RELAY_TIMEOUT_MS, left()))),
229
242
  });
230
243
  const answer = (await res.json().catch(() => ({})));
231
- if (res.ok && answer && typeof answer === "object" && Object.keys(answer).length > 0) {
232
- stdout.write(JSON.stringify(answer));
244
+ if (res.ok && answer && typeof answer === "object" && !Array.isArray(answer) && Object.keys(answer).length > 0) {
245
+ stdout.write(asciiJson(answer));
233
246
  }
234
247
  }
235
248
  catch (err) {
@@ -52,6 +52,21 @@ export declare function derivationKey(daemon?: boolean): {
52
52
  apiKey?: string;
53
53
  source?: string;
54
54
  };
55
+ /**
56
+ * `BIR_SEGMENT_ARM`: whether a segment the service hands out may run mid-task.
57
+ * On unless it is `0`, from runner 0.3.19 (the service's segmentsbreakdown.md
58
+ * SB-21): segments found by repeated work, and the halves after a plan's
59
+ * decision. `0` is observe-only: what would have armed is logged, and nothing
60
+ * is replaced. While it is on, whole plans may start mid-task by intent too:
61
+ * `BIR_INTENT_ARM` follows it unless that is set.
62
+ */
63
+ export declare function segmentArmFromEnv(env?: NodeJS.ProcessEnv): boolean;
64
+ /**
65
+ * What the `replay.enabled` startup line says segments do. A segment starts
66
+ * only where the probe is waited on, so `BIR_INTENT_ARM=0` starts nothing
67
+ * mid-task, segments included — the rule `bir doctor` words from /health.
68
+ */
69
+ export declare function segmentsAtStartup(segmentArm: boolean, env?: NodeJS.ProcessEnv): string;
55
70
  /** Where the two replay switches came from, for `/health` and `bir doctor`. */
56
71
  export type ReplaySource = "env" | "sidecar" | "default";
57
72
  /** What a recorder still serves (K4, `ControlServer.activity()`). */
@@ -152,6 +152,27 @@ export function derivationKey(daemon = process.env[DAEMON_ENV] === "1") {
152
152
  }
153
153
  return {};
154
154
  }
155
+ /**
156
+ * `BIR_SEGMENT_ARM`: whether a segment the service hands out may run mid-task.
157
+ * On unless it is `0`, from runner 0.3.19 (the service's segmentsbreakdown.md
158
+ * SB-21): segments found by repeated work, and the halves after a plan's
159
+ * decision. `0` is observe-only: what would have armed is logged, and nothing
160
+ * is replaced. While it is on, whole plans may start mid-task by intent too:
161
+ * `BIR_INTENT_ARM` follows it unless that is set.
162
+ */
163
+ export function segmentArmFromEnv(env = process.env) {
164
+ return env.BIR_SEGMENT_ARM !== "0";
165
+ }
166
+ /**
167
+ * What the `replay.enabled` startup line says segments do. A segment starts
168
+ * only where the probe is waited on, so `BIR_INTENT_ARM=0` starts nothing
169
+ * mid-task, segments included — the rule `bir doctor` words from /health.
170
+ */
171
+ export function segmentsAtStartup(segmentArm, env = process.env) {
172
+ if (!segmentArm)
173
+ return "observed only (BIR_SEGMENT_ARM=0)";
174
+ return env.BIR_INTENT_ARM === "0" ? "observed only (BIR_INTENT_ARM=0)" : "run mid-task";
175
+ }
155
176
  /**
156
177
  * Assemble the replay configuration (docs/calculatedReplay.md §5.3 of the guide).
157
178
  *
@@ -204,19 +225,21 @@ function buildReplayOptions(keeper, policy, key) {
204
225
  intentMatch: {
205
226
  enabled: process.env.BIR_INTENT_MATCH !== "0",
206
227
  // The probe's own ceiling, not a wait on the hook's path: `PreToolUse`
207
- // sends it and answers without it (hit counting only) unless arming is
208
- // on (BIR_SEGMENT_ARM=1 or BIR_INTENT_ARM=1), and then waits at most
228
+ // waits for it only while arming is on — the default, unless
229
+ // BIR_SEGMENT_ARM=0 or BIR_INTENT_ARM=0 — and then at most
209
230
  // BIR_INTENT_ARM_BUDGET_MS (500 ms), never while the service is failing
210
- // (control/server.ts). 4 000 leaves a request whose best segment lands
211
- // in the not-clear band its one live question under the service's own
212
- // 2 500 ms attempt (segmented.md R-HIT-11).
231
+ // (control/server.ts); otherwise it answers without it (hit counting
232
+ // only). 4 000 leaves a request whose best segment lands in the
233
+ // not-clear band its one live question under the service's own 2 500 ms
234
+ // attempt (segmented.md R-HIT-11).
213
235
  budgetMs: positiveInt(process.env.BIR_INTENT_MATCH_BUDGET_MS, 4_000),
214
236
  maxPerTurn: positiveInt(process.env.BIR_INTENT_MATCHES_PER_TURN, 3),
215
237
  maxRequestsPerTurn: positiveInt(process.env.BIR_INTENT_REQUESTS_PER_TURN, 40),
216
238
  },
217
- // Observe-only until an operator has read the logs and turned it on
218
- // (segmented.md R-OUT-10, R-LIFE-7). Arming is never the way to find out.
219
- segmentArm: process.env.BIR_SEGMENT_ARM === "1",
239
+ // On by default from 0.3.19 (the service's segmentsbreakdown.md SB-21): a
240
+ // plan that stops at its decision hands the rest to segments of each half,
241
+ // and they run only mid-task. BIR_SEGMENT_ARM=0 brings back observe-only.
242
+ segmentArm: segmentArmFromEnv(),
220
243
  // Both read live, never captured (C5). The recorder signs in after it
221
244
  // listens, refreshes the token as the session outlives it, and may sign
222
245
  // in or out while it runs; replay reads whatever holds *now*. The old
@@ -247,6 +270,8 @@ function buildReplayOptions(keeper, policy, key) {
247
270
  ? "the service"
248
271
  : "recorded sample values — sign in, or a scenario with a target will not run"),
249
272
  intentMatch: opts.intentMatch?.enabled ? "on" : "off",
273
+ // Whether a segment the service hands out runs, or is only logged.
274
+ segments: segmentsAtStartup(opts.segmentArm === true),
250
275
  source,
251
276
  why: "a prompt that matches a calculated scenario runs it: the scenario's own steps run without a " +
252
277
  "permission prompt of their own (never in plan mode); every other tool call is asked about as " +
@@ -24,9 +24,26 @@
24
24
  * accepts unauthenticated step reports is a local exfiltration channel.
25
25
  */
26
26
  import { type IntentSource } from "./transcript.js";
27
- import { type Recorder } from "../record/recorder.js";
27
+ import { type AttentionPlan, type Recorder } from "../record/recorder.js";
28
28
  import { type ReplayOptions } from "../replay/controller.js";
29
29
  import { type ServiceHealth } from "../util/service-health.js";
30
+ /**
31
+ * The project a run or a tool list belongs to (the service's review of
32
+ * calcscenarioautocorrect.md, item 16): the first 16 hex characters of the
33
+ * sha256 of its folder, made absolute and — on Windows — lower-cased with
34
+ * forward slashes, as the discovery file's key is (paths.ts, `controlKey`).
35
+ * The service only compares it, so a plan's recording finds its own
36
+ * project's tools and never another project's server of the same name.
37
+ */
38
+ export declare function projectKey(dir: string): string;
39
+ /**
40
+ * The one line a session start shows the person (review item 8): which plan
41
+ * waits, for how long, why, and where to look —
42
+ * `bir: a plan needs you — "Check engaged users and reassign leads" held for 3 days, a correction is waiting: <url>`
43
+ * — and with more than one, how many, and the first one's page. A plan that
44
+ * is not held, whose correction waits, says only that.
45
+ */
46
+ export declare function attentionText(plans: readonly AttentionPlan[], now?: number): string | undefined;
30
47
  /**
31
48
  * The prompt match gate (see {@link ControlServer.armFromMatch}). A match that
32
49
  * answered slower than `slowMs` (`BIR_MATCH_SLOW_MS`), or not inside its
@@ -259,12 +276,13 @@ export interface ControlServerOptions {
259
276
  * (fallbk.md §Runner 4).
260
277
  *
261
278
  * Arming there means *waiting* there, on every unsteered tool call, for a
262
- * service round trip — so by default the probe is sent without waiting, for
263
- * hit counting only, and nothing arms mid-turn. It waits (at most
264
- * `BIR_INTENT_ARM_BUDGET_MS`, 500 ms, and never while the service is failing)
265
- * when segment arming is on (`BIR_SEGMENT_ARM=1`), or when `BIR_INTENT_ARM=1`
266
- * asks for whole-scenario intent arming as before. `BIR_INTENT_ARM=0` never
267
- * waits.
279
+ * service round trip. It waits (at most `BIR_INTENT_ARM_BUDGET_MS`, 500 ms,
280
+ * and never while the service is failing) when segment arming is on — the
281
+ * recorder's default from 0.3.19, `BIR_SEGMENT_ARM` unset or `1` — or when
282
+ * `BIR_INTENT_ARM=1` asks for whole-scenario intent arming. `BIR_INTENT_ARM=0`
283
+ * never waits. Otherwise, as in a server built with neither (a test, an
284
+ * embedder), the probe is sent without waiting, for hit counting only, and
285
+ * nothing arms mid-turn.
268
286
  */
269
287
  intentArm?: boolean;
270
288
  /**
@@ -311,6 +329,20 @@ export declare class ControlServer {
311
329
  private readonly proxySessions;
312
330
  /** `run_scenario` pres not yet consumed by a `/scenario/run`, oldest first. */
313
331
  private directCalls;
332
+ /**
333
+ * `report_wrong_result` pres not yet consumed by a `/scenario/dispute`,
334
+ * oldest first (review item 19): how a report from `bir-scenario`, which is
335
+ * never told its session, finds the session — and so the plan — it is about.
336
+ */
337
+ private reportCalls;
338
+ /** Plans the agent said were wrong, and the service's answer — on its way, or in. */
339
+ private readonly disputed;
340
+ /**
341
+ * The plans that need their owner, by account, as the service last listed
342
+ * them (`GET /scenarios/attention`, review item 8), and the ask still on its
343
+ * way. Null is "nothing can be said": no service, an older one, a failure.
344
+ */
345
+ private readonly attention;
314
346
  /** Plans a `/scenario/run` is executing right now: one run per plan. */
315
347
  private readonly runningPlans;
316
348
  /**
@@ -572,6 +604,16 @@ export declare class ControlServer {
572
604
  */
573
605
  private ensureRun;
574
606
  private runMetadata;
607
+ /**
608
+ * The project key of everything this recorder sends (review item 16): its
609
+ * runs and its tool lists. The folder this recorder serves — the one its
610
+ * discovery file is keyed by and its hooks are installed in — and not a
611
+ * session's `cwd`: that follows the agent's `cd` from turn to turn, and a
612
+ * tool list is sent when its proxy starts, before any of its calls ties it
613
+ * to a session. The service matches a plan's recording to its tools by
614
+ * equality, so both sides must hash the same folder.
615
+ */
616
+ private projectKeyHere;
575
617
  /**
576
618
  * A similar-prompt hit means the service created no run, so every later step
577
619
  * post would 404. Stop recording it — that half is v1 and is mandatory, and
@@ -668,6 +710,16 @@ export declare class ControlServer {
668
710
  * is none, and a `bir-scenario` must have proved it serves the tool.
669
711
  */
670
712
  private deliveryToolAvailable;
713
+ /**
714
+ * Whether a running `bir-scenario` here serves `report_wrong_result` right
715
+ * now (review item 19): one that is not off said so in its hello. What a
716
+ * bundle delivered beside a call of the agent's own (a divergence) is laid
717
+ * out by: such a bundle forbids every tool call but this one. A direct
718
+ * plan's bundle goes by its own `bir-scenario`'s word instead, which came
719
+ * with its `/scenario/run` (see {@link onScenarioRun}). Configured is not
720
+ * connected, as for the delivery tool.
721
+ */
722
+ private reportToolServed;
671
723
  /**
672
724
  * Whether this project's configuration has the `bir` server, as far as
673
725
  * anything here knows: a hook that named one of its tools, the caller's
@@ -681,6 +733,25 @@ export declare class ControlServer {
681
733
  * only — no other I/O.
682
734
  */
683
735
  private liveHellos;
736
+ /**
737
+ * The tool list of each wrapped server last sent to the service, by
738
+ * account, project and server (AC12; review item 16): its hash, when, and
739
+ * whether the service took it (null while the send is on its way).
740
+ */
741
+ private readonly toolListsSent;
742
+ /**
743
+ * `POST /proxy/tools` — a proxy's server listed its tools (`tools/list`;
744
+ * the service's autocorrect.md AC12). Its names, descriptions, input
745
+ * schemas and what it says of each tool (`annotations`) go to the service —
746
+ * secret-shaped text taken out, and within what the service keeps — with
747
+ * the project they are of ({@link projectKeyHere}, the key every run here
748
+ * carries). Sent when the list changed since that server's last one for
749
+ * this account and project, when the last send failed ten minutes ago, or
750
+ * six hours after the last one the service took. `bir off`, and a session
751
+ * started with BIR_OFF=1, send nothing, as for a step. Always `{ ok: true }`;
752
+ * never slow.
753
+ */
754
+ private onProxyTools;
684
755
  /**
685
756
  * `POST /scenario/hello` — a running `bir-scenario` saying what it serves
686
757
  * (K3): on `initialize`, on `tools/list`, when its off state flips and every
@@ -689,6 +760,42 @@ export declare class ControlServer {
689
760
  * and is not off still runs. Always `{ ok: true }`; never slow.
690
761
  */
691
762
  private onScenarioHello;
763
+ /** Remember a `report_wrong_result` pre (see {@link reportCalls}): a few, and only for a while. */
764
+ private noteReportCall;
765
+ /**
766
+ * `POST /scenario/dispute` — the agent said, through `bir`'s
767
+ * `report_wrong_result`, that a replay's results do not answer its request
768
+ * (the service's review of calcscenarioautocorrect.md, item 19): the plan
769
+ * took the wrong half of an "if … otherwise", or acted on the wrong item. A
770
+ * wrong half is caught only when the agent says so, and until now there was
771
+ * no way to say it but in an answer nobody might read.
772
+ *
773
+ * WHOSE PLAN. The session is the one whose `PreToolUse` for the tool carried
774
+ * the request's `toolUseId` (`bir-scenario` forwards the host's id for the
775
+ * call, as for `run_scenario`); without one, the one session where such a
776
+ * call is waiting; without that either, the one session whose plan ran in
777
+ * the last half hour. Never a guess between two. The plan is the newest one
778
+ * that ran in that session's latest turn with a plan: this turn's, or the
779
+ * one before it.
780
+ *
781
+ * WHAT GOES. `POST /scenarios/:scenarioId/dispute` with the reason, `by:
782
+ * "agent"`, and the turn's execution ticket as `executionId` — the id its
783
+ * report books under when the turn ends, so the service may well have the
784
+ * ticket and not yet the row. Once per plan and turn: a second report of the
785
+ * same plan is answered from the first. Nothing here undoes anything.
786
+ *
787
+ * Answers `{ ok, held?, why? }` for `bir-scenario` to put in one sentence.
788
+ * Never throws, and is never slow beyond the service's own bound.
789
+ */
790
+ private onScenarioDispute;
791
+ /** The session a `/scenario/dispute` is about (see {@link onScenarioDispute}), or undefined. */
792
+ private sessionForDispute;
793
+ /**
794
+ * The plan a report of a wrong replay is about: the newest that ran — a
795
+ * step of it did its work — in the session's latest turn with one, this
796
+ * turn or the one before it.
797
+ */
798
+ private disputedPlan;
692
799
  /**
693
800
  * Re-read whether this project configures the `bir` server — only when
694
801
  * ~/.claude.json or .mcp.json changed since the last read. A file the host is
@@ -713,6 +820,20 @@ export declare class ControlServer {
713
820
  * not Claude Code, and undefined when it is.
714
821
  */
715
822
  private hostContext;
823
+ /**
824
+ * The day this session's agent was told it is (issue 1212), read from its
825
+ * transcript each time it is asked. A session of another host has none.
826
+ */
827
+ private agentDayFor;
828
+ /**
829
+ * What a run's finish says of its agent's day (the service's review of
830
+ * calcscenarioautocorrect.md, item 1): the line of the session's system
831
+ * prompt that gives the date — never Claude Code's own date reminder, which
832
+ * is only this machine's day — and this machine's day when the turn's prompt
833
+ * came. The service checks a corrected plan against this run, and reads its
834
+ * prompt with this day, as a replay would have. Undefined with neither.
835
+ */
836
+ private runAgentDay;
716
837
  /**
717
838
  * The earlier tool results the reading of new values may look at: this
718
839
  * prompt's own, from the transcript — and so none for a plan that is
@@ -801,6 +922,20 @@ export declare class ControlServer {
801
922
  /** Transcript cost since `mark`; unmeasured (and zero) without one, or with a model that has no price. */
802
923
  private costSince;
803
924
  private reportExecution;
925
+ /**
926
+ * The plan's own calls of the turn, whole, as an answered turn's report
927
+ * carries them (the service's review of calcscenarioautocorrect.md, item
928
+ * 12): what each step sent and what came back, as they were kept for the
929
+ * turn (replay/controller.ts, `TurnCall`) — in a recording's own form,
930
+ * redacted and capped at 64 KiB apiece, as a redo's copies are (AC2) —
931
+ * where the report's own copies have 3,800 bytes. The calls of a called sub-task are
932
+ * left out: their step numbers are the sub-task's own, and would read as the
933
+ * plan's steps of the same number. `kept` is what the turn kept, taken while
934
+ * the seal still had it; this runs after the answer's wait, off the hook's
935
+ * path. The recorder cuts them to what one report may carry when it sends
936
+ * them.
937
+ */
938
+ private answerCallsOf;
804
939
  /**
805
940
  * Seal the session's run now, and drain it to the service in the background.
806
941
  *
@@ -831,6 +966,26 @@ export declare class ControlServer {
831
966
  */
832
967
  private drainRun;
833
968
  private onSessionStart;
969
+ /**
970
+ * The line a session start shows the person when plans of this account
971
+ * wait for their owner (`GET /scenarios/attention`; the service's review of
972
+ * calcscenarioautocorrect.md, item 8), or undefined. A `systemMessage`:
973
+ * Claude Code shows it to the person and adds nothing to the model's
974
+ * context. Asked at most once in {@link ATTENTION_CACHE_MS} per account (in
975
+ * {@link ATTENTION_RETRY_MS} when it could say nothing), waited for at most
976
+ * {@link ATTENTION_WAIT_MS}, and never while the service is failing; any
977
+ * failure says nothing. Not after a compaction — the
978
+ * session is mid-work, and was told at its start — and never in a session
979
+ * of another host, whose adapter shows a person nothing.
980
+ */
981
+ private attentionLine;
982
+ /**
983
+ * This account's plans that need their owner: the cached answer while it is
984
+ * fresh, else the service's, waited for no longer than
985
+ * {@link ATTENTION_WAIT_MS}. An answer that comes later still fills the
986
+ * cache for the next session start. One ask at a time per account.
987
+ */
988
+ private attentionFor;
834
989
  private onPrompt;
835
990
  /**
836
991
  * `PreToolUse`. Two shapes of answer:
@@ -1000,14 +1155,19 @@ export declare class ControlServer {
1000
1155
  */
1001
1156
  private excludeRunsFor;
1002
1157
  /**
1003
- * Per recording a plan of this turn came from, how far that plan got
1004
- * (segmented.md R-OUT-6).
1158
+ * Per plan of this turn, its recording and how far the plan got in its own
1159
+ * chain (segmented.md R-OUT-6).
1005
1160
  *
1006
1161
  * The server uses it to refuse a segment that starts *before* where this turn
1007
1162
  * already is: re-running steps the turn has just done is worse than not
1008
- * replaying at all. The largest position per recording wins, and a plan
1009
- * declined for anything but a known-bad first step reports nothing — it ran
1010
- * none of the recording, so it blocks none of it.
1163
+ * replaying at all. The position is the plan's own: the step it handed over
1164
+ * at (its decision included), or its last step when it ran to its end. The
1165
+ * server maps it onto the recording — a segment's first position, a
1166
+ * decision's step before it — and takes the largest per recording (the
1167
+ * service's segmentsbreakdown.md D18). One entry per plan, so that two plans
1168
+ * of one recording, whose own positions are on different scales, never
1169
+ * hide each other. A plan declined for anything but a known-bad first step
1170
+ * reports nothing — it ran none of the recording, so it blocks none of it.
1011
1171
  *
1012
1172
  * Rebuilt on every probe rather than cached: a plan that hands over between
1013
1173
  * two calls changes the answer.
@@ -1058,6 +1218,16 @@ export declare class ControlServer {
1058
1218
  * whose intent-armed plan handed over is recording the model's work anyway.
1059
1219
  */
1060
1220
  private noteHandover;
1221
+ /**
1222
+ * Drop a fragment scheduled at a decision (the service's
1223
+ * segmentsbreakdown.md SB-16) when a plan the intent check armed takes the
1224
+ * agent's next call: what follows is that plan's half, not the other one,
1225
+ * and recorded as the other half it would become a wrong segment C. Cleared
1226
+ * rather than ended, so a later hand-over of that plan schedules its own
1227
+ * fragment, as any plan's does in a turn with none. Any other fragment is
1228
+ * left as it is.
1229
+ */
1230
+ private dropDecisionFragment;
1061
1231
  /**
1062
1232
  * Open the fragment run and record from this call on (fallbk.md D7).
1063
1233
  *
@@ -1353,7 +1523,16 @@ export declare class ControlServer {
1353
1523
  private soleCandidate;
1354
1524
  /** Record the merged pair for a correlation and latch it against re-entry. */
1355
1525
  private settle;
1356
- /** The one place a tool step pair is written. Redaction and caps live here. */
1526
+ /**
1527
+ * The one place a tool step pair is written. Redaction and caps live here.
1528
+ *
1529
+ * Both rows carry the call's id (`metadata.callId`, review item 7) — the
1530
+ * caller's, or one minted here for a call no hook named — and the answer
1531
+ * of a call that failed says so (`metadata.isError`). The service pairs a
1532
+ * call with its answer by that id, where it used to pair them by tool name
1533
+ * in the order they came: two calls of one tool at once, answered out of
1534
+ * order, took each other's answers.
1535
+ */
1357
1536
  private record;
1358
1537
  }
1359
1538
  /**