pullfrog 0.1.95 → 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.
@@ -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. */
@@ -9,6 +9,9 @@ import type { ToolContext } from "../mcp/server.ts";
9
9
  * `Repo.model` slug), PATCHed once at end-of-run so per-model cost analytics
10
10
  * don't parse the audit-only `payload`.
11
11
  * Keep in sync with `STRING_FIELDS` in `app/api/workflow-run/[runId]/route.ts`.
12
+ * `succeeded` and the usage fields below are this start's own report: a custom
13
+ * workflow can start the action several times in one run, and the server keeps
14
+ * one row per start, keyed by `START_KEY`, and sums them onto the run.
12
15
  */
13
16
  declare const STRING_KEYS: readonly ["prNodeId", "issueNodeId", "reviewNodeId", "planCommentNodeId", "summarySnapshot", "model", "agent", "credential"];
14
17
  /**
@@ -18,7 +21,11 @@ declare const STRING_KEYS: readonly ["prNodeId", "issueNodeId", "reviewNodeId",
18
21
  * amounts. Keep in sync with `INT_FIELDS` + `DECIMAL_FIELDS` in the server route.
19
22
  */
20
23
  declare const NUMBER_KEYS: readonly ["inputTokens", "outputTokens", "cacheReadTokens", "cacheWriteTokens", "costUsd"];
21
- export type WorkflowRunPatch = Partial<Record<(typeof STRING_KEYS)[number], string>> & Partial<Record<(typeof NUMBER_KEYS)[number], number>>;
24
+ /** one-way flags, only ever sent as `true`. Keep in sync with `BOOLEAN_FIELDS` in the server route. */
25
+ declare const FLAG_KEYS: readonly ["reviewSkipped"];
26
+ export type WorkflowRunPatch = Partial<Record<(typeof STRING_KEYS)[number], string>> & Partial<Record<(typeof NUMBER_KEYS)[number], number>> & Partial<Record<(typeof FLAG_KEYS)[number], true>> & {
27
+ succeeded?: boolean;
28
+ };
22
29
  /** PATCH workflow-run fields (Pullfrog JWT, not GitHub). */
23
30
  export declare function patchWorkflowRunFields(ctx: ToolContext, fields: WorkflowRunPatch): Promise<void>;
24
31
  /**
@@ -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
@@ -3,6 +3,11 @@ import type { RouterTier } from "../models.ts";
3
3
  import type { CommercialRefusal } from "./billingErrors.ts";
4
4
  import type { RepoContext } from "./github.ts";
5
5
  import type { CredentialAccess } from "./subscriptionCredentials.ts";
6
+ /**
7
+ * One per process, which is one start of the action within its GitHub run. Sent to run-context
8
+ * first, so a custom run's start is recorded before it reports, and then on each workflow-run PATCH.
9
+ */
10
+ export declare const START_KEY: `${string}-${string}-${string}-${string}-${string}`;
6
11
  export interface Mode {
7
12
  id: string;
8
13
  name: string;
@@ -108,6 +113,9 @@ export declare function fetchRunContext(params: {
108
113
  /** `payload.type` — lets the server apply this repo's per-trigger model
109
114
  * override, which it cannot derive from owner/repo alone. */
110
115
  runType?: string | undefined;
116
+ /** a plain-text prompt, which no Pullfrog dispatch sends — so the server
117
+ * can record this run without waiting for a reservation to claim it. */
118
+ plainPrompt: boolean;
111
119
  /** `payload.routing.tier` — the model router's tier, which run-context
112
120
  * applies to the Router proxy mint the same way it applies `runType`. */
113
121
  routedTier?: RouterTier | undefined;
@@ -37,6 +37,8 @@ interface ResolveRunContextDataParams {
37
37
  * because run-context is fetched first and needs it to pick this trigger's
38
38
  * model override. */
39
39
  runType?: string | undefined;
40
+ /** the prompt is plain text, never a Pullfrog dispatch payload. */
41
+ plainPrompt: boolean;
40
42
  /** the model router's tier from the payload, forwarded so run-context applies it to the proxy mint. */
41
43
  routedTier?: RouterTier | undefined;
42
44
  }
@@ -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 = {
@@ -32,6 +32,14 @@
32
32
  * See wiki/review-approval.md.
33
33
  */
34
34
  export declare const RUN_STATUS_CHECK_NAME = "pullfrog";
35
+ /**
36
+ * GitHub's own Actions app. It publishes a check run per job, named after the
37
+ * job — and the generated workflow's job is also called `pullfrog`, so the
38
+ * collision is structural rather than incidental. Renaming our check is not
39
+ * available: it was tried on 2026-08-02 and blocked merges on four customer
40
+ * repos (see above), so the reuse lookup filters instead. See #1196.
41
+ */
42
+ export declare const GITHUB_ACTIONS_APP_SLUG = "github-actions";
35
43
  /** the review-verdict check. opt-in, terminal-only, and deliberately separate from the above. */
36
44
  export declare const APPROVAL_CHECK_NAME = "pullfrog-approval";
37
45
  /**
@@ -147,6 +155,8 @@ export declare function finalizeRunStatusCheck(params: {
147
155
  conclusion: RunStatusCheckConclusion;
148
156
  detailsUrl: string | undefined;
149
157
  reviewUrl?: string | undefined;
158
+ /** the run's own final report, shown on success when nothing else carries it. */
159
+ note?: string | undefined;
150
160
  }): Promise<void>;
151
161
  /**
152
162
  * Post an already-terminal check-run in one call.
@@ -163,6 +173,7 @@ export declare function createTerminalRunStatusCheck(params: {
163
173
  conclusion: RunStatusCheckConclusion;
164
174
  detailsUrl: string | undefined;
165
175
  reviewUrl?: string | undefined;
176
+ note?: string | undefined;
166
177
  }): Promise<void>;
167
178
  /**
168
179
  * Whether this check still needs finalizing.
@@ -11,7 +11,8 @@ import type { ToolContext } from "../mcp/server.ts";
11
11
  * - `pullfrog-approval` stays opt-in (`Repo.approvalCheck`, default off) and terminal-only:
12
12
  * it asserts a review verdict, which only exists once a run produces one. anchored
13
13
  * to the exact reviewed sha so a mid-run push leaves the new head unapproved until
14
- * a follow-up re-review reports.
14
+ * a follow-up re-review reports. a re-review that submits no review carries the
15
+ * prior verdict forward instead (see `carriedApproval`).
15
16
  *
16
17
  * best-effort throughout: a check-post failure (transient 5xx, closed PR, revoked
17
18
  * permission) must never flip the run's own outcome. the `workflow_run.completed` webhook
@@ -12,6 +12,7 @@ export declare class SpawnTimeoutError extends Error {
12
12
  export type SignalHandler = (signal: NodeJS.Signals) => void;
13
13
  export declare function trackChild(options: TrackChildOptions): void;
14
14
  export declare function untrackChild(child: ChildProcess): void;
15
+ export declare function isTrackedChild(child: ChildProcess): boolean;
15
16
  export declare function setSignalHandler(handler: SignalHandler | null): void;
16
17
  export declare function killTrackedChildren(): void;
17
18
  /**
@@ -50,6 +51,7 @@ export interface SpawnOptions {
50
51
  killGroup?: boolean;
51
52
  retain?: RetainMode;
52
53
  maxRetainedBytes?: number;
54
+ signal?: AbortSignal | undefined;
53
55
  }
54
56
  /**
55
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.95",
3
+ "version": "0.1.97",
4
4
  "cliContractVersion": "1",
5
5
  "type": "module",
6
6
  "bin": {
@@ -66,7 +66,7 @@
66
66
  "undici": "^7.22.0",
67
67
  "vitest": "^4.0.17",
68
68
  "yaml": "^2.8.2",
69
- "yes": "0.1.0",
69
+ "yes": "0.2.3",
70
70
  "zod": "4.3.6"
71
71
  },
72
72
  "repository": {