pullfrog 0.1.96 → 0.1.97
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/agents/opencodePlugin.d.ts +4 -2
- package/dist/cli.mjs +714 -526
- package/dist/index.js +718 -539
- package/dist/internal.js +60 -23
- package/dist/mcp/checkout.d.ts +2 -0
- package/dist/utils/apiKeys.d.ts +26 -0
- package/dist/utils/changeImpact.d.ts +1 -0
- package/dist/utils/codexHome.d.ts +4 -6
- package/dist/utils/gitAuth.d.ts +1 -0
- package/dist/utils/oauthWriteback.d.ts +5 -0
- package/dist/utils/openCodeModels.d.ts +19 -2
- package/dist/utils/providerErrors.d.ts +4 -1
- package/dist/utils/runErrorRenderer.d.ts +25 -9
- package/dist/utils/subprocess.d.ts +1 -0
- package/package.json +1 -1
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.
|
|
11
|
+
version: "0.1.97",
|
|
12
12
|
cliContractVersion: "1",
|
|
13
13
|
type: "module",
|
|
14
14
|
bin: {
|
|
@@ -14932,6 +14932,15 @@ var providers = {
|
|
|
14932
14932
|
openRouterEffort: [],
|
|
14933
14933
|
openRouterResolve: "openrouter/qwen/qwen3.8-flash"
|
|
14934
14934
|
},
|
|
14935
|
+
// Zen lists Mistral Large 4 at half its list price for launch, level with
|
|
14936
|
+
// the routers. hidden until a published `pullfrog` resolves it, as there.
|
|
14937
|
+
"mistral-large": {
|
|
14938
|
+
displayName: "Mistral Large",
|
|
14939
|
+
resolve: "opencode/mistral-large-4",
|
|
14940
|
+
effort: ["none", "high"],
|
|
14941
|
+
openRouterResolve: "openrouter/mistralai/mistral-large-4-0",
|
|
14942
|
+
hidden: true
|
|
14943
|
+
},
|
|
14935
14944
|
"gpt-5-nano": {
|
|
14936
14945
|
displayName: "GPT Nano",
|
|
14937
14946
|
resolve: "opencode/gpt-5.4-nano",
|
|
@@ -14980,12 +14989,12 @@ var providers = {
|
|
|
14980
14989
|
fallback: "opencode/big-pickle"
|
|
14981
14990
|
},
|
|
14982
14991
|
// Zen's FREE stealth preview (listed 2026-09-23), the third after
|
|
14983
|
-
// big-pickle and union-alpha.
|
|
14984
|
-
//
|
|
14985
|
-
//
|
|
14992
|
+
// big-pickle and union-alpha. Zen's docs: zero retention, no training.
|
|
14993
|
+
// unlike the first two it publishes an effort ladder, so the gate
|
|
14994
|
+
// mirrors one here.
|
|
14986
14995
|
"space-bunny": {
|
|
14987
14996
|
displayName: "Space Bunny",
|
|
14988
|
-
description: "Stealth preview
|
|
14997
|
+
description: "Stealth preview, free for a limited time",
|
|
14989
14998
|
resolve: "opencode/space-bunny-free",
|
|
14990
14999
|
effort: ["low", "medium", "high", "xhigh", "max"],
|
|
14991
15000
|
// free to run, still gated on the provider's own OPENCODE_API_KEY —
|
|
@@ -15003,17 +15012,30 @@ var providers = {
|
|
|
15003
15012
|
// see the big-pickle note above (#1077).
|
|
15004
15013
|
isFree: true
|
|
15005
15014
|
},
|
|
15006
|
-
// Zen's FREE stealth preview (listed 2026-10-01), the fifth promo. Zen
|
|
15007
|
-
//
|
|
15015
|
+
// Zen's FREE stealth preview (listed 2026-10-01), the fifth promo. Zen's
|
|
15016
|
+
// docs: data may be used to improve the model during the free period.
|
|
15008
15017
|
"fledge-alpha": {
|
|
15009
15018
|
displayName: "Fledge Alpha",
|
|
15010
|
-
description: "Stealth preview
|
|
15019
|
+
description: "Stealth preview; data may be used to improve it",
|
|
15011
15020
|
resolve: "opencode/fledge-alpha-free",
|
|
15012
15021
|
effort: ["low", "high", "max"],
|
|
15013
15022
|
// free to run, still gated on the provider's own OPENCODE_API_KEY —
|
|
15014
15023
|
// see the big-pickle note above (#1077).
|
|
15015
15024
|
isFree: true
|
|
15016
15025
|
},
|
|
15026
|
+
// Zen's FREE preview (listed 2026-10-06), the sixth promo, on Fledge
|
|
15027
|
+
// Alpha's terms. hidden until a published `pullfrog` resolves it.
|
|
15028
|
+
exo: {
|
|
15029
|
+
displayName: "Exo",
|
|
15030
|
+
description: "Preview; data may be used to improve it",
|
|
15031
|
+
resolve: "opencode/exo-free",
|
|
15032
|
+
// Zen publishes a single rung.
|
|
15033
|
+
effort: ["high"],
|
|
15034
|
+
// free to run, still gated on the provider's own OPENCODE_API_KEY —
|
|
15035
|
+
// see the big-pickle note above (#1077).
|
|
15036
|
+
isFree: true,
|
|
15037
|
+
hidden: true
|
|
15038
|
+
},
|
|
15017
15039
|
// Zen's live free MiMo, and the second free row in a menu that big-pickle
|
|
15018
15040
|
// was alone in since `mimo-v2-pro-free` lost its model.
|
|
15019
15041
|
mimo: {
|
|
@@ -15469,6 +15491,17 @@ var providers = {
|
|
|
15469
15491
|
resolve: "openrouter/meta/muse-spark-1.3-contributor",
|
|
15470
15492
|
effort: ["minimal", "low", "medium", "high", "xhigh", "max"],
|
|
15471
15493
|
openRouterResolve: "openrouter/meta/muse-spark-1.3-contributor"
|
|
15494
|
+
},
|
|
15495
|
+
// Mistral's flagship (public preview, 2026-10-06), on the routers only:
|
|
15496
|
+
// the catalog carries no direct Mistral provider. out of the pickers
|
|
15497
|
+
// until a published `pullfrog` resolves the slug; see "A new alias
|
|
15498
|
+
// reaches the picker at merge".
|
|
15499
|
+
"mistral-large": {
|
|
15500
|
+
displayName: "Mistral Large",
|
|
15501
|
+
resolve: "openrouter/mistralai/mistral-large-4-0",
|
|
15502
|
+
effort: ["none", "high"],
|
|
15503
|
+
openRouterResolve: "openrouter/mistralai/mistral-large-4-0",
|
|
15504
|
+
hidden: true
|
|
15472
15505
|
}
|
|
15473
15506
|
}
|
|
15474
15507
|
}),
|
|
@@ -15560,6 +15593,13 @@ var providers = {
|
|
|
15560
15593
|
displayName: "Muse Spark",
|
|
15561
15594
|
resolve: "vercel/meta/muse-spark-1.3",
|
|
15562
15595
|
effort: ["minimal", "low", "medium", "high", "xhigh"]
|
|
15596
|
+
},
|
|
15597
|
+
// hidden until a published `pullfrog` resolves it, as on OpenRouter.
|
|
15598
|
+
"mistral-large": {
|
|
15599
|
+
displayName: "Mistral Large",
|
|
15600
|
+
resolve: "vercel/mistral/mistral-large-4",
|
|
15601
|
+
effort: ["none", "low", "medium", "high"],
|
|
15602
|
+
hidden: true
|
|
15563
15603
|
}
|
|
15564
15604
|
}
|
|
15565
15605
|
})
|
|
@@ -15852,17 +15892,16 @@ var REVIEWER_AGENT_NAME = "reviewfrog";
|
|
|
15852
15892
|
// modes.ts
|
|
15853
15893
|
var PR_SUMMARY_FORMAT = `### Default format
|
|
15854
15894
|
|
|
15855
|
-
The body has at most
|
|
15895
|
+
The body has at most two parts, in this order:
|
|
15856
15896
|
|
|
15857
15897
|
1. **Reviewed changes preamble** \u2014 a bolded \`**Reviewed changes**\` lead-in with one sentence on what was reviewed in this run (for \`IncrementalReview\`: what changed since the prior pullfrog review), then a bullet list of the substantive changes \u2014 short bolded title, one sentence each. A reviewer should understand the full reviewed scope from this list alone. Close the preamble with the metadata comment below.
|
|
15858
15898
|
2. **Cross-cutting issue sections** (zero or more) \u2014 one \`### {emoji} {what's wrong, not what to do}\` heading per concern.
|
|
15859
|
-
3. **\`### \u2139\uFE0F Nitpicks\`** at the very bottom, if any \u2014 a flat bullet list, no technical-details block. Only for what has no runtime effect: a stale or wrong comment or doc, a misleading name, a typo, dead code, a missing test. Never formatting or whitespace.
|
|
15860
15899
|
|
|
15861
15900
|
**Inline vs. body.** Concerns that anchor to a specific line go inline (the \`comments\` parameter), even when their implications are broad. Body \`### \` sections are reserved for concerns that have **no line to anchor to** \u2014 *absence* (something the diff should have done but didn't), *sequencing* (rollout / deletion / migration order), *design decisions only the human can make*, or *scope questions the diff raises but doesn't address*. With no non-anchorable concerns, the body is just the preamble + metadata.
|
|
15862
15901
|
|
|
15863
15902
|
**Severity emoji** on every \`### \` heading, and nowhere else: \u{1F6A8} critical (blocks merge \u2014 data loss, security, broken core flow) \xB7 \u26A0\uFE0F important (must address before merging) \xB7 \u2139\uFE0F informational (mergeable as-is).
|
|
15864
15903
|
|
|
15865
|
-
**Behavior decides placement, not fix size.** A finding that changes what the code does \u2014 a wrong value or message, a crash, a race, a leak, a missed cleanup, a slow path \u2014 gets its own inline comment (or a \`### \` section if no line anchors it) at its real severity, however small the fix.
|
|
15904
|
+
**Behavior decides placement, not fix size.** A finding that changes what the code does \u2014 a wrong value or message, a crash, a race, a leak, a missed cleanup, a slow path \u2014 gets its own inline comment (or a \`### \` section if no line anchors it) at its real severity, however small the fix.
|
|
15866
15905
|
|
|
15867
15906
|
**Blank line between every block-level element.** GitHub's markdown parser requires one before and after HTML tags (\`<details>\`, \`<summary>\`, \`<sub>\`, \`<br/>\`) \u2014 without it GitHub treats what follows as a continuation of the HTML block and renders your markdown as literal text. This is a parser quirk, not a style preference, and it permanently breaks the posted review.
|
|
15868
15907
|
|
|
@@ -16011,8 +16050,6 @@ For simple, well-defined tasks, skip the plan phase and go straight to build.`
|
|
|
16011
16050
|
|
|
16012
16051
|
3. **triage**: orient yourself on the PR \u2014 identify *what kind of thing this is* (domain it touches, seams it crosses, external contracts it depends on, user-facing surfaces it changes). pull as much context as you need to render a confident, well-grounded review: read related files, grep for callers of changed symbols, check tests that exercise the touched paths, fetch related GitHub state. **you are the synthesizer** \u2014 never delegate understanding to subagents.
|
|
16013
16052
|
|
|
16014
|
-
when the diff adds or changes a test, check that it can actually fail: a test that would still pass with the bug present is theatre, not coverage. the usual tell is a loose assertion standing where an exact one belongs \u2014 \`>=\` or a truthiness check over an expected value, or a snapshot that absorbs whatever it is handed. read the assertion against the behavior it claims to pin, not against whether it currently passes.
|
|
16015
|
-
|
|
16016
16053
|
skip the deeper pass and submit a \`No new issues found.\` review per step 7 only when the diff has **no behavioral surface at all** \u2014 doc typos, whitespace/formatting, lockfile or generated-code regeneration, a mechanical rename whose only effect is import-path updates. line count is not the signal: a one-line change to auth, money, SQL, a comparison operator, a redirect, or a config default is not trivial.
|
|
16017
16054
|
|
|
16018
16055
|
4. **specialist decision**: after reading the complete diff, name the questions you still cannot answer confidently yourself, and dispatch one \`${REVIEWER_AGENT_NAME}\` specialist per question. a question qualifies only when a specialist could return evidence that **changes your disposition** on the PR \u2014 generic requests for another look, extra confidence, or polish do not. most reviews need zero or one; some need several.
|
|
@@ -16036,8 +16073,6 @@ For simple, well-defined tasks, skip the plan phase and go straight to build.`
|
|
|
16036
16073
|
|
|
16037
16074
|
6. **aggregate & draft**: when specialist results land, merge findings; de-dup overlaps (two specialists catching the same issue = higher-confidence signal); trace each finding yourself before accepting it. drop praise, style preferences, speculative/unverified claims, findings about pre-existing code unrelated to the PR (heuristic: if the finding's root cause lives in lines this PR added or modified, it's in scope; otherwise drop unless the PR plausibly introduced or amplified the regression), and anything not actionable. also drop **bloat-shaped findings** \u2014 proposed fixes that would add defensive checks for cases that can't happen, abstractions used once, comments restating obvious code, tests asserting tautologies, or "just-in-case" guards. subagents are fallible and bias toward recommending changes; the bar for an actionable inline comment is sound + correct + elegant. recommending a change that improves only one of the three (or worse, degrades elegance to nominally improve correctness) makes the codebase worse, not better.
|
|
16038
16075
|
|
|
16039
|
-
**Hunt for non-anchored concerns before drafting.** After collecting your anchored findings, deliberately scan for concerns that have no specific line to point at \u2014 typically: deletion / cleanup plans for code the diff replaces or shadows; rollout sequencing (what happens to in-flight state during deploy / revert?); scope questions that only the human can answer (e.g. is the legacy path going away or is this a long-term dual track?); architectural risks the diff opens up that aren't a single-line bug.
|
|
16040
|
-
|
|
16041
16076
|
for surviving findings, draft inline comments with NEW line numbers from the diff \u2014 attach a \`<details>Technical details</details>\` block to any inline comment whose fix is non-trivial or has cross-file implications (see Inline technical details in the format below). every comment must be actionable, 2-3 sentences max in the visible part, one per root cause \u2014 name its other sites in that comment rather than repeating it. use GitHub permalink format for code references. for impact-analysis findings (stale references after rename/remove), report them in the review body ordered by severity (runtime breakage > incorrect docs > stale comments) rather than as inline comments unless they're anchored to a specific line.
|
|
16042
16077
|
|
|
16043
16078
|
7. **submit**: ALWAYS submit exactly one review via \`${t("create_pull_request_review")}\`. Do NOT call \`report_progress\` \u2014 the review is the final record and the progress comment will be cleaned up automatically.
|
|
@@ -16059,7 +16094,7 @@ For simple, well-defined tasks, skip the plan phase and go straight to build.`
|
|
|
16059
16094
|
\`approved: false\`. Body opens with \`> [!CAUTION]\\n> This PR introduces ...\`, followed by the PR summary. Include all inline comments via \`comments\`.
|
|
16060
16095
|
- **must-address non-critical findings** (real consequences if shipped \u2014 incorrect behavior in non-critical paths, missing validation on user input, regressions the author should fix before merge):
|
|
16061
16096
|
\`approved: false\`. Body opens with \`> [!IMPORTANT]\\n> ...\`, followed by the PR summary. Reserve this tier for findings with concrete fallout \u2014 do NOT use \`[!IMPORTANT]\` for nits, style preferences, or "consider also" suggestions. Include all inline comments via \`comments\`.
|
|
16062
|
-
- **minor suggestions only** (low-stakes findings
|
|
16097
|
+
- **minor suggestions only** (low-stakes findings):
|
|
16063
16098
|
\`approved: false\`. Body opens with \`> \u2139\uFE0F No critical issues \u2014 minor suggestions inline.\\n\\n\` followed by the PR summary. Include all inline comments via \`comments\`. Vary the wording after the emoji to fit the review (e.g. "Minor suggestions only.", "Two rough edges worth a look."), but always keep the \u2139\uFE0F prefix and keep it short.
|
|
16064
16099
|
- **informational observations** (mergeable as-is, nothing actionable \u2014 e.g. prior feedback addressed cleanly, calling out something noteworthy without recommending a change):
|
|
16065
16100
|
\`approved: true\`. Body opens with \`> \u2705 No new issues found.\\n\\n\` followed by the PR summary. Do NOT include inline \`comments\` \u2014 the \u2705 signals "no action needed", which contradicts an actionable anchor; if a point is concrete enough to anchor to a line, downgrade the whole review to "minor suggestions only" (\`approved: false\`) instead.
|
|
@@ -16102,8 +16137,6 @@ ${PR_SUMMARY_FORMAT}`
|
|
|
16102
16137
|
|
|
16103
16138
|
5. **triage**: orient on the *incremental* changes \u2014 domain, seams, external contracts, user-facing surfaces. pull as much context as you need to render a confident review: read related files, grep for callers of changed symbols, check tests that exercise the touched paths. **you are the synthesizer.**
|
|
16104
16139
|
|
|
16105
|
-
a test added or changed in this delta must be able to fail \u2014 one that would still pass with the bug present is theatre, not coverage. the tell is a loose assertion where an exact one belongs (\`>=\` or a truthiness check over an expected value, a snapshot that absorbs whatever it is handed).
|
|
16106
|
-
|
|
16107
16140
|
skip the deeper pass and jump to step 10's non-substantive path (do NOT submit a review) only when the incremental changes have **no behavioral surface at all** \u2014 formatting, comment tweaks, import reordering, lockfile regen, a mechanical rename of import paths. line count is not the signal: a one-line change to auth, money, SQL, a comparison operator, a redirect, or a config default is not trivial.
|
|
16108
16141
|
|
|
16109
16142
|
6. **specialist decision**: after covering the incremental and full diffs, name the questions about the new changes that you still cannot answer confidently yourself, and dispatch one \`${REVIEWER_AGENT_NAME}\` specialist per question. a question qualifies only when a specialist could return evidence that **changes your disposition** on the PR \u2014 generic requests for another look, extra confidence, or polish do not. most incremental reviews need zero or one, especially thread-reply re-reviews; some need several.
|
|
@@ -16127,11 +16160,9 @@ ${PR_SUMMARY_FORMAT}`
|
|
|
16127
16160
|
|
|
16128
16161
|
8. **aggregate, draft, self-critique**: merge findings (yours + output from every specialist you dispatched); de-dup overlaps; trace each finding yourself. drop praise, style preferences, speculative/unverified claims, findings about pre-existing code unrelated to the new commits, anything not actionable, and anything that re-states prior review feedback (heuristic: if the finding's root cause lives in lines the *new commits* added or modified, it's in scope; otherwise drop). also drop **bloat-shaped findings** \u2014 proposed fixes that would add defensive checks for cases that can't happen, abstractions used once, comments restating obvious code, tests asserting tautologies, or "just-in-case" guards. subagents are fallible and bias toward recommending changes; the bar for an actionable inline comment is sound + correct + elegant. recommending a change that improves only one of the three (or degrades elegance to nominally improve correctness) makes the codebase worse, not better. To compute "lines the new commits added or modified": if \`incrementalDiffPath\` from step 2 is present, use it directly. Otherwise, take the prior Pullfrog review's \`commit_id\` (returned alongside each entry from \`${t("list_pull_request_reviews")}\` in step 4) and run \`git diff <prior-review-sha>..HEAD\` to isolate the lines added since that review.
|
|
16129
16162
|
|
|
16130
|
-
**Hunt for non-anchored concerns before drafting.** After collecting your anchored findings, deliberately scan for concerns that have no specific line to point at \u2014 typically: deletion / cleanup plans for code the new commits replace or shadow; rollout sequencing (what happens to in-flight state during deploy / revert?); scope questions that only the human can answer (e.g. is the legacy path going away or is this a long-term dual track?); architectural risks the new commits open up that aren't a single-line bug.
|
|
16131
|
-
|
|
16132
16163
|
draft inline comments with NEW line numbers from the full PR diff \u2014 attach a \`<details>Technical details</details>\` block to any inline comment whose fix is non-trivial or has cross-file implications (see Inline technical details in the format below). every comment must be actionable, 2-3 sentences max in the visible part, one per root cause \u2014 name its other sites in that comment rather than repeating it.
|
|
16133
16164
|
|
|
16134
|
-
9. **build the review body**: use the same default format as Review mode (preamble + optional cross-cutting \`### \` sections
|
|
16165
|
+
9. **build the review body**: use the same default format as Review mode (preamble + optional cross-cutting \`### \` sections) \u2014 scoped to the **incremental delta**, not the full PR. The "Reviewed changes" bullets describe what changed since the prior pullfrog review (each bullet starts with a past-tense verb, e.g. \`- Extracted shared CLI runtime into a single module\`). Do NOT include a separate "Prior review feedback" checklist \u2014 that's tracked in the rolling PR summary snapshot for the next agent run, and surfacing it in the user-facing body is noise (changes that addressed prior feedback are already covered by the Reviewed-changes bullets). In some cases you may receive a complete diff for the whole PR instead of an incremental one; when this happens, determine what changed since Pullfrog's most recent review yourself before drafting bullets.
|
|
16135
16166
|
|
|
16136
16167
|
10. Submit \u2014 every run must end with EXACTLY ONE of \`${t("create_pull_request_review")}\` (substantive review) or \`${t("report_progress")}\` (no-review acknowledgement). do NOT call \`create_issue_comment\` for review output.
|
|
16137
16168
|
|
|
@@ -16142,7 +16173,7 @@ ${PR_SUMMARY_FORMAT}`
|
|
|
16142
16173
|
- IF NO NEW ISSUES, NON-SUBSTANTIVE CHANGES ONLY (trivial formatting, import reordering, comment tweaks): do NOT submit a review. Instead call \`${t("report_progress")}\` with \`no_review_needed: true\` and a 1-2 sentence note explaining no review was warranted (e.g. "No new issues. Changes since last review are formatting-only."). this leaves a visible signal that the run completed.
|
|
16143
16174
|
- ELSE IF NEW CRITICAL ISSUES (blocks merge \u2014 bugs, security, data loss, broken core flows): call \`${t("create_pull_request_review")}\` with \`approved: false\`, all comments, and the review body. body opens with \`> [!CAUTION]\\n> This PR introduces ...\`, followed by the PR summary using the default format below.
|
|
16144
16175
|
- ELSE IF NEW MUST-ADDRESS NON-CRITICAL FINDINGS (real consequences if shipped \u2014 incorrect behavior, missing validation, regressions the author should fix before merge): call \`${t("create_pull_request_review")}\` with \`approved: false\`, all comments, and the review body. body opens with \`> [!IMPORTANT]\\n> ...\`, followed by the PR summary using the default format below. Do NOT use this tier for nits, style preferences, or "consider also" suggestions.
|
|
16145
|
-
- ELSE IF NEW MINOR SUGGESTIONS ONLY (low-stakes findings
|
|
16176
|
+
- ELSE IF NEW MINOR SUGGESTIONS ONLY (low-stakes findings): call \`${t("create_pull_request_review")}\` with \`approved: false\`, all comments, and the review body. body opens with \`> \u2139\uFE0F No critical issues \u2014 minor suggestions inline.\\n\\n\` (vary the wording after \u2139\uFE0F to fit the review), followed by the PR summary using the default format below.
|
|
16146
16177
|
- ELSE IF INFORMATIONAL OBSERVATIONS (mergeable as-is, but worth surfacing \u2014 e.g. prior feedback addressed cleanly, or a noteworthy positive observation): call \`${t("create_pull_request_review")}\` with \`approved: true\`, NO inline comments, and the review body. body opens with \`> \u2705 No new issues found.\\n\\n\` (or similar friendly green opener), followed by the PR summary using the default format below. If a point is concrete enough to anchor to a line, downgrade the whole review to "minor suggestions only" (\`approved: false\`) instead \u2014 the \u2705 signals "no action needed", which contradicts an actionable anchor.
|
|
16147
16178
|
- ELSE IF NO NEW ISSUES, SUBSTANTIVE CHANGES (new functionality, behavior changes, or fixes to prior review feedback): call \`${t("create_pull_request_review")}\` to create a PR review. If all previous reviews have been properly addressed and no new issues were discovered, set \`approved: true\`. body opens with \`> \u2705 No new issues found.\\n\\n\`, followed by the PR summary using the default format below.
|
|
16148
16179
|
|
|
@@ -16851,7 +16882,13 @@ var PROVIDER_ERROR_PATTERNS = [
|
|
|
16851
16882
|
// …/settings/credits`) matched neither this nor `requires more credits`, so
|
|
16852
16883
|
// the one run in that episode that was a genuinely empty wallet rendered with
|
|
16853
16884
|
// no CTA at all while runs 1-6 rendered correctly.
|
|
16854
|
-
|
|
16885
|
+
// `account funds` is Zen's (#1474).
|
|
16886
|
+
{
|
|
16887
|
+
regex: /Insufficient (?:balance|credits|account funds)/i,
|
|
16888
|
+
label: PROVIDER_BILLING_EXHAUSTED_LABEL
|
|
16889
|
+
},
|
|
16890
|
+
// OpenAI's `You have no credits remaining` (#1474).
|
|
16891
|
+
{ regex: /no credits remaining/i, label: PROVIDER_BILLING_EXHAUSTED_LABEL },
|
|
16855
16892
|
{ regex: /credit balance is too low/i, label: PROVIDER_BILLING_EXHAUSTED_LABEL },
|
|
16856
16893
|
// "spending cap" (Gemini) and "spending limit" (xAI, #1076) are the same
|
|
16857
16894
|
// condition in two house styles — a configured ceiling was reached.
|
package/dist/mcp/checkout.d.ts
CHANGED
|
@@ -61,6 +61,8 @@ export type PrData = {
|
|
|
61
61
|
};
|
|
62
62
|
type CheckoutPrBranchParams = GitContext & {
|
|
63
63
|
beforeSha?: string | undefined;
|
|
64
|
+
/** aborts the in-flight fetch (SIGTERM, so git removes its own locks) when the tool deadline fires */
|
|
65
|
+
signal: AbortSignal;
|
|
64
66
|
};
|
|
65
67
|
/**
|
|
66
68
|
* Shared helper to checkout a PR branch and configure fork remotes.
|
package/dist/utils/apiKeys.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { CredentialCandidate } from "./subscriptionCredentials.ts";
|
|
2
|
+
import type { ProbeVerdict } from "./subscriptionProbe.ts";
|
|
1
3
|
/** shared sentence in the four `resolveSlug` routing-slug throws. */
|
|
2
4
|
export declare const BYOK_SLUG_MARKER = "env var is required when the model is set to";
|
|
3
5
|
/** shared sentence in the four `validate*Setup` throws. */
|
|
@@ -49,6 +51,12 @@ export declare const ROUTER_UNFUNDED_MARKER = "your Pullfrog Router balance is e
|
|
|
49
51
|
* hand that agent the ability to suppress every other error body.
|
|
50
52
|
*/
|
|
51
53
|
export declare const CREDENTIAL_REJECTED_MARKER = "was rejected by its provider";
|
|
54
|
+
/**
|
|
55
|
+
* marker for the credential pool's refusal: every configured credential for the
|
|
56
|
+
* model was rejected or exhausted. verbatim for the same reason as
|
|
57
|
+
* `CREDENTIAL_REJECTED_MARKER` — each bullet quotes the probe's verdict.
|
|
58
|
+
*/
|
|
59
|
+
export declare const CREDENTIAL_POOL_REFUSED_MARKER = "No configured credential can serve this run";
|
|
52
60
|
/**
|
|
53
61
|
* Three ways to arrive at "the runner has no key", each with a different CTA:
|
|
54
62
|
* we couldn't read the key you stored, your Router wallet ran dry and you have
|
|
@@ -184,6 +192,24 @@ export declare function buildRejectedCredentialError(params: {
|
|
|
184
192
|
/** stored in Pullfrog (console / `pullfrog auth`) rather than a GitHub Actions secret. */
|
|
185
193
|
inPullfrogStore: boolean;
|
|
186
194
|
}): string;
|
|
195
|
+
export type RefusedCredential = {
|
|
196
|
+
name: string;
|
|
197
|
+
source: CredentialCandidate["source"] | "workflow";
|
|
198
|
+
verdict: Extract<ProbeVerdict, {
|
|
199
|
+
status: "rejected" | "exhausted";
|
|
200
|
+
}>;
|
|
201
|
+
};
|
|
202
|
+
/**
|
|
203
|
+
* The credential pool probed every credential configured for the model and
|
|
204
|
+
* none can serve it. One bullet per credential: the probe's own verdict, quoted,
|
|
205
|
+
* plus the remedy its kind and status call for — never inferred from the prose.
|
|
206
|
+
*/
|
|
207
|
+
export declare function buildCredentialPoolRefusedError(params: {
|
|
208
|
+
model: string;
|
|
209
|
+
refused: RefusedCredential[];
|
|
210
|
+
owner: string;
|
|
211
|
+
name: string;
|
|
212
|
+
}): string;
|
|
187
213
|
/**
|
|
188
214
|
* Friendly Markdown summary for both the missing-key and invalid-key cases.
|
|
189
215
|
* Used in the catch / result-failure paths in `main.ts` to overwrite the raw
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/** the data dir a subscription was installed into this run, which opencode's XDG_DATA_HOME must name. */
|
|
2
|
+
export declare function getSubscriptionDataHome(): string | undefined;
|
|
1
3
|
export declare function clearInstalledSubscription(name: "CODEX_AUTH_JSON" | "GROK_AUTH_JSON"): void;
|
|
2
4
|
/** sandbox-hidden home for pullfrog-managed on-disk secrets in CI. bash via
|
|
3
5
|
* MCP shell tmpfs-overlays this path; opencode's internal auth module
|
|
@@ -11,8 +13,6 @@ export interface InstalledCodexAuth {
|
|
|
11
13
|
/** absolute path of the auth.json we wrote — caller passes this to the
|
|
12
14
|
* post-hook via core.saveState for refresh-detection later. */
|
|
13
15
|
authPath: string;
|
|
14
|
-
/** value to set as XDG_DATA_HOME for the OpenCode subprocess. */
|
|
15
|
-
xdgDataHome: string;
|
|
16
16
|
/** refresh_token from the env at materialization time. post-hook
|
|
17
17
|
* compares against the on-disk file after the run to detect whether
|
|
18
18
|
* OpenCode refreshed during the session (only happens on long runs
|
|
@@ -31,15 +31,13 @@ export interface InstalledCodexAuth {
|
|
|
31
31
|
* caller treats null as "no codex auth, fall through to API key flow".
|
|
32
32
|
*
|
|
33
33
|
* The env value is server-side guaranteed fresh by `maybeRotateCodexSecret`
|
|
34
|
-
* in the run-context endpoint. We parse + write it here and
|
|
35
|
-
* `
|
|
34
|
+
* in the run-context endpoint. We parse + write it here and record the dir
|
|
35
|
+
* (`getSubscriptionDataHome`) so every opencode subprocess discovers the
|
|
36
36
|
* auth.json; no refresh, no DB interaction. */
|
|
37
37
|
export declare function installCodexAuth(): InstalledCodexAuth | null;
|
|
38
38
|
export interface InstalledXaiAuth {
|
|
39
39
|
/** absolute path of the auth.json we wrote — the post-hook diffs it. */
|
|
40
40
|
authPath: string;
|
|
41
|
-
/** value to set as XDG_DATA_HOME for the OpenCode subprocess. */
|
|
42
|
-
xdgDataHome: string;
|
|
43
41
|
/** refresh_token at materialization time. opencode's XaiAuthPlugin rotates
|
|
44
42
|
* in-process on a long run, so the post-hook compares against this to decide
|
|
45
43
|
* whether anything needs writing back. */
|
package/dist/utils/gitAuth.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ type GitAuthOptions = {
|
|
|
22
22
|
* when present, an auth-class failure is retried ONCE with a fresh token.
|
|
23
23
|
*/
|
|
24
24
|
refreshGitToken?: ((stale: string) => Promise<string>) | undefined;
|
|
25
|
+
signal?: AbortSignal | undefined;
|
|
25
26
|
};
|
|
26
27
|
/**
|
|
27
28
|
* installation-token 401s on git. GitHub intermittently mints a token its git
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
* post-hook's stdlib-only import graph and bring back #815. Keep the two in
|
|
7
7
|
* sync by hand; `entryPost.stdlibOnly.test.ts` guards the reason they differ. */
|
|
8
8
|
export declare const OAUTH_WRITEBACK_STATE = "oauth_writeback";
|
|
9
|
+
/** GHA state key for the run's subscription data dir (codexHome.ts); `entryPost.ts` spells it literally too. */
|
|
10
|
+
export declare const SUBSCRIPTION_DATA_DIR_STATE = "subscription_data_dir";
|
|
9
11
|
/**
|
|
10
12
|
* Persist any OAuth refresh chain the run rotated, back into Pullfrog's own
|
|
11
13
|
* secret store.
|
|
@@ -22,6 +24,9 @@ export declare const OAUTH_WRITEBACK_STATE = "oauth_writeback";
|
|
|
22
24
|
* to write them back from inside a job — so a token stashed there silently
|
|
23
25
|
* goes stale on the first refresh and the next run fails. See wiki/codex-auth.md.
|
|
24
26
|
*
|
|
27
|
+
* Then removes the run's subscription data dir (codexHome.ts), which holds the
|
|
28
|
+
* auth.json diffed here.
|
|
29
|
+
*
|
|
25
30
|
* Best-effort throughout: a missed write-back costs the user one
|
|
26
31
|
* `pullfrog auth codex` re-run, while a throw here would fail a workflow whose
|
|
27
32
|
* agent already succeeded.
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HOME and XDG dirs for every opencode subprocess, all inside this run. the runner user's own
|
|
3
|
+
* are shared by every run on a self-hosted host, and so is opencode's SQLite db in them (#1475).
|
|
4
|
+
*/
|
|
5
|
+
export declare function openCodeHomeEnv(tmpdir: string): {
|
|
6
|
+
HOME: string;
|
|
7
|
+
XDG_CONFIG_HOME: string;
|
|
8
|
+
XDG_DATA_HOME: string;
|
|
9
|
+
XDG_CACHE_HOME: string;
|
|
10
|
+
XDG_STATE_HOME: string;
|
|
11
|
+
};
|
|
1
12
|
/**
|
|
2
13
|
* Why the last `opencode models` came back empty, in opencode's own words.
|
|
3
14
|
*
|
|
@@ -9,11 +20,17 @@
|
|
|
9
20
|
export declare function getModelsFailure(): string | undefined;
|
|
10
21
|
/** Snapshot the set of models OpenCode can serve from the current env, BEFORE
|
|
11
22
|
* Pullfrog-stored credentials are merged in. Call once early in `main.ts`. */
|
|
12
|
-
export declare function captureBaselineModels(
|
|
23
|
+
export declare function captureBaselineModels(params: {
|
|
24
|
+
cliPath: string;
|
|
25
|
+
tmpdir: string;
|
|
26
|
+
}): void;
|
|
13
27
|
/** Snapshot the set of models OpenCode can serve AFTER dbSecrets +
|
|
14
28
|
* Codex auth.json are in place. Logs the diff against the baseline as
|
|
15
29
|
* `» BYOK auth enabled N model(s): …`. */
|
|
16
|
-
export declare function captureAuthorizedModels(
|
|
30
|
+
export declare function captureAuthorizedModels(params: {
|
|
31
|
+
cliPath: string;
|
|
32
|
+
tmpdir: string;
|
|
33
|
+
}): void;
|
|
17
34
|
/** A model whose credential the pool just probed. Added directly: a second
|
|
18
35
|
* `opencode models` would run inside the checkout and load the repo's own
|
|
19
36
|
* `.opencode` plugins, which can outlast its timeout and empty the set. */
|
|
@@ -7,7 +7,8 @@ export declare const PROVIDER_NO_ENDPOINTS_LABEL = "provider no routable endpoin
|
|
|
7
7
|
* `PROVIDER_BILLING_EXHAUSTED_LABEL`: that label routes to a body whose CTA is
|
|
8
8
|
* "Top up your provider balance", and a capped ChatGPT or Zen plan has no
|
|
9
9
|
* balance to top up — it resets on its own. This label carries the cause into
|
|
10
|
-
* the headline and lets the generic body stand
|
|
10
|
+
* the hang headline and lets the generic body stand; a turn it ends directly
|
|
11
|
+
* gets its own wait-or-raise copy in `renderRunError` (#1474).
|
|
11
12
|
*/
|
|
12
13
|
export declare const PROVIDER_USAGE_LIMIT_LABEL = "provider usage limit reached";
|
|
13
14
|
export declare function findAnthropicSpendCap(text: string): {
|
|
@@ -35,6 +36,8 @@ export declare function isRouterKeylimitExhaustedError(text: string): boolean;
|
|
|
35
36
|
* is the user's own provider account.
|
|
36
37
|
*/
|
|
37
38
|
export declare function isProviderBillingExhausted(text: string): boolean;
|
|
39
|
+
/** A capped subscription window or a configured usage limit — see `PROVIDER_USAGE_LIMIT_LABEL`. */
|
|
40
|
+
export declare function isProviderUsageLimit(text: string): boolean;
|
|
38
41
|
/**
|
|
39
42
|
* OpenRouter accepted the request and found nowhere to send it: the account's
|
|
40
43
|
* data policy or guardrail settings exclude every provider serving the picked
|
|
@@ -13,19 +13,28 @@
|
|
|
13
13
|
* "key budget exhausted" string mid-run. Both render via
|
|
14
14
|
* `formatBillingErrorSummary` so the user sees actionable copy.
|
|
15
15
|
*
|
|
16
|
+
* 1a. Pre-rendered bodies — a throw site that already built the markdown
|
|
17
|
+
* (`MODEL_ACCESS_MARKER`, `SECRETS_UNAVAILABLE_MARKER`,
|
|
18
|
+
* `ROUTER_UNFUNDED_MARKER`, `CREDENTIAL_REJECTED_MARKER`,
|
|
19
|
+
* `CREDENTIAL_POOL_REFUSED_MARKER`) passes through verbatim on both surfaces.
|
|
20
|
+
*
|
|
16
21
|
* 2. BYOK provider billing-exhausted (#835) — DeepSeek "Insufficient
|
|
17
22
|
* Balance", Anthropic "credit balance is too low", OpenCode Zen
|
|
18
23
|
* `CreditsError`, Gemini "spending cap". Checked before api-key auth
|
|
19
24
|
* because billing-exhausted responses often carry 401 status codes
|
|
20
25
|
* that `isApiKeyAuthError` would otherwise mis-classify.
|
|
21
26
|
*
|
|
27
|
+
* 2a. Provider usage limit (#1474) — a capped subscription window or a
|
|
28
|
+
* configured workspace limit; "wait or raise it", never "top up".
|
|
29
|
+
*
|
|
22
30
|
* 3. API-key auth error — `isApiKeyAuthError` sniffs the raw error string
|
|
23
31
|
* (or the activity-timeout hang body when present, since that's where
|
|
24
32
|
* the underlying provider error often lands); `formatApiKeyErrorSummary`
|
|
25
33
|
* renders provider + console-link copy.
|
|
26
34
|
*
|
|
27
|
-
* 4. ProviderModelNotFoundError — configured model id
|
|
28
|
-
*
|
|
35
|
+
* 4. ProviderModelNotFoundError — configured model id not in the OpenCode
|
|
36
|
+
* catalog (opencode ≥1.18 words it `Model not found: <id>`, #1470);
|
|
37
|
+
* renders a nudge to pick a different model.
|
|
29
38
|
*
|
|
30
39
|
* 4a. No-provider-available (#1077) — the model IS in the catalog but the
|
|
31
40
|
* provider declines to route it on this account's plan (OpenCode Zen's
|
|
@@ -60,17 +69,24 @@
|
|
|
60
69
|
* event counts and a benign stderr tail are operator-grade detail that
|
|
61
70
|
* only alarm the average user.
|
|
62
71
|
*
|
|
63
|
-
* 6.
|
|
72
|
+
* 6. Unclassified provider error (#1474) — `errorMessage` starts with
|
|
73
|
+
* `provider error: `. opencode prefixes every provider-side turn failure
|
|
74
|
+
* with the provider's text; claude-code (a zero-event exit) and codex (no
|
|
75
|
+
* turn error, no stderr) prefix only our classifier label. Either names
|
|
76
|
+
* something the user can act on, so both surfaces get a lead line plus the
|
|
77
|
+
* raw text in a fenced block.
|
|
78
|
+
*
|
|
79
|
+
* 7. Default — the job summary gets a plain-English lead sentence plus the
|
|
64
80
|
* raw error in a fenced code block under the `### ❌ Pullfrog failed`
|
|
65
81
|
* banner; the PR comment collapses to the same one-line logs link as
|
|
66
82
|
* the hang case, since the raw internal string helps nobody on the PR.
|
|
67
83
|
*
|
|
68
|
-
* Net: the actionable classifications (billing, API-key,
|
|
69
|
-
* no-provider-available, context-overflow, run time limit,
|
|
70
|
-
* render identical bodies on
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* branding + rerun links.
|
|
84
|
+
* Net: the actionable classifications (billing, usage limit, API-key,
|
|
85
|
+
* model-not-found, no-provider-available, context-overflow, run time limit,
|
|
86
|
+
* transient-upstream, unclassified provider error) render identical bodies on
|
|
87
|
+
* both surfaces; the non-actionable ones (unexplained hang, generic) keep the
|
|
88
|
+
* forensics in the Actions job summary and show a calm one-liner in the PR
|
|
89
|
+
* comment, whose footer already carries Pullfrog branding + rerun links.
|
|
74
90
|
*/
|
|
75
91
|
import type { AgentDiagnostic } from "./agentHangReport.ts";
|
|
76
92
|
export type RenderedRunError = {
|