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/cli.mjs CHANGED
@@ -102476,6 +102476,9 @@ function formatMcpToolRef(agentId, toolName) {
102476
102476
  return agentId;
102477
102477
  }
102478
102478
  }
102479
+ function hasSimilarIssues(params) {
102480
+ return params.repoIntelligence && !!params.event.issue_number && !params.event.is_pr;
102481
+ }
102479
102482
 
102480
102483
  // utils/activity.ts
102481
102484
  import { performance as performance2 } from "node:perf_hooks";
@@ -102484,6 +102487,7 @@ function isMonitorDebugEnabled() {
102484
102487
  }
102485
102488
  var DEFAULT_ACTIVITY_TIMEOUT_MS = 3e5;
102486
102489
  var AGENT_ACTIVITY_TIMEOUT_MS = 9e5;
102490
+ var AGENT_FIRST_EVENT_TIMEOUT_MS = 12e4;
102487
102491
  var DEFAULT_ACTIVITY_CHECK_INTERVAL_MS = 5e3;
102488
102492
  var DEBUG_TS_PREFIX = /^(?:\[\d{4}-\d{2}-\d{2}T[^\]]+\]\s+)?/.source;
102489
102493
  var ACTIVITY_NOISE_PATTERNS = [
@@ -103177,7 +103181,7 @@ var import_semver = __toESM(require_semver2(), 1);
103177
103181
  // package.json
103178
103182
  var package_default = {
103179
103183
  name: "pullfrog",
103180
- version: "0.1.49",
103184
+ version: "0.1.50",
103181
103185
  type: "module",
103182
103186
  bin: {
103183
103187
  pullfrog: "dist/cli.mjs",
@@ -111048,7 +111052,7 @@ async function installOpencodeCli(params) {
111048
111052
  installDependencies: true
111049
111053
  });
111050
111054
  }
111051
- var AUTO_SELECT_WARNING = "select a model explicitly in the Pullfrog console (https://pullfrog.com/console) to avoid this.";
111055
+ 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.";
111052
111056
  function autoSelectModel() {
111053
111057
  const authorized2 = getAuthorizedModels();
111054
111058
  if (authorized2.size > 0) {
@@ -111260,9 +111264,18 @@ async function consumeEvents(ctx, signal) {
111260
111264
  }
111261
111265
  }
111262
111266
  }
111267
+ function isModelOutput(ctx, part) {
111268
+ if (part.type !== "text") return true;
111269
+ if (ctx.promptMessageID === void 0) {
111270
+ ctx.promptMessageID = part.messageID;
111271
+ return false;
111272
+ }
111273
+ return part.messageID !== ctx.promptMessageID;
111274
+ }
111263
111275
  async function dispatchEvent(ctx, event) {
111264
111276
  if (event.type === "message.part.updated") {
111265
111277
  ctx.lastEventAt = performance6.now();
111278
+ if (isModelOutput(ctx, event.properties.part)) ctx.sawModelOutput = true;
111266
111279
  await onPartUpdated(ctx, event.properties.part);
111267
111280
  return;
111268
111281
  }
@@ -111406,6 +111419,8 @@ async function runPromptTurn(ctx, params) {
111406
111419
  ctx.currentTurn = newTurn();
111407
111420
  const turn = ctx.currentTurn;
111408
111421
  const part = { type: "text", text: params.text };
111422
+ ctx.promptMessageID = void 0;
111423
+ ctx.sawModelOutput = false;
111409
111424
  let assistant;
111410
111425
  let returnedParts;
111411
111426
  let networkError = null;
@@ -111579,11 +111594,14 @@ function startInnerActivityWatchdog(params) {
111579
111594
  const id = setInterval(() => {
111580
111595
  if (fired) return;
111581
111596
  const idleMs = performance6.now() - params.ctx.lastEventAt;
111582
- if (idleMs <= params.timeoutMs) return;
111597
+ const budgetMs = params.ctx.sawModelOutput ? params.timeoutMs : AGENT_FIRST_EVENT_TIMEOUT_MS;
111598
+ if (idleMs <= budgetMs) return;
111583
111599
  fired = true;
111584
111600
  const idleSec = Math.round(idleMs / 1e3);
111601
+ params.ctx.diagnostic.idleSec = idleSec;
111602
+ params.ctx.diagnostic.sawModelOutput = params.ctx.sawModelOutput;
111585
111603
  log.info(
111586
- `\xBB no opencode events for ${idleSec}s \u2014 aborting in-flight prompt and notifying harness`
111604
+ 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`
111587
111605
  );
111588
111606
  params.abortController.abort();
111589
111607
  try {
@@ -111708,6 +111726,8 @@ var opencode = agent({
111708
111726
  currentTurn: null,
111709
111727
  eventCount: 0,
111710
111728
  lastEventAt: performance6.now(),
111729
+ sawModelOutput: false,
111730
+ promptMessageID: void 0,
111711
111731
  taskDispatchByCallID: /* @__PURE__ */ new Map(),
111712
111732
  loggedToolCallIDs: /* @__PURE__ */ new Set(),
111713
111733
  recentStderr: server.recentStderr,
@@ -111715,6 +111735,8 @@ var opencode = agent({
111715
111735
  label: "Pullfrog",
111716
111736
  recentStderr: server.recentStderr,
111717
111737
  lastProviderError: void 0,
111738
+ idleSec: void 0,
111739
+ sawModelOutput: false,
111718
111740
  eventCount: 0
111719
111741
  }
111720
111742
  };
@@ -159948,6 +159970,12 @@ function fixDoubleEscapedString(str) {
159948
159970
  return str;
159949
159971
  }
159950
159972
 
159973
+ // utils/isPullfrog.ts
159974
+ function isPullfrog(actor) {
159975
+ actor = actor?.toLowerCase().replace("[bot]", "");
159976
+ return !!actor && (actor === "pullfrog" || actor === "pullfrogdev");
159977
+ }
159978
+
159951
159979
  // utils/isTransientNetworkError.ts
159952
159980
  function isTransientNetworkError(error49, extraPatterns = []) {
159953
159981
  if (!(error49 instanceof Error)) return false;
@@ -160054,216 +160082,6 @@ function aggregateUsage(entries) {
160054
160082
  return out;
160055
160083
  }
160056
160084
 
160057
- // utils/payload.ts
160058
- var core4 = __toESM(require_core(), 1);
160059
- import { readFileSync as readFileSync4 } from "node:fs";
160060
- import { isAbsolute as isAbsolute2, resolve as resolve2 } from "node:path";
160061
-
160062
- // utils/versioning.ts
160063
- var import_semver2 = __toESM(require_semver2(), 1);
160064
- var COMPATIBILITY_POLICY = "non-breaking";
160065
- function validateCompatibility(payloadVersion, actionVersion) {
160066
- const payloadSemVer = import_semver2.default.parse(payloadVersion);
160067
- if (!payloadSemVer)
160068
- throw new Error(`Payload version ${payloadVersion} is not a valid semantic version.`);
160069
- const major = payloadSemVer.major;
160070
- const minor = payloadSemVer.minor;
160071
- const patch = payloadSemVer.patch;
160072
- const compatibilityRange = COMPATIBILITY_POLICY === "same-features" ? `^${major}.${minor}.${major === 0 ? patch : 0}` : `^${major}.${major === 0 ? minor : 0}.${major === 0 ? "x" : 0}`;
160073
- if (!import_semver2.default.satisfies(actionVersion, compatibilityRange)) {
160074
- throw new Error(
160075
- `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.`
160076
- );
160077
- }
160078
- }
160079
-
160080
- // utils/payload.ts
160081
- var ShellPermissionInput = type.enumerated("disabled", "restricted", "enabled");
160082
- var PushPermissionInput = type.enumerated("disabled", "restricted", "enabled");
160083
- var StatusChecksInput = type.enumerated("disabled", "enabled");
160084
- var ProgressCommentsInput = type.enumerated("disabled", "enabled");
160085
- var JsonPayload = type({
160086
- "~pullfrog": "true",
160087
- version: "string",
160088
- "model?": "string | undefined",
160089
- "modelExplicit?": "boolean | undefined",
160090
- "effort?": "number | string | undefined",
160091
- prompt: "string",
160092
- "triggerer?": "string | undefined",
160093
- "baseInstructions?": "string | undefined",
160094
- "eventInstructions?": "string",
160095
- "previousRunsNote?": "string",
160096
- "event?": "object",
160097
- "xrepo?": type({
160098
- mode: "'all' | 'explicit'",
160099
- read: "string[]",
160100
- write: "string[]",
160101
- // optional so a payload from an older server build (pre-`unavailable`)
160102
- // still parses against a newer action across a rolling deploy.
160103
- "unavailable?": "string[]"
160104
- }).or("undefined"),
160105
- "timeout?": "string | undefined",
160106
- "progressComment?": type({
160107
- id: "string",
160108
- type: "'issue' | 'review'"
160109
- }).or("undefined"),
160110
- // optional so a payload from an older server build (pre-`checkRun`) still parses
160111
- // against a newer action across a rolling deploy.
160112
- "checkRun?": type({ id: "string" }).or("undefined"),
160113
- "generateSummary?": "boolean | undefined"
160114
- });
160115
- var COLLABORATOR_PERMISSIONS = ["admin", "maintain", "write"];
160116
- function isCollaborator(event) {
160117
- const perm = event.authorPermission;
160118
- return perm !== void 0 && COLLABORATOR_PERMISSIONS.includes(perm);
160119
- }
160120
- var Inputs = type({
160121
- "prompt?": type.string.or("undefined"),
160122
- "prompt_file?": type.string.or("undefined"),
160123
- "model?": type.string.or("undefined"),
160124
- "effort?": type.string.or("undefined"),
160125
- "timeout?": type.string.or("undefined"),
160126
- "push?": PushPermissionInput.or("undefined"),
160127
- "shell?": ShellPermissionInput.or("undefined"),
160128
- "status_checks?": StatusChecksInput.or("undefined"),
160129
- "progress_comments?": ProgressCommentsInput.or("undefined"),
160130
- "cwd?": type.string.or("undefined"),
160131
- "output_schema?": type.string.or("undefined")
160132
- });
160133
- function isPayloadEvent(value2) {
160134
- return typeof value2 === "object" && value2 !== null && "trigger" in value2;
160135
- }
160136
- function resolveCwd(cwd) {
160137
- const workspace = process.env.GITHUB_WORKSPACE;
160138
- if (!cwd) return workspace;
160139
- if (isAbsolute2(cwd)) return cwd;
160140
- return workspace ? resolve2(workspace, cwd) : cwd;
160141
- }
160142
- function resolvePromptInput() {
160143
- const promptInput = core4.getInput("prompt");
160144
- const promptFile = core4.getInput("prompt_file");
160145
- if (promptInput && promptFile) {
160146
- throw new Error("set exactly one of 'prompt' or 'prompt_file' inputs, not both.");
160147
- }
160148
- if (promptFile) {
160149
- return resolvePromptFile(promptFile);
160150
- }
160151
- if (!promptInput) {
160152
- throw new Error("one of 'prompt' or 'prompt_file' inputs is required.");
160153
- }
160154
- let parsed2;
160155
- try {
160156
- parsed2 = JSON.parse(promptInput);
160157
- } catch {
160158
- return promptInput;
160159
- }
160160
- if (!parsed2 || typeof parsed2 !== "object" || !("~pullfrog" in parsed2)) {
160161
- return promptInput;
160162
- }
160163
- const jsonPayload = JsonPayload.assert(parsed2);
160164
- validateCompatibility(jsonPayload.version, package_default.version);
160165
- return jsonPayload;
160166
- }
160167
- function resolvePromptFile(input) {
160168
- const workspace = process.env.GITHUB_WORKSPACE;
160169
- const path4 = isAbsolute2(input) ? input : workspace ? resolve2(workspace, input) : resolve2(input);
160170
- const content = readFileSync4(path4, "utf-8");
160171
- if (!content.trim()) {
160172
- throw new Error(`prompt_file ${JSON.stringify(input)} is empty.`);
160173
- }
160174
- return content;
160175
- }
160176
- function resolveNonPromptInputs() {
160177
- return Inputs.omit("prompt", "prompt_file").assert({
160178
- model: core4.getInput("model") || void 0,
160179
- effort: core4.getInput("effort") || void 0,
160180
- timeout: core4.getInput("timeout") || void 0,
160181
- cwd: core4.getInput("cwd") || void 0,
160182
- push: core4.getInput("push") || void 0,
160183
- shell: core4.getInput("shell") || void 0,
160184
- status_checks: core4.getInput("status_checks") || void 0,
160185
- progress_comments: core4.getInput("progress_comments") || void 0
160186
- });
160187
- }
160188
- var isPullfrog = (actor) => {
160189
- actor = actor?.replace("[bot]", "");
160190
- return !!actor && (actor === "pullfrog" || actor === "pullfrogdev");
160191
- };
160192
- function resolvePayload(resolvedPromptInput, repoSettings) {
160193
- const [prompt, jsonPayload] = typeof resolvedPromptInput !== "string" ? [resolvedPromptInput.prompt, resolvedPromptInput] : [resolvedPromptInput, void 0];
160194
- const inputs = resolveNonPromptInputs();
160195
- const rawEvent = jsonPayload?.event;
160196
- const event = isPayloadEvent(rawEvent) ? rawEvent : { trigger: "unknown" };
160197
- const model = jsonPayload?.model ?? inputs.model ?? repoSettings.model ?? void 0;
160198
- const rawEffort = jsonPayload?.effort ?? inputs.effort ?? repoSettings.effort ?? void 0;
160199
- const effort = rawEffort === void 0 ? void 0 : parseEffortPosition(String(rawEffort));
160200
- const isNonCollaborator = !isCollaborator(event);
160201
- const repoShell = repoSettings.shell ?? "restricted";
160202
- const inputShell = inputs.shell;
160203
- let resolvedShell = repoShell;
160204
- if (inputShell === "disabled") {
160205
- resolvedShell = "disabled";
160206
- } else if (inputShell === "restricted" && resolvedShell === "enabled") {
160207
- resolvedShell = "restricted";
160208
- }
160209
- if (isNonCollaborator && resolvedShell === "enabled") {
160210
- resolvedShell = "restricted";
160211
- }
160212
- return {
160213
- "~pullfrog": true,
160214
- version: jsonPayload?.version ?? package_default.version,
160215
- model,
160216
- // explicit only when the model came from a per-run override flag (carried on
160217
- // the JSON payload). a GHA `model` input or the repo default is not explicit.
160218
- modelExplicit: jsonPayload?.modelExplicit ?? false,
160219
- effort,
160220
- prompt,
160221
- 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
160222
- (!isPullfrog(process.env.GITHUB_ACTOR) ? process.env.GITHUB_ACTOR : void 0),
160223
- baseInstructions: jsonPayload?.baseInstructions,
160224
- eventInstructions: jsonPayload?.eventInstructions,
160225
- previousRunsNote: jsonPayload?.previousRunsNote,
160226
- event,
160227
- xrepo: jsonPayload?.xrepo,
160228
- timeout: inputs.timeout ?? jsonPayload?.timeout,
160229
- cwd: resolveCwd(inputs.cwd),
160230
- progressComment: jsonPayload?.progressComment,
160231
- checkRun: jsonPayload?.checkRun,
160232
- generateSummary: jsonPayload?.generateSummary,
160233
- // permissions: inputs > repoSettings > fallbacks
160234
- push: inputs.push ?? repoSettings.push ?? "restricted",
160235
- shell: resolvedShell,
160236
- // the `pullfrog` run-lifecycle check. ON by default — the whole point is that a PR
160237
- // shows whether Pullfrog is running without anyone having to opt in. the workflow
160238
- // input is the source of truth when set (mirrors `push`); otherwise the repo
160239
- // setting decides.
160240
- runStatusCheck: inputs.status_checks === void 0 ? repoSettings.statusChecks : inputs.status_checks === "enabled",
160241
- // the `pullfrog-approval` verdict check stays opt-in and workflow-only. it exists to
160242
- // be *required* by branch protection, so it must never turn itself on.
160243
- approvalCheck: inputs.status_checks === "enabled",
160244
- // temporary progress chrome. the workflow input is the source of truth when
160245
- // set (mirrors `push`); otherwise the repo setting decides. defaults to true.
160246
- progressComments: inputs.progress_comments === void 0 ? repoSettings.progressComments : inputs.progress_comments === "enabled",
160247
- // set by proxy logic in main.ts when routing through OpenRouter
160248
- proxyModel: void 0
160249
- };
160250
- }
160251
- function resolveOutputSchema() {
160252
- const raw2 = core4.getInput("output_schema");
160253
- if (!raw2) return void 0;
160254
- let parsed2;
160255
- try {
160256
- parsed2 = JSON.parse(raw2);
160257
- } catch {
160258
- throw new Error(`invalid output_schema: not valid JSON`);
160259
- }
160260
- if (!parsed2 || typeof parsed2 !== "object" || Array.isArray(parsed2)) {
160261
- throw new Error(`invalid output_schema: must be a JSON object`);
160262
- }
160263
- log.info("\xBB structured output schema provided \u2014 output will be required");
160264
- return parsed2;
160265
- }
160266
-
160267
160085
  // mcp/comment.ts
160268
160086
  function isNotFoundError(error49) {
160269
160087
  return error49 instanceof Error && error49.message.includes("Not Found");
@@ -162574,7 +162392,7 @@ function isMetadataYarnClassic(metadataPath) {
162574
162392
  }
162575
162393
 
162576
162394
  // utils/packageManager.ts
162577
- var import_semver3 = __toESM(require_semver2(), 1);
162395
+ var import_semver2 = __toESM(require_semver2(), 1);
162578
162396
  import { existsSync as existsSync5 } from "node:fs";
162579
162397
  import { mkdir, readFile as readFile2 } from "node:fs/promises";
162580
162398
  import { delimiter, join as join17 } from "node:path";
@@ -162596,7 +162414,7 @@ function parsePackageManagerField(value2) {
162596
162414
  return {
162597
162415
  name,
162598
162416
  version: version3,
162599
- concrete: import_semver3.default.valid(version3) !== null,
162417
+ concrete: import_semver2.default.valid(version3) !== null,
162600
162418
  source: "packageManager"
162601
162419
  };
162602
162420
  }
@@ -162610,7 +162428,7 @@ function parseDevEnginesField(field) {
162610
162428
  return {
162611
162429
  name: field.name,
162612
162430
  version: version3,
162613
- concrete: import_semver3.default.valid(version3) !== null,
162431
+ concrete: import_semver2.default.valid(version3) !== null,
162614
162432
  source: "devEngines"
162615
162433
  };
162616
162434
  }
@@ -162644,7 +162462,7 @@ async function resolvePackageManagerSpec(cwd) {
162644
162462
  }
162645
162463
  return devSpec;
162646
162464
  }
162647
- if (pmSpec.concrete && import_semver3.default.satisfies(pmSpec.version, devSpec.version)) {
162465
+ if (pmSpec.concrete && import_semver2.default.satisfies(pmSpec.version, devSpec.version)) {
162648
162466
  return pmSpec;
162649
162467
  }
162650
162468
  if (pmSpec.concrete) {
@@ -162856,10 +162674,10 @@ ${errorMessage}`]
162856
162674
  };
162857
162675
 
162858
162676
  // prep/installPythonDependencies.ts
162859
- import { existsSync as existsSync7, readFileSync as readFileSync5 } from "node:fs";
162677
+ import { existsSync as existsSync7, readFileSync as readFileSync4 } from "node:fs";
162860
162678
  import { join as join19 } from "node:path";
162861
162679
  function declaresBuildSystem(path4) {
162862
- return /^\s*\[\s*build-system\s*\]/m.test(readFileSync5(path4, "utf8"));
162680
+ return /^\s*\[\s*build-system\s*\]/m.test(readFileSync4(path4, "utf8"));
162863
162681
  }
162864
162682
  function configApplies(config3, cwd) {
162865
162683
  const path4 = join19(cwd, config3.file);
@@ -164994,6 +164812,36 @@ function KillBackgroundTool(ctx) {
164994
164812
  });
164995
164813
  }
164996
164814
 
164815
+ // mcp/similarIssues.ts
164816
+ var SimilarIssues = type({
164817
+ issue_number: type.number.describe("The issue number to find older duplicate candidates for")
164818
+ });
164819
+ function SimilarIssuesTool(ctx) {
164820
+ return tool({
164821
+ name: "find_similar_issues",
164822
+ 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.",
164823
+ mutates: true,
164824
+ parameters: SimilarIssues,
164825
+ execute: execute(async (input) => {
164826
+ if (ctx.payload.event.is_pr || ctx.payload.event.issue_number !== input.issue_number) {
164827
+ throw new Error("find_similar_issues is limited to the issue that triggered this run");
164828
+ }
164829
+ const response = await apiFetch({
164830
+ path: `/api/repo/${ctx.repo.owner}/${ctx.repo.name}/issues/${input.issue_number}/similar`,
164831
+ headers: {
164832
+ authorization: `Bearer ${ctx.apiToken}`
164833
+ },
164834
+ signal: AbortSignal.timeout(13 * 6e4)
164835
+ });
164836
+ if (!response.ok) {
164837
+ const message = await response.text();
164838
+ throw new Error(`similar issue lookup returned ${response.status}: ${message}`);
164839
+ }
164840
+ return { result: await response.json() };
164841
+ })
164842
+ });
164843
+ }
164844
+
164997
164845
  // mcp/upload.ts
164998
164846
  import * as fs4 from "node:fs";
164999
164847
  import * as path3 from "node:path";
@@ -165232,6 +165080,9 @@ function buildCommonTools(ctx, outputSchema) {
165232
165080
  if (ctx.xrepo) {
165233
165081
  tools.push(ListReposTool(ctx), CheckoutRepoTool(ctx));
165234
165082
  }
165083
+ if (hasSimilarIssues({ repoIntelligence: ctx.repoIntelligence, event: ctx.payload.event })) {
165084
+ tools.push(SimilarIssuesTool(ctx));
165085
+ }
165235
165086
  const isStandalone = ctx.payload.event.trigger === "unknown";
165236
165087
  if (isStandalone || outputSchema) {
165237
165088
  tools.push(SetOutputTool(ctx, outputSchema));
@@ -166175,6 +166026,11 @@ Rules:
166175
166026
  ### GitHub
166176
166027
 
166177
166028
  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.
166029
+ ${hasSimilarIssues({ repoIntelligence: ctx.repoIntelligence, event: ctx.payload.event }) ? `
166030
+ #### Duplicate detection (enabled for this repository)
166031
+
166032
+ Call \`${t2("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.
166033
+ ` : ""}
166178
166034
 
166179
166035
  ${getShellInstructions(ctx.payload.shell, t2)}
166180
166036
 
@@ -166501,7 +166357,7 @@ function buildModelAccessError(input) {
166501
166357
  }
166502
166358
 
166503
166359
  // utils/normalizeEnv.ts
166504
- var core5 = __toESM(require_core(), 1);
166360
+ var core4 = __toESM(require_core(), 1);
166505
166361
  function sanitizeSecret(key, value2) {
166506
166362
  const trimmed = value2.trim();
166507
166363
  if (trimmed.length === 0) {
@@ -166515,7 +166371,7 @@ function sanitizeSecret(key, value2) {
166515
166371
  `\xBB stripped whitespace from ${key} (whitespace in secret values breaks GitHub Actions log masking)`
166516
166372
  );
166517
166373
  }
166518
- core5.setSecret(trimmed);
166374
+ core4.setSecret(trimmed);
166519
166375
  return trimmed;
166520
166376
  }
166521
166377
  function normalizeEnv() {
@@ -166559,7 +166415,7 @@ function normalizeEnv() {
166559
166415
  }
166560
166416
 
166561
166417
  // utils/overrides.ts
166562
- var core6 = __toESM(require_core(), 1);
166418
+ var core5 = __toESM(require_core(), 1);
166563
166419
  var DENIED_OVERRIDE_NAMES = /* @__PURE__ */ new Set([
166564
166420
  "GITHUB_TOKEN",
166565
166421
  "GH_TOKEN",
@@ -166605,7 +166461,7 @@ function applyOverrides(params) {
166605
166461
  denied.push(key);
166606
166462
  continue;
166607
166463
  }
166608
- if (value2.length > 0) core6.setSecret(value2);
166464
+ if (value2.length > 0) core5.setSecret(value2);
166609
166465
  params.env[key] = value2;
166610
166466
  applied.push(key);
166611
166467
  }
@@ -166613,6 +166469,212 @@ function applyOverrides(params) {
166613
166469
  return { applied, denied };
166614
166470
  }
166615
166471
 
166472
+ // utils/payload.ts
166473
+ var core6 = __toESM(require_core(), 1);
166474
+ import { readFileSync as readFileSync6 } from "node:fs";
166475
+ import { isAbsolute as isAbsolute2, resolve as resolve2 } from "node:path";
166476
+
166477
+ // utils/versioning.ts
166478
+ var import_semver3 = __toESM(require_semver2(), 1);
166479
+ var COMPATIBILITY_POLICY = "non-breaking";
166480
+ function validateCompatibility(payloadVersion, actionVersion) {
166481
+ const payloadSemVer = import_semver3.default.parse(payloadVersion);
166482
+ if (!payloadSemVer)
166483
+ throw new Error(`Payload version ${payloadVersion} is not a valid semantic version.`);
166484
+ const major = payloadSemVer.major;
166485
+ const minor = payloadSemVer.minor;
166486
+ const patch = payloadSemVer.patch;
166487
+ const compatibilityRange = COMPATIBILITY_POLICY === "same-features" ? `^${major}.${minor}.${major === 0 ? patch : 0}` : `^${major}.${major === 0 ? minor : 0}.${major === 0 ? "x" : 0}`;
166488
+ if (!import_semver3.default.satisfies(actionVersion, compatibilityRange)) {
166489
+ throw new Error(
166490
+ `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.`
166491
+ );
166492
+ }
166493
+ }
166494
+
166495
+ // utils/payload.ts
166496
+ var ShellPermissionInput = type.enumerated("disabled", "restricted", "enabled");
166497
+ var PushPermissionInput = type.enumerated("disabled", "restricted", "enabled");
166498
+ var StatusChecksInput = type.enumerated("disabled", "enabled");
166499
+ var ProgressCommentsInput = type.enumerated("disabled", "enabled");
166500
+ var JsonPayload = type({
166501
+ "~pullfrog": "true",
166502
+ version: "string",
166503
+ "model?": "string | undefined",
166504
+ "modelExplicit?": "boolean | undefined",
166505
+ "effort?": "number | string | undefined",
166506
+ prompt: "string",
166507
+ "triggerer?": "string | undefined",
166508
+ "baseInstructions?": "string | undefined",
166509
+ "eventInstructions?": "string",
166510
+ "previousRunsNote?": "string",
166511
+ "event?": "object",
166512
+ "xrepo?": type({
166513
+ mode: "'all' | 'explicit'",
166514
+ read: "string[]",
166515
+ write: "string[]",
166516
+ // optional so a payload from an older server build (pre-`unavailable`)
166517
+ // still parses against a newer action across a rolling deploy.
166518
+ "unavailable?": "string[]"
166519
+ }).or("undefined"),
166520
+ "timeout?": "string | undefined",
166521
+ "progressComment?": type({
166522
+ id: "string",
166523
+ type: "'issue' | 'review'"
166524
+ }).or("undefined"),
166525
+ // optional so a payload from an older server build (pre-`checkRun`) still parses
166526
+ // against a newer action across a rolling deploy.
166527
+ "checkRun?": type({ id: "string" }).or("undefined"),
166528
+ "generateSummary?": "boolean | undefined"
166529
+ });
166530
+ var COLLABORATOR_PERMISSIONS = ["admin", "maintain", "write"];
166531
+ function isCollaborator(event) {
166532
+ const perm = event.authorPermission;
166533
+ return perm !== void 0 && COLLABORATOR_PERMISSIONS.includes(perm);
166534
+ }
166535
+ var Inputs = type({
166536
+ "prompt?": type.string.or("undefined"),
166537
+ "prompt_file?": type.string.or("undefined"),
166538
+ "model?": type.string.or("undefined"),
166539
+ "effort?": type.string.or("undefined"),
166540
+ "timeout?": type.string.or("undefined"),
166541
+ "push?": PushPermissionInput.or("undefined"),
166542
+ "shell?": ShellPermissionInput.or("undefined"),
166543
+ "status_checks?": StatusChecksInput.or("undefined"),
166544
+ "progress_comments?": ProgressCommentsInput.or("undefined"),
166545
+ "cwd?": type.string.or("undefined"),
166546
+ "output_schema?": type.string.or("undefined")
166547
+ });
166548
+ function isPayloadEvent(value2) {
166549
+ return typeof value2 === "object" && value2 !== null && "trigger" in value2;
166550
+ }
166551
+ function resolveCwd(cwd) {
166552
+ const workspace = process.env.GITHUB_WORKSPACE;
166553
+ if (!cwd) return workspace;
166554
+ if (isAbsolute2(cwd)) return cwd;
166555
+ return workspace ? resolve2(workspace, cwd) : cwd;
166556
+ }
166557
+ function resolvePromptInput() {
166558
+ const promptInput = core6.getInput("prompt");
166559
+ const promptFile = core6.getInput("prompt_file");
166560
+ if (promptInput && promptFile) {
166561
+ throw new Error("set exactly one of 'prompt' or 'prompt_file' inputs, not both.");
166562
+ }
166563
+ if (promptFile) {
166564
+ return resolvePromptFile(promptFile);
166565
+ }
166566
+ if (!promptInput) {
166567
+ throw new Error("one of 'prompt' or 'prompt_file' inputs is required.");
166568
+ }
166569
+ let parsed2;
166570
+ try {
166571
+ parsed2 = JSON.parse(promptInput);
166572
+ } catch {
166573
+ return promptInput;
166574
+ }
166575
+ if (!parsed2 || typeof parsed2 !== "object" || !("~pullfrog" in parsed2)) {
166576
+ return promptInput;
166577
+ }
166578
+ const jsonPayload = JsonPayload.assert(parsed2);
166579
+ validateCompatibility(jsonPayload.version, package_default.version);
166580
+ return jsonPayload;
166581
+ }
166582
+ function resolvePromptFile(input) {
166583
+ const workspace = process.env.GITHUB_WORKSPACE;
166584
+ const path4 = isAbsolute2(input) ? input : workspace ? resolve2(workspace, input) : resolve2(input);
166585
+ const content = readFileSync6(path4, "utf-8");
166586
+ if (!content.trim()) {
166587
+ throw new Error(`prompt_file ${JSON.stringify(input)} is empty.`);
166588
+ }
166589
+ return content;
166590
+ }
166591
+ function resolveNonPromptInputs() {
166592
+ return Inputs.omit("prompt", "prompt_file").assert({
166593
+ model: core6.getInput("model") || void 0,
166594
+ effort: core6.getInput("effort") || void 0,
166595
+ timeout: core6.getInput("timeout") || void 0,
166596
+ cwd: core6.getInput("cwd") || void 0,
166597
+ push: core6.getInput("push") || void 0,
166598
+ shell: core6.getInput("shell") || void 0,
166599
+ status_checks: core6.getInput("status_checks") || void 0,
166600
+ progress_comments: core6.getInput("progress_comments") || void 0
166601
+ });
166602
+ }
166603
+ function resolvePayload(resolvedPromptInput, repoSettings) {
166604
+ const [prompt, jsonPayload] = typeof resolvedPromptInput !== "string" ? [resolvedPromptInput.prompt, resolvedPromptInput] : [resolvedPromptInput, void 0];
166605
+ const inputs = resolveNonPromptInputs();
166606
+ const rawEvent = jsonPayload?.event;
166607
+ const event = isPayloadEvent(rawEvent) ? rawEvent : { trigger: "unknown" };
166608
+ const model = jsonPayload?.model ?? inputs.model ?? repoSettings.model ?? void 0;
166609
+ const rawEffort = jsonPayload?.effort ?? inputs.effort ?? repoSettings.effort ?? void 0;
166610
+ const effort = rawEffort === void 0 ? void 0 : parseEffortPosition(String(rawEffort));
166611
+ const isNonCollaborator = !isCollaborator(event);
166612
+ const repoShell = repoSettings.shell ?? "restricted";
166613
+ const inputShell = inputs.shell;
166614
+ let resolvedShell = repoShell;
166615
+ if (inputShell === "disabled") {
166616
+ resolvedShell = "disabled";
166617
+ } else if (inputShell === "restricted" && resolvedShell === "enabled") {
166618
+ resolvedShell = "restricted";
166619
+ }
166620
+ if (isNonCollaborator && resolvedShell === "enabled") {
166621
+ resolvedShell = "restricted";
166622
+ }
166623
+ return {
166624
+ "~pullfrog": true,
166625
+ version: jsonPayload?.version ?? package_default.version,
166626
+ model,
166627
+ // explicit only when the model came from a per-run override flag (carried on
166628
+ // the JSON payload). a GHA `model` input or the repo default is not explicit.
166629
+ modelExplicit: jsonPayload?.modelExplicit ?? false,
166630
+ effort,
166631
+ prompt,
166632
+ 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
166633
+ (!isPullfrog(process.env.GITHUB_ACTOR) ? process.env.GITHUB_ACTOR : void 0),
166634
+ baseInstructions: jsonPayload?.baseInstructions,
166635
+ eventInstructions: jsonPayload?.eventInstructions,
166636
+ previousRunsNote: jsonPayload?.previousRunsNote,
166637
+ event,
166638
+ xrepo: jsonPayload?.xrepo,
166639
+ timeout: inputs.timeout ?? jsonPayload?.timeout,
166640
+ cwd: resolveCwd(inputs.cwd),
166641
+ progressComment: jsonPayload?.progressComment,
166642
+ checkRun: jsonPayload?.checkRun,
166643
+ generateSummary: jsonPayload?.generateSummary,
166644
+ // permissions: inputs > repoSettings > fallbacks
166645
+ push: inputs.push ?? repoSettings.push ?? "restricted",
166646
+ shell: resolvedShell,
166647
+ // the `pullfrog` run-lifecycle check. ON by default — the whole point is that a PR
166648
+ // shows whether Pullfrog is running without anyone having to opt in. the workflow
166649
+ // input is the source of truth when set (mirrors `push`); otherwise the repo
166650
+ // setting decides.
166651
+ runStatusCheck: inputs.status_checks === void 0 ? repoSettings.statusChecks : inputs.status_checks === "enabled",
166652
+ // the `pullfrog-approval` verdict check stays opt-in and workflow-only. it exists to
166653
+ // be *required* by branch protection, so it must never turn itself on.
166654
+ approvalCheck: inputs.status_checks === "enabled",
166655
+ // temporary progress chrome. the workflow input is the source of truth when
166656
+ // set (mirrors `push`); otherwise the repo setting decides. defaults to true.
166657
+ progressComments: inputs.progress_comments === void 0 ? repoSettings.progressComments : inputs.progress_comments === "enabled",
166658
+ // set by proxy logic in main.ts when routing through OpenRouter
166659
+ proxyModel: void 0
166660
+ };
166661
+ }
166662
+ function resolveOutputSchema() {
166663
+ const raw2 = core6.getInput("output_schema");
166664
+ if (!raw2) return void 0;
166665
+ let parsed2;
166666
+ try {
166667
+ parsed2 = JSON.parse(raw2);
166668
+ } catch {
166669
+ throw new Error(`invalid output_schema: not valid JSON`);
166670
+ }
166671
+ if (!parsed2 || typeof parsed2 !== "object" || Array.isArray(parsed2)) {
166672
+ throw new Error(`invalid output_schema: must be a JSON object`);
166673
+ }
166674
+ log.info("\xBB structured output schema provided \u2014 output will be required");
166675
+ return parsed2;
166676
+ }
166677
+
166616
166678
  // utils/proxy.ts
166617
166679
  var core8 = __toESM(require_core(), 1);
166618
166680
 
@@ -167160,6 +167222,7 @@ var defaultSettings = {
167160
167222
  prApproveEnabled: false,
167161
167223
  autoMergeEnabled: false,
167162
167224
  signedCommits: false,
167225
+ repoIntelligence: false,
167163
167226
  progressComments: true,
167164
167227
  statusChecks: true,
167165
167228
  modeInstructions: {},
@@ -167299,7 +167362,8 @@ function formatAgentHangBody(input) {
167299
167362
  const headline = `**${input.diagnostic.label} ${verb}**${cause}`;
167300
167363
  const explanation = formatExplanation({
167301
167364
  isHang: input.isHang,
167302
- errorMessage: input.errorMessage
167365
+ errorMessage: input.errorMessage,
167366
+ idleSec: input.diagnostic.idleSec
167303
167367
  });
167304
167368
  const parts = [headline, "", `${explanation} ${formatEventsPart(input.diagnostic)}`];
167305
167369
  const tail = renderStderrTail(input.diagnostic.recentStderr);
@@ -167320,7 +167384,7 @@ function formatAgentHangBody(input) {
167320
167384
  }
167321
167385
  function formatExplanation(input) {
167322
167386
  if (!input.isHang) return `The agent exited unexpectedly: ${input.errorMessage}`;
167323
- const idleSec = parseIdleSec(input.errorMessage);
167387
+ const idleSec = input.idleSec ?? parseIdleSec(input.errorMessage);
167324
167388
  if (idleSec === void 0) {
167325
167389
  return "The agent stopped emitting events and was killed by the activity-timeout watchdog.";
167326
167390
  }
@@ -167331,11 +167395,13 @@ function parseIdleSec(message) {
167331
167395
  return match3 ? Number(match3[1]) : void 0;
167332
167396
  }
167333
167397
  function formatEventsPart(diagnostic) {
167334
- if (diagnostic.eventCount > 0) {
167398
+ if (diagnostic.lastProviderError) {
167335
167399
  return `${diagnostic.eventCount} events were processed before the failure.`;
167336
167400
  }
167337
- if (diagnostic.lastProviderError) return "No events were emitted before the failure.";
167338
- return "No events were emitted \u2014 check whether the model provider is reachable.";
167401
+ if (!diagnostic.sawModelOutput) {
167402
+ 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.";
167403
+ }
167404
+ return `${diagnostic.eventCount} events were processed before the failure.`;
167339
167405
  }
167340
167406
  function renderStderrTail(lines) {
167341
167407
  if (lines.length === 0) return "";
@@ -167691,8 +167757,8 @@ function getCurrentWorkflowFilename() {
167691
167757
  }
167692
167758
 
167693
167759
  // utils/runStatusCheck.ts
167694
- var RUN_STATUS_CHECK_NAME = "Pullfrog";
167695
- var APPROVAL_CHECK_NAME = "Pullfrog approval";
167760
+ var RUN_STATUS_CHECK_NAME = "pullfrog";
167761
+ var APPROVAL_CHECK_NAME = "pullfrog-approval";
167696
167762
  function parseCheckRunId(raw2) {
167697
167763
  if (!raw2?.id) return void 0;
167698
167764
  const id = parseInt(raw2.id, 10);
@@ -168217,7 +168283,7 @@ async function main() {
168217
168283
  _promise2 && await _promise2;
168218
168284
  }
168219
168285
  }
168220
- createTempDirectory();
168286
+ const tmpdir4 = createTempDirectory();
168221
168287
  const opencodeCliPath = await agents.opencode.install();
168222
168288
  captureBaselineModels(opencodeCliPath);
168223
168289
  if (runContext.dbSecrets) {
@@ -168271,7 +168337,6 @@ async function main() {
168271
168337
  if (payload.cwd && process.cwd() !== payload.cwd) {
168272
168338
  process.chdir(payload.cwd);
168273
168339
  }
168274
- const tmpdir4 = createTempDirectory();
168275
168340
  const originalBody = payload.event.body;
168276
168341
  const resolvedBody = await resolveBody({
168277
168342
  event: payload.event,
@@ -168380,6 +168445,7 @@ async function main() {
168380
168445
  prApproveEnabled: runContext.repoSettings.prApproveEnabled,
168381
168446
  autoMergeEnabled: runContext.repoSettings.autoMergeEnabled,
168382
168447
  signedCommits: runContext.repoSettings.signedCommits,
168448
+ repoIntelligence: runContext.repoSettings.repoIntelligence,
168383
168449
  modeInstructions: runContext.repoSettings.modeInstructions,
168384
168450
  toolState,
168385
168451
  runId: runInfo.runId,
@@ -168454,6 +168520,7 @@ async function main() {
168454
168520
  agentId,
168455
168521
  outputSchema,
168456
168522
  signedCommits: runContext.repoSettings.signedCommits,
168523
+ repoIntelligence: runContext.repoSettings.repoIntelligence,
168457
168524
  learningsFilePath: toolState.learningsFilePath ?? null,
168458
168525
  learningsHeadings: runContext.repoSettings.learningsHeadings,
168459
168526
  setupHookFailure: describeSetupFailure(setupHook.failure),
@@ -169650,7 +169717,7 @@ async function runCli4(input) {
169650
169717
  }
169651
169718
 
169652
169719
  // cli.ts
169653
- var VERSION10 = "0.1.49";
169720
+ var VERSION10 = "0.1.50";
169654
169721
  var bin = basename2(process.argv[1] || "");
169655
169722
  var PROG = bin === "pf" || bin === "pullfrog" ? bin : "pullfrog";
169656
169723
  var rawArgs = process.argv.slice(2);