comfyui-mcp 0.48.21 → 0.48.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. package/README.md +2 -2
  2. package/dist/comfyui/client.js +46 -6
  3. package/dist/comfyui/client.js.map +1 -1
  4. package/dist/config.js +15 -0
  5. package/dist/config.js.map +1 -1
  6. package/dist/orchestrator/error-text.js +29 -1
  7. package/dist/orchestrator/error-text.js.map +1 -1
  8. package/dist/orchestrator/error-text.test.js +28 -0
  9. package/dist/orchestrator/error-text.test.js.map +1 -1
  10. package/dist/orchestrator/index.js +151 -50
  11. package/dist/orchestrator/index.js.map +1 -1
  12. package/dist/orchestrator/panel-agent.js +21 -4
  13. package/dist/orchestrator/panel-agent.js.map +1 -1
  14. package/dist/orchestrator/panel-tools.js +1098 -168
  15. package/dist/orchestrator/panel-tools.js.map +1 -1
  16. package/dist/orchestrator/ready-banner.js +70 -0
  17. package/dist/orchestrator/ready-banner.js.map +1 -0
  18. package/dist/services/api-nodes.js +5 -7
  19. package/dist/services/api-nodes.js.map +1 -1
  20. package/dist/services/download-cache.js +126 -18
  21. package/dist/services/download-cache.js.map +1 -1
  22. package/dist/services/download-jobs.js +463 -14
  23. package/dist/services/download-jobs.js.map +1 -1
  24. package/dist/services/download-progress.js +247 -2
  25. package/dist/services/download-progress.js.map +1 -1
  26. package/dist/services/env-capabilities.js +80 -9
  27. package/dist/services/env-capabilities.js.map +1 -1
  28. package/dist/services/generate-audio.js +16 -0
  29. package/dist/services/generate-audio.js.map +1 -1
  30. package/dist/services/local-models-fallback.js +7 -3
  31. package/dist/services/local-models-fallback.js.map +1 -1
  32. package/dist/services/manifest.js +180 -65
  33. package/dist/services/manifest.js.map +1 -1
  34. package/dist/services/model-resolver.js +267 -11
  35. package/dist/services/model-resolver.js.map +1 -1
  36. package/dist/services/node-management.js +38 -27
  37. package/dist/services/node-management.js.map +1 -1
  38. package/dist/services/output-dir.js +33 -3
  39. package/dist/services/output-dir.js.map +1 -1
  40. package/dist/services/panel-secrets.js +19 -7
  41. package/dist/services/panel-secrets.js.map +1 -1
  42. package/dist/services/queue-monitor.js +45 -2
  43. package/dist/services/queue-monitor.js.map +1 -1
  44. package/dist/services/queue-monitor.stall.test.js +78 -0
  45. package/dist/services/queue-monitor.stall.test.js.map +1 -0
  46. package/dist/services/secure-bridge.js +84 -0
  47. package/dist/services/secure-bridge.js.map +1 -1
  48. package/dist/services/storage/azure-blob.js +8 -3
  49. package/dist/services/storage/azure-blob.js.map +1 -1
  50. package/dist/services/storage/index.js +3 -3
  51. package/dist/services/storage/index.js.map +1 -1
  52. package/dist/services/storage/s3.js +9 -3
  53. package/dist/services/storage/s3.js.map +1 -1
  54. package/dist/services/ui-bridge.js +294 -24
  55. package/dist/services/ui-bridge.js.map +1 -1
  56. package/dist/services/workflow-converter.js +767 -72
  57. package/dist/services/workflow-converter.js.map +1 -1
  58. package/dist/services/workflow-lock.js +127 -27
  59. package/dist/services/workflow-lock.js.map +1 -1
  60. package/dist/services/workspace-env.js +48 -0
  61. package/dist/services/workspace-env.js.map +1 -1
  62. package/dist/tools/generate-audio.js +44 -0
  63. package/dist/tools/generate-audio.js.map +1 -1
  64. package/dist/tools/model-extras.js +24 -4
  65. package/dist/tools/model-extras.js.map +1 -1
  66. package/dist/tools/model-management.js +124 -14
  67. package/dist/tools/model-management.js.map +1 -1
  68. package/dist/tools/report-issue.js +220 -66
  69. package/dist/tools/report-issue.js.map +1 -1
  70. package/dist/tools/report-issue.test.js +223 -83
  71. package/dist/tools/report-issue.test.js.map +1 -1
  72. package/dist/tools/skills-access.js +7 -2
  73. package/dist/tools/skills-access.js.map +1 -1
  74. package/dist/tools/template-schema.js +5 -5
  75. package/dist/tools/template-schema.js.map +1 -1
  76. package/dist/tools/workflow-compose.js +18 -3
  77. package/dist/tools/workflow-compose.js.map +1 -1
  78. package/package.json +1 -1
  79. package/packs/anima/install-runpod.sh +0 -1
  80. package/packs/anima/install-windows.bat +0 -1
  81. package/packs/anima/manifest.yaml +0 -1
  82. package/packs/anima-img2img/install-runpod.sh +0 -1
  83. package/packs/anima-img2img/install-windows.bat +0 -1
  84. package/packs/anima-img2img/manifest.yaml +0 -1
  85. package/packs/anima-inpaint/install-runpod.sh +0 -1
  86. package/packs/anima-inpaint/install-windows.bat +0 -1
  87. package/packs/anima-inpaint/manifest.yaml +0 -1
  88. package/packs/anima-txt2img/install-runpod.sh +0 -1
  89. package/packs/anima-txt2img/install-windows.bat +0 -1
  90. package/packs/anima-txt2img/manifest.yaml +0 -1
  91. package/packs/artokun-flow/install-runpod.sh +0 -1
  92. package/packs/artokun-flow/install-windows.bat +0 -1
  93. package/packs/artokun-flow/manifest.yaml +0 -1
  94. package/packs/ernie/install-runpod.sh +0 -1
  95. package/packs/ernie/install-windows.bat +0 -1
  96. package/packs/ernie/manifest.yaml +0 -1
  97. package/packs/ernie-combo/install-runpod.sh +0 -1
  98. package/packs/ernie-combo/install-windows.bat +0 -1
  99. package/packs/ernie-combo/manifest.yaml +0 -1
  100. package/packs/ernie-img2img/install-runpod.sh +0 -1
  101. package/packs/ernie-img2img/install-windows.bat +0 -1
  102. package/packs/ernie-img2img/manifest.yaml +0 -1
  103. package/packs/ernie-txt2img/install-runpod.sh +0 -1
  104. package/packs/ernie-txt2img/install-windows.bat +0 -1
  105. package/packs/ernie-txt2img/manifest.yaml +0 -1
  106. package/packs/ideogram/install-runpod.sh +0 -1
  107. package/packs/ideogram/install-windows.bat +0 -1
  108. package/packs/ideogram/manifest.yaml +0 -1
  109. package/packs/ideogram-img2img/install-runpod.sh +0 -1
  110. package/packs/ideogram-img2img/install-windows.bat +0 -1
  111. package/packs/ideogram-img2img/manifest.yaml +0 -1
  112. package/packs/ideogram-txt2img/install-runpod.sh +0 -1
  113. package/packs/ideogram-txt2img/install-windows.bat +0 -1
  114. package/packs/ideogram-txt2img/manifest.yaml +0 -1
  115. package/packs/ltx-2.3/install-runpod.sh +0 -1
  116. package/packs/ltx-2.3/install-windows.bat +0 -1
  117. package/packs/ltx-2.3/manifest.yaml +0 -1
  118. package/packs/ltx-2.3-extender/install-runpod.sh +0 -1
  119. package/packs/ltx-2.3-extender/install-windows.bat +0 -1
  120. package/packs/ltx-2.3-extender/manifest.yaml +0 -1
  121. package/packs/ltx-2.3-extender-no-audio/install-runpod.sh +0 -1
  122. package/packs/ltx-2.3-extender-no-audio/install-windows.bat +0 -1
  123. package/packs/ltx-2.3-extender-no-audio/manifest.yaml +0 -1
  124. package/packs/ltx-2.3-flf/install-runpod.sh +0 -1
  125. package/packs/ltx-2.3-flf/install-windows.bat +0 -1
  126. package/packs/ltx-2.3-flf/manifest.yaml +0 -1
  127. package/packs/ltx-2.3-img2vid/install-runpod.sh +0 -1
  128. package/packs/ltx-2.3-img2vid/install-windows.bat +0 -1
  129. package/packs/ltx-2.3-img2vid/manifest.yaml +3 -4
  130. package/packs/ltx-2.3-txt2vid/install-runpod.sh +0 -1
  131. package/packs/ltx-2.3-txt2vid/install-windows.bat +0 -1
  132. package/packs/ltx-2.3-txt2vid/manifest.yaml +3 -4
  133. package/packs/ltx-2.3-xy-plot/install-runpod.sh +0 -1
  134. package/packs/ltx-2.3-xy-plot/install-windows.bat +0 -1
  135. package/packs/ltx-2.3-xy-plot/manifest.yaml +0 -1
  136. package/packs/ltx23-distill-3stage/install-runpod.sh +0 -1
  137. package/packs/ltx23-distill-3stage/install-windows.bat +0 -1
  138. package/packs/ltx23-distill-3stage/manifest.yaml +0 -1
  139. package/packs/qwen-image/install-runpod.sh +0 -1
  140. package/packs/qwen-image/install-windows.bat +0 -1
  141. package/packs/qwen-image/manifest.yaml +0 -1
  142. package/packs/qwen-image-edit/install-runpod.sh +0 -1
  143. package/packs/qwen-image-edit/install-windows.bat +0 -1
  144. package/packs/qwen-image-edit/manifest.yaml +0 -1
  145. package/packs/qwen-image-edit-edit/install-runpod.sh +0 -1
  146. package/packs/qwen-image-edit-edit/install-windows.bat +0 -1
  147. package/packs/qwen-image-edit-edit/manifest.yaml +0 -1
  148. package/packs/wan-animate/install-runpod.sh +0 -1
  149. package/packs/wan-animate/install-windows.bat +0 -1
  150. package/packs/wan-animate/manifest.yaml +0 -1
  151. package/packs/wan-animate-character/install-runpod.sh +0 -1
  152. package/packs/wan-animate-character/install-windows.bat +0 -1
  153. package/packs/wan-animate-character/manifest.yaml +0 -1
  154. package/packs/wan-animate-ofm/install-runpod.sh +0 -1
  155. package/packs/wan-animate-ofm/install-windows.bat +0 -1
  156. package/packs/wan-animate-ofm/manifest.yaml +0 -1
  157. package/packs/wan-longer-videos/install-runpod.sh +0 -1
  158. package/packs/wan-longer-videos/install-windows.bat +0 -1
  159. package/packs/wan-longer-videos/manifest.yaml +0 -1
  160. package/packs/wan-longer-videos-i2v/install-runpod.sh +0 -1
  161. package/packs/wan-longer-videos-i2v/install-windows.bat +0 -1
  162. package/packs/wan-longer-videos-i2v/manifest.yaml +0 -1
  163. package/packs/wan-longer-videos-i2v-96gb/install-runpod.sh +0 -1
  164. package/packs/wan-longer-videos-i2v-96gb/install-windows.bat +0 -1
  165. package/packs/wan-longer-videos-i2v-96gb/manifest.yaml +0 -1
  166. package/packs/wan-longer-videos-t2v/install-runpod.sh +0 -1
  167. package/packs/wan-longer-videos-t2v/install-windows.bat +0 -1
  168. package/packs/wan-longer-videos-t2v/manifest.yaml +0 -1
  169. package/packs/wan-longer-videos-t2v-96gb/install-runpod.sh +0 -1
  170. package/packs/wan-longer-videos-t2v-96gb/install-windows.bat +0 -1
  171. package/packs/wan-longer-videos-t2v-96gb/manifest.yaml +0 -1
  172. package/packs/wan-longer-videos-v2v/install-runpod.sh +0 -1
  173. package/packs/wan-longer-videos-v2v/install-windows.bat +0 -1
  174. package/packs/wan-longer-videos-v2v/manifest.yaml +0 -1
  175. package/packs/wan-longer-videos-v2v-96gb/install-runpod.sh +0 -1
  176. package/packs/wan-longer-videos-v2v-96gb/install-windows.bat +0 -1
  177. package/packs/wan-longer-videos-v2v-96gb/manifest.yaml +0 -1
  178. package/packs/wan-multitalk/install-runpod.sh +0 -1
  179. package/packs/wan-multitalk/install-windows.bat +0 -1
  180. package/packs/wan-multitalk/manifest.yaml +0 -1
  181. package/packs/wan-pusa-extend/install-runpod.sh +0 -1
  182. package/packs/wan-pusa-extend/install-windows.bat +0 -1
  183. package/packs/wan-pusa-extend/manifest.yaml +0 -1
  184. package/packs/wan-transparent/install-runpod.sh +0 -1
  185. package/packs/wan-transparent/install-windows.bat +0 -1
  186. package/packs/wan-transparent/manifest.yaml +0 -1
  187. package/packs/wan-transparent-img2vid/install-runpod.sh +0 -1
  188. package/packs/wan-transparent-img2vid/install-windows.bat +0 -1
  189. package/packs/wan-transparent-img2vid/manifest.yaml +0 -1
  190. package/packs/wan-transparent-img2vid-96gb/install-runpod.sh +0 -1
  191. package/packs/wan-transparent-img2vid-96gb/install-windows.bat +0 -1
  192. package/packs/wan-transparent-img2vid-96gb/manifest.yaml +0 -1
  193. package/packs/z-image-base/install-runpod.sh +0 -1
  194. package/packs/z-image-base/install-windows.bat +0 -1
  195. package/packs/z-image-base/manifest.yaml +0 -1
  196. package/packs/z-image-base-combo/install-runpod.sh +0 -1
  197. package/packs/z-image-base-combo/install-windows.bat +0 -1
  198. package/packs/z-image-base-combo/manifest.yaml +0 -1
  199. package/packs/z-image-base-controlnet/install-runpod.sh +0 -1
  200. package/packs/z-image-base-controlnet/install-windows.bat +0 -1
  201. package/packs/z-image-base-controlnet/manifest.yaml +0 -1
  202. package/packs/z-image-base-img2img/install-runpod.sh +0 -1
  203. package/packs/z-image-base-img2img/install-windows.bat +0 -1
  204. package/packs/z-image-base-img2img/manifest.yaml +0 -1
  205. package/packs/z-image-base-inpaint/install-runpod.sh +0 -1
  206. package/packs/z-image-base-inpaint/install-windows.bat +0 -1
  207. package/packs/z-image-base-inpaint/manifest.yaml +0 -1
  208. package/packs/z-image-base-txt2img/install-runpod.sh +0 -1
  209. package/packs/z-image-base-txt2img/install-windows.bat +0 -1
  210. package/packs/z-image-base-txt2img/manifest.yaml +0 -1
  211. package/packs/z-image-turbo/install-runpod.sh +0 -1
  212. package/packs/z-image-turbo/install-windows.bat +0 -1
  213. package/packs/z-image-turbo/manifest.yaml +0 -1
  214. package/packs/z-image-turbo-combo/install-runpod.sh +0 -1
  215. package/packs/z-image-turbo-combo/install-windows.bat +0 -1
  216. package/packs/z-image-turbo-combo/manifest.yaml +0 -1
  217. package/packs/z-image-turbo-controlnet/install-runpod.sh +0 -1
  218. package/packs/z-image-turbo-controlnet/install-windows.bat +0 -1
  219. package/packs/z-image-turbo-controlnet/manifest.yaml +0 -1
  220. package/packs/z-image-turbo-detail-daemon/install-runpod.sh +0 -1
  221. package/packs/z-image-turbo-detail-daemon/install-windows.bat +0 -1
  222. package/packs/z-image-turbo-detail-daemon/manifest.yaml +0 -1
  223. package/packs/z-image-turbo-img2img/install-runpod.sh +0 -1
  224. package/packs/z-image-turbo-img2img/install-windows.bat +0 -1
  225. package/packs/z-image-turbo-img2img/manifest.yaml +0 -1
  226. package/packs/z-image-turbo-inpainting/install-runpod.sh +0 -1
  227. package/packs/z-image-turbo-inpainting/install-windows.bat +0 -1
  228. package/packs/z-image-turbo-inpainting/manifest.yaml +0 -1
  229. package/packs/z-image-turbo-txt2img/install-runpod.sh +0 -1
  230. package/packs/z-image-turbo-txt2img/install-windows.bat +0 -1
  231. package/packs/z-image-turbo-txt2img/manifest.yaml +0 -1
  232. package/packs/z-image-xy-plot/install-runpod.sh +0 -1
  233. package/packs/z-image-xy-plot/install-windows.bat +0 -1
  234. package/packs/z-image-xy-plot/manifest.yaml +0 -1
  235. package/scripts/test-packs.sh +8 -3
@@ -16,7 +16,7 @@ import { fileURLToPath } from "node:url";
16
16
  import { randomBytes } from "node:crypto";
17
17
  import readline from "node:readline";
18
18
  import { startUiBridge, isLoopbackBindHost } from "../services/ui-bridge.js";
19
- import { setupSecureBridge } from "../services/secure-bridge.js";
19
+ import { setupSecureBridge, resolveComfyuiPathForTarget } from "../services/secure-bridge.js";
20
20
  import { startQuickTunnel } from "../services/tunnel.js";
21
21
  import { detectInstallMode } from "../services/self-update.js";
22
22
  import { SelfRestarter } from "../services/self-restart.js";
@@ -50,6 +50,7 @@ import { resolvePrompt, registerPrompt, onPromptsChanged } from "../services/pro
50
50
  import { allBackendReadiness } from "./backend-readiness.js";
51
51
  import { handleOAuthBegin, handleOAuthStatus, handleOAuthSignout } from "./oauth-bridge.js";
52
52
  import { buildStartFailureNotice } from "./start-failure-notice.js";
53
+ import { readyBannerText, bannerCorrection } from "./ready-banner.js";
53
54
  import { OAUTH_PROVIDERS } from "../services/oauth-flow.js";
54
55
  import { startPanelMcpHttpServer } from "./panel-mcp-http.js";
55
56
  import { startPanelConsoleHttpServer } from "./panel-console-http.js";
@@ -64,7 +65,7 @@ import { unloadAllOllama, warmOllama, resolveOllamaHost } from "../services/olla
64
65
  import { isLocalLmstudio, startLmstudioServer, unloadAllLmstudio, warmLmstudio, } from "../services/lmstudio-lifecycle.js";
65
66
  import { llamacppProps, llamacppToolsReady } from "../services/llamacpp-probe.js";
66
67
  import { getAgentSettings, setAgentSettings, normalizePreferredModels } from "../services/panel-settings.js";
67
- import { gatherEnvCapabilities, buildPanelSystemAppend, } from "../services/env-capabilities.js";
68
+ import { gatherEnvCapabilities, buildPanelSystemAppend, resolveBackends, } from "../services/env-capabilities.js";
68
69
  import { WorkflowTargetStore } from "../services/workflow-target-store.js";
69
70
  const PANEL_SYSTEM_APPEND = `You are the autonomous assistant embedded directly in a ComfyUI sidebar panel. The person is working in ComfyUI and talks to you through that panel: their messages arrive as your prompts, and everything you write is shown to them in the panel chat. Write for that reader — lead with the result, keep replies short and concrete, and don't narrate routine internal steps.
70
71
 
@@ -80,7 +81,7 @@ If a workflow needs a custom node the user doesn't have, don't silently skip it
80
81
 
81
82
  CRASH RECOVERY — when a custom node BREAKS or CRASHED ComfyUI, fix it before giving up. If your turn begins with a "⚠️ ComfyUI crashed …" note (it names the fatal log block and the most likely culprit custom node + file:line), or a run dies with a node-level error you can pin to one pack, do NOT just re-run the same graph — ESCALATE to actually fix that node, narrating each step to the user as you go: (a) UPDATE it to the latest code — call panel_update_node with the culprit's id (or the comfyui MCP update_custom_node / fix_custom_node). Try version 'nightly' to grab a just-landed upstream fix. Poll panel_node_queue_status, then panel_restart_comfyui → you resume and RETRY the action to see if the crash is gone. (b) If updating doesn't fix it, reach into COMFYUI_PATH/custom_nodes/<NodeDir> with your shell (Bash): if it's a git repo (a .git dir), run git fetch && git pull (or check out the nightly branch) to force the latest, reinstall its requirements if needed, then restart + retry. (c) If there's no git or it's still broken, attempt a TARGETED source patch of the crashing file:line, then VERIFY the fix actually resolves the crash (restart + retry the same action — confirm it no longer faults). Once verified, OFFER to suggest the fix upstream to the repo owner (open an issue or PR describing the crash + your patch) — describe it and ask the user first; do NOT auto-file anything. Combine this cleanly with the normal install→restart→continue flow above: a fresh install that crashes on first use is the same loop (update/patch the just-installed node, don't abandon it).
82
83
 
83
- REPORT OUR OWN BUGS (we're in beta — bias HARD toward filing) — distinct from the third-party case above. Whenever YOU hit a defect in comfyui-mcp or the sidebar panel ITSELF — a tool/panel/orchestrator call that errors, crashes ComfyUI, returns wrong/misleading/empty output, fails silently, or a limitation that forced you into an awkward workaround/retry — invoke the report-bug skill (read_skill "report-bug") to autonomously file a via-panel report, EVEN IF you already recovered or worked around it. You do NOT need to be blocked, and it does NOT need to be fatal — the workaround itself is the signal. The skill scrubs secrets and files through our intake Worker (which dedupes server-side, so over-reporting is cheap); then just drop the user a one-line note. Do NOT ask permission for OUR bugs — file them autonomously (unlike third-party/custom-node bugs above, where you OFFER and ask first). Ordinary generation errors (OOM, missing model, bad params, user mistakes) are NOT bug reports — those stay troubleshooting.
84
+ REPORT OUR OWN BUGS (we're in beta — bias HARD toward filing) — distinct from the third-party case above. Whenever YOU hit a defect in comfyui-mcp or the sidebar panel ITSELF — a tool/panel/orchestrator call that errors, crashes ComfyUI, returns wrong/misleading/empty output, fails silently, or a limitation that forced you into an awkward workaround/retry — invoke the report-bug skill (read_skill "report-bug") to autonomously file a via-panel report, EVEN IF you already recovered or worked around it. You do NOT need to be blocked, and it does NOT need to be fatal — the workaround itself is the signal. AND report the FIX, not just the failure: any time a defect in comfyui-mcp or the panel gets fixed or patched during the session — you edited/worked around the code, changed a setting, or otherwise made a broken thing work — and the user confirms it's working (they're happy with the fix), file the report RIGHT THEN, capturing BOTH the original bug AND the fix that worked, so it can be fixed upstream at the source (a confirmed working fix is the most valuable thing to send upstream). File it immediately and autonomously — do NOT wait, and do NOT ask. The skill scrubs secrets and files through our intake Worker (which dedupes server-side, so over-reporting is cheap); then just drop the user a one-line note. Do NOT ask permission for OUR bugs — file them autonomously (unlike third-party/custom-node bugs above, where you OFFER and ask first). Ordinary generation errors (OOM, missing model, bad params, user mistakes) are NOT bug reports — those stay troubleshooting.
84
85
 
85
86
  WEDGED RENDER / OOM / VRAM PINNED — when a generation is stuck or hits CUDA out-of-memory, or a cancel didn't actually free GPU memory (models still resident, VRAM pinned, the next run still OOMs), call panel_free_vram to UNLOAD all models and free VRAM before retrying — it does NOT restart ComfyUI, so it's the cheap first move. Escalation ladder: cancel the run → panel_free_vram (unload + free) → retry; only as a LAST RESORT panel_restart_comfyui (which refuses mid-render and guards the running generation). Reach for panel_free_vram before a restart whenever a cancel left memory pinned.
86
87
 
@@ -94,7 +95,7 @@ MERGE / COMPOSE WORKFLOWS — to bring nodes from ONE workflow into ANOTHER (com
94
95
 
95
96
  REUSE SUBGRAPHS via the blueprint library — when the user builds a useful subgraph and wants to reuse it (now or in other workflows), SAVE it: panel_create_subgraph to group the nodes (if not already a subgraph), then panel_save_subgraph(node_id, name) publishes it to their library programmatically (no dialog). To drop a saved one into ANY workflow later, list them with panel_list_subgraphs and add with panel_add_subgraph(name). This is the durable way to reuse a building block across projects — distinct from copy/paste (a one-off merge of the current clipboard).
96
97
 
97
- PREFER READY EXPERTISE OVER HAND-BUILDING. When the user asks you to "set up", "build", or "make" a workflow for a specific model FAMILY (krea2, wan, flux, qwen, ltx, z-image, ideogram, anima, ernie, etc.), do NOT immediately hand-build a generic graph from scratch. FIRST, in order: (a) consult the matching SKILL for that family. If you already have a skill for it loaded in your context, use it. If you do NOT have its full guidance in front of you, do NOT guess from memory — actually CALL the comfyui MCP's list_skills to see what's bundled, then read_skill(name) to load the real family expertise (model slots, the node graph, settings, gotchas) before you build; (b) check the installer PACKS by CALLING list_packs (each packs/<name>/ has a ready manifest.yaml AND a ready workflow.json). If a pack matches the family, PREFER it: apply_manifest --path <its manifest_path> installs the right custom nodes + model weights, and the pack's workflow.json is the expert graph — CALL read_pack_workflow(name) to get that ready graph and recreate it on the live canvas via panel_add_node/panel_connect/panel_set_widget so the user watches it build (or enqueue it headlessly when they don't need it on-canvas), instead of inventing your own. Don't claim a skill or pack exists unless a tool result confirmed it; (c) check the official ComfyUI workflow Templates — call list_workflow_templates (it lists the server's bundled comfyui-workflow-templates + custom-node templates) for a matching starter, and point the user at it in the frontend's Templates browser. Only build from scratch if NOTHING matches — and when you do, briefly say what you checked (skill, packs, templates) so the user knows you didn't reinvent the wheel. And never wipe the user's current canvas (no panel_clear) until the replacement is actually ready to drop in. To load a ready pack graph onto the live canvas in one shot (instead of recreating it node-by-node), use panel_load_workflow(pack:<name>) — the pack's UI workflow.json is read server-side and dropped onto the canvas, undoable.
98
+ PREFER READY EXPERTISE OVER HAND-BUILDING. When the user asks you to "set up", "build", or "make" a workflow for a specific model FAMILY (krea2, wan, flux, qwen, ltx, z-image, ideogram, anima, ernie, etc.), do NOT immediately hand-build a generic graph from scratch. FIRST, in order: (a) consult the matching SKILL for that family. If you already have a skill for it loaded in your context, use it. If you do NOT have its full guidance in front of you, do NOT guess from memory — actually CALL the comfyui MCP's list_skills to see what's bundled, then read_skill(name) to load the real family expertise (model slots, the node graph, settings, gotchas) before you build; (b) check the installer PACKS by CALLING list_packs (each packs/<name>/ has a ready manifest.yaml AND a ready workflow.json). If a pack matches the family, PREFER it: apply_manifest --path <its manifest_path> installs the right custom nodes + model weights, and the pack's workflow.json is the expert graph — CALL read_pack_workflow(name) to get that ready graph and recreate it on the live canvas via panel_add_node/panel_connect/panel_set_widget so the user watches it build (or enqueue it headlessly when they don't need it on-canvas), instead of inventing your own. Don't claim a skill or pack exists unless a tool result confirmed it; (c) check the ComfyUI workflow Templates — call list_workflow_templates (it lists CUSTOM-NODE-contributed templates from the server's /api/workflow_templates index; it does NOT enumerate ComfyUI's own core bundled templates, which are served separately) for a matching starter, and ALSO point the user at the frontend's Templates browser directly, since core templates only appear there. Only build from scratch if NOTHING matches — and when you do, briefly say what you checked (skill, packs, templates) so the user knows you didn't reinvent the wheel. And never wipe the user's current canvas (no panel_clear) until the replacement is actually ready to drop in. To load a ready pack graph onto the live canvas in one shot (instead of recreating it node-by-node), use panel_load_workflow(pack:<name>) — the pack's UI workflow.json is read server-side and dropped onto the canvas, undoable.
98
99
 
99
100
  OPENING A STAGED / DOWNLOADED WORKFLOW. When you've saved or downloaded a workflow .json into the user's ComfyUI workflows folder (e.g. an example you fetched), open it with panel_open_workflow(path:<name-or-path>) — it now REFRESHES the frontend's (cached) workflow list before searching, so a just-staged file is found and opened natively in its own tab. For a workflow .json that lives OUTSIDE the workflows folder (any absolute path on the ComfyUI machine, or a downloaded example you didn't move into workflows/), load it directly onto the live canvas with panel_load_workflow(path:<file>) — the orchestrator reads + parses the JSON server-side and drops it on the canvas in one shot, so even a large (100KB+) workflow never has to shuttle through this chat. Prefer panel_load_workflow(path:<file>) over pasting a big workflow JSON inline as the graph arg.
100
101
 
@@ -965,7 +966,26 @@ export async function runPanelOrchestrator() {
965
966
  // local install — the spawn env builders prefer COMFYUI_PATH over the
966
967
  // force-remote flag, so a leaked path would silently defeat --force-remote.
967
968
  const localPathForTarget = (url) => !isForceRemoteFlagSet() && isLoopbackUrl(url) ? localComfyuiPath : undefined;
968
- let comfyuiPath = localPathForTarget(comfyuiUrl);
969
+ // #296: an embedded LOCAL sidebar session with NO CLI-configured workspace
970
+ // (COMFYUI_PATH unset AND auto-detect empty) would otherwise spawn path-less —
971
+ // its comfyui MCP stuck in a degraded "local but path-less" state (no
972
+ // download_model/apply_manifest/model-scan LOCAL surface, panel_load_workflow's
973
+ // local fallback blind). The panel already KNOWS where ComfyUI lives and serves
974
+ // it at GET /comfyui_mcp_panel/status (base_path). resolveComfyuiPathForTarget
975
+ // consumes it as a last-resort fallback for a loopback, non-force-remote target
976
+ // (adopting only a path that actually exists on THIS machine). Awaited here so
977
+ // the recovered path is in place BEFORE the MCP-env builders / manager below
978
+ // capture it. Best-effort — a failed/absent status route leaves us path-less.
979
+ let comfyuiPath = await resolveComfyuiPathForTarget({
980
+ target: comfyuiUrl,
981
+ localPath: localComfyuiPath,
982
+ forceRemote: isForceRemoteFlagSet(),
983
+ isLoopback: isLoopbackUrl(comfyuiUrl),
984
+ });
985
+ if (comfyuiPath && comfyuiPath !== localPathForTarget(comfyuiUrl)) {
986
+ logger.info(`[panel-orchestrator] no COMFYUI_PATH/default workspace; adopted ComfyUI ` +
987
+ `base_path from the panel status route: ${comfyuiPath} (#296).`);
988
+ }
969
989
  // Force the child remote only when opted in (--force-remote) or the target is
970
990
  // non-loopback; a default loopback panel user with no COMFYUI_PATH is left to
971
991
  // auto-detect its local install (keeps download_model/apply_manifest/scans).
@@ -1214,6 +1234,15 @@ export async function runPanelOrchestrator() {
1214
1234
  const defaultBackend = KNOWN_BACKENDS.has(backendId) ? backendId : "claude";
1215
1235
  const AGENT_KEY_SEP = "::";
1216
1236
  const tabBackends = new Map(); // panel tabId -> selected backend
1237
+ // #376: the model label advertised in a tab's ready banner (sent at hello, before
1238
+ // the SDK reports the real model). onSession re-sends a corrected banner when the
1239
+ // resolved model differs from what's stored here. A tab with NO entry never got a
1240
+ // greeting (a resume/reconnect), so it must never be "corrected".
1241
+ const advertisedBannerModel = new Map();
1242
+ // The SDK-resolved model per tab, learned from the session/init event. Kept so a
1243
+ // greeting emitted AFTER the model is already known (the onSession-before-greeting
1244
+ // race) can label itself correctly instead of showing the pre-init default.
1245
+ const resolvedModelByTab = new Map();
1217
1246
  const headlessTabs = new Set(); // tabs with no ComfyUI canvas (mobile/remote) — deliver renders in-turn
1218
1247
  const workflowTargets = new WorkflowTargetStore();
1219
1248
  const backendForTab = (panelTabId) => tabBackends.get(panelTabId) ?? defaultBackend;
@@ -1272,11 +1301,32 @@ export async function runPanelOrchestrator() {
1272
1301
  if (gen !== envRefreshGen)
1273
1302
  return; // superseded — let the newer refresh own the prompt
1274
1303
  // Belt-and-suspenders: gather is internally guarded, but never let a stray
1275
- // throw break the prompt — fall back to the static append.
1304
+ // throw break the prompt — fall back to the static append. Also DROP any
1305
+ // previously-gathered envCaps so systemAppendForBackend() falls back to the
1306
+ // same static append for EVERY backend (not just the default) — otherwise a
1307
+ // non-default backend would rebuild a stale env block off the old caps,
1308
+ // disagreeing with the reset panelSystemAppend (#358 wiring).
1309
+ envCaps = undefined;
1276
1310
  panelSystemAppend = resolvePrompt("panel.persona", PANEL_SYSTEM_APPEND);
1277
1311
  logger.debug(`[panel-orchestrator] env-capabilities probe failed (using static prompt): ${err instanceof Error ? err.message : String(err)}`);
1278
1312
  }
1279
1313
  }
1314
+ // The ENVIRONMENT block's `Backend:` line must name the provider running THIS
1315
+ // tab's turn, not the process default (#358). Backends are per-tab now, so the
1316
+ // single `panelSystemAppend` (built with the default backend id) would tell a
1317
+ // Grok tab it's Claude. Rebuild the block per-tab-backend: same env facts, but
1318
+ // the backend label recomputed from the tab's actual backend id. Falls back to
1319
+ // the shared append when the env probe produced nothing (envCaps undefined).
1320
+ const systemAppendForBackend = (bId) => {
1321
+ if (!envCaps)
1322
+ return panelSystemAppend;
1323
+ const { backend, otherBackendAvailable } = resolveBackends(bId);
1324
+ // Already the default's label → reuse the shared string (no rebuild).
1325
+ if (envCaps.backend === backend && envCaps.otherBackendAvailable === otherBackendAvailable) {
1326
+ return panelSystemAppend;
1327
+ }
1328
+ return buildPanelSystemAppend(resolvePrompt("panel.persona", PANEL_SYSTEM_APPEND), { ...envCaps, backend, otherBackendAvailable });
1329
+ };
1280
1330
  // Build it before any agent could spawn. Guarded so a probe stall can't block
1281
1331
  // orchestrator startup beyond the probes' own (short) timeouts.
1282
1332
  await refreshEnvCapabilities();
@@ -1425,12 +1475,14 @@ export async function runPanelOrchestrator() {
1425
1475
  const makeBackend = (key) => {
1426
1476
  const backend = backendOf(key);
1427
1477
  const panelTabId = panelTabOf(key);
1478
+ // The ENVIRONMENT block's `Backend:` line must name THIS backend (#358).
1479
+ const sysAppend = systemAppendForBackend(backend);
1428
1480
  try {
1429
1481
  if (backend === "codex") {
1430
1482
  return new CodexBackend({
1431
1483
  cwd: comfyuiPath ?? process.cwd(),
1432
1484
  model: codexModel,
1433
- systemAppend: panelSystemAppend,
1485
+ systemAppend: sysAppend,
1434
1486
  comfyuiUrl,
1435
1487
  mcpServers: makeHttpBackendMcpServers(panelTabId),
1436
1488
  });
@@ -1439,7 +1491,7 @@ export async function runPanelOrchestrator() {
1439
1491
  return new GeminiBackend({
1440
1492
  cwd: comfyuiPath ?? process.cwd(),
1441
1493
  model: geminiModel,
1442
- systemAppend: panelSystemAppend,
1494
+ systemAppend: sysAppend,
1443
1495
  comfyuiUrl,
1444
1496
  mcpServers: makeHttpBackendMcpServers(panelTabId),
1445
1497
  });
@@ -1448,7 +1500,7 @@ export async function runPanelOrchestrator() {
1448
1500
  return new AntigravityBackend({
1449
1501
  cwd: comfyuiPath ?? process.cwd(),
1450
1502
  ...(antigravityModel ? { model: antigravityModel } : {}),
1451
- systemAppend: panelSystemAppend,
1503
+ systemAppend: sysAppend,
1452
1504
  mcpServers: makeHttpBackendMcpServers(panelTabId),
1453
1505
  });
1454
1506
  }
@@ -1456,7 +1508,7 @@ export async function runPanelOrchestrator() {
1456
1508
  return new GrokBackend({
1457
1509
  cwd: comfyuiPath ?? process.cwd(),
1458
1510
  model: grokModel,
1459
- systemAppend: panelSystemAppend,
1511
+ systemAppend: sysAppend,
1460
1512
  comfyuiUrl,
1461
1513
  mcpServers: makeHttpBackendMcpServers(panelTabId),
1462
1514
  });
@@ -1465,7 +1517,7 @@ export async function runPanelOrchestrator() {
1465
1517
  return new OllamaBackend({
1466
1518
  cwd: comfyuiPath ?? process.cwd(),
1467
1519
  model: ollamaModel,
1468
- systemAppend: panelSystemAppend,
1520
+ systemAppend: sysAppend,
1469
1521
  comfyuiUrl,
1470
1522
  mcpServers: makeHttpBackendMcpServers(panelTabId),
1471
1523
  ...ollamaDeps(),
@@ -1475,7 +1527,7 @@ export async function runPanelOrchestrator() {
1475
1527
  return new OllamaBackend({
1476
1528
  cwd: comfyuiPath ?? process.cwd(),
1477
1529
  model: openrouterModel,
1478
- systemAppend: panelSystemAppend,
1530
+ systemAppend: sysAppend,
1479
1531
  comfyuiUrl,
1480
1532
  mcpServers: makeHttpBackendMcpServers(panelTabId),
1481
1533
  ...openrouterDeps(),
@@ -1485,7 +1537,7 @@ export async function runPanelOrchestrator() {
1485
1537
  return new OllamaBackend({
1486
1538
  cwd: comfyuiPath ?? process.cwd(),
1487
1539
  model: lmstudioModel,
1488
- systemAppend: panelSystemAppend,
1540
+ systemAppend: sysAppend,
1489
1541
  comfyuiUrl,
1490
1542
  mcpServers: makeHttpBackendMcpServers(panelTabId),
1491
1543
  ...lmstudioDeps(),
@@ -1495,7 +1547,7 @@ export async function runPanelOrchestrator() {
1495
1547
  return new OllamaBackend({
1496
1548
  cwd: comfyuiPath ?? process.cwd(),
1497
1549
  model: llamacppModel,
1498
- systemAppend: panelSystemAppend,
1550
+ systemAppend: sysAppend,
1499
1551
  comfyuiUrl,
1500
1552
  mcpServers: makeHttpBackendMcpServers(panelTabId),
1501
1553
  ...llamacppDeps(),
@@ -1505,7 +1557,7 @@ export async function runPanelOrchestrator() {
1505
1557
  return new OllamaBackend({
1506
1558
  cwd: comfyuiPath ?? process.cwd(),
1507
1559
  model: customModel,
1508
- systemAppend: panelSystemAppend,
1560
+ systemAppend: sysAppend,
1509
1561
  comfyuiUrl,
1510
1562
  mcpServers: makeHttpBackendMcpServers(panelTabId),
1511
1563
  ...customDeps(),
@@ -1515,7 +1567,7 @@ export async function runPanelOrchestrator() {
1515
1567
  return new ChatGptOAuthBackend({
1516
1568
  cwd: comfyuiPath ?? process.cwd(),
1517
1569
  model: chatgptModel,
1518
- systemAppend: panelSystemAppend,
1570
+ systemAppend: sysAppend,
1519
1571
  comfyuiUrl,
1520
1572
  mcpServers: makeHttpBackendMcpServers(panelTabId),
1521
1573
  });
@@ -1524,7 +1576,7 @@ export async function runPanelOrchestrator() {
1524
1576
  if (simpleKeyReg) {
1525
1577
  // glm/moonshot (and any future simple api-key provider) share one factory.
1526
1578
  return makeOpenAiKeyBackend(simpleKeyReg, {
1527
- systemAppend: panelSystemAppend,
1579
+ systemAppend: sysAppend,
1528
1580
  comfyuiUrl,
1529
1581
  mcpServers: makeHttpBackendMcpServers(panelTabId),
1530
1582
  });
@@ -1533,7 +1585,7 @@ export async function runPanelOrchestrator() {
1533
1585
  return new KimiBackend({
1534
1586
  cwd: comfyuiPath ?? process.cwd(),
1535
1587
  model: kimiModel,
1536
- systemAppend: panelSystemAppend,
1588
+ systemAppend: sysAppend,
1537
1589
  comfyuiUrl,
1538
1590
  mcpServers: makeHttpBackendMcpServers(panelTabId),
1539
1591
  });
@@ -1546,7 +1598,7 @@ export async function runPanelOrchestrator() {
1546
1598
  return new CopilotBackend({
1547
1599
  cwd: comfyuiPath ?? process.cwd(),
1548
1600
  model: copilotModel,
1549
- systemAppend: panelSystemAppend,
1601
+ systemAppend: sysAppend,
1550
1602
  comfyuiUrl,
1551
1603
  mcpServers: makeHttpBackendMcpServers(panelTabId),
1552
1604
  });
@@ -1649,6 +1701,10 @@ export async function runPanelOrchestrator() {
1649
1701
  makeBackend,
1650
1702
  comfyuiUrl, // for fetching image bytes to inline into agent turns
1651
1703
  systemAppend: panelSystemAppend,
1704
+ // Per-KEY env block so the CLAUDE path (makeBackend returns undefined) still
1705
+ // gets the tab's actual backend on its `Backend:` line (#358). Non-claude
1706
+ // tabs get it via makeBackend's sysAppend; this covers the built-in default.
1707
+ makeSystemAppend: (key) => systemAppendForBackend(backendOf(key)),
1652
1708
  pluginPath: pluginAvailable ? pluginPath : undefined,
1653
1709
  // In-process live-graph MCP for CLAUDE keys only (codex/gemini drive the
1654
1710
  // canvas through the loopback HTTP MCP instead). Bound to the PANEL tab so
@@ -1675,9 +1731,28 @@ export async function runPanelOrchestrator() {
1675
1731
  // Per-response usage → the panel's context/usage meter (updates live).
1676
1732
  onStatus: (key, status) => pushStatus(panelTabOf(key), status),
1677
1733
  // Report the SDK session id so the panel can persist it and resume on reload.
1678
- onSession: (key, sessionId) => {
1679
- bridge.push({ type: "session", session_id: sessionId }, panelTabOf(key));
1734
+ onSession: (key, sessionId, model) => {
1735
+ const panelTab = panelTabOf(key);
1736
+ bridge.push({ type: "session", session_id: sessionId }, panelTab);
1680
1737
  bridge.broadcastTabList(); // a session started/changed → refresh mirror pickers
1738
+ // #376: the ready banner was sent at hello with the PRE-init default model.
1739
+ // Now the SDK reports the ACTUALLY-resolved model — remember it, then re-send
1740
+ // a corrected banner IFF a banner was actually advertised for this tab AND the
1741
+ // resolved model differs (bannerCorrection returns null otherwise, so a resume
1742
+ // with no prior greeting is never "corrected" and a correct banner never
1743
+ // duplicates).
1744
+ if (typeof model === "string" && model.trim())
1745
+ resolvedModelByTab.set(panelTab, model);
1746
+ const corrected = bannerCorrection({
1747
+ backend: backendForTab(panelTab),
1748
+ advertisedLabel: advertisedBannerModel.get(panelTab),
1749
+ resolvedModel: model,
1750
+ customBaseUrl,
1751
+ });
1752
+ if (corrected) {
1753
+ advertisedBannerModel.set(panelTab, model);
1754
+ bridge.push({ type: "say", text: corrected }, panelTab);
1755
+ }
1681
1756
  },
1682
1757
  // Per-turn rewind anchor (assistant UUID) → the panel stores it so a later
1683
1758
  // "rewind conversation to here" can fork the session at that point.
@@ -1856,6 +1931,38 @@ export async function runPanelOrchestrator() {
1856
1931
  // (hello, runpod tools, watcher auto-stop/vanish), so they can't drift
1857
1932
  // split-brained again (#269).
1858
1933
  logger.info(`[panel-orchestrator] retargeted ComfyUI ${prev} → ${comfyuiUrl} (${isLoopbackUrl(next) ? "local" : "remote"} mode) from panel hello`);
1934
+ // #296: the synchronous `localPathForTarget(next)` above can't see the NEWLY
1935
+ // targeted panel's base_path, so a retarget to a loopback ComfyUI with no
1936
+ // env/auto-detected path lands path-less even though that panel serves it. Kick
1937
+ // off a best-effort recovery from the NEW target's status route (fire-and-forget
1938
+ // so the retarget stays synchronous). On success — and only if a newer retarget
1939
+ // hasn't superseded us mid-fetch — adopt the path and rebuild the comfyui MCP
1940
+ // spawn env so each tab's child respawns in LOCAL mode. Skipped entirely when a
1941
+ // local path is already set (the common case does no I/O).
1942
+ if (!comfyuiPath) {
1943
+ void (async () => {
1944
+ const recovered = await resolveComfyuiPathForTarget({
1945
+ target: next,
1946
+ localPath: localComfyuiPath,
1947
+ forceRemote: isForceRemoteFlagSet(),
1948
+ isLoopback: isLoopbackUrl(next),
1949
+ });
1950
+ if (!recovered)
1951
+ return;
1952
+ // Superseded by a newer retarget while we fetched — do nothing.
1953
+ if (canonTargetUrl(comfyuiUrl) !== canonTargetUrl(next))
1954
+ return;
1955
+ if (comfyuiPath === recovered)
1956
+ return;
1957
+ comfyuiPath = recovered;
1958
+ logger.info(`[panel-orchestrator] recovered ComfyUI base_path from panel status route ` +
1959
+ `after retarget: ${recovered} (#296).`);
1960
+ // Rebuild the comfyui MCP spawn env (now carrying COMFYUI_PATH) and respawn
1961
+ // each tab's agent at its next idle so the LIVE child runs in LOCAL mode.
1962
+ manager.setMcpServers(buildMcpServers());
1963
+ manager.restartAllForMcpEnv();
1964
+ })();
1965
+ }
1859
1966
  // Advertising itself happens unconditionally on every hello (see below) —
1860
1967
  // a retarget doesn't need its own advertise call here.
1861
1968
  return true;
@@ -1868,10 +1975,23 @@ export async function runPanelOrchestrator() {
1868
1975
  // idle so the LIVE comfyui MCP subprocess is recreated WITH the new env, and
1869
1976
  // nudge it to retry the action the secret unblocked. No process restart, no
1870
1977
  // reload fight. The value is never logged — only the env-var KEYS.
1871
- const unsubscribeSecrets = onComfyuiSecretsChanged(() => {
1978
+ const SECRET_RETRY_NUDGE = "🔑 The API token you just provided is now active for the comfyui tools — retry the action that needed it (e.g. the download that returned 401).";
1979
+ const unsubscribeSecrets = onComfyuiSecretsChanged((change) => {
1872
1980
  manager.setMcpServers(buildMcpServers());
1873
- manager.restartAllForMcpEnv("🔑 The API token you just provided is now active for the comfyui tools retry the action that needed it (e.g. the download that returned 401).");
1874
- logger.info(`[panel-orchestrator] tool secret saved comfyui MCP env updated + agents respawn on idle (keys: ${comfyuiSecretKeys().join(", ") || "none"})`);
1981
+ // EVERY tab's comfyui child needs the new/removed env, so respawn each at its
1982
+ // next idle but SILENTLY (no nudge). The env rebuild is shared; the "retry
1983
+ // the action" nudge is not, so it must never broadcast to unrelated tabs.
1984
+ // restartAllForMcpEnv() is nudge-preserving, so this can't erase a per-request
1985
+ // nudge already queued on another tab (#164).
1986
+ manager.restartAllForMcpEnv();
1987
+ // NUDGE only the tab whose panel_request_secret this change answers — a
1988
+ // Settings slot save, a background token (re)load, or a revoke leaves
1989
+ // `requested` false and nudges nothing. The per-tab pending-restart map
1990
+ // coalesces, so a repeat event for the same tab can't double-inject (#164).
1991
+ const nudgedTab = change.requested && change.tabId ? change.tabId : null;
1992
+ if (nudgedTab)
1993
+ manager.restartForMcpEnv(agentKeyFor(nudgedTab), SECRET_RETRY_NUDGE);
1994
+ logger.info(`[panel-orchestrator] tool secret ${change.requested ? "saved (requested)" : "changed"} → comfyui MCP env updated + agents respawn on idle${nudgedTab ? ` + retry nudge → tab ${nudgedTab.slice(0, 8)}` : ""} (keys: ${comfyuiSecretKeys().join(", ") || "none"})`);
1875
1995
  });
1876
1996
  // An agent-provider secret changed (e.g. the OpenRouter API key set from the
1877
1997
  // panel). Hydrate it into env, drop the cached openrouter probe/model list so
@@ -2383,32 +2503,13 @@ export async function runPanelOrchestrator() {
2383
2503
  }
2384
2504
  // Greet only on a FRESH session (a resume/reconnect already has the thread).
2385
2505
  if (!resume) {
2386
- const readyText = reg
2387
- ? reg.readyMessage(agentLabel)
2388
- : isCx
2389
- ? `🟢 comfyui-mcp agent ready — ${agentLabel} on your Codex (ChatGPT) account. Ask away.`
2390
- : isCg
2391
- ? `🟢 comfyui-mcp agent ready — ${agentLabel} on your ChatGPT subscription (direct OAuth). Ask away.`
2392
- : isGm
2393
- ? `🟢 comfyui-mcp agent ready — ${agentLabel} on your Google account (Gemini Code Assist). Ask away.`
2394
- : isAg
2395
- ? `🟢 comfyui-mcp agent ready — ${agentLabel} on your Google AI subscription via Antigravity CLI. Note: agy turns show final answers only (no live tool progress). Ask away.`
2396
- : isGk
2397
- ? `🟢 comfyui-mcp agent ready — ${agentLabel} on your Grok (xAI) account. Ask away.`
2398
- : isOl
2399
- ? `🟢 comfyui-mcp agent ready — ${agentLabel} running locally via Ollama (no account, no API key). Small local models are slower and simpler than frontier ones — expect fewer frills. Ask away.`
2400
- : isLs
2401
- ? `🟢 comfyui-mcp agent ready — ${agentLabel} running locally via LM Studio (no account, no API key). Small local models are slower and simpler than frontier ones — expect fewer frills. Ask away.`
2402
- : isLc
2403
- ? `🟢 comfyui-mcp agent ready — ${agentLabel} running locally via llama.cpp (no account, no API key). Small local models are slower and simpler than frontier ones — expect fewer frills. Ask away.`
2404
- : isCu
2405
- ? `🟢 comfyui-mcp agent ready — ${agentLabel} via your custom endpoint (${customBaseUrl}). Ask away.`
2406
- : isOr
2407
- ? `🟢 comfyui-mcp agent ready — ${agentLabel} via OpenRouter (hosted API, your OPENROUTER_API_KEY). Ask away.`
2408
- : isCp
2409
- ? `🟢 comfyui-mcp agent ready — ${agentLabel} on your GitHub Copilot subscription (⚠️ experimental, ToS risk — you opted in). Ask away.`
2410
- : `🟢 comfyui-mcp agent ready — ${agentLabel} on your Claude subscription. Ask away.`;
2411
- bridge.push({ type: "say", text: readyText }, panelTab);
2506
+ // Prefer an ALREADY-resolved model if the SDK init raced ahead of this
2507
+ // greeting (#376) — otherwise the pre-init label. Remember what we
2508
+ // advertised so the onSession correction re-sends only on a real
2509
+ // mismatch.
2510
+ const bannerLabel = resolvedModelByTab.get(panelTab) ?? agentLabel;
2511
+ advertisedBannerModel.set(panelTab, bannerLabel);
2512
+ bridge.push({ type: "say", text: readyBannerText(backend, bannerLabel, customBaseUrl) }, panelTab);
2412
2513
  }
2413
2514
  bridge.push({ type: "ack", ok: true, kind: "ready", agent: agentLabel, backend }, panelTab);
2414
2515
  logger.debug(`[panel-orchestrator] tab ${panelTab.slice(0, 8)} connected (${backend}) — agent healthy, ready ack`);