@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
package/dist/index.js CHANGED
@@ -304,7 +304,7 @@ var DEFAULT_ORGANIZATION_MODEL_NAVIGATION = {
304
304
  };
305
305
 
306
306
  // src/organization-model/schema.ts
307
- var OrganizationModelSchema = z.object({
307
+ var OrganizationModelSchemaBase = z.object({
308
308
  version: z.literal(1).default(1),
309
309
  domains: z.array(SemanticDomainSchema).default([]),
310
310
  branding: OrganizationModelBrandingSchema,
@@ -315,6 +315,181 @@ var OrganizationModelSchema = z.object({
315
315
  delivery: OrganizationModelDeliverySchema,
316
316
  resourceMappings: z.array(ResourceMappingSchema).default([])
317
317
  });
318
+ function addIssue(ctx, path, message) {
319
+ ctx.addIssue({
320
+ code: z.ZodIssueCode.custom,
321
+ path,
322
+ message
323
+ });
324
+ }
325
+ function collectIds(items, ctx, collectionPath, label) {
326
+ const itemsById = /* @__PURE__ */ new Map();
327
+ items.forEach((item, index) => {
328
+ if (itemsById.has(item.id)) {
329
+ addIssue(ctx, [...collectionPath, index, "id"], `${label} id "${item.id}" must be unique`);
330
+ return;
331
+ }
332
+ itemsById.set(item.id, item);
333
+ });
334
+ return itemsById;
335
+ }
336
+ var OrganizationModelSchema = OrganizationModelSchemaBase.superRefine((model, ctx) => {
337
+ const domainsById = collectIds(model.domains, ctx, ["domains"], "Domain");
338
+ const surfacesById = collectIds(model.navigation.surfaces, ctx, ["navigation", "surfaces"], "Surface");
339
+ collectIds(model.navigation.groups, ctx, ["navigation", "groups"], "Navigation group");
340
+ collectIds(model.resourceMappings, ctx, ["resourceMappings"], "Resource mapping");
341
+ const resourceMappingsByResourceId = /* @__PURE__ */ new Map();
342
+ model.resourceMappings.forEach((resourceMapping, index) => {
343
+ if (resourceMappingsByResourceId.has(resourceMapping.resourceId)) {
344
+ addIssue(
345
+ ctx,
346
+ ["resourceMappings", index, "resourceId"],
347
+ `Resource mapping resourceId "${resourceMapping.resourceId}" must be unique`
348
+ );
349
+ return;
350
+ }
351
+ resourceMappingsByResourceId.set(resourceMapping.resourceId, resourceMapping);
352
+ });
353
+ if (model.navigation.defaultSurfaceId && !surfacesById.has(model.navigation.defaultSurfaceId)) {
354
+ addIssue(
355
+ ctx,
356
+ ["navigation", "defaultSurfaceId"],
357
+ `Default surface "${model.navigation.defaultSurfaceId}" must reference a declared navigation surface`
358
+ );
359
+ }
360
+ model.navigation.groups.forEach((group, groupIndex) => {
361
+ group.surfaceIds.forEach((surfaceId, surfaceIndex) => {
362
+ if (!surfacesById.has(surfaceId)) {
363
+ addIssue(
364
+ ctx,
365
+ ["navigation", "groups", groupIndex, "surfaceIds", surfaceIndex],
366
+ `Navigation group "${group.id}" references unknown surface "${surfaceId}"`
367
+ );
368
+ }
369
+ });
370
+ });
371
+ model.domains.forEach((domain, domainIndex) => {
372
+ domain.surfaceIds.forEach((surfaceId, surfaceIndex) => {
373
+ const surface = surfacesById.get(surfaceId);
374
+ if (!surface) {
375
+ addIssue(
376
+ ctx,
377
+ ["domains", domainIndex, "surfaceIds", surfaceIndex],
378
+ `Domain "${domain.id}" references unknown surface "${surfaceId}"`
379
+ );
380
+ return;
381
+ }
382
+ if (!surface.domainIds.includes(domain.id)) {
383
+ addIssue(
384
+ ctx,
385
+ ["domains", domainIndex, "surfaceIds", surfaceIndex],
386
+ `Domain "${domain.id}" references surface "${surfaceId}" but that surface does not include domain "${domain.id}"`
387
+ );
388
+ }
389
+ });
390
+ domain.resourceIds.forEach((resourceId, resourceIndex) => {
391
+ const resourceMapping = resourceMappingsByResourceId.get(resourceId);
392
+ if (!resourceMapping) {
393
+ addIssue(
394
+ ctx,
395
+ ["domains", domainIndex, "resourceIds", resourceIndex],
396
+ `Domain "${domain.id}" references unknown resource "${resourceId}"`
397
+ );
398
+ return;
399
+ }
400
+ if (!resourceMapping.domainIds.includes(domain.id)) {
401
+ addIssue(
402
+ ctx,
403
+ ["domains", domainIndex, "resourceIds", resourceIndex],
404
+ `Domain "${domain.id}" references resource "${resourceId}" but that resource mapping does not include domain "${domain.id}"`
405
+ );
406
+ }
407
+ });
408
+ });
409
+ model.navigation.surfaces.forEach((surface, surfaceIndex) => {
410
+ if (surface.parentId && !surfacesById.has(surface.parentId)) {
411
+ addIssue(
412
+ ctx,
413
+ ["navigation", "surfaces", surfaceIndex, "parentId"],
414
+ `Surface "${surface.id}" references unknown parent surface "${surface.parentId}"`
415
+ );
416
+ }
417
+ surface.domainIds.forEach((domainId, domainIndex) => {
418
+ const domain = domainsById.get(domainId);
419
+ if (!domain) {
420
+ addIssue(
421
+ ctx,
422
+ ["navigation", "surfaces", surfaceIndex, "domainIds", domainIndex],
423
+ `Surface "${surface.id}" references unknown domain "${domainId}"`
424
+ );
425
+ return;
426
+ }
427
+ if (!domain.surfaceIds.includes(surface.id)) {
428
+ addIssue(
429
+ ctx,
430
+ ["navigation", "surfaces", surfaceIndex, "domainIds", domainIndex],
431
+ `Surface "${surface.id}" references domain "${domainId}" but that domain does not include surface "${surface.id}"`
432
+ );
433
+ }
434
+ });
435
+ surface.resourceIds.forEach((resourceId, resourceIndex) => {
436
+ const resourceMapping = resourceMappingsByResourceId.get(resourceId);
437
+ if (!resourceMapping) {
438
+ addIssue(
439
+ ctx,
440
+ ["navigation", "surfaces", surfaceIndex, "resourceIds", resourceIndex],
441
+ `Surface "${surface.id}" references unknown resource "${resourceId}"`
442
+ );
443
+ return;
444
+ }
445
+ if (!resourceMapping.surfaceIds.includes(surface.id)) {
446
+ addIssue(
447
+ ctx,
448
+ ["navigation", "surfaces", surfaceIndex, "resourceIds", resourceIndex],
449
+ `Surface "${surface.id}" references resource "${resourceId}" but that resource mapping does not include surface "${surface.id}"`
450
+ );
451
+ }
452
+ });
453
+ });
454
+ model.resourceMappings.forEach((resourceMapping, resourceIndex) => {
455
+ resourceMapping.domainIds.forEach((domainId, domainIndex) => {
456
+ const domain = domainsById.get(domainId);
457
+ if (!domain) {
458
+ addIssue(
459
+ ctx,
460
+ ["resourceMappings", resourceIndex, "domainIds", domainIndex],
461
+ `Resource mapping "${resourceMapping.id}" references unknown domain "${domainId}"`
462
+ );
463
+ return;
464
+ }
465
+ if (!domain.resourceIds.includes(resourceMapping.resourceId)) {
466
+ addIssue(
467
+ ctx,
468
+ ["resourceMappings", resourceIndex, "domainIds", domainIndex],
469
+ `Resource mapping "${resourceMapping.id}" references domain "${domainId}" but that domain does not include resource "${resourceMapping.resourceId}"`
470
+ );
471
+ }
472
+ });
473
+ resourceMapping.surfaceIds.forEach((surfaceId, surfaceIndex) => {
474
+ const surface = surfacesById.get(surfaceId);
475
+ if (!surface) {
476
+ addIssue(
477
+ ctx,
478
+ ["resourceMappings", resourceIndex, "surfaceIds", surfaceIndex],
479
+ `Resource mapping "${resourceMapping.id}" references unknown surface "${surfaceId}"`
480
+ );
481
+ return;
482
+ }
483
+ if (!surface.resourceIds.includes(resourceMapping.resourceId)) {
484
+ addIssue(
485
+ ctx,
486
+ ["resourceMappings", resourceIndex, "surfaceIds", surfaceIndex],
487
+ `Resource mapping "${resourceMapping.id}" references surface "${surfaceId}" but that surface does not include resource "${resourceMapping.resourceId}"`
488
+ );
489
+ }
490
+ });
491
+ });
492
+ });
318
493
 
319
494
  // src/organization-model/defaults.ts
320
495
  var DEFAULT_ORGANIZATION_MODEL = {
@@ -342,7 +517,7 @@ var DEFAULT_ORGANIZATION_MODEL = {
342
517
  },
343
518
  {
344
519
  id: "delivery",
345
- label: "Delivery",
520
+ label: "Projects",
346
521
  description: "Projects, milestones, and client work execution",
347
522
  color: "orange",
348
523
  entityIds: ["delivery.project", "delivery.milestone", "delivery.task"],
@@ -392,11 +567,28 @@ function deepMerge(base, override) {
392
567
  }
393
568
  return result;
394
569
  }
570
+ function normalizeInheritedNavigationGroups(model, override) {
571
+ if (override?.navigation?.surfaces === void 0 || override.navigation.groups !== void 0) {
572
+ return model;
573
+ }
574
+ const validSurfaceIds = new Set(model.navigation.surfaces.map((surface) => surface.id));
575
+ const groups = model.navigation.groups.map((group) => ({
576
+ ...group,
577
+ surfaceIds: group.surfaceIds.filter((surfaceId) => validSurfaceIds.has(surfaceId))
578
+ })).filter((group) => group.surfaceIds.length > 0);
579
+ return {
580
+ ...model,
581
+ navigation: {
582
+ ...model.navigation,
583
+ groups
584
+ }
585
+ };
586
+ }
395
587
  function defineOrganizationModel(model) {
396
588
  return model;
397
589
  }
398
590
  function resolveOrganizationModel(override) {
399
- const merged = deepMerge(DEFAULT_ORGANIZATION_MODEL, override);
591
+ const merged = normalizeInheritedNavigationGroups(deepMerge(DEFAULT_ORGANIZATION_MODEL, override), override);
400
592
  return OrganizationModelSchema.parse(merged);
401
593
  }
402
594
 
@@ -304,7 +304,7 @@ var DEFAULT_ORGANIZATION_MODEL_NAVIGATION = {
304
304
  };
305
305
 
306
306
  // src/organization-model/schema.ts
307
- var OrganizationModelSchema = z.object({
307
+ var OrganizationModelSchemaBase = z.object({
308
308
  version: z.literal(1).default(1),
309
309
  domains: z.array(SemanticDomainSchema).default([]),
310
310
  branding: OrganizationModelBrandingSchema,
@@ -315,6 +315,181 @@ var OrganizationModelSchema = z.object({
315
315
  delivery: OrganizationModelDeliverySchema,
316
316
  resourceMappings: z.array(ResourceMappingSchema).default([])
317
317
  });
318
+ function addIssue(ctx, path, message) {
319
+ ctx.addIssue({
320
+ code: z.ZodIssueCode.custom,
321
+ path,
322
+ message
323
+ });
324
+ }
325
+ function collectIds(items, ctx, collectionPath, label) {
326
+ const itemsById = /* @__PURE__ */ new Map();
327
+ items.forEach((item, index) => {
328
+ if (itemsById.has(item.id)) {
329
+ addIssue(ctx, [...collectionPath, index, "id"], `${label} id "${item.id}" must be unique`);
330
+ return;
331
+ }
332
+ itemsById.set(item.id, item);
333
+ });
334
+ return itemsById;
335
+ }
336
+ var OrganizationModelSchema = OrganizationModelSchemaBase.superRefine((model, ctx) => {
337
+ const domainsById = collectIds(model.domains, ctx, ["domains"], "Domain");
338
+ const surfacesById = collectIds(model.navigation.surfaces, ctx, ["navigation", "surfaces"], "Surface");
339
+ collectIds(model.navigation.groups, ctx, ["navigation", "groups"], "Navigation group");
340
+ collectIds(model.resourceMappings, ctx, ["resourceMappings"], "Resource mapping");
341
+ const resourceMappingsByResourceId = /* @__PURE__ */ new Map();
342
+ model.resourceMappings.forEach((resourceMapping, index) => {
343
+ if (resourceMappingsByResourceId.has(resourceMapping.resourceId)) {
344
+ addIssue(
345
+ ctx,
346
+ ["resourceMappings", index, "resourceId"],
347
+ `Resource mapping resourceId "${resourceMapping.resourceId}" must be unique`
348
+ );
349
+ return;
350
+ }
351
+ resourceMappingsByResourceId.set(resourceMapping.resourceId, resourceMapping);
352
+ });
353
+ if (model.navigation.defaultSurfaceId && !surfacesById.has(model.navigation.defaultSurfaceId)) {
354
+ addIssue(
355
+ ctx,
356
+ ["navigation", "defaultSurfaceId"],
357
+ `Default surface "${model.navigation.defaultSurfaceId}" must reference a declared navigation surface`
358
+ );
359
+ }
360
+ model.navigation.groups.forEach((group, groupIndex) => {
361
+ group.surfaceIds.forEach((surfaceId, surfaceIndex) => {
362
+ if (!surfacesById.has(surfaceId)) {
363
+ addIssue(
364
+ ctx,
365
+ ["navigation", "groups", groupIndex, "surfaceIds", surfaceIndex],
366
+ `Navigation group "${group.id}" references unknown surface "${surfaceId}"`
367
+ );
368
+ }
369
+ });
370
+ });
371
+ model.domains.forEach((domain, domainIndex) => {
372
+ domain.surfaceIds.forEach((surfaceId, surfaceIndex) => {
373
+ const surface = surfacesById.get(surfaceId);
374
+ if (!surface) {
375
+ addIssue(
376
+ ctx,
377
+ ["domains", domainIndex, "surfaceIds", surfaceIndex],
378
+ `Domain "${domain.id}" references unknown surface "${surfaceId}"`
379
+ );
380
+ return;
381
+ }
382
+ if (!surface.domainIds.includes(domain.id)) {
383
+ addIssue(
384
+ ctx,
385
+ ["domains", domainIndex, "surfaceIds", surfaceIndex],
386
+ `Domain "${domain.id}" references surface "${surfaceId}" but that surface does not include domain "${domain.id}"`
387
+ );
388
+ }
389
+ });
390
+ domain.resourceIds.forEach((resourceId, resourceIndex) => {
391
+ const resourceMapping = resourceMappingsByResourceId.get(resourceId);
392
+ if (!resourceMapping) {
393
+ addIssue(
394
+ ctx,
395
+ ["domains", domainIndex, "resourceIds", resourceIndex],
396
+ `Domain "${domain.id}" references unknown resource "${resourceId}"`
397
+ );
398
+ return;
399
+ }
400
+ if (!resourceMapping.domainIds.includes(domain.id)) {
401
+ addIssue(
402
+ ctx,
403
+ ["domains", domainIndex, "resourceIds", resourceIndex],
404
+ `Domain "${domain.id}" references resource "${resourceId}" but that resource mapping does not include domain "${domain.id}"`
405
+ );
406
+ }
407
+ });
408
+ });
409
+ model.navigation.surfaces.forEach((surface, surfaceIndex) => {
410
+ if (surface.parentId && !surfacesById.has(surface.parentId)) {
411
+ addIssue(
412
+ ctx,
413
+ ["navigation", "surfaces", surfaceIndex, "parentId"],
414
+ `Surface "${surface.id}" references unknown parent surface "${surface.parentId}"`
415
+ );
416
+ }
417
+ surface.domainIds.forEach((domainId, domainIndex) => {
418
+ const domain = domainsById.get(domainId);
419
+ if (!domain) {
420
+ addIssue(
421
+ ctx,
422
+ ["navigation", "surfaces", surfaceIndex, "domainIds", domainIndex],
423
+ `Surface "${surface.id}" references unknown domain "${domainId}"`
424
+ );
425
+ return;
426
+ }
427
+ if (!domain.surfaceIds.includes(surface.id)) {
428
+ addIssue(
429
+ ctx,
430
+ ["navigation", "surfaces", surfaceIndex, "domainIds", domainIndex],
431
+ `Surface "${surface.id}" references domain "${domainId}" but that domain does not include surface "${surface.id}"`
432
+ );
433
+ }
434
+ });
435
+ surface.resourceIds.forEach((resourceId, resourceIndex) => {
436
+ const resourceMapping = resourceMappingsByResourceId.get(resourceId);
437
+ if (!resourceMapping) {
438
+ addIssue(
439
+ ctx,
440
+ ["navigation", "surfaces", surfaceIndex, "resourceIds", resourceIndex],
441
+ `Surface "${surface.id}" references unknown resource "${resourceId}"`
442
+ );
443
+ return;
444
+ }
445
+ if (!resourceMapping.surfaceIds.includes(surface.id)) {
446
+ addIssue(
447
+ ctx,
448
+ ["navigation", "surfaces", surfaceIndex, "resourceIds", resourceIndex],
449
+ `Surface "${surface.id}" references resource "${resourceId}" but that resource mapping does not include surface "${surface.id}"`
450
+ );
451
+ }
452
+ });
453
+ });
454
+ model.resourceMappings.forEach((resourceMapping, resourceIndex) => {
455
+ resourceMapping.domainIds.forEach((domainId, domainIndex) => {
456
+ const domain = domainsById.get(domainId);
457
+ if (!domain) {
458
+ addIssue(
459
+ ctx,
460
+ ["resourceMappings", resourceIndex, "domainIds", domainIndex],
461
+ `Resource mapping "${resourceMapping.id}" references unknown domain "${domainId}"`
462
+ );
463
+ return;
464
+ }
465
+ if (!domain.resourceIds.includes(resourceMapping.resourceId)) {
466
+ addIssue(
467
+ ctx,
468
+ ["resourceMappings", resourceIndex, "domainIds", domainIndex],
469
+ `Resource mapping "${resourceMapping.id}" references domain "${domainId}" but that domain does not include resource "${resourceMapping.resourceId}"`
470
+ );
471
+ }
472
+ });
473
+ resourceMapping.surfaceIds.forEach((surfaceId, surfaceIndex) => {
474
+ const surface = surfacesById.get(surfaceId);
475
+ if (!surface) {
476
+ addIssue(
477
+ ctx,
478
+ ["resourceMappings", resourceIndex, "surfaceIds", surfaceIndex],
479
+ `Resource mapping "${resourceMapping.id}" references unknown surface "${surfaceId}"`
480
+ );
481
+ return;
482
+ }
483
+ if (!surface.resourceIds.includes(resourceMapping.resourceId)) {
484
+ addIssue(
485
+ ctx,
486
+ ["resourceMappings", resourceIndex, "surfaceIds", surfaceIndex],
487
+ `Resource mapping "${resourceMapping.id}" references surface "${surfaceId}" but that surface does not include resource "${resourceMapping.resourceId}"`
488
+ );
489
+ }
490
+ });
491
+ });
492
+ });
318
493
 
319
494
  // src/organization-model/defaults.ts
320
495
  var DEFAULT_ORGANIZATION_MODEL = {
@@ -342,7 +517,7 @@ var DEFAULT_ORGANIZATION_MODEL = {
342
517
  },
343
518
  {
344
519
  id: "delivery",
345
- label: "Delivery",
520
+ label: "Projects",
346
521
  description: "Projects, milestones, and client work execution",
347
522
  color: "orange",
348
523
  entityIds: ["delivery.project", "delivery.milestone", "delivery.task"],
@@ -392,11 +567,28 @@ function deepMerge(base, override) {
392
567
  }
393
568
  return result;
394
569
  }
570
+ function normalizeInheritedNavigationGroups(model, override) {
571
+ if (override?.navigation?.surfaces === void 0 || override.navigation.groups !== void 0) {
572
+ return model;
573
+ }
574
+ const validSurfaceIds = new Set(model.navigation.surfaces.map((surface) => surface.id));
575
+ const groups = model.navigation.groups.map((group) => ({
576
+ ...group,
577
+ surfaceIds: group.surfaceIds.filter((surfaceId) => validSurfaceIds.has(surfaceId))
578
+ })).filter((group) => group.surfaceIds.length > 0);
579
+ return {
580
+ ...model,
581
+ navigation: {
582
+ ...model.navigation,
583
+ groups
584
+ }
585
+ };
586
+ }
395
587
  function defineOrganizationModel(model) {
396
588
  return model;
397
589
  }
398
590
  function resolveOrganizationModel(override) {
399
- const merged = deepMerge(DEFAULT_ORGANIZATION_MODEL, override);
591
+ const merged = normalizeInheritedNavigationGroups(deepMerge(DEFAULT_ORGANIZATION_MODEL, override), override);
400
592
  return OrganizationModelSchema.parse(merged);
401
593
  }
402
594
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elevasis/core",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "license": "MIT",
5
5
  "description": "Minimal shared constants across Elevasis monorepo",
6
6
  "sideEffects": false,
@@ -5,22 +5,13 @@ import {
5
5
  organizationModel as foundationOrganizationModel,
6
6
  quickAccessSurfaceIds
7
7
  } from '../../../../external/_template/foundations/config/organization-model'
8
- import { resolveOrganizationModel } from '../published'
9
8
 
10
9
  describe('template foundations compatibility', () => {
11
- it('adapts the foundations organization model onto the published core contract while preserving UI helpers', () => {
12
- const resolved = resolveOrganizationModel(foundationOrganizationModel)
13
-
10
+ it('preserves the adapter-level shell helpers on top of the canonical model', () => {
14
11
  expect(foundationOrganizationModel.navigation.homeLabel).toBe(homeLabel)
15
12
  expect(foundationOrganizationModel.navigation.quickAccessSurfaceIds).toEqual(quickAccessSurfaceIds)
16
- expect(resolved.branding.organizationName).toBe(foundationOrganizationModel.branding.organizationName)
17
- expect(resolved.branding.productName).toBe(foundationOrganizationModel.branding.productName)
18
- expect(resolved.navigation.defaultSurfaceId).toBe('operations')
19
- expect(resolved.navigation.surfaces.map((surface) => surface.id)).toEqual(
20
- foundationOrganizationModel.navigation.surfaces.map((surface) => surface.id)
21
- )
22
- expect(resolved.features.enabled.acquisition).toBe(true)
23
- expect(resolved.features.enabled.calibration).toBe(false)
13
+ expect(foundationOrganizationModel.version).toBe(1)
14
+ expect(foundationOrganizationModel.navigation.defaultSurfaceId).toBe('operations')
24
15
  expect(getOrganizationSurface('operations')).toMatchObject({
25
16
  id: 'operations',
26
17
  label: 'Operations',
@@ -28,7 +19,97 @@ describe('template foundations compatibility', () => {
28
19
  surfaceType: 'graph',
29
20
  featureKey: 'operations'
30
21
  })
31
- expect(resolved.crm.pipelines.length).toBeGreaterThan(0)
32
- expect(resolved.delivery.projectStatuses.length).toBeGreaterThan(0)
22
+
23
+ expect(getOrganizationSurface('operations.organization-graph')).toMatchObject({
24
+ id: 'operations.organization-graph',
25
+ label: 'Operations',
26
+ path: '/operations',
27
+ surfaceType: 'graph',
28
+ featureKey: 'operations'
29
+ })
30
+ })
31
+
32
+ it('projects legacy template aliases with canonical navigation metadata', () => {
33
+ const legacySurfaces = [
34
+ {
35
+ id: 'operations',
36
+ label: 'Operations',
37
+ surfaceType: 'graph',
38
+ expectedFeatureKey: 'operations',
39
+ expectedIcon: 'operations',
40
+ expectedPath: '/operations',
41
+ expectedDomainIds: ['operations'],
42
+ expectedEntityIds: [],
43
+ expectedCapabilityIds: ['operations.organization-graph', 'operations.command-view']
44
+ },
45
+ {
46
+ id: 'projects',
47
+ label: 'Projects',
48
+ surfaceType: 'list',
49
+ expectedFeatureKey: 'projects',
50
+ expectedIcon: 'projects',
51
+ expectedPath: '/projects',
52
+ expectedDomainIds: ['delivery'],
53
+ expectedEntityIds: ['delivery.project'],
54
+ expectedCapabilityIds: ['delivery.projects.view']
55
+ },
56
+ {
57
+ id: 'lead-gen',
58
+ label: 'Lead Gen',
59
+ surfaceType: 'list',
60
+ expectedFeatureKey: 'lead-gen',
61
+ expectedIcon: 'lead-gen',
62
+ expectedPath: '/lead-gen',
63
+ expectedDomainIds: ['lead-gen'],
64
+ expectedEntityIds: ['leadgen.list'],
65
+ expectedCapabilityIds: ['leadgen.lists.manage']
66
+ },
67
+ {
68
+ id: 'crm',
69
+ label: 'CRM',
70
+ surfaceType: 'graph',
71
+ expectedFeatureKey: 'crm',
72
+ expectedIcon: 'crm',
73
+ expectedPath: '/crm',
74
+ expectedDomainIds: ['crm'],
75
+ expectedEntityIds: ['crm.deal'],
76
+ expectedCapabilityIds: ['crm.pipeline.manage']
77
+ }
78
+ ] as const
79
+
80
+ expect(getOrganizationSurface('operations.organization-graph')).toBeDefined()
81
+
82
+ for (const surface of legacySurfaces) {
83
+ expect(getOrganizationSurface(surface.id)).toMatchObject({
84
+ id: surface.id,
85
+ label: surface.label,
86
+ path: surface.expectedPath,
87
+ surfaceType: surface.surfaceType,
88
+ icon: surface.expectedIcon,
89
+ featureKey: surface.expectedFeatureKey,
90
+ domainIds: surface.expectedDomainIds,
91
+ entityIds: surface.expectedEntityIds,
92
+ resourceIds: [],
93
+ capabilityIds: surface.expectedCapabilityIds
94
+ })
95
+ }
96
+ })
97
+
98
+ it('maps grouped core features onto the legacy template feature vocabulary', () => {
99
+ expect(foundationOrganizationModel.features.enabled.acquisition).toBe(true)
100
+ expect(foundationOrganizationModel.features.enabled.delivery).toBe(true)
101
+ expect(foundationOrganizationModel.features.enabled.crm).toBe(
102
+ foundationOrganizationModel.features.enabled.acquisition
103
+ )
104
+ expect(foundationOrganizationModel.features.enabled['lead-gen']).toBe(
105
+ foundationOrganizationModel.features.enabled.acquisition
106
+ )
107
+ expect(foundationOrganizationModel.features.enabled.projects).toBe(
108
+ foundationOrganizationModel.features.enabled.delivery
109
+ )
110
+
111
+ expect(foundationOrganizationModel.features.labels.crm).toBe('CRM')
112
+ expect(foundationOrganizationModel.features.labels['lead-gen']).toBe('Lead Gen')
113
+ expect(foundationOrganizationModel.features.labels.projects).toBe('Projects')
33
114
  })
34
115
  })
@@ -10,13 +10,14 @@
10
10
  /**
11
11
  * Per-user-per-org config (stored in org_memberships.config)
12
12
  * Controls which features a specific member can access within their org
13
- * Valid feature keys: operations, monitoring, acquisition, calibration, seo
13
+ * Valid feature keys: operations, monitoring, acquisition, delivery, calibration, seo
14
14
  */
15
15
  export interface MembershipFeatureConfig {
16
16
  features?: {
17
17
  operations?: boolean
18
18
  monitoring?: boolean
19
19
  acquisition?: boolean
20
+ delivery?: boolean
20
21
  calibration?: boolean
21
22
  seo?: boolean
22
23
  }
@@ -1,7 +1,7 @@
1
- import { z } from 'zod'
2
- import type { AgentDefinition } from '../../agent/core/types'
3
- import type { KnowledgeMap } from '../../agent/knowledge-map/types'
4
- import { createApprovalTool } from '../../tools/platform'
1
+ import { z } from 'zod'
2
+ import type { AgentDefinition } from '../../agent/core/types'
3
+ import type { KnowledgeMap } from '../../agent/knowledge-map/types'
4
+ import { createApprovalTool } from '../../tools/platform/approval/tool'
5
5
 
6
6
  /**
7
7
  * Create test agent with platform tools
@@ -244,6 +244,8 @@ export {
244
244
  type INotificationsService,
245
245
  type ICredentialsService,
246
246
  type IIntegrationService,
247
+ type ICrmService,
248
+ type IProjectsService,
247
249
  // Email Service types
248
250
  type IEmailService,
249
251
  type SendPlatformEmailParams,
@@ -321,24 +323,8 @@ export {
321
323
  } from './tools/registry'
322
324
 
323
325
  // Platform Tools (server-side only)
326
+ // Retained internal/compatibility surface. Deployed SDK workers use platform.call() -> dispatcher.
324
327
  export {
325
- createApprovalTool,
326
- createSchedulerTool,
327
- createNotificationTool,
328
- type ApprovalToolConfig,
329
- type SchedulerToolConfig,
330
- type NotificationToolConfig,
331
- // Resource Invocation (service, types, and tool factories)
332
- ResourceInvocationService,
333
- createResourceInvocationService,
334
- type ResourceExecutionRequest,
335
- type ResourceInvocationResult,
336
- // Tool factories (Wave 2a)
337
- createResourceInvocationTool,
338
- createDynamicResourceInvocationTool,
339
- type ResourceInvocationToolConfig,
340
- dynamicResourceInvocationSchema,
341
- dynamicResourceInvocationOutputSchema,
342
328
  // Storage Tools
343
329
  createStorageUploadTool,
344
330
  createStorageSignedUrlTool,
@@ -387,8 +373,6 @@ export {
387
373
  CancelScheduleByKeyOutputSchema,
388
374
  DeleteScheduleByKeyInputSchema,
389
375
  DeleteScheduleByKeyOutputSchema,
390
- // HITL Cancel by Metadata Tool
391
- createHitlCancelByMetadataTool,
392
376
  type HitlCancelByMetadataInput,
393
377
  type HitlCancelByMetadataOutput,
394
378
  HitlCancelByMetadataInputSchema,
@@ -428,6 +412,8 @@ export type {
428
412
  StorageToolMap,
429
413
  NotificationToolMap,
430
414
  NotificationSDKInput,
415
+ ProjectsToolMap,
416
+ CrmToolMap,
431
417
  LeadToolMap,
432
418
  ListToolMap,
433
419
  PdfToolMap,