@elevasis/core 0.20.0 → 0.22.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 (77) hide show
  1. package/dist/index.d.ts +524 -6
  2. package/dist/index.js +417 -42
  3. package/dist/knowledge/index.d.ts +151 -1
  4. package/dist/organization-model/index.d.ts +524 -6
  5. package/dist/organization-model/index.js +417 -42
  6. package/dist/test-utils/index.d.ts +270 -1
  7. package/dist/test-utils/index.js +407 -41
  8. package/package.json +5 -5
  9. package/src/_gen/__tests__/__snapshots__/contracts.md.snap +501 -303
  10. package/src/auth/multi-tenancy/permissions.ts +20 -8
  11. package/src/business/README.md +2 -2
  12. package/src/business/acquisition/api-schemas.test.ts +198 -0
  13. package/src/business/acquisition/api-schemas.ts +250 -9
  14. package/src/business/acquisition/build-templates.test.ts +28 -0
  15. package/src/business/acquisition/build-templates.ts +20 -8
  16. package/src/business/acquisition/index.ts +12 -0
  17. package/src/business/acquisition/types.ts +6 -1
  18. package/src/business/clients/api-schemas.test.ts +115 -0
  19. package/src/business/clients/api-schemas.ts +158 -0
  20. package/src/business/clients/index.ts +1 -0
  21. package/src/business/deals/api-schemas.ts +8 -0
  22. package/src/business/index.ts +5 -2
  23. package/src/business/projects/types.ts +19 -0
  24. package/src/execution/engine/__tests__/fixtures/test-agents.ts +10 -8
  25. package/src/execution/engine/agent/core/__tests__/agent.test.ts +16 -12
  26. package/src/execution/engine/agent/core/__tests__/error-passthrough.test.ts +4 -3
  27. package/src/execution/engine/agent/core/types.ts +25 -15
  28. package/src/execution/engine/agent/index.ts +6 -4
  29. package/src/execution/engine/agent/reasoning/__tests__/request-builder.test.ts +24 -18
  30. package/src/execution/engine/index.ts +3 -0
  31. package/src/execution/engine/tools/integration/server/adapters/apify/apify-adapter.test.ts +55 -0
  32. package/src/execution/engine/tools/integration/server/adapters/apify/apify-adapter.ts +107 -41
  33. package/src/execution/engine/tools/integration/server/adapters/apollo/apollo-adapter.test.ts +48 -0
  34. package/src/execution/engine/tools/integration/server/adapters/apollo/apollo-adapter.ts +99 -0
  35. package/src/execution/engine/tools/integration/server/adapters/apollo/index.ts +1 -0
  36. package/src/execution/engine/tools/integration/server/adapters/clickup/clickup-adapter.test.ts +18 -0
  37. package/src/execution/engine/tools/integration/server/adapters/clickup/clickup-adapter.ts +194 -0
  38. package/src/execution/engine/tools/integration/server/adapters/clickup/index.ts +7 -0
  39. package/src/execution/engine/workflow/types.ts +7 -0
  40. package/src/integrations/credentials/api-schemas.ts +21 -2
  41. package/src/integrations/credentials/schemas.ts +200 -164
  42. package/src/organization-model/README.md +10 -3
  43. package/src/organization-model/__tests__/defaults.test.ts +6 -0
  44. package/src/organization-model/__tests__/domains/resources.test.ts +188 -0
  45. package/src/organization-model/__tests__/domains/roles.test.ts +402 -347
  46. package/src/organization-model/__tests__/domains/systems.test.ts +193 -0
  47. package/src/organization-model/__tests__/knowledge.test.ts +39 -0
  48. package/src/organization-model/__tests__/prospecting-ssot.test.ts +7 -4
  49. package/src/organization-model/__tests__/resolve.test.ts +1 -1
  50. package/src/organization-model/defaults.ts +24 -3
  51. package/src/organization-model/domains/knowledge.ts +3 -2
  52. package/src/organization-model/domains/prospecting.ts +182 -25
  53. package/src/organization-model/domains/resources.ts +88 -0
  54. package/src/organization-model/domains/roles.ts +93 -55
  55. package/src/organization-model/domains/sales.ts +24 -3
  56. package/src/organization-model/domains/systems.ts +46 -0
  57. package/src/organization-model/icons.ts +1 -0
  58. package/src/organization-model/index.ts +2 -0
  59. package/src/organization-model/organization-model.mdx +33 -14
  60. package/src/organization-model/published.ts +52 -1
  61. package/src/organization-model/schema.ts +121 -0
  62. package/src/organization-model/types.ts +46 -1
  63. package/src/platform/api/types.ts +38 -35
  64. package/src/platform/constants/versions.ts +1 -1
  65. package/src/platform/registry/__tests__/resource-registry.test.ts +2051 -2005
  66. package/src/platform/registry/__tests__/validation.test.ts +1343 -1086
  67. package/src/platform/registry/index.ts +14 -0
  68. package/src/platform/registry/resource-registry.ts +40 -2
  69. package/src/platform/registry/serialization.ts +241 -202
  70. package/src/platform/registry/serialized-types.ts +1 -0
  71. package/src/platform/registry/types.ts +411 -361
  72. package/src/platform/registry/validation.ts +743 -513
  73. package/src/projects/api-schemas.ts +290 -267
  74. package/src/reference/_generated/contracts.md +501 -303
  75. package/src/reference/glossary.md +8 -3
  76. package/src/server.ts +2 -0
  77. package/src/supabase/database.types.ts +121 -0
@@ -0,0 +1,193 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import {
3
+ DEFAULT_ORGANIZATION_MODEL_SYSTEMS,
4
+ SystemEntrySchema,
5
+ SystemKindSchema,
6
+ SystemsDomainSchema,
7
+ SystemStatusSchema
8
+ } from '../../domains/systems'
9
+ import { resolveOrganizationModel } from '../../resolve'
10
+
11
+ const VALID_SYSTEM = {
12
+ id: 'sys.lead-gen',
13
+ title: 'Lead Generation Pipeline',
14
+ description: 'Coordinates prospecting, enrichment, qualification, and outreach preparation.',
15
+ kind: 'operational' as const,
16
+ status: 'active' as const
17
+ }
18
+
19
+ describe('SystemEntrySchema - positive parse', () => {
20
+ it('accepts a minimal tenant-defined system', () => {
21
+ const result = SystemEntrySchema.safeParse(VALID_SYSTEM)
22
+
23
+ expect(result.success).toBe(true)
24
+ if (result.success) {
25
+ expect(result.data.governedByKnowledge).toEqual([])
26
+ expect(result.data.drivesGoals).toEqual([])
27
+ }
28
+ })
29
+
30
+ it('accepts all optional governance references when present', () => {
31
+ const result = SystemEntrySchema.safeParse({
32
+ ...VALID_SYSTEM,
33
+ responsibleRoleId: 'role.sales-ops',
34
+ governedByKnowledge: ['knowledge.lead-gen-playbook'],
35
+ drivesGoals: ['goal.pipeline-coverage']
36
+ })
37
+
38
+ expect(result.success).toBe(true)
39
+ })
40
+
41
+ it('trims id, title, and description', () => {
42
+ const result = SystemEntrySchema.safeParse({
43
+ ...VALID_SYSTEM,
44
+ id: ' sys.crm ',
45
+ title: ' CRM ',
46
+ description: ' Owns relationship and deal pipeline operations. '
47
+ })
48
+
49
+ expect(result.success).toBe(true)
50
+ if (result.success) {
51
+ expect(result.data.id).toBe('sys.crm')
52
+ expect(result.data.title).toBe('CRM')
53
+ expect(result.data.description).toBe('Owns relationship and deal pipeline operations.')
54
+ }
55
+ })
56
+ })
57
+
58
+ describe('SystemEntrySchema - negative parse', () => {
59
+ it('rejects missing required fields', () => {
60
+ expect(SystemEntrySchema.safeParse({ id: 'sys.missing' }).success).toBe(false)
61
+ })
62
+
63
+ it('rejects an unknown system kind', () => {
64
+ expect(SystemEntrySchema.safeParse({ ...VALID_SYSTEM, kind: 'sales' }).success).toBe(false)
65
+ })
66
+
67
+ it('rejects an unknown system status', () => {
68
+ expect(SystemEntrySchema.safeParse({ ...VALID_SYSTEM, status: 'paused' }).success).toBe(false)
69
+ })
70
+
71
+ it('rejects IDs that do not match the OM model-id format', () => {
72
+ expect(SystemEntrySchema.safeParse({ ...VALID_SYSTEM, id: 'Sys Lead Gen' }).success).toBe(false)
73
+ })
74
+ })
75
+
76
+ describe('SystemsDomainSchema', () => {
77
+ it('accepts an empty systems array', () => {
78
+ expect(SystemsDomainSchema.safeParse({ systems: [] }).success).toBe(true)
79
+ })
80
+
81
+ it('defaults systems to an empty array when omitted', () => {
82
+ const result = SystemsDomainSchema.safeParse({})
83
+
84
+ expect(result.success).toBe(true)
85
+ if (result.success) {
86
+ expect(result.data).toEqual(DEFAULT_ORGANIZATION_MODEL_SYSTEMS)
87
+ }
88
+ })
89
+ })
90
+
91
+ describe('SystemKindSchema and SystemStatusSchema', () => {
92
+ it.each(['product', 'operational', 'platform', 'diagnostic'] as const)('accepts kind "%s"', (kind) => {
93
+ expect(SystemKindSchema.safeParse(kind).success).toBe(true)
94
+ })
95
+
96
+ it.each(['active', 'deprecated', 'archived'] as const)('accepts status "%s"', (status) => {
97
+ expect(SystemStatusSchema.safeParse(status).success).toBe(true)
98
+ })
99
+ })
100
+
101
+ describe('resolveOrganizationModel - systems domain integration', () => {
102
+ it('omitting systems resolves to the empty default catalog', () => {
103
+ const model = resolveOrganizationModel({})
104
+
105
+ expect(model.systems).toEqual(DEFAULT_ORGANIZATION_MODEL_SYSTEMS)
106
+ })
107
+
108
+ it('accepts valid responsible role, knowledge, and goal references', () => {
109
+ expect(() =>
110
+ resolveOrganizationModel({
111
+ roles: { roles: [{ id: 'role.sales-ops', title: 'Sales Ops' }] },
112
+ goals: {
113
+ objectives: [
114
+ {
115
+ id: 'goal.pipeline-coverage',
116
+ description: 'Improve sales pipeline coverage',
117
+ periodStart: '2026-01-01',
118
+ periodEnd: '2026-12-31'
119
+ }
120
+ ]
121
+ },
122
+ knowledge: {
123
+ nodes: [
124
+ {
125
+ id: 'knowledge.lead-gen-playbook',
126
+ kind: 'playbook',
127
+ title: 'Lead Gen Playbook',
128
+ summary: 'How lead generation is governed.',
129
+ body: '## Lead Gen',
130
+ updatedAt: '2026-05-08'
131
+ }
132
+ ]
133
+ },
134
+ systems: {
135
+ systems: [
136
+ {
137
+ ...VALID_SYSTEM,
138
+ responsibleRoleId: 'role.sales-ops',
139
+ governedByKnowledge: ['knowledge.lead-gen-playbook'],
140
+ drivesGoals: ['goal.pipeline-coverage']
141
+ }
142
+ ]
143
+ }
144
+ })
145
+ ).not.toThrow()
146
+ })
147
+
148
+ it('throws when system IDs are duplicated', () => {
149
+ expect(() =>
150
+ resolveOrganizationModel({
151
+ systems: {
152
+ systems: [
153
+ VALID_SYSTEM,
154
+ {
155
+ ...VALID_SYSTEM,
156
+ title: 'Duplicate System'
157
+ }
158
+ ]
159
+ }
160
+ })
161
+ ).toThrow(/System id \\"sys\.lead-gen\\" must be unique/)
162
+ })
163
+
164
+ it('throws when responsibleRoleId references an unknown role', () => {
165
+ expect(() =>
166
+ resolveOrganizationModel({
167
+ systems: {
168
+ systems: [{ ...VALID_SYSTEM, responsibleRoleId: 'role.missing' }]
169
+ }
170
+ })
171
+ ).toThrow(/unknown responsibleRoleId \\"role\.missing\\"/)
172
+ })
173
+
174
+ it('throws when governedByKnowledge references an unknown knowledge node', () => {
175
+ expect(() =>
176
+ resolveOrganizationModel({
177
+ systems: {
178
+ systems: [{ ...VALID_SYSTEM, governedByKnowledge: ['knowledge.missing'] }]
179
+ }
180
+ })
181
+ ).toThrow(/unknown knowledge node \\"knowledge\.missing\\"/)
182
+ })
183
+
184
+ it('throws when drivesGoals references an unknown goal', () => {
185
+ expect(() =>
186
+ resolveOrganizationModel({
187
+ systems: {
188
+ systems: [{ ...VALID_SYSTEM, drivesGoals: ['goal.missing'] }]
189
+ }
190
+ })
191
+ ).toThrow(/unknown goal \\"goal\.missing\\"/)
192
+ })
193
+ })
@@ -8,6 +8,7 @@ import {
8
8
  import { OrganizationGraphEdgeKindSchema } from '../graph/schema'
9
9
  import { buildOrganizationGraph } from '../graph/build'
10
10
  import { DEFAULT_ORGANIZATION_MODEL, DEFAULT_ORGANIZATION_MODEL_KNOWLEDGE } from '../defaults'
11
+ import { resolveOrganizationModel } from '../resolve'
11
12
  import type { OrganizationModel } from '../types'
12
13
 
13
14
  // ---------------------------------------------------------------------------
@@ -74,6 +75,15 @@ describe('OrgKnowledgeNodeSchema', () => {
74
75
  expect(node.ownerIds).toEqual([])
75
76
  })
76
77
 
78
+ it('accepts role ids as knowledge owners', () => {
79
+ const node = OrgKnowledgeNodeSchema.parse({
80
+ ...makeNode('strategy'),
81
+ ownerIds: ['role.ops-lead', 'role.ceo']
82
+ })
83
+
84
+ expect(node.ownerIds).toEqual(['role.ops-lead', 'role.ceo'])
85
+ })
86
+
77
87
  it('accepts an optional semantic icon token', () => {
78
88
  const node = OrgKnowledgeNodeSchema.parse({
79
89
  ...makeNode('playbook'),
@@ -93,6 +103,35 @@ describe('OrgKnowledgeNodeSchema', () => {
93
103
  })
94
104
  })
95
105
 
106
+ describe('resolveOrganizationModel with knowledge owner roles', () => {
107
+ const node = {
108
+ id: 'knowledge.test-owner',
109
+ kind: 'playbook' as const,
110
+ title: 'Owned Knowledge',
111
+ summary: 'A knowledge node with role ownership.',
112
+ body: '## Body\n\nContent.',
113
+ ownerIds: ['role.ops-lead'],
114
+ updatedAt: '2026-05-08'
115
+ }
116
+
117
+ it('passes when ownerIds reference declared role ids', () => {
118
+ expect(() =>
119
+ resolveOrganizationModel({
120
+ roles: { roles: [{ id: 'role.ops-lead', title: 'Ops Lead' }] },
121
+ knowledge: { nodes: [node] }
122
+ })
123
+ ).not.toThrow()
124
+ })
125
+
126
+ it('throws when ownerIds reference unknown role ids', () => {
127
+ expect(() =>
128
+ resolveOrganizationModel({
129
+ knowledge: { nodes: [node] }
130
+ })
131
+ ).toThrow(/unknown owner role/)
132
+ })
133
+ })
134
+
96
135
  // ---------------------------------------------------------------------------
97
136
  // OrgKnowledgeKindSchema — enum shape
98
137
  // ---------------------------------------------------------------------------
@@ -5,6 +5,7 @@ import { CAPABILITY_REGISTRY, DEFAULT_ORGANIZATION_MODEL_PROSPECTING, PROSPECTIN
5
5
  const EXPECTED_CAPABILITY_RESOURCE_BY_ID: Record<string, string> = {
6
6
  'lead-gen.company.source': 'lgn-import-workflow',
7
7
  'lead-gen.company.apollo-import': 'lgn-01c-apollo-import-workflow',
8
+ 'lead-gen.company.apify-crawl': 'lgn-02a-apify-website-crawl-workflow',
8
9
  'lead-gen.contact.discover': 'lgn-04-email-discovery-workflow',
9
10
  'lead-gen.contact.verify-email': 'lgn-05-email-verification-workflow',
10
11
  'lead-gen.company.website-extract': 'lgn-02-website-extract-workflow',
@@ -24,7 +25,9 @@ describe('prospecting organization-model SSOT', () => {
24
25
  label: 'Decision-makers found',
25
26
  description: 'Decision-maker contacts discovered and attached to a qualified company.',
26
27
  order: 6,
27
- entity: 'company'
28
+ entity: 'company',
29
+ recordEntity: 'contact',
30
+ recordStageKey: 'discovered'
28
31
  })
29
32
  })
30
33
 
@@ -63,12 +66,12 @@ describe('prospecting organization-model SSOT', () => {
63
66
 
64
67
  it('derives prospecting lifecycle stages from the stage catalog by entity', () => {
65
68
  const expectedCompanyStages = Object.values(LEAD_GEN_STAGE_CATALOG)
66
- .filter((stage) => stage.entity === 'company')
69
+ .filter((stage) => stage.entity === 'company' || stage.additionalEntities?.includes('company'))
67
70
  .sort((a, b) => a.order - b.order)
68
71
  .map(({ key, label, order }) => ({ id: key, label, order }))
69
72
 
70
73
  const expectedContactStages = Object.values(LEAD_GEN_STAGE_CATALOG)
71
- .filter((stage) => stage.entity === 'contact')
74
+ .filter((stage) => stage.entity === 'contact' || stage.additionalEntities?.includes('contact'))
72
75
  .sort((a, b) => a.order - b.order)
73
76
  .map(({ key, label, order }) => ({ id: key, label, order }))
74
77
 
@@ -84,7 +87,7 @@ describe('prospecting organization-model SSOT', () => {
84
87
  })
85
88
 
86
89
  it('covers all known lead-gen capability registry entries', () => {
87
- expect(CAPABILITY_REGISTRY).toHaveLength(12)
90
+ expect(CAPABILITY_REGISTRY).toHaveLength(13)
88
91
  const actualResourceById = Object.fromEntries(CAPABILITY_REGISTRY.map((c) => [c.id, c.resourceId]))
89
92
  expect(actualResourceById).toEqual(EXPECTED_CAPABILITY_RESOURCE_BY_ID)
90
93
  })
@@ -7,7 +7,7 @@ describe('organization-model resolve', () => {
7
7
 
8
8
  expect(model.version).toBe(1)
9
9
  expect(model.features.find((feature) => feature.id === 'dashboard')?.path).toBe('/')
10
- expect(model.features.find((feature) => feature.id === 'sales')?.path).toBeUndefined()
10
+ expect(model.features.find((feature) => feature.id === 'sales')?.path).toBe('/sales')
11
11
  expect(model.features.find((feature) => feature.id === 'sales.crm')?.path).toBe('/crm')
12
12
  expect(model.features.find((feature) => feature.id === 'admin')?.requiresAdmin).toBe(true)
13
13
  expect(model.features.find((feature) => feature.id === 'archive')?.devOnly).toBe(true)
@@ -20,6 +20,8 @@ import { DEFAULT_ORGANIZATION_MODEL_PROSPECTING } from './domains/prospecting'
20
20
  import { DEFAULT_ORGANIZATION_MODEL_OPERATIONS } from './domains/operations'
21
21
  import { DEFAULT_ORGANIZATION_MODEL_ROLES } from './domains/roles'
22
22
  import { DEFAULT_ORGANIZATION_MODEL_GOALS } from './domains/goals'
23
+ import { DEFAULT_ORGANIZATION_MODEL_SYSTEMS } from './domains/systems'
24
+ import { DEFAULT_ORGANIZATION_MODEL_RESOURCES } from './domains/resources'
23
25
  import { DEFAULT_ORGANIZATION_MODEL_STATUSES } from './domains/statuses'
24
26
  import type { KnowledgeDomain } from './domains/knowledge'
25
27
 
@@ -61,6 +63,15 @@ export const DEFAULT_ORGANIZATION_MODEL: OrganizationModel = {
61
63
  color: 'green',
62
64
  icon: 'feature.finance'
63
65
  },
66
+ {
67
+ id: 'business',
68
+ label: 'Business',
69
+ description: 'Revenue, client relationships, and project delivery',
70
+ enabled: true,
71
+ color: 'blue',
72
+ icon: 'feature.business',
73
+ uiPosition: 'sidebar-primary'
74
+ },
64
75
  {
65
76
  id: 'sales',
66
77
  label: 'Sales',
@@ -68,7 +79,7 @@ export const DEFAULT_ORGANIZATION_MODEL: OrganizationModel = {
68
79
  enabled: true,
69
80
  color: 'blue',
70
81
  icon: 'feature.sales',
71
- uiPosition: 'sidebar-primary'
82
+ path: '/sales'
72
83
  },
73
84
  {
74
85
  id: 'sales.crm',
@@ -95,8 +106,16 @@ export const DEFAULT_ORGANIZATION_MODEL: OrganizationModel = {
95
106
  enabled: true,
96
107
  color: 'orange',
97
108
  icon: 'feature.projects',
98
- path: '/projects',
99
- uiPosition: 'sidebar-primary'
109
+ path: '/projects'
110
+ },
111
+ {
112
+ id: 'clients',
113
+ label: 'Clients',
114
+ description: 'Client relationships, accounts, and business context',
115
+ enabled: true,
116
+ color: 'orange',
117
+ icon: 'feature.projects',
118
+ path: '/business/clients'
100
119
  },
101
120
  {
102
121
  id: 'operations',
@@ -341,6 +360,8 @@ export const DEFAULT_ORGANIZATION_MODEL: OrganizationModel = {
341
360
  offerings: DEFAULT_ORGANIZATION_MODEL_OFFERINGS,
342
361
  roles: DEFAULT_ORGANIZATION_MODEL_ROLES,
343
362
  goals: DEFAULT_ORGANIZATION_MODEL_GOALS,
363
+ systems: DEFAULT_ORGANIZATION_MODEL_SYSTEMS,
364
+ resources: DEFAULT_ORGANIZATION_MODEL_RESOURCES,
344
365
  statuses: DEFAULT_ORGANIZATION_MODEL_STATUSES,
345
366
  operations: DEFAULT_ORGANIZATION_MODEL_OPERATIONS,
346
367
  knowledge: DEFAULT_ORGANIZATION_MODEL_KNOWLEDGE
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod'
2
2
  import { IconNameSchema, ModelIdSchema } from './shared'
3
3
  import { NodeIdStringSchema } from './features'
4
+ import { RoleIdSchema } from './roles'
4
5
 
5
6
  // ---------------------------------------------------------------------------
6
7
  // KnowledgeLink — a typed graph link pointing to another OM node.
@@ -40,8 +41,8 @@ export const OrgKnowledgeNodeSchema = z.object({
40
41
  skills: z.array(KnowledgeSkillBindingSchema).optional(),
41
42
  /** Domain key used to derive fast graph->skill registries. */
42
43
  domain: KnowledgeDomainBindingSchema.optional(),
43
- /** Identifiers of the roles or members who own this knowledge node. */
44
- ownerIds: z.array(ModelIdSchema).default([]),
44
+ /** Role identifiers that own this knowledge node. */
45
+ ownerIds: z.array(RoleIdSchema).default([]),
45
46
  /** ISO date string (YYYY-MM-DD or full ISO 8601) of last meaningful update. */
46
47
  updatedAt: z.string().trim().min(1).max(50)
47
48
  })