@elevasis/core 0.28.0 → 0.30.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 (36) hide show
  1. package/dist/auth/index.d.ts +5289 -0
  2. package/dist/auth/index.js +595 -0
  3. package/dist/index.d.ts +11 -11
  4. package/dist/knowledge/index.d.ts +1 -1
  5. package/dist/organization-model/index.d.ts +11 -11
  6. package/dist/test-utils/index.d.ts +24 -1
  7. package/package.json +7 -3
  8. package/src/__tests__/publish.test.ts +8 -7
  9. package/src/auth/__tests__/access-key-coverage.test.ts +42 -0
  10. package/src/auth/__tests__/access-key-scan.ts +117 -0
  11. package/src/auth/__tests__/access-keys.test.ts +81 -0
  12. package/src/auth/__tests__/access-model.test.ts +257 -0
  13. package/src/auth/__tests__/access-test-fixtures.ts +50 -0
  14. package/src/auth/__tests__/key-catalog-drift.test.ts +33 -0
  15. package/src/auth/__tests__/platform-admin-bypass-parity.test.ts +67 -0
  16. package/src/auth/access-keys.ts +173 -0
  17. package/src/auth/access-model.ts +185 -0
  18. package/src/auth/index.ts +6 -2
  19. package/src/auth/multi-tenancy/memberships/membership.ts +2 -4
  20. package/src/auth/multi-tenancy/permissions.ts +1 -1
  21. package/src/auth/multi-tenancy/types.ts +3 -12
  22. package/src/business/acquisition/api-schemas.test.ts +59 -8
  23. package/src/business/acquisition/api-schemas.ts +10 -5
  24. package/src/business/acquisition/build-templates.test.ts +187 -240
  25. package/src/business/acquisition/build-templates.ts +87 -98
  26. package/src/business/acquisition/types.ts +390 -389
  27. package/src/execution/engine/index.ts +6 -4
  28. package/src/execution/engine/tools/lead-service-types.ts +63 -34
  29. package/src/execution/engine/tools/platform/acquisition/types.ts +7 -8
  30. package/src/execution/engine/tools/registry.ts +6 -4
  31. package/src/execution/engine/tools/tool-maps.ts +23 -1
  32. package/src/organization-model/domains/prospecting.ts +2 -327
  33. package/src/organization-model/migration-helpers.ts +16 -12
  34. package/src/reference/_generated/contracts.md +352 -328
  35. package/src/reference/glossary.md +8 -6
  36. package/src/supabase/database.types.ts +13 -0
@@ -1,395 +1,396 @@
1
- import type { Database } from '../../supabase/database.types'
1
+ import type { Database } from '../../supabase/database.types'
2
2
  import type {
3
3
  ActionRegistry,
4
4
  CredentialRequirement,
5
5
  RecordColumnConfig
6
6
  } from '../../organization-model/domains/prospecting'
7
- import type { PipelineStage, ProcessingStageStatus } from './api-schemas'
8
-
9
- // =============================================================================
10
- // Supabase-Generated Types (Re-exported from database.types)
11
- // =============================================================================
12
-
13
- /** Raw database row type for acq_lists table */
14
- export type AcqListRow = Database['public']['Tables']['acq_lists']['Row']
15
- /** Insert type for acq_lists table */
16
- export type AcqListInsert = Database['public']['Tables']['acq_lists']['Insert']
17
-
18
- /** Raw database row type for acq_companies table */
19
- export type AcqCompanyRow = Database['public']['Tables']['acq_companies']['Row']
20
- /** Insert type for acq_companies table */
21
- export type AcqCompanyInsert = Database['public']['Tables']['acq_companies']['Insert']
22
-
23
- /** Raw database row type for acq_contacts table */
24
- export type AcqContactRow = Database['public']['Tables']['acq_contacts']['Row']
25
- /** Insert type for acq_contacts table */
26
- export type AcqContactInsert = Database['public']['Tables']['acq_contacts']['Insert']
27
-
28
- /** Raw database row type for acq_deals table */
29
- export type AcqDealRow = Database['public']['Tables']['acq_deals']['Row']
30
- /** Insert type for acq_deals table */
31
- export type AcqDealInsert = Database['public']['Tables']['acq_deals']['Insert']
32
-
33
- /** Raw database row type for acq_deal_tasks table */
34
- export type AcqDealTaskRow = Database['public']['Tables']['acq_deal_tasks']['Row']
35
- /** Insert type for acq_deal_tasks table */
36
- export type AcqDealTaskInsert = Database['public']['Tables']['acq_deal_tasks']['Insert']
37
-
38
- // =============================================================================
39
- // Supporting Types
40
- // =============================================================================
41
-
42
- /**
43
- * Represents a web post from company website scraping.
44
- * Used for recent blog posts, news, or announcements.
45
- */
46
- export interface WebPost {
47
- /** ISO date string of when the post was published */
48
- date: string
49
- /** Title of the web post */
50
- title: string
51
- /** Brief summary of the post content */
52
- summary: string
53
- /** AI-generated insight about the post's relevance */
54
- aiInsight?: string
55
- }
56
-
7
+ import type { DataMode, PipelineStage, ProcessingStageStatus } from './api-schemas'
8
+
9
+ // =============================================================================
10
+ // Supabase-Generated Types (Re-exported from database.types)
11
+ // =============================================================================
12
+
13
+ /** Raw database row type for acq_lists table */
14
+ export type AcqListRow = Database['public']['Tables']['acq_lists']['Row']
15
+ /** Insert type for acq_lists table */
16
+ export type AcqListInsert = Database['public']['Tables']['acq_lists']['Insert']
17
+
18
+ /** Raw database row type for acq_companies table */
19
+ export type AcqCompanyRow = Database['public']['Tables']['acq_companies']['Row']
20
+ /** Insert type for acq_companies table */
21
+ export type AcqCompanyInsert = Database['public']['Tables']['acq_companies']['Insert']
22
+
23
+ /** Raw database row type for acq_contacts table */
24
+ export type AcqContactRow = Database['public']['Tables']['acq_contacts']['Row']
25
+ /** Insert type for acq_contacts table */
26
+ export type AcqContactInsert = Database['public']['Tables']['acq_contacts']['Insert']
27
+
28
+ /** Raw database row type for acq_deals table */
29
+ export type AcqDealRow = Database['public']['Tables']['acq_deals']['Row']
30
+ /** Insert type for acq_deals table */
31
+ export type AcqDealInsert = Database['public']['Tables']['acq_deals']['Insert']
32
+
33
+ /** Raw database row type for acq_deal_tasks table */
34
+ export type AcqDealTaskRow = Database['public']['Tables']['acq_deal_tasks']['Row']
35
+ /** Insert type for acq_deal_tasks table */
36
+ export type AcqDealTaskInsert = Database['public']['Tables']['acq_deal_tasks']['Insert']
37
+
38
+ // =============================================================================
39
+ // Supporting Types
40
+ // =============================================================================
41
+
42
+ /**
43
+ * Represents a web post from company website scraping.
44
+ * Used for recent blog posts, news, or announcements.
45
+ */
46
+ export interface WebPost {
47
+ /** ISO date string of when the post was published */
48
+ date: string
49
+ /** Title of the web post */
50
+ title: string
51
+ /** Brief summary of the post content */
52
+ summary: string
53
+ /** AI-generated insight about the post's relevance */
54
+ aiInsight?: string
55
+ }
56
+
57
57
  export type LeadGenStageKey = string
58
- export type LeadGenActionKey = ActionRegistry[number]['id']
59
-
60
- export interface ProcessingStateEntry {
61
- status: ProcessingStageStatus
62
- data?: unknown
63
- }
64
-
65
- export type ProcessingState = Partial<Record<LeadGenStageKey, ProcessingStateEntry>>
66
- export type CompanyProcessingState = ProcessingState
67
- export type ContactProcessingState = ProcessingState
68
- /** @deprecated Use `processingState`. Retained only as a compile-time/read-shape bridge for external tenants. */
69
- export type LegacyPipelineStatus = unknown
70
-
71
- /**
72
- * Enrichment data collected for a company from various sources.
73
- */
74
- export interface CompanyEnrichmentData {
75
- googleMaps?: {
76
- placeId?: string
77
- totalScore?: number
78
- reviewsCount?: number
79
- address?: string
80
- phone?: string
81
- categoryName?: string
82
- googleMapsUrl?: string
83
- scrapedAt?: string
84
- }
85
- websiteCrawl?: {
86
- companyDescription?: string
87
- services?: string[]
88
- specialties?: string[]
89
- staff?: Array<{ name: string; title?: string; email?: string }>
90
- automationGaps?: string[]
91
- targetAudience?: string
92
- category?: string
93
- segment?: string
94
- recentWin?: string
95
- emailCount?: number
96
- pageCount?: number
97
- totalChars?: number
98
- crawledAt?: string
99
- extractedAt?: string
100
- }
101
- website?: {
102
- missionVision?: string
103
- uniqueAttributes?: string
104
- coreOfferings?: string
105
- targetAudience?: string
106
- companyValues?: string
107
- businessDescription?: string
108
- recentPosts?: Array<{ date?: string; title?: string; summary?: string; aiInsight?: string }>
109
- }
110
- tomba?: {
111
- waterfallEmail?: {
112
- email: string
113
- name?: string
114
- title?: string
115
- department?: string
116
- } | null
117
- genericEmail?: string | null
118
- totalFound?: number
119
- searchedAt?: string
120
- }
121
- }
122
-
123
- /**
124
- * Enrichment data collected for a contact from various sources.
125
- */
126
- export interface ContactEnrichmentData {
127
- linkedin?: {
128
- summary?: string
129
- pastExperience?: string
130
- education?: string
131
- activity?: Array<{ date?: string; content?: string }>
132
- }
133
- }
134
-
135
- // =============================================================================
136
- // Domain Types (camelCase transformations of database rows)
137
- // =============================================================================
138
-
139
- export type ListStatus = 'draft' | 'enriching' | 'launched' | 'closing' | 'archived'
140
-
141
- export interface ScrapingConfig {
142
- source?: string
143
- query?: string
144
- filters?: Record<string, unknown>
145
- [key: string]: unknown
146
- }
147
-
148
- export interface IcpRubric {
149
- targetDescription?: string
150
- minReviewCount?: number
151
- minRating?: number
152
- excludeFranchises?: boolean
153
- customRules?: string
154
- qualificationRubricKey?: string | null
155
- [key: string]: unknown
156
- }
157
-
158
- export interface PipelineConfig {
159
- stages: PipelineStage[]
160
- }
161
-
162
- export type BuildPlanSnapshotPrimaryEntity = 'company' | 'contact'
163
- export type BuildPlanSnapshotOutput = 'company' | 'contact' | 'export'
164
- export type BuildPlanSnapshotDependencyMode = 'per-record-eligibility'
165
-
166
- export interface BuildPlanSnapshotStep {
167
- id: string
168
- label: string
169
- description?: string
170
- primaryEntity: BuildPlanSnapshotPrimaryEntity
171
- outputs: BuildPlanSnapshotOutput[]
172
- stageKey: string
173
- recordEntity?: BuildPlanSnapshotPrimaryEntity
174
- recordsStageKey?: string
175
- recordSourceStageKey?: string
176
- dependsOn?: string[]
177
- dependencyMode: BuildPlanSnapshotDependencyMode
178
- actionKey: string
179
- defaultBatchSize: number
180
- maxBatchSize: number
181
- recordColumns?: Partial<Record<BuildPlanSnapshotPrimaryEntity, RecordColumnConfig[]>>
182
- credentialRequirements?: CredentialRequirement[]
183
- }
184
-
185
- export interface BuildPlanSnapshot {
186
- templateId: string
187
- templateLabel: string
188
- steps: BuildPlanSnapshotStep[]
189
- }
190
-
191
- export interface AcqListMetadata extends Record<string, unknown> {
192
- buildPlanSnapshot?: BuildPlanSnapshot
193
- }
194
-
195
- export interface AcqList {
196
- id: string
197
- organizationId: string
198
- name: string
199
- description: string | null
200
- batchIds: string[]
201
- instantlyCampaignId: string | null
202
- status: ListStatus
203
- scrapingConfig: ScrapingConfig
204
- icp: IcpRubric
205
- pipelineConfig: PipelineConfig
206
- metadata: AcqListMetadata
207
- launchedAt: Date | null
208
- completedAt: Date | null
209
- createdAt: Date
210
- }
211
-
212
- /**
213
- * Company record in the acquisition database.
214
- * Contains enriched company data from various sources.
215
- * Transformed from AcqCompanyRow with camelCase properties.
216
- */
217
- export interface AcqCompany {
218
- id: string
219
- organizationId: string
220
- name: string
221
- domain: string | null
222
- linkedinUrl: string | null
223
- website: string | null
224
- numEmployees: number | null
225
- foundedYear: number | null
226
- locationCity: string | null
227
- locationState: string | null
228
- category: string | null
229
- categoryPain: string | null
230
- segment: string | null
231
- processingState: CompanyProcessingState | null
232
- /** @deprecated Use `processingState`. This legacy DB column has been removed; responses return null. */
233
- pipelineStatus?: LegacyPipelineStatus | null
234
- enrichmentData: CompanyEnrichmentData | null
235
- source: string | null
236
- batchId: string | null
237
- status: 'active' | 'invalid'
238
- verticalResearch: string | null
239
- /** Track A: flat qualification score (null until a scoring rubric is defined). Added by W1 migration. */
240
- qualificationScore: number | null
241
- /** Track A: flat qualification signals jsonb preserving the result payload shape. Added by W1 migration. */
242
- qualificationSignals: Record<string, unknown> | null
243
- /** Track A: key identifying the rubric used for qualification. Added by W1 migration. */
244
- qualificationRubricKey: string | null
245
- createdAt: Date
246
- updatedAt: Date
247
- }
248
-
249
- /**
250
- * Contact record in the acquisition database.
251
- * Contains enriched contact data and personalization content.
252
- * Transformed from AcqContactRow with camelCase properties.
253
- */
254
- export interface AcqContact {
255
- id: string
256
- organizationId: string
257
- companyId: string | null
258
- email: string
259
- emailValid: 'VALID' | 'INVALID' | 'RISKY' | 'UNKNOWN' | null
260
- firstName: string | null
261
- lastName: string | null
262
- linkedinUrl: string | null
263
- title: string | null
264
- headline: string | null
265
- filterReason: string | null
266
- openingLine: string | null
267
- source: string | null
268
- sourceId: string | null
269
- processingState: ContactProcessingState | null
270
- /** @deprecated Use `processingState`. This legacy DB column has been removed; responses return null. */
271
- pipelineStatus?: LegacyPipelineStatus | null
272
- enrichmentData: ContactEnrichmentData | null
273
- /** Attio Person record ID - set when contact responds and is added to CRM */
274
- attioPersonId: string | null
275
- batchId: string | null
276
- status: 'active' | 'invalid'
277
- createdAt: Date
278
- updatedAt: Date
279
- }
280
-
281
- // ─── Deal UI Types ───────────────────────────────────────────────────────────
282
-
283
- export type DealStage = 'interested' | 'proposal' | 'closing' | 'closed_won' | 'closed_lost' | 'nurturing'
284
-
285
- export type DealPriorityBucketKey = 'needs_response' | 'follow_up_due' | 'waiting' | 'stale' | 'closed_low'
286
-
287
- export interface DealPriority {
288
- bucketKey: DealPriorityBucketKey
289
- rank: number
290
- label: string
291
- color: string
292
- reason: string
293
- latestActivityAt: string | null
294
- nextActionAt: string | null
295
- }
296
-
297
- export interface KanbanStageConfig {
298
- color: string // Mantine color token (e.g. 'blue', 'teal')
299
- label?: string // Optional display label override
300
- }
301
-
302
- export type KanbanBoardConfig = Partial<Record<DealStage, KanbanStageConfig>>
303
-
304
- export interface DealContact {
305
- id: string
306
- first_name: string | null
307
- last_name: string | null
308
- email: string
309
- title: string | null
310
- headline: string | null
311
- linkedin_url: string | null
312
- processing_state: Record<string, unknown> | null
313
- enrichment_data: Record<string, unknown> | null
314
- company: {
315
- id: string
316
- name: string
317
- domain: string | null
318
- website: string | null
319
- linkedin_url: string | null
320
- segment: string | null
321
- category: string | null
322
- num_employees: number | null
323
- } | null
324
- }
325
-
326
- export interface DealFilters {
327
- stage?: DealStage
328
- search?: string
329
- limit?: number
330
- offset?: number
331
- }
332
-
333
- /** Deal list item with joined contact and company data */
334
- export interface DealListItem extends AcqDealRow {
335
- priority: DealPriority
336
- ownership: 'us' | 'them' | null
337
- nextAction: string | null
338
- contact: DealContact | null
339
- }
340
-
341
- export type DealDetail = DealListItem
342
-
343
- /** Task kind options for a deal task (human follow-up action type) */
344
- export type AcqDealTaskKind = 'call' | 'email' | 'meeting' | 'other'
345
-
346
- /**
347
- * A CRM to-do item attached to a deal representing a human follow-up action.
348
- * Transformed from AcqDealTaskRow with camelCase properties.
349
- */
350
- export interface AcqDealTask {
351
- id: string
352
- organizationId: string
353
- dealId: string
354
- title: string
355
- description: string | null
356
- kind: AcqDealTaskKind
357
- dueAt: string | null
358
- assigneeUserId: string | null
359
- completedAt: string | null
360
- completedByUserId: string | null
361
- createdAt: string
362
- updatedAt: string
363
- createdByUserId: string | null
364
- }
365
-
366
- // ─── Progress / Telemetry Types ──────────────────────────────────────────────
367
-
368
- /**
369
- * Live-scan aggregate telemetry for a single list, computed on demand from
370
- * the list junction tables and current contact deliverability state.
371
- * `stageCounts` are attempted counts from list-row processing_state.
372
- */
373
- export interface ListTelemetry {
374
- listId: string
375
- totalCompanies: number
376
- totalContacts: number
377
- stageCounts: {
378
- populated: number
379
- extracted: number
380
- qualified: number
381
- discovered: number
382
- verified: number
383
- personalized: number
384
- uploaded: number
385
- }
386
- deliverability: {
387
- valid: number
388
- risky: number
389
- invalid: number
390
- unknown: number
391
- bounced: number
392
- }
393
- /** Reserved -- active workflow IDs associated with this list. */
394
- activeWorkflows?: string[]
395
- }
58
+ export type LeadGenActionKey = ActionRegistry[number]['id']
59
+
60
+ export interface ProcessingStateEntry {
61
+ status: ProcessingStageStatus
62
+ data?: unknown
63
+ }
64
+
65
+ export type ProcessingState = Partial<Record<LeadGenStageKey, ProcessingStateEntry>>
66
+ export type CompanyProcessingState = ProcessingState
67
+ export type ContactProcessingState = ProcessingState
68
+ /** @deprecated Use `processingState`. Retained only as a compile-time/read-shape bridge for external tenants. */
69
+ export type LegacyPipelineStatus = unknown
70
+
71
+ /**
72
+ * Enrichment data collected for a company from various sources.
73
+ */
74
+ export interface CompanyEnrichmentData {
75
+ googleMaps?: {
76
+ placeId?: string
77
+ totalScore?: number
78
+ reviewsCount?: number
79
+ address?: string
80
+ phone?: string
81
+ categoryName?: string
82
+ googleMapsUrl?: string
83
+ scrapedAt?: string
84
+ }
85
+ websiteCrawl?: {
86
+ companyDescription?: string
87
+ services?: string[]
88
+ specialties?: string[]
89
+ staff?: Array<{ name: string; title?: string; email?: string }>
90
+ automationGaps?: string[]
91
+ targetAudience?: string
92
+ category?: string
93
+ segment?: string
94
+ recentWin?: string
95
+ emailCount?: number
96
+ pageCount?: number
97
+ totalChars?: number
98
+ crawledAt?: string
99
+ extractedAt?: string
100
+ }
101
+ website?: {
102
+ missionVision?: string
103
+ uniqueAttributes?: string
104
+ coreOfferings?: string
105
+ targetAudience?: string
106
+ companyValues?: string
107
+ businessDescription?: string
108
+ recentPosts?: Array<{ date?: string; title?: string; summary?: string; aiInsight?: string }>
109
+ }
110
+ tomba?: {
111
+ waterfallEmail?: {
112
+ email: string
113
+ name?: string
114
+ title?: string
115
+ department?: string
116
+ } | null
117
+ genericEmail?: string | null
118
+ totalFound?: number
119
+ searchedAt?: string
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Enrichment data collected for a contact from various sources.
125
+ */
126
+ export interface ContactEnrichmentData {
127
+ linkedin?: {
128
+ summary?: string
129
+ pastExperience?: string
130
+ education?: string
131
+ activity?: Array<{ date?: string; content?: string }>
132
+ }
133
+ }
134
+
135
+ // =============================================================================
136
+ // Domain Types (camelCase transformations of database rows)
137
+ // =============================================================================
138
+
139
+ export type ListStatus = 'draft' | 'enriching' | 'launched' | 'closing' | 'archived'
140
+
141
+ export interface ScrapingConfig {
142
+ source?: string
143
+ query?: string
144
+ filters?: Record<string, unknown>
145
+ [key: string]: unknown
146
+ }
147
+
148
+ export interface IcpRubric {
149
+ targetDescription?: string
150
+ minReviewCount?: number
151
+ minRating?: number
152
+ excludeFranchises?: boolean
153
+ customRules?: string
154
+ qualificationRubricKey?: string | null
155
+ [key: string]: unknown
156
+ }
157
+
158
+ export interface PipelineConfig {
159
+ stages?: PipelineStage[]
160
+ dataMode?: DataMode
161
+ }
162
+
163
+ export type BuildPlanSnapshotPrimaryEntity = 'company' | 'contact'
164
+ export type BuildPlanSnapshotOutput = 'company' | 'contact' | 'export'
165
+ export type BuildPlanSnapshotDependencyMode = 'per-record-eligibility'
166
+
167
+ export interface BuildPlanSnapshotStep {
168
+ id: string
169
+ label: string
170
+ description?: string
171
+ primaryEntity: BuildPlanSnapshotPrimaryEntity
172
+ outputs: BuildPlanSnapshotOutput[]
173
+ stageKey: string
174
+ recordEntity?: BuildPlanSnapshotPrimaryEntity
175
+ recordsStageKey?: string
176
+ recordSourceStageKey?: string
177
+ dependsOn?: string[]
178
+ dependencyMode: BuildPlanSnapshotDependencyMode
179
+ actionKey: string
180
+ defaultBatchSize: number
181
+ maxBatchSize: number
182
+ recordColumns?: Partial<Record<BuildPlanSnapshotPrimaryEntity, RecordColumnConfig[]>>
183
+ credentialRequirements?: CredentialRequirement[]
184
+ }
185
+
186
+ export interface BuildPlanSnapshot {
187
+ templateId: string
188
+ templateLabel: string
189
+ steps: BuildPlanSnapshotStep[]
190
+ }
191
+
192
+ export interface AcqListMetadata extends Record<string, unknown> {
193
+ buildPlanSnapshot?: BuildPlanSnapshot
194
+ }
195
+
196
+ export interface AcqList {
197
+ id: string
198
+ organizationId: string
199
+ name: string
200
+ description: string | null
201
+ batchIds: string[]
202
+ instantlyCampaignId: string | null
203
+ status: ListStatus
204
+ scrapingConfig: ScrapingConfig
205
+ icp: IcpRubric
206
+ pipelineConfig: PipelineConfig
207
+ metadata: AcqListMetadata
208
+ launchedAt: Date | null
209
+ completedAt: Date | null
210
+ createdAt: Date
211
+ }
212
+
213
+ /**
214
+ * Company record in the acquisition database.
215
+ * Contains enriched company data from various sources.
216
+ * Transformed from AcqCompanyRow with camelCase properties.
217
+ */
218
+ export interface AcqCompany {
219
+ id: string
220
+ organizationId: string
221
+ name: string
222
+ domain: string | null
223
+ linkedinUrl: string | null
224
+ website: string | null
225
+ numEmployees: number | null
226
+ foundedYear: number | null
227
+ locationCity: string | null
228
+ locationState: string | null
229
+ category: string | null
230
+ categoryPain: string | null
231
+ segment: string | null
232
+ processingState: CompanyProcessingState | null
233
+ /** @deprecated Use `processingState`. This legacy DB column has been removed; responses return null. */
234
+ pipelineStatus?: LegacyPipelineStatus | null
235
+ enrichmentData: CompanyEnrichmentData | null
236
+ source: string | null
237
+ batchId: string | null
238
+ status: 'active' | 'invalid'
239
+ verticalResearch: string | null
240
+ /** Track A: flat qualification score (null until a scoring rubric is defined). Added by W1 migration. */
241
+ qualificationScore: number | null
242
+ /** Track A: flat qualification signals jsonb preserving the result payload shape. Added by W1 migration. */
243
+ qualificationSignals: Record<string, unknown> | null
244
+ /** Track A: key identifying the rubric used for qualification. Added by W1 migration. */
245
+ qualificationRubricKey: string | null
246
+ createdAt: Date
247
+ updatedAt: Date
248
+ }
249
+
250
+ /**
251
+ * Contact record in the acquisition database.
252
+ * Contains enriched contact data and personalization content.
253
+ * Transformed from AcqContactRow with camelCase properties.
254
+ */
255
+ export interface AcqContact {
256
+ id: string
257
+ organizationId: string
258
+ companyId: string | null
259
+ email: string
260
+ emailValid: 'VALID' | 'INVALID' | 'RISKY' | 'UNKNOWN' | null
261
+ firstName: string | null
262
+ lastName: string | null
263
+ linkedinUrl: string | null
264
+ title: string | null
265
+ headline: string | null
266
+ filterReason: string | null
267
+ openingLine: string | null
268
+ source: string | null
269
+ sourceId: string | null
270
+ processingState: ContactProcessingState | null
271
+ /** @deprecated Use `processingState`. This legacy DB column has been removed; responses return null. */
272
+ pipelineStatus?: LegacyPipelineStatus | null
273
+ enrichmentData: ContactEnrichmentData | null
274
+ /** Attio Person record ID - set when contact responds and is added to CRM */
275
+ attioPersonId: string | null
276
+ batchId: string | null
277
+ status: 'active' | 'invalid'
278
+ createdAt: Date
279
+ updatedAt: Date
280
+ }
281
+
282
+ // ─── Deal UI Types ───────────────────────────────────────────────────────────
283
+
284
+ export type DealStage = 'interested' | 'proposal' | 'closing' | 'closed_won' | 'closed_lost' | 'nurturing'
285
+
286
+ export type DealPriorityBucketKey = 'needs_response' | 'follow_up_due' | 'waiting' | 'stale' | 'closed_low'
287
+
288
+ export interface DealPriority {
289
+ bucketKey: DealPriorityBucketKey
290
+ rank: number
291
+ label: string
292
+ color: string
293
+ reason: string
294
+ latestActivityAt: string | null
295
+ nextActionAt: string | null
296
+ }
297
+
298
+ export interface KanbanStageConfig {
299
+ color: string // Mantine color token (e.g. 'blue', 'teal')
300
+ label?: string // Optional display label override
301
+ }
302
+
303
+ export type KanbanBoardConfig = Partial<Record<DealStage, KanbanStageConfig>>
304
+
305
+ export interface DealContact {
306
+ id: string
307
+ first_name: string | null
308
+ last_name: string | null
309
+ email: string
310
+ title: string | null
311
+ headline: string | null
312
+ linkedin_url: string | null
313
+ processing_state: Record<string, unknown> | null
314
+ enrichment_data: Record<string, unknown> | null
315
+ company: {
316
+ id: string
317
+ name: string
318
+ domain: string | null
319
+ website: string | null
320
+ linkedin_url: string | null
321
+ segment: string | null
322
+ category: string | null
323
+ num_employees: number | null
324
+ } | null
325
+ }
326
+
327
+ export interface DealFilters {
328
+ stage?: DealStage
329
+ search?: string
330
+ limit?: number
331
+ offset?: number
332
+ }
333
+
334
+ /** Deal list item with joined contact and company data */
335
+ export interface DealListItem extends AcqDealRow {
336
+ priority: DealPriority
337
+ ownership: 'us' | 'them' | null
338
+ nextAction: string | null
339
+ contact: DealContact | null
340
+ }
341
+
342
+ export type DealDetail = DealListItem
343
+
344
+ /** Task kind options for a deal task (human follow-up action type) */
345
+ export type AcqDealTaskKind = 'call' | 'email' | 'meeting' | 'other'
346
+
347
+ /**
348
+ * A CRM to-do item attached to a deal representing a human follow-up action.
349
+ * Transformed from AcqDealTaskRow with camelCase properties.
350
+ */
351
+ export interface AcqDealTask {
352
+ id: string
353
+ organizationId: string
354
+ dealId: string
355
+ title: string
356
+ description: string | null
357
+ kind: AcqDealTaskKind
358
+ dueAt: string | null
359
+ assigneeUserId: string | null
360
+ completedAt: string | null
361
+ completedByUserId: string | null
362
+ createdAt: string
363
+ updatedAt: string
364
+ createdByUserId: string | null
365
+ }
366
+
367
+ // ─── Progress / Telemetry Types ──────────────────────────────────────────────
368
+
369
+ /**
370
+ * Live-scan aggregate telemetry for a single list, computed on demand from
371
+ * the list junction tables and current contact deliverability state.
372
+ * `stageCounts` are attempted counts from list-row processing_state.
373
+ */
374
+ export interface ListTelemetry {
375
+ listId: string
376
+ totalCompanies: number
377
+ totalContacts: number
378
+ stageCounts: {
379
+ populated: number
380
+ extracted: number
381
+ qualified: number
382
+ discovered: number
383
+ verified: number
384
+ personalized: number
385
+ uploaded: number
386
+ }
387
+ deliverability: {
388
+ valid: number
389
+ risky: number
390
+ invalid: number
391
+ unknown: number
392
+ bounced: number
393
+ }
394
+ /** Reserved -- active workflow IDs associated with this list. */
395
+ activeWorkflows?: string[]
396
+ }