comfyui-mcp 0.52.131 → 0.52.132
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.
- package/dist/orchestrator/panel-tools.js +90 -16
- package/dist/orchestrator/panel-tools.js.map +1 -1
- package/dist/services/ui-bridge.js +76 -16
- package/dist/services/ui-bridge.js.map +1 -1
- package/package.json +7 -3
- package/scripts/anti-slop-baseline.json +801 -0
- package/scripts/check-anti-slop.mjs +461 -0
- package/scripts/codex-knowledge-parity-smoke.mjs +1 -1
- package/scripts/run-checks.mjs +62 -0
|
@@ -42,7 +42,7 @@ import { readWorkflowListReadiness, workflowListReadinessOf, } from "../services
|
|
|
42
42
|
import { isPreExecutorRefusal } from "../services/panel-refusal.js";
|
|
43
43
|
import { createSdkMcpServer, tool } from "@anthropic-ai/claude-agent-sdk";
|
|
44
44
|
import { parse as parseYaml } from "yaml";
|
|
45
|
-
import { requiredPanelVersion, SEMVER_RE, LATE_ASK_TTL_MS, tabIncarnationSlot, SHOW_MEDIA_KIND_MIN_PANEL_VERSION, showMediaItemsPanelCannotPaint,
|
|
45
|
+
import { panelVersionForCapability, requiredPanelVersion, SEMVER_RE, LATE_ASK_TTL_MS, tabIncarnationSlot, SHOW_MEDIA_KIND_MIN_PANEL_VERSION, showMediaItemsPanelCannotPaint, } from "../services/ui-bridge.js";
|
|
46
46
|
import { compareSemver } from "../services/self-update.js";
|
|
47
47
|
import { describeInstallPanelAction } from "../services/panel-recovery.js";
|
|
48
48
|
import { peekResolvedPanelBase, primePanelBase, verifiedPanelDiskVersion, } from "../services/panel-workspace.js";
|
|
@@ -5355,12 +5355,71 @@ async function authoritativePromotedScopeError(ctx, expected, innerNodeId) {
|
|
|
5355
5355
|
const error = currentPromotedScopeError(ctx, expected, true, observed);
|
|
5356
5356
|
return { error, observed };
|
|
5357
5357
|
}
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5358
|
+
/** A refusal a retry can legitimately clear. Anything caused by the panel BUILD
|
|
5359
|
+
* goes through {@link promotedPanelBuildRefusal} instead — #2365 carved the
|
|
5360
|
+
* first of those out with a `capabilityName` option on this function, and that
|
|
5361
|
+
* option is gone because every capability call site now uses the dedicated
|
|
5362
|
+
* path, which also names the advertised version and a route that works. */
|
|
5363
|
+
function promotedWriteRefusal(widget, reason) {
|
|
5362
5364
|
return fail(`panel_set_widget refused the promoted "${widget}" write because ${reason}. ` +
|
|
5363
|
-
`No graph_set_widget was dispatched;
|
|
5365
|
+
`No graph_set_widget was dispatched; retry only after the panel binding and ` +
|
|
5366
|
+
`subgraph mapping are stable.`);
|
|
5367
|
+
}
|
|
5368
|
+
/**
|
|
5369
|
+
* panel#1859 — the same refusal, for the one cause a retry can never reach.
|
|
5370
|
+
*
|
|
5371
|
+
* MCP 0.52.129 shipped the #2314 promoted-write fence; the panel build that
|
|
5372
|
+
* satisfies it shipped twenty minutes later in 0.15.101. Every user in between
|
|
5373
|
+
* got {@link promotedWriteRefusal}'s "retry only after the panel binding and
|
|
5374
|
+
* subgraph mapping are stable" for a condition that is a JS bundle on disk. The
|
|
5375
|
+
* reporter retried five times, re-issued panel_set_workflow_target (which
|
|
5376
|
+
* answered `already_current`), and hard-refreshed the tab; none of that
|
|
5377
|
+
* replaces the bundle, so the loop had no exit.
|
|
5378
|
+
*
|
|
5379
|
+
* So this says what is actually wrong, rules out the three things a caller
|
|
5380
|
+
* would otherwise try next, and names a route to the value that works today.
|
|
5381
|
+
* The refusal itself is unchanged — a pre-0.15.101 panel genuinely cannot
|
|
5382
|
+
* enforce the fence, and loosening it would reopen #2314.
|
|
5383
|
+
*/
|
|
5384
|
+
function promotedPanelBuildRefusal(ctx, nodeId, widget, reason, capability) {
|
|
5385
|
+
// A fence capability reads `false` for TWO different facts: this hello did
|
|
5386
|
+
// not advertise it, or resolveTarget threw and there was no hello at all. Only
|
|
5387
|
+
// the first is about the bundle on disk. If the receiver cannot be resolved,
|
|
5388
|
+
// the honest answer is the transient one — telling someone whose tab just
|
|
5389
|
+
// dropped to update and hard-refresh is the same unreachable advice this
|
|
5390
|
+
// whole change exists to remove, and would be worse than what it replaced
|
|
5391
|
+
// because the wording below is emphatic that retrying cannot help.
|
|
5392
|
+
//
|
|
5393
|
+
// Read in the SAME synchronous turn as the capability that sent us here, with
|
|
5394
|
+
// no await between, so run-to-completion means both saw one connection.
|
|
5395
|
+
if (ctx.tabReceiverResolvable?.() !== true) {
|
|
5396
|
+
return promotedWriteRefusal(widget, "the receiving panel could not be resolved while its promoted-write fence " +
|
|
5397
|
+
"capabilities were read, so whether this is a stale binding or an " +
|
|
5398
|
+
"out-of-date panel build is not decidable from here");
|
|
5399
|
+
}
|
|
5400
|
+
const floor = panelVersionForCapability(capability);
|
|
5401
|
+
const advertised = tabAdvertisedPanelVersion(ctx);
|
|
5402
|
+
const update = describeInstallPanelAction("update", "update the ComfyUI-MCP panel via ComfyUI Manager");
|
|
5403
|
+
// Never interpolate the caller's raw node_id: it is untrusted argument shape.
|
|
5404
|
+
// A container id that will not canonicalize is dropped from the sentence
|
|
5405
|
+
// rather than echoed, which leaves the route stated but unparameterized.
|
|
5406
|
+
const canonicalId = canonicalQueriedNodeId(nodeId);
|
|
5407
|
+
const enterArg = canonicalId ? `node_id: ${canonicalId}` : "the container's node_id";
|
|
5408
|
+
return fail(`panel_set_widget refused the promoted "${widget}" write because the connected ` +
|
|
5409
|
+
`panel build ${reason}. No graph_set_widget was dispatched.\n` +
|
|
5410
|
+
`This is a panel BUILD skew, not a transient binding state. Retrying, ` +
|
|
5411
|
+
`panel_set_workflow_target, and reloading the ComfyUI tab all leave the same ` +
|
|
5412
|
+
`bundle running, so none of them can clear it.` +
|
|
5413
|
+
(advertised ? ` This tab announced panel ${advertised}.` : "") +
|
|
5414
|
+
` A promoted-container write needs ${floor ? `panel ≥${floor}` : "a newer panel build"}. ` +
|
|
5415
|
+
`${update}, then HARD-REFRESH the browser tab (Ctrl+Shift+R, or Cmd+Shift+R on ` +
|
|
5416
|
+
`macOS); a restart alone leaves the tab running cached old JS.\n` +
|
|
5417
|
+
`Until then the value is still reachable by hand: ` +
|
|
5418
|
+
`panel_enter_subgraph(${enterArg}), then panel_set_widget on the ` +
|
|
5419
|
+
`inner node that owns "${widget}". If the write does not stick because the ` +
|
|
5420
|
+
`container's promoted rail holds the value, un-promote it first ` +
|
|
5421
|
+
`(panel_promote_widget with demote:true), write the inner node, and re-promote — ` +
|
|
5422
|
+
`that is the sequence panel#1859 reported as clearing it.`);
|
|
5364
5423
|
}
|
|
5365
5424
|
/**
|
|
5366
5425
|
* #2314 — inspect a promoted target before the first write. A valid mapping is
|
|
@@ -5448,13 +5507,19 @@ async function preparePromotedWidgetWrite(ctx, nodeId, widget) {
|
|
|
5448
5507
|
if (!envelope) {
|
|
5449
5508
|
return promotedWriteRefusal(widget, "graph_get_subgraph returned a malformed, stale, or incomplete ownership envelope");
|
|
5450
5509
|
}
|
|
5451
|
-
//
|
|
5452
|
-
//
|
|
5453
|
-
//
|
|
5454
|
-
//
|
|
5455
|
-
//
|
|
5510
|
+
// panel#1859 — ask the HELLO before reading the witness out of the envelope.
|
|
5511
|
+
// A build that never advertised this fence also never writes
|
|
5512
|
+
// `subgraph_of.graph_identity` (both landed in panel 0.15.101; v0.15.85
|
|
5513
|
+
// replies with a bare `subgraph_of: { node_id, title }`), so diagnosing it as
|
|
5514
|
+
// a missing field describes the symptom while the cause is the bundle on
|
|
5515
|
+
// disk. This ordering is #2365's idea, kept.
|
|
5516
|
+
//
|
|
5517
|
+
// It also narrows the branch below to its true meaning: a receiver that DID
|
|
5518
|
+
// advertise the fence and still failed to publish the identity is an
|
|
5519
|
+
// inconsistent reply, which a retry can legitimately clear.
|
|
5456
5520
|
if (ctx.tabExpectedScopeGraphIdentityFenceCapability?.() !== true) {
|
|
5457
|
-
return
|
|
5521
|
+
return promotedPanelBuildRefusal(ctx, nodeId, widget, "does not advertise the atomic promoted graph-identity write fence, so it " +
|
|
5522
|
+
"cannot publish the target graph identity a promoted write is fenced on", "enforces_expected_scope_graph_identity_at_write");
|
|
5458
5523
|
}
|
|
5459
5524
|
const scope = promotedScopeWitnessFromEnvelope(envelope);
|
|
5460
5525
|
if (!scope) {
|
|
@@ -5512,16 +5577,20 @@ async function preparePromotedWidgetWrite(ctx, nodeId, widget) {
|
|
|
5512
5577
|
if (terminalBlocked)
|
|
5513
5578
|
return terminalBlocked;
|
|
5514
5579
|
}
|
|
5580
|
+
// panel#1859 — these three are capability skew, exactly like the missing
|
|
5581
|
+
// graph identity above: the hello either advertises the fence or it does not,
|
|
5582
|
+
// and a caller cannot change that by trying again.
|
|
5515
5583
|
if (ctx.tabExpectedNodeTypeFenceCapability?.() !== true) {
|
|
5516
|
-
return
|
|
5584
|
+
return promotedPanelBuildRefusal(ctx, nodeId, widget, "does not advertise the atomic inner-node write fence", "enforces_expected_node_type_at_write");
|
|
5517
5585
|
}
|
|
5518
|
-
//
|
|
5519
|
-
//
|
|
5586
|
+
// The graph-identity fence is checked before the scope witness above (#2365),
|
|
5587
|
+
// so reaching here means the hello advertised it.
|
|
5588
|
+
// Legacy panels do not publish a parent-rail witness and retain the
|
|
5520
5589
|
// established same-name recovery contract. A current witness-capable panel
|
|
5521
5590
|
// must advertise the synchronous final rail re-resolution before MCP sends
|
|
5522
5591
|
// the inner/shared-subgraph write.
|
|
5523
5592
|
if (publishesCompleteTerminalWitness && ctx.tabPromotedParentRailFenceCapability?.() !== true) {
|
|
5524
|
-
return
|
|
5593
|
+
return promotedPanelBuildRefusal(ctx, nodeId, widget, "does not advertise the atomic promoted parent-rail write fence", "enforces_promoted_parent_rail_at_write");
|
|
5525
5594
|
}
|
|
5526
5595
|
return {
|
|
5527
5596
|
kind: "promoted-write",
|
|
@@ -11884,6 +11953,11 @@ export function makePanelToolCtx(bridge, tabId, workflowTargets, onRunTicketOpen
|
|
|
11884
11953
|
bridge.tabPromotedTerminalWitnessCapability(ctx.tabId);
|
|
11885
11954
|
ctx.tabPromotedParentRailFenceCapability = () => typeof bridge.tabPromotedParentRailFenceCapability === "function" &&
|
|
11886
11955
|
bridge.tabPromotedParentRailFenceCapability(ctx.tabId);
|
|
11956
|
+
// Absent on a bridge that does not implement it, which reads as "cannot
|
|
11957
|
+
// prove the receiver is there" — the conservative direction, since it only
|
|
11958
|
+
// ever softens a refusal's wording away from blaming the panel build.
|
|
11959
|
+
ctx.tabReceiverResolvable = () => typeof bridge.tabReceiverResolvable === "function" &&
|
|
11960
|
+
bridge.tabReceiverResolvable(ctx.tabId);
|
|
11887
11961
|
ctx.tabGraphMutationCapability = () => bridge.tabGraphMutationCapability(ctx.tabId);
|
|
11888
11962
|
return ctx;
|
|
11889
11963
|
}
|