comfyui-mcp 0.52.37 → 0.52.39

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.
@@ -0,0 +1,183 @@
1
+ /**
2
+ * Tool ↔ bridge-command correspondence used by agent-facing refusals.
3
+ *
4
+ * Two maps, one reverse index. The forward maps are NOT injective (graph_load
5
+ * is dispatched by two tools), so the reverse index omits ambiguous commands
6
+ * rather than guessing — a confidently wrong tool name is the defect this
7
+ * module exists to prevent (#1933, #1935).
8
+ *
9
+ * Lives here, not in panel-tools.ts, so ui-bridge can name tools in its own
10
+ * refusals without a circular import (panel-tools already imports ui-bridge).
11
+ */
12
+ /**
13
+ * #694 — the MUTATING panel tools that accept retry_of, keyed to the bridge
14
+ * command each dispatches. Covers every command the bridge fences to a workflow
15
+ * (GRAPH_CMD_EFFECT "targeted" — see requiresWorkflowStampEnforcement) plus the
16
+ * four workflow mutators (workflow_save / workflow_save_as / workflow_rename /
17
+ * workflow_close). Navigation/creation (workflow_open / workflow_new) and the
18
+ * tools whose descriptions declare them view/read-only (panel_find_nodes,
19
+ * panel_canvas, panel_screenshot, panel_list_subgraphs) are excluded: a read must
20
+ * never mint or carry a retry token — its retry could be answered from the ledger
21
+ * with a STALE outcome (codex gate).
22
+ *
23
+ * Four UI-STATE commands are admitted on purpose (graph_select_nodes,
24
+ * graph_enter/exit_subgraph, graph_copy_nodes): they change selection, subgraph
25
+ * scope or clipboard idempotently, so a deduped retry is a no-op. THIS MAP — not
26
+ * the workflow fence — is what decides whether a token is minted and whether a
27
+ * caller-supplied one reaches the wire, so those four keep behaving exactly as
28
+ * they did before #778 reclassified them as `inert` for the fence. The two
29
+ * questions are related but not the same, and answering one with the other is
30
+ * the defect #778 is about.
31
+ *
32
+ * EXPLICIT MAP, mirroring the RETRY_SAFE_CMDS / MUTATING_GRAPH_EDIT_CMDS
33
+ * maintenance model — keep in sync when mutating tools are added. Exported for
34
+ * the #694 surface-integrity test.
35
+ */
36
+ export const RETRY_TOKEN_CMD_BY_TOOL = {
37
+ panel_add_node: "graph_add_node",
38
+ panel_edit_node: "graph_edit_node",
39
+ panel_remove_node: "graph_remove_node",
40
+ panel_clear: "graph_clear",
41
+ panel_flatten_workflow: "graph_load",
42
+ panel_load_workflow: "graph_load",
43
+ panel_connect: "graph_connect",
44
+ panel_disconnect: "graph_disconnect",
45
+ // Idempotent: inputs/outputs/default_mode are absolute values, so a deduped
46
+ // retry writes the same metadata again rather than doubling anything.
47
+ panel_configure_app_mode: "graph_configure_app_mode",
48
+ panel_set_widget: "graph_set_widget",
49
+ panel_remove_widget: "graph_remove_widget",
50
+ panel_set_property: "graph_set_node_property",
51
+ panel_move_node: "graph_move_node",
52
+ panel_resize_node: "graph_resize_node",
53
+ panel_auto_layout: "graph_auto_layout",
54
+ panel_run: "graph_run",
55
+ panel_save_workflow: "workflow_save", // workflow_save_as when `name` is given
56
+ panel_rename_workflow: "workflow_rename",
57
+ panel_close_workflow: "workflow_close",
58
+ panel_select_nodes: "graph_select_nodes",
59
+ panel_create_subgraph: "graph_create_subgraph",
60
+ panel_subgraph_group: "graph_subgraph_group",
61
+ panel_copy_nodes: "graph_copy_nodes",
62
+ panel_paste_nodes: "graph_paste_nodes",
63
+ panel_save_subgraph: "graph_save_subgraph",
64
+ panel_add_subgraph: "graph_add_subgraph",
65
+ panel_create_group: "graph_create_group",
66
+ panel_move_group: "graph_move_group",
67
+ panel_edit_group: "graph_edit_group",
68
+ panel_remove_group: "graph_remove_group",
69
+ panel_set_node_title: "graph_set_title",
70
+ panel_set_node_collapsed: "graph_set_node_collapsed",
71
+ panel_set_node_mode: "graph_set_node_mode",
72
+ panel_set_node_color: "graph_set_node_color",
73
+ panel_enter_subgraph: "graph_enter_subgraph",
74
+ panel_exit_subgraph: "graph_exit_subgraph",
75
+ panel_move_rail: "graph_move_rail",
76
+ panel_promote_widget: "graph_promote_widget",
77
+ panel_expose_subgraph_output: "graph_expose_subgraph_output",
78
+ panel_expose_subgraph_input: "graph_expose_subgraph_input",
79
+ panel_unexpose_subgraph_output: "graph_unexpose_subgraph_output",
80
+ panel_unexpose_subgraph_input: "graph_unexpose_subgraph_input",
81
+ panel_unpack_subgraph: "graph_unpack_subgraph",
82
+ panel_update_node: "graph_update_node",
83
+ };
84
+ /**
85
+ * #1935 — the READ / view-only tools that agent-facing refusals may advertise,
86
+ * keyed to the bridge command each dispatches.
87
+ *
88
+ * Separate from RETRY_TOKEN_CMD_BY_TOOL on purpose: a read must never mint a
89
+ * retry token (that map's own doc). The instance-mismatch refusal in ui-bridge
90
+ * still has to NAME these, and the mapping is NOT the `panel_` prefix
91
+ * mechanically applied — `graph_query`'s tool is `panel_query_graph`, not
92
+ * `panel_graph_query`.
93
+ *
94
+ * `graph_get_state` is omitted: it is an internal pre-0.8.2 fallback
95
+ * (panel_strip_workflow reconstruction), not a registered tool. Naming it would
96
+ * send the agent to an "unknown tool" the same way the bare bridge commands did.
97
+ */
98
+ export const GRAPH_READ_CMD_BY_TOOL = {
99
+ panel_graph_outline: "graph_outline",
100
+ panel_query_graph: "graph_query",
101
+ panel_get_errors: "graph_get_errors",
102
+ panel_find_nodes: "graph_find_nodes",
103
+ panel_list_subgraphs: "graph_list_subgraphs",
104
+ panel_screenshot: "graph_screenshot",
105
+ panel_canvas: "graph_canvas",
106
+ };
107
+ /**
108
+ * #1933 — the READ TOOLS the QUEUE BUSY refusal advertises, by their REGISTERED
109
+ * tool name.
110
+ *
111
+ * They must be tool names, not the `graph_outline` / `graph_query` /
112
+ * `graph_get_errors` bridge commands they dispatch: this string is the whole
113
+ * recovery contract for a fenced write, and an agent that follows it calls what
114
+ * it is told to call. A bridge command is not callable, so the advice returned
115
+ * "unknown tool" — a hard failure mid-degraded-state that the agent cannot
116
+ * diagnose, and from which it can only conclude that reads are unavailable
117
+ * during a render, which is precisely the state panel#1489 fixed and this
118
+ * sentence exists to advertise (reported from the field in
119
+ * artokun/comfyui-mcp-panel#1549).
120
+ *
121
+ * Written out (not derived by prefixing `panel_`) because `graph_query`'s tool
122
+ * is `panel_query_graph`. `graph-read-during-render.test.ts` asserts every one
123
+ * of them is in `buildPanelToolDefs()` — the registry the caller actually
124
+ * dispatches against. That test is what stops this list from drifting again:
125
+ * the old literal was green for the whole of its life with all three names
126
+ * wrong, because the assertion matched `/graph_outline/`, which a wrong name
127
+ * and a right one both satisfy.
128
+ *
129
+ * #1935 reuses this same list in the workflow-instance-stamp refusal's
130
+ * refused/unknown branches, rather than a third hand-written name list.
131
+ */
132
+ export const QUEUE_BUSY_READ_TOOLS = [
133
+ "panel_graph_outline",
134
+ "panel_query_graph",
135
+ "panel_get_errors",
136
+ ];
137
+ /**
138
+ * #1933/#1935 — the reverse of RETRY_TOKEN_CMD_BY_TOOL ∪ GRAPH_READ_CMD_BY_TOOL:
139
+ * which TOOL does a caller reach this bridge command through?
140
+ *
141
+ * An agent-facing message written below the tool layer can only see `cmd.cmd`;
142
+ * `PanelToolCtx.call` takes a command object and no tool identity, so by the
143
+ * time a pre-dispatch refusal is worded the tool name is genuinely gone. This
144
+ * recovers it from the maps that already maintain the correspondence, rather
145
+ * than adding another hand-written list that can drift out of step with the
146
+ * first.
147
+ *
148
+ * IT REFUSES TO GUESS. The forward maps are not injective — `graph_load` is
149
+ * dispatched by both `panel_load_workflow` and `panel_flatten_workflow` — and
150
+ * naming one of two possible tools is a confidently wrong answer, which is worse
151
+ * than declining to name one. Ambiguous commands are therefore OMITTED, and
152
+ * callers must handle `undefined`.
153
+ *
154
+ * Coverage of mutations is not assumed either: every `targeted` GRAPH_CMD_EFFECT
155
+ * command except `graph_run` is a value of the retry map, so the queue-busy
156
+ * fence resolves a tool for everything it can refuse except `graph_load`.
157
+ * `graph-read-during-render.test.ts` pins both halves — the resolution and the
158
+ * refusal to guess. Reads resolve through GRAPH_READ_CMD_BY_TOOL; `graph_get_state`
159
+ * is intentionally absent (no callable tool).
160
+ */
161
+ export const PANEL_TOOL_BY_GRAPH_CMD = (() => {
162
+ const claims = new Map();
163
+ for (const map of [RETRY_TOKEN_CMD_BY_TOOL, GRAPH_READ_CMD_BY_TOOL]) {
164
+ for (const [tool, cmd] of Object.entries(map)) {
165
+ const prior = claims.get(cmd);
166
+ if (prior)
167
+ prior.push(tool);
168
+ else
169
+ claims.set(cmd, [tool]);
170
+ }
171
+ }
172
+ const unique = new Map();
173
+ for (const [cmd, tools] of claims)
174
+ if (tools.length === 1)
175
+ unique.set(cmd, tools[0]);
176
+ return unique;
177
+ })();
178
+ /** #1933/#1935 — the registered tool a bridge command is reached through, or
179
+ * `undefined` when no tool, or more than one tool, claims it. */
180
+ export function panelToolForGraphCmd(cmd) {
181
+ return PANEL_TOOL_BY_GRAPH_CMD.get(cmd);
182
+ }
183
+ //# sourceMappingURL=panel-graph-cmd-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panel-graph-cmd-tools.js","sourceRoot":"","sources":["../../src/services/panel-graph-cmd-tools.ts"],"names":[],"mappings":"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"}
@@ -25,6 +25,7 @@ import { describePanelUpdateRecovery, } from "./panel-recovery.js";
25
25
  import { primePanelBase, verifiedPanelDiskVersion } from "./panel-workspace.js";
26
26
  import { compareSemver, detectInstallMode } from "./self-update.js";
27
27
  import { SHARED_SESSION_SCOPE, isScopeAddress } from "./session-scope.js";
28
+ import { QUEUE_BUSY_READ_TOOLS, panelToolForGraphCmd, } from "./panel-graph-cmd-tools.js";
28
29
  export const DEFAULT_BRIDGE_PORT = 9101;
29
30
  /** Panel build that first implements the full graph_* / ui_* bridge command set.
30
31
  * Used as the DEFAULT minimum for any bridge command not listed in
@@ -4160,22 +4161,46 @@ export class UiBridge {
4160
4161
  const PROBE = `Try panel_list_workflows — the panel exempts that read from this fence (it is the ` +
4161
4162
  `recovery probe), though a build predating the exemption fences it too. Non-graph ` +
4162
4163
  `tools are unaffected.`;
4164
+ // #1935 — name TOOLS, not the bridge commands they dispatch. An agent
4165
+ // that follows this recovery advice calls what it is told to call, and
4166
+ // none of graph_outline / graph_query / graph_get_state is registered.
4167
+ // The mapping is not a mechanical `panel_` prefix (graph_query →
4168
+ // panel_query_graph), so names come from panelToolForGraphCmd /
4169
+ // QUEUE_BUSY_READ_TOOLS rather than a third hand-written list.
4170
+ // graph_get_state is an internal pre-0.8.2 fallback with no callable
4171
+ // tool — the reverse index omits it, so it drops out of the execute
4172
+ // list rather than being advertised.
4173
+ const readTools = QUEUE_BUSY_READ_TOOLS.join(" / ");
4174
+ const viewTools = [
4175
+ "graph_outline",
4176
+ "graph_query",
4177
+ "graph_get_state",
4178
+ "graph_find_nodes",
4179
+ "graph_list_subgraphs",
4180
+ "graph_screenshot",
4181
+ "graph_canvas",
4182
+ ]
4183
+ .map((c) => panelToolForGraphCmd(c))
4184
+ .filter((n) => n !== undefined)
4185
+ .join(", ");
4163
4186
  const readsNote = readsVerdict === "refused"
4164
4187
  ? `GRAPH READS ARE REFUSED TOO, for this same missing stamp: this tab's panel ` +
4165
4188
  `enforces the per-command fence and refuses an UNSTAMPED command rather than ` +
4166
- `fail open, so graph_outline / graph_query answer "workflow instance mismatch: ` +
4189
+ `fail open, so ${readTools} answer "workflow instance mismatch: ` +
4167
4190
  `this command carries no workflow-instance stamp" as well. ${PROBE}`
4168
4191
  : readsVerdict === "unknown"
4169
4192
  ? `WHETHER GRAPH READS STILL WORK IS NOT KNOWN FROM HERE, and is not claimed: a ` +
4170
4193
  `read carries this session's stamp (${trustedStamp}), and this tab's panel runs ` +
4171
4194
  `it only while that stamp still names the ACTIVE canvas — a comparison only the ` +
4172
4195
  `panel can make. If the workflow was switched or replaced after this session ` +
4173
- `bound to it, graph_outline / graph_query are refused with "workflow instance ` +
4196
+ `bound to it, ${readTools} are refused with "workflow instance ` +
4174
4197
  `mismatch" as well; if it was not, they work. ${PROBE}`
4175
- : `Reads and view-only commands still work (graph_outline, graph_query, ` +
4176
- `graph_get_state, graph_find_nodes, graph_list_subgraphs, graph_screenshot, ` +
4177
- `graph_canvas).`;
4178
- const refusal = markDispatched(new Error(`"${cmd.cmd}" cannot be safely targeted to the active workflow: ${why}. ${readsNote}`), false);
4198
+ : `Reads and view-only commands still work (${viewTools}).`;
4199
+ // Same defect on the SUBJECT: `cmd.cmd` is the bridge command the tool
4200
+ // dispatched (observed: panel_add_node arrives here as graph_add_node).
4201
+ // Ambiguous commands fall back to a subject naming no tool at all.
4202
+ const subject = panelToolForGraphCmd(typeof cmd.cmd === "string" ? cmd.cmd : "") ?? "This command";
4203
+ const refusal = markDispatched(new Error(`"${subject}" cannot be safely targeted to the active workflow: ${why}. ${readsNote}`), false);
4179
4204
  return Promise.reject(capabilityMissing ? markCapabilityRefusal(refusal) : refusal);
4180
4205
  }
4181
4206
  }