pullfrog 0.1.76 → 0.1.78

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.
@@ -76,23 +76,57 @@ export declare function providerGatewayOverride(model: string | undefined): Reco
76
76
  * openai-compatible's do; `validateAzureSetup` guarantees they parse.
77
77
  */
78
78
  export declare function azureProvider(model: string | undefined): Record<string, AzureProviderEntry>;
79
+ export type OpenRouterDataCollection = "allow" | "deny";
79
80
  /**
80
- * Build the `provider.openrouter.models[id].options` map that pins every Kimi
81
- * K2 OpenRouter alias away from the Enforcer-less providers via OpenRouter's
82
- * `provider: {ignore:[...]}` routing directive. Sourced from the model registry
83
- * so a Kimi alias/route change in `action/models.ts` flows through automatically.
81
+ * OpenRouter's request-level `provider.data_collection` decides whether hosts
82
+ * that retain or train on prompts may serve a run. The account-wide privacy
83
+ * setting is only a ceiling a request can tighten, so the policy rides on the
84
+ * run instead: OSS-funded runs `allow` (public code on headless runs — and the
85
+ * cheapest V4.1 Flash host, DeepSeek's own, is a training host), Router runs
86
+ * `deny` (a customer's private code never reaches one; every catalog target
87
+ * was still served under `deny` on 2026-09-11), and a BYOK OpenRouter key gets
88
+ * no preference — the customer's own account decides. see wiki/muse-spark.md.
89
+ */
90
+ export declare function openRouterDataCollection(ctx: {
91
+ payload: {
92
+ proxyModel?: string | undefined;
93
+ };
94
+ toolState: {
95
+ oss?: boolean | undefined;
96
+ };
97
+ }): OpenRouterDataCollection | undefined;
98
+ /**
99
+ * The `provider.openrouter` config block: the run's data policy as `extraBody`
100
+ * (the SDK merges it into every request), plus the per-model `options` that
101
+ * carry OpenRouter's `provider` routing object where a model needs its own —
102
+ * Kimi K2 pinned away from the Enforcer-less providers, and the Muse Spark
103
+ * contributor tier opened to data collection whatever the run's policy, since
104
+ * Meta training on the traffic is what that tier is. Sourced from the model
105
+ * registry so an alias/route change in `action/models.ts` flows through.
84
106
  *
85
107
  * Wiring: the per-model `options` object merges into the request options
86
108
  * (opencode `session/llm/request.ts` `mergeOptions(base, model.options)`),
87
109
  * gets wrapped under `providerOptions.openrouter` (`provider/transform.ts`
88
110
  * `sdkKey("@openrouter/ai-sdk-provider") === "openrouter"`), and the OpenRouter
89
- * AI-SDK provider spreads everything under `providerOptions.openrouter` straight
90
- * into the request body (`@openrouter/ai-sdk-provider` `doStream`/`doGenerate`),
91
- * so `provider` lands as the top-level routing object OpenRouter expects.
111
+ * AI-SDK provider spreads everything under `providerOptions.openrouter` into the
112
+ * request body LAST — after `extraBody` — so a per-model `provider` object
113
+ * replaces the run-level one wholesale. that is why the Kimi entry restates
114
+ * the run's `data_collection` beside its `ignore` list.
92
115
  */
93
- export declare function kimiOpenRouterProviderOverrides(): Record<string, {
94
- options: object;
95
- }>;
116
+ export declare function openRouterProvider(policy: OpenRouterDataCollection | undefined): {
117
+ options?: {
118
+ extraBody: {
119
+ provider: {
120
+ data_collection: OpenRouterDataCollection;
121
+ };
122
+ };
123
+ };
124
+ models: Record<string, {
125
+ options: {
126
+ provider: object;
127
+ };
128
+ }>;
129
+ };
96
130
  /**
97
131
  * Read-only `reviewfrog` subagent for lens-based review. Non-mutative +
98
132
  * non-recursive — enforced by the system prompt in reviewer.ts.
package/dist/cli.mjs CHANGED
@@ -123000,7 +123000,7 @@ var import_picocolors = __toESM(require_picocolors(), 1);
123000
123000
  // package.json
123001
123001
  var package_default = {
123002
123002
  name: "pullfrog",
123003
- version: "0.1.76",
123003
+ version: "0.1.78",
123004
123004
  cliContractVersion: "1",
123005
123005
  type: "module",
123006
123006
  bin: {
@@ -125122,26 +125122,34 @@ var providers = {
125122
125122
  models: {
125123
125123
  grok: {
125124
125124
  displayName: "Grok",
125125
- resolve: "xai/grok-4.5",
125126
- effort: ["low", "medium", "high"],
125127
- openRouterResolve: "openrouter/x-ai/grok-4.5",
125125
+ resolve: "xai/grok-4.6",
125126
+ effort: ["low", "medium", "high", "xhigh"],
125127
+ openRouterResolve: "openrouter/x-ai/grok-4.6",
125128
125128
  preferred: true
125129
125129
  },
125130
+ // the Smart ladder's cheap rung: 1.25/2.5 against 4.6's 2/6, and a 0.2
125131
+ // cache read against 0.5 — the only live xAI model a rung can save on.
125132
+ "grok-4.3": {
125133
+ displayName: "Grok 4.3",
125134
+ resolve: "xai/grok-4.3",
125135
+ effort: ["none", "low", "medium", "high"],
125136
+ openRouterResolve: "openrouter/x-ai/grok-4.3"
125137
+ },
125130
125138
  // legacy aliases — xAI retired the entire fast/code-fast line on
125131
125139
  // 2026-05-15 (https://docs.x.ai/developers/migration/may-15-deprecation)
125132
125140
  // and now redirects every deprecated text-model slug to grok-4.3 at
125133
125141
  // standard pricing. fall back to the live `xai/grok` so the alias
125134
- // chain resolves to grok-4.5 for both direct-key and OpenRouter users.
125142
+ // chain resolves to grok-4.6 for both direct-key and OpenRouter users.
125135
125143
  "grok-fast": {
125136
125144
  displayName: "Grok Fast",
125137
125145
  resolve: "xai/grok-4-1-fast",
125138
- openRouterResolve: "openrouter/x-ai/grok-4.5",
125146
+ openRouterResolve: "openrouter/x-ai/grok-4.6",
125139
125147
  fallback: "xai/grok"
125140
125148
  },
125141
125149
  "grok-code-fast": {
125142
125150
  displayName: "Grok Code Fast",
125143
125151
  resolve: "xai/grok-code-fast-1",
125144
- openRouterResolve: "openrouter/x-ai/grok-4.5",
125152
+ openRouterResolve: "openrouter/x-ai/grok-4.6",
125145
125153
  fallback: "xai/grok"
125146
125154
  }
125147
125155
  }
@@ -125160,6 +125168,12 @@ var providers = {
125160
125168
  // $0.0405. there is no `~deepseek/*-pro-latest` pointer to track, so this
125161
125169
  // one has to be a dated pin; the `models-catalog` drift test is what
125162
125170
  // catches the next release.
125171
+ // from 2026-09-14 DeepSeek serves every direct `deepseek-v4-pro` call with
125172
+ // V4.1-Flash at Flash rates until V4.1-Pro ships. it is still the only Pro
125173
+ // id the vendor lists, and the OpenRouter 0813 pin is third-party-hosted
125174
+ // and unaffected — so no `fallback` to Flash, which would drag the Router
125175
+ // route down with it. the OSS program dropped Pro from its menu instead;
125176
+ // see `OSS_MODEL_ALLOWLIST`.
125163
125177
  "deepseek-pro": {
125164
125178
  displayName: "DeepSeek Pro",
125165
125179
  resolve: "deepseek/deepseek-v4-pro",
@@ -125168,30 +125182,24 @@ var providers = {
125168
125182
  openRouterResolve: "openrouter/deepseek/deepseek-v4-pro-0813",
125169
125183
  preferred: true
125170
125184
  },
125171
- // DeepSeek upgrades the `deepseek-v4-flash` API model in place, so the
125172
- // direct `resolve` needs no version. OpenRouter instead forked the 0731
125173
- // release into its own id and left the April preview live under the
125174
- // unversioned one at a HIGHER price ($0.14/$0.28 vs $0.09/$0.18) — so the
125175
- // OpenRouter route can never use the bare id. it tracks the rolling
125176
- // `~deepseek/*-latest` pointer (rule 4), which follows the next in-place
125177
- // upgrade on its own rather than pinning a dated snapshot that goes stale
125178
- // every release. that pointer is NOT priced like 0731 — its listed rates
125179
- // run +40% on output and +57.5% on cache read, and it costs 12% more per
125180
- // run on authoritative spend ($0.0405 vs $0.0361). kept anyway: ~$45/yr
125181
- // buys auto-following the upgrade, and the listed gap overstates the real
125182
- // one because a rolling alias prices at its routing ceiling.
125183
- // 0731 gained a `low` rung and renamed the top to `max`, and that ladder
125184
- // now reads the same on BOTH routes — the in-place direct upgrade caught
125185
- // up to the fork. so `low`, not `high`, is position 0 either way, which is
125186
- // why the OSS effort floor in `main.ts` pins the `high` rung by name. Pro
125187
- // is the same shape via OpenRouter since 0813; only its direct route still
125188
- // starts at `high`.
125185
+ // V4.1-Flash (2026-09-10) retired `deepseek-v4-flash`; DeepSeek's /models
125186
+ // now lists the unversioned `deepseek-flash`, so the direct resolve is the
125187
+ // rolling id at the same price. OpenRouter treats V4.1 as its own id
125188
+ // (`deepseek/deepseek-v4.1-flash`, $0.15/$0.6, cache $0.003) and its
125189
+ // `~deepseek/deepseek-v4-flash-latest` pointer still served 0731 on
125190
+ // 2026-09-11, so this is a pinned id rather than the pointer and the next
125191
+ // Flash release is a catalog edit (the bump cron flags it). this target is
125192
+ // the funded OSS default: priced on the measured OSS token mix at x1.34
125193
+ // the pointer's $0.0531/run on the DeepSeek-hosted endpoint — see
125194
+ // wiki/oss-model-allowlist.md. the bare `deepseek-v4-flash` on OpenRouter
125195
+ // is still the April preview, so that route can never use it. both routes
125196
+ // publish `low, high, max`; the OSS effort floor pins `high` by name.
125189
125197
  "deepseek-flash": {
125190
125198
  displayName: "DeepSeek Flash",
125191
- resolve: "deepseek/deepseek-v4-flash",
125199
+ resolve: "deepseek/deepseek-flash",
125192
125200
  effort: ["low", "high", "max"],
125193
125201
  openRouterEffort: ["low", "high", "max"],
125194
- openRouterResolve: "openrouter/~deepseek/deepseek-v4-flash-latest"
125202
+ openRouterResolve: "openrouter/deepseek/deepseek-v4.1-flash"
125195
125203
  },
125196
125204
  // legacy aliases — deepseek retires these on 2026-07-24; transparently
125197
125205
  // upgrade existing users to the v4 family via the fallback chain.
@@ -125294,6 +125302,40 @@ var providers = {
125294
125302
  }
125295
125303
  }
125296
125304
  }),
125305
+ // Meta Model API — the direct route to Muse Spark. a registry entry, like
125306
+ // kimi-for-coding: models.dev carries `meta` (`https://api.meta.ai/v1`, npm
125307
+ // `@ai-sdk/openai`) and opencode 1.18.29 enables it from a bare
125308
+ // META_MODEL_API_KEY — all five ids list in `opencode models`, and a bogus key
125309
+ // reaches Meta's own auth check (`invalid_api_key`). see wiki/muse-spark.md.
125310
+ meta: provider({
125311
+ displayName: "Meta",
125312
+ envVars: ["META_MODEL_API_KEY"],
125313
+ models: {
125314
+ // the Standard tier. no `subagentModel`: the contributor tier below is
125315
+ // the same model, not a cheaper sibling.
125316
+ "muse-spark": {
125317
+ displayName: "Muse Spark",
125318
+ resolve: "meta/muse-spark-1.3",
125319
+ effort: ["minimal", "low", "medium", "high", "xhigh", "max"],
125320
+ openRouterResolve: "openrouter/meta/muse-spark-1.3",
125321
+ preferred: true
125322
+ },
125323
+ // the Contributor tier: the same model at $0.10 / $0.20 (cache read
125324
+ // $0.002) because Meta trains on the prompts and completions. an opt-in
125325
+ // pick on the OSS menu (public code, headless runs), never the default.
125326
+ // OpenRouter only serves it when the account allows paid-model training,
125327
+ // so the runtime opens data collection for this id alone — see
125328
+ // `openRouterProvider` in opencodeShared.ts.
125329
+ "muse-spark-contributor": {
125330
+ displayName: "Muse Spark Contributor",
125331
+ description: "Meta trains on prompts and completions",
125332
+ resolve: "meta/muse-spark-1.3-contributor",
125333
+ effort: ["minimal", "low", "medium", "high", "xhigh"],
125334
+ openRouterEffort: ["minimal", "low", "medium", "high", "xhigh", "max"],
125335
+ openRouterResolve: "openrouter/meta/muse-spark-1.3-contributor"
125336
+ }
125337
+ }
125338
+ }),
125297
125339
  opencode: provider({
125298
125340
  displayName: "OpenCode Zen",
125299
125341
  envVars: ["OPENCODE_API_KEY"],
@@ -125494,6 +125536,17 @@ var providers = {
125494
125536
  effort: ["minimal", "low", "medium", "high"],
125495
125537
  openRouterResolve: "openrouter/openai/gpt-5-nano"
125496
125538
  },
125539
+ // Zen's PAID Muse Spark, priced like Meta's own Standard tier. not the
125540
+ // `-contributor-free` id beside it, which is the data-collection variant
125541
+ // Go refuses with DataPolicyError. unverified through the harness on
125542
+ // 2026-09-11: the workspace was out of balance and a claude-sonnet-5
125543
+ // control failed identically. see wiki/muse-spark.md.
125544
+ "muse-spark": {
125545
+ displayName: "Muse Spark",
125546
+ resolve: "opencode/muse-spark-1.3",
125547
+ effort: ["minimal", "low", "medium", "high", "xhigh", "max"],
125548
+ openRouterResolve: "openrouter/meta/muse-spark-1.3"
125549
+ },
125497
125550
  // Zen's live free MiMo, and the second free row in a menu that big-pickle
125498
125551
  // was alone in since `mimo-v2-pro-free` lost its model.
125499
125552
  mimo: {
@@ -125873,9 +125926,9 @@ var providers = {
125873
125926
  },
125874
125927
  grok: {
125875
125928
  displayName: "Grok",
125876
- resolve: "openrouter/x-ai/grok-4.5",
125877
- effort: ["low", "medium", "high"],
125878
- openRouterResolve: "openrouter/x-ai/grok-4.5"
125929
+ resolve: "openrouter/x-ai/grok-4.6",
125930
+ effort: ["low", "medium", "high", "xhigh"],
125931
+ openRouterResolve: "openrouter/x-ai/grok-4.6"
125879
125932
  },
125880
125933
  // dated pin, not the bare id — see `deepseek/deepseek-pro` for why
125881
125934
  // OpenRouter's unversioned `deepseek-v4-pro` is the stale April preview.
@@ -125885,13 +125938,14 @@ var providers = {
125885
125938
  effort: ["low", "high", "max"],
125886
125939
  openRouterResolve: "openrouter/deepseek/deepseek-v4-pro-0813"
125887
125940
  },
125888
- // rolling pointer, not the bare id — see `deepseek/deepseek-flash` for why
125889
- // OpenRouter's unversioned `deepseek-v4-flash` is the stale April preview.
125941
+ // the V4.1 id, not the bare one — see `deepseek/deepseek-flash` for why
125942
+ // OpenRouter's unversioned `deepseek-v4-flash` is the stale April preview
125943
+ // and why the `~deepseek/*-latest` pointer is no longer the target.
125890
125944
  "deepseek-flash": {
125891
125945
  displayName: "DeepSeek Flash",
125892
- resolve: "openrouter/~deepseek/deepseek-v4-flash-latest",
125946
+ resolve: "openrouter/deepseek/deepseek-v4.1-flash",
125893
125947
  effort: ["low", "high", "max"],
125894
- openRouterResolve: "openrouter/~deepseek/deepseek-v4-flash-latest"
125948
+ openRouterResolve: "openrouter/deepseek/deepseek-v4.1-flash"
125895
125949
  },
125896
125950
  // legacy alias — deepseek retires this on 2026-07-24; transparently
125897
125951
  // upgrade existing users to the v4 family via the fallback chain.
@@ -125922,6 +125976,23 @@ var providers = {
125922
125976
  displayName: "MiniMax M3",
125923
125977
  resolve: "openrouter/minimax/minimax-m3",
125924
125978
  openRouterResolve: "openrouter/minimax/minimax-m3"
125979
+ },
125980
+ // OpenRouter gates Meta models on an ACCOUNT-level 18+ confirmation
125981
+ // (openrouter.ai/settings/preferences). Pullfrog's account has it, so the
125982
+ // Router path runs; a BYOK OpenRouter key without it refuses with that
125983
+ // link before the request reaches Meta. see wiki/muse-spark.md.
125984
+ "muse-spark": {
125985
+ displayName: "Muse Spark",
125986
+ resolve: "openrouter/meta/muse-spark-1.3",
125987
+ effort: ["minimal", "low", "medium", "high", "xhigh", "max"],
125988
+ openRouterResolve: "openrouter/meta/muse-spark-1.3"
125989
+ },
125990
+ "muse-spark-contributor": {
125991
+ displayName: "Muse Spark Contributor",
125992
+ description: "Meta trains on prompts and completions",
125993
+ resolve: "openrouter/meta/muse-spark-1.3-contributor",
125994
+ effort: ["minimal", "low", "medium", "high", "xhigh", "max"],
125995
+ openRouterResolve: "openrouter/meta/muse-spark-1.3-contributor"
125925
125996
  }
125926
125997
  }
125927
125998
  }),
@@ -125989,10 +126060,13 @@ var providers = {
125989
126060
  resolve: "vercel/deepseek/deepseek-v4-pro-0813",
125990
126061
  effort: ["high", "xhigh"]
125991
126062
  },
126063
+ // the gateway's bare `deepseek-v4-flash` is the April preview — the same
126064
+ // fork trap as OpenRouter, with `deepseek-v4-flash-0731` beside it — so
126065
+ // this entry sat on the preview until V4.1. the gateway publishes no
126066
+ // effort ladder for the V4.1 id.
125992
126067
  "deepseek-flash": {
125993
126068
  displayName: "DeepSeek Flash",
125994
- resolve: "vercel/deepseek/deepseek-v4-flash",
125995
- effort: ["high", "xhigh"]
126069
+ resolve: "vercel/deepseek/deepseek-v4.1-flash"
125996
126070
  },
125997
126071
  glm: {
125998
126072
  displayName: "GLM",
@@ -126003,6 +126077,11 @@ var providers = {
126003
126077
  displayName: "Kimi K3",
126004
126078
  resolve: "vercel/moonshotai/kimi-k3",
126005
126079
  effort: ["low", "high", "max"]
126080
+ },
126081
+ // the gateway publishes no effort ladder for this id.
126082
+ "muse-spark": {
126083
+ displayName: "Muse Spark",
126084
+ resolve: "vercel/meta/muse-spark-1.3"
126006
126085
  }
126007
126086
  }
126008
126087
  })
@@ -126072,6 +126151,71 @@ var AUTO_TIER_TARGET = {
126072
126151
  function isAutoTier(slug2) {
126073
126152
  return slug2 === AUTO_EFFICIENT || slug2 === AUTO_INTELLIGENT;
126074
126153
  }
126154
+ var PROVIDER_LADDERS = {
126155
+ anthropic: {
126156
+ minimal: "anthropic/claude-haiku",
126157
+ light: "anthropic/claude-sonnet",
126158
+ standard: "anthropic/claude-opus",
126159
+ deep: "anthropic/claude-opus"
126160
+ },
126161
+ openai: {
126162
+ minimal: "openai/gpt-luna",
126163
+ light: "openai/gpt-terra",
126164
+ standard: "openai/gpt-sol",
126165
+ deep: "openai/gpt-astra"
126166
+ },
126167
+ google: {
126168
+ minimal: "google/gemini-flash",
126169
+ light: "google/gemini-flash",
126170
+ standard: "google/gemini-pro",
126171
+ deep: "google/gemini-pro"
126172
+ },
126173
+ deepseek: {
126174
+ minimal: "deepseek/deepseek-flash",
126175
+ light: "deepseek/deepseek-flash",
126176
+ standard: "deepseek/deepseek-pro",
126177
+ deep: "deepseek/deepseek-pro"
126178
+ },
126179
+ xai: {
126180
+ minimal: "xai/grok-4.3",
126181
+ light: "xai/grok-4.3",
126182
+ standard: "xai/grok",
126183
+ deep: "xai/grok"
126184
+ },
126185
+ moonshotai: {
126186
+ minimal: "moonshotai/kimi-k2",
126187
+ light: "moonshotai/kimi-k2",
126188
+ standard: "moonshotai/kimi-k3",
126189
+ deep: "moonshotai/kimi-k3"
126190
+ },
126191
+ openrouter: {
126192
+ minimal: "openrouter/deepseek-flash",
126193
+ light: "openrouter/gpt-luna",
126194
+ standard: "openrouter/gpt-sol",
126195
+ deep: "openrouter/claude-opus"
126196
+ },
126197
+ opencode: {
126198
+ minimal: "opencode/gpt-luna",
126199
+ light: "opencode/gpt-luna",
126200
+ standard: "opencode/gpt-sol",
126201
+ deep: "opencode/claude-opus"
126202
+ }
126203
+ };
126204
+ var ROUTER_LADDER = {
126205
+ minimal: "deepseek/deepseek-flash",
126206
+ light: "openai/gpt-luna",
126207
+ standard: "openai/gpt-sol",
126208
+ deep: "anthropic/claude-opus"
126209
+ };
126210
+ var AUTO_PREFIX = "auto/";
126211
+ function autoLadder(slug2) {
126212
+ if (!slug2 || !slug2.startsWith(AUTO_PREFIX) || isAutoTier(slug2)) return void 0;
126213
+ const target = slug2.slice(AUTO_PREFIX.length);
126214
+ return target === "router" ? ROUTER_LADDER : PROVIDER_LADDERS[target];
126215
+ }
126216
+ var ROUTED_PROVIDERS = Object.keys(
126217
+ PROVIDER_LADDERS
126218
+ ).map((key) => ({ key, displayName: providers[key].displayName }));
126075
126219
  function isCardGatedModel(slug2) {
126076
126220
  return resolveOpenRouterModel(slug2) !== void 0;
126077
126221
  }
@@ -126080,7 +126224,7 @@ function resolveModelSlug(slug2) {
126080
126224
  }
126081
126225
  var MAX_FALLBACK_DEPTH = 10;
126082
126226
  function resolveDisplayAlias(slug2) {
126083
- let current = isAutoTier(slug2) ? AUTO_TIER_TARGET[slug2] : slug2;
126227
+ let current = isAutoTier(slug2) ? AUTO_TIER_TARGET[slug2] : autoLadder(slug2)?.standard ?? slug2;
126084
126228
  const visited = /* @__PURE__ */ new Set();
126085
126229
  for (let i2 = 0; i2 < MAX_FALLBACK_DEPTH; i2++) {
126086
126230
  if (visited.has(current)) return void 0;
@@ -126115,11 +126259,23 @@ if (!defaultProxyAlias?.openRouterResolve) {
126115
126259
  }
126116
126260
  var DEFAULT_PROXY_MODEL = defaultProxyAlias.openRouterResolve;
126117
126261
  var defaultProxyDisplayName = defaultProxyAlias.displayName;
126262
+ for (const [name, ladder] of Object.entries({ ...PROVIDER_LADDERS, router: ROUTER_LADDER })) {
126263
+ const terminal = /* @__PURE__ */ new Set();
126264
+ for (const slug2 of Object.values(ladder)) {
126265
+ const alias = resolveDisplayAlias(slug2);
126266
+ if (!alias) throw new Error(`${name} ladder names unknown alias ${slug2}`);
126267
+ terminal.add(alias.slug);
126268
+ }
126269
+ if (terminal.size < 2) throw new Error(`${name} ladder runs every tier on ${[...terminal][0]}`);
126270
+ }
126118
126271
  var OSS_MODEL_ALLOWLIST = [
126119
126272
  "deepseek/deepseek-flash",
126120
- "deepseek/deepseek-pro",
126121
126273
  "openai/gpt-luna",
126122
- "openrouter/minimax-m2.5"
126274
+ "openrouter/minimax-m2.5",
126275
+ // opt-in: 0.86x the old default on the measured mix, and Meta trains on the
126276
+ // traffic — fine for public code on headless runs, the human decided
126277
+ // 2026-09-11. unmeasured on a real run; not the default until it is.
126278
+ "meta/muse-spark-contributor"
126123
126279
  ];
126124
126280
  var OSS_RECOMMENDED_MODEL = AUTO_TIER_TARGET[AUTO_EFFICIENT];
126125
126281
  function ossFundedModelNames() {
@@ -135795,13 +135951,29 @@ function azureProvider(model) {
135795
135951
  };
135796
135952
  }
135797
135953
  var KIMI_ENFORCERLESS_PROVIDERS = ["siliconflow", "together"];
135798
- function kimiOpenRouterProviderOverrides() {
135799
- return Object.fromEntries(
135800
- modelAliases.map((a2) => a2.openRouterResolve).filter((r2) => r2?.includes("kimi") ?? false).map((r2) => [
135801
- r2.replace(/^openrouter\//, ""),
135802
- { options: { provider: { ignore: KIMI_ENFORCERLESS_PROVIDERS } } }
135803
- ])
135804
- );
135954
+ function openRouterDataCollection(ctx) {
135955
+ if (!ctx.payload.proxyModel) return void 0;
135956
+ return ctx.toolState.oss ? "allow" : "deny";
135957
+ }
135958
+ function dataCollectionPreference(policy) {
135959
+ if (!policy) return {};
135960
+ return { data_collection: policy };
135961
+ }
135962
+ function openRouterProvider(policy) {
135963
+ const models = {};
135964
+ for (const alias of modelAliases) {
135965
+ const id = alias.openRouterResolve?.replace(/^openrouter\//, "");
135966
+ if (!id) continue;
135967
+ if (id.endsWith("-contributor")) {
135968
+ models[id] = { options: { provider: { data_collection: "allow" } } };
135969
+ }
135970
+ if (id.includes("kimi")) {
135971
+ const provider2 = { ...dataCollectionPreference(policy), ignore: KIMI_ENFORCERLESS_PROVIDERS };
135972
+ models[id] = { options: { provider: provider2 } };
135973
+ }
135974
+ }
135975
+ if (!policy) return { models };
135976
+ return { options: { extraBody: { provider: { data_collection: policy } } }, models };
135805
135977
  }
135806
135978
  function buildReviewerAgentConfig(orchestratorModel) {
135807
135979
  const overrides = deriveSubagentModels(orchestratorModel);
@@ -135877,13 +136049,12 @@ function buildSecurityConfig(ctx, model) {
135877
136049
  log2.info(`\xBB subagent models: reviewfrog=${reviewerModel}`);
135878
136050
  return cfg;
135879
136051
  })(),
135880
- // kimi pinned away from Enforcer-less openrouter providers (siliconflow /
135881
- // together) that drop optional tool-call params — per-model on the
135882
- // openrouter route, so other models are unaffected. this is routing, not
135883
- // reasoning: every model's effort now rides the per-prompt `variant`.
135884
- // see opencodeShared.ts.
136052
+ // openrouter routing, not reasoning (every model's effort rides the
136053
+ // per-prompt `variant`): the run's data-collection policy, kimi pinned
136054
+ // away from Enforcer-less providers, the Muse Spark contributor tier
136055
+ // opened to training hosts. see opencodeShared.ts.
135885
136056
  provider: {
135886
- openrouter: { models: kimiOpenRouterProviderOverrides() },
136057
+ openrouter: openRouterProvider(openRouterDataCollection(ctx)),
135887
136058
  ...openAICompatibleProvider(model),
135888
136059
  ...azureProvider(model),
135889
136060
  ...providerGatewayOverride(model)
@@ -216616,6 +216787,15 @@ var JsonPayload = type({
216616
216787
  "type?": "string | undefined",
216617
216788
  "model?": "string | undefined",
216618
216789
  "modelExplicit?": "boolean | undefined",
216790
+ // optional so a payload from a pre-router server build still parses against a
216791
+ // newer action across a rolling deploy.
216792
+ "routing?": type({
216793
+ tier: "'minimal' | 'light' | 'standard' | 'deep'",
216794
+ stakes: "number",
216795
+ workload: "number",
216796
+ source: "'scorer' | 'heuristic' | 'fixed'",
216797
+ rationale: "string"
216798
+ }).or("undefined"),
216619
216799
  "effort?": "number | string | undefined",
216620
216800
  "debug?": "boolean | undefined",
216621
216801
  prompt: "string",
@@ -216797,6 +216977,7 @@ function resolvePayload(resolvedPromptInput, repoSettings) {
216797
216977
  // explicit only when the model came from a per-run override flag (carried on
216798
216978
  // the JSON payload). a GHA `model` input or the repo default is not explicit.
216799
216979
  modelExplicit: jsonPayload?.modelExplicit ?? false,
216980
+ routing: jsonPayload?.routing,
216800
216981
  effort,
216801
216982
  debug: debug4 || void 0,
216802
216983
  prompt,
@@ -217483,9 +217664,12 @@ async function fetchRunContext(params) {
217483
217664
  if (params.oidcToken) {
217484
217665
  headers["X-GitHub-OIDC-Token"] = params.oidcToken;
217485
217666
  }
217486
- const query = params.runType ? `?type=${encodeURIComponent(params.runType)}` : "";
217667
+ const query = new URLSearchParams();
217668
+ if (params.runType) query.set("type", params.runType);
217669
+ if (params.routedTier) query.set("tier", params.routedTier);
217670
+ const search2 = query.toString();
217487
217671
  const response = await apiFetch({
217488
- path: `/api/repo/${params.repoContext.owner}/${params.repoContext.name}/run-context${query}`,
217672
+ path: `/api/repo/${params.repoContext.owner}/${params.repoContext.name}/run-context${search2 ? `?${search2}` : ""}`,
217489
217673
  headers,
217490
217674
  signal: controller.signal
217491
217675
  });
@@ -217552,7 +217736,13 @@ async function resolveRunContextData(params) {
217552
217736
  retries: [100, 500],
217553
217737
  bail: (error109) => !isTransientOctokitError(error109)
217554
217738
  })(),
217555
- fetchRunContext({ token: params.token, repoContext, oidcToken, runType: params.runType })
217739
+ fetchRunContext({
217740
+ token: params.token,
217741
+ repoContext,
217742
+ oidcToken,
217743
+ runType: params.runType,
217744
+ routedTier: params.routedTier
217745
+ })
217556
217746
  ]);
217557
217747
  return {
217558
217748
  repo: {
@@ -218460,6 +218650,12 @@ function logRunStartup(ctx) {
218460
218650
  log2.info(
218461
218651
  `\xBB effort: ${resolveEffortForLog({ payload: ctx.payload, resolvedModel: ctx.resolvedModel })}`
218462
218652
  );
218653
+ const routing = ctx.payload.routing;
218654
+ if (routing) {
218655
+ log2.info(
218656
+ `\xBB routed: ${routing.tier} (${routing.source}: stakes ${routing.stakes}, workload ${routing.workload}) \u2014 ${routing.rationale}`
218657
+ );
218658
+ }
218463
218659
  log2.info(
218464
218660
  `\xBB agent: ${resolveAgentForLog({ agentName: ctx.agentName, resolvedModel: ctx.resolvedModel })}`
218465
218661
  );
@@ -218654,7 +218850,8 @@ async function main() {
218654
218850
  const runContext = await resolveRunContextData({
218655
218851
  octokit: initialOctokit,
218656
218852
  token: jobToken,
218657
- runType: typeof resolvedPromptInput === "string" ? void 0 : resolvedPromptInput.type
218853
+ runType: typeof resolvedPromptInput === "string" ? void 0 : resolvedPromptInput.type,
218854
+ routedTier: typeof resolvedPromptInput === "string" ? void 0 : resolvedPromptInput.routing?.tier
218658
218855
  });
218659
218856
  timer.checkpoint("runContextData");
218660
218857
  const payload = resolvePayload(resolvedPromptInput, runContext.repoSettings);
@@ -219775,7 +219972,7 @@ function resolveTarget2(repo) {
219775
219972
  return { owner: match3[1], repo: match3[2] };
219776
219973
  }
219777
219974
  function cliVersion() {
219778
- const parts = /^(\d+)\.(\d+)\.(\d+)$/.exec("0.1.76");
219975
+ const parts = /^(\d+)\.(\d+)\.(\d+)$/.exec("0.1.78");
219779
219976
  if (!parts) return "0.0.0";
219780
219977
  return `${Number(parts[1])}.${Number(parts[2])}.${Number(parts[3])}`;
219781
219978
  }
@@ -219942,7 +220139,7 @@ async function runCli8(input2) {
219942
220139
  }
219943
220140
 
219944
220141
  // cli.ts
219945
- var VERSION10 = "0.1.76";
220142
+ var VERSION10 = "0.1.78";
219946
220143
  var bin = basename2(process.argv[1] || "");
219947
220144
  var PROG = bin === "pf" || bin === "pullfrog" ? bin : "pullfrog";
219948
220145
  var rawArgs = process.argv.slice(2);
@@ -4,6 +4,7 @@
4
4
  * Other files in action/ re-export from this file for backward compatibility.
5
5
  */
6
6
  import type { EffortPosition } from "./effort.ts";
7
+ import type { RouterTier } from "./models.ts";
7
8
  export declare const pullfrogMcpName = "pullfrog";
8
9
  /** @see {@link file://./agents/shared.ts} Agent interface that uses this type */
9
10
  export type AgentId = "claude" | "codex" | "opencode";
@@ -51,8 +52,8 @@ export declare function findDanglingPromptToolRefs(params: {
51
52
  }): string[];
52
53
  export type { EffortPosition } from "./effort.ts";
53
54
  export { DEFAULT_EFFORT_POSITION, EFFORT_ALIASES, isEffortPosition, offeredRungs, parseEffortPosition, resolveRung, rungLabel, rungPosition, SUBSIDY_RUNG, } from "./effort.ts";
54
- export type { AutoTier, ModelAlias, ModelProvider, ProviderConfig } from "./models.ts";
55
- export { AUTO_EFFICIENT, AUTO_INTELLIGENT, DEFAULT_PROXY_MODEL, defaultAutoTier, getAutoSelectHintModel, getModelEffortLevels, getModelEnvVars, getModelManagedCredentials, getModelProvider, getProviderDisplayName, isAutoTier, isCardGatedModel, isOssAllowedModel, modelAliases, modelHasStoredAuth, OSS_MODEL_ALLOWLIST, OSS_RECOMMENDED_MODEL, ossFundedModelNames, PROVIDER_GATEWAY_URL_ENV, parseModel, providers, resolveAutoTier, resolveCliModel, resolveDisplayAlias, resolveModelRung, resolveModelSlug, resolveOpenRouterModel, } from "./models.ts";
55
+ export type { AutoTier, ModelAlias, ModelProvider, ProviderConfig, RouterTier, } from "./models.ts";
56
+ export { AUTO_EFFICIENT, AUTO_INTELLIGENT, AUTO_ROUTER, autoLadder, autoRoutedSlug, DEFAULT_PROXY_MODEL, defaultAutoTier, getAutoSelectHintModel, getModelEffortLevels, getModelEnvVars, getModelManagedCredentials, getModelProvider, getProviderDisplayName, isAutoRouted, isAutoSlug, isAutoTier, isCardGatedModel, isOssAllowedModel, isRouterTier, ladderRungs, modelAliases, modelHasStoredAuth, OSS_MODEL_ALLOWLIST, OSS_RECOMMENDED_MODEL, ossFundedModelNames, PROVIDER_GATEWAY_URL_ENV, parseModel, providers, ROUTED_PROVIDERS, ROUTER_LADDER, ROUTER_TIERS, resolveAutoTier, resolveCliModel, resolveDisplayAlias, resolveModelRung, resolveModelSlug, resolveOpenRouterModel, resolveRoutedModel, } from "./models.ts";
56
57
  export type ToolPermission = "disabled" | "enabled";
57
58
  export type ShellPermission = "disabled" | "restricted" | "enabled";
58
59
  export type PushPermission = "disabled" | "restricted" | "enabled";
@@ -262,6 +263,21 @@ export interface XrepoConfig {
262
263
  */
263
264
  unavailable?: string[] | undefined;
264
265
  }
266
+ /**
267
+ * the model router's decision, carried so the runner can log it and forward the
268
+ * tier to run-context as `?tier=` — the Router proxy mint happens there and
269
+ * never sees `model` below. see wiki/router.md.
270
+ */
271
+ export interface PayloadRouting {
272
+ tier: RouterTier;
273
+ stakes: number;
274
+ workload: number;
275
+ /** who decided: the scorer, the heuristic it falls back to, or the fixed tier a non-review kind of work carries. mirrors the `RouterSource` enum. */
276
+ source: RouterSource;
277
+ /** the scorer's own sentence or two on what a reviewer must reconstruct */
278
+ rationale: string;
279
+ }
280
+ export type RouterSource = "scorer" | "heuristic" | "fixed";
265
281
  export interface WriteablePayload {
266
282
  "~pullfrog": true;
267
283
  /** semantic version of the payload to ensure compatibility */
@@ -283,6 +299,8 @@ export interface WriteablePayload {
283
299
  * baseInstructions flag) keeps the soft-fallback safety net. see modelAccess.ts.
284
300
  */
285
301
  modelExplicit?: boolean | undefined;
302
+ /** the model router's decision, when it made one; `model` above already reflects it */
303
+ routing?: PayloadRouting | undefined;
286
304
  /** reasoning-effort position on [0,1]; 0 is the model's cheapest rung, 1 its priciest */
287
305
  effort?: EffortPosition | undefined;
288
306
  /**