comfyui-mcp 0.52.199 → 0.52.200

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.
@@ -6035,13 +6035,21 @@ function forgetStaleSubgraphIdentity(ctx) {
6035
6035
  ctx.bridge?.clearPromotedSubgraphIdentity?.(ctx.tabId);
6036
6036
  }
6037
6037
  /** Tabs whose promoted-container mapping is unverified after a queue-busy
6038
- * mutation refusal. `graph_get_subgraph` can stay indeterminate for ordinary
6039
- * root nodes until something walks the live graph (#2730). */
6038
+ * mutation refusal or a canvas-replacing load. `graph_get_subgraph` can stay
6039
+ * indeterminate for ordinary root nodes and live subgraphs until something
6040
+ * walks the live graph (#2730, #2886). */
6040
6041
  const staleSubgraphMappingTabs = new Set();
6041
6042
  function noteStaleSubgraphMapping(tabId) {
6042
6043
  if (tabId)
6043
6044
  staleSubgraphMappingTabs.add(tabId);
6044
6045
  }
6046
+ /** A successful `graph_load` replaced the canvas. Drop leftover subgraph
6047
+ * identity and force one mapping refresh before the next promoted write so
6048
+ * `panel_set_widget` does not stay refused until a manual current-target rebind. */
6049
+ function noteCanvasReplacedForPromotedWrites(ctx) {
6050
+ forgetStaleSubgraphIdentity(ctx);
6051
+ noteStaleSubgraphMapping(ctx.tabId);
6052
+ }
6045
6053
  export function __resetStaleSubgraphMappingForTest() {
6046
6054
  staleSubgraphMappingTabs.clear();
6047
6055
  }
@@ -6586,6 +6594,9 @@ function classifyPromotedSubgraphReadError(res) {
6586
6594
  }
6587
6595
  return "permanent";
6588
6596
  }
6597
+ function isStaleSubgraphMappingRead(res) {
6598
+ return /subgraph mapping is not loaded/i.test(textOfToolResult(res));
6599
+ }
6589
6600
  /**
6590
6601
  * panel#1869 — the panel's `[canvas-root-divergence]` refusal is a DIAGNOSIS,
6591
6602
  * and must not be replaced by the promoted-container wording.
@@ -7442,7 +7453,12 @@ async function preparePromotedWidgetWrite(ctx, nodeId, widget) {
7442
7453
  // ordinary root node. Refresh the mapping once and re-probe. Still
7443
7454
  // unverifiable → refuse. Do not spend the transient subgraph re-read on
7444
7455
  // a permanent application error.
7445
- if (!mappingRefreshed && rootGraphIdentity !== null) {
7456
+ // #2886 a live subgraph container has the same stale-registry miss
7457
+ // after graph_load. Re-probing only the ordinary fast path left those
7458
+ // writes refused; retry graph_get_subgraph once the mapping walk ran.
7459
+ // Do not replay other permanent application errors.
7460
+ const mappingUnknown = isStaleSubgraphMappingRead(sub);
7461
+ if (!mappingRefreshed && (rootGraphIdentity !== null || mappingUnknown)) {
7446
7462
  const refreshError = await refreshSubgraphMapping();
7447
7463
  if (refreshError)
7448
7464
  return refreshError;
@@ -7455,61 +7471,89 @@ async function preparePromotedWidgetWrite(ctx, nodeId, widget) {
7455
7471
  return ordinaryBindingRefusal(widget, driftAfterMapping);
7456
7472
  return ordinaryWritePlanFromScope(widget, targetScope, tabBefore, identityBefore);
7457
7473
  }
7458
- }
7459
- return promotedSubgraphReadRefusal(widget, sub, "graph_get_subgraph could not determine whether the addressed node is a promoted container");
7460
- }
7461
- // A binding/subgraph registration transition can make the first read
7462
- // indeterminate even though the same live container is readable immediately
7463
- // afterward. Refresh once, then keep the existing fail-closed classification
7464
- // and identity checks for the fresh reply.
7465
- const refreshedSub = await ctx.call({ cmd: "graph_get_subgraph", node_id: nodeId }, undefined, undefined, undefined, PROMOTED_PREFLIGHT_READ_OPTIONS);
7466
- if (refreshedSub.isError) {
7467
- const refreshedReadKind = classifyPromotedSubgraphReadError(refreshedSub);
7468
- if (refreshedReadKind === "definitive-ordinary") {
7469
- const driftAfterRefresh = panelBindingDriftReason(ctx, "after the subgraph refresh", hasIdentityApi, identityBefore, tabBefore);
7470
- if (driftAfterRefresh)
7471
- return promotedWriteRefusal(widget, driftAfterRefresh);
7472
- const expectedNodeType = definitiveNonPromotedNodeType(refreshedSub);
7473
- if (!expectedNodeType) {
7474
- return promotedWriteRefusal(widget, "the definitive ordinary refresh did not publish a node type fence");
7475
- }
7476
- const expectedNodeIdentity = targetScope?.nodeIdentity;
7477
- if (!expectedNodeIdentity) {
7478
- return promotedWriteRefusal(widget, "the preflight ordinary-node read did not publish a trustworthy node identity fence");
7474
+ if (mappingUnknown) {
7475
+ sub = await ctx.call({ cmd: "graph_get_subgraph", node_id: nodeId }, undefined, undefined, undefined, PROMOTED_PREFLIGHT_READ_OPTIONS);
7479
7476
  }
7480
- if (ctx.tabExpectedNodeTypeFenceCapability?.() !== true) {
7481
- return promotedPanelBuildRefusal(ctx, nodeId, widget, "does not advertise the atomic ordinary-node write fence", "enforces_expected_node_type_at_write");
7482
- }
7483
- if (ctx.tabExpectedNodeIdentityFenceCapability?.() !== true) {
7484
- return promotedPanelBuildRefusal(ctx, nodeId, widget, "does not advertise the atomic ordinary-node identity write fence", "enforces_expected_node_identity_at_write");
7485
- }
7486
- const ordinaryScope = targetScope;
7487
- if (!ordinaryScope?.graphIdentity) {
7488
- return ordinaryBindingRefusal(widget, "did not publish the current graph identity needed to fence this ordinary write");
7477
+ }
7478
+ if (sub.isError) {
7479
+ if (classifyPromotedSubgraphReadError(sub) === "definitive-ordinary") {
7480
+ const driftAfterMappingRead = panelBindingDriftReason(ctx, "after the subgraph mapping refresh", hasIdentityApi, identityBefore, tabBefore);
7481
+ if (driftAfterMappingRead)
7482
+ return promotedWriteRefusal(widget, driftAfterMappingRead);
7483
+ if (targetScope?.activeView === "subgraph" && targetScope.node === "ordinary") {
7484
+ if (!targetScope.nodeIdentity)
7485
+ return null;
7486
+ const expectedNodeType = definitiveNonPromotedNodeType(sub);
7487
+ if (!sameDefinitiveOrdinaryNodeType(expectedNodeType, targetScope.nodeType)) {
7488
+ return promotedWriteRefusal(widget, "the ordinary node type changed between the scope and subgraph reads");
7489
+ }
7490
+ if (ctx.tabExpectedNodeTypeFenceCapability?.() !== true) {
7491
+ return promotedPanelBuildRefusal(ctx, nodeId, widget, "does not advertise the atomic ordinary-node write fence", "enforces_expected_node_type_at_write");
7492
+ }
7493
+ if (ctx.tabExpectedNodeIdentityFenceCapability?.() !== true) {
7494
+ return promotedPanelBuildRefusal(ctx, nodeId, widget, "does not advertise the atomic ordinary-node identity write fence", "enforces_expected_node_identity_at_write");
7495
+ }
7496
+ return ordinaryWritePlanFromScope(widget, targetScope, tabBefore, identityBefore);
7497
+ }
7498
+ return null;
7489
7499
  }
7490
- return {
7491
- kind: "ordinary-write",
7492
- activeView: ordinaryScope.activeView,
7493
- ...(ordinaryScope.scopeOwnerNodeId !== undefined
7494
- ? { scopeOwnerNodeId: ordinaryScope.scopeOwnerNodeId }
7495
- : {}),
7496
- graphIdentity: ordinaryScope.graphIdentity,
7497
- ...(ordinaryScope.workflowUuid !== undefined
7498
- ? { workflowUuid: ordinaryScope.workflowUuid }
7499
- : {}),
7500
- expectedNodeType,
7501
- expectedNodeIdentity,
7502
- binding: {
7503
- tabId: tabBefore,
7504
- ...(isUsablePanelConnectionIdentity(identityBefore)
7505
- ? { identity: identityBefore }
7500
+ return promotedSubgraphReadRefusal(widget, sub, "graph_get_subgraph could not determine whether the addressed node is a promoted container");
7501
+ }
7502
+ }
7503
+ else {
7504
+ // A binding/subgraph registration transition can make the first read
7505
+ // indeterminate even though the same live container is readable immediately
7506
+ // afterward. Refresh once, then keep the existing fail-closed classification
7507
+ // and identity checks for the fresh reply.
7508
+ const refreshedSub = await ctx.call({ cmd: "graph_get_subgraph", node_id: nodeId }, undefined, undefined, undefined, PROMOTED_PREFLIGHT_READ_OPTIONS);
7509
+ if (refreshedSub.isError) {
7510
+ const refreshedReadKind = classifyPromotedSubgraphReadError(refreshedSub);
7511
+ if (refreshedReadKind === "definitive-ordinary") {
7512
+ const driftAfterRefresh = panelBindingDriftReason(ctx, "after the subgraph refresh", hasIdentityApi, identityBefore, tabBefore);
7513
+ if (driftAfterRefresh)
7514
+ return promotedWriteRefusal(widget, driftAfterRefresh);
7515
+ const expectedNodeType = definitiveNonPromotedNodeType(refreshedSub);
7516
+ if (!expectedNodeType) {
7517
+ return promotedWriteRefusal(widget, "the definitive ordinary refresh did not publish a node type fence");
7518
+ }
7519
+ const expectedNodeIdentity = targetScope?.nodeIdentity;
7520
+ if (!expectedNodeIdentity) {
7521
+ return promotedWriteRefusal(widget, "the preflight ordinary-node read did not publish a trustworthy node identity fence");
7522
+ }
7523
+ if (ctx.tabExpectedNodeTypeFenceCapability?.() !== true) {
7524
+ return promotedPanelBuildRefusal(ctx, nodeId, widget, "does not advertise the atomic ordinary-node write fence", "enforces_expected_node_type_at_write");
7525
+ }
7526
+ if (ctx.tabExpectedNodeIdentityFenceCapability?.() !== true) {
7527
+ return promotedPanelBuildRefusal(ctx, nodeId, widget, "does not advertise the atomic ordinary-node identity write fence", "enforces_expected_node_identity_at_write");
7528
+ }
7529
+ const ordinaryScope = targetScope;
7530
+ if (!ordinaryScope?.graphIdentity) {
7531
+ return ordinaryBindingRefusal(widget, "did not publish the current graph identity needed to fence this ordinary write");
7532
+ }
7533
+ return {
7534
+ kind: "ordinary-write",
7535
+ activeView: ordinaryScope.activeView,
7536
+ ...(ordinaryScope.scopeOwnerNodeId !== undefined
7537
+ ? { scopeOwnerNodeId: ordinaryScope.scopeOwnerNodeId }
7506
7538
  : {}),
7507
- },
7508
- };
7539
+ graphIdentity: ordinaryScope.graphIdentity,
7540
+ ...(ordinaryScope.workflowUuid !== undefined
7541
+ ? { workflowUuid: ordinaryScope.workflowUuid }
7542
+ : {}),
7543
+ expectedNodeType,
7544
+ expectedNodeIdentity,
7545
+ binding: {
7546
+ tabId: tabBefore,
7547
+ ...(isUsablePanelConnectionIdentity(identityBefore)
7548
+ ? { identity: identityBefore }
7549
+ : {}),
7550
+ },
7551
+ };
7552
+ }
7553
+ return promotedSubgraphReadRefusal(widget, refreshedSub, "graph_get_subgraph could not determine whether the addressed node is a promoted container");
7509
7554
  }
7510
- return promotedSubgraphReadRefusal(widget, refreshedSub, "graph_get_subgraph could not determine whether the addressed node is a promoted container");
7555
+ sub = refreshedSub;
7511
7556
  }
7512
- sub = refreshedSub;
7513
7557
  }
7514
7558
  let payload = parseToolResultJson(sub);
7515
7559
  if (!payload || !promotedEnvelopeCarriesEvidence(payload)) {
@@ -17918,6 +17962,12 @@ export function buildPanelToolDefs() {
17918
17962
  // `format:"api"`. That field is present only on this branch, which makes it the
17919
17963
  // signal rather than an inference.
17920
17964
  const reply = parseToolResultJson(loaded);
17965
+ // #2886 — a successful load replaces the canvas. UI-format keeps the
17966
+ // instance uuid, so the fence is still valid, but leftover subgraph
17967
+ // identity and the subgraph registry still describe the PREVIOUS graph.
17968
+ // Mark mapping stale here; do not re-derive from whatever is active now.
17969
+ if (reply?.loaded === true)
17970
+ noteCanvasReplacedForPromotedWrites(ctx);
17921
17971
  const remintedInstance = reply?.loaded === true && reply?.format === "api";
17922
17972
  if (!remintedInstance)
17923
17973
  return loaded;