pullfrog 0.1.67 → 0.1.69
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 +72 -150
- package/dist/cli.mjs +236 -77
- package/dist/effort.d.ts +10 -0
- package/dist/external.d.ts +18 -1
- package/dist/index.js +234 -75
- package/dist/internal/index.d.ts +1 -1
- package/dist/internal.js +91 -8
- package/dist/mcp/reviewComments.d.ts +42 -0
- package/dist/toolState.d.ts +1 -1
- package/dist/utils/apiKeys.d.ts +19 -0
- package/dist/utils/billingErrors.d.ts +1 -2
- package/dist/utils/buildPullfrogFooter.d.ts +4 -1
- package/dist/utils/payload.d.ts +1 -0
- package/dist/utils/providerErrors.d.ts +8 -0
- package/dist/utils/proxy.d.ts +24 -0
- package/dist/utils/runContext.d.ts +7 -0
- package/dist/utils/runContextData.d.ts +9 -0
- package/package.json +1 -1
package/dist/external.d.ts
CHANGED
|
@@ -50,7 +50,7 @@ export declare function findDanglingPromptToolRefs(params: {
|
|
|
50
50
|
toolNames: string[];
|
|
51
51
|
}): string[];
|
|
52
52
|
export type { EffortPosition } from "./effort.ts";
|
|
53
|
-
export { DEFAULT_EFFORT_POSITION, EFFORT_ALIASES, isEffortPosition, offeredRungs, parseEffortPosition, resolveRung, rungLabel, rungPosition, } from "./effort.ts";
|
|
53
|
+
export { DEFAULT_EFFORT_POSITION, EFFORT_ALIASES, isEffortPosition, offeredRungs, parseEffortPosition, resolveRung, rungLabel, rungPosition, SUBSIDY_RUNG, } from "./effort.ts";
|
|
54
54
|
export type { AutoTier, ModelAlias, ModelProvider, ProviderConfig } from "./models.ts";
|
|
55
55
|
export { AUTO_EFFICIENT, AUTO_INTELLIGENT, DEFAULT_PROXY_MODEL, defaultAutoTier, getAutoSelectHintModel, getModelEffortLevels, getModelEnvVars, getModelManagedCredentials, getModelProvider, getProviderDisplayName, isAutoTier, isCardGatedModel, isOssAllowedModel, modelAliases, modelHasStoredAuth, OSS_MODEL_ALLOWLIST, OSS_RECOMMENDED_MODEL, ossFundedModelNames, parseModel, providers, resolveAutoTier, resolveCliModel, resolveDisplayAlias, resolveModelRung, resolveModelSlug, resolveOpenRouterModel, } from "./models.ts";
|
|
56
56
|
export type ToolPermission = "disabled" | "enabled";
|
|
@@ -137,6 +137,15 @@ interface PullRequestReviewSubmittedEvent extends BasePayloadEvent {
|
|
|
137
137
|
body: string | null;
|
|
138
138
|
review_state: string;
|
|
139
139
|
branch: string;
|
|
140
|
+
/**
|
|
141
|
+
* How far past the dispatching review this run may read: `all` on a
|
|
142
|
+
* Pullfrog-authored PR, `mentions` when only `@pullfrog`-mentioning threads
|
|
143
|
+
* are in scope. The server already computes this to decide whether to
|
|
144
|
+
* dispatch; forwarding it is what lets `get_review_comments` pick up the
|
|
145
|
+
* comments that land while the run is working. Optional so a payload from an
|
|
146
|
+
* older server build still parses against a newer action.
|
|
147
|
+
*/
|
|
148
|
+
address_scope?: "all" | "mentions" | undefined;
|
|
140
149
|
}
|
|
141
150
|
interface PullRequestReviewCommentCreatedEvent extends BasePayloadEvent {
|
|
142
151
|
trigger: "pull_request_review_comment_created";
|
|
@@ -257,6 +266,14 @@ export interface WriteablePayload {
|
|
|
257
266
|
"~pullfrog": true;
|
|
258
267
|
/** semantic version of the payload to ensure compatibility */
|
|
259
268
|
version: string;
|
|
269
|
+
/**
|
|
270
|
+
* which kind of work this run is (`pr-created`, `issue-created`, …). the
|
|
271
|
+
* server's own run-type vocabulary, opaque here: the action only forwards it
|
|
272
|
+
* to `run-context`, which resolves the repo's per-trigger model override
|
|
273
|
+
* against it. absent ⇒ the repo default, which is what every run got before
|
|
274
|
+
* overrides existed.
|
|
275
|
+
*/
|
|
276
|
+
type?: string | undefined;
|
|
260
277
|
/** provider/model slug (e.g. "anthropic/claude-opus") */
|
|
261
278
|
model?: string | undefined;
|
|
262
279
|
/**
|
package/dist/index.js
CHANGED
|
@@ -107718,6 +107718,7 @@ import { performance as performance5 } from "node:perf_hooks";
|
|
|
107718
107718
|
|
|
107719
107719
|
// effort.ts
|
|
107720
107720
|
var DEFAULT_EFFORT_POSITION = 0.5;
|
|
107721
|
+
var SUBSIDY_RUNG = "high";
|
|
107721
107722
|
var EFFORT_ALIASES = {
|
|
107722
107723
|
low: 0,
|
|
107723
107724
|
medium: 0.25,
|
|
@@ -108121,6 +108122,15 @@ var providers = {
|
|
|
108121
108122
|
// the model does cost nothing.
|
|
108122
108123
|
isFree: true
|
|
108123
108124
|
},
|
|
108125
|
+
// Zen meters Fable like any other model, so this route reaches it without
|
|
108126
|
+
// the Anthropic access grant `anthropic/claude-fable` still needs.
|
|
108127
|
+
"claude-fable": {
|
|
108128
|
+
displayName: "Claude Fable",
|
|
108129
|
+
resolve: "opencode/claude-fable-5-1",
|
|
108130
|
+
effort: ["low", "medium", "high", "xhigh", "max"],
|
|
108131
|
+
openRouterResolve: "openrouter/~anthropic/claude-fable-latest",
|
|
108132
|
+
subagentModel: "claude-sonnet"
|
|
108133
|
+
},
|
|
108124
108134
|
"claude-opus": {
|
|
108125
108135
|
displayName: "Claude Opus",
|
|
108126
108136
|
resolve: "opencode/claude-opus-5",
|
|
@@ -108234,6 +108244,17 @@ var providers = {
|
|
|
108234
108244
|
effort: ["minimal", "low", "medium", "high"],
|
|
108235
108245
|
openRouterResolve: "openrouter/google/gemini-3.6-flash"
|
|
108236
108246
|
},
|
|
108247
|
+
// Zen serves K3, but rule 7 can only move a mirror when its upstream moves,
|
|
108248
|
+
// so the K3 generation could never reach this block by bumping `kimi-k2`.
|
|
108249
|
+
"kimi-k3": {
|
|
108250
|
+
displayName: "Kimi K3",
|
|
108251
|
+
resolve: "opencode/kimi-k3",
|
|
108252
|
+
// Zen publishes a single rung for K3 where OpenRouter publishes three.
|
|
108253
|
+
effort: ["max"],
|
|
108254
|
+
openRouterEffort: ["low", "high", "max"],
|
|
108255
|
+
openRouterResolve: "openrouter/moonshotai/kimi-k3",
|
|
108256
|
+
subagentModel: "kimi-k2"
|
|
108257
|
+
},
|
|
108237
108258
|
"kimi-k2": {
|
|
108238
108259
|
displayName: "Kimi K2",
|
|
108239
108260
|
// k2.7-code is UNDEPLOYED on Zen — still listed in /zen/v1/models, but
|
|
@@ -108245,25 +108266,57 @@ var providers = {
|
|
|
108245
108266
|
resolve: "opencode/kimi-k2.6",
|
|
108246
108267
|
openRouterResolve: "openrouter/moonshotai/kimi-k2.7-code"
|
|
108247
108268
|
},
|
|
108269
|
+
// M3 is its own line beside M2, not a bump of it — same reason `kimi-k3`
|
|
108270
|
+
// sits beside `kimi-k2` rather than replacing it.
|
|
108271
|
+
"minimax-m3": {
|
|
108272
|
+
displayName: "MiniMax M3",
|
|
108273
|
+
resolve: "opencode/minimax-m3",
|
|
108274
|
+
openRouterResolve: "openrouter/minimax/minimax-m3"
|
|
108275
|
+
},
|
|
108248
108276
|
// slug pins the m2 line for DB stability; resolve tracks the current m2.7.
|
|
108249
108277
|
"minimax-m2.5": {
|
|
108250
108278
|
displayName: "MiniMax M2",
|
|
108251
108279
|
resolve: "opencode/minimax-m2.7",
|
|
108252
108280
|
openRouterResolve: "openrouter/minimax/minimax-m2.7"
|
|
108253
108281
|
},
|
|
108282
|
+
// Z.ai and xAI reach Zen subscribers only here — before this, `opencode-go`
|
|
108283
|
+
// was the catalog's only GLM route, and Grok had no Zen route at all.
|
|
108284
|
+
glm: {
|
|
108285
|
+
displayName: "GLM",
|
|
108286
|
+
resolve: "opencode/glm-5.3",
|
|
108287
|
+
effort: ["low", "high", "max"],
|
|
108288
|
+
openRouterResolve: "openrouter/z-ai/glm-5.3"
|
|
108289
|
+
},
|
|
108290
|
+
grok: {
|
|
108291
|
+
displayName: "Grok",
|
|
108292
|
+
resolve: "opencode/grok-4.6",
|
|
108293
|
+
effort: ["low", "medium", "high", "xhigh"],
|
|
108294
|
+
openRouterResolve: "openrouter/x-ai/grok-4.6"
|
|
108295
|
+
},
|
|
108254
108296
|
"gpt-5-nano": {
|
|
108255
108297
|
displayName: "GPT Nano",
|
|
108256
108298
|
resolve: "opencode/gpt-5-nano",
|
|
108257
108299
|
effort: ["minimal", "low", "medium", "high"],
|
|
108258
108300
|
openRouterResolve: "openrouter/openai/gpt-5-nano"
|
|
108259
108301
|
},
|
|
108302
|
+
// Zen's live free MiMo, and the second free row in a menu that big-pickle
|
|
108303
|
+
// was alone in since `mimo-v2-pro-free` lost its model.
|
|
108304
|
+
mimo: {
|
|
108305
|
+
displayName: "MiMo",
|
|
108306
|
+
resolve: "opencode/mimo-v2.5-free",
|
|
108307
|
+
// free to run, still gated on the provider's own OPENCODE_API_KEY —
|
|
108308
|
+
// see the big-pickle note above (#1077).
|
|
108309
|
+
isFree: true
|
|
108310
|
+
},
|
|
108260
108311
|
"mimo-v2-pro-free": {
|
|
108261
108312
|
displayName: "MiMo V2 Pro",
|
|
108262
108313
|
resolve: "opencode/mimo-v2-pro-free",
|
|
108263
108314
|
// free to run, still gated on the provider's own OPENCODE_API_KEY —
|
|
108264
108315
|
// see the big-pickle note above (#1077).
|
|
108265
108316
|
isFree: true,
|
|
108266
|
-
|
|
108317
|
+
// the model id Zen dropped; land a stored MiMo pick back on MiMo rather
|
|
108318
|
+
// than on the unrelated model it had to settle for while none was live.
|
|
108319
|
+
fallback: "opencode/mimo"
|
|
108267
108320
|
},
|
|
108268
108321
|
"minimax-m2.5-free": {
|
|
108269
108322
|
displayName: "MiniMax M2",
|
|
@@ -108279,10 +108332,11 @@ var providers = {
|
|
|
108279
108332
|
// OpenCode Go is a separate $10/mo subscription from Zen, served on its own
|
|
108280
108333
|
// base URL (`https://opencode.ai/zen/go/v1`) but authenticated with the SAME
|
|
108281
108334
|
// `OPENCODE_API_KEY`. it carries the open-weight coding models plus a couple
|
|
108282
|
-
// of frontier ones,
|
|
108283
|
-
//
|
|
108284
|
-
//
|
|
108285
|
-
//
|
|
108335
|
+
// of frontier ones, six of which are served ONLY here — Zen's `/v1/models`
|
|
108336
|
+
// does not list qwen3.7/3.8-*, mimo-v2.5-pro, longcat-2.0 or hy3. (it listed
|
|
108337
|
+
// no glm-5.3* either until 2026-09, which is why `opencode/glm` trailed on
|
|
108338
|
+
// 5.2.) so for a Go subscriber this provider is not a duplicate route to Zen,
|
|
108339
|
+
// it is the only route to a large part of what they pay for.
|
|
108286
108340
|
// like `opencode` and `openrouter` this is a ROUTER, not a vendor: slugs and
|
|
108287
108341
|
// display names mirror the upstream brand tier, and the picker groups them
|
|
108288
108342
|
// under the upstream vendor.
|
|
@@ -108351,13 +108405,25 @@ var providers = {
|
|
|
108351
108405
|
// `high` the Go route does not.
|
|
108352
108406
|
effort: ["low", "medium", "xhigh"],
|
|
108353
108407
|
openRouterEffort: ["minimal", "low", "medium", "high", "xhigh"],
|
|
108354
|
-
|
|
108408
|
+
// OpenRouter retired the rolling `qwen3.8-max` for a dated snapshot, so
|
|
108409
|
+
// this side has to pin one; the Go route still carries the rolling id.
|
|
108410
|
+
openRouterResolve: "openrouter/qwen/qwen3.8-max-0902"
|
|
108355
108411
|
},
|
|
108356
108412
|
"qwen-plus": {
|
|
108357
108413
|
displayName: "Qwen Plus",
|
|
108358
108414
|
resolve: "opencode-go/qwen3.7-plus",
|
|
108359
108415
|
openRouterResolve: "openrouter/qwen/qwen3.7-plus"
|
|
108360
108416
|
},
|
|
108417
|
+
// the cheap rung Alibaba added under Plus (0.15/0.47 against 0.5/3), and
|
|
108418
|
+
// the only model either OpenCode plan has added since this block was written.
|
|
108419
|
+
"qwen-flash": {
|
|
108420
|
+
displayName: "Qwen Flash",
|
|
108421
|
+
resolve: "opencode-go/qwen3.8-flash",
|
|
108422
|
+
effort: ["low", "medium", "xhigh"],
|
|
108423
|
+
// the Go route publishes rungs where OpenRouter publishes none.
|
|
108424
|
+
openRouterEffort: [],
|
|
108425
|
+
openRouterResolve: "openrouter/qwen/qwen3.8-flash"
|
|
108426
|
+
},
|
|
108361
108427
|
// MiniMax — parity with opencode/* and openrouter/*; the m2 slug pins the
|
|
108362
108428
|
// line for DB stability while the resolve tracks the current m2.7.
|
|
108363
108429
|
"minimax-m3": {
|
|
@@ -109482,6 +109548,7 @@ async function installFromNpmTarball(params) {
|
|
|
109482
109548
|
// utils/providerErrors.ts
|
|
109483
109549
|
var PROVIDER_BILLING_EXHAUSTED_LABEL = "provider billing exhausted";
|
|
109484
109550
|
var PROVIDER_NO_ENDPOINTS_LABEL = "provider no routable endpoints";
|
|
109551
|
+
var PROVIDER_USAGE_LIMIT_LABEL = "provider usage limit reached";
|
|
109485
109552
|
var statusKey = `\\b(?:status[_ ]?code|http[_ ]?status|status)["']?\\s*[:=]\\s*["']?`;
|
|
109486
109553
|
var ANTHROPIC_SPEND_CAP_PATTERN = /reached your specified API usage limits\.?(?:\s*You will regain access on ([^"\n]+?)\.)?/i;
|
|
109487
109554
|
function findAnthropicSpendCap(text) {
|
|
@@ -109527,6 +109594,14 @@ var PROVIDER_ERROR_PATTERNS = [
|
|
|
109527
109594
|
// the wallet-shaped patterns above reach, and its 400 sits nowhere near a
|
|
109528
109595
|
// `status:` key either — so the whole message was dropped on the floor (#1208).
|
|
109529
109596
|
{ regex: ANTHROPIC_SPEND_CAP_PATTERN, label: PROVIDER_BILLING_EXHAUSTED_LABEL },
|
|
109597
|
+
// OpenCode Zen / Codex-subscription cap: `AI_APICallError: The usage limit has
|
|
109598
|
+
// been reached`. Its noun is "usage limit" with no "rate", no status code and
|
|
109599
|
+
// no wallet wording, so every pattern here missed it and `lastProviderError`
|
|
109600
|
+
// stayed unset — which suppresses nothing, so the run was reported as "The
|
|
109601
|
+
// model produced no output at all … usually transient; re-running often
|
|
109602
|
+
// succeeds" while the same log held nine of these. Measured on a live account
|
|
109603
|
+
// whose ChatGPT subscription was capped.
|
|
109604
|
+
{ regex: /\bthe usage limit has been reached\b/i, label: PROVIDER_USAGE_LIMIT_LABEL },
|
|
109530
109605
|
// the OpenRouter shapes `isRouterKeylimitExhaustedError` matches. on a Router
|
|
109531
109606
|
// run those mean the PULLFROG wallet is empty and `renderRunError` returns a
|
|
109532
109607
|
// `BillingError` before ever reaching this list; on a BYOK run the very same
|
|
@@ -109657,7 +109732,7 @@ function ossEffortFloor(ctx) {
|
|
|
109657
109732
|
const alias = resolveRunAlias(ctx);
|
|
109658
109733
|
const published = alias?.openRouterEffort ?? alias?.effort;
|
|
109659
109734
|
if (!published) return 0;
|
|
109660
|
-
return rungPosition({ rung:
|
|
109735
|
+
return rungPosition({ rung: SUBSIDY_RUNG, published }) ?? 0;
|
|
109661
109736
|
}
|
|
109662
109737
|
function resolveRunEffort(ctx) {
|
|
109663
109738
|
const configured = ctx.payload.effort !== void 0;
|
|
@@ -109689,7 +109764,7 @@ var import_semver = __toESM(require_semver2(), 1);
|
|
|
109689
109764
|
// package.json
|
|
109690
109765
|
var package_default = {
|
|
109691
109766
|
name: "pullfrog",
|
|
109692
|
-
version: "0.1.
|
|
109767
|
+
version: "0.1.69",
|
|
109693
109768
|
type: "module",
|
|
109694
109769
|
bin: {
|
|
109695
109770
|
pullfrog: "dist/cli.mjs",
|
|
@@ -110496,6 +110571,7 @@ For simple, well-defined tasks, skip the plan phase and go straight to build.`
|
|
|
110496
110571
|
- record what was done (or why nothing was done)
|
|
110497
110572
|
|
|
110498
110573
|
5. Quality check:
|
|
110574
|
+
- re-call \`${t("get_review_comments")}\` with \`fresh: true\` before committing: reviewers keep commenting while you work, and any thread it now returns that you have not addressed is in scope for this run.
|
|
110499
110575
|
- test changes, then review the diff before committing \u2014 verify only intended changes are present, no debug artifacts remain, no fix turned out to be bloat in context (revert any that did), and the changes are clean enough that a senior engineer would approve without hesitation
|
|
110500
110576
|
- ${commitStep}
|
|
110501
110577
|
|
|
@@ -110505,7 +110581,7 @@ For simple, well-defined tasks, skip the plan phase and go straight to build.`
|
|
|
110505
110581
|
- **once the fix is live on the remote**, for each thread you acted on:
|
|
110506
110582
|
- reply ONCE via \`${t("reply_to_review_comment")}\`. The \`comment_id\` parameter takes the root comment's numeric \`id=\` (from the first \`comment author=...\` tag in the \`${t("get_review_comments")}\` output) \u2014 NOT the \`thread=\` value; that's a separate GraphQL ID used by resolve. The runtime dedupes identical bodies within a session.
|
|
110507
110583
|
- **immediately** call \`${t("resolve_review_thread")}\` with that thread's \`thread=\` value as \`thread_id\`. Resolve every thread where you (a) made the requested code change in full \u2014 partial fixes leave the thread open \u2014 OR (b) replied with a substantive answer the user explicitly asked for. Do NOT resolve threads where you pushed back on the request and the disagreement is unresolved; leave those open for the human to mediate.
|
|
110508
|
-
- call \`${t("report_progress")}\` with a brief summary
|
|
110584
|
+
- call \`${t("report_progress")}\` with a brief summary. If your last \`${t("get_review_comments")}\` turned up threads you did not address, name them there \u2014 a review arriving this late is suppressed as a duplicate and nothing else will pick it up.`
|
|
110509
110585
|
},
|
|
110510
110586
|
// Review and IncrementalReview route the minimum reviewfrog specialists
|
|
110511
110587
|
// needed to cover unresolved, disposition-changing hypotheses. Most runs
|
|
@@ -192373,6 +192449,9 @@ function formatModelLabel(params) {
|
|
|
192373
192449
|
if (params.fallbackFrom) {
|
|
192374
192450
|
return `${base} (credentials for ${providerDisplayName(params.fallbackFrom)} were rejected by the provider)`;
|
|
192375
192451
|
}
|
|
192452
|
+
if (params.clamped?.reason === "trial") {
|
|
192453
|
+
return `${base} (free trial)`;
|
|
192454
|
+
}
|
|
192376
192455
|
if (params.clamped) {
|
|
192377
192456
|
const target = isAutoTier(params.clamped.from) ? "the intelligent tier" : `\`${resolveDisplayAlias(params.clamped.from)?.displayName ?? params.clamped.from}\``;
|
|
192378
192457
|
return params.clamped.reason === "card" ? `${base} (${target} needs a [card on file](https://docs.pullfrog.com/models))` : `${base} (${target} needs a [provider key](https://docs.pullfrog.com/models) \u2014 no Router support yet)`;
|
|
@@ -192382,6 +192461,15 @@ function formatModelLabel(params) {
|
|
|
192382
192461
|
}
|
|
192383
192462
|
return base;
|
|
192384
192463
|
}
|
|
192464
|
+
function buildTrialDisclosure(owner) {
|
|
192465
|
+
const consoleUrl = owner ? `https://pullfrog.com/console/${owner}` : "https://pullfrog.com/console";
|
|
192466
|
+
return [
|
|
192467
|
+
"---",
|
|
192468
|
+
"",
|
|
192469
|
+
"> [!IMPORTANT]",
|
|
192470
|
+
`> **This run used the free trial model.** \`DeepSeek Flash\` is fast and cheap \u2014 expect lighter work than a frontier model. [Connect a subscription or API key \u2192](${consoleUrl})`
|
|
192471
|
+
].join("\n");
|
|
192472
|
+
}
|
|
192385
192473
|
function buildPullfrogFooter(params) {
|
|
192386
192474
|
const parts = [];
|
|
192387
192475
|
if (params.shaPinned) {
|
|
@@ -192414,10 +192502,13 @@ function buildPullfrogFooter(params) {
|
|
|
192414
192502
|
);
|
|
192415
192503
|
}
|
|
192416
192504
|
const allParts = [...parts, "[\u{1D54F}](https://x.com/pullfrogai)"];
|
|
192505
|
+
const disclosure = params.clamped?.reason === "trial" ? `${buildTrialDisclosure(params.owner)}
|
|
192506
|
+
|
|
192507
|
+
` : "";
|
|
192417
192508
|
return `
|
|
192418
192509
|
|
|
192419
192510
|
${PULLFROG_DIVIDER}
|
|
192420
|
-
<sup>${FROG_LOGO} \uFF5C ${allParts.join(" \uFF5C ")}</sup>`;
|
|
192511
|
+
${disclosure}<sup>${FROG_LOGO} \uFF5C ${allParts.join(" \uFF5C ")}</sup>`;
|
|
192421
192512
|
}
|
|
192422
192513
|
function stripExistingFooter(body) {
|
|
192423
192514
|
const dividerIndex = body.indexOf(PULLFROG_DIVIDER);
|
|
@@ -192571,7 +192662,8 @@ function buildCommentFooter(ctx, customParts) {
|
|
|
192571
192662
|
clamped: ctx.toolState.modelClamped,
|
|
192572
192663
|
unselectedProxyDefault: ctx.toolState.unselectedProxyDefault,
|
|
192573
192664
|
shaPinned: ctx.toolState.shaPinned,
|
|
192574
|
-
oss: ctx.oss
|
|
192665
|
+
oss: ctx.oss,
|
|
192666
|
+
owner: ctx.repo.owner
|
|
192575
192667
|
});
|
|
192576
192668
|
}
|
|
192577
192669
|
function buildImplementPlanLink(ctx, issueNumber, commentId) {
|
|
@@ -193570,7 +193662,8 @@ async function createAndSubmitWithFooter(ctx, params, opts) {
|
|
|
193570
193662
|
clamped: ctx.toolState.modelClamped,
|
|
193571
193663
|
unselectedProxyDefault: ctx.toolState.unselectedProxyDefault,
|
|
193572
193664
|
shaPinned: ctx.toolState.shaPinned,
|
|
193573
|
-
oss: ctx.oss
|
|
193665
|
+
oss: ctx.oss,
|
|
193666
|
+
owner: ctx.repo.owner
|
|
193574
193667
|
});
|
|
193575
193668
|
return await ctx.octokit.rest.pulls.submitReview({
|
|
193576
193669
|
owner: params.owner,
|
|
@@ -196331,7 +196424,8 @@ function buildPrBodyWithFooter(ctx, body) {
|
|
|
196331
196424
|
clamped: ctx.toolState.modelClamped,
|
|
196332
196425
|
unselectedProxyDefault: ctx.toolState.unselectedProxyDefault,
|
|
196333
196426
|
shaPinned: ctx.toolState.shaPinned,
|
|
196334
|
-
oss: ctx.oss
|
|
196427
|
+
oss: ctx.oss,
|
|
196428
|
+
owner: ctx.repo.owner
|
|
196335
196429
|
});
|
|
196336
196430
|
const bodyWithoutFooter = stripExistingFooter(fixDoubleEscapedString(body));
|
|
196337
196431
|
return `${bodyWithoutFooter}${footer}`;
|
|
@@ -196666,7 +196760,10 @@ function extractFromFilePatches(hunks, startLine, endLine, side) {
|
|
|
196666
196760
|
}
|
|
196667
196761
|
var GetReviewComments = type({
|
|
196668
196762
|
pull_number: type.number.describe("The pull request number"),
|
|
196669
|
-
review_id: type.number.describe("The review ID to get comments for")
|
|
196763
|
+
review_id: type.number.describe("The review ID to get comments for"),
|
|
196764
|
+
"fresh?": type.boolean.describe(
|
|
196765
|
+
"Bypass the 60s thread cache. Use for the re-read before committing, so comments that landed while you worked are actually seen; leave unset otherwise."
|
|
196766
|
+
)
|
|
196670
196767
|
});
|
|
196671
196768
|
function hasThumbsUpFrom(comment, username) {
|
|
196672
196769
|
if (!comment.reactionGroups) return false;
|
|
@@ -196700,7 +196797,7 @@ function formatReviewThreads(threadBlocks, header) {
|
|
|
196700
196797
|
}
|
|
196701
196798
|
const lines = [];
|
|
196702
196799
|
lines.push(
|
|
196703
|
-
`#
|
|
196800
|
+
`# Open Threads (${threadBlocks.length}) on PR #${header.pullNumber} - dispatched by Review ${header.reviewId} from ${header.reviewer}`
|
|
196704
196801
|
);
|
|
196705
196802
|
lines.push("");
|
|
196706
196803
|
if (threadBlocks.length > 0) {
|
|
@@ -196841,11 +196938,27 @@ async function getReviewThreads(input2) {
|
|
|
196841
196938
|
if (!thread?.comments?.nodes) return false;
|
|
196842
196939
|
return thread.comments.nodes.some((c) => c?.pullRequestReview?.databaseId === input2.reviewId);
|
|
196843
196940
|
});
|
|
196844
|
-
if (
|
|
196845
|
-
|
|
196941
|
+
if (input2.approvedBy) {
|
|
196942
|
+
const username = input2.approvedBy;
|
|
196943
|
+
return threadsForReview.filter((thread) => threadHasThumbsUpFrom(thread, username));
|
|
196846
196944
|
}
|
|
196847
|
-
|
|
196848
|
-
return threadsForReview
|
|
196945
|
+
if (!input2.addressScope) return threadsForReview;
|
|
196946
|
+
return [...threadsForReview, ...concurrentThreads(allThreads, threadsForReview, input2)];
|
|
196947
|
+
}
|
|
196948
|
+
function concurrentThreads(allThreads, threadsForReview, input2) {
|
|
196949
|
+
const alreadyIncluded = new Set(threadsForReview.map((thread) => thread.id));
|
|
196950
|
+
return allThreads.filter((thread) => {
|
|
196951
|
+
if (!thread?.comments?.nodes || alreadyIncluded.has(thread.id)) return false;
|
|
196952
|
+
if (thread.isResolved) return false;
|
|
196953
|
+
const comments = thread.comments.nodes.filter((c) => c !== null);
|
|
196954
|
+
if (comments.length === 0) return false;
|
|
196955
|
+
if (comments.some((c) => isPullfrog(c.author?.login))) return false;
|
|
196956
|
+
return input2.addressScope === "all" || comments.some((c) => mentionsPullfrog(c.body));
|
|
196957
|
+
});
|
|
196958
|
+
}
|
|
196959
|
+
function mentionsPullfrog(body) {
|
|
196960
|
+
if (!body) return false;
|
|
196961
|
+
return body.split(/\r?\n/).filter((line) => !line.trim().startsWith(">")).join("\n").toLowerCase().includes("@pullfrog");
|
|
196849
196962
|
}
|
|
196850
196963
|
function formatReviewData(input2) {
|
|
196851
196964
|
const rawReviewBody = input2.review.body;
|
|
@@ -196942,6 +197055,7 @@ function GetReviewCommentsTool(ctx) {
|
|
|
196942
197055
|
parameters: GetReviewComments,
|
|
196943
197056
|
execute: execute(async (params) => {
|
|
196944
197057
|
const approvedBy = ctx.payload.event.trigger === "fix_review" && ctx.payload.event.approved_only ? ctx.payload.triggerer : void 0;
|
|
197058
|
+
if (params.fresh) invalidateReviewThreadCache(ctx.repo);
|
|
196945
197059
|
const result = await getReviewData({
|
|
196946
197060
|
octokit: ctx.octokit,
|
|
196947
197061
|
owner: ctx.repo.owner,
|
|
@@ -196949,6 +197063,7 @@ function GetReviewCommentsTool(ctx) {
|
|
|
196949
197063
|
pullNumber: params.pull_number,
|
|
196950
197064
|
reviewId: params.review_id,
|
|
196951
197065
|
approvedBy,
|
|
197066
|
+
addressScope: ctx.payload.event.address_scope,
|
|
196952
197067
|
tmpdir: ctx.tmpdir,
|
|
196953
197068
|
githubToken: ctx.githubInstallationToken
|
|
196954
197069
|
});
|
|
@@ -196979,7 +197094,7 @@ function GetReviewCommentsTool(ctx) {
|
|
|
196979
197094
|
threadCount: threadBlocks.length,
|
|
196980
197095
|
commentsPath,
|
|
196981
197096
|
toc: formatted.toc,
|
|
196982
|
-
instructions: `the file at commentsPath contains ${threadBlocks.length} review threads with full conversation history. comments marked with * are from the target review (${params.review_id}). the TOC shows each thread's file:line and the line number where it appears in the file. to read a specific thread, use: grep -A 50 "^## <file:line>" ${commentsPath} (replace <file:line> with the path from the TOC, e.g. "^## action/utils/foo.ts:42"). address each thread in order, working through one file at a time.`
|
|
197097
|
+
instructions: `the file at commentsPath contains ${threadBlocks.length} review threads with full conversation history. comments marked with * are from the target review (${params.review_id}); any other thread is open feedback on this PR that is also yours to address, including comments that landed after this run started. the TOC shows each thread's file:line and the line number where it appears in the file. to read a specific thread, use: grep -A 50 "^## <file:line>" ${commentsPath} (replace <file:line> with the path from the TOC, e.g. "^## action/utils/foo.ts:42"). address each thread in order, working through one file at a time.`
|
|
196983
197098
|
};
|
|
196984
197099
|
})
|
|
196985
197100
|
});
|
|
@@ -197638,10 +197753,12 @@ function isByokSetupError(text) {
|
|
|
197638
197753
|
var SECRETS_UNAVAILABLE_MARKER = "couldn't load your Pullfrog secrets";
|
|
197639
197754
|
var ROUTER_UNFUNDED_MARKER = "your Pullfrog Router balance is empty";
|
|
197640
197755
|
var CREDENTIAL_REJECTED_MARKER = "was rejected by its provider";
|
|
197641
|
-
|
|
197642
|
-
|
|
197643
|
-
|
|
197644
|
-
|
|
197756
|
+
var NoUsableCredentialError = class extends Error {
|
|
197757
|
+
};
|
|
197758
|
+
function throwKeyError(params) {
|
|
197759
|
+
if (params.secretsUnavailable) throw new Error(buildSecretsUnavailableError(params));
|
|
197760
|
+
if (params.routerUnfunded) throw new NoUsableCredentialError(buildRouterUnfundedError(params));
|
|
197761
|
+
throw new NoUsableCredentialError(buildMissingApiKeyError(params));
|
|
197645
197762
|
}
|
|
197646
197763
|
function buildRouterUnfundedError(params) {
|
|
197647
197764
|
const billingUrl = `${getApiUrl()}/console/${params.owner}#billing`;
|
|
@@ -197840,57 +197957,51 @@ function validateAgentApiKey(params) {
|
|
|
197840
197957
|
validateVertexSetup({ owner: params.owner, name: params.name });
|
|
197841
197958
|
return;
|
|
197842
197959
|
}
|
|
197843
|
-
|
|
197844
|
-
|
|
197960
|
+
if (process.env[BEDROCK_MODEL_ID_ENV]?.trim() === params.model) {
|
|
197961
|
+
validateBedrockSetup({ owner: params.owner, name: params.name });
|
|
197962
|
+
return;
|
|
197963
|
+
}
|
|
197845
197964
|
}
|
|
197846
197965
|
if (params.agent.name === "opencode") {
|
|
197847
197966
|
if (params.authorized.has(params.model)) return;
|
|
197848
197967
|
const reason = getModelsFailure();
|
|
197849
197968
|
if (reason) throw new Error(reason);
|
|
197850
197969
|
if (getModelEnvVars(params.model).some(hasEnvVar2)) return;
|
|
197851
|
-
|
|
197852
|
-
buildKeyError({
|
|
197853
|
-
owner: params.owner,
|
|
197854
|
-
name: params.name,
|
|
197855
|
-
model: params.model,
|
|
197856
|
-
secretsUnavailable: params.secretsUnavailable,
|
|
197857
|
-
routerUnfunded: params.routerUnfunded
|
|
197858
|
-
})
|
|
197859
|
-
);
|
|
197860
|
-
}
|
|
197861
|
-
if (hasSingleProviderAuth(params.agent.name)) return;
|
|
197862
|
-
throw new Error(
|
|
197863
|
-
buildKeyError({
|
|
197970
|
+
throwKeyError({
|
|
197864
197971
|
owner: params.owner,
|
|
197865
197972
|
name: params.name,
|
|
197866
197973
|
model: params.model,
|
|
197867
197974
|
secretsUnavailable: params.secretsUnavailable,
|
|
197868
197975
|
routerUnfunded: params.routerUnfunded
|
|
197869
|
-
})
|
|
197870
|
-
|
|
197976
|
+
});
|
|
197977
|
+
}
|
|
197978
|
+
if (hasSingleProviderAuth(params.agent.name)) return;
|
|
197979
|
+
throwKeyError({
|
|
197980
|
+
owner: params.owner,
|
|
197981
|
+
name: params.name,
|
|
197982
|
+
model: params.model,
|
|
197983
|
+
secretsUnavailable: params.secretsUnavailable,
|
|
197984
|
+
routerUnfunded: params.routerUnfunded
|
|
197985
|
+
});
|
|
197871
197986
|
}
|
|
197872
197987
|
if (params.agent.name === "opencode") {
|
|
197873
197988
|
if ([...params.authorized].some(modelHasRuntimeAuth)) return;
|
|
197874
197989
|
const reason = getModelsFailure();
|
|
197875
197990
|
if (reason) throw new Error(reason);
|
|
197876
|
-
|
|
197877
|
-
buildKeyError({
|
|
197878
|
-
owner: params.owner,
|
|
197879
|
-
name: params.name,
|
|
197880
|
-
secretsUnavailable: params.secretsUnavailable,
|
|
197881
|
-
routerUnfunded: params.routerUnfunded
|
|
197882
|
-
})
|
|
197883
|
-
);
|
|
197884
|
-
}
|
|
197885
|
-
if (hasSingleProviderAuth(params.agent.name)) return;
|
|
197886
|
-
throw new Error(
|
|
197887
|
-
buildKeyError({
|
|
197991
|
+
throwKeyError({
|
|
197888
197992
|
owner: params.owner,
|
|
197889
197993
|
name: params.name,
|
|
197890
197994
|
secretsUnavailable: params.secretsUnavailable,
|
|
197891
197995
|
routerUnfunded: params.routerUnfunded
|
|
197892
|
-
})
|
|
197893
|
-
|
|
197996
|
+
});
|
|
197997
|
+
}
|
|
197998
|
+
if (hasSingleProviderAuth(params.agent.name)) return;
|
|
197999
|
+
throwKeyError({
|
|
198000
|
+
owner: params.owner,
|
|
198001
|
+
name: params.name,
|
|
198002
|
+
secretsUnavailable: params.secretsUnavailable,
|
|
198003
|
+
routerUnfunded: params.routerUnfunded
|
|
198004
|
+
});
|
|
197894
198005
|
}
|
|
197895
198006
|
function isApiKeyAuthError(text) {
|
|
197896
198007
|
if (!text) return false;
|
|
@@ -198015,9 +198126,9 @@ function formatApiKeyErrorSummary(params) {
|
|
|
198015
198126
|
].join("\n");
|
|
198016
198127
|
}
|
|
198017
198128
|
return [
|
|
198018
|
-
`**Your LLM provider API key was rejected.** Rotate the key in your provider dashboard, then update the
|
|
198129
|
+
`**Your LLM provider API key was rejected.** Rotate the key in your provider dashboard, then update the copy Pullfrog uses \u2014 wherever you saved it.`,
|
|
198019
198130
|
"",
|
|
198020
|
-
`[
|
|
198131
|
+
`[Pullfrog secrets \u2192](${getApiUrl()}/console/${params.owner}) \xB7 [Repo secrets \u2192](${githubSecretsUrl}) \xB7 [Model settings \u2192](${settingsUrl}) \xB7 [Setup docs \u2192](https://docs.pullfrog.com/keys) \xB7 [Ask in Discord \u2192](https://discord.gg/8y96raFg8e)`
|
|
198021
198132
|
].join("\n");
|
|
198022
198133
|
}
|
|
198023
198134
|
|
|
@@ -199243,6 +199354,9 @@ var DebugInput = type.enumerated("disabled", "enabled");
|
|
|
199243
199354
|
var JsonPayload = type({
|
|
199244
199355
|
"~pullfrog": "true",
|
|
199245
199356
|
version: "string",
|
|
199357
|
+
// the server's run type, forwarded verbatim to run-context. optional so a
|
|
199358
|
+
// payload from an older server build still parses against a newer action.
|
|
199359
|
+
"type?": "string | undefined",
|
|
199246
199360
|
"model?": "string | undefined",
|
|
199247
199361
|
"modelExplicit?": "boolean | undefined",
|
|
199248
199362
|
"effort?": "number | string | undefined",
|
|
@@ -199728,7 +199842,8 @@ ${ctx.error}` : ctx.error;
|
|
|
199728
199842
|
clamped: ctx.toolState.modelClamped,
|
|
199729
199843
|
unselectedProxyDefault: ctx.toolState.unselectedProxyDefault,
|
|
199730
199844
|
shaPinned: ctx.toolState.shaPinned,
|
|
199731
|
-
oss: ctx.toolState.oss
|
|
199845
|
+
oss: ctx.toolState.oss,
|
|
199846
|
+
owner: repoContext.owner
|
|
199732
199847
|
});
|
|
199733
199848
|
const body = `${formattedError}${footer}`;
|
|
199734
199849
|
const comment = ctx.toolState.progressComment;
|
|
@@ -199811,7 +199926,7 @@ async function mintProxyKey(ctx) {
|
|
|
199811
199926
|
}
|
|
199812
199927
|
}
|
|
199813
199928
|
async function buildProxyTokenHeaders(ctx) {
|
|
199814
|
-
const fundingSource = ctx.
|
|
199929
|
+
const fundingSource = ctx.fundingSource;
|
|
199815
199930
|
if (ctx.oidcCredentials) {
|
|
199816
199931
|
const creds = ctx.oidcCredentials;
|
|
199817
199932
|
const oidcToken = await op(() => fetchIdTokenFromStash(creds), {
|
|
@@ -199843,7 +199958,7 @@ async function resolveProxyModel(ctx) {
|
|
|
199843
199958
|
const key = await mintProxyKey({
|
|
199844
199959
|
oidcCredentials: ctx.oidcCredentials,
|
|
199845
199960
|
repo: ctx.repo,
|
|
199846
|
-
|
|
199961
|
+
fundingSource: ctx.oss ? "oss" : "router"
|
|
199847
199962
|
});
|
|
199848
199963
|
if (!key) return;
|
|
199849
199964
|
process.env.OPENROUTER_API_KEY = key;
|
|
@@ -199915,6 +200030,26 @@ async function runProxyResolution(ctx) {
|
|
|
199915
200030
|
throw error62;
|
|
199916
200031
|
}
|
|
199917
200032
|
}
|
|
200033
|
+
async function resolveTrialFallback(ctx) {
|
|
200034
|
+
if (process.env.PULLFROG_MODEL?.trim()) return false;
|
|
200035
|
+
if (!ctx.oidcCredentials && !isLocalApiUrl()) return false;
|
|
200036
|
+
const key = await mintProxyKey({
|
|
200037
|
+
oidcCredentials: ctx.oidcCredentials,
|
|
200038
|
+
repo: ctx.repo,
|
|
200039
|
+
fundingSource: "trial"
|
|
200040
|
+
});
|
|
200041
|
+
if (!key) return false;
|
|
200042
|
+
process.env.OPENROUTER_API_KEY = key;
|
|
200043
|
+
core9.setSecret(key);
|
|
200044
|
+
ctx.payload.proxyModel = DEFAULT_PROXY_MODEL;
|
|
200045
|
+
ctx.toolState.model = DEFAULT_PROXY_MODEL;
|
|
200046
|
+
ctx.toolState.modelClamped = {
|
|
200047
|
+
from: ctx.configuredModel ?? DEFAULT_PROXY_MODEL,
|
|
200048
|
+
reason: "trial"
|
|
200049
|
+
};
|
|
200050
|
+
log.info(`\xBB proxy: trial \u2192 ${DEFAULT_PROXY_MODEL}`);
|
|
200051
|
+
return true;
|
|
200052
|
+
}
|
|
199918
200053
|
|
|
199919
200054
|
// utils/prSummary.ts
|
|
199920
200055
|
import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile3 } from "node:fs/promises";
|
|
@@ -200092,8 +200227,9 @@ async function fetchRunContext(params) {
|
|
|
200092
200227
|
if (params.oidcToken) {
|
|
200093
200228
|
headers["X-GitHub-OIDC-Token"] = params.oidcToken;
|
|
200094
200229
|
}
|
|
200230
|
+
const query = params.runType ? `?type=${encodeURIComponent(params.runType)}` : "";
|
|
200095
200231
|
const response = await apiFetch({
|
|
200096
|
-
path: `/api/repo/${params.repoContext.owner}/${params.repoContext.name}/run-context`,
|
|
200232
|
+
path: `/api/repo/${params.repoContext.owner}/${params.repoContext.name}/run-context${query}`,
|
|
200097
200233
|
headers,
|
|
200098
200234
|
signal: controller.signal
|
|
200099
200235
|
});
|
|
@@ -200130,7 +200266,8 @@ async function fetchRunContext(params) {
|
|
|
200130
200266
|
proxyModel: data.proxyModel,
|
|
200131
200267
|
dbSecrets: data.dbSecrets,
|
|
200132
200268
|
secretsUnavailable: data.secretsUnavailable,
|
|
200133
|
-
routerUnfunded: data.routerUnfunded
|
|
200269
|
+
routerUnfunded: data.routerUnfunded,
|
|
200270
|
+
trialFallback: data.trialFallback
|
|
200134
200271
|
};
|
|
200135
200272
|
} catch {
|
|
200136
200273
|
clearTimeout(timeoutId);
|
|
@@ -200171,7 +200308,7 @@ async function resolveRunContextData(params) {
|
|
|
200171
200308
|
retries: [100, 500],
|
|
200172
200309
|
bail: (error62) => !isTransientOctokitError(error62)
|
|
200173
200310
|
})(),
|
|
200174
|
-
fetchRunContext({ token: params.token, repoContext, oidcToken })
|
|
200311
|
+
fetchRunContext({ token: params.token, repoContext, oidcToken, runType: params.runType })
|
|
200175
200312
|
]);
|
|
200176
200313
|
return {
|
|
200177
200314
|
repo: {
|
|
@@ -200189,7 +200326,8 @@ async function resolveRunContextData(params) {
|
|
|
200189
200326
|
// a failed mint on a runner that should have been able to mint is the same
|
|
200190
200327
|
// outcome as the server-side failure: the run never sees stored secrets.
|
|
200191
200328
|
secretsUnavailable: runContext.secretsUnavailable || !!process.env.ACTIONS_ID_TOKEN_REQUEST_URL && oidcToken === void 0,
|
|
200192
|
-
routerUnfunded: runContext.routerUnfunded
|
|
200329
|
+
routerUnfunded: runContext.routerUnfunded,
|
|
200330
|
+
trialFallback: runContext.trialFallback
|
|
200193
200331
|
};
|
|
200194
200332
|
}
|
|
200195
200333
|
|
|
@@ -201269,7 +201407,11 @@ async function main() {
|
|
|
201269
201407
|
resolveGit();
|
|
201270
201408
|
const jobToken = getJobToken();
|
|
201271
201409
|
const initialOctokit = createOctokit(jobToken);
|
|
201272
|
-
const runContext = await resolveRunContextData({
|
|
201410
|
+
const runContext = await resolveRunContextData({
|
|
201411
|
+
octokit: initialOctokit,
|
|
201412
|
+
token: jobToken,
|
|
201413
|
+
runType: typeof resolvedPromptInput === "string" ? void 0 : resolvedPromptInput.type
|
|
201414
|
+
});
|
|
201273
201415
|
timer.checkpoint("runContextData");
|
|
201274
201416
|
const payload = resolvePayload(resolvedPromptInput, runContext.repoSettings);
|
|
201275
201417
|
const toolState = initToolState({
|
|
@@ -201450,7 +201592,7 @@ async function main() {
|
|
|
201450
201592
|
}
|
|
201451
201593
|
const resolvedModel = credentials.kind === "fellBack" ? credentials.replacement : configuredModel;
|
|
201452
201594
|
vertexCredentials = materializeVertexCredentials({ model: resolvedModel });
|
|
201453
|
-
|
|
201595
|
+
let agent2 = resolveAgent({
|
|
201454
201596
|
model: resolvedModel,
|
|
201455
201597
|
proxyModel: payload.proxyModel,
|
|
201456
201598
|
// the account opt-in and the canary arm are both admissions to codex, so
|
|
@@ -201462,15 +201604,32 @@ async function main() {
|
|
|
201462
201604
|
const effectiveModel = payload.proxyModel ?? resolvedModel ?? payload.model;
|
|
201463
201605
|
toolState.model = effectiveModel;
|
|
201464
201606
|
if (!payload.proxyModel) {
|
|
201465
|
-
|
|
201466
|
-
|
|
201467
|
-
|
|
201468
|
-
|
|
201469
|
-
|
|
201470
|
-
|
|
201471
|
-
|
|
201472
|
-
|
|
201473
|
-
|
|
201607
|
+
try {
|
|
201608
|
+
validateAgentApiKey({
|
|
201609
|
+
agent: agent2,
|
|
201610
|
+
model: effectiveModel,
|
|
201611
|
+
authorized: getAuthorizedModels(),
|
|
201612
|
+
owner: runContext.repo.owner,
|
|
201613
|
+
name: runContext.repo.name,
|
|
201614
|
+
secretsUnavailable: runContext.secretsUnavailable,
|
|
201615
|
+
routerUnfunded: runContext.routerUnfunded
|
|
201616
|
+
});
|
|
201617
|
+
} catch (missingKey) {
|
|
201618
|
+
const funded = runContext.trialFallback && missingKey instanceof NoUsableCredentialError ? await resolveTrialFallback({
|
|
201619
|
+
payload,
|
|
201620
|
+
configuredModel: effectiveModel,
|
|
201621
|
+
oidcCredentials,
|
|
201622
|
+
repo: runContext.repo,
|
|
201623
|
+
toolState
|
|
201624
|
+
}) : false;
|
|
201625
|
+
if (!funded) throw missingKey;
|
|
201626
|
+
agent2 = resolveAgent({
|
|
201627
|
+
model: resolvedModel,
|
|
201628
|
+
proxyModel: payload.proxyModel,
|
|
201629
|
+
codexAgent: runContext.repoSettings.codexAgent || payload.codexArm === true
|
|
201630
|
+
});
|
|
201631
|
+
toolState.agent = agent2.name;
|
|
201632
|
+
}
|
|
201474
201633
|
}
|
|
201475
201634
|
await setupGit({
|
|
201476
201635
|
gitToken: tokenRef.gitToken,
|