@elevasis/core 0.12.0 → 0.14.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 (50) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/index.js +9 -2
  3. package/dist/organization-model/index.d.ts +1 -1
  4. package/dist/organization-model/index.js +9 -2
  5. package/dist/test-utils/index.d.ts +480 -389
  6. package/dist/test-utils/index.js +28 -2
  7. package/package.json +1 -1
  8. package/src/_gen/__tests__/__snapshots__/contracts.md.snap +2324 -0
  9. package/src/auth/multi-tenancy/credentials/__tests__/encryption.test.ts +217 -216
  10. package/src/auth/multi-tenancy/credentials/server/encryption.ts +5 -19
  11. package/src/auth/multi-tenancy/credentials/server/kek-loader.ts +3 -13
  12. package/src/auth/multi-tenancy/permissions.ts +12 -5
  13. package/src/business/acquisition/activity-events.test.ts +250 -0
  14. package/src/business/acquisition/activity-events.ts +84 -0
  15. package/src/business/acquisition/api-schemas.test.ts +1180 -0
  16. package/src/business/acquisition/api-schemas.ts +456 -235
  17. package/src/business/acquisition/crm-state-actions.test.ts +160 -0
  18. package/src/business/acquisition/derive-actions.test.ts +518 -0
  19. package/src/business/acquisition/derive-actions.ts +103 -0
  20. package/src/business/acquisition/index.ts +51 -11
  21. package/src/business/acquisition/stateful.ts +30 -0
  22. package/src/business/acquisition/types.ts +44 -77
  23. package/src/execution/engine/index.ts +4 -1
  24. package/src/execution/engine/tools/integration/server/adapters/apify/__tests__/apify-run-actor.integration.test.ts +1 -2
  25. package/src/execution/engine/tools/integration/server/adapters/attio/__tests__/attio-crud.integration.test.ts +363 -361
  26. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/get-record/index.test.ts +162 -186
  27. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-records/index.test.ts +316 -338
  28. package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-adapter.ts +204 -210
  29. package/src/execution/engine/tools/integration/server/adapters/resend/fetch/send-email/index.test.ts +88 -0
  30. package/src/execution/engine/tools/integration/server/adapters/resend/fetch/send-email/index.ts +141 -134
  31. package/src/execution/engine/tools/integration/server/adapters/resend/fetch/utils/types.ts +76 -75
  32. package/src/execution/engine/tools/integration/service.test.ts +34 -9
  33. package/src/execution/engine/tools/integration/service.ts +6 -3
  34. package/src/execution/engine/tools/lead-service-types.ts +90 -30
  35. package/src/execution/engine/tools/platform/acquisition/types.ts +266 -260
  36. package/src/execution/engine/tools/registry.ts +5 -4
  37. package/src/execution/engine/tools/tool-maps.ts +43 -21
  38. package/src/execution/engine/workflow/types.ts +11 -0
  39. package/src/organization-model/contracts.ts +4 -4
  40. package/src/organization-model/domains/navigation.ts +62 -62
  41. package/src/organization-model/domains/sales.ts +272 -0
  42. package/src/organization-model/organization-graph.mdx +2 -2
  43. package/src/organization-model/published.ts +21 -21
  44. package/src/organization-model/resolve.ts +21 -8
  45. package/src/platform/constants/versions.ts +1 -1
  46. package/src/reference/_generated/contracts.md +2324 -0
  47. package/src/scaffold-registry/index.ts +10 -9
  48. package/src/scaffold-registry/schema.ts +68 -62
  49. package/src/supabase/database.types.ts +2958 -2884
  50. package/src/test-utils/rls/RLSTestContext.ts +585 -553
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod'
2
- import { UuidSchema } from '../../platform/utils/validation'
2
+ import { UuidSchema, NonEmptyStringSchema } from '../../platform/utils/validation'
3
+ import { LEAD_GEN_STAGE_CATALOG } from '../../organization-model/domains/sales'
3
4
 
4
5
  /**
5
6
  * Deal Management API Schemas
@@ -38,21 +39,21 @@ export const DealTaskIdParamsSchema = z.object({
38
39
  // Query schemas (coerce strings from query params)
39
40
  // ---------------------------------------------------------------------------
40
41
 
41
- export const ListDealsQuerySchema = z
42
- .object({
43
- stage: DealStageSchema.optional(),
44
- search: z.string().optional(),
45
- limit: z.coerce.number().int().positive().default(50),
46
- offset: z.coerce.number().int().min(0).default(0)
47
- })
48
- .strict()
49
-
50
- export const DealLookupQuerySchema = z
51
- .object({
52
- search: z.string().trim().min(1).max(200).optional(),
53
- limit: z.coerce.number().int().min(1).max(25).default(10)
54
- })
55
- .strict()
42
+ export const ListDealsQuerySchema = z
43
+ .object({
44
+ stage: DealStageSchema.optional(),
45
+ search: z.string().optional(),
46
+ limit: z.coerce.number().int().positive().default(50),
47
+ offset: z.coerce.number().int().min(0).default(0)
48
+ })
49
+ .strict()
50
+
51
+ export const DealLookupQuerySchema = z
52
+ .object({
53
+ search: z.string().trim().min(1).max(200).optional(),
54
+ limit: z.coerce.number().int().min(1).max(25).default(10)
55
+ })
56
+ .strict()
56
57
 
57
58
  export const ListDealTasksDueQuerySchema = z
58
59
  .object({
@@ -81,9 +82,26 @@ export const CreateDealTaskRequestSchema = z
81
82
  })
82
83
  .strict()
83
84
 
84
- export const SyncDealStageRequestSchema = z
85
+ export const TransitionItemRequestSchema = z
85
86
  .object({
86
- stage: DealStageSchema
87
+ pipelineKey: z.string().min(1),
88
+ stageKey: z.string().min(1),
89
+ stateKey: z.string().nullable().optional(),
90
+ reason: z.string().optional(),
91
+ expectedUpdatedAt: z.string().datetime().optional()
92
+ })
93
+ .strict()
94
+
95
+ export const ExecuteActionParamsSchema = z
96
+ .object({
97
+ dealId: UuidSchema,
98
+ actionKey: NonEmptyStringSchema
99
+ })
100
+ .strict()
101
+
102
+ export const ExecuteActionRequestSchema = z
103
+ .object({
104
+ payload: z.record(z.string(), z.unknown()).optional()
87
105
  })
88
106
  .strict()
89
107
 
@@ -123,19 +141,20 @@ export const DealContactSummarySchema = z.object({
123
141
  * Deal list item with joined contact (and company via contact).
124
142
  * Matches DealListItem from @repo/core types.
125
143
  */
126
- export const DealListItemSchema = z.object({
144
+ export const DealListItemSchema = z.object({
127
145
  // acq_deals columns
128
146
  id: z.string(),
129
147
  organization_id: z.string(),
130
148
  contact_id: z.string().nullable(),
131
149
  contact_email: z.string(),
132
- cached_stage: z.string().nullable(),
150
+ pipeline_key: z.string(),
151
+ stage_key: z.string().nullable(),
152
+ state_key: z.string().nullable(),
133
153
  activity_log: z.unknown(),
134
154
  discovery_data: z.unknown().nullable(),
135
155
  discovery_submitted_at: z.string().nullable(),
136
156
  discovery_submitted_by: z.string().nullable(),
137
157
  proposal_data: z.unknown().nullable(),
138
- proposal_status: z.string().nullable(),
139
158
  proposal_sent_at: z.string().nullable(),
140
159
  proposal_pdf_url: z.string().nullable(),
141
160
  signature_envelope_id: z.string().nullable(),
@@ -148,54 +167,55 @@ export const DealListItemSchema = z.object({
148
167
  created_at: z.string(),
149
168
  updated_at: z.string(),
150
169
  // joined relation
151
- contact: DealContactSummarySchema.nullable()
152
- })
153
-
154
- export const DealListResponseSchema = z.object({
155
- data: z.array(DealListItemSchema),
156
- total: z.number().int(),
157
- limit: z.number().int(),
158
- offset: z.number().int()
159
- })
160
-
161
- export const DealStageSummarySchema = z.object({
162
- stage: z.string(),
163
- count: z.number().int(),
164
- oldestUpdatedAt: z.string().nullable(),
165
- newestUpdatedAt: z.string().nullable()
166
- })
167
-
168
- export const StaleDealSummarySchema = z.object({
169
- id: z.string(),
170
- contactEmail: z.string(),
171
- cachedStage: z.string(),
172
- updatedAt: z.string(),
173
- daysStale: z.number().int()
174
- })
175
-
176
- export const DealSummaryResponseSchema = z.object({
177
- totalDeals: z.number().int(),
178
- openDeals: z.number().int(),
179
- wonDeals: z.number().int(),
180
- lostDeals: z.number().int(),
181
- winRate: z.number(),
182
- avgDealSize: z.number(),
183
- totalPipelineValue: z.number(),
184
- stageSummary: z.array(DealStageSummarySchema),
185
- staleDeals: z.array(StaleDealSummarySchema)
186
- })
187
-
188
- export const DealLookupItemSchema = z.object({
189
- id: z.string(),
190
- contactEmail: z.string(),
191
- cachedStage: z.string().nullable(),
192
- updatedAt: z.string(),
193
- contactName: z.string().nullable(),
194
- companyName: z.string().nullable(),
195
- displayLabel: z.string()
196
- })
197
-
198
- export const DealLookupResponseSchema = z.array(DealLookupItemSchema)
170
+ contact: DealContactSummarySchema.nullable()
171
+ })
172
+
173
+ export const DealListResponseSchema = z.object({
174
+ data: z.array(DealListItemSchema),
175
+ total: z.number().int(),
176
+ limit: z.number().int(),
177
+ offset: z.number().int()
178
+ })
179
+
180
+ export const DealStageSummarySchema = z.object({
181
+ stage: z.string(),
182
+ count: z.number().int(),
183
+ totalValue: z.number(),
184
+ oldestUpdatedAt: z.string().nullable(),
185
+ newestUpdatedAt: z.string().nullable()
186
+ })
187
+
188
+ export const StaleDealSummarySchema = z.object({
189
+ id: z.string(),
190
+ contactEmail: z.string(),
191
+ stageKey: z.string(),
192
+ updatedAt: z.string(),
193
+ daysStale: z.number().int()
194
+ })
195
+
196
+ export const DealSummaryResponseSchema = z.object({
197
+ totalDeals: z.number().int(),
198
+ openDeals: z.number().int(),
199
+ wonDeals: z.number().int(),
200
+ lostDeals: z.number().int(),
201
+ winRate: z.number(),
202
+ avgDealSize: z.number(),
203
+ totalPipelineValue: z.number(),
204
+ stageSummary: z.array(DealStageSummarySchema),
205
+ staleDeals: z.array(StaleDealSummarySchema)
206
+ })
207
+
208
+ export const DealLookupItemSchema = z.object({
209
+ id: z.string(),
210
+ contactEmail: z.string(),
211
+ stageKey: z.string().nullable(),
212
+ updatedAt: z.string(),
213
+ contactName: z.string().nullable(),
214
+ companyName: z.string().nullable(),
215
+ displayLabel: z.string()
216
+ })
217
+
218
+ export const DealLookupResponseSchema = z.array(DealLookupItemSchema)
199
219
 
200
220
  /**
201
221
  * Deal detail shape — currently the same as a list item (full joined record).
@@ -250,20 +270,22 @@ export const DealSchemas = {
250
270
  DealTaskIdParams: DealTaskIdParamsSchema,
251
271
 
252
272
  // Queries
253
- ListDealsQuery: ListDealsQuerySchema,
254
- DealLookupQuery: DealLookupQuerySchema,
255
- ListDealTasksDueQuery: ListDealTasksDueQuerySchema,
273
+ ListDealsQuery: ListDealsQuerySchema,
274
+ DealLookupQuery: DealLookupQuerySchema,
275
+ ListDealTasksDueQuery: ListDealTasksDueQuerySchema,
256
276
 
257
277
  // Request bodies
258
278
  CreateDealNoteRequest: CreateDealNoteRequestSchema,
259
279
  CreateDealTaskRequest: CreateDealTaskRequestSchema,
260
- SyncDealStageRequest: SyncDealStageRequestSchema,
280
+ TransitionItemRequest: TransitionItemRequestSchema,
281
+ ExecuteActionParams: ExecuteActionParamsSchema,
282
+ ExecuteActionRequest: ExecuteActionRequestSchema,
261
283
 
262
284
  // Responses
263
- DealListResponse: DealListResponseSchema,
264
- DealSummaryResponse: DealSummaryResponseSchema,
265
- DealLookupResponse: DealLookupResponseSchema,
266
- DealDetailResponse: DealDetailResponseSchema,
285
+ DealListResponse: DealListResponseSchema,
286
+ DealSummaryResponse: DealSummaryResponseSchema,
287
+ DealLookupResponse: DealLookupResponseSchema,
288
+ DealDetailResponse: DealDetailResponseSchema,
267
289
  DealNoteResponse: DealNoteResponseSchema,
268
290
  DealNoteListResponse: DealNoteListResponseSchema,
269
291
  DealTaskResponse: DealTaskResponseSchema,
@@ -278,17 +300,19 @@ export type DealStage = z.infer<typeof DealStageSchema>
278
300
  export type AcqDealTaskKind = z.infer<typeof AcqDealTaskKindSchema>
279
301
  export type DealIdParams = z.infer<typeof DealIdParamsSchema>
280
302
  export type DealTaskIdParams = z.infer<typeof DealTaskIdParamsSchema>
281
- export type ListDealsQuery = z.infer<typeof ListDealsQuerySchema>
282
- export type DealLookupQuery = z.infer<typeof DealLookupQuerySchema>
283
- export type ListDealTasksDueQuery = z.infer<typeof ListDealTasksDueQuerySchema>
303
+ export type ListDealsQuery = z.infer<typeof ListDealsQuerySchema>
304
+ export type DealLookupQuery = z.infer<typeof DealLookupQuerySchema>
305
+ export type ListDealTasksDueQuery = z.infer<typeof ListDealTasksDueQuerySchema>
284
306
  export type CreateDealNoteRequest = z.infer<typeof CreateDealNoteRequestSchema>
285
307
  export type CreateDealTaskRequest = z.infer<typeof CreateDealTaskRequestSchema>
286
- export type SyncDealStageRequest = z.infer<typeof SyncDealStageRequestSchema>
287
- export type DealListResponse = z.infer<typeof DealListResponseSchema>
288
- export type DealSummaryResponse = z.infer<typeof DealSummaryResponseSchema>
289
- export type DealLookupItem = z.infer<typeof DealLookupItemSchema>
290
- export type DealLookupResponse = z.infer<typeof DealLookupResponseSchema>
291
- export type DealDetailResponse = z.infer<typeof DealDetailResponseSchema>
308
+ export type TransitionItemRequest = z.infer<typeof TransitionItemRequestSchema>
309
+ export type ExecuteActionParams = z.infer<typeof ExecuteActionParamsSchema>
310
+ export type ExecuteActionRequest = z.infer<typeof ExecuteActionRequestSchema>
311
+ export type DealListResponse = z.infer<typeof DealListResponseSchema>
312
+ export type DealSummaryResponse = z.infer<typeof DealSummaryResponseSchema>
313
+ export type DealLookupItem = z.infer<typeof DealLookupItemSchema>
314
+ export type DealLookupResponse = z.infer<typeof DealLookupResponseSchema>
315
+ export type DealDetailResponse = z.infer<typeof DealDetailResponseSchema>
292
316
  export type DealNoteResponse = z.infer<typeof DealNoteResponseSchema>
293
317
  export type DealNoteListResponse = z.infer<typeof DealNoteListResponseSchema>
294
318
  export type DealTaskResponse = z.infer<typeof DealTaskResponseSchema>
@@ -308,61 +332,61 @@ export type DealTaskListResponse = z.infer<typeof DealTaskListResponseSchema>
308
332
  // ---------------------------------------------------------------------------
309
333
 
310
334
  // ---------------------------------------------------------------------------
311
- // Primitives — list config subtrees
335
+ // Primitives — list status enum + jsonb config schemas
312
336
  // ---------------------------------------------------------------------------
313
337
 
314
- export const ListQualificationSchema = z.object({
315
- targetDescription: z.string(),
316
- minReviewCount: z.number().int().min(0),
317
- minRating: z.number().min(0).max(5),
318
- excludeFranchises: z.boolean(),
319
- customRules: z.string()
320
- })
321
-
322
- export const ListEnrichmentSchema = z.object({
323
- emailDiscovery: z
324
- .object({
325
- primary: z.enum(['tomba', 'anymailfinder']),
326
- credentialName: z.string().optional()
327
- })
328
- .optional(),
329
- emailVerification: z
330
- .object({
331
- provider: z.literal('millionverifier'),
332
- threshold: z.enum(['ok', 'ok+catch_all']).optional()
333
- })
334
- .optional()
335
- })
338
+ /**
339
+ * Lifecycle status enum for `acq_lists.status` (mirrors DB CHECK constraint
340
+ * from migration 20260428000003_lead_gen_acq_lists_status_and_config.sql).
341
+ */
342
+ export const ListStatusSchema = z.enum(['draft', 'enriching', 'launched', 'closing', 'archived'])
336
343
 
337
- export const ListPersonalizationSchema = z.object({
338
- industryContext: z.string().optional(),
339
- emailBody: z.string().optional(),
340
- creativeDirection: z.string().optional(),
341
- exclusionRules: z.array(z.string()).optional()
344
+ /**
345
+ * Scraping criteria stored in `acq_lists.scraping_config` jsonb.
346
+ * Edited via the UI; consumed by lgn-01 prospecting workflows (Apify input shape,
347
+ * geography, vertical, size). All fields are optional — empty config is valid.
348
+ */
349
+ export const ScrapingConfigSchema = z.object({
350
+ vertical: z.string().trim().max(255).optional(),
351
+ geography: z.string().trim().max(500).optional(),
352
+ size: z.string().trim().max(255).optional(),
353
+ apifyInput: z.record(z.string(), z.unknown()).optional()
342
354
  })
343
355
 
344
- export const PipelineStepSchema = z.object({
345
- key: z.string(),
346
- label: z.string(),
347
- resourceId: z.string(),
348
- inputTemplate: z.record(z.string(), z.unknown()),
349
- enabled: z.boolean(),
350
- order: z.number().int()
356
+ /**
357
+ * ICP / qualification rubric stored in `acq_lists.icp` jsonb.
358
+ * Replaces the legacy `config.qualification` blob. Consumed by the
359
+ * company-qualification workflow.
360
+ */
361
+ export const IcpRubricSchema = z.object({
362
+ qualificationRubricKey: z.string().trim().max(255).nullish(),
363
+ targetDescription: z.string().optional(),
364
+ minReviewCount: z.number().int().min(0).optional(),
365
+ minRating: z.number().min(0).max(5).optional(),
366
+ excludeFranchises: z.boolean().optional(),
367
+ customRules: z.string().optional()
351
368
  })
352
369
 
353
- export const ListPipelineSchema = z.object({
354
- steps: z.array(PipelineStepSchema)
370
+ /**
371
+ * One stage entry in a list's `pipeline_config.stages[]`. The `key` is
372
+ * validated against `LEAD_GEN_STAGE_CATALOG` so list pipeline definitions
373
+ * stay aligned with the org-os semantic layer.
374
+ */
375
+ export const PipelineStageSchema = z.object({
376
+ key: z.string().refine((value) => Object.prototype.hasOwnProperty.call(LEAD_GEN_STAGE_CATALOG, value), {
377
+ message: 'pipeline stage key must match LEAD_GEN_STAGE_CATALOG'
378
+ }),
379
+ label: z.string().optional(),
380
+ enabled: z.boolean().optional(),
381
+ order: z.number().int().optional()
355
382
  })
356
383
 
357
384
  /**
358
- * Full ListConfig shape. `qualification` is required; everything else optional.
359
- * Matches `acq_lists.config` jsonb and ListConfig type in types.ts.
385
+ * Pipeline presentation contract stored in `acq_lists.pipeline_config` jsonb.
386
+ * `stages[].key` validates against the catalog; the rest is presentation only.
360
387
  */
361
- export const ListConfigSchema = z.object({
362
- qualification: ListQualificationSchema,
363
- enrichment: ListEnrichmentSchema.optional(),
364
- personalization: ListPersonalizationSchema.optional(),
365
- pipeline: ListPipelineSchema.optional()
388
+ export const PipelineConfigSchema = z.object({
389
+ stages: z.array(PipelineStageSchema).optional()
366
390
  })
367
391
 
368
392
  // ---------------------------------------------------------------------------
@@ -413,44 +437,49 @@ export const CreateListRequestSchema = z
413
437
  .object({
414
438
  name: z.string().trim().min(1).max(255),
415
439
  description: z.string().trim().nullable().optional(),
416
- type: z.string().default('manual'),
417
- config: ListConfigSchema.optional()
440
+ status: ListStatusSchema.optional(),
441
+ scrapingConfig: ScrapingConfigSchema.optional(),
442
+ icp: IcpRubricSchema.optional(),
443
+ pipelineConfig: PipelineConfigSchema.optional()
444
+ })
445
+ .strict()
446
+
447
+ export const UpdateListRequestSchema = z
448
+ .object({
449
+ name: z.string().trim().min(1).max(255).optional(),
450
+ description: z.string().trim().nullable().optional(),
451
+ batchIds: z.array(z.string()).optional()
418
452
  })
419
453
  .strict()
454
+ .refine((data) => data.name !== undefined || data.description !== undefined || data.batchIds !== undefined, {
455
+ message: 'At least one field (name, description, or batchIds) must be provided'
456
+ })
420
457
 
421
- export const UpdateListRequestSchema = z
422
- .object({
423
- name: z.string().trim().min(1).max(255).optional(),
424
- description: z.string().trim().nullable().optional(),
425
- status: z.string().optional(),
426
- batchIds: z.array(z.string()).optional()
427
- })
428
- .strict()
429
- .refine(
430
- (data) =>
431
- data.name !== undefined ||
432
- data.description !== undefined ||
433
- data.status !== undefined ||
434
- data.batchIds !== undefined,
435
- {
436
- message: 'At least one field (name, description, status, or batchIds) must be provided'
437
- }
438
- )
458
+ /**
459
+ * Status-only PATCH body for `/acquisition/lists/:listId/status`.
460
+ * Replaces the previous `transitionList` flow.
461
+ */
462
+ export const UpdateListStatusRequestSchema = z
463
+ .object({
464
+ status: ListStatusSchema
465
+ })
466
+ .strict()
439
467
 
440
468
  /**
441
- * Partial patch for list.config UI sends only the edited tab's subtree.
442
- * Zod v4: use .partial() on each subtree and remake the root schema.
443
- * Since the root ListConfigSchema marks `qualification` as required, we must
444
- * produce a manually-built deep-partial that makes qualification optional too.
469
+ * Partial patch for the three jsonb config columns. UI sends only the edited
470
+ * subtree; server writes the field as-is (no deep merge each column is
471
+ * replaced atomically when present in the patch).
445
472
  */
446
473
  export const UpdateListConfigRequestSchema = z
447
474
  .object({
448
- qualification: ListQualificationSchema.partial().optional(),
449
- enrichment: ListEnrichmentSchema.partial().optional(),
450
- personalization: ListPersonalizationSchema.partial().optional(),
451
- pipeline: ListPipelineSchema.partial().optional()
475
+ scrapingConfig: ScrapingConfigSchema.partial().optional(),
476
+ icp: IcpRubricSchema.partial().optional(),
477
+ pipelineConfig: PipelineConfigSchema.partial().optional()
452
478
  })
453
479
  .strict()
480
+ .refine((data) => data.scrapingConfig !== undefined || data.icp !== undefined || data.pipelineConfig !== undefined, {
481
+ message: 'At least one of scrapingConfig, icp, or pipelineConfig must be provided'
482
+ })
454
483
 
455
484
  export const AddCompaniesToListRequestSchema = z
456
485
  .object({
@@ -493,12 +522,18 @@ export const AcqListResponseSchema = z.object({
493
522
  type: z.string(),
494
523
  batchIds: z.array(z.string()),
495
524
  instantlyCampaignId: z.string().nullable(),
496
- status: z.string(),
525
+ /** Lifecycle status (draft | enriching | launched | closing | archived). */
526
+ status: ListStatusSchema,
497
527
  metadata: z.record(z.string(), z.unknown()),
498
528
  launchedAt: z.string().nullable(),
499
529
  completedAt: z.string().nullable(),
500
530
  createdAt: z.string(),
501
- config: ListConfigSchema
531
+ /** Scraping criteria stored as jsonb on the row. */
532
+ scrapingConfig: ScrapingConfigSchema,
533
+ /** ICP / qualification rubric stored as jsonb on the row. */
534
+ icp: IcpRubricSchema,
535
+ /** Pipeline presentation contract stored as jsonb on the row. */
536
+ pipelineConfig: PipelineConfigSchema
502
537
  })
503
538
 
504
539
  export const AcqListListResponseSchema = z.array(AcqListResponseSchema)
@@ -507,6 +542,28 @@ export const ListTelemetryResponseSchema = ListTelemetrySchema
507
542
 
508
543
  export const ListTelemetryListResponseSchema = z.array(ListTelemetrySchema)
509
544
 
545
+ /**
546
+ * Per-stage progress aggregate for a single pipeline stage.
547
+ * `done` = count of members/companies where `(processing_state->>'<key>')::boolean` is true.
548
+ * `total` = total member/company count for the list.
549
+ */
550
+ export const ListStageProgressSchema = z.object({
551
+ done: z.number().int().min(0),
552
+ total: z.number().int().min(0)
553
+ })
554
+
555
+ /**
556
+ * Progress response for GET /acquisition/lists/:listId/progress.
557
+ * Aggregated on-demand via COUNT(*) FILTER over processing_state flags (Decision #4).
558
+ * `byStage` keys are driven by the list's pipeline_config.stages[].key.
559
+ */
560
+ export const ListProgressResponseSchema = z.object({
561
+ totalMembers: z.number().int().min(0),
562
+ totalCompanies: z.number().int().min(0),
563
+ byCompanyStage: z.record(z.string(), ListStageProgressSchema),
564
+ byContactStage: z.record(z.string(), ListStageProgressSchema)
565
+ })
566
+
510
567
  /**
511
568
  * Row from acq_list_executions joined with the execution summary,
512
569
  * shaped for the /lists/:id/executions response.
@@ -544,21 +601,21 @@ export const ContactIdParamsSchema = z.object({
544
601
  contactId: UuidSchema
545
602
  })
546
603
 
547
- export const ListCompaniesQuerySchema = z
548
- .object({
549
- search: z.string().trim().min(1).max(200).optional(),
550
- listId: UuidSchema.optional(),
551
- domain: z.string().trim().min(1).max(255).optional(),
604
+ export const ListCompaniesQuerySchema = z
605
+ .object({
606
+ search: z.string().trim().min(1).max(200).optional(),
607
+ listId: UuidSchema.optional(),
608
+ domain: z.string().trim().min(1).max(255).optional(),
552
609
  website: z.string().trim().min(1).max(2048).optional(),
553
610
  segment: z.string().trim().min(1).max(255).optional(),
554
- category: z.string().trim().min(1).max(255).optional(),
555
- batchId: z.string().trim().min(1).max(255).optional(),
556
- status: AcqCompanyStatusSchema.optional(),
557
- includeAll: QueryBooleanSchema.optional(),
558
- limit: z.coerce.number().int().min(1).max(5000).default(50),
559
- offset: z.coerce.number().int().min(0).default(0)
560
- })
561
- .strict()
611
+ category: z.string().trim().min(1).max(255).optional(),
612
+ batchId: z.string().trim().min(1).max(255).optional(),
613
+ status: AcqCompanyStatusSchema.optional(),
614
+ includeAll: QueryBooleanSchema.optional(),
615
+ limit: z.coerce.number().int().min(1).max(5000).default(50),
616
+ offset: z.coerce.number().int().min(0).default(0)
617
+ })
618
+ .strict()
562
619
 
563
620
  export const ListContactsQuerySchema = z
564
621
  .object({
@@ -681,10 +738,10 @@ export const UpdateContactRequestSchema = z
681
738
  }
682
739
  )
683
740
 
684
- export const AcqCompanyResponseSchema = z.object({
685
- id: z.string(),
686
- organizationId: z.string(),
687
- name: z.string(),
741
+ export const AcqCompanyResponseSchema = z.object({
742
+ id: z.string(),
743
+ organizationId: z.string(),
744
+ name: z.string(),
688
745
  domain: z.string().nullable(),
689
746
  linkedinUrl: z.string().nullable(),
690
747
  website: z.string().nullable(),
@@ -698,41 +755,41 @@ export const AcqCompanyResponseSchema = z.object({
698
755
  pipelineStatus: z.record(z.string(), z.unknown()).nullable(),
699
756
  enrichmentData: z.record(z.string(), z.unknown()).nullable(),
700
757
  source: z.string().nullable(),
701
- batchId: z.string().nullable(),
702
- status: AcqCompanyStatusSchema,
703
- contactCount: z.number().int().min(0),
704
- verticalResearch: z.string().nullable(),
705
- createdAt: z.string(),
706
- updatedAt: z.string()
707
- })
708
-
709
- export const AcqCompanyListResponseSchema = z.object({
710
- data: z.array(AcqCompanyResponseSchema),
711
- total: z.number().int(),
712
- limit: z.number().int(),
713
- offset: z.number().int()
714
- })
715
-
716
- export const AcqCompanyFacetsResponseSchema = z.object({
717
- segments: z.array(z.string()),
718
- categories: z.array(z.string()),
719
- statuses: z.array(AcqCompanyStatusSchema)
720
- })
721
-
722
- export const AcqContactCompanySummarySchema = z.object({
723
- id: z.string(),
724
- name: z.string(),
725
- domain: z.string().nullable(),
726
- website: z.string().nullable(),
727
- linkedinUrl: z.string().nullable(),
728
- segment: z.string().nullable(),
729
- category: z.string().nullable(),
730
- status: AcqCompanyStatusSchema
731
- })
732
-
733
- export const AcqContactResponseSchema = z.object({
734
- id: z.string(),
735
- organizationId: z.string(),
758
+ batchId: z.string().nullable(),
759
+ status: AcqCompanyStatusSchema,
760
+ contactCount: z.number().int().min(0),
761
+ verticalResearch: z.string().nullable(),
762
+ createdAt: z.string(),
763
+ updatedAt: z.string()
764
+ })
765
+
766
+ export const AcqCompanyListResponseSchema = z.object({
767
+ data: z.array(AcqCompanyResponseSchema),
768
+ total: z.number().int(),
769
+ limit: z.number().int(),
770
+ offset: z.number().int()
771
+ })
772
+
773
+ export const AcqCompanyFacetsResponseSchema = z.object({
774
+ segments: z.array(z.string()),
775
+ categories: z.array(z.string()),
776
+ statuses: z.array(AcqCompanyStatusSchema)
777
+ })
778
+
779
+ export const AcqContactCompanySummarySchema = z.object({
780
+ id: z.string(),
781
+ name: z.string(),
782
+ domain: z.string().nullable(),
783
+ website: z.string().nullable(),
784
+ linkedinUrl: z.string().nullable(),
785
+ segment: z.string().nullable(),
786
+ category: z.string().nullable(),
787
+ status: AcqCompanyStatusSchema
788
+ })
789
+
790
+ export const AcqContactResponseSchema = z.object({
791
+ id: z.string(),
792
+ organizationId: z.string(),
736
793
  companyId: z.string().nullable(),
737
794
  email: z.string(),
738
795
  emailValid: AcqEmailValidSchema.nullable(),
@@ -747,13 +804,13 @@ export const AcqContactResponseSchema = z.object({
747
804
  sourceId: z.string().nullable(),
748
805
  pipelineStatus: z.record(z.string(), z.unknown()).nullable(),
749
806
  enrichmentData: z.record(z.string(), z.unknown()).nullable(),
750
- attioPersonId: z.string().nullable(),
751
- batchId: z.string().nullable(),
752
- status: AcqContactStatusSchema,
753
- company: AcqContactCompanySummarySchema.nullable().optional(),
754
- createdAt: z.string(),
755
- updatedAt: z.string()
756
- })
807
+ attioPersonId: z.string().nullable(),
808
+ batchId: z.string().nullable(),
809
+ status: AcqContactStatusSchema,
810
+ company: AcqContactCompanySummarySchema.nullable().optional(),
811
+ createdAt: z.string(),
812
+ updatedAt: z.string()
813
+ })
757
814
 
758
815
  export const AcqContactListResponseSchema = z.object({
759
816
  data: z.array(AcqContactResponseSchema),
@@ -762,15 +819,124 @@ export const AcqContactListResponseSchema = z.object({
762
819
  offset: z.number().int()
763
820
  })
764
821
 
822
+ // ---------------------------------------------------------------------------
823
+ // Track A: Artifacts API Schemas
824
+ // ---------------------------------------------------------------------------
825
+
826
+ export const AcqArtifactOwnerKindSchema = z.enum(['company', 'contact', 'deal', 'list', 'list_member'])
827
+
828
+ export const ListArtifactsQuerySchema = z
829
+ .object({
830
+ ownerKind: AcqArtifactOwnerKindSchema,
831
+ ownerId: UuidSchema
832
+ })
833
+ .strict()
834
+
835
+ export const CreateArtifactRequestSchema = z
836
+ .object({
837
+ ownerKind: AcqArtifactOwnerKindSchema,
838
+ ownerId: UuidSchema,
839
+ kind: z.string().trim().min(1).max(255),
840
+ content: z.record(z.string(), z.unknown()),
841
+ sourceExecutionId: UuidSchema.optional()
842
+ })
843
+ .strict()
844
+
845
+ export const AcqArtifactResponseSchema = z.object({
846
+ id: z.string(),
847
+ organizationId: z.string(),
848
+ ownerKind: z.string(),
849
+ ownerId: z.string(),
850
+ kind: z.string(),
851
+ content: z.record(z.string(), z.unknown()),
852
+ sourceExecutionId: z.string().nullable(),
853
+ createdBy: z.string().nullable(),
854
+ createdAt: z.string(),
855
+ version: z.number().int()
856
+ })
857
+
858
+ export const AcqArtifactListResponseSchema = z.object({
859
+ artifacts: z.array(AcqArtifactResponseSchema)
860
+ })
861
+
862
+ // ---------------------------------------------------------------------------
863
+ // Track B: List Members API Schemas
864
+ // ---------------------------------------------------------------------------
865
+
866
+ export const ListMembersQuerySchema = z
867
+ .object({
868
+ limit: z.coerce.number().int().min(1).max(500).default(50),
869
+ offset: z.coerce.number().int().min(0).default(0)
870
+ })
871
+ .strict()
872
+
873
+ export const MemberIdParamsSchema = z.object({
874
+ memberId: UuidSchema
875
+ })
876
+
877
+ export const AcqListMemberContactSummarySchema = z.object({
878
+ id: z.string(),
879
+ email: z.string(),
880
+ firstName: z.string().nullable(),
881
+ lastName: z.string().nullable(),
882
+ title: z.string().nullable(),
883
+ linkedinUrl: z.string().nullable(),
884
+ companyId: z.string().nullable()
885
+ })
886
+
887
+ export const AcqListMemberResponseSchema = z.object({
888
+ id: z.string(),
889
+ listId: z.string(),
890
+ contactId: z.string(),
891
+ pipelineKey: z.string(),
892
+ stageKey: z.string(),
893
+ stateKey: z.string(),
894
+ activityLog: z.unknown(),
895
+ addedAt: z.string(),
896
+ addedBy: z.string().nullable(),
897
+ sourceExecutionId: z.string().nullable(),
898
+ contact: AcqListMemberContactSummarySchema.nullable()
899
+ })
900
+
901
+ export const AcqListMembersResponseSchema = z.object({
902
+ members: z.array(AcqListMemberResponseSchema)
903
+ })
904
+
905
+ // ---------------------------------------------------------------------------
906
+ // Track B: List Companies API Schemas
907
+ // ---------------------------------------------------------------------------
908
+
909
+ export const ListCompanyIdParamsSchema = z.object({
910
+ listCompanyId: UuidSchema
911
+ })
912
+
913
+ export const AcqListCompanyResponseSchema = z.object({
914
+ id: z.string(),
915
+ listId: z.string(),
916
+ companyId: z.string(),
917
+ pipelineKey: z.string(),
918
+ stageKey: z.string(),
919
+ stateKey: z.string(),
920
+ activityLog: z.unknown(),
921
+ addedAt: z.string(),
922
+ addedBy: z.string().nullable(),
923
+ sourceExecutionId: z.string().nullable()
924
+ })
925
+
926
+ // ---------------------------------------------------------------------------
927
+ // Track B: Transition Request (shared by list, list-member, list-company)
928
+ // TransitionItemRequestSchema already exists above (for deals) — reuse it.
929
+ // ---------------------------------------------------------------------------
930
+
765
931
  export const AcqCompanySchemas = {
766
932
  CompanyIdParams: CompanyIdParamsSchema,
767
933
  ListCompaniesQuery: ListCompaniesQuerySchema,
768
934
  CreateCompanyRequest: CreateCompanyRequestSchema,
769
- UpdateCompanyRequest: UpdateCompanyRequestSchema,
770
- AcqCompanyResponse: AcqCompanyResponseSchema,
771
- AcqCompanyListResponse: AcqCompanyListResponseSchema,
772
- AcqCompanyFacetsResponse: AcqCompanyFacetsResponseSchema
773
- }
935
+ UpdateCompanyRequest: UpdateCompanyRequestSchema,
936
+ AcqCompanyResponse: AcqCompanyResponseSchema,
937
+ AcqCompanyListResponse: AcqCompanyListResponseSchema,
938
+ AcqCompanyFacetsResponse: AcqCompanyFacetsResponseSchema
939
+ }
774
940
 
775
941
  export const AcqContactSchemas = {
776
942
  ContactIdParams: ContactIdParamsSchema,
@@ -792,12 +958,12 @@ export type CreateCompanyRequest = z.infer<typeof CreateCompanyRequestSchema>
792
958
  export type UpdateCompanyRequest = z.infer<typeof UpdateCompanyRequestSchema>
793
959
  export type CreateContactRequest = z.infer<typeof CreateContactRequestSchema>
794
960
  export type UpdateContactRequest = z.infer<typeof UpdateContactRequestSchema>
795
- export type AcqCompanyResponse = z.infer<typeof AcqCompanyResponseSchema>
796
- export type AcqCompanyListResponse = z.infer<typeof AcqCompanyListResponseSchema>
797
- export type AcqCompanyFacetsResponse = z.infer<typeof AcqCompanyFacetsResponseSchema>
798
- export type AcqContactCompanySummary = z.infer<typeof AcqContactCompanySummarySchema>
799
- export type AcqContactResponse = z.infer<typeof AcqContactResponseSchema>
800
- export type AcqContactListResponse = z.infer<typeof AcqContactListResponseSchema>
961
+ export type AcqCompanyResponse = z.infer<typeof AcqCompanyResponseSchema>
962
+ export type AcqCompanyListResponse = z.infer<typeof AcqCompanyListResponseSchema>
963
+ export type AcqCompanyFacetsResponse = z.infer<typeof AcqCompanyFacetsResponseSchema>
964
+ export type AcqContactCompanySummary = z.infer<typeof AcqContactCompanySummarySchema>
965
+ export type AcqContactResponse = z.infer<typeof AcqContactResponseSchema>
966
+ export type AcqContactListResponse = z.infer<typeof AcqContactListResponseSchema>
801
967
 
802
968
  // ---------------------------------------------------------------------------
803
969
  // Bundled export
@@ -808,14 +974,18 @@ export const AcqListSchemas = {
808
974
  ListIdParams: ListIdParamsSchema,
809
975
 
810
976
  // Primitives (for UI / tests)
811
- ListConfig: ListConfigSchema,
977
+ ListStatus: ListStatusSchema,
978
+ ScrapingConfig: ScrapingConfigSchema,
979
+ IcpRubric: IcpRubricSchema,
980
+ PipelineConfig: PipelineConfigSchema,
981
+ PipelineStage: PipelineStageSchema,
812
982
  ListStageCounts: ListStageCountsSchema,
813
983
  ListTelemetry: ListTelemetrySchema,
814
- PipelineStep: PipelineStepSchema,
815
984
 
816
985
  // Requests
817
986
  CreateListRequest: CreateListRequestSchema,
818
987
  UpdateListRequest: UpdateListRequestSchema,
988
+ UpdateListStatusRequest: UpdateListStatusRequestSchema,
819
989
  UpdateListConfigRequest: UpdateListConfigRequestSchema,
820
990
  AddCompaniesToListRequest: AddCompaniesToListRequestSchema,
821
991
  RemoveCompaniesFromListRequest: RemoveCompaniesFromListRequestSchema,
@@ -827,20 +997,69 @@ export const AcqListSchemas = {
827
997
  AcqListListResponse: AcqListListResponseSchema,
828
998
  ListTelemetryResponse: ListTelemetryResponseSchema,
829
999
  ListTelemetryListResponse: ListTelemetryListResponseSchema,
830
- ListExecutionsResponse: ListExecutionsResponseSchema
1000
+ ListExecutionsResponse: ListExecutionsResponseSchema,
1001
+ ListProgressResponse: ListProgressResponseSchema
1002
+ }
1003
+
1004
+ // ---------------------------------------------------------------------------
1005
+ // Track A/B bundled schemas
1006
+ // ---------------------------------------------------------------------------
1007
+
1008
+ export const AcqSubstrateSchemas = {
1009
+ // Artifacts
1010
+ ListArtifactsQuery: ListArtifactsQuerySchema,
1011
+ CreateArtifactRequest: CreateArtifactRequestSchema,
1012
+ AcqArtifactResponse: AcqArtifactResponseSchema,
1013
+ AcqArtifactListResponse: AcqArtifactListResponseSchema,
1014
+
1015
+ // List members
1016
+ ListMembersQuery: ListMembersQuerySchema,
1017
+ MemberIdParams: MemberIdParamsSchema,
1018
+ AcqListMemberResponse: AcqListMemberResponseSchema,
1019
+ AcqListMembersResponse: AcqListMembersResponseSchema,
1020
+
1021
+ // List companies
1022
+ ListCompanyIdParams: ListCompanyIdParamsSchema,
1023
+ AcqListCompanyResponse: AcqListCompanyResponseSchema,
1024
+
1025
+ // Transition (shared with deals — TransitionItemRequestSchema)
1026
+ TransitionItemRequest: TransitionItemRequestSchema
831
1027
  }
832
1028
 
833
1029
  // ---------------------------------------------------------------------------
834
1030
  // Inferred types
835
1031
  // ---------------------------------------------------------------------------
836
1032
 
837
- export type ListConfigInput = z.infer<typeof ListConfigSchema>
1033
+ // ---------------------------------------------------------------------------
1034
+ // Inferred types — Track A/B substrate
1035
+ // ---------------------------------------------------------------------------
1036
+
1037
+ export type AcqArtifactOwnerKind = z.infer<typeof AcqArtifactOwnerKindSchema>
1038
+ export type ListArtifactsQuery = z.infer<typeof ListArtifactsQuerySchema>
1039
+ export type CreateArtifactRequest = z.infer<typeof CreateArtifactRequestSchema>
1040
+ export type AcqArtifactResponse = z.infer<typeof AcqArtifactResponseSchema>
1041
+ export type AcqArtifactListResponse = z.infer<typeof AcqArtifactListResponseSchema>
1042
+ export type ListMembersQuery = z.infer<typeof ListMembersQuerySchema>
1043
+ export type MemberIdParams = z.infer<typeof MemberIdParamsSchema>
1044
+ export type AcqListMemberContactSummary = z.infer<typeof AcqListMemberContactSummarySchema>
1045
+ export type AcqListMemberResponse = z.infer<typeof AcqListMemberResponseSchema>
1046
+ export type AcqListMembersResponse = z.infer<typeof AcqListMembersResponseSchema>
1047
+ export type ListCompanyIdParams = z.infer<typeof ListCompanyIdParamsSchema>
1048
+ export type AcqListCompanyResponse = z.infer<typeof AcqListCompanyResponseSchema>
1049
+
1050
+ // ---------------------------------------------------------------------------
1051
+
1052
+ export type ListStatus = z.infer<typeof ListStatusSchema>
1053
+ export type ScrapingConfig = z.infer<typeof ScrapingConfigSchema>
1054
+ export type IcpRubric = z.infer<typeof IcpRubricSchema>
1055
+ export type PipelineStage = z.infer<typeof PipelineStageSchema>
1056
+ export type PipelineConfig = z.infer<typeof PipelineConfigSchema>
838
1057
  export type ListStageCountsInput = z.infer<typeof ListStageCountsSchema>['stageCounts']
839
1058
  export type ListTelemetryInput = z.infer<typeof ListTelemetrySchema>
840
- export type PipelineStepInput = z.infer<typeof PipelineStepSchema>
841
1059
  export type ListIdParams = z.infer<typeof ListIdParamsSchema>
842
1060
  export type CreateListRequest = z.infer<typeof CreateListRequestSchema>
843
1061
  export type UpdateListRequest = z.infer<typeof UpdateListRequestSchema>
1062
+ export type UpdateListStatusRequest = z.infer<typeof UpdateListStatusRequestSchema>
844
1063
  export type UpdateListConfigRequest = z.infer<typeof UpdateListConfigRequestSchema>
845
1064
  export type AddCompaniesToListRequest = z.infer<typeof AddCompaniesToListRequestSchema>
846
1065
  export type RemoveCompaniesFromListRequest = z.infer<typeof RemoveCompaniesFromListRequestSchema>
@@ -852,3 +1071,5 @@ export type ListTelemetryResponse = z.infer<typeof ListTelemetryResponseSchema>
852
1071
  export type ListTelemetryListResponse = z.infer<typeof ListTelemetryListResponseSchema>
853
1072
  export type ListExecutionSummaryInput = z.infer<typeof ListExecutionSummarySchema>
854
1073
  export type ListExecutionsResponse = z.infer<typeof ListExecutionsResponseSchema>
1074
+ export type ListStageProgress = z.infer<typeof ListStageProgressSchema>
1075
+ export type ListProgress = z.infer<typeof ListProgressResponseSchema>