snow-flow 10.0.1-dev.443 → 10.0.1-dev.445

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.443",
3
+ "version": "10.0.1-dev.445",
4
4
  "name": "snow-flow",
5
5
  "description": "Snow-Flow - ServiceNow Multi-Agent Development Framework powered by AI",
6
6
  "license": "Elastic-2.0",
@@ -311,6 +311,216 @@ async function addActionViaGraphQL(
311
311
  }
312
312
  }
313
313
 
314
+ // ── FLOW LOGIC (If/Else, For Each, etc.) ─────────────────────────────
315
+
316
+ async function addFlowLogicViaGraphQL(
317
+ client: any,
318
+ flowId: string,
319
+ logicType: string,
320
+ inputs?: Record<string, string>,
321
+ order?: number,
322
+ parentUiId?: string
323
+ ): Promise<{ success: boolean; logicId?: string; steps?: any; error?: string }> {
324
+ const steps: any = {};
325
+
326
+ // Dynamically look up flow logic definition in sys_hub_flow_logic_definition
327
+ let defId: string | null = null;
328
+ let defName = '';
329
+ let defType = logicType;
330
+ // Try exact match on type (IF, FOR_EACH, DO_UNTIL, SWITCH), then name
331
+ for (const field of ['type', 'name']) {
332
+ if (defId) break;
333
+ try {
334
+ const resp = await client.get('/api/now/table/sys_hub_flow_logic_definition', {
335
+ params: { sysparm_query: field + '=' + logicType, sysparm_fields: 'sys_id,type,name,description', sysparm_limit: 1 }
336
+ });
337
+ const found = resp.data.result?.[0];
338
+ if (found?.sys_id) {
339
+ defId = found.sys_id;
340
+ defName = found.name || logicType;
341
+ defType = found.type || logicType;
342
+ steps.def_lookup = { id: found.sys_id, type: found.type, name: found.name, matched: field + '=' + logicType };
343
+ }
344
+ } catch (_) {}
345
+ }
346
+ // Fallback: LIKE search
347
+ if (!defId) {
348
+ try {
349
+ const resp = await client.get('/api/now/table/sys_hub_flow_logic_definition', {
350
+ params: {
351
+ sysparm_query: 'typeLIKE' + logicType + '^ORnameLIKE' + logicType,
352
+ sysparm_fields: 'sys_id,type,name,description', sysparm_limit: 5
353
+ }
354
+ });
355
+ const results = resp.data.result || [];
356
+ steps.def_lookup_fallback_candidates = results.map((r: any) => ({ sys_id: r.sys_id, type: r.type, name: r.name }));
357
+ if (results[0]?.sys_id) {
358
+ defId = results[0].sys_id;
359
+ defName = results[0].name || logicType;
360
+ defType = results[0].type || logicType;
361
+ steps.def_lookup = { id: results[0].sys_id, type: results[0].type, name: results[0].name, matched: 'LIKE ' + logicType };
362
+ }
363
+ } catch (_) {}
364
+ }
365
+ if (!defId) return { success: false, error: 'Flow logic definition not found for: ' + logicType, steps };
366
+
367
+ const uuid = generateUUID();
368
+ const logicResponseFields = 'flowLogics { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }';
369
+ try {
370
+ const result = await executeFlowPatchMutation(client, {
371
+ flowId: flowId,
372
+ flowLogics: {
373
+ insert: [{
374
+ order: String(order || 1),
375
+ uiUniqueIdentifier: uuid,
376
+ parent: '',
377
+ metadata: '{"predicates":[]}',
378
+ flowSysId: flowId,
379
+ generationSource: '',
380
+ definitionId: defId,
381
+ type: 'flowlogic',
382
+ parentUiId: parentUiId || '',
383
+ inputs: []
384
+ }]
385
+ }
386
+ }, logicResponseFields);
387
+
388
+ const logicId = result?.flowLogics?.inserts?.[0]?.sysId;
389
+ steps.insert = { success: !!logicId, logicId, uuid };
390
+ if (!logicId) return { success: false, steps, error: 'GraphQL flow logic INSERT returned no ID' };
391
+
392
+ // Update with input values if provided
393
+ if (inputs && Object.keys(inputs).length > 0) {
394
+ const updateInputs = Object.entries(inputs).map(([name, value]) => ({
395
+ name,
396
+ value: { schemaless: false, schemalessValue: '', value: String(value) }
397
+ }));
398
+ try {
399
+ await executeFlowPatchMutation(client, {
400
+ flowId: flowId,
401
+ flowLogics: { update: [{ uiUniqueIdentifier: uuid, type: 'flowlogic', inputs: updateInputs }] }
402
+ }, logicResponseFields);
403
+ steps.value_update = { success: true, inputs: updateInputs.map(i => i.name) };
404
+ } catch (e: any) {
405
+ steps.value_update = { success: false, error: e.message };
406
+ }
407
+ }
408
+
409
+ return { success: true, logicId, steps };
410
+ } catch (e: any) {
411
+ steps.insert = { success: false, error: e.message };
412
+ return { success: false, steps, error: 'GraphQL flow logic INSERT failed: ' + e.message };
413
+ }
414
+ }
415
+
416
+ // ── SUBFLOW CALL (invoke a subflow as a step) ────────────────────────
417
+
418
+ async function addSubflowCallViaGraphQL(
419
+ client: any,
420
+ flowId: string,
421
+ subflowId: string,
422
+ inputs?: Record<string, string>,
423
+ order?: number,
424
+ parentUiId?: string
425
+ ): Promise<{ success: boolean; callId?: string; steps?: any; error?: string }> {
426
+ const steps: any = {};
427
+
428
+ // Resolve subflow: look up by sys_id, name, or internal_name in sys_hub_flow
429
+ let subflowSysId = isSysId(subflowId) ? subflowId : null;
430
+ let subflowName = '';
431
+ if (!subflowSysId) {
432
+ for (const field of ['name', 'internal_name']) {
433
+ if (subflowSysId) break;
434
+ try {
435
+ const resp = await client.get('/api/now/table/sys_hub_flow', {
436
+ params: {
437
+ sysparm_query: field + '=' + subflowId + '^type=subflow',
438
+ sysparm_fields: 'sys_id,name,internal_name',
439
+ sysparm_limit: 1
440
+ }
441
+ });
442
+ const found = resp.data.result?.[0];
443
+ if (found?.sys_id) {
444
+ subflowSysId = found.sys_id;
445
+ subflowName = found.name || subflowId;
446
+ steps.subflow_lookup = { id: found.sys_id, name: found.name, internal_name: found.internal_name, matched: field + '=' + subflowId };
447
+ }
448
+ } catch (_) {}
449
+ }
450
+ // LIKE fallback
451
+ if (!subflowSysId) {
452
+ try {
453
+ const resp = await client.get('/api/now/table/sys_hub_flow', {
454
+ params: {
455
+ sysparm_query: 'nameLIKE' + subflowId + '^type=subflow',
456
+ sysparm_fields: 'sys_id,name,internal_name',
457
+ sysparm_limit: 5
458
+ }
459
+ });
460
+ const results = resp.data.result || [];
461
+ steps.subflow_lookup_candidates = results.map((r: any) => ({ sys_id: r.sys_id, name: r.name, internal_name: r.internal_name }));
462
+ if (results[0]?.sys_id) {
463
+ subflowSysId = results[0].sys_id;
464
+ subflowName = results[0].name || subflowId;
465
+ steps.subflow_lookup = { id: results[0].sys_id, name: results[0].name, matched: 'LIKE ' + subflowId };
466
+ }
467
+ } catch (_) {}
468
+ }
469
+ }
470
+ if (!subflowSysId) return { success: false, error: 'Subflow not found: ' + subflowId, steps };
471
+
472
+ if (!subflowName) subflowName = subflowId;
473
+
474
+ const uuid = generateUUID();
475
+ const subflowResponseFields = 'subflows { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }';
476
+ try {
477
+ const result = await executeFlowPatchMutation(client, {
478
+ flowId: flowId,
479
+ subflows: {
480
+ insert: [{
481
+ metadata: '{"predicates":[]}',
482
+ flowSysId: flowId,
483
+ generationSource: '',
484
+ name: subflowName,
485
+ order: String(order || 1),
486
+ parent: parentUiId || '',
487
+ subflowSysId: subflowSysId,
488
+ uiUniqueIdentifier: uuid,
489
+ type: 'subflow',
490
+ parentUiId: parentUiId || '',
491
+ inputs: []
492
+ }]
493
+ }
494
+ }, subflowResponseFields);
495
+
496
+ const callId = result?.subflows?.inserts?.[0]?.sysId;
497
+ steps.insert = { success: !!callId, callId, uuid };
498
+ if (!callId) return { success: false, steps, error: 'GraphQL subflow INSERT returned no ID' };
499
+
500
+ // Update with input values if provided
501
+ if (inputs && Object.keys(inputs).length > 0) {
502
+ const updateInputs = Object.entries(inputs).map(([name, value]) => ({
503
+ name,
504
+ value: { schemaless: false, schemalessValue: '', value: String(value) }
505
+ }));
506
+ try {
507
+ await executeFlowPatchMutation(client, {
508
+ flowId: flowId,
509
+ subflows: { update: [{ uiUniqueIdentifier: uuid, type: 'subflow', inputs: updateInputs }] }
510
+ }, subflowResponseFields);
511
+ steps.value_update = { success: true, inputs: updateInputs.map(i => i.name) };
512
+ } catch (e: any) {
513
+ steps.value_update = { success: false, error: e.message };
514
+ }
515
+ }
516
+
517
+ return { success: true, callId, steps };
518
+ } catch (e: any) {
519
+ steps.insert = { success: false, error: e.message };
520
+ return { success: false, steps, error: 'GraphQL subflow INSERT failed: ' + e.message };
521
+ }
522
+ }
523
+
314
524
  async function createFlowViaProcessFlowAPI(
315
525
  client: any,
316
526
  params: {
@@ -419,8 +629,8 @@ export const toolDefinition: MCPToolDefinition = {
419
629
  properties: {
420
630
  action: {
421
631
  type: 'string',
422
- enum: ['create', 'create_subflow', 'list', 'get', 'update', 'activate', 'deactivate', 'delete', 'publish', 'add_trigger', 'update_trigger', 'add_action'],
423
- description: 'Action to perform. Use add_trigger/add_action to add new elements, update_trigger to change an existing trigger type/table/condition.'
632
+ enum: ['create', 'create_subflow', 'list', 'get', 'update', 'activate', 'deactivate', 'delete', 'publish', 'add_trigger', 'update_trigger', 'add_action', 'add_flow_logic', 'add_subflow'],
633
+ description: 'Action to perform. Use add_trigger/add_action/add_flow_logic/add_subflow to add elements, update_trigger to change an existing trigger. Flow logic types: IF, FOR_EACH, DO_UNTIL, SWITCH. add_subflow calls an existing subflow as a step.'
424
634
  },
425
635
 
426
636
  flow_id: {
@@ -506,6 +716,22 @@ export const toolDefinition: MCPToolDefinition = {
506
716
  description: 'Activate flow after creation (default: true)',
507
717
  default: true
508
718
  },
719
+ logic_type: {
720
+ type: 'string',
721
+ description: 'Flow logic type for add_flow_logic. Looked up dynamically in sys_hub_flow_logic_definition. Common values: IF, FOR_EACH, DO_UNTIL, SWITCH'
722
+ },
723
+ logic_inputs: {
724
+ type: 'object',
725
+ description: 'Input values for the flow logic block (e.g. {condition: "expression", condition_name: "My Condition"})'
726
+ },
727
+ parent_ui_id: {
728
+ type: 'string',
729
+ description: 'Parent UI unique identifier for nesting elements inside flow logic blocks (e.g. placing actions/subflows inside an If block)'
730
+ },
731
+ subflow_id: {
732
+ type: 'string',
733
+ description: 'Subflow sys_id or name to call as a step (for add_subflow action). Looked up in sys_hub_flow where type=subflow.'
734
+ },
509
735
  type: {
510
736
  type: 'string',
511
737
  enum: ['flow', 'subflow', 'all'],
@@ -1444,6 +1670,74 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
1444
1670
  : createErrorResult(addActResult.error || 'Failed to add action');
1445
1671
  }
1446
1672
 
1673
+ // ────────────────────────────────────────────────────────────────
1674
+ // ADD_FLOW_LOGIC — add If/Else, For Each, Do Until, Switch blocks
1675
+ // ────────────────────────────────────────────────────────────────
1676
+ case 'add_flow_logic': {
1677
+ if (!args.flow_id) {
1678
+ throw new SnowFlowError(ErrorType.VALIDATION_ERROR, 'flow_id is required for add_flow_logic');
1679
+ }
1680
+ if (!args.logic_type) {
1681
+ throw new SnowFlowError(ErrorType.VALIDATION_ERROR, 'logic_type is required for add_flow_logic (e.g. IF, FOR_EACH, DO_UNTIL, SWITCH)');
1682
+ }
1683
+ var addLogicFlowId = await resolveFlowId(client, args.flow_id);
1684
+ var addLogicType = args.logic_type;
1685
+ var addLogicInputs = args.logic_inputs || {};
1686
+ var addLogicOrder = args.order;
1687
+ var addLogicParentUiId = args.parent_ui_id || '';
1688
+
1689
+ var addLogicResult = await addFlowLogicViaGraphQL(client, addLogicFlowId, addLogicType, addLogicInputs, addLogicOrder, addLogicParentUiId);
1690
+
1691
+ var addLogicSummary = summary();
1692
+ if (addLogicResult.success) {
1693
+ addLogicSummary
1694
+ .success('Flow logic added via GraphQL')
1695
+ .field('Flow', addLogicFlowId)
1696
+ .field('Type', addLogicType)
1697
+ .field('Logic ID', addLogicResult.logicId || 'unknown');
1698
+ } else {
1699
+ addLogicSummary.error('Failed to add flow logic: ' + (addLogicResult.error || 'unknown'));
1700
+ }
1701
+
1702
+ return addLogicResult.success
1703
+ ? createSuccessResult({ action: 'add_flow_logic', ...addLogicResult }, {}, addLogicSummary.build())
1704
+ : createErrorResult(addLogicResult.error || 'Failed to add flow logic');
1705
+ }
1706
+
1707
+ // ────────────────────────────────────────────────────────────────
1708
+ // ADD_SUBFLOW — call an existing subflow as a step in the flow
1709
+ // ────────────────────────────────────────────────────────────────
1710
+ case 'add_subflow': {
1711
+ if (!args.flow_id) {
1712
+ throw new SnowFlowError(ErrorType.VALIDATION_ERROR, 'flow_id is required for add_subflow');
1713
+ }
1714
+ if (!args.subflow_id) {
1715
+ throw new SnowFlowError(ErrorType.VALIDATION_ERROR, 'subflow_id is required for add_subflow (sys_id or name of the subflow to call)');
1716
+ }
1717
+ var addSubFlowId = await resolveFlowId(client, args.flow_id);
1718
+ var addSubSubflowId = args.subflow_id;
1719
+ var addSubInputs = args.action_inputs || args.inputs || {};
1720
+ var addSubOrder = args.order;
1721
+ var addSubParentUiId = args.parent_ui_id || '';
1722
+
1723
+ var addSubResult = await addSubflowCallViaGraphQL(client, addSubFlowId, addSubSubflowId, addSubInputs, addSubOrder, addSubParentUiId);
1724
+
1725
+ var addSubSummary = summary();
1726
+ if (addSubResult.success) {
1727
+ addSubSummary
1728
+ .success('Subflow call added via GraphQL')
1729
+ .field('Flow', addSubFlowId)
1730
+ .field('Subflow', addSubSubflowId)
1731
+ .field('Call ID', addSubResult.callId || 'unknown');
1732
+ } else {
1733
+ addSubSummary.error('Failed to add subflow call: ' + (addSubResult.error || 'unknown'));
1734
+ }
1735
+
1736
+ return addSubResult.success
1737
+ ? createSuccessResult({ action: 'add_subflow', ...addSubResult }, {}, addSubSummary.build())
1738
+ : createErrorResult(addSubResult.error || 'Failed to add subflow call');
1739
+ }
1740
+
1447
1741
  default:
1448
1742
  throw new SnowFlowError(ErrorType.VALIDATION_ERROR, 'Unknown action: ' + action);
1449
1743
  }