pullfrog 0.1.29 → 0.1.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/claudePretoolGate.d.ts +5 -1
- package/dist/agents/opencodePlugin.d.ts +2 -2
- package/dist/agents/postRun.d.ts +8 -31
- package/dist/agents/shared.d.ts +6 -0
- package/dist/agents/subagentToolGates.d.ts +22 -40
- package/dist/cli.mjs +63989 -59666
- package/dist/external.d.ts +45 -2
- package/dist/index.js +62509 -58186
- package/dist/internal/index.d.ts +2 -2
- package/dist/internal.js +99 -33
- package/dist/mcp/checkSuite.d.ts +3 -1
- package/dist/mcp/checkout.d.ts +4 -2
- package/dist/mcp/comment.d.ts +12 -4
- package/dist/mcp/commitInfo.d.ts +3 -1
- package/dist/mcp/dependencies.d.ts +6 -2
- package/dist/mcp/git.d.ts +32 -6
- package/dist/mcp/issue.d.ts +26 -1
- package/dist/mcp/issueComments.d.ts +3 -1
- package/dist/mcp/issueEvents.d.ts +3 -1
- package/dist/mcp/issueInfo.d.ts +3 -1
- package/dist/mcp/labels.d.ts +16 -1
- package/dist/mcp/output.d.ts +6 -2
- package/dist/mcp/pr.d.ts +19 -2
- package/dist/mcp/prInfo.d.ts +3 -1
- package/dist/mcp/resolveRepoCtx.d.ts +34 -0
- package/dist/mcp/review.d.ts +9 -3
- package/dist/mcp/reviewComments.d.ts +9 -3
- package/dist/mcp/selectMode.d.ts +3 -1
- package/dist/mcp/server.d.ts +7 -1
- package/dist/mcp/shared.d.ts +18 -1
- package/dist/mcp/shell.d.ts +6 -2
- package/dist/mcp/upload.d.ts +3 -1
- package/dist/mcp/xrepo.d.ts +15 -0
- package/dist/models.d.ts +58 -5
- package/dist/modes.d.ts +1 -1
- package/dist/toolState.d.ts +66 -16
- package/dist/utils/apiCommit.d.ts +40 -0
- package/dist/utils/apiKeys.d.ts +19 -0
- package/dist/utils/buildPullfrogFooter.d.ts +27 -0
- package/dist/utils/claudeSubscription.d.ts +30 -0
- package/dist/utils/errorReport.d.ts +2 -2
- package/dist/utils/github.d.ts +28 -2
- package/dist/utils/instructions.d.ts +11 -0
- package/dist/utils/isTransientNetworkError.d.ts +10 -0
- package/dist/utils/learnings.d.ts +14 -0
- package/dist/utils/modelAccess.d.ts +64 -0
- package/dist/utils/openCodeModels.d.ts +2 -2
- package/dist/utils/payload.d.ts +16 -0
- package/dist/utils/proxy.d.ts +3 -5
- package/dist/utils/runContext.d.ts +4 -0
- package/dist/utils/runContextData.d.ts +10 -0
- package/dist/utils/runErrorRenderer.d.ts +2 -2
- package/dist/utils/setup.d.ts +18 -3
- package/dist/utils/token.d.ts +22 -3
- package/dist/yes/index.d.ts +32 -0
- package/dist/yes/standard-schema.d.ts +117 -0
- package/dist/yes.js +1244 -0
- package/package.json +19 -10
- package/dist/utils/byokFallback.d.ts +0 -44
- package/dist/utils/retry.d.ts +0 -13
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type RepoAccess } from "../toolState.ts";
|
|
2
|
+
import { type OctokitWithPlugins } from "../utils/github.ts";
|
|
3
|
+
import type { ToolContext } from "./server.ts";
|
|
4
|
+
/**
|
|
5
|
+
* per-repo execution context for a single tool call. resolved from the
|
|
6
|
+
* optional `repo` param (a bare repo name — owner-implicit, same account as
|
|
7
|
+
* the primary). carries the correct token tier + working directory so a tool
|
|
8
|
+
* can talk to GitHub and run git against the right checkout.
|
|
9
|
+
*/
|
|
10
|
+
export interface RepoCtx {
|
|
11
|
+
owner: string;
|
|
12
|
+
name: string;
|
|
13
|
+
/** working tree: primary = process.cwd(); secondary = ctx.tmpdir/xrepo/<name> */
|
|
14
|
+
dir: string;
|
|
15
|
+
access: RepoAccess;
|
|
16
|
+
/** octokit scoped to the repo's tier (write tokens for primary/write, read token for read) */
|
|
17
|
+
octokit: OctokitWithPlugins;
|
|
18
|
+
/** git token scoped to the repo's tier */
|
|
19
|
+
gitToken: string;
|
|
20
|
+
/** re-mint this tier's git token on an auth-class push failure (push retries) */
|
|
21
|
+
refreshGitToken: ((stale: string) => Promise<string>) | undefined;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* resolve the execution context for a `repo`-scoped tool call.
|
|
25
|
+
*
|
|
26
|
+
* - omitted / primary name → the primary ctx (unchanged single-repo path).
|
|
27
|
+
* - a `write`-tier secondary → the same write-scoped tokens as the primary
|
|
28
|
+
* (mcpToken + gitToken are minted over the write set ∪ primary).
|
|
29
|
+
* - a `read`-tier secondary → the contents:read token (clone-for-reference).
|
|
30
|
+
*
|
|
31
|
+
* throws when `repo` isn't a registered checkout — secondaries must be cloned
|
|
32
|
+
* via `checkout_repo` first, which is what populates `toolState.repos`.
|
|
33
|
+
*/
|
|
34
|
+
export declare function resolveRepoCtx(ctx: ToolContext, repo?: string | undefined): RepoCtx;
|
package/dist/mcp/review.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RestEndpointMethodTypes } from "@octokit/rest";
|
|
2
|
-
import type
|
|
2
|
+
import { type CommentableLines } from "../toolState.ts";
|
|
3
3
|
import type { ToolContext } from "./server.ts";
|
|
4
4
|
export type { CommentableLines };
|
|
5
5
|
/**
|
|
@@ -69,7 +69,7 @@ export type DuplicateReviewDecision = {
|
|
|
69
69
|
* adds noise to the PR.
|
|
70
70
|
*
|
|
71
71
|
* legitimate follow-up reviews after new commits ARE allowed: the
|
|
72
|
-
* new-commits-mid-review path advances
|
|
72
|
+
* new-commits-mid-review path advances the primary repo state's checkoutSha past the
|
|
73
73
|
* previously reviewed sha, and a subsequent checkout_pr advances it again.
|
|
74
74
|
* any call where checkoutSha has moved past the prior reviewedSha is a real
|
|
75
75
|
* follow-up and goes through. anything else — same sha, or no checkoutSha
|
|
@@ -103,6 +103,7 @@ export declare function duplicateReviewDecision(params: {
|
|
|
103
103
|
*/
|
|
104
104
|
export declare function reviewSkipDecision(params: {
|
|
105
105
|
approved: boolean;
|
|
106
|
+
requestChanges?: boolean;
|
|
106
107
|
body: string | null | undefined;
|
|
107
108
|
hasComments: boolean;
|
|
108
109
|
prApproveEnabled: boolean;
|
|
@@ -112,6 +113,7 @@ export declare const CreatePullRequestReview: import("arktype/internal/variants/
|
|
|
112
113
|
pull_number: number;
|
|
113
114
|
body?: string;
|
|
114
115
|
approved?: boolean;
|
|
116
|
+
request_changes?: boolean;
|
|
115
117
|
commit_id?: string;
|
|
116
118
|
comments?: {
|
|
117
119
|
path: string;
|
|
@@ -126,6 +128,7 @@ export declare function CreatePullRequestReviewTool(ctx: ToolContext): import("f
|
|
|
126
128
|
pull_number: number;
|
|
127
129
|
body?: string;
|
|
128
130
|
approved?: boolean;
|
|
131
|
+
request_changes?: boolean;
|
|
129
132
|
commit_id?: string;
|
|
130
133
|
comments?: {
|
|
131
134
|
path: string;
|
|
@@ -139,6 +142,7 @@ export declare function CreatePullRequestReviewTool(ctx: ToolContext): import("f
|
|
|
139
142
|
pull_number: number;
|
|
140
143
|
body?: string;
|
|
141
144
|
approved?: boolean;
|
|
145
|
+
request_changes?: boolean;
|
|
142
146
|
commit_id?: string;
|
|
143
147
|
comments?: {
|
|
144
148
|
path: string;
|
|
@@ -148,7 +152,9 @@ export declare function CreatePullRequestReviewTool(ctx: ToolContext): import("f
|
|
|
148
152
|
suggestion?: string;
|
|
149
153
|
start_line?: number;
|
|
150
154
|
}[];
|
|
151
|
-
}
|
|
155
|
+
}>> & {
|
|
156
|
+
mutates?: boolean;
|
|
157
|
+
};
|
|
152
158
|
/**
|
|
153
159
|
* clear a pending review draft stranded on the PR by a prior hard-killed run
|
|
154
160
|
* (workflow timeout, OOM) so the next createReview can succeed.
|
|
@@ -147,7 +147,9 @@ export declare function GetReviewCommentsTool(ctx: ToolContext): import("fastmcp
|
|
|
147
147
|
}, {
|
|
148
148
|
pull_number: number;
|
|
149
149
|
review_id: number;
|
|
150
|
-
}
|
|
150
|
+
}>> & {
|
|
151
|
+
mutates?: boolean;
|
|
152
|
+
};
|
|
151
153
|
export declare const ListPullRequestReviews: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
152
154
|
pull_number: number;
|
|
153
155
|
}, {}>;
|
|
@@ -155,7 +157,9 @@ export declare function ListPullRequestReviewsTool(ctx: ToolContext): import("fa
|
|
|
155
157
|
pull_number: number;
|
|
156
158
|
}, {
|
|
157
159
|
pull_number: number;
|
|
158
|
-
}
|
|
160
|
+
}>> & {
|
|
161
|
+
mutates?: boolean;
|
|
162
|
+
};
|
|
159
163
|
export declare const ResolveReviewThread: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
160
164
|
thread_id: string;
|
|
161
165
|
}, {}>;
|
|
@@ -163,5 +167,7 @@ export declare function ResolveReviewThreadTool(ctx: ToolContext): import("fastm
|
|
|
163
167
|
thread_id: string;
|
|
164
168
|
}, {
|
|
165
169
|
thread_id: string;
|
|
166
|
-
}
|
|
170
|
+
}>> & {
|
|
171
|
+
mutates?: boolean;
|
|
172
|
+
};
|
|
167
173
|
export {};
|
package/dist/mcp/selectMode.d.ts
CHANGED
package/dist/mcp/server.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import "./arkConfig.ts";
|
|
2
|
-
import { type AgentId } from "../external.ts";
|
|
2
|
+
import { type AgentId, type XrepoConfig } from "../external.ts";
|
|
3
3
|
import type { Mode } from "../modes.ts";
|
|
4
4
|
import type { ToolState } from "../toolState.ts";
|
|
5
5
|
import type { OctokitWithPlugins } from "../utils/github.ts";
|
|
6
6
|
import type { ResolvedPayload } from "../utils/payload.ts";
|
|
7
7
|
import type { AccountPlan } from "../utils/runContext.ts";
|
|
8
8
|
import type { RunContextData } from "../utils/runContextData.ts";
|
|
9
|
+
import { type PullfrogTool } from "./shared.ts";
|
|
9
10
|
export interface ToolContext {
|
|
10
11
|
agentId: AgentId;
|
|
11
12
|
repo: RunContextData["repo"];
|
|
@@ -13,11 +14,15 @@ export interface ToolContext {
|
|
|
13
14
|
octokit: OctokitWithPlugins;
|
|
14
15
|
githubInstallationToken: string;
|
|
15
16
|
gitToken: string;
|
|
17
|
+
refreshGitToken: ((stale: string) => Promise<string>) | undefined;
|
|
18
|
+
readToken: string | undefined;
|
|
19
|
+
xrepo: XrepoConfig | undefined;
|
|
16
20
|
apiToken: string;
|
|
17
21
|
modes: Mode[];
|
|
18
22
|
postCheckoutScript: string | null;
|
|
19
23
|
prepushScript: string | null;
|
|
20
24
|
prApproveEnabled: boolean;
|
|
25
|
+
signedCommits: boolean;
|
|
21
26
|
modeInstructions: Record<string, string>;
|
|
22
27
|
toolState: ToolState;
|
|
23
28
|
runId: number | undefined;
|
|
@@ -29,6 +34,7 @@ export interface ToolContext {
|
|
|
29
34
|
resolvedModel: string | undefined;
|
|
30
35
|
}
|
|
31
36
|
type JsonSchema = Record<string, unknown>;
|
|
37
|
+
export declare function buildOrchestratorTools(ctx: ToolContext, outputSchema?: JsonSchema): PullfrogTool[];
|
|
32
38
|
type McpHttpServerOptions = {
|
|
33
39
|
outputSchema?: JsonSchema | undefined;
|
|
34
40
|
};
|
package/dist/mcp/shared.d.ts
CHANGED
|
@@ -1,7 +1,24 @@
|
|
|
1
1
|
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
2
2
|
import type { FastMCP, Tool } from "fastmcp";
|
|
3
3
|
import type { ToolContext } from "./server.ts";
|
|
4
|
-
|
|
4
|
+
/** extract the HTTP status from an unknown thrown value (octokit RequestError etc.), or undefined. */
|
|
5
|
+
export declare function getHttpStatus(err: unknown): number | undefined;
|
|
6
|
+
/**
|
|
7
|
+
* a Pullfrog MCP tool definition. `mutates` marks a named state-changing tool
|
|
8
|
+
* that must be reserved for the orchestrator and denied to subagents — it is
|
|
9
|
+
* the single source of truth the subagent deny list derives from (see
|
|
10
|
+
* action/agents/subagentToolGates.ts). general-purpose execution tools (`git`,
|
|
11
|
+
* `shell`) are deliberately left unmarked: they can mutate but are allowed for
|
|
12
|
+
* subagents and gated by command-arg validation instead.
|
|
13
|
+
*/
|
|
14
|
+
export type PullfrogTool = Tool<any, any> & {
|
|
15
|
+
mutates?: boolean;
|
|
16
|
+
};
|
|
17
|
+
export declare const tool: <const params>(toolDef: Tool<any, StandardSchemaV1<params>> & {
|
|
18
|
+
mutates?: boolean;
|
|
19
|
+
}) => Tool<any, StandardSchemaV1<params>> & {
|
|
20
|
+
mutates?: boolean;
|
|
21
|
+
};
|
|
5
22
|
export interface ToolResult {
|
|
6
23
|
content: {
|
|
7
24
|
type: "text";
|
package/dist/mcp/shell.d.ts
CHANGED
|
@@ -26,7 +26,9 @@ export declare function ShellTool(ctx: ToolContext): import("fastmcp").Tool<any,
|
|
|
26
26
|
timeout?: number;
|
|
27
27
|
working_directory?: string;
|
|
28
28
|
background?: boolean;
|
|
29
|
-
}
|
|
29
|
+
}>> & {
|
|
30
|
+
mutates?: boolean;
|
|
31
|
+
};
|
|
30
32
|
export declare const KillBackgroundParams: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
31
33
|
handle: string;
|
|
32
34
|
}, {}>;
|
|
@@ -34,4 +36,6 @@ export declare function KillBackgroundTool(ctx: ToolContext): import("fastmcp").
|
|
|
34
36
|
handle: string;
|
|
35
37
|
}, {
|
|
36
38
|
handle: string;
|
|
37
|
-
}
|
|
39
|
+
}>> & {
|
|
40
|
+
mutates?: boolean;
|
|
41
|
+
};
|
package/dist/mcp/upload.d.ts
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ToolContext } from "./server.ts";
|
|
2
|
+
export declare const ListRepos: import("arktype/internal/variants/object.ts").ObjectType<object, {}>;
|
|
3
|
+
export declare function ListReposTool(ctx: ToolContext): import("fastmcp").Tool<any, import("@standard-schema/spec").StandardSchemaV1<object, object>> & {
|
|
4
|
+
mutates?: boolean;
|
|
5
|
+
};
|
|
6
|
+
export declare const CheckoutRepo: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
7
|
+
repo: string;
|
|
8
|
+
}, {}>;
|
|
9
|
+
export declare function CheckoutRepoTool(ctx: ToolContext): import("fastmcp").Tool<any, import("@standard-schema/spec").StandardSchemaV1<{
|
|
10
|
+
repo: string;
|
|
11
|
+
}, {
|
|
12
|
+
repo: string;
|
|
13
|
+
}>> & {
|
|
14
|
+
mutates?: boolean;
|
|
15
|
+
};
|
package/dist/models.d.ts
CHANGED
|
@@ -36,7 +36,11 @@ export interface ModelAlias {
|
|
|
36
36
|
preferred: boolean;
|
|
37
37
|
/** whether this alias is free and requires no API key */
|
|
38
38
|
isFree: boolean;
|
|
39
|
-
/** slug of a replacement model
|
|
39
|
+
/** slug of a replacement model to resolve through. presence means this alias
|
|
40
|
+
* never runs as-is — resolution redirects to the replacement and it's hidden
|
|
41
|
+
* from pickers. covers permanent deprecation AND riding out a temporarily
|
|
42
|
+
* unavailable model: point it at a cheaper tier (downgrade) or a working
|
|
43
|
+
* sibling/higher tier (upgrade), then clear it when the model returns. */
|
|
40
44
|
fallback: string | undefined;
|
|
41
45
|
/** dynamic-resolution discriminant — see ModelRouting docs */
|
|
42
46
|
routing: ModelRouting | undefined;
|
|
@@ -57,7 +61,9 @@ interface ModelDef {
|
|
|
57
61
|
preferred?: boolean;
|
|
58
62
|
envVars?: readonly string[];
|
|
59
63
|
isFree?: boolean;
|
|
60
|
-
/** slug of a replacement model
|
|
64
|
+
/** slug of a replacement model to resolve through — permanent deprecation or
|
|
65
|
+
* temporary unavailability (downgrade/upgrade until the model returns). see
|
|
66
|
+
* ModelAlias.fallback. */
|
|
61
67
|
fallback?: string;
|
|
62
68
|
/** dynamic-resolution discriminant — see ModelRouting docs */
|
|
63
69
|
routing?: ModelRouting;
|
|
@@ -104,9 +110,53 @@ export declare function getModelEnvVars(slug: string): string[];
|
|
|
104
110
|
* see `provider.managedCredentials` and wiki/codex-auth.md. */
|
|
105
111
|
export declare function getModelManagedCredentials(slug: string): string[];
|
|
106
112
|
export declare const modelAliases: ModelAlias[];
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
113
|
+
/**
|
|
114
|
+
* `repo.model` sentinels for the two managed auto tiers. stored verbatim in
|
|
115
|
+
* the DB (they pass the `provider/model` shape check) and resolved to a
|
|
116
|
+
* concrete alias by `resolveDisplayAlias` below, so every downstream consumer
|
|
117
|
+
* (CLI resolve, OpenRouter resolve, footer label) handles them transparently.
|
|
118
|
+
*
|
|
119
|
+
* `efficient` mirrors the OSS/default subsidy model (Kimi K2 — fast + cheap);
|
|
120
|
+
* `intelligent` is the frontier pick (Claude Opus). a `null` model means the
|
|
121
|
+
* tier hasn't been pinned: callers default by card status via `defaultAutoTier`.
|
|
122
|
+
*/
|
|
123
|
+
export declare const AUTO_EFFICIENT = "auto/efficient";
|
|
124
|
+
export declare const AUTO_INTELLIGENT = "auto/intelligent";
|
|
125
|
+
export type AutoTier = typeof AUTO_EFFICIENT | typeof AUTO_INTELLIGENT;
|
|
126
|
+
export declare function isAutoTier(slug: string | null | undefined): slug is AutoTier;
|
|
127
|
+
/**
|
|
128
|
+
* the tier to default to when the user hasn't pinned one. card on file →
|
|
129
|
+
* intelligent (they've signalled willingness to pay for frontier reviews);
|
|
130
|
+
* otherwise → efficient (safe, cheap). server (`run-context`) and the console
|
|
131
|
+
* picker both call this so the displayed default and the runtime default agree.
|
|
132
|
+
*/
|
|
133
|
+
export declare function defaultAutoTier(hasCard: boolean): AutoTier;
|
|
134
|
+
/**
|
|
135
|
+
* the auto tier that actually runs for an account, enforcing the card gate:
|
|
136
|
+
* the intelligent tier (Opus) requires a card on file, so a no-card account is
|
|
137
|
+
* always clamped to efficient (Kimi) — whether intelligent was the card-based
|
|
138
|
+
* default or an explicit pick made while a card was once present. keeps a trial
|
|
139
|
+
* balance from being torched on a premium model before the user has a card.
|
|
140
|
+
* `model` may be null, an `auto/*` sentinel, or a concrete pick being
|
|
141
|
+
* clamped — callers route concrete picks here only when `!hasCard` (carded
|
|
142
|
+
* concrete picks resolve directly), so a concrete `model` always lands on the
|
|
143
|
+
* efficient tier via the no-card early return.
|
|
144
|
+
*/
|
|
145
|
+
export declare function resolveAutoTier(params: {
|
|
146
|
+
model: string | null;
|
|
147
|
+
hasCard: boolean;
|
|
148
|
+
}): AutoTier;
|
|
149
|
+
/**
|
|
150
|
+
* Router-resolvable — the set a card on file unlocks. custom (non-Auto) picks
|
|
151
|
+
* are card-gated wholesale on the Router: the console locks the Custom tab
|
|
152
|
+
* without a card and the server clamps any stored pick to the efficient tier.
|
|
153
|
+
* a pick with no openRouterResolve (a model OpenRouter doesn't serve yet)
|
|
154
|
+
* lands on the efficient default with or without a card, so "add a card to
|
|
155
|
+
* run this model" messaging would be false for it — hence the predicate.
|
|
156
|
+
* resolveOpenRouterModel walks display aliases, so auto sentinels and
|
|
157
|
+
* deprecated slugs are judged by the model that actually runs.
|
|
158
|
+
*/
|
|
159
|
+
export declare function isCardGatedModel(slug: string): boolean;
|
|
110
160
|
/** resolve a model slug to its concrete models.dev specifier (e.g. "anthropic/claude-opus-4-6") */
|
|
111
161
|
export declare function resolveModelSlug(slug: string): string | undefined;
|
|
112
162
|
/**
|
|
@@ -132,6 +182,9 @@ export declare function resolveCliModel(slug: string): string | undefined;
|
|
|
132
182
|
* (e.g. free opencode models).
|
|
133
183
|
*/
|
|
134
184
|
export declare function resolveOpenRouterModel(slug: string): string | undefined;
|
|
185
|
+
export declare const DEFAULT_PROXY_MODEL: string;
|
|
186
|
+
/** short label for the model auto-select picks today (console hint copy). */
|
|
187
|
+
export declare function getAutoSelectHintModel(): string;
|
|
135
188
|
/** env var that supplies the Bedrock model ID for the `bedrock/byok` slug. */
|
|
136
189
|
export declare const BEDROCK_MODEL_ID_ENV = "BEDROCK_MODEL_ID";
|
|
137
190
|
/** env var that supplies the Vertex AI model ID for the `vertex/byok` slug. */
|
package/dist/modes.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export interface Mode {
|
|
|
5
5
|
prompt?: string | undefined;
|
|
6
6
|
}
|
|
7
7
|
export declare const PR_SUMMARY_FORMAT = "### Default format\n\nThe body has at most three parts in this exact order:\n\n1. **Reviewed changes preamble** \u2014 one bolded inline lead-in describing what was reviewed in this run, a bullet list of the substantive changes, and an HTML comment carrying review metadata for downstream agents.\n2. **Cross-cutting issue sections** (zero or more) \u2014 one `### ` heading per concern, with a human-readable problem write-up and a collapsed `<details>Technical details</details>` block underneath.\n3. **`### \u2139\uFE0F Nitpicks`** at the very bottom (only if there are nits worth surfacing in the body) \u2014 a flat bullet list, no technical-details block.\n\nInline-vs-body split: concerns that anchor to a specific line go inline (use the `comments` parameter). Body `### ` sections are reserved for concerns that **have no line to anchor to** \u2014 typically because the concern is about *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 implicitly raises but doesn't address*. A concern that anchors to a line but has broad implications still goes inline (use the technical-details block there to capture the implications \u2014 see Inline technical details below). If you found no non-anchorable concerns, the body has zero `### ` issue sections \u2014 just the preamble + metadata.\n\n## 1. Reviewed changes preamble\n\nOpen with a single bolded inline lead-in followed immediately by the bullet list (no `### Key changes` heading, no `<b>TL;DR</b>`):\n\n```\n**Reviewed changes** \u2014 one sentence on what was reviewed in this run. For Review (initial), this is what the PR does and why. For IncrementalReview, this is what changed since the prior pullfrog review. Focus on intent, not mechanics.\n\n- **Short human-readable title** \u2014 1 sentence per substantive change. Write a short prose phrase; when you name a file, type, or function, put that name in backticks (e.g. **Add \\`TodoTracker\\` for live checklists**). A reviewer should understand the full reviewed scope from this list alone \u2014 this IS the dispassionate \"what was reviewed and what changed\" overview, so cover the substantive changes, not just the loudest ones.\n\n<!--\nPullfrog review metadata \u2014 for any agent (or human-with-agent) reading this\nreview. Incorporate the fields below into your understanding of the context\nthis review was made in. The findings below were written against\n{head_sha_short}; if new commits have landed on {head_ref} since this review\nwas submitted, treat any specific bug, file, or line callout as POTENTIALLY\nSTALE \u2014 re-diff against {head_sha_short} (or trigger a fresh review) and\nfactor commits past {head_sha_short} into your understanding of the current\nstate before acting on findings.\n\n- Mode: Review (initial) or IncrementalReview (delta against prior pullfrog review)\n- Files reviewed: {file_count}\n- Commits reviewed: {commit_count}\n- Base: {base_ref} ({base_sha_short})\n- Head: {head_ref} ({head_sha_short})\n- Reviewed commits:\n - {sha_short} \u2014 {commit_subject}\n - ...\n- Prior pullfrog review: none or {prior_sha_short} ({prior_review_html_url})\n- Submitted at: {iso_timestamp}\n-->\n```\n\nPull every metadata field from the `checkout_pr` tool's response \u2014 file count, commit count, base/head ref + SHA, the commit list. For `IncrementalReview` runs, populate `Prior pullfrog review` with the prior review's commit_id (short SHA) and `html_url` from `list_pull_request_reviews`.\n\n## 2. Cross-cutting issue sections (zero or more)\n\nFor each cross-cutting concern, one `### ` section. Use this exact shape:\n\n```\n### {emoji} {short, descriptive title \u2014 what's wrong, not what to do}\n\n{Human-readable problem write-up. Describes the PROBLEM only \u2014 what's broken, what the symptom is, what the blast radius is. NO asks, NO suggested fixes, NO \"the right thing to do is...\". Asks and fixes live in the technical-details block below; the visible part is for the human to *understand* the problem, not to implement it.}\n\n<details><summary>Technical details</summary>\n\n\\`\\`\\`\\`markdown\n# {title repeated}\n\n## Affected sites\n- {file path:line} \u2014 {what's wrong there}\n- ...\n\n## Required outcome\n- {what the fix needs to achieve, not how to achieve it}\n- ...\n\n## Suggested approach (optional)\n{When the fix shape is non-obvious, sketch one or more reasonable directions. Skip when the outcome alone makes the fix obvious.}\n\n## Open questions for the human (optional)\n- {Any decision an implementing agent shouldn't make unilaterally \u2014 pricing thresholds, breaking-change policy, naming, scope of follow-up.}\n\\`\\`\\`\\`\n\n</details>\n```\n\nConcrete example of the visible part of a non-anchored section (technical-details block unchanged from the template above):\n\n```\n### \u2139\uFE0F Legacy `opencode.ts` has no documented deletion plan\n\nThe v2 harness lands alongside the v1 file and imports one helper from it. Worth a follow-up issue or a TODO so the next maintainer doesn't have to re-derive the cleanup plan.\n```\n\nThe example's value is its *shape*: a finding about absence (no deletion plan), not a line-anchored bug. Body sections live or die on whether the concern genuinely doesn't fit on a line.\n\n**Heading severity emoji** \u2014 every `### ` heading carries one:\n\n- \uD83D\uDEA8 critical \u2014 blocks merge (data loss, security, broken core flow)\n- \u26A0\uFE0F important \u2014 must address before merging (regression, missing validation, incorrect behavior)\n- \u2139\uFE0F informational \u2014 surfaced for awareness; mergeable as-is\n\n**Visible problem write-up rules:**\n\n- **No asks, no suggested fixes** in the visible part. The visible portion describes the problem; the technical-details block describes the fix shape and any open questions. The exception: a fix so self-evident that NOT stating it would be weird (e.g. \"the typo is missing an 'r'\") \u2014 in that case, fold it into the problem statement and skip the suggested-approach block in technical details too.\n- **Never two successive plain paragraphs.** Every transition between block-level elements must alternate prose with structure: paragraph \u2192 bullet list \u2192 paragraph; paragraph \u2192 code fence \u2192 bullet list; paragraph \u2192 table \u2192 paragraph. Two consecutive paragraphs in a row create a wall of text that's impossible to digest. If you catch yourself writing one, find a way to split it: pull a list out of it, drop a 2-3 line code fence between them, or merge them into a single tighter paragraph.\n- **Per-paragraph budget:** ~3 sentences max. Past that, you're explaining where you should be structuring.\n- **Identifier discipline still applies** in the visible part. Lead with behavior in plain English; name an identifier only when it's the subject of the concern or a public surface a reader would recognize. The technical-details block is where dense identifier references belong.\n\n**Technical-details block rules:**\n\n- Wrapped in a 4-backtick markdown fence (`\\`\\`\\`\\`markdown ... \\`\\`\\`\\``) so it's visually distinct, one-click copyable, and can contain its own 3-backtick code fences without escape gymnastics. The contents are agent-readable \u2014 a fix-agent will pull the body down and use this block as the brief.\n- File paths and `file:line` refs are encouraged (and necessary) \u2014 the next agent uses these to navigate. Identifier density is fine here.\n- Slightly more verbose than the absolute minimum is OK when it materially helps the next agent: a small code snippet showing the symptom, a short table of mismatched key/column pairs, a one-paragraph \"why CI doesn't catch it\" note. Skip massive regression-test scaffolding or full route rewrites \u2014 the implementing agent writes those.\n- Use the four standard sections (`Affected sites`, `Required outcome`, optional `Suggested approach`, optional `Open questions for the human`). Skip the optional sections when they wouldn't add anything.\n\n## Inline technical details\n\nInline comments are short (~2-3 sentences) by default. When an inline finding has broader implications worth recording for a fix-agent \u2014 e.g. a localized bug whose proper fix requires touching several files, or where the right fix depends on a design decision the human needs to make \u2014 append a collapsed `<details><summary>Technical details</summary>` block to the inline comment's body. Same shape as the body-section technical-details block (4-backtick fenced markdown, `## Affected sites` / `## Required outcome` / optional `## Suggested approach` / optional `## Open questions for the human`).\n\nGitHub renders the same markdown parser in inline comments as in the review body, so the collapsed-details affordance works the same way. The visible part of the inline comment stays scannable; the depth is one click away for any agent that needs it.\n\n## 3. `### \u2139\uFE0F Nitpicks` (optional, last section)\n\nOnly when there are nits that for some reason can't be inlined. Filepaths in nit text are fine \u2014 these are simple enough that a human or agent reads once and acts. No technical-details block.\n\n```\n### \u2139\uFE0F Nitpicks\n\n- {nit, with file path inline if useful, \u2264 ~200 chars}\n- ...\n```\n\n## Inline comment shape\n\nInline comments use the same severity framing as body `### ` sections, scaled down for line-anchored use:\n\n- **Lead with a 1-2 sentence problem statement.** The reader is looking at the line in question, so don't restate what the line says \u2014 describe what's wrong with it. Optionally prefix the visible line with a severity emoji (\uD83D\uDEA8 / \u26A0\uFE0F / \u2139\uFE0F) when severity isn't obvious from context.\n- **Optional `<details><summary>Technical details</summary>...</details>` collapsible** for findings whose technical context (longer file:line references, related-code snippets, suggested approach, regression-risk notes) would overwhelm the human-readable lead-in. Same agent-readable purpose, same 4-backtick fence shape, and same 4-section structure as the body's technical-details block \u2014 see *Inline technical details* above. Encouraged whenever the depth helps a downstream fix-agent; don't force one when the inline lead-in already says everything.\n- **Visible portion \u2264 2-3 sentences.** If you find yourself writing more, that's the cue to split the depth into the `Technical details` collapsible.\n\n## Body-wide rules\n\n- **Inline-vs-body discipline (repeated for emphasis):** anything that anchors to a specific line goes inline (with a `<details>Technical details</details>` block when the implications are broad). The body is for non-anchorable concerns only \u2014 absence, sequencing, design decisions, scope questions, architectural risk.\n- **No `### Issues found` heading** above the issue sections \u2014 each `### ` heading IS the issue.\n- **Severity emoji on every `### ` heading** (\uD83D\uDEA8 / \u26A0\uFE0F / \u2139\uFE0F). No emoji on the preamble lead-in or anywhere else.\n- **GitHub block-level rendering**: GitHub's markdown parser requires a blank line between ALL block-level elements (HTML tags like `<br/>`, `<sub>`, `<details>`, `<b>` and markdown syntax like headings, lists, blockquotes, code fences, paragraphs). Without a blank line, GitHub treats following content as a continuation of the HTML block and renders markdown syntax as literal text. ALWAYS separate block-level elements with a blank line.\n- **Backtick-wrap** every variable, identifier, or file name when you mention one (in either visible or technical-details portions).\n- **Don't repeat diff content**, don't include raw `+123 / -45` stats, don't include a changelog section, don't use horizontal rules (`---`).\n- **Pull file/commit counts from `checkout_pr` metadata** \u2014 never count manually.\n- **Legacy headings REMOVED.** Do not use `### Key changes`, `### Issues found`, `<b>TL;DR</b>`, or `<sub><b>Summary</b>`. The new structure subsumes them.";
|
|
8
|
-
export declare function computeModes(agentId: AgentId): Mode[];
|
|
8
|
+
export declare function computeModes(agentId: AgentId, signedCommits?: boolean): Mode[];
|
|
9
9
|
export declare const modes: Mode[];
|
|
10
10
|
/**
|
|
11
11
|
* modes that legitimately never modify the working tree. used by the post-run
|
package/dist/toolState.d.ts
CHANGED
|
@@ -32,6 +32,40 @@ export type CommentableLines = {
|
|
|
32
32
|
RIGHT: Set<number>;
|
|
33
33
|
LEFT: Set<number>;
|
|
34
34
|
};
|
|
35
|
+
/** access tier of a checked-out repo, drives token routing + push gating. */
|
|
36
|
+
export type RepoAccess = "primary" | "write" | "read";
|
|
37
|
+
/**
|
|
38
|
+
* per-repo working-tree + PR state. the primary repo is keyed like any other
|
|
39
|
+
* in `toolState.repos`; secondaries are added by `checkout_repo`. this is the
|
|
40
|
+
* single source of truth for all repo-scoped git/PR state — there are no
|
|
41
|
+
* parallel inline fields on `ToolState`. read via `primaryRepoState(toolState)`
|
|
42
|
+
* for primary-only call sites, or `requireRepoState(toolState, owner, name)`
|
|
43
|
+
* when a `repo` param routes to a specific checkout.
|
|
44
|
+
*/
|
|
45
|
+
export interface RepoToolState {
|
|
46
|
+
owner: string;
|
|
47
|
+
name: string;
|
|
48
|
+
/** working directory: primary = `process.cwd()`; secondary = `ctx.tmpdir/xrepo/<name>` */
|
|
49
|
+
dir: string;
|
|
50
|
+
access: RepoAccess;
|
|
51
|
+
defaultBranch?: string;
|
|
52
|
+
pushUrl?: string;
|
|
53
|
+
pushDest?: StoredPushDest;
|
|
54
|
+
initialHead?: {
|
|
55
|
+
kind: "branch";
|
|
56
|
+
name: string;
|
|
57
|
+
} | {
|
|
58
|
+
kind: "detached";
|
|
59
|
+
sha: string;
|
|
60
|
+
};
|
|
61
|
+
issueNumber?: number;
|
|
62
|
+
checkoutSha?: string;
|
|
63
|
+
commentableLinesByFile?: Map<string, CommentableLines>;
|
|
64
|
+
commentableLinesPullNumber?: number;
|
|
65
|
+
commentableLinesCheckoutSha?: string | undefined;
|
|
66
|
+
beforeSha?: string;
|
|
67
|
+
diffCoverage?: DiffCoverageState | undefined;
|
|
68
|
+
}
|
|
35
69
|
/**
|
|
36
70
|
* mutable per-run record of facts that occurred during execution. shared
|
|
37
71
|
* between the action process and the MCP server (one process — toolState is
|
|
@@ -55,21 +89,8 @@ export type CommentableLines = {
|
|
|
55
89
|
* MCP tool already records.
|
|
56
90
|
*/
|
|
57
91
|
export interface ToolState {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
initialHead?: {
|
|
61
|
-
kind: "branch";
|
|
62
|
-
name: string;
|
|
63
|
-
} | {
|
|
64
|
-
kind: "detached";
|
|
65
|
-
sha: string;
|
|
66
|
-
};
|
|
67
|
-
issueNumber?: number;
|
|
68
|
-
checkoutSha?: string;
|
|
69
|
-
commentableLinesByFile?: Map<string, CommentableLines>;
|
|
70
|
-
commentableLinesPullNumber?: number;
|
|
71
|
-
commentableLinesCheckoutSha?: string | undefined;
|
|
72
|
-
beforeSha?: string;
|
|
92
|
+
repos: Map<string, RepoToolState>;
|
|
93
|
+
primaryRepoKey: string;
|
|
73
94
|
selectedMode?: string;
|
|
74
95
|
prepushFailureCount: number;
|
|
75
96
|
backgroundProcesses: Map<string, BackgroundProcess>;
|
|
@@ -94,6 +115,7 @@ export interface ToolState {
|
|
|
94
115
|
lastProgressBody?: string;
|
|
95
116
|
wasUpdated?: boolean;
|
|
96
117
|
finalSummaryWritten?: boolean;
|
|
118
|
+
answerCommentPosted?: boolean;
|
|
97
119
|
existingPlanCommentId?: number;
|
|
98
120
|
previousPlanBody?: string;
|
|
99
121
|
summaryFilePath?: string;
|
|
@@ -102,15 +124,23 @@ export interface ToolState {
|
|
|
102
124
|
learningsFilePath?: string;
|
|
103
125
|
learningsSeed?: string;
|
|
104
126
|
learningsPersistAttempted?: boolean;
|
|
127
|
+
xrepoLearningsFilePath?: string;
|
|
128
|
+
xrepoLearningsSeed?: string;
|
|
129
|
+
xrepoLearningsPersistAttempted?: boolean;
|
|
105
130
|
output?: string | undefined;
|
|
106
131
|
usageEntries: AgentUsage[];
|
|
107
132
|
model?: string | undefined;
|
|
108
133
|
modelFallback?: {
|
|
109
134
|
from: string;
|
|
110
135
|
} | undefined;
|
|
136
|
+
unselectedProxyDefault?: boolean | undefined;
|
|
137
|
+
modelClamped?: {
|
|
138
|
+
from: string;
|
|
139
|
+
reason: "card" | "noRouterPath";
|
|
140
|
+
} | undefined;
|
|
141
|
+
shaPinned?: boolean | undefined;
|
|
111
142
|
oss?: boolean | undefined;
|
|
112
143
|
todoTracker?: TodoTracker | undefined;
|
|
113
|
-
diffCoverage?: DiffCoverageState | undefined;
|
|
114
144
|
agentDiagnostic?: AgentDiagnostic | undefined;
|
|
115
145
|
}
|
|
116
146
|
interface InitToolStateParams {
|
|
@@ -118,6 +148,26 @@ interface InitToolStateParams {
|
|
|
118
148
|
id: string;
|
|
119
149
|
type: ProgressCommentType;
|
|
120
150
|
} | undefined;
|
|
151
|
+
owner: string;
|
|
152
|
+
name: string;
|
|
153
|
+
dir: string;
|
|
121
154
|
}
|
|
122
155
|
export declare function initToolState(params: InitToolStateParams): ToolState;
|
|
156
|
+
/** stable "owner/name" key for the `repos` map. lowercased — GitHub repo names are case-insensitive. */
|
|
157
|
+
export declare function repoKey(owner: string, name: string): string;
|
|
158
|
+
/** the primary (triggering) repo's state. throws if init hasn't run. */
|
|
159
|
+
export declare function primaryRepoState(toolState: ToolState): RepoToolState;
|
|
160
|
+
/**
|
|
161
|
+
* state for a specific checked-out repo. throws when the repo isn't a
|
|
162
|
+
* registered checkout — keeps the push/PR allow-set honest (the only way a
|
|
163
|
+
* secondary becomes registered is via `checkout_repo`).
|
|
164
|
+
*/
|
|
165
|
+
export declare function requireRepoState(toolState: ToolState, owner: string, name: string): RepoToolState;
|
|
166
|
+
/** get-or-create a repo's state (used by checkout_repo when registering a secondary). */
|
|
167
|
+
export declare function ensureRepoState(toolState: ToolState, init: {
|
|
168
|
+
owner: string;
|
|
169
|
+
name: string;
|
|
170
|
+
dir: string;
|
|
171
|
+
access: RepoAccess;
|
|
172
|
+
}): RepoToolState;
|
|
123
173
|
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** one working-tree change to include in an API commit. */
|
|
2
|
+
export type ChangedFile = {
|
|
3
|
+
path: string;
|
|
4
|
+
deleted: boolean;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* all working-tree changes vs HEAD: tracked changes (committed-to-index,
|
|
8
|
+
* staged, and unstaged all collapse into `git diff HEAD`) plus untracked
|
|
9
|
+
* files from `git status`. respects .gitignore. throws on unresolved
|
|
10
|
+
* conflicts — the caller's guidance is to resolve and `git add` first.
|
|
11
|
+
*/
|
|
12
|
+
export declare function detectWorkingTreeChanges(): ChangedFile[];
|
|
13
|
+
/**
|
|
14
|
+
* refuse content the API path cannot faithfully commit: git-lfs files (the
|
|
15
|
+
* pointer would be committed without the lfs object upload that the git
|
|
16
|
+
* pre-push hook performs) and directories (nested repositories / submodule
|
|
17
|
+
* pointers). other clean filters are fine — blob content goes through
|
|
18
|
+
* `git hash-object`, which applies them exactly like a local commit.
|
|
19
|
+
*/
|
|
20
|
+
export declare function assertApiCommittable(files: ChangedFile[]): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* create one GitHub-signed commit on `remoteBranch` containing `files` read
|
|
23
|
+
* from the working tree, parented on `parents` (first parent supplies the
|
|
24
|
+
* base tree; a second parent — MERGE_HEAD — makes it a true merge commit so
|
|
25
|
+
* base-branch integration doesn't pollute the PR diff). empty `files` with
|
|
26
|
+
* two parents concludes a merge that resolved to the first parent's tree.
|
|
27
|
+
* creates the remote branch at the new commit when it doesn't exist yet.
|
|
28
|
+
*/
|
|
29
|
+
export declare function createSignedCommit(params: {
|
|
30
|
+
token: string;
|
|
31
|
+
owner: string;
|
|
32
|
+
repo: string;
|
|
33
|
+
remoteBranch: string;
|
|
34
|
+
message: string;
|
|
35
|
+
parents: string[];
|
|
36
|
+
files: ChangedFile[];
|
|
37
|
+
}): Promise<{
|
|
38
|
+
sha: string;
|
|
39
|
+
createdBranch: boolean;
|
|
40
|
+
}>;
|
package/dist/utils/apiKeys.d.ts
CHANGED
|
@@ -27,8 +27,27 @@ export declare function validateAgentApiKey(params: {
|
|
|
27
27
|
* {"type":"error","error":{"type":"authentication_error", ...
|
|
28
28
|
* "Invalid bearer token"}}`) emitted by the Claude CLI for revoked /
|
|
29
29
|
* mistyped / rotated `ANTHROPIC_API_KEY`. see #782.
|
|
30
|
+
* - expired credentials (#931): Bedrock 403 `Failed to authenticate. API
|
|
31
|
+
* Error: 403 {"Message":"*** has expired"}` (short-lived bearer tokens),
|
|
32
|
+
* OpenAI OAuth "Your authentication token has expired", and Codex
|
|
33
|
+
* "Token refresh failed: 401". the Bedrock pattern is anchored to the
|
|
34
|
+
* Claude CLI emission ("Failed to authenticate. API Error:") so generic
|
|
35
|
+
* auth chatter in agent stderr can't misclassify a hang as a key error.
|
|
36
|
+
* - DeepSeek invalid key (#960): `Authentication Fails, Your api key:
|
|
37
|
+
* ****XXXX is invalid`. anchored to "Your api key: ... is invalid" so it
|
|
38
|
+
* can't collide with DeepSeek's already-handled `Insufficient balance`
|
|
39
|
+
* billing shape (which routes to formatProviderBillingExhausted).
|
|
30
40
|
*/
|
|
31
41
|
export declare function isApiKeyAuthError(text: string): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Expired OAuth-connection credential shapes (#931) — the fix is to
|
|
44
|
+
* re-authenticate the provider connection (`pullfrog auth <provider>`), not
|
|
45
|
+
* to rotate a repo-secret API key, so `formatApiKeyErrorSummary` renders
|
|
46
|
+
* distinct copy for these. Patterns are deliberately narrow:
|
|
47
|
+
* "authentication token has expired" (not bare "token has expired") so a
|
|
48
|
+
* GitHub installation-token expiry can't be misread as an LLM key problem.
|
|
49
|
+
*/
|
|
50
|
+
export declare function isOAuthCredentialExpiredError(text: string): boolean;
|
|
32
51
|
/**
|
|
33
52
|
* Friendly Markdown summary for both the missing-key and invalid-key cases.
|
|
34
53
|
* Used in the catch / result-failure paths in `main.ts` to overwrite the raw
|
|
@@ -24,6 +24,33 @@ export interface BuildPullfrogFooterParams {
|
|
|
24
24
|
* so the substitution is visible in PR comments + reviews.
|
|
25
25
|
*/
|
|
26
26
|
fallbackFrom?: string | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* When a Router account had a model (or the intelligent tier) selected that
|
|
29
|
+
* the server clamped to the efficient default — custom picks are card-gated
|
|
30
|
+
* wholesale. `from` is the configured slug (e.g. "anthropic/claude-opus");
|
|
31
|
+
* `reason` names the binding constraint — "card" (no card on file) renders
|
|
32
|
+
* `Using <Kimi K2> (<Claude Opus> needs a card on file)`, "noRouterPath"
|
|
33
|
+
* (no openRouterResolve yet and no stored provider key) renders a
|
|
34
|
+
* provider-key nudge — so the downgrade is visible rather than silently
|
|
35
|
+
* presenting Kimi as the pick.
|
|
36
|
+
*/
|
|
37
|
+
clamped?: {
|
|
38
|
+
from: string;
|
|
39
|
+
reason: "card" | "noRouterPath";
|
|
40
|
+
} | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* true when the run used the default proxy model only because no model was
|
|
43
|
+
* selected (Router billing + "auto"). the footer appends a note nudging the
|
|
44
|
+
* user to pick a model — the cost-optimized default is a weaker reviewer
|
|
45
|
+
* than a frontier model.
|
|
46
|
+
*/
|
|
47
|
+
unselectedProxyDefault?: boolean | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* true when the action is pinned to a full commit SHA — the footer leads
|
|
50
|
+
* with a maintenance nudge to switch to the moving `@v0` tag (a SHA pin
|
|
51
|
+
* freezes the post-run cleanup step, which silently fails the workflow).
|
|
52
|
+
*/
|
|
53
|
+
shaPinned?: boolean | undefined;
|
|
27
54
|
/**
|
|
28
55
|
* true when the run's model costs are covered by the Pullfrog for OSS
|
|
29
56
|
* program — the footer renders `Using <model> (free via Pullfrog for OSS)`
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export type SubscriptionPreflight = {
|
|
2
|
+
usable: true;
|
|
3
|
+
} | {
|
|
4
|
+
usable: false;
|
|
5
|
+
reason: string;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* preflight a Claude subscription OAuth token (`CLAUDE_CODE_OAUTH_TOKEN`)
|
|
9
|
+
* with a 1-token Messages call, so the agent can fall back to
|
|
10
|
+
* `ANTHROPIC_API_KEY` when the subscription is exhausted or revoked instead
|
|
11
|
+
* of failing the whole run at its first model call. rides the same de-facto
|
|
12
|
+
* OAuth surface Claude Code itself uses: Bearer auth + the
|
|
13
|
+
* `claude-code-20250219,oauth-2025-04-20` betas + the identity system prompt.
|
|
14
|
+
*
|
|
15
|
+
* probes the run's own model when known — subscription limits can be
|
|
16
|
+
* per-model ("You've hit your Opus limit"), so a cheaper stand-in could pass
|
|
17
|
+
* preflight and still leave the run dead on arrival.
|
|
18
|
+
*
|
|
19
|
+
* fail-open by design: only 401 (revoked/expired token) and 429
|
|
20
|
+
* (session/weekly/per-model limit) mark the token unusable. network errors,
|
|
21
|
+
* 5xx, and request-shape drift (400) all keep today's subscription-first
|
|
22
|
+
* behavior, so the preflight can never fail a run that would have worked —
|
|
23
|
+
* the worst wrong answer is a run that bills the API key instead of the
|
|
24
|
+
* subscription.
|
|
25
|
+
*/
|
|
26
|
+
export declare function preflightClaudeSubscription(params: {
|
|
27
|
+
token: string;
|
|
28
|
+
/** bare Anthropic model id the run will use (e.g. "claude-fable-5") */
|
|
29
|
+
model: string | undefined;
|
|
30
|
+
}): Promise<SubscriptionPreflight>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ToolState } from "../toolState.ts";
|
|
2
2
|
interface ReportErrorParams {
|
|
3
3
|
toolState: ToolState;
|
|
4
4
|
error: string;
|
|
@@ -6,7 +6,7 @@ interface ReportErrorParams {
|
|
|
6
6
|
/**
|
|
7
7
|
* When the run has no pre-existing progress comment to update (silent
|
|
8
8
|
* IncrementalReview / pull_request_synchronize, mode-less polls), create
|
|
9
|
-
* a fresh issue comment on `
|
|
9
|
+
* a fresh issue comment on the primary repo state's `issueNumber` instead of returning
|
|
10
10
|
* silently. Used for terminal errors (BillingError, TransientError) where
|
|
11
11
|
* the GH job summary is the only other surface and most users never open
|
|
12
12
|
* it. see #775.
|