@elevasis/core 0.15.0 → 0.16.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 (88) hide show
  1. package/dist/index.d.ts +1718 -19
  2. package/dist/index.js +369 -25
  3. package/dist/organization-model/index.d.ts +1718 -19
  4. package/dist/organization-model/index.js +369 -25
  5. package/dist/test-utils/index.d.ts +1108 -371
  6. package/dist/test-utils/index.js +357 -17
  7. package/package.json +5 -1
  8. package/src/__tests__/publish.test.ts +14 -13
  9. package/src/__tests__/template-core-compatibility.test.ts +4 -4
  10. package/src/_gen/__tests__/__snapshots__/contracts.md.snap +1109 -882
  11. package/src/auth/multi-tenancy/index.ts +3 -0
  12. package/src/auth/multi-tenancy/theme-presets.ts +45 -0
  13. package/src/auth/multi-tenancy/types.ts +57 -83
  14. package/src/auth/multi-tenancy/users/api-schemas.ts +165 -194
  15. package/src/business/acquisition/activity-events.ts +13 -4
  16. package/src/business/acquisition/api-schemas.test.ts +315 -4
  17. package/src/business/acquisition/api-schemas.ts +122 -8
  18. package/src/business/acquisition/build-templates.ts +44 -0
  19. package/src/business/acquisition/crm-next-action.test.ts +262 -0
  20. package/src/business/acquisition/crm-next-action.ts +220 -0
  21. package/src/business/acquisition/crm-priority.test.ts +216 -0
  22. package/src/business/acquisition/crm-priority.ts +349 -0
  23. package/src/business/acquisition/crm-state-actions.test.ts +151 -160
  24. package/src/business/acquisition/deal-ownership.test.ts +351 -0
  25. package/src/business/acquisition/deal-ownership.ts +120 -0
  26. package/src/business/acquisition/derive-actions.test.ts +101 -37
  27. package/src/business/acquisition/derive-actions.ts +102 -87
  28. package/src/business/acquisition/index.ts +10 -0
  29. package/src/business/acquisition/types.ts +400 -366
  30. package/src/business/crm/api-schemas.ts +40 -0
  31. package/src/business/crm/index.ts +1 -0
  32. package/src/business/deals/api-schemas.ts +79 -0
  33. package/src/business/deals/index.ts +1 -0
  34. package/src/business/projects/types.ts +124 -88
  35. package/src/execution/core/runner-types.ts +61 -80
  36. package/src/execution/engine/index.ts +4 -3
  37. package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-tools.ts +105 -104
  38. package/src/execution/engine/tools/integration/server/adapters/instantly/instantly-tools.ts +1474 -1473
  39. package/src/execution/engine/tools/integration/server/adapters/millionverifier/millionverifier-tools.ts +103 -102
  40. package/src/execution/engine/tools/integration/server/adapters/signature-api/signature-api-tools.ts +182 -179
  41. package/src/execution/engine/tools/integration/server/adapters/stripe/stripe-tools.ts +310 -309
  42. package/src/execution/engine/tools/integration/tool.ts +255 -253
  43. package/src/execution/engine/tools/lead-service-types.ts +939 -924
  44. package/src/execution/engine/tools/messages.ts +43 -0
  45. package/src/execution/engine/tools/platform/acquisition/list-tools.ts +6 -5
  46. package/src/execution/engine/tools/platform/acquisition/types.ts +5 -2
  47. package/src/execution/engine/tools/platform/email/types.ts +97 -96
  48. package/src/execution/engine/tools/registry.ts +4 -3
  49. package/src/execution/engine/tools/tool-maps.ts +3 -1
  50. package/src/execution/engine/tools/types.ts +234 -233
  51. package/src/execution/engine/workflow/types.ts +195 -193
  52. package/src/execution/external/api-schemas.ts +40 -0
  53. package/src/execution/external/index.ts +1 -0
  54. package/src/knowledge/README.md +32 -0
  55. package/src/knowledge/__tests__/queries.test.ts +504 -0
  56. package/src/knowledge/format.ts +99 -0
  57. package/src/knowledge/index.ts +5 -0
  58. package/src/knowledge/queries.ts +256 -0
  59. package/src/organization-model/__tests__/defaults.test.ts +172 -172
  60. package/src/organization-model/__tests__/foundation.test.ts +7 -7
  61. package/src/organization-model/__tests__/icons.test.ts +27 -0
  62. package/src/organization-model/__tests__/knowledge.test.ts +214 -0
  63. package/src/organization-model/contracts.ts +17 -15
  64. package/src/organization-model/defaults.ts +74 -19
  65. package/src/organization-model/domains/knowledge.ts +53 -0
  66. package/src/organization-model/domains/navigation.ts +416 -399
  67. package/src/organization-model/domains/prospecting.ts +204 -1
  68. package/src/organization-model/domains/sales.test.ts +29 -0
  69. package/src/organization-model/domains/sales.ts +102 -0
  70. package/src/organization-model/domains/shared.ts +6 -5
  71. package/src/organization-model/foundation.ts +10 -6
  72. package/src/organization-model/graph/build.ts +209 -182
  73. package/src/organization-model/graph/schema.ts +37 -34
  74. package/src/organization-model/graph/types.ts +47 -31
  75. package/src/organization-model/icons.ts +81 -0
  76. package/src/organization-model/index.ts +8 -3
  77. package/src/organization-model/organization-model.mdx +1 -1
  78. package/src/organization-model/published.ts +103 -86
  79. package/src/organization-model/schema.ts +90 -85
  80. package/src/organization-model/types.ts +42 -35
  81. package/src/platform/constants/versions.ts +1 -1
  82. package/src/platform/index.ts +23 -27
  83. package/src/platform/registry/index.ts +0 -4
  84. package/src/platform/registry/resource-registry.ts +0 -77
  85. package/src/platform/registry/serialized-types.ts +148 -219
  86. package/src/platform/registry/stats-types.ts +60 -60
  87. package/src/reference/_generated/contracts.md +829 -595
  88. package/src/supabase/database.types.ts +2978 -2958
@@ -6,13 +6,35 @@ export const ProspectingLifecycleStageSchema = DisplayMetadataSchema.extend({
6
6
  order: z.number().int().min(0)
7
7
  })
8
8
 
9
+ export const ProspectingBuildTemplateStepSchema = DisplayMetadataSchema.extend({
10
+ id: ModelIdSchema,
11
+ primaryEntity: z.enum(['company', 'contact']),
12
+ outputs: z.array(z.enum(['company', 'contact', 'export'])).min(1),
13
+ stageKey: ModelIdSchema,
14
+ dependsOn: z.array(ModelIdSchema).optional(),
15
+ dependencyMode: z.literal('per-record-eligibility'),
16
+ capabilityKey: ModelIdSchema,
17
+ defaultBatchSize: z.number().int().positive(),
18
+ maxBatchSize: z.number().int().positive()
19
+ }).refine((step) => step.defaultBatchSize <= step.maxBatchSize, {
20
+ message: 'defaultBatchSize must be less than or equal to maxBatchSize',
21
+ path: ['defaultBatchSize']
22
+ })
23
+
24
+ export const ProspectingBuildTemplateSchema = DisplayMetadataSchema.extend({
25
+ id: ModelIdSchema,
26
+ steps: z.array(ProspectingBuildTemplateStepSchema).min(1)
27
+ })
28
+
9
29
  export const OrganizationModelProspectingSchema = z.object({
10
30
  listEntityId: ModelIdSchema,
11
31
  companyEntityId: ModelIdSchema,
12
32
  contactEntityId: ModelIdSchema,
13
33
  description: DescriptionSchema.optional(),
14
34
  companyStages: z.array(ProspectingLifecycleStageSchema).min(1),
15
- contactStages: z.array(ProspectingLifecycleStageSchema).min(1)
35
+ contactStages: z.array(ProspectingLifecycleStageSchema).min(1),
36
+ defaultBuildTemplateId: ModelIdSchema,
37
+ buildTemplates: z.array(ProspectingBuildTemplateSchema).min(1)
16
38
  })
17
39
 
18
40
  export const DEFAULT_ORGANIZATION_MODEL_PROSPECTING: z.infer<typeof OrganizationModelProspectingSchema> = {
@@ -29,5 +51,186 @@ export const DEFAULT_ORGANIZATION_MODEL_PROSPECTING: z.infer<typeof Organization
29
51
  { id: 'verified', label: 'Verified', order: 2 },
30
52
  { id: 'personalized', label: 'Personalized', order: 3 },
31
53
  { id: 'uploaded', label: 'Uploaded', order: 4 }
54
+ ],
55
+ defaultBuildTemplateId: 'local-services',
56
+ buildTemplates: [
57
+ {
58
+ id: 'local-services',
59
+ label: 'Local Services Prospecting',
60
+ description:
61
+ 'Curated local-services list build using company sourcing, website analysis, qualification, contact discovery, verification, personalization, and review.',
62
+ steps: [
63
+ {
64
+ id: 'source-companies',
65
+ label: 'Source companies',
66
+ primaryEntity: 'company',
67
+ outputs: ['company'],
68
+ stageKey: 'populated',
69
+ dependencyMode: 'per-record-eligibility',
70
+ capabilityKey: 'lead-gen.company.source',
71
+ defaultBatchSize: 100,
72
+ maxBatchSize: 250
73
+ },
74
+ {
75
+ id: 'analyze-websites',
76
+ label: 'Analyze websites',
77
+ primaryEntity: 'company',
78
+ outputs: ['company'],
79
+ stageKey: 'extracted',
80
+ dependsOn: ['source-companies'],
81
+ dependencyMode: 'per-record-eligibility',
82
+ capabilityKey: 'lead-gen.company.website-extract',
83
+ defaultBatchSize: 50,
84
+ maxBatchSize: 100
85
+ },
86
+ {
87
+ id: 'qualify-companies',
88
+ label: 'Qualify companies',
89
+ primaryEntity: 'company',
90
+ outputs: ['company'],
91
+ stageKey: 'qualified',
92
+ dependsOn: ['analyze-websites'],
93
+ dependencyMode: 'per-record-eligibility',
94
+ capabilityKey: 'lead-gen.company.qualify',
95
+ defaultBatchSize: 100,
96
+ maxBatchSize: 250
97
+ },
98
+ {
99
+ id: 'find-contacts',
100
+ label: 'Find contacts',
101
+ primaryEntity: 'contact',
102
+ outputs: ['contact'],
103
+ stageKey: 'discovered',
104
+ dependsOn: ['qualify-companies'],
105
+ dependencyMode: 'per-record-eligibility',
106
+ capabilityKey: 'lead-gen.contact.discover',
107
+ defaultBatchSize: 50,
108
+ maxBatchSize: 100
109
+ },
110
+ {
111
+ id: 'verify-emails',
112
+ label: 'Verify emails',
113
+ primaryEntity: 'contact',
114
+ outputs: ['contact'],
115
+ stageKey: 'verified',
116
+ dependsOn: ['find-contacts'],
117
+ dependencyMode: 'per-record-eligibility',
118
+ capabilityKey: 'lead-gen.contact.verify-email',
119
+ defaultBatchSize: 100,
120
+ maxBatchSize: 500
121
+ },
122
+ {
123
+ id: 'personalize',
124
+ label: 'Personalize',
125
+ primaryEntity: 'contact',
126
+ outputs: ['contact'],
127
+ stageKey: 'personalized',
128
+ dependsOn: ['verify-emails'],
129
+ dependencyMode: 'per-record-eligibility',
130
+ capabilityKey: 'lead-gen.contact.personalize',
131
+ defaultBatchSize: 25,
132
+ maxBatchSize: 100
133
+ },
134
+ {
135
+ id: 'review',
136
+ label: 'Review',
137
+ primaryEntity: 'contact',
138
+ outputs: ['export'],
139
+ stageKey: 'uploaded',
140
+ dependsOn: ['personalize'],
141
+ dependencyMode: 'per-record-eligibility',
142
+ capabilityKey: 'lead-gen.review.outreach-ready',
143
+ defaultBatchSize: 25,
144
+ maxBatchSize: 100
145
+ }
146
+ ]
147
+ },
148
+ {
149
+ id: 'dtc-subscription-apollo-clickup',
150
+ label: 'DTC Subscription Apollo Export',
151
+ description:
152
+ 'Prospecting pipeline for DTC subscription or subscription-ready brands where Apollo is the source and contact-enrichment layer, Elevasis handles company research and fit scoring, and approved leads export as an approved lead list.',
153
+ steps: [
154
+ {
155
+ id: 'import-apollo-search',
156
+ label: 'Import Apollo search',
157
+ description: 'Pull companies and seed contact data from a predefined Apollo search or list.',
158
+ primaryEntity: 'company',
159
+ outputs: ['company', 'contact'],
160
+ stageKey: 'populated',
161
+ dependencyMode: 'per-record-eligibility',
162
+ capabilityKey: 'lead-gen.company.apollo-import',
163
+ defaultBatchSize: 250,
164
+ maxBatchSize: 1000
165
+ },
166
+ {
167
+ id: 'analyze-websites',
168
+ label: 'Analyze websites',
169
+ description: 'Extract subscription, product, retention, and tech-stack signals from each brand website.',
170
+ primaryEntity: 'company',
171
+ outputs: ['company'],
172
+ stageKey: 'extracted',
173
+ dependsOn: ['import-apollo-search'],
174
+ dependencyMode: 'per-record-eligibility',
175
+ capabilityKey: 'lead-gen.company.website-extract',
176
+ defaultBatchSize: 50,
177
+ maxBatchSize: 100
178
+ },
179
+ {
180
+ id: 'score-dtc-fit',
181
+ label: 'Score DTC fit',
182
+ description:
183
+ 'Classify subscription potential, consumable-product fit, retention maturity, and disqualifiers.',
184
+ primaryEntity: 'company',
185
+ outputs: ['company'],
186
+ stageKey: 'qualified',
187
+ dependsOn: ['analyze-websites'],
188
+ dependencyMode: 'per-record-eligibility',
189
+ capabilityKey: 'lead-gen.company.dtc-subscription-qualify',
190
+ defaultBatchSize: 100,
191
+ maxBatchSize: 250
192
+ },
193
+ {
194
+ id: 'enrich-decision-makers',
195
+ label: 'Enrich decision-makers',
196
+ description:
197
+ 'Use Apollo to find qualified contacts such as founders, retention leads, lifecycle leads, and marketing owners.',
198
+ primaryEntity: 'contact',
199
+ outputs: ['contact'],
200
+ stageKey: 'discovered',
201
+ dependsOn: ['score-dtc-fit'],
202
+ dependencyMode: 'per-record-eligibility',
203
+ capabilityKey: 'lead-gen.contact.apollo-decision-maker-enrich',
204
+ defaultBatchSize: 100,
205
+ maxBatchSize: 250
206
+ },
207
+ {
208
+ id: 'verify-emails',
209
+ label: 'Verify emails',
210
+ description: 'Verify deliverability before the QC and handoff step.',
211
+ primaryEntity: 'contact',
212
+ outputs: ['contact'],
213
+ stageKey: 'verified',
214
+ dependsOn: ['enrich-decision-makers'],
215
+ dependencyMode: 'per-record-eligibility',
216
+ capabilityKey: 'lead-gen.contact.verify-email',
217
+ defaultBatchSize: 250,
218
+ maxBatchSize: 500
219
+ },
220
+ {
221
+ id: 'review-and-export',
222
+ label: 'Review and export',
223
+ description: 'Operator QC approves or rejects leads, then approved records are exported as a lead list.',
224
+ primaryEntity: 'company',
225
+ outputs: ['export'],
226
+ stageKey: 'uploaded',
227
+ dependsOn: ['verify-emails'],
228
+ dependencyMode: 'per-record-eligibility',
229
+ capabilityKey: 'lead-gen.export.list',
230
+ defaultBatchSize: 100,
231
+ maxBatchSize: 250
232
+ }
233
+ ]
234
+ }
32
235
  ]
33
236
  }
@@ -9,6 +9,8 @@ import {
9
9
  CRM_FOLLOWUP_1_SENT_STATE,
10
10
  CRM_FOLLOWUP_2_SENT_STATE,
11
11
  CRM_FOLLOWUP_3_SENT_STATE,
12
+ CRM_PRIORITY_BUCKETS,
13
+ DEFAULT_CRM_PRIORITY_RULE_CONFIG,
12
14
  getValidStatesForStage,
13
15
  findPipeline,
14
16
  ACQ_LIST_MEMBERS_LEAD_GEN_PIPELINE,
@@ -96,6 +98,33 @@ describe('CRM state constant shapes', () => {
96
98
  })
97
99
  })
98
100
 
101
+ describe('DEFAULT_CRM_PRIORITY_RULE_CONFIG', () => {
102
+ it('defines the canonical CRM priority buckets in sort order', () => {
103
+ expect(CRM_PRIORITY_BUCKETS.map((bucket) => bucket.bucketKey)).toEqual([
104
+ 'needs_response',
105
+ 'follow_up_due',
106
+ 'waiting',
107
+ 'stale',
108
+ 'closed_low'
109
+ ])
110
+ expect(CRM_PRIORITY_BUCKETS.map((bucket) => bucket.rank)).toEqual([10, 20, 30, 40, 50])
111
+ })
112
+
113
+ it('includes display metadata for each bucket', () => {
114
+ for (const bucket of CRM_PRIORITY_BUCKETS) {
115
+ expect(bucket.label).toEqual(expect.any(String))
116
+ expect(bucket.rank).toEqual(expect.any(Number))
117
+ expect(bucket.color).toEqual(expect.any(String))
118
+ }
119
+ })
120
+
121
+ it('maps CRM states and closed stages into priority rules', () => {
122
+ expect(DEFAULT_CRM_PRIORITY_RULE_CONFIG.closedStageKeys).toEqual(['closed_won', 'closed_lost'])
123
+ expect(DEFAULT_CRM_PRIORITY_RULE_CONFIG.followUpAfterDaysByStateKey.discovery_link_sent).toBe(3)
124
+ expect(DEFAULT_CRM_PRIORITY_RULE_CONFIG.staleAfterDays).toBeGreaterThan(0)
125
+ })
126
+ })
127
+
99
128
  describe('getValidStatesForStage', () => {
100
129
  it('returns all 8 states for the interested stage', () => {
101
130
  const states = getValidStatesForStage(CRM_PIPELINE_DEFINITION, 'interested')
@@ -151,6 +151,24 @@ export interface StatefulPipelineDefinition {
151
151
  stages: StatefulStageDefinition[]
152
152
  }
153
153
 
154
+ export type CrmPriorityBucketKey = 'needs_response' | 'follow_up_due' | 'waiting' | 'stale' | 'closed_low'
155
+
156
+ export interface CrmPriorityBucketDefinition {
157
+ bucketKey: CrmPriorityBucketKey
158
+ label: string
159
+ /** Lower ranks sort first in deal lists and pipeline columns. */
160
+ rank: number
161
+ /** UI color token. Consumers may map this to their design system. */
162
+ color: string
163
+ }
164
+
165
+ export interface CrmPriorityRuleConfig {
166
+ buckets: CrmPriorityBucketDefinition[]
167
+ closedStageKeys: string[]
168
+ followUpAfterDaysByStateKey: Record<string, number>
169
+ staleAfterDays: number
170
+ }
171
+
154
172
  /**
155
173
  * Find a pipeline definition by pipeline_key within an array of definitions.
156
174
  * Preferred over direct array indexing since the model is array-based (not keyed).
@@ -252,6 +270,90 @@ export const CRM_PIPELINE_DEFINITION: StatefulPipelineDefinition = {
252
270
  ]
253
271
  }
254
272
 
273
+ export const CRM_PRIORITY_BUCKETS: CrmPriorityBucketDefinition[] = [
274
+ { bucketKey: 'needs_response', label: 'Needs Response', rank: 10, color: 'red' },
275
+ { bucketKey: 'follow_up_due', label: 'Follow-up Due', rank: 20, color: 'orange' },
276
+ { bucketKey: 'waiting', label: 'Waiting', rank: 30, color: 'blue' },
277
+ { bucketKey: 'stale', label: 'Stale', rank: 40, color: 'gray' },
278
+ { bucketKey: 'closed_low', label: 'Closed', rank: 50, color: 'dark' }
279
+ ]
280
+
281
+ export const DEFAULT_CRM_PRIORITY_RULE_CONFIG: CrmPriorityRuleConfig = {
282
+ buckets: CRM_PRIORITY_BUCKETS,
283
+ closedStageKeys: ['closed_won', 'closed_lost'],
284
+ followUpAfterDaysByStateKey: {
285
+ discovery_link_sent: 3,
286
+ discovery_nudging: 2,
287
+ reply_sent: 3,
288
+ followup_1_sent: 3,
289
+ followup_2_sent: 5,
290
+ followup_3_sent: 7
291
+ },
292
+ staleAfterDays: 14
293
+ }
294
+
295
+ // ============================================================================
296
+ // CRM Next-Action Rule Config
297
+ //
298
+ // Maps (state_key, ownership) → suggested action key from DEFAULT_CRM_ACTIONS.
299
+ // Per-org overrides live at organizations.config.crm.next_actions; merged at
300
+ // evaluator read time (same pattern as priority). The evaluator returns the
301
+ // action's `key` string, or null for closed deals / unrecognised combinations.
302
+ // ============================================================================
303
+
304
+ export interface CrmNextActionMapping {
305
+ /** state_key to match, or '*' for any state. */
306
+ stateKey: string
307
+ /** ownership value to match ('us' | 'them'), or '*' for any. */
308
+ ownership: 'us' | 'them' | '*'
309
+ /** Action key from DEFAULT_CRM_ACTIONS (or a custom org-defined key). */
310
+ actionKey: string
311
+ /**
312
+ * When true, this mapping only fires if age >= staleAfterDays.
313
+ * Defaults to false.
314
+ */
315
+ requiresStale?: boolean
316
+ }
317
+
318
+ export interface CrmNextActionRuleConfig {
319
+ mappings: CrmNextActionMapping[]
320
+ /** Fallback action key when ownership === 'us' and no explicit mapping matches. */
321
+ ownershipUsFallback: string
322
+ }
323
+
324
+ /**
325
+ * Default next-action rule config. Evaluated in order — first match wins.
326
+ *
327
+ * Mapping table (state_key, ownership) → action_key:
328
+ * (discovery_link_sent, 'them') → 'send_nudge' (after staleAfterDays)
329
+ * (discovery_replied, 'us') → 'send_reply'
330
+ * (discovery_nudging, 'them') → 'send_nudge'
331
+ * (*, 'us') → ownershipUsFallback ('send_reply')
332
+ *
333
+ * Closed deals → null (handled before mapping evaluation).
334
+ */
335
+ export const DEFAULT_CRM_NEXT_ACTION_RULE_CONFIG: CrmNextActionRuleConfig = {
336
+ mappings: [
337
+ {
338
+ stateKey: 'discovery_link_sent',
339
+ ownership: 'them',
340
+ actionKey: 'send_nudge',
341
+ requiresStale: true
342
+ },
343
+ {
344
+ stateKey: 'discovery_replied',
345
+ ownership: 'us',
346
+ actionKey: 'send_reply'
347
+ },
348
+ {
349
+ stateKey: 'discovery_nudging',
350
+ ownership: 'them',
351
+ actionKey: 'send_nudge'
352
+ }
353
+ ],
354
+ ownershipUsFallback: 'send_reply'
355
+ }
356
+
255
357
  /** Common states that appear across multiple stages. */
256
358
  const PENDING_STATE: StatefulStateDefinition = { stateKey: 'pending', label: 'Pending' }
257
359
 
@@ -1,4 +1,5 @@
1
- import { z } from 'zod'
1
+ import { z } from 'zod'
2
+ import { OrganizationModelIconTokenSchema } from '../icons'
2
3
 
3
4
  export const ModelIdSchema = z
4
5
  .string()
@@ -8,10 +9,10 @@ export const ModelIdSchema = z
8
9
  .regex(/^[a-z0-9]+(?:[-._][a-z0-9]+)*$/, 'IDs must be lowercase and use -, _, or . separators')
9
10
 
10
11
  export const LabelSchema = z.string().trim().min(1).max(120)
11
- export const DescriptionSchema = z.string().trim().min(1).max(2000)
12
- export const ColorTokenSchema = z.string().trim().min(1).max(50)
13
- export const IconNameSchema = z.string().trim().min(1).max(80)
14
- export const PathSchema = z.string().trim().startsWith('/').max(300)
12
+ export const DescriptionSchema = z.string().trim().min(1).max(2000)
13
+ export const ColorTokenSchema = z.string().trim().min(1).max(50)
14
+ export const IconNameSchema = OrganizationModelIconTokenSchema
15
+ export const PathSchema = z.string().trim().startsWith('/').max(300)
15
16
 
16
17
  export const ReferenceIdsSchema = z.array(ModelIdSchema).default([])
17
18
 
@@ -1,9 +1,13 @@
1
1
  import { resolveOrganizationModel } from './resolve'
2
+ import type { OrganizationModelBuiltinIconToken } from './icons'
2
3
  import type { DeepPartial, OrganizationModel, OrganizationModelFeature } from './types'
3
4
 
4
5
  export type FoundationSurfaceType = 'page' | 'dashboard' | 'graph' | 'detail' | 'list' | 'settings'
5
6
 
6
- export type FoundationSurfaceIcon = 'dashboard' | 'crm' | 'lead-gen' | 'projects' | 'operations' | 'settings'
7
+ export type FoundationSurfaceIcon = Extract<
8
+ OrganizationModelBuiltinIconToken,
9
+ 'feature.dashboard' | 'feature.crm' | 'feature.lead-gen' | 'feature.projects' | 'feature.operations' | 'feature.settings'
10
+ >
7
11
 
8
12
  export interface FoundationNavigationSurface extends Omit<OrganizationModelFeature, 'icon' | 'uiPosition'> {
9
13
  icon?: FoundationSurfaceIcon
@@ -50,11 +54,11 @@ export function createFoundationOrganizationModel(override: DeepPartial<Organiza
50
54
  const crmFeature = requireCoreFeature('sales.crm')
51
55
 
52
56
  const navigationSurfaces: FoundationNavigationSurface[] = [
53
- { ...operationsFeature, id: 'operations', path: '/operations', icon: 'operations', surfaceType: 'dashboard' },
54
- { ...projectsFeature, icon: 'projects', surfaceType: 'list' },
55
- { ...leadGenFeature, id: 'lead-gen', icon: 'lead-gen', surfaceType: 'list' },
56
- { ...crmFeature, id: 'crm', icon: 'crm', surfaceType: 'graph' },
57
- { ...requireCoreFeature('settings.account'), id: 'settings', icon: 'settings', surfaceType: 'settings' }
57
+ { ...operationsFeature, id: 'operations', path: '/operations', icon: 'feature.operations', surfaceType: 'dashboard' },
58
+ { ...projectsFeature, icon: 'feature.projects', surfaceType: 'list' },
59
+ { ...leadGenFeature, id: 'lead-gen', icon: 'feature.lead-gen', surfaceType: 'list' },
60
+ { ...crmFeature, id: 'crm', icon: 'feature.crm', surfaceType: 'graph' },
61
+ { ...requireCoreFeature('settings.account'), id: 'settings', icon: 'feature.settings', surfaceType: 'settings' }
58
62
  ]
59
63
 
60
64
  const homeLabel = 'Dashboard'