@basein/runner 0.2.11 → 0.2.12

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.
@@ -176,7 +176,7 @@ Three additions to the v1 topology, and nothing else moves:
176
176
  |---|---|---|
177
177
  | `bir-hooks` control server | extended | Holds the `ScenarioReplayPlan` on `RunState`. Arms it, pins inputs, threads outputs, dispatches direct steps, redeems the ticket |
178
178
  | `bir-proxy` | extended | Long-polls for work; executes a `tools/call` on the upstream it already owns |
179
- | `bir-scenario` | **new**, ~120 lines | A stdio MCP server exposing one tool, `run_scenario`. The delivery channel for direct mode (§6) |
179
+ | `bir-scenario` | **new**, ~120 lines | A stdio MCP server exposing one tool, `run_scenario`. The delivery channel for direct mode (§6). It later gained the scenario tools of §16.5 |
180
180
 
181
181
  ---
182
182
 
@@ -815,7 +815,7 @@ If that is not enough for a deployment, the mitigation is a real one and is out
815
815
  evaluate the logic in a `node:vm` context with a frozen, minimal global. Filed as §20 question 3.
816
816
 
817
817
  **A hand-edited step is the same code, and runs the same way.** Since 2026-09-25 (the service's
818
- `editSteps.md`, not yet released) the owner — or Claude working for them — can change a step's
818
+ `editSteps.md`; built, not yet released) the owner — or Claude working for them — can change a step's
819
819
  logic with `bir scenario edit`. The service checks the change against the recording before it saves
820
820
  it, but that check is about *what the code computes*. Once saved, the step arrives in the same
821
821
  payload and runs here unattended, with `new Function`, next to the same credentials, exactly like
@@ -823,13 +823,39 @@ calculated code. The provenance argument still holds: only the owner can edit, o
823
823
  and anyone else — an admin included — gets `404`. What changes is that *"the model wrote it"* is no
824
824
  longer always true.
825
825
 
826
- The service now sandboxes its own evaluation of every logic body: one worker thread with an empty
827
- environment and a memory cap, a fresh `node:vm` context per evaluation with a time limit and nothing
828
- but JavaScript's own built-ins, values in and out as JSON text only. It had to: there, one account's
829
- code runs in the process that holds every account's data and the service's secrets. **The runner
830
- still does not sandbox.** Here the code runs on the owner's machine, for the owner's account. One
831
- consequence to know: code that uses `fetch`, `process` or a timer throws in the service's check, so
832
- it cannot be saved without `--force`; a forced step would run here with all of them.
826
+ The service sandboxes its own evaluation of every logic body (built with the same service change;
827
+ not yet released). It had to: there, one account's code runs next to every account's data and the
828
+ service's secrets. How it is built:
829
+
830
+ - **A fresh `node:vm` context for each evaluation**, with a time limit (`LOGIC_TIMEOUT_MS`). Only
831
+ JavaScript's own built-ins are in it, less `ArrayBuffer`, `SharedArrayBuffer`, `DataView`, the
832
+ typed arrays, `Atomics`, `WebAssembly` and `FinalizationRegistry`; `Symbol.for` and
833
+ `Symbol.keyFor` are removed too. No `require`, `process`, `fetch`, `Buffer` or timers. Values go
834
+ in and come out as JSON text only.
835
+ - **That context lives in a separate evaluator process**, not in the service: an empty
836
+ environment, `--permission` (no files, no child processes), `--disallow-code-generation-from-strings`,
837
+ `--frozen-intrinsics`, and a heap of `LOGIC_MEMORY_MB`.
838
+ - **A relay worker thread of the service drives that process.** No logic runs in the relay. It
839
+ kills the evaluator when a body runs too long, and the next job starts a new one; a body that
840
+ runs out of memory takes down only the evaluator, and is run once more on a fresh one before it
841
+ is blamed. The relay also replaces the evaluator when its heap stays above half of
842
+ `LOGIC_MEMORY_MB` after a job, and after 2,000 jobs. A body that overran its time or memory, or
843
+ took the evaluator down twice, is refused at once, with the same message, for the next 10
844
+ minutes.
845
+ - **The hand-edit check runs in a check worker thread of its own**, with its own relay and
846
+ evaluator process, under a sandbox-time budget
847
+ (`EDIT_CHECK_BUDGET_MS`, 5000 ms by default; past it, *too costly to check*), so a slow check
848
+ never holds up the service's other requests.
849
+
850
+ **The runner still does not sandbox.** Here the code runs on the owner's machine, for the owner's
851
+ account, with `new Function`. One consequence to know: **the service's check only judges what a
852
+ body returns for the recording's values.** Code that calls `fetch`, `process` or a timer on that
853
+ path, and does not catch the error, throws in the check, so it cannot be saved without `--force`.
854
+ But a call behind a `typeof` guard (`if (typeof fetch === "function") …`), or on a branch the
855
+ recording's values never take, is never reached in the check; one inside a `try` fails there
856
+ quietly. Such code passes, it is saved like any other change, and here it runs with `fetch`,
857
+ `process` and timers all present. The service's sandbox protects the service; it proves
858
+ nothing about what a body does on the runner. Read every hand edit before it is saved.
833
859
 
834
860
  ### 13.2 Replay bypasses permission prompts
835
861
 
@@ -901,7 +927,7 @@ src/
901
927
  │ ├─ source-run.ts # lazy GET /recordings/runs/:id → recorded outputs by tool (§8.1)
902
928
  │ └─ types.ts # SerializedScenario, SerializedScenarioStep
903
929
  ├─ bin/
904
- │ ├─ bir-scenario.ts ← new: the one-tool MCP server (§6.3)
930
+ │ ├─ bir-scenario.ts ← new: the `bir` MCP server — `run_scenario` (§6.3), later the scenario tools (§16.5)
905
931
  │ └─ bir.ts # + `--replay`, `bir scenario …`, `bir replay` (§16.5)
906
932
  ├─ control/
907
933
  │ ├─ server.ts # + arm / pin / thread / dispatch / report
@@ -1046,8 +1072,8 @@ and a session with no BaseIn credentials still replays (it just books nothing).
1046
1072
 
1047
1073
  ### 16.5 CLI surface
1048
1074
 
1049
- `bir` gains one flag and one command group. Everything here is a thin client over endpoints that
1050
- already exist server-side, except `bir replay`, which drives §16.2's executor directly.
1075
+ `bir` gains one flag and one command group. Everything here is a thin client over service
1076
+ endpoints, except `bir replay`, which drives §16.2's executor directly.
1051
1077
 
1052
1078
  ```
1053
1079
  bir install --replay register the `bir` MCP server; raise the prompt-hook timeout to 15 s
@@ -1056,43 +1082,77 @@ bir uninstall --replay reverse exactly that, leaving the rest of the inst
1056
1082
  bir scenario list GET /recordings/runs runs, iterations, scenario state
1057
1083
  bir scenario show <runId> GET /recordings/runs/:id/scenario intent, params, steps, logic
1058
1084
  bir scenario show <scnId> GET /scenarios/:id the same, by scenario id (segments too)
1059
- [--step <n>: one step]
1085
+ [--step <n>: one step, with scenarioId and chainRevision first]
1060
1086
  bir scenario calc <runId> POST /recordings/runs/:id/calculate [--force to re-derive in place]
1061
1087
  [--force --discard-edits: re-derive a plan with hand edits]
1088
+ bir scenario calc <scnId> --force
1089
+ POST /scenarios/:id/recalculate recalculate in place (segments too)
1090
+ [--discard-edits: sends {discardEdits: true}]
1062
1091
  bir scenario replay <scnId> --prompt "…" [--dry]
1063
1092
 
1064
1093
  bir scenario check <id> --step <n> POST /scenarios/:id/steps/:n/check try a change; writes nothing
1065
1094
  bir scenario edit <id> --step <n> PATCH /scenarios/:id/steps/:n the same check, then save
1066
1095
  bir scenario edits <id> GET /scenarios/:id/edits the history, newest first
1067
- bir scenario undo <id> [--edit <e>] POST /scenarios/:id/edits/:e/revert put one step back
1096
+ bir scenario undo <id> [--edit <e> | --step <n>]
1097
+ POST /scenarios/:id/edits/:e/revert take back the newest edit (of step n)
1068
1098
  bir scenario editing on|off|status (no route) ~/.baseinstrunner/installed.json which `bir` MCP tools are offered
1069
1099
 
1070
1100
  bir replay --scenario <scnId> --prompt "…" [--dry] alias; the Tier 2 / debugging path (§12.1)
1071
1101
  ```
1072
1102
 
1073
- The second group, and `show <scnId>`, `--step` and `--discard-edits`, were added on 2026-09-25 by
1074
- the service's `editSteps.md` and are not yet released; their routes are new on the service. They
1075
- are the one place `bir` changes a scenario, so the rules are strict:
1103
+ The second group, and `show <scnId>`, `--step`, `calc <scnId>` and `--discard-edits`, were added
1104
+ on 2026-09-25 by the service's `editSteps.md`. They are built and tested, and not yet released in
1105
+ the form described here (version 0.2.11 on npm has an early form): they need the next
1106
+ `@basein/runner` version and a service deploy, because their routes are new on the service. They
1107
+ are where `bir` changes a scenario by hand, so the rules are strict:
1076
1108
 
1077
1109
  - **Owner only.** Anyone else, an admin included, gets `404`, never `403`.
1078
1110
  - **A change is saved only if it reproduces the recording and is not a copy**, or on purpose with
1079
- `--force --note "<why>"`. `check` and `edit` run the same check; `check` never writes.
1111
+ `--force --note "<why>"`. `check` and `edit` run the same check; `check` never writes. When the
1112
+ check had any problem, a forced save is recorded as forced, and `bir investigate` keeps saying so.
1080
1113
  - `<id>` is a `run_` id, resolved to its whole-run scenario with `GET /recordings/runs/:id/scenario`,
1081
- or a `scn_` id, used as it is. Logic goes in with `--input-logic` / `--output-logic` from a file,
1082
- or `-` for stdin — never inline, because shell quoting mangles JavaScript. `edit` also takes
1083
- `--freeze`, `--unfreeze`, `--note` and `--revision <n>` (the `expectedRevision` of the body).
1084
- - `undo` without `--edit` undoes the newest edit that can be undone. Every save and every undo bumps
1085
- the scenario's `chainRevision`.
1114
+ or a `scn_` id, used as it is. A sub-task scenario has only its `scn_` id; for a run with only
1115
+ sub-task scenarios, `bir` lists their `scn_` ids. Logic goes in with `--input-logic` /
1116
+ `--output-logic` from a file, or `-` for stdin — never inline, because shell quoting mangles
1117
+ JavaScript. `edit` also takes `--freeze`, `--unfreeze`, `--note` and `--revision <n>` (the
1118
+ `expectedRevision` of the body).
1119
+ - `show --step <n>` prints the step object with `scenarioId` and `chainRevision` first. The revision
1120
+ is the scenario's, and is what `--revision` takes.
1121
+ - **`undo` walks back.** Without `--edit` it takes back the newest edit (never an undo entry) that
1122
+ can be undone; with `--step <n>`, the newest edit of step `n`. Run again, it goes one further
1123
+ back. `--edit` and `--step` cannot be given together. To redo, undo the undo entry by its
1124
+ `sedit_` id; the undo prints that command. The service checks an undo like a save, for the later
1125
+ steps and the final answer (so only when it changes output logic), and answers
1126
+ `422 undo_refused` when it would break them, unless `--force --note "<why>"` is given. Every
1127
+ save and every undo bumps the scenario's `chainRevision`.
1128
+ - A save lifts a mark made by hand (`--freeze`, `by_hand`) only with `--unfreeze`; undoing the
1129
+ freeze, or `calc --force --discard-edits`, also removes it. A forced save lifts only a mark the
1130
+ calculation set.
1086
1131
  - `calc --force` answers `409 scenario_has_edits` on a plan with hand edits, unless
1087
- `--discard-edits` is given.
1132
+ `--discard-edits` is given. `calc <scnId>` without `--force` is a usage error: nothing is sent.
1133
+ - Answers `bir` puts in words: `400 nothing_to_change` (the code and mark asked for are the step's
1134
+ already), `413 payload_too_large`, `503 logic_sandbox_unavailable` (nothing was checked, saved or
1135
+ undone), and a check report with `noChange: true` (`check` then says nothing would change, and
1136
+ exits `0`).
1088
1137
  - Exit codes: `0` checked OK, saved or undone; `1` refused, not found, or a service error; `2`
1089
1138
  usage. `--json` prints the service's body unchanged.
1090
1139
 
1091
- The same commands are tools on the `bir` MCP server. `scenario_show`, `scenario_edits` and
1092
- `investigate` only read, and are always offered. `scenario_check`, `scenario_edit` and
1093
- `scenario_undo` are offered only in a project where `bir scenario editing on` was run, because that
1094
- server runs in every session of every installed project. Each tool runs the installed `bir … --json`;
1095
- a refusal comes back as an MCP error result carrying the report. The operator's walk-through is
1140
+ The same commands are tools on the `bir` MCP server. `scenario_show` (`target`, `step`),
1141
+ `scenario_edits` (`target`) and `investigate` (`id`) only read, and are always offered.
1142
+ `scenario_check` (`target`, `step`, `inputLogic`, `outputLogic`, `unfreeze`), `scenario_edit` (the
1143
+ same, plus `freeze`, `force`, `note`, `revision`) and `scenario_undo` (`target`, `edit` or `step`,
1144
+ `force`, `note`) are offered only in a project where `bir scenario editing on` was run, because that
1145
+ server runs in every session of every installed project. Each tool runs this package's own
1146
+ `bir … --json`, with logic bodies passed through temporary files; a refusal comes back as an MCP
1147
+ error result carrying the report. A call the host cancels, or leaves behind when it exits, has its
1148
+ `bir` stopped and its files removed.
1149
+
1150
+ **`bir`'s own calls are never recorded** (the service's `editSteps.md` D13; not yet released). The
1151
+ `bir` server's tools other than `run_scenario`, and a `Bash` or `PowerShell` command made only of
1152
+ `bir` invocations (and a plain `cd`), are host housekeeping, like `ToolSearch`
1153
+ ([housekeeping.ts](../src/record/housekeeping.ts)). They are never recorded as steps, and never
1154
+ count as divergence from a plan. A session that fixes a plan must not become a plan that edits
1155
+ plans when it is replayed. The operator's walk-through is
1096
1156
  [calculatedReplayGuide.md](calculatedReplayGuide.md) §9.2.
1097
1157
 
1098
1158
  `--dry` maps to BaseIn's `POST /scenarios/:id/replay`, which evaluates the stored logic against the