comfyui-mcp 0.52.83 → 0.52.84

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.
@@ -5693,10 +5693,22 @@ function isRetryableRunToNodeStampRace(res, rejection) {
5693
5693
  return (/workflow graph CHANGED after the run was queued/i.test(text) &&
5694
5694
  /Nothing was queued/i.test(text));
5695
5695
  }
5696
+ /**
5697
+ * #2120 — an older panel can decorate a fixed seed while it builds the deferred
5698
+ * run prompt. That can make the same scoped run hit the certified stamp race more
5699
+ * than once. The panel still has to identify the changed entry as a seed; this is
5700
+ * deliberately narrower than allowing an arbitrary third dispatch.
5701
+ */
5702
+ const MAX_SCOPED_STAMP_RACE_RETRIES = 2;
5703
+ function isSeedRunToNodeStampRace(res, rejection) {
5704
+ return (isRetryableRunToNodeStampRace(res, rejection) &&
5705
+ /\bdiffering\b[\s\S]{0,160}\bseed\b/i.test(toolResultText(rejection)));
5706
+ }
5696
5707
  export const __panelRunTestHooks = {
5697
5708
  detectRunRejection,
5698
5709
  formatRunRejection,
5699
5710
  isRetryableRunToNodeStampRace,
5711
+ isSeedRunToNodeStampRace,
5700
5712
  describeDroppedOutputs,
5701
5713
  };
5702
5714
  /** Drop a trailing .json (case-insensitive) so filename/path forms compare equal. */
@@ -13276,15 +13288,25 @@ export function buildPanelToolDefs() {
13276
13288
  // "you'll be notified automatically" guidance. Only a genuine queue
13277
13289
  // gets the anti-poll note below.
13278
13290
  let rejection = detectRunRejection(res);
13279
- // #772 — ONE re-issue, and only for the scoped-run stamp race the panel
13280
- // explicitly certifies as not-queued. Gated on OUR OWN args (this really was a
13281
- // scoped run) and on the panel having ANSWERED, so an unknown outcome is never
13282
- // retried. Exactly once: a second race is surfaced, never re-raced.
13291
+ // #772/#2120 — re-issue only the scoped-run stamp race the panel explicitly
13292
+ // certifies as not-queued. Gated on OUR OWN args (this really was a scoped run)
13293
+ // and on the panel having ANSWERED, so an unknown outcome is never retried.
13294
+ // The ordinary race gets one re-issue. A repeated race gets ONE additional
13295
+ // chance only when the panel names a differing seed entry — the fixed-seed
13296
+ // decoration race reported in #2120. Every attempt still passes the panel's
13297
+ // graph-stamp check independently; this is not a bypass or a blind mutation
13298
+ // retry.
13283
13299
  let scopeRebuilt = false;
13284
- if (rejection &&
13300
+ let scopeRetryCount = 0;
13301
+ while (rejection &&
13285
13302
  typeof args.to_node_id === "number" &&
13286
- isRetryableRunToNodeStampRace(res, rejection)) {
13303
+ isRetryableRunToNodeStampRace(res, rejection) &&
13304
+ (scopeRetryCount === 0 ||
13305
+ (scopeRetryCount < MAX_SCOPED_STAMP_RACE_RETRIES &&
13306
+ isSeedRunToNodeStampRace(res, rejection)))) {
13287
13307
  scopeRebuilt = true;
13308
+ scopeRetryCount += 1;
13309
+ let retryResultReceived = false;
13288
13310
  try {
13289
13311
  // #1050 — SETTLE before re-issuing. The panel refuses this race when the
13290
13312
  // graph changed between dispatch and apply, and re-dispatching in the
@@ -13306,6 +13328,7 @@ export function buildPanelToolDefs() {
13306
13328
  // prompt id rather than allowing that stale arm to be synthesized (#2021).
13307
13329
  runDispatchedAt = Date.now();
13308
13330
  res = await ctx.call(runCmd, 20000, observeRunRid);
13331
+ retryResultReceived = true;
13309
13332
  // #1175 — the re-issue can miss its window exactly as the first
13310
13333
  // dispatch can, and this is the one whose outcome is genuinely open
13311
13334
  // (the first was CERTIFIED to have queued nothing). Reconcile it on
@@ -13314,17 +13337,37 @@ export function buildPanelToolDefs() {
13314
13337
  rejection = detectRunRejection(res);
13315
13338
  }
13316
13339
  catch (err) {
13340
+ // A bridge/late-receipt failure after ctx.call already returned must not
13341
+ // erase the retry's own result (notably its retry_of token). Preserve it
13342
+ // verbatim and stop: no further dispatch is justified by a failed local
13343
+ // reconciliation step.
13344
+ if (retryResultReceived) {
13345
+ return appendToolResultText(res, `\n\n(Dispatch history: the retry dispatch returned the result above, but local ` +
13346
+ `late-ack reconciliation failed before this handler could finish ` +
13347
+ `interpreting it. That result is preserved and no further dispatch was ` +
13348
+ `made. Do not infer an additional queue entry from the reconciliation ` +
13349
+ `failure alone. (${err instanceof Error ? err.message : String(err)})`);
13350
+ }
13317
13351
  // ctx.call settles every panel/transport failure into a ToolResult and does
13318
13352
  // not throw today — but this await is the one point where a throw would
13319
- // DESTROY evidence we already hold: that the first dispatch was certified
13320
- // not-queued, and that a second dispatch went out whose outcome is now
13321
- // unknown. Propagating the raw throw would lose both and invite a third
13322
- // dispatch. Report what we observed, claim nothing we did not.
13323
- return fail(`panel_run re-issued the scoped run after the panel's certified run-to-node ` +
13324
- `stamp race, and that SECOND dispatch failed before any reply could be read — ` +
13325
- `its outcome is UNKNOWN and it may have queued a render. The FIRST dispatch was ` +
13326
- `certified by the panel to have queued nothing. Do NOT re-run blindly: check the ` +
13327
- `queue (action:"list") or get_history before deciding. (${err instanceof Error ? err.message : String(err)})`);
13353
+ // DESTROY evidence we already hold: that every prior dispatch was
13354
+ // certified not-queued, and that this dispatch went out whose outcome is
13355
+ // now unknown. Propagating the raw throw would lose both and invite a
13356
+ // blind dispatch. Report what we observed, claim nothing we did not.
13357
+ if (scopeRetryCount === 1) {
13358
+ return fail(`panel_run re-issued the scoped run after the panel's certified run-to-node ` +
13359
+ `stamp race, and that SECOND dispatch failed before any reply could be read ` +
13360
+ `its outcome is UNKNOWN and it may have queued a render. The FIRST dispatch was ` +
13361
+ `certified by the panel to have queued nothing. Do NOT re-run blindly: check the ` +
13362
+ `queue (action:"list") or get_history before deciding. (${err instanceof Error ? err.message : String(err)})`);
13363
+ }
13364
+ return fail(`panel_run re-issued the scoped run ${scopeRetryCount} times after the panel ` +
13365
+ `certified each prior run-to-node stamp race as not-queued, and the ` +
13366
+ `dispatch ${scopeRetryCount + 1} failed before any reply could be read — ` +
13367
+ `its outcome is UNKNOWN and it may have queued a render. The first ` +
13368
+ `${scopeRetryCount} dispatches were certified by the panel to have queued ` +
13369
+ `nothing. Do NOT re-run blindly: check the queue (action:"list") or ` +
13370
+ `get_history before deciding. (${err instanceof Error ? err.message : String(err)})`);
13328
13371
  }
13329
13372
  }
13330
13373
  if (rejection) {
@@ -13341,14 +13384,24 @@ export function buildPanelToolDefs() {
13341
13384
  // cannot tell whether these are one event or two.
13342
13385
  if (!scopeRebuilt)
13343
13386
  return lateAckNote ? appendToolResultText(rejection, lateAckNote) : rejection;
13344
- return appendToolResultText(rejection, `\n\n(Dispatch history: the first graph_run was refused by the panel's run-to-node ` +
13345
- `graph-stamp race, which CERTIFIED that nothing was queued, so the scoped run was ` +
13346
- `re-issued exactly once after a short pause to let pending graph edits land ` +
13347
- `(#1050). The failure above is that SECOND dispatch; it was not retried again. ` +
13348
- `Judge whether anything was queued from that message alone the first dispatch ` +
13349
- `definitely queued nothing. Racing AGAIN after the pause means the graph is still ` +
13350
- `changing under the run: let the canvas settle, then re-run.)` +
13351
- lateAckNote);
13387
+ return appendToolResultText(rejection, scopeRetryCount === 1
13388
+ ? `\n\n(Dispatch history: the first graph_run was refused by the panel's run-to-node ` +
13389
+ `graph-stamp race, which CERTIFIED that nothing was queued, so the scoped run was ` +
13390
+ `re-issued exactly once after a short pause to let pending graph edits land ` +
13391
+ `(#1050). The failure above is that SECOND dispatch; it was not retried again. ` +
13392
+ `Judge whether anything was queued from that message alone the first dispatch ` +
13393
+ `definitely queued nothing. Racing AGAIN after the pause means the graph is still ` +
13394
+ `changing under the run: let the canvas settle, then re-run.)` +
13395
+ lateAckNote
13396
+ : `\n\n(Dispatch history: the first ${scopeRetryCount} graph_run dispatches were each ` +
13397
+ `refused by the panel's run-to-node graph-stamp race, and each CERTIFIED ` +
13398
+ `that nothing was queued. The scoped run was re-issued ${scopeRetryCount} ` +
13399
+ `times after short pauses to let pending graph edits land (#1050). The ` +
13400
+ `failure above is dispatch ${scopeRetryCount + 1}; it was not retried again. ` +
13401
+ `Judge whether anything was queued from that message alone — the first ` +
13402
+ `${scopeRetryCount} dispatches definitely queued nothing. Let the canvas ` +
13403
+ `settle, then re-run.)` +
13404
+ lateAckNote);
13352
13405
  }
13353
13406
  // Attribute this genuine queue to ourselves so a later panel_run in the
13354
13407
  // same batch recognizes the in-flight job as our own and doesn't false-warn
@@ -13479,22 +13532,28 @@ export function buildPanelToolDefs() {
13479
13532
  // clear_pending remedy that would wipe the user's whole batch (#559). A
13480
13533
  // genuinely stalled render is caught by the turn-start stall notice, which
13481
13534
  // does the staleness gating and keeps the interrupt guidance.
13482
- // #772 — never report a dispatch history the caller did not see. When the scoped
13483
- // run only queued on the SECOND dispatch, say so, so the agent does not read the
13484
- // earlier refusal (if it surfaces in a log) as a separate queued job.
13535
+ // #772/#2120 — never report a dispatch history the caller did not see. Say
13536
+ // exactly which earlier dispatches were certified clean, so the agent does
13537
+ // not read a refusal (if it surfaces in a log) as a separate queued job.
13485
13538
  //
13486
13539
  // It says only what was OBSERVED (codex gate). An earlier draft said "exactly one
13487
13540
  // render was queued" — but `batch_count` is "times to queue", so a successful
13488
13541
  // dispatch can enqueue several, and the panel reports one prompt id, not a count.
13489
- // The certified fact is about DISPATCHES, not renders: the first queued nothing,
13490
- // so everything in the queue came from the second. State that and stop.
13491
- const retryNote = scopeRebuilt
13492
- ? "\n\n[NOTE] The first dispatch of this scoped run was refused by the panel's run-to-node " +
13493
- "graph-stamp race (the graph changed between dispatch and apply); the panel certified that " +
13494
- "nothing was queued, so the scope was rebuilt and re-issued once, and THAT is the run above. " +
13495
- "The first dispatch contributed NOTHING to the queue, so whatever this run queued was " +
13496
- "queued once, not twice."
13497
- : "";
13542
+ // The certified fact is about DISPATCHES, not renders: the prior dispatches
13543
+ // queued nothing, so everything in the queue came from the final dispatch.
13544
+ const retryNote = scopeRetryCount === 0
13545
+ ? ""
13546
+ : scopeRetryCount === 1
13547
+ ? "\n\n[NOTE] The first dispatch of this scoped run was refused by the panel's run-to-node " +
13548
+ "graph-stamp race (the graph changed between dispatch and apply); the panel certified that " +
13549
+ "nothing was queued, so the scope was rebuilt and re-issued once, and THAT is the run above. " +
13550
+ "The first dispatch contributed NOTHING to the queue, so whatever this run queued was " +
13551
+ "queued once, not twice."
13552
+ : `\n\n[NOTE] The first ${scopeRetryCount} dispatches of this scoped run were refused by the ` +
13553
+ `panel's run-to-node graph-stamp race (the graph changed between dispatch and apply), ` +
13554
+ `and each was certified to have queued NOTHING. The scope was rebuilt and re-issued ` +
13555
+ `${scopeRetryCount} times, and THAT is the run above. Whatever this run queued came ` +
13556
+ `from the final dispatch; the earlier dispatches contributed NOTHING to the queue.`;
13498
13557
  let warn = "";
13499
13558
  if (pre.connected && pre.running) {
13500
13559
  const pending = Math.max(0, pre.queueDepth - 1);