@basein/runner 0.3.21 → 0.3.22

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 (48) hide show
  1. package/dist/bin/bir-hooks.d.ts +7 -16
  2. package/dist/bin/bir-hooks.js +7 -16
  3. package/dist/bin/bir-scenario.js +1 -1
  4. package/dist/bin/bir.js +1 -21
  5. package/dist/bin/investigate.d.ts +0 -9
  6. package/dist/bin/investigate.js +14 -42
  7. package/dist/bin/scenario-edit.js +13 -44
  8. package/dist/control/recorder-main.d.ts +0 -15
  9. package/dist/control/recorder-main.js +7 -39
  10. package/dist/control/server.d.ts +24 -137
  11. package/dist/control/server.js +101 -995
  12. package/dist/record/recorder.d.ts +11 -142
  13. package/dist/record/recorder.js +0 -3
  14. package/dist/record/remote-recorder.d.ts +1 -9
  15. package/dist/record/remote-recorder.js +3 -49
  16. package/dist/record/switchable-recorder.js +0 -2
  17. package/dist/replay/bundle.d.ts +2 -2
  18. package/dist/replay/bundle.js +3 -3
  19. package/dist/replay/controller.d.ts +19 -123
  20. package/dist/replay/controller.js +151 -518
  21. package/dist/replay/derive.d.ts +0 -10
  22. package/dist/replay/derive.js +0 -3
  23. package/dist/replay/handover.d.ts +2 -37
  24. package/dist/replay/handover.js +38 -106
  25. package/dist/replay/logic.d.ts +0 -11
  26. package/dist/replay/logic.js +0 -24
  27. package/dist/replay/plan.d.ts +43 -163
  28. package/dist/replay/plan.js +144 -556
  29. package/dist/replay/safety.d.ts +4 -5
  30. package/dist/replay/safety.js +7 -7
  31. package/dist/replay/source-run.d.ts +15 -24
  32. package/dist/replay/source-run.js +25 -58
  33. package/dist/replay/targets.d.ts +11 -21
  34. package/dist/replay/targets.js +30 -69
  35. package/dist/replay/types.d.ts +9 -96
  36. package/dist/replay/types.js +3 -16
  37. package/dist/util/service-headers.d.ts +1 -2
  38. package/dist/util/service-headers.js +1 -2
  39. package/docs/calculatedReplay.md +17 -74
  40. package/docs/calculatedReplayGuide.md +9 -30
  41. package/package.json +1 -1
  42. package/production.md +5 -35
  43. package/dist/replay/condition-words.d.ts +0 -28
  44. package/dist/replay/condition-words.js +0 -59
  45. package/dist/replay/flatten.d.ts +0 -161
  46. package/dist/replay/flatten.js +0 -222
  47. package/dist/replay/reads-only.d.ts +0 -29
  48. package/dist/replay/reads-only.js +0 -261
@@ -53,16 +53,6 @@ export interface DeriveContext {
53
53
  * into the session only after the prompt hook (issue 1212).
54
54
  */
55
55
  agentDay?: () => AgentDay | undefined;
56
- /**
57
- * A reading for a scenario that can follow a plan's decision, made while the
58
- * agent decides (the service's offer1.md O4): the plan, and its decision
59
- * step. With it the service reads this turn's results without a live call,
60
- * and the prompt as the user's request. Only the service reads it.
61
- */
62
- atDecision?: {
63
- scenarioId: string;
64
- stepIndex: number;
65
- };
66
56
  }
67
57
  /**
68
58
  * The service, which derives when this runner has no key of its own
@@ -347,9 +347,6 @@ async function deriveViaService(opts, service, schema, keys) {
347
347
  // 2023-11-30 …"), which the service turns into the day it reads from
348
348
  // (issue 1212). Older services ignore it.
349
349
  ...(opts.todaySaid ? { todaySaid: opts.todaySaid } : {}),
350
- // A reading made at a plan's decision (offer1.md O4): the service then
351
- // takes this turn's results with no live call. Older services ignore it.
352
- ...(opts.atDecision ? { atDecision: opts.atDecision } : {}),
353
350
  }),
354
351
  signal: opts.signal ?? timeoutSignal(),
355
352
  });
@@ -63,13 +63,7 @@ export type HandoverStopReason =
63
63
  * neither true nor false: nothing says whether this request needs the
64
64
  * step, and nothing is guessed. The step did not run.
65
65
  */
66
- | "test_failed"
67
- /**
68
- * A sub-task's last step was left out by its test (AC5), and the sub-task
69
- * could not hand its results back to its caller: the body that maps them
70
- * threw. Its steps that ran did their work; the one left out did not run.
71
- */
72
- | "result_map";
66
+ | "test_failed";
73
67
  export interface HandoverNoteInput {
74
68
  intent: string;
75
69
  kind: FallbackKind;
@@ -92,8 +86,7 @@ export interface HandoverNoteInput {
92
86
  ranSteps?: number[];
93
87
  /**
94
88
  * The steps before the stop that their tests left out, 1-based and
95
- * inclusive (a sub-task left out whole spans several): named, so the model
96
- * neither thinks they ran nor runs them.
89
+ * inclusive: named, so the model neither thinks they ran nor runs them.
97
90
  */
98
91
  leftOut?: Array<{
99
92
  from: number;
@@ -101,8 +94,6 @@ export interface HandoverNoteInput {
101
94
  toolName: string;
102
95
  text: string | null;
103
96
  }>;
104
- /** For `result_map`: the sub-task whose results could not go back. */
105
- subTask?: string | null;
106
97
  /**
107
98
  * Of {@link executed}, how many are the model's own tool calls above — the
108
99
  * steps a steered session ran before a divergence took over. Only read when
@@ -132,37 +123,11 @@ export interface HandoverNoteInput {
132
123
  remainingTools: string[];
133
124
  params?: Readonly<Record<string, unknown>>;
134
125
  delivery: HandoverDelivery;
135
- /**
136
- * The plan stopped in front of a *call* it cannot run (segmented.md
137
- * R-CALL-29, R-CALL-36). The agent is told what that stretch of work was and
138
- * why it is not running, so it does that part itself rather than wondering
139
- * what is missing.
140
- */
141
- unusable?: {
142
- reason: string;
143
- callIntent?: string | null;
144
- segmentId?: string | null;
145
- ownerScenarioId?: string | null;
146
- };
147
- /**
148
- * The scenario that owns the stopped step, when it is not the one that was
149
- * matched — a parked step inside a called segment (R-CALL-30).
150
- */
151
- ownerScenarioId?: string | null;
152
- /** The sub-task the stopped step belongs to, when it is inside one. */
153
- inSubTask?: string | null;
154
126
  /**
155
127
  * For `other_branch`: the stopped step's test in words — what the steps
156
128
  * from there on are for, which this request is not (autocorrect.md AC5).
157
129
  */
158
130
  whenText?: string | null;
159
- /**
160
- * For `decision`: the live request the plan serves, the user's own words.
161
- * Its sentences with a condition word are quoted, so the agent sees the
162
- * choice that is its to make (the service's segmentsbreakdown.md SB-18).
163
- * Read for no other kind.
164
- */
165
- request?: string | null;
166
131
  }
167
132
  export declare function buildHandoverNote(n: HandoverNoteInput): string;
168
133
  /** "Step 2", "Steps 1–3", "Steps 1, 3 and 5–6": 1-based numbers in order, runs joined. */
@@ -11,24 +11,11 @@
11
11
  * and a tool's own payload is already in front of it where it ran.
12
12
  */
13
13
  import { truncate } from "./bundle.js";
14
- import { conditionSentences } from "./condition-words.js";
15
14
  /** Hard ceiling on a note. Long enough to carry derived params, short enough to read. */
16
15
  export const MAX_HANDOVER_NOTE = 4_000;
17
16
  const MAX_ERROR_CHARS = 500;
18
17
  const MAX_PARAMS_CHARS = 1_500;
19
- /** What the note says about each reason a call cannot run (R-CALL-36). */
20
- const UNUSABLE_LINE = {
21
- switched_off: "whose recorded scenario is switched off",
22
- not_ready: "whose recorded scenario is not ready",
23
- missing: "whose recorded scenario was deleted",
24
- stale: "whose recorded scenario is out of date",
25
- unresolved: "with no matching recorded scenario right now",
26
- depth: "whose recorded scenario is nested too deep to run here",
27
- unsupported: "this runner cannot run",
28
- };
29
18
  export function buildHandoverNote(n) {
30
- if (n.kind === "decision")
31
- return decisionNote(n);
32
19
  const intent = n.intent.length > 240 ? `${n.intent.slice(0, 240)}…` : n.intent;
33
20
  const human = n.stepIndex + 1;
34
21
  const tool = n.stepToolName ? ` (${n.stepToolName})` : "";
@@ -38,91 +25,6 @@ export function buildHandoverNote(n) {
38
25
  ];
39
26
  if (intent)
40
27
  lines.push(`Intent: ${intent}`);
41
- lines.push(...ranLines(n));
42
- if (n.kind === "unusable_call") {
43
- // A call is a stretch of work, not a tool: the agent is told what the
44
- // sub-task was, not which tool did not run.
45
- const what = n.unusable?.callIntent ? ` ("${truncate(n.unusable.callIntent, 200)}")` : "";
46
- const why = UNUSABLE_LINE[n.unusable?.reason ?? ""] ?? "that cannot run here";
47
- lines.push(`Step ${human} of ${n.totalSteps} is a sub-task${what} ${why}.`);
48
- }
49
- else if (n.kind === "known_bad_step") {
50
- const times = n.failureCount ? ` ${n.failureCount} times` : "";
51
- // A parked step inside a called segment names the scenario that owns it:
52
- // that is where the repair happens, and the agent's own step numbering has
53
- // no row for it (R-CALL-30).
54
- const owner = n.inSubTask
55
- ? `, in sub-task "${truncate(n.inSubTask, 120)}"${n.ownerScenarioId ? `, scenario ${n.ownerScenarioId}` : ""}`
56
- : "";
57
- lines.push(`Step ${human} of ${n.totalSteps}${tool.replace(/\)$/, `${owner})`)} did not run: it has failed${times} before, so the scenario stops in front of it.`);
58
- }
59
- else if (n.kind === "nondeterministic_step") {
60
- // The calculation could not write code for this step's input: it is a
61
- // judgement, and the agent is the one to make it (plan-services.md D8).
62
- lines.push(`Step ${human} of ${n.totalSteps}${tool} did not run: its input needs a judgement the scenario cannot compute. Make that choice yourself and carry on.`);
63
- }
64
- else if (n.kind === "other_branch") {
65
- // The plan holds one half of an "if … otherwise" and this request is the
66
- // other (autocorrect.md AC5). The steps from here are not wrong, they are
67
- // not this request's: the model is told what they are for, never to run
68
- // them, and the list of their tools is left out below for that reason.
69
- const why = n.whenText ? `they are only for when ${n.whenText}, and that is not so for this request` : "this request is the other case of the task, which they are not for";
70
- lines.push(n.stepIndex >= n.totalSteps
71
- ? "None of the scenario's steps was needed for this request. Do the task as this request needs it."
72
- : `Step ${human} of ${n.totalSteps}${tool} and the steps after it did not run: ${why}. Do the rest of the task yourself, as this request needs it.`);
73
- }
74
- else {
75
- lines.push(stepFailedLine(n, human, tool));
76
- }
77
- // Not for the other half: those tools are the ones this request must not
78
- // run, and naming them invites exactly that.
79
- if (n.remainingTools.length > 0 && n.kind !== "other_branch") {
80
- lines.push(`The remaining planned steps were: ${n.remainingTools.join(", ")}.`);
81
- }
82
- if (n.params && Object.keys(n.params).length > 0) {
83
- let json;
84
- try {
85
- json = JSON.stringify(n.params);
86
- }
87
- catch {
88
- json = "[unserializable]";
89
- }
90
- lines.push(`Parameters derived for this request: ${truncate(json, MAX_PARAMS_CHARS)}`);
91
- }
92
- return truncate(lines.join("\n"), MAX_HANDOVER_NOTE);
93
- }
94
- /**
95
- * The note of a plan stopped at its decision (the service's
96
- * segmentsbreakdown.md SB-18): what ran, and the request's own words for the
97
- * choice the agent now makes. Nothing that names the recorded half: no
98
- * intent, no remaining steps, no parameters, and never the condition the
99
- * service keeps (`stop.why`). Each would hint which way the recording went.
100
- */
101
- function decisionNote(n) {
102
- // Where the results of the steps that ran are, as the ran-steps line says.
103
- const above = Math.max(0, Math.min(n.executedAbove ?? 0, n.executed));
104
- const delivery = above >= n.executed ? "above" : n.delivery;
105
- const from = n.executed === 0
106
- ? ""
107
- : delivery === "above"
108
- ? " from the results above"
109
- : delivery === "below"
110
- ? above > 0
111
- ? " from the results above and below"
112
- : " from the results below"
113
- : "";
114
- const quote = conditionSentences(n.request);
115
- const ask = quote ? `This request asks for a decision: “${quote}”.` : "This task has a decision to make here.";
116
- const lines = [
117
- "[BaseInstRunner calculated replay] The calculated scenario for this request ran the steps before a decision, and stopped there.",
118
- ...ranLines(n),
119
- `${ask} Decide${from} which way applies, then continue the task, calling tools as needed.`,
120
- ];
121
- return truncate(lines.join("\n"), MAX_HANDOVER_NOTE);
122
- }
123
- /** What ran before the stop, and what of it the model may assume: the lines every note carries. */
124
- function ranLines(n) {
125
- const lines = [];
126
28
  // Steps left out by their tests sit among those before the stop (AC5): the
127
29
  // ones that ran are then named, never "steps 1–N", which would say a step
128
30
  // ran that did not.
@@ -188,7 +90,44 @@ function ranLines(n) {
188
90
  lines.push(`Step ${n.inFlight.stepIndex + 1} of ${n.totalSteps}${inTool} was started and has not reported back: ` +
189
91
  "it may not have run, or may still complete. Check whether its work happened before you repeat it.");
190
92
  }
191
- return lines;
93
+ if (n.kind === "known_bad_step") {
94
+ const times = n.failureCount ? ` ${n.failureCount} times` : "";
95
+ lines.push(`Step ${human} of ${n.totalSteps}${tool} did not run: it has failed${times} before, so the scenario stops in front of it.`);
96
+ }
97
+ else if (n.kind === "nondeterministic_step") {
98
+ // The calculation could not write code for this step's input: it is a
99
+ // judgement, and the agent is the one to make it (plan-services.md D8).
100
+ lines.push(`Step ${human} of ${n.totalSteps}${tool} did not run: its input needs a judgement the scenario cannot compute. Make that choice yourself and carry on.`);
101
+ }
102
+ else if (n.kind === "other_branch") {
103
+ // The plan holds one half of an "if … otherwise" and this request is the
104
+ // other (autocorrect.md AC5). The steps from here are not wrong, they are
105
+ // not this request's: the model is told what they are for, never to run
106
+ // them, and the list of their tools is left out below for that reason.
107
+ const why = n.whenText ? `they are only for when ${n.whenText}, and that is not so for this request` : "this request is the other case of the task, which they are not for";
108
+ lines.push(n.stepIndex >= n.totalSteps
109
+ ? "None of the scenario's steps was needed for this request. Do the task as this request needs it."
110
+ : `Step ${human} of ${n.totalSteps}${tool} and the steps after it did not run: ${why}. Do the rest of the task yourself, as this request needs it.`);
111
+ }
112
+ else {
113
+ lines.push(stepFailedLine(n, human, tool));
114
+ }
115
+ // Not for the other half: those tools are the ones this request must not
116
+ // run, and naming them invites exactly that.
117
+ if (n.remainingTools.length > 0 && n.kind !== "other_branch") {
118
+ lines.push(`The remaining planned steps were: ${n.remainingTools.join(", ")}.`);
119
+ }
120
+ if (n.params && Object.keys(n.params).length > 0) {
121
+ let json;
122
+ try {
123
+ json = JSON.stringify(n.params);
124
+ }
125
+ catch {
126
+ json = "[unserializable]";
127
+ }
128
+ lines.push(`Parameters derived for this request: ${truncate(json, MAX_PARAMS_CHARS)}`);
129
+ }
130
+ return truncate(lines.join("\n"), MAX_HANDOVER_NOTE);
192
131
  }
193
132
  /** "Step 2", "Steps 1–3", "Steps 1, 3 and 5–6": 1-based numbers in order, runs joined. */
194
133
  export function stepsPhrase(numbers) {
@@ -240,13 +179,6 @@ function stepFailedLine(n, human, tool) {
240
179
  `could not read its output${detail ? ` (${detail})` : ""}, so it stops there. Do not run it again.`);
241
180
  }
242
181
  switch (n.stopReason) {
243
- case "result_map": {
244
- // Nothing of the stopped position ran: its sub-task's last step was left
245
- // out, and what failed is handing the sub-task's results on.
246
- const what = n.subTask ? ` "${truncate(n.subTask, 120)}"` : "";
247
- return (`The sub-task${what} could not hand back its results${detail ? ` (${detail})` : ""}, ` +
248
- "so the scenario stops after it. Do the rest of the task yourself.");
249
- }
250
182
  case "test_failed":
251
183
  // The test could not answer, so whether the step is needed is open: the
252
184
  // model decides, as it would without the scenario.
@@ -88,15 +88,4 @@ export declare function evalToolInputLogic(code: string, parameters: Record<stri
88
88
  export declare function evalToolOutputLogic(code: string, toolOutput: string, parameters: Record<string, unknown>, intent: string, respParams?: Record<string, unknown>): Record<string, unknown>;
89
89
  /** Build the final response model from everything the steps accumulated. */
90
90
  export declare function evalResponseParamsLogic(code: string, respParams: Record<string, unknown>, parameters: Record<string, unknown>, intent: string): Record<string, unknown>;
91
- /**
92
- * The two mapping bodies of a call (segmented.md R-CALL-20).
93
- *
94
- * `paramMapLogic` builds the called segment's parameters from the caller's
95
- * state; `resultMapLogic` turns what the segment emitted into the keys the
96
- * caller's later steps read. Both belong to the *(call, segment) pair* rather
97
- * than to either side alone, which is why two chains calling one segment carry
98
- * two of them.
99
- */
100
- export declare function evalParamMapLogic(code: string, parameters: Record<string, unknown>, intent: string, respParams?: Record<string, unknown>): Record<string, unknown>;
101
- export declare function evalResultMapLogic(code: string, out: Record<string, unknown>, parameters: Record<string, unknown>, intent: string, respParams?: Record<string, unknown>): Record<string, unknown>;
102
91
  //# sourceMappingURL=logic.d.ts.map
@@ -177,28 +177,4 @@ export function evalResponseParamsLogic(code, respParams, parameters, intent) {
177
177
  intent,
178
178
  ]), "responseParamsLogic");
179
179
  }
180
- /**
181
- * The two mapping bodies of a call (segmented.md R-CALL-20).
182
- *
183
- * `paramMapLogic` builds the called segment's parameters from the caller's
184
- * state; `resultMapLogic` turns what the segment emitted into the keys the
185
- * caller's later steps read. Both belong to the *(call, segment) pair* rather
186
- * than to either side alone, which is why two chains calling one segment carry
187
- * two of them.
188
- */
189
- export function evalParamMapLogic(code, parameters, intent, respParams = {}) {
190
- return assertPlainObject(run("paramMapLogic", ["parameters", "intent", "respParams"], code, [
191
- parameters,
192
- intent,
193
- respParams,
194
- ]), "paramMapLogic");
195
- }
196
- export function evalResultMapLogic(code, out, parameters, intent, respParams = {}) {
197
- return assertPlainObject(run("resultMapLogic", ["out", "parameters", "intent", "respParams"], code, [
198
- out,
199
- parameters,
200
- intent,
201
- respParams,
202
- ]), "resultMapLogic");
203
- }
204
180
  //# sourceMappingURL=logic.js.map