pullfrog 0.1.31 → 0.1.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -1
- package/dist/cli.mjs +461 -203
- package/dist/index.js +460 -202
- package/dist/internal.js +15 -10
- package/dist/mcp/review.d.ts +41 -0
- package/dist/toolState.d.ts +4 -0
- package/dist/utils/agent.d.ts +3 -1
- package/dist/utils/isTransientNetworkError.d.ts +9 -0
- package/dist/utils/payload.d.ts +10 -1
- package/dist/utils/statusChecks.d.ts +25 -0
- package/dist/yes/index.d.ts +26 -0
- package/dist/yes.js +38 -1
- package/package.json +1 -1
package/dist/internal.js
CHANGED
|
@@ -9,15 +9,20 @@ var providers = {
|
|
|
9
9
|
displayName: "Anthropic",
|
|
10
10
|
envVars: ["ANTHROPIC_API_KEY", "CLAUDE_CODE_OAUTH_TOKEN"],
|
|
11
11
|
models: {
|
|
12
|
-
// claude-fable-5 is
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
12
|
+
// claude-fable-5 is selectable but not recommended/auto-selected — it's
|
|
13
|
+
// moving to usage-credits-only billing and isn't broadly available yet, so
|
|
14
|
+
// opus stays the universally-available flagship, the AUTO_INTELLIGENT tier
|
|
15
|
+
// target, and the recommended pick. an explicit fable pick hits the real
|
|
16
|
+
// API and is access-gated: it errors for accounts without access today and
|
|
17
|
+
// just works once usage credits are live — no silent opus fallback, the
|
|
18
|
+
// API is the source of truth (#959).
|
|
17
19
|
"claude-fable": {
|
|
18
20
|
displayName: "Claude Fable",
|
|
19
21
|
resolve: "anthropic/claude-fable-5",
|
|
20
|
-
|
|
22
|
+
// rolling alias: models.dev's OpenRouter mirror lags brand-new pinned
|
|
23
|
+
// versions (claude-fable-5 isn't indexed yet), so track ~…-latest to
|
|
24
|
+
// stay catalog-valid and auto-follow version bumps.
|
|
25
|
+
openRouterResolve: "openrouter/~anthropic/claude-fable-latest",
|
|
21
26
|
subagentModel: "claude-sonnet"
|
|
22
27
|
},
|
|
23
28
|
"claude-opus": {
|
|
@@ -29,8 +34,8 @@ var providers = {
|
|
|
29
34
|
},
|
|
30
35
|
"claude-sonnet": {
|
|
31
36
|
displayName: "Claude Sonnet",
|
|
32
|
-
resolve: "anthropic/claude-sonnet-
|
|
33
|
-
openRouterResolve: "openrouter/anthropic/claude-sonnet-
|
|
37
|
+
resolve: "anthropic/claude-sonnet-5",
|
|
38
|
+
openRouterResolve: "openrouter/anthropic/claude-sonnet-5"
|
|
34
39
|
},
|
|
35
40
|
"claude-haiku": {
|
|
36
41
|
displayName: "Claude Haiku",
|
|
@@ -208,8 +213,8 @@ var providers = {
|
|
|
208
213
|
},
|
|
209
214
|
"claude-sonnet": {
|
|
210
215
|
displayName: "Claude Sonnet",
|
|
211
|
-
resolve: "opencode/claude-sonnet-
|
|
212
|
-
openRouterResolve: "openrouter/anthropic/claude-sonnet-
|
|
216
|
+
resolve: "opencode/claude-sonnet-5",
|
|
217
|
+
openRouterResolve: "openrouter/anthropic/claude-sonnet-5"
|
|
213
218
|
},
|
|
214
219
|
"claude-haiku": {
|
|
215
220
|
displayName: "Claude Haiku",
|
package/dist/mcp/review.d.ts
CHANGED
|
@@ -23,6 +23,41 @@ export declare const TRANSIENT_REVIEW_RETRY_DELAYS_MS: number[];
|
|
|
23
23
|
*/
|
|
24
24
|
export declare function commentableLinesForFile(patch: string | undefined): CommentableLines;
|
|
25
25
|
export declare function buildCommentableMap(ctx: ToolContext, pullNumber: number): Promise<Map<string, CommentableLines>>;
|
|
26
|
+
/**
|
|
27
|
+
* count unresolved review threads on a PR whose root comment was authored by
|
|
28
|
+
* Pullfrog's bot. this is the full set of open Pullfrog findings on the PR —
|
|
29
|
+
* the basis for both the never-approve-with-outstanding-issues invariant (the
|
|
30
|
+
* approval gate below) and the `pullfrog-approval` status verdict. it is a
|
|
31
|
+
* function of all open findings, NOT just the latest commit's delta.
|
|
32
|
+
*
|
|
33
|
+
* outdated-but-unresolved threads still count — GitHub marks a thread
|
|
34
|
+
* `[OUTDATED]` when the anchor line moved (reformat, rename, force-push), which
|
|
35
|
+
* is not the same as the concern being addressed. human-reviewer threads are
|
|
36
|
+
* excluded: they belong to those reviewers to resolve. walks every page so a
|
|
37
|
+
* long-lived PR with >100 threads can't hide an outstanding finding.
|
|
38
|
+
*/
|
|
39
|
+
export declare function countOutstandingPullfrogThreads(ctx: ToolContext, pullNumber: number): Promise<number>;
|
|
40
|
+
/**
|
|
41
|
+
* proactive approve-when-clean for the Fix-all / Fix-👍s flow (`fix_review`
|
|
42
|
+
* trigger). when such a run completes successfully and every Pullfrog-originated
|
|
43
|
+
* review thread it raised is resolved, post a NEW approving review summarizing
|
|
44
|
+
* the run's work.
|
|
45
|
+
*
|
|
46
|
+
* the verification step shares `countOutstandingPullfrogThreads` with the
|
|
47
|
+
* approval gate in create_pull_request_review: that gate REACTIVELY blocks a
|
|
48
|
+
* bad approval; this is the PROACTIVE approve side. approval is contingent on
|
|
49
|
+
* Pullfrog's own findings actually being addressed — never a blind post-fix
|
|
50
|
+
* approval. a Fix-👍s run that only resolved a subset naturally fails the
|
|
51
|
+
* outstanding-thread check and is left unapproved.
|
|
52
|
+
*
|
|
53
|
+
* respects `prApproveEnabled` (the repo's "Allow Pullfrog to approve PRs"
|
|
54
|
+
* opt-in): a repo that hasn't enabled binding bot approvals never gets one
|
|
55
|
+
* here. skips when the agent already rendered an explicit review verdict this
|
|
56
|
+
* run (`toolState.approval`), so an agent that found a real issue and submitted
|
|
57
|
+
* a non-approving review is never overridden. best-effort — a failure must not
|
|
58
|
+
* flip the run's outcome.
|
|
59
|
+
*/
|
|
60
|
+
export declare function approveAfterFix(ctx: ToolContext): Promise<void>;
|
|
26
61
|
export type ReviewCommentInput = NonNullable<RestEndpointMethodTypes["pulls"]["createReview"]["parameters"]["comments"]>[number];
|
|
27
62
|
export interface DroppedComment {
|
|
28
63
|
path: string;
|
|
@@ -155,6 +190,11 @@ export declare function CreatePullRequestReviewTool(ctx: ToolContext): import("f
|
|
|
155
190
|
}>> & {
|
|
156
191
|
mutates?: boolean;
|
|
157
192
|
};
|
|
193
|
+
type FooterOpts = {
|
|
194
|
+
body: string;
|
|
195
|
+
approved: boolean;
|
|
196
|
+
hasComments: boolean;
|
|
197
|
+
};
|
|
158
198
|
/**
|
|
159
199
|
* clear a pending review draft stranded on the PR by a prior hard-killed run
|
|
160
200
|
* (workflow timeout, OOM) so the next createReview can succeed.
|
|
@@ -196,6 +236,7 @@ export declare function clearStrandedPendingReview(ctx: ToolContext, params: {
|
|
|
196
236
|
* comments-only) until a body-path run happened to clear the draft.
|
|
197
237
|
*/
|
|
198
238
|
export declare function createReviewWithStrandedRecovery(ctx: ToolContext, params: RestEndpointMethodTypes["pulls"]["createReview"]["parameters"]): Promise<Awaited<ReturnType<typeof ctx.octokit.rest.pulls.createReview>>>;
|
|
239
|
+
export declare function createAndSubmitWithFooter(ctx: ToolContext, params: RestEndpointMethodTypes["pulls"]["createReview"]["parameters"], opts: FooterOpts): Promise<Awaited<ReturnType<typeof ctx.octokit.rest.pulls.submitReview>>>;
|
|
199
240
|
/**
|
|
200
241
|
* report the review node ID so the WorkflowRun is marked as "review submitted".
|
|
201
242
|
* exported for use in main.ts post-agent cleanup.
|
package/dist/toolState.d.ts
CHANGED
|
@@ -100,6 +100,10 @@ export interface ToolState {
|
|
|
100
100
|
nodeId: string;
|
|
101
101
|
reviewedSha: string | undefined;
|
|
102
102
|
};
|
|
103
|
+
approval?: {
|
|
104
|
+
wouldApprove: boolean;
|
|
105
|
+
sha: string | undefined;
|
|
106
|
+
};
|
|
103
107
|
reviewReplies?: Map<number, {
|
|
104
108
|
commentId: number;
|
|
105
109
|
url: string | undefined;
|
package/dist/utils/agent.d.ts
CHANGED
|
@@ -10,7 +10,9 @@ import type { Agent } from "../agents/index.ts";
|
|
|
10
10
|
* Bedrock model, change `BEDROCK_MODEL_ID`, not `PULLFROG_MODEL`.
|
|
11
11
|
* 2. slug from repo config / payload → alias registry. routing slugs
|
|
12
12
|
* (e.g. `bedrock/byok`) defer to a separate env var (`BEDROCK_MODEL_ID`).
|
|
13
|
-
*
|
|
13
|
+
* a non-curated value with a slash passes through unchanged as a raw
|
|
14
|
+
* models.dev specifier (same as `PULLFROG_MODEL`), not auto-selected away.
|
|
15
|
+
* 3. undefined (no slug, or a bare-word non-specifier) — agent auto-selects.
|
|
14
16
|
*/
|
|
15
17
|
export declare function resolveModel(ctx: {
|
|
16
18
|
slug?: string | undefined;
|
|
@@ -8,3 +8,12 @@
|
|
|
8
8
|
* substrings on `error.message` (e.g. provider-specific framings).
|
|
9
9
|
*/
|
|
10
10
|
export declare function isTransientNetworkError(error: unknown, extraPatterns?: string[]): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* broader transient predicate for octokit calls: an HTTP 5xx (GitHub upstream
|
|
13
|
+
* blip) OR a network-class error. distinct from `isTransientNetworkError` — an
|
|
14
|
+
* HTTP 4xx (404/401/403/422) is deterministic and fails fast. the action
|
|
15
|
+
* octokit carries only the throttling plugin + a 401 re-mint (no 5xx retry),
|
|
16
|
+
* so callers on critical paths retry idempotent GitHub reads/dispatches with
|
|
17
|
+
* this. mirrors the server's `isTransientUpstreamError`. see #999.
|
|
18
|
+
*/
|
|
19
|
+
export declare function isTransientOctokitError(error: unknown): boolean;
|
package/dist/utils/payload.d.ts
CHANGED
|
@@ -25,17 +25,25 @@ export declare const JsonPayload: import("arktype/internal/variants/object.ts").
|
|
|
25
25
|
generateSummary?: boolean | undefined;
|
|
26
26
|
}, {}>;
|
|
27
27
|
export declare const Inputs: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
28
|
-
prompt
|
|
28
|
+
prompt?: string | undefined;
|
|
29
|
+
prompt_file?: string | undefined;
|
|
29
30
|
model?: string | undefined;
|
|
30
31
|
timeout?: string | undefined;
|
|
31
32
|
push?: "disabled" | "enabled" | "restricted" | undefined;
|
|
32
33
|
shell?: "disabled" | "enabled" | "restricted" | undefined;
|
|
34
|
+
status_checks?: "disabled" | "enabled" | undefined;
|
|
33
35
|
cwd?: string | undefined;
|
|
34
36
|
output_schema?: string | undefined;
|
|
35
37
|
}, {}>;
|
|
36
38
|
export type Inputs = typeof Inputs.infer;
|
|
37
39
|
export type ResolvedPromptInput = string | typeof JsonPayload.infer;
|
|
38
40
|
export declare function resolvePromptInput(): ResolvedPromptInput;
|
|
41
|
+
/**
|
|
42
|
+
* true when `actor` is Pullfrog's own GitHub identity — the bot login
|
|
43
|
+
* (`pullfrog[bot]` / `pullfrogdev[bot]`) or the bare account. used to skip
|
|
44
|
+
* self-triggered events and to recognize Pullfrog-originated review threads.
|
|
45
|
+
*/
|
|
46
|
+
export declare const isPullfrog: (actor: string | null | undefined) => boolean;
|
|
39
47
|
export declare function resolvePayload(resolvedPromptInput: ResolvedPromptInput, repoSettings: RepoSettings): {
|
|
40
48
|
"~pullfrog": true;
|
|
41
49
|
version: string;
|
|
@@ -62,6 +70,7 @@ export declare function resolvePayload(resolvedPromptInput: ResolvedPromptInput,
|
|
|
62
70
|
generateSummary: boolean | undefined;
|
|
63
71
|
push: import("../external.ts").PushPermission;
|
|
64
72
|
shell: import("../external.ts").ShellPermission;
|
|
73
|
+
statusChecks: boolean;
|
|
65
74
|
proxyModel: string | undefined;
|
|
66
75
|
};
|
|
67
76
|
export type ResolvedPayload = ReturnType<typeof resolvePayload>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ToolContext } from "../mcp/server.ts";
|
|
2
|
+
/**
|
|
3
|
+
* post the opt-in `pullfrog` (run completion) and `pullfrog-approval` (review
|
|
4
|
+
* verdict) commit-status check-runs so they can be required by branch
|
|
5
|
+
* protection. no-op unless the `status_checks` input is enabled and the run is
|
|
6
|
+
* on a pull request.
|
|
7
|
+
*
|
|
8
|
+
* terminal-only by design: we never create an `in_progress` check. a
|
|
9
|
+
* hard-cancelled run (SIGKILL) would strand a required check `in_progress` and
|
|
10
|
+
* block merges forever; an absent required check already blocks merge the same
|
|
11
|
+
* way while the run is in flight, with no stuck-check failure mode.
|
|
12
|
+
*
|
|
13
|
+
* - `pullfrog` is posted on every PR run: success iff the run finished
|
|
14
|
+
* successfully, failure on error/timeout. review-verdict-agnostic.
|
|
15
|
+
* - `pullfrog-approval` is posted only when this run produced an approval
|
|
16
|
+
* verdict (`toolState.approval`, set by create_pull_request_review),
|
|
17
|
+
* anchored to the reviewed sha so a mid-run push leaves the new head
|
|
18
|
+
* unapproved until the follow-up re-review reports.
|
|
19
|
+
*
|
|
20
|
+
* best-effort throughout: a check-post failure (fork PR head not in the base
|
|
21
|
+
* repo, transient 5xx, closed PR) must never flip the run's own outcome.
|
|
22
|
+
*/
|
|
23
|
+
export declare function reportStatusChecks(ctx: ToolContext, params: {
|
|
24
|
+
runSucceeded: boolean;
|
|
25
|
+
}): Promise<void>;
|
package/dist/yes/index.d.ts
CHANGED
|
@@ -4,6 +4,21 @@ export declare function range(n: number): number[];
|
|
|
4
4
|
export declare function range(start: number, end: number): number[];
|
|
5
5
|
export declare function range(n: number, fn: (i: number) => number): number[];
|
|
6
6
|
export declare function schedule(fn: (i: number) => number, count: number): number[];
|
|
7
|
+
/**
|
|
8
|
+
* read a retry delay (ms) from a thrown HTTP error's response headers, honoring
|
|
9
|
+
* GitHub's rate-limit backoff hints on octokit errors (`error.response.headers`):
|
|
10
|
+
* - `retry-after` (RFC 9110 delta-seconds or an HTTP-date) — an explicit
|
|
11
|
+
* backoff directive whenever present (covers secondary rate limits, which
|
|
12
|
+
* carry it with `x-ratelimit-remaining > 0`).
|
|
13
|
+
* - else `x-ratelimit-reset` (epoch seconds) but ONLY when
|
|
14
|
+
* `x-ratelimit-remaining` is `"0"` — GitHub sends the reset header on ~every
|
|
15
|
+
* response (~1h ahead), so it's a backoff hint only under actual primary
|
|
16
|
+
* rate-limit exhaustion (matching octokit's throttling plugin). without this
|
|
17
|
+
* gate a non-rate-limit retried error (e.g. a permissions 403) would be
|
|
18
|
+
* wrongly delayed to the reset time.
|
|
19
|
+
* returns `undefined` when no applicable hint is present/parseable.
|
|
20
|
+
*/
|
|
21
|
+
export declare function retryAfterMs(error: unknown): number | undefined;
|
|
7
22
|
type InferInput<S> = S extends StandardSchemaV1<infer I, any> ? I : never;
|
|
8
23
|
type InferOutput<S> = S extends StandardSchemaV1<any, infer O> ? O : never;
|
|
9
24
|
export interface OpOptions<TReturn = any> {
|
|
@@ -11,6 +26,17 @@ export interface OpOptions<TReturn = any> {
|
|
|
11
26
|
ttl?: number;
|
|
12
27
|
maxItems?: number;
|
|
13
28
|
retries?: number[];
|
|
29
|
+
/**
|
|
30
|
+
* honor a server backoff hint on retries: when a retried error carries a
|
|
31
|
+
* `retry-after` / `x-ratelimit-reset` header, schedule the next retry from
|
|
32
|
+
* that value (clamped to `[fixed delay, retryAfterCap]`) instead of the fixed
|
|
33
|
+
* `retries[attempt]` backoff. `true` uses the built-in `retryAfterMs` reader;
|
|
34
|
+
* a function extracts a delay-ms from a custom error shape. errors without the
|
|
35
|
+
* header fall back to the fixed backoff.
|
|
36
|
+
*/
|
|
37
|
+
retryAfter?: boolean | ((error: unknown) => number | undefined);
|
|
38
|
+
/** cap (ms) on a honored retry-after hint. default 20000. */
|
|
39
|
+
retryAfterCap?: number;
|
|
14
40
|
cacheHit?: ((key: string) => void) | null;
|
|
15
41
|
cacheMiss?: ((key: string) => void) | null;
|
|
16
42
|
skipCache?: (result: TReturn) => boolean;
|
package/dist/yes.js
CHANGED
|
@@ -1022,6 +1022,34 @@ function range(a, b) {
|
|
|
1022
1022
|
function schedule(fn, count) {
|
|
1023
1023
|
return Array.from({ length: count }, (_, i) => fn(i));
|
|
1024
1024
|
}
|
|
1025
|
+
var DEFAULT_RETRY_AFTER_CAP_MS = 2e4;
|
|
1026
|
+
function getErrorResponseHeaders(error) {
|
|
1027
|
+
if (error === null || typeof error !== "object") return void 0;
|
|
1028
|
+
const response = error.response;
|
|
1029
|
+
if (response === null || typeof response !== "object") return void 0;
|
|
1030
|
+
const headers = response.headers;
|
|
1031
|
+
if (headers === null || typeof headers !== "object") return void 0;
|
|
1032
|
+
return headers;
|
|
1033
|
+
}
|
|
1034
|
+
function retryAfterMs(error) {
|
|
1035
|
+
const headers = getErrorResponseHeaders(error);
|
|
1036
|
+
if (!headers) return void 0;
|
|
1037
|
+
const retryAfter = headers["retry-after"];
|
|
1038
|
+
if (typeof retryAfter === "string" && retryAfter.trim() !== "") {
|
|
1039
|
+
const seconds = Number(retryAfter);
|
|
1040
|
+
if (Number.isFinite(seconds)) return Math.max(0, seconds * 1e3);
|
|
1041
|
+
const dateMs = Date.parse(retryAfter);
|
|
1042
|
+
if (!Number.isNaN(dateMs)) return Math.max(0, dateMs - Date.now());
|
|
1043
|
+
}
|
|
1044
|
+
if (headers["x-ratelimit-remaining"] === "0") {
|
|
1045
|
+
const reset = headers["x-ratelimit-reset"];
|
|
1046
|
+
if (typeof reset === "string" && reset.trim() !== "") {
|
|
1047
|
+
const resetEpoch = Number(reset);
|
|
1048
|
+
if (Number.isFinite(resetEpoch)) return Math.max(0, resetEpoch * 1e3 - Date.now());
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
return void 0;
|
|
1052
|
+
}
|
|
1025
1053
|
function validateSchema(schema, value) {
|
|
1026
1054
|
const result = schema["~standard"].validate(value);
|
|
1027
1055
|
if (result instanceof Promise) {
|
|
@@ -1159,7 +1187,15 @@ function _op(fn, options) {
|
|
|
1159
1187
|
log.info({ key, error, attempt });
|
|
1160
1188
|
}
|
|
1161
1189
|
} else {
|
|
1162
|
-
|
|
1190
|
+
let delay = retries[attempt];
|
|
1191
|
+
if (options.retryAfter) {
|
|
1192
|
+
const extract = options.retryAfter === true ? retryAfterMs : options.retryAfter;
|
|
1193
|
+
const hint = extract(error);
|
|
1194
|
+
if (hint !== void 0) {
|
|
1195
|
+
const cap = options.retryAfterCap ?? DEFAULT_RETRY_AFTER_CAP_MS;
|
|
1196
|
+
delay = Math.max(delay, Math.min(cap, hint));
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1163
1199
|
if (namePrefix) {
|
|
1164
1200
|
log.info(
|
|
1165
1201
|
`${namePrefix}attempt ${attempt + 1}/${retries.length + 1} failed, retrying in ${delay}ms`
|
|
@@ -1240,5 +1276,6 @@ function _op(fn, options) {
|
|
|
1240
1276
|
export {
|
|
1241
1277
|
op,
|
|
1242
1278
|
range,
|
|
1279
|
+
retryAfterMs,
|
|
1243
1280
|
schedule
|
|
1244
1281
|
};
|