bareloop 0.32.0 → 0.34.0
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/CHANGELOG.md +95 -0
- package/README.md +4 -1
- package/bareloop.context.md +163 -82
- package/package.json +1 -1
- package/src/authorflow.js +5 -3
- package/src/authorjob.js +3 -1
- package/src/authorrun.js +36 -8
- package/src/authorscout.js +10 -6
- package/src/bundlerun.js +266 -0
- package/src/cli.js +24 -352
- package/src/interviewrun.js +6 -4
- package/src/invoke.js +22 -0
- package/src/job.js +2 -2
- package/src/judged.js +27 -5
- package/src/kinds.js +1 -0
- package/src/ledger.js +3 -4
- package/src/monthly.js +112 -35
- package/src/panel/authorroutes.js +12 -2
- package/src/panel/authorsession.js +24 -4
- package/src/panel/server.js +18 -13
- package/src/planrun.js +10 -8
- package/src/providerrows.js +87 -1
- package/src/providers.js +4 -4
- package/src/run.js +7 -2
- package/src/runlist.js +94 -7
- package/src/source.js +8 -3
- package/src/u-watchdog.mjs +296 -0
- package/src/userrun.js +207 -65
- package/types/authorflow.d.ts +8 -2
- package/types/authorjob.d.ts +6 -1
- package/types/authorscout.d.ts +11 -2
- package/types/bundlerun.d.ts +26 -0
- package/types/invoke.d.ts +8 -0
- package/types/judged.d.ts +10 -3
- package/types/monthly.d.ts +57 -10
- package/types/panel/authorroutes.d.ts +15 -0
- package/types/planrun.d.ts +13 -1
- package/types/providerrows.d.ts +66 -0
- package/types/providers.d.ts +1 -2
- package/types/run.d.ts +14 -1
- package/types/runlist.d.ts +79 -3
- package/types/source.d.ts +8 -3
- package/types/userrun.d.ts +49 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,101 @@ All notable changes to bareloop are documented here. Format:
|
|
|
5
5
|
[SemVer](https://semver.org/spec/v2.0.0.html). Pre-1.0: **minor** = a ladder rung or
|
|
6
6
|
feature lands, **patch** = docs, fixes, scaffolding.
|
|
7
7
|
|
|
8
|
+
## [0.34.0] - 2026-10-01
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Judge rounds now carry `rateSource` (`'caller'` under your price, else the guess label the worker rounds carry), so a customer-priced judge round counts as vouched, not other, in the provenance readout and the monthly counts. Dollar totals are unchanged. Evidence: live run `mup3h70u` (136 worker rounds `caller`, 14 judge rounds without it).
|
|
13
|
+
- The monthly-limit refusal now names the money a run in progress holds: `Max $4.00 (monthly limit, $5.95 held by a run in progress)` (`runs` when several); with nothing held the text is unchanged. `monthSpend` / `checkMonthlyRoom` also return `heldUsd` and `heldRuns`.
|
|
14
|
+
- The interview's "Run it now?" now starts the packaged door (`bin/bareloop.mjs author`, same flags) instead of `scripts/run-author.mjs`, which the published package does not ship.
|
|
15
|
+
- One "is this run alive" rule (`runIsAlive`, `src/runlist.js`) now drives both the panel's [▶] / [?] glyph and the monthly limit's hold: the run row's pid first (`isLiveRunner`), the spine-mtime rule only for a row with no pid. A run whose pid is alive but whose spine is quiet for 10+ minutes now reads running, not died; a dead pid with a fresh spine reads died. A `job-end` on the spine still wins.
|
|
16
|
+
- `--resume` now also refuses when the predecessor run's own run-list row carries the pid of a live bareloop runner, not only when a watchdog kill record names it (same refusal text).
|
|
17
|
+
- Every panel GET/HEAD route now refuses (`403`) a request whose `Host` is not the panel's own `127.0.0.1:<port>` (one shared `checkHostGuard`, also used by the write routes' guard), so a page on another origin cannot read the run list through a rebound DNS name. No token is needed to read.
|
|
18
|
+
- The monthly-limit check (and so every run start) no longer parses the spine of every run ever listed: it reads only runs listed in the current local month, plus any claim still held by a live runner. The Money tab's all-time figures still read everything.
|
|
19
|
+
- A key row priced 0 / 0 is still accepted, but the `price yours:` readout (run preview and run tail) now ends with a warning that the price is $0 so spend and limits read $0.
|
|
20
|
+
- A run that stops at $0 on a SOURCE-MANIFEST-RED, DESTINATION-RED or CLOSE-TIMING-RED now releases its monthly claim (the same `released` entry as a run that never started), so it leaves no ghost row and the month no longer reads "at least".
|
|
21
|
+
- A spec whose `budgetUsd` is not a finite number (e.g. `1e999`) is refused by `validateJob` at $0, and the monthly-limit check (`checkMonthlyRoom` / `claimRun`) no longer reads a non-finite cap as $0 — it never fits.
|
|
22
|
+
- A bundle money halt that used the whole signed ceiling now says no room is left under the signed ceiling (and prints no resume line) instead of a `--budget <more than X, at most X>` placeholder nothing can satisfy.
|
|
23
|
+
|
|
24
|
+
## [0.33.0] — 2026-09-30
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- **Your own price, per key row.** `config.json` `keys.<ENV NAME>.priceInPerM` / `priceOutPerM` (USD per
|
|
29
|
+
1M tokens, in and out; both or neither, each a finite number 0 or more) set what the model behind a key
|
|
30
|
+
costs. `ratesFor(provider, baseUrl, rows, model)` (`src/providerrows.js`) is the one lookup, on the same
|
|
31
|
+
row `keyNameFor` picks the key from. The run doors (`run-u` / `run <bundle>` / the panel via
|
|
32
|
+
`src/userrun.js`, `run-author`, the panel's authoring session) resolve it once at $0 and hand it to every
|
|
33
|
+
model call as bare-agent's `Loop({ rates })`: worker, scout and planner rounds, the drafter and confirm
|
|
34
|
+
turn, and the judge (priced at the judge model's own row; falls back to the worker's price when the model
|
|
35
|
+
names no row on that endpoint — `judgeRatesFor`). Priced rounds read `rateSource:'caller'`. A bad
|
|
36
|
+
price (one field, negative, non-number) refuses the run at $0 with "Nothing spent." and never falls back
|
|
37
|
+
to the guess. No price = no `rates` key = the built-in guess, unchanged. The run preview and run tail print
|
|
38
|
+
`price yours: in $0.006 / out $1.20 per 1M tokens (DEEPSEEK_API_KEY row)` when a price is set. New:
|
|
39
|
+
`runJob` / `runPlan` options `rates` / `judgeRates`; `runAuthorScout`, `authorCloseForJob`,
|
|
40
|
+
`makeLoopGenerate` and `defaultJudgeLoop` take an optional `rates`. The Settings screen field is a later
|
|
41
|
+
UI part; until then edit `config.json`. Past runs are not re-priced. Not covered: native (CLI) sessions
|
|
42
|
+
(they report their own cost) and the reuse engine (`runReuse`), which resolves no key row.
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
|
|
46
|
+
- **Monthly limit: a run claims its cap at start and holds it until it is done.** The run-start seam
|
|
47
|
+
writes the run's row FIRST (now carrying `pid` and `capUsd`; no lock file — records only), then reads
|
|
48
|
+
`runs.jsonl`: only the claims above its own row count, so whoever runs first holds. A run whose pid is
|
|
49
|
+
a live bareloop runner stays held at its full cap however quiet its spine (no more 10-minute silence
|
|
50
|
+
drop); a dead run's claim is closed by whoever finds it, with an attributed `settled` note
|
|
51
|
+
(`process gone`, first note wins); a run that does not fit appends a `released` entry and refuses
|
|
52
|
+
with the exact `Max $X (monthly limit)` text. A finished run appends its own `settled` entry. Old
|
|
53
|
+
rows without a `pid` keep the spine-mtime rule. The `resumingSpine` special case is gone (a resumed
|
|
54
|
+
leg's dead process already counts at its floor). `--resume`'s "is the old process alive" check now
|
|
55
|
+
recognises `bareloop run-u` / `bareloop run` and shares `isLiveRunner`.
|
|
56
|
+
`isLiveRunner` now needs a node executable (or a runner-named argv[0]) plus a runner-named argument, so a
|
|
57
|
+
recycled pid running e.g. `nvim` on a bareloop path no longer holds a dead run's cap; a claimed run (or, with no monthly limit, a listed one) that
|
|
58
|
+
exits at $0 before its spine exists is released (`reason: 'not started'`) instead of leaving a ghost row.
|
|
59
|
+
- **One runner: `bareloop run <bundle>` is now a thin door to the `run-u` engine.** The exported
|
|
60
|
+
bundle CLI no longer carries its own `runJob` caller (`doRun`) and provider wiring
|
|
61
|
+
(`buildProviders`); `src/bundlerun.js` keeps only the bundle checks (integrity, deps, envelope,
|
|
62
|
+
blessing before any key read), the tightened spec, the fresh worktree, the history row, the
|
|
63
|
+
first-green blessing and the merge hints. Consequences: a bundle can name any provider whose key
|
|
64
|
+
is in your keys file or env (the ANTHROPIC-only refusal is gone); the **monthly $ limit now
|
|
65
|
+
applies to a bundle** run (exit 2, nothing spent, no worktree); a missing key is the engine's
|
|
66
|
+
refusal, **exit 2 naming the key** (it used to print the bundle README and exit 0 — the README now
|
|
67
|
+
prints on the unblessed first-run screen); the close-fix cap for a bundle is run-u's 4 (was 3 —
|
|
68
|
+
one number for every caller); the outside watchdog now guards a bundle run; a bundle green mints
|
|
69
|
+
no bridge file; the engine's job-end tail read tolerates a torn spine line (as the bundle door's
|
|
70
|
+
already did).
|
|
71
|
+
- **`bareloop run <bundle> --resume <runid> [--repo <path>]`** — a halted bundle run resumes into
|
|
72
|
+
its own worktree through the engine's resume gates and spend fold. Each run leg writes
|
|
73
|
+
`runs/<runid>/run.json`; a resume's `history.jsonl` row carries `resumedFrom`. Proven with tests against a real git worktree and a scripted
|
|
74
|
+
provider; not run live.
|
|
75
|
+
|
|
76
|
+
### Fixed
|
|
77
|
+
|
|
78
|
+
- **The outside watchdog now ships in the package (F205).** `scripts/u-watchdog.mjs` moved to
|
|
79
|
+
`src/u-watchdog.mjs`: `package.json` `files` omits `scripts/`, so an installed run spawned a guard
|
|
80
|
+
whose script did not exist and it died silently at startup.
|
|
81
|
+
- **The bare `bareloop` menu's "2 run" now honours the monthly limit.** It handed the bundle door no keys
|
|
82
|
+
home, so the limit, your price and the key-row choice were all off; it now gets the same home
|
|
83
|
+
`bareloop run <bundle>` does.
|
|
84
|
+
- **A halted bundle run's printed resume command keeps your tightened `--budget` / `--wall`.** It used to drop
|
|
85
|
+
them, so the next leg silently reverted to the signed ceiling. The money-halt hint for a bundle no longer
|
|
86
|
+
says to edit `budgetUsd` (a bundle's spec is manifest-hashed and cannot be edited).
|
|
87
|
+
After a MONEY halt the line does not repeat the exhausted `--budget` (chain spend is folded, so pasting it
|
|
88
|
+
halted again at once); it prints `--budget <more than $<spent so far>, at most $<signed budgetUsd>>` for the
|
|
89
|
+
person to fill in, and the lever list says to choose a larger `--budget` up to the signed amount. Nothing is
|
|
90
|
+
filled in automatically. Other halts keep your tightened `--budget` / `--wall` as before. Printed text only.
|
|
91
|
+
- **Printed commands no longer name scripts the package does not ship.** Entered through `bareloop run-u` /
|
|
92
|
+
`interview` / `author`, the approve, resume, pause and reopen lines (and the interview's run-author line and
|
|
93
|
+
the author's run-u line) now read `bareloop run-u …` etc.; `node scripts/*.mjs` is printed only from the
|
|
94
|
+
source-tree scripts. One owner for the spelling: `commandFor` in `src/invoke.js`.
|
|
95
|
+
- **Printed copy-paste commands name the key variable the run reads and carry `--registry` / `--workflow`.**
|
|
96
|
+
The approve and resume lines from `run-u` and `run-author` used to leave these out, so a pasted line could
|
|
97
|
+
read a different key row or drop the run-list registration (F207 b). Printed text only.
|
|
98
|
+
|
|
99
|
+
### Removed
|
|
100
|
+
|
|
101
|
+
- `doRun`/`buildProviders` in `src/cli.js` and the `resumable: false` wiring for bundles (F130).
|
|
102
|
+
|
|
8
103
|
## [0.32.0] — 2026-09-29
|
|
9
104
|
|
|
10
105
|
### Added
|
package/README.md
CHANGED
|
@@ -143,7 +143,10 @@ bareloop run ./my-maintainer.bareloop --repo /path/to/checkout --approve <bundle
|
|
|
143
143
|
`export` only ships a job that has already earned a real green — it refuses to bundle an
|
|
144
144
|
unproven draft. The first `run` on a new machine asks you to approve the bundle's printed
|
|
145
145
|
hash; once that run goes green, later runs need no re-approval as long as the bundle stays
|
|
146
|
-
unchanged. Every run works in its own fresh git worktree and leaves the merge to you.
|
|
146
|
+
unchanged. Every run works in its own fresh git worktree and leaves the merge to you. `bareloop
|
|
147
|
+
run` is a door to the same engine as `bareloop run-u`, so it uses any provider your keys file
|
|
148
|
+
has, honours the monthly limit, and a halted run can be continued with
|
|
149
|
+
`bareloop run <bundle> --resume <runid>`. Full
|
|
147
150
|
contract — flags, approval/blessing rules, exit codes — in
|
|
148
151
|
[`bareloop.context.md`](bareloop.context.md).
|
|
149
152
|
|
package/bareloop.context.md
CHANGED
|
@@ -91,8 +91,9 @@ inherited rule carries the green that minted it and the contrast that attributed
|
|
|
91
91
|
chose. `JUDGE_MODEL` stays exported as the pre-item-32 pin — no grading path reads it.
|
|
92
92
|
The judge's key follows the resolved judge provider's own `envKey`, with `JUDGE_API_KEY`
|
|
93
93
|
as a role-named override in front of it. The bundle runner
|
|
94
|
-
(`bareloop run`) is
|
|
95
|
-
|
|
94
|
+
(`bareloop run`) is a door to this same engine (`src/bundlerun.js` → `src/userrun.js`), so it
|
|
95
|
+
reads whatever key the bundle's provider names (keys file or env) and refuses a missing one
|
|
96
|
+
at $0, exit 2, naming it.
|
|
96
97
|
- **Reuse — where a workflow comes from:** a plain `runJob` always drafts cold. Passing
|
|
97
98
|
`bridge` starts from one standalone bridge file (`src/reuse.js`'s envelope, `## The reuse
|
|
98
99
|
ENVELOPE and runReuse` below). The CLI's `--registry <dir>` / library-level `registryDir`
|
|
@@ -157,7 +158,7 @@ minting claim, or the shell-owned retry cap — all unknown-field reds.
|
|
|
157
158
|
| `provider` | `anthropic-api` \| `openai-api` \| `gemini-api` \| `clipipe-subscription` | menu (`PROVIDERS`); part of the lineage key by definition. Only `anthropic-api` is guaranteed (F48); `openai-api` (PRD item 28) goes through the provider factory (`src/providers.js`), reads `OPENAI_API_KEY`, and admits only `deepseek-flash` today (F171: swapped from the retired `deepseek-chat`) — see "Worker surface" above for its `baseUrl` field and per-model gating; `gemini-api` (PRD item 31.3) reads `GEMINI_API_KEY` and is ADMITTED-PENDING-PROBE — zero runs, and every launch says so (`PROBE_STATUS`/`probeWarningLines`); `clipipe-subscription` drives the worker natively and needs `opts.nativeProvider` |
|
|
158
159
|
| `conditions` | `{ providerPath?, closeVerbosity?, taskFraming?, scaffold? }` | declared keys only, string values — the environment label (consumed by the N3 lineage key; recorded on spines from run one) |
|
|
159
160
|
| `cadence` | `{ unit: hour\|day\|week, every: 1..30 }` | validated now, consumed at N5 (Scheduler) |
|
|
160
|
-
| `budgetUsd` | `0 < n <= shell cap` | ceiling chain: workflow ≤ job ≤ shell — each layer may tighten, never exceed |
|
|
161
|
+
| `budgetUsd` | finite `0 < n <= shell cap` | ceiling chain: workflow ≤ job ≤ shell — each layer may tighten, never exceed |
|
|
161
162
|
| `maxWallMs` | optional integer ms `>= MIN_WALL_MS` (one close timeout) | the run's wall clock. **NO DEFAULT, by ruling** — absent means time-unbounded *by explicit operator choice*, never by fallback (F45: a defaulted cap is a silent second ceiling). Enforcement is a BETWEEN-ROUND deadline, so the honest worst case is `maxWallMs + closeStages × closeTimeoutMs` — every stage of a staged close gets the FULL timeout — and all three numbers are reported (`loop.stop()` cannot cut an in-flight call — F61 measured 500ms→4,018ms). The `MIN_WALL_MS` floor is a ONE-stage, un-autoset-default number, so a spec with many stages OR a raised/autoset `closeTimeoutMs` can validate while its overshoot dwarfs its cap: `runPlan` adds a RUN-START check (PRD item 27/M3) that refuses `wall-under-close-timeout` at $0 once the EFFECTIVE per-stage ceiling is known (autoset or signed), rather than a silent clamp. Operator-only, tighten-only; adding or changing it changes the spec hash |
|
|
162
163
|
| `closeTimeoutMs` | optional integer ms `>= CLOSE_TIMEOUT_FLOOR_MS` (120,000 — hamr's arbiter floor, 2026-09-07) | PRD item 27/M3's SIGNED override for the close's own per-stage wall-clock cap. Absent (the normal case): the run autosets it from a $0 seed timing pass — `max(FLOOR, K × slowest measured stage)`, `K = 5` (hamr, 2026-09-07: "2 and 5 are fine"). Present: the operator's own number wins outright and may legally sit ABOVE *or below* the autoset estimate (the rates-passthrough shape, F113 — never tighten-only). Arbiter territory exactly like `budgetUsd`/`maxWallMs`/`close[].sha256`: the authoring pipeline's `assembleSpec` refuses a draft carrying it |
|
|
163
164
|
| `model` | optional non-empty string, exact provider model id (e.g. `"claude-sonnet-5"`) | the WORKER's model, build-list #3. Absent means today's runner default; present it wins over a `--model` flag outright, and a flag naming a different id is refused (`resolveWorkerModel`, `src/job.js`) rather than silently overridden. Part of the signed hash like every other field. Worker only — the JUDGE's model comes from the `judge` row below, which DEFAULTS to this same worker model |
|
|
@@ -1160,7 +1161,7 @@ Reserved spine vocabulary (V7, machinery-free until job #1 surfaces one):
|
|
|
1160
1161
|
`coordination-red` — a failure between units (scope contention, step order, store
|
|
1161
1162
|
races), never to be folded into worker/interpreter reds.
|
|
1162
1163
|
|
|
1163
|
-
### `runJob(spec, { approvals, workdir, provider, nativeProvider?, providerFor?, emit, capRuns?, strikeLimit?, shellCapUsd?, closeTimeoutMs?, closeDir?, layerRoot?, readShim?, scout?, bridge?, draftSpentUsd?, draftSpendComplete?, priorSpentUsd?, priorSpendComplete?, priorWallMs?, resumeSeed?, resumeGrades?, resumeReplans?, resumeBranch?, humanRuling?, heldRuling?, reviewDoor?, doorRerun?, resumable? })` → outcome — `src/run.js`
|
|
1164
|
+
### `runJob(spec, { approvals, workdir, provider, nativeProvider?, providerFor?, rates?, judgeRates?, emit, capRuns?, strikeLimit?, shellCapUsd?, closeTimeoutMs?, closeDir?, layerRoot?, readShim?, scout?, bridge?, draftSpentUsd?, draftSpendComplete?, priorSpentUsd?, priorSpendComplete?, priorWallMs?, resumeSeed?, resumeGrades?, resumeReplans?, resumeBranch?, humanRuling?, heldRuling?, reviewDoor?, doorRerun?, resumable? })` → outcome — `src/run.js`
|
|
1164
1165
|
|
|
1165
1166
|
**`draftSpentUsd` (hamr's ruling 2026-09-28, "one cap covers drafting + run") — money the
|
|
1166
1167
|
AUTHORING pipeline already spent on this job before it was signed.** Shrinks THIS run's own
|
|
@@ -1215,9 +1216,9 @@ readout seed (grades) — the distinction matters and is spelled out there.
|
|
|
1215
1216
|
|
|
1216
1217
|
`resumable` (default `true`, PRD item 27(c)/F130) says whether THIS runner supports
|
|
1217
1218
|
`--resume` at all — `run-u.mjs` leaves it at the default (byte-identical to before this
|
|
1218
|
-
flag existed)
|
|
1219
|
-
|
|
1220
|
-
|
|
1219
|
+
flag existed). The exported bundle CLI is no longer a second `runJob` caller (`bareloop run`
|
|
1220
|
+
goes through `src/userrun.js`), so it leaves it at the default too and supports
|
|
1221
|
+
`bareloop run <bundle> --resume <runid>`; the F130 `resumable: false` was retired with `doRun`.
|
|
1221
1222
|
|
|
1222
1223
|
The runner — the shell's top layer, and the ONE entry. It composes everything below it and
|
|
1223
1224
|
interprets nothing itself. Sequence: **approval gate** (human-signs-always — refuses an
|
|
@@ -1306,6 +1307,11 @@ knob, not a product default — the spec names no scout, so the signed hash is u
|
|
|
1306
1307
|
`--read-shim`: an unrecognised value exits 2 at argv, and every re-invocation the runner prints
|
|
1307
1308
|
carries `--scout off` when set, so a resume never silently drops the arm.
|
|
1308
1309
|
|
|
1310
|
+
Every re-invocation the runner prints (the preview's approve line, resume, pause and reopen hints)
|
|
1311
|
+
names the key variable the run really reads (the job's key row, not the provider shape's default
|
|
1312
|
+
name) and carries `--registry <dir> --workflow <name>` whenever they were given, so a pasted command
|
|
1313
|
+
never greens with no registry row (F207).
|
|
1314
|
+
|
|
1309
1315
|
**`bareloop run-u --draft-spend-incomplete`** is a boolean presence-flag (no value) that sets
|
|
1310
1316
|
`runJob`'s `draftSpendComplete: false`, saying the `--draft-spent-usd` figure is a floor. It
|
|
1311
1317
|
has no guard against being passed without `--draft-spent-usd` — alone it is inert (nothing is
|
|
@@ -1843,19 +1849,24 @@ bare-agent's `Loop` takes `rates: {in, out, cacheReadMult?, cacheWriteMult?}`
|
|
|
1843
1849
|
tokens, the two multipliers applying to the input rate for the cache-read and cache-write
|
|
1844
1850
|
tiers (defaulting to Anthropic's 0.1× / 1.25×). A caller-supplied rate is recorded as
|
|
1845
1851
|
VOUCHED rather than as a guess, and it silences bare-agent's own guesstimate warning.
|
|
1846
|
-
**
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
`
|
|
1857
|
-
|
|
1858
|
-
`
|
|
1852
|
+
**Where bareloop takes it from:** the CUSTOMER's own price on the key's row in
|
|
1853
|
+
`~/.config/bareloop/config.json` (`keys.<ENV NAME>.priceInPerM` / `priceOutPerM`, USD per 1M
|
|
1854
|
+
tokens — see "Settings" below). `runJob` / `runPlan` accept `rates` (and `judgeRates`) as data and
|
|
1855
|
+
hand them to the one `Loop` that drives worker, scout and planner rounds; the run doors resolve
|
|
1856
|
+
them once. No price on the row = no `rates` key anywhere = the guesstimate above, byte-identical to
|
|
1857
|
+
before. bareloop keeps no price list of its own, ever.
|
|
1858
|
+
|
|
1859
|
+
**The provenance is on the record, per round (`rateSource`).** The pinned bare-agent (`^0.43.0`)
|
|
1860
|
+
carries it on every metering payload, and bareloop forwards it VERBATIM (`rateSourceFields`,
|
|
1861
|
+
`src/planrun.js`): every API `worker-round` (worker, scout, planner, fix loop) carries it beside
|
|
1862
|
+
`pricing` today — `'caller'` when a customer price is on the key's row, else `'tier'`/`'default'`
|
|
1863
|
+
(the guess). `judge-round` records carry it too (the judge Loop's per-call label, forwarded by
|
|
1864
|
+
`src/kinds.js` `onJudgeCost`): `'caller'` under your price on the judge's row, else the guess label. The
|
|
1865
|
+
exceptions read UNKNOWN provenance, correctly, and by the same rule that governs the archive: every
|
|
1866
|
+
round archived before the signal existed (judge rounds before this change included). The native
|
|
1867
|
+
per-turn `worker-turn` carries `null`, bareloop's OWN statement rather than a forwarded one: that
|
|
1868
|
+
surface prices the SESSION, so a turn had no rate to guess. A `worker-round`/`worker-turn`
|
|
1869
|
+
carries bare-agent's own label beside `pricing`:
|
|
1859
1870
|
`'provider'` (the provider reported its own authoritative cost — the native CLI surface)
|
|
1860
1871
|
and `'caller'` (you passed the rate) are VOUCHED; `'tier'` (a recognized Claude tier,
|
|
1861
1872
|
where **both** of bareloop's own production models land) and `'default'` (the blind
|
|
@@ -2986,7 +2997,8 @@ separate tarball step).
|
|
|
2986
2997
|
README.md the operator questions, in the order `bareloop run` asks them
|
|
2987
2998
|
blessing.json absent at export; written by the run that first greens it
|
|
2988
2999
|
history.jsonl absent at export; one line appended per `bareloop run` on this machine
|
|
2989
|
-
runs/<runid>/ absent at export; one per run —
|
|
3000
|
+
runs/<runid>/ absent at export; one per run leg — spine.jsonl, gate-audit.jsonl, close/ (the
|
|
3001
|
+
close's books) and run.json ({runid, worktree, seed, repo, at, resumedFrom?})
|
|
2990
3002
|
```
|
|
2991
3003
|
|
|
2992
3004
|
**`bundleHash`** = sha256 over the sorted `path:contentSha256` lines of exactly `spec.json`
|
|
@@ -3001,7 +3013,7 @@ a `package.json` (name/version/`dependencies: {"bareloop": "^…"}`, `private: t
|
|
|
3001
3013
|
corrects an earlier, now-stale reading of this section that claimed no `package.json` was
|
|
3002
3014
|
written; `tests/bundle.test.js` proves both the write and the hash-exclusion directly. The
|
|
3003
3015
|
`runs/<runid>/` directory (this run's relocated `spine.jsonl`/`gate-audit.jsonl`,
|
|
3004
|
-
written by `bareloop run`, step 6 below) is real and live but is not in the frozen spec's
|
|
3016
|
+
written by `bareloop run`, step 6 below; `run.json` records where the worktree is, for a resume) is real and live but is not in the frozen spec's
|
|
3005
3017
|
layout table at all. `history.jsonl`'s row also carries `bundleHash` and `approveHash`
|
|
3006
3018
|
(the POC-fact correction, below) beyond the fields the original layout table named.
|
|
3007
3019
|
|
|
@@ -3126,8 +3138,8 @@ script leaves the signed spec's own hash intact. `runJob`'s close-first precheck
|
|
|
3126
3138
|
one — a fake green, $0 spent, no work done, reported as success. `readBundle`'s
|
|
3127
3139
|
`bundle-tampered` red is therefore not a courtesy check: it is the only thing standing
|
|
3128
3140
|
between a swapped script and a fake green, so `bareloop run` calls it as literal step 1,
|
|
3129
|
-
before the envelope check, before the
|
|
3130
|
-
ever reached.
|
|
3141
|
+
before the envelope check, before the blessing check, before any key is read, before the
|
|
3142
|
+
worktree, before the engine is ever reached.
|
|
3131
3143
|
|
|
3132
3144
|
#### The CLI — `bareloop export | run | history | replay | run-u | interview | author`, `bin/bareloop.mjs`
|
|
3133
3145
|
|
|
@@ -3138,7 +3150,7 @@ ever reached.
|
|
|
3138
3150
|
(`export { main as cliMain } from './cli.js'` — renamed on export so it never collides with
|
|
3139
3151
|
some other module's own generic `main`). `cliMain(argv, deps)` is the in-process test/
|
|
3140
3152
|
integration seam: pass `deps.provider` (and optionally `providerFor`/`judgeProvider`) and the
|
|
3141
|
-
real
|
|
3153
|
+
real key check is skipped entirely — a scripted provider *is* the run, the
|
|
3142
3154
|
same seam `tests/planrun.test.js` uses. `deps` also accepts `env`, `stdout`, `stderr`, `cwd`,
|
|
3143
3155
|
`now`, `stdin` — omit any of them and the real `process.*` equivalent is used.
|
|
3144
3156
|
|
|
@@ -3148,9 +3160,14 @@ below. `run-u`, `interview` and `author` (below) are not on this menu (PANEL-BUI
|
|
|
3148
3160
|
tasks 2/4-4/4): each one's own flag grammar is too wide for a line-at-a-time wizard, and the
|
|
3149
3161
|
menu's job is to ask the SAME questions the sub-command below already answers, never to
|
|
3150
3162
|
invent a new interview — this is doubly true for `interview`/`author`, which already ARE
|
|
3151
|
-
interviews of their own.
|
|
3163
|
+
interviews of their own. Choice `2 run` is handed the SAME resolved keys home `bareloop run <bundle>` gets, so the
|
|
3164
|
+
monthly limit, your price and the key-row choice apply exactly as on the sub-command (before this it
|
|
3165
|
+
got none, and a run started from the menu skipped the limit). All three are dispatched by name only: `bareloop run-u <flags…>`,
|
|
3152
3166
|
`bareloop interview <flags…>`, `bareloop author <flags…>`.
|
|
3153
3167
|
|
|
3168
|
+
When two key rows share one provider and endpoint, a CLI run uses the FIRST row in the keys list, so put the
|
|
3169
|
+
key you want first (the panel picks the row by the Model menu).
|
|
3170
|
+
|
|
3154
3171
|
- **`bareloop export <jobs/x.json> --registry <dir> --out <dir>`** → resolves the spec's
|
|
3155
3172
|
close-script paths against the spec file's own directory (never the process cwd), calls
|
|
3156
3173
|
`exportBundle`. On success prints `bundleHash:`, the sorted file list, and "this bundle is
|
|
@@ -3158,54 +3175,64 @@ interviews of their own. All three are dispatched by name only: `bareloop run-u
|
|
|
3158
3175
|
missing flag, `0` on success.
|
|
3159
3176
|
|
|
3160
3177
|
- **`bareloop run <bundleDir> --repo <path> [--budget N] [--wall MIN] [--approve <bundleHash>]`**
|
|
3161
|
-
|
|
3178
|
+
and **`bareloop run <bundleDir> --resume <runid> [--repo <path>] [...]`** — a thin door
|
|
3179
|
+
(`src/bundlerun.js`) to the same engine `bareloop run-u` drives (`src/userrun.js`): one
|
|
3180
|
+
runner, so providers, keys (any provider the bundle's spec names — the keys file or env), the
|
|
3181
|
+
judge, the monthly $ limit, the outside watchdog and the readout are that engine's. The door
|
|
3182
|
+
does only what is bundle-specific, in this order, no step reordered:
|
|
3162
3183
|
1. `readBundle(bundleDir)` — the tamper check (N4, above). Any red stops here, exit `1`.
|
|
3163
3184
|
2. `checkBundleDeps(bundleDir)` (F128) — can the bundle's own `close/` directory resolve
|
|
3164
|
-
`require.resolve('bareloop')`? A bundle
|
|
3165
|
-
dependency — see the corrected adopter flow below) has no `node_modules` of its own,
|
|
3166
|
-
which would otherwise crash the close deep inside the precheck with a bare
|
|
3167
|
-
`ERR_MODULE_NOT_FOUND`. A red here (`bundle-deps-missing`) prints the exact cure line
|
|
3185
|
+
`require.resolve('bareloop')`? A red (`bundle-deps-missing`) prints the exact cure line
|
|
3168
3186
|
(`cd <bundleDir> && npm install`) and stops, exit `1`, before the envelope check, the
|
|
3169
|
-
key, or any worktree.
|
|
3187
|
+
blessing check, any key, or any worktree.
|
|
3170
3188
|
3. `checkEnvelope(spec, { budgetUsd?, maxWallMs? })` — `--wall` is minutes, converted to ms.
|
|
3171
3189
|
A red (an invalid number, or a widen) stops here, exit `1`.
|
|
3172
|
-
4.
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
(
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
`
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3190
|
+
4. Blessing — BEFORE any key is read. No `blessing.json` yet: print the bundle's `README.md`
|
|
3191
|
+
(the operator questions), the "first run" notice + the minting-run line (F128-corrected —
|
|
3192
|
+
see below), the `bundleHash`, and **require** `--approve <bundleHash>` to match exactly, or
|
|
3193
|
+
exit `1`. A `blessing.json` present: `verifyBlessing` must pass (`blessing-stale` stops here,
|
|
3194
|
+
exit `1`); a `--approve` flag is accepted but ignored with a printed note ("no-resign").
|
|
3195
|
+
A resume passes the same checks (an unblessed bundle whose first run halted still needs
|
|
3196
|
+
`--approve`).
|
|
3197
|
+
5. The tree, planned not created: a fresh run refuses (exit `1`) if `--repo` is not a git repo
|
|
3198
|
+
with ≥1 commit or `<repo>/.bareloop/wt/<runid>` already exists; the engine then creates
|
|
3199
|
+
`git worktree add --detach <repo>/.bareloop/wt/<runid> HEAD` only once ITS OWN $0 refusals
|
|
3200
|
+
(missing key, monthly limit, param guards) have passed, so a refusal leaves no worktree —
|
|
3201
|
+
**always fresh, never reused** (a reused worktree would read a prior run's edits as
|
|
3202
|
+
"already-green"). A **resume** (`--resume <runid>`) instead reads `runs/<runid>/run.json`,
|
|
3203
|
+
re-enters that run's own worktree (a missing `run.json`, a vanished worktree, or a `--repo`
|
|
3204
|
+
different from the recorded one is a stop, exit `1`, spending nothing; `--repo` is optional
|
|
3205
|
+
on a resume) and hands the engine `resumeRun` on that run's spine — the engine's own gates
|
|
3206
|
+
(checkpoint age, tree-at-seed, liveness) and spend fold apply, so `--budget` can never widen
|
|
3207
|
+
a resume. The new leg gets its own `runs/<newid>/`. The resume command a halted leg prints carries that leg's own
|
|
3208
|
+
`--budget`/`--wall`, only if you passed them (a tightened ceiling never silently reverts to the signed
|
|
3209
|
+
one). After a MONEY halt the line does NOT repeat the exhausted `--budget` (chain spend is folded, so
|
|
3210
|
+
it would halt again at once); it prints `--budget <more than $<spent so far>, at most $<signed
|
|
3211
|
+
budgetUsd>>` for you to fill in, and is not runnable until you do. When the run spent the WHOLE signed
|
|
3212
|
+
ceiling, that range is empty, so no resume line is printed: the block says no room is left under the signed ceiling. A bundle's spec is
|
|
3213
|
+
manifest-hashed, so its halt hint never asks you to edit `budgetUsd`.
|
|
3214
|
+
6. `resolveBundleSpec` (the `$BARELOOP_BUNDLE` substitution) → tighten `budgetUsd`/`maxWallMs`
|
|
3215
|
+
in memory if `--budget`/`--wall` were given → `approveHash = jobSpecHash(that tightened+
|
|
3216
|
+
resolved spec)`, handed to the engine as the approval it checks against (`signer: 'bundle'`;
|
|
3217
|
+
the human-approved `bundleHash` never changes). The spine, the close's books, the relocated
|
|
3218
|
+
`gate-audit.jsonl` and `run.json` are written under `<bundleDir>/runs/<runid>/`.
|
|
3219
|
+
7. Engine outcomes the door keeps: a missing key is the engine's refusal, **exit `2`**, naming
|
|
3220
|
+
the key (it used to print the README and exit `0`); the close-fix cap is run-u's `CAP_RUNS`
|
|
3221
|
+
(4 — one number for every caller; a bundle used 3 before); the monthly limit applies (exit
|
|
3222
|
+
`2`, nothing spent); a bundle green mints **no bridge file** (a bundle's bridges are shipped
|
|
3223
|
+
inputs); `--door`/`--decide`/`--review-door` are not exposed on `run`.
|
|
3224
|
+
8. `appendHistory` — one `history.jsonl` line off this leg's own `job-end`: `{ runid, at,
|
|
3225
|
+
outcome, spentUsd, spendComplete, budgetUsd, maxWallMs, worktree, branch, bundleHash,
|
|
3226
|
+
approveHash, resumedFrom? }` (the `bundleHash` ↔ `approveHash` pairing, POC fact 2).
|
|
3227
|
+
`spentUsd`/`spendComplete` are never fabricated as `0` when unknown. No `job-end` (a $0
|
|
3228
|
+
refusal, a crash) writes no row. A `green` outcome on a still-unblessed bundle also calls
|
|
3229
|
+
`bless(bundleDir, { bundleHash, runid, outcome, host })`.
|
|
3230
|
+
9. The tail: the engine's readout (`outcome`, `spent` with `≥$…` when incomplete, …), then the
|
|
3231
|
+
door's `branch`, `worktree`, a `git merge <branch>` instruction ("merge stays human — this
|
|
3232
|
+
CLI never merges") and "the worktree is kept until you remove it: `git worktree remove
|
|
3233
|
+
<worktree>`" (F110). **Exit `0` ONLY for `green`/`already-green` — every other outcome
|
|
3234
|
+
exits `1`**, and the engine's `2` (refusal) / `3` (spine leak) are preserved, so a caller
|
|
3235
|
+
scripting off the exit code can never mistake a red run for a green one.
|
|
3209
3236
|
|
|
3210
3237
|
**Adopter flow, corrected (F128, 2026-09-06 — hamr's paid fire found the frozen spec's
|
|
3211
3238
|
validation step 2 named the wrong install shape):** `npm install <bundle dir>` run from a
|
|
@@ -3302,6 +3329,8 @@ interviews of their own. All three are dispatched by name only: `bareloop run-u
|
|
|
3302
3329
|
`baselineKind`/`question`: the plain-English question is derived in `src/panel/server.js`
|
|
3303
3330
|
(`stageQuestionText`) from the signed stage's kind/params only, `null` when no honest
|
|
3304
3331
|
wording exists (the client then shows the stage's own name). Every
|
|
3332
|
+
GET/HEAD answers only when its `Host` header is the panel's own `127.0.0.1:<port>` (`checkHostGuard`,
|
|
3333
|
+
`src/panel/authorroutes.js`; any other Host is `403`, no token needed to read). Every
|
|
3305
3334
|
P1 endpoint is GET/HEAD only (anything else — including every write verb — is `405`, outside the
|
|
3306
3335
|
two human-click-guarded write families `/api/author/*` and `/api/settings/*` below); a URL
|
|
3307
3336
|
never joins a path segment into a filesystem read — a runid is looked up in the run list
|
|
@@ -3347,7 +3376,8 @@ interviews of their own. All three are dispatched by name only: `bareloop run-u
|
|
|
3347
3376
|
requires the human-click guard: an `x-bareloop-token` header matching a fresh token minted
|
|
3348
3377
|
once per server start (templated into `index.html` like the port) AND an Origin/Host
|
|
3349
3378
|
naming this exact `127.0.0.1:<port>` — a request failing either gets `403`, before the
|
|
3350
|
-
route body ever runs.
|
|
3379
|
+
route body ever runs. The token protects against other browser origins, not against other local users or
|
|
3380
|
+
processes on the same machine (local-trust model). `POST /api/author/start` (one job-card body; `409` while a prior
|
|
3351
3381
|
session is still non-terminal — one authoring session at a time) creates a session
|
|
3352
3382
|
(`src/panel/authorsession.js`) that runs `prepareSource`/`detectLanguage`/`validateJob`/
|
|
3353
3383
|
`authorCloseForJob`/`assembleSpec`/`prepareSigning` in-process, exactly the library calls
|
|
@@ -3398,6 +3428,11 @@ interviews of their own. All three are dispatched by name only: `bareloop run-u
|
|
|
3398
3428
|
preview `scripts/run-u.mjs --job <x>` prints with no `ANTHROPIC_API_KEY`/`OPENAI_API_KEY`
|
|
3399
3429
|
set. `scripts/run-u.mjs` is unchanged in behaviour: it is the pre-existing thin adapter
|
|
3400
3430
|
over this same `src/userrun.js:main`, not rewired to go through `src/cli.js`.
|
|
3431
|
+
**Printed commands are spelled the way you entered:** every re-invocation these flows print (the approve
|
|
3432
|
+
line, resume, pause, reopen, the interview's run-author line, the author's run-u line) reads `bareloop
|
|
3433
|
+
run-u …` / `bareloop author …` when reached through `bareloop`, and `node scripts/run-u.mjs …` only through
|
|
3434
|
+
the source-tree adapters — the tarball ships no `scripts/`. One owner: `commandFor` (`src/invoke.js`),
|
|
3435
|
+
fed by each flow's `deps.invokedAs`.
|
|
3401
3436
|
|
|
3402
3437
|
- **`bareloop interview <flags…>`** (PANEL-BUILD.md P0 task 3/4) → the close-authoring
|
|
3403
3438
|
interview, at the terminal, one question at a time (D10; `docs/logs/FINDINGS.md`'s
|
|
@@ -3412,7 +3447,7 @@ interviews of their own. All three are dispatched by name only: `bareloop run-u
|
|
|
3412
3447
|
mechanically, $0, via `prepareSource`/`proveDestination`), then the picked class's frozen
|
|
3413
3448
|
question set (`questionsFor`/`requiredAnswersFor`, `src/authorjob.js`), then the operator's
|
|
3414
3449
|
own job name/budget/wall. On success it writes `answers.json`/`specdraft.json` under `--out`
|
|
3415
|
-
and either offers to spawn `scripts/run-author.mjs
|
|
3450
|
+
and either offers to spawn the packaged `bin/bareloop.mjs author` (same flags as `scripts/run-author.mjs`, which the package does not ship; only when a clean key is present and no
|
|
3416
3451
|
install gap remains — default answer is no) or prints the exact command to run it later.
|
|
3417
3452
|
Because this module reads a real TTY/piped stdin, `src/cli.js` hands it `stdin`/`stdout`/
|
|
3418
3453
|
`stderr` (the raw streams, not the wrapped `out`/`err` line-functions every other command
|
|
@@ -3510,20 +3545,65 @@ Two files in `~/.config/bareloop/`, outside any repo:
|
|
|
3510
3545
|
`updateConfig(patch, { home })` (atomic tmp + rename, mode 600, unknown fields kept).
|
|
3511
3546
|
Fields: `monthlyLimitUsd` (number above 0; absent or `null` = no limit; any other value present
|
|
3512
3547
|
is a `ConfigError`, refused like an unreadable file), `anthropicBalanceNote` (a note the
|
|
3513
|
-
person types; no check ever reads it), `keys.<ENV NAME>` = `{ name, shape, baseUrl }` (the Providers rows' settings). A secret-shaped
|
|
3548
|
+
person types; no check ever reads it), `keys.<ENV NAME>` = `{ name, shape, baseUrl, priceInPerM?, priceOutPerM? }` (the Providers rows' settings, plus the customer's own price below). A secret-shaped
|
|
3514
3549
|
string anywhere in the document is refused on save. An unreadable file is a `problem` string and
|
|
3515
3550
|
a refused run start ($0), never "no limit". There is no monthly TIME limit (dropped).
|
|
3516
3551
|
|
|
3552
|
+
**Your own price — `keys.<ENV NAME>.priceInPerM` / `priceOutPerM`.** The customer sets the price of the
|
|
3553
|
+
model behind a key, on that key's row, in USD **per 1M tokens** (input and output), for example
|
|
3554
|
+
`"keys": { "DEEPSEEK_API_KEY": { "priceInPerM": 0.006, "priceOutPerM": 1.2 } }`. There is no built-in
|
|
3555
|
+
price list. Both numbers or neither: each must be a finite number 0 or more, and one alone, a negative,
|
|
3556
|
+
a string or `null` refuses the run at $0 (`ConfigError`, exit 2, "Nothing spent.") — a bad price never
|
|
3557
|
+
falls back to the guess. A Settings save keeps the two fields (the Settings SCREEN has no price field
|
|
3558
|
+
yet; edit `config.json`). `ratesFor(provider, baseUrl, rows, model)` (`src/providerrows.js`) is the one
|
|
3559
|
+
lookup, on the SAME row `keyNameFor` picks the key from (`findRow`: shape + endpoint + model), returning
|
|
3560
|
+
`null` (no price) or `{ rates: {in, out}, inPerM, outPerM, envName }` with `rates` in bare-agent's
|
|
3561
|
+
per-1K unit (`perM / 1000`). The run doors resolve it once, at $0, before the monthly claim and any
|
|
3562
|
+
worktree or spine (`src/userrun.js`; `src/authorrun.js`; the panel's authoring session): the worker row
|
|
3563
|
+
prices worker, scout and planner rounds; the judge is priced at ITS model's own row — for a different
|
|
3564
|
+
provider its own row, for the worker's provider the row on the worker's endpoint named for the judge's model
|
|
3565
|
+
(`judgeRatesFor`, `src/providerrows.js`), falling back to the worker's price when the model names no row.
|
|
3566
|
+
(The judge's KEY still follows the worker's row, so with two rows on one endpoint key and price can differ.) A priced round is stamped `rateSource:'caller'` (VOUCHED, see `spendProvenance`); with
|
|
3567
|
+
no price it stays the built-in guess, as before. The run preview and the run tail print
|
|
3568
|
+
`price yours: in $0.006 / out $1.20 per 1M tokens (DEEPSEEK_API_KEY row)` when a price is set, and
|
|
3569
|
+
nothing when none is; a 0 / 0 price is accepted but the line then ends `— WARNING: this price is $0, so spend and limits read $0`. **The price is yours, and it sets what the cap means:** the cap and the halts run
|
|
3570
|
+
on the dollars your price produces, so a price set too low weakens the cap (a run can do far more
|
|
3571
|
+
than you meant before it halts). When a vendor lists two prices (for example a cache-hit and a
|
|
3572
|
+
cache-miss input rate) enter the higher one. bare-agent applies its own cache multipliers (read 0.1×,
|
|
3573
|
+
write 1.25× of the input rate) to cache tokens; they are not configurable here, so cache tokens are
|
|
3574
|
+
priced from your input price by that rule. A price applies from the next run: **past runs are never
|
|
3575
|
+
re-priced** (a spine and the run list keep the dollars they booked). Not covered by a price: a native
|
|
3576
|
+
(CLI) session, which reports its own cost, and the reuse engine (`runReuse` / `selectBridge`), which
|
|
3577
|
+
resolves no key row.
|
|
3578
|
+
|
|
3517
3579
|
**Monthly $ limit.** `checkMonthlyRoom({ capUsd, home, now })` → `{ ok, leftUsd, limitUsd,
|
|
3518
|
-
atLeast }`; `monthlyRefusalText(room)` is the ONE spelling: `Max $<left, 2 decimals>
|
|
3519
|
-
limit)
|
|
3580
|
+
atLeast, heldUsd, heldRuns }`; `monthlyRefusalText(room)` is the ONE spelling: `Max $<left, 2 decimals>
|
|
3581
|
+
(monthly limit)`, and when run(s) in progress hold money beyond what they have spent, the same line
|
|
3582
|
+
reads `Max $4.00 (monthly limit, $5.95 held by a run in progress)` (`runs` when more than one). It is called at the run-start seam in `src/userrun.js` `execute` (before any token
|
|
3520
3583
|
spends; the CLI prints it to stderr, exit 2) and at the panel's Sign & run (`signRun` in
|
|
3521
3584
|
`src/panel/authorroutes.js`, refused server-side whatever the page shows). The page only echoes the
|
|
3522
3585
|
same text under `#jf-cap-money` via `GET /api/author/monthly-check?cap=`. "This month" is the local
|
|
3523
|
-
calendar month; a died or incomplete-spend run makes the total an "at least" figure.
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3586
|
+
calendar month; a died or incomplete-spend run makes the total an "at least" figure.
|
|
3587
|
+
|
|
3588
|
+
*Hold until done.* A run CLAIMS the limit when it starts and holds it until it is done — no lock file,
|
|
3589
|
+
records only. `claimRun` (`src/monthly.js`) writes the run's row FIRST (it carries its `pid` and
|
|
3590
|
+
`capUsd`), then reads `runs.jsonl`: earlier claims come first, and only the claims ABOVE its own row count
|
|
3591
|
+
(rows below it yield to it). A claim above is held at the larger of its spend so far and its cap while
|
|
3592
|
+
its pid is a live bareloop runner (`isLiveRunner`, `src/runlist.js`, also what `--resume` uses to refuse a
|
|
3593
|
+
resume against a live run: the process is a node executable with a `bareloop`/`bareloop.mjs`/`run-u.mjs`/
|
|
3594
|
+
`u-watchdog.mjs` argument, or is itself named so — a recycled pid running some other program never holds a
|
|
3595
|
+
cap, though a recycled node program with such an argument still would), however quiet its spine (`runIsAlive`, `src/runlist.js`, is the one "is this run alive" rule: the panel's [▶] / [?] glyph reads it too; `--resume` also refuses on the pid in the predecessor's own run-list row, not only the watchdog's record); a claim whose process is gone is closed by whoever
|
|
3596
|
+
finds it, with a note (`{runid, type:'settled', by:<finder>, reason:'process gone', spentUsd:<floor>}`) and
|
|
3597
|
+
counts its real spend. If the sum fits, the run goes; if not, it appends `{type:'released',
|
|
3598
|
+
reason:'refused'}` and refuses with the exact text, nothing spent. A run whose row is listed (claimed, or with no limit set) that then exits at $0 before its spine has a first record (a refused
|
|
3599
|
+
`--resume` patient, a failing tree setup) appends `{type:'released', reason:'not started'}` too, so it leaves no row; so does a $0 refusal after the spine exists (`SOURCE-MANIFEST-RED`, `DESTINATION-RED`, `CLOSE-TIMING-RED`). A row that cannot be written, or an
|
|
3600
|
+
unreadable config, refuses too — never "no limit". When a run ends it appends its own
|
|
3601
|
+
`{runid, type:'settled', by:<runid>, spentUsd, spendComplete, at}`. Two runs may close the same dead claim;
|
|
3602
|
+
the FIRST note for a runid is authoritative. `readRunList` folds these entries out of the run rows
|
|
3603
|
+
(`events`; a `released` claim's row is not listed). Month totals still read real spend off the spines. An
|
|
3604
|
+
older row with no `pid` keeps the spine-mtime rule (`DIED_MTIME_MS`: a spine written to within it is in
|
|
3605
|
+
flight, at its leg cap). The refusal check parses only the spines of runs listed in the current local month (plus any claim still held by a live runner); the all-time readers still read every run. A run refused because an earlier claim was itself later refused is the accepted
|
|
3606
|
+
safe-side cost of doing without a lock.
|
|
3527
3607
|
Only the refusal reserves the cap (`monthSpend().reservedUsd`); the Money tab's month figure is
|
|
3528
3608
|
real spend.
|
|
3529
3609
|
|
|
@@ -3798,7 +3878,7 @@ coldReset, the close-first precheck — is untouched: `--spec` only NAMES the jo
|
|
|
3798
3878
|
a `--job` row does. `src/authorrun.js`'s (lifted out of `scripts/run-author.mjs` by
|
|
3799
3879
|
PANEL-BUILD.md P0) "SIGNING PREPARED — NOT SIGNED" screen now
|
|
3800
3880
|
prints the ready-to-paste `--spec` command (real path, real `--approve <hash>`, the
|
|
3801
|
-
|
|
3881
|
+
key variable the job's key row really reads (`keyNameFor`, F187/F207)) — a person finishing the interview alone can reach a
|
|
3802
3882
|
running job with no `jobs/` edit and no JOBS-table row.
|
|
3803
3883
|
|
|
3804
3884
|
## Architecture
|
|
@@ -3836,9 +3916,10 @@ merge.** What an adopter can plug into `runJob` (`src/run.js:205`):
|
|
|
3836
3916
|
`emit(type, data)` that appends one JSONL line per event; any function with that
|
|
3837
3917
|
signature can stand in as a custom sink, but it is write-only — nothing in bareloop reads
|
|
3838
3918
|
the spine back, so a custom sink cannot feed anything into a run's own decisions.
|
|
3839
|
-
- **`rates` is
|
|
3840
|
-
|
|
3841
|
-
|
|
3919
|
+
- **`rates` is data the run doors hand in, not a behaviour hook.** `runJob` / `runPlan` take
|
|
3920
|
+
`rates` / `judgeRates` (`{in, out}`, USD per 1K) resolved from the customer's own price on a key
|
|
3921
|
+
row (`ratesFor`, `src/providerrows.js`); bareloop keeps no price table and never picks a price
|
|
3922
|
+
itself. A price is the customer's, and sets what the cap means.
|
|
3842
3923
|
|
|
3843
3924
|
Everything else in `runJob`'s options — budgets, caps, the close, the fence, merge/publish
|
|
3844
3925
|
— is arbiter territory and is never adopter-suppliable as behavior, only as signed,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bareloop",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.34.0",
|
|
4
4
|
"description": "Workflows that earn their own design, with receipts — agent-authored workflows for repeated, long, verifiable jobs that improve across runs under an un-gameable gate.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
package/src/authorflow.js
CHANGED
|
@@ -1438,12 +1438,14 @@ export function makeCostBook({ ceilingUsd = null, onCall = () => {} } = {}) {
|
|
|
1438
1438
|
* an estimate, and it is why tool mode does not cost double what text mode did.
|
|
1439
1439
|
*
|
|
1440
1440
|
* `cacheMessages` (F18) and `maxTokens` (F30) are not optional on this stack.
|
|
1441
|
-
*
|
|
1441
|
+
* `rates` is the customer's own price for this provider's key row (USD per 1K, `ratesFor`);
|
|
1442
|
+
* absent = bare-agent's built-in guess, exactly as before.
|
|
1443
|
+
* @param {any} provider @param {{system?: string, maxTokens?: number, rates?: {in: number, out: number}|null}} [o]
|
|
1442
1444
|
* @returns {(messages: any[], tools: any[], opts?: any) => Promise<any>}
|
|
1443
1445
|
*/
|
|
1444
|
-
export function makeLoopGenerate(provider, { system = AUTHOR_SYSTEM, maxTokens = AUTHOR_MAX_TOKENS } = {}) {
|
|
1446
|
+
export function makeLoopGenerate(provider, { system = AUTHOR_SYSTEM, maxTokens = AUTHOR_MAX_TOKENS, rates = null } = {}) {
|
|
1445
1447
|
return async (/** @type {any[]} */ messages, /** @type {any[]} */ tools, /** @type {any} */ opts = {}) => {
|
|
1446
|
-
const loop = new Loop({ provider, system });
|
|
1448
|
+
const loop = new Loop({ provider, system, ...(rates ? { rates } : {}) });
|
|
1447
1449
|
const wired = tools.map((t) => ({
|
|
1448
1450
|
...t,
|
|
1449
1451
|
execute: async (/** @type {any} */ a) => { const r = await t.execute(a); loop.stop(); return r; },
|
package/src/authorjob.js
CHANGED
|
@@ -419,6 +419,7 @@ function composerRefusal(reds) {
|
|
|
419
419
|
* verdictType?: string|null,
|
|
420
420
|
* questions?: Record<string|number, string>|null, generate?: Function, provider?: any,
|
|
421
421
|
* seedRef?: string|null, scout?: any, listing?: any, ceilingUsd?: number|null, judgeModel?: string|null,
|
|
422
|
+
* rates?: {in: number, out: number}|null,
|
|
422
423
|
* onPhase?: (phase: string, data?: any) => void,
|
|
423
424
|
* onCall?: (call: {label: string, costUsd: number|null, unpricedRounds: number}) => void,
|
|
424
425
|
* seedFn?: Function, scoutFn?: Function, listingFn?: Function,
|
|
@@ -435,6 +436,7 @@ export async function authorCloseForJob({
|
|
|
435
436
|
answers, repoPath = null, lang, verdictType = null, questions = null,
|
|
436
437
|
generate, provider = null, seedRef = null, scout = null, listing = null,
|
|
437
438
|
ceilingUsd = null,
|
|
439
|
+
rates = null,
|
|
438
440
|
// Q2 IS GONE from the numbered interview (PRD item 33 M3 piece 3) — the
|
|
439
441
|
// fence it used to describe in prose is now Destination's own proven
|
|
440
442
|
// `writeScope`, and this is what carries it into the composer prompt
|
|
@@ -578,7 +580,7 @@ export async function authorCloseForJob({
|
|
|
578
580
|
// real tree. `attempts` is the ceiling the scout enforces on itself, so the
|
|
579
581
|
// reader knows how many the line could repeat for.
|
|
580
582
|
onPhase('scout', { attempts: SCOUT_ATTEMPTS });
|
|
581
|
-
survey = await scoutFn({ workdir, provider, ceilingUsd, onCall });
|
|
583
|
+
survey = await scoutFn({ workdir, provider, ceilingUsd, onCall, ...(rates ? { rates } : {}) });
|
|
582
584
|
// …and the survey's own VERDICT, said the moment it lands. `state` is what
|
|
583
585
|
// every downstream refusal keys on (F59: absent is never "no facts needed"),
|
|
584
586
|
// and the fact count is the one number that says whether the ladder came
|