@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.
Files changed (44) hide show
  1. package/dist/index.d.ts +82 -1
  2. package/dist/index.js +291 -171
  3. package/dist/knowledge/index.d.ts +43 -0
  4. package/dist/organization-model/index.d.ts +82 -1
  5. package/dist/organization-model/index.js +291 -171
  6. package/dist/test-utils/index.d.ts +41 -12
  7. package/dist/test-utils/index.js +291 -171
  8. package/package.json +2 -1
  9. package/src/_gen/__tests__/__snapshots__/contracts.md.snap +78 -65
  10. package/src/auth/multi-tenancy/organizations/__tests__/api-schemas.test.ts +194 -0
  11. package/src/auth/multi-tenancy/organizations/api-schemas.ts +136 -128
  12. package/src/business/acquisition/api-schemas.test.ts +100 -2
  13. package/src/business/acquisition/api-schemas.ts +81 -43
  14. package/src/business/acquisition/build-templates.test.ts +212 -0
  15. package/src/business/acquisition/types.ts +21 -38
  16. package/src/execution/engine/index.ts +436 -434
  17. package/src/execution/engine/tools/integration/server/adapters/google-calendar/google-calendar-adapter.ts +428 -0
  18. package/src/execution/engine/tools/integration/server/adapters/google-calendar/index.ts +2 -0
  19. package/src/execution/engine/tools/lead-service-types.ts +51 -9
  20. package/src/execution/engine/tools/platform/acquisition/company-tools.ts +7 -6
  21. package/src/execution/engine/tools/platform/acquisition/contact-tools.ts +6 -5
  22. package/src/execution/engine/tools/platform/acquisition/types.ts +20 -9
  23. package/src/execution/engine/tools/registry.ts +700 -698
  24. package/src/execution/engine/tools/tool-maps.ts +10 -0
  25. package/src/execution/external/__tests__/api-schemas.test.ts +127 -0
  26. package/src/integrations/oauth/__tests__/provider-registry.test.ts +7 -6
  27. package/src/integrations/oauth/provider-registry.ts +74 -61
  28. package/src/integrations/oauth/server/credentials.ts +43 -39
  29. package/src/knowledge/__tests__/queries.test.ts +89 -0
  30. package/src/organization-model/__tests__/icons.test.ts +61 -0
  31. package/src/organization-model/__tests__/knowledge.test.ts +118 -1
  32. package/src/organization-model/__tests__/prospecting-ssot.test.ts +94 -0
  33. package/src/organization-model/defaults.ts +8 -0
  34. package/src/organization-model/domains/knowledge.ts +9 -0
  35. package/src/organization-model/domains/prospecting.ts +272 -226
  36. package/src/organization-model/domains/sales.ts +32 -25
  37. package/src/organization-model/icons.ts +3 -0
  38. package/src/organization-model/types.ts +9 -1
  39. package/src/platform/constants/versions.ts +1 -1
  40. package/src/platform/utils/__tests__/validation.test.ts +1084 -1083
  41. package/src/platform/utils/validation.ts +425 -425
  42. package/src/reference/_generated/contracts.md +78 -65
  43. package/src/server.ts +6 -0
  44. package/src/supabase/database.types.ts +6 -12
@@ -1,128 +1,136 @@
1
- /**
2
- * Organizations Domain - Zod Validation Schemas
3
- *
4
- * Validation schemas for organization management endpoints.
5
- * Includes request bodies, query params, and path params.
6
- *
7
- * Security:
8
- * - All schemas use .strict() to prevent mass assignment attacks
9
- * - UUID/WorkOS ID validation prevents invalid references
10
- * - String length limits prevent DoS
11
- * - Domain and metadata size limits
12
- */
13
-
14
- import { z } from 'zod'
15
- import { UuidSchema } from '../../../platform/utils/validation'
16
-
17
- // ============================================================================
18
- // Shared Schemas
19
- // ============================================================================
20
-
21
- /**
22
- * Organization name validation
23
- * - Alphanumeric, spaces, hyphens, underscores only
24
- * - 2-100 characters
25
- *
26
- * Security: Prevents injection, DoS via long names
27
- */
28
- export const OrganizationNameSchema = z.string()
29
- .min(2, 'Organization name must be at least 2 characters')
30
- .max(100, 'Organization name must be at most 100 characters')
31
- .trim()
32
- .regex(/^[a-zA-Z0-9\s\-_]+$/, 'Organization name must contain only letters, numbers, spaces, hyphens, and underscores')
33
-
34
- /**
35
- * Organization ID validation
36
- * Supports both UUID and WorkOS org_ prefixed IDs
37
- */
38
- export const OrganizationIdSchema = z.union([
39
- UuidSchema,
40
- z.string().regex(/^org_[a-zA-Z0-9]+$/, 'Invalid WorkOS organization ID')
41
- ])
42
-
43
- /**
44
- * Organization domain data schema
45
- */
46
- export const OrganizationDomainSchema = z.object({
47
- domain: z.string().min(3).max(255),
48
- state: z.enum(['verified', 'pending', 'failed']).optional()
49
- })
50
-
51
- // ============================================================================
52
- // Path Parameters
53
- // ============================================================================
54
-
55
- /**
56
- * Validate organization ID in URL path
57
- * Used by: GET/PUT/DELETE /organizations/:id
58
- */
59
- export const OrganizationIdParamSchema = z
60
- .object({
61
- id: OrganizationIdSchema
62
- })
63
- .strict()
64
-
65
- // ============================================================================
66
- // Request Bodies
67
- // ============================================================================
68
-
69
- /**
70
- * Create new organization
71
- * POST /organizations
72
- *
73
- * Security:
74
- * - Name format validated (alphanumeric + spaces + hyphens + underscores)
75
- * - Domain array size limited (max 10)
76
- * - Metadata size limited (10KB)
77
- * - Strict mode prevents unknown field injection
78
- */
79
- export const CreateOrganizationSchema = z
80
- .object({
81
- name: OrganizationNameSchema,
82
- domainData: z.array(OrganizationDomainSchema).max(10).optional(),
83
- metadata: z.record(z.string(), z.unknown())
84
- .refine(
85
- val => JSON.stringify(val).length <= 10240,
86
- 'Metadata must be under 10KB'
87
- )
88
- .optional()
89
- })
90
- .strict()
91
-
92
- /**
93
- * Update organization
94
- * PUT /organizations/:id
95
- *
96
- * Security:
97
- * - All fields optional (partial update)
98
- * - Same validation as create
99
- */
100
- export const UpdateOrganizationSchema = CreateOrganizationSchema.partial().strict()
101
-
102
- // ============================================================================
103
- // Query Parameters
104
- // ============================================================================
105
-
106
- /**
107
- * List organizations with filters
108
- * GET /organizations
109
- *
110
- * Security:
111
- * - Limit bounded (prevents DoS)
112
- * - WorkOS pagination cursors
113
- */
114
- export const ListOrganizationsQuerySchema = z.object({
115
- limit: z.coerce.number().int().min(1).max(100).default(20),
116
- before: z.string().optional(), // WorkOS pagination cursor
117
- after: z.string().optional() // WorkOS pagination cursor
118
- })
119
-
120
- // ============================================================================
121
- // TypeScript Type Exports
122
- // ============================================================================
123
-
124
- // Export inferred types for use in route handlers
125
- export type CreateOrganizationInput = z.infer<typeof CreateOrganizationSchema>
126
- export type UpdateOrganizationInput = z.infer<typeof UpdateOrganizationSchema>
127
- export type ListOrganizationsQuery = z.infer<typeof ListOrganizationsQuerySchema>
128
- export type OrganizationIdParam = z.infer<typeof OrganizationIdParamSchema>
1
+ /**
2
+ * Organizations Domain - Zod Validation Schemas
3
+ *
4
+ * Validation schemas for organization management endpoints.
5
+ * Includes request bodies, query params, and path params.
6
+ *
7
+ * Security:
8
+ * - All schemas use .strict() to prevent mass assignment attacks
9
+ * - UUID/WorkOS ID validation prevents invalid references
10
+ * - String length limits prevent DoS
11
+ * - Domain and metadata size limits
12
+ */
13
+
14
+ import { z } from 'zod'
15
+ import { UuidSchema } from '../../../platform/utils/validation'
16
+
17
+ // ============================================================================
18
+ // Shared Schemas
19
+ // ============================================================================
20
+
21
+ /**
22
+ * Organization name validation
23
+ * - Alphanumeric, spaces, hyphens, underscores only
24
+ * - 2-100 characters
25
+ *
26
+ * Security: Prevents injection, DoS via long names
27
+ */
28
+ export const OrganizationNameSchema = z
29
+ .string()
30
+ .min(2, 'Organization name must be at least 2 characters')
31
+ .max(100, 'Organization name must be at most 100 characters')
32
+ .trim()
33
+ .regex(
34
+ /^[a-zA-Z0-9\s\-_]+$/,
35
+ 'Organization name must contain only letters, numbers, spaces, hyphens, and underscores'
36
+ )
37
+
38
+ /**
39
+ * Organization ID validation
40
+ * Supports both UUID and WorkOS org_ prefixed IDs
41
+ */
42
+ export const OrganizationIdSchema = z.union([
43
+ UuidSchema,
44
+ z.string().regex(/^org_[a-zA-Z0-9]+$/, 'Invalid WorkOS organization ID')
45
+ ])
46
+
47
+ /**
48
+ * Organization domain data schema
49
+ */
50
+ export const OrganizationDomainSchema = z.object({
51
+ domain: z.string().min(3).max(255),
52
+ state: z.enum(['verified', 'pending', 'failed']).optional()
53
+ })
54
+
55
+ // ============================================================================
56
+ // Path Parameters
57
+ // ============================================================================
58
+
59
+ /**
60
+ * Validate organization ID in URL path
61
+ * Used by: GET/PUT/DELETE /organizations/:id
62
+ */
63
+ export const OrganizationIdParamSchema = z
64
+ .object({
65
+ id: OrganizationIdSchema
66
+ })
67
+ .strict()
68
+
69
+ // ============================================================================
70
+ // Request Bodies
71
+ // ============================================================================
72
+
73
+ /**
74
+ * Create new organization
75
+ * POST /organizations
76
+ *
77
+ * Security:
78
+ * - Name format validated (alphanumeric + spaces + hyphens + underscores)
79
+ * - Domain array size limited (max 10)
80
+ * - Metadata size limited (10KB)
81
+ * - Strict mode prevents unknown field injection
82
+ */
83
+ export const CreateOrganizationSchema = z
84
+ .object({
85
+ name: OrganizationNameSchema,
86
+ domainData: z.array(OrganizationDomainSchema).max(10).optional(),
87
+ metadata: z
88
+ .record(z.string(), z.unknown())
89
+ .refine((val) => JSON.stringify(val).length <= 10240, 'Metadata must be under 10KB')
90
+ .optional()
91
+ })
92
+ .strict()
93
+
94
+ /**
95
+ * Update organization
96
+ * PUT /organizations/:id
97
+ *
98
+ * Security:
99
+ * - All fields optional (partial update)
100
+ * - Same validation as create
101
+ * - At least one field required (matches documented Update schema convention,
102
+ * see .claude/rules/core-package.md → "api-schemas.ts Pattern")
103
+ */
104
+ export const UpdateOrganizationSchema = CreateOrganizationSchema.partial()
105
+ .strict()
106
+ .refine((data) => Object.keys(data).length > 0, {
107
+ message: 'At least one field (name, domainData, or metadata) must be provided'
108
+ })
109
+
110
+ // ============================================================================
111
+ // Query Parameters
112
+ // ============================================================================
113
+
114
+ /**
115
+ * List organizations with filters
116
+ * GET /organizations
117
+ *
118
+ * Security:
119
+ * - Limit bounded (prevents DoS)
120
+ * - WorkOS pagination cursors
121
+ */
122
+ export const ListOrganizationsQuerySchema = z.object({
123
+ limit: z.coerce.number().int().min(1).max(100).default(20),
124
+ before: z.string().optional(), // WorkOS pagination cursor
125
+ after: z.string().optional() // WorkOS pagination cursor
126
+ })
127
+
128
+ // ============================================================================
129
+ // TypeScript Type Exports
130
+ // ============================================================================
131
+
132
+ // Export inferred types for use in route handlers
133
+ export type CreateOrganizationInput = z.infer<typeof CreateOrganizationSchema>
134
+ export type UpdateOrganizationInput = z.infer<typeof UpdateOrganizationSchema>
135
+ export type ListOrganizationsQuery = z.infer<typeof ListOrganizationsQuerySchema>
136
+ export type OrganizationIdParam = z.infer<typeof OrganizationIdParamSchema>
@@ -13,6 +13,7 @@ import {
13
13
  AcqContactStatusSchema,
14
14
  AcqEmailValidSchema,
15
15
  AcqListResponseSchema,
16
+ BuildPlanSnapshotSchema,
16
17
  CreateArtifactRequestSchema,
17
18
  CreateCompanyRequestSchema,
18
19
  CreateContactRequestSchema,
@@ -43,6 +44,7 @@ import {
43
44
  UpdateListRequestSchema,
44
45
  UpdateListStatusRequestSchema
45
46
  } from './api-schemas'
47
+ import { createBuildPlanSnapshotFromTemplateId } from './build-templates'
46
48
 
47
49
  // ---------------------------------------------------------------------------
48
50
  // Helpers
@@ -416,6 +418,22 @@ describe('UpdateContactRequestSchema', () => {
416
418
  )
417
419
  })
418
420
 
421
+ it('accepts processingState keyed by the stage catalog', () => {
422
+ const result = UpdateContactRequestSchema.safeParse({
423
+ processingState: {
424
+ [LEAD_GEN_STAGE_CATALOG.verified.key]: {
425
+ status: 'no_result'
426
+ }
427
+ }
428
+ })
429
+
430
+ expect(result.success).toBe(true)
431
+ })
432
+
433
+ it('accepts deprecated pipelineStatus as a compatibility no-op', () => {
434
+ expect(UpdateContactRequestSchema.safeParse({ pipelineStatus: 'emailed' }).success).toBe(true)
435
+ })
436
+
419
437
  it('rejects unknown top-level fields (strict mode)', () => {
420
438
  expect(UpdateContactRequestSchema.safeParse({ firstName: 'Alice', unknown: 'x' }).success).toBe(false)
421
439
  })
@@ -743,7 +761,7 @@ describe('DealDetailResponseSchema (forward-compat)', () => {
743
761
  title: null,
744
762
  headline: null,
745
763
  linkedin_url: null,
746
- pipeline_status: null,
764
+ processing_state: null,
747
765
  enrichment_data: null,
748
766
  company: null
749
767
  }
@@ -881,6 +899,58 @@ describe('PipelineStageSchema', () => {
881
899
  })
882
900
  })
883
901
 
902
+ // ---------------------------------------------------------------------------
903
+ // BuildPlanSnapshotSchema
904
+ // ---------------------------------------------------------------------------
905
+
906
+ describe('BuildPlanSnapshotSchema', () => {
907
+ const validSnapshot = createBuildPlanSnapshotFromTemplateId('dtc-subscription-apollo-clickup')
908
+
909
+ it('accepts a snapshot generated from a prospecting build template', () => {
910
+ expect(validSnapshot).not.toBeNull()
911
+ expect(BuildPlanSnapshotSchema.safeParse(validSnapshot).success).toBe(true)
912
+ })
913
+
914
+ it('rejects a step stageKey outside LEAD_GEN_STAGE_CATALOG', () => {
915
+ const result = BuildPlanSnapshotSchema.safeParse({
916
+ ...validSnapshot,
917
+ steps: [{ ...validSnapshot!.steps[0], stageKey: 'made-up-stage' }]
918
+ })
919
+
920
+ expect(result.success).toBe(false)
921
+ })
922
+
923
+ it('rejects a step capabilityKey outside CAPABILITY_REGISTRY', () => {
924
+ const result = BuildPlanSnapshotSchema.safeParse({
925
+ ...validSnapshot,
926
+ steps: [{ ...validSnapshot!.steps[0], capabilityKey: 'lead-gen.missing.capability' }]
927
+ })
928
+
929
+ expect(result.success).toBe(false)
930
+ })
931
+
932
+ it('rejects duplicate step ids', () => {
933
+ const first = validSnapshot!.steps[0]!
934
+ const second = validSnapshot!.steps[1]!
935
+ const rest = validSnapshot!.steps.slice(2)
936
+ const result = BuildPlanSnapshotSchema.safeParse({
937
+ ...validSnapshot,
938
+ steps: [first, { ...second, id: first.id }, ...rest]
939
+ })
940
+
941
+ expect(result.success).toBe(false)
942
+ })
943
+
944
+ it('rejects dependsOn references to unknown step ids', () => {
945
+ const result = BuildPlanSnapshotSchema.safeParse({
946
+ ...validSnapshot,
947
+ steps: [{ ...validSnapshot!.steps[0], dependsOn: ['missing-step'] }]
948
+ })
949
+
950
+ expect(result.success).toBe(false)
951
+ })
952
+ })
953
+
884
954
  // ---------------------------------------------------------------------------
885
955
  // ScrapingConfigSchema
886
956
  // ---------------------------------------------------------------------------
@@ -1255,6 +1325,33 @@ describe('UpdateCompanyRequestSchema', () => {
1255
1325
  expect(UpdateCompanyRequestSchema.safeParse({ status: 'invalid' }).success).toBe(true)
1256
1326
  })
1257
1327
 
1328
+ it('accepts processingState keyed by the stage catalog', () => {
1329
+ const result = UpdateCompanyRequestSchema.safeParse({
1330
+ processingState: {
1331
+ [LEAD_GEN_STAGE_CATALOG.qualified.key]: {
1332
+ status: 'success',
1333
+ data: { score: 92 }
1334
+ }
1335
+ }
1336
+ })
1337
+
1338
+ expect(result.success).toBe(true)
1339
+ })
1340
+
1341
+ it('accepts deprecated pipelineStatus as a compatibility no-op', () => {
1342
+ expect(UpdateCompanyRequestSchema.safeParse({ pipelineStatus: 'emailed' }).success).toBe(true)
1343
+ })
1344
+
1345
+ it('rejects processingState keys outside the stage catalog', () => {
1346
+ expect(
1347
+ UpdateCompanyRequestSchema.safeParse({
1348
+ processingState: {
1349
+ madeUpStage: { status: 'success' }
1350
+ }
1351
+ }).success
1352
+ ).toBe(false)
1353
+ })
1354
+
1258
1355
  it('accepts numEmployees of 0', () => {
1259
1356
  expect(UpdateCompanyRequestSchema.safeParse({ numEmployees: 0 }).success).toBe(true)
1260
1357
  })
@@ -1468,7 +1565,7 @@ describe('AcqContactResponseSchema (forward-compat)', () => {
1468
1565
  openingLine: null,
1469
1566
  source: null,
1470
1567
  sourceId: null,
1471
- pipelineStatus: null,
1568
+ processingState: null,
1472
1569
  enrichmentData: null,
1473
1570
  attioPersonId: null,
1474
1571
  batchId: null,
@@ -1488,4 +1585,5 @@ describe('AcqContactResponseSchema (forward-compat)', () => {
1488
1585
  it('rejects an invalid emailValid value', () => {
1489
1586
  expect(AcqContactResponseSchema.safeParse({ ...baseContact, emailValid: 'BAD' }).success).toBe(false)
1490
1587
  })
1588
+
1491
1589
  })
@@ -1,10 +1,36 @@
1
1
  import { z } from 'zod'
2
2
  import { UuidSchema, NonEmptyStringSchema } from '../../platform/utils/validation'
3
3
  import { LEAD_GEN_STAGE_CATALOG } from '../../organization-model/domains/sales'
4
+ import { CAPABILITY_REGISTRY } from '../../organization-model/domains/prospecting'
4
5
  import { isProspectingBuildTemplateId } from './build-templates'
5
6
  export { CrmPriorityBucketKeySchema, CrmPriorityBucketOverrideSchema, CrmPriorityOverrideSchema } from './crm-priority'
6
7
  export type { CrmPriorityBucketOverride, CrmPriorityOverride, ResolvedCrmPriorityRuleConfig } from './crm-priority'
7
8
 
9
+ export const ProcessingStageStatusSchema = z.enum(['success', 'no_result', 'skipped', 'error'])
10
+
11
+ export const LeadGenStageKeySchema = z
12
+ .string()
13
+ .refine((value) => Object.prototype.hasOwnProperty.call(LEAD_GEN_STAGE_CATALOG, value), {
14
+ message: 'processing state key must match LEAD_GEN_STAGE_CATALOG'
15
+ })
16
+
17
+ export const LeadGenCapabilityKeySchema = z
18
+ .string()
19
+ .refine((value) => Object.prototype.hasOwnProperty.call(CAPABILITY_REGISTRY, value), {
20
+ message: 'capabilityKey must match CAPABILITY_REGISTRY'
21
+ })
22
+
23
+ export const ProcessingStateEntrySchema = z
24
+ .object({
25
+ status: ProcessingStageStatusSchema,
26
+ data: z.unknown().optional()
27
+ })
28
+ .passthrough()
29
+
30
+ export const ProcessingStateSchema = z.record(LeadGenStageKeySchema, ProcessingStateEntrySchema)
31
+ export const CompanyProcessingStateSchema = ProcessingStateSchema
32
+ export const ContactProcessingStateSchema = ProcessingStateSchema
33
+
8
34
  /**
9
35
  * Deal Management API Schemas
10
36
  *
@@ -132,7 +158,7 @@ export const DealContactSummarySchema = z.object({
132
158
  title: z.string().nullable(),
133
159
  headline: z.string().nullable(),
134
160
  linkedin_url: z.string().nullable(),
135
- pipeline_status: z.record(z.string(), z.unknown()).nullable(),
161
+ processing_state: ProcessingStateSchema.nullable(),
136
162
  enrichment_data: z.record(z.string(), z.unknown()).nullable(),
137
163
  company: z
138
164
  .object({
@@ -442,10 +468,10 @@ export const BuildPlanSnapshotStepSchema = z
442
468
  description: z.string().trim().min(1).max(2000).optional(),
443
469
  primaryEntity: z.enum(['company', 'contact']),
444
470
  outputs: z.array(z.enum(['company', 'contact', 'export'])).min(1),
445
- stageKey: z.string().trim().min(1).max(100),
471
+ stageKey: LeadGenStageKeySchema,
446
472
  dependsOn: z.array(z.string().trim().min(1).max(100)).optional(),
447
473
  dependencyMode: z.literal('per-record-eligibility'),
448
- capabilityKey: z.string().trim().min(1).max(100),
474
+ capabilityKey: LeadGenCapabilityKeySchema,
449
475
  defaultBatchSize: z.number().int().positive(),
450
476
  maxBatchSize: z.number().int().positive()
451
477
  })
@@ -454,11 +480,38 @@ export const BuildPlanSnapshotStepSchema = z
454
480
  path: ['defaultBatchSize']
455
481
  })
456
482
 
457
- export const BuildPlanSnapshotSchema = z.object({
458
- templateId: z.string().trim().min(1).max(100),
459
- templateLabel: z.string().trim().min(1).max(120),
460
- steps: z.array(BuildPlanSnapshotStepSchema).min(1)
461
- })
483
+ export const BuildPlanSnapshotSchema = z
484
+ .object({
485
+ templateId: z.string().trim().min(1).max(100),
486
+ templateLabel: z.string().trim().min(1).max(120),
487
+ steps: z.array(BuildPlanSnapshotStepSchema).min(1)
488
+ })
489
+ .superRefine((snapshot, ctx) => {
490
+ const stepIds = new Set<string>()
491
+
492
+ snapshot.steps.forEach((step, index) => {
493
+ if (stepIds.has(step.id)) {
494
+ ctx.addIssue({
495
+ code: z.ZodIssueCode.custom,
496
+ message: `duplicate build-plan step id "${step.id}"`,
497
+ path: ['steps', index, 'id']
498
+ })
499
+ }
500
+ stepIds.add(step.id)
501
+ })
502
+
503
+ snapshot.steps.forEach((step, index) => {
504
+ for (const dependencyId of step.dependsOn ?? []) {
505
+ if (!stepIds.has(dependencyId)) {
506
+ ctx.addIssue({
507
+ code: z.ZodIssueCode.custom,
508
+ message: `dependsOn references unknown build-plan step "${dependencyId}"`,
509
+ path: ['steps', index, 'dependsOn']
510
+ })
511
+ }
512
+ }
513
+ })
514
+ })
462
515
 
463
516
  export const AcqListMetadataSchema = z
464
517
  .object({
@@ -638,13 +691,6 @@ export const ListTelemetryResponseSchema = ListTelemetrySchema
638
691
 
639
692
  export const ListTelemetryListResponseSchema = z.array(ListTelemetrySchema)
640
693
 
641
- /**
642
- * Terminal row-level status for one lead-gen processing stage.
643
- * Missing key still means not attempted; legacy boolean `true` is normalized
644
- * to `success` by the API reader during rollout.
645
- */
646
- export const ProcessingStageStatusSchema = z.enum(['success', 'no_result', 'skipped', 'error'])
647
-
648
694
  /**
649
695
  * Per-stage progress aggregate for a single pipeline stage.
650
696
  * `attempted` counts terminal statuses, including success, no-result, skipped,
@@ -704,29 +750,6 @@ export const AcqCompanyStatusSchema = z.enum(['active', 'invalid'])
704
750
  export const AcqContactStatusSchema = z.enum(['active', 'invalid'])
705
751
  export const AcqEmailValidSchema = z.enum(['VALID', 'INVALID', 'RISKY', 'UNKNOWN'])
706
752
 
707
- /**
708
- * Zod schema mirroring the CompanyPipelineStatus interface from types.ts.
709
- * Used by operations-layer CompanyRecord schemas to validate pipelineStatus
710
- * against the canonical shape instead of using open passthrough().
711
- */
712
- export const CompanyPipelineStatusSchema = z
713
- .object({
714
- acquired: z.boolean().optional(),
715
- enrichment: z
716
- .record(
717
- z.string(),
718
- z
719
- .object({
720
- status: z.enum(['pending', 'complete', 'failed', 'skipped']),
721
- completedAt: z.string().optional(),
722
- error: z.string().optional()
723
- })
724
- .passthrough()
725
- )
726
- .optional()
727
- })
728
- .passthrough()
729
-
730
753
  export const CompanyIdParamsSchema = z.object({
731
754
  companyId: UuidSchema
732
755
  })
@@ -743,6 +766,7 @@ export const ListCompaniesQuerySchema = z
743
766
  website: z.string().trim().min(1).max(2048).optional(),
744
767
  segment: z.string().trim().min(1).max(255).optional(),
745
768
  category: z.string().trim().min(1).max(255).optional(),
769
+ pipelineStatus: z.unknown().optional(),
746
770
  batchId: z.string().trim().min(1).max(255).optional(),
747
771
  status: AcqCompanyStatusSchema.optional(),
748
772
  includeAll: QueryBooleanSchema.optional(),
@@ -776,6 +800,7 @@ export const CreateCompanyRequestSchema = z
776
800
  category: z.string().trim().min(1).max(255).optional(),
777
801
  source: z.string().trim().min(1).max(255).optional(),
778
802
  batchId: z.string().trim().min(1).max(255).optional(),
803
+ pipelineStatus: z.unknown().optional(),
779
804
  verticalResearch: z.string().trim().min(1).max(5000).optional()
780
805
  })
781
806
  .strict()
@@ -792,7 +817,8 @@ export const UpdateCompanyRequestSchema = z
792
817
  locationState: z.string().trim().min(1).max(255).optional(),
793
818
  category: z.string().trim().min(1).max(255).optional(),
794
819
  segment: z.string().trim().min(1).max(255).optional(),
795
- pipelineStatus: z.record(z.string(), z.unknown()).optional(),
820
+ processingState: CompanyProcessingStateSchema.optional(),
821
+ pipelineStatus: z.unknown().optional(),
796
822
  enrichmentData: z.record(z.string(), z.unknown()).optional(),
797
823
  source: z.string().trim().min(1).max(255).optional(),
798
824
  batchId: z.string().trim().min(1).max(255).optional(),
@@ -812,6 +838,7 @@ export const UpdateCompanyRequestSchema = z
812
838
  data.locationState !== undefined ||
813
839
  data.category !== undefined ||
814
840
  data.segment !== undefined ||
841
+ data.processingState !== undefined ||
815
842
  data.pipelineStatus !== undefined ||
816
843
  data.enrichmentData !== undefined ||
817
844
  data.source !== undefined ||
@@ -833,7 +860,8 @@ export const CreateContactRequestSchema = z
833
860
  title: z.string().trim().min(1).max(255).optional(),
834
861
  source: z.string().trim().min(1).max(255).optional(),
835
862
  sourceId: z.string().trim().min(1).max(255).optional(),
836
- batchId: z.string().trim().min(1).max(255).optional()
863
+ batchId: z.string().trim().min(1).max(255).optional(),
864
+ pipelineStatus: z.unknown().optional()
837
865
  })
838
866
  .strict()
839
867
 
@@ -848,7 +876,8 @@ export const UpdateContactRequestSchema = z
848
876
  headline: z.string().trim().min(1).max(5000).optional(),
849
877
  filterReason: z.string().trim().min(1).max(5000).optional(),
850
878
  openingLine: z.string().trim().min(1).max(5000).optional(),
851
- pipelineStatus: z.record(z.string(), z.unknown()).optional(),
879
+ processingState: ContactProcessingStateSchema.optional(),
880
+ pipelineStatus: z.unknown().optional(),
852
881
  enrichmentData: z.record(z.string(), z.unknown()).optional(),
853
882
  status: AcqContactStatusSchema.optional()
854
883
  })
@@ -864,6 +893,7 @@ export const UpdateContactRequestSchema = z
864
893
  data.headline !== undefined ||
865
894
  data.filterReason !== undefined ||
866
895
  data.openingLine !== undefined ||
896
+ data.processingState !== undefined ||
867
897
  data.pipelineStatus !== undefined ||
868
898
  data.enrichmentData !== undefined ||
869
899
  data.status !== undefined,
@@ -886,7 +916,8 @@ export const AcqCompanyResponseSchema = z.object({
886
916
  category: z.string().nullable(),
887
917
  categoryPain: z.string().nullable(),
888
918
  segment: z.string().nullable(),
889
- pipelineStatus: z.record(z.string(), z.unknown()).nullable(),
919
+ processingState: CompanyProcessingStateSchema.nullable(),
920
+ pipelineStatus: z.unknown().nullable().optional(),
890
921
  enrichmentData: z.record(z.string(), z.unknown()).nullable(),
891
922
  source: z.string().nullable(),
892
923
  batchId: z.string().nullable(),
@@ -936,7 +967,8 @@ export const AcqContactResponseSchema = z.object({
936
967
  openingLine: z.string().nullable(),
937
968
  source: z.string().nullable(),
938
969
  sourceId: z.string().nullable(),
939
- pipelineStatus: z.record(z.string(), z.unknown()).nullable(),
970
+ processingState: ContactProcessingStateSchema.nullable(),
971
+ pipelineStatus: z.unknown().nullable().optional(),
940
972
  enrichmentData: z.record(z.string(), z.unknown()).nullable(),
941
973
  attioPersonId: z.string().nullable(),
942
974
  batchId: z.string().nullable(),
@@ -1063,6 +1095,7 @@ export const AcqListCompanyResponseSchema = z.object({
1063
1095
  // ---------------------------------------------------------------------------
1064
1096
 
1065
1097
  export const AcqCompanySchemas = {
1098
+ CompanyProcessingState: CompanyProcessingStateSchema,
1066
1099
  CompanyIdParams: CompanyIdParamsSchema,
1067
1100
  ListCompaniesQuery: ListCompaniesQuerySchema,
1068
1101
  CreateCompanyRequest: CreateCompanyRequestSchema,
@@ -1073,6 +1106,7 @@ export const AcqCompanySchemas = {
1073
1106
  }
1074
1107
 
1075
1108
  export const AcqContactSchemas = {
1109
+ ContactProcessingState: ContactProcessingStateSchema,
1076
1110
  ContactIdParams: ContactIdParamsSchema,
1077
1111
  ListContactsQuery: ListContactsQuerySchema,
1078
1112
  CreateContactRequest: CreateContactRequestSchema,
@@ -1116,7 +1150,10 @@ export const AcqListSchemas = {
1116
1150
  BuildPlanSnapshot: BuildPlanSnapshotSchema,
1117
1151
  BuildPlanSnapshotStep: BuildPlanSnapshotStepSchema,
1118
1152
  AcqListMetadata: AcqListMetadataSchema,
1153
+ LeadGenStageKey: LeadGenStageKeySchema,
1154
+ LeadGenCapabilityKey: LeadGenCapabilityKeySchema,
1119
1155
  ProcessingStageStatus: ProcessingStageStatusSchema,
1156
+ ProcessingState: ProcessingStateSchema,
1120
1157
  ListStageCounts: ListStageCountsSchema,
1121
1158
  ListTelemetry: ListTelemetrySchema,
1122
1159
 
@@ -1195,6 +1232,7 @@ export type PipelineConfig = z.infer<typeof PipelineConfigSchema>
1195
1232
  export type BuildPlanSnapshotStep = z.infer<typeof BuildPlanSnapshotStepSchema>
1196
1233
  export type BuildPlanSnapshot = z.infer<typeof BuildPlanSnapshotSchema>
1197
1234
  export type AcqListMetadata = z.infer<typeof AcqListMetadataSchema>
1235
+ export type LeadGenCapabilityKey = z.infer<typeof LeadGenCapabilityKeySchema>
1198
1236
  export type ProcessingStageStatus = z.infer<typeof ProcessingStageStatusSchema>
1199
1237
  export type ListStageCountsInput = z.infer<typeof ListStageCountsSchema>['stageCounts']
1200
1238
  export type ListTelemetryInput = z.infer<typeof ListTelemetrySchema>