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/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.96",
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. OpenRouter's listing: prompts and
14984
- // completions may be retained by the provider, not trained on. unlike the
14985
- // first two it publishes an effort ladder, so the gate mirrors one here.
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; prompts may be retained",
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
- // has not published its data terms yet, so the description claims none.
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, free for a limited time",
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 three parts, in this order:
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. Never put it in Nitpicks: before submitting, re-read each Nitpicks bullet and move any that fails this test inline.
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, or Nitpicks only):
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 + optional \`### \u2139\uFE0F Nitpicks\`) \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.
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, or Nitpicks only): 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.
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
- { regex: /Insufficient (?:balance|credits)/i, label: PROVIDER_BILLING_EXHAUSTED_LABEL },
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.
@@ -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.
@@ -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
@@ -6,6 +6,7 @@ type CreateChangeImpactParams = {
6
6
  pullNumber: number;
7
7
  baseSha: string;
8
8
  headSha: string;
9
+ signal: AbortSignal;
9
10
  };
10
11
  type ChangeImpactResult = {
11
12
  path: string;
@@ -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 set
35
- * `process.env.XDG_DATA_HOME` so every opencode subprocess discovers the
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. */
@@ -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(cliPath: string): void;
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(cliPath: string): void;
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 no longer in the
28
- * OpenCode catalog; renders a nudge to pick a different model.
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. Default — the job summary gets a plain-English lead sentence plus the
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, model-not-found,
69
- * no-provider-available, context-overflow, run time limit, transient-upstream)
70
- * render identical bodies on both surfaces; the non-actionable ones (unexplained hang,
71
- * generic) keep the forensics in the Actions job summary and show a calm
72
- * one-liner in the PR comment, whose footer already carries Pullfrog
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 = {
@@ -51,6 +51,7 @@ export interface SpawnOptions {
51
51
  killGroup?: boolean;
52
52
  retain?: RetainMode;
53
53
  maxRetainedBytes?: number;
54
+ signal?: AbortSignal | undefined;
54
55
  }
55
56
  /**
56
57
  * Bounded string accumulator that keeps the tail of appended chunks.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pullfrog",
3
- "version": "0.1.96",
3
+ "version": "0.1.97",
4
4
  "cliContractVersion": "1",
5
5
  "type": "module",
6
6
  "bin": {