comfyui-mcp 0.52.75 → 0.52.77

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.
@@ -31,6 +31,7 @@ import { describeReceived } from "./node-id.js";
31
31
  * …" about one the caller is looking at. Coerced here instead. */
32
32
  export const GROUP_ID_PATTERN = /^-?\d+$/;
33
33
  export const GROUP_ID_MESSAGE = "a group id must be an integer (e.g. 2) — panel_query_graph's groups[] and panel_create_group both return it";
34
+ export const GROUP_REF_MESSAGE = "a group reference must be an integer id (e.g. 2) or a non-empty group title (e.g. 'Notes')";
34
35
  /**
35
36
  * Normalize an accepted group id to the NUMBER the wire has always carried.
36
37
  *
@@ -1 +1 @@
1
- {"version":3,"file":"panel-arg-aliases.js","sourceRoot":"","sources":["../../src/orchestrator/panel-arg-aliases.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"panel-arg-aliases.js","sourceRoot":"","sources":["../../src/orchestrator/panel-arg-aliases.ts"],"names":[],"mappings":"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"}
@@ -48,7 +48,7 @@ import { peekResolvedPanelBase, primePanelBase, verifiedPanelDiskVersion, } from
48
48
  import { conversationOfScopeAddress, isScopeAddress, shortTabId } from "../services/session-scope.js";
49
49
  import { NODE_ID_MESSAGE, NODE_ID_PATTERN, normalizeNodeId, PLAIN_NODE_ID_PATTERN, unionErrorFor, } from "./node-id.js";
50
50
  import { unrecognizedKeysError } from "./unrecognized-keys.js";
51
- import { FIND_NODES_DEFAULT_FIELDS, FIND_NODES_FIELDS, GROUP_ID_MESSAGE, GROUP_ID_PATTERN, normalizeGroupId, normalizeShowMediaItem, normalizeTodoItemKeys, pickAlias, projectFindNodesPayload, } from "./panel-arg-aliases.js";
51
+ import { FIND_NODES_DEFAULT_FIELDS, FIND_NODES_FIELDS, GROUP_ID_MESSAGE, GROUP_ID_PATTERN, GROUP_REF_MESSAGE, normalizeGroupId, normalizeShowMediaItem, normalizeTodoItemKeys, pickAlias, projectFindNodesPayload, } from "./panel-arg-aliases.js";
52
52
  import { addressedNodeMatchesPersistRemedy, parseAmbiguousPromotedWidgetRefusal, parseContradictoryPromotedWidgetRefusal, parseSubgraphScopeRefusal, parseUnpromotedControlPersistRemedy, resolveInnerPromotedTarget, } from "./promoted-widget.js";
53
53
  import { includeRequestedCreateGroupMembers } from "./create-group-membership.js";
54
54
  import { sizeForUncollapse } from "./edit-node-uncollapse.js";
@@ -4297,6 +4297,8 @@ const ANIMA_REGIONAL_WIRED_PROMPT_INPUT = {
4297
4297
  scene_prompt: "scene_prompt_in",
4298
4298
  negative_prompt: "negative_prompt_in",
4299
4299
  };
4300
+ const DASIWA_STACK_WIDGET = "stack_data";
4301
+ const DASIWA_LTX2_LORA_LOADER = "DaSiWa_LTX2LoraLoader";
4300
4302
  function isAnimaRegionalPromptWidget(widget) {
4301
4303
  return ANIMA_REGIONAL_PROMPT_WIDGETS.has(widget);
4302
4304
  }
@@ -4338,6 +4340,101 @@ function parseQueriedNodeType(payload) {
4338
4340
  }
4339
4341
  return null;
4340
4342
  }
4343
+ function canonicalQueriedNodeId(value) {
4344
+ if (typeof value === "number") {
4345
+ return Number.isSafeInteger(value) ? String(value) : null;
4346
+ }
4347
+ if (typeof value !== "string" || !NODE_ID_PATTERN.test(value))
4348
+ return null;
4349
+ const normalized = normalizeNodeId(value);
4350
+ return typeof normalized === "number" && !Number.isSafeInteger(normalized)
4351
+ ? null
4352
+ : String(normalized);
4353
+ }
4354
+ function parseQueriedNodeIdentityRow(row) {
4355
+ if (!row || typeof row !== "object" || Array.isArray(row))
4356
+ return null;
4357
+ const record = row;
4358
+ const id = canonicalQueriedNodeId(record.id);
4359
+ if (!id)
4360
+ return null;
4361
+ const type = record.type;
4362
+ const classType = record.class_type;
4363
+ if (type !== undefined && typeof type !== "string")
4364
+ return null;
4365
+ if (classType !== undefined && typeof classType !== "string")
4366
+ return null;
4367
+ if (typeof type === "string" && typeof classType === "string" && type !== classType) {
4368
+ return null;
4369
+ }
4370
+ const resolvedType = typeof type === "string" ? type : classType;
4371
+ return resolvedType ? { id, type: resolvedType } : null;
4372
+ }
4373
+ /** Strict identity parser for the DaSiWa refusal gate. Unlike the older type-only
4374
+ * parser above, this accepts exactly one non-truncated row and authenticates its id. */
4375
+ function parseVerifiedQueriedNodeIdentity(payload) {
4376
+ if (!payload)
4377
+ return null;
4378
+ if (Object.prototype.hasOwnProperty.call(payload, "truncated") &&
4379
+ payload.truncated !== false) {
4380
+ return null;
4381
+ }
4382
+ if (Object.prototype.hasOwnProperty.call(payload, "nodes")) {
4383
+ return Array.isArray(payload.nodes) && payload.nodes.length === 1
4384
+ ? parseQueriedNodeIdentityRow(payload.nodes[0])
4385
+ : null;
4386
+ }
4387
+ if (typeof payload.text !== "string")
4388
+ return null;
4389
+ const rows = [];
4390
+ const lines = payload.text.split(/\r?\n/);
4391
+ for (const [index, line] of lines.entries()) {
4392
+ const trimmed = line.trim();
4393
+ if (!trimmed)
4394
+ continue;
4395
+ // The live compact projection always prefixes its rows with this bounded summary.
4396
+ // Keep the grammar narrow so arbitrary prose cannot make an unverified row usable,
4397
+ // while accepting the documented header that older tests and panels may omit.
4398
+ if (index === 0 &&
4399
+ /^\d+ match\(es\) of \d+ in scope \(viewing: \d+ nodes\)(?: · traversal depth≤-?\d+)?$/.test(trimmed)) {
4400
+ continue;
4401
+ }
4402
+ // A compact row may be followed by one of the panel's bounded clipping notes or
4403
+ // truncation remedies. They do not identify nodes and are ignored only after a row
4404
+ // has been authenticated below.
4405
+ if (rows.length > 0 &&
4406
+ (/^\(\d+ widget value\(s\) clipped to \d+ chars/.test(trimmed) ||
4407
+ /^… truncated at \d+ of \d+/.test(trimmed))) {
4408
+ continue;
4409
+ }
4410
+ if (trimmed.startsWith("#")) {
4411
+ const compact = trimmed.match(/^#(\S+)\s+(\S+)(?:\s|$)/);
4412
+ if (!compact)
4413
+ return null;
4414
+ const id = canonicalQueriedNodeId(compact[1]);
4415
+ if (!id)
4416
+ return null;
4417
+ rows.push({ id, type: compact[2] });
4418
+ continue;
4419
+ }
4420
+ if (trimmed.startsWith("{")) {
4421
+ let row;
4422
+ try {
4423
+ row = JSON.parse(trimmed);
4424
+ }
4425
+ catch {
4426
+ return null;
4427
+ }
4428
+ const parsed = parseQueriedNodeIdentityRow(row);
4429
+ if (!parsed)
4430
+ return null;
4431
+ rows.push(parsed);
4432
+ continue;
4433
+ }
4434
+ return null;
4435
+ }
4436
+ return rows.length === 1 ? rows[0] : null;
4437
+ }
4341
4438
  function animaRegionalPromptRefusal(type, widget) {
4342
4439
  const wired = ANIMA_REGIONAL_WIRED_PROMPT_INPUT[widget];
4343
4440
  const route = wired
@@ -4367,6 +4464,162 @@ async function refuseAnimaRegionalPromptWrite(ctx, nodeId, widget) {
4367
4464
  return null;
4368
4465
  return animaRegionalPromptRefusal(type, widget);
4369
4466
  }
4467
+ function daSiWaStackRefusal(type) {
4468
+ return fail(`panel_set_widget cannot set "${DASIWA_STACK_WIDGET}" on ${type}. ` +
4469
+ `The node's custom multi-row widget owns the LoRA stack in its internal JS state and ` +
4470
+ `re-serializes that state over widget.value, so the normal graph_set_widget success ` +
4471
+ `echo would be a false success. Edit the stack rows in the node UI instead; do not retry ` +
4472
+ `panel_set_widget for this widget.`);
4473
+ }
4474
+ function daSiWaIdentityRefusal(reason) {
4475
+ return fail(`panel_set_widget refused "${DASIWA_STACK_WIDGET}" because the panel probe could not ` +
4476
+ `prove the requested node is the intended ${DASIWA_LTX2_LORA_LOADER} (${reason}). ` +
4477
+ `No graph_set_widget was dispatched. Edit the stack rows in the node UI instead; ` +
4478
+ `do not retry this write until the node identity can be read exactly.`);
4479
+ }
4480
+ function isUsablePanelConnectionIdentity(identity) {
4481
+ return (!!identity &&
4482
+ Number.isSafeInteger(identity.generation) &&
4483
+ typeof identity.tabSessionId === "string" &&
4484
+ identity.tabSessionId.length > 0);
4485
+ }
4486
+ function samePanelConnectionIdentity(left, right) {
4487
+ return left.generation === right.generation && left.tabSessionId === right.tabSessionId;
4488
+ }
4489
+ /** Refuse the DaSiWa stack widget whose custom UI deterministically overwrites a
4490
+ * graph_set_widget write after acknowledging it. This gate fails closed unless
4491
+ * the probe proves the requested node and remains bound to the same panel tab. */
4492
+ async function refuseDaSiWaStackWrite(ctx, nodeId, widget) {
4493
+ if (widget !== DASIWA_STACK_WIDGET)
4494
+ return null;
4495
+ try {
4496
+ if (ctx.tabExpectedNodeTypeFenceCapability?.() !== true) {
4497
+ return daSiWaIdentityRefusal("the bound panel does not advertise the atomic expected-node-type write fence; update the panel and hard-refresh");
4498
+ }
4499
+ }
4500
+ catch {
4501
+ return daSiWaIdentityRefusal("the panel expected-node-type write fence could not be verified");
4502
+ }
4503
+ const tabBefore = ctx.tabId;
4504
+ let identityBefore;
4505
+ try {
4506
+ identityBefore = ctx.panelConnectionIdentity?.();
4507
+ }
4508
+ catch {
4509
+ return daSiWaIdentityRefusal("the bound browser-tab identity was unreadable");
4510
+ }
4511
+ if (!isUsablePanelConnectionIdentity(identityBefore)) {
4512
+ return daSiWaIdentityRefusal("the bound browser-tab identity was unavailable");
4513
+ }
4514
+ let probe;
4515
+ try {
4516
+ probe = await ctx.call({
4517
+ cmd: "graph_query",
4518
+ ids: [nodeId],
4519
+ fields: "compact",
4520
+ limit: 1,
4521
+ });
4522
+ }
4523
+ catch {
4524
+ return daSiWaIdentityRefusal("the graph_query probe failed before identity could be verified");
4525
+ }
4526
+ if (ctx.tabId !== tabBefore) {
4527
+ return daSiWaIdentityRefusal("the probe was answered by a different panel tab");
4528
+ }
4529
+ let identityAfter;
4530
+ try {
4531
+ identityAfter = ctx.panelConnectionIdentity?.();
4532
+ }
4533
+ catch {
4534
+ return daSiWaIdentityRefusal("the panel-tab identity became unreadable after the probe");
4535
+ }
4536
+ if (!isUsablePanelConnectionIdentity(identityAfter)) {
4537
+ return daSiWaIdentityRefusal("the panel-tab identity became unavailable after the probe");
4538
+ }
4539
+ if (!samePanelConnectionIdentity(identityBefore, identityAfter)) {
4540
+ return daSiWaIdentityRefusal("the panel-tab connection changed during the probe");
4541
+ }
4542
+ if (probe.isError) {
4543
+ return daSiWaIdentityRefusal("the graph_query probe returned an error");
4544
+ }
4545
+ const identity = parseVerifiedQueriedNodeIdentity(parseToolResultJson(probe));
4546
+ const requestedId = canonicalQueriedNodeId(nodeId);
4547
+ if (!identity || !requestedId) {
4548
+ return daSiWaIdentityRefusal("the graph_query reply was malformed, truncated, or did not contain exactly one verifiable row");
4549
+ }
4550
+ if (identity.id !== requestedId) {
4551
+ return daSiWaIdentityRefusal("the graph_query row identified a different node_id");
4552
+ }
4553
+ if (identity.type !== DASIWA_LTX2_LORA_LOADER)
4554
+ return null;
4555
+ return daSiWaStackRefusal(identity.type);
4556
+ }
4557
+ /** Recheck the target immediately before a stack_data mutation. The first probe
4558
+ * prevents the known refusal from dispatching; this final fence catches a tab
4559
+ * reconnect or node replacement observed between that probe and graph_set_widget. */
4560
+ async function verifyDaSiWaStackWriteFence(ctx, nodeId) {
4561
+ try {
4562
+ if (ctx.tabExpectedNodeTypeFenceCapability?.() !== true) {
4563
+ return daSiWaIdentityRefusal("the bound panel does not advertise the atomic expected-node-type write fence; update the panel and hard-refresh");
4564
+ }
4565
+ }
4566
+ catch {
4567
+ return daSiWaIdentityRefusal("the panel expected-node-type write fence could not be verified");
4568
+ }
4569
+ const tabBefore = ctx.tabId;
4570
+ let identityBefore;
4571
+ try {
4572
+ identityBefore = ctx.panelConnectionIdentity?.();
4573
+ }
4574
+ catch {
4575
+ return daSiWaIdentityRefusal("the bound browser-tab identity was unreadable before dispatch");
4576
+ }
4577
+ if (!isUsablePanelConnectionIdentity(identityBefore)) {
4578
+ return daSiWaIdentityRefusal("the bound browser-tab identity was unavailable before dispatch");
4579
+ }
4580
+ let probe;
4581
+ try {
4582
+ probe = await ctx.call({
4583
+ cmd: "graph_query",
4584
+ ids: [nodeId],
4585
+ fields: "compact",
4586
+ limit: 1,
4587
+ });
4588
+ }
4589
+ catch {
4590
+ return daSiWaIdentityRefusal("the final graph_query fence failed before dispatch");
4591
+ }
4592
+ if (ctx.tabId !== tabBefore) {
4593
+ return daSiWaIdentityRefusal("the final fence was answered by a different panel tab");
4594
+ }
4595
+ let identityAfter;
4596
+ try {
4597
+ identityAfter = ctx.panelConnectionIdentity?.();
4598
+ }
4599
+ catch {
4600
+ return daSiWaIdentityRefusal("the panel-tab identity became unreadable before dispatch");
4601
+ }
4602
+ if (!isUsablePanelConnectionIdentity(identityAfter)) {
4603
+ return daSiWaIdentityRefusal("the panel-tab identity became unavailable before dispatch");
4604
+ }
4605
+ if (!samePanelConnectionIdentity(identityBefore, identityAfter)) {
4606
+ return daSiWaIdentityRefusal("the panel-tab connection changed before dispatch");
4607
+ }
4608
+ if (probe.isError) {
4609
+ return daSiWaIdentityRefusal("the final graph_query fence returned an error");
4610
+ }
4611
+ const identity = parseVerifiedQueriedNodeIdentity(parseToolResultJson(probe));
4612
+ const requestedId = canonicalQueriedNodeId(nodeId);
4613
+ if (!identity || !requestedId) {
4614
+ return daSiWaIdentityRefusal("the final graph_query fence was malformed, truncated, or did not contain exactly one verifiable row");
4615
+ }
4616
+ if (identity.id !== requestedId) {
4617
+ return daSiWaIdentityRefusal("the final graph_query fence identified a different node_id");
4618
+ }
4619
+ return identity.type === DASIWA_LTX2_LORA_LOADER
4620
+ ? daSiWaStackRefusal(identity.type)
4621
+ : { expectedNodeType: identity.type };
4622
+ }
4370
4623
  // ---- #809: turn the panel's silent `truncated: true` booleans into a remedy --------
4371
4624
  //
4372
4625
  // A bare boolean is the WORST truncation signal there is: it is a field, not prose, so
@@ -10216,6 +10469,7 @@ export function makePanelToolCtx(bridge, tabId, workflowTargets, onRunTicketOpen
10216
10469
  ctx.panelConnectionIdentity = panelConnectionIdentity;
10217
10470
  ctx.awaitPostRestartReachable = awaitPostRestartReachable;
10218
10471
  ctx.tabCanMutateGraph = () => bridge.tabCanMutateGraph(ctx.tabId);
10472
+ ctx.tabExpectedNodeTypeFenceCapability = () => bridge.tabExpectedNodeTypeFenceCapability(ctx.tabId);
10219
10473
  ctx.tabGraphMutationCapability = () => bridge.tabGraphMutationCapability(ctx.tabId);
10220
10474
  return ctx;
10221
10475
  }
@@ -11320,9 +11574,85 @@ const groupId = () => z
11320
11574
  * undefined` at `group_id` puts both halves of the answer in front of the caller
11321
11575
  * and makes them join it.
11322
11576
  */
11323
- function resolveGroupIdArg(tool, args) {
11577
+ function resolveGroupRefArg(tool, args) {
11324
11578
  return pickAlias(tool, { name: "group_id", value: args.group_id }, { name: "group", value: args.group });
11325
11579
  }
11580
+ /**
11581
+ * Resolve the title form of `group` before reaching the panel's ID-only group
11582
+ * mutators. The panel already exposes the complete group rider on graph_query,
11583
+ * and its subgraph-group resolver uses the same case-insensitive substring
11584
+ * semantics. Reuse that observation, but refuse when it cannot identify exactly
11585
+ * one current group rather than guessing which box to edit or remove.
11586
+ */
11587
+ async function resolveGroupMutationTarget(ref, ctx) {
11588
+ if (typeof ref !== "string")
11589
+ return { ok: true, value: ref };
11590
+ const query = await ctx.call({ cmd: "graph_query", fields: "ids", limit: 1 }, 8000);
11591
+ if (query.isError)
11592
+ return { ok: false, result: query };
11593
+ const payload = parseToolResultJson(query);
11594
+ if (payload?.groups_truncated === true) {
11595
+ return {
11596
+ ok: false,
11597
+ result: fail(`Cannot safely resolve group title "${ref}": the live groups index was truncated, so another matching title may be hidden. ` +
11598
+ "Re-read with panel_graph_outline or use group_id with the exact id."),
11599
+ };
11600
+ }
11601
+ const groups = payload?.groups;
11602
+ if (!Array.isArray(groups)) {
11603
+ return {
11604
+ ok: false,
11605
+ result: fail(`Cannot resolve group title "${ref}": the live graph did not return its groups index. ` +
11606
+ "Re-read with panel_query_graph and retry with group_id."),
11607
+ };
11608
+ }
11609
+ const needle = ref.toLowerCase();
11610
+ const matches = groups.filter((group) => {
11611
+ if (!group || typeof group !== "object" || Array.isArray(group))
11612
+ return false;
11613
+ const title = group.title;
11614
+ return typeof title === "string" && title.toLowerCase().includes(needle);
11615
+ });
11616
+ if (matches.length === 0) {
11617
+ return {
11618
+ ok: false,
11619
+ result: fail(`No group title matching "${ref}" was found in the current graph. ` +
11620
+ "Use panel_query_graph to inspect groups[].title, or pass group_id."),
11621
+ };
11622
+ }
11623
+ if (matches.length > 1) {
11624
+ const candidates = matches
11625
+ .map((group) => {
11626
+ const id = group.id;
11627
+ const title = typeof group.title === "string" ? group.title : "?";
11628
+ return `${String(title)} (id ${String(id)})`;
11629
+ })
11630
+ .join(", ");
11631
+ return {
11632
+ ok: false,
11633
+ result: fail(`Group title "${ref}" is ambiguous: it matches ${candidates}. ` +
11634
+ "Use group_id with the exact id instead of guessing."),
11635
+ };
11636
+ }
11637
+ const match = matches[0];
11638
+ if (match.title_clipped === true) {
11639
+ return {
11640
+ ok: false,
11641
+ result: fail(`Cannot safely resolve group title "${ref}": the matching title was clipped in the live groups index. ` +
11642
+ "Re-read the graph and use group_id with the exact id."),
11643
+ };
11644
+ }
11645
+ const id = match.id;
11646
+ if (!((typeof id === "number" && Number.isSafeInteger(id)) ||
11647
+ (typeof id === "string" && id.length > 0))) {
11648
+ return {
11649
+ ok: false,
11650
+ result: fail(`Cannot safely resolve group title "${ref}": its live group entry has no usable id. ` +
11651
+ "Use panel_query_graph to inspect the current groups index."),
11652
+ };
11653
+ }
11654
+ return { ok: true, value: id };
11655
+ }
11326
11656
  /**
11327
11657
  * #1968 — apply `panel_find_nodes`' `fields` projection to the panel's reply.
11328
11658
  *
@@ -11389,17 +11719,18 @@ const todoChecklist = () => z.array(z.object({
11389
11719
  .describe("Step state (default 'pending'). Mark the one you're on 'active'."),
11390
11720
  }));
11391
11721
  /** The shared schema pair every group tool takes, so the three cannot drift into
11392
- * disagreeing about which spellings they accept (#1968). Both are optional in
11393
- * the SHAPE and exactly one is required by `resolveGroupIdArg` — a cross-field
11722
+ * disagreeing about which spellings they accept (#1968/#2108). Both are optional
11723
+ * in the SHAPE and exactly one is required by `resolveGroupRefArg` — a cross-field
11394
11724
  * rule a flat ZodRawShape cannot express, handled the same way
11395
11725
  * `validatePanelEditNodeArgs` handles node_id/node_ids. */
11396
11726
  const GROUP_ID_ARGS = {
11397
11727
  group_id: groupId()
11398
11728
  .optional()
11399
11729
  .describe("Group id from panel_query_graph's groups[] / panel_create_group."),
11400
- group: groupId()
11730
+ group: z
11731
+ .union([groupId(), z.string().trim().min(1, GROUP_REF_MESSAGE)], { error: unionErrorFor(GROUP_REF_MESSAGE) })
11401
11732
  .optional()
11402
- .describe("Alias for group_id the same id under the name panel_subgraph_group takes and panel_create_group returns."),
11733
+ .describe("Group title (case-insensitive substring) or numeric id; this is the title-aware alias for group_id."),
11403
11734
  };
11404
11735
  /**
11405
11736
  * #1497 — the ONE node-id argument #845 never reached: panel_run's `to_node_id`.
@@ -12386,7 +12717,7 @@ export function buildPanelToolDefs() {
12386
12717
  node_id: nodeId().describe("Node id whose input to disconnect."),
12387
12718
  input: slotRef.optional().describe("Input slot name or index (default 0)."),
12388
12719
  }, async (args, ctx) => ctx.call({ cmd: "graph_disconnect", node_id: args.node_id, input: args.input })),
12389
- def("panel_set_widget", "Set a widget value on a node in the user's open graph (steps, cfg, seed, ckpt_name, text prompts, …). Returns the previous and new value (a string longer than 1000 chars is echoed as {chars, sha256, preview} so batched calls stay inside the outer tool-result budget — pass `echo: \"full\"` for the verbatim string). Undoable with Ctrl+Z. To CLEAR a text widget to an empty string, pass `clear: true` (some MCP clients drop an empty-string `value` from the serialized payload, so `value: \"\"` may not arrive — `clear: true` always works). For the LTXDirector timeline node (WhatDreamsCost CSGlide), set `timeline_data` with the FULL timeline JSON (segments + global_prompt) to drive its custom timeline UI — this re-syncs the editor and regenerates its derived `local_prompts`/`segment_lengths`/`guide_strength` widgets; setting those derived widgets directly is refused (they are silently reverted). For AnimaRegionalCanvasInline / Krea2RegionalCanvasInline (LC123), quality/scene/red/green/blue/negative prompt writes are refused: the custom textarea and node.properties.animaPrompts overwrite widget.value on APPLY. Drive quality/scene/negative via a PrimitiveStringMultiline wired into quality_prompt_in / scene_prompt_in / negative_prompt_in; red/green/blue have no socket.", {
12720
+ def("panel_set_widget", "Set a widget value on a node in the user's open graph (steps, cfg, seed, ckpt_name, text prompts, …). Returns the previous and new value (a string longer than 1000 chars is echoed as {chars, sha256, preview} so batched calls stay inside the outer tool-result budget — pass `echo: \"full\"` for the verbatim string). Undoable with Ctrl+Z. To CLEAR a text widget to an empty string, pass `clear: true` (some MCP clients drop an empty-string `value` from the serialized payload, so `value: \"\"` may not arrive — `clear: true` always works). For the LTXDirector timeline node (WhatDreamsCost CSGlide), set `timeline_data` with the FULL timeline JSON (segments + global_prompt) to drive its custom timeline UI — this re-syncs the editor and regenerates its derived `local_prompts`/`segment_lengths`/`guide_strength` widgets; setting those derived widgets directly is refused (they are silently reverted). For AnimaRegionalCanvasInline / Krea2RegionalCanvasInline (LC123), quality/scene/red/green/blue/negative prompt writes are refused: the custom textarea and node.properties.animaPrompts overwrite widget.value on APPLY. Drive quality/scene/negative via a PrimitiveStringMultiline wired into quality_prompt_in / scene_prompt_in / negative_prompt_in; red/green/blue have no socket. DaSiWa_LTX2LoraLoader's `stack_data` write is also refused: its custom multi-row widget reserializes its own JS state over `widget.value`, so the echoed write would be a false success; edit the stack rows in the node UI instead.", {
12390
12721
  node_id: nodeId().describe("Node id from panel_graph_outline / panel_query_graph."),
12391
12722
  widget: z.string().describe("Widget name (e.g. 'steps', 'cfg', 'text')."),
12392
12723
  value: z
@@ -12417,6 +12748,16 @@ export function buildPanelToolDefs() {
12417
12748
  const blocked = await refuseAnimaRegionalPromptWrite(ctx, args.node_id, args.widget);
12418
12749
  if (blocked)
12419
12750
  return blocked;
12751
+ const daSiWaBlocked = await refuseDaSiWaStackWrite(ctx, args.node_id, args.widget);
12752
+ if (daSiWaBlocked)
12753
+ return daSiWaBlocked;
12754
+ let expectedNodeType;
12755
+ if (args.widget === DASIWA_STACK_WIDGET) {
12756
+ const daSiWaFence = await verifyDaSiWaStackWriteFence(ctx, args.node_id);
12757
+ if (daSiWaFence && "content" in daSiWaFence)
12758
+ return daSiWaFence;
12759
+ expectedNodeType = daSiWaFence?.expectedNodeType;
12760
+ }
12420
12761
  // #599: the frontend runs refresh-before-validate here (pulls a fresh
12421
12762
  // /object_info so a just-staged/-downloaded/-installed value is accepted on
12422
12763
  // a single revalidation, #338/#458) — that authoritative fetch can outlast
@@ -12429,7 +12770,19 @@ export function buildPanelToolDefs() {
12429
12770
  // to the raw success reply so a later appendToolResultText disclosure
12430
12771
  // (which makes the text no longer parse as JSON) cannot dodge it.
12431
12772
  const echoFull = args.echo === "full";
12432
- const write = async (nodeId, widget) => stripVerifiedLastObservedSchemaNote(summarizeSetWidgetEcho(await ctx.call({ cmd: "graph_set_widget", node_id: nodeId, widget, value }, OBJECT_INFO_REFRESH_ACK_TIMEOUT_MS), echoFull));
12773
+ const write = async (nodeId, widget, targetExpectedNodeType = expectedNodeType) => stripVerifiedLastObservedSchemaNote(summarizeSetWidgetEcho(await ctx.call({
12774
+ cmd: "graph_set_widget",
12775
+ node_id: nodeId,
12776
+ widget,
12777
+ value,
12778
+ // The caller supplies the type proven for THIS addressed node.
12779
+ // The outer final fence is used for direct/re-mapped writes;
12780
+ // promoted recovery re-probes its inner node after entering
12781
+ // and supplies that node's own type.
12782
+ ...(targetExpectedNodeType
12783
+ ? { expected_node_type: targetExpectedNodeType }
12784
+ : {}),
12785
+ }, OBJECT_INFO_REFRESH_ACK_TIMEOUT_MS), echoFull));
12433
12786
  let first = await write(args.node_id, args.widget);
12434
12787
  if (!first.isError) {
12435
12788
  markPanelSchemaReady(panelSchemaKey(ctx));
@@ -12527,7 +12880,38 @@ export function buildPanelToolDefs() {
12527
12880
  `Resolved it to inner node ${inner.innerNodeId} but panel_enter_subgraph FAILED: ` +
12528
12881
  `${textOfToolResult(entered)})`);
12529
12882
  }
12530
- const written = await write(inner.innerNodeId, inner.widget);
12883
+ let innerExpectedNodeType;
12884
+ if (expectedNodeType !== undefined) {
12885
+ // The inner mapping was discovered before an awaited enter. Re-query
12886
+ // the addressed inner node after entering so the stack_data write gets
12887
+ // its OWN live type fence; carrying the outer wrapper's type would
12888
+ // reject a valid inner node, while omitting a fence would let a same-
12889
+ // type replacement mutate a detached object and report false success.
12890
+ const innerProbe = await ctx.call({ cmd: "graph_query", ids: [inner.innerNodeId], fields: "compact", limit: 1 }, OBJECT_INFO_REFRESH_ACK_TIMEOUT_MS);
12891
+ const innerIdentity = innerProbe.isError
12892
+ ? null
12893
+ : parseVerifiedQueriedNodeIdentity(parseToolResultJson(innerProbe));
12894
+ const expectedInnerId = canonicalQueriedNodeId(inner.innerNodeId);
12895
+ if (!innerIdentity || !expectedInnerId || innerIdentity.id !== expectedInnerId) {
12896
+ const exited = await ctx.call({ cmd: "graph_exit_subgraph" }, 15000);
12897
+ return appendToolResultText(first, `\n\n(The panel listed "${refusal.widget}" as promoted and mapped it to inner node ` +
12898
+ `${inner.innerNodeId}, but the post-enter identity probe did not verify that ` +
12899
+ `exact live node${innerProbe.isError ? `: ${textOfToolResult(innerProbe)}` : "."} ` +
12900
+ `The write was not retried.${exited.isError
12901
+ ? ` panel_exit_subgraph also FAILED: ${textOfToolResult(exited)}`
12902
+ : ""})`);
12903
+ }
12904
+ if (innerIdentity.type === DASIWA_LTX2_LORA_LOADER) {
12905
+ const exited = await ctx.call({ cmd: "graph_exit_subgraph" }, 15000);
12906
+ return appendToolResultText(first, `\n\n(The promoted inner node ${inner.innerNodeId} was identified as ` +
12907
+ `${DASIWA_LTX2_LORA_LOADER}; ${textOfToolResult(daSiWaStackRefusal(innerIdentity.type))} ` +
12908
+ `No inner graph_set_widget was dispatched.${exited.isError
12909
+ ? ` panel_exit_subgraph also FAILED: ${textOfToolResult(exited)}`
12910
+ : ""})`);
12911
+ }
12912
+ innerExpectedNodeType = innerIdentity.type;
12913
+ }
12914
+ const written = await write(inner.innerNodeId, inner.widget, innerExpectedNodeType);
12531
12915
  const exited = await ctx.call({ cmd: "graph_exit_subgraph" }, 15000);
12532
12916
  if (!written.isError) {
12533
12917
  const via = `\n\n(Applied via the inner widget this promotion lists: node ${inner.innerNodeId} ` +
@@ -14904,17 +15288,20 @@ CHECKED FOR YOU: the graph read this message prescribes was just run, and it ` +
14904
15288
  color: args.color,
14905
15289
  font_size: args.font_size,
14906
15290
  }, 15000), args.node_ids, (cmd, timeoutMs) => ctx.call(cmd, timeoutMs))),
14907
- def("panel_move_group", "Move a group box to a new top-left [x, y] on the user's open graph. By default the nodes inside the group move with it (like dragging the group header); pass move_nodes:false to move only the box. Group id comes from panel_query_graph (the `groups` array on every result) or panel_create_group, as `group_id` or `group`. Undoable.", {
15291
+ def("panel_move_group", "Move a group box to a new top-left [x, y] on the user's open graph. By default the nodes inside the group move with it (like dragging the group header); pass move_nodes:false to move only the box. Identify it by numeric id from panel_query_graph / panel_create_group via `group_id`, or by case-insensitive group-title substring via `group`. Undoable.", {
14908
15292
  ...GROUP_ID_ARGS,
14909
15293
  pos: xy().describe("New top-left [x, y] (two numbers)."),
14910
15294
  move_nodes: z.boolean().optional().describe("Move the contained nodes too (default true)."),
14911
15295
  }, async (args, ctx) => {
14912
- const id = resolveGroupIdArg("panel_move_group", args);
14913
- if (!id.ok)
14914
- return fail(id.error);
14915
- return ctx.call({ cmd: "graph_move_group", group_id: id.value, pos: args.pos, move_nodes: args.move_nodes });
15296
+ const ref = resolveGroupRefArg("panel_move_group", args);
15297
+ if (!ref.ok)
15298
+ return fail(ref.error);
15299
+ const target = await resolveGroupMutationTarget(ref.value, ctx);
15300
+ if (!target.ok)
15301
+ return target.result;
15302
+ return ctx.call({ cmd: "graph_move_group", group_id: target.value, pos: args.pos, move_nodes: args.move_nodes });
14916
15303
  }),
14917
- def("panel_edit_group", "Edit a group box: its title, color, font_size, and/or bounds [x, y, width, height]. Only the fields you pass are changed. Identify it with `group_id` or `group`. Undoable.", {
15304
+ def("panel_edit_group", "Edit a group box: its title, color, font_size, and/or bounds [x, y, width, height]. Only the fields you pass are changed. Identify it by numeric id via `group_id`, or by case-insensitive group-title substring via `group`. Undoable.", {
14918
15305
  ...GROUP_ID_ARGS,
14919
15306
  title: z.string().optional().describe("New label."),
14920
15307
  color: z.string().optional().describe("New box/header color, e.g. '#3f789e'."),
@@ -14923,23 +15310,29 @@ CHECKED FOR YOU: the graph read this message prescribes was just run, and it ` +
14923
15310
  .optional()
14924
15311
  .describe("Resize/reposition the box: [x, y, width, height] (four numbers)."),
14925
15312
  }, async (args, ctx) => {
14926
- const id = resolveGroupIdArg("panel_edit_group", args);
14927
- if (!id.ok)
14928
- return fail(id.error);
15313
+ const ref = resolveGroupRefArg("panel_edit_group", args);
15314
+ if (!ref.ok)
15315
+ return fail(ref.error);
15316
+ const target = await resolveGroupMutationTarget(ref.value, ctx);
15317
+ if (!target.ok)
15318
+ return target.result;
14929
15319
  return ctx.call({
14930
15320
  cmd: "graph_edit_group",
14931
- group_id: id.value,
15321
+ group_id: target.value,
14932
15322
  title: args.title,
14933
15323
  color: args.color,
14934
15324
  font_size: args.font_size,
14935
15325
  bounds: args.bounds,
14936
15326
  }, 15000);
14937
15327
  }),
14938
- def("panel_remove_group", "Remove a group box from the user's open graph. The nodes inside the group are NOT deleted — only the box. Pass the id as `group_id` (or `group` the same argument under either name). Undoable.", { ...GROUP_ID_ARGS }, async (args, ctx) => {
14939
- const id = resolveGroupIdArg("panel_remove_group", args);
14940
- if (!id.ok)
14941
- return fail(id.error);
14942
- return ctx.call({ cmd: "graph_remove_group", group_id: id.value }, 15000);
15328
+ def("panel_remove_group", "Remove a group box from the user's open graph. The nodes inside the group are NOT deleted — only the box. Identify it by numeric id via `group_id`, or by case-insensitive group-title substring via `group`. Undoable.", { ...GROUP_ID_ARGS }, async (args, ctx) => {
15329
+ const ref = resolveGroupRefArg("panel_remove_group", args);
15330
+ if (!ref.ok)
15331
+ return fail(ref.error);
15332
+ const target = await resolveGroupMutationTarget(ref.value, ctx);
15333
+ if (!target.ok)
15334
+ return target.result;
15335
+ return ctx.call({ cmd: "graph_remove_group", group_id: target.value }, 15000);
14943
15336
  }),
14944
15337
  def("panel_set_node_title", "Compatibility wrapper for panel_edit_node(title).", { node_id: nodeId(), title: z.string() }, async (args, ctx) => ctx.call({ cmd: "graph_set_title", node_id: args.node_id, title: args.title }, 15000)),
14945
15338
  def("panel_set_node_collapsed", "Compatibility wrapper for panel_edit_node(collapsed).", { node_id: nodeId(), collapsed: z.boolean().optional() }, async (args, ctx) => ctx.call({ cmd: "graph_set_node_collapsed", node_id: args.node_id, collapsed: args.collapsed ?? true })),