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.
@@ -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;
@@ -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`), and
6
- * `fetch failed` wrapping a network `cause`. False for anything carrying an
7
- * HTTP status (`err.status`/`err.statusCode`, 4xx/5xx/429) or an explicit
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
  */