@elevasis/core 0.1.0 → 0.2.0

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.
Files changed (34) hide show
  1. package/dist/index.js +195 -3
  2. package/dist/organization-model/index.js +195 -3
  3. package/package.json +1 -1
  4. package/src/__tests__/template-foundations-compatibility.test.ts +95 -14
  5. package/src/auth/multi-tenancy/types.ts +2 -1
  6. package/src/execution/engine/__tests__/fixtures/test-agents.ts +4 -4
  7. package/src/execution/engine/index.ts +5 -19
  8. package/src/execution/engine/tools/platform/index.ts +9 -33
  9. package/src/execution/engine/tools/registry.ts +109 -2
  10. package/src/execution/engine/tools/tool-maps.ts +88 -0
  11. package/src/organization-model/README.md +19 -4
  12. package/src/organization-model/__tests__/graph.test.ts +612 -0
  13. package/src/organization-model/__tests__/resolve.test.ts +208 -0
  14. package/src/organization-model/defaults.ts +1 -1
  15. package/src/organization-model/organization-graph.mdx +262 -0
  16. package/src/organization-model/organization-model.mdx +257 -0
  17. package/src/organization-model/resolve.ts +26 -2
  18. package/src/organization-model/schema.ts +203 -1
  19. package/src/platform/constants/versions.ts +1 -1
  20. package/src/platform/registry/__tests__/resource-registry.integration.test.ts +24 -0
  21. package/src/platform/registry/__tests__/resource-registry.test.ts +63 -0
  22. package/src/platform/registry/resource-registry.ts +98 -10
  23. package/src/projects/api-schemas.ts +2 -1
  24. package/src/reference/_generated/contracts.md +1044 -0
  25. package/src/reference/glossary.md +88 -0
  26. package/src/server.ts +2 -3
  27. package/src/execution/engine/tools/platform/resource-invocation/__tests__/edge-cases.test.ts +0 -507
  28. package/src/execution/engine/tools/platform/resource-invocation/__tests__/resource-invocation-service.test.ts +0 -500
  29. package/src/execution/engine/tools/platform/resource-invocation/__tests__/tool.test.ts +0 -555
  30. package/src/execution/engine/tools/platform/resource-invocation/dynamic-tool.ts +0 -94
  31. package/src/execution/engine/tools/platform/resource-invocation/index.ts +0 -14
  32. package/src/execution/engine/tools/platform/resource-invocation/resource-invocation-service.ts +0 -147
  33. package/src/execution/engine/tools/platform/resource-invocation/tool.ts +0 -115
  34. package/src/execution/engine/tools/platform/resource-invocation/types.ts +0 -31
@@ -80,9 +80,621 @@ describe('organization-graph', () => {
80
80
  )
81
81
  })
82
82
 
83
+ describe('titleCase fallback for feature labels', () => {
84
+ it('uses titleCase of the feature key when no label is configured', () => {
85
+ const model = resolveOrganizationModel(
86
+ defineOrganizationModel({
87
+ branding: { organizationName: 'Acme', productName: 'Acme OS', shortName: 'Acme' }
88
+ })
89
+ )
90
+
91
+ const graph = buildOrganizationGraph({ organizationModel: model })
92
+
93
+ // All feature keys are single lowercase words with no label overrides by default.
94
+ // titleCase('acquisition') => 'Acquisition', etc.
95
+ const featureKeys = ['acquisition', 'delivery', 'operations', 'monitoring', 'settings', 'seo', 'calibration']
96
+ for (const key of featureKeys) {
97
+ const node = graph.nodes.find((n) => n.id === `feature:${key}`)
98
+ expect(node, `feature node for '${key}' should exist`).toBeDefined()
99
+ // No label override is set, so the label must be the titleCase of the key.
100
+ const expected = key.charAt(0).toUpperCase() + key.slice(1)
101
+ expect(node?.label).toBe(expected)
102
+ }
103
+ })
104
+
105
+ it('uses the explicit label from features.labels when one is configured', () => {
106
+ const model = resolveOrganizationModel(
107
+ defineOrganizationModel({
108
+ branding: { organizationName: 'Acme', productName: 'Acme OS', shortName: 'Acme' },
109
+ features: {
110
+ labels: { acquisition: 'Lead Generation' }
111
+ }
112
+ })
113
+ )
114
+
115
+ const graph = buildOrganizationGraph({ organizationModel: model })
116
+
117
+ const node = graph.nodes.find((n) => n.id === 'feature:acquisition')
118
+ expect(node?.label).toBe('Lead Generation')
119
+
120
+ // Other features without explicit labels still fall back to titleCase.
121
+ const deliveryNode = graph.nodes.find((n) => n.id === 'feature:delivery')
122
+ expect(deliveryNode?.label).toBe('Delivery')
123
+ })
124
+ })
125
+
126
+ describe('resource node upsert merging', () => {
127
+ it('keeps resourceMappings label and merges description from commandViewData', () => {
128
+ // Surface and domain are kept minimal (no cross-references) so that
129
+ // OrganizationModelSchema bidirectional integrity checks pass. The test
130
+ // is only exercising resource node merging, not surface/domain linkage.
131
+ const model = resolveOrganizationModel(
132
+ defineOrganizationModel({
133
+ domains: [
134
+ {
135
+ id: 'operations',
136
+ label: 'Operations',
137
+ color: 'violet',
138
+ entityIds: [],
139
+ surfaceIds: ['operations.command-view'],
140
+ resourceIds: ['workflow-order'],
141
+ capabilityIds: []
142
+ }
143
+ ],
144
+ navigation: {
145
+ defaultSurfaceId: 'operations.command-view',
146
+ surfaces: [
147
+ {
148
+ id: 'operations.command-view',
149
+ label: 'Command View',
150
+ path: '/operations/command-view',
151
+ surfaceType: 'graph',
152
+ featureKey: 'operations',
153
+ domainIds: ['operations'],
154
+ entityIds: [],
155
+ resourceIds: ['workflow-order'],
156
+ capabilityIds: []
157
+ }
158
+ ],
159
+ groups: [
160
+ {
161
+ id: 'primary-operations',
162
+ label: 'Operations',
163
+ placement: 'primary',
164
+ surfaceIds: ['operations.command-view']
165
+ }
166
+ ]
167
+ },
168
+ resourceMappings: [
169
+ {
170
+ id: 'workflow-order-model',
171
+ label: 'Order Workflow',
172
+ resourceId: 'workflow-order',
173
+ resourceType: 'workflow',
174
+ domainIds: ['operations'],
175
+ entityIds: [],
176
+ surfaceIds: ['operations.command-view'],
177
+ capabilityIds: []
178
+ }
179
+ ]
180
+ })
181
+ )
182
+
183
+ const graph = buildOrganizationGraph({
184
+ organizationModel: model,
185
+ commandViewData: {
186
+ workflows: [
187
+ {
188
+ resourceId: 'workflow-order',
189
+ name: 'Order Workflow Runtime',
190
+ description: 'Runtime description from commandViewData',
191
+ version: '1.0.0',
192
+ type: 'workflow',
193
+ status: 'prod',
194
+ domains: ['operations'],
195
+ stepCount: 2,
196
+ entryPoint: 'start'
197
+ }
198
+ ],
199
+ agents: [],
200
+ triggers: [],
201
+ integrations: [],
202
+ externalResources: [],
203
+ humanCheckpoints: [],
204
+ edges: []
205
+ }
206
+ })
207
+
208
+ const resourceNodes = graph.nodes.filter((n) => n.id === 'resource:workflow-order')
209
+ expect(resourceNodes).toHaveLength(1)
210
+
211
+ const node = resourceNodes[0]
212
+ // Label from resourceMappings is a real label (not just the sourceId), so it is preserved.
213
+ expect(node.label).toBe('Order Workflow')
214
+ // Description was absent in resourceMappings, so it is filled in from commandViewData.
215
+ expect(node.description).toBe('Runtime description from commandViewData')
216
+ })
217
+
218
+ it('preserves existing non-default label even when commandViewData provides a different name', () => {
219
+ const model = resolveOrganizationModel(
220
+ defineOrganizationModel({
221
+ domains: [
222
+ {
223
+ id: 'ops',
224
+ label: 'Ops',
225
+ color: 'blue',
226
+ entityIds: [],
227
+ surfaceIds: ['ops.view'],
228
+ resourceIds: ['res-a'],
229
+ capabilityIds: []
230
+ }
231
+ ],
232
+ navigation: {
233
+ defaultSurfaceId: 'ops.view',
234
+ surfaces: [
235
+ {
236
+ id: 'ops.view',
237
+ label: 'Ops View',
238
+ path: '/ops/view',
239
+ surfaceType: 'graph',
240
+ featureKey: 'operations',
241
+ domainIds: ['ops'],
242
+ entityIds: [],
243
+ resourceIds: ['res-a'],
244
+ capabilityIds: []
245
+ }
246
+ ],
247
+ groups: [{ id: 'g', label: 'Ops', placement: 'primary', surfaceIds: ['ops.view'] }]
248
+ },
249
+ resourceMappings: [
250
+ {
251
+ id: 'res-a-model',
252
+ label: 'My Custom Label',
253
+ resourceId: 'res-a',
254
+ resourceType: 'workflow',
255
+ domainIds: ['ops'],
256
+ entityIds: [],
257
+ surfaceIds: ['ops.view'],
258
+ capabilityIds: []
259
+ }
260
+ ]
261
+ })
262
+ )
263
+
264
+ const graph = buildOrganizationGraph({
265
+ organizationModel: model,
266
+ commandViewData: {
267
+ workflows: [
268
+ {
269
+ resourceId: 'res-a',
270
+ name: 'Runtime Name Should Not Override',
271
+ description: 'desc',
272
+ version: '1.0.0',
273
+ type: 'workflow',
274
+ status: 'prod',
275
+ domains: ['ops'],
276
+ stepCount: 1,
277
+ entryPoint: 'start'
278
+ }
279
+ ],
280
+ agents: [],
281
+ triggers: [],
282
+ integrations: [],
283
+ externalResources: [],
284
+ humanCheckpoints: [],
285
+ edges: []
286
+ }
287
+ })
288
+
289
+ const node = graph.nodes.find((n) => n.id === 'resource:res-a')
290
+ expect(node?.label).toBe('My Custom Label')
291
+ })
292
+ })
293
+
294
+ describe('de-duplication of graph elements', () => {
295
+ it('creates only one domain node when multiple surfaces reference the same domain', () => {
296
+ const model = resolveOrganizationModel(
297
+ defineOrganizationModel({
298
+ domains: [
299
+ {
300
+ id: 'crm',
301
+ label: 'CRM',
302
+ color: 'blue',
303
+ entityIds: [],
304
+ surfaceIds: ['crm.pipeline', 'crm.accounts'],
305
+ resourceIds: [],
306
+ capabilityIds: []
307
+ }
308
+ ],
309
+ navigation: {
310
+ defaultSurfaceId: 'crm.pipeline',
311
+ surfaces: [
312
+ {
313
+ id: 'crm.pipeline',
314
+ label: 'Pipeline',
315
+ path: '/crm/pipeline',
316
+ surfaceType: 'graph',
317
+ featureKey: 'acquisition',
318
+ domainIds: ['crm'],
319
+ entityIds: [],
320
+ resourceIds: [],
321
+ capabilityIds: []
322
+ },
323
+ {
324
+ id: 'crm.accounts',
325
+ label: 'Accounts',
326
+ path: '/crm/accounts',
327
+ surfaceType: 'list',
328
+ featureKey: 'acquisition',
329
+ domainIds: ['crm'],
330
+ entityIds: [],
331
+ resourceIds: [],
332
+ capabilityIds: []
333
+ }
334
+ ],
335
+ groups: [{ id: 'g', label: 'CRM', placement: 'primary', surfaceIds: ['crm.pipeline', 'crm.accounts'] }]
336
+ }
337
+ })
338
+ )
339
+
340
+ const graph = buildOrganizationGraph({ organizationModel: model })
341
+
342
+ const domainNodes = graph.nodes.filter((n) => n.id === 'domain:crm')
343
+ expect(domainNodes).toHaveLength(1)
344
+ })
345
+
346
+ it('creates only one entity node when the same entity appears in both a domain and a surface', () => {
347
+ const model = resolveOrganizationModel(
348
+ defineOrganizationModel({
349
+ domains: [
350
+ {
351
+ id: 'crm',
352
+ label: 'CRM',
353
+ color: 'blue',
354
+ entityIds: ['crm.deal'],
355
+ surfaceIds: ['crm.pipeline'],
356
+ resourceIds: [],
357
+ capabilityIds: []
358
+ }
359
+ ],
360
+ navigation: {
361
+ defaultSurfaceId: 'crm.pipeline',
362
+ surfaces: [
363
+ {
364
+ id: 'crm.pipeline',
365
+ label: 'Pipeline',
366
+ path: '/crm/pipeline',
367
+ surfaceType: 'graph',
368
+ featureKey: 'acquisition',
369
+ domainIds: ['crm'],
370
+ entityIds: ['crm.deal'],
371
+ resourceIds: [],
372
+ capabilityIds: []
373
+ }
374
+ ],
375
+ groups: [{ id: 'g', label: 'CRM', placement: 'primary', surfaceIds: ['crm.pipeline'] }]
376
+ }
377
+ })
378
+ )
379
+
380
+ const graph = buildOrganizationGraph({ organizationModel: model })
381
+
382
+ const entityNodes = graph.nodes.filter((n) => n.id === 'entity:crm.deal')
383
+ expect(entityNodes).toHaveLength(1)
384
+ })
385
+
386
+ it('creates only one resource node when the same resource appears in resourceMappings and commandViewData', () => {
387
+ const model = resolveOrganizationModel(
388
+ defineOrganizationModel({
389
+ domains: [
390
+ {
391
+ id: 'ops',
392
+ label: 'Ops',
393
+ color: 'violet',
394
+ entityIds: [],
395
+ surfaceIds: ['ops.view'],
396
+ resourceIds: ['wf-shared'],
397
+ capabilityIds: []
398
+ }
399
+ ],
400
+ navigation: {
401
+ defaultSurfaceId: 'ops.view',
402
+ surfaces: [
403
+ {
404
+ id: 'ops.view',
405
+ label: 'Ops View',
406
+ path: '/ops/view',
407
+ surfaceType: 'graph',
408
+ featureKey: 'operations',
409
+ domainIds: ['ops'],
410
+ entityIds: [],
411
+ resourceIds: ['wf-shared'],
412
+ capabilityIds: []
413
+ }
414
+ ],
415
+ groups: [{ id: 'g', label: 'Ops', placement: 'primary', surfaceIds: ['ops.view'] }]
416
+ },
417
+ resourceMappings: [
418
+ {
419
+ id: 'wf-shared-model',
420
+ label: 'Shared Workflow',
421
+ resourceId: 'wf-shared',
422
+ resourceType: 'workflow',
423
+ domainIds: ['ops'],
424
+ entityIds: [],
425
+ surfaceIds: ['ops.view'],
426
+ capabilityIds: []
427
+ }
428
+ ]
429
+ })
430
+ )
431
+
432
+ const graph = buildOrganizationGraph({
433
+ organizationModel: model,
434
+ commandViewData: {
435
+ workflows: [
436
+ {
437
+ resourceId: 'wf-shared',
438
+ name: 'Shared Workflow Runtime',
439
+ description: 'desc',
440
+ version: '1.0.0',
441
+ type: 'workflow',
442
+ status: 'prod',
443
+ domains: ['ops'],
444
+ stepCount: 1,
445
+ entryPoint: 'start'
446
+ }
447
+ ],
448
+ agents: [],
449
+ triggers: [],
450
+ integrations: [],
451
+ externalResources: [],
452
+ humanCheckpoints: [],
453
+ edges: []
454
+ }
455
+ })
456
+
457
+ const resourceNodes = graph.nodes.filter((n) => n.id === 'resource:wf-shared')
458
+ expect(resourceNodes).toHaveLength(1)
459
+ })
460
+ })
461
+
462
+ describe('commandViewData edge relationships', () => {
463
+ it('creates a stub resource node with label === resourceId for edges referencing unknown resources', () => {
464
+ const model = resolveOrganizationModel(
465
+ defineOrganizationModel({
466
+ domains: [
467
+ {
468
+ id: 'ops',
469
+ label: 'Ops',
470
+ color: 'violet',
471
+ entityIds: [],
472
+ surfaceIds: [],
473
+ resourceIds: [],
474
+ capabilityIds: []
475
+ }
476
+ ],
477
+ navigation: {
478
+ defaultSurfaceId: 'ops.view',
479
+ surfaces: [
480
+ {
481
+ id: 'ops.view',
482
+ label: 'Ops View',
483
+ path: '/ops/view',
484
+ surfaceType: 'graph',
485
+ featureKey: 'operations',
486
+ domainIds: [],
487
+ entityIds: [],
488
+ resourceIds: [],
489
+ capabilityIds: []
490
+ }
491
+ ],
492
+ groups: [{ id: 'g', label: 'Ops', placement: 'primary', surfaceIds: ['ops.view'] }]
493
+ }
494
+ })
495
+ )
496
+
497
+ // Both source and target in the edge are NOT listed in any workflow/agent/etc. array.
498
+ const graph = buildOrganizationGraph({
499
+ organizationModel: model,
500
+ commandViewData: {
501
+ workflows: [],
502
+ agents: [],
503
+ triggers: [],
504
+ integrations: [],
505
+ externalResources: [],
506
+ humanCheckpoints: [],
507
+ edges: [
508
+ {
509
+ id: 'edge-unknown',
510
+ source: 'unknown-source',
511
+ target: 'unknown-target',
512
+ relationship: 'triggers'
513
+ }
514
+ ]
515
+ }
516
+ })
517
+
518
+ const sourceNode = graph.nodes.find((n) => n.id === 'resource:unknown-source')
519
+ expect(sourceNode).toBeDefined()
520
+ expect(sourceNode?.label).toBe('unknown-source')
521
+ expect(sourceNode?.sourceId).toBe('unknown-source')
522
+
523
+ const targetNode = graph.nodes.find((n) => n.id === 'resource:unknown-target')
524
+ expect(targetNode).toBeDefined()
525
+ expect(targetNode?.label).toBe('unknown-target')
526
+ expect(targetNode?.sourceId).toBe('unknown-target')
527
+ })
528
+
529
+ it('normalizes humanCheckpoints type to human_checkpoint on resource nodes', () => {
530
+ const model = resolveOrganizationModel(
531
+ defineOrganizationModel({
532
+ domains: [
533
+ {
534
+ id: 'ops',
535
+ label: 'Ops',
536
+ color: 'violet',
537
+ entityIds: [],
538
+ surfaceIds: [],
539
+ resourceIds: [],
540
+ capabilityIds: []
541
+ }
542
+ ],
543
+ navigation: {
544
+ defaultSurfaceId: 'ops.view',
545
+ surfaces: [
546
+ {
547
+ id: 'ops.view',
548
+ label: 'Ops View',
549
+ path: '/ops/view',
550
+ surfaceType: 'graph',
551
+ featureKey: 'operations',
552
+ domainIds: [],
553
+ entityIds: [],
554
+ resourceIds: [],
555
+ capabilityIds: []
556
+ }
557
+ ],
558
+ groups: [{ id: 'g', label: 'Ops', placement: 'primary', surfaceIds: ['ops.view'] }]
559
+ }
560
+ })
561
+ )
562
+
563
+ const graph = buildOrganizationGraph({
564
+ organizationModel: model,
565
+ commandViewData: {
566
+ workflows: [],
567
+ agents: [],
568
+ triggers: [],
569
+ integrations: [],
570
+ externalResources: [],
571
+ humanCheckpoints: [
572
+ {
573
+ resourceId: 'approval-step',
574
+ name: 'Approval Step',
575
+ description: 'Human review gate',
576
+ version: '1.0.0',
577
+ type: 'human',
578
+ status: 'prod',
579
+ domains: ['ops']
580
+ }
581
+ ],
582
+ edges: []
583
+ }
584
+ })
585
+
586
+ const node = graph.nodes.find((n) => n.id === 'resource:approval-step')
587
+ expect(node).toBeDefined()
588
+ expect(node?.resourceType).toBe('human_checkpoint')
589
+ })
590
+
591
+ it('creates a references edge with label and relationshipType for commandViewData edges', () => {
592
+ const model = resolveOrganizationModel(
593
+ defineOrganizationModel({
594
+ domains: [
595
+ {
596
+ id: 'ops',
597
+ label: 'Ops',
598
+ color: 'violet',
599
+ entityIds: [],
600
+ surfaceIds: [],
601
+ resourceIds: [],
602
+ capabilityIds: []
603
+ }
604
+ ],
605
+ navigation: {
606
+ defaultSurfaceId: 'ops.view',
607
+ surfaces: [
608
+ {
609
+ id: 'ops.view',
610
+ label: 'Ops View',
611
+ path: '/ops/view',
612
+ surfaceType: 'graph',
613
+ featureKey: 'operations',
614
+ domainIds: [],
615
+ entityIds: [],
616
+ resourceIds: [],
617
+ capabilityIds: []
618
+ }
619
+ ],
620
+ groups: [{ id: 'g', label: 'Ops', placement: 'primary', surfaceIds: ['ops.view'] }]
621
+ }
622
+ })
623
+ )
624
+
625
+ const graph = buildOrganizationGraph({
626
+ organizationModel: model,
627
+ commandViewData: {
628
+ workflows: [],
629
+ agents: [],
630
+ triggers: [],
631
+ integrations: [],
632
+ externalResources: [],
633
+ humanCheckpoints: [],
634
+ edges: [
635
+ {
636
+ id: 'edge-triggers',
637
+ source: 'svc-a',
638
+ target: 'svc-b',
639
+ relationship: 'triggers'
640
+ }
641
+ ]
642
+ }
643
+ })
644
+
645
+ expect(graph.edges).toEqual(
646
+ expect.arrayContaining([
647
+ expect.objectContaining({
648
+ kind: 'references',
649
+ sourceId: 'resource:svc-a',
650
+ targetId: 'resource:svc-b',
651
+ label: 'triggers',
652
+ relationshipType: 'triggers'
653
+ })
654
+ ])
655
+ )
656
+ })
657
+ })
658
+
83
659
  it('bridges command view topology into resource nodes and labeled relationship edges', () => {
84
660
  const model = resolveOrganizationModel(
85
661
  defineOrganizationModel({
662
+ domains: [
663
+ {
664
+ id: 'operations',
665
+ label: 'Operations',
666
+ description: 'Operational resources, topology, and orchestration visibility',
667
+ color: 'violet',
668
+ entityIds: [],
669
+ surfaceIds: ['operations.command-view'],
670
+ resourceIds: ['workflow-order'],
671
+ capabilityIds: ['operations.command-view']
672
+ }
673
+ ],
674
+ navigation: {
675
+ defaultSurfaceId: 'operations.command-view',
676
+ surfaces: [
677
+ {
678
+ id: 'operations.command-view',
679
+ label: 'Command View',
680
+ path: '/operations/command-view',
681
+ surfaceType: 'graph',
682
+ featureKey: 'operations',
683
+ domainIds: ['operations'],
684
+ entityIds: [],
685
+ resourceIds: ['workflow-order'],
686
+ capabilityIds: ['operations.command-view']
687
+ }
688
+ ],
689
+ groups: [
690
+ {
691
+ id: 'primary-operations',
692
+ label: 'Operations',
693
+ placement: 'primary',
694
+ surfaceIds: ['operations.command-view']
695
+ }
696
+ ]
697
+ },
86
698
  resourceMappings: [
87
699
  {
88
700
  id: 'workflow-order-model',