pullfrog 0.1.99 → 0.1.101

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.
@@ -6,7 +6,7 @@ export { CLI_CONTRACT_HEADER, CLI_CONTRACT_VERSION, CLI_UPGRADE_MESSAGE } from "
6
6
  export type { ConfigField, ConfigScope, ConfigValue } from "../configuration.ts";
7
7
  export { configEntrySchema, configFields, configResponseSchema, configValueSchema, } from "../configuration.ts";
8
8
  export type { AuthorPermission, AutoTier, EffortPosition, ModelAlias, ModelProvider, Payload, PayloadEvent, PayloadRouting, ProviderConfig, PushPermission, RouterSource, RouterTier, ShellPermission, ToolPermission, WriteablePayload, XrepoConfig, } from "../external.ts";
9
- export { AUTO_EFFICIENT, AUTO_INTELLIGENT, AUTO_ROUTER, autoLadder, autoRoutedSlug, DEFAULT_EFFORT_POSITION, DEFAULT_PROXY_MODEL, defaultAutoTier, EFFORT_ALIASES, getAutoSelectHintModel, getModelEffortLevels, getModelEnvVars, getModelManagedCredentials, getModelProvider, getProviderDisplayName, isAutoRouted, isAutoSlug, isAutoTier, isCardGatedModel, isEffortPosition, isOssAllowedModel, isRouterTier, ladderRungs, modelAliases, modelHasStoredAuth, modelIdLabel, OSS_MODEL_ALLOWLIST, OSS_RECOMMENDED_MODEL, offeredRungs, ossFundedModelNames, PROVIDER_GATEWAY_URL_ENV, parseEffortPosition, parseModel, providers, pullfrogMcpName, ROUTED_PROVIDERS, ROUTER_LADDER, ROUTER_TIERS, resolveAutoTier, resolveCliModel, resolveDisplayAlias, resolveModelRung, resolveModelSlug, resolveOpenRouterModel, resolvePickModel, resolveRoutedModel, resolveRung, routerProxyOptOut, rungLabel, rungPosition, SUBSIDY_RUNG, } from "../external.ts";
9
+ export { AUTO_EFFICIENT, AUTO_INTELLIGENT, AUTO_ROUTER, autoLadder, autoRoutedSlug, DEFAULT_EFFORT_POSITION, DEFAULT_PROXY_MODEL, defaultAutoTier, EFFORT_ALIASES, getAutoSelectHintModel, getModelEffortLevels, getModelEnvVars, getModelManagedCredentials, getModelProvider, getProviderDisplayName, isAutoRouted, isAutoSlug, isAutoTier, isCardGatedModel, isEffortPosition, isOssAllowedModel, isRouterTier, ladderRungs, modelAliases, modelHasStoredAuth, modelIdLabel, OSS_MODEL_ALLOWLIST, OSS_RECOMMENDED_MODEL, offeredRungs, ossFundedModelNames, PROVIDER_GATEWAY_URL_ENV, parseEffortPosition, parseModel, providers, pullfrogMcpName, ROUTED_PROVIDERS, ROUTER_LADDER, ROUTER_TIERS, resolveAutoTier, resolveCliModel, resolveDisplayAlias, resolveModelRung, resolveModelSlug, resolveOpenRouterModel, resolvePickModel, resolveRoutedModel, resolveRung, routerProxyOptOut, rungLabel, rungPosition, SUBSIDY_RUNG, TRIAL_FUNDED_USD_LIMIT, } from "../external.ts";
10
10
  export type { Mode } from "../modes.ts";
11
11
  export { modes } from "../modes.ts";
12
12
  export { type CommercialRefusal, commercialPaywallBody } from "../utils/billingErrors.ts";
@@ -16,9 +16,10 @@ export type { CodexAuthBody } from "../utils/codexOAuth.ts";
16
16
  export { decodeJwtExpMs, OAuthInvalidGrantError, parseCodexAuthBody, refreshCodexAuthBody, stringifyCodexAuthBody, } from "../utils/codexOAuth.ts";
17
17
  export type { CredentialVerdict } from "../utils/credentialCheck.ts";
18
18
  export { isCredentialProbeable, verifyCredential } from "../utils/credentialCheck.ts";
19
+ export { renderFailure } from "../utils/failureBody.ts";
19
20
  export type { ResourceUsage, UsageSummary } from "../utils/github.ts";
20
21
  export { isPullfrog } from "../utils/isPullfrog.ts";
21
- export { isLeapingIntoActionCommentBody, LEAPING_INTO_ACTION_PREFIX, } from "../utils/leapingComment.ts";
22
+ export { isLeapingIntoActionCommentBody, isLiveChecklistCommentBody, LEAPING_INTO_ACTION_PREFIX, } from "../utils/leapingComment.ts";
22
23
  export { MAX_LEARNINGS_LENGTH, truncateAtLineBoundary } from "../utils/learningsTruncate.ts";
23
24
  export { countOutstandingPullfrogThreads } from "../utils/outstandingThreads.ts";
24
25
  export type { CreateProgressCommentTarget, ProgressComment, ProgressCommentType, } from "../utils/progressComment.ts";
package/dist/internal.js CHANGED
@@ -8,7 +8,7 @@ var __export = (target, all) => {
8
8
  // package.json
9
9
  var package_default = {
10
10
  name: "pullfrog",
11
- version: "0.1.99",
11
+ version: "0.1.101",
12
12
  cliContractVersion: "1",
13
13
  type: "module",
14
14
  bin: {
@@ -14475,9 +14475,9 @@ var providers = {
14475
14475
  models: {
14476
14476
  "gemini-pro": {
14477
14477
  displayName: "Gemini Pro",
14478
- resolve: "google/gemini-3.1-pro-preview",
14478
+ resolve: "google/gemini-4-argon",
14479
14479
  effort: ["low", "medium", "high"],
14480
- openRouterResolve: "openrouter/google/gemini-3.1-pro-preview",
14480
+ openRouterResolve: "openrouter/google/gemini-4-argon",
14481
14481
  preferred: true
14482
14482
  // Inherit (subagents stay on Pro). Google has no in-between tier;
14483
14483
  // dropping to Flash for review work was a meaningful capability cliff
@@ -14667,7 +14667,7 @@ var providers = {
14667
14667
  displayName: "Kimi K3",
14668
14668
  description: "1M context, but only on Allegretto and above \xB7 Moderato caps it at 256K",
14669
14669
  resolve: "kimi-code-plan-cn/k3",
14670
- effort: ["low", "high", "max"],
14670
+ effort: ["none", "low", "high", "max"],
14671
14671
  subagentModel: "kimi-k2"
14672
14672
  },
14673
14673
  // `preferred` sits on the 256K variant rather than the 1M flagship, which
@@ -14682,7 +14682,7 @@ var providers = {
14682
14682
  displayName: "Kimi K3 256K",
14683
14683
  description: "Same model at 256K for ~half the quota \xB7 Moderato or above",
14684
14684
  resolve: "kimi-code-plan-cn/k3-256k",
14685
- effort: ["low", "high", "max"],
14685
+ effort: ["none", "low", "high", "max"],
14686
14686
  preferred: true,
14687
14687
  subagentModel: "kimi-k2"
14688
14688
  },
@@ -14690,9 +14690,10 @@ var providers = {
14690
14690
  displayName: "Kimi K2",
14691
14691
  description: "Included in every Kimi membership tier",
14692
14692
  resolve: "kimi-code-plan-cn/kimi-for-coding",
14693
- // models.dev published this ladder on 2026-09-14; the catalog gate
14694
- // mirrors it, so main was red until the alias carried it too.
14695
- effort: ["low", "high", "max"]
14693
+ // models.dev published `low, high, max` on 2026-09-14 and added `none`
14694
+ // on 2026-10-19; the catalog gate mirrors it, so main was red until the
14695
+ // alias carried it too.
14696
+ effort: ["none", "low", "high", "max"]
14696
14697
  },
14697
14698
  "kimi-k2-highspeed": {
14698
14699
  displayName: "Kimi K2 HighSpeed",
@@ -14862,9 +14863,9 @@ var providers = {
14862
14863
  },
14863
14864
  "gemini-pro": {
14864
14865
  displayName: "Gemini Pro",
14865
- resolve: "opencode/gemini-3.1-pro",
14866
+ resolve: "opencode/gemini-4-argon",
14866
14867
  effort: ["low", "medium", "high"],
14867
- openRouterResolve: "openrouter/google/gemini-3.1-pro-preview"
14868
+ openRouterResolve: "openrouter/google/gemini-4-argon"
14868
14869
  // Inherit — see google/gemini-pro for rationale.
14869
14870
  },
14870
14871
  "gemini-flash": {
@@ -15596,8 +15597,8 @@ var providers = {
15596
15597
  },
15597
15598
  "gemini-pro": {
15598
15599
  displayName: "Gemini Pro",
15599
- resolve: "vercel/google/gemini-3.1-pro-preview",
15600
- effort: ["low", "high"]
15600
+ resolve: "vercel/google/gemini-4-argon",
15601
+ effort: ["low", "medium", "high"]
15601
15602
  },
15602
15603
  "gemini-flash": {
15603
15604
  displayName: "Gemini Flash",
@@ -15865,6 +15866,13 @@ if (!defaultProxyAlias?.openRouterResolve) {
15865
15866
  }
15866
15867
  var DEFAULT_PROXY_MODEL = defaultProxyAlias.openRouterResolve;
15867
15868
  var defaultProxyDisplayName = defaultProxyAlias.displayName;
15869
+ var trialProxyAlias = resolveDisplayAlias("openai/gpt-luna");
15870
+ if (!trialProxyAlias?.openRouterResolve) {
15871
+ throw new Error("TRIAL_PROXY_MODEL: openai/gpt-luna has no openRouterResolve");
15872
+ }
15873
+ var TRIAL_PROXY_MODEL = trialProxyAlias.openRouterResolve;
15874
+ var TRIAL_PROXY_DISPLAY_NAME = trialProxyAlias.displayName;
15875
+ var TRIAL_FUNDED_USD_LIMIT = 10;
15868
15876
  for (const [name, ladder] of Object.entries({ ...PROVIDER_LADDERS, router: ROUTER_LADDER })) {
15869
15877
  const terminal = /* @__PURE__ */ new Set();
15870
15878
  for (const slug of Object.values(ladder)) {
@@ -16306,6 +16314,36 @@ ${PR_SUMMARY_FORMAT}`
16306
16314
  }
16307
16315
  var modes = computeModes("opencode");
16308
16316
 
16317
+ // utils/failureBody.ts
16318
+ function renderFailure(params) {
16319
+ const blocks = [
16320
+ params.detail ? `**${params.headline}** ${params.detail}` : `**${params.headline}**`
16321
+ ];
16322
+ if (params.list) blocks.push(params.list.map((item) => `- ${item}`).join("\n"));
16323
+ if (params.error) blocks.push(fenced(params.error));
16324
+ if (params.details) {
16325
+ blocks.push(
16326
+ `<details><summary>${params.details.summary}</summary>
16327
+
16328
+ ${fenced(params.details.text)}
16329
+
16330
+ </details>`
16331
+ );
16332
+ }
16333
+ if (params.links?.length) {
16334
+ blocks.push(params.links.map((link) => `[${link.label} \u2192](${link.url})`).join(" \xB7 "));
16335
+ }
16336
+ return blocks.join("\n\n");
16337
+ }
16338
+ function fenced(text) {
16339
+ let longest = 0;
16340
+ for (const match of text.matchAll(/`+/g)) longest = Math.max(longest, match[0].length);
16341
+ const fence = "`".repeat(Math.max(3, longest + 1));
16342
+ return `${fence}
16343
+ ${text}
16344
+ ${fence}`;
16345
+ }
16346
+
16309
16347
  // utils/billingErrors.ts
16310
16348
  function commercialPaywallBody(params) {
16311
16349
  let action;
@@ -16347,7 +16385,7 @@ function formatModelLabel(params) {
16347
16385
  }
16348
16386
  const base = alias?.isFree ? `\`${modelName}\` (free)` : `\`${modelName}\``;
16349
16387
  if (params.clamped?.reason === "trial") {
16350
- return `${base} (model usage covered by Pullfrog)`;
16388
+ return `${base} (free trial credits)`;
16351
16389
  }
16352
16390
  if (params.clamped) {
16353
16391
  const target = isAutoTier(params.clamped.from) ? "the intelligent tier" : `\`${resolveDisplayAlias(params.clamped.from)?.displayName ?? params.clamped.from}\``;
@@ -16358,13 +16396,14 @@ function formatModelLabel(params) {
16358
16396
  }
16359
16397
  return base;
16360
16398
  }
16361
- function buildTrialDisclosure(owner) {
16362
- const consoleUrl = owner ? `https://pullfrog.com/console/${owner}` : "https://pullfrog.com/console";
16399
+ function buildTrialDisclosure(params) {
16400
+ const consoleUrl = params.owner ? `https://pullfrog.com/console/${params.owner}#billing` : "https://pullfrog.com/console";
16401
+ const left = params.creditsLeftUsd === void 0 ? "" : ` ($${Math.max(0, params.creditsLeftUsd).toFixed(2)} of $${TRIAL_FUNDED_USD_LIMIT} left)`;
16363
16402
  return [
16364
16403
  "---",
16365
16404
  "",
16366
16405
  "> [!IMPORTANT]",
16367
- `> **Pullfrog covered this run's model usage.** \`DeepSeek Flash\` is fast and cheap \u2014 expect lighter work than a frontier model. This model allowance is temporary and separate from your Pullfrog plan. [Connect a model-provider subscription or API key \u2192](${consoleUrl})`
16406
+ `> **This run used your free trial credits**${left} on \`${TRIAL_PROXY_DISPLAY_NAME}\`, a fast, lightweight model. [Connect a provider \u2192](${consoleUrl}) to run the model you choose.`
16368
16407
  ].join("\n");
16369
16408
  }
16370
16409
  function buildPullfrogFooter(params) {
@@ -16387,13 +16426,13 @@ function buildPullfrogFooter(params) {
16387
16426
  `Using ${formatModelLabel({
16388
16427
  model: params.model,
16389
16428
  clamped: params.clamped,
16390
- unselectedProxyDefault: params.unselectedProxyDefault,
16429
+ unselectedProxyDefault: params.unselectedProxyDefault && !params.failed,
16391
16430
  oss: params.oss
16392
16431
  })}`
16393
16432
  );
16394
16433
  }
16395
- const allParts = [...parts, "[\u{1D54F}](https://x.com/pullfrogai)"];
16396
- const disclosure = params.clamped?.reason === "trial" ? `${buildTrialDisclosure(params.owner)}
16434
+ const allParts = params.failed ? parts : [...parts, "[\u{1D54F}](https://x.com/pullfrogai)"];
16435
+ const disclosure = params.clamped?.reason === "trial" && !params.failed ? `${buildTrialDisclosure({ owner: params.owner, creditsLeftUsd: params.clamped.creditsLeftUsd })}
16397
16436
 
16398
16437
  ` : "";
16399
16438
  return `
@@ -16641,6 +16680,10 @@ function isLeapingIntoActionCommentBody(body) {
16641
16680
  const firstLine = content.split(/\r?\n/, 1)[0]?.trimEnd() ?? "";
16642
16681
  return new RegExp(`(^|\\s)${LEAPING_INTO_ACTION_PREFIX}(\\.\\.\\.)?$`).test(firstLine);
16643
16682
  }
16683
+ var TODO_SPINNER_URL = "https://uploads.pullfrog.com/Progress%20Indicator.gif";
16684
+ function isLiveChecklistCommentBody(body) {
16685
+ return body.includes(TODO_SPINNER_URL);
16686
+ }
16644
16687
 
16645
16688
  // utils/learningsTruncate.ts
16646
16689
  var MAX_LEARNINGS_LENGTH = 1e5;
@@ -16925,6 +16968,8 @@ var PROVIDER_ERROR_PATTERNS = [
16925
16968
  },
16926
16969
  // OpenAI's `You have no credits remaining` (#1474).
16927
16970
  { regex: /no credits remaining/i, label: PROVIDER_BILLING_EXHAUSTED_LABEL },
16971
+ // Codex on a ChatGPT workspace: `Your workspace is out of credits. Add credits to continue.`
16972
+ { regex: /workspace is out of credits/i, label: PROVIDER_BILLING_EXHAUSTED_LABEL },
16928
16973
  { regex: /credit balance is too low/i, label: PROVIDER_BILLING_EXHAUSTED_LABEL },
16929
16974
  // "spending cap" (Gemini) and "spending limit" (xAI, #1076) are the same
16930
16975
  // condition in two house styles — a configured ceiling was reached.
@@ -16954,6 +16999,9 @@ var PROVIDER_ERROR_PATTERNS = [
16954
16999
  // succeeds" while the same log held nine of these. Measured on a live account
16955
17000
  // whose ChatGPT subscription was capped.
16956
17001
  { regex: /\bthe usage limit has been reached\b/i, label: PROVIDER_USAGE_LIMIT_LABEL },
17002
+ // the Codex CLI's wording for the same capped plan: `You’ve hit your usage limit. … try
17003
+ // again at 7:58 AM.` Claude's `hit your session limit` names its window and stays out.
17004
+ { regex: /\bhit your usage limit\b/i, label: PROVIDER_USAGE_LIMIT_LABEL },
16957
17005
  // the OpenRouter shapes `isRouterKeylimitExhaustedError` matches. on a Router
16958
17006
  // run those mean the PULLFROG wallet is empty and `renderRunError` returns a
16959
17007
  // `BillingError` before ever reaching this list; on a BYOK run the very same
@@ -17481,6 +17529,7 @@ export {
17481
17529
  SUBSIDY_RUNG,
17482
17530
  SubscriptionCredentialError,
17483
17531
  TIMEOUT_DISABLED,
17532
+ TRIAL_FUNDED_USD_LIMIT,
17484
17533
  autoLadder,
17485
17534
  autoRoutedSlug,
17486
17535
  buildPullfrogFooter,
@@ -17513,6 +17562,7 @@ export {
17513
17562
  isCredentialProbeable,
17514
17563
  isEffortPosition,
17515
17564
  isLeapingIntoActionCommentBody,
17565
+ isLiveChecklistCommentBody,
17516
17566
  isOssAllowedModel,
17517
17567
  isPullfrog,
17518
17568
  isRouterTier,
@@ -17534,6 +17584,7 @@ export {
17534
17584
  pullfrogMcpName,
17535
17585
  refreshCodexAuthBody,
17536
17586
  refreshXaiAuthBody,
17587
+ renderFailure,
17537
17588
  resolveAutoTier,
17538
17589
  resolveCliModel,
17539
17590
  resolveDisplayAlias,
package/dist/models.d.ts CHANGED
@@ -354,6 +354,10 @@ export declare function resolveModelRung(params: {
354
354
  useOpenRouter: boolean;
355
355
  }): string | undefined;
356
356
  export declare const DEFAULT_PROXY_MODEL: string;
357
+ export declare const TRIAL_PROXY_MODEL: string;
358
+ export declare const TRIAL_PROXY_DISPLAY_NAME: string;
359
+ /** lifetime cap, in USD, on the model usage Pullfrog funds for an account's trial runs. */
360
+ export declare const TRIAL_FUNDED_USD_LIMIT = 10;
357
361
  /**
358
362
  * the models Pullfrog is willing to fund on the OSS program. an accepted OSS
359
363
  * repo may pick any of these and run-context honors the pick; anything else
@@ -1,6 +1,7 @@
1
1
  import type { AgentCredential, AgentUsage } from "./agents/shared.ts";
2
2
  import type { PrepResult } from "./prep/types.ts";
3
3
  import type { AgentDiagnostic } from "./utils/agentHangReport.ts";
4
+ import type { ModelClamp } from "./utils/buildPullfrogFooter.ts";
4
5
  import type { DiffCoverageState } from "./utils/diffCoverage.ts";
5
6
  import { type ProgressComment, type ProgressCommentType } from "./utils/progressComment.ts";
6
7
  import type { TodoTracker } from "./utils/todoTracking.ts";
@@ -140,10 +141,7 @@ export interface ToolState {
140
141
  agent?: string | undefined;
141
142
  credential?: AgentCredential | undefined;
142
143
  unselectedProxyDefault?: boolean | undefined;
143
- modelClamped?: {
144
- from: string;
145
- reason: "card" | "noRouterPath" | "oss" | "trial";
146
- } | undefined;
144
+ modelClamped?: ModelClamp | undefined;
147
145
  oss?: boolean | undefined;
148
146
  todoTracker?: TodoTracker | undefined;
149
147
  agentDiagnostic?: AgentDiagnostic | undefined;
@@ -33,19 +33,15 @@ export type AgentDiagnostic = {
33
33
  sawModelOutput: boolean;
34
34
  };
35
35
  /**
36
- * Build a user-facing markdown body for an agent hang or failure.
36
+ * The body for a run the activity watchdog ended. Rendered into both the PR
37
+ * progress comment and the job summary; `null` when the harness left no
38
+ * diagnostic, so the caller falls back to its bare-error rendering.
37
39
  *
38
- * Rendered into both the PR progress comment and the GitHub Actions job
39
- * summary. Returns `null` when no diagnostic is available, which signals to
40
- * the caller to fall back to its bare-error rendering.
41
- *
42
- * `errorMessage` is the underlying timer / spawn reject string (e.g.
43
- * `activity timeout: no output for 301s`). The idle seconds are parsed out
44
- * of it for the hang explanation — total runtime would overstate the stall
45
- * for runs that streamed for a long time before going quiet.
40
+ * The headline says whose side went quiet, because a silent run is nearly
41
+ * always the provider's and "Pullfrog stalled" read as ours. The agent's last
42
+ * output goes in a collapsed section for whoever wants it.
46
43
  */
47
44
  export declare function formatAgentHangBody(input: {
48
45
  diagnostic: AgentDiagnostic | undefined;
49
- isHang: boolean;
50
46
  errorMessage: string;
51
47
  }): string | null;
@@ -1,9 +1,10 @@
1
+ import { type FailureLink } from "./failureBody.ts";
1
2
  import type { CredentialCandidate } from "./subscriptionCredentials.ts";
2
3
  import type { ProbeVerdict } from "./subscriptionProbe.ts";
3
4
  /** shared sentence in the four `resolveSlug` routing-slug throws. */
4
- export declare const BYOK_SLUG_MARKER = "env var is required when the model is set to";
5
+ export declare const BYOK_SLUG_MARKER = "must be set when the model is";
5
6
  /** shared sentence in the four `validate*Setup` throws. */
6
- export declare const BYOK_CONFIG_MARKER = "selected but required configuration is missing:";
7
+ export declare const BYOK_CONFIG_MARKER = "is missing required settings:";
7
8
  /**
8
9
  * A BYOK provider setup is incomplete, at either of the two points that catch it:
9
10
  * `resolveSlug` refusing a routing slug whose backing env var is unset, and the
@@ -28,14 +29,35 @@ export declare function isByokSetupError(text: string): boolean;
28
29
  * `MODEL_ACCESS_MARKER`) because blaming the user for a transient fetch failure
29
30
  * on our side is the wrong CTA — their key is configured and still stored.
30
31
  */
31
- export declare const SECRETS_UNAVAILABLE_MARKER = "couldn't load your Pullfrog secrets";
32
+ export declare const SECRETS_UNAVAILABLE_MARKER = "couldn't load your stored secrets";
32
33
  /**
33
34
  * marker for the "your Router wallet is empty AND you have no provider key"
34
35
  * body. its own marker rather than the missing-key one because
35
36
  * `formatApiKeyErrorSummary` rebuilds any body carrying `MISSING_KEY_MARKER`
36
37
  * that doesn't start with it, which would silently discard this copy.
37
38
  */
38
- export declare const ROUTER_UNFUNDED_MARKER = "your Pullfrog Router balance is empty";
39
+ export declare const ROUTER_UNFUNDED_MARKER = "Your Pullfrog Router balance is empty";
40
+ /**
41
+ * marker for the body a trial account gets once its funded credits are spent:
42
+ * at the late fallback, or when the funded key runs out mid-run. Without it the
43
+ * first is told to add an API key and the second that a Router wallet it never
44
+ * had went negative, and neither mentions the trial.
45
+ */
46
+ export declare const TRIAL_SPENT_MARKER = "reached the limit of your free trial credits";
47
+ export declare function buildTrialSpentError(params: {
48
+ owner: string;
49
+ }): string;
50
+ /** the repo's console page: its model, instructions and scripts. */
51
+ export declare function repoConsoleUrl(repo: {
52
+ owner: string;
53
+ name: string;
54
+ }): string;
55
+ export declare function pickModelLink(repo: {
56
+ owner: string;
57
+ name: string;
58
+ }): FailureLink;
59
+ /** the account console's billing card: provider keys, subscriptions and Router credit. */
60
+ export declare function providerLink(owner: string): FailureLink;
39
61
  /**
40
62
  * marker for a credential the provider explicitly REJECTED before the agent
41
63
  * started. needs a verbatim guard for the same reason as its three siblings,
@@ -76,6 +98,7 @@ export declare const CREDENTIAL_POOL_REFUSED_MARKER = "No configured credential
76
98
  */
77
99
  export declare class NoUsableCredentialError extends Error {
78
100
  }
101
+ export declare function buildSecretsUnavailableError(): string;
79
102
  /**
80
103
  * Validate that the resolved model can actually be served by the chosen
81
104
  * agent. For routing slugs (Bedrock / Vertex) the auth shape is multi-var
@@ -160,6 +183,8 @@ export declare function isMalformedKeyError(text: string): boolean;
160
183
  * Anthropic entitlement denial for Claude Pro/Max subscription auth (#1072):
161
184
  * an org admin turned off Claude Code subscription access. no credential the
162
185
  * user controls fixes it, so it gets its own copy instead of the re-auth CTA.
186
+ * The probe sees the same denial as a 403, `OAuth authentication is currently
187
+ * not allowed for this organization`, which used to be answered "sign in again".
163
188
  */
164
189
  export declare function isClaudeSubscriptionDisabledError(text: string): boolean;
165
190
  /**
@@ -178,6 +203,15 @@ export declare function isClaudeSubscriptionDisabledError(text: string): boolean
178
203
  */
179
204
  export declare function isKimiPlanTierError(text: string): boolean;
180
205
  export declare function isClaudeSessionLimitError(text: string): boolean;
206
+ /** the subscription that serves `model`'s provider, named by the env var that carries it. */
207
+ export declare function subscriptionFor(model: string | undefined): {
208
+ label: string;
209
+ command: string;
210
+ provider: string;
211
+ env: string;
212
+ } | undefined;
213
+ /** a provider id's display name, e.g. `openai` → OpenAI. */
214
+ export declare function providerDisplayName(id: string | null | undefined): string | undefined;
181
215
  /**
182
216
  * The provider rejected a credential we checked before the agent started, and
183
217
  * nothing else on the account can serve this run. Names the credential and the
@@ -201,8 +235,8 @@ export type RefusedCredential = {
201
235
  };
202
236
  /**
203
237
  * 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.
238
+ * none can serve it. One sentence per credential, from its kind and status —
239
+ * never inferred from the prose — with the probe's own verdicts collapsed below.
206
240
  */
207
241
  export declare function buildCredentialPoolRefusedError(params: {
208
242
  model: string;
@@ -213,10 +247,16 @@ export declare function buildCredentialPoolRefusedError(params: {
213
247
  /**
214
248
  * Friendly Markdown summary for both the missing-key and invalid-key cases.
215
249
  * Used in the catch / result-failure paths in `main.ts` to overwrite the raw
216
- * agent error before it's posted to the PR progress comment.
250
+ * agent error before it's posted to the PR progress comment. `model` names the
251
+ * subscription in play — guessing it from the env once told a GPT run its Claude
252
+ * subscription was rejected — and `output`, the provider's own words, is
253
+ * collapsed under the copy.
217
254
  */
218
255
  export declare function formatApiKeyErrorSummary(params: {
219
256
  owner: string;
220
257
  name: string;
221
258
  raw: string;
259
+ model?: string | undefined;
260
+ credential?: string | undefined;
261
+ output?: string | undefined;
222
262
  }): string;
@@ -41,7 +41,9 @@ export declare class BillingError extends Error {
41
41
  * GH Actions apply whatever retry policy the workflow has configured.
42
42
  */
43
43
  export declare class TransientError extends Error {
44
- constructor(message: string);
44
+ /** the customer-facing follow-up to `message`, which is the headline. */
45
+ detail: string;
46
+ constructor(message: string, detail?: string);
45
47
  }
46
48
  /**
47
49
  * Why the org commercial gate refused a run (billing model v2), resolved by
@@ -114,6 +116,7 @@ export declare function formatBillingErrorSummary(error: BillingError, owner: st
114
116
  /**
115
117
  * Render a TransientError as user-facing markdown. Distinct framing from
116
118
  * BillingError so the user doesn't read an alarm and assume their card
117
- * failed, this branch is "our fault, retry shortly", not theirs.
119
+ * failed, this branch is "our fault, retry shortly", not theirs. Both parts are
120
+ * ours and customer-ready; the server's own wording goes to the log.
118
121
  */
119
- export declare function formatTransientErrorSummary(error: TransientError, owner: string): string;
122
+ export declare function formatTransientErrorSummary(error: TransientError): string;
@@ -1,4 +1,18 @@
1
1
  export declare const PULLFROG_DIVIDER = "<!-- PULLFROG_DIVIDER_DO_NOT_REMOVE_PLZ -->";
2
+ /**
3
+ * A run whose configured model was swapped for one Pullfrog could fund. `trial`
4
+ * carries the account's remaining trial credits, as run-context counted them
5
+ * when the run started, so the disclosure can say how much is left; undefined
6
+ * when the server predates the figure.
7
+ */
8
+ export type ModelClamp = {
9
+ from: string;
10
+ reason: "card" | "noRouterPath" | "oss";
11
+ } | {
12
+ from: string;
13
+ reason: "trial";
14
+ creditsLeftUsd: number | undefined;
15
+ };
2
16
  export interface WorkflowRunFooterInfo {
3
17
  owner: string;
4
18
  repo: string;
@@ -27,10 +41,7 @@ export interface BuildPullfrogFooterParams {
27
41
  * provider-key nudge — so the downgrade is visible rather than silently
28
42
  * presenting Kimi as the pick.
29
43
  */
30
- clamped?: {
31
- from: string;
32
- reason: "card" | "noRouterPath" | "oss" | "trial";
33
- } | undefined;
44
+ clamped?: ModelClamp | undefined;
34
45
  /**
35
46
  * true when the run used the default proxy model only because no model was
36
47
  * selected (Router billing + "auto"). the footer appends a note nudging the
@@ -47,6 +58,8 @@ export interface BuildPullfrogFooterParams {
47
58
  /** repo owner, used to deep-link the trial disclosure at that account's own
48
59
  * console rather than the generic landing page. */
49
60
  owner?: string | undefined;
61
+ /** a failure comment: no 𝕏 link, model nudge or trial disclosure under an error. */
62
+ failed?: boolean | undefined;
50
63
  }
51
64
  /**
52
65
  * build a pullfrog footer with configurable parts
@@ -2,7 +2,6 @@ import { type ToolState } from "../toolState.ts";
2
2
  interface ReportErrorParams {
3
3
  toolState: ToolState;
4
4
  error: string;
5
- title?: string;
6
5
  /**
7
6
  * When the run has no pre-existing progress comment to update (silent
8
7
  * IncrementalReview / pull_request_synchronize, mode-less polls), create
@@ -0,0 +1,27 @@
1
+ export type FailureLink = {
2
+ label: string;
3
+ url: string;
4
+ };
5
+ /**
6
+ * The one shape every failure comment takes: a bold, sentence-case headline that
7
+ * names the cause, then at most a short paragraph or a list of two or more, then
8
+ * the error or the tool's own output, then at most two links. Built here rather
9
+ * than per message so those rules hold for every message at once.
10
+ *
11
+ * `error` is shown open, for when the error IS the explanation (an unrecognized
12
+ * failure). `details` is collapsed, for output that backs up a headline we
13
+ * already wrote. Free of imports: `billingErrors.ts` reaches client bundles
14
+ * through `action/internal`.
15
+ */
16
+ export declare function renderFailure(params: {
17
+ headline: string;
18
+ detail?: string | undefined;
19
+ list?: [string, string, ...string[]] | undefined;
20
+ error?: string | undefined;
21
+ details?: {
22
+ summary: string;
23
+ text: string;
24
+ } | undefined;
25
+ links?: [] | [FailureLink] | [FailureLink, FailureLink] | undefined;
26
+ }): string;
27
+ export declare const DISCORD_LINK: FailureLink;
@@ -9,3 +9,12 @@
9
9
  */
10
10
  export declare const LEAPING_INTO_ACTION_PREFIX = "Leaping into action";
11
11
  export declare function isLeapingIntoActionCommentBody(body: string): boolean;
12
+ /** the spinner on a to-do list's in-progress item. */
13
+ export declare const TODO_SPINNER_URL = "https://uploads.pullfrog.com/Progress%20Indicator.gif";
14
+ /**
15
+ * A live to-do list: one item still carries the spinner. Every finished body — a
16
+ * final summary, a salvaged answer, an error report — renders with in-progress
17
+ * items completed, so the spinner is only ever on a list whose run is still going
18
+ * or died without writing again.
19
+ */
20
+ export declare function isLiveChecklistCommentBody(body: string): boolean;
@@ -61,6 +61,7 @@ export declare function runProxyResolution(ctx: {
61
61
  export declare function resolveTrialFallback(ctx: {
62
62
  payload: ResolvedPayload;
63
63
  configuredModel: string | undefined;
64
+ creditsLeftUsd: number | undefined;
64
65
  oidcCredentials: OidcCredentials | null;
65
66
  repo: {
66
67
  owner: string;
@@ -42,8 +42,9 @@ import type { GitHubAppPermissions } from "./github.ts";
42
42
  * wiki/autonomous-maintenance.md's "no injected instruction can cause a
43
43
  * merge" still true. Legitimate pushes go through `push_branch`, which
44
44
  * enforces the branch rules.
45
- * - `checks` is absent for the same class of reason: no human role grants it,
46
- * and it would let the agent post its own passing `pullfrog-approval` status
45
+ * - `checks` is `read`, never `write`, for the same class of reason: every role
46
+ * can read a PR's check runs, but no human role can write one, and write would
47
+ * let the agent post its own passing `pullfrog-approval` status
47
48
  * (see wiki/review-approval.md).
48
49
  * - Every qualifying role therefore receives the SAME set. The role decides
49
50
  * whether a token is minted at all, not how wide it is — `admin`, `maintain`
@@ -100,6 +100,9 @@ export interface RunContext {
100
100
  * run — the runner may mint a subsidized efficient-tier key if, and only if,
101
101
  * its own key search comes up dry. see the run-context route. */
102
102
  trialFallback?: boolean | undefined;
103
+ /** the trial account's remaining funded credits, in USD, when it is inside its
104
+ * trial at all. zero or below means the credits are used up. */
105
+ trialCreditsLeftUsd?: number | undefined;
103
106
  }
104
107
  /**
105
108
  * fetch run context from Pullfrog API
@@ -29,6 +29,8 @@ export interface RunContextData {
29
29
  * own key search comes up dry. a permission, not a routing decision — the
30
30
  * server cannot see workflow `env:` keys. see `RunContext.trialFallback`. */
31
31
  trialFallback?: boolean | undefined;
32
+ /** see `RunContext.trialCreditsLeftUsd`. */
33
+ trialCreditsLeftUsd?: number | undefined;
32
34
  }
33
35
  interface ResolveRunContextDataParams {
34
36
  octokit: OctokitWithPlugins;