bareloop 0.23.0 → 0.24.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 +122 -0
- package/bareloop.context.md +56 -19
- package/package.json +1 -1
- package/src/authorflow.js +17 -7
- package/src/authoring.js +32 -0
- package/src/authorjob.js +108 -36
- package/src/calibrate.js +33 -12
- package/src/cardauthor.js +23 -16
- package/src/cli.js +36 -5
- package/src/declaredclose.js +17 -10
- package/src/index.js +8 -5
- package/src/job.js +58 -3
- package/src/judged.js +132 -14
- package/src/kinds.js +77 -12
- package/src/planrun.js +14 -2
- package/src/providers.js +143 -18
- package/src/run.js +9 -4
- package/src/validate.js +4 -0
- package/types/authoring.d.ts +30 -0
- package/types/authorjob.d.ts +10 -6
- package/types/calibrate.d.ts +9 -2
- package/types/cardauthor.d.ts +13 -11
- package/types/cli.d.ts +2 -1
- package/types/declaredclose.d.ts +2 -0
- package/types/index.d.ts +1 -1
- package/types/job.d.ts +22 -1
- package/types/judged.d.ts +123 -14
- package/types/kinds.d.ts +42 -3
- package/types/planrun.d.ts +10 -2
- package/types/providers.d.ts +73 -11
- package/types/run.d.ts +9 -3
package/src/providers.js
CHANGED
|
@@ -12,16 +12,18 @@
|
|
|
12
12
|
// models; it is exactly the two provider identities the PROVIDERS menu
|
|
13
13
|
// (`src/job.js`) admits.
|
|
14
14
|
//
|
|
15
|
-
// What this module is NOT: it never
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
// provider
|
|
15
|
+
// What this module is NOT: it never RESOLVES a judge identity itself (that
|
|
16
|
+
// stays arbiter territory, `resolveJudge`/`resolveJobJudge` in
|
|
17
|
+
// src/judged.js — the spec's signed `judge` override, else the job's own
|
|
18
|
+
// worker provider and model, PRD item 32) — it only CONSTRUCTS the judge
|
|
19
|
+
// provider object from an identity the caller already resolved
|
|
20
|
+
// (`buildRunnerProviders`, below); it never prices a round (F113: rates
|
|
21
|
+
// passthrough is dead), and never decides a budget.
|
|
20
22
|
|
|
21
23
|
import { createRequire } from 'node:module';
|
|
22
24
|
|
|
23
25
|
const require = createRequire(import.meta.url);
|
|
24
|
-
const { AnthropicProvider, OpenAIProvider } = require('bare-agent/providers');
|
|
26
|
+
const { AnthropicProvider, OpenAIProvider, GeminiProvider } = require('bare-agent/providers');
|
|
25
27
|
|
|
26
28
|
/**
|
|
27
29
|
* Today's anthropic-api tier table — EXACTLY the `DEFAULT_TIER_MODELS` both
|
|
@@ -49,6 +51,38 @@ export const OPENAI_TIER_MODELS = Object.freeze({
|
|
|
49
51
|
haiku: 'deepseek-chat',
|
|
50
52
|
});
|
|
51
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Gemini (PRD item 31.3, hamr 2026-09-09: "anthropic, openai, gemini drop
|
|
56
|
+
* ollama for now"). Two real tiers, unlike DeepSeek's single model: `flash` is
|
|
57
|
+
* the cheap tier by design, so `haiku` maps to it honestly rather than
|
|
58
|
+
* repeating one id.
|
|
59
|
+
*
|
|
60
|
+
* ADMITTED-PENDING-PROBE. The probe rule stands (PRD item 28 ruling (d),
|
|
61
|
+
* 2026-09-09): no endpoint is in the menu without its own clean paid probe,
|
|
62
|
+
* and `gemini-api` has ZERO runs. `anthropic-api` is the original (~167
|
|
63
|
+
* archived runs) and `openai-api` was probed green through the shipped runner
|
|
64
|
+
* (run mtu12vks, $1.64/$4, F157); gemini owes that fire and has not paid it.
|
|
65
|
+
* `PROBE_STATUS` below is the machine-readable form of that debt — nothing in
|
|
66
|
+
* here silently pretends the provider is proven.
|
|
67
|
+
*/
|
|
68
|
+
export const GEMINI_TIER_MODELS = Object.freeze({
|
|
69
|
+
sonnet: 'gemini-2.5-pro',
|
|
70
|
+
haiku: 'gemini-2.5-flash',
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Which admitted providers have paid for their own end-to-end probe, and which
|
|
75
|
+
* have not. Read by the runner to print a loud marker; never read to REFUSE —
|
|
76
|
+
* hamr admitted gemini to the menu, and a table that quietly withheld it would
|
|
77
|
+
* be a second, invisible ruling. An unprobed provider runs, and says so.
|
|
78
|
+
* @type {Readonly<Record<string, {probed: boolean, evidence: string}>>}
|
|
79
|
+
*/
|
|
80
|
+
export const PROBE_STATUS = Object.freeze({
|
|
81
|
+
'anthropic-api': Object.freeze({ probed: true, evidence: 'the original surface — ~167 archived runs' }),
|
|
82
|
+
'openai-api': Object.freeze({ probed: true, evidence: 'run mtu12vks, green through scripts/run-u.mjs, $1.64/$4 (F157)' }),
|
|
83
|
+
'gemini-api': Object.freeze({ probed: false, evidence: 'ZERO runs — admitted to the menu, owes its own paid probe (PRD item 31.3)' }),
|
|
84
|
+
});
|
|
85
|
+
|
|
52
86
|
/**
|
|
53
87
|
* Per-MODEL request-key gating (F149, live-measured 2026-09-09): DeepSeek
|
|
54
88
|
* silently IGNORES the modern `max_completion_tokens` key — asked for 64
|
|
@@ -73,15 +107,44 @@ const OPENAI_MODEL_OPTIONS = Object.freeze({
|
|
|
73
107
|
* @property {new (options: any) => any} ctor the bare-agent provider class
|
|
74
108
|
* @property {string} envKey the environment variable this provider reads its key from
|
|
75
109
|
* @property {Readonly<Record<string, string>>} tiers tier name -> model id
|
|
76
|
-
* @property {
|
|
110
|
+
* @property {string} endpointKey the CONSTRUCTOR OPTION NAME this provider reads its endpoint from — NOT assumed to be `baseUrl` (see below)
|
|
111
|
+
* @property {(model: string|undefined) => Record<string, any>} paramsFor per-model request-key/param gating, applied on TOP of {exposeErrorBody:true, apiKey, model, <endpointKey>?}
|
|
77
112
|
*/
|
|
78
113
|
|
|
114
|
+
// ── WHY `endpointKey` IS A FIELD AND NOT THE LITERAL `baseUrl` ──────────────
|
|
115
|
+
//
|
|
116
|
+
// hamr's framing for this item was "api + api shape + endpoint" (PRD 31.3), and
|
|
117
|
+
// the endpoint half is the one that does NOT generalize. Read from bare-agent's
|
|
118
|
+
// own constructors, not assumed:
|
|
119
|
+
//
|
|
120
|
+
// AnthropicProvider `baseUrl` (provider-anthropic.js:49)
|
|
121
|
+
// OpenAIProvider `baseUrl` (provider-openai.js:76)
|
|
122
|
+
// GeminiProvider `baseUrl` (provider-gemini.js:47)
|
|
123
|
+
// OllamaProvider `url` (provider-ollama.js:32) <- and NO apiKey at all
|
|
124
|
+
//
|
|
125
|
+
// Every one of these constructors defaults its endpoint when the option is
|
|
126
|
+
// absent and none of them validate unknown option names. So passing `baseUrl`
|
|
127
|
+
// to Ollama would not throw: it would be IGNORED, the provider would default to
|
|
128
|
+
// `http://localhost:11434`, and the run would quietly talk to the wrong machine.
|
|
129
|
+
// That is the F149 class exactly — DeepSeek silently ignoring
|
|
130
|
+
// `max_completion_tokens` and returning 665 tokens for a 64-token cap — where a
|
|
131
|
+
// silently-dropped option produces a plausible-looking run against a
|
|
132
|
+
// configuration nobody chose.
|
|
133
|
+
//
|
|
134
|
+
// Ollama is NOT admitted today (hamr, 2026-09-09: "drop ollama for now"), so
|
|
135
|
+
// this field currently reads `baseUrl` for all three entries and its value is
|
|
136
|
+
// never exercised against a second spelling. It is here anyway, because the
|
|
137
|
+
// alternative is a hardcoded `baseUrl` that is correct for exactly as long as
|
|
138
|
+
// the menu holds only http-shaped API providers, and the FIRST thing that
|
|
139
|
+
// changes when Ollama is admitted is the one line nobody would think to look at.
|
|
140
|
+
|
|
79
141
|
/** @type {Readonly<Record<string, ProviderTableEntry>>} */
|
|
80
142
|
const PROVIDER_TABLE = Object.freeze({
|
|
81
143
|
'anthropic-api': Object.freeze({
|
|
82
144
|
ctor: AnthropicProvider,
|
|
83
145
|
envKey: 'ANTHROPIC_API_KEY',
|
|
84
146
|
tiers: ANTHROPIC_TIER_MODELS,
|
|
147
|
+
endpointKey: 'baseUrl',
|
|
85
148
|
// Anthropic has only ever had one request key for the output cap
|
|
86
149
|
// (`max_tokens`) — nothing to gate here, ever (unlike OpenAI-shaped
|
|
87
150
|
// backends, which fork on `max_tokens` vs `max_completion_tokens`).
|
|
@@ -91,8 +154,19 @@ const PROVIDER_TABLE = Object.freeze({
|
|
|
91
154
|
ctor: OpenAIProvider,
|
|
92
155
|
envKey: 'OPENAI_API_KEY',
|
|
93
156
|
tiers: OPENAI_TIER_MODELS,
|
|
157
|
+
endpointKey: 'baseUrl',
|
|
94
158
|
paramsFor: (model) => OPENAI_MODEL_OPTIONS[/** @type {string} */ (model)] ?? {},
|
|
95
159
|
}),
|
|
160
|
+
'gemini-api': Object.freeze({
|
|
161
|
+
ctor: GeminiProvider,
|
|
162
|
+
envKey: 'GEMINI_API_KEY',
|
|
163
|
+
tiers: GEMINI_TIER_MODELS,
|
|
164
|
+
endpointKey: 'baseUrl',
|
|
165
|
+
// Gemini carries no `max_tokens`/`max_completion_tokens` fork of its own —
|
|
166
|
+
// bare-agent's GeminiProvider maps the cap internally. Nothing to gate, and
|
|
167
|
+
// an empty object is the honest statement of that, not an omission.
|
|
168
|
+
paramsFor: () => ({}),
|
|
169
|
+
}),
|
|
96
170
|
});
|
|
97
171
|
|
|
98
172
|
/**
|
|
@@ -130,7 +204,13 @@ export function makeProvider(providerName, { apiKey, model, baseUrl } = {}) {
|
|
|
130
204
|
exposeErrorBody: true,
|
|
131
205
|
apiKey,
|
|
132
206
|
model,
|
|
133
|
-
|
|
207
|
+
// the endpoint goes in under THIS PROVIDER'S OWN OPTION NAME, never a
|
|
208
|
+
// hardcoded `baseUrl` (PRD item 31.3). The spec field stays `baseUrl` for
|
|
209
|
+
// everyone — one name for the person writing the job — and the translation
|
|
210
|
+
// to the constructor's spelling happens here, once. A provider whose option
|
|
211
|
+
// is spelled differently would otherwise IGNORE the endpoint silently and
|
|
212
|
+
// run against its own default; see the note above the table.
|
|
213
|
+
...(baseUrl !== undefined ? { [entry.endpointKey]: baseUrl } : {}),
|
|
134
214
|
...entry.paramsFor(model),
|
|
135
215
|
});
|
|
136
216
|
}
|
|
@@ -144,24 +224,33 @@ export function makeProvider(providerName, { apiKey, model, baseUrl } = {}) {
|
|
|
144
224
|
* before this factory existed — that decision stays where each runner's own
|
|
145
225
|
* `--model` flag/spec precedence lives, not duplicated in here).
|
|
146
226
|
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
227
|
+
* THE JUDGE IS NO LONGER PINNED TO ANTHROPIC (PRD item 32.2, hamr 2026-09-10:
|
|
228
|
+
* *"what i care about is that judge becomes llm agnostic and not set to one
|
|
229
|
+
* model or provider"*). It is constructed through this same factory, from a
|
|
230
|
+
* `judgeProviderName`/`judgeModel` pair the CALLER resolved (`resolveJudge`,
|
|
231
|
+
* src/judged.js — the spec's signed `judge` override, else the job's own worker
|
|
232
|
+
* provider and model). Item 28's pin, and item 28 part (2)'s deferral of a
|
|
233
|
+
* signed `judge:{provider,model}` field, both close here.
|
|
234
|
+
*
|
|
235
|
+
* `judgeApiKey` stays a SEPARATE argument rather than being derived: the judge
|
|
236
|
+
* may legitimately be a different provider from the worker, and reading its key
|
|
237
|
+
* for it would put a second env-var reader in a module whose whole job is to
|
|
238
|
+
* have exactly one. The caller reads the resolved judge provider's own `envKey`
|
|
239
|
+
* and hands the key in — the same contract the worker key already has here.
|
|
154
240
|
* @param {object} args
|
|
155
241
|
* @param {string} args.providerName spec.provider (or the anthropic-api default)
|
|
156
242
|
* @param {string|undefined} args.apiKey the WORKER's key, read from the provider's own `envKey`
|
|
157
243
|
* @param {string} args.model the already-resolved worker model id
|
|
158
244
|
* @param {Readonly<Record<string, string>>} args.tierModels the already-resolved tier -> model map (spec override folded in by the caller)
|
|
159
245
|
* @param {string|undefined} [args.baseUrl] spec.baseUrl, if any
|
|
160
|
-
* @param {string|undefined} args.judgeApiKey the judge's own key
|
|
161
|
-
* @param {string} args.judgeModel
|
|
246
|
+
* @param {string|undefined} args.judgeApiKey the judge's own key, read by the caller from the RESOLVED judge provider's `envKey`
|
|
247
|
+
* @param {string} args.judgeModel the resolved judge model id
|
|
248
|
+
* @param {string} [args.judgeProviderName] the resolved judge provider name; defaults to `providerName` (the job's own worker provider), which is `resolveJudge`'s own default
|
|
249
|
+
* @param {string|undefined} [args.judgeBaseUrl] the judge provider's endpoint, when it takes one
|
|
162
250
|
*/
|
|
163
251
|
export function buildRunnerProviders({
|
|
164
252
|
providerName, apiKey, model, tierModels, baseUrl, judgeApiKey, judgeModel,
|
|
253
|
+
judgeProviderName = providerName, judgeBaseUrl = undefined,
|
|
165
254
|
}) {
|
|
166
255
|
const provider = makeProvider(providerName, { apiKey, model, baseUrl });
|
|
167
256
|
/** @type {Record<string, any>} */
|
|
@@ -171,6 +260,42 @@ export function buildRunnerProviders({
|
|
|
171
260
|
? provider
|
|
172
261
|
: makeProvider(providerName, { apiKey, model: tierModels[tier], baseUrl })
|
|
173
262
|
));
|
|
174
|
-
|
|
263
|
+
// the judge goes through the SAME table as the worker — one place names a
|
|
264
|
+
// provider identity, which is the entire reason this factory exists. Reusing
|
|
265
|
+
// the worker instance when the identity matches exactly is not an optimisation
|
|
266
|
+
// for its own sake: a second instance against the same endpoint/model is a
|
|
267
|
+
// second prompt-cache prefix, and cache writes are ~41% of the bill.
|
|
268
|
+
const judgeProvider = (judgeProviderName === providerName && judgeModel === model && judgeApiKey === apiKey
|
|
269
|
+
&& judgeBaseUrl === baseUrl)
|
|
270
|
+
? provider
|
|
271
|
+
: makeProvider(judgeProviderName, { apiKey: judgeApiKey, model: judgeModel, baseUrl: judgeBaseUrl });
|
|
175
272
|
return { provider, providerFor, judgeProvider };
|
|
176
273
|
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* The launch-time warning lines for an ADMITTED-PENDING-PROBE provider, or
|
|
277
|
+
* `null` when the provider has paid for its own end-to-end probe (and for an
|
|
278
|
+
* off-table name — an unknown provider is `resolveProvider`'s named throw to
|
|
279
|
+
* report, never this function's warning to soften).
|
|
280
|
+
*
|
|
281
|
+
* This lives here, next to `PROBE_STATUS`, rather than inline in the runner,
|
|
282
|
+
* for one reason: inline it was UNTESTABLE. `scripts/run-u.mjs` reaches this
|
|
283
|
+
* point only with a real job row (a machine-local patient path) and a real
|
|
284
|
+
* signed spec, so nothing in the suite could reach the branch, and a warning
|
|
285
|
+
* nothing executes drifts exactly the way the green-only prose did (item 31.2).
|
|
286
|
+
*
|
|
287
|
+
* It never REFUSES, and it never returns a shape a caller can mistake for a
|
|
288
|
+
* refusal: hamr admitted `gemini-api` to the menu, and a probe table that
|
|
289
|
+
* quietly withheld it would be a second ruling nobody made.
|
|
290
|
+
* @param {string} providerName
|
|
291
|
+
* @returns {string[]|null} lines to print, in order, or null when there is nothing to say
|
|
292
|
+
*/
|
|
293
|
+
export function probeWarningLines(providerName) {
|
|
294
|
+
const probe = PROBE_STATUS[/** @type {keyof typeof PROBE_STATUS} */ (providerName)];
|
|
295
|
+
if (!probe || probe.probed !== false) return null;
|
|
296
|
+
return [
|
|
297
|
+
`⚠ UNPROVEN PROVIDER — "${providerName}" is on the menu but has never been probed end to end.`,
|
|
298
|
+
` ${probe.evidence}`,
|
|
299
|
+
' It will run. Nothing has shown that it CAN run this job to a close-rendered verdict.',
|
|
300
|
+
];
|
|
301
|
+
}
|
package/src/run.js
CHANGED
|
@@ -95,9 +95,14 @@ async function primitiveSmoke(workdir) {
|
|
|
95
95
|
* metered claude-json text provider for the toolless drafter); ignored on every Loop-driven provider
|
|
96
96
|
* @param {(type: string, data?: object) => object} opts.emit spine emitter
|
|
97
97
|
* @param {(tier: string) => any} [opts.providerFor] P: per-step model-tier provider factory (forwarded to the plan flow)
|
|
98
|
+
* @param {string|null} [opts.judgeModel] SOFTGREEN — WHICH model the judge provider drives
|
|
99
|
+
* (PRD item 32.1), resolved by the CALLER (`resolveJudge`, src/judged.js) and passed
|
|
100
|
+
* through untouched. Travels with `judgeProvider`; absent, a judged stage stops as a
|
|
101
|
+
* wiring gap rather than grading under an identity nobody named.
|
|
98
102
|
* @param {any} [opts.judgeProvider] SOFTGREEN — the provider a JUDGED close stage runs its
|
|
99
|
-
* locate call through,
|
|
100
|
-
*
|
|
103
|
+
* locate call through, built by the CALLER from the RESOLVED judge identity (PRD item
|
|
104
|
+
* 32.1: `resolveJudge`/`resolveJobJudge`, src/judged.js — the spec's signed `judge`
|
|
105
|
+
* override, else the job's own worker provider and model), never pinned. Forwarded to the plan
|
|
101
106
|
* flow. It is its own seam and not `provider`/`providerFor`, because the judge tier is
|
|
102
107
|
* never a step knob and never agent-selectable; absent, a judged stage instrument-stops as
|
|
103
108
|
* a wiring gap rather than grading on whatever binding happened to be at hand. Its spend
|
|
@@ -237,7 +242,7 @@ async function primitiveSmoke(workdir) {
|
|
|
237
242
|
* deliberately NOT in the resumable/checkpoint set (`src/reuse.js`) — an
|
|
238
243
|
* unknown in-flight state is not a known-safe resume point.
|
|
239
244
|
*/
|
|
240
|
-
export async function runJob(rawSpec, { approvals, workdir, provider, nativeProvider, providerFor, judgeProvider = null, emit, capRuns = 3, strikeLimit, shellCapUsd = 2, closeTimeoutMs, closeDir = null, layerRoot = false, readShim = false, scout = true, bridge = null, priorSpentUsd = 0, priorSpendComplete = true, priorWallMs = 0, resumeSeed = null, resumeGrades = [], resumeReplans = null, resumeBranch = null, humanRuling = null, heldRuling = null, reviewDoor = null, doorRerun = null, resumable = true }) {
|
|
245
|
+
export async function runJob(rawSpec, { approvals, workdir, provider, nativeProvider, providerFor, judgeProvider = null, judgeModel = null, emit, capRuns = 3, strikeLimit, shellCapUsd = 2, closeTimeoutMs, closeDir = null, layerRoot = false, readShim = false, scout = true, bridge = null, priorSpentUsd = 0, priorSpendComplete = true, priorWallMs = 0, resumeSeed = null, resumeGrades = [], resumeReplans = null, resumeBranch = null, humanRuling = null, heldRuling = null, reviewDoor = null, doorRerun = null, resumable = true }) {
|
|
241
246
|
// THE READ SHIM's ARM, resolved at the door — the FIRST thing this entry does,
|
|
242
247
|
// before the ledger, before the approval gate, before a byte of the spec is read.
|
|
243
248
|
// An unrecognised spelling throws here at zero cost instead of being coerced by
|
|
@@ -487,7 +492,7 @@ export async function runJob(rawSpec, { approvals, workdir, provider, nativeProv
|
|
|
487
492
|
// accounts it natively (F12) and the job-end money contract is unchanged.
|
|
488
493
|
{
|
|
489
494
|
const outcome = await runPlan(job, {
|
|
490
|
-
workdir, provider, nativeProvider, providerFor, judgeProvider, emit: meter, capRuns, ...(strikeLimit !== undefined ? { strikeLimit } : {}), closeTimeoutMs, closeDir, layerRoot, readShim, scout, bridge, priorWallMs: chainWallMs, resumeSeed, resumeGrades, resumeReplans, resumeBranch, humanRuling, heldRuling, reviewDoor, doorRerun, priorSpentUsd: chainFoldUsd, resumable,
|
|
495
|
+
workdir, provider, nativeProvider, providerFor, judgeProvider, judgeModel, emit: meter, capRuns, ...(strikeLimit !== undefined ? { strikeLimit } : {}), closeTimeoutMs, closeDir, layerRoot, readShim, scout, bridge, priorWallMs: chainWallMs, resumeSeed, resumeGrades, resumeReplans, resumeBranch, humanRuling, heldRuling, reviewDoor, doorRerun, priorSpentUsd: chainFoldUsd, resumable,
|
|
491
496
|
remainingUsd: () => Math.min(shellCapUsd, job.budgetUsd - spentUsd),
|
|
492
497
|
isUnpriced: () => unpriced, // F6: let the plan flow bail in-flight, not just after it returns
|
|
493
498
|
spendComplete, // …and let its money-halt readout say whether the remaining it quotes is exact
|
package/src/validate.js
CHANGED
|
@@ -147,6 +147,10 @@ export const SECRET_PATTERNS = [
|
|
|
147
147
|
/(?<![A-Za-z0-9_-])github_pat_[A-Za-z0-9_]{20,}/,
|
|
148
148
|
/(?<![A-Za-z0-9_-])AKIA[0-9A-Z]{16}/,
|
|
149
149
|
/(?<![A-Za-z0-9_-])xox[bap]-[A-Za-z0-9-]{10,}/,
|
|
150
|
+
// Google/Gemini API key: `AIza` + 35 chars of [0-9A-Za-z_-] (39 total, fixed
|
|
151
|
+
// length by Google's own issuance format). Admitted with the gemini-api
|
|
152
|
+
// provider (PRD item 31.3, F160) — was missing on this branch until then.
|
|
153
|
+
/(?<![A-Za-z0-9_-])AIza[0-9A-Za-z_-]{35}/,
|
|
150
154
|
];
|
|
151
155
|
const SECRET_RE = new RegExp(SECRET_PATTERNS.map((r) => r.source).join('|'));
|
|
152
156
|
|
package/types/authoring.d.ts
CHANGED
|
@@ -328,6 +328,36 @@ export const VERDICT_CLASSES: readonly string[];
|
|
|
328
328
|
export const LOCKED_CLASSES: readonly string[];
|
|
329
329
|
/** the classes v1 actually builds */
|
|
330
330
|
export const LIVE_CLASSES: readonly string[];
|
|
331
|
+
/** OFF THE MENU, but still built — a THIRD state, distinct from both lists above
|
|
332
|
+
* (PRD item 31.1, hamr 2026-09-09: *"retire hitl from list, keep its code"*, and
|
|
333
|
+
* *"if it's not on menu, you can't reach it, from menu"*).
|
|
334
|
+
*
|
|
335
|
+
* `hitl` was retired by ruling at v1.71 and the class moved to fwdloop (PRD item
|
|
336
|
+
* 29), but no constant was ever updated, so `runInterview` still returned
|
|
337
|
+
* `ok:true` for a hitl pick — an authorable job for a class this product no
|
|
338
|
+
* longer closes.
|
|
339
|
+
*
|
|
340
|
+
* Why this is NOT `LOCKED_CLASSES`. Locked means INEXPRESSIBLE: no guard battery
|
|
341
|
+
* exists, so `validateCloseDecl` cannot check the close and must refuse
|
|
342
|
+
* (`src/declaredclose.js:306`). hitl's battery, question set, composition rule and
|
|
343
|
+
* runtime all exist and are tested — locking it would make every one of them
|
|
344
|
+
* unreachable, which is not "keeping the code", it is abandoning it in place with
|
|
345
|
+
* its tests deleted. So the split is:
|
|
346
|
+
*
|
|
347
|
+
* LOCKED_CLASSES — no battery. Cannot be validated, cannot run. (empty today)
|
|
348
|
+
* UNLISTED_CLASSES — battery exists, validates and RUNS. Just never OFFERED.
|
|
349
|
+
* LIVE_CLASSES — everything the system can validate and run (locked removed).
|
|
350
|
+
*
|
|
351
|
+
* Read by the AUTHORING surface only (`runInterview`, `authorClose`'s precheck).
|
|
352
|
+
* The spec validator, the close validator and the runtime deliberately do NOT
|
|
353
|
+
* read it: a hitl spec that already exists still validates and still runs, which
|
|
354
|
+
* is what keeps the code alive and under test. Nothing can AUTHOR a new one.
|
|
355
|
+
* @type {readonly string[]} */
|
|
356
|
+
export const UNLISTED_CLASSES: readonly string[];
|
|
357
|
+
/** what the authoring interview actually OFFERS: live, minus unlisted. This is
|
|
358
|
+
* "the menu" in hamr's sentence, and it is the only list an authoring refusal
|
|
359
|
+
* should ever name. */
|
|
360
|
+
export const MENU_CLASSES: readonly string[];
|
|
331
361
|
/**
|
|
332
362
|
* The whole vocabulary, as DATA. `shape` and `asserts` are the strings the
|
|
333
363
|
* authoring prompt renders (M3) — the prompt and this validator must judge the
|
package/types/authorjob.d.ts
CHANGED
|
@@ -81,7 +81,7 @@ export function refuseLockedKind(kind: string): Refusal;
|
|
|
81
81
|
* @param {{answers: Record<string|number, any>, repoPath?: string|null, lang: string,
|
|
82
82
|
* verdictType?: string|null,
|
|
83
83
|
* questions?: Record<string|number, string>|null, generate?: Function, provider?: any,
|
|
84
|
-
* seedRef?: string|null, scout?: any, listing?: any, ceilingUsd?: number|null,
|
|
84
|
+
* seedRef?: string|null, scout?: any, listing?: any, ceilingUsd?: number|null, judgeModel?: string|null,
|
|
85
85
|
* onPhase?: (phase: string, data?: any) => void,
|
|
86
86
|
* onCall?: (call: {label: string, costUsd: number|null, unpricedRounds: number}) => void,
|
|
87
87
|
* seedFn?: Function, scoutFn?: Function, listingFn?: Function,
|
|
@@ -91,7 +91,7 @@ export function refuseLockedKind(kind: string): Refusal;
|
|
|
91
91
|
* closeDecl: any, seedRef: string|null, authoring: any, judged: any, reds: Red[],
|
|
92
92
|
* stop: string|null, cost: any, interview: any}>}
|
|
93
93
|
*/
|
|
94
|
-
export function authorCloseForJob({ answers, repoPath, lang, verdictType, questions, generate, provider, seedRef, scout, listing, ceilingUsd, onPhase, onCall, seedFn, scoutFn, listingFn, authorFn, authorOpts, signerFix, proposeFn, compileOpts, }: {
|
|
94
|
+
export function authorCloseForJob({ answers, repoPath, lang, verdictType, questions, generate, provider, seedRef, scout, listing, ceilingUsd, judgeModel, onPhase, onCall, seedFn, scoutFn, listingFn, authorFn, authorOpts, signerFix, proposeFn, compileOpts, }: {
|
|
95
95
|
answers: Record<string | number, any>;
|
|
96
96
|
repoPath?: string | null;
|
|
97
97
|
lang: string;
|
|
@@ -103,6 +103,7 @@ export function authorCloseForJob({ answers, repoPath, lang, verdictType, questi
|
|
|
103
103
|
scout?: any;
|
|
104
104
|
listing?: any;
|
|
105
105
|
ceilingUsd?: number | null;
|
|
106
|
+
judgeModel?: string | null;
|
|
106
107
|
onPhase?: (phase: string, data?: any) => void;
|
|
107
108
|
onCall?: (call: {
|
|
108
109
|
label: string;
|
|
@@ -207,12 +208,12 @@ export function assembleSpec(specDraft: any, { closeDecl, verdictType }: {
|
|
|
207
208
|
*
|
|
208
209
|
* @param {{spec: any, workdir: string, seedRef?: string|null, shellCapUsd?: number,
|
|
209
210
|
* timeoutMs?: number, seedFn?: Function, listingFn?: Function, seedReadFn?: Function,
|
|
210
|
-
* judgeLoop?: Function|null, onJudgeCost?: ((c: any) => void)|null,
|
|
211
|
-
* ceilingUsd?: number|null, priorCalls?: any[]}} o
|
|
211
|
+
* judgeLoop?: Function|null, judgeModel?: string|null, onJudgeCost?: ((c: any) => void)|null,
|
|
212
|
+
* calibrateFn?: Function, ceilingUsd?: number|null, priorCalls?: any[]}} o
|
|
212
213
|
* @returns {Promise<{ok: boolean, specHash: string|null, seedRef: string|null,
|
|
213
214
|
* gates: any, work: any[], guards: any[], stops: any[], reds: Red[], refusal: Refusal|null}>}
|
|
214
215
|
*/
|
|
215
|
-
export function prepareSigning({ spec, workdir, seedRef, shellCapUsd, timeoutMs, seedFn, listingFn, seedReadFn, judgeLoop, onJudgeCost, calibrateFn, ceilingUsd, priorCalls, }: {
|
|
216
|
+
export function prepareSigning({ spec, workdir, seedRef, shellCapUsd, timeoutMs, seedFn, listingFn, seedReadFn, judgeLoop, judgeModel, onJudgeCost, calibrateFn, ceilingUsd, priorCalls, }: {
|
|
216
217
|
spec: any;
|
|
217
218
|
workdir: string;
|
|
218
219
|
seedRef?: string | null;
|
|
@@ -222,6 +223,7 @@ export function prepareSigning({ spec, workdir, seedRef, shellCapUsd, timeoutMs,
|
|
|
222
223
|
listingFn?: Function;
|
|
223
224
|
seedReadFn?: Function;
|
|
224
225
|
judgeLoop?: Function | null;
|
|
226
|
+
judgeModel?: string | null;
|
|
225
227
|
onJudgeCost?: ((c: any) => void) | null;
|
|
226
228
|
calibrateFn?: Function;
|
|
227
229
|
ceilingUsd?: number | null;
|
|
@@ -277,4 +279,6 @@ import { requiredAnswersFor } from './authorflow.js';
|
|
|
277
279
|
import { VERDICT_CLASSES } from './authoring.js';
|
|
278
280
|
import { LOCKED_CLASSES } from './authoring.js';
|
|
279
281
|
import { LIVE_CLASSES } from './authoring.js';
|
|
280
|
-
|
|
282
|
+
import { UNLISTED_CLASSES } from './authoring.js';
|
|
283
|
+
import { MENU_CLASSES } from './authoring.js';
|
|
284
|
+
export { QUESTION_SETS, questionsFor, requiredAnswersFor, VERDICT_CLASSES, LOCKED_CLASSES, LIVE_CLASSES, UNLISTED_CLASSES, MENU_CLASSES };
|
package/types/calibrate.d.ts
CHANGED
|
@@ -80,7 +80,13 @@ export function factsResist(facts: any, gold: any, artifactText: string): {
|
|
|
80
80
|
* graded one: `ok` is false, the cases nobody bought are absent rather than recorded
|
|
81
81
|
* as failures, and `casualty` stays null — the transport was fine, the operator's own
|
|
82
82
|
* governance stopped the gate.
|
|
83
|
-
*
|
|
83
|
+
* `judgeModel` is REQUIRED and has no default (PRD item 32.1). It is the identity
|
|
84
|
+
* the caller resolved (`resolveJudge`, src/judged.js) and it is what the graded set
|
|
85
|
+
* is STAMPED with — `setHash` folds it in, so a set certified by one judge cannot be
|
|
86
|
+
* mistaken for the same set certified by another. A default here would be the silent
|
|
87
|
+
* fall-back the whole judge-identity change exists to remove: a gate that grades
|
|
88
|
+
* under a model nobody named certifies a floor nobody can attribute.
|
|
89
|
+
* @param {{cases: any, card: any, judgeLoop: Function|null, judgeModel: string,
|
|
84
90
|
* onCost?: (c: any) => void, attempts?: number, battery?: any[], batteryCard?: any,
|
|
85
91
|
* capStop?: () => 'cap-halt'|'pricing-red'|null}} o
|
|
86
92
|
* @returns {Promise<{ok: boolean, stop: string|null, casualty: any,
|
|
@@ -90,10 +96,11 @@ export function factsResist(facts: any, gold: any, artifactText: string): {
|
|
|
90
96
|
* cardHash: string|null, casesHash: string|null, setHash: string|null,
|
|
91
97
|
* costUsd: number|null, knownUsd: number, spendComplete: boolean, calls: any[]}>}
|
|
92
98
|
*/
|
|
93
|
-
export function runCalibration({ cases, card, judgeLoop, onCost, attempts, battery, batteryCard, capStop, }: {
|
|
99
|
+
export function runCalibration({ cases, card, judgeLoop, judgeModel, onCost, attempts, battery, batteryCard, capStop, }: {
|
|
94
100
|
cases: any;
|
|
95
101
|
card: any;
|
|
96
102
|
judgeLoop: Function | null;
|
|
103
|
+
judgeModel: string;
|
|
97
104
|
onCost?: (c: any) => void;
|
|
98
105
|
attempts?: number;
|
|
99
106
|
battery?: any[];
|
package/types/cardauthor.d.ts
CHANGED
|
@@ -261,15 +261,16 @@ export function signJudgedArtifacts({ proposal, fix }: {
|
|
|
261
261
|
* - THE CASES land beside the stages as `calibration.cases` — a close-level
|
|
262
262
|
* artifact, because the set calibrates the RULER and not one stage's params,
|
|
263
263
|
* and because module 5's gate reads it before any stage runs.
|
|
264
|
-
* - THE JUDGE MODEL lands beside them as `calibration.judgeModel
|
|
265
|
-
*
|
|
266
|
-
*
|
|
267
|
-
*
|
|
268
|
-
* the set has to carry which
|
|
269
|
-
* signed floor from a
|
|
270
|
-
* a
|
|
271
|
-
* re-sign is a re-run of the calibration gate.
|
|
272
|
-
*
|
|
264
|
+
* - THE JUDGE MODEL lands beside them as `calibration.judgeModel`. Since PRD item
|
|
265
|
+
* 32.1 that is the RESOLVED judge identity the caller hands in (`resolveJudge`,
|
|
266
|
+
* src/judged.js — the spec's signed `judge` override, else the job's own worker
|
|
267
|
+
* model), not a library pin. A calibration set is only worth the judge that
|
|
268
|
+
* graded it, so the set has to carry which judge certified it or nothing
|
|
269
|
+
* downstream can tell a signed floor from a moved one. Storing it HERE, inside
|
|
270
|
+
* the spec, is what makes a judge change flip `jobSpecHash`: the signature dies,
|
|
271
|
+
* the signer re-signs, and a re-sign is a re-run of the calibration gate. It is
|
|
272
|
+
* REQUIRED, with no default — a set stamped with a model nobody named is exactly
|
|
273
|
+
* the unattributable floor this field exists to prevent (F45).
|
|
273
274
|
*
|
|
274
275
|
* A card with NOWHERE TO LAND THROWS. It is a caller's own broken input — a
|
|
275
276
|
* signed rubric folded into a close with no judged ruler — and there is no safe
|
|
@@ -278,12 +279,13 @@ export function signJudgedArtifacts({ proposal, fix }: {
|
|
|
278
279
|
* The input is never mutated: a closeDecl is a signed artefact and rewriting a
|
|
279
280
|
* caller's copy in place is how two readers come to disagree about what was
|
|
280
281
|
* signed.
|
|
281
|
-
* @param {any} closeDecl @param {{card: any, cases: any[]}} o
|
|
282
|
+
* @param {any} closeDecl @param {{card: any, cases: any[], judgeModel: string}} o
|
|
282
283
|
* @returns {any} a new closeDecl
|
|
283
284
|
*/
|
|
284
|
-
export function foldJudgedArtifacts(closeDecl: any, { card, cases }: {
|
|
285
|
+
export function foldJudgedArtifacts(closeDecl: any, { card, cases, judgeModel }: {
|
|
285
286
|
card: any;
|
|
286
287
|
cases: any[];
|
|
288
|
+
judgeModel: string;
|
|
287
289
|
}): any;
|
|
288
290
|
/**
|
|
289
291
|
* ONE compile call: the person's Q6 and Q7 in, a proposal out.
|
package/types/cli.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* code, so `bin/bareloop.mjs` can set `process.exitCode` and let node flush
|
|
4
4
|
* queued stdout on its own (F-something: `process.exit()` can discard it).
|
|
5
5
|
* @param {string[]} argv
|
|
6
|
-
* @param {{ env?: Record<string,string|undefined>, stdout?: any, stderr?: any, cwd?: string, provider?: any, providerFor?: any, judgeProvider?: any, now?: () => number, stdin?: any }} deps
|
|
6
|
+
* @param {{ env?: Record<string,string|undefined>, stdout?: any, stderr?: any, cwd?: string, provider?: any, providerFor?: any, judgeProvider?: any, judgeModel?: string|null, now?: () => number, stdin?: any }} deps
|
|
7
7
|
* @returns {Promise<number>}
|
|
8
8
|
*/
|
|
9
9
|
export function main(argv: string[], deps?: {
|
|
@@ -14,6 +14,7 @@ export function main(argv: string[], deps?: {
|
|
|
14
14
|
provider?: any;
|
|
15
15
|
providerFor?: any;
|
|
16
16
|
judgeProvider?: any;
|
|
17
|
+
judgeModel?: string | null;
|
|
17
18
|
now?: () => number;
|
|
18
19
|
stdin?: any;
|
|
19
20
|
}): Promise<number>;
|
package/types/declaredclose.d.ts
CHANGED
|
@@ -110,6 +110,7 @@ export function guardNames(closeDecl: any, verdictType: string): string[];
|
|
|
110
110
|
* seedTrees?: any, gapCap?: number, maxBuffer?: number, baselineMode?: 'auto'|'worktree',
|
|
111
111
|
* humanRuling?: {decision: string, text?: string|null}|null,
|
|
112
112
|
* judgeLoop?: ((o: {system: string}) => any)|null,
|
|
113
|
+
* judgeModel?: string|null,
|
|
113
114
|
* onJudgeCost?: ((c: any) => void)|null,
|
|
114
115
|
* callBounds?: (() => {timeoutMs?: number, deadlineMs?: number})|null}} [opts]
|
|
115
116
|
* @returns {Promise<any>}
|
|
@@ -129,6 +130,7 @@ export function runDeclaredStages(stages: any[], redact?: (s: string) => string,
|
|
|
129
130
|
judgeLoop?: ((o: {
|
|
130
131
|
system: string;
|
|
131
132
|
}) => any) | null;
|
|
133
|
+
judgeModel?: string | null;
|
|
132
134
|
onJudgeCost?: ((c: any) => void) | null;
|
|
133
135
|
callBounds?: (() => {
|
|
134
136
|
timeoutMs?: number;
|
package/types/index.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export { workBranchName, prepareWorkBranch, WORK_BRANCH_PREFIX, WORK_BRANCH_RE }
|
|
|
15
15
|
export { createReadShim, wrapReadTool, readShimArm, readShimStrategy, READ_SHIM_CAP, READ_SHIM_STRATEGY, READ_SHIM_DIFF_STRATEGY, READ_SHIM_ARMS } from "./readshim.js";
|
|
16
16
|
export { runDeclaredClose, runStage, seedRead, seedAtHead, seedListing, changedSet, makeSeedTrees, LIVE_KINDS, STOP_FAULTS, JUDGED_MARKER, EXIT_GREEN, EXIT_RED, EXIT_STOP, HUMAN_DECISIONS, SEED_EXEMPT_KINDS, JUDGED_FLOOR_KIND, normalizeHumanRuling, resolveHumanRuling, JUDGE_ATTEMPTS, MAX_JUDGED_PATHS, REVIEW_DOOR, DOOR_OPEN_OUTCOMES, REVIEW_DOOR_CLASSES, doorOpens, mechanicalStages } from "./kinds.js";
|
|
17
17
|
export { answerReviewDoor, doorRecordOf, doorAgeGate } from "./reviewdoor.js";
|
|
18
|
-
export { JUDGE_MODEL, JUDGE_MAX_TOKENS, JUDGE_RULES, JUDGE_RULE_IDS, LOCATE_AXES, LOCATE_LABEL, validateCard, validateFacts, locatePrompt, runLocate, decide, defaultJudgeLoop, CALIBRATION_SIZE, CASE_VERDICTS, validateCalibrationSet, validateJudgedArtifacts, expectedOf } from "./judged.js";
|
|
18
|
+
export { JUDGE_MODEL, resolveJudge, JUDGE_MAX_TOKENS, JUDGE_RULES, JUDGE_RULE_IDS, LOCATE_AXES, LOCATE_LABEL, validateCard, validateFacts, locatePrompt, runLocate, decide, defaultJudgeLoop, CALIBRATION_SIZE, CASE_VERDICTS, validateCalibrationSet, validateJudgedArtifacts, expectedOf } from "./judged.js";
|
|
19
19
|
export { proposeJudgedArtifacts, signJudgedArtifacts, foldJudgedArtifacts, proposalSchema, proposalTool, cardCasesPrompt, PROPOSAL_TOOL_NAME, PROPOSAL_LABEL, MAX_PROPOSAL_RETRIES, COMPILE_SYSTEM } from "./cardauthor.js";
|
|
20
20
|
export { runCalibration, compareExpectation, factsResist, artifactHash, INJECTION_LOCATE_BATTERY, INJECTION_CARD, CALIBRATION_LABEL, CASUALTY_AXES } from "./calibrate.js";
|
|
21
21
|
export { validateDeclaration, normalizeDeclaration, KIND_CATALOGUE, CATALOGUE_KINDS, CATALOGUE_LIVE_KINDS, LOCKED_KINDS, TYPES_GENRE, TYPES_GENRE_TEMPLATE, GENRE_LANGUAGES, genreEnv, genreOwnedEnvNames, genreInstruments, DIRECTIONS, BASELINES, MAX_STAGES, VERDICT_CLASSES, LOCKED_CLASSES, LIVE_CLASSES, CLASS_BATTERIES, classGuards, classMenu, closeCeiling, NEVER_OFFERED_KINDS } from "./authoring.js";
|
package/types/job.d.ts
CHANGED
|
@@ -210,7 +210,28 @@ export const CADENCE_UNITS: readonly string[];
|
|
|
210
210
|
/** PRD item 28 (2026-09-06/09-09): `openai-api` — an OpenAI-shaped provider,
|
|
211
211
|
* constructed via `src/providers.js`'s factory. hamr's ruling (PRD 30.7):
|
|
212
212
|
* `deepseek-chat` is THE secondary provider (one, not a menu of half-tested
|
|
213
|
-
* models); the factory's tier table is where that lives, not this menu.
|
|
213
|
+
* models); the factory's tier table is where that lives, not this menu.
|
|
214
|
+
*
|
|
215
|
+
* PRD item 31.3 (hamr, 2026-09-09: *"anthropic, openai, gemini drop ollama for
|
|
216
|
+
* now"*): `gemini-api` joins. That is bareloop matching bare-agent's own
|
|
217
|
+
* adapter surface rather than curating a shorter list — bare-agent exports four
|
|
218
|
+
* real HTTP backends (Anthropic, OpenAI, Gemini, Ollama) plus CLIPipe and the
|
|
219
|
+
* FallbackProvider composite; `provider-http.js`, `provider-stop-reason.js`,
|
|
220
|
+
* `provider-temperature.js` and `provider-usage.js` are shared HELPERS, not
|
|
221
|
+
* backends, and were miscounted as providers once — the export list, never the
|
|
222
|
+
* filenames, is the source of truth.
|
|
223
|
+
*
|
|
224
|
+
* Ollama is deliberately NOT here. Beyond hamr's word, it is the one entry
|
|
225
|
+
* whose economics differ in kind: it takes no API key and bills nothing, so
|
|
226
|
+
* every round would price at $0 through machinery that treats $0 as a real
|
|
227
|
+
* price. An unpriced round reported as free is the exact honesty violation the
|
|
228
|
+
* `?? 0` rule exists to stop, and admitting it needs that question answered
|
|
229
|
+
* first, not a menu entry.
|
|
230
|
+
*
|
|
231
|
+
* `gemini-api` is ADMITTED-PENDING-PROBE: the probe rule stands (item 28
|
|
232
|
+
* ruling (d)) and gemini has zero runs. See `PROBE_STATUS` in
|
|
233
|
+
* `src/providers.js` — a spec may name it, and the runner says out loud that
|
|
234
|
+
* nothing has proven it. */
|
|
214
235
|
export const PROVIDERS: readonly string[];
|
|
215
236
|
/** V3 environment label: declared keys only — every field is a lineage-key
|
|
216
237
|
* candidate at N3. `provider` is part of the key by definition (top-level,
|