bareloop 0.40.0 → 0.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/bareloop.context.md +97 -124
  3. package/package.json +2 -2
  4. package/src/authorflow.js +9 -9
  5. package/src/authorjob.js +4 -4
  6. package/src/authorrun.js +25 -25
  7. package/src/authorscout.js +4 -4
  8. package/src/bundle.js +7 -4
  9. package/src/bundlerun.js +4 -1
  10. package/src/cli.js +1 -1
  11. package/src/config.js +129 -29
  12. package/src/draftspend.js +4 -4
  13. package/src/index.js +2 -4
  14. package/src/interviewrun.js +17 -25
  15. package/src/job.js +13 -7
  16. package/src/judged.js +11 -43
  17. package/src/keysfile.js +15 -7
  18. package/src/kinds.js +2 -2
  19. package/src/modellist.js +84 -0
  20. package/src/monthly.js +23 -43
  21. package/src/panel/authorroutes.js +19 -14
  22. package/src/panel/authorsession.js +62 -58
  23. package/src/panel/index.html +520 -145
  24. package/src/panel/runroutes.js +3 -0
  25. package/src/panel/server.js +35 -15
  26. package/src/panel/settingsroutes.js +232 -173
  27. package/src/planrun.js +2 -2
  28. package/src/providerrows.js +226 -141
  29. package/src/providers.js +58 -332
  30. package/src/providershapes.js +18 -0
  31. package/src/replay.js +1 -1
  32. package/src/thinking.js +130 -0
  33. package/src/userrun.js +50 -73
  34. package/types/authorflow.d.ts +5 -5
  35. package/types/authorjob.d.ts +2 -2
  36. package/types/authorscout.d.ts +2 -2
  37. package/types/config.d.ts +23 -6
  38. package/types/draftspend.d.ts +8 -8
  39. package/types/index.d.ts +1 -1
  40. package/types/job.d.ts +5 -5
  41. package/types/judged.d.ts +5 -36
  42. package/types/keysfile.d.ts +15 -4
  43. package/types/modellist.d.ts +50 -0
  44. package/types/monthly.d.ts +4 -3
  45. package/types/panel/authorsession.d.ts +2 -9
  46. package/types/panel/server.d.ts +14 -4
  47. package/types/providerrows.d.ts +192 -99
  48. package/types/providers.d.ts +34 -159
  49. package/types/providershapes.d.ts +10 -0
  50. package/types/thinking.d.ts +90 -0
  51. package/types/userrun.d.ts +6 -1
@@ -1,47 +1,3 @@
1
- /**
2
- * The per-call options the rubric JUDGE's model calls carry for this provider instance: `{ thinkingMode: 'off' }` when
3
- * bare-agent measures the model switchable, else `{}` (F192: judge runs thinking off; a judge on a model that cannot turn it
4
- * off must not throw).
5
- * @param {any} provider a constructed bare-agent provider
6
- * @returns {Record<string, any>}
7
- */
8
- export function judgeCallOptions(provider: any): Record<string, any>;
9
- /**
10
- * Throw unless `mode` is one of {@link DRAFT_THINKING_MODES} — an operator-param guard, so a typo never silently means a mode.
11
- * @param {unknown} mode
12
- * @returns {'on'|'off'}
13
- */
14
- export function assertDraftThinking(mode: unknown): "on" | "off";
15
- /**
16
- * What the drafting calls on this provider ACTUALLY carry for thinking, for the draft log and the panel row: `'on'` (mode on,
17
- * nothing sent), `'off'` (mode off and the switch is sent), `'not-switchable'` (mode off, model measured not switchable —
18
- * nothing sent), `'unverified'` (mode off, model not measured by bare-agent — nothing sent). Never re-derives a model list.
19
- * @param {any} provider a constructed bare-agent provider
20
- * @param {unknown} [mode] 'on' | 'off' (default 'off')
21
- * @returns {'on'|'off'|'not-switchable'|'unverified'}
22
- */
23
- export function draftThinkingSent(provider: any, mode?: unknown): "on" | "off" | "not-switchable" | "unverified";
24
- /**
25
- * {@link draftThinkingSent} for a model the caller has only NAMED (provider shape, model id, baseUrl) — no key, no network. For
26
- * the panel's first drafting row, which is shown before the key is read. Constructing the provider only makes the class and
27
- * the model id available to `thinkingSupport`; it sends nothing.
28
- * @param {{providerName: string, model: string, baseUrl?: string|null}} o
29
- * @param {unknown} [mode] 'on' | 'off' (default 'off')
30
- * @returns {'on'|'off'|'not-switchable'|'unverified'}
31
- */
32
- export function draftThinkingFor({ providerName, model, baseUrl }: {
33
- providerName: string;
34
- model: string;
35
- baseUrl?: string | null;
36
- }, mode?: unknown): "on" | "off" | "not-switchable" | "unverified";
37
- /**
38
- * The ONE owner of the per-call options every DRAFTING model call carries (scout, confirm, author, revise, structure retries).
39
- * `{ thinkingMode: 'off' }` exactly when {@link draftThinkingSent} says `'off'`, else `{}`.
40
- * @param {any} provider a constructed bare-agent provider
41
- * @param {unknown} [mode] 'on' | 'off' (default 'off')
42
- * @returns {Record<string, any>}
43
- */
44
- export function draftCallOptions(provider: any, mode?: unknown): Record<string, any>;
45
1
  /**
46
2
  * Look up a provider's table entry. Throws a NAMED error on an unrecognized
47
3
  * provider — never a silent default (the `resolveRates`-style-substring-
@@ -81,80 +37,67 @@ export function resolveProvider(providerName: string): ProviderTableEntry;
81
37
  */
82
38
  export function apiKeyProblem(value: string): string | null;
83
39
  /**
84
- * Construct ONE provider instance for `providerName`/`model`, applying
85
- * `exposeErrorBody: true` (F153 — every constructed provider, unconditionally)
86
- * and the model's own request-key gating (`paramsFor`). `baseUrl` (PRD item
87
- * 28, ruling (d), 2026-09-09: admitted in v1) is forwarded only when given —
88
- * every provider constructor here defaults it on its own when absent.
40
+ * Construct ONE provider instance for `providerName`/`model`, applying `exposeErrorBody: true` (F153 — every constructed
41
+ * provider, unconditionally). `baseUrl` (PRD item 28, ruling (d), 2026-09-09: admitted in v1) is forwarded only when given.
42
+ * `legacyMaxTokens` is the Settings ROW's "old max_tokens" tick: a server that ignores `max_completion_tokens` (F149) does not
43
+ * bind the output cap until it is on. It is forwarded only for `openai-api` and only when `true` — nothing here sniffs a host
44
+ * or a model id to decide it.
89
45
  * @param {string} providerName
90
- * @param {{apiKey?: string, model?: string, baseUrl?: string}} [options]
46
+ * @param {{apiKey?: string, model?: string, baseUrl?: string, legacyMaxTokens?: boolean}} [options]
91
47
  */
92
- export function makeProvider(providerName: string, { apiKey, model, baseUrl }?: {
48
+ export function makeProvider(providerName: string, { apiKey, model, baseUrl, legacyMaxTokens }?: {
93
49
  apiKey?: string;
94
50
  model?: string;
95
51
  baseUrl?: string;
52
+ legacyMaxTokens?: boolean;
96
53
  }): any;
97
54
  /**
98
- * Build the `{provider, providerFor, judgeProvider}` triple both runners
99
- * need (`src/authorrun.js` and `src/panel/authorsession.js` call it) — ONE seam instead of two copies of the same construction and
100
- * tier-memoization logic. `model`/`tierModels` are already-RESOLVED inputs
101
- * (the caller runs `resolveWorkerModel` itself, exactly as both runners did
102
- * before this factory existed — that decision stays where each runner's own
103
- * `--model` flag/spec precedence lives, not duplicated in here).
55
+ * Build the `{provider, providerFor, judgeProvider}` triple both authoring runners need (`src/authorrun.js` and
56
+ * `src/panel/authorsession.js` call it) — ONE seam instead of two copies of the same construction. `model` is the
57
+ * already-RESOLVED worker model id (the caller picked the Settings line).
104
58
  *
105
- * THE JUDGE IS NO LONGER PINNED TO ANTHROPIC (PRD item 32.2, hamr 2026-09-10:
106
- * *"what i care about is that judge becomes llm agnostic and not set to one
107
- * model or provider"*). It is constructed through this same factory, from a
108
- * `judgeProviderName`/`judgeModel` pair the CALLER resolved (`resolveJudge`,
109
- * src/judged.js — the spec's signed `judge` override, else the job's own worker
110
- * provider and model). Item 28's pin, and item 28 part (2)'s deferral of a
111
- * signed `judge:{provider,model}` field, both close here.
112
- *
113
- * `judgeApiKey` stays a SEPARATE argument rather than being derived: the judge
114
- * may legitimately be a different provider from the worker, and reading its key
115
- * for it would put a second env-var reader in a module whose whole job is to
116
- * have exactly one. The caller reads the resolved judge provider's own `envKey`
117
- * and hands the key in — the same contract the worker key already has here.
59
+ * THE JUDGE IS NOT PINNED (PRD item 32.2): it is constructed through this same factory from a `judgeProviderName`/`judgeModel`
60
+ * pair the CALLER resolved. `judgeApiKey` stays a SEPARATE argument: the judge may be a different provider from the worker,
61
+ * and each key is read by the caller from its own row.
118
62
  * @param {object} args
119
- * @param {string} args.providerName spec.provider (or the anthropic-api default)
120
- * @param {string|undefined} args.apiKey the WORKER's key, read from the provider's own `envKey`
63
+ * @param {string} args.providerName the worker's API shape id
64
+ * @param {string|undefined} args.apiKey the WORKER's key, read from its row's env name
121
65
  * @param {string} args.model the already-resolved worker model id
122
- * @param {Readonly<Record<string, string>>} args.tierModels the already-resolved tier -> model map (spec override folded in by the caller)
123
- * @param {string|undefined} [args.baseUrl] spec.baseUrl, if any
124
- * @param {string|undefined} args.judgeApiKey the judge's own key, read by the caller from the RESOLVED judge provider's `envKey`
66
+ * @param {string|undefined} [args.baseUrl] the worker row's Base URL
67
+ * @param {boolean} [args.legacyMaxTokens] the worker row's old max_tokens tick
68
+ * @param {string|undefined} args.judgeApiKey the judge's own key
125
69
  * @param {string} args.judgeModel the resolved judge model id
126
- * @param {string} [args.judgeProviderName] the resolved judge provider name; defaults to `providerName` (the job's own worker provider), which is `resolveJudge`'s own default
127
- * @param {string|undefined} [args.judgeBaseUrl] the judge provider's endpoint, when it takes one
70
+ * @param {string} [args.judgeProviderName] the resolved judge shape; defaults to `providerName`
71
+ * @param {string|undefined} [args.judgeBaseUrl] the judge row's endpoint
72
+ * @param {boolean} [args.judgeLegacyMaxTokens] the judge row's old max_tokens tick (defaults to the worker's when the judge shares its provider)
128
73
  */
129
- export function buildRunnerProviders({ providerName, apiKey, model, tierModels, baseUrl, judgeApiKey, judgeModel, judgeProviderName, judgeBaseUrl, }: {
74
+ export function buildRunnerProviders({ providerName, apiKey, model, baseUrl, legacyMaxTokens, judgeApiKey, judgeModel, judgeProviderName, judgeBaseUrl, judgeLegacyMaxTokens, }: {
130
75
  providerName: string;
131
76
  apiKey: string | undefined;
132
77
  model: string;
133
- tierModels: Readonly<Record<string, string>>;
134
78
  baseUrl?: string | undefined;
79
+ legacyMaxTokens?: boolean | undefined;
135
80
  judgeApiKey: string | undefined;
136
81
  judgeModel: string;
137
82
  judgeProviderName?: string | undefined;
138
83
  judgeBaseUrl?: string | undefined;
84
+ judgeLegacyMaxTokens?: boolean | undefined;
139
85
  }): {
140
86
  provider: any;
141
- providerFor: (tier: string) => any;
87
+ providerFor: (_tier: string) => any;
142
88
  judgeProvider: any;
143
89
  };
144
90
  /**
145
- * $0 reachability check: a GET against `providerName`'s own models-list
146
- * endpoint, server-side, with the key in a header — NEVER a completion
147
- * request, so it never spends a token (PANEL-BUILD.md P3 build item 7: "the
148
- * person must get feedback BEFORE drafting if the chosen model can't
149
- * work"). Reused, unmodified, by the panel's model-readiness route today
150
- * (`src/panel/authorroutes.js`) and by the future Settings providers
151
- * table's own "test" column (PANEL-BUILD.md P4) — ONE function, not two
152
- * hand-typed copies of the same probe.
91
+ * $0 reachability check: ONE GET of the provider's models list (`src/modellist.js`, the one owner of
92
+ * that call), server-side, with the key in a header — NEVER a completion request, so it never spends
93
+ * a token (PANEL-BUILD.md P3 build item 7: "the person must get feedback BEFORE drafting if the
94
+ * chosen model can't work"). Used by the panel's model-readiness route (`src/panel/authorroutes.js`);
95
+ * Settings > Providers' Test and Refresh call `listModels` directly. A blank `baseUrl` makes no call.
153
96
  *
154
- * NEVER returns the key, never logs it — the key only ever appears in a
155
- * request header sent straight to the provider's own host.
97
+ * NEVER returns the key, never logs it — the key only ever appears in a request header sent
98
+ * straight to the provider's own host.
156
99
  * @param {object} o
157
- * @param {string} o.providerName
100
+ * @param {string} o.providerName an API shape id
158
101
  * @param {string} o.apiKey
159
102
  * @param {string} [o.model] when given, the response is also checked for this model id
160
103
  * @param {string} [o.baseUrl]
@@ -194,55 +137,6 @@ export function checkProviderReachable({ providerName, apiKey, model, baseUrl, f
194
137
  * @returns {string[]|null} lines to print, in order, or null when there is nothing to say
195
138
  */
196
139
  export function probeWarningLines(providerName: string): string[] | null;
197
- /**
198
- * Today's anthropic-api tier table — EXACTLY the `DEFAULT_TIER_MODELS` both
199
- * runners hardcoded before this factory existed. Any change to these two
200
- * values is a change to shipped anthropic-api behaviour, not a refactor.
201
- */
202
- export const ANTHROPIC_TIER_MODELS: Readonly<{
203
- sonnet: "claude-sonnet-5";
204
- haiku: "claude-haiku-4-5-20251001";
205
- }>;
206
- /**
207
- * DeepSeek (F149/F150) is a SINGLE model, not a tiered family — hamr's
208
- * ruling above: one secondary provider, not a menu. Both tiers map to the
209
- * same model id ON PURPOSE: `resolveWorkerModel`/a runner's `providerFor
210
- * (tier)` only ever LOOK UP a tier's model id (src/job.js:818-827,
211
- * scripts/run-u.mjs's `providerFor`) — neither assumes the tiers name
212
- * different models — so repeating the one id is the honest representation
213
- * of a single-model provider, not a second model masquerading as `haiku`.
214
- * If DeepSeek (or a future openai-api entrant) ever ships a genuinely
215
- * cheaper tier model, this is where it would earn its own `haiku` id.
216
- *
217
- * MODEL SWAP (F171, item 34 L16, hamr 2026-09-13): `deepseek-chat` is no
218
- * longer served — a live `/models` call returned only `deepseek-flash` and
219
- * `deepseek-v4-pro`. Switched to `deepseek-flash` (DeepSeek V4.1). F149's
220
- * `legacyMaxTokens` measurement (asked 64 output tokens, got 665/608) was
221
- * taken on `deepseek-chat` and is UNVERIFIED on `deepseek-flash` — kept on
222
- * below as the fail-safe (over-capping direction is safe; see F171).
223
- */
224
- export const OPENAI_TIER_MODELS: Readonly<{
225
- sonnet: "deepseek-flash";
226
- haiku: "deepseek-flash";
227
- }>;
228
- /**
229
- * Gemini (PRD item 31.3, hamr 2026-09-09: "anthropic, openai, gemini drop
230
- * ollama for now"). Two real tiers, unlike DeepSeek's single model: `flash` is
231
- * the cheap tier by design, so `haiku` maps to it honestly rather than
232
- * repeating one id.
233
- *
234
- * ADMITTED-PENDING-PROBE. The probe rule stands (PRD item 28 ruling (d),
235
- * 2026-09-09): no endpoint is in the menu without its own clean paid probe,
236
- * and `gemini-api` has ZERO runs. `anthropic-api` is the original (~167
237
- * archived runs) and `openai-api` was probed green through the shipped runner
238
- * (run mtu12vks, $1.64/$4, F157); gemini owes that fire and has not paid it.
239
- * `PROBE_STATUS` below is the machine-readable form of that debt — nothing in
240
- * here silently pretends the provider is proven.
241
- */
242
- export const GEMINI_TIER_MODELS: Readonly<{
243
- sonnet: "gemini-2.5-pro";
244
- haiku: "gemini-2.5-flash";
245
- }>;
246
140
  /**
247
141
  * Which admitted providers have paid for their own end-to-end probe, and which
248
142
  * have not. Read by the runner to print a loud marker; never read to REFUSE —
@@ -254,32 +148,13 @@ export const PROBE_STATUS: Readonly<Record<string, {
254
148
  probed: boolean;
255
149
  evidence: string;
256
150
  }>>;
257
- /**
258
- * The drafter's THINKING knob — an enumerated OPERATOR param, never model output and never read from env inside the library.
259
- * `'on'` sends nothing (the model's own thinking, byte-identical to a no-knob call); `'off'` sends `thinkingMode: 'off'` where
260
- * bare-agent measures the model switchable. The DEFAULT is `'off'` (hamr 2026-10-11, decided by the frozen ON/OFF contrast,
261
- * docs/logs/2026-10-10-drafter-thinking-contrast-prereg.md); `'on'` stays selectable for a later Settings checkbox.
262
- */
263
- export const DRAFT_THINKING_MODES: readonly string[];
264
151
  export type ProviderTableEntry = {
265
152
  /**
266
153
  * the bare-agent provider class
267
154
  */
268
155
  ctor: new (options: any) => any;
269
- /**
270
- * the environment variable this provider reads its key from
271
- */
272
- envKey: string;
273
- /**
274
- * tier name -> model id
275
- */
276
- tiers: Readonly<Record<string, string>>;
277
156
  /**
278
157
  * the CONSTRUCTOR OPTION NAME this provider reads its endpoint from — NOT assumed to be `baseUrl` (see below)
279
158
  */
280
159
  endpointKey: string;
281
- /**
282
- * per-model request-key/param gating, applied on TOP of {exposeErrorBody:true, apiKey, model, <endpointKey>?}
283
- */
284
- paramsFor: (model: string | undefined) => Record<string, any>;
285
160
  };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * `id` is the value saved in config.json (and the signed spec's `provider`).
3
+ * @type {readonly {id: string, label: string}[]}
4
+ */
5
+ export const SHAPES: readonly {
6
+ id: string;
7
+ label: string;
8
+ }[];
9
+ /** @type {readonly string[]} */
10
+ export const SHAPE_IDS: readonly string[];
@@ -0,0 +1,90 @@
1
+ /**
2
+ * What bare-agent knows about switching this model's thinking. A missing kind, a non-function `impl`, or a throw all read
3
+ * `'unverified'` — never a guess in either direction.
4
+ * @param {string} shape an API shape id (src/providershapes.js)
5
+ * @param {string} model
6
+ * @param {{ baseUrl?: string|null, impl?: Function }} [o] `impl` is a test seam; production uses bare-agent's own
7
+ * @returns {true|false|'unverified'}
8
+ */
9
+ export function thinkingSupport(shape: string, model: string, { baseUrl, impl }?: {
10
+ baseUrl?: string | null;
11
+ impl?: Function;
12
+ }): true | false | "unverified";
13
+ /**
14
+ * The per-call options for one support answer and one tick (`true` = "Thinking on"). The table (PANEL-BUILD.md P8, ruling Q1):
15
+ * tick on : true -> on | false -> {} (the box is greyed, cannot be on) | 'unverified' -> on
16
+ * tick off: true -> off | false -> {} (the model thinks as it would) | 'unverified' -> off (bare-agent decides; plain request on failure)
17
+ * @param {true|false|'unverified'} support
18
+ * @param {boolean} tick
19
+ * @returns {{ thinkingMode: 'on'|'off' } | {}}
20
+ */
21
+ export function thinkingOptions(support: true | false | "unverified", tick: boolean): {
22
+ thinkingMode: "on" | "off";
23
+ } | {};
24
+ /**
25
+ * Throw unless `mode` is one of {@link THINKING_MODES} — an operator-param guard, so a typo never silently means a mode.
26
+ * @param {unknown} mode
27
+ * @returns {'on'|'off'}
28
+ */
29
+ export function assertThinkingMode(mode: unknown): "on" | "off";
30
+ /**
31
+ * The API shape of a constructed bare-agent provider (the three client classes only); `null` for a test double or a clipipe.
32
+ * @param {any} provider
33
+ * @returns {string|null}
34
+ */
35
+ export function shapeOfProvider(provider: any): string | null;
36
+ /**
37
+ * The per-call options every DRAFTING and JUDGING model call on this provider instance carries for this mode. A provider that is
38
+ * not one of the three client classes (a test double, a clipipe) is never sent the switch.
39
+ * @param {any} provider a constructed bare-agent provider
40
+ * @param {unknown} [mode] 'on' | 'off' (default 'off')
41
+ * @returns {{ thinkingMode: 'on'|'off' } | {}}
42
+ */
43
+ export function thinkingCallOptions(provider: any, mode?: unknown): {
44
+ thinkingMode: "on" | "off";
45
+ } | {};
46
+ /**
47
+ * What the drafting calls will carry for a model the caller has only NAMED (shape, model id, Base URL) — no key, no network. The
48
+ * first drafting row is shown from this, before the key is read.
49
+ * @param {{providerName: string, model: string, baseUrl?: string|null}} o
50
+ * @param {unknown} [mode] 'on' | 'off'
51
+ * @returns {{ support: true|false|'unverified', mode: 'on'|'off', options: {thinkingMode?: 'on'|'off'} }}
52
+ */
53
+ export function thinkingPlanFor({ providerName, model, baseUrl }: {
54
+ providerName: string;
55
+ model: string;
56
+ baseUrl?: string | null;
57
+ }, mode?: unknown): {
58
+ support: true | false | "unverified";
59
+ mode: "on" | "off";
60
+ options: {
61
+ thinkingMode?: "on" | "off";
62
+ };
63
+ };
64
+ /**
65
+ * The server's answer for one Model-menu line: whether the "Thinking on" box can change and the words under it. `false` greys and
66
+ * unticks the box (the note reads {@link THINKING_FIXED_SAY}); `'unverified'` leaves it enabled with no note.
67
+ * @param {string} shape @param {string} model @param {string|null|undefined} baseUrl
68
+ * @returns {{ support: true|false|'unverified', canChange: boolean, say: string }}
69
+ */
70
+ export function thinkingForLine(shape: string, model: string, baseUrl: string | null | undefined): {
71
+ support: true | false | "unverified";
72
+ canChange: boolean;
73
+ say: string;
74
+ };
75
+ /**
76
+ * The first drafting row's words — code-owned, ALWAYS starting "thinking on" or "thinking off". `status` is bare-agent's own
77
+ * `thinkingStatus` for a call that has run ('applied' | 'unsupported' | 'ignored' | 'unknown'); undefined = before any call, read from
78
+ * the plan (`support`, `mode`).
79
+ * @param {{ mode: 'on'|'off', support?: true|false|'unverified', status?: string }} s
80
+ * @returns {string}
81
+ */
82
+ export function thinkingRowText({ mode, support, status }: {
83
+ mode: "on" | "off";
84
+ support?: true | false | "unverified";
85
+ status?: string;
86
+ }): string;
87
+ /** The words under a greyed "Thinking on" box (the server's `thinking.canChange` decides; the page decides nothing). */
88
+ export const THINKING_FIXED_SAY: "can't change this for this model";
89
+ /** The enumerated operator knob a drafting call is built with: 'on' | 'off' (the tick, spelled as a word). */
90
+ export const THINKING_MODES: readonly string[];
@@ -136,9 +136,13 @@ export type RunOpts = {
136
136
  */
137
137
  into?: string | undefined;
138
138
  /**
139
- * the raw `--model` tier name (`sonnet`/`haiku`), or null for the default
139
+ * the raw `--model <id>` (a model id of a ticked Settings line), or null
140
140
  */
141
141
  model?: string | null | undefined;
142
+ /**
143
+ * the raw `--provider <KEY>` (a key's env name), or null
144
+ */
145
+ providerKey?: string | null | undefined;
142
146
  /**
143
147
  * the raw `--read-shim` arm name, or null for the default
144
148
  */
@@ -205,6 +209,7 @@ export type RunCtx = {
205
209
  */
206
210
  into?: string | undefined;
207
211
  model: string | null;
212
+ providerKey: string | null;
208
213
  readShim: string | null;
209
214
  scout: string | null;
210
215
  draftSpentUsd: string | null;