@elevasis/core 0.18.0 → 0.19.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.
- package/dist/index.d.ts +82 -1
- package/dist/index.js +291 -171
- package/dist/knowledge/index.d.ts +43 -0
- package/dist/organization-model/index.d.ts +82 -1
- package/dist/organization-model/index.js +291 -171
- package/dist/test-utils/index.d.ts +41 -12
- package/dist/test-utils/index.js +291 -171
- package/package.json +2 -1
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +78 -65
- package/src/auth/multi-tenancy/organizations/__tests__/api-schemas.test.ts +194 -0
- package/src/auth/multi-tenancy/organizations/api-schemas.ts +136 -128
- package/src/business/acquisition/api-schemas.test.ts +100 -2
- package/src/business/acquisition/api-schemas.ts +81 -43
- package/src/business/acquisition/build-templates.test.ts +212 -0
- package/src/business/acquisition/types.ts +21 -38
- package/src/execution/engine/index.ts +436 -434
- package/src/execution/engine/tools/integration/server/adapters/google-calendar/google-calendar-adapter.ts +428 -0
- package/src/execution/engine/tools/integration/server/adapters/google-calendar/index.ts +2 -0
- package/src/execution/engine/tools/lead-service-types.ts +51 -9
- package/src/execution/engine/tools/platform/acquisition/company-tools.ts +7 -6
- package/src/execution/engine/tools/platform/acquisition/contact-tools.ts +6 -5
- package/src/execution/engine/tools/platform/acquisition/types.ts +20 -9
- package/src/execution/engine/tools/registry.ts +700 -698
- package/src/execution/engine/tools/tool-maps.ts +10 -0
- package/src/execution/external/__tests__/api-schemas.test.ts +127 -0
- package/src/integrations/oauth/__tests__/provider-registry.test.ts +7 -6
- package/src/integrations/oauth/provider-registry.ts +74 -61
- package/src/integrations/oauth/server/credentials.ts +43 -39
- package/src/knowledge/__tests__/queries.test.ts +89 -0
- package/src/organization-model/__tests__/icons.test.ts +61 -0
- package/src/organization-model/__tests__/knowledge.test.ts +118 -1
- package/src/organization-model/__tests__/prospecting-ssot.test.ts +94 -0
- package/src/organization-model/defaults.ts +8 -0
- package/src/organization-model/domains/knowledge.ts +9 -0
- package/src/organization-model/domains/prospecting.ts +272 -226
- package/src/organization-model/domains/sales.ts +32 -25
- package/src/organization-model/icons.ts +3 -0
- package/src/organization-model/types.ts +9 -1
- package/src/platform/constants/versions.ts +1 -1
- package/src/platform/utils/__tests__/validation.test.ts +1084 -1083
- package/src/platform/utils/validation.ts +425 -425
- package/src/reference/_generated/contracts.md +78 -65
- package/src/server.ts +6 -0
- package/src/supabase/database.types.ts +6 -12
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
OrgKnowledgeNodeSchema,
|
|
4
|
+
OrgKnowledgeKindSchema,
|
|
5
|
+
KnowledgeDomainSchema,
|
|
6
|
+
KnowledgeLinkSchema
|
|
7
|
+
} from '../domains/knowledge'
|
|
3
8
|
import { OrganizationGraphEdgeKindSchema } from '../graph/schema'
|
|
4
9
|
import { buildOrganizationGraph } from '../graph/build'
|
|
5
10
|
import { DEFAULT_ORGANIZATION_MODEL, DEFAULT_ORGANIZATION_MODEL_KNOWLEDGE } from '../defaults'
|
|
@@ -212,3 +217,115 @@ describe('buildOrganizationGraph with knowledge', () => {
|
|
|
212
217
|
expect(defaultKnowledgeNodes).toHaveLength(0)
|
|
213
218
|
})
|
|
214
219
|
})
|
|
220
|
+
|
|
221
|
+
// ---------------------------------------------------------------------------
|
|
222
|
+
// KnowledgeLinkSchema — nodeId format validation
|
|
223
|
+
// ---------------------------------------------------------------------------
|
|
224
|
+
|
|
225
|
+
describe('KnowledgeLinkSchema', () => {
|
|
226
|
+
it('accepts a valid kind:dotted-path nodeId', () => {
|
|
227
|
+
expect(() => KnowledgeLinkSchema.parse({ nodeId: 'feature:sales.crm' })).not.toThrow()
|
|
228
|
+
expect(() => KnowledgeLinkSchema.parse({ nodeId: 'feature:sales.lead-gen' })).not.toThrow()
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
it('accepts resource: and knowledge: prefixed node IDs', () => {
|
|
232
|
+
expect(() => KnowledgeLinkSchema.parse({ nodeId: 'resource:my-resource' })).not.toThrow()
|
|
233
|
+
expect(() => KnowledgeLinkSchema.parse({ nodeId: 'knowledge:knowledge.test-doc' })).not.toThrow()
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
it('rejects a bare dotted ID with no kind: prefix', () => {
|
|
237
|
+
// NodeIdStringSchema requires kind:dotted-path format
|
|
238
|
+
expect(KnowledgeLinkSchema.safeParse({ nodeId: 'sales.crm' }).success).toBe(false)
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
it('rejects an empty nodeId', () => {
|
|
242
|
+
expect(KnowledgeLinkSchema.safeParse({ nodeId: '' }).success).toBe(false)
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
it('rejects a nodeId with uppercase characters', () => {
|
|
246
|
+
// NodeIdStringSchema regex requires lowercase
|
|
247
|
+
expect(KnowledgeLinkSchema.safeParse({ nodeId: 'Feature:Sales.CRM' }).success).toBe(false)
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
it('rejects a link missing the nodeId field', () => {
|
|
251
|
+
expect(KnowledgeLinkSchema.safeParse({}).success).toBe(false)
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
it('rejects a plain number as nodeId', () => {
|
|
255
|
+
expect(KnowledgeLinkSchema.safeParse({ nodeId: 123 }).success).toBe(false)
|
|
256
|
+
})
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
// ---------------------------------------------------------------------------
|
|
260
|
+
// OrgKnowledgeNodeSchema — field length constraints
|
|
261
|
+
// ---------------------------------------------------------------------------
|
|
262
|
+
|
|
263
|
+
describe('OrgKnowledgeNodeSchema length constraints', () => {
|
|
264
|
+
const BASE = {
|
|
265
|
+
id: 'knowledge.test-node',
|
|
266
|
+
kind: 'playbook',
|
|
267
|
+
title: 'Test Node',
|
|
268
|
+
summary: 'A valid summary.',
|
|
269
|
+
body: 'Body content.',
|
|
270
|
+
links: [],
|
|
271
|
+
ownerIds: [],
|
|
272
|
+
updatedAt: '2026-05-01'
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
it('rejects title exceeding 200 characters', () => {
|
|
276
|
+
const result = OrgKnowledgeNodeSchema.safeParse({ ...BASE, title: 'x'.repeat(201) })
|
|
277
|
+
expect(result.success).toBe(false)
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
it('accepts title exactly at 200 characters', () => {
|
|
281
|
+
const result = OrgKnowledgeNodeSchema.safeParse({ ...BASE, title: 'x'.repeat(200) })
|
|
282
|
+
expect(result.success).toBe(true)
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
it('rejects summary exceeding 1000 characters', () => {
|
|
286
|
+
const result = OrgKnowledgeNodeSchema.safeParse({ ...BASE, summary: 'x'.repeat(1001) })
|
|
287
|
+
expect(result.success).toBe(false)
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
it('accepts summary exactly at 1000 characters', () => {
|
|
291
|
+
const result = OrgKnowledgeNodeSchema.safeParse({ ...BASE, summary: 'x'.repeat(1000) })
|
|
292
|
+
expect(result.success).toBe(true)
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
it('rejects empty title', () => {
|
|
296
|
+
const result = OrgKnowledgeNodeSchema.safeParse({ ...BASE, title: '' })
|
|
297
|
+
expect(result.success).toBe(false)
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
it('rejects empty summary', () => {
|
|
301
|
+
const result = OrgKnowledgeNodeSchema.safeParse({ ...BASE, summary: '' })
|
|
302
|
+
expect(result.success).toBe(false)
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
it('rejects empty body', () => {
|
|
306
|
+
const result = OrgKnowledgeNodeSchema.safeParse({ ...BASE, body: '' })
|
|
307
|
+
expect(result.success).toBe(false)
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
it('rejects id with uppercase characters', () => {
|
|
311
|
+
// ModelIdSchema requires lowercase kebab/dot/underscore separated IDs
|
|
312
|
+
const result = OrgKnowledgeNodeSchema.safeParse({ ...BASE, id: 'Knowledge.Test' })
|
|
313
|
+
expect(result.success).toBe(false)
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
it('rejects id exceeding 100 characters', () => {
|
|
317
|
+
const result = OrgKnowledgeNodeSchema.safeParse({ ...BASE, id: 'knowledge.' + 'a'.repeat(95) })
|
|
318
|
+
expect(result.success).toBe(false)
|
|
319
|
+
})
|
|
320
|
+
|
|
321
|
+
it('accepts a valid custom icon token on a node', () => {
|
|
322
|
+
const result = OrgKnowledgeNodeSchema.safeParse({ ...BASE, icon: 'custom.my-icon' })
|
|
323
|
+
expect(result.success).toBe(true)
|
|
324
|
+
})
|
|
325
|
+
|
|
326
|
+
it('trims whitespace from title and summary', () => {
|
|
327
|
+
const node = OrgKnowledgeNodeSchema.parse({ ...BASE, title: ' Trimmed ', summary: ' Summary. ' })
|
|
328
|
+
expect(node.title).toBe('Trimmed')
|
|
329
|
+
expect(node.summary).toBe('Summary.')
|
|
330
|
+
})
|
|
331
|
+
})
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { LEAD_GEN_STAGE_CATALOG } from '../domains/sales'
|
|
3
|
+
import {
|
|
4
|
+
CAPABILITY_REGISTRY,
|
|
5
|
+
DEFAULT_ORGANIZATION_MODEL_PROSPECTING,
|
|
6
|
+
PROSPECTING_STEPS
|
|
7
|
+
} from '../domains/prospecting'
|
|
8
|
+
|
|
9
|
+
const EXPECTED_CAPABILITY_REGISTRY = {
|
|
10
|
+
'lead-gen.company.source': 'lgn-import-workflow',
|
|
11
|
+
'lead-gen.company.apollo-import': 'lgn-01c-apollo-import-workflow',
|
|
12
|
+
'lead-gen.contact.discover': 'lgn-04-email-discovery-workflow',
|
|
13
|
+
'lead-gen.contact.verify-email': 'lgn-05-email-verification-workflow',
|
|
14
|
+
'lead-gen.company.website-extract': 'lgn-02-website-extract-workflow',
|
|
15
|
+
'lead-gen.company.qualify': 'lgn-03-company-qualification-workflow',
|
|
16
|
+
'lead-gen.company.dtc-subscription-qualify': 'lgn-03b-dtc-subscription-score-workflow',
|
|
17
|
+
'lead-gen.contact.apollo-decision-maker-enrich': 'lgn-04b-apollo-decision-maker-enrich-workflow',
|
|
18
|
+
'lead-gen.contact.personalize': 'ist-personalization-workflow',
|
|
19
|
+
'lead-gen.review.outreach-ready': 'ist-upload-contacts-workflow',
|
|
20
|
+
'lead-gen.export.list': 'lgn-06-export-list-workflow',
|
|
21
|
+
'lead-gen.company.cleanup': 'lgn-company-cleanup-workflow'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
describe('prospecting organization-model SSOT', () => {
|
|
25
|
+
it('catalogs the DTC decision-maker enrichment stage', () => {
|
|
26
|
+
expect(LEAD_GEN_STAGE_CATALOG['decision-makers-enriched']).toEqual({
|
|
27
|
+
key: 'decision-makers-enriched',
|
|
28
|
+
label: 'Decision-makers found',
|
|
29
|
+
description: 'Decision-maker contacts discovered and attached to a qualified company.',
|
|
30
|
+
order: 6,
|
|
31
|
+
entity: 'company'
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('scopes prospecting steps per build template', () => {
|
|
36
|
+
expect(PROSPECTING_STEPS.localServices.findContacts).toMatchObject({
|
|
37
|
+
id: 'find-contacts',
|
|
38
|
+
primaryEntity: 'contact',
|
|
39
|
+
stageKey: 'discovered'
|
|
40
|
+
})
|
|
41
|
+
expect(PROSPECTING_STEPS.dtcApolloClickup.enrichDecisionMakers).toMatchObject({
|
|
42
|
+
id: 'enrich-decision-makers',
|
|
43
|
+
primaryEntity: 'company',
|
|
44
|
+
stageKey: 'decision-makers-enriched'
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('uses catalog stage keys for every prospecting step', () => {
|
|
49
|
+
const catalogKeys = new Set(Object.keys(LEAD_GEN_STAGE_CATALOG))
|
|
50
|
+
|
|
51
|
+
for (const templateSteps of Object.values(PROSPECTING_STEPS)) {
|
|
52
|
+
for (const step of Object.values(templateSteps)) {
|
|
53
|
+
expect(catalogKeys.has(step.stageKey), step.id).toBe(true)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('uses registered capabilities for every prospecting step', () => {
|
|
59
|
+
const capabilityKeys = new Set(Object.keys(CAPABILITY_REGISTRY))
|
|
60
|
+
|
|
61
|
+
for (const templateSteps of Object.values(PROSPECTING_STEPS)) {
|
|
62
|
+
for (const step of Object.values(templateSteps)) {
|
|
63
|
+
expect(capabilityKeys.has(step.capabilityKey), step.id).toBe(true)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('derives prospecting lifecycle stages from the stage catalog by entity', () => {
|
|
69
|
+
const expectedCompanyStages = Object.values(LEAD_GEN_STAGE_CATALOG)
|
|
70
|
+
.filter((stage) => stage.entity === 'company')
|
|
71
|
+
.sort((a, b) => a.order - b.order)
|
|
72
|
+
.map(({ key, label, order }) => ({ id: key, label, order }))
|
|
73
|
+
|
|
74
|
+
const expectedContactStages = Object.values(LEAD_GEN_STAGE_CATALOG)
|
|
75
|
+
.filter((stage) => stage.entity === 'contact')
|
|
76
|
+
.sort((a, b) => a.order - b.order)
|
|
77
|
+
.map(({ key, label, order }) => ({ id: key, label, order }))
|
|
78
|
+
|
|
79
|
+
expect(DEFAULT_ORGANIZATION_MODEL_PROSPECTING.companyStages).toEqual(expectedCompanyStages)
|
|
80
|
+
expect(DEFAULT_ORGANIZATION_MODEL_PROSPECTING.contactStages).toEqual(expectedContactStages)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('build templates reference PROSPECTING_STEPS entries', () => {
|
|
84
|
+
const [localServices, dtcApolloClickup] = DEFAULT_ORGANIZATION_MODEL_PROSPECTING.buildTemplates
|
|
85
|
+
|
|
86
|
+
expect(localServices?.steps).toEqual(Object.values(PROSPECTING_STEPS.localServices))
|
|
87
|
+
expect(dtcApolloClickup?.steps).toEqual(Object.values(PROSPECTING_STEPS.dtcApolloClickup))
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('covers all known lead-gen capability registry entries', () => {
|
|
91
|
+
expect(CAPABILITY_REGISTRY).toEqual(EXPECTED_CAPABILITY_REGISTRY)
|
|
92
|
+
expect(Object.keys(CAPABILITY_REGISTRY)).toHaveLength(12)
|
|
93
|
+
})
|
|
94
|
+
})
|
|
@@ -150,6 +150,14 @@ export const DEFAULT_ORGANIZATION_MODEL: OrganizationModel = {
|
|
|
150
150
|
enabled: true,
|
|
151
151
|
uiPosition: 'sidebar-primary'
|
|
152
152
|
},
|
|
153
|
+
{
|
|
154
|
+
id: 'monitoring.calendar',
|
|
155
|
+
label: 'Calendar',
|
|
156
|
+
description: 'Google Calendar events and agenda views',
|
|
157
|
+
enabled: true,
|
|
158
|
+
path: '/monitoring/calendar',
|
|
159
|
+
icon: 'feature.calendar'
|
|
160
|
+
},
|
|
153
161
|
{
|
|
154
162
|
id: 'monitoring.activity-log',
|
|
155
163
|
label: 'Activity Log',
|
|
@@ -12,6 +12,9 @@ export const KnowledgeLinkSchema = z.object({
|
|
|
12
12
|
nodeId: NodeIdStringSchema
|
|
13
13
|
})
|
|
14
14
|
|
|
15
|
+
export const KnowledgeSkillBindingSchema = z.string().trim().min(1).max(120)
|
|
16
|
+
export const KnowledgeDomainBindingSchema = z.string().trim().min(1).max(80)
|
|
17
|
+
|
|
15
18
|
// ---------------------------------------------------------------------------
|
|
16
19
|
// OrgKnowledgeNode — single schema, `kind` discriminator drives presentation.
|
|
17
20
|
// Phase 1: body is raw MDX string (lossless migration from operations/ docs).
|
|
@@ -33,6 +36,10 @@ export const OrgKnowledgeNodeSchema = z.object({
|
|
|
33
36
|
* Each link emits a `governs` edge: knowledge-node -> target node.
|
|
34
37
|
*/
|
|
35
38
|
links: z.array(KnowledgeLinkSchema).default([]),
|
|
39
|
+
/** Operator skill or command bindings relevant to this node. */
|
|
40
|
+
skills: z.array(KnowledgeSkillBindingSchema).optional(),
|
|
41
|
+
/** Domain key used to derive fast graph->skill registries. */
|
|
42
|
+
domain: KnowledgeDomainBindingSchema.optional(),
|
|
36
43
|
/** Identifiers of the roles or members who own this knowledge node. */
|
|
37
44
|
ownerIds: z.array(ModelIdSchema).default([]),
|
|
38
45
|
/** ISO date string (YYYY-MM-DD or full ISO 8601) of last meaningful update. */
|
|
@@ -50,4 +57,6 @@ export const KnowledgeDomainSchema = z.object({
|
|
|
50
57
|
export type OrgKnowledgeNode = z.infer<typeof OrgKnowledgeNodeSchema>
|
|
51
58
|
export type OrgKnowledgeKind = z.infer<typeof OrgKnowledgeKindSchema>
|
|
52
59
|
export type KnowledgeLink = z.infer<typeof KnowledgeLinkSchema>
|
|
60
|
+
export type KnowledgeSkillBinding = z.infer<typeof KnowledgeSkillBindingSchema>
|
|
61
|
+
export type KnowledgeDomainBinding = z.infer<typeof KnowledgeDomainBindingSchema>
|
|
53
62
|
export type KnowledgeDomain = z.infer<typeof KnowledgeDomainSchema>
|