@basein/runner 0.3.9 → 0.3.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +46 -5
  2. package/dist/auth/client.d.ts +15 -0
  3. package/dist/auth/client.js +34 -12
  4. package/dist/bin/bir-hooks.d.ts +1 -1
  5. package/dist/bin/bir-hooks.js +1 -1
  6. package/dist/bin/bir.d.ts +2 -0
  7. package/dist/bin/bir.js +60 -6
  8. package/dist/bin/investigate.d.ts +5 -0
  9. package/dist/bin/investigate.js +117 -15
  10. package/dist/bin/setup.d.ts +12 -0
  11. package/dist/bin/setup.js +185 -56
  12. package/dist/bin/update.d.ts +81 -0
  13. package/dist/bin/update.js +194 -0
  14. package/dist/config/adapters/claude-code.d.ts +17 -0
  15. package/dist/config/adapters/claude-code.js +28 -1
  16. package/dist/config/generate.d.ts +14 -0
  17. package/dist/control/daemon.d.ts +26 -1
  18. package/dist/control/daemon.js +53 -3
  19. package/dist/control/pin-guard.d.ts +6 -1
  20. package/dist/control/pin-guard.js +15 -1
  21. package/dist/control/recorder-keeper.js +2 -1
  22. package/dist/control/server.d.ts +200 -2
  23. package/dist/control/server.js +741 -48
  24. package/dist/control/transcript.d.ts +4 -0
  25. package/dist/control/transcript.js +4 -0
  26. package/dist/proxy/session.js +1 -0
  27. package/dist/record/housekeeping.d.ts +7 -1
  28. package/dist/record/housekeeping.js +11 -3
  29. package/dist/record/recorder.d.ts +34 -0
  30. package/dist/record/remote-recorder.d.ts +6 -0
  31. package/dist/record/remote-recorder.js +31 -4
  32. package/dist/record/switchable-recorder.js +1 -0
  33. package/dist/replay/controller.d.ts +132 -3
  34. package/dist/replay/controller.js +248 -24
  35. package/dist/replay/derive.js +2 -0
  36. package/dist/replay/handover.d.ts +13 -1
  37. package/dist/replay/handover.js +19 -0
  38. package/dist/replay/safety.d.ts +15 -4
  39. package/dist/replay/safety.js +19 -6
  40. package/dist/replay/source-run.js +2 -1
  41. package/dist/util/service-headers.d.ts +24 -0
  42. package/dist/util/service-headers.js +47 -0
  43. package/docs/BaseInstRunner.md +115 -1
  44. package/docs/calculatedReplay.md +209 -32
  45. package/docs/calculatedReplayGuide.md +133 -12
  46. package/docs/installRun.md +91 -10
  47. package/docs/quickstart.md +64 -9
  48. package/package.json +1 -1
  49. package/production.md +81 -17
  50. package/scripts/install.ps1 +25 -1
@@ -83,7 +83,58 @@ export interface HookPayload {
83
83
  * only. See {@link ControlServer.offSessions}.
84
84
  */
85
85
  bir_off?: unknown;
86
+ /**
87
+ * Which host the session runs in. An adapter sends it with every request,
88
+ * so a recorder that restarted in the middle of a run still learns it; the
89
+ * session keeps the first one (see {@link hostOf}).
90
+ */
91
+ host?: {
92
+ app?: unknown;
93
+ version?: unknown;
94
+ adapter?: unknown;
95
+ scope?: unknown;
96
+ };
97
+ /**
98
+ * With `/session/prompt`: the adapter can step through a plan, and asks
99
+ * for one. An adapter that only records never sends it, and no plan is
100
+ * started for its run.
101
+ */
102
+ replay?: unknown;
103
+ /** With `/session/prompt`: the host's tools whose input is run, or written into a file. */
104
+ sinks?: {
105
+ command?: unknown;
106
+ write?: unknown;
107
+ };
108
+ /** With `/tool/pre`: what the model wrote before the call. */
109
+ context?: unknown;
110
+ /** With `/tool/pre`: what the model reasoned before the call. */
111
+ thinking?: unknown;
112
+ /**
113
+ * The adapter's own call for a step of the plan, not the model's: on the
114
+ * `/tool/pre` that asks for the step's arguments, and on the `/tool/post`
115
+ * that brings its result.
116
+ */
117
+ scripted?: unknown;
118
+ /** With the first scripted `/tool/pre` of a run: the tools the agent holds. */
119
+ tools?: unknown;
120
+ /** With a scripted `/tool/post`: the host stopped the step itself (`rejected`, `edited`). */
121
+ host_stop?: unknown;
122
+ /** With `/session/stop`: the run's final answer. */
123
+ final_answer?: unknown;
124
+ /** With `/session/stop`: the tokens the run's model calls used, per model, under Claude's own names. */
125
+ usage?: unknown;
126
+ /** With `/session/stop`: the run failed, and why. */
127
+ failed?: unknown;
128
+ /** With `/session/stop`: the recording is not the whole run, and why. */
129
+ incomplete?: unknown;
86
130
  }
131
+ /**
132
+ * What `/health` answers as `hostFields`: how much of another host's protocol
133
+ * this recorder understands. An adapter reads it to know which fields it may
134
+ * rely on — with 1 it records; from 2 it may also ask for a plan and step
135
+ * through it, because the checks a replay in another host needs are here.
136
+ */
137
+ export declare const HOST_FIELDS = 2;
87
138
  export interface ControlServerOptions {
88
139
  recorder: Recorder;
89
140
  /**
@@ -296,6 +347,13 @@ export declare class ControlServer {
296
347
  * SessionStart.
297
348
  */
298
349
  private readonly offSessions;
350
+ /**
351
+ * Sessions of another host that named a scope the recorder cannot keep as
352
+ * it is (see {@link badScope}) → when last heard. Nothing of such a session
353
+ * is recorded or replayed: every hook of it is answered `{}`. Kept apart
354
+ * from {@link sessions}, so it never holds a run or takes a proxy's step.
355
+ */
356
+ private readonly refusedSessions;
299
357
  /**
300
358
  * Proxy ids that registered with *this* recorder (UPG-7). A poll from any
301
359
  * other id is a proxy from before this recorder started — a 0.2.12 proxy
@@ -434,6 +492,18 @@ export declare class ControlServer {
434
492
  private sessionOff;
435
493
  /** Mark `sessionId` off (see {@link sessionOff}), sealing whatever it had open. */
436
494
  private markSessionOff;
495
+ /**
496
+ * Whether this hook belongs to a session of another host whose scope the
497
+ * recorder cannot keep as it is (see {@link badScope}), and so is answered
498
+ * `{}` unprocessed — every hook of it, like a session that is off. One
499
+ * line says so, once for the session.
500
+ *
501
+ * A session that already has a host keeps it: a later payload cannot turn
502
+ * a run that is being recorded into one that is not.
503
+ */
504
+ private hostRefused;
505
+ /** Flag every run open right now as having lost steps (see `RunState.lossy`). */
506
+ private markOpenRunsLossy;
437
507
  /** Whether `sessionId` is a session started with `BIR_OFF=1`. */
438
508
  private isSessionOff;
439
509
  /** The route's hook deadline, or undefined when `route` is not a hook this recorder serves. */
@@ -492,7 +562,12 @@ export declare class ControlServer {
492
562
  reap(now?: number): void;
493
563
  /** Drop a session and everything keyed on it. Its last run was sealed and drained already. */
494
564
  private forgetSession;
495
- /** Open a run for `session`, or return the open one. */
565
+ /**
566
+ * Open a run for `session`, or return the open one.
567
+ *
568
+ * `asked` is what the prompt that opens the run asked for, in a session of
569
+ * another host: whether its adapter wants a plan (`HookPayload.replay`).
570
+ */
496
571
  private ensureRun;
497
572
  private runMetadata;
498
573
  /**
@@ -589,6 +664,41 @@ export declare class ControlServer {
589
664
  private refreshDeliveryTool;
590
665
  /** `arming`, never rejecting: a broken arm is logged and is no plan at all. */
591
666
  private armOrLog;
667
+ /**
668
+ * Whether `run` only records: a run of another host whose adapter did not
669
+ * ask for a plan with its prompt. Treated as a run with replay off in the
670
+ * three places that ask (`onPrompt`, `armFromMatch`, `tryIntentMatch`).
671
+ *
672
+ * An adapter that only records never sends `replay`. Without this the
673
+ * recorder would start a plan for every prompt that matches one, read the
674
+ * model's own calls against it, and book a replay nobody ran. A run another
675
+ * hook opened carries no flag either, and only records.
676
+ */
677
+ private recordsOnly;
678
+ /**
679
+ * What {@link ReplayController.arm} is told of `session` when its host is
680
+ * not Claude Code, and undefined when it is.
681
+ */
682
+ private hostContext;
683
+ /**
684
+ * The earlier tool results the reading of new values may look at: this
685
+ * prompt's own, from the transcript — and so none for a plan that is
686
+ * started at the prompt (segmented.md R-PARAM-2a).
687
+ *
688
+ * `at` is where the plan is being started, and at the prompt a transcript
689
+ * has nothing to give. The prompt was only just submitted, so it has no
690
+ * results of its own; and the transcript does not hold it yet, so what the
691
+ * transcript calls "this prompt's results" are still the *previous*
692
+ * prompt's. They used to be sent as this task's, and they answered for it:
693
+ * a second prompt that named one person ran on the person the first
694
+ * prompt's results were full of.
695
+ *
696
+ * A session of another host has no transcript, and a plan starts there at
697
+ * the prompt only: none either. Its adapter used to hand the conversation's
698
+ * earlier results over with the prompt (`recent_results`) — the same
699
+ * mistake, made on purpose. An adapter that still sends them is not read.
700
+ */
701
+ private recentResultsFor;
592
702
  /**
593
703
  * The prompt hook answered without a directive: retire any prompt-armed plan
594
704
  * of `run` that could still steer. `armFromMatch` arms one live only while
@@ -616,6 +726,9 @@ export declare class ControlServer {
616
726
  * routinely left half-open, which is what happened in practice.
617
727
  *
618
728
  * Idempotent: the second caller sees `finished` and does nothing.
729
+ *
730
+ * `reason` says why the run is closed. It decides something only in a
731
+ * session of another host (see {@link CloseReason}).
619
732
  */
620
733
  private sealRun;
621
734
  /**
@@ -640,7 +753,14 @@ export declare class ControlServer {
640
753
  * false` and zero rather than a number that is confidently wrong.
641
754
  */
642
755
  private runCost;
643
- /** Transcript cost since `mark`; unmeasured (and zero) without one. */
756
+ /**
757
+ * What a run of another host cost, from the tokens its adapter handed over
758
+ * with the stop. Measured only when at least one entry has tokens and every
759
+ * model in the list has a price; with no usage at all, not measured — and
760
+ * one `run.unmeasured` line says which.
761
+ */
762
+ private hostCost;
763
+ /** Transcript cost since `mark`; unmeasured (and zero) without one, or with a model that has no price. */
644
764
  private costSince;
645
765
  private reportExecution;
646
766
  /**
@@ -691,6 +811,68 @@ export declare class ControlServer {
691
811
  * still running when the deadline passes — see {@link replayOverran}.
692
812
  */
693
813
  private onToolPre;
814
+ /**
815
+ * `/tool/pre` in a session whose host is not Claude Code — a LangChain
816
+ * application, say (the service's integratioLangChane.md). Two kinds of
817
+ * request, told apart by `scripted`:
818
+ *
819
+ * - **The adapter asks for the plan's next step** (`scripted: true`). It
820
+ * is answering in the model's place and wants the step's arguments; the
821
+ * host then runs its own tool with them. See {@link onScriptedPre}.
822
+ * - **The model's own call.** Recorded, never pinned. See {@link onOwnPre}.
823
+ *
824
+ * ONLY A PROMPT OPENS A RUN for such a host. A hook that finds none open
825
+ * opens none and answers `{}`: the run failed and its adapter closed it,
826
+ * or the two-hour limit sealed it, or the recorder restarted under it. What
827
+ * arrives afterwards — the application going on from its checkpoint, a
828
+ * retry middleware trying the call again — must not become a second
829
+ * recording with an empty prompt.
830
+ */
831
+ private onHostToolPre;
832
+ /**
833
+ * The adapter of another host asks for the plan's next step. The answer
834
+ * either pins the call — `allow`, with the step's computed arguments — or
835
+ * carries no pin, which ends the plan for the adapter. It then says why:
836
+ *
837
+ * - `noPlan`: the recorder holds no live plan for the run. It ran to its
838
+ * end, was handed over, or the recorder restarted while the run waited
839
+ * for a person. Nothing is recorded: no call follows this request.
840
+ * - `declined`: the plan names a tool the agent does not hold.
841
+ * - a refusal whose reason is the hand-over note, when the plan stopped
842
+ * in this very request.
843
+ */
844
+ private onScriptedPre;
845
+ /**
846
+ * The first tool the plan will call that the agent does not hold, or
847
+ * undefined when it holds them all — or did not say which it holds.
848
+ */
849
+ private missingHostTool;
850
+ /**
851
+ * The model's own call, in a session of another host: any `/tool/pre`
852
+ * without `scripted`.
853
+ *
854
+ * NEVER PINNED, and never answered with a bundle. In Claude Code a call to
855
+ * the plan's next tool is pinned, because there the model is the one who
856
+ * makes the plan's calls. Here the adapter makes them, so a call the model
857
+ * made is the model's whatever tool it names: answered with the plan's
858
+ * arguments, the tool would run with arguments the model never wrote, and
859
+ * the model would read a result that does not belong to the call it sees.
860
+ *
861
+ * A plan that is still live is handed over. The call runs as the model
862
+ * wrote it — the note is not given in its place — and the note goes out
863
+ * with this call's `/tool/post`. The recording of the model's own work
864
+ * after the hand-over (the fragment) opens with this very call: opened one
865
+ * call later, the call that left the plan would be in no recording.
866
+ */
867
+ private onOwnPre;
868
+ /**
869
+ * What the model wrote and reasoned before a call, as its host handed it
870
+ * over with the call — the values {@link intentForToolUse} and
871
+ * {@link thinkingForToolUse} read from a transcript, under the same switch:
872
+ * a source `BIR_INTENT_SOURCES` turned off is not taken from the payload
873
+ * either. `context` is the written text only; thinking rides apart.
874
+ */
875
+ private hostWords;
694
876
  /** This run's plan, when it is armed, still live, and announced to the model. */
695
877
  private livePlan;
696
878
  /** Whether a `run_scenario` pre for `state` is still waiting for its `/scenario/run`. */
@@ -888,6 +1070,22 @@ export declare class ControlServer {
888
1070
  */
889
1071
  private onAgent;
890
1072
  private onStop;
1073
+ /**
1074
+ * Keep what another host handed over with its stop — on the run, not on the
1075
+ * session: a later prompt of the session opens a new run, which must not
1076
+ * inherit an answer or a cost. Returns why the run is closed.
1077
+ *
1078
+ * - `final_answer`, `usage`: the answer and the tokens (see `sealRun`,
1079
+ * `hostCost`).
1080
+ * - `incomplete`: the recording is not the whole run — the adapter lost
1081
+ * calls, a person changed one, or one was answered in its tool's place.
1082
+ * The run is flagged lossy, so its finish carries the service's mark; the
1083
+ * mark has no place for a text, which goes to the log only.
1084
+ * - `failed`: the run failed. It is closed with that text as its error
1085
+ * and no final answer. An exception's text can hold a password or a
1086
+ * user's data: it is cleaned and cut like every text that is sent.
1087
+ */
1088
+ private noteHostStop;
891
1089
  /**
892
1090
  * `SessionEnd` — `/clear`, exit, logout. Answered at once: the host is
893
1091
  * waiting on it to exit, and this process outlives the session, so the run