comfyui-mcp 0.50.98 → 0.50.100
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.
|
@@ -713,6 +713,24 @@ function isWorkflowInstanceMismatch(err) {
|
|
|
713
713
|
const msg = err instanceof Error ? err.message : String(err ?? "");
|
|
714
714
|
return /workflow instance mismatch/i.test(msg);
|
|
715
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
|
+
}
|
|
716
734
|
let retrySettleMsOverride = null;
|
|
717
735
|
/** Short pause before the single post-drop retry, letting the replacement tab
|
|
718
736
|
* finish its reconnect hello so ensureReachable can resolve it. Test-overridable. */
|
|
@@ -4932,6 +4950,57 @@ export function makePanelToolCtx(bridge, tabId, workflowTargets) {
|
|
|
4932
4950
|
// sent agents into a futile retry/rebind loop that contradicted the embedded
|
|
4933
4951
|
// guidance. Key on the TYPED marker and surface the cause verbatim instead; it
|
|
4934
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
|
+
}
|
|
4935
5004
|
// #1330 — CORROBORATE A FENCE MISMATCH INSTEAD OF LETTING IT REPEAT.
|
|
4936
5005
|
//
|
|
4937
5006
|
// The reporter built 12 nodes successfully, then EVERY panel_connect was refused
|
|
@@ -5099,15 +5168,17 @@ export function makePanelToolCtx(bridge, tabId, workflowTargets) {
|
|
|
5099
5168
|
catch (err) {
|
|
5100
5169
|
// Only a card-reply TIMEOUT is recoverable/honest-as-timeout: poll the late
|
|
5101
5170
|
// buffer, then report "timeout" if still unanswered. Any other error (no
|
|
5102
|
-
// panel, transport failure)
|
|
5103
|
-
// as
|
|
5171
|
+
// panel, transport failure) still SKIPS the destructive op — but it is reported
|
|
5172
|
+
// as `unreachable`, not `no` (#1332). The user did not decline; we never got to
|
|
5173
|
+
// ask them, and a caller that says "cancelled" on this is describing a decision
|
|
5174
|
+
// nobody made.
|
|
5104
5175
|
if (isReplyTimeoutError(err)) {
|
|
5105
5176
|
const late = await pollLateAskReply(bridge, askId, timing, budgetEnd);
|
|
5106
5177
|
if (late !== undefined)
|
|
5107
5178
|
return isAffirmative(late) ? "yes" : "no";
|
|
5108
5179
|
return "timeout";
|
|
5109
5180
|
}
|
|
5110
|
-
return "
|
|
5181
|
+
return "unreachable";
|
|
5111
5182
|
}
|
|
5112
5183
|
};
|
|
5113
5184
|
// EXPLICIT self-heal — see PanelToolCtx.rebindToActiveTab. Only rebinds when
|
|
@@ -6617,6 +6688,14 @@ export function buildPanelToolDefs() {
|
|
|
6617
6688
|
return ok("Timed out waiting for your confirmation, so I left the canvas as-is. " +
|
|
6618
6689
|
"Tell me to clear it again when you're ready.");
|
|
6619
6690
|
}
|
|
6691
|
+
if (decision === "unreachable") {
|
|
6692
|
+
// #1332 — the canvas is still untouched, which is the part that matters, but
|
|
6693
|
+
// the user never saw the question. Saying "cancelled" would credit them with
|
|
6694
|
+
// a decision they were never offered.
|
|
6695
|
+
return ok("The canvas was left as-is — but NOT because it was declined: the panel could " +
|
|
6696
|
+
"not be reached to ask, so the question never appeared. Nothing was cleared. " +
|
|
6697
|
+
"Check the panel tab is connected, then ask again.");
|
|
6698
|
+
}
|
|
6620
6699
|
if (decision !== "yes") {
|
|
6621
6700
|
return ok("Cancelled — the canvas was left as-is.");
|
|
6622
6701
|
}
|
|
@@ -8809,7 +8888,26 @@ export function buildPanelToolDefs() {
|
|
|
8809
8888
|
: "Cancelled — no new restart was dispatched. ComfyUI was briefly " +
|
|
8810
8889
|
"unreachable but is healthy again.");
|
|
8811
8890
|
}
|
|
8812
|
-
|
|
8891
|
+
// #1332 — the reporter's exact string, and it was FALSE. They accepted the
|
|
8892
|
+
// restart, ComfyUI restarted (a fresh startup in the server log), and this
|
|
8893
|
+
// said it had not — because the restart dropped the socket the answer had to
|
|
8894
|
+
// travel back on, and a transport failure used to arrive here as "no".
|
|
8895
|
+
//
|
|
8896
|
+
// The probes above already refuse to claim "not restarted" while the server
|
|
8897
|
+
// is DOWN. This is the remaining case: the server is HEALTHY, which is
|
|
8898
|
+
// equally true of "nothing happened" and of "it restarted and came back".
|
|
8899
|
+
// With an explicit decline we know which; without one we do not, and the
|
|
8900
|
+
// sentence must stop asserting it.
|
|
8901
|
+
return ok(decision === "unreachable"
|
|
8902
|
+
? "This call did NOT dispatch a restart. Whether ComfyUI restarted for some " +
|
|
8903
|
+
"other reason cannot be told from here: the panel could not be reached to " +
|
|
8904
|
+
"ask for confirmation — the question never appeared — so no decision was " +
|
|
8905
|
+
"made either way, and the server is reachable now, which looks the same " +
|
|
8906
|
+
"whether it never went down or went down and came back. If you asked for a " +
|
|
8907
|
+
"restart and one has already happened, this is that transport loss, not a " +
|
|
8908
|
+
"cancellation. Check the ComfyUI log for a fresh startup line before " +
|
|
8909
|
+
"restarting again."
|
|
8910
|
+
: "Cancelled — ComfyUI was not restarted.");
|
|
8813
8911
|
}
|
|
8814
8912
|
// Heal an orphaned session onto the live tab FIRST, then bind the reboot dispatch
|
|
8815
8913
|
// to that ONE tab id (no await between capture and dispatch, so JS run-to-
|