bareloop 0.22.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 +247 -0
- package/README.md +1 -1
- package/bareloop.context.md +113 -29
- package/package.json +2 -2
- package/src/authorflow.js +24 -9
- package/src/authoring.js +32 -0
- package/src/authorjob.js +108 -36
- package/src/authorscout.js +83 -9
- package/src/calibrate.js +43 -12
- package/src/cardauthor.js +23 -16
- package/src/cli.js +73 -21
- package/src/declaredclose.js +25 -11
- package/src/index.js +8 -5
- package/src/job.js +92 -2
- package/src/judged.js +240 -19
- package/src/kinds.js +89 -12
- package/src/planrun.js +194 -41
- package/src/providers.js +301 -0
- package/src/ratelimit.js +116 -0
- package/src/reuse.js +9 -1
- package/src/run.js +68 -9
- package/src/transport.js +29 -12
- package/src/validate.js +4 -0
- package/types/authoring.d.ts +30 -0
- package/types/authorjob.d.ts +10 -6
- package/types/authorscout.d.ts +27 -2
- 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 +8 -1
- package/types/index.d.ts +1 -1
- package/types/job.d.ts +25 -0
- package/types/judged.d.ts +130 -16
- package/types/kinds.d.ts +51 -3
- package/types/planrun.d.ts +10 -2
- package/types/providers.d.ts +166 -0
- package/types/ratelimit.d.ts +59 -0
- package/types/run.d.ts +17 -3
- package/types/transport.d.ts +10 -4
package/src/ratelimit.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// PRD item 28's parked (a/b) ruling, hamr's verbatim call: "do a/b as a
|
|
2
|
+
// fallback, and verify/validate + no regression." A vendor HTTP 429 is a
|
|
3
|
+
// response — the provider answered, unlike src/transport.js's no-response
|
|
4
|
+
// class — but unlike every other status this repo already declines to retry
|
|
5
|
+
// (4xx/5xx generally, F115's own boundary), a 429 carries the ONE piece of
|
|
6
|
+
// information that turns "retry blindly" into "retry honestly": the vendor's
|
|
7
|
+
// own stated wait. This module is transport.js's SIBLING, not an extension
|
|
8
|
+
// of it — `isTransportFailure` must keep returning FALSE for anything
|
|
9
|
+
// carrying an HTTP status (429 included), and nothing here widens that.
|
|
10
|
+
//
|
|
11
|
+
// This is a FALLBACK, not the primary answer. The primary answer (F139,
|
|
12
|
+
// measured 2026-09-08) is picking a worker model with adequate rate
|
|
13
|
+
// headroom: gpt-4.1 = 30,000 TPM (429s deterministically on our workload,
|
|
14
|
+
// 2 of 2 runs), gpt-4.1-mini = 200,000 TPM, gpt-5-mini/gpt-5 = 500,000 TPM.
|
|
15
|
+
// One bounded retry only softens the failure mode when that primary answer
|
|
16
|
+
// was not followed (or headroom was still exceeded); it does not replace it.
|
|
17
|
+
|
|
18
|
+
/** The fixed retry budget: ONE extra attempt after a 429. Never read from a
|
|
19
|
+
* job spec or CLI flag — raising it is arbiter territory, mirroring
|
|
20
|
+
* transport.js's TRANSPORT_RETRIES exactly (tighten-only doctrine). */
|
|
21
|
+
export const RATE_LIMIT_RETRIES = 1;
|
|
22
|
+
|
|
23
|
+
/** bare-agent's `Retry.call` (if ever used) counts ATTEMPTS, not retries;
|
|
24
|
+
* this module's own hand-rolled ladder (src/planrun.js) does the same for
|
|
25
|
+
* symmetry with TRANSPORT_MAX_ATTEMPTS. */
|
|
26
|
+
export const RATE_LIMIT_MAX_ATTEMPTS = RATE_LIMIT_RETRIES + 1;
|
|
27
|
+
|
|
28
|
+
/** The hard cap on how long we will ever wait for a vendor-stated 429 delay.
|
|
29
|
+
* A vendor asking for longer than this is not honoured — we do not retry at
|
|
30
|
+
* all in that case. Waiting that long is the operator's decision, not ours
|
|
31
|
+
* (arbiter territory: nothing here escalates the wait past what hamr set). */
|
|
32
|
+
export const RATE_LIMIT_MAX_WAIT_MS = 60_000;
|
|
33
|
+
|
|
34
|
+
/** Used only when the vendor's message carries no parseable delay at all. */
|
|
35
|
+
export const RATE_LIMIT_DEFAULT_WAIT_MS = 5_000;
|
|
36
|
+
|
|
37
|
+
/** A parsed delay is the EARLIEST moment the vendor's own window resets, not
|
|
38
|
+
* a safe one to fire on the dot — this margin is added to any PARSED value
|
|
39
|
+
* (never to the default) so the retry lands just after the window, not on
|
|
40
|
+
* its leading edge. Never allowed to push a parsed value over the cap. */
|
|
41
|
+
const PARSED_DELAY_MARGIN_MS = 250;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Is `err` an HTTP 429 (rate-limited) response? True ONLY for an explicit
|
|
45
|
+
* status of 429 on the error itself. Nothing else qualifies — not message
|
|
46
|
+
* text (a message merely containing "429" is not this), not `retryable`,
|
|
47
|
+
* not any other status. Symmetric with `isTransportFailure`'s own status
|
|
48
|
+
* check (src/transport.js), just reading the opposite branch of it.
|
|
49
|
+
* @param {any} err
|
|
50
|
+
* @returns {boolean}
|
|
51
|
+
*/
|
|
52
|
+
export function isRateLimited(err) {
|
|
53
|
+
if (err == null) return false;
|
|
54
|
+
const status = err.status ?? err.statusCode;
|
|
55
|
+
return status === 429;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The vendor's RAW stated wait, in milliseconds, parsed straight from the
|
|
60
|
+
* error message — no default, no margin, no cap. `null` when nothing in the
|
|
61
|
+
* message matches a known shape. Exported separately from `rateLimitWaitMs`
|
|
62
|
+
* so a caller that wants to report the vendor's own number on the spine
|
|
63
|
+
* (`statedMs`) never has to re-derive it from the margin-adjusted figure.
|
|
64
|
+
*
|
|
65
|
+
* bare-agent does not expose the vendor's `retry-after` /
|
|
66
|
+
* `x-ratelimit-reset-*` response headers on the thrown error (BA-26) — the
|
|
67
|
+
* delay exists only in the vendor's English message text, so this parses
|
|
68
|
+
* that text. Verified real shape (OpenAI, run `8ev2sdkn`, 2026-09-08):
|
|
69
|
+
* "Please try again in 11.996s." This also supports "try again in 1.5
|
|
70
|
+
* seconds", "try again in 20ms", "retry after 30s", and "retry after 30
|
|
71
|
+
* seconds" — case-insensitive.
|
|
72
|
+
* @param {any} err
|
|
73
|
+
* @returns {number|null}
|
|
74
|
+
*/
|
|
75
|
+
export function statedWaitMs(err) {
|
|
76
|
+
const message = String(err?.message ?? err ?? '');
|
|
77
|
+
const match = message.match(/(?:try again in|retry after)\s*([\d.]+)\s*(ms|milliseconds?|s|seconds?)?/i);
|
|
78
|
+
if (!match) return null;
|
|
79
|
+
|
|
80
|
+
const value = Number(match[1]);
|
|
81
|
+
if (!Number.isFinite(value) || value < 0) return null;
|
|
82
|
+
|
|
83
|
+
const unit = (match[2] ?? 's').toLowerCase();
|
|
84
|
+
const isMs = unit === 'ms' || unit.startsWith('millisecond');
|
|
85
|
+
return isMs ? value : value * 1000;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* How long to wait before retrying a 429, in milliseconds — or `null`
|
|
90
|
+
* meaning "do not retry at all" (either the parsed wait exceeds
|
|
91
|
+
* `RATE_LIMIT_MAX_WAIT_MS`, in which case waiting that long is the
|
|
92
|
+
* operator's decision, not ours).
|
|
93
|
+
*
|
|
94
|
+
* A parsed value gets `PARSED_DELAY_MARGIN_MS` added (the vendor's stated
|
|
95
|
+
* number is the earliest moment its window resets, not a safe one to fire
|
|
96
|
+
* on), capped so the margin itself can never push a parsed value over
|
|
97
|
+
* `RATE_LIMIT_MAX_WAIT_MS`. When nothing parses, `RATE_LIMIT_DEFAULT_WAIT_MS`
|
|
98
|
+
* is returned as-is, with no margin added (there is nothing to add margin
|
|
99
|
+
* to — it is already a conservative guess, not an earliest-moment reading).
|
|
100
|
+
* @param {any} err
|
|
101
|
+
* @returns {number|null}
|
|
102
|
+
*/
|
|
103
|
+
export function rateLimitWaitMs(err) {
|
|
104
|
+
const parsedMs = statedWaitMs(err);
|
|
105
|
+
if (parsedMs === null) {
|
|
106
|
+
return RATE_LIMIT_DEFAULT_WAIT_MS <= RATE_LIMIT_MAX_WAIT_MS ? RATE_LIMIT_DEFAULT_WAIT_MS : null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// The RAW parsed value is what is judged against the cap ("a vendor asking
|
|
110
|
+
// for longer than this is not honoured") — the margin is added only AFTER
|
|
111
|
+
// that gate, and clamped so it can never itself push the result past the
|
|
112
|
+
// cap, but it never turns an already-honoured wait into a refusal.
|
|
113
|
+
if (parsedMs > RATE_LIMIT_MAX_WAIT_MS) return null;
|
|
114
|
+
if (parsedMs === 0) return 0;
|
|
115
|
+
return Math.min(parsedMs + PARSED_DELAY_MARGIN_MS, RATE_LIMIT_MAX_WAIT_MS);
|
|
116
|
+
}
|
package/src/reuse.js
CHANGED
|
@@ -60,6 +60,7 @@ import { readGrade } from './trend.js';
|
|
|
60
60
|
import { HITL_PAUSE, HUMAN_CHECKPOINTS } from './declaredclose.js';
|
|
61
61
|
import { extractArtifact, priceOf } from './text.js';
|
|
62
62
|
import { runJob as shippedRunJob, ACCOUNTED_ROUND_TYPES } from './run.js';
|
|
63
|
+
import { PROVIDER_TIMEOUT_MS } from './clock.js';
|
|
63
64
|
|
|
64
65
|
const require = createRequire(import.meta.url);
|
|
65
66
|
const { Loop } = require('bare-agent');
|
|
@@ -416,7 +417,14 @@ export async function selectBridge({ registry, job, ask, provider, pinned = null
|
|
|
416
417
|
let result;
|
|
417
418
|
try {
|
|
418
419
|
const loop = new Loop({ provider });
|
|
419
|
-
|
|
420
|
+
// F152/PRD 30.4 — the picker's call carried no time bound at all: a live
|
|
421
|
+
// endpoint that accepts and never answers hangs the process forever
|
|
422
|
+
// (measured elsewhere in this repo). This runs BETWEEN bridge tries, not
|
|
423
|
+
// inside any one try's own signed `perTryWallMs`, so there is no wall to
|
|
424
|
+
// derive a tighter number from — the same "no clock here" reasoning
|
|
425
|
+
// `authorscout.js` documents, and the same fallback: the idle-timeout
|
|
426
|
+
// default the run path itself uses when ITS OWN wall is unbounded.
|
|
427
|
+
result = await loop.run([{ role: 'user', content: prompt }], [], { maxTokens: SELECTION_MAX_TOKENS, timeoutMs: PROVIDER_TIMEOUT_MS });
|
|
420
428
|
} catch (e) {
|
|
421
429
|
// a throw out of the selection call is TRANSPORT — a casualty, and the run stops on
|
|
422
430
|
// it rather than quietly drafting cold on a decision that was never made
|
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
|
|
@@ -228,8 +233,16 @@ async function primitiveSmoke(workdir) {
|
|
|
228
233
|
* 'close-red' | 'close-unsupported' | 'recipe-stale' | 'branch-red' | 'pricing-red' | 'provider-red' |
|
|
229
234
|
* 'interpreter-red' | 'cap-halt' | 'wall-halt' | 'step-stalled' |
|
|
230
235
|
* 'hitl-pause' | 'hitl-decision-red' | `step-red:<id>`
|
|
236
|
+
* NOTE: `job-end` can ALSO land with outcome 'runner-drained' (F140/PRD item
|
|
237
|
+
* 28(c)) — the `beforeExit` backstop's own terminal, emitted from OUTSIDE this
|
|
238
|
+
* function's normal control flow when the process drains with no `job-end` at
|
|
239
|
+
* all (a provider promise that never settled). It never appears as this
|
|
240
|
+
* function's own RETURN value (nothing is left to receive it), only as the
|
|
241
|
+
* spine's terminal record. Non-zero exit, `spendComplete:false` always, and
|
|
242
|
+
* deliberately NOT in the resumable/checkpoint set (`src/reuse.js`) — an
|
|
243
|
+
* unknown in-flight state is not a known-safe resume point.
|
|
231
244
|
*/
|
|
232
|
-
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 }) {
|
|
233
246
|
// THE READ SHIM's ARM, resolved at the door — the FIRST thing this entry does,
|
|
234
247
|
// before the ledger, before the approval gate, before a byte of the spec is read.
|
|
235
248
|
// An unrecognised spelling throws here at zero cost instead of being coerced by
|
|
@@ -345,6 +358,15 @@ export async function runJob(rawSpec, { approvals, workdir, provider, nativeProv
|
|
|
345
358
|
// than guessing.
|
|
346
359
|
verdictType: job.verdictType,
|
|
347
360
|
...(typeof provider?.model === 'string' ? { model: provider.model } : {}),
|
|
361
|
+
// PRD item 28 / F156 — WHICH provider actually ran, and against which
|
|
362
|
+
// endpoint. Report-only, but without it an archived spine cannot be
|
|
363
|
+
// segmented by provider at all: every cost, duration and outcome row from
|
|
364
|
+
// the day openai-api joined the menu would pool anthropic and non-anthropic
|
|
365
|
+
// runs into one contaminated aggregate, which is the reading error this repo
|
|
366
|
+
// has already paid for once. `baseUrl` rides along only when the job set one
|
|
367
|
+
// (an absent baseUrl is the vendor's own host, not an unknown).
|
|
368
|
+
provider: typeof job.provider === 'string' ? job.provider : null,
|
|
369
|
+
...(typeof job.baseUrl === 'string' ? { baseUrl: job.baseUrl } : {}),
|
|
348
370
|
// F118: replay reads this; report-only. `dirty` is deliberately omitted —
|
|
349
371
|
// it can't be known without a shell, and null = unknown, never faked.
|
|
350
372
|
code: (() => { const { version, sha } = codeVersion(); return { version, sha }; })(),
|
|
@@ -380,12 +402,46 @@ export async function runJob(rawSpec, { approvals, workdir, provider, nativeProv
|
|
|
380
402
|
: {}),
|
|
381
403
|
});
|
|
382
404
|
|
|
405
|
+
// F140/PRD item 28 (c) — the backstop that fires on the ABSENCE of a
|
|
406
|
+
// `job-end`. A provider promise that never settles (BA-25's class; 0.42.0
|
|
407
|
+
// closed the known drop cases per F141 but the class itself is "an
|
|
408
|
+
// instrument fires on absence", not a specific bug) lets node drain with
|
|
409
|
+
// nothing else holding the event loop, and the process exits with no
|
|
410
|
+
// terminal record at all — no escalation, no outcome, no way to tell a
|
|
411
|
+
// silent drain from an operator kill. `ended` is the one flag every
|
|
412
|
+
// job-end site sets via the local `end()` helper below (never a second
|
|
413
|
+
// spelling of "did we finish" that could fall out of sync with the real
|
|
414
|
+
// emits). One `beforeExit` listener per call, removed in the `finally` so
|
|
415
|
+
// multiple runs in one process (tests included) never leak listeners.
|
|
416
|
+
let ended = false;
|
|
417
|
+
/** @type {(outcome: string, extra?: object) => object} */
|
|
418
|
+
const end = (outcome, extra = {}) => {
|
|
419
|
+
ended = true;
|
|
420
|
+
return emit('job-end', { outcome, ...extra });
|
|
421
|
+
};
|
|
422
|
+
const onBeforeExit = () => {
|
|
423
|
+
if (ended) return;
|
|
424
|
+
// A drained run's in-flight spend is unknowable — the floor already
|
|
425
|
+
// accumulated is honest, an exact-looking total would not be (F6).
|
|
426
|
+
end('runner-drained', {
|
|
427
|
+
...spend(),
|
|
428
|
+
spendComplete: false,
|
|
429
|
+
detail: 'process drained with no job-end — a provider call never settled (F140)',
|
|
430
|
+
});
|
|
431
|
+
// Never process.exit() (can discard queued stdout) — set the code and
|
|
432
|
+
// let node exit on its own; preserve a more specific code if one is
|
|
433
|
+
// already set.
|
|
434
|
+
process.exitCode = process.exitCode || 1;
|
|
435
|
+
};
|
|
436
|
+
process.once('beforeExit', onBeforeExit);
|
|
437
|
+
|
|
438
|
+
try {
|
|
383
439
|
// 2. known-answer smoke before tokens (A3: silent degradation throws nothing)
|
|
384
440
|
const smoke = await primitiveSmoke(workdir);
|
|
385
441
|
emit('primitive-smoke', smoke);
|
|
386
442
|
if (!smoke.ok) {
|
|
387
443
|
emit('escalation', { category: 'smoke-red', decisionReady: true, decision: `The ${smoke.primitive} primitive failed its known-answer check — no run verdict is trustworthy on a degraded primitive.`, options: ['fix the primitive/store', 'abandon the run'], detail: smoke.detail });
|
|
388
|
-
|
|
444
|
+
end('smoke-red', spend());
|
|
389
445
|
return 'smoke-red';
|
|
390
446
|
}
|
|
391
447
|
|
|
@@ -426,7 +482,7 @@ export async function runJob(rawSpec, { approvals, workdir, provider, nativeProv
|
|
|
426
482
|
};
|
|
427
483
|
const pricingRed = () => {
|
|
428
484
|
emit('escalation', { category: 'pricing-red', decisionReady: true, decision: 'A provider result carried no priced cost — the hard cap cannot govern spend it cannot see (unpriced is never free, F6).', options: ['bind a priced provider/model', 'abandon the run'], spentUsd });
|
|
429
|
-
|
|
485
|
+
end('pricing-red', spend());
|
|
430
486
|
return 'pricing-red';
|
|
431
487
|
};
|
|
432
488
|
|
|
@@ -436,14 +492,14 @@ export async function runJob(rawSpec, { approvals, workdir, provider, nativeProv
|
|
|
436
492
|
// accounts it natively (F12) and the job-end money contract is unchanged.
|
|
437
493
|
{
|
|
438
494
|
const outcome = await runPlan(job, {
|
|
439
|
-
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,
|
|
440
496
|
remainingUsd: () => Math.min(shellCapUsd, job.budgetUsd - spentUsd),
|
|
441
497
|
isUnpriced: () => unpriced, // F6: let the plan flow bail in-flight, not just after it returns
|
|
442
498
|
spendComplete, // …and let its money-halt readout say whether the remaining it quotes is exact
|
|
443
499
|
});
|
|
444
500
|
if (unpriced) return pricingRed();
|
|
445
501
|
if (outcome.startsWith('step-red:')) {
|
|
446
|
-
|
|
502
|
+
end('step-red', { step: outcome.slice('step-red:'.length), ...spend() });
|
|
447
503
|
} else if (outcome === 'provider-red' || outcome === 'step-stalled') {
|
|
448
504
|
// F44: a transport-throw provider-red never returned a usage figure for the
|
|
449
505
|
// failed call, so the priced sum is a FLOOR, not the total — spendComplete
|
|
@@ -453,10 +509,13 @@ export async function runJob(rawSpec, { approvals, workdir, provider, nativeProv
|
|
|
453
509
|
// the known cost of self-heal — and an unbilled reissue is indistinguishable
|
|
454
510
|
// from a billed one from here. Reporting the priced sum as exact would be F6
|
|
455
511
|
// in a self-heal coat.
|
|
456
|
-
|
|
512
|
+
end(outcome, { ...spend(), spendComplete: false });
|
|
457
513
|
} else {
|
|
458
|
-
|
|
514
|
+
end(outcome, spend());
|
|
459
515
|
}
|
|
460
516
|
return outcome;
|
|
461
517
|
}
|
|
518
|
+
} finally {
|
|
519
|
+
process.removeListener('beforeExit', onBeforeExit);
|
|
520
|
+
}
|
|
462
521
|
}
|
package/src/transport.js
CHANGED
|
@@ -3,14 +3,18 @@
|
|
|
3
3
|
//
|
|
4
4
|
// A transport failure is fetch THROWING — the request never produced an HTTP
|
|
5
5
|
// response at all (a TLS fault mid-record, a reset socket, a DNS/network
|
|
6
|
-
// failure)
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
6
|
+
// failure) — OR bare-agent's own BA-25 `guardResponseSettles` rejecting a
|
|
7
|
+
// response cut after headers but before the body completed (a peer
|
|
8
|
+
// `'aborted'`/`'error'`/premature `'close'`), tagged `context.bound:
|
|
9
|
+
// 'transport'` (bare-agent 0.42.0, node_modules/bare-agent/src/provider-http.js).
|
|
10
|
+
// That is a DIFFERENT unknown from an HTTP response the provider DID send
|
|
11
|
+
// (4xx/5xx/429): a response means the provider answered, so bare-agent's own
|
|
12
|
+
// retry policy for those stays exactly what it is — this module never widens
|
|
13
|
+
// that. Retrying only the no-response case is the whole point: a call that
|
|
14
|
+
// dies mid-read may already have been billed (retry can pay twice), so the
|
|
15
|
+
// retry budget stays as tight as hamr authorized it — exactly one extra
|
|
16
|
+
// attempt, never configurable upward from a job spec or argv (tighten-only
|
|
17
|
+
// doctrine).
|
|
14
18
|
//
|
|
15
19
|
// Scope: this module wires ONE seam — the worker Loop built in
|
|
16
20
|
// src/planrun.js's `newLoop` (~line 2239), which is the ONE factory behind
|
|
@@ -31,11 +35,17 @@ export const TRANSPORT_MAX_ATTEMPTS = TRANSPORT_RETRIES + 1;
|
|
|
31
35
|
* Is `err` a transport-class failure — the request never produced an HTTP
|
|
32
36
|
* response? True for: a TLS fault mid-record ("bad record mac" and kin), a
|
|
33
37
|
* reset/broken/timed-out socket (ECONNRESET, EPIPE, ETIMEDOUT, ECONNREFUSED,
|
|
34
|
-
* ENETUNREACH — by code on the error itself or on `err.cause`),
|
|
35
|
-
*
|
|
36
|
-
*
|
|
38
|
+
* ENETUNREACH — by code on the error itself or on `err.cause`), `fetch
|
|
39
|
+
* failed` wrapping a network `cause`, and — as of bare-agent 0.42.0 (BA-25)
|
|
40
|
+
* — a `ProviderError` whose `err.context.bound === 'transport'`: the
|
|
41
|
+
* response stream ended (`'aborted'`/`'error'`/premature `'close'`) after
|
|
42
|
+
* headers but before the body completed, which bare-agent itself rejects as
|
|
43
|
+
* retryable with no HTTP status. False for anything carrying an HTTP status
|
|
44
|
+
* (`err.status`/`err.statusCode`, 4xx/5xx/429) or an explicit
|
|
37
45
|
* `err.retryable === false` — those are provider RESPONSES, not transport
|
|
38
|
-
* casualties, and bare-agent's own policy governs them unchanged
|
|
46
|
+
* casualties, and bare-agent's own policy governs them unchanged; both
|
|
47
|
+
* guards run BEFORE the `context.bound` check, so a status or an explicit
|
|
48
|
+
* `retryable:false` wins even if `bound` says 'transport'.
|
|
39
49
|
* @param {any} err
|
|
40
50
|
* @returns {boolean}
|
|
41
51
|
*/
|
|
@@ -47,6 +57,13 @@ export function isTransportFailure(err) {
|
|
|
47
57
|
if (typeof status === 'number') return false;
|
|
48
58
|
if (err.retryable === false) return false;
|
|
49
59
|
|
|
60
|
+
// BA-25 (bare-agent 0.42.0): bare-agent's own transport classification —
|
|
61
|
+
// `guardResponseSettles` tags a body-cut-after-headers rejection with
|
|
62
|
+
// `context.bound: 'transport'`. Only 'transport' qualifies here — a
|
|
63
|
+
// sibling bound like 'idle' (BA-19's deadline-idle guard) already
|
|
64
|
+
// qualifies via its ETIMEDOUT code below, never by bound alone.
|
|
65
|
+
if (err.context && err.context.bound === 'transport') return true;
|
|
66
|
+
|
|
50
67
|
const codeOf = (e) => (e && typeof e.code === 'string' ? e.code : null);
|
|
51
68
|
const TRANSPORT_CODES = new Set(['ECONNRESET', 'EPIPE', 'ETIMEDOUT', 'ECONNREFUSED', 'ENETUNREACH', 'EAI_AGAIN']);
|
|
52
69
|
const code = codeOf(err) ?? codeOf(err.cause);
|
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/authorscout.d.ts
CHANGED
|
@@ -91,7 +91,7 @@ export function defaultSurveyor({ workdir, granted, auditPath, ctx }: {
|
|
|
91
91
|
*
|
|
92
92
|
* @param {{workdir: string, provider?: any,
|
|
93
93
|
* rounds?: number, minBytes?: number, blobMax?: number, maxTokens?: number, ctx?: boolean,
|
|
94
|
-
* attempts?: number, ceilingUsd?: number|null,
|
|
94
|
+
* attempts?: number, ceilingUsd?: number|null, callTimeoutMs?: number,
|
|
95
95
|
* onCall?: (call: {label: string, costUsd: number|null, unpricedRounds: number}) => void,
|
|
96
96
|
* createLoop?: (o: {system: string, policy: any, onLlmResult: any, provider: any}) => any,
|
|
97
97
|
* createSurveyor?: (o: {workdir: string, granted: readonly string[], ctx: boolean}) => Promise<any>}} o
|
|
@@ -99,7 +99,7 @@ export function defaultSurveyor({ workdir, granted, auditPath, ctx }: {
|
|
|
99
99
|
* calls: {label: string, costUsd: number|null, unpricedRounds: number}[],
|
|
100
100
|
* budgetStop: 'cap-halt'|'pricing-red'|null}>}
|
|
101
101
|
*/
|
|
102
|
-
export function runAuthorScout({ workdir, provider, rounds, minBytes, blobMax, maxTokens, ctx, attempts, ceilingUsd, onCall, createLoop, createSurveyor, }: {
|
|
102
|
+
export function runAuthorScout({ workdir, provider, rounds, minBytes, blobMax, maxTokens, ctx, attempts, ceilingUsd, callTimeoutMs, onCall, createLoop, createSurveyor, }: {
|
|
103
103
|
workdir: string;
|
|
104
104
|
provider?: any;
|
|
105
105
|
rounds?: number;
|
|
@@ -109,6 +109,7 @@ export function runAuthorScout({ workdir, provider, rounds, minBytes, blobMax, m
|
|
|
109
109
|
ctx?: boolean;
|
|
110
110
|
attempts?: number;
|
|
111
111
|
ceilingUsd?: number | null;
|
|
112
|
+
callTimeoutMs?: number;
|
|
112
113
|
onCall?: (call: {
|
|
113
114
|
label: string;
|
|
114
115
|
costUsd: number | null;
|
|
@@ -205,6 +206,30 @@ export function buildSeedListing({ workdir, seedRef, sourcePaths, testPaths, lis
|
|
|
205
206
|
tier: string;
|
|
206
207
|
};
|
|
207
208
|
}>;
|
|
209
|
+
/**
|
|
210
|
+
* F152/PRD 30.4 — the scout's per-call IDLE bound. Every `loop.run()` in this
|
|
211
|
+
* module carried NO time bound at all: a live endpoint that accepts and never
|
|
212
|
+
* answers hangs the process forever (measured) — an open socket is an active
|
|
213
|
+
* handle, so node never drains and no backstop can fire.
|
|
214
|
+
*
|
|
215
|
+
* UNLIKE the run path (`callBounds()`, src/planrun.js:1518), this pipeline has
|
|
216
|
+
* NO wall clock to derive a bound FROM — the authoring pipeline is genuinely
|
|
217
|
+
* clockless (`src/authorjob.js`/`src/authorflow.js`/`scripts/run-author.mjs`
|
|
218
|
+
* carry a money ceiling, `ceilingUsd`/`capStop`, and never a `maxWallMs`).
|
|
219
|
+
* Rather than inventing a fresh number, this reuses the EXACT ceiling the run
|
|
220
|
+
* path itself falls back to when ITS OWN wall is unbounded
|
|
221
|
+
* (`PROVIDER_TIMEOUT_MS`, `src/clock.js` — BA-18's idle timeout, already an
|
|
222
|
+
* approved "no wall present" default one layer up): an authoring-time call is
|
|
223
|
+
* the identical situation, one call earlier.
|
|
224
|
+
*
|
|
225
|
+
* It bounds SOCKET INACTIVITY only (reset by every byte, per BA-18) — a call
|
|
226
|
+
* that is merely slow keeps streaming and is untouched; only a truly silent
|
|
227
|
+
* endpoint trips it. TIGHTEN-ONLY, like every cap in this file: this is an
|
|
228
|
+
* operator-set ceiling fixed here, not a param a caller or a signed spec may
|
|
229
|
+
* widen (a defaulted cap is a silent second ceiling, and there is no clock
|
|
230
|
+
* whose narrower reading could ever tighten it further).
|
|
231
|
+
*/
|
|
232
|
+
export const AUTHOR_CALL_TIMEOUT_MS: 600000;
|
|
208
233
|
/** src/planrun.js:42 — the survey's hard round bound. */
|
|
209
234
|
export const AUTHOR_SCOUT_ROUNDS: 8;
|
|
210
235
|
/** src/planrun.js:44 — the survey is a PROMPT INGREDIENT for the authoring call. */
|
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,7 +110,9 @@ 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
|
-
*
|
|
113
|
+
* judgeModel?: string|null,
|
|
114
|
+
* onJudgeCost?: ((c: any) => void)|null,
|
|
115
|
+
* callBounds?: (() => {timeoutMs?: number, deadlineMs?: number})|null}} [opts]
|
|
114
116
|
* @returns {Promise<any>}
|
|
115
117
|
*/
|
|
116
118
|
export function runDeclaredStages(stages: any[], redact?: (s: string) => string, opts?: {
|
|
@@ -128,7 +130,12 @@ export function runDeclaredStages(stages: any[], redact?: (s: string) => string,
|
|
|
128
130
|
judgeLoop?: ((o: {
|
|
129
131
|
system: string;
|
|
130
132
|
}) => any) | null;
|
|
133
|
+
judgeModel?: string | null;
|
|
131
134
|
onJudgeCost?: ((c: any) => void) | null;
|
|
135
|
+
callBounds?: (() => {
|
|
136
|
+
timeoutMs?: number;
|
|
137
|
+
deadlineMs?: number;
|
|
138
|
+
}) | null;
|
|
132
139
|
}): Promise<any>;
|
|
133
140
|
/**
|
|
134
141
|
* The trend reader's input for ONE close verdict, whichever executor produced
|