pullfrog 0.1.76 → 0.1.77

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/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.77",
123004
123004
  cliContractVersion: "1",
123005
123005
  type: "module",
123006
123006
  bin: {
@@ -126072,6 +126072,71 @@ var AUTO_TIER_TARGET = {
126072
126072
  function isAutoTier(slug2) {
126073
126073
  return slug2 === AUTO_EFFICIENT || slug2 === AUTO_INTELLIGENT;
126074
126074
  }
126075
+ var PROVIDER_LADDERS = {
126076
+ anthropic: {
126077
+ minimal: "anthropic/claude-haiku",
126078
+ light: "anthropic/claude-sonnet",
126079
+ standard: "anthropic/claude-opus",
126080
+ deep: "anthropic/claude-opus"
126081
+ },
126082
+ openai: {
126083
+ minimal: "openai/gpt-luna",
126084
+ light: "openai/gpt-terra",
126085
+ standard: "openai/gpt-sol",
126086
+ deep: "openai/gpt-astra"
126087
+ },
126088
+ google: {
126089
+ minimal: "google/gemini-flash",
126090
+ light: "google/gemini-flash",
126091
+ standard: "google/gemini-pro",
126092
+ deep: "google/gemini-pro"
126093
+ },
126094
+ deepseek: {
126095
+ minimal: "deepseek/deepseek-flash",
126096
+ light: "deepseek/deepseek-flash",
126097
+ standard: "deepseek/deepseek-pro",
126098
+ deep: "deepseek/deepseek-pro"
126099
+ },
126100
+ xai: {
126101
+ minimal: "xai/grok-fast",
126102
+ light: "xai/grok-fast",
126103
+ standard: "xai/grok",
126104
+ deep: "xai/grok"
126105
+ },
126106
+ moonshotai: {
126107
+ minimal: "moonshotai/kimi-k2",
126108
+ light: "moonshotai/kimi-k2",
126109
+ standard: "moonshotai/kimi-k3",
126110
+ deep: "moonshotai/kimi-k3"
126111
+ },
126112
+ openrouter: {
126113
+ minimal: "openrouter/deepseek-flash",
126114
+ light: "openrouter/gpt-luna",
126115
+ standard: "openrouter/gpt-sol",
126116
+ deep: "openrouter/claude-opus"
126117
+ },
126118
+ opencode: {
126119
+ minimal: "opencode/gpt-luna",
126120
+ light: "opencode/gpt-luna",
126121
+ standard: "opencode/gpt-sol",
126122
+ deep: "opencode/claude-opus"
126123
+ }
126124
+ };
126125
+ var ROUTER_LADDER = {
126126
+ minimal: "deepseek/deepseek-flash",
126127
+ light: "openai/gpt-luna",
126128
+ standard: "openai/gpt-sol",
126129
+ deep: "anthropic/claude-opus"
126130
+ };
126131
+ var AUTO_PREFIX = "auto/";
126132
+ function autoLadder(slug2) {
126133
+ if (!slug2 || !slug2.startsWith(AUTO_PREFIX) || isAutoTier(slug2)) return void 0;
126134
+ const target = slug2.slice(AUTO_PREFIX.length);
126135
+ return target === "router" ? ROUTER_LADDER : PROVIDER_LADDERS[target];
126136
+ }
126137
+ var ROUTED_PROVIDERS = Object.keys(
126138
+ PROVIDER_LADDERS
126139
+ ).map((key) => ({ key, displayName: providers[key].displayName }));
126075
126140
  function isCardGatedModel(slug2) {
126076
126141
  return resolveOpenRouterModel(slug2) !== void 0;
126077
126142
  }
@@ -126080,7 +126145,7 @@ function resolveModelSlug(slug2) {
126080
126145
  }
126081
126146
  var MAX_FALLBACK_DEPTH = 10;
126082
126147
  function resolveDisplayAlias(slug2) {
126083
- let current = isAutoTier(slug2) ? AUTO_TIER_TARGET[slug2] : slug2;
126148
+ let current = isAutoTier(slug2) ? AUTO_TIER_TARGET[slug2] : autoLadder(slug2)?.standard ?? slug2;
126084
126149
  const visited = /* @__PURE__ */ new Set();
126085
126150
  for (let i2 = 0; i2 < MAX_FALLBACK_DEPTH; i2++) {
126086
126151
  if (visited.has(current)) return void 0;
@@ -126115,6 +126180,11 @@ if (!defaultProxyAlias?.openRouterResolve) {
126115
126180
  }
126116
126181
  var DEFAULT_PROXY_MODEL = defaultProxyAlias.openRouterResolve;
126117
126182
  var defaultProxyDisplayName = defaultProxyAlias.displayName;
126183
+ for (const [name, ladder] of Object.entries({ ...PROVIDER_LADDERS, router: ROUTER_LADDER })) {
126184
+ for (const slug2 of Object.values(ladder)) {
126185
+ if (!resolveDisplayAlias(slug2)) throw new Error(`${name} ladder names unknown alias ${slug2}`);
126186
+ }
126187
+ }
126118
126188
  var OSS_MODEL_ALLOWLIST = [
126119
126189
  "deepseek/deepseek-flash",
126120
126190
  "deepseek/deepseek-pro",
@@ -216616,6 +216686,15 @@ var JsonPayload = type({
216616
216686
  "type?": "string | undefined",
216617
216687
  "model?": "string | undefined",
216618
216688
  "modelExplicit?": "boolean | undefined",
216689
+ // optional so a payload from a pre-router server build still parses against a
216690
+ // newer action across a rolling deploy.
216691
+ "routing?": type({
216692
+ tier: "'minimal' | 'light' | 'standard' | 'deep'",
216693
+ stakes: "number",
216694
+ workload: "number",
216695
+ source: "'scorer' | 'heuristic' | 'fixed'",
216696
+ rationale: "string"
216697
+ }).or("undefined"),
216619
216698
  "effort?": "number | string | undefined",
216620
216699
  "debug?": "boolean | undefined",
216621
216700
  prompt: "string",
@@ -216797,6 +216876,7 @@ function resolvePayload(resolvedPromptInput, repoSettings) {
216797
216876
  // explicit only when the model came from a per-run override flag (carried on
216798
216877
  // the JSON payload). a GHA `model` input or the repo default is not explicit.
216799
216878
  modelExplicit: jsonPayload?.modelExplicit ?? false,
216879
+ routing: jsonPayload?.routing,
216800
216880
  effort,
216801
216881
  debug: debug4 || void 0,
216802
216882
  prompt,
@@ -217483,9 +217563,12 @@ async function fetchRunContext(params) {
217483
217563
  if (params.oidcToken) {
217484
217564
  headers["X-GitHub-OIDC-Token"] = params.oidcToken;
217485
217565
  }
217486
- const query = params.runType ? `?type=${encodeURIComponent(params.runType)}` : "";
217566
+ const query = new URLSearchParams();
217567
+ if (params.runType) query.set("type", params.runType);
217568
+ if (params.routedTier) query.set("tier", params.routedTier);
217569
+ const search2 = query.toString();
217487
217570
  const response = await apiFetch({
217488
- path: `/api/repo/${params.repoContext.owner}/${params.repoContext.name}/run-context${query}`,
217571
+ path: `/api/repo/${params.repoContext.owner}/${params.repoContext.name}/run-context${search2 ? `?${search2}` : ""}`,
217489
217572
  headers,
217490
217573
  signal: controller.signal
217491
217574
  });
@@ -217552,7 +217635,13 @@ async function resolveRunContextData(params) {
217552
217635
  retries: [100, 500],
217553
217636
  bail: (error109) => !isTransientOctokitError(error109)
217554
217637
  })(),
217555
- fetchRunContext({ token: params.token, repoContext, oidcToken, runType: params.runType })
217638
+ fetchRunContext({
217639
+ token: params.token,
217640
+ repoContext,
217641
+ oidcToken,
217642
+ runType: params.runType,
217643
+ routedTier: params.routedTier
217644
+ })
217556
217645
  ]);
217557
217646
  return {
217558
217647
  repo: {
@@ -218460,6 +218549,12 @@ function logRunStartup(ctx) {
218460
218549
  log2.info(
218461
218550
  `\xBB effort: ${resolveEffortForLog({ payload: ctx.payload, resolvedModel: ctx.resolvedModel })}`
218462
218551
  );
218552
+ const routing = ctx.payload.routing;
218553
+ if (routing) {
218554
+ log2.info(
218555
+ `\xBB routed: ${routing.tier} (${routing.source}: stakes ${routing.stakes}, workload ${routing.workload}) \u2014 ${routing.rationale}`
218556
+ );
218557
+ }
218463
218558
  log2.info(
218464
218559
  `\xBB agent: ${resolveAgentForLog({ agentName: ctx.agentName, resolvedModel: ctx.resolvedModel })}`
218465
218560
  );
@@ -218654,7 +218749,8 @@ async function main() {
218654
218749
  const runContext = await resolveRunContextData({
218655
218750
  octokit: initialOctokit,
218656
218751
  token: jobToken,
218657
- runType: typeof resolvedPromptInput === "string" ? void 0 : resolvedPromptInput.type
218752
+ runType: typeof resolvedPromptInput === "string" ? void 0 : resolvedPromptInput.type,
218753
+ routedTier: typeof resolvedPromptInput === "string" ? void 0 : resolvedPromptInput.routing?.tier
218658
218754
  });
218659
218755
  timer.checkpoint("runContextData");
218660
218756
  const payload = resolvePayload(resolvedPromptInput, runContext.repoSettings);
@@ -219775,7 +219871,7 @@ function resolveTarget2(repo) {
219775
219871
  return { owner: match3[1], repo: match3[2] };
219776
219872
  }
219777
219873
  function cliVersion() {
219778
- const parts = /^(\d+)\.(\d+)\.(\d+)$/.exec("0.1.76");
219874
+ const parts = /^(\d+)\.(\d+)\.(\d+)$/.exec("0.1.77");
219779
219875
  if (!parts) return "0.0.0";
219780
219876
  return `${Number(parts[1])}.${Number(parts[2])}.${Number(parts[3])}`;
219781
219877
  }
@@ -219942,7 +220038,7 @@ async function runCli8(input2) {
219942
220038
  }
219943
220039
 
219944
220040
  // cli.ts
219945
- var VERSION10 = "0.1.76";
220041
+ var VERSION10 = "0.1.77";
219946
220042
  var bin = basename2(process.argv[1] || "");
219947
220043
  var PROG = bin === "pf" || bin === "pullfrog" ? bin : "pullfrog";
219948
220044
  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
  /**
package/dist/index.js CHANGED
@@ -108891,6 +108891,71 @@ var AUTO_TIER_TARGET = {
108891
108891
  function isAutoTier(slug2) {
108892
108892
  return slug2 === AUTO_EFFICIENT || slug2 === AUTO_INTELLIGENT;
108893
108893
  }
108894
+ var PROVIDER_LADDERS = {
108895
+ anthropic: {
108896
+ minimal: "anthropic/claude-haiku",
108897
+ light: "anthropic/claude-sonnet",
108898
+ standard: "anthropic/claude-opus",
108899
+ deep: "anthropic/claude-opus"
108900
+ },
108901
+ openai: {
108902
+ minimal: "openai/gpt-luna",
108903
+ light: "openai/gpt-terra",
108904
+ standard: "openai/gpt-sol",
108905
+ deep: "openai/gpt-astra"
108906
+ },
108907
+ google: {
108908
+ minimal: "google/gemini-flash",
108909
+ light: "google/gemini-flash",
108910
+ standard: "google/gemini-pro",
108911
+ deep: "google/gemini-pro"
108912
+ },
108913
+ deepseek: {
108914
+ minimal: "deepseek/deepseek-flash",
108915
+ light: "deepseek/deepseek-flash",
108916
+ standard: "deepseek/deepseek-pro",
108917
+ deep: "deepseek/deepseek-pro"
108918
+ },
108919
+ xai: {
108920
+ minimal: "xai/grok-fast",
108921
+ light: "xai/grok-fast",
108922
+ standard: "xai/grok",
108923
+ deep: "xai/grok"
108924
+ },
108925
+ moonshotai: {
108926
+ minimal: "moonshotai/kimi-k2",
108927
+ light: "moonshotai/kimi-k2",
108928
+ standard: "moonshotai/kimi-k3",
108929
+ deep: "moonshotai/kimi-k3"
108930
+ },
108931
+ openrouter: {
108932
+ minimal: "openrouter/deepseek-flash",
108933
+ light: "openrouter/gpt-luna",
108934
+ standard: "openrouter/gpt-sol",
108935
+ deep: "openrouter/claude-opus"
108936
+ },
108937
+ opencode: {
108938
+ minimal: "opencode/gpt-luna",
108939
+ light: "opencode/gpt-luna",
108940
+ standard: "opencode/gpt-sol",
108941
+ deep: "opencode/claude-opus"
108942
+ }
108943
+ };
108944
+ var ROUTER_LADDER = {
108945
+ minimal: "deepseek/deepseek-flash",
108946
+ light: "openai/gpt-luna",
108947
+ standard: "openai/gpt-sol",
108948
+ deep: "anthropic/claude-opus"
108949
+ };
108950
+ var AUTO_PREFIX = "auto/";
108951
+ function autoLadder(slug2) {
108952
+ if (!slug2 || !slug2.startsWith(AUTO_PREFIX) || isAutoTier(slug2)) return void 0;
108953
+ const target = slug2.slice(AUTO_PREFIX.length);
108954
+ return target === "router" ? ROUTER_LADDER : PROVIDER_LADDERS[target];
108955
+ }
108956
+ var ROUTED_PROVIDERS = Object.keys(
108957
+ PROVIDER_LADDERS
108958
+ ).map((key) => ({ key, displayName: providers[key].displayName }));
108894
108959
  function isCardGatedModel(slug2) {
108895
108960
  return resolveOpenRouterModel(slug2) !== void 0;
108896
108961
  }
@@ -108899,7 +108964,7 @@ function resolveModelSlug(slug2) {
108899
108964
  }
108900
108965
  var MAX_FALLBACK_DEPTH = 10;
108901
108966
  function resolveDisplayAlias(slug2) {
108902
- let current = isAutoTier(slug2) ? AUTO_TIER_TARGET[slug2] : slug2;
108967
+ let current = isAutoTier(slug2) ? AUTO_TIER_TARGET[slug2] : autoLadder(slug2)?.standard ?? slug2;
108903
108968
  const visited = /* @__PURE__ */ new Set();
108904
108969
  for (let i = 0; i < MAX_FALLBACK_DEPTH; i++) {
108905
108970
  if (visited.has(current)) return void 0;
@@ -108934,6 +108999,11 @@ if (!defaultProxyAlias?.openRouterResolve) {
108934
108999
  }
108935
109000
  var DEFAULT_PROXY_MODEL = defaultProxyAlias.openRouterResolve;
108936
109001
  var defaultProxyDisplayName = defaultProxyAlias.displayName;
109002
+ for (const [name, ladder] of Object.entries({ ...PROVIDER_LADDERS, router: ROUTER_LADDER })) {
109003
+ for (const slug2 of Object.values(ladder)) {
109004
+ if (!resolveDisplayAlias(slug2)) throw new Error(`${name} ladder names unknown alias ${slug2}`);
109005
+ }
109006
+ }
108937
109007
  var OSS_MODEL_ALLOWLIST = [
108938
109008
  "deepseek/deepseek-flash",
108939
109009
  "deepseek/deepseek-pro",
@@ -109806,7 +109876,7 @@ var import_semver = __toESM(require_semver2(), 1);
109806
109876
  // package.json
109807
109877
  var package_default = {
109808
109878
  name: "pullfrog",
109809
- version: "0.1.76",
109879
+ version: "0.1.77",
109810
109880
  cliContractVersion: "1",
109811
109881
  type: "module",
109812
109882
  bin: {
@@ -199476,6 +199546,15 @@ var JsonPayload = type({
199476
199546
  "type?": "string | undefined",
199477
199547
  "model?": "string | undefined",
199478
199548
  "modelExplicit?": "boolean | undefined",
199549
+ // optional so a payload from a pre-router server build still parses against a
199550
+ // newer action across a rolling deploy.
199551
+ "routing?": type({
199552
+ tier: "'minimal' | 'light' | 'standard' | 'deep'",
199553
+ stakes: "number",
199554
+ workload: "number",
199555
+ source: "'scorer' | 'heuristic' | 'fixed'",
199556
+ rationale: "string"
199557
+ }).or("undefined"),
199479
199558
  "effort?": "number | string | undefined",
199480
199559
  "debug?": "boolean | undefined",
199481
199560
  prompt: "string",
@@ -199657,6 +199736,7 @@ function resolvePayload(resolvedPromptInput, repoSettings) {
199657
199736
  // explicit only when the model came from a per-run override flag (carried on
199658
199737
  // the JSON payload). a GHA `model` input or the repo default is not explicit.
199659
199738
  modelExplicit: jsonPayload?.modelExplicit ?? false,
199739
+ routing: jsonPayload?.routing,
199660
199740
  effort,
199661
199741
  debug: debug3 || void 0,
199662
199742
  prompt,
@@ -200343,9 +200423,12 @@ async function fetchRunContext(params) {
200343
200423
  if (params.oidcToken) {
200344
200424
  headers["X-GitHub-OIDC-Token"] = params.oidcToken;
200345
200425
  }
200346
- const query = params.runType ? `?type=${encodeURIComponent(params.runType)}` : "";
200426
+ const query = new URLSearchParams();
200427
+ if (params.runType) query.set("type", params.runType);
200428
+ if (params.routedTier) query.set("tier", params.routedTier);
200429
+ const search2 = query.toString();
200347
200430
  const response = await apiFetch({
200348
- path: `/api/repo/${params.repoContext.owner}/${params.repoContext.name}/run-context${query}`,
200431
+ path: `/api/repo/${params.repoContext.owner}/${params.repoContext.name}/run-context${search2 ? `?${search2}` : ""}`,
200349
200432
  headers,
200350
200433
  signal: controller.signal
200351
200434
  });
@@ -200412,7 +200495,13 @@ async function resolveRunContextData(params) {
200412
200495
  retries: [100, 500],
200413
200496
  bail: (error62) => !isTransientOctokitError(error62)
200414
200497
  })(),
200415
- fetchRunContext({ token: params.token, repoContext, oidcToken, runType: params.runType })
200498
+ fetchRunContext({
200499
+ token: params.token,
200500
+ repoContext,
200501
+ oidcToken,
200502
+ runType: params.runType,
200503
+ routedTier: params.routedTier
200504
+ })
200416
200505
  ]);
200417
200506
  return {
200418
200507
  repo: {
@@ -201320,6 +201409,12 @@ function logRunStartup(ctx) {
201320
201409
  log.info(
201321
201410
  `\xBB effort: ${resolveEffortForLog({ payload: ctx.payload, resolvedModel: ctx.resolvedModel })}`
201322
201411
  );
201412
+ const routing = ctx.payload.routing;
201413
+ if (routing) {
201414
+ log.info(
201415
+ `\xBB routed: ${routing.tier} (${routing.source}: stakes ${routing.stakes}, workload ${routing.workload}) \u2014 ${routing.rationale}`
201416
+ );
201417
+ }
201323
201418
  log.info(
201324
201419
  `\xBB agent: ${resolveAgentForLog({ agentName: ctx.agentName, resolvedModel: ctx.resolvedModel })}`
201325
201420
  );
@@ -201514,7 +201609,8 @@ async function main() {
201514
201609
  const runContext = await resolveRunContextData({
201515
201610
  octokit: initialOctokit,
201516
201611
  token: jobToken,
201517
- runType: typeof resolvedPromptInput === "string" ? void 0 : resolvedPromptInput.type
201612
+ runType: typeof resolvedPromptInput === "string" ? void 0 : resolvedPromptInput.type,
201613
+ routedTier: typeof resolvedPromptInput === "string" ? void 0 : resolvedPromptInput.routing?.tier
201518
201614
  });
201519
201615
  timer.checkpoint("runContextData");
201520
201616
  const payload = resolvePayload(resolvedPromptInput, runContext.repoSettings);
@@ -5,8 +5,8 @@
5
5
  export { CLI_CONTRACT_HEADER, CLI_CONTRACT_VERSION, CLI_UPGRADE_MESSAGE } from "../cliContract.ts";
6
6
  export type { ConfigField, ConfigScope, ConfigValue } from "../configuration.ts";
7
7
  export { configEntrySchema, configFields, configResponseSchema, configValueSchema, } from "../configuration.ts";
8
- export type { AuthorPermission, AutoTier, EffortPosition, ModelAlias, ModelProvider, Payload, PayloadEvent, ProviderConfig, PushPermission, ShellPermission, ToolPermission, WriteablePayload, XrepoConfig, } from "../external.ts";
9
- export { AUTO_EFFICIENT, AUTO_INTELLIGENT, DEFAULT_EFFORT_POSITION, DEFAULT_PROXY_MODEL, defaultAutoTier, EFFORT_ALIASES, getAutoSelectHintModel, getModelEffortLevels, getModelEnvVars, getModelManagedCredentials, getModelProvider, getProviderDisplayName, isAutoTier, isCardGatedModel, isEffortPosition, isOssAllowedModel, modelAliases, modelHasStoredAuth, OSS_MODEL_ALLOWLIST, OSS_RECOMMENDED_MODEL, offeredRungs, ossFundedModelNames, PROVIDER_GATEWAY_URL_ENV, parseEffortPosition, parseModel, providers, pullfrogMcpName, resolveAutoTier, resolveCliModel, resolveDisplayAlias, resolveModelRung, resolveModelSlug, resolveOpenRouterModel, resolveRung, rungLabel, rungPosition, SUBSIDY_RUNG, } from "../external.ts";
8
+ export type { AuthorPermission, AutoTier, EffortPosition, ModelAlias, ModelProvider, Payload, PayloadEvent, PayloadRouting, ProviderConfig, PushPermission, RouterSource, RouterTier, ShellPermission, ToolPermission, WriteablePayload, XrepoConfig, } from "../external.ts";
9
+ export { AUTO_EFFICIENT, AUTO_INTELLIGENT, AUTO_ROUTER, autoLadder, autoRoutedSlug, DEFAULT_EFFORT_POSITION, DEFAULT_PROXY_MODEL, defaultAutoTier, EFFORT_ALIASES, getAutoSelectHintModel, getModelEffortLevels, getModelEnvVars, getModelManagedCredentials, getModelProvider, getProviderDisplayName, isAutoRouted, isAutoSlug, isAutoTier, isCardGatedModel, isEffortPosition, isOssAllowedModel, isRouterTier, ladderRungs, modelAliases, modelHasStoredAuth, OSS_MODEL_ALLOWLIST, OSS_RECOMMENDED_MODEL, offeredRungs, ossFundedModelNames, PROVIDER_GATEWAY_URL_ENV, parseEffortPosition, parseModel, providers, pullfrogMcpName, ROUTED_PROVIDERS, ROUTER_LADDER, ROUTER_TIERS, resolveAutoTier, resolveCliModel, resolveDisplayAlias, resolveModelRung, resolveModelSlug, resolveOpenRouterModel, resolveRoutedModel, resolveRung, rungLabel, rungPosition, SUBSIDY_RUNG, } from "../external.ts";
10
10
  export type { Mode } from "../modes.ts";
11
11
  export { modes } from "../modes.ts";
12
12
  export { commercialPaywallBody } from "../utils/billingErrors.ts";
package/dist/internal.js CHANGED
@@ -8,7 +8,7 @@ var __export = (target, all) => {
8
8
  // package.json
9
9
  var package_default = {
10
10
  name: "pullfrog",
11
- version: "0.1.76",
11
+ version: "0.1.77",
12
12
  cliContractVersion: "1",
13
13
  type: "module",
14
14
  bin: {
@@ -15475,6 +15475,97 @@ function resolveAutoTier(params) {
15475
15475
  if (!params.hasCard) return AUTO_EFFICIENT;
15476
15476
  return isAutoTier(params.model) ? params.model : defaultAutoTier(params.hasCard);
15477
15477
  }
15478
+ var ROUTER_TIERS = ["minimal", "light", "standard", "deep"];
15479
+ function isRouterTier(value) {
15480
+ return typeof value === "string" && ROUTER_TIERS.some((tier) => tier === value);
15481
+ }
15482
+ var PROVIDER_LADDERS = {
15483
+ anthropic: {
15484
+ minimal: "anthropic/claude-haiku",
15485
+ light: "anthropic/claude-sonnet",
15486
+ standard: "anthropic/claude-opus",
15487
+ deep: "anthropic/claude-opus"
15488
+ },
15489
+ openai: {
15490
+ minimal: "openai/gpt-luna",
15491
+ light: "openai/gpt-terra",
15492
+ standard: "openai/gpt-sol",
15493
+ deep: "openai/gpt-astra"
15494
+ },
15495
+ google: {
15496
+ minimal: "google/gemini-flash",
15497
+ light: "google/gemini-flash",
15498
+ standard: "google/gemini-pro",
15499
+ deep: "google/gemini-pro"
15500
+ },
15501
+ deepseek: {
15502
+ minimal: "deepseek/deepseek-flash",
15503
+ light: "deepseek/deepseek-flash",
15504
+ standard: "deepseek/deepseek-pro",
15505
+ deep: "deepseek/deepseek-pro"
15506
+ },
15507
+ xai: {
15508
+ minimal: "xai/grok-fast",
15509
+ light: "xai/grok-fast",
15510
+ standard: "xai/grok",
15511
+ deep: "xai/grok"
15512
+ },
15513
+ moonshotai: {
15514
+ minimal: "moonshotai/kimi-k2",
15515
+ light: "moonshotai/kimi-k2",
15516
+ standard: "moonshotai/kimi-k3",
15517
+ deep: "moonshotai/kimi-k3"
15518
+ },
15519
+ openrouter: {
15520
+ minimal: "openrouter/deepseek-flash",
15521
+ light: "openrouter/gpt-luna",
15522
+ standard: "openrouter/gpt-sol",
15523
+ deep: "openrouter/claude-opus"
15524
+ },
15525
+ opencode: {
15526
+ minimal: "opencode/gpt-luna",
15527
+ light: "opencode/gpt-luna",
15528
+ standard: "opencode/gpt-sol",
15529
+ deep: "opencode/claude-opus"
15530
+ }
15531
+ };
15532
+ var ROUTER_LADDER = {
15533
+ minimal: "deepseek/deepseek-flash",
15534
+ light: "openai/gpt-luna",
15535
+ standard: "openai/gpt-sol",
15536
+ deep: "anthropic/claude-opus"
15537
+ };
15538
+ var AUTO_ROUTER = "auto/router";
15539
+ var AUTO_PREFIX = "auto/";
15540
+ function autoLadder(slug) {
15541
+ if (!slug || !slug.startsWith(AUTO_PREFIX) || isAutoTier(slug)) return void 0;
15542
+ const target = slug.slice(AUTO_PREFIX.length);
15543
+ return target === "router" ? ROUTER_LADDER : PROVIDER_LADDERS[target];
15544
+ }
15545
+ function isAutoRouted(slug) {
15546
+ return autoLadder(slug) !== void 0;
15547
+ }
15548
+ function isAutoSlug(slug) {
15549
+ return isAutoTier(slug) || isAutoRouted(slug);
15550
+ }
15551
+ function autoRoutedSlug(provider2) {
15552
+ return `${AUTO_PREFIX}${provider2}`;
15553
+ }
15554
+ var ROUTED_PROVIDERS = Object.keys(
15555
+ PROVIDER_LADDERS
15556
+ ).map((key) => ({ key, displayName: providers[key].displayName }));
15557
+ function ladderRungs(ladder) {
15558
+ const rungs = [];
15559
+ for (const tier of ROUTER_TIERS) {
15560
+ const last = rungs[rungs.length - 1];
15561
+ if (last?.slug === ladder[tier]) last.tiers.push(tier);
15562
+ else rungs.push({ slug: ladder[tier], tiers: [tier] });
15563
+ }
15564
+ return rungs;
15565
+ }
15566
+ function resolveRoutedModel(params) {
15567
+ return autoLadder(params.configured)?.[params.tier];
15568
+ }
15478
15569
  function isCardGatedModel(slug) {
15479
15570
  return resolveOpenRouterModel(slug) !== void 0;
15480
15571
  }
@@ -15483,7 +15574,7 @@ function resolveModelSlug(slug) {
15483
15574
  }
15484
15575
  var MAX_FALLBACK_DEPTH = 10;
15485
15576
  function resolveDisplayAlias(slug) {
15486
- let current = isAutoTier(slug) ? AUTO_TIER_TARGET[slug] : slug;
15577
+ let current = isAutoTier(slug) ? AUTO_TIER_TARGET[slug] : autoLadder(slug)?.standard ?? slug;
15487
15578
  const visited = /* @__PURE__ */ new Set();
15488
15579
  for (let i = 0; i < MAX_FALLBACK_DEPTH; i++) {
15489
15580
  if (visited.has(current)) return void 0;
@@ -15518,6 +15609,11 @@ if (!defaultProxyAlias?.openRouterResolve) {
15518
15609
  }
15519
15610
  var DEFAULT_PROXY_MODEL = defaultProxyAlias.openRouterResolve;
15520
15611
  var defaultProxyDisplayName = defaultProxyAlias.displayName;
15612
+ for (const [name, ladder] of Object.entries({ ...PROVIDER_LADDERS, router: ROUTER_LADDER })) {
15613
+ for (const slug of Object.values(ladder)) {
15614
+ if (!resolveDisplayAlias(slug)) throw new Error(`${name} ladder names unknown alias ${slug}`);
15615
+ }
15616
+ }
15521
15617
  var OSS_MODEL_ALLOWLIST = [
15522
15618
  "deepseek/deepseek-flash",
15523
15619
  "deepseek/deepseek-pro",
@@ -16599,6 +16695,7 @@ export {
16599
16695
  APPROVAL_CHECK_NAME,
16600
16696
  AUTO_EFFICIENT,
16601
16697
  AUTO_INTELLIGENT,
16698
+ AUTO_ROUTER,
16602
16699
  CLI_CONTRACT_HEADER,
16603
16700
  CLI_CONTRACT_VERSION,
16604
16701
  CLI_UPGRADE_MESSAGE,
@@ -16612,9 +16709,14 @@ export {
16612
16709
  OSS_RECOMMENDED_MODEL,
16613
16710
  PROVIDER_GATEWAY_URL_ENV,
16614
16711
  PULLFROG_DIVIDER,
16712
+ ROUTED_PROVIDERS,
16713
+ ROUTER_LADDER,
16714
+ ROUTER_TIERS,
16615
16715
  RUN_STATUS_CHECK_NAME,
16616
16716
  SUBSIDY_RUNG,
16617
16717
  TIMEOUT_DISABLED,
16718
+ autoLadder,
16719
+ autoRoutedSlug,
16618
16720
  buildPullfrogFooter,
16619
16721
  commercialPaywallBody,
16620
16722
  configEntrySchema,
@@ -16634,6 +16736,8 @@ export {
16634
16736
  getModelProvider,
16635
16737
  getProgressComment,
16636
16738
  getProviderDisplayName,
16739
+ isAutoRouted,
16740
+ isAutoSlug,
16637
16741
  isAutoTier,
16638
16742
  isCardGatedModel,
16639
16743
  isCredentialProbeable,
@@ -16641,7 +16745,9 @@ export {
16641
16745
  isLeapingIntoActionCommentBody,
16642
16746
  isOssAllowedModel,
16643
16747
  isPullfrog,
16748
+ isRouterTier,
16644
16749
  isValidTimeString,
16750
+ ladderRungs,
16645
16751
  modelAliases,
16646
16752
  modelHasStoredAuth,
16647
16753
  modes,
@@ -16662,6 +16768,7 @@ export {
16662
16768
  resolveModelRung,
16663
16769
  resolveModelSlug,
16664
16770
  resolveOpenRouterModel,
16771
+ resolveRoutedModel,
16665
16772
  resolveRung,
16666
16773
  runStatusCheckNeedsFinalizing,
16667
16774
  rungLabel,
package/dist/models.d.ts CHANGED
@@ -218,6 +218,58 @@ export declare function resolveAutoTier(params: {
218
218
  model: string | null;
219
219
  hasCard: boolean;
220
220
  }): AutoTier;
221
+ /**
222
+ * the four rungs the model router scores a review into, ascending. `minimal` is
223
+ * a PR with no behavioural surface, `deep` one whose defects are absences the
224
+ * diff does not show. see wiki/router.md for how a tier is earned.
225
+ */
226
+ export declare const ROUTER_TIERS: readonly ["minimal", "light", "standard", "deep"];
227
+ export type RouterTier = (typeof ROUTER_TIERS)[number];
228
+ export declare function isRouterTier(value: unknown): value is RouterTier;
229
+ type Ladder = Record<RouterTier, string>;
230
+ /**
231
+ * the Router's own ladder for an account on `auto/router` with a card on
232
+ * file. an account without a card is never routed onto this:
233
+ * `resolveAutoTier` clamps it to the efficient default at every tier, exactly
234
+ * as before the router existed.
235
+ */
236
+ export declare const ROUTER_LADDER: Ladder;
237
+ /**
238
+ * the `repo.model` sentinel that turns the router on: `auto/<provider>` runs
239
+ * each piece of work on that provider's ladder, `auto/router` on the Pullfrog
240
+ * Router's. distinct from the two managed tiers above, which each name ONE
241
+ * model. a Pro feature — outside Pro the sentinel simply runs its ladder's
242
+ * `standard` rung, which is what `resolveDisplayAlias` resolves it to.
243
+ */
244
+ export declare const AUTO_ROUTER = "auto/router";
245
+ /** the ladder an Auto sentinel routes on; undefined for a tier or a pin */
246
+ export declare function autoLadder(slug: string | null | undefined): Ladder | undefined;
247
+ export declare function isAutoRouted(slug: string | null | undefined): boolean;
248
+ /** any `auto/*` sentinel — a managed tier or the router — as opposed to a concrete pin */
249
+ export declare function isAutoSlug(slug: string | null | undefined): boolean;
250
+ export declare function autoRoutedSlug(provider: string): string;
251
+ /** the providers a BYOK repo can put on Auto — exactly the ones with a ladder */
252
+ export declare const ROUTED_PROVIDERS: readonly {
253
+ key: string;
254
+ displayName: string;
255
+ }[];
256
+ /**
257
+ * a ladder's distinct models in ascending order, each with the tiers it
258
+ * serves — the console's "what Auto runs on" list, where a model that covers
259
+ * two tiers is one row.
260
+ */
261
+ export declare function ladderRungs(ladder: Ladder): {
262
+ slug: string;
263
+ tiers: RouterTier[];
264
+ }[];
265
+ /**
266
+ * the model a routed run takes: the tier's rung on the ladder the configured
267
+ * Auto sentinel names. undefined for a pin — a pin opts out of routing.
268
+ */
269
+ export declare function resolveRoutedModel(params: {
270
+ configured: string | null | undefined;
271
+ tier: RouterTier;
272
+ }): string | undefined;
221
273
  /**
222
274
  * Router-resolvable — the set a card on file unlocks. custom (non-Auto) picks
223
275
  * are card-gated wholesale on the Router: the console locks the Custom tab
@@ -7,6 +7,13 @@ export declare const JsonPayload: import("arktype/internal/variants/object.ts").
7
7
  type?: string | undefined;
8
8
  model?: string | undefined;
9
9
  modelExplicit?: boolean | undefined;
10
+ routing?: {
11
+ tier: "minimal" | "light" | "standard" | "deep";
12
+ stakes: number;
13
+ workload: number;
14
+ source: "scorer" | "heuristic" | "fixed";
15
+ rationale: string;
16
+ } | undefined;
10
17
  effort?: string | number | undefined;
11
18
  debug?: boolean | undefined;
12
19
  triggerer?: string | undefined;
@@ -54,6 +61,13 @@ export declare function resolvePayload(resolvedPromptInput: ResolvedPromptInput,
54
61
  version: string;
55
62
  model: string | undefined;
56
63
  modelExplicit: boolean;
64
+ routing: {
65
+ tier: "minimal" | "light" | "standard" | "deep";
66
+ stakes: number;
67
+ workload: number;
68
+ source: "scorer" | "heuristic" | "fixed";
69
+ rationale: string;
70
+ } | undefined;
57
71
  effort: number | undefined;
58
72
  debug: true | undefined;
59
73
  prompt: string;
@@ -1,4 +1,5 @@
1
1
  import type { PushPermission, ShellPermission } from "../external.ts";
2
+ import type { RouterTier } from "../models.ts";
2
3
  import type { RepoContext } from "./github.ts";
3
4
  export interface Mode {
4
5
  id: string;
@@ -104,4 +105,7 @@ export declare function fetchRunContext(params: {
104
105
  /** `payload.type` — lets the server apply this repo's per-trigger model
105
106
  * override, which it cannot derive from owner/repo alone. */
106
107
  runType?: string | undefined;
108
+ /** `payload.routing.tier` — the model router's tier, which run-context
109
+ * applies to the Router proxy mint the same way it applies `runType`. */
110
+ routedTier?: RouterTier | undefined;
107
111
  }): Promise<RunContext>;
@@ -1,4 +1,5 @@
1
1
  import type { Octokit } from "@octokit/rest";
2
+ import type { RouterTier } from "../models.ts";
2
3
  import { type OctokitWithPlugins } from "./github.ts";
3
4
  import { type AccountPlan, type CommercialRefusal, type RepoSettings } from "./runContext.ts";
4
5
  export interface RunContextData {
@@ -33,6 +34,8 @@ interface ResolveRunContextDataParams {
33
34
  * because run-context is fetched first and needs it to pick this trigger's
34
35
  * model override. */
35
36
  runType?: string | undefined;
37
+ /** the model router's tier from the payload, forwarded so run-context applies it to the proxy mint. */
38
+ routedTier?: RouterTier | undefined;
36
39
  }
37
40
  /**
38
41
  * initialize run context data: parse context, fetch repo info and settings
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pullfrog",
3
- "version": "0.1.76",
3
+ "version": "0.1.77",
4
4
  "cliContractVersion": "1",
5
5
  "type": "module",
6
6
  "bin": {