snow-flow 10.0.1-dev.464 → 10.0.1-dev.466

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.464",
3
+ "version": "10.0.1-dev.466",
4
4
  "name": "snow-flow",
5
5
  "description": "Snow-Flow - ServiceNow Multi-Agent Development Framework powered by AI",
6
6
  "license": "Elastic-2.0",
@@ -141,7 +141,7 @@ async function buildActionInputsForInsert(
141
141
  }
142
142
  var match = actionParams.find(function (p: any) {
143
143
  var el = str(p.element);
144
- return el.endsWith('_' + key) || el === key || str(p.label).toLowerCase() === key.toLowerCase();
144
+ return el.endsWith('_' + key) || el.startsWith(key + '_') || el === key || str(p.label).toLowerCase() === key.toLowerCase();
145
145
  });
146
146
  if (match) resolvedInputs[str(match.element)] = value;
147
147
  else resolvedInputs[key] = value;
@@ -1001,6 +1001,15 @@ async function addActionViaGraphQL(
1001
1001
  steps.insert_order = resolvedOrder;
1002
1002
 
1003
1003
  const uuid = generateUUID();
1004
+
1005
+ // ── Data pill transformation for record actions (Update/Create Record) ──
1006
+ // These actions need: record → data pill, table_name → displayValue, field values → packed into values string
1007
+ var recordActionResult = await transformActionInputsForRecordAction(
1008
+ client, flowId, inputResult.inputs, inputResult.resolvedInputs,
1009
+ inputResult.actionParams, uuid
1010
+ );
1011
+ steps.record_action = recordActionResult.steps;
1012
+
1004
1013
  const actionResponseFields = 'actions { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }' +
1005
1014
  ' flowLogics { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }' +
1006
1015
  ' subflows { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }';
@@ -1019,11 +1028,16 @@ async function addActionViaGraphQL(
1019
1028
  uiUniqueIdentifier: uuid,
1020
1029
  type: 'action',
1021
1030
  parentUiId: parentUiId || '',
1022
- inputs: inputResult.inputs
1031
+ inputs: recordActionResult.inputs
1023
1032
  }]
1024
1033
  }
1025
1034
  };
1026
1035
 
1036
+ // Add labelCache entries for data pill references in record actions
1037
+ if (recordActionResult.labelCacheEntries.length > 0) {
1038
+ flowPatch.labelCache = { insert: recordActionResult.labelCacheEntries };
1039
+ }
1040
+
1027
1041
  // Add parent flow logic update signal (tells GraphQL the parent was modified)
1028
1042
  if (parentUiId) {
1029
1043
  flowPatch.flowLogics = { update: [{ uiUniqueIdentifier: parentUiId, type: 'flowlogic' }] };
@@ -1196,94 +1210,251 @@ function parseEncodedQuery(query: string): { prefix: string; field: string; oper
1196
1210
  }
1197
1211
 
1198
1212
  /**
1199
- * Transform an encoded query condition value to use data pill references.
1200
- * Replaces field names with {{dataPillBase.fieldName}} syntax.
1213
+ * Check if a condition value looks like a standard ServiceNow encoded query.
1214
+ * Standard encoded queries use: field_name=value^field_name2!=value2
1215
+ *
1216
+ * Returns false for JavaScript expressions, scripts, fd_data references, etc.
1217
+ * which should be passed through as-is without data pill transformation.
1218
+ */
1219
+ function isStandardEncodedQuery(condition: string): boolean {
1220
+ if (!condition) return false;
1221
+ // Parentheses indicate function calls or grouping expressions
1222
+ if (/[()]/.test(condition)) return false;
1223
+ // Method calls like .toString(, .replace(, .match(
1224
+ if (/\.\w+\(/.test(condition)) return false;
1225
+ // Regex patterns like /[
1226
+ if (/\/\[/.test(condition)) return false;
1227
+ // JS equality operators == or ===
1228
+ if (/===?/.test(condition)) return false;
1229
+ // JS modulo, logical AND/OR
1230
+ if (/%/.test(condition)) return false;
1231
+ if (/&&|\|\|/.test(condition)) return false;
1232
+ // Flow Designer internal variable references
1233
+ if (condition.startsWith('fd_data.')) return false;
1234
+ // Already contains data pill references (already transformed)
1235
+ if (condition.includes('{{')) return false;
1236
+ return true;
1237
+ }
1238
+
1239
+ /**
1240
+ * Transform an encoded query condition into Flow Designer data pill format.
1201
1241
  *
1202
- * Example: "category=inquiry" "{{Created or Updated_1.current.category}}=inquiry"
1242
+ * The UI format uses a record-level data pill prepended to the encoded query:
1243
+ * "category=inquiry" → "{{Created or Updated_1.current}}category=inquiry"
1244
+ *
1245
+ * The record pill tells Flow Designer which record/table the condition applies to.
1246
+ * The encoded query after the pill is the actual filter.
1203
1247
  */
1204
1248
  function transformConditionToDataPills(conditionValue: string, dataPillBase: string): string {
1205
1249
  if (!conditionValue || !dataPillBase) return conditionValue;
1206
-
1207
- var clauses = parseEncodedQuery(conditionValue);
1208
- if (clauses.length === 0) return conditionValue;
1209
-
1210
- var result = '';
1211
- for (var i = 0; i < clauses.length; i++) {
1212
- var clause = clauses[i];
1213
- result += clause.prefix;
1214
- result += '{{' + dataPillBase + '.' + clause.field + '}}';
1215
- result += clause.operator;
1216
- result += clause.value;
1217
- }
1218
-
1219
- return result;
1250
+ // Prepend the record-level data pill to the encoded query
1251
+ return '{{' + dataPillBase + '}}' + conditionValue;
1220
1252
  }
1221
1253
 
1222
1254
  /**
1223
- * Build labelCache entries for data pills used in flow logic conditions.
1224
- * Each unique data pill reference needs a labelCache entry so the Flow Designer UI
1225
- * can display the data pill label correctly.
1255
+ * Build a single record-level labelCache entry for the data pill used in a condition.
1256
+ * The UI registers the record pill with the condition input, matching the captured format:
1226
1257
  *
1227
- * The entry format matches the captured UI mutation format.
1258
+ * { name: "Created or Updated_1.current",
1259
+ * label: "Trigger - Record Created or Updated➛Incident Record",
1260
+ * reference: "incident", type: "reference", base_type: "reference",
1261
+ * usedInstances: [{ uiUniqueIdentifier, inputName: "condition" }] }
1228
1262
  */
1229
1263
  function buildConditionLabelCache(
1230
- conditionValue: string,
1231
1264
  dataPillBase: string,
1232
1265
  triggerName: string,
1233
1266
  table: string,
1234
1267
  tableLabel: string,
1235
1268
  logicUiId: string
1236
1269
  ): any[] {
1237
- if (!conditionValue || !dataPillBase) return [];
1270
+ if (!dataPillBase) return [];
1271
+
1272
+ return [{
1273
+ name: dataPillBase,
1274
+ label: 'Trigger - Record ' + triggerName + '\u27a1' + tableLabel + ' Record',
1275
+ reference: table,
1276
+ reference_display: tableLabel,
1277
+ type: 'reference',
1278
+ base_type: 'reference',
1279
+ attributes: '',
1280
+ usedInstances: [{ uiUniqueIdentifier: logicUiId, inputName: 'condition' }],
1281
+ choices: {}
1282
+ }];
1283
+ }
1238
1284
 
1239
- var clauses = parseEncodedQuery(conditionValue);
1240
- if (clauses.length === 0) return [];
1285
+ // ── DATA PILL SUPPORT FOR RECORD ACTIONS (Update/Create Record) ──────
1241
1286
 
1242
- // Build a label cache entry for each unique field-level data pill
1243
- var seen: Record<string, boolean> = {};
1244
- var entries: any[] = [];
1287
+ /** Shorthands that users can pass for `record` to mean "the trigger's current record". */
1288
+ const RECORD_PILL_SHORTHANDS = ['current', 'trigger.current', 'trigger_record', 'trigger record'];
1245
1289
 
1246
- // Also register the record-level data pill (the base: TriggerName_1.current)
1247
- // This is needed for the condition builder to understand the context
1248
- if (!seen[dataPillBase]) {
1249
- seen[dataPillBase] = true;
1250
- entries.push({
1290
+ /**
1291
+ * Post-process action inputs for record-modifying actions (Update Record, Create Record).
1292
+ *
1293
+ * These actions have 3 key inputs:
1294
+ * - `record`: reference to the record → needs data pill format {{TriggerName_1.current}}
1295
+ * - `table_name`: target table → needs displayValue (e.g. "Incident")
1296
+ * - `values`: packed field=value pairs → e.g. "priority=2^state=3"
1297
+ *
1298
+ * User-provided field-value pairs that don't match defined action parameters are
1299
+ * automatically packed into the `values` string.
1300
+ *
1301
+ * Returns the transformed inputs and labelCache entries.
1302
+ */
1303
+ async function transformActionInputsForRecordAction(
1304
+ client: any,
1305
+ flowId: string,
1306
+ actionInputs: any[],
1307
+ resolvedInputs: Record<string, string>,
1308
+ actionParams: any[],
1309
+ uuid: string
1310
+ ): Promise<{ inputs: any[]; labelCacheEntries: any[]; steps: any }> {
1311
+ var steps: any = {};
1312
+
1313
+ // Detect if this is a record action: must have both `record` and `table_name` parameters
1314
+ var definedParamNames = actionParams.map(function (p: any) { return str(p.element); });
1315
+ var hasRecord = definedParamNames.includes('record');
1316
+ var hasTableName = definedParamNames.includes('table_name');
1317
+ var hasValues = definedParamNames.includes('values');
1318
+
1319
+ if (!hasRecord || !hasTableName) {
1320
+ steps.record_action = false;
1321
+ return { inputs: actionInputs, labelCacheEntries: [], steps };
1322
+ }
1323
+ steps.record_action = true;
1324
+
1325
+ // Get trigger info for data pill construction
1326
+ var triggerInfo = await getFlowTriggerInfo(client, flowId);
1327
+ steps.trigger_info = {
1328
+ dataPillBase: triggerInfo.dataPillBase,
1329
+ triggerName: triggerInfo.triggerName,
1330
+ table: triggerInfo.table,
1331
+ tableLabel: triggerInfo.tableLabel,
1332
+ error: triggerInfo.error
1333
+ };
1334
+
1335
+ var dataPillBase = triggerInfo.dataPillBase; // e.g. "Created or Updated_1.current"
1336
+ var labelCacheEntries: any[] = [];
1337
+ var usedInstances: { uiUniqueIdentifier: string; inputName: string }[] = [];
1338
+
1339
+ // ── 1. Transform `record` input to data pill ──────────────────────
1340
+ var recordInput = actionInputs.find(function (inp: any) { return inp.name === 'record'; });
1341
+ if (recordInput && dataPillBase) {
1342
+ var recordVal = recordInput.value?.value || '';
1343
+ var isShorthand = RECORD_PILL_SHORTHANDS.includes(recordVal.toLowerCase());
1344
+ var isAlreadyPill = recordVal.startsWith('{{');
1345
+
1346
+ if (isShorthand || !recordVal) {
1347
+ // Auto-fill with trigger's current record data pill
1348
+ recordInput.value = { schemaless: false, schemalessValue: '', value: '{{' + dataPillBase + '}}' };
1349
+ usedInstances.push({ uiUniqueIdentifier: uuid, inputName: 'record' });
1350
+ steps.record_transform = { original: recordVal, pill: '{{' + dataPillBase + '}}' };
1351
+ } else if (isAlreadyPill) {
1352
+ usedInstances.push({ uiUniqueIdentifier: uuid, inputName: 'record' });
1353
+ }
1354
+ }
1355
+
1356
+ // ── 2. Transform `table_name` input with displayValue ─────────────
1357
+ var tableNameInput = actionInputs.find(function (inp: any) { return inp.name === 'table_name'; });
1358
+ if (tableNameInput) {
1359
+ var tableVal = tableNameInput.value?.value || '';
1360
+ // Also accept `table` as user key (maps to table_name)
1361
+ if (!tableVal && resolvedInputs['table']) {
1362
+ tableVal = resolvedInputs['table'];
1363
+ }
1364
+ // If still empty, use trigger's table
1365
+ if (!tableVal && triggerInfo.table) {
1366
+ tableVal = triggerInfo.table;
1367
+ }
1368
+ if (tableVal) {
1369
+ // Look up display name for the table
1370
+ var tableDisplayName = triggerInfo.tableLabel || '';
1371
+ if (tableVal !== triggerInfo.table || !tableDisplayName) {
1372
+ // Different table than trigger — look up its label
1373
+ try {
1374
+ var tblResp = await client.get('/api/now/table/sys_db_object', {
1375
+ params: { sysparm_query: 'name=' + tableVal, sysparm_fields: 'label', sysparm_display_value: 'true', sysparm_limit: 1 }
1376
+ });
1377
+ tableDisplayName = str(tblResp.data.result?.[0]?.label) || tableVal.charAt(0).toUpperCase() + tableVal.slice(1).replace(/_/g, ' ');
1378
+ } catch (_) {
1379
+ tableDisplayName = tableVal.charAt(0).toUpperCase() + tableVal.slice(1).replace(/_/g, ' ');
1380
+ }
1381
+ }
1382
+ tableNameInput.value = { schemaless: false, schemalessValue: '', value: tableVal };
1383
+ tableNameInput.displayValue = { schemaless: false, schemalessValue: '', value: tableDisplayName };
1384
+ steps.table_name_transform = { value: tableVal, displayValue: tableDisplayName };
1385
+ }
1386
+ }
1387
+
1388
+ // ── 3. Pack non-parameter field values into `values` string ───────
1389
+ // Any user-provided key that is NOT a defined action parameter goes into the values string
1390
+ var valuesInput = actionInputs.find(function (inp: any) { return inp.name === 'values'; });
1391
+ if (valuesInput) {
1392
+ var fieldPairs: string[] = [];
1393
+ var existingValues = valuesInput.value?.value || '';
1394
+
1395
+ // If user already passed a pre-built values string, use it
1396
+ if (existingValues && existingValues.includes('=')) {
1397
+ fieldPairs.push(existingValues);
1398
+ }
1399
+
1400
+ // Find user-provided keys that are not defined action parameters
1401
+ for (var key of Object.keys(resolvedInputs)) {
1402
+ if (definedParamNames.includes(key)) continue;
1403
+ // Also skip table (alias for table_name) and record
1404
+ if (key === 'table' || key === 'record') continue;
1405
+
1406
+ var val = resolvedInputs[key];
1407
+
1408
+ // Check if value should be a data pill reference
1409
+ if (val && dataPillBase) {
1410
+ var valLower = val.toLowerCase();
1411
+ if (RECORD_PILL_SHORTHANDS.includes(valLower)) {
1412
+ // Shorthand → record-level data pill
1413
+ val = '{{' + dataPillBase + '}}';
1414
+ usedInstances.push({ uiUniqueIdentifier: uuid, inputName: key });
1415
+ } else if (valLower.startsWith('trigger.current.') || valLower.startsWith('current.')) {
1416
+ // Field-level data pill: "trigger.current.assigned_to" → {{dataPillBase.assigned_to}}
1417
+ var fieldName = valLower.startsWith('trigger.current.') ? val.substring(16) : val.substring(8);
1418
+ val = '{{' + dataPillBase + '.' + fieldName + '}}';
1419
+ usedInstances.push({ uiUniqueIdentifier: uuid, inputName: key });
1420
+ } else if (val.startsWith('{{')) {
1421
+ // Already a data pill
1422
+ usedInstances.push({ uiUniqueIdentifier: uuid, inputName: key });
1423
+ }
1424
+ }
1425
+
1426
+ fieldPairs.push(key + '=' + val);
1427
+ }
1428
+
1429
+ if (fieldPairs.length > 0) {
1430
+ var packedValues = fieldPairs.join('^');
1431
+ valuesInput.value = { schemaless: false, schemalessValue: '', value: packedValues };
1432
+ steps.values_transform = { packed: packedValues, fieldCount: fieldPairs.length };
1433
+ }
1434
+ }
1435
+
1436
+ // ── 4. Build labelCache entries for data pills ────────────────────
1437
+ if (dataPillBase && usedInstances.length > 0) {
1438
+ var tableRef = triggerInfo.tableRef || triggerInfo.table || '';
1439
+ var tableLabel = triggerInfo.tableLabel || '';
1440
+
1441
+ // Record-level data pill entry
1442
+ labelCacheEntries.push({
1251
1443
  name: dataPillBase,
1252
- label: 'Trigger - Record ' + triggerName + '\u27a1' + tableLabel + ' Record',
1253
- reference: table,
1444
+ label: 'Trigger - Record ' + triggerInfo.triggerName + '\u27a1' + tableLabel + ' Record',
1445
+ reference: tableRef,
1254
1446
  reference_display: tableLabel,
1255
1447
  type: 'reference',
1256
1448
  base_type: 'reference',
1257
1449
  attributes: '',
1258
- usedInstances: [{ uiUniqueIdentifier: logicUiId, inputName: 'condition' }],
1450
+ usedInstances: usedInstances,
1259
1451
  choices: {}
1260
1452
  });
1261
- }
1262
1453
 
1263
- for (var i = 0; i < clauses.length; i++) {
1264
- var field = clauses[i].field;
1265
- if (!field) continue;
1266
- var pillName = dataPillBase + '.' + field;
1267
- if (seen[pillName]) continue;
1268
- seen[pillName] = true;
1269
-
1270
- // Capitalize field name for label
1271
- var fieldLabel = field.replace(/_/g, ' ').replace(/\b\w/g, function (c: string) { return c.toUpperCase(); });
1272
-
1273
- entries.push({
1274
- name: pillName,
1275
- label: 'Trigger - Record ' + triggerName + '\u27a1' + tableLabel + ' Record\u27a1' + fieldLabel,
1276
- reference: '',
1277
- reference_display: '',
1278
- type: 'string',
1279
- base_type: 'string',
1280
- attributes: '',
1281
- usedInstances: [{ uiUniqueIdentifier: logicUiId, inputName: 'condition' }],
1282
- choices: {}
1283
- });
1454
+ steps.label_cache = { count: labelCacheEntries.length, pills: [dataPillBase], usedInstances: usedInstances.length };
1284
1455
  }
1285
1456
 
1286
- return entries;
1457
+ return { inputs: actionInputs, labelCacheEntries, steps };
1287
1458
  }
1288
1459
 
1289
1460
  // ── FLOW LOGIC (If/Else, For Each, etc.) ─────────────────────────────
@@ -1358,31 +1529,32 @@ async function addFlowLogicViaGraphQL(
1358
1529
  steps.resolved_inputs = inputResult.resolvedInputs;
1359
1530
  steps.input_query_stats = { defParamsFound: inputResult.defParamsCount, inputsBuilt: inputResult.inputs.length, error: inputResult.inputQueryError };
1360
1531
 
1361
- // ── Data pill transformation for condition inputs ───────────────────
1362
- // Flow Designer conditions require data pill references ({{TriggerName_1.current.field}})
1363
- // instead of bare field names. Transform encoded query conditions and build labelCache.
1532
+ // ── Detect condition that needs data pill transformation ────────────
1533
+ // Flow Designer sets conditions via a SEPARATE UPDATE after the element is created.
1534
+ // The condition format uses record-level data pill: {{TriggerName_1.current}}encodedQuery
1535
+ // Non-standard conditions (JS expressions, fd_data refs) are passed through as-is.
1364
1536
  const uuid = generateUUID();
1365
- var labelCacheEntries: any[] = [];
1366
-
1367
1537
  var conditionInput = inputResult.inputs.find(function (inp: any) { return inp.name === 'condition'; });
1368
1538
  var rawCondition = conditionInput?.value?.value || '';
1369
- if (rawCondition && rawCondition !== '^EQ') {
1370
- var triggerInfo = await getFlowTriggerInfo(client, flowId);
1371
- steps.trigger_info = { dataPillBase: triggerInfo.dataPillBase, triggerName: triggerInfo.triggerName, table: triggerInfo.table, tableLabel: triggerInfo.tableLabel, error: triggerInfo.error };
1372
-
1373
- if (triggerInfo.dataPillBase) {
1374
- // Transform condition value: "category=inquiry" → "{{Created or Updated_1.current.category}}=inquiry"
1375
- var transformedCondition = transformConditionToDataPills(rawCondition, triggerInfo.dataPillBase);
1376
- conditionInput.value = { schemaless: false, schemalessValue: '', value: transformedCondition };
1377
- steps.condition_transform = { original: rawCondition, transformed: transformedCondition };
1378
-
1379
- // Build labelCache entries for each data pill used in the condition
1380
- labelCacheEntries = buildConditionLabelCache(
1381
- rawCondition, triggerInfo.dataPillBase, triggerInfo.triggerName,
1382
- triggerInfo.tableRef, triggerInfo.tableLabel, uuid
1383
- );
1384
- steps.label_cache = { count: labelCacheEntries.length, pills: labelCacheEntries.map(function (e: any) { return e.name; }) };
1539
+ var needsConditionUpdate = false;
1540
+ var conditionTriggerInfo: any = null;
1541
+
1542
+ if (rawCondition && rawCondition !== '^EQ' && isStandardEncodedQuery(rawCondition)) {
1543
+ conditionTriggerInfo = await getFlowTriggerInfo(client, flowId);
1544
+ steps.trigger_info = {
1545
+ dataPillBase: conditionTriggerInfo.dataPillBase, triggerName: conditionTriggerInfo.triggerName,
1546
+ table: conditionTriggerInfo.table, tableLabel: conditionTriggerInfo.tableLabel, error: conditionTriggerInfo.error
1547
+ };
1548
+ if (conditionTriggerInfo.dataPillBase) {
1549
+ needsConditionUpdate = true;
1550
+ // Clear condition in INSERT — it will be set via separate UPDATE with labelCache
1551
+ conditionInput.value = { schemaless: false, schemalessValue: '', value: '' };
1552
+ steps.condition_strategy = 'two_step';
1385
1553
  }
1554
+ } else if (rawCondition && rawCondition !== '^EQ') {
1555
+ // Non-standard condition (JS expression, fd_data ref, etc.) — pass through as-is
1556
+ steps.condition_strategy = 'passthrough';
1557
+ steps.condition_not_encoded_query = true;
1386
1558
  }
1387
1559
 
1388
1560
  // Calculate insertion order
@@ -1419,23 +1591,55 @@ async function addFlowLogicViaGraphQL(
1419
1591
  }
1420
1592
  };
1421
1593
 
1422
- // Add labelCache entries for data pill references in conditions
1423
- if (labelCacheEntries.length > 0) {
1424
- flowPatch.labelCache = { insert: labelCacheEntries };
1425
- }
1426
-
1427
1594
  // Add parent flow logic update signal (tells GraphQL the parent was modified)
1428
1595
  if (parentUiId) {
1429
1596
  flowPatch.flowLogics.update = [{ uiUniqueIdentifier: parentUiId, type: 'flowlogic' }];
1430
1597
  }
1431
1598
 
1432
1599
  try {
1600
+ // Step 1: INSERT the flow logic element (with empty condition if data pill transform is needed)
1433
1601
  const result = await executeFlowPatchMutation(client, flowPatch, logicResponseFields);
1434
1602
  const logicId = result?.flowLogics?.inserts?.[0]?.sysId;
1435
1603
  const returnedUuid = result?.flowLogics?.inserts?.[0]?.uiUniqueIdentifier || uuid;
1436
1604
  steps.insert = { success: !!logicId, logicId, uuid: returnedUuid };
1437
1605
  if (!logicId) return { success: false, steps, error: 'GraphQL flow logic INSERT returned no ID' };
1438
1606
 
1607
+ // Step 2: UPDATE condition with data pill + labelCache (separate mutation, matching UI behavior)
1608
+ // The Flow Designer UI always sets conditions in a separate UPDATE after creating the element.
1609
+ if (needsConditionUpdate && conditionTriggerInfo) {
1610
+ var dataPillBase = conditionTriggerInfo.dataPillBase;
1611
+ var transformedCondition = transformConditionToDataPills(rawCondition, dataPillBase);
1612
+ var labelCacheEntries = buildConditionLabelCache(
1613
+ dataPillBase, conditionTriggerInfo.triggerName,
1614
+ conditionTriggerInfo.tableRef, conditionTriggerInfo.tableLabel, returnedUuid
1615
+ );
1616
+
1617
+ steps.condition_transform = { original: rawCondition, transformed: transformedCondition };
1618
+ steps.label_cache = { count: labelCacheEntries.length, pills: labelCacheEntries.map(function (e: any) { return e.name; }) };
1619
+
1620
+ try {
1621
+ var updatePatch: any = {
1622
+ flowId: flowId,
1623
+ labelCache: { insert: labelCacheEntries },
1624
+ flowLogics: {
1625
+ update: [{
1626
+ uiUniqueIdentifier: returnedUuid,
1627
+ type: 'flowlogic',
1628
+ inputs: [{
1629
+ name: 'condition',
1630
+ value: { schemaless: false, schemalessValue: '', value: transformedCondition }
1631
+ }]
1632
+ }]
1633
+ }
1634
+ };
1635
+ await executeFlowPatchMutation(client, updatePatch, logicResponseFields);
1636
+ steps.condition_update = { success: true };
1637
+ } catch (ue: any) {
1638
+ steps.condition_update = { success: false, error: ue.message };
1639
+ // Element was created successfully — condition update failure is non-fatal
1640
+ }
1641
+ }
1642
+
1439
1643
  return { success: true, logicId, uiUniqueIdentifier: returnedUuid, steps };
1440
1644
  } catch (e: any) {
1441
1645
  steps.insert = { success: false, error: e.message };