comfyui-mcp 0.51.26 → 0.51.28

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.
@@ -3218,6 +3218,49 @@ WHY THIS READ WAS NEEDED AT ALL: this session's panel is ${v.version}, and a ` +
3218
3218
  * happening here.
3219
3219
  */
3220
3220
  const FENCE_CORROBORATION_RECHECK_STEPS_MS = [400, 900, 1600];
3221
+ /**
3222
+ * #1478 — NAME THE CAUSE OF A MISMATCH THE LOAD MAY HAVE CREATED.
3223
+ *
3224
+ * An API-format `graph_load` can re-mint the canvas workflow instance, which leaves this
3225
+ * session's fence naming the old one — so the very next `panel_graph_outline` is refused
3226
+ * with `workflow instance mismatch`. The reporter hit that twice, deterministically. The
3227
+ * refusal then says "NO PANEL COMMAND CLAIMED IT", pointing at "the user switched tabs"
3228
+ * when a panel command one call earlier is the actual cause.
3229
+ *
3230
+ * WHAT THIS DELIBERATELY DOES NOT DO — and four review rounds are the reason:
3231
+ *
3232
+ * 1. It does NOT auto-repair the fence. The obvious fix runs the same re-derivation the
3233
+ * documented recovery runs, and that adopts WHATEVER IS ACTIVE NOW with no tie to the
3234
+ * load: a user switching canvases in the window would stamp the session to a different
3235
+ * workflow and the next edit would land on the wrong graph. `panel_set_workflow_target`
3236
+ * is sound doing this only because the user explicitly asked to follow what is live.
3237
+ * 2. It does NOT claim the fence IS stale. Every categorical version of that sentence was
3238
+ * falsified: a UI-format load with an active workflow passes `__cmcpKeepInstance: true`
3239
+ * and preserves the instance outright; and a second API load into the same
3240
+ * already-active `graph_load.json` workflow reuses that object, so its uuid — which is
3241
+ * object-keyed — does not change either. The reply carries nothing that separates
3242
+ * "re-minted" from "reused", so asserting staleness is a guess dressed as a fact.
3243
+ *
3244
+ * So it states the CONDITIONAL, which is true on every path: this kind of load can move
3245
+ * the instance, here is the symptom, here is the cause, here is the one call that clears
3246
+ * it. A reader who is not refused ignores it; a reader who is refused stops hunting a tab
3247
+ * switch that never happened.
3248
+ *
3249
+ * The categorical version needs the panel's help: give `graph_load`'s reply a
3250
+ * `workflow_uuid`, as #762/#800 did for `workflow_new` / `workflow_save`, and the load can
3251
+ * then compare it against the fence and say exactly what happened — or claim the identity
3252
+ * it created outright, which is race-proof and refusal-proof and is why those two work
3253
+ * that way.
3254
+ */
3255
+ function noteStaleFenceAfterLoad() {
3256
+ return (`
3257
+
3258
+ NOTE: an API-format load CAN re-mint the canvas workflow instance. If your next ` +
3259
+ `graph command is refused with "workflow instance mismatch", that is why — the cause is ` +
3260
+ `this load, NOT the user switching tabs (the refusal's own text suggests otherwise). ` +
3261
+ `Clear it with panel_set_workflow_target({mode:"current"}), which re-derives the fence ` +
3262
+ `from the live canvas, then retry. If the next command is not refused, nothing needs doing.`);
3263
+ }
3221
3264
  async function rebindWorkflowFence(ctx) {
3222
3265
  const tabAtStart = ctx.tabId;
3223
3266
  let before = currentWorkflowFence(ctx);
@@ -7509,7 +7552,25 @@ export function buildPanelToolDefs() {
7509
7552
  throw new Error("Provide one of `pack` (a bundled pack name), `path` (a workflow .json on disk), or `graph` (a UI workflow).");
7510
7553
  }
7511
7554
  // Generous timeout — loading a large graph onto the live canvas can take a moment.
7512
- return await ctx.call({ cmd: "graph_load", graph: data }, 30000);
7555
+ const loaded = await ctx.call({ cmd: "graph_load", graph: data }, 30000);
7556
+ if (loaded.isError)
7557
+ return loaded;
7558
+ // Keyed on the reply SAYING the graph was replaced, not merely on the call not
7559
+ // erroring (codex r2). A non-error envelope that reports `loaded:false` did not
7560
+ // replace anything, and telling that caller their fence is stale would be a
7561
+ // fabricated consequence of a load that never happened — the same
7562
+ // unmeasured-claim defect this note exists to remove.
7563
+ // ONLY THE API-FORMAT LOAD MINTS A NEW INSTANCE (codex r3, checked in the panel).
7564
+ // The UI-format path passes `__cmcpKeepInstance: true` whenever a workflow is
7565
+ // active, deliberately PRESERVING the instance so the agent's own follow-up
7566
+ // commands are not rejected — so a note claiming the fence went stale would be
7567
+ // false for the commonest load. The API path (`app.loadApiJson`) passes no such
7568
+ // option and does re-mint, and it is the path the reporter hit: their reply reads
7569
+ // `format:"api"`. That field is present only on this branch, which makes it the
7570
+ // signal rather than an inference.
7571
+ const reply = parseToolResultJson(loaded);
7572
+ const remintedInstance = reply?.loaded === true && reply?.format === "api";
7573
+ return remintedInstance ? appendNote(loaded, noteStaleFenceAfterLoad()) : loaded;
7513
7574
  }
7514
7575
  catch (err) {
7515
7576
  return fail(err);