pullfrog 0.1.46 → 0.1.48

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.
@@ -2,10 +2,11 @@
2
2
  * Internal entrypoint for the root app.
3
3
  * Re-exports shared types, values, and utilities needed by the Next.js app.
4
4
  */
5
- export type { AuthorPermission, AutoTier, ModelAlias, ModelProvider, Payload, PayloadEvent, ProviderConfig, PushPermission, ShellPermission, ToolPermission, WriteablePayload, XrepoConfig, } from "../external.ts";
6
- export { AUTO_EFFICIENT, AUTO_INTELLIGENT, DEFAULT_PROXY_MODEL, defaultAutoTier, getAutoSelectHintModel, getModelEnvVars, getModelManagedCredentials, getModelProvider, getProviderDisplayName, isAutoTier, isCardGatedModel, modelAliases, parseModel, providers, pullfrogMcpName, resolveAutoTier, resolveCliModel, resolveDisplayAlias, resolveModelSlug, resolveOpenRouterModel, } from "../external.ts";
5
+ export type { AuthorPermission, AutoTier, EffortPosition, ModelAlias, ModelProvider, Payload, PayloadEvent, ProviderConfig, PushPermission, ShellPermission, ToolPermission, WriteablePayload, XrepoConfig, } from "../external.ts";
6
+ export { AUTO_EFFICIENT, AUTO_INTELLIGENT, DEFAULT_EFFORT_POSITION, DEFAULT_PROXY_MODEL, defaultAutoTier, EFFORT_ALIASES, getAutoSelectHintModel, getModelEffortLevels, getModelEnvVars, getModelManagedCredentials, getModelProvider, getProviderDisplayName, isAutoTier, isCardGatedModel, isEffortPosition, modelAliases, offeredRungs, parseEffortPosition, parseModel, providers, pullfrogMcpName, resolveAutoTier, resolveCliModel, resolveDisplayAlias, resolveModelRung, resolveModelSlug, resolveOpenRouterModel, resolveRung, rungLabel, rungPosition, } from "../external.ts";
7
7
  export type { Mode } from "../modes.ts";
8
8
  export { modes } from "../modes.ts";
9
+ export { commercialPaywallBody } from "../utils/billingErrors.ts";
9
10
  export type { BuildPullfrogFooterParams, WorkflowRunFooterInfo, } from "../utils/buildPullfrogFooter.ts";
10
11
  export { buildPullfrogFooter, PULLFROG_DIVIDER, stripExistingFooter, } from "../utils/buildPullfrogFooter.ts";
11
12
  export type { CodexAuthBody } from "../utils/codexOAuth.ts";
@@ -15,4 +16,6 @@ export { isLeapingIntoActionCommentBody, LEAPING_INTO_ACTION_PREFIX, } from "../
15
16
  export { MAX_LEARNINGS_LENGTH, truncateAtLineBoundary } from "../utils/learningsTruncate.ts";
16
17
  export type { CreateProgressCommentTarget, ProgressComment, ProgressCommentType, } from "../utils/progressComment.ts";
17
18
  export { createLeapingProgressComment, deleteProgressCommentApi, getProgressComment, updateProgressComment, } from "../utils/progressComment.ts";
19
+ export type { RunStatusCheckConclusion, RunStatusCheckOctokit, } from "../utils/runStatusCheck.ts";
20
+ export { APPROVAL_CHECK_NAME, createRunStatusCheck, finalizeRunStatusCheck, RUN_STATUS_CHECK_NAME, runStatusCheckNeedsFinalizing, } from "../utils/runStatusCheck.ts";
18
21
  export { isValidTimeString, parseTimeString, TIMEOUT_DISABLED, } from "../utils/time.ts";
package/dist/internal.js CHANGED
@@ -1,5 +1,53 @@
1
1
  import { createRequire as __createRequire } from 'module'; import { fileURLToPath as __fileURLToPath } from 'url'; import { dirname as __dirnameFn } from 'path'; const require = __createRequire(import.meta.url); const __filename = __fileURLToPath(import.meta.url); const __dirname = __dirnameFn(__filename);
2
2
 
3
+ // effort.ts
4
+ var DEFAULT_EFFORT_POSITION = 0.75;
5
+ var EFFORT_ALIASES = {
6
+ low: 0,
7
+ medium: 0.25,
8
+ high: 0.5,
9
+ xhigh: 0.75,
10
+ max: 1
11
+ };
12
+ var DISABLING_RUNGS = ["none", "minimal"];
13
+ function isEffortPosition(value) {
14
+ return Number.isFinite(value) && value >= 0 && value <= 1;
15
+ }
16
+ function parseEffortPosition(raw) {
17
+ const normalized = raw.trim().toLowerCase();
18
+ const named = EFFORT_ALIASES[normalized === "med" ? "medium" : normalized];
19
+ if (named !== void 0) return named;
20
+ const numeric = Number(normalized);
21
+ return normalized !== "" && isEffortPosition(numeric) ? numeric : void 0;
22
+ }
23
+ function offeredRungs(published) {
24
+ return published.filter((rung) => !DISABLING_RUNGS.includes(rung));
25
+ }
26
+ function resolveRung(params) {
27
+ const rungs = offeredRungs(params.published);
28
+ if (rungs.length === 0) return void 0;
29
+ const clamped = Math.min(1, Math.max(0, params.position));
30
+ return rungs[Math.floor(clamped * (rungs.length - 1))];
31
+ }
32
+ function rungPosition(params) {
33
+ const rungs = offeredRungs(params.published);
34
+ const index = rungs.indexOf(params.rung);
35
+ if (index === -1) return void 0;
36
+ return rungs.length > 1 ? index / (rungs.length - 1) : 0;
37
+ }
38
+ function rungLabel(rung) {
39
+ const known = {
40
+ none: "None",
41
+ minimal: "Minimal",
42
+ low: "Low",
43
+ medium: "Medium",
44
+ high: "High",
45
+ xhigh: "Extra high",
46
+ max: "Max"
47
+ };
48
+ return known[rung] ?? rung.charAt(0).toUpperCase() + rung.slice(1);
49
+ }
50
+
3
51
  // models.ts
4
52
  function provider(config) {
5
53
  return config;
@@ -19,6 +67,7 @@ var providers = {
19
67
  "claude-fable": {
20
68
  displayName: "Claude Fable",
21
69
  resolve: "anthropic/claude-fable-5",
70
+ effort: ["low", "medium", "high", "xhigh", "max"],
22
71
  // rolling alias: models.dev's OpenRouter mirror lags brand-new pinned
23
72
  // versions (claude-fable-5 isn't indexed yet), so track ~…-latest to
24
73
  // stay catalog-valid and auto-follow version bumps.
@@ -28,6 +77,7 @@ var providers = {
28
77
  "claude-opus": {
29
78
  displayName: "Claude Opus",
30
79
  resolve: "anthropic/claude-opus-5",
80
+ effort: ["low", "medium", "high", "xhigh", "max"],
31
81
  openRouterResolve: "openrouter/anthropic/claude-opus-5",
32
82
  preferred: true,
33
83
  subagentModel: "claude-sonnet"
@@ -35,6 +85,7 @@ var providers = {
35
85
  "claude-sonnet": {
36
86
  displayName: "Claude Sonnet",
37
87
  resolve: "anthropic/claude-sonnet-5",
88
+ effort: ["low", "medium", "high", "xhigh", "max"],
38
89
  openRouterResolve: "openrouter/anthropic/claude-sonnet-5"
39
90
  },
40
91
  "claude-haiku": {
@@ -52,6 +103,7 @@ var providers = {
52
103
  gpt: {
53
104
  displayName: "GPT Sol",
54
105
  resolve: "openai/gpt-5.6-sol",
106
+ effort: ["none", "low", "medium", "high", "xhigh", "max"],
55
107
  openRouterResolve: "openrouter/openai/gpt-5.6-sol",
56
108
  preferred: true,
57
109
  subagentModel: "gpt-terra"
@@ -63,6 +115,7 @@ var providers = {
63
115
  displayName: "GPT Sol Pro",
64
116
  description: "Maximum reasoning effort",
65
117
  resolve: "openai/gpt-5.6-sol",
118
+ effort: ["none", "low", "medium", "high", "xhigh", "max"],
66
119
  openRouterResolve: "openrouter/openai/gpt-5.6-sol-pro",
67
120
  subagentModel: "gpt"
68
121
  },
@@ -72,11 +125,13 @@ var providers = {
72
125
  "gpt-terra": {
73
126
  displayName: "GPT Terra",
74
127
  resolve: "openai/gpt-5.6-terra",
128
+ effort: ["none", "low", "medium", "high", "xhigh", "max"],
75
129
  openRouterResolve: "openrouter/openai/gpt-5.6-terra"
76
130
  },
77
131
  "gpt-mini": {
78
132
  displayName: "GPT Luna",
79
133
  resolve: "openai/gpt-5.6-luna",
134
+ effort: ["none", "low", "medium", "high", "xhigh", "max"],
80
135
  openRouterResolve: "openrouter/openai/gpt-5.6-luna"
81
136
  },
82
137
  // legacy aliases — openai unified the codex line into the main GPT family
@@ -106,6 +161,7 @@ var providers = {
106
161
  o3: {
107
162
  displayName: "O3",
108
163
  resolve: "openai/o3",
164
+ effort: ["low", "medium", "high"],
109
165
  openRouterResolve: "openrouter/openai/o3"
110
166
  }
111
167
  }
@@ -117,6 +173,7 @@ var providers = {
117
173
  "gemini-pro": {
118
174
  displayName: "Gemini Pro",
119
175
  resolve: "google/gemini-3.1-pro-preview",
176
+ effort: ["low", "medium", "high"],
120
177
  openRouterResolve: "openrouter/google/gemini-3.1-pro-preview",
121
178
  preferred: true
122
179
  // Inherit (subagents stay on Pro). Google has no in-between tier;
@@ -128,6 +185,7 @@ var providers = {
128
185
  "gemini-flash": {
129
186
  displayName: "Gemini Flash",
130
187
  resolve: "google/gemini-3.5-flash",
188
+ effort: ["minimal", "low", "medium", "high"],
131
189
  openRouterResolve: "openrouter/google/gemini-3.5-flash"
132
190
  }
133
191
  }
@@ -139,6 +197,7 @@ var providers = {
139
197
  grok: {
140
198
  displayName: "Grok",
141
199
  resolve: "xai/grok-4.3",
200
+ effort: ["none", "low", "medium", "high"],
142
201
  openRouterResolve: "openrouter/x-ai/grok-4.3",
143
202
  preferred: true
144
203
  },
@@ -168,12 +227,16 @@ var providers = {
168
227
  "deepseek-pro": {
169
228
  displayName: "DeepSeek Pro",
170
229
  resolve: "deepseek/deepseek-v4-pro",
230
+ effort: ["high", "max"],
231
+ openRouterEffort: ["high", "xhigh"],
171
232
  openRouterResolve: "openrouter/deepseek/deepseek-v4-pro",
172
233
  preferred: true
173
234
  },
174
235
  "deepseek-flash": {
175
236
  displayName: "DeepSeek Flash",
176
237
  resolve: "deepseek/deepseek-v4-flash",
238
+ effort: ["high", "max"],
239
+ openRouterEffort: ["high", "xhigh"],
177
240
  openRouterResolve: "openrouter/deepseek/deepseek-v4-flash"
178
241
  },
179
242
  // legacy aliases — deepseek retires these on 2026-07-24; transparently
@@ -202,6 +265,7 @@ var providers = {
202
265
  "kimi-k3": {
203
266
  displayName: "Kimi K3",
204
267
  resolve: "moonshotai/kimi-k3",
268
+ effort: ["low", "high", "max"],
205
269
  openRouterResolve: "openrouter/moonshotai/kimi-k3",
206
270
  preferred: true,
207
271
  subagentModel: "kimi-k2"
@@ -214,7 +278,7 @@ var providers = {
214
278
  }
215
279
  }),
216
280
  opencode: provider({
217
- displayName: "OpenCode",
281
+ displayName: "OpenCode Zen",
218
282
  envVars: ["OPENCODE_API_KEY"],
219
283
  models: {
220
284
  "big-pickle": {
@@ -227,12 +291,14 @@ var providers = {
227
291
  "claude-opus": {
228
292
  displayName: "Claude Opus",
229
293
  resolve: "opencode/claude-opus-5",
294
+ effort: ["low", "medium", "high", "xhigh", "max"],
230
295
  openRouterResolve: "openrouter/anthropic/claude-opus-5",
231
296
  subagentModel: "claude-sonnet"
232
297
  },
233
298
  "claude-sonnet": {
234
299
  displayName: "Claude Sonnet",
235
300
  resolve: "opencode/claude-sonnet-5",
301
+ effort: ["low", "medium", "high", "xhigh", "max"],
236
302
  openRouterResolve: "openrouter/anthropic/claude-sonnet-5"
237
303
  },
238
304
  "claude-haiku": {
@@ -243,6 +309,7 @@ var providers = {
243
309
  gpt: {
244
310
  displayName: "GPT Sol",
245
311
  resolve: "opencode/gpt-5.6-sol",
312
+ effort: ["none", "low", "medium", "high", "xhigh", "max"],
246
313
  openRouterResolve: "openrouter/openai/gpt-5.6-sol",
247
314
  subagentModel: "gpt-terra"
248
315
  },
@@ -251,6 +318,7 @@ var providers = {
251
318
  displayName: "GPT Sol Pro",
252
319
  description: "Maximum reasoning effort",
253
320
  resolve: "opencode/gpt-5.6-sol",
321
+ effort: ["none", "low", "medium", "high", "xhigh", "max"],
254
322
  openRouterResolve: "openrouter/openai/gpt-5.6-sol-pro",
255
323
  subagentModel: "gpt"
256
324
  },
@@ -258,11 +326,13 @@ var providers = {
258
326
  "gpt-terra": {
259
327
  displayName: "GPT Terra",
260
328
  resolve: "opencode/gpt-5.6-terra",
329
+ effort: ["none", "low", "medium", "high", "xhigh", "max"],
261
330
  openRouterResolve: "openrouter/openai/gpt-5.6-terra"
262
331
  },
263
332
  "gpt-mini": {
264
333
  displayName: "GPT Luna",
265
334
  resolve: "opencode/gpt-5.6-luna",
335
+ effort: ["none", "low", "medium", "high", "xhigh", "max"],
266
336
  openRouterResolve: "openrouter/openai/gpt-5.6-luna"
267
337
  },
268
338
  // legacy aliases — see openai provider above for context.
@@ -288,12 +358,14 @@ var providers = {
288
358
  "gemini-pro": {
289
359
  displayName: "Gemini Pro",
290
360
  resolve: "opencode/gemini-3.1-pro",
361
+ effort: ["low", "medium", "high"],
291
362
  openRouterResolve: "openrouter/google/gemini-3.1-pro-preview"
292
363
  // Inherit — see google/gemini-pro for rationale.
293
364
  },
294
365
  "gemini-flash": {
295
366
  displayName: "Gemini Flash",
296
367
  resolve: "opencode/gemini-3.5-flash",
368
+ effort: ["minimal", "low", "medium", "high"],
297
369
  openRouterResolve: "openrouter/google/gemini-3.5-flash"
298
370
  },
299
371
  "kimi-k2": {
@@ -312,6 +384,7 @@ var providers = {
312
384
  "gpt-5-nano": {
313
385
  displayName: "GPT Nano",
314
386
  resolve: "opencode/gpt-5-nano",
387
+ effort: ["minimal", "low", "medium", "high"],
315
388
  openRouterResolve: "openrouter/openai/gpt-5-nano"
316
389
  },
317
390
  "mimo-v2-pro-free": {
@@ -338,6 +411,8 @@ var providers = {
338
411
  "glm-5.1": {
339
412
  displayName: "GLM 5.2",
340
413
  resolve: "opencode-go/glm-5.2",
414
+ effort: ["high", "max"],
415
+ openRouterEffort: ["high", "xhigh"],
341
416
  openRouterResolve: "openrouter/z-ai/glm-5.2",
342
417
  preferred: true
343
418
  },
@@ -391,6 +466,10 @@ var providers = {
391
466
  // bring-your-own generic OpenAI-compatible endpoint — Cloudflare AI Gateway,
392
467
  // Alibaba DashScope, self-hosted vLLM, or any compatible gateway. base URL +
393
468
  // key + model ID are all supplied via env; nothing is cataloged or bumped.
469
+ // the two token limits are deliberately absent: this list is the auth
470
+ // heuristic (`hasPullfrogStoredAuthForModel`, `validateAgentApiKey`), and a
471
+ // stored context number is config, not proof of a key. the console picks
472
+ // them up from `PROVIDER_EXTRA_SECRET_NAMES` instead.
394
473
  envVars: ["OPENAI_COMPATIBLE_BASE_URL", "OPENAI_COMPATIBLE_API_KEY", "OPENAI_COMPATIBLE_MODEL"],
395
474
  models: {
396
475
  // single routing entry — the actual model ID is read from
@@ -410,6 +489,7 @@ var providers = {
410
489
  "claude-opus": {
411
490
  displayName: "Claude Opus",
412
491
  resolve: "openrouter/~anthropic/claude-opus-latest",
492
+ effort: ["low", "medium", "high", "xhigh", "max"],
413
493
  openRouterResolve: "openrouter/~anthropic/claude-opus-latest",
414
494
  preferred: true,
415
495
  subagentModel: "claude-sonnet"
@@ -417,6 +497,7 @@ var providers = {
417
497
  "claude-sonnet": {
418
498
  displayName: "Claude Sonnet",
419
499
  resolve: "openrouter/~anthropic/claude-sonnet-latest",
500
+ effort: ["low", "medium", "high", "xhigh", "max"],
420
501
  openRouterResolve: "openrouter/~anthropic/claude-sonnet-latest"
421
502
  },
422
503
  "claude-haiku": {
@@ -431,6 +512,7 @@ var providers = {
431
512
  gpt: {
432
513
  displayName: "GPT Sol",
433
514
  resolve: "openrouter/openai/gpt-5.6-sol",
515
+ effort: ["none", "low", "medium", "high", "xhigh", "max"],
434
516
  openRouterResolve: "openrouter/openai/gpt-5.6-sol",
435
517
  subagentModel: "gpt-terra"
436
518
  },
@@ -439,6 +521,7 @@ var providers = {
439
521
  displayName: "GPT Sol Pro",
440
522
  description: "Maximum reasoning effort",
441
523
  resolve: "openrouter/openai/gpt-5.6-sol-pro",
524
+ effort: ["none", "low", "medium", "high", "xhigh", "max"],
442
525
  openRouterResolve: "openrouter/openai/gpt-5.6-sol-pro",
443
526
  subagentModel: "gpt"
444
527
  },
@@ -446,11 +529,13 @@ var providers = {
446
529
  "gpt-terra": {
447
530
  displayName: "GPT Terra",
448
531
  resolve: "openrouter/openai/gpt-5.6-terra",
532
+ effort: ["none", "low", "medium", "high", "xhigh", "max"],
449
533
  openRouterResolve: "openrouter/openai/gpt-5.6-terra"
450
534
  },
451
535
  "gpt-mini": {
452
536
  displayName: "GPT Luna",
453
537
  resolve: "openrouter/openai/gpt-5.6-luna",
538
+ effort: ["none", "low", "medium", "high", "xhigh", "max"],
454
539
  openRouterResolve: "openrouter/openai/gpt-5.6-luna"
455
540
  },
456
541
  // legacy aliases — see openai provider for context.
@@ -476,32 +561,38 @@ var providers = {
476
561
  "o4-mini": {
477
562
  displayName: "O4 Mini",
478
563
  resolve: "openrouter/openai/o4-mini",
564
+ effort: ["low", "medium", "high"],
479
565
  openRouterResolve: "openrouter/openai/o4-mini"
480
566
  },
481
567
  "gemini-pro": {
482
568
  displayName: "Gemini Pro",
483
569
  resolve: "openrouter/~google/gemini-pro-latest",
570
+ effort: ["low", "medium", "high"],
484
571
  openRouterResolve: "openrouter/~google/gemini-pro-latest"
485
572
  // Inherit — see google/gemini-pro for rationale.
486
573
  },
487
574
  "gemini-flash": {
488
575
  displayName: "Gemini Flash",
489
576
  resolve: "openrouter/~google/gemini-flash-latest",
577
+ effort: ["minimal", "low", "medium", "high"],
490
578
  openRouterResolve: "openrouter/~google/gemini-flash-latest"
491
579
  },
492
580
  grok: {
493
581
  displayName: "Grok",
494
582
  resolve: "openrouter/x-ai/grok-4.3",
583
+ effort: ["none", "low", "medium", "high"],
495
584
  openRouterResolve: "openrouter/x-ai/grok-4.3"
496
585
  },
497
586
  "deepseek-pro": {
498
587
  displayName: "DeepSeek Pro",
499
588
  resolve: "openrouter/deepseek/deepseek-v4-pro",
589
+ effort: ["high", "xhigh"],
500
590
  openRouterResolve: "openrouter/deepseek/deepseek-v4-pro"
501
591
  },
502
592
  "deepseek-flash": {
503
593
  displayName: "DeepSeek Flash",
504
594
  resolve: "openrouter/deepseek/deepseek-v4-flash",
595
+ effort: ["high", "xhigh"],
505
596
  openRouterResolve: "openrouter/deepseek/deepseek-v4-flash"
506
597
  },
507
598
  // legacy alias — deepseek retires this on 2026-07-24; transparently
@@ -520,6 +611,7 @@ var providers = {
520
611
  "kimi-k3": {
521
612
  displayName: "Kimi K3",
522
613
  resolve: "openrouter/moonshotai/kimi-k3",
614
+ effort: ["low", "high", "max"],
523
615
  openRouterResolve: "openrouter/moonshotai/kimi-k3"
524
616
  },
525
617
  // slug pins the m2 line for DB stability; resolve tracks the current m2.7.
@@ -583,6 +675,8 @@ var modelAliases = Object.entries(providers).flatMap(
583
675
  // here to a fully-qualified slug so callers can look up the target alias
584
676
  // directly without re-deriving the provider.
585
677
  subagentModel: def.subagentModel ? `${providerKey}/${def.subagentModel}` : void 0,
678
+ effort: def.effort,
679
+ openRouterEffort: def.openRouterEffort,
586
680
  hidden: def.hidden ?? false
587
681
  }))
588
682
  );
@@ -628,6 +722,17 @@ function resolveCliModel(slug) {
628
722
  function resolveOpenRouterModel(slug) {
629
723
  return resolveDisplayAlias(slug)?.openRouterResolve;
630
724
  }
725
+ function getModelEffortLevels(params) {
726
+ const alias = resolveDisplayAlias(params.slug);
727
+ if (!alias) return void 0;
728
+ if (params.useOpenRouter) return alias.openRouterEffort ?? alias.effort;
729
+ return alias.effort;
730
+ }
731
+ function resolveModelRung(params) {
732
+ const published = getModelEffortLevels(params);
733
+ if (!published) return void 0;
734
+ return resolveRung({ position: params.position, published });
735
+ }
631
736
  var defaultProxyAlias = resolveDisplayAlias(AUTO_EFFICIENT);
632
737
  if (!defaultProxyAlias?.openRouterResolve) {
633
738
  throw new Error(`DEFAULT_PROXY_MODEL: ${AUTO_EFFICIENT} has no openRouterResolve`);
@@ -1043,6 +1148,27 @@ ${PR_SUMMARY_FORMAT}`
1043
1148
  }
1044
1149
  var modes = computeModes("opencode");
1045
1150
 
1151
+ // utils/billingErrors.ts
1152
+ function commercialPaywallBody(params) {
1153
+ if (params.reason === "commercial") {
1154
+ return [
1155
+ `**Pullfrog needs a confirmed Pro plan for ${params.ownerLogin}, so this run paused.**`,
1156
+ "",
1157
+ "Open Plan and payment to confirm Pro or review the organization's billing status.",
1158
+ "",
1159
+ `[Review plan and payment \u2192](${params.url})`
1160
+ ].join("\n");
1161
+ }
1162
+ params.reason;
1163
+ return [
1164
+ `**Pullfrog paused runs on ${params.ownerLogin}: the Pro renewal failed.**`,
1165
+ "",
1166
+ "Update the card on file to resume runs.",
1167
+ "",
1168
+ `[Update billing \u2192](${params.url})`
1169
+ ].join("\n");
1170
+ }
1171
+
1046
1172
  // utils/buildPullfrogFooter.ts
1047
1173
  var PULLFROG_DIVIDER = "<!-- PULLFROG_DIVIDER_DO_NOT_REMOVE_PLZ -->";
1048
1174
  var FROG_LOGO = `<a href="https://pullfrog.com"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pullfrog.com/logos/frog-white-full-18px.png"><img src="https://pullfrog.com/logos/frog-green-full-18px.png" width="9px" height="9px" style="vertical-align: middle; " alt="Pullfrog"></picture></a>`;
@@ -1329,6 +1455,113 @@ async function createLeapingProgressComment(ctx, target, body) {
1329
1455
  };
1330
1456
  }
1331
1457
 
1458
+ // utils/runStatusCheck.ts
1459
+ var RUN_STATUS_CHECK_NAME = "Pullfrog";
1460
+ var APPROVAL_CHECK_NAME = "Pullfrog approval";
1461
+ var IN_PROGRESS_OUTPUT = {
1462
+ title: "Pullfrog is running",
1463
+ summary: "Pullfrog is working on this pull request. This check updates when the run finishes."
1464
+ };
1465
+ function disableCheckLine(owner, repo) {
1466
+ const url = `https://pullfrog.com/console/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}#auto-review-prs`;
1467
+ return `
1468
+
1469
+ [Turn off this check \u2192](${url}) \u2014 it reports run status only and gates nothing unless you required it in branch protection.`;
1470
+ }
1471
+ var TERMINAL_OUTPUT = {
1472
+ success: {
1473
+ title: "Pullfrog run completed",
1474
+ summary: "The Pullfrog run finished successfully."
1475
+ },
1476
+ failure: {
1477
+ title: "Pullfrog run failed",
1478
+ summary: "The Pullfrog run failed. See the run logs for details."
1479
+ },
1480
+ cancelled: {
1481
+ title: "Pullfrog run cancelled",
1482
+ summary: "The Pullfrog run was cancelled before it finished."
1483
+ },
1484
+ timed_out: {
1485
+ title: "Pullfrog run timed out",
1486
+ summary: "The Pullfrog run exceeded its timeout. See the run logs for details."
1487
+ },
1488
+ action_required: {
1489
+ title: "Pullfrog run needs attention",
1490
+ summary: "The Pullfrog run stopped and needs attention. See the run logs for details."
1491
+ },
1492
+ neutral: {
1493
+ title: "Pullfrog run finished",
1494
+ summary: "The Pullfrog run finished without a pass or fail outcome."
1495
+ },
1496
+ skipped: {
1497
+ title: "Pullfrog run skipped",
1498
+ summary: "This run was superseded by another Pullfrog run."
1499
+ },
1500
+ stale: {
1501
+ title: "Pullfrog run didn't finish",
1502
+ summary: "Pullfrog never received a completion signal for this run. See the run logs for details."
1503
+ }
1504
+ };
1505
+ function terminalOutput(params) {
1506
+ const base = TERMINAL_OUTPUT[params.conclusion];
1507
+ const review = params.reviewUrl ? `
1508
+
1509
+ [View the review Pullfrog posted \u2192](${params.reviewUrl})` : "";
1510
+ const disable = params.conclusion === "success" ? "" : disableCheckLine(params.owner, params.repo);
1511
+ return { title: base.title, summary: base.summary + review + disable };
1512
+ }
1513
+ async function createRunStatusCheck(params) {
1514
+ const existing = await params.octokit.rest.checks.listForRef({
1515
+ owner: params.owner,
1516
+ repo: params.repo,
1517
+ ref: params.headSha,
1518
+ check_name: RUN_STATUS_CHECK_NAME,
1519
+ status: "in_progress"
1520
+ }).catch(() => void 0);
1521
+ const reusable = existing?.data.check_runs[0];
1522
+ if (reusable) return reusable.id;
1523
+ const createParams = {
1524
+ owner: params.owner,
1525
+ repo: params.repo,
1526
+ name: RUN_STATUS_CHECK_NAME,
1527
+ head_sha: params.headSha,
1528
+ status: "in_progress",
1529
+ output: IN_PROGRESS_OUTPUT
1530
+ };
1531
+ if (params.detailsUrl) createParams.details_url = params.detailsUrl;
1532
+ const created = await params.octokit.rest.checks.create(createParams);
1533
+ return created.data.id;
1534
+ }
1535
+ async function finalizeRunStatusCheck(params) {
1536
+ const updateParams = {
1537
+ owner: params.owner,
1538
+ repo: params.repo,
1539
+ check_run_id: params.checkRunId,
1540
+ status: "completed",
1541
+ conclusion: params.conclusion,
1542
+ output: terminalOutput({
1543
+ conclusion: params.conclusion,
1544
+ owner: params.owner,
1545
+ repo: params.repo,
1546
+ reviewUrl: params.reviewUrl
1547
+ })
1548
+ };
1549
+ if (params.detailsUrl) updateParams.details_url = params.detailsUrl;
1550
+ await params.octokit.rest.checks.update(updateParams);
1551
+ }
1552
+ async function runStatusCheckNeedsFinalizing(params) {
1553
+ try {
1554
+ const existing = await params.octokit.rest.checks.get({
1555
+ owner: params.owner,
1556
+ repo: params.repo,
1557
+ check_run_id: params.checkRunId
1558
+ });
1559
+ return existing.data.status !== "completed";
1560
+ } catch {
1561
+ return false;
1562
+ }
1563
+ }
1564
+
1332
1565
  // utils/time.ts
1333
1566
  var TIMEOUT_DISABLED = "none";
1334
1567
  var TIME_STRING_REGEX = /^(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$/;
@@ -1344,20 +1577,28 @@ function isValidTimeString(input) {
1344
1577
  return parseTimeString(input) !== null;
1345
1578
  }
1346
1579
  export {
1580
+ APPROVAL_CHECK_NAME,
1347
1581
  AUTO_EFFICIENT,
1348
1582
  AUTO_INTELLIGENT,
1583
+ DEFAULT_EFFORT_POSITION,
1349
1584
  DEFAULT_PROXY_MODEL,
1585
+ EFFORT_ALIASES,
1350
1586
  LEAPING_INTO_ACTION_PREFIX,
1351
1587
  MAX_LEARNINGS_LENGTH,
1352
1588
  OAuthInvalidGrantError,
1353
1589
  PULLFROG_DIVIDER,
1590
+ RUN_STATUS_CHECK_NAME,
1354
1591
  TIMEOUT_DISABLED,
1355
1592
  buildPullfrogFooter,
1593
+ commercialPaywallBody,
1356
1594
  createLeapingProgressComment,
1595
+ createRunStatusCheck,
1357
1596
  decodeJwtExpMs,
1358
1597
  defaultAutoTier,
1359
1598
  deleteProgressCommentApi,
1599
+ finalizeRunStatusCheck,
1360
1600
  getAutoSelectHintModel,
1601
+ getModelEffortLevels,
1361
1602
  getModelEnvVars,
1362
1603
  getModelManagedCredentials,
1363
1604
  getModelProvider,
@@ -1365,11 +1606,14 @@ export {
1365
1606
  getProviderDisplayName,
1366
1607
  isAutoTier,
1367
1608
  isCardGatedModel,
1609
+ isEffortPosition,
1368
1610
  isLeapingIntoActionCommentBody,
1369
1611
  isValidTimeString,
1370
1612
  modelAliases,
1371
1613
  modes,
1614
+ offeredRungs,
1372
1615
  parseCodexAuthBody,
1616
+ parseEffortPosition,
1373
1617
  parseModel,
1374
1618
  parseTimeString,
1375
1619
  providers,
@@ -1378,8 +1622,13 @@ export {
1378
1622
  resolveAutoTier,
1379
1623
  resolveCliModel,
1380
1624
  resolveDisplayAlias,
1625
+ resolveModelRung,
1381
1626
  resolveModelSlug,
1382
1627
  resolveOpenRouterModel,
1628
+ resolveRung,
1629
+ runStatusCheckNeedsFinalizing,
1630
+ rungLabel,
1631
+ rungPosition,
1383
1632
  stringifyCodexAuthBody,
1384
1633
  stripExistingFooter,
1385
1634
  truncateAtLineBoundary,
package/dist/models.d.ts CHANGED
@@ -4,6 +4,7 @@
4
4
  * slugs use the format `provider/model-id` (e.g. "anthropic/claude-opus").
5
5
  * bump `resolve` when a new model generation ships — the alias (slug) stays stable.
6
6
  */
7
+ import { type EffortPosition } from "./effort.ts";
7
8
  /**
8
9
  * routing discriminant for entries whose `resolve` is dynamic — looked up
9
10
  * from a separate env var at run time rather than fixed in the catalog.
@@ -50,6 +51,14 @@ export interface ModelAlias {
50
51
  /** alias key (within same provider) of the cheaper sibling reviewfrog should
51
52
  * use as its lens-fanout subagent. e.g. claude-opus → "claude-sonnet". */
52
53
  subagentModel: string | undefined;
54
+ /** reasoning-effort rungs this model accepts on the direct route, ascending.
55
+ * mirrors models.dev `reasoning_options[type=effort].values`. undefined means
56
+ * the model has no effort control and the setting is a documented no-op. */
57
+ effort: readonly string[] | undefined;
58
+ /** effort rungs on the OpenRouter route, when they differ from `effort` (the
59
+ * DeepSeek/GLM top rung is `max` natively but `xhigh` via OpenRouter).
60
+ * undefined falls back to `effort`. */
61
+ openRouterEffort: readonly string[] | undefined;
53
62
  /** hide from selectable lists (UI dropdowns, CLI pickers). does NOT affect
54
63
  * resolution — for that use `fallback`. used to keep a redundant alias out of
55
64
  * pickers (e.g. the free `minimax-m2.5-free` duplicate). */
@@ -75,6 +84,10 @@ interface ModelDef {
75
84
  /** alias key (within same provider) of the cheaper sibling reviewfrog should
76
85
  * use as its lens-fanout subagent (e.g. claude-opus → "claude-sonnet"). */
77
86
  subagentModel?: string;
87
+ /** effort rungs accepted on the direct route, ascending — see ModelAlias.effort */
88
+ effort?: readonly string[];
89
+ /** effort rungs on the OpenRouter route when they differ — see ModelAlias.openRouterEffort */
90
+ openRouterEffort?: readonly string[];
78
91
  /** hide from selectable lists. does NOT affect resolution; for that use `fallback`. */
79
92
  hidden?: boolean;
80
93
  }
@@ -123,8 +136,8 @@ export declare const modelAliases: ModelAlias[];
123
136
  * (CLI resolve, OpenRouter resolve, footer label) handles them transparently.
124
137
  *
125
138
  * `efficient` mirrors the OSS/default subsidy model (DeepSeek V4 Pro — beats
126
- * Kimi K2.7 on agentic-review quality at ~4-5x lower cost, run at high
127
- * reasoning effort via `deepseekHighEffortOverrides`); `intelligent` is the
139
+ * Kimi K2.7 on agentic-review quality at ~4-5x lower cost, run at whatever
140
+ * effort the repo asks for, clamped to its published ladder); `intelligent` is the
128
141
  * frontier pick (Claude Opus). a `null` model means the tier hasn't been
129
142
  * pinned: callers default by card status via `defaultAutoTier`.
130
143
  */
@@ -190,6 +203,27 @@ export declare function resolveCliModel(slug: string): string | undefined;
190
203
  * (e.g. free opencode models).
191
204
  */
192
205
  export declare function resolveOpenRouterModel(slug: string): string | undefined;
206
+ /**
207
+ * the effort rungs `slug` accepts on the route being used, or undefined when the
208
+ * model has no effort control at all. walks the fallback chain, so a deprecated
209
+ * slug is judged by the model that actually runs. drives both the console's
210
+ * level list and the runtime clamp, so the two can't disagree.
211
+ */
212
+ export declare function getModelEffortLevels(params: {
213
+ slug: string;
214
+ useOpenRouter: boolean;
215
+ }): readonly string[] | undefined;
216
+ /**
217
+ * the rung a run actually sends: a position landed on this model's own published
218
+ * ladder. the result is always a rung the model offers, so there is no arithmetic
219
+ * anywhere that can produce a level it doesn't have. undefined means the model
220
+ * publishes no rungs and the harness gets no flag.
221
+ */
222
+ export declare function resolveModelRung(params: {
223
+ slug: string;
224
+ position: EffortPosition;
225
+ useOpenRouter: boolean;
226
+ }): string | undefined;
193
227
  export declare const DEFAULT_PROXY_MODEL: string;
194
228
  /** short label for the model auto-select picks today (console hint copy). */
195
229
  export declare function getAutoSelectHintModel(): string;
@@ -104,6 +104,7 @@ export interface ToolState {
104
104
  approval?: {
105
105
  wouldApprove: boolean;
106
106
  sha: string | undefined;
107
+ url?: string | undefined;
107
108
  };
108
109
  reviewReplies?: Map<number, {
109
110
  commentId: number;