comfyui-mcp 0.50.80 → 0.50.82

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.
@@ -227,7 +227,7 @@ export class AskAnswerJournalImpl {
227
227
  /**
228
228
  * Which browser-tab INCARNATION currently holds a panel tab (#486).
229
229
  *
230
- * A `wf:<hash>` tab id names a saved workflow, so it recurs: a second browser
230
+ * A `wf:` route tab id names a saved workflow, so it recurs: a second browser
231
231
  * tab opening that workflow takes the key over. Anything that holds or settles
232
232
  * per-tab state must therefore be scoped to (tab, incarnation) — otherwise the
233
233
  * newcomer reads, reports and ACKS the departed tab's bookkeeping, and the
@@ -2256,7 +2256,7 @@ export async function runPanelOrchestrator() {
2256
2256
  // receipt for the ordinary path and must treat every answer as possibly lost;
2257
2257
  // with it, "the model read this" is a fact and only the genuinely unconfirmed
2258
2258
  // ones are held open (#486).
2259
- // #486 — a `wf:<hash>` tab id names a WORKFLOW, so a second browser tab can
2259
+ // #486 — a `wf:` route tab id names a WORKFLOW, so a second browser tab can
2260
2260
  // take it over. The journal scopes every per-tab debt to whoever is actually
2261
2261
  // holding the key, so the newcomer can neither be told nor settle the
2262
2262
  // departed tab's owed disclosure.
@@ -2266,7 +2266,7 @@ export async function runPanelOrchestrator() {
2266
2266
  // warning is owed), so it needs a LIFECYCLE end instead: a tab leaving the
2267
2267
  // bridge's connection map surfaces whatever it is still owed and retires it.
2268
2268
  // Journal entries survive — a disconnect is usually a reload.
2269
- // #486 — a DIFFERENT browser tab taking over a recurring `wf:<hash>` key is a
2269
+ // #486 — a DIFFERENT browser tab taking over a recurring `wf:` route key is a
2270
2270
  // CONVERSATION BOUNDARY, exactly like New chat or a provider switch: the
2271
2271
  // newcomer never asked the previous occupant's questions, so its answers must
2272
2272
  // stop being recoverable and stop being pushed. closeAsks downgrades and
@@ -7905,6 +7905,44 @@ export function buildPanelToolDefs() {
7905
7905
  if (mode === "pinned" && pinnedWorkflowUuid) {
7906
7906
  refreshWorkflowUuid(ctx, { workflow_uuid: pinnedWorkflowUuid });
7907
7907
  }
7908
+ // #888 — make the SCOPE PIN follow the named workflow.
7909
+ //
7910
+ // Pinning wrote the workflow-target store above and, until now, nothing
7911
+ // else. But after a mixed-origin turn the thing refusing every subsequent
7912
+ // scope-addressed command is a DIFFERENT piece of state — the turn-origin
7913
+ // pin, left ambiguous (`null`) by the #884 fence. So this tool reported
7914
+ // success truthfully while the actual blocker went untouched, and the next
7915
+ // `panel_graph_outline` returned the identical ambiguity error.
7916
+ //
7917
+ // The advice to do this is already in the code: the repin handler's own
7918
+ // refusal says "Name the workflow instead — panel_set_workflow_target with
7919
+ // a path, or panel_open_workflow — and the pin follows it." It did not
7920
+ // follow, because only `mode:"current"` ever reached that handler.
7921
+ //
7922
+ // Routed through the SAME handler, so the P0 safety gate is shared rather
7923
+ // than re-implemented: a pin that still reaches a live tab of this
7924
+ // conversation is never displaced, however explicit the request. This can
7925
+ // only ever recover a pin that is dead or ambiguous.
7926
+ //
7927
+ // Best-effort by construction: the pin store write above has already
7928
+ // succeeded and is what the caller asked for. A repin that cannot happen
7929
+ // must not retract it, so the outcome is reported, never thrown.
7930
+ let scopeRepin;
7931
+ if (mode === "pinned" && pinPath && isScopeAddress(ctx.tabId)) {
7932
+ // The PATH, never a tab id derived from it. `wf:<path>` is the saved
7933
+ // workflow HANDLE; a real tab id is a bridge ROUTE, `wf:<route>:<path>`
7934
+ // (panel #640). The first cut of this fix compared the handle against
7935
+ // routes, so it never matched and refused every time — inert, while a
7936
+ // source-text "wiring" assertion passed, because grepping for a call
7937
+ // cannot tell reachable code from dead code. The handler now does the
7938
+ // matching against the routes the bridge actually holds.
7939
+ try {
7940
+ scopeRepin = ctx.bridge.repinScopeToWorkflow?.(ctx.tabId, pinPath);
7941
+ }
7942
+ catch {
7943
+ scopeRepin = undefined; // never worse than the pre-#888 silence
7944
+ }
7945
+ }
7908
7946
  ctx.bridge.push({ type: "workflow_target", target }, ctx.tabId);
7909
7947
  // #770/#803/#716 — ROUTING is only half of "follow the tab that's live now".
7910
7948
  // rebindToActiveTab is a NO-OP whenever the bound tab is still REACHABLE, which
@@ -8037,12 +8075,23 @@ export function buildPanelToolDefs() {
8037
8075
  `binding.\n\nAPPLIED (do not repeat this part): the workflow target is now ` +
8038
8076
  `mode:"current"${rebindNote ? `.${rebindNote}` : "."}\n\nNOT APPLIED:${fence.note}`);
8039
8077
  }
8078
+ // #888 — SAY what the scope repin did. A silent success is as unhelpful
8079
+ // here as the silent refusal #1077 fixed: the whole complaint is that
8080
+ // pinning reported success while routing stayed ambiguous, so "the routing
8081
+ // ambiguity is cleared" is the one fact that makes this reply actionable.
8082
+ // A refusal carries its own reason out for the same reason.
8083
+ const scopeRepinNote = typeof scopeRepin === "string"
8084
+ ? ` This session's turn routing was AMBIGUOUS (a reconnect delivered messages from several workflows at once) and is now pinned to this workflow, so graph tools will resolve deterministically.`
8085
+ : scopeRepin && typeof scopeRepin === "object" && scopeRepin.reason
8086
+ ? ` NOTE — the workflow target was set, but this session's turn routing was NOT re-pinned: ${scopeRepin.reason}.`
8087
+ : "";
8040
8088
  return ok({
8041
8089
  ...target,
8042
8090
  ...(deferredBind ? { deferred: true } : {}),
8043
8091
  ...(fence ? { graph_binding: fence.binding } : {}),
8044
8092
  ...(fenceRebind ? { graph_binding_status: fenceRebind.status } : {}),
8045
- note: hint + rebindNote + (fence?.note ?? ""),
8093
+ ...(typeof scopeRepin === "string" ? { turn_routing: "repinned" } : {}),
8094
+ note: hint + rebindNote + (fence?.note ?? "") + scopeRepinNote,
8046
8095
  });
8047
8096
  }),
8048
8097
  def("panel_new_workflow", "Open a brand-new BLANK workflow in a NEW TAB. Use this whenever the user wants a 'new workflow' / 'fresh canvas' / 'start over for a new project'. This does NOT touch their current workflow — it opens a separate tab. NEVER use panel_clear for a new workflow (panel_clear wipes the CURRENT graph and is only for 'clear/reset this canvas').", {}, async (_args, ctx) => {