comfyui-mcp 0.50.8 → 0.50.9

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.
@@ -621,6 +621,33 @@ function isTransientReconnectError(err) {
621
621
  const msg = err instanceof Error ? err.message : String(err ?? "");
622
622
  return /no connected tab|genuinely gone|is not open|Failed to fetch|Panel not reachable|ECONNRESET|socket hang up|premature close|other side closed|ECONNABORTED|EPIPE/i.test(msg);
623
623
  }
624
+ /**
625
+ * #1027 — the panel's WORKFLOW-SWITCH critical section, which is retryable by
626
+ * construction and was not being retried.
627
+ *
628
+ * While a workflow switch/reload is in flight the panel refuses every graph and
629
+ * workflow executor rather than queueing it, because refusing cannot reorder or
630
+ * double-apply. Its own words:
631
+ *
632
+ * the panel is switching/refreshing "<key>" right now, so "<cmd>" was NOT
633
+ * applied — nothing changed. Retry in a moment.
634
+ *
635
+ * Two facts make this the safest retry in the file: the panel STATES nothing was
636
+ * applied, and the section lasts a fraction of a second. Yet none of that text
637
+ * matched the transient classifier, so a read issued right after
638
+ * panel_open_workflow surfaced the refusal to the agent instead of waiting the
639
+ * ~400ms the panel asked for. A reporter driving several tabs read-only hit it
640
+ * repeatedly, and read the resulting instance-mismatch errors as a wedge.
641
+ *
642
+ * TEXT-matched, deliberately, unlike #1001's typed marker: this error is minted
643
+ * in the browser and arrives over the wire, so there is no object identity to
644
+ * carry a symbol across. The phrasing is distinctive enough to key on, and the
645
+ * retry it enables is bounded to RETRY-SAFE commands either way.
646
+ */
647
+ function isWorkflowSwitchGuardRefusal(err) {
648
+ const msg = err instanceof Error ? err.message : String(err ?? "");
649
+ return /panel is switching\/refreshing|panel is switching or reloading/i.test(msg);
650
+ }
624
651
  let retrySettleMsOverride = null;
625
652
  /** Short pause before the single post-drop retry, letting the replacement tab
626
653
  * finish its reconnect hello so ensureReachable can resolve it. Test-overridable. */
@@ -3956,13 +3983,31 @@ export function makePanelToolCtx(bridge, tabId, workflowTargets) {
3956
3983
  // For idempotent commands, settle briefly, rebind onto the now-live tab, and
3957
3984
  // retry ONE time before surfacing an error (#278/#310/#332/#481). Mutating
3958
3985
  // edits are excluded from RETRY_SAFE_CMDS, so they never double-apply.
3959
- if (isRetrySafeCmd(cmd) && isTransientReconnectError(err)) {
3986
+ // #1027 the workflow-switch critical section is retried on the same path.
3987
+ // The panel refuses during it, states that nothing was applied, and asks
3988
+ // for a retry in a moment; the section lasts a fraction of a second, which
3989
+ // is what retrySettleMs already waits. Still gated on RETRY-SAFE commands,
3990
+ // so a mutation is never re-issued on our own initiative.
3991
+ if (isRetrySafeCmd(cmd) && (isTransientReconnectError(err) || isWorkflowSwitchGuardRefusal(err))) {
3960
3992
  try {
3961
3993
  await sleep(retrySettleMs());
3962
3994
  ensureReachable(); // rebinds a current-mode session onto the reconnected tab
3963
3995
  return ok(await sendRouted(cmd, timeoutMs, observeRid));
3964
3996
  }
3965
3997
  catch (err2) {
3998
+ // #1027 — a switch STILL in progress is not a reconnect, and saying so
3999
+ // would be the #1001 mistake again: the tab is connected and healthy,
4000
+ // it is simply mid-switch. Name the actual state and the actual wait.
4001
+ if (isWorkflowSwitchGuardRefusal(err2)) {
4002
+ const name = typeof cmd.cmd === "string" ? cmd.cmd : "panel command";
4003
+ return fail(`${name} was NOT applied — nothing changed. The panel is still switching or ` +
4004
+ `reloading the workflow on the canvas, which it refuses commands during so a ` +
4005
+ `command cannot land on the wrong graph. The tab is connected and this is not a ` +
4006
+ `reconnect: it normally clears in well under a second, so simply retry. If a ` +
4007
+ `switch appears stuck, check the canvas — a load dialog or an unsaved-changes ` +
4008
+ `prompt can hold it open awaiting the user. ` +
4009
+ `(${err2 instanceof Error ? err2.message : String(err2)})`);
4010
+ }
3966
4011
  // The retry also failed — surface an actionable reconnecting status rather
3967
4012
  // than a bare transport error (#332), while still failing honestly.
3968
4013
  if (isTransientReconnectError(err2)) {