bareloop 0.40.0 → 0.41.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 +33 -0
- package/bareloop.context.md +97 -124
- package/package.json +2 -2
- package/src/authorflow.js +9 -9
- package/src/authorjob.js +4 -4
- package/src/authorrun.js +25 -25
- package/src/authorscout.js +4 -4
- package/src/bundle.js +7 -4
- package/src/bundlerun.js +4 -1
- package/src/cli.js +1 -1
- package/src/config.js +129 -29
- package/src/draftspend.js +4 -4
- package/src/index.js +2 -4
- package/src/interviewrun.js +17 -25
- package/src/job.js +13 -7
- package/src/judged.js +11 -43
- package/src/keysfile.js +15 -7
- package/src/kinds.js +2 -2
- package/src/modellist.js +84 -0
- package/src/monthly.js +23 -43
- package/src/panel/authorroutes.js +19 -14
- package/src/panel/authorsession.js +62 -58
- package/src/panel/index.html +520 -145
- package/src/panel/runroutes.js +3 -0
- package/src/panel/server.js +35 -15
- package/src/panel/settingsroutes.js +232 -173
- package/src/planrun.js +2 -2
- package/src/providerrows.js +226 -141
- package/src/providers.js +58 -332
- package/src/providershapes.js +18 -0
- package/src/replay.js +1 -1
- package/src/thinking.js +130 -0
- package/src/userrun.js +50 -73
- package/types/authorflow.d.ts +5 -5
- package/types/authorjob.d.ts +2 -2
- package/types/authorscout.d.ts +2 -2
- package/types/config.d.ts +23 -6
- package/types/draftspend.d.ts +8 -8
- package/types/index.d.ts +1 -1
- package/types/job.d.ts +5 -5
- package/types/judged.d.ts +5 -36
- package/types/keysfile.d.ts +15 -4
- package/types/modellist.d.ts +50 -0
- package/types/monthly.d.ts +4 -3
- package/types/panel/authorsession.d.ts +2 -9
- package/types/panel/server.d.ts +14 -4
- package/types/providerrows.d.ts +192 -99
- package/types/providers.d.ts +34 -159
- package/types/providershapes.d.ts +10 -0
- package/types/thinking.d.ts +90 -0
- package/types/userrun.d.ts +6 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,39 @@ 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.41.0] - 2026-10-12
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Settings > Providers, rewritten from fwdloop's layout (P8).** One shared grid: a key row (API shape, Base URL, the "old max_tokens" tick, Tokens, `[ Test ] [ ⟳ Refresh ]`) with its model lines under it (Use tick, In / Out $/1M, tokens), a folded summary, `label : value` lines below 1280px, and the KEYS strip stacking below 900px. **Test** and **Refresh** are one free `GET <Base URL>/models` (`src/modellist.js`); `gemini-api` is now testable. `Open keys folder` stays in the KEYS strip.
|
|
13
|
+
- **Model lines.** Config gains `models.<ENV>` — a list of `{ model, use, inPerM, outPerM }` — and `keys.<ENV>` becomes `{ shape, baseUrl, legacyMaxTokens }`. A price is a pair or nothing, a ticked line needs both, 0 / 0 is a chosen price. An older `keys.<ENV>.{name,priceInPerM,priceOutPerM}` still reads as one line until the row's first save.
|
|
14
|
+
- **"Thinking on" tick (per job).** A tick under Model in the New job card, unticked by default, saved in the signed job as the optional `thinking` boolean (absent = off; an old spec and any unticked job keep their hash). It covers drafting and the rubric judge, never the worker. `src/thinking.js` is the one owner; bare-agent (^0.52.0) says what each model can switch and reports `thinkingStatus`, which the draft log and the drafting `thinking` row show.
|
|
15
|
+
- **`--model <id>` and `--provider <KEY>`** on `run-u`, `run <bundle>` and the resume route name the Settings line a run works on (for a spec that names no model); `interview` takes `--model` and writes it into the draft.
|
|
16
|
+
- `tests/no-hardcode.test.js`: a search of `src/` and `bin/` finds no provider name, model name, web address or price in code, except an allow-list with a reason per entry.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- **Resume of a pre-P8 run whose signed spec names no model** opens the Resume card's Model box (it was locked, so the person could not act on `--model`). The menu offers only lines of the spec's API shape (and endpoint, when the spec names one) and shows `Tick a <shape> model in Settings > Providers.` when none match; Sign & resume stays blocked until a usable line is picked, and the pick is sent as model + provider. The spec and its hash are untouched. Changing the Model line now blocks Start/Sign until its key check returns, and a late answer for a line no longer selected is ignored (the New job card shares this path).
|
|
21
|
+
- **Chat's Model menu** lists only ticked, priced lines as `<KEY> · <model>`; a card carries `provider` (the key's env name) beside `model`, and a stale pick is refused at $0 with one of three plain sentences.
|
|
22
|
+
- **A job's key is its row's own variable name.** There is no built-in key name, tier table, default model, default address or per-model gating: the "old max_tokens" tick on the row decides the output-cap spelling.
|
|
23
|
+
- **Existing DeepSeek rows need one tick.** An existing DeepSeek row (or any OpenAI-compatible server that wants the old `max_tokens` field) must have "old max_tokens" ticked once in Settings > Providers: the built-in per-model rule that did this is gone (no-hardcode ruling), and until the tick is set the output cap may not bind (F149).
|
|
24
|
+
- The Money tab groups spend by the Providers row that served it (a run no row serves lands under its own shape and address) and carries tokens per model.
|
|
25
|
+
- A fresh keys file holds two comment lines and no preset key.
|
|
26
|
+
- Dependencies: `bare-agent` ^0.51.0 to ^0.52.0.
|
|
27
|
+
- The exported bundle README tells the user to put their provider's API key in their keys file (the name their Settings row uses), with no vendor.
|
|
28
|
+
|
|
29
|
+
### Removed
|
|
30
|
+
|
|
31
|
+
- **Balance** (the Settings column, both routes and the `anthropicBalanceNote` config field).
|
|
32
|
+
- Built-in tiers and keys: `*_TIER_MODELS`, `envKey`, `OPENAI_MODEL_OPTIONS`/`paramsFor`, `PRESET_KEY_NAMES`, the vendor default addresses, the `--model sonnet|haiku` aliases, the vendor map in the Money tab.
|
|
33
|
+
- **`JUDGE_MODEL`** is no longer exported from the package root or `src/judged.js` (a public-API removal: pass your own model id). `resolveWorkerModel` takes `suppliedModel` instead of `defaultModel` and throws when no model is named.
|
|
34
|
+
- The bench row `bareguard-types-deepseek` is renamed `bareguard-types-gateway` (spec file renamed; contents and hash untouched; the patient directory it names is now `bareloop-patients/bareguard-u-gateway`).
|
|
35
|
+
|
|
36
|
+
### Breaking
|
|
37
|
+
|
|
38
|
+
- **The config shape changed.** Every row that relied on a built-in default (shape, address, model, price) must have its API shape, Base URL and model lines entered or ticked ONCE in Settings. A row on a server that ignores `max_completion_tokens` (F149) needs "old max_tokens" ticked by hand, once, before a paid run: an output cap that does not bind is a money hazard.
|
|
39
|
+
- **Specs signed before this release that name no `model` (or no `baseUrl`)** have nothing to run on until a line is named at run start: `--model <id>` (the hash is untouched). A signed model no ticked line carries is refused at $0.
|
|
40
|
+
|
|
8
41
|
## [0.40.0] - 2026-10-11
|
|
9
42
|
|
|
10
43
|
### Added
|
package/bareloop.context.md
CHANGED
|
@@ -56,20 +56,16 @@ inherited rule carries the green that minted it and the contrast that attributed
|
|
|
56
56
|
with `anthropic-api` on cost, and budgets don't transfer between them.
|
|
57
57
|
`job.provider === 'clipipe-subscription'` with no `nativeProvider` supplied is
|
|
58
58
|
`interpreter-red`, never a silent fall-back to the metered API (`src/planrun.js:1073`).
|
|
59
|
-
`provider:
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
`legacyMaxTokens`, because DeepSeek silently ignores `max_completion_tokens` and an
|
|
70
|
-
output cap that does not bind is a money hazard. The judge's calls additionally carry the per-call `thinkingMode: 'off'` where bare-agent's `thinkingSupport` says the model is switchable (read through `judgeCallOptions`, applied by `defaultJudgeLoop`; F192) because deepseek-flash's default thinking ate the 4000-token locate budget; workers are untouched. `provider: 'gemini-api'` (PRD item 31.3)
|
|
71
|
-
takes the same `Loop` path, reads `GEMINI_API_KEY`, and maps two real tiers
|
|
72
|
-
(`sonnet` → `gemini-2.5-pro`, `haiku` → `gemini-2.5-flash`). It is
|
|
59
|
+
`provider` names an API SHAPE — a wire protocol, never a vendor: `anthropic-api`, `openai-api` (an OpenAI-compatible endpoint, with an optional job-level
|
|
60
|
+
`baseUrl`: https, or http on loopback only; credentials in the URL are a red) or `gemini-api`. Nothing about a vendor, a model id, an address, a price or a
|
|
61
|
+
key name is built in (P8): a job's WORKER runs on a **Settings line** — a key row (its API shape, Base URL and "old max_tokens" tick) with a model line the
|
|
62
|
+
person ticked and priced (see "Settings > Providers" below). The provider table lives in `src/providers.js` (`resolveProvider`/`makeProvider`/
|
|
63
|
+
`buildRunnerProviders`) and holds only the bare-agent client class and endpoint option name per shape; an unknown provider name THROWS there, it never falls
|
|
64
|
+
back to a default. The request-key spelling is the ROW's tick, never a model-id table: a server that ignores `max_completion_tokens` (F149) does not bind
|
|
65
|
+
the output cap until the "old max_tokens" box is ticked, and an output cap that does not bind is a money hazard, so tick it on that row before a paid run
|
|
66
|
+
(`makeProvider(shape, { legacyMaxTokens })`, forwarded for `openai-api` only). The judge's calls carry the per-call `thinkingMode` the job's signed "Thinking on" tick asks for
|
|
67
|
+
(`src/thinking.js`, applied by `defaultJudgeLoop`; F192: unticked = off, because a model's default thinking can eat the 4000-token locate budget); workers are untouched. A plan step's
|
|
68
|
+
`model: "sonnet"` is a documented schema token meaning "this job's worker model" (`STEP_MODELS`), not a model id. `gemini-api` (PRD item 31.3) is
|
|
73
69
|
**ADMITTED-PENDING-PROBE**: it has ZERO runs, and the probe rule is not waived for it —
|
|
74
70
|
`PROBE_STATUS` (`src/providers.js`) is the machine-readable form of that debt, and
|
|
75
71
|
`probeWarningLines(provider)` renders the loud launch-time marker `src/userrun.js`
|
|
@@ -85,11 +81,11 @@ inherited rule carries the green that minted it and the contrast that attributed
|
|
|
85
81
|
at $0 through machinery that treats $0 as a real price. The JUDGE IS NO LONGER PINNED
|
|
86
82
|
(PRD item 32): `resolveJudge` (exported) resolves it per job — the spec's signed
|
|
87
83
|
`judge: {provider, model}` if it names one, else **this job's own worker provider and
|
|
88
|
-
model**, so a
|
|
84
|
+
model**, so a job on any provider judges on its own provider and needs no second
|
|
89
85
|
account. There is no library fall-back: a run that cannot name its judge has not wired
|
|
90
86
|
one, and the judged stage instrument-STOPS rather than grading under an identity nobody
|
|
91
|
-
chose. `JUDGE_MODEL`
|
|
92
|
-
The judge's key
|
|
87
|
+
chose. The old `JUDGE_MODEL` constant is gone (a vendor model id is not a public export).
|
|
88
|
+
The judge's key is the resolved judge row's own variable (the worker's row when the judge shares its shape), with `JUDGE_API_KEY`
|
|
93
89
|
as a role-named override in front of it. The bundle runner
|
|
94
90
|
(`bareloop run`) is a door to this same engine (`src/bundlerun.js` → `src/userrun.js`), so it
|
|
95
91
|
reads whatever key the bundle's provider names (keys file or env) and refuses a missing one
|
|
@@ -155,13 +151,14 @@ minting claim, or the shell-owned retry cap — all unknown-field reds.
|
|
|
155
151
|
|---|---|---|
|
|
156
152
|
| `job` | kebab-case slug | |
|
|
157
153
|
| `description` | non-empty string | |
|
|
158
|
-
| `provider` | `anthropic-api` \| `openai-api` \| `gemini-api` \| `clipipe-subscription` | menu (`PROVIDERS`)
|
|
154
|
+
| `provider` | `anthropic-api` \| `openai-api` \| `gemini-api` \| `clipipe-subscription` | menu (`PROVIDERS`): an API SHAPE (a wire protocol), part of the lineage key by definition. Only `anthropic-api` is guaranteed (F48); the shapes go through the provider factory (`src/providers.js`) — see "Worker surface" above for the `baseUrl` field and the row's old-max_tokens tick; `gemini-api` (PRD item 31.3) is ADMITTED-PENDING-PROBE — zero runs, and every launch says so (`PROBE_STATUS`/`probeWarningLines`); `clipipe-subscription` drives the worker natively and needs `opts.nativeProvider` |
|
|
159
155
|
| `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) |
|
|
160
156
|
| `cadence` | `{ unit: hour\|day\|week, every: 1..30 }` | validated now, consumed at N5 (Scheduler) |
|
|
161
157
|
| `budgetUsd` | finite `0 < n <= shell cap` | ceiling chain: workflow ≤ job ≤ shell — each layer may tighten, never exceed |
|
|
162
158
|
| `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 |
|
|
163
159
|
| `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 |
|
|
164
|
-
| `model` | optional non-empty string, exact
|
|
160
|
+
| `model` | optional non-empty string, the exact model id of a line ticked in Settings > Providers | the WORKER's model. A job carries its own model (P8: bareloop has no default); a spec signed before that (no `model`, perhaps no `baseUrl`) runs on the line `--model <id>` names (plus `--provider <KEY>` when two keys tick it) — the signed hash is untouched. 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 |
|
|
161
|
+
| `thinking` | optional boolean | the job's "Thinking on" tick (P8): absent = off, and an old spec keeps the hash it always had; present it is signed like `model`. Covers DRAFTING and the rubric JUDGE only — the worker is never handed a thinking option (see "Thinking on" below) |
|
|
165
162
|
| `judge` | optional `{provider, model}`, both non-empty; `provider` from the `PROVIDERS` menu | the signed JUDGE override (PRD item 32). Absent, the judge resolves to this job's own `provider`/`model` (`resolveJudge`) — which is what lets a non-Anthropic job judge without a second account. Present, it pins a judge deliberately different from the worker (an independent reader). Both halves are required together: a judge with a provider and no model is an identity nobody signed. Part of the signed hash, so changing it kills the signature and forces recalibration. Unknown nested keys red — no smuggling level exists in a signed spec. The drafting agent cannot express it: naming your own examiner is arbiter territory |
|
|
166
163
|
| `writeScope` | array of contained globs | the operator's outer fence; the plan's own scopes must fit inside it, same containment code |
|
|
167
164
|
| `steps` | RETIRED | operator-authored `steps[]` was deleted (PRD v1.32); a spec carrying it reds `shape-retired:steps` by name rather than half-running |
|
|
@@ -363,7 +360,7 @@ middle writes; `validatePlan` gates it against the SIGNED job spec before tokens
|
|
|
363
360
|
| `steps[].tools` | non-empty unique subset of the SPEC ceiling | a verb beyond the ceiling reds `verb-escape` with the verb as structured data (overreach, distinct from the operator-side `request-red`); `run` is `verb-escape` at every layer |
|
|
364
361
|
| `steps[].rounds` | int 1..shell cap (default 40) | the step's per-attempt tool-round bound (the Gate's `maxTurns` natively) |
|
|
365
362
|
| `steps[].target` | path inside the fence | v1.18 deliverable; REQUIRED on write-granted steps |
|
|
366
|
-
| `steps[].model` | optional; `sonnet` (`STEP_MODELS`) | the step's model TIER — a menu, never a model string the agent spells (
|
|
363
|
+
| `steps[].model` | optional; `sonnet` (`STEP_MODELS`) | the step's model TIER — a menu, never a model string the agent spells (`sonnet` is a schema token meaning this job's worker model; the runner maps it, there is no tier table). `opus` is deliberately absent: reserved for work the human assigns, never plan-selectable; `haiku` was removed 2026-08-06 as a reversible ATTRIBUTION PROBE (F87, see `STEP_MODELS`), so the menu is one entry deep and the field is no longer offered in the drafting prompt. The field and its validator branch STAY: `model: "sonnet"` still validates, a stored bridge declaring it still loads, and restoring haiku is a one-token edit — this is not a finding that haiku is incapable. **The narrowing binds only what the AGENT may express.** Which real model a tier maps to, and what the caller runs as its own default worker model, stay the OPERATOR's: `providerFor` is yours, and the in-repo runner's `--model haiku` probe knob is untouched (PRD v1.36) — the runner's tier map is deliberately WIDER than the plan menu. Off-menu reds `invalid-value`. A step naming a tier when the caller supplied no `providerFor` factory is an `interpreter-red` STOP, never a silent run on the default tier |
|
|
367
364
|
| `steps[].attempts` | optional; int `>= 1` | **RETIRED and INERT (F79) — it bounds nothing.** A step's iterations are governed by the STRIKE LADDER (below), which is shell-owned and inexpressible from the plan. The field is no longer offered in the drafting prompt and the runner ignores it; it is deliberately still ACCEPTED, because stored bridge plans carry it and a plan minted under one runner's number must not red under another's. The shape is still checked (a known field holding garbage stays a `bounds` red); the old `<= capRuns` ceiling is gone with the cap it named. It was previously TIGHTEN-only — which meant a step that needed MORE iterations could not ask, and drafters measurably tightened it instead (F77) |
|
|
368
365
|
| `steps[].scope` | optional; one value from the SAME menu `tree-changed` uses | narrows this step's live WRITE fence to a subset of the signed `writeScope` — chosen from the offered scope menu (below), never authored. Off-menu reds `invalid-value` carrying the menu |
|
|
369
366
|
| `steps[].exit` | 1..2 items (`MAX_EXITS_PER_STEP`), ALL must pass (AND-only, no OR/NOT) | closed menu (`EXIT_TYPES`): `artifact-written(path, pattern?)` · `tree-changed(scope)` · `json-valid(path)` · `check-passes(name)`. **`tree-changed.scope` is a MENU, not a glob the agent authors** — the runner enumerates the signed fence entries plus the real directories beneath them (shallowest-first, capped at `MAX_SCOPE_MENU`=24; `legalScopes` in `src/plan.js`), `planPrompt` lists them verbatim, and `validatePlan` accepts membership only — the SAME array on both sides, so what was offered is what is accepted; omitted, it derives from `writeScope` alone — never a free-text fallback. An off-menu value that escapes the fence still reds `scope-escape` (the ledger's attribution class), an in-fence unoffered value reds `invalid-value` carrying the menu. `runPlan` emits `scope-menu {offered, truncated, offerableCount?, cap?}` so a capped menu is never silently complete. `check-passes` must name a stage on the close's DERIVED menu (`check-unknown` red names the offered menu — no operator authors this list, it comes straight from `checkMenu(close)`); on a write-granted step it must be paired with `tree-changed` (`exit-illegal` — the seed tree is green, a lone check would pass untouched, F17/F46). **`check-passes` also requires a write-class verb (`write`/`edit`) on the SAME step** (`exit-illegal` — BREAKING for plan authors: a read-only "verify" step carrying a check used to validate and no longer does). A failing check's gap is re-delivered to that step's OWN worker, so a step holding a check must be able to act on it; a read-only one is a mailbox with no hands and stalls to cap on a byte-identical gap (measured on 4 of 4 drafted plans). The check belongs on the step that fixes; the run's final verification is the operator's close, which the agent never authors. The rule is stated identically in the drafter prompt and the validator, so the two can never disagree — and it is suppressed when `tools` failed to parse (the step's hands are unknowable then, and the real defect already redded: one defect, one red). `artifact-written.pattern` must compile AND survive a ReDoS shape check — an unbounded quantifier over a group that itself repeats unboundedly (`(a+)+`, `(\d*)*`, even wrapped: `((a+))+`) is an `invalid-value` red (F49, catastrophic-backtracking footgun; rewrite without a repeated group inside a repeat). Exits verify FORM, not truth — progress gates; the operator's close stays the one arbiter |
|
|
@@ -647,26 +644,29 @@ plan with the first leg's spend carried as prior spend and `offerQuestions: fals
|
|
|
647
644
|
plan's open questions) are a separate, unchanged mechanism: they are asked before drafting, about the plan; try-3 questions
|
|
648
645
|
are asked after two failed drafts, about a job line.
|
|
649
646
|
|
|
650
|
-
**
|
|
651
|
-
|
|
652
|
-
(
|
|
653
|
-
|
|
654
|
-
(`
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
`
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
`'
|
|
666
|
-
|
|
667
|
-
`thinking`
|
|
668
|
-
`
|
|
669
|
-
|
|
647
|
+
**Thinking on (per job; hamr 2026-10-11).** The New job card has a "Thinking on" tick under Model, unticked by default, saved in the SIGNED job as the optional
|
|
648
|
+
boolean `thinking` (absent = off; an old spec and any unticked job keep the hash they would have had; a ticked job's `jobSpecHash` and `workflowKey` differ). It
|
|
649
|
+
covers DRAFTING (scout, confirm, author, revise, structure retries) AND the rubric JUDGE; the WORKER is never handed a thinking option. A Reuse card does not
|
|
650
|
+
offer the tick: it never drafts and the judge follows the SIGNED value, so the 10/10 calibration gate runs with the same tick as the later grading
|
|
651
|
+
(`defaultJudgeLoop({ ..., thinking })` takes it in all three constructors: the run, `run-author`, the panel's gate). bareloop keeps NO per-model thinking list and no
|
|
652
|
+
vendor regex: bare-agent (>= 0.52) owns the request shapes, and `thinkingSupport(kind, model, { baseUrl })` (a pure lookup, no network) answers `true` (measured
|
|
653
|
+
switchable), `false` (measured NOT switchable) or `'unverified'`. `src/thinking.js` is the ONE owner of what a call sends:
|
|
654
|
+
|
|
655
|
+
| tick | support `true` | support `false` | support `'unverified'` |
|
|
656
|
+
|---|---|---|---|
|
|
657
|
+
| on | `{thinkingMode:'on'}` | `{}` (the box is greyed and cannot be on) | `{thinkingMode:'on'}` |
|
|
658
|
+
| off | `{thinkingMode:'off'}` | `{}` (the model thinks as it would) | `{thinkingMode:'off'}` (bare-agent decides) |
|
|
659
|
+
|
|
660
|
+
bare-agent 0.52 never throws because a model cannot switch: the call always runs and reports `GenerateResult.thinkingStatus` (`'applied'` / `'unsupported'` /
|
|
661
|
+
`'ignored'` / `'unknown'`; `Loop.run()` surfaces the worst across rounds). For an unmeasured model it tries the other shapes, then ONE plain request
|
|
662
|
+
(`'unsupported'`, the result kept), so drafting does not break. The raw `thinking` option is never sent. The per-call option is threaded through
|
|
663
|
+
`makeLoopGenerate(provider, { draftThinking: 'on' | 'off' })` and `authorCloseForJob({ draftThinking })` (any other value throws, an operator param); the panel builds it
|
|
664
|
+
from `card.thinking`. `GET /api/author/models` answers each Model line with `thinking: { support, canChange, say }`: the page decides nothing: `canChange:false` greys
|
|
665
|
+
AND unticks the box with `say` ("can't change this for this model"); `'unverified'` leaves it enabled with no note. Each panel `draft-log.jsonl` `call` entry records
|
|
666
|
+
`thinking` (`'on'` / `'off'`, the tick), `thinkingSupport` (`true` / `false` / `'unverified'`) and, when bare-agent reported one, `thinkingStatus`. The drafting list opens
|
|
667
|
+
with a code-owned `thinking` row (`thinkingRowText`, before "checking setup"): `thinking off` / `thinking on`; `thinking off (not verified for this model)`;
|
|
668
|
+
`thinking on (this model can't change it)`; and, once a call reports a status, `thinking on (the model thought anyway)` (`'ignored'`), `thinking on (this model can't change it)`
|
|
669
|
+
(`'unsupported'`) or `thinking off (not confirmed)` (`'unknown'`) — a `step-label` record in the draft log carries the change. A Reuse workflow session drafts nothing and has no row.
|
|
670
670
|
|
|
671
671
|
|
|
672
672
|
**D5 — the mandatory guards are SHOWN and FIXED, and the battery keys off the VERDICT
|
|
@@ -950,13 +950,12 @@ down.
|
|
|
950
950
|
`--base-url` is OPTIONAL with NO default (PRD item 33 close-out — L17 named the provider but
|
|
951
951
|
never admitted the endpoint): a provider name's table entry (`src/providers.js`) can be reached
|
|
952
952
|
through an OpenAI-compatible gateway other than its own default host — DeepSeek, today's one
|
|
953
|
-
secondary, is reached as `--provider openai-api --base-url
|
|
953
|
+
secondary, is reached as `--provider openai-api --base-url <the endpoint>`; `--model <id>` names the Settings line the job runs on and lands in the draft's `model`. Absent,
|
|
954
954
|
the draft carries NO `baseUrl` field at all (never `null`, never `''`) and every provider
|
|
955
955
|
constructor defaults its own endpoint. When given, it lands in the draft's `baseUrl` field
|
|
956
956
|
beside `provider` and passes through the SAME `validateJob` pass every other field takes below,
|
|
957
957
|
at $0: `http://` to a public host, an embedded `user:pass@`, or an unparseable string reds there,
|
|
958
|
-
before anything is written. The key
|
|
959
|
-
draft's key still loads from `OPENAI_API_KEY`, and the hand-off says so.
|
|
958
|
+
before anything is written. The key is the Settings ROW's variable that carries the model line — the hand-off names it.
|
|
960
959
|
|
|
961
960
|
**Source and Destination are the interview's own FIRST TWO QUESTIONS, and they REPLACE
|
|
962
961
|
`--patient`** (PRD item 33 M3, ruling 2, `docs/product/ITEM33-BUILD.md`). Source — "a local
|
|
@@ -1021,8 +1020,8 @@ spends it.
|
|
|
1021
1020
|
|
|
1022
1021
|
**It ends by OFFERING the paid step, and the offer's default is NO.** `run-author.mjs` is a
|
|
1023
1022
|
DIFFERENT PROCESS under its own ceiling; the interview prints its exact command line (with
|
|
1024
|
-
`--budget` propagated only when one was given, and a note when the CHOSEN
|
|
1025
|
-
|
|
1023
|
+
`--budget` propagated only when one was given, and a note when the CHOSEN row's own key — the
|
|
1024
|
+
variable named by the Settings row that carries the model line, resolved through the same rows
|
|
1026
1025
|
`run-author.mjs` and `run-u.mjs` share — is unset in this shell, OR set but MALFORMED: F181 — a
|
|
1027
1026
|
value carrying a line break/control character/stray whitespace (a two-line secret-store entry,
|
|
1028
1027
|
e.g.) reads a distinct reason via `apiKeyProblem` (`src/providers.js`), never the value)
|
|
@@ -1834,9 +1833,7 @@ omitted is the cold path and byte-identical to a run nobody resumed.
|
|
|
1834
1833
|
F87; the field stays legal and one-token reversible, and the narrowing binds only what the
|
|
1835
1834
|
AGENT may express, never what the operator may run).
|
|
1836
1835
|
The tier menu is signed into the schema; mapping a tier
|
|
1837
|
-
onto a real model (and any per-model provider params) is the RUNNER's territory —
|
|
1838
|
-
forces the `sonnet` tier onto any particular model, and running haiku as your default worker
|
|
1839
|
-
model stays an operator choice the plan menu never governed — so both
|
|
1836
|
+
onto a real model (and any per-model provider params) is the RUNNER's territory — the runner maps the one `sonnet` token to the job's worker model (there is no tier table) — so both
|
|
1840
1837
|
`runJob` and `runPlan` take `providerFor?: (tier: string) => provider`. It is called ONLY for
|
|
1841
1838
|
a step that declared a tier, once per such step — the scout, the plan drafter and every
|
|
1842
1839
|
untiered step stay on `opts.provider`, so a caller that never plans tiers can omit it
|
|
@@ -1911,14 +1908,14 @@ bare-agent's `Loop` takes `rates: {in, out, cacheReadMult?, cacheWriteMult?}`
|
|
|
1911
1908
|
tokens, the two multipliers applying to the input rate for the cache-read and cache-write
|
|
1912
1909
|
tiers (defaulting to Anthropic's 0.1× / 1.25×). A caller-supplied rate is recorded as
|
|
1913
1910
|
VOUCHED rather than as a guess, and it silences bare-agent's own guesstimate warning.
|
|
1914
|
-
**Where bareloop takes it from:** the CUSTOMER's own price on the
|
|
1915
|
-
`~/.config/bareloop/config.json` (`
|
|
1911
|
+
**Where bareloop takes it from:** the CUSTOMER's own price on the model line in
|
|
1912
|
+
`~/.config/bareloop/config.json` (`models.<ENV NAME>[].inPerM` / `outPerM`, USD per 1M
|
|
1916
1913
|
tokens — see "Settings" below). `runJob` / `runPlan` accept `rates` (and `judgeRates`) as data and
|
|
1917
1914
|
hand them to the one `Loop` that drives worker, scout and planner rounds; the run doors resolve
|
|
1918
1915
|
them once. No price on the row = no `rates` key anywhere = the guesstimate above, byte-identical to
|
|
1919
1916
|
before. bareloop keeps no price list of its own, ever.
|
|
1920
1917
|
|
|
1921
|
-
**The provenance is on the record, per round (`rateSource`).** The pinned bare-agent (`^0.
|
|
1918
|
+
**The provenance is on the record, per round (`rateSource`).** The pinned bare-agent (`^0.52.0`)
|
|
1922
1919
|
carries it on every metering payload, and bareloop forwards it VERBATIM (`rateSourceFields`,
|
|
1923
1920
|
`src/planrun.js`): every API `worker-round` (worker, scout, planner, fix loop) carries it beside
|
|
1924
1921
|
`pricing` today — `'caller'` when a customer price is on the key's row, else `'tier'`/`'default'`
|
|
@@ -3254,7 +3251,7 @@ worktree, before the engine is ever reached.
|
|
|
3254
3251
|
some other module's own generic `main`). `cliMain(argv, deps)` is the in-process test/
|
|
3255
3252
|
integration seam: pass `deps.provider` (and optionally `providerFor`/`judgeProvider`) and the
|
|
3256
3253
|
real key check is skipped entirely — a scripted provider *is* the run, the
|
|
3257
|
-
same seam `tests/planrun
|
|
3254
|
+
same seam `tests/planrun-*.test.js` uses. `deps` also accepts `env`, `stdout`, `stderr`, `cwd`,
|
|
3258
3255
|
`now`, `stdin` — omit any of them and the real `process.*` equivalent is used.
|
|
3259
3256
|
|
|
3260
3257
|
Bare `bareloop` (no sub-command) prints a numbered menu — `1 export 2 run 3 history q
|
|
@@ -3493,7 +3490,7 @@ key you want first (the panel picks the row by the Model menu).
|
|
|
3493
3490
|
(`src/panel/authorsession.js`) that runs `prepareSource`/`detectLanguage`/`validateJob`/
|
|
3494
3491
|
`authorCloseForJob`/`assembleSpec`/`prepareSigning` in-process, exactly the library calls
|
|
3495
3492
|
`bareloop interview`/`bareloop author` already make — never a script's own readline loop,
|
|
3496
|
-
never a reimplementation of any gate. `GET /api/author/model-check?model=<id>` is the $0 readiness probe the job card runs
|
|
3493
|
+
never a reimplementation of any gate. `GET /api/author/model-check?provider=<KEY>&model=<id>` is the $0 readiness probe the job card runs
|
|
3497
3494
|
before Start (`checkProviderReachable`, `src/providers.js` — a models-list GET only, never a
|
|
3498
3495
|
completion; the page sees the key's NAME and a status word, never the value). `POST
|
|
3499
3496
|
/api/author/:id/check-deps` is the install-gap's "Check again" (the main button's wording while waiting on an install) (`phase:'install-needed'`:
|
|
@@ -3574,8 +3571,9 @@ key you want first (the panel picks the row by the Model menu).
|
|
|
3574
3571
|
part stays running; a part that finished before the death keeps its own status. The page's step map is a row of fit-to-text chips that flow and wrap (no truncation, no horizontal scroll), `[sign title]` each, in plan order, with an arrow between neighbours and a leading arrow on a wrapped line; the status word (from the same table) rides in the chip's tooltip, a retried step has a dashed border and a `↻N` tries mark, a leg boundary a dotted right edge, and a click jumps to that part's Audit row (the live part reads `[▶]`); the step cards and Audit rows
|
|
3575
3572
|
show the word only; no surface says "done".
|
|
3576
3573
|
**Resume (item 2)** → `POST /api/runs/:runid/resume` (`src/panel/runroutes.js`), behind
|
|
3577
|
-
`checkHumanGuard` (token + own address, like `/api/author/*`); body `{budgetUsd?, maxWallMin?}`
|
|
3578
|
-
(blank = the signed caps
|
|
3574
|
+
`checkHumanGuard` (token + own address, like `/api/author/*`); body `{budgetUsd?, maxWallMin?, model?, provider?}`
|
|
3575
|
+
(blank = the signed caps; `model` / `provider` — a key's env name — are sent only for an older spec that names no model: they ride the
|
|
3576
|
+
spawn as `run-u`'s `--model` / `--provider`, and the signed hash is untouched). `404` unknown run; `409` when `resumePlanFor` says the engine would
|
|
3579
3577
|
refuse (`Resume is not available for this run (why).`); `400` for a bad cap, a money cap at or below what is already spent (or a time cap at or below the time
|
|
3580
3578
|
used) — refused at $0 before any write or spawn — or the monthly $
|
|
3581
3579
|
limit (same library check and text as Sign & run). If a cap changed, the new spec is written as
|
|
@@ -3729,11 +3727,11 @@ key you want first (the panel picks the row by the Model menu).
|
|
|
3729
3727
|
`resolved-spec.json`, F185) — exactly one, never both, never neither; `--resume
|
|
3730
3728
|
<runid|path>`; `--door <runid|path>` with `--decide <rerun|accept|pause>` and (`rerun`
|
|
3731
3729
|
only) `--text <…>`; `--review-door`; `--approve <specHash>`; `--registry <dir>` +
|
|
3732
|
-
`--workflow <name>`; `--model <
|
|
3730
|
+
`--workflow <name>`; `--model <id>` (a model id of a ticked Settings line — for a spec that names none) and `--provider <KEY>` (the key's env name, when two keys tick one id; no tier aliases); `--read-shim <off|cap|diff|all>`; `--scout
|
|
3733
3731
|
<on|off>`. No key/no injected `deps.provider`: prints the preview (invocation hints, the
|
|
3734
|
-
right key
|
|
3735
|
-
|
|
3736
|
-
set. `scripts/run-u.mjs` is unchanged in behaviour: it is the pre-existing thin adapter
|
|
3732
|
+
right key variable — the Settings row's own) and exits `0`, spending nothing. A spec that names no
|
|
3733
|
+
model and no `--model`, or a model no ticked line carries, is refused at $0 with one plain sentence
|
|
3734
|
+
(`Name the model with --model <id>: …` / `<model> is not set up: …`). `scripts/run-u.mjs` is unchanged in behaviour: it is the pre-existing thin adapter
|
|
3737
3735
|
over this same `src/userrun.js:main`, not rewired to go through `src/cli.js`.
|
|
3738
3736
|
**Printed commands are spelled the way you entered:** every re-invocation these flows print (the approve
|
|
3739
3737
|
line, resume, pause, reopen, the interview's run-author line, the author's run-u line) reads `bareloop
|
|
@@ -3852,37 +3850,26 @@ Two files in `~/.config/bareloop/`, outside any repo:
|
|
|
3852
3850
|
- **`config.json` — every setting, never a key value.** `readConfig({ home })` /
|
|
3853
3851
|
`updateConfig(patch, { home })` (atomic tmp + rename, mode 600, unknown fields kept).
|
|
3854
3852
|
Fields: `monthlyLimitUsd` (number above 0; absent or `null` = no limit; any other value present
|
|
3855
|
-
is a `ConfigError`, refused like an unreadable file), `
|
|
3856
|
-
|
|
3853
|
+
is a `ConfigError`, refused like an unreadable file), `keys.<ENV NAME>` = `{ shape, baseUrl, legacyMaxTokens }` (the Providers rows' settings: an API shape id, the typed Base URL,
|
|
3854
|
+
the "old max_tokens" tick), `models.<ENV NAME>` = a list of `{ model, use, inPerM?, outPerM? }` lines (replaced WHOLE on every save; the customer's own price below). Nothing is built
|
|
3855
|
+
in: a row with nothing saved is shape `openai-api`, no address, no lines. A secret-shaped
|
|
3857
3856
|
string anywhere in the document is refused on save. An unreadable file is a `problem` string and
|
|
3858
3857
|
a refused run start ($0), never "no limit". There is no monthly TIME limit (dropped).
|
|
3859
3858
|
|
|
3860
|
-
**Your own price —
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
(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
|
|
3875
|
-
no price it stays the built-in guess, as before. The run preview and the run tail print
|
|
3876
|
-
`price yours: in $0.006 / out $1.20 per 1M tokens (DEEPSEEK_API_KEY row)` when a price is set, and
|
|
3877
|
-
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
|
|
3878
|
-
on the dollars your price produces, so a price set too low weakens the cap (a run can do far more
|
|
3879
|
-
than you meant before it halts). When a vendor lists two prices (for example a cache-hit and a
|
|
3880
|
-
cache-miss input rate) enter the higher one. bare-agent applies its own cache multipliers (read 0.1×,
|
|
3881
|
-
write 1.25× of the input rate) to cache tokens; they are not configurable here, so cache tokens are
|
|
3882
|
-
priced from your input price by that rule. A price applies from the next run: **past runs are never
|
|
3883
|
-
re-priced** (a spine and the run list keep the dollars they booked). Not covered by a price: a native
|
|
3884
|
-
(CLI) session, which reports its own cost, and the reuse engine (`runReuse` / `selectBridge`), which
|
|
3885
|
-
resolves no key row.
|
|
3859
|
+
**Your own price — the model LINE's `inPerM` / `outPerM`.** The customer sets the price of each model they use, on that model's line under its key row, in USD **per 1M tokens**
|
|
3860
|
+
(input and output): `"models": { "MY_KEY": [ { "model": "<id>", "use": true, "inPerM": 0.006, "outPerM": 1.2 } ] }`. There is no built-in price list. A price is a PAIR or nothing: each number
|
|
3861
|
+
a finite number 0 or more (0 / 0 typed is a chosen price), and a ticked line needs both (`modelsProblem`, `src/config.js`, the one rule). A line without both prices cannot be ticked, so it
|
|
3862
|
+
is never in Chat's menu and a run on it refuses at $0 (`<model> is not set up: …`). `ratesFor(provider, baseUrl, rows, model)` (`src/providerrows.js`) reads the line's price (`ratesOfLine`);
|
|
3863
|
+
the run doors resolve the chosen line once, at $0, before the monthly claim and any worktree or spine (`src/userrun.js`; `src/authorrun.js`; the panel's authoring session): the worker line
|
|
3864
|
+
prices worker, scout and planner rounds; the judge is priced at ITS model's own line — for a different provider its own row, for the worker's provider the row on the worker's endpoint
|
|
3865
|
+
with a line named for the judge's model (`judgeRatesFor`), falling back to the worker's price when the model names no line. A priced round is stamped `rateSource:'caller'`
|
|
3866
|
+
(VOUCHED, see `spendProvenance`). The run preview and the run tail print `price yours: in $0.006 / out $1.20 per 1M tokens (MY_KEY row)`; a 0 / 0 price is accepted but the
|
|
3867
|
+
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 on the dollars your
|
|
3868
|
+
price produces, so a price set too low weakens the cap. When a vendor lists two prices (for example a cache-hit and a cache-miss input rate) enter the higher one. bare-agent
|
|
3869
|
+
applies its own cache multipliers (read 0.1×, write 1.25× of the input rate) to cache tokens; they are not configurable here. A price applies from the next run: **past runs are
|
|
3870
|
+
never re-priced**. Not covered by a price: a native (CLI) session, which reports its own cost, and the reuse engine (`runReuse` / `selectBridge`), which resolves no key row. An
|
|
3871
|
+
older config's `keys.<ENV>.{name,priceInPerM,priceOutPerM}` still reads as ONE line (ticked when both prices are valid numbers, else unticked with no price) until the first Settings
|
|
3872
|
+
save of that row's list removes them.
|
|
3886
3873
|
|
|
3887
3874
|
**Monthly $ limit.** `checkMonthlyRoom({ capUsd, home, now })` → `{ ok, leftUsd, limitUsd,
|
|
3888
3875
|
atLeast, heldUsd, heldRuns }`; `monthlyRefusalText(room)` is the ONE spelling: `Max $<left, 2 decimals>
|
|
@@ -3926,45 +3913,31 @@ real spend.
|
|
|
3926
3913
|
|
|
3927
3914
|
**Money & limits tab** (`GET/POST /api/settings/money`): two tiles ($ and tokens, this month / to date, a
|
|
3928
3915
|
`≥` prefix when a figure is a floor) and a per-provider table ($, minutes, tokens; a run is grouped
|
|
3929
|
-
by the
|
|
3916
|
+
by the Providers ROW that served it — its key variable name — and a run no row serves lands under its own shape and address). The limit field auto-saves on
|
|
3930
3917
|
change (no Save button); blank clears it. `POST` needs an explicit `monthlyLimitUsd` key (a number
|
|
3931
3918
|
above 0, or `null`/blank to clear) — an empty or key-less body is a `400`, never "clear the limit".
|
|
3932
3919
|
|
|
3933
|
-
**Providers
|
|
3934
|
-
|
|
3935
|
-
created (mode 600) on the first Providers read with
|
|
3936
|
-
`
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
(
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
`
|
|
3949
|
-
|
|
3950
|
-
`
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
non-zero loud guess, never a real $0).
|
|
3955
|
-
|
|
3956
|
-
`providerrows.js` is the ONE owner of "model → key + shape + URL": `keyRows`, `findRow` (rows on the
|
|
3957
|
-
same shape + endpoint; the Name breaks a tie), `modelChoiceFor` / `chatModels` (Chat's Model menu IS
|
|
3958
|
-
the rows; a blank Name is not offered), `keyNameFor` / `applyConfiguredKey` (a job on `(provider,
|
|
3959
|
-
baseUrl[, model])` reads the matching row's variable, else the provider's built-in one). All doors
|
|
3960
|
-
(`run-u`, `interview`, `author`, the panel's drafting) use it. Routes (all behind the per-start token
|
|
3961
|
-
and Origin check; the chat can never reach them): `GET /api/settings/providers`, `POST .../row`
|
|
3962
|
-
(`{envName, name, shape, baseUrl}`), `POST .../balance-note`, `POST .../test` (one
|
|
3963
|
-
`checkProviderReachable` models-list GET at the row's shape + URL, $0; a refused connection reads
|
|
3964
|
-
`nothing answering at <URL>`), `GET .../balance?env=`
|
|
3965
|
-
(DeepSeek-hosted row, server-side), and `GET /api/author/models` (Chat's Model menu). The old
|
|
3966
|
-
`providers.<row>.key` config field and the key dropdown are gone. Like the authoring routes, a POST
|
|
3967
|
-
body over 1 MiB is refused `413`.
|
|
3920
|
+
**Settings > Providers (P4b, P8 — rewritten from fwdloop's m4d-am3 layout; `src/providerrows.js`, `src/modellist.js`, `src/panel/settingsroutes.js`).** One row per key in `.env` that HAS A VALUE, plus a key
|
|
3921
|
+
only the shell holds when config already names it (`keys.<ENV>` or `models.<ENV>`; its status reads `found in shell`) — nothing is hardcoded, there is no add-row and no remove-row control. A missing `.env`
|
|
3922
|
+
is created (mode 600, dir 700) on the first Providers read with two comment lines and no key. A value of `null` counts as SET and means "no real key" (the provider is handed the placeholder
|
|
3923
|
+
`no-key-needed`, never the word `null`; the row shows `set in file`). A key whose value has a space or a line break shows `The key has a space or a line break in it. Fix it in your keys file, then
|
|
3924
|
+
reload keys.` Each row: the env NAME + status, an **API shape** select (Anthropic / OpenAI-compatible / Gemini), the typed **Base URL** (empty = no run, no list, `type the Base URL`), the **Old max_tokens**
|
|
3925
|
+
tick (applies to `openai-api` only), **Tokens** (the key's spend), `[ Test ] [ ⟳ Refresh ]`, and its model lines: a **Use** tick, the model name, **In / Out $/1M** boxes, tokens (`—` when no spend row names the model).
|
|
3926
|
+
A listed id from Refresh is shown unticked and is not saved until touched; `+ type a model name` adds a line unticked. Saves are on change; success reads `saved — applies from the next model call`;
|
|
3927
|
+
every refusal is a plain sentence in `say` (`Fill both prices to tick this model.`, `Fill both price boxes or clear both.`, `A price must be a number, 0 or more.`, …). **Open keys folder** (in the KEYS strip,
|
|
3928
|
+
`POST /api/settings/open-keys-folder`) opens the folder of the keys file only (`xdg-open` / `open`, argv array, detached, never a path from the request; a non-empty body is `no-path-allowed`); where it cannot —
|
|
3929
|
+
another OS, a failed spawn, a test process with no injected opener — it answers `opened:false` and the page shows the path as text.
|
|
3930
|
+
|
|
3931
|
+
`providerrows.js` is the ONE owner of "model → key + shape + URL": `keyRows` (`{ envName, provider, baseUrl, legacyMaxTokens, models }`), `effectiveModels` (the older single-name read), `tickedLines`,
|
|
3932
|
+
`findRow` (rows on the same shape + endpoint — a blank Base URL is blank, there is no default host; the row with a ticked, priced line for the model breaks a tie), `modelChoiceFor(rows, { provider, model })`
|
|
3933
|
+
(by the KEY's env name; the line must be ticked and priced), `judgePick` (the three refusals: `Tick a model in Settings > Providers. Nothing was spent.` / `Pick a model. Nothing was spent.` /
|
|
3934
|
+
`<KEY> is not set up: put its key in your keys file and type its Base URL in Settings > Providers. Nothing was spent.`), `resolveWorker` (a run's line: the spec's model, else `--model`; `--provider <KEY>`
|
|
3935
|
+
narrows), `keyValueOf` (a row's own variable; the `null` marker becomes the placeholder). All doors (`run-u`, `run <bundle>`, `interview`, `author`, the panel's drafting) use it. Routes (all behind the per-start
|
|
3936
|
+
token and Origin check, GET too; the chat can never reach them): `GET /api/settings/providers` (`{ok, keysFile:{path,exists,refusal}, configProblem, shapes, rows:[{slot, label, envVar, keyStatus, shape, baseUrl,
|
|
3937
|
+
legacyMaxTokens, tokens, models}]}`), `POST .../provider` (`{slot, shape?, baseUrl?, legacyMaxTokens?}`, every field judged before anything is written), `POST .../model` (`{slot, model, use?, inPerM?, outPerM?}`),
|
|
3938
|
+
`POST .../models` (Refresh) and `POST .../test` (both ONE free GET of `<Base URL>/models` through `src/modellist.js`, key only in a header per shape, redirect not followed, 4 s; no completion, no spend row),
|
|
3939
|
+
and `GET /api/author/models` (Chat's Model menu: every ticked-and-priced line, `{slot, model, label, thinking}`, `<KEY> · <model>`). There is no Balance route. Like the authoring routes, a POST body over 1 MiB is refused `413`.
|
|
3940
|
+
A Chat card carries `provider` (the key's env name) beside `model`; `/api/author/model-check`, `start-from` and `reuse-jobs` take `?provider=` too.
|
|
3968
3941
|
|
|
3969
3942
|
### The source front door — a plain folder/file/URL, no `--patient` (PRD item 33/M2, `src/source.js`)
|
|
3970
3943
|
|
|
@@ -4360,4 +4333,4 @@ Goal, Success, Guardrails and Judge examples are one box, **The job**, and Sourc
|
|
|
4360
4333
|
- **The job:** each plain line is numbered 1, 2, 3 (the number sits in a gutter outside the box). A `~` line has no number and belongs to the numbered line above; consecutive `~` lines join into ONE rule with `'; '`. A `~` line starting `PASS:` / `FAIL:` is a judge example (a rubric check needs at least one of each; a deterministic check refuses them); any other `~` line is a guardrail. A `~` before line 1 is a $0 red. Empty lines are ignored. `parseJobBlock(text)` → `{ ok, lines: [{ n, text, rule, examples }] }` or `{ ok:false, error }`; `jobBlockFitsCheck(lines, rubric)` is the Check-type cross-check.
|
|
4361
4334
|
- **Inputs:** one `label: value` per line, numbered. Line 1 = the repo (absolute path). Lines 2+ = files or folders inside that repo, repo-relative; `proveInputFiles(inputs)` (`src/source.js`) proves them at $0 before any token — exists, inside the repo, tracked, and (files) not an environment file by name, not a symlink, text, under the size ceiling, a known secret shape in a file's content is MASKED wherever bareloop records it, never refused (the P6 worktree door ruling; `.env` by name and symlinks stay refused). A miss is `Inputs, line N: ...`. Search lines and files outside the repo are `only files inside the repo for now`. The panel proves them in `POST /api/author/start` (400 naming the line); the CLI re-asks.
|
|
4362
4335
|
- **Drafter:** every stage carries `fromLine`, or the line is listed in `closeDecl.refused` with a reason (see `closeDecl` above); the confirm turn's checks are `{ text, fromLine: [N, ...] }`; the plan draws a multi-line check under EACH line it serves.
|
|
4363
|
-
- **Panel:** the card is `{ jobName, checkType, model, jobText, inputs, destination, capUsd, maxWallMs }
|
|
4336
|
+
- **Panel:** the card is `{ jobName, checkType, provider, model, thinking, jobText, inputs, destination, capUsd, maxWallMs }` (`provider` = the picked key's env name, `thinking` = the "Thinking on" tick). The confirm plan is a structured chat message `{ role: 'bot', kind: 'plan', plan }` and the Job tab reads the SIGNED spec through the same model (`src/panel/jobplan.js`: `planFromConfirm`, `planFromSpec`); the page draws both with one `renderJobPlan()`. `GET /api/runs/:id/job` returns `plan`, `jobLines`, `inputs`, `specHash`, `signedAt` (the run's FIRST `job-start`) and `signed` (`YYYY-MM-DD HH:MM · <first 6 of specHash>`, local time). An older spec (no `jobLines`) renders the signed goal as line 1 with its stage names as checks and the note `older job — numbered lines were not saved`; Reuse / Edit / Resume prefill the same fallback.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bareloop",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.41.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",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"check:lockfile": "node scripts/check-lockfile.mjs"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"bare-agent": "^0.
|
|
30
|
+
"bare-agent": "^0.52.0",
|
|
31
31
|
"bareguard": "^0.21.0",
|
|
32
32
|
"litectx": "^0.32.0"
|
|
33
33
|
},
|
package/src/authorflow.js
CHANGED
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
// No spine emission here — M4 wires events.
|
|
74
74
|
|
|
75
75
|
import { renderInputs } from './jobblock.js';
|
|
76
|
-
import {
|
|
76
|
+
import { thinkingCallOptions } from './thinking.js';
|
|
77
77
|
import { createRequire } from 'node:module';
|
|
78
78
|
import { realpathSync } from 'node:fs';
|
|
79
79
|
import { join, relative, isAbsolute } from 'node:path';
|
|
@@ -1536,7 +1536,7 @@ export function resolveSourcePrefixes({ workdir, sourcePaths, seedFiles }) {
|
|
|
1536
1536
|
*/
|
|
1537
1537
|
/**
|
|
1538
1538
|
* @param {{ceilingUsd?: number|null,
|
|
1539
|
-
* onCall?: (call: {label: string, costUsd: number|null, unpricedRounds: number,
|
|
1539
|
+
* onCall?: (call: {label: string, costUsd: number|null, unpricedRounds: number, thinkingStatus?: string}) => void}} [o]
|
|
1540
1540
|
* `ceilingUsd` is the OPERATOR's ceiling; `null`/absent is UNBOUNDED.
|
|
1541
1541
|
* `onCall` is a REPORTING seam fired as each metered call lands — the shell's
|
|
1542
1542
|
* only chance to know what has been spent while the run is still alive. It is
|
|
@@ -1557,9 +1557,9 @@ export function makeCostBook({ ceilingUsd = null, onCall = () => {} } = {}) {
|
|
|
1557
1557
|
const call = { label, ...priceOf(r) };
|
|
1558
1558
|
entries.push(call);
|
|
1559
1559
|
raws.push(scrubRaw({ label, attempt, text: r?.text, reason: r?.error ? String(r.error) : null }));
|
|
1560
|
-
// bare-agent's per-run `
|
|
1561
|
-
// so the cost report and every persisted shape stay as they were; absent when thinkingMode was not sent
|
|
1562
|
-
onCall({ ...call, ...(r?.
|
|
1560
|
+
// bare-agent's per-run `thinkingStatus` ('applied' | 'unsupported' | 'ignored' | 'unknown') rides the REPORTING seam only —
|
|
1561
|
+
// never the stored entry, so the cost report and every persisted shape stay as they were; absent when thinkingMode was not sent
|
|
1562
|
+
onCall({ ...call, ...(r?.thinkingStatus === undefined ? {} : { thinkingStatus: r.thinkingStatus }) });
|
|
1563
1563
|
return entries.at(-1);
|
|
1564
1564
|
},
|
|
1565
1565
|
/** @param {{label: string, costUsd: number|null, unpricedRounds: number}[]} calls
|
|
@@ -1599,13 +1599,13 @@ export function makeCostBook({ ceilingUsd = null, onCall = () => {} } = {}) {
|
|
|
1599
1599
|
* `cacheMessages` (F18) and `maxTokens` (F30) are not optional on this stack.
|
|
1600
1600
|
* `rates` is the customer's own price for this provider's key row (USD per 1K, `ratesFor`);
|
|
1601
1601
|
* absent = bare-agent's built-in guess, exactly as before.
|
|
1602
|
-
* `draftThinking` ('on' | 'off',
|
|
1602
|
+
* `draftThinking` ('on' | 'off', the job's "Thinking on" tick spelled as a word; default 'off') adds the drafter's per-call thinking option via `thinkingCallOptions` (src/thinking.js, the one owner).
|
|
1603
1603
|
* @param {any} provider @param {{system?: string, maxTokens?: number, rates?: {in: number, out: number}|null, draftThinking?: string}} [o]
|
|
1604
1604
|
* @returns {(messages: any[], tools: any[], opts?: any) => Promise<any>}
|
|
1605
1605
|
*/
|
|
1606
1606
|
export function makeLoopGenerate(provider, { system = AUTHOR_SYSTEM, maxTokens = AUTHOR_MAX_TOKENS, rates = null, draftThinking = 'off' } = {}) {
|
|
1607
|
-
// the drafter's per-call options (src/
|
|
1608
|
-
const callOptions =
|
|
1607
|
+
// the drafter's per-call options (src/thinking.js `thinkingCallOptions`, one owner); a bad mode throws HERE, at construction
|
|
1608
|
+
const callOptions = thinkingCallOptions(provider, draftThinking);
|
|
1609
1609
|
return async (/** @type {any[]} */ messages, /** @type {any[]} */ tools, /** @type {any} */ opts = {}) => {
|
|
1610
1610
|
const loop = new Loop({ provider, system, ...(rates ? { rates } : {}) });
|
|
1611
1611
|
const wired = tools.map((t) => ({
|
|
@@ -2265,7 +2265,7 @@ export async function runConfirmTurn({
|
|
|
2265
2265
|
* generate: Function, seedReadFn?: Function, closeCtx?: any,
|
|
2266
2266
|
* ceilingUsd?: number|null,
|
|
2267
2267
|
* onPhase?: (phase: string, data?: any) => void,
|
|
2268
|
-
* onCall?: (call: {label: string, costUsd: number|null, unpricedRounds: number,
|
|
2268
|
+
* onCall?: (call: {label: string, costUsd: number|null, unpricedRounds: number, thinkingStatus?: string}) => void,
|
|
2269
2269
|
* maxRevisions?: number, structureRetries?: number,
|
|
2270
2270
|
* structuredMode?: 'tool'|'text', catalogue?: Record<string, any>, writeScope?: string[]|null,
|
|
2271
2271
|
* priorCalls?: {label: string, costUsd: number|null, unpricedRounds: number}[]|null,
|