comfyui-mcp 0.52.55 → 0.52.57

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.
@@ -58,6 +58,26 @@ import { clearSwitchHold, describeSwitchHold, recordSwitchHold, successProvesSwi
58
58
  import { NO_ORIGIN_REMEDY } from "./fence-refusal.js";
59
59
  import { completeGetErrorsAudit } from "./get-errors-audit.js";
60
60
  import { applyKitchenRecommendation, assessKitchen, assessKitchenGraph, extractLoaders, gatherKitchenStatus, kitchenProactiveHint, } from "../services/kitchen.js";
61
+ export function resolveClientKind(ctx) {
62
+ const b = ctx.bridge;
63
+ const routed = typeof b?.liveTabIdFor === "function" ? b.liveTabIdFor(ctx.tabId) : undefined;
64
+ // A live resolution wins. When nothing resolves: a CONCRETE id still names
65
+ // the mailbox recipient; a SCOPE address names nobody.
66
+ // `isScopeAddress` is typed `id is string`; its false branch would otherwise
67
+ // narrow a `string` tabId to `never` before `.length` is read (tsc TS2339).
68
+ const address = ctx.tabId;
69
+ const destination = typeof routed === "string" && routed.length > 0
70
+ ? routed
71
+ : typeof address === "string" && address.length > 0 && !isScopeAddress(address)
72
+ ? address
73
+ : undefined;
74
+ if (destination == null)
75
+ return { kind: "unroutable" };
76
+ if (typeof b?.isHeadless === "function" && b.isHeadless(destination)) {
77
+ return { kind: "headless", tabId: destination };
78
+ }
79
+ return { kind: "interactive", tabId: destination };
80
+ }
61
81
  /** #884 — journal TICKETS (run completions #468, ask answers #486) must be
62
82
  * keyed by the REAL tab a run/card was routed to: the panel reports back under
63
83
  * that tab id, and a ticket keyed by the shared scope address a tool ctx is
@@ -738,6 +758,10 @@ export const __panelToolsTestHooks = {
738
758
  * it fails even where the handler suite does not reach. The handler CALL SITE
739
759
  * is covered separately (deleting the wiring must also fail a named test). */
740
760
  annotateShowMediaAck,
761
+ /** #2012 — the destination-kind helper. Handler call sites are covered
762
+ * separately (reverting `panel_show_media` to `isHeadless` on the address
763
+ * must also fail a named test). */
764
+ resolveClientKind,
741
765
  /** Direct access to the #742 decline recheck loop so its hard-deadline
742
766
  * guarantee (codex gate r2) can be unit-tested with a custom deadline. */
743
767
  probeDeclineRecovery,
@@ -3050,6 +3074,15 @@ function annotateShowMediaAck(res, dispatched) {
3050
3074
  * "partial_accounting" or "mailboxed". Structured so a caller can
3051
3075
  * branch on it without reading the prose. */
3052
3076
  unaccounted_because: verdict.reason,
3077
+ /** #2013 — only on a mailbox receipt. A concrete tab id is a known
3078
+ * recipient; `null` means the box is scope-keyed (whoever hellos
3079
+ * next). Structured so a caller does not have to parse the prose. */
3080
+ ...(verdict.reason === "mailboxed"
3081
+ ? {
3082
+ queued_for: verdict.queuedFor ?? null,
3083
+ recipient_known: verdict.queuedFor != null,
3084
+ }
3085
+ : {}),
3053
3086
  /** EVERY dispatched item, even under partial accounting: a reply
3054
3087
  * that covers 1 of 2 does not say WHICH, so none can be settled. */
3055
3088
  unaccounted: dispatched.map((d) => ({ filename: d.filename, kind: d.kind })),
@@ -16420,8 +16453,14 @@ CHECKED FOR YOU: the graph read this message prescribes was just run, and it ` +
16420
16453
  // bytes HERE and inline them as a data URL. Best-effort: any failure
16421
16454
  // (fetch error, non-media, too big) falls back to forwarding the ref,
16422
16455
  // which the client renders as a caption card.
16456
+ //
16457
+ // #2012 — judge the DESTINATION, not the address. `ctx.tabId` may be
16458
+ // a scope / prefix / alias; `isHeadless` on the address is a lookup
16459
+ // miss that answers false and a phone gets a /view URL it cannot
16460
+ // fetch. Positive requirement: inline only a proven headless
16461
+ // destination.
16423
16462
  let inlined = false;
16424
- if (ctx.bridge.isHeadless(ctx.tabId)) {
16463
+ if (resolveClientKind(ctx).kind === "headless") {
16425
16464
  try {
16426
16465
  const base = (process.env.COMFYUI_URL ?? "http://127.0.0.1:8188").replace(/\/+$/, "");
16427
16466
  const qs = new URLSearchParams({ filename: src.filename, type: src.type ?? "output" });