pullfrog 0.1.49 → 0.1.50

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/index.js CHANGED
@@ -100541,6 +100541,9 @@ function formatMcpToolRef(agentId, toolName) {
100541
100541
  return agentId;
100542
100542
  }
100543
100543
  }
100544
+ function hasSimilarIssues(params) {
100545
+ return params.repoIntelligence && !!params.event.issue_number && !params.event.is_pr;
100546
+ }
100544
100547
 
100545
100548
  // utils/activity.ts
100546
100549
  import { performance as performance2 } from "node:perf_hooks";
@@ -100549,6 +100552,7 @@ function isMonitorDebugEnabled() {
100549
100552
  }
100550
100553
  var DEFAULT_ACTIVITY_TIMEOUT_MS = 3e5;
100551
100554
  var AGENT_ACTIVITY_TIMEOUT_MS = 9e5;
100555
+ var AGENT_FIRST_EVENT_TIMEOUT_MS = 12e4;
100552
100556
  var DEFAULT_ACTIVITY_CHECK_INTERVAL_MS = 5e3;
100553
100557
  var DEBUG_TS_PREFIX = /^(?:\[\d{4}-\d{2}-\d{2}T[^\]]+\]\s+)?/.source;
100554
100558
  var ACTIVITY_NOISE_PATTERNS = [
@@ -101242,7 +101246,7 @@ var import_semver = __toESM(require_semver2(), 1);
101242
101246
  // package.json
101243
101247
  var package_default = {
101244
101248
  name: "pullfrog",
101245
- version: "0.1.49",
101249
+ version: "0.1.50",
101246
101250
  type: "module",
101247
101251
  bin: {
101248
101252
  pullfrog: "dist/cli.mjs",
@@ -109155,7 +109159,7 @@ async function installOpencodeCli(params) {
109155
109159
  installDependencies: true
109156
109160
  });
109157
109161
  }
109158
- var AUTO_SELECT_WARNING = "select a model explicitly in the Pullfrog console (https://pullfrog.com/console) to avoid this.";
109162
+ var AUTO_SELECT_WARNING = "select a model explicitly in the Pullfrog console (https://pullfrog.com/console) to avoid this \u2014 if the picker is locked, this run had no Pullfrog Router funding: add a card or top up your credit.";
109159
109163
  function autoSelectModel() {
109160
109164
  const authorized2 = getAuthorizedModels();
109161
109165
  if (authorized2.size > 0) {
@@ -109367,9 +109371,18 @@ async function consumeEvents(ctx, signal) {
109367
109371
  }
109368
109372
  }
109369
109373
  }
109374
+ function isModelOutput(ctx, part) {
109375
+ if (part.type !== "text") return true;
109376
+ if (ctx.promptMessageID === void 0) {
109377
+ ctx.promptMessageID = part.messageID;
109378
+ return false;
109379
+ }
109380
+ return part.messageID !== ctx.promptMessageID;
109381
+ }
109370
109382
  async function dispatchEvent(ctx, event) {
109371
109383
  if (event.type === "message.part.updated") {
109372
109384
  ctx.lastEventAt = performance6.now();
109385
+ if (isModelOutput(ctx, event.properties.part)) ctx.sawModelOutput = true;
109373
109386
  await onPartUpdated(ctx, event.properties.part);
109374
109387
  return;
109375
109388
  }
@@ -109513,6 +109526,8 @@ async function runPromptTurn(ctx, params) {
109513
109526
  ctx.currentTurn = newTurn();
109514
109527
  const turn = ctx.currentTurn;
109515
109528
  const part = { type: "text", text: params.text };
109529
+ ctx.promptMessageID = void 0;
109530
+ ctx.sawModelOutput = false;
109516
109531
  let assistant;
109517
109532
  let returnedParts;
109518
109533
  let networkError = null;
@@ -109686,11 +109701,14 @@ function startInnerActivityWatchdog(params) {
109686
109701
  const id = setInterval(() => {
109687
109702
  if (fired) return;
109688
109703
  const idleMs = performance6.now() - params.ctx.lastEventAt;
109689
- if (idleMs <= params.timeoutMs) return;
109704
+ const budgetMs = params.ctx.sawModelOutput ? params.timeoutMs : AGENT_FIRST_EVENT_TIMEOUT_MS;
109705
+ if (idleMs <= budgetMs) return;
109690
109706
  fired = true;
109691
109707
  const idleSec = Math.round(idleMs / 1e3);
109708
+ params.ctx.diagnostic.idleSec = idleSec;
109709
+ params.ctx.diagnostic.sawModelOutput = params.ctx.sawModelOutput;
109692
109710
  log.info(
109693
- `\xBB no opencode events for ${idleSec}s \u2014 aborting in-flight prompt and notifying harness`
109711
+ params.ctx.sawModelOutput ? `\xBB no opencode events for ${idleSec}s \u2014 aborting in-flight prompt and notifying harness` : `\xBB no opencode events for ${idleSec}s \u2014 the provider never returned a first token; aborting in-flight prompt and notifying harness`
109694
109712
  );
109695
109713
  params.abortController.abort();
109696
109714
  try {
@@ -109815,6 +109833,8 @@ var opencode = agent({
109815
109833
  currentTurn: null,
109816
109834
  eventCount: 0,
109817
109835
  lastEventAt: performance6.now(),
109836
+ sawModelOutput: false,
109837
+ promptMessageID: void 0,
109818
109838
  taskDispatchByCallID: /* @__PURE__ */ new Map(),
109819
109839
  loggedToolCallIDs: /* @__PURE__ */ new Set(),
109820
109840
  recentStderr: server.recentStderr,
@@ -109822,6 +109842,8 @@ var opencode = agent({
109822
109842
  label: "Pullfrog",
109823
109843
  recentStderr: server.recentStderr,
109824
109844
  lastProviderError: void 0,
109845
+ idleSec: void 0,
109846
+ sawModelOutput: false,
109825
109847
  eventCount: 0
109826
109848
  }
109827
109849
  };
@@ -158055,6 +158077,12 @@ function fixDoubleEscapedString(str) {
158055
158077
  return str;
158056
158078
  }
158057
158079
 
158080
+ // utils/isPullfrog.ts
158081
+ function isPullfrog(actor) {
158082
+ actor = actor?.toLowerCase().replace("[bot]", "");
158083
+ return !!actor && (actor === "pullfrog" || actor === "pullfrogdev");
158084
+ }
158085
+
158058
158086
  // utils/isTransientNetworkError.ts
158059
158087
  function isTransientNetworkError(error49, extraPatterns = []) {
158060
158088
  if (!(error49 instanceof Error)) return false;
@@ -158161,216 +158189,6 @@ function aggregateUsage(entries) {
158161
158189
  return out;
158162
158190
  }
158163
158191
 
158164
- // utils/payload.ts
158165
- var core4 = __toESM(require_core(), 1);
158166
- import { readFileSync as readFileSync3 } from "node:fs";
158167
- import { isAbsolute as isAbsolute2, resolve as resolve2 } from "node:path";
158168
-
158169
- // utils/versioning.ts
158170
- var import_semver2 = __toESM(require_semver2(), 1);
158171
- var COMPATIBILITY_POLICY = "non-breaking";
158172
- function validateCompatibility(payloadVersion, actionVersion) {
158173
- const payloadSemVer = import_semver2.default.parse(payloadVersion);
158174
- if (!payloadSemVer)
158175
- throw new Error(`Payload version ${payloadVersion} is not a valid semantic version.`);
158176
- const major = payloadSemVer.major;
158177
- const minor = payloadSemVer.minor;
158178
- const patch = payloadSemVer.patch;
158179
- const compatibilityRange = COMPATIBILITY_POLICY === "same-features" ? `^${major}.${minor}.${major === 0 ? patch : 0}` : `^${major}.${major === 0 ? minor : 0}.${major === 0 ? "x" : 0}`;
158180
- if (!import_semver2.default.satisfies(actionVersion, compatibilityRange)) {
158181
- throw new Error(
158182
- `Payload version ${payloadVersion} is incompatible with action version ${actionVersion}. Please update your workflow to use at least ${import_semver2.default.minVersion(compatibilityRange)} version of the action.`
158183
- );
158184
- }
158185
- }
158186
-
158187
- // utils/payload.ts
158188
- var ShellPermissionInput = type.enumerated("disabled", "restricted", "enabled");
158189
- var PushPermissionInput = type.enumerated("disabled", "restricted", "enabled");
158190
- var StatusChecksInput = type.enumerated("disabled", "enabled");
158191
- var ProgressCommentsInput = type.enumerated("disabled", "enabled");
158192
- var JsonPayload = type({
158193
- "~pullfrog": "true",
158194
- version: "string",
158195
- "model?": "string | undefined",
158196
- "modelExplicit?": "boolean | undefined",
158197
- "effort?": "number | string | undefined",
158198
- prompt: "string",
158199
- "triggerer?": "string | undefined",
158200
- "baseInstructions?": "string | undefined",
158201
- "eventInstructions?": "string",
158202
- "previousRunsNote?": "string",
158203
- "event?": "object",
158204
- "xrepo?": type({
158205
- mode: "'all' | 'explicit'",
158206
- read: "string[]",
158207
- write: "string[]",
158208
- // optional so a payload from an older server build (pre-`unavailable`)
158209
- // still parses against a newer action across a rolling deploy.
158210
- "unavailable?": "string[]"
158211
- }).or("undefined"),
158212
- "timeout?": "string | undefined",
158213
- "progressComment?": type({
158214
- id: "string",
158215
- type: "'issue' | 'review'"
158216
- }).or("undefined"),
158217
- // optional so a payload from an older server build (pre-`checkRun`) still parses
158218
- // against a newer action across a rolling deploy.
158219
- "checkRun?": type({ id: "string" }).or("undefined"),
158220
- "generateSummary?": "boolean | undefined"
158221
- });
158222
- var COLLABORATOR_PERMISSIONS = ["admin", "maintain", "write"];
158223
- function isCollaborator(event) {
158224
- const perm = event.authorPermission;
158225
- return perm !== void 0 && COLLABORATOR_PERMISSIONS.includes(perm);
158226
- }
158227
- var Inputs = type({
158228
- "prompt?": type.string.or("undefined"),
158229
- "prompt_file?": type.string.or("undefined"),
158230
- "model?": type.string.or("undefined"),
158231
- "effort?": type.string.or("undefined"),
158232
- "timeout?": type.string.or("undefined"),
158233
- "push?": PushPermissionInput.or("undefined"),
158234
- "shell?": ShellPermissionInput.or("undefined"),
158235
- "status_checks?": StatusChecksInput.or("undefined"),
158236
- "progress_comments?": ProgressCommentsInput.or("undefined"),
158237
- "cwd?": type.string.or("undefined"),
158238
- "output_schema?": type.string.or("undefined")
158239
- });
158240
- function isPayloadEvent(value2) {
158241
- return typeof value2 === "object" && value2 !== null && "trigger" in value2;
158242
- }
158243
- function resolveCwd(cwd) {
158244
- const workspace = process.env.GITHUB_WORKSPACE;
158245
- if (!cwd) return workspace;
158246
- if (isAbsolute2(cwd)) return cwd;
158247
- return workspace ? resolve2(workspace, cwd) : cwd;
158248
- }
158249
- function resolvePromptInput() {
158250
- const promptInput = core4.getInput("prompt");
158251
- const promptFile = core4.getInput("prompt_file");
158252
- if (promptInput && promptFile) {
158253
- throw new Error("set exactly one of 'prompt' or 'prompt_file' inputs, not both.");
158254
- }
158255
- if (promptFile) {
158256
- return resolvePromptFile(promptFile);
158257
- }
158258
- if (!promptInput) {
158259
- throw new Error("one of 'prompt' or 'prompt_file' inputs is required.");
158260
- }
158261
- let parsed2;
158262
- try {
158263
- parsed2 = JSON.parse(promptInput);
158264
- } catch {
158265
- return promptInput;
158266
- }
158267
- if (!parsed2 || typeof parsed2 !== "object" || !("~pullfrog" in parsed2)) {
158268
- return promptInput;
158269
- }
158270
- const jsonPayload = JsonPayload.assert(parsed2);
158271
- validateCompatibility(jsonPayload.version, package_default.version);
158272
- return jsonPayload;
158273
- }
158274
- function resolvePromptFile(input) {
158275
- const workspace = process.env.GITHUB_WORKSPACE;
158276
- const path4 = isAbsolute2(input) ? input : workspace ? resolve2(workspace, input) : resolve2(input);
158277
- const content = readFileSync3(path4, "utf-8");
158278
- if (!content.trim()) {
158279
- throw new Error(`prompt_file ${JSON.stringify(input)} is empty.`);
158280
- }
158281
- return content;
158282
- }
158283
- function resolveNonPromptInputs() {
158284
- return Inputs.omit("prompt", "prompt_file").assert({
158285
- model: core4.getInput("model") || void 0,
158286
- effort: core4.getInput("effort") || void 0,
158287
- timeout: core4.getInput("timeout") || void 0,
158288
- cwd: core4.getInput("cwd") || void 0,
158289
- push: core4.getInput("push") || void 0,
158290
- shell: core4.getInput("shell") || void 0,
158291
- status_checks: core4.getInput("status_checks") || void 0,
158292
- progress_comments: core4.getInput("progress_comments") || void 0
158293
- });
158294
- }
158295
- var isPullfrog = (actor) => {
158296
- actor = actor?.replace("[bot]", "");
158297
- return !!actor && (actor === "pullfrog" || actor === "pullfrogdev");
158298
- };
158299
- function resolvePayload(resolvedPromptInput, repoSettings) {
158300
- const [prompt, jsonPayload] = typeof resolvedPromptInput !== "string" ? [resolvedPromptInput.prompt, resolvedPromptInput] : [resolvedPromptInput, void 0];
158301
- const inputs = resolveNonPromptInputs();
158302
- const rawEvent = jsonPayload?.event;
158303
- const event = isPayloadEvent(rawEvent) ? rawEvent : { trigger: "unknown" };
158304
- const model = jsonPayload?.model ?? inputs.model ?? repoSettings.model ?? void 0;
158305
- const rawEffort = jsonPayload?.effort ?? inputs.effort ?? repoSettings.effort ?? void 0;
158306
- const effort = rawEffort === void 0 ? void 0 : parseEffortPosition(String(rawEffort));
158307
- const isNonCollaborator = !isCollaborator(event);
158308
- const repoShell = repoSettings.shell ?? "restricted";
158309
- const inputShell = inputs.shell;
158310
- let resolvedShell = repoShell;
158311
- if (inputShell === "disabled") {
158312
- resolvedShell = "disabled";
158313
- } else if (inputShell === "restricted" && resolvedShell === "enabled") {
158314
- resolvedShell = "restricted";
158315
- }
158316
- if (isNonCollaborator && resolvedShell === "enabled") {
158317
- resolvedShell = "restricted";
158318
- }
158319
- return {
158320
- "~pullfrog": true,
158321
- version: jsonPayload?.version ?? package_default.version,
158322
- model,
158323
- // explicit only when the model came from a per-run override flag (carried on
158324
- // the JSON payload). a GHA `model` input or the repo default is not explicit.
158325
- modelExplicit: jsonPayload?.modelExplicit ?? false,
158326
- effort,
158327
- prompt,
158328
- triggerer: jsonPayload?.triggerer ?? // it's not a common use case but GITHUB_ACTOR can be a user when the workflow is manually triggered by a user through GitHub Actions UI
158329
- (!isPullfrog(process.env.GITHUB_ACTOR) ? process.env.GITHUB_ACTOR : void 0),
158330
- baseInstructions: jsonPayload?.baseInstructions,
158331
- eventInstructions: jsonPayload?.eventInstructions,
158332
- previousRunsNote: jsonPayload?.previousRunsNote,
158333
- event,
158334
- xrepo: jsonPayload?.xrepo,
158335
- timeout: inputs.timeout ?? jsonPayload?.timeout,
158336
- cwd: resolveCwd(inputs.cwd),
158337
- progressComment: jsonPayload?.progressComment,
158338
- checkRun: jsonPayload?.checkRun,
158339
- generateSummary: jsonPayload?.generateSummary,
158340
- // permissions: inputs > repoSettings > fallbacks
158341
- push: inputs.push ?? repoSettings.push ?? "restricted",
158342
- shell: resolvedShell,
158343
- // the `pullfrog` run-lifecycle check. ON by default — the whole point is that a PR
158344
- // shows whether Pullfrog is running without anyone having to opt in. the workflow
158345
- // input is the source of truth when set (mirrors `push`); otherwise the repo
158346
- // setting decides.
158347
- runStatusCheck: inputs.status_checks === void 0 ? repoSettings.statusChecks : inputs.status_checks === "enabled",
158348
- // the `pullfrog-approval` verdict check stays opt-in and workflow-only. it exists to
158349
- // be *required* by branch protection, so it must never turn itself on.
158350
- approvalCheck: inputs.status_checks === "enabled",
158351
- // temporary progress chrome. the workflow input is the source of truth when
158352
- // set (mirrors `push`); otherwise the repo setting decides. defaults to true.
158353
- progressComments: inputs.progress_comments === void 0 ? repoSettings.progressComments : inputs.progress_comments === "enabled",
158354
- // set by proxy logic in main.ts when routing through OpenRouter
158355
- proxyModel: void 0
158356
- };
158357
- }
158358
- function resolveOutputSchema() {
158359
- const raw2 = core4.getInput("output_schema");
158360
- if (!raw2) return void 0;
158361
- let parsed2;
158362
- try {
158363
- parsed2 = JSON.parse(raw2);
158364
- } catch {
158365
- throw new Error(`invalid output_schema: not valid JSON`);
158366
- }
158367
- if (!parsed2 || typeof parsed2 !== "object" || Array.isArray(parsed2)) {
158368
- throw new Error(`invalid output_schema: must be a JSON object`);
158369
- }
158370
- log.info("\xBB structured output schema provided \u2014 output will be required");
158371
- return parsed2;
158372
- }
158373
-
158374
158192
  // mcp/comment.ts
158375
158193
  function isNotFoundError(error49) {
158376
158194
  return error49 instanceof Error && error49.message.includes("Not Found");
@@ -160681,7 +160499,7 @@ function isMetadataYarnClassic(metadataPath) {
160681
160499
  }
160682
160500
 
160683
160501
  // utils/packageManager.ts
160684
- var import_semver3 = __toESM(require_semver2(), 1);
160502
+ var import_semver2 = __toESM(require_semver2(), 1);
160685
160503
  import { existsSync as existsSync5 } from "node:fs";
160686
160504
  import { mkdir, readFile as readFile2 } from "node:fs/promises";
160687
160505
  import { delimiter, join as join16 } from "node:path";
@@ -160703,7 +160521,7 @@ function parsePackageManagerField(value2) {
160703
160521
  return {
160704
160522
  name,
160705
160523
  version: version3,
160706
- concrete: import_semver3.default.valid(version3) !== null,
160524
+ concrete: import_semver2.default.valid(version3) !== null,
160707
160525
  source: "packageManager"
160708
160526
  };
160709
160527
  }
@@ -160717,7 +160535,7 @@ function parseDevEnginesField(field) {
160717
160535
  return {
160718
160536
  name: field.name,
160719
160537
  version: version3,
160720
- concrete: import_semver3.default.valid(version3) !== null,
160538
+ concrete: import_semver2.default.valid(version3) !== null,
160721
160539
  source: "devEngines"
160722
160540
  };
160723
160541
  }
@@ -160751,7 +160569,7 @@ async function resolvePackageManagerSpec(cwd) {
160751
160569
  }
160752
160570
  return devSpec;
160753
160571
  }
160754
- if (pmSpec.concrete && import_semver3.default.satisfies(pmSpec.version, devSpec.version)) {
160572
+ if (pmSpec.concrete && import_semver2.default.satisfies(pmSpec.version, devSpec.version)) {
160755
160573
  return pmSpec;
160756
160574
  }
160757
160575
  if (pmSpec.concrete) {
@@ -160963,10 +160781,10 @@ ${errorMessage}`]
160963
160781
  };
160964
160782
 
160965
160783
  // prep/installPythonDependencies.ts
160966
- import { existsSync as existsSync7, readFileSync as readFileSync4 } from "node:fs";
160784
+ import { existsSync as existsSync7, readFileSync as readFileSync3 } from "node:fs";
160967
160785
  import { join as join18 } from "node:path";
160968
160786
  function declaresBuildSystem(path4) {
160969
- return /^\s*\[\s*build-system\s*\]/m.test(readFileSync4(path4, "utf8"));
160787
+ return /^\s*\[\s*build-system\s*\]/m.test(readFileSync3(path4, "utf8"));
160970
160788
  }
160971
160789
  function configApplies(config3, cwd) {
160972
160790
  const path4 = join18(cwd, config3.file);
@@ -163101,6 +162919,36 @@ function KillBackgroundTool(ctx) {
163101
162919
  });
163102
162920
  }
163103
162921
 
162922
+ // mcp/similarIssues.ts
162923
+ var SimilarIssues = type({
162924
+ issue_number: type.number.describe("The issue number to find older duplicate candidates for")
162925
+ });
162926
+ function SimilarIssuesTool(ctx) {
162927
+ return tool({
162928
+ name: "find_similar_issues",
162929
+ description: "Find high-recall older duplicate candidates for an issue. Similarity is only retrieval: inspect promising candidates with get_issue before deciding whether they are duplicates.",
162930
+ mutates: true,
162931
+ parameters: SimilarIssues,
162932
+ execute: execute(async (input) => {
162933
+ if (ctx.payload.event.is_pr || ctx.payload.event.issue_number !== input.issue_number) {
162934
+ throw new Error("find_similar_issues is limited to the issue that triggered this run");
162935
+ }
162936
+ const response = await apiFetch({
162937
+ path: `/api/repo/${ctx.repo.owner}/${ctx.repo.name}/issues/${input.issue_number}/similar`,
162938
+ headers: {
162939
+ authorization: `Bearer ${ctx.apiToken}`
162940
+ },
162941
+ signal: AbortSignal.timeout(13 * 6e4)
162942
+ });
162943
+ if (!response.ok) {
162944
+ const message = await response.text();
162945
+ throw new Error(`similar issue lookup returned ${response.status}: ${message}`);
162946
+ }
162947
+ return { result: await response.json() };
162948
+ })
162949
+ });
162950
+ }
162951
+
163104
162952
  // mcp/upload.ts
163105
162953
  import * as fs4 from "node:fs";
163106
162954
  import * as path3 from "node:path";
@@ -163339,6 +163187,9 @@ function buildCommonTools(ctx, outputSchema) {
163339
163187
  if (ctx.xrepo) {
163340
163188
  tools.push(ListReposTool(ctx), CheckoutRepoTool(ctx));
163341
163189
  }
163190
+ if (hasSimilarIssues({ repoIntelligence: ctx.repoIntelligence, event: ctx.payload.event })) {
163191
+ tools.push(SimilarIssuesTool(ctx));
163192
+ }
163342
163193
  const isStandalone = ctx.payload.event.trigger === "unknown";
163343
163194
  if (isStandalone || outputSchema) {
163344
163195
  tools.push(SetOutputTool(ctx, outputSchema));
@@ -164282,6 +164133,11 @@ Rules:
164282
164133
  ### GitHub
164283
164134
 
164284
164135
  Use MCP tools from ${pullfrogMcpName} for all GitHub operations. Never use the \`gh\` CLI \u2014 it is not authenticated and will fail. The MCP tools handle authentication and enforce permissions.
164136
+ ${hasSimilarIssues({ repoIntelligence: ctx.repoIntelligence, event: ctx.payload.event }) ? `
164137
+ #### Duplicate detection (enabled for this repository)
164138
+
164139
+ Call \`${t("find_similar_issues")}\` for #${ctx.payload.event.issue_number} before planning. If it duplicates an existing issue, link that instead of producing a plan; never close or label on similarity alone.
164140
+ ` : ""}
164285
164141
 
164286
164142
  ${getShellInstructions(ctx.payload.shell, t)}
164287
164143
 
@@ -164608,7 +164464,7 @@ function buildModelAccessError(input) {
164608
164464
  }
164609
164465
 
164610
164466
  // utils/normalizeEnv.ts
164611
- var core5 = __toESM(require_core(), 1);
164467
+ var core4 = __toESM(require_core(), 1);
164612
164468
  function sanitizeSecret(key, value2) {
164613
164469
  const trimmed = value2.trim();
164614
164470
  if (trimmed.length === 0) {
@@ -164622,7 +164478,7 @@ function sanitizeSecret(key, value2) {
164622
164478
  `\xBB stripped whitespace from ${key} (whitespace in secret values breaks GitHub Actions log masking)`
164623
164479
  );
164624
164480
  }
164625
- core5.setSecret(trimmed);
164481
+ core4.setSecret(trimmed);
164626
164482
  return trimmed;
164627
164483
  }
164628
164484
  function normalizeEnv() {
@@ -164666,7 +164522,7 @@ function normalizeEnv() {
164666
164522
  }
164667
164523
 
164668
164524
  // utils/overrides.ts
164669
- var core6 = __toESM(require_core(), 1);
164525
+ var core5 = __toESM(require_core(), 1);
164670
164526
  var DENIED_OVERRIDE_NAMES = /* @__PURE__ */ new Set([
164671
164527
  "GITHUB_TOKEN",
164672
164528
  "GH_TOKEN",
@@ -164712,7 +164568,7 @@ function applyOverrides(params) {
164712
164568
  denied.push(key);
164713
164569
  continue;
164714
164570
  }
164715
- if (value2.length > 0) core6.setSecret(value2);
164571
+ if (value2.length > 0) core5.setSecret(value2);
164716
164572
  params.env[key] = value2;
164717
164573
  applied.push(key);
164718
164574
  }
@@ -164720,6 +164576,212 @@ function applyOverrides(params) {
164720
164576
  return { applied, denied };
164721
164577
  }
164722
164578
 
164579
+ // utils/payload.ts
164580
+ var core6 = __toESM(require_core(), 1);
164581
+ import { readFileSync as readFileSync5 } from "node:fs";
164582
+ import { isAbsolute as isAbsolute2, resolve as resolve2 } from "node:path";
164583
+
164584
+ // utils/versioning.ts
164585
+ var import_semver3 = __toESM(require_semver2(), 1);
164586
+ var COMPATIBILITY_POLICY = "non-breaking";
164587
+ function validateCompatibility(payloadVersion, actionVersion) {
164588
+ const payloadSemVer = import_semver3.default.parse(payloadVersion);
164589
+ if (!payloadSemVer)
164590
+ throw new Error(`Payload version ${payloadVersion} is not a valid semantic version.`);
164591
+ const major = payloadSemVer.major;
164592
+ const minor = payloadSemVer.minor;
164593
+ const patch = payloadSemVer.patch;
164594
+ const compatibilityRange = COMPATIBILITY_POLICY === "same-features" ? `^${major}.${minor}.${major === 0 ? patch : 0}` : `^${major}.${major === 0 ? minor : 0}.${major === 0 ? "x" : 0}`;
164595
+ if (!import_semver3.default.satisfies(actionVersion, compatibilityRange)) {
164596
+ throw new Error(
164597
+ `Payload version ${payloadVersion} is incompatible with action version ${actionVersion}. Please update your workflow to use at least ${import_semver3.default.minVersion(compatibilityRange)} version of the action.`
164598
+ );
164599
+ }
164600
+ }
164601
+
164602
+ // utils/payload.ts
164603
+ var ShellPermissionInput = type.enumerated("disabled", "restricted", "enabled");
164604
+ var PushPermissionInput = type.enumerated("disabled", "restricted", "enabled");
164605
+ var StatusChecksInput = type.enumerated("disabled", "enabled");
164606
+ var ProgressCommentsInput = type.enumerated("disabled", "enabled");
164607
+ var JsonPayload = type({
164608
+ "~pullfrog": "true",
164609
+ version: "string",
164610
+ "model?": "string | undefined",
164611
+ "modelExplicit?": "boolean | undefined",
164612
+ "effort?": "number | string | undefined",
164613
+ prompt: "string",
164614
+ "triggerer?": "string | undefined",
164615
+ "baseInstructions?": "string | undefined",
164616
+ "eventInstructions?": "string",
164617
+ "previousRunsNote?": "string",
164618
+ "event?": "object",
164619
+ "xrepo?": type({
164620
+ mode: "'all' | 'explicit'",
164621
+ read: "string[]",
164622
+ write: "string[]",
164623
+ // optional so a payload from an older server build (pre-`unavailable`)
164624
+ // still parses against a newer action across a rolling deploy.
164625
+ "unavailable?": "string[]"
164626
+ }).or("undefined"),
164627
+ "timeout?": "string | undefined",
164628
+ "progressComment?": type({
164629
+ id: "string",
164630
+ type: "'issue' | 'review'"
164631
+ }).or("undefined"),
164632
+ // optional so a payload from an older server build (pre-`checkRun`) still parses
164633
+ // against a newer action across a rolling deploy.
164634
+ "checkRun?": type({ id: "string" }).or("undefined"),
164635
+ "generateSummary?": "boolean | undefined"
164636
+ });
164637
+ var COLLABORATOR_PERMISSIONS = ["admin", "maintain", "write"];
164638
+ function isCollaborator(event) {
164639
+ const perm = event.authorPermission;
164640
+ return perm !== void 0 && COLLABORATOR_PERMISSIONS.includes(perm);
164641
+ }
164642
+ var Inputs = type({
164643
+ "prompt?": type.string.or("undefined"),
164644
+ "prompt_file?": type.string.or("undefined"),
164645
+ "model?": type.string.or("undefined"),
164646
+ "effort?": type.string.or("undefined"),
164647
+ "timeout?": type.string.or("undefined"),
164648
+ "push?": PushPermissionInput.or("undefined"),
164649
+ "shell?": ShellPermissionInput.or("undefined"),
164650
+ "status_checks?": StatusChecksInput.or("undefined"),
164651
+ "progress_comments?": ProgressCommentsInput.or("undefined"),
164652
+ "cwd?": type.string.or("undefined"),
164653
+ "output_schema?": type.string.or("undefined")
164654
+ });
164655
+ function isPayloadEvent(value2) {
164656
+ return typeof value2 === "object" && value2 !== null && "trigger" in value2;
164657
+ }
164658
+ function resolveCwd(cwd) {
164659
+ const workspace = process.env.GITHUB_WORKSPACE;
164660
+ if (!cwd) return workspace;
164661
+ if (isAbsolute2(cwd)) return cwd;
164662
+ return workspace ? resolve2(workspace, cwd) : cwd;
164663
+ }
164664
+ function resolvePromptInput() {
164665
+ const promptInput = core6.getInput("prompt");
164666
+ const promptFile = core6.getInput("prompt_file");
164667
+ if (promptInput && promptFile) {
164668
+ throw new Error("set exactly one of 'prompt' or 'prompt_file' inputs, not both.");
164669
+ }
164670
+ if (promptFile) {
164671
+ return resolvePromptFile(promptFile);
164672
+ }
164673
+ if (!promptInput) {
164674
+ throw new Error("one of 'prompt' or 'prompt_file' inputs is required.");
164675
+ }
164676
+ let parsed2;
164677
+ try {
164678
+ parsed2 = JSON.parse(promptInput);
164679
+ } catch {
164680
+ return promptInput;
164681
+ }
164682
+ if (!parsed2 || typeof parsed2 !== "object" || !("~pullfrog" in parsed2)) {
164683
+ return promptInput;
164684
+ }
164685
+ const jsonPayload = JsonPayload.assert(parsed2);
164686
+ validateCompatibility(jsonPayload.version, package_default.version);
164687
+ return jsonPayload;
164688
+ }
164689
+ function resolvePromptFile(input) {
164690
+ const workspace = process.env.GITHUB_WORKSPACE;
164691
+ const path4 = isAbsolute2(input) ? input : workspace ? resolve2(workspace, input) : resolve2(input);
164692
+ const content = readFileSync5(path4, "utf-8");
164693
+ if (!content.trim()) {
164694
+ throw new Error(`prompt_file ${JSON.stringify(input)} is empty.`);
164695
+ }
164696
+ return content;
164697
+ }
164698
+ function resolveNonPromptInputs() {
164699
+ return Inputs.omit("prompt", "prompt_file").assert({
164700
+ model: core6.getInput("model") || void 0,
164701
+ effort: core6.getInput("effort") || void 0,
164702
+ timeout: core6.getInput("timeout") || void 0,
164703
+ cwd: core6.getInput("cwd") || void 0,
164704
+ push: core6.getInput("push") || void 0,
164705
+ shell: core6.getInput("shell") || void 0,
164706
+ status_checks: core6.getInput("status_checks") || void 0,
164707
+ progress_comments: core6.getInput("progress_comments") || void 0
164708
+ });
164709
+ }
164710
+ function resolvePayload(resolvedPromptInput, repoSettings) {
164711
+ const [prompt, jsonPayload] = typeof resolvedPromptInput !== "string" ? [resolvedPromptInput.prompt, resolvedPromptInput] : [resolvedPromptInput, void 0];
164712
+ const inputs = resolveNonPromptInputs();
164713
+ const rawEvent = jsonPayload?.event;
164714
+ const event = isPayloadEvent(rawEvent) ? rawEvent : { trigger: "unknown" };
164715
+ const model = jsonPayload?.model ?? inputs.model ?? repoSettings.model ?? void 0;
164716
+ const rawEffort = jsonPayload?.effort ?? inputs.effort ?? repoSettings.effort ?? void 0;
164717
+ const effort = rawEffort === void 0 ? void 0 : parseEffortPosition(String(rawEffort));
164718
+ const isNonCollaborator = !isCollaborator(event);
164719
+ const repoShell = repoSettings.shell ?? "restricted";
164720
+ const inputShell = inputs.shell;
164721
+ let resolvedShell = repoShell;
164722
+ if (inputShell === "disabled") {
164723
+ resolvedShell = "disabled";
164724
+ } else if (inputShell === "restricted" && resolvedShell === "enabled") {
164725
+ resolvedShell = "restricted";
164726
+ }
164727
+ if (isNonCollaborator && resolvedShell === "enabled") {
164728
+ resolvedShell = "restricted";
164729
+ }
164730
+ return {
164731
+ "~pullfrog": true,
164732
+ version: jsonPayload?.version ?? package_default.version,
164733
+ model,
164734
+ // explicit only when the model came from a per-run override flag (carried on
164735
+ // the JSON payload). a GHA `model` input or the repo default is not explicit.
164736
+ modelExplicit: jsonPayload?.modelExplicit ?? false,
164737
+ effort,
164738
+ prompt,
164739
+ triggerer: jsonPayload?.triggerer ?? // it's not a common use case but GITHUB_ACTOR can be a user when the workflow is manually triggered by a user through GitHub Actions UI
164740
+ (!isPullfrog(process.env.GITHUB_ACTOR) ? process.env.GITHUB_ACTOR : void 0),
164741
+ baseInstructions: jsonPayload?.baseInstructions,
164742
+ eventInstructions: jsonPayload?.eventInstructions,
164743
+ previousRunsNote: jsonPayload?.previousRunsNote,
164744
+ event,
164745
+ xrepo: jsonPayload?.xrepo,
164746
+ timeout: inputs.timeout ?? jsonPayload?.timeout,
164747
+ cwd: resolveCwd(inputs.cwd),
164748
+ progressComment: jsonPayload?.progressComment,
164749
+ checkRun: jsonPayload?.checkRun,
164750
+ generateSummary: jsonPayload?.generateSummary,
164751
+ // permissions: inputs > repoSettings > fallbacks
164752
+ push: inputs.push ?? repoSettings.push ?? "restricted",
164753
+ shell: resolvedShell,
164754
+ // the `pullfrog` run-lifecycle check. ON by default — the whole point is that a PR
164755
+ // shows whether Pullfrog is running without anyone having to opt in. the workflow
164756
+ // input is the source of truth when set (mirrors `push`); otherwise the repo
164757
+ // setting decides.
164758
+ runStatusCheck: inputs.status_checks === void 0 ? repoSettings.statusChecks : inputs.status_checks === "enabled",
164759
+ // the `pullfrog-approval` verdict check stays opt-in and workflow-only. it exists to
164760
+ // be *required* by branch protection, so it must never turn itself on.
164761
+ approvalCheck: inputs.status_checks === "enabled",
164762
+ // temporary progress chrome. the workflow input is the source of truth when
164763
+ // set (mirrors `push`); otherwise the repo setting decides. defaults to true.
164764
+ progressComments: inputs.progress_comments === void 0 ? repoSettings.progressComments : inputs.progress_comments === "enabled",
164765
+ // set by proxy logic in main.ts when routing through OpenRouter
164766
+ proxyModel: void 0
164767
+ };
164768
+ }
164769
+ function resolveOutputSchema() {
164770
+ const raw2 = core6.getInput("output_schema");
164771
+ if (!raw2) return void 0;
164772
+ let parsed2;
164773
+ try {
164774
+ parsed2 = JSON.parse(raw2);
164775
+ } catch {
164776
+ throw new Error(`invalid output_schema: not valid JSON`);
164777
+ }
164778
+ if (!parsed2 || typeof parsed2 !== "object" || Array.isArray(parsed2)) {
164779
+ throw new Error(`invalid output_schema: must be a JSON object`);
164780
+ }
164781
+ log.info("\xBB structured output schema provided \u2014 output will be required");
164782
+ return parsed2;
164783
+ }
164784
+
164723
164785
  // utils/proxy.ts
164724
164786
  var core8 = __toESM(require_core(), 1);
164725
164787
 
@@ -165267,6 +165329,7 @@ var defaultSettings = {
165267
165329
  prApproveEnabled: false,
165268
165330
  autoMergeEnabled: false,
165269
165331
  signedCommits: false,
165332
+ repoIntelligence: false,
165270
165333
  progressComments: true,
165271
165334
  statusChecks: true,
165272
165335
  modeInstructions: {},
@@ -165406,7 +165469,8 @@ function formatAgentHangBody(input) {
165406
165469
  const headline = `**${input.diagnostic.label} ${verb}**${cause}`;
165407
165470
  const explanation = formatExplanation({
165408
165471
  isHang: input.isHang,
165409
- errorMessage: input.errorMessage
165472
+ errorMessage: input.errorMessage,
165473
+ idleSec: input.diagnostic.idleSec
165410
165474
  });
165411
165475
  const parts = [headline, "", `${explanation} ${formatEventsPart(input.diagnostic)}`];
165412
165476
  const tail = renderStderrTail(input.diagnostic.recentStderr);
@@ -165427,7 +165491,7 @@ function formatAgentHangBody(input) {
165427
165491
  }
165428
165492
  function formatExplanation(input) {
165429
165493
  if (!input.isHang) return `The agent exited unexpectedly: ${input.errorMessage}`;
165430
- const idleSec = parseIdleSec(input.errorMessage);
165494
+ const idleSec = input.idleSec ?? parseIdleSec(input.errorMessage);
165431
165495
  if (idleSec === void 0) {
165432
165496
  return "The agent stopped emitting events and was killed by the activity-timeout watchdog.";
165433
165497
  }
@@ -165438,11 +165502,13 @@ function parseIdleSec(message) {
165438
165502
  return match3 ? Number(match3[1]) : void 0;
165439
165503
  }
165440
165504
  function formatEventsPart(diagnostic) {
165441
- if (diagnostic.eventCount > 0) {
165505
+ if (diagnostic.lastProviderError) {
165442
165506
  return `${diagnostic.eventCount} events were processed before the failure.`;
165443
165507
  }
165444
- if (diagnostic.lastProviderError) return "No events were emitted before the failure.";
165445
- return "No events were emitted \u2014 check whether the model provider is reachable.";
165508
+ if (!diagnostic.sawModelOutput) {
165509
+ return "The model produced no output at all before the stall \u2014 the request was sent but nothing came back. This is usually transient; re-running often succeeds.";
165510
+ }
165511
+ return `${diagnostic.eventCount} events were processed before the failure.`;
165446
165512
  }
165447
165513
  function renderStderrTail(lines) {
165448
165514
  if (lines.length === 0) return "";
@@ -165798,8 +165864,8 @@ function getCurrentWorkflowFilename() {
165798
165864
  }
165799
165865
 
165800
165866
  // utils/runStatusCheck.ts
165801
- var RUN_STATUS_CHECK_NAME = "Pullfrog";
165802
- var APPROVAL_CHECK_NAME = "Pullfrog approval";
165867
+ var RUN_STATUS_CHECK_NAME = "pullfrog";
165868
+ var APPROVAL_CHECK_NAME = "pullfrog-approval";
165803
165869
  function parseCheckRunId(raw2) {
165804
165870
  if (!raw2?.id) return void 0;
165805
165871
  const id = parseInt(raw2.id, 10);
@@ -166324,7 +166390,7 @@ async function main() {
166324
166390
  _promise2 && await _promise2;
166325
166391
  }
166326
166392
  }
166327
- createTempDirectory();
166393
+ const tmpdir3 = createTempDirectory();
166328
166394
  const opencodeCliPath = await agents.opencode.install();
166329
166395
  captureBaselineModels(opencodeCliPath);
166330
166396
  if (runContext.dbSecrets) {
@@ -166378,7 +166444,6 @@ async function main() {
166378
166444
  if (payload.cwd && process.cwd() !== payload.cwd) {
166379
166445
  process.chdir(payload.cwd);
166380
166446
  }
166381
- const tmpdir3 = createTempDirectory();
166382
166447
  const originalBody = payload.event.body;
166383
166448
  const resolvedBody = await resolveBody({
166384
166449
  event: payload.event,
@@ -166487,6 +166552,7 @@ async function main() {
166487
166552
  prApproveEnabled: runContext.repoSettings.prApproveEnabled,
166488
166553
  autoMergeEnabled: runContext.repoSettings.autoMergeEnabled,
166489
166554
  signedCommits: runContext.repoSettings.signedCommits,
166555
+ repoIntelligence: runContext.repoSettings.repoIntelligence,
166490
166556
  modeInstructions: runContext.repoSettings.modeInstructions,
166491
166557
  toolState,
166492
166558
  runId: runInfo.runId,
@@ -166561,6 +166627,7 @@ async function main() {
166561
166627
  agentId,
166562
166628
  outputSchema,
166563
166629
  signedCommits: runContext.repoSettings.signedCommits,
166630
+ repoIntelligence: runContext.repoSettings.repoIntelligence,
166564
166631
  learningsFilePath: toolState.learningsFilePath ?? null,
166565
166632
  learningsHeadings: runContext.repoSettings.learningsHeadings,
166566
166633
  setupHookFailure: describeSetupFailure(setupHook.failure),