pullfrog 0.1.62 → 0.1.63

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
@@ -107358,7 +107358,7 @@ var import_semver = __toESM(require_semver2(), 1);
107358
107358
  // package.json
107359
107359
  var package_default = {
107360
107360
  name: "pullfrog",
107361
- version: "0.1.62",
107361
+ version: "0.1.63",
107362
107362
  type: "module",
107363
107363
  bin: {
107364
107364
  pullfrog: "dist/cli.mjs",
@@ -116237,7 +116237,7 @@ function isModelOutput(ctx, part) {
116237
116237
  async function dispatchEvent(ctx, event) {
116238
116238
  if (event.type === "message.part.updated") {
116239
116239
  ctx.lastEventAt = performance7.now();
116240
- if (isModelOutput(ctx, event.properties.part)) ctx.sawModelOutput = true;
116240
+ if (isModelOutput(ctx, event.properties.part)) ctx.diagnostic.sawModelOutput = true;
116241
116241
  await onPartUpdated(ctx, event.properties.part);
116242
116242
  return;
116243
116243
  }
@@ -116383,7 +116383,8 @@ async function runPromptTurn(ctx, params) {
116383
116383
  const turn = ctx.currentTurn;
116384
116384
  const part = { type: "text", text: params.text };
116385
116385
  ctx.promptMessageID = void 0;
116386
- ctx.sawModelOutput = false;
116386
+ ctx.diagnostic.sawModelOutput = false;
116387
+ ctx.diagnostic.idleSec = void 0;
116387
116388
  let assistant;
116388
116389
  let returnedParts;
116389
116390
  let networkError = null;
@@ -116443,7 +116444,7 @@ async function runPromptTurn(ctx, params) {
116443
116444
  usage
116444
116445
  };
116445
116446
  }
116446
- if (!ctx.sawModelOutput && finalText.trim().length === 0 && !usage) {
116447
+ if (!ctx.diagnostic.sawModelOutput && finalText.trim().length === 0 && !usage) {
116447
116448
  return {
116448
116449
  success: false,
116449
116450
  output: finalText,
@@ -116568,19 +116569,18 @@ function startInnerActivityWatchdog(params) {
116568
116569
  const id = setInterval(() => {
116569
116570
  if (fired) return;
116570
116571
  const idleMs = performance7.now() - params.ctx.lastEventAt;
116571
- const compiledMs = params.ctx.sawModelOutput ? params.timeoutMs : AGENT_FIRST_EVENT_TIMEOUT_MS;
116572
+ const compiledMs = params.ctx.diagnostic.sawModelOutput ? params.timeoutMs : AGENT_FIRST_EVENT_TIMEOUT_MS;
116572
116573
  const budgetMs = everFired ? compiledMs : watchdogBudgetMs(
116573
116574
  compiledMs,
116574
- params.ctx.sawModelOutput ? "PULLFROG_E2E_ACTIVITY_TIMEOUT_MS" : "PULLFROG_E2E_FIRST_EVENT_TIMEOUT_MS"
116575
+ params.ctx.diagnostic.sawModelOutput ? "PULLFROG_E2E_ACTIVITY_TIMEOUT_MS" : "PULLFROG_E2E_FIRST_EVENT_TIMEOUT_MS"
116575
116576
  );
116576
116577
  if (idleMs <= budgetMs) return;
116577
116578
  fired = true;
116578
116579
  everFired = true;
116579
116580
  const idleSec = Math.round(idleMs / 1e3);
116580
116581
  params.ctx.diagnostic.idleSec = idleSec;
116581
- params.ctx.diagnostic.sawModelOutput = params.ctx.sawModelOutput;
116582
116582
  log2.info(
116583
- 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`
116583
+ params.ctx.diagnostic.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`
116584
116584
  );
116585
116585
  params.abortTurn();
116586
116586
  try {
@@ -116724,7 +116724,6 @@ var opencode = agent({
116724
116724
  currentTurn: null,
116725
116725
  eventCount: 0,
116726
116726
  lastEventAt: performance7.now(),
116727
- sawModelOutput: false,
116728
116727
  promptMessageID: void 0,
116729
116728
  taskDispatchByCallID: /* @__PURE__ */ new Map(),
116730
116729
  loggedToolCallIDs: /* @__PURE__ */ new Set(),
@@ -116788,7 +116787,9 @@ var opencode = agent({
116788
116787
  );
116789
116788
  };
116790
116789
  const standDownIfRecovered = (turn) => {
116791
- if (watchdog.firedThisTurn() && turn.success) ctx.onTurnRecovered?.();
116790
+ if (!watchdog.firedThisTurn() || !turn.success) return turn;
116791
+ runnerCtx.diagnostic.idleSec = void 0;
116792
+ ctx.onTurnRecovered?.();
116792
116793
  return turn;
116793
116794
  };
116794
116795
  const result = await attempt(text);
@@ -196276,6 +196277,43 @@ function resolveCwd(cwd) {
196276
196277
  if (isAbsolute2(cwd)) return cwd;
196277
196278
  return workspace ? resolve2(workspace, cwd) : cwd;
196278
196279
  }
196280
+ var DISPATCH_PAYLOAD_KEY = '"~pullfrog"';
196281
+ function sliceBalancedObject(text, open) {
196282
+ let depth = 0;
196283
+ let inString = false;
196284
+ for (let i2 = open; i2 < text.length; i2++) {
196285
+ const char = text[i2];
196286
+ if (inString) {
196287
+ if (char === "\\") i2++;
196288
+ else if (char === '"') inString = false;
196289
+ continue;
196290
+ }
196291
+ if (char === '"') inString = true;
196292
+ else if (char === "{") depth++;
196293
+ else if (char === "}" && --depth === 0) return text.slice(open, i2 + 1);
196294
+ }
196295
+ return void 0;
196296
+ }
196297
+ function findEmbeddedDispatchPayload(text) {
196298
+ const limit = text.lastIndexOf(DISPATCH_PAYLOAD_KEY);
196299
+ if (limit === -1) return void 0;
196300
+ for (let open = text.indexOf("{"); open !== -1 && open < limit; open = text.indexOf("{", open + 1)) {
196301
+ const candidate = sliceBalancedObject(text, open);
196302
+ if (!candidate) continue;
196303
+ try {
196304
+ const parsed2 = JSON.parse(candidate);
196305
+ if (JsonPayload.allows(parsed2)) return parsed2;
196306
+ } catch {
196307
+ }
196308
+ }
196309
+ return void 0;
196310
+ }
196311
+ function assertDispatchPayloadIntact(promptInput) {
196312
+ if (!findEmbeddedDispatchPayload(promptInput)) return;
196313
+ throw new Error(
196314
+ "this workflow wraps `${{ inputs.prompt }}` in other text, so Pullfrog's dispatch payload can't be read and the run would lose its pull request context, its progress comment, and its standing instructions. in `.github/workflows/pullfrog.yml`, set `prompt:` to exactly `${{ inputs.prompt }}`, and move your own preamble into Standing instructions in the Pullfrog console."
196315
+ );
196316
+ }
196279
196317
  function resolvePromptInput() {
196280
196318
  const promptInput = core7.getInput("prompt");
196281
196319
  const promptFile = core7.getInput("prompt_file");
@@ -196292,9 +196330,9 @@ function resolvePromptInput() {
196292
196330
  try {
196293
196331
  parsed2 = JSON.parse(promptInput);
196294
196332
  } catch {
196295
- return promptInput;
196296
196333
  }
196297
196334
  if (!parsed2 || typeof parsed2 !== "object" || !("~pullfrog" in parsed2)) {
196335
+ assertDispatchPayloadIntact(promptInput);
196298
196336
  return promptInput;
196299
196337
  }
196300
196338
  const jsonPayload = JsonPayload.assert(parsed2);
@@ -197472,7 +197510,7 @@ ${body}`, comment: body };
197472
197510
  ${body}`, comment: body };
197473
197511
  }
197474
197512
  if (hangBody) {
197475
- const explained = input.agentDiagnostic?.lastProviderError !== void 0;
197513
+ const explained = input.agentDiagnostic?.lastProviderError !== void 0 || input.agentDiagnostic?.sawModelOutput === false;
197476
197514
  return {
197477
197515
  summary: `### \u274C Pullfrog failed
197478
197516
 
@@ -199265,7 +199303,7 @@ async function runCli4(input) {
199265
199303
  }
199266
199304
 
199267
199305
  // cli.ts
199268
- var VERSION10 = "0.1.62";
199306
+ var VERSION10 = "0.1.63";
199269
199307
  var bin = basename2(process.argv[1] || "");
199270
199308
  var PROG = bin === "pf" || bin === "pullfrog" ? bin : "pullfrog";
199271
199309
  var rawArgs = process.argv.slice(2);
package/dist/index.js CHANGED
@@ -105191,7 +105191,7 @@ var import_semver = __toESM(require_semver2(), 1);
105191
105191
  // package.json
105192
105192
  var package_default = {
105193
105193
  name: "pullfrog",
105194
- version: "0.1.62",
105194
+ version: "0.1.63",
105195
105195
  type: "module",
105196
105196
  bin: {
105197
105197
  pullfrog: "dist/cli.mjs",
@@ -114117,7 +114117,7 @@ function isModelOutput(ctx, part) {
114117
114117
  async function dispatchEvent(ctx, event) {
114118
114118
  if (event.type === "message.part.updated") {
114119
114119
  ctx.lastEventAt = performance7.now();
114120
- if (isModelOutput(ctx, event.properties.part)) ctx.sawModelOutput = true;
114120
+ if (isModelOutput(ctx, event.properties.part)) ctx.diagnostic.sawModelOutput = true;
114121
114121
  await onPartUpdated(ctx, event.properties.part);
114122
114122
  return;
114123
114123
  }
@@ -114263,7 +114263,8 @@ async function runPromptTurn(ctx, params) {
114263
114263
  const turn = ctx.currentTurn;
114264
114264
  const part = { type: "text", text: params.text };
114265
114265
  ctx.promptMessageID = void 0;
114266
- ctx.sawModelOutput = false;
114266
+ ctx.diagnostic.sawModelOutput = false;
114267
+ ctx.diagnostic.idleSec = void 0;
114267
114268
  let assistant;
114268
114269
  let returnedParts;
114269
114270
  let networkError = null;
@@ -114323,7 +114324,7 @@ async function runPromptTurn(ctx, params) {
114323
114324
  usage
114324
114325
  };
114325
114326
  }
114326
- if (!ctx.sawModelOutput && finalText.trim().length === 0 && !usage) {
114327
+ if (!ctx.diagnostic.sawModelOutput && finalText.trim().length === 0 && !usage) {
114327
114328
  return {
114328
114329
  success: false,
114329
114330
  output: finalText,
@@ -114448,19 +114449,18 @@ function startInnerActivityWatchdog(params) {
114448
114449
  const id = setInterval(() => {
114449
114450
  if (fired) return;
114450
114451
  const idleMs = performance7.now() - params.ctx.lastEventAt;
114451
- const compiledMs = params.ctx.sawModelOutput ? params.timeoutMs : AGENT_FIRST_EVENT_TIMEOUT_MS;
114452
+ const compiledMs = params.ctx.diagnostic.sawModelOutput ? params.timeoutMs : AGENT_FIRST_EVENT_TIMEOUT_MS;
114452
114453
  const budgetMs = everFired ? compiledMs : watchdogBudgetMs(
114453
114454
  compiledMs,
114454
- params.ctx.sawModelOutput ? "PULLFROG_E2E_ACTIVITY_TIMEOUT_MS" : "PULLFROG_E2E_FIRST_EVENT_TIMEOUT_MS"
114455
+ params.ctx.diagnostic.sawModelOutput ? "PULLFROG_E2E_ACTIVITY_TIMEOUT_MS" : "PULLFROG_E2E_FIRST_EVENT_TIMEOUT_MS"
114455
114456
  );
114456
114457
  if (idleMs <= budgetMs) return;
114457
114458
  fired = true;
114458
114459
  everFired = true;
114459
114460
  const idleSec = Math.round(idleMs / 1e3);
114460
114461
  params.ctx.diagnostic.idleSec = idleSec;
114461
- params.ctx.diagnostic.sawModelOutput = params.ctx.sawModelOutput;
114462
114462
  log.info(
114463
- 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`
114463
+ params.ctx.diagnostic.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`
114464
114464
  );
114465
114465
  params.abortTurn();
114466
114466
  try {
@@ -114604,7 +114604,6 @@ var opencode = agent({
114604
114604
  currentTurn: null,
114605
114605
  eventCount: 0,
114606
114606
  lastEventAt: performance7.now(),
114607
- sawModelOutput: false,
114608
114607
  promptMessageID: void 0,
114609
114608
  taskDispatchByCallID: /* @__PURE__ */ new Map(),
114610
114609
  loggedToolCallIDs: /* @__PURE__ */ new Set(),
@@ -114668,7 +114667,9 @@ var opencode = agent({
114668
114667
  );
114669
114668
  };
114670
114669
  const standDownIfRecovered = (turn) => {
114671
- if (watchdog.firedThisTurn() && turn.success) ctx.onTurnRecovered?.();
114670
+ if (!watchdog.firedThisTurn() || !turn.success) return turn;
114671
+ runnerCtx.diagnostic.idleSec = void 0;
114672
+ ctx.onTurnRecovered?.();
114672
114673
  return turn;
114673
114674
  };
114674
114675
  const result = await attempt(text);
@@ -194156,6 +194157,43 @@ function resolveCwd(cwd) {
194156
194157
  if (isAbsolute2(cwd)) return cwd;
194157
194158
  return workspace ? resolve2(workspace, cwd) : cwd;
194158
194159
  }
194160
+ var DISPATCH_PAYLOAD_KEY = '"~pullfrog"';
194161
+ function sliceBalancedObject(text, open) {
194162
+ let depth = 0;
194163
+ let inString = false;
194164
+ for (let i = open; i < text.length; i++) {
194165
+ const char = text[i];
194166
+ if (inString) {
194167
+ if (char === "\\") i++;
194168
+ else if (char === '"') inString = false;
194169
+ continue;
194170
+ }
194171
+ if (char === '"') inString = true;
194172
+ else if (char === "{") depth++;
194173
+ else if (char === "}" && --depth === 0) return text.slice(open, i + 1);
194174
+ }
194175
+ return void 0;
194176
+ }
194177
+ function findEmbeddedDispatchPayload(text) {
194178
+ const limit = text.lastIndexOf(DISPATCH_PAYLOAD_KEY);
194179
+ if (limit === -1) return void 0;
194180
+ for (let open = text.indexOf("{"); open !== -1 && open < limit; open = text.indexOf("{", open + 1)) {
194181
+ const candidate = sliceBalancedObject(text, open);
194182
+ if (!candidate) continue;
194183
+ try {
194184
+ const parsed2 = JSON.parse(candidate);
194185
+ if (JsonPayload.allows(parsed2)) return parsed2;
194186
+ } catch {
194187
+ }
194188
+ }
194189
+ return void 0;
194190
+ }
194191
+ function assertDispatchPayloadIntact(promptInput) {
194192
+ if (!findEmbeddedDispatchPayload(promptInput)) return;
194193
+ throw new Error(
194194
+ "this workflow wraps `${{ inputs.prompt }}` in other text, so Pullfrog's dispatch payload can't be read and the run would lose its pull request context, its progress comment, and its standing instructions. in `.github/workflows/pullfrog.yml`, set `prompt:` to exactly `${{ inputs.prompt }}`, and move your own preamble into Standing instructions in the Pullfrog console."
194195
+ );
194196
+ }
194159
194197
  function resolvePromptInput() {
194160
194198
  const promptInput = core7.getInput("prompt");
194161
194199
  const promptFile = core7.getInput("prompt_file");
@@ -194172,9 +194210,9 @@ function resolvePromptInput() {
194172
194210
  try {
194173
194211
  parsed2 = JSON.parse(promptInput);
194174
194212
  } catch {
194175
- return promptInput;
194176
194213
  }
194177
194214
  if (!parsed2 || typeof parsed2 !== "object" || !("~pullfrog" in parsed2)) {
194215
+ assertDispatchPayloadIntact(promptInput);
194178
194216
  return promptInput;
194179
194217
  }
194180
194218
  const jsonPayload = JsonPayload.assert(parsed2);
@@ -195352,7 +195390,7 @@ ${body}`, comment: body };
195352
195390
  ${body}`, comment: body };
195353
195391
  }
195354
195392
  if (hangBody) {
195355
- const explained = input.agentDiagnostic?.lastProviderError !== void 0;
195393
+ const explained = input.agentDiagnostic?.lastProviderError !== void 0 || input.agentDiagnostic?.sawModelOutput === false;
195356
195394
  return {
195357
195395
  summary: `### \u274C Pullfrog failed
195358
195396
 
@@ -45,12 +45,12 @@
45
45
  * `"activity timeout"` or `"agent still pending"` AND none of the
46
46
  * above matched. The harness keeps structured diagnostic state on
47
47
  * `toolState.agentDiagnostic`; `formatAgentHangBody` renders that into
48
- * the job summary. The PR comment instead collapses to a one-line
49
- * `**Run failed.** [View the logs →]` the watchdog jargon, event
50
- * counts, and benign stderr tail are operator-grade detail that only
51
- * alarm the average user. The one exception is a hang masking billing
52
- * exhaustion (#778), where `formatAgentHangBody` emits an actionable
53
- * top-up CTA that the comment keeps verbatim.
48
+ * the job summary. The PR comment keeps that same body whenever the hang
49
+ * is EXPLAINED a classified provider error (#778, #1183) or a provider
50
+ * that never returned a first token and otherwise collapses to a
51
+ * one-line `**Run failed.** [View the logs →]`, because watchdog jargon,
52
+ * event counts and a benign stderr tail are operator-grade detail that
53
+ * only alarm the average user.
54
54
  *
55
55
  * 6. Default — the job summary gets a plain-English lead sentence plus the
56
56
  * raw error in a fenced code block under the `### ❌ Pullfrog failed`
@@ -59,7 +59,7 @@
59
59
  *
60
60
  * Net: the actionable classifications (billing, API-key, model-not-found,
61
61
  * no-provider-available, context-overflow, transient-upstream) render identical
62
- * bodies on both surfaces; the non-actionable ones (hang,
62
+ * bodies on both surfaces; the non-actionable ones (unexplained hang,
63
63
  * generic) keep the forensics in the Actions job summary and show a calm
64
64
  * one-liner in the PR comment, whose footer already carries Pullfrog
65
65
  * branding + rerun links.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pullfrog",
3
- "version": "0.1.62",
3
+ "version": "0.1.63",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "pullfrog": "dist/cli.mjs",