comfyui-mcp 0.50.97 → 0.50.99
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,40 @@ 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
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* #1331 — the bridge refused a mutation because the active workflow has no identity to
|
|
718
|
+
* fence against (ui-bridge's `no trusted identity` branch).
|
|
719
|
+
*
|
|
720
|
+
* A DIFFERENT refusal from the one above, and the pair is easy to conflate: that one is
|
|
721
|
+
* two identities disagreeing, this one is there being no identity at all. They also need
|
|
722
|
+
* different handling — a mismatch may clear by itself, this never does.
|
|
723
|
+
*
|
|
724
|
+
* Kept separate from the capability refusal beside it because those need opposite
|
|
725
|
+
* advice: a capability gap needs a panel UPDATE, this needs the workflow re-opened. What
|
|
726
|
+
* they share is that the generic "retry / rebind with mode:current" wrapper can clear
|
|
727
|
+
* NEITHER, and appending it sent the reporter of #1331 through four calls to reach a
|
|
728
|
+
* state the panel already believed it was in.
|
|
729
|
+
*/
|
|
730
|
+
function isNoTrustedIdentityRefusal(err) {
|
|
731
|
+
const msg = err instanceof Error ? err.message : String(err ?? "");
|
|
732
|
+
return /no trusted identity/i.test(msg);
|
|
733
|
+
}
|
|
700
734
|
let retrySettleMsOverride = null;
|
|
701
735
|
/** Short pause before the single post-drop retry, letting the replacement tab
|
|
702
736
|
* finish its reconnect hello so ensureReachable can resolve it. Test-overridable. */
|
|
@@ -4916,6 +4950,115 @@ export function makePanelToolCtx(bridge, tabId, workflowTargets) {
|
|
|
4916
4950
|
// sent agents into a futile retry/rebind loop that contradicted the embedded
|
|
4917
4951
|
// guidance. Key on the TYPED marker and surface the cause verbatim instead; it
|
|
4918
4952
|
// already names the real recovery (update + restart + browser hard-refresh).
|
|
4953
|
+
// #1331 — DECIDE WHICH "no trusted identity" THIS IS, instead of asserting one.
|
|
4954
|
+
//
|
|
4955
|
+
// The reporter was told to "retry in a moment, or rebind with
|
|
4956
|
+
// panel_set_workflow_target({mode:"current"})", followed it, and it did not work:
|
|
4957
|
+
// recovery took panel_open_workflow on the workflow that was ALREADY active —
|
|
4958
|
+
// four calls to reach a state the panel already believed it was in.
|
|
4959
|
+
//
|
|
4960
|
+
// But that advice is NOT always wrong, and my first attempt at this deleted it
|
|
4961
|
+
// outright — which #709's test caught. Two different states produce the identical
|
|
4962
|
+
// bridge message, and they need opposite remedies:
|
|
4963
|
+
//
|
|
4964
|
+
// session bound to a STALE tab, live canvas HAS an identity
|
|
4965
|
+
// → rebinding genuinely re-resolves it (#709). The advice is correct.
|
|
4966
|
+
// the LIVE canvas itself has no identity
|
|
4967
|
+
// → the rebind READS an identity to adopt and finds none, so it reports
|
|
4968
|
+
// success while every mutation keeps failing (#1331).
|
|
4969
|
+
//
|
|
4970
|
+
// The bridge cannot tell them apart — from its side both are "no identity". The
|
|
4971
|
+
// orchestrator can, with the same read-only re-derivation the documented recovery
|
|
4972
|
+
// performs. So it is measured, once, and the answer names the remedy that fits.
|
|
4973
|
+
if (isNoTrustedIdentityRefusal(err) && isMutatingGraphCmd(cmd)) {
|
|
4974
|
+
const raw = err instanceof Error ? err.message : String(err);
|
|
4975
|
+
try {
|
|
4976
|
+
const rebind = await rebindWorkflowFence(ctx);
|
|
4977
|
+
if (rebind.status === "no_identity") {
|
|
4978
|
+
return fail(`${raw}\n\nCHECKED, so this is not a guess: the live canvas was re-read and it ` +
|
|
4979
|
+
`carries no workflow identity either (${rebind.why}). ` +
|
|
4980
|
+
`panel_set_workflow_target({mode:"current"}) will NOT clear this — it chooses ` +
|
|
4981
|
+
`WHICH workflow to fence against and cannot mint an identity for one that has ` +
|
|
4982
|
+
`none, so it reports success while every mutation keeps failing. RECOVERY: ` +
|
|
4983
|
+
`panel_open_workflow(<path>) on this same workflow; re-opening it is what gives ` +
|
|
4984
|
+
`it an identity. If it has never been saved there is no path to re-open — save ` +
|
|
4985
|
+
`it first with panel_save_workflow, which also gives it a stable identity.`);
|
|
4986
|
+
}
|
|
4987
|
+
if (rebind.status === "refreshed" || rebind.status === "already_current") {
|
|
4988
|
+
return fail(`${raw}\n\nCHECKED: the live canvas DOES carry an identity (${rebind.uuid}) and ` +
|
|
4989
|
+
`this session's fence has been re-derived onto it. RETRY THIS EXACT CALL ONCE — ` +
|
|
4990
|
+
`nothing was applied, so a retry cannot double-apply.`);
|
|
4991
|
+
}
|
|
4992
|
+
// unreadable / uncorroborated — say so rather than picking a remedy.
|
|
4993
|
+
return fail(`${raw}\n\nCHECKED, and the answer is UNKNOWN: the live canvas could not be re-read ` +
|
|
4994
|
+
`well enough to say whether it has an identity. Try ` +
|
|
4995
|
+
`panel_open_workflow(<path>) on the workflow you mean — it is the only recovery ` +
|
|
4996
|
+
`that works in BOTH states, because it gives the workflow an identity rather than ` +
|
|
4997
|
+
`adopting one that may not exist.`);
|
|
4998
|
+
}
|
|
4999
|
+
catch {
|
|
5000
|
+
// The diagnosis must never change how the call failed.
|
|
5001
|
+
return fail(raw);
|
|
5002
|
+
}
|
|
5003
|
+
}
|
|
5004
|
+
// #1330 — CORROBORATE A FENCE MISMATCH INSTEAD OF LETTING IT REPEAT.
|
|
5005
|
+
//
|
|
5006
|
+
// The reporter built 12 nodes successfully, then EVERY panel_connect was refused
|
|
5007
|
+
// on an instance mismatch. Calling panel_set_workflow_target({mode:"current"})
|
|
5008
|
+
// straight afterwards reported `already_current` naming the SAME uuid the command
|
|
5009
|
+
// had been issued for, and the retried connects all succeeded. So the mismatch was
|
|
5010
|
+
// transient and self-correcting, and the caller paid 14 refusals to find that out —
|
|
5011
|
+
// because nothing in the refusal distinguishes "the canvas really is a different
|
|
5012
|
+
// workflow" from "the identity flipped for a moment while you were building".
|
|
5013
|
+
//
|
|
5014
|
+
// The fence is NOT weakened and nothing is auto-applied. This performs the same
|
|
5015
|
+
// read-only re-derivation the documented recovery performs, then says which of the
|
|
5016
|
+
// two states was found. One informed retry replaces fourteen blind ones.
|
|
5017
|
+
//
|
|
5018
|
+
// Safe to recommend a retry because a fence refusal is checked BEFORE the handler
|
|
5019
|
+
// runs — "Nothing was applied" is structural here, not an echoed claim.
|
|
5020
|
+
if (isWorkflowInstanceMismatch(err) && isMutatingGraphCmd(cmd)) {
|
|
5021
|
+
const name = typeof cmd.cmd === "string" ? cmd.cmd : "panel command";
|
|
5022
|
+
const raw = err instanceof Error ? err.message : String(err);
|
|
5023
|
+
// The uuid the command CARRIED, read from the panel's own refusal rather than
|
|
5024
|
+
// from `cmd`: the stamp is applied downstream of here, so `cmd.workflow_uuid`
|
|
5025
|
+
// is undefined at this point and keying on it silently disabled the transient
|
|
5026
|
+
// branch — the one case this whole block exists for. The panel states both
|
|
5027
|
+
// sides of the comparison it performed, which is the authoritative record of
|
|
5028
|
+
// what was compared.
|
|
5029
|
+
const stamped = /issued for workflow instance ([0-9a-f-]{36})/i.exec(err instanceof Error ? err.message : String(err))?.[1] ?? null;
|
|
5030
|
+
let verdict;
|
|
5031
|
+
try {
|
|
5032
|
+
const rebind = await rebindWorkflowFence(ctx);
|
|
5033
|
+
verdict =
|
|
5034
|
+
rebind.status === "already_current" && stamped && rebind.uuid === stamped
|
|
5035
|
+
? `\n\nAUTO-REBIND: ATTEMPTED, and the live canvas now reports the SAME workflow ` +
|
|
5036
|
+
`instance this command carried (${stamped}) — nothing needed repairing. The ` +
|
|
5037
|
+
`mismatch was TRANSIENT: the identity flipped and settled back, which happens ` +
|
|
5038
|
+
`while a new unsaved workflow is still materialising. RETRY THIS EXACT CALL ONCE. ` +
|
|
5039
|
+
`Nothing was applied, so a retry cannot double-apply, and re-issuing the whole ` +
|
|
5040
|
+
`build would duplicate the work that already succeeded.`
|
|
5041
|
+
: rebind.status === "already_current"
|
|
5042
|
+
? `\n\nAUTO-REBIND: ATTEMPTED; the fence already named the live canvas ` +
|
|
5043
|
+
`(${rebind.uuid}), so it was not the stale side. Retry once — if it refuses ` +
|
|
5044
|
+
`again with the same pair, the two identities are genuinely disagreeing and ` +
|
|
5045
|
+
`panel_open_workflow is the way to settle which one you mean.`
|
|
5046
|
+
: rebind.status === "refreshed"
|
|
5047
|
+
? `\n\nAUTO-REBIND: ATTEMPTED and the fence was RE-DERIVED onto the live canvas ` +
|
|
5048
|
+
`(now ${rebind.uuid}). Retry once. If you meant the EARLIER workflow, re-select ` +
|
|
5049
|
+
`it with panel_open_workflow first — this session now points at the live one.`
|
|
5050
|
+
: `\n\nAUTO-REBIND: ATTEMPTED and did NOT succeed (${rebind.status}), so the fence ` +
|
|
5051
|
+
`is unchanged and a bare retry will fail the same way. Re-select the workflow ` +
|
|
5052
|
+
`you mean with panel_open_workflow, then retry.`;
|
|
5053
|
+
}
|
|
5054
|
+
catch (rebindErr) {
|
|
5055
|
+
// Never let the diagnosis fail the call differently than it already failed.
|
|
5056
|
+
verdict =
|
|
5057
|
+
`\n\nAUTO-REBIND: ATTEMPTED and threw, so the fence state is UNKNOWN — this refusal ` +
|
|
5058
|
+
`stands on its own terms. (${rebindErr instanceof Error ? rebindErr.message : String(rebindErr)})`;
|
|
5059
|
+
}
|
|
5060
|
+
return fail(`${name} was NOT applied — nothing changed. ${raw}${verdict}`);
|
|
5061
|
+
}
|
|
4919
5062
|
if (isCapabilityRefusal(err)) {
|
|
4920
5063
|
return fail(err instanceof Error ? err.message : String(err));
|
|
4921
5064
|
}
|