pullfrog 0.1.17 → 0.1.19
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/cli.mjs +63 -46
- package/dist/index.js +62 -45
- package/dist/utils/runErrorRenderer.d.ts +18 -12
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -101880,7 +101880,7 @@ var import_semver = __toESM(require_semver2(), 1);
|
|
|
101880
101880
|
// package.json
|
|
101881
101881
|
var package_default = {
|
|
101882
101882
|
name: "pullfrog",
|
|
101883
|
-
version: "0.1.
|
|
101883
|
+
version: "0.1.19",
|
|
101884
101884
|
type: "module",
|
|
101885
101885
|
bin: {
|
|
101886
101886
|
pullfrog: "dist/cli.mjs",
|
|
@@ -107960,6 +107960,20 @@ function buildSecurityConfig(ctx, model) {
|
|
|
107960
107960
|
const slashIndex = model.indexOf("/");
|
|
107961
107961
|
if (slashIndex > 0) {
|
|
107962
107962
|
config3.enabled_providers = [model.slice(0, slashIndex).toLowerCase()];
|
|
107963
|
+
if (model.startsWith("openrouter/moonshotai/")) {
|
|
107964
|
+
const modelID = model.slice(slashIndex + 1);
|
|
107965
|
+
config3.provider = {
|
|
107966
|
+
...config3.provider,
|
|
107967
|
+
openrouter: {
|
|
107968
|
+
npm: "@openrouter/ai-sdk-provider@2.9.0",
|
|
107969
|
+
options: {
|
|
107970
|
+
baseURL: "https://openrouter.ai/api/v1",
|
|
107971
|
+
apiKey: "{env:OPENROUTER_API_KEY}"
|
|
107972
|
+
},
|
|
107973
|
+
models: { [modelID]: {} }
|
|
107974
|
+
}
|
|
107975
|
+
};
|
|
107976
|
+
}
|
|
107963
107977
|
}
|
|
107964
107978
|
}
|
|
107965
107979
|
return JSON.stringify(config3);
|
|
@@ -108084,7 +108098,7 @@ function newTurn() {
|
|
|
108084
108098
|
};
|
|
108085
108099
|
}
|
|
108086
108100
|
async function consumeEvents(ctx, signal) {
|
|
108087
|
-
const result = await ctx.client.event.subscribe();
|
|
108101
|
+
const result = await ctx.client.event.subscribe({}, { signal });
|
|
108088
108102
|
for await (const event of result.stream) {
|
|
108089
108103
|
if (signal.aborted) break;
|
|
108090
108104
|
ctx.eventCount += 1;
|
|
@@ -151963,6 +151977,30 @@ function classifyPushError(msg) {
|
|
|
151963
151977
|
return "unknown";
|
|
151964
151978
|
}
|
|
151965
151979
|
var TRANSIENT_RETRY_DELAYS_MS = [2e3, 5e3];
|
|
151980
|
+
async function pushWithRetry(args2, token) {
|
|
151981
|
+
let lastErr;
|
|
151982
|
+
for (let attempt = 0; attempt <= TRANSIENT_RETRY_DELAYS_MS.length; attempt++) {
|
|
151983
|
+
try {
|
|
151984
|
+
await $git("push", args2, { token });
|
|
151985
|
+
if (attempt > 0) log.info(`push succeeded on attempt ${attempt + 1}`);
|
|
151986
|
+
return;
|
|
151987
|
+
} catch (err) {
|
|
151988
|
+
lastErr = err;
|
|
151989
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
151990
|
+
if (classifyPushError(msg) === "transient" && attempt < TRANSIENT_RETRY_DELAYS_MS.length) {
|
|
151991
|
+
const baseDelay = TRANSIENT_RETRY_DELAYS_MS[attempt] ?? 5e3;
|
|
151992
|
+
const delay2 = Math.round(baseDelay * (0.75 + Math.random() * 0.5));
|
|
151993
|
+
log.info(
|
|
151994
|
+
`push attempt ${attempt + 1} failed (transient), retrying in ${delay2}ms: ${msg.slice(0, 300)}`
|
|
151995
|
+
);
|
|
151996
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
151997
|
+
continue;
|
|
151998
|
+
}
|
|
151999
|
+
throw err;
|
|
152000
|
+
}
|
|
152001
|
+
}
|
|
152002
|
+
throw lastErr instanceof Error ? lastErr : new Error(String(lastErr));
|
|
152003
|
+
}
|
|
151966
152004
|
function PushBranchTool(ctx) {
|
|
151967
152005
|
const defaultBranch = ctx.repo.data.default_branch || "main";
|
|
151968
152006
|
const pushPermission = ctx.payload.push;
|
|
@@ -152030,48 +152068,23 @@ ${postHookStatus}`
|
|
|
152030
152068
|
if (force) {
|
|
152031
152069
|
log.warning(`force pushing - this will overwrite remote history`);
|
|
152032
152070
|
}
|
|
152033
|
-
|
|
152034
|
-
|
|
152035
|
-
|
|
152036
|
-
|
|
152037
|
-
|
|
152038
|
-
|
|
152039
|
-
|
|
152040
|
-
|
|
152041
|
-
log.info(`push succeeded on attempt ${attempt + 1}`);
|
|
152042
|
-
}
|
|
152043
|
-
pushed = true;
|
|
152044
|
-
break;
|
|
152045
|
-
} catch (err) {
|
|
152046
|
-
lastErr = err;
|
|
152047
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
152048
|
-
const kind = classifyPushError(msg);
|
|
152049
|
-
if (kind === "concurrent-push") {
|
|
152050
|
-
const integrateStep = ctx.payload.shell === "disabled" ? `2. use the git tool to merge the remote branch into yours: git({ command: "merge", args: ["origin/${pushDest.remoteBranch}"] })` : `2. use the git tool to rebase or merge your changes on top: git({ command: "merge", args: ["origin/${pushDest.remoteBranch}"] }) (or 'rebase')`;
|
|
152051
|
-
throw new Error(
|
|
152052
|
-
`push rejected: the remote branch '${pushDest.remoteBranch}' has new commits you don't have locally (often a concurrent push to the same branch).
|
|
152071
|
+
try {
|
|
152072
|
+
await pushWithRetry(pushArgs, ctx.gitToken);
|
|
152073
|
+
} catch (err) {
|
|
152074
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
152075
|
+
if (classifyPushError(msg) === "concurrent-push") {
|
|
152076
|
+
const integrateStep = ctx.payload.shell === "disabled" ? `2. use the git tool to merge the remote branch into yours: git({ command: "merge", args: ["origin/${pushDest.remoteBranch}"] })` : `2. use the git tool to rebase or merge your changes on top: git({ command: "merge", args: ["origin/${pushDest.remoteBranch}"] }) (or 'rebase')`;
|
|
152077
|
+
throw new Error(
|
|
152078
|
+
`push rejected: the remote branch '${pushDest.remoteBranch}' has new commits you don't have locally (often a concurrent push to the same branch).
|
|
152053
152079
|
|
|
152054
152080
|
to resolve this:
|
|
152055
152081
|
1. use git_fetch to fetch the remote branch: git_fetch({ ref: "${pushDest.remoteBranch}" })
|
|
152056
152082
|
${integrateStep}
|
|
152057
152083
|
3. resolve any merge conflicts if needed
|
|
152058
152084
|
4. retry push_branch`
|
|
152059
|
-
|
|
152060
|
-
}
|
|
152061
|
-
if (kind === "transient" && attempt < TRANSIENT_RETRY_DELAYS_MS.length) {
|
|
152062
|
-
const baseDelay = TRANSIENT_RETRY_DELAYS_MS[attempt] ?? 5e3;
|
|
152063
|
-
const delay2 = Math.round(baseDelay * (0.75 + Math.random() * 0.5));
|
|
152064
|
-
log.info(
|
|
152065
|
-
`push attempt ${attempt + 1} failed (transient), retrying in ${delay2}ms: ${msg.slice(0, 300)}`
|
|
152066
|
-
);
|
|
152067
|
-
await new Promise((r) => setTimeout(r, delay2));
|
|
152068
|
-
continue;
|
|
152069
|
-
}
|
|
152070
|
-
throw err;
|
|
152085
|
+
);
|
|
152071
152086
|
}
|
|
152072
|
-
|
|
152073
|
-
if (!pushed) {
|
|
152074
|
-
throw lastErr instanceof Error ? lastErr : new Error(String(lastErr));
|
|
152087
|
+
throw err;
|
|
152075
152088
|
}
|
|
152076
152089
|
const pushedSha = $2("git", ["rev-parse", "HEAD"], { log: false }).trim();
|
|
152077
152090
|
log.info(
|
|
@@ -152314,9 +152327,7 @@ function DeleteBranchTool(ctx) {
|
|
|
152314
152327
|
`Blocked: cannot delete the default branch '${defaultBranch}'. If you really need to delete or rename it, do it manually via the repository settings.`
|
|
152315
152328
|
);
|
|
152316
152329
|
}
|
|
152317
|
-
await
|
|
152318
|
-
token: ctx.gitToken
|
|
152319
|
-
});
|
|
152330
|
+
await pushWithRetry(["origin", "--delete", `refs/heads/${params.branchName}`], ctx.gitToken);
|
|
152320
152331
|
log.info(`\xBB deleted branch ${params.branchName}`);
|
|
152321
152332
|
return { success: true, deleted: params.branchName };
|
|
152322
152333
|
})
|
|
@@ -152340,9 +152351,7 @@ function PushTagsTool(ctx) {
|
|
|
152340
152351
|
}
|
|
152341
152352
|
validateTagName(params.tag);
|
|
152342
152353
|
const pushArgs = [...params.force ? ["-f"] : [], "origin", `refs/tags/${params.tag}`];
|
|
152343
|
-
await
|
|
152344
|
-
token: ctx.gitToken
|
|
152345
|
-
});
|
|
152354
|
+
await pushWithRetry(pushArgs, ctx.gitToken);
|
|
152346
152355
|
log.info(`\xBB pushed tag ${params.tag}`);
|
|
152347
152356
|
return { success: true, tag: params.tag };
|
|
152348
152357
|
})
|
|
@@ -161267,6 +161276,13 @@ function formatGenericFailure(errorMessage) {
|
|
|
161267
161276
|
"```"
|
|
161268
161277
|
].join("\n");
|
|
161269
161278
|
}
|
|
161279
|
+
function formatMinimalFailureComment(repo) {
|
|
161280
|
+
const runId = process.env.GITHUB_RUN_ID;
|
|
161281
|
+
if (!runId) return "**Run failed.**";
|
|
161282
|
+
const server = process.env.GITHUB_SERVER_URL ?? "https://github.com";
|
|
161283
|
+
const url4 = `${server}/${repo.owner}/${repo.name}/actions/runs/${runId}`;
|
|
161284
|
+
return `**Run failed.** [View the logs \u2192](${url4})`;
|
|
161285
|
+
}
|
|
161270
161286
|
var PROVIDER_BILLING_URLS = {
|
|
161271
161287
|
deepseek: "https://platform.deepseek.com/top_up",
|
|
161272
161288
|
anthropic: "https://console.anthropic.com/settings/billing",
|
|
@@ -161340,11 +161356,12 @@ ${body}`, comment: body };
|
|
|
161340
161356
|
return { summary: body, comment: body };
|
|
161341
161357
|
}
|
|
161342
161358
|
if (hangBody) {
|
|
161359
|
+
const isBillingExhausted = input.agentDiagnostic?.lastProviderError === "provider billing exhausted";
|
|
161343
161360
|
return {
|
|
161344
161361
|
summary: `### \u274C Pullfrog failed
|
|
161345
161362
|
|
|
161346
161363
|
${hangBody}`,
|
|
161347
|
-
comment: hangBody
|
|
161364
|
+
comment: isBillingExhausted ? hangBody : formatMinimalFailureComment(input.repo)
|
|
161348
161365
|
};
|
|
161349
161366
|
}
|
|
161350
161367
|
const genericBody = formatGenericFailure(input.errorMessage);
|
|
@@ -161352,7 +161369,7 @@ ${hangBody}`,
|
|
|
161352
161369
|
summary: `### \u274C Pullfrog failed
|
|
161353
161370
|
|
|
161354
161371
|
${genericBody}`,
|
|
161355
|
-
comment:
|
|
161372
|
+
comment: formatMinimalFailureComment(input.repo)
|
|
161356
161373
|
};
|
|
161357
161374
|
}
|
|
161358
161375
|
|
|
@@ -163002,7 +163019,7 @@ async function run2() {
|
|
|
163002
163019
|
}
|
|
163003
163020
|
|
|
163004
163021
|
// cli.ts
|
|
163005
|
-
var VERSION10 = "0.1.
|
|
163022
|
+
var VERSION10 = "0.1.19";
|
|
163006
163023
|
var bin = basename2(process.argv[1] || "");
|
|
163007
163024
|
var PROG = bin === "pf" || bin === "pullfrog" ? bin : "pullfrog";
|
|
163008
163025
|
var rawArgs = process.argv.slice(2);
|
package/dist/index.js
CHANGED
|
@@ -100080,7 +100080,7 @@ var import_semver = __toESM(require_semver2(), 1);
|
|
|
100080
100080
|
// package.json
|
|
100081
100081
|
var package_default = {
|
|
100082
100082
|
name: "pullfrog",
|
|
100083
|
-
version: "0.1.
|
|
100083
|
+
version: "0.1.19",
|
|
100084
100084
|
type: "module",
|
|
100085
100085
|
bin: {
|
|
100086
100086
|
pullfrog: "dist/cli.mjs",
|
|
@@ -106202,6 +106202,20 @@ function buildSecurityConfig(ctx, model) {
|
|
|
106202
106202
|
const slashIndex = model.indexOf("/");
|
|
106203
106203
|
if (slashIndex > 0) {
|
|
106204
106204
|
config3.enabled_providers = [model.slice(0, slashIndex).toLowerCase()];
|
|
106205
|
+
if (model.startsWith("openrouter/moonshotai/")) {
|
|
106206
|
+
const modelID = model.slice(slashIndex + 1);
|
|
106207
|
+
config3.provider = {
|
|
106208
|
+
...config3.provider,
|
|
106209
|
+
openrouter: {
|
|
106210
|
+
npm: "@openrouter/ai-sdk-provider@2.9.0",
|
|
106211
|
+
options: {
|
|
106212
|
+
baseURL: "https://openrouter.ai/api/v1",
|
|
106213
|
+
apiKey: "{env:OPENROUTER_API_KEY}"
|
|
106214
|
+
},
|
|
106215
|
+
models: { [modelID]: {} }
|
|
106216
|
+
}
|
|
106217
|
+
};
|
|
106218
|
+
}
|
|
106205
106219
|
}
|
|
106206
106220
|
}
|
|
106207
106221
|
return JSON.stringify(config3);
|
|
@@ -106326,7 +106340,7 @@ function newTurn() {
|
|
|
106326
106340
|
};
|
|
106327
106341
|
}
|
|
106328
106342
|
async function consumeEvents(ctx, signal) {
|
|
106329
|
-
const result = await ctx.client.event.subscribe();
|
|
106343
|
+
const result = await ctx.client.event.subscribe({}, { signal });
|
|
106330
106344
|
for await (const event of result.stream) {
|
|
106331
106345
|
if (signal.aborted) break;
|
|
106332
106346
|
ctx.eventCount += 1;
|
|
@@ -150205,6 +150219,30 @@ function classifyPushError(msg) {
|
|
|
150205
150219
|
return "unknown";
|
|
150206
150220
|
}
|
|
150207
150221
|
var TRANSIENT_RETRY_DELAYS_MS = [2e3, 5e3];
|
|
150222
|
+
async function pushWithRetry(args2, token) {
|
|
150223
|
+
let lastErr;
|
|
150224
|
+
for (let attempt = 0; attempt <= TRANSIENT_RETRY_DELAYS_MS.length; attempt++) {
|
|
150225
|
+
try {
|
|
150226
|
+
await $git("push", args2, { token });
|
|
150227
|
+
if (attempt > 0) log.info(`push succeeded on attempt ${attempt + 1}`);
|
|
150228
|
+
return;
|
|
150229
|
+
} catch (err) {
|
|
150230
|
+
lastErr = err;
|
|
150231
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
150232
|
+
if (classifyPushError(msg) === "transient" && attempt < TRANSIENT_RETRY_DELAYS_MS.length) {
|
|
150233
|
+
const baseDelay = TRANSIENT_RETRY_DELAYS_MS[attempt] ?? 5e3;
|
|
150234
|
+
const delay2 = Math.round(baseDelay * (0.75 + Math.random() * 0.5));
|
|
150235
|
+
log.info(
|
|
150236
|
+
`push attempt ${attempt + 1} failed (transient), retrying in ${delay2}ms: ${msg.slice(0, 300)}`
|
|
150237
|
+
);
|
|
150238
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
150239
|
+
continue;
|
|
150240
|
+
}
|
|
150241
|
+
throw err;
|
|
150242
|
+
}
|
|
150243
|
+
}
|
|
150244
|
+
throw lastErr instanceof Error ? lastErr : new Error(String(lastErr));
|
|
150245
|
+
}
|
|
150208
150246
|
function PushBranchTool(ctx) {
|
|
150209
150247
|
const defaultBranch = ctx.repo.data.default_branch || "main";
|
|
150210
150248
|
const pushPermission = ctx.payload.push;
|
|
@@ -150272,48 +150310,23 @@ ${postHookStatus}`
|
|
|
150272
150310
|
if (force) {
|
|
150273
150311
|
log.warning(`force pushing - this will overwrite remote history`);
|
|
150274
150312
|
}
|
|
150275
|
-
|
|
150276
|
-
|
|
150277
|
-
|
|
150278
|
-
|
|
150279
|
-
|
|
150280
|
-
|
|
150281
|
-
|
|
150282
|
-
|
|
150283
|
-
log.info(`push succeeded on attempt ${attempt + 1}`);
|
|
150284
|
-
}
|
|
150285
|
-
pushed = true;
|
|
150286
|
-
break;
|
|
150287
|
-
} catch (err) {
|
|
150288
|
-
lastErr = err;
|
|
150289
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
150290
|
-
const kind = classifyPushError(msg);
|
|
150291
|
-
if (kind === "concurrent-push") {
|
|
150292
|
-
const integrateStep = ctx.payload.shell === "disabled" ? `2. use the git tool to merge the remote branch into yours: git({ command: "merge", args: ["origin/${pushDest.remoteBranch}"] })` : `2. use the git tool to rebase or merge your changes on top: git({ command: "merge", args: ["origin/${pushDest.remoteBranch}"] }) (or 'rebase')`;
|
|
150293
|
-
throw new Error(
|
|
150294
|
-
`push rejected: the remote branch '${pushDest.remoteBranch}' has new commits you don't have locally (often a concurrent push to the same branch).
|
|
150313
|
+
try {
|
|
150314
|
+
await pushWithRetry(pushArgs, ctx.gitToken);
|
|
150315
|
+
} catch (err) {
|
|
150316
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
150317
|
+
if (classifyPushError(msg) === "concurrent-push") {
|
|
150318
|
+
const integrateStep = ctx.payload.shell === "disabled" ? `2. use the git tool to merge the remote branch into yours: git({ command: "merge", args: ["origin/${pushDest.remoteBranch}"] })` : `2. use the git tool to rebase or merge your changes on top: git({ command: "merge", args: ["origin/${pushDest.remoteBranch}"] }) (or 'rebase')`;
|
|
150319
|
+
throw new Error(
|
|
150320
|
+
`push rejected: the remote branch '${pushDest.remoteBranch}' has new commits you don't have locally (often a concurrent push to the same branch).
|
|
150295
150321
|
|
|
150296
150322
|
to resolve this:
|
|
150297
150323
|
1. use git_fetch to fetch the remote branch: git_fetch({ ref: "${pushDest.remoteBranch}" })
|
|
150298
150324
|
${integrateStep}
|
|
150299
150325
|
3. resolve any merge conflicts if needed
|
|
150300
150326
|
4. retry push_branch`
|
|
150301
|
-
|
|
150302
|
-
}
|
|
150303
|
-
if (kind === "transient" && attempt < TRANSIENT_RETRY_DELAYS_MS.length) {
|
|
150304
|
-
const baseDelay = TRANSIENT_RETRY_DELAYS_MS[attempt] ?? 5e3;
|
|
150305
|
-
const delay2 = Math.round(baseDelay * (0.75 + Math.random() * 0.5));
|
|
150306
|
-
log.info(
|
|
150307
|
-
`push attempt ${attempt + 1} failed (transient), retrying in ${delay2}ms: ${msg.slice(0, 300)}`
|
|
150308
|
-
);
|
|
150309
|
-
await new Promise((r) => setTimeout(r, delay2));
|
|
150310
|
-
continue;
|
|
150311
|
-
}
|
|
150312
|
-
throw err;
|
|
150327
|
+
);
|
|
150313
150328
|
}
|
|
150314
|
-
|
|
150315
|
-
if (!pushed) {
|
|
150316
|
-
throw lastErr instanceof Error ? lastErr : new Error(String(lastErr));
|
|
150329
|
+
throw err;
|
|
150317
150330
|
}
|
|
150318
150331
|
const pushedSha = $("git", ["rev-parse", "HEAD"], { log: false }).trim();
|
|
150319
150332
|
log.info(
|
|
@@ -150556,9 +150569,7 @@ function DeleteBranchTool(ctx) {
|
|
|
150556
150569
|
`Blocked: cannot delete the default branch '${defaultBranch}'. If you really need to delete or rename it, do it manually via the repository settings.`
|
|
150557
150570
|
);
|
|
150558
150571
|
}
|
|
150559
|
-
await
|
|
150560
|
-
token: ctx.gitToken
|
|
150561
|
-
});
|
|
150572
|
+
await pushWithRetry(["origin", "--delete", `refs/heads/${params.branchName}`], ctx.gitToken);
|
|
150562
150573
|
log.info(`\xBB deleted branch ${params.branchName}`);
|
|
150563
150574
|
return { success: true, deleted: params.branchName };
|
|
150564
150575
|
})
|
|
@@ -150582,9 +150593,7 @@ function PushTagsTool(ctx) {
|
|
|
150582
150593
|
}
|
|
150583
150594
|
validateTagName(params.tag);
|
|
150584
150595
|
const pushArgs = [...params.force ? ["-f"] : [], "origin", `refs/tags/${params.tag}`];
|
|
150585
|
-
await
|
|
150586
|
-
token: ctx.gitToken
|
|
150587
|
-
});
|
|
150596
|
+
await pushWithRetry(pushArgs, ctx.gitToken);
|
|
150588
150597
|
log.info(`\xBB pushed tag ${params.tag}`);
|
|
150589
150598
|
return { success: true, tag: params.tag };
|
|
150590
150599
|
})
|
|
@@ -159509,6 +159518,13 @@ function formatGenericFailure(errorMessage) {
|
|
|
159509
159518
|
"```"
|
|
159510
159519
|
].join("\n");
|
|
159511
159520
|
}
|
|
159521
|
+
function formatMinimalFailureComment(repo) {
|
|
159522
|
+
const runId = process.env.GITHUB_RUN_ID;
|
|
159523
|
+
if (!runId) return "**Run failed.**";
|
|
159524
|
+
const server = process.env.GITHUB_SERVER_URL ?? "https://github.com";
|
|
159525
|
+
const url4 = `${server}/${repo.owner}/${repo.name}/actions/runs/${runId}`;
|
|
159526
|
+
return `**Run failed.** [View the logs \u2192](${url4})`;
|
|
159527
|
+
}
|
|
159512
159528
|
var PROVIDER_BILLING_URLS = {
|
|
159513
159529
|
deepseek: "https://platform.deepseek.com/top_up",
|
|
159514
159530
|
anthropic: "https://console.anthropic.com/settings/billing",
|
|
@@ -159582,11 +159598,12 @@ ${body}`, comment: body };
|
|
|
159582
159598
|
return { summary: body, comment: body };
|
|
159583
159599
|
}
|
|
159584
159600
|
if (hangBody) {
|
|
159601
|
+
const isBillingExhausted = input.agentDiagnostic?.lastProviderError === "provider billing exhausted";
|
|
159585
159602
|
return {
|
|
159586
159603
|
summary: `### \u274C Pullfrog failed
|
|
159587
159604
|
|
|
159588
159605
|
${hangBody}`,
|
|
159589
|
-
comment: hangBody
|
|
159606
|
+
comment: isBillingExhausted ? hangBody : formatMinimalFailureComment(input.repo)
|
|
159590
159607
|
};
|
|
159591
159608
|
}
|
|
159592
159609
|
const genericBody = formatGenericFailure(input.errorMessage);
|
|
@@ -159594,7 +159611,7 @@ ${hangBody}`,
|
|
|
159594
159611
|
summary: `### \u274C Pullfrog failed
|
|
159595
159612
|
|
|
159596
159613
|
${genericBody}`,
|
|
159597
|
-
comment:
|
|
159614
|
+
comment: formatMinimalFailureComment(input.repo)
|
|
159598
159615
|
};
|
|
159599
159616
|
}
|
|
159600
159617
|
|
|
@@ -30,18 +30,24 @@
|
|
|
30
30
|
* 5. Activity-timeout hang — `errorMessage` starts with
|
|
31
31
|
* `"activity timeout"` or `"agent still pending"` AND none of the
|
|
32
32
|
* above matched. The harness keeps structured diagnostic state on
|
|
33
|
-
* `toolState.agentDiagnostic`; `formatAgentHangBody` renders that
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* in
|
|
43
|
-
*
|
|
44
|
-
*
|
|
33
|
+
* `toolState.agentDiagnostic`; `formatAgentHangBody` renders that into
|
|
34
|
+
* the job summary. The PR comment instead collapses to a one-line
|
|
35
|
+
* `**Run failed.** [View the logs →]` — the watchdog jargon, event
|
|
36
|
+
* counts, and benign stderr tail are operator-grade detail that only
|
|
37
|
+
* alarm the average user. The one exception is a hang masking billing
|
|
38
|
+
* exhaustion (#778), where `formatAgentHangBody` emits an actionable
|
|
39
|
+
* top-up CTA that the comment keeps verbatim.
|
|
40
|
+
*
|
|
41
|
+
* 6. Default — the job summary gets a plain-English lead sentence plus the
|
|
42
|
+
* raw error in a fenced code block under the `### ❌ Pullfrog failed`
|
|
43
|
+
* banner; the PR comment collapses to the same one-line logs link as
|
|
44
|
+
* the hang case, since the raw internal string helps nobody on the PR.
|
|
45
|
+
*
|
|
46
|
+
* Net: the actionable classifications (billing, API-key, model-not-found)
|
|
47
|
+
* render identical bodies on both surfaces; the non-actionable ones (hang,
|
|
48
|
+
* generic) keep the forensics in the Actions job summary and show a calm
|
|
49
|
+
* one-liner in the PR comment, whose footer already carries Pullfrog
|
|
50
|
+
* branding + rerun links.
|
|
45
51
|
*/
|
|
46
52
|
import type { AgentDiagnostic } from "./agentHangReport.ts";
|
|
47
53
|
export type RenderedRunError = {
|