snow-flow 10.0.1-dev.450 → 10.0.1-dev.451

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
- "version": "10.0.1-dev.450",
3
+ "version": "10.0.1-dev.451",
4
4
  "name": "snow-flow",
5
5
  "description": "Snow-Flow - ServiceNow Multi-Agent Development Framework powered by AI",
6
6
  "license": "Elastic-2.0",
@@ -72,6 +72,239 @@ async function executeFlowPatchMutation(
72
72
  return resp.data?.data?.global?.snFlowDesigner?.flow || resp.data;
73
73
  }
74
74
 
75
+ // Type label mapping for parameter definitions
76
+ const TYPE_LABELS: Record<string, string> = {
77
+ string: 'String', integer: 'Integer', boolean: 'True/False', choice: 'Choice',
78
+ reference: 'Reference', object: 'Object', glide_date_time: 'Date/Time',
79
+ glide_date: 'Date', decimal: 'Decimal', conditions: 'Conditions',
80
+ glide_list: 'List', html: 'HTML', script: 'Script', url: 'URL',
81
+ };
82
+
83
+ /**
84
+ * Build full action input objects matching the Flow Designer UI format.
85
+ * The UI sends inputs WITH parameter definitions in the INSERT mutation (not empty inputs + separate UPDATE).
86
+ */
87
+ async function buildActionInputsForInsert(
88
+ client: any,
89
+ actionDefId: string,
90
+ userValues?: Record<string, string>
91
+ ): Promise<{ inputs: any[]; resolvedInputs: Record<string, string>; actionParams: any[] }> {
92
+ // Query sys_hub_action_input with full field set
93
+ var actionParams: any[] = [];
94
+ try {
95
+ var resp = await client.get('/api/now/table/sys_hub_action_input', {
96
+ params: {
97
+ sysparm_query: 'model=' + actionDefId,
98
+ sysparm_fields: 'sys_id,element,label,internal_type,mandatory,default_value,order,max_length,hint,read_only,extended,data_structure,reference,reference_display,ref_qual,choice_option,table_name,column_name,use_dependent,dependent_on,show_ref_finder,local,attributes,sys_class_name',
99
+ sysparm_display_value: 'false',
100
+ sysparm_limit: 50
101
+ }
102
+ });
103
+ actionParams = resp.data.result || [];
104
+ } catch (_) {}
105
+
106
+ // Fuzzy-match user-provided values to actual field names
107
+ var resolvedInputs: Record<string, string> = {};
108
+ if (userValues) {
109
+ var paramElements = actionParams.map(function (p: any) { return p.element; });
110
+ for (var [key, value] of Object.entries(userValues)) {
111
+ if (paramElements.includes(key)) {
112
+ resolvedInputs[key] = value;
113
+ continue;
114
+ }
115
+ var match = actionParams.find(function (p: any) {
116
+ return p.element.endsWith('_' + key) || p.element === key || (p.label || '').toLowerCase() === key.toLowerCase();
117
+ });
118
+ if (match) resolvedInputs[match.element] = value;
119
+ else resolvedInputs[key] = value;
120
+ }
121
+ }
122
+
123
+ // Build full input objects with parameter definitions (matching UI format)
124
+ var inputs = actionParams.map(function (rec: any) {
125
+ var paramType = rec.internal_type || 'string';
126
+ var userVal = resolvedInputs[rec.element] || '';
127
+ return {
128
+ id: rec.sys_id,
129
+ name: rec.element,
130
+ children: [],
131
+ displayValue: { value: '' },
132
+ value: { schemaless: false, schemalessValue: '', value: userVal },
133
+ parameter: {
134
+ id: rec.sys_id,
135
+ label: rec.label || rec.element,
136
+ name: rec.element,
137
+ type: paramType,
138
+ type_label: TYPE_LABELS[paramType] || paramType.charAt(0).toUpperCase() + paramType.slice(1),
139
+ hint: rec.hint || '',
140
+ order: parseInt(rec.order || '0', 10),
141
+ extended: rec.extended === 'true',
142
+ mandatory: rec.mandatory === 'true',
143
+ readonly: rec.read_only === 'true',
144
+ maxsize: parseInt(rec.max_length || '8000', 10),
145
+ data_structure: rec.data_structure || '',
146
+ reference: rec.reference || '',
147
+ reference_display: rec.reference_display || '',
148
+ ref_qual: rec.ref_qual || '',
149
+ choiceOption: rec.choice_option || '',
150
+ table: rec.table_name || '',
151
+ columnName: rec.column_name || '',
152
+ defaultValue: rec.default_value || '',
153
+ use_dependent: rec.use_dependent === 'true',
154
+ dependent_on: rec.dependent_on || '',
155
+ show_ref_finder: rec.show_ref_finder === 'true',
156
+ local: rec.local === 'true',
157
+ attributes: rec.attributes || '',
158
+ sys_class_name: rec.sys_class_name || '',
159
+ children: []
160
+ }
161
+ };
162
+ });
163
+
164
+ return { inputs, resolvedInputs, actionParams };
165
+ }
166
+
167
+ /**
168
+ * Find all flow elements at order >= targetOrder and build GraphQL update payloads to bump their order by 1.
169
+ * This is needed when inserting an element inside a flow logic block (e.g. action inside If block).
170
+ * The UI does this to keep the Else block (and other subsequent elements) after the new element.
171
+ */
172
+ async function findElementsToReorder(
173
+ client: any,
174
+ flowId: string,
175
+ targetOrder: number
176
+ ): Promise<{ flowLogicUpdates: any[]; actionUpdates: any[]; subflowUpdates: any[] }> {
177
+ var flowLogicUpdates: any[] = [];
178
+ var actionUpdates: any[] = [];
179
+ var subflowUpdates: any[] = [];
180
+
181
+ // Flow logic blocks
182
+ try {
183
+ var resp = await client.get('/api/now/table/sys_hub_flow_logic', {
184
+ params: {
185
+ sysparm_query: 'flow=' + flowId + '^order>=' + targetOrder,
186
+ sysparm_fields: 'sys_id,order,ui_unique_identifier',
187
+ sysparm_limit: 100
188
+ }
189
+ });
190
+ for (var rec of (resp.data.result || [])) {
191
+ var uuid = rec.ui_unique_identifier;
192
+ var curOrder = parseInt(rec.order || '0', 10);
193
+ if (uuid && curOrder >= targetOrder) {
194
+ flowLogicUpdates.push({ order: String(curOrder + 1), uiUniqueIdentifier: uuid, type: 'flowlogic' });
195
+ }
196
+ }
197
+ } catch (_) {}
198
+
199
+ // Action instances
200
+ try {
201
+ var resp2 = await client.get('/api/now/table/sys_hub_action_instance', {
202
+ params: {
203
+ sysparm_query: 'flow=' + flowId + '^order>=' + targetOrder,
204
+ sysparm_fields: 'sys_id,order,ui_unique_identifier',
205
+ sysparm_limit: 100
206
+ }
207
+ });
208
+ for (var rec2 of (resp2.data.result || [])) {
209
+ var uuid2 = rec2.ui_unique_identifier;
210
+ var curOrder2 = parseInt(rec2.order || '0', 10);
211
+ if (uuid2 && curOrder2 >= targetOrder) {
212
+ actionUpdates.push({ order: String(curOrder2 + 1), uiUniqueIdentifier: uuid2, type: 'action' });
213
+ }
214
+ }
215
+ } catch (_) {}
216
+
217
+ // Subflow instances
218
+ try {
219
+ var resp3 = await client.get('/api/now/table/sys_hub_sub_flow_instance', {
220
+ params: {
221
+ sysparm_query: 'flow=' + flowId + '^order>=' + targetOrder,
222
+ sysparm_fields: 'sys_id,order,ui_unique_identifier',
223
+ sysparm_limit: 100
224
+ }
225
+ });
226
+ for (var rec3 of (resp3.data.result || [])) {
227
+ var uuid3 = rec3.ui_unique_identifier;
228
+ var curOrder3 = parseInt(rec3.order || '0', 10);
229
+ if (uuid3 && curOrder3 >= targetOrder) {
230
+ subflowUpdates.push({ order: String(curOrder3 + 1), uiUniqueIdentifier: uuid3, type: 'subflow' });
231
+ }
232
+ }
233
+ } catch (_) {}
234
+
235
+ return { flowLogicUpdates, actionUpdates, subflowUpdates };
236
+ }
237
+
238
+ /**
239
+ * Calculate the insert order for an element being added inside a parent flow logic block.
240
+ * Returns the order right after the last child of the parent, and reorder info for elements that need bumping.
241
+ */
242
+ async function calculateInsertOrder(
243
+ client: any,
244
+ flowId: string,
245
+ parentUiId?: string,
246
+ explicitOrder?: number
247
+ ): Promise<{ insertOrder: number; reorders: { flowLogicUpdates: any[]; actionUpdates: any[]; subflowUpdates: any[] } }> {
248
+ var noReorders = { flowLogicUpdates: [], actionUpdates: [], subflowUpdates: [] };
249
+
250
+ // Explicit order: bump elements at that position
251
+ if (explicitOrder) {
252
+ var reorders = await findElementsToReorder(client, flowId, explicitOrder);
253
+ return { insertOrder: explicitOrder, reorders };
254
+ }
255
+
256
+ // No parent: append at end, no bumping needed
257
+ if (!parentUiId) {
258
+ var nextOrder = await getNextOrder(client, flowId);
259
+ return { insertOrder: nextOrder, reorders: noReorders };
260
+ }
261
+
262
+ // Parent specified: find parent's order, then find max child order
263
+ var parentSysId = '';
264
+ var parentOrder = 0;
265
+ try {
266
+ var pResp = await client.get('/api/now/table/sys_hub_flow_logic', {
267
+ params: {
268
+ sysparm_query: 'flow=' + flowId + '^ui_unique_identifier=' + parentUiId,
269
+ sysparm_fields: 'sys_id,order',
270
+ sysparm_limit: 1
271
+ }
272
+ });
273
+ var found = pResp.data.result?.[0];
274
+ if (found) {
275
+ parentSysId = found.sys_id;
276
+ parentOrder = parseInt(found.order || '0', 10);
277
+ }
278
+ } catch (_) {}
279
+
280
+ if (!parentSysId) {
281
+ // Fallback: append at end
282
+ var fallbackOrder = await getNextOrder(client, flowId);
283
+ return { insertOrder: fallbackOrder, reorders: noReorders };
284
+ }
285
+
286
+ // Find max order of existing children of this parent
287
+ var maxChildOrder = parentOrder;
288
+ for (var table of ['sys_hub_action_instance', 'sys_hub_flow_logic', 'sys_hub_sub_flow_instance']) {
289
+ try {
290
+ var cResp = await client.get('/api/now/table/' + table, {
291
+ params: {
292
+ sysparm_query: 'flow=' + flowId + '^parent=' + parentSysId + '^ORDERBYDESCorder',
293
+ sysparm_fields: 'order',
294
+ sysparm_limit: 1
295
+ }
296
+ });
297
+ var childOrder = parseInt(cResp.data.result?.[0]?.order || '0', 10);
298
+ if (childOrder > maxChildOrder) maxChildOrder = childOrder;
299
+ } catch (_) {}
300
+ }
301
+
302
+ // Insert after last child; bump everything at that position
303
+ var insertOrder = maxChildOrder + 1;
304
+ var reorderInfo = await findElementsToReorder(client, flowId, insertOrder);
305
+ return { insertOrder: insertOrder, reorders: reorderInfo };
306
+ }
307
+
75
308
  async function addTriggerViaGraphQL(
76
309
  client: any,
77
310
  flowId: string,
@@ -209,7 +442,6 @@ async function addActionViaGraphQL(
209
442
 
210
443
  // Dynamically look up action definition in sys_hub_action_type_snapshot
211
444
  let actionDefId: string | null = null;
212
- // Try exact match on internal_name first, then name
213
445
  for (const field of ['internal_name', 'name']) {
214
446
  if (actionDefId) break;
215
447
  try {
@@ -223,7 +455,6 @@ async function addActionViaGraphQL(
223
455
  }
224
456
  } catch (_) {}
225
457
  }
226
- // Fallback: LIKE search on both fields
227
458
  if (!actionDefId) {
228
459
  try {
229
460
  const resp = await client.get('/api/now/table/sys_hub_action_type_snapshot', {
@@ -242,53 +473,29 @@ async function addActionViaGraphQL(
242
473
  }
243
474
  if (!actionDefId) return { success: false, error: 'Action definition not found for: ' + actionType, steps };
244
475
 
245
- // Look up available input fields from sys_hub_action_input
246
- let actionParams: { element: string; label: string; mandatory: boolean; default_value: string; internal_type: string }[] = [];
247
- try {
248
- const resp = await client.get('/api/now/table/sys_hub_action_input', {
249
- params: {
250
- sysparm_query: 'model=' + actionDefId,
251
- sysparm_fields: 'sys_id,element,label,mandatory,default_value,internal_type',
252
- sysparm_display_value: 'false',
253
- sysparm_limit: 50
254
- }
255
- });
256
- actionParams = (resp.data.result || []).map((r: any) => ({
257
- element: r.element,
258
- label: r.label,
259
- mandatory: r.mandatory === 'true' || r.mandatory === true,
260
- default_value: r.default_value || '',
261
- internal_type: r.internal_type || ''
262
- }));
263
- steps.available_inputs = actionParams;
264
- } catch (_) {}
265
-
266
- // Match provided inputs to actual field names (fuzzy: "message" → "log_message", "level" → "log_level")
267
- const resolvedInputs: Record<string, string> = {};
268
- if (inputs) {
269
- const paramElements = actionParams.map(p => p.element);
270
- for (const [key, value] of Object.entries(inputs)) {
271
- // Exact match first
272
- if (paramElements.includes(key)) {
273
- resolvedInputs[key] = value;
274
- continue;
275
- }
276
- // Try to find a param whose element ends with the key or contains it
277
- const match = actionParams.find(p => p.element.endsWith('_' + key) || p.element === key || p.label.toLowerCase() === key.toLowerCase());
278
- if (match) {
279
- resolvedInputs[match.element] = value;
280
- } else {
281
- resolvedInputs[key] = value;
282
- }
283
- }
284
- steps.resolved_inputs = resolvedInputs;
476
+ // Build full input objects with parameter definitions (matching UI format)
477
+ const inputResult = await buildActionInputsForInsert(client, actionDefId, inputs);
478
+ steps.available_inputs = inputResult.actionParams.map((p: any) => ({ element: p.element, label: p.label }));
479
+ steps.resolved_inputs = inputResult.resolvedInputs;
480
+
481
+ // Calculate insertion order and find elements that need reordering
482
+ const orderCalc = await calculateInsertOrder(client, flowId, parentUiId, order);
483
+ const resolvedOrder = orderCalc.insertOrder;
484
+ steps.insert_order = resolvedOrder;
485
+ if (orderCalc.reorders.flowLogicUpdates.length > 0 || orderCalc.reorders.actionUpdates.length > 0 || orderCalc.reorders.subflowUpdates.length > 0) {
486
+ steps.reordered_elements = {
487
+ flowLogics: orderCalc.reorders.flowLogicUpdates.length,
488
+ actions: orderCalc.reorders.actionUpdates.length,
489
+ subflows: orderCalc.reorders.subflowUpdates.length
490
+ };
285
491
  }
286
492
 
287
493
  const uuid = generateUUID();
288
- const resolvedOrder = order || await getNextOrder(client, flowId);
289
494
  const actionResponseFields = 'actions { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }' +
290
- (parentUiId ? ' flowLogics { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }' : '');
291
- // Build mutation payload when nesting inside a flow logic block, also update the parent
495
+ ' flowLogics { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }' +
496
+ ' subflows { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }';
497
+
498
+ // Build mutation payload — single INSERT with full inputs (matching UI behavior)
292
499
  const flowPatch: any = {
293
500
  flowId: flowId,
294
501
  actions: {
@@ -302,35 +509,35 @@ async function addActionViaGraphQL(
302
509
  uiUniqueIdentifier: uuid,
303
510
  type: 'action',
304
511
  parentUiId: parentUiId || '',
305
- inputs: []
512
+ inputs: inputResult.inputs
306
513
  }]
307
514
  }
308
515
  };
516
+
517
+ // Merge reorder updates + parent flow logic update into the mutation
518
+ var flowLogicUpdates: any[] = orderCalc.reorders.flowLogicUpdates.slice();
309
519
  if (parentUiId) {
310
- flowPatch.flowLogics = { update: [{ uiUniqueIdentifier: parentUiId, type: 'flowlogic' }] };
520
+ // Add parent update (no order change, just signals the parent was modified)
521
+ // Avoid duplicating if the parent is already in the reorder list
522
+ var parentAlreadyIncluded = flowLogicUpdates.some(function (u: any) { return u.uiUniqueIdentifier === parentUiId; });
523
+ if (!parentAlreadyIncluded) {
524
+ flowLogicUpdates.push({ uiUniqueIdentifier: parentUiId, type: 'flowlogic' });
525
+ }
526
+ }
527
+ if (flowLogicUpdates.length > 0) {
528
+ flowPatch.flowLogics = { update: flowLogicUpdates };
529
+ }
530
+ if (orderCalc.reorders.actionUpdates.length > 0) {
531
+ flowPatch.actions.update = orderCalc.reorders.actionUpdates;
311
532
  }
533
+ if (orderCalc.reorders.subflowUpdates.length > 0) {
534
+ flowPatch.subflows = { update: orderCalc.reorders.subflowUpdates };
535
+ }
536
+
312
537
  try {
313
538
  const result = await executeFlowPatchMutation(client, flowPatch, actionResponseFields);
314
-
315
539
  const actionId = result?.actions?.inserts?.[0]?.sysId;
316
540
  steps.insert = { success: !!actionId, actionId, uuid };
317
-
318
- if (actionId && Object.keys(resolvedInputs).length > 0) {
319
- const updateInputs = Object.entries(resolvedInputs).map(([name, value]) => ({
320
- name,
321
- value: { schemaless: false, schemalessValue: '', value: String(value) }
322
- }));
323
- try {
324
- await executeFlowPatchMutation(client, {
325
- flowId: flowId,
326
- actions: { update: [{ uiUniqueIdentifier: uuid, type: 'action', inputs: updateInputs }] }
327
- }, actionResponseFields);
328
- steps.value_update = { success: true, inputs: updateInputs.map(i => i.name) };
329
- } catch (e: any) {
330
- steps.value_update = { success: false, error: e.message };
331
- }
332
- }
333
-
334
541
  return { success: true, actionId: actionId || undefined, steps };
335
542
  } catch (e: any) {
336
543
  steps.insert = { success: false, error: e.message };
@@ -391,49 +598,63 @@ async function addFlowLogicViaGraphQL(
391
598
  }
392
599
  if (!defId) return { success: false, error: 'Flow logic definition not found for: ' + logicType, steps };
393
600
 
601
+ // Calculate insertion order with reordering
602
+ const orderCalc = await calculateInsertOrder(client, flowId, parentUiId, order);
603
+ const resolvedOrder = orderCalc.insertOrder;
604
+ steps.insert_order = resolvedOrder;
605
+
394
606
  const uuid = generateUUID();
395
- const resolvedOrder = order || await getNextOrder(client, flowId);
396
- const logicResponseFields = 'flowLogics { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }';
397
- try {
398
- const result = await executeFlowPatchMutation(client, {
399
- flowId: flowId,
400
- flowLogics: {
401
- insert: [{
402
- order: String(resolvedOrder),
403
- uiUniqueIdentifier: uuid,
404
- parent: '',
405
- metadata: '{"predicates":[]}',
406
- flowSysId: flowId,
407
- generationSource: '',
408
- definitionId: defId,
409
- type: 'flowlogic',
410
- parentUiId: parentUiId || '',
411
- inputs: []
412
- }]
413
- }
414
- }, logicResponseFields);
607
+ const logicResponseFields = 'flowLogics { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }' +
608
+ ' actions { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }' +
609
+ ' subflows { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }';
610
+
611
+ // Build flow logic input objects (for INSERT — matching UI format with id, name, value, parameter)
612
+ var logicInputObjects: any[] = [];
613
+ if (inputs && Object.keys(inputs).length > 0) {
614
+ logicInputObjects = Object.entries(inputs).map(function ([name, value]) {
615
+ return {
616
+ name: name,
617
+ value: { schemaless: false, schemalessValue: '', value: String(value) }
618
+ };
619
+ });
620
+ }
415
621
 
622
+ var flowPatch: any = {
623
+ flowId: flowId,
624
+ flowLogics: {
625
+ insert: [{
626
+ order: String(resolvedOrder),
627
+ uiUniqueIdentifier: uuid,
628
+ parent: '',
629
+ metadata: '{"predicates":[]}',
630
+ flowSysId: flowId,
631
+ generationSource: '',
632
+ definitionId: defId,
633
+ type: 'flowlogic',
634
+ parentUiId: parentUiId || '',
635
+ inputs: logicInputObjects
636
+ }]
637
+ }
638
+ };
639
+
640
+ // Merge reorder updates into flowLogics.update
641
+ if (orderCalc.reorders.flowLogicUpdates.length > 0) {
642
+ if (!flowPatch.flowLogics.update) flowPatch.flowLogics.update = [];
643
+ flowPatch.flowLogics.update = flowPatch.flowLogics.update.concat(orderCalc.reorders.flowLogicUpdates);
644
+ }
645
+ if (orderCalc.reorders.actionUpdates.length > 0) {
646
+ flowPatch.actions = { update: orderCalc.reorders.actionUpdates };
647
+ }
648
+ if (orderCalc.reorders.subflowUpdates.length > 0) {
649
+ flowPatch.subflows = { update: orderCalc.reorders.subflowUpdates };
650
+ }
651
+
652
+ try {
653
+ const result = await executeFlowPatchMutation(client, flowPatch, logicResponseFields);
416
654
  const logicId = result?.flowLogics?.inserts?.[0]?.sysId;
417
655
  steps.insert = { success: !!logicId, logicId, uuid };
418
656
  if (!logicId) return { success: false, steps, error: 'GraphQL flow logic INSERT returned no ID' };
419
657
 
420
- // Update with input values if provided
421
- if (inputs && Object.keys(inputs).length > 0) {
422
- const updateInputs = Object.entries(inputs).map(([name, value]) => ({
423
- name,
424
- value: { schemaless: false, schemalessValue: '', value: String(value) }
425
- }));
426
- try {
427
- await executeFlowPatchMutation(client, {
428
- flowId: flowId,
429
- flowLogics: { update: [{ uiUniqueIdentifier: uuid, type: 'flowlogic', inputs: updateInputs }] }
430
- }, logicResponseFields);
431
- steps.value_update = { success: true, inputs: updateInputs.map(i => i.name) };
432
- } catch (e: any) {
433
- steps.value_update = { success: false, error: e.message };
434
- }
435
- }
436
-
437
658
  return { success: true, logicId, steps };
438
659
  } catch (e: any) {
439
660
  steps.insert = { success: false, error: e.message };
@@ -499,10 +720,24 @@ async function addSubflowCallViaGraphQL(
499
720
 
500
721
  if (!subflowName) subflowName = subflowId;
501
722
 
723
+ // Calculate insertion order with reordering
724
+ const orderCalc = await calculateInsertOrder(client, flowId, parentUiId, order);
725
+ const resolvedOrder = orderCalc.insertOrder;
726
+ steps.insert_order = resolvedOrder;
727
+
502
728
  const uuid = generateUUID();
503
- const resolvedOrder = order || await getNextOrder(client, flowId);
504
729
  const subflowResponseFields = 'subflows { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }' +
505
- (parentUiId ? ' flowLogics { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }' : '');
730
+ ' flowLogics { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }' +
731
+ ' actions { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }';
732
+
733
+ // Build subflow input objects for INSERT
734
+ var subInputObjects: any[] = [];
735
+ if (inputs && Object.keys(inputs).length > 0) {
736
+ subInputObjects = Object.entries(inputs).map(function ([name, value]) {
737
+ return { name: name, value: { schemaless: false, schemalessValue: '', value: String(value) } };
738
+ });
739
+ }
740
+
506
741
  const subPatch: any = {
507
742
  flowId: flowId,
508
743
  subflows: {
@@ -517,37 +752,35 @@ async function addSubflowCallViaGraphQL(
517
752
  uiUniqueIdentifier: uuid,
518
753
  type: 'subflow',
519
754
  parentUiId: parentUiId || '',
520
- inputs: []
755
+ inputs: subInputObjects
521
756
  }]
522
757
  }
523
758
  };
759
+
760
+ // Merge reorder updates + parent flow logic update
761
+ var subFlowLogicUpdates: any[] = orderCalc.reorders.flowLogicUpdates.slice();
524
762
  if (parentUiId) {
525
- subPatch.flowLogics = { update: [{ uiUniqueIdentifier: parentUiId, type: 'flowlogic' }] };
763
+ var parentIncluded = subFlowLogicUpdates.some(function (u: any) { return u.uiUniqueIdentifier === parentUiId; });
764
+ if (!parentIncluded) {
765
+ subFlowLogicUpdates.push({ uiUniqueIdentifier: parentUiId, type: 'flowlogic' });
766
+ }
767
+ }
768
+ if (subFlowLogicUpdates.length > 0) {
769
+ subPatch.flowLogics = { update: subFlowLogicUpdates };
770
+ }
771
+ if (orderCalc.reorders.actionUpdates.length > 0) {
772
+ subPatch.actions = { update: orderCalc.reorders.actionUpdates };
526
773
  }
774
+ if (orderCalc.reorders.subflowUpdates.length > 0) {
775
+ if (!subPatch.subflows.update) subPatch.subflows.update = [];
776
+ subPatch.subflows.update = subPatch.subflows.update.concat(orderCalc.reorders.subflowUpdates);
777
+ }
778
+
527
779
  try {
528
780
  const result = await executeFlowPatchMutation(client, subPatch, subflowResponseFields);
529
-
530
781
  const callId = result?.subflows?.inserts?.[0]?.sysId;
531
782
  steps.insert = { success: !!callId, callId, uuid };
532
783
  if (!callId) return { success: false, steps, error: 'GraphQL subflow INSERT returned no ID' };
533
-
534
- // Update with input values if provided
535
- if (inputs && Object.keys(inputs).length > 0) {
536
- const updateInputs = Object.entries(inputs).map(([name, value]) => ({
537
- name,
538
- value: { schemaless: false, schemalessValue: '', value: String(value) }
539
- }));
540
- try {
541
- await executeFlowPatchMutation(client, {
542
- flowId: flowId,
543
- subflows: { update: [{ uiUniqueIdentifier: uuid, type: 'subflow', inputs: updateInputs }] }
544
- }, subflowResponseFields);
545
- steps.value_update = { success: true, inputs: updateInputs.map(i => i.name) };
546
- } catch (e: any) {
547
- steps.value_update = { success: false, error: e.message };
548
- }
549
- }
550
-
551
784
  return { success: true, callId, steps };
552
785
  } catch (e: any) {
553
786
  steps.insert = { success: false, error: e.message };