bareloop 0.22.0 → 0.23.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 +125 -0
- package/README.md +1 -1
- package/bareloop.context.md +60 -13
- package/package.json +2 -2
- package/src/authorflow.js +7 -2
- package/src/authorscout.js +83 -9
- package/src/calibrate.js +10 -0
- package/src/cli.js +40 -19
- package/src/declaredclose.js +9 -2
- package/src/job.js +37 -2
- package/src/judged.js +108 -5
- package/src/kinds.js +12 -0
- package/src/planrun.js +180 -39
- package/src/providers.js +176 -0
- package/src/ratelimit.js +116 -0
- package/src/reuse.js +9 -1
- package/src/run.js +59 -5
- package/src/transport.js +29 -12
- package/types/authorscout.d.ts +27 -2
- package/types/declaredclose.d.ts +6 -1
- package/types/job.d.ts +4 -0
- package/types/judged.d.ts +7 -2
- package/types/kinds.d.ts +9 -0
- package/types/providers.d.ts +104 -0
- package/types/ratelimit.d.ts +59 -0
- package/types/run.d.ts +8 -0
- package/types/transport.d.ts +10 -4
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Look up a provider's table entry. Throws a NAMED error on an unrecognized
|
|
3
|
+
* provider — never a silent default (the `resolveRates`-style-substring-
|
|
4
|
+
* match lesson: a forward hazard even at zero observed rounds, MEMORY.md).
|
|
5
|
+
* `src/job.js`'s `PROVIDERS` menu is the one place a spec's `provider`
|
|
6
|
+
* field is validated; this is the runtime construction seam it feeds.
|
|
7
|
+
* @param {string} providerName
|
|
8
|
+
* @returns {ProviderTableEntry}
|
|
9
|
+
*/
|
|
10
|
+
export function resolveProvider(providerName: string): ProviderTableEntry;
|
|
11
|
+
/**
|
|
12
|
+
* Construct ONE provider instance for `providerName`/`model`, applying
|
|
13
|
+
* `exposeErrorBody: true` (F153 — every constructed provider, unconditionally)
|
|
14
|
+
* and the model's own request-key gating (`paramsFor`). `baseUrl` (PRD item
|
|
15
|
+
* 28, ruling (d), 2026-09-09: admitted in v1) is forwarded only when given —
|
|
16
|
+
* every provider constructor here defaults it on its own when absent.
|
|
17
|
+
* @param {string} providerName
|
|
18
|
+
* @param {{apiKey?: string, model?: string, baseUrl?: string}} [options]
|
|
19
|
+
*/
|
|
20
|
+
export function makeProvider(providerName: string, { apiKey, model, baseUrl }?: {
|
|
21
|
+
apiKey?: string;
|
|
22
|
+
model?: string;
|
|
23
|
+
baseUrl?: string;
|
|
24
|
+
}): any;
|
|
25
|
+
/**
|
|
26
|
+
* Build the `{provider, providerFor, judgeProvider}` triple both runners
|
|
27
|
+
* need (`src/cli.js`'s `buildProviders`, `scripts/run-u.mjs`'s top-level
|
|
28
|
+
* wiring) — ONE seam instead of two copies of the same construction and
|
|
29
|
+
* tier-memoization logic. `model`/`tierModels` are already-RESOLVED inputs
|
|
30
|
+
* (the caller runs `resolveWorkerModel` itself, exactly as both runners did
|
|
31
|
+
* before this factory existed — that decision stays where each runner's own
|
|
32
|
+
* `--model` flag/spec precedence lives, not duplicated in here).
|
|
33
|
+
*
|
|
34
|
+
* The JUDGE stays pinned to `anthropic-api`/`judgeModel` regardless of
|
|
35
|
+
* `providerName` — arbiter territory (PRD item 28: "the judge stays PINNED
|
|
36
|
+
* … regardless of the job's worker provider"). A signed `judge:{provider,
|
|
37
|
+
* model}` field with its own calibration record is PRD item 28 part (2),
|
|
38
|
+
* NOT built here; until it lands, the judge always needs its own
|
|
39
|
+
* `judgeApiKey` (today, always `ANTHROPIC_API_KEY`), independent of which
|
|
40
|
+
* key the worker used.
|
|
41
|
+
* @param {object} args
|
|
42
|
+
* @param {string} args.providerName spec.provider (or the anthropic-api default)
|
|
43
|
+
* @param {string|undefined} args.apiKey the WORKER's key, read from the provider's own `envKey`
|
|
44
|
+
* @param {string} args.model the already-resolved worker model id
|
|
45
|
+
* @param {Readonly<Record<string, string>>} args.tierModels the already-resolved tier -> model map (spec override folded in by the caller)
|
|
46
|
+
* @param {string|undefined} [args.baseUrl] spec.baseUrl, if any
|
|
47
|
+
* @param {string|undefined} args.judgeApiKey the judge's own key (today: `ANTHROPIC_API_KEY`)
|
|
48
|
+
* @param {string} args.judgeModel `JUDGE_MODEL`
|
|
49
|
+
*/
|
|
50
|
+
export function buildRunnerProviders({ providerName, apiKey, model, tierModels, baseUrl, judgeApiKey, judgeModel, }: {
|
|
51
|
+
providerName: string;
|
|
52
|
+
apiKey: string | undefined;
|
|
53
|
+
model: string;
|
|
54
|
+
tierModels: Readonly<Record<string, string>>;
|
|
55
|
+
baseUrl?: string | undefined;
|
|
56
|
+
judgeApiKey: string | undefined;
|
|
57
|
+
judgeModel: string;
|
|
58
|
+
}): {
|
|
59
|
+
provider: any;
|
|
60
|
+
providerFor: (tier: string) => any;
|
|
61
|
+
judgeProvider: any;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Today's anthropic-api tier table — EXACTLY the `DEFAULT_TIER_MODELS` both
|
|
65
|
+
* runners hardcoded before this factory existed. Any change to these two
|
|
66
|
+
* values is a change to shipped anthropic-api behaviour, not a refactor.
|
|
67
|
+
*/
|
|
68
|
+
export const ANTHROPIC_TIER_MODELS: Readonly<{
|
|
69
|
+
sonnet: "claude-sonnet-5";
|
|
70
|
+
haiku: "claude-haiku-4-5-20251001";
|
|
71
|
+
}>;
|
|
72
|
+
/**
|
|
73
|
+
* DeepSeek (F149/F150) is a SINGLE model, not a tiered family — hamr's
|
|
74
|
+
* ruling above: one secondary provider, not a menu. Both tiers map to the
|
|
75
|
+
* same model id ON PURPOSE: `resolveWorkerModel`/a runner's `providerFor
|
|
76
|
+
* (tier)` only ever LOOK UP a tier's model id (src/job.js:818-827,
|
|
77
|
+
* scripts/run-u.mjs's `providerFor`) — neither assumes the tiers name
|
|
78
|
+
* different models — so repeating the one id is the honest representation
|
|
79
|
+
* of a single-model provider, not a second model masquerading as `haiku`.
|
|
80
|
+
* If DeepSeek (or a future openai-api entrant) ever ships a genuinely
|
|
81
|
+
* cheaper tier model, this is where it would earn its own `haiku` id.
|
|
82
|
+
*/
|
|
83
|
+
export const OPENAI_TIER_MODELS: Readonly<{
|
|
84
|
+
sonnet: "deepseek-chat";
|
|
85
|
+
haiku: "deepseek-chat";
|
|
86
|
+
}>;
|
|
87
|
+
export type ProviderTableEntry = {
|
|
88
|
+
/**
|
|
89
|
+
* the bare-agent provider class
|
|
90
|
+
*/
|
|
91
|
+
ctor: new (options: any) => any;
|
|
92
|
+
/**
|
|
93
|
+
* the environment variable this provider reads its key from
|
|
94
|
+
*/
|
|
95
|
+
envKey: string;
|
|
96
|
+
/**
|
|
97
|
+
* tier name -> model id
|
|
98
|
+
*/
|
|
99
|
+
tiers: Readonly<Record<string, string>>;
|
|
100
|
+
/**
|
|
101
|
+
* per-model request-key/param gating, applied on TOP of {exposeErrorBody:true, apiKey, model, baseUrl?}
|
|
102
|
+
*/
|
|
103
|
+
paramsFor: (model: string | undefined) => Record<string, any>;
|
|
104
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Is `err` an HTTP 429 (rate-limited) response? True ONLY for an explicit
|
|
3
|
+
* status of 429 on the error itself. Nothing else qualifies — not message
|
|
4
|
+
* text (a message merely containing "429" is not this), not `retryable`,
|
|
5
|
+
* not any other status. Symmetric with `isTransportFailure`'s own status
|
|
6
|
+
* check (src/transport.js), just reading the opposite branch of it.
|
|
7
|
+
* @param {any} err
|
|
8
|
+
* @returns {boolean}
|
|
9
|
+
*/
|
|
10
|
+
export function isRateLimited(err: any): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* The vendor's RAW stated wait, in milliseconds, parsed straight from the
|
|
13
|
+
* error message — no default, no margin, no cap. `null` when nothing in the
|
|
14
|
+
* message matches a known shape. Exported separately from `rateLimitWaitMs`
|
|
15
|
+
* so a caller that wants to report the vendor's own number on the spine
|
|
16
|
+
* (`statedMs`) never has to re-derive it from the margin-adjusted figure.
|
|
17
|
+
*
|
|
18
|
+
* bare-agent does not expose the vendor's `retry-after` /
|
|
19
|
+
* `x-ratelimit-reset-*` response headers on the thrown error (BA-26) — the
|
|
20
|
+
* delay exists only in the vendor's English message text, so this parses
|
|
21
|
+
* that text. Verified real shape (OpenAI, run `8ev2sdkn`, 2026-09-08):
|
|
22
|
+
* "Please try again in 11.996s." This also supports "try again in 1.5
|
|
23
|
+
* seconds", "try again in 20ms", "retry after 30s", and "retry after 30
|
|
24
|
+
* seconds" — case-insensitive.
|
|
25
|
+
* @param {any} err
|
|
26
|
+
* @returns {number|null}
|
|
27
|
+
*/
|
|
28
|
+
export function statedWaitMs(err: any): number | null;
|
|
29
|
+
/**
|
|
30
|
+
* How long to wait before retrying a 429, in milliseconds — or `null`
|
|
31
|
+
* meaning "do not retry at all" (either the parsed wait exceeds
|
|
32
|
+
* `RATE_LIMIT_MAX_WAIT_MS`, in which case waiting that long is the
|
|
33
|
+
* operator's decision, not ours).
|
|
34
|
+
*
|
|
35
|
+
* A parsed value gets `PARSED_DELAY_MARGIN_MS` added (the vendor's stated
|
|
36
|
+
* number is the earliest moment its window resets, not a safe one to fire
|
|
37
|
+
* on), capped so the margin itself can never push a parsed value over
|
|
38
|
+
* `RATE_LIMIT_MAX_WAIT_MS`. When nothing parses, `RATE_LIMIT_DEFAULT_WAIT_MS`
|
|
39
|
+
* is returned as-is, with no margin added (there is nothing to add margin
|
|
40
|
+
* to — it is already a conservative guess, not an earliest-moment reading).
|
|
41
|
+
* @param {any} err
|
|
42
|
+
* @returns {number|null}
|
|
43
|
+
*/
|
|
44
|
+
export function rateLimitWaitMs(err: any): number | null;
|
|
45
|
+
/** The fixed retry budget: ONE extra attempt after a 429. Never read from a
|
|
46
|
+
* job spec or CLI flag — raising it is arbiter territory, mirroring
|
|
47
|
+
* transport.js's TRANSPORT_RETRIES exactly (tighten-only doctrine). */
|
|
48
|
+
export const RATE_LIMIT_RETRIES: 1;
|
|
49
|
+
/** bare-agent's `Retry.call` (if ever used) counts ATTEMPTS, not retries;
|
|
50
|
+
* this module's own hand-rolled ladder (src/planrun.js) does the same for
|
|
51
|
+
* symmetry with TRANSPORT_MAX_ATTEMPTS. */
|
|
52
|
+
export const RATE_LIMIT_MAX_ATTEMPTS: number;
|
|
53
|
+
/** The hard cap on how long we will ever wait for a vendor-stated 429 delay.
|
|
54
|
+
* A vendor asking for longer than this is not honoured — we do not retry at
|
|
55
|
+
* all in that case. Waiting that long is the operator's decision, not ours
|
|
56
|
+
* (arbiter territory: nothing here escalates the wait past what hamr set). */
|
|
57
|
+
export const RATE_LIMIT_MAX_WAIT_MS: 60000;
|
|
58
|
+
/** Used only when the vendor's message carries no parseable delay at all. */
|
|
59
|
+
export const RATE_LIMIT_DEFAULT_WAIT_MS: 5000;
|
package/types/run.d.ts
CHANGED
|
@@ -167,6 +167,14 @@
|
|
|
167
167
|
* 'close-red' | 'close-unsupported' | 'recipe-stale' | 'branch-red' | 'pricing-red' | 'provider-red' |
|
|
168
168
|
* 'interpreter-red' | 'cap-halt' | 'wall-halt' | 'step-stalled' |
|
|
169
169
|
* 'hitl-pause' | 'hitl-decision-red' | `step-red:<id>`
|
|
170
|
+
* NOTE: `job-end` can ALSO land with outcome 'runner-drained' (F140/PRD item
|
|
171
|
+
* 28(c)) — the `beforeExit` backstop's own terminal, emitted from OUTSIDE this
|
|
172
|
+
* function's normal control flow when the process drains with no `job-end` at
|
|
173
|
+
* all (a provider promise that never settled). It never appears as this
|
|
174
|
+
* function's own RETURN value (nothing is left to receive it), only as the
|
|
175
|
+
* spine's terminal record. Non-zero exit, `spendComplete:false` always, and
|
|
176
|
+
* deliberately NOT in the resumable/checkpoint set (`src/reuse.js`) — an
|
|
177
|
+
* unknown in-flight state is not a known-safe resume point.
|
|
170
178
|
*/
|
|
171
179
|
export function runJob(rawSpec: object | string, { approvals, workdir, provider, nativeProvider, providerFor, judgeProvider, emit, capRuns, strikeLimit, shellCapUsd, closeTimeoutMs, closeDir, layerRoot, readShim, scout, bridge, priorSpentUsd, priorSpendComplete, priorWallMs, resumeSeed, resumeGrades, resumeReplans, resumeBranch, humanRuling, heldRuling, reviewDoor, doorRerun, resumable }: {
|
|
172
180
|
approvals: unknown;
|
package/types/transport.d.ts
CHANGED
|
@@ -2,11 +2,17 @@
|
|
|
2
2
|
* Is `err` a transport-class failure — the request never produced an HTTP
|
|
3
3
|
* response? True for: a TLS fault mid-record ("bad record mac" and kin), a
|
|
4
4
|
* reset/broken/timed-out socket (ECONNRESET, EPIPE, ETIMEDOUT, ECONNREFUSED,
|
|
5
|
-
* ENETUNREACH — by code on the error itself or on `err.cause`),
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* ENETUNREACH — by code on the error itself or on `err.cause`), `fetch
|
|
6
|
+
* failed` wrapping a network `cause`, and — as of bare-agent 0.42.0 (BA-25)
|
|
7
|
+
* — a `ProviderError` whose `err.context.bound === 'transport'`: the
|
|
8
|
+
* response stream ended (`'aborted'`/`'error'`/premature `'close'`) after
|
|
9
|
+
* headers but before the body completed, which bare-agent itself rejects as
|
|
10
|
+
* retryable with no HTTP status. False for anything carrying an HTTP status
|
|
11
|
+
* (`err.status`/`err.statusCode`, 4xx/5xx/429) or an explicit
|
|
8
12
|
* `err.retryable === false` — those are provider RESPONSES, not transport
|
|
9
|
-
* casualties, and bare-agent's own policy governs them unchanged
|
|
13
|
+
* casualties, and bare-agent's own policy governs them unchanged; both
|
|
14
|
+
* guards run BEFORE the `context.bound` check, so a status or an explicit
|
|
15
|
+
* `retryable:false` wins even if `bound` says 'transport'.
|
|
10
16
|
* @param {any} err
|
|
11
17
|
* @returns {boolean}
|
|
12
18
|
*/
|