pullfrog 0.1.67 → 0.1.69
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/README.md +72 -150
- package/dist/cli.mjs +236 -77
- package/dist/effort.d.ts +10 -0
- package/dist/external.d.ts +18 -1
- package/dist/index.js +234 -75
- package/dist/internal/index.d.ts +1 -1
- package/dist/internal.js +91 -8
- package/dist/mcp/reviewComments.d.ts +42 -0
- package/dist/toolState.d.ts +1 -1
- package/dist/utils/apiKeys.d.ts +19 -0
- package/dist/utils/billingErrors.d.ts +1 -2
- package/dist/utils/buildPullfrogFooter.d.ts +4 -1
- package/dist/utils/payload.d.ts +1 -0
- package/dist/utils/providerErrors.d.ts +8 -0
- package/dist/utils/proxy.d.ts +24 -0
- package/dist/utils/runContext.d.ts +7 -0
- package/dist/utils/runContextData.d.ts +9 -0
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -110208,6 +110208,7 @@ import { performance as performance5 } from "node:perf_hooks";
|
|
|
110208
110208
|
|
|
110209
110209
|
// effort.ts
|
|
110210
110210
|
var DEFAULT_EFFORT_POSITION = 0.5;
|
|
110211
|
+
var SUBSIDY_RUNG = "high";
|
|
110211
110212
|
var EFFORT_ALIASES = {
|
|
110212
110213
|
low: 0,
|
|
110213
110214
|
medium: 0.25,
|
|
@@ -110611,6 +110612,15 @@ var providers = {
|
|
|
110611
110612
|
// the model does cost nothing.
|
|
110612
110613
|
isFree: true
|
|
110613
110614
|
},
|
|
110615
|
+
// Zen meters Fable like any other model, so this route reaches it without
|
|
110616
|
+
// the Anthropic access grant `anthropic/claude-fable` still needs.
|
|
110617
|
+
"claude-fable": {
|
|
110618
|
+
displayName: "Claude Fable",
|
|
110619
|
+
resolve: "opencode/claude-fable-5-1",
|
|
110620
|
+
effort: ["low", "medium", "high", "xhigh", "max"],
|
|
110621
|
+
openRouterResolve: "openrouter/~anthropic/claude-fable-latest",
|
|
110622
|
+
subagentModel: "claude-sonnet"
|
|
110623
|
+
},
|
|
110614
110624
|
"claude-opus": {
|
|
110615
110625
|
displayName: "Claude Opus",
|
|
110616
110626
|
resolve: "opencode/claude-opus-5",
|
|
@@ -110724,6 +110734,17 @@ var providers = {
|
|
|
110724
110734
|
effort: ["minimal", "low", "medium", "high"],
|
|
110725
110735
|
openRouterResolve: "openrouter/google/gemini-3.6-flash"
|
|
110726
110736
|
},
|
|
110737
|
+
// Zen serves K3, but rule 7 can only move a mirror when its upstream moves,
|
|
110738
|
+
// so the K3 generation could never reach this block by bumping `kimi-k2`.
|
|
110739
|
+
"kimi-k3": {
|
|
110740
|
+
displayName: "Kimi K3",
|
|
110741
|
+
resolve: "opencode/kimi-k3",
|
|
110742
|
+
// Zen publishes a single rung for K3 where OpenRouter publishes three.
|
|
110743
|
+
effort: ["max"],
|
|
110744
|
+
openRouterEffort: ["low", "high", "max"],
|
|
110745
|
+
openRouterResolve: "openrouter/moonshotai/kimi-k3",
|
|
110746
|
+
subagentModel: "kimi-k2"
|
|
110747
|
+
},
|
|
110727
110748
|
"kimi-k2": {
|
|
110728
110749
|
displayName: "Kimi K2",
|
|
110729
110750
|
// k2.7-code is UNDEPLOYED on Zen — still listed in /zen/v1/models, but
|
|
@@ -110735,25 +110756,57 @@ var providers = {
|
|
|
110735
110756
|
resolve: "opencode/kimi-k2.6",
|
|
110736
110757
|
openRouterResolve: "openrouter/moonshotai/kimi-k2.7-code"
|
|
110737
110758
|
},
|
|
110759
|
+
// M3 is its own line beside M2, not a bump of it — same reason `kimi-k3`
|
|
110760
|
+
// sits beside `kimi-k2` rather than replacing it.
|
|
110761
|
+
"minimax-m3": {
|
|
110762
|
+
displayName: "MiniMax M3",
|
|
110763
|
+
resolve: "opencode/minimax-m3",
|
|
110764
|
+
openRouterResolve: "openrouter/minimax/minimax-m3"
|
|
110765
|
+
},
|
|
110738
110766
|
// slug pins the m2 line for DB stability; resolve tracks the current m2.7.
|
|
110739
110767
|
"minimax-m2.5": {
|
|
110740
110768
|
displayName: "MiniMax M2",
|
|
110741
110769
|
resolve: "opencode/minimax-m2.7",
|
|
110742
110770
|
openRouterResolve: "openrouter/minimax/minimax-m2.7"
|
|
110743
110771
|
},
|
|
110772
|
+
// Z.ai and xAI reach Zen subscribers only here — before this, `opencode-go`
|
|
110773
|
+
// was the catalog's only GLM route, and Grok had no Zen route at all.
|
|
110774
|
+
glm: {
|
|
110775
|
+
displayName: "GLM",
|
|
110776
|
+
resolve: "opencode/glm-5.3",
|
|
110777
|
+
effort: ["low", "high", "max"],
|
|
110778
|
+
openRouterResolve: "openrouter/z-ai/glm-5.3"
|
|
110779
|
+
},
|
|
110780
|
+
grok: {
|
|
110781
|
+
displayName: "Grok",
|
|
110782
|
+
resolve: "opencode/grok-4.6",
|
|
110783
|
+
effort: ["low", "medium", "high", "xhigh"],
|
|
110784
|
+
openRouterResolve: "openrouter/x-ai/grok-4.6"
|
|
110785
|
+
},
|
|
110744
110786
|
"gpt-5-nano": {
|
|
110745
110787
|
displayName: "GPT Nano",
|
|
110746
110788
|
resolve: "opencode/gpt-5-nano",
|
|
110747
110789
|
effort: ["minimal", "low", "medium", "high"],
|
|
110748
110790
|
openRouterResolve: "openrouter/openai/gpt-5-nano"
|
|
110749
110791
|
},
|
|
110792
|
+
// Zen's live free MiMo, and the second free row in a menu that big-pickle
|
|
110793
|
+
// was alone in since `mimo-v2-pro-free` lost its model.
|
|
110794
|
+
mimo: {
|
|
110795
|
+
displayName: "MiMo",
|
|
110796
|
+
resolve: "opencode/mimo-v2.5-free",
|
|
110797
|
+
// free to run, still gated on the provider's own OPENCODE_API_KEY —
|
|
110798
|
+
// see the big-pickle note above (#1077).
|
|
110799
|
+
isFree: true
|
|
110800
|
+
},
|
|
110750
110801
|
"mimo-v2-pro-free": {
|
|
110751
110802
|
displayName: "MiMo V2 Pro",
|
|
110752
110803
|
resolve: "opencode/mimo-v2-pro-free",
|
|
110753
110804
|
// free to run, still gated on the provider's own OPENCODE_API_KEY —
|
|
110754
110805
|
// see the big-pickle note above (#1077).
|
|
110755
110806
|
isFree: true,
|
|
110756
|
-
|
|
110807
|
+
// the model id Zen dropped; land a stored MiMo pick back on MiMo rather
|
|
110808
|
+
// than on the unrelated model it had to settle for while none was live.
|
|
110809
|
+
fallback: "opencode/mimo"
|
|
110757
110810
|
},
|
|
110758
110811
|
"minimax-m2.5-free": {
|
|
110759
110812
|
displayName: "MiniMax M2",
|
|
@@ -110769,10 +110822,11 @@ var providers = {
|
|
|
110769
110822
|
// OpenCode Go is a separate $10/mo subscription from Zen, served on its own
|
|
110770
110823
|
// base URL (`https://opencode.ai/zen/go/v1`) but authenticated with the SAME
|
|
110771
110824
|
// `OPENCODE_API_KEY`. it carries the open-weight coding models plus a couple
|
|
110772
|
-
// of frontier ones,
|
|
110773
|
-
//
|
|
110774
|
-
//
|
|
110775
|
-
//
|
|
110825
|
+
// of frontier ones, six of which are served ONLY here — Zen's `/v1/models`
|
|
110826
|
+
// does not list qwen3.7/3.8-*, mimo-v2.5-pro, longcat-2.0 or hy3. (it listed
|
|
110827
|
+
// no glm-5.3* either until 2026-09, which is why `opencode/glm` trailed on
|
|
110828
|
+
// 5.2.) so for a Go subscriber this provider is not a duplicate route to Zen,
|
|
110829
|
+
// it is the only route to a large part of what they pay for.
|
|
110776
110830
|
// like `opencode` and `openrouter` this is a ROUTER, not a vendor: slugs and
|
|
110777
110831
|
// display names mirror the upstream brand tier, and the picker groups them
|
|
110778
110832
|
// under the upstream vendor.
|
|
@@ -110841,13 +110895,25 @@ var providers = {
|
|
|
110841
110895
|
// `high` the Go route does not.
|
|
110842
110896
|
effort: ["low", "medium", "xhigh"],
|
|
110843
110897
|
openRouterEffort: ["minimal", "low", "medium", "high", "xhigh"],
|
|
110844
|
-
|
|
110898
|
+
// OpenRouter retired the rolling `qwen3.8-max` for a dated snapshot, so
|
|
110899
|
+
// this side has to pin one; the Go route still carries the rolling id.
|
|
110900
|
+
openRouterResolve: "openrouter/qwen/qwen3.8-max-0902"
|
|
110845
110901
|
},
|
|
110846
110902
|
"qwen-plus": {
|
|
110847
110903
|
displayName: "Qwen Plus",
|
|
110848
110904
|
resolve: "opencode-go/qwen3.7-plus",
|
|
110849
110905
|
openRouterResolve: "openrouter/qwen/qwen3.7-plus"
|
|
110850
110906
|
},
|
|
110907
|
+
// the cheap rung Alibaba added under Plus (0.15/0.47 against 0.5/3), and
|
|
110908
|
+
// the only model either OpenCode plan has added since this block was written.
|
|
110909
|
+
"qwen-flash": {
|
|
110910
|
+
displayName: "Qwen Flash",
|
|
110911
|
+
resolve: "opencode-go/qwen3.8-flash",
|
|
110912
|
+
effort: ["low", "medium", "xhigh"],
|
|
110913
|
+
// the Go route publishes rungs where OpenRouter publishes none.
|
|
110914
|
+
openRouterEffort: [],
|
|
110915
|
+
openRouterResolve: "openrouter/qwen/qwen3.8-flash"
|
|
110916
|
+
},
|
|
110851
110917
|
// MiniMax — parity with opencode/* and openrouter/*; the m2 slug pins the
|
|
110852
110918
|
// line for DB stability while the resolve tracks the current m2.7.
|
|
110853
110919
|
"minimax-m3": {
|
|
@@ -111972,6 +112038,7 @@ async function installFromNpmTarball(params) {
|
|
|
111972
112038
|
// utils/providerErrors.ts
|
|
111973
112039
|
var PROVIDER_BILLING_EXHAUSTED_LABEL = "provider billing exhausted";
|
|
111974
112040
|
var PROVIDER_NO_ENDPOINTS_LABEL = "provider no routable endpoints";
|
|
112041
|
+
var PROVIDER_USAGE_LIMIT_LABEL = "provider usage limit reached";
|
|
111975
112042
|
var statusKey = `\\b(?:status[_ ]?code|http[_ ]?status|status)["']?\\s*[:=]\\s*["']?`;
|
|
111976
112043
|
var ANTHROPIC_SPEND_CAP_PATTERN = /reached your specified API usage limits\.?(?:\s*You will regain access on ([^"\n]+?)\.)?/i;
|
|
111977
112044
|
function findAnthropicSpendCap(text) {
|
|
@@ -112017,6 +112084,14 @@ var PROVIDER_ERROR_PATTERNS = [
|
|
|
112017
112084
|
// the wallet-shaped patterns above reach, and its 400 sits nowhere near a
|
|
112018
112085
|
// `status:` key either — so the whole message was dropped on the floor (#1208).
|
|
112019
112086
|
{ regex: ANTHROPIC_SPEND_CAP_PATTERN, label: PROVIDER_BILLING_EXHAUSTED_LABEL },
|
|
112087
|
+
// OpenCode Zen / Codex-subscription cap: `AI_APICallError: The usage limit has
|
|
112088
|
+
// been reached`. Its noun is "usage limit" with no "rate", no status code and
|
|
112089
|
+
// no wallet wording, so every pattern here missed it and `lastProviderError`
|
|
112090
|
+
// stayed unset — which suppresses nothing, so the run was reported as "The
|
|
112091
|
+
// model produced no output at all … usually transient; re-running often
|
|
112092
|
+
// succeeds" while the same log held nine of these. Measured on a live account
|
|
112093
|
+
// whose ChatGPT subscription was capped.
|
|
112094
|
+
{ regex: /\bthe usage limit has been reached\b/i, label: PROVIDER_USAGE_LIMIT_LABEL },
|
|
112020
112095
|
// the OpenRouter shapes `isRouterKeylimitExhaustedError` matches. on a Router
|
|
112021
112096
|
// run those mean the PULLFROG wallet is empty and `renderRunError` returns a
|
|
112022
112097
|
// `BillingError` before ever reaching this list; on a BYOK run the very same
|
|
@@ -112147,7 +112222,7 @@ function ossEffortFloor(ctx) {
|
|
|
112147
112222
|
const alias = resolveRunAlias(ctx);
|
|
112148
112223
|
const published = alias?.openRouterEffort ?? alias?.effort;
|
|
112149
112224
|
if (!published) return 0;
|
|
112150
|
-
return rungPosition({ rung:
|
|
112225
|
+
return rungPosition({ rung: SUBSIDY_RUNG, published }) ?? 0;
|
|
112151
112226
|
}
|
|
112152
112227
|
function resolveRunEffort(ctx) {
|
|
112153
112228
|
const configured = ctx.payload.effort !== void 0;
|
|
@@ -112179,7 +112254,7 @@ var import_semver = __toESM(require_semver2(), 1);
|
|
|
112179
112254
|
// package.json
|
|
112180
112255
|
var package_default = {
|
|
112181
112256
|
name: "pullfrog",
|
|
112182
|
-
version: "0.1.
|
|
112257
|
+
version: "0.1.69",
|
|
112183
112258
|
type: "module",
|
|
112184
112259
|
bin: {
|
|
112185
112260
|
pullfrog: "dist/cli.mjs",
|
|
@@ -112986,6 +113061,7 @@ For simple, well-defined tasks, skip the plan phase and go straight to build.`
|
|
|
112986
113061
|
- record what was done (or why nothing was done)
|
|
112987
113062
|
|
|
112988
113063
|
5. Quality check:
|
|
113064
|
+
- re-call \`${t2("get_review_comments")}\` with \`fresh: true\` before committing: reviewers keep commenting while you work, and any thread it now returns that you have not addressed is in scope for this run.
|
|
112989
113065
|
- test changes, then review the diff before committing \u2014 verify only intended changes are present, no debug artifacts remain, no fix turned out to be bloat in context (revert any that did), and the changes are clean enough that a senior engineer would approve without hesitation
|
|
112990
113066
|
- ${commitStep}
|
|
112991
113067
|
|
|
@@ -112995,7 +113071,7 @@ For simple, well-defined tasks, skip the plan phase and go straight to build.`
|
|
|
112995
113071
|
- **once the fix is live on the remote**, for each thread you acted on:
|
|
112996
113072
|
- reply ONCE via \`${t2("reply_to_review_comment")}\`. The \`comment_id\` parameter takes the root comment's numeric \`id=\` (from the first \`comment author=...\` tag in the \`${t2("get_review_comments")}\` output) \u2014 NOT the \`thread=\` value; that's a separate GraphQL ID used by resolve. The runtime dedupes identical bodies within a session.
|
|
112997
113073
|
- **immediately** call \`${t2("resolve_review_thread")}\` with that thread's \`thread=\` value as \`thread_id\`. Resolve every thread where you (a) made the requested code change in full \u2014 partial fixes leave the thread open \u2014 OR (b) replied with a substantive answer the user explicitly asked for. Do NOT resolve threads where you pushed back on the request and the disagreement is unresolved; leave those open for the human to mediate.
|
|
112998
|
-
- call \`${t2("report_progress")}\` with a brief summary
|
|
113074
|
+
- call \`${t2("report_progress")}\` with a brief summary. If your last \`${t2("get_review_comments")}\` turned up threads you did not address, name them there \u2014 a review arriving this late is suppressed as a duplicate and nothing else will pick it up.`
|
|
112999
113075
|
},
|
|
113000
113076
|
// Review and IncrementalReview route the minimum reviewfrog specialists
|
|
113001
113077
|
// needed to cover unresolved, disposition-changing hypotheses. Most runs
|
|
@@ -194790,6 +194866,9 @@ function formatModelLabel(params) {
|
|
|
194790
194866
|
if (params.fallbackFrom) {
|
|
194791
194867
|
return `${base} (credentials for ${providerDisplayName(params.fallbackFrom)} were rejected by the provider)`;
|
|
194792
194868
|
}
|
|
194869
|
+
if (params.clamped?.reason === "trial") {
|
|
194870
|
+
return `${base} (free trial)`;
|
|
194871
|
+
}
|
|
194793
194872
|
if (params.clamped) {
|
|
194794
194873
|
const target = isAutoTier(params.clamped.from) ? "the intelligent tier" : `\`${resolveDisplayAlias(params.clamped.from)?.displayName ?? params.clamped.from}\``;
|
|
194795
194874
|
return params.clamped.reason === "card" ? `${base} (${target} needs a [card on file](https://docs.pullfrog.com/models))` : `${base} (${target} needs a [provider key](https://docs.pullfrog.com/models) \u2014 no Router support yet)`;
|
|
@@ -194799,6 +194878,15 @@ function formatModelLabel(params) {
|
|
|
194799
194878
|
}
|
|
194800
194879
|
return base;
|
|
194801
194880
|
}
|
|
194881
|
+
function buildTrialDisclosure(owner) {
|
|
194882
|
+
const consoleUrl2 = owner ? `https://pullfrog.com/console/${owner}` : "https://pullfrog.com/console";
|
|
194883
|
+
return [
|
|
194884
|
+
"---",
|
|
194885
|
+
"",
|
|
194886
|
+
"> [!IMPORTANT]",
|
|
194887
|
+
`> **This run used the free trial model.** \`DeepSeek Flash\` is fast and cheap \u2014 expect lighter work than a frontier model. [Connect a subscription or API key \u2192](${consoleUrl2})`
|
|
194888
|
+
].join("\n");
|
|
194889
|
+
}
|
|
194802
194890
|
function buildPullfrogFooter(params) {
|
|
194803
194891
|
const parts = [];
|
|
194804
194892
|
if (params.shaPinned) {
|
|
@@ -194831,10 +194919,13 @@ function buildPullfrogFooter(params) {
|
|
|
194831
194919
|
);
|
|
194832
194920
|
}
|
|
194833
194921
|
const allParts = [...parts, "[\u{1D54F}](https://x.com/pullfrogai)"];
|
|
194922
|
+
const disclosure = params.clamped?.reason === "trial" ? `${buildTrialDisclosure(params.owner)}
|
|
194923
|
+
|
|
194924
|
+
` : "";
|
|
194834
194925
|
return `
|
|
194835
194926
|
|
|
194836
194927
|
${PULLFROG_DIVIDER}
|
|
194837
|
-
<sup>${FROG_LOGO} \uFF5C ${allParts.join(" \uFF5C ")}</sup>`;
|
|
194928
|
+
${disclosure}<sup>${FROG_LOGO} \uFF5C ${allParts.join(" \uFF5C ")}</sup>`;
|
|
194838
194929
|
}
|
|
194839
194930
|
function stripExistingFooter(body) {
|
|
194840
194931
|
const dividerIndex = body.indexOf(PULLFROG_DIVIDER);
|
|
@@ -194988,7 +195079,8 @@ function buildCommentFooter(ctx, customParts) {
|
|
|
194988
195079
|
clamped: ctx.toolState.modelClamped,
|
|
194989
195080
|
unselectedProxyDefault: ctx.toolState.unselectedProxyDefault,
|
|
194990
195081
|
shaPinned: ctx.toolState.shaPinned,
|
|
194991
|
-
oss: ctx.oss
|
|
195082
|
+
oss: ctx.oss,
|
|
195083
|
+
owner: ctx.repo.owner
|
|
194992
195084
|
});
|
|
194993
195085
|
}
|
|
194994
195086
|
function buildImplementPlanLink(ctx, issueNumber, commentId) {
|
|
@@ -195987,7 +196079,8 @@ async function createAndSubmitWithFooter(ctx, params, opts) {
|
|
|
195987
196079
|
clamped: ctx.toolState.modelClamped,
|
|
195988
196080
|
unselectedProxyDefault: ctx.toolState.unselectedProxyDefault,
|
|
195989
196081
|
shaPinned: ctx.toolState.shaPinned,
|
|
195990
|
-
oss: ctx.oss
|
|
196082
|
+
oss: ctx.oss,
|
|
196083
|
+
owner: ctx.repo.owner
|
|
195991
196084
|
});
|
|
195992
196085
|
return await ctx.octokit.rest.pulls.submitReview({
|
|
195993
196086
|
owner: params.owner,
|
|
@@ -198748,7 +198841,8 @@ function buildPrBodyWithFooter(ctx, body) {
|
|
|
198748
198841
|
clamped: ctx.toolState.modelClamped,
|
|
198749
198842
|
unselectedProxyDefault: ctx.toolState.unselectedProxyDefault,
|
|
198750
198843
|
shaPinned: ctx.toolState.shaPinned,
|
|
198751
|
-
oss: ctx.oss
|
|
198844
|
+
oss: ctx.oss,
|
|
198845
|
+
owner: ctx.repo.owner
|
|
198752
198846
|
});
|
|
198753
198847
|
const bodyWithoutFooter = stripExistingFooter(fixDoubleEscapedString(body));
|
|
198754
198848
|
return `${bodyWithoutFooter}${footer}`;
|
|
@@ -199083,7 +199177,10 @@ function extractFromFilePatches(hunks, startLine, endLine, side) {
|
|
|
199083
199177
|
}
|
|
199084
199178
|
var GetReviewComments = type({
|
|
199085
199179
|
pull_number: type.number.describe("The pull request number"),
|
|
199086
|
-
review_id: type.number.describe("The review ID to get comments for")
|
|
199180
|
+
review_id: type.number.describe("The review ID to get comments for"),
|
|
199181
|
+
"fresh?": type.boolean.describe(
|
|
199182
|
+
"Bypass the 60s thread cache. Use for the re-read before committing, so comments that landed while you worked are actually seen; leave unset otherwise."
|
|
199183
|
+
)
|
|
199087
199184
|
});
|
|
199088
199185
|
function hasThumbsUpFrom(comment, username) {
|
|
199089
199186
|
if (!comment.reactionGroups) return false;
|
|
@@ -199117,7 +199214,7 @@ function formatReviewThreads(threadBlocks, header) {
|
|
|
199117
199214
|
}
|
|
199118
199215
|
const lines = [];
|
|
199119
199216
|
lines.push(
|
|
199120
|
-
`#
|
|
199217
|
+
`# Open Threads (${threadBlocks.length}) on PR #${header.pullNumber} - dispatched by Review ${header.reviewId} from ${header.reviewer}`
|
|
199121
199218
|
);
|
|
199122
199219
|
lines.push("");
|
|
199123
199220
|
if (threadBlocks.length > 0) {
|
|
@@ -199258,11 +199355,27 @@ async function getReviewThreads(input2) {
|
|
|
199258
199355
|
if (!thread?.comments?.nodes) return false;
|
|
199259
199356
|
return thread.comments.nodes.some((c2) => c2?.pullRequestReview?.databaseId === input2.reviewId);
|
|
199260
199357
|
});
|
|
199261
|
-
if (
|
|
199262
|
-
|
|
199358
|
+
if (input2.approvedBy) {
|
|
199359
|
+
const username = input2.approvedBy;
|
|
199360
|
+
return threadsForReview.filter((thread) => threadHasThumbsUpFrom(thread, username));
|
|
199263
199361
|
}
|
|
199264
|
-
|
|
199265
|
-
return threadsForReview
|
|
199362
|
+
if (!input2.addressScope) return threadsForReview;
|
|
199363
|
+
return [...threadsForReview, ...concurrentThreads(allThreads, threadsForReview, input2)];
|
|
199364
|
+
}
|
|
199365
|
+
function concurrentThreads(allThreads, threadsForReview, input2) {
|
|
199366
|
+
const alreadyIncluded = new Set(threadsForReview.map((thread) => thread.id));
|
|
199367
|
+
return allThreads.filter((thread) => {
|
|
199368
|
+
if (!thread?.comments?.nodes || alreadyIncluded.has(thread.id)) return false;
|
|
199369
|
+
if (thread.isResolved) return false;
|
|
199370
|
+
const comments = thread.comments.nodes.filter((c2) => c2 !== null);
|
|
199371
|
+
if (comments.length === 0) return false;
|
|
199372
|
+
if (comments.some((c2) => isPullfrog(c2.author?.login))) return false;
|
|
199373
|
+
return input2.addressScope === "all" || comments.some((c2) => mentionsPullfrog(c2.body));
|
|
199374
|
+
});
|
|
199375
|
+
}
|
|
199376
|
+
function mentionsPullfrog(body) {
|
|
199377
|
+
if (!body) return false;
|
|
199378
|
+
return body.split(/\r?\n/).filter((line) => !line.trim().startsWith(">")).join("\n").toLowerCase().includes("@pullfrog");
|
|
199266
199379
|
}
|
|
199267
199380
|
function formatReviewData(input2) {
|
|
199268
199381
|
const rawReviewBody = input2.review.body;
|
|
@@ -199359,6 +199472,7 @@ function GetReviewCommentsTool(ctx) {
|
|
|
199359
199472
|
parameters: GetReviewComments,
|
|
199360
199473
|
execute: execute(async (params) => {
|
|
199361
199474
|
const approvedBy = ctx.payload.event.trigger === "fix_review" && ctx.payload.event.approved_only ? ctx.payload.triggerer : void 0;
|
|
199475
|
+
if (params.fresh) invalidateReviewThreadCache(ctx.repo);
|
|
199362
199476
|
const result = await getReviewData({
|
|
199363
199477
|
octokit: ctx.octokit,
|
|
199364
199478
|
owner: ctx.repo.owner,
|
|
@@ -199366,6 +199480,7 @@ function GetReviewCommentsTool(ctx) {
|
|
|
199366
199480
|
pullNumber: params.pull_number,
|
|
199367
199481
|
reviewId: params.review_id,
|
|
199368
199482
|
approvedBy,
|
|
199483
|
+
addressScope: ctx.payload.event.address_scope,
|
|
199369
199484
|
tmpdir: ctx.tmpdir,
|
|
199370
199485
|
githubToken: ctx.githubInstallationToken
|
|
199371
199486
|
});
|
|
@@ -199396,7 +199511,7 @@ function GetReviewCommentsTool(ctx) {
|
|
|
199396
199511
|
threadCount: threadBlocks.length,
|
|
199397
199512
|
commentsPath,
|
|
199398
199513
|
toc: formatted.toc,
|
|
199399
|
-
instructions: `the file at commentsPath contains ${threadBlocks.length} review threads with full conversation history. comments marked with * are from the target review (${params.review_id}). the TOC shows each thread's file:line and the line number where it appears in the file. to read a specific thread, use: grep -A 50 "^## <file:line>" ${commentsPath} (replace <file:line> with the path from the TOC, e.g. "^## action/utils/foo.ts:42"). address each thread in order, working through one file at a time.`
|
|
199514
|
+
instructions: `the file at commentsPath contains ${threadBlocks.length} review threads with full conversation history. comments marked with * are from the target review (${params.review_id}); any other thread is open feedback on this PR that is also yours to address, including comments that landed after this run started. the TOC shows each thread's file:line and the line number where it appears in the file. to read a specific thread, use: grep -A 50 "^## <file:line>" ${commentsPath} (replace <file:line> with the path from the TOC, e.g. "^## action/utils/foo.ts:42"). address each thread in order, working through one file at a time.`
|
|
199400
199515
|
};
|
|
199401
199516
|
})
|
|
199402
199517
|
});
|
|
@@ -200055,10 +200170,12 @@ function isByokSetupError(text) {
|
|
|
200055
200170
|
var SECRETS_UNAVAILABLE_MARKER = "couldn't load your Pullfrog secrets";
|
|
200056
200171
|
var ROUTER_UNFUNDED_MARKER = "your Pullfrog Router balance is empty";
|
|
200057
200172
|
var CREDENTIAL_REJECTED_MARKER = "was rejected by its provider";
|
|
200058
|
-
|
|
200059
|
-
|
|
200060
|
-
|
|
200061
|
-
|
|
200173
|
+
var NoUsableCredentialError = class extends Error {
|
|
200174
|
+
};
|
|
200175
|
+
function throwKeyError(params) {
|
|
200176
|
+
if (params.secretsUnavailable) throw new Error(buildSecretsUnavailableError(params));
|
|
200177
|
+
if (params.routerUnfunded) throw new NoUsableCredentialError(buildRouterUnfundedError(params));
|
|
200178
|
+
throw new NoUsableCredentialError(buildMissingApiKeyError(params));
|
|
200062
200179
|
}
|
|
200063
200180
|
function buildRouterUnfundedError(params) {
|
|
200064
200181
|
const billingUrl = `${getApiUrl()}/console/${params.owner}#billing`;
|
|
@@ -200257,57 +200374,51 @@ function validateAgentApiKey(params) {
|
|
|
200257
200374
|
validateVertexSetup({ owner: params.owner, name: params.name });
|
|
200258
200375
|
return;
|
|
200259
200376
|
}
|
|
200260
|
-
|
|
200261
|
-
|
|
200377
|
+
if (process.env[BEDROCK_MODEL_ID_ENV]?.trim() === params.model) {
|
|
200378
|
+
validateBedrockSetup({ owner: params.owner, name: params.name });
|
|
200379
|
+
return;
|
|
200380
|
+
}
|
|
200262
200381
|
}
|
|
200263
200382
|
if (params.agent.name === "opencode") {
|
|
200264
200383
|
if (params.authorized.has(params.model)) return;
|
|
200265
200384
|
const reason = getModelsFailure();
|
|
200266
200385
|
if (reason) throw new Error(reason);
|
|
200267
200386
|
if (getModelEnvVars(params.model).some(hasEnvVar2)) return;
|
|
200268
|
-
|
|
200269
|
-
buildKeyError({
|
|
200270
|
-
owner: params.owner,
|
|
200271
|
-
name: params.name,
|
|
200272
|
-
model: params.model,
|
|
200273
|
-
secretsUnavailable: params.secretsUnavailable,
|
|
200274
|
-
routerUnfunded: params.routerUnfunded
|
|
200275
|
-
})
|
|
200276
|
-
);
|
|
200277
|
-
}
|
|
200278
|
-
if (hasSingleProviderAuth(params.agent.name)) return;
|
|
200279
|
-
throw new Error(
|
|
200280
|
-
buildKeyError({
|
|
200387
|
+
throwKeyError({
|
|
200281
200388
|
owner: params.owner,
|
|
200282
200389
|
name: params.name,
|
|
200283
200390
|
model: params.model,
|
|
200284
200391
|
secretsUnavailable: params.secretsUnavailable,
|
|
200285
200392
|
routerUnfunded: params.routerUnfunded
|
|
200286
|
-
})
|
|
200287
|
-
|
|
200393
|
+
});
|
|
200394
|
+
}
|
|
200395
|
+
if (hasSingleProviderAuth(params.agent.name)) return;
|
|
200396
|
+
throwKeyError({
|
|
200397
|
+
owner: params.owner,
|
|
200398
|
+
name: params.name,
|
|
200399
|
+
model: params.model,
|
|
200400
|
+
secretsUnavailable: params.secretsUnavailable,
|
|
200401
|
+
routerUnfunded: params.routerUnfunded
|
|
200402
|
+
});
|
|
200288
200403
|
}
|
|
200289
200404
|
if (params.agent.name === "opencode") {
|
|
200290
200405
|
if ([...params.authorized].some(modelHasRuntimeAuth)) return;
|
|
200291
200406
|
const reason = getModelsFailure();
|
|
200292
200407
|
if (reason) throw new Error(reason);
|
|
200293
|
-
|
|
200294
|
-
buildKeyError({
|
|
200295
|
-
owner: params.owner,
|
|
200296
|
-
name: params.name,
|
|
200297
|
-
secretsUnavailable: params.secretsUnavailable,
|
|
200298
|
-
routerUnfunded: params.routerUnfunded
|
|
200299
|
-
})
|
|
200300
|
-
);
|
|
200301
|
-
}
|
|
200302
|
-
if (hasSingleProviderAuth(params.agent.name)) return;
|
|
200303
|
-
throw new Error(
|
|
200304
|
-
buildKeyError({
|
|
200408
|
+
throwKeyError({
|
|
200305
200409
|
owner: params.owner,
|
|
200306
200410
|
name: params.name,
|
|
200307
200411
|
secretsUnavailable: params.secretsUnavailable,
|
|
200308
200412
|
routerUnfunded: params.routerUnfunded
|
|
200309
|
-
})
|
|
200310
|
-
|
|
200413
|
+
});
|
|
200414
|
+
}
|
|
200415
|
+
if (hasSingleProviderAuth(params.agent.name)) return;
|
|
200416
|
+
throwKeyError({
|
|
200417
|
+
owner: params.owner,
|
|
200418
|
+
name: params.name,
|
|
200419
|
+
secretsUnavailable: params.secretsUnavailable,
|
|
200420
|
+
routerUnfunded: params.routerUnfunded
|
|
200421
|
+
});
|
|
200311
200422
|
}
|
|
200312
200423
|
function isApiKeyAuthError(text) {
|
|
200313
200424
|
if (!text) return false;
|
|
@@ -200432,9 +200543,9 @@ function formatApiKeyErrorSummary(params) {
|
|
|
200432
200543
|
].join("\n");
|
|
200433
200544
|
}
|
|
200434
200545
|
return [
|
|
200435
|
-
`**Your LLM provider API key was rejected.** Rotate the key in your provider dashboard, then update the
|
|
200546
|
+
`**Your LLM provider API key was rejected.** Rotate the key in your provider dashboard, then update the copy Pullfrog uses \u2014 wherever you saved it.`,
|
|
200436
200547
|
"",
|
|
200437
|
-
`[
|
|
200548
|
+
`[Pullfrog secrets \u2192](${getApiUrl()}/console/${params.owner}) \xB7 [Repo secrets \u2192](${githubSecretsUrl}) \xB7 [Model settings \u2192](${settingsUrl}) \xB7 [Setup docs \u2192](https://docs.pullfrog.com/keys) \xB7 [Ask in Discord \u2192](https://discord.gg/8y96raFg8e)`
|
|
200438
200549
|
].join("\n");
|
|
200439
200550
|
}
|
|
200440
200551
|
|
|
@@ -201660,6 +201771,9 @@ var DebugInput = type.enumerated("disabled", "enabled");
|
|
|
201660
201771
|
var JsonPayload = type({
|
|
201661
201772
|
"~pullfrog": "true",
|
|
201662
201773
|
version: "string",
|
|
201774
|
+
// the server's run type, forwarded verbatim to run-context. optional so a
|
|
201775
|
+
// payload from an older server build still parses against a newer action.
|
|
201776
|
+
"type?": "string | undefined",
|
|
201663
201777
|
"model?": "string | undefined",
|
|
201664
201778
|
"modelExplicit?": "boolean | undefined",
|
|
201665
201779
|
"effort?": "number | string | undefined",
|
|
@@ -202145,7 +202259,8 @@ ${ctx.error}` : ctx.error;
|
|
|
202145
202259
|
clamped: ctx.toolState.modelClamped,
|
|
202146
202260
|
unselectedProxyDefault: ctx.toolState.unselectedProxyDefault,
|
|
202147
202261
|
shaPinned: ctx.toolState.shaPinned,
|
|
202148
|
-
oss: ctx.toolState.oss
|
|
202262
|
+
oss: ctx.toolState.oss,
|
|
202263
|
+
owner: repoContext.owner
|
|
202149
202264
|
});
|
|
202150
202265
|
const body = `${formattedError}${footer}`;
|
|
202151
202266
|
const comment = ctx.toolState.progressComment;
|
|
@@ -202228,7 +202343,7 @@ async function mintProxyKey(ctx) {
|
|
|
202228
202343
|
}
|
|
202229
202344
|
}
|
|
202230
202345
|
async function buildProxyTokenHeaders(ctx) {
|
|
202231
|
-
const fundingSource = ctx.
|
|
202346
|
+
const fundingSource = ctx.fundingSource;
|
|
202232
202347
|
if (ctx.oidcCredentials) {
|
|
202233
202348
|
const creds = ctx.oidcCredentials;
|
|
202234
202349
|
const oidcToken = await op(() => fetchIdTokenFromStash(creds), {
|
|
@@ -202260,7 +202375,7 @@ async function resolveProxyModel(ctx) {
|
|
|
202260
202375
|
const key = await mintProxyKey({
|
|
202261
202376
|
oidcCredentials: ctx.oidcCredentials,
|
|
202262
202377
|
repo: ctx.repo,
|
|
202263
|
-
|
|
202378
|
+
fundingSource: ctx.oss ? "oss" : "router"
|
|
202264
202379
|
});
|
|
202265
202380
|
if (!key) return;
|
|
202266
202381
|
process.env.OPENROUTER_API_KEY = key;
|
|
@@ -202332,6 +202447,26 @@ async function runProxyResolution(ctx) {
|
|
|
202332
202447
|
throw error62;
|
|
202333
202448
|
}
|
|
202334
202449
|
}
|
|
202450
|
+
async function resolveTrialFallback(ctx) {
|
|
202451
|
+
if (process.env.PULLFROG_MODEL?.trim()) return false;
|
|
202452
|
+
if (!ctx.oidcCredentials && !isLocalApiUrl()) return false;
|
|
202453
|
+
const key = await mintProxyKey({
|
|
202454
|
+
oidcCredentials: ctx.oidcCredentials,
|
|
202455
|
+
repo: ctx.repo,
|
|
202456
|
+
fundingSource: "trial"
|
|
202457
|
+
});
|
|
202458
|
+
if (!key) return false;
|
|
202459
|
+
process.env.OPENROUTER_API_KEY = key;
|
|
202460
|
+
core9.setSecret(key);
|
|
202461
|
+
ctx.payload.proxyModel = DEFAULT_PROXY_MODEL;
|
|
202462
|
+
ctx.toolState.model = DEFAULT_PROXY_MODEL;
|
|
202463
|
+
ctx.toolState.modelClamped = {
|
|
202464
|
+
from: ctx.configuredModel ?? DEFAULT_PROXY_MODEL,
|
|
202465
|
+
reason: "trial"
|
|
202466
|
+
};
|
|
202467
|
+
log2.info(`\xBB proxy: trial \u2192 ${DEFAULT_PROXY_MODEL}`);
|
|
202468
|
+
return true;
|
|
202469
|
+
}
|
|
202335
202470
|
|
|
202336
202471
|
// utils/prSummary.ts
|
|
202337
202472
|
import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile3 } from "node:fs/promises";
|
|
@@ -202509,8 +202644,9 @@ async function fetchRunContext(params) {
|
|
|
202509
202644
|
if (params.oidcToken) {
|
|
202510
202645
|
headers["X-GitHub-OIDC-Token"] = params.oidcToken;
|
|
202511
202646
|
}
|
|
202647
|
+
const query = params.runType ? `?type=${encodeURIComponent(params.runType)}` : "";
|
|
202512
202648
|
const response = await apiFetch({
|
|
202513
|
-
path: `/api/repo/${params.repoContext.owner}/${params.repoContext.name}/run-context`,
|
|
202649
|
+
path: `/api/repo/${params.repoContext.owner}/${params.repoContext.name}/run-context${query}`,
|
|
202514
202650
|
headers,
|
|
202515
202651
|
signal: controller.signal
|
|
202516
202652
|
});
|
|
@@ -202547,7 +202683,8 @@ async function fetchRunContext(params) {
|
|
|
202547
202683
|
proxyModel: data.proxyModel,
|
|
202548
202684
|
dbSecrets: data.dbSecrets,
|
|
202549
202685
|
secretsUnavailable: data.secretsUnavailable,
|
|
202550
|
-
routerUnfunded: data.routerUnfunded
|
|
202686
|
+
routerUnfunded: data.routerUnfunded,
|
|
202687
|
+
trialFallback: data.trialFallback
|
|
202551
202688
|
};
|
|
202552
202689
|
} catch {
|
|
202553
202690
|
clearTimeout(timeoutId);
|
|
@@ -202588,7 +202725,7 @@ async function resolveRunContextData(params) {
|
|
|
202588
202725
|
retries: [100, 500],
|
|
202589
202726
|
bail: (error62) => !isTransientOctokitError(error62)
|
|
202590
202727
|
})(),
|
|
202591
|
-
fetchRunContext({ token: params.token, repoContext, oidcToken })
|
|
202728
|
+
fetchRunContext({ token: params.token, repoContext, oidcToken, runType: params.runType })
|
|
202592
202729
|
]);
|
|
202593
202730
|
return {
|
|
202594
202731
|
repo: {
|
|
@@ -202606,7 +202743,8 @@ async function resolveRunContextData(params) {
|
|
|
202606
202743
|
// a failed mint on a runner that should have been able to mint is the same
|
|
202607
202744
|
// outcome as the server-side failure: the run never sees stored secrets.
|
|
202608
202745
|
secretsUnavailable: runContext.secretsUnavailable || !!process.env.ACTIONS_ID_TOKEN_REQUEST_URL && oidcToken === void 0,
|
|
202609
|
-
routerUnfunded: runContext.routerUnfunded
|
|
202746
|
+
routerUnfunded: runContext.routerUnfunded,
|
|
202747
|
+
trialFallback: runContext.trialFallback
|
|
202610
202748
|
};
|
|
202611
202749
|
}
|
|
202612
202750
|
|
|
@@ -203686,7 +203824,11 @@ async function main() {
|
|
|
203686
203824
|
resolveGit();
|
|
203687
203825
|
const jobToken = getJobToken();
|
|
203688
203826
|
const initialOctokit = createOctokit(jobToken);
|
|
203689
|
-
const runContext = await resolveRunContextData({
|
|
203827
|
+
const runContext = await resolveRunContextData({
|
|
203828
|
+
octokit: initialOctokit,
|
|
203829
|
+
token: jobToken,
|
|
203830
|
+
runType: typeof resolvedPromptInput === "string" ? void 0 : resolvedPromptInput.type
|
|
203831
|
+
});
|
|
203690
203832
|
timer.checkpoint("runContextData");
|
|
203691
203833
|
const payload = resolvePayload(resolvedPromptInput, runContext.repoSettings);
|
|
203692
203834
|
const toolState = initToolState({
|
|
@@ -203867,7 +204009,7 @@ async function main() {
|
|
|
203867
204009
|
}
|
|
203868
204010
|
const resolvedModel = credentials.kind === "fellBack" ? credentials.replacement : configuredModel;
|
|
203869
204011
|
vertexCredentials = materializeVertexCredentials({ model: resolvedModel });
|
|
203870
|
-
|
|
204012
|
+
let agent2 = resolveAgent({
|
|
203871
204013
|
model: resolvedModel,
|
|
203872
204014
|
proxyModel: payload.proxyModel,
|
|
203873
204015
|
// the account opt-in and the canary arm are both admissions to codex, so
|
|
@@ -203879,15 +204021,32 @@ async function main() {
|
|
|
203879
204021
|
const effectiveModel = payload.proxyModel ?? resolvedModel ?? payload.model;
|
|
203880
204022
|
toolState.model = effectiveModel;
|
|
203881
204023
|
if (!payload.proxyModel) {
|
|
203882
|
-
|
|
203883
|
-
|
|
203884
|
-
|
|
203885
|
-
|
|
203886
|
-
|
|
203887
|
-
|
|
203888
|
-
|
|
203889
|
-
|
|
203890
|
-
|
|
204024
|
+
try {
|
|
204025
|
+
validateAgentApiKey({
|
|
204026
|
+
agent: agent2,
|
|
204027
|
+
model: effectiveModel,
|
|
204028
|
+
authorized: getAuthorizedModels(),
|
|
204029
|
+
owner: runContext.repo.owner,
|
|
204030
|
+
name: runContext.repo.name,
|
|
204031
|
+
secretsUnavailable: runContext.secretsUnavailable,
|
|
204032
|
+
routerUnfunded: runContext.routerUnfunded
|
|
204033
|
+
});
|
|
204034
|
+
} catch (missingKey) {
|
|
204035
|
+
const funded = runContext.trialFallback && missingKey instanceof NoUsableCredentialError ? await resolveTrialFallback({
|
|
204036
|
+
payload,
|
|
204037
|
+
configuredModel: effectiveModel,
|
|
204038
|
+
oidcCredentials,
|
|
204039
|
+
repo: runContext.repo,
|
|
204040
|
+
toolState
|
|
204041
|
+
}) : false;
|
|
204042
|
+
if (!funded) throw missingKey;
|
|
204043
|
+
agent2 = resolveAgent({
|
|
204044
|
+
model: resolvedModel,
|
|
204045
|
+
proxyModel: payload.proxyModel,
|
|
204046
|
+
codexAgent: runContext.repoSettings.codexAgent || payload.codexArm === true
|
|
204047
|
+
});
|
|
204048
|
+
toolState.agent = agent2.name;
|
|
204049
|
+
}
|
|
203891
204050
|
}
|
|
203892
204051
|
await setupGit({
|
|
203893
204052
|
gitToken: tokenRef.gitToken,
|
|
@@ -204797,7 +204956,7 @@ function resolveTarget(repo) {
|
|
|
204797
204956
|
return { owner: match3[1], repo: match3[2] };
|
|
204798
204957
|
}
|
|
204799
204958
|
function cliVersion() {
|
|
204800
|
-
const parts = /^(\d+)\.(\d+)\.(\d+)$/.exec("0.1.
|
|
204959
|
+
const parts = /^(\d+)\.(\d+)\.(\d+)$/.exec("0.1.69");
|
|
204801
204960
|
if (!parts) return "0.0.0";
|
|
204802
204961
|
return `${Number(parts[1])}.${Number(parts[2])}.${Number(parts[3])}`;
|
|
204803
204962
|
}
|
|
@@ -204964,7 +205123,7 @@ async function runCli5(input2) {
|
|
|
204964
205123
|
}
|
|
204965
205124
|
|
|
204966
205125
|
// cli.ts
|
|
204967
|
-
var VERSION10 = "0.1.
|
|
205126
|
+
var VERSION10 = "0.1.69";
|
|
204968
205127
|
var bin = basename2(process.argv[1] || "");
|
|
204969
205128
|
var PROG = bin === "pf" || bin === "pullfrog" ? bin : "pullfrog";
|
|
204970
205129
|
var rawArgs = process.argv.slice(2);
|
package/dist/effort.d.ts
CHANGED
|
@@ -32,6 +32,16 @@ export type EffortPosition = number;
|
|
|
32
32
|
* ran at before this setting existed.
|
|
33
33
|
*/
|
|
34
34
|
export declare const DEFAULT_EFFORT_POSITION: EffortPosition;
|
|
35
|
+
/**
|
|
36
|
+
* the rung a Pullfrog-subsidised OSS run is pinned to, whatever the repo or a
|
|
37
|
+
* per-kind-of-work row has stored — see `ossEffortFloor`, which spends it, and
|
|
38
|
+
* the console's Model card, which stops offering the control because of it.
|
|
39
|
+
*
|
|
40
|
+
* shared rather than written twice: the runtime pins the run and the console
|
|
41
|
+
* says so, and a console that names a different rung than the one that runs is
|
|
42
|
+
* the same class of bug as the Router opt-out the two surfaces disagreed on.
|
|
43
|
+
*/
|
|
44
|
+
export declare const SUBSIDY_RUNG = "high";
|
|
35
45
|
/**
|
|
36
46
|
* names we accept on user-facing surfaces (`--effort=`, the action input),
|
|
37
47
|
* mapped to fixed points on the axis. these are a typing convenience, NOT an
|