# Changelog All notable changes to this project are documented here. This project adheres to [Semantic Versioning](https://semver.org/) and the format follows [Keep a Changelog](https://keepachangelog.com/). ## [Unreleased] ### Fixed - Reconnect reconciliation: the "outcome could not be confirmed" and "cancelled while disconnected" notices now send the run's `prompt_id` as a field, so comfyui-mcp correlates them to the run they are about instead of reporting "the panel reported NO prompt id for this run" and leaving the run's ticket open (comfyui-mcp#2989). ### Fixed - panel_set_widget no longer refuses a number written to a backend INT/FLOAT input whose live canvas widget an extension has rebuilt as a label combo. Core `ImageFromBatch.length` is `["INT", {...}]` in `/object_info`, but on the reporter's canvas it rendered as a duration dropdown (`custom`, `30 seconds`, `1 minute`, …), and the integer 16 was refused with `Value 16 is not a valid option for combo widget "length"` listing presets no backend input accepts. The write now consults the freshly fetched server schema for the exact class and concrete input on every write attempt: a declared INT or FLOAT means a combo-shaped live widget is validated and written as a number (the reply discloses `backend_declared_type`), and a non-numeric value — including one of the artifact's own labels, which the live list used to accept and serialize as a string into an INT input — is refused terminally naming the backend type. A server-declared combo (V1 or V2), a STRING input, or a snapshot-only schema leave the write unchanged and still refuse an off-list number (comfyui-mcp#2988) ## [0.15.184] - 2026-10-14 ### Added - Settings › Ollama › Context window: Auto (the model's native window, up to 64K), Max (its full window), or a fixed 16K–256K. Applies from the next message; needs comfyui-mcp 0.52.206 or later. ## [0.15.183] - 2026-10-01 ### Changed - Resume limited maintenance of the local panel and provider choice; remove the October 9 shutdown commitment and keep automatic issue reporting disabled. Reassess support when Comfy's official local panel becomes publicly available. - Supersede the September archive notice with the narrower support policy (#2300). ### Fixed - Unpacking follows reminted interior-node identities and named slots, repairs displaced wires inside one undo step, and rolls back when identity cannot be proven. Temporary provenance never enters saved workflows or undo history (#2311). - Lost subgraph-conversion replies recover only from an exact live-graph receipt and recheck serialization; graph reloads and partial selections fail closed rather than duplicating conversions. - Workflow mismatch and reconnect guidance protect unsaved edits and no longer recommend reopening the active tab. The underlying frontend provenance defect remains unresolved. - Error scans reject detached node objects even when another workflow reuses their IDs, and a readable empty canvas clears stale missing-type records. - Generated custom-widget refresh passes serialized widget values to configuration hooks; existing retention verification rejects values overwritten by those hooks. ## [0.15.182] - 2026-09-07 ### Fixed - interactive ask/question cards now retain their URL+epoch correlation across a same-session reconnect, replay the pending answer only to that proven session, and withdraw mismatched cards without painting duplicates (#2218) ## [0.15.181] - 2026-09-07 ### Fixed - the link-id repair now reads the LEGACY link store too, so it applies on older LiteGraph builds (#2108). highestLinkId read only `_links`, the modern private Map; older builds expose the plain record as `links`, which the rest of the panel already handles (disconnect-verify, connect-verify). On those builds the helper returned null, so `adjusted` was false and the stale counter was never raised — the overwrite this fix exists to prevent survived it, silently, because "no links" and "no store" gave the same answer. Adds execution tests that mint an id after the repair and assert it is free, rather than only reading the bundle for call sites. Found by the Copilot review on the PR - panel_connect no longer overwrites an unrelated link (#2108, #2196). A link id is minted - panel_paste_nodes raises the counter too — it was the one allocating path still - the panel no longer REQUESTS emoji presentation for its warning glyph (panel#2023). Nine - the VARIATION SELECTOR removal now covers every shipped web/js file rather than the main bundle alone. This drops U+FE0F only, which affects text-default glyphs such as the U+26A0 warning sign; it does NOT remove the panel emoji-font dependency, since 44 emoji-presentation-default characters remain in web/js and 16 more in each of the 12 shipped locale catalogues (nine more selectors still rendered from web/js/lib), and the chat strip patterns match the current spelling again — dropping the selector from the emitted warnings had silently killed them, so agent-only GRAPH VALIDATION ERRORS / MISSING ASSETS / LAST RUN FAILED blocks were reaching the user (#2023) - the CivitAI sign-in timeout no longer recommends a setting that cannot sign the browser in (#2044). It pointed at the CivitAI API token setting; `py/civitai_proxy.py` authenticates the browser solely from its OAuth token file and never reads CIVITAI_API_TOKEN — the name occurs there only inside a docstring — so the advice named a remedy for a different subsystem. The message now names --enable-cors-header, whose absence installs the origin-only middleware that produces the 403, and says the token setting will not help. It also no longer rides the 3.5s toast default: it is the only explanation the user gets after a four-minute wait - CivitAI sign-in now says when it did not complete (panel#2044). The poll gave up after - the sign-in timeout notice names the token setting by REFERENCE, not by copying its - the unknown-staleness hint no longer recommends a save that can destroy the change it warns about (#2139). Both arms said "save first"; classifyInPlaceOverwrite returns "skip" for a tab already isPersisted, so an ordinary save of a persisted tab overwrites in place WITHOUT reading disk — and this branch exists precisely because the on-disk contents could not be established, so the advice could clobber an external edit. Both arms now point at a NEW path, which keeps both versions. The arm test also pinned only the presence of both strings, so a negated condition would have swapped dirty and clean advice undetected; it now pins the mapping. Found by the Copilot review on the PR - panel_run keeps keyed completion output buffered when lifecycle-start, prompt-receipt, or subsequent prompt-start frames are delayed, including output that arrives after the bounded queue dispatch returns, so timer, queue-idle, sequential-start, and terminal-before-receipt paths cannot emit an unkeyed fallback before the keyed replay; legacy unkeyed orphan salvage remains unchanged (#365) - a stale socket flag refused every panel_run while everything else worked (#2248) ## [0.15.180] - 2026-09-07 ### Fixed - Dynamic widget recovery now preserves same-value relocation cleanup and re-resolves shallow-to-deep live widgets during queue restore; panel_run seals DynamicCombo setters before restore and resolves detached children to their live replacements (#2033/#2140) - panel_connect accepts an Autogrow display-label alias (`ref_image_0` → `ref_images.ref_image_0`) instead of refusing with a false "no input accepts type IMAGE"; the resolved live slot name still feeds dotted-name reconcile, and an unmatched name reports internal slot names rather than blaming the origin type (#2273) ## [0.15.179] - 2026-09-05 ### Fixed - panel_set_widget combo refusals list the live options for generic enums (device/precision) instead of applying the private filename/path redaction to every combo (#2265, #2271) ## [0.15.178] - 2026-09-05 ### Fixed - fetch_image no longer sends `/view` through ComfyUI `api.fetchApi`, which prefixes `/api` so local media became `/api/view` and failed with `Failed to fetch` after headless ECONNREFUSED. `/view` uses origin-validated `fileURL` + same-origin fetch (and `Comfy-User` when the API object has a user). History / system_stats keep `fetchApi` so cloud auth headers and 401 remint stay intact (comfyui-mcp#2884, #2261) ## [0.15.177] - 2026-09-05 ### Fixed - panel_set_widget wraps live COMFY_DYNAMICCOMBO_V3 parents before the write so a Vue/widget-store flush cannot rebuild dotted FLOAT children from spec defaults while the receipt still says applied:true; panel_query_graph then sees the value that was written (#2031) - Save-As after panel_refresh_nodes proves destination canvas identity before refusing, recaptures the active tracker (and reseals a missing root uuid) so refresh does not invalidate content identity, and a failed copy's source restore actually runs then recaptures so the next graph read is not root-shape-mismatch (#2257) - panel_run no longer refuses forever on a STALE socket flag (#2854). ComfyUI arms comfyBackendSocketDown from a failed _pollQueue during a long GPU-bound render, and `reconnected` never fires because the websocket never left OPEN -- so the flag never cleared. Every other panel_* path asks comfyBackendIsDown(), whose #1325 rule is that flagged-down over an OPEN socket is stale rather than down; the run-dispatch identity read the raw flag instead, so it alone reported "backend socket down" and refused every dispatch while reads, edits and the ComfyUI queue all worked. Fail-closed directions are unchanged: the flag with a non-OPEN or unreadable readyState still refuses ## [0.15.176] - 2026-09-05 ### Fixed - After an already-open panel_open_workflow applies last_open, panel_graph_outline / panel_query_graph refuse a leftover previous-tab or archived canvas when the live graph is smaller than the named file — failed switch repaint and another open tab matching the live root stay fail-closed; fixtures without leftover proof stay available (#1215, #2255) ## [0.15.175] - 2026-09-05 ### Fixed - panel_run refuses a stale live browser bundle with a Ctrl+Shift+R hard-refresh requirement instead of silently dropping dispatch; unverified scoped pending items are still removed fail-closed (#2252, #2253) ## [0.15.174] - 2026-09-04 ### Fixed - After a delivered panel_open_workflow, the switch fence no longer latches panel_list_workflows / panel_set_workflow_target({mode:"current"}) / panel_graph_outline for minutes while a later settle or safe-repaint is still pending. workflow_list stays the recovery probe; leftover previous-tab graph is still refused (#2249, #2250, #1215) ## [0.15.173] - 2026-09-04 ### Fixed - panel_get_errors no longer reports a clean graph when missing models are still named on a promoted native-subgraph host rail: the load-time store blames the inner locator, and the inner widget may hold an on-disk fallback while the host still names the absent file. The live combo scan now also walks nested subgraphs from the bound root, so inner loaders are judged even when the user is looking at the root canvas (#984, #2245) ## [0.15.172] - 2026-09-04 ### Fixed - panel_run compiles promoted host-rail width/height and linked PrimitiveNode/GetNode primitive values into the flattened subgraph prompt so inner stored fallbacks do not execute; GetNode/SetNode tensor buses are not reported as dropped value sources (#1181, #2243) ## [0.15.171] - 2026-09-04 ### Fixed - panel_run recovers the ComfyUI prompt_id after a post-dispatch `/prompt` fetch failure by reconciling a client-generated dispatch id against queue/history, and treats a confirmed miss as safe to retry (#2203, #2215) - the post-dispatch receipt recovery no longer treats a shared queue mark as a per-request receipt, no longer reads a FAILED /queue or /history response as an empty one, no longer loses an acceptance whose id was recovered after a malformed 200, and states plainly that a confirmed miss is a bounded observation rather than promising a retry is safe (#2203, #2215) - Preserve the panel-wide Codex conversation across a workflow remount when the canonical IndexedDB history read times out or is otherwise unavailable; only a confirmed empty archive may clear the transcript and session (#2201, #2214) - the canonical chat-history read is decided by the IndexedDB TRANSACTION rather than the request, so a get that succeeds before its transaction aborts is no longer treated as authoritative; and a delayed hydration retry no longer reselects the mount-time thread over a chat the user picked while the store was recovering (#2201, #2214) ## [0.15.170] - 2026-09-04 ### Fixed - After panel_open_workflow switches tabs but cannot repaint, panel_graph_outline / panel_query_graph refuse instead of serving the previous tab's graph under the new workflow's fence — panel_set_workflow_target is not a remedy (#1215, #2238) ## [0.15.169] - 2026-09-04 ### Fixed - graph_set_widget acknowledges a long CLIPTextEncode / multiline text write as soon as the live editor holds the value, instead of waiting on a backgrounded-tab rAF flush until the 90s relay times out (#2233, #2236) - Refused completion retries of a finished video reuse the composed storyboard identity and skip re-upload, so a down bridge cannot fill ComfyUI/temp with unique `storyboard_*.png` / `poster_*.png` copies every sweep (#2234, #2235) ## [0.15.168] - 2026-09-04 ### Fixed - After entering a MiniMax H3 subgraph and replacing inner loaders, graph_get_subgraph still classifies the HOST wrapper and publishes a complete promoted-terminal witness for `value` / `value_2`, so panel_set_widget can dispatch instead of treating the in-subgraph lookup as an unclassifiable container (#2057, #2231) ## [0.15.167] - 2026-09-04 ### Fixed - fetch_comfyui_read keeps the Comfy API object as `this` when calling apiURL/fileURL, so frontend helpers that read `this.api_base` still resolve after restart instead of throwing "Cannot read properties of undefined (reading 'api_base')" (#2228, #2229) ## [0.15.166] - 2026-09-04 ### Fixed - After panel_open_workflow reopens a modified subgraph, promoted width/height/seed host rails rebind onto the unique inner input-rail slots and graph_get_subgraph publishes a complete promoted-terminal witness, so panel_set_widget can write those widgets instead of refusing an unresolved mapping. panel_refresh_nodes runs the same rebind. A stale properties.proxyWidgets pair from the file is not a veto once the live parent rail is authenticated (#2225, #2226) ## [0.15.165] - 2026-09-04 ### Fixed - panel_set_widget refreshes LoadImage/LoadVideo combo choices from the connected ComfyUI `/object_info/` input-file inventory after a combo miss, invalidates the cached whole-map list, and accepts only the exact uploaded relative filename — so a file `upload_image` just verified is not rejected against a stale page-load dropdown (#2222, #2223) ## [0.15.164] - 2026-09-03 ### Fixed - The agent TODO tray can be collapsed from its Plan header (and re-expanded) so a forgotten panel_set_todo no longer pins up to 9rem of the chat column; empty panel_set_todo still clears it (#2165, #2219) ## [0.15.163] - 2026-09-03 ### Fixed - A custom video save node no longer looks like a preview-only run: a `{filename, subfolder, type:"output"}` MP4 under an unrecognised key (NKDVideoViewer's `nkd_video`) is collected as saved output, so panel_run completion names the file instead of saying "no saved output node ran … Add a SaveImage node" while describing the preview taps. CompareFrames temp dumps and a genuinely preview-only run are unchanged (#2128) - graph_get_subgraph no longer caps its inner node list at MAX_STATE_NODES, so a large subgraph's ownership envelope stays `truncated:false` with `node_count === nodes.length` and panel_set_widget can dispatch a promoted write instead of treating the listing cap as an incomplete witness (#2057) ## [0.15.162] - 2026-09-03 ### Fixed - After a large custom-node install, adding a node waits for /object_info with an adaptive command-budget cap instead of a fixed 10s fetch, and still refuses if the schema never arrives (#2050) - panel_show_media splits a combined `video/` output filename into `/view`'s `subfolder` + basename so a valid history reference no longer 404s (#2193) ## [0.15.161] - 2026-09-03 ### Fixed - panel_save_workflow restamps a leftover nested `extra.comfyui_mcp.workflow_path` when the canvas uuid already matches the tab, and ImpactSwitch `findInputSlot` restore failures no longer dead-end that rebind behind an unrestorable open (#2194, #2206) ## [0.15.160] - 2026-09-03 ### Fixed - fetch_comfyui_read now admits the closed `models` and `models/` inventory operations so list_local_models can relay a reachable remote ComfyUI through the live panel instead of dying on the diagnostics-only allowlist (comfyui-mcp#2511) - admit models inventory through fetch_comfyui_read (#2198) - panel_get_errors stops reporting validation errors the live graph disagrees with (#2195) ## [0.15.159] - 2026-09-02 ### Fixed - rehydrate loaded node defs before run (#2188) ## [0.15.158] - 2026-09-02 ### Fixed - panel_create_subgraph and panel_subgraph_group now preflight the full transitive upstream chain for detached nodes and refuse before ComfyUI's conversion can throw or partially mutate the live graph; graph-ownership and cycle guards preserve valid downstream and unlinked selections, while measured throw verdicts remain in place (#1463, #2186) ## [0.15.157] - 2026-09-02 ### Fixed - panel_set_widget on rgthree Fast Bypasser/Muter rows now applies the requested mode idempotently through the forced-value path, authenticates row-to-node closures, and journals all reachable mode changes for fail-closed rollback (#2151) ## [0.15.156] - 2026-09-02 ### Fixed - panel_add_node reuses the freshly fetched node definition when repairing stale registered schemas, so a stale-bundle refusal on a second whole-schema refresh cannot block LoadImage (#2124, #2181) ## [0.15.155] - 2026-09-02 ### Fixed - panel_search_nodes keeps serialized browser transport failures classified and diagnosable when ComfyUI-Manager mappings returns no HTTP response (#2024) ## [0.15.154] - 2026-09-02 ### Fixed - allow workflow template reads - prioritize live error scans in get_errors ## [0.15.153] - 2026-09-02 ### Fixed - make restore callback preflight directional - prevent ImpactSwitch restore callback crash ## [0.15.152] - 2026-09-02 ### Fixed - retain current promoted DOM rails (#1707) ## [0.15.151] - 2026-09-02 ### Fixed - The panel stops flooding ComfyUI's log ring buffer with bridge advertisements. The orchestrator re-POSTs `/comfyui_mcp_panel/advertise_bridge` on a deliberate 5 s heartbeat and again on every panel hello (it self-heals an advertise lost across a pod-side ComfyUI restart, which otherwise leaves a stale token no browser refresh can recover from), and the pack logged `secure bridge advertised` / `local bridge advertised` for every accepted POST. That is ~1440 lines an hour into the size-capped deque ComfyUI serves from `GET /internal/logs`: a reporter diagnosing a `LoadImageOutput` node fetched 25,006 characters that were 100% panel heartbeat, with every startup, model-load and traceback line already evicted, on a remote Colab instance with no filesystem fallback. The advertisement is now announced on a CHANGE only - first establish, a new tunnel, or a reconnect that moves the loopback port - which also collapses the seven-deep bursts a fresh connection emitted within ~10 ms. The heartbeat itself is untouched; it is load-bearing and belongs to the orchestrator. Separately, EVERY panel log line now costs one ring entry instead of two: ComfyUI's `LogInterceptor` appends one timestamped entry per `sys.stdout.write()` call, and `print()` writes the text and the newline separately - so each line was stored unterminated, with the following entry's timestamp visibly concatenated onto it (`...trycloudflare.com/2026-09-01T19:07:18.981112 - `), followed by a second, empty entry. `print(..., end="")` does not fix that (it still writes the empty terminator as a second call); the line is now emitted in a single write (#2162) - panel_run(to_node_id) stops refusing every scoped run on a workflow containing a random-mode DaSiWa_SeedControl. That node rolls its seed by wrapping `app.graphToPrompt` itself, and the panel's pre-dispatch graph stamp builds its fingerprint BY calling `app.graphToPrompt` — so the stamp rolled one seed and the dispatch rolled a different one, and the two serializations disagreed on `seed_value` and `seed_control_state` every time, on a completely idle canvas. The panel refused with `the workflow graph CHANGED after the run was queued. The differing entries: 2739 seed_control_state; 2739 seed_value` and queued nothing; because the roll fires once per serialization, neither the panel's own restamp nor the orchestrator's re-issues could ever converge, so all three dispatches lost and run-to-node was unusable on those graphs. The queue-time volatile-input walk now recognizes this pack as a seventh volatility signal, alongside rgthree (#1124), cg-use-everywhere (#1273), VHS (#2099) and Ideogram (#2130). The gates mirror the pack's own source one for one: the exact node class, both backing widgets present, no external `seed` link, and a mode that is not the literal `"fixed"` — an absent, empty or unparseable state widget is RANDOM and does roll, which is the freshly-dropped node. Drift coverage is otherwise unchanged: a fixed-mode or externally-driven SeedControl keeps both inputs hashed, and any other edit in the same window (including on the same node) is still detected and still named (comfyui-mcp#2712) - panel_reload({scope:"frontend"}) now acknowledges a successful soft reload before cache-busted navigation, with a final fail-closed workflow fence so stale-bundle recovery does not time out on the socket it is replacing (#584) - panel_set_widget on an rgthree `Fast Bypasser (rgthree)` / `Fast Muter (rgthree)` row no longer INVERTS the linked node it was asked to set. These are not the group rows #2146 covers: they are plain toggles, one per node wired into the changer's inputs, named `Enable ` — which is how the reported row "Enable LC Film Stock (B&W)" is addressed on a Fast Bypasser retitled "LC Bypasser". The pack gives each row `widget.callback = () => widget.doModeChange()`, with NO arguments, and `doModeChange` derives its new value from the LINKED NODE'S CURRENT MODE when none is passed — so the row's callback is a toggle, not a setter, and the ordinary assign-then-fire-the-callback path ignored the value that had just been written. Whenever the requested value already agreed with the row, the write flipped the graph the other way: asking to disable an effect that was already bypassed set that node back to ALWAYS, and the read-back then failed and rolled the ROW value back while nothing rolled the node's MODE back — so the caller was told the write failed while the effect they asked to disable was live, and the next queued render ran it. Asking to enable an already-enabled row bypassed it, the same way. The write now drives the pack's own forced-value entry point, `doModeChange(value)` — the one `forceWidgetOn`/`forceWidgetOff` use — so setting a row to the value it already holds is an idempotent no-op that also repairs a linked node whose mode had drifted (bypassing a node on canvas moves its mode while the row keeps its old value, because the pack re-syncs a row only when its NAME changes). The modes the action can reach are journalled first — the nodes on the changer's inputs, through Reroute/Node Combiner/Node Collector, into subgraphs, and onward through a Mute / Bypass Repeater's propagation — so a write that fails verification restores every mode it moved instead of leaving the graph half-switched; a boundary that cannot be established refuses BEFORE the action runs rather than mutating modes this writer could not put back. The row's own value is no longer assigned by the writer at all, which is what makes the read-back evidence that the action ran: `doModeChange` sets it itself, on the line after it changes the mode. A `toggleRestriction` of "max one"/"always one" is left to the node to enforce exactly as a click would, and a row it refuses to switch off is reported as a failed write rather than claimed as applied. Every other widget on those nodes, and the Fast Groups Bypasser/Muter rows, are unchanged (#2151) - retain VHS dimensions across format updates (#2167) - refuse empty graph reads with missing-node state (#2166) - treat DaSiWa_SeedControl's queue-time seed roll as volatility, not graph drift (#2163) - classify and MEASURE a failed workflow switch instead of asserting it (#2159) ### Changed - pin the #2148 repair on a promoted MULTILINE rail (#2160) - a missing completion receipt is not only an old orchestrator (#2156) ## [0.15.150] - 2026-09-01 ### Fixed - panel_run now rechecks reconnect, bridge-route, workflow, and run-to-node identity after preflight and across queue dispatch, refusing before an unstable dispatch and returning `queued_unknown` with the concrete prompt receipt when a handoff crosses an accepted run (#166) - panel_run(to_node_id) stops refusing every scoped run on a workflow that instantiates one subgraph definition more than once. ComfyUI subgraph instances share a single definition object, and the queue-time volatile-input walk that keeps a scoped run's graph stamp stable deduped by graph OBJECT — so a definition used twice was walked once, and every exclusion landed on the FIRST instance's exec-id prefix only. On a MiniMax H3 graph whose second pass is a second instance of the first pass's subgraph, the #1331 leftover link-driven `model` widget was excluded at `100:29` and still hashed at `135:29`, so the panel reported `the workflow graph CHANGED after the run was queued. The differing entry: 135:29 model` on an idle canvas, for the first dispatch and the post-settle retry alike, and queued nothing. The walk now runs once per INSTANCE, carrying that instance's prefix, guarded against a cycle on its own ancestor path rather than by graph identity — no depth cap, because a cap that tripped would drop the deepest subgraph's exclusions and reintroduce this same permanent refusal one level further down. Drift coverage is unchanged per input: an edit inside the second instance is still detected, and still named at the instance that changed (comfyui-mcp#2699) - panel_add_node no longer refuses with "the active workflow or graph view changed" on a frontend that exposes no active workflow: absent-at-both-ends is unchanged, not a tab switch, so the mutation runs on the same canvas the reads already succeeded on (#2125) - A SaveAudio render is reported instead of dropped: ComfyUI's `audio` output bag is collected, so an audio player is painted in chat and the run's completion frame names the files rather than telling the agent the run "produced no image or video output" and that no output node produced a file. Audio is named, never attached — the agent still cannot hear it (#2126) - A run whose result is a 3D model no longer reports the opposite: ComfyUI's two 3D output shapes are collected (`SaveGLB`'s `3d` descriptors, and the bare path string `Save3DAdvanced` / `SaveGaussianSplat` / `SavePointCloud` / `Preview3D` put in `result`), so a workflow ending in `Save3DAdvanced` names the `.glb` it wrote instead of saying "no saved output node ran ... Add a SaveImage node". The completion note no longer infers that claim from the image set alone, so a preview-tapped audio run stops making it too; a genuinely preview-only run still gets the original advice. 3D is named, never attached (#2128) - panel_save_workflow no longer reports `saved:true` over a file that is missing what the canvas has: the stale-snapshot save guard used to require one of ComfyUI's suppression flags to still be set at write time, so a capture swallowed a moment earlier — or by one of the two early returns the flag model cannot see (`!app.graph`, and the `isActiveTracker` check that also makes `prepareForSave()` a silent no-op) — let the write through. It now also accepts ComfyUI's own `ChangeTracker.graphEqual`, the comparator the capture itself uses, still reporting a difference after the refresh was requested. Presentation-only drift (a dragged node) and frontends without that comparator save exactly as before (#2133) - panel_restart_comfyui no longer refuses with "this is a ComfyUI Desktop instance, and no Desktop relaunch path ... is available" on an instance that is not Desktop: the #1999 guard proved Desktop with `isEmbeddedDesktopShell`, whose `Electron/` User-Agent arm answers a question about the browser, so an ordinary ComfyUI viewed through any Electron-embedded browser was refused a Manager reboot that would have worked. Desktop is now proven by the injected `electronAPI` bridge — the same evidence ComfyUI's own frontend uses, which never sniffs the User-Agent. The bridge is also read from every candidate global instead of the first non-nullish one, so a bridge carrying no restore function can no longer mask a later one that has `restartCore` / `restartApp` / `relaunchApp`. A real Desktop shell with no relaunch path still refuses before the stop, unchanged (#2134) - A prompt build no longer silently resets a nested DynamicCombo value, and a dynamic-widget serializer throw now names a node. The reconcile that runs before every `graphToPrompt` restored a dynamic root's dotted children from one lookup table built before the restore began, but restoring the shallowest child drives the frontend's native rebuild, which REPLACES every widget below it — so each deeper value was written to a widget the node was no longer carrying. SaveVideo's `format.codec.encoding.crf` was reset to the schema default by an ordinary run, and for a dynamic child the write drove a detached accessor, whose rebuild deletes the group's rows and their widget-store entries before it checks that it is still attached. That is also what separated the two recoveries in the report: a same-value `panel_set_widget` runs this restore and did not recover the node, a real `mp4 → mkv → mp4` round trip skips it and did. Each name is now re-resolved against the live widget list immediately before it is written. Separately, `Dynamic widget doesn't exist on node` used to reach the caller as a bare string whenever there was no orphan and no typed PrimitiveNode to name — `panel_get_errors` is clean for this failure, so there was nothing to bisect from. It now names any DynamicCombo root whose selected option declares a child row the node is not carrying (or a duplicated dotted row), and falls back to listing the graph's dynamic-combo nodes as candidates rather than naming nothing (#2140) - panel_set_widget can now address one of several widgets that share a name, and says which one it wrote. An rgthree Fast Groups Bypasser/Muter names every group-toggle row `RGTHREE_TOGGLE_AND_NAV`; `panel_query_graph`'s `duplicate_widgets` has reported each row with a stable index and its own label since #1402, but the write resolved by name alone and always took the FIRST — so the second group's toggle had no address at all, and on a Bypasser that row's action is what changes the modes of its group's nodes. Two addresses now reach a specific row, both carried by the existing `widget` string: `RGTHREE_TOGGLE_AND_NAV[1]` (composing with sub-fields as `[1].toggled`), and the display label when exactly one widget on the node carries it. The number in brackets is the widget's position in the node — the same `index` `duplicate_widgets` publishes — so a reported index pastes straight back as an address. An out-of-range index and an ambiguous label are refused by name rather than falling back to the first row, and every successful write to a duplicated name now reports `widget_occurrence: {index, of, label}`, whose index is the row’s position AFTER the write so it can be sent straight back as an address (flagged `stale` when the write’s own callback removed the row) — including the receipt a write that lands after the caller's timeout comes back with. Exact spelling still wins, so a widget genuinely named `foo[1]` is unaffected, and a duplicated widget whose own name contains dots (`foo.bar[1]`) is addressable too. A row that MOVED between the address being resolved and the write is refused rather than written over: the row object is pinned by identity, which catches a reorder even when the rows carry identical labels or none, and the label decides only when a rebuild has replaced the objects — if the label cannot say which row is which, the write is refused rather than landing on a coin flip. A promoted subgraph write refuses rather than dropping the index, and the deferred (`defer_until_idle`) path refuses an occurrence- or label-addressed write because its replay re-resolves by name (#2143, #1465, #524) - panel_set_widget can set a promoted subgraph STRING widget again. On the reported frontends the parent rail for a promoted prompt writes this wrapper's own promoted-value store — the entry queue compilation reads — and then ALSO assigns the shared subgraph definition's inner widget, which an on-canvas edit of the same control never does. The #1707 fence saw the definition move and rolled the whole write back rather than report an instance-scoped write that was not one, so a promoted `CLIPTextEncode.text` could not be written at all: not from the wrapper, and not by entering the subgraph, since that path resolves to the same rail. The collateral assignment is now UNDONE instead of the write being refused, and the result is only reported once both halves have been observed — the shared definition structurally back on its captured value, AND the rail and every parent-facing display proxy still holding the requested one. A rail that is genuinely one store with the definition fails that check (restoring the inner widget drags the rail back with it) and is refused exactly as before; so is an inner widget that will not take the restore, and a rail whose own store did not take the value but whose textarea shows it — the repair is judged on the per-widgetId store queue compilation reads, never on a DOM editor, so it can never erase the definition on evidence that the write landed nowhere durable. The refusal now names WHICH check blocked the repair, because the three have different remedies: a rail that came back with the definition is one store (unpack or edit the definition), an inner widget that will not take its own value back is an unrestorable definition, and a stale parent-facing display proxy is a PROJECTION a reopen rebuilds — not a shared store. A repair that cannot be verified leaves precisely the state the write left, so the refusal still describes the write rather than the repair. A successful repair reports `shared_definition_write_through: true` next to `value_scope: "instance"`, so a caller is told the definition was touched and put back rather than having to infer it. Every rail that does not write through replies exactly as it did before (comfyui-mcp#2689) - A refused promoted-widget write no longer leaves the SHARED subgraph definition holding the wrong value: on a frontend whose promoted rail writes through to the inner widget, the rollback restores the shared inner widget LAST, so the rail restore can no longer forward its own captured value back onto the definition every sibling instance reads. It was visible only when the rail and the definition had diverged, and was reported as "Rollback of inner ... did not take effect ... the graph may be in a partial state" (#2132) - panel_free_vram no longer reports `freed:true` with `branch: verified_system_stats` over occupancy it re-read and watched NOT move. ComfyUI's `POST /free` sets the `unload_models` / `free_memory` flags on its prompt queue and returns 200 immediately; the unload itself runs afterwards on the prompt-worker thread, which does not read those flags until the prompt it is currently executing finishes. The panel read `/system_stats` on the next line after that 200, so it raced the worker and photographed the pre-free numbers — the report's `before_mb 9426 → after_mb 9426, freed_mb 0` on a card a health call found at 10.7/12.0 GB free moments later. The reply now settles before it claims: occupancy is re-sampled every 250 ms for up to 5 s, the verified branch is reached only once it actually drops, and `waited_ms` / `samples` say what was watched. When the budget expires with occupancy unchanged the reply is `freed:false, outcome:"pending", branch:"unload_not_observed"` and explains why — a /free issued during a render is queued behind that render — explicitly separating it from the still-pinned device that would justify panel_restart_comfyui, and noting that /free is idempotent so re-issuing is safe. An occupancy read that MISSES still degrades to a receipt rather than failing a free that landed; a missed before-read is now `after_only_occupancy` rather than being labelled verified. The panel's own chat row stops saying "Unloaded models — freed VRAM" for a pending unload. Because those numbers now decide `freed`, they are compared per DEVICE rather than as a cross-device total: occupancy is matched by ComfyUI's `type:index` device identity (falling back to `name`) and summed only over the devices present, uniquely, in both readings, so a device that stops answering between the two reads can no longer shrink a total and look like an unload — with `cuda:0` at 8000 MB and `cuda:1` at 2000 MB before, and only `cuda:0` answering after, the reply is pending with `freed_mb: 0` instead of claiming 2000 MB freed. It takes one flaky row, not a removed GPU, since a device whose counters are unreadable is dropped from the reading — and a counter is now only read when it is actually numeric, so `vram_free: null` is an unreadable device rather than a fully occupied card. A `freed:false` reply also stops the orchestrator re-reading occupancy for its own pinned-device diagnosis (#1866/#1887 only runs when the panel claimed `freed:true`), so the panel now makes that call itself on the server its tab fronts: `/system_stats` carries `torch_vram_total` / `torch_vram_free` per device — THIS instance's allocator, unlike the device-global counters — so an unchanged reading whose torch pool is at least 1 GiB and at most 20% free is reported as `branch: torch_pool_pinned` naming those devices and pointing at panel_restart_comfyui, rather than as a pending unload to wait out. An empty or unknown torch pool stays pending, and the verified branch is untouched because the orchestrator's check still runs there. That pin reading is taken only on device types that actually report a torch allocator pool (`cuda`/`xpu`/`npu`/`mlu`): on `cpu` and `mps` ComfyUI fills `torch_vram_total` / `torch_vram_free` from `psutil.virtual_memory()`, so a CPU-only or Apple-Silicon install on a machine merely low on RAM would otherwise have been told to restart ComfyUI over another process's memory (#2144) - repair a promoted rail's write-through instead of refusing the write (#2148) - address a duplicated widget name by occurrence or label, and say which row was written (#2145) ## [0.15.149] - 2026-08-30 ### Fixed - Truncated or concatenated panel tool names return a malformed-name validation error instead of "unknown tool", and apply no mutation (#1297, #2122) - panel_set_widget clear of an already-empty combo (VHS_LoadVideo video="") succeeds even when the dropdown has no empty option (#2010, #2121) - a wildcard-to-wildcard connect refusal is reported as unbound * ports needing a concrete typed producer, not a false type mismatch (#2028, #2120) ## [0.15.148] - 2026-08-30 ### Fixed - panel_set_widget returns a late mutation receipt when graph_set_widget applies after the caller timeout, and retry_of replays that receipt by request id without a duplicate write (#2116) ## [0.15.147] - 2026-08-30 ### Fixed - panel_set_widget no longer refuses promoted subgraph widget writes on a bound live canvas when Web Locks cannot mint an exclusive tab identity: hello still publishes a page-lifetime connection identity, and fail-closed omission is kept only when identity is actually unavailable (#2104, #2114) ## [0.15.146] - 2026-08-30 ### Fixed - Generated images/videos no longer have to auto-post into the chat: a new Generated media in chat setting (on by default) can keep the transcript text-only without hiding agent-visible outputs, and Video previews in chat is labeled as inline playback so it is no longer mistaken for that switch (#2034) ## [0.15.145] - 2026-08-30 ### Fixed - panel_set_widget on a subgraph's promoted widget writes the enclosing container rail even when the call lands on the inner link-driven terminal, instead of reporting applied while the parent value stays stale (#2109) - live graph reads recover after a manual canvas edit: a hung panel_graph_outline no longer pins retries, tracker snapshot flush stays mutation-only, and image/canvas widget values are clipped without a full stringify that could miss the 20s RPC window (#2003) - panel_save_subgraph rewrites legacy proxyWidgets object/null metadata to the string-tuple schema before publishing, and refuses with the affected inner node/widget and a demote repair when that mapping cannot be proved lossless (#2005) ## [0.15.144] - 2026-08-30 ### Fixed - panel_set_widget acknowledges an inner-subgraph widget write once readback matches, instead of waiting out the 90s relay on a hanging widget callback or parent-rail restore (#2001) - panel_promote_widget refuses canvas-only callback widgets (control_after_generate) and writes only [nodeId, widgetName] string pairs through the legacy promotion store, so saved workflows stay loadable (#2002) ## [0.15.143] - 2026-08-30 ### Fixed - panel_open_workflow of a listed unsaved tmp: tab after reconnect no longer false-negatives: it rechecks the active routing key before failing, and an applied switch that is still unreadable returns the receipt rather than a hard error (#2022) - panel_copy_nodes with explicit node_ids no longer copies a leftover additive canvas selection (#2004) ## [0.15.142] - 2026-08-30 ### Fixed - chat autoscroll no longer yanks the transcript back to the bottom while the user is scrolling up: an upward gesture unsticks immediately, including inside the 48px slack, and the stabilizer waits until they return to the bottom or jump to latest (#2000) ## [0.15.141] - 2026-08-30 ### Fixed - panel_strip_workflow no longer fails when a newer MCP requires the panel's node schema: graph_get_object_info is a canvas-independent read (not a mutation), and graph_serialize keeps extra schema fields while attaching name-keyed widget values so conversion does not depend on positional schema agreement (#1996) - scoped panel_run keeps the run-to-node target when a queuePrompt wrapper drops the third argument, instead of relying only on request-body repair (#1998) - panel_restart_comfyui uses ComfyUI Desktop's restartCore/restartApp to restore the backend after stop, and refuses a Manager reboot when no Desktop relaunch path is available, so a Desktop instance is not left stopped (#1999) - panel_run serializes a freshly typed PrimitiveNode STRING widget from the live graph instead of rejecting it as a missing dynamic widget (#2009) - KSampler nodes added with panel_add_node keep control_after_generate='randomize' armed, so the ordinary Queue button rolls the seed instead of cache-hitting on 0 until a tab reload (#2029) ## [0.15.140] - 2026-08-30 ### Fixed - panel_search_nodes answers from installed /object_info when Manager search times out, and a timeout miss is a retryable named reason instead of an empty 15s hang (#2099) - panel_set_widget on a subgraph host's promoted CLIPTextEncode.text writes the parent rail, not only the inner converted-to-input widget, so queue serialization uses the new value (#366) ## [0.15.139] - 2026-08-30 ### Fixed - after ComfyUI restart, re-hello the existing workflow tab without reloading unsaved in-memory edits, and panel_set_workflow_target({mode:"current"}) forces that re-register when the server is ready but the tab command channel is stale (#2030) ## [0.15.138] - 2026-08-30 ### Fixed - panel_set_widget keeps COMFY_DYNAMICCOMBO_V3 dotted-child writes across queue-time reserialize instead of reverting to spec defaults (#2031, #2096) - surface the Manager install traceback in panel_install_node (#2012, #2075) ## [0.15.137] - 2026-08-30 ### Fixed - graph_load imports a wrapped `{prompt: .}` API/prompt graph the same way as a bare `graph.api.json` map (#2011, #2078) - graph_outline follows the live canvas after an active-tab instance change instead of refusing the first read; mutations stay fenced until an explicit rebind (#2007, #2070) - omit private combo option values from panel_set_widget refusal diagnostics while preserving the invalid-value verdict and option count - panel_set_widget writes AnimaPromptPlus custom textarea widgets (quality_prompt) through the live editor / store setValue path instead of waiting on a widget callback that never settles (#2020, #2079) ## [0.15.136] - 2026-08-30 ### Fixed - panel_install_node surfaces the Manager install traceback instead of hiding it behind a generic "Installation failed" pointer to the server log (#2012) - panel_refresh_nodes refuses a stale browser bundle with a Ctrl+Shift+R remedy instead of clearing last-known schema (#2027) - panel_refresh_nodes publishes the workflow UUID of the canvas it refreshed, and refuses a retryable route-registration miss if the active route moves, instead of returning refreshed:true with another UUID (#2026) - panel_set_widget no longer reports outcome-unknown after the value has landed: the handler ack is flushed once graph_set_widget resolves, and a timeout after delivery returns applied and verified from an idempotent readback (#2025) ## [0.15.135] - 2026-08-30 ### Fixed - keep the rendered changelog artifact synchronized when a release gains a late-merged entry - sync rendered changelog for v0.15.134 ## [0.15.134] - 2026-08-30 ### Fixed - panel_set_widget no longer refuses a loaded subgraph's promoted combo or number that outline already lists: the host rail resolves to its inner input-rail terminal so the promoted-terminal witness completes (#2057) - panel_add_node can add a class from a type-scoped /object_info read when the full dump misses its fixed budget on a large install, without treating a stale whole cache as verified (#2050) - relay fixed `/object_info` reads for the headless MCP fallback when the configured ComfyUI route is unreachable (#2283) ## [0.15.133] - 2026-08-30 ### Fixed - panel_search_nodes retries the legacy `/customnode/getmappings` route when the browser never gets an HTTP response from `/v2/customnode/getmappings`, and the structured miss names that transport failure instead of a bare Failed to fetch (#2024) - panel_connect by Autogrow name keeps later MiniMax H3 slots on their original wires (#2008) ## [0.15.132] - 2026-08-30 ### Fixed - panel_set_widget no longer refuses a root PrimitiveNode as an unclassifiable promoted container: leftover `.subgraph` (even a live-looking inner graph or a throwing getter) keeps `is_subgraph:false` and graph_get_subgraph throws the definitive "is not a subgraph" line (#2006, #2073) ## [0.15.131] - 2026-08-30 ### Fixed - the pre-publish registry parity scan now inspects the packaged archive rather than the repo, is wired into the publish job, and refuses to treat a Pending registry status as a pass (#1886) ## [0.15.130] - 2026-08-30 ### Fixed - Codex generatedImage results paint as chat attachments (#1994) - panel_run on frontend 1.49.6 delivers to_node_id natively when app.queuePrompt wrappers drop the third argument (#1782) - retry one readable workflow-open normalization race while keeping persistent content mismatches fail-closed (#1898) ## [0.15.129] - 2026-08-30 ### Fixed - queued runs keep their receipt; applied widget writes do not hang on rAF (#2054) ## [0.15.128] - 2026-08-30 ### Fixed - after reconnect, a queued run is never reported as user-rejected (#1995) - panel_set_widget writes DOM-backed multiline editors (StringMultilineTagEditor `text`) through the live input/contenteditable that getValue reads, so a no-op setValue after configure no longer refuses the update (#1997) ## [0.15.127] - 2026-08-30 ### Fixed - SaveVideo DynamicCombo leftovers are cleaned again at queue time: a bare orphan `codec` next to nested `format.codec` is dropped before graphToPrompt, a first-serialize throw retries once, and the refusal names the node when it still fails (#1931) ## [0.15.126] - 2026-08-30 ### Fixed - the pre-publish registry parity scan now inspects the packaged archive rather than the repo, is wired into the publish job, and refuses to treat a Pending registry status as a pass (#1886) - after a restart confirmation times out without rebooting, an in-place workflow save rebinds the same-origin userdata route and retries the write once, so a dirty tab is not stranded behind the browsers bare Failed to fetch; if the write still cannot land, the error names the userdata URL and any HTTP status/body (#1757) - promoted widget writes no longer treat a stale inner Primitive handle as the parent rail, so MiniMax H3 duration/value_1, turbo_mode, turbo_steps, and lora_name serialize the new value (#366) - scoped run-to-node no longer false-refuses after a finished subgraph edit: the graph stamp waits for pending canvas work to settle and restamps once if the revision moved before queue, without falling through to a full-graph run (#572) - resolve the 18+ consent card before the 300s tools/call deadline so an idle user gets a structured timeout instead of a transport hang (#390) - preserve current untagged canvas capture so an already-current untagged root is not treated as the previous tab (#1215, #2038) ## [0.15.125] - 2026-08-30 ### Fixed - preserve and synchronously fence node-incarnation witnesses across deferred replays and custom LTX/PromptRelay/Ideogram/MiniMax writes (#2021, #2478) - reject dynamic error-tag proxies - publish identity in compact pinpoint rows - fence deferred and custom widget writes (#2478) - keep additive source edits out of target state (#2017) - preserve browser stack for panel_run queue errors (#248) (#2013) ### Changed - record node identity fence (#2021) ## [0.15.124] - 2026-08-29 ### Fixed - raise the default render-stall warning to 600 seconds so long sampler, VAE, and video nodes are not mistaken for wedged work - retain keyed promoted display projections - sync promoted widget writes from root scope - raise default render stall threshold for long nodes - report host_links_reindexed on unexpose so MCP can skip the stale warning (#1993) ## [0.15.123] - 2026-08-28 ### Fixed - treat LIST as a core socket even with leftover widget defaults (#751, #1990) ## [0.15.122] - 2026-08-27 ### Fixed - remote, tunnelled, and mobile clients never see the in-app update prompt or one-click install+restart; those stay deferred until the next local host session (#1943, #1944) - defer host-mutating update UI on remote clients (#1984) ### Added - panel_civitai_results reports the live CivitAI lightbox (open model, version ladder + selection, files, creator note, download target) so "Ask agent to download" is no longer a request about a screen the agent cannot see (#1964) - CivitAI pane: Upscaler/Embeddings/Poses are real tabs (empty grids name types the current tab cannot see); sample URLs are the fetchable `/comfyui_mcp_panel/civitai/media` path plus a civitai.com pageUrl; open/clear echo applied tab/query/docked/cleared-count (#1958) - read-surface accuracy: query/view detail rows always include `mode` (including `active`) and surface writable `pinned`/`shape`; paste replies document that internal wires among the copied set are preserved; graph_serialize is one JSON `{workflow, node_count}` rather than two text blocks; auto-layout re-fit excludes pinned outliers so a skipped member cannot stretch a group over the canvas (#1957) - the agent can read what the live CivitAI pane is actually showing (painted grid cards, query, dock, overlay presence, optional contact-sheet of already-decoded thumbs) from the pane itself — not by re-fetching the public API, which never serves CivitAI RED — and can dock or undock the unified side panel after #1952's close (#1961, #1962) - read the live CivitAI pane and dock it ## [0.15.121] - 2026-08-27 ### Fixed - panel_set_widget no longer refuses a root-level ordinary node as an unclassifiable promoted container: graph_get_subgraph throws the definitive "is not a subgraph" line, and pinpoint detail keeps a bounded `is_subgraph:false` row (#1941) ### Added - the panel notifies a local host session when a pack update is available, shows what it fixes, and offers one-click install+restart; remote and mobile clients are not prompted (#1942, #1944) ## [0.15.120] - 2026-08-27 ### Fixed - graph_unexpose_subgraph_input / _output re-point remaining host SubgraphNode links after a non-last boundary slot is removed, so later host wires stay valid at queue (#1969, artokun/comfyui-mcp#2437) - panel_connect to a raw rail id (`-20`/`-10`) refuses instead of silently auto-exposing a subgraph boundary slot (#1953) - close resident panes and dismiss live cards (#1980) - reindex remaining host links after unexpose (#1978) ### Added - the Training / CivitAI side panel and live UI/media cards can now be closed/dismissed over the same bridge path the agent uses to open them, so a long session can shed resident renderer state (#1952, #1960) - panel_set_widget on an rgthree Fast Groups property (`matchTitle`/`matchColors`/`sort`/`toggleRestriction`) names `panel_set_property` instead of a pressable-widget dead end, and lists each available widget name once (#1956) ## [0.15.119] - 2026-08-27 ### Fixed - panel_set_widget refuses MiniMaxH3Director `timeline_data` and `builder_state` writes that leave the derived prompt stale (#1935) - SaveVideo keeps nested `format.codec` and drops the orphan top-level `codec`, so add and load stay queueable (#1931) - panel_run no longer reports CompareFrames temp images as "no media": unrecognised `*images` outputs are counted and named, and none of them are attached (#1934) - panel_set_widget rebuilds generated custom-widget rows after a hidden backend write, so Deno Multi LoRA (and the same pattern) updates visible rows and height without leaving and re-entering (#1932) - panel_set_widget no longer reports success when a just-added primitive's frontend init later clears the value (#1922) - panel_set_widget dispatches a promoted-widget write when the live canvas is a verified-stable root, instead of refusing with "scope became unverifiable" (#1925, artokun/comfyui-mcp#2435) ## [0.15.118] - 2026-08-27 ### Fixed - panel_restart_comfyui restarts the ComfyUI bound to the live canvas, not the orchestrator's boot target (#1913) - panel_open_workflow no longer times out undetermined after a restart: an unsettled reconnect is a retryable refusal (`applied: false`) instead of a delivered command with no receipt (#1914) ## [0.15.117] - 2026-08-27 ### Fixed - do not capture or admit the previous tab's graph after a switch (#1951) ## [0.15.116] - 2026-08-27 ### Fixed - panel_set_widget no longer queues a second whole `/object_info` behind a timed-out `api.getNodeDefs()`, so the type-scoped fallback can use the remaining command time on a large install (#1739) - retain verified add-node schema across a timed-out refresh (#1709) - a Registry publish of version X leaves tag vX on the commit that cut it, and changelog generation recognises `chore: release vX.Y.Z` so an untagged previous cut still bounds the next entry (#1882) - live-canvas capture no longer skips silently when Pinia `$subscribe` is missing (#1911) - disclose connect changes on nodes the command never named (#1928) - workflow_open no longer paints or admits the previous tab's graph after a switch, even when the live canvas still carries the target UUID (#1215) ## [0.15.115] - 2026-08-27 ### Fixed - keep `is_subgraph` on oversized-node detail stubs so wide root nodes stay writable (artokun/comfyui-mcp#2436) - panel_unpack_subgraph no longer crashes when a subgraph rail serialises the same link id twice (#1938) ## [0.15.114] - 2026-08-27 ### Fixed - re-vendor the tool vocabulary so connect no longer permanently warns about a hash no comfyui-mcp build produces (#1927) - a token COUNT is a generation control, not a credential (#1919) ### Changed ## [0.15.113] - 2026-08-27 ### Fixed - reach WebSocket writes by name, clearing the last rule -> 0 findings (#1924) - reach litegraph slot wiring by name, clearing $socket3 (#1923) - capture the original before forking, instead of binding it (#1921) - read the probe socket through a buffered wrapper too, 3 findings -> 2 (#1918) - actually run the network-rule replica in CI, as a ratchet (#1917) - the parity replica missed reads - $socket_stage_recv matches .recv( too (#1916) - preserve node search cancellation - bound node search replies (#1908) - cut the registry network findings from six files to two (#1909) - reject stale canvas capture after tab switch ### Changed ## [0.15.112] - 2026-08-27 ### Fixed - bound panel_search_nodes Manager replies and preserved caller cancellation (#1908, #1912) ## [0.15.111] - 2026-08-27 ### Fixed - cut the registry network findings from six files to two (#1909) - reject stale canvas capture after tab switch ## [0.15.110] - 2026-08-27 ### Fixed - gate releases on rendered changelog version (#1902) ### Changed - Fix workflow open normalization race (#1898) (#1904) ## [0.15.109] - 2026-08-26 ### Fixed - verify the rendered changelog artifact stays synchronized with the source (#1891, #1900) ## [0.15.108] - 2026-08-26 ### Fixed - reconcile generated changelog sections and release-history guards (#1891, #1894) ## [0.15.107] - 2026-08-26 ### Fixed - de-duplicate the 0.15.106 changelog section (#1890) ## [0.15.106] - 2026-08-26 ### Fixed - a release tag proves the tagged tree is the version it claims, and that nothing shipped untagged (#1885) - the tag guard fails closed on an unreadable commit, and checks its own tag target (#1887) - de-duplicate the 0.15.105 changelog section (#1884) ### Changed - 0.15.105 does not contain the release tag guard, so its entry moved to Unreleased (#1888) ## [0.15.105] - 2026-08-26 ### Fixed - scrolling up during a streaming turn no longer snaps back to the bottom (#1879) - graph_connect reports live dynamic slot rewrites and safely bounds hostile disclosure values (#1876) - the pruned-retry disclosure says why the first post is not pre-pruned (#1881) - normalise CRLF in the scope-fence source pins so they run on Windows (#1880) - preserve unsaved workflow identity across proxy reads (#1790) (#1875) ## [0.15.104] - 2026-08-26 ### Fixed - a save that named nothing is never told to "choose a different name" (#1866) ### Changed - local YARA parity replica, and defuse the changelog's own network tokens (#1874) - drive the receipt-less completion end to end, through the real delivery boundary (#1872) ## [0.15.103] - 2026-08-26 ### Fixed - stop tripping the registry network rule on Function.prototype.bind (#1867) - drop the vendored @a2ui/lit bundle and render leaves natively (#1865) - partially_queued early returns include seed-repetition warning (#1857) ### Changed - add behavioural test for repeating_controls_note attachment (#1862) ## [0.15.102] - 2026-08-26 ### Fixed - a connect that renames its target says so, instead of leaving the caller a stale title (#1856) ## [0.15.101] - 2026-08-26 ### Fixed - atomically fence promoted widget receiver scope (#2314) (#1831) - bound the pre-receipt completion replay so it cannot storm the agent (#1850) ## [0.15.100] - 2026-08-26 ### Fixed - preserve chat autoscroll through browser scroll anchoring and interactive-card reveals (#1841) - cache-bust inline previews when ComfyUI reuses an output filename (#1834) - keep completion identity safe across reroutes, reloads, repeated registration, and id-less receipts (#1837, #1839, #1845) ## [0.15.99] - 2026-08-26 ### Fixed - make recovered-completion reload refusals visible to the agent (#1830) ## [0.15.98] - 2026-08-26 ### Fixed - preserve panel_run completion receipts across delayed prompts, teardown, and restart (#1824) ## [0.15.97] - 2026-08-26 ### Fixed - the start command leads the setup card instead of trailing it (#1822) - the provider setup card scrolls instead of clipping its own tail (#1821) - the version gate compares an independent witness, and unsticks 0.15.85 (#1825) ## [0.15.96] - 2026-08-25 ### Fixed - keep the selected provider visible and preselected when host availability is incomplete (#1818) ## [0.15.95] - 2026-08-25 ### Fixed - support safely validated workflow-library subfolders for panel saves (#1794) ## [0.15.94] - 2026-08-25 ### Fixed - fail closed when workflow-open binding settles or reconnect proofs become stale during concurrent tab operations (#887) ## [0.15.93] - 2026-08-25 ### Fixed - add an authenticated fixed-operation ComfyUI read relay for history, system stats, and logs (#2283) ## [0.15.92] - 2026-08-25 ### Fixed - preserve the live Grok turn across dedicated workflow-tab creation and distinguish tab swaps from real ComfyUI restarts (#1810) ## [0.15.91] - 2026-08-25 ### Fixed - label cache-assisted completion durations as workflow time, not render time (#1805) ## [0.15.90] - 2026-08-25 ### Fixed - contain long chat transcripts without losing scroll-to-bottom behavior (#1801) - retry guarded root-identity healing after workflow switches (#1854) ## [0.15.89] - 2026-08-25 ### Fixed - rebind active and graph state after refusing to close an unsaved workflow (#1795) ## [0.15.88] - 2026-08-25 ### Fixed - recover graph tools after restart reconnect without a hard refresh (#1790) ## [0.15.87] - 2026-08-25 ### Fixed - rebuild fresh SaveVideo dynamic widgets and clear stale widget state (#2254) ## [0.15.86] - 2026-08-25 ### Fixed - gate workflow targeting on current reconnect binding proof (#1785) ## [0.15.85] - 2026-08-25 ### Fixed - fence scoped schema evidence (#2249) - retain usable schema across refresh failures (#2249) ## [0.15.84] - 2026-08-25 ### Fixed - carry both frontend scope option keys (#1782) - classify empty completion results (#1781) - diagnose completion delivery outcomes (#1781) ## [0.15.83] - 2026-08-25 ### Fixed - classify empty completion results (#1781) - diagnose completion delivery outcomes (#1781) ## [0.15.82] - 2026-08-25 ### Fixed - describe graph_get_virtual_types activity results (#1776) ## [0.15.81] - 2026-08-25 ### Fixed - defer safe widget edits until queue idle (#1716) ## [0.15.80] - 2026-08-25 ### Fixed - preserve VideoTileLivePlanner ROI on copy (#1761) - reset Save-As source after workflow load (#1762) - preserve restart confirmation answers (#1764) - resolve panel_set_widget against live node list (#1759) - explain workflow save transport failures (#1757) ## [0.15.79] - 2026-08-25 ### Fixed - Render orphan-link repairs with a meaningful activity warning instead of an undefined disconnect summary (#1750) ## [0.15.78] - 2026-08-24 ### Fixed - Join in-flight panel refreshes through download completion handoff instead of returning `refresh_still_running` (#1758) ## [0.15.77] - 2026-08-25 ### Fixed - Recover panel refreshes after concurrent download completions and coalesced trailing refreshes (#1736) ## [0.15.76] - 2026-08-24 ### Fixed - Preserve Save-As canvas identity through partial restores, tab switches, overlap, and failure cleanup (#939) - Scale object-info schema probes for remote ComfyUI origins while preserving bounded fail-closed writes (#1734) - Recover accessor-backed Impact BooleanWidget writes without masking same-message setter failures (#1735) ## [0.15.75] - 2026-08-24 ### Fixed - Keep Panel run receipts route-bound and exactly-once across reconnects, remounts, and late queue delivery (#1728) - Redact credential-like values through graph outlines and subgraph provenance, including shared aliases (#1729) - Refresh live asset membership before API-upload missing-asset validation (#1733) ## [0.15.74] - 2026-08-24 ### Fixed - Preserve refresh successor verdicts after reconnect so early results cannot report stale success (#1725) ## [0.15.73] - 2026-08-24 ### Fixed - Tolerate live Ideogram derived-widget serializer churn while preserving unrelated drift checks (#2130) ## [0.15.72] - 2026-08-24 ### Fixed - Isolate storyboard posters per render to prevent stale same-filename results (#1718) ## [0.15.71] - 2026-08-24 ### Added - add authenticated fetch_image bridge command (#1730) ## [0.15.70] - 2026-08-24 ### Fixed - deep-freeze all object info cache reads (#1709) - preserve refreshed whole schema after add (#1709) - replace stale whole schema authority (#1709) - retire whole schema authority on class reads (#1709) - reuse verified add-node schema (#1709) ## [0.15.69] - 2026-08-24 ### Fixed - name remote combo refusal provenance ## [0.15.68] - 2026-08-24 ### Fixed - roll back Fast Bypasser linked modes (#2146) ## [0.15.67] - 2026-08-24 ### Fixed - panel_refresh_nodes now follows chained forced refresh successors and never reports premature success while a later refresh is still running (#1695) - late combo refresh mutations remain fenced and combo trust stays fail-closed across reconnect refreshes (#1695) ## [0.15.66] - 2026-08-23 ### Fixed - disclose rgthree lora row creation route (#1694) - fail closed on incomplete live scans (#1691) - bound live error lookups (#1691) - preserve refusal precedence for mixed receipts (#1690) - make incomplete run receipts uncertain (#1690) - refuse id-less run acknowledgements (#1690) - reject ambiguous inner error ids - reject foreign scoped execution errors - preserve scoped execution errors ### Changed - release-0.15.62 (#1704) - fix/1682 refresh join (#1703) - release-0.15.61 (#1702) - fix/1681 widget detail budget (#1701) - release-0.15.60 (#1700) - fix/1680 refresh completion handle (#1699) ## [0.15.65] - 2026-08-23 ### Fixed - fail closed on incomplete live scans (#1691) - bound live error lookups (#1691) - preserve refusal precedence for mixed receipts (#1690) - make incomplete run receipts uncertain (#1690) - refuse id-less run acknowledgements (#1690) - reject ambiguous inner error ids - reject foreign scoped execution errors - preserve scoped execution errors ### Changed - release-0.15.62 (#1704) - fix/1682 refresh join (#1703) - release-0.15.61 (#1702) - fix/1681 widget detail budget (#1701) - release-0.15.60 (#1700) - fix/1680 refresh completion handle (#1699) ## [0.15.64] - 2026-08-23 ### Fixed - preserve refusal precedence for mixed receipts (#1690) - make incomplete run receipts uncertain (#1690) - refuse id-less run acknowledgements (#1690) - reject ambiguous inner error ids - reject foreign scoped execution errors - preserve scoped execution errors ### Changed - release-0.15.62 (#1704) - fix/1682 refresh join (#1703) - release-0.15.61 (#1702) - fix/1681 widget detail budget (#1701) - release-0.15.60 (#1700) - fix/1680 refresh completion handle (#1699) ## [0.15.63] - 2026-08-23 ### Fixed - reject ambiguous inner error ids - reject foreign scoped execution errors - preserve scoped execution errors ### Changed - release-0.15.62 (#1704) - fix/1682 refresh join (#1703) - release-0.15.61 (#1702) - fix/1681 widget detail budget (#1701) - release-0.15.60 (#1700) - fix/1680 refresh completion handle (#1699) ## [0.15.62] - 2026-08-23 ### Changed - fix/1682 refresh join (#1703) - release-0.15.61 (#1702) - fix/1681 widget detail budget (#1701) - release-0.15.60 (#1700) - fix/1680 refresh completion handle (#1699) ## [0.15.61] - 2026-08-23 ### Changed - fix/1681 widget detail budget (#1701) - release-0.15.60 (#1700) - fix/1680 refresh completion handle (#1699) ## [0.15.60] - 2026-08-23 ### Changed - fix/1680 refresh completion handle (#1699) ## [0.15.59] - 2026-08-23 ### Fixed - refuse derived MiniMaxH3Director prompt writes (#1697) ## [0.15.58] - 2026-08-23 ### Fixed - reject replaced panel targets - advertise expected node type fence - enforce expected target type at write boundary ## [0.15.57] - 2026-08-23 ### Fixed - panel_find_nodes now matches raw quoted substrings in STRING widget values (#1678) ## [0.15.56] - 2026-08-23 ### Fixed - refuse arbitrary Git URLs on Manager v4 before queueing (#1539) ## [0.15.55] - 2026-08-23 ### Fixed - treat VHS date prefixes as queue-time volatile ## [0.15.54] - 2026-08-23 ### Fixed - recover link-disconnect restore crashes ## [0.15.53] - 2026-08-23 ### Fixed - normalize app mode canvas metadata ## [0.15.52] - 2026-08-23 ### Fixed - preserve graph serializer failures (#1669) ## [0.15.51] - 2026-08-23 ### Fixed - skip frontend-authored CustomCombo availability ## [0.15.50] - 2026-08-23 ### Fixed - verify region palettes - respect import authority and rollback - handle empty editor and palette state - rehydrate Ideogram region editor writes ## [0.15.49] - 2026-08-23 ### Fixed - make path fallback explicit - prefer live ComfyUI base path ## [0.15.48] - 2026-08-22 ### Fixed - reject empty object info maps - reject malformed object info fallback - harden fallback pack map keys - panel_list_nodes returns an inspectable fallback when Manager is unreachable ## [0.15.47] - 2026-08-22 ### Changed - fix-late-workflow-save-receipts (#1657) ## [0.15.46] - 2026-08-22 ### Fixed - first open of the active workflow after restart succeeds (#1643) ## [0.15.45] - 2026-08-22 ### Fixed - outline reporting root after reconnect lets enter_subgraph use the root (#1639) ## [0.15.44] - 2026-08-22 ### Fixed - after a reconnect, a graph outline that reports viewing.scope root makes the immediately following enter/mutation use root, instead of rejecting a root node as "currently inside a subgraph" (#1636) - panel_set_widget no longer re-waits on silent `api.getNodeDefs()` / `GET /object_info` probes when a same-connection schema snapshot is already held (#1582) ### Fixed - installed nested models on Windows are not reported missing (#1637) - panel_set_widget no longer re-waits on silent schema probes (#1638) ## [0.15.43] - 2026-08-22 ### Fixed - panel_new_workflow no longer claims a blank tab while the previous graph is still on the canvas (#1632) ## [0.15.42] - 2026-08-22 ### Fixed - a restart confirmation card is brought on screen instead of timing out unseen: the Agent tab opens, stick-to-bottom is forced, and the card scrolls immediately rather than waiting on an rAF a backgrounded tab will never fire (#1625) - resolve Manager installed directory IDs before update ## [0.15.41] - 2026-08-22 ### Fixed - fail honestly when remote /view is unavailable (#1623) ## [0.15.40] - 2026-08-22 ### Fixed - panel_open_workflow keeps authored node size/order and leaves a clean tab unmodified (#1621) - replay remote PreviewImage completion (#1624) ## [0.15.39] - 2026-08-22 ### Fixed - deliberate-sweep runs retain their prompt and SaveImage filename widgets when the frontend queue is already busy (#1588) ## [0.15.38] - 2026-08-22 ### Fixed - panel_open_workflow no longer reports unknown content when only randomMin/randomMax normalize (#1613) - a finished stills render notifies the agent before the orchestrator fallback (#1612) - trust live VIDEO producers for add-node socket proof ## [0.15.37] - 2026-08-22 ### Fixed - opening an already-open saved workflow publishes the live fence uuid, so the next graph read is not refused as a stale instance (#1581) - `panel_add_node` accepts a core VIDEO socket when a live canvas producer exposes VIDEO, even if the full `/object_info` read times out (#1589) - opening a saved workflow leaves graph tools on that canvas - report the tab re-list from the open LIST, and hold the fence across the load - the adopt arm requires the same workflow FILE, not just a matching selector - reopening a closed tab loads the workflow the store's early return skipped ## [0.15.36] - 2026-08-22 ### Fixed - the Agent tab appears in the sidebar after a clean load (#1609) - a save whose only drift is node geometry is no longer refused (#1606) - accept wildcard producers for custom sockets (#1584) (#1603) ## [0.15.35] - 2026-08-21 ### Fixed - the pack no longer treats any TCP listener on the bridge port as a running orchestrator (Logitech G HUB's `lghub_agent` sits on 9180); identity is a hello/models handshake, the default is 9199 with 9180 as a legacy fallback, `/status` and Connect try 9180 before spawning a 9199 orchestrator, and a live 9180 session is not stranded across a panel update (#1596 / mcp#2030) - shorten silent schema probe with current snapshot - upgrade queued refresh handoff - answer refresh before synchronous reapply - graph audits no longer report stale output links or contradictory downstream consumers (#1590) ## [0.15.34] - 2026-08-21 ### Fixed - close wrapped secret and version regressions - the proxy attribution needs a body that NAMES a responder ## [0.15.33] - 2026-08-21 ### Fixed - free_vram classifies what answered instead of pasting the proxy's error page (#1591) - a frontend-only node stays addable when the object_info fetch does not answer (#1586) - a benign load-time rewrite INSIDE a subgraph definition stops refusing the open (#1578) - a subgraph node's rebuilt widget list is compared by MEMBERSHIP, not by array identity (#1577) - a scoped batch drives ComfyUI's own control hooks, so batch_count means N renders (#1576) - the video completion stops waiting on work the agent never sees (#1579) - the live availability scan reads V2/V3 combos, not just the V1 shape (#1568) - a save whose tracker snapshot is behind the canvas is refused, not reported as success (#1567) - three refusal/comment inaccuracies from #1561 now state what the code observed (#1574) - graph_run answers inside the window its reply is relayed in (#1570) - recognise ComfyUI's file_upload flag so Load3D model inputs get upload handling (#1571) - panel_refresh_nodes spends the window it holds, and stops blaming a server it never saw fail (#1566) - a type-scoped /object_info answers the fence when the whole map never lands (#1561) ## [0.15.32] - 2026-08-21 ### Fixed - Prompt Builder prompt_text writes keep builder_state in the same step (#1551) - serverDeclaresEmptyComboOptions reads the V2 combo shape (#1552) - a FLOAT widget's quantization is explained by the grid the frontend actually ran (#1550) ## [0.15.31] - 2026-08-20 ### Fixed - loading a saved workflow no longer resets a subgraph host's prompt, dimensions, length, and selectors to definition defaults (#874) - panel_add_node names a failed custom-node pack as the reason a type is missing only when ComfyUI-Manager's node map says that pack provides it; an unproven failure is reported as a separate issue rather than the cause (#1544) ### Fixed - saved subgraph host widgets survive panel_load_workflow (#1547) - name a failed pack as the cause only when Manager's node map proves it owns the type (#1545) ## [0.15.30] - 2026-08-20 ### Fixed - the default saveWorkflow test double writes the tab's own path (#1542) ## [0.15.29] - 2026-08-20 ### Fixed - a no-name save of a .app.json workflow writes that file, not a plain .json fork (#1538) - a long timestamp-less transcript keeps the order it was written in (#1536 / #1539) ## [0.15.28] - 2026-08-20 ### Fixed - panel_set_widget retains VHS_LoadVideo custom dimensions (#1534) ## [0.15.27] - 2026-08-20 ### Fixed - the archive import carried the same broken tiebreak (#1531) - a timestamp-less transcript keeps the order it was written in (#1530) - the slot snapshot is instrumentation, not a precondition for firing the hook - a programmatic widget write announces itself to the node, not just to the widget ## [0.15.26] - 2026-08-20 ### Fixed - `panel_add_node` of a subgraph UUID already loaded in the live workflow no longer rejects it as an unknown backend node or names an unrelated failed pack (#1523) - a run ComfyUI accepted while dropping some outputs is reported as queued, with the dropped outputs named, instead of as a refusal (#1504) - `panel_list_nodes` with `search` (or `query`) filters the installed-pack list instead of returning every pack (#1496) - a run-to-node no longer has its own branch vetoed by an uninstalled pack on a branch it excluded (mcp#1871) - the panel's own pre-flight stops refusing before ComfyUI is asked, so that recovery can run (mcp#1871) - a group is built from the same footprint its members are judged by, so requested collapsed nodes are included (mcp#1877) - a locator that could not look no longer reports the node as being from another workflow (#1501) - a refusal claims nothing about a final size it never read (#1872) ## [0.15.25] - 2026-08-20 ### Fixed - a title-only edit is judged against the node's own snapshot, not a size range (#1509) - a later phase must not speak for a run that obtained no /object_info (#1508) ### Changed - EXECUTE the rider, so its workflow-key gate can be watched to fail (#1505) ## [0.15.24] - 2026-08-20 ### Fixed - the node-def refresh asks the raw /object_info route when the frontend client does not answer (#1502) - a graph read dates itself against the manual-change block it contradicts (#1503) - an instance-scoped promoted write discloses the inner callback it did not invoke (#1500) - the "you are inside a subgraph" remedy is now checked, not assumed (#1499) ## [0.15.23] - 2026-08-20 ### Fixed - adopt a loopback bridge URL the orchestrator advertises, not just a tunnel one (#1487) ## [0.15.22] - 2026-08-20 ### Fixed - a Manager install queue that stays `in_progress` with no count change is reported as a silent stall, not as progress: `panel_node_queue_status` still attaches Manager's real counts, names any in-progress pack, and after two minutes of the same fingerprint tells you to verify with `panel_list_nodes` instead of waiting forever (#1480) ### Fixed - a silent Manager in_progress is named as a stall, not as progress (#1483) ## [0.15.21] - 2026-08-20 ### Fixed - the video card mounts, reserves its real space, and pauses before it unmounts (#1481) - a tab switch no longer leaves graph reads refused on a stale root identity (#1478) - a schema read that timed out is UNKNOWN, not proof that nothing outputs the type (#1479) ## [0.15.20] - 2026-08-20 ### Fixed - a subgraph conversion that THROWS says whether the graph changed (#1466) ### Changed - bump @types/node from 22.20.0 to 26.2.0 (#1471) ## [0.15.19] - 2026-08-20 ### Changed - bump the npm-minor-patch group with 2 updates (#1469) - bump the actions-all group with 3 updates (#1468) ## [0.15.18] - 2026-08-20 ### Fixed - pasting a prompt copied out of a document no longer drops every character: a clipboard carrying both a file and text (which is what Word, Outlook, Excel and most web pages produce on Windows) had its text discarded by the file branch, silently and with no placeholder (#1467) - a message referencing an attachment whose content is no longer loaded now says so, instead of sending a message that only looks complete (#1467) ## [0.15.17] - 2026-08-20 ### Fixed - key the requested name and the canvas name with the operation each needs (#1462) ### Changed - watch dependencies for advisories, weekly and grouped (#1464) ## [0.15.16] - 2026-08-20 ### Fixed - adding an unknown node type no longer reports an unrelated ReActor import failure when that pack currently provides other live types (#1447) - the save-timeout reply no longer claims the canvas is not the save's destination when it is: the requested name and the frontend's derived filename are now compared after the same normalization, so `workflow_save_as({name:"Foo.json"})` keeps reporting `modified:true` instead of withholding it (#1458) ## [0.15.15] - 2026-08-20 ### Fixed - entering a copied subgraph, editing its inner graph, and exiting no longer clears promoted widget values on the parent instances (artokun/comfyui-mcp#1827) ### Fixed - the save timeout reports what it observed, never that the write landed (#1456) - promoted parent widget values survive inner subgraph edits (#1454) ## [0.15.14] - 2026-08-19 ### Added - `graph_unexpose_subgraph_input` / `graph_unexpose_subgraph_output` remove a subgraph boundary slot by name — the inverse of the expose pair; dropped interior/host wires are counted and reported (artokun/comfyui-mcp#1294) ### Fixed - `panel_get_errors` no longer flags a pasted LoadImage file like `pasted/image (992).png` as missing when the file is on disk and `/view` serves it (#1357) ### Added - a supported way to remove a subgraph boundary slot (artokun/comfyui-mcp#1294) (#1437) ### Fixed - a pasted LoadImage file with spaces is no longer flagged missing (#1440) - panel_save_workflow replies when the save hangs instead of claiming the tab is frozen (#1439) ## [0.15.13] - 2026-08-19 ### Fixed - `panel_get_errors` no longer joins a previous workflow's runtime failure onto a current node that only shares the same id — correlation now requires matching node type as well (#1448) ### Fixed - panel_get_errors no longer attaches a stale execution error to a reused node id (#1451) ## [0.15.12] - 2026-08-19 ### Fixed - graph mutations notify the visible canvas (and bump the frontend layout revision) so a successful edit/remove actually appears, not only in panel_graph_outline (#1443) ### Fixed - editing a node keeps compact geometry instead of inflating it (#1446) - graph edits appear on the canvas, not only in the outline (#1445) ## [0.15.11] - 2026-08-19 ### Added - Qwen Code as a selectable Agent Panel provider (#1438) - graph_get_virtual_types — serve the frontend's proven virtual-node registry (artokun/comfyui-mcp#1400) (#1441) ## [0.15.10] - 2026-08-19 ### Added - `graph_get_virtual_types` bridge command reports the node types this page's LiteGraph registry proves frontend-virtual (`isVirtualNode === true` on a probe instance of the registered class — KJNodes' Get/Set bus, rgthree's Label / Fast Groups toggles, and any pack that sets the same flag), so the orchestrator's headless `check_runtime` can consult the authority instead of a name list (comfyui-mcp#1400) - `graph_configure_app_mode` sets ComfyUI App Mode inputs, outputs, and default mode on the live canvas without clobbering `extra.comfyui_mcp` (#1429) - `graph_save_subgraph` can replace a user-published blueprint in place with `overwrite:true` — no Save dialog, bundled/global blueprints stay protected (#1122) - `panel_remove_node` accepts `node_ids` and removes every listed node in one undo step — one Ctrl+Z restores them all, and a single reply names what left and what did not (#841) ### Added - panel_remove_node takes node_ids as one undo step (#1431) - set ComfyUI App Mode inputs, outputs, and default mode on the live canvas (#1433) - replace a published subgraph blueprint with overwrite:true (#1432) ### Fixed - outline clip footer names note nodes whose text was clipped (#1435) ## [0.15.9] - 2026-08-19 ### Fixed - `panel_open_workflow` reports the fence uuid from the same active-workflow observation as the binding fields beside it, instead of a second live read that could disagree (#1014) ### Fixed - an open reply reports one identity observation, not two (#1428) - a promoted-widget write lands in the instance that was addressed, not the shared subgraph definition (#1427) ## [0.15.8] - 2026-08-19 ### Fixed - stale-combo refusal tells the truth about the refresh, and the /view probe is bounded (#1425) - lightbox stage and collapsed cards honor the same honest failure state (#1424) ## [0.15.7] - 2026-08-19 ### Fixed - `panel_refresh_nodes` no longer times out with no acknowledgement when a node-def refresh is already running: of the six commands relayed in the orchestrator's 30 s window only `graph_add_node` and `nodes_install` held a command budget, and `refresh_nodes` — whose whole body IS a wait on the node-def refresh — did not, so a forced call waited unbounded on the in-flight run AND on its own trailing run — past the window, so the reply the panel's bounds exist to produce never left the tab and the user got `did not reply … the ComfyUI tab may be backgrounded or frozen` about a healthy, idle tab. It now bounds that wait at 25 s (the same budget `graph_add_node` and `nodes_install` hold against the same window) and answers `refreshed:false` with `reason:"refresh_still_running"` and a retry that works — the abandoned run is not cancelled, so it is still registering the definitions the call asked for (#1404) ### Fixed - graph_set_widget stale-combo recovery takes the command budget (#1416) - refresh_nodes takes a command budget, so a contended refresh replies instead of timing out (#1409) - A2UI card buttons send a user message when clicked (#1410) - sanitize invalid properties.aux_id on node add/paste so one node can no longer poison every save/load (#1412) ## [0.15.6] - 2026-08-19 ### Fixed - `panel_query_graph` (fields:'detail') no longer last-wins-collapses widgets that share a name: when a name repeats (rgthree Fast Groups toggle rows), every occurrence is listed so a duplicate or orphaned row is visible (#1402) (#1406) - `duplicate_widgets` (the #1402 duplicate-widget report) no longer throws the whole detail read away on a widget named `__proto__`/`constructor`/`toString` — occurrences are accumulated prototype-safely — and is now bounded by the same `max_chars` budget as `widgets` (dropped occurrences are announced with the lever that lifts them, never silently lost), so a many-group Fast Groups Bypasser cannot push the detail line into a whole-row stub. `panel_graph_outline` also labels each same-named row from the widget itself rather than a last-wins name-keyed map, so two different group toggles are no longer both annotated with the last row's label (#1402) (#1405) ## [0.15.5] - 2026-08-19 ### Added - converting a video-gen workflow to an app exposes LTX 2.3/Director, Wan, Bernini, Hunyuan and Easy-Use Media generation parameters, treats LoadVideo/VHS/easy loadVideo as video inputs, and collects SaveVideo/VHS_VideoCombine/easy saveVideo as video outputs (including ComfyUI history `videos[]`) instead of classifying them as stills (#428) - `/record-skill` snapshots the open graph as a reusable skill file (`skills//SKILL.md`) so the agent can rebuild it (#350) ### Fixed - `workflow_open` no longer reports a false content mismatch on `definitions` when the frontend renumbered subgraph NODE ids during the load: the relabeling is proven (same nodes in the same order, links and promoted widgets patched through one injective map) rather than tolerated, and it still refuses when a root node promotes a widget from a node the relabeling touched (artokun/comfyui-mcp#1706) - a panel tab whose bridge ROUTE went stale re-advertises itself instead of waiting for a browser refresh: the 600 ms workflow poll now watches the route the orchestrator keys its tab registry on, not only the workflow-instance fence identity, so a re-hello that did not land after a switch, a first save or a rename is retried (bounded) rather than leaving every graph call addressed at a tab id nothing answers to (#1389) ### Added - converting a video-gen workflow to an app exposes LTX/Wan/Bernini/Hunyuan params and video I/O (#1397) - /record-skill saves the open graph as a reusable skill (#1398) ### Fixed - sweep a stolen preview by which node emitted, not by what its type could show (#1392) - a re-pended run completion re-arms the reconcile sweep (#1403) - a back-to-back graph command flushes the pending tracker snapshot before the fence (#1400) - the missing-node COUNT is filtered with the names it was counting (#1396) - graph_add_node still replies when its own refresh run would miss the window (#1399) - the promoted-widget guard reads every dialect of an id that MOVED, not one spelling (#1395) - the coalescer-join pin is checked at EVERY call site, not once over the body (#1393) - a stale ROUTE re-advertises itself, like a stale fence identity already does (#1391) - a subgraph node-id renumber is a relabeling the panel can PROVE, not lost content (#1388) - video previews can be disabled — placeholder keeps first frame, filename and lightbox access (#1386) - refuse workflow names with path separators instead of silently nesting (#1384) - a nested composite field inside a JSON widget validates against its own value's shape (#1382) - the load watch reports whether it was ENTERED, not just installed (#1387) ## [0.15.4] - 2026-08-19 ### Fixed - a combo that cannot list its files is no authority on whether a file exists: an unreadable listing no longer reads as an empty one (#1357) - an abandoned combo refresh is reported as a missing confirmation rather than silently trusted: V2 and dynamic combo specs are rebuilt too, and a spec the panel cannot derive marks the run uncovered instead of suppressing a real missing-asset warning (#1193) - a caller-supplied link-exclusion Set is normalised instead of trusted, so a raw-number Set cannot silently lose the exclusion and credit a pre-existing link to the current call (#1272) - `panel_auto_layout` apply inside a subgraph no longer escapes to and rearranges the root graph (#1328) - `panel_get_errors` no longer keeps load-time missing-node-type errors after a ComfyUI restart that registered those classes — leftover placeholders are reported as needing a save+reopen, not as still-uninstalled types (#1332) - agent-directed instruction blocks (LIVE-CANVAS TOOLS, MANUAL CANVAS CHANGES, query-graph budget accounting) stay in the agent's context and no longer render in the user's chat (#1310) - graph mutations no longer stay stuck in `[backend-reconnecting]` after a long Wan render: a busy `/prompt` poll is not a down socket, and binding status now distinguishes a readable canvas from a backend that is actually reconnecting (#1325) - panel_update_node surfaces the Manager update traceback instead of hiding it behind a generic "check the server log" error (#1320) - dictation listens for the spoken language when the panel UI is the English catalog floor — a German (or other unshipped) speaker no longer gets an English recognizer (#1329) - panel_set_widget can author an LTXDirector timeline from the serialized widgets when the live timeline editor is not initialized (#1308) - live-sync no longer reports notified unless the active canvas actually holds the saved workflow (#1299) - `panel_edit_group({bounds})` writes the group box only — contained nodes stay at their canvas coordinates (#1306) - an already-full origin can save workflow drafts again after the chat-history quota fix (#1305) - a group that encloses rgthree Label nodes now moves, instead of being refused because those nodes' visual bounds are not the panel's generic footprint — the same nodes already moved fine one-by-one with panel_edit_node (#1300) - a subgraph preview widget is promoted through the frontend's previewExposure store, and a failed link-only promote no longer hides behind a missing promotion store (#1271) ### Fixed - author an LTXDirector timeline without a live editor (#1371) - leftover link-driven widgets are not graph drift after reconnect (#1379) - a connect that THROWS is judged on the link the live graph shows, not on the exception (#1352) - dictation listens for the spoken language, not the English UI floor (#1375) - a widget write is verified against the node property litegraph binds it to, and says UNKNOWN when it cannot be (#1363) - a load the panel WATCHED run to completion is not a content mismatch (#1358) - live_sync reports notified only when the active canvas applied (#1372) - a Save-As acknowledges the CAPTURE it observed, not the openWorkflow it called (#1359) - panel_edit_group bounds writes the box only (#1369) - a frontend VIRTUAL node is not a missing one — the derivable signal, at three call sites (#1353) - the FIFTH wait in graph_add_node draws from the command budget too (#1349) - reclaim an already-full origin so ComfyUI can save drafts (#1367) - keep execution image previews on the node that emitted them (#1361) - a frontend-only add_node refusal names the unloaded pack JS, not a missing install (#1366) - missing-node mutation errors name the current live ids (#1365) - a group containing rgthree Label nodes moves instead of being refused (#1364) - a dynamically added node survives workflow switch + reconnect (#1362) - promote preview widgets through previewExposure (#1360) - copy/paste keeps groups and branch positions (#1356) - video preview uses the source duration, not a truncated default (#1355) - graph_add_node gets ONE budget, so its bounds stop adding up (#1342) - the fixed-seed ARRAY and its SENTENCE come from one predicate (#1350) - a ComfyUI restart the bridge SURVIVES re-advertises the tab route (#1347) - a slow-but-healthy fetch no longer starves the combo refresh of its budget (#1346) - the outline resolves a link's live target slot through the backlink — a stale target_slot renders nothing instead of fabricating connectivity (#1340) - a proven read keeps its stale-tag bypass through the executor's own fence re-assert (#1345) - an untagged canvas is no longer captured into the target tab's state on a switch (#1344) - a rename is announced as a rename, not a workflow switch (#1343) - the post-open frame wait is bounded — a starved rAF no longer latches the switch fence (#1341) ## [0.15.3] - 2026-08-19 ### Fixed - a second panel bundle in the page stands down instead of fighting for the tab (#1269) - one node's configure throw no longer aborts the rest of a graph load (#1260) - a grounding auto-persist keeps the workflow's identity across its object swap (#1263) - a cg-use-everywhere graph's broadcast targets are queue-time volatile, so run-to-node's graph stamp matches on an untouched canvas (#1273) - panel_add_node refreshes a drifted node schema itself, then re-checks — the refusal is what survives (#1242) ## [0.15.2] - 2026-08-18 ### Fixed - a virtual PrimitiveNode feeding a subgraph input is reported as the non-source it is (#1181) - a structural hand edit inside the tracker's capture lag no longer refuses the workflow's own canvas (#1187) - an open refused on a properties difference names the keys that differ (#886) - a first save publishes the identity it produced even when the swap carry fails safe (#978) - a widget callback's throw now names the file it surfaced from, origin scrubbed (#976) ## [0.15.1] - 2026-08-18 ### Added - send this version's changelog to the Comfy Registry — every release has shipped blank (#810) ### Fixed - the shadow sheds version payloads canonical holds, so the byte bound holds (#1318) - a backend restart under a live tab re-probes the pack version and self-heals (#1317) - panel_refresh_nodes can no longer report success over a pruned live canvas (#1316) - a stale-canvas tab can no longer persist its foreign graph over another workflow's file (#1315) - unpack_subgraph verifies external links survived and refuses loudly when any were dropped (#1314) - panel_set_widget refreshes a node's dynamic input slots after the write (#1313) - a same-node connect refusal is reported as LiteGraph's loopback guard, not a false type mismatch (#1311) ## [0.15.0] - 2026-08-17 ### Added - opening the panel can now START the local MCP orchestrator for you, with a chooser for the LLM provider. The companion launcher binds loopback-only behind a 32-byte token; the pack proxies it so the token never reaches the browser, and the proxy refuses a declared Origin that is not the Host the browser addressed — a plain cross-tab form post can no longer start the process on your behalf (#1243) - `panel_set_widget` can CREATE an rgthree `lora_N` slot: writing `lora_1` on a fresh node mints the row instead of being refused for a widget only the node's DOM-only "Add Lora" button could bring into existence. Keyed on node type, the `lora_` name shape and a lora-slot-shaped value, so an ordinary typo cannot reach it (#757) ### Fixed - a voice-dictation error now names the cause and the way out, not just the Web Speech code (#1288) - dictation listens in the panel's language, not the browser's (#1289) - the desktop app disables dictation with the reason, instead of failing on every click (#1290) - a search limit above the cap is disclosed as limit_cap, not silently honored (#1287) ## [0.14.44] - 2026-08-16 ### Fixed - a node read by id returns its full widget value, not a survey clip (#1634) - a drifted graph fence is refreshed before the next call, not after it refuses (#1209) - the Settings path no longer leaves the saved default naming a backend it never reached (#1198) - a wedged orchestrator's death is recorded where the panel concludes it (#1168) - an open where only presentation moved is not a failure (#1623) - a subgraph conversion that broke the graph stops reporting success (#1571) - a Manager that CANNOT report a failure stops reading as one reporting none (#1606) - a subgraph is not a second unexplained difference, and the reassurance's own predicate reads the UNEXPLAINED surfaces too (#1588) - graph mutations no longer lose the tab route after creating a workflow (#1095) - a combo write is decided by whether the panel could READ the option list (#1126) - a completion recovered from history reports when it RENDERED, not when it was replayed (#1199) - a multi-word panel_search_nodes query finds the pack it names (#1088) - a Manager task that terminally errored is not a queued install (#1539) - a widget the node computes from its own editor is not writable (#1569) ## [0.14.43] - 2026-08-15 ### Fixed - a sidebar tab that is SELECTED but never paints now says so, once, with what was observed — the panel used to just look empty, with no way to tell a broken extension from a frontend that dropped the tab (#779) - the conversation is always panel-owned: the workflow/ask chat scopes are retired, so a session can no longer be scoped to anything but the orchestrator (mcp#884) ## [0.14.42] - 2026-08-15 ### Fixed - run-to-node is no longer permanently refused when an armed Seed (rgthree) substitutes its value at queue time — that substitution is not graph drift, and every retry used to fail identically (#1124) ## [0.14.41] - 2026-08-15 ### Fixed - graph_load now reports the workflow identity it actually loaded into on BOTH reply paths, including the API-format load the report came from (#1478) ## [0.14.40] - 2026-08-14 ### Fixed - a group whose members are COLLAPSED nodes now moves, instead of being refused after those members positions had already been written (#813) ## [0.14.39] - 2026-08-14 ### Fixed - a widget edit is no longer refused just because the /object_info probe went silent while ComfyUI was busy rendering — it is authorized from the last whole schema observed on the same backend connection, which a restart always invalidates (#1223) ## [0.14.38] - 2026-08-14 ### Fixed - translating the error disarmed every ComfyUI-Manager fallback (#1230) ## [0.14.37] - 2026-08-14 ### Fixed - a run whose outcome could not be confirmed is reported as a neutral event instead of an urgent error, so cancelling a large batch no longer tells the agent every prompt ERRORED (#1226, comfyui-mcp#1489) ## [0.14.36] - 2026-08-14 ### Fixed - panel_open_workflow now asks the SERVER whether the file exists before refusing, so a workflow staged into the workflows folder out-of-band is reported as a stale list rather than a missing file (#1222, comfyui-mcp#1448) ## [0.14.35] - 2026-08-14 ### Fixed - a graph mutation refused during a reconnect now says so in a FIELD the caller can key on, instead of only in the sentence (#1216, comfyui-mcp#1529) ## [0.14.34] - 2026-08-14 ### Fixed - every release since 0.14.31 writes two sections for one version (#1219) - rebuild combo options during the reapply sweep, and disclose an empty authoritative list (#1218) ## [0.14.33] - 2026-08-14 ### Fixed - panel_get_errors is a READ, so the dirty-tab mutation fence no longer refuses it as "this mutation" (#1211, comfyui-mcp#1478) - bound the remaining unbounded network awaits (#1201) ## [0.14.32] - 2026-08-14 ### Fixed - a faithful workflow_open no longer reports CONTENT_UNVERIFIED just because the frontend rebuilt each node's `inputs` from its definition (#1208, comfyui-mcp#1467) ## [0.14.31] - 2026-08-14 ### Fixed - panel_open_workflow no longer claims the workflow list "WAS re-read from the server" when it cannot know that — the frontend's sync swallows its own errors (#1206, comfyui-mcp#1448) ## [0.14.30] - 2026-08-14 ### Fixed - the video storyboard sampler now names WHICH failure it hit, instead of reporting six different causes as one silent nothing (comfyui-mcp#1493) ## [0.14.29] - 2026-08-13 ### Fixed - **A backend switch that cannot complete no longer leaves the panel claiming the new one (#1184) (#1196).** Picking a different provider committed the choice — in memory, to the chips, and to the stored runtime pick — before checking whether the old provider's session could be safely ended. When that check failed the switch stopped there, and because the stored pick outlives the tab, a reload adopted a backend the panel had never actually connected to. The conversation replay armed for the new provider was left armed too, so the next message shipped the whole prior transcript back to the provider that already had it. Nothing is committed now until the switch is known to be possible, and a switch that stops says so instead of failing silently. ## [0.14.28] - 2026-08-13 ### Fixed - **Adding a node no longer waits forever on a ComfyUI that stopped answering (#1180) (#1186).** When the connection to ComfyUI goes half-open — the socket is up, the server never replies — a request does not fail, it simply never returns. Five requests for the node schema on the add-a-node and refresh-nodes paths had no time limit, so the panel parked on the first one it reached and the add landed minutes later, after the reply it belonged to had already been given up on. Each now gives up on its own and falls through to the handling that was already written for a schema it could not read. The log read that runs while *explaining* one of those failures is bounded too; it was inheriting the very stall it was called to describe. **No change on a healthy ComfyUI**, where these all answer in well under a second. ## [0.14.27] - 2026-08-13 ### Fixed - bound the two drive waits on the CivitAI fetch, so a slow CivitAI no longer makes a healthy panel look dead (#1189) ## [0.14.26] - 2026-08-13 ### Changed - **Restarting the agent holds its exclusion flag until the reconnect (#1171).** The panel keeps one flag so that restarting the agent and reloading it cannot run at the same time, but the restart released it as soon as the request came back — while it was still ending the current turn, clearing the markers that say a turn is in flight, invalidating the old session, and reconnecting. The flag is now held until the reconnect, which is what the reload path already did, so the two agree. **No visible change when the panel manages ComfyUI's agent the usual way**: this distribution's restart route always answers "not restarted" (the orchestrator runs out-of-band), so the work this now protects is skipped anyway. It matters for setups whose restart route really does restart the agent, and it removes the window before one of those hits it. ### Fixed ## [0.14.25] - 2026-08-13 ### Fixed - **Setting a widget no longer hangs for 30 seconds after a ComfyUI restart (#1161).** Once ComfyUI had been restarted mid-session, setting any widget on any node timed out, every time, while every other panel command answered instantly — reading the graph, renaming a node, listing workflows, queueing a run. Setting a widget is the one action that reads the backend's node definitions before it writes, and a restart can leave the browser holding a connection that never answers and never fails, so that read waited forever. The panel already had a second way to ask — a direct request that keeps working when the first route does not — but it was never reached, because nothing gave up on the first one. The lookup now has an overall time budget, so a route that stops answering falls through to the one that does and the write simply succeeds. The budget covers the whole lookup rather than being handed to each step in turn, so the wait cannot stack — but the second route is also guaranteed a share of it, because a first route that stops answering would otherwise use the budget up and leave nothing for the route that still works. A route that answers quickly hands back the time it did not use, so a slow install still gets the whole budget to finish in. The budget is twenty seconds, which is generous rather than tight: fetching the whole node-definition document was measured at well under a second even on a large install with sixty-odd node packs. If nothing answers, the refusal names every attempt and says how long each one was actually given, rather than quoting a wait it never spent. ### Fixed ## [0.14.24] - 2026-08-12 ### Fixed - **A successful agent restart no longer leaves the old turn's state armed against a fresh agent (#1166).** When a restart genuinely replaces the agent, the panel retires the markers that say a turn is in flight. Those clears all sat after a step that can bail out — if the old session could not be invalidated durably, the restart returned early and skipped every one of them. The killed turn, a pending soft-reload marker and the restart-resume marker were all left armed against an agent that no longer existed, so the next reconnect could announce a reload that never happened, or resume the very conversation the restart was meant to discard. They are now retired before that step, so nothing it does can skip them. That pause itself is deliberate and stays: reconnecting there would restore the session the restart exists to throw away. What it did not do was say so — the status chip, dot and buttons still showed a live connection, so the panel contradicted its own message and left no way to act on it. It now shows the real state and restores the Connect button. ## [0.14.23] - 2026-08-12 ### Fixed - **The mid-task nudge no longer fires on a turn you just started (#1163).** A defect in the outage accounting added for #1145, found by review of that change rather than in the wild. A turn beginning while the bridge was still down zeroed the measured outage but left the outage itself running, so it was later measured from before that turn existed. Reachable in ordinary use: sending a message needs only an open socket, not a completed handshake, and the socket comes back well before the model list does — so typing into that gap could draw "your connection dropped mid-task — continue exactly what you were doing" on top of the message just sent, making the agent restart or duplicate the work it was asked to do. A turn start now ENDS the outage rather than only zeroing the total. What that frame proves is narrow but sufficient: a turn is in flight. The nudge exists solely to rescue an agent left IDLE — a session resumed with full context and nothing pending — so once a turn is running there is nothing to rescue, whether it is the original turn re-announced by an orchestrator that survived or a new one just typed into a replaced agent. Nudging either is the harm. The converse still holds and is covered by tests: an orchestrator that died has no turn to announce, so its nudge fires as before. ## [0.14.22] - 2026-08-12 ### Fixed - let a truncated provider hint be read on hover (#1165) - translate the "why can't I use this provider" hints in all 11 languages (#1162) - translate the 22 strings that answer "why can't I use this provider" (#1160) - **A ComfyUI restart that comes back quickly keeps its nudge (#1145).** The nudge tells a resumed agent its connection dropped mid-task and to continue what it was doing, and it fires only for a REAL restart — judged by how long the bridge was gone. But the panel assigns its socket before the connection resolves, so a REFUSED reconnect attempt is still the active socket and ran the close handler in full, re-stamping the drop time. With backoff doubling, the retries land near one, three, seven and fifteen seconds, so the attempt that finally connected was separated from the previous failed close by only that attempt's delay. The guard weighed its own retry cadence, not the outage: a genuine restart returning inside about seven seconds measured roughly four and lost its nudge, leaving the agent idle with a resumed session and no pending turn — after exactly the event the nudge exists for. The outage is now stamped once, by the first close that begins it, and its duration measured at the handshake that ends it. A second reading of the same guard is closed with it: a drop stamp answers "how long since the last drop, whenever that was", and kept answering after the outage it described had ended — so a `ready` repeating on a live socket (the panel re-advertises after every successful `free_vram`, #310) could be told about a drop from ten minutes and two reconnects earlier. That is #1138's defect with a real timestamp in place of the zero sentinel. What the guard reads is now a measured duration, scoped to the turn now running, and a handshake that ended no outage records nothing rather than inheriting the previous one. The accounting moved into a tracker with unit tests that drive it through the real sequence — drop, refused retry, refused retry, handshake — because no single call can distinguish a drop from the third refused attempt, and a source scan over the panel could not have caught this (a review previously proved by mutation that such scans stay green through an inversion). Each guard was mutation-checked against those tests. The two sibling `ready`-ack branches were audited for the same class and are clean: the reboot-resume branch already decides on an observed drop rather than elapsed time ("elapsed time is not evidence of a new connection; an observed drop is"), and the soft-reload branch reads a marker it sets and clears itself. The mid-task branch was the only one deriving a restart from a clock. ## [0.14.21] - 2026-08-12 ### Added - Brazilian Portuguese (pt-BR) panel catalog — 1107 keys + 52 settings (#1156) - Russian (ru) panel catalog — 1161 keys, all four plural forms (#1155) - complete the Simplified Chinese (zh) panel catalog — 353 → 999 keys (#1151) ### Fixed - an empty baseline is not proof of a different graph, and say what actually recovers (#1158) ## [0.14.20] - 2026-08-12 ### Added - Spanish (es) panel catalog (#1152) - add the Arabic (ar) panel catalog (#1143) - add the Traditional Chinese (zh-TW) panel catalog (#1153) - add the French (fr) panel catalog — 1107 keys, one/many/other plurals (#1150) - add the Turkish (tr) panel catalog (#1148) - add the Persian (fa) panel catalog (#1147) ### Fixed - a definitions difference that is only link renumbering is not a content change (#1125) - the status pill froze because onStatus threw on every status frame (#1154) ## [0.14.19] - 2026-08-12 ### Added - complete the Japanese panel translation (999 keys) (#1140) ### Fixed - name every uninstalled node type before queueing, not one rejection at a time (#1129) - decode string escapes when extracting, so \n is a line break and not two characters (#1144) ## [0.14.18] - 2026-08-12 ### Fixed - **A live-socket re-advertise no longer reads as a fresh connect, so a benign reconnect cannot tell you your connection dropped mid-task (#1138).** The panel injects a user message — "Your connection dropped mid-task … continue exactly what you were doing" — plus a transcript line whenever a `ready` ack arrives with a mid-task marker set. The guard meant to suppress that on a live session read `Date.now() - lastBridgeDownAt < 6000`, and that timestamp is `0` until the bridge socket actually closes. So a bridge that never dropped presented as a ~56-year gap — the longest possible — which a long-gap-means-real-restart heuristic read as the strongest possible evidence of a restart. The guard was exactly inverted in the case it existed to catch: the better established that nothing had dropped, the more confidently it fired. Reachable in normal use, because `ready` repeats on a live socket and the panel re-advertises after every successful `free_vram` by design (#310). Freeing VRAM mid-task could therefore tell you your connection had dropped when nothing had, and tell a still-working agent to resume — making it restart or duplicate the render it was already running. A real restart still nudges, including exactly at the six-second boundary. The decision moved into a pure predicate so it is covered by behaviour rather than by a source scan: a review demonstrated by mutation that token-presence tests over this file stay green when such a guard is inverted, which is the one regression that matters here. Two related problems were found and deliberately left for their own issue rather than patched here: every FAILED reconnect attempt re-stamps that timestamp, so the gap can measure a backoff step instead of the outage; and the other `ready`-ack branches have not been audited for the same sentinel exposure. ## [0.14.17] - 2026-08-12 ### Added - freeze the English catalog and give translators a rendering instrument (#1135) ### Fixed - the status chip must report the socket the session actually uses (#1137) - disclose the foreign source state on a FAILING open too (#1131) - a successful open must not report a canvas it did not verify (#1110) - say whether the workflow list was actually re-read, and stop blaming the folder (#1123) - advertise the vendored tool vocabulary in the hello (#1119) - a numeric from_output reuses the rail slot instead of minting one named "4" (#1117) - panel_screenshot stops throwing when a node has no type (#1115) - render the effort LABEL, not the raw token - the connect-screen blurb, and a broader audit than sinks can give - an open that detects the wrong graph must refuse, not just say so (#1112) - stop asking every run-to-node caller to report a permanent fallback (#1107) - refuse a write to rgthree's Fast Groups toggle — it is a derived readout (#1106) - a correction to an identical value is not a correction (#1104) - warn when a direct write lands on a link-driven widget (#1102) - name both ChatGPT routes, and stop the label map gating the handshake (#1100) - a host probe must not shrink an authoritative provider list (#1094) - don't capture another workflow's canvas into the tab being opened (#1092) - wire the strings no coverage metric could see - resolve subgraph-qualified node ids instead of coercing them to NaN (#1090) - rgthree seeds are invisible to the batch-repeat warning (#1082) - Korean is complete — every panel string now has a translation - core SaveGLB is addable — a 3D file union names formats nothing OUTPUTS (#1078) - fill Korean from 37% to 50% — the visible chrome was untranslated - defect (2) — scope the #226 guard to the hazard it names (#1075) - satisfy the tool-vocabulary gate, which this batch tripped four ways ### Changed - 0.14.16 (#1132) - 0.14.15 (#1128) - 0.14.14 (#1121) - re-vendor the tool vocabulary — the handshake found real drift (#1120) - 0.14.13 (#1118) - 0.14.12 (#1116) - 0.14.11 (#1113) - 0.14.10 (#1109) - 0.14.9 (#1105) - 0.14.8 (#1103) - 0.14.7 (#1101) - 0.14.6 (#1099) - 0.14.5 (#1093) - 0.14.4 (#1091) - 0.14.3 (#1086) - 0.14.2 (#1081) - 0.14.1 (#1076) - 0.13.9 (#1072) ## [0.14.16] - 2026-08-12 ### Fixed - **`panel_open_workflow` now warns when the graph it painted may be another workflow's (#1089).** The reporter got a clean success — right path, right filename, right `workflow_uuid`, `modified: false` — while the canvas held the graph of the workflow they had just saved-as FROM. No warning of any kind. Their next calls were `panel_remove_node`, and Save-As preserves ids, so those deletions would largely have LANDED, on the wrong workflow, silently. Nothing was fooled, which is why no existing check caught it. All four parts of the post-repaint proof are taken against the root the LOADER produced; none of them looks at the state the load was handed. When that state holds another workflow's graph, the open reproduces it faithfully and every part passes — each a true statement about a poisoned SOURCE. That is also why the other report on the same end state (#1111) DID warn while this one did not: there the state had not been contaminated, so the comparison had something to disagree with. The reply now carries `foreign_source_state` when the state provably held a different OPEN workflow's identity. It says to verify the graph before editing, explains that every other field on the reply is TRUE of the tab and says nothing about which graph the state held, and names the disk recovery together with its cost — a tab reporting no unsaved edits can still lose values a NODE wrote rather than the user (a populated wildcard, a rolled seed, #874). It says MAY be, not IS, and deliberately: a tab switch can leave this tab's OWN graph sitting under another tab's metadata residue (#817), which is indistinguishable from a foreign graph, so only the caller's comparison separates them. **This warns, it does not prevent.** Two stronger remedies were built and removed, and both are recorded in the code so they are not tried again. Refusing the open removes the repaint's root re-stamp — the one documented heal for a conflicting root tag — and strands every `graph_*` command, including the `panel_load_workflow` the refusal recommended, whose own error sends the caller back into the refusal. Auto-correcting from disk cannot be gated safely, because the tab's modified flag is wrong in both directions: it misses node-written values, and it stays spuriously set for the life of any tab the panel opened cold. - **The same finding now rides a FAILING open too (#1089).** It was attached to the success reply only, so an open that also failed content verification dropped it — the worse combination, and the one #1111 reported: a mismatch WAS announced and the canvas was still the previous workflow's. The content warning says to re-read the graph; it did not say the state was another workflow's, which is the part that explains why the re-read looks plausible rather than alarming. ### Changed - **Korean is complete — every panel string now has a translation (#1080).** The visible chrome had been left untranslated at 37% coverage; the connect-screen blurb, the effort label (which rendered its raw token rather than a name), and a set of strings no coverage metric could see are now wired. ## [0.14.15] - 2026-08-12 ### Fixed - **`panel_open_workflow` no longer claims a refresh it never performed (#1448).** The refusal said the file "isn't among the saved/open workflows even after a refresh" — for a file the reporter had confirmed on disk INSIDE that folder, twice. Both ways the refresh can fail to happen were silent: a frontend without `syncWorkflows` skipped it entirely, and a throw was swallowed by a `console.warn` no agent session reads. It now reports which actually occurred — list re-read, no sync method on this frontend, or re-read failed with the reason — and says outright that a skipped or failed re-read is NOT evidence the file is absent. Its remedy also stopped naming the wrong cause: "for a file outside the workflows folder" reads as a diagnosis, and sent someone away from a file that was exactly where they thought. `panel_load_workflow` is still offered, as a branch rather than a verdict. The refusal now also shows the selector SHAPES the store actually holds, which are not guessable from outside: `path` is `workflows/X.json`, `filename` carries NO extension, and `key` does. ## [0.14.14] - 2026-08-12 ### Fixed - **The panel now says WHICH tool vocabulary it vendored, at connect (#236).** The panel calls MCP tool names as bare string literals and validates them against a vendored copy of the vocabulary — which proves the literals match that copy, never that the copy matches the server it is talking to. When the two disagreed, the failure surfaced at call time as `unknown tool`, which reads as a broken panel and gives an agent nothing to act on. The hello now carries a hash of the vendored vocabulary, and the orchestrator compares it at connect (comfyui-mcp 0.51.13). A version string cannot do this job: two builds of one version can carry different vocabularies, and two versions can carry identical ones. Safe in both directions of skew — an orchestrator that predates the check ignores the field, and one that has it reads an ABSENT hash as unverified, never as disagreement. - **Re-vendored the tool vocabulary.** The first live run of that handshake found real drift: the vendored copy was missing `panel_remove_widget`, 91 panel tools against the server's 92. Found by the mechanism built for it rather than by someone hitting `unknown tool`. ## [0.14.13] - 2026-08-11 ### Fixed - **A numeric `from_output` no longer mints a junk rail slot named "4" (#1114).** Inside a subgraph, `panel_connect({ from_output: 4, ... })` replied `exposed` rather than `connected` and left a permanent rail input literally named `"4"`, visible on the parent subgraph node too. The rail lookup gated its index branch on `typeof ref === "number"`, but MCP argument coercion delivers `4` as the string `"4"` — so the lookup missed, and the caller read the miss as "no such slot" and created one. A lookup that failed closed would have been a refusal; this one edited the graph. The index parse is deliberately strict (`"04"` and `"007"` are names, not index 4 and 7), so a mistyped name cannot land silently on an unrelated slot. And when a ref matches BOTH a slot name and a different index — a rail whose slots are digit-named out of order — it now refuses and names both candidates instead of guessing, because nothing at that point can tell which was meant. ## [0.14.12] - 2026-08-11 ### Fixed - panel_screenshot stops throwing when a node has no type (#1115) - an open that detects the wrong graph must refuse, not just say so (#1112) - stop asking every run-to-node caller to report a permanent fallback (#1107) - refuse a write to rgthree's Fast Groups toggle — it is a derived readout (#1106) - a correction to an identical value is not a correction (#1104) - warn when a direct write lands on a link-driven widget (#1102) - name both ChatGPT routes, and stop the label map gating the handshake (#1100) - a host probe must not shrink an authoritative provider list (#1094) - don't capture another workflow's canvas into the tab being opened (#1092) - resolve subgraph-qualified node ids instead of coercing them to NaN (#1090) - rgthree seeds are invisible to the batch-repeat warning (#1082) - core SaveGLB is addable — a 3D file union names formats nothing OUTPUTS (#1078) - defect (2) — scope the #226 guard to the hazard it names (#1075) - satisfy the tool-vocabulary gate, which this batch tripped four ways ### Changed - 0.14.11 (#1113) - 0.14.10 (#1109) - 0.14.9 (#1105) - 0.14.8 (#1103) - 0.14.7 (#1101) - 0.14.6 (#1099) - 0.14.5 (#1093) - 0.14.4 (#1091) - 0.14.3 (#1086) - 0.14.2 (#1081) - 0.14.1 (#1076) - 0.13.9 (#1072) ## [0.14.11] - 2026-08-11 ### Fixed - an open that detects the wrong graph must refuse, not just say so (#1112) - stop asking every run-to-node caller to report a permanent fallback (#1107) - refuse a write to rgthree's Fast Groups toggle — it is a derived readout (#1106) - a correction to an identical value is not a correction (#1104) - warn when a direct write lands on a link-driven widget (#1102) - name both ChatGPT routes, and stop the label map gating the handshake (#1100) - a host probe must not shrink an authoritative provider list (#1094) - don't capture another workflow's canvas into the tab being opened (#1092) - resolve subgraph-qualified node ids instead of coercing them to NaN (#1090) - rgthree seeds are invisible to the batch-repeat warning (#1082) - core SaveGLB is addable — a 3D file union names formats nothing OUTPUTS (#1078) - defect (2) — scope the #226 guard to the hazard it names (#1075) - satisfy the tool-vocabulary gate, which this batch tripped four ways ### Changed - 0.14.10 (#1109) - 0.14.9 (#1105) - 0.14.8 (#1103) - 0.14.7 (#1101) - 0.14.6 (#1099) - 0.14.5 (#1093) - 0.14.4 (#1091) - 0.14.3 (#1086) - 0.14.2 (#1081) - 0.14.1 (#1076) - 0.13.9 (#1072) ## [0.14.10] - 2026-08-11 ### Fixed - stop asking every run-to-node caller to report a permanent fallback (#1107) - refuse a write to rgthree's Fast Groups toggle — it is a derived readout (#1106) - a correction to an identical value is not a correction (#1104) - warn when a direct write lands on a link-driven widget (#1102) - name both ChatGPT routes, and stop the label map gating the handshake (#1100) - a host probe must not shrink an authoritative provider list (#1094) - don't capture another workflow's canvas into the tab being opened (#1092) - resolve subgraph-qualified node ids instead of coercing them to NaN (#1090) - rgthree seeds are invisible to the batch-repeat warning (#1082) - core SaveGLB is addable — a 3D file union names formats nothing OUTPUTS (#1078) - defect (2) — scope the #226 guard to the hazard it names (#1075) - satisfy the tool-vocabulary gate, which this batch tripped four ways ### Changed - 0.14.9 (#1105) - 0.14.8 (#1103) - 0.14.7 (#1101) - 0.14.6 (#1099) - 0.14.5 (#1093) - 0.14.4 (#1091) - 0.14.3 (#1086) - 0.14.2 (#1081) - 0.14.1 (#1076) - 0.13.9 (#1072) ## [0.14.9] - 2026-08-11 > #1085: adding an ImageCropV2 warned that this tab's node definitions were out of date and > that a value had been replaced — showing the old and new values side by side, identical. ### Fixed - adding a node no longer reports a value as "corrected" when nothing about it changed. The check compared values by identity rather than by content, and a value that is a group of numbers — like ImageCropV2's crop region — is a fresh group every time it is read, so it never matched itself. Every add of such a node raised the warning and told you to reload the tab. ### Changed - values are now compared by what they contain, at any nesting depth. A value that genuinely changed is still corrected and still reported — that warning exists for a real case and it keeps working. - anything the comparison cannot read faithfully is treated as changed rather than guessed at, which is what it did before. That is the safe direction: it can mention a correction that was not needed, but it will never stay silent about one that was. ## [0.14.8] - 2026-08-11 > #1087: setting a widget inside a subgraph reported success and changed nothing about the > render — a run asked for 10 steps and sampled at 14, with nothing to indicate the value > had been ignored. ### Fixed - setting a widget that is fed by a connection now tells you it will not affect the render. When a subgraph promotes a widget to its outer node, the inner copy is driven by that connection and the outer value is what actually runs — so writing the inner one stored a number nothing reads. The write still happens (that inner value is the subgraph's stored default, and setting it is a reasonable thing to want), but the reply now says plainly that it will not change the output, and points at the outer node to set instead. ### Changed - the check reuses exactly what the graph outline already shows for these widgets, so it reports the same connection the outline names rather than a second opinion. - writing the widget on the OUTER subgraph node is unchanged and still the way to make it take effect — that path already updates both copies, and it does not warn. ## [0.14.7] - 2026-08-11 > #1084: the provider picker showed "ChatGPT" next to a lowercase "chatgpt", which looked > like an accidental duplicate and gave you nothing to choose between. ### Fixed - both ways of reaching ChatGPT are now named. They are genuinely different routes to the same subscription — **ChatGPT (Codex)** runs it through the Codex app-server, **ChatGPT (direct OAuth)** talks to it directly with no extra process — and only the first had a name, so the second showed its raw id. - the panel now knows which provider it is connected to when you use the direct-OAuth route. This was the larger half: the panel decided whether a provider was "known" by whether it had a display name for it, so the unnamed one skipped the step that records the connection — leaving the remembered provider, the "Ask …" prompt and the highlighted chip all showing the previous one. ### Changed - a provider your agent machine reports but this panel version has never heard of is now accepted rather than ignored, and shown under its own id until a release names it. New providers can land before the panel ships a label for them, and that ordering is normal. - ComfyUI's Settings dropdown lists both routes too, so the default provider can be set to either. ## [0.14.6] - 2026-08-11 > #1083: connect to ChatGPT, reopen the model picker, and LM Studio, llama.cpp, Custom > endpoint and Copilot are gone — with no way back to a Custom endpoint you had configured. ### Fixed - the provider list no longer loses providers after connecting. The panel learns which providers exist from two places: the machine actually running your agents, which knows about all of them, and ComfyUI itself, which only knows a shorter built-in list. A routine background refresh from ComfyUI was replacing the full list with the short one, so everything past OpenRouter disappeared from the picker. ### Changed - the shorter list is now merged into the fuller one instead of replacing it. A provider ComfyUI knows about but your agent machine did not mention is still added, and one it has stopped reporting can still go away — what it can no longer do is delete a provider the agent machine told us about. - a provider's Running indicator still updates from those refreshes, so this does not trade a disappearing provider for one that looks permanently idle. - the "(experimental)" marking and its terms-of-service warning on GitHub Copilot are now held back from those refreshes too, so a routine poll cannot quietly drop the warning. ## [0.14.5] - 2026-08-11 > #968: opening one workflow could silently give it a DIFFERENT workflow's graph — and > every check said it was fine, honestly. This is the cause, found and fixed. ### Fixed - switching to a workflow no longer copies the previous tab's graph into it. The panel switched tabs, then took a snapshot of the canvas, then repainted — and the snapshot ran while the canvas still showed the tab you were leaving. So the workflow you opened had the OTHER one's graph written into its unsaved state, was marked as edited, and was then repainted from exactly that. Every check afterwards compared the canvas to that state and agreed, because by then they genuinely matched. Only the file on disk disagreed, which is why reloading from disk was the one thing that fixed it. ### Changed - the snapshot is now skipped when the canvas provably belongs to another workflow. When it provably belongs to the one being opened — the ordinary case, and the one the snapshot was added for — nothing changes at all. - one case is knowingly not covered: a canvas the panel has never tagged cannot be attributed to anyone, so it is still snapshotted as before. Guessing there would mean either overwriting your live edits or writing the wrong graph, and neither is worth doing on a guess. ## [0.14.4] - 2026-08-11 ### Fixed - resolve subgraph-qualified node ids instead of coercing them to NaN (#1090) - rgthree seeds are invisible to the batch-repeat warning (#1082) - core SaveGLB is addable — a 3D file union names formats nothing OUTPUTS (#1078) - defect (2) — scope the #226 guard to the hazard it names (#1075) - satisfy the tool-vocabulary gate, which this batch tripped four ways ### Changed - 0.14.3 (#1086) - 0.14.2 (#1081) - 0.14.1 (#1076) - 0.13.9 (#1072) ## [0.14.3] - 2026-08-11 > #1339: a batch of ten came back as ten identical images, with nothing said about it. The > warning that exists for exactly this looked for ComfyUI's own `control_after_generate` > widget — and rgthree's Seed node DELETES that widget, so the most widely used custom seed > node was invisible to it. ### Fixed - a batch that will reuse one seed now says so when the seed comes from an rgthree Seed node. ### Changed - it reports the thing that actually decides the outcome: whether the node is ARMED (its seed widget holding `-1`, `-2` or `-3`) or holds a concrete number that is submitted verbatim for every item. It stays quiet when the node genuinely varies — measured, an armed node gets a fresh seed per item even in a scoped batch, so the older warning's reasoning does not apply to it and is not reused. - an armed node can still repeat when its `randomMin`/`randomMax` properties — or the seed widget's step — admit a single value. Measured over 200 draws, `min=0 max=5 step=100` returns ONE value while `min < max` looks perfectly healthy. That case is named too, with the remedy that fits it. - a muted or bypassed seed node is not named, since it contributes nothing to the run. ### Notes - your seeds are never rewritten. This says what will happen; it does not change your values. ## [0.14.2] - 2026-08-11 > #1062: asking the agent to add `SaveGLB` always failed. It is the only core node that > writes a `.glb`, so while this was broken you could not build an image-to-3D or > text-to-3D workflow through the panel at all. ### Fixed - `SaveGLB` can be added again. Its `mesh` input accepts a list of 3D formats, and the panel refused to place the node unless it could prove every single one was a real connection type. Seven of the fourteen are formats ComfyUI can WRITE that nothing on your machine PRODUCES — so the proof could never be found, on any install, and retrying or refreshing the node list could not help. ComfyUI's core 3D file formats now count as connection types on their own. ### Changed - the check that this relaxes is the one that stops a node being added when an input needs a widget that never loaded, so it was loosened narrowly and in one direction only: the list of 3D formats is a fixed set of the 13 ComfyUI ships, not a `FILE_3D_*` pattern. A custom node inventing its own `FILE_3D_…` type is still held to the same proof as before, and an input that asks for a real widget still waits for it. - an input that names the widget it draws as (`widgetType`) is now correctly treated as a widget rather than a connection — a gap found while making the above safe, which could have let a node be added with neither a value nor a connection on a required input. Where that name is one of the four built-in kinds, it needs nothing loaded and no longer waits. ## [0.14.1] - 2026-08-11 > #1066, the other half: 0.13.9 stopped a URL-derived tab from building an unwritable > `workflows/http://…/YourName.json`, but the tab still could not be saved. A second guard > refused every name, because it could not prove what was on disk at the URL path — and > nothing can. This is the fix that lets the tab save. ### Fixed - a temporary tab whose path came from a URL now saves. The refusal came from a safety guard that protects against a save MOVING (destroying) the original file. That could happen when ComfyUI's `saveWorkflowAs` was the copy route — it moves a temporary rather than copying it. The panel stopped using it some time ago; the only relocating route left builds a new workflow in memory and writes that, never touching the source's file. So the guard now runs after the route is chosen and refuses only when no move-free route exists. ### Changed - three separate attempts to prove such a source absent are recorded in #1066 as dead ends, because the trap is easy to fall into: the path shape (`://`) proves nothing — on Linux a real folder can legally be named `notes://draft`; the `isTemporary` flag proves nothing — real saved files can carry it after a load race; and ComfyUI's `/userdata` answers a URL-shaped path with a **500**, not a "not found", so the disk cannot answer either. The fix does not add a fourth attempt. It stops needing the proof. - the "the original is gone" safety check now also runs for a source that could not be classified, which is the case with the least evidence and so the one that most needs checking afterwards. Its message now reports what was observed rather than blaming the save: two checks can prove a file DISAPPEARED, not who removed it. ### Notes - your workflows are protected by the same rule as before — a save may never remove a file that exists. What changed is only where that rule can be broken, and the check that catches it if it ever is. ## [0.14.0] - 2026-08-11 > The panel shipped English-only while ComfyUI ships 12 languages, so setting ComfyUI to > Korean gave you an English sidebar inside a Korean app. ### Added - the panel, every sub-panel and its helper text are translated — roughly 1,000 strings. Korean, Simplified Chinese and Japanese are filled in; the other eight fall back to English per string until they are. The 52 rows in ComfyUI's own Settings dialog translate too. - a **Panel language** setting: follow ComfyUI's language automatically, or pick one of the same 12 explicitly. - Arabic and Persian lay out right-to-left, scoped to the panel so ComfyUI's canvas is untouched. ### Changed - counts read correctly in every language. "1 model / 3 models" is an English rule; Korean has one form, Russian four, Arabic six, and the panel now uses each language's actual rules. ### Notes - this entry was written after the fact — 0.14.0 shipped without one, which meant the panel's own changelog view had nothing to show for the release. ## [0.13.9] - 2026-08-11 > #1066: open an output image in ComfyUI and it mints a temporary workflow tab whose path is > the URL the asset came from. Renaming that tab replaces only its filename — so the URL > stays on as the tab's folder, and the tab could not be saved under ANY name. ### Fixed - saving such a tab no longer builds `workflows/http://127.0.0.1:8188/api/YourName.json` and fails with a 500. A URL-shaped folder is recognised and the save is redirected to the workflows root, the same as any other unwritable location. ### Changed - a URL-derived tab is deliberately NOT treated as an "external file". That classification drives a copy route whose whole premise is a real file on disk to copy — and a URL is not one, so treating it that way kept the tab unsaveable by a different path. This is why the reporter's own first successful save required treating the URL source as never persisted. - one known cost, taken deliberately: on Linux/macOS a folder can legally be named with `://` in it, and a tab in such a folder now has its Save-As redirected to the workflows root. A redirected save is visible and recoverable; the 500 it replaces was not. Windows cannot hit this at all, since `:` is illegal in a filename there. ## [0.13.8] - 2026-08-11 > #968: three reports of the panel saying a tab was bound to the workflow you asked for > while the canvas held a DIFFERENT workflow's graph — once queueing the wrong one. Every > check reported healthy, and every check was telling the truth. ### Added - `panel_open_workflow` on an already-open tab now compares the file it just read against the canvas, and says so when they share **no node ids at all**. In the report that was 44 nodes on screen against 40 in the file with none in common — which is not what editing looks like. ### Changed - it is a DISCLOSURE, not a refusal. Clearing a tab and rebuilding, or pasting a whole graph in before saving, look the same from here, so the note names those alternatives and leaves the judgement to you. A refusal built on that ambiguity would be a wrong-graph refusal of its own — the same class of harm as the bug. - why nothing caught this before: the repaint loads from the tab's own state and proves the canvas against **that state**, while the staleness check compares the file against the tab's **baseline**. Both pass honestly when the tab's own state is carrying another workflow's graph. Nobody compared the file to the canvas, even though that code path had already read the file. ## [0.13.7] - 2026-08-11 > artokun/comfyui-mcp#938: an agent could WRITE a dynamic widget row and could delete the > whole NODE, but had no way to delete one row — the add/remove affordance is a canvas-drawn > button it cannot click. ### Added - `graph_remove_widget` removes ONE dynamic widget row (rgthree Power Lora Loader `lora_N`, Impact/Inspire list rows). Undoable with Ctrl+Z. ### Fixed - the remaining rows are NOT renumbered. `lora_N` is a monotonic id, not a position: `configure()` re-mints the names from serialized ORDER on every load, and the backend reads `**kwargs` filtered by name prefix, so gaps never reach it. The reply lists the remaining names, because an agent that assumed renumbering would address the wrong row. ### Changed - removal is refused, with the specific reason, for an input the BACKEND declares (it would change what is sent at queue time), a frontend-generated control widget, a widget whose input slot currently has a link, and a subgraph container's promoted widgets. Node definitions that could not be READ are reported as unknown rather than treated as "declares nothing" — the difference between those two is the only thing separating a row from a KSampler's `steps`. ### Fixed - a DUPLICATE delivery of a request the panel is already running no longer waits forever on it (#646). The command ledger marks a command in-flight and never evicts that entry — dropping an unsettled command would let a replay apply the same mutation twice — so an executor that never returned left every redelivery awaiting a promise that could not resolve, and the panel answered nothing at all. The duplicate now gets a real answer: still running, nothing was applied twice, do not retry, read the graph to see whether it took effect. - that wait is bounded by the CALLER's own deadline, never by a number the panel invents. A fixed timeout is wrong in both directions — too high rescues nobody, too low reports "still running" for a command that was merely slow. The panel cannot see that deadline today, so absent it the behaviour is unchanged; this half activates when the orchestrator sends the timeout it already computes. ## [0.13.6] - 2026-08-11 > #968: three reports of the panel saying "bound to the requested workflow" while graph > commands kept hitting the previous one — once queueing the wrong workflow outright. They > have not converged because, after the fact, a stale binding and a fresh one look identical. ### Added - a workflow-instance refusal now reports **what last moved the active workflow**, when anything did. If a panel command made the move it is named. If nothing claimed it, the refusal says so — and says plainly that this does NOT prove the panel was uninvolved, because not every command can register a claim. What it does establish either way is that a binding taken before that move is stale. ### Changed - it decides nothing, deliberately. No refusal becomes an acceptance, and a refusal with no move to report is byte-for-byte what it was. Widening trust on an entry route nobody has identified is how a refusal turns into the silent wrong-graph edit this issue is about. - ruled out along the way, and recorded on the issue: `panel_open_workflow` forces the canvas repaint itself and verifies it, both of its skip paths fail closed, and the report where the wrong workflow was queued ran on a build that already had both protections. So the binding is correct when it is made, and something re-points the tab afterwards. ## [0.13.5] - 2026-08-11 > #1369: `panel_add_node` applied a node definition's declared default over a widget's > value whenever the two differed. For a COMBO whose option list does not CONTAIN that > default, the "correction" wrote a value the node cannot accept — and reported success. ### Fixed - a stale-schema "correction" no longer rewrites a valid COMBO widget to a value the node cannot accept (#1369). The live KJNodes def is `"sage_attention": [["disabled","auto",...], {"default": false}]` — the widget held a valid `"disabled"` and was overwritten with `false`. - when a declared default is absent from that same definition's option list, the existing value is KEPT and the refusal recorded, rather than written and called a correction. ### Changed - verified against this machine's live ComfyUI (4183 node types): of 1105 combo inputs carrying a default, 1083 corrections still apply and 22 are refused. The 22 are the reported KJNodes case plus model-filename defaults naming files not installed here, which ComfyUI's own `validate_inputs` would reject as well. No valid default was refused — the direction that would have quietly disabled the correction entirely. ## [0.13.4] - 2026-08-10 > #584: a ComfyUI tab that keeps running OLD panel JS after a reload, so the orchestrator > sees a stale version and refuses graph writes. This is a backstop for hosts that leave the > door open — not a cure, and the notes below say exactly which. ### Fixed - the pack now gives its own `/extensions/comfyui-mcp-panel/` assets a cache policy on hosts that set none, matching the value current ComfyUI already applies to every extension. Older ComfyUI builds serve extension files with an ETag from mtime+size and no policy, which is the shape that lets a replaced file keep being served from cache. ### Changed - **Measured before claiming**: ComfyUI 0.31.1 already sends `Cache-Control: no-store` for every `/extensions/` path, so on current builds this changes nothing. The staleness reproduced while developing 0.13.3 turned out not to be a cache at all — it was a reload cancelled by ComfyUI's unsaved-changes prompt. - A first attempt used a weaker header and was described as a no-op. It was not: this pack's middleware runs INSIDE ComfyUI's own, so the host's `setdefault` preserved the weaker value and the panel's assets ended up with a LOOSER policy than every other pack's. The shipped version matches the host's value, so that inversion cannot happen. ## [0.13.3] - 2026-08-10 > #753: the panel's text was small and the obvious fix did nothing. Overriding > `.cmcp-root { font-size }` scaled almost nothing, because every inner size was `rem` — > resolved against the PAGE root, not the panel. ### Added - **`--cmcp-fs`, one variable that scales the panel's text.** Set it on `:root` or `.cmcp-root` in a user stylesheet (default `0.8125rem`) and every panel font size follows, including the CivitAI explorer, the Apps tab and the modals. Measured: overriding it to 1.5x moves 2110 of 2476 rendered elements by exactly that. - It does NOT scale spacing, icons, or the handful of elements that carry a fixed pixel size, and the setting's tooltip now says so rather than promising more. ### Changed - 214 inner font sizes became `calc(var(--cmcp-fs, 0.8125rem) * k)`, each reproducing its original pixel size at the default — verified element by element against a capture of the panel taken before the change, with no drift beyond sub-pixel rounding. - The "Panel UI scale (%)" setting is unchanged and is still the way to scale the panel as a whole. Its tooltip used to explain why a stylesheet override could not work; that trap is gone, so it now names the knob that does. ### Fixed - the status caret set its size through a JS assignment rather than a stylesheet rule, so it had been missed by every sweep. It scales with the rest now. ## [0.13.2] - 2026-08-10 > #945: the panel could not tell one workflow OBJECT from another without looking at the > canvas — so two guards that decide whether an identity belongs to a copy were deciding > nothing at all. ### Fixed - a workflow that is not the one on screen can now be asked for its own identity (#945). The three fields that lookup used are absent on current ComfyUI, so it answered null every time, and both fork guards behind it short-circuited on their first line. It now falls back to the workflow's own captured state — but only for a workflow that is NOT mounted, and the reason for that restriction is the whole of this fix: for the workflow on screen, that field is a clone of the live canvas, so reading it there would answer "whose identity is this?" with "whatever is on screen", which is precisely what the caller asked it not to do. - the mounted check compares identity the way the rest of the panel does, rather than with `===`. ComfyUI hands out reactive proxies in some places and raw objects in others, and the path that matters most here unwraps deliberately — measured on this machine, the active workflow IS a proxy whose raw target is a different object, so a strict comparison would have let the canvas back in with no race required. ### Changed - where identity is WRITTEN is untouched. Embedding into the workflow's captured state moves where identity persists — it stops reaching the graph that a save serializes — and was reverted once for exactly that. Reading a field is not writing it. ## [0.13.1] - 2026-08-10 > The other half of #952. A question card whose connection was replaced already stopped > looking answerable in 0.13.0; the command behind it, however, was left waiting forever. ### Fixed - withdrawing an interactive card now ENDS the command behind it (#952). `panel_ask` and the secret request are the only commands whose executor blocks on a person, and retiring their card deliberately did not resolve its promise — so the executor stayed suspended, its entry in the command ledger stayed in flight, and entries in flight are never evicted, by design. Every withdrawn question kept a slot for the life of the tab and held the ledger's bound out of reach; a duplicate delivery of that request id then waited on a promise that could never resolve, and the panel answered nothing at all. Retirement now settles the command explicitly, as a failure that fabricates no answer. - a payload-free failure for those two commands is no longer rewritten as "the panel collected the response, but the connection dropped" when nothing was collected. Replies that actually carry the person's own input are redacted exactly as before. ### Changed - a correction to this project's own notes on #952, recorded because the plan rested on it: the orchestrator mints its session epoch once per PROCESS, so a tab reconnecting to the same process keeps its ledger scope. The earlier claim that a reconnect lands in a new scope and fails open into a duplicate card was wrong; that only happens across an orchestrator restart. ## [0.13.0] - 2026-08-10 > A panel connected to a ComfyUI the orchestrator cannot reach — a tunnel, a proxy URL, a > loopback-only host — could not convert its own live canvas. Anything that pairs a captured > graph with node definitions took the graph from the panel and the definitions from > whatever COMFYUI_URL points at, which is the same machine locally and a different one > remotely. ### Added - the panel can now serve the node definitions of the ComfyUI it is actually looking at, fetched by the browser that is already talking to it (#1006). That is the panel half of the fix; the orchestrator side dispatches to it. - the reply names which ComfyUI answered, and refuses rather than returning a partial schema — a conversion run on half a schema produces a wrong answer, not a smaller one. - the payload is large (4183 node types on the machine this was built against), so a caller can pass back a fingerprint and be told the type set is unchanged instead of receiving it again. The reply says what that does not cover: a combo list or a widget name can change without changing which types exist. ### Fixed - mark a question card whose connection dropped, instead of leaving two live ones (#952) (#1027) - a created tab must leave a fence its own commands can pass (#1019) (#1025) - report the provenance a catalogue answer can actually establish (#890) (#1023) - Save-As must leave a fence the next command can pass (#978) (#1021) - stop refusing a write for object_info that a reconnect never restored (#982) (#1018) - a tab switch onto a MODIFIED workflow still refuses on a stale tag (#995) (#1016) - stop a faithful workflow_open reporting CONTENT_UNVERIFIED and withholding the fence (#1001) (#1013) - stop claiming a complete refresh that did not rehydrate anything (#981) (#1011) ### Changed - 0.12.2 (#1028) - 0.12.1 (#1026) - 0.12.0 (#1024) - 0.11.99 (#1022) - 0.11.98 (#1020) - 0.11.97 (#1017) - 0.11.96 (#1015) - 0.11.95 (#1012) ## [0.12.2] - 2026-08-10 > When the panel loses its connection while the agent is waiting on a question, the card > stays on screen and stays clickable — but its answer can no longer reach anyone. If the > agent asks again after reconnecting you get two identical questions, one of which does > nothing, and until now the panel left you to work out which. ### Fixed - a question card whose connection has been replaced is now visibly retired: its buttons stop working and it says the connection dropped and to answer the newer card (#952). - the same for a secret request, with wording that never sends you to paste a token into the chat — that card exists so the value reaches the orchestrator through a masked input the agent never sees. - a card is only retired when the connection that ASKED for it has actually been replaced. A reconnect that lands on the same socket, or a re-handshake (which happens routinely), leaves a live card alone — as does the Settings token field, which has no agent behind it and still works after a reconnect. - a created tab must leave a fence its own commands can pass (#1019) (#1025) - report the provenance a catalogue answer can actually establish (#890) (#1023) - Save-As must leave a fence the next command can pass (#978) (#1021) - stop refusing a write for object_info that a reconnect never restored (#982) (#1018) - a tab switch onto a MODIFIED workflow still refuses on a stale tag (#995) (#1016) - stop a faithful workflow_open reporting CONTENT_UNVERIFIED and withholding the fence (#1001) (#1013) - stop claiming a complete refresh that did not rehydrate anything (#981) (#1011) ### Changed - 0.12.1 (#1026) - 0.12.0 (#1024) - 0.11.99 (#1022) - 0.11.98 (#1020) - 0.11.97 (#1017) - 0.11.96 (#1015) - 0.11.95 (#1012) ## [0.12.1] - 2026-08-10 > When a command is refused because it targets a different workflow than the canvas, the > panel offers two recoveries: re-target, or re-open the workflow you meant. For a tab that > has never been saved — the one a fresh `panel_new_workflow` just made — the second is not > available, because opening resolves a workflow by its path and that tab has none. A > reporter had to work that out for themselves. ### Fixed - the refusal now says so, when the panel can actually see it: the active tab is unsaved, so re-opening cannot re-select THAT tab, and re-targeting is the route if the canvas you want is the active one (#1019). - the note is kept narrow on purpose. Opening a different, saved workflow still works — the refusal means the command targeted something other than the active canvas, and that something may well be a saved workflow the existing remedy reaches perfectly well. - a saved tab reads exactly as before, and a tab the panel could not read says nothing at all: an unproven fact has no place in a refusal. - report the provenance a catalogue answer can actually establish (#890) (#1023) - Save-As must leave a fence the next command can pass (#978) (#1021) - stop refusing a write for object_info that a reconnect never restored (#982) (#1018) - a tab switch onto a MODIFIED workflow still refuses on a stale tag (#995) (#1016) - stop a faithful workflow_open reporting CONTENT_UNVERIFIED and withholding the fence (#1001) (#1013) - stop claiming a complete refresh that did not rehydrate anything (#981) (#1011) ### Changed - 0.12.0 (#1024) - 0.11.99 (#1022) - 0.11.98 (#1020) - 0.11.97 (#1017) - 0.11.96 (#1015) - 0.11.95 (#1012) ## [0.12.0] - 2026-08-10 > Searching for a custom node pack and getting "no matches" reads as "that pack does not > exist". It can equally mean the list that was searched predates the pack: a machine whose > network blocks the registry does not get an empty catalogue from ComfyUI-Manager, it gets > a full one that may be months old, and nothing in the answer distinguishes the two. ### Fixed - a node search that matches nothing now says how many packs it searched, that the request asked Manager for its cached copy, and — explicitly — that Manager does not report whether it honoured that, when the data was fetched, or whether it came from the network, the on-disk cache or the copy bundled with Manager (#890). So the result stops implying a pack does not exist when what it actually shows is that it was not in the list searched. - the panel deliberately makes no guess about staleness. Measured on a working install, the served catalogue is not the bundled map (5583 packs against 4884, sharing about 1800 entries), so the obvious "is this the bundled copy" test would never fire — it would ship as a check that always passes and quietly reassures. - a search that finds something is unchanged, and an empty catalogue keeps its existing stronger answer: nothing was searched at all. - Save-As must leave a fence the next command can pass (#978) (#1021) - stop refusing a write for object_info that a reconnect never restored (#982) (#1018) - a tab switch onto a MODIFIED workflow still refuses on a stale tag (#995) (#1016) - stop a faithful workflow_open reporting CONTENT_UNVERIFIED and withholding the fence (#1001) (#1013) - stop claiming a complete refresh that did not rehydrate anything (#981) (#1011) ### Changed - 0.11.99 (#1022) - 0.11.98 (#1020) - 0.11.97 (#1017) - 0.11.96 (#1015) - 0.11.95 (#1012) ## [0.11.99] - 2026-08-10 > After a Save-As, graph tools could keep refusing even for an agent that did exactly what > the reply told it to do. The reply said to re-target to the new workflow, and that clears > one guard — but ComfyUI activates the saved copy without repainting the canvas, so the > canvas is still showing the workflow you saved FROM. The second guard then refuses, > correctly, and nothing said why. ### Fixed - a Save-As reply now says that no canvas repaint was requested, and that re-targeting may therefore not be enough for graph tools: if a graph command is then refused for a root-workflow-uuid mismatch, that is the reason, and opening the saved workflow is what brings it onto the canvas (#978). - the warning is stated conditionally rather than asserted, because a tab switch or a reconnect can repaint during the save — the panel knows the save did not ask for a repaint, not what the canvas holds by the time the reply is read. - a FIRST save of an unsaved workflow no longer gets any of that: it keeps the same identity, nothing is about to be refused, and telling that caller to re-target and re-open would have sent them fixing a problem they do not have. - stop refusing a write for object_info that a reconnect never restored (#982) (#1018) - a tab switch onto a MODIFIED workflow still refuses on a stale tag (#995) (#1016) - stop a faithful workflow_open reporting CONTENT_UNVERIFIED and withholding the fence (#1001) (#1013) - stop claiming a complete refresh that did not rehydrate anything (#981) (#1011) ### Changed - 0.11.98 (#1020) - 0.11.97 (#1017) - 0.11.96 (#1015) - 0.11.95 (#1012) ## [0.11.98] - 2026-08-10 > A widget write could be refused for "object_info is unavailable — the backend is > unreachable or the fetch failed" on a machine where ComfyUI was answering perfectly > well. Two problems in one sentence: the panel had only one way to ask for the schema, > and when that way failed it reported a cause it had never established. The reporter went > checking a backend that was fine. ### Fixed - a widget write no longer gives up when the frontend's own `getNodeDefs()` call fails — it asks the same question again over plain HTTP, which is the route that answers when the client does not (#982). Verified live: with the client made to throw, the fallback returned the full 4183-type schema on this install. - the refusal now says what actually happened rather than naming a cause. It reports that no usable schema was obtained and lists what each route did, so a backend that answers by hand and a panel that cannot use it are told apart immediately. - an EMPTY schema from the frontend client is treated as its answer, not as a failure to answer, so the fallback can never overrule a deliberate deny-all with a broader one. Everything else about the fence is unchanged: only a usable schema authorizes a write. - the diagnostic text is bounded — control characters collapsed, per-entry length capped, at most four routes named with the remainder counted — because it comes from a backend or an extension and ends up in a message someone reads. - a tab switch onto a MODIFIED workflow still refuses on a stale tag (#995) (#1016) - stop a faithful workflow_open reporting CONTENT_UNVERIFIED and withholding the fence (#1001) (#1013) - stop claiming a complete refresh that did not rehydrate anything (#981) (#1011) ### Changed - 0.11.97 (#1017) - 0.11.96 (#1015) - 0.11.95 (#1012) ## [0.11.97] - 2026-08-10 > A workflow with unsaved edits could have every graph tool refused after a tab switch, > including read-only ones, while the panel's own tab list confirmed the intended > workflow active. ComfyUI reuses one canvas object across tabs and leaves the previous > workflow's identity tag on it; the escape that handles that was written for a CLEAN tab > only, and the report was filed on a modified one. Reproduced live through UI clicks: > the canvas provably matched the active workflow and the panel refused anyway. ### Fixed - a READ on a canvas whose content matches the active workflow's own state is no longer refused because the canvas carries the previous tab's identity tag, even when the workflow has unsaved edits (#995). Nothing is written: the tag is left exactly as it was, and the refusal is lifted for that one call. - mutations are deliberately NOT included. Content equality against an edited tab's snapshot cannot say WHOSE canvas is mounted — two tabs can hold the same graph — and a write on that evidence could land on the wrong one. The bypass is opt-in, set in a single place for the commands classified read-only, so nothing else can acquire it by omission. - an open workflow holding the same content as the canvas now blocks the bypass rather than being skipped as unprovable, since with edits allowed on both sides a twin would otherwise be invisible. Verified on the live install: a real twin blocked it, and with no twin present the read went through. - stop a faithful workflow_open reporting CONTENT_UNVERIFIED and withholding the fence (#1001) (#1013) - stop claiming a complete refresh that did not rehydrate anything (#981) (#1011) ### Changed - 0.11.96 (#1015) - 0.11.95 (#1012) ## [0.11.96] - 2026-08-10 > A report said the post-load compare in `workflow_open` was racing the frontend's > normalisation. It is not a race. Sampling that compare at 0ms, one animation frame, > 50ms, 250ms, 1s and 2s after the load resolved gave an identical answer every time — it > is deterministic, and it fires for any workflow whose stored node sizes are not what > this frontend computes. A perfect open was reported as unverified, and because that > verdict throws, the open never published the workflow identity, so the NEXT command was > refused too. The reporter needed four calls to reach a state the panel already believed > it was in. ### Fixed - an open that reproduced a workflow faithfully no longer reports its content unverified, and no longer withholds the workflow identity the following command needs (#1001). Content is proven when every node came back with the same id, type and serialized fields apart from node HEIGHT — the one thing the frontend was measured recomputing on load — with the width unchanged and both values readable numbers. Anything else, including a changed width, still refuses. - the same compare reported a phantom difference on every node of every saved workflow. The frontend stamps `showAdvanced` on each node it instantiates with the value `undefined`, which no saved file can carry because JSON drops it. A key present with `undefined` now reads as absent; a key present as `null` still counts, since JSON does carry null and a nulled widget value is a real loss. - when an open proves content that way, the reply says so — which field differed, that every difference was a height, and that the panel observed the difference rather than its cause. Saving from there writes the recomputed value, which is now stated rather than left to be discovered. ### Changed - 0.11.95 (#1012) ## [0.11.95] - 2026-08-10 > `panel_refresh_nodes` said `{ok:true, refreshed:true}` while `panel_get_errors` kept > listing the same classes as missing, after the packs were installed and ComfyUI was > restarted. Both answers were right, which is why neither helped: the definitions really > were re-fetched, and the nodes really were still broken. Measured on the running > install — registering a class does not rehydrate a node that was placed while the class > was unknown. It keeps no definition, and the frontend's missing-node record is a > load-time snapshot nothing ever clears. Clearing that snapshot would have been the > obvious fix and the wrong one: `get_errors` would report clean while the canvas still > held a dead node. The refresh now reports what it actually left behind. ### Fixed - a refresh that leaves placeholders behind now says so, instead of reporting a clean result the canvas contradicts (#981). The reply carries `requires_reload`, the affected nodes, and a note that names the remedy — save the workflow, then reopen that saved workflow — and is explicit that this is an attempt rather than a guarantee, since a class present in the registry can still fail to construct. Verified live, on a canvas where exactly that happened. - the first version of this check reported four false positives — `Note`, `Reroute`, `PrimitiveNode` and `MarkdownNote` all lack a backend definition, so a canvas with a single Note on it would have demanded a workflow reload after every refresh. The scan is now confined to types the frontend itself recorded as missing when the workflow loaded, and asks the client registry rather than `/object_info`: the server having a definition is not the same as this page being able to build the node. ## [0.11.94] - 2026-08-10 > Covers changes since 0.11.93. ### Added - **"Run to node" with a batch now warns that your seed will not change (#988).** Someone ran `panel_run` with `to_node_id` and `batch_count: 3` on a workflow whose KSampler was set to randomize. Three prompts were accepted; the first took 22 seconds and the rest finished in about a fifth of a second, returning the same file and the same image. Every item used the same seed. Measured by comparing the actual requests: an unscoped batch of three sends three different seeds, and a scoped batch of three sends the same seed three times. ComfyUI does not advance `control_after_generate` between the items of a *partial* execution — which is what running to a node is — so the later prompts are duplicates it answers from cache. Checked in both of ComfyUI's widget-control modes, and it happens in both. The panel now says so when you queue that combination: which controls will repeat, why, and the two things that do work — `batch_count: 1` several times setting the value yourself, or dropping `to_node_id` so the run is unscoped. **It does not silently change your seeds.** Doing that would mean re-implementing ComfyUI's own widget behaviour — randomize, increment and decrement all differ, each with its own range — on the one path where the panel already has to patch the request. The warning also says what it cannot know: it lists every such control in the workflow, because it cannot tell which ones a scoped run actually reaches. ## [0.11.93] - 2026-08-10 > Covers changes since 0.11.92. ### Added - **A repeated render result now says so (#986).** Someone had the same finished clip announced to the agent six times in about thirty seconds — each with a different prompt id, each with a "render time" of a tenth of a second against a first render that had taken almost eleven minutes. Every one asked the agent to respond, and nothing told it these were the same file it had already reviewed. They were separate prompts, because the runs were re-queued from the canvas and ComfyUI answered each from its cache. The panel's existing check compares prompt ids, so it had nothing to match on. It now also compares what the run actually produced: a completion whose output files were already delivered says which earlier run delivered them, and whether it finished too fast to have rendered anything. **Nothing is held back.** An earlier version of this suppressed the repeats outright, and that turned out not to be safe — a node that writes to a fixed filename can produce a genuinely different result in under a second, and there is no way to tell that apart from a cached replay. Withholding a render you waited for is worse than an extra message, so every completion still arrives; it just arrives labelled. The label is careful about what it knows: the panel compares file *references*, not file contents, and says so, and it does not claim a run did real work when all it can see is that the run was not suspiciously fast. ## [0.11.92] - 2026-08-10 > Covers changes since 0.11.91. ### Fixed - **Unpacking a subgraph no longer throws away the values you set on it (#979).** Someone unpacked a subgraph and got the pack's template prompt back instead of the long custom one they had written, and a duration of 2 instead of the 15 they had set. Model and VAE choices survived; the values they had actually typed did not. When a widget is promoted to the outside of a subgraph, the value you see on the parent is the one that renders. Unpacking inlined the **inner** node's value instead — usually whatever the pack shipped as a default. Since unpacking is destructive, the parent's value was gone at that point, and the only fix was remembering what it had been and typing it back. The values are now carried inward before the subgraph is taken apart, and the result says which ones moved. The rest of this change is about what happens when that carry goes wrong. Custom nodes can do arbitrary things when a widget is written — clamp the value, throw halfway, change something else on the node. If any of that happens, the panel now **restores the workflow and refuses to unpack**, rather than pulling the subgraph apart around a value it cannot account for. Refusing leaves everything intact and recoverable; guessing would not. ## [0.11.91] - 2026-08-10 > Covers changes since 0.11.90. ### Changed - **"Run to node" falling back to a request repair now tells you a version that works (#996).** When the panel runs a single branch, it asks ComfyUI to scope the run. On some frontend builds that request never carries the scope, so the panel writes it into the request itself and says so — a fallback that works, but one the message asked you to report while giving you nothing to compare against. It now carries one measured fact: on **ComfyUI frontend 1.48.7** the scope does reach the request, so the fallback is not expected there and trying that build may be the quickest workaround. That was established by capturing the outgoing request on 1.48.7 — which shows the request is built correctly, not that a whole run behaves differently, and the message says so rather than implying more. It still does **not** claim which builds are affected. That is one build measured; turning it into a version range would repeat a mistake this file has made before, when a range covering builds nobody had tested told three reporters their own evidence could not be happening. ## [0.11.90] - 2026-08-10 > Covers changes since 0.11.89. ### Added - **`panel_run` now warns when your workflow has outputs inside a muted subgraph that will render anyway (#985).** Someone ran a workflow with one active source subgraph and two muted ones. All three rendered — Wan, LTXV and MiniMax H3 loaded in turn, three videos saved, 18 minutes 44 seconds — and the run reported plain success. The cause is not in this panel, and the fix cannot be either. ComfyUI applies a subgraph's mute/bypass **only at the top level of a workflow**. Mute a subgraph that sits inside another subgraph and it is ignored: everything in it still runs. A whole-workflow run hands prompt building to ComfyUI, so pressing ComfyUI's own Queue button does exactly the same thing. Measured on ComfyUI 0.31.1 with frontend 1.48.7, for mute and bypass alike. What the panel can stop doing is being quiet about it. A whole-workflow run now names the output nodes that sit inside a nested muted or bypassed subgraph, in the one-line summary as well as the full result, so you can interrupt instead of finding out when the renders land. Running to a specific output node (`to_node_id`) scopes execution correctly and is the way to render just the branch you want. Two things it deliberately does **not** do. It does not change what executes — quietly dropping nodes from a run you asked for would be its own kind of wrong. And it does not claim to know your ComfyUI version: the warning is read from your workflow's structure, so on a build that handles nested subgraphs properly it will warn about a run that was fine. The message says so, and tells you how to check. Muting a **top-level** subgraph — the ordinary way to switch a branch off — is silent, because that one genuinely works. ## [0.11.89] - 2026-08-10 > Covers changes since 0.11.88. ### Fixed - **`panel_get_errors` no longer reports a problem and "no errors" in the same answer (#984).** The panel has two ways of finding a widget that names a file the server does not have: the snapshot ComfyUI takes when a workflow loads, and a live check against the server's current node list. The live one was added later, and the "no errors recorded" verdict was never taught about it — so a defect only the live check could see was reported in the payload while the summary said `Checked errors — none`. The case that shows it: a `CheckpointLoader` whose `config_name` names a `models/configs` file that is not there. No missing-**model** tracking covers that folder, so the load-time snapshot has nothing, and the live check is the only one that knows. Both halves now decide the verdict together. Two related corrections came out of reviewing it: - **The same missing file is no longer counted twice.** Both halves usually find it, and the summary was adding the lists — reporting six problems where there were three. - **A widget that is driven by a connection is no longer judged on its own value.** When you convert a widget to an input and connect something, ComfyUI keeps the widget around and runs the connection instead; its leftover value is often stale. Checking it reported an error on a workflow that runs perfectly well. Worth saying plainly for anyone who filed something like this: if `panel_get_errors` reports clean while your canvas shows red loaders, this fix may not be your problem. The originally reported shape did not reproduce — with loaders naming absent files, including subfolder-qualified ones with backslashes, every layer detected them correctly. A tool refusal in the same report pointed at a stale panel bundle in the browser instead, for which the remedy is a hard refresh. ## [0.11.88] - 2026-08-10 > Covers changes since 0.11.87. ### Fixed - **A widget write that a node's own callback breaks no longer reads as the panel failing (#976).** `panel_set_widget` would apply your value, verify by read-back that it was in effect, refuse to roll it back — and then say "an exception was thrown while applying the write". Which reads as *the panel failed to apply your write*. It is the opposite of what happened, and it is why this was reported as a panel defect. The reporter's node was `MiniMaxH3Director`, but the same message appears on a stock `CLIPTextEncode` whose callback is made to throw, so nothing about it was specific to that pack. The wording named nothing on purpose. A widget write touches value setters on the widget, the promoted rail and the display proxies; it reads properties that can be throwing accessors; and assigning to a frozen widget throws with no node code involved at all. Blaming "the callback" for any of those would be a guess. So the panel now measures it instead of guessing. The callback is looked up and its arguments are evaluated before the attributed step begins, and only an exception coming out of the attempt to invoke it is attributed — which is why the disclosure says "attempt to invoke" and never that the callback ran: a value that is not a function, a class, and a revoked proxy all throw without any code of theirs executing. When the callback is not a function at all, it says that outright. It does not go further than that. It does not say who supplied the callback, and it does not assign fault: the panel invokes callbacks programmatically, and that alone is enough to make a callback written for a mouse click throw. What it does say, first, is that your value is in effect. Two things found on the way, both of which could hide a real problem: - a callback doing `throw undefined` (or `null`, `0`, `""`) produced **no warning at all** — the write reported clean while the callback's side effects had not run - a thrown value that fights back (a proxy whose `message` and prototype both throw) could break the report that exists to disclose it, losing the failure entirely ### Changed - Documentation corrections to two binding claims and a run-scope build range (#970, #752), and e2e cleanup that no longer leaves saved workflows behind or passes against the wrong orchestrator (#907, #847). ## [0.11.87] - 2026-08-09 ### Fixed - **A failed install by git URL now points at the tool that can actually do it (#920).** When you install a pack that is not in the node registry, ComfyUI-Manager cannot clone your URL — it resolves installs from its own database, and the parameter that would carry a URL is accepted and then ignored. The failure now tells you to use `install_custom_node` instead: that one runs on the machine rather than in the browser, so it clones the repository into `custom_nodes/` itself. Worth saying explicitly because `panel_install_node`'s own description tells you to prefer it over `install_custom_node` — which is backwards for this case, and following it leaves you stuck. The message now says the preference does not apply here, and notes where the fallback cannot help either: a remote ComfyUI has no local tree to clone into. ## [0.11.86] - 2026-08-09 > Covers changes since 0.11.85. ### Changed - **Editing many widgets in a row is no longer slow (#716).** Every `panel_set_widget` re-downloaded ComfyUI's entire node schema before writing — on an install with 63 custom node packs that is 5.4 MB and roughly 170 ms, every single time. A prompt-editing task that touched 29 widgets paid for it 29 times. A burst of writes now shares one download instead of taking one each. Measured on the test rig: 12 widget writes went from 12 downloads to 1. The safety check those downloads exist for is unchanged. A write is still authorised against the live backend's node list, and that list is re-read the moment anything the panel can see could have changed it — a node refresh, an install, a completed download, or a reconnect. The one case it cannot see is a pack uninstalled through ComfyUI's own Manager while an agent is mid-edit, which leaves a window of up to 1.5 seconds; that is written down in the code rather than left for someone to discover. ### Fixed - **A changelog entry no longer repeats a release that already shipped.** Version tags here lag the releases themselves, and the generator preferred the newest tag over the newest release commit — so with `v0.11.84` tagged and 0.11.85 untagged, this entry initially re-listed a fix 0.11.85 had already announced. It now takes whichever of the two is actually newer, decided by ancestry rather than by date. ## [0.11.85] - 2026-08-09 > Covers changes since 0.11.84. ### Fixed - **A refresh that lands while ComfyUI is reconnecting no longer reports a dead server (#954).** Calling `panel_refresh_nodes` just after a restart-related operation could come back `object_info_fetch_failed` — "Failed to fetch" — while other reads succeeded moments later against the same server. Worse than the false failure was its advice: it told you to check that the ComfyUI process was still running, sending you after something that was never down. The fetch now retries three times over about half a second before giving up, which is enough to cross a reconnect blip. The panel already did this at startup, so the same hiccup was survivable when the page loaded and fatal to an agent tool call a minute later. A backend that really is gone still reports exactly what it reported before, with the same detail — a retry that turned a real outage into a vaguer message would trade one wrong answer for another. ## [0.11.84] - 2026-08-09 _No user-facing changes._ ## [0.11.83] - 2026-08-09 > Covers changes since 0.11.82. ### Added - **The panel now tells you what changed when it updates under you (#758).** It installs from the Comfy Registry and the orchestrator runs the latest agent, so the version can move without you asking — and the first sign was usually something behaving differently than you remembered, which reads as a bug rather than a release. On the first load after an update the transcript shows what shipped between the version this browser last saw and this one, each line tagged Added, Fixed or Changed. That distinction is the point: "this used to work differently on purpose" is a different message from "this was broken". It appears once, says nothing on a fresh install, and says nothing if you roll back. Settings → Comfy MCP Agent → About → **Show what is new** re-opens it whenever you want, since a line in a transcript scrolls away and the request was for somewhere to look. ### Fixed - **A failed install by git URL now says what is actually wrong (#920).** Passing a repository URL for a pack that is not in the node registry failed with *"Node '@' not found in [...]"* — a registry lookup naming an id you never supplied, which reads like a lookup bug and sends you to re-check spelling and channels. It now explains that this is a registry lookup, that ComfyUI-Manager's `repository` parameter is accepted and then ignored by its install handler, and what does work: clone into `custom_nodes/` and restart, ask the author to publish, or — two steps, not one — run with `--enable-manager-legacy-ui` (which *replaces* the v2 Manager API) **and** set `allow_git_url_install = true` in `config.ini`, without which that route answers 404. Also **corrects the 0.11.75 entry**, which claimed installing from a GitHub URL "actually clones it now". It does not, and that entry now says so. ## [0.11.82] - 2026-08-09 > Covers changes since 0.11.81. ### Added - **`panel_open_workflow` now reports which workflow it observed to be active, beside the one that was requested (refs #887).** The reply named only the target, so a caller could not tell "the requested workflow is active" from "the requested workflow is what you asked for" — and a Save-As taken on that reading writes the live canvas, which may be a different workflow. The reply carries `active_routing_key` and `active_matches_target` (true, false, or null when it could not be read), with a warning when they disagree. This is groundwork, not the fix: the contradictory "you are NOT on the wrong workflow" message is composed by the orchestrator, which does not read these fields yet, so nothing user-visible changes until it does. #887 stays open. ## [0.11.81] - 2026-08-09 > Covers changes since 0.11.80. ### Fixed - **A Save-As no longer strands the agent that performed it (#941).** `panel_save_workflow` with a new name writes the copy and switches the active canvas to it — which fences the very session that asked for the save, so every following `panel_*` graph call is refused. That is survivable only if the reply says what to re-fence to, and it did not: it reported the identity as unavailable, while the next call was refused *using* an identity the panel had declined to publish one call earlier. The identity read is deliberately pure — a fence refreshed from a value a read invented would be agreeing with itself rather than observing anything — and a Save-As activates a brand-new object nothing had established an identity for. So the read honestly found nothing while the fence, whose own read mints, immediately found one. The identity is now established as part of the save, from the record the save itself produced and proved active, never from a later look at whichever canvas happens to be current. A Save-As that cannot produce one still reports absence rather than substituting a different canvas. - Name the canvas swap point, from the canvas-rebuild side (#944). ## [0.11.80] - 2026-08-09 > Covers changes since 0.11.79. ### Fixed - **Your first chat about a workflow no longer disappears when you filter to that workflow (#847).** Chat, start a second chat, then tick "Current workflow only": the first conversation vanished, even though both were held on the same canvas. The panel saves an unsaved workflow before a turn ("grounding", #330), and ComfyUI replaces the workflow object at that save. Every carrier that could hand the identity to the successor is empty at that instant — the object maps are keyed on the object that just went away, the embedded uuid is unreadable because the fields it is looked for in are all absent on this frontend, and the path alias gets written from the new id. So the successor minted a fresh identity and the conversation from seconds earlier was left holding one that nothing answered to. The tab's pre-save route id is now captured *inside* the grounding transaction and filed under the path that save produced. That location matters: an observer watching the change afterwards cannot prove the old id was this tab's past rather than a workflow you switched away from, which is why the panel deliberately migrates nothing there. The save knows by causation — it is the panel's own call, on its own active workflow — so nothing is inferred. Scope is small on purpose: these forms are consulted by the history filter alone. They never resolve a workflow's identity, authorize a graph write, or restore a session. One limit stays open and is documented rather than hidden — deleting a workflow and creating a new one with the same name can show the old chats under the new one, since a path is not proof of ownership. ## [0.11.79] - 2026-08-09 > Covers changes since 0.11.78. ### Fixed - **The e2e suite stops leaving saved workflows in your library, and says so when it cannot be sure (#907).** Specs that persist a workflow were leaving the file behind: 1272 of 1288 files in the dev workflow directory were test output. Per-spec cleanup could not fix it, because it sat at the end of a test body and so never ran when a test failed. Cleanup is now suite-level, and deletes only files that appeared during the run AND carry the suite's own name prefix — ComfyUI names an unnamed save `Untitled