comfyui-mcp 0.50.97 → 0.50.98
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.
|
@@ -697,6 +697,22 @@ function isWorkflowSwitchGuardRefusal(err) {
|
|
|
697
697
|
const msg = err instanceof Error ? err.message : String(err ?? "");
|
|
698
698
|
return /panel is switching\/refreshing|panel is switching or reloading/i.test(msg);
|
|
699
699
|
}
|
|
700
|
+
/**
|
|
701
|
+
* #1330 — the panel's workflow-instance fence refused this command.
|
|
702
|
+
*
|
|
703
|
+
* Distinct from every other refusal here in one way that matters: the panel checks the
|
|
704
|
+
* fence BEFORE the handler runs, so "Nothing was applied" is structural, not a claim.
|
|
705
|
+
* That is what makes it safe to tell a caller to retry — and it is why this is worth
|
|
706
|
+
* separating from the transport failures around it, where the outcome is unknown.
|
|
707
|
+
*
|
|
708
|
+
* Matched on the leading token the panel preserves deliberately for exactly this
|
|
709
|
+
* purpose (see workflowInstanceMismatchMessage: "the leading `workflow instance
|
|
710
|
+
* mismatch:` token is preserved deliberately").
|
|
711
|
+
*/
|
|
712
|
+
function isWorkflowInstanceMismatch(err) {
|
|
713
|
+
const msg = err instanceof Error ? err.message : String(err ?? "");
|
|
714
|
+
return /workflow instance mismatch/i.test(msg);
|
|
715
|
+
}
|
|
700
716
|
let retrySettleMsOverride = null;
|
|
701
717
|
/** Short pause before the single post-drop retry, letting the replacement tab
|
|
702
718
|
* finish its reconnect hello so ensureReachable can resolve it. Test-overridable. */
|
|
@@ -4916,6 +4932,64 @@ export function makePanelToolCtx(bridge, tabId, workflowTargets) {
|
|
|
4916
4932
|
// sent agents into a futile retry/rebind loop that contradicted the embedded
|
|
4917
4933
|
// guidance. Key on the TYPED marker and surface the cause verbatim instead; it
|
|
4918
4934
|
// already names the real recovery (update + restart + browser hard-refresh).
|
|
4935
|
+
// #1330 — CORROBORATE A FENCE MISMATCH INSTEAD OF LETTING IT REPEAT.
|
|
4936
|
+
//
|
|
4937
|
+
// The reporter built 12 nodes successfully, then EVERY panel_connect was refused
|
|
4938
|
+
// on an instance mismatch. Calling panel_set_workflow_target({mode:"current"})
|
|
4939
|
+
// straight afterwards reported `already_current` naming the SAME uuid the command
|
|
4940
|
+
// had been issued for, and the retried connects all succeeded. So the mismatch was
|
|
4941
|
+
// transient and self-correcting, and the caller paid 14 refusals to find that out —
|
|
4942
|
+
// because nothing in the refusal distinguishes "the canvas really is a different
|
|
4943
|
+
// workflow" from "the identity flipped for a moment while you were building".
|
|
4944
|
+
//
|
|
4945
|
+
// The fence is NOT weakened and nothing is auto-applied. This performs the same
|
|
4946
|
+
// read-only re-derivation the documented recovery performs, then says which of the
|
|
4947
|
+
// two states was found. One informed retry replaces fourteen blind ones.
|
|
4948
|
+
//
|
|
4949
|
+
// Safe to recommend a retry because a fence refusal is checked BEFORE the handler
|
|
4950
|
+
// runs — "Nothing was applied" is structural here, not an echoed claim.
|
|
4951
|
+
if (isWorkflowInstanceMismatch(err) && isMutatingGraphCmd(cmd)) {
|
|
4952
|
+
const name = typeof cmd.cmd === "string" ? cmd.cmd : "panel command";
|
|
4953
|
+
const raw = err instanceof Error ? err.message : String(err);
|
|
4954
|
+
// The uuid the command CARRIED, read from the panel's own refusal rather than
|
|
4955
|
+
// from `cmd`: the stamp is applied downstream of here, so `cmd.workflow_uuid`
|
|
4956
|
+
// is undefined at this point and keying on it silently disabled the transient
|
|
4957
|
+
// branch — the one case this whole block exists for. The panel states both
|
|
4958
|
+
// sides of the comparison it performed, which is the authoritative record of
|
|
4959
|
+
// what was compared.
|
|
4960
|
+
const stamped = /issued for workflow instance ([0-9a-f-]{36})/i.exec(err instanceof Error ? err.message : String(err))?.[1] ?? null;
|
|
4961
|
+
let verdict;
|
|
4962
|
+
try {
|
|
4963
|
+
const rebind = await rebindWorkflowFence(ctx);
|
|
4964
|
+
verdict =
|
|
4965
|
+
rebind.status === "already_current" && stamped && rebind.uuid === stamped
|
|
4966
|
+
? `\n\nAUTO-REBIND: ATTEMPTED, and the live canvas now reports the SAME workflow ` +
|
|
4967
|
+
`instance this command carried (${stamped}) — nothing needed repairing. The ` +
|
|
4968
|
+
`mismatch was TRANSIENT: the identity flipped and settled back, which happens ` +
|
|
4969
|
+
`while a new unsaved workflow is still materialising. RETRY THIS EXACT CALL ONCE. ` +
|
|
4970
|
+
`Nothing was applied, so a retry cannot double-apply, and re-issuing the whole ` +
|
|
4971
|
+
`build would duplicate the work that already succeeded.`
|
|
4972
|
+
: rebind.status === "already_current"
|
|
4973
|
+
? `\n\nAUTO-REBIND: ATTEMPTED; the fence already named the live canvas ` +
|
|
4974
|
+
`(${rebind.uuid}), so it was not the stale side. Retry once — if it refuses ` +
|
|
4975
|
+
`again with the same pair, the two identities are genuinely disagreeing and ` +
|
|
4976
|
+
`panel_open_workflow is the way to settle which one you mean.`
|
|
4977
|
+
: rebind.status === "refreshed"
|
|
4978
|
+
? `\n\nAUTO-REBIND: ATTEMPTED and the fence was RE-DERIVED onto the live canvas ` +
|
|
4979
|
+
`(now ${rebind.uuid}). Retry once. If you meant the EARLIER workflow, re-select ` +
|
|
4980
|
+
`it with panel_open_workflow first — this session now points at the live one.`
|
|
4981
|
+
: `\n\nAUTO-REBIND: ATTEMPTED and did NOT succeed (${rebind.status}), so the fence ` +
|
|
4982
|
+
`is unchanged and a bare retry will fail the same way. Re-select the workflow ` +
|
|
4983
|
+
`you mean with panel_open_workflow, then retry.`;
|
|
4984
|
+
}
|
|
4985
|
+
catch (rebindErr) {
|
|
4986
|
+
// Never let the diagnosis fail the call differently than it already failed.
|
|
4987
|
+
verdict =
|
|
4988
|
+
`\n\nAUTO-REBIND: ATTEMPTED and threw, so the fence state is UNKNOWN — this refusal ` +
|
|
4989
|
+
`stands on its own terms. (${rebindErr instanceof Error ? rebindErr.message : String(rebindErr)})`;
|
|
4990
|
+
}
|
|
4991
|
+
return fail(`${name} was NOT applied — nothing changed. ${raw}${verdict}`);
|
|
4992
|
+
}
|
|
4919
4993
|
if (isCapabilityRefusal(err)) {
|
|
4920
4994
|
return fail(err instanceof Error ? err.message : String(err));
|
|
4921
4995
|
}
|